+2020-02-13 Simon Marchi <simon.marchi@efficios.com>
+
+ * Makefile.in: Rename source files from .c to .cc.
+ * %.c: Rename to %.cc.
+ * configure.ac: Rename server.c to server.cc.
+ * configure: Re-generate.
+
2020-02-13 Simon Marchi <simon.marchi@efficios.com>
* Makefile.in: Rename gdbsupport source files from .c to .cc.
# Note that these are overridden by GNU make-specific code below if
# GNU make is used. The overrides implement dependency tracking.
-COMPILE.pre = $(CXX) -x c++ $(CXX_DIALECT)
+COMPILE.pre = $(CXX) $(CXX_DIALECT)
COMPILE.post = -c -o $@
COMPILE = $(ECHO_CXX) $(COMPILE.pre) $(INTERNAL_CFLAGS) $(COMPILE.post)
POSTCOMPILE = @true
# All source files that go into linking GDB remote server.
SFILES = \
- $(srcdir)/debug.c \
- $(srcdir)/dll.c \
- $(srcdir)/gdbreplay.c \
- $(srcdir)/hostio.c \
- $(srcdir)/hostio-errno.c \
- $(srcdir)/i387-fp.c \
- $(srcdir)/inferiors.c \
- $(srcdir)/linux-aarch64-low.c \
- $(srcdir)/linux-arm-low.c \
- $(srcdir)/linux-bfin-low.c \
- $(srcdir)/linux-cris-low.c \
- $(srcdir)/linux-crisv32-low.c \
- $(srcdir)/linux-ia64-low.c \
- $(srcdir)/linux-low.c \
- $(srcdir)/linux-m32r-low.c \
- $(srcdir)/linux-m68k-low.c \
- $(srcdir)/linux-mips-low.c \
- $(srcdir)/linux-nios2-low.c \
- $(srcdir)/linux-ppc-low.c \
- $(srcdir)/linux-s390-low.c \
- $(srcdir)/linux-sh-low.c \
- $(srcdir)/linux-sparc-low.c \
- $(srcdir)/linux-tile-low.c \
- $(srcdir)/linux-x86-low.c \
- $(srcdir)/linux-xtensa-low.c \
- $(srcdir)/mem-break.c \
- $(srcdir)/proc-service.c \
+ $(srcdir)/debug.cc \
+ $(srcdir)/dll.cc \
+ $(srcdir)/gdbreplay.cc \
+ $(srcdir)/hostio.cc \
+ $(srcdir)/hostio-errno.cc \
+ $(srcdir)/i387-fp.cc \
+ $(srcdir)/inferiors.cc \
+ $(srcdir)/linux-aarch64-low.cc \
+ $(srcdir)/linux-arm-low.cc \
+ $(srcdir)/linux-bfin-low.cc \
+ $(srcdir)/linux-cris-low.cc \
+ $(srcdir)/linux-crisv32-low.cc \
+ $(srcdir)/linux-ia64-low.cc \
+ $(srcdir)/linux-low.cc \
+ $(srcdir)/linux-m32r-low.cc \
+ $(srcdir)/linux-m68k-low.cc \
+ $(srcdir)/linux-mips-low.cc \
+ $(srcdir)/linux-nios2-low.cc \
+ $(srcdir)/linux-ppc-low.cc \
+ $(srcdir)/linux-s390-low.cc \
+ $(srcdir)/linux-sh-low.cc \
+ $(srcdir)/linux-sparc-low.cc \
+ $(srcdir)/linux-tile-low.cc \
+ $(srcdir)/linux-x86-low.cc \
+ $(srcdir)/linux-xtensa-low.cc \
+ $(srcdir)/mem-break.cc \
+ $(srcdir)/proc-service.cc \
$(srcdir)/proc-service.list \
- $(srcdir)/regcache.c \
- $(srcdir)/remote-utils.c \
- $(srcdir)/server.c \
- $(srcdir)/symbol.c \
- $(srcdir)/target.c \
- $(srcdir)/thread-db.c \
- $(srcdir)/utils.c \
- $(srcdir)/win32-arm-low.c \
- $(srcdir)/win32-i386-low.c \
- $(srcdir)/win32-low.c \
- $(srcdir)/wincecompat.c \
- $(srcdir)/x86-low.c \
+ $(srcdir)/regcache.cc \
+ $(srcdir)/remote-utils.cc \
+ $(srcdir)/server.cc \
+ $(srcdir)/symbol.cc \
+ $(srcdir)/target.cc \
+ $(srcdir)/thread-db.cc \
+ $(srcdir)/utils.cc \
+ $(srcdir)/win32-arm-low.cc \
+ $(srcdir)/win32-i386-low.cc \
+ $(srcdir)/win32-low.cc \
+ $(srcdir)/wincecompat.cc \
+ $(srcdir)/x86-low.cc \
$(srcdir)/../gdb/alloc.c \
$(srcdir)/../gdb/arch/arm.c \
$(srcdir)/../gdb/arch/arm-get-next-pcs.c \
etags \
`for i in yzzy ${DEPFILES}; do \
if [ x$$i != xyzzy ]; then \
- echo ${srcdir}/$$i | sed -e 's/\.o$$/\.c/' \
+ echo ${srcdir}/$$i | sed -e 's/\.o$$/\.cc/' \
-e 's,/\(arch\|nat\|target\)/,/../\1/,' \
-e 's,/\(gdbsupport\)/,/../../\1/,'; \
fi; \
rm -f *.o ${ADD_FILES} *~
rm -f gdbserver$(EXEEXT) gdbreplay$(EXEEXT) core make.log
rm -f $(IPA_LIB)
- rm -f *-generated.c
+ rm -f *-generated.cc
rm -f stamp-xml
rm -f $(DEPDIR)/*.Po
for i in $(CONFIG_SRC_SUBDIR); do \
force:
-version-generated.c: Makefile $(srcdir)/../gdb/version.in $(srcdir)/../bfd/version.h $(srcdir)/../gdbsupport/create-version.sh
+version-generated.cc: Makefile $(srcdir)/../gdb/version.in $(srcdir)/../bfd/version.h $(srcdir)/../gdbsupport/create-version.sh
$(ECHO_GEN) $(SHELL) $(srcdir)/../gdbsupport/create-version.sh $(srcdir)/../gdb \
$(host_alias) $(target_alias) $@
-xml-builtin-generated.c: stamp-xml; @true
+xml-builtin-generated.cc: stamp-xml; @true
stamp-xml: $(XML_DIR)/feature_to_c.sh Makefile $(XML_FILES)
$(SILENCE) rm -f xml-builtin.tmp
$(ECHO_GEN_XML_BUILTIN_GENERATED) $(SHELL) $(XML_DIR)/feature_to_c.sh \
xml-builtin.tmp $(XML_FILES)
- $(SILENCE) $(SHELL) $(srcdir)/../move-if-change xml-builtin.tmp xml-builtin-generated.c
+ $(SILENCE) $(SHELL) $(srcdir)/../move-if-change xml-builtin.tmp xml-builtin-generated.cc
$(SILENCE) echo stamp > stamp-xml
-.PRECIOUS: xml-builtin.c
+.PRECIOUS: xml-builtin.cc
# GNU Make has an annoying habit of putting *all* the Makefile variables
# into the environment, unless you include this target as a circumvention.
# Rules for special cases.
-ax-ipa.o: ax.c
+ax-ipa.o: ax.cc
$(IPAGENT_COMPILE) $(WARN_CFLAGS_NO_FORMAT) $<
$(POSTCOMPILE)
-ax.o: ax.c
+ax.o: ax.cc
$(COMPILE) $(WARN_CFLAGS_NO_FORMAT) $<
$(POSTCOMPILE)
# Rules for objects that go in the in-process agent.
arch/%-ipa.o: ../gdb/arch/%.c
- $(IPAGENT_COMPILE) $<
+ $(IPAGENT_COMPILE) -x c++ $<
$(POSTCOMPILE)
gdbsupport/%-ipa.o: ../gdbsupport/%.cc
$(IPAGENT_COMPILE) $<
$(POSTCOMPILE)
-%-ipa.o: %-generated.c
+%-ipa.o: %-generated.cc
$(IPAGENT_COMPILE) $<
$(POSTCOMPILE)
-%-ipa.o: %.c
+%-ipa.o: %.cc
$(IPAGENT_COMPILE) $<
$(POSTCOMPILE)
%-ipa.o: ../gdb/%.c
- $(IPAGENT_COMPILE) $<
+ $(IPAGENT_COMPILE) -x c++ $<
$(POSTCOMPILE)
# Note: Between two matching pattern rules, GNU Make 3.81 chooses the first one.
-# Therefore, this one needs to be before "%.o: %.c" for it to be considered for
-# files such as linux-amd64-ipa.o generated from linux-amd64-ipa.c.
+# Therefore, this one needs to be before "%.o: %.cc" for it to be considered for
+# files such as linux-amd64-ipa.o generated from linux-amd64-ipa.cc.
#
# Later versions of GNU Make choose the rule with the shortest stem, so it would
# work in any order.
-%-ipa.o: %-ipa.c
+%-ipa.o: %-ipa.cc
$(IPAGENT_COMPILE) $<
$(POSTCOMPILE)
# Rules for objects that go in the gdbserver binary.
arch/%.o: ../gdb/arch/%.c
- $(COMPILE) $<
+ $(COMPILE) -x c++ $<
$(POSTCOMPILE)
gdbsupport/%.o: ../gdbsupport/%.cc
$(COMPILE) $<
$(POSTCOMPILE)
-%.o: %-generated.c
+%.o: %-generated.cc
$(COMPILE) $<
$(POSTCOMPILE)
-%.o: %.c
+%.o: %.cc
$(COMPILE) $<
$(POSTCOMPILE)
nat/%.o: ../gdb/nat/%.c
- $(COMPILE) $<
+ $(COMPILE) -x c++ $<
$(POSTCOMPILE)
target/%.o: ../gdb/target/%.c
- $(COMPILE) $<
+ $(COMPILE) -x c++ $<
$(POSTCOMPILE)
%.o: ../gdb/%.c
- $(COMPILE) $<
+ $(COMPILE) -x c++ $<
$(POSTCOMPILE)
# Rules for register format descriptions. Suffix destination files with
# -generated to identify and clean them easily.
-%-generated.c: ../gdb/regformats/%.dat $(regdat_sh)
+%-generated.cc: ../gdb/regformats/%.dat $(regdat_sh)
$(ECHO_REGDAT) $(SHELL) $(regdat_sh) $< $@
-%-generated.c: ../gdb/regformats/arm/%.dat $(regdat_sh)
+%-generated.cc: ../gdb/regformats/arm/%.dat $(regdat_sh)
$(ECHO_REGDAT) $(SHELL) $(regdat_sh) $< $@
-%-generated.c: ../gdb/regformats/rs6000/%.dat $(regdat_sh)
+%-generated.cc: ../gdb/regformats/rs6000/%.dat $(regdat_sh)
$(ECHO_REGDAT) $(SHELL) $(regdat_sh) $< $@
#
else
override COMPILE.pre = source='$<' object='$@' libtool=no \
DEPDIR=$(DEPDIR) $(DEPMODE) $(depcomp) \
- $(CXX) -x c++ $(CXX_DIALECT)
+ $(CXX) $(CXX_DIALECT)
# depcomp handles atomicity for us, so we don't need a postcompile
# step.
override POSTCOMPILE =
# Disable implicit make rules.
include $(srcdir)/../gdb/disable-implicit-rules.mk
-# Do not delete intermediate files (e.g. *-generated.c).
+# Do not delete intermediate files (e.g. *-generated.cc).
.SECONDARY:
# This is the end of "Makefile.in".
+++ /dev/null
-/* Agent expression code for remote server.
- Copyright (C) 2009-2020 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 "ax.h"
-#include "gdbsupport/format.h"
-#include "tracepoint.h"
-#include "gdbsupport/rsp-low.h"
-
-static void ax_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
-
-#ifdef IN_PROCESS_AGENT
-int debug_agent = 0;
-#endif
-
-static void
-ax_vdebug (const char *fmt, ...)
-{
- char buf[1024];
- va_list ap;
-
- va_start (ap, fmt);
- vsprintf (buf, fmt, ap);
-#ifdef IN_PROCESS_AGENT
- fprintf (stderr, PROG "/ax: %s\n", buf);
-#else
- debug_printf (PROG "/ax: %s\n", buf);
-#endif
- va_end (ap);
-}
-
-#define ax_debug_1(level, fmt, args...) \
- do { \
- if (level <= debug_threads) \
- ax_vdebug ((fmt), ##args); \
- } while (0)
-
-#define ax_debug(FMT, args...) \
- ax_debug_1 (1, FMT, ##args)
-
-/* This enum must exactly match what is documented in
- gdb/doc/agentexpr.texi, including all the numerical values. */
-
-enum gdb_agent_op
- {
-#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) \
- gdb_agent_op_ ## NAME = VALUE,
-#include "gdbsupport/ax.def"
-#undef DEFOP
- gdb_agent_op_last
- };
-
-static const char *gdb_agent_op_names [gdb_agent_op_last] =
- {
- "?undef?"
-#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) , # NAME
-#include "gdbsupport/ax.def"
-#undef DEFOP
- };
-
-#ifndef IN_PROCESS_AGENT
-static const unsigned char gdb_agent_op_sizes [gdb_agent_op_last] =
- {
- 0
-#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) , SIZE
-#include "gdbsupport/ax.def"
-#undef DEFOP
- };
-#endif
-
-/* A wrapper for gdb_agent_op_names that does some bounds-checking. */
-
-static const char *
-gdb_agent_op_name (int op)
-{
- if (op < 0 || op >= gdb_agent_op_last || gdb_agent_op_names[op] == NULL)
- return "?undef?";
- return gdb_agent_op_names[op];
-}
-
-#ifndef IN_PROCESS_AGENT
-
-/* The packet form of an agent expression consists of an 'X', number
- of bytes in expression, a comma, and then the bytes. */
-
-struct agent_expr *
-gdb_parse_agent_expr (const char **actparm)
-{
- const char *act = *actparm;
- ULONGEST xlen;
- struct agent_expr *aexpr;
-
- ++act; /* skip the X */
- act = unpack_varlen_hex (act, &xlen);
- ++act; /* skip a comma */
- aexpr = XNEW (struct agent_expr);
- aexpr->length = xlen;
- aexpr->bytes = (unsigned char *) xmalloc (xlen);
- hex2bin (act, aexpr->bytes, xlen);
- *actparm = act + (xlen * 2);
- return aexpr;
-}
-
-void
-gdb_free_agent_expr (struct agent_expr *aexpr)
-{
- if (aexpr != NULL)
- {
- free (aexpr->bytes);
- free (aexpr);
- }
-}
-
-/* Convert the bytes of an agent expression back into hex digits, so
- they can be printed or uploaded. This allocates the buffer,
- callers should free when they are done with it. */
-
-char *
-gdb_unparse_agent_expr (struct agent_expr *aexpr)
-{
- char *rslt;
-
- rslt = (char *) xmalloc (2 * aexpr->length + 1);
- bin2hex (aexpr->bytes, rslt, aexpr->length);
- return rslt;
-}
-
-/* Bytecode compilation. */
-
-CORE_ADDR current_insn_ptr;
-
-int emit_error;
-
-struct bytecode_address
-{
- int pc;
- CORE_ADDR address;
- int goto_pc;
- /* Offset and size of field to be modified in the goto block. */
- int from_offset, from_size;
- struct bytecode_address *next;
-} *bytecode_address_table;
-
-void
-emit_prologue (void)
-{
- target_emit_ops ()->emit_prologue ();
-}
-
-void
-emit_epilogue (void)
-{
- target_emit_ops ()->emit_epilogue ();
-}
-
-static void
-emit_add (void)
-{
- target_emit_ops ()->emit_add ();
-}
-
-static void
-emit_sub (void)
-{
- target_emit_ops ()->emit_sub ();
-}
-
-static void
-emit_mul (void)
-{
- target_emit_ops ()->emit_mul ();
-}
-
-static void
-emit_lsh (void)
-{
- target_emit_ops ()->emit_lsh ();
-}
-
-static void
-emit_rsh_signed (void)
-{
- target_emit_ops ()->emit_rsh_signed ();
-}
-
-static void
-emit_rsh_unsigned (void)
-{
- target_emit_ops ()->emit_rsh_unsigned ();
-}
-
-static void
-emit_ext (int arg)
-{
- target_emit_ops ()->emit_ext (arg);
-}
-
-static void
-emit_log_not (void)
-{
- target_emit_ops ()->emit_log_not ();
-}
-
-static void
-emit_bit_and (void)
-{
- target_emit_ops ()->emit_bit_and ();
-}
-
-static void
-emit_bit_or (void)
-{
- target_emit_ops ()->emit_bit_or ();
-}
-
-static void
-emit_bit_xor (void)
-{
- target_emit_ops ()->emit_bit_xor ();
-}
-
-static void
-emit_bit_not (void)
-{
- target_emit_ops ()->emit_bit_not ();
-}
-
-static void
-emit_equal (void)
-{
- target_emit_ops ()->emit_equal ();
-}
-
-static void
-emit_less_signed (void)
-{
- target_emit_ops ()->emit_less_signed ();
-}
-
-static void
-emit_less_unsigned (void)
-{
- target_emit_ops ()->emit_less_unsigned ();
-}
-
-static void
-emit_ref (int size)
-{
- target_emit_ops ()->emit_ref (size);
-}
-
-static void
-emit_if_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_if_goto (offset_p, size_p);
-}
-
-static void
-emit_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_goto (offset_p, size_p);
-}
-
-static void
-write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
-{
- target_emit_ops ()->write_goto_address (from, to, size);
-}
-
-static void
-emit_const (LONGEST num)
-{
- target_emit_ops ()->emit_const (num);
-}
-
-static void
-emit_reg (int reg)
-{
- target_emit_ops ()->emit_reg (reg);
-}
-
-static void
-emit_pop (void)
-{
- target_emit_ops ()->emit_pop ();
-}
-
-static void
-emit_stack_flush (void)
-{
- target_emit_ops ()->emit_stack_flush ();
-}
-
-static void
-emit_zero_ext (int arg)
-{
- target_emit_ops ()->emit_zero_ext (arg);
-}
-
-static void
-emit_swap (void)
-{
- target_emit_ops ()->emit_swap ();
-}
-
-static void
-emit_stack_adjust (int n)
-{
- target_emit_ops ()->emit_stack_adjust (n);
-}
-
-/* FN's prototype is `LONGEST(*fn)(int)'. */
-
-static void
-emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- target_emit_ops ()->emit_int_call_1 (fn, arg1);
-}
-
-/* FN's prototype is `void(*fn)(int,LONGEST)'. */
-
-static void
-emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- target_emit_ops ()->emit_void_call_2 (fn, arg1);
-}
-
-static void
-emit_eq_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_eq_goto (offset_p, size_p);
-}
-
-static void
-emit_ne_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_ne_goto (offset_p, size_p);
-}
-
-static void
-emit_lt_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_lt_goto (offset_p, size_p);
-}
-
-static void
-emit_ge_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_ge_goto (offset_p, size_p);
-}
-
-static void
-emit_gt_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_gt_goto (offset_p, size_p);
-}
-
-static void
-emit_le_goto (int *offset_p, int *size_p)
-{
- target_emit_ops ()->emit_le_goto (offset_p, size_p);
-}
-
-/* Scan an agent expression for any evidence that the given PC is the
- target of a jump bytecode in the expression. */
-
-static int
-is_goto_target (struct agent_expr *aexpr, int pc)
-{
- int i;
- unsigned char op;
-
- for (i = 0; i < aexpr->length; i += 1 + gdb_agent_op_sizes[op])
- {
- op = aexpr->bytes[i];
-
- if (op == gdb_agent_op_goto || op == gdb_agent_op_if_goto)
- {
- int target = (aexpr->bytes[i + 1] << 8) + aexpr->bytes[i + 2];
- if (target == pc)
- return 1;
- }
- }
-
- return 0;
-}
-
-/* Given an agent expression, turn it into native code. */
-
-enum eval_result_type
-compile_bytecodes (struct agent_expr *aexpr)
-{
- int pc = 0;
- int done = 0;
- unsigned char op, next_op;
- int arg;
- /* This is only used to build 64-bit value for constants. */
- ULONGEST top;
- struct bytecode_address *aentry, *aentry2;
-
-#define UNHANDLED \
- do \
- { \
- ax_debug ("Cannot compile op 0x%x\n", op); \
- return expr_eval_unhandled_opcode; \
- } while (0)
-
- if (aexpr->length == 0)
- {
- ax_debug ("empty agent expression\n");
- return expr_eval_empty_expression;
- }
-
- bytecode_address_table = NULL;
-
- while (!done)
- {
- op = aexpr->bytes[pc];
-
- ax_debug ("About to compile op 0x%x, pc=%d\n", op, pc);
-
- /* Record the compiled-code address of the bytecode, for use by
- jump instructions. */
- aentry = XNEW (struct bytecode_address);
- aentry->pc = pc;
- aentry->address = current_insn_ptr;
- aentry->goto_pc = -1;
- aentry->from_offset = aentry->from_size = 0;
- aentry->next = bytecode_address_table;
- bytecode_address_table = aentry;
-
- ++pc;
-
- emit_error = 0;
-
- switch (op)
- {
- case gdb_agent_op_add:
- emit_add ();
- break;
-
- case gdb_agent_op_sub:
- emit_sub ();
- break;
-
- case gdb_agent_op_mul:
- emit_mul ();
- break;
-
- case gdb_agent_op_div_signed:
- UNHANDLED;
- break;
-
- case gdb_agent_op_div_unsigned:
- UNHANDLED;
- break;
-
- case gdb_agent_op_rem_signed:
- UNHANDLED;
- break;
-
- case gdb_agent_op_rem_unsigned:
- UNHANDLED;
- break;
-
- case gdb_agent_op_lsh:
- emit_lsh ();
- break;
-
- case gdb_agent_op_rsh_signed:
- emit_rsh_signed ();
- break;
-
- case gdb_agent_op_rsh_unsigned:
- emit_rsh_unsigned ();
- break;
-
- case gdb_agent_op_trace:
- UNHANDLED;
- break;
-
- case gdb_agent_op_trace_quick:
- UNHANDLED;
- break;
-
- case gdb_agent_op_log_not:
- emit_log_not ();
- break;
-
- case gdb_agent_op_bit_and:
- emit_bit_and ();
- break;
-
- case gdb_agent_op_bit_or:
- emit_bit_or ();
- break;
-
- case gdb_agent_op_bit_xor:
- emit_bit_xor ();
- break;
-
- case gdb_agent_op_bit_not:
- emit_bit_not ();
- break;
-
- case gdb_agent_op_equal:
- next_op = aexpr->bytes[pc];
- if (next_op == gdb_agent_op_if_goto
- && !is_goto_target (aexpr, pc)
- && target_emit_ops ()->emit_eq_goto)
- {
- ax_debug ("Combining equal & if_goto");
- pc += 1;
- aentry->pc = pc;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_eq_goto (&(aentry->from_offset), &(aentry->from_size));
- }
- else if (next_op == gdb_agent_op_log_not
- && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
- && !is_goto_target (aexpr, pc + 1)
- && target_emit_ops ()->emit_ne_goto)
- {
- ax_debug ("Combining equal & log_not & if_goto");
- pc += 2;
- aentry->pc = pc;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_ne_goto (&(aentry->from_offset), &(aentry->from_size));
- }
- else
- emit_equal ();
- break;
-
- case gdb_agent_op_less_signed:
- next_op = aexpr->bytes[pc];
- if (next_op == gdb_agent_op_if_goto
- && !is_goto_target (aexpr, pc))
- {
- ax_debug ("Combining less_signed & if_goto");
- pc += 1;
- aentry->pc = pc;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_lt_goto (&(aentry->from_offset), &(aentry->from_size));
- }
- else if (next_op == gdb_agent_op_log_not
- && !is_goto_target (aexpr, pc)
- && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
- && !is_goto_target (aexpr, pc + 1))
- {
- ax_debug ("Combining less_signed & log_not & if_goto");
- pc += 2;
- aentry->pc = pc;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_ge_goto (&(aentry->from_offset), &(aentry->from_size));
- }
- else
- emit_less_signed ();
- break;
-
- case gdb_agent_op_less_unsigned:
- emit_less_unsigned ();
- break;
-
- case gdb_agent_op_ext:
- arg = aexpr->bytes[pc++];
- if (arg < (sizeof (LONGEST) * 8))
- emit_ext (arg);
- break;
-
- case gdb_agent_op_ref8:
- emit_ref (1);
- break;
-
- case gdb_agent_op_ref16:
- emit_ref (2);
- break;
-
- case gdb_agent_op_ref32:
- emit_ref (4);
- break;
-
- case gdb_agent_op_ref64:
- emit_ref (8);
- break;
-
- case gdb_agent_op_if_goto:
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_if_goto (&(aentry->from_offset), &(aentry->from_size));
- break;
-
- case gdb_agent_op_goto:
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_goto (&(aentry->from_offset), &(aentry->from_size));
- break;
-
- case gdb_agent_op_const8:
- emit_stack_flush ();
- top = aexpr->bytes[pc++];
- emit_const (top);
- break;
-
- case gdb_agent_op_const16:
- emit_stack_flush ();
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- emit_const (top);
- break;
-
- case gdb_agent_op_const32:
- emit_stack_flush ();
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- emit_const (top);
- break;
-
- case gdb_agent_op_const64:
- emit_stack_flush ();
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- emit_const (top);
- break;
-
- case gdb_agent_op_reg:
- emit_stack_flush ();
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- emit_reg (arg);
- break;
-
- case gdb_agent_op_end:
- ax_debug ("At end of expression\n");
-
- /* Assume there is one stack element left, and that it is
- cached in "top" where emit_epilogue can get to it. */
- emit_stack_adjust (1);
-
- done = 1;
- break;
-
- case gdb_agent_op_dup:
- /* In our design, dup is equivalent to stack flushing. */
- emit_stack_flush ();
- break;
-
- case gdb_agent_op_pop:
- emit_pop ();
- break;
-
- case gdb_agent_op_zero_ext:
- arg = aexpr->bytes[pc++];
- if (arg < (sizeof (LONGEST) * 8))
- emit_zero_ext (arg);
- break;
-
- case gdb_agent_op_swap:
- next_op = aexpr->bytes[pc];
- /* Detect greater-than comparison sequences. */
- if (next_op == gdb_agent_op_less_signed
- && !is_goto_target (aexpr, pc)
- && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
- && !is_goto_target (aexpr, pc + 1))
- {
- ax_debug ("Combining swap & less_signed & if_goto");
- pc += 2;
- aentry->pc = pc;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_gt_goto (&(aentry->from_offset), &(aentry->from_size));
- }
- else if (next_op == gdb_agent_op_less_signed
- && !is_goto_target (aexpr, pc)
- && (aexpr->bytes[pc + 1] == gdb_agent_op_log_not)
- && !is_goto_target (aexpr, pc + 1)
- && (aexpr->bytes[pc + 2] == gdb_agent_op_if_goto)
- && !is_goto_target (aexpr, pc + 2))
- {
- ax_debug ("Combining swap & less_signed & log_not & if_goto");
- pc += 3;
- aentry->pc = pc;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- aentry->goto_pc = arg;
- emit_le_goto (&(aentry->from_offset), &(aentry->from_size));
- }
- else
- emit_swap ();
- break;
-
- case gdb_agent_op_getv:
- emit_stack_flush ();
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- emit_int_call_1 (get_get_tsv_func_addr (),
- arg);
- break;
-
- case gdb_agent_op_setv:
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- emit_void_call_2 (get_set_tsv_func_addr (),
- arg);
- break;
-
- case gdb_agent_op_tracev:
- UNHANDLED;
- break;
-
- /* GDB never (currently) generates any of these ops. */
- case gdb_agent_op_float:
- case gdb_agent_op_ref_float:
- case gdb_agent_op_ref_double:
- case gdb_agent_op_ref_long_double:
- case gdb_agent_op_l_to_d:
- case gdb_agent_op_d_to_l:
- case gdb_agent_op_trace16:
- UNHANDLED;
- break;
-
- default:
- ax_debug ("Agent expression op 0x%x not recognized\n", op);
- /* Don't struggle on, things will just get worse. */
- return expr_eval_unrecognized_opcode;
- }
-
- /* This catches errors that occur in target-specific code
- emission. */
- if (emit_error)
- {
- ax_debug ("Error %d while emitting code for %s\n",
- emit_error, gdb_agent_op_name (op));
- return expr_eval_unhandled_opcode;
- }
-
- ax_debug ("Op %s compiled\n", gdb_agent_op_name (op));
- }
-
- /* Now fill in real addresses as goto destinations. */
- for (aentry = bytecode_address_table; aentry; aentry = aentry->next)
- {
- int written = 0;
-
- if (aentry->goto_pc < 0)
- continue;
-
- /* Find the location that we are going to, and call back into
- target-specific code to write the actual address or
- displacement. */
- for (aentry2 = bytecode_address_table; aentry2; aentry2 = aentry2->next)
- {
- if (aentry2->pc == aentry->goto_pc)
- {
- ax_debug ("Want to jump from %s to %s\n",
- paddress (aentry->address),
- paddress (aentry2->address));
- write_goto_address (aentry->address + aentry->from_offset,
- aentry2->address, aentry->from_size);
- written = 1;
- break;
- }
- }
-
- /* Error out if we didn't find a destination. */
- if (!written)
- {
- ax_debug ("Destination of goto %d not found\n",
- aentry->goto_pc);
- return expr_eval_invalid_goto;
- }
- }
-
- return expr_eval_no_error;
-}
-
-#endif
-
-/* Make printf-type calls using arguments supplied from the host. We
- need to parse the format string ourselves, and call the formatting
- function with one argument at a time, partly because there is no
- safe portable way to construct a varargs call, and partly to serve
- as a security barrier against bad format strings that might get
- in. */
-
-static void
-ax_printf (CORE_ADDR fn, CORE_ADDR chan, const char *format,
- int nargs, ULONGEST *args)
-{
- const char *f = format;
- int i;
- const char *current_substring;
- int nargs_wanted;
-
- ax_debug ("Printf of \"%s\" with %d args", format, nargs);
-
- format_pieces fpieces (&f);
-
- nargs_wanted = 0;
- for (auto &&piece : fpieces)
- if (piece.argclass != literal_piece)
- ++nargs_wanted;
-
- if (nargs != nargs_wanted)
- error (_("Wrong number of arguments for specified format-string"));
-
- i = 0;
- for (auto &&piece : fpieces)
- {
- current_substring = piece.string;
- ax_debug ("current substring is '%s', class is %d",
- current_substring, piece.argclass);
- switch (piece.argclass)
- {
- case string_arg:
- {
- gdb_byte *str;
- CORE_ADDR tem;
- int j;
-
- tem = args[i];
- if (tem == 0)
- {
- printf (current_substring, "(null)");
- break;
- }
-
- /* This is a %s argument. Find the length of the string. */
- for (j = 0;; j++)
- {
- gdb_byte c;
-
- read_inferior_memory (tem + j, &c, 1);
- if (c == 0)
- break;
- }
-
- /* Copy the string contents into a string inside GDB. */
- str = (gdb_byte *) alloca (j + 1);
- if (j != 0)
- read_inferior_memory (tem, str, j);
- str[j] = 0;
-
- printf (current_substring, (char *) str);
- }
- break;
-
- case long_long_arg:
-#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG)
- {
- long long val = args[i];
-
- printf (current_substring, val);
- break;
- }
-#else
- error (_("long long not supported in agent printf"));
-#endif
- case int_arg:
- {
- int val = args[i];
-
- printf (current_substring, val);
- break;
- }
-
- case long_arg:
- {
- long val = args[i];
-
- printf (current_substring, val);
- break;
- }
-
- case size_t_arg:
- {
- size_t val = args[i];
-
- printf (current_substring, val);
- break;
- }
-
- case literal_piece:
- /* Print a portion of the format string that has no
- directives. Note that this will not include any
- ordinary %-specs, but it might include "%%". That is
- why we use printf_filtered and not puts_filtered here.
- Also, we pass a dummy argument because some platforms
- have modified GCC to include -Wformat-security by
- default, which will warn here if there is no
- argument. */
- printf (current_substring, 0);
- break;
-
- default:
- error (_("Format directive in '%s' not supported in agent printf"),
- current_substring);
- }
-
- /* Maybe advance to the next argument. */
- if (piece.argclass != literal_piece)
- ++i;
- }
-
- fflush (stdout);
-}
-
-/* The agent expression evaluator, as specified by the GDB docs. It
- returns 0 if everything went OK, and a nonzero error code
- otherwise. */
-
-enum eval_result_type
-gdb_eval_agent_expr (struct eval_agent_expr_context *ctx,
- struct agent_expr *aexpr,
- ULONGEST *rslt)
-{
- int pc = 0;
-#define STACK_MAX 100
- ULONGEST stack[STACK_MAX], top;
- int sp = 0;
- unsigned char op;
- int arg;
-
- /* This union is a convenient way to convert representations. For
- now, assume a standard architecture where the hardware integer
- types have 8, 16, 32, 64 bit types. A more robust solution would
- be to import stdint.h from gnulib. */
- union
- {
- union
- {
- unsigned char bytes[1];
- unsigned char val;
- } u8;
- union
- {
- unsigned char bytes[2];
- unsigned short val;
- } u16;
- union
- {
- unsigned char bytes[4];
- unsigned int val;
- } u32;
- union
- {
- unsigned char bytes[8];
- ULONGEST val;
- } u64;
- } cnv;
-
- if (aexpr->length == 0)
- {
- ax_debug ("empty agent expression");
- return expr_eval_empty_expression;
- }
-
- /* Cache the stack top in its own variable. Much of the time we can
- operate on this variable, rather than dinking with the stack. It
- needs to be copied to the stack when sp changes. */
- top = 0;
-
- while (1)
- {
- op = aexpr->bytes[pc++];
-
- ax_debug ("About to interpret byte 0x%x", op);
-
- switch (op)
- {
- case gdb_agent_op_add:
- top += stack[--sp];
- break;
-
- case gdb_agent_op_sub:
- top = stack[--sp] - top;
- break;
-
- case gdb_agent_op_mul:
- top *= stack[--sp];
- break;
-
- case gdb_agent_op_div_signed:
- if (top == 0)
- {
- ax_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = ((LONGEST) stack[--sp]) / ((LONGEST) top);
- break;
-
- case gdb_agent_op_div_unsigned:
- if (top == 0)
- {
- ax_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = stack[--sp] / top;
- break;
-
- case gdb_agent_op_rem_signed:
- if (top == 0)
- {
- ax_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = ((LONGEST) stack[--sp]) % ((LONGEST) top);
- break;
-
- case gdb_agent_op_rem_unsigned:
- if (top == 0)
- {
- ax_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = stack[--sp] % top;
- break;
-
- case gdb_agent_op_lsh:
- top = stack[--sp] << top;
- break;
-
- case gdb_agent_op_rsh_signed:
- top = ((LONGEST) stack[--sp]) >> top;
- break;
-
- case gdb_agent_op_rsh_unsigned:
- top = stack[--sp] >> top;
- break;
-
- case gdb_agent_op_trace:
- agent_mem_read (ctx, NULL, (CORE_ADDR) stack[--sp],
- (ULONGEST) top);
- if (--sp >= 0)
- top = stack[sp];
- break;
-
- case gdb_agent_op_trace_quick:
- arg = aexpr->bytes[pc++];
- agent_mem_read (ctx, NULL, (CORE_ADDR) top, (ULONGEST) arg);
- break;
-
- case gdb_agent_op_log_not:
- top = !top;
- break;
-
- case gdb_agent_op_bit_and:
- top &= stack[--sp];
- break;
-
- case gdb_agent_op_bit_or:
- top |= stack[--sp];
- break;
-
- case gdb_agent_op_bit_xor:
- top ^= stack[--sp];
- break;
-
- case gdb_agent_op_bit_not:
- top = ~top;
- break;
-
- case gdb_agent_op_equal:
- top = (stack[--sp] == top);
- break;
-
- case gdb_agent_op_less_signed:
- top = (((LONGEST) stack[--sp]) < ((LONGEST) top));
- break;
-
- case gdb_agent_op_less_unsigned:
- top = (stack[--sp] < top);
- break;
-
- case gdb_agent_op_ext:
- arg = aexpr->bytes[pc++];
- if (arg < (sizeof (LONGEST) * 8))
- {
- LONGEST mask = 1 << (arg - 1);
- top &= ((LONGEST) 1 << arg) - 1;
- top = (top ^ mask) - mask;
- }
- break;
-
- case gdb_agent_op_ref8:
- agent_mem_read (ctx, cnv.u8.bytes, (CORE_ADDR) top, 1);
- top = cnv.u8.val;
- break;
-
- case gdb_agent_op_ref16:
- agent_mem_read (ctx, cnv.u16.bytes, (CORE_ADDR) top, 2);
- top = cnv.u16.val;
- break;
-
- case gdb_agent_op_ref32:
- agent_mem_read (ctx, cnv.u32.bytes, (CORE_ADDR) top, 4);
- top = cnv.u32.val;
- break;
-
- case gdb_agent_op_ref64:
- agent_mem_read (ctx, cnv.u64.bytes, (CORE_ADDR) top, 8);
- top = cnv.u64.val;
- break;
-
- case gdb_agent_op_if_goto:
- if (top)
- pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
- else
- pc += 2;
- if (--sp >= 0)
- top = stack[sp];
- break;
-
- case gdb_agent_op_goto:
- pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
- break;
-
- case gdb_agent_op_const8:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- top = aexpr->bytes[pc++];
- break;
-
- case gdb_agent_op_const16:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- break;
-
- case gdb_agent_op_const32:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- break;
-
- case gdb_agent_op_const64:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- break;
-
- case gdb_agent_op_reg:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- {
- int regnum = arg;
- struct regcache *regcache = ctx->regcache;
-
- switch (register_size (regcache->tdesc, regnum))
- {
- case 8:
- collect_register (regcache, regnum, cnv.u64.bytes);
- top = cnv.u64.val;
- break;
- case 4:
- collect_register (regcache, regnum, cnv.u32.bytes);
- top = cnv.u32.val;
- break;
- case 2:
- collect_register (regcache, regnum, cnv.u16.bytes);
- top = cnv.u16.val;
- break;
- case 1:
- collect_register (regcache, regnum, cnv.u8.bytes);
- top = cnv.u8.val;
- break;
- default:
- internal_error (__FILE__, __LINE__,
- "unhandled register size");
- }
- }
- break;
-
- case gdb_agent_op_end:
- ax_debug ("At end of expression, sp=%d, stack top cache=0x%s",
- sp, pulongest (top));
- if (rslt)
- {
- if (sp <= 0)
- {
- /* This should be an error */
- ax_debug ("Stack is empty, nothing to return");
- return expr_eval_empty_stack;
- }
- *rslt = top;
- }
- return expr_eval_no_error;
-
- case gdb_agent_op_dup:
- stack[sp++] = top;
- break;
-
- case gdb_agent_op_pop:
- if (--sp >= 0)
- top = stack[sp];
- break;
-
- case gdb_agent_op_pick:
- arg = aexpr->bytes[pc++];
- stack[sp] = top;
- top = stack[sp - arg];
- ++sp;
- break;
-
- case gdb_agent_op_rot:
- {
- ULONGEST tem = stack[sp - 1];
-
- stack[sp - 1] = stack[sp - 2];
- stack[sp - 2] = top;
- top = tem;
- }
- break;
-
- case gdb_agent_op_zero_ext:
- arg = aexpr->bytes[pc++];
- if (arg < (sizeof (LONGEST) * 8))
- top &= ((LONGEST) 1 << arg) - 1;
- break;
-
- case gdb_agent_op_swap:
- /* Interchange top two stack elements, making sure top gets
- copied back onto stack. */
- stack[sp] = top;
- top = stack[sp - 1];
- stack[sp - 1] = stack[sp];
- break;
-
- case gdb_agent_op_getv:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- top = agent_get_trace_state_variable_value (arg);
- break;
-
- case gdb_agent_op_setv:
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- agent_set_trace_state_variable_value (arg, top);
- /* Note that we leave the value on the stack, for the
- benefit of later/enclosing expressions. */
- break;
-
- case gdb_agent_op_tracev:
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- agent_tsv_read (ctx, arg);
- break;
-
- case gdb_agent_op_tracenz:
- agent_mem_read_string (ctx, NULL, (CORE_ADDR) stack[--sp],
- (ULONGEST) top);
- if (--sp >= 0)
- top = stack[sp];
- break;
-
- case gdb_agent_op_printf:
- {
- int nargs, slen, i;
- CORE_ADDR fn = 0, chan = 0;
- /* Can't have more args than the entire size of the stack. */
- ULONGEST args[STACK_MAX];
- char *format;
-
- nargs = aexpr->bytes[pc++];
- slen = aexpr->bytes[pc++];
- slen = (slen << 8) + aexpr->bytes[pc++];
- format = (char *) &(aexpr->bytes[pc]);
- pc += slen;
- /* Pop function and channel. */
- fn = top;
- if (--sp >= 0)
- top = stack[sp];
- chan = top;
- if (--sp >= 0)
- top = stack[sp];
- /* Pop arguments into a dedicated array. */
- for (i = 0; i < nargs; ++i)
- {
- args[i] = top;
- if (--sp >= 0)
- top = stack[sp];
- }
-
- /* A bad format string means something is very wrong; give
- up immediately. */
- if (format[slen - 1] != '\0')
- error (_("Unterminated format string in printf bytecode"));
-
- ax_printf (fn, chan, format, nargs, args);
- }
- break;
-
- /* GDB never (currently) generates any of these ops. */
- case gdb_agent_op_float:
- case gdb_agent_op_ref_float:
- case gdb_agent_op_ref_double:
- case gdb_agent_op_ref_long_double:
- case gdb_agent_op_l_to_d:
- case gdb_agent_op_d_to_l:
- case gdb_agent_op_trace16:
- ax_debug ("Agent expression op 0x%x valid, but not handled",
- op);
- /* If ever GDB generates any of these, we don't have the
- option of ignoring. */
- return expr_eval_unhandled_opcode;
-
- default:
- ax_debug ("Agent expression op 0x%x not recognized", op);
- /* Don't struggle on, things will just get worse. */
- return expr_eval_unrecognized_opcode;
- }
-
- /* Check for stack badness. */
- if (sp >= (STACK_MAX - 1))
- {
- ax_debug ("Expression stack overflow");
- return expr_eval_stack_overflow;
- }
-
- if (sp < 0)
- {
- ax_debug ("Expression stack underflow");
- return expr_eval_stack_underflow;
- }
-
- ax_debug ("Op %s -> sp=%d, top=0x%s",
- gdb_agent_op_name (op), sp, phex_nz (top, 0));
- }
-}
--- /dev/null
+/* Agent expression code for remote server.
+ Copyright (C) 2009-2020 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 "ax.h"
+#include "gdbsupport/format.h"
+#include "tracepoint.h"
+#include "gdbsupport/rsp-low.h"
+
+static void ax_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
+
+#ifdef IN_PROCESS_AGENT
+int debug_agent = 0;
+#endif
+
+static void
+ax_vdebug (const char *fmt, ...)
+{
+ char buf[1024];
+ va_list ap;
+
+ va_start (ap, fmt);
+ vsprintf (buf, fmt, ap);
+#ifdef IN_PROCESS_AGENT
+ fprintf (stderr, PROG "/ax: %s\n", buf);
+#else
+ debug_printf (PROG "/ax: %s\n", buf);
+#endif
+ va_end (ap);
+}
+
+#define ax_debug_1(level, fmt, args...) \
+ do { \
+ if (level <= debug_threads) \
+ ax_vdebug ((fmt), ##args); \
+ } while (0)
+
+#define ax_debug(FMT, args...) \
+ ax_debug_1 (1, FMT, ##args)
+
+/* This enum must exactly match what is documented in
+ gdb/doc/agentexpr.texi, including all the numerical values. */
+
+enum gdb_agent_op
+ {
+#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) \
+ gdb_agent_op_ ## NAME = VALUE,
+#include "gdbsupport/ax.def"
+#undef DEFOP
+ gdb_agent_op_last
+ };
+
+static const char *gdb_agent_op_names [gdb_agent_op_last] =
+ {
+ "?undef?"
+#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) , # NAME
+#include "gdbsupport/ax.def"
+#undef DEFOP
+ };
+
+#ifndef IN_PROCESS_AGENT
+static const unsigned char gdb_agent_op_sizes [gdb_agent_op_last] =
+ {
+ 0
+#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) , SIZE
+#include "gdbsupport/ax.def"
+#undef DEFOP
+ };
+#endif
+
+/* A wrapper for gdb_agent_op_names that does some bounds-checking. */
+
+static const char *
+gdb_agent_op_name (int op)
+{
+ if (op < 0 || op >= gdb_agent_op_last || gdb_agent_op_names[op] == NULL)
+ return "?undef?";
+ return gdb_agent_op_names[op];
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* The packet form of an agent expression consists of an 'X', number
+ of bytes in expression, a comma, and then the bytes. */
+
+struct agent_expr *
+gdb_parse_agent_expr (const char **actparm)
+{
+ const char *act = *actparm;
+ ULONGEST xlen;
+ struct agent_expr *aexpr;
+
+ ++act; /* skip the X */
+ act = unpack_varlen_hex (act, &xlen);
+ ++act; /* skip a comma */
+ aexpr = XNEW (struct agent_expr);
+ aexpr->length = xlen;
+ aexpr->bytes = (unsigned char *) xmalloc (xlen);
+ hex2bin (act, aexpr->bytes, xlen);
+ *actparm = act + (xlen * 2);
+ return aexpr;
+}
+
+void
+gdb_free_agent_expr (struct agent_expr *aexpr)
+{
+ if (aexpr != NULL)
+ {
+ free (aexpr->bytes);
+ free (aexpr);
+ }
+}
+
+/* Convert the bytes of an agent expression back into hex digits, so
+ they can be printed or uploaded. This allocates the buffer,
+ callers should free when they are done with it. */
+
+char *
+gdb_unparse_agent_expr (struct agent_expr *aexpr)
+{
+ char *rslt;
+
+ rslt = (char *) xmalloc (2 * aexpr->length + 1);
+ bin2hex (aexpr->bytes, rslt, aexpr->length);
+ return rslt;
+}
+
+/* Bytecode compilation. */
+
+CORE_ADDR current_insn_ptr;
+
+int emit_error;
+
+struct bytecode_address
+{
+ int pc;
+ CORE_ADDR address;
+ int goto_pc;
+ /* Offset and size of field to be modified in the goto block. */
+ int from_offset, from_size;
+ struct bytecode_address *next;
+} *bytecode_address_table;
+
+void
+emit_prologue (void)
+{
+ target_emit_ops ()->emit_prologue ();
+}
+
+void
+emit_epilogue (void)
+{
+ target_emit_ops ()->emit_epilogue ();
+}
+
+static void
+emit_add (void)
+{
+ target_emit_ops ()->emit_add ();
+}
+
+static void
+emit_sub (void)
+{
+ target_emit_ops ()->emit_sub ();
+}
+
+static void
+emit_mul (void)
+{
+ target_emit_ops ()->emit_mul ();
+}
+
+static void
+emit_lsh (void)
+{
+ target_emit_ops ()->emit_lsh ();
+}
+
+static void
+emit_rsh_signed (void)
+{
+ target_emit_ops ()->emit_rsh_signed ();
+}
+
+static void
+emit_rsh_unsigned (void)
+{
+ target_emit_ops ()->emit_rsh_unsigned ();
+}
+
+static void
+emit_ext (int arg)
+{
+ target_emit_ops ()->emit_ext (arg);
+}
+
+static void
+emit_log_not (void)
+{
+ target_emit_ops ()->emit_log_not ();
+}
+
+static void
+emit_bit_and (void)
+{
+ target_emit_ops ()->emit_bit_and ();
+}
+
+static void
+emit_bit_or (void)
+{
+ target_emit_ops ()->emit_bit_or ();
+}
+
+static void
+emit_bit_xor (void)
+{
+ target_emit_ops ()->emit_bit_xor ();
+}
+
+static void
+emit_bit_not (void)
+{
+ target_emit_ops ()->emit_bit_not ();
+}
+
+static void
+emit_equal (void)
+{
+ target_emit_ops ()->emit_equal ();
+}
+
+static void
+emit_less_signed (void)
+{
+ target_emit_ops ()->emit_less_signed ();
+}
+
+static void
+emit_less_unsigned (void)
+{
+ target_emit_ops ()->emit_less_unsigned ();
+}
+
+static void
+emit_ref (int size)
+{
+ target_emit_ops ()->emit_ref (size);
+}
+
+static void
+emit_if_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_if_goto (offset_p, size_p);
+}
+
+static void
+emit_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_goto (offset_p, size_p);
+}
+
+static void
+write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ target_emit_ops ()->write_goto_address (from, to, size);
+}
+
+static void
+emit_const (LONGEST num)
+{
+ target_emit_ops ()->emit_const (num);
+}
+
+static void
+emit_reg (int reg)
+{
+ target_emit_ops ()->emit_reg (reg);
+}
+
+static void
+emit_pop (void)
+{
+ target_emit_ops ()->emit_pop ();
+}
+
+static void
+emit_stack_flush (void)
+{
+ target_emit_ops ()->emit_stack_flush ();
+}
+
+static void
+emit_zero_ext (int arg)
+{
+ target_emit_ops ()->emit_zero_ext (arg);
+}
+
+static void
+emit_swap (void)
+{
+ target_emit_ops ()->emit_swap ();
+}
+
+static void
+emit_stack_adjust (int n)
+{
+ target_emit_ops ()->emit_stack_adjust (n);
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'. */
+
+static void
+emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ target_emit_ops ()->emit_int_call_1 (fn, arg1);
+}
+
+/* FN's prototype is `void(*fn)(int,LONGEST)'. */
+
+static void
+emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ target_emit_ops ()->emit_void_call_2 (fn, arg1);
+}
+
+static void
+emit_eq_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_eq_goto (offset_p, size_p);
+}
+
+static void
+emit_ne_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_ne_goto (offset_p, size_p);
+}
+
+static void
+emit_lt_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_lt_goto (offset_p, size_p);
+}
+
+static void
+emit_ge_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_ge_goto (offset_p, size_p);
+}
+
+static void
+emit_gt_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_gt_goto (offset_p, size_p);
+}
+
+static void
+emit_le_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_le_goto (offset_p, size_p);
+}
+
+/* Scan an agent expression for any evidence that the given PC is the
+ target of a jump bytecode in the expression. */
+
+static int
+is_goto_target (struct agent_expr *aexpr, int pc)
+{
+ int i;
+ unsigned char op;
+
+ for (i = 0; i < aexpr->length; i += 1 + gdb_agent_op_sizes[op])
+ {
+ op = aexpr->bytes[i];
+
+ if (op == gdb_agent_op_goto || op == gdb_agent_op_if_goto)
+ {
+ int target = (aexpr->bytes[i + 1] << 8) + aexpr->bytes[i + 2];
+ if (target == pc)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Given an agent expression, turn it into native code. */
+
+enum eval_result_type
+compile_bytecodes (struct agent_expr *aexpr)
+{
+ int pc = 0;
+ int done = 0;
+ unsigned char op, next_op;
+ int arg;
+ /* This is only used to build 64-bit value for constants. */
+ ULONGEST top;
+ struct bytecode_address *aentry, *aentry2;
+
+#define UNHANDLED \
+ do \
+ { \
+ ax_debug ("Cannot compile op 0x%x\n", op); \
+ return expr_eval_unhandled_opcode; \
+ } while (0)
+
+ if (aexpr->length == 0)
+ {
+ ax_debug ("empty agent expression\n");
+ return expr_eval_empty_expression;
+ }
+
+ bytecode_address_table = NULL;
+
+ while (!done)
+ {
+ op = aexpr->bytes[pc];
+
+ ax_debug ("About to compile op 0x%x, pc=%d\n", op, pc);
+
+ /* Record the compiled-code address of the bytecode, for use by
+ jump instructions. */
+ aentry = XNEW (struct bytecode_address);
+ aentry->pc = pc;
+ aentry->address = current_insn_ptr;
+ aentry->goto_pc = -1;
+ aentry->from_offset = aentry->from_size = 0;
+ aentry->next = bytecode_address_table;
+ bytecode_address_table = aentry;
+
+ ++pc;
+
+ emit_error = 0;
+
+ switch (op)
+ {
+ case gdb_agent_op_add:
+ emit_add ();
+ break;
+
+ case gdb_agent_op_sub:
+ emit_sub ();
+ break;
+
+ case gdb_agent_op_mul:
+ emit_mul ();
+ break;
+
+ case gdb_agent_op_div_signed:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_div_unsigned:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_rem_signed:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_rem_unsigned:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_lsh:
+ emit_lsh ();
+ break;
+
+ case gdb_agent_op_rsh_signed:
+ emit_rsh_signed ();
+ break;
+
+ case gdb_agent_op_rsh_unsigned:
+ emit_rsh_unsigned ();
+ break;
+
+ case gdb_agent_op_trace:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_trace_quick:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_log_not:
+ emit_log_not ();
+ break;
+
+ case gdb_agent_op_bit_and:
+ emit_bit_and ();
+ break;
+
+ case gdb_agent_op_bit_or:
+ emit_bit_or ();
+ break;
+
+ case gdb_agent_op_bit_xor:
+ emit_bit_xor ();
+ break;
+
+ case gdb_agent_op_bit_not:
+ emit_bit_not ();
+ break;
+
+ case gdb_agent_op_equal:
+ next_op = aexpr->bytes[pc];
+ if (next_op == gdb_agent_op_if_goto
+ && !is_goto_target (aexpr, pc)
+ && target_emit_ops ()->emit_eq_goto)
+ {
+ ax_debug ("Combining equal & if_goto");
+ pc += 1;
+ aentry->pc = pc;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_eq_goto (&(aentry->from_offset), &(aentry->from_size));
+ }
+ else if (next_op == gdb_agent_op_log_not
+ && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
+ && !is_goto_target (aexpr, pc + 1)
+ && target_emit_ops ()->emit_ne_goto)
+ {
+ ax_debug ("Combining equal & log_not & if_goto");
+ pc += 2;
+ aentry->pc = pc;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_ne_goto (&(aentry->from_offset), &(aentry->from_size));
+ }
+ else
+ emit_equal ();
+ break;
+
+ case gdb_agent_op_less_signed:
+ next_op = aexpr->bytes[pc];
+ if (next_op == gdb_agent_op_if_goto
+ && !is_goto_target (aexpr, pc))
+ {
+ ax_debug ("Combining less_signed & if_goto");
+ pc += 1;
+ aentry->pc = pc;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_lt_goto (&(aentry->from_offset), &(aentry->from_size));
+ }
+ else if (next_op == gdb_agent_op_log_not
+ && !is_goto_target (aexpr, pc)
+ && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
+ && !is_goto_target (aexpr, pc + 1))
+ {
+ ax_debug ("Combining less_signed & log_not & if_goto");
+ pc += 2;
+ aentry->pc = pc;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_ge_goto (&(aentry->from_offset), &(aentry->from_size));
+ }
+ else
+ emit_less_signed ();
+ break;
+
+ case gdb_agent_op_less_unsigned:
+ emit_less_unsigned ();
+ break;
+
+ case gdb_agent_op_ext:
+ arg = aexpr->bytes[pc++];
+ if (arg < (sizeof (LONGEST) * 8))
+ emit_ext (arg);
+ break;
+
+ case gdb_agent_op_ref8:
+ emit_ref (1);
+ break;
+
+ case gdb_agent_op_ref16:
+ emit_ref (2);
+ break;
+
+ case gdb_agent_op_ref32:
+ emit_ref (4);
+ break;
+
+ case gdb_agent_op_ref64:
+ emit_ref (8);
+ break;
+
+ case gdb_agent_op_if_goto:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_if_goto (&(aentry->from_offset), &(aentry->from_size));
+ break;
+
+ case gdb_agent_op_goto:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_goto (&(aentry->from_offset), &(aentry->from_size));
+ break;
+
+ case gdb_agent_op_const8:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_const16:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_const32:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_const64:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_reg:
+ emit_stack_flush ();
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ emit_reg (arg);
+ break;
+
+ case gdb_agent_op_end:
+ ax_debug ("At end of expression\n");
+
+ /* Assume there is one stack element left, and that it is
+ cached in "top" where emit_epilogue can get to it. */
+ emit_stack_adjust (1);
+
+ done = 1;
+ break;
+
+ case gdb_agent_op_dup:
+ /* In our design, dup is equivalent to stack flushing. */
+ emit_stack_flush ();
+ break;
+
+ case gdb_agent_op_pop:
+ emit_pop ();
+ break;
+
+ case gdb_agent_op_zero_ext:
+ arg = aexpr->bytes[pc++];
+ if (arg < (sizeof (LONGEST) * 8))
+ emit_zero_ext (arg);
+ break;
+
+ case gdb_agent_op_swap:
+ next_op = aexpr->bytes[pc];
+ /* Detect greater-than comparison sequences. */
+ if (next_op == gdb_agent_op_less_signed
+ && !is_goto_target (aexpr, pc)
+ && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
+ && !is_goto_target (aexpr, pc + 1))
+ {
+ ax_debug ("Combining swap & less_signed & if_goto");
+ pc += 2;
+ aentry->pc = pc;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_gt_goto (&(aentry->from_offset), &(aentry->from_size));
+ }
+ else if (next_op == gdb_agent_op_less_signed
+ && !is_goto_target (aexpr, pc)
+ && (aexpr->bytes[pc + 1] == gdb_agent_op_log_not)
+ && !is_goto_target (aexpr, pc + 1)
+ && (aexpr->bytes[pc + 2] == gdb_agent_op_if_goto)
+ && !is_goto_target (aexpr, pc + 2))
+ {
+ ax_debug ("Combining swap & less_signed & log_not & if_goto");
+ pc += 3;
+ aentry->pc = pc;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_le_goto (&(aentry->from_offset), &(aentry->from_size));
+ }
+ else
+ emit_swap ();
+ break;
+
+ case gdb_agent_op_getv:
+ emit_stack_flush ();
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ emit_int_call_1 (get_get_tsv_func_addr (),
+ arg);
+ break;
+
+ case gdb_agent_op_setv:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ emit_void_call_2 (get_set_tsv_func_addr (),
+ arg);
+ break;
+
+ case gdb_agent_op_tracev:
+ UNHANDLED;
+ break;
+
+ /* GDB never (currently) generates any of these ops. */
+ case gdb_agent_op_float:
+ case gdb_agent_op_ref_float:
+ case gdb_agent_op_ref_double:
+ case gdb_agent_op_ref_long_double:
+ case gdb_agent_op_l_to_d:
+ case gdb_agent_op_d_to_l:
+ case gdb_agent_op_trace16:
+ UNHANDLED;
+ break;
+
+ default:
+ ax_debug ("Agent expression op 0x%x not recognized\n", op);
+ /* Don't struggle on, things will just get worse. */
+ return expr_eval_unrecognized_opcode;
+ }
+
+ /* This catches errors that occur in target-specific code
+ emission. */
+ if (emit_error)
+ {
+ ax_debug ("Error %d while emitting code for %s\n",
+ emit_error, gdb_agent_op_name (op));
+ return expr_eval_unhandled_opcode;
+ }
+
+ ax_debug ("Op %s compiled\n", gdb_agent_op_name (op));
+ }
+
+ /* Now fill in real addresses as goto destinations. */
+ for (aentry = bytecode_address_table; aentry; aentry = aentry->next)
+ {
+ int written = 0;
+
+ if (aentry->goto_pc < 0)
+ continue;
+
+ /* Find the location that we are going to, and call back into
+ target-specific code to write the actual address or
+ displacement. */
+ for (aentry2 = bytecode_address_table; aentry2; aentry2 = aentry2->next)
+ {
+ if (aentry2->pc == aentry->goto_pc)
+ {
+ ax_debug ("Want to jump from %s to %s\n",
+ paddress (aentry->address),
+ paddress (aentry2->address));
+ write_goto_address (aentry->address + aentry->from_offset,
+ aentry2->address, aentry->from_size);
+ written = 1;
+ break;
+ }
+ }
+
+ /* Error out if we didn't find a destination. */
+ if (!written)
+ {
+ ax_debug ("Destination of goto %d not found\n",
+ aentry->goto_pc);
+ return expr_eval_invalid_goto;
+ }
+ }
+
+ return expr_eval_no_error;
+}
+
+#endif
+
+/* Make printf-type calls using arguments supplied from the host. We
+ need to parse the format string ourselves, and call the formatting
+ function with one argument at a time, partly because there is no
+ safe portable way to construct a varargs call, and partly to serve
+ as a security barrier against bad format strings that might get
+ in. */
+
+static void
+ax_printf (CORE_ADDR fn, CORE_ADDR chan, const char *format,
+ int nargs, ULONGEST *args)
+{
+ const char *f = format;
+ int i;
+ const char *current_substring;
+ int nargs_wanted;
+
+ ax_debug ("Printf of \"%s\" with %d args", format, nargs);
+
+ format_pieces fpieces (&f);
+
+ nargs_wanted = 0;
+ for (auto &&piece : fpieces)
+ if (piece.argclass != literal_piece)
+ ++nargs_wanted;
+
+ if (nargs != nargs_wanted)
+ error (_("Wrong number of arguments for specified format-string"));
+
+ i = 0;
+ for (auto &&piece : fpieces)
+ {
+ current_substring = piece.string;
+ ax_debug ("current substring is '%s', class is %d",
+ current_substring, piece.argclass);
+ switch (piece.argclass)
+ {
+ case string_arg:
+ {
+ gdb_byte *str;
+ CORE_ADDR tem;
+ int j;
+
+ tem = args[i];
+ if (tem == 0)
+ {
+ printf (current_substring, "(null)");
+ break;
+ }
+
+ /* This is a %s argument. Find the length of the string. */
+ for (j = 0;; j++)
+ {
+ gdb_byte c;
+
+ read_inferior_memory (tem + j, &c, 1);
+ if (c == 0)
+ break;
+ }
+
+ /* Copy the string contents into a string inside GDB. */
+ str = (gdb_byte *) alloca (j + 1);
+ if (j != 0)
+ read_inferior_memory (tem, str, j);
+ str[j] = 0;
+
+ printf (current_substring, (char *) str);
+ }
+ break;
+
+ case long_long_arg:
+#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG)
+ {
+ long long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+#else
+ error (_("long long not supported in agent printf"));
+#endif
+ case int_arg:
+ {
+ int val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case long_arg:
+ {
+ long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case size_t_arg:
+ {
+ size_t val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case literal_piece:
+ /* Print a portion of the format string that has no
+ directives. Note that this will not include any
+ ordinary %-specs, but it might include "%%". That is
+ why we use printf_filtered and not puts_filtered here.
+ Also, we pass a dummy argument because some platforms
+ have modified GCC to include -Wformat-security by
+ default, which will warn here if there is no
+ argument. */
+ printf (current_substring, 0);
+ break;
+
+ default:
+ error (_("Format directive in '%s' not supported in agent printf"),
+ current_substring);
+ }
+
+ /* Maybe advance to the next argument. */
+ if (piece.argclass != literal_piece)
+ ++i;
+ }
+
+ fflush (stdout);
+}
+
+/* The agent expression evaluator, as specified by the GDB docs. It
+ returns 0 if everything went OK, and a nonzero error code
+ otherwise. */
+
+enum eval_result_type
+gdb_eval_agent_expr (struct eval_agent_expr_context *ctx,
+ struct agent_expr *aexpr,
+ ULONGEST *rslt)
+{
+ int pc = 0;
+#define STACK_MAX 100
+ ULONGEST stack[STACK_MAX], top;
+ int sp = 0;
+ unsigned char op;
+ int arg;
+
+ /* This union is a convenient way to convert representations. For
+ now, assume a standard architecture where the hardware integer
+ types have 8, 16, 32, 64 bit types. A more robust solution would
+ be to import stdint.h from gnulib. */
+ union
+ {
+ union
+ {
+ unsigned char bytes[1];
+ unsigned char val;
+ } u8;
+ union
+ {
+ unsigned char bytes[2];
+ unsigned short val;
+ } u16;
+ union
+ {
+ unsigned char bytes[4];
+ unsigned int val;
+ } u32;
+ union
+ {
+ unsigned char bytes[8];
+ ULONGEST val;
+ } u64;
+ } cnv;
+
+ if (aexpr->length == 0)
+ {
+ ax_debug ("empty agent expression");
+ return expr_eval_empty_expression;
+ }
+
+ /* Cache the stack top in its own variable. Much of the time we can
+ operate on this variable, rather than dinking with the stack. It
+ needs to be copied to the stack when sp changes. */
+ top = 0;
+
+ while (1)
+ {
+ op = aexpr->bytes[pc++];
+
+ ax_debug ("About to interpret byte 0x%x", op);
+
+ switch (op)
+ {
+ case gdb_agent_op_add:
+ top += stack[--sp];
+ break;
+
+ case gdb_agent_op_sub:
+ top = stack[--sp] - top;
+ break;
+
+ case gdb_agent_op_mul:
+ top *= stack[--sp];
+ break;
+
+ case gdb_agent_op_div_signed:
+ if (top == 0)
+ {
+ ax_debug ("Attempted to divide by zero");
+ return expr_eval_divide_by_zero;
+ }
+ top = ((LONGEST) stack[--sp]) / ((LONGEST) top);
+ break;
+
+ case gdb_agent_op_div_unsigned:
+ if (top == 0)
+ {
+ ax_debug ("Attempted to divide by zero");
+ return expr_eval_divide_by_zero;
+ }
+ top = stack[--sp] / top;
+ break;
+
+ case gdb_agent_op_rem_signed:
+ if (top == 0)
+ {
+ ax_debug ("Attempted to divide by zero");
+ return expr_eval_divide_by_zero;
+ }
+ top = ((LONGEST) stack[--sp]) % ((LONGEST) top);
+ break;
+
+ case gdb_agent_op_rem_unsigned:
+ if (top == 0)
+ {
+ ax_debug ("Attempted to divide by zero");
+ return expr_eval_divide_by_zero;
+ }
+ top = stack[--sp] % top;
+ break;
+
+ case gdb_agent_op_lsh:
+ top = stack[--sp] << top;
+ break;
+
+ case gdb_agent_op_rsh_signed:
+ top = ((LONGEST) stack[--sp]) >> top;
+ break;
+
+ case gdb_agent_op_rsh_unsigned:
+ top = stack[--sp] >> top;
+ break;
+
+ case gdb_agent_op_trace:
+ agent_mem_read (ctx, NULL, (CORE_ADDR) stack[--sp],
+ (ULONGEST) top);
+ if (--sp >= 0)
+ top = stack[sp];
+ break;
+
+ case gdb_agent_op_trace_quick:
+ arg = aexpr->bytes[pc++];
+ agent_mem_read (ctx, NULL, (CORE_ADDR) top, (ULONGEST) arg);
+ break;
+
+ case gdb_agent_op_log_not:
+ top = !top;
+ break;
+
+ case gdb_agent_op_bit_and:
+ top &= stack[--sp];
+ break;
+
+ case gdb_agent_op_bit_or:
+ top |= stack[--sp];
+ break;
+
+ case gdb_agent_op_bit_xor:
+ top ^= stack[--sp];
+ break;
+
+ case gdb_agent_op_bit_not:
+ top = ~top;
+ break;
+
+ case gdb_agent_op_equal:
+ top = (stack[--sp] == top);
+ break;
+
+ case gdb_agent_op_less_signed:
+ top = (((LONGEST) stack[--sp]) < ((LONGEST) top));
+ break;
+
+ case gdb_agent_op_less_unsigned:
+ top = (stack[--sp] < top);
+ break;
+
+ case gdb_agent_op_ext:
+ arg = aexpr->bytes[pc++];
+ if (arg < (sizeof (LONGEST) * 8))
+ {
+ LONGEST mask = 1 << (arg - 1);
+ top &= ((LONGEST) 1 << arg) - 1;
+ top = (top ^ mask) - mask;
+ }
+ break;
+
+ case gdb_agent_op_ref8:
+ agent_mem_read (ctx, cnv.u8.bytes, (CORE_ADDR) top, 1);
+ top = cnv.u8.val;
+ break;
+
+ case gdb_agent_op_ref16:
+ agent_mem_read (ctx, cnv.u16.bytes, (CORE_ADDR) top, 2);
+ top = cnv.u16.val;
+ break;
+
+ case gdb_agent_op_ref32:
+ agent_mem_read (ctx, cnv.u32.bytes, (CORE_ADDR) top, 4);
+ top = cnv.u32.val;
+ break;
+
+ case gdb_agent_op_ref64:
+ agent_mem_read (ctx, cnv.u64.bytes, (CORE_ADDR) top, 8);
+ top = cnv.u64.val;
+ break;
+
+ case gdb_agent_op_if_goto:
+ if (top)
+ pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
+ else
+ pc += 2;
+ if (--sp >= 0)
+ top = stack[sp];
+ break;
+
+ case gdb_agent_op_goto:
+ pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
+ break;
+
+ case gdb_agent_op_const8:
+ /* Flush the cached stack top. */
+ stack[sp++] = top;
+ top = aexpr->bytes[pc++];
+ break;
+
+ case gdb_agent_op_const16:
+ /* Flush the cached stack top. */
+ stack[sp++] = top;
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ break;
+
+ case gdb_agent_op_const32:
+ /* Flush the cached stack top. */
+ stack[sp++] = top;
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ break;
+
+ case gdb_agent_op_const64:
+ /* Flush the cached stack top. */
+ stack[sp++] = top;
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ break;
+
+ case gdb_agent_op_reg:
+ /* Flush the cached stack top. */
+ stack[sp++] = top;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ {
+ int regnum = arg;
+ struct regcache *regcache = ctx->regcache;
+
+ switch (register_size (regcache->tdesc, regnum))
+ {
+ case 8:
+ collect_register (regcache, regnum, cnv.u64.bytes);
+ top = cnv.u64.val;
+ break;
+ case 4:
+ collect_register (regcache, regnum, cnv.u32.bytes);
+ top = cnv.u32.val;
+ break;
+ case 2:
+ collect_register (regcache, regnum, cnv.u16.bytes);
+ top = cnv.u16.val;
+ break;
+ case 1:
+ collect_register (regcache, regnum, cnv.u8.bytes);
+ top = cnv.u8.val;
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ "unhandled register size");
+ }
+ }
+ break;
+
+ case gdb_agent_op_end:
+ ax_debug ("At end of expression, sp=%d, stack top cache=0x%s",
+ sp, pulongest (top));
+ if (rslt)
+ {
+ if (sp <= 0)
+ {
+ /* This should be an error */
+ ax_debug ("Stack is empty, nothing to return");
+ return expr_eval_empty_stack;
+ }
+ *rslt = top;
+ }
+ return expr_eval_no_error;
+
+ case gdb_agent_op_dup:
+ stack[sp++] = top;
+ break;
+
+ case gdb_agent_op_pop:
+ if (--sp >= 0)
+ top = stack[sp];
+ break;
+
+ case gdb_agent_op_pick:
+ arg = aexpr->bytes[pc++];
+ stack[sp] = top;
+ top = stack[sp - arg];
+ ++sp;
+ break;
+
+ case gdb_agent_op_rot:
+ {
+ ULONGEST tem = stack[sp - 1];
+
+ stack[sp - 1] = stack[sp - 2];
+ stack[sp - 2] = top;
+ top = tem;
+ }
+ break;
+
+ case gdb_agent_op_zero_ext:
+ arg = aexpr->bytes[pc++];
+ if (arg < (sizeof (LONGEST) * 8))
+ top &= ((LONGEST) 1 << arg) - 1;
+ break;
+
+ case gdb_agent_op_swap:
+ /* Interchange top two stack elements, making sure top gets
+ copied back onto stack. */
+ stack[sp] = top;
+ top = stack[sp - 1];
+ stack[sp - 1] = stack[sp];
+ break;
+
+ case gdb_agent_op_getv:
+ /* Flush the cached stack top. */
+ stack[sp++] = top;
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ top = agent_get_trace_state_variable_value (arg);
+ break;
+
+ case gdb_agent_op_setv:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ agent_set_trace_state_variable_value (arg, top);
+ /* Note that we leave the value on the stack, for the
+ benefit of later/enclosing expressions. */
+ break;
+
+ case gdb_agent_op_tracev:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ agent_tsv_read (ctx, arg);
+ break;
+
+ case gdb_agent_op_tracenz:
+ agent_mem_read_string (ctx, NULL, (CORE_ADDR) stack[--sp],
+ (ULONGEST) top);
+ if (--sp >= 0)
+ top = stack[sp];
+ break;
+
+ case gdb_agent_op_printf:
+ {
+ int nargs, slen, i;
+ CORE_ADDR fn = 0, chan = 0;
+ /* Can't have more args than the entire size of the stack. */
+ ULONGEST args[STACK_MAX];
+ char *format;
+
+ nargs = aexpr->bytes[pc++];
+ slen = aexpr->bytes[pc++];
+ slen = (slen << 8) + aexpr->bytes[pc++];
+ format = (char *) &(aexpr->bytes[pc]);
+ pc += slen;
+ /* Pop function and channel. */
+ fn = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ chan = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ /* Pop arguments into a dedicated array. */
+ for (i = 0; i < nargs; ++i)
+ {
+ args[i] = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ }
+
+ /* A bad format string means something is very wrong; give
+ up immediately. */
+ if (format[slen - 1] != '\0')
+ error (_("Unterminated format string in printf bytecode"));
+
+ ax_printf (fn, chan, format, nargs, args);
+ }
+ break;
+
+ /* GDB never (currently) generates any of these ops. */
+ case gdb_agent_op_float:
+ case gdb_agent_op_ref_float:
+ case gdb_agent_op_ref_double:
+ case gdb_agent_op_ref_long_double:
+ case gdb_agent_op_l_to_d:
+ case gdb_agent_op_d_to_l:
+ case gdb_agent_op_trace16:
+ ax_debug ("Agent expression op 0x%x valid, but not handled",
+ op);
+ /* If ever GDB generates any of these, we don't have the
+ option of ignoring. */
+ return expr_eval_unhandled_opcode;
+
+ default:
+ ax_debug ("Agent expression op 0x%x not recognized", op);
+ /* Don't struggle on, things will just get worse. */
+ return expr_eval_unrecognized_opcode;
+ }
+
+ /* Check for stack badness. */
+ if (sp >= (STACK_MAX - 1))
+ {
+ ax_debug ("Expression stack overflow");
+ return expr_eval_stack_overflow;
+ }
+
+ if (sp < 0)
+ {
+ ax_debug ("Expression stack underflow");
+ return expr_eval_stack_underflow;
+ }
+
+ ax_debug ("Op %s -> sp=%d, top=0x%s",
+ gdb_agent_op_name (op), sp, phex_nz (top, 0));
+ }
+}
PACKAGE_BUGREPORT=
PACKAGE_URL=
-ac_unique_file="server.c"
+ac_unique_file="server.cc"
# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
dnl Process this file with autoconf to produce a configure script.
-AC_INIT(server.c)
+AC_INIT(server.cc)
AC_CONFIG_HEADERS(config.h:config.in, [echo > stamp-h])
AM_MAINTAINER_MODE
+++ /dev/null
-/* Debugging routines for the remote server for GDB.
- Copyright (C) 2014-2020 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 <chrono>
-
-#if !defined (IN_PROCESS_AGENT)
-int remote_debug = 0;
-#endif
-
-/* Output file for debugging. Default to standard error. */
-FILE *debug_file = stderr;
-
-/* See debug.h. */
-int debug_threads;
-
-/* Include timestamps in debugging output. */
-int debug_timestamp;
-
-#if !defined (IN_PROCESS_AGENT)
-
-/* See debug.h. */
-
-void
-debug_set_output (const char *new_debug_file)
-{
- /* Close any existing file and reset to standard error. */
- if (debug_file != stderr)
- {
- fclose (debug_file);
- }
- debug_file = stderr;
-
- /* Catch empty filenames. */
- if (new_debug_file == nullptr || strlen (new_debug_file) == 0)
- return;
-
- FILE *fptr = fopen (new_debug_file, "w");
-
- if (fptr == nullptr)
- {
- debug_printf ("Cannot open %s for writing. %s. Switching to stderr.\n",
- new_debug_file, safe_strerror (errno));
- return;
- }
-
- debug_file = fptr;
-}
-
-#endif
-
-/* Print a debugging message.
- If the text begins a new line it is preceded by a timestamp.
- We don't get fancy with newline checking, we just check whether the
- previous call ended with "\n". */
-
-void
-debug_vprintf (const char *format, va_list ap)
-{
-#if !defined (IN_PROCESS_AGENT)
- /* N.B. Not thread safe, and can't be used, as is, with IPA. */
- static int new_line = 1;
-
- if (debug_timestamp && new_line)
- {
- using namespace std::chrono;
-
- steady_clock::time_point now = steady_clock::now ();
- seconds s = duration_cast<seconds> (now.time_since_epoch ());
- microseconds us = duration_cast<microseconds> (now.time_since_epoch ()) - s;
-
- fprintf (debug_file, "%ld.%06ld ", (long) s.count (), (long) us.count ());
- }
-#endif
-
- vfprintf (debug_file, format, ap);
-
-#if !defined (IN_PROCESS_AGENT)
- if (*format)
- new_line = format[strlen (format) - 1] == '\n';
-#endif
-}
-
-/* Flush debugging output.
- This is called, for example, when starting an inferior to ensure all debug
- output thus far appears before any inferior output. */
-
-void
-debug_flush (void)
-{
- fflush (debug_file);
-}
-
-/* Notify the user that the code is entering FUNCTION_NAME.
- FUNCTION_NAME is the name of the calling function, or NULL if unknown.
-
- This is intended to be called via the debug_enter macro. */
-
-void
-do_debug_enter (const char *function_name)
-{
- if (function_name != NULL)
- debug_printf (">>>> entering %s\n", function_name);
-}
-
-/* Notify the user that the code is exiting FUNCTION_NAME.
- FUNCTION_NAME is the name of the calling function, or NULL if unknown.
-
- This is intended to be called via the debug_exit macro. */
-
-void
-do_debug_exit (const char *function_name)
-{
- if (function_name != NULL)
- debug_printf ("<<<< exiting %s\n", function_name);
-}
-
-/* See debug.h. */
-
-ssize_t
-debug_write (const void *buf, size_t nbyte)
-{
- int fd = fileno (debug_file);
- return write (fd, buf, nbyte);
-}
--- /dev/null
+/* Debugging routines for the remote server for GDB.
+ Copyright (C) 2014-2020 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 <chrono>
+
+#if !defined (IN_PROCESS_AGENT)
+int remote_debug = 0;
+#endif
+
+/* Output file for debugging. Default to standard error. */
+FILE *debug_file = stderr;
+
+/* See debug.h. */
+int debug_threads;
+
+/* Include timestamps in debugging output. */
+int debug_timestamp;
+
+#if !defined (IN_PROCESS_AGENT)
+
+/* See debug.h. */
+
+void
+debug_set_output (const char *new_debug_file)
+{
+ /* Close any existing file and reset to standard error. */
+ if (debug_file != stderr)
+ {
+ fclose (debug_file);
+ }
+ debug_file = stderr;
+
+ /* Catch empty filenames. */
+ if (new_debug_file == nullptr || strlen (new_debug_file) == 0)
+ return;
+
+ FILE *fptr = fopen (new_debug_file, "w");
+
+ if (fptr == nullptr)
+ {
+ debug_printf ("Cannot open %s for writing. %s. Switching to stderr.\n",
+ new_debug_file, safe_strerror (errno));
+ return;
+ }
+
+ debug_file = fptr;
+}
+
+#endif
+
+/* Print a debugging message.
+ If the text begins a new line it is preceded by a timestamp.
+ We don't get fancy with newline checking, we just check whether the
+ previous call ended with "\n". */
+
+void
+debug_vprintf (const char *format, va_list ap)
+{
+#if !defined (IN_PROCESS_AGENT)
+ /* N.B. Not thread safe, and can't be used, as is, with IPA. */
+ static int new_line = 1;
+
+ if (debug_timestamp && new_line)
+ {
+ using namespace std::chrono;
+
+ steady_clock::time_point now = steady_clock::now ();
+ seconds s = duration_cast<seconds> (now.time_since_epoch ());
+ microseconds us = duration_cast<microseconds> (now.time_since_epoch ()) - s;
+
+ fprintf (debug_file, "%ld.%06ld ", (long) s.count (), (long) us.count ());
+ }
+#endif
+
+ vfprintf (debug_file, format, ap);
+
+#if !defined (IN_PROCESS_AGENT)
+ if (*format)
+ new_line = format[strlen (format) - 1] == '\n';
+#endif
+}
+
+/* Flush debugging output.
+ This is called, for example, when starting an inferior to ensure all debug
+ output thus far appears before any inferior output. */
+
+void
+debug_flush (void)
+{
+ fflush (debug_file);
+}
+
+/* Notify the user that the code is entering FUNCTION_NAME.
+ FUNCTION_NAME is the name of the calling function, or NULL if unknown.
+
+ This is intended to be called via the debug_enter macro. */
+
+void
+do_debug_enter (const char *function_name)
+{
+ if (function_name != NULL)
+ debug_printf (">>>> entering %s\n", function_name);
+}
+
+/* Notify the user that the code is exiting FUNCTION_NAME.
+ FUNCTION_NAME is the name of the calling function, or NULL if unknown.
+
+ This is intended to be called via the debug_exit macro. */
+
+void
+do_debug_exit (const char *function_name)
+{
+ if (function_name != NULL)
+ debug_printf ("<<<< exiting %s\n", function_name);
+}
+
+/* See debug.h. */
+
+ssize_t
+debug_write (const void *buf, size_t nbyte)
+{
+ int fd = fileno (debug_file);
+ return write (fd, buf, nbyte);
+}
+++ /dev/null
-/* Copyright (C) 2002-2020 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 "dll.h"
-
-#include <algorithm>
-
-/* An "unspecified" CORE_ADDR, for match_dll. */
-#define UNSPECIFIED_CORE_ADDR (~(CORE_ADDR) 0)
-
-std::list<dll_info> all_dlls;
-int dlls_changed;
-
-/* Record a newly loaded DLL at BASE_ADDR. */
-
-void
-loaded_dll (const char *name, CORE_ADDR base_addr)
-{
- all_dlls.emplace_back (name != NULL ? name : "", base_addr);
- dlls_changed = 1;
-}
-
-/* Record that the DLL with NAME and BASE_ADDR has been unloaded. */
-
-void
-unloaded_dll (const char *name, CORE_ADDR base_addr)
-{
- auto pred = [&] (const dll_info &dll)
- {
- if (base_addr != UNSPECIFIED_CORE_ADDR
- && base_addr == dll.base_addr)
- return true;
-
- if (name != NULL && dll.name == name)
- return true;
-
- return false;
- };
-
- auto iter = std::find_if (all_dlls.begin (), all_dlls.end (), pred);
-
- if (iter == all_dlls.end ())
- /* For some inferiors we might get unloaded_dll events without having
- a corresponding loaded_dll. In that case, the dll cannot be found
- in ALL_DLL, and there is nothing further for us to do.
-
- This has been observed when running 32bit executables on Windows64
- (i.e. through WOW64, the interface between the 32bits and 64bits
- worlds). In that case, the inferior always does some strange
- unloading of unnamed dll. */
- return;
- else
- {
- /* DLL has been found so remove the entry and free associated
- resources. */
- all_dlls.erase (iter);
- dlls_changed = 1;
- }
-}
-
-void
-clear_dlls (void)
-{
- all_dlls.clear ();
-}
--- /dev/null
+/* Copyright (C) 2002-2020 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 "dll.h"
+
+#include <algorithm>
+
+/* An "unspecified" CORE_ADDR, for match_dll. */
+#define UNSPECIFIED_CORE_ADDR (~(CORE_ADDR) 0)
+
+std::list<dll_info> all_dlls;
+int dlls_changed;
+
+/* Record a newly loaded DLL at BASE_ADDR. */
+
+void
+loaded_dll (const char *name, CORE_ADDR base_addr)
+{
+ all_dlls.emplace_back (name != NULL ? name : "", base_addr);
+ dlls_changed = 1;
+}
+
+/* Record that the DLL with NAME and BASE_ADDR has been unloaded. */
+
+void
+unloaded_dll (const char *name, CORE_ADDR base_addr)
+{
+ auto pred = [&] (const dll_info &dll)
+ {
+ if (base_addr != UNSPECIFIED_CORE_ADDR
+ && base_addr == dll.base_addr)
+ return true;
+
+ if (name != NULL && dll.name == name)
+ return true;
+
+ return false;
+ };
+
+ auto iter = std::find_if (all_dlls.begin (), all_dlls.end (), pred);
+
+ if (iter == all_dlls.end ())
+ /* For some inferiors we might get unloaded_dll events without having
+ a corresponding loaded_dll. In that case, the dll cannot be found
+ in ALL_DLL, and there is nothing further for us to do.
+
+ This has been observed when running 32bit executables on Windows64
+ (i.e. through WOW64, the interface between the 32bits and 64bits
+ worlds). In that case, the inferior always does some strange
+ unloading of unnamed dll. */
+ return;
+ else
+ {
+ /* DLL has been found so remove the entry and free associated
+ resources. */
+ all_dlls.erase (iter);
+ dlls_changed = 1;
+ }
+}
+
+void
+clear_dlls (void)
+{
+ all_dlls.clear ();
+}
+++ /dev/null
-/* Event loop machinery for the remote server for GDB.
- Copyright (C) 1999-2020 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/>. */
-
-/* Based on src/gdb/event-loop.c. */
-
-#include "server.h"
-
-#include <sys/types.h>
-#include "gdbsupport/gdb_sys_time.h"
-
-#ifdef USE_WIN32API
-#include <windows.h>
-#include <io.h>
-#endif
-
-#include <unistd.h>
-#include <queue>
-
-typedef int (event_handler_func) (gdb_fildes_t);
-
-/* Tell create_file_handler what events we are interested in. */
-
-#define GDB_READABLE (1<<1)
-#define GDB_WRITABLE (1<<2)
-#define GDB_EXCEPTION (1<<3)
-
-/* Events are queued by on the event_queue and serviced later
- on by do_one_event. An event can be, for instance, a file
- descriptor becoming ready to be read. Servicing an event simply
- means that the procedure PROC will be called. We have 2 queues,
- one for file handlers that we listen to in the event loop, and one
- for the file handlers+events that are ready. The procedure PROC
- associated with each event is always the same (handle_file_event).
- Its duty is to invoke the handler associated with the file
- descriptor whose state change generated the event, plus doing other
- cleanups and such. */
-
-struct gdb_event
- {
- /* Procedure to call to service this event. */
- event_handler_func *proc;
-
- /* File descriptor that is ready. */
- gdb_fildes_t fd;
- };
-
-/* Information about each file descriptor we register with the event
- loop. */
-
-typedef struct file_handler
- {
- /* File descriptor. */
- gdb_fildes_t fd;
-
- /* Events we want to monitor. */
- int mask;
-
- /* Events that have been seen since the last time. */
- int ready_mask;
-
- /* Procedure to call when fd is ready. */
- handler_func *proc;
-
- /* Argument to pass to proc. */
- gdb_client_data client_data;
-
- /* Was an error detected on this fd? */
- int error;
-
- /* Next registered file descriptor. */
- struct file_handler *next_file;
- }
-file_handler;
-
-typedef gdb::unique_xmalloc_ptr<gdb_event> gdb_event_up;
-
-static std::queue<gdb_event_up, std::list<gdb_event_up>> event_queue;
-
-/* Gdb_notifier is just a list of file descriptors gdb is interested
- in. These are the input file descriptor, and the target file
- descriptor. Each of the elements in the gdb_notifier list is
- basically a description of what kind of events gdb is interested
- in, for each fd. */
-
-static struct
- {
- /* Ptr to head of file handler list. */
- file_handler *first_file_handler;
-
- /* Masks to be used in the next call to select. Bits are set in
- response to calls to create_file_handler. */
- fd_set check_masks[3];
-
- /* What file descriptors were found ready by select. */
- fd_set ready_masks[3];
-
- /* Number of valid bits (highest fd value + 1). (for select) */
- int num_fds;
- }
-gdb_notifier;
-
-/* Callbacks are just routines that are executed before waiting for the
- next event. In GDB this is struct gdb_timer. We don't need timers
- so rather than copy all that complexity in gdbserver, we provide what
- we need, but we do so in a way that if/when the day comes that we need
- that complexity, it'll be easier to add - replace callbacks with timers
- and use a delta of zero (which is all gdb currently uses timers for anyway).
-
- PROC will be executed before gdbserver goes to sleep to wait for the
- next event. */
-
-struct callback_event
- {
- int id;
- callback_handler_func *proc;
- gdb_client_data data;
- struct callback_event *next;
- };
-
-/* Table of registered callbacks. */
-
-static struct
- {
- struct callback_event *first;
- struct callback_event *last;
-
- /* Id of the last callback created. */
- int num_callbacks;
- }
-callback_list;
-
-void
-initialize_event_loop (void)
-{
-}
-
-/* Process one event. If an event was processed, 1 is returned
- otherwise 0 is returned. Scan the queue from head to tail,
- processing therefore the high priority events first, by invoking
- the associated event handler procedure. */
-
-static int
-process_event (void)
-{
- /* Let's get rid of the event from the event queue. We need to
- do this now because while processing the event, since the
- proc function could end up jumping out to the caller of this
- function. In that case, we would have on the event queue an
- event which has been processed, but not deleted. */
- if (!event_queue.empty ())
- {
- gdb_event_up event_ptr = std::move (event_queue.front ());
- event_queue.pop ();
-
- event_handler_func *proc = event_ptr->proc;
- gdb_fildes_t fd = event_ptr->fd;
-
- /* Now call the procedure associated with the event. */
- if ((*proc) (fd))
- return -1;
- return 1;
- }
-
- /* This is the case if there are no event on the event queue. */
- return 0;
-}
-
-/* Append PROC to the callback list.
- The result is the "id" of the callback that can be passed back to
- delete_callback_event. */
-
-int
-append_callback_event (callback_handler_func *proc, gdb_client_data data)
-{
- struct callback_event *event_ptr = XNEW (struct callback_event);
-
- event_ptr->id = callback_list.num_callbacks++;
- event_ptr->proc = proc;
- event_ptr->data = data;
- event_ptr->next = NULL;
- if (callback_list.first == NULL)
- callback_list.first = event_ptr;
- if (callback_list.last != NULL)
- callback_list.last->next = event_ptr;
- callback_list.last = event_ptr;
- return event_ptr->id;
-}
-
-/* Delete callback ID.
- It is not an error callback ID doesn't exist. */
-
-void
-delete_callback_event (int id)
-{
- struct callback_event **p;
-
- for (p = &callback_list.first; *p != NULL; p = &(*p)->next)
- {
- struct callback_event *event_ptr = *p;
-
- if (event_ptr->id == id)
- {
- *p = event_ptr->next;
- if (event_ptr == callback_list.last)
- callback_list.last = NULL;
- free (event_ptr);
- break;
- }
- }
-}
-
-/* Run the next callback.
- The result is 1 if a callback was called and event processing
- should continue, -1 if the callback wants the event loop to exit,
- and 0 if there are no more callbacks. */
-
-static int
-process_callback (void)
-{
- struct callback_event *event_ptr;
-
- event_ptr = callback_list.first;
- if (event_ptr != NULL)
- {
- callback_handler_func *proc = event_ptr->proc;
- gdb_client_data data = event_ptr->data;
-
- /* Remove the event before calling PROC,
- more events may get added by PROC. */
- callback_list.first = event_ptr->next;
- if (callback_list.first == NULL)
- callback_list.last = NULL;
- free (event_ptr);
- if ((*proc) (data))
- return -1;
- return 1;
- }
-
- return 0;
-}
-
-/* Add a file handler/descriptor to the list of descriptors we are
- interested in. FD is the file descriptor for the file/stream to be
- listened to. MASK is a combination of READABLE, WRITABLE,
- EXCEPTION. PROC is the procedure that will be called when an event
- occurs for FD. CLIENT_DATA is the argument to pass to PROC. */
-
-static void
-create_file_handler (gdb_fildes_t fd, int mask, handler_func *proc,
- gdb_client_data client_data)
-{
- file_handler *file_ptr;
-
- /* Do we already have a file handler for this file? (We may be
- changing its associated procedure). */
- for (file_ptr = gdb_notifier.first_file_handler;
- file_ptr != NULL;
- file_ptr = file_ptr->next_file)
- if (file_ptr->fd == fd)
- break;
-
- /* It is a new file descriptor. Add it to the list. Otherwise,
- just change the data associated with it. */
- if (file_ptr == NULL)
- {
- file_ptr = XNEW (struct file_handler);
- file_ptr->fd = fd;
- file_ptr->ready_mask = 0;
- file_ptr->next_file = gdb_notifier.first_file_handler;
- gdb_notifier.first_file_handler = file_ptr;
-
- if (mask & GDB_READABLE)
- FD_SET (fd, &gdb_notifier.check_masks[0]);
- else
- FD_CLR (fd, &gdb_notifier.check_masks[0]);
-
- if (mask & GDB_WRITABLE)
- FD_SET (fd, &gdb_notifier.check_masks[1]);
- else
- FD_CLR (fd, &gdb_notifier.check_masks[1]);
-
- if (mask & GDB_EXCEPTION)
- FD_SET (fd, &gdb_notifier.check_masks[2]);
- else
- FD_CLR (fd, &gdb_notifier.check_masks[2]);
-
- if (gdb_notifier.num_fds <= fd)
- gdb_notifier.num_fds = fd + 1;
- }
-
- file_ptr->proc = proc;
- file_ptr->client_data = client_data;
- file_ptr->mask = mask;
-}
-
-/* Wrapper function for create_file_handler. */
-
-void
-add_file_handler (gdb_fildes_t fd,
- handler_func *proc, gdb_client_data client_data)
-{
- create_file_handler (fd, GDB_READABLE | GDB_EXCEPTION, proc, client_data);
-}
-
-/* Remove the file descriptor FD from the list of monitored fd's:
- i.e. we don't care anymore about events on the FD. */
-
-void
-delete_file_handler (gdb_fildes_t fd)
-{
- file_handler *file_ptr, *prev_ptr = NULL;
- int i;
-
- /* Find the entry for the given file. */
-
- for (file_ptr = gdb_notifier.first_file_handler;
- file_ptr != NULL;
- file_ptr = file_ptr->next_file)
- if (file_ptr->fd == fd)
- break;
-
- if (file_ptr == NULL)
- return;
-
- if (file_ptr->mask & GDB_READABLE)
- FD_CLR (fd, &gdb_notifier.check_masks[0]);
- if (file_ptr->mask & GDB_WRITABLE)
- FD_CLR (fd, &gdb_notifier.check_masks[1]);
- if (file_ptr->mask & GDB_EXCEPTION)
- FD_CLR (fd, &gdb_notifier.check_masks[2]);
-
- /* Find current max fd. */
-
- if ((fd + 1) == gdb_notifier.num_fds)
- {
- gdb_notifier.num_fds--;
- for (i = gdb_notifier.num_fds; i; i--)
- {
- if (FD_ISSET (i - 1, &gdb_notifier.check_masks[0])
- || FD_ISSET (i - 1, &gdb_notifier.check_masks[1])
- || FD_ISSET (i - 1, &gdb_notifier.check_masks[2]))
- break;
- }
- gdb_notifier.num_fds = i;
- }
-
- /* Deactivate the file descriptor, by clearing its mask, so that it
- will not fire again. */
-
- file_ptr->mask = 0;
-
- /* Get rid of the file handler in the file handler list. */
- if (file_ptr == gdb_notifier.first_file_handler)
- gdb_notifier.first_file_handler = file_ptr->next_file;
- else
- {
- for (prev_ptr = gdb_notifier.first_file_handler;
- prev_ptr->next_file != file_ptr;
- prev_ptr = prev_ptr->next_file)
- ;
- prev_ptr->next_file = file_ptr->next_file;
- }
- free (file_ptr);
-}
-
-/* Handle the given event by calling the procedure associated to the
- corresponding file handler. Called by process_event indirectly,
- through event_ptr->proc. EVENT_FILE_DESC is file descriptor of the
- event in the front of the event queue. */
-
-static int
-handle_file_event (gdb_fildes_t event_file_desc)
-{
- file_handler *file_ptr;
- int mask;
-
- /* Search the file handler list to find one that matches the fd in
- the event. */
- for (file_ptr = gdb_notifier.first_file_handler; file_ptr != NULL;
- file_ptr = file_ptr->next_file)
- {
- if (file_ptr->fd == event_file_desc)
- {
- /* See if the desired events (mask) match the received
- events (ready_mask). */
-
- if (file_ptr->ready_mask & GDB_EXCEPTION)
- {
- warning ("Exception condition detected on fd %s",
- pfildes (file_ptr->fd));
- file_ptr->error = 1;
- }
- else
- file_ptr->error = 0;
- mask = file_ptr->ready_mask & file_ptr->mask;
-
- /* Clear the received events for next time around. */
- file_ptr->ready_mask = 0;
-
- /* If there was a match, then call the handler. */
- if (mask != 0)
- {
- if ((*file_ptr->proc) (file_ptr->error,
- file_ptr->client_data) < 0)
- return -1;
- }
- break;
- }
- }
-
- return 0;
-}
-
-/* Create a file event, to be enqueued in the event queue for
- processing. The procedure associated to this event is always
- handle_file_event, which will in turn invoke the one that was
- associated to FD when it was registered with the event loop. */
-
-static gdb_event *
-create_file_event (gdb_fildes_t fd)
-{
- gdb_event *file_event_ptr;
-
- file_event_ptr = XNEW (gdb_event);
- file_event_ptr->proc = handle_file_event;
- file_event_ptr->fd = fd;
-
- return file_event_ptr;
-}
-
-/* Called by do_one_event to wait for new events on the monitored file
- descriptors. Queue file events as they are detected by the poll.
- If there are no events, this function will block in the call to
- select. Return -1 if there are no files descriptors to monitor,
- otherwise return 0. */
-
-static int
-wait_for_event (void)
-{
- file_handler *file_ptr;
- int num_found = 0;
-
- /* Make sure all output is done before getting another event. */
- fflush (stdout);
- fflush (stderr);
-
- if (gdb_notifier.num_fds == 0)
- return -1;
-
- gdb_notifier.ready_masks[0] = gdb_notifier.check_masks[0];
- gdb_notifier.ready_masks[1] = gdb_notifier.check_masks[1];
- gdb_notifier.ready_masks[2] = gdb_notifier.check_masks[2];
- num_found = select (gdb_notifier.num_fds,
- &gdb_notifier.ready_masks[0],
- &gdb_notifier.ready_masks[1],
- &gdb_notifier.ready_masks[2],
- NULL);
-
- /* Clear the masks after an error from select. */
- if (num_found == -1)
- {
- FD_ZERO (&gdb_notifier.ready_masks[0]);
- FD_ZERO (&gdb_notifier.ready_masks[1]);
- FD_ZERO (&gdb_notifier.ready_masks[2]);
-#ifdef EINTR
- /* Dont print anything if we got a signal, let gdb handle
- it. */
- if (errno != EINTR)
- perror_with_name ("select");
-#endif
- }
-
- /* Enqueue all detected file events. */
-
- for (file_ptr = gdb_notifier.first_file_handler;
- file_ptr != NULL && num_found > 0;
- file_ptr = file_ptr->next_file)
- {
- int mask = 0;
-
- if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[0]))
- mask |= GDB_READABLE;
- if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[1]))
- mask |= GDB_WRITABLE;
- if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[2]))
- mask |= GDB_EXCEPTION;
-
- if (!mask)
- continue;
- else
- num_found--;
-
- /* Enqueue an event only if this is still a new event for this
- fd. */
-
- if (file_ptr->ready_mask == 0)
- {
- gdb_event *file_event_ptr = create_file_event (file_ptr->fd);
-
- event_queue.emplace (file_event_ptr);
- }
- file_ptr->ready_mask = mask;
- }
-
- return 0;
-}
-
-/* Start up the event loop. This is the entry point to the event
- loop. */
-
-void
-start_event_loop (void)
-{
- /* Loop until there is nothing to do. This is the entry point to
- the event loop engine. If nothing is ready at this time, wait
- for something to happen (via wait_for_event), then process it.
- Return when there are no longer event sources to wait for. */
-
- while (1)
- {
- /* Any events already waiting in the queue? */
- int res = process_event ();
-
- /* Did the event handler want the event loop to stop? */
- if (res == -1)
- return;
-
- if (res)
- continue;
-
- /* Process any queued callbacks before we go to sleep. */
- res = process_callback ();
-
- /* Did the callback want the event loop to stop? */
- if (res == -1)
- return;
-
- if (res)
- continue;
-
- /* Wait for a new event. If wait_for_event returns -1, we
- should get out because this means that there are no event
- sources left. This will make the event loop stop, and the
- application exit. */
-
- if (wait_for_event () < 0)
- return;
- }
-
- /* We are done with the event loop. There are no more event sources
- to listen to. So we exit gdbserver. */
-}
--- /dev/null
+/* Event loop machinery for the remote server for GDB.
+ Copyright (C) 1999-2020 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/>. */
+
+/* Based on src/gdb/event-loop.c. */
+
+#include "server.h"
+
+#include <sys/types.h>
+#include "gdbsupport/gdb_sys_time.h"
+
+#ifdef USE_WIN32API
+#include <windows.h>
+#include <io.h>
+#endif
+
+#include <unistd.h>
+#include <queue>
+
+typedef int (event_handler_func) (gdb_fildes_t);
+
+/* Tell create_file_handler what events we are interested in. */
+
+#define GDB_READABLE (1<<1)
+#define GDB_WRITABLE (1<<2)
+#define GDB_EXCEPTION (1<<3)
+
+/* Events are queued by on the event_queue and serviced later
+ on by do_one_event. An event can be, for instance, a file
+ descriptor becoming ready to be read. Servicing an event simply
+ means that the procedure PROC will be called. We have 2 queues,
+ one for file handlers that we listen to in the event loop, and one
+ for the file handlers+events that are ready. The procedure PROC
+ associated with each event is always the same (handle_file_event).
+ Its duty is to invoke the handler associated with the file
+ descriptor whose state change generated the event, plus doing other
+ cleanups and such. */
+
+struct gdb_event
+ {
+ /* Procedure to call to service this event. */
+ event_handler_func *proc;
+
+ /* File descriptor that is ready. */
+ gdb_fildes_t fd;
+ };
+
+/* Information about each file descriptor we register with the event
+ loop. */
+
+typedef struct file_handler
+ {
+ /* File descriptor. */
+ gdb_fildes_t fd;
+
+ /* Events we want to monitor. */
+ int mask;
+
+ /* Events that have been seen since the last time. */
+ int ready_mask;
+
+ /* Procedure to call when fd is ready. */
+ handler_func *proc;
+
+ /* Argument to pass to proc. */
+ gdb_client_data client_data;
+
+ /* Was an error detected on this fd? */
+ int error;
+
+ /* Next registered file descriptor. */
+ struct file_handler *next_file;
+ }
+file_handler;
+
+typedef gdb::unique_xmalloc_ptr<gdb_event> gdb_event_up;
+
+static std::queue<gdb_event_up, std::list<gdb_event_up>> event_queue;
+
+/* Gdb_notifier is just a list of file descriptors gdb is interested
+ in. These are the input file descriptor, and the target file
+ descriptor. Each of the elements in the gdb_notifier list is
+ basically a description of what kind of events gdb is interested
+ in, for each fd. */
+
+static struct
+ {
+ /* Ptr to head of file handler list. */
+ file_handler *first_file_handler;
+
+ /* Masks to be used in the next call to select. Bits are set in
+ response to calls to create_file_handler. */
+ fd_set check_masks[3];
+
+ /* What file descriptors were found ready by select. */
+ fd_set ready_masks[3];
+
+ /* Number of valid bits (highest fd value + 1). (for select) */
+ int num_fds;
+ }
+gdb_notifier;
+
+/* Callbacks are just routines that are executed before waiting for the
+ next event. In GDB this is struct gdb_timer. We don't need timers
+ so rather than copy all that complexity in gdbserver, we provide what
+ we need, but we do so in a way that if/when the day comes that we need
+ that complexity, it'll be easier to add - replace callbacks with timers
+ and use a delta of zero (which is all gdb currently uses timers for anyway).
+
+ PROC will be executed before gdbserver goes to sleep to wait for the
+ next event. */
+
+struct callback_event
+ {
+ int id;
+ callback_handler_func *proc;
+ gdb_client_data data;
+ struct callback_event *next;
+ };
+
+/* Table of registered callbacks. */
+
+static struct
+ {
+ struct callback_event *first;
+ struct callback_event *last;
+
+ /* Id of the last callback created. */
+ int num_callbacks;
+ }
+callback_list;
+
+void
+initialize_event_loop (void)
+{
+}
+
+/* Process one event. If an event was processed, 1 is returned
+ otherwise 0 is returned. Scan the queue from head to tail,
+ processing therefore the high priority events first, by invoking
+ the associated event handler procedure. */
+
+static int
+process_event (void)
+{
+ /* Let's get rid of the event from the event queue. We need to
+ do this now because while processing the event, since the
+ proc function could end up jumping out to the caller of this
+ function. In that case, we would have on the event queue an
+ event which has been processed, but not deleted. */
+ if (!event_queue.empty ())
+ {
+ gdb_event_up event_ptr = std::move (event_queue.front ());
+ event_queue.pop ();
+
+ event_handler_func *proc = event_ptr->proc;
+ gdb_fildes_t fd = event_ptr->fd;
+
+ /* Now call the procedure associated with the event. */
+ if ((*proc) (fd))
+ return -1;
+ return 1;
+ }
+
+ /* This is the case if there are no event on the event queue. */
+ return 0;
+}
+
+/* Append PROC to the callback list.
+ The result is the "id" of the callback that can be passed back to
+ delete_callback_event. */
+
+int
+append_callback_event (callback_handler_func *proc, gdb_client_data data)
+{
+ struct callback_event *event_ptr = XNEW (struct callback_event);
+
+ event_ptr->id = callback_list.num_callbacks++;
+ event_ptr->proc = proc;
+ event_ptr->data = data;
+ event_ptr->next = NULL;
+ if (callback_list.first == NULL)
+ callback_list.first = event_ptr;
+ if (callback_list.last != NULL)
+ callback_list.last->next = event_ptr;
+ callback_list.last = event_ptr;
+ return event_ptr->id;
+}
+
+/* Delete callback ID.
+ It is not an error callback ID doesn't exist. */
+
+void
+delete_callback_event (int id)
+{
+ struct callback_event **p;
+
+ for (p = &callback_list.first; *p != NULL; p = &(*p)->next)
+ {
+ struct callback_event *event_ptr = *p;
+
+ if (event_ptr->id == id)
+ {
+ *p = event_ptr->next;
+ if (event_ptr == callback_list.last)
+ callback_list.last = NULL;
+ free (event_ptr);
+ break;
+ }
+ }
+}
+
+/* Run the next callback.
+ The result is 1 if a callback was called and event processing
+ should continue, -1 if the callback wants the event loop to exit,
+ and 0 if there are no more callbacks. */
+
+static int
+process_callback (void)
+{
+ struct callback_event *event_ptr;
+
+ event_ptr = callback_list.first;
+ if (event_ptr != NULL)
+ {
+ callback_handler_func *proc = event_ptr->proc;
+ gdb_client_data data = event_ptr->data;
+
+ /* Remove the event before calling PROC,
+ more events may get added by PROC. */
+ callback_list.first = event_ptr->next;
+ if (callback_list.first == NULL)
+ callback_list.last = NULL;
+ free (event_ptr);
+ if ((*proc) (data))
+ return -1;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Add a file handler/descriptor to the list of descriptors we are
+ interested in. FD is the file descriptor for the file/stream to be
+ listened to. MASK is a combination of READABLE, WRITABLE,
+ EXCEPTION. PROC is the procedure that will be called when an event
+ occurs for FD. CLIENT_DATA is the argument to pass to PROC. */
+
+static void
+create_file_handler (gdb_fildes_t fd, int mask, handler_func *proc,
+ gdb_client_data client_data)
+{
+ file_handler *file_ptr;
+
+ /* Do we already have a file handler for this file? (We may be
+ changing its associated procedure). */
+ for (file_ptr = gdb_notifier.first_file_handler;
+ file_ptr != NULL;
+ file_ptr = file_ptr->next_file)
+ if (file_ptr->fd == fd)
+ break;
+
+ /* It is a new file descriptor. Add it to the list. Otherwise,
+ just change the data associated with it. */
+ if (file_ptr == NULL)
+ {
+ file_ptr = XNEW (struct file_handler);
+ file_ptr->fd = fd;
+ file_ptr->ready_mask = 0;
+ file_ptr->next_file = gdb_notifier.first_file_handler;
+ gdb_notifier.first_file_handler = file_ptr;
+
+ if (mask & GDB_READABLE)
+ FD_SET (fd, &gdb_notifier.check_masks[0]);
+ else
+ FD_CLR (fd, &gdb_notifier.check_masks[0]);
+
+ if (mask & GDB_WRITABLE)
+ FD_SET (fd, &gdb_notifier.check_masks[1]);
+ else
+ FD_CLR (fd, &gdb_notifier.check_masks[1]);
+
+ if (mask & GDB_EXCEPTION)
+ FD_SET (fd, &gdb_notifier.check_masks[2]);
+ else
+ FD_CLR (fd, &gdb_notifier.check_masks[2]);
+
+ if (gdb_notifier.num_fds <= fd)
+ gdb_notifier.num_fds = fd + 1;
+ }
+
+ file_ptr->proc = proc;
+ file_ptr->client_data = client_data;
+ file_ptr->mask = mask;
+}
+
+/* Wrapper function for create_file_handler. */
+
+void
+add_file_handler (gdb_fildes_t fd,
+ handler_func *proc, gdb_client_data client_data)
+{
+ create_file_handler (fd, GDB_READABLE | GDB_EXCEPTION, proc, client_data);
+}
+
+/* Remove the file descriptor FD from the list of monitored fd's:
+ i.e. we don't care anymore about events on the FD. */
+
+void
+delete_file_handler (gdb_fildes_t fd)
+{
+ file_handler *file_ptr, *prev_ptr = NULL;
+ int i;
+
+ /* Find the entry for the given file. */
+
+ for (file_ptr = gdb_notifier.first_file_handler;
+ file_ptr != NULL;
+ file_ptr = file_ptr->next_file)
+ if (file_ptr->fd == fd)
+ break;
+
+ if (file_ptr == NULL)
+ return;
+
+ if (file_ptr->mask & GDB_READABLE)
+ FD_CLR (fd, &gdb_notifier.check_masks[0]);
+ if (file_ptr->mask & GDB_WRITABLE)
+ FD_CLR (fd, &gdb_notifier.check_masks[1]);
+ if (file_ptr->mask & GDB_EXCEPTION)
+ FD_CLR (fd, &gdb_notifier.check_masks[2]);
+
+ /* Find current max fd. */
+
+ if ((fd + 1) == gdb_notifier.num_fds)
+ {
+ gdb_notifier.num_fds--;
+ for (i = gdb_notifier.num_fds; i; i--)
+ {
+ if (FD_ISSET (i - 1, &gdb_notifier.check_masks[0])
+ || FD_ISSET (i - 1, &gdb_notifier.check_masks[1])
+ || FD_ISSET (i - 1, &gdb_notifier.check_masks[2]))
+ break;
+ }
+ gdb_notifier.num_fds = i;
+ }
+
+ /* Deactivate the file descriptor, by clearing its mask, so that it
+ will not fire again. */
+
+ file_ptr->mask = 0;
+
+ /* Get rid of the file handler in the file handler list. */
+ if (file_ptr == gdb_notifier.first_file_handler)
+ gdb_notifier.first_file_handler = file_ptr->next_file;
+ else
+ {
+ for (prev_ptr = gdb_notifier.first_file_handler;
+ prev_ptr->next_file != file_ptr;
+ prev_ptr = prev_ptr->next_file)
+ ;
+ prev_ptr->next_file = file_ptr->next_file;
+ }
+ free (file_ptr);
+}
+
+/* Handle the given event by calling the procedure associated to the
+ corresponding file handler. Called by process_event indirectly,
+ through event_ptr->proc. EVENT_FILE_DESC is file descriptor of the
+ event in the front of the event queue. */
+
+static int
+handle_file_event (gdb_fildes_t event_file_desc)
+{
+ file_handler *file_ptr;
+ int mask;
+
+ /* Search the file handler list to find one that matches the fd in
+ the event. */
+ for (file_ptr = gdb_notifier.first_file_handler; file_ptr != NULL;
+ file_ptr = file_ptr->next_file)
+ {
+ if (file_ptr->fd == event_file_desc)
+ {
+ /* See if the desired events (mask) match the received
+ events (ready_mask). */
+
+ if (file_ptr->ready_mask & GDB_EXCEPTION)
+ {
+ warning ("Exception condition detected on fd %s",
+ pfildes (file_ptr->fd));
+ file_ptr->error = 1;
+ }
+ else
+ file_ptr->error = 0;
+ mask = file_ptr->ready_mask & file_ptr->mask;
+
+ /* Clear the received events for next time around. */
+ file_ptr->ready_mask = 0;
+
+ /* If there was a match, then call the handler. */
+ if (mask != 0)
+ {
+ if ((*file_ptr->proc) (file_ptr->error,
+ file_ptr->client_data) < 0)
+ return -1;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* Create a file event, to be enqueued in the event queue for
+ processing. The procedure associated to this event is always
+ handle_file_event, which will in turn invoke the one that was
+ associated to FD when it was registered with the event loop. */
+
+static gdb_event *
+create_file_event (gdb_fildes_t fd)
+{
+ gdb_event *file_event_ptr;
+
+ file_event_ptr = XNEW (gdb_event);
+ file_event_ptr->proc = handle_file_event;
+ file_event_ptr->fd = fd;
+
+ return file_event_ptr;
+}
+
+/* Called by do_one_event to wait for new events on the monitored file
+ descriptors. Queue file events as they are detected by the poll.
+ If there are no events, this function will block in the call to
+ select. Return -1 if there are no files descriptors to monitor,
+ otherwise return 0. */
+
+static int
+wait_for_event (void)
+{
+ file_handler *file_ptr;
+ int num_found = 0;
+
+ /* Make sure all output is done before getting another event. */
+ fflush (stdout);
+ fflush (stderr);
+
+ if (gdb_notifier.num_fds == 0)
+ return -1;
+
+ gdb_notifier.ready_masks[0] = gdb_notifier.check_masks[0];
+ gdb_notifier.ready_masks[1] = gdb_notifier.check_masks[1];
+ gdb_notifier.ready_masks[2] = gdb_notifier.check_masks[2];
+ num_found = select (gdb_notifier.num_fds,
+ &gdb_notifier.ready_masks[0],
+ &gdb_notifier.ready_masks[1],
+ &gdb_notifier.ready_masks[2],
+ NULL);
+
+ /* Clear the masks after an error from select. */
+ if (num_found == -1)
+ {
+ FD_ZERO (&gdb_notifier.ready_masks[0]);
+ FD_ZERO (&gdb_notifier.ready_masks[1]);
+ FD_ZERO (&gdb_notifier.ready_masks[2]);
+#ifdef EINTR
+ /* Dont print anything if we got a signal, let gdb handle
+ it. */
+ if (errno != EINTR)
+ perror_with_name ("select");
+#endif
+ }
+
+ /* Enqueue all detected file events. */
+
+ for (file_ptr = gdb_notifier.first_file_handler;
+ file_ptr != NULL && num_found > 0;
+ file_ptr = file_ptr->next_file)
+ {
+ int mask = 0;
+
+ if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[0]))
+ mask |= GDB_READABLE;
+ if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[1]))
+ mask |= GDB_WRITABLE;
+ if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[2]))
+ mask |= GDB_EXCEPTION;
+
+ if (!mask)
+ continue;
+ else
+ num_found--;
+
+ /* Enqueue an event only if this is still a new event for this
+ fd. */
+
+ if (file_ptr->ready_mask == 0)
+ {
+ gdb_event *file_event_ptr = create_file_event (file_ptr->fd);
+
+ event_queue.emplace (file_event_ptr);
+ }
+ file_ptr->ready_mask = mask;
+ }
+
+ return 0;
+}
+
+/* Start up the event loop. This is the entry point to the event
+ loop. */
+
+void
+start_event_loop (void)
+{
+ /* Loop until there is nothing to do. This is the entry point to
+ the event loop engine. If nothing is ready at this time, wait
+ for something to happen (via wait_for_event), then process it.
+ Return when there are no longer event sources to wait for. */
+
+ while (1)
+ {
+ /* Any events already waiting in the queue? */
+ int res = process_event ();
+
+ /* Did the event handler want the event loop to stop? */
+ if (res == -1)
+ return;
+
+ if (res)
+ continue;
+
+ /* Process any queued callbacks before we go to sleep. */
+ res = process_callback ();
+
+ /* Did the callback want the event loop to stop? */
+ if (res == -1)
+ return;
+
+ if (res)
+ continue;
+
+ /* Wait for a new event. If wait_for_event returns -1, we
+ should get out because this means that there are no event
+ sources left. This will make the event loop stop, and the
+ application exit. */
+
+ if (wait_for_event () < 0)
+ return;
+ }
+
+ /* We are done with the event loop. There are no more event sources
+ to listen to. So we exit gdbserver. */
+}
+++ /dev/null
-/* Fork a Unix child process, and set up to debug it, for GDBserver.
- Copyright (C) 1989-2020 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 "gdbsupport/job-control.h"
-#include "nat/fork-inferior.h"
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-
-#ifdef SIGTTOU
-/* A file descriptor for the controlling terminal. */
-static int terminal_fd;
-
-/* TERMINAL_FD's original foreground group. */
-static pid_t old_foreground_pgrp;
-
-/* Hand back terminal ownership to the original foreground group. */
-
-static void
-restore_old_foreground_pgrp (void)
-{
- tcsetpgrp (terminal_fd, old_foreground_pgrp);
-}
-#endif
-
-/* See nat/fork-inferior.h. */
-
-void
-prefork_hook (const char *args)
-{
- client_state &cs = get_client_state ();
- if (debug_threads)
- {
- debug_printf ("args: %s\n", args);
- debug_flush ();
- }
-
-#ifdef SIGTTOU
- signal (SIGTTOU, SIG_DFL);
- signal (SIGTTIN, SIG_DFL);
-#endif
-
- /* Clear this so the backend doesn't get confused, thinking
- CONT_THREAD died, and it needs to resume all threads. */
- cs.cont_thread = null_ptid;
-}
-
-/* See nat/fork-inferior.h. */
-
-void
-postfork_hook (pid_t pid)
-{
-}
-
-/* See nat/fork-inferior.h. */
-
-void
-postfork_child_hook ()
-{
- /* This is set to the result of setpgrp, which if vforked, will be
- visible to you in the parent process. It's only used by humans
- for debugging. */
- static int debug_setpgrp = 657473;
-
- debug_setpgrp = gdb_setpgid ();
- if (debug_setpgrp == -1)
- perror (_("setpgrp failed in child"));
-}
-
-/* See nat/fork-inferior.h. */
-
-void
-gdb_flush_out_err ()
-{
- fflush (stdout);
- fflush (stderr);
-}
-
-/* See server.h. */
-
-void
-post_fork_inferior (int pid, const char *program)
-{
- client_state &cs = get_client_state ();
-#ifdef SIGTTOU
- signal (SIGTTOU, SIG_IGN);
- signal (SIGTTIN, SIG_IGN);
- terminal_fd = fileno (stderr);
- old_foreground_pgrp = tcgetpgrp (terminal_fd);
- tcsetpgrp (terminal_fd, pid);
- atexit (restore_old_foreground_pgrp);
-#endif
-
- startup_inferior (the_target, pid,
- START_INFERIOR_TRAPS_EXPECTED,
- &cs.last_status, &cs.last_ptid);
- current_thread->last_resume_kind = resume_stop;
- current_thread->last_status = cs.last_status;
- signal_pid = pid;
- target_post_create_inferior ();
- fprintf (stderr, "Process %s created; pid = %d\n", program, pid);
- fflush (stderr);
-}
--- /dev/null
+/* Fork a Unix child process, and set up to debug it, for GDBserver.
+ Copyright (C) 1989-2020 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 "gdbsupport/job-control.h"
+#include "nat/fork-inferior.h"
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef SIGTTOU
+/* A file descriptor for the controlling terminal. */
+static int terminal_fd;
+
+/* TERMINAL_FD's original foreground group. */
+static pid_t old_foreground_pgrp;
+
+/* Hand back terminal ownership to the original foreground group. */
+
+static void
+restore_old_foreground_pgrp (void)
+{
+ tcsetpgrp (terminal_fd, old_foreground_pgrp);
+}
+#endif
+
+/* See nat/fork-inferior.h. */
+
+void
+prefork_hook (const char *args)
+{
+ client_state &cs = get_client_state ();
+ if (debug_threads)
+ {
+ debug_printf ("args: %s\n", args);
+ debug_flush ();
+ }
+
+#ifdef SIGTTOU
+ signal (SIGTTOU, SIG_DFL);
+ signal (SIGTTIN, SIG_DFL);
+#endif
+
+ /* Clear this so the backend doesn't get confused, thinking
+ CONT_THREAD died, and it needs to resume all threads. */
+ cs.cont_thread = null_ptid;
+}
+
+/* See nat/fork-inferior.h. */
+
+void
+postfork_hook (pid_t pid)
+{
+}
+
+/* See nat/fork-inferior.h. */
+
+void
+postfork_child_hook ()
+{
+ /* This is set to the result of setpgrp, which if vforked, will be
+ visible to you in the parent process. It's only used by humans
+ for debugging. */
+ static int debug_setpgrp = 657473;
+
+ debug_setpgrp = gdb_setpgid ();
+ if (debug_setpgrp == -1)
+ perror (_("setpgrp failed in child"));
+}
+
+/* See nat/fork-inferior.h. */
+
+void
+gdb_flush_out_err ()
+{
+ fflush (stdout);
+ fflush (stderr);
+}
+
+/* See server.h. */
+
+void
+post_fork_inferior (int pid, const char *program)
+{
+ client_state &cs = get_client_state ();
+#ifdef SIGTTOU
+ signal (SIGTTOU, SIG_IGN);
+ signal (SIGTTIN, SIG_IGN);
+ terminal_fd = fileno (stderr);
+ old_foreground_pgrp = tcgetpgrp (terminal_fd);
+ tcsetpgrp (terminal_fd, pid);
+ atexit (restore_old_foreground_pgrp);
+#endif
+
+ startup_inferior (the_target, pid,
+ START_INFERIOR_TRAPS_EXPECTED,
+ &cs.last_status, &cs.last_ptid);
+ current_thread->last_resume_kind = resume_stop;
+ current_thread->last_status = cs.last_status;
+ signal_pid = pid;
+ target_post_create_inferior ();
+ fprintf (stderr, "Process %s created; pid = %d\n", program, pid);
+ fflush (stderr);
+}
+++ /dev/null
-/* Replay a remote debug session logfile for GDB.
- Copyright (C) 1996-2020 Free Software Foundation, Inc.
- Written by Fred Fish (fnf@cygnus.com) from pieces of gdbserver.
-
- 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 "gdbsupport/common-defs.h"
-
-#undef PACKAGE
-#undef PACKAGE_NAME
-#undef PACKAGE_VERSION
-#undef PACKAGE_STRING
-#undef PACKAGE_TARNAME
-
-#include <config.h>
-#include "gdbsupport/version.h"
-
-#if HAVE_SYS_FILE_H
-#include <sys/file.h>
-#endif
-#if HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#include <ctype.h>
-#if HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#include <unistd.h>
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#if HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-
-#if USE_WIN32API
-#include <ws2tcpip.h>
-#endif
-
-#include "gdbsupport/netstuff.h"
-#include "gdbsupport/rsp-low.h"
-
-#ifndef HAVE_SOCKLEN_T
-typedef int socklen_t;
-#endif
-
-/* Sort of a hack... */
-#define EOL (EOF - 1)
-
-static int remote_desc;
-
-#ifdef __MINGW32CE__
-
-#ifndef COUNTOF
-#define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0]))
-#endif
-
-#define errno (GetLastError ())
-
-char *
-strerror (DWORD error)
-{
- static char buf[1024];
- WCHAR *msgbuf;
- DWORD lasterr = GetLastError ();
- DWORD chars = FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM
- | FORMAT_MESSAGE_ALLOCATE_BUFFER,
- NULL,
- error,
- 0, /* Default language */
- (LPVOID)&msgbuf,
- 0,
- NULL);
- if (chars != 0)
- {
- /* If there is an \r\n appended, zap it. */
- if (chars >= 2
- && msgbuf[chars - 2] == '\r'
- && msgbuf[chars - 1] == '\n')
- {
- chars -= 2;
- msgbuf[chars] = 0;
- }
-
- if (chars > ((COUNTOF (buf)) - 1))
- {
- chars = COUNTOF (buf) - 1;
- msgbuf [chars] = 0;
- }
-
- wcstombs (buf, msgbuf, chars + 1);
- LocalFree (msgbuf);
- }
- else
- sprintf (buf, "unknown win32 error (%ld)", error);
-
- SetLastError (lasterr);
- return buf;
-}
-
-#endif /* __MINGW32CE__ */
-
-static void
-sync_error (FILE *fp, const char *desc, int expect, int got)
-{
- fprintf (stderr, "\n%s\n", desc);
- fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n",
- ftell (fp), expect, got);
- fflush (stderr);
- exit (1);
-}
-
-static void
-remote_error (const char *desc)
-{
- fprintf (stderr, "\n%s\n", desc);
- fflush (stderr);
- exit (1);
-}
-
-static void
-remote_close (void)
-{
-#ifdef USE_WIN32API
- closesocket (remote_desc);
-#else
- close (remote_desc);
-#endif
-}
-
-/* Open a connection to a remote debugger.
- NAME is the filename used for communication. */
-
-static void
-remote_open (char *name)
-{
- char *last_colon = strrchr (name, ':');
-
- if (last_colon == NULL)
- {
- fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
- fflush (stderr);
- exit (1);
- }
-
-#ifdef USE_WIN32API
- static int winsock_initialized;
-#endif
- int tmp;
- int tmp_desc;
- struct addrinfo hint;
- struct addrinfo *ainfo;
-
- memset (&hint, 0, sizeof (hint));
- /* Assume no prefix will be passed, therefore we should use
- AF_UNSPEC. */
- hint.ai_family = AF_UNSPEC;
- hint.ai_socktype = SOCK_STREAM;
- hint.ai_protocol = IPPROTO_TCP;
-
- parsed_connection_spec parsed = parse_connection_spec (name, &hint);
-
- if (parsed.port_str.empty ())
- error (_("Missing port on hostname '%s'"), name);
-
-#ifdef USE_WIN32API
- if (!winsock_initialized)
- {
- WSADATA wsad;
-
- WSAStartup (MAKEWORD (1, 0), &wsad);
- winsock_initialized = 1;
- }
-#endif
-
- int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
- &hint, &ainfo);
-
- if (r != 0)
- {
- fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
- parsed.host_str.c_str (), parsed.port_str.c_str (),
- gai_strerror (r));
- fflush (stderr);
- exit (1);
- }
-
- scoped_free_addrinfo free_ainfo (ainfo);
-
- struct addrinfo *p;
-
- for (p = ainfo; p != NULL; p = p->ai_next)
- {
- tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
-
- if (tmp_desc >= 0)
- break;
- }
-
- if (p == NULL)
- perror_with_name ("Cannot open socket");
-
- /* Allow rapid reuse of this port. */
- tmp = 1;
- setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
- sizeof (tmp));
-
- switch (p->ai_family)
- {
- case AF_INET:
- ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
- break;
- default:
- fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
- exit (1);
- }
-
- if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
- perror_with_name ("Can't bind address");
-
- if (p->ai_socktype == SOCK_DGRAM)
- remote_desc = tmp_desc;
- else
- {
- struct sockaddr_storage sockaddr;
- socklen_t sockaddrsize = sizeof (sockaddr);
- char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
-
- if (listen (tmp_desc, 1) != 0)
- perror_with_name ("Can't listen on socket");
-
- remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
- &sockaddrsize);
-
- if (remote_desc == -1)
- perror_with_name ("Accept failed");
-
- /* Enable TCP keep alive process. */
- tmp = 1;
- setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE,
- (char *) &tmp, sizeof (tmp));
-
- /* Tell TCP not to delay small packets. This greatly speeds up
- interactive response. */
- tmp = 1;
- setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
- (char *) &tmp, sizeof (tmp));
-
- if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
- orig_host, sizeof (orig_host),
- orig_port, sizeof (orig_port),
- NI_NUMERICHOST | NI_NUMERICSERV) == 0)
- {
- fprintf (stderr, "Remote debugging from host %s, port %s\n",
- orig_host, orig_port);
- fflush (stderr);
- }
-
-#ifndef USE_WIN32API
- close (tmp_desc); /* No longer need this */
-
- signal (SIGPIPE, SIG_IGN); /* If we don't do this, then
- gdbreplay simply exits when
- the remote side dies. */
-#else
- closesocket (tmp_desc); /* No longer need this */
-#endif
- }
-
-#if defined(F_SETFL) && defined (FASYNC)
- fcntl (remote_desc, F_SETFL, FASYNC);
-#endif
-
- fprintf (stderr, "Replay logfile using %s\n", name);
- fflush (stderr);
-}
-
-static int
-logchar (FILE *fp)
-{
- int ch;
- int ch2;
-
- ch = fgetc (fp);
- if (ch != '\r')
- {
- fputc (ch, stdout);
- fflush (stdout);
- }
- switch (ch)
- {
- /* Treat \r\n as a newline. */
- case '\r':
- ch = fgetc (fp);
- if (ch == '\n')
- ch = EOL;
- else
- {
- ungetc (ch, fp);
- ch = '\r';
- }
- fputc (ch == EOL ? '\n' : '\r', stdout);
- fflush (stdout);
- break;
- case '\n':
- ch = EOL;
- break;
- case '\\':
- ch = fgetc (fp);
- fputc (ch, stdout);
- fflush (stdout);
- switch (ch)
- {
- case '\\':
- break;
- case 'b':
- ch = '\b';
- break;
- case 'f':
- ch = '\f';
- break;
- case 'n':
- ch = '\n';
- break;
- case 'r':
- ch = '\r';
- break;
- case 't':
- ch = '\t';
- break;
- case 'v':
- ch = '\v';
- break;
- case 'x':
- ch2 = fgetc (fp);
- fputc (ch2, stdout);
- fflush (stdout);
- ch = fromhex (ch2) << 4;
- ch2 = fgetc (fp);
- fputc (ch2, stdout);
- fflush (stdout);
- ch |= fromhex (ch2);
- break;
- default:
- /* Treat any other char as just itself */
- break;
- }
- default:
- break;
- }
- return (ch);
-}
-
-static int
-gdbchar (int desc)
-{
- unsigned char fromgdb;
-
- if (read (desc, &fromgdb, 1) != 1)
- return -1;
- else
- return fromgdb;
-}
-
-/* Accept input from gdb and match with chars from fp (after skipping one
- blank) up until a \n is read from fp (which is not matched) */
-
-static void
-expect (FILE *fp)
-{
- int fromlog;
- int fromgdb;
-
- if ((fromlog = logchar (fp)) != ' ')
- {
- sync_error (fp, "Sync error during gdb read of leading blank", ' ',
- fromlog);
- }
- do
- {
- fromlog = logchar (fp);
- if (fromlog == EOL)
- break;
- fromgdb = gdbchar (remote_desc);
- if (fromgdb < 0)
- remote_error ("Error during read from gdb");
- }
- while (fromlog == fromgdb);
-
- if (fromlog != EOL)
- {
- sync_error (fp, "Sync error during read of gdb packet from log", fromlog,
- fromgdb);
- }
-}
-
-/* Play data back to gdb from fp (after skipping leading blank) up until a
- \n is read from fp (which is discarded and not sent to gdb). */
-
-static void
-play (FILE *fp)
-{
- int fromlog;
- char ch;
-
- if ((fromlog = logchar (fp)) != ' ')
- {
- sync_error (fp, "Sync error skipping blank during write to gdb", ' ',
- fromlog);
- }
- while ((fromlog = logchar (fp)) != EOL)
- {
- ch = fromlog;
- if (write (remote_desc, &ch, 1) != 1)
- remote_error ("Error during write to gdb");
- }
-}
-
-static void
-gdbreplay_version (void)
-{
- printf ("GNU gdbreplay %s%s\n"
- "Copyright (C) 2020 Free Software Foundation, Inc.\n"
- "gdbreplay is free software, covered by "
- "the GNU General Public License.\n"
- "This gdbreplay was configured as \"%s\"\n",
- PKGVERSION, version, host_name);
-}
-
-static void
-gdbreplay_usage (FILE *stream)
-{
- fprintf (stream, "Usage:\tgdbreplay LOGFILE HOST:PORT\n");
- if (REPORT_BUGS_TO[0] && stream == stdout)
- fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO);
-}
-
-/* Main function. This is called by the real "main" function,
- wrapped in a TRY_CATCH that handles any uncaught exceptions. */
-
-static void ATTRIBUTE_NORETURN
-captured_main (int argc, char *argv[])
-{
- FILE *fp;
- int ch;
-
- if (argc >= 2 && strcmp (argv[1], "--version") == 0)
- {
- gdbreplay_version ();
- exit (0);
- }
- if (argc >= 2 && strcmp (argv[1], "--help") == 0)
- {
- gdbreplay_usage (stdout);
- exit (0);
- }
-
- if (argc < 3)
- {
- gdbreplay_usage (stderr);
- exit (1);
- }
- fp = fopen (argv[1], "r");
- if (fp == NULL)
- {
- perror_with_name (argv[1]);
- }
- remote_open (argv[2]);
- while ((ch = logchar (fp)) != EOF)
- {
- switch (ch)
- {
- case 'w':
- /* data sent from gdb to gdbreplay, accept and match it */
- expect (fp);
- break;
- case 'r':
- /* data sent from gdbreplay to gdb, play it */
- play (fp);
- break;
- case 'c':
- /* Command executed by gdb */
- while ((ch = logchar (fp)) != EOL);
- break;
- }
- }
- remote_close ();
- exit (0);
-}
-
-int
-main (int argc, char *argv[])
-{
- try
- {
- captured_main (argc, argv);
- }
- catch (const gdb_exception &exception)
- {
- if (exception.reason == RETURN_ERROR)
- {
- fflush (stdout);
- fprintf (stderr, "%s\n", exception.what ());
- }
-
- exit (1);
- }
-
- gdb_assert_not_reached ("captured_main should never return");
-}
--- /dev/null
+/* Replay a remote debug session logfile for GDB.
+ Copyright (C) 1996-2020 Free Software Foundation, Inc.
+ Written by Fred Fish (fnf@cygnus.com) from pieces of gdbserver.
+
+ 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 "gdbsupport/common-defs.h"
+
+#undef PACKAGE
+#undef PACKAGE_NAME
+#undef PACKAGE_VERSION
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+
+#include <config.h>
+#include "gdbsupport/version.h"
+
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <ctype.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <unistd.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#if USE_WIN32API
+#include <ws2tcpip.h>
+#endif
+
+#include "gdbsupport/netstuff.h"
+#include "gdbsupport/rsp-low.h"
+
+#ifndef HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+/* Sort of a hack... */
+#define EOL (EOF - 1)
+
+static int remote_desc;
+
+#ifdef __MINGW32CE__
+
+#ifndef COUNTOF
+#define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0]))
+#endif
+
+#define errno (GetLastError ())
+
+char *
+strerror (DWORD error)
+{
+ static char buf[1024];
+ WCHAR *msgbuf;
+ DWORD lasterr = GetLastError ();
+ DWORD chars = FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL,
+ error,
+ 0, /* Default language */
+ (LPVOID)&msgbuf,
+ 0,
+ NULL);
+ if (chars != 0)
+ {
+ /* If there is an \r\n appended, zap it. */
+ if (chars >= 2
+ && msgbuf[chars - 2] == '\r'
+ && msgbuf[chars - 1] == '\n')
+ {
+ chars -= 2;
+ msgbuf[chars] = 0;
+ }
+
+ if (chars > ((COUNTOF (buf)) - 1))
+ {
+ chars = COUNTOF (buf) - 1;
+ msgbuf [chars] = 0;
+ }
+
+ wcstombs (buf, msgbuf, chars + 1);
+ LocalFree (msgbuf);
+ }
+ else
+ sprintf (buf, "unknown win32 error (%ld)", error);
+
+ SetLastError (lasterr);
+ return buf;
+}
+
+#endif /* __MINGW32CE__ */
+
+static void
+sync_error (FILE *fp, const char *desc, int expect, int got)
+{
+ fprintf (stderr, "\n%s\n", desc);
+ fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n",
+ ftell (fp), expect, got);
+ fflush (stderr);
+ exit (1);
+}
+
+static void
+remote_error (const char *desc)
+{
+ fprintf (stderr, "\n%s\n", desc);
+ fflush (stderr);
+ exit (1);
+}
+
+static void
+remote_close (void)
+{
+#ifdef USE_WIN32API
+ closesocket (remote_desc);
+#else
+ close (remote_desc);
+#endif
+}
+
+/* Open a connection to a remote debugger.
+ NAME is the filename used for communication. */
+
+static void
+remote_open (char *name)
+{
+ char *last_colon = strrchr (name, ':');
+
+ if (last_colon == NULL)
+ {
+ fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
+ fflush (stderr);
+ exit (1);
+ }
+
+#ifdef USE_WIN32API
+ static int winsock_initialized;
+#endif
+ int tmp;
+ int tmp_desc;
+ struct addrinfo hint;
+ struct addrinfo *ainfo;
+
+ memset (&hint, 0, sizeof (hint));
+ /* Assume no prefix will be passed, therefore we should use
+ AF_UNSPEC. */
+ hint.ai_family = AF_UNSPEC;
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+
+ parsed_connection_spec parsed = parse_connection_spec (name, &hint);
+
+ if (parsed.port_str.empty ())
+ error (_("Missing port on hostname '%s'"), name);
+
+#ifdef USE_WIN32API
+ if (!winsock_initialized)
+ {
+ WSADATA wsad;
+
+ WSAStartup (MAKEWORD (1, 0), &wsad);
+ winsock_initialized = 1;
+ }
+#endif
+
+ int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+ &hint, &ainfo);
+
+ if (r != 0)
+ {
+ fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
+ parsed.host_str.c_str (), parsed.port_str.c_str (),
+ gai_strerror (r));
+ fflush (stderr);
+ exit (1);
+ }
+
+ scoped_free_addrinfo free_ainfo (ainfo);
+
+ struct addrinfo *p;
+
+ for (p = ainfo; p != NULL; p = p->ai_next)
+ {
+ tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
+
+ if (tmp_desc >= 0)
+ break;
+ }
+
+ if (p == NULL)
+ perror_with_name ("Cannot open socket");
+
+ /* Allow rapid reuse of this port. */
+ tmp = 1;
+ setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+ sizeof (tmp));
+
+ switch (p->ai_family)
+ {
+ case AF_INET:
+ ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
+ break;
+ default:
+ fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
+ exit (1);
+ }
+
+ if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
+ perror_with_name ("Can't bind address");
+
+ if (p->ai_socktype == SOCK_DGRAM)
+ remote_desc = tmp_desc;
+ else
+ {
+ struct sockaddr_storage sockaddr;
+ socklen_t sockaddrsize = sizeof (sockaddr);
+ char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+ if (listen (tmp_desc, 1) != 0)
+ perror_with_name ("Can't listen on socket");
+
+ remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
+ &sockaddrsize);
+
+ if (remote_desc == -1)
+ perror_with_name ("Accept failed");
+
+ /* Enable TCP keep alive process. */
+ tmp = 1;
+ setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE,
+ (char *) &tmp, sizeof (tmp));
+
+ /* Tell TCP not to delay small packets. This greatly speeds up
+ interactive response. */
+ tmp = 1;
+ setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
+ (char *) &tmp, sizeof (tmp));
+
+ if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
+ orig_host, sizeof (orig_host),
+ orig_port, sizeof (orig_port),
+ NI_NUMERICHOST | NI_NUMERICSERV) == 0)
+ {
+ fprintf (stderr, "Remote debugging from host %s, port %s\n",
+ orig_host, orig_port);
+ fflush (stderr);
+ }
+
+#ifndef USE_WIN32API
+ close (tmp_desc); /* No longer need this */
+
+ signal (SIGPIPE, SIG_IGN); /* If we don't do this, then
+ gdbreplay simply exits when
+ the remote side dies. */
+#else
+ closesocket (tmp_desc); /* No longer need this */
+#endif
+ }
+
+#if defined(F_SETFL) && defined (FASYNC)
+ fcntl (remote_desc, F_SETFL, FASYNC);
+#endif
+
+ fprintf (stderr, "Replay logfile using %s\n", name);
+ fflush (stderr);
+}
+
+static int
+logchar (FILE *fp)
+{
+ int ch;
+ int ch2;
+
+ ch = fgetc (fp);
+ if (ch != '\r')
+ {
+ fputc (ch, stdout);
+ fflush (stdout);
+ }
+ switch (ch)
+ {
+ /* Treat \r\n as a newline. */
+ case '\r':
+ ch = fgetc (fp);
+ if (ch == '\n')
+ ch = EOL;
+ else
+ {
+ ungetc (ch, fp);
+ ch = '\r';
+ }
+ fputc (ch == EOL ? '\n' : '\r', stdout);
+ fflush (stdout);
+ break;
+ case '\n':
+ ch = EOL;
+ break;
+ case '\\':
+ ch = fgetc (fp);
+ fputc (ch, stdout);
+ fflush (stdout);
+ switch (ch)
+ {
+ case '\\':
+ break;
+ case 'b':
+ ch = '\b';
+ break;
+ case 'f':
+ ch = '\f';
+ break;
+ case 'n':
+ ch = '\n';
+ break;
+ case 'r':
+ ch = '\r';
+ break;
+ case 't':
+ ch = '\t';
+ break;
+ case 'v':
+ ch = '\v';
+ break;
+ case 'x':
+ ch2 = fgetc (fp);
+ fputc (ch2, stdout);
+ fflush (stdout);
+ ch = fromhex (ch2) << 4;
+ ch2 = fgetc (fp);
+ fputc (ch2, stdout);
+ fflush (stdout);
+ ch |= fromhex (ch2);
+ break;
+ default:
+ /* Treat any other char as just itself */
+ break;
+ }
+ default:
+ break;
+ }
+ return (ch);
+}
+
+static int
+gdbchar (int desc)
+{
+ unsigned char fromgdb;
+
+ if (read (desc, &fromgdb, 1) != 1)
+ return -1;
+ else
+ return fromgdb;
+}
+
+/* Accept input from gdb and match with chars from fp (after skipping one
+ blank) up until a \n is read from fp (which is not matched) */
+
+static void
+expect (FILE *fp)
+{
+ int fromlog;
+ int fromgdb;
+
+ if ((fromlog = logchar (fp)) != ' ')
+ {
+ sync_error (fp, "Sync error during gdb read of leading blank", ' ',
+ fromlog);
+ }
+ do
+ {
+ fromlog = logchar (fp);
+ if (fromlog == EOL)
+ break;
+ fromgdb = gdbchar (remote_desc);
+ if (fromgdb < 0)
+ remote_error ("Error during read from gdb");
+ }
+ while (fromlog == fromgdb);
+
+ if (fromlog != EOL)
+ {
+ sync_error (fp, "Sync error during read of gdb packet from log", fromlog,
+ fromgdb);
+ }
+}
+
+/* Play data back to gdb from fp (after skipping leading blank) up until a
+ \n is read from fp (which is discarded and not sent to gdb). */
+
+static void
+play (FILE *fp)
+{
+ int fromlog;
+ char ch;
+
+ if ((fromlog = logchar (fp)) != ' ')
+ {
+ sync_error (fp, "Sync error skipping blank during write to gdb", ' ',
+ fromlog);
+ }
+ while ((fromlog = logchar (fp)) != EOL)
+ {
+ ch = fromlog;
+ if (write (remote_desc, &ch, 1) != 1)
+ remote_error ("Error during write to gdb");
+ }
+}
+
+static void
+gdbreplay_version (void)
+{
+ printf ("GNU gdbreplay %s%s\n"
+ "Copyright (C) 2020 Free Software Foundation, Inc.\n"
+ "gdbreplay is free software, covered by "
+ "the GNU General Public License.\n"
+ "This gdbreplay was configured as \"%s\"\n",
+ PKGVERSION, version, host_name);
+}
+
+static void
+gdbreplay_usage (FILE *stream)
+{
+ fprintf (stream, "Usage:\tgdbreplay LOGFILE HOST:PORT\n");
+ if (REPORT_BUGS_TO[0] && stream == stdout)
+ fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO);
+}
+
+/* Main function. This is called by the real "main" function,
+ wrapped in a TRY_CATCH that handles any uncaught exceptions. */
+
+static void ATTRIBUTE_NORETURN
+captured_main (int argc, char *argv[])
+{
+ FILE *fp;
+ int ch;
+
+ if (argc >= 2 && strcmp (argv[1], "--version") == 0)
+ {
+ gdbreplay_version ();
+ exit (0);
+ }
+ if (argc >= 2 && strcmp (argv[1], "--help") == 0)
+ {
+ gdbreplay_usage (stdout);
+ exit (0);
+ }
+
+ if (argc < 3)
+ {
+ gdbreplay_usage (stderr);
+ exit (1);
+ }
+ fp = fopen (argv[1], "r");
+ if (fp == NULL)
+ {
+ perror_with_name (argv[1]);
+ }
+ remote_open (argv[2]);
+ while ((ch = logchar (fp)) != EOF)
+ {
+ switch (ch)
+ {
+ case 'w':
+ /* data sent from gdb to gdbreplay, accept and match it */
+ expect (fp);
+ break;
+ case 'r':
+ /* data sent from gdbreplay to gdb, play it */
+ play (fp);
+ break;
+ case 'c':
+ /* Command executed by gdb */
+ while ((ch = logchar (fp)) != EOL);
+ break;
+ }
+ }
+ remote_close ();
+ exit (0);
+}
+
+int
+main (int argc, char *argv[])
+{
+ try
+ {
+ captured_main (argc, argv);
+ }
+ catch (const gdb_exception &exception)
+ {
+ if (exception.reason == RETURN_ERROR)
+ {
+ fflush (stdout);
+ fprintf (stderr, "%s\n", exception.what ());
+ }
+
+ exit (1);
+ }
+
+ gdb_assert_not_reached ("captured_main should never return");
+}
+++ /dev/null
-/* Host file transfer support for gdbserver.
- Copyright (C) 2007-2020 Free Software Foundation, Inc.
-
- Contributed by CodeSourcery.
-
- 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/>. */
-
-/* This file implements the hostio_last_error target callback
- on top of errno. */
-
-#include "server.h"
-
-#include "hostio.h"
-
-#include "gdbsupport/fileio.h"
-
-void
-hostio_last_error_from_errno (char *buf)
-{
- int error = errno;
- int fileio_error = host_to_fileio_error (error);
- sprintf (buf, "F-1,%x", fileio_error);
-}
--- /dev/null
+/* Host file transfer support for gdbserver.
+ Copyright (C) 2007-2020 Free Software Foundation, Inc.
+
+ Contributed by CodeSourcery.
+
+ 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/>. */
+
+/* This file implements the hostio_last_error target callback
+ on top of errno. */
+
+#include "server.h"
+
+#include "hostio.h"
+
+#include "gdbsupport/fileio.h"
+
+void
+hostio_last_error_from_errno (char *buf)
+{
+ int error = errno;
+ int fileio_error = host_to_fileio_error (error);
+ sprintf (buf, "F-1,%x", fileio_error);
+}
+++ /dev/null
-/* Host file transfer support for gdbserver.
- Copyright (C) 2007-2020 Free Software Foundation, Inc.
-
- Contributed by CodeSourcery.
-
- 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 "gdb/fileio.h"
-#include "hostio.h"
-
-#include <fcntl.h>
-#include <limits.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "gdbsupport/fileio.h"
-
-struct fd_list
-{
- int fd;
- struct fd_list *next;
-};
-
-static struct fd_list *open_fds;
-
-static int
-safe_fromhex (char a, int *nibble)
-{
- if (a >= '0' && a <= '9')
- *nibble = a - '0';
- else if (a >= 'a' && a <= 'f')
- *nibble = a - 'a' + 10;
- else if (a >= 'A' && a <= 'F')
- *nibble = a - 'A' + 10;
- else
- return -1;
-
- return 0;
-}
-
-/* Filenames are hex encoded, so the maximum we can handle is half the
- packet buffer size. Cap to PATH_MAX, if it is shorter. */
-#if !defined (PATH_MAX) || (PATH_MAX > (PBUFSIZ / 2 + 1))
-# define HOSTIO_PATH_MAX (PBUFSIZ / 2 + 1)
-#else
-# define HOSTIO_PATH_MAX PATH_MAX
-#endif
-
-static int
-require_filename (char **pp, char *filename)
-{
- int count;
- char *p;
-
- p = *pp;
- count = 0;
-
- while (*p && *p != ',')
- {
- int nib1, nib2;
-
- /* Don't allow overflow. */
- if (count >= HOSTIO_PATH_MAX - 1)
- return -1;
-
- if (safe_fromhex (p[0], &nib1)
- || safe_fromhex (p[1], &nib2))
- return -1;
-
- filename[count++] = nib1 * 16 + nib2;
- p += 2;
- }
-
- filename[count] = '\0';
- *pp = p;
- return 0;
-}
-
-static int
-require_int (char **pp, int *value)
-{
- char *p;
- int count, firstdigit;
-
- p = *pp;
- *value = 0;
- count = 0;
- firstdigit = -1;
-
- while (*p && *p != ',')
- {
- int nib;
-
- if (safe_fromhex (p[0], &nib))
- return -1;
-
- if (firstdigit == -1)
- firstdigit = nib;
-
- /* Don't allow overflow. */
- if (count >= 8 || (count == 7 && firstdigit >= 0x8))
- return -1;
-
- *value = *value * 16 + nib;
- p++;
- count++;
- }
-
- *pp = p;
- return 0;
-}
-
-static int
-require_data (char *p, int p_len, char **data, int *data_len)
-{
- int input_index, output_index, escaped;
-
- *data = (char *) xmalloc (p_len);
-
- output_index = 0;
- escaped = 0;
- for (input_index = 0; input_index < p_len; input_index++)
- {
- char b = p[input_index];
-
- if (escaped)
- {
- (*data)[output_index++] = b ^ 0x20;
- escaped = 0;
- }
- else if (b == '}')
- escaped = 1;
- else
- (*data)[output_index++] = b;
- }
-
- if (escaped)
- {
- free (*data);
- return -1;
- }
-
- *data_len = output_index;
- return 0;
-}
-
-static int
-require_comma (char **pp)
-{
- if (**pp == ',')
- {
- (*pp)++;
- return 0;
- }
- else
- return -1;
-}
-
-static int
-require_end (char *p)
-{
- if (*p == '\0')
- return 0;
- else
- return -1;
-}
-
-static int
-require_valid_fd (int fd)
-{
- struct fd_list *fd_ptr;
-
- for (fd_ptr = open_fds; fd_ptr != NULL; fd_ptr = fd_ptr->next)
- if (fd_ptr->fd == fd)
- return 0;
-
- return -1;
-}
-
-/* Fill in own_buf with the last hostio error packet, however it
- suitable for the target. */
-static void
-hostio_error (char *own_buf)
-{
- the_target->hostio_last_error (own_buf);
-}
-
-static void
-hostio_packet_error (char *own_buf)
-{
- sprintf (own_buf, "F-1,%x", FILEIO_EINVAL);
-}
-
-static void
-hostio_reply (char *own_buf, int result)
-{
- sprintf (own_buf, "F%x", result);
-}
-
-static int
-hostio_reply_with_data (char *own_buf, char *buffer, int len,
- int *new_packet_len)
-{
- int input_index, output_index, out_maxlen;
-
- sprintf (own_buf, "F%x;", len);
- output_index = strlen (own_buf);
-
- out_maxlen = PBUFSIZ;
-
- for (input_index = 0; input_index < len; input_index++)
- {
- char b = buffer[input_index];
-
- if (b == '$' || b == '#' || b == '}' || b == '*')
- {
- /* These must be escaped. */
- if (output_index + 2 > out_maxlen)
- break;
- own_buf[output_index++] = '}';
- own_buf[output_index++] = b ^ 0x20;
- }
- else
- {
- if (output_index + 1 > out_maxlen)
- break;
- own_buf[output_index++] = b;
- }
- }
-
- *new_packet_len = output_index;
- return input_index;
-}
-
-/* Process ID of inferior whose filesystem hostio functions
- that take FILENAME arguments will use. Zero means to use
- our own filesystem. */
-
-static int hostio_fs_pid;
-
-/* See hostio.h. */
-
-void
-hostio_handle_new_gdb_connection (void)
-{
- hostio_fs_pid = 0;
-}
-
-/* Handle a "vFile:setfs:" packet. */
-
-static void
-handle_setfs (char *own_buf)
-{
- char *p;
- int pid;
-
- /* If the target doesn't have any of the in-filesystem-of methods
- then there's no point in GDB sending "vFile:setfs:" packets. We
- reply with an empty packet (i.e. we pretend we don't understand
- "vFile:setfs:") and that should stop GDB sending any more. */
- if (the_target->multifs_open == NULL
- && the_target->multifs_unlink == NULL
- && the_target->multifs_readlink == NULL)
- {
- own_buf[0] = '\0';
- return;
- }
-
- p = own_buf + strlen ("vFile:setfs:");
-
- if (require_int (&p, &pid)
- || pid < 0
- || require_end (p))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
- hostio_fs_pid = pid;
-
- hostio_reply (own_buf, 0);
-}
-
-static void
-handle_open (char *own_buf)
-{
- char filename[HOSTIO_PATH_MAX];
- char *p;
- int fileio_flags, fileio_mode, flags, fd;
- mode_t mode;
- struct fd_list *new_fd;
-
- p = own_buf + strlen ("vFile:open:");
-
- if (require_filename (&p, filename)
- || require_comma (&p)
- || require_int (&p, &fileio_flags)
- || require_comma (&p)
- || require_int (&p, &fileio_mode)
- || require_end (p)
- || fileio_to_host_openflags (fileio_flags, &flags)
- || fileio_to_host_mode (fileio_mode, &mode))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
- /* We do not need to convert MODE, since the fileio protocol
- uses the standard values. */
- if (hostio_fs_pid != 0 && the_target->multifs_open != NULL)
- fd = the_target->multifs_open (hostio_fs_pid, filename,
- flags, mode);
- else
- fd = open (filename, flags, mode);
-
- if (fd == -1)
- {
- hostio_error (own_buf);
- return;
- }
-
- /* Record the new file descriptor. */
- new_fd = XNEW (struct fd_list);
- new_fd->fd = fd;
- new_fd->next = open_fds;
- open_fds = new_fd;
-
- hostio_reply (own_buf, fd);
-}
-
-static void
-handle_pread (char *own_buf, int *new_packet_len)
-{
- int fd, ret, len, offset, bytes_sent;
- char *p, *data;
- static int max_reply_size = -1;
-
- p = own_buf + strlen ("vFile:pread:");
-
- if (require_int (&p, &fd)
- || require_comma (&p)
- || require_valid_fd (fd)
- || require_int (&p, &len)
- || require_comma (&p)
- || require_int (&p, &offset)
- || require_end (p))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
- /* Do not attempt to read more than the maximum number of bytes
- hostio_reply_with_data can fit in a packet. We may still read
- too much because of escaping, but this is handled below. */
- if (max_reply_size == -1)
- {
- sprintf (own_buf, "F%x;", PBUFSIZ);
- max_reply_size = PBUFSIZ - strlen (own_buf);
- }
- if (len > max_reply_size)
- len = max_reply_size;
-
- data = (char *) xmalloc (len);
-#ifdef HAVE_PREAD
- ret = pread (fd, data, len, offset);
-#else
- ret = -1;
-#endif
- /* If we have no pread or it failed for this file, use lseek/read. */
- if (ret == -1)
- {
- ret = lseek (fd, offset, SEEK_SET);
- if (ret != -1)
- ret = read (fd, data, len);
- }
-
- if (ret == -1)
- {
- hostio_error (own_buf);
- free (data);
- return;
- }
-
- bytes_sent = hostio_reply_with_data (own_buf, data, ret, new_packet_len);
-
- /* If we were using read, and the data did not all fit in the reply,
- we would have to back up using lseek here. With pread it does
- not matter. But we still have a problem; the return value in the
- packet might be wrong, so we must fix it. This time it will
- definitely fit. */
- if (bytes_sent < ret)
- bytes_sent = hostio_reply_with_data (own_buf, data, bytes_sent,
- new_packet_len);
-
- free (data);
-}
-
-static void
-handle_pwrite (char *own_buf, int packet_len)
-{
- int fd, ret, len, offset;
- char *p, *data;
-
- p = own_buf + strlen ("vFile:pwrite:");
-
- if (require_int (&p, &fd)
- || require_comma (&p)
- || require_valid_fd (fd)
- || require_int (&p, &offset)
- || require_comma (&p)
- || require_data (p, packet_len - (p - own_buf), &data, &len))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
-#ifdef HAVE_PWRITE
- ret = pwrite (fd, data, len, offset);
-#else
- ret = -1;
-#endif
- /* If we have no pwrite or it failed for this file, use lseek/write. */
- if (ret == -1)
- {
- ret = lseek (fd, offset, SEEK_SET);
- if (ret != -1)
- ret = write (fd, data, len);
- }
-
- if (ret == -1)
- {
- hostio_error (own_buf);
- free (data);
- return;
- }
-
- hostio_reply (own_buf, ret);
- free (data);
-}
-
-static void
-handle_fstat (char *own_buf, int *new_packet_len)
-{
- int fd, bytes_sent;
- char *p;
- struct stat st;
- struct fio_stat fst;
-
- p = own_buf + strlen ("vFile:fstat:");
-
- if (require_int (&p, &fd)
- || require_valid_fd (fd)
- || require_end (p))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
- if (fstat (fd, &st) == -1)
- {
- hostio_error (own_buf);
- return;
- }
-
- host_to_fileio_stat (&st, &fst);
-
- bytes_sent = hostio_reply_with_data (own_buf,
- (char *) &fst, sizeof (fst),
- new_packet_len);
-
- /* If the response does not fit into a single packet, do not attempt
- to return a partial response, but simply fail. */
- if (bytes_sent < sizeof (fst))
- write_enn (own_buf);
-}
-
-static void
-handle_close (char *own_buf)
-{
- int fd, ret;
- char *p;
- struct fd_list **open_fd_p, *old_fd;
-
- p = own_buf + strlen ("vFile:close:");
-
- if (require_int (&p, &fd)
- || require_valid_fd (fd)
- || require_end (p))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
- ret = close (fd);
-
- if (ret == -1)
- {
- hostio_error (own_buf);
- return;
- }
-
- open_fd_p = &open_fds;
- /* We know that fd is in the list, thanks to require_valid_fd. */
- while ((*open_fd_p)->fd != fd)
- open_fd_p = &(*open_fd_p)->next;
-
- old_fd = *open_fd_p;
- *open_fd_p = (*open_fd_p)->next;
- free (old_fd);
-
- hostio_reply (own_buf, ret);
-}
-
-static void
-handle_unlink (char *own_buf)
-{
- char filename[HOSTIO_PATH_MAX];
- char *p;
- int ret;
-
- p = own_buf + strlen ("vFile:unlink:");
-
- if (require_filename (&p, filename)
- || require_end (p))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
- if (hostio_fs_pid != 0 && the_target->multifs_unlink != NULL)
- ret = the_target->multifs_unlink (hostio_fs_pid, filename);
- else
- ret = unlink (filename);
-
- if (ret == -1)
- {
- hostio_error (own_buf);
- return;
- }
-
- hostio_reply (own_buf, ret);
-}
-
-static void
-handle_readlink (char *own_buf, int *new_packet_len)
-{
- char filename[HOSTIO_PATH_MAX], linkname[HOSTIO_PATH_MAX];
- char *p;
- int ret, bytes_sent;
-
- p = own_buf + strlen ("vFile:readlink:");
-
- if (require_filename (&p, filename)
- || require_end (p))
- {
- hostio_packet_error (own_buf);
- return;
- }
-
- if (hostio_fs_pid != 0 && the_target->multifs_readlink != NULL)
- ret = the_target->multifs_readlink (hostio_fs_pid, filename,
- linkname,
- sizeof (linkname) - 1);
- else
- ret = readlink (filename, linkname, sizeof (linkname) - 1);
-
- if (ret == -1)
- {
- hostio_error (own_buf);
- return;
- }
-
- bytes_sent = hostio_reply_with_data (own_buf, linkname, ret, new_packet_len);
-
- /* If the response does not fit into a single packet, do not attempt
- to return a partial response, but simply fail. */
- if (bytes_sent < ret)
- sprintf (own_buf, "F-1,%x", FILEIO_ENAMETOOLONG);
-}
-
-/* Handle all the 'F' file transfer packets. */
-
-int
-handle_vFile (char *own_buf, int packet_len, int *new_packet_len)
-{
- if (startswith (own_buf, "vFile:open:"))
- handle_open (own_buf);
- else if (startswith (own_buf, "vFile:pread:"))
- handle_pread (own_buf, new_packet_len);
- else if (startswith (own_buf, "vFile:pwrite:"))
- handle_pwrite (own_buf, packet_len);
- else if (startswith (own_buf, "vFile:fstat:"))
- handle_fstat (own_buf, new_packet_len);
- else if (startswith (own_buf, "vFile:close:"))
- handle_close (own_buf);
- else if (startswith (own_buf, "vFile:unlink:"))
- handle_unlink (own_buf);
- else if (startswith (own_buf, "vFile:readlink:"))
- handle_readlink (own_buf, new_packet_len);
- else if (startswith (own_buf, "vFile:setfs:"))
- handle_setfs (own_buf);
- else
- return 0;
-
- return 1;
-}
--- /dev/null
+/* Host file transfer support for gdbserver.
+ Copyright (C) 2007-2020 Free Software Foundation, Inc.
+
+ Contributed by CodeSourcery.
+
+ 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 "gdb/fileio.h"
+#include "hostio.h"
+
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "gdbsupport/fileio.h"
+
+struct fd_list
+{
+ int fd;
+ struct fd_list *next;
+};
+
+static struct fd_list *open_fds;
+
+static int
+safe_fromhex (char a, int *nibble)
+{
+ if (a >= '0' && a <= '9')
+ *nibble = a - '0';
+ else if (a >= 'a' && a <= 'f')
+ *nibble = a - 'a' + 10;
+ else if (a >= 'A' && a <= 'F')
+ *nibble = a - 'A' + 10;
+ else
+ return -1;
+
+ return 0;
+}
+
+/* Filenames are hex encoded, so the maximum we can handle is half the
+ packet buffer size. Cap to PATH_MAX, if it is shorter. */
+#if !defined (PATH_MAX) || (PATH_MAX > (PBUFSIZ / 2 + 1))
+# define HOSTIO_PATH_MAX (PBUFSIZ / 2 + 1)
+#else
+# define HOSTIO_PATH_MAX PATH_MAX
+#endif
+
+static int
+require_filename (char **pp, char *filename)
+{
+ int count;
+ char *p;
+
+ p = *pp;
+ count = 0;
+
+ while (*p && *p != ',')
+ {
+ int nib1, nib2;
+
+ /* Don't allow overflow. */
+ if (count >= HOSTIO_PATH_MAX - 1)
+ return -1;
+
+ if (safe_fromhex (p[0], &nib1)
+ || safe_fromhex (p[1], &nib2))
+ return -1;
+
+ filename[count++] = nib1 * 16 + nib2;
+ p += 2;
+ }
+
+ filename[count] = '\0';
+ *pp = p;
+ return 0;
+}
+
+static int
+require_int (char **pp, int *value)
+{
+ char *p;
+ int count, firstdigit;
+
+ p = *pp;
+ *value = 0;
+ count = 0;
+ firstdigit = -1;
+
+ while (*p && *p != ',')
+ {
+ int nib;
+
+ if (safe_fromhex (p[0], &nib))
+ return -1;
+
+ if (firstdigit == -1)
+ firstdigit = nib;
+
+ /* Don't allow overflow. */
+ if (count >= 8 || (count == 7 && firstdigit >= 0x8))
+ return -1;
+
+ *value = *value * 16 + nib;
+ p++;
+ count++;
+ }
+
+ *pp = p;
+ return 0;
+}
+
+static int
+require_data (char *p, int p_len, char **data, int *data_len)
+{
+ int input_index, output_index, escaped;
+
+ *data = (char *) xmalloc (p_len);
+
+ output_index = 0;
+ escaped = 0;
+ for (input_index = 0; input_index < p_len; input_index++)
+ {
+ char b = p[input_index];
+
+ if (escaped)
+ {
+ (*data)[output_index++] = b ^ 0x20;
+ escaped = 0;
+ }
+ else if (b == '}')
+ escaped = 1;
+ else
+ (*data)[output_index++] = b;
+ }
+
+ if (escaped)
+ {
+ free (*data);
+ return -1;
+ }
+
+ *data_len = output_index;
+ return 0;
+}
+
+static int
+require_comma (char **pp)
+{
+ if (**pp == ',')
+ {
+ (*pp)++;
+ return 0;
+ }
+ else
+ return -1;
+}
+
+static int
+require_end (char *p)
+{
+ if (*p == '\0')
+ return 0;
+ else
+ return -1;
+}
+
+static int
+require_valid_fd (int fd)
+{
+ struct fd_list *fd_ptr;
+
+ for (fd_ptr = open_fds; fd_ptr != NULL; fd_ptr = fd_ptr->next)
+ if (fd_ptr->fd == fd)
+ return 0;
+
+ return -1;
+}
+
+/* Fill in own_buf with the last hostio error packet, however it
+ suitable for the target. */
+static void
+hostio_error (char *own_buf)
+{
+ the_target->hostio_last_error (own_buf);
+}
+
+static void
+hostio_packet_error (char *own_buf)
+{
+ sprintf (own_buf, "F-1,%x", FILEIO_EINVAL);
+}
+
+static void
+hostio_reply (char *own_buf, int result)
+{
+ sprintf (own_buf, "F%x", result);
+}
+
+static int
+hostio_reply_with_data (char *own_buf, char *buffer, int len,
+ int *new_packet_len)
+{
+ int input_index, output_index, out_maxlen;
+
+ sprintf (own_buf, "F%x;", len);
+ output_index = strlen (own_buf);
+
+ out_maxlen = PBUFSIZ;
+
+ for (input_index = 0; input_index < len; input_index++)
+ {
+ char b = buffer[input_index];
+
+ if (b == '$' || b == '#' || b == '}' || b == '*')
+ {
+ /* These must be escaped. */
+ if (output_index + 2 > out_maxlen)
+ break;
+ own_buf[output_index++] = '}';
+ own_buf[output_index++] = b ^ 0x20;
+ }
+ else
+ {
+ if (output_index + 1 > out_maxlen)
+ break;
+ own_buf[output_index++] = b;
+ }
+ }
+
+ *new_packet_len = output_index;
+ return input_index;
+}
+
+/* Process ID of inferior whose filesystem hostio functions
+ that take FILENAME arguments will use. Zero means to use
+ our own filesystem. */
+
+static int hostio_fs_pid;
+
+/* See hostio.h. */
+
+void
+hostio_handle_new_gdb_connection (void)
+{
+ hostio_fs_pid = 0;
+}
+
+/* Handle a "vFile:setfs:" packet. */
+
+static void
+handle_setfs (char *own_buf)
+{
+ char *p;
+ int pid;
+
+ /* If the target doesn't have any of the in-filesystem-of methods
+ then there's no point in GDB sending "vFile:setfs:" packets. We
+ reply with an empty packet (i.e. we pretend we don't understand
+ "vFile:setfs:") and that should stop GDB sending any more. */
+ if (the_target->multifs_open == NULL
+ && the_target->multifs_unlink == NULL
+ && the_target->multifs_readlink == NULL)
+ {
+ own_buf[0] = '\0';
+ return;
+ }
+
+ p = own_buf + strlen ("vFile:setfs:");
+
+ if (require_int (&p, &pid)
+ || pid < 0
+ || require_end (p))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ hostio_fs_pid = pid;
+
+ hostio_reply (own_buf, 0);
+}
+
+static void
+handle_open (char *own_buf)
+{
+ char filename[HOSTIO_PATH_MAX];
+ char *p;
+ int fileio_flags, fileio_mode, flags, fd;
+ mode_t mode;
+ struct fd_list *new_fd;
+
+ p = own_buf + strlen ("vFile:open:");
+
+ if (require_filename (&p, filename)
+ || require_comma (&p)
+ || require_int (&p, &fileio_flags)
+ || require_comma (&p)
+ || require_int (&p, &fileio_mode)
+ || require_end (p)
+ || fileio_to_host_openflags (fileio_flags, &flags)
+ || fileio_to_host_mode (fileio_mode, &mode))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ /* We do not need to convert MODE, since the fileio protocol
+ uses the standard values. */
+ if (hostio_fs_pid != 0 && the_target->multifs_open != NULL)
+ fd = the_target->multifs_open (hostio_fs_pid, filename,
+ flags, mode);
+ else
+ fd = open (filename, flags, mode);
+
+ if (fd == -1)
+ {
+ hostio_error (own_buf);
+ return;
+ }
+
+ /* Record the new file descriptor. */
+ new_fd = XNEW (struct fd_list);
+ new_fd->fd = fd;
+ new_fd->next = open_fds;
+ open_fds = new_fd;
+
+ hostio_reply (own_buf, fd);
+}
+
+static void
+handle_pread (char *own_buf, int *new_packet_len)
+{
+ int fd, ret, len, offset, bytes_sent;
+ char *p, *data;
+ static int max_reply_size = -1;
+
+ p = own_buf + strlen ("vFile:pread:");
+
+ if (require_int (&p, &fd)
+ || require_comma (&p)
+ || require_valid_fd (fd)
+ || require_int (&p, &len)
+ || require_comma (&p)
+ || require_int (&p, &offset)
+ || require_end (p))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ /* Do not attempt to read more than the maximum number of bytes
+ hostio_reply_with_data can fit in a packet. We may still read
+ too much because of escaping, but this is handled below. */
+ if (max_reply_size == -1)
+ {
+ sprintf (own_buf, "F%x;", PBUFSIZ);
+ max_reply_size = PBUFSIZ - strlen (own_buf);
+ }
+ if (len > max_reply_size)
+ len = max_reply_size;
+
+ data = (char *) xmalloc (len);
+#ifdef HAVE_PREAD
+ ret = pread (fd, data, len, offset);
+#else
+ ret = -1;
+#endif
+ /* If we have no pread or it failed for this file, use lseek/read. */
+ if (ret == -1)
+ {
+ ret = lseek (fd, offset, SEEK_SET);
+ if (ret != -1)
+ ret = read (fd, data, len);
+ }
+
+ if (ret == -1)
+ {
+ hostio_error (own_buf);
+ free (data);
+ return;
+ }
+
+ bytes_sent = hostio_reply_with_data (own_buf, data, ret, new_packet_len);
+
+ /* If we were using read, and the data did not all fit in the reply,
+ we would have to back up using lseek here. With pread it does
+ not matter. But we still have a problem; the return value in the
+ packet might be wrong, so we must fix it. This time it will
+ definitely fit. */
+ if (bytes_sent < ret)
+ bytes_sent = hostio_reply_with_data (own_buf, data, bytes_sent,
+ new_packet_len);
+
+ free (data);
+}
+
+static void
+handle_pwrite (char *own_buf, int packet_len)
+{
+ int fd, ret, len, offset;
+ char *p, *data;
+
+ p = own_buf + strlen ("vFile:pwrite:");
+
+ if (require_int (&p, &fd)
+ || require_comma (&p)
+ || require_valid_fd (fd)
+ || require_int (&p, &offset)
+ || require_comma (&p)
+ || require_data (p, packet_len - (p - own_buf), &data, &len))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+#ifdef HAVE_PWRITE
+ ret = pwrite (fd, data, len, offset);
+#else
+ ret = -1;
+#endif
+ /* If we have no pwrite or it failed for this file, use lseek/write. */
+ if (ret == -1)
+ {
+ ret = lseek (fd, offset, SEEK_SET);
+ if (ret != -1)
+ ret = write (fd, data, len);
+ }
+
+ if (ret == -1)
+ {
+ hostio_error (own_buf);
+ free (data);
+ return;
+ }
+
+ hostio_reply (own_buf, ret);
+ free (data);
+}
+
+static void
+handle_fstat (char *own_buf, int *new_packet_len)
+{
+ int fd, bytes_sent;
+ char *p;
+ struct stat st;
+ struct fio_stat fst;
+
+ p = own_buf + strlen ("vFile:fstat:");
+
+ if (require_int (&p, &fd)
+ || require_valid_fd (fd)
+ || require_end (p))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ if (fstat (fd, &st) == -1)
+ {
+ hostio_error (own_buf);
+ return;
+ }
+
+ host_to_fileio_stat (&st, &fst);
+
+ bytes_sent = hostio_reply_with_data (own_buf,
+ (char *) &fst, sizeof (fst),
+ new_packet_len);
+
+ /* If the response does not fit into a single packet, do not attempt
+ to return a partial response, but simply fail. */
+ if (bytes_sent < sizeof (fst))
+ write_enn (own_buf);
+}
+
+static void
+handle_close (char *own_buf)
+{
+ int fd, ret;
+ char *p;
+ struct fd_list **open_fd_p, *old_fd;
+
+ p = own_buf + strlen ("vFile:close:");
+
+ if (require_int (&p, &fd)
+ || require_valid_fd (fd)
+ || require_end (p))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ ret = close (fd);
+
+ if (ret == -1)
+ {
+ hostio_error (own_buf);
+ return;
+ }
+
+ open_fd_p = &open_fds;
+ /* We know that fd is in the list, thanks to require_valid_fd. */
+ while ((*open_fd_p)->fd != fd)
+ open_fd_p = &(*open_fd_p)->next;
+
+ old_fd = *open_fd_p;
+ *open_fd_p = (*open_fd_p)->next;
+ free (old_fd);
+
+ hostio_reply (own_buf, ret);
+}
+
+static void
+handle_unlink (char *own_buf)
+{
+ char filename[HOSTIO_PATH_MAX];
+ char *p;
+ int ret;
+
+ p = own_buf + strlen ("vFile:unlink:");
+
+ if (require_filename (&p, filename)
+ || require_end (p))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ if (hostio_fs_pid != 0 && the_target->multifs_unlink != NULL)
+ ret = the_target->multifs_unlink (hostio_fs_pid, filename);
+ else
+ ret = unlink (filename);
+
+ if (ret == -1)
+ {
+ hostio_error (own_buf);
+ return;
+ }
+
+ hostio_reply (own_buf, ret);
+}
+
+static void
+handle_readlink (char *own_buf, int *new_packet_len)
+{
+ char filename[HOSTIO_PATH_MAX], linkname[HOSTIO_PATH_MAX];
+ char *p;
+ int ret, bytes_sent;
+
+ p = own_buf + strlen ("vFile:readlink:");
+
+ if (require_filename (&p, filename)
+ || require_end (p))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ if (hostio_fs_pid != 0 && the_target->multifs_readlink != NULL)
+ ret = the_target->multifs_readlink (hostio_fs_pid, filename,
+ linkname,
+ sizeof (linkname) - 1);
+ else
+ ret = readlink (filename, linkname, sizeof (linkname) - 1);
+
+ if (ret == -1)
+ {
+ hostio_error (own_buf);
+ return;
+ }
+
+ bytes_sent = hostio_reply_with_data (own_buf, linkname, ret, new_packet_len);
+
+ /* If the response does not fit into a single packet, do not attempt
+ to return a partial response, but simply fail. */
+ if (bytes_sent < ret)
+ sprintf (own_buf, "F-1,%x", FILEIO_ENAMETOOLONG);
+}
+
+/* Handle all the 'F' file transfer packets. */
+
+int
+handle_vFile (char *own_buf, int packet_len, int *new_packet_len)
+{
+ if (startswith (own_buf, "vFile:open:"))
+ handle_open (own_buf);
+ else if (startswith (own_buf, "vFile:pread:"))
+ handle_pread (own_buf, new_packet_len);
+ else if (startswith (own_buf, "vFile:pwrite:"))
+ handle_pwrite (own_buf, packet_len);
+ else if (startswith (own_buf, "vFile:fstat:"))
+ handle_fstat (own_buf, new_packet_len);
+ else if (startswith (own_buf, "vFile:close:"))
+ handle_close (own_buf);
+ else if (startswith (own_buf, "vFile:unlink:"))
+ handle_unlink (own_buf);
+ else if (startswith (own_buf, "vFile:readlink:"))
+ handle_readlink (own_buf, new_packet_len);
+ else if (startswith (own_buf, "vFile:setfs:"))
+ handle_setfs (own_buf);
+ else
+ return 0;
+
+ return 1;
+}
+++ /dev/null
-/* i387-specific utility functions, for the remote server for GDB.
- Copyright (C) 2000-2020 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 "i387-fp.h"
-#include "gdbsupport/x86-xstate.h"
-
-static const int num_mpx_bnd_registers = 4;
-static const int num_mpx_cfg_registers = 2;
-static const int num_avx512_k_registers = 8;
-static const int num_avx512_zmmh_low_registers = 16;
-static const int num_avx512_zmmh_high_registers = 16;
-static const int num_avx512_ymmh_registers = 16;
-static const int num_avx512_xmm_registers = 16;
-static const int num_pkeys_registers = 1;
-
-/* Note: These functions preserve the reserved bits in control registers.
- However, gdbserver promptly throws away that information. */
-
-/* These structs should have the proper sizes and alignment on both
- i386 and x86-64 machines. */
-
-struct i387_fsave {
- /* All these are only sixteen bits, plus padding, except for fop (which
- is only eleven bits), and fooff / fioff (which are 32 bits each). */
- unsigned short fctrl;
- unsigned short pad1;
- unsigned short fstat;
- unsigned short pad2;
- unsigned short ftag;
- unsigned short pad3;
- unsigned int fioff;
- unsigned short fiseg;
- unsigned short fop;
- unsigned int fooff;
- unsigned short foseg;
- unsigned short pad4;
-
- /* Space for eight 80-bit FP values. */
- unsigned char st_space[80];
-};
-
-struct i387_fxsave {
- /* All these are only sixteen bits, plus padding, except for fop (which
- is only eleven bits), and fooff / fioff (which are 32 bits each). */
- unsigned short fctrl;
- unsigned short fstat;
- unsigned short ftag;
- unsigned short fop;
- unsigned int fioff;
- unsigned short fiseg;
- unsigned short pad1;
- unsigned int fooff;
- unsigned short foseg;
- unsigned short pad12;
-
- unsigned int mxcsr;
- unsigned int pad3;
-
- /* Space for eight 80-bit FP values in 128-bit spaces. */
- unsigned char st_space[128];
-
- /* Space for eight 128-bit XMM values, or 16 on x86-64. */
- unsigned char xmm_space[256];
-};
-
-struct i387_xsave {
- /* All these are only sixteen bits, plus padding, except for fop (which
- is only eleven bits), and fooff / fioff (which are 32 bits each). */
- unsigned short fctrl;
- unsigned short fstat;
- unsigned short ftag;
- unsigned short fop;
- unsigned int fioff;
- unsigned short fiseg;
- unsigned short pad1;
- unsigned int fooff;
- unsigned short foseg;
- unsigned short pad12;
-
- unsigned int mxcsr;
- unsigned int mxcsr_mask;
-
- /* Space for eight 80-bit FP values in 128-bit spaces. */
- unsigned char st_space[128];
-
- /* Space for eight 128-bit XMM values, or 16 on x86-64. */
- unsigned char xmm_space[256];
-
- unsigned char reserved1[48];
-
- /* The extended control register 0 (the XFEATURE_ENABLED_MASK
- register). */
- unsigned long long xcr0;
-
- unsigned char reserved2[40];
-
- /* The XSTATE_BV bit vector. */
- unsigned long long xstate_bv;
-
- unsigned char reserved3[56];
-
- /* Space for eight upper 128-bit YMM values, or 16 on x86-64. */
- unsigned char ymmh_space[256];
-
- unsigned char reserved4[128];
-
- /* Space for 4 bound registers values of 128 bits. */
- unsigned char mpx_bnd_space[64];
-
- /* Space for 2 MPX configuration registers of 64 bits
- plus reserved space. */
- unsigned char mpx_cfg_space[16];
-
- unsigned char reserved5[48];
-
- /* Space for 8 OpMask register values of 64 bits. */
- unsigned char k_space[64];
-
- /* Space for 16 256-bit zmm0-15. */
- unsigned char zmmh_low_space[512];
-
- /* Space for 16 512-bit zmm16-31 values. */
- unsigned char zmmh_high_space[1024];
-
- /* Space for 1 32-bit PKRU register. The HW XSTATE size for this feature is
- actually 64 bits, but WRPKRU/RDPKRU instructions ignore upper 32 bits. */
- unsigned char pkru_space[8];
-};
-
-void
-i387_cache_to_fsave (struct regcache *regcache, void *buf)
-{
- struct i387_fsave *fp = (struct i387_fsave *) buf;
- int i;
- int st0_regnum = find_regno (regcache->tdesc, "st0");
- unsigned long val2;
-
- for (i = 0; i < 8; i++)
- collect_register (regcache, i + st0_regnum,
- ((char *) &fp->st_space[0]) + i * 10);
-
- fp->fioff = regcache_raw_get_unsigned_by_name (regcache, "fioff");
- fp->fooff = regcache_raw_get_unsigned_by_name (regcache, "fooff");
-
- /* This one's 11 bits... */
- val2 = regcache_raw_get_unsigned_by_name (regcache, "fop");
- fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
-
- /* Some registers are 16-bit. */
- fp->fctrl = regcache_raw_get_unsigned_by_name (regcache, "fctrl");
- fp->fstat = regcache_raw_get_unsigned_by_name (regcache, "fstat");
- fp->ftag = regcache_raw_get_unsigned_by_name (regcache, "ftag");
- fp->fiseg = regcache_raw_get_unsigned_by_name (regcache, "fiseg");
- fp->foseg = regcache_raw_get_unsigned_by_name (regcache, "foseg");
-}
-
-void
-i387_fsave_to_cache (struct regcache *regcache, const void *buf)
-{
- struct i387_fsave *fp = (struct i387_fsave *) buf;
- int i;
- int st0_regnum = find_regno (regcache->tdesc, "st0");
- unsigned long val;
-
- for (i = 0; i < 8; i++)
- supply_register (regcache, i + st0_regnum,
- ((char *) &fp->st_space[0]) + i * 10);
-
- supply_register_by_name (regcache, "fioff", &fp->fioff);
- supply_register_by_name (regcache, "fooff", &fp->fooff);
-
- /* Some registers are 16-bit. */
- val = fp->fctrl & 0xFFFF;
- supply_register_by_name (regcache, "fctrl", &val);
-
- val = fp->fstat & 0xFFFF;
- supply_register_by_name (regcache, "fstat", &val);
-
- val = fp->ftag & 0xFFFF;
- supply_register_by_name (regcache, "ftag", &val);
-
- val = fp->fiseg & 0xFFFF;
- supply_register_by_name (regcache, "fiseg", &val);
-
- val = fp->foseg & 0xFFFF;
- supply_register_by_name (regcache, "foseg", &val);
-
- /* fop has only 11 valid bits. */
- val = (fp->fop) & 0x7FF;
- supply_register_by_name (regcache, "fop", &val);
-}
-
-void
-i387_cache_to_fxsave (struct regcache *regcache, void *buf)
-{
- struct i387_fxsave *fp = (struct i387_fxsave *) buf;
- int i;
- int st0_regnum = find_regno (regcache->tdesc, "st0");
- int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
- unsigned long val, val2;
- /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
- int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
-
- for (i = 0; i < 8; i++)
- collect_register (regcache, i + st0_regnum,
- ((char *) &fp->st_space[0]) + i * 16);
- for (i = 0; i < num_xmm_registers; i++)
- collect_register (regcache, i + xmm0_regnum,
- ((char *) &fp->xmm_space[0]) + i * 16);
-
- fp->fioff = regcache_raw_get_unsigned_by_name (regcache, "fioff");
- fp->fooff = regcache_raw_get_unsigned_by_name (regcache, "fooff");
- fp->mxcsr = regcache_raw_get_unsigned_by_name (regcache, "mxcsr");
-
- /* This one's 11 bits... */
- val2 = regcache_raw_get_unsigned_by_name (regcache, "fop");
- fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
-
- /* Some registers are 16-bit. */
- fp->fctrl = regcache_raw_get_unsigned_by_name (regcache, "fctrl");
- fp->fstat = regcache_raw_get_unsigned_by_name (regcache, "fstat");
-
- /* Convert to the simplifed tag form stored in fxsave data. */
- val = regcache_raw_get_unsigned_by_name (regcache, "ftag");
- val2 = 0;
- for (i = 7; i >= 0; i--)
- {
- int tag = (val >> (i * 2)) & 3;
-
- if (tag != 3)
- val2 |= (1 << i);
- }
- fp->ftag = val2;
-
- fp->fiseg = regcache_raw_get_unsigned_by_name (regcache, "fiseg");
- fp->foseg = regcache_raw_get_unsigned_by_name (regcache, "foseg");
-}
-
-void
-i387_cache_to_xsave (struct regcache *regcache, void *buf)
-{
- struct i387_xsave *fp = (struct i387_xsave *) buf;
- int i;
- unsigned long val, val2;
- unsigned long long xstate_bv = 0;
- unsigned long long clear_bv = 0;
- char raw[64];
- char *p;
- /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
- int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
-
- /* The supported bits in `xstat_bv' are 8 bytes. Clear part in
- vector registers if its bit in xstat_bv is zero. */
- clear_bv = (~fp->xstate_bv) & x86_xcr0;
-
- /* Clear part in x87 and vector registers if its bit in xstat_bv is
- zero. */
- if (clear_bv)
- {
- if ((clear_bv & X86_XSTATE_X87))
- {
- for (i = 0; i < 8; i++)
- memset (((char *) &fp->st_space[0]) + i * 16, 0, 10);
-
- fp->fioff = 0;
- fp->fooff = 0;
- fp->fctrl = I387_FCTRL_INIT_VAL;
- fp->fstat = 0;
- fp->ftag = 0;
- fp->fiseg = 0;
- fp->foseg = 0;
- fp->fop = 0;
- }
-
- if ((clear_bv & X86_XSTATE_SSE))
- for (i = 0; i < num_xmm_registers; i++)
- memset (((char *) &fp->xmm_space[0]) + i * 16, 0, 16);
-
- if ((clear_bv & X86_XSTATE_AVX))
- for (i = 0; i < num_xmm_registers; i++)
- memset (((char *) &fp->ymmh_space[0]) + i * 16, 0, 16);
-
- if ((clear_bv & X86_XSTATE_SSE) && (clear_bv & X86_XSTATE_AVX))
- memset (((char *) &fp->mxcsr), 0, 4);
-
- if ((clear_bv & X86_XSTATE_BNDREGS))
- for (i = 0; i < num_mpx_bnd_registers; i++)
- memset (((char *) &fp->mpx_bnd_space[0]) + i * 16, 0, 16);
-
- if ((clear_bv & X86_XSTATE_BNDCFG))
- for (i = 0; i < num_mpx_cfg_registers; i++)
- memset (((char *) &fp->mpx_cfg_space[0]) + i * 8, 0, 8);
-
- if ((clear_bv & X86_XSTATE_K))
- for (i = 0; i < num_avx512_k_registers; i++)
- memset (((char *) &fp->k_space[0]) + i * 8, 0, 8);
-
- if ((clear_bv & X86_XSTATE_ZMM_H))
- for (i = 0; i < num_avx512_zmmh_low_registers; i++)
- memset (((char *) &fp->zmmh_low_space[0]) + i * 32, 0, 32);
-
- if ((clear_bv & X86_XSTATE_ZMM))
- {
- for (i = 0; i < num_avx512_zmmh_high_registers; i++)
- memset (((char *) &fp->zmmh_low_space[0]) + 32 + i * 64, 0, 32);
- for (i = 0; i < num_avx512_xmm_registers; i++)
- memset (((char *) &fp->zmmh_high_space[0]) + i * 64, 0, 16);
- for (i = 0; i < num_avx512_ymmh_registers; i++)
- memset (((char *) &fp->zmmh_high_space[0]) + 16 + i * 64, 0, 16);
- }
-
- if ((clear_bv & X86_XSTATE_PKRU))
- for (i = 0; i < num_pkeys_registers; i++)
- memset (((char *) &fp->pkru_space[0]) + i * 4, 0, 4);
- }
-
- /* Check if any x87 registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_X87))
- {
- int st0_regnum = find_regno (regcache->tdesc, "st0");
-
- for (i = 0; i < 8; i++)
- {
- collect_register (regcache, i + st0_regnum, raw);
- p = ((char *) &fp->st_space[0]) + i * 16;
- if (memcmp (raw, p, 10))
- {
- xstate_bv |= X86_XSTATE_X87;
- memcpy (p, raw, 10);
- }
- }
- }
-
- /* Check if any SSE registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_SSE))
- {
- int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
-
- for (i = 0; i < num_xmm_registers; i++)
- {
- collect_register (regcache, i + xmm0_regnum, raw);
- p = ((char *) &fp->xmm_space[0]) + i * 16;
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_SSE;
- memcpy (p, raw, 16);
- }
- }
- }
-
- /* Check if any AVX registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_AVX))
- {
- int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
-
- for (i = 0; i < num_xmm_registers; i++)
- {
- collect_register (regcache, i + ymm0h_regnum, raw);
- p = ((char *) &fp->ymmh_space[0]) + i * 16;
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_AVX;
- memcpy (p, raw, 16);
- }
- }
- }
-
- /* Check if any bound register has changed. */
- if ((x86_xcr0 & X86_XSTATE_BNDREGS))
- {
- int bnd0r_regnum = find_regno (regcache->tdesc, "bnd0raw");
-
- for (i = 0; i < num_mpx_bnd_registers; i++)
- {
- collect_register (regcache, i + bnd0r_regnum, raw);
- p = ((char *) &fp->mpx_bnd_space[0]) + i * 16;
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_BNDREGS;
- memcpy (p, raw, 16);
- }
- }
- }
-
- /* Check if any status register has changed. */
- if ((x86_xcr0 & X86_XSTATE_BNDCFG))
- {
- int bndcfg_regnum = find_regno (regcache->tdesc, "bndcfgu");
-
- for (i = 0; i < num_mpx_cfg_registers; i++)
- {
- collect_register (regcache, i + bndcfg_regnum, raw);
- p = ((char *) &fp->mpx_cfg_space[0]) + i * 8;
- if (memcmp (raw, p, 8))
- {
- xstate_bv |= X86_XSTATE_BNDCFG;
- memcpy (p, raw, 8);
- }
- }
- }
-
- /* Check if any K registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_K))
- {
- int k0_regnum = find_regno (regcache->tdesc, "k0");
-
- for (i = 0; i < num_avx512_k_registers; i++)
- {
- collect_register (regcache, i + k0_regnum, raw);
- p = ((char *) &fp->k_space[0]) + i * 8;
- if (memcmp (raw, p, 8) != 0)
- {
- xstate_bv |= X86_XSTATE_K;
- memcpy (p, raw, 8);
- }
- }
- }
-
- /* Check if any of ZMM0H-ZMM15H registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_ZMM_H))
- {
- int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
-
- for (i = 0; i < num_avx512_zmmh_low_registers; i++)
- {
- collect_register (regcache, i + zmm0h_regnum, raw);
- p = ((char *) &fp->zmmh_low_space[0]) + i * 32;
- if (memcmp (raw, p, 32) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM_H;
- memcpy (p, raw, 32);
- }
- }
- }
-
- /* Check if any of ZMM16H-ZMM31H registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_ZMM))
- {
- int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
-
- for (i = 0; i < num_avx512_zmmh_high_registers; i++)
- {
- collect_register (regcache, i + zmm16h_regnum, raw);
- p = ((char *) &fp->zmmh_high_space[0]) + 32 + i * 64;
- if (memcmp (raw, p, 32) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM;
- memcpy (p, raw, 32);
- }
- }
- }
-
- /* Check if any XMM_AVX512 registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_ZMM))
- {
- int xmm_avx512_regnum = find_regno (regcache->tdesc, "xmm16");
-
- for (i = 0; i < num_avx512_xmm_registers; i++)
- {
- collect_register (regcache, i + xmm_avx512_regnum, raw);
- p = ((char *) &fp->zmmh_high_space[0]) + i * 64;
- if (memcmp (raw, p, 16) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM;
- memcpy (p, raw, 16);
- }
- }
- }
-
- /* Check if any YMMH_AVX512 registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_ZMM))
- {
- int ymmh_avx512_regnum = find_regno (regcache->tdesc, "ymm16h");
-
- for (i = 0; i < num_avx512_ymmh_registers; i++)
- {
- collect_register (regcache, i + ymmh_avx512_regnum, raw);
- p = ((char *) &fp->zmmh_high_space[0]) + 16 + i * 64;
- if (memcmp (raw, p, 16) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM;
- memcpy (p, raw, 16);
- }
- }
- }
-
- /* Check if any PKEYS registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_PKRU))
- {
- int pkru_regnum = find_regno (regcache->tdesc, "pkru");
-
- for (i = 0; i < num_pkeys_registers; i++)
- {
- collect_register (regcache, i + pkru_regnum, raw);
- p = ((char *) &fp->pkru_space[0]) + i * 4;
- if (memcmp (raw, p, 4) != 0)
- {
- xstate_bv |= X86_XSTATE_PKRU;
- memcpy (p, raw, 4);
- }
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_SSE) || (x86_xcr0 & X86_XSTATE_AVX))
- {
- collect_register_by_name (regcache, "mxcsr", raw);
- if (memcmp (raw, &fp->mxcsr, 4) != 0)
- {
- if (((fp->xstate_bv | xstate_bv)
- & (X86_XSTATE_SSE | X86_XSTATE_AVX)) == 0)
- xstate_bv |= X86_XSTATE_SSE;
- memcpy (&fp->mxcsr, raw, 4);
- }
- }
-
- if (x86_xcr0 & X86_XSTATE_X87)
- {
- collect_register_by_name (regcache, "fioff", raw);
- if (memcmp (raw, &fp->fioff, 4) != 0)
- {
- xstate_bv |= X86_XSTATE_X87;
- memcpy (&fp->fioff, raw, 4);
- }
-
- collect_register_by_name (regcache, "fooff", raw);
- if (memcmp (raw, &fp->fooff, 4) != 0)
- {
- xstate_bv |= X86_XSTATE_X87;
- memcpy (&fp->fooff, raw, 4);
- }
-
- /* This one's 11 bits... */
- val2 = regcache_raw_get_unsigned_by_name (regcache, "fop");
- val2 = (val2 & 0x7FF) | (fp->fop & 0xF800);
- if (fp->fop != val2)
- {
- xstate_bv |= X86_XSTATE_X87;
- fp->fop = val2;
- }
-
- /* Some registers are 16-bit. */
- val = regcache_raw_get_unsigned_by_name (regcache, "fctrl");
- if (fp->fctrl != val)
- {
- xstate_bv |= X86_XSTATE_X87;
- fp->fctrl = val;
- }
-
- val = regcache_raw_get_unsigned_by_name (regcache, "fstat");
- if (fp->fstat != val)
- {
- xstate_bv |= X86_XSTATE_X87;
- fp->fstat = val;
- }
-
- /* Convert to the simplifed tag form stored in fxsave data. */
- val = regcache_raw_get_unsigned_by_name (regcache, "ftag");
- val2 = 0;
- for (i = 7; i >= 0; i--)
- {
- int tag = (val >> (i * 2)) & 3;
-
- if (tag != 3)
- val2 |= (1 << i);
- }
- if (fp->ftag != val2)
- {
- xstate_bv |= X86_XSTATE_X87;
- fp->ftag = val2;
- }
-
- val = regcache_raw_get_unsigned_by_name (regcache, "fiseg");
- if (fp->fiseg != val)
- {
- xstate_bv |= X86_XSTATE_X87;
- fp->fiseg = val;
- }
-
- val = regcache_raw_get_unsigned_by_name (regcache, "foseg");
- if (fp->foseg != val)
- {
- xstate_bv |= X86_XSTATE_X87;
- fp->foseg = val;
- }
- }
-
- /* Update the corresponding bits in xstate_bv if any SSE/AVX
- registers are changed. */
- fp->xstate_bv |= xstate_bv;
-}
-
-static int
-i387_ftag (struct i387_fxsave *fp, int regno)
-{
- unsigned char *raw = &fp->st_space[regno * 16];
- unsigned int exponent;
- unsigned long fraction[2];
- int integer;
-
- integer = raw[7] & 0x80;
- exponent = (((raw[9] & 0x7f) << 8) | raw[8]);
- fraction[0] = ((raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]);
- fraction[1] = (((raw[7] & 0x7f) << 24) | (raw[6] << 16)
- | (raw[5] << 8) | raw[4]);
-
- if (exponent == 0x7fff)
- {
- /* Special. */
- return (2);
- }
- else if (exponent == 0x0000)
- {
- if (fraction[0] == 0x0000 && fraction[1] == 0x0000 && !integer)
- {
- /* Zero. */
- return (1);
- }
- else
- {
- /* Special. */
- return (2);
- }
- }
- else
- {
- if (integer)
- {
- /* Valid. */
- return (0);
- }
- else
- {
- /* Special. */
- return (2);
- }
- }
-}
-
-void
-i387_fxsave_to_cache (struct regcache *regcache, const void *buf)
-{
- struct i387_fxsave *fp = (struct i387_fxsave *) buf;
- int i, top;
- int st0_regnum = find_regno (regcache->tdesc, "st0");
- int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
- unsigned long val;
- /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
- int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
-
- for (i = 0; i < 8; i++)
- supply_register (regcache, i + st0_regnum,
- ((char *) &fp->st_space[0]) + i * 16);
- for (i = 0; i < num_xmm_registers; i++)
- supply_register (regcache, i + xmm0_regnum,
- ((char *) &fp->xmm_space[0]) + i * 16);
-
- supply_register_by_name (regcache, "fioff", &fp->fioff);
- supply_register_by_name (regcache, "fooff", &fp->fooff);
- supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
-
- /* Some registers are 16-bit. */
- val = fp->fctrl & 0xFFFF;
- supply_register_by_name (regcache, "fctrl", &val);
-
- val = fp->fstat & 0xFFFF;
- supply_register_by_name (regcache, "fstat", &val);
-
- /* Generate the form of ftag data that GDB expects. */
- top = (fp->fstat >> 11) & 0x7;
- val = 0;
- for (i = 7; i >= 0; i--)
- {
- int tag;
- if (fp->ftag & (1 << i))
- tag = i387_ftag (fp, (i + 8 - top) % 8);
- else
- tag = 3;
- val |= tag << (2 * i);
- }
- supply_register_by_name (regcache, "ftag", &val);
-
- val = fp->fiseg & 0xFFFF;
- supply_register_by_name (regcache, "fiseg", &val);
-
- val = fp->foseg & 0xFFFF;
- supply_register_by_name (regcache, "foseg", &val);
-
- val = (fp->fop) & 0x7FF;
- supply_register_by_name (regcache, "fop", &val);
-}
-
-void
-i387_xsave_to_cache (struct regcache *regcache, const void *buf)
-{
- struct i387_xsave *fp = (struct i387_xsave *) buf;
- struct i387_fxsave *fxp = (struct i387_fxsave *) buf;
- int i, top;
- unsigned long val;
- unsigned long long clear_bv;
- gdb_byte *p;
- /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
- int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
-
- /* The supported bits in `xstat_bv' are 8 bytes. Clear part in
- vector registers if its bit in xstat_bv is zero. */
- clear_bv = (~fp->xstate_bv) & x86_xcr0;
-
- /* Check if any x87 registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_X87) != 0)
- {
- int st0_regnum = find_regno (regcache->tdesc, "st0");
-
- if ((clear_bv & X86_XSTATE_X87) != 0)
- {
- for (i = 0; i < 8; i++)
- supply_register_zeroed (regcache, i + st0_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->st_space[0];
- for (i = 0; i < 8; i++)
- supply_register (regcache, i + st0_regnum, p + i * 16);
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_SSE) != 0)
- {
- int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
-
- if ((clear_bv & X86_XSTATE_SSE))
- {
- for (i = 0; i < num_xmm_registers; i++)
- supply_register_zeroed (regcache, i + xmm0_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->xmm_space[0];
- for (i = 0; i < num_xmm_registers; i++)
- supply_register (regcache, i + xmm0_regnum, p + i * 16);
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_AVX) != 0)
- {
- int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
-
- if ((clear_bv & X86_XSTATE_AVX) != 0)
- {
- for (i = 0; i < num_xmm_registers; i++)
- supply_register_zeroed (regcache, i + ymm0h_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->ymmh_space[0];
- for (i = 0; i < num_xmm_registers; i++)
- supply_register (regcache, i + ymm0h_regnum, p + i * 16);
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_BNDREGS))
- {
- int bnd0r_regnum = find_regno (regcache->tdesc, "bnd0raw");
-
-
- if ((clear_bv & X86_XSTATE_BNDREGS) != 0)
- {
- for (i = 0; i < num_mpx_bnd_registers; i++)
- supply_register_zeroed (regcache, i + bnd0r_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->mpx_bnd_space[0];
- for (i = 0; i < num_mpx_bnd_registers; i++)
- supply_register (regcache, i + bnd0r_regnum, p + i * 16);
- }
-
- }
-
- if ((x86_xcr0 & X86_XSTATE_BNDCFG))
- {
- int bndcfg_regnum = find_regno (regcache->tdesc, "bndcfgu");
-
- if ((clear_bv & X86_XSTATE_BNDCFG) != 0)
- {
- for (i = 0; i < num_mpx_cfg_registers; i++)
- supply_register_zeroed (regcache, i + bndcfg_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->mpx_cfg_space[0];
- for (i = 0; i < num_mpx_cfg_registers; i++)
- supply_register (regcache, i + bndcfg_regnum, p + i * 8);
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_K) != 0)
- {
- int k0_regnum = find_regno (regcache->tdesc, "k0");
-
- if ((clear_bv & X86_XSTATE_K) != 0)
- {
- for (i = 0; i < num_avx512_k_registers; i++)
- supply_register_zeroed (regcache, i + k0_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->k_space[0];
- for (i = 0; i < num_avx512_k_registers; i++)
- supply_register (regcache, i + k0_regnum, p + i * 8);
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_ZMM_H) != 0)
- {
- int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
-
- if ((clear_bv & X86_XSTATE_ZMM_H) != 0)
- {
- for (i = 0; i < num_avx512_zmmh_low_registers; i++)
- supply_register_zeroed (regcache, i + zmm0h_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->zmmh_low_space[0];
- for (i = 0; i < num_avx512_zmmh_low_registers; i++)
- supply_register (regcache, i + zmm0h_regnum, p + i * 32);
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_ZMM) != 0)
- {
- int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
- int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
- int xmm16_regnum = find_regno (regcache->tdesc, "xmm16");
-
- if ((clear_bv & X86_XSTATE_ZMM) != 0)
- {
- for (i = 0; i < num_avx512_zmmh_high_registers; i++)
- supply_register_zeroed (regcache, i + zmm16h_regnum);
- for (i = 0; i < num_avx512_ymmh_registers; i++)
- supply_register_zeroed (regcache, i + ymm16h_regnum);
- for (i = 0; i < num_avx512_xmm_registers; i++)
- supply_register_zeroed (regcache, i + xmm16_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->zmmh_high_space[0];
- for (i = 0; i < num_avx512_zmmh_high_registers; i++)
- supply_register (regcache, i + zmm16h_regnum, p + 32 + i * 64);
- for (i = 0; i < num_avx512_ymmh_registers; i++)
- supply_register (regcache, i + ymm16h_regnum, p + 16 + i * 64);
- for (i = 0; i < num_avx512_xmm_registers; i++)
- supply_register (regcache, i + xmm16_regnum, p + i * 64);
- }
- }
-
- if ((x86_xcr0 & X86_XSTATE_PKRU) != 0)
- {
- int pkru_regnum = find_regno (regcache->tdesc, "pkru");
-
- if ((clear_bv & X86_XSTATE_PKRU) != 0)
- {
- for (i = 0; i < num_pkeys_registers; i++)
- supply_register_zeroed (regcache, i + pkru_regnum);
- }
- else
- {
- p = (gdb_byte *) &fp->pkru_space[0];
- for (i = 0; i < num_pkeys_registers; i++)
- supply_register (regcache, i + pkru_regnum, p + i * 4);
- }
- }
-
- if ((clear_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX))
- == (X86_XSTATE_SSE | X86_XSTATE_AVX))
- {
- unsigned int default_mxcsr = I387_MXCSR_INIT_VAL;
- supply_register_by_name (regcache, "mxcsr", &default_mxcsr);
- }
- else
- supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
-
- if ((clear_bv & X86_XSTATE_X87) != 0)
- {
- supply_register_by_name_zeroed (regcache, "fioff");
- supply_register_by_name_zeroed (regcache, "fooff");
-
- val = I387_FCTRL_INIT_VAL;
- supply_register_by_name (regcache, "fctrl", &val);
-
- supply_register_by_name_zeroed (regcache, "fstat");
-
- val = 0xFFFF;
- supply_register_by_name (regcache, "ftag", &val);
-
- supply_register_by_name_zeroed (regcache, "fiseg");
- supply_register_by_name_zeroed (regcache, "foseg");
- supply_register_by_name_zeroed (regcache, "fop");
- }
- else
- {
- supply_register_by_name (regcache, "fioff", &fp->fioff);
- supply_register_by_name (regcache, "fooff", &fp->fooff);
-
- /* Some registers are 16-bit. */
- val = fp->fctrl & 0xFFFF;
- supply_register_by_name (regcache, "fctrl", &val);
-
- val = fp->fstat & 0xFFFF;
- supply_register_by_name (regcache, "fstat", &val);
-
- /* Generate the form of ftag data that GDB expects. */
- top = (fp->fstat >> 11) & 0x7;
- val = 0;
- for (i = 7; i >= 0; i--)
- {
- int tag;
- if (fp->ftag & (1 << i))
- tag = i387_ftag (fxp, (i + 8 - top) % 8);
- else
- tag = 3;
- val |= tag << (2 * i);
- }
- supply_register_by_name (regcache, "ftag", &val);
-
- val = fp->fiseg & 0xFFFF;
- supply_register_by_name (regcache, "fiseg", &val);
-
- val = fp->foseg & 0xFFFF;
- supply_register_by_name (regcache, "foseg", &val);
-
- val = (fp->fop) & 0x7FF;
- supply_register_by_name (regcache, "fop", &val);
- }
-}
-
-/* Default to SSE. */
-unsigned long long x86_xcr0 = X86_XSTATE_SSE_MASK;
--- /dev/null
+/* i387-specific utility functions, for the remote server for GDB.
+ Copyright (C) 2000-2020 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 "i387-fp.h"
+#include "gdbsupport/x86-xstate.h"
+
+static const int num_mpx_bnd_registers = 4;
+static const int num_mpx_cfg_registers = 2;
+static const int num_avx512_k_registers = 8;
+static const int num_avx512_zmmh_low_registers = 16;
+static const int num_avx512_zmmh_high_registers = 16;
+static const int num_avx512_ymmh_registers = 16;
+static const int num_avx512_xmm_registers = 16;
+static const int num_pkeys_registers = 1;
+
+/* Note: These functions preserve the reserved bits in control registers.
+ However, gdbserver promptly throws away that information. */
+
+/* These structs should have the proper sizes and alignment on both
+ i386 and x86-64 machines. */
+
+struct i387_fsave {
+ /* All these are only sixteen bits, plus padding, except for fop (which
+ is only eleven bits), and fooff / fioff (which are 32 bits each). */
+ unsigned short fctrl;
+ unsigned short pad1;
+ unsigned short fstat;
+ unsigned short pad2;
+ unsigned short ftag;
+ unsigned short pad3;
+ unsigned int fioff;
+ unsigned short fiseg;
+ unsigned short fop;
+ unsigned int fooff;
+ unsigned short foseg;
+ unsigned short pad4;
+
+ /* Space for eight 80-bit FP values. */
+ unsigned char st_space[80];
+};
+
+struct i387_fxsave {
+ /* All these are only sixteen bits, plus padding, except for fop (which
+ is only eleven bits), and fooff / fioff (which are 32 bits each). */
+ unsigned short fctrl;
+ unsigned short fstat;
+ unsigned short ftag;
+ unsigned short fop;
+ unsigned int fioff;
+ unsigned short fiseg;
+ unsigned short pad1;
+ unsigned int fooff;
+ unsigned short foseg;
+ unsigned short pad12;
+
+ unsigned int mxcsr;
+ unsigned int pad3;
+
+ /* Space for eight 80-bit FP values in 128-bit spaces. */
+ unsigned char st_space[128];
+
+ /* Space for eight 128-bit XMM values, or 16 on x86-64. */
+ unsigned char xmm_space[256];
+};
+
+struct i387_xsave {
+ /* All these are only sixteen bits, plus padding, except for fop (which
+ is only eleven bits), and fooff / fioff (which are 32 bits each). */
+ unsigned short fctrl;
+ unsigned short fstat;
+ unsigned short ftag;
+ unsigned short fop;
+ unsigned int fioff;
+ unsigned short fiseg;
+ unsigned short pad1;
+ unsigned int fooff;
+ unsigned short foseg;
+ unsigned short pad12;
+
+ unsigned int mxcsr;
+ unsigned int mxcsr_mask;
+
+ /* Space for eight 80-bit FP values in 128-bit spaces. */
+ unsigned char st_space[128];
+
+ /* Space for eight 128-bit XMM values, or 16 on x86-64. */
+ unsigned char xmm_space[256];
+
+ unsigned char reserved1[48];
+
+ /* The extended control register 0 (the XFEATURE_ENABLED_MASK
+ register). */
+ unsigned long long xcr0;
+
+ unsigned char reserved2[40];
+
+ /* The XSTATE_BV bit vector. */
+ unsigned long long xstate_bv;
+
+ unsigned char reserved3[56];
+
+ /* Space for eight upper 128-bit YMM values, or 16 on x86-64. */
+ unsigned char ymmh_space[256];
+
+ unsigned char reserved4[128];
+
+ /* Space for 4 bound registers values of 128 bits. */
+ unsigned char mpx_bnd_space[64];
+
+ /* Space for 2 MPX configuration registers of 64 bits
+ plus reserved space. */
+ unsigned char mpx_cfg_space[16];
+
+ unsigned char reserved5[48];
+
+ /* Space for 8 OpMask register values of 64 bits. */
+ unsigned char k_space[64];
+
+ /* Space for 16 256-bit zmm0-15. */
+ unsigned char zmmh_low_space[512];
+
+ /* Space for 16 512-bit zmm16-31 values. */
+ unsigned char zmmh_high_space[1024];
+
+ /* Space for 1 32-bit PKRU register. The HW XSTATE size for this feature is
+ actually 64 bits, but WRPKRU/RDPKRU instructions ignore upper 32 bits. */
+ unsigned char pkru_space[8];
+};
+
+void
+i387_cache_to_fsave (struct regcache *regcache, void *buf)
+{
+ struct i387_fsave *fp = (struct i387_fsave *) buf;
+ int i;
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+ unsigned long val2;
+
+ for (i = 0; i < 8; i++)
+ collect_register (regcache, i + st0_regnum,
+ ((char *) &fp->st_space[0]) + i * 10);
+
+ fp->fioff = regcache_raw_get_unsigned_by_name (regcache, "fioff");
+ fp->fooff = regcache_raw_get_unsigned_by_name (regcache, "fooff");
+
+ /* This one's 11 bits... */
+ val2 = regcache_raw_get_unsigned_by_name (regcache, "fop");
+ fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
+
+ /* Some registers are 16-bit. */
+ fp->fctrl = regcache_raw_get_unsigned_by_name (regcache, "fctrl");
+ fp->fstat = regcache_raw_get_unsigned_by_name (regcache, "fstat");
+ fp->ftag = regcache_raw_get_unsigned_by_name (regcache, "ftag");
+ fp->fiseg = regcache_raw_get_unsigned_by_name (regcache, "fiseg");
+ fp->foseg = regcache_raw_get_unsigned_by_name (regcache, "foseg");
+}
+
+void
+i387_fsave_to_cache (struct regcache *regcache, const void *buf)
+{
+ struct i387_fsave *fp = (struct i387_fsave *) buf;
+ int i;
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+ unsigned long val;
+
+ for (i = 0; i < 8; i++)
+ supply_register (regcache, i + st0_regnum,
+ ((char *) &fp->st_space[0]) + i * 10);
+
+ supply_register_by_name (regcache, "fioff", &fp->fioff);
+ supply_register_by_name (regcache, "fooff", &fp->fooff);
+
+ /* Some registers are 16-bit. */
+ val = fp->fctrl & 0xFFFF;
+ supply_register_by_name (regcache, "fctrl", &val);
+
+ val = fp->fstat & 0xFFFF;
+ supply_register_by_name (regcache, "fstat", &val);
+
+ val = fp->ftag & 0xFFFF;
+ supply_register_by_name (regcache, "ftag", &val);
+
+ val = fp->fiseg & 0xFFFF;
+ supply_register_by_name (regcache, "fiseg", &val);
+
+ val = fp->foseg & 0xFFFF;
+ supply_register_by_name (regcache, "foseg", &val);
+
+ /* fop has only 11 valid bits. */
+ val = (fp->fop) & 0x7FF;
+ supply_register_by_name (regcache, "fop", &val);
+}
+
+void
+i387_cache_to_fxsave (struct regcache *regcache, void *buf)
+{
+ struct i387_fxsave *fp = (struct i387_fxsave *) buf;
+ int i;
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
+ unsigned long val, val2;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
+
+ for (i = 0; i < 8; i++)
+ collect_register (regcache, i + st0_regnum,
+ ((char *) &fp->st_space[0]) + i * 16);
+ for (i = 0; i < num_xmm_registers; i++)
+ collect_register (regcache, i + xmm0_regnum,
+ ((char *) &fp->xmm_space[0]) + i * 16);
+
+ fp->fioff = regcache_raw_get_unsigned_by_name (regcache, "fioff");
+ fp->fooff = regcache_raw_get_unsigned_by_name (regcache, "fooff");
+ fp->mxcsr = regcache_raw_get_unsigned_by_name (regcache, "mxcsr");
+
+ /* This one's 11 bits... */
+ val2 = regcache_raw_get_unsigned_by_name (regcache, "fop");
+ fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
+
+ /* Some registers are 16-bit. */
+ fp->fctrl = regcache_raw_get_unsigned_by_name (regcache, "fctrl");
+ fp->fstat = regcache_raw_get_unsigned_by_name (regcache, "fstat");
+
+ /* Convert to the simplifed tag form stored in fxsave data. */
+ val = regcache_raw_get_unsigned_by_name (regcache, "ftag");
+ val2 = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag = (val >> (i * 2)) & 3;
+
+ if (tag != 3)
+ val2 |= (1 << i);
+ }
+ fp->ftag = val2;
+
+ fp->fiseg = regcache_raw_get_unsigned_by_name (regcache, "fiseg");
+ fp->foseg = regcache_raw_get_unsigned_by_name (regcache, "foseg");
+}
+
+void
+i387_cache_to_xsave (struct regcache *regcache, void *buf)
+{
+ struct i387_xsave *fp = (struct i387_xsave *) buf;
+ int i;
+ unsigned long val, val2;
+ unsigned long long xstate_bv = 0;
+ unsigned long long clear_bv = 0;
+ char raw[64];
+ char *p;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
+
+ /* The supported bits in `xstat_bv' are 8 bytes. Clear part in
+ vector registers if its bit in xstat_bv is zero. */
+ clear_bv = (~fp->xstate_bv) & x86_xcr0;
+
+ /* Clear part in x87 and vector registers if its bit in xstat_bv is
+ zero. */
+ if (clear_bv)
+ {
+ if ((clear_bv & X86_XSTATE_X87))
+ {
+ for (i = 0; i < 8; i++)
+ memset (((char *) &fp->st_space[0]) + i * 16, 0, 10);
+
+ fp->fioff = 0;
+ fp->fooff = 0;
+ fp->fctrl = I387_FCTRL_INIT_VAL;
+ fp->fstat = 0;
+ fp->ftag = 0;
+ fp->fiseg = 0;
+ fp->foseg = 0;
+ fp->fop = 0;
+ }
+
+ if ((clear_bv & X86_XSTATE_SSE))
+ for (i = 0; i < num_xmm_registers; i++)
+ memset (((char *) &fp->xmm_space[0]) + i * 16, 0, 16);
+
+ if ((clear_bv & X86_XSTATE_AVX))
+ for (i = 0; i < num_xmm_registers; i++)
+ memset (((char *) &fp->ymmh_space[0]) + i * 16, 0, 16);
+
+ if ((clear_bv & X86_XSTATE_SSE) && (clear_bv & X86_XSTATE_AVX))
+ memset (((char *) &fp->mxcsr), 0, 4);
+
+ if ((clear_bv & X86_XSTATE_BNDREGS))
+ for (i = 0; i < num_mpx_bnd_registers; i++)
+ memset (((char *) &fp->mpx_bnd_space[0]) + i * 16, 0, 16);
+
+ if ((clear_bv & X86_XSTATE_BNDCFG))
+ for (i = 0; i < num_mpx_cfg_registers; i++)
+ memset (((char *) &fp->mpx_cfg_space[0]) + i * 8, 0, 8);
+
+ if ((clear_bv & X86_XSTATE_K))
+ for (i = 0; i < num_avx512_k_registers; i++)
+ memset (((char *) &fp->k_space[0]) + i * 8, 0, 8);
+
+ if ((clear_bv & X86_XSTATE_ZMM_H))
+ for (i = 0; i < num_avx512_zmmh_low_registers; i++)
+ memset (((char *) &fp->zmmh_low_space[0]) + i * 32, 0, 32);
+
+ if ((clear_bv & X86_XSTATE_ZMM))
+ {
+ for (i = 0; i < num_avx512_zmmh_high_registers; i++)
+ memset (((char *) &fp->zmmh_low_space[0]) + 32 + i * 64, 0, 32);
+ for (i = 0; i < num_avx512_xmm_registers; i++)
+ memset (((char *) &fp->zmmh_high_space[0]) + i * 64, 0, 16);
+ for (i = 0; i < num_avx512_ymmh_registers; i++)
+ memset (((char *) &fp->zmmh_high_space[0]) + 16 + i * 64, 0, 16);
+ }
+
+ if ((clear_bv & X86_XSTATE_PKRU))
+ for (i = 0; i < num_pkeys_registers; i++)
+ memset (((char *) &fp->pkru_space[0]) + i * 4, 0, 4);
+ }
+
+ /* Check if any x87 registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_X87))
+ {
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+
+ for (i = 0; i < 8; i++)
+ {
+ collect_register (regcache, i + st0_regnum, raw);
+ p = ((char *) &fp->st_space[0]) + i * 16;
+ if (memcmp (raw, p, 10))
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (p, raw, 10);
+ }
+ }
+ }
+
+ /* Check if any SSE registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_SSE))
+ {
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
+
+ for (i = 0; i < num_xmm_registers; i++)
+ {
+ collect_register (regcache, i + xmm0_regnum, raw);
+ p = ((char *) &fp->xmm_space[0]) + i * 16;
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (p, raw, 16);
+ }
+ }
+ }
+
+ /* Check if any AVX registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_AVX))
+ {
+ int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
+
+ for (i = 0; i < num_xmm_registers; i++)
+ {
+ collect_register (regcache, i + ymm0h_regnum, raw);
+ p = ((char *) &fp->ymmh_space[0]) + i * 16;
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= X86_XSTATE_AVX;
+ memcpy (p, raw, 16);
+ }
+ }
+ }
+
+ /* Check if any bound register has changed. */
+ if ((x86_xcr0 & X86_XSTATE_BNDREGS))
+ {
+ int bnd0r_regnum = find_regno (regcache->tdesc, "bnd0raw");
+
+ for (i = 0; i < num_mpx_bnd_registers; i++)
+ {
+ collect_register (regcache, i + bnd0r_regnum, raw);
+ p = ((char *) &fp->mpx_bnd_space[0]) + i * 16;
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= X86_XSTATE_BNDREGS;
+ memcpy (p, raw, 16);
+ }
+ }
+ }
+
+ /* Check if any status register has changed. */
+ if ((x86_xcr0 & X86_XSTATE_BNDCFG))
+ {
+ int bndcfg_regnum = find_regno (regcache->tdesc, "bndcfgu");
+
+ for (i = 0; i < num_mpx_cfg_registers; i++)
+ {
+ collect_register (regcache, i + bndcfg_regnum, raw);
+ p = ((char *) &fp->mpx_cfg_space[0]) + i * 8;
+ if (memcmp (raw, p, 8))
+ {
+ xstate_bv |= X86_XSTATE_BNDCFG;
+ memcpy (p, raw, 8);
+ }
+ }
+ }
+
+ /* Check if any K registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_K))
+ {
+ int k0_regnum = find_regno (regcache->tdesc, "k0");
+
+ for (i = 0; i < num_avx512_k_registers; i++)
+ {
+ collect_register (regcache, i + k0_regnum, raw);
+ p = ((char *) &fp->k_space[0]) + i * 8;
+ if (memcmp (raw, p, 8) != 0)
+ {
+ xstate_bv |= X86_XSTATE_K;
+ memcpy (p, raw, 8);
+ }
+ }
+ }
+
+ /* Check if any of ZMM0H-ZMM15H registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_ZMM_H))
+ {
+ int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
+
+ for (i = 0; i < num_avx512_zmmh_low_registers; i++)
+ {
+ collect_register (regcache, i + zmm0h_regnum, raw);
+ p = ((char *) &fp->zmmh_low_space[0]) + i * 32;
+ if (memcmp (raw, p, 32) != 0)
+ {
+ xstate_bv |= X86_XSTATE_ZMM_H;
+ memcpy (p, raw, 32);
+ }
+ }
+ }
+
+ /* Check if any of ZMM16H-ZMM31H registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_ZMM))
+ {
+ int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
+
+ for (i = 0; i < num_avx512_zmmh_high_registers; i++)
+ {
+ collect_register (regcache, i + zmm16h_regnum, raw);
+ p = ((char *) &fp->zmmh_high_space[0]) + 32 + i * 64;
+ if (memcmp (raw, p, 32) != 0)
+ {
+ xstate_bv |= X86_XSTATE_ZMM;
+ memcpy (p, raw, 32);
+ }
+ }
+ }
+
+ /* Check if any XMM_AVX512 registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_ZMM))
+ {
+ int xmm_avx512_regnum = find_regno (regcache->tdesc, "xmm16");
+
+ for (i = 0; i < num_avx512_xmm_registers; i++)
+ {
+ collect_register (regcache, i + xmm_avx512_regnum, raw);
+ p = ((char *) &fp->zmmh_high_space[0]) + i * 64;
+ if (memcmp (raw, p, 16) != 0)
+ {
+ xstate_bv |= X86_XSTATE_ZMM;
+ memcpy (p, raw, 16);
+ }
+ }
+ }
+
+ /* Check if any YMMH_AVX512 registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_ZMM))
+ {
+ int ymmh_avx512_regnum = find_regno (regcache->tdesc, "ymm16h");
+
+ for (i = 0; i < num_avx512_ymmh_registers; i++)
+ {
+ collect_register (regcache, i + ymmh_avx512_regnum, raw);
+ p = ((char *) &fp->zmmh_high_space[0]) + 16 + i * 64;
+ if (memcmp (raw, p, 16) != 0)
+ {
+ xstate_bv |= X86_XSTATE_ZMM;
+ memcpy (p, raw, 16);
+ }
+ }
+ }
+
+ /* Check if any PKEYS registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_PKRU))
+ {
+ int pkru_regnum = find_regno (regcache->tdesc, "pkru");
+
+ for (i = 0; i < num_pkeys_registers; i++)
+ {
+ collect_register (regcache, i + pkru_regnum, raw);
+ p = ((char *) &fp->pkru_space[0]) + i * 4;
+ if (memcmp (raw, p, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_PKRU;
+ memcpy (p, raw, 4);
+ }
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_SSE) || (x86_xcr0 & X86_XSTATE_AVX))
+ {
+ collect_register_by_name (regcache, "mxcsr", raw);
+ if (memcmp (raw, &fp->mxcsr, 4) != 0)
+ {
+ if (((fp->xstate_bv | xstate_bv)
+ & (X86_XSTATE_SSE | X86_XSTATE_AVX)) == 0)
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (&fp->mxcsr, raw, 4);
+ }
+ }
+
+ if (x86_xcr0 & X86_XSTATE_X87)
+ {
+ collect_register_by_name (regcache, "fioff", raw);
+ if (memcmp (raw, &fp->fioff, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (&fp->fioff, raw, 4);
+ }
+
+ collect_register_by_name (regcache, "fooff", raw);
+ if (memcmp (raw, &fp->fooff, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (&fp->fooff, raw, 4);
+ }
+
+ /* This one's 11 bits... */
+ val2 = regcache_raw_get_unsigned_by_name (regcache, "fop");
+ val2 = (val2 & 0x7FF) | (fp->fop & 0xF800);
+ if (fp->fop != val2)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fop = val2;
+ }
+
+ /* Some registers are 16-bit. */
+ val = regcache_raw_get_unsigned_by_name (regcache, "fctrl");
+ if (fp->fctrl != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fctrl = val;
+ }
+
+ val = regcache_raw_get_unsigned_by_name (regcache, "fstat");
+ if (fp->fstat != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fstat = val;
+ }
+
+ /* Convert to the simplifed tag form stored in fxsave data. */
+ val = regcache_raw_get_unsigned_by_name (regcache, "ftag");
+ val2 = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag = (val >> (i * 2)) & 3;
+
+ if (tag != 3)
+ val2 |= (1 << i);
+ }
+ if (fp->ftag != val2)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->ftag = val2;
+ }
+
+ val = regcache_raw_get_unsigned_by_name (regcache, "fiseg");
+ if (fp->fiseg != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fiseg = val;
+ }
+
+ val = regcache_raw_get_unsigned_by_name (regcache, "foseg");
+ if (fp->foseg != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->foseg = val;
+ }
+ }
+
+ /* Update the corresponding bits in xstate_bv if any SSE/AVX
+ registers are changed. */
+ fp->xstate_bv |= xstate_bv;
+}
+
+static int
+i387_ftag (struct i387_fxsave *fp, int regno)
+{
+ unsigned char *raw = &fp->st_space[regno * 16];
+ unsigned int exponent;
+ unsigned long fraction[2];
+ int integer;
+
+ integer = raw[7] & 0x80;
+ exponent = (((raw[9] & 0x7f) << 8) | raw[8]);
+ fraction[0] = ((raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]);
+ fraction[1] = (((raw[7] & 0x7f) << 24) | (raw[6] << 16)
+ | (raw[5] << 8) | raw[4]);
+
+ if (exponent == 0x7fff)
+ {
+ /* Special. */
+ return (2);
+ }
+ else if (exponent == 0x0000)
+ {
+ if (fraction[0] == 0x0000 && fraction[1] == 0x0000 && !integer)
+ {
+ /* Zero. */
+ return (1);
+ }
+ else
+ {
+ /* Special. */
+ return (2);
+ }
+ }
+ else
+ {
+ if (integer)
+ {
+ /* Valid. */
+ return (0);
+ }
+ else
+ {
+ /* Special. */
+ return (2);
+ }
+ }
+}
+
+void
+i387_fxsave_to_cache (struct regcache *regcache, const void *buf)
+{
+ struct i387_fxsave *fp = (struct i387_fxsave *) buf;
+ int i, top;
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
+ unsigned long val;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
+
+ for (i = 0; i < 8; i++)
+ supply_register (regcache, i + st0_regnum,
+ ((char *) &fp->st_space[0]) + i * 16);
+ for (i = 0; i < num_xmm_registers; i++)
+ supply_register (regcache, i + xmm0_regnum,
+ ((char *) &fp->xmm_space[0]) + i * 16);
+
+ supply_register_by_name (regcache, "fioff", &fp->fioff);
+ supply_register_by_name (regcache, "fooff", &fp->fooff);
+ supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+
+ /* Some registers are 16-bit. */
+ val = fp->fctrl & 0xFFFF;
+ supply_register_by_name (regcache, "fctrl", &val);
+
+ val = fp->fstat & 0xFFFF;
+ supply_register_by_name (regcache, "fstat", &val);
+
+ /* Generate the form of ftag data that GDB expects. */
+ top = (fp->fstat >> 11) & 0x7;
+ val = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag;
+ if (fp->ftag & (1 << i))
+ tag = i387_ftag (fp, (i + 8 - top) % 8);
+ else
+ tag = 3;
+ val |= tag << (2 * i);
+ }
+ supply_register_by_name (regcache, "ftag", &val);
+
+ val = fp->fiseg & 0xFFFF;
+ supply_register_by_name (regcache, "fiseg", &val);
+
+ val = fp->foseg & 0xFFFF;
+ supply_register_by_name (regcache, "foseg", &val);
+
+ val = (fp->fop) & 0x7FF;
+ supply_register_by_name (regcache, "fop", &val);
+}
+
+void
+i387_xsave_to_cache (struct regcache *regcache, const void *buf)
+{
+ struct i387_xsave *fp = (struct i387_xsave *) buf;
+ struct i387_fxsave *fxp = (struct i387_fxsave *) buf;
+ int i, top;
+ unsigned long val;
+ unsigned long long clear_bv;
+ gdb_byte *p;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;
+
+ /* The supported bits in `xstat_bv' are 8 bytes. Clear part in
+ vector registers if its bit in xstat_bv is zero. */
+ clear_bv = (~fp->xstate_bv) & x86_xcr0;
+
+ /* Check if any x87 registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_X87) != 0)
+ {
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+
+ if ((clear_bv & X86_XSTATE_X87) != 0)
+ {
+ for (i = 0; i < 8; i++)
+ supply_register_zeroed (regcache, i + st0_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->st_space[0];
+ for (i = 0; i < 8; i++)
+ supply_register (regcache, i + st0_regnum, p + i * 16);
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_SSE) != 0)
+ {
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
+
+ if ((clear_bv & X86_XSTATE_SSE))
+ {
+ for (i = 0; i < num_xmm_registers; i++)
+ supply_register_zeroed (regcache, i + xmm0_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->xmm_space[0];
+ for (i = 0; i < num_xmm_registers; i++)
+ supply_register (regcache, i + xmm0_regnum, p + i * 16);
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_AVX) != 0)
+ {
+ int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
+
+ if ((clear_bv & X86_XSTATE_AVX) != 0)
+ {
+ for (i = 0; i < num_xmm_registers; i++)
+ supply_register_zeroed (regcache, i + ymm0h_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->ymmh_space[0];
+ for (i = 0; i < num_xmm_registers; i++)
+ supply_register (regcache, i + ymm0h_regnum, p + i * 16);
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_BNDREGS))
+ {
+ int bnd0r_regnum = find_regno (regcache->tdesc, "bnd0raw");
+
+
+ if ((clear_bv & X86_XSTATE_BNDREGS) != 0)
+ {
+ for (i = 0; i < num_mpx_bnd_registers; i++)
+ supply_register_zeroed (regcache, i + bnd0r_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->mpx_bnd_space[0];
+ for (i = 0; i < num_mpx_bnd_registers; i++)
+ supply_register (regcache, i + bnd0r_regnum, p + i * 16);
+ }
+
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_BNDCFG))
+ {
+ int bndcfg_regnum = find_regno (regcache->tdesc, "bndcfgu");
+
+ if ((clear_bv & X86_XSTATE_BNDCFG) != 0)
+ {
+ for (i = 0; i < num_mpx_cfg_registers; i++)
+ supply_register_zeroed (regcache, i + bndcfg_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->mpx_cfg_space[0];
+ for (i = 0; i < num_mpx_cfg_registers; i++)
+ supply_register (regcache, i + bndcfg_regnum, p + i * 8);
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_K) != 0)
+ {
+ int k0_regnum = find_regno (regcache->tdesc, "k0");
+
+ if ((clear_bv & X86_XSTATE_K) != 0)
+ {
+ for (i = 0; i < num_avx512_k_registers; i++)
+ supply_register_zeroed (regcache, i + k0_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->k_space[0];
+ for (i = 0; i < num_avx512_k_registers; i++)
+ supply_register (regcache, i + k0_regnum, p + i * 8);
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_ZMM_H) != 0)
+ {
+ int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
+
+ if ((clear_bv & X86_XSTATE_ZMM_H) != 0)
+ {
+ for (i = 0; i < num_avx512_zmmh_low_registers; i++)
+ supply_register_zeroed (regcache, i + zmm0h_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->zmmh_low_space[0];
+ for (i = 0; i < num_avx512_zmmh_low_registers; i++)
+ supply_register (regcache, i + zmm0h_regnum, p + i * 32);
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_ZMM) != 0)
+ {
+ int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
+ int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
+ int xmm16_regnum = find_regno (regcache->tdesc, "xmm16");
+
+ if ((clear_bv & X86_XSTATE_ZMM) != 0)
+ {
+ for (i = 0; i < num_avx512_zmmh_high_registers; i++)
+ supply_register_zeroed (regcache, i + zmm16h_regnum);
+ for (i = 0; i < num_avx512_ymmh_registers; i++)
+ supply_register_zeroed (regcache, i + ymm16h_regnum);
+ for (i = 0; i < num_avx512_xmm_registers; i++)
+ supply_register_zeroed (regcache, i + xmm16_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->zmmh_high_space[0];
+ for (i = 0; i < num_avx512_zmmh_high_registers; i++)
+ supply_register (regcache, i + zmm16h_regnum, p + 32 + i * 64);
+ for (i = 0; i < num_avx512_ymmh_registers; i++)
+ supply_register (regcache, i + ymm16h_regnum, p + 16 + i * 64);
+ for (i = 0; i < num_avx512_xmm_registers; i++)
+ supply_register (regcache, i + xmm16_regnum, p + i * 64);
+ }
+ }
+
+ if ((x86_xcr0 & X86_XSTATE_PKRU) != 0)
+ {
+ int pkru_regnum = find_regno (regcache->tdesc, "pkru");
+
+ if ((clear_bv & X86_XSTATE_PKRU) != 0)
+ {
+ for (i = 0; i < num_pkeys_registers; i++)
+ supply_register_zeroed (regcache, i + pkru_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->pkru_space[0];
+ for (i = 0; i < num_pkeys_registers; i++)
+ supply_register (regcache, i + pkru_regnum, p + i * 4);
+ }
+ }
+
+ if ((clear_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX))
+ == (X86_XSTATE_SSE | X86_XSTATE_AVX))
+ {
+ unsigned int default_mxcsr = I387_MXCSR_INIT_VAL;
+ supply_register_by_name (regcache, "mxcsr", &default_mxcsr);
+ }
+ else
+ supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+
+ if ((clear_bv & X86_XSTATE_X87) != 0)
+ {
+ supply_register_by_name_zeroed (regcache, "fioff");
+ supply_register_by_name_zeroed (regcache, "fooff");
+
+ val = I387_FCTRL_INIT_VAL;
+ supply_register_by_name (regcache, "fctrl", &val);
+
+ supply_register_by_name_zeroed (regcache, "fstat");
+
+ val = 0xFFFF;
+ supply_register_by_name (regcache, "ftag", &val);
+
+ supply_register_by_name_zeroed (regcache, "fiseg");
+ supply_register_by_name_zeroed (regcache, "foseg");
+ supply_register_by_name_zeroed (regcache, "fop");
+ }
+ else
+ {
+ supply_register_by_name (regcache, "fioff", &fp->fioff);
+ supply_register_by_name (regcache, "fooff", &fp->fooff);
+
+ /* Some registers are 16-bit. */
+ val = fp->fctrl & 0xFFFF;
+ supply_register_by_name (regcache, "fctrl", &val);
+
+ val = fp->fstat & 0xFFFF;
+ supply_register_by_name (regcache, "fstat", &val);
+
+ /* Generate the form of ftag data that GDB expects. */
+ top = (fp->fstat >> 11) & 0x7;
+ val = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag;
+ if (fp->ftag & (1 << i))
+ tag = i387_ftag (fxp, (i + 8 - top) % 8);
+ else
+ tag = 3;
+ val |= tag << (2 * i);
+ }
+ supply_register_by_name (regcache, "ftag", &val);
+
+ val = fp->fiseg & 0xFFFF;
+ supply_register_by_name (regcache, "fiseg", &val);
+
+ val = fp->foseg & 0xFFFF;
+ supply_register_by_name (regcache, "foseg", &val);
+
+ val = (fp->fop) & 0x7FF;
+ supply_register_by_name (regcache, "fop", &val);
+ }
+}
+
+/* Default to SSE. */
+unsigned long long x86_xcr0 = X86_XSTATE_SSE_MASK;
+++ /dev/null
-/* Inferior process information for the remote server for GDB.
- Copyright (C) 2002-2020 Free Software Foundation, Inc.
-
- Contributed by MontaVista Software.
-
- 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 "gdbsupport/common-inferior.h"
-#include "gdbthread.h"
-#include "dll.h"
-
-std::list<process_info *> all_processes;
-std::list<thread_info *> all_threads;
-
-struct thread_info *current_thread;
-
-/* The current working directory used to start the inferior. */
-static const char *current_inferior_cwd = NULL;
-
-struct thread_info *
-add_thread (ptid_t thread_id, void *target_data)
-{
- struct thread_info *new_thread = XCNEW (struct thread_info);
-
- new_thread->id = thread_id;
- new_thread->last_resume_kind = resume_continue;
- new_thread->last_status.kind = TARGET_WAITKIND_IGNORE;
-
- all_threads.push_back (new_thread);
-
- if (current_thread == NULL)
- current_thread = new_thread;
-
- new_thread->target_data = target_data;
-
- return new_thread;
-}
-
-/* See gdbthread.h. */
-
-struct thread_info *
-get_first_thread (void)
-{
- if (!all_threads.empty ())
- return all_threads.front ();
- else
- return NULL;
-}
-
-struct thread_info *
-find_thread_ptid (ptid_t ptid)
-{
- return find_thread ([&] (thread_info *thread) {
- return thread->id == ptid;
- });
-}
-
-/* Find a thread associated with the given PROCESS, or NULL if no
- such thread exists. */
-
-static struct thread_info *
-find_thread_process (const struct process_info *const process)
-{
- return find_any_thread_of_pid (process->pid);
-}
-
-/* See gdbthread.h. */
-
-struct thread_info *
-find_any_thread_of_pid (int pid)
-{
- return find_thread (pid, [] (thread_info *thread) {
- return true;
- });
-}
-
-static void
-free_one_thread (thread_info *thread)
-{
- free_register_cache (thread_regcache_data (thread));
- free (thread);
-}
-
-void
-remove_thread (struct thread_info *thread)
-{
- if (thread->btrace != NULL)
- target_disable_btrace (thread->btrace);
-
- discard_queued_stop_replies (ptid_of (thread));
- all_threads.remove (thread);
- free_one_thread (thread);
- if (current_thread == thread)
- current_thread = NULL;
-}
-
-void *
-thread_target_data (struct thread_info *thread)
-{
- return thread->target_data;
-}
-
-struct regcache *
-thread_regcache_data (struct thread_info *thread)
-{
- return thread->regcache_data;
-}
-
-void
-set_thread_regcache_data (struct thread_info *thread, struct regcache *data)
-{
- thread->regcache_data = data;
-}
-
-void
-clear_inferiors (void)
-{
- for_each_thread (free_one_thread);
- all_threads.clear ();
-
- clear_dlls ();
-
- current_thread = NULL;
-}
-
-struct process_info *
-add_process (int pid, int attached)
-{
- process_info *process = new process_info (pid, attached);
-
- all_processes.push_back (process);
-
- return process;
-}
-
-/* Remove a process from the common process list and free the memory
- allocated for it.
- The caller is responsible for freeing private data first. */
-
-void
-remove_process (struct process_info *process)
-{
- clear_symbol_cache (&process->symbol_cache);
- free_all_breakpoints (process);
- gdb_assert (find_thread_process (process) == NULL);
- all_processes.remove (process);
- delete process;
-}
-
-process_info *
-find_process_pid (int pid)
-{
- return find_process ([&] (process_info *process) {
- return process->pid == pid;
- });
-}
-
-/* Get the first process in the process list, or NULL if the list is empty. */
-
-process_info *
-get_first_process (void)
-{
- if (!all_processes.empty ())
- return all_processes.front ();
- else
- return NULL;
-}
-
-/* Return non-zero if there are any inferiors that we have created
- (as opposed to attached-to). */
-
-int
-have_started_inferiors_p (void)
-{
- return find_process ([] (process_info *process) {
- return !process->attached;
- }) != NULL;
-}
-
-/* Return non-zero if there are any inferiors that we have attached to. */
-
-int
-have_attached_inferiors_p (void)
-{
- return find_process ([] (process_info *process) {
- return process->attached;
- }) != NULL;
-}
-
-struct process_info *
-get_thread_process (const struct thread_info *thread)
-{
- return find_process_pid (thread->id.pid ());
-}
-
-struct process_info *
-current_process (void)
-{
- gdb_assert (current_thread != NULL);
- return get_thread_process (current_thread);
-}
-
-/* See gdbsupport/common-gdbthread.h. */
-
-void
-switch_to_thread (process_stratum_target *ops, ptid_t ptid)
-{
- gdb_assert (ptid != minus_one_ptid);
- current_thread = find_thread_ptid (ptid);
-}
-
-/* See gdbsupport/common-inferior.h. */
-
-const char *
-get_inferior_cwd ()
-{
- return current_inferior_cwd;
-}
-
-/* See gdbsupport/common-inferior.h. */
-
-void
-set_inferior_cwd (const char *cwd)
-{
- xfree ((void *) current_inferior_cwd);
- if (cwd != NULL)
- current_inferior_cwd = xstrdup (cwd);
- else
- current_inferior_cwd = NULL;
-}
--- /dev/null
+/* Inferior process information for the remote server for GDB.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ 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 "gdbsupport/common-inferior.h"
+#include "gdbthread.h"
+#include "dll.h"
+
+std::list<process_info *> all_processes;
+std::list<thread_info *> all_threads;
+
+struct thread_info *current_thread;
+
+/* The current working directory used to start the inferior. */
+static const char *current_inferior_cwd = NULL;
+
+struct thread_info *
+add_thread (ptid_t thread_id, void *target_data)
+{
+ struct thread_info *new_thread = XCNEW (struct thread_info);
+
+ new_thread->id = thread_id;
+ new_thread->last_resume_kind = resume_continue;
+ new_thread->last_status.kind = TARGET_WAITKIND_IGNORE;
+
+ all_threads.push_back (new_thread);
+
+ if (current_thread == NULL)
+ current_thread = new_thread;
+
+ new_thread->target_data = target_data;
+
+ return new_thread;
+}
+
+/* See gdbthread.h. */
+
+struct thread_info *
+get_first_thread (void)
+{
+ if (!all_threads.empty ())
+ return all_threads.front ();
+ else
+ return NULL;
+}
+
+struct thread_info *
+find_thread_ptid (ptid_t ptid)
+{
+ return find_thread ([&] (thread_info *thread) {
+ return thread->id == ptid;
+ });
+}
+
+/* Find a thread associated with the given PROCESS, or NULL if no
+ such thread exists. */
+
+static struct thread_info *
+find_thread_process (const struct process_info *const process)
+{
+ return find_any_thread_of_pid (process->pid);
+}
+
+/* See gdbthread.h. */
+
+struct thread_info *
+find_any_thread_of_pid (int pid)
+{
+ return find_thread (pid, [] (thread_info *thread) {
+ return true;
+ });
+}
+
+static void
+free_one_thread (thread_info *thread)
+{
+ free_register_cache (thread_regcache_data (thread));
+ free (thread);
+}
+
+void
+remove_thread (struct thread_info *thread)
+{
+ if (thread->btrace != NULL)
+ target_disable_btrace (thread->btrace);
+
+ discard_queued_stop_replies (ptid_of (thread));
+ all_threads.remove (thread);
+ free_one_thread (thread);
+ if (current_thread == thread)
+ current_thread = NULL;
+}
+
+void *
+thread_target_data (struct thread_info *thread)
+{
+ return thread->target_data;
+}
+
+struct regcache *
+thread_regcache_data (struct thread_info *thread)
+{
+ return thread->regcache_data;
+}
+
+void
+set_thread_regcache_data (struct thread_info *thread, struct regcache *data)
+{
+ thread->regcache_data = data;
+}
+
+void
+clear_inferiors (void)
+{
+ for_each_thread (free_one_thread);
+ all_threads.clear ();
+
+ clear_dlls ();
+
+ current_thread = NULL;
+}
+
+struct process_info *
+add_process (int pid, int attached)
+{
+ process_info *process = new process_info (pid, attached);
+
+ all_processes.push_back (process);
+
+ return process;
+}
+
+/* Remove a process from the common process list and free the memory
+ allocated for it.
+ The caller is responsible for freeing private data first. */
+
+void
+remove_process (struct process_info *process)
+{
+ clear_symbol_cache (&process->symbol_cache);
+ free_all_breakpoints (process);
+ gdb_assert (find_thread_process (process) == NULL);
+ all_processes.remove (process);
+ delete process;
+}
+
+process_info *
+find_process_pid (int pid)
+{
+ return find_process ([&] (process_info *process) {
+ return process->pid == pid;
+ });
+}
+
+/* Get the first process in the process list, or NULL if the list is empty. */
+
+process_info *
+get_first_process (void)
+{
+ if (!all_processes.empty ())
+ return all_processes.front ();
+ else
+ return NULL;
+}
+
+/* Return non-zero if there are any inferiors that we have created
+ (as opposed to attached-to). */
+
+int
+have_started_inferiors_p (void)
+{
+ return find_process ([] (process_info *process) {
+ return !process->attached;
+ }) != NULL;
+}
+
+/* Return non-zero if there are any inferiors that we have attached to. */
+
+int
+have_attached_inferiors_p (void)
+{
+ return find_process ([] (process_info *process) {
+ return process->attached;
+ }) != NULL;
+}
+
+struct process_info *
+get_thread_process (const struct thread_info *thread)
+{
+ return find_process_pid (thread->id.pid ());
+}
+
+struct process_info *
+current_process (void)
+{
+ gdb_assert (current_thread != NULL);
+ return get_thread_process (current_thread);
+}
+
+/* See gdbsupport/common-gdbthread.h. */
+
+void
+switch_to_thread (process_stratum_target *ops, ptid_t ptid)
+{
+ gdb_assert (ptid != minus_one_ptid);
+ current_thread = find_thread_ptid (ptid);
+}
+
+/* See gdbsupport/common-inferior.h. */
+
+const char *
+get_inferior_cwd ()
+{
+ return current_inferior_cwd;
+}
+
+/* See gdbsupport/common-inferior.h. */
+
+void
+set_inferior_cwd (const char *cwd)
+{
+ xfree ((void *) current_inferior_cwd);
+ if (cwd != NULL)
+ current_inferior_cwd = xstrdup (cwd);
+ else
+ current_inferior_cwd = NULL;
+}
+++ /dev/null
-/* Copyright (C) 1995-2020 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 "arch/arm.h"
-#include "arch/arm-linux.h"
-#include "linux-low.h"
-#include "linux-aarch32-low.h"
-
-#include <sys/ptrace.h>
-/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
- On Bionic elf.h and linux/elf.h have conflicting definitions. */
-#ifndef ELFMAG0
-#include <elf.h>
-#endif
-
-/* Correct in either endianness. */
-#define arm_abi_breakpoint 0xef9f0001UL
-
-/* For new EABI binaries. We recognize it regardless of which ABI
- is used for gdbserver, so single threaded debugging should work
- OK, but for multi-threaded debugging we only insert the current
- ABI's breakpoint instruction. For now at least. */
-#define arm_eabi_breakpoint 0xe7f001f0UL
-
-#if (defined __ARM_EABI__ || defined __aarch64__)
-static const unsigned long arm_breakpoint = arm_eabi_breakpoint;
-#else
-static const unsigned long arm_breakpoint = arm_abi_breakpoint;
-#endif
-
-#define arm_breakpoint_len 4
-static const unsigned short thumb_breakpoint = 0xde01;
-#define thumb_breakpoint_len 2
-static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
-#define thumb2_breakpoint_len 4
-
-/* Some older versions of GNU/Linux and Android do not define
- the following macros. */
-#ifndef NT_ARM_VFP
-#define NT_ARM_VFP 0x400
-#endif
-
-/* Collect GP registers from REGCACHE to buffer BUF. */
-
-void
-arm_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
- uint32_t *regs = (uint32_t *) buf;
- uint32_t cpsr = regs[ARM_CPSR_GREGNUM];
-
- for (i = ARM_A1_REGNUM; i <= ARM_PC_REGNUM; i++)
- collect_register (regcache, i, ®s[i]);
-
- collect_register (regcache, ARM_PS_REGNUM, ®s[ARM_CPSR_GREGNUM]);
- /* Keep reserved bits bit 20 to bit 23. */
- regs[ARM_CPSR_GREGNUM] = ((regs[ARM_CPSR_GREGNUM] & 0xff0fffff)
- | (cpsr & 0x00f00000));
-}
-
-/* Supply GP registers contents, stored in BUF, to REGCACHE. */
-
-void
-arm_store_gregset (struct regcache *regcache, const void *buf)
-{
- int i;
- char zerobuf[8];
- const uint32_t *regs = (const uint32_t *) buf;
- uint32_t cpsr = regs[ARM_CPSR_GREGNUM];
-
- memset (zerobuf, 0, 8);
- for (i = ARM_A1_REGNUM; i <= ARM_PC_REGNUM; i++)
- supply_register (regcache, i, ®s[i]);
-
- for (; i < ARM_PS_REGNUM; i++)
- supply_register (regcache, i, zerobuf);
-
- /* Clear reserved bits bit 20 to bit 23. */
- cpsr &= 0xff0fffff;
- supply_register (regcache, ARM_PS_REGNUM, &cpsr);
-}
-
-/* Collect NUM number of VFP registers from REGCACHE to buffer BUF. */
-
-void
-arm_fill_vfpregset_num (struct regcache *regcache, void *buf, int num)
-{
- int i, base;
-
- gdb_assert (num == 16 || num == 32);
-
- base = find_regno (regcache->tdesc, "d0");
- for (i = 0; i < num; i++)
- collect_register (regcache, base + i, (char *) buf + i * 8);
-
- collect_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
-}
-
-/* Supply NUM number of VFP registers contents, stored in BUF, to
- REGCACHE. */
-
-void
-arm_store_vfpregset_num (struct regcache *regcache, const void *buf, int num)
-{
- int i, base;
-
- gdb_assert (num == 16 || num == 32);
-
- base = find_regno (regcache->tdesc, "d0");
- for (i = 0; i < num; i++)
- supply_register (regcache, base + i, (char *) buf + i * 8);
-
- supply_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
-}
-
-static void
-arm_fill_vfpregset (struct regcache *regcache, void *buf)
-{
- arm_fill_vfpregset_num (regcache, buf, 32);
-}
-
-static void
-arm_store_vfpregset (struct regcache *regcache, const void *buf)
-{
- arm_store_vfpregset_num (regcache, buf, 32);
-}
-
-/* Register sets with using PTRACE_GETREGSET. */
-
-static struct regset_info aarch32_regsets[] = {
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
- ARM_CORE_REGS_SIZE + ARM_INT_REGISTER_SIZE, GENERAL_REGS,
- arm_fill_gregset, arm_store_gregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_VFP, ARM_VFP3_REGS_SIZE,
- EXTENDED_REGS,
- arm_fill_vfpregset, arm_store_vfpregset },
- NULL_REGSET
-};
-
-static struct regsets_info aarch32_regsets_info =
- {
- aarch32_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-struct regs_info regs_info_aarch32 =
- {
- NULL, /* regset_bitmap */
- NULL, /* usrregs */
- &aarch32_regsets_info
- };
-
-/* Returns 1 if the current instruction set is thumb, 0 otherwise. */
-
-int
-arm_is_thumb_mode (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long cpsr;
-
- collect_register_by_name (regcache, "cpsr", &cpsr);
-
- if (cpsr & 0x20)
- return 1;
- else
- return 0;
-}
-
-/* Returns 1 if there is a software breakpoint at location. */
-
-int
-arm_breakpoint_at (CORE_ADDR where)
-{
- if (arm_is_thumb_mode ())
- {
- /* Thumb mode. */
- unsigned short insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
- if (insn == thumb_breakpoint)
- return 1;
-
- if (insn == thumb2_breakpoint[0])
- {
- (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
- if (insn == thumb2_breakpoint[1])
- return 1;
- }
- }
- else
- {
- /* ARM mode. */
- unsigned long insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == arm_abi_breakpoint)
- return 1;
-
- if (insn == arm_eabi_breakpoint)
- return 1;
- }
-
- return 0;
-}
-
-/* Implementation of linux_target_ops method "breakpoint_kind_from_pc".
-
- Determine the type and size of breakpoint to insert at PCPTR. Uses the
- program counter value to determine whether a 16-bit or 32-bit breakpoint
- should be used. It returns the breakpoint's kind, and adjusts the program
- counter (if necessary) to point to the actual memory location where the
- breakpoint should be inserted. */
-
-int
-arm_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
-{
- if (IS_THUMB_ADDR (*pcptr))
- {
- gdb_byte buf[2];
-
- *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
-
- /* Check whether we are replacing a thumb2 32-bit instruction. */
- if (target_read_memory (*pcptr, buf, 2) == 0)
- {
- unsigned short inst1 = 0;
-
- target_read_memory (*pcptr, (gdb_byte *) &inst1, 2);
- if (thumb_insn_size (inst1) == 4)
- return ARM_BP_KIND_THUMB2;
- }
- return ARM_BP_KIND_THUMB;
- }
- else
- return ARM_BP_KIND_ARM;
-}
-
-/* Implementation of the linux_target_ops method "sw_breakpoint_from_kind". */
-
-const gdb_byte *
-arm_sw_breakpoint_from_kind (int kind , int *size)
-{
- *size = arm_breakpoint_len;
- /* Define an ARM-mode breakpoint; we only set breakpoints in the C
- library, which is most likely to be ARM. If the kernel supports
- clone events, we will never insert a breakpoint, so even a Thumb
- C library will work; so will mixing EABI/non-EABI gdbserver and
- application. */
- switch (kind)
- {
- case ARM_BP_KIND_THUMB:
- *size = thumb_breakpoint_len;
- return (gdb_byte *) &thumb_breakpoint;
- case ARM_BP_KIND_THUMB2:
- *size = thumb2_breakpoint_len;
- return (gdb_byte *) &thumb2_breakpoint;
- case ARM_BP_KIND_ARM:
- *size = arm_breakpoint_len;
- return (const gdb_byte *) &arm_breakpoint;
- default:
- return NULL;
- }
- return NULL;
-}
-
-/* Implementation of the linux_target_ops method
- "breakpoint_kind_from_current_state". */
-
-int
-arm_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
-{
- if (arm_is_thumb_mode ())
- {
- *pcptr = MAKE_THUMB_ADDR (*pcptr);
- return arm_breakpoint_kind_from_pc (pcptr);
- }
- else
- {
- return arm_breakpoint_kind_from_pc (pcptr);
- }
-}
-
-void
-initialize_low_arch_aarch32 (void)
-{
- initialize_regsets_info (&aarch32_regsets_info);
-}
--- /dev/null
+/* Copyright (C) 1995-2020 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 "arch/arm.h"
+#include "arch/arm-linux.h"
+#include "linux-low.h"
+#include "linux-aarch32-low.h"
+
+#include <sys/ptrace.h>
+/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
+ On Bionic elf.h and linux/elf.h have conflicting definitions. */
+#ifndef ELFMAG0
+#include <elf.h>
+#endif
+
+/* Correct in either endianness. */
+#define arm_abi_breakpoint 0xef9f0001UL
+
+/* For new EABI binaries. We recognize it regardless of which ABI
+ is used for gdbserver, so single threaded debugging should work
+ OK, but for multi-threaded debugging we only insert the current
+ ABI's breakpoint instruction. For now at least. */
+#define arm_eabi_breakpoint 0xe7f001f0UL
+
+#if (defined __ARM_EABI__ || defined __aarch64__)
+static const unsigned long arm_breakpoint = arm_eabi_breakpoint;
+#else
+static const unsigned long arm_breakpoint = arm_abi_breakpoint;
+#endif
+
+#define arm_breakpoint_len 4
+static const unsigned short thumb_breakpoint = 0xde01;
+#define thumb_breakpoint_len 2
+static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
+#define thumb2_breakpoint_len 4
+
+/* Some older versions of GNU/Linux and Android do not define
+ the following macros. */
+#ifndef NT_ARM_VFP
+#define NT_ARM_VFP 0x400
+#endif
+
+/* Collect GP registers from REGCACHE to buffer BUF. */
+
+void
+arm_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+ uint32_t *regs = (uint32_t *) buf;
+ uint32_t cpsr = regs[ARM_CPSR_GREGNUM];
+
+ for (i = ARM_A1_REGNUM; i <= ARM_PC_REGNUM; i++)
+ collect_register (regcache, i, ®s[i]);
+
+ collect_register (regcache, ARM_PS_REGNUM, ®s[ARM_CPSR_GREGNUM]);
+ /* Keep reserved bits bit 20 to bit 23. */
+ regs[ARM_CPSR_GREGNUM] = ((regs[ARM_CPSR_GREGNUM] & 0xff0fffff)
+ | (cpsr & 0x00f00000));
+}
+
+/* Supply GP registers contents, stored in BUF, to REGCACHE. */
+
+void
+arm_store_gregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+ char zerobuf[8];
+ const uint32_t *regs = (const uint32_t *) buf;
+ uint32_t cpsr = regs[ARM_CPSR_GREGNUM];
+
+ memset (zerobuf, 0, 8);
+ for (i = ARM_A1_REGNUM; i <= ARM_PC_REGNUM; i++)
+ supply_register (regcache, i, ®s[i]);
+
+ for (; i < ARM_PS_REGNUM; i++)
+ supply_register (regcache, i, zerobuf);
+
+ /* Clear reserved bits bit 20 to bit 23. */
+ cpsr &= 0xff0fffff;
+ supply_register (regcache, ARM_PS_REGNUM, &cpsr);
+}
+
+/* Collect NUM number of VFP registers from REGCACHE to buffer BUF. */
+
+void
+arm_fill_vfpregset_num (struct regcache *regcache, void *buf, int num)
+{
+ int i, base;
+
+ gdb_assert (num == 16 || num == 32);
+
+ base = find_regno (regcache->tdesc, "d0");
+ for (i = 0; i < num; i++)
+ collect_register (regcache, base + i, (char *) buf + i * 8);
+
+ collect_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
+}
+
+/* Supply NUM number of VFP registers contents, stored in BUF, to
+ REGCACHE. */
+
+void
+arm_store_vfpregset_num (struct regcache *regcache, const void *buf, int num)
+{
+ int i, base;
+
+ gdb_assert (num == 16 || num == 32);
+
+ base = find_regno (regcache->tdesc, "d0");
+ for (i = 0; i < num; i++)
+ supply_register (regcache, base + i, (char *) buf + i * 8);
+
+ supply_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
+}
+
+static void
+arm_fill_vfpregset (struct regcache *regcache, void *buf)
+{
+ arm_fill_vfpregset_num (regcache, buf, 32);
+}
+
+static void
+arm_store_vfpregset (struct regcache *regcache, const void *buf)
+{
+ arm_store_vfpregset_num (regcache, buf, 32);
+}
+
+/* Register sets with using PTRACE_GETREGSET. */
+
+static struct regset_info aarch32_regsets[] = {
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
+ ARM_CORE_REGS_SIZE + ARM_INT_REGISTER_SIZE, GENERAL_REGS,
+ arm_fill_gregset, arm_store_gregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_VFP, ARM_VFP3_REGS_SIZE,
+ EXTENDED_REGS,
+ arm_fill_vfpregset, arm_store_vfpregset },
+ NULL_REGSET
+};
+
+static struct regsets_info aarch32_regsets_info =
+ {
+ aarch32_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+struct regs_info regs_info_aarch32 =
+ {
+ NULL, /* regset_bitmap */
+ NULL, /* usrregs */
+ &aarch32_regsets_info
+ };
+
+/* Returns 1 if the current instruction set is thumb, 0 otherwise. */
+
+int
+arm_is_thumb_mode (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+ unsigned long cpsr;
+
+ collect_register_by_name (regcache, "cpsr", &cpsr);
+
+ if (cpsr & 0x20)
+ return 1;
+ else
+ return 0;
+}
+
+/* Returns 1 if there is a software breakpoint at location. */
+
+int
+arm_breakpoint_at (CORE_ADDR where)
+{
+ if (arm_is_thumb_mode ())
+ {
+ /* Thumb mode. */
+ unsigned short insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
+ if (insn == thumb_breakpoint)
+ return 1;
+
+ if (insn == thumb2_breakpoint[0])
+ {
+ (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
+ if (insn == thumb2_breakpoint[1])
+ return 1;
+ }
+ }
+ else
+ {
+ /* ARM mode. */
+ unsigned long insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
+ if (insn == arm_abi_breakpoint)
+ return 1;
+
+ if (insn == arm_eabi_breakpoint)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Implementation of linux_target_ops method "breakpoint_kind_from_pc".
+
+ Determine the type and size of breakpoint to insert at PCPTR. Uses the
+ program counter value to determine whether a 16-bit or 32-bit breakpoint
+ should be used. It returns the breakpoint's kind, and adjusts the program
+ counter (if necessary) to point to the actual memory location where the
+ breakpoint should be inserted. */
+
+int
+arm_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+ if (IS_THUMB_ADDR (*pcptr))
+ {
+ gdb_byte buf[2];
+
+ *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+ /* Check whether we are replacing a thumb2 32-bit instruction. */
+ if (target_read_memory (*pcptr, buf, 2) == 0)
+ {
+ unsigned short inst1 = 0;
+
+ target_read_memory (*pcptr, (gdb_byte *) &inst1, 2);
+ if (thumb_insn_size (inst1) == 4)
+ return ARM_BP_KIND_THUMB2;
+ }
+ return ARM_BP_KIND_THUMB;
+ }
+ else
+ return ARM_BP_KIND_ARM;
+}
+
+/* Implementation of the linux_target_ops method "sw_breakpoint_from_kind". */
+
+const gdb_byte *
+arm_sw_breakpoint_from_kind (int kind , int *size)
+{
+ *size = arm_breakpoint_len;
+ /* Define an ARM-mode breakpoint; we only set breakpoints in the C
+ library, which is most likely to be ARM. If the kernel supports
+ clone events, we will never insert a breakpoint, so even a Thumb
+ C library will work; so will mixing EABI/non-EABI gdbserver and
+ application. */
+ switch (kind)
+ {
+ case ARM_BP_KIND_THUMB:
+ *size = thumb_breakpoint_len;
+ return (gdb_byte *) &thumb_breakpoint;
+ case ARM_BP_KIND_THUMB2:
+ *size = thumb2_breakpoint_len;
+ return (gdb_byte *) &thumb2_breakpoint;
+ case ARM_BP_KIND_ARM:
+ *size = arm_breakpoint_len;
+ return (const gdb_byte *) &arm_breakpoint;
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+/* Implementation of the linux_target_ops method
+ "breakpoint_kind_from_current_state". */
+
+int
+arm_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+ if (arm_is_thumb_mode ())
+ {
+ *pcptr = MAKE_THUMB_ADDR (*pcptr);
+ return arm_breakpoint_kind_from_pc (pcptr);
+ }
+ else
+ {
+ return arm_breakpoint_kind_from_pc (pcptr);
+ }
+}
+
+void
+initialize_low_arch_aarch32 (void)
+{
+ initialize_regsets_info (&aarch32_regsets_info);
+}
+++ /dev/null
-/* Copyright (C) 2019-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-
-#include "linux-aarch32-tdesc.h"
-
-#include "tdesc.h"
-#include "arch/aarch32.h"
-#include <inttypes.h>
-
-static struct target_desc *tdesc_aarch32;
-
-/* See linux-aarch32-tdesc.h. */
-
-const target_desc *
-aarch32_linux_read_description ()
-{
- if (tdesc_aarch32 == nullptr)
- {
- tdesc_aarch32 = aarch32_create_target_description ();
-
- static const char *expedite_regs[] = { "r11", "sp", "pc", 0 };
- init_target_desc (tdesc_aarch32, expedite_regs);
- }
- return tdesc_aarch32;
-}
-
-/* See linux-aarch32-tdesc.h. */
-
-bool
-is_aarch32_linux_description (const target_desc *tdesc)
-{
- gdb_assert (tdesc != nullptr);
- return tdesc == tdesc_aarch32;
-}
--- /dev/null
+/* Copyright (C) 2019-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+
+#include "linux-aarch32-tdesc.h"
+
+#include "tdesc.h"
+#include "arch/aarch32.h"
+#include <inttypes.h>
+
+static struct target_desc *tdesc_aarch32;
+
+/* See linux-aarch32-tdesc.h. */
+
+const target_desc *
+aarch32_linux_read_description ()
+{
+ if (tdesc_aarch32 == nullptr)
+ {
+ tdesc_aarch32 = aarch32_create_target_description ();
+
+ static const char *expedite_regs[] = { "r11", "sp", "pc", 0 };
+ init_target_desc (tdesc_aarch32, expedite_regs);
+ }
+ return tdesc_aarch32;
+}
+
+/* See linux-aarch32-tdesc.h. */
+
+bool
+is_aarch32_linux_description (const target_desc *tdesc)
+{
+ gdb_assert (tdesc != nullptr);
+ return tdesc == tdesc_aarch32;
+}
+++ /dev/null
-/* GNU/Linux/AArch64 specific low level interface, for the in-process
- agent library for GDB.
-
- Copyright (C) 2015-2020 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 <elf.h>
-#ifdef HAVE_GETAUXVAL
-#include <sys/auxv.h>
-#endif
-#include "linux-aarch64-tdesc.h"
-
-/* Each register saved by the jump pad is in a 16 byte cell. */
-#define FT_CR_SIZE 16
-
-#define FT_CR_FPCR 0
-#define FT_CR_FPSR 1
-#define FT_CR_CPSR 2
-#define FT_CR_PC 3
-#define FT_CR_SP 4
-#define FT_CR_X0 5
-#define FT_CR_GPR(n) (FT_CR_X0 + (n))
-#define FT_CR_FPR(n) (FT_CR_GPR (31) + (n))
-
-/* Mapping between registers collected by the jump pad and GDB's register
- array layout used by regcache.
-
- See linux-aarch64-low.c (aarch64_install_fast_tracepoint_jump_pad) for
- more details. */
-
-static const int aarch64_ft_collect_regmap[] = {
- 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),
- /* FP */
- FT_CR_GPR (29),
- /* LR */
- FT_CR_GPR (30),
- FT_CR_SP,
- FT_CR_PC,
- FT_CR_CPSR,
- FT_CR_FPR (0),
- FT_CR_FPR (1),
- FT_CR_FPR (2),
- FT_CR_FPR (3),
- FT_CR_FPR (4),
- FT_CR_FPR (5),
- FT_CR_FPR (6),
- FT_CR_FPR (7),
- FT_CR_FPR (8),
- FT_CR_FPR (9),
- FT_CR_FPR (10),
- FT_CR_FPR (11),
- FT_CR_FPR (12),
- FT_CR_FPR (13),
- FT_CR_FPR (14),
- FT_CR_FPR (15),
- FT_CR_FPR (16),
- FT_CR_FPR (17),
- FT_CR_FPR (18),
- FT_CR_FPR (19),
- FT_CR_FPR (20),
- FT_CR_FPR (21),
- FT_CR_FPR (22),
- FT_CR_FPR (23),
- FT_CR_FPR (24),
- FT_CR_FPR (25),
- FT_CR_FPR (26),
- FT_CR_FPR (27),
- FT_CR_FPR (28),
- FT_CR_FPR (29),
- FT_CR_FPR (30),
- FT_CR_FPR (31),
- FT_CR_FPSR,
- FT_CR_FPCR
-};
-
-#define AARCH64_NUM_FT_COLLECT_GREGS \
- (sizeof (aarch64_ft_collect_regmap) / sizeof(aarch64_ft_collect_regmap[0]))
-
-/* Fill in REGCACHE with registers saved by the jump pad in BUF. */
-
-void
-supply_fast_tracepoint_registers (struct regcache *regcache,
- const unsigned char *buf)
-{
- int i;
-
- for (i = 0; i < AARCH64_NUM_FT_COLLECT_GREGS; i++)
- supply_register (regcache, i,
- ((char *) buf)
- + (aarch64_ft_collect_regmap[i] * FT_CR_SIZE));
-}
-
-ULONGEST
-get_raw_reg (const unsigned char *raw_regs, int regnum)
-{
- if (regnum >= AARCH64_NUM_FT_COLLECT_GREGS)
- return 0;
-
- return *(ULONGEST *) (raw_regs
- + aarch64_ft_collect_regmap[regnum] * FT_CR_SIZE);
-}
-
-/* Return target_desc to use for IPA, given the tdesc index passed by
- gdbserver. Index is ignored, since we have only one tdesc
- at the moment. SVE and pauth not yet supported. */
-
-const struct target_desc *
-get_ipa_tdesc (int idx)
-{
- return aarch64_linux_read_description (0, false);
-}
-
-/* Allocate buffer for the jump pads. The branch instruction has a reach
- of +/- 128MiB, and the executable is loaded at 0x400000 (4MiB).
- To maximize the area of executable that can use tracepoints, try
- allocating at 0x400000 - size initially, decreasing until we hit
- a free area. */
-
-void *
-alloc_jump_pad_buffer (size_t size)
-{
- uintptr_t addr;
- uintptr_t exec_base = getauxval (AT_PHDR);
- int pagesize;
- void *res;
-
- if (exec_base == 0)
- exec_base = 0x400000;
-
- 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;
-}
-
-void
-initialize_low_tracepoint (void)
-{
- /* SVE and pauth not yet supported. */
- aarch64_linux_read_description (0, false);
-}
--- /dev/null
+/* GNU/Linux/AArch64 specific low level interface, for the in-process
+ agent library for GDB.
+
+ Copyright (C) 2015-2020 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 <elf.h>
+#ifdef HAVE_GETAUXVAL
+#include <sys/auxv.h>
+#endif
+#include "linux-aarch64-tdesc.h"
+
+/* Each register saved by the jump pad is in a 16 byte cell. */
+#define FT_CR_SIZE 16
+
+#define FT_CR_FPCR 0
+#define FT_CR_FPSR 1
+#define FT_CR_CPSR 2
+#define FT_CR_PC 3
+#define FT_CR_SP 4
+#define FT_CR_X0 5
+#define FT_CR_GPR(n) (FT_CR_X0 + (n))
+#define FT_CR_FPR(n) (FT_CR_GPR (31) + (n))
+
+/* Mapping between registers collected by the jump pad and GDB's register
+ array layout used by regcache.
+
+ See linux-aarch64-low.c (aarch64_install_fast_tracepoint_jump_pad) for
+ more details. */
+
+static const int aarch64_ft_collect_regmap[] = {
+ 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),
+ /* FP */
+ FT_CR_GPR (29),
+ /* LR */
+ FT_CR_GPR (30),
+ FT_CR_SP,
+ FT_CR_PC,
+ FT_CR_CPSR,
+ FT_CR_FPR (0),
+ FT_CR_FPR (1),
+ FT_CR_FPR (2),
+ FT_CR_FPR (3),
+ FT_CR_FPR (4),
+ FT_CR_FPR (5),
+ FT_CR_FPR (6),
+ FT_CR_FPR (7),
+ FT_CR_FPR (8),
+ FT_CR_FPR (9),
+ FT_CR_FPR (10),
+ FT_CR_FPR (11),
+ FT_CR_FPR (12),
+ FT_CR_FPR (13),
+ FT_CR_FPR (14),
+ FT_CR_FPR (15),
+ FT_CR_FPR (16),
+ FT_CR_FPR (17),
+ FT_CR_FPR (18),
+ FT_CR_FPR (19),
+ FT_CR_FPR (20),
+ FT_CR_FPR (21),
+ FT_CR_FPR (22),
+ FT_CR_FPR (23),
+ FT_CR_FPR (24),
+ FT_CR_FPR (25),
+ FT_CR_FPR (26),
+ FT_CR_FPR (27),
+ FT_CR_FPR (28),
+ FT_CR_FPR (29),
+ FT_CR_FPR (30),
+ FT_CR_FPR (31),
+ FT_CR_FPSR,
+ FT_CR_FPCR
+};
+
+#define AARCH64_NUM_FT_COLLECT_GREGS \
+ (sizeof (aarch64_ft_collect_regmap) / sizeof(aarch64_ft_collect_regmap[0]))
+
+/* Fill in REGCACHE with registers saved by the jump pad in BUF. */
+
+void
+supply_fast_tracepoint_registers (struct regcache *regcache,
+ const unsigned char *buf)
+{
+ int i;
+
+ for (i = 0; i < AARCH64_NUM_FT_COLLECT_GREGS; i++)
+ supply_register (regcache, i,
+ ((char *) buf)
+ + (aarch64_ft_collect_regmap[i] * FT_CR_SIZE));
+}
+
+ULONGEST
+get_raw_reg (const unsigned char *raw_regs, int regnum)
+{
+ if (regnum >= AARCH64_NUM_FT_COLLECT_GREGS)
+ return 0;
+
+ return *(ULONGEST *) (raw_regs
+ + aarch64_ft_collect_regmap[regnum] * FT_CR_SIZE);
+}
+
+/* Return target_desc to use for IPA, given the tdesc index passed by
+ gdbserver. Index is ignored, since we have only one tdesc
+ at the moment. SVE and pauth not yet supported. */
+
+const struct target_desc *
+get_ipa_tdesc (int idx)
+{
+ return aarch64_linux_read_description (0, false);
+}
+
+/* Allocate buffer for the jump pads. The branch instruction has a reach
+ of +/- 128MiB, and the executable is loaded at 0x400000 (4MiB).
+ To maximize the area of executable that can use tracepoints, try
+ allocating at 0x400000 - size initially, decreasing until we hit
+ a free area. */
+
+void *
+alloc_jump_pad_buffer (size_t size)
+{
+ uintptr_t addr;
+ uintptr_t exec_base = getauxval (AT_PHDR);
+ int pagesize;
+ void *res;
+
+ if (exec_base == 0)
+ exec_base = 0x400000;
+
+ 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;
+}
+
+void
+initialize_low_tracepoint (void)
+{
+ /* SVE and pauth not yet supported. */
+ aarch64_linux_read_description (0, false);
+}
+++ /dev/null
-/* GNU/Linux/AArch64 specific low level interface, for the remote server for
- GDB.
-
- Copyright (C) 2009-2020 Free Software Foundation, Inc.
- Contributed by ARM Ltd.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "nat/aarch64-linux.h"
-#include "nat/aarch64-linux-hw-point.h"
-#include "arch/aarch64-insn.h"
-#include "linux-aarch32-low.h"
-#include "elf/common.h"
-#include "ax.h"
-#include "tracepoint.h"
-#include "debug.h"
-
-#include <signal.h>
-#include <sys/user.h>
-#include "nat/gdb_ptrace.h"
-#include <asm/ptrace.h>
-#include <inttypes.h>
-#include <endian.h>
-#include <sys/uio.h>
-
-#include "gdb_proc_service.h"
-#include "arch/aarch64.h"
-#include "linux-aarch32-tdesc.h"
-#include "linux-aarch64-tdesc.h"
-#include "nat/aarch64-sve-linux-ptrace.h"
-#include "tdesc.h"
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-/* Per-process arch-specific data we want to keep. */
-
-struct arch_process_info
-{
- /* Hardware breakpoint/watchpoint data.
- The reason for them to be per-process rather than per-thread is
- due to the lack of information in the gdbserver environment;
- gdbserver is not told that whether a requested hardware
- breakpoint/watchpoint is thread specific or not, so it has to set
- each hw bp/wp for every thread in the current process. The
- higher level bp/wp management in gdb will resume a thread if a hw
- bp/wp trap is not expected for it. Since the hw bp/wp setting is
- same for each thread, it is reasonable for the data to live here.
- */
- struct aarch64_debug_reg_state debug_reg_state;
-};
-
-/* Return true if the size of register 0 is 8 byte. */
-
-static int
-is_64bit_tdesc (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
-
- return register_size (regcache->tdesc, 0) == 8;
-}
-
-/* Return true if the regcache contains the number of SVE registers. */
-
-static bool
-is_sve_tdesc (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
-
- return tdesc_contains_feature (regcache->tdesc, "org.gnu.gdb.aarch64.sve");
-}
-
-static void
-aarch64_fill_gregset (struct regcache *regcache, void *buf)
-{
- struct user_pt_regs *regset = (struct user_pt_regs *) buf;
- int i;
-
- for (i = 0; i < AARCH64_X_REGS_NUM; i++)
- collect_register (regcache, AARCH64_X0_REGNUM + i, ®set->regs[i]);
- collect_register (regcache, AARCH64_SP_REGNUM, ®set->sp);
- collect_register (regcache, AARCH64_PC_REGNUM, ®set->pc);
- collect_register (regcache, AARCH64_CPSR_REGNUM, ®set->pstate);
-}
-
-static void
-aarch64_store_gregset (struct regcache *regcache, const void *buf)
-{
- const struct user_pt_regs *regset = (const struct user_pt_regs *) buf;
- int i;
-
- for (i = 0; i < AARCH64_X_REGS_NUM; i++)
- supply_register (regcache, AARCH64_X0_REGNUM + i, ®set->regs[i]);
- supply_register (regcache, AARCH64_SP_REGNUM, ®set->sp);
- supply_register (regcache, AARCH64_PC_REGNUM, ®set->pc);
- supply_register (regcache, AARCH64_CPSR_REGNUM, ®set->pstate);
-}
-
-static void
-aarch64_fill_fpregset (struct regcache *regcache, void *buf)
-{
- struct user_fpsimd_state *regset = (struct user_fpsimd_state *) buf;
- int i;
-
- for (i = 0; i < AARCH64_V_REGS_NUM; i++)
- collect_register (regcache, AARCH64_V0_REGNUM + i, ®set->vregs[i]);
- collect_register (regcache, AARCH64_FPSR_REGNUM, ®set->fpsr);
- collect_register (regcache, AARCH64_FPCR_REGNUM, ®set->fpcr);
-}
-
-static void
-aarch64_store_fpregset (struct regcache *regcache, const void *buf)
-{
- const struct user_fpsimd_state *regset
- = (const struct user_fpsimd_state *) buf;
- int i;
-
- for (i = 0; i < AARCH64_V_REGS_NUM; i++)
- supply_register (regcache, AARCH64_V0_REGNUM + i, ®set->vregs[i]);
- supply_register (regcache, AARCH64_FPSR_REGNUM, ®set->fpsr);
- supply_register (regcache, AARCH64_FPCR_REGNUM, ®set->fpcr);
-}
-
-/* Store the pauth registers to regcache. */
-
-static void
-aarch64_store_pauthregset (struct regcache *regcache, const void *buf)
-{
- uint64_t *pauth_regset = (uint64_t *) buf;
- int pauth_base = find_regno (regcache->tdesc, "pauth_dmask");
-
- if (pauth_base == 0)
- return;
-
- supply_register (regcache, AARCH64_PAUTH_DMASK_REGNUM (pauth_base),
- &pauth_regset[0]);
- supply_register (regcache, AARCH64_PAUTH_CMASK_REGNUM (pauth_base),
- &pauth_regset[1]);
-}
-
-/* Implementation of linux_target_ops method "get_pc". */
-
-static CORE_ADDR
-aarch64_get_pc (struct regcache *regcache)
-{
- if (register_size (regcache->tdesc, 0) == 8)
- return linux_get_pc_64bit (regcache);
- else
- return linux_get_pc_32bit (regcache);
-}
-
-/* Implementation of linux_target_ops method "set_pc". */
-
-static void
-aarch64_set_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- if (register_size (regcache->tdesc, 0) == 8)
- linux_set_pc_64bit (regcache, pc);
- else
- linux_set_pc_32bit (regcache, pc);
-}
-
-#define aarch64_breakpoint_len 4
-
-/* AArch64 BRK software debug mode instruction.
- This instruction needs to match gdb/aarch64-tdep.c
- (aarch64_default_breakpoint). */
-static const gdb_byte aarch64_breakpoint[] = {0x00, 0x00, 0x20, 0xd4};
-
-/* Implementation of linux_target_ops method "breakpoint_at". */
-
-static int
-aarch64_breakpoint_at (CORE_ADDR where)
-{
- if (is_64bit_tdesc ())
- {
- gdb_byte insn[aarch64_breakpoint_len];
-
- (*the_target->read_memory) (where, (unsigned char *) &insn,
- aarch64_breakpoint_len);
- if (memcmp (insn, aarch64_breakpoint, aarch64_breakpoint_len) == 0)
- return 1;
-
- return 0;
- }
- else
- return arm_breakpoint_at (where);
-}
-
-static void
-aarch64_init_debug_reg_state (struct aarch64_debug_reg_state *state)
-{
- int i;
-
- for (i = 0; i < AARCH64_HBP_MAX_NUM; ++i)
- {
- state->dr_addr_bp[i] = 0;
- state->dr_ctrl_bp[i] = 0;
- state->dr_ref_count_bp[i] = 0;
- }
-
- for (i = 0; i < AARCH64_HWP_MAX_NUM; ++i)
- {
- state->dr_addr_wp[i] = 0;
- state->dr_ctrl_wp[i] = 0;
- state->dr_ref_count_wp[i] = 0;
- }
-}
-
-/* Return the pointer to the debug register state structure in the
- current process' arch-specific data area. */
-
-struct aarch64_debug_reg_state *
-aarch64_get_debug_reg_state (pid_t pid)
-{
- struct process_info *proc = find_process_pid (pid);
-
- return &proc->priv->arch_private->debug_reg_state;
-}
-
-/* Implementation of linux_target_ops method "supports_z_point_type". */
-
-static int
-aarch64_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_SW_BP:
- case Z_PACKET_HW_BP:
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_READ_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
- return 0;
- }
-}
-
-/* Implementation of linux_target_ops method "insert_point".
-
- It actually only records the info of the to-be-inserted bp/wp;
- the actual insertion will happen when threads are resumed. */
-
-static int
-aarch64_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
-{
- int ret;
- enum target_hw_bp_type targ_type;
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (pid_of (current_thread));
-
- if (show_debug_regs)
- fprintf (stderr, "insert_point on entry (addr=0x%08lx, len=%d)\n",
- (unsigned long) addr, len);
-
- /* Determine the type from the raw breakpoint type. */
- targ_type = raw_bkpt_type_to_target_hw_bp_type (type);
-
- if (targ_type != hw_execute)
- {
- if (aarch64_linux_region_ok_for_watchpoint (addr, len))
- ret = aarch64_handle_watchpoint (targ_type, addr, len,
- 1 /* is_insert */, state);
- else
- ret = -1;
- }
- else
- {
- if (len == 3)
- {
- /* LEN is 3 means the breakpoint is set on a 32-bit thumb
- instruction. Set it to 2 to correctly encode length bit
- mask in hardware/watchpoint control register. */
- len = 2;
- }
- ret = aarch64_handle_breakpoint (targ_type, addr, len,
- 1 /* is_insert */, state);
- }
-
- if (show_debug_regs)
- aarch64_show_debug_reg_state (state, "insert_point", addr, len,
- targ_type);
-
- return ret;
-}
-
-/* Implementation of linux_target_ops method "remove_point".
-
- It actually only records the info of the to-be-removed bp/wp,
- the actual removal will be done when threads are resumed. */
-
-static int
-aarch64_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
-{
- int ret;
- enum target_hw_bp_type targ_type;
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (pid_of (current_thread));
-
- if (show_debug_regs)
- fprintf (stderr, "remove_point on entry (addr=0x%08lx, len=%d)\n",
- (unsigned long) addr, len);
-
- /* Determine the type from the raw breakpoint type. */
- targ_type = raw_bkpt_type_to_target_hw_bp_type (type);
-
- /* Set up state pointers. */
- if (targ_type != hw_execute)
- ret =
- aarch64_handle_watchpoint (targ_type, addr, len, 0 /* is_insert */,
- state);
- else
- {
- if (len == 3)
- {
- /* LEN is 3 means the breakpoint is set on a 32-bit thumb
- instruction. Set it to 2 to correctly encode length bit
- mask in hardware/watchpoint control register. */
- len = 2;
- }
- ret = aarch64_handle_breakpoint (targ_type, addr, len,
- 0 /* is_insert */, state);
- }
-
- if (show_debug_regs)
- aarch64_show_debug_reg_state (state, "remove_point", addr, len,
- targ_type);
-
- return ret;
-}
-
-/* Implementation of linux_target_ops method "stopped_data_address". */
-
-static CORE_ADDR
-aarch64_stopped_data_address (void)
-{
- siginfo_t siginfo;
- int pid, i;
- struct aarch64_debug_reg_state *state;
-
- pid = lwpid_of (current_thread);
-
- /* Get the siginfo. */
- if (ptrace (PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0)
- return (CORE_ADDR) 0;
-
- /* Need to be a hardware breakpoint/watchpoint trap. */
- if (siginfo.si_signo != SIGTRAP
- || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
- return (CORE_ADDR) 0;
-
- /* Check if the address matches any watched address. */
- state = aarch64_get_debug_reg_state (pid_of (current_thread));
- for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
- {
- const unsigned int offset
- = aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
- const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
- const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
- const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
- const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
- const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
-
- if (state->dr_ref_count_wp[i]
- && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
- && addr_trap >= addr_watch_aligned
- && addr_trap < addr_watch + len)
- {
- /* ADDR_TRAP reports the first address of the memory range
- accessed by the CPU, regardless of what was the memory
- range watched. Thus, a large CPU access that straddles
- the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
- ADDR_TRAP that is lower than the
- ADDR_WATCH..ADDR_WATCH+LEN range. E.g.:
-
- addr: | 4 | 5 | 6 | 7 | 8 |
- |---- range watched ----|
- |----------- range accessed ------------|
-
- In this case, ADDR_TRAP will be 4.
-
- To match a watchpoint known to GDB core, we must never
- report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
- range. ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
- positive on kernels older than 4.10. See PR
- external/20207. */
- return addr_orig;
- }
- }
-
- return (CORE_ADDR) 0;
-}
-
-/* Implementation of linux_target_ops method "stopped_by_watchpoint". */
-
-static int
-aarch64_stopped_by_watchpoint (void)
-{
- if (aarch64_stopped_data_address () != 0)
- return 1;
- else
- return 0;
-}
-
-/* Fetch the thread-local storage pointer for libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- return aarch64_ps_get_thread_area (ph, lwpid, idx, base,
- is_64bit_tdesc ());
-}
-
-/* Implementation of linux_target_ops method "siginfo_fixup". */
-
-static int
-aarch64_linux_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction)
-{
- /* Is the inferior 32-bit? If so, then fixup the siginfo object. */
- if (!is_64bit_tdesc ())
- {
- if (direction == 0)
- aarch64_compat_siginfo_from_siginfo ((struct compat_siginfo *) inf,
- native);
- else
- aarch64_siginfo_from_compat_siginfo (native,
- (struct compat_siginfo *) inf);
-
- return 1;
- }
-
- return 0;
-}
-
-/* Implementation of linux_target_ops method "new_process". */
-
-static struct arch_process_info *
-aarch64_linux_new_process (void)
-{
- struct arch_process_info *info = XCNEW (struct arch_process_info);
-
- aarch64_init_debug_reg_state (&info->debug_reg_state);
-
- return info;
-}
-
-/* Implementation of linux_target_ops method "delete_process". */
-
-static void
-aarch64_linux_delete_process (struct arch_process_info *info)
-{
- xfree (info);
-}
-
-/* Implementation of linux_target_ops method "linux_new_fork". */
-
-static void
-aarch64_linux_new_fork (struct process_info *parent,
- struct process_info *child)
-{
- /* These are allocated by linux_add_process. */
- gdb_assert (parent->priv != NULL
- && parent->priv->arch_private != NULL);
- gdb_assert (child->priv != NULL
- && child->priv->arch_private != NULL);
-
- /* Linux kernel before 2.6.33 commit
- 72f674d203cd230426437cdcf7dd6f681dad8b0d
- will inherit hardware debug registers from parent
- on fork/vfork/clone. Newer Linux kernels create such tasks with
- zeroed debug registers.
-
- GDB core assumes the child inherits the watchpoints/hw
- breakpoints of the parent, and will remove them all from the
- forked off process. Copy the debug registers mirrors into the
- new process so that all breakpoints and watchpoints can be
- removed together. The debug registers mirror will become zeroed
- in the end before detaching the forked off process, thus making
- this compatible with older Linux kernels too. */
-
- *child->priv->arch_private = *parent->priv->arch_private;
-}
-
-/* Matches HWCAP_PACA in kernel header arch/arm64/include/uapi/asm/hwcap.h. */
-#define AARCH64_HWCAP_PACA (1 << 30)
-
-/* Implementation of linux_target_ops method "arch_setup". */
-
-static void
-aarch64_arch_setup (void)
-{
- unsigned int machine;
- int is_elf64;
- int tid;
-
- tid = lwpid_of (current_thread);
-
- is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
-
- if (is_elf64)
- {
- uint64_t vq = aarch64_sve_get_vq (tid);
- unsigned long hwcap = linux_get_hwcap (8);
- bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
-
- current_process ()->tdesc = aarch64_linux_read_description (vq, pauth_p);
- }
- else
- current_process ()->tdesc = aarch32_linux_read_description ();
-
- aarch64_linux_get_debug_reg_capacity (lwpid_of (current_thread));
-}
-
-/* Wrapper for aarch64_sve_regs_copy_to_reg_buf. */
-
-static void
-aarch64_sve_regs_copy_to_regcache (struct regcache *regcache, const void *buf)
-{
- return aarch64_sve_regs_copy_to_reg_buf (regcache, buf);
-}
-
-/* Wrapper for aarch64_sve_regs_copy_from_reg_buf. */
-
-static void
-aarch64_sve_regs_copy_from_regcache (struct regcache *regcache, void *buf)
-{
- return aarch64_sve_regs_copy_from_reg_buf (regcache, buf);
-}
-
-static struct regset_info aarch64_regsets[] =
-{
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
- sizeof (struct user_pt_regs), GENERAL_REGS,
- aarch64_fill_gregset, aarch64_store_gregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
- sizeof (struct user_fpsimd_state), FP_REGS,
- aarch64_fill_fpregset, aarch64_store_fpregset
- },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
- AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
- NULL, aarch64_store_pauthregset },
- NULL_REGSET
-};
-
-static struct regsets_info aarch64_regsets_info =
- {
- aarch64_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct regs_info regs_info_aarch64 =
- {
- NULL, /* regset_bitmap */
- NULL, /* usrregs */
- &aarch64_regsets_info,
- };
-
-static struct regset_info aarch64_sve_regsets[] =
-{
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
- sizeof (struct user_pt_regs), GENERAL_REGS,
- aarch64_fill_gregset, aarch64_store_gregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_SVE,
- SVE_PT_SIZE (AARCH64_MAX_SVE_VQ, SVE_PT_REGS_SVE), EXTENDED_REGS,
- aarch64_sve_regs_copy_from_regcache, aarch64_sve_regs_copy_to_regcache
- },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
- AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
- NULL, aarch64_store_pauthregset },
- NULL_REGSET
-};
-
-static struct regsets_info aarch64_sve_regsets_info =
- {
- aarch64_sve_regsets, /* regsets. */
- 0, /* num_regsets. */
- NULL, /* disabled_regsets. */
- };
-
-static struct regs_info regs_info_aarch64_sve =
- {
- NULL, /* regset_bitmap. */
- NULL, /* usrregs. */
- &aarch64_sve_regsets_info,
- };
-
-/* Implementation of linux_target_ops method "regs_info". */
-
-static const struct regs_info *
-aarch64_regs_info (void)
-{
- if (!is_64bit_tdesc ())
- return ®s_info_aarch32;
-
- if (is_sve_tdesc ())
- return ®s_info_aarch64_sve;
-
- return ®s_info_aarch64;
-}
-
-/* Implementation of linux_target_ops method "supports_tracepoints". */
-
-static int
-aarch64_supports_tracepoints (void)
-{
- if (current_thread == NULL)
- return 1;
- else
- {
- /* We don't support tracepoints on aarch32 now. */
- return is_64bit_tdesc ();
- }
-}
-
-/* Implementation of linux_target_ops method "get_thread_area". */
-
-static int
-aarch64_get_thread_area (int lwpid, CORE_ADDR *addrp)
-{
- struct iovec iovec;
- uint64_t reg;
-
- iovec.iov_base = ®
- iovec.iov_len = sizeof (reg);
-
- if (ptrace (PTRACE_GETREGSET, lwpid, NT_ARM_TLS, &iovec) != 0)
- return -1;
-
- *addrp = reg;
-
- return 0;
-}
-
-/* Implementation of linux_target_ops method "get_syscall_trapinfo". */
-
-static void
-aarch64_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
-{
- int use_64bit = register_size (regcache->tdesc, 0) == 8;
-
- if (use_64bit)
- {
- long l_sysno;
-
- collect_register_by_name (regcache, "x8", &l_sysno);
- *sysno = (int) l_sysno;
- }
- else
- collect_register_by_name (regcache, "r7", sysno);
-}
-
-/* List of condition codes that we need. */
-
-enum aarch64_condition_codes
-{
- EQ = 0x0,
- NE = 0x1,
- LO = 0x3,
- GE = 0xa,
- LT = 0xb,
- GT = 0xc,
- LE = 0xd,
-};
-
-enum aarch64_operand_type
-{
- OPERAND_IMMEDIATE,
- OPERAND_REGISTER,
-};
-
-/* Representation of an operand. At this time, it only supports register
- and immediate types. */
-
-struct aarch64_operand
-{
- /* Type of the operand. */
- enum aarch64_operand_type type;
-
- /* Value of the operand according to the type. */
- union
- {
- uint32_t imm;
- struct aarch64_register reg;
- };
-};
-
-/* List of registers that we are currently using, we can add more here as
- we need to use them. */
-
-/* General purpose scratch registers (64 bit). */
-static const struct aarch64_register x0 = { 0, 1 };
-static const struct aarch64_register x1 = { 1, 1 };
-static const struct aarch64_register x2 = { 2, 1 };
-static const struct aarch64_register x3 = { 3, 1 };
-static const struct aarch64_register x4 = { 4, 1 };
-
-/* General purpose scratch registers (32 bit). */
-static const struct aarch64_register w0 = { 0, 0 };
-static const struct aarch64_register w2 = { 2, 0 };
-
-/* Intra-procedure scratch registers. */
-static const struct aarch64_register ip0 = { 16, 1 };
-
-/* Special purpose registers. */
-static const struct aarch64_register fp = { 29, 1 };
-static const struct aarch64_register lr = { 30, 1 };
-static const struct aarch64_register sp = { 31, 1 };
-static const struct aarch64_register xzr = { 31, 1 };
-
-/* Dynamically allocate a new register. If we know the register
- statically, we should make it a global as above instead of using this
- helper function. */
-
-static struct aarch64_register
-aarch64_register (unsigned num, int is64)
-{
- return (struct aarch64_register) { num, is64 };
-}
-
-/* Helper function to create a register operand, for instructions with
- different types of operands.
-
- For example:
- p += emit_mov (p, x0, register_operand (x1)); */
-
-static struct aarch64_operand
-register_operand (struct aarch64_register reg)
-{
- struct aarch64_operand operand;
-
- operand.type = OPERAND_REGISTER;
- operand.reg = reg;
-
- return operand;
-}
-
-/* Helper function to create an immediate operand, for instructions with
- different types of operands.
-
- For example:
- p += emit_mov (p, x0, immediate_operand (12)); */
-
-static struct aarch64_operand
-immediate_operand (uint32_t imm)
-{
- struct aarch64_operand operand;
-
- operand.type = OPERAND_IMMEDIATE;
- operand.imm = imm;
-
- return operand;
-}
-
-/* Helper function to create an offset memory operand.
-
- For example:
- p += emit_ldr (p, x0, sp, offset_memory_operand (16)); */
-
-static struct aarch64_memory_operand
-offset_memory_operand (int32_t offset)
-{
- return (struct aarch64_memory_operand) { MEMORY_OPERAND_OFFSET, offset };
-}
-
-/* Helper function to create a pre-index memory operand.
-
- For example:
- p += emit_ldr (p, x0, sp, preindex_memory_operand (16)); */
-
-static struct aarch64_memory_operand
-preindex_memory_operand (int32_t index)
-{
- return (struct aarch64_memory_operand) { MEMORY_OPERAND_PREINDEX, index };
-}
-
-/* Helper function to create a post-index memory operand.
-
- For example:
- p += emit_ldr (p, x0, sp, postindex_memory_operand (16)); */
-
-static struct aarch64_memory_operand
-postindex_memory_operand (int32_t index)
-{
- return (struct aarch64_memory_operand) { MEMORY_OPERAND_POSTINDEX, index };
-}
-
-/* System control registers. These special registers can be written and
- read with the MRS and MSR instructions.
-
- - NZCV: Condition flags. GDB refers to this register under the CPSR
- name.
- - FPSR: Floating-point status register.
- - FPCR: Floating-point control registers.
- - TPIDR_EL0: Software thread ID register. */
-
-enum aarch64_system_control_registers
-{
- /* op0 op1 crn crm op2 */
- NZCV = (0x1 << 14) | (0x3 << 11) | (0x4 << 7) | (0x2 << 3) | 0x0,
- FPSR = (0x1 << 14) | (0x3 << 11) | (0x4 << 7) | (0x4 << 3) | 0x1,
- FPCR = (0x1 << 14) | (0x3 << 11) | (0x4 << 7) | (0x4 << 3) | 0x0,
- TPIDR_EL0 = (0x1 << 14) | (0x3 << 11) | (0xd << 7) | (0x0 << 3) | 0x2
-};
-
-/* Write a BLR instruction into *BUF.
-
- BLR rn
-
- RN is the register to branch to. */
-
-static int
-emit_blr (uint32_t *buf, struct aarch64_register rn)
-{
- return aarch64_emit_insn (buf, BLR | ENCODE (rn.num, 5, 5));
-}
-
-/* Write a RET instruction into *BUF.
-
- RET xn
-
- RN is the register to branch to. */
-
-static int
-emit_ret (uint32_t *buf, struct aarch64_register rn)
-{
- return aarch64_emit_insn (buf, RET | ENCODE (rn.num, 5, 5));
-}
-
-static int
-emit_load_store_pair (uint32_t *buf, enum aarch64_opcodes opcode,
- struct aarch64_register rt,
- struct aarch64_register rt2,
- struct aarch64_register rn,
- struct aarch64_memory_operand operand)
-{
- uint32_t opc;
- uint32_t pre_index;
- uint32_t write_back;
-
- if (rt.is64)
- opc = ENCODE (2, 2, 30);
- else
- opc = ENCODE (0, 2, 30);
-
- switch (operand.type)
- {
- case MEMORY_OPERAND_OFFSET:
- {
- pre_index = ENCODE (1, 1, 24);
- write_back = ENCODE (0, 1, 23);
- break;
- }
- case MEMORY_OPERAND_POSTINDEX:
- {
- pre_index = ENCODE (0, 1, 24);
- write_back = ENCODE (1, 1, 23);
- break;
- }
- case MEMORY_OPERAND_PREINDEX:
- {
- pre_index = ENCODE (1, 1, 24);
- write_back = ENCODE (1, 1, 23);
- break;
- }
- default:
- return 0;
- }
-
- return aarch64_emit_insn (buf, opcode | opc | pre_index | write_back
- | ENCODE (operand.index >> 3, 7, 15)
- | ENCODE (rt2.num, 5, 10)
- | ENCODE (rn.num, 5, 5) | ENCODE (rt.num, 5, 0));
-}
-
-/* Write a STP instruction into *BUF.
-
- STP rt, rt2, [rn, #offset]
- STP rt, rt2, [rn, #index]!
- STP rt, rt2, [rn], #index
-
- RT and RT2 are the registers to store.
- RN is the base address register.
- OFFSET is the immediate to add to the base address. It is limited to a
- -512 .. 504 range (7 bits << 3). */
-
-static int
-emit_stp (uint32_t *buf, struct aarch64_register rt,
- struct aarch64_register rt2, struct aarch64_register rn,
- struct aarch64_memory_operand operand)
-{
- return emit_load_store_pair (buf, STP, rt, rt2, rn, operand);
-}
-
-/* Write a LDP instruction into *BUF.
-
- LDP rt, rt2, [rn, #offset]
- LDP rt, rt2, [rn, #index]!
- LDP rt, rt2, [rn], #index
-
- RT and RT2 are the registers to store.
- RN is the base address register.
- OFFSET is the immediate to add to the base address. It is limited to a
- -512 .. 504 range (7 bits << 3). */
-
-static int
-emit_ldp (uint32_t *buf, struct aarch64_register rt,
- struct aarch64_register rt2, struct aarch64_register rn,
- struct aarch64_memory_operand operand)
-{
- return emit_load_store_pair (buf, LDP, rt, rt2, rn, operand);
-}
-
-/* Write a LDP (SIMD&VFP) instruction using Q registers into *BUF.
-
- LDP qt, qt2, [rn, #offset]
-
- RT and RT2 are the Q registers to store.
- RN is the base address register.
- OFFSET is the immediate to add to the base address. It is limited to
- -1024 .. 1008 range (7 bits << 4). */
-
-static int
-emit_ldp_q_offset (uint32_t *buf, unsigned rt, unsigned rt2,
- struct aarch64_register rn, int32_t offset)
-{
- uint32_t opc = ENCODE (2, 2, 30);
- uint32_t pre_index = ENCODE (1, 1, 24);
-
- return aarch64_emit_insn (buf, LDP_SIMD_VFP | opc | pre_index
- | ENCODE (offset >> 4, 7, 15)
- | ENCODE (rt2, 5, 10)
- | ENCODE (rn.num, 5, 5) | ENCODE (rt, 5, 0));
-}
-
-/* Write a STP (SIMD&VFP) instruction using Q registers into *BUF.
-
- STP qt, qt2, [rn, #offset]
-
- RT and RT2 are the Q registers to store.
- RN is the base address register.
- OFFSET is the immediate to add to the base address. It is limited to
- -1024 .. 1008 range (7 bits << 4). */
-
-static int
-emit_stp_q_offset (uint32_t *buf, unsigned rt, unsigned rt2,
- struct aarch64_register rn, int32_t offset)
-{
- uint32_t opc = ENCODE (2, 2, 30);
- uint32_t pre_index = ENCODE (1, 1, 24);
-
- return aarch64_emit_insn (buf, STP_SIMD_VFP | opc | pre_index
- | ENCODE (offset >> 4, 7, 15)
- | ENCODE (rt2, 5, 10)
- | ENCODE (rn.num, 5, 5) | ENCODE (rt, 5, 0));
-}
-
-/* Write a LDRH instruction into *BUF.
-
- LDRH wt, [xn, #offset]
- LDRH wt, [xn, #index]!
- LDRH wt, [xn], #index
-
- RT is the register to store.
- RN is the base address register.
- OFFSET is the immediate to add to the base address. It is limited to
- 0 .. 32760 range (12 bits << 3). */
-
-static int
-emit_ldrh (uint32_t *buf, struct aarch64_register rt,
- struct aarch64_register rn,
- struct aarch64_memory_operand operand)
-{
- return aarch64_emit_load_store (buf, 1, LDR, rt, rn, operand);
-}
-
-/* Write a LDRB instruction into *BUF.
-
- LDRB wt, [xn, #offset]
- LDRB wt, [xn, #index]!
- LDRB wt, [xn], #index
-
- RT is the register to store.
- RN is the base address register.
- OFFSET is the immediate to add to the base address. It is limited to
- 0 .. 32760 range (12 bits << 3). */
-
-static int
-emit_ldrb (uint32_t *buf, struct aarch64_register rt,
- struct aarch64_register rn,
- struct aarch64_memory_operand operand)
-{
- return aarch64_emit_load_store (buf, 0, LDR, rt, rn, operand);
-}
-
-
-
-/* Write a STR instruction into *BUF.
-
- STR rt, [rn, #offset]
- STR rt, [rn, #index]!
- STR rt, [rn], #index
-
- RT is the register to store.
- RN is the base address register.
- OFFSET is the immediate to add to the base address. It is limited to
- 0 .. 32760 range (12 bits << 3). */
-
-static int
-emit_str (uint32_t *buf, struct aarch64_register rt,
- struct aarch64_register rn,
- struct aarch64_memory_operand operand)
-{
- return aarch64_emit_load_store (buf, rt.is64 ? 3 : 2, STR, rt, rn, operand);
-}
-
-/* Helper function emitting an exclusive load or store instruction. */
-
-static int
-emit_load_store_exclusive (uint32_t *buf, uint32_t size,
- enum aarch64_opcodes opcode,
- struct aarch64_register rs,
- struct aarch64_register rt,
- struct aarch64_register rt2,
- struct aarch64_register rn)
-{
- return aarch64_emit_insn (buf, opcode | ENCODE (size, 2, 30)
- | ENCODE (rs.num, 5, 16) | ENCODE (rt2.num, 5, 10)
- | ENCODE (rn.num, 5, 5) | ENCODE (rt.num, 5, 0));
-}
-
-/* Write a LAXR instruction into *BUF.
-
- LDAXR rt, [xn]
-
- RT is the destination register.
- RN is the base address register. */
-
-static int
-emit_ldaxr (uint32_t *buf, struct aarch64_register rt,
- struct aarch64_register rn)
-{
- return emit_load_store_exclusive (buf, rt.is64 ? 3 : 2, LDAXR, xzr, rt,
- xzr, rn);
-}
-
-/* Write a STXR instruction into *BUF.
-
- STXR ws, rt, [xn]
-
- RS is the result register, it indicates if the store succeeded or not.
- RT is the destination register.
- RN is the base address register. */
-
-static int
-emit_stxr (uint32_t *buf, struct aarch64_register rs,
- struct aarch64_register rt, struct aarch64_register rn)
-{
- return emit_load_store_exclusive (buf, rt.is64 ? 3 : 2, STXR, rs, rt,
- xzr, rn);
-}
-
-/* Write a STLR instruction into *BUF.
-
- STLR rt, [xn]
-
- RT is the register to store.
- RN is the base address register. */
-
-static int
-emit_stlr (uint32_t *buf, struct aarch64_register rt,
- struct aarch64_register rn)
-{
- return emit_load_store_exclusive (buf, rt.is64 ? 3 : 2, STLR, xzr, rt,
- xzr, rn);
-}
-
-/* Helper function for data processing instructions with register sources. */
-
-static int
-emit_data_processing_reg (uint32_t *buf, uint32_t opcode,
- struct aarch64_register rd,
- struct aarch64_register rn,
- struct aarch64_register rm)
-{
- uint32_t size = ENCODE (rd.is64, 1, 31);
-
- return aarch64_emit_insn (buf, opcode | size | ENCODE (rm.num, 5, 16)
- | ENCODE (rn.num, 5, 5) | ENCODE (rd.num, 5, 0));
-}
-
-/* Helper function for data processing instructions taking either a register
- or an immediate. */
-
-static int
-emit_data_processing (uint32_t *buf, enum aarch64_opcodes opcode,
- struct aarch64_register rd,
- struct aarch64_register rn,
- struct aarch64_operand operand)
-{
- uint32_t size = ENCODE (rd.is64, 1, 31);
- /* The opcode is different for register and immediate source operands. */
- uint32_t operand_opcode;
-
- if (operand.type == OPERAND_IMMEDIATE)
- {
- /* xxx1 000x xxxx xxxx xxxx xxxx xxxx xxxx */
- operand_opcode = ENCODE (8, 4, 25);
-
- return aarch64_emit_insn (buf, opcode | operand_opcode | size
- | ENCODE (operand.imm, 12, 10)
- | ENCODE (rn.num, 5, 5)
- | ENCODE (rd.num, 5, 0));
- }
- else
- {
- /* xxx0 101x xxxx xxxx xxxx xxxx xxxx xxxx */
- operand_opcode = ENCODE (5, 4, 25);
-
- return emit_data_processing_reg (buf, opcode | operand_opcode, rd,
- rn, operand.reg);
- }
-}
-
-/* Write an ADD instruction into *BUF.
-
- ADD rd, rn, #imm
- ADD rd, rn, rm
-
- This function handles both an immediate and register add.
-
- RD is the destination register.
- RN is the input register.
- OPERAND is the source operand, either of type OPERAND_IMMEDIATE or
- OPERAND_REGISTER. */
-
-static int
-emit_add (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_operand operand)
-{
- return emit_data_processing (buf, ADD, rd, rn, operand);
-}
-
-/* Write a SUB instruction into *BUF.
-
- SUB rd, rn, #imm
- SUB rd, rn, rm
-
- This function handles both an immediate and register sub.
-
- RD is the destination register.
- RN is the input register.
- IMM is the immediate to substract to RN. */
-
-static int
-emit_sub (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_operand operand)
-{
- return emit_data_processing (buf, SUB, rd, rn, operand);
-}
-
-/* Write a MOV instruction into *BUF.
-
- MOV rd, #imm
- MOV rd, rm
-
- This function handles both a wide immediate move and a register move,
- with the condition that the source register is not xzr. xzr and the
- stack pointer share the same encoding and this function only supports
- the stack pointer.
-
- RD is the destination register.
- OPERAND is the source operand, either of type OPERAND_IMMEDIATE or
- OPERAND_REGISTER. */
-
-static int
-emit_mov (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_operand operand)
-{
- if (operand.type == OPERAND_IMMEDIATE)
- {
- uint32_t size = ENCODE (rd.is64, 1, 31);
- /* Do not shift the immediate. */
- uint32_t shift = ENCODE (0, 2, 21);
-
- return aarch64_emit_insn (buf, MOV | size | shift
- | ENCODE (operand.imm, 16, 5)
- | ENCODE (rd.num, 5, 0));
- }
- else
- return emit_add (buf, rd, operand.reg, immediate_operand (0));
-}
-
-/* Write a MOVK instruction into *BUF.
-
- MOVK rd, #imm, lsl #shift
-
- RD is the destination register.
- IMM is the immediate.
- SHIFT is the logical shift left to apply to IMM. */
-
-static int
-emit_movk (uint32_t *buf, struct aarch64_register rd, uint32_t imm,
- unsigned shift)
-{
- uint32_t size = ENCODE (rd.is64, 1, 31);
-
- return aarch64_emit_insn (buf, MOVK | size | ENCODE (shift, 2, 21) |
- ENCODE (imm, 16, 5) | ENCODE (rd.num, 5, 0));
-}
-
-/* Write instructions into *BUF in order to move ADDR into a register.
- ADDR can be a 64-bit value.
-
- This function will emit a series of MOV and MOVK instructions, such as:
-
- MOV xd, #(addr)
- MOVK xd, #(addr >> 16), lsl #16
- MOVK xd, #(addr >> 32), lsl #32
- MOVK xd, #(addr >> 48), lsl #48 */
-
-static int
-emit_mov_addr (uint32_t *buf, struct aarch64_register rd, CORE_ADDR addr)
-{
- uint32_t *p = buf;
-
- /* The MOV (wide immediate) instruction clears to top bits of the
- register. */
- p += emit_mov (p, rd, immediate_operand (addr & 0xffff));
-
- if ((addr >> 16) != 0)
- p += emit_movk (p, rd, (addr >> 16) & 0xffff, 1);
- else
- return p - buf;
-
- if ((addr >> 32) != 0)
- p += emit_movk (p, rd, (addr >> 32) & 0xffff, 2);
- else
- return p - buf;
-
- if ((addr >> 48) != 0)
- p += emit_movk (p, rd, (addr >> 48) & 0xffff, 3);
-
- return p - buf;
-}
-
-/* Write a SUBS instruction into *BUF.
-
- SUBS rd, rn, rm
-
- This instruction update the condition flags.
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_subs (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_operand operand)
-{
- return emit_data_processing (buf, SUBS, rd, rn, operand);
-}
-
-/* Write a CMP instruction into *BUF.
-
- CMP rn, rm
-
- This instruction is an alias of SUBS xzr, rn, rm.
-
- RN and RM are the registers to compare. */
-
-static int
-emit_cmp (uint32_t *buf, struct aarch64_register rn,
- struct aarch64_operand operand)
-{
- return emit_subs (buf, xzr, rn, operand);
-}
-
-/* Write a AND instruction into *BUF.
-
- AND rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_and (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, AND, rd, rn, rm);
-}
-
-/* Write a ORR instruction into *BUF.
-
- ORR rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_orr (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, ORR, rd, rn, rm);
-}
-
-/* Write a ORN instruction into *BUF.
-
- ORN rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_orn (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, ORN, rd, rn, rm);
-}
-
-/* Write a EOR instruction into *BUF.
-
- EOR rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_eor (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, EOR, rd, rn, rm);
-}
-
-/* Write a MVN instruction into *BUF.
-
- MVN rd, rm
-
- This is an alias for ORN rd, xzr, rm.
-
- RD is the destination register.
- RM is the source register. */
-
-static int
-emit_mvn (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rm)
-{
- return emit_orn (buf, rd, xzr, rm);
-}
-
-/* Write a LSLV instruction into *BUF.
-
- LSLV rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_lslv (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, LSLV, rd, rn, rm);
-}
-
-/* Write a LSRV instruction into *BUF.
-
- LSRV rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_lsrv (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, LSRV, rd, rn, rm);
-}
-
-/* Write a ASRV instruction into *BUF.
-
- ASRV rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_asrv (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, ASRV, rd, rn, rm);
-}
-
-/* Write a MUL instruction into *BUF.
-
- MUL rd, rn, rm
-
- RD is the destination register.
- RN and RM are the source registers. */
-
-static int
-emit_mul (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm)
-{
- return emit_data_processing_reg (buf, MUL, rd, rn, rm);
-}
-
-/* Write a MRS instruction into *BUF. The register size is 64-bit.
-
- MRS xt, system_reg
-
- RT is the destination register.
- SYSTEM_REG is special purpose register to read. */
-
-static int
-emit_mrs (uint32_t *buf, struct aarch64_register rt,
- enum aarch64_system_control_registers system_reg)
-{
- return aarch64_emit_insn (buf, MRS | ENCODE (system_reg, 15, 5)
- | ENCODE (rt.num, 5, 0));
-}
-
-/* Write a MSR instruction into *BUF. The register size is 64-bit.
-
- MSR system_reg, xt
-
- SYSTEM_REG is special purpose register to write.
- RT is the input register. */
-
-static int
-emit_msr (uint32_t *buf, enum aarch64_system_control_registers system_reg,
- struct aarch64_register rt)
-{
- return aarch64_emit_insn (buf, MSR | ENCODE (system_reg, 15, 5)
- | ENCODE (rt.num, 5, 0));
-}
-
-/* Write a SEVL instruction into *BUF.
-
- This is a hint instruction telling the hardware to trigger an event. */
-
-static int
-emit_sevl (uint32_t *buf)
-{
- return aarch64_emit_insn (buf, SEVL);
-}
-
-/* Write a WFE instruction into *BUF.
-
- This is a hint instruction telling the hardware to wait for an event. */
-
-static int
-emit_wfe (uint32_t *buf)
-{
- return aarch64_emit_insn (buf, WFE);
-}
-
-/* Write a SBFM instruction into *BUF.
-
- SBFM rd, rn, #immr, #imms
-
- This instruction moves the bits from #immr to #imms into the
- destination, sign extending the result.
-
- RD is the destination register.
- RN is the source register.
- IMMR is the bit number to start at (least significant bit).
- IMMS is the bit number to stop at (most significant bit). */
-
-static int
-emit_sbfm (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, uint32_t immr, uint32_t imms)
-{
- uint32_t size = ENCODE (rd.is64, 1, 31);
- uint32_t n = ENCODE (rd.is64, 1, 22);
-
- return aarch64_emit_insn (buf, SBFM | size | n | ENCODE (immr, 6, 16)
- | ENCODE (imms, 6, 10) | ENCODE (rn.num, 5, 5)
- | ENCODE (rd.num, 5, 0));
-}
-
-/* Write a SBFX instruction into *BUF.
-
- SBFX rd, rn, #lsb, #width
-
- This instruction moves #width bits from #lsb into the destination, sign
- extending the result. This is an alias for:
-
- SBFM rd, rn, #lsb, #(lsb + width - 1)
-
- RD is the destination register.
- RN is the source register.
- LSB is the bit number to start at (least significant bit).
- WIDTH is the number of bits to move. */
-
-static int
-emit_sbfx (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, uint32_t lsb, uint32_t width)
-{
- return emit_sbfm (buf, rd, rn, lsb, lsb + width - 1);
-}
-
-/* Write a UBFM instruction into *BUF.
-
- UBFM rd, rn, #immr, #imms
-
- This instruction moves the bits from #immr to #imms into the
- destination, extending the result with zeros.
-
- RD is the destination register.
- RN is the source register.
- IMMR is the bit number to start at (least significant bit).
- IMMS is the bit number to stop at (most significant bit). */
-
-static int
-emit_ubfm (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, uint32_t immr, uint32_t imms)
-{
- uint32_t size = ENCODE (rd.is64, 1, 31);
- uint32_t n = ENCODE (rd.is64, 1, 22);
-
- return aarch64_emit_insn (buf, UBFM | size | n | ENCODE (immr, 6, 16)
- | ENCODE (imms, 6, 10) | ENCODE (rn.num, 5, 5)
- | ENCODE (rd.num, 5, 0));
-}
-
-/* Write a UBFX instruction into *BUF.
-
- UBFX rd, rn, #lsb, #width
-
- This instruction moves #width bits from #lsb into the destination,
- extending the result with zeros. This is an alias for:
-
- UBFM rd, rn, #lsb, #(lsb + width - 1)
-
- RD is the destination register.
- RN is the source register.
- LSB is the bit number to start at (least significant bit).
- WIDTH is the number of bits to move. */
-
-static int
-emit_ubfx (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, uint32_t lsb, uint32_t width)
-{
- return emit_ubfm (buf, rd, rn, lsb, lsb + width - 1);
-}
-
-/* Write a CSINC instruction into *BUF.
-
- CSINC rd, rn, rm, cond
-
- This instruction conditionally increments rn or rm and places the result
- in rd. rn is chosen is the condition is true.
-
- RD is the destination register.
- RN and RM are the source registers.
- COND is the encoded condition. */
-
-static int
-emit_csinc (uint32_t *buf, struct aarch64_register rd,
- struct aarch64_register rn, struct aarch64_register rm,
- unsigned cond)
-{
- uint32_t size = ENCODE (rd.is64, 1, 31);
-
- return aarch64_emit_insn (buf, CSINC | size | ENCODE (rm.num, 5, 16)
- | ENCODE (cond, 4, 12) | ENCODE (rn.num, 5, 5)
- | ENCODE (rd.num, 5, 0));
-}
-
-/* Write a CSET instruction into *BUF.
-
- CSET rd, cond
-
- This instruction conditionally write 1 or 0 in the destination register.
- 1 is written if the condition is true. This is an alias for:
-
- CSINC rd, xzr, xzr, !cond
-
- Note that the condition needs to be inverted.
-
- RD is the destination register.
- RN and RM are the source registers.
- COND is the encoded condition. */
-
-static int
-emit_cset (uint32_t *buf, struct aarch64_register rd, unsigned cond)
-{
- /* The least significant bit of the condition needs toggling in order to
- invert it. */
- return emit_csinc (buf, rd, xzr, xzr, cond ^ 0x1);
-}
-
-/* Write LEN instructions from BUF into the inferior memory at *TO.
-
- Note instructions are always little endian on AArch64, unlike data. */
-
-static void
-append_insns (CORE_ADDR *to, size_t len, const uint32_t *buf)
-{
- size_t byte_len = len * sizeof (uint32_t);
-#if (__BYTE_ORDER == __BIG_ENDIAN)
- uint32_t *le_buf = (uint32_t *) xmalloc (byte_len);
- size_t i;
-
- for (i = 0; i < len; i++)
- le_buf[i] = htole32 (buf[i]);
-
- target_write_memory (*to, (const unsigned char *) le_buf, byte_len);
-
- xfree (le_buf);
-#else
- target_write_memory (*to, (const unsigned char *) buf, byte_len);
-#endif
-
- *to += byte_len;
-}
-
-/* Sub-class of struct aarch64_insn_data, store information of
- instruction relocation for fast tracepoint. Visitor can
- relocate an instruction from BASE.INSN_ADDR to NEW_ADDR and save
- the relocated instructions in buffer pointed by INSN_PTR. */
-
-struct aarch64_insn_relocation_data
-{
- struct aarch64_insn_data base;
-
- /* The new address the instruction is relocated to. */
- CORE_ADDR new_addr;
- /* Pointer to the buffer of relocated instruction(s). */
- uint32_t *insn_ptr;
-};
-
-/* Implementation of aarch64_insn_visitor method "b". */
-
-static void
-aarch64_ftrace_insn_reloc_b (const int is_bl, const int32_t offset,
- struct aarch64_insn_data *data)
-{
- struct aarch64_insn_relocation_data *insn_reloc
- = (struct aarch64_insn_relocation_data *) data;
- int64_t new_offset
- = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
-
- if (can_encode_int32 (new_offset, 28))
- insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, is_bl, new_offset);
-}
-
-/* Implementation of aarch64_insn_visitor method "b_cond". */
-
-static void
-aarch64_ftrace_insn_reloc_b_cond (const unsigned cond, const int32_t offset,
- struct aarch64_insn_data *data)
-{
- struct aarch64_insn_relocation_data *insn_reloc
- = (struct aarch64_insn_relocation_data *) data;
- int64_t new_offset
- = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
-
- if (can_encode_int32 (new_offset, 21))
- {
- insn_reloc->insn_ptr += emit_bcond (insn_reloc->insn_ptr, cond,
- new_offset);
- }
- else if (can_encode_int32 (new_offset, 28))
- {
- /* The offset is out of range for a conditional branch
- instruction but not for a unconditional branch. We can use
- the following instructions instead:
-
- B.COND TAKEN ; If cond is true, then jump to TAKEN.
- B NOT_TAKEN ; Else jump over TAKEN and continue.
- TAKEN:
- B #(offset - 8)
- NOT_TAKEN:
-
- */
-
- insn_reloc->insn_ptr += emit_bcond (insn_reloc->insn_ptr, cond, 8);
- insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
- insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, new_offset - 8);
- }
-}
-
-/* Implementation of aarch64_insn_visitor method "cb". */
-
-static void
-aarch64_ftrace_insn_reloc_cb (const int32_t offset, const int is_cbnz,
- const unsigned rn, int is64,
- struct aarch64_insn_data *data)
-{
- struct aarch64_insn_relocation_data *insn_reloc
- = (struct aarch64_insn_relocation_data *) data;
- int64_t new_offset
- = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
-
- if (can_encode_int32 (new_offset, 21))
- {
- insn_reloc->insn_ptr += emit_cb (insn_reloc->insn_ptr, is_cbnz,
- aarch64_register (rn, is64), new_offset);
- }
- else if (can_encode_int32 (new_offset, 28))
- {
- /* The offset is out of range for a compare and branch
- instruction but not for a unconditional branch. We can use
- the following instructions instead:
-
- CBZ xn, TAKEN ; xn == 0, then jump to TAKEN.
- B NOT_TAKEN ; Else jump over TAKEN and continue.
- TAKEN:
- B #(offset - 8)
- NOT_TAKEN:
-
- */
- insn_reloc->insn_ptr += emit_cb (insn_reloc->insn_ptr, is_cbnz,
- aarch64_register (rn, is64), 8);
- insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
- insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, new_offset - 8);
- }
-}
-
-/* Implementation of aarch64_insn_visitor method "tb". */
-
-static void
-aarch64_ftrace_insn_reloc_tb (const int32_t offset, int is_tbnz,
- const unsigned rt, unsigned bit,
- struct aarch64_insn_data *data)
-{
- struct aarch64_insn_relocation_data *insn_reloc
- = (struct aarch64_insn_relocation_data *) data;
- int64_t new_offset
- = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
-
- if (can_encode_int32 (new_offset, 16))
- {
- insn_reloc->insn_ptr += emit_tb (insn_reloc->insn_ptr, is_tbnz, bit,
- aarch64_register (rt, 1), new_offset);
- }
- else if (can_encode_int32 (new_offset, 28))
- {
- /* The offset is out of range for a test bit and branch
- instruction but not for a unconditional branch. We can use
- the following instructions instead:
-
- TBZ xn, #bit, TAKEN ; xn[bit] == 0, then jump to TAKEN.
- B NOT_TAKEN ; Else jump over TAKEN and continue.
- TAKEN:
- B #(offset - 8)
- NOT_TAKEN:
-
- */
- insn_reloc->insn_ptr += emit_tb (insn_reloc->insn_ptr, is_tbnz, bit,
- aarch64_register (rt, 1), 8);
- insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
- insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0,
- new_offset - 8);
- }
-}
-
-/* Implementation of aarch64_insn_visitor method "adr". */
-
-static void
-aarch64_ftrace_insn_reloc_adr (const int32_t offset, const unsigned rd,
- const int is_adrp,
- struct aarch64_insn_data *data)
-{
- struct aarch64_insn_relocation_data *insn_reloc
- = (struct aarch64_insn_relocation_data *) data;
- /* We know exactly the address the ADR{P,} instruction will compute.
- We can just write it to the destination register. */
- CORE_ADDR address = data->insn_addr + offset;
-
- if (is_adrp)
- {
- /* Clear the lower 12 bits of the offset to get the 4K page. */
- insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
- aarch64_register (rd, 1),
- address & ~0xfff);
- }
- else
- insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
- aarch64_register (rd, 1), address);
-}
-
-/* Implementation of aarch64_insn_visitor method "ldr_literal". */
-
-static void
-aarch64_ftrace_insn_reloc_ldr_literal (const int32_t offset, const int is_sw,
- const unsigned rt, const int is64,
- struct aarch64_insn_data *data)
-{
- struct aarch64_insn_relocation_data *insn_reloc
- = (struct aarch64_insn_relocation_data *) data;
- CORE_ADDR address = data->insn_addr + offset;
-
- insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
- aarch64_register (rt, 1), address);
-
- /* We know exactly what address to load from, and what register we
- can use:
-
- MOV xd, #(oldloc + offset)
- MOVK xd, #((oldloc + offset) >> 16), lsl #16
- ...
-
- LDR xd, [xd] ; or LDRSW xd, [xd]
-
- */
-
- if (is_sw)
- insn_reloc->insn_ptr += emit_ldrsw (insn_reloc->insn_ptr,
- aarch64_register (rt, 1),
- aarch64_register (rt, 1),
- offset_memory_operand (0));
- else
- insn_reloc->insn_ptr += emit_ldr (insn_reloc->insn_ptr,
- aarch64_register (rt, is64),
- aarch64_register (rt, 1),
- offset_memory_operand (0));
-}
-
-/* Implementation of aarch64_insn_visitor method "others". */
-
-static void
-aarch64_ftrace_insn_reloc_others (const uint32_t insn,
- struct aarch64_insn_data *data)
-{
- struct aarch64_insn_relocation_data *insn_reloc
- = (struct aarch64_insn_relocation_data *) data;
-
- /* The instruction is not PC relative. Just re-emit it at the new
- location. */
- insn_reloc->insn_ptr += aarch64_emit_insn (insn_reloc->insn_ptr, insn);
-}
-
-static const struct aarch64_insn_visitor visitor =
-{
- aarch64_ftrace_insn_reloc_b,
- aarch64_ftrace_insn_reloc_b_cond,
- aarch64_ftrace_insn_reloc_cb,
- aarch64_ftrace_insn_reloc_tb,
- aarch64_ftrace_insn_reloc_adr,
- aarch64_ftrace_insn_reloc_ldr_literal,
- aarch64_ftrace_insn_reloc_others,
-};
-
-/* Implementation of linux_target_ops method
- "install_fast_tracepoint_jump_pad". */
-
-static int
-aarch64_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;
- int64_t offset;
- int i;
- uint32_t insn;
- CORE_ADDR buildaddr = *jump_entry;
- struct aarch64_insn_relocation_data insn_data;
-
- /* We need to save the current state on the stack both to restore it
- later and to collect register values when the tracepoint is hit.
-
- The saved registers are pushed in a layout that needs to be in sync
- with aarch64_ft_collect_regmap (see linux-aarch64-ipa.c). Later on
- the supply_fast_tracepoint_registers function will fill in the
- register cache from a pointer to saved registers on the stack we build
- here.
-
- For simplicity, we set the size of each cell on the stack to 16 bytes.
- This way one cell can hold any register type, from system registers
- to the 128 bit SIMD&FP registers. Furthermore, the stack pointer
- has to be 16 bytes aligned anyway.
-
- Note that the CPSR register does not exist on AArch64. Instead we
- can access system bits describing the process state with the
- MRS/MSR instructions, namely the condition flags. We save them as
- if they are part of a CPSR register because that's how GDB
- interprets these system bits. At the moment, only the condition
- flags are saved in CPSR (NZCV).
-
- Stack layout, each cell is 16 bytes (descending):
-
- High *-------- SIMD&FP registers from 31 down to 0. --------*
- | q31 |
- . .
- . . 32 cells
- . .
- | q0 |
- *---- General purpose registers from 30 down to 0. ----*
- | x30 |
- . .
- . . 31 cells
- . .
- | x0 |
- *------------- Special purpose registers. -------------*
- | SP |
- | PC |
- | CPSR (NZCV) | 5 cells
- | FPSR |
- | FPCR | <- SP + 16
- *------------- collecting_t object --------------------*
- | TPIDR_EL0 | struct tracepoint * |
- Low *------------------------------------------------------*
-
- After this stack is set up, we issue a call to the collector, passing
- it the saved registers at (SP + 16). */
-
- /* Push SIMD&FP registers on the stack:
-
- SUB sp, sp, #(32 * 16)
-
- STP q30, q31, [sp, #(30 * 16)]
- ...
- STP q0, q1, [sp]
-
- */
- p += emit_sub (p, sp, sp, immediate_operand (32 * 16));
- for (i = 30; i >= 0; i -= 2)
- p += emit_stp_q_offset (p, i, i + 1, sp, i * 16);
-
- /* Push general purpose registers on the stack. Note that we do not need
- to push x31 as it represents the xzr register and not the stack
- pointer in a STR instruction.
-
- SUB sp, sp, #(31 * 16)
-
- STR x30, [sp, #(30 * 16)]
- ...
- STR x0, [sp]
-
- */
- p += emit_sub (p, sp, sp, immediate_operand (31 * 16));
- for (i = 30; i >= 0; i -= 1)
- p += emit_str (p, aarch64_register (i, 1), sp,
- offset_memory_operand (i * 16));
-
- /* Make space for 5 more cells.
-
- SUB sp, sp, #(5 * 16)
-
- */
- p += emit_sub (p, sp, sp, immediate_operand (5 * 16));
-
-
- /* Save SP:
-
- ADD x4, sp, #((32 + 31 + 5) * 16)
- STR x4, [sp, #(4 * 16)]
-
- */
- p += emit_add (p, x4, sp, immediate_operand ((32 + 31 + 5) * 16));
- p += emit_str (p, x4, sp, offset_memory_operand (4 * 16));
-
- /* Save PC (tracepoint address):
-
- MOV x3, #(tpaddr)
- ...
-
- STR x3, [sp, #(3 * 16)]
-
- */
-
- p += emit_mov_addr (p, x3, tpaddr);
- p += emit_str (p, x3, sp, offset_memory_operand (3 * 16));
-
- /* Save CPSR (NZCV), FPSR and FPCR:
-
- MRS x2, nzcv
- MRS x1, fpsr
- MRS x0, fpcr
-
- STR x2, [sp, #(2 * 16)]
- STR x1, [sp, #(1 * 16)]
- STR x0, [sp, #(0 * 16)]
-
- */
- p += emit_mrs (p, x2, NZCV);
- p += emit_mrs (p, x1, FPSR);
- p += emit_mrs (p, x0, FPCR);
- p += emit_str (p, x2, sp, offset_memory_operand (2 * 16));
- p += emit_str (p, x1, sp, offset_memory_operand (1 * 16));
- p += emit_str (p, x0, sp, offset_memory_operand (0 * 16));
-
- /* Push the collecting_t object. It consist of the address of the
- tracepoint and an ID for the current thread. We get the latter by
- reading the tpidr_el0 system register. It corresponds to the
- NT_ARM_TLS register accessible with ptrace.
-
- MOV x0, #(tpoint)
- ...
-
- MRS x1, tpidr_el0
-
- STP x0, x1, [sp, #-16]!
-
- */
-
- p += emit_mov_addr (p, x0, tpoint);
- p += emit_mrs (p, x1, TPIDR_EL0);
- p += emit_stp (p, x0, x1, sp, preindex_memory_operand (-16));
-
- /* Spin-lock:
-
- The shared memory for the lock is at lockaddr. It will hold zero
- if no-one is holding the lock, otherwise it contains the address of
- the collecting_t object on the stack of the thread which acquired it.
-
- At this stage, the stack pointer points to this thread's collecting_t
- object.
-
- We use the following registers:
- - x0: Address of the lock.
- - x1: Pointer to collecting_t object.
- - x2: Scratch register.
-
- MOV x0, #(lockaddr)
- ...
- MOV x1, sp
-
- ; Trigger an event local to this core. So the following WFE
- ; instruction is ignored.
- SEVL
- again:
- ; Wait for an event. The event is triggered by either the SEVL
- ; or STLR instructions (store release).
- WFE
-
- ; Atomically read at lockaddr. This marks the memory location as
- ; exclusive. This instruction also has memory constraints which
- ; make sure all previous data reads and writes are done before
- ; executing it.
- LDAXR x2, [x0]
-
- ; Try again if another thread holds the lock.
- CBNZ x2, again
-
- ; We can lock it! Write the address of the collecting_t object.
- ; This instruction will fail if the memory location is not marked
- ; as exclusive anymore. If it succeeds, it will remove the
- ; exclusive mark on the memory location. This way, if another
- ; thread executes this instruction before us, we will fail and try
- ; all over again.
- STXR w2, x1, [x0]
- CBNZ w2, again
-
- */
-
- p += emit_mov_addr (p, x0, lockaddr);
- p += emit_mov (p, x1, register_operand (sp));
-
- p += emit_sevl (p);
- p += emit_wfe (p);
- p += emit_ldaxr (p, x2, x0);
- p += emit_cb (p, 1, w2, -2 * 4);
- p += emit_stxr (p, w2, x1, x0);
- p += emit_cb (p, 1, x2, -4 * 4);
-
- /* Call collector (struct tracepoint *, unsigned char *):
-
- MOV x0, #(tpoint)
- ...
-
- ; Saved registers start after the collecting_t object.
- ADD x1, sp, #16
-
- ; We use an intra-procedure-call scratch register.
- MOV ip0, #(collector)
- ...
-
- ; And call back to C!
- BLR ip0
-
- */
-
- p += emit_mov_addr (p, x0, tpoint);
- p += emit_add (p, x1, sp, immediate_operand (16));
-
- p += emit_mov_addr (p, ip0, collector);
- p += emit_blr (p, ip0);
-
- /* Release the lock.
-
- MOV x0, #(lockaddr)
- ...
-
- ; This instruction is a normal store with memory ordering
- ; constraints. Thanks to this we do not have to put a data
- ; barrier instruction to make sure all data read and writes are done
- ; before this instruction is executed. Furthermore, this instruction
- ; will trigger an event, letting other threads know they can grab
- ; the lock.
- STLR xzr, [x0]
-
- */
- p += emit_mov_addr (p, x0, lockaddr);
- p += emit_stlr (p, xzr, x0);
-
- /* Free collecting_t object:
-
- ADD sp, sp, #16
-
- */
- p += emit_add (p, sp, sp, immediate_operand (16));
-
- /* Restore CPSR (NZCV), FPSR and FPCR. And free all special purpose
- registers from the stack.
-
- LDR x2, [sp, #(2 * 16)]
- LDR x1, [sp, #(1 * 16)]
- LDR x0, [sp, #(0 * 16)]
-
- MSR NZCV, x2
- MSR FPSR, x1
- MSR FPCR, x0
-
- ADD sp, sp #(5 * 16)
-
- */
- p += emit_ldr (p, x2, sp, offset_memory_operand (2 * 16));
- p += emit_ldr (p, x1, sp, offset_memory_operand (1 * 16));
- p += emit_ldr (p, x0, sp, offset_memory_operand (0 * 16));
- p += emit_msr (p, NZCV, x2);
- p += emit_msr (p, FPSR, x1);
- p += emit_msr (p, FPCR, x0);
-
- p += emit_add (p, sp, sp, immediate_operand (5 * 16));
-
- /* Pop general purpose registers:
-
- LDR x0, [sp]
- ...
- LDR x30, [sp, #(30 * 16)]
-
- ADD sp, sp, #(31 * 16)
-
- */
- for (i = 0; i <= 30; i += 1)
- p += emit_ldr (p, aarch64_register (i, 1), sp,
- offset_memory_operand (i * 16));
- p += emit_add (p, sp, sp, immediate_operand (31 * 16));
-
- /* Pop SIMD&FP registers:
-
- LDP q0, q1, [sp]
- ...
- LDP q30, q31, [sp, #(30 * 16)]
-
- ADD sp, sp, #(32 * 16)
-
- */
- for (i = 0; i <= 30; i += 2)
- p += emit_ldp_q_offset (p, i, i + 1, sp, i * 16);
- p += emit_add (p, sp, sp, immediate_operand (32 * 16));
-
- /* Write the code into the inferior memory. */
- append_insns (&buildaddr, p - buf, buf);
-
- /* Now emit the relocated instruction. */
- *adjusted_insn_addr = buildaddr;
- target_read_uint32 (tpaddr, &insn);
-
- insn_data.base.insn_addr = tpaddr;
- insn_data.new_addr = buildaddr;
- insn_data.insn_ptr = buf;
-
- aarch64_relocate_instruction (insn, &visitor,
- (struct aarch64_insn_data *) &insn_data);
-
- /* We may not have been able to relocate the instruction. */
- if (insn_data.insn_ptr == buf)
- {
- sprintf (err,
- "E.Could not relocate instruction from %s to %s.",
- core_addr_to_string_nz (tpaddr),
- core_addr_to_string_nz (buildaddr));
- return 1;
- }
- else
- append_insns (&buildaddr, insn_data.insn_ptr - buf, buf);
- *adjusted_insn_addr_end = buildaddr;
-
- /* Go back to the start of the buffer. */
- p = buf;
-
- /* Emit a branch back from the jump pad. */
- offset = (tpaddr + orig_size - buildaddr);
- if (!can_encode_int32 (offset, 28))
- {
- sprintf (err,
- "E.Jump back from jump pad too far from tracepoint "
- "(offset 0x%" PRIx64 " cannot be encoded in 28 bits).",
- offset);
- return 1;
- }
-
- p += emit_b (p, 0, offset);
- append_insns (&buildaddr, p - buf, buf);
-
- /* Give the caller a branch instruction into the jump pad. */
- offset = (*jump_entry - tpaddr);
- if (!can_encode_int32 (offset, 28))
- {
- sprintf (err,
- "E.Jump pad too far from tracepoint "
- "(offset 0x%" PRIx64 " cannot be encoded in 28 bits).",
- offset);
- return 1;
- }
-
- emit_b ((uint32_t *) jjump_pad_insn, 0, offset);
- *jjump_pad_insn_size = 4;
-
- /* Return the end address of our pad. */
- *jump_entry = buildaddr;
-
- return 0;
-}
-
-/* Helper function writing LEN instructions from START into
- current_insn_ptr. */
-
-static void
-emit_ops_insns (const uint32_t *start, int len)
-{
- CORE_ADDR buildaddr = current_insn_ptr;
-
- if (debug_threads)
- debug_printf ("Adding %d instrucions at %s\n",
- len, paddress (buildaddr));
-
- append_insns (&buildaddr, len, start);
- current_insn_ptr = buildaddr;
-}
-
-/* Pop a register from the stack. */
-
-static int
-emit_pop (uint32_t *buf, struct aarch64_register rt)
-{
- return emit_ldr (buf, rt, sp, postindex_memory_operand (1 * 16));
-}
-
-/* Push a register on the stack. */
-
-static int
-emit_push (uint32_t *buf, struct aarch64_register rt)
-{
- return emit_str (buf, rt, sp, preindex_memory_operand (-1 * 16));
-}
-
-/* Implementation of emit_ops method "emit_prologue". */
-
-static void
-aarch64_emit_prologue (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- /* This function emit a prologue for the following function prototype:
-
- enum eval_result_type f (unsigned char *regs,
- ULONGEST *value);
-
- The first argument is a buffer of raw registers. The second
- argument is the result of
- evaluating the expression, which will be set to whatever is on top of
- the stack at the end.
-
- The stack set up by the prologue is as such:
-
- High *------------------------------------------------------*
- | LR |
- | FP | <- FP
- | x1 (ULONGEST *value) |
- | x0 (unsigned char *regs) |
- Low *------------------------------------------------------*
-
- As we are implementing a stack machine, each opcode can expand the
- stack so we never know how far we are from the data saved by this
- prologue. In order to be able refer to value and regs later, we save
- the current stack pointer in the frame pointer. This way, it is not
- clobbered when calling C functions.
-
- Finally, throughout every operation, we are using register x0 as the
- top of the stack, and x1 as a scratch register. */
-
- p += emit_stp (p, x0, x1, sp, preindex_memory_operand (-2 * 16));
- p += emit_str (p, lr, sp, offset_memory_operand (3 * 8));
- p += emit_str (p, fp, sp, offset_memory_operand (2 * 8));
-
- p += emit_add (p, fp, sp, immediate_operand (2 * 8));
-
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_epilogue". */
-
-static void
-aarch64_emit_epilogue (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- /* Store the result of the expression (x0) in *value. */
- p += emit_sub (p, x1, fp, immediate_operand (1 * 8));
- p += emit_ldr (p, x1, x1, offset_memory_operand (0));
- p += emit_str (p, x0, x1, offset_memory_operand (0));
-
- /* Restore the previous state. */
- p += emit_add (p, sp, fp, immediate_operand (2 * 8));
- p += emit_ldp (p, fp, lr, fp, offset_memory_operand (0));
-
- /* Return expr_eval_no_error. */
- p += emit_mov (p, x0, immediate_operand (expr_eval_no_error));
- p += emit_ret (p, lr);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_add". */
-
-static void
-aarch64_emit_add (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_add (p, x0, x1, register_operand (x0));
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_sub". */
-
-static void
-aarch64_emit_sub (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_sub (p, x0, x1, register_operand (x0));
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_mul". */
-
-static void
-aarch64_emit_mul (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_mul (p, x0, x1, x0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_lsh". */
-
-static void
-aarch64_emit_lsh (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_lslv (p, x0, x1, x0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_rsh_signed". */
-
-static void
-aarch64_emit_rsh_signed (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_asrv (p, x0, x1, x0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_rsh_unsigned". */
-
-static void
-aarch64_emit_rsh_unsigned (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_lsrv (p, x0, x1, x0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_ext". */
-
-static void
-aarch64_emit_ext (int arg)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_sbfx (p, x0, x0, 0, arg);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_log_not". */
-
-static void
-aarch64_emit_log_not (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- /* If the top of the stack is 0, replace it with 1. Else replace it with
- 0. */
-
- p += emit_cmp (p, x0, immediate_operand (0));
- p += emit_cset (p, x0, EQ);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_bit_and". */
-
-static void
-aarch64_emit_bit_and (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_and (p, x0, x0, x1);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_bit_or". */
-
-static void
-aarch64_emit_bit_or (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_orr (p, x0, x0, x1);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_bit_xor". */
-
-static void
-aarch64_emit_bit_xor (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_eor (p, x0, x0, x1);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_bit_not". */
-
-static void
-aarch64_emit_bit_not (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_mvn (p, x0, x0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_equal". */
-
-static void
-aarch64_emit_equal (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x0, register_operand (x1));
- p += emit_cset (p, x0, EQ);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_less_signed". */
-
-static void
-aarch64_emit_less_signed (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- p += emit_cset (p, x0, LT);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_less_unsigned". */
-
-static void
-aarch64_emit_less_unsigned (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- p += emit_cset (p, x0, LO);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_ref". */
-
-static void
-aarch64_emit_ref (int size)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- switch (size)
- {
- case 1:
- p += emit_ldrb (p, w0, x0, offset_memory_operand (0));
- break;
- case 2:
- p += emit_ldrh (p, w0, x0, offset_memory_operand (0));
- break;
- case 4:
- p += emit_ldr (p, w0, x0, offset_memory_operand (0));
- break;
- case 8:
- p += emit_ldr (p, x0, x0, offset_memory_operand (0));
- break;
- default:
- /* Unknown size, bail on compilation. */
- emit_error = 1;
- break;
- }
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_if_goto". */
-
-static void
-aarch64_emit_if_goto (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- /* The Z flag is set or cleared here. */
- p += emit_cmp (p, x0, immediate_operand (0));
- /* This instruction must not change the Z flag. */
- p += emit_pop (p, x0);
- /* Branch over the next instruction if x0 == 0. */
- p += emit_bcond (p, EQ, 8);
-
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = (p - buf) * 4;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_goto". */
-
-static void
-aarch64_emit_goto (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = 0;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "write_goto_address". */
-
-static void
-aarch64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
-{
- uint32_t insn;
-
- emit_b (&insn, 0, to - from);
- append_insns (&from, 1, &insn);
-}
-
-/* Implementation of emit_ops method "emit_const". */
-
-static void
-aarch64_emit_const (LONGEST num)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_mov_addr (p, x0, num);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_call". */
-
-static void
-aarch64_emit_call (CORE_ADDR fn)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_mov_addr (p, ip0, fn);
- p += emit_blr (p, ip0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_reg". */
-
-static void
-aarch64_emit_reg (int reg)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- /* Set x0 to unsigned char *regs. */
- p += emit_sub (p, x0, fp, immediate_operand (2 * 8));
- p += emit_ldr (p, x0, x0, offset_memory_operand (0));
- p += emit_mov (p, x1, immediate_operand (reg));
-
- emit_ops_insns (buf, p - buf);
-
- aarch64_emit_call (get_raw_reg_func_addr ());
-}
-
-/* Implementation of emit_ops method "emit_pop". */
-
-static void
-aarch64_emit_pop (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_stack_flush". */
-
-static void
-aarch64_emit_stack_flush (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_push (p, x0);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_zero_ext". */
-
-static void
-aarch64_emit_zero_ext (int arg)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_ubfx (p, x0, x0, 0, arg);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_swap". */
-
-static void
-aarch64_emit_swap (void)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_ldr (p, x1, sp, offset_memory_operand (0 * 16));
- p += emit_str (p, x0, sp, offset_memory_operand (0 * 16));
- p += emit_mov (p, x0, register_operand (x1));
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_stack_adjust". */
-
-static void
-aarch64_emit_stack_adjust (int n)
-{
- /* This is not needed with our design. */
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_add (p, sp, sp, immediate_operand (n * 16));
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_int_call_1". */
-
-static void
-aarch64_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_mov (p, x0, immediate_operand (arg1));
-
- emit_ops_insns (buf, p - buf);
-
- aarch64_emit_call (fn);
-}
-
-/* Implementation of emit_ops method "emit_void_call_2". */
-
-static void
-aarch64_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- /* Push x0 on the stack. */
- aarch64_emit_stack_flush ();
-
- /* Setup arguments for the function call:
-
- x0: arg1
- x1: top of the stack
-
- MOV x1, x0
- MOV x0, #arg1 */
-
- p += emit_mov (p, x1, register_operand (x0));
- p += emit_mov (p, x0, immediate_operand (arg1));
-
- emit_ops_insns (buf, p - buf);
-
- aarch64_emit_call (fn);
-
- /* Restore x0. */
- aarch64_emit_pop ();
-}
-
-/* Implementation of emit_ops method "emit_eq_goto". */
-
-static void
-aarch64_emit_eq_goto (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- /* Branch over the next instruction if x0 != x1. */
- p += emit_bcond (p, NE, 8);
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = (p - buf) * 4;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_ne_goto". */
-
-static void
-aarch64_emit_ne_goto (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- /* Branch over the next instruction if x0 == x1. */
- p += emit_bcond (p, EQ, 8);
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = (p - buf) * 4;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_lt_goto". */
-
-static void
-aarch64_emit_lt_goto (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- /* Branch over the next instruction if x0 >= x1. */
- p += emit_bcond (p, GE, 8);
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = (p - buf) * 4;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_le_goto". */
-
-static void
-aarch64_emit_le_goto (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- /* Branch over the next instruction if x0 > x1. */
- p += emit_bcond (p, GT, 8);
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = (p - buf) * 4;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_gt_goto". */
-
-static void
-aarch64_emit_gt_goto (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- /* Branch over the next instruction if x0 <= x1. */
- p += emit_bcond (p, LE, 8);
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = (p - buf) * 4;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-/* Implementation of emit_ops method "emit_ge_got". */
-
-static void
-aarch64_emit_ge_got (int *offset_p, int *size_p)
-{
- uint32_t buf[16];
- uint32_t *p = buf;
-
- p += emit_pop (p, x1);
- p += emit_cmp (p, x1, register_operand (x0));
- /* Branch over the next instruction if x0 <= x1. */
- p += emit_bcond (p, LT, 8);
- /* The NOP instruction will be patched with an unconditional branch. */
- if (offset_p)
- *offset_p = (p - buf) * 4;
- if (size_p)
- *size_p = 4;
- p += emit_nop (p);
-
- emit_ops_insns (buf, p - buf);
-}
-
-static struct emit_ops aarch64_emit_ops_impl =
-{
- aarch64_emit_prologue,
- aarch64_emit_epilogue,
- aarch64_emit_add,
- aarch64_emit_sub,
- aarch64_emit_mul,
- aarch64_emit_lsh,
- aarch64_emit_rsh_signed,
- aarch64_emit_rsh_unsigned,
- aarch64_emit_ext,
- aarch64_emit_log_not,
- aarch64_emit_bit_and,
- aarch64_emit_bit_or,
- aarch64_emit_bit_xor,
- aarch64_emit_bit_not,
- aarch64_emit_equal,
- aarch64_emit_less_signed,
- aarch64_emit_less_unsigned,
- aarch64_emit_ref,
- aarch64_emit_if_goto,
- aarch64_emit_goto,
- aarch64_write_goto_address,
- aarch64_emit_const,
- aarch64_emit_call,
- aarch64_emit_reg,
- aarch64_emit_pop,
- aarch64_emit_stack_flush,
- aarch64_emit_zero_ext,
- aarch64_emit_swap,
- aarch64_emit_stack_adjust,
- aarch64_emit_int_call_1,
- aarch64_emit_void_call_2,
- aarch64_emit_eq_goto,
- aarch64_emit_ne_goto,
- aarch64_emit_lt_goto,
- aarch64_emit_le_goto,
- aarch64_emit_gt_goto,
- aarch64_emit_ge_got,
-};
-
-/* Implementation of linux_target_ops method "emit_ops". */
-
-static struct emit_ops *
-aarch64_emit_ops (void)
-{
- return &aarch64_emit_ops_impl;
-}
-
-/* Implementation of linux_target_ops method
- "get_min_fast_tracepoint_insn_len". */
-
-static int
-aarch64_get_min_fast_tracepoint_insn_len (void)
-{
- return 4;
-}
-
-/* Implementation of linux_target_ops method "supports_range_stepping". */
-
-static int
-aarch64_supports_range_stepping (void)
-{
- return 1;
-}
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-aarch64_sw_breakpoint_from_kind (int kind, int *size)
-{
- if (is_64bit_tdesc ())
- {
- *size = aarch64_breakpoint_len;
- return aarch64_breakpoint;
- }
- else
- return arm_sw_breakpoint_from_kind (kind, size);
-}
-
-/* Implementation of linux_target_ops method "breakpoint_kind_from_pc". */
-
-static int
-aarch64_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
-{
- if (is_64bit_tdesc ())
- return aarch64_breakpoint_len;
- else
- return arm_breakpoint_kind_from_pc (pcptr);
-}
-
-/* Implementation of the linux_target_ops method
- "breakpoint_kind_from_current_state". */
-
-static int
-aarch64_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
-{
- if (is_64bit_tdesc ())
- return aarch64_breakpoint_len;
- else
- return arm_breakpoint_kind_from_current_state (pcptr);
-}
-
-/* Support for hardware single step. */
-
-static int
-aarch64_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-struct linux_target_ops the_low_target =
-{
- aarch64_arch_setup,
- aarch64_regs_info,
- NULL, /* cannot_fetch_register */
- NULL, /* cannot_store_register */
- NULL, /* fetch_register */
- aarch64_get_pc,
- aarch64_set_pc,
- aarch64_breakpoint_kind_from_pc,
- aarch64_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
- 0, /* decr_pc_after_break */
- aarch64_breakpoint_at,
- aarch64_supports_z_point_type,
- aarch64_insert_point,
- aarch64_remove_point,
- aarch64_stopped_by_watchpoint,
- aarch64_stopped_data_address,
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- aarch64_linux_siginfo_fixup,
- aarch64_linux_new_process,
- aarch64_linux_delete_process,
- aarch64_linux_new_thread,
- aarch64_linux_delete_thread,
- aarch64_linux_new_fork,
- aarch64_linux_prepare_to_resume,
- NULL, /* process_qsupported */
- aarch64_supports_tracepoints,
- aarch64_get_thread_area,
- aarch64_install_fast_tracepoint_jump_pad,
- aarch64_emit_ops,
- aarch64_get_min_fast_tracepoint_insn_len,
- aarch64_supports_range_stepping,
- aarch64_breakpoint_kind_from_current_state,
- aarch64_supports_hardware_single_step,
- aarch64_get_syscall_trapinfo,
-};
-
-void
-initialize_low_arch (void)
-{
- initialize_low_arch_aarch32 ();
-
- initialize_regsets_info (&aarch64_regsets_info);
- initialize_regsets_info (&aarch64_sve_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/AArch64 specific low level interface, for the remote server for
+ GDB.
+
+ Copyright (C) 2009-2020 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "nat/aarch64-linux.h"
+#include "nat/aarch64-linux-hw-point.h"
+#include "arch/aarch64-insn.h"
+#include "linux-aarch32-low.h"
+#include "elf/common.h"
+#include "ax.h"
+#include "tracepoint.h"
+#include "debug.h"
+
+#include <signal.h>
+#include <sys/user.h>
+#include "nat/gdb_ptrace.h"
+#include <asm/ptrace.h>
+#include <inttypes.h>
+#include <endian.h>
+#include <sys/uio.h>
+
+#include "gdb_proc_service.h"
+#include "arch/aarch64.h"
+#include "linux-aarch32-tdesc.h"
+#include "linux-aarch64-tdesc.h"
+#include "nat/aarch64-sve-linux-ptrace.h"
+#include "tdesc.h"
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+/* Per-process arch-specific data we want to keep. */
+
+struct arch_process_info
+{
+ /* Hardware breakpoint/watchpoint data.
+ The reason for them to be per-process rather than per-thread is
+ due to the lack of information in the gdbserver environment;
+ gdbserver is not told that whether a requested hardware
+ breakpoint/watchpoint is thread specific or not, so it has to set
+ each hw bp/wp for every thread in the current process. The
+ higher level bp/wp management in gdb will resume a thread if a hw
+ bp/wp trap is not expected for it. Since the hw bp/wp setting is
+ same for each thread, it is reasonable for the data to live here.
+ */
+ struct aarch64_debug_reg_state debug_reg_state;
+};
+
+/* Return true if the size of register 0 is 8 byte. */
+
+static int
+is_64bit_tdesc (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+
+ return register_size (regcache->tdesc, 0) == 8;
+}
+
+/* Return true if the regcache contains the number of SVE registers. */
+
+static bool
+is_sve_tdesc (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+
+ return tdesc_contains_feature (regcache->tdesc, "org.gnu.gdb.aarch64.sve");
+}
+
+static void
+aarch64_fill_gregset (struct regcache *regcache, void *buf)
+{
+ struct user_pt_regs *regset = (struct user_pt_regs *) buf;
+ int i;
+
+ for (i = 0; i < AARCH64_X_REGS_NUM; i++)
+ collect_register (regcache, AARCH64_X0_REGNUM + i, ®set->regs[i]);
+ collect_register (regcache, AARCH64_SP_REGNUM, ®set->sp);
+ collect_register (regcache, AARCH64_PC_REGNUM, ®set->pc);
+ collect_register (regcache, AARCH64_CPSR_REGNUM, ®set->pstate);
+}
+
+static void
+aarch64_store_gregset (struct regcache *regcache, const void *buf)
+{
+ const struct user_pt_regs *regset = (const struct user_pt_regs *) buf;
+ int i;
+
+ for (i = 0; i < AARCH64_X_REGS_NUM; i++)
+ supply_register (regcache, AARCH64_X0_REGNUM + i, ®set->regs[i]);
+ supply_register (regcache, AARCH64_SP_REGNUM, ®set->sp);
+ supply_register (regcache, AARCH64_PC_REGNUM, ®set->pc);
+ supply_register (regcache, AARCH64_CPSR_REGNUM, ®set->pstate);
+}
+
+static void
+aarch64_fill_fpregset (struct regcache *regcache, void *buf)
+{
+ struct user_fpsimd_state *regset = (struct user_fpsimd_state *) buf;
+ int i;
+
+ for (i = 0; i < AARCH64_V_REGS_NUM; i++)
+ collect_register (regcache, AARCH64_V0_REGNUM + i, ®set->vregs[i]);
+ collect_register (regcache, AARCH64_FPSR_REGNUM, ®set->fpsr);
+ collect_register (regcache, AARCH64_FPCR_REGNUM, ®set->fpcr);
+}
+
+static void
+aarch64_store_fpregset (struct regcache *regcache, const void *buf)
+{
+ const struct user_fpsimd_state *regset
+ = (const struct user_fpsimd_state *) buf;
+ int i;
+
+ for (i = 0; i < AARCH64_V_REGS_NUM; i++)
+ supply_register (regcache, AARCH64_V0_REGNUM + i, ®set->vregs[i]);
+ supply_register (regcache, AARCH64_FPSR_REGNUM, ®set->fpsr);
+ supply_register (regcache, AARCH64_FPCR_REGNUM, ®set->fpcr);
+}
+
+/* Store the pauth registers to regcache. */
+
+static void
+aarch64_store_pauthregset (struct regcache *regcache, const void *buf)
+{
+ uint64_t *pauth_regset = (uint64_t *) buf;
+ int pauth_base = find_regno (regcache->tdesc, "pauth_dmask");
+
+ if (pauth_base == 0)
+ return;
+
+ supply_register (regcache, AARCH64_PAUTH_DMASK_REGNUM (pauth_base),
+ &pauth_regset[0]);
+ supply_register (regcache, AARCH64_PAUTH_CMASK_REGNUM (pauth_base),
+ &pauth_regset[1]);
+}
+
+/* Implementation of linux_target_ops method "get_pc". */
+
+static CORE_ADDR
+aarch64_get_pc (struct regcache *regcache)
+{
+ if (register_size (regcache->tdesc, 0) == 8)
+ return linux_get_pc_64bit (regcache);
+ else
+ return linux_get_pc_32bit (regcache);
+}
+
+/* Implementation of linux_target_ops method "set_pc". */
+
+static void
+aarch64_set_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ if (register_size (regcache->tdesc, 0) == 8)
+ linux_set_pc_64bit (regcache, pc);
+ else
+ linux_set_pc_32bit (regcache, pc);
+}
+
+#define aarch64_breakpoint_len 4
+
+/* AArch64 BRK software debug mode instruction.
+ This instruction needs to match gdb/aarch64-tdep.c
+ (aarch64_default_breakpoint). */
+static const gdb_byte aarch64_breakpoint[] = {0x00, 0x00, 0x20, 0xd4};
+
+/* Implementation of linux_target_ops method "breakpoint_at". */
+
+static int
+aarch64_breakpoint_at (CORE_ADDR where)
+{
+ if (is_64bit_tdesc ())
+ {
+ gdb_byte insn[aarch64_breakpoint_len];
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn,
+ aarch64_breakpoint_len);
+ if (memcmp (insn, aarch64_breakpoint, aarch64_breakpoint_len) == 0)
+ return 1;
+
+ return 0;
+ }
+ else
+ return arm_breakpoint_at (where);
+}
+
+static void
+aarch64_init_debug_reg_state (struct aarch64_debug_reg_state *state)
+{
+ int i;
+
+ for (i = 0; i < AARCH64_HBP_MAX_NUM; ++i)
+ {
+ state->dr_addr_bp[i] = 0;
+ state->dr_ctrl_bp[i] = 0;
+ state->dr_ref_count_bp[i] = 0;
+ }
+
+ for (i = 0; i < AARCH64_HWP_MAX_NUM; ++i)
+ {
+ state->dr_addr_wp[i] = 0;
+ state->dr_ctrl_wp[i] = 0;
+ state->dr_ref_count_wp[i] = 0;
+ }
+}
+
+/* Return the pointer to the debug register state structure in the
+ current process' arch-specific data area. */
+
+struct aarch64_debug_reg_state *
+aarch64_get_debug_reg_state (pid_t pid)
+{
+ struct process_info *proc = find_process_pid (pid);
+
+ return &proc->priv->arch_private->debug_reg_state;
+}
+
+/* Implementation of linux_target_ops method "supports_z_point_type". */
+
+static int
+aarch64_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ case Z_PACKET_HW_BP:
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_READ_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Implementation of linux_target_ops method "insert_point".
+
+ It actually only records the info of the to-be-inserted bp/wp;
+ the actual insertion will happen when threads are resumed. */
+
+static int
+aarch64_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ int ret;
+ enum target_hw_bp_type targ_type;
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (pid_of (current_thread));
+
+ if (show_debug_regs)
+ fprintf (stderr, "insert_point on entry (addr=0x%08lx, len=%d)\n",
+ (unsigned long) addr, len);
+
+ /* Determine the type from the raw breakpoint type. */
+ targ_type = raw_bkpt_type_to_target_hw_bp_type (type);
+
+ if (targ_type != hw_execute)
+ {
+ if (aarch64_linux_region_ok_for_watchpoint (addr, len))
+ ret = aarch64_handle_watchpoint (targ_type, addr, len,
+ 1 /* is_insert */, state);
+ else
+ ret = -1;
+ }
+ else
+ {
+ if (len == 3)
+ {
+ /* LEN is 3 means the breakpoint is set on a 32-bit thumb
+ instruction. Set it to 2 to correctly encode length bit
+ mask in hardware/watchpoint control register. */
+ len = 2;
+ }
+ ret = aarch64_handle_breakpoint (targ_type, addr, len,
+ 1 /* is_insert */, state);
+ }
+
+ if (show_debug_regs)
+ aarch64_show_debug_reg_state (state, "insert_point", addr, len,
+ targ_type);
+
+ return ret;
+}
+
+/* Implementation of linux_target_ops method "remove_point".
+
+ It actually only records the info of the to-be-removed bp/wp,
+ the actual removal will be done when threads are resumed. */
+
+static int
+aarch64_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ int ret;
+ enum target_hw_bp_type targ_type;
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (pid_of (current_thread));
+
+ if (show_debug_regs)
+ fprintf (stderr, "remove_point on entry (addr=0x%08lx, len=%d)\n",
+ (unsigned long) addr, len);
+
+ /* Determine the type from the raw breakpoint type. */
+ targ_type = raw_bkpt_type_to_target_hw_bp_type (type);
+
+ /* Set up state pointers. */
+ if (targ_type != hw_execute)
+ ret =
+ aarch64_handle_watchpoint (targ_type, addr, len, 0 /* is_insert */,
+ state);
+ else
+ {
+ if (len == 3)
+ {
+ /* LEN is 3 means the breakpoint is set on a 32-bit thumb
+ instruction. Set it to 2 to correctly encode length bit
+ mask in hardware/watchpoint control register. */
+ len = 2;
+ }
+ ret = aarch64_handle_breakpoint (targ_type, addr, len,
+ 0 /* is_insert */, state);
+ }
+
+ if (show_debug_regs)
+ aarch64_show_debug_reg_state (state, "remove_point", addr, len,
+ targ_type);
+
+ return ret;
+}
+
+/* Implementation of linux_target_ops method "stopped_data_address". */
+
+static CORE_ADDR
+aarch64_stopped_data_address (void)
+{
+ siginfo_t siginfo;
+ int pid, i;
+ struct aarch64_debug_reg_state *state;
+
+ pid = lwpid_of (current_thread);
+
+ /* Get the siginfo. */
+ if (ptrace (PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0)
+ return (CORE_ADDR) 0;
+
+ /* Need to be a hardware breakpoint/watchpoint trap. */
+ if (siginfo.si_signo != SIGTRAP
+ || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
+ return (CORE_ADDR) 0;
+
+ /* Check if the address matches any watched address. */
+ state = aarch64_get_debug_reg_state (pid_of (current_thread));
+ for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
+ {
+ const unsigned int offset
+ = aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
+ const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
+ const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
+ const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
+ const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
+ const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
+
+ if (state->dr_ref_count_wp[i]
+ && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
+ && addr_trap >= addr_watch_aligned
+ && addr_trap < addr_watch + len)
+ {
+ /* ADDR_TRAP reports the first address of the memory range
+ accessed by the CPU, regardless of what was the memory
+ range watched. Thus, a large CPU access that straddles
+ the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
+ ADDR_TRAP that is lower than the
+ ADDR_WATCH..ADDR_WATCH+LEN range. E.g.:
+
+ addr: | 4 | 5 | 6 | 7 | 8 |
+ |---- range watched ----|
+ |----------- range accessed ------------|
+
+ In this case, ADDR_TRAP will be 4.
+
+ To match a watchpoint known to GDB core, we must never
+ report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
+ range. ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
+ positive on kernels older than 4.10. See PR
+ external/20207. */
+ return addr_orig;
+ }
+ }
+
+ return (CORE_ADDR) 0;
+}
+
+/* Implementation of linux_target_ops method "stopped_by_watchpoint". */
+
+static int
+aarch64_stopped_by_watchpoint (void)
+{
+ if (aarch64_stopped_data_address () != 0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Fetch the thread-local storage pointer for libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ return aarch64_ps_get_thread_area (ph, lwpid, idx, base,
+ is_64bit_tdesc ());
+}
+
+/* Implementation of linux_target_ops method "siginfo_fixup". */
+
+static int
+aarch64_linux_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction)
+{
+ /* Is the inferior 32-bit? If so, then fixup the siginfo object. */
+ if (!is_64bit_tdesc ())
+ {
+ if (direction == 0)
+ aarch64_compat_siginfo_from_siginfo ((struct compat_siginfo *) inf,
+ native);
+ else
+ aarch64_siginfo_from_compat_siginfo (native,
+ (struct compat_siginfo *) inf);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Implementation of linux_target_ops method "new_process". */
+
+static struct arch_process_info *
+aarch64_linux_new_process (void)
+{
+ struct arch_process_info *info = XCNEW (struct arch_process_info);
+
+ aarch64_init_debug_reg_state (&info->debug_reg_state);
+
+ return info;
+}
+
+/* Implementation of linux_target_ops method "delete_process". */
+
+static void
+aarch64_linux_delete_process (struct arch_process_info *info)
+{
+ xfree (info);
+}
+
+/* Implementation of linux_target_ops method "linux_new_fork". */
+
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->priv != NULL
+ && parent->priv->arch_private != NULL);
+ gdb_assert (child->priv != NULL
+ && child->priv->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->priv->arch_private = *parent->priv->arch_private;
+}
+
+/* Matches HWCAP_PACA in kernel header arch/arm64/include/uapi/asm/hwcap.h. */
+#define AARCH64_HWCAP_PACA (1 << 30)
+
+/* Implementation of linux_target_ops method "arch_setup". */
+
+static void
+aarch64_arch_setup (void)
+{
+ unsigned int machine;
+ int is_elf64;
+ int tid;
+
+ tid = lwpid_of (current_thread);
+
+ is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+
+ if (is_elf64)
+ {
+ uint64_t vq = aarch64_sve_get_vq (tid);
+ unsigned long hwcap = linux_get_hwcap (8);
+ bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
+
+ current_process ()->tdesc = aarch64_linux_read_description (vq, pauth_p);
+ }
+ else
+ current_process ()->tdesc = aarch32_linux_read_description ();
+
+ aarch64_linux_get_debug_reg_capacity (lwpid_of (current_thread));
+}
+
+/* Wrapper for aarch64_sve_regs_copy_to_reg_buf. */
+
+static void
+aarch64_sve_regs_copy_to_regcache (struct regcache *regcache, const void *buf)
+{
+ return aarch64_sve_regs_copy_to_reg_buf (regcache, buf);
+}
+
+/* Wrapper for aarch64_sve_regs_copy_from_reg_buf. */
+
+static void
+aarch64_sve_regs_copy_from_regcache (struct regcache *regcache, void *buf)
+{
+ return aarch64_sve_regs_copy_from_reg_buf (regcache, buf);
+}
+
+static struct regset_info aarch64_regsets[] =
+{
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
+ sizeof (struct user_pt_regs), GENERAL_REGS,
+ aarch64_fill_gregset, aarch64_store_gregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
+ sizeof (struct user_fpsimd_state), FP_REGS,
+ aarch64_fill_fpregset, aarch64_store_fpregset
+ },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
+ AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
+ NULL, aarch64_store_pauthregset },
+ NULL_REGSET
+};
+
+static struct regsets_info aarch64_regsets_info =
+ {
+ aarch64_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct regs_info regs_info_aarch64 =
+ {
+ NULL, /* regset_bitmap */
+ NULL, /* usrregs */
+ &aarch64_regsets_info,
+ };
+
+static struct regset_info aarch64_sve_regsets[] =
+{
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
+ sizeof (struct user_pt_regs), GENERAL_REGS,
+ aarch64_fill_gregset, aarch64_store_gregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_SVE,
+ SVE_PT_SIZE (AARCH64_MAX_SVE_VQ, SVE_PT_REGS_SVE), EXTENDED_REGS,
+ aarch64_sve_regs_copy_from_regcache, aarch64_sve_regs_copy_to_regcache
+ },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
+ AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
+ NULL, aarch64_store_pauthregset },
+ NULL_REGSET
+};
+
+static struct regsets_info aarch64_sve_regsets_info =
+ {
+ aarch64_sve_regsets, /* regsets. */
+ 0, /* num_regsets. */
+ NULL, /* disabled_regsets. */
+ };
+
+static struct regs_info regs_info_aarch64_sve =
+ {
+ NULL, /* regset_bitmap. */
+ NULL, /* usrregs. */
+ &aarch64_sve_regsets_info,
+ };
+
+/* Implementation of linux_target_ops method "regs_info". */
+
+static const struct regs_info *
+aarch64_regs_info (void)
+{
+ if (!is_64bit_tdesc ())
+ return ®s_info_aarch32;
+
+ if (is_sve_tdesc ())
+ return ®s_info_aarch64_sve;
+
+ return ®s_info_aarch64;
+}
+
+/* Implementation of linux_target_ops method "supports_tracepoints". */
+
+static int
+aarch64_supports_tracepoints (void)
+{
+ if (current_thread == NULL)
+ return 1;
+ else
+ {
+ /* We don't support tracepoints on aarch32 now. */
+ return is_64bit_tdesc ();
+ }
+}
+
+/* Implementation of linux_target_ops method "get_thread_area". */
+
+static int
+aarch64_get_thread_area (int lwpid, CORE_ADDR *addrp)
+{
+ struct iovec iovec;
+ uint64_t reg;
+
+ iovec.iov_base = ®
+ iovec.iov_len = sizeof (reg);
+
+ if (ptrace (PTRACE_GETREGSET, lwpid, NT_ARM_TLS, &iovec) != 0)
+ return -1;
+
+ *addrp = reg;
+
+ return 0;
+}
+
+/* Implementation of linux_target_ops method "get_syscall_trapinfo". */
+
+static void
+aarch64_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
+{
+ int use_64bit = register_size (regcache->tdesc, 0) == 8;
+
+ if (use_64bit)
+ {
+ long l_sysno;
+
+ collect_register_by_name (regcache, "x8", &l_sysno);
+ *sysno = (int) l_sysno;
+ }
+ else
+ collect_register_by_name (regcache, "r7", sysno);
+}
+
+/* List of condition codes that we need. */
+
+enum aarch64_condition_codes
+{
+ EQ = 0x0,
+ NE = 0x1,
+ LO = 0x3,
+ GE = 0xa,
+ LT = 0xb,
+ GT = 0xc,
+ LE = 0xd,
+};
+
+enum aarch64_operand_type
+{
+ OPERAND_IMMEDIATE,
+ OPERAND_REGISTER,
+};
+
+/* Representation of an operand. At this time, it only supports register
+ and immediate types. */
+
+struct aarch64_operand
+{
+ /* Type of the operand. */
+ enum aarch64_operand_type type;
+
+ /* Value of the operand according to the type. */
+ union
+ {
+ uint32_t imm;
+ struct aarch64_register reg;
+ };
+};
+
+/* List of registers that we are currently using, we can add more here as
+ we need to use them. */
+
+/* General purpose scratch registers (64 bit). */
+static const struct aarch64_register x0 = { 0, 1 };
+static const struct aarch64_register x1 = { 1, 1 };
+static const struct aarch64_register x2 = { 2, 1 };
+static const struct aarch64_register x3 = { 3, 1 };
+static const struct aarch64_register x4 = { 4, 1 };
+
+/* General purpose scratch registers (32 bit). */
+static const struct aarch64_register w0 = { 0, 0 };
+static const struct aarch64_register w2 = { 2, 0 };
+
+/* Intra-procedure scratch registers. */
+static const struct aarch64_register ip0 = { 16, 1 };
+
+/* Special purpose registers. */
+static const struct aarch64_register fp = { 29, 1 };
+static const struct aarch64_register lr = { 30, 1 };
+static const struct aarch64_register sp = { 31, 1 };
+static const struct aarch64_register xzr = { 31, 1 };
+
+/* Dynamically allocate a new register. If we know the register
+ statically, we should make it a global as above instead of using this
+ helper function. */
+
+static struct aarch64_register
+aarch64_register (unsigned num, int is64)
+{
+ return (struct aarch64_register) { num, is64 };
+}
+
+/* Helper function to create a register operand, for instructions with
+ different types of operands.
+
+ For example:
+ p += emit_mov (p, x0, register_operand (x1)); */
+
+static struct aarch64_operand
+register_operand (struct aarch64_register reg)
+{
+ struct aarch64_operand operand;
+
+ operand.type = OPERAND_REGISTER;
+ operand.reg = reg;
+
+ return operand;
+}
+
+/* Helper function to create an immediate operand, for instructions with
+ different types of operands.
+
+ For example:
+ p += emit_mov (p, x0, immediate_operand (12)); */
+
+static struct aarch64_operand
+immediate_operand (uint32_t imm)
+{
+ struct aarch64_operand operand;
+
+ operand.type = OPERAND_IMMEDIATE;
+ operand.imm = imm;
+
+ return operand;
+}
+
+/* Helper function to create an offset memory operand.
+
+ For example:
+ p += emit_ldr (p, x0, sp, offset_memory_operand (16)); */
+
+static struct aarch64_memory_operand
+offset_memory_operand (int32_t offset)
+{
+ return (struct aarch64_memory_operand) { MEMORY_OPERAND_OFFSET, offset };
+}
+
+/* Helper function to create a pre-index memory operand.
+
+ For example:
+ p += emit_ldr (p, x0, sp, preindex_memory_operand (16)); */
+
+static struct aarch64_memory_operand
+preindex_memory_operand (int32_t index)
+{
+ return (struct aarch64_memory_operand) { MEMORY_OPERAND_PREINDEX, index };
+}
+
+/* Helper function to create a post-index memory operand.
+
+ For example:
+ p += emit_ldr (p, x0, sp, postindex_memory_operand (16)); */
+
+static struct aarch64_memory_operand
+postindex_memory_operand (int32_t index)
+{
+ return (struct aarch64_memory_operand) { MEMORY_OPERAND_POSTINDEX, index };
+}
+
+/* System control registers. These special registers can be written and
+ read with the MRS and MSR instructions.
+
+ - NZCV: Condition flags. GDB refers to this register under the CPSR
+ name.
+ - FPSR: Floating-point status register.
+ - FPCR: Floating-point control registers.
+ - TPIDR_EL0: Software thread ID register. */
+
+enum aarch64_system_control_registers
+{
+ /* op0 op1 crn crm op2 */
+ NZCV = (0x1 << 14) | (0x3 << 11) | (0x4 << 7) | (0x2 << 3) | 0x0,
+ FPSR = (0x1 << 14) | (0x3 << 11) | (0x4 << 7) | (0x4 << 3) | 0x1,
+ FPCR = (0x1 << 14) | (0x3 << 11) | (0x4 << 7) | (0x4 << 3) | 0x0,
+ TPIDR_EL0 = (0x1 << 14) | (0x3 << 11) | (0xd << 7) | (0x0 << 3) | 0x2
+};
+
+/* Write a BLR instruction into *BUF.
+
+ BLR rn
+
+ RN is the register to branch to. */
+
+static int
+emit_blr (uint32_t *buf, struct aarch64_register rn)
+{
+ return aarch64_emit_insn (buf, BLR | ENCODE (rn.num, 5, 5));
+}
+
+/* Write a RET instruction into *BUF.
+
+ RET xn
+
+ RN is the register to branch to. */
+
+static int
+emit_ret (uint32_t *buf, struct aarch64_register rn)
+{
+ return aarch64_emit_insn (buf, RET | ENCODE (rn.num, 5, 5));
+}
+
+static int
+emit_load_store_pair (uint32_t *buf, enum aarch64_opcodes opcode,
+ struct aarch64_register rt,
+ struct aarch64_register rt2,
+ struct aarch64_register rn,
+ struct aarch64_memory_operand operand)
+{
+ uint32_t opc;
+ uint32_t pre_index;
+ uint32_t write_back;
+
+ if (rt.is64)
+ opc = ENCODE (2, 2, 30);
+ else
+ opc = ENCODE (0, 2, 30);
+
+ switch (operand.type)
+ {
+ case MEMORY_OPERAND_OFFSET:
+ {
+ pre_index = ENCODE (1, 1, 24);
+ write_back = ENCODE (0, 1, 23);
+ break;
+ }
+ case MEMORY_OPERAND_POSTINDEX:
+ {
+ pre_index = ENCODE (0, 1, 24);
+ write_back = ENCODE (1, 1, 23);
+ break;
+ }
+ case MEMORY_OPERAND_PREINDEX:
+ {
+ pre_index = ENCODE (1, 1, 24);
+ write_back = ENCODE (1, 1, 23);
+ break;
+ }
+ default:
+ return 0;
+ }
+
+ return aarch64_emit_insn (buf, opcode | opc | pre_index | write_back
+ | ENCODE (operand.index >> 3, 7, 15)
+ | ENCODE (rt2.num, 5, 10)
+ | ENCODE (rn.num, 5, 5) | ENCODE (rt.num, 5, 0));
+}
+
+/* Write a STP instruction into *BUF.
+
+ STP rt, rt2, [rn, #offset]
+ STP rt, rt2, [rn, #index]!
+ STP rt, rt2, [rn], #index
+
+ RT and RT2 are the registers to store.
+ RN is the base address register.
+ OFFSET is the immediate to add to the base address. It is limited to a
+ -512 .. 504 range (7 bits << 3). */
+
+static int
+emit_stp (uint32_t *buf, struct aarch64_register rt,
+ struct aarch64_register rt2, struct aarch64_register rn,
+ struct aarch64_memory_operand operand)
+{
+ return emit_load_store_pair (buf, STP, rt, rt2, rn, operand);
+}
+
+/* Write a LDP instruction into *BUF.
+
+ LDP rt, rt2, [rn, #offset]
+ LDP rt, rt2, [rn, #index]!
+ LDP rt, rt2, [rn], #index
+
+ RT and RT2 are the registers to store.
+ RN is the base address register.
+ OFFSET is the immediate to add to the base address. It is limited to a
+ -512 .. 504 range (7 bits << 3). */
+
+static int
+emit_ldp (uint32_t *buf, struct aarch64_register rt,
+ struct aarch64_register rt2, struct aarch64_register rn,
+ struct aarch64_memory_operand operand)
+{
+ return emit_load_store_pair (buf, LDP, rt, rt2, rn, operand);
+}
+
+/* Write a LDP (SIMD&VFP) instruction using Q registers into *BUF.
+
+ LDP qt, qt2, [rn, #offset]
+
+ RT and RT2 are the Q registers to store.
+ RN is the base address register.
+ OFFSET is the immediate to add to the base address. It is limited to
+ -1024 .. 1008 range (7 bits << 4). */
+
+static int
+emit_ldp_q_offset (uint32_t *buf, unsigned rt, unsigned rt2,
+ struct aarch64_register rn, int32_t offset)
+{
+ uint32_t opc = ENCODE (2, 2, 30);
+ uint32_t pre_index = ENCODE (1, 1, 24);
+
+ return aarch64_emit_insn (buf, LDP_SIMD_VFP | opc | pre_index
+ | ENCODE (offset >> 4, 7, 15)
+ | ENCODE (rt2, 5, 10)
+ | ENCODE (rn.num, 5, 5) | ENCODE (rt, 5, 0));
+}
+
+/* Write a STP (SIMD&VFP) instruction using Q registers into *BUF.
+
+ STP qt, qt2, [rn, #offset]
+
+ RT and RT2 are the Q registers to store.
+ RN is the base address register.
+ OFFSET is the immediate to add to the base address. It is limited to
+ -1024 .. 1008 range (7 bits << 4). */
+
+static int
+emit_stp_q_offset (uint32_t *buf, unsigned rt, unsigned rt2,
+ struct aarch64_register rn, int32_t offset)
+{
+ uint32_t opc = ENCODE (2, 2, 30);
+ uint32_t pre_index = ENCODE (1, 1, 24);
+
+ return aarch64_emit_insn (buf, STP_SIMD_VFP | opc | pre_index
+ | ENCODE (offset >> 4, 7, 15)
+ | ENCODE (rt2, 5, 10)
+ | ENCODE (rn.num, 5, 5) | ENCODE (rt, 5, 0));
+}
+
+/* Write a LDRH instruction into *BUF.
+
+ LDRH wt, [xn, #offset]
+ LDRH wt, [xn, #index]!
+ LDRH wt, [xn], #index
+
+ RT is the register to store.
+ RN is the base address register.
+ OFFSET is the immediate to add to the base address. It is limited to
+ 0 .. 32760 range (12 bits << 3). */
+
+static int
+emit_ldrh (uint32_t *buf, struct aarch64_register rt,
+ struct aarch64_register rn,
+ struct aarch64_memory_operand operand)
+{
+ return aarch64_emit_load_store (buf, 1, LDR, rt, rn, operand);
+}
+
+/* Write a LDRB instruction into *BUF.
+
+ LDRB wt, [xn, #offset]
+ LDRB wt, [xn, #index]!
+ LDRB wt, [xn], #index
+
+ RT is the register to store.
+ RN is the base address register.
+ OFFSET is the immediate to add to the base address. It is limited to
+ 0 .. 32760 range (12 bits << 3). */
+
+static int
+emit_ldrb (uint32_t *buf, struct aarch64_register rt,
+ struct aarch64_register rn,
+ struct aarch64_memory_operand operand)
+{
+ return aarch64_emit_load_store (buf, 0, LDR, rt, rn, operand);
+}
+
+
+
+/* Write a STR instruction into *BUF.
+
+ STR rt, [rn, #offset]
+ STR rt, [rn, #index]!
+ STR rt, [rn], #index
+
+ RT is the register to store.
+ RN is the base address register.
+ OFFSET is the immediate to add to the base address. It is limited to
+ 0 .. 32760 range (12 bits << 3). */
+
+static int
+emit_str (uint32_t *buf, struct aarch64_register rt,
+ struct aarch64_register rn,
+ struct aarch64_memory_operand operand)
+{
+ return aarch64_emit_load_store (buf, rt.is64 ? 3 : 2, STR, rt, rn, operand);
+}
+
+/* Helper function emitting an exclusive load or store instruction. */
+
+static int
+emit_load_store_exclusive (uint32_t *buf, uint32_t size,
+ enum aarch64_opcodes opcode,
+ struct aarch64_register rs,
+ struct aarch64_register rt,
+ struct aarch64_register rt2,
+ struct aarch64_register rn)
+{
+ return aarch64_emit_insn (buf, opcode | ENCODE (size, 2, 30)
+ | ENCODE (rs.num, 5, 16) | ENCODE (rt2.num, 5, 10)
+ | ENCODE (rn.num, 5, 5) | ENCODE (rt.num, 5, 0));
+}
+
+/* Write a LAXR instruction into *BUF.
+
+ LDAXR rt, [xn]
+
+ RT is the destination register.
+ RN is the base address register. */
+
+static int
+emit_ldaxr (uint32_t *buf, struct aarch64_register rt,
+ struct aarch64_register rn)
+{
+ return emit_load_store_exclusive (buf, rt.is64 ? 3 : 2, LDAXR, xzr, rt,
+ xzr, rn);
+}
+
+/* Write a STXR instruction into *BUF.
+
+ STXR ws, rt, [xn]
+
+ RS is the result register, it indicates if the store succeeded or not.
+ RT is the destination register.
+ RN is the base address register. */
+
+static int
+emit_stxr (uint32_t *buf, struct aarch64_register rs,
+ struct aarch64_register rt, struct aarch64_register rn)
+{
+ return emit_load_store_exclusive (buf, rt.is64 ? 3 : 2, STXR, rs, rt,
+ xzr, rn);
+}
+
+/* Write a STLR instruction into *BUF.
+
+ STLR rt, [xn]
+
+ RT is the register to store.
+ RN is the base address register. */
+
+static int
+emit_stlr (uint32_t *buf, struct aarch64_register rt,
+ struct aarch64_register rn)
+{
+ return emit_load_store_exclusive (buf, rt.is64 ? 3 : 2, STLR, xzr, rt,
+ xzr, rn);
+}
+
+/* Helper function for data processing instructions with register sources. */
+
+static int
+emit_data_processing_reg (uint32_t *buf, uint32_t opcode,
+ struct aarch64_register rd,
+ struct aarch64_register rn,
+ struct aarch64_register rm)
+{
+ uint32_t size = ENCODE (rd.is64, 1, 31);
+
+ return aarch64_emit_insn (buf, opcode | size | ENCODE (rm.num, 5, 16)
+ | ENCODE (rn.num, 5, 5) | ENCODE (rd.num, 5, 0));
+}
+
+/* Helper function for data processing instructions taking either a register
+ or an immediate. */
+
+static int
+emit_data_processing (uint32_t *buf, enum aarch64_opcodes opcode,
+ struct aarch64_register rd,
+ struct aarch64_register rn,
+ struct aarch64_operand operand)
+{
+ uint32_t size = ENCODE (rd.is64, 1, 31);
+ /* The opcode is different for register and immediate source operands. */
+ uint32_t operand_opcode;
+
+ if (operand.type == OPERAND_IMMEDIATE)
+ {
+ /* xxx1 000x xxxx xxxx xxxx xxxx xxxx xxxx */
+ operand_opcode = ENCODE (8, 4, 25);
+
+ return aarch64_emit_insn (buf, opcode | operand_opcode | size
+ | ENCODE (operand.imm, 12, 10)
+ | ENCODE (rn.num, 5, 5)
+ | ENCODE (rd.num, 5, 0));
+ }
+ else
+ {
+ /* xxx0 101x xxxx xxxx xxxx xxxx xxxx xxxx */
+ operand_opcode = ENCODE (5, 4, 25);
+
+ return emit_data_processing_reg (buf, opcode | operand_opcode, rd,
+ rn, operand.reg);
+ }
+}
+
+/* Write an ADD instruction into *BUF.
+
+ ADD rd, rn, #imm
+ ADD rd, rn, rm
+
+ This function handles both an immediate and register add.
+
+ RD is the destination register.
+ RN is the input register.
+ OPERAND is the source operand, either of type OPERAND_IMMEDIATE or
+ OPERAND_REGISTER. */
+
+static int
+emit_add (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_operand operand)
+{
+ return emit_data_processing (buf, ADD, rd, rn, operand);
+}
+
+/* Write a SUB instruction into *BUF.
+
+ SUB rd, rn, #imm
+ SUB rd, rn, rm
+
+ This function handles both an immediate and register sub.
+
+ RD is the destination register.
+ RN is the input register.
+ IMM is the immediate to substract to RN. */
+
+static int
+emit_sub (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_operand operand)
+{
+ return emit_data_processing (buf, SUB, rd, rn, operand);
+}
+
+/* Write a MOV instruction into *BUF.
+
+ MOV rd, #imm
+ MOV rd, rm
+
+ This function handles both a wide immediate move and a register move,
+ with the condition that the source register is not xzr. xzr and the
+ stack pointer share the same encoding and this function only supports
+ the stack pointer.
+
+ RD is the destination register.
+ OPERAND is the source operand, either of type OPERAND_IMMEDIATE or
+ OPERAND_REGISTER. */
+
+static int
+emit_mov (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_operand operand)
+{
+ if (operand.type == OPERAND_IMMEDIATE)
+ {
+ uint32_t size = ENCODE (rd.is64, 1, 31);
+ /* Do not shift the immediate. */
+ uint32_t shift = ENCODE (0, 2, 21);
+
+ return aarch64_emit_insn (buf, MOV | size | shift
+ | ENCODE (operand.imm, 16, 5)
+ | ENCODE (rd.num, 5, 0));
+ }
+ else
+ return emit_add (buf, rd, operand.reg, immediate_operand (0));
+}
+
+/* Write a MOVK instruction into *BUF.
+
+ MOVK rd, #imm, lsl #shift
+
+ RD is the destination register.
+ IMM is the immediate.
+ SHIFT is the logical shift left to apply to IMM. */
+
+static int
+emit_movk (uint32_t *buf, struct aarch64_register rd, uint32_t imm,
+ unsigned shift)
+{
+ uint32_t size = ENCODE (rd.is64, 1, 31);
+
+ return aarch64_emit_insn (buf, MOVK | size | ENCODE (shift, 2, 21) |
+ ENCODE (imm, 16, 5) | ENCODE (rd.num, 5, 0));
+}
+
+/* Write instructions into *BUF in order to move ADDR into a register.
+ ADDR can be a 64-bit value.
+
+ This function will emit a series of MOV and MOVK instructions, such as:
+
+ MOV xd, #(addr)
+ MOVK xd, #(addr >> 16), lsl #16
+ MOVK xd, #(addr >> 32), lsl #32
+ MOVK xd, #(addr >> 48), lsl #48 */
+
+static int
+emit_mov_addr (uint32_t *buf, struct aarch64_register rd, CORE_ADDR addr)
+{
+ uint32_t *p = buf;
+
+ /* The MOV (wide immediate) instruction clears to top bits of the
+ register. */
+ p += emit_mov (p, rd, immediate_operand (addr & 0xffff));
+
+ if ((addr >> 16) != 0)
+ p += emit_movk (p, rd, (addr >> 16) & 0xffff, 1);
+ else
+ return p - buf;
+
+ if ((addr >> 32) != 0)
+ p += emit_movk (p, rd, (addr >> 32) & 0xffff, 2);
+ else
+ return p - buf;
+
+ if ((addr >> 48) != 0)
+ p += emit_movk (p, rd, (addr >> 48) & 0xffff, 3);
+
+ return p - buf;
+}
+
+/* Write a SUBS instruction into *BUF.
+
+ SUBS rd, rn, rm
+
+ This instruction update the condition flags.
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_subs (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_operand operand)
+{
+ return emit_data_processing (buf, SUBS, rd, rn, operand);
+}
+
+/* Write a CMP instruction into *BUF.
+
+ CMP rn, rm
+
+ This instruction is an alias of SUBS xzr, rn, rm.
+
+ RN and RM are the registers to compare. */
+
+static int
+emit_cmp (uint32_t *buf, struct aarch64_register rn,
+ struct aarch64_operand operand)
+{
+ return emit_subs (buf, xzr, rn, operand);
+}
+
+/* Write a AND instruction into *BUF.
+
+ AND rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_and (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, AND, rd, rn, rm);
+}
+
+/* Write a ORR instruction into *BUF.
+
+ ORR rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_orr (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, ORR, rd, rn, rm);
+}
+
+/* Write a ORN instruction into *BUF.
+
+ ORN rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_orn (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, ORN, rd, rn, rm);
+}
+
+/* Write a EOR instruction into *BUF.
+
+ EOR rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_eor (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, EOR, rd, rn, rm);
+}
+
+/* Write a MVN instruction into *BUF.
+
+ MVN rd, rm
+
+ This is an alias for ORN rd, xzr, rm.
+
+ RD is the destination register.
+ RM is the source register. */
+
+static int
+emit_mvn (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rm)
+{
+ return emit_orn (buf, rd, xzr, rm);
+}
+
+/* Write a LSLV instruction into *BUF.
+
+ LSLV rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_lslv (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, LSLV, rd, rn, rm);
+}
+
+/* Write a LSRV instruction into *BUF.
+
+ LSRV rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_lsrv (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, LSRV, rd, rn, rm);
+}
+
+/* Write a ASRV instruction into *BUF.
+
+ ASRV rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_asrv (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, ASRV, rd, rn, rm);
+}
+
+/* Write a MUL instruction into *BUF.
+
+ MUL rd, rn, rm
+
+ RD is the destination register.
+ RN and RM are the source registers. */
+
+static int
+emit_mul (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm)
+{
+ return emit_data_processing_reg (buf, MUL, rd, rn, rm);
+}
+
+/* Write a MRS instruction into *BUF. The register size is 64-bit.
+
+ MRS xt, system_reg
+
+ RT is the destination register.
+ SYSTEM_REG is special purpose register to read. */
+
+static int
+emit_mrs (uint32_t *buf, struct aarch64_register rt,
+ enum aarch64_system_control_registers system_reg)
+{
+ return aarch64_emit_insn (buf, MRS | ENCODE (system_reg, 15, 5)
+ | ENCODE (rt.num, 5, 0));
+}
+
+/* Write a MSR instruction into *BUF. The register size is 64-bit.
+
+ MSR system_reg, xt
+
+ SYSTEM_REG is special purpose register to write.
+ RT is the input register. */
+
+static int
+emit_msr (uint32_t *buf, enum aarch64_system_control_registers system_reg,
+ struct aarch64_register rt)
+{
+ return aarch64_emit_insn (buf, MSR | ENCODE (system_reg, 15, 5)
+ | ENCODE (rt.num, 5, 0));
+}
+
+/* Write a SEVL instruction into *BUF.
+
+ This is a hint instruction telling the hardware to trigger an event. */
+
+static int
+emit_sevl (uint32_t *buf)
+{
+ return aarch64_emit_insn (buf, SEVL);
+}
+
+/* Write a WFE instruction into *BUF.
+
+ This is a hint instruction telling the hardware to wait for an event. */
+
+static int
+emit_wfe (uint32_t *buf)
+{
+ return aarch64_emit_insn (buf, WFE);
+}
+
+/* Write a SBFM instruction into *BUF.
+
+ SBFM rd, rn, #immr, #imms
+
+ This instruction moves the bits from #immr to #imms into the
+ destination, sign extending the result.
+
+ RD is the destination register.
+ RN is the source register.
+ IMMR is the bit number to start at (least significant bit).
+ IMMS is the bit number to stop at (most significant bit). */
+
+static int
+emit_sbfm (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, uint32_t immr, uint32_t imms)
+{
+ uint32_t size = ENCODE (rd.is64, 1, 31);
+ uint32_t n = ENCODE (rd.is64, 1, 22);
+
+ return aarch64_emit_insn (buf, SBFM | size | n | ENCODE (immr, 6, 16)
+ | ENCODE (imms, 6, 10) | ENCODE (rn.num, 5, 5)
+ | ENCODE (rd.num, 5, 0));
+}
+
+/* Write a SBFX instruction into *BUF.
+
+ SBFX rd, rn, #lsb, #width
+
+ This instruction moves #width bits from #lsb into the destination, sign
+ extending the result. This is an alias for:
+
+ SBFM rd, rn, #lsb, #(lsb + width - 1)
+
+ RD is the destination register.
+ RN is the source register.
+ LSB is the bit number to start at (least significant bit).
+ WIDTH is the number of bits to move. */
+
+static int
+emit_sbfx (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, uint32_t lsb, uint32_t width)
+{
+ return emit_sbfm (buf, rd, rn, lsb, lsb + width - 1);
+}
+
+/* Write a UBFM instruction into *BUF.
+
+ UBFM rd, rn, #immr, #imms
+
+ This instruction moves the bits from #immr to #imms into the
+ destination, extending the result with zeros.
+
+ RD is the destination register.
+ RN is the source register.
+ IMMR is the bit number to start at (least significant bit).
+ IMMS is the bit number to stop at (most significant bit). */
+
+static int
+emit_ubfm (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, uint32_t immr, uint32_t imms)
+{
+ uint32_t size = ENCODE (rd.is64, 1, 31);
+ uint32_t n = ENCODE (rd.is64, 1, 22);
+
+ return aarch64_emit_insn (buf, UBFM | size | n | ENCODE (immr, 6, 16)
+ | ENCODE (imms, 6, 10) | ENCODE (rn.num, 5, 5)
+ | ENCODE (rd.num, 5, 0));
+}
+
+/* Write a UBFX instruction into *BUF.
+
+ UBFX rd, rn, #lsb, #width
+
+ This instruction moves #width bits from #lsb into the destination,
+ extending the result with zeros. This is an alias for:
+
+ UBFM rd, rn, #lsb, #(lsb + width - 1)
+
+ RD is the destination register.
+ RN is the source register.
+ LSB is the bit number to start at (least significant bit).
+ WIDTH is the number of bits to move. */
+
+static int
+emit_ubfx (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, uint32_t lsb, uint32_t width)
+{
+ return emit_ubfm (buf, rd, rn, lsb, lsb + width - 1);
+}
+
+/* Write a CSINC instruction into *BUF.
+
+ CSINC rd, rn, rm, cond
+
+ This instruction conditionally increments rn or rm and places the result
+ in rd. rn is chosen is the condition is true.
+
+ RD is the destination register.
+ RN and RM are the source registers.
+ COND is the encoded condition. */
+
+static int
+emit_csinc (uint32_t *buf, struct aarch64_register rd,
+ struct aarch64_register rn, struct aarch64_register rm,
+ unsigned cond)
+{
+ uint32_t size = ENCODE (rd.is64, 1, 31);
+
+ return aarch64_emit_insn (buf, CSINC | size | ENCODE (rm.num, 5, 16)
+ | ENCODE (cond, 4, 12) | ENCODE (rn.num, 5, 5)
+ | ENCODE (rd.num, 5, 0));
+}
+
+/* Write a CSET instruction into *BUF.
+
+ CSET rd, cond
+
+ This instruction conditionally write 1 or 0 in the destination register.
+ 1 is written if the condition is true. This is an alias for:
+
+ CSINC rd, xzr, xzr, !cond
+
+ Note that the condition needs to be inverted.
+
+ RD is the destination register.
+ RN and RM are the source registers.
+ COND is the encoded condition. */
+
+static int
+emit_cset (uint32_t *buf, struct aarch64_register rd, unsigned cond)
+{
+ /* The least significant bit of the condition needs toggling in order to
+ invert it. */
+ return emit_csinc (buf, rd, xzr, xzr, cond ^ 0x1);
+}
+
+/* Write LEN instructions from BUF into the inferior memory at *TO.
+
+ Note instructions are always little endian on AArch64, unlike data. */
+
+static void
+append_insns (CORE_ADDR *to, size_t len, const uint32_t *buf)
+{
+ size_t byte_len = len * sizeof (uint32_t);
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+ uint32_t *le_buf = (uint32_t *) xmalloc (byte_len);
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ le_buf[i] = htole32 (buf[i]);
+
+ target_write_memory (*to, (const unsigned char *) le_buf, byte_len);
+
+ xfree (le_buf);
+#else
+ target_write_memory (*to, (const unsigned char *) buf, byte_len);
+#endif
+
+ *to += byte_len;
+}
+
+/* Sub-class of struct aarch64_insn_data, store information of
+ instruction relocation for fast tracepoint. Visitor can
+ relocate an instruction from BASE.INSN_ADDR to NEW_ADDR and save
+ the relocated instructions in buffer pointed by INSN_PTR. */
+
+struct aarch64_insn_relocation_data
+{
+ struct aarch64_insn_data base;
+
+ /* The new address the instruction is relocated to. */
+ CORE_ADDR new_addr;
+ /* Pointer to the buffer of relocated instruction(s). */
+ uint32_t *insn_ptr;
+};
+
+/* Implementation of aarch64_insn_visitor method "b". */
+
+static void
+aarch64_ftrace_insn_reloc_b (const int is_bl, const int32_t offset,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int64_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 28))
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, is_bl, new_offset);
+}
+
+/* Implementation of aarch64_insn_visitor method "b_cond". */
+
+static void
+aarch64_ftrace_insn_reloc_b_cond (const unsigned cond, const int32_t offset,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int64_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 21))
+ {
+ insn_reloc->insn_ptr += emit_bcond (insn_reloc->insn_ptr, cond,
+ new_offset);
+ }
+ else if (can_encode_int32 (new_offset, 28))
+ {
+ /* The offset is out of range for a conditional branch
+ instruction but not for a unconditional branch. We can use
+ the following instructions instead:
+
+ B.COND TAKEN ; If cond is true, then jump to TAKEN.
+ B NOT_TAKEN ; Else jump over TAKEN and continue.
+ TAKEN:
+ B #(offset - 8)
+ NOT_TAKEN:
+
+ */
+
+ insn_reloc->insn_ptr += emit_bcond (insn_reloc->insn_ptr, cond, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, new_offset - 8);
+ }
+}
+
+/* Implementation of aarch64_insn_visitor method "cb". */
+
+static void
+aarch64_ftrace_insn_reloc_cb (const int32_t offset, const int is_cbnz,
+ const unsigned rn, int is64,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int64_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 21))
+ {
+ insn_reloc->insn_ptr += emit_cb (insn_reloc->insn_ptr, is_cbnz,
+ aarch64_register (rn, is64), new_offset);
+ }
+ else if (can_encode_int32 (new_offset, 28))
+ {
+ /* The offset is out of range for a compare and branch
+ instruction but not for a unconditional branch. We can use
+ the following instructions instead:
+
+ CBZ xn, TAKEN ; xn == 0, then jump to TAKEN.
+ B NOT_TAKEN ; Else jump over TAKEN and continue.
+ TAKEN:
+ B #(offset - 8)
+ NOT_TAKEN:
+
+ */
+ insn_reloc->insn_ptr += emit_cb (insn_reloc->insn_ptr, is_cbnz,
+ aarch64_register (rn, is64), 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, new_offset - 8);
+ }
+}
+
+/* Implementation of aarch64_insn_visitor method "tb". */
+
+static void
+aarch64_ftrace_insn_reloc_tb (const int32_t offset, int is_tbnz,
+ const unsigned rt, unsigned bit,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int64_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 16))
+ {
+ insn_reloc->insn_ptr += emit_tb (insn_reloc->insn_ptr, is_tbnz, bit,
+ aarch64_register (rt, 1), new_offset);
+ }
+ else if (can_encode_int32 (new_offset, 28))
+ {
+ /* The offset is out of range for a test bit and branch
+ instruction but not for a unconditional branch. We can use
+ the following instructions instead:
+
+ TBZ xn, #bit, TAKEN ; xn[bit] == 0, then jump to TAKEN.
+ B NOT_TAKEN ; Else jump over TAKEN and continue.
+ TAKEN:
+ B #(offset - 8)
+ NOT_TAKEN:
+
+ */
+ insn_reloc->insn_ptr += emit_tb (insn_reloc->insn_ptr, is_tbnz, bit,
+ aarch64_register (rt, 1), 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0,
+ new_offset - 8);
+ }
+}
+
+/* Implementation of aarch64_insn_visitor method "adr". */
+
+static void
+aarch64_ftrace_insn_reloc_adr (const int32_t offset, const unsigned rd,
+ const int is_adrp,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ /* We know exactly the address the ADR{P,} instruction will compute.
+ We can just write it to the destination register. */
+ CORE_ADDR address = data->insn_addr + offset;
+
+ if (is_adrp)
+ {
+ /* Clear the lower 12 bits of the offset to get the 4K page. */
+ insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
+ aarch64_register (rd, 1),
+ address & ~0xfff);
+ }
+ else
+ insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
+ aarch64_register (rd, 1), address);
+}
+
+/* Implementation of aarch64_insn_visitor method "ldr_literal". */
+
+static void
+aarch64_ftrace_insn_reloc_ldr_literal (const int32_t offset, const int is_sw,
+ const unsigned rt, const int is64,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ CORE_ADDR address = data->insn_addr + offset;
+
+ insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
+ aarch64_register (rt, 1), address);
+
+ /* We know exactly what address to load from, and what register we
+ can use:
+
+ MOV xd, #(oldloc + offset)
+ MOVK xd, #((oldloc + offset) >> 16), lsl #16
+ ...
+
+ LDR xd, [xd] ; or LDRSW xd, [xd]
+
+ */
+
+ if (is_sw)
+ insn_reloc->insn_ptr += emit_ldrsw (insn_reloc->insn_ptr,
+ aarch64_register (rt, 1),
+ aarch64_register (rt, 1),
+ offset_memory_operand (0));
+ else
+ insn_reloc->insn_ptr += emit_ldr (insn_reloc->insn_ptr,
+ aarch64_register (rt, is64),
+ aarch64_register (rt, 1),
+ offset_memory_operand (0));
+}
+
+/* Implementation of aarch64_insn_visitor method "others". */
+
+static void
+aarch64_ftrace_insn_reloc_others (const uint32_t insn,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+
+ /* The instruction is not PC relative. Just re-emit it at the new
+ location. */
+ insn_reloc->insn_ptr += aarch64_emit_insn (insn_reloc->insn_ptr, insn);
+}
+
+static const struct aarch64_insn_visitor visitor =
+{
+ aarch64_ftrace_insn_reloc_b,
+ aarch64_ftrace_insn_reloc_b_cond,
+ aarch64_ftrace_insn_reloc_cb,
+ aarch64_ftrace_insn_reloc_tb,
+ aarch64_ftrace_insn_reloc_adr,
+ aarch64_ftrace_insn_reloc_ldr_literal,
+ aarch64_ftrace_insn_reloc_others,
+};
+
+/* Implementation of linux_target_ops method
+ "install_fast_tracepoint_jump_pad". */
+
+static int
+aarch64_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;
+ int64_t offset;
+ int i;
+ uint32_t insn;
+ CORE_ADDR buildaddr = *jump_entry;
+ struct aarch64_insn_relocation_data insn_data;
+
+ /* We need to save the current state on the stack both to restore it
+ later and to collect register values when the tracepoint is hit.
+
+ The saved registers are pushed in a layout that needs to be in sync
+ with aarch64_ft_collect_regmap (see linux-aarch64-ipa.c). Later on
+ the supply_fast_tracepoint_registers function will fill in the
+ register cache from a pointer to saved registers on the stack we build
+ here.
+
+ For simplicity, we set the size of each cell on the stack to 16 bytes.
+ This way one cell can hold any register type, from system registers
+ to the 128 bit SIMD&FP registers. Furthermore, the stack pointer
+ has to be 16 bytes aligned anyway.
+
+ Note that the CPSR register does not exist on AArch64. Instead we
+ can access system bits describing the process state with the
+ MRS/MSR instructions, namely the condition flags. We save them as
+ if they are part of a CPSR register because that's how GDB
+ interprets these system bits. At the moment, only the condition
+ flags are saved in CPSR (NZCV).
+
+ Stack layout, each cell is 16 bytes (descending):
+
+ High *-------- SIMD&FP registers from 31 down to 0. --------*
+ | q31 |
+ . .
+ . . 32 cells
+ . .
+ | q0 |
+ *---- General purpose registers from 30 down to 0. ----*
+ | x30 |
+ . .
+ . . 31 cells
+ . .
+ | x0 |
+ *------------- Special purpose registers. -------------*
+ | SP |
+ | PC |
+ | CPSR (NZCV) | 5 cells
+ | FPSR |
+ | FPCR | <- SP + 16
+ *------------- collecting_t object --------------------*
+ | TPIDR_EL0 | struct tracepoint * |
+ Low *------------------------------------------------------*
+
+ After this stack is set up, we issue a call to the collector, passing
+ it the saved registers at (SP + 16). */
+
+ /* Push SIMD&FP registers on the stack:
+
+ SUB sp, sp, #(32 * 16)
+
+ STP q30, q31, [sp, #(30 * 16)]
+ ...
+ STP q0, q1, [sp]
+
+ */
+ p += emit_sub (p, sp, sp, immediate_operand (32 * 16));
+ for (i = 30; i >= 0; i -= 2)
+ p += emit_stp_q_offset (p, i, i + 1, sp, i * 16);
+
+ /* Push general purpose registers on the stack. Note that we do not need
+ to push x31 as it represents the xzr register and not the stack
+ pointer in a STR instruction.
+
+ SUB sp, sp, #(31 * 16)
+
+ STR x30, [sp, #(30 * 16)]
+ ...
+ STR x0, [sp]
+
+ */
+ p += emit_sub (p, sp, sp, immediate_operand (31 * 16));
+ for (i = 30; i >= 0; i -= 1)
+ p += emit_str (p, aarch64_register (i, 1), sp,
+ offset_memory_operand (i * 16));
+
+ /* Make space for 5 more cells.
+
+ SUB sp, sp, #(5 * 16)
+
+ */
+ p += emit_sub (p, sp, sp, immediate_operand (5 * 16));
+
+
+ /* Save SP:
+
+ ADD x4, sp, #((32 + 31 + 5) * 16)
+ STR x4, [sp, #(4 * 16)]
+
+ */
+ p += emit_add (p, x4, sp, immediate_operand ((32 + 31 + 5) * 16));
+ p += emit_str (p, x4, sp, offset_memory_operand (4 * 16));
+
+ /* Save PC (tracepoint address):
+
+ MOV x3, #(tpaddr)
+ ...
+
+ STR x3, [sp, #(3 * 16)]
+
+ */
+
+ p += emit_mov_addr (p, x3, tpaddr);
+ p += emit_str (p, x3, sp, offset_memory_operand (3 * 16));
+
+ /* Save CPSR (NZCV), FPSR and FPCR:
+
+ MRS x2, nzcv
+ MRS x1, fpsr
+ MRS x0, fpcr
+
+ STR x2, [sp, #(2 * 16)]
+ STR x1, [sp, #(1 * 16)]
+ STR x0, [sp, #(0 * 16)]
+
+ */
+ p += emit_mrs (p, x2, NZCV);
+ p += emit_mrs (p, x1, FPSR);
+ p += emit_mrs (p, x0, FPCR);
+ p += emit_str (p, x2, sp, offset_memory_operand (2 * 16));
+ p += emit_str (p, x1, sp, offset_memory_operand (1 * 16));
+ p += emit_str (p, x0, sp, offset_memory_operand (0 * 16));
+
+ /* Push the collecting_t object. It consist of the address of the
+ tracepoint and an ID for the current thread. We get the latter by
+ reading the tpidr_el0 system register. It corresponds to the
+ NT_ARM_TLS register accessible with ptrace.
+
+ MOV x0, #(tpoint)
+ ...
+
+ MRS x1, tpidr_el0
+
+ STP x0, x1, [sp, #-16]!
+
+ */
+
+ p += emit_mov_addr (p, x0, tpoint);
+ p += emit_mrs (p, x1, TPIDR_EL0);
+ p += emit_stp (p, x0, x1, sp, preindex_memory_operand (-16));
+
+ /* Spin-lock:
+
+ The shared memory for the lock is at lockaddr. It will hold zero
+ if no-one is holding the lock, otherwise it contains the address of
+ the collecting_t object on the stack of the thread which acquired it.
+
+ At this stage, the stack pointer points to this thread's collecting_t
+ object.
+
+ We use the following registers:
+ - x0: Address of the lock.
+ - x1: Pointer to collecting_t object.
+ - x2: Scratch register.
+
+ MOV x0, #(lockaddr)
+ ...
+ MOV x1, sp
+
+ ; Trigger an event local to this core. So the following WFE
+ ; instruction is ignored.
+ SEVL
+ again:
+ ; Wait for an event. The event is triggered by either the SEVL
+ ; or STLR instructions (store release).
+ WFE
+
+ ; Atomically read at lockaddr. This marks the memory location as
+ ; exclusive. This instruction also has memory constraints which
+ ; make sure all previous data reads and writes are done before
+ ; executing it.
+ LDAXR x2, [x0]
+
+ ; Try again if another thread holds the lock.
+ CBNZ x2, again
+
+ ; We can lock it! Write the address of the collecting_t object.
+ ; This instruction will fail if the memory location is not marked
+ ; as exclusive anymore. If it succeeds, it will remove the
+ ; exclusive mark on the memory location. This way, if another
+ ; thread executes this instruction before us, we will fail and try
+ ; all over again.
+ STXR w2, x1, [x0]
+ CBNZ w2, again
+
+ */
+
+ p += emit_mov_addr (p, x0, lockaddr);
+ p += emit_mov (p, x1, register_operand (sp));
+
+ p += emit_sevl (p);
+ p += emit_wfe (p);
+ p += emit_ldaxr (p, x2, x0);
+ p += emit_cb (p, 1, w2, -2 * 4);
+ p += emit_stxr (p, w2, x1, x0);
+ p += emit_cb (p, 1, x2, -4 * 4);
+
+ /* Call collector (struct tracepoint *, unsigned char *):
+
+ MOV x0, #(tpoint)
+ ...
+
+ ; Saved registers start after the collecting_t object.
+ ADD x1, sp, #16
+
+ ; We use an intra-procedure-call scratch register.
+ MOV ip0, #(collector)
+ ...
+
+ ; And call back to C!
+ BLR ip0
+
+ */
+
+ p += emit_mov_addr (p, x0, tpoint);
+ p += emit_add (p, x1, sp, immediate_operand (16));
+
+ p += emit_mov_addr (p, ip0, collector);
+ p += emit_blr (p, ip0);
+
+ /* Release the lock.
+
+ MOV x0, #(lockaddr)
+ ...
+
+ ; This instruction is a normal store with memory ordering
+ ; constraints. Thanks to this we do not have to put a data
+ ; barrier instruction to make sure all data read and writes are done
+ ; before this instruction is executed. Furthermore, this instruction
+ ; will trigger an event, letting other threads know they can grab
+ ; the lock.
+ STLR xzr, [x0]
+
+ */
+ p += emit_mov_addr (p, x0, lockaddr);
+ p += emit_stlr (p, xzr, x0);
+
+ /* Free collecting_t object:
+
+ ADD sp, sp, #16
+
+ */
+ p += emit_add (p, sp, sp, immediate_operand (16));
+
+ /* Restore CPSR (NZCV), FPSR and FPCR. And free all special purpose
+ registers from the stack.
+
+ LDR x2, [sp, #(2 * 16)]
+ LDR x1, [sp, #(1 * 16)]
+ LDR x0, [sp, #(0 * 16)]
+
+ MSR NZCV, x2
+ MSR FPSR, x1
+ MSR FPCR, x0
+
+ ADD sp, sp #(5 * 16)
+
+ */
+ p += emit_ldr (p, x2, sp, offset_memory_operand (2 * 16));
+ p += emit_ldr (p, x1, sp, offset_memory_operand (1 * 16));
+ p += emit_ldr (p, x0, sp, offset_memory_operand (0 * 16));
+ p += emit_msr (p, NZCV, x2);
+ p += emit_msr (p, FPSR, x1);
+ p += emit_msr (p, FPCR, x0);
+
+ p += emit_add (p, sp, sp, immediate_operand (5 * 16));
+
+ /* Pop general purpose registers:
+
+ LDR x0, [sp]
+ ...
+ LDR x30, [sp, #(30 * 16)]
+
+ ADD sp, sp, #(31 * 16)
+
+ */
+ for (i = 0; i <= 30; i += 1)
+ p += emit_ldr (p, aarch64_register (i, 1), sp,
+ offset_memory_operand (i * 16));
+ p += emit_add (p, sp, sp, immediate_operand (31 * 16));
+
+ /* Pop SIMD&FP registers:
+
+ LDP q0, q1, [sp]
+ ...
+ LDP q30, q31, [sp, #(30 * 16)]
+
+ ADD sp, sp, #(32 * 16)
+
+ */
+ for (i = 0; i <= 30; i += 2)
+ p += emit_ldp_q_offset (p, i, i + 1, sp, i * 16);
+ p += emit_add (p, sp, sp, immediate_operand (32 * 16));
+
+ /* Write the code into the inferior memory. */
+ append_insns (&buildaddr, p - buf, buf);
+
+ /* Now emit the relocated instruction. */
+ *adjusted_insn_addr = buildaddr;
+ target_read_uint32 (tpaddr, &insn);
+
+ insn_data.base.insn_addr = tpaddr;
+ insn_data.new_addr = buildaddr;
+ insn_data.insn_ptr = buf;
+
+ aarch64_relocate_instruction (insn, &visitor,
+ (struct aarch64_insn_data *) &insn_data);
+
+ /* We may not have been able to relocate the instruction. */
+ if (insn_data.insn_ptr == buf)
+ {
+ sprintf (err,
+ "E.Could not relocate instruction from %s to %s.",
+ core_addr_to_string_nz (tpaddr),
+ core_addr_to_string_nz (buildaddr));
+ return 1;
+ }
+ else
+ append_insns (&buildaddr, insn_data.insn_ptr - buf, buf);
+ *adjusted_insn_addr_end = buildaddr;
+
+ /* Go back to the start of the buffer. */
+ p = buf;
+
+ /* Emit a branch back from the jump pad. */
+ offset = (tpaddr + orig_size - buildaddr);
+ if (!can_encode_int32 (offset, 28))
+ {
+ sprintf (err,
+ "E.Jump back from jump pad too far from tracepoint "
+ "(offset 0x%" PRIx64 " cannot be encoded in 28 bits).",
+ offset);
+ return 1;
+ }
+
+ p += emit_b (p, 0, offset);
+ append_insns (&buildaddr, p - buf, buf);
+
+ /* Give the caller a branch instruction into the jump pad. */
+ offset = (*jump_entry - tpaddr);
+ if (!can_encode_int32 (offset, 28))
+ {
+ sprintf (err,
+ "E.Jump pad too far from tracepoint "
+ "(offset 0x%" PRIx64 " cannot be encoded in 28 bits).",
+ offset);
+ return 1;
+ }
+
+ emit_b ((uint32_t *) jjump_pad_insn, 0, offset);
+ *jjump_pad_insn_size = 4;
+
+ /* Return the end address of our pad. */
+ *jump_entry = buildaddr;
+
+ return 0;
+}
+
+/* Helper function writing LEN instructions from START into
+ current_insn_ptr. */
+
+static void
+emit_ops_insns (const uint32_t *start, int len)
+{
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ if (debug_threads)
+ debug_printf ("Adding %d instrucions at %s\n",
+ len, paddress (buildaddr));
+
+ append_insns (&buildaddr, len, start);
+ current_insn_ptr = buildaddr;
+}
+
+/* Pop a register from the stack. */
+
+static int
+emit_pop (uint32_t *buf, struct aarch64_register rt)
+{
+ return emit_ldr (buf, rt, sp, postindex_memory_operand (1 * 16));
+}
+
+/* Push a register on the stack. */
+
+static int
+emit_push (uint32_t *buf, struct aarch64_register rt)
+{
+ return emit_str (buf, rt, sp, preindex_memory_operand (-1 * 16));
+}
+
+/* Implementation of emit_ops method "emit_prologue". */
+
+static void
+aarch64_emit_prologue (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ /* This function emit a prologue for the following function prototype:
+
+ enum eval_result_type f (unsigned char *regs,
+ ULONGEST *value);
+
+ The first argument is a buffer of raw registers. The second
+ argument is the result of
+ evaluating the expression, which will be set to whatever is on top of
+ the stack at the end.
+
+ The stack set up by the prologue is as such:
+
+ High *------------------------------------------------------*
+ | LR |
+ | FP | <- FP
+ | x1 (ULONGEST *value) |
+ | x0 (unsigned char *regs) |
+ Low *------------------------------------------------------*
+
+ As we are implementing a stack machine, each opcode can expand the
+ stack so we never know how far we are from the data saved by this
+ prologue. In order to be able refer to value and regs later, we save
+ the current stack pointer in the frame pointer. This way, it is not
+ clobbered when calling C functions.
+
+ Finally, throughout every operation, we are using register x0 as the
+ top of the stack, and x1 as a scratch register. */
+
+ p += emit_stp (p, x0, x1, sp, preindex_memory_operand (-2 * 16));
+ p += emit_str (p, lr, sp, offset_memory_operand (3 * 8));
+ p += emit_str (p, fp, sp, offset_memory_operand (2 * 8));
+
+ p += emit_add (p, fp, sp, immediate_operand (2 * 8));
+
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_epilogue". */
+
+static void
+aarch64_emit_epilogue (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ /* Store the result of the expression (x0) in *value. */
+ p += emit_sub (p, x1, fp, immediate_operand (1 * 8));
+ p += emit_ldr (p, x1, x1, offset_memory_operand (0));
+ p += emit_str (p, x0, x1, offset_memory_operand (0));
+
+ /* Restore the previous state. */
+ p += emit_add (p, sp, fp, immediate_operand (2 * 8));
+ p += emit_ldp (p, fp, lr, fp, offset_memory_operand (0));
+
+ /* Return expr_eval_no_error. */
+ p += emit_mov (p, x0, immediate_operand (expr_eval_no_error));
+ p += emit_ret (p, lr);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_add". */
+
+static void
+aarch64_emit_add (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_add (p, x0, x1, register_operand (x0));
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_sub". */
+
+static void
+aarch64_emit_sub (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_sub (p, x0, x1, register_operand (x0));
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_mul". */
+
+static void
+aarch64_emit_mul (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_mul (p, x0, x1, x0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_lsh". */
+
+static void
+aarch64_emit_lsh (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_lslv (p, x0, x1, x0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_rsh_signed". */
+
+static void
+aarch64_emit_rsh_signed (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_asrv (p, x0, x1, x0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_rsh_unsigned". */
+
+static void
+aarch64_emit_rsh_unsigned (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_lsrv (p, x0, x1, x0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_ext". */
+
+static void
+aarch64_emit_ext (int arg)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_sbfx (p, x0, x0, 0, arg);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_log_not". */
+
+static void
+aarch64_emit_log_not (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ /* If the top of the stack is 0, replace it with 1. Else replace it with
+ 0. */
+
+ p += emit_cmp (p, x0, immediate_operand (0));
+ p += emit_cset (p, x0, EQ);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_bit_and". */
+
+static void
+aarch64_emit_bit_and (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_and (p, x0, x0, x1);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_bit_or". */
+
+static void
+aarch64_emit_bit_or (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_orr (p, x0, x0, x1);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_bit_xor". */
+
+static void
+aarch64_emit_bit_xor (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_eor (p, x0, x0, x1);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_bit_not". */
+
+static void
+aarch64_emit_bit_not (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_mvn (p, x0, x0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_equal". */
+
+static void
+aarch64_emit_equal (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x0, register_operand (x1));
+ p += emit_cset (p, x0, EQ);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_less_signed". */
+
+static void
+aarch64_emit_less_signed (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ p += emit_cset (p, x0, LT);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_less_unsigned". */
+
+static void
+aarch64_emit_less_unsigned (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ p += emit_cset (p, x0, LO);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_ref". */
+
+static void
+aarch64_emit_ref (int size)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ switch (size)
+ {
+ case 1:
+ p += emit_ldrb (p, w0, x0, offset_memory_operand (0));
+ break;
+ case 2:
+ p += emit_ldrh (p, w0, x0, offset_memory_operand (0));
+ break;
+ case 4:
+ p += emit_ldr (p, w0, x0, offset_memory_operand (0));
+ break;
+ case 8:
+ p += emit_ldr (p, x0, x0, offset_memory_operand (0));
+ break;
+ default:
+ /* Unknown size, bail on compilation. */
+ emit_error = 1;
+ break;
+ }
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_if_goto". */
+
+static void
+aarch64_emit_if_goto (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ /* The Z flag is set or cleared here. */
+ p += emit_cmp (p, x0, immediate_operand (0));
+ /* This instruction must not change the Z flag. */
+ p += emit_pop (p, x0);
+ /* Branch over the next instruction if x0 == 0. */
+ p += emit_bcond (p, EQ, 8);
+
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = (p - buf) * 4;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_goto". */
+
+static void
+aarch64_emit_goto (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = 0;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "write_goto_address". */
+
+static void
+aarch64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ uint32_t insn;
+
+ emit_b (&insn, 0, to - from);
+ append_insns (&from, 1, &insn);
+}
+
+/* Implementation of emit_ops method "emit_const". */
+
+static void
+aarch64_emit_const (LONGEST num)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_mov_addr (p, x0, num);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_call". */
+
+static void
+aarch64_emit_call (CORE_ADDR fn)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_mov_addr (p, ip0, fn);
+ p += emit_blr (p, ip0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_reg". */
+
+static void
+aarch64_emit_reg (int reg)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ /* Set x0 to unsigned char *regs. */
+ p += emit_sub (p, x0, fp, immediate_operand (2 * 8));
+ p += emit_ldr (p, x0, x0, offset_memory_operand (0));
+ p += emit_mov (p, x1, immediate_operand (reg));
+
+ emit_ops_insns (buf, p - buf);
+
+ aarch64_emit_call (get_raw_reg_func_addr ());
+}
+
+/* Implementation of emit_ops method "emit_pop". */
+
+static void
+aarch64_emit_pop (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_stack_flush". */
+
+static void
+aarch64_emit_stack_flush (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_push (p, x0);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_zero_ext". */
+
+static void
+aarch64_emit_zero_ext (int arg)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_ubfx (p, x0, x0, 0, arg);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_swap". */
+
+static void
+aarch64_emit_swap (void)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_ldr (p, x1, sp, offset_memory_operand (0 * 16));
+ p += emit_str (p, x0, sp, offset_memory_operand (0 * 16));
+ p += emit_mov (p, x0, register_operand (x1));
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_stack_adjust". */
+
+static void
+aarch64_emit_stack_adjust (int n)
+{
+ /* This is not needed with our design. */
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_add (p, sp, sp, immediate_operand (n * 16));
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_int_call_1". */
+
+static void
+aarch64_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_mov (p, x0, immediate_operand (arg1));
+
+ emit_ops_insns (buf, p - buf);
+
+ aarch64_emit_call (fn);
+}
+
+/* Implementation of emit_ops method "emit_void_call_2". */
+
+static void
+aarch64_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ /* Push x0 on the stack. */
+ aarch64_emit_stack_flush ();
+
+ /* Setup arguments for the function call:
+
+ x0: arg1
+ x1: top of the stack
+
+ MOV x1, x0
+ MOV x0, #arg1 */
+
+ p += emit_mov (p, x1, register_operand (x0));
+ p += emit_mov (p, x0, immediate_operand (arg1));
+
+ emit_ops_insns (buf, p - buf);
+
+ aarch64_emit_call (fn);
+
+ /* Restore x0. */
+ aarch64_emit_pop ();
+}
+
+/* Implementation of emit_ops method "emit_eq_goto". */
+
+static void
+aarch64_emit_eq_goto (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ /* Branch over the next instruction if x0 != x1. */
+ p += emit_bcond (p, NE, 8);
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = (p - buf) * 4;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_ne_goto". */
+
+static void
+aarch64_emit_ne_goto (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ /* Branch over the next instruction if x0 == x1. */
+ p += emit_bcond (p, EQ, 8);
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = (p - buf) * 4;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_lt_goto". */
+
+static void
+aarch64_emit_lt_goto (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ /* Branch over the next instruction if x0 >= x1. */
+ p += emit_bcond (p, GE, 8);
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = (p - buf) * 4;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_le_goto". */
+
+static void
+aarch64_emit_le_goto (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ /* Branch over the next instruction if x0 > x1. */
+ p += emit_bcond (p, GT, 8);
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = (p - buf) * 4;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_gt_goto". */
+
+static void
+aarch64_emit_gt_goto (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ /* Branch over the next instruction if x0 <= x1. */
+ p += emit_bcond (p, LE, 8);
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = (p - buf) * 4;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+/* Implementation of emit_ops method "emit_ge_got". */
+
+static void
+aarch64_emit_ge_got (int *offset_p, int *size_p)
+{
+ uint32_t buf[16];
+ uint32_t *p = buf;
+
+ p += emit_pop (p, x1);
+ p += emit_cmp (p, x1, register_operand (x0));
+ /* Branch over the next instruction if x0 <= x1. */
+ p += emit_bcond (p, LT, 8);
+ /* The NOP instruction will be patched with an unconditional branch. */
+ if (offset_p)
+ *offset_p = (p - buf) * 4;
+ if (size_p)
+ *size_p = 4;
+ p += emit_nop (p);
+
+ emit_ops_insns (buf, p - buf);
+}
+
+static struct emit_ops aarch64_emit_ops_impl =
+{
+ aarch64_emit_prologue,
+ aarch64_emit_epilogue,
+ aarch64_emit_add,
+ aarch64_emit_sub,
+ aarch64_emit_mul,
+ aarch64_emit_lsh,
+ aarch64_emit_rsh_signed,
+ aarch64_emit_rsh_unsigned,
+ aarch64_emit_ext,
+ aarch64_emit_log_not,
+ aarch64_emit_bit_and,
+ aarch64_emit_bit_or,
+ aarch64_emit_bit_xor,
+ aarch64_emit_bit_not,
+ aarch64_emit_equal,
+ aarch64_emit_less_signed,
+ aarch64_emit_less_unsigned,
+ aarch64_emit_ref,
+ aarch64_emit_if_goto,
+ aarch64_emit_goto,
+ aarch64_write_goto_address,
+ aarch64_emit_const,
+ aarch64_emit_call,
+ aarch64_emit_reg,
+ aarch64_emit_pop,
+ aarch64_emit_stack_flush,
+ aarch64_emit_zero_ext,
+ aarch64_emit_swap,
+ aarch64_emit_stack_adjust,
+ aarch64_emit_int_call_1,
+ aarch64_emit_void_call_2,
+ aarch64_emit_eq_goto,
+ aarch64_emit_ne_goto,
+ aarch64_emit_lt_goto,
+ aarch64_emit_le_goto,
+ aarch64_emit_gt_goto,
+ aarch64_emit_ge_got,
+};
+
+/* Implementation of linux_target_ops method "emit_ops". */
+
+static struct emit_ops *
+aarch64_emit_ops (void)
+{
+ return &aarch64_emit_ops_impl;
+}
+
+/* Implementation of linux_target_ops method
+ "get_min_fast_tracepoint_insn_len". */
+
+static int
+aarch64_get_min_fast_tracepoint_insn_len (void)
+{
+ return 4;
+}
+
+/* Implementation of linux_target_ops method "supports_range_stepping". */
+
+static int
+aarch64_supports_range_stepping (void)
+{
+ return 1;
+}
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+aarch64_sw_breakpoint_from_kind (int kind, int *size)
+{
+ if (is_64bit_tdesc ())
+ {
+ *size = aarch64_breakpoint_len;
+ return aarch64_breakpoint;
+ }
+ else
+ return arm_sw_breakpoint_from_kind (kind, size);
+}
+
+/* Implementation of linux_target_ops method "breakpoint_kind_from_pc". */
+
+static int
+aarch64_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+ if (is_64bit_tdesc ())
+ return aarch64_breakpoint_len;
+ else
+ return arm_breakpoint_kind_from_pc (pcptr);
+}
+
+/* Implementation of the linux_target_ops method
+ "breakpoint_kind_from_current_state". */
+
+static int
+aarch64_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+ if (is_64bit_tdesc ())
+ return aarch64_breakpoint_len;
+ else
+ return arm_breakpoint_kind_from_current_state (pcptr);
+}
+
+/* Support for hardware single step. */
+
+static int
+aarch64_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+struct linux_target_ops the_low_target =
+{
+ aarch64_arch_setup,
+ aarch64_regs_info,
+ NULL, /* cannot_fetch_register */
+ NULL, /* cannot_store_register */
+ NULL, /* fetch_register */
+ aarch64_get_pc,
+ aarch64_set_pc,
+ aarch64_breakpoint_kind_from_pc,
+ aarch64_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
+ 0, /* decr_pc_after_break */
+ aarch64_breakpoint_at,
+ aarch64_supports_z_point_type,
+ aarch64_insert_point,
+ aarch64_remove_point,
+ aarch64_stopped_by_watchpoint,
+ aarch64_stopped_data_address,
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ aarch64_linux_siginfo_fixup,
+ aarch64_linux_new_process,
+ aarch64_linux_delete_process,
+ aarch64_linux_new_thread,
+ aarch64_linux_delete_thread,
+ aarch64_linux_new_fork,
+ aarch64_linux_prepare_to_resume,
+ NULL, /* process_qsupported */
+ aarch64_supports_tracepoints,
+ aarch64_get_thread_area,
+ aarch64_install_fast_tracepoint_jump_pad,
+ aarch64_emit_ops,
+ aarch64_get_min_fast_tracepoint_insn_len,
+ aarch64_supports_range_stepping,
+ aarch64_breakpoint_kind_from_current_state,
+ aarch64_supports_hardware_single_step,
+ aarch64_get_syscall_trapinfo,
+};
+
+void
+initialize_low_arch (void)
+{
+ initialize_low_arch_aarch32 ();
+
+ initialize_regsets_info (&aarch64_regsets_info);
+ initialize_regsets_info (&aarch64_sve_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/aarch64 specific target description, for the remote server
- for GDB.
- Copyright (C) 2017-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-
-#include "linux-aarch64-tdesc.h"
-
-#include "tdesc.h"
-#include "arch/aarch64.h"
-#include "linux-aarch32-low.h"
-#include <inttypes.h>
-
-/* All possible aarch64 target descriptors. */
-struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/];
-
-/* Create the aarch64 target description. */
-
-const target_desc *
-aarch64_linux_read_description (uint64_t vq, bool pauth_p)
-{
- if (vq > AARCH64_MAX_SVE_VQ)
- error (_("VQ is %" PRIu64 ", maximum supported value is %d"), vq,
- AARCH64_MAX_SVE_VQ);
-
- struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p];
-
- if (tdesc == NULL)
- {
- tdesc = aarch64_create_target_description (vq, pauth_p);
-
- static const char *expedite_regs_aarch64[] = { "x29", "sp", "pc", NULL };
- static const char *expedite_regs_aarch64_sve[] = { "x29", "sp", "pc",
- "vg", NULL };
-
- if (vq == 0)
- init_target_desc (tdesc, expedite_regs_aarch64);
- else
- init_target_desc (tdesc, expedite_regs_aarch64_sve);
-
- tdesc_aarch64_list[vq][pauth_p] = tdesc;
- }
-
- return tdesc;
-}
--- /dev/null
+/* GNU/Linux/aarch64 specific target description, for the remote server
+ for GDB.
+ Copyright (C) 2017-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+
+#include "linux-aarch64-tdesc.h"
+
+#include "tdesc.h"
+#include "arch/aarch64.h"
+#include "linux-aarch32-low.h"
+#include <inttypes.h>
+
+/* All possible aarch64 target descriptors. */
+struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/];
+
+/* Create the aarch64 target description. */
+
+const target_desc *
+aarch64_linux_read_description (uint64_t vq, bool pauth_p)
+{
+ if (vq > AARCH64_MAX_SVE_VQ)
+ error (_("VQ is %" PRIu64 ", maximum supported value is %d"), vq,
+ AARCH64_MAX_SVE_VQ);
+
+ struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p];
+
+ if (tdesc == NULL)
+ {
+ tdesc = aarch64_create_target_description (vq, pauth_p);
+
+ static const char *expedite_regs_aarch64[] = { "x29", "sp", "pc", NULL };
+ static const char *expedite_regs_aarch64_sve[] = { "x29", "sp", "pc",
+ "vg", NULL };
+
+ if (vq == 0)
+ init_target_desc (tdesc, expedite_regs_aarch64);
+ else
+ init_target_desc (tdesc, expedite_regs_aarch64_sve);
+
+ tdesc_aarch64_list[vq][pauth_p] = tdesc;
+ }
+
+ return tdesc;
+}
+++ /dev/null
-/* GNU/Linux/x86-64 specific low level interface, for the in-process
- agent library for GDB.
-
- Copyright (C) 2010-2020 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-x86-tdesc.h"
-#include "gdbsupport/x86-xstate.h"
-
-/* Defined in auto-generated file amd64-linux.c. */
-void init_registers_amd64_linux (void);
-extern const struct target_desc *tdesc_amd64_linux;
-
-/* fast tracepoints collect registers. */
-
-#define FT_CR_RIP 0
-#define FT_CR_EFLAGS 1
-#define FT_CR_R8 2
-#define FT_CR_R9 3
-#define FT_CR_R10 4
-#define FT_CR_R11 5
-#define FT_CR_R12 6
-#define FT_CR_R13 7
-#define FT_CR_R14 8
-#define FT_CR_R15 9
-#define FT_CR_RAX 10
-#define FT_CR_RBX 11
-#define FT_CR_RCX 12
-#define FT_CR_RDX 13
-#define FT_CR_RSI 14
-#define FT_CR_RDI 15
-#define FT_CR_RBP 16
-#define FT_CR_RSP 17
-
-static const int x86_64_ft_collect_regmap[] = {
- FT_CR_RAX * 8, FT_CR_RBX * 8, FT_CR_RCX * 8, FT_CR_RDX * 8,
- FT_CR_RSI * 8, FT_CR_RDI * 8, FT_CR_RBP * 8, FT_CR_RSP * 8,
- FT_CR_R8 * 8, FT_CR_R9 * 8, FT_CR_R10 * 8, FT_CR_R11 * 8,
- FT_CR_R12 * 8, FT_CR_R13 * 8, FT_CR_R14 * 8, FT_CR_R15 * 8,
- FT_CR_RIP * 8, FT_CR_EFLAGS * 8
-};
-
-#define X86_64_NUM_FT_COLLECT_GREGS \
- (sizeof (x86_64_ft_collect_regmap) / sizeof(x86_64_ft_collect_regmap[0]))
-
-void
-supply_fast_tracepoint_registers (struct regcache *regcache,
- const unsigned char *buf)
-{
- int i;
-
- for (i = 0; i < X86_64_NUM_FT_COLLECT_GREGS; i++)
- supply_register (regcache, i,
- ((char *) buf) + x86_64_ft_collect_regmap[i]);
-}
-
-ULONGEST
-get_raw_reg (const unsigned char *raw_regs, int regnum)
-{
- if (regnum >= X86_64_NUM_FT_COLLECT_GREGS)
- return 0;
-
- return *(ULONGEST *) (raw_regs + x86_64_ft_collect_regmap[regnum]);
-}
-
-#ifdef HAVE_UST
-
-#include <ust/processor.h>
-
-/* "struct registers" is the UST object type holding the registers at
- the time of the static tracepoint marker call. This doesn't
- contain RIP, but we know what it must have been (the marker
- address). */
-
-#define ST_REGENTRY(REG) \
- { \
- offsetof (struct registers, REG), \
- sizeof (((struct registers *) NULL)->REG) \
- }
-
-static struct
-{
- int offset;
- int size;
-} x86_64_st_collect_regmap[] =
- {
- ST_REGENTRY(rax),
- ST_REGENTRY(rbx),
- ST_REGENTRY(rcx),
- ST_REGENTRY(rdx),
- ST_REGENTRY(rsi),
- ST_REGENTRY(rdi),
- ST_REGENTRY(rbp),
- ST_REGENTRY(rsp),
- ST_REGENTRY(r8),
- ST_REGENTRY(r9),
- ST_REGENTRY(r10),
- ST_REGENTRY(r11),
- ST_REGENTRY(r12),
- ST_REGENTRY(r13),
- ST_REGENTRY(r14),
- ST_REGENTRY(r15),
- { -1, 0 },
- ST_REGENTRY(rflags),
- ST_REGENTRY(cs),
- ST_REGENTRY(ss),
- };
-
-#define X86_64_NUM_ST_COLLECT_GREGS \
- (sizeof (x86_64_st_collect_regmap) / sizeof (x86_64_st_collect_regmap[0]))
-
-/* GDB's RIP register number. */
-#define AMD64_RIP_REGNUM 16
-
-void
-supply_static_tracepoint_registers (struct regcache *regcache,
- const unsigned char *buf,
- CORE_ADDR pc)
-{
- int i;
- unsigned long newpc = pc;
-
- supply_register (regcache, AMD64_RIP_REGNUM, &newpc);
-
- for (i = 0; i < X86_64_NUM_ST_COLLECT_GREGS; i++)
- if (x86_64_st_collect_regmap[i].offset != -1)
- {
- switch (x86_64_st_collect_regmap[i].size)
- {
- case 8:
- supply_register (regcache, i,
- ((char *) buf)
- + x86_64_st_collect_regmap[i].offset);
- break;
- case 2:
- {
- unsigned long reg
- = * (short *) (((char *) buf)
- + x86_64_st_collect_regmap[i].offset);
- reg &= 0xffff;
- supply_register (regcache, i, ®);
- }
- break;
- default:
- internal_error (__FILE__, __LINE__,
- "unhandled register size: %d",
- x86_64_st_collect_regmap[i].size);
- break;
- }
- }
-}
-
-#endif /* HAVE_UST */
-
-#if !defined __ILP32__
-/* Map the tdesc index to xcr0 mask. */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
- X86_XSTATE_X87_MASK,
- X86_XSTATE_SSE_MASK,
- X86_XSTATE_AVX_MASK,
- X86_XSTATE_MPX_MASK,
- X86_XSTATE_AVX_MPX_MASK,
- X86_XSTATE_AVX_AVX512_MASK,
- X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-#endif
-
-/* Return target_desc to use for IPA, given the tdesc index passed by
- gdbserver. */
-
-const struct target_desc *
-get_ipa_tdesc (int idx)
-{
- if (idx >= X86_TDESC_LAST)
- {
- internal_error (__FILE__, __LINE__,
- "unknown ipa tdesc index: %d", idx);
- }
-
-#if defined __ILP32__
- switch (idx)
- {
- case X86_TDESC_SSE:
- return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
- case X86_TDESC_AVX:
- return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
- case X86_TDESC_AVX_AVX512:
- return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
- default:
- break;
- }
-#else
- return amd64_linux_read_description (idx2mask[idx], false);
-#endif
-
- internal_error (__FILE__, __LINE__,
- "unknown ipa tdesc index: %d", idx);
-}
-
-/* Allocate buffer for the jump pads. The branch instruction has a
- reach of +/- 31-bit, and the executable is loaded at low addresses.
-
- 64-bit: Use MAP_32BIT to allocate in the first 2GB. Shared
- libraries, being allocated at the top, are unfortunately out of
- luck.
-
- x32: Since MAP_32BIT is 64-bit only, do the placement manually.
- Try allocating at '0x80000000 - SIZE' initially, decreasing until
- we hit a free area. This ensures the executable is fully covered,
- and is as close as possible to the shared libraries, which are
- usually mapped at the top of the first 4GB of the address space.
-*/
-
-void *
-alloc_jump_pad_buffer (size_t size)
-{
-#if __ILP32__
- uintptr_t addr;
- int pagesize;
-
- pagesize = sysconf (_SC_PAGE_SIZE);
- if (pagesize == -1)
- perror_with_name ("sysconf");
-
- addr = 0x80000000 - 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)
- {
- void *res;
-
- /* 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 *res = mmap (NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
-
- if (res == MAP_FAILED)
- return NULL;
-
- return res;
-#endif
-}
-
-void
-initialize_low_tracepoint (void)
-{
-#if defined __ILP32__
- amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
- amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
- amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
-#else
- for (auto i = 0; i < X86_TDESC_LAST; i++)
- amd64_linux_read_description (idx2mask[i], false);
-#endif
-}
--- /dev/null
+/* GNU/Linux/x86-64 specific low level interface, for the in-process
+ agent library for GDB.
+
+ Copyright (C) 2010-2020 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-x86-tdesc.h"
+#include "gdbsupport/x86-xstate.h"
+
+/* Defined in auto-generated file amd64-linux.c. */
+void init_registers_amd64_linux (void);
+extern const struct target_desc *tdesc_amd64_linux;
+
+/* fast tracepoints collect registers. */
+
+#define FT_CR_RIP 0
+#define FT_CR_EFLAGS 1
+#define FT_CR_R8 2
+#define FT_CR_R9 3
+#define FT_CR_R10 4
+#define FT_CR_R11 5
+#define FT_CR_R12 6
+#define FT_CR_R13 7
+#define FT_CR_R14 8
+#define FT_CR_R15 9
+#define FT_CR_RAX 10
+#define FT_CR_RBX 11
+#define FT_CR_RCX 12
+#define FT_CR_RDX 13
+#define FT_CR_RSI 14
+#define FT_CR_RDI 15
+#define FT_CR_RBP 16
+#define FT_CR_RSP 17
+
+static const int x86_64_ft_collect_regmap[] = {
+ FT_CR_RAX * 8, FT_CR_RBX * 8, FT_CR_RCX * 8, FT_CR_RDX * 8,
+ FT_CR_RSI * 8, FT_CR_RDI * 8, FT_CR_RBP * 8, FT_CR_RSP * 8,
+ FT_CR_R8 * 8, FT_CR_R9 * 8, FT_CR_R10 * 8, FT_CR_R11 * 8,
+ FT_CR_R12 * 8, FT_CR_R13 * 8, FT_CR_R14 * 8, FT_CR_R15 * 8,
+ FT_CR_RIP * 8, FT_CR_EFLAGS * 8
+};
+
+#define X86_64_NUM_FT_COLLECT_GREGS \
+ (sizeof (x86_64_ft_collect_regmap) / sizeof(x86_64_ft_collect_regmap[0]))
+
+void
+supply_fast_tracepoint_registers (struct regcache *regcache,
+ const unsigned char *buf)
+{
+ int i;
+
+ for (i = 0; i < X86_64_NUM_FT_COLLECT_GREGS; i++)
+ supply_register (regcache, i,
+ ((char *) buf) + x86_64_ft_collect_regmap[i]);
+}
+
+ULONGEST
+get_raw_reg (const unsigned char *raw_regs, int regnum)
+{
+ if (regnum >= X86_64_NUM_FT_COLLECT_GREGS)
+ return 0;
+
+ return *(ULONGEST *) (raw_regs + x86_64_ft_collect_regmap[regnum]);
+}
+
+#ifdef HAVE_UST
+
+#include <ust/processor.h>
+
+/* "struct registers" is the UST object type holding the registers at
+ the time of the static tracepoint marker call. This doesn't
+ contain RIP, but we know what it must have been (the marker
+ address). */
+
+#define ST_REGENTRY(REG) \
+ { \
+ offsetof (struct registers, REG), \
+ sizeof (((struct registers *) NULL)->REG) \
+ }
+
+static struct
+{
+ int offset;
+ int size;
+} x86_64_st_collect_regmap[] =
+ {
+ ST_REGENTRY(rax),
+ ST_REGENTRY(rbx),
+ ST_REGENTRY(rcx),
+ ST_REGENTRY(rdx),
+ ST_REGENTRY(rsi),
+ ST_REGENTRY(rdi),
+ ST_REGENTRY(rbp),
+ ST_REGENTRY(rsp),
+ ST_REGENTRY(r8),
+ ST_REGENTRY(r9),
+ ST_REGENTRY(r10),
+ ST_REGENTRY(r11),
+ ST_REGENTRY(r12),
+ ST_REGENTRY(r13),
+ ST_REGENTRY(r14),
+ ST_REGENTRY(r15),
+ { -1, 0 },
+ ST_REGENTRY(rflags),
+ ST_REGENTRY(cs),
+ ST_REGENTRY(ss),
+ };
+
+#define X86_64_NUM_ST_COLLECT_GREGS \
+ (sizeof (x86_64_st_collect_regmap) / sizeof (x86_64_st_collect_regmap[0]))
+
+/* GDB's RIP register number. */
+#define AMD64_RIP_REGNUM 16
+
+void
+supply_static_tracepoint_registers (struct regcache *regcache,
+ const unsigned char *buf,
+ CORE_ADDR pc)
+{
+ int i;
+ unsigned long newpc = pc;
+
+ supply_register (regcache, AMD64_RIP_REGNUM, &newpc);
+
+ for (i = 0; i < X86_64_NUM_ST_COLLECT_GREGS; i++)
+ if (x86_64_st_collect_regmap[i].offset != -1)
+ {
+ switch (x86_64_st_collect_regmap[i].size)
+ {
+ case 8:
+ supply_register (regcache, i,
+ ((char *) buf)
+ + x86_64_st_collect_regmap[i].offset);
+ break;
+ case 2:
+ {
+ unsigned long reg
+ = * (short *) (((char *) buf)
+ + x86_64_st_collect_regmap[i].offset);
+ reg &= 0xffff;
+ supply_register (regcache, i, ®);
+ }
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ "unhandled register size: %d",
+ x86_64_st_collect_regmap[i].size);
+ break;
+ }
+ }
+}
+
+#endif /* HAVE_UST */
+
+#if !defined __ILP32__
+/* Map the tdesc index to xcr0 mask. */
+static uint64_t idx2mask[X86_TDESC_LAST] = {
+ X86_XSTATE_X87_MASK,
+ X86_XSTATE_SSE_MASK,
+ X86_XSTATE_AVX_MASK,
+ X86_XSTATE_MPX_MASK,
+ X86_XSTATE_AVX_MPX_MASK,
+ X86_XSTATE_AVX_AVX512_MASK,
+ X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
+};
+#endif
+
+/* Return target_desc to use for IPA, given the tdesc index passed by
+ gdbserver. */
+
+const struct target_desc *
+get_ipa_tdesc (int idx)
+{
+ if (idx >= X86_TDESC_LAST)
+ {
+ internal_error (__FILE__, __LINE__,
+ "unknown ipa tdesc index: %d", idx);
+ }
+
+#if defined __ILP32__
+ switch (idx)
+ {
+ case X86_TDESC_SSE:
+ return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
+ case X86_TDESC_AVX:
+ return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
+ case X86_TDESC_AVX_AVX512:
+ return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
+ default:
+ break;
+ }
+#else
+ return amd64_linux_read_description (idx2mask[idx], false);
+#endif
+
+ internal_error (__FILE__, __LINE__,
+ "unknown ipa tdesc index: %d", idx);
+}
+
+/* Allocate buffer for the jump pads. The branch instruction has a
+ reach of +/- 31-bit, and the executable is loaded at low addresses.
+
+ 64-bit: Use MAP_32BIT to allocate in the first 2GB. Shared
+ libraries, being allocated at the top, are unfortunately out of
+ luck.
+
+ x32: Since MAP_32BIT is 64-bit only, do the placement manually.
+ Try allocating at '0x80000000 - SIZE' initially, decreasing until
+ we hit a free area. This ensures the executable is fully covered,
+ and is as close as possible to the shared libraries, which are
+ usually mapped at the top of the first 4GB of the address space.
+*/
+
+void *
+alloc_jump_pad_buffer (size_t size)
+{
+#if __ILP32__
+ uintptr_t addr;
+ int pagesize;
+
+ pagesize = sysconf (_SC_PAGE_SIZE);
+ if (pagesize == -1)
+ perror_with_name ("sysconf");
+
+ addr = 0x80000000 - 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)
+ {
+ void *res;
+
+ /* 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 *res = mmap (NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
+
+ if (res == MAP_FAILED)
+ return NULL;
+
+ return res;
+#endif
+}
+
+void
+initialize_low_tracepoint (void)
+{
+#if defined __ILP32__
+ amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
+ amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
+ amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
+#else
+ for (auto i = 0; i < X86_TDESC_LAST; i++)
+ amd64_linux_read_description (idx2mask[i], false);
+#endif
+}
+++ /dev/null
-/* GNU/Linux/ARM specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "arch/arm.h"
-#include "arch/arm-linux.h"
-#include "arch/arm-get-next-pcs.h"
-#include "linux-aarch32-low.h"
-#include "linux-aarch32-tdesc.h"
-#include "linux-arm-tdesc.h"
-
-#include <sys/uio.h>
-/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
- On Bionic elf.h and linux/elf.h have conflicting definitions. */
-#ifndef ELFMAG0
-#include <elf.h>
-#endif
-#include "nat/gdb_ptrace.h"
-#include <signal.h>
-#include <sys/syscall.h>
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 22
-#endif
-
-#ifndef PTRACE_GETWMMXREGS
-# define PTRACE_GETWMMXREGS 18
-# define PTRACE_SETWMMXREGS 19
-#endif
-
-#ifndef PTRACE_GETVFPREGS
-# define PTRACE_GETVFPREGS 27
-# define PTRACE_SETVFPREGS 28
-#endif
-
-#ifndef PTRACE_GETHBPREGS
-#define PTRACE_GETHBPREGS 29
-#define PTRACE_SETHBPREGS 30
-#endif
-
-/* Information describing the hardware breakpoint capabilities. */
-static struct
-{
- unsigned char arch;
- unsigned char max_wp_length;
- unsigned char wp_count;
- unsigned char bp_count;
-} arm_linux_hwbp_cap;
-
-/* Enum describing the different types of ARM hardware break-/watch-points. */
-typedef enum
-{
- arm_hwbp_break = 0,
- arm_hwbp_load = 1,
- arm_hwbp_store = 2,
- arm_hwbp_access = 3
-} arm_hwbp_type;
-
-/* Type describing an ARM Hardware Breakpoint Control register value. */
-typedef unsigned int arm_hwbp_control_t;
-
-/* Structure used to keep track of hardware break-/watch-points. */
-struct arm_linux_hw_breakpoint
-{
- /* Address to break on, or being watched. */
- unsigned int address;
- /* Control register for break-/watch- point. */
- arm_hwbp_control_t control;
-};
-
-/* Since we cannot dynamically allocate subfields of arch_process_info,
- assume a maximum number of supported break-/watchpoints. */
-#define MAX_BPTS 32
-#define MAX_WPTS 32
-
-/* Per-process arch-specific data we want to keep. */
-struct arch_process_info
-{
- /* Hardware breakpoints for this process. */
- struct arm_linux_hw_breakpoint bpts[MAX_BPTS];
- /* Hardware watchpoints for this process. */
- struct arm_linux_hw_breakpoint wpts[MAX_WPTS];
-};
-
-/* Per-thread arch-specific data we want to keep. */
-struct arch_lwp_info
-{
- /* Non-zero if our copy differs from what's recorded in the thread. */
- char bpts_changed[MAX_BPTS];
- char wpts_changed[MAX_WPTS];
- /* Cached stopped data address. */
- CORE_ADDR stopped_data_address;
-};
-
-/* These are in <asm/elf.h> in current kernels. */
-#define HWCAP_VFP 64
-#define HWCAP_IWMMXT 512
-#define HWCAP_NEON 4096
-#define HWCAP_VFPv3 8192
-#define HWCAP_VFPv3D16 16384
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-#define arm_num_regs 26
-
-static int arm_regmap[] = {
- 0, 4, 8, 12, 16, 20, 24, 28,
- 32, 36, 40, 44, 48, 52, 56, 60,
- -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 64
-};
-
-/* Forward declarations needed for get_next_pcs ops. */
-static ULONGEST get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
- int len,
- int byte_order);
-
-static CORE_ADDR get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
- CORE_ADDR val);
-
-static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self);
-
-static int get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
-
-/* get_next_pcs operations. */
-static struct arm_get_next_pcs_ops get_next_pcs_ops = {
- get_next_pcs_read_memory_unsigned_integer,
- get_next_pcs_syscall_next_pc,
- get_next_pcs_addr_bits_remove,
- get_next_pcs_is_thumb,
- arm_linux_get_next_pcs_fixup,
-};
-
-static int
-arm_cannot_store_register (int regno)
-{
- return (regno >= arm_num_regs);
-}
-
-static int
-arm_cannot_fetch_register (int regno)
-{
- return (regno >= arm_num_regs);
-}
-
-static void
-arm_fill_wmmxregset (struct regcache *regcache, void *buf)
-{
- if (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT)
- return;
-
- for (int i = 0; i < 16; i++)
- collect_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
-
- /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */
- for (int i = 0; i < 6; i++)
- collect_register (regcache, arm_num_regs + i + 16,
- (char *) buf + 16 * 8 + i * 4);
-}
-
-static void
-arm_store_wmmxregset (struct regcache *regcache, const void *buf)
-{
- if (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT)
- return;
-
- for (int i = 0; i < 16; i++)
- supply_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
-
- /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */
- for (int i = 0; i < 6; i++)
- supply_register (regcache, arm_num_regs + i + 16,
- (char *) buf + 16 * 8 + i * 4);
-}
-
-static void
-arm_fill_vfpregset (struct regcache *regcache, void *buf)
-{
- int num;
-
- if (is_aarch32_linux_description (regcache->tdesc))
- num = 32;
- else
- {
- arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc);
-
- if (fp_type == ARM_FP_TYPE_VFPV3)
- num = 32;
- else if (fp_type == ARM_FP_TYPE_VFPV2)
- num = 16;
- else
- return;
- }
-
- arm_fill_vfpregset_num (regcache, buf, num);
-}
-
-/* Wrapper of UNMAKE_THUMB_ADDR for get_next_pcs. */
-static CORE_ADDR
-get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
-{
- return UNMAKE_THUMB_ADDR (val);
-}
-
-static void
-arm_store_vfpregset (struct regcache *regcache, const void *buf)
-{
- int num;
-
- if (is_aarch32_linux_description (regcache->tdesc))
- num = 32;
- else
- {
- arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc);
-
- if (fp_type == ARM_FP_TYPE_VFPV3)
- num = 32;
- else if (fp_type == ARM_FP_TYPE_VFPV2)
- num = 16;
- else
- return;
- }
-
- arm_store_vfpregset_num (regcache, buf, num);
-}
-
-/* Wrapper of arm_is_thumb_mode for get_next_pcs. */
-static int
-get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
-{
- return arm_is_thumb_mode ();
-}
-
-/* Read memory from the inferior.
- BYTE_ORDER is ignored and there to keep compatiblity with GDB's
- read_memory_unsigned_integer. */
-static ULONGEST
-get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
- int len,
- int byte_order)
-{
- ULONGEST res;
-
- res = 0;
- target_read_memory (memaddr, (unsigned char *) &res, len);
-
- return res;
-}
-
-/* Fetch the thread-local storage pointer for libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
- return PS_ERR;
-
- /* IDX is the bias from the thread pointer to the beginning of the
- thread descriptor. It has to be subtracted due to implementation
- quirks in libthread_db. */
- *base = (void *) ((char *)*base - idx);
-
- return PS_OK;
-}
-
-
-/* Query Hardware Breakpoint information for the target we are attached to
- (using PID as ptrace argument) and set up arm_linux_hwbp_cap. */
-static void
-arm_linux_init_hwbp_cap (int pid)
-{
- unsigned int val;
-
- if (ptrace (PTRACE_GETHBPREGS, pid, 0, &val) < 0)
- return;
-
- arm_linux_hwbp_cap.arch = (unsigned char)((val >> 24) & 0xff);
- if (arm_linux_hwbp_cap.arch == 0)
- return;
-
- arm_linux_hwbp_cap.max_wp_length = (unsigned char)((val >> 16) & 0xff);
- arm_linux_hwbp_cap.wp_count = (unsigned char)((val >> 8) & 0xff);
- arm_linux_hwbp_cap.bp_count = (unsigned char)(val & 0xff);
-
- if (arm_linux_hwbp_cap.wp_count > MAX_WPTS)
- internal_error (__FILE__, __LINE__, "Unsupported number of watchpoints");
- if (arm_linux_hwbp_cap.bp_count > MAX_BPTS)
- internal_error (__FILE__, __LINE__, "Unsupported number of breakpoints");
-}
-
-/* How many hardware breakpoints are available? */
-static int
-arm_linux_get_hw_breakpoint_count (void)
-{
- return arm_linux_hwbp_cap.bp_count;
-}
-
-/* How many hardware watchpoints are available? */
-static int
-arm_linux_get_hw_watchpoint_count (void)
-{
- return arm_linux_hwbp_cap.wp_count;
-}
-
-/* Maximum length of area watched by hardware watchpoint. */
-static int
-arm_linux_get_hw_watchpoint_max_length (void)
-{
- return arm_linux_hwbp_cap.max_wp_length;
-}
-
-/* Initialize an ARM hardware break-/watch-point control register value.
- BYTE_ADDRESS_SELECT is the mask of bytes to trigger on; HWBP_TYPE is the
- type of break-/watch-point; ENABLE indicates whether the point is enabled.
- */
-static arm_hwbp_control_t
-arm_hwbp_control_initialize (unsigned byte_address_select,
- arm_hwbp_type hwbp_type,
- int enable)
-{
- gdb_assert ((byte_address_select & ~0xffU) == 0);
- gdb_assert (hwbp_type != arm_hwbp_break
- || ((byte_address_select & 0xfU) != 0));
-
- return (byte_address_select << 5) | (hwbp_type << 3) | (3 << 1) | enable;
-}
-
-/* Does the breakpoint control value CONTROL have the enable bit set? */
-static int
-arm_hwbp_control_is_enabled (arm_hwbp_control_t control)
-{
- return control & 0x1;
-}
-
-/* Is the breakpoint control value CONTROL initialized? */
-static int
-arm_hwbp_control_is_initialized (arm_hwbp_control_t control)
-{
- return control != 0;
-}
-
-/* Change a breakpoint control word so that it is in the disabled state. */
-static arm_hwbp_control_t
-arm_hwbp_control_disable (arm_hwbp_control_t control)
-{
- return control & ~0x1;
-}
-
-/* Are two break-/watch-points equal? */
-static int
-arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1,
- const struct arm_linux_hw_breakpoint *p2)
-{
- return p1->address == p2->address && p1->control == p2->control;
-}
-
-/* Convert a raw breakpoint type to an enum arm_hwbp_type. */
-
-static arm_hwbp_type
-raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type)
-{
- switch (raw_type)
- {
- case raw_bkpt_type_hw:
- return arm_hwbp_break;
- case raw_bkpt_type_write_wp:
- return arm_hwbp_store;
- case raw_bkpt_type_read_wp:
- return arm_hwbp_load;
- case raw_bkpt_type_access_wp:
- return arm_hwbp_access;
- default:
- gdb_assert_not_reached ("unhandled raw type");
- }
-}
-
-/* Initialize the hardware breakpoint structure P for a breakpoint or
- watchpoint at ADDR to LEN. The type of watchpoint is given in TYPE.
- Returns -1 if TYPE is unsupported, or -2 if the particular combination
- of ADDR and LEN cannot be implemented. Otherwise, returns 0 if TYPE
- represents a breakpoint and 1 if type represents a watchpoint. */
-static int
-arm_linux_hw_point_initialize (enum raw_bkpt_type raw_type, CORE_ADDR addr,
- int len, struct arm_linux_hw_breakpoint *p)
-{
- arm_hwbp_type hwbp_type;
- unsigned mask;
-
- hwbp_type = raw_bkpt_type_to_arm_hwbp_type (raw_type);
-
- if (hwbp_type == arm_hwbp_break)
- {
- /* For breakpoints, the length field encodes the mode. */
- switch (len)
- {
- case 2: /* 16-bit Thumb mode breakpoint */
- case 3: /* 32-bit Thumb mode breakpoint */
- mask = 0x3;
- addr &= ~1;
- break;
- case 4: /* 32-bit ARM mode breakpoint */
- mask = 0xf;
- addr &= ~3;
- break;
- default:
- /* Unsupported. */
- return -2;
- }
- }
- else
- {
- CORE_ADDR max_wp_length = arm_linux_get_hw_watchpoint_max_length ();
- CORE_ADDR aligned_addr;
-
- /* Can not set watchpoints for zero or negative lengths. */
- if (len <= 0)
- return -2;
- /* The current ptrace interface can only handle watchpoints that are a
- power of 2. */
- if ((len & (len - 1)) != 0)
- return -2;
-
- /* Test that the range [ADDR, ADDR + LEN) fits into the largest address
- range covered by a watchpoint. */
- aligned_addr = addr & ~(max_wp_length - 1);
- if (aligned_addr + max_wp_length < addr + len)
- return -2;
-
- mask = (1 << len) - 1;
- }
-
- p->address = (unsigned int) addr;
- p->control = arm_hwbp_control_initialize (mask, hwbp_type, 1);
-
- return hwbp_type != arm_hwbp_break;
-}
-
-/* Callback to mark a watch-/breakpoint to be updated in all threads of
- the current process. */
-
-static void
-update_registers_callback (thread_info *thread, int watch, int i)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- /* The actual update is done later just before resuming the lwp,
- we just mark that the registers need updating. */
- if (watch)
- lwp->arch_private->wpts_changed[i] = 1;
- else
- lwp->arch_private->bpts_changed[i] = 1;
-
- /* If the lwp isn't stopped, force it to momentarily pause, so
- we can update its breakpoint registers. */
- if (!lwp->stopped)
- linux_stop_lwp (lwp);
-}
-
-static int
-arm_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_SW_BP:
- case Z_PACKET_HW_BP:
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_READ_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
- /* Leave the handling of sw breakpoints with the gdb client. */
- return 0;
- }
-}
-
-/* Insert hardware break-/watchpoint. */
-static int
-arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
-{
- struct process_info *proc = current_process ();
- struct arm_linux_hw_breakpoint p, *pts;
- int watch, i, count;
-
- watch = arm_linux_hw_point_initialize (type, addr, len, &p);
- if (watch < 0)
- {
- /* Unsupported. */
- return watch == -1 ? 1 : -1;
- }
-
- if (watch)
- {
- count = arm_linux_get_hw_watchpoint_count ();
- pts = proc->priv->arch_private->wpts;
- }
- else
- {
- count = arm_linux_get_hw_breakpoint_count ();
- pts = proc->priv->arch_private->bpts;
- }
-
- for (i = 0; i < count; i++)
- if (!arm_hwbp_control_is_enabled (pts[i].control))
- {
- pts[i] = p;
-
- /* Only update the threads of the current process. */
- for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
- {
- update_registers_callback (thread, watch, i);
- });
-
- return 0;
- }
-
- /* We're out of watchpoints. */
- return -1;
-}
-
-/* Remove hardware break-/watchpoint. */
-static int
-arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
-{
- struct process_info *proc = current_process ();
- struct arm_linux_hw_breakpoint p, *pts;
- int watch, i, count;
-
- watch = arm_linux_hw_point_initialize (type, addr, len, &p);
- if (watch < 0)
- {
- /* Unsupported. */
- return -1;
- }
-
- if (watch)
- {
- count = arm_linux_get_hw_watchpoint_count ();
- pts = proc->priv->arch_private->wpts;
- }
- else
- {
- count = arm_linux_get_hw_breakpoint_count ();
- pts = proc->priv->arch_private->bpts;
- }
-
- for (i = 0; i < count; i++)
- if (arm_linux_hw_breakpoint_equal (&p, pts + i))
- {
- pts[i].control = arm_hwbp_control_disable (pts[i].control);
-
- /* Only update the threads of the current process. */
- for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
- {
- update_registers_callback (thread, watch, i);
- });
-
- return 0;
- }
-
- /* No watchpoint matched. */
- return -1;
-}
-
-/* Return whether current thread is stopped due to a watchpoint. */
-static int
-arm_stopped_by_watchpoint (void)
-{
- struct lwp_info *lwp = get_thread_lwp (current_thread);
- siginfo_t siginfo;
-
- /* We must be able to set hardware watchpoints. */
- if (arm_linux_get_hw_watchpoint_count () == 0)
- return 0;
-
- /* Retrieve siginfo. */
- errno = 0;
- ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo);
- if (errno != 0)
- return 0;
-
- /* This must be a hardware breakpoint. */
- if (siginfo.si_signo != SIGTRAP
- || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
- return 0;
-
- /* If we are in a positive slot then we're looking at a breakpoint and not
- a watchpoint. */
- if (siginfo.si_errno >= 0)
- return 0;
-
- /* Cache stopped data address for use by arm_stopped_data_address. */
- lwp->arch_private->stopped_data_address
- = (CORE_ADDR) (uintptr_t) siginfo.si_addr;
-
- return 1;
-}
-
-/* Return data address that triggered watchpoint. Called only if
- arm_stopped_by_watchpoint returned true. */
-static CORE_ADDR
-arm_stopped_data_address (void)
-{
- struct lwp_info *lwp = get_thread_lwp (current_thread);
- return lwp->arch_private->stopped_data_address;
-}
-
-/* Called when a new process is created. */
-static struct arch_process_info *
-arm_new_process (void)
-{
- struct arch_process_info *info = XCNEW (struct arch_process_info);
- return info;
-}
-
-/* Called when a process is being deleted. */
-
-static void
-arm_delete_process (struct arch_process_info *info)
-{
- xfree (info);
-}
-
-/* Called when a new thread is detected. */
-static void
-arm_new_thread (struct lwp_info *lwp)
-{
- struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
- int i;
-
- for (i = 0; i < MAX_BPTS; i++)
- info->bpts_changed[i] = 1;
- for (i = 0; i < MAX_WPTS; i++)
- info->wpts_changed[i] = 1;
-
- lwp->arch_private = info;
-}
-
-/* Function to call when a thread is being deleted. */
-
-static void
-arm_delete_thread (struct arch_lwp_info *arch_lwp)
-{
- xfree (arch_lwp);
-}
-
-static void
-arm_new_fork (struct process_info *parent, struct process_info *child)
-{
- struct arch_process_info *parent_proc_info;
- struct arch_process_info *child_proc_info;
- struct lwp_info *child_lwp;
- struct arch_lwp_info *child_lwp_info;
- int i;
-
- /* These are allocated by linux_add_process. */
- gdb_assert (parent->priv != NULL
- && parent->priv->arch_private != NULL);
- gdb_assert (child->priv != NULL
- && child->priv->arch_private != NULL);
-
- parent_proc_info = parent->priv->arch_private;
- child_proc_info = child->priv->arch_private;
-
- /* Linux kernel before 2.6.33 commit
- 72f674d203cd230426437cdcf7dd6f681dad8b0d
- will inherit hardware debug registers from parent
- on fork/vfork/clone. Newer Linux kernels create such tasks with
- zeroed debug registers.
-
- GDB core assumes the child inherits the watchpoints/hw
- breakpoints of the parent, and will remove them all from the
- forked off process. Copy the debug registers mirrors into the
- new process so that all breakpoints and watchpoints can be
- removed together. The debug registers mirror will become zeroed
- in the end before detaching the forked off process, thus making
- this compatible with older Linux kernels too. */
-
- *child_proc_info = *parent_proc_info;
-
- /* Mark all the hardware breakpoints and watchpoints as changed to
- make sure that the registers will be updated. */
- child_lwp = find_lwp_pid (ptid_t (child->pid));
- child_lwp_info = child_lwp->arch_private;
- for (i = 0; i < MAX_BPTS; i++)
- child_lwp_info->bpts_changed[i] = 1;
- for (i = 0; i < MAX_WPTS; i++)
- child_lwp_info->wpts_changed[i] = 1;
-}
-
-/* Called when resuming a thread.
- If the debug regs have changed, update the thread's copies. */
-static void
-arm_prepare_to_resume (struct lwp_info *lwp)
-{
- struct thread_info *thread = get_lwp_thread (lwp);
- int pid = lwpid_of (thread);
- struct process_info *proc = find_process_pid (pid_of (thread));
- struct arch_process_info *proc_info = proc->priv->arch_private;
- struct arch_lwp_info *lwp_info = lwp->arch_private;
- int i;
-
- for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++)
- if (lwp_info->bpts_changed[i])
- {
- errno = 0;
-
- if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) ((i << 1) + 1),
- &proc_info->bpts[i].address) < 0)
- perror_with_name ("Unexpected error setting breakpoint address");
-
- if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) ((i << 1) + 2),
- &proc_info->bpts[i].control) < 0)
- perror_with_name ("Unexpected error setting breakpoint");
-
- lwp_info->bpts_changed[i] = 0;
- }
-
- for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++)
- if (lwp_info->wpts_changed[i])
- {
- errno = 0;
-
- if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) -((i << 1) + 1),
- &proc_info->wpts[i].address) < 0)
- perror_with_name ("Unexpected error setting watchpoint address");
-
- if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) -((i << 1) + 2),
- &proc_info->wpts[i].control) < 0)
- perror_with_name ("Unexpected error setting watchpoint");
-
- lwp_info->wpts_changed[i] = 0;
- }
-}
-
-/* Find the next pc for a sigreturn or rt_sigreturn syscall. In
- addition, set IS_THUMB depending on whether we will return to ARM
- or Thumb code.
- See arm-linux.h for stack layout details. */
-static CORE_ADDR
-arm_sigreturn_next_pc (struct regcache *regcache, int svc_number,
- int *is_thumb)
-{
- unsigned long sp;
- unsigned long sp_data;
- /* Offset of PC register. */
- int pc_offset = 0;
- CORE_ADDR next_pc = 0;
- uint32_t cpsr;
-
- gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn);
-
- collect_register_by_name (regcache, "sp", &sp);
- (*the_target->read_memory) (sp, (unsigned char *) &sp_data, 4);
-
- pc_offset = arm_linux_sigreturn_next_pc_offset
- (sp, sp_data, svc_number, __NR_sigreturn == svc_number ? 1 : 0);
-
- (*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4);
-
- /* Set IS_THUMB according the CPSR saved on the stack. */
- (*the_target->read_memory) (sp + pc_offset + 4, (unsigned char *) &cpsr, 4);
- *is_thumb = ((cpsr & CPSR_T) != 0);
-
- return next_pc;
-}
-
-/* When PC is at a syscall instruction, return the PC of the next
- instruction to be executed. */
-static CORE_ADDR
-get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
-{
- CORE_ADDR next_pc = 0;
- CORE_ADDR pc = regcache_read_pc (self->regcache);
- int is_thumb = arm_is_thumb_mode ();
- ULONGEST svc_number = 0;
- struct regcache *regcache = self->regcache;
-
- if (is_thumb)
- {
- collect_register (regcache, 7, &svc_number);
- next_pc = pc + 2;
- }
- else
- {
- unsigned long this_instr;
- unsigned long svc_operand;
-
- target_read_memory (pc, (unsigned char *) &this_instr, 4);
- svc_operand = (0x00ffffff & this_instr);
-
- if (svc_operand) /* OABI. */
- {
- svc_number = svc_operand - 0x900000;
- }
- else /* EABI. */
- {
- collect_register (regcache, 7, &svc_number);
- }
-
- next_pc = pc + 4;
- }
-
- /* This is a sigreturn or sigreturn_rt syscall. */
- if (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn)
- {
- /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
- update IS_THUMB. */
- next_pc = arm_sigreturn_next_pc (regcache, svc_number, &is_thumb);
- }
-
- /* Addresses for calling Thumb functions have the bit 0 set. */
- if (is_thumb)
- next_pc = MAKE_THUMB_ADDR (next_pc);
-
- return next_pc;
-}
-
-static const struct target_desc *
-arm_read_description (void)
-{
- unsigned long arm_hwcap = linux_get_hwcap (4);
-
- if (arm_hwcap & HWCAP_IWMMXT)
- return arm_linux_read_description (ARM_FP_TYPE_IWMMXT);
-
- if (arm_hwcap & HWCAP_VFP)
- {
- /* Make sure that the kernel supports reading VFP registers. Support was
- added in 2.6.30. */
- int pid = lwpid_of (current_thread);
- errno = 0;
- char *buf = (char *) alloca (ARM_VFP3_REGS_SIZE);
- if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 && errno == EIO)
- return arm_linux_read_description (ARM_FP_TYPE_NONE);
-
- /* NEON implies either no VFP, or VFPv3-D32. We only support
- it with VFP. */
- if (arm_hwcap & HWCAP_NEON)
- return aarch32_linux_read_description ();
- else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
- return arm_linux_read_description (ARM_FP_TYPE_VFPV3);
- else
- return arm_linux_read_description (ARM_FP_TYPE_VFPV2);
- }
-
- /* The default configuration uses legacy FPA registers, probably
- simulated. */
- return arm_linux_read_description (ARM_FP_TYPE_NONE);
-}
-
-static void
-arm_arch_setup (void)
-{
- int tid = lwpid_of (current_thread);
- int gpregs[18];
- struct iovec iov;
-
- /* Query hardware watchpoint/breakpoint capabilities. */
- arm_linux_init_hwbp_cap (tid);
-
- current_process ()->tdesc = arm_read_description ();
-
- iov.iov_base = gpregs;
- iov.iov_len = sizeof (gpregs);
-
- /* Check if PTRACE_GETREGSET works. */
- if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
- have_ptrace_getregset = 1;
- else
- have_ptrace_getregset = 0;
-}
-
-/* Fetch the next possible PCs after the current instruction executes. */
-
-static std::vector<CORE_ADDR>
-arm_gdbserver_get_next_pcs (struct regcache *regcache)
-{
- struct arm_get_next_pcs next_pcs_ctx;
-
- arm_get_next_pcs_ctor (&next_pcs_ctx,
- &get_next_pcs_ops,
- /* Byte order is ignored assumed as host. */
- 0,
- 0,
- 1,
- regcache);
-
- return arm_get_next_pcs (&next_pcs_ctx);
-}
-
-/* Support for hardware single step. */
-
-static int
-arm_supports_hardware_single_step (void)
-{
- return 0;
-}
-
-/* Implementation of linux_target_ops method "get_syscall_trapinfo". */
-
-static void
-arm_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
-{
- if (arm_is_thumb_mode ())
- collect_register_by_name (regcache, "r7", sysno);
- else
- {
- unsigned long pc;
- unsigned long insn;
-
- collect_register_by_name (regcache, "pc", &pc);
-
- if ((*the_target->read_memory) (pc - 4, (unsigned char *) &insn, 4))
- *sysno = UNKNOWN_SYSCALL;
- else
- {
- unsigned long svc_operand = (0x00ffffff & insn);
-
- if (svc_operand)
- {
- /* OABI */
- *sysno = svc_operand - 0x900000;
- }
- else
- {
- /* EABI */
- collect_register_by_name (regcache, "r7", sysno);
- }
- }
- }
-}
-
-/* Register sets without using PTRACE_GETREGSET. */
-
-static struct regset_info arm_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 0,
- ARM_CORE_REGS_SIZE + ARM_INT_REGISTER_SIZE, GENERAL_REGS,
- arm_fill_gregset, arm_store_gregset },
- { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, IWMMXT_REGS_SIZE, EXTENDED_REGS,
- arm_fill_wmmxregset, arm_store_wmmxregset },
- { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, ARM_VFP3_REGS_SIZE, EXTENDED_REGS,
- arm_fill_vfpregset, arm_store_vfpregset },
- NULL_REGSET
-};
-
-static struct regsets_info arm_regsets_info =
- {
- arm_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info arm_usrregs_info =
- {
- arm_num_regs,
- arm_regmap,
- };
-
-static struct regs_info regs_info_arm =
- {
- NULL, /* regset_bitmap */
- &arm_usrregs_info,
- &arm_regsets_info
- };
-
-static const struct regs_info *
-arm_regs_info (void)
-{
- const struct target_desc *tdesc = current_process ()->tdesc;
-
- if (have_ptrace_getregset == 1
- && (is_aarch32_linux_description (tdesc)
- || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
- return ®s_info_aarch32;
-
- return ®s_info_arm;
-}
-
-struct linux_target_ops the_low_target = {
- arm_arch_setup,
- arm_regs_info,
- arm_cannot_fetch_register,
- arm_cannot_store_register,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- arm_breakpoint_kind_from_pc,
- arm_sw_breakpoint_from_kind,
- arm_gdbserver_get_next_pcs,
- 0,
- arm_breakpoint_at,
- arm_supports_z_point_type,
- arm_insert_point,
- arm_remove_point,
- arm_stopped_by_watchpoint,
- arm_stopped_data_address,
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- arm_new_process,
- arm_delete_process,
- arm_new_thread,
- arm_delete_thread,
- arm_new_fork,
- arm_prepare_to_resume,
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- arm_breakpoint_kind_from_current_state,
- arm_supports_hardware_single_step,
- arm_get_syscall_trapinfo,
-};
-
-void
-initialize_low_arch (void)
-{
- initialize_low_arch_aarch32 ();
- initialize_regsets_info (&arm_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/ARM specific low level interface, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "arch/arm.h"
+#include "arch/arm-linux.h"
+#include "arch/arm-get-next-pcs.h"
+#include "linux-aarch32-low.h"
+#include "linux-aarch32-tdesc.h"
+#include "linux-arm-tdesc.h"
+
+#include <sys/uio.h>
+/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
+ On Bionic elf.h and linux/elf.h have conflicting definitions. */
+#ifndef ELFMAG0
+#include <elf.h>
+#endif
+#include "nat/gdb_ptrace.h"
+#include <signal.h>
+#include <sys/syscall.h>
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 22
+#endif
+
+#ifndef PTRACE_GETWMMXREGS
+# define PTRACE_GETWMMXREGS 18
+# define PTRACE_SETWMMXREGS 19
+#endif
+
+#ifndef PTRACE_GETVFPREGS
+# define PTRACE_GETVFPREGS 27
+# define PTRACE_SETVFPREGS 28
+#endif
+
+#ifndef PTRACE_GETHBPREGS
+#define PTRACE_GETHBPREGS 29
+#define PTRACE_SETHBPREGS 30
+#endif
+
+/* Information describing the hardware breakpoint capabilities. */
+static struct
+{
+ unsigned char arch;
+ unsigned char max_wp_length;
+ unsigned char wp_count;
+ unsigned char bp_count;
+} arm_linux_hwbp_cap;
+
+/* Enum describing the different types of ARM hardware break-/watch-points. */
+typedef enum
+{
+ arm_hwbp_break = 0,
+ arm_hwbp_load = 1,
+ arm_hwbp_store = 2,
+ arm_hwbp_access = 3
+} arm_hwbp_type;
+
+/* Type describing an ARM Hardware Breakpoint Control register value. */
+typedef unsigned int arm_hwbp_control_t;
+
+/* Structure used to keep track of hardware break-/watch-points. */
+struct arm_linux_hw_breakpoint
+{
+ /* Address to break on, or being watched. */
+ unsigned int address;
+ /* Control register for break-/watch- point. */
+ arm_hwbp_control_t control;
+};
+
+/* Since we cannot dynamically allocate subfields of arch_process_info,
+ assume a maximum number of supported break-/watchpoints. */
+#define MAX_BPTS 32
+#define MAX_WPTS 32
+
+/* Per-process arch-specific data we want to keep. */
+struct arch_process_info
+{
+ /* Hardware breakpoints for this process. */
+ struct arm_linux_hw_breakpoint bpts[MAX_BPTS];
+ /* Hardware watchpoints for this process. */
+ struct arm_linux_hw_breakpoint wpts[MAX_WPTS];
+};
+
+/* Per-thread arch-specific data we want to keep. */
+struct arch_lwp_info
+{
+ /* Non-zero if our copy differs from what's recorded in the thread. */
+ char bpts_changed[MAX_BPTS];
+ char wpts_changed[MAX_WPTS];
+ /* Cached stopped data address. */
+ CORE_ADDR stopped_data_address;
+};
+
+/* These are in <asm/elf.h> in current kernels. */
+#define HWCAP_VFP 64
+#define HWCAP_IWMMXT 512
+#define HWCAP_NEON 4096
+#define HWCAP_VFPv3 8192
+#define HWCAP_VFPv3D16 16384
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+#define arm_num_regs 26
+
+static int arm_regmap[] = {
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 64
+};
+
+/* Forward declarations needed for get_next_pcs ops. */
+static ULONGEST get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+ int len,
+ int byte_order);
+
+static CORE_ADDR get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+ CORE_ADDR val);
+
+static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self);
+
+static int get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
+
+/* get_next_pcs operations. */
+static struct arm_get_next_pcs_ops get_next_pcs_ops = {
+ get_next_pcs_read_memory_unsigned_integer,
+ get_next_pcs_syscall_next_pc,
+ get_next_pcs_addr_bits_remove,
+ get_next_pcs_is_thumb,
+ arm_linux_get_next_pcs_fixup,
+};
+
+static int
+arm_cannot_store_register (int regno)
+{
+ return (regno >= arm_num_regs);
+}
+
+static int
+arm_cannot_fetch_register (int regno)
+{
+ return (regno >= arm_num_regs);
+}
+
+static void
+arm_fill_wmmxregset (struct regcache *regcache, void *buf)
+{
+ if (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT)
+ return;
+
+ for (int i = 0; i < 16; i++)
+ collect_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
+
+ /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */
+ for (int i = 0; i < 6; i++)
+ collect_register (regcache, arm_num_regs + i + 16,
+ (char *) buf + 16 * 8 + i * 4);
+}
+
+static void
+arm_store_wmmxregset (struct regcache *regcache, const void *buf)
+{
+ if (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT)
+ return;
+
+ for (int i = 0; i < 16; i++)
+ supply_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
+
+ /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */
+ for (int i = 0; i < 6; i++)
+ supply_register (regcache, arm_num_regs + i + 16,
+ (char *) buf + 16 * 8 + i * 4);
+}
+
+static void
+arm_fill_vfpregset (struct regcache *regcache, void *buf)
+{
+ int num;
+
+ if (is_aarch32_linux_description (regcache->tdesc))
+ num = 32;
+ else
+ {
+ arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc);
+
+ if (fp_type == ARM_FP_TYPE_VFPV3)
+ num = 32;
+ else if (fp_type == ARM_FP_TYPE_VFPV2)
+ num = 16;
+ else
+ return;
+ }
+
+ arm_fill_vfpregset_num (regcache, buf, num);
+}
+
+/* Wrapper of UNMAKE_THUMB_ADDR for get_next_pcs. */
+static CORE_ADDR
+get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
+{
+ return UNMAKE_THUMB_ADDR (val);
+}
+
+static void
+arm_store_vfpregset (struct regcache *regcache, const void *buf)
+{
+ int num;
+
+ if (is_aarch32_linux_description (regcache->tdesc))
+ num = 32;
+ else
+ {
+ arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc);
+
+ if (fp_type == ARM_FP_TYPE_VFPV3)
+ num = 32;
+ else if (fp_type == ARM_FP_TYPE_VFPV2)
+ num = 16;
+ else
+ return;
+ }
+
+ arm_store_vfpregset_num (regcache, buf, num);
+}
+
+/* Wrapper of arm_is_thumb_mode for get_next_pcs. */
+static int
+get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
+{
+ return arm_is_thumb_mode ();
+}
+
+/* Read memory from the inferior.
+ BYTE_ORDER is ignored and there to keep compatiblity with GDB's
+ read_memory_unsigned_integer. */
+static ULONGEST
+get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+ int len,
+ int byte_order)
+{
+ ULONGEST res;
+
+ res = 0;
+ target_read_memory (memaddr, (unsigned char *) &res, len);
+
+ return res;
+}
+
+/* Fetch the thread-local storage pointer for libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
+ return PS_ERR;
+
+ /* IDX is the bias from the thread pointer to the beginning of the
+ thread descriptor. It has to be subtracted due to implementation
+ quirks in libthread_db. */
+ *base = (void *) ((char *)*base - idx);
+
+ return PS_OK;
+}
+
+
+/* Query Hardware Breakpoint information for the target we are attached to
+ (using PID as ptrace argument) and set up arm_linux_hwbp_cap. */
+static void
+arm_linux_init_hwbp_cap (int pid)
+{
+ unsigned int val;
+
+ if (ptrace (PTRACE_GETHBPREGS, pid, 0, &val) < 0)
+ return;
+
+ arm_linux_hwbp_cap.arch = (unsigned char)((val >> 24) & 0xff);
+ if (arm_linux_hwbp_cap.arch == 0)
+ return;
+
+ arm_linux_hwbp_cap.max_wp_length = (unsigned char)((val >> 16) & 0xff);
+ arm_linux_hwbp_cap.wp_count = (unsigned char)((val >> 8) & 0xff);
+ arm_linux_hwbp_cap.bp_count = (unsigned char)(val & 0xff);
+
+ if (arm_linux_hwbp_cap.wp_count > MAX_WPTS)
+ internal_error (__FILE__, __LINE__, "Unsupported number of watchpoints");
+ if (arm_linux_hwbp_cap.bp_count > MAX_BPTS)
+ internal_error (__FILE__, __LINE__, "Unsupported number of breakpoints");
+}
+
+/* How many hardware breakpoints are available? */
+static int
+arm_linux_get_hw_breakpoint_count (void)
+{
+ return arm_linux_hwbp_cap.bp_count;
+}
+
+/* How many hardware watchpoints are available? */
+static int
+arm_linux_get_hw_watchpoint_count (void)
+{
+ return arm_linux_hwbp_cap.wp_count;
+}
+
+/* Maximum length of area watched by hardware watchpoint. */
+static int
+arm_linux_get_hw_watchpoint_max_length (void)
+{
+ return arm_linux_hwbp_cap.max_wp_length;
+}
+
+/* Initialize an ARM hardware break-/watch-point control register value.
+ BYTE_ADDRESS_SELECT is the mask of bytes to trigger on; HWBP_TYPE is the
+ type of break-/watch-point; ENABLE indicates whether the point is enabled.
+ */
+static arm_hwbp_control_t
+arm_hwbp_control_initialize (unsigned byte_address_select,
+ arm_hwbp_type hwbp_type,
+ int enable)
+{
+ gdb_assert ((byte_address_select & ~0xffU) == 0);
+ gdb_assert (hwbp_type != arm_hwbp_break
+ || ((byte_address_select & 0xfU) != 0));
+
+ return (byte_address_select << 5) | (hwbp_type << 3) | (3 << 1) | enable;
+}
+
+/* Does the breakpoint control value CONTROL have the enable bit set? */
+static int
+arm_hwbp_control_is_enabled (arm_hwbp_control_t control)
+{
+ return control & 0x1;
+}
+
+/* Is the breakpoint control value CONTROL initialized? */
+static int
+arm_hwbp_control_is_initialized (arm_hwbp_control_t control)
+{
+ return control != 0;
+}
+
+/* Change a breakpoint control word so that it is in the disabled state. */
+static arm_hwbp_control_t
+arm_hwbp_control_disable (arm_hwbp_control_t control)
+{
+ return control & ~0x1;
+}
+
+/* Are two break-/watch-points equal? */
+static int
+arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1,
+ const struct arm_linux_hw_breakpoint *p2)
+{
+ return p1->address == p2->address && p1->control == p2->control;
+}
+
+/* Convert a raw breakpoint type to an enum arm_hwbp_type. */
+
+static arm_hwbp_type
+raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type)
+{
+ switch (raw_type)
+ {
+ case raw_bkpt_type_hw:
+ return arm_hwbp_break;
+ case raw_bkpt_type_write_wp:
+ return arm_hwbp_store;
+ case raw_bkpt_type_read_wp:
+ return arm_hwbp_load;
+ case raw_bkpt_type_access_wp:
+ return arm_hwbp_access;
+ default:
+ gdb_assert_not_reached ("unhandled raw type");
+ }
+}
+
+/* Initialize the hardware breakpoint structure P for a breakpoint or
+ watchpoint at ADDR to LEN. The type of watchpoint is given in TYPE.
+ Returns -1 if TYPE is unsupported, or -2 if the particular combination
+ of ADDR and LEN cannot be implemented. Otherwise, returns 0 if TYPE
+ represents a breakpoint and 1 if type represents a watchpoint. */
+static int
+arm_linux_hw_point_initialize (enum raw_bkpt_type raw_type, CORE_ADDR addr,
+ int len, struct arm_linux_hw_breakpoint *p)
+{
+ arm_hwbp_type hwbp_type;
+ unsigned mask;
+
+ hwbp_type = raw_bkpt_type_to_arm_hwbp_type (raw_type);
+
+ if (hwbp_type == arm_hwbp_break)
+ {
+ /* For breakpoints, the length field encodes the mode. */
+ switch (len)
+ {
+ case 2: /* 16-bit Thumb mode breakpoint */
+ case 3: /* 32-bit Thumb mode breakpoint */
+ mask = 0x3;
+ addr &= ~1;
+ break;
+ case 4: /* 32-bit ARM mode breakpoint */
+ mask = 0xf;
+ addr &= ~3;
+ break;
+ default:
+ /* Unsupported. */
+ return -2;
+ }
+ }
+ else
+ {
+ CORE_ADDR max_wp_length = arm_linux_get_hw_watchpoint_max_length ();
+ CORE_ADDR aligned_addr;
+
+ /* Can not set watchpoints for zero or negative lengths. */
+ if (len <= 0)
+ return -2;
+ /* The current ptrace interface can only handle watchpoints that are a
+ power of 2. */
+ if ((len & (len - 1)) != 0)
+ return -2;
+
+ /* Test that the range [ADDR, ADDR + LEN) fits into the largest address
+ range covered by a watchpoint. */
+ aligned_addr = addr & ~(max_wp_length - 1);
+ if (aligned_addr + max_wp_length < addr + len)
+ return -2;
+
+ mask = (1 << len) - 1;
+ }
+
+ p->address = (unsigned int) addr;
+ p->control = arm_hwbp_control_initialize (mask, hwbp_type, 1);
+
+ return hwbp_type != arm_hwbp_break;
+}
+
+/* Callback to mark a watch-/breakpoint to be updated in all threads of
+ the current process. */
+
+static void
+update_registers_callback (thread_info *thread, int watch, int i)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ /* The actual update is done later just before resuming the lwp,
+ we just mark that the registers need updating. */
+ if (watch)
+ lwp->arch_private->wpts_changed[i] = 1;
+ else
+ lwp->arch_private->bpts_changed[i] = 1;
+
+ /* If the lwp isn't stopped, force it to momentarily pause, so
+ we can update its breakpoint registers. */
+ if (!lwp->stopped)
+ linux_stop_lwp (lwp);
+}
+
+static int
+arm_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ case Z_PACKET_HW_BP:
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_READ_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ /* Leave the handling of sw breakpoints with the gdb client. */
+ return 0;
+ }
+}
+
+/* Insert hardware break-/watchpoint. */
+static int
+arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+ struct arm_linux_hw_breakpoint p, *pts;
+ int watch, i, count;
+
+ watch = arm_linux_hw_point_initialize (type, addr, len, &p);
+ if (watch < 0)
+ {
+ /* Unsupported. */
+ return watch == -1 ? 1 : -1;
+ }
+
+ if (watch)
+ {
+ count = arm_linux_get_hw_watchpoint_count ();
+ pts = proc->priv->arch_private->wpts;
+ }
+ else
+ {
+ count = arm_linux_get_hw_breakpoint_count ();
+ pts = proc->priv->arch_private->bpts;
+ }
+
+ for (i = 0; i < count; i++)
+ if (!arm_hwbp_control_is_enabled (pts[i].control))
+ {
+ pts[i] = p;
+
+ /* Only update the threads of the current process. */
+ for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
+ {
+ update_registers_callback (thread, watch, i);
+ });
+
+ return 0;
+ }
+
+ /* We're out of watchpoints. */
+ return -1;
+}
+
+/* Remove hardware break-/watchpoint. */
+static int
+arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+ struct arm_linux_hw_breakpoint p, *pts;
+ int watch, i, count;
+
+ watch = arm_linux_hw_point_initialize (type, addr, len, &p);
+ if (watch < 0)
+ {
+ /* Unsupported. */
+ return -1;
+ }
+
+ if (watch)
+ {
+ count = arm_linux_get_hw_watchpoint_count ();
+ pts = proc->priv->arch_private->wpts;
+ }
+ else
+ {
+ count = arm_linux_get_hw_breakpoint_count ();
+ pts = proc->priv->arch_private->bpts;
+ }
+
+ for (i = 0; i < count; i++)
+ if (arm_linux_hw_breakpoint_equal (&p, pts + i))
+ {
+ pts[i].control = arm_hwbp_control_disable (pts[i].control);
+
+ /* Only update the threads of the current process. */
+ for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
+ {
+ update_registers_callback (thread, watch, i);
+ });
+
+ return 0;
+ }
+
+ /* No watchpoint matched. */
+ return -1;
+}
+
+/* Return whether current thread is stopped due to a watchpoint. */
+static int
+arm_stopped_by_watchpoint (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+ siginfo_t siginfo;
+
+ /* We must be able to set hardware watchpoints. */
+ if (arm_linux_get_hw_watchpoint_count () == 0)
+ return 0;
+
+ /* Retrieve siginfo. */
+ errno = 0;
+ ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo);
+ if (errno != 0)
+ return 0;
+
+ /* This must be a hardware breakpoint. */
+ if (siginfo.si_signo != SIGTRAP
+ || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
+ return 0;
+
+ /* If we are in a positive slot then we're looking at a breakpoint and not
+ a watchpoint. */
+ if (siginfo.si_errno >= 0)
+ return 0;
+
+ /* Cache stopped data address for use by arm_stopped_data_address. */
+ lwp->arch_private->stopped_data_address
+ = (CORE_ADDR) (uintptr_t) siginfo.si_addr;
+
+ return 1;
+}
+
+/* Return data address that triggered watchpoint. Called only if
+ arm_stopped_by_watchpoint returned true. */
+static CORE_ADDR
+arm_stopped_data_address (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+ return lwp->arch_private->stopped_data_address;
+}
+
+/* Called when a new process is created. */
+static struct arch_process_info *
+arm_new_process (void)
+{
+ struct arch_process_info *info = XCNEW (struct arch_process_info);
+ return info;
+}
+
+/* Called when a process is being deleted. */
+
+static void
+arm_delete_process (struct arch_process_info *info)
+{
+ xfree (info);
+}
+
+/* Called when a new thread is detected. */
+static void
+arm_new_thread (struct lwp_info *lwp)
+{
+ struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
+ int i;
+
+ for (i = 0; i < MAX_BPTS; i++)
+ info->bpts_changed[i] = 1;
+ for (i = 0; i < MAX_WPTS; i++)
+ info->wpts_changed[i] = 1;
+
+ lwp->arch_private = info;
+}
+
+/* Function to call when a thread is being deleted. */
+
+static void
+arm_delete_thread (struct arch_lwp_info *arch_lwp)
+{
+ xfree (arch_lwp);
+}
+
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+ struct arch_process_info *parent_proc_info;
+ struct arch_process_info *child_proc_info;
+ struct lwp_info *child_lwp;
+ struct arch_lwp_info *child_lwp_info;
+ int i;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->priv != NULL
+ && parent->priv->arch_private != NULL);
+ gdb_assert (child->priv != NULL
+ && child->priv->arch_private != NULL);
+
+ parent_proc_info = parent->priv->arch_private;
+ child_proc_info = child->priv->arch_private;
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child_proc_info = *parent_proc_info;
+
+ /* Mark all the hardware breakpoints and watchpoints as changed to
+ make sure that the registers will be updated. */
+ child_lwp = find_lwp_pid (ptid_t (child->pid));
+ child_lwp_info = child_lwp->arch_private;
+ for (i = 0; i < MAX_BPTS; i++)
+ child_lwp_info->bpts_changed[i] = 1;
+ for (i = 0; i < MAX_WPTS; i++)
+ child_lwp_info->wpts_changed[i] = 1;
+}
+
+/* Called when resuming a thread.
+ If the debug regs have changed, update the thread's copies. */
+static void
+arm_prepare_to_resume (struct lwp_info *lwp)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+ int pid = lwpid_of (thread);
+ struct process_info *proc = find_process_pid (pid_of (thread));
+ struct arch_process_info *proc_info = proc->priv->arch_private;
+ struct arch_lwp_info *lwp_info = lwp->arch_private;
+ int i;
+
+ for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++)
+ if (lwp_info->bpts_changed[i])
+ {
+ errno = 0;
+
+ if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) ((i << 1) + 1),
+ &proc_info->bpts[i].address) < 0)
+ perror_with_name ("Unexpected error setting breakpoint address");
+
+ if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) ((i << 1) + 2),
+ &proc_info->bpts[i].control) < 0)
+ perror_with_name ("Unexpected error setting breakpoint");
+
+ lwp_info->bpts_changed[i] = 0;
+ }
+
+ for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++)
+ if (lwp_info->wpts_changed[i])
+ {
+ errno = 0;
+
+ if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) -((i << 1) + 1),
+ &proc_info->wpts[i].address) < 0)
+ perror_with_name ("Unexpected error setting watchpoint address");
+
+ if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) -((i << 1) + 2),
+ &proc_info->wpts[i].control) < 0)
+ perror_with_name ("Unexpected error setting watchpoint");
+
+ lwp_info->wpts_changed[i] = 0;
+ }
+}
+
+/* Find the next pc for a sigreturn or rt_sigreturn syscall. In
+ addition, set IS_THUMB depending on whether we will return to ARM
+ or Thumb code.
+ See arm-linux.h for stack layout details. */
+static CORE_ADDR
+arm_sigreturn_next_pc (struct regcache *regcache, int svc_number,
+ int *is_thumb)
+{
+ unsigned long sp;
+ unsigned long sp_data;
+ /* Offset of PC register. */
+ int pc_offset = 0;
+ CORE_ADDR next_pc = 0;
+ uint32_t cpsr;
+
+ gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn);
+
+ collect_register_by_name (regcache, "sp", &sp);
+ (*the_target->read_memory) (sp, (unsigned char *) &sp_data, 4);
+
+ pc_offset = arm_linux_sigreturn_next_pc_offset
+ (sp, sp_data, svc_number, __NR_sigreturn == svc_number ? 1 : 0);
+
+ (*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4);
+
+ /* Set IS_THUMB according the CPSR saved on the stack. */
+ (*the_target->read_memory) (sp + pc_offset + 4, (unsigned char *) &cpsr, 4);
+ *is_thumb = ((cpsr & CPSR_T) != 0);
+
+ return next_pc;
+}
+
+/* When PC is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+static CORE_ADDR
+get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
+{
+ CORE_ADDR next_pc = 0;
+ CORE_ADDR pc = regcache_read_pc (self->regcache);
+ int is_thumb = arm_is_thumb_mode ();
+ ULONGEST svc_number = 0;
+ struct regcache *regcache = self->regcache;
+
+ if (is_thumb)
+ {
+ collect_register (regcache, 7, &svc_number);
+ next_pc = pc + 2;
+ }
+ else
+ {
+ unsigned long this_instr;
+ unsigned long svc_operand;
+
+ target_read_memory (pc, (unsigned char *) &this_instr, 4);
+ svc_operand = (0x00ffffff & this_instr);
+
+ if (svc_operand) /* OABI. */
+ {
+ svc_number = svc_operand - 0x900000;
+ }
+ else /* EABI. */
+ {
+ collect_register (regcache, 7, &svc_number);
+ }
+
+ next_pc = pc + 4;
+ }
+
+ /* This is a sigreturn or sigreturn_rt syscall. */
+ if (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn)
+ {
+ /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
+ update IS_THUMB. */
+ next_pc = arm_sigreturn_next_pc (regcache, svc_number, &is_thumb);
+ }
+
+ /* Addresses for calling Thumb functions have the bit 0 set. */
+ if (is_thumb)
+ next_pc = MAKE_THUMB_ADDR (next_pc);
+
+ return next_pc;
+}
+
+static const struct target_desc *
+arm_read_description (void)
+{
+ unsigned long arm_hwcap = linux_get_hwcap (4);
+
+ if (arm_hwcap & HWCAP_IWMMXT)
+ return arm_linux_read_description (ARM_FP_TYPE_IWMMXT);
+
+ if (arm_hwcap & HWCAP_VFP)
+ {
+ /* Make sure that the kernel supports reading VFP registers. Support was
+ added in 2.6.30. */
+ int pid = lwpid_of (current_thread);
+ errno = 0;
+ char *buf = (char *) alloca (ARM_VFP3_REGS_SIZE);
+ if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 && errno == EIO)
+ return arm_linux_read_description (ARM_FP_TYPE_NONE);
+
+ /* NEON implies either no VFP, or VFPv3-D32. We only support
+ it with VFP. */
+ if (arm_hwcap & HWCAP_NEON)
+ return aarch32_linux_read_description ();
+ else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+ return arm_linux_read_description (ARM_FP_TYPE_VFPV3);
+ else
+ return arm_linux_read_description (ARM_FP_TYPE_VFPV2);
+ }
+
+ /* The default configuration uses legacy FPA registers, probably
+ simulated. */
+ return arm_linux_read_description (ARM_FP_TYPE_NONE);
+}
+
+static void
+arm_arch_setup (void)
+{
+ int tid = lwpid_of (current_thread);
+ int gpregs[18];
+ struct iovec iov;
+
+ /* Query hardware watchpoint/breakpoint capabilities. */
+ arm_linux_init_hwbp_cap (tid);
+
+ current_process ()->tdesc = arm_read_description ();
+
+ iov.iov_base = gpregs;
+ iov.iov_len = sizeof (gpregs);
+
+ /* Check if PTRACE_GETREGSET works. */
+ if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
+ have_ptrace_getregset = 1;
+ else
+ have_ptrace_getregset = 0;
+}
+
+/* Fetch the next possible PCs after the current instruction executes. */
+
+static std::vector<CORE_ADDR>
+arm_gdbserver_get_next_pcs (struct regcache *regcache)
+{
+ struct arm_get_next_pcs next_pcs_ctx;
+
+ arm_get_next_pcs_ctor (&next_pcs_ctx,
+ &get_next_pcs_ops,
+ /* Byte order is ignored assumed as host. */
+ 0,
+ 0,
+ 1,
+ regcache);
+
+ return arm_get_next_pcs (&next_pcs_ctx);
+}
+
+/* Support for hardware single step. */
+
+static int
+arm_supports_hardware_single_step (void)
+{
+ return 0;
+}
+
+/* Implementation of linux_target_ops method "get_syscall_trapinfo". */
+
+static void
+arm_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
+{
+ if (arm_is_thumb_mode ())
+ collect_register_by_name (regcache, "r7", sysno);
+ else
+ {
+ unsigned long pc;
+ unsigned long insn;
+
+ collect_register_by_name (regcache, "pc", &pc);
+
+ if ((*the_target->read_memory) (pc - 4, (unsigned char *) &insn, 4))
+ *sysno = UNKNOWN_SYSCALL;
+ else
+ {
+ unsigned long svc_operand = (0x00ffffff & insn);
+
+ if (svc_operand)
+ {
+ /* OABI */
+ *sysno = svc_operand - 0x900000;
+ }
+ else
+ {
+ /* EABI */
+ collect_register_by_name (regcache, "r7", sysno);
+ }
+ }
+ }
+}
+
+/* Register sets without using PTRACE_GETREGSET. */
+
+static struct regset_info arm_regsets[] = {
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0,
+ ARM_CORE_REGS_SIZE + ARM_INT_REGISTER_SIZE, GENERAL_REGS,
+ arm_fill_gregset, arm_store_gregset },
+ { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, IWMMXT_REGS_SIZE, EXTENDED_REGS,
+ arm_fill_wmmxregset, arm_store_wmmxregset },
+ { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, ARM_VFP3_REGS_SIZE, EXTENDED_REGS,
+ arm_fill_vfpregset, arm_store_vfpregset },
+ NULL_REGSET
+};
+
+static struct regsets_info arm_regsets_info =
+ {
+ arm_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info arm_usrregs_info =
+ {
+ arm_num_regs,
+ arm_regmap,
+ };
+
+static struct regs_info regs_info_arm =
+ {
+ NULL, /* regset_bitmap */
+ &arm_usrregs_info,
+ &arm_regsets_info
+ };
+
+static const struct regs_info *
+arm_regs_info (void)
+{
+ const struct target_desc *tdesc = current_process ()->tdesc;
+
+ if (have_ptrace_getregset == 1
+ && (is_aarch32_linux_description (tdesc)
+ || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
+ return ®s_info_aarch32;
+
+ return ®s_info_arm;
+}
+
+struct linux_target_ops the_low_target = {
+ arm_arch_setup,
+ arm_regs_info,
+ arm_cannot_fetch_register,
+ arm_cannot_store_register,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ arm_breakpoint_kind_from_pc,
+ arm_sw_breakpoint_from_kind,
+ arm_gdbserver_get_next_pcs,
+ 0,
+ arm_breakpoint_at,
+ arm_supports_z_point_type,
+ arm_insert_point,
+ arm_remove_point,
+ arm_stopped_by_watchpoint,
+ arm_stopped_data_address,
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ arm_new_process,
+ arm_delete_process,
+ arm_new_thread,
+ arm_delete_thread,
+ arm_new_fork,
+ arm_prepare_to_resume,
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ arm_breakpoint_kind_from_current_state,
+ arm_supports_hardware_single_step,
+ arm_get_syscall_trapinfo,
+};
+
+void
+initialize_low_arch (void)
+{
+ initialize_low_arch_aarch32 ();
+ initialize_regsets_info (&arm_regsets_info);
+}
+++ /dev/null
-/* Copyright (C) 2019-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-
-#include "linux-arm-tdesc.h"
-
-#include "tdesc.h"
-#include "arch/arm.h"
-#include <inttypes.h>
-
-/* All possible Arm target descriptors. */
-static struct target_desc *tdesc_arm_list[ARM_FP_TYPE_INVALID];
-
-/* See linux-arm-tdesc.h. */
-
-const target_desc *
-arm_linux_read_description (arm_fp_type fp_type)
-{
- struct target_desc *tdesc = tdesc_arm_list[fp_type];
-
- if (tdesc == nullptr)
- {
- tdesc = arm_create_target_description (fp_type);
-
- static const char *expedite_regs[] = { "r11", "sp", "pc", 0 };
- init_target_desc (tdesc, expedite_regs);
-
- tdesc_arm_list[fp_type] = tdesc;
- }
-
- return tdesc;
-}
-
-/* See linux-arm-tdesc.h. */
-
-arm_fp_type
-arm_linux_get_tdesc_fp_type (const target_desc *tdesc)
-{
- gdb_assert (tdesc != nullptr);
-
- /* Many of the tdesc_arm_list entries may not have been initialised yet. This
- is ok, because tdesc must be one of the initialised ones. */
- for (int i = ARM_FP_TYPE_NONE; i < ARM_FP_TYPE_INVALID; i++)
- {
- if (tdesc == tdesc_arm_list[i])
- return (arm_fp_type) i;
- }
-
- return ARM_FP_TYPE_INVALID;
-}
--- /dev/null
+/* Copyright (C) 2019-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+
+#include "linux-arm-tdesc.h"
+
+#include "tdesc.h"
+#include "arch/arm.h"
+#include <inttypes.h>
+
+/* All possible Arm target descriptors. */
+static struct target_desc *tdesc_arm_list[ARM_FP_TYPE_INVALID];
+
+/* See linux-arm-tdesc.h. */
+
+const target_desc *
+arm_linux_read_description (arm_fp_type fp_type)
+{
+ struct target_desc *tdesc = tdesc_arm_list[fp_type];
+
+ if (tdesc == nullptr)
+ {
+ tdesc = arm_create_target_description (fp_type);
+
+ static const char *expedite_regs[] = { "r11", "sp", "pc", 0 };
+ init_target_desc (tdesc, expedite_regs);
+
+ tdesc_arm_list[fp_type] = tdesc;
+ }
+
+ return tdesc;
+}
+
+/* See linux-arm-tdesc.h. */
+
+arm_fp_type
+arm_linux_get_tdesc_fp_type (const target_desc *tdesc)
+{
+ gdb_assert (tdesc != nullptr);
+
+ /* Many of the tdesc_arm_list entries may not have been initialised yet. This
+ is ok, because tdesc must be one of the initialised ones. */
+ for (int i = ARM_FP_TYPE_NONE; i < ARM_FP_TYPE_INVALID; i++)
+ {
+ if (tdesc == tdesc_arm_list[i])
+ return (arm_fp_type) i;
+ }
+
+ return ARM_FP_TYPE_INVALID;
+}
+++ /dev/null
-/* GNU/Linux/BFIN specific low level interface, for the remote server for GDB.
-
- Copyright (C) 2005-2020 Free Software Foundation, Inc.
-
- Contributed by Analog Devices, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include <asm/ptrace.h>
-
-/* Defined in auto-generated file reg-bfin.c. */
-void init_registers_bfin (void);
-extern const struct target_desc *tdesc_bfin;
-
-static int bfin_regmap[] =
-{
- PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
- PT_P0, PT_P1, PT_P2, PT_P3, PT_P4, PT_P5, PT_USP, PT_FP,
- PT_I0, PT_I1, PT_I2, PT_I3, PT_M0, PT_M1, PT_M2, PT_M3,
- PT_B0, PT_B1, PT_B2, PT_B3, PT_L0, PT_L1, PT_L2, PT_L3,
- PT_A0X, PT_A0W, PT_A1X, PT_A1W, PT_ASTAT, PT_RETS,
- PT_LC0, PT_LT0, PT_LB0, PT_LC1, PT_LT1, PT_LB1,
- -1 /* PT_CYCLES */, -1 /* PT_CYCLES2 */,
- -1 /* PT_USP */, PT_SEQSTAT, PT_SYSCFG, PT_PC, PT_RETX, PT_RETN, PT_RETE,
- PT_PC,
-};
-
-#define bfin_num_regs ARRAY_SIZE (bfin_regmap)
-
-static int
-bfin_cannot_store_register (int regno)
-{
- return (regno >= bfin_num_regs);
-}
-
-static int
-bfin_cannot_fetch_register (int regno)
-{
- return (regno >= bfin_num_regs);
-}
-
-#define bfin_breakpoint_len 2
-static const gdb_byte bfin_breakpoint[bfin_breakpoint_len] = {0xa1, 0x00};
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-bfin_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = bfin_breakpoint_len;
- return bfin_breakpoint;
-}
-
-static int
-bfin_breakpoint_at (CORE_ADDR where)
-{
- unsigned char insn[bfin_breakpoint_len];
-
- read_inferior_memory(where, insn, bfin_breakpoint_len);
- if (insn[0] == bfin_breakpoint[0]
- && insn[1] == bfin_breakpoint[1])
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-static void
-bfin_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_bfin;
-}
-
-/* Support for hardware single step. */
-
-static int
-bfin_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static struct usrregs_info bfin_usrregs_info =
- {
- bfin_num_regs,
- bfin_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &bfin_usrregs_info,
- };
-
-static const struct regs_info *
-bfin_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- bfin_arch_setup,
- bfin_regs_info,
- bfin_cannot_fetch_register,
- bfin_cannot_store_register,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_kind_from_pc */
- bfin_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
- 2,
- bfin_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- bfin_supports_hardware_single_step,
-};
-
-
-void
-initialize_low_arch (void)
-{
- init_registers_bfin ();
-}
--- /dev/null
+/* GNU/Linux/BFIN specific low level interface, for the remote server for GDB.
+
+ Copyright (C) 2005-2020 Free Software Foundation, Inc.
+
+ Contributed by Analog Devices, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include <asm/ptrace.h>
+
+/* Defined in auto-generated file reg-bfin.c. */
+void init_registers_bfin (void);
+extern const struct target_desc *tdesc_bfin;
+
+static int bfin_regmap[] =
+{
+ PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
+ PT_P0, PT_P1, PT_P2, PT_P3, PT_P4, PT_P5, PT_USP, PT_FP,
+ PT_I0, PT_I1, PT_I2, PT_I3, PT_M0, PT_M1, PT_M2, PT_M3,
+ PT_B0, PT_B1, PT_B2, PT_B3, PT_L0, PT_L1, PT_L2, PT_L3,
+ PT_A0X, PT_A0W, PT_A1X, PT_A1W, PT_ASTAT, PT_RETS,
+ PT_LC0, PT_LT0, PT_LB0, PT_LC1, PT_LT1, PT_LB1,
+ -1 /* PT_CYCLES */, -1 /* PT_CYCLES2 */,
+ -1 /* PT_USP */, PT_SEQSTAT, PT_SYSCFG, PT_PC, PT_RETX, PT_RETN, PT_RETE,
+ PT_PC,
+};
+
+#define bfin_num_regs ARRAY_SIZE (bfin_regmap)
+
+static int
+bfin_cannot_store_register (int regno)
+{
+ return (regno >= bfin_num_regs);
+}
+
+static int
+bfin_cannot_fetch_register (int regno)
+{
+ return (regno >= bfin_num_regs);
+}
+
+#define bfin_breakpoint_len 2
+static const gdb_byte bfin_breakpoint[bfin_breakpoint_len] = {0xa1, 0x00};
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+bfin_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = bfin_breakpoint_len;
+ return bfin_breakpoint;
+}
+
+static int
+bfin_breakpoint_at (CORE_ADDR where)
+{
+ unsigned char insn[bfin_breakpoint_len];
+
+ read_inferior_memory(where, insn, bfin_breakpoint_len);
+ if (insn[0] == bfin_breakpoint[0]
+ && insn[1] == bfin_breakpoint[1])
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+static void
+bfin_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_bfin;
+}
+
+/* Support for hardware single step. */
+
+static int
+bfin_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static struct usrregs_info bfin_usrregs_info =
+ {
+ bfin_num_regs,
+ bfin_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &bfin_usrregs_info,
+ };
+
+static const struct regs_info *
+bfin_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ bfin_arch_setup,
+ bfin_regs_info,
+ bfin_cannot_fetch_register,
+ bfin_cannot_store_register,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_kind_from_pc */
+ bfin_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
+ 2,
+ bfin_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ bfin_supports_hardware_single_step,
+};
+
+
+void
+initialize_low_arch (void)
+{
+ init_registers_bfin ();
+}
+++ /dev/null
-/* GNU/Linux/CRIS specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "nat/gdb_ptrace.h"
-
-/* Defined in auto-generated file reg-cris.c. */
-void init_registers_cris (void);
-extern const struct target_desc *tdesc_cris;
-
-/* CRISv10 */
-#define cris_num_regs 32
-
-/* Locations need to match <include/asm/arch/ptrace.h>. */
-static int cris_regmap[] = {
- 15*4, 14*4, 13*4, 12*4,
- 11*4, 10*4, 9*4, 8*4,
- 7*4, 6*4, 5*4, 4*4,
- 3*4, 2*4, 23*4, 19*4,
-
- -1, -1, -1, -1,
- -1, 17*4, -1, 16*4,
- -1, -1, -1, 18*4,
- -1, 17*4, -1, -1
-
-};
-
-static int
-cris_cannot_store_register (int regno)
-{
- if (cris_regmap[regno] == -1)
- return 1;
-
- return (regno >= cris_num_regs);
-}
-
-static int
-cris_cannot_fetch_register (int regno)
-{
- if (cris_regmap[regno] == -1)
- return 1;
-
- return (regno >= cris_num_regs);
-}
-
-static const unsigned short cris_breakpoint = 0xe938;
-#define cris_breakpoint_len 2
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-cris_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = cris_breakpoint_len;
- return (const gdb_byte *) &cris_breakpoint;
-}
-
-static int
-cris_breakpoint_at (CORE_ADDR where)
-{
- unsigned short insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn,
- cris_breakpoint_len);
- if (insn == cris_breakpoint)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-static void
-cris_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_cris;
-}
-
-static struct usrregs_info cris_usrregs_info =
- {
- cris_num_regs,
- cris_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &cris_usrregs_info,
- };
-
-static const struct regs_info *
-cris_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- cris_arch_setup,
- cris_regs_info,
- cris_cannot_fetch_register,
- cris_cannot_store_register,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_kind_from_pc */
- cris_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
- 0,
- cris_breakpoint_at,
-};
-
-void
-initialize_low_arch (void)
-{
- init_registers_cris ();
-}
--- /dev/null
+/* GNU/Linux/CRIS specific low level interface, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "nat/gdb_ptrace.h"
+
+/* Defined in auto-generated file reg-cris.c. */
+void init_registers_cris (void);
+extern const struct target_desc *tdesc_cris;
+
+/* CRISv10 */
+#define cris_num_regs 32
+
+/* Locations need to match <include/asm/arch/ptrace.h>. */
+static int cris_regmap[] = {
+ 15*4, 14*4, 13*4, 12*4,
+ 11*4, 10*4, 9*4, 8*4,
+ 7*4, 6*4, 5*4, 4*4,
+ 3*4, 2*4, 23*4, 19*4,
+
+ -1, -1, -1, -1,
+ -1, 17*4, -1, 16*4,
+ -1, -1, -1, 18*4,
+ -1, 17*4, -1, -1
+
+};
+
+static int
+cris_cannot_store_register (int regno)
+{
+ if (cris_regmap[regno] == -1)
+ return 1;
+
+ return (regno >= cris_num_regs);
+}
+
+static int
+cris_cannot_fetch_register (int regno)
+{
+ if (cris_regmap[regno] == -1)
+ return 1;
+
+ return (regno >= cris_num_regs);
+}
+
+static const unsigned short cris_breakpoint = 0xe938;
+#define cris_breakpoint_len 2
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+cris_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = cris_breakpoint_len;
+ return (const gdb_byte *) &cris_breakpoint;
+}
+
+static int
+cris_breakpoint_at (CORE_ADDR where)
+{
+ unsigned short insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn,
+ cris_breakpoint_len);
+ if (insn == cris_breakpoint)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+static void
+cris_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_cris;
+}
+
+static struct usrregs_info cris_usrregs_info =
+ {
+ cris_num_regs,
+ cris_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &cris_usrregs_info,
+ };
+
+static const struct regs_info *
+cris_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ cris_arch_setup,
+ cris_regs_info,
+ cris_cannot_fetch_register,
+ cris_cannot_store_register,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_kind_from_pc */
+ cris_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
+ 0,
+ cris_breakpoint_at,
+};
+
+void
+initialize_low_arch (void)
+{
+ init_registers_cris ();
+}
+++ /dev/null
-/* GNU/Linux/CRIS specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "nat/gdb_ptrace.h"
-
-/* Defined in auto-generated file reg-crisv32.c. */
-void init_registers_crisv32 (void);
-extern const struct target_desc *tdesc_crisv32;
-
-/* CRISv32 */
-#define cris_num_regs 49
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-/* Note: Ignoring USP (having the stack pointer in two locations causes trouble
- without any significant gain). */
-
-/* Locations need to match <include/asm/arch/ptrace.h>. */
-static int cris_regmap[] = {
- 1*4, 2*4, 3*4, 4*4,
- 5*4, 6*4, 7*4, 8*4,
- 9*4, 10*4, 11*4, 12*4,
- 13*4, 14*4, 24*4, 15*4,
-
- -1, -1, -1, 16*4,
- -1, 22*4, 23*4, 17*4,
- -1, -1, 21*4, 20*4,
- -1, 19*4, -1, 18*4,
-
- 25*4,
-
- 26*4, -1, -1, 29*4,
- 30*4, 31*4, 32*4, 33*4,
- 34*4, 35*4, 36*4, 37*4,
- 38*4, 39*4, 40*4, -1
-
-};
-
-static const unsigned short cris_breakpoint = 0xe938;
-#define cris_breakpoint_len 2
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-cris_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = cris_breakpoint_len;
- return (const gdb_byte *) &cris_breakpoint;
-}
-
-static int
-cris_breakpoint_at (CORE_ADDR where)
-{
- unsigned short insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn,
- cris_breakpoint_len);
- if (insn == cris_breakpoint)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-static void
-cris_write_data_breakpoint (struct regcache *regcache,
- int bp, unsigned long start, unsigned long end)
-{
- switch (bp)
- {
- case 0:
- supply_register_by_name (regcache, "s3", &start);
- supply_register_by_name (regcache, "s4", &end);
- break;
- case 1:
- supply_register_by_name (regcache, "s5", &start);
- supply_register_by_name (regcache, "s6", &end);
- break;
- case 2:
- supply_register_by_name (regcache, "s7", &start);
- supply_register_by_name (regcache, "s8", &end);
- break;
- case 3:
- supply_register_by_name (regcache, "s9", &start);
- supply_register_by_name (regcache, "s10", &end);
- break;
- case 4:
- supply_register_by_name (regcache, "s11", &start);
- supply_register_by_name (regcache, "s12", &end);
- break;
- case 5:
- supply_register_by_name (regcache, "s13", &start);
- supply_register_by_name (regcache, "s14", &end);
- break;
- }
-}
-
-static int
-cris_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_READ_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
- return 0;
- }
-}
-
-static int
-cris_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
-{
- int bp;
- unsigned long bp_ctrl;
- unsigned long start, end;
- unsigned long ccs;
- struct regcache *regcache;
-
- regcache = get_thread_regcache (current_thread, 1);
-
- /* Read watchpoints are set as access watchpoints, because of GDB's
- inability to deal with pure read watchpoints. */
- if (type == raw_bkpt_type_read_wp)
- type = raw_bkpt_type_access_wp;
-
- /* Get the configuration register. */
- collect_register_by_name (regcache, "s0", &bp_ctrl);
-
- /* The watchpoint allocation scheme is the simplest possible.
- For example, if a region is watched for read and
- a write watch is requested, a new watchpoint will
- be used. Also, if a watch for a region that is already
- covered by one or more existing watchpoints, a new
- watchpoint will be used. */
-
- /* First, find a free data watchpoint. */
- for (bp = 0; bp < 6; bp++)
- {
- /* Each data watchpoint's control registers occupy 2 bits
- (hence the 3), starting at bit 2 for D0 (hence the 2)
- with 4 bits between for each watchpoint (yes, the 4). */
- if (!(bp_ctrl & (0x3 << (2 + (bp * 4)))))
- break;
- }
-
- if (bp > 5)
- {
- /* We're out of watchpoints. */
- return -1;
- }
-
- /* Configure the control register first. */
- if (type == raw_bkpt_type_read_wp || type == raw_bkpt_type_access_wp)
- {
- /* Trigger on read. */
- bp_ctrl |= (1 << (2 + bp * 4));
- }
- if (type == raw_bkpt_type_write_wp || type == raw_bkpt_type_access_wp)
- {
- /* Trigger on write. */
- bp_ctrl |= (2 << (2 + bp * 4));
- }
-
- /* Setup the configuration register. */
- supply_register_by_name (regcache, "s0", &bp_ctrl);
-
- /* Setup the range. */
- start = addr;
- end = addr + len - 1;
-
- /* Configure the watchpoint register. */
- cris_write_data_breakpoint (regcache, bp, start, end);
-
- collect_register_by_name (regcache, "ccs", &ccs);
- /* Set the S1 flag to enable watchpoints. */
- ccs |= (1 << 19);
- supply_register_by_name (regcache, "ccs", &ccs);
-
- return 0;
-}
-
-static int
-cris_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, int len,
- struct raw_breakpoint *bp)
-{
- int bp;
- unsigned long bp_ctrl;
- unsigned long start, end;
- struct regcache *regcache;
- unsigned long bp_d_regs[12];
-
- regcache = get_thread_regcache (current_thread, 1);
-
- /* Read watchpoints are set as access watchpoints, because of GDB's
- inability to deal with pure read watchpoints. */
- if (type == raw_bkpt_type_read_wp)
- type = raw_bkpt_type_access_wp;
-
- /* Get the configuration register. */
- collect_register_by_name (regcache, "s0", &bp_ctrl);
-
- /* Try to find a watchpoint that is configured for the
- specified range, then check that read/write also matches. */
-
- /* Ugly pointer arithmetic, since I cannot rely on a
- single switch (addr) as there may be several watchpoints with
- the same start address for example. */
-
- /* Get all range registers to simplify search. */
- collect_register_by_name (regcache, "s3", &bp_d_regs[0]);
- collect_register_by_name (regcache, "s4", &bp_d_regs[1]);
- collect_register_by_name (regcache, "s5", &bp_d_regs[2]);
- collect_register_by_name (regcache, "s6", &bp_d_regs[3]);
- collect_register_by_name (regcache, "s7", &bp_d_regs[4]);
- collect_register_by_name (regcache, "s8", &bp_d_regs[5]);
- collect_register_by_name (regcache, "s9", &bp_d_regs[6]);
- collect_register_by_name (regcache, "s10", &bp_d_regs[7]);
- collect_register_by_name (regcache, "s11", &bp_d_regs[8]);
- collect_register_by_name (regcache, "s12", &bp_d_regs[9]);
- collect_register_by_name (regcache, "s13", &bp_d_regs[10]);
- collect_register_by_name (regcache, "s14", &bp_d_regs[11]);
-
- for (bp = 0; bp < 6; bp++)
- {
- if (bp_d_regs[bp * 2] == addr
- && bp_d_regs[bp * 2 + 1] == (addr + len - 1)) {
- /* Matching range. */
- int bitpos = 2 + bp * 4;
- int rw_bits;
-
- /* Read/write bits for this BP. */
- rw_bits = (bp_ctrl & (0x3 << bitpos)) >> bitpos;
-
- if ((type == raw_bkpt_type_read_wp && rw_bits == 0x1)
- || (type == raw_bkpt_type_write_wp && rw_bits == 0x2)
- || (type == raw_bkpt_type_access_wp && rw_bits == 0x3))
- {
- /* Read/write matched. */
- break;
- }
- }
- }
-
- if (bp > 5)
- {
- /* No watchpoint matched. */
- return -1;
- }
-
- /* Found a matching watchpoint. Now, deconfigure it by
- both disabling read/write in bp_ctrl and zeroing its
- start/end addresses. */
- bp_ctrl &= ~(3 << (2 + (bp * 4)));
- /* Setup the configuration register. */
- supply_register_by_name (regcache, "s0", &bp_ctrl);
-
- start = end = 0;
- /* Configure the watchpoint register. */
- cris_write_data_breakpoint (regcache, bp, start, end);
-
- /* Note that we don't clear the S1 flag here. It's done when continuing. */
- return 0;
-}
-
-static int
-cris_stopped_by_watchpoint (void)
-{
- unsigned long exs;
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
-
- collect_register_by_name (regcache, "exs", &exs);
-
- return (((exs & 0xff00) >> 8) == 0xc);
-}
-
-static CORE_ADDR
-cris_stopped_data_address (void)
-{
- unsigned long eda;
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
-
- collect_register_by_name (regcache, "eda", &eda);
-
- /* FIXME: Possibly adjust to match watched range. */
- return eda;
-}
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
- return PS_ERR;
-
- /* IDX is the bias from the thread pointer to the beginning of the
- thread descriptor. It has to be subtracted due to implementation
- quirks in libthread_db. */
- *base = (void *) ((char *) *base - idx);
- return PS_OK;
-}
-
-static void
-cris_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
-
- for (i = 0; i < cris_num_regs; i++)
- {
- if (cris_regmap[i] != -1)
- collect_register (regcache, i, ((char *) buf) + cris_regmap[i]);
- }
-}
-
-static void
-cris_store_gregset (struct regcache *regcache, const void *buf)
-{
- int i;
-
- for (i = 0; i < cris_num_regs; i++)
- {
- if (cris_regmap[i] != -1)
- supply_register (regcache, i, ((char *) buf) + cris_regmap[i]);
- }
-}
-
-static void
-cris_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_crisv32;
-}
-
-/* Support for hardware single step. */
-
-static int
-cris_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static struct regset_info cris_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, cris_num_regs * 4,
- GENERAL_REGS, cris_fill_gregset, cris_store_gregset },
- NULL_REGSET
-};
-
-
-static struct regsets_info cris_regsets_info =
- {
- cris_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info cris_usrregs_info =
- {
- cris_num_regs,
- cris_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &cris_usrregs_info,
- &cris_regsets_info
- };
-
-static const struct regs_info *
-cris_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- cris_arch_setup,
- cris_regs_info,
- NULL,
- NULL,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_kind_from_pc */
- cris_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
- 0,
- cris_breakpoint_at,
- cris_supports_z_point_type,
- cris_insert_point,
- cris_remove_point,
- cris_stopped_by_watchpoint,
- cris_stopped_data_address,
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- cris_supports_hardware_single_step,
-};
-
-void
-initialize_low_arch (void)
-{
- init_registers_crisv32 ();
-
- initialize_regsets_info (&cris_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/CRIS specific low level interface, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "nat/gdb_ptrace.h"
+
+/* Defined in auto-generated file reg-crisv32.c. */
+void init_registers_crisv32 (void);
+extern const struct target_desc *tdesc_crisv32;
+
+/* CRISv32 */
+#define cris_num_regs 49
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+/* Note: Ignoring USP (having the stack pointer in two locations causes trouble
+ without any significant gain). */
+
+/* Locations need to match <include/asm/arch/ptrace.h>. */
+static int cris_regmap[] = {
+ 1*4, 2*4, 3*4, 4*4,
+ 5*4, 6*4, 7*4, 8*4,
+ 9*4, 10*4, 11*4, 12*4,
+ 13*4, 14*4, 24*4, 15*4,
+
+ -1, -1, -1, 16*4,
+ -1, 22*4, 23*4, 17*4,
+ -1, -1, 21*4, 20*4,
+ -1, 19*4, -1, 18*4,
+
+ 25*4,
+
+ 26*4, -1, -1, 29*4,
+ 30*4, 31*4, 32*4, 33*4,
+ 34*4, 35*4, 36*4, 37*4,
+ 38*4, 39*4, 40*4, -1
+
+};
+
+static const unsigned short cris_breakpoint = 0xe938;
+#define cris_breakpoint_len 2
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+cris_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = cris_breakpoint_len;
+ return (const gdb_byte *) &cris_breakpoint;
+}
+
+static int
+cris_breakpoint_at (CORE_ADDR where)
+{
+ unsigned short insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn,
+ cris_breakpoint_len);
+ if (insn == cris_breakpoint)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+static void
+cris_write_data_breakpoint (struct regcache *regcache,
+ int bp, unsigned long start, unsigned long end)
+{
+ switch (bp)
+ {
+ case 0:
+ supply_register_by_name (regcache, "s3", &start);
+ supply_register_by_name (regcache, "s4", &end);
+ break;
+ case 1:
+ supply_register_by_name (regcache, "s5", &start);
+ supply_register_by_name (regcache, "s6", &end);
+ break;
+ case 2:
+ supply_register_by_name (regcache, "s7", &start);
+ supply_register_by_name (regcache, "s8", &end);
+ break;
+ case 3:
+ supply_register_by_name (regcache, "s9", &start);
+ supply_register_by_name (regcache, "s10", &end);
+ break;
+ case 4:
+ supply_register_by_name (regcache, "s11", &start);
+ supply_register_by_name (regcache, "s12", &end);
+ break;
+ case 5:
+ supply_register_by_name (regcache, "s13", &start);
+ supply_register_by_name (regcache, "s14", &end);
+ break;
+ }
+}
+
+static int
+cris_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_READ_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+cris_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ int bp;
+ unsigned long bp_ctrl;
+ unsigned long start, end;
+ unsigned long ccs;
+ struct regcache *regcache;
+
+ regcache = get_thread_regcache (current_thread, 1);
+
+ /* Read watchpoints are set as access watchpoints, because of GDB's
+ inability to deal with pure read watchpoints. */
+ if (type == raw_bkpt_type_read_wp)
+ type = raw_bkpt_type_access_wp;
+
+ /* Get the configuration register. */
+ collect_register_by_name (regcache, "s0", &bp_ctrl);
+
+ /* The watchpoint allocation scheme is the simplest possible.
+ For example, if a region is watched for read and
+ a write watch is requested, a new watchpoint will
+ be used. Also, if a watch for a region that is already
+ covered by one or more existing watchpoints, a new
+ watchpoint will be used. */
+
+ /* First, find a free data watchpoint. */
+ for (bp = 0; bp < 6; bp++)
+ {
+ /* Each data watchpoint's control registers occupy 2 bits
+ (hence the 3), starting at bit 2 for D0 (hence the 2)
+ with 4 bits between for each watchpoint (yes, the 4). */
+ if (!(bp_ctrl & (0x3 << (2 + (bp * 4)))))
+ break;
+ }
+
+ if (bp > 5)
+ {
+ /* We're out of watchpoints. */
+ return -1;
+ }
+
+ /* Configure the control register first. */
+ if (type == raw_bkpt_type_read_wp || type == raw_bkpt_type_access_wp)
+ {
+ /* Trigger on read. */
+ bp_ctrl |= (1 << (2 + bp * 4));
+ }
+ if (type == raw_bkpt_type_write_wp || type == raw_bkpt_type_access_wp)
+ {
+ /* Trigger on write. */
+ bp_ctrl |= (2 << (2 + bp * 4));
+ }
+
+ /* Setup the configuration register. */
+ supply_register_by_name (regcache, "s0", &bp_ctrl);
+
+ /* Setup the range. */
+ start = addr;
+ end = addr + len - 1;
+
+ /* Configure the watchpoint register. */
+ cris_write_data_breakpoint (regcache, bp, start, end);
+
+ collect_register_by_name (regcache, "ccs", &ccs);
+ /* Set the S1 flag to enable watchpoints. */
+ ccs |= (1 << 19);
+ supply_register_by_name (regcache, "ccs", &ccs);
+
+ return 0;
+}
+
+static int
+cris_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, int len,
+ struct raw_breakpoint *bp)
+{
+ int bp;
+ unsigned long bp_ctrl;
+ unsigned long start, end;
+ struct regcache *regcache;
+ unsigned long bp_d_regs[12];
+
+ regcache = get_thread_regcache (current_thread, 1);
+
+ /* Read watchpoints are set as access watchpoints, because of GDB's
+ inability to deal with pure read watchpoints. */
+ if (type == raw_bkpt_type_read_wp)
+ type = raw_bkpt_type_access_wp;
+
+ /* Get the configuration register. */
+ collect_register_by_name (regcache, "s0", &bp_ctrl);
+
+ /* Try to find a watchpoint that is configured for the
+ specified range, then check that read/write also matches. */
+
+ /* Ugly pointer arithmetic, since I cannot rely on a
+ single switch (addr) as there may be several watchpoints with
+ the same start address for example. */
+
+ /* Get all range registers to simplify search. */
+ collect_register_by_name (regcache, "s3", &bp_d_regs[0]);
+ collect_register_by_name (regcache, "s4", &bp_d_regs[1]);
+ collect_register_by_name (regcache, "s5", &bp_d_regs[2]);
+ collect_register_by_name (regcache, "s6", &bp_d_regs[3]);
+ collect_register_by_name (regcache, "s7", &bp_d_regs[4]);
+ collect_register_by_name (regcache, "s8", &bp_d_regs[5]);
+ collect_register_by_name (regcache, "s9", &bp_d_regs[6]);
+ collect_register_by_name (regcache, "s10", &bp_d_regs[7]);
+ collect_register_by_name (regcache, "s11", &bp_d_regs[8]);
+ collect_register_by_name (regcache, "s12", &bp_d_regs[9]);
+ collect_register_by_name (regcache, "s13", &bp_d_regs[10]);
+ collect_register_by_name (regcache, "s14", &bp_d_regs[11]);
+
+ for (bp = 0; bp < 6; bp++)
+ {
+ if (bp_d_regs[bp * 2] == addr
+ && bp_d_regs[bp * 2 + 1] == (addr + len - 1)) {
+ /* Matching range. */
+ int bitpos = 2 + bp * 4;
+ int rw_bits;
+
+ /* Read/write bits for this BP. */
+ rw_bits = (bp_ctrl & (0x3 << bitpos)) >> bitpos;
+
+ if ((type == raw_bkpt_type_read_wp && rw_bits == 0x1)
+ || (type == raw_bkpt_type_write_wp && rw_bits == 0x2)
+ || (type == raw_bkpt_type_access_wp && rw_bits == 0x3))
+ {
+ /* Read/write matched. */
+ break;
+ }
+ }
+ }
+
+ if (bp > 5)
+ {
+ /* No watchpoint matched. */
+ return -1;
+ }
+
+ /* Found a matching watchpoint. Now, deconfigure it by
+ both disabling read/write in bp_ctrl and zeroing its
+ start/end addresses. */
+ bp_ctrl &= ~(3 << (2 + (bp * 4)));
+ /* Setup the configuration register. */
+ supply_register_by_name (regcache, "s0", &bp_ctrl);
+
+ start = end = 0;
+ /* Configure the watchpoint register. */
+ cris_write_data_breakpoint (regcache, bp, start, end);
+
+ /* Note that we don't clear the S1 flag here. It's done when continuing. */
+ return 0;
+}
+
+static int
+cris_stopped_by_watchpoint (void)
+{
+ unsigned long exs;
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+
+ collect_register_by_name (regcache, "exs", &exs);
+
+ return (((exs & 0xff00) >> 8) == 0xc);
+}
+
+static CORE_ADDR
+cris_stopped_data_address (void)
+{
+ unsigned long eda;
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+
+ collect_register_by_name (regcache, "eda", &eda);
+
+ /* FIXME: Possibly adjust to match watched range. */
+ return eda;
+}
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
+ return PS_ERR;
+
+ /* IDX is the bias from the thread pointer to the beginning of the
+ thread descriptor. It has to be subtracted due to implementation
+ quirks in libthread_db. */
+ *base = (void *) ((char *) *base - idx);
+ return PS_OK;
+}
+
+static void
+cris_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+
+ for (i = 0; i < cris_num_regs; i++)
+ {
+ if (cris_regmap[i] != -1)
+ collect_register (regcache, i, ((char *) buf) + cris_regmap[i]);
+ }
+}
+
+static void
+cris_store_gregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+
+ for (i = 0; i < cris_num_regs; i++)
+ {
+ if (cris_regmap[i] != -1)
+ supply_register (regcache, i, ((char *) buf) + cris_regmap[i]);
+ }
+}
+
+static void
+cris_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_crisv32;
+}
+
+/* Support for hardware single step. */
+
+static int
+cris_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static struct regset_info cris_regsets[] = {
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, cris_num_regs * 4,
+ GENERAL_REGS, cris_fill_gregset, cris_store_gregset },
+ NULL_REGSET
+};
+
+
+static struct regsets_info cris_regsets_info =
+ {
+ cris_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info cris_usrregs_info =
+ {
+ cris_num_regs,
+ cris_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &cris_usrregs_info,
+ &cris_regsets_info
+ };
+
+static const struct regs_info *
+cris_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ cris_arch_setup,
+ cris_regs_info,
+ NULL,
+ NULL,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_kind_from_pc */
+ cris_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
+ 0,
+ cris_breakpoint_at,
+ cris_supports_z_point_type,
+ cris_insert_point,
+ cris_remove_point,
+ cris_stopped_by_watchpoint,
+ cris_stopped_data_address,
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ cris_supports_hardware_single_step,
+};
+
+void
+initialize_low_arch (void)
+{
+ init_registers_crisv32 ();
+
+ initialize_regsets_info (&cris_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/x86 specific low level interface, for the in-process
- agent library for GDB.
-
- Copyright (C) 2010-2020 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-x86-tdesc.h"
-#include "gdbsupport/x86-xstate.h"
-
-/* GDB register numbers. */
-
-enum i386_gdb_regnum
-{
- I386_EAX_REGNUM, /* %eax */
- I386_ECX_REGNUM, /* %ecx */
- I386_EDX_REGNUM, /* %edx */
- I386_EBX_REGNUM, /* %ebx */
- I386_ESP_REGNUM, /* %esp */
- I386_EBP_REGNUM, /* %ebp */
- I386_ESI_REGNUM, /* %esi */
- I386_EDI_REGNUM, /* %edi */
- I386_EIP_REGNUM, /* %eip */
- I386_EFLAGS_REGNUM, /* %eflags */
- I386_CS_REGNUM, /* %cs */
- I386_SS_REGNUM, /* %ss */
- I386_DS_REGNUM, /* %ds */
- I386_ES_REGNUM, /* %es */
- I386_FS_REGNUM, /* %fs */
- I386_GS_REGNUM, /* %gs */
- I386_ST0_REGNUM /* %st(0) */
-};
-
-#define i386_num_regs 16
-
-#define FT_CR_EAX 15
-#define FT_CR_ECX 14
-#define FT_CR_EDX 13
-#define FT_CR_EBX 12
-#define FT_CR_UESP 11
-#define FT_CR_EBP 10
-#define FT_CR_ESI 9
-#define FT_CR_EDI 8
-#define FT_CR_EIP 7
-#define FT_CR_EFL 6
-#define FT_CR_DS 5
-#define FT_CR_ES 4
-#define FT_CR_FS 3
-#define FT_CR_GS 2
-#define FT_CR_SS 1
-#define FT_CR_CS 0
-
-/* Mapping between the general-purpose registers in jump tracepoint
- format and GDB's register array layout. */
-
-static const int i386_ft_collect_regmap[] =
-{
- FT_CR_EAX * 4, FT_CR_ECX * 4, FT_CR_EDX * 4, FT_CR_EBX * 4,
- FT_CR_UESP * 4, FT_CR_EBP * 4, FT_CR_ESI * 4, FT_CR_EDI * 4,
- FT_CR_EIP * 4, FT_CR_EFL * 4, FT_CR_CS * 4, FT_CR_SS * 4,
- FT_CR_DS * 4, FT_CR_ES * 4, FT_CR_FS * 4, FT_CR_GS * 4
-};
-
-void
-supply_fast_tracepoint_registers (struct regcache *regcache,
- const unsigned char *buf)
-{
- int i;
-
- for (i = 0; i < i386_num_regs; i++)
- {
- int regval;
-
- if (i >= I386_CS_REGNUM && i <= I386_GS_REGNUM)
- regval = *(short *) (((char *) buf) + i386_ft_collect_regmap[i]);
- else
- regval = *(int *) (((char *) buf) + i386_ft_collect_regmap[i]);
-
- supply_register (regcache, i, ®val);
- }
-}
-
-ULONGEST
-get_raw_reg (const unsigned char *raw_regs, int regnum)
-{
- /* This should maybe be allowed to return an error code, or perhaps
- better, have the emit_reg detect this, and emit a constant zero,
- or something. */
-
- if (regnum > i386_num_regs)
- return 0;
- else if (regnum >= I386_CS_REGNUM && regnum <= I386_GS_REGNUM)
- return *(short *) (raw_regs + i386_ft_collect_regmap[regnum]);
- else
- return *(int *) (raw_regs + i386_ft_collect_regmap[regnum]);
-}
-
-#ifdef HAVE_UST
-
-#include <ust/processor.h>
-
-/* "struct registers" is the UST object type holding the registers at
- the time of the static tracepoint marker call. This doesn't
- contain EIP, but we know what it must have been (the marker
- address). */
-
-#define ST_REGENTRY(REG) \
- { \
- offsetof (struct registers, REG), \
- sizeof (((struct registers *) NULL)->REG) \
- }
-
-static struct
-{
- int offset;
- int size;
-} i386_st_collect_regmap[] =
- {
- ST_REGENTRY(eax),
- ST_REGENTRY(ecx),
- ST_REGENTRY(edx),
- ST_REGENTRY(ebx),
- ST_REGENTRY(esp),
- ST_REGENTRY(ebp),
- ST_REGENTRY(esi),
- ST_REGENTRY(edi),
- { -1, 0 }, /* eip */
- ST_REGENTRY(eflags),
- ST_REGENTRY(cs),
- ST_REGENTRY(ss),
- };
-
-#define i386_NUM_ST_COLLECT_GREGS \
- (sizeof (i386_st_collect_regmap) / sizeof (i386_st_collect_regmap[0]))
-
-void
-supply_static_tracepoint_registers (struct regcache *regcache,
- const unsigned char *buf,
- CORE_ADDR pc)
-{
- int i;
- unsigned int newpc = pc;
-
- supply_register (regcache, I386_EIP_REGNUM, &newpc);
-
- for (i = 0; i < i386_NUM_ST_COLLECT_GREGS; i++)
- if (i386_st_collect_regmap[i].offset != -1)
- {
- switch (i386_st_collect_regmap[i].size)
- {
- case 4:
- supply_register (regcache, i,
- ((char *) buf)
- + i386_st_collect_regmap[i].offset);
- break;
- case 2:
- {
- unsigned long reg
- = * (short *) (((char *) buf)
- + i386_st_collect_regmap[i].offset);
- reg &= 0xffff;
- supply_register (regcache, i, ®);
- }
- break;
- default:
- internal_error (__FILE__, __LINE__, "unhandled register size: %d",
- i386_st_collect_regmap[i].size);
- }
- }
-}
-
-#endif /* HAVE_UST */
-
-
-/* This is only needed because reg-i386-linux-lib.o references it. We
- may use it proper at some point. */
-const char *gdbserver_xmltarget;
-
-/* Attempt to allocate memory for trampolines in the first 64 KiB of
- memory to enable smaller jump patches. */
-
-static void
-initialize_fast_tracepoint_trampoline_buffer (void)
-{
- const CORE_ADDR buffer_end = 64 * 1024;
- /* Ensure that the buffer will be at least 1 KiB in size, which is
- enough space for over 200 fast tracepoints. */
- const int min_buffer_size = 1024;
- char buf[IPA_BUFSIZ];
- CORE_ADDR mmap_min_addr = buffer_end + 1;
- ULONGEST buffer_size;
- FILE *f = fopen ("/proc/sys/vm/mmap_min_addr", "r");
-
- if (!f)
- {
- snprintf (buf, sizeof (buf), "mmap_min_addr open failed: %s",
- safe_strerror (errno));
- set_trampoline_buffer_space (0, 0, buf);
- return;
- }
-
- if (fgets (buf, IPA_BUFSIZ, f))
- sscanf (buf, "%llu", &mmap_min_addr);
-
- fclose (f);
-
- buffer_size = buffer_end - mmap_min_addr;
-
- if (buffer_size >= min_buffer_size)
- {
- if (mmap ((void *) (uintptr_t) mmap_min_addr, buffer_size,
- PROT_READ | PROT_EXEC | PROT_WRITE,
- MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
- -1, 0)
- != MAP_FAILED)
- set_trampoline_buffer_space (mmap_min_addr, buffer_end, NULL);
- else
- {
- snprintf (buf, IPA_BUFSIZ, "low-64K-buffer mmap() failed: %s",
- safe_strerror (errno));
- set_trampoline_buffer_space (0, 0, buf);
- }
- }
- else
- {
- snprintf (buf, IPA_BUFSIZ, "mmap_min_addr is %d, must be %d or less",
- (int) mmap_min_addr, (int) buffer_end - min_buffer_size);
- set_trampoline_buffer_space (0, 0, buf);
- }
-}
-
-/* Map the tdesc index to xcr0 mask. */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
- X86_XSTATE_X87_MASK,
- X86_XSTATE_SSE_MASK,
- X86_XSTATE_AVX_MASK,
- X86_XSTATE_MPX_MASK,
- X86_XSTATE_AVX_MPX_MASK,
- X86_XSTATE_AVX_AVX512_MASK,
- X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-
-/* Return target_desc to use for IPA, given the tdesc index passed by
- gdbserver. */
-
-const struct target_desc *
-get_ipa_tdesc (int idx)
-{
- if (idx >= X86_TDESC_LAST)
- {
- internal_error (__FILE__, __LINE__,
- "unknown ipa tdesc index: %d", idx);
- }
- return i386_linux_read_description (idx2mask[idx]);
-}
-
-/* Allocate buffer for the jump pads. On i386, we can reach an arbitrary
- address with a jump instruction, so just allocate normally. */
-
-void *
-alloc_jump_pad_buffer (size_t size)
-{
- void *res = mmap (NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-
- if (res == MAP_FAILED)
- return NULL;
-
- return res;
-}
-
-void
-initialize_low_tracepoint (void)
-{
- initialize_fast_tracepoint_trampoline_buffer ();
- for (auto i = 0; i < X86_TDESC_LAST; i++)
- i386_linux_read_description (idx2mask[i]);
-}
--- /dev/null
+/* GNU/Linux/x86 specific low level interface, for the in-process
+ agent library for GDB.
+
+ Copyright (C) 2010-2020 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-x86-tdesc.h"
+#include "gdbsupport/x86-xstate.h"
+
+/* GDB register numbers. */
+
+enum i386_gdb_regnum
+{
+ I386_EAX_REGNUM, /* %eax */
+ I386_ECX_REGNUM, /* %ecx */
+ I386_EDX_REGNUM, /* %edx */
+ I386_EBX_REGNUM, /* %ebx */
+ I386_ESP_REGNUM, /* %esp */
+ I386_EBP_REGNUM, /* %ebp */
+ I386_ESI_REGNUM, /* %esi */
+ I386_EDI_REGNUM, /* %edi */
+ I386_EIP_REGNUM, /* %eip */
+ I386_EFLAGS_REGNUM, /* %eflags */
+ I386_CS_REGNUM, /* %cs */
+ I386_SS_REGNUM, /* %ss */
+ I386_DS_REGNUM, /* %ds */
+ I386_ES_REGNUM, /* %es */
+ I386_FS_REGNUM, /* %fs */
+ I386_GS_REGNUM, /* %gs */
+ I386_ST0_REGNUM /* %st(0) */
+};
+
+#define i386_num_regs 16
+
+#define FT_CR_EAX 15
+#define FT_CR_ECX 14
+#define FT_CR_EDX 13
+#define FT_CR_EBX 12
+#define FT_CR_UESP 11
+#define FT_CR_EBP 10
+#define FT_CR_ESI 9
+#define FT_CR_EDI 8
+#define FT_CR_EIP 7
+#define FT_CR_EFL 6
+#define FT_CR_DS 5
+#define FT_CR_ES 4
+#define FT_CR_FS 3
+#define FT_CR_GS 2
+#define FT_CR_SS 1
+#define FT_CR_CS 0
+
+/* Mapping between the general-purpose registers in jump tracepoint
+ format and GDB's register array layout. */
+
+static const int i386_ft_collect_regmap[] =
+{
+ FT_CR_EAX * 4, FT_CR_ECX * 4, FT_CR_EDX * 4, FT_CR_EBX * 4,
+ FT_CR_UESP * 4, FT_CR_EBP * 4, FT_CR_ESI * 4, FT_CR_EDI * 4,
+ FT_CR_EIP * 4, FT_CR_EFL * 4, FT_CR_CS * 4, FT_CR_SS * 4,
+ FT_CR_DS * 4, FT_CR_ES * 4, FT_CR_FS * 4, FT_CR_GS * 4
+};
+
+void
+supply_fast_tracepoint_registers (struct regcache *regcache,
+ const unsigned char *buf)
+{
+ int i;
+
+ for (i = 0; i < i386_num_regs; i++)
+ {
+ int regval;
+
+ if (i >= I386_CS_REGNUM && i <= I386_GS_REGNUM)
+ regval = *(short *) (((char *) buf) + i386_ft_collect_regmap[i]);
+ else
+ regval = *(int *) (((char *) buf) + i386_ft_collect_regmap[i]);
+
+ supply_register (regcache, i, ®val);
+ }
+}
+
+ULONGEST
+get_raw_reg (const unsigned char *raw_regs, int regnum)
+{
+ /* This should maybe be allowed to return an error code, or perhaps
+ better, have the emit_reg detect this, and emit a constant zero,
+ or something. */
+
+ if (regnum > i386_num_regs)
+ return 0;
+ else if (regnum >= I386_CS_REGNUM && regnum <= I386_GS_REGNUM)
+ return *(short *) (raw_regs + i386_ft_collect_regmap[regnum]);
+ else
+ return *(int *) (raw_regs + i386_ft_collect_regmap[regnum]);
+}
+
+#ifdef HAVE_UST
+
+#include <ust/processor.h>
+
+/* "struct registers" is the UST object type holding the registers at
+ the time of the static tracepoint marker call. This doesn't
+ contain EIP, but we know what it must have been (the marker
+ address). */
+
+#define ST_REGENTRY(REG) \
+ { \
+ offsetof (struct registers, REG), \
+ sizeof (((struct registers *) NULL)->REG) \
+ }
+
+static struct
+{
+ int offset;
+ int size;
+} i386_st_collect_regmap[] =
+ {
+ ST_REGENTRY(eax),
+ ST_REGENTRY(ecx),
+ ST_REGENTRY(edx),
+ ST_REGENTRY(ebx),
+ ST_REGENTRY(esp),
+ ST_REGENTRY(ebp),
+ ST_REGENTRY(esi),
+ ST_REGENTRY(edi),
+ { -1, 0 }, /* eip */
+ ST_REGENTRY(eflags),
+ ST_REGENTRY(cs),
+ ST_REGENTRY(ss),
+ };
+
+#define i386_NUM_ST_COLLECT_GREGS \
+ (sizeof (i386_st_collect_regmap) / sizeof (i386_st_collect_regmap[0]))
+
+void
+supply_static_tracepoint_registers (struct regcache *regcache,
+ const unsigned char *buf,
+ CORE_ADDR pc)
+{
+ int i;
+ unsigned int newpc = pc;
+
+ supply_register (regcache, I386_EIP_REGNUM, &newpc);
+
+ for (i = 0; i < i386_NUM_ST_COLLECT_GREGS; i++)
+ if (i386_st_collect_regmap[i].offset != -1)
+ {
+ switch (i386_st_collect_regmap[i].size)
+ {
+ case 4:
+ supply_register (regcache, i,
+ ((char *) buf)
+ + i386_st_collect_regmap[i].offset);
+ break;
+ case 2:
+ {
+ unsigned long reg
+ = * (short *) (((char *) buf)
+ + i386_st_collect_regmap[i].offset);
+ reg &= 0xffff;
+ supply_register (regcache, i, ®);
+ }
+ break;
+ default:
+ internal_error (__FILE__, __LINE__, "unhandled register size: %d",
+ i386_st_collect_regmap[i].size);
+ }
+ }
+}
+
+#endif /* HAVE_UST */
+
+
+/* This is only needed because reg-i386-linux-lib.o references it. We
+ may use it proper at some point. */
+const char *gdbserver_xmltarget;
+
+/* Attempt to allocate memory for trampolines in the first 64 KiB of
+ memory to enable smaller jump patches. */
+
+static void
+initialize_fast_tracepoint_trampoline_buffer (void)
+{
+ const CORE_ADDR buffer_end = 64 * 1024;
+ /* Ensure that the buffer will be at least 1 KiB in size, which is
+ enough space for over 200 fast tracepoints. */
+ const int min_buffer_size = 1024;
+ char buf[IPA_BUFSIZ];
+ CORE_ADDR mmap_min_addr = buffer_end + 1;
+ ULONGEST buffer_size;
+ FILE *f = fopen ("/proc/sys/vm/mmap_min_addr", "r");
+
+ if (!f)
+ {
+ snprintf (buf, sizeof (buf), "mmap_min_addr open failed: %s",
+ safe_strerror (errno));
+ set_trampoline_buffer_space (0, 0, buf);
+ return;
+ }
+
+ if (fgets (buf, IPA_BUFSIZ, f))
+ sscanf (buf, "%llu", &mmap_min_addr);
+
+ fclose (f);
+
+ buffer_size = buffer_end - mmap_min_addr;
+
+ if (buffer_size >= min_buffer_size)
+ {
+ if (mmap ((void *) (uintptr_t) mmap_min_addr, buffer_size,
+ PROT_READ | PROT_EXEC | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0)
+ != MAP_FAILED)
+ set_trampoline_buffer_space (mmap_min_addr, buffer_end, NULL);
+ else
+ {
+ snprintf (buf, IPA_BUFSIZ, "low-64K-buffer mmap() failed: %s",
+ safe_strerror (errno));
+ set_trampoline_buffer_space (0, 0, buf);
+ }
+ }
+ else
+ {
+ snprintf (buf, IPA_BUFSIZ, "mmap_min_addr is %d, must be %d or less",
+ (int) mmap_min_addr, (int) buffer_end - min_buffer_size);
+ set_trampoline_buffer_space (0, 0, buf);
+ }
+}
+
+/* Map the tdesc index to xcr0 mask. */
+static uint64_t idx2mask[X86_TDESC_LAST] = {
+ X86_XSTATE_X87_MASK,
+ X86_XSTATE_SSE_MASK,
+ X86_XSTATE_AVX_MASK,
+ X86_XSTATE_MPX_MASK,
+ X86_XSTATE_AVX_MPX_MASK,
+ X86_XSTATE_AVX_AVX512_MASK,
+ X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
+};
+
+/* Return target_desc to use for IPA, given the tdesc index passed by
+ gdbserver. */
+
+const struct target_desc *
+get_ipa_tdesc (int idx)
+{
+ if (idx >= X86_TDESC_LAST)
+ {
+ internal_error (__FILE__, __LINE__,
+ "unknown ipa tdesc index: %d", idx);
+ }
+ return i386_linux_read_description (idx2mask[idx]);
+}
+
+/* Allocate buffer for the jump pads. On i386, we can reach an arbitrary
+ address with a jump instruction, so just allocate normally. */
+
+void *
+alloc_jump_pad_buffer (size_t size)
+{
+ void *res = mmap (NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (res == MAP_FAILED)
+ return NULL;
+
+ return res;
+}
+
+void
+initialize_low_tracepoint (void)
+{
+ initialize_fast_tracepoint_trampoline_buffer ();
+ for (auto i = 0; i < X86_TDESC_LAST; i++)
+ i386_linux_read_description (idx2mask[i]);
+}
+++ /dev/null
-/* GNU/Linux/IA64 specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-/* Defined in auto-generated file reg-ia64.c. */
-void init_registers_ia64 (void);
-extern const struct target_desc *tdesc_ia64;
-
-#define ia64_num_regs 462
-
-#include <asm/ptrace_offsets.h>
-
-static int ia64_regmap[] =
- {
- /* general registers */
- -1, /* gr0 not available; i.e, it's always zero */
- PT_R1,
- PT_R2,
- PT_R3,
- PT_R4,
- PT_R5,
- PT_R6,
- PT_R7,
- PT_R8,
- PT_R9,
- PT_R10,
- PT_R11,
- PT_R12,
- PT_R13,
- PT_R14,
- PT_R15,
- PT_R16,
- PT_R17,
- PT_R18,
- PT_R19,
- PT_R20,
- PT_R21,
- PT_R22,
- PT_R23,
- PT_R24,
- PT_R25,
- PT_R26,
- PT_R27,
- PT_R28,
- PT_R29,
- PT_R30,
- PT_R31,
- /* gr32 through gr127 not directly available via the ptrace interface */
- -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,
- -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,
- -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,
- /* Floating point registers */
- -1, -1, /* f0 and f1 not available (f0 is +0.0 and f1 is +1.0) */
- PT_F2,
- PT_F3,
- PT_F4,
- PT_F5,
- PT_F6,
- PT_F7,
- PT_F8,
- PT_F9,
- PT_F10,
- PT_F11,
- PT_F12,
- PT_F13,
- PT_F14,
- PT_F15,
- PT_F16,
- PT_F17,
- PT_F18,
- PT_F19,
- PT_F20,
- PT_F21,
- PT_F22,
- PT_F23,
- PT_F24,
- PT_F25,
- PT_F26,
- PT_F27,
- PT_F28,
- PT_F29,
- PT_F30,
- PT_F31,
- PT_F32,
- PT_F33,
- PT_F34,
- PT_F35,
- PT_F36,
- PT_F37,
- PT_F38,
- PT_F39,
- PT_F40,
- PT_F41,
- PT_F42,
- PT_F43,
- PT_F44,
- PT_F45,
- PT_F46,
- PT_F47,
- PT_F48,
- PT_F49,
- PT_F50,
- PT_F51,
- PT_F52,
- PT_F53,
- PT_F54,
- PT_F55,
- PT_F56,
- PT_F57,
- PT_F58,
- PT_F59,
- PT_F60,
- PT_F61,
- PT_F62,
- PT_F63,
- PT_F64,
- PT_F65,
- PT_F66,
- PT_F67,
- PT_F68,
- PT_F69,
- PT_F70,
- PT_F71,
- PT_F72,
- PT_F73,
- PT_F74,
- PT_F75,
- PT_F76,
- PT_F77,
- PT_F78,
- PT_F79,
- PT_F80,
- PT_F81,
- PT_F82,
- PT_F83,
- PT_F84,
- PT_F85,
- PT_F86,
- PT_F87,
- PT_F88,
- PT_F89,
- PT_F90,
- PT_F91,
- PT_F92,
- PT_F93,
- PT_F94,
- PT_F95,
- PT_F96,
- PT_F97,
- PT_F98,
- PT_F99,
- PT_F100,
- PT_F101,
- PT_F102,
- PT_F103,
- PT_F104,
- PT_F105,
- PT_F106,
- PT_F107,
- PT_F108,
- PT_F109,
- PT_F110,
- PT_F111,
- PT_F112,
- PT_F113,
- PT_F114,
- PT_F115,
- PT_F116,
- PT_F117,
- PT_F118,
- PT_F119,
- PT_F120,
- PT_F121,
- PT_F122,
- PT_F123,
- PT_F124,
- PT_F125,
- PT_F126,
- PT_F127,
- /* predicate registers - we don't fetch these individually */
- -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,
- -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,
- /* branch registers */
- PT_B0,
- PT_B1,
- PT_B2,
- PT_B3,
- PT_B4,
- PT_B5,
- PT_B6,
- PT_B7,
- /* virtual frame pointer and virtual return address pointer */
- -1, -1,
- /* other registers */
- PT_PR,
- PT_CR_IIP, /* ip */
- PT_CR_IPSR, /* psr */
- PT_CFM, /* cfm */
- /* kernel registers not visible via ptrace interface (?) */
- -1, -1, -1, -1, -1, -1, -1, -1,
- /* hole */
- -1, -1, -1, -1, -1, -1, -1, -1,
- PT_AR_RSC,
- PT_AR_BSP,
- PT_AR_BSPSTORE,
- PT_AR_RNAT,
- -1,
- -1, /* Not available: FCR, IA32 floating control register */
- -1, -1,
- -1, /* Not available: EFLAG */
- -1, /* Not available: CSD */
- -1, /* Not available: SSD */
- -1, /* Not available: CFLG */
- -1, /* Not available: FSR */
- -1, /* Not available: FIR */
- -1, /* Not available: FDR */
- -1,
- PT_AR_CCV,
- -1, -1, -1,
- PT_AR_UNAT,
- -1, -1, -1,
- PT_AR_FPSR,
- -1, -1, -1,
- -1, /* Not available: ITC */
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1,
- PT_AR_PFS,
- PT_AR_LC,
- PT_AR_EC,
- -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, -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,
- };
-
-static int
-ia64_cannot_store_register (int regno)
-{
- return 0;
-}
-
-static int
-ia64_cannot_fetch_register (int regno)
-{
- return 0;
-}
-
-/* GDB register numbers. */
-#define IA64_GR0_REGNUM 0
-#define IA64_FR0_REGNUM 128
-#define IA64_FR1_REGNUM 129
-
-static int
-ia64_fetch_register (struct regcache *regcache, int regnum)
-{
- /* r0 cannot be fetched but is always zero. */
- if (regnum == IA64_GR0_REGNUM)
- {
- const gdb_byte zero[8] = { 0 };
-
- gdb_assert (sizeof (zero) == register_size (regcache->tdesc, regnum));
- supply_register (regcache, regnum, zero);
- return 1;
- }
-
- /* fr0 cannot be fetched but is always zero. */
- if (regnum == IA64_FR0_REGNUM)
- {
- const gdb_byte f_zero[16] = { 0 };
-
- gdb_assert (sizeof (f_zero) == register_size (regcache->tdesc, regnum));
- supply_register (regcache, regnum, f_zero);
- return 1;
- }
-
- /* fr1 cannot be fetched but is always one (1.0). */
- if (regnum == IA64_FR1_REGNUM)
- {
- const gdb_byte f_one[16] =
- { 0, 0, 0, 0, 0, 0, 0, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0 };
-
- gdb_assert (sizeof (f_one) == register_size (regcache->tdesc, regnum));
- supply_register (regcache, regnum, f_one);
- return 1;
- }
-
- return 0;
-}
-
-static struct usrregs_info ia64_usrregs_info =
- {
- ia64_num_regs,
- ia64_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &ia64_usrregs_info
- };
-
-static const struct regs_info *
-ia64_regs_info (void)
-{
- return ®s_info;
-}
-
-static void
-ia64_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_ia64;
-}
-
-
-struct linux_target_ops the_low_target = {
- ia64_arch_setup,
- ia64_regs_info,
- ia64_cannot_fetch_register,
- ia64_cannot_store_register,
- ia64_fetch_register,
-};
-
-void
-initialize_low_arch (void)
-{
- init_registers_ia64 ();
-}
--- /dev/null
+/* GNU/Linux/IA64 specific low level interface, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+/* Defined in auto-generated file reg-ia64.c. */
+void init_registers_ia64 (void);
+extern const struct target_desc *tdesc_ia64;
+
+#define ia64_num_regs 462
+
+#include <asm/ptrace_offsets.h>
+
+static int ia64_regmap[] =
+ {
+ /* general registers */
+ -1, /* gr0 not available; i.e, it's always zero */
+ PT_R1,
+ PT_R2,
+ PT_R3,
+ PT_R4,
+ PT_R5,
+ PT_R6,
+ PT_R7,
+ PT_R8,
+ PT_R9,
+ PT_R10,
+ PT_R11,
+ PT_R12,
+ PT_R13,
+ PT_R14,
+ PT_R15,
+ PT_R16,
+ PT_R17,
+ PT_R18,
+ PT_R19,
+ PT_R20,
+ PT_R21,
+ PT_R22,
+ PT_R23,
+ PT_R24,
+ PT_R25,
+ PT_R26,
+ PT_R27,
+ PT_R28,
+ PT_R29,
+ PT_R30,
+ PT_R31,
+ /* gr32 through gr127 not directly available via the ptrace interface */
+ -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,
+ -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,
+ -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,
+ /* Floating point registers */
+ -1, -1, /* f0 and f1 not available (f0 is +0.0 and f1 is +1.0) */
+ PT_F2,
+ PT_F3,
+ PT_F4,
+ PT_F5,
+ PT_F6,
+ PT_F7,
+ PT_F8,
+ PT_F9,
+ PT_F10,
+ PT_F11,
+ PT_F12,
+ PT_F13,
+ PT_F14,
+ PT_F15,
+ PT_F16,
+ PT_F17,
+ PT_F18,
+ PT_F19,
+ PT_F20,
+ PT_F21,
+ PT_F22,
+ PT_F23,
+ PT_F24,
+ PT_F25,
+ PT_F26,
+ PT_F27,
+ PT_F28,
+ PT_F29,
+ PT_F30,
+ PT_F31,
+ PT_F32,
+ PT_F33,
+ PT_F34,
+ PT_F35,
+ PT_F36,
+ PT_F37,
+ PT_F38,
+ PT_F39,
+ PT_F40,
+ PT_F41,
+ PT_F42,
+ PT_F43,
+ PT_F44,
+ PT_F45,
+ PT_F46,
+ PT_F47,
+ PT_F48,
+ PT_F49,
+ PT_F50,
+ PT_F51,
+ PT_F52,
+ PT_F53,
+ PT_F54,
+ PT_F55,
+ PT_F56,
+ PT_F57,
+ PT_F58,
+ PT_F59,
+ PT_F60,
+ PT_F61,
+ PT_F62,
+ PT_F63,
+ PT_F64,
+ PT_F65,
+ PT_F66,
+ PT_F67,
+ PT_F68,
+ PT_F69,
+ PT_F70,
+ PT_F71,
+ PT_F72,
+ PT_F73,
+ PT_F74,
+ PT_F75,
+ PT_F76,
+ PT_F77,
+ PT_F78,
+ PT_F79,
+ PT_F80,
+ PT_F81,
+ PT_F82,
+ PT_F83,
+ PT_F84,
+ PT_F85,
+ PT_F86,
+ PT_F87,
+ PT_F88,
+ PT_F89,
+ PT_F90,
+ PT_F91,
+ PT_F92,
+ PT_F93,
+ PT_F94,
+ PT_F95,
+ PT_F96,
+ PT_F97,
+ PT_F98,
+ PT_F99,
+ PT_F100,
+ PT_F101,
+ PT_F102,
+ PT_F103,
+ PT_F104,
+ PT_F105,
+ PT_F106,
+ PT_F107,
+ PT_F108,
+ PT_F109,
+ PT_F110,
+ PT_F111,
+ PT_F112,
+ PT_F113,
+ PT_F114,
+ PT_F115,
+ PT_F116,
+ PT_F117,
+ PT_F118,
+ PT_F119,
+ PT_F120,
+ PT_F121,
+ PT_F122,
+ PT_F123,
+ PT_F124,
+ PT_F125,
+ PT_F126,
+ PT_F127,
+ /* predicate registers - we don't fetch these individually */
+ -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,
+ -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,
+ /* branch registers */
+ PT_B0,
+ PT_B1,
+ PT_B2,
+ PT_B3,
+ PT_B4,
+ PT_B5,
+ PT_B6,
+ PT_B7,
+ /* virtual frame pointer and virtual return address pointer */
+ -1, -1,
+ /* other registers */
+ PT_PR,
+ PT_CR_IIP, /* ip */
+ PT_CR_IPSR, /* psr */
+ PT_CFM, /* cfm */
+ /* kernel registers not visible via ptrace interface (?) */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* hole */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ PT_AR_RSC,
+ PT_AR_BSP,
+ PT_AR_BSPSTORE,
+ PT_AR_RNAT,
+ -1,
+ -1, /* Not available: FCR, IA32 floating control register */
+ -1, -1,
+ -1, /* Not available: EFLAG */
+ -1, /* Not available: CSD */
+ -1, /* Not available: SSD */
+ -1, /* Not available: CFLG */
+ -1, /* Not available: FSR */
+ -1, /* Not available: FIR */
+ -1, /* Not available: FDR */
+ -1,
+ PT_AR_CCV,
+ -1, -1, -1,
+ PT_AR_UNAT,
+ -1, -1, -1,
+ PT_AR_FPSR,
+ -1, -1, -1,
+ -1, /* Not available: ITC */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ PT_AR_PFS,
+ PT_AR_LC,
+ PT_AR_EC,
+ -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, -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,
+ };
+
+static int
+ia64_cannot_store_register (int regno)
+{
+ return 0;
+}
+
+static int
+ia64_cannot_fetch_register (int regno)
+{
+ return 0;
+}
+
+/* GDB register numbers. */
+#define IA64_GR0_REGNUM 0
+#define IA64_FR0_REGNUM 128
+#define IA64_FR1_REGNUM 129
+
+static int
+ia64_fetch_register (struct regcache *regcache, int regnum)
+{
+ /* r0 cannot be fetched but is always zero. */
+ if (regnum == IA64_GR0_REGNUM)
+ {
+ const gdb_byte zero[8] = { 0 };
+
+ gdb_assert (sizeof (zero) == register_size (regcache->tdesc, regnum));
+ supply_register (regcache, regnum, zero);
+ return 1;
+ }
+
+ /* fr0 cannot be fetched but is always zero. */
+ if (regnum == IA64_FR0_REGNUM)
+ {
+ const gdb_byte f_zero[16] = { 0 };
+
+ gdb_assert (sizeof (f_zero) == register_size (regcache->tdesc, regnum));
+ supply_register (regcache, regnum, f_zero);
+ return 1;
+ }
+
+ /* fr1 cannot be fetched but is always one (1.0). */
+ if (regnum == IA64_FR1_REGNUM)
+ {
+ const gdb_byte f_one[16] =
+ { 0, 0, 0, 0, 0, 0, 0, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0 };
+
+ gdb_assert (sizeof (f_one) == register_size (regcache->tdesc, regnum));
+ supply_register (regcache, regnum, f_one);
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct usrregs_info ia64_usrregs_info =
+ {
+ ia64_num_regs,
+ ia64_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &ia64_usrregs_info
+ };
+
+static const struct regs_info *
+ia64_regs_info (void)
+{
+ return ®s_info;
+}
+
+static void
+ia64_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_ia64;
+}
+
+
+struct linux_target_ops the_low_target = {
+ ia64_arch_setup,
+ ia64_regs_info,
+ ia64_cannot_fetch_register,
+ ia64_cannot_store_register,
+ ia64_fetch_register,
+};
+
+void
+initialize_low_arch (void)
+{
+ init_registers_ia64 ();
+}
+++ /dev/null
-/* Low level interface to ptrace, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "nat/linux-osdata.h"
-#include "gdbsupport/agent.h"
-#include "tdesc.h"
-#include "gdbsupport/rsp-low.h"
-#include "gdbsupport/signals-state-save-restore.h"
-#include "nat/linux-nat.h"
-#include "nat/linux-waitpid.h"
-#include "gdbsupport/gdb_wait.h"
-#include "nat/gdb_ptrace.h"
-#include "nat/linux-ptrace.h"
-#include "nat/linux-procfs.h"
-#include "nat/linux-personality.h"
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <sched.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/vfs.h>
-#include <sys/uio.h>
-#include "gdbsupport/filestuff.h"
-#include "tracepoint.h"
-#include "hostio.h"
-#include <inttypes.h>
-#include "gdbsupport/common-inferior.h"
-#include "nat/fork-inferior.h"
-#include "gdbsupport/environ.h"
-#include "gdbsupport/gdb-sigmask.h"
-#include "gdbsupport/scoped_restore.h"
-#ifndef ELFMAG0
-/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
- then ELFMAG0 will have been defined. If it didn't get included by
- gdb_proc_service.h then including it will likely introduce a duplicate
- definition of elf_fpregset_t. */
-#include <elf.h>
-#endif
-#include "nat/linux-namespaces.h"
-
-#ifdef HAVE_PERSONALITY
-# include <sys/personality.h>
-# if !HAVE_DECL_ADDR_NO_RANDOMIZE
-# define ADDR_NO_RANDOMIZE 0x0040000
-# endif
-#endif
-
-#ifndef O_LARGEFILE
-#define O_LARGEFILE 0
-#endif
-
-#ifndef AT_HWCAP2
-#define AT_HWCAP2 26
-#endif
-
-/* Some targets did not define these ptrace constants from the start,
- so gdbserver defines them locally here. In the future, these may
- be removed after they are added to asm/ptrace.h. */
-#if !(defined(PT_TEXT_ADDR) \
- || defined(PT_DATA_ADDR) \
- || defined(PT_TEXT_END_ADDR))
-#if defined(__mcoldfire__)
-/* These are still undefined in 3.10 kernels. */
-#define PT_TEXT_ADDR 49*4
-#define PT_DATA_ADDR 50*4
-#define PT_TEXT_END_ADDR 51*4
-/* BFIN already defines these since at least 2.6.32 kernels. */
-#elif defined(BFIN)
-#define PT_TEXT_ADDR 220
-#define PT_TEXT_END_ADDR 224
-#define PT_DATA_ADDR 228
-/* These are still undefined in 3.10 kernels. */
-#elif defined(__TMS320C6X__)
-#define PT_TEXT_ADDR (0x10000*4)
-#define PT_DATA_ADDR (0x10004*4)
-#define PT_TEXT_END_ADDR (0x10008*4)
-#endif
-#endif
-
-#ifdef HAVE_LINUX_BTRACE
-# include "nat/linux-btrace.h"
-# include "gdbsupport/btrace-common.h"
-#endif
-
-#ifndef HAVE_ELF32_AUXV_T
-/* Copied from glibc's elf.h. */
-typedef struct
-{
- uint32_t a_type; /* Entry type */
- union
- {
- uint32_t a_val; /* Integer value */
- /* We use to have pointer elements added here. We cannot do that,
- though, since it does not work when using 32-bit definitions
- on 64-bit platforms and vice versa. */
- } a_un;
-} Elf32_auxv_t;
-#endif
-
-#ifndef HAVE_ELF64_AUXV_T
-/* Copied from glibc's elf.h. */
-typedef struct
-{
- uint64_t a_type; /* Entry type */
- union
- {
- uint64_t a_val; /* Integer value */
- /* We use to have pointer elements added here. We cannot do that,
- though, since it does not work when using 32-bit definitions
- on 64-bit platforms and vice versa. */
- } a_un;
-} Elf64_auxv_t;
-#endif
-
-/* Does the current host support PTRACE_GETREGSET? */
-int have_ptrace_getregset = -1;
-
-/* LWP accessors. */
-
-/* See nat/linux-nat.h. */
-
-ptid_t
-ptid_of_lwp (struct lwp_info *lwp)
-{
- return ptid_of (get_lwp_thread (lwp));
-}
-
-/* See nat/linux-nat.h. */
-
-void
-lwp_set_arch_private_info (struct lwp_info *lwp,
- struct arch_lwp_info *info)
-{
- lwp->arch_private = info;
-}
-
-/* See nat/linux-nat.h. */
-
-struct arch_lwp_info *
-lwp_arch_private_info (struct lwp_info *lwp)
-{
- return lwp->arch_private;
-}
-
-/* See nat/linux-nat.h. */
-
-int
-lwp_is_stopped (struct lwp_info *lwp)
-{
- return lwp->stopped;
-}
-
-/* See nat/linux-nat.h. */
-
-enum target_stop_reason
-lwp_stop_reason (struct lwp_info *lwp)
-{
- return lwp->stop_reason;
-}
-
-/* See nat/linux-nat.h. */
-
-int
-lwp_is_stepping (struct lwp_info *lwp)
-{
- return lwp->stepping;
-}
-
-/* A list of all unknown processes which receive stop signals. Some
- other process will presumably claim each of these as forked
- children momentarily. */
-
-struct simple_pid_list
-{
- /* The process ID. */
- int pid;
-
- /* The status as reported by waitpid. */
- int status;
-
- /* Next in chain. */
- struct simple_pid_list *next;
-};
-struct simple_pid_list *stopped_pids;
-
-/* Trivial list manipulation functions to keep track of a list of new
- stopped processes. */
-
-static void
-add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
-{
- struct simple_pid_list *new_pid = XNEW (struct simple_pid_list);
-
- new_pid->pid = pid;
- new_pid->status = status;
- new_pid->next = *listp;
- *listp = new_pid;
-}
-
-static int
-pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
-{
- struct simple_pid_list **p;
-
- for (p = listp; *p != NULL; p = &(*p)->next)
- if ((*p)->pid == pid)
- {
- struct simple_pid_list *next = (*p)->next;
-
- *statusp = (*p)->status;
- xfree (*p);
- *p = next;
- return 1;
- }
- return 0;
-}
-
-enum stopping_threads_kind
- {
- /* Not stopping threads presently. */
- NOT_STOPPING_THREADS,
-
- /* Stopping threads. */
- STOPPING_THREADS,
-
- /* Stopping and suspending threads. */
- STOPPING_AND_SUSPENDING_THREADS
- };
-
-/* This is set while stop_all_lwps is in effect. */
-enum stopping_threads_kind stopping_threads = NOT_STOPPING_THREADS;
-
-/* FIXME make into a target method? */
-int using_threads = 1;
-
-/* True if we're presently stabilizing threads (moving them out of
- jump pads). */
-static int stabilizing_threads;
-
-static void linux_resume_one_lwp (struct lwp_info *lwp,
- int step, int signal, siginfo_t *info);
-static void linux_resume (struct thread_resume *resume_info, size_t n);
-static void stop_all_lwps (int suspend, struct lwp_info *except);
-static void unstop_all_lwps (int unsuspend, struct lwp_info *except);
-static void unsuspend_all_lwps (struct lwp_info *except);
-static int linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
- int *wstat, int options);
-static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
-static struct lwp_info *add_lwp (ptid_t ptid);
-static void linux_mourn (struct process_info *process);
-static int linux_stopped_by_watchpoint (void);
-static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
-static int lwp_is_marked_dead (struct lwp_info *lwp);
-static void proceed_all_lwps (void);
-static int finish_step_over (struct lwp_info *lwp);
-static int kill_lwp (unsigned long lwpid, int signo);
-static void enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info);
-static void complete_ongoing_step_over (void);
-static int linux_low_ptrace_options (int attached);
-static int check_ptrace_stopped_lwp_gone (struct lwp_info *lp);
-static void proceed_one_lwp (thread_info *thread, lwp_info *except);
-
-/* When the event-loop is doing a step-over, this points at the thread
- being stepped. */
-ptid_t step_over_bkpt;
-
-/* True if the low target can hardware single-step. */
-
-static int
-can_hardware_single_step (void)
-{
- if (the_low_target.supports_hardware_single_step != NULL)
- return the_low_target.supports_hardware_single_step ();
- else
- return 0;
-}
-
-/* True if the low target can software single-step. Such targets
- implement the GET_NEXT_PCS callback. */
-
-static int
-can_software_single_step (void)
-{
- return (the_low_target.get_next_pcs != NULL);
-}
-
-/* True if the low target supports memory breakpoints. If so, we'll
- have a GET_PC implementation. */
-
-static int
-supports_breakpoints (void)
-{
- return (the_low_target.get_pc != NULL);
-}
-
-/* Returns true if this target can support fast tracepoints. This
- does not mean that the in-process agent has been loaded in the
- inferior. */
-
-static int
-supports_fast_tracepoints (void)
-{
- return the_low_target.install_fast_tracepoint_jump_pad != NULL;
-}
-
-/* True if LWP is stopped in its stepping range. */
-
-static int
-lwp_in_step_range (struct lwp_info *lwp)
-{
- CORE_ADDR pc = lwp->stop_pc;
-
- return (pc >= lwp->step_range_start && pc < lwp->step_range_end);
-}
-
-struct pending_signals
-{
- int signal;
- siginfo_t info;
- struct pending_signals *prev;
-};
-
-/* The read/write ends of the pipe registered as waitable file in the
- event loop. */
-static int linux_event_pipe[2] = { -1, -1 };
-
-/* True if we're currently in async mode. */
-#define target_is_async_p() (linux_event_pipe[0] != -1)
-
-static void send_sigstop (struct lwp_info *lwp);
-static void wait_for_sigstop (void);
-
-/* Return non-zero if HEADER is a 64-bit ELF file. */
-
-static int
-elf_64_header_p (const Elf64_Ehdr *header, unsigned int *machine)
-{
- if (header->e_ident[EI_MAG0] == ELFMAG0
- && header->e_ident[EI_MAG1] == ELFMAG1
- && header->e_ident[EI_MAG2] == ELFMAG2
- && header->e_ident[EI_MAG3] == ELFMAG3)
- {
- *machine = header->e_machine;
- return header->e_ident[EI_CLASS] == ELFCLASS64;
-
- }
- *machine = EM_NONE;
- return -1;
-}
-
-/* Return non-zero if FILE is a 64-bit ELF file,
- zero if the file is not a 64-bit ELF file,
- and -1 if the file is not accessible or doesn't exist. */
-
-static int
-elf_64_file_p (const char *file, unsigned int *machine)
-{
- Elf64_Ehdr header;
- int fd;
-
- fd = open (file, O_RDONLY);
- if (fd < 0)
- return -1;
-
- if (read (fd, &header, sizeof (header)) != sizeof (header))
- {
- close (fd);
- return 0;
- }
- close (fd);
-
- return elf_64_header_p (&header, machine);
-}
-
-/* Accepts an integer PID; Returns true if the executable PID is
- running is a 64-bit ELF file.. */
-
-int
-linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine)
-{
- char file[PATH_MAX];
-
- sprintf (file, "/proc/%d/exe", pid);
- return elf_64_file_p (file, machine);
-}
-
-static void
-delete_lwp (struct lwp_info *lwp)
-{
- struct thread_info *thr = get_lwp_thread (lwp);
-
- if (debug_threads)
- debug_printf ("deleting %ld\n", lwpid_of (thr));
-
- remove_thread (thr);
-
- if (the_low_target.delete_thread != NULL)
- the_low_target.delete_thread (lwp->arch_private);
- else
- gdb_assert (lwp->arch_private == NULL);
-
- free (lwp);
-}
-
-/* Add a process to the common process list, and set its private
- data. */
-
-static struct process_info *
-linux_add_process (int pid, int attached)
-{
- struct process_info *proc;
-
- proc = add_process (pid, attached);
- proc->priv = XCNEW (struct process_info_private);
-
- if (the_low_target.new_process != NULL)
- proc->priv->arch_private = the_low_target.new_process ();
-
- return proc;
-}
-
-static CORE_ADDR get_pc (struct lwp_info *lwp);
-
-/* Call the target arch_setup function on the current thread. */
-
-static void
-linux_arch_setup (void)
-{
- the_low_target.arch_setup ();
-}
-
-/* Call the target arch_setup function on THREAD. */
-
-static void
-linux_arch_setup_thread (struct thread_info *thread)
-{
- struct thread_info *saved_thread;
-
- saved_thread = current_thread;
- current_thread = thread;
-
- linux_arch_setup ();
-
- current_thread = saved_thread;
-}
-
-/* Handle a GNU/Linux extended wait response. If we see a clone,
- fork, or vfork event, we need to add the new LWP to our list
- (and return 0 so as not to report the trap to higher layers).
- If we see an exec event, we will modify ORIG_EVENT_LWP to point
- to a new LWP representing the new program. */
-
-static int
-handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
-{
- client_state &cs = get_client_state ();
- struct lwp_info *event_lwp = *orig_event_lwp;
- int event = linux_ptrace_get_extended_event (wstat);
- struct thread_info *event_thr = get_lwp_thread (event_lwp);
- struct lwp_info *new_lwp;
-
- gdb_assert (event_lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
-
- /* All extended events we currently use are mid-syscall. Only
- PTRACE_EVENT_STOP is delivered more like a signal-stop, but
- you have to be using PTRACE_SEIZE to get that. */
- event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
-
- if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
- || (event == PTRACE_EVENT_CLONE))
- {
- ptid_t ptid;
- unsigned long new_pid;
- int ret, status;
-
- /* Get the pid of the new lwp. */
- ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
- &new_pid);
-
- /* If we haven't already seen the new PID stop, wait for it now. */
- if (!pull_pid_from_list (&stopped_pids, new_pid, &status))
- {
- /* The new child has a pending SIGSTOP. We can't affect it until it
- hits the SIGSTOP, but we're already attached. */
-
- ret = my_waitpid (new_pid, &status, __WALL);
-
- if (ret == -1)
- perror_with_name ("waiting for new child");
- else if (ret != new_pid)
- warning ("wait returned unexpected PID %d", ret);
- else if (!WIFSTOPPED (status))
- warning ("wait returned unexpected status 0x%x", status);
- }
-
- if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
- {
- struct process_info *parent_proc;
- struct process_info *child_proc;
- struct lwp_info *child_lwp;
- struct thread_info *child_thr;
- struct target_desc *tdesc;
-
- ptid = ptid_t (new_pid, new_pid, 0);
-
- if (debug_threads)
- {
- debug_printf ("HEW: Got fork event from LWP %ld, "
- "new child is %d\n",
- ptid_of (event_thr).lwp (),
- ptid.pid ());
- }
-
- /* Add the new process to the tables and clone the breakpoint
- lists of the parent. We need to do this even if the new process
- will be detached, since we will need the process object and the
- breakpoints to remove any breakpoints from memory when we
- detach, and the client side will access registers. */
- child_proc = linux_add_process (new_pid, 0);
- gdb_assert (child_proc != NULL);
- child_lwp = add_lwp (ptid);
- gdb_assert (child_lwp != NULL);
- child_lwp->stopped = 1;
- child_lwp->must_set_ptrace_flags = 1;
- child_lwp->status_pending_p = 0;
- child_thr = get_lwp_thread (child_lwp);
- child_thr->last_resume_kind = resume_stop;
- child_thr->last_status.kind = TARGET_WAITKIND_STOPPED;
-
- /* If we're suspending all threads, leave this one suspended
- too. If the fork/clone parent is stepping over a breakpoint,
- all other threads have been suspended already. Leave the
- child suspended too. */
- if (stopping_threads == STOPPING_AND_SUSPENDING_THREADS
- || event_lwp->bp_reinsert != 0)
- {
- if (debug_threads)
- debug_printf ("HEW: leaving child suspended\n");
- child_lwp->suspended = 1;
- }
-
- parent_proc = get_thread_process (event_thr);
- child_proc->attached = parent_proc->attached;
-
- if (event_lwp->bp_reinsert != 0
- && can_software_single_step ()
- && event == PTRACE_EVENT_VFORK)
- {
- /* If we leave single-step breakpoints there, child will
- hit it, so uninsert single-step breakpoints from parent
- (and child). Once vfork child is done, reinsert
- them back to parent. */
- uninsert_single_step_breakpoints (event_thr);
- }
-
- clone_all_breakpoints (child_thr, event_thr);
-
- tdesc = allocate_target_description ();
- copy_target_description (tdesc, parent_proc->tdesc);
- child_proc->tdesc = tdesc;
-
- /* Clone arch-specific process data. */
- if (the_low_target.new_fork != NULL)
- the_low_target.new_fork (parent_proc, child_proc);
-
- /* Save fork info in the parent thread. */
- if (event == PTRACE_EVENT_FORK)
- event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
- else if (event == PTRACE_EVENT_VFORK)
- event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
-
- event_lwp->waitstatus.value.related_pid = ptid;
-
- /* The status_pending field contains bits denoting the
- extended event, so when the pending event is handled,
- the handler will look at lwp->waitstatus. */
- event_lwp->status_pending_p = 1;
- event_lwp->status_pending = wstat;
-
- /* Link the threads until the parent event is passed on to
- higher layers. */
- event_lwp->fork_relative = child_lwp;
- child_lwp->fork_relative = event_lwp;
-
- /* If the parent thread is doing step-over with single-step
- breakpoints, the list of single-step breakpoints are cloned
- from the parent's. Remove them from the child process.
- In case of vfork, we'll reinsert them back once vforked
- child is done. */
- if (event_lwp->bp_reinsert != 0
- && can_software_single_step ())
- {
- /* The child process is forked and stopped, so it is safe
- to access its memory without stopping all other threads
- from other processes. */
- delete_single_step_breakpoints (child_thr);
-
- gdb_assert (has_single_step_breakpoints (event_thr));
- gdb_assert (!has_single_step_breakpoints (child_thr));
- }
-
- /* Report the event. */
- return 0;
- }
-
- if (debug_threads)
- debug_printf ("HEW: Got clone event "
- "from LWP %ld, new child is LWP %ld\n",
- lwpid_of (event_thr), new_pid);
-
- ptid = ptid_t (pid_of (event_thr), new_pid, 0);
- new_lwp = add_lwp (ptid);
-
- /* Either we're going to immediately resume the new thread
- or leave it stopped. linux_resume_one_lwp is a nop if it
- thinks the thread is currently running, so set this first
- before calling linux_resume_one_lwp. */
- new_lwp->stopped = 1;
-
- /* If we're suspending all threads, leave this one suspended
- too. If the fork/clone parent is stepping over a breakpoint,
- all other threads have been suspended already. Leave the
- child suspended too. */
- if (stopping_threads == STOPPING_AND_SUSPENDING_THREADS
- || event_lwp->bp_reinsert != 0)
- new_lwp->suspended = 1;
-
- /* Normally we will get the pending SIGSTOP. But in some cases
- we might get another signal delivered to the group first.
- If we do get another signal, be sure not to lose it. */
- if (WSTOPSIG (status) != SIGSTOP)
- {
- new_lwp->stop_expected = 1;
- new_lwp->status_pending_p = 1;
- new_lwp->status_pending = status;
- }
- else if (cs.report_thread_events)
- {
- new_lwp->waitstatus.kind = TARGET_WAITKIND_THREAD_CREATED;
- new_lwp->status_pending_p = 1;
- new_lwp->status_pending = status;
- }
-
-#ifdef USE_THREAD_DB
- thread_db_notice_clone (event_thr, ptid);
-#endif
-
- /* Don't report the event. */
- return 1;
- }
- else if (event == PTRACE_EVENT_VFORK_DONE)
- {
- event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
-
- if (event_lwp->bp_reinsert != 0 && can_software_single_step ())
- {
- reinsert_single_step_breakpoints (event_thr);
-
- gdb_assert (has_single_step_breakpoints (event_thr));
- }
-
- /* Report the event. */
- return 0;
- }
- else if (event == PTRACE_EVENT_EXEC && cs.report_exec_events)
- {
- struct process_info *proc;
- std::vector<int> syscalls_to_catch;
- ptid_t event_ptid;
- pid_t event_pid;
-
- if (debug_threads)
- {
- debug_printf ("HEW: Got exec event from LWP %ld\n",
- lwpid_of (event_thr));
- }
-
- /* Get the event ptid. */
- event_ptid = ptid_of (event_thr);
- event_pid = event_ptid.pid ();
-
- /* Save the syscall list from the execing process. */
- proc = get_thread_process (event_thr);
- syscalls_to_catch = std::move (proc->syscalls_to_catch);
-
- /* Delete the execing process and all its threads. */
- linux_mourn (proc);
- current_thread = NULL;
-
- /* Create a new process/lwp/thread. */
- proc = linux_add_process (event_pid, 0);
- event_lwp = add_lwp (event_ptid);
- event_thr = get_lwp_thread (event_lwp);
- gdb_assert (current_thread == event_thr);
- linux_arch_setup_thread (event_thr);
-
- /* Set the event status. */
- event_lwp->waitstatus.kind = TARGET_WAITKIND_EXECD;
- event_lwp->waitstatus.value.execd_pathname
- = xstrdup (linux_proc_pid_to_exec_file (lwpid_of (event_thr)));
-
- /* Mark the exec status as pending. */
- event_lwp->stopped = 1;
- event_lwp->status_pending_p = 1;
- event_lwp->status_pending = wstat;
- event_thr->last_resume_kind = resume_continue;
- event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
-
- /* Update syscall state in the new lwp, effectively mid-syscall too. */
- event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
-
- /* Restore the list to catch. Don't rely on the client, which is free
- to avoid sending a new list when the architecture doesn't change.
- Also, for ANY_SYSCALL, the architecture doesn't really matter. */
- proc->syscalls_to_catch = std::move (syscalls_to_catch);
-
- /* Report the event. */
- *orig_event_lwp = event_lwp;
- return 0;
- }
-
- internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
-}
-
-/* Return the PC as read from the regcache of LWP, without any
- adjustment. */
-
-static CORE_ADDR
-get_pc (struct lwp_info *lwp)
-{
- struct thread_info *saved_thread;
- struct regcache *regcache;
- CORE_ADDR pc;
-
- if (the_low_target.get_pc == NULL)
- return 0;
-
- saved_thread = current_thread;
- current_thread = get_lwp_thread (lwp);
-
- regcache = get_thread_regcache (current_thread, 1);
- pc = (*the_low_target.get_pc) (regcache);
-
- if (debug_threads)
- debug_printf ("pc is 0x%lx\n", (long) pc);
-
- current_thread = saved_thread;
- return pc;
-}
-
-/* This function should only be called if LWP got a SYSCALL_SIGTRAP.
- Fill *SYSNO with the syscall nr trapped. */
-
-static void
-get_syscall_trapinfo (struct lwp_info *lwp, int *sysno)
-{
- struct thread_info *saved_thread;
- struct regcache *regcache;
-
- if (the_low_target.get_syscall_trapinfo == NULL)
- {
- /* If we cannot get the syscall trapinfo, report an unknown
- system call number. */
- *sysno = UNKNOWN_SYSCALL;
- return;
- }
-
- saved_thread = current_thread;
- current_thread = get_lwp_thread (lwp);
-
- regcache = get_thread_regcache (current_thread, 1);
- (*the_low_target.get_syscall_trapinfo) (regcache, sysno);
-
- if (debug_threads)
- debug_printf ("get_syscall_trapinfo sysno %d\n", *sysno);
-
- current_thread = saved_thread;
-}
-
-static int check_stopped_by_watchpoint (struct lwp_info *child);
-
-/* Called when the LWP stopped for a signal/trap. If it stopped for a
- trap check what caused it (breakpoint, watchpoint, trace, etc.),
- and save the result in the LWP's stop_reason field. If it stopped
- for a breakpoint, decrement the PC if necessary on the lwp's
- architecture. Returns true if we now have the LWP's stop PC. */
-
-static int
-save_stop_reason (struct lwp_info *lwp)
-{
- CORE_ADDR pc;
- CORE_ADDR sw_breakpoint_pc;
- struct thread_info *saved_thread;
-#if USE_SIGTRAP_SIGINFO
- siginfo_t siginfo;
-#endif
-
- if (the_low_target.get_pc == NULL)
- return 0;
-
- pc = get_pc (lwp);
- sw_breakpoint_pc = pc - the_low_target.decr_pc_after_break;
-
- /* breakpoint_at reads from the current thread. */
- saved_thread = current_thread;
- current_thread = get_lwp_thread (lwp);
-
-#if USE_SIGTRAP_SIGINFO
- if (ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
- (PTRACE_TYPE_ARG3) 0, &siginfo) == 0)
- {
- if (siginfo.si_signo == SIGTRAP)
- {
- if (GDB_ARCH_IS_TRAP_BRKPT (siginfo.si_code)
- && GDB_ARCH_IS_TRAP_HWBKPT (siginfo.si_code))
- {
- /* The si_code is ambiguous on this arch -- check debug
- registers. */
- if (!check_stopped_by_watchpoint (lwp))
- lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
- }
- else if (GDB_ARCH_IS_TRAP_BRKPT (siginfo.si_code))
- {
- /* If we determine the LWP stopped for a SW breakpoint,
- trust it. Particularly don't check watchpoint
- registers, because at least on s390, we'd find
- stopped-by-watchpoint as long as there's a watchpoint
- set. */
- lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
- }
- else if (GDB_ARCH_IS_TRAP_HWBKPT (siginfo.si_code))
- {
- /* This can indicate either a hardware breakpoint or
- hardware watchpoint. Check debug registers. */
- if (!check_stopped_by_watchpoint (lwp))
- lwp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
- }
- else if (siginfo.si_code == TRAP_TRACE)
- {
- /* We may have single stepped an instruction that
- triggered a watchpoint. In that case, on some
- architectures (such as x86), instead of TRAP_HWBKPT,
- si_code indicates TRAP_TRACE, and we need to check
- the debug registers separately. */
- if (!check_stopped_by_watchpoint (lwp))
- lwp->stop_reason = TARGET_STOPPED_BY_SINGLE_STEP;
- }
- }
- }
-#else
- /* We may have just stepped a breakpoint instruction. E.g., in
- non-stop mode, GDB first tells the thread A to step a range, and
- then the user inserts a breakpoint inside the range. In that
- case we need to report the breakpoint PC. */
- if ((!lwp->stepping || lwp->stop_pc == sw_breakpoint_pc)
- && (*the_low_target.breakpoint_at) (sw_breakpoint_pc))
- lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
-
- if (hardware_breakpoint_inserted_here (pc))
- lwp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
-
- if (lwp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
- check_stopped_by_watchpoint (lwp);
-#endif
-
- if (lwp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
- {
- if (debug_threads)
- {
- struct thread_info *thr = get_lwp_thread (lwp);
-
- debug_printf ("CSBB: %s stopped by software breakpoint\n",
- target_pid_to_str (ptid_of (thr)));
- }
-
- /* Back up the PC if necessary. */
- if (pc != sw_breakpoint_pc)
- {
- struct regcache *regcache
- = get_thread_regcache (current_thread, 1);
- (*the_low_target.set_pc) (regcache, sw_breakpoint_pc);
- }
-
- /* Update this so we record the correct stop PC below. */
- pc = sw_breakpoint_pc;
- }
- else if (lwp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)
- {
- if (debug_threads)
- {
- struct thread_info *thr = get_lwp_thread (lwp);
-
- debug_printf ("CSBB: %s stopped by hardware breakpoint\n",
- target_pid_to_str (ptid_of (thr)));
- }
- }
- else if (lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT)
- {
- if (debug_threads)
- {
- struct thread_info *thr = get_lwp_thread (lwp);
-
- debug_printf ("CSBB: %s stopped by hardware watchpoint\n",
- target_pid_to_str (ptid_of (thr)));
- }
- }
- else if (lwp->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP)
- {
- if (debug_threads)
- {
- struct thread_info *thr = get_lwp_thread (lwp);
-
- debug_printf ("CSBB: %s stopped by trace\n",
- target_pid_to_str (ptid_of (thr)));
- }
- }
-
- lwp->stop_pc = pc;
- current_thread = saved_thread;
- return 1;
-}
-
-static struct lwp_info *
-add_lwp (ptid_t ptid)
-{
- struct lwp_info *lwp;
-
- lwp = XCNEW (struct lwp_info);
-
- lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
-
- lwp->thread = add_thread (ptid, lwp);
-
- if (the_low_target.new_thread != NULL)
- the_low_target.new_thread (lwp);
-
- return lwp;
-}
-
-/* Callback to be used when calling fork_inferior, responsible for
- actually initiating the tracing of the inferior. */
-
-static void
-linux_ptrace_fun ()
-{
- if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) 0) < 0)
- trace_start_error_with_name ("ptrace");
-
- if (setpgid (0, 0) < 0)
- trace_start_error_with_name ("setpgid");
-
- /* If GDBserver is connected to gdb via stdio, redirect the inferior's
- stdout to stderr so that inferior i/o doesn't corrupt the connection.
- Also, redirect stdin to /dev/null. */
- if (remote_connection_is_stdio ())
- {
- if (close (0) < 0)
- trace_start_error_with_name ("close");
- if (open ("/dev/null", O_RDONLY) < 0)
- trace_start_error_with_name ("open");
- if (dup2 (2, 1) < 0)
- trace_start_error_with_name ("dup2");
- if (write (2, "stdin/stdout redirected\n",
- sizeof ("stdin/stdout redirected\n") - 1) < 0)
- {
- /* Errors ignored. */;
- }
- }
-}
-
-/* Start an inferior process and returns its pid.
- PROGRAM is the name of the program to be started, and PROGRAM_ARGS
- are its arguments. */
-
-static int
-linux_create_inferior (const char *program,
- const std::vector<char *> &program_args)
-{
- client_state &cs = get_client_state ();
- struct lwp_info *new_lwp;
- int pid;
- ptid_t ptid;
-
- {
- maybe_disable_address_space_randomization restore_personality
- (cs.disable_randomization);
- std::string str_program_args = stringify_argv (program_args);
-
- pid = fork_inferior (program,
- str_program_args.c_str (),
- get_environ ()->envp (), linux_ptrace_fun,
- NULL, NULL, NULL, NULL);
- }
-
- linux_add_process (pid, 0);
-
- ptid = ptid_t (pid, pid, 0);
- new_lwp = add_lwp (ptid);
- new_lwp->must_set_ptrace_flags = 1;
-
- post_fork_inferior (pid, program);
-
- return pid;
-}
-
-/* Implement the post_create_inferior target_ops method. */
-
-static void
-linux_post_create_inferior (void)
-{
- struct lwp_info *lwp = get_thread_lwp (current_thread);
-
- linux_arch_setup ();
-
- if (lwp->must_set_ptrace_flags)
- {
- struct process_info *proc = current_process ();
- int options = linux_low_ptrace_options (proc->attached);
-
- linux_enable_event_reporting (lwpid_of (current_thread), options);
- lwp->must_set_ptrace_flags = 0;
- }
-}
-
-/* Attach to an inferior process. Returns 0 on success, ERRNO on
- error. */
-
-int
-linux_attach_lwp (ptid_t ptid)
-{
- struct lwp_info *new_lwp;
- int lwpid = ptid.lwp ();
-
- if (ptrace (PTRACE_ATTACH, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0)
- != 0)
- return errno;
-
- new_lwp = add_lwp (ptid);
-
- /* We need to wait for SIGSTOP before being able to make the next
- ptrace call on this LWP. */
- new_lwp->must_set_ptrace_flags = 1;
-
- if (linux_proc_pid_is_stopped (lwpid))
- {
- if (debug_threads)
- debug_printf ("Attached to a stopped process\n");
-
- /* The process is definitely stopped. It is in a job control
- stop, unless the kernel predates the TASK_STOPPED /
- TASK_TRACED distinction, in which case it might be in a
- ptrace stop. Make sure it is in a ptrace stop; from there we
- can kill it, signal it, et cetera.
-
- First make sure there is a pending SIGSTOP. Since we are
- already attached, the process can not transition from stopped
- to running without a PTRACE_CONT; so we know this signal will
- go into the queue. The SIGSTOP generated by PTRACE_ATTACH is
- probably already in the queue (unless this kernel is old
- enough to use TASK_STOPPED for ptrace stops); but since
- SIGSTOP is not an RT signal, it can only be queued once. */
- kill_lwp (lwpid, SIGSTOP);
-
- /* Finally, resume the stopped process. This will deliver the
- SIGSTOP (or a higher priority signal, just like normal
- PTRACE_ATTACH), which we'll catch later on. */
- ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
- }
-
- /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
- brings it to a halt.
-
- There are several cases to consider here:
-
- 1) gdbserver has already attached to the process and is being notified
- of a new thread that is being created.
- In this case we should ignore that SIGSTOP and resume the
- process. This is handled below by setting stop_expected = 1,
- and the fact that add_thread sets last_resume_kind ==
- resume_continue.
-
- 2) This is the first thread (the process thread), and we're attaching
- to it via attach_inferior.
- In this case we want the process thread to stop.
- This is handled by having linux_attach set last_resume_kind ==
- resume_stop after we return.
-
- If the pid we are attaching to is also the tgid, we attach to and
- stop all the existing threads. Otherwise, we attach to pid and
- ignore any other threads in the same group as this pid.
-
- 3) GDB is connecting to gdbserver and is requesting an enumeration of all
- existing threads.
- In this case we want the thread to stop.
- FIXME: This case is currently not properly handled.
- We should wait for the SIGSTOP but don't. Things work apparently
- because enough time passes between when we ptrace (ATTACH) and when
- gdb makes the next ptrace call on the thread.
-
- On the other hand, if we are currently trying to stop all threads, we
- should treat the new thread as if we had sent it a SIGSTOP. This works
- because we are guaranteed that the add_lwp call above added us to the
- end of the list, and so the new thread has not yet reached
- wait_for_sigstop (but will). */
- new_lwp->stop_expected = 1;
-
- return 0;
-}
-
-/* Callback for linux_proc_attach_tgid_threads. Attach to PTID if not
- already attached. Returns true if a new LWP is found, false
- otherwise. */
-
-static int
-attach_proc_task_lwp_callback (ptid_t ptid)
-{
- /* Is this a new thread? */
- if (find_thread_ptid (ptid) == NULL)
- {
- int lwpid = ptid.lwp ();
- int err;
-
- if (debug_threads)
- debug_printf ("Found new lwp %d\n", lwpid);
-
- err = linux_attach_lwp (ptid);
-
- /* Be quiet if we simply raced with the thread exiting. EPERM
- is returned if the thread's task still exists, and is marked
- as exited or zombie, as well as other conditions, so in that
- case, confirm the status in /proc/PID/status. */
- if (err == ESRCH
- || (err == EPERM && linux_proc_pid_is_gone (lwpid)))
- {
- if (debug_threads)
- {
- debug_printf ("Cannot attach to lwp %d: "
- "thread is gone (%d: %s)\n",
- lwpid, err, safe_strerror (err));
- }
- }
- else if (err != 0)
- {
- std::string reason
- = linux_ptrace_attach_fail_reason_string (ptid, err);
-
- warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
- }
-
- return 1;
- }
- return 0;
-}
-
-static void async_file_mark (void);
-
-/* Attach to PID. If PID is the tgid, attach to it and all
- of its threads. */
-
-static int
-linux_attach (unsigned long pid)
-{
- struct process_info *proc;
- struct thread_info *initial_thread;
- ptid_t ptid = ptid_t (pid, pid, 0);
- int err;
-
- proc = linux_add_process (pid, 1);
-
- /* Attach to PID. We will check for other threads
- soon. */
- err = linux_attach_lwp (ptid);
- if (err != 0)
- {
- remove_process (proc);
-
- std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
- error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
- }
-
- /* Don't ignore the initial SIGSTOP if we just attached to this
- process. It will be collected by wait shortly. */
- initial_thread = find_thread_ptid (ptid_t (pid, pid, 0));
- initial_thread->last_resume_kind = resume_stop;
-
- /* We must attach to every LWP. If /proc is mounted, use that to
- find them now. On the one hand, the inferior may be using raw
- clone instead of using pthreads. On the other hand, even if it
- is using pthreads, GDB may not be connected yet (thread_db needs
- to do symbol lookups, through qSymbol). Also, thread_db walks
- structures in the inferior's address space to find the list of
- threads/LWPs, and those structures may well be corrupted. Note
- that once thread_db is loaded, we'll still use it to list threads
- and associate pthread info with each LWP. */
- linux_proc_attach_tgid_threads (pid, attach_proc_task_lwp_callback);
-
- /* GDB will shortly read the xml target description for this
- process, to figure out the process' architecture. But the target
- description is only filled in when the first process/thread in
- the thread group reports its initial PTRACE_ATTACH SIGSTOP. Do
- that now, otherwise, if GDB is fast enough, it could read the
- target description _before_ that initial stop. */
- if (non_stop)
- {
- struct lwp_info *lwp;
- int wstat, lwpid;
- ptid_t pid_ptid = ptid_t (pid);
-
- lwpid = linux_wait_for_event_filtered (pid_ptid, pid_ptid,
- &wstat, __WALL);
- gdb_assert (lwpid > 0);
-
- lwp = find_lwp_pid (ptid_t (lwpid));
-
- if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGSTOP)
- {
- lwp->status_pending_p = 1;
- lwp->status_pending = wstat;
- }
-
- initial_thread->last_resume_kind = resume_continue;
-
- async_file_mark ();
-
- gdb_assert (proc->tdesc != NULL);
- }
-
- return 0;
-}
-
-static int
-last_thread_of_process_p (int pid)
-{
- bool seen_one = false;
-
- thread_info *thread = find_thread (pid, [&] (thread_info *thr_arg)
- {
- if (!seen_one)
- {
- /* This is the first thread of this process we see. */
- seen_one = true;
- return false;
- }
- else
- {
- /* This is the second thread of this process we see. */
- return true;
- }
- });
-
- return thread == NULL;
-}
-
-/* Kill LWP. */
-
-static void
-linux_kill_one_lwp (struct lwp_info *lwp)
-{
- struct thread_info *thr = get_lwp_thread (lwp);
- int pid = lwpid_of (thr);
-
- /* PTRACE_KILL is unreliable. After stepping into a signal handler,
- there is no signal context, and ptrace(PTRACE_KILL) (or
- ptrace(PTRACE_CONT, SIGKILL), pretty much the same) acts like
- ptrace(CONT, pid, 0,0) and just resumes the tracee. A better
- alternative is to kill with SIGKILL. We only need one SIGKILL
- per process, not one for each thread. But since we still support
- support debugging programs using raw clone without CLONE_THREAD,
- we send one for each thread. For years, we used PTRACE_KILL
- only, so we're being a bit paranoid about some old kernels where
- PTRACE_KILL might work better (dubious if there are any such, but
- that's why it's paranoia), so we try SIGKILL first, PTRACE_KILL
- second, and so we're fine everywhere. */
-
- errno = 0;
- kill_lwp (pid, SIGKILL);
- if (debug_threads)
- {
- int save_errno = errno;
-
- debug_printf ("LKL: kill_lwp (SIGKILL) %s, 0, 0 (%s)\n",
- target_pid_to_str (ptid_of (thr)),
- save_errno ? safe_strerror (save_errno) : "OK");
- }
-
- errno = 0;
- ptrace (PTRACE_KILL, pid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
- if (debug_threads)
- {
- int save_errno = errno;
-
- debug_printf ("LKL: PTRACE_KILL %s, 0, 0 (%s)\n",
- target_pid_to_str (ptid_of (thr)),
- save_errno ? safe_strerror (save_errno) : "OK");
- }
-}
-
-/* Kill LWP and wait for it to die. */
-
-static void
-kill_wait_lwp (struct lwp_info *lwp)
-{
- struct thread_info *thr = get_lwp_thread (lwp);
- int pid = ptid_of (thr).pid ();
- int lwpid = ptid_of (thr).lwp ();
- int wstat;
- int res;
-
- if (debug_threads)
- debug_printf ("kwl: killing lwp %d, for pid: %d\n", lwpid, pid);
-
- do
- {
- linux_kill_one_lwp (lwp);
-
- /* Make sure it died. Notes:
-
- - The loop is most likely unnecessary.
-
- - We don't use linux_wait_for_event as that could delete lwps
- while we're iterating over them. We're not interested in
- any pending status at this point, only in making sure all
- wait status on the kernel side are collected until the
- process is reaped.
-
- - We don't use __WALL here as the __WALL emulation relies on
- SIGCHLD, and killing a stopped process doesn't generate
- one, nor an exit status.
- */
- res = my_waitpid (lwpid, &wstat, 0);
- if (res == -1 && errno == ECHILD)
- res = my_waitpid (lwpid, &wstat, __WCLONE);
- } while (res > 0 && WIFSTOPPED (wstat));
-
- /* Even if it was stopped, the child may have already disappeared.
- E.g., if it was killed by SIGKILL. */
- if (res < 0 && errno != ECHILD)
- perror_with_name ("kill_wait_lwp");
-}
-
-/* Callback for `for_each_thread'. Kills an lwp of a given process,
- except the leader. */
-
-static void
-kill_one_lwp_callback (thread_info *thread, int pid)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- /* We avoid killing the first thread here, because of a Linux kernel (at
- least 2.6.0-test7 through 2.6.8-rc4) bug; if we kill the parent before
- the children get a chance to be reaped, it will remain a zombie
- forever. */
-
- if (lwpid_of (thread) == pid)
- {
- if (debug_threads)
- debug_printf ("lkop: is last of process %s\n",
- target_pid_to_str (thread->id));
- return;
- }
-
- kill_wait_lwp (lwp);
-}
-
-static int
-linux_kill (process_info *process)
-{
- int pid = process->pid;
-
- /* If we're killing a running inferior, make sure it is stopped
- first, as PTRACE_KILL will not work otherwise. */
- stop_all_lwps (0, NULL);
-
- for_each_thread (pid, [&] (thread_info *thread)
- {
- kill_one_lwp_callback (thread, pid);
- });
-
- /* See the comment in linux_kill_one_lwp. We did not kill the first
- thread in the list, so do so now. */
- lwp_info *lwp = find_lwp_pid (ptid_t (pid));
-
- if (lwp == NULL)
- {
- if (debug_threads)
- debug_printf ("lk_1: cannot find lwp for pid: %d\n",
- pid);
- }
- else
- kill_wait_lwp (lwp);
-
- the_target->mourn (process);
-
- /* Since we presently can only stop all lwps of all processes, we
- need to unstop lwps of other processes. */
- unstop_all_lwps (0, NULL);
- return 0;
-}
-
-/* Get pending signal of THREAD, for detaching purposes. This is the
- signal the thread last stopped for, which we need to deliver to the
- thread when detaching, otherwise, it'd be suppressed/lost. */
-
-static int
-get_detach_signal (struct thread_info *thread)
-{
- client_state &cs = get_client_state ();
- enum gdb_signal signo = GDB_SIGNAL_0;
- int status;
- struct lwp_info *lp = get_thread_lwp (thread);
-
- if (lp->status_pending_p)
- status = lp->status_pending;
- else
- {
- /* If the thread had been suspended by gdbserver, and it stopped
- cleanly, then it'll have stopped with SIGSTOP. But we don't
- want to deliver that SIGSTOP. */
- if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
- || thread->last_status.value.sig == GDB_SIGNAL_0)
- return 0;
-
- /* Otherwise, we may need to deliver the signal we
- intercepted. */
- status = lp->last_status;
- }
-
- if (!WIFSTOPPED (status))
- {
- if (debug_threads)
- debug_printf ("GPS: lwp %s hasn't stopped: no pending signal\n",
- target_pid_to_str (ptid_of (thread)));
- return 0;
- }
-
- /* Extended wait statuses aren't real SIGTRAPs. */
- if (WSTOPSIG (status) == SIGTRAP && linux_is_extended_waitstatus (status))
- {
- if (debug_threads)
- debug_printf ("GPS: lwp %s had stopped with extended "
- "status: no pending signal\n",
- target_pid_to_str (ptid_of (thread)));
- return 0;
- }
-
- signo = gdb_signal_from_host (WSTOPSIG (status));
-
- if (cs.program_signals_p && !cs.program_signals[signo])
- {
- if (debug_threads)
- debug_printf ("GPS: lwp %s had signal %s, but it is in nopass state\n",
- target_pid_to_str (ptid_of (thread)),
- gdb_signal_to_string (signo));
- return 0;
- }
- else if (!cs.program_signals_p
- /* If we have no way to know which signals GDB does not
- want to have passed to the program, assume
- SIGTRAP/SIGINT, which is GDB's default. */
- && (signo == GDB_SIGNAL_TRAP || signo == GDB_SIGNAL_INT))
- {
- if (debug_threads)
- debug_printf ("GPS: lwp %s had signal %s, "
- "but we don't know if we should pass it. "
- "Default to not.\n",
- target_pid_to_str (ptid_of (thread)),
- gdb_signal_to_string (signo));
- return 0;
- }
- else
- {
- if (debug_threads)
- debug_printf ("GPS: lwp %s has pending signal %s: delivering it.\n",
- target_pid_to_str (ptid_of (thread)),
- gdb_signal_to_string (signo));
-
- return WSTOPSIG (status);
- }
-}
-
-/* Detach from LWP. */
-
-static void
-linux_detach_one_lwp (struct lwp_info *lwp)
-{
- struct thread_info *thread = get_lwp_thread (lwp);
- int sig;
- int lwpid;
-
- /* If there is a pending SIGSTOP, get rid of it. */
- if (lwp->stop_expected)
- {
- if (debug_threads)
- debug_printf ("Sending SIGCONT to %s\n",
- target_pid_to_str (ptid_of (thread)));
-
- kill_lwp (lwpid_of (thread), SIGCONT);
- lwp->stop_expected = 0;
- }
-
- /* Pass on any pending signal for this thread. */
- sig = get_detach_signal (thread);
-
- /* Preparing to resume may try to write registers, and fail if the
- lwp is zombie. If that happens, ignore the error. We'll handle
- it below, when detach fails with ESRCH. */
- try
- {
- /* Flush any pending changes to the process's registers. */
- regcache_invalidate_thread (thread);
-
- /* Finally, let it resume. */
- if (the_low_target.prepare_to_resume != NULL)
- the_low_target.prepare_to_resume (lwp);
- }
- catch (const gdb_exception_error &ex)
- {
- if (!check_ptrace_stopped_lwp_gone (lwp))
- throw;
- }
-
- lwpid = lwpid_of (thread);
- if (ptrace (PTRACE_DETACH, lwpid, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) (long) sig) < 0)
- {
- int save_errno = errno;
-
- /* We know the thread exists, so ESRCH must mean the lwp is
- zombie. This can happen if one of the already-detached
- threads exits the whole thread group. In that case we're
- still attached, and must reap the lwp. */
- if (save_errno == ESRCH)
- {
- int ret, status;
-
- ret = my_waitpid (lwpid, &status, __WALL);
- if (ret == -1)
- {
- warning (_("Couldn't reap LWP %d while detaching: %s"),
- lwpid, safe_strerror (errno));
- }
- else if (!WIFEXITED (status) && !WIFSIGNALED (status))
- {
- warning (_("Reaping LWP %d while detaching "
- "returned unexpected status 0x%x"),
- lwpid, status);
- }
- }
- else
- {
- error (_("Can't detach %s: %s"),
- target_pid_to_str (ptid_of (thread)),
- safe_strerror (save_errno));
- }
- }
- else if (debug_threads)
- {
- debug_printf ("PTRACE_DETACH (%s, %s, 0) (OK)\n",
- target_pid_to_str (ptid_of (thread)),
- strsignal (sig));
- }
-
- delete_lwp (lwp);
-}
-
-/* Callback for for_each_thread. Detaches from non-leader threads of a
- given process. */
-
-static void
-linux_detach_lwp_callback (thread_info *thread)
-{
- /* We don't actually detach from the thread group leader just yet.
- If the thread group exits, we must reap the zombie clone lwps
- before we're able to reap the leader. */
- if (thread->id.pid () == thread->id.lwp ())
- return;
-
- lwp_info *lwp = get_thread_lwp (thread);
- linux_detach_one_lwp (lwp);
-}
-
-static int
-linux_detach (process_info *process)
-{
- struct lwp_info *main_lwp;
-
- /* As there's a step over already in progress, let it finish first,
- otherwise nesting a stabilize_threads operation on top gets real
- messy. */
- complete_ongoing_step_over ();
-
- /* Stop all threads before detaching. First, ptrace requires that
- the thread is stopped to successfully detach. Second, thread_db
- may need to uninstall thread event breakpoints from memory, which
- only works with a stopped process anyway. */
- stop_all_lwps (0, NULL);
-
-#ifdef USE_THREAD_DB
- thread_db_detach (process);
-#endif
-
- /* Stabilize threads (move out of jump pads). */
- stabilize_threads ();
-
- /* Detach from the clone lwps first. If the thread group exits just
- while we're detaching, we must reap the clone lwps before we're
- able to reap the leader. */
- for_each_thread (process->pid, linux_detach_lwp_callback);
-
- main_lwp = find_lwp_pid (ptid_t (process->pid));
- linux_detach_one_lwp (main_lwp);
-
- the_target->mourn (process);
-
- /* Since we presently can only stop all lwps of all processes, we
- need to unstop lwps of other processes. */
- unstop_all_lwps (0, NULL);
- return 0;
-}
-
-/* Remove all LWPs that belong to process PROC from the lwp list. */
-
-static void
-linux_mourn (struct process_info *process)
-{
- struct process_info_private *priv;
-
-#ifdef USE_THREAD_DB
- thread_db_mourn (process);
-#endif
-
- for_each_thread (process->pid, [] (thread_info *thread)
- {
- delete_lwp (get_thread_lwp (thread));
- });
-
- /* Freeing all private data. */
- priv = process->priv;
- if (the_low_target.delete_process != NULL)
- the_low_target.delete_process (priv->arch_private);
- else
- gdb_assert (priv->arch_private == NULL);
- free (priv);
- process->priv = NULL;
-
- remove_process (process);
-}
-
-static void
-linux_join (int pid)
-{
- int status, ret;
-
- do {
- ret = my_waitpid (pid, &status, 0);
- if (WIFEXITED (status) || WIFSIGNALED (status))
- break;
- } while (ret != -1 || errno != ECHILD);
-}
-
-/* Return nonzero if the given thread is still alive. */
-static int
-linux_thread_alive (ptid_t ptid)
-{
- struct lwp_info *lwp = find_lwp_pid (ptid);
-
- /* We assume we always know if a thread exits. If a whole process
- exited but we still haven't been able to report it to GDB, we'll
- hold on to the last lwp of the dead process. */
- if (lwp != NULL)
- return !lwp_is_marked_dead (lwp);
- else
- return 0;
-}
-
-/* Return 1 if this lwp still has an interesting status pending. If
- not (e.g., it had stopped for a breakpoint that is gone), return
- false. */
-
-static int
-thread_still_has_status_pending_p (struct thread_info *thread)
-{
- struct lwp_info *lp = get_thread_lwp (thread);
-
- if (!lp->status_pending_p)
- return 0;
-
- if (thread->last_resume_kind != resume_stop
- && (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
- || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT))
- {
- struct thread_info *saved_thread;
- CORE_ADDR pc;
- int discard = 0;
-
- gdb_assert (lp->last_status != 0);
-
- pc = get_pc (lp);
-
- saved_thread = current_thread;
- current_thread = thread;
-
- if (pc != lp->stop_pc)
- {
- if (debug_threads)
- debug_printf ("PC of %ld changed\n",
- lwpid_of (thread));
- discard = 1;
- }
-
-#if !USE_SIGTRAP_SIGINFO
- else if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
- && !(*the_low_target.breakpoint_at) (pc))
- {
- if (debug_threads)
- debug_printf ("previous SW breakpoint of %ld gone\n",
- lwpid_of (thread));
- discard = 1;
- }
- else if (lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT
- && !hardware_breakpoint_inserted_here (pc))
- {
- if (debug_threads)
- debug_printf ("previous HW breakpoint of %ld gone\n",
- lwpid_of (thread));
- discard = 1;
- }
-#endif
-
- current_thread = saved_thread;
-
- if (discard)
- {
- if (debug_threads)
- debug_printf ("discarding pending breakpoint status\n");
- lp->status_pending_p = 0;
- return 0;
- }
- }
-
- return 1;
-}
-
-/* Returns true if LWP is resumed from the client's perspective. */
-
-static int
-lwp_resumed (struct lwp_info *lwp)
-{
- struct thread_info *thread = get_lwp_thread (lwp);
-
- if (thread->last_resume_kind != resume_stop)
- return 1;
-
- /* Did gdb send us a `vCont;t', but we haven't reported the
- corresponding stop to gdb yet? If so, the thread is still
- resumed/running from gdb's perspective. */
- if (thread->last_resume_kind == resume_stop
- && thread->last_status.kind == TARGET_WAITKIND_IGNORE)
- return 1;
-
- return 0;
-}
-
-/* Return true if this lwp has an interesting status pending. */
-static bool
-status_pending_p_callback (thread_info *thread, ptid_t ptid)
-{
- struct lwp_info *lp = get_thread_lwp (thread);
-
- /* Check if we're only interested in events from a specific process
- or a specific LWP. */
- if (!thread->id.matches (ptid))
- return 0;
-
- if (!lwp_resumed (lp))
- return 0;
-
- if (lp->status_pending_p
- && !thread_still_has_status_pending_p (thread))
- {
- linux_resume_one_lwp (lp, lp->stepping, GDB_SIGNAL_0, NULL);
- return 0;
- }
-
- return lp->status_pending_p;
-}
-
-struct lwp_info *
-find_lwp_pid (ptid_t ptid)
-{
- thread_info *thread = find_thread ([&] (thread_info *thr_arg)
- {
- int lwp = ptid.lwp () != 0 ? ptid.lwp () : ptid.pid ();
- return thr_arg->id.lwp () == lwp;
- });
-
- if (thread == NULL)
- return NULL;
-
- return get_thread_lwp (thread);
-}
-
-/* Return the number of known LWPs in the tgid given by PID. */
-
-static int
-num_lwps (int pid)
-{
- int count = 0;
-
- for_each_thread (pid, [&] (thread_info *thread)
- {
- count++;
- });
-
- return count;
-}
-
-/* See nat/linux-nat.h. */
-
-struct lwp_info *
-iterate_over_lwps (ptid_t filter,
- gdb::function_view<iterate_over_lwps_ftype> callback)
-{
- thread_info *thread = find_thread (filter, [&] (thread_info *thr_arg)
- {
- lwp_info *lwp = get_thread_lwp (thr_arg);
-
- return callback (lwp);
- });
-
- if (thread == NULL)
- return NULL;
-
- return get_thread_lwp (thread);
-}
-
-/* Detect zombie thread group leaders, and "exit" them. We can't reap
- their exits until all other threads in the group have exited. */
-
-static void
-check_zombie_leaders (void)
-{
- for_each_process ([] (process_info *proc) {
- pid_t leader_pid = pid_of (proc);
- struct lwp_info *leader_lp;
-
- leader_lp = find_lwp_pid (ptid_t (leader_pid));
-
- if (debug_threads)
- debug_printf ("leader_pid=%d, leader_lp!=NULL=%d, "
- "num_lwps=%d, zombie=%d\n",
- leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
- linux_proc_pid_is_zombie (leader_pid));
-
- if (leader_lp != NULL && !leader_lp->stopped
- /* Check if there are other threads in the group, as we may
- have raced with the inferior simply exiting. */
- && !last_thread_of_process_p (leader_pid)
- && linux_proc_pid_is_zombie (leader_pid))
- {
- /* A leader zombie can mean one of two things:
-
- - It exited, and there's an exit status pending
- available, or only the leader exited (not the whole
- program). In the latter case, we can't waitpid the
- leader's exit status until all other threads are gone.
-
- - There are 3 or more threads in the group, and a thread
- other than the leader exec'd. On an exec, the Linux
- kernel destroys all other threads (except the execing
- one) in the thread group, and resets the execing thread's
- tid to the tgid. No exit notification is sent for the
- execing thread -- from the ptracer's perspective, it
- appears as though the execing thread just vanishes.
- Until we reap all other threads except the leader and the
- execing thread, the leader will be zombie, and the
- execing thread will be in `D (disc sleep)'. As soon as
- all other threads are reaped, the execing thread changes
- it's tid to the tgid, and the previous (zombie) leader
- vanishes, giving place to the "new" leader. We could try
- distinguishing the exit and exec cases, by waiting once
- more, and seeing if something comes out, but it doesn't
- sound useful. The previous leader _does_ go away, and
- we'll re-add the new one once we see the exec event
- (which is just the same as what would happen if the
- previous leader did exit voluntarily before some other
- thread execs). */
-
- if (debug_threads)
- debug_printf ("CZL: Thread group leader %d zombie "
- "(it exited, or another thread execd).\n",
- leader_pid);
-
- delete_lwp (leader_lp);
- }
- });
-}
-
-/* Callback for `find_thread'. Returns the first LWP that is not
- stopped. */
-
-static bool
-not_stopped_callback (thread_info *thread, ptid_t filter)
-{
- if (!thread->id.matches (filter))
- return false;
-
- lwp_info *lwp = get_thread_lwp (thread);
-
- return !lwp->stopped;
-}
-
-/* Increment LWP's suspend count. */
-
-static void
-lwp_suspended_inc (struct lwp_info *lwp)
-{
- lwp->suspended++;
-
- if (debug_threads && lwp->suspended > 4)
- {
- struct thread_info *thread = get_lwp_thread (lwp);
-
- debug_printf ("LWP %ld has a suspiciously high suspend count,"
- " suspended=%d\n", lwpid_of (thread), lwp->suspended);
- }
-}
-
-/* Decrement LWP's suspend count. */
-
-static void
-lwp_suspended_decr (struct lwp_info *lwp)
-{
- lwp->suspended--;
-
- if (lwp->suspended < 0)
- {
- struct thread_info *thread = get_lwp_thread (lwp);
-
- internal_error (__FILE__, __LINE__,
- "unsuspend LWP %ld, suspended=%d\n", lwpid_of (thread),
- lwp->suspended);
- }
-}
-
-/* This function should only be called if the LWP got a SIGTRAP.
-
- Handle any tracepoint steps or hits. Return true if a tracepoint
- event was handled, 0 otherwise. */
-
-static int
-handle_tracepoints (struct lwp_info *lwp)
-{
- struct thread_info *tinfo = get_lwp_thread (lwp);
- int tpoint_related_event = 0;
-
- gdb_assert (lwp->suspended == 0);
-
- /* If this tracepoint hit causes a tracing stop, we'll immediately
- uninsert tracepoints. To do this, we temporarily pause all
- threads, unpatch away, and then unpause threads. We need to make
- sure the unpausing doesn't resume LWP too. */
- lwp_suspended_inc (lwp);
-
- /* And we need to be sure that any all-threads-stopping doesn't try
- to move threads out of the jump pads, as it could deadlock the
- inferior (LWP could be in the jump pad, maybe even holding the
- lock.) */
-
- /* Do any necessary step collect actions. */
- tpoint_related_event |= tracepoint_finished_step (tinfo, lwp->stop_pc);
-
- tpoint_related_event |= handle_tracepoint_bkpts (tinfo, lwp->stop_pc);
-
- /* See if we just hit a tracepoint and do its main collect
- actions. */
- tpoint_related_event |= tracepoint_was_hit (tinfo, lwp->stop_pc);
-
- lwp_suspended_decr (lwp);
-
- gdb_assert (lwp->suspended == 0);
- gdb_assert (!stabilizing_threads
- || (lwp->collecting_fast_tracepoint
- != fast_tpoint_collect_result::not_collecting));
-
- if (tpoint_related_event)
- {
- if (debug_threads)
- debug_printf ("got a tracepoint event\n");
- return 1;
- }
-
- return 0;
-}
-
-/* Convenience wrapper. Returns information about LWP's fast tracepoint
- collection status. */
-
-static fast_tpoint_collect_result
-linux_fast_tracepoint_collecting (struct lwp_info *lwp,
- struct fast_tpoint_collect_status *status)
-{
- CORE_ADDR thread_area;
- struct thread_info *thread = get_lwp_thread (lwp);
-
- if (the_low_target.get_thread_area == NULL)
- return fast_tpoint_collect_result::not_collecting;
-
- /* 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. */
- if ((*the_low_target.get_thread_area) (lwpid_of (thread), &thread_area) == -1)
- return fast_tpoint_collect_result::not_collecting;
-
- return fast_tracepoint_collecting (thread_area, lwp->stop_pc, status);
-}
-
-/* The reason we resume in the caller, is because we want to be able
- to pass lwp->status_pending as WSTAT, and we need to clear
- status_pending_p before resuming, otherwise, linux_resume_one_lwp
- refuses to resume. */
-
-static int
-maybe_move_out_of_jump_pad (struct lwp_info *lwp, int *wstat)
-{
- struct thread_info *saved_thread;
-
- saved_thread = current_thread;
- current_thread = get_lwp_thread (lwp);
-
- if ((wstat == NULL
- || (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) != SIGTRAP))
- && supports_fast_tracepoints ()
- && agent_loaded_p ())
- {
- struct fast_tpoint_collect_status status;
-
- if (debug_threads)
- debug_printf ("Checking whether LWP %ld needs to move out of the "
- "jump pad.\n",
- lwpid_of (current_thread));
-
- fast_tpoint_collect_result r
- = linux_fast_tracepoint_collecting (lwp, &status);
-
- if (wstat == NULL
- || (WSTOPSIG (*wstat) != SIGILL
- && WSTOPSIG (*wstat) != SIGFPE
- && WSTOPSIG (*wstat) != SIGSEGV
- && WSTOPSIG (*wstat) != SIGBUS))
- {
- lwp->collecting_fast_tracepoint = r;
-
- if (r != fast_tpoint_collect_result::not_collecting)
- {
- if (r == fast_tpoint_collect_result::before_insn
- && lwp->exit_jump_pad_bkpt == NULL)
- {
- /* Haven't executed the original instruction yet.
- Set breakpoint there, and wait till it's hit,
- then single-step until exiting the jump pad. */
- lwp->exit_jump_pad_bkpt
- = set_breakpoint_at (status.adjusted_insn_addr, NULL);
- }
-
- if (debug_threads)
- debug_printf ("Checking whether LWP %ld needs to move out of "
- "the jump pad...it does\n",
- lwpid_of (current_thread));
- current_thread = saved_thread;
-
- return 1;
- }
- }
- else
- {
- /* If we get a synchronous signal while collecting, *and*
- while executing the (relocated) original instruction,
- reset the PC to point at the tpoint address, before
- reporting to GDB. Otherwise, it's an IPA lib bug: just
- report the signal to GDB, and pray for the best. */
-
- lwp->collecting_fast_tracepoint
- = fast_tpoint_collect_result::not_collecting;
-
- if (r != fast_tpoint_collect_result::not_collecting
- && (status.adjusted_insn_addr <= lwp->stop_pc
- && lwp->stop_pc < status.adjusted_insn_addr_end))
- {
- siginfo_t info;
- struct regcache *regcache;
-
- /* The si_addr on a few signals references the address
- of the faulting instruction. Adjust that as
- well. */
- if ((WSTOPSIG (*wstat) == SIGILL
- || WSTOPSIG (*wstat) == SIGFPE
- || WSTOPSIG (*wstat) == SIGBUS
- || WSTOPSIG (*wstat) == SIGSEGV)
- && ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
- (PTRACE_TYPE_ARG3) 0, &info) == 0
- /* Final check just to make sure we don't clobber
- the siginfo of non-kernel-sent signals. */
- && (uintptr_t) info.si_addr == lwp->stop_pc)
- {
- info.si_addr = (void *) (uintptr_t) status.tpoint_addr;
- ptrace (PTRACE_SETSIGINFO, lwpid_of (current_thread),
- (PTRACE_TYPE_ARG3) 0, &info);
- }
-
- regcache = get_thread_regcache (current_thread, 1);
- (*the_low_target.set_pc) (regcache, status.tpoint_addr);
- lwp->stop_pc = status.tpoint_addr;
-
- /* Cancel any fast tracepoint lock this thread was
- holding. */
- force_unlock_trace_buffer ();
- }
-
- if (lwp->exit_jump_pad_bkpt != NULL)
- {
- if (debug_threads)
- debug_printf ("Cancelling fast exit-jump-pad: removing bkpt. "
- "stopping all threads momentarily.\n");
-
- stop_all_lwps (1, lwp);
-
- delete_breakpoint (lwp->exit_jump_pad_bkpt);
- lwp->exit_jump_pad_bkpt = NULL;
-
- unstop_all_lwps (1, lwp);
-
- gdb_assert (lwp->suspended >= 0);
- }
- }
- }
-
- if (debug_threads)
- debug_printf ("Checking whether LWP %ld needs to move out of the "
- "jump pad...no\n",
- lwpid_of (current_thread));
-
- current_thread = saved_thread;
- return 0;
-}
-
-/* Enqueue one signal in the "signals to report later when out of the
- jump pad" list. */
-
-static void
-enqueue_one_deferred_signal (struct lwp_info *lwp, int *wstat)
-{
- struct pending_signals *p_sig;
- struct thread_info *thread = get_lwp_thread (lwp);
-
- if (debug_threads)
- debug_printf ("Deferring signal %d for LWP %ld.\n",
- WSTOPSIG (*wstat), lwpid_of (thread));
-
- if (debug_threads)
- {
- struct pending_signals *sig;
-
- for (sig = lwp->pending_signals_to_report;
- sig != NULL;
- sig = sig->prev)
- debug_printf (" Already queued %d\n",
- sig->signal);
-
- debug_printf (" (no more currently queued signals)\n");
- }
-
- /* Don't enqueue non-RT signals if they are already in the deferred
- queue. (SIGSTOP being the easiest signal to see ending up here
- twice) */
- if (WSTOPSIG (*wstat) < __SIGRTMIN)
- {
- struct pending_signals *sig;
-
- for (sig = lwp->pending_signals_to_report;
- sig != NULL;
- sig = sig->prev)
- {
- if (sig->signal == WSTOPSIG (*wstat))
- {
- if (debug_threads)
- debug_printf ("Not requeuing already queued non-RT signal %d"
- " for LWP %ld\n",
- sig->signal,
- lwpid_of (thread));
- return;
- }
- }
- }
-
- p_sig = XCNEW (struct pending_signals);
- p_sig->prev = lwp->pending_signals_to_report;
- p_sig->signal = WSTOPSIG (*wstat);
-
- ptrace (PTRACE_GETSIGINFO, lwpid_of (thread), (PTRACE_TYPE_ARG3) 0,
- &p_sig->info);
-
- lwp->pending_signals_to_report = p_sig;
-}
-
-/* Dequeue one signal from the "signals to report later when out of
- the jump pad" list. */
-
-static int
-dequeue_one_deferred_signal (struct lwp_info *lwp, int *wstat)
-{
- struct thread_info *thread = get_lwp_thread (lwp);
-
- if (lwp->pending_signals_to_report != NULL)
- {
- struct pending_signals **p_sig;
-
- p_sig = &lwp->pending_signals_to_report;
- while ((*p_sig)->prev != NULL)
- p_sig = &(*p_sig)->prev;
-
- *wstat = W_STOPCODE ((*p_sig)->signal);
- if ((*p_sig)->info.si_signo != 0)
- ptrace (PTRACE_SETSIGINFO, lwpid_of (thread), (PTRACE_TYPE_ARG3) 0,
- &(*p_sig)->info);
- free (*p_sig);
- *p_sig = NULL;
-
- if (debug_threads)
- debug_printf ("Reporting deferred signal %d for LWP %ld.\n",
- WSTOPSIG (*wstat), lwpid_of (thread));
-
- if (debug_threads)
- {
- struct pending_signals *sig;
-
- for (sig = lwp->pending_signals_to_report;
- sig != NULL;
- sig = sig->prev)
- debug_printf (" Still queued %d\n",
- sig->signal);
-
- debug_printf (" (no more queued signals)\n");
- }
-
- return 1;
- }
-
- return 0;
-}
-
-/* Fetch the possibly triggered data watchpoint info and store it in
- CHILD.
-
- On some archs, like x86, that use debug registers to set
- watchpoints, it's possible that the way to know which watched
- address trapped, is to check the register that is used to select
- which address to watch. Problem is, between setting the watchpoint
- and reading back which data address trapped, the user may change
- the set of watchpoints, and, as a consequence, GDB changes the
- debug registers in the inferior. To avoid reading back a stale
- stopped-data-address when that happens, we cache in LP the fact
- that a watchpoint trapped, and the corresponding data address, as
- soon as we see CHILD stop with a SIGTRAP. If GDB changes the debug
- registers meanwhile, we have the cached data we can rely on. */
-
-static int
-check_stopped_by_watchpoint (struct lwp_info *child)
-{
- if (the_low_target.stopped_by_watchpoint != NULL)
- {
- struct thread_info *saved_thread;
-
- saved_thread = current_thread;
- current_thread = get_lwp_thread (child);
-
- if (the_low_target.stopped_by_watchpoint ())
- {
- child->stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
-
- if (the_low_target.stopped_data_address != NULL)
- child->stopped_data_address
- = the_low_target.stopped_data_address ();
- else
- child->stopped_data_address = 0;
- }
-
- current_thread = saved_thread;
- }
-
- return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
-}
-
-/* Return the ptrace options that we want to try to enable. */
-
-static int
-linux_low_ptrace_options (int attached)
-{
- client_state &cs = get_client_state ();
- int options = 0;
-
- if (!attached)
- options |= PTRACE_O_EXITKILL;
-
- if (cs.report_fork_events)
- options |= PTRACE_O_TRACEFORK;
-
- if (cs.report_vfork_events)
- options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
-
- if (cs.report_exec_events)
- options |= PTRACE_O_TRACEEXEC;
-
- options |= PTRACE_O_TRACESYSGOOD;
-
- return options;
-}
-
-/* Do low-level handling of the event, and check if we should go on
- and pass it to caller code. Return the affected lwp if we are, or
- NULL otherwise. */
-
-static struct lwp_info *
-linux_low_filter_event (int lwpid, int wstat)
-{
- client_state &cs = get_client_state ();
- struct lwp_info *child;
- struct thread_info *thread;
- int have_stop_pc = 0;
-
- child = find_lwp_pid (ptid_t (lwpid));
-
- /* Check for stop events reported by a process we didn't already
- know about - anything not already in our LWP list.
-
- If we're expecting to receive stopped processes after
- fork, vfork, and clone events, then we'll just add the
- new one to our list and go back to waiting for the event
- to be reported - the stopped process might be returned
- from waitpid before or after the event is.
-
- But note the case of a non-leader thread exec'ing after the
- leader having exited, and gone from our lists (because
- check_zombie_leaders deleted it). The non-leader thread
- changes its tid to the tgid. */
-
- if (WIFSTOPPED (wstat) && child == NULL && WSTOPSIG (wstat) == SIGTRAP
- && linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC)
- {
- ptid_t child_ptid;
-
- /* A multi-thread exec after we had seen the leader exiting. */
- if (debug_threads)
- {
- debug_printf ("LLW: Re-adding thread group leader LWP %d"
- "after exec.\n", lwpid);
- }
-
- child_ptid = ptid_t (lwpid, lwpid, 0);
- child = add_lwp (child_ptid);
- child->stopped = 1;
- current_thread = child->thread;
- }
-
- /* If we didn't find a process, one of two things presumably happened:
- - A process we started and then detached from has exited. Ignore it.
- - A process we are controlling has forked and the new child's stop
- was reported to us by the kernel. Save its PID. */
- if (child == NULL && WIFSTOPPED (wstat))
- {
- add_to_pid_list (&stopped_pids, lwpid, wstat);
- return NULL;
- }
- else if (child == NULL)
- return NULL;
-
- thread = get_lwp_thread (child);
-
- child->stopped = 1;
-
- child->last_status = wstat;
-
- /* Check if the thread has exited. */
- if ((WIFEXITED (wstat) || WIFSIGNALED (wstat)))
- {
- if (debug_threads)
- debug_printf ("LLFE: %d exited.\n", lwpid);
-
- if (finish_step_over (child))
- {
- /* Unsuspend all other LWPs, and set them back running again. */
- unsuspend_all_lwps (child);
- }
-
- /* If there is at least one more LWP, then the exit signal was
- not the end of the debugged application and should be
- ignored, unless GDB wants to hear about thread exits. */
- if (cs.report_thread_events
- || last_thread_of_process_p (pid_of (thread)))
- {
- /* Since events are serialized to GDB core, and we can't
- report this one right now. Leave the status pending for
- the next time we're able to report it. */
- mark_lwp_dead (child, wstat);
- return child;
- }
- else
- {
- delete_lwp (child);
- return NULL;
- }
- }
-
- gdb_assert (WIFSTOPPED (wstat));
-
- if (WIFSTOPPED (wstat))
- {
- struct process_info *proc;
-
- /* Architecture-specific setup after inferior is running. */
- proc = find_process_pid (pid_of (thread));
- if (proc->tdesc == NULL)
- {
- if (proc->attached)
- {
- /* This needs to happen after we have attached to the
- inferior and it is stopped for the first time, but
- before we access any inferior registers. */
- linux_arch_setup_thread (thread);
- }
- else
- {
- /* The process is started, but GDBserver will do
- architecture-specific setup after the program stops at
- the first instruction. */
- child->status_pending_p = 1;
- child->status_pending = wstat;
- return child;
- }
- }
- }
-
- if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
- {
- struct process_info *proc = find_process_pid (pid_of (thread));
- int options = linux_low_ptrace_options (proc->attached);
-
- linux_enable_event_reporting (lwpid, options);
- child->must_set_ptrace_flags = 0;
- }
-
- /* Always update syscall_state, even if it will be filtered later. */
- if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SYSCALL_SIGTRAP)
- {
- child->syscall_state
- = (child->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
- ? TARGET_WAITKIND_SYSCALL_RETURN
- : TARGET_WAITKIND_SYSCALL_ENTRY);
- }
- else
- {
- /* Almost all other ptrace-stops are known to be outside of system
- calls, with further exceptions in handle_extended_wait. */
- child->syscall_state = TARGET_WAITKIND_IGNORE;
- }
-
- /* Be careful to not overwrite stop_pc until save_stop_reason is
- called. */
- if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
- && linux_is_extended_waitstatus (wstat))
- {
- child->stop_pc = get_pc (child);
- if (handle_extended_wait (&child, wstat))
- {
- /* The event has been handled, so just return without
- reporting it. */
- return NULL;
- }
- }
-
- if (linux_wstatus_maybe_breakpoint (wstat))
- {
- if (save_stop_reason (child))
- have_stop_pc = 1;
- }
-
- if (!have_stop_pc)
- child->stop_pc = get_pc (child);
-
- if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP
- && child->stop_expected)
- {
- if (debug_threads)
- debug_printf ("Expected stop.\n");
- child->stop_expected = 0;
-
- if (thread->last_resume_kind == resume_stop)
- {
- /* We want to report the stop to the core. Treat the
- SIGSTOP as a normal event. */
- if (debug_threads)
- debug_printf ("LLW: resume_stop SIGSTOP caught for %s.\n",
- target_pid_to_str (ptid_of (thread)));
- }
- else if (stopping_threads != NOT_STOPPING_THREADS)
- {
- /* Stopping threads. We don't want this SIGSTOP to end up
- pending. */
- if (debug_threads)
- debug_printf ("LLW: SIGSTOP caught for %s "
- "while stopping threads.\n",
- target_pid_to_str (ptid_of (thread)));
- return NULL;
- }
- else
- {
- /* This is a delayed SIGSTOP. Filter out the event. */
- if (debug_threads)
- debug_printf ("LLW: %s %s, 0, 0 (discard delayed SIGSTOP)\n",
- child->stepping ? "step" : "continue",
- target_pid_to_str (ptid_of (thread)));
-
- linux_resume_one_lwp (child, child->stepping, 0, NULL);
- return NULL;
- }
- }
-
- child->status_pending_p = 1;
- child->status_pending = wstat;
- return child;
-}
-
-/* Return true if THREAD is doing hardware single step. */
-
-static int
-maybe_hw_step (struct thread_info *thread)
-{
- if (can_hardware_single_step ())
- return 1;
- else
- {
- /* GDBserver must insert single-step breakpoint for software
- single step. */
- gdb_assert (has_single_step_breakpoints (thread));
- return 0;
- }
-}
-
-/* Resume LWPs that are currently stopped without any pending status
- to report, but are resumed from the core's perspective. */
-
-static void
-resume_stopped_resumed_lwps (thread_info *thread)
-{
- struct lwp_info *lp = get_thread_lwp (thread);
-
- if (lp->stopped
- && !lp->suspended
- && !lp->status_pending_p
- && thread->last_status.kind == TARGET_WAITKIND_IGNORE)
- {
- int step = 0;
-
- if (thread->last_resume_kind == resume_step)
- step = maybe_hw_step (thread);
-
- if (debug_threads)
- debug_printf ("RSRL: resuming stopped-resumed LWP %s at %s: step=%d\n",
- target_pid_to_str (ptid_of (thread)),
- paddress (lp->stop_pc),
- step);
-
- linux_resume_one_lwp (lp, step, GDB_SIGNAL_0, NULL);
- }
-}
-
-/* Wait for an event from child(ren) WAIT_PTID, and return any that
- match FILTER_PTID (leaving others pending). The PTIDs can be:
- minus_one_ptid, to specify any child; a pid PTID, specifying all
- lwps of a thread group; or a PTID representing a single lwp. Store
- the stop status through the status pointer WSTAT. OPTIONS is
- passed to the waitpid call. Return 0 if no event was found and
- OPTIONS contains WNOHANG. Return -1 if no unwaited-for children
- was found. Return the PID of the stopped child otherwise. */
-
-static int
-linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
- int *wstatp, int options)
-{
- struct thread_info *event_thread;
- struct lwp_info *event_child, *requested_child;
- sigset_t block_mask, prev_mask;
-
- retry:
- /* N.B. event_thread points to the thread_info struct that contains
- event_child. Keep them in sync. */
- event_thread = NULL;
- event_child = NULL;
- requested_child = NULL;
-
- /* Check for a lwp with a pending status. */
-
- if (filter_ptid == minus_one_ptid || filter_ptid.is_pid ())
- {
- event_thread = find_thread_in_random ([&] (thread_info *thread)
- {
- return status_pending_p_callback (thread, filter_ptid);
- });
-
- if (event_thread != NULL)
- event_child = get_thread_lwp (event_thread);
- if (debug_threads && event_thread)
- debug_printf ("Got a pending child %ld\n", lwpid_of (event_thread));
- }
- else if (filter_ptid != null_ptid)
- {
- requested_child = find_lwp_pid (filter_ptid);
-
- if (stopping_threads == NOT_STOPPING_THREADS
- && requested_child->status_pending_p
- && (requested_child->collecting_fast_tracepoint
- != fast_tpoint_collect_result::not_collecting))
- {
- enqueue_one_deferred_signal (requested_child,
- &requested_child->status_pending);
- requested_child->status_pending_p = 0;
- requested_child->status_pending = 0;
- linux_resume_one_lwp (requested_child, 0, 0, NULL);
- }
-
- if (requested_child->suspended
- && requested_child->status_pending_p)
- {
- internal_error (__FILE__, __LINE__,
- "requesting an event out of a"
- " suspended child?");
- }
-
- if (requested_child->status_pending_p)
- {
- event_child = requested_child;
- event_thread = get_lwp_thread (event_child);
- }
- }
-
- if (event_child != NULL)
- {
- if (debug_threads)
- debug_printf ("Got an event from pending child %ld (%04x)\n",
- lwpid_of (event_thread), event_child->status_pending);
- *wstatp = event_child->status_pending;
- event_child->status_pending_p = 0;
- event_child->status_pending = 0;
- current_thread = event_thread;
- return lwpid_of (event_thread);
- }
-
- /* But if we don't find a pending event, we'll have to wait.
-
- We only enter this loop if no process has a pending wait status.
- Thus any action taken in response to a wait status inside this
- loop is responding as soon as we detect the status, not after any
- pending events. */
-
- /* Make sure SIGCHLD is blocked until the sigsuspend below. Block
- all signals while here. */
- sigfillset (&block_mask);
- gdb_sigmask (SIG_BLOCK, &block_mask, &prev_mask);
-
- /* Always pull all events out of the kernel. We'll randomly select
- an event LWP out of all that have events, to prevent
- starvation. */
- while (event_child == NULL)
- {
- pid_t ret = 0;
-
- /* Always use -1 and WNOHANG, due to couple of a kernel/ptrace
- quirks:
-
- - If the thread group leader exits while other threads in the
- thread group still exist, waitpid(TGID, ...) hangs. That
- waitpid won't return an exit status until the other threads
- in the group are reaped.
-
- - When a non-leader thread execs, that thread just vanishes
- without reporting an exit (so we'd hang if we waited for it
- explicitly in that case). The exec event is reported to
- the TGID pid. */
- errno = 0;
- ret = my_waitpid (-1, wstatp, options | WNOHANG);
-
- if (debug_threads)
- debug_printf ("LWFE: waitpid(-1, ...) returned %d, %s\n",
- ret, errno ? safe_strerror (errno) : "ERRNO-OK");
-
- if (ret > 0)
- {
- if (debug_threads)
- {
- debug_printf ("LLW: waitpid %ld received %s\n",
- (long) ret, status_to_str (*wstatp));
- }
-
- /* Filter all events. IOW, leave all events pending. We'll
- randomly select an event LWP out of all that have events
- below. */
- linux_low_filter_event (ret, *wstatp);
- /* Retry until nothing comes out of waitpid. A single
- SIGCHLD can indicate more than one child stopped. */
- continue;
- }
-
- /* Now that we've pulled all events out of the kernel, resume
- LWPs that don't have an interesting event to report. */
- if (stopping_threads == NOT_STOPPING_THREADS)
- for_each_thread (resume_stopped_resumed_lwps);
-
- /* ... and find an LWP with a status to report to the core, if
- any. */
- event_thread = find_thread_in_random ([&] (thread_info *thread)
- {
- return status_pending_p_callback (thread, filter_ptid);
- });
-
- if (event_thread != NULL)
- {
- event_child = get_thread_lwp (event_thread);
- *wstatp = event_child->status_pending;
- event_child->status_pending_p = 0;
- event_child->status_pending = 0;
- break;
- }
-
- /* Check for zombie thread group leaders. Those can't be reaped
- until all other threads in the thread group are. */
- check_zombie_leaders ();
-
- auto not_stopped = [&] (thread_info *thread)
- {
- return not_stopped_callback (thread, wait_ptid);
- };
-
- /* If there are no resumed children left in the set of LWPs we
- want to wait for, bail. We can't just block in
- waitpid/sigsuspend, because lwps might have been left stopped
- in trace-stop state, and we'd be stuck forever waiting for
- their status to change (which would only happen if we resumed
- them). Even if WNOHANG is set, this return code is preferred
- over 0 (below), as it is more detailed. */
- if (find_thread (not_stopped) == NULL)
- {
- if (debug_threads)
- debug_printf ("LLW: exit (no unwaited-for LWP)\n");
- gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
- return -1;
- }
-
- /* No interesting event to report to the caller. */
- if ((options & WNOHANG))
- {
- if (debug_threads)
- debug_printf ("WNOHANG set, no event found\n");
-
- gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
- return 0;
- }
-
- /* Block until we get an event reported with SIGCHLD. */
- if (debug_threads)
- debug_printf ("sigsuspend'ing\n");
-
- sigsuspend (&prev_mask);
- gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
- goto retry;
- }
-
- gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
-
- current_thread = event_thread;
-
- return lwpid_of (event_thread);
-}
-
-/* Wait for an event from child(ren) PTID. PTIDs can be:
- minus_one_ptid, to specify any child; a pid PTID, specifying all
- lwps of a thread group; or a PTID representing a single lwp. Store
- the stop status through the status pointer WSTAT. OPTIONS is
- passed to the waitpid call. Return 0 if no event was found and
- OPTIONS contains WNOHANG. Return -1 if no unwaited-for children
- was found. Return the PID of the stopped child otherwise. */
-
-static int
-linux_wait_for_event (ptid_t ptid, int *wstatp, int options)
-{
- return linux_wait_for_event_filtered (ptid, ptid, wstatp, options);
-}
-
-/* Select one LWP out of those that have events pending. */
-
-static void
-select_event_lwp (struct lwp_info **orig_lp)
-{
- struct thread_info *event_thread = NULL;
-
- /* In all-stop, give preference to the LWP that is being
- single-stepped. There will be at most one, and it's the LWP that
- the core is most interested in. If we didn't do this, then we'd
- have to handle pending step SIGTRAPs somehow in case the core
- later continues the previously-stepped thread, otherwise we'd
- report the pending SIGTRAP, and the core, not having stepped the
- thread, wouldn't understand what the trap was for, and therefore
- would report it to the user as a random signal. */
- if (!non_stop)
- {
- event_thread = find_thread ([] (thread_info *thread)
- {
- lwp_info *lp = get_thread_lwp (thread);
-
- return (thread->last_status.kind == TARGET_WAITKIND_IGNORE
- && thread->last_resume_kind == resume_step
- && lp->status_pending_p);
- });
-
- if (event_thread != NULL)
- {
- if (debug_threads)
- debug_printf ("SEL: Select single-step %s\n",
- target_pid_to_str (ptid_of (event_thread)));
- }
- }
- if (event_thread == NULL)
- {
- /* No single-stepping LWP. Select one at random, out of those
- which have had events. */
-
- event_thread = find_thread_in_random ([&] (thread_info *thread)
- {
- lwp_info *lp = get_thread_lwp (thread);
-
- /* Only resumed LWPs that have an event pending. */
- return (thread->last_status.kind == TARGET_WAITKIND_IGNORE
- && lp->status_pending_p);
- });
- }
-
- if (event_thread != NULL)
- {
- struct lwp_info *event_lp = get_thread_lwp (event_thread);
-
- /* Switch the event LWP. */
- *orig_lp = event_lp;
- }
-}
-
-/* Decrement the suspend count of all LWPs, except EXCEPT, if non
- NULL. */
-
-static void
-unsuspend_all_lwps (struct lwp_info *except)
-{
- for_each_thread ([&] (thread_info *thread)
- {
- lwp_info *lwp = get_thread_lwp (thread);
-
- if (lwp != except)
- lwp_suspended_decr (lwp);
- });
-}
-
-static void move_out_of_jump_pad_callback (thread_info *thread);
-static bool stuck_in_jump_pad_callback (thread_info *thread);
-static bool lwp_running (thread_info *thread);
-static ptid_t linux_wait_1 (ptid_t ptid,
- struct target_waitstatus *ourstatus,
- int target_options);
-
-/* Stabilize threads (move out of jump pads).
-
- If a thread is midway collecting a fast tracepoint, we need to
- finish the collection and move it out of the jump pad before
- reporting the signal.
-
- This avoids recursion while collecting (when a signal arrives
- midway, and the signal handler itself collects), which would trash
- the trace buffer. In case the user set a breakpoint in a signal
- handler, this avoids the backtrace showing the jump pad, etc..
- Most importantly, there are certain things we can't do safely if
- threads are stopped in a jump pad (or in its callee's). For
- example:
-
- - starting a new trace run. A thread still collecting the
- previous run, could trash the trace buffer when resumed. The trace
- buffer control structures would have been reset but the thread had
- no way to tell. The thread could even midway memcpy'ing to the
- buffer, which would mean that when resumed, it would clobber the
- trace buffer that had been set for a new run.
-
- - we can't rewrite/reuse the jump pads for new tracepoints
- safely. Say you do tstart while a thread is stopped midway while
- collecting. When the thread is later resumed, it finishes the
- collection, and returns to the jump pad, to execute the original
- instruction that was under the tracepoint jump at the time the
- older run had been started. If the jump pad had been rewritten
- since for something else in the new run, the thread would now
- execute the wrong / random instructions. */
-
-static void
-linux_stabilize_threads (void)
-{
- thread_info *thread_stuck = find_thread (stuck_in_jump_pad_callback);
-
- if (thread_stuck != NULL)
- {
- if (debug_threads)
- debug_printf ("can't stabilize, LWP %ld is stuck in jump pad\n",
- lwpid_of (thread_stuck));
- return;
- }
-
- thread_info *saved_thread = current_thread;
-
- stabilizing_threads = 1;
-
- /* Kick 'em all. */
- for_each_thread (move_out_of_jump_pad_callback);
-
- /* Loop until all are stopped out of the jump pads. */
- while (find_thread (lwp_running) != NULL)
- {
- struct target_waitstatus ourstatus;
- struct lwp_info *lwp;
- int wstat;
-
- /* Note that we go through the full wait even loop. While
- moving threads out of jump pad, we need to be able to step
- over internal breakpoints and such. */
- linux_wait_1 (minus_one_ptid, &ourstatus, 0);
-
- if (ourstatus.kind == TARGET_WAITKIND_STOPPED)
- {
- lwp = get_thread_lwp (current_thread);
-
- /* Lock it. */
- lwp_suspended_inc (lwp);
-
- if (ourstatus.value.sig != GDB_SIGNAL_0
- || current_thread->last_resume_kind == resume_stop)
- {
- wstat = W_STOPCODE (gdb_signal_to_host (ourstatus.value.sig));
- enqueue_one_deferred_signal (lwp, &wstat);
- }
- }
- }
-
- unsuspend_all_lwps (NULL);
-
- stabilizing_threads = 0;
-
- current_thread = saved_thread;
-
- if (debug_threads)
- {
- thread_stuck = find_thread (stuck_in_jump_pad_callback);
-
- if (thread_stuck != NULL)
- debug_printf ("couldn't stabilize, LWP %ld got stuck in jump pad\n",
- lwpid_of (thread_stuck));
- }
-}
-
-/* Convenience function that is called when the kernel reports an
- event that is not passed out to GDB. */
-
-static ptid_t
-ignore_event (struct target_waitstatus *ourstatus)
-{
- /* If we got an event, there may still be others, as a single
- SIGCHLD can indicate more than one child stopped. This forces
- another target_wait call. */
- async_file_mark ();
-
- ourstatus->kind = TARGET_WAITKIND_IGNORE;
- return null_ptid;
-}
-
-/* Convenience function that is called when the kernel reports an exit
- event. This decides whether to report the event to GDB as a
- process exit event, a thread exit event, or to suppress the
- event. */
-
-static ptid_t
-filter_exit_event (struct lwp_info *event_child,
- struct target_waitstatus *ourstatus)
-{
- client_state &cs = get_client_state ();
- struct thread_info *thread = get_lwp_thread (event_child);
- ptid_t ptid = ptid_of (thread);
-
- if (!last_thread_of_process_p (pid_of (thread)))
- {
- if (cs.report_thread_events)
- ourstatus->kind = TARGET_WAITKIND_THREAD_EXITED;
- else
- ourstatus->kind = TARGET_WAITKIND_IGNORE;
-
- delete_lwp (event_child);
- }
- return ptid;
-}
-
-/* Returns 1 if GDB is interested in any event_child syscalls. */
-
-static int
-gdb_catching_syscalls_p (struct lwp_info *event_child)
-{
- struct thread_info *thread = get_lwp_thread (event_child);
- struct process_info *proc = get_thread_process (thread);
-
- return !proc->syscalls_to_catch.empty ();
-}
-
-/* Returns 1 if GDB is interested in the event_child syscall.
- Only to be called when stopped reason is SYSCALL_SIGTRAP. */
-
-static int
-gdb_catch_this_syscall_p (struct lwp_info *event_child)
-{
- int sysno;
- struct thread_info *thread = get_lwp_thread (event_child);
- struct process_info *proc = get_thread_process (thread);
-
- if (proc->syscalls_to_catch.empty ())
- return 0;
-
- if (proc->syscalls_to_catch[0] == ANY_SYSCALL)
- return 1;
-
- get_syscall_trapinfo (event_child, &sysno);
-
- for (int iter : proc->syscalls_to_catch)
- if (iter == sysno)
- return 1;
-
- return 0;
-}
-
-/* Wait for process, returns status. */
-
-static ptid_t
-linux_wait_1 (ptid_t ptid,
- struct target_waitstatus *ourstatus, int target_options)
-{
- client_state &cs = get_client_state ();
- int w;
- struct lwp_info *event_child;
- int options;
- int pid;
- int step_over_finished;
- int bp_explains_trap;
- int maybe_internal_trap;
- int report_to_gdb;
- int trace_event;
- int in_step_range;
- int any_resumed;
-
- if (debug_threads)
- {
- debug_enter ();
- debug_printf ("linux_wait_1: [%s]\n", target_pid_to_str (ptid));
- }
-
- /* Translate generic target options into linux options. */
- options = __WALL;
- if (target_options & TARGET_WNOHANG)
- options |= WNOHANG;
-
- bp_explains_trap = 0;
- trace_event = 0;
- in_step_range = 0;
- ourstatus->kind = TARGET_WAITKIND_IGNORE;
-
- auto status_pending_p_any = [&] (thread_info *thread)
- {
- return status_pending_p_callback (thread, minus_one_ptid);
- };
-
- auto not_stopped = [&] (thread_info *thread)
- {
- return not_stopped_callback (thread, minus_one_ptid);
- };
-
- /* Find a resumed LWP, if any. */
- if (find_thread (status_pending_p_any) != NULL)
- any_resumed = 1;
- else if (find_thread (not_stopped) != NULL)
- any_resumed = 1;
- else
- any_resumed = 0;
-
- if (step_over_bkpt == null_ptid)
- pid = linux_wait_for_event (ptid, &w, options);
- else
- {
- if (debug_threads)
- debug_printf ("step_over_bkpt set [%s], doing a blocking wait\n",
- target_pid_to_str (step_over_bkpt));
- pid = linux_wait_for_event (step_over_bkpt, &w, options & ~WNOHANG);
- }
-
- if (pid == 0 || (pid == -1 && !any_resumed))
- {
- gdb_assert (target_options & TARGET_WNOHANG);
-
- if (debug_threads)
- {
- debug_printf ("linux_wait_1 ret = null_ptid, "
- "TARGET_WAITKIND_IGNORE\n");
- debug_exit ();
- }
-
- ourstatus->kind = TARGET_WAITKIND_IGNORE;
- return null_ptid;
- }
- else if (pid == -1)
- {
- if (debug_threads)
- {
- debug_printf ("linux_wait_1 ret = null_ptid, "
- "TARGET_WAITKIND_NO_RESUMED\n");
- debug_exit ();
- }
-
- ourstatus->kind = TARGET_WAITKIND_NO_RESUMED;
- return null_ptid;
- }
-
- event_child = get_thread_lwp (current_thread);
-
- /* linux_wait_for_event only returns an exit status for the last
- child of a process. Report it. */
- if (WIFEXITED (w) || WIFSIGNALED (w))
- {
- if (WIFEXITED (w))
- {
- ourstatus->kind = TARGET_WAITKIND_EXITED;
- ourstatus->value.integer = WEXITSTATUS (w);
-
- if (debug_threads)
- {
- debug_printf ("linux_wait_1 ret = %s, exited with "
- "retcode %d\n",
- target_pid_to_str (ptid_of (current_thread)),
- WEXITSTATUS (w));
- debug_exit ();
- }
- }
- else
- {
- ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
- ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (w));
-
- if (debug_threads)
- {
- debug_printf ("linux_wait_1 ret = %s, terminated with "
- "signal %d\n",
- target_pid_to_str (ptid_of (current_thread)),
- WTERMSIG (w));
- debug_exit ();
- }
- }
-
- if (ourstatus->kind == TARGET_WAITKIND_EXITED)
- return filter_exit_event (event_child, ourstatus);
-
- return ptid_of (current_thread);
- }
-
- /* If step-over executes a breakpoint instruction, in the case of a
- hardware single step it means a gdb/gdbserver breakpoint had been
- planted on top of a permanent breakpoint, in the case of a software
- single step it may just mean that gdbserver hit the reinsert breakpoint.
- The PC has been adjusted by save_stop_reason to point at
- the breakpoint address.
- So in the case of the hardware single step advance the PC manually
- past the breakpoint and in the case of software single step advance only
- if it's not the single_step_breakpoint we are hitting.
- This avoids that a program would keep trapping a permanent breakpoint
- forever. */
- if (step_over_bkpt != null_ptid
- && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
- && (event_child->stepping
- || !single_step_breakpoint_inserted_here (event_child->stop_pc)))
- {
- int increment_pc = 0;
- int breakpoint_kind = 0;
- CORE_ADDR stop_pc = event_child->stop_pc;
-
- breakpoint_kind =
- the_target->breakpoint_kind_from_current_state (&stop_pc);
- the_target->sw_breakpoint_from_kind (breakpoint_kind, &increment_pc);
-
- if (debug_threads)
- {
- debug_printf ("step-over for %s executed software breakpoint\n",
- target_pid_to_str (ptid_of (current_thread)));
- }
-
- if (increment_pc != 0)
- {
- struct regcache *regcache
- = get_thread_regcache (current_thread, 1);
-
- event_child->stop_pc += increment_pc;
- (*the_low_target.set_pc) (regcache, event_child->stop_pc);
-
- if (!(*the_low_target.breakpoint_at) (event_child->stop_pc))
- event_child->stop_reason = TARGET_STOPPED_BY_NO_REASON;
- }
- }
-
- /* If this event was not handled before, and is not a SIGTRAP, we
- report it. SIGILL and SIGSEGV are also treated as traps in case
- a breakpoint is inserted at the current PC. If this target does
- not support internal breakpoints at all, we also report the
- SIGTRAP without further processing; it's of no concern to us. */
- maybe_internal_trap
- = (supports_breakpoints ()
- && (WSTOPSIG (w) == SIGTRAP
- || ((WSTOPSIG (w) == SIGILL
- || WSTOPSIG (w) == SIGSEGV)
- && (*the_low_target.breakpoint_at) (event_child->stop_pc))));
-
- if (maybe_internal_trap)
- {
- /* Handle anything that requires bookkeeping before deciding to
- report the event or continue waiting. */
-
- /* First check if we can explain the SIGTRAP with an internal
- breakpoint, or if we should possibly report the event to GDB.
- Do this before anything that may remove or insert a
- breakpoint. */
- bp_explains_trap = breakpoint_inserted_here (event_child->stop_pc);
-
- /* We have a SIGTRAP, possibly a step-over dance has just
- finished. If so, tweak the state machine accordingly,
- reinsert breakpoints and delete any single-step
- breakpoints. */
- step_over_finished = finish_step_over (event_child);
-
- /* Now invoke the callbacks of any internal breakpoints there. */
- check_breakpoints (event_child->stop_pc);
-
- /* Handle tracepoint data collecting. This may overflow the
- trace buffer, and cause a tracing stop, removing
- breakpoints. */
- trace_event = handle_tracepoints (event_child);
-
- if (bp_explains_trap)
- {
- if (debug_threads)
- debug_printf ("Hit a gdbserver breakpoint.\n");
- }
- }
- else
- {
- /* We have some other signal, possibly a step-over dance was in
- progress, and it should be cancelled too. */
- step_over_finished = finish_step_over (event_child);
- }
-
- /* We have all the data we need. Either report the event to GDB, or
- resume threads and keep waiting for more. */
-
- /* If we're collecting a fast tracepoint, finish the collection and
- move out of the jump pad before delivering a signal. See
- linux_stabilize_threads. */
-
- if (WIFSTOPPED (w)
- && WSTOPSIG (w) != SIGTRAP
- && supports_fast_tracepoints ()
- && agent_loaded_p ())
- {
- if (debug_threads)
- debug_printf ("Got signal %d for LWP %ld. Check if we need "
- "to defer or adjust it.\n",
- WSTOPSIG (w), lwpid_of (current_thread));
-
- /* Allow debugging the jump pad itself. */
- if (current_thread->last_resume_kind != resume_step
- && maybe_move_out_of_jump_pad (event_child, &w))
- {
- enqueue_one_deferred_signal (event_child, &w);
-
- if (debug_threads)
- debug_printf ("Signal %d for LWP %ld deferred (in jump pad)\n",
- WSTOPSIG (w), lwpid_of (current_thread));
-
- linux_resume_one_lwp (event_child, 0, 0, NULL);
-
- if (debug_threads)
- debug_exit ();
- return ignore_event (ourstatus);
- }
- }
-
- if (event_child->collecting_fast_tracepoint
- != fast_tpoint_collect_result::not_collecting)
- {
- if (debug_threads)
- debug_printf ("LWP %ld was trying to move out of the jump pad (%d). "
- "Check if we're already there.\n",
- lwpid_of (current_thread),
- (int) event_child->collecting_fast_tracepoint);
-
- trace_event = 1;
-
- event_child->collecting_fast_tracepoint
- = linux_fast_tracepoint_collecting (event_child, NULL);
-
- if (event_child->collecting_fast_tracepoint
- != fast_tpoint_collect_result::before_insn)
- {
- /* No longer need this breakpoint. */
- if (event_child->exit_jump_pad_bkpt != NULL)
- {
- if (debug_threads)
- debug_printf ("No longer need exit-jump-pad bkpt; removing it."
- "stopping all threads momentarily.\n");
-
- /* Other running threads could hit this breakpoint.
- We don't handle moribund locations like GDB does,
- instead we always pause all threads when removing
- breakpoints, so that any step-over or
- decr_pc_after_break adjustment is always taken
- care of while the breakpoint is still
- inserted. */
- stop_all_lwps (1, event_child);
-
- delete_breakpoint (event_child->exit_jump_pad_bkpt);
- event_child->exit_jump_pad_bkpt = NULL;
-
- unstop_all_lwps (1, event_child);
-
- gdb_assert (event_child->suspended >= 0);
- }
- }
-
- if (event_child->collecting_fast_tracepoint
- == fast_tpoint_collect_result::not_collecting)
- {
- if (debug_threads)
- debug_printf ("fast tracepoint finished "
- "collecting successfully.\n");
-
- /* We may have a deferred signal to report. */
- if (dequeue_one_deferred_signal (event_child, &w))
- {
- if (debug_threads)
- debug_printf ("dequeued one signal.\n");
- }
- else
- {
- if (debug_threads)
- debug_printf ("no deferred signals.\n");
-
- if (stabilizing_threads)
- {
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig = GDB_SIGNAL_0;
-
- if (debug_threads)
- {
- debug_printf ("linux_wait_1 ret = %s, stopped "
- "while stabilizing threads\n",
- target_pid_to_str (ptid_of (current_thread)));
- debug_exit ();
- }
-
- return ptid_of (current_thread);
- }
- }
- }
- }
-
- /* Check whether GDB would be interested in this event. */
-
- /* Check if GDB is interested in this syscall. */
- if (WIFSTOPPED (w)
- && WSTOPSIG (w) == SYSCALL_SIGTRAP
- && !gdb_catch_this_syscall_p (event_child))
- {
- if (debug_threads)
- {
- debug_printf ("Ignored syscall for LWP %ld.\n",
- lwpid_of (current_thread));
- }
-
- linux_resume_one_lwp (event_child, event_child->stepping,
- 0, NULL);
-
- if (debug_threads)
- debug_exit ();
- return ignore_event (ourstatus);
- }
-
- /* If GDB is not interested in this signal, don't stop other
- threads, and don't report it to GDB. Just resume the inferior
- right away. We do this for threading-related signals as well as
- any that GDB specifically requested we ignore. But never ignore
- SIGSTOP if we sent it ourselves, and do not ignore signals when
- stepping - they may require special handling to skip the signal
- handler. Also never ignore signals that could be caused by a
- breakpoint. */
- if (WIFSTOPPED (w)
- && current_thread->last_resume_kind != resume_step
- && (
-#if defined (USE_THREAD_DB) && !defined (__ANDROID__)
- (current_process ()->priv->thread_db != NULL
- && (WSTOPSIG (w) == __SIGRTMIN
- || WSTOPSIG (w) == __SIGRTMIN + 1))
- ||
-#endif
- (cs.pass_signals[gdb_signal_from_host (WSTOPSIG (w))]
- && !(WSTOPSIG (w) == SIGSTOP
- && current_thread->last_resume_kind == resume_stop)
- && !linux_wstatus_maybe_breakpoint (w))))
- {
- siginfo_t info, *info_p;
-
- if (debug_threads)
- debug_printf ("Ignored signal %d for LWP %ld.\n",
- WSTOPSIG (w), lwpid_of (current_thread));
-
- if (ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
- (PTRACE_TYPE_ARG3) 0, &info) == 0)
- info_p = &info;
- else
- info_p = NULL;
-
- if (step_over_finished)
- {
- /* We cancelled this thread's step-over above. We still
- need to unsuspend all other LWPs, and set them back
- running again while the signal handler runs. */
- unsuspend_all_lwps (event_child);
-
- /* Enqueue the pending signal info so that proceed_all_lwps
- doesn't lose it. */
- enqueue_pending_signal (event_child, WSTOPSIG (w), info_p);
-
- proceed_all_lwps ();
- }
- else
- {
- linux_resume_one_lwp (event_child, event_child->stepping,
- WSTOPSIG (w), info_p);
- }
-
- if (debug_threads)
- debug_exit ();
-
- return ignore_event (ourstatus);
- }
-
- /* Note that all addresses are always "out of the step range" when
- there's no range to begin with. */
- in_step_range = lwp_in_step_range (event_child);
-
- /* If GDB wanted this thread to single step, and the thread is out
- of the step range, we always want to report the SIGTRAP, and let
- GDB handle it. Watchpoints should always be reported. So should
- signals we can't explain. A SIGTRAP we can't explain could be a
- GDB breakpoint --- we may or not support Z0 breakpoints. If we
- do, we're be able to handle GDB breakpoints on top of internal
- breakpoints, by handling the internal breakpoint and still
- reporting the event to GDB. If we don't, we're out of luck, GDB
- won't see the breakpoint hit. If we see a single-step event but
- the thread should be continuing, don't pass the trap to gdb.
- That indicates that we had previously finished a single-step but
- left the single-step pending -- see
- complete_ongoing_step_over. */
- report_to_gdb = (!maybe_internal_trap
- || (current_thread->last_resume_kind == resume_step
- && !in_step_range)
- || event_child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT
- || (!in_step_range
- && !bp_explains_trap
- && !trace_event
- && !step_over_finished
- && !(current_thread->last_resume_kind == resume_continue
- && event_child->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP))
- || (gdb_breakpoint_here (event_child->stop_pc)
- && gdb_condition_true_at_breakpoint (event_child->stop_pc)
- && gdb_no_commands_at_breakpoint (event_child->stop_pc))
- || event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE);
-
- run_breakpoint_commands (event_child->stop_pc);
-
- /* We found no reason GDB would want us to stop. We either hit one
- of our own breakpoints, or finished an internal step GDB
- shouldn't know about. */
- if (!report_to_gdb)
- {
- if (debug_threads)
- {
- if (bp_explains_trap)
- debug_printf ("Hit a gdbserver breakpoint.\n");
- if (step_over_finished)
- debug_printf ("Step-over finished.\n");
- if (trace_event)
- debug_printf ("Tracepoint event.\n");
- if (lwp_in_step_range (event_child))
- debug_printf ("Range stepping pc 0x%s [0x%s, 0x%s).\n",
- paddress (event_child->stop_pc),
- paddress (event_child->step_range_start),
- paddress (event_child->step_range_end));
- }
-
- /* We're not reporting this breakpoint to GDB, so apply the
- decr_pc_after_break adjustment to the inferior's regcache
- ourselves. */
-
- if (the_low_target.set_pc != NULL)
- {
- struct regcache *regcache
- = get_thread_regcache (current_thread, 1);
- (*the_low_target.set_pc) (regcache, event_child->stop_pc);
- }
-
- if (step_over_finished)
- {
- /* If we have finished stepping over a breakpoint, we've
- stopped and suspended all LWPs momentarily except the
- stepping one. This is where we resume them all again.
- We're going to keep waiting, so use proceed, which
- handles stepping over the next breakpoint. */
- unsuspend_all_lwps (event_child);
- }
- else
- {
- /* Remove the single-step breakpoints if any. Note that
- there isn't single-step breakpoint if we finished stepping
- over. */
- if (can_software_single_step ()
- && has_single_step_breakpoints (current_thread))
- {
- stop_all_lwps (0, event_child);
- delete_single_step_breakpoints (current_thread);
- unstop_all_lwps (0, event_child);
- }
- }
-
- if (debug_threads)
- debug_printf ("proceeding all threads.\n");
- proceed_all_lwps ();
-
- if (debug_threads)
- debug_exit ();
-
- return ignore_event (ourstatus);
- }
-
- if (debug_threads)
- {
- if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
- {
- std::string str
- = target_waitstatus_to_string (&event_child->waitstatus);
-
- debug_printf ("LWP %ld: extended event with waitstatus %s\n",
- lwpid_of (get_lwp_thread (event_child)), str.c_str ());
- }
- if (current_thread->last_resume_kind == resume_step)
- {
- if (event_child->step_range_start == event_child->step_range_end)
- debug_printf ("GDB wanted to single-step, reporting event.\n");
- else if (!lwp_in_step_range (event_child))
- debug_printf ("Out of step range, reporting event.\n");
- }
- if (event_child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT)
- debug_printf ("Stopped by watchpoint.\n");
- else if (gdb_breakpoint_here (event_child->stop_pc))
- debug_printf ("Stopped by GDB breakpoint.\n");
- if (debug_threads)
- debug_printf ("Hit a non-gdbserver trap event.\n");
- }
-
- /* Alright, we're going to report a stop. */
-
- /* Remove single-step breakpoints. */
- if (can_software_single_step ())
- {
- /* Remove single-step breakpoints or not. It it is true, stop all
- lwps, so that other threads won't hit the breakpoint in the
- staled memory. */
- int remove_single_step_breakpoints_p = 0;
-
- if (non_stop)
- {
- remove_single_step_breakpoints_p
- = has_single_step_breakpoints (current_thread);
- }
- else
- {
- /* In all-stop, a stop reply cancels all previous resume
- requests. Delete all single-step breakpoints. */
-
- find_thread ([&] (thread_info *thread) {
- if (has_single_step_breakpoints (thread))
- {
- remove_single_step_breakpoints_p = 1;
- return true;
- }
-
- return false;
- });
- }
-
- if (remove_single_step_breakpoints_p)
- {
- /* If we remove single-step breakpoints from memory, stop all lwps,
- so that other threads won't hit the breakpoint in the staled
- memory. */
- stop_all_lwps (0, event_child);
-
- if (non_stop)
- {
- gdb_assert (has_single_step_breakpoints (current_thread));
- delete_single_step_breakpoints (current_thread);
- }
- else
- {
- for_each_thread ([] (thread_info *thread){
- if (has_single_step_breakpoints (thread))
- delete_single_step_breakpoints (thread);
- });
- }
-
- unstop_all_lwps (0, event_child);
- }
- }
-
- if (!stabilizing_threads)
- {
- /* In all-stop, stop all threads. */
- if (!non_stop)
- stop_all_lwps (0, NULL);
-
- if (step_over_finished)
- {
- if (!non_stop)
- {
- /* If we were doing a step-over, all other threads but
- the stepping one had been paused in start_step_over,
- with their suspend counts incremented. We don't want
- to do a full unstop/unpause, because we're in
- all-stop mode (so we want threads stopped), but we
- still need to unsuspend the other threads, to
- decrement their `suspended' count back. */
- unsuspend_all_lwps (event_child);
- }
- else
- {
- /* If we just finished a step-over, then all threads had
- been momentarily paused. In all-stop, that's fine,
- we want threads stopped by now anyway. In non-stop,
- we need to re-resume threads that GDB wanted to be
- running. */
- unstop_all_lwps (1, event_child);
- }
- }
-
- /* If we're not waiting for a specific LWP, choose an event LWP
- from among those that have had events. Giving equal priority
- to all LWPs that have had events helps prevent
- starvation. */
- if (ptid == minus_one_ptid)
- {
- event_child->status_pending_p = 1;
- event_child->status_pending = w;
-
- select_event_lwp (&event_child);
-
- /* current_thread and event_child must stay in sync. */
- current_thread = get_lwp_thread (event_child);
-
- event_child->status_pending_p = 0;
- w = event_child->status_pending;
- }
-
-
- /* Stabilize threads (move out of jump pads). */
- if (!non_stop)
- stabilize_threads ();
- }
- else
- {
- /* If we just finished a step-over, then all threads had been
- momentarily paused. In all-stop, that's fine, we want
- threads stopped by now anyway. In non-stop, we need to
- re-resume threads that GDB wanted to be running. */
- if (step_over_finished)
- unstop_all_lwps (1, event_child);
- }
-
- if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
- {
- /* If the reported event is an exit, fork, vfork or exec, let
- GDB know. */
-
- /* Break the unreported fork relationship chain. */
- if (event_child->waitstatus.kind == TARGET_WAITKIND_FORKED
- || event_child->waitstatus.kind == TARGET_WAITKIND_VFORKED)
- {
- event_child->fork_relative->fork_relative = NULL;
- event_child->fork_relative = NULL;
- }
-
- *ourstatus = event_child->waitstatus;
- /* Clear the event lwp's waitstatus since we handled it already. */
- event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
- }
- else
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
-
- /* Now that we've selected our final event LWP, un-adjust its PC if
- it was a software breakpoint, and the client doesn't know we can
- adjust the breakpoint ourselves. */
- if (event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
- && !cs.swbreak_feature)
- {
- int decr_pc = the_low_target.decr_pc_after_break;
-
- if (decr_pc != 0)
- {
- struct regcache *regcache
- = get_thread_regcache (current_thread, 1);
- (*the_low_target.set_pc) (regcache, event_child->stop_pc + decr_pc);
- }
- }
-
- if (WSTOPSIG (w) == SYSCALL_SIGTRAP)
- {
- get_syscall_trapinfo (event_child,
- &ourstatus->value.syscall_number);
- ourstatus->kind = event_child->syscall_state;
- }
- else if (current_thread->last_resume_kind == resume_stop
- && WSTOPSIG (w) == SIGSTOP)
- {
- /* A thread that has been requested to stop by GDB with vCont;t,
- and it stopped cleanly, so report as SIG0. The use of
- SIGSTOP is an implementation detail. */
- ourstatus->value.sig = GDB_SIGNAL_0;
- }
- else if (current_thread->last_resume_kind == resume_stop
- && WSTOPSIG (w) != SIGSTOP)
- {
- /* A thread that has been requested to stop by GDB with vCont;t,
- but, it stopped for other reasons. */
- ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
- }
- else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
- {
- ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
- }
-
- gdb_assert (step_over_bkpt == null_ptid);
-
- if (debug_threads)
- {
- debug_printf ("linux_wait_1 ret = %s, %d, %d\n",
- target_pid_to_str (ptid_of (current_thread)),
- ourstatus->kind, ourstatus->value.sig);
- debug_exit ();
- }
-
- if (ourstatus->kind == TARGET_WAITKIND_EXITED)
- return filter_exit_event (event_child, ourstatus);
-
- return ptid_of (current_thread);
-}
-
-/* Get rid of any pending event in the pipe. */
-static void
-async_file_flush (void)
-{
- int ret;
- char buf;
-
- do
- ret = read (linux_event_pipe[0], &buf, 1);
- while (ret >= 0 || (ret == -1 && errno == EINTR));
-}
-
-/* Put something in the pipe, so the event loop wakes up. */
-static void
-async_file_mark (void)
-{
- int ret;
-
- async_file_flush ();
-
- do
- ret = write (linux_event_pipe[1], "+", 1);
- while (ret == 0 || (ret == -1 && errno == EINTR));
-
- /* Ignore EAGAIN. If the pipe is full, the event loop will already
- be awakened anyway. */
-}
-
-static ptid_t
-linux_wait (ptid_t ptid,
- struct target_waitstatus *ourstatus, int target_options)
-{
- ptid_t event_ptid;
-
- /* Flush the async file first. */
- if (target_is_async_p ())
- async_file_flush ();
-
- do
- {
- event_ptid = linux_wait_1 (ptid, ourstatus, target_options);
- }
- while ((target_options & TARGET_WNOHANG) == 0
- && event_ptid == null_ptid
- && ourstatus->kind == TARGET_WAITKIND_IGNORE);
-
- /* If at least one stop was reported, there may be more. A single
- SIGCHLD can signal more than one child stop. */
- if (target_is_async_p ()
- && (target_options & TARGET_WNOHANG) != 0
- && event_ptid != null_ptid)
- async_file_mark ();
-
- return event_ptid;
-}
-
-/* Send a signal to an LWP. */
-
-static int
-kill_lwp (unsigned long lwpid, int signo)
-{
- int ret;
-
- errno = 0;
- ret = syscall (__NR_tkill, lwpid, signo);
- if (errno == ENOSYS)
- {
- /* If tkill fails, then we are not using nptl threads, a
- configuration we no longer support. */
- perror_with_name (("tkill"));
- }
- return ret;
-}
-
-void
-linux_stop_lwp (struct lwp_info *lwp)
-{
- send_sigstop (lwp);
-}
-
-static void
-send_sigstop (struct lwp_info *lwp)
-{
- int pid;
-
- pid = lwpid_of (get_lwp_thread (lwp));
-
- /* If we already have a pending stop signal for this process, don't
- send another. */
- if (lwp->stop_expected)
- {
- if (debug_threads)
- debug_printf ("Have pending sigstop for lwp %d\n", pid);
-
- return;
- }
-
- if (debug_threads)
- debug_printf ("Sending sigstop to lwp %d\n", pid);
-
- lwp->stop_expected = 1;
- kill_lwp (pid, SIGSTOP);
-}
-
-static void
-send_sigstop (thread_info *thread, lwp_info *except)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- /* Ignore EXCEPT. */
- if (lwp == except)
- return;
-
- if (lwp->stopped)
- return;
-
- send_sigstop (lwp);
-}
-
-/* Increment the suspend count of an LWP, and stop it, if not stopped
- yet. */
-static void
-suspend_and_send_sigstop (thread_info *thread, lwp_info *except)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- /* Ignore EXCEPT. */
- if (lwp == except)
- return;
-
- lwp_suspended_inc (lwp);
-
- send_sigstop (thread, except);
-}
-
-static void
-mark_lwp_dead (struct lwp_info *lwp, int wstat)
-{
- /* Store the exit status for later. */
- lwp->status_pending_p = 1;
- lwp->status_pending = wstat;
-
- /* Store in waitstatus as well, as there's nothing else to process
- for this event. */
- if (WIFEXITED (wstat))
- {
- lwp->waitstatus.kind = TARGET_WAITKIND_EXITED;
- lwp->waitstatus.value.integer = WEXITSTATUS (wstat);
- }
- else if (WIFSIGNALED (wstat))
- {
- lwp->waitstatus.kind = TARGET_WAITKIND_SIGNALLED;
- lwp->waitstatus.value.sig = gdb_signal_from_host (WTERMSIG (wstat));
- }
-
- /* Prevent trying to stop it. */
- lwp->stopped = 1;
-
- /* No further stops are expected from a dead lwp. */
- lwp->stop_expected = 0;
-}
-
-/* Return true if LWP has exited already, and has a pending exit event
- to report to GDB. */
-
-static int
-lwp_is_marked_dead (struct lwp_info *lwp)
-{
- return (lwp->status_pending_p
- && (WIFEXITED (lwp->status_pending)
- || WIFSIGNALED (lwp->status_pending)));
-}
-
-/* Wait for all children to stop for the SIGSTOPs we just queued. */
-
-static void
-wait_for_sigstop (void)
-{
- struct thread_info *saved_thread;
- ptid_t saved_tid;
- int wstat;
- int ret;
-
- saved_thread = current_thread;
- if (saved_thread != NULL)
- saved_tid = saved_thread->id;
- else
- saved_tid = null_ptid; /* avoid bogus unused warning */
-
- if (debug_threads)
- debug_printf ("wait_for_sigstop: pulling events\n");
-
- /* Passing NULL_PTID as filter indicates we want all events to be
- left pending. Eventually this returns when there are no
- unwaited-for children left. */
- ret = linux_wait_for_event_filtered (minus_one_ptid, null_ptid,
- &wstat, __WALL);
- gdb_assert (ret == -1);
-
- if (saved_thread == NULL || linux_thread_alive (saved_tid))
- current_thread = saved_thread;
- else
- {
- if (debug_threads)
- debug_printf ("Previously current thread died.\n");
-
- /* We can't change the current inferior behind GDB's back,
- otherwise, a subsequent command may apply to the wrong
- process. */
- current_thread = NULL;
- }
-}
-
-/* Returns true if THREAD is stopped in a jump pad, and we can't
- move it out, because we need to report the stop event to GDB. For
- example, if the user puts a breakpoint in the jump pad, it's
- because she wants to debug it. */
-
-static bool
-stuck_in_jump_pad_callback (thread_info *thread)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- if (lwp->suspended != 0)
- {
- internal_error (__FILE__, __LINE__,
- "LWP %ld is suspended, suspended=%d\n",
- lwpid_of (thread), lwp->suspended);
- }
- gdb_assert (lwp->stopped);
-
- /* Allow debugging the jump pad, gdb_collect, etc.. */
- return (supports_fast_tracepoints ()
- && agent_loaded_p ()
- && (gdb_breakpoint_here (lwp->stop_pc)
- || lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT
- || thread->last_resume_kind == resume_step)
- && (linux_fast_tracepoint_collecting (lwp, NULL)
- != fast_tpoint_collect_result::not_collecting));
-}
-
-static void
-move_out_of_jump_pad_callback (thread_info *thread)
-{
- struct thread_info *saved_thread;
- struct lwp_info *lwp = get_thread_lwp (thread);
- int *wstat;
-
- if (lwp->suspended != 0)
- {
- internal_error (__FILE__, __LINE__,
- "LWP %ld is suspended, suspended=%d\n",
- lwpid_of (thread), lwp->suspended);
- }
- gdb_assert (lwp->stopped);
-
- /* For gdb_breakpoint_here. */
- saved_thread = current_thread;
- current_thread = thread;
-
- wstat = lwp->status_pending_p ? &lwp->status_pending : NULL;
-
- /* Allow debugging the jump pad, gdb_collect, etc. */
- if (!gdb_breakpoint_here (lwp->stop_pc)
- && lwp->stop_reason != TARGET_STOPPED_BY_WATCHPOINT
- && thread->last_resume_kind != resume_step
- && maybe_move_out_of_jump_pad (lwp, wstat))
- {
- if (debug_threads)
- debug_printf ("LWP %ld needs stabilizing (in jump pad)\n",
- lwpid_of (thread));
-
- if (wstat)
- {
- lwp->status_pending_p = 0;
- enqueue_one_deferred_signal (lwp, wstat);
-
- if (debug_threads)
- debug_printf ("Signal %d for LWP %ld deferred "
- "(in jump pad)\n",
- WSTOPSIG (*wstat), lwpid_of (thread));
- }
-
- linux_resume_one_lwp (lwp, 0, 0, NULL);
- }
- else
- lwp_suspended_inc (lwp);
-
- current_thread = saved_thread;
-}
-
-static bool
-lwp_running (thread_info *thread)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- if (lwp_is_marked_dead (lwp))
- return false;
-
- return !lwp->stopped;
-}
-
-/* Stop all lwps that aren't stopped yet, except EXCEPT, if not NULL.
- If SUSPEND, then also increase the suspend count of every LWP,
- except EXCEPT. */
-
-static void
-stop_all_lwps (int suspend, struct lwp_info *except)
-{
- /* Should not be called recursively. */
- gdb_assert (stopping_threads == NOT_STOPPING_THREADS);
-
- if (debug_threads)
- {
- debug_enter ();
- debug_printf ("stop_all_lwps (%s, except=%s)\n",
- suspend ? "stop-and-suspend" : "stop",
- except != NULL
- ? target_pid_to_str (ptid_of (get_lwp_thread (except)))
- : "none");
- }
-
- stopping_threads = (suspend
- ? STOPPING_AND_SUSPENDING_THREADS
- : STOPPING_THREADS);
-
- if (suspend)
- for_each_thread ([&] (thread_info *thread)
- {
- suspend_and_send_sigstop (thread, except);
- });
- else
- for_each_thread ([&] (thread_info *thread)
- {
- send_sigstop (thread, except);
- });
-
- wait_for_sigstop ();
- stopping_threads = NOT_STOPPING_THREADS;
-
- if (debug_threads)
- {
- debug_printf ("stop_all_lwps done, setting stopping_threads "
- "back to !stopping\n");
- debug_exit ();
- }
-}
-
-/* Enqueue one signal in the chain of signals which need to be
- delivered to this process on next resume. */
-
-static void
-enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info)
-{
- struct pending_signals *p_sig = XNEW (struct pending_signals);
-
- p_sig->prev = lwp->pending_signals;
- p_sig->signal = signal;
- if (info == NULL)
- memset (&p_sig->info, 0, sizeof (siginfo_t));
- else
- memcpy (&p_sig->info, info, sizeof (siginfo_t));
- lwp->pending_signals = p_sig;
-}
-
-/* Install breakpoints for software single stepping. */
-
-static void
-install_software_single_step_breakpoints (struct lwp_info *lwp)
-{
- struct thread_info *thread = get_lwp_thread (lwp);
- struct regcache *regcache = get_thread_regcache (thread, 1);
-
- scoped_restore save_current_thread = make_scoped_restore (¤t_thread);
-
- current_thread = thread;
- std::vector<CORE_ADDR> next_pcs = the_low_target.get_next_pcs (regcache);
-
- for (CORE_ADDR pc : next_pcs)
- set_single_step_breakpoint (pc, current_ptid);
-}
-
-/* Single step via hardware or software single step.
- Return 1 if hardware single stepping, 0 if software single stepping
- or can't single step. */
-
-static int
-single_step (struct lwp_info* lwp)
-{
- int step = 0;
-
- if (can_hardware_single_step ())
- {
- step = 1;
- }
- else if (can_software_single_step ())
- {
- install_software_single_step_breakpoints (lwp);
- step = 0;
- }
- else
- {
- if (debug_threads)
- debug_printf ("stepping is not implemented on this target");
- }
-
- return step;
-}
-
-/* The signal can be delivered to the inferior if we are not trying to
- finish a fast tracepoint collect. Since signal can be delivered in
- the step-over, the program may go to signal handler and trap again
- after return from the signal handler. We can live with the spurious
- double traps. */
-
-static int
-lwp_signal_can_be_delivered (struct lwp_info *lwp)
-{
- return (lwp->collecting_fast_tracepoint
- == fast_tpoint_collect_result::not_collecting);
-}
-
-/* Resume execution of LWP. If STEP is nonzero, single-step it. If
- SIGNAL is nonzero, give it that signal. */
-
-static void
-linux_resume_one_lwp_throw (struct lwp_info *lwp,
- int step, int signal, siginfo_t *info)
-{
- struct thread_info *thread = get_lwp_thread (lwp);
- struct thread_info *saved_thread;
- int ptrace_request;
- struct process_info *proc = get_thread_process (thread);
-
- /* Note that target description may not be initialised
- (proc->tdesc == NULL) at this point because the program hasn't
- stopped at the first instruction yet. It means GDBserver skips
- the extra traps from the wrapper program (see option --wrapper).
- Code in this function that requires register access should be
- guarded by proc->tdesc == NULL or something else. */
-
- if (lwp->stopped == 0)
- return;
-
- gdb_assert (lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
-
- fast_tpoint_collect_result fast_tp_collecting
- = lwp->collecting_fast_tracepoint;
-
- gdb_assert (!stabilizing_threads
- || (fast_tp_collecting
- != fast_tpoint_collect_result::not_collecting));
-
- /* Cancel actions that rely on GDB not changing the PC (e.g., the
- user used the "jump" command, or "set $pc = foo"). */
- if (thread->while_stepping != NULL && lwp->stop_pc != get_pc (lwp))
- {
- /* Collecting 'while-stepping' actions doesn't make sense
- anymore. */
- release_while_stepping_state_list (thread);
- }
-
- /* If we have pending signals or status, and a new signal, enqueue the
- signal. Also enqueue the signal if it can't be delivered to the
- inferior right now. */
- if (signal != 0
- && (lwp->status_pending_p
- || lwp->pending_signals != NULL
- || !lwp_signal_can_be_delivered (lwp)))
- {
- enqueue_pending_signal (lwp, signal, info);
-
- /* Postpone any pending signal. It was enqueued above. */
- signal = 0;
- }
-
- if (lwp->status_pending_p)
- {
- if (debug_threads)
- debug_printf ("Not resuming lwp %ld (%s, stop %s);"
- " has pending status\n",
- lwpid_of (thread), step ? "step" : "continue",
- lwp->stop_expected ? "expected" : "not expected");
- return;
- }
-
- saved_thread = current_thread;
- current_thread = thread;
-
- /* This bit needs some thinking about. If we get a signal that
- we must report while a single-step reinsert is still pending,
- we often end up resuming the thread. It might be better to
- (ew) allow a stack of pending events; then we could be sure that
- the reinsert happened right away and not lose any signals.
-
- Making this stack would also shrink the window in which breakpoints are
- uninserted (see comment in linux_wait_for_lwp) but not enough for
- complete correctness, so it won't solve that problem. It may be
- worthwhile just to solve this one, however. */
- if (lwp->bp_reinsert != 0)
- {
- if (debug_threads)
- debug_printf (" pending reinsert at 0x%s\n",
- paddress (lwp->bp_reinsert));
-
- if (can_hardware_single_step ())
- {
- if (fast_tp_collecting == fast_tpoint_collect_result::not_collecting)
- {
- if (step == 0)
- warning ("BAD - reinserting but not stepping.");
- if (lwp->suspended)
- warning ("BAD - reinserting and suspended(%d).",
- lwp->suspended);
- }
- }
-
- step = maybe_hw_step (thread);
- }
-
- if (fast_tp_collecting == fast_tpoint_collect_result::before_insn)
- {
- if (debug_threads)
- debug_printf ("lwp %ld wants to get out of fast tracepoint jump pad"
- " (exit-jump-pad-bkpt)\n",
- lwpid_of (thread));
- }
- else if (fast_tp_collecting == fast_tpoint_collect_result::at_insn)
- {
- if (debug_threads)
- debug_printf ("lwp %ld wants to get out of fast tracepoint jump pad"
- " single-stepping\n",
- lwpid_of (thread));
-
- if (can_hardware_single_step ())
- step = 1;
- else
- {
- internal_error (__FILE__, __LINE__,
- "moving out of jump pad single-stepping"
- " not implemented on this target");
- }
- }
-
- /* If we have while-stepping actions in this thread set it stepping.
- If we have a signal to deliver, it may or may not be set to
- SIG_IGN, we don't know. Assume so, and allow collecting
- while-stepping into a signal handler. A possible smart thing to
- do would be to set an internal breakpoint at the signal return
- address, continue, and carry on catching this while-stepping
- action only when that breakpoint is hit. A future
- enhancement. */
- if (thread->while_stepping != NULL)
- {
- if (debug_threads)
- debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
- lwpid_of (thread));
-
- step = single_step (lwp);
- }
-
- if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
- {
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
-
- lwp->stop_pc = (*the_low_target.get_pc) (regcache);
-
- if (debug_threads)
- {
- debug_printf (" %s from pc 0x%lx\n", step ? "step" : "continue",
- (long) lwp->stop_pc);
- }
- }
-
- /* If we have pending signals, consume one if it can be delivered to
- the inferior. */
- if (lwp->pending_signals != NULL && lwp_signal_can_be_delivered (lwp))
- {
- struct pending_signals **p_sig;
-
- p_sig = &lwp->pending_signals;
- while ((*p_sig)->prev != NULL)
- p_sig = &(*p_sig)->prev;
-
- signal = (*p_sig)->signal;
- if ((*p_sig)->info.si_signo != 0)
- ptrace (PTRACE_SETSIGINFO, lwpid_of (thread), (PTRACE_TYPE_ARG3) 0,
- &(*p_sig)->info);
-
- free (*p_sig);
- *p_sig = NULL;
- }
-
- if (debug_threads)
- debug_printf ("Resuming lwp %ld (%s, signal %d, stop %s)\n",
- lwpid_of (thread), step ? "step" : "continue", signal,
- lwp->stop_expected ? "expected" : "not expected");
-
- if (the_low_target.prepare_to_resume != NULL)
- the_low_target.prepare_to_resume (lwp);
-
- regcache_invalidate_thread (thread);
- errno = 0;
- lwp->stepping = step;
- if (step)
- ptrace_request = PTRACE_SINGLESTEP;
- else if (gdb_catching_syscalls_p (lwp))
- ptrace_request = PTRACE_SYSCALL;
- else
- ptrace_request = PTRACE_CONT;
- ptrace (ptrace_request,
- lwpid_of (thread),
- (PTRACE_TYPE_ARG3) 0,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- of coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_TYPE_ARG4) (uintptr_t) signal);
-
- current_thread = saved_thread;
- if (errno)
- perror_with_name ("resuming thread");
-
- /* Successfully resumed. Clear state that no longer makes sense,
- and mark the LWP as running. Must not do this before resuming
- otherwise if that fails other code will be confused. E.g., we'd
- later try to stop the LWP and hang forever waiting for a stop
- status. Note that we must not throw after this is cleared,
- otherwise handle_zombie_lwp_error would get confused. */
- lwp->stopped = 0;
- lwp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
-}
-
-/* Called when we try to resume a stopped LWP and that errors out. If
- the LWP is no longer in ptrace-stopped state (meaning it's zombie,
- or about to become), discard the error, clear any pending status
- the LWP may have, and return true (we'll collect the exit status
- soon enough). Otherwise, return false. */
-
-static int
-check_ptrace_stopped_lwp_gone (struct lwp_info *lp)
-{
- struct thread_info *thread = get_lwp_thread (lp);
-
- /* If we get an error after resuming the LWP successfully, we'd
- confuse !T state for the LWP being gone. */
- gdb_assert (lp->stopped);
-
- /* We can't just check whether the LWP is in 'Z (Zombie)' state,
- because even if ptrace failed with ESRCH, the tracee may be "not
- yet fully dead", but already refusing ptrace requests. In that
- case the tracee has 'R (Running)' state for a little bit
- (observed in Linux 3.18). See also the note on ESRCH in the
- ptrace(2) man page. Instead, check whether the LWP has any state
- other than ptrace-stopped. */
-
- /* Don't assume anything if /proc/PID/status can't be read. */
- if (linux_proc_pid_is_trace_stopped_nowarn (lwpid_of (thread)) == 0)
- {
- lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
- lp->status_pending_p = 0;
- return 1;
- }
- return 0;
-}
-
-/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
- disappears while we try to resume it. */
-
-static void
-linux_resume_one_lwp (struct lwp_info *lwp,
- int step, int signal, siginfo_t *info)
-{
- try
- {
- linux_resume_one_lwp_throw (lwp, step, signal, info);
- }
- catch (const gdb_exception_error &ex)
- {
- if (!check_ptrace_stopped_lwp_gone (lwp))
- throw;
- }
-}
-
-/* This function is called once per thread via for_each_thread.
- We look up which resume request applies to THREAD and mark it with a
- pointer to the appropriate resume request.
-
- This algorithm is O(threads * resume elements), but resume elements
- is small (and will remain small at least until GDB supports thread
- suspension). */
-
-static void
-linux_set_resume_request (thread_info *thread, thread_resume *resume, size_t n)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- for (int ndx = 0; ndx < n; ndx++)
- {
- ptid_t ptid = resume[ndx].thread;
- if (ptid == minus_one_ptid
- || ptid == thread->id
- /* Handle both 'pPID' and 'pPID.-1' as meaning 'all threads
- of PID'. */
- || (ptid.pid () == pid_of (thread)
- && (ptid.is_pid ()
- || ptid.lwp () == -1)))
- {
- if (resume[ndx].kind == resume_stop
- && thread->last_resume_kind == resume_stop)
- {
- if (debug_threads)
- debug_printf ("already %s LWP %ld at GDB's request\n",
- (thread->last_status.kind
- == TARGET_WAITKIND_STOPPED)
- ? "stopped"
- : "stopping",
- lwpid_of (thread));
-
- continue;
- }
-
- /* Ignore (wildcard) resume requests for already-resumed
- threads. */
- if (resume[ndx].kind != resume_stop
- && thread->last_resume_kind != resume_stop)
- {
- if (debug_threads)
- debug_printf ("already %s LWP %ld at GDB's request\n",
- (thread->last_resume_kind
- == resume_step)
- ? "stepping"
- : "continuing",
- lwpid_of (thread));
- continue;
- }
-
- /* Don't let wildcard resumes resume fork children that GDB
- does not yet know are new fork children. */
- if (lwp->fork_relative != NULL)
- {
- struct lwp_info *rel = lwp->fork_relative;
-
- if (rel->status_pending_p
- && (rel->waitstatus.kind == TARGET_WAITKIND_FORKED
- || rel->waitstatus.kind == TARGET_WAITKIND_VFORKED))
- {
- if (debug_threads)
- debug_printf ("not resuming LWP %ld: has queued stop reply\n",
- lwpid_of (thread));
- continue;
- }
- }
-
- /* If the thread has a pending event that has already been
- reported to GDBserver core, but GDB has not pulled the
- event out of the vStopped queue yet, likewise, ignore the
- (wildcard) resume request. */
- if (in_queued_stop_replies (thread->id))
- {
- if (debug_threads)
- debug_printf ("not resuming LWP %ld: has queued stop reply\n",
- lwpid_of (thread));
- continue;
- }
-
- lwp->resume = &resume[ndx];
- thread->last_resume_kind = lwp->resume->kind;
-
- lwp->step_range_start = lwp->resume->step_range_start;
- lwp->step_range_end = lwp->resume->step_range_end;
-
- /* If we had a deferred signal to report, dequeue one now.
- This can happen if LWP gets more than one signal while
- trying to get out of a jump pad. */
- if (lwp->stopped
- && !lwp->status_pending_p
- && dequeue_one_deferred_signal (lwp, &lwp->status_pending))
- {
- lwp->status_pending_p = 1;
-
- if (debug_threads)
- debug_printf ("Dequeueing deferred signal %d for LWP %ld, "
- "leaving status pending.\n",
- WSTOPSIG (lwp->status_pending),
- lwpid_of (thread));
- }
-
- return;
- }
- }
-
- /* No resume action for this thread. */
- lwp->resume = NULL;
-}
-
-/* find_thread callback for linux_resume. Return true if this lwp has an
- interesting status pending. */
-
-static bool
-resume_status_pending_p (thread_info *thread)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- /* LWPs which will not be resumed are not interesting, because
- we might not wait for them next time through linux_wait. */
- if (lwp->resume == NULL)
- return false;
-
- return thread_still_has_status_pending_p (thread);
-}
-
-/* Return 1 if this lwp that GDB wants running is stopped at an
- internal breakpoint that we need to step over. It assumes that any
- required STOP_PC adjustment has already been propagated to the
- inferior's regcache. */
-
-static bool
-need_step_over_p (thread_info *thread)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
- struct thread_info *saved_thread;
- CORE_ADDR pc;
- struct process_info *proc = get_thread_process (thread);
-
- /* GDBserver is skipping the extra traps from the wrapper program,
- don't have to do step over. */
- if (proc->tdesc == NULL)
- return false;
-
- /* LWPs which will not be resumed are not interesting, because we
- might not wait for them next time through linux_wait. */
-
- if (!lwp->stopped)
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? Ignoring, not stopped\n",
- lwpid_of (thread));
- return false;
- }
-
- if (thread->last_resume_kind == resume_stop)
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? Ignoring, should remain"
- " stopped\n",
- lwpid_of (thread));
- return false;
- }
-
- gdb_assert (lwp->suspended >= 0);
-
- if (lwp->suspended)
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? Ignoring, suspended\n",
- lwpid_of (thread));
- return false;
- }
-
- if (lwp->status_pending_p)
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? Ignoring, has pending"
- " status.\n",
- lwpid_of (thread));
- return false;
- }
-
- /* Note: PC, not STOP_PC. Either GDB has adjusted the PC already,
- or we have. */
- pc = get_pc (lwp);
-
- /* If the PC has changed since we stopped, then don't do anything,
- and let the breakpoint/tracepoint be hit. This happens if, for
- instance, GDB handled the decr_pc_after_break subtraction itself,
- GDB is OOL stepping this thread, or the user has issued a "jump"
- command, or poked thread's registers herself. */
- if (pc != lwp->stop_pc)
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? Cancelling, PC was changed. "
- "Old stop_pc was 0x%s, PC is now 0x%s\n",
- lwpid_of (thread),
- paddress (lwp->stop_pc), paddress (pc));
- return false;
- }
-
- /* On software single step target, resume the inferior with signal
- rather than stepping over. */
- if (can_software_single_step ()
- && lwp->pending_signals != NULL
- && lwp_signal_can_be_delivered (lwp))
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? Ignoring, has pending"
- " signals.\n",
- lwpid_of (thread));
-
- return false;
- }
-
- saved_thread = current_thread;
- current_thread = thread;
-
- /* We can only step over breakpoints we know about. */
- if (breakpoint_here (pc) || fast_tracepoint_jump_here (pc))
- {
- /* Don't step over a breakpoint that GDB expects to hit
- though. If the condition is being evaluated on the target's side
- and it evaluate to false, step over this breakpoint as well. */
- if (gdb_breakpoint_here (pc)
- && gdb_condition_true_at_breakpoint (pc)
- && gdb_no_commands_at_breakpoint (pc))
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? yes, but found"
- " GDB breakpoint at 0x%s; skipping step over\n",
- lwpid_of (thread), paddress (pc));
-
- current_thread = saved_thread;
- return false;
- }
- else
- {
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? yes, "
- "found breakpoint at 0x%s\n",
- lwpid_of (thread), paddress (pc));
-
- /* We've found an lwp that needs stepping over --- return 1 so
- that find_thread stops looking. */
- current_thread = saved_thread;
-
- return true;
- }
- }
-
- current_thread = saved_thread;
-
- if (debug_threads)
- debug_printf ("Need step over [LWP %ld]? No, no breakpoint found"
- " at 0x%s\n",
- lwpid_of (thread), paddress (pc));
-
- return false;
-}
-
-/* Start a step-over operation on LWP. When LWP stopped at a
- breakpoint, to make progress, we need to remove the breakpoint out
- of the way. If we let other threads run while we do that, they may
- pass by the breakpoint location and miss hitting it. To avoid
- that, a step-over momentarily stops all threads while LWP is
- single-stepped by either hardware or software while the breakpoint
- is temporarily uninserted from the inferior. When the single-step
- finishes, we reinsert the breakpoint, and let all threads that are
- supposed to be running, run again. */
-
-static int
-start_step_over (struct lwp_info *lwp)
-{
- struct thread_info *thread = get_lwp_thread (lwp);
- struct thread_info *saved_thread;
- CORE_ADDR pc;
- int step;
-
- if (debug_threads)
- debug_printf ("Starting step-over on LWP %ld. Stopping all threads\n",
- lwpid_of (thread));
-
- stop_all_lwps (1, lwp);
-
- if (lwp->suspended != 0)
- {
- internal_error (__FILE__, __LINE__,
- "LWP %ld suspended=%d\n", lwpid_of (thread),
- lwp->suspended);
- }
-
- if (debug_threads)
- debug_printf ("Done stopping all threads for step-over.\n");
-
- /* Note, we should always reach here with an already adjusted PC,
- either by GDB (if we're resuming due to GDB's request), or by our
- caller, if we just finished handling an internal breakpoint GDB
- shouldn't care about. */
- pc = get_pc (lwp);
-
- saved_thread = current_thread;
- current_thread = thread;
-
- lwp->bp_reinsert = pc;
- uninsert_breakpoints_at (pc);
- uninsert_fast_tracepoint_jumps_at (pc);
-
- step = single_step (lwp);
-
- current_thread = saved_thread;
-
- linux_resume_one_lwp (lwp, step, 0, NULL);
-
- /* Require next event from this LWP. */
- step_over_bkpt = thread->id;
- return 1;
-}
-
-/* Finish a step-over. Reinsert the breakpoint we had uninserted in
- start_step_over, if still there, and delete any single-step
- breakpoints we've set, on non hardware single-step targets. */
-
-static int
-finish_step_over (struct lwp_info *lwp)
-{
- if (lwp->bp_reinsert != 0)
- {
- struct thread_info *saved_thread = current_thread;
-
- if (debug_threads)
- debug_printf ("Finished step over.\n");
-
- current_thread = get_lwp_thread (lwp);
-
- /* Reinsert any breakpoint at LWP->BP_REINSERT. Note that there
- may be no breakpoint to reinsert there by now. */
- reinsert_breakpoints_at (lwp->bp_reinsert);
- reinsert_fast_tracepoint_jumps_at (lwp->bp_reinsert);
-
- lwp->bp_reinsert = 0;
-
- /* Delete any single-step breakpoints. No longer needed. We
- don't have to worry about other threads hitting this trap,
- and later not being able to explain it, because we were
- stepping over a breakpoint, and we hold all threads but
- LWP stopped while doing that. */
- if (!can_hardware_single_step ())
- {
- gdb_assert (has_single_step_breakpoints (current_thread));
- delete_single_step_breakpoints (current_thread);
- }
-
- step_over_bkpt = null_ptid;
- current_thread = saved_thread;
- return 1;
- }
- else
- return 0;
-}
-
-/* If there's a step over in progress, wait until all threads stop
- (that is, until the stepping thread finishes its step), and
- unsuspend all lwps. The stepping thread ends with its status
- pending, which is processed later when we get back to processing
- events. */
-
-static void
-complete_ongoing_step_over (void)
-{
- if (step_over_bkpt != null_ptid)
- {
- struct lwp_info *lwp;
- int wstat;
- int ret;
-
- if (debug_threads)
- debug_printf ("detach: step over in progress, finish it first\n");
-
- /* Passing NULL_PTID as filter indicates we want all events to
- be left pending. Eventually this returns when there are no
- unwaited-for children left. */
- ret = linux_wait_for_event_filtered (minus_one_ptid, null_ptid,
- &wstat, __WALL);
- gdb_assert (ret == -1);
-
- lwp = find_lwp_pid (step_over_bkpt);
- if (lwp != NULL)
- finish_step_over (lwp);
- step_over_bkpt = null_ptid;
- unsuspend_all_lwps (lwp);
- }
-}
-
-/* This function is called once per thread. We check the thread's resume
- request, which will tell us whether to resume, step, or leave the thread
- stopped; and what signal, if any, it should be sent.
-
- For threads which we aren't explicitly told otherwise, we preserve
- the stepping flag; this is used for stepping over gdbserver-placed
- breakpoints.
-
- If pending_flags was set in any thread, we queue any needed
- signals, since we won't actually resume. We already have a pending
- event to report, so we don't need to preserve any step requests;
- they should be re-issued if necessary. */
-
-static void
-linux_resume_one_thread (thread_info *thread, bool leave_all_stopped)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
- int leave_pending;
-
- if (lwp->resume == NULL)
- return;
-
- if (lwp->resume->kind == resume_stop)
- {
- if (debug_threads)
- debug_printf ("resume_stop request for LWP %ld\n", lwpid_of (thread));
-
- if (!lwp->stopped)
- {
- if (debug_threads)
- debug_printf ("stopping LWP %ld\n", lwpid_of (thread));
-
- /* Stop the thread, and wait for the event asynchronously,
- through the event loop. */
- send_sigstop (lwp);
- }
- else
- {
- if (debug_threads)
- debug_printf ("already stopped LWP %ld\n",
- lwpid_of (thread));
-
- /* The LWP may have been stopped in an internal event that
- was not meant to be notified back to GDB (e.g., gdbserver
- breakpoint), so we should be reporting a stop event in
- this case too. */
-
- /* If the thread already has a pending SIGSTOP, this is a
- no-op. Otherwise, something later will presumably resume
- the thread and this will cause it to cancel any pending
- operation, due to last_resume_kind == resume_stop. If
- the thread already has a pending status to report, we
- will still report it the next time we wait - see
- status_pending_p_callback. */
-
- /* If we already have a pending signal to report, then
- there's no need to queue a SIGSTOP, as this means we're
- midway through moving the LWP out of the jumppad, and we
- will report the pending signal as soon as that is
- finished. */
- if (lwp->pending_signals_to_report == NULL)
- send_sigstop (lwp);
- }
-
- /* For stop requests, we're done. */
- lwp->resume = NULL;
- thread->last_status.kind = TARGET_WAITKIND_IGNORE;
- return;
- }
-
- /* If this thread which is about to be resumed has a pending status,
- then don't resume it - we can just report the pending status.
- Likewise if it is suspended, because e.g., another thread is
- stepping past a breakpoint. Make sure to queue any signals that
- would otherwise be sent. In all-stop mode, we do this decision
- based on if *any* thread has a pending status. If there's a
- thread that needs the step-over-breakpoint dance, then don't
- resume any other thread but that particular one. */
- leave_pending = (lwp->suspended
- || lwp->status_pending_p
- || leave_all_stopped);
-
- /* If we have a new signal, enqueue the signal. */
- if (lwp->resume->sig != 0)
- {
- siginfo_t info, *info_p;
-
- /* If this is the same signal we were previously stopped by,
- make sure to queue its siginfo. */
- if (WIFSTOPPED (lwp->last_status)
- && WSTOPSIG (lwp->last_status) == lwp->resume->sig
- && ptrace (PTRACE_GETSIGINFO, lwpid_of (thread),
- (PTRACE_TYPE_ARG3) 0, &info) == 0)
- info_p = &info;
- else
- info_p = NULL;
-
- enqueue_pending_signal (lwp, lwp->resume->sig, info_p);
- }
-
- if (!leave_pending)
- {
- if (debug_threads)
- debug_printf ("resuming LWP %ld\n", lwpid_of (thread));
-
- proceed_one_lwp (thread, NULL);
- }
- else
- {
- if (debug_threads)
- debug_printf ("leaving LWP %ld stopped\n", lwpid_of (thread));
- }
-
- thread->last_status.kind = TARGET_WAITKIND_IGNORE;
- lwp->resume = NULL;
-}
-
-static void
-linux_resume (struct thread_resume *resume_info, size_t n)
-{
- struct thread_info *need_step_over = NULL;
-
- if (debug_threads)
- {
- debug_enter ();
- debug_printf ("linux_resume:\n");
- }
-
- for_each_thread ([&] (thread_info *thread)
- {
- linux_set_resume_request (thread, resume_info, n);
- });
-
- /* If there is a thread which would otherwise be resumed, which has
- a pending status, then don't resume any threads - we can just
- report the pending status. Make sure to queue any signals that
- would otherwise be sent. In non-stop mode, we'll apply this
- logic to each thread individually. We consume all pending events
- before considering to start a step-over (in all-stop). */
- bool any_pending = false;
- if (!non_stop)
- any_pending = find_thread (resume_status_pending_p) != NULL;
-
- /* If there is a thread which would otherwise be resumed, which is
- stopped at a breakpoint that needs stepping over, then don't
- resume any threads - have it step over the breakpoint with all
- other threads stopped, then resume all threads again. Make sure
- to queue any signals that would otherwise be delivered or
- queued. */
- if (!any_pending && supports_breakpoints ())
- need_step_over = find_thread (need_step_over_p);
-
- bool leave_all_stopped = (need_step_over != NULL || any_pending);
-
- if (debug_threads)
- {
- if (need_step_over != NULL)
- debug_printf ("Not resuming all, need step over\n");
- else if (any_pending)
- debug_printf ("Not resuming, all-stop and found "
- "an LWP with pending status\n");
- else
- debug_printf ("Resuming, no pending status or step over needed\n");
- }
-
- /* Even if we're leaving threads stopped, queue all signals we'd
- otherwise deliver. */
- for_each_thread ([&] (thread_info *thread)
- {
- linux_resume_one_thread (thread, leave_all_stopped);
- });
-
- if (need_step_over)
- start_step_over (get_thread_lwp (need_step_over));
-
- if (debug_threads)
- {
- debug_printf ("linux_resume done\n");
- debug_exit ();
- }
-
- /* We may have events that were pending that can/should be sent to
- the client now. Trigger a linux_wait call. */
- if (target_is_async_p ())
- async_file_mark ();
-}
-
-/* This function is called once per thread. We check the thread's
- last resume request, which will tell us whether to resume, step, or
- leave the thread stopped. Any signal the client requested to be
- delivered has already been enqueued at this point.
-
- If any thread that GDB wants running is stopped at an internal
- breakpoint that needs stepping over, we start a step-over operation
- on that particular thread, and leave all others stopped. */
-
-static void
-proceed_one_lwp (thread_info *thread, lwp_info *except)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
- int step;
-
- if (lwp == except)
- return;
-
- if (debug_threads)
- debug_printf ("proceed_one_lwp: lwp %ld\n", lwpid_of (thread));
-
- if (!lwp->stopped)
- {
- if (debug_threads)
- debug_printf (" LWP %ld already running\n", lwpid_of (thread));
- return;
- }
-
- if (thread->last_resume_kind == resume_stop
- && thread->last_status.kind != TARGET_WAITKIND_IGNORE)
- {
- if (debug_threads)
- debug_printf (" client wants LWP to remain %ld stopped\n",
- lwpid_of (thread));
- return;
- }
-
- if (lwp->status_pending_p)
- {
- if (debug_threads)
- debug_printf (" LWP %ld has pending status, leaving stopped\n",
- lwpid_of (thread));
- return;
- }
-
- gdb_assert (lwp->suspended >= 0);
-
- if (lwp->suspended)
- {
- if (debug_threads)
- debug_printf (" LWP %ld is suspended\n", lwpid_of (thread));
- return;
- }
-
- if (thread->last_resume_kind == resume_stop
- && lwp->pending_signals_to_report == NULL
- && (lwp->collecting_fast_tracepoint
- == fast_tpoint_collect_result::not_collecting))
- {
- /* We haven't reported this LWP as stopped yet (otherwise, the
- last_status.kind check above would catch it, and we wouldn't
- reach here. This LWP may have been momentarily paused by a
- stop_all_lwps call while handling for example, another LWP's
- step-over. In that case, the pending expected SIGSTOP signal
- that was queued at vCont;t handling time will have already
- been consumed by wait_for_sigstop, and so we need to requeue
- another one here. Note that if the LWP already has a SIGSTOP
- pending, this is a no-op. */
-
- if (debug_threads)
- debug_printf ("Client wants LWP %ld to stop. "
- "Making sure it has a SIGSTOP pending\n",
- lwpid_of (thread));
-
- send_sigstop (lwp);
- }
-
- if (thread->last_resume_kind == resume_step)
- {
- if (debug_threads)
- debug_printf (" stepping LWP %ld, client wants it stepping\n",
- lwpid_of (thread));
-
- /* If resume_step is requested by GDB, install single-step
- breakpoints when the thread is about to be actually resumed if
- the single-step breakpoints weren't removed. */
- if (can_software_single_step ()
- && !has_single_step_breakpoints (thread))
- install_software_single_step_breakpoints (lwp);
-
- step = maybe_hw_step (thread);
- }
- else if (lwp->bp_reinsert != 0)
- {
- if (debug_threads)
- debug_printf (" stepping LWP %ld, reinsert set\n",
- lwpid_of (thread));
-
- step = maybe_hw_step (thread);
- }
- else
- step = 0;
-
- linux_resume_one_lwp (lwp, step, 0, NULL);
-}
-
-static void
-unsuspend_and_proceed_one_lwp (thread_info *thread, lwp_info *except)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- if (lwp == except)
- return;
-
- lwp_suspended_decr (lwp);
-
- proceed_one_lwp (thread, except);
-}
-
-/* When we finish a step-over, set threads running again. If there's
- another thread that may need a step-over, now's the time to start
- it. Eventually, we'll move all threads past their breakpoints. */
-
-static void
-proceed_all_lwps (void)
-{
- struct thread_info *need_step_over;
-
- /* If there is a thread which would otherwise be resumed, which is
- stopped at a breakpoint that needs stepping over, then don't
- resume any threads - have it step over the breakpoint with all
- other threads stopped, then resume all threads again. */
-
- if (supports_breakpoints ())
- {
- need_step_over = find_thread (need_step_over_p);
-
- if (need_step_over != NULL)
- {
- if (debug_threads)
- debug_printf ("proceed_all_lwps: found "
- "thread %ld needing a step-over\n",
- lwpid_of (need_step_over));
-
- start_step_over (get_thread_lwp (need_step_over));
- return;
- }
- }
-
- if (debug_threads)
- debug_printf ("Proceeding, no step-over needed\n");
-
- for_each_thread ([] (thread_info *thread)
- {
- proceed_one_lwp (thread, NULL);
- });
-}
-
-/* Stopped LWPs that the client wanted to be running, that don't have
- pending statuses, are set to run again, except for EXCEPT, if not
- NULL. This undoes a stop_all_lwps call. */
-
-static void
-unstop_all_lwps (int unsuspend, struct lwp_info *except)
-{
- if (debug_threads)
- {
- debug_enter ();
- if (except)
- debug_printf ("unstopping all lwps, except=(LWP %ld)\n",
- lwpid_of (get_lwp_thread (except)));
- else
- debug_printf ("unstopping all lwps\n");
- }
-
- if (unsuspend)
- for_each_thread ([&] (thread_info *thread)
- {
- unsuspend_and_proceed_one_lwp (thread, except);
- });
- else
- for_each_thread ([&] (thread_info *thread)
- {
- proceed_one_lwp (thread, except);
- });
-
- if (debug_threads)
- {
- debug_printf ("unstop_all_lwps done\n");
- debug_exit ();
- }
-}
-
-
-#ifdef HAVE_LINUX_REGSETS
-
-#define use_linux_regsets 1
-
-/* Returns true if REGSET has been disabled. */
-
-static int
-regset_disabled (struct regsets_info *info, struct regset_info *regset)
-{
- return (info->disabled_regsets != NULL
- && info->disabled_regsets[regset - info->regsets]);
-}
-
-/* Disable REGSET. */
-
-static void
-disable_regset (struct regsets_info *info, struct regset_info *regset)
-{
- int dr_offset;
-
- dr_offset = regset - info->regsets;
- if (info->disabled_regsets == NULL)
- info->disabled_regsets = (char *) xcalloc (1, info->num_regsets);
- info->disabled_regsets[dr_offset] = 1;
-}
-
-static int
-regsets_fetch_inferior_registers (struct regsets_info *regsets_info,
- struct regcache *regcache)
-{
- struct regset_info *regset;
- int saw_general_regs = 0;
- int pid;
- struct iovec iov;
-
- pid = lwpid_of (current_thread);
- for (regset = regsets_info->regsets; regset->size >= 0; regset++)
- {
- void *buf, *data;
- int nt_type, res;
-
- if (regset->size == 0 || regset_disabled (regsets_info, regset))
- continue;
-
- buf = xmalloc (regset->size);
-
- nt_type = regset->nt_type;
- if (nt_type)
- {
- iov.iov_base = buf;
- iov.iov_len = regset->size;
- data = (void *) &iov;
- }
- else
- data = buf;
-
-#ifndef __sparc__
- res = ptrace (regset->get_request, pid,
- (PTRACE_TYPE_ARG3) (long) nt_type, data);
-#else
- res = ptrace (regset->get_request, pid, data, nt_type);
-#endif
- if (res < 0)
- {
- if (errno == EIO
- || (errno == EINVAL && regset->type == OPTIONAL_REGS))
- {
- /* If we get EIO on a regset, or an EINVAL and the regset is
- optional, do not try it again for this process mode. */
- disable_regset (regsets_info, regset);
- }
- else if (errno == ENODATA)
- {
- /* ENODATA may be returned if the regset is currently
- not "active". This can happen in normal operation,
- so suppress the warning in this case. */
- }
- else if (errno == ESRCH)
- {
- /* At this point, ESRCH should mean the process is
- already gone, in which case we simply ignore attempts
- to read its registers. */
- }
- else
- {
- char s[256];
- sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%d",
- pid);
- perror (s);
- }
- }
- else
- {
- if (regset->type == GENERAL_REGS)
- saw_general_regs = 1;
- regset->store_function (regcache, buf);
- }
- free (buf);
- }
- if (saw_general_regs)
- return 0;
- else
- return 1;
-}
-
-static int
-regsets_store_inferior_registers (struct regsets_info *regsets_info,
- struct regcache *regcache)
-{
- struct regset_info *regset;
- int saw_general_regs = 0;
- int pid;
- struct iovec iov;
-
- pid = lwpid_of (current_thread);
- for (regset = regsets_info->regsets; regset->size >= 0; regset++)
- {
- void *buf, *data;
- int nt_type, res;
-
- if (regset->size == 0 || regset_disabled (regsets_info, regset)
- || regset->fill_function == NULL)
- continue;
-
- buf = xmalloc (regset->size);
-
- /* First fill the buffer with the current register set contents,
- in case there are any items in the kernel's regset that are
- not in gdbserver's regcache. */
-
- nt_type = regset->nt_type;
- if (nt_type)
- {
- iov.iov_base = buf;
- iov.iov_len = regset->size;
- data = (void *) &iov;
- }
- else
- data = buf;
-
-#ifndef __sparc__
- res = ptrace (regset->get_request, pid,
- (PTRACE_TYPE_ARG3) (long) nt_type, data);
-#else
- res = ptrace (regset->get_request, pid, data, nt_type);
-#endif
-
- if (res == 0)
- {
- /* Then overlay our cached registers on that. */
- regset->fill_function (regcache, buf);
-
- /* Only now do we write the register set. */
-#ifndef __sparc__
- res = ptrace (regset->set_request, pid,
- (PTRACE_TYPE_ARG3) (long) nt_type, data);
-#else
- res = ptrace (regset->set_request, pid, data, nt_type);
-#endif
- }
-
- if (res < 0)
- {
- if (errno == EIO
- || (errno == EINVAL && regset->type == OPTIONAL_REGS))
- {
- /* If we get EIO on a regset, or an EINVAL and the regset is
- optional, do not try it again for this process mode. */
- disable_regset (regsets_info, regset);
- }
- else if (errno == ESRCH)
- {
- /* At this point, ESRCH should mean the process is
- already gone, in which case we simply ignore attempts
- to change its registers. See also the related
- comment in linux_resume_one_lwp. */
- free (buf);
- return 0;
- }
- else
- {
- perror ("Warning: ptrace(regsets_store_inferior_registers)");
- }
- }
- else if (regset->type == GENERAL_REGS)
- saw_general_regs = 1;
- free (buf);
- }
- if (saw_general_regs)
- return 0;
- else
- return 1;
-}
-
-#else /* !HAVE_LINUX_REGSETS */
-
-#define use_linux_regsets 0
-#define regsets_fetch_inferior_registers(regsets_info, regcache) 1
-#define regsets_store_inferior_registers(regsets_info, regcache) 1
-
-#endif
-
-/* Return 1 if register REGNO is supported by one of the regset ptrace
- calls or 0 if it has to be transferred individually. */
-
-static int
-linux_register_in_regsets (const struct regs_info *regs_info, int regno)
-{
- unsigned char mask = 1 << (regno % 8);
- size_t index = regno / 8;
-
- return (use_linux_regsets
- && (regs_info->regset_bitmap == NULL
- || (regs_info->regset_bitmap[index] & mask) != 0));
-}
-
-#ifdef HAVE_LINUX_USRREGS
-
-static int
-register_addr (const struct usrregs_info *usrregs, int regnum)
-{
- int addr;
-
- if (regnum < 0 || regnum >= usrregs->num_regs)
- error ("Invalid register number %d.", regnum);
-
- addr = usrregs->regmap[regnum];
-
- return addr;
-}
-
-/* Fetch one register. */
-static void
-fetch_register (const struct usrregs_info *usrregs,
- struct regcache *regcache, int regno)
-{
- CORE_ADDR regaddr;
- int i, size;
- char *buf;
- int pid;
-
- if (regno >= usrregs->num_regs)
- return;
- if ((*the_low_target.cannot_fetch_register) (regno))
- return;
-
- regaddr = register_addr (usrregs, regno);
- if (regaddr == -1)
- return;
-
- size = ((register_size (regcache->tdesc, regno)
- + sizeof (PTRACE_XFER_TYPE) - 1)
- & -sizeof (PTRACE_XFER_TYPE));
- buf = (char *) alloca (size);
-
- pid = lwpid_of (current_thread);
- for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
- {
- errno = 0;
- *(PTRACE_XFER_TYPE *) (buf + i) =
- ptrace (PTRACE_PEEKUSER, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- of coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_TYPE_ARG3) (uintptr_t) regaddr, (PTRACE_TYPE_ARG4) 0);
- regaddr += sizeof (PTRACE_XFER_TYPE);
- if (errno != 0)
- {
- /* Mark register REGNO unavailable. */
- supply_register (regcache, regno, NULL);
- return;
- }
- }
-
- if (the_low_target.supply_ptrace_register)
- the_low_target.supply_ptrace_register (regcache, regno, buf);
- else
- supply_register (regcache, regno, buf);
-}
-
-/* Store one register. */
-static void
-store_register (const struct usrregs_info *usrregs,
- struct regcache *regcache, int regno)
-{
- CORE_ADDR regaddr;
- int i, size;
- char *buf;
- int pid;
-
- if (regno >= usrregs->num_regs)
- return;
- if ((*the_low_target.cannot_store_register) (regno))
- return;
-
- regaddr = register_addr (usrregs, regno);
- if (regaddr == -1)
- return;
-
- size = ((register_size (regcache->tdesc, regno)
- + sizeof (PTRACE_XFER_TYPE) - 1)
- & -sizeof (PTRACE_XFER_TYPE));
- buf = (char *) alloca (size);
- memset (buf, 0, size);
-
- if (the_low_target.collect_ptrace_register)
- the_low_target.collect_ptrace_register (regcache, regno, buf);
- else
- collect_register (regcache, regno, buf);
-
- pid = lwpid_of (current_thread);
- for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
- {
- errno = 0;
- ptrace (PTRACE_POKEUSER, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_TYPE_ARG3) (uintptr_t) regaddr,
- (PTRACE_TYPE_ARG4) *(PTRACE_XFER_TYPE *) (buf + i));
- if (errno != 0)
- {
- /* At this point, ESRCH should mean the process is
- already gone, in which case we simply ignore attempts
- to change its registers. See also the related
- comment in linux_resume_one_lwp. */
- if (errno == ESRCH)
- return;
-
- if ((*the_low_target.cannot_store_register) (regno) == 0)
- error ("writing register %d: %s", regno, safe_strerror (errno));
- }
- regaddr += sizeof (PTRACE_XFER_TYPE);
- }
-}
-
-/* Fetch all registers, or just one, from the child process.
- If REGNO is -1, do this for all registers, skipping any that are
- assumed to have been retrieved by regsets_fetch_inferior_registers,
- unless ALL is non-zero.
- Otherwise, REGNO specifies which register (so we can save time). */
-static void
-usr_fetch_inferior_registers (const struct regs_info *regs_info,
- struct regcache *regcache, int regno, int all)
-{
- struct usrregs_info *usr = regs_info->usrregs;
-
- if (regno == -1)
- {
- for (regno = 0; regno < usr->num_regs; regno++)
- if (all || !linux_register_in_regsets (regs_info, regno))
- fetch_register (usr, regcache, regno);
- }
- else
- fetch_register (usr, regcache, regno);
-}
-
-/* Store our register values back into the inferior.
- If REGNO is -1, do this for all registers, skipping any that are
- assumed to have been saved by regsets_store_inferior_registers,
- unless ALL is non-zero.
- Otherwise, REGNO specifies which register (so we can save time). */
-static void
-usr_store_inferior_registers (const struct regs_info *regs_info,
- struct regcache *regcache, int regno, int all)
-{
- struct usrregs_info *usr = regs_info->usrregs;
-
- if (regno == -1)
- {
- for (regno = 0; regno < usr->num_regs; regno++)
- if (all || !linux_register_in_regsets (regs_info, regno))
- store_register (usr, regcache, regno);
- }
- else
- store_register (usr, regcache, regno);
-}
-
-#else /* !HAVE_LINUX_USRREGS */
-
-#define usr_fetch_inferior_registers(regs_info, regcache, regno, all) do {} while (0)
-#define usr_store_inferior_registers(regs_info, regcache, regno, all) do {} while (0)
-
-#endif
-
-
-static void
-linux_fetch_registers (struct regcache *regcache, int regno)
-{
- int use_regsets;
- int all = 0;
- const struct regs_info *regs_info = (*the_low_target.regs_info) ();
-
- if (regno == -1)
- {
- if (the_low_target.fetch_register != NULL
- && regs_info->usrregs != NULL)
- for (regno = 0; regno < regs_info->usrregs->num_regs; regno++)
- (*the_low_target.fetch_register) (regcache, regno);
-
- all = regsets_fetch_inferior_registers (regs_info->regsets_info, regcache);
- if (regs_info->usrregs != NULL)
- usr_fetch_inferior_registers (regs_info, regcache, -1, all);
- }
- else
- {
- if (the_low_target.fetch_register != NULL
- && (*the_low_target.fetch_register) (regcache, regno))
- return;
-
- use_regsets = linux_register_in_regsets (regs_info, regno);
- if (use_regsets)
- all = regsets_fetch_inferior_registers (regs_info->regsets_info,
- regcache);
- if ((!use_regsets || all) && regs_info->usrregs != NULL)
- usr_fetch_inferior_registers (regs_info, regcache, regno, 1);
- }
-}
-
-static void
-linux_store_registers (struct regcache *regcache, int regno)
-{
- int use_regsets;
- int all = 0;
- const struct regs_info *regs_info = (*the_low_target.regs_info) ();
-
- if (regno == -1)
- {
- all = regsets_store_inferior_registers (regs_info->regsets_info,
- regcache);
- if (regs_info->usrregs != NULL)
- usr_store_inferior_registers (regs_info, regcache, regno, all);
- }
- else
- {
- use_regsets = linux_register_in_regsets (regs_info, regno);
- if (use_regsets)
- all = regsets_store_inferior_registers (regs_info->regsets_info,
- regcache);
- if ((!use_regsets || all) && regs_info->usrregs != NULL)
- usr_store_inferior_registers (regs_info, regcache, regno, 1);
- }
-}
-
-
-/* Copy LEN bytes from inferior's memory starting at MEMADDR
- to debugger memory starting at MYADDR. */
-
-static int
-linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- int pid = lwpid_of (current_thread);
- PTRACE_XFER_TYPE *buffer;
- CORE_ADDR addr;
- int count;
- char filename[64];
- int i;
- int ret;
- int fd;
-
- /* Try using /proc. Don't bother for one word. */
- if (len >= 3 * sizeof (long))
- {
- int bytes;
-
- /* We could keep this file open and cache it - possibly one per
- thread. That requires some juggling, but is even faster. */
- sprintf (filename, "/proc/%d/mem", pid);
- fd = open (filename, O_RDONLY | O_LARGEFILE);
- if (fd == -1)
- goto no_proc;
-
- /* If pread64 is available, use it. It's faster if the kernel
- supports it (only one syscall), and it's 64-bit safe even on
- 32-bit platforms (for instance, SPARC debugging a SPARC64
- application). */
-#ifdef HAVE_PREAD64
- bytes = pread64 (fd, myaddr, len, memaddr);
-#else
- bytes = -1;
- if (lseek (fd, memaddr, SEEK_SET) != -1)
- bytes = read (fd, myaddr, len);
-#endif
-
- close (fd);
- if (bytes == len)
- return 0;
-
- /* Some data was read, we'll try to get the rest with ptrace. */
- if (bytes > 0)
- {
- memaddr += bytes;
- myaddr += bytes;
- len -= bytes;
- }
- }
-
- no_proc:
- /* Round starting address down to longword boundary. */
- addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
- /* Round ending address up; get number of longwords that makes. */
- count = ((((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
- / sizeof (PTRACE_XFER_TYPE));
- /* Allocate buffer of that many longwords. */
- buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
-
- /* Read all the longwords */
- errno = 0;
- for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
- {
- /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- buffer[i] = ptrace (PTRACE_PEEKTEXT, pid,
- (PTRACE_TYPE_ARG3) (uintptr_t) addr,
- (PTRACE_TYPE_ARG4) 0);
- if (errno)
- break;
- }
- ret = errno;
-
- /* Copy appropriate bytes out of the buffer. */
- if (i > 0)
- {
- i *= sizeof (PTRACE_XFER_TYPE);
- i -= memaddr & (sizeof (PTRACE_XFER_TYPE) - 1);
- memcpy (myaddr,
- (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
- i < len ? i : len);
- }
-
- return ret;
-}
-
-/* Copy LEN bytes of data from debugger memory at MYADDR to inferior's
- memory at MEMADDR. On failure (cannot write to the inferior)
- returns the value of errno. Always succeeds if LEN is zero. */
-
-static int
-linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
-{
- int i;
- /* Round starting address down to longword boundary. */
- CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
- /* Round ending address up; get number of longwords that makes. */
- int count
- = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
- / sizeof (PTRACE_XFER_TYPE);
-
- /* Allocate buffer of that many longwords. */
- PTRACE_XFER_TYPE *buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
-
- int pid = lwpid_of (current_thread);
-
- if (len == 0)
- {
- /* Zero length write always succeeds. */
- return 0;
- }
-
- if (debug_threads)
- {
- /* Dump up to four bytes. */
- char str[4 * 2 + 1];
- char *p = str;
- int dump = len < 4 ? len : 4;
-
- for (i = 0; i < dump; i++)
- {
- sprintf (p, "%02x", myaddr[i]);
- p += 2;
- }
- *p = '\0';
-
- debug_printf ("Writing %s to 0x%08lx in process %d\n",
- str, (long) memaddr, pid);
- }
-
- /* Fill start and end extra bytes of buffer with existing memory data. */
-
- errno = 0;
- /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- buffer[0] = ptrace (PTRACE_PEEKTEXT, pid,
- (PTRACE_TYPE_ARG3) (uintptr_t) addr,
- (PTRACE_TYPE_ARG4) 0);
- if (errno)
- return errno;
-
- if (count > 1)
- {
- errno = 0;
- buffer[count - 1]
- = ptrace (PTRACE_PEEKTEXT, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_TYPE_ARG3) (uintptr_t) (addr + (count - 1)
- * sizeof (PTRACE_XFER_TYPE)),
- (PTRACE_TYPE_ARG4) 0);
- if (errno)
- return errno;
- }
-
- /* Copy data to be written over corresponding part of buffer. */
-
- memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
- myaddr, len);
-
- /* Write the entire buffer. */
-
- for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
- {
- errno = 0;
- ptrace (PTRACE_POKETEXT, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_TYPE_ARG3) (uintptr_t) addr,
- (PTRACE_TYPE_ARG4) buffer[i]);
- if (errno)
- return errno;
- }
-
- return 0;
-}
-
-static void
-linux_look_up_symbols (void)
-{
-#ifdef USE_THREAD_DB
- struct process_info *proc = current_process ();
-
- if (proc->priv->thread_db != NULL)
- return;
-
- thread_db_init ();
-#endif
-}
-
-static void
-linux_request_interrupt (void)
-{
- /* Send a SIGINT to the process group. This acts just like the user
- typed a ^C on the controlling terminal. */
- kill (-signal_pid, SIGINT);
-}
-
-/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
- to debugger memory starting at MYADDR. */
-
-static int
-linux_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
-{
- char filename[PATH_MAX];
- int fd, n;
- int pid = lwpid_of (current_thread);
-
- xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
-
- fd = open (filename, O_RDONLY);
- if (fd < 0)
- return -1;
-
- if (offset != (CORE_ADDR) 0
- && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
- n = -1;
- else
- n = read (fd, myaddr, len);
-
- close (fd);
-
- return n;
-}
-
-/* These breakpoint and watchpoint related wrapper functions simply
- pass on the function call if the target has registered a
- corresponding function. */
-
-static int
-linux_supports_z_point_type (char z_type)
-{
- return (the_low_target.supports_z_point_type != NULL
- && the_low_target.supports_z_point_type (z_type));
-}
-
-static int
-linux_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- if (type == raw_bkpt_type_sw)
- return insert_memory_breakpoint (bp);
- else if (the_low_target.insert_point != NULL)
- return the_low_target.insert_point (type, addr, size, bp);
- else
- /* Unsupported (see target.h). */
- return 1;
-}
-
-static int
-linux_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- if (type == raw_bkpt_type_sw)
- return remove_memory_breakpoint (bp);
- else if (the_low_target.remove_point != NULL)
- return the_low_target.remove_point (type, addr, size, bp);
- else
- /* Unsupported (see target.h). */
- return 1;
-}
-
-/* Implement the to_stopped_by_sw_breakpoint target_ops
- method. */
-
-static int
-linux_stopped_by_sw_breakpoint (void)
-{
- struct lwp_info *lwp = get_thread_lwp (current_thread);
-
- return (lwp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT);
-}
-
-/* Implement the to_supports_stopped_by_sw_breakpoint target_ops
- method. */
-
-static int
-linux_supports_stopped_by_sw_breakpoint (void)
-{
- return USE_SIGTRAP_SIGINFO;
-}
-
-/* Implement the to_stopped_by_hw_breakpoint target_ops
- method. */
-
-static int
-linux_stopped_by_hw_breakpoint (void)
-{
- struct lwp_info *lwp = get_thread_lwp (current_thread);
-
- return (lwp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT);
-}
-
-/* Implement the to_supports_stopped_by_hw_breakpoint target_ops
- method. */
-
-static int
-linux_supports_stopped_by_hw_breakpoint (void)
-{
- return USE_SIGTRAP_SIGINFO;
-}
-
-/* Implement the supports_hardware_single_step target_ops method. */
-
-static int
-linux_supports_hardware_single_step (void)
-{
- return can_hardware_single_step ();
-}
-
-static int
-linux_supports_software_single_step (void)
-{
- return can_software_single_step ();
-}
-
-static int
-linux_stopped_by_watchpoint (void)
-{
- struct lwp_info *lwp = get_thread_lwp (current_thread);
-
- return lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
-}
-
-static CORE_ADDR
-linux_stopped_data_address (void)
-{
- struct lwp_info *lwp = get_thread_lwp (current_thread);
-
- return lwp->stopped_data_address;
-}
-
-#if defined(__UCLIBC__) && defined(HAS_NOMMU) \
- && defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) \
- && defined(PT_TEXT_END_ADDR)
-
-/* This is only used for targets that define PT_TEXT_ADDR,
- PT_DATA_ADDR and PT_TEXT_END_ADDR. If those are not defined, supposedly
- the target has different ways of acquiring this information, like
- loadmaps. */
-
-/* Under uClinux, programs are loaded at non-zero offsets, which we need
- to tell gdb about. */
-
-static int
-linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
-{
- unsigned long text, text_end, data;
- int pid = lwpid_of (current_thread);
-
- errno = 0;
-
- text = ptrace (PTRACE_PEEKUSER, pid, (PTRACE_TYPE_ARG3) PT_TEXT_ADDR,
- (PTRACE_TYPE_ARG4) 0);
- text_end = ptrace (PTRACE_PEEKUSER, pid, (PTRACE_TYPE_ARG3) PT_TEXT_END_ADDR,
- (PTRACE_TYPE_ARG4) 0);
- data = ptrace (PTRACE_PEEKUSER, pid, (PTRACE_TYPE_ARG3) PT_DATA_ADDR,
- (PTRACE_TYPE_ARG4) 0);
-
- if (errno == 0)
- {
- /* Both text and data offsets produced at compile-time (and so
- used by gdb) are relative to the beginning of the program,
- with the data segment immediately following the text segment.
- However, the actual runtime layout in memory may put the data
- somewhere else, so when we send gdb a data base-address, we
- use the real data base address and subtract the compile-time
- data base-address from it (which is just the length of the
- text segment). BSS immediately follows data in both
- cases. */
- *text_p = text;
- *data_p = data - (text_end - text);
-
- return 1;
- }
- return 0;
-}
-#endif
-
-static int
-linux_qxfer_osdata (const char *annex,
- unsigned char *readbuf, unsigned const char *writebuf,
- CORE_ADDR offset, int len)
-{
- return linux_common_xfer_osdata (annex, readbuf, offset, len);
-}
-
-/* Convert a native/host siginfo object, into/from the siginfo in the
- layout of the inferiors' architecture. */
-
-static void
-siginfo_fixup (siginfo_t *siginfo, gdb_byte *inf_siginfo, int direction)
-{
- int done = 0;
-
- if (the_low_target.siginfo_fixup != NULL)
- done = the_low_target.siginfo_fixup (siginfo, inf_siginfo, direction);
-
- /* If there was no callback, or the callback didn't do anything,
- then just do a straight memcpy. */
- if (!done)
- {
- if (direction == 1)
- memcpy (siginfo, inf_siginfo, sizeof (siginfo_t));
- else
- memcpy (inf_siginfo, siginfo, sizeof (siginfo_t));
- }
-}
-
-static int
-linux_xfer_siginfo (const char *annex, unsigned char *readbuf,
- unsigned const char *writebuf, CORE_ADDR offset, int len)
-{
- int pid;
- siginfo_t siginfo;
- gdb_byte inf_siginfo[sizeof (siginfo_t)];
-
- if (current_thread == NULL)
- return -1;
-
- pid = lwpid_of (current_thread);
-
- if (debug_threads)
- debug_printf ("%s siginfo for lwp %d.\n",
- readbuf != NULL ? "Reading" : "Writing",
- pid);
-
- if (offset >= sizeof (siginfo))
- return -1;
-
- if (ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo) != 0)
- return -1;
-
- /* When GDBSERVER is built as a 64-bit application, ptrace writes into
- SIGINFO an object with 64-bit layout. Since debugging a 32-bit
- inferior with a 64-bit GDBSERVER should look the same as debugging it
- with a 32-bit GDBSERVER, we need to convert it. */
- siginfo_fixup (&siginfo, inf_siginfo, 0);
-
- if (offset + len > sizeof (siginfo))
- len = sizeof (siginfo) - offset;
-
- if (readbuf != NULL)
- memcpy (readbuf, inf_siginfo + offset, len);
- else
- {
- memcpy (inf_siginfo + offset, writebuf, len);
-
- /* Convert back to ptrace layout before flushing it out. */
- siginfo_fixup (&siginfo, inf_siginfo, 1);
-
- if (ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo) != 0)
- return -1;
- }
-
- return len;
-}
-
-/* SIGCHLD handler that serves two purposes: In non-stop/async mode,
- so we notice when children change state; as the handler for the
- sigsuspend in my_waitpid. */
-
-static void
-sigchld_handler (int signo)
-{
- int old_errno = errno;
-
- if (debug_threads)
- {
- do
- {
- /* Use the async signal safe debug function. */
- if (debug_write ("sigchld_handler\n",
- sizeof ("sigchld_handler\n") - 1) < 0)
- break; /* just ignore */
- } while (0);
- }
-
- if (target_is_async_p ())
- async_file_mark (); /* trigger a linux_wait */
-
- errno = old_errno;
-}
-
-static int
-linux_supports_non_stop (void)
-{
- return 1;
-}
-
-static int
-linux_async (int enable)
-{
- int previous = target_is_async_p ();
-
- if (debug_threads)
- debug_printf ("linux_async (%d), previous=%d\n",
- enable, previous);
-
- if (previous != enable)
- {
- sigset_t mask;
- sigemptyset (&mask);
- sigaddset (&mask, SIGCHLD);
-
- gdb_sigmask (SIG_BLOCK, &mask, NULL);
-
- if (enable)
- {
- if (pipe (linux_event_pipe) == -1)
- {
- linux_event_pipe[0] = -1;
- linux_event_pipe[1] = -1;
- gdb_sigmask (SIG_UNBLOCK, &mask, NULL);
-
- warning ("creating event pipe failed.");
- return previous;
- }
-
- fcntl (linux_event_pipe[0], F_SETFL, O_NONBLOCK);
- fcntl (linux_event_pipe[1], F_SETFL, O_NONBLOCK);
-
- /* Register the event loop handler. */
- add_file_handler (linux_event_pipe[0],
- handle_target_event, NULL);
-
- /* Always trigger a linux_wait. */
- async_file_mark ();
- }
- else
- {
- delete_file_handler (linux_event_pipe[0]);
-
- close (linux_event_pipe[0]);
- close (linux_event_pipe[1]);
- linux_event_pipe[0] = -1;
- linux_event_pipe[1] = -1;
- }
-
- gdb_sigmask (SIG_UNBLOCK, &mask, NULL);
- }
-
- return previous;
-}
-
-static int
-linux_start_non_stop (int nonstop)
-{
- /* Register or unregister from event-loop accordingly. */
- linux_async (nonstop);
-
- if (target_is_async_p () != (nonstop != 0))
- return -1;
-
- return 0;
-}
-
-static int
-linux_supports_multi_process (void)
-{
- return 1;
-}
-
-/* Check if fork events are supported. */
-
-static int
-linux_supports_fork_events (void)
-{
- return linux_supports_tracefork ();
-}
-
-/* Check if vfork events are supported. */
-
-static int
-linux_supports_vfork_events (void)
-{
- return linux_supports_tracefork ();
-}
-
-/* Check if exec events are supported. */
-
-static int
-linux_supports_exec_events (void)
-{
- return linux_supports_traceexec ();
-}
-
-/* Target hook for 'handle_new_gdb_connection'. Causes a reset of the
- ptrace flags for all inferiors. This is in case the new GDB connection
- doesn't support the same set of events that the previous one did. */
-
-static void
-linux_handle_new_gdb_connection (void)
-{
- /* Request that all the lwps reset their ptrace options. */
- for_each_thread ([] (thread_info *thread)
- {
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- if (!lwp->stopped)
- {
- /* Stop the lwp so we can modify its ptrace options. */
- lwp->must_set_ptrace_flags = 1;
- linux_stop_lwp (lwp);
- }
- else
- {
- /* Already stopped; go ahead and set the ptrace options. */
- struct process_info *proc = find_process_pid (pid_of (thread));
- int options = linux_low_ptrace_options (proc->attached);
-
- linux_enable_event_reporting (lwpid_of (thread), options);
- lwp->must_set_ptrace_flags = 0;
- }
- });
-}
-
-static int
-linux_supports_disable_randomization (void)
-{
-#ifdef HAVE_PERSONALITY
- return 1;
-#else
- return 0;
-#endif
-}
-
-static int
-linux_supports_agent (void)
-{
- return 1;
-}
-
-static int
-linux_supports_range_stepping (void)
-{
- if (can_software_single_step ())
- return 1;
- if (*the_low_target.supports_range_stepping == NULL)
- return 0;
-
- return (*the_low_target.supports_range_stepping) ();
-}
-
-#if defined PT_GETDSBT || defined PTRACE_GETFDPIC
-struct target_loadseg
-{
- /* Core address to which the segment is mapped. */
- Elf32_Addr addr;
- /* VMA recorded in the program header. */
- Elf32_Addr p_vaddr;
- /* Size of this segment in memory. */
- Elf32_Word p_memsz;
-};
-
-# if defined PT_GETDSBT
-struct target_loadmap
-{
- /* Protocol version number, must be zero. */
- Elf32_Word version;
- /* Pointer to the DSBT table, its size, and the DSBT index. */
- unsigned *dsbt_table;
- unsigned dsbt_size, dsbt_index;
- /* Number of segments in this map. */
- Elf32_Word nsegs;
- /* The actual memory map. */
- struct target_loadseg segs[/*nsegs*/];
-};
-# define LINUX_LOADMAP PT_GETDSBT
-# define LINUX_LOADMAP_EXEC PTRACE_GETDSBT_EXEC
-# define LINUX_LOADMAP_INTERP PTRACE_GETDSBT_INTERP
-# else
-struct target_loadmap
-{
- /* Protocol version number, must be zero. */
- Elf32_Half version;
- /* Number of segments in this map. */
- Elf32_Half nsegs;
- /* The actual memory map. */
- struct target_loadseg segs[/*nsegs*/];
-};
-# define LINUX_LOADMAP PTRACE_GETFDPIC
-# define LINUX_LOADMAP_EXEC PTRACE_GETFDPIC_EXEC
-# define LINUX_LOADMAP_INTERP PTRACE_GETFDPIC_INTERP
-# endif
-
-static int
-linux_read_loadmap (const char *annex, CORE_ADDR offset,
- unsigned char *myaddr, unsigned int len)
-{
- int pid = lwpid_of (current_thread);
- int addr = -1;
- struct target_loadmap *data = NULL;
- unsigned int actual_length, copy_length;
-
- if (strcmp (annex, "exec") == 0)
- addr = (int) LINUX_LOADMAP_EXEC;
- else if (strcmp (annex, "interp") == 0)
- addr = (int) LINUX_LOADMAP_INTERP;
- else
- return -1;
-
- if (ptrace (LINUX_LOADMAP, pid, addr, &data) != 0)
- return -1;
-
- if (data == NULL)
- return -1;
-
- actual_length = sizeof (struct target_loadmap)
- + sizeof (struct target_loadseg) * data->nsegs;
-
- if (offset < 0 || offset > actual_length)
- return -1;
-
- copy_length = actual_length - offset < len ? actual_length - offset : len;
- memcpy (myaddr, (char *) data + offset, copy_length);
- return copy_length;
-}
-#else
-# define linux_read_loadmap NULL
-#endif /* defined PT_GETDSBT || defined PTRACE_GETFDPIC */
-
-static void
-linux_process_qsupported (char **features, int count)
-{
- if (the_low_target.process_qsupported != NULL)
- the_low_target.process_qsupported (features, count);
-}
-
-static int
-linux_supports_catch_syscall (void)
-{
- return (the_low_target.get_syscall_trapinfo != NULL
- && linux_supports_tracesysgood ());
-}
-
-static int
-linux_get_ipa_tdesc_idx (void)
-{
- if (the_low_target.get_ipa_tdesc_idx == NULL)
- return 0;
-
- return (*the_low_target.get_ipa_tdesc_idx) ();
-}
-
-static int
-linux_supports_tracepoints (void)
-{
- if (*the_low_target.supports_tracepoints == NULL)
- return 0;
-
- return (*the_low_target.supports_tracepoints) ();
-}
-
-static CORE_ADDR
-linux_read_pc (struct regcache *regcache)
-{
- if (the_low_target.get_pc == NULL)
- return 0;
-
- return (*the_low_target.get_pc) (regcache);
-}
-
-static void
-linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- gdb_assert (the_low_target.set_pc != NULL);
-
- (*the_low_target.set_pc) (regcache, pc);
-}
-
-static int
-linux_thread_stopped (struct thread_info *thread)
-{
- return get_thread_lwp (thread)->stopped;
-}
-
-/* This exposes stop-all-threads functionality to other modules. */
-
-static void
-linux_pause_all (int freeze)
-{
- stop_all_lwps (freeze, NULL);
-}
-
-/* This exposes unstop-all-threads functionality to other gdbserver
- modules. */
-
-static void
-linux_unpause_all (int unfreeze)
-{
- unstop_all_lwps (unfreeze, NULL);
-}
-
-static int
-linux_prepare_to_access_memory (void)
-{
- /* Neither ptrace nor /proc/PID/mem allow accessing memory through a
- running LWP. */
- if (non_stop)
- linux_pause_all (1);
- return 0;
-}
-
-static void
-linux_done_accessing_memory (void)
-{
- /* Neither ptrace nor /proc/PID/mem allow accessing memory through a
- running LWP. */
- if (non_stop)
- linux_unpause_all (1);
-}
-
-static int
-linux_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)
-{
- return (*the_low_target.install_fast_tracepoint_jump_pad)
- (tpoint, tpaddr, collector, lockaddr, orig_size,
- jump_entry, trampoline, trampoline_size,
- jjump_pad_insn, jjump_pad_insn_size,
- adjusted_insn_addr, adjusted_insn_addr_end,
- err);
-}
-
-static struct emit_ops *
-linux_emit_ops (void)
-{
- if (the_low_target.emit_ops != NULL)
- return (*the_low_target.emit_ops) ();
- else
- return NULL;
-}
-
-static int
-linux_get_min_fast_tracepoint_insn_len (void)
-{
- return (*the_low_target.get_min_fast_tracepoint_insn_len) ();
-}
-
-/* Extract &phdr and num_phdr in the inferior. Return 0 on success. */
-
-static int
-get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64,
- CORE_ADDR *phdr_memaddr, int *num_phdr)
-{
- char filename[PATH_MAX];
- int fd;
- const int auxv_size = is_elf64
- ? sizeof (Elf64_auxv_t) : sizeof (Elf32_auxv_t);
- char buf[sizeof (Elf64_auxv_t)]; /* The larger of the two. */
-
- xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
-
- fd = open (filename, O_RDONLY);
- if (fd < 0)
- return 1;
-
- *phdr_memaddr = 0;
- *num_phdr = 0;
- while (read (fd, buf, auxv_size) == auxv_size
- && (*phdr_memaddr == 0 || *num_phdr == 0))
- {
- if (is_elf64)
- {
- Elf64_auxv_t *const aux = (Elf64_auxv_t *) buf;
-
- switch (aux->a_type)
- {
- case AT_PHDR:
- *phdr_memaddr = aux->a_un.a_val;
- break;
- case AT_PHNUM:
- *num_phdr = aux->a_un.a_val;
- break;
- }
- }
- else
- {
- Elf32_auxv_t *const aux = (Elf32_auxv_t *) buf;
-
- switch (aux->a_type)
- {
- case AT_PHDR:
- *phdr_memaddr = aux->a_un.a_val;
- break;
- case AT_PHNUM:
- *num_phdr = aux->a_un.a_val;
- break;
- }
- }
- }
-
- close (fd);
-
- if (*phdr_memaddr == 0 || *num_phdr == 0)
- {
- warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: "
- "phdr_memaddr = %ld, phdr_num = %d",
- (long) *phdr_memaddr, *num_phdr);
- return 2;
- }
-
- return 0;
-}
-
-/* Return &_DYNAMIC (via PT_DYNAMIC) in the inferior, or 0 if not present. */
-
-static CORE_ADDR
-get_dynamic (const int pid, const int is_elf64)
-{
- CORE_ADDR phdr_memaddr, relocation;
- int num_phdr, i;
- unsigned char *phdr_buf;
- const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr);
-
- if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr))
- return 0;
-
- gdb_assert (num_phdr < 100); /* Basic sanity check. */
- phdr_buf = (unsigned char *) alloca (num_phdr * phdr_size);
-
- if (linux_read_memory (phdr_memaddr, phdr_buf, num_phdr * phdr_size))
- return 0;
-
- /* Compute relocation: it is expected to be 0 for "regular" executables,
- non-zero for PIE ones. */
- relocation = -1;
- for (i = 0; relocation == -1 && i < num_phdr; i++)
- if (is_elf64)
- {
- Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
-
- if (p->p_type == PT_PHDR)
- relocation = phdr_memaddr - p->p_vaddr;
- }
- else
- {
- Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
-
- if (p->p_type == PT_PHDR)
- relocation = phdr_memaddr - p->p_vaddr;
- }
-
- if (relocation == -1)
- {
- /* PT_PHDR is optional, but necessary for PIE in general. Fortunately
- any real world executables, including PIE executables, have always
- PT_PHDR present. PT_PHDR is not present in some shared libraries or
- in fpc (Free Pascal 2.4) binaries but neither of those have a need for
- or present DT_DEBUG anyway (fpc binaries are statically linked).
-
- Therefore if there exists DT_DEBUG there is always also PT_PHDR.
-
- GDB could find RELOCATION also from AT_ENTRY - e_entry. */
-
- return 0;
- }
-
- for (i = 0; i < num_phdr; i++)
- {
- if (is_elf64)
- {
- Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
-
- if (p->p_type == PT_DYNAMIC)
- return p->p_vaddr + relocation;
- }
- else
- {
- Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
-
- if (p->p_type == PT_DYNAMIC)
- return p->p_vaddr + relocation;
- }
- }
-
- return 0;
-}
-
-/* Return &_r_debug in the inferior, or -1 if not present. Return value
- can be 0 if the inferior does not yet have the library list initialized.
- We look for DT_MIPS_RLD_MAP first. MIPS executables use this instead of
- DT_DEBUG, although they sometimes contain an unused DT_DEBUG entry too. */
-
-static CORE_ADDR
-get_r_debug (const int pid, const int is_elf64)
-{
- CORE_ADDR dynamic_memaddr;
- const int dyn_size = is_elf64 ? sizeof (Elf64_Dyn) : sizeof (Elf32_Dyn);
- unsigned char buf[sizeof (Elf64_Dyn)]; /* The larger of the two. */
- CORE_ADDR map = -1;
-
- dynamic_memaddr = get_dynamic (pid, is_elf64);
- if (dynamic_memaddr == 0)
- return map;
-
- while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0)
- {
- if (is_elf64)
- {
- Elf64_Dyn *const dyn = (Elf64_Dyn *) buf;
-#if defined DT_MIPS_RLD_MAP || defined DT_MIPS_RLD_MAP_REL
- union
- {
- Elf64_Xword map;
- unsigned char buf[sizeof (Elf64_Xword)];
- }
- rld_map;
-#endif
-#ifdef DT_MIPS_RLD_MAP
- if (dyn->d_tag == DT_MIPS_RLD_MAP)
- {
- if (linux_read_memory (dyn->d_un.d_val,
- rld_map.buf, sizeof (rld_map.buf)) == 0)
- return rld_map.map;
- else
- break;
- }
-#endif /* DT_MIPS_RLD_MAP */
-#ifdef DT_MIPS_RLD_MAP_REL
- if (dyn->d_tag == DT_MIPS_RLD_MAP_REL)
- {
- if (linux_read_memory (dyn->d_un.d_val + dynamic_memaddr,
- rld_map.buf, sizeof (rld_map.buf)) == 0)
- return rld_map.map;
- else
- break;
- }
-#endif /* DT_MIPS_RLD_MAP_REL */
-
- if (dyn->d_tag == DT_DEBUG && map == -1)
- map = dyn->d_un.d_val;
-
- if (dyn->d_tag == DT_NULL)
- break;
- }
- else
- {
- Elf32_Dyn *const dyn = (Elf32_Dyn *) buf;
-#if defined DT_MIPS_RLD_MAP || defined DT_MIPS_RLD_MAP_REL
- union
- {
- Elf32_Word map;
- unsigned char buf[sizeof (Elf32_Word)];
- }
- rld_map;
-#endif
-#ifdef DT_MIPS_RLD_MAP
- if (dyn->d_tag == DT_MIPS_RLD_MAP)
- {
- if (linux_read_memory (dyn->d_un.d_val,
- rld_map.buf, sizeof (rld_map.buf)) == 0)
- return rld_map.map;
- else
- break;
- }
-#endif /* DT_MIPS_RLD_MAP */
-#ifdef DT_MIPS_RLD_MAP_REL
- if (dyn->d_tag == DT_MIPS_RLD_MAP_REL)
- {
- if (linux_read_memory (dyn->d_un.d_val + dynamic_memaddr,
- rld_map.buf, sizeof (rld_map.buf)) == 0)
- return rld_map.map;
- else
- break;
- }
-#endif /* DT_MIPS_RLD_MAP_REL */
-
- if (dyn->d_tag == DT_DEBUG && map == -1)
- map = dyn->d_un.d_val;
-
- if (dyn->d_tag == DT_NULL)
- break;
- }
-
- dynamic_memaddr += dyn_size;
- }
-
- return map;
-}
-
-/* Read one pointer from MEMADDR in the inferior. */
-
-static int
-read_one_ptr (CORE_ADDR memaddr, CORE_ADDR *ptr, int ptr_size)
-{
- int ret;
-
- /* Go through a union so this works on either big or little endian
- hosts, when the inferior's pointer size is smaller than the size
- of CORE_ADDR. It is assumed the inferior's endianness is the
- same of the superior's. */
- union
- {
- CORE_ADDR core_addr;
- unsigned int ui;
- unsigned char uc;
- } addr;
-
- ret = linux_read_memory (memaddr, &addr.uc, ptr_size);
- if (ret == 0)
- {
- if (ptr_size == sizeof (CORE_ADDR))
- *ptr = addr.core_addr;
- else if (ptr_size == sizeof (unsigned int))
- *ptr = addr.ui;
- else
- gdb_assert_not_reached ("unhandled pointer size");
- }
- return ret;
-}
-
-struct link_map_offsets
- {
- /* Offset and size of r_debug.r_version. */
- int r_version_offset;
-
- /* Offset and size of r_debug.r_map. */
- int r_map_offset;
-
- /* Offset to l_addr field in struct link_map. */
- int l_addr_offset;
-
- /* Offset to l_name field in struct link_map. */
- int l_name_offset;
-
- /* Offset to l_ld field in struct link_map. */
- int l_ld_offset;
-
- /* Offset to l_next field in struct link_map. */
- int l_next_offset;
-
- /* Offset to l_prev field in struct link_map. */
- int l_prev_offset;
- };
-
-/* Construct qXfer:libraries-svr4:read reply. */
-
-static int
-linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
- unsigned const char *writebuf,
- CORE_ADDR offset, int len)
-{
- struct process_info_private *const priv = current_process ()->priv;
- char filename[PATH_MAX];
- int pid, is_elf64;
-
- static const struct link_map_offsets lmo_32bit_offsets =
- {
- 0, /* r_version offset. */
- 4, /* r_debug.r_map offset. */
- 0, /* l_addr offset in link_map. */
- 4, /* l_name offset in link_map. */
- 8, /* l_ld offset in link_map. */
- 12, /* l_next offset in link_map. */
- 16 /* l_prev offset in link_map. */
- };
-
- static const struct link_map_offsets lmo_64bit_offsets =
- {
- 0, /* r_version offset. */
- 8, /* r_debug.r_map offset. */
- 0, /* l_addr offset in link_map. */
- 8, /* l_name offset in link_map. */
- 16, /* l_ld offset in link_map. */
- 24, /* l_next offset in link_map. */
- 32 /* l_prev offset in link_map. */
- };
- const struct link_map_offsets *lmo;
- unsigned int machine;
- int ptr_size;
- CORE_ADDR lm_addr = 0, lm_prev = 0;
- CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
- int header_done = 0;
-
- if (writebuf != NULL)
- return -2;
- if (readbuf == NULL)
- return -1;
-
- pid = lwpid_of (current_thread);
- xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
- is_elf64 = elf_64_file_p (filename, &machine);
- lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
- ptr_size = is_elf64 ? 8 : 4;
-
- while (annex[0] != '\0')
- {
- const char *sep;
- CORE_ADDR *addrp;
- int name_len;
-
- sep = strchr (annex, '=');
- if (sep == NULL)
- break;
-
- name_len = sep - annex;
- if (name_len == 5 && startswith (annex, "start"))
- addrp = &lm_addr;
- else if (name_len == 4 && startswith (annex, "prev"))
- addrp = &lm_prev;
- else
- {
- annex = strchr (sep, ';');
- if (annex == NULL)
- break;
- annex++;
- continue;
- }
-
- annex = decode_address_to_semicolon (addrp, sep + 1);
- }
-
- if (lm_addr == 0)
- {
- int r_version = 0;
-
- if (priv->r_debug == 0)
- priv->r_debug = get_r_debug (pid, is_elf64);
-
- /* We failed to find DT_DEBUG. Such situation will not change
- for this inferior - do not retry it. Report it to GDB as
- E01, see for the reasons at the GDB solib-svr4.c side. */
- if (priv->r_debug == (CORE_ADDR) -1)
- return -1;
-
- if (priv->r_debug != 0)
- {
- if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
- (unsigned char *) &r_version,
- sizeof (r_version)) != 0
- || r_version != 1)
- {
- warning ("unexpected r_debug version %d", r_version);
- }
- else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
- &lm_addr, ptr_size) != 0)
- {
- warning ("unable to read r_map from 0x%lx",
- (long) priv->r_debug + lmo->r_map_offset);
- }
- }
- }
-
- std::string document = "<library-list-svr4 version=\"1.0\"";
-
- while (lm_addr
- && read_one_ptr (lm_addr + lmo->l_name_offset,
- &l_name, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_addr_offset,
- &l_addr, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_ld_offset,
- &l_ld, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_prev_offset,
- &l_prev, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_next_offset,
- &l_next, ptr_size) == 0)
- {
- unsigned char libname[PATH_MAX];
-
- if (lm_prev != l_prev)
- {
- warning ("Corrupted shared library list: 0x%lx != 0x%lx",
- (long) lm_prev, (long) l_prev);
- break;
- }
-
- /* Ignore the first entry even if it has valid name as the first entry
- corresponds to the main executable. The first entry should not be
- skipped if the dynamic loader was loaded late by a static executable
- (see solib-svr4.c parameter ignore_first). But in such case the main
- executable does not have PT_DYNAMIC present and this function already
- exited above due to failed get_r_debug. */
- if (lm_prev == 0)
- string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
- else
- {
- /* Not checking for error because reading may stop before
- we've got PATH_MAX worth of characters. */
- libname[0] = '\0';
- linux_read_memory (l_name, libname, sizeof (libname) - 1);
- libname[sizeof (libname) - 1] = '\0';
- if (libname[0] != '\0')
- {
- if (!header_done)
- {
- /* Terminate `<library-list-svr4'. */
- document += '>';
- header_done = 1;
- }
-
- string_appendf (document, "<library name=\"");
- xml_escape_text_append (&document, (char *) libname);
- string_appendf (document, "\" lm=\"0x%lx\" "
- "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
- (unsigned long) lm_addr, (unsigned long) l_addr,
- (unsigned long) l_ld);
- }
- }
-
- lm_prev = lm_addr;
- lm_addr = l_next;
- }
-
- if (!header_done)
- {
- /* Empty list; terminate `<library-list-svr4'. */
- document += "/>";
- }
- else
- document += "</library-list-svr4>";
-
- int document_len = document.length ();
- if (offset < document_len)
- document_len -= offset;
- else
- document_len = 0;
- if (len > document_len)
- len = document_len;
-
- memcpy (readbuf, document.data () + offset, len);
-
- return len;
-}
-
-#ifdef HAVE_LINUX_BTRACE
-
-/* See to_disable_btrace target method. */
-
-static int
-linux_low_disable_btrace (struct btrace_target_info *tinfo)
-{
- enum btrace_error err;
-
- err = linux_disable_btrace (tinfo);
- return (err == BTRACE_ERR_NONE ? 0 : -1);
-}
-
-/* Encode an Intel Processor Trace configuration. */
-
-static void
-linux_low_encode_pt_config (struct buffer *buffer,
- const struct btrace_data_pt_config *config)
-{
- buffer_grow_str (buffer, "<pt-config>\n");
-
- switch (config->cpu.vendor)
- {
- case CV_INTEL:
- buffer_xml_printf (buffer, "<cpu vendor=\"GenuineIntel\" family=\"%u\" "
- "model=\"%u\" stepping=\"%u\"/>\n",
- config->cpu.family, config->cpu.model,
- config->cpu.stepping);
- break;
-
- default:
- break;
- }
-
- buffer_grow_str (buffer, "</pt-config>\n");
-}
-
-/* Encode a raw buffer. */
-
-static void
-linux_low_encode_raw (struct buffer *buffer, const gdb_byte *data,
- unsigned int size)
-{
- if (size == 0)
- return;
-
- /* We use hex encoding - see gdbsupport/rsp-low.h. */
- buffer_grow_str (buffer, "<raw>\n");
-
- while (size-- > 0)
- {
- char elem[2];
-
- elem[0] = tohex ((*data >> 4) & 0xf);
- elem[1] = tohex (*data++ & 0xf);
-
- buffer_grow (buffer, elem, 2);
- }
-
- buffer_grow_str (buffer, "</raw>\n");
-}
-
-/* See to_read_btrace target method. */
-
-static int
-linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
- enum btrace_read_type type)
-{
- struct btrace_data btrace;
- enum btrace_error err;
-
- err = linux_read_btrace (&btrace, tinfo, type);
- if (err != BTRACE_ERR_NONE)
- {
- if (err == BTRACE_ERR_OVERFLOW)
- buffer_grow_str0 (buffer, "E.Overflow.");
- else
- buffer_grow_str0 (buffer, "E.Generic Error.");
-
- return -1;
- }
-
- switch (btrace.format)
- {
- case BTRACE_FORMAT_NONE:
- buffer_grow_str0 (buffer, "E.No Trace.");
- return -1;
-
- case BTRACE_FORMAT_BTS:
- buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
- buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
-
- for (const btrace_block &block : *btrace.variant.bts.blocks)
- buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
- paddress (block.begin), paddress (block.end));
-
- buffer_grow_str0 (buffer, "</btrace>\n");
- break;
-
- case BTRACE_FORMAT_PT:
- buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
- buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
- buffer_grow_str (buffer, "<pt>\n");
-
- linux_low_encode_pt_config (buffer, &btrace.variant.pt.config);
-
- linux_low_encode_raw (buffer, btrace.variant.pt.data,
- btrace.variant.pt.size);
-
- buffer_grow_str (buffer, "</pt>\n");
- buffer_grow_str0 (buffer, "</btrace>\n");
- break;
-
- default:
- buffer_grow_str0 (buffer, "E.Unsupported Trace Format.");
- return -1;
- }
-
- return 0;
-}
-
-/* See to_btrace_conf target method. */
-
-static int
-linux_low_btrace_conf (const struct btrace_target_info *tinfo,
- struct buffer *buffer)
-{
- const struct btrace_config *conf;
-
- buffer_grow_str (buffer, "<!DOCTYPE btrace-conf SYSTEM \"btrace-conf.dtd\">\n");
- buffer_grow_str (buffer, "<btrace-conf version=\"1.0\">\n");
-
- conf = linux_btrace_conf (tinfo);
- if (conf != NULL)
- {
- switch (conf->format)
- {
- case BTRACE_FORMAT_NONE:
- break;
-
- case BTRACE_FORMAT_BTS:
- buffer_xml_printf (buffer, "<bts");
- buffer_xml_printf (buffer, " size=\"0x%x\"", conf->bts.size);
- buffer_xml_printf (buffer, " />\n");
- break;
-
- case BTRACE_FORMAT_PT:
- buffer_xml_printf (buffer, "<pt");
- buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
- buffer_xml_printf (buffer, "/>\n");
- break;
- }
- }
-
- buffer_grow_str0 (buffer, "</btrace-conf>\n");
- return 0;
-}
-#endif /* HAVE_LINUX_BTRACE */
-
-/* See nat/linux-nat.h. */
-
-ptid_t
-current_lwp_ptid (void)
-{
- return ptid_of (current_thread);
-}
-
-/* Implementation of the target_ops method "breakpoint_kind_from_pc". */
-
-static int
-linux_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
-{
- if (the_low_target.breakpoint_kind_from_pc != NULL)
- return (*the_low_target.breakpoint_kind_from_pc) (pcptr);
- else
- return default_breakpoint_kind_from_pc (pcptr);
-}
-
-/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-linux_sw_breakpoint_from_kind (int kind, int *size)
-{
- gdb_assert (the_low_target.sw_breakpoint_from_kind != NULL);
-
- return (*the_low_target.sw_breakpoint_from_kind) (kind, size);
-}
-
-/* Implementation of the target_ops method
- "breakpoint_kind_from_current_state". */
-
-static int
-linux_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
-{
- if (the_low_target.breakpoint_kind_from_current_state != NULL)
- return (*the_low_target.breakpoint_kind_from_current_state) (pcptr);
- else
- return linux_breakpoint_kind_from_pc (pcptr);
-}
-
-/* Default implementation of linux_target_ops method "set_pc" for
- 32-bit pc register which is literally named "pc". */
-
-void
-linux_set_pc_32bit (struct regcache *regcache, CORE_ADDR pc)
-{
- uint32_t newpc = pc;
-
- supply_register_by_name (regcache, "pc", &newpc);
-}
-
-/* Default implementation of linux_target_ops method "get_pc" for
- 32-bit pc register which is literally named "pc". */
-
-CORE_ADDR
-linux_get_pc_32bit (struct regcache *regcache)
-{
- uint32_t pc;
-
- collect_register_by_name (regcache, "pc", &pc);
- if (debug_threads)
- debug_printf ("stop pc is 0x%" PRIx32 "\n", pc);
- return pc;
-}
-
-/* Default implementation of linux_target_ops method "set_pc" for
- 64-bit pc register which is literally named "pc". */
-
-void
-linux_set_pc_64bit (struct regcache *regcache, CORE_ADDR pc)
-{
- uint64_t newpc = pc;
-
- supply_register_by_name (regcache, "pc", &newpc);
-}
-
-/* Default implementation of linux_target_ops method "get_pc" for
- 64-bit pc register which is literally named "pc". */
-
-CORE_ADDR
-linux_get_pc_64bit (struct regcache *regcache)
-{
- uint64_t pc;
-
- collect_register_by_name (regcache, "pc", &pc);
- if (debug_threads)
- debug_printf ("stop pc is 0x%" PRIx64 "\n", pc);
- return pc;
-}
-
-/* See linux-low.h. */
-
-int
-linux_get_auxv (int wordsize, CORE_ADDR match, CORE_ADDR *valp)
-{
- gdb_byte *data = (gdb_byte *) alloca (2 * wordsize);
- int offset = 0;
-
- gdb_assert (wordsize == 4 || wordsize == 8);
-
- while ((*the_target->read_auxv) (offset, data, 2 * wordsize) == 2 * wordsize)
- {
- if (wordsize == 4)
- {
- uint32_t *data_p = (uint32_t *) data;
- if (data_p[0] == match)
- {
- *valp = data_p[1];
- return 1;
- }
- }
- else
- {
- uint64_t *data_p = (uint64_t *) data;
- if (data_p[0] == match)
- {
- *valp = data_p[1];
- return 1;
- }
- }
-
- offset += 2 * wordsize;
- }
-
- return 0;
-}
-
-/* See linux-low.h. */
-
-CORE_ADDR
-linux_get_hwcap (int wordsize)
-{
- CORE_ADDR hwcap = 0;
- linux_get_auxv (wordsize, AT_HWCAP, &hwcap);
- return hwcap;
-}
-
-/* See linux-low.h. */
-
-CORE_ADDR
-linux_get_hwcap2 (int wordsize)
-{
- CORE_ADDR hwcap2 = 0;
- linux_get_auxv (wordsize, AT_HWCAP2, &hwcap2);
- return hwcap2;
-}
-
-static process_stratum_target linux_target_ops = {
- linux_create_inferior,
- linux_post_create_inferior,
- linux_attach,
- linux_kill,
- linux_detach,
- linux_mourn,
- linux_join,
- linux_thread_alive,
- linux_resume,
- linux_wait,
- linux_fetch_registers,
- linux_store_registers,
- linux_prepare_to_access_memory,
- linux_done_accessing_memory,
- linux_read_memory,
- linux_write_memory,
- linux_look_up_symbols,
- linux_request_interrupt,
- linux_read_auxv,
- linux_supports_z_point_type,
- linux_insert_point,
- linux_remove_point,
- linux_stopped_by_sw_breakpoint,
- linux_supports_stopped_by_sw_breakpoint,
- linux_stopped_by_hw_breakpoint,
- linux_supports_stopped_by_hw_breakpoint,
- linux_supports_hardware_single_step,
- linux_stopped_by_watchpoint,
- linux_stopped_data_address,
-#if defined(__UCLIBC__) && defined(HAS_NOMMU) \
- && defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) \
- && defined(PT_TEXT_END_ADDR)
- linux_read_offsets,
-#else
- NULL,
-#endif
-#ifdef USE_THREAD_DB
- thread_db_get_tls_address,
-#else
- NULL,
-#endif
- hostio_last_error_from_errno,
- linux_qxfer_osdata,
- linux_xfer_siginfo,
- linux_supports_non_stop,
- linux_async,
- linux_start_non_stop,
- linux_supports_multi_process,
- linux_supports_fork_events,
- linux_supports_vfork_events,
- linux_supports_exec_events,
- linux_handle_new_gdb_connection,
-#ifdef USE_THREAD_DB
- thread_db_handle_monitor_command,
-#else
- NULL,
-#endif
- linux_common_core_of_thread,
- linux_read_loadmap,
- linux_process_qsupported,
- linux_supports_tracepoints,
- linux_read_pc,
- linux_write_pc,
- linux_thread_stopped,
- NULL,
- linux_pause_all,
- linux_unpause_all,
- linux_stabilize_threads,
- linux_install_fast_tracepoint_jump_pad,
- linux_emit_ops,
- linux_supports_disable_randomization,
- linux_get_min_fast_tracepoint_insn_len,
- linux_qxfer_libraries_svr4,
- linux_supports_agent,
-#ifdef HAVE_LINUX_BTRACE
- linux_enable_btrace,
- linux_low_disable_btrace,
- linux_low_read_btrace,
- linux_low_btrace_conf,
-#else
- NULL,
- NULL,
- NULL,
- NULL,
-#endif
- linux_supports_range_stepping,
- linux_proc_pid_to_exec_file,
- linux_mntns_open_cloexec,
- linux_mntns_unlink,
- linux_mntns_readlink,
- linux_breakpoint_kind_from_pc,
- linux_sw_breakpoint_from_kind,
- linux_proc_tid_get_name,
- linux_breakpoint_kind_from_current_state,
- linux_supports_software_single_step,
- linux_supports_catch_syscall,
- linux_get_ipa_tdesc_idx,
-#if USE_THREAD_DB
- thread_db_thread_handle,
-#else
- NULL,
-#endif
-};
-
-#ifdef HAVE_LINUX_REGSETS
-void
-initialize_regsets_info (struct regsets_info *info)
-{
- for (info->num_regsets = 0;
- info->regsets[info->num_regsets].size >= 0;
- info->num_regsets++)
- ;
-}
-#endif
-
-void
-initialize_low (void)
-{
- struct sigaction sigchld_action;
-
- memset (&sigchld_action, 0, sizeof (sigchld_action));
- set_target_ops (&linux_target_ops);
-
- linux_ptrace_init_warnings ();
- linux_proc_init_warnings ();
-
- sigchld_action.sa_handler = sigchld_handler;
- sigemptyset (&sigchld_action.sa_mask);
- sigchld_action.sa_flags = SA_RESTART;
- sigaction (SIGCHLD, &sigchld_action, NULL);
-
- initialize_low_arch ();
-
- linux_check_ptrace_features ();
-}
--- /dev/null
+/* Low level interface to ptrace, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "nat/linux-osdata.h"
+#include "gdbsupport/agent.h"
+#include "tdesc.h"
+#include "gdbsupport/rsp-low.h"
+#include "gdbsupport/signals-state-save-restore.h"
+#include "nat/linux-nat.h"
+#include "nat/linux-waitpid.h"
+#include "gdbsupport/gdb_wait.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/linux-ptrace.h"
+#include "nat/linux-procfs.h"
+#include "nat/linux-personality.h"
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sched.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <sys/uio.h>
+#include "gdbsupport/filestuff.h"
+#include "tracepoint.h"
+#include "hostio.h"
+#include <inttypes.h>
+#include "gdbsupport/common-inferior.h"
+#include "nat/fork-inferior.h"
+#include "gdbsupport/environ.h"
+#include "gdbsupport/gdb-sigmask.h"
+#include "gdbsupport/scoped_restore.h"
+#ifndef ELFMAG0
+/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
+ then ELFMAG0 will have been defined. If it didn't get included by
+ gdb_proc_service.h then including it will likely introduce a duplicate
+ definition of elf_fpregset_t. */
+#include <elf.h>
+#endif
+#include "nat/linux-namespaces.h"
+
+#ifdef HAVE_PERSONALITY
+# include <sys/personality.h>
+# if !HAVE_DECL_ADDR_NO_RANDOMIZE
+# define ADDR_NO_RANDOMIZE 0x0040000
+# endif
+#endif
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+
+#ifndef AT_HWCAP2
+#define AT_HWCAP2 26
+#endif
+
+/* Some targets did not define these ptrace constants from the start,
+ so gdbserver defines them locally here. In the future, these may
+ be removed after they are added to asm/ptrace.h. */
+#if !(defined(PT_TEXT_ADDR) \
+ || defined(PT_DATA_ADDR) \
+ || defined(PT_TEXT_END_ADDR))
+#if defined(__mcoldfire__)
+/* These are still undefined in 3.10 kernels. */
+#define PT_TEXT_ADDR 49*4
+#define PT_DATA_ADDR 50*4
+#define PT_TEXT_END_ADDR 51*4
+/* BFIN already defines these since at least 2.6.32 kernels. */
+#elif defined(BFIN)
+#define PT_TEXT_ADDR 220
+#define PT_TEXT_END_ADDR 224
+#define PT_DATA_ADDR 228
+/* These are still undefined in 3.10 kernels. */
+#elif defined(__TMS320C6X__)
+#define PT_TEXT_ADDR (0x10000*4)
+#define PT_DATA_ADDR (0x10004*4)
+#define PT_TEXT_END_ADDR (0x10008*4)
+#endif
+#endif
+
+#ifdef HAVE_LINUX_BTRACE
+# include "nat/linux-btrace.h"
+# include "gdbsupport/btrace-common.h"
+#endif
+
+#ifndef HAVE_ELF32_AUXV_T
+/* Copied from glibc's elf.h. */
+typedef struct
+{
+ uint32_t a_type; /* Entry type */
+ union
+ {
+ uint32_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf32_auxv_t;
+#endif
+
+#ifndef HAVE_ELF64_AUXV_T
+/* Copied from glibc's elf.h. */
+typedef struct
+{
+ uint64_t a_type; /* Entry type */
+ union
+ {
+ uint64_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf64_auxv_t;
+#endif
+
+/* Does the current host support PTRACE_GETREGSET? */
+int have_ptrace_getregset = -1;
+
+/* LWP accessors. */
+
+/* See nat/linux-nat.h. */
+
+ptid_t
+ptid_of_lwp (struct lwp_info *lwp)
+{
+ return ptid_of (get_lwp_thread (lwp));
+}
+
+/* See nat/linux-nat.h. */
+
+void
+lwp_set_arch_private_info (struct lwp_info *lwp,
+ struct arch_lwp_info *info)
+{
+ lwp->arch_private = info;
+}
+
+/* See nat/linux-nat.h. */
+
+struct arch_lwp_info *
+lwp_arch_private_info (struct lwp_info *lwp)
+{
+ return lwp->arch_private;
+}
+
+/* See nat/linux-nat.h. */
+
+int
+lwp_is_stopped (struct lwp_info *lwp)
+{
+ return lwp->stopped;
+}
+
+/* See nat/linux-nat.h. */
+
+enum target_stop_reason
+lwp_stop_reason (struct lwp_info *lwp)
+{
+ return lwp->stop_reason;
+}
+
+/* See nat/linux-nat.h. */
+
+int
+lwp_is_stepping (struct lwp_info *lwp)
+{
+ return lwp->stepping;
+}
+
+/* A list of all unknown processes which receive stop signals. Some
+ other process will presumably claim each of these as forked
+ children momentarily. */
+
+struct simple_pid_list
+{
+ /* The process ID. */
+ int pid;
+
+ /* The status as reported by waitpid. */
+ int status;
+
+ /* Next in chain. */
+ struct simple_pid_list *next;
+};
+struct simple_pid_list *stopped_pids;
+
+/* Trivial list manipulation functions to keep track of a list of new
+ stopped processes. */
+
+static void
+add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
+{
+ struct simple_pid_list *new_pid = XNEW (struct simple_pid_list);
+
+ new_pid->pid = pid;
+ new_pid->status = status;
+ new_pid->next = *listp;
+ *listp = new_pid;
+}
+
+static int
+pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
+{
+ struct simple_pid_list **p;
+
+ for (p = listp; *p != NULL; p = &(*p)->next)
+ if ((*p)->pid == pid)
+ {
+ struct simple_pid_list *next = (*p)->next;
+
+ *statusp = (*p)->status;
+ xfree (*p);
+ *p = next;
+ return 1;
+ }
+ return 0;
+}
+
+enum stopping_threads_kind
+ {
+ /* Not stopping threads presently. */
+ NOT_STOPPING_THREADS,
+
+ /* Stopping threads. */
+ STOPPING_THREADS,
+
+ /* Stopping and suspending threads. */
+ STOPPING_AND_SUSPENDING_THREADS
+ };
+
+/* This is set while stop_all_lwps is in effect. */
+enum stopping_threads_kind stopping_threads = NOT_STOPPING_THREADS;
+
+/* FIXME make into a target method? */
+int using_threads = 1;
+
+/* True if we're presently stabilizing threads (moving them out of
+ jump pads). */
+static int stabilizing_threads;
+
+static void linux_resume_one_lwp (struct lwp_info *lwp,
+ int step, int signal, siginfo_t *info);
+static void linux_resume (struct thread_resume *resume_info, size_t n);
+static void stop_all_lwps (int suspend, struct lwp_info *except);
+static void unstop_all_lwps (int unsuspend, struct lwp_info *except);
+static void unsuspend_all_lwps (struct lwp_info *except);
+static int linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
+ int *wstat, int options);
+static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void linux_mourn (struct process_info *process);
+static int linux_stopped_by_watchpoint (void);
+static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int lwp_is_marked_dead (struct lwp_info *lwp);
+static void proceed_all_lwps (void);
+static int finish_step_over (struct lwp_info *lwp);
+static int kill_lwp (unsigned long lwpid, int signo);
+static void enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info);
+static void complete_ongoing_step_over (void);
+static int linux_low_ptrace_options (int attached);
+static int check_ptrace_stopped_lwp_gone (struct lwp_info *lp);
+static void proceed_one_lwp (thread_info *thread, lwp_info *except);
+
+/* When the event-loop is doing a step-over, this points at the thread
+ being stepped. */
+ptid_t step_over_bkpt;
+
+/* True if the low target can hardware single-step. */
+
+static int
+can_hardware_single_step (void)
+{
+ if (the_low_target.supports_hardware_single_step != NULL)
+ return the_low_target.supports_hardware_single_step ();
+ else
+ return 0;
+}
+
+/* True if the low target can software single-step. Such targets
+ implement the GET_NEXT_PCS callback. */
+
+static int
+can_software_single_step (void)
+{
+ return (the_low_target.get_next_pcs != NULL);
+}
+
+/* True if the low target supports memory breakpoints. If so, we'll
+ have a GET_PC implementation. */
+
+static int
+supports_breakpoints (void)
+{
+ return (the_low_target.get_pc != NULL);
+}
+
+/* Returns true if this target can support fast tracepoints. This
+ does not mean that the in-process agent has been loaded in the
+ inferior. */
+
+static int
+supports_fast_tracepoints (void)
+{
+ return the_low_target.install_fast_tracepoint_jump_pad != NULL;
+}
+
+/* True if LWP is stopped in its stepping range. */
+
+static int
+lwp_in_step_range (struct lwp_info *lwp)
+{
+ CORE_ADDR pc = lwp->stop_pc;
+
+ return (pc >= lwp->step_range_start && pc < lwp->step_range_end);
+}
+
+struct pending_signals
+{
+ int signal;
+ siginfo_t info;
+ struct pending_signals *prev;
+};
+
+/* The read/write ends of the pipe registered as waitable file in the
+ event loop. */
+static int linux_event_pipe[2] = { -1, -1 };
+
+/* True if we're currently in async mode. */
+#define target_is_async_p() (linux_event_pipe[0] != -1)
+
+static void send_sigstop (struct lwp_info *lwp);
+static void wait_for_sigstop (void);
+
+/* Return non-zero if HEADER is a 64-bit ELF file. */
+
+static int
+elf_64_header_p (const Elf64_Ehdr *header, unsigned int *machine)
+{
+ if (header->e_ident[EI_MAG0] == ELFMAG0
+ && header->e_ident[EI_MAG1] == ELFMAG1
+ && header->e_ident[EI_MAG2] == ELFMAG2
+ && header->e_ident[EI_MAG3] == ELFMAG3)
+ {
+ *machine = header->e_machine;
+ return header->e_ident[EI_CLASS] == ELFCLASS64;
+
+ }
+ *machine = EM_NONE;
+ return -1;
+}
+
+/* Return non-zero if FILE is a 64-bit ELF file,
+ zero if the file is not a 64-bit ELF file,
+ and -1 if the file is not accessible or doesn't exist. */
+
+static int
+elf_64_file_p (const char *file, unsigned int *machine)
+{
+ Elf64_Ehdr header;
+ int fd;
+
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (read (fd, &header, sizeof (header)) != sizeof (header))
+ {
+ close (fd);
+ return 0;
+ }
+ close (fd);
+
+ return elf_64_header_p (&header, machine);
+}
+
+/* Accepts an integer PID; Returns true if the executable PID is
+ running is a 64-bit ELF file.. */
+
+int
+linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine)
+{
+ char file[PATH_MAX];
+
+ sprintf (file, "/proc/%d/exe", pid);
+ return elf_64_file_p (file, machine);
+}
+
+static void
+delete_lwp (struct lwp_info *lwp)
+{
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ if (debug_threads)
+ debug_printf ("deleting %ld\n", lwpid_of (thr));
+
+ remove_thread (thr);
+
+ if (the_low_target.delete_thread != NULL)
+ the_low_target.delete_thread (lwp->arch_private);
+ else
+ gdb_assert (lwp->arch_private == NULL);
+
+ free (lwp);
+}
+
+/* Add a process to the common process list, and set its private
+ data. */
+
+static struct process_info *
+linux_add_process (int pid, int attached)
+{
+ struct process_info *proc;
+
+ proc = add_process (pid, attached);
+ proc->priv = XCNEW (struct process_info_private);
+
+ if (the_low_target.new_process != NULL)
+ proc->priv->arch_private = the_low_target.new_process ();
+
+ return proc;
+}
+
+static CORE_ADDR get_pc (struct lwp_info *lwp);
+
+/* Call the target arch_setup function on the current thread. */
+
+static void
+linux_arch_setup (void)
+{
+ the_low_target.arch_setup ();
+}
+
+/* Call the target arch_setup function on THREAD. */
+
+static void
+linux_arch_setup_thread (struct thread_info *thread)
+{
+ struct thread_info *saved_thread;
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ linux_arch_setup ();
+
+ current_thread = saved_thread;
+}
+
+/* Handle a GNU/Linux extended wait response. If we see a clone,
+ fork, or vfork event, we need to add the new LWP to our list
+ (and return 0 so as not to report the trap to higher layers).
+ If we see an exec event, we will modify ORIG_EVENT_LWP to point
+ to a new LWP representing the new program. */
+
+static int
+handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
+{
+ client_state &cs = get_client_state ();
+ struct lwp_info *event_lwp = *orig_event_lwp;
+ int event = linux_ptrace_get_extended_event (wstat);
+ struct thread_info *event_thr = get_lwp_thread (event_lwp);
+ struct lwp_info *new_lwp;
+
+ gdb_assert (event_lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
+
+ /* All extended events we currently use are mid-syscall. Only
+ PTRACE_EVENT_STOP is delivered more like a signal-stop, but
+ you have to be using PTRACE_SEIZE to get that. */
+ event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
+
+ if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
+ || (event == PTRACE_EVENT_CLONE))
+ {
+ ptid_t ptid;
+ unsigned long new_pid;
+ int ret, status;
+
+ /* Get the pid of the new lwp. */
+ ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
+ &new_pid);
+
+ /* If we haven't already seen the new PID stop, wait for it now. */
+ if (!pull_pid_from_list (&stopped_pids, new_pid, &status))
+ {
+ /* The new child has a pending SIGSTOP. We can't affect it until it
+ hits the SIGSTOP, but we're already attached. */
+
+ ret = my_waitpid (new_pid, &status, __WALL);
+
+ if (ret == -1)
+ perror_with_name ("waiting for new child");
+ else if (ret != new_pid)
+ warning ("wait returned unexpected PID %d", ret);
+ else if (!WIFSTOPPED (status))
+ warning ("wait returned unexpected status 0x%x", status);
+ }
+
+ if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
+ {
+ struct process_info *parent_proc;
+ struct process_info *child_proc;
+ struct lwp_info *child_lwp;
+ struct thread_info *child_thr;
+ struct target_desc *tdesc;
+
+ ptid = ptid_t (new_pid, new_pid, 0);
+
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got fork event from LWP %ld, "
+ "new child is %d\n",
+ ptid_of (event_thr).lwp (),
+ ptid.pid ());
+ }
+
+ /* Add the new process to the tables and clone the breakpoint
+ lists of the parent. We need to do this even if the new process
+ will be detached, since we will need the process object and the
+ breakpoints to remove any breakpoints from memory when we
+ detach, and the client side will access registers. */
+ child_proc = linux_add_process (new_pid, 0);
+ gdb_assert (child_proc != NULL);
+ child_lwp = add_lwp (ptid);
+ gdb_assert (child_lwp != NULL);
+ child_lwp->stopped = 1;
+ child_lwp->must_set_ptrace_flags = 1;
+ child_lwp->status_pending_p = 0;
+ child_thr = get_lwp_thread (child_lwp);
+ child_thr->last_resume_kind = resume_stop;
+ child_thr->last_status.kind = TARGET_WAITKIND_STOPPED;
+
+ /* If we're suspending all threads, leave this one suspended
+ too. If the fork/clone parent is stepping over a breakpoint,
+ all other threads have been suspended already. Leave the
+ child suspended too. */
+ if (stopping_threads == STOPPING_AND_SUSPENDING_THREADS
+ || event_lwp->bp_reinsert != 0)
+ {
+ if (debug_threads)
+ debug_printf ("HEW: leaving child suspended\n");
+ child_lwp->suspended = 1;
+ }
+
+ parent_proc = get_thread_process (event_thr);
+ child_proc->attached = parent_proc->attached;
+
+ if (event_lwp->bp_reinsert != 0
+ && can_software_single_step ()
+ && event == PTRACE_EVENT_VFORK)
+ {
+ /* If we leave single-step breakpoints there, child will
+ hit it, so uninsert single-step breakpoints from parent
+ (and child). Once vfork child is done, reinsert
+ them back to parent. */
+ uninsert_single_step_breakpoints (event_thr);
+ }
+
+ clone_all_breakpoints (child_thr, event_thr);
+
+ tdesc = allocate_target_description ();
+ copy_target_description (tdesc, parent_proc->tdesc);
+ child_proc->tdesc = tdesc;
+
+ /* Clone arch-specific process data. */
+ if (the_low_target.new_fork != NULL)
+ the_low_target.new_fork (parent_proc, child_proc);
+
+ /* Save fork info in the parent thread. */
+ if (event == PTRACE_EVENT_FORK)
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+ else if (event == PTRACE_EVENT_VFORK)
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+
+ event_lwp->waitstatus.value.related_pid = ptid;
+
+ /* The status_pending field contains bits denoting the
+ extended event, so when the pending event is handled,
+ the handler will look at lwp->waitstatus. */
+ event_lwp->status_pending_p = 1;
+ event_lwp->status_pending = wstat;
+
+ /* Link the threads until the parent event is passed on to
+ higher layers. */
+ event_lwp->fork_relative = child_lwp;
+ child_lwp->fork_relative = event_lwp;
+
+ /* If the parent thread is doing step-over with single-step
+ breakpoints, the list of single-step breakpoints are cloned
+ from the parent's. Remove them from the child process.
+ In case of vfork, we'll reinsert them back once vforked
+ child is done. */
+ if (event_lwp->bp_reinsert != 0
+ && can_software_single_step ())
+ {
+ /* The child process is forked and stopped, so it is safe
+ to access its memory without stopping all other threads
+ from other processes. */
+ delete_single_step_breakpoints (child_thr);
+
+ gdb_assert (has_single_step_breakpoints (event_thr));
+ gdb_assert (!has_single_step_breakpoints (child_thr));
+ }
+
+ /* Report the event. */
+ return 0;
+ }
+
+ if (debug_threads)
+ debug_printf ("HEW: Got clone event "
+ "from LWP %ld, new child is LWP %ld\n",
+ lwpid_of (event_thr), new_pid);
+
+ ptid = ptid_t (pid_of (event_thr), new_pid, 0);
+ new_lwp = add_lwp (ptid);
+
+ /* Either we're going to immediately resume the new thread
+ or leave it stopped. linux_resume_one_lwp is a nop if it
+ thinks the thread is currently running, so set this first
+ before calling linux_resume_one_lwp. */
+ new_lwp->stopped = 1;
+
+ /* If we're suspending all threads, leave this one suspended
+ too. If the fork/clone parent is stepping over a breakpoint,
+ all other threads have been suspended already. Leave the
+ child suspended too. */
+ if (stopping_threads == STOPPING_AND_SUSPENDING_THREADS
+ || event_lwp->bp_reinsert != 0)
+ new_lwp->suspended = 1;
+
+ /* Normally we will get the pending SIGSTOP. But in some cases
+ we might get another signal delivered to the group first.
+ If we do get another signal, be sure not to lose it. */
+ if (WSTOPSIG (status) != SIGSTOP)
+ {
+ new_lwp->stop_expected = 1;
+ new_lwp->status_pending_p = 1;
+ new_lwp->status_pending = status;
+ }
+ else if (cs.report_thread_events)
+ {
+ new_lwp->waitstatus.kind = TARGET_WAITKIND_THREAD_CREATED;
+ new_lwp->status_pending_p = 1;
+ new_lwp->status_pending = status;
+ }
+
+#ifdef USE_THREAD_DB
+ thread_db_notice_clone (event_thr, ptid);
+#endif
+
+ /* Don't report the event. */
+ return 1;
+ }
+ else if (event == PTRACE_EVENT_VFORK_DONE)
+ {
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+
+ if (event_lwp->bp_reinsert != 0 && can_software_single_step ())
+ {
+ reinsert_single_step_breakpoints (event_thr);
+
+ gdb_assert (has_single_step_breakpoints (event_thr));
+ }
+
+ /* Report the event. */
+ return 0;
+ }
+ else if (event == PTRACE_EVENT_EXEC && cs.report_exec_events)
+ {
+ struct process_info *proc;
+ std::vector<int> syscalls_to_catch;
+ ptid_t event_ptid;
+ pid_t event_pid;
+
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got exec event from LWP %ld\n",
+ lwpid_of (event_thr));
+ }
+
+ /* Get the event ptid. */
+ event_ptid = ptid_of (event_thr);
+ event_pid = event_ptid.pid ();
+
+ /* Save the syscall list from the execing process. */
+ proc = get_thread_process (event_thr);
+ syscalls_to_catch = std::move (proc->syscalls_to_catch);
+
+ /* Delete the execing process and all its threads. */
+ linux_mourn (proc);
+ current_thread = NULL;
+
+ /* Create a new process/lwp/thread. */
+ proc = linux_add_process (event_pid, 0);
+ event_lwp = add_lwp (event_ptid);
+ event_thr = get_lwp_thread (event_lwp);
+ gdb_assert (current_thread == event_thr);
+ linux_arch_setup_thread (event_thr);
+
+ /* Set the event status. */
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_EXECD;
+ event_lwp->waitstatus.value.execd_pathname
+ = xstrdup (linux_proc_pid_to_exec_file (lwpid_of (event_thr)));
+
+ /* Mark the exec status as pending. */
+ event_lwp->stopped = 1;
+ event_lwp->status_pending_p = 1;
+ event_lwp->status_pending = wstat;
+ event_thr->last_resume_kind = resume_continue;
+ event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
+
+ /* Update syscall state in the new lwp, effectively mid-syscall too. */
+ event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
+
+ /* Restore the list to catch. Don't rely on the client, which is free
+ to avoid sending a new list when the architecture doesn't change.
+ Also, for ANY_SYSCALL, the architecture doesn't really matter. */
+ proc->syscalls_to_catch = std::move (syscalls_to_catch);
+
+ /* Report the event. */
+ *orig_event_lwp = event_lwp;
+ return 0;
+ }
+
+ internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
+}
+
+/* Return the PC as read from the regcache of LWP, without any
+ adjustment. */
+
+static CORE_ADDR
+get_pc (struct lwp_info *lwp)
+{
+ struct thread_info *saved_thread;
+ struct regcache *regcache;
+ CORE_ADDR pc;
+
+ if (the_low_target.get_pc == NULL)
+ return 0;
+
+ saved_thread = current_thread;
+ current_thread = get_lwp_thread (lwp);
+
+ regcache = get_thread_regcache (current_thread, 1);
+ pc = (*the_low_target.get_pc) (regcache);
+
+ if (debug_threads)
+ debug_printf ("pc is 0x%lx\n", (long) pc);
+
+ current_thread = saved_thread;
+ return pc;
+}
+
+/* This function should only be called if LWP got a SYSCALL_SIGTRAP.
+ Fill *SYSNO with the syscall nr trapped. */
+
+static void
+get_syscall_trapinfo (struct lwp_info *lwp, int *sysno)
+{
+ struct thread_info *saved_thread;
+ struct regcache *regcache;
+
+ if (the_low_target.get_syscall_trapinfo == NULL)
+ {
+ /* If we cannot get the syscall trapinfo, report an unknown
+ system call number. */
+ *sysno = UNKNOWN_SYSCALL;
+ return;
+ }
+
+ saved_thread = current_thread;
+ current_thread = get_lwp_thread (lwp);
+
+ regcache = get_thread_regcache (current_thread, 1);
+ (*the_low_target.get_syscall_trapinfo) (regcache, sysno);
+
+ if (debug_threads)
+ debug_printf ("get_syscall_trapinfo sysno %d\n", *sysno);
+
+ current_thread = saved_thread;
+}
+
+static int check_stopped_by_watchpoint (struct lwp_info *child);
+
+/* Called when the LWP stopped for a signal/trap. If it stopped for a
+ trap check what caused it (breakpoint, watchpoint, trace, etc.),
+ and save the result in the LWP's stop_reason field. If it stopped
+ for a breakpoint, decrement the PC if necessary on the lwp's
+ architecture. Returns true if we now have the LWP's stop PC. */
+
+static int
+save_stop_reason (struct lwp_info *lwp)
+{
+ CORE_ADDR pc;
+ CORE_ADDR sw_breakpoint_pc;
+ struct thread_info *saved_thread;
+#if USE_SIGTRAP_SIGINFO
+ siginfo_t siginfo;
+#endif
+
+ if (the_low_target.get_pc == NULL)
+ return 0;
+
+ pc = get_pc (lwp);
+ sw_breakpoint_pc = pc - the_low_target.decr_pc_after_break;
+
+ /* breakpoint_at reads from the current thread. */
+ saved_thread = current_thread;
+ current_thread = get_lwp_thread (lwp);
+
+#if USE_SIGTRAP_SIGINFO
+ if (ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
+ (PTRACE_TYPE_ARG3) 0, &siginfo) == 0)
+ {
+ if (siginfo.si_signo == SIGTRAP)
+ {
+ if (GDB_ARCH_IS_TRAP_BRKPT (siginfo.si_code)
+ && GDB_ARCH_IS_TRAP_HWBKPT (siginfo.si_code))
+ {
+ /* The si_code is ambiguous on this arch -- check debug
+ registers. */
+ if (!check_stopped_by_watchpoint (lwp))
+ lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+ }
+ else if (GDB_ARCH_IS_TRAP_BRKPT (siginfo.si_code))
+ {
+ /* If we determine the LWP stopped for a SW breakpoint,
+ trust it. Particularly don't check watchpoint
+ registers, because at least on s390, we'd find
+ stopped-by-watchpoint as long as there's a watchpoint
+ set. */
+ lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+ }
+ else if (GDB_ARCH_IS_TRAP_HWBKPT (siginfo.si_code))
+ {
+ /* This can indicate either a hardware breakpoint or
+ hardware watchpoint. Check debug registers. */
+ if (!check_stopped_by_watchpoint (lwp))
+ lwp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+ }
+ else if (siginfo.si_code == TRAP_TRACE)
+ {
+ /* We may have single stepped an instruction that
+ triggered a watchpoint. In that case, on some
+ architectures (such as x86), instead of TRAP_HWBKPT,
+ si_code indicates TRAP_TRACE, and we need to check
+ the debug registers separately. */
+ if (!check_stopped_by_watchpoint (lwp))
+ lwp->stop_reason = TARGET_STOPPED_BY_SINGLE_STEP;
+ }
+ }
+ }
+#else
+ /* We may have just stepped a breakpoint instruction. E.g., in
+ non-stop mode, GDB first tells the thread A to step a range, and
+ then the user inserts a breakpoint inside the range. In that
+ case we need to report the breakpoint PC. */
+ if ((!lwp->stepping || lwp->stop_pc == sw_breakpoint_pc)
+ && (*the_low_target.breakpoint_at) (sw_breakpoint_pc))
+ lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+
+ if (hardware_breakpoint_inserted_here (pc))
+ lwp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+
+ if (lwp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
+ check_stopped_by_watchpoint (lwp);
+#endif
+
+ if (lwp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by software breakpoint\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+
+ /* Back up the PC if necessary. */
+ if (pc != sw_breakpoint_pc)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+ (*the_low_target.set_pc) (regcache, sw_breakpoint_pc);
+ }
+
+ /* Update this so we record the correct stop PC below. */
+ pc = sw_breakpoint_pc;
+ }
+ else if (lwp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by hardware breakpoint\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+ }
+ else if (lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT)
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by hardware watchpoint\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+ }
+ else if (lwp->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP)
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by trace\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+ }
+
+ lwp->stop_pc = pc;
+ current_thread = saved_thread;
+ return 1;
+}
+
+static struct lwp_info *
+add_lwp (ptid_t ptid)
+{
+ struct lwp_info *lwp;
+
+ lwp = XCNEW (struct lwp_info);
+
+ lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+
+ lwp->thread = add_thread (ptid, lwp);
+
+ if (the_low_target.new_thread != NULL)
+ the_low_target.new_thread (lwp);
+
+ return lwp;
+}
+
+/* Callback to be used when calling fork_inferior, responsible for
+ actually initiating the tracing of the inferior. */
+
+static void
+linux_ptrace_fun ()
+{
+ if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) 0) < 0)
+ trace_start_error_with_name ("ptrace");
+
+ if (setpgid (0, 0) < 0)
+ trace_start_error_with_name ("setpgid");
+
+ /* If GDBserver is connected to gdb via stdio, redirect the inferior's
+ stdout to stderr so that inferior i/o doesn't corrupt the connection.
+ Also, redirect stdin to /dev/null. */
+ if (remote_connection_is_stdio ())
+ {
+ if (close (0) < 0)
+ trace_start_error_with_name ("close");
+ if (open ("/dev/null", O_RDONLY) < 0)
+ trace_start_error_with_name ("open");
+ if (dup2 (2, 1) < 0)
+ trace_start_error_with_name ("dup2");
+ if (write (2, "stdin/stdout redirected\n",
+ sizeof ("stdin/stdout redirected\n") - 1) < 0)
+ {
+ /* Errors ignored. */;
+ }
+ }
+}
+
+/* Start an inferior process and returns its pid.
+ PROGRAM is the name of the program to be started, and PROGRAM_ARGS
+ are its arguments. */
+
+static int
+linux_create_inferior (const char *program,
+ const std::vector<char *> &program_args)
+{
+ client_state &cs = get_client_state ();
+ struct lwp_info *new_lwp;
+ int pid;
+ ptid_t ptid;
+
+ {
+ maybe_disable_address_space_randomization restore_personality
+ (cs.disable_randomization);
+ std::string str_program_args = stringify_argv (program_args);
+
+ pid = fork_inferior (program,
+ str_program_args.c_str (),
+ get_environ ()->envp (), linux_ptrace_fun,
+ NULL, NULL, NULL, NULL);
+ }
+
+ linux_add_process (pid, 0);
+
+ ptid = ptid_t (pid, pid, 0);
+ new_lwp = add_lwp (ptid);
+ new_lwp->must_set_ptrace_flags = 1;
+
+ post_fork_inferior (pid, program);
+
+ return pid;
+}
+
+/* Implement the post_create_inferior target_ops method. */
+
+static void
+linux_post_create_inferior (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+ linux_arch_setup ();
+
+ if (lwp->must_set_ptrace_flags)
+ {
+ struct process_info *proc = current_process ();
+ int options = linux_low_ptrace_options (proc->attached);
+
+ linux_enable_event_reporting (lwpid_of (current_thread), options);
+ lwp->must_set_ptrace_flags = 0;
+ }
+}
+
+/* Attach to an inferior process. Returns 0 on success, ERRNO on
+ error. */
+
+int
+linux_attach_lwp (ptid_t ptid)
+{
+ struct lwp_info *new_lwp;
+ int lwpid = ptid.lwp ();
+
+ if (ptrace (PTRACE_ATTACH, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0)
+ != 0)
+ return errno;
+
+ new_lwp = add_lwp (ptid);
+
+ /* We need to wait for SIGSTOP before being able to make the next
+ ptrace call on this LWP. */
+ new_lwp->must_set_ptrace_flags = 1;
+
+ if (linux_proc_pid_is_stopped (lwpid))
+ {
+ if (debug_threads)
+ debug_printf ("Attached to a stopped process\n");
+
+ /* The process is definitely stopped. It is in a job control
+ stop, unless the kernel predates the TASK_STOPPED /
+ TASK_TRACED distinction, in which case it might be in a
+ ptrace stop. Make sure it is in a ptrace stop; from there we
+ can kill it, signal it, et cetera.
+
+ First make sure there is a pending SIGSTOP. Since we are
+ already attached, the process can not transition from stopped
+ to running without a PTRACE_CONT; so we know this signal will
+ go into the queue. The SIGSTOP generated by PTRACE_ATTACH is
+ probably already in the queue (unless this kernel is old
+ enough to use TASK_STOPPED for ptrace stops); but since
+ SIGSTOP is not an RT signal, it can only be queued once. */
+ kill_lwp (lwpid, SIGSTOP);
+
+ /* Finally, resume the stopped process. This will deliver the
+ SIGSTOP (or a higher priority signal, just like normal
+ PTRACE_ATTACH), which we'll catch later on. */
+ ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
+ }
+
+ /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
+ brings it to a halt.
+
+ There are several cases to consider here:
+
+ 1) gdbserver has already attached to the process and is being notified
+ of a new thread that is being created.
+ In this case we should ignore that SIGSTOP and resume the
+ process. This is handled below by setting stop_expected = 1,
+ and the fact that add_thread sets last_resume_kind ==
+ resume_continue.
+
+ 2) This is the first thread (the process thread), and we're attaching
+ to it via attach_inferior.
+ In this case we want the process thread to stop.
+ This is handled by having linux_attach set last_resume_kind ==
+ resume_stop after we return.
+
+ If the pid we are attaching to is also the tgid, we attach to and
+ stop all the existing threads. Otherwise, we attach to pid and
+ ignore any other threads in the same group as this pid.
+
+ 3) GDB is connecting to gdbserver and is requesting an enumeration of all
+ existing threads.
+ In this case we want the thread to stop.
+ FIXME: This case is currently not properly handled.
+ We should wait for the SIGSTOP but don't. Things work apparently
+ because enough time passes between when we ptrace (ATTACH) and when
+ gdb makes the next ptrace call on the thread.
+
+ On the other hand, if we are currently trying to stop all threads, we
+ should treat the new thread as if we had sent it a SIGSTOP. This works
+ because we are guaranteed that the add_lwp call above added us to the
+ end of the list, and so the new thread has not yet reached
+ wait_for_sigstop (but will). */
+ new_lwp->stop_expected = 1;
+
+ return 0;
+}
+
+/* Callback for linux_proc_attach_tgid_threads. Attach to PTID if not
+ already attached. Returns true if a new LWP is found, false
+ otherwise. */
+
+static int
+attach_proc_task_lwp_callback (ptid_t ptid)
+{
+ /* Is this a new thread? */
+ if (find_thread_ptid (ptid) == NULL)
+ {
+ int lwpid = ptid.lwp ();
+ int err;
+
+ if (debug_threads)
+ debug_printf ("Found new lwp %d\n", lwpid);
+
+ err = linux_attach_lwp (ptid);
+
+ /* Be quiet if we simply raced with the thread exiting. EPERM
+ is returned if the thread's task still exists, and is marked
+ as exited or zombie, as well as other conditions, so in that
+ case, confirm the status in /proc/PID/status. */
+ if (err == ESRCH
+ || (err == EPERM && linux_proc_pid_is_gone (lwpid)))
+ {
+ if (debug_threads)
+ {
+ debug_printf ("Cannot attach to lwp %d: "
+ "thread is gone (%d: %s)\n",
+ lwpid, err, safe_strerror (err));
+ }
+ }
+ else if (err != 0)
+ {
+ std::string reason
+ = linux_ptrace_attach_fail_reason_string (ptid, err);
+
+ warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+static void async_file_mark (void);
+
+/* Attach to PID. If PID is the tgid, attach to it and all
+ of its threads. */
+
+static int
+linux_attach (unsigned long pid)
+{
+ struct process_info *proc;
+ struct thread_info *initial_thread;
+ ptid_t ptid = ptid_t (pid, pid, 0);
+ int err;
+
+ proc = linux_add_process (pid, 1);
+
+ /* Attach to PID. We will check for other threads
+ soon. */
+ err = linux_attach_lwp (ptid);
+ if (err != 0)
+ {
+ remove_process (proc);
+
+ std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+ error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
+ }
+
+ /* Don't ignore the initial SIGSTOP if we just attached to this
+ process. It will be collected by wait shortly. */
+ initial_thread = find_thread_ptid (ptid_t (pid, pid, 0));
+ initial_thread->last_resume_kind = resume_stop;
+
+ /* We must attach to every LWP. If /proc is mounted, use that to
+ find them now. On the one hand, the inferior may be using raw
+ clone instead of using pthreads. On the other hand, even if it
+ is using pthreads, GDB may not be connected yet (thread_db needs
+ to do symbol lookups, through qSymbol). Also, thread_db walks
+ structures in the inferior's address space to find the list of
+ threads/LWPs, and those structures may well be corrupted. Note
+ that once thread_db is loaded, we'll still use it to list threads
+ and associate pthread info with each LWP. */
+ linux_proc_attach_tgid_threads (pid, attach_proc_task_lwp_callback);
+
+ /* GDB will shortly read the xml target description for this
+ process, to figure out the process' architecture. But the target
+ description is only filled in when the first process/thread in
+ the thread group reports its initial PTRACE_ATTACH SIGSTOP. Do
+ that now, otherwise, if GDB is fast enough, it could read the
+ target description _before_ that initial stop. */
+ if (non_stop)
+ {
+ struct lwp_info *lwp;
+ int wstat, lwpid;
+ ptid_t pid_ptid = ptid_t (pid);
+
+ lwpid = linux_wait_for_event_filtered (pid_ptid, pid_ptid,
+ &wstat, __WALL);
+ gdb_assert (lwpid > 0);
+
+ lwp = find_lwp_pid (ptid_t (lwpid));
+
+ if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGSTOP)
+ {
+ lwp->status_pending_p = 1;
+ lwp->status_pending = wstat;
+ }
+
+ initial_thread->last_resume_kind = resume_continue;
+
+ async_file_mark ();
+
+ gdb_assert (proc->tdesc != NULL);
+ }
+
+ return 0;
+}
+
+static int
+last_thread_of_process_p (int pid)
+{
+ bool seen_one = false;
+
+ thread_info *thread = find_thread (pid, [&] (thread_info *thr_arg)
+ {
+ if (!seen_one)
+ {
+ /* This is the first thread of this process we see. */
+ seen_one = true;
+ return false;
+ }
+ else
+ {
+ /* This is the second thread of this process we see. */
+ return true;
+ }
+ });
+
+ return thread == NULL;
+}
+
+/* Kill LWP. */
+
+static void
+linux_kill_one_lwp (struct lwp_info *lwp)
+{
+ struct thread_info *thr = get_lwp_thread (lwp);
+ int pid = lwpid_of (thr);
+
+ /* PTRACE_KILL is unreliable. After stepping into a signal handler,
+ there is no signal context, and ptrace(PTRACE_KILL) (or
+ ptrace(PTRACE_CONT, SIGKILL), pretty much the same) acts like
+ ptrace(CONT, pid, 0,0) and just resumes the tracee. A better
+ alternative is to kill with SIGKILL. We only need one SIGKILL
+ per process, not one for each thread. But since we still support
+ support debugging programs using raw clone without CLONE_THREAD,
+ we send one for each thread. For years, we used PTRACE_KILL
+ only, so we're being a bit paranoid about some old kernels where
+ PTRACE_KILL might work better (dubious if there are any such, but
+ that's why it's paranoia), so we try SIGKILL first, PTRACE_KILL
+ second, and so we're fine everywhere. */
+
+ errno = 0;
+ kill_lwp (pid, SIGKILL);
+ if (debug_threads)
+ {
+ int save_errno = errno;
+
+ debug_printf ("LKL: kill_lwp (SIGKILL) %s, 0, 0 (%s)\n",
+ target_pid_to_str (ptid_of (thr)),
+ save_errno ? safe_strerror (save_errno) : "OK");
+ }
+
+ errno = 0;
+ ptrace (PTRACE_KILL, pid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
+ if (debug_threads)
+ {
+ int save_errno = errno;
+
+ debug_printf ("LKL: PTRACE_KILL %s, 0, 0 (%s)\n",
+ target_pid_to_str (ptid_of (thr)),
+ save_errno ? safe_strerror (save_errno) : "OK");
+ }
+}
+
+/* Kill LWP and wait for it to die. */
+
+static void
+kill_wait_lwp (struct lwp_info *lwp)
+{
+ struct thread_info *thr = get_lwp_thread (lwp);
+ int pid = ptid_of (thr).pid ();
+ int lwpid = ptid_of (thr).lwp ();
+ int wstat;
+ int res;
+
+ if (debug_threads)
+ debug_printf ("kwl: killing lwp %d, for pid: %d\n", lwpid, pid);
+
+ do
+ {
+ linux_kill_one_lwp (lwp);
+
+ /* Make sure it died. Notes:
+
+ - The loop is most likely unnecessary.
+
+ - We don't use linux_wait_for_event as that could delete lwps
+ while we're iterating over them. We're not interested in
+ any pending status at this point, only in making sure all
+ wait status on the kernel side are collected until the
+ process is reaped.
+
+ - We don't use __WALL here as the __WALL emulation relies on
+ SIGCHLD, and killing a stopped process doesn't generate
+ one, nor an exit status.
+ */
+ res = my_waitpid (lwpid, &wstat, 0);
+ if (res == -1 && errno == ECHILD)
+ res = my_waitpid (lwpid, &wstat, __WCLONE);
+ } while (res > 0 && WIFSTOPPED (wstat));
+
+ /* Even if it was stopped, the child may have already disappeared.
+ E.g., if it was killed by SIGKILL. */
+ if (res < 0 && errno != ECHILD)
+ perror_with_name ("kill_wait_lwp");
+}
+
+/* Callback for `for_each_thread'. Kills an lwp of a given process,
+ except the leader. */
+
+static void
+kill_one_lwp_callback (thread_info *thread, int pid)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ /* We avoid killing the first thread here, because of a Linux kernel (at
+ least 2.6.0-test7 through 2.6.8-rc4) bug; if we kill the parent before
+ the children get a chance to be reaped, it will remain a zombie
+ forever. */
+
+ if (lwpid_of (thread) == pid)
+ {
+ if (debug_threads)
+ debug_printf ("lkop: is last of process %s\n",
+ target_pid_to_str (thread->id));
+ return;
+ }
+
+ kill_wait_lwp (lwp);
+}
+
+static int
+linux_kill (process_info *process)
+{
+ int pid = process->pid;
+
+ /* If we're killing a running inferior, make sure it is stopped
+ first, as PTRACE_KILL will not work otherwise. */
+ stop_all_lwps (0, NULL);
+
+ for_each_thread (pid, [&] (thread_info *thread)
+ {
+ kill_one_lwp_callback (thread, pid);
+ });
+
+ /* See the comment in linux_kill_one_lwp. We did not kill the first
+ thread in the list, so do so now. */
+ lwp_info *lwp = find_lwp_pid (ptid_t (pid));
+
+ if (lwp == NULL)
+ {
+ if (debug_threads)
+ debug_printf ("lk_1: cannot find lwp for pid: %d\n",
+ pid);
+ }
+ else
+ kill_wait_lwp (lwp);
+
+ the_target->mourn (process);
+
+ /* Since we presently can only stop all lwps of all processes, we
+ need to unstop lwps of other processes. */
+ unstop_all_lwps (0, NULL);
+ return 0;
+}
+
+/* Get pending signal of THREAD, for detaching purposes. This is the
+ signal the thread last stopped for, which we need to deliver to the
+ thread when detaching, otherwise, it'd be suppressed/lost. */
+
+static int
+get_detach_signal (struct thread_info *thread)
+{
+ client_state &cs = get_client_state ();
+ enum gdb_signal signo = GDB_SIGNAL_0;
+ int status;
+ struct lwp_info *lp = get_thread_lwp (thread);
+
+ if (lp->status_pending_p)
+ status = lp->status_pending;
+ else
+ {
+ /* If the thread had been suspended by gdbserver, and it stopped
+ cleanly, then it'll have stopped with SIGSTOP. But we don't
+ want to deliver that SIGSTOP. */
+ if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
+ || thread->last_status.value.sig == GDB_SIGNAL_0)
+ return 0;
+
+ /* Otherwise, we may need to deliver the signal we
+ intercepted. */
+ status = lp->last_status;
+ }
+
+ if (!WIFSTOPPED (status))
+ {
+ if (debug_threads)
+ debug_printf ("GPS: lwp %s hasn't stopped: no pending signal\n",
+ target_pid_to_str (ptid_of (thread)));
+ return 0;
+ }
+
+ /* Extended wait statuses aren't real SIGTRAPs. */
+ if (WSTOPSIG (status) == SIGTRAP && linux_is_extended_waitstatus (status))
+ {
+ if (debug_threads)
+ debug_printf ("GPS: lwp %s had stopped with extended "
+ "status: no pending signal\n",
+ target_pid_to_str (ptid_of (thread)));
+ return 0;
+ }
+
+ signo = gdb_signal_from_host (WSTOPSIG (status));
+
+ if (cs.program_signals_p && !cs.program_signals[signo])
+ {
+ if (debug_threads)
+ debug_printf ("GPS: lwp %s had signal %s, but it is in nopass state\n",
+ target_pid_to_str (ptid_of (thread)),
+ gdb_signal_to_string (signo));
+ return 0;
+ }
+ else if (!cs.program_signals_p
+ /* If we have no way to know which signals GDB does not
+ want to have passed to the program, assume
+ SIGTRAP/SIGINT, which is GDB's default. */
+ && (signo == GDB_SIGNAL_TRAP || signo == GDB_SIGNAL_INT))
+ {
+ if (debug_threads)
+ debug_printf ("GPS: lwp %s had signal %s, "
+ "but we don't know if we should pass it. "
+ "Default to not.\n",
+ target_pid_to_str (ptid_of (thread)),
+ gdb_signal_to_string (signo));
+ return 0;
+ }
+ else
+ {
+ if (debug_threads)
+ debug_printf ("GPS: lwp %s has pending signal %s: delivering it.\n",
+ target_pid_to_str (ptid_of (thread)),
+ gdb_signal_to_string (signo));
+
+ return WSTOPSIG (status);
+ }
+}
+
+/* Detach from LWP. */
+
+static void
+linux_detach_one_lwp (struct lwp_info *lwp)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+ int sig;
+ int lwpid;
+
+ /* If there is a pending SIGSTOP, get rid of it. */
+ if (lwp->stop_expected)
+ {
+ if (debug_threads)
+ debug_printf ("Sending SIGCONT to %s\n",
+ target_pid_to_str (ptid_of (thread)));
+
+ kill_lwp (lwpid_of (thread), SIGCONT);
+ lwp->stop_expected = 0;
+ }
+
+ /* Pass on any pending signal for this thread. */
+ sig = get_detach_signal (thread);
+
+ /* Preparing to resume may try to write registers, and fail if the
+ lwp is zombie. If that happens, ignore the error. We'll handle
+ it below, when detach fails with ESRCH. */
+ try
+ {
+ /* Flush any pending changes to the process's registers. */
+ regcache_invalidate_thread (thread);
+
+ /* Finally, let it resume. */
+ if (the_low_target.prepare_to_resume != NULL)
+ the_low_target.prepare_to_resume (lwp);
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ if (!check_ptrace_stopped_lwp_gone (lwp))
+ throw;
+ }
+
+ lwpid = lwpid_of (thread);
+ if (ptrace (PTRACE_DETACH, lwpid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) (long) sig) < 0)
+ {
+ int save_errno = errno;
+
+ /* We know the thread exists, so ESRCH must mean the lwp is
+ zombie. This can happen if one of the already-detached
+ threads exits the whole thread group. In that case we're
+ still attached, and must reap the lwp. */
+ if (save_errno == ESRCH)
+ {
+ int ret, status;
+
+ ret = my_waitpid (lwpid, &status, __WALL);
+ if (ret == -1)
+ {
+ warning (_("Couldn't reap LWP %d while detaching: %s"),
+ lwpid, safe_strerror (errno));
+ }
+ else if (!WIFEXITED (status) && !WIFSIGNALED (status))
+ {
+ warning (_("Reaping LWP %d while detaching "
+ "returned unexpected status 0x%x"),
+ lwpid, status);
+ }
+ }
+ else
+ {
+ error (_("Can't detach %s: %s"),
+ target_pid_to_str (ptid_of (thread)),
+ safe_strerror (save_errno));
+ }
+ }
+ else if (debug_threads)
+ {
+ debug_printf ("PTRACE_DETACH (%s, %s, 0) (OK)\n",
+ target_pid_to_str (ptid_of (thread)),
+ strsignal (sig));
+ }
+
+ delete_lwp (lwp);
+}
+
+/* Callback for for_each_thread. Detaches from non-leader threads of a
+ given process. */
+
+static void
+linux_detach_lwp_callback (thread_info *thread)
+{
+ /* We don't actually detach from the thread group leader just yet.
+ If the thread group exits, we must reap the zombie clone lwps
+ before we're able to reap the leader. */
+ if (thread->id.pid () == thread->id.lwp ())
+ return;
+
+ lwp_info *lwp = get_thread_lwp (thread);
+ linux_detach_one_lwp (lwp);
+}
+
+static int
+linux_detach (process_info *process)
+{
+ struct lwp_info *main_lwp;
+
+ /* As there's a step over already in progress, let it finish first,
+ otherwise nesting a stabilize_threads operation on top gets real
+ messy. */
+ complete_ongoing_step_over ();
+
+ /* Stop all threads before detaching. First, ptrace requires that
+ the thread is stopped to successfully detach. Second, thread_db
+ may need to uninstall thread event breakpoints from memory, which
+ only works with a stopped process anyway. */
+ stop_all_lwps (0, NULL);
+
+#ifdef USE_THREAD_DB
+ thread_db_detach (process);
+#endif
+
+ /* Stabilize threads (move out of jump pads). */
+ stabilize_threads ();
+
+ /* Detach from the clone lwps first. If the thread group exits just
+ while we're detaching, we must reap the clone lwps before we're
+ able to reap the leader. */
+ for_each_thread (process->pid, linux_detach_lwp_callback);
+
+ main_lwp = find_lwp_pid (ptid_t (process->pid));
+ linux_detach_one_lwp (main_lwp);
+
+ the_target->mourn (process);
+
+ /* Since we presently can only stop all lwps of all processes, we
+ need to unstop lwps of other processes. */
+ unstop_all_lwps (0, NULL);
+ return 0;
+}
+
+/* Remove all LWPs that belong to process PROC from the lwp list. */
+
+static void
+linux_mourn (struct process_info *process)
+{
+ struct process_info_private *priv;
+
+#ifdef USE_THREAD_DB
+ thread_db_mourn (process);
+#endif
+
+ for_each_thread (process->pid, [] (thread_info *thread)
+ {
+ delete_lwp (get_thread_lwp (thread));
+ });
+
+ /* Freeing all private data. */
+ priv = process->priv;
+ if (the_low_target.delete_process != NULL)
+ the_low_target.delete_process (priv->arch_private);
+ else
+ gdb_assert (priv->arch_private == NULL);
+ free (priv);
+ process->priv = NULL;
+
+ remove_process (process);
+}
+
+static void
+linux_join (int pid)
+{
+ int status, ret;
+
+ do {
+ ret = my_waitpid (pid, &status, 0);
+ if (WIFEXITED (status) || WIFSIGNALED (status))
+ break;
+ } while (ret != -1 || errno != ECHILD);
+}
+
+/* Return nonzero if the given thread is still alive. */
+static int
+linux_thread_alive (ptid_t ptid)
+{
+ struct lwp_info *lwp = find_lwp_pid (ptid);
+
+ /* We assume we always know if a thread exits. If a whole process
+ exited but we still haven't been able to report it to GDB, we'll
+ hold on to the last lwp of the dead process. */
+ if (lwp != NULL)
+ return !lwp_is_marked_dead (lwp);
+ else
+ return 0;
+}
+
+/* Return 1 if this lwp still has an interesting status pending. If
+ not (e.g., it had stopped for a breakpoint that is gone), return
+ false. */
+
+static int
+thread_still_has_status_pending_p (struct thread_info *thread)
+{
+ struct lwp_info *lp = get_thread_lwp (thread);
+
+ if (!lp->status_pending_p)
+ return 0;
+
+ if (thread->last_resume_kind != resume_stop
+ && (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT))
+ {
+ struct thread_info *saved_thread;
+ CORE_ADDR pc;
+ int discard = 0;
+
+ gdb_assert (lp->last_status != 0);
+
+ pc = get_pc (lp);
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ if (pc != lp->stop_pc)
+ {
+ if (debug_threads)
+ debug_printf ("PC of %ld changed\n",
+ lwpid_of (thread));
+ discard = 1;
+ }
+
+#if !USE_SIGTRAP_SIGINFO
+ else if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ && !(*the_low_target.breakpoint_at) (pc))
+ {
+ if (debug_threads)
+ debug_printf ("previous SW breakpoint of %ld gone\n",
+ lwpid_of (thread));
+ discard = 1;
+ }
+ else if (lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT
+ && !hardware_breakpoint_inserted_here (pc))
+ {
+ if (debug_threads)
+ debug_printf ("previous HW breakpoint of %ld gone\n",
+ lwpid_of (thread));
+ discard = 1;
+ }
+#endif
+
+ current_thread = saved_thread;
+
+ if (discard)
+ {
+ if (debug_threads)
+ debug_printf ("discarding pending breakpoint status\n");
+ lp->status_pending_p = 0;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Returns true if LWP is resumed from the client's perspective. */
+
+static int
+lwp_resumed (struct lwp_info *lwp)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ if (thread->last_resume_kind != resume_stop)
+ return 1;
+
+ /* Did gdb send us a `vCont;t', but we haven't reported the
+ corresponding stop to gdb yet? If so, the thread is still
+ resumed/running from gdb's perspective. */
+ if (thread->last_resume_kind == resume_stop
+ && thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+ return 1;
+
+ return 0;
+}
+
+/* Return true if this lwp has an interesting status pending. */
+static bool
+status_pending_p_callback (thread_info *thread, ptid_t ptid)
+{
+ struct lwp_info *lp = get_thread_lwp (thread);
+
+ /* Check if we're only interested in events from a specific process
+ or a specific LWP. */
+ if (!thread->id.matches (ptid))
+ return 0;
+
+ if (!lwp_resumed (lp))
+ return 0;
+
+ if (lp->status_pending_p
+ && !thread_still_has_status_pending_p (thread))
+ {
+ linux_resume_one_lwp (lp, lp->stepping, GDB_SIGNAL_0, NULL);
+ return 0;
+ }
+
+ return lp->status_pending_p;
+}
+
+struct lwp_info *
+find_lwp_pid (ptid_t ptid)
+{
+ thread_info *thread = find_thread ([&] (thread_info *thr_arg)
+ {
+ int lwp = ptid.lwp () != 0 ? ptid.lwp () : ptid.pid ();
+ return thr_arg->id.lwp () == lwp;
+ });
+
+ if (thread == NULL)
+ return NULL;
+
+ return get_thread_lwp (thread);
+}
+
+/* Return the number of known LWPs in the tgid given by PID. */
+
+static int
+num_lwps (int pid)
+{
+ int count = 0;
+
+ for_each_thread (pid, [&] (thread_info *thread)
+ {
+ count++;
+ });
+
+ return count;
+}
+
+/* See nat/linux-nat.h. */
+
+struct lwp_info *
+iterate_over_lwps (ptid_t filter,
+ gdb::function_view<iterate_over_lwps_ftype> callback)
+{
+ thread_info *thread = find_thread (filter, [&] (thread_info *thr_arg)
+ {
+ lwp_info *lwp = get_thread_lwp (thr_arg);
+
+ return callback (lwp);
+ });
+
+ if (thread == NULL)
+ return NULL;
+
+ return get_thread_lwp (thread);
+}
+
+/* Detect zombie thread group leaders, and "exit" them. We can't reap
+ their exits until all other threads in the group have exited. */
+
+static void
+check_zombie_leaders (void)
+{
+ for_each_process ([] (process_info *proc) {
+ pid_t leader_pid = pid_of (proc);
+ struct lwp_info *leader_lp;
+
+ leader_lp = find_lwp_pid (ptid_t (leader_pid));
+
+ if (debug_threads)
+ debug_printf ("leader_pid=%d, leader_lp!=NULL=%d, "
+ "num_lwps=%d, zombie=%d\n",
+ leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
+ linux_proc_pid_is_zombie (leader_pid));
+
+ if (leader_lp != NULL && !leader_lp->stopped
+ /* Check if there are other threads in the group, as we may
+ have raced with the inferior simply exiting. */
+ && !last_thread_of_process_p (leader_pid)
+ && linux_proc_pid_is_zombie (leader_pid))
+ {
+ /* A leader zombie can mean one of two things:
+
+ - It exited, and there's an exit status pending
+ available, or only the leader exited (not the whole
+ program). In the latter case, we can't waitpid the
+ leader's exit status until all other threads are gone.
+
+ - There are 3 or more threads in the group, and a thread
+ other than the leader exec'd. On an exec, the Linux
+ kernel destroys all other threads (except the execing
+ one) in the thread group, and resets the execing thread's
+ tid to the tgid. No exit notification is sent for the
+ execing thread -- from the ptracer's perspective, it
+ appears as though the execing thread just vanishes.
+ Until we reap all other threads except the leader and the
+ execing thread, the leader will be zombie, and the
+ execing thread will be in `D (disc sleep)'. As soon as
+ all other threads are reaped, the execing thread changes
+ it's tid to the tgid, and the previous (zombie) leader
+ vanishes, giving place to the "new" leader. We could try
+ distinguishing the exit and exec cases, by waiting once
+ more, and seeing if something comes out, but it doesn't
+ sound useful. The previous leader _does_ go away, and
+ we'll re-add the new one once we see the exec event
+ (which is just the same as what would happen if the
+ previous leader did exit voluntarily before some other
+ thread execs). */
+
+ if (debug_threads)
+ debug_printf ("CZL: Thread group leader %d zombie "
+ "(it exited, or another thread execd).\n",
+ leader_pid);
+
+ delete_lwp (leader_lp);
+ }
+ });
+}
+
+/* Callback for `find_thread'. Returns the first LWP that is not
+ stopped. */
+
+static bool
+not_stopped_callback (thread_info *thread, ptid_t filter)
+{
+ if (!thread->id.matches (filter))
+ return false;
+
+ lwp_info *lwp = get_thread_lwp (thread);
+
+ return !lwp->stopped;
+}
+
+/* Increment LWP's suspend count. */
+
+static void
+lwp_suspended_inc (struct lwp_info *lwp)
+{
+ lwp->suspended++;
+
+ if (debug_threads && lwp->suspended > 4)
+ {
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ debug_printf ("LWP %ld has a suspiciously high suspend count,"
+ " suspended=%d\n", lwpid_of (thread), lwp->suspended);
+ }
+}
+
+/* Decrement LWP's suspend count. */
+
+static void
+lwp_suspended_decr (struct lwp_info *lwp)
+{
+ lwp->suspended--;
+
+ if (lwp->suspended < 0)
+ {
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ internal_error (__FILE__, __LINE__,
+ "unsuspend LWP %ld, suspended=%d\n", lwpid_of (thread),
+ lwp->suspended);
+ }
+}
+
+/* This function should only be called if the LWP got a SIGTRAP.
+
+ Handle any tracepoint steps or hits. Return true if a tracepoint
+ event was handled, 0 otherwise. */
+
+static int
+handle_tracepoints (struct lwp_info *lwp)
+{
+ struct thread_info *tinfo = get_lwp_thread (lwp);
+ int tpoint_related_event = 0;
+
+ gdb_assert (lwp->suspended == 0);
+
+ /* If this tracepoint hit causes a tracing stop, we'll immediately
+ uninsert tracepoints. To do this, we temporarily pause all
+ threads, unpatch away, and then unpause threads. We need to make
+ sure the unpausing doesn't resume LWP too. */
+ lwp_suspended_inc (lwp);
+
+ /* And we need to be sure that any all-threads-stopping doesn't try
+ to move threads out of the jump pads, as it could deadlock the
+ inferior (LWP could be in the jump pad, maybe even holding the
+ lock.) */
+
+ /* Do any necessary step collect actions. */
+ tpoint_related_event |= tracepoint_finished_step (tinfo, lwp->stop_pc);
+
+ tpoint_related_event |= handle_tracepoint_bkpts (tinfo, lwp->stop_pc);
+
+ /* See if we just hit a tracepoint and do its main collect
+ actions. */
+ tpoint_related_event |= tracepoint_was_hit (tinfo, lwp->stop_pc);
+
+ lwp_suspended_decr (lwp);
+
+ gdb_assert (lwp->suspended == 0);
+ gdb_assert (!stabilizing_threads
+ || (lwp->collecting_fast_tracepoint
+ != fast_tpoint_collect_result::not_collecting));
+
+ if (tpoint_related_event)
+ {
+ if (debug_threads)
+ debug_printf ("got a tracepoint event\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Convenience wrapper. Returns information about LWP's fast tracepoint
+ collection status. */
+
+static fast_tpoint_collect_result
+linux_fast_tracepoint_collecting (struct lwp_info *lwp,
+ struct fast_tpoint_collect_status *status)
+{
+ CORE_ADDR thread_area;
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ if (the_low_target.get_thread_area == NULL)
+ return fast_tpoint_collect_result::not_collecting;
+
+ /* 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. */
+ if ((*the_low_target.get_thread_area) (lwpid_of (thread), &thread_area) == -1)
+ return fast_tpoint_collect_result::not_collecting;
+
+ return fast_tracepoint_collecting (thread_area, lwp->stop_pc, status);
+}
+
+/* The reason we resume in the caller, is because we want to be able
+ to pass lwp->status_pending as WSTAT, and we need to clear
+ status_pending_p before resuming, otherwise, linux_resume_one_lwp
+ refuses to resume. */
+
+static int
+maybe_move_out_of_jump_pad (struct lwp_info *lwp, int *wstat)
+{
+ struct thread_info *saved_thread;
+
+ saved_thread = current_thread;
+ current_thread = get_lwp_thread (lwp);
+
+ if ((wstat == NULL
+ || (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) != SIGTRAP))
+ && supports_fast_tracepoints ()
+ && agent_loaded_p ())
+ {
+ struct fast_tpoint_collect_status status;
+
+ if (debug_threads)
+ debug_printf ("Checking whether LWP %ld needs to move out of the "
+ "jump pad.\n",
+ lwpid_of (current_thread));
+
+ fast_tpoint_collect_result r
+ = linux_fast_tracepoint_collecting (lwp, &status);
+
+ if (wstat == NULL
+ || (WSTOPSIG (*wstat) != SIGILL
+ && WSTOPSIG (*wstat) != SIGFPE
+ && WSTOPSIG (*wstat) != SIGSEGV
+ && WSTOPSIG (*wstat) != SIGBUS))
+ {
+ lwp->collecting_fast_tracepoint = r;
+
+ if (r != fast_tpoint_collect_result::not_collecting)
+ {
+ if (r == fast_tpoint_collect_result::before_insn
+ && lwp->exit_jump_pad_bkpt == NULL)
+ {
+ /* Haven't executed the original instruction yet.
+ Set breakpoint there, and wait till it's hit,
+ then single-step until exiting the jump pad. */
+ lwp->exit_jump_pad_bkpt
+ = set_breakpoint_at (status.adjusted_insn_addr, NULL);
+ }
+
+ if (debug_threads)
+ debug_printf ("Checking whether LWP %ld needs to move out of "
+ "the jump pad...it does\n",
+ lwpid_of (current_thread));
+ current_thread = saved_thread;
+
+ return 1;
+ }
+ }
+ else
+ {
+ /* If we get a synchronous signal while collecting, *and*
+ while executing the (relocated) original instruction,
+ reset the PC to point at the tpoint address, before
+ reporting to GDB. Otherwise, it's an IPA lib bug: just
+ report the signal to GDB, and pray for the best. */
+
+ lwp->collecting_fast_tracepoint
+ = fast_tpoint_collect_result::not_collecting;
+
+ if (r != fast_tpoint_collect_result::not_collecting
+ && (status.adjusted_insn_addr <= lwp->stop_pc
+ && lwp->stop_pc < status.adjusted_insn_addr_end))
+ {
+ siginfo_t info;
+ struct regcache *regcache;
+
+ /* The si_addr on a few signals references the address
+ of the faulting instruction. Adjust that as
+ well. */
+ if ((WSTOPSIG (*wstat) == SIGILL
+ || WSTOPSIG (*wstat) == SIGFPE
+ || WSTOPSIG (*wstat) == SIGBUS
+ || WSTOPSIG (*wstat) == SIGSEGV)
+ && ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
+ (PTRACE_TYPE_ARG3) 0, &info) == 0
+ /* Final check just to make sure we don't clobber
+ the siginfo of non-kernel-sent signals. */
+ && (uintptr_t) info.si_addr == lwp->stop_pc)
+ {
+ info.si_addr = (void *) (uintptr_t) status.tpoint_addr;
+ ptrace (PTRACE_SETSIGINFO, lwpid_of (current_thread),
+ (PTRACE_TYPE_ARG3) 0, &info);
+ }
+
+ regcache = get_thread_regcache (current_thread, 1);
+ (*the_low_target.set_pc) (regcache, status.tpoint_addr);
+ lwp->stop_pc = status.tpoint_addr;
+
+ /* Cancel any fast tracepoint lock this thread was
+ holding. */
+ force_unlock_trace_buffer ();
+ }
+
+ if (lwp->exit_jump_pad_bkpt != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("Cancelling fast exit-jump-pad: removing bkpt. "
+ "stopping all threads momentarily.\n");
+
+ stop_all_lwps (1, lwp);
+
+ delete_breakpoint (lwp->exit_jump_pad_bkpt);
+ lwp->exit_jump_pad_bkpt = NULL;
+
+ unstop_all_lwps (1, lwp);
+
+ gdb_assert (lwp->suspended >= 0);
+ }
+ }
+ }
+
+ if (debug_threads)
+ debug_printf ("Checking whether LWP %ld needs to move out of the "
+ "jump pad...no\n",
+ lwpid_of (current_thread));
+
+ current_thread = saved_thread;
+ return 0;
+}
+
+/* Enqueue one signal in the "signals to report later when out of the
+ jump pad" list. */
+
+static void
+enqueue_one_deferred_signal (struct lwp_info *lwp, int *wstat)
+{
+ struct pending_signals *p_sig;
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ if (debug_threads)
+ debug_printf ("Deferring signal %d for LWP %ld.\n",
+ WSTOPSIG (*wstat), lwpid_of (thread));
+
+ if (debug_threads)
+ {
+ struct pending_signals *sig;
+
+ for (sig = lwp->pending_signals_to_report;
+ sig != NULL;
+ sig = sig->prev)
+ debug_printf (" Already queued %d\n",
+ sig->signal);
+
+ debug_printf (" (no more currently queued signals)\n");
+ }
+
+ /* Don't enqueue non-RT signals if they are already in the deferred
+ queue. (SIGSTOP being the easiest signal to see ending up here
+ twice) */
+ if (WSTOPSIG (*wstat) < __SIGRTMIN)
+ {
+ struct pending_signals *sig;
+
+ for (sig = lwp->pending_signals_to_report;
+ sig != NULL;
+ sig = sig->prev)
+ {
+ if (sig->signal == WSTOPSIG (*wstat))
+ {
+ if (debug_threads)
+ debug_printf ("Not requeuing already queued non-RT signal %d"
+ " for LWP %ld\n",
+ sig->signal,
+ lwpid_of (thread));
+ return;
+ }
+ }
+ }
+
+ p_sig = XCNEW (struct pending_signals);
+ p_sig->prev = lwp->pending_signals_to_report;
+ p_sig->signal = WSTOPSIG (*wstat);
+
+ ptrace (PTRACE_GETSIGINFO, lwpid_of (thread), (PTRACE_TYPE_ARG3) 0,
+ &p_sig->info);
+
+ lwp->pending_signals_to_report = p_sig;
+}
+
+/* Dequeue one signal from the "signals to report later when out of
+ the jump pad" list. */
+
+static int
+dequeue_one_deferred_signal (struct lwp_info *lwp, int *wstat)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ if (lwp->pending_signals_to_report != NULL)
+ {
+ struct pending_signals **p_sig;
+
+ p_sig = &lwp->pending_signals_to_report;
+ while ((*p_sig)->prev != NULL)
+ p_sig = &(*p_sig)->prev;
+
+ *wstat = W_STOPCODE ((*p_sig)->signal);
+ if ((*p_sig)->info.si_signo != 0)
+ ptrace (PTRACE_SETSIGINFO, lwpid_of (thread), (PTRACE_TYPE_ARG3) 0,
+ &(*p_sig)->info);
+ free (*p_sig);
+ *p_sig = NULL;
+
+ if (debug_threads)
+ debug_printf ("Reporting deferred signal %d for LWP %ld.\n",
+ WSTOPSIG (*wstat), lwpid_of (thread));
+
+ if (debug_threads)
+ {
+ struct pending_signals *sig;
+
+ for (sig = lwp->pending_signals_to_report;
+ sig != NULL;
+ sig = sig->prev)
+ debug_printf (" Still queued %d\n",
+ sig->signal);
+
+ debug_printf (" (no more queued signals)\n");
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Fetch the possibly triggered data watchpoint info and store it in
+ CHILD.
+
+ On some archs, like x86, that use debug registers to set
+ watchpoints, it's possible that the way to know which watched
+ address trapped, is to check the register that is used to select
+ which address to watch. Problem is, between setting the watchpoint
+ and reading back which data address trapped, the user may change
+ the set of watchpoints, and, as a consequence, GDB changes the
+ debug registers in the inferior. To avoid reading back a stale
+ stopped-data-address when that happens, we cache in LP the fact
+ that a watchpoint trapped, and the corresponding data address, as
+ soon as we see CHILD stop with a SIGTRAP. If GDB changes the debug
+ registers meanwhile, we have the cached data we can rely on. */
+
+static int
+check_stopped_by_watchpoint (struct lwp_info *child)
+{
+ if (the_low_target.stopped_by_watchpoint != NULL)
+ {
+ struct thread_info *saved_thread;
+
+ saved_thread = current_thread;
+ current_thread = get_lwp_thread (child);
+
+ if (the_low_target.stopped_by_watchpoint ())
+ {
+ child->stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
+
+ if (the_low_target.stopped_data_address != NULL)
+ child->stopped_data_address
+ = the_low_target.stopped_data_address ();
+ else
+ child->stopped_data_address = 0;
+ }
+
+ current_thread = saved_thread;
+ }
+
+ return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
+}
+
+/* Return the ptrace options that we want to try to enable. */
+
+static int
+linux_low_ptrace_options (int attached)
+{
+ client_state &cs = get_client_state ();
+ int options = 0;
+
+ if (!attached)
+ options |= PTRACE_O_EXITKILL;
+
+ if (cs.report_fork_events)
+ options |= PTRACE_O_TRACEFORK;
+
+ if (cs.report_vfork_events)
+ options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
+
+ if (cs.report_exec_events)
+ options |= PTRACE_O_TRACEEXEC;
+
+ options |= PTRACE_O_TRACESYSGOOD;
+
+ return options;
+}
+
+/* Do low-level handling of the event, and check if we should go on
+ and pass it to caller code. Return the affected lwp if we are, or
+ NULL otherwise. */
+
+static struct lwp_info *
+linux_low_filter_event (int lwpid, int wstat)
+{
+ client_state &cs = get_client_state ();
+ struct lwp_info *child;
+ struct thread_info *thread;
+ int have_stop_pc = 0;
+
+ child = find_lwp_pid (ptid_t (lwpid));
+
+ /* Check for stop events reported by a process we didn't already
+ know about - anything not already in our LWP list.
+
+ If we're expecting to receive stopped processes after
+ fork, vfork, and clone events, then we'll just add the
+ new one to our list and go back to waiting for the event
+ to be reported - the stopped process might be returned
+ from waitpid before or after the event is.
+
+ But note the case of a non-leader thread exec'ing after the
+ leader having exited, and gone from our lists (because
+ check_zombie_leaders deleted it). The non-leader thread
+ changes its tid to the tgid. */
+
+ if (WIFSTOPPED (wstat) && child == NULL && WSTOPSIG (wstat) == SIGTRAP
+ && linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC)
+ {
+ ptid_t child_ptid;
+
+ /* A multi-thread exec after we had seen the leader exiting. */
+ if (debug_threads)
+ {
+ debug_printf ("LLW: Re-adding thread group leader LWP %d"
+ "after exec.\n", lwpid);
+ }
+
+ child_ptid = ptid_t (lwpid, lwpid, 0);
+ child = add_lwp (child_ptid);
+ child->stopped = 1;
+ current_thread = child->thread;
+ }
+
+ /* If we didn't find a process, one of two things presumably happened:
+ - A process we started and then detached from has exited. Ignore it.
+ - A process we are controlling has forked and the new child's stop
+ was reported to us by the kernel. Save its PID. */
+ if (child == NULL && WIFSTOPPED (wstat))
+ {
+ add_to_pid_list (&stopped_pids, lwpid, wstat);
+ return NULL;
+ }
+ else if (child == NULL)
+ return NULL;
+
+ thread = get_lwp_thread (child);
+
+ child->stopped = 1;
+
+ child->last_status = wstat;
+
+ /* Check if the thread has exited. */
+ if ((WIFEXITED (wstat) || WIFSIGNALED (wstat)))
+ {
+ if (debug_threads)
+ debug_printf ("LLFE: %d exited.\n", lwpid);
+
+ if (finish_step_over (child))
+ {
+ /* Unsuspend all other LWPs, and set them back running again. */
+ unsuspend_all_lwps (child);
+ }
+
+ /* If there is at least one more LWP, then the exit signal was
+ not the end of the debugged application and should be
+ ignored, unless GDB wants to hear about thread exits. */
+ if (cs.report_thread_events
+ || last_thread_of_process_p (pid_of (thread)))
+ {
+ /* Since events are serialized to GDB core, and we can't
+ report this one right now. Leave the status pending for
+ the next time we're able to report it. */
+ mark_lwp_dead (child, wstat);
+ return child;
+ }
+ else
+ {
+ delete_lwp (child);
+ return NULL;
+ }
+ }
+
+ gdb_assert (WIFSTOPPED (wstat));
+
+ if (WIFSTOPPED (wstat))
+ {
+ struct process_info *proc;
+
+ /* Architecture-specific setup after inferior is running. */
+ proc = find_process_pid (pid_of (thread));
+ if (proc->tdesc == NULL)
+ {
+ if (proc->attached)
+ {
+ /* This needs to happen after we have attached to the
+ inferior and it is stopped for the first time, but
+ before we access any inferior registers. */
+ linux_arch_setup_thread (thread);
+ }
+ else
+ {
+ /* The process is started, but GDBserver will do
+ architecture-specific setup after the program stops at
+ the first instruction. */
+ child->status_pending_p = 1;
+ child->status_pending = wstat;
+ return child;
+ }
+ }
+ }
+
+ if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
+ {
+ struct process_info *proc = find_process_pid (pid_of (thread));
+ int options = linux_low_ptrace_options (proc->attached);
+
+ linux_enable_event_reporting (lwpid, options);
+ child->must_set_ptrace_flags = 0;
+ }
+
+ /* Always update syscall_state, even if it will be filtered later. */
+ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SYSCALL_SIGTRAP)
+ {
+ child->syscall_state
+ = (child->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
+ ? TARGET_WAITKIND_SYSCALL_RETURN
+ : TARGET_WAITKIND_SYSCALL_ENTRY);
+ }
+ else
+ {
+ /* Almost all other ptrace-stops are known to be outside of system
+ calls, with further exceptions in handle_extended_wait. */
+ child->syscall_state = TARGET_WAITKIND_IGNORE;
+ }
+
+ /* Be careful to not overwrite stop_pc until save_stop_reason is
+ called. */
+ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
+ && linux_is_extended_waitstatus (wstat))
+ {
+ child->stop_pc = get_pc (child);
+ if (handle_extended_wait (&child, wstat))
+ {
+ /* The event has been handled, so just return without
+ reporting it. */
+ return NULL;
+ }
+ }
+
+ if (linux_wstatus_maybe_breakpoint (wstat))
+ {
+ if (save_stop_reason (child))
+ have_stop_pc = 1;
+ }
+
+ if (!have_stop_pc)
+ child->stop_pc = get_pc (child);
+
+ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP
+ && child->stop_expected)
+ {
+ if (debug_threads)
+ debug_printf ("Expected stop.\n");
+ child->stop_expected = 0;
+
+ if (thread->last_resume_kind == resume_stop)
+ {
+ /* We want to report the stop to the core. Treat the
+ SIGSTOP as a normal event. */
+ if (debug_threads)
+ debug_printf ("LLW: resume_stop SIGSTOP caught for %s.\n",
+ target_pid_to_str (ptid_of (thread)));
+ }
+ else if (stopping_threads != NOT_STOPPING_THREADS)
+ {
+ /* Stopping threads. We don't want this SIGSTOP to end up
+ pending. */
+ if (debug_threads)
+ debug_printf ("LLW: SIGSTOP caught for %s "
+ "while stopping threads.\n",
+ target_pid_to_str (ptid_of (thread)));
+ return NULL;
+ }
+ else
+ {
+ /* This is a delayed SIGSTOP. Filter out the event. */
+ if (debug_threads)
+ debug_printf ("LLW: %s %s, 0, 0 (discard delayed SIGSTOP)\n",
+ child->stepping ? "step" : "continue",
+ target_pid_to_str (ptid_of (thread)));
+
+ linux_resume_one_lwp (child, child->stepping, 0, NULL);
+ return NULL;
+ }
+ }
+
+ child->status_pending_p = 1;
+ child->status_pending = wstat;
+ return child;
+}
+
+/* Return true if THREAD is doing hardware single step. */
+
+static int
+maybe_hw_step (struct thread_info *thread)
+{
+ if (can_hardware_single_step ())
+ return 1;
+ else
+ {
+ /* GDBserver must insert single-step breakpoint for software
+ single step. */
+ gdb_assert (has_single_step_breakpoints (thread));
+ return 0;
+ }
+}
+
+/* Resume LWPs that are currently stopped without any pending status
+ to report, but are resumed from the core's perspective. */
+
+static void
+resume_stopped_resumed_lwps (thread_info *thread)
+{
+ struct lwp_info *lp = get_thread_lwp (thread);
+
+ if (lp->stopped
+ && !lp->suspended
+ && !lp->status_pending_p
+ && thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+ {
+ int step = 0;
+
+ if (thread->last_resume_kind == resume_step)
+ step = maybe_hw_step (thread);
+
+ if (debug_threads)
+ debug_printf ("RSRL: resuming stopped-resumed LWP %s at %s: step=%d\n",
+ target_pid_to_str (ptid_of (thread)),
+ paddress (lp->stop_pc),
+ step);
+
+ linux_resume_one_lwp (lp, step, GDB_SIGNAL_0, NULL);
+ }
+}
+
+/* Wait for an event from child(ren) WAIT_PTID, and return any that
+ match FILTER_PTID (leaving others pending). The PTIDs can be:
+ minus_one_ptid, to specify any child; a pid PTID, specifying all
+ lwps of a thread group; or a PTID representing a single lwp. Store
+ the stop status through the status pointer WSTAT. OPTIONS is
+ passed to the waitpid call. Return 0 if no event was found and
+ OPTIONS contains WNOHANG. Return -1 if no unwaited-for children
+ was found. Return the PID of the stopped child otherwise. */
+
+static int
+linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
+ int *wstatp, int options)
+{
+ struct thread_info *event_thread;
+ struct lwp_info *event_child, *requested_child;
+ sigset_t block_mask, prev_mask;
+
+ retry:
+ /* N.B. event_thread points to the thread_info struct that contains
+ event_child. Keep them in sync. */
+ event_thread = NULL;
+ event_child = NULL;
+ requested_child = NULL;
+
+ /* Check for a lwp with a pending status. */
+
+ if (filter_ptid == minus_one_ptid || filter_ptid.is_pid ())
+ {
+ event_thread = find_thread_in_random ([&] (thread_info *thread)
+ {
+ return status_pending_p_callback (thread, filter_ptid);
+ });
+
+ if (event_thread != NULL)
+ event_child = get_thread_lwp (event_thread);
+ if (debug_threads && event_thread)
+ debug_printf ("Got a pending child %ld\n", lwpid_of (event_thread));
+ }
+ else if (filter_ptid != null_ptid)
+ {
+ requested_child = find_lwp_pid (filter_ptid);
+
+ if (stopping_threads == NOT_STOPPING_THREADS
+ && requested_child->status_pending_p
+ && (requested_child->collecting_fast_tracepoint
+ != fast_tpoint_collect_result::not_collecting))
+ {
+ enqueue_one_deferred_signal (requested_child,
+ &requested_child->status_pending);
+ requested_child->status_pending_p = 0;
+ requested_child->status_pending = 0;
+ linux_resume_one_lwp (requested_child, 0, 0, NULL);
+ }
+
+ if (requested_child->suspended
+ && requested_child->status_pending_p)
+ {
+ internal_error (__FILE__, __LINE__,
+ "requesting an event out of a"
+ " suspended child?");
+ }
+
+ if (requested_child->status_pending_p)
+ {
+ event_child = requested_child;
+ event_thread = get_lwp_thread (event_child);
+ }
+ }
+
+ if (event_child != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("Got an event from pending child %ld (%04x)\n",
+ lwpid_of (event_thread), event_child->status_pending);
+ *wstatp = event_child->status_pending;
+ event_child->status_pending_p = 0;
+ event_child->status_pending = 0;
+ current_thread = event_thread;
+ return lwpid_of (event_thread);
+ }
+
+ /* But if we don't find a pending event, we'll have to wait.
+
+ We only enter this loop if no process has a pending wait status.
+ Thus any action taken in response to a wait status inside this
+ loop is responding as soon as we detect the status, not after any
+ pending events. */
+
+ /* Make sure SIGCHLD is blocked until the sigsuspend below. Block
+ all signals while here. */
+ sigfillset (&block_mask);
+ gdb_sigmask (SIG_BLOCK, &block_mask, &prev_mask);
+
+ /* Always pull all events out of the kernel. We'll randomly select
+ an event LWP out of all that have events, to prevent
+ starvation. */
+ while (event_child == NULL)
+ {
+ pid_t ret = 0;
+
+ /* Always use -1 and WNOHANG, due to couple of a kernel/ptrace
+ quirks:
+
+ - If the thread group leader exits while other threads in the
+ thread group still exist, waitpid(TGID, ...) hangs. That
+ waitpid won't return an exit status until the other threads
+ in the group are reaped.
+
+ - When a non-leader thread execs, that thread just vanishes
+ without reporting an exit (so we'd hang if we waited for it
+ explicitly in that case). The exec event is reported to
+ the TGID pid. */
+ errno = 0;
+ ret = my_waitpid (-1, wstatp, options | WNOHANG);
+
+ if (debug_threads)
+ debug_printf ("LWFE: waitpid(-1, ...) returned %d, %s\n",
+ ret, errno ? safe_strerror (errno) : "ERRNO-OK");
+
+ if (ret > 0)
+ {
+ if (debug_threads)
+ {
+ debug_printf ("LLW: waitpid %ld received %s\n",
+ (long) ret, status_to_str (*wstatp));
+ }
+
+ /* Filter all events. IOW, leave all events pending. We'll
+ randomly select an event LWP out of all that have events
+ below. */
+ linux_low_filter_event (ret, *wstatp);
+ /* Retry until nothing comes out of waitpid. A single
+ SIGCHLD can indicate more than one child stopped. */
+ continue;
+ }
+
+ /* Now that we've pulled all events out of the kernel, resume
+ LWPs that don't have an interesting event to report. */
+ if (stopping_threads == NOT_STOPPING_THREADS)
+ for_each_thread (resume_stopped_resumed_lwps);
+
+ /* ... and find an LWP with a status to report to the core, if
+ any. */
+ event_thread = find_thread_in_random ([&] (thread_info *thread)
+ {
+ return status_pending_p_callback (thread, filter_ptid);
+ });
+
+ if (event_thread != NULL)
+ {
+ event_child = get_thread_lwp (event_thread);
+ *wstatp = event_child->status_pending;
+ event_child->status_pending_p = 0;
+ event_child->status_pending = 0;
+ break;
+ }
+
+ /* Check for zombie thread group leaders. Those can't be reaped
+ until all other threads in the thread group are. */
+ check_zombie_leaders ();
+
+ auto not_stopped = [&] (thread_info *thread)
+ {
+ return not_stopped_callback (thread, wait_ptid);
+ };
+
+ /* If there are no resumed children left in the set of LWPs we
+ want to wait for, bail. We can't just block in
+ waitpid/sigsuspend, because lwps might have been left stopped
+ in trace-stop state, and we'd be stuck forever waiting for
+ their status to change (which would only happen if we resumed
+ them). Even if WNOHANG is set, this return code is preferred
+ over 0 (below), as it is more detailed. */
+ if (find_thread (not_stopped) == NULL)
+ {
+ if (debug_threads)
+ debug_printf ("LLW: exit (no unwaited-for LWP)\n");
+ gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
+ return -1;
+ }
+
+ /* No interesting event to report to the caller. */
+ if ((options & WNOHANG))
+ {
+ if (debug_threads)
+ debug_printf ("WNOHANG set, no event found\n");
+
+ gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
+ return 0;
+ }
+
+ /* Block until we get an event reported with SIGCHLD. */
+ if (debug_threads)
+ debug_printf ("sigsuspend'ing\n");
+
+ sigsuspend (&prev_mask);
+ gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
+ goto retry;
+ }
+
+ gdb_sigmask (SIG_SETMASK, &prev_mask, NULL);
+
+ current_thread = event_thread;
+
+ return lwpid_of (event_thread);
+}
+
+/* Wait for an event from child(ren) PTID. PTIDs can be:
+ minus_one_ptid, to specify any child; a pid PTID, specifying all
+ lwps of a thread group; or a PTID representing a single lwp. Store
+ the stop status through the status pointer WSTAT. OPTIONS is
+ passed to the waitpid call. Return 0 if no event was found and
+ OPTIONS contains WNOHANG. Return -1 if no unwaited-for children
+ was found. Return the PID of the stopped child otherwise. */
+
+static int
+linux_wait_for_event (ptid_t ptid, int *wstatp, int options)
+{
+ return linux_wait_for_event_filtered (ptid, ptid, wstatp, options);
+}
+
+/* Select one LWP out of those that have events pending. */
+
+static void
+select_event_lwp (struct lwp_info **orig_lp)
+{
+ struct thread_info *event_thread = NULL;
+
+ /* In all-stop, give preference to the LWP that is being
+ single-stepped. There will be at most one, and it's the LWP that
+ the core is most interested in. If we didn't do this, then we'd
+ have to handle pending step SIGTRAPs somehow in case the core
+ later continues the previously-stepped thread, otherwise we'd
+ report the pending SIGTRAP, and the core, not having stepped the
+ thread, wouldn't understand what the trap was for, and therefore
+ would report it to the user as a random signal. */
+ if (!non_stop)
+ {
+ event_thread = find_thread ([] (thread_info *thread)
+ {
+ lwp_info *lp = get_thread_lwp (thread);
+
+ return (thread->last_status.kind == TARGET_WAITKIND_IGNORE
+ && thread->last_resume_kind == resume_step
+ && lp->status_pending_p);
+ });
+
+ if (event_thread != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("SEL: Select single-step %s\n",
+ target_pid_to_str (ptid_of (event_thread)));
+ }
+ }
+ if (event_thread == NULL)
+ {
+ /* No single-stepping LWP. Select one at random, out of those
+ which have had events. */
+
+ event_thread = find_thread_in_random ([&] (thread_info *thread)
+ {
+ lwp_info *lp = get_thread_lwp (thread);
+
+ /* Only resumed LWPs that have an event pending. */
+ return (thread->last_status.kind == TARGET_WAITKIND_IGNORE
+ && lp->status_pending_p);
+ });
+ }
+
+ if (event_thread != NULL)
+ {
+ struct lwp_info *event_lp = get_thread_lwp (event_thread);
+
+ /* Switch the event LWP. */
+ *orig_lp = event_lp;
+ }
+}
+
+/* Decrement the suspend count of all LWPs, except EXCEPT, if non
+ NULL. */
+
+static void
+unsuspend_all_lwps (struct lwp_info *except)
+{
+ for_each_thread ([&] (thread_info *thread)
+ {
+ lwp_info *lwp = get_thread_lwp (thread);
+
+ if (lwp != except)
+ lwp_suspended_decr (lwp);
+ });
+}
+
+static void move_out_of_jump_pad_callback (thread_info *thread);
+static bool stuck_in_jump_pad_callback (thread_info *thread);
+static bool lwp_running (thread_info *thread);
+static ptid_t linux_wait_1 (ptid_t ptid,
+ struct target_waitstatus *ourstatus,
+ int target_options);
+
+/* Stabilize threads (move out of jump pads).
+
+ If a thread is midway collecting a fast tracepoint, we need to
+ finish the collection and move it out of the jump pad before
+ reporting the signal.
+
+ This avoids recursion while collecting (when a signal arrives
+ midway, and the signal handler itself collects), which would trash
+ the trace buffer. In case the user set a breakpoint in a signal
+ handler, this avoids the backtrace showing the jump pad, etc..
+ Most importantly, there are certain things we can't do safely if
+ threads are stopped in a jump pad (or in its callee's). For
+ example:
+
+ - starting a new trace run. A thread still collecting the
+ previous run, could trash the trace buffer when resumed. The trace
+ buffer control structures would have been reset but the thread had
+ no way to tell. The thread could even midway memcpy'ing to the
+ buffer, which would mean that when resumed, it would clobber the
+ trace buffer that had been set for a new run.
+
+ - we can't rewrite/reuse the jump pads for new tracepoints
+ safely. Say you do tstart while a thread is stopped midway while
+ collecting. When the thread is later resumed, it finishes the
+ collection, and returns to the jump pad, to execute the original
+ instruction that was under the tracepoint jump at the time the
+ older run had been started. If the jump pad had been rewritten
+ since for something else in the new run, the thread would now
+ execute the wrong / random instructions. */
+
+static void
+linux_stabilize_threads (void)
+{
+ thread_info *thread_stuck = find_thread (stuck_in_jump_pad_callback);
+
+ if (thread_stuck != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("can't stabilize, LWP %ld is stuck in jump pad\n",
+ lwpid_of (thread_stuck));
+ return;
+ }
+
+ thread_info *saved_thread = current_thread;
+
+ stabilizing_threads = 1;
+
+ /* Kick 'em all. */
+ for_each_thread (move_out_of_jump_pad_callback);
+
+ /* Loop until all are stopped out of the jump pads. */
+ while (find_thread (lwp_running) != NULL)
+ {
+ struct target_waitstatus ourstatus;
+ struct lwp_info *lwp;
+ int wstat;
+
+ /* Note that we go through the full wait even loop. While
+ moving threads out of jump pad, we need to be able to step
+ over internal breakpoints and such. */
+ linux_wait_1 (minus_one_ptid, &ourstatus, 0);
+
+ if (ourstatus.kind == TARGET_WAITKIND_STOPPED)
+ {
+ lwp = get_thread_lwp (current_thread);
+
+ /* Lock it. */
+ lwp_suspended_inc (lwp);
+
+ if (ourstatus.value.sig != GDB_SIGNAL_0
+ || current_thread->last_resume_kind == resume_stop)
+ {
+ wstat = W_STOPCODE (gdb_signal_to_host (ourstatus.value.sig));
+ enqueue_one_deferred_signal (lwp, &wstat);
+ }
+ }
+ }
+
+ unsuspend_all_lwps (NULL);
+
+ stabilizing_threads = 0;
+
+ current_thread = saved_thread;
+
+ if (debug_threads)
+ {
+ thread_stuck = find_thread (stuck_in_jump_pad_callback);
+
+ if (thread_stuck != NULL)
+ debug_printf ("couldn't stabilize, LWP %ld got stuck in jump pad\n",
+ lwpid_of (thread_stuck));
+ }
+}
+
+/* Convenience function that is called when the kernel reports an
+ event that is not passed out to GDB. */
+
+static ptid_t
+ignore_event (struct target_waitstatus *ourstatus)
+{
+ /* If we got an event, there may still be others, as a single
+ SIGCHLD can indicate more than one child stopped. This forces
+ another target_wait call. */
+ async_file_mark ();
+
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ return null_ptid;
+}
+
+/* Convenience function that is called when the kernel reports an exit
+ event. This decides whether to report the event to GDB as a
+ process exit event, a thread exit event, or to suppress the
+ event. */
+
+static ptid_t
+filter_exit_event (struct lwp_info *event_child,
+ struct target_waitstatus *ourstatus)
+{
+ client_state &cs = get_client_state ();
+ struct thread_info *thread = get_lwp_thread (event_child);
+ ptid_t ptid = ptid_of (thread);
+
+ if (!last_thread_of_process_p (pid_of (thread)))
+ {
+ if (cs.report_thread_events)
+ ourstatus->kind = TARGET_WAITKIND_THREAD_EXITED;
+ else
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+
+ delete_lwp (event_child);
+ }
+ return ptid;
+}
+
+/* Returns 1 if GDB is interested in any event_child syscalls. */
+
+static int
+gdb_catching_syscalls_p (struct lwp_info *event_child)
+{
+ struct thread_info *thread = get_lwp_thread (event_child);
+ struct process_info *proc = get_thread_process (thread);
+
+ return !proc->syscalls_to_catch.empty ();
+}
+
+/* Returns 1 if GDB is interested in the event_child syscall.
+ Only to be called when stopped reason is SYSCALL_SIGTRAP. */
+
+static int
+gdb_catch_this_syscall_p (struct lwp_info *event_child)
+{
+ int sysno;
+ struct thread_info *thread = get_lwp_thread (event_child);
+ struct process_info *proc = get_thread_process (thread);
+
+ if (proc->syscalls_to_catch.empty ())
+ return 0;
+
+ if (proc->syscalls_to_catch[0] == ANY_SYSCALL)
+ return 1;
+
+ get_syscall_trapinfo (event_child, &sysno);
+
+ for (int iter : proc->syscalls_to_catch)
+ if (iter == sysno)
+ return 1;
+
+ return 0;
+}
+
+/* Wait for process, returns status. */
+
+static ptid_t
+linux_wait_1 (ptid_t ptid,
+ struct target_waitstatus *ourstatus, int target_options)
+{
+ client_state &cs = get_client_state ();
+ int w;
+ struct lwp_info *event_child;
+ int options;
+ int pid;
+ int step_over_finished;
+ int bp_explains_trap;
+ int maybe_internal_trap;
+ int report_to_gdb;
+ int trace_event;
+ int in_step_range;
+ int any_resumed;
+
+ if (debug_threads)
+ {
+ debug_enter ();
+ debug_printf ("linux_wait_1: [%s]\n", target_pid_to_str (ptid));
+ }
+
+ /* Translate generic target options into linux options. */
+ options = __WALL;
+ if (target_options & TARGET_WNOHANG)
+ options |= WNOHANG;
+
+ bp_explains_trap = 0;
+ trace_event = 0;
+ in_step_range = 0;
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+
+ auto status_pending_p_any = [&] (thread_info *thread)
+ {
+ return status_pending_p_callback (thread, minus_one_ptid);
+ };
+
+ auto not_stopped = [&] (thread_info *thread)
+ {
+ return not_stopped_callback (thread, minus_one_ptid);
+ };
+
+ /* Find a resumed LWP, if any. */
+ if (find_thread (status_pending_p_any) != NULL)
+ any_resumed = 1;
+ else if (find_thread (not_stopped) != NULL)
+ any_resumed = 1;
+ else
+ any_resumed = 0;
+
+ if (step_over_bkpt == null_ptid)
+ pid = linux_wait_for_event (ptid, &w, options);
+ else
+ {
+ if (debug_threads)
+ debug_printf ("step_over_bkpt set [%s], doing a blocking wait\n",
+ target_pid_to_str (step_over_bkpt));
+ pid = linux_wait_for_event (step_over_bkpt, &w, options & ~WNOHANG);
+ }
+
+ if (pid == 0 || (pid == -1 && !any_resumed))
+ {
+ gdb_assert (target_options & TARGET_WNOHANG);
+
+ if (debug_threads)
+ {
+ debug_printf ("linux_wait_1 ret = null_ptid, "
+ "TARGET_WAITKIND_IGNORE\n");
+ debug_exit ();
+ }
+
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ return null_ptid;
+ }
+ else if (pid == -1)
+ {
+ if (debug_threads)
+ {
+ debug_printf ("linux_wait_1 ret = null_ptid, "
+ "TARGET_WAITKIND_NO_RESUMED\n");
+ debug_exit ();
+ }
+
+ ourstatus->kind = TARGET_WAITKIND_NO_RESUMED;
+ return null_ptid;
+ }
+
+ event_child = get_thread_lwp (current_thread);
+
+ /* linux_wait_for_event only returns an exit status for the last
+ child of a process. Report it. */
+ if (WIFEXITED (w) || WIFSIGNALED (w))
+ {
+ if (WIFEXITED (w))
+ {
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = WEXITSTATUS (w);
+
+ if (debug_threads)
+ {
+ debug_printf ("linux_wait_1 ret = %s, exited with "
+ "retcode %d\n",
+ target_pid_to_str (ptid_of (current_thread)),
+ WEXITSTATUS (w));
+ debug_exit ();
+ }
+ }
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (w));
+
+ if (debug_threads)
+ {
+ debug_printf ("linux_wait_1 ret = %s, terminated with "
+ "signal %d\n",
+ target_pid_to_str (ptid_of (current_thread)),
+ WTERMSIG (w));
+ debug_exit ();
+ }
+ }
+
+ if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+ return filter_exit_event (event_child, ourstatus);
+
+ return ptid_of (current_thread);
+ }
+
+ /* If step-over executes a breakpoint instruction, in the case of a
+ hardware single step it means a gdb/gdbserver breakpoint had been
+ planted on top of a permanent breakpoint, in the case of a software
+ single step it may just mean that gdbserver hit the reinsert breakpoint.
+ The PC has been adjusted by save_stop_reason to point at
+ the breakpoint address.
+ So in the case of the hardware single step advance the PC manually
+ past the breakpoint and in the case of software single step advance only
+ if it's not the single_step_breakpoint we are hitting.
+ This avoids that a program would keep trapping a permanent breakpoint
+ forever. */
+ if (step_over_bkpt != null_ptid
+ && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ && (event_child->stepping
+ || !single_step_breakpoint_inserted_here (event_child->stop_pc)))
+ {
+ int increment_pc = 0;
+ int breakpoint_kind = 0;
+ CORE_ADDR stop_pc = event_child->stop_pc;
+
+ breakpoint_kind =
+ the_target->breakpoint_kind_from_current_state (&stop_pc);
+ the_target->sw_breakpoint_from_kind (breakpoint_kind, &increment_pc);
+
+ if (debug_threads)
+ {
+ debug_printf ("step-over for %s executed software breakpoint\n",
+ target_pid_to_str (ptid_of (current_thread)));
+ }
+
+ if (increment_pc != 0)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+
+ event_child->stop_pc += increment_pc;
+ (*the_low_target.set_pc) (regcache, event_child->stop_pc);
+
+ if (!(*the_low_target.breakpoint_at) (event_child->stop_pc))
+ event_child->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ }
+ }
+
+ /* If this event was not handled before, and is not a SIGTRAP, we
+ report it. SIGILL and SIGSEGV are also treated as traps in case
+ a breakpoint is inserted at the current PC. If this target does
+ not support internal breakpoints at all, we also report the
+ SIGTRAP without further processing; it's of no concern to us. */
+ maybe_internal_trap
+ = (supports_breakpoints ()
+ && (WSTOPSIG (w) == SIGTRAP
+ || ((WSTOPSIG (w) == SIGILL
+ || WSTOPSIG (w) == SIGSEGV)
+ && (*the_low_target.breakpoint_at) (event_child->stop_pc))));
+
+ if (maybe_internal_trap)
+ {
+ /* Handle anything that requires bookkeeping before deciding to
+ report the event or continue waiting. */
+
+ /* First check if we can explain the SIGTRAP with an internal
+ breakpoint, or if we should possibly report the event to GDB.
+ Do this before anything that may remove or insert a
+ breakpoint. */
+ bp_explains_trap = breakpoint_inserted_here (event_child->stop_pc);
+
+ /* We have a SIGTRAP, possibly a step-over dance has just
+ finished. If so, tweak the state machine accordingly,
+ reinsert breakpoints and delete any single-step
+ breakpoints. */
+ step_over_finished = finish_step_over (event_child);
+
+ /* Now invoke the callbacks of any internal breakpoints there. */
+ check_breakpoints (event_child->stop_pc);
+
+ /* Handle tracepoint data collecting. This may overflow the
+ trace buffer, and cause a tracing stop, removing
+ breakpoints. */
+ trace_event = handle_tracepoints (event_child);
+
+ if (bp_explains_trap)
+ {
+ if (debug_threads)
+ debug_printf ("Hit a gdbserver breakpoint.\n");
+ }
+ }
+ else
+ {
+ /* We have some other signal, possibly a step-over dance was in
+ progress, and it should be cancelled too. */
+ step_over_finished = finish_step_over (event_child);
+ }
+
+ /* We have all the data we need. Either report the event to GDB, or
+ resume threads and keep waiting for more. */
+
+ /* If we're collecting a fast tracepoint, finish the collection and
+ move out of the jump pad before delivering a signal. See
+ linux_stabilize_threads. */
+
+ if (WIFSTOPPED (w)
+ && WSTOPSIG (w) != SIGTRAP
+ && supports_fast_tracepoints ()
+ && agent_loaded_p ())
+ {
+ if (debug_threads)
+ debug_printf ("Got signal %d for LWP %ld. Check if we need "
+ "to defer or adjust it.\n",
+ WSTOPSIG (w), lwpid_of (current_thread));
+
+ /* Allow debugging the jump pad itself. */
+ if (current_thread->last_resume_kind != resume_step
+ && maybe_move_out_of_jump_pad (event_child, &w))
+ {
+ enqueue_one_deferred_signal (event_child, &w);
+
+ if (debug_threads)
+ debug_printf ("Signal %d for LWP %ld deferred (in jump pad)\n",
+ WSTOPSIG (w), lwpid_of (current_thread));
+
+ linux_resume_one_lwp (event_child, 0, 0, NULL);
+
+ if (debug_threads)
+ debug_exit ();
+ return ignore_event (ourstatus);
+ }
+ }
+
+ if (event_child->collecting_fast_tracepoint
+ != fast_tpoint_collect_result::not_collecting)
+ {
+ if (debug_threads)
+ debug_printf ("LWP %ld was trying to move out of the jump pad (%d). "
+ "Check if we're already there.\n",
+ lwpid_of (current_thread),
+ (int) event_child->collecting_fast_tracepoint);
+
+ trace_event = 1;
+
+ event_child->collecting_fast_tracepoint
+ = linux_fast_tracepoint_collecting (event_child, NULL);
+
+ if (event_child->collecting_fast_tracepoint
+ != fast_tpoint_collect_result::before_insn)
+ {
+ /* No longer need this breakpoint. */
+ if (event_child->exit_jump_pad_bkpt != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("No longer need exit-jump-pad bkpt; removing it."
+ "stopping all threads momentarily.\n");
+
+ /* Other running threads could hit this breakpoint.
+ We don't handle moribund locations like GDB does,
+ instead we always pause all threads when removing
+ breakpoints, so that any step-over or
+ decr_pc_after_break adjustment is always taken
+ care of while the breakpoint is still
+ inserted. */
+ stop_all_lwps (1, event_child);
+
+ delete_breakpoint (event_child->exit_jump_pad_bkpt);
+ event_child->exit_jump_pad_bkpt = NULL;
+
+ unstop_all_lwps (1, event_child);
+
+ gdb_assert (event_child->suspended >= 0);
+ }
+ }
+
+ if (event_child->collecting_fast_tracepoint
+ == fast_tpoint_collect_result::not_collecting)
+ {
+ if (debug_threads)
+ debug_printf ("fast tracepoint finished "
+ "collecting successfully.\n");
+
+ /* We may have a deferred signal to report. */
+ if (dequeue_one_deferred_signal (event_child, &w))
+ {
+ if (debug_threads)
+ debug_printf ("dequeued one signal.\n");
+ }
+ else
+ {
+ if (debug_threads)
+ debug_printf ("no deferred signals.\n");
+
+ if (stabilizing_threads)
+ {
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig = GDB_SIGNAL_0;
+
+ if (debug_threads)
+ {
+ debug_printf ("linux_wait_1 ret = %s, stopped "
+ "while stabilizing threads\n",
+ target_pid_to_str (ptid_of (current_thread)));
+ debug_exit ();
+ }
+
+ return ptid_of (current_thread);
+ }
+ }
+ }
+ }
+
+ /* Check whether GDB would be interested in this event. */
+
+ /* Check if GDB is interested in this syscall. */
+ if (WIFSTOPPED (w)
+ && WSTOPSIG (w) == SYSCALL_SIGTRAP
+ && !gdb_catch_this_syscall_p (event_child))
+ {
+ if (debug_threads)
+ {
+ debug_printf ("Ignored syscall for LWP %ld.\n",
+ lwpid_of (current_thread));
+ }
+
+ linux_resume_one_lwp (event_child, event_child->stepping,
+ 0, NULL);
+
+ if (debug_threads)
+ debug_exit ();
+ return ignore_event (ourstatus);
+ }
+
+ /* If GDB is not interested in this signal, don't stop other
+ threads, and don't report it to GDB. Just resume the inferior
+ right away. We do this for threading-related signals as well as
+ any that GDB specifically requested we ignore. But never ignore
+ SIGSTOP if we sent it ourselves, and do not ignore signals when
+ stepping - they may require special handling to skip the signal
+ handler. Also never ignore signals that could be caused by a
+ breakpoint. */
+ if (WIFSTOPPED (w)
+ && current_thread->last_resume_kind != resume_step
+ && (
+#if defined (USE_THREAD_DB) && !defined (__ANDROID__)
+ (current_process ()->priv->thread_db != NULL
+ && (WSTOPSIG (w) == __SIGRTMIN
+ || WSTOPSIG (w) == __SIGRTMIN + 1))
+ ||
+#endif
+ (cs.pass_signals[gdb_signal_from_host (WSTOPSIG (w))]
+ && !(WSTOPSIG (w) == SIGSTOP
+ && current_thread->last_resume_kind == resume_stop)
+ && !linux_wstatus_maybe_breakpoint (w))))
+ {
+ siginfo_t info, *info_p;
+
+ if (debug_threads)
+ debug_printf ("Ignored signal %d for LWP %ld.\n",
+ WSTOPSIG (w), lwpid_of (current_thread));
+
+ if (ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
+ (PTRACE_TYPE_ARG3) 0, &info) == 0)
+ info_p = &info;
+ else
+ info_p = NULL;
+
+ if (step_over_finished)
+ {
+ /* We cancelled this thread's step-over above. We still
+ need to unsuspend all other LWPs, and set them back
+ running again while the signal handler runs. */
+ unsuspend_all_lwps (event_child);
+
+ /* Enqueue the pending signal info so that proceed_all_lwps
+ doesn't lose it. */
+ enqueue_pending_signal (event_child, WSTOPSIG (w), info_p);
+
+ proceed_all_lwps ();
+ }
+ else
+ {
+ linux_resume_one_lwp (event_child, event_child->stepping,
+ WSTOPSIG (w), info_p);
+ }
+
+ if (debug_threads)
+ debug_exit ();
+
+ return ignore_event (ourstatus);
+ }
+
+ /* Note that all addresses are always "out of the step range" when
+ there's no range to begin with. */
+ in_step_range = lwp_in_step_range (event_child);
+
+ /* If GDB wanted this thread to single step, and the thread is out
+ of the step range, we always want to report the SIGTRAP, and let
+ GDB handle it. Watchpoints should always be reported. So should
+ signals we can't explain. A SIGTRAP we can't explain could be a
+ GDB breakpoint --- we may or not support Z0 breakpoints. If we
+ do, we're be able to handle GDB breakpoints on top of internal
+ breakpoints, by handling the internal breakpoint and still
+ reporting the event to GDB. If we don't, we're out of luck, GDB
+ won't see the breakpoint hit. If we see a single-step event but
+ the thread should be continuing, don't pass the trap to gdb.
+ That indicates that we had previously finished a single-step but
+ left the single-step pending -- see
+ complete_ongoing_step_over. */
+ report_to_gdb = (!maybe_internal_trap
+ || (current_thread->last_resume_kind == resume_step
+ && !in_step_range)
+ || event_child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT
+ || (!in_step_range
+ && !bp_explains_trap
+ && !trace_event
+ && !step_over_finished
+ && !(current_thread->last_resume_kind == resume_continue
+ && event_child->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP))
+ || (gdb_breakpoint_here (event_child->stop_pc)
+ && gdb_condition_true_at_breakpoint (event_child->stop_pc)
+ && gdb_no_commands_at_breakpoint (event_child->stop_pc))
+ || event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE);
+
+ run_breakpoint_commands (event_child->stop_pc);
+
+ /* We found no reason GDB would want us to stop. We either hit one
+ of our own breakpoints, or finished an internal step GDB
+ shouldn't know about. */
+ if (!report_to_gdb)
+ {
+ if (debug_threads)
+ {
+ if (bp_explains_trap)
+ debug_printf ("Hit a gdbserver breakpoint.\n");
+ if (step_over_finished)
+ debug_printf ("Step-over finished.\n");
+ if (trace_event)
+ debug_printf ("Tracepoint event.\n");
+ if (lwp_in_step_range (event_child))
+ debug_printf ("Range stepping pc 0x%s [0x%s, 0x%s).\n",
+ paddress (event_child->stop_pc),
+ paddress (event_child->step_range_start),
+ paddress (event_child->step_range_end));
+ }
+
+ /* We're not reporting this breakpoint to GDB, so apply the
+ decr_pc_after_break adjustment to the inferior's regcache
+ ourselves. */
+
+ if (the_low_target.set_pc != NULL)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+ (*the_low_target.set_pc) (regcache, event_child->stop_pc);
+ }
+
+ if (step_over_finished)
+ {
+ /* If we have finished stepping over a breakpoint, we've
+ stopped and suspended all LWPs momentarily except the
+ stepping one. This is where we resume them all again.
+ We're going to keep waiting, so use proceed, which
+ handles stepping over the next breakpoint. */
+ unsuspend_all_lwps (event_child);
+ }
+ else
+ {
+ /* Remove the single-step breakpoints if any. Note that
+ there isn't single-step breakpoint if we finished stepping
+ over. */
+ if (can_software_single_step ()
+ && has_single_step_breakpoints (current_thread))
+ {
+ stop_all_lwps (0, event_child);
+ delete_single_step_breakpoints (current_thread);
+ unstop_all_lwps (0, event_child);
+ }
+ }
+
+ if (debug_threads)
+ debug_printf ("proceeding all threads.\n");
+ proceed_all_lwps ();
+
+ if (debug_threads)
+ debug_exit ();
+
+ return ignore_event (ourstatus);
+ }
+
+ if (debug_threads)
+ {
+ if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ {
+ std::string str
+ = target_waitstatus_to_string (&event_child->waitstatus);
+
+ debug_printf ("LWP %ld: extended event with waitstatus %s\n",
+ lwpid_of (get_lwp_thread (event_child)), str.c_str ());
+ }
+ if (current_thread->last_resume_kind == resume_step)
+ {
+ if (event_child->step_range_start == event_child->step_range_end)
+ debug_printf ("GDB wanted to single-step, reporting event.\n");
+ else if (!lwp_in_step_range (event_child))
+ debug_printf ("Out of step range, reporting event.\n");
+ }
+ if (event_child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT)
+ debug_printf ("Stopped by watchpoint.\n");
+ else if (gdb_breakpoint_here (event_child->stop_pc))
+ debug_printf ("Stopped by GDB breakpoint.\n");
+ if (debug_threads)
+ debug_printf ("Hit a non-gdbserver trap event.\n");
+ }
+
+ /* Alright, we're going to report a stop. */
+
+ /* Remove single-step breakpoints. */
+ if (can_software_single_step ())
+ {
+ /* Remove single-step breakpoints or not. It it is true, stop all
+ lwps, so that other threads won't hit the breakpoint in the
+ staled memory. */
+ int remove_single_step_breakpoints_p = 0;
+
+ if (non_stop)
+ {
+ remove_single_step_breakpoints_p
+ = has_single_step_breakpoints (current_thread);
+ }
+ else
+ {
+ /* In all-stop, a stop reply cancels all previous resume
+ requests. Delete all single-step breakpoints. */
+
+ find_thread ([&] (thread_info *thread) {
+ if (has_single_step_breakpoints (thread))
+ {
+ remove_single_step_breakpoints_p = 1;
+ return true;
+ }
+
+ return false;
+ });
+ }
+
+ if (remove_single_step_breakpoints_p)
+ {
+ /* If we remove single-step breakpoints from memory, stop all lwps,
+ so that other threads won't hit the breakpoint in the staled
+ memory. */
+ stop_all_lwps (0, event_child);
+
+ if (non_stop)
+ {
+ gdb_assert (has_single_step_breakpoints (current_thread));
+ delete_single_step_breakpoints (current_thread);
+ }
+ else
+ {
+ for_each_thread ([] (thread_info *thread){
+ if (has_single_step_breakpoints (thread))
+ delete_single_step_breakpoints (thread);
+ });
+ }
+
+ unstop_all_lwps (0, event_child);
+ }
+ }
+
+ if (!stabilizing_threads)
+ {
+ /* In all-stop, stop all threads. */
+ if (!non_stop)
+ stop_all_lwps (0, NULL);
+
+ if (step_over_finished)
+ {
+ if (!non_stop)
+ {
+ /* If we were doing a step-over, all other threads but
+ the stepping one had been paused in start_step_over,
+ with their suspend counts incremented. We don't want
+ to do a full unstop/unpause, because we're in
+ all-stop mode (so we want threads stopped), but we
+ still need to unsuspend the other threads, to
+ decrement their `suspended' count back. */
+ unsuspend_all_lwps (event_child);
+ }
+ else
+ {
+ /* If we just finished a step-over, then all threads had
+ been momentarily paused. In all-stop, that's fine,
+ we want threads stopped by now anyway. In non-stop,
+ we need to re-resume threads that GDB wanted to be
+ running. */
+ unstop_all_lwps (1, event_child);
+ }
+ }
+
+ /* If we're not waiting for a specific LWP, choose an event LWP
+ from among those that have had events. Giving equal priority
+ to all LWPs that have had events helps prevent
+ starvation. */
+ if (ptid == minus_one_ptid)
+ {
+ event_child->status_pending_p = 1;
+ event_child->status_pending = w;
+
+ select_event_lwp (&event_child);
+
+ /* current_thread and event_child must stay in sync. */
+ current_thread = get_lwp_thread (event_child);
+
+ event_child->status_pending_p = 0;
+ w = event_child->status_pending;
+ }
+
+
+ /* Stabilize threads (move out of jump pads). */
+ if (!non_stop)
+ stabilize_threads ();
+ }
+ else
+ {
+ /* If we just finished a step-over, then all threads had been
+ momentarily paused. In all-stop, that's fine, we want
+ threads stopped by now anyway. In non-stop, we need to
+ re-resume threads that GDB wanted to be running. */
+ if (step_over_finished)
+ unstop_all_lwps (1, event_child);
+ }
+
+ if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ {
+ /* If the reported event is an exit, fork, vfork or exec, let
+ GDB know. */
+
+ /* Break the unreported fork relationship chain. */
+ if (event_child->waitstatus.kind == TARGET_WAITKIND_FORKED
+ || event_child->waitstatus.kind == TARGET_WAITKIND_VFORKED)
+ {
+ event_child->fork_relative->fork_relative = NULL;
+ event_child->fork_relative = NULL;
+ }
+
+ *ourstatus = event_child->waitstatus;
+ /* Clear the event lwp's waitstatus since we handled it already. */
+ event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ }
+ else
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+ /* Now that we've selected our final event LWP, un-adjust its PC if
+ it was a software breakpoint, and the client doesn't know we can
+ adjust the breakpoint ourselves. */
+ if (event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ && !cs.swbreak_feature)
+ {
+ int decr_pc = the_low_target.decr_pc_after_break;
+
+ if (decr_pc != 0)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+ (*the_low_target.set_pc) (regcache, event_child->stop_pc + decr_pc);
+ }
+ }
+
+ if (WSTOPSIG (w) == SYSCALL_SIGTRAP)
+ {
+ get_syscall_trapinfo (event_child,
+ &ourstatus->value.syscall_number);
+ ourstatus->kind = event_child->syscall_state;
+ }
+ else if (current_thread->last_resume_kind == resume_stop
+ && WSTOPSIG (w) == SIGSTOP)
+ {
+ /* A thread that has been requested to stop by GDB with vCont;t,
+ and it stopped cleanly, so report as SIG0. The use of
+ SIGSTOP is an implementation detail. */
+ ourstatus->value.sig = GDB_SIGNAL_0;
+ }
+ else if (current_thread->last_resume_kind == resume_stop
+ && WSTOPSIG (w) != SIGSTOP)
+ {
+ /* A thread that has been requested to stop by GDB with vCont;t,
+ but, it stopped for other reasons. */
+ ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
+ }
+ else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
+ {
+ ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
+ }
+
+ gdb_assert (step_over_bkpt == null_ptid);
+
+ if (debug_threads)
+ {
+ debug_printf ("linux_wait_1 ret = %s, %d, %d\n",
+ target_pid_to_str (ptid_of (current_thread)),
+ ourstatus->kind, ourstatus->value.sig);
+ debug_exit ();
+ }
+
+ if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+ return filter_exit_event (event_child, ourstatus);
+
+ return ptid_of (current_thread);
+}
+
+/* Get rid of any pending event in the pipe. */
+static void
+async_file_flush (void)
+{
+ int ret;
+ char buf;
+
+ do
+ ret = read (linux_event_pipe[0], &buf, 1);
+ while (ret >= 0 || (ret == -1 && errno == EINTR));
+}
+
+/* Put something in the pipe, so the event loop wakes up. */
+static void
+async_file_mark (void)
+{
+ int ret;
+
+ async_file_flush ();
+
+ do
+ ret = write (linux_event_pipe[1], "+", 1);
+ while (ret == 0 || (ret == -1 && errno == EINTR));
+
+ /* Ignore EAGAIN. If the pipe is full, the event loop will already
+ be awakened anyway. */
+}
+
+static ptid_t
+linux_wait (ptid_t ptid,
+ struct target_waitstatus *ourstatus, int target_options)
+{
+ ptid_t event_ptid;
+
+ /* Flush the async file first. */
+ if (target_is_async_p ())
+ async_file_flush ();
+
+ do
+ {
+ event_ptid = linux_wait_1 (ptid, ourstatus, target_options);
+ }
+ while ((target_options & TARGET_WNOHANG) == 0
+ && event_ptid == null_ptid
+ && ourstatus->kind == TARGET_WAITKIND_IGNORE);
+
+ /* If at least one stop was reported, there may be more. A single
+ SIGCHLD can signal more than one child stop. */
+ if (target_is_async_p ()
+ && (target_options & TARGET_WNOHANG) != 0
+ && event_ptid != null_ptid)
+ async_file_mark ();
+
+ return event_ptid;
+}
+
+/* Send a signal to an LWP. */
+
+static int
+kill_lwp (unsigned long lwpid, int signo)
+{
+ int ret;
+
+ errno = 0;
+ ret = syscall (__NR_tkill, lwpid, signo);
+ if (errno == ENOSYS)
+ {
+ /* If tkill fails, then we are not using nptl threads, a
+ configuration we no longer support. */
+ perror_with_name (("tkill"));
+ }
+ return ret;
+}
+
+void
+linux_stop_lwp (struct lwp_info *lwp)
+{
+ send_sigstop (lwp);
+}
+
+static void
+send_sigstop (struct lwp_info *lwp)
+{
+ int pid;
+
+ pid = lwpid_of (get_lwp_thread (lwp));
+
+ /* If we already have a pending stop signal for this process, don't
+ send another. */
+ if (lwp->stop_expected)
+ {
+ if (debug_threads)
+ debug_printf ("Have pending sigstop for lwp %d\n", pid);
+
+ return;
+ }
+
+ if (debug_threads)
+ debug_printf ("Sending sigstop to lwp %d\n", pid);
+
+ lwp->stop_expected = 1;
+ kill_lwp (pid, SIGSTOP);
+}
+
+static void
+send_sigstop (thread_info *thread, lwp_info *except)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ /* Ignore EXCEPT. */
+ if (lwp == except)
+ return;
+
+ if (lwp->stopped)
+ return;
+
+ send_sigstop (lwp);
+}
+
+/* Increment the suspend count of an LWP, and stop it, if not stopped
+ yet. */
+static void
+suspend_and_send_sigstop (thread_info *thread, lwp_info *except)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ /* Ignore EXCEPT. */
+ if (lwp == except)
+ return;
+
+ lwp_suspended_inc (lwp);
+
+ send_sigstop (thread, except);
+}
+
+static void
+mark_lwp_dead (struct lwp_info *lwp, int wstat)
+{
+ /* Store the exit status for later. */
+ lwp->status_pending_p = 1;
+ lwp->status_pending = wstat;
+
+ /* Store in waitstatus as well, as there's nothing else to process
+ for this event. */
+ if (WIFEXITED (wstat))
+ {
+ lwp->waitstatus.kind = TARGET_WAITKIND_EXITED;
+ lwp->waitstatus.value.integer = WEXITSTATUS (wstat);
+ }
+ else if (WIFSIGNALED (wstat))
+ {
+ lwp->waitstatus.kind = TARGET_WAITKIND_SIGNALLED;
+ lwp->waitstatus.value.sig = gdb_signal_from_host (WTERMSIG (wstat));
+ }
+
+ /* Prevent trying to stop it. */
+ lwp->stopped = 1;
+
+ /* No further stops are expected from a dead lwp. */
+ lwp->stop_expected = 0;
+}
+
+/* Return true if LWP has exited already, and has a pending exit event
+ to report to GDB. */
+
+static int
+lwp_is_marked_dead (struct lwp_info *lwp)
+{
+ return (lwp->status_pending_p
+ && (WIFEXITED (lwp->status_pending)
+ || WIFSIGNALED (lwp->status_pending)));
+}
+
+/* Wait for all children to stop for the SIGSTOPs we just queued. */
+
+static void
+wait_for_sigstop (void)
+{
+ struct thread_info *saved_thread;
+ ptid_t saved_tid;
+ int wstat;
+ int ret;
+
+ saved_thread = current_thread;
+ if (saved_thread != NULL)
+ saved_tid = saved_thread->id;
+ else
+ saved_tid = null_ptid; /* avoid bogus unused warning */
+
+ if (debug_threads)
+ debug_printf ("wait_for_sigstop: pulling events\n");
+
+ /* Passing NULL_PTID as filter indicates we want all events to be
+ left pending. Eventually this returns when there are no
+ unwaited-for children left. */
+ ret = linux_wait_for_event_filtered (minus_one_ptid, null_ptid,
+ &wstat, __WALL);
+ gdb_assert (ret == -1);
+
+ if (saved_thread == NULL || linux_thread_alive (saved_tid))
+ current_thread = saved_thread;
+ else
+ {
+ if (debug_threads)
+ debug_printf ("Previously current thread died.\n");
+
+ /* We can't change the current inferior behind GDB's back,
+ otherwise, a subsequent command may apply to the wrong
+ process. */
+ current_thread = NULL;
+ }
+}
+
+/* Returns true if THREAD is stopped in a jump pad, and we can't
+ move it out, because we need to report the stop event to GDB. For
+ example, if the user puts a breakpoint in the jump pad, it's
+ because she wants to debug it. */
+
+static bool
+stuck_in_jump_pad_callback (thread_info *thread)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ if (lwp->suspended != 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "LWP %ld is suspended, suspended=%d\n",
+ lwpid_of (thread), lwp->suspended);
+ }
+ gdb_assert (lwp->stopped);
+
+ /* Allow debugging the jump pad, gdb_collect, etc.. */
+ return (supports_fast_tracepoints ()
+ && agent_loaded_p ()
+ && (gdb_breakpoint_here (lwp->stop_pc)
+ || lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT
+ || thread->last_resume_kind == resume_step)
+ && (linux_fast_tracepoint_collecting (lwp, NULL)
+ != fast_tpoint_collect_result::not_collecting));
+}
+
+static void
+move_out_of_jump_pad_callback (thread_info *thread)
+{
+ struct thread_info *saved_thread;
+ struct lwp_info *lwp = get_thread_lwp (thread);
+ int *wstat;
+
+ if (lwp->suspended != 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "LWP %ld is suspended, suspended=%d\n",
+ lwpid_of (thread), lwp->suspended);
+ }
+ gdb_assert (lwp->stopped);
+
+ /* For gdb_breakpoint_here. */
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ wstat = lwp->status_pending_p ? &lwp->status_pending : NULL;
+
+ /* Allow debugging the jump pad, gdb_collect, etc. */
+ if (!gdb_breakpoint_here (lwp->stop_pc)
+ && lwp->stop_reason != TARGET_STOPPED_BY_WATCHPOINT
+ && thread->last_resume_kind != resume_step
+ && maybe_move_out_of_jump_pad (lwp, wstat))
+ {
+ if (debug_threads)
+ debug_printf ("LWP %ld needs stabilizing (in jump pad)\n",
+ lwpid_of (thread));
+
+ if (wstat)
+ {
+ lwp->status_pending_p = 0;
+ enqueue_one_deferred_signal (lwp, wstat);
+
+ if (debug_threads)
+ debug_printf ("Signal %d for LWP %ld deferred "
+ "(in jump pad)\n",
+ WSTOPSIG (*wstat), lwpid_of (thread));
+ }
+
+ linux_resume_one_lwp (lwp, 0, 0, NULL);
+ }
+ else
+ lwp_suspended_inc (lwp);
+
+ current_thread = saved_thread;
+}
+
+static bool
+lwp_running (thread_info *thread)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ if (lwp_is_marked_dead (lwp))
+ return false;
+
+ return !lwp->stopped;
+}
+
+/* Stop all lwps that aren't stopped yet, except EXCEPT, if not NULL.
+ If SUSPEND, then also increase the suspend count of every LWP,
+ except EXCEPT. */
+
+static void
+stop_all_lwps (int suspend, struct lwp_info *except)
+{
+ /* Should not be called recursively. */
+ gdb_assert (stopping_threads == NOT_STOPPING_THREADS);
+
+ if (debug_threads)
+ {
+ debug_enter ();
+ debug_printf ("stop_all_lwps (%s, except=%s)\n",
+ suspend ? "stop-and-suspend" : "stop",
+ except != NULL
+ ? target_pid_to_str (ptid_of (get_lwp_thread (except)))
+ : "none");
+ }
+
+ stopping_threads = (suspend
+ ? STOPPING_AND_SUSPENDING_THREADS
+ : STOPPING_THREADS);
+
+ if (suspend)
+ for_each_thread ([&] (thread_info *thread)
+ {
+ suspend_and_send_sigstop (thread, except);
+ });
+ else
+ for_each_thread ([&] (thread_info *thread)
+ {
+ send_sigstop (thread, except);
+ });
+
+ wait_for_sigstop ();
+ stopping_threads = NOT_STOPPING_THREADS;
+
+ if (debug_threads)
+ {
+ debug_printf ("stop_all_lwps done, setting stopping_threads "
+ "back to !stopping\n");
+ debug_exit ();
+ }
+}
+
+/* Enqueue one signal in the chain of signals which need to be
+ delivered to this process on next resume. */
+
+static void
+enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info)
+{
+ struct pending_signals *p_sig = XNEW (struct pending_signals);
+
+ p_sig->prev = lwp->pending_signals;
+ p_sig->signal = signal;
+ if (info == NULL)
+ memset (&p_sig->info, 0, sizeof (siginfo_t));
+ else
+ memcpy (&p_sig->info, info, sizeof (siginfo_t));
+ lwp->pending_signals = p_sig;
+}
+
+/* Install breakpoints for software single stepping. */
+
+static void
+install_software_single_step_breakpoints (struct lwp_info *lwp)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+ struct regcache *regcache = get_thread_regcache (thread, 1);
+
+ scoped_restore save_current_thread = make_scoped_restore (¤t_thread);
+
+ current_thread = thread;
+ std::vector<CORE_ADDR> next_pcs = the_low_target.get_next_pcs (regcache);
+
+ for (CORE_ADDR pc : next_pcs)
+ set_single_step_breakpoint (pc, current_ptid);
+}
+
+/* Single step via hardware or software single step.
+ Return 1 if hardware single stepping, 0 if software single stepping
+ or can't single step. */
+
+static int
+single_step (struct lwp_info* lwp)
+{
+ int step = 0;
+
+ if (can_hardware_single_step ())
+ {
+ step = 1;
+ }
+ else if (can_software_single_step ())
+ {
+ install_software_single_step_breakpoints (lwp);
+ step = 0;
+ }
+ else
+ {
+ if (debug_threads)
+ debug_printf ("stepping is not implemented on this target");
+ }
+
+ return step;
+}
+
+/* The signal can be delivered to the inferior if we are not trying to
+ finish a fast tracepoint collect. Since signal can be delivered in
+ the step-over, the program may go to signal handler and trap again
+ after return from the signal handler. We can live with the spurious
+ double traps. */
+
+static int
+lwp_signal_can_be_delivered (struct lwp_info *lwp)
+{
+ return (lwp->collecting_fast_tracepoint
+ == fast_tpoint_collect_result::not_collecting);
+}
+
+/* Resume execution of LWP. If STEP is nonzero, single-step it. If
+ SIGNAL is nonzero, give it that signal. */
+
+static void
+linux_resume_one_lwp_throw (struct lwp_info *lwp,
+ int step, int signal, siginfo_t *info)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+ struct thread_info *saved_thread;
+ int ptrace_request;
+ struct process_info *proc = get_thread_process (thread);
+
+ /* Note that target description may not be initialised
+ (proc->tdesc == NULL) at this point because the program hasn't
+ stopped at the first instruction yet. It means GDBserver skips
+ the extra traps from the wrapper program (see option --wrapper).
+ Code in this function that requires register access should be
+ guarded by proc->tdesc == NULL or something else. */
+
+ if (lwp->stopped == 0)
+ return;
+
+ gdb_assert (lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
+
+ fast_tpoint_collect_result fast_tp_collecting
+ = lwp->collecting_fast_tracepoint;
+
+ gdb_assert (!stabilizing_threads
+ || (fast_tp_collecting
+ != fast_tpoint_collect_result::not_collecting));
+
+ /* Cancel actions that rely on GDB not changing the PC (e.g., the
+ user used the "jump" command, or "set $pc = foo"). */
+ if (thread->while_stepping != NULL && lwp->stop_pc != get_pc (lwp))
+ {
+ /* Collecting 'while-stepping' actions doesn't make sense
+ anymore. */
+ release_while_stepping_state_list (thread);
+ }
+
+ /* If we have pending signals or status, and a new signal, enqueue the
+ signal. Also enqueue the signal if it can't be delivered to the
+ inferior right now. */
+ if (signal != 0
+ && (lwp->status_pending_p
+ || lwp->pending_signals != NULL
+ || !lwp_signal_can_be_delivered (lwp)))
+ {
+ enqueue_pending_signal (lwp, signal, info);
+
+ /* Postpone any pending signal. It was enqueued above. */
+ signal = 0;
+ }
+
+ if (lwp->status_pending_p)
+ {
+ if (debug_threads)
+ debug_printf ("Not resuming lwp %ld (%s, stop %s);"
+ " has pending status\n",
+ lwpid_of (thread), step ? "step" : "continue",
+ lwp->stop_expected ? "expected" : "not expected");
+ return;
+ }
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ /* This bit needs some thinking about. If we get a signal that
+ we must report while a single-step reinsert is still pending,
+ we often end up resuming the thread. It might be better to
+ (ew) allow a stack of pending events; then we could be sure that
+ the reinsert happened right away and not lose any signals.
+
+ Making this stack would also shrink the window in which breakpoints are
+ uninserted (see comment in linux_wait_for_lwp) but not enough for
+ complete correctness, so it won't solve that problem. It may be
+ worthwhile just to solve this one, however. */
+ if (lwp->bp_reinsert != 0)
+ {
+ if (debug_threads)
+ debug_printf (" pending reinsert at 0x%s\n",
+ paddress (lwp->bp_reinsert));
+
+ if (can_hardware_single_step ())
+ {
+ if (fast_tp_collecting == fast_tpoint_collect_result::not_collecting)
+ {
+ if (step == 0)
+ warning ("BAD - reinserting but not stepping.");
+ if (lwp->suspended)
+ warning ("BAD - reinserting and suspended(%d).",
+ lwp->suspended);
+ }
+ }
+
+ step = maybe_hw_step (thread);
+ }
+
+ if (fast_tp_collecting == fast_tpoint_collect_result::before_insn)
+ {
+ if (debug_threads)
+ debug_printf ("lwp %ld wants to get out of fast tracepoint jump pad"
+ " (exit-jump-pad-bkpt)\n",
+ lwpid_of (thread));
+ }
+ else if (fast_tp_collecting == fast_tpoint_collect_result::at_insn)
+ {
+ if (debug_threads)
+ debug_printf ("lwp %ld wants to get out of fast tracepoint jump pad"
+ " single-stepping\n",
+ lwpid_of (thread));
+
+ if (can_hardware_single_step ())
+ step = 1;
+ else
+ {
+ internal_error (__FILE__, __LINE__,
+ "moving out of jump pad single-stepping"
+ " not implemented on this target");
+ }
+ }
+
+ /* If we have while-stepping actions in this thread set it stepping.
+ If we have a signal to deliver, it may or may not be set to
+ SIG_IGN, we don't know. Assume so, and allow collecting
+ while-stepping into a signal handler. A possible smart thing to
+ do would be to set an internal breakpoint at the signal return
+ address, continue, and carry on catching this while-stepping
+ action only when that breakpoint is hit. A future
+ enhancement. */
+ if (thread->while_stepping != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
+ lwpid_of (thread));
+
+ step = single_step (lwp);
+ }
+
+ if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
+ {
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+
+ lwp->stop_pc = (*the_low_target.get_pc) (regcache);
+
+ if (debug_threads)
+ {
+ debug_printf (" %s from pc 0x%lx\n", step ? "step" : "continue",
+ (long) lwp->stop_pc);
+ }
+ }
+
+ /* If we have pending signals, consume one if it can be delivered to
+ the inferior. */
+ if (lwp->pending_signals != NULL && lwp_signal_can_be_delivered (lwp))
+ {
+ struct pending_signals **p_sig;
+
+ p_sig = &lwp->pending_signals;
+ while ((*p_sig)->prev != NULL)
+ p_sig = &(*p_sig)->prev;
+
+ signal = (*p_sig)->signal;
+ if ((*p_sig)->info.si_signo != 0)
+ ptrace (PTRACE_SETSIGINFO, lwpid_of (thread), (PTRACE_TYPE_ARG3) 0,
+ &(*p_sig)->info);
+
+ free (*p_sig);
+ *p_sig = NULL;
+ }
+
+ if (debug_threads)
+ debug_printf ("Resuming lwp %ld (%s, signal %d, stop %s)\n",
+ lwpid_of (thread), step ? "step" : "continue", signal,
+ lwp->stop_expected ? "expected" : "not expected");
+
+ if (the_low_target.prepare_to_resume != NULL)
+ the_low_target.prepare_to_resume (lwp);
+
+ regcache_invalidate_thread (thread);
+ errno = 0;
+ lwp->stepping = step;
+ if (step)
+ ptrace_request = PTRACE_SINGLESTEP;
+ else if (gdb_catching_syscalls_p (lwp))
+ ptrace_request = PTRACE_SYSCALL;
+ else
+ ptrace_request = PTRACE_CONT;
+ ptrace (ptrace_request,
+ lwpid_of (thread),
+ (PTRACE_TYPE_ARG3) 0,
+ /* Coerce to a uintptr_t first to avoid potential gcc warning
+ of coercing an 8 byte integer to a 4 byte pointer. */
+ (PTRACE_TYPE_ARG4) (uintptr_t) signal);
+
+ current_thread = saved_thread;
+ if (errno)
+ perror_with_name ("resuming thread");
+
+ /* Successfully resumed. Clear state that no longer makes sense,
+ and mark the LWP as running. Must not do this before resuming
+ otherwise if that fails other code will be confused. E.g., we'd
+ later try to stop the LWP and hang forever waiting for a stop
+ status. Note that we must not throw after this is cleared,
+ otherwise handle_zombie_lwp_error would get confused. */
+ lwp->stopped = 0;
+ lwp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+}
+
+/* Called when we try to resume a stopped LWP and that errors out. If
+ the LWP is no longer in ptrace-stopped state (meaning it's zombie,
+ or about to become), discard the error, clear any pending status
+ the LWP may have, and return true (we'll collect the exit status
+ soon enough). Otherwise, return false. */
+
+static int
+check_ptrace_stopped_lwp_gone (struct lwp_info *lp)
+{
+ struct thread_info *thread = get_lwp_thread (lp);
+
+ /* If we get an error after resuming the LWP successfully, we'd
+ confuse !T state for the LWP being gone. */
+ gdb_assert (lp->stopped);
+
+ /* We can't just check whether the LWP is in 'Z (Zombie)' state,
+ because even if ptrace failed with ESRCH, the tracee may be "not
+ yet fully dead", but already refusing ptrace requests. In that
+ case the tracee has 'R (Running)' state for a little bit
+ (observed in Linux 3.18). See also the note on ESRCH in the
+ ptrace(2) man page. Instead, check whether the LWP has any state
+ other than ptrace-stopped. */
+
+ /* Don't assume anything if /proc/PID/status can't be read. */
+ if (linux_proc_pid_is_trace_stopped_nowarn (lwpid_of (thread)) == 0)
+ {
+ lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ lp->status_pending_p = 0;
+ return 1;
+ }
+ return 0;
+}
+
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
+ disappears while we try to resume it. */
+
+static void
+linux_resume_one_lwp (struct lwp_info *lwp,
+ int step, int signal, siginfo_t *info)
+{
+ try
+ {
+ linux_resume_one_lwp_throw (lwp, step, signal, info);
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ if (!check_ptrace_stopped_lwp_gone (lwp))
+ throw;
+ }
+}
+
+/* This function is called once per thread via for_each_thread.
+ We look up which resume request applies to THREAD and mark it with a
+ pointer to the appropriate resume request.
+
+ This algorithm is O(threads * resume elements), but resume elements
+ is small (and will remain small at least until GDB supports thread
+ suspension). */
+
+static void
+linux_set_resume_request (thread_info *thread, thread_resume *resume, size_t n)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ for (int ndx = 0; ndx < n; ndx++)
+ {
+ ptid_t ptid = resume[ndx].thread;
+ if (ptid == minus_one_ptid
+ || ptid == thread->id
+ /* Handle both 'pPID' and 'pPID.-1' as meaning 'all threads
+ of PID'. */
+ || (ptid.pid () == pid_of (thread)
+ && (ptid.is_pid ()
+ || ptid.lwp () == -1)))
+ {
+ if (resume[ndx].kind == resume_stop
+ && thread->last_resume_kind == resume_stop)
+ {
+ if (debug_threads)
+ debug_printf ("already %s LWP %ld at GDB's request\n",
+ (thread->last_status.kind
+ == TARGET_WAITKIND_STOPPED)
+ ? "stopped"
+ : "stopping",
+ lwpid_of (thread));
+
+ continue;
+ }
+
+ /* Ignore (wildcard) resume requests for already-resumed
+ threads. */
+ if (resume[ndx].kind != resume_stop
+ && thread->last_resume_kind != resume_stop)
+ {
+ if (debug_threads)
+ debug_printf ("already %s LWP %ld at GDB's request\n",
+ (thread->last_resume_kind
+ == resume_step)
+ ? "stepping"
+ : "continuing",
+ lwpid_of (thread));
+ continue;
+ }
+
+ /* Don't let wildcard resumes resume fork children that GDB
+ does not yet know are new fork children. */
+ if (lwp->fork_relative != NULL)
+ {
+ struct lwp_info *rel = lwp->fork_relative;
+
+ if (rel->status_pending_p
+ && (rel->waitstatus.kind == TARGET_WAITKIND_FORKED
+ || rel->waitstatus.kind == TARGET_WAITKIND_VFORKED))
+ {
+ if (debug_threads)
+ debug_printf ("not resuming LWP %ld: has queued stop reply\n",
+ lwpid_of (thread));
+ continue;
+ }
+ }
+
+ /* If the thread has a pending event that has already been
+ reported to GDBserver core, but GDB has not pulled the
+ event out of the vStopped queue yet, likewise, ignore the
+ (wildcard) resume request. */
+ if (in_queued_stop_replies (thread->id))
+ {
+ if (debug_threads)
+ debug_printf ("not resuming LWP %ld: has queued stop reply\n",
+ lwpid_of (thread));
+ continue;
+ }
+
+ lwp->resume = &resume[ndx];
+ thread->last_resume_kind = lwp->resume->kind;
+
+ lwp->step_range_start = lwp->resume->step_range_start;
+ lwp->step_range_end = lwp->resume->step_range_end;
+
+ /* If we had a deferred signal to report, dequeue one now.
+ This can happen if LWP gets more than one signal while
+ trying to get out of a jump pad. */
+ if (lwp->stopped
+ && !lwp->status_pending_p
+ && dequeue_one_deferred_signal (lwp, &lwp->status_pending))
+ {
+ lwp->status_pending_p = 1;
+
+ if (debug_threads)
+ debug_printf ("Dequeueing deferred signal %d for LWP %ld, "
+ "leaving status pending.\n",
+ WSTOPSIG (lwp->status_pending),
+ lwpid_of (thread));
+ }
+
+ return;
+ }
+ }
+
+ /* No resume action for this thread. */
+ lwp->resume = NULL;
+}
+
+/* find_thread callback for linux_resume. Return true if this lwp has an
+ interesting status pending. */
+
+static bool
+resume_status_pending_p (thread_info *thread)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ /* LWPs which will not be resumed are not interesting, because
+ we might not wait for them next time through linux_wait. */
+ if (lwp->resume == NULL)
+ return false;
+
+ return thread_still_has_status_pending_p (thread);
+}
+
+/* Return 1 if this lwp that GDB wants running is stopped at an
+ internal breakpoint that we need to step over. It assumes that any
+ required STOP_PC adjustment has already been propagated to the
+ inferior's regcache. */
+
+static bool
+need_step_over_p (thread_info *thread)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+ struct thread_info *saved_thread;
+ CORE_ADDR pc;
+ struct process_info *proc = get_thread_process (thread);
+
+ /* GDBserver is skipping the extra traps from the wrapper program,
+ don't have to do step over. */
+ if (proc->tdesc == NULL)
+ return false;
+
+ /* LWPs which will not be resumed are not interesting, because we
+ might not wait for them next time through linux_wait. */
+
+ if (!lwp->stopped)
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? Ignoring, not stopped\n",
+ lwpid_of (thread));
+ return false;
+ }
+
+ if (thread->last_resume_kind == resume_stop)
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? Ignoring, should remain"
+ " stopped\n",
+ lwpid_of (thread));
+ return false;
+ }
+
+ gdb_assert (lwp->suspended >= 0);
+
+ if (lwp->suspended)
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? Ignoring, suspended\n",
+ lwpid_of (thread));
+ return false;
+ }
+
+ if (lwp->status_pending_p)
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? Ignoring, has pending"
+ " status.\n",
+ lwpid_of (thread));
+ return false;
+ }
+
+ /* Note: PC, not STOP_PC. Either GDB has adjusted the PC already,
+ or we have. */
+ pc = get_pc (lwp);
+
+ /* If the PC has changed since we stopped, then don't do anything,
+ and let the breakpoint/tracepoint be hit. This happens if, for
+ instance, GDB handled the decr_pc_after_break subtraction itself,
+ GDB is OOL stepping this thread, or the user has issued a "jump"
+ command, or poked thread's registers herself. */
+ if (pc != lwp->stop_pc)
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? Cancelling, PC was changed. "
+ "Old stop_pc was 0x%s, PC is now 0x%s\n",
+ lwpid_of (thread),
+ paddress (lwp->stop_pc), paddress (pc));
+ return false;
+ }
+
+ /* On software single step target, resume the inferior with signal
+ rather than stepping over. */
+ if (can_software_single_step ()
+ && lwp->pending_signals != NULL
+ && lwp_signal_can_be_delivered (lwp))
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? Ignoring, has pending"
+ " signals.\n",
+ lwpid_of (thread));
+
+ return false;
+ }
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ /* We can only step over breakpoints we know about. */
+ if (breakpoint_here (pc) || fast_tracepoint_jump_here (pc))
+ {
+ /* Don't step over a breakpoint that GDB expects to hit
+ though. If the condition is being evaluated on the target's side
+ and it evaluate to false, step over this breakpoint as well. */
+ if (gdb_breakpoint_here (pc)
+ && gdb_condition_true_at_breakpoint (pc)
+ && gdb_no_commands_at_breakpoint (pc))
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? yes, but found"
+ " GDB breakpoint at 0x%s; skipping step over\n",
+ lwpid_of (thread), paddress (pc));
+
+ current_thread = saved_thread;
+ return false;
+ }
+ else
+ {
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? yes, "
+ "found breakpoint at 0x%s\n",
+ lwpid_of (thread), paddress (pc));
+
+ /* We've found an lwp that needs stepping over --- return 1 so
+ that find_thread stops looking. */
+ current_thread = saved_thread;
+
+ return true;
+ }
+ }
+
+ current_thread = saved_thread;
+
+ if (debug_threads)
+ debug_printf ("Need step over [LWP %ld]? No, no breakpoint found"
+ " at 0x%s\n",
+ lwpid_of (thread), paddress (pc));
+
+ return false;
+}
+
+/* Start a step-over operation on LWP. When LWP stopped at a
+ breakpoint, to make progress, we need to remove the breakpoint out
+ of the way. If we let other threads run while we do that, they may
+ pass by the breakpoint location and miss hitting it. To avoid
+ that, a step-over momentarily stops all threads while LWP is
+ single-stepped by either hardware or software while the breakpoint
+ is temporarily uninserted from the inferior. When the single-step
+ finishes, we reinsert the breakpoint, and let all threads that are
+ supposed to be running, run again. */
+
+static int
+start_step_over (struct lwp_info *lwp)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+ struct thread_info *saved_thread;
+ CORE_ADDR pc;
+ int step;
+
+ if (debug_threads)
+ debug_printf ("Starting step-over on LWP %ld. Stopping all threads\n",
+ lwpid_of (thread));
+
+ stop_all_lwps (1, lwp);
+
+ if (lwp->suspended != 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "LWP %ld suspended=%d\n", lwpid_of (thread),
+ lwp->suspended);
+ }
+
+ if (debug_threads)
+ debug_printf ("Done stopping all threads for step-over.\n");
+
+ /* Note, we should always reach here with an already adjusted PC,
+ either by GDB (if we're resuming due to GDB's request), or by our
+ caller, if we just finished handling an internal breakpoint GDB
+ shouldn't care about. */
+ pc = get_pc (lwp);
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ lwp->bp_reinsert = pc;
+ uninsert_breakpoints_at (pc);
+ uninsert_fast_tracepoint_jumps_at (pc);
+
+ step = single_step (lwp);
+
+ current_thread = saved_thread;
+
+ linux_resume_one_lwp (lwp, step, 0, NULL);
+
+ /* Require next event from this LWP. */
+ step_over_bkpt = thread->id;
+ return 1;
+}
+
+/* Finish a step-over. Reinsert the breakpoint we had uninserted in
+ start_step_over, if still there, and delete any single-step
+ breakpoints we've set, on non hardware single-step targets. */
+
+static int
+finish_step_over (struct lwp_info *lwp)
+{
+ if (lwp->bp_reinsert != 0)
+ {
+ struct thread_info *saved_thread = current_thread;
+
+ if (debug_threads)
+ debug_printf ("Finished step over.\n");
+
+ current_thread = get_lwp_thread (lwp);
+
+ /* Reinsert any breakpoint at LWP->BP_REINSERT. Note that there
+ may be no breakpoint to reinsert there by now. */
+ reinsert_breakpoints_at (lwp->bp_reinsert);
+ reinsert_fast_tracepoint_jumps_at (lwp->bp_reinsert);
+
+ lwp->bp_reinsert = 0;
+
+ /* Delete any single-step breakpoints. No longer needed. We
+ don't have to worry about other threads hitting this trap,
+ and later not being able to explain it, because we were
+ stepping over a breakpoint, and we hold all threads but
+ LWP stopped while doing that. */
+ if (!can_hardware_single_step ())
+ {
+ gdb_assert (has_single_step_breakpoints (current_thread));
+ delete_single_step_breakpoints (current_thread);
+ }
+
+ step_over_bkpt = null_ptid;
+ current_thread = saved_thread;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* If there's a step over in progress, wait until all threads stop
+ (that is, until the stepping thread finishes its step), and
+ unsuspend all lwps. The stepping thread ends with its status
+ pending, which is processed later when we get back to processing
+ events. */
+
+static void
+complete_ongoing_step_over (void)
+{
+ if (step_over_bkpt != null_ptid)
+ {
+ struct lwp_info *lwp;
+ int wstat;
+ int ret;
+
+ if (debug_threads)
+ debug_printf ("detach: step over in progress, finish it first\n");
+
+ /* Passing NULL_PTID as filter indicates we want all events to
+ be left pending. Eventually this returns when there are no
+ unwaited-for children left. */
+ ret = linux_wait_for_event_filtered (minus_one_ptid, null_ptid,
+ &wstat, __WALL);
+ gdb_assert (ret == -1);
+
+ lwp = find_lwp_pid (step_over_bkpt);
+ if (lwp != NULL)
+ finish_step_over (lwp);
+ step_over_bkpt = null_ptid;
+ unsuspend_all_lwps (lwp);
+ }
+}
+
+/* This function is called once per thread. We check the thread's resume
+ request, which will tell us whether to resume, step, or leave the thread
+ stopped; and what signal, if any, it should be sent.
+
+ For threads which we aren't explicitly told otherwise, we preserve
+ the stepping flag; this is used for stepping over gdbserver-placed
+ breakpoints.
+
+ If pending_flags was set in any thread, we queue any needed
+ signals, since we won't actually resume. We already have a pending
+ event to report, so we don't need to preserve any step requests;
+ they should be re-issued if necessary. */
+
+static void
+linux_resume_one_thread (thread_info *thread, bool leave_all_stopped)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+ int leave_pending;
+
+ if (lwp->resume == NULL)
+ return;
+
+ if (lwp->resume->kind == resume_stop)
+ {
+ if (debug_threads)
+ debug_printf ("resume_stop request for LWP %ld\n", lwpid_of (thread));
+
+ if (!lwp->stopped)
+ {
+ if (debug_threads)
+ debug_printf ("stopping LWP %ld\n", lwpid_of (thread));
+
+ /* Stop the thread, and wait for the event asynchronously,
+ through the event loop. */
+ send_sigstop (lwp);
+ }
+ else
+ {
+ if (debug_threads)
+ debug_printf ("already stopped LWP %ld\n",
+ lwpid_of (thread));
+
+ /* The LWP may have been stopped in an internal event that
+ was not meant to be notified back to GDB (e.g., gdbserver
+ breakpoint), so we should be reporting a stop event in
+ this case too. */
+
+ /* If the thread already has a pending SIGSTOP, this is a
+ no-op. Otherwise, something later will presumably resume
+ the thread and this will cause it to cancel any pending
+ operation, due to last_resume_kind == resume_stop. If
+ the thread already has a pending status to report, we
+ will still report it the next time we wait - see
+ status_pending_p_callback. */
+
+ /* If we already have a pending signal to report, then
+ there's no need to queue a SIGSTOP, as this means we're
+ midway through moving the LWP out of the jumppad, and we
+ will report the pending signal as soon as that is
+ finished. */
+ if (lwp->pending_signals_to_report == NULL)
+ send_sigstop (lwp);
+ }
+
+ /* For stop requests, we're done. */
+ lwp->resume = NULL;
+ thread->last_status.kind = TARGET_WAITKIND_IGNORE;
+ return;
+ }
+
+ /* If this thread which is about to be resumed has a pending status,
+ then don't resume it - we can just report the pending status.
+ Likewise if it is suspended, because e.g., another thread is
+ stepping past a breakpoint. Make sure to queue any signals that
+ would otherwise be sent. In all-stop mode, we do this decision
+ based on if *any* thread has a pending status. If there's a
+ thread that needs the step-over-breakpoint dance, then don't
+ resume any other thread but that particular one. */
+ leave_pending = (lwp->suspended
+ || lwp->status_pending_p
+ || leave_all_stopped);
+
+ /* If we have a new signal, enqueue the signal. */
+ if (lwp->resume->sig != 0)
+ {
+ siginfo_t info, *info_p;
+
+ /* If this is the same signal we were previously stopped by,
+ make sure to queue its siginfo. */
+ if (WIFSTOPPED (lwp->last_status)
+ && WSTOPSIG (lwp->last_status) == lwp->resume->sig
+ && ptrace (PTRACE_GETSIGINFO, lwpid_of (thread),
+ (PTRACE_TYPE_ARG3) 0, &info) == 0)
+ info_p = &info;
+ else
+ info_p = NULL;
+
+ enqueue_pending_signal (lwp, lwp->resume->sig, info_p);
+ }
+
+ if (!leave_pending)
+ {
+ if (debug_threads)
+ debug_printf ("resuming LWP %ld\n", lwpid_of (thread));
+
+ proceed_one_lwp (thread, NULL);
+ }
+ else
+ {
+ if (debug_threads)
+ debug_printf ("leaving LWP %ld stopped\n", lwpid_of (thread));
+ }
+
+ thread->last_status.kind = TARGET_WAITKIND_IGNORE;
+ lwp->resume = NULL;
+}
+
+static void
+linux_resume (struct thread_resume *resume_info, size_t n)
+{
+ struct thread_info *need_step_over = NULL;
+
+ if (debug_threads)
+ {
+ debug_enter ();
+ debug_printf ("linux_resume:\n");
+ }
+
+ for_each_thread ([&] (thread_info *thread)
+ {
+ linux_set_resume_request (thread, resume_info, n);
+ });
+
+ /* If there is a thread which would otherwise be resumed, which has
+ a pending status, then don't resume any threads - we can just
+ report the pending status. Make sure to queue any signals that
+ would otherwise be sent. In non-stop mode, we'll apply this
+ logic to each thread individually. We consume all pending events
+ before considering to start a step-over (in all-stop). */
+ bool any_pending = false;
+ if (!non_stop)
+ any_pending = find_thread (resume_status_pending_p) != NULL;
+
+ /* If there is a thread which would otherwise be resumed, which is
+ stopped at a breakpoint that needs stepping over, then don't
+ resume any threads - have it step over the breakpoint with all
+ other threads stopped, then resume all threads again. Make sure
+ to queue any signals that would otherwise be delivered or
+ queued. */
+ if (!any_pending && supports_breakpoints ())
+ need_step_over = find_thread (need_step_over_p);
+
+ bool leave_all_stopped = (need_step_over != NULL || any_pending);
+
+ if (debug_threads)
+ {
+ if (need_step_over != NULL)
+ debug_printf ("Not resuming all, need step over\n");
+ else if (any_pending)
+ debug_printf ("Not resuming, all-stop and found "
+ "an LWP with pending status\n");
+ else
+ debug_printf ("Resuming, no pending status or step over needed\n");
+ }
+
+ /* Even if we're leaving threads stopped, queue all signals we'd
+ otherwise deliver. */
+ for_each_thread ([&] (thread_info *thread)
+ {
+ linux_resume_one_thread (thread, leave_all_stopped);
+ });
+
+ if (need_step_over)
+ start_step_over (get_thread_lwp (need_step_over));
+
+ if (debug_threads)
+ {
+ debug_printf ("linux_resume done\n");
+ debug_exit ();
+ }
+
+ /* We may have events that were pending that can/should be sent to
+ the client now. Trigger a linux_wait call. */
+ if (target_is_async_p ())
+ async_file_mark ();
+}
+
+/* This function is called once per thread. We check the thread's
+ last resume request, which will tell us whether to resume, step, or
+ leave the thread stopped. Any signal the client requested to be
+ delivered has already been enqueued at this point.
+
+ If any thread that GDB wants running is stopped at an internal
+ breakpoint that needs stepping over, we start a step-over operation
+ on that particular thread, and leave all others stopped. */
+
+static void
+proceed_one_lwp (thread_info *thread, lwp_info *except)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+ int step;
+
+ if (lwp == except)
+ return;
+
+ if (debug_threads)
+ debug_printf ("proceed_one_lwp: lwp %ld\n", lwpid_of (thread));
+
+ if (!lwp->stopped)
+ {
+ if (debug_threads)
+ debug_printf (" LWP %ld already running\n", lwpid_of (thread));
+ return;
+ }
+
+ if (thread->last_resume_kind == resume_stop
+ && thread->last_status.kind != TARGET_WAITKIND_IGNORE)
+ {
+ if (debug_threads)
+ debug_printf (" client wants LWP to remain %ld stopped\n",
+ lwpid_of (thread));
+ return;
+ }
+
+ if (lwp->status_pending_p)
+ {
+ if (debug_threads)
+ debug_printf (" LWP %ld has pending status, leaving stopped\n",
+ lwpid_of (thread));
+ return;
+ }
+
+ gdb_assert (lwp->suspended >= 0);
+
+ if (lwp->suspended)
+ {
+ if (debug_threads)
+ debug_printf (" LWP %ld is suspended\n", lwpid_of (thread));
+ return;
+ }
+
+ if (thread->last_resume_kind == resume_stop
+ && lwp->pending_signals_to_report == NULL
+ && (lwp->collecting_fast_tracepoint
+ == fast_tpoint_collect_result::not_collecting))
+ {
+ /* We haven't reported this LWP as stopped yet (otherwise, the
+ last_status.kind check above would catch it, and we wouldn't
+ reach here. This LWP may have been momentarily paused by a
+ stop_all_lwps call while handling for example, another LWP's
+ step-over. In that case, the pending expected SIGSTOP signal
+ that was queued at vCont;t handling time will have already
+ been consumed by wait_for_sigstop, and so we need to requeue
+ another one here. Note that if the LWP already has a SIGSTOP
+ pending, this is a no-op. */
+
+ if (debug_threads)
+ debug_printf ("Client wants LWP %ld to stop. "
+ "Making sure it has a SIGSTOP pending\n",
+ lwpid_of (thread));
+
+ send_sigstop (lwp);
+ }
+
+ if (thread->last_resume_kind == resume_step)
+ {
+ if (debug_threads)
+ debug_printf (" stepping LWP %ld, client wants it stepping\n",
+ lwpid_of (thread));
+
+ /* If resume_step is requested by GDB, install single-step
+ breakpoints when the thread is about to be actually resumed if
+ the single-step breakpoints weren't removed. */
+ if (can_software_single_step ()
+ && !has_single_step_breakpoints (thread))
+ install_software_single_step_breakpoints (lwp);
+
+ step = maybe_hw_step (thread);
+ }
+ else if (lwp->bp_reinsert != 0)
+ {
+ if (debug_threads)
+ debug_printf (" stepping LWP %ld, reinsert set\n",
+ lwpid_of (thread));
+
+ step = maybe_hw_step (thread);
+ }
+ else
+ step = 0;
+
+ linux_resume_one_lwp (lwp, step, 0, NULL);
+}
+
+static void
+unsuspend_and_proceed_one_lwp (thread_info *thread, lwp_info *except)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ if (lwp == except)
+ return;
+
+ lwp_suspended_decr (lwp);
+
+ proceed_one_lwp (thread, except);
+}
+
+/* When we finish a step-over, set threads running again. If there's
+ another thread that may need a step-over, now's the time to start
+ it. Eventually, we'll move all threads past their breakpoints. */
+
+static void
+proceed_all_lwps (void)
+{
+ struct thread_info *need_step_over;
+
+ /* If there is a thread which would otherwise be resumed, which is
+ stopped at a breakpoint that needs stepping over, then don't
+ resume any threads - have it step over the breakpoint with all
+ other threads stopped, then resume all threads again. */
+
+ if (supports_breakpoints ())
+ {
+ need_step_over = find_thread (need_step_over_p);
+
+ if (need_step_over != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("proceed_all_lwps: found "
+ "thread %ld needing a step-over\n",
+ lwpid_of (need_step_over));
+
+ start_step_over (get_thread_lwp (need_step_over));
+ return;
+ }
+ }
+
+ if (debug_threads)
+ debug_printf ("Proceeding, no step-over needed\n");
+
+ for_each_thread ([] (thread_info *thread)
+ {
+ proceed_one_lwp (thread, NULL);
+ });
+}
+
+/* Stopped LWPs that the client wanted to be running, that don't have
+ pending statuses, are set to run again, except for EXCEPT, if not
+ NULL. This undoes a stop_all_lwps call. */
+
+static void
+unstop_all_lwps (int unsuspend, struct lwp_info *except)
+{
+ if (debug_threads)
+ {
+ debug_enter ();
+ if (except)
+ debug_printf ("unstopping all lwps, except=(LWP %ld)\n",
+ lwpid_of (get_lwp_thread (except)));
+ else
+ debug_printf ("unstopping all lwps\n");
+ }
+
+ if (unsuspend)
+ for_each_thread ([&] (thread_info *thread)
+ {
+ unsuspend_and_proceed_one_lwp (thread, except);
+ });
+ else
+ for_each_thread ([&] (thread_info *thread)
+ {
+ proceed_one_lwp (thread, except);
+ });
+
+ if (debug_threads)
+ {
+ debug_printf ("unstop_all_lwps done\n");
+ debug_exit ();
+ }
+}
+
+
+#ifdef HAVE_LINUX_REGSETS
+
+#define use_linux_regsets 1
+
+/* Returns true if REGSET has been disabled. */
+
+static int
+regset_disabled (struct regsets_info *info, struct regset_info *regset)
+{
+ return (info->disabled_regsets != NULL
+ && info->disabled_regsets[regset - info->regsets]);
+}
+
+/* Disable REGSET. */
+
+static void
+disable_regset (struct regsets_info *info, struct regset_info *regset)
+{
+ int dr_offset;
+
+ dr_offset = regset - info->regsets;
+ if (info->disabled_regsets == NULL)
+ info->disabled_regsets = (char *) xcalloc (1, info->num_regsets);
+ info->disabled_regsets[dr_offset] = 1;
+}
+
+static int
+regsets_fetch_inferior_registers (struct regsets_info *regsets_info,
+ struct regcache *regcache)
+{
+ struct regset_info *regset;
+ int saw_general_regs = 0;
+ int pid;
+ struct iovec iov;
+
+ pid = lwpid_of (current_thread);
+ for (regset = regsets_info->regsets; regset->size >= 0; regset++)
+ {
+ void *buf, *data;
+ int nt_type, res;
+
+ if (regset->size == 0 || regset_disabled (regsets_info, regset))
+ continue;
+
+ buf = xmalloc (regset->size);
+
+ nt_type = regset->nt_type;
+ if (nt_type)
+ {
+ iov.iov_base = buf;
+ iov.iov_len = regset->size;
+ data = (void *) &iov;
+ }
+ else
+ data = buf;
+
+#ifndef __sparc__
+ res = ptrace (regset->get_request, pid,
+ (PTRACE_TYPE_ARG3) (long) nt_type, data);
+#else
+ res = ptrace (regset->get_request, pid, data, nt_type);
+#endif
+ if (res < 0)
+ {
+ if (errno == EIO
+ || (errno == EINVAL && regset->type == OPTIONAL_REGS))
+ {
+ /* If we get EIO on a regset, or an EINVAL and the regset is
+ optional, do not try it again for this process mode. */
+ disable_regset (regsets_info, regset);
+ }
+ else if (errno == ENODATA)
+ {
+ /* ENODATA may be returned if the regset is currently
+ not "active". This can happen in normal operation,
+ so suppress the warning in this case. */
+ }
+ else if (errno == ESRCH)
+ {
+ /* At this point, ESRCH should mean the process is
+ already gone, in which case we simply ignore attempts
+ to read its registers. */
+ }
+ else
+ {
+ char s[256];
+ sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%d",
+ pid);
+ perror (s);
+ }
+ }
+ else
+ {
+ if (regset->type == GENERAL_REGS)
+ saw_general_regs = 1;
+ regset->store_function (regcache, buf);
+ }
+ free (buf);
+ }
+ if (saw_general_regs)
+ return 0;
+ else
+ return 1;
+}
+
+static int
+regsets_store_inferior_registers (struct regsets_info *regsets_info,
+ struct regcache *regcache)
+{
+ struct regset_info *regset;
+ int saw_general_regs = 0;
+ int pid;
+ struct iovec iov;
+
+ pid = lwpid_of (current_thread);
+ for (regset = regsets_info->regsets; regset->size >= 0; regset++)
+ {
+ void *buf, *data;
+ int nt_type, res;
+
+ if (regset->size == 0 || regset_disabled (regsets_info, regset)
+ || regset->fill_function == NULL)
+ continue;
+
+ buf = xmalloc (regset->size);
+
+ /* First fill the buffer with the current register set contents,
+ in case there are any items in the kernel's regset that are
+ not in gdbserver's regcache. */
+
+ nt_type = regset->nt_type;
+ if (nt_type)
+ {
+ iov.iov_base = buf;
+ iov.iov_len = regset->size;
+ data = (void *) &iov;
+ }
+ else
+ data = buf;
+
+#ifndef __sparc__
+ res = ptrace (regset->get_request, pid,
+ (PTRACE_TYPE_ARG3) (long) nt_type, data);
+#else
+ res = ptrace (regset->get_request, pid, data, nt_type);
+#endif
+
+ if (res == 0)
+ {
+ /* Then overlay our cached registers on that. */
+ regset->fill_function (regcache, buf);
+
+ /* Only now do we write the register set. */
+#ifndef __sparc__
+ res = ptrace (regset->set_request, pid,
+ (PTRACE_TYPE_ARG3) (long) nt_type, data);
+#else
+ res = ptrace (regset->set_request, pid, data, nt_type);
+#endif
+ }
+
+ if (res < 0)
+ {
+ if (errno == EIO
+ || (errno == EINVAL && regset->type == OPTIONAL_REGS))
+ {
+ /* If we get EIO on a regset, or an EINVAL and the regset is
+ optional, do not try it again for this process mode. */
+ disable_regset (regsets_info, regset);
+ }
+ else if (errno == ESRCH)
+ {
+ /* At this point, ESRCH should mean the process is
+ already gone, in which case we simply ignore attempts
+ to change its registers. See also the related
+ comment in linux_resume_one_lwp. */
+ free (buf);
+ return 0;
+ }
+ else
+ {
+ perror ("Warning: ptrace(regsets_store_inferior_registers)");
+ }
+ }
+ else if (regset->type == GENERAL_REGS)
+ saw_general_regs = 1;
+ free (buf);
+ }
+ if (saw_general_regs)
+ return 0;
+ else
+ return 1;
+}
+
+#else /* !HAVE_LINUX_REGSETS */
+
+#define use_linux_regsets 0
+#define regsets_fetch_inferior_registers(regsets_info, regcache) 1
+#define regsets_store_inferior_registers(regsets_info, regcache) 1
+
+#endif
+
+/* Return 1 if register REGNO is supported by one of the regset ptrace
+ calls or 0 if it has to be transferred individually. */
+
+static int
+linux_register_in_regsets (const struct regs_info *regs_info, int regno)
+{
+ unsigned char mask = 1 << (regno % 8);
+ size_t index = regno / 8;
+
+ return (use_linux_regsets
+ && (regs_info->regset_bitmap == NULL
+ || (regs_info->regset_bitmap[index] & mask) != 0));
+}
+
+#ifdef HAVE_LINUX_USRREGS
+
+static int
+register_addr (const struct usrregs_info *usrregs, int regnum)
+{
+ int addr;
+
+ if (regnum < 0 || regnum >= usrregs->num_regs)
+ error ("Invalid register number %d.", regnum);
+
+ addr = usrregs->regmap[regnum];
+
+ return addr;
+}
+
+/* Fetch one register. */
+static void
+fetch_register (const struct usrregs_info *usrregs,
+ struct regcache *regcache, int regno)
+{
+ CORE_ADDR regaddr;
+ int i, size;
+ char *buf;
+ int pid;
+
+ if (regno >= usrregs->num_regs)
+ return;
+ if ((*the_low_target.cannot_fetch_register) (regno))
+ return;
+
+ regaddr = register_addr (usrregs, regno);
+ if (regaddr == -1)
+ return;
+
+ size = ((register_size (regcache->tdesc, regno)
+ + sizeof (PTRACE_XFER_TYPE) - 1)
+ & -sizeof (PTRACE_XFER_TYPE));
+ buf = (char *) alloca (size);
+
+ pid = lwpid_of (current_thread);
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ *(PTRACE_XFER_TYPE *) (buf + i) =
+ ptrace (PTRACE_PEEKUSER, pid,
+ /* Coerce to a uintptr_t first to avoid potential gcc warning
+ of coercing an 8 byte integer to a 4 byte pointer. */
+ (PTRACE_TYPE_ARG3) (uintptr_t) regaddr, (PTRACE_TYPE_ARG4) 0);
+ regaddr += sizeof (PTRACE_XFER_TYPE);
+ if (errno != 0)
+ {
+ /* Mark register REGNO unavailable. */
+ supply_register (regcache, regno, NULL);
+ return;
+ }
+ }
+
+ if (the_low_target.supply_ptrace_register)
+ the_low_target.supply_ptrace_register (regcache, regno, buf);
+ else
+ supply_register (regcache, regno, buf);
+}
+
+/* Store one register. */
+static void
+store_register (const struct usrregs_info *usrregs,
+ struct regcache *regcache, int regno)
+{
+ CORE_ADDR regaddr;
+ int i, size;
+ char *buf;
+ int pid;
+
+ if (regno >= usrregs->num_regs)
+ return;
+ if ((*the_low_target.cannot_store_register) (regno))
+ return;
+
+ regaddr = register_addr (usrregs, regno);
+ if (regaddr == -1)
+ return;
+
+ size = ((register_size (regcache->tdesc, regno)
+ + sizeof (PTRACE_XFER_TYPE) - 1)
+ & -sizeof (PTRACE_XFER_TYPE));
+ buf = (char *) alloca (size);
+ memset (buf, 0, size);
+
+ if (the_low_target.collect_ptrace_register)
+ the_low_target.collect_ptrace_register (regcache, regno, buf);
+ else
+ collect_register (regcache, regno, buf);
+
+ pid = lwpid_of (current_thread);
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ ptrace (PTRACE_POKEUSER, pid,
+ /* Coerce to a uintptr_t first to avoid potential gcc warning
+ about coercing an 8 byte integer to a 4 byte pointer. */
+ (PTRACE_TYPE_ARG3) (uintptr_t) regaddr,
+ (PTRACE_TYPE_ARG4) *(PTRACE_XFER_TYPE *) (buf + i));
+ if (errno != 0)
+ {
+ /* At this point, ESRCH should mean the process is
+ already gone, in which case we simply ignore attempts
+ to change its registers. See also the related
+ comment in linux_resume_one_lwp. */
+ if (errno == ESRCH)
+ return;
+
+ if ((*the_low_target.cannot_store_register) (regno) == 0)
+ error ("writing register %d: %s", regno, safe_strerror (errno));
+ }
+ regaddr += sizeof (PTRACE_XFER_TYPE);
+ }
+}
+
+/* Fetch all registers, or just one, from the child process.
+ If REGNO is -1, do this for all registers, skipping any that are
+ assumed to have been retrieved by regsets_fetch_inferior_registers,
+ unless ALL is non-zero.
+ Otherwise, REGNO specifies which register (so we can save time). */
+static void
+usr_fetch_inferior_registers (const struct regs_info *regs_info,
+ struct regcache *regcache, int regno, int all)
+{
+ struct usrregs_info *usr = regs_info->usrregs;
+
+ if (regno == -1)
+ {
+ for (regno = 0; regno < usr->num_regs; regno++)
+ if (all || !linux_register_in_regsets (regs_info, regno))
+ fetch_register (usr, regcache, regno);
+ }
+ else
+ fetch_register (usr, regcache, regno);
+}
+
+/* Store our register values back into the inferior.
+ If REGNO is -1, do this for all registers, skipping any that are
+ assumed to have been saved by regsets_store_inferior_registers,
+ unless ALL is non-zero.
+ Otherwise, REGNO specifies which register (so we can save time). */
+static void
+usr_store_inferior_registers (const struct regs_info *regs_info,
+ struct regcache *regcache, int regno, int all)
+{
+ struct usrregs_info *usr = regs_info->usrregs;
+
+ if (regno == -1)
+ {
+ for (regno = 0; regno < usr->num_regs; regno++)
+ if (all || !linux_register_in_regsets (regs_info, regno))
+ store_register (usr, regcache, regno);
+ }
+ else
+ store_register (usr, regcache, regno);
+}
+
+#else /* !HAVE_LINUX_USRREGS */
+
+#define usr_fetch_inferior_registers(regs_info, regcache, regno, all) do {} while (0)
+#define usr_store_inferior_registers(regs_info, regcache, regno, all) do {} while (0)
+
+#endif
+
+
+static void
+linux_fetch_registers (struct regcache *regcache, int regno)
+{
+ int use_regsets;
+ int all = 0;
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();
+
+ if (regno == -1)
+ {
+ if (the_low_target.fetch_register != NULL
+ && regs_info->usrregs != NULL)
+ for (regno = 0; regno < regs_info->usrregs->num_regs; regno++)
+ (*the_low_target.fetch_register) (regcache, regno);
+
+ all = regsets_fetch_inferior_registers (regs_info->regsets_info, regcache);
+ if (regs_info->usrregs != NULL)
+ usr_fetch_inferior_registers (regs_info, regcache, -1, all);
+ }
+ else
+ {
+ if (the_low_target.fetch_register != NULL
+ && (*the_low_target.fetch_register) (regcache, regno))
+ return;
+
+ use_regsets = linux_register_in_regsets (regs_info, regno);
+ if (use_regsets)
+ all = regsets_fetch_inferior_registers (regs_info->regsets_info,
+ regcache);
+ if ((!use_regsets || all) && regs_info->usrregs != NULL)
+ usr_fetch_inferior_registers (regs_info, regcache, regno, 1);
+ }
+}
+
+static void
+linux_store_registers (struct regcache *regcache, int regno)
+{
+ int use_regsets;
+ int all = 0;
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();
+
+ if (regno == -1)
+ {
+ all = regsets_store_inferior_registers (regs_info->regsets_info,
+ regcache);
+ if (regs_info->usrregs != NULL)
+ usr_store_inferior_registers (regs_info, regcache, regno, all);
+ }
+ else
+ {
+ use_regsets = linux_register_in_regsets (regs_info, regno);
+ if (use_regsets)
+ all = regsets_store_inferior_registers (regs_info->regsets_info,
+ regcache);
+ if ((!use_regsets || all) && regs_info->usrregs != NULL)
+ usr_store_inferior_registers (regs_info, regcache, regno, 1);
+ }
+}
+
+
+/* Copy LEN bytes from inferior's memory starting at MEMADDR
+ to debugger memory starting at MYADDR. */
+
+static int
+linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ int pid = lwpid_of (current_thread);
+ PTRACE_XFER_TYPE *buffer;
+ CORE_ADDR addr;
+ int count;
+ char filename[64];
+ int i;
+ int ret;
+ int fd;
+
+ /* Try using /proc. Don't bother for one word. */
+ if (len >= 3 * sizeof (long))
+ {
+ int bytes;
+
+ /* We could keep this file open and cache it - possibly one per
+ thread. That requires some juggling, but is even faster. */
+ sprintf (filename, "/proc/%d/mem", pid);
+ fd = open (filename, O_RDONLY | O_LARGEFILE);
+ if (fd == -1)
+ goto no_proc;
+
+ /* If pread64 is available, use it. It's faster if the kernel
+ supports it (only one syscall), and it's 64-bit safe even on
+ 32-bit platforms (for instance, SPARC debugging a SPARC64
+ application). */
+#ifdef HAVE_PREAD64
+ bytes = pread64 (fd, myaddr, len, memaddr);
+#else
+ bytes = -1;
+ if (lseek (fd, memaddr, SEEK_SET) != -1)
+ bytes = read (fd, myaddr, len);
+#endif
+
+ close (fd);
+ if (bytes == len)
+ return 0;
+
+ /* Some data was read, we'll try to get the rest with ptrace. */
+ if (bytes > 0)
+ {
+ memaddr += bytes;
+ myaddr += bytes;
+ len -= bytes;
+ }
+ }
+
+ no_proc:
+ /* Round starting address down to longword boundary. */
+ addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
+ /* Round ending address up; get number of longwords that makes. */
+ count = ((((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
+ / sizeof (PTRACE_XFER_TYPE));
+ /* Allocate buffer of that many longwords. */
+ buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
+
+ /* Read all the longwords */
+ errno = 0;
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
+ {
+ /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning
+ about coercing an 8 byte integer to a 4 byte pointer. */
+ buffer[i] = ptrace (PTRACE_PEEKTEXT, pid,
+ (PTRACE_TYPE_ARG3) (uintptr_t) addr,
+ (PTRACE_TYPE_ARG4) 0);
+ if (errno)
+ break;
+ }
+ ret = errno;
+
+ /* Copy appropriate bytes out of the buffer. */
+ if (i > 0)
+ {
+ i *= sizeof (PTRACE_XFER_TYPE);
+ i -= memaddr & (sizeof (PTRACE_XFER_TYPE) - 1);
+ memcpy (myaddr,
+ (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
+ i < len ? i : len);
+ }
+
+ return ret;
+}
+
+/* Copy LEN bytes of data from debugger memory at MYADDR to inferior's
+ memory at MEMADDR. On failure (cannot write to the inferior)
+ returns the value of errno. Always succeeds if LEN is zero. */
+
+static int
+linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ int i;
+ /* Round starting address down to longword boundary. */
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
+ /* Round ending address up; get number of longwords that makes. */
+ int count
+ = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
+ / sizeof (PTRACE_XFER_TYPE);
+
+ /* Allocate buffer of that many longwords. */
+ PTRACE_XFER_TYPE *buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
+
+ int pid = lwpid_of (current_thread);
+
+ if (len == 0)
+ {
+ /* Zero length write always succeeds. */
+ return 0;
+ }
+
+ if (debug_threads)
+ {
+ /* Dump up to four bytes. */
+ char str[4 * 2 + 1];
+ char *p = str;
+ int dump = len < 4 ? len : 4;
+
+ for (i = 0; i < dump; i++)
+ {
+ sprintf (p, "%02x", myaddr[i]);
+ p += 2;
+ }
+ *p = '\0';
+
+ debug_printf ("Writing %s to 0x%08lx in process %d\n",
+ str, (long) memaddr, pid);
+ }
+
+ /* Fill start and end extra bytes of buffer with existing memory data. */
+
+ errno = 0;
+ /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning
+ about coercing an 8 byte integer to a 4 byte pointer. */
+ buffer[0] = ptrace (PTRACE_PEEKTEXT, pid,
+ (PTRACE_TYPE_ARG3) (uintptr_t) addr,
+ (PTRACE_TYPE_ARG4) 0);
+ if (errno)
+ return errno;
+
+ if (count > 1)
+ {
+ errno = 0;
+ buffer[count - 1]
+ = ptrace (PTRACE_PEEKTEXT, pid,
+ /* Coerce to a uintptr_t first to avoid potential gcc warning
+ about coercing an 8 byte integer to a 4 byte pointer. */
+ (PTRACE_TYPE_ARG3) (uintptr_t) (addr + (count - 1)
+ * sizeof (PTRACE_XFER_TYPE)),
+ (PTRACE_TYPE_ARG4) 0);
+ if (errno)
+ return errno;
+ }
+
+ /* Copy data to be written over corresponding part of buffer. */
+
+ memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
+ myaddr, len);
+
+ /* Write the entire buffer. */
+
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ ptrace (PTRACE_POKETEXT, pid,
+ /* Coerce to a uintptr_t first to avoid potential gcc warning
+ about coercing an 8 byte integer to a 4 byte pointer. */
+ (PTRACE_TYPE_ARG3) (uintptr_t) addr,
+ (PTRACE_TYPE_ARG4) buffer[i]);
+ if (errno)
+ return errno;
+ }
+
+ return 0;
+}
+
+static void
+linux_look_up_symbols (void)
+{
+#ifdef USE_THREAD_DB
+ struct process_info *proc = current_process ();
+
+ if (proc->priv->thread_db != NULL)
+ return;
+
+ thread_db_init ();
+#endif
+}
+
+static void
+linux_request_interrupt (void)
+{
+ /* Send a SIGINT to the process group. This acts just like the user
+ typed a ^C on the controlling terminal. */
+ kill (-signal_pid, SIGINT);
+}
+
+/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
+ to debugger memory starting at MYADDR. */
+
+static int
+linux_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
+{
+ char filename[PATH_MAX];
+ int fd, n;
+ int pid = lwpid_of (current_thread);
+
+ xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (offset != (CORE_ADDR) 0
+ && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+ n = -1;
+ else
+ n = read (fd, myaddr, len);
+
+ close (fd);
+
+ return n;
+}
+
+/* These breakpoint and watchpoint related wrapper functions simply
+ pass on the function call if the target has registered a
+ corresponding function. */
+
+static int
+linux_supports_z_point_type (char z_type)
+{
+ return (the_low_target.supports_z_point_type != NULL
+ && the_low_target.supports_z_point_type (z_type));
+}
+
+static int
+linux_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ if (type == raw_bkpt_type_sw)
+ return insert_memory_breakpoint (bp);
+ else if (the_low_target.insert_point != NULL)
+ return the_low_target.insert_point (type, addr, size, bp);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+linux_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ if (type == raw_bkpt_type_sw)
+ return remove_memory_breakpoint (bp);
+ else if (the_low_target.remove_point != NULL)
+ return the_low_target.remove_point (type, addr, size, bp);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+/* Implement the to_stopped_by_sw_breakpoint target_ops
+ method. */
+
+static int
+linux_stopped_by_sw_breakpoint (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+ return (lwp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT);
+}
+
+/* Implement the to_supports_stopped_by_sw_breakpoint target_ops
+ method. */
+
+static int
+linux_supports_stopped_by_sw_breakpoint (void)
+{
+ return USE_SIGTRAP_SIGINFO;
+}
+
+/* Implement the to_stopped_by_hw_breakpoint target_ops
+ method. */
+
+static int
+linux_stopped_by_hw_breakpoint (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+ return (lwp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT);
+}
+
+/* Implement the to_supports_stopped_by_hw_breakpoint target_ops
+ method. */
+
+static int
+linux_supports_stopped_by_hw_breakpoint (void)
+{
+ return USE_SIGTRAP_SIGINFO;
+}
+
+/* Implement the supports_hardware_single_step target_ops method. */
+
+static int
+linux_supports_hardware_single_step (void)
+{
+ return can_hardware_single_step ();
+}
+
+static int
+linux_supports_software_single_step (void)
+{
+ return can_software_single_step ();
+}
+
+static int
+linux_stopped_by_watchpoint (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+ return lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
+}
+
+static CORE_ADDR
+linux_stopped_data_address (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+ return lwp->stopped_data_address;
+}
+
+#if defined(__UCLIBC__) && defined(HAS_NOMMU) \
+ && defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) \
+ && defined(PT_TEXT_END_ADDR)
+
+/* This is only used for targets that define PT_TEXT_ADDR,
+ PT_DATA_ADDR and PT_TEXT_END_ADDR. If those are not defined, supposedly
+ the target has different ways of acquiring this information, like
+ loadmaps. */
+
+/* Under uClinux, programs are loaded at non-zero offsets, which we need
+ to tell gdb about. */
+
+static int
+linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
+{
+ unsigned long text, text_end, data;
+ int pid = lwpid_of (current_thread);
+
+ errno = 0;
+
+ text = ptrace (PTRACE_PEEKUSER, pid, (PTRACE_TYPE_ARG3) PT_TEXT_ADDR,
+ (PTRACE_TYPE_ARG4) 0);
+ text_end = ptrace (PTRACE_PEEKUSER, pid, (PTRACE_TYPE_ARG3) PT_TEXT_END_ADDR,
+ (PTRACE_TYPE_ARG4) 0);
+ data = ptrace (PTRACE_PEEKUSER, pid, (PTRACE_TYPE_ARG3) PT_DATA_ADDR,
+ (PTRACE_TYPE_ARG4) 0);
+
+ if (errno == 0)
+ {
+ /* Both text and data offsets produced at compile-time (and so
+ used by gdb) are relative to the beginning of the program,
+ with the data segment immediately following the text segment.
+ However, the actual runtime layout in memory may put the data
+ somewhere else, so when we send gdb a data base-address, we
+ use the real data base address and subtract the compile-time
+ data base-address from it (which is just the length of the
+ text segment). BSS immediately follows data in both
+ cases. */
+ *text_p = text;
+ *data_p = data - (text_end - text);
+
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+static int
+linux_qxfer_osdata (const char *annex,
+ unsigned char *readbuf, unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ return linux_common_xfer_osdata (annex, readbuf, offset, len);
+}
+
+/* Convert a native/host siginfo object, into/from the siginfo in the
+ layout of the inferiors' architecture. */
+
+static void
+siginfo_fixup (siginfo_t *siginfo, gdb_byte *inf_siginfo, int direction)
+{
+ int done = 0;
+
+ if (the_low_target.siginfo_fixup != NULL)
+ done = the_low_target.siginfo_fixup (siginfo, inf_siginfo, direction);
+
+ /* If there was no callback, or the callback didn't do anything,
+ then just do a straight memcpy. */
+ if (!done)
+ {
+ if (direction == 1)
+ memcpy (siginfo, inf_siginfo, sizeof (siginfo_t));
+ else
+ memcpy (inf_siginfo, siginfo, sizeof (siginfo_t));
+ }
+}
+
+static int
+linux_xfer_siginfo (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf, CORE_ADDR offset, int len)
+{
+ int pid;
+ siginfo_t siginfo;
+ gdb_byte inf_siginfo[sizeof (siginfo_t)];
+
+ if (current_thread == NULL)
+ return -1;
+
+ pid = lwpid_of (current_thread);
+
+ if (debug_threads)
+ debug_printf ("%s siginfo for lwp %d.\n",
+ readbuf != NULL ? "Reading" : "Writing",
+ pid);
+
+ if (offset >= sizeof (siginfo))
+ return -1;
+
+ if (ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo) != 0)
+ return -1;
+
+ /* When GDBSERVER is built as a 64-bit application, ptrace writes into
+ SIGINFO an object with 64-bit layout. Since debugging a 32-bit
+ inferior with a 64-bit GDBSERVER should look the same as debugging it
+ with a 32-bit GDBSERVER, we need to convert it. */
+ siginfo_fixup (&siginfo, inf_siginfo, 0);
+
+ if (offset + len > sizeof (siginfo))
+ len = sizeof (siginfo) - offset;
+
+ if (readbuf != NULL)
+ memcpy (readbuf, inf_siginfo + offset, len);
+ else
+ {
+ memcpy (inf_siginfo + offset, writebuf, len);
+
+ /* Convert back to ptrace layout before flushing it out. */
+ siginfo_fixup (&siginfo, inf_siginfo, 1);
+
+ if (ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo) != 0)
+ return -1;
+ }
+
+ return len;
+}
+
+/* SIGCHLD handler that serves two purposes: In non-stop/async mode,
+ so we notice when children change state; as the handler for the
+ sigsuspend in my_waitpid. */
+
+static void
+sigchld_handler (int signo)
+{
+ int old_errno = errno;
+
+ if (debug_threads)
+ {
+ do
+ {
+ /* Use the async signal safe debug function. */
+ if (debug_write ("sigchld_handler\n",
+ sizeof ("sigchld_handler\n") - 1) < 0)
+ break; /* just ignore */
+ } while (0);
+ }
+
+ if (target_is_async_p ())
+ async_file_mark (); /* trigger a linux_wait */
+
+ errno = old_errno;
+}
+
+static int
+linux_supports_non_stop (void)
+{
+ return 1;
+}
+
+static int
+linux_async (int enable)
+{
+ int previous = target_is_async_p ();
+
+ if (debug_threads)
+ debug_printf ("linux_async (%d), previous=%d\n",
+ enable, previous);
+
+ if (previous != enable)
+ {
+ sigset_t mask;
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+
+ gdb_sigmask (SIG_BLOCK, &mask, NULL);
+
+ if (enable)
+ {
+ if (pipe (linux_event_pipe) == -1)
+ {
+ linux_event_pipe[0] = -1;
+ linux_event_pipe[1] = -1;
+ gdb_sigmask (SIG_UNBLOCK, &mask, NULL);
+
+ warning ("creating event pipe failed.");
+ return previous;
+ }
+
+ fcntl (linux_event_pipe[0], F_SETFL, O_NONBLOCK);
+ fcntl (linux_event_pipe[1], F_SETFL, O_NONBLOCK);
+
+ /* Register the event loop handler. */
+ add_file_handler (linux_event_pipe[0],
+ handle_target_event, NULL);
+
+ /* Always trigger a linux_wait. */
+ async_file_mark ();
+ }
+ else
+ {
+ delete_file_handler (linux_event_pipe[0]);
+
+ close (linux_event_pipe[0]);
+ close (linux_event_pipe[1]);
+ linux_event_pipe[0] = -1;
+ linux_event_pipe[1] = -1;
+ }
+
+ gdb_sigmask (SIG_UNBLOCK, &mask, NULL);
+ }
+
+ return previous;
+}
+
+static int
+linux_start_non_stop (int nonstop)
+{
+ /* Register or unregister from event-loop accordingly. */
+ linux_async (nonstop);
+
+ if (target_is_async_p () != (nonstop != 0))
+ return -1;
+
+ return 0;
+}
+
+static int
+linux_supports_multi_process (void)
+{
+ return 1;
+}
+
+/* Check if fork events are supported. */
+
+static int
+linux_supports_fork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported. */
+
+static int
+linux_supports_vfork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if exec events are supported. */
+
+static int
+linux_supports_exec_events (void)
+{
+ return linux_supports_traceexec ();
+}
+
+/* Target hook for 'handle_new_gdb_connection'. Causes a reset of the
+ ptrace flags for all inferiors. This is in case the new GDB connection
+ doesn't support the same set of events that the previous one did. */
+
+static void
+linux_handle_new_gdb_connection (void)
+{
+ /* Request that all the lwps reset their ptrace options. */
+ for_each_thread ([] (thread_info *thread)
+ {
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ if (!lwp->stopped)
+ {
+ /* Stop the lwp so we can modify its ptrace options. */
+ lwp->must_set_ptrace_flags = 1;
+ linux_stop_lwp (lwp);
+ }
+ else
+ {
+ /* Already stopped; go ahead and set the ptrace options. */
+ struct process_info *proc = find_process_pid (pid_of (thread));
+ int options = linux_low_ptrace_options (proc->attached);
+
+ linux_enable_event_reporting (lwpid_of (thread), options);
+ lwp->must_set_ptrace_flags = 0;
+ }
+ });
+}
+
+static int
+linux_supports_disable_randomization (void)
+{
+#ifdef HAVE_PERSONALITY
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static int
+linux_supports_agent (void)
+{
+ return 1;
+}
+
+static int
+linux_supports_range_stepping (void)
+{
+ if (can_software_single_step ())
+ return 1;
+ if (*the_low_target.supports_range_stepping == NULL)
+ return 0;
+
+ return (*the_low_target.supports_range_stepping) ();
+}
+
+#if defined PT_GETDSBT || defined PTRACE_GETFDPIC
+struct target_loadseg
+{
+ /* Core address to which the segment is mapped. */
+ Elf32_Addr addr;
+ /* VMA recorded in the program header. */
+ Elf32_Addr p_vaddr;
+ /* Size of this segment in memory. */
+ Elf32_Word p_memsz;
+};
+
+# if defined PT_GETDSBT
+struct target_loadmap
+{
+ /* Protocol version number, must be zero. */
+ Elf32_Word version;
+ /* Pointer to the DSBT table, its size, and the DSBT index. */
+ unsigned *dsbt_table;
+ unsigned dsbt_size, dsbt_index;
+ /* Number of segments in this map. */
+ Elf32_Word nsegs;
+ /* The actual memory map. */
+ struct target_loadseg segs[/*nsegs*/];
+};
+# define LINUX_LOADMAP PT_GETDSBT
+# define LINUX_LOADMAP_EXEC PTRACE_GETDSBT_EXEC
+# define LINUX_LOADMAP_INTERP PTRACE_GETDSBT_INTERP
+# else
+struct target_loadmap
+{
+ /* Protocol version number, must be zero. */
+ Elf32_Half version;
+ /* Number of segments in this map. */
+ Elf32_Half nsegs;
+ /* The actual memory map. */
+ struct target_loadseg segs[/*nsegs*/];
+};
+# define LINUX_LOADMAP PTRACE_GETFDPIC
+# define LINUX_LOADMAP_EXEC PTRACE_GETFDPIC_EXEC
+# define LINUX_LOADMAP_INTERP PTRACE_GETFDPIC_INTERP
+# endif
+
+static int
+linux_read_loadmap (const char *annex, CORE_ADDR offset,
+ unsigned char *myaddr, unsigned int len)
+{
+ int pid = lwpid_of (current_thread);
+ int addr = -1;
+ struct target_loadmap *data = NULL;
+ unsigned int actual_length, copy_length;
+
+ if (strcmp (annex, "exec") == 0)
+ addr = (int) LINUX_LOADMAP_EXEC;
+ else if (strcmp (annex, "interp") == 0)
+ addr = (int) LINUX_LOADMAP_INTERP;
+ else
+ return -1;
+
+ if (ptrace (LINUX_LOADMAP, pid, addr, &data) != 0)
+ return -1;
+
+ if (data == NULL)
+ return -1;
+
+ actual_length = sizeof (struct target_loadmap)
+ + sizeof (struct target_loadseg) * data->nsegs;
+
+ if (offset < 0 || offset > actual_length)
+ return -1;
+
+ copy_length = actual_length - offset < len ? actual_length - offset : len;
+ memcpy (myaddr, (char *) data + offset, copy_length);
+ return copy_length;
+}
+#else
+# define linux_read_loadmap NULL
+#endif /* defined PT_GETDSBT || defined PTRACE_GETFDPIC */
+
+static void
+linux_process_qsupported (char **features, int count)
+{
+ if (the_low_target.process_qsupported != NULL)
+ the_low_target.process_qsupported (features, count);
+}
+
+static int
+linux_supports_catch_syscall (void)
+{
+ return (the_low_target.get_syscall_trapinfo != NULL
+ && linux_supports_tracesysgood ());
+}
+
+static int
+linux_get_ipa_tdesc_idx (void)
+{
+ if (the_low_target.get_ipa_tdesc_idx == NULL)
+ return 0;
+
+ return (*the_low_target.get_ipa_tdesc_idx) ();
+}
+
+static int
+linux_supports_tracepoints (void)
+{
+ if (*the_low_target.supports_tracepoints == NULL)
+ return 0;
+
+ return (*the_low_target.supports_tracepoints) ();
+}
+
+static CORE_ADDR
+linux_read_pc (struct regcache *regcache)
+{
+ if (the_low_target.get_pc == NULL)
+ return 0;
+
+ return (*the_low_target.get_pc) (regcache);
+}
+
+static void
+linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ gdb_assert (the_low_target.set_pc != NULL);
+
+ (*the_low_target.set_pc) (regcache, pc);
+}
+
+static int
+linux_thread_stopped (struct thread_info *thread)
+{
+ return get_thread_lwp (thread)->stopped;
+}
+
+/* This exposes stop-all-threads functionality to other modules. */
+
+static void
+linux_pause_all (int freeze)
+{
+ stop_all_lwps (freeze, NULL);
+}
+
+/* This exposes unstop-all-threads functionality to other gdbserver
+ modules. */
+
+static void
+linux_unpause_all (int unfreeze)
+{
+ unstop_all_lwps (unfreeze, NULL);
+}
+
+static int
+linux_prepare_to_access_memory (void)
+{
+ /* Neither ptrace nor /proc/PID/mem allow accessing memory through a
+ running LWP. */
+ if (non_stop)
+ linux_pause_all (1);
+ return 0;
+}
+
+static void
+linux_done_accessing_memory (void)
+{
+ /* Neither ptrace nor /proc/PID/mem allow accessing memory through a
+ running LWP. */
+ if (non_stop)
+ linux_unpause_all (1);
+}
+
+static int
+linux_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)
+{
+ return (*the_low_target.install_fast_tracepoint_jump_pad)
+ (tpoint, tpaddr, collector, lockaddr, orig_size,
+ jump_entry, trampoline, trampoline_size,
+ jjump_pad_insn, jjump_pad_insn_size,
+ adjusted_insn_addr, adjusted_insn_addr_end,
+ err);
+}
+
+static struct emit_ops *
+linux_emit_ops (void)
+{
+ if (the_low_target.emit_ops != NULL)
+ return (*the_low_target.emit_ops) ();
+ else
+ return NULL;
+}
+
+static int
+linux_get_min_fast_tracepoint_insn_len (void)
+{
+ return (*the_low_target.get_min_fast_tracepoint_insn_len) ();
+}
+
+/* Extract &phdr and num_phdr in the inferior. Return 0 on success. */
+
+static int
+get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64,
+ CORE_ADDR *phdr_memaddr, int *num_phdr)
+{
+ char filename[PATH_MAX];
+ int fd;
+ const int auxv_size = is_elf64
+ ? sizeof (Elf64_auxv_t) : sizeof (Elf32_auxv_t);
+ char buf[sizeof (Elf64_auxv_t)]; /* The larger of the two. */
+
+ xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return 1;
+
+ *phdr_memaddr = 0;
+ *num_phdr = 0;
+ while (read (fd, buf, auxv_size) == auxv_size
+ && (*phdr_memaddr == 0 || *num_phdr == 0))
+ {
+ if (is_elf64)
+ {
+ Elf64_auxv_t *const aux = (Elf64_auxv_t *) buf;
+
+ switch (aux->a_type)
+ {
+ case AT_PHDR:
+ *phdr_memaddr = aux->a_un.a_val;
+ break;
+ case AT_PHNUM:
+ *num_phdr = aux->a_un.a_val;
+ break;
+ }
+ }
+ else
+ {
+ Elf32_auxv_t *const aux = (Elf32_auxv_t *) buf;
+
+ switch (aux->a_type)
+ {
+ case AT_PHDR:
+ *phdr_memaddr = aux->a_un.a_val;
+ break;
+ case AT_PHNUM:
+ *num_phdr = aux->a_un.a_val;
+ break;
+ }
+ }
+ }
+
+ close (fd);
+
+ if (*phdr_memaddr == 0 || *num_phdr == 0)
+ {
+ warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: "
+ "phdr_memaddr = %ld, phdr_num = %d",
+ (long) *phdr_memaddr, *num_phdr);
+ return 2;
+ }
+
+ return 0;
+}
+
+/* Return &_DYNAMIC (via PT_DYNAMIC) in the inferior, or 0 if not present. */
+
+static CORE_ADDR
+get_dynamic (const int pid, const int is_elf64)
+{
+ CORE_ADDR phdr_memaddr, relocation;
+ int num_phdr, i;
+ unsigned char *phdr_buf;
+ const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr);
+
+ if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr))
+ return 0;
+
+ gdb_assert (num_phdr < 100); /* Basic sanity check. */
+ phdr_buf = (unsigned char *) alloca (num_phdr * phdr_size);
+
+ if (linux_read_memory (phdr_memaddr, phdr_buf, num_phdr * phdr_size))
+ return 0;
+
+ /* Compute relocation: it is expected to be 0 for "regular" executables,
+ non-zero for PIE ones. */
+ relocation = -1;
+ for (i = 0; relocation == -1 && i < num_phdr; i++)
+ if (is_elf64)
+ {
+ Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_PHDR)
+ relocation = phdr_memaddr - p->p_vaddr;
+ }
+ else
+ {
+ Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_PHDR)
+ relocation = phdr_memaddr - p->p_vaddr;
+ }
+
+ if (relocation == -1)
+ {
+ /* PT_PHDR is optional, but necessary for PIE in general. Fortunately
+ any real world executables, including PIE executables, have always
+ PT_PHDR present. PT_PHDR is not present in some shared libraries or
+ in fpc (Free Pascal 2.4) binaries but neither of those have a need for
+ or present DT_DEBUG anyway (fpc binaries are statically linked).
+
+ Therefore if there exists DT_DEBUG there is always also PT_PHDR.
+
+ GDB could find RELOCATION also from AT_ENTRY - e_entry. */
+
+ return 0;
+ }
+
+ for (i = 0; i < num_phdr; i++)
+ {
+ if (is_elf64)
+ {
+ Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_DYNAMIC)
+ return p->p_vaddr + relocation;
+ }
+ else
+ {
+ Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_DYNAMIC)
+ return p->p_vaddr + relocation;
+ }
+ }
+
+ return 0;
+}
+
+/* Return &_r_debug in the inferior, or -1 if not present. Return value
+ can be 0 if the inferior does not yet have the library list initialized.
+ We look for DT_MIPS_RLD_MAP first. MIPS executables use this instead of
+ DT_DEBUG, although they sometimes contain an unused DT_DEBUG entry too. */
+
+static CORE_ADDR
+get_r_debug (const int pid, const int is_elf64)
+{
+ CORE_ADDR dynamic_memaddr;
+ const int dyn_size = is_elf64 ? sizeof (Elf64_Dyn) : sizeof (Elf32_Dyn);
+ unsigned char buf[sizeof (Elf64_Dyn)]; /* The larger of the two. */
+ CORE_ADDR map = -1;
+
+ dynamic_memaddr = get_dynamic (pid, is_elf64);
+ if (dynamic_memaddr == 0)
+ return map;
+
+ while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0)
+ {
+ if (is_elf64)
+ {
+ Elf64_Dyn *const dyn = (Elf64_Dyn *) buf;
+#if defined DT_MIPS_RLD_MAP || defined DT_MIPS_RLD_MAP_REL
+ union
+ {
+ Elf64_Xword map;
+ unsigned char buf[sizeof (Elf64_Xword)];
+ }
+ rld_map;
+#endif
+#ifdef DT_MIPS_RLD_MAP
+ if (dyn->d_tag == DT_MIPS_RLD_MAP)
+ {
+ if (linux_read_memory (dyn->d_un.d_val,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP */
+#ifdef DT_MIPS_RLD_MAP_REL
+ if (dyn->d_tag == DT_MIPS_RLD_MAP_REL)
+ {
+ if (linux_read_memory (dyn->d_un.d_val + dynamic_memaddr,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP_REL */
+
+ if (dyn->d_tag == DT_DEBUG && map == -1)
+ map = dyn->d_un.d_val;
+
+ if (dyn->d_tag == DT_NULL)
+ break;
+ }
+ else
+ {
+ Elf32_Dyn *const dyn = (Elf32_Dyn *) buf;
+#if defined DT_MIPS_RLD_MAP || defined DT_MIPS_RLD_MAP_REL
+ union
+ {
+ Elf32_Word map;
+ unsigned char buf[sizeof (Elf32_Word)];
+ }
+ rld_map;
+#endif
+#ifdef DT_MIPS_RLD_MAP
+ if (dyn->d_tag == DT_MIPS_RLD_MAP)
+ {
+ if (linux_read_memory (dyn->d_un.d_val,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP */
+#ifdef DT_MIPS_RLD_MAP_REL
+ if (dyn->d_tag == DT_MIPS_RLD_MAP_REL)
+ {
+ if (linux_read_memory (dyn->d_un.d_val + dynamic_memaddr,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP_REL */
+
+ if (dyn->d_tag == DT_DEBUG && map == -1)
+ map = dyn->d_un.d_val;
+
+ if (dyn->d_tag == DT_NULL)
+ break;
+ }
+
+ dynamic_memaddr += dyn_size;
+ }
+
+ return map;
+}
+
+/* Read one pointer from MEMADDR in the inferior. */
+
+static int
+read_one_ptr (CORE_ADDR memaddr, CORE_ADDR *ptr, int ptr_size)
+{
+ int ret;
+
+ /* Go through a union so this works on either big or little endian
+ hosts, when the inferior's pointer size is smaller than the size
+ of CORE_ADDR. It is assumed the inferior's endianness is the
+ same of the superior's. */
+ union
+ {
+ CORE_ADDR core_addr;
+ unsigned int ui;
+ unsigned char uc;
+ } addr;
+
+ ret = linux_read_memory (memaddr, &addr.uc, ptr_size);
+ if (ret == 0)
+ {
+ if (ptr_size == sizeof (CORE_ADDR))
+ *ptr = addr.core_addr;
+ else if (ptr_size == sizeof (unsigned int))
+ *ptr = addr.ui;
+ else
+ gdb_assert_not_reached ("unhandled pointer size");
+ }
+ return ret;
+}
+
+struct link_map_offsets
+ {
+ /* Offset and size of r_debug.r_version. */
+ int r_version_offset;
+
+ /* Offset and size of r_debug.r_map. */
+ int r_map_offset;
+
+ /* Offset to l_addr field in struct link_map. */
+ int l_addr_offset;
+
+ /* Offset to l_name field in struct link_map. */
+ int l_name_offset;
+
+ /* Offset to l_ld field in struct link_map. */
+ int l_ld_offset;
+
+ /* Offset to l_next field in struct link_map. */
+ int l_next_offset;
+
+ /* Offset to l_prev field in struct link_map. */
+ int l_prev_offset;
+ };
+
+/* Construct qXfer:libraries-svr4:read reply. */
+
+static int
+linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ struct process_info_private *const priv = current_process ()->priv;
+ char filename[PATH_MAX];
+ int pid, is_elf64;
+
+ static const struct link_map_offsets lmo_32bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 4, /* r_debug.r_map offset. */
+ 0, /* l_addr offset in link_map. */
+ 4, /* l_name offset in link_map. */
+ 8, /* l_ld offset in link_map. */
+ 12, /* l_next offset in link_map. */
+ 16 /* l_prev offset in link_map. */
+ };
+
+ static const struct link_map_offsets lmo_64bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 8, /* r_debug.r_map offset. */
+ 0, /* l_addr offset in link_map. */
+ 8, /* l_name offset in link_map. */
+ 16, /* l_ld offset in link_map. */
+ 24, /* l_next offset in link_map. */
+ 32 /* l_prev offset in link_map. */
+ };
+ const struct link_map_offsets *lmo;
+ unsigned int machine;
+ int ptr_size;
+ CORE_ADDR lm_addr = 0, lm_prev = 0;
+ CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
+ int header_done = 0;
+
+ if (writebuf != NULL)
+ return -2;
+ if (readbuf == NULL)
+ return -1;
+
+ pid = lwpid_of (current_thread);
+ xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
+ is_elf64 = elf_64_file_p (filename, &machine);
+ lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
+ ptr_size = is_elf64 ? 8 : 4;
+
+ while (annex[0] != '\0')
+ {
+ const char *sep;
+ CORE_ADDR *addrp;
+ int name_len;
+
+ sep = strchr (annex, '=');
+ if (sep == NULL)
+ break;
+
+ name_len = sep - annex;
+ if (name_len == 5 && startswith (annex, "start"))
+ addrp = &lm_addr;
+ else if (name_len == 4 && startswith (annex, "prev"))
+ addrp = &lm_prev;
+ else
+ {
+ annex = strchr (sep, ';');
+ if (annex == NULL)
+ break;
+ annex++;
+ continue;
+ }
+
+ annex = decode_address_to_semicolon (addrp, sep + 1);
+ }
+
+ if (lm_addr == 0)
+ {
+ int r_version = 0;
+
+ if (priv->r_debug == 0)
+ priv->r_debug = get_r_debug (pid, is_elf64);
+
+ /* We failed to find DT_DEBUG. Such situation will not change
+ for this inferior - do not retry it. Report it to GDB as
+ E01, see for the reasons at the GDB solib-svr4.c side. */
+ if (priv->r_debug == (CORE_ADDR) -1)
+ return -1;
+
+ if (priv->r_debug != 0)
+ {
+ if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
+ (unsigned char *) &r_version,
+ sizeof (r_version)) != 0
+ || r_version != 1)
+ {
+ warning ("unexpected r_debug version %d", r_version);
+ }
+ else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
+ &lm_addr, ptr_size) != 0)
+ {
+ warning ("unable to read r_map from 0x%lx",
+ (long) priv->r_debug + lmo->r_map_offset);
+ }
+ }
+ }
+
+ std::string document = "<library-list-svr4 version=\"1.0\"";
+
+ while (lm_addr
+ && read_one_ptr (lm_addr + lmo->l_name_offset,
+ &l_name, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_addr_offset,
+ &l_addr, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_ld_offset,
+ &l_ld, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_prev_offset,
+ &l_prev, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_next_offset,
+ &l_next, ptr_size) == 0)
+ {
+ unsigned char libname[PATH_MAX];
+
+ if (lm_prev != l_prev)
+ {
+ warning ("Corrupted shared library list: 0x%lx != 0x%lx",
+ (long) lm_prev, (long) l_prev);
+ break;
+ }
+
+ /* Ignore the first entry even if it has valid name as the first entry
+ corresponds to the main executable. The first entry should not be
+ skipped if the dynamic loader was loaded late by a static executable
+ (see solib-svr4.c parameter ignore_first). But in such case the main
+ executable does not have PT_DYNAMIC present and this function already
+ exited above due to failed get_r_debug. */
+ if (lm_prev == 0)
+ string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
+ else
+ {
+ /* Not checking for error because reading may stop before
+ we've got PATH_MAX worth of characters. */
+ libname[0] = '\0';
+ linux_read_memory (l_name, libname, sizeof (libname) - 1);
+ libname[sizeof (libname) - 1] = '\0';
+ if (libname[0] != '\0')
+ {
+ if (!header_done)
+ {
+ /* Terminate `<library-list-svr4'. */
+ document += '>';
+ header_done = 1;
+ }
+
+ string_appendf (document, "<library name=\"");
+ xml_escape_text_append (&document, (char *) libname);
+ string_appendf (document, "\" lm=\"0x%lx\" "
+ "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+ (unsigned long) lm_addr, (unsigned long) l_addr,
+ (unsigned long) l_ld);
+ }
+ }
+
+ lm_prev = lm_addr;
+ lm_addr = l_next;
+ }
+
+ if (!header_done)
+ {
+ /* Empty list; terminate `<library-list-svr4'. */
+ document += "/>";
+ }
+ else
+ document += "</library-list-svr4>";
+
+ int document_len = document.length ();
+ if (offset < document_len)
+ document_len -= offset;
+ else
+ document_len = 0;
+ if (len > document_len)
+ len = document_len;
+
+ memcpy (readbuf, document.data () + offset, len);
+
+ return len;
+}
+
+#ifdef HAVE_LINUX_BTRACE
+
+/* See to_disable_btrace target method. */
+
+static int
+linux_low_disable_btrace (struct btrace_target_info *tinfo)
+{
+ enum btrace_error err;
+
+ err = linux_disable_btrace (tinfo);
+ return (err == BTRACE_ERR_NONE ? 0 : -1);
+}
+
+/* Encode an Intel Processor Trace configuration. */
+
+static void
+linux_low_encode_pt_config (struct buffer *buffer,
+ const struct btrace_data_pt_config *config)
+{
+ buffer_grow_str (buffer, "<pt-config>\n");
+
+ switch (config->cpu.vendor)
+ {
+ case CV_INTEL:
+ buffer_xml_printf (buffer, "<cpu vendor=\"GenuineIntel\" family=\"%u\" "
+ "model=\"%u\" stepping=\"%u\"/>\n",
+ config->cpu.family, config->cpu.model,
+ config->cpu.stepping);
+ break;
+
+ default:
+ break;
+ }
+
+ buffer_grow_str (buffer, "</pt-config>\n");
+}
+
+/* Encode a raw buffer. */
+
+static void
+linux_low_encode_raw (struct buffer *buffer, const gdb_byte *data,
+ unsigned int size)
+{
+ if (size == 0)
+ return;
+
+ /* We use hex encoding - see gdbsupport/rsp-low.h. */
+ buffer_grow_str (buffer, "<raw>\n");
+
+ while (size-- > 0)
+ {
+ char elem[2];
+
+ elem[0] = tohex ((*data >> 4) & 0xf);
+ elem[1] = tohex (*data++ & 0xf);
+
+ buffer_grow (buffer, elem, 2);
+ }
+
+ buffer_grow_str (buffer, "</raw>\n");
+}
+
+/* See to_read_btrace target method. */
+
+static int
+linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
+ enum btrace_read_type type)
+{
+ struct btrace_data btrace;
+ enum btrace_error err;
+
+ err = linux_read_btrace (&btrace, tinfo, type);
+ if (err != BTRACE_ERR_NONE)
+ {
+ if (err == BTRACE_ERR_OVERFLOW)
+ buffer_grow_str0 (buffer, "E.Overflow.");
+ else
+ buffer_grow_str0 (buffer, "E.Generic Error.");
+
+ return -1;
+ }
+
+ switch (btrace.format)
+ {
+ case BTRACE_FORMAT_NONE:
+ buffer_grow_str0 (buffer, "E.No Trace.");
+ return -1;
+
+ case BTRACE_FORMAT_BTS:
+ buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+ buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+
+ for (const btrace_block &block : *btrace.variant.bts.blocks)
+ buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
+ paddress (block.begin), paddress (block.end));
+
+ buffer_grow_str0 (buffer, "</btrace>\n");
+ break;
+
+ case BTRACE_FORMAT_PT:
+ buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+ buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+ buffer_grow_str (buffer, "<pt>\n");
+
+ linux_low_encode_pt_config (buffer, &btrace.variant.pt.config);
+
+ linux_low_encode_raw (buffer, btrace.variant.pt.data,
+ btrace.variant.pt.size);
+
+ buffer_grow_str (buffer, "</pt>\n");
+ buffer_grow_str0 (buffer, "</btrace>\n");
+ break;
+
+ default:
+ buffer_grow_str0 (buffer, "E.Unsupported Trace Format.");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* See to_btrace_conf target method. */
+
+static int
+linux_low_btrace_conf (const struct btrace_target_info *tinfo,
+ struct buffer *buffer)
+{
+ const struct btrace_config *conf;
+
+ buffer_grow_str (buffer, "<!DOCTYPE btrace-conf SYSTEM \"btrace-conf.dtd\">\n");
+ buffer_grow_str (buffer, "<btrace-conf version=\"1.0\">\n");
+
+ conf = linux_btrace_conf (tinfo);
+ if (conf != NULL)
+ {
+ switch (conf->format)
+ {
+ case BTRACE_FORMAT_NONE:
+ break;
+
+ case BTRACE_FORMAT_BTS:
+ buffer_xml_printf (buffer, "<bts");
+ buffer_xml_printf (buffer, " size=\"0x%x\"", conf->bts.size);
+ buffer_xml_printf (buffer, " />\n");
+ break;
+
+ case BTRACE_FORMAT_PT:
+ buffer_xml_printf (buffer, "<pt");
+ buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
+ buffer_xml_printf (buffer, "/>\n");
+ break;
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</btrace-conf>\n");
+ return 0;
+}
+#endif /* HAVE_LINUX_BTRACE */
+
+/* See nat/linux-nat.h. */
+
+ptid_t
+current_lwp_ptid (void)
+{
+ return ptid_of (current_thread);
+}
+
+/* Implementation of the target_ops method "breakpoint_kind_from_pc". */
+
+static int
+linux_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+ if (the_low_target.breakpoint_kind_from_pc != NULL)
+ return (*the_low_target.breakpoint_kind_from_pc) (pcptr);
+ else
+ return default_breakpoint_kind_from_pc (pcptr);
+}
+
+/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+linux_sw_breakpoint_from_kind (int kind, int *size)
+{
+ gdb_assert (the_low_target.sw_breakpoint_from_kind != NULL);
+
+ return (*the_low_target.sw_breakpoint_from_kind) (kind, size);
+}
+
+/* Implementation of the target_ops method
+ "breakpoint_kind_from_current_state". */
+
+static int
+linux_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+ if (the_low_target.breakpoint_kind_from_current_state != NULL)
+ return (*the_low_target.breakpoint_kind_from_current_state) (pcptr);
+ else
+ return linux_breakpoint_kind_from_pc (pcptr);
+}
+
+/* Default implementation of linux_target_ops method "set_pc" for
+ 32-bit pc register which is literally named "pc". */
+
+void
+linux_set_pc_32bit (struct regcache *regcache, CORE_ADDR pc)
+{
+ uint32_t newpc = pc;
+
+ supply_register_by_name (regcache, "pc", &newpc);
+}
+
+/* Default implementation of linux_target_ops method "get_pc" for
+ 32-bit pc register which is literally named "pc". */
+
+CORE_ADDR
+linux_get_pc_32bit (struct regcache *regcache)
+{
+ uint32_t pc;
+
+ collect_register_by_name (regcache, "pc", &pc);
+ if (debug_threads)
+ debug_printf ("stop pc is 0x%" PRIx32 "\n", pc);
+ return pc;
+}
+
+/* Default implementation of linux_target_ops method "set_pc" for
+ 64-bit pc register which is literally named "pc". */
+
+void
+linux_set_pc_64bit (struct regcache *regcache, CORE_ADDR pc)
+{
+ uint64_t newpc = pc;
+
+ supply_register_by_name (regcache, "pc", &newpc);
+}
+
+/* Default implementation of linux_target_ops method "get_pc" for
+ 64-bit pc register which is literally named "pc". */
+
+CORE_ADDR
+linux_get_pc_64bit (struct regcache *regcache)
+{
+ uint64_t pc;
+
+ collect_register_by_name (regcache, "pc", &pc);
+ if (debug_threads)
+ debug_printf ("stop pc is 0x%" PRIx64 "\n", pc);
+ return pc;
+}
+
+/* See linux-low.h. */
+
+int
+linux_get_auxv (int wordsize, CORE_ADDR match, CORE_ADDR *valp)
+{
+ gdb_byte *data = (gdb_byte *) alloca (2 * wordsize);
+ int offset = 0;
+
+ gdb_assert (wordsize == 4 || wordsize == 8);
+
+ while ((*the_target->read_auxv) (offset, data, 2 * wordsize) == 2 * wordsize)
+ {
+ if (wordsize == 4)
+ {
+ uint32_t *data_p = (uint32_t *) data;
+ if (data_p[0] == match)
+ {
+ *valp = data_p[1];
+ return 1;
+ }
+ }
+ else
+ {
+ uint64_t *data_p = (uint64_t *) data;
+ if (data_p[0] == match)
+ {
+ *valp = data_p[1];
+ return 1;
+ }
+ }
+
+ offset += 2 * wordsize;
+ }
+
+ return 0;
+}
+
+/* See linux-low.h. */
+
+CORE_ADDR
+linux_get_hwcap (int wordsize)
+{
+ CORE_ADDR hwcap = 0;
+ linux_get_auxv (wordsize, AT_HWCAP, &hwcap);
+ return hwcap;
+}
+
+/* See linux-low.h. */
+
+CORE_ADDR
+linux_get_hwcap2 (int wordsize)
+{
+ CORE_ADDR hwcap2 = 0;
+ linux_get_auxv (wordsize, AT_HWCAP2, &hwcap2);
+ return hwcap2;
+}
+
+static process_stratum_target linux_target_ops = {
+ linux_create_inferior,
+ linux_post_create_inferior,
+ linux_attach,
+ linux_kill,
+ linux_detach,
+ linux_mourn,
+ linux_join,
+ linux_thread_alive,
+ linux_resume,
+ linux_wait,
+ linux_fetch_registers,
+ linux_store_registers,
+ linux_prepare_to_access_memory,
+ linux_done_accessing_memory,
+ linux_read_memory,
+ linux_write_memory,
+ linux_look_up_symbols,
+ linux_request_interrupt,
+ linux_read_auxv,
+ linux_supports_z_point_type,
+ linux_insert_point,
+ linux_remove_point,
+ linux_stopped_by_sw_breakpoint,
+ linux_supports_stopped_by_sw_breakpoint,
+ linux_stopped_by_hw_breakpoint,
+ linux_supports_stopped_by_hw_breakpoint,
+ linux_supports_hardware_single_step,
+ linux_stopped_by_watchpoint,
+ linux_stopped_data_address,
+#if defined(__UCLIBC__) && defined(HAS_NOMMU) \
+ && defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) \
+ && defined(PT_TEXT_END_ADDR)
+ linux_read_offsets,
+#else
+ NULL,
+#endif
+#ifdef USE_THREAD_DB
+ thread_db_get_tls_address,
+#else
+ NULL,
+#endif
+ hostio_last_error_from_errno,
+ linux_qxfer_osdata,
+ linux_xfer_siginfo,
+ linux_supports_non_stop,
+ linux_async,
+ linux_start_non_stop,
+ linux_supports_multi_process,
+ linux_supports_fork_events,
+ linux_supports_vfork_events,
+ linux_supports_exec_events,
+ linux_handle_new_gdb_connection,
+#ifdef USE_THREAD_DB
+ thread_db_handle_monitor_command,
+#else
+ NULL,
+#endif
+ linux_common_core_of_thread,
+ linux_read_loadmap,
+ linux_process_qsupported,
+ linux_supports_tracepoints,
+ linux_read_pc,
+ linux_write_pc,
+ linux_thread_stopped,
+ NULL,
+ linux_pause_all,
+ linux_unpause_all,
+ linux_stabilize_threads,
+ linux_install_fast_tracepoint_jump_pad,
+ linux_emit_ops,
+ linux_supports_disable_randomization,
+ linux_get_min_fast_tracepoint_insn_len,
+ linux_qxfer_libraries_svr4,
+ linux_supports_agent,
+#ifdef HAVE_LINUX_BTRACE
+ linux_enable_btrace,
+ linux_low_disable_btrace,
+ linux_low_read_btrace,
+ linux_low_btrace_conf,
+#else
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+#endif
+ linux_supports_range_stepping,
+ linux_proc_pid_to_exec_file,
+ linux_mntns_open_cloexec,
+ linux_mntns_unlink,
+ linux_mntns_readlink,
+ linux_breakpoint_kind_from_pc,
+ linux_sw_breakpoint_from_kind,
+ linux_proc_tid_get_name,
+ linux_breakpoint_kind_from_current_state,
+ linux_supports_software_single_step,
+ linux_supports_catch_syscall,
+ linux_get_ipa_tdesc_idx,
+#if USE_THREAD_DB
+ thread_db_thread_handle,
+#else
+ NULL,
+#endif
+};
+
+#ifdef HAVE_LINUX_REGSETS
+void
+initialize_regsets_info (struct regsets_info *info)
+{
+ for (info->num_regsets = 0;
+ info->regsets[info->num_regsets].size >= 0;
+ info->num_regsets++)
+ ;
+}
+#endif
+
+void
+initialize_low (void)
+{
+ struct sigaction sigchld_action;
+
+ memset (&sigchld_action, 0, sizeof (sigchld_action));
+ set_target_ops (&linux_target_ops);
+
+ linux_ptrace_init_warnings ();
+ linux_proc_init_warnings ();
+
+ sigchld_action.sa_handler = sigchld_handler;
+ sigemptyset (&sigchld_action.sa_mask);
+ sigchld_action.sa_flags = SA_RESTART;
+ sigaction (SIGCHLD, &sigchld_action, NULL);
+
+ initialize_low_arch ();
+
+ linux_check_ptrace_features ();
+}
+++ /dev/null
-/* GNU/Linux/m32r specific low level interface, for the remote server for GDB.
- Copyright (C) 2005-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-/* Defined in auto-generated file reg-m32r.c. */
-void init_registers_m32r (void);
-extern const struct target_desc *tdesc_m32r;
-
-#define m32r_num_regs 25
-
-static int m32r_regmap[] = {
-#ifdef PT_R0
- PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
- PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_FP, PT_LR, PT_SPU,
- PT_PSW, PT_CBR, PT_SPI, PT_SPU, PT_BPC, PT_PC, PT_ACCL, PT_ACCH, PT_EVB
-#else
- 4 * 4, 4 * 5, 4 * 6, 4 * 7, 4 * 0, 4 * 1, 4 * 2, 4 * 8,
- 4 * 9, 4 * 10, 4 * 11, 4 * 12, 4 * 13, 4 * 24, 4 * 25, 4 * 23,
- 4 * 19, 4 * 31, 4 * 26, 4 * 23, 4 * 20, 4 * 30, 4 * 16, 4 * 15, 4 * 32
-#endif
-};
-
-static int
-m32r_cannot_store_register (int regno)
-{
- return (regno >= m32r_num_regs);
-}
-
-static int
-m32r_cannot_fetch_register (int regno)
-{
- return (regno >= m32r_num_regs);
-}
-
-static const unsigned short m32r_breakpoint = 0x10f1;
-#define m32r_breakpoint_len 2
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-m32r_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = m32r_breakpoint_len;
- return (const gdb_byte *) &m32r_breakpoint;
-}
-
-static int
-m32r_breakpoint_at (CORE_ADDR where)
-{
- unsigned short insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn,
- m32r_breakpoint_len);
- if (insn == m32r_breakpoint)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-static void
-m32r_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_m32r;
-}
-
-/* Support for hardware single step. */
-
-static int
-m32r_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static struct usrregs_info m32r_usrregs_info =
- {
- m32r_num_regs,
- m32r_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &m32r_usrregs_info,
- };
-
-static const struct regs_info *
-m32r_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- m32r_arch_setup,
- m32r_regs_info,
- m32r_cannot_fetch_register,
- m32r_cannot_store_register,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_from_pc */
- m32r_sw_breakpoint_from_kind,
- NULL,
- 0,
- m32r_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- m32r_supports_hardware_single_step,
-};
-
-void
-initialize_low_arch (void)
-{
- init_registers_m32r ();
-}
--- /dev/null
+/* GNU/Linux/m32r specific low level interface, for the remote server for GDB.
+ Copyright (C) 2005-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+/* Defined in auto-generated file reg-m32r.c. */
+void init_registers_m32r (void);
+extern const struct target_desc *tdesc_m32r;
+
+#define m32r_num_regs 25
+
+static int m32r_regmap[] = {
+#ifdef PT_R0
+ PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
+ PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_FP, PT_LR, PT_SPU,
+ PT_PSW, PT_CBR, PT_SPI, PT_SPU, PT_BPC, PT_PC, PT_ACCL, PT_ACCH, PT_EVB
+#else
+ 4 * 4, 4 * 5, 4 * 6, 4 * 7, 4 * 0, 4 * 1, 4 * 2, 4 * 8,
+ 4 * 9, 4 * 10, 4 * 11, 4 * 12, 4 * 13, 4 * 24, 4 * 25, 4 * 23,
+ 4 * 19, 4 * 31, 4 * 26, 4 * 23, 4 * 20, 4 * 30, 4 * 16, 4 * 15, 4 * 32
+#endif
+};
+
+static int
+m32r_cannot_store_register (int regno)
+{
+ return (regno >= m32r_num_regs);
+}
+
+static int
+m32r_cannot_fetch_register (int regno)
+{
+ return (regno >= m32r_num_regs);
+}
+
+static const unsigned short m32r_breakpoint = 0x10f1;
+#define m32r_breakpoint_len 2
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+m32r_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = m32r_breakpoint_len;
+ return (const gdb_byte *) &m32r_breakpoint;
+}
+
+static int
+m32r_breakpoint_at (CORE_ADDR where)
+{
+ unsigned short insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn,
+ m32r_breakpoint_len);
+ if (insn == m32r_breakpoint)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+static void
+m32r_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_m32r;
+}
+
+/* Support for hardware single step. */
+
+static int
+m32r_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static struct usrregs_info m32r_usrregs_info =
+ {
+ m32r_num_regs,
+ m32r_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &m32r_usrregs_info,
+ };
+
+static const struct regs_info *
+m32r_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ m32r_arch_setup,
+ m32r_regs_info,
+ m32r_cannot_fetch_register,
+ m32r_cannot_store_register,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_from_pc */
+ m32r_sw_breakpoint_from_kind,
+ NULL,
+ 0,
+ m32r_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ m32r_supports_hardware_single_step,
+};
+
+void
+initialize_low_arch (void)
+{
+ init_registers_m32r ();
+}
+++ /dev/null
-/* GNU/Linux/m68k specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-/* Defined in auto-generated file reg-m68k.c. */
-void init_registers_m68k (void);
-extern const struct target_desc *tdesc_m68k;
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-#define m68k_num_regs 29
-#define m68k_num_gregs 18
-
-/* This table must line up with REGISTER_NAMES in tm-m68k.h */
-static int m68k_regmap[] =
-{
-#ifdef PT_D0
- PT_D0 * 4, PT_D1 * 4, PT_D2 * 4, PT_D3 * 4,
- PT_D4 * 4, PT_D5 * 4, PT_D6 * 4, PT_D7 * 4,
- PT_A0 * 4, PT_A1 * 4, PT_A2 * 4, PT_A3 * 4,
- PT_A4 * 4, PT_A5 * 4, PT_A6 * 4, PT_USP * 4,
- PT_SR * 4, PT_PC * 4,
-#else
- 14 * 4, 0 * 4, 1 * 4, 2 * 4, 3 * 4, 4 * 4, 5 * 4, 6 * 4,
- 7 * 4, 8 * 4, 9 * 4, 10 * 4, 11 * 4, 12 * 4, 13 * 4, 15 * 4,
- 17 * 4, 18 * 4,
-#endif
-#ifdef PT_FP0
- PT_FP0 * 4, PT_FP1 * 4, PT_FP2 * 4, PT_FP3 * 4,
- PT_FP4 * 4, PT_FP5 * 4, PT_FP6 * 4, PT_FP7 * 4,
- PT_FPCR * 4, PT_FPSR * 4, PT_FPIAR * 4
-#else
- 21 * 4, 24 * 4, 27 * 4, 30 * 4, 33 * 4, 36 * 4,
- 39 * 4, 42 * 4, 45 * 4, 46 * 4, 47 * 4
-#endif
-};
-
-static int
-m68k_cannot_store_register (int regno)
-{
- return (regno >= m68k_num_regs);
-}
-
-static int
-m68k_cannot_fetch_register (int regno)
-{
- return (regno >= m68k_num_regs);
-}
-
-#ifdef HAVE_PTRACE_GETREGS
-#include <sys/procfs.h>
-#include "nat/gdb_ptrace.h"
-
-static void
-m68k_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
-
- for (i = 0; i < m68k_num_gregs; i++)
- collect_register (regcache, i, (char *) buf + m68k_regmap[i]);
-}
-
-static void
-m68k_store_gregset (struct regcache *regcache, const void *buf)
-{
- int i;
-
- for (i = 0; i < m68k_num_gregs; i++)
- supply_register (regcache, i, (const char *) buf + m68k_regmap[i]);
-}
-
-static void
-m68k_fill_fpregset (struct regcache *regcache, void *buf)
-{
- int i;
-
- for (i = m68k_num_gregs; i < m68k_num_regs; i++)
- collect_register (regcache, i, ((char *) buf
- + (m68k_regmap[i] - m68k_regmap[m68k_num_gregs])));
-}
-
-static void
-m68k_store_fpregset (struct regcache *regcache, const void *buf)
-{
- int i;
-
- for (i = m68k_num_gregs; i < m68k_num_regs; i++)
- supply_register (regcache, i, ((const char *) buf
- + (m68k_regmap[i] - m68k_regmap[m68k_num_gregs])));
-}
-
-#endif /* HAVE_PTRACE_GETREGS */
-
-static struct regset_info m68k_regsets[] = {
-#ifdef HAVE_PTRACE_GETREGS
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
- GENERAL_REGS,
- m68k_fill_gregset, m68k_store_gregset },
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (elf_fpregset_t),
- FP_REGS,
- m68k_fill_fpregset, m68k_store_fpregset },
-#endif /* HAVE_PTRACE_GETREGS */
- NULL_REGSET
-};
-
-static const gdb_byte m68k_breakpoint[] = { 0x4E, 0x4F };
-#define m68k_breakpoint_len 2
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-m68k_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = m68k_breakpoint_len;
- return m68k_breakpoint;
-}
-
-static int
-m68k_breakpoint_at (CORE_ADDR pc)
-{
- unsigned char c[2];
-
- read_inferior_memory (pc, c, 2);
- if (c[0] == 0x4E && c[1] == 0x4F)
- return 1;
-
- return 0;
-}
-
-#include <asm/ptrace.h>
-
-#ifdef PTRACE_GET_THREAD_AREA
-/* Fetch the thread-local storage pointer for libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
- return PS_ERR;
-
- /* IDX is the bias from the thread pointer to the beginning of the
- thread descriptor. It has to be subtracted due to implementation
- quirks in libthread_db. */
- *base = (void *) ((char *)*base - idx);
-
- return PS_OK;
-}
-#endif /* PTRACE_GET_THREAD_AREA */
-
-static struct regsets_info m68k_regsets_info =
- {
- m68k_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info m68k_usrregs_info =
- {
- m68k_num_regs,
- m68k_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &m68k_usrregs_info,
- &m68k_regsets_info
- };
-
-static const struct regs_info *
-m68k_regs_info (void)
-{
- return ®s_info;
-}
-
-static void
-m68k_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_m68k;
-}
-
-/* Support for hardware single step. */
-
-static int
-m68k_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-struct linux_target_ops the_low_target = {
- m68k_arch_setup,
- m68k_regs_info,
- m68k_cannot_fetch_register,
- m68k_cannot_store_register,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_kind_from_pc */
- m68k_sw_breakpoint_from_kind,
- NULL,
- 2,
- m68k_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- m68k_supports_hardware_single_step,
-};
-
-void
-initialize_low_arch (void)
-{
- /* Initialize the Linux target descriptions. */
- init_registers_m68k ();
-
- initialize_regsets_info (&m68k_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/m68k specific low level interface, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+/* Defined in auto-generated file reg-m68k.c. */
+void init_registers_m68k (void);
+extern const struct target_desc *tdesc_m68k;
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+#define m68k_num_regs 29
+#define m68k_num_gregs 18
+
+/* This table must line up with REGISTER_NAMES in tm-m68k.h */
+static int m68k_regmap[] =
+{
+#ifdef PT_D0
+ PT_D0 * 4, PT_D1 * 4, PT_D2 * 4, PT_D3 * 4,
+ PT_D4 * 4, PT_D5 * 4, PT_D6 * 4, PT_D7 * 4,
+ PT_A0 * 4, PT_A1 * 4, PT_A2 * 4, PT_A3 * 4,
+ PT_A4 * 4, PT_A5 * 4, PT_A6 * 4, PT_USP * 4,
+ PT_SR * 4, PT_PC * 4,
+#else
+ 14 * 4, 0 * 4, 1 * 4, 2 * 4, 3 * 4, 4 * 4, 5 * 4, 6 * 4,
+ 7 * 4, 8 * 4, 9 * 4, 10 * 4, 11 * 4, 12 * 4, 13 * 4, 15 * 4,
+ 17 * 4, 18 * 4,
+#endif
+#ifdef PT_FP0
+ PT_FP0 * 4, PT_FP1 * 4, PT_FP2 * 4, PT_FP3 * 4,
+ PT_FP4 * 4, PT_FP5 * 4, PT_FP6 * 4, PT_FP7 * 4,
+ PT_FPCR * 4, PT_FPSR * 4, PT_FPIAR * 4
+#else
+ 21 * 4, 24 * 4, 27 * 4, 30 * 4, 33 * 4, 36 * 4,
+ 39 * 4, 42 * 4, 45 * 4, 46 * 4, 47 * 4
+#endif
+};
+
+static int
+m68k_cannot_store_register (int regno)
+{
+ return (regno >= m68k_num_regs);
+}
+
+static int
+m68k_cannot_fetch_register (int regno)
+{
+ return (regno >= m68k_num_regs);
+}
+
+#ifdef HAVE_PTRACE_GETREGS
+#include <sys/procfs.h>
+#include "nat/gdb_ptrace.h"
+
+static void
+m68k_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+
+ for (i = 0; i < m68k_num_gregs; i++)
+ collect_register (regcache, i, (char *) buf + m68k_regmap[i]);
+}
+
+static void
+m68k_store_gregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+
+ for (i = 0; i < m68k_num_gregs; i++)
+ supply_register (regcache, i, (const char *) buf + m68k_regmap[i]);
+}
+
+static void
+m68k_fill_fpregset (struct regcache *regcache, void *buf)
+{
+ int i;
+
+ for (i = m68k_num_gregs; i < m68k_num_regs; i++)
+ collect_register (regcache, i, ((char *) buf
+ + (m68k_regmap[i] - m68k_regmap[m68k_num_gregs])));
+}
+
+static void
+m68k_store_fpregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+
+ for (i = m68k_num_gregs; i < m68k_num_regs; i++)
+ supply_register (regcache, i, ((const char *) buf
+ + (m68k_regmap[i] - m68k_regmap[m68k_num_gregs])));
+}
+
+#endif /* HAVE_PTRACE_GETREGS */
+
+static struct regset_info m68k_regsets[] = {
+#ifdef HAVE_PTRACE_GETREGS
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
+ GENERAL_REGS,
+ m68k_fill_gregset, m68k_store_gregset },
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (elf_fpregset_t),
+ FP_REGS,
+ m68k_fill_fpregset, m68k_store_fpregset },
+#endif /* HAVE_PTRACE_GETREGS */
+ NULL_REGSET
+};
+
+static const gdb_byte m68k_breakpoint[] = { 0x4E, 0x4F };
+#define m68k_breakpoint_len 2
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+m68k_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = m68k_breakpoint_len;
+ return m68k_breakpoint;
+}
+
+static int
+m68k_breakpoint_at (CORE_ADDR pc)
+{
+ unsigned char c[2];
+
+ read_inferior_memory (pc, c, 2);
+ if (c[0] == 0x4E && c[1] == 0x4F)
+ return 1;
+
+ return 0;
+}
+
+#include <asm/ptrace.h>
+
+#ifdef PTRACE_GET_THREAD_AREA
+/* Fetch the thread-local storage pointer for libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
+ return PS_ERR;
+
+ /* IDX is the bias from the thread pointer to the beginning of the
+ thread descriptor. It has to be subtracted due to implementation
+ quirks in libthread_db. */
+ *base = (void *) ((char *)*base - idx);
+
+ return PS_OK;
+}
+#endif /* PTRACE_GET_THREAD_AREA */
+
+static struct regsets_info m68k_regsets_info =
+ {
+ m68k_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info m68k_usrregs_info =
+ {
+ m68k_num_regs,
+ m68k_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &m68k_usrregs_info,
+ &m68k_regsets_info
+ };
+
+static const struct regs_info *
+m68k_regs_info (void)
+{
+ return ®s_info;
+}
+
+static void
+m68k_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_m68k;
+}
+
+/* Support for hardware single step. */
+
+static int
+m68k_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+struct linux_target_ops the_low_target = {
+ m68k_arch_setup,
+ m68k_regs_info,
+ m68k_cannot_fetch_register,
+ m68k_cannot_store_register,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_kind_from_pc */
+ m68k_sw_breakpoint_from_kind,
+ NULL,
+ 2,
+ m68k_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ m68k_supports_hardware_single_step,
+};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+ init_registers_m68k ();
+
+ initialize_regsets_info (&m68k_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/MIPS specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-#include "nat/gdb_ptrace.h"
-#include <endian.h>
-
-#include "nat/mips-linux-watch.h"
-#include "gdb_proc_service.h"
-
-/* Defined in auto-generated file mips-linux.c. */
-void init_registers_mips_linux (void);
-extern const struct target_desc *tdesc_mips_linux;
-
-/* Defined in auto-generated file mips-dsp-linux.c. */
-void init_registers_mips_dsp_linux (void);
-extern const struct target_desc *tdesc_mips_dsp_linux;
-
-/* Defined in auto-generated file mips64-linux.c. */
-void init_registers_mips64_linux (void);
-extern const struct target_desc *tdesc_mips64_linux;
-
-/* Defined in auto-generated file mips64-dsp-linux.c. */
-void init_registers_mips64_dsp_linux (void);
-extern const struct target_desc *tdesc_mips64_dsp_linux;
-
-#ifdef __mips64
-#define tdesc_mips_linux tdesc_mips64_linux
-#define tdesc_mips_dsp_linux tdesc_mips64_dsp_linux
-#endif
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-#define mips_num_regs 73
-#define mips_dsp_num_regs 80
-
-#include <asm/ptrace.h>
-
-#ifndef DSP_BASE
-#define DSP_BASE 71
-#define DSP_CONTROL 77
-#endif
-
-union mips_register
-{
- unsigned char buf[8];
-
- /* Deliberately signed, for proper sign extension. */
- int reg32;
- long long reg64;
-};
-
-/* Return the ptrace ``address'' of register REGNO. */
-
-#define mips_base_regs \
- -1, 1, 2, 3, 4, 5, 6, 7, \
- 8, 9, 10, 11, 12, 13, 14, 15, \
- 16, 17, 18, 19, 20, 21, 22, 23, \
- 24, 25, 26, 27, 28, 29, 30, 31, \
- \
- -1, MMLO, MMHI, BADVADDR, CAUSE, PC, \
- \
- FPR_BASE, FPR_BASE + 1, FPR_BASE + 2, FPR_BASE + 3, \
- FPR_BASE + 4, FPR_BASE + 5, FPR_BASE + 6, FPR_BASE + 7, \
- FPR_BASE + 8, FPR_BASE + 9, FPR_BASE + 10, FPR_BASE + 11, \
- FPR_BASE + 12, FPR_BASE + 13, FPR_BASE + 14, FPR_BASE + 15, \
- FPR_BASE + 16, FPR_BASE + 17, FPR_BASE + 18, FPR_BASE + 19, \
- FPR_BASE + 20, FPR_BASE + 21, FPR_BASE + 22, FPR_BASE + 23, \
- FPR_BASE + 24, FPR_BASE + 25, FPR_BASE + 26, FPR_BASE + 27, \
- FPR_BASE + 28, FPR_BASE + 29, FPR_BASE + 30, FPR_BASE + 31, \
- FPC_CSR, FPC_EIR
-
-#define mips_dsp_regs \
- DSP_BASE, DSP_BASE + 1, DSP_BASE + 2, DSP_BASE + 3, \
- DSP_BASE + 4, DSP_BASE + 5, \
- DSP_CONTROL
-
-static int mips_regmap[mips_num_regs] = {
- mips_base_regs,
- 0
-};
-
-static int mips_dsp_regmap[mips_dsp_num_regs] = {
- mips_base_regs,
- mips_dsp_regs,
- 0
-};
-
-/* DSP registers are not in any regset and can only be accessed
- individually. */
-
-static unsigned char mips_dsp_regset_bitmap[(mips_dsp_num_regs + 7) / 8] = {
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x80
-};
-
-static int have_dsp = -1;
-
-/* Try peeking at an arbitrarily chosen DSP register and pick the available
- user register set accordingly. */
-
-static const struct target_desc *
-mips_read_description (void)
-{
- if (have_dsp < 0)
- {
- int pid = lwpid_of (current_thread);
-
- errno = 0;
- ptrace (PTRACE_PEEKUSER, pid, DSP_CONTROL, 0);
- switch (errno)
- {
- case 0:
- have_dsp = 1;
- break;
- case EIO:
- have_dsp = 0;
- break;
- default:
- perror_with_name ("ptrace");
- break;
- }
- }
-
- return have_dsp ? tdesc_mips_dsp_linux : tdesc_mips_linux;
-}
-
-static void
-mips_arch_setup (void)
-{
- current_process ()->tdesc = mips_read_description ();
-}
-
-static struct usrregs_info *
-get_usrregs_info (void)
-{
- const struct regs_info *regs_info = the_low_target.regs_info ();
-
- return regs_info->usrregs;
-}
-
-/* Per-process arch-specific data we want to keep. */
-
-struct arch_process_info
-{
- /* -1 if the kernel and/or CPU do not support watch registers.
- 1 if watch_readback is valid and we can read style, num_valid
- and the masks.
- 0 if we need to read the watch_readback. */
-
- int watch_readback_valid;
-
- /* Cached watch register read values. */
-
- struct pt_watch_regs watch_readback;
-
- /* Current watchpoint requests for this process. */
-
- struct mips_watchpoint *current_watches;
-
- /* The current set of watch register values for writing the
- registers. */
-
- struct pt_watch_regs watch_mirror;
-};
-
-/* Per-thread arch-specific data we want to keep. */
-
-struct arch_lwp_info
-{
- /* Non-zero if our copy differs from what's recorded in the thread. */
- int watch_registers_changed;
-};
-
-/* From mips-linux-nat.c. */
-
-/* Pseudo registers can not be read. ptrace does not provide a way to
- read (or set) PS_REGNUM, and there's no point in reading or setting
- ZERO_REGNUM, it's always 0. We also can not set BADVADDR, CAUSE,
- or FCRIR via ptrace(). */
-
-static int
-mips_cannot_fetch_register (int regno)
-{
- const struct target_desc *tdesc;
-
- if (get_usrregs_info ()->regmap[regno] == -1)
- return 1;
-
- tdesc = current_process ()->tdesc;
-
- /* On n32 we can't access 64-bit registers via PTRACE_PEEKUSR. */
- if (register_size (tdesc, regno) > sizeof (PTRACE_XFER_TYPE))
- return 1;
-
- if (find_regno (tdesc, "r0") == regno)
- return 1;
-
- return 0;
-}
-
-static int
-mips_cannot_store_register (int regno)
-{
- const struct target_desc *tdesc;
-
- if (get_usrregs_info ()->regmap[regno] == -1)
- return 1;
-
- tdesc = current_process ()->tdesc;
-
- /* On n32 we can't access 64-bit registers via PTRACE_POKEUSR. */
- if (register_size (tdesc, regno) > sizeof (PTRACE_XFER_TYPE))
- return 1;
-
- if (find_regno (tdesc, "r0") == regno)
- return 1;
-
- if (find_regno (tdesc, "cause") == regno)
- return 1;
-
- if (find_regno (tdesc, "badvaddr") == regno)
- return 1;
-
- if (find_regno (tdesc, "fir") == regno)
- return 1;
-
- return 0;
-}
-
-static int
-mips_fetch_register (struct regcache *regcache, int regno)
-{
- const struct target_desc *tdesc = current_process ()->tdesc;
-
- if (find_regno (tdesc, "r0") == regno)
- {
- supply_register_zeroed (regcache, regno);
- return 1;
- }
-
- return 0;
-}
-
-static CORE_ADDR
-mips_get_pc (struct regcache *regcache)
-{
- union mips_register pc;
- collect_register_by_name (regcache, "pc", pc.buf);
- return register_size (regcache->tdesc, 0) == 4 ? pc.reg32 : pc.reg64;
-}
-
-static void
-mips_set_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- union mips_register newpc;
- if (register_size (regcache->tdesc, 0) == 4)
- newpc.reg32 = pc;
- else
- newpc.reg64 = pc;
-
- supply_register_by_name (regcache, "pc", newpc.buf);
-}
-
-/* Correct in either endianness. */
-static const unsigned int mips_breakpoint = 0x0005000d;
-#define mips_breakpoint_len 4
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-mips_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = mips_breakpoint_len;
- return (const gdb_byte *) &mips_breakpoint;
-}
-
-static int
-mips_breakpoint_at (CORE_ADDR where)
-{
- unsigned int insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == mips_breakpoint)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-/* Mark the watch registers of lwp, represented by ENTRY, as changed. */
-
-static void
-update_watch_registers_callback (thread_info *thread)
-{
- struct lwp_info *lwp = get_thread_lwp (thread);
-
- /* The actual update is done later just before resuming the lwp,
- we just mark that the registers need updating. */
- lwp->arch_private->watch_registers_changed = 1;
-
- /* If the lwp isn't stopped, force it to momentarily pause, so
- we can update its watch registers. */
- if (!lwp->stopped)
- linux_stop_lwp (lwp);
-}
-
-/* This is the implementation of linux_target_ops method
- new_process. */
-
-static struct arch_process_info *
-mips_linux_new_process (void)
-{
- struct arch_process_info *info = XCNEW (struct arch_process_info);
-
- return info;
-}
-
-/* This is the implementation of linux_target_ops method
- delete_process. */
-
-static void
-mips_linux_delete_process (struct arch_process_info *info)
-{
- xfree (info);
-}
-
-/* This is the implementation of linux_target_ops method new_thread.
- Mark the watch registers as changed, so the threads' copies will
- be updated. */
-
-static void
-mips_linux_new_thread (struct lwp_info *lwp)
-{
- struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
-
- info->watch_registers_changed = 1;
-
- lwp->arch_private = info;
-}
-
-/* Function to call when a thread is being deleted. */
-
-static void
-mips_linux_delete_thread (struct arch_lwp_info *arch_lwp)
-{
- xfree (arch_lwp);
-}
-
-/* Create a new mips_watchpoint and add it to the list. */
-
-static void
-mips_add_watchpoint (struct arch_process_info *priv, CORE_ADDR addr, int len,
- enum target_hw_bp_type watch_type)
-{
- struct mips_watchpoint *new_watch;
- struct mips_watchpoint **pw;
-
- new_watch = XNEW (struct mips_watchpoint);
- new_watch->addr = addr;
- new_watch->len = len;
- new_watch->type = watch_type;
- new_watch->next = NULL;
-
- pw = &priv->current_watches;
- while (*pw != NULL)
- pw = &(*pw)->next;
- *pw = new_watch;
-}
-
-/* Hook to call when a new fork is attached. */
-
-static void
-mips_linux_new_fork (struct process_info *parent,
- struct process_info *child)
-{
- struct arch_process_info *parent_private;
- struct arch_process_info *child_private;
- struct mips_watchpoint *wp;
-
- /* These are allocated by linux_add_process. */
- gdb_assert (parent->priv != NULL
- && parent->priv->arch_private != NULL);
- gdb_assert (child->priv != NULL
- && child->priv->arch_private != NULL);
-
- /* Linux kernel before 2.6.33 commit
- 72f674d203cd230426437cdcf7dd6f681dad8b0d
- will inherit hardware debug registers from parent
- on fork/vfork/clone. Newer Linux kernels create such tasks with
- zeroed debug registers.
-
- GDB core assumes the child inherits the watchpoints/hw
- breakpoints of the parent, and will remove them all from the
- forked off process. Copy the debug registers mirrors into the
- new process so that all breakpoints and watchpoints can be
- removed together. The debug registers mirror will become zeroed
- in the end before detaching the forked off process, thus making
- this compatible with older Linux kernels too. */
-
- parent_private = parent->priv->arch_private;
- child_private = child->priv->arch_private;
-
- child_private->watch_readback_valid = parent_private->watch_readback_valid;
- child_private->watch_readback = parent_private->watch_readback;
-
- for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
- mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
-
- child_private->watch_mirror = parent_private->watch_mirror;
-}
-/* This is the implementation of linux_target_ops method
- prepare_to_resume. If the watch regs have changed, update the
- thread's copies. */
-
-static void
-mips_linux_prepare_to_resume (struct lwp_info *lwp)
-{
- ptid_t ptid = ptid_of (get_lwp_thread (lwp));
- struct process_info *proc = find_process_pid (ptid.pid ());
- struct arch_process_info *priv = proc->priv->arch_private;
-
- if (lwp->arch_private->watch_registers_changed)
- {
- /* Only update the watch registers if we have set or unset a
- watchpoint already. */
- if (mips_linux_watch_get_num_valid (&priv->watch_mirror) > 0)
- {
- /* Write the mirrored watch register values. */
- int tid = ptid.lwp ();
-
- if (-1 == ptrace (PTRACE_SET_WATCH_REGS, tid,
- &priv->watch_mirror, NULL))
- perror_with_name ("Couldn't write watch register");
- }
-
- lwp->arch_private->watch_registers_changed = 0;
- }
-}
-
-static int
-mips_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_READ_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
- return 0;
- }
-}
-
-/* This is the implementation of linux_target_ops method
- insert_point. */
-
-static int
-mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
-{
- struct process_info *proc = current_process ();
- struct arch_process_info *priv = proc->priv->arch_private;
- struct pt_watch_regs regs;
- long lwpid;
- enum target_hw_bp_type watch_type;
- uint32_t irw;
-
- lwpid = lwpid_of (current_thread);
- if (!mips_linux_read_watch_registers (lwpid,
- &priv->watch_readback,
- &priv->watch_readback_valid,
- 0))
- return -1;
-
- if (len <= 0)
- return -1;
-
- regs = priv->watch_readback;
- /* Add the current watches. */
- mips_linux_watch_populate_regs (priv->current_watches, ®s);
-
- /* Now try to add the new watch. */
- watch_type = raw_bkpt_type_to_target_hw_bp_type (type);
- irw = mips_linux_watch_type_to_irw (watch_type);
- if (!mips_linux_watch_try_one_watch (®s, addr, len, irw))
- return -1;
-
- /* It fit. Stick it on the end of the list. */
- mips_add_watchpoint (priv, addr, len, watch_type);
-
- priv->watch_mirror = regs;
-
- /* Only update the threads of this process. */
- for_each_thread (proc->pid, update_watch_registers_callback);
-
- return 0;
-}
-
-/* This is the implementation of linux_target_ops method
- remove_point. */
-
-static int
-mips_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
-{
- struct process_info *proc = current_process ();
- struct arch_process_info *priv = proc->priv->arch_private;
-
- int deleted_one;
- enum target_hw_bp_type watch_type;
-
- struct mips_watchpoint **pw;
- struct mips_watchpoint *w;
-
- /* Search for a known watch that matches. Then unlink and free it. */
- watch_type = raw_bkpt_type_to_target_hw_bp_type (type);
- deleted_one = 0;
- pw = &priv->current_watches;
- while ((w = *pw))
- {
- if (w->addr == addr && w->len == len && w->type == watch_type)
- {
- *pw = w->next;
- free (w);
- deleted_one = 1;
- break;
- }
- pw = &(w->next);
- }
-
- if (!deleted_one)
- return -1; /* We don't know about it, fail doing nothing. */
-
- /* At this point watch_readback is known to be valid because we
- could not have added the watch without reading it. */
- gdb_assert (priv->watch_readback_valid == 1);
-
- priv->watch_mirror = priv->watch_readback;
- mips_linux_watch_populate_regs (priv->current_watches,
- &priv->watch_mirror);
-
- /* Only update the threads of this process. */
- for_each_thread (proc->pid, update_watch_registers_callback);
-
- return 0;
-}
-
-/* This is the implementation of linux_target_ops method
- stopped_by_watchpoint. The watchhi R and W bits indicate
- the watch register triggered. */
-
-static int
-mips_stopped_by_watchpoint (void)
-{
- struct process_info *proc = current_process ();
- struct arch_process_info *priv = proc->priv->arch_private;
- int n;
- int num_valid;
- long lwpid = lwpid_of (current_thread);
-
- if (!mips_linux_read_watch_registers (lwpid,
- &priv->watch_readback,
- &priv->watch_readback_valid,
- 1))
- return 0;
-
- num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback);
-
- for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++)
- if (mips_linux_watch_get_watchhi (&priv->watch_readback, n)
- & (R_MASK | W_MASK))
- return 1;
-
- return 0;
-}
-
-/* This is the implementation of linux_target_ops method
- stopped_data_address. */
-
-static CORE_ADDR
-mips_stopped_data_address (void)
-{
- struct process_info *proc = current_process ();
- struct arch_process_info *priv = proc->priv->arch_private;
- int n;
- int num_valid;
- long lwpid = lwpid_of (current_thread);
-
- /* On MIPS we don't know the low order 3 bits of the data address.
- GDB does not support remote targets that can't report the
- watchpoint address. So, make our best guess; return the starting
- address of a watchpoint request which overlaps the one that
- triggered. */
-
- if (!mips_linux_read_watch_registers (lwpid,
- &priv->watch_readback,
- &priv->watch_readback_valid,
- 0))
- return 0;
-
- num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback);
-
- for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++)
- if (mips_linux_watch_get_watchhi (&priv->watch_readback, n)
- & (R_MASK | W_MASK))
- {
- CORE_ADDR t_low, t_hi;
- int t_irw;
- struct mips_watchpoint *watch;
-
- t_low = mips_linux_watch_get_watchlo (&priv->watch_readback, n);
- t_irw = t_low & IRW_MASK;
- t_hi = (mips_linux_watch_get_watchhi (&priv->watch_readback, n)
- | IRW_MASK);
- t_low &= ~(CORE_ADDR)t_hi;
-
- for (watch = priv->current_watches;
- watch != NULL;
- watch = watch->next)
- {
- CORE_ADDR addr = watch->addr;
- CORE_ADDR last_byte = addr + watch->len - 1;
-
- if ((t_irw & mips_linux_watch_type_to_irw (watch->type)) == 0)
- {
- /* Different type. */
- continue;
- }
- /* Check for overlap of even a single byte. */
- if (last_byte >= t_low && addr <= t_low + t_hi)
- return addr;
- }
- }
-
- /* Shouldn't happen. */
- return 0;
-}
-
-/* Fetch the thread-local storage pointer for libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
- return PS_ERR;
-
- /* IDX is the bias from the thread pointer to the beginning of the
- thread descriptor. It has to be subtracted due to implementation
- quirks in libthread_db. */
- *base = (void *) ((char *)*base - idx);
-
- return PS_OK;
-}
-
-static void
-mips_collect_register (struct regcache *regcache,
- int use_64bit, int regno, union mips_register *reg)
-{
- union mips_register tmp_reg;
-
- if (use_64bit)
- {
- collect_register (regcache, regno, &tmp_reg.reg64);
- *reg = tmp_reg;
- }
- else
- {
- collect_register (regcache, regno, &tmp_reg.reg32);
- reg->reg64 = tmp_reg.reg32;
- }
-}
-
-static void
-mips_supply_register (struct regcache *regcache,
- int use_64bit, int regno, const union mips_register *reg)
-{
- int offset = 0;
-
- /* For big-endian 32-bit targets, ignore the high four bytes of each
- eight-byte slot. */
- if (__BYTE_ORDER == __BIG_ENDIAN && !use_64bit)
- offset = 4;
-
- supply_register (regcache, regno, reg->buf + offset);
-}
-
-#ifdef HAVE_PTRACE_GETREGS
-
-static void
-mips_collect_register_32bit (struct regcache *regcache,
- int use_64bit, int regno, unsigned char *buf)
-{
- union mips_register tmp_reg;
- int reg32;
-
- mips_collect_register (regcache, use_64bit, regno, &tmp_reg);
- reg32 = tmp_reg.reg64;
- memcpy (buf, ®32, 4);
-}
-
-static void
-mips_supply_register_32bit (struct regcache *regcache,
- int use_64bit, int regno, const unsigned char *buf)
-{
- union mips_register tmp_reg;
- int reg32;
-
- memcpy (®32, buf, 4);
- tmp_reg.reg64 = reg32;
- mips_supply_register (regcache, use_64bit, regno, &tmp_reg);
-}
-
-static void
-mips_fill_gregset (struct regcache *regcache, void *buf)
-{
- union mips_register *regset = (union mips_register *) buf;
- int i, use_64bit;
- const struct target_desc *tdesc = regcache->tdesc;
-
- use_64bit = (register_size (tdesc, 0) == 8);
-
- for (i = 1; i < 32; i++)
- mips_collect_register (regcache, use_64bit, i, regset + i);
-
- mips_collect_register (regcache, use_64bit,
- find_regno (tdesc, "lo"), regset + 32);
- mips_collect_register (regcache, use_64bit,
- find_regno (tdesc, "hi"), regset + 33);
- mips_collect_register (regcache, use_64bit,
- find_regno (tdesc, "pc"), regset + 34);
- mips_collect_register (regcache, use_64bit,
- find_regno (tdesc, "badvaddr"), regset + 35);
- mips_collect_register (regcache, use_64bit,
- find_regno (tdesc, "status"), regset + 36);
- mips_collect_register (regcache, use_64bit,
- find_regno (tdesc, "cause"), regset + 37);
-
- mips_collect_register (regcache, use_64bit,
- find_regno (tdesc, "restart"), regset + 0);
-}
-
-static void
-mips_store_gregset (struct regcache *regcache, const void *buf)
-{
- const union mips_register *regset = (const union mips_register *) buf;
- int i, use_64bit;
-
- use_64bit = (register_size (regcache->tdesc, 0) == 8);
-
- supply_register_by_name_zeroed (regcache, "r0");
-
- for (i = 1; i < 32; i++)
- mips_supply_register (regcache, use_64bit, i, regset + i);
-
- mips_supply_register (regcache, use_64bit,
- find_regno (regcache->tdesc, "lo"), regset + 32);
- mips_supply_register (regcache, use_64bit,
- find_regno (regcache->tdesc, "hi"), regset + 33);
- mips_supply_register (regcache, use_64bit,
- find_regno (regcache->tdesc, "pc"), regset + 34);
- mips_supply_register (regcache, use_64bit,
- find_regno (regcache->tdesc, "badvaddr"), regset + 35);
- mips_supply_register (regcache, use_64bit,
- find_regno (regcache->tdesc, "status"), regset + 36);
- mips_supply_register (regcache, use_64bit,
- find_regno (regcache->tdesc, "cause"), regset + 37);
-
- mips_supply_register (regcache, use_64bit,
- find_regno (regcache->tdesc, "restart"), regset + 0);
-}
-
-static void
-mips_fill_fpregset (struct regcache *regcache, void *buf)
-{
- union mips_register *regset = (union mips_register *) buf;
- int i, use_64bit, first_fp, big_endian;
-
- use_64bit = (register_size (regcache->tdesc, 0) == 8);
- first_fp = find_regno (regcache->tdesc, "f0");
- big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
-
- /* See GDB for a discussion of this peculiar layout. */
- for (i = 0; i < 32; i++)
- if (use_64bit)
- collect_register (regcache, first_fp + i, regset[i].buf);
- else
- collect_register (regcache, first_fp + i,
- regset[i & ~1].buf + 4 * (big_endian != (i & 1)));
-
- mips_collect_register_32bit (regcache, use_64bit,
- find_regno (regcache->tdesc, "fcsr"), regset[32].buf);
- mips_collect_register_32bit (regcache, use_64bit,
- find_regno (regcache->tdesc, "fir"),
- regset[32].buf + 4);
-}
-
-static void
-mips_store_fpregset (struct regcache *regcache, const void *buf)
-{
- const union mips_register *regset = (const union mips_register *) buf;
- int i, use_64bit, first_fp, big_endian;
-
- use_64bit = (register_size (regcache->tdesc, 0) == 8);
- first_fp = find_regno (regcache->tdesc, "f0");
- big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
-
- /* See GDB for a discussion of this peculiar layout. */
- for (i = 0; i < 32; i++)
- if (use_64bit)
- supply_register (regcache, first_fp + i, regset[i].buf);
- else
- supply_register (regcache, first_fp + i,
- regset[i & ~1].buf + 4 * (big_endian != (i & 1)));
-
- mips_supply_register_32bit (regcache, use_64bit,
- find_regno (regcache->tdesc, "fcsr"),
- regset[32].buf);
- mips_supply_register_32bit (regcache, use_64bit,
- find_regno (regcache->tdesc, "fir"),
- regset[32].buf + 4);
-}
-#endif /* HAVE_PTRACE_GETREGS */
-
-/* Take care of 32-bit registers with 64-bit ptrace, POKEUSER side. */
-
-static void
-mips_collect_ptrace_register (struct regcache *regcache,
- int regno, char *buf)
-{
- int use_64bit = sizeof (PTRACE_XFER_TYPE) == 8;
-
- if (use_64bit && register_size (regcache->tdesc, regno) == 4)
- {
- union mips_register reg;
-
- mips_collect_register (regcache, 0, regno, ®);
- memcpy (buf, ®, sizeof (reg));
- }
- else
- collect_register (regcache, regno, buf);
-}
-
-/* Take care of 32-bit registers with 64-bit ptrace, PEEKUSER side. */
-
-static void
-mips_supply_ptrace_register (struct regcache *regcache,
- int regno, const char *buf)
-{
- int use_64bit = sizeof (PTRACE_XFER_TYPE) == 8;
-
- if (use_64bit && register_size (regcache->tdesc, regno) == 4)
- {
- union mips_register reg;
-
- memcpy (®, buf, sizeof (reg));
- mips_supply_register (regcache, 0, regno, ®);
- }
- else
- supply_register (regcache, regno, buf);
-}
-
-static struct regset_info mips_regsets[] = {
-#ifdef HAVE_PTRACE_GETREGS
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, 38 * 8, GENERAL_REGS,
- mips_fill_gregset, mips_store_gregset },
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, 33 * 8, FP_REGS,
- mips_fill_fpregset, mips_store_fpregset },
-#endif /* HAVE_PTRACE_GETREGS */
- NULL_REGSET
-};
-
-static struct regsets_info mips_regsets_info =
- {
- mips_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info mips_dsp_usrregs_info =
- {
- mips_dsp_num_regs,
- mips_dsp_regmap,
- };
-
-static struct usrregs_info mips_usrregs_info =
- {
- mips_num_regs,
- mips_regmap,
- };
-
-static struct regs_info dsp_regs_info =
- {
- mips_dsp_regset_bitmap,
- &mips_dsp_usrregs_info,
- &mips_regsets_info
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &mips_usrregs_info,
- &mips_regsets_info
- };
-
-static const struct regs_info *
-mips_regs_info (void)
-{
- if (have_dsp)
- return &dsp_regs_info;
- else
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- mips_arch_setup,
- mips_regs_info,
- mips_cannot_fetch_register,
- mips_cannot_store_register,
- mips_fetch_register,
- mips_get_pc,
- mips_set_pc,
- NULL, /* breakpoint_kind_from_pc */
- mips_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
- 0,
- mips_breakpoint_at,
- mips_supports_z_point_type,
- mips_insert_point,
- mips_remove_point,
- mips_stopped_by_watchpoint,
- mips_stopped_data_address,
- mips_collect_ptrace_register,
- mips_supply_ptrace_register,
- NULL, /* siginfo_fixup */
- mips_linux_new_process,
- mips_linux_delete_process,
- mips_linux_new_thread,
- mips_linux_delete_thread,
- mips_linux_new_fork,
- mips_linux_prepare_to_resume
-};
-
-void
-initialize_low_arch (void)
-{
- /* Initialize the Linux target descriptions. */
- init_registers_mips_linux ();
- init_registers_mips_dsp_linux ();
- init_registers_mips64_linux ();
- init_registers_mips64_dsp_linux ();
-
- initialize_regsets_info (&mips_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/MIPS specific low level interface, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+#include "nat/gdb_ptrace.h"
+#include <endian.h>
+
+#include "nat/mips-linux-watch.h"
+#include "gdb_proc_service.h"
+
+/* Defined in auto-generated file mips-linux.c. */
+void init_registers_mips_linux (void);
+extern const struct target_desc *tdesc_mips_linux;
+
+/* Defined in auto-generated file mips-dsp-linux.c. */
+void init_registers_mips_dsp_linux (void);
+extern const struct target_desc *tdesc_mips_dsp_linux;
+
+/* Defined in auto-generated file mips64-linux.c. */
+void init_registers_mips64_linux (void);
+extern const struct target_desc *tdesc_mips64_linux;
+
+/* Defined in auto-generated file mips64-dsp-linux.c. */
+void init_registers_mips64_dsp_linux (void);
+extern const struct target_desc *tdesc_mips64_dsp_linux;
+
+#ifdef __mips64
+#define tdesc_mips_linux tdesc_mips64_linux
+#define tdesc_mips_dsp_linux tdesc_mips64_dsp_linux
+#endif
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+#define mips_num_regs 73
+#define mips_dsp_num_regs 80
+
+#include <asm/ptrace.h>
+
+#ifndef DSP_BASE
+#define DSP_BASE 71
+#define DSP_CONTROL 77
+#endif
+
+union mips_register
+{
+ unsigned char buf[8];
+
+ /* Deliberately signed, for proper sign extension. */
+ int reg32;
+ long long reg64;
+};
+
+/* Return the ptrace ``address'' of register REGNO. */
+
+#define mips_base_regs \
+ -1, 1, 2, 3, 4, 5, 6, 7, \
+ 8, 9, 10, 11, 12, 13, 14, 15, \
+ 16, 17, 18, 19, 20, 21, 22, 23, \
+ 24, 25, 26, 27, 28, 29, 30, 31, \
+ \
+ -1, MMLO, MMHI, BADVADDR, CAUSE, PC, \
+ \
+ FPR_BASE, FPR_BASE + 1, FPR_BASE + 2, FPR_BASE + 3, \
+ FPR_BASE + 4, FPR_BASE + 5, FPR_BASE + 6, FPR_BASE + 7, \
+ FPR_BASE + 8, FPR_BASE + 9, FPR_BASE + 10, FPR_BASE + 11, \
+ FPR_BASE + 12, FPR_BASE + 13, FPR_BASE + 14, FPR_BASE + 15, \
+ FPR_BASE + 16, FPR_BASE + 17, FPR_BASE + 18, FPR_BASE + 19, \
+ FPR_BASE + 20, FPR_BASE + 21, FPR_BASE + 22, FPR_BASE + 23, \
+ FPR_BASE + 24, FPR_BASE + 25, FPR_BASE + 26, FPR_BASE + 27, \
+ FPR_BASE + 28, FPR_BASE + 29, FPR_BASE + 30, FPR_BASE + 31, \
+ FPC_CSR, FPC_EIR
+
+#define mips_dsp_regs \
+ DSP_BASE, DSP_BASE + 1, DSP_BASE + 2, DSP_BASE + 3, \
+ DSP_BASE + 4, DSP_BASE + 5, \
+ DSP_CONTROL
+
+static int mips_regmap[mips_num_regs] = {
+ mips_base_regs,
+ 0
+};
+
+static int mips_dsp_regmap[mips_dsp_num_regs] = {
+ mips_base_regs,
+ mips_dsp_regs,
+ 0
+};
+
+/* DSP registers are not in any regset and can only be accessed
+ individually. */
+
+static unsigned char mips_dsp_regset_bitmap[(mips_dsp_num_regs + 7) / 8] = {
+ 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x80
+};
+
+static int have_dsp = -1;
+
+/* Try peeking at an arbitrarily chosen DSP register and pick the available
+ user register set accordingly. */
+
+static const struct target_desc *
+mips_read_description (void)
+{
+ if (have_dsp < 0)
+ {
+ int pid = lwpid_of (current_thread);
+
+ errno = 0;
+ ptrace (PTRACE_PEEKUSER, pid, DSP_CONTROL, 0);
+ switch (errno)
+ {
+ case 0:
+ have_dsp = 1;
+ break;
+ case EIO:
+ have_dsp = 0;
+ break;
+ default:
+ perror_with_name ("ptrace");
+ break;
+ }
+ }
+
+ return have_dsp ? tdesc_mips_dsp_linux : tdesc_mips_linux;
+}
+
+static void
+mips_arch_setup (void)
+{
+ current_process ()->tdesc = mips_read_description ();
+}
+
+static struct usrregs_info *
+get_usrregs_info (void)
+{
+ const struct regs_info *regs_info = the_low_target.regs_info ();
+
+ return regs_info->usrregs;
+}
+
+/* Per-process arch-specific data we want to keep. */
+
+struct arch_process_info
+{
+ /* -1 if the kernel and/or CPU do not support watch registers.
+ 1 if watch_readback is valid and we can read style, num_valid
+ and the masks.
+ 0 if we need to read the watch_readback. */
+
+ int watch_readback_valid;
+
+ /* Cached watch register read values. */
+
+ struct pt_watch_regs watch_readback;
+
+ /* Current watchpoint requests for this process. */
+
+ struct mips_watchpoint *current_watches;
+
+ /* The current set of watch register values for writing the
+ registers. */
+
+ struct pt_watch_regs watch_mirror;
+};
+
+/* Per-thread arch-specific data we want to keep. */
+
+struct arch_lwp_info
+{
+ /* Non-zero if our copy differs from what's recorded in the thread. */
+ int watch_registers_changed;
+};
+
+/* From mips-linux-nat.c. */
+
+/* Pseudo registers can not be read. ptrace does not provide a way to
+ read (or set) PS_REGNUM, and there's no point in reading or setting
+ ZERO_REGNUM, it's always 0. We also can not set BADVADDR, CAUSE,
+ or FCRIR via ptrace(). */
+
+static int
+mips_cannot_fetch_register (int regno)
+{
+ const struct target_desc *tdesc;
+
+ if (get_usrregs_info ()->regmap[regno] == -1)
+ return 1;
+
+ tdesc = current_process ()->tdesc;
+
+ /* On n32 we can't access 64-bit registers via PTRACE_PEEKUSR. */
+ if (register_size (tdesc, regno) > sizeof (PTRACE_XFER_TYPE))
+ return 1;
+
+ if (find_regno (tdesc, "r0") == regno)
+ return 1;
+
+ return 0;
+}
+
+static int
+mips_cannot_store_register (int regno)
+{
+ const struct target_desc *tdesc;
+
+ if (get_usrregs_info ()->regmap[regno] == -1)
+ return 1;
+
+ tdesc = current_process ()->tdesc;
+
+ /* On n32 we can't access 64-bit registers via PTRACE_POKEUSR. */
+ if (register_size (tdesc, regno) > sizeof (PTRACE_XFER_TYPE))
+ return 1;
+
+ if (find_regno (tdesc, "r0") == regno)
+ return 1;
+
+ if (find_regno (tdesc, "cause") == regno)
+ return 1;
+
+ if (find_regno (tdesc, "badvaddr") == regno)
+ return 1;
+
+ if (find_regno (tdesc, "fir") == regno)
+ return 1;
+
+ return 0;
+}
+
+static int
+mips_fetch_register (struct regcache *regcache, int regno)
+{
+ const struct target_desc *tdesc = current_process ()->tdesc;
+
+ if (find_regno (tdesc, "r0") == regno)
+ {
+ supply_register_zeroed (regcache, regno);
+ return 1;
+ }
+
+ return 0;
+}
+
+static CORE_ADDR
+mips_get_pc (struct regcache *regcache)
+{
+ union mips_register pc;
+ collect_register_by_name (regcache, "pc", pc.buf);
+ return register_size (regcache->tdesc, 0) == 4 ? pc.reg32 : pc.reg64;
+}
+
+static void
+mips_set_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ union mips_register newpc;
+ if (register_size (regcache->tdesc, 0) == 4)
+ newpc.reg32 = pc;
+ else
+ newpc.reg64 = pc;
+
+ supply_register_by_name (regcache, "pc", newpc.buf);
+}
+
+/* Correct in either endianness. */
+static const unsigned int mips_breakpoint = 0x0005000d;
+#define mips_breakpoint_len 4
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+mips_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = mips_breakpoint_len;
+ return (const gdb_byte *) &mips_breakpoint;
+}
+
+static int
+mips_breakpoint_at (CORE_ADDR where)
+{
+ unsigned int insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
+ if (insn == mips_breakpoint)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+/* Mark the watch registers of lwp, represented by ENTRY, as changed. */
+
+static void
+update_watch_registers_callback (thread_info *thread)
+{
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ /* The actual update is done later just before resuming the lwp,
+ we just mark that the registers need updating. */
+ lwp->arch_private->watch_registers_changed = 1;
+
+ /* If the lwp isn't stopped, force it to momentarily pause, so
+ we can update its watch registers. */
+ if (!lwp->stopped)
+ linux_stop_lwp (lwp);
+}
+
+/* This is the implementation of linux_target_ops method
+ new_process. */
+
+static struct arch_process_info *
+mips_linux_new_process (void)
+{
+ struct arch_process_info *info = XCNEW (struct arch_process_info);
+
+ return info;
+}
+
+/* This is the implementation of linux_target_ops method
+ delete_process. */
+
+static void
+mips_linux_delete_process (struct arch_process_info *info)
+{
+ xfree (info);
+}
+
+/* This is the implementation of linux_target_ops method new_thread.
+ Mark the watch registers as changed, so the threads' copies will
+ be updated. */
+
+static void
+mips_linux_new_thread (struct lwp_info *lwp)
+{
+ struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
+
+ info->watch_registers_changed = 1;
+
+ lwp->arch_private = info;
+}
+
+/* Function to call when a thread is being deleted. */
+
+static void
+mips_linux_delete_thread (struct arch_lwp_info *arch_lwp)
+{
+ xfree (arch_lwp);
+}
+
+/* Create a new mips_watchpoint and add it to the list. */
+
+static void
+mips_add_watchpoint (struct arch_process_info *priv, CORE_ADDR addr, int len,
+ enum target_hw_bp_type watch_type)
+{
+ struct mips_watchpoint *new_watch;
+ struct mips_watchpoint **pw;
+
+ new_watch = XNEW (struct mips_watchpoint);
+ new_watch->addr = addr;
+ new_watch->len = len;
+ new_watch->type = watch_type;
+ new_watch->next = NULL;
+
+ pw = &priv->current_watches;
+ while (*pw != NULL)
+ pw = &(*pw)->next;
+ *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached. */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ struct arch_process_info *parent_private;
+ struct arch_process_info *child_private;
+ struct mips_watchpoint *wp;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->priv != NULL
+ && parent->priv->arch_private != NULL);
+ gdb_assert (child->priv != NULL
+ && child->priv->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ parent_private = parent->priv->arch_private;
+ child_private = child->priv->arch_private;
+
+ child_private->watch_readback_valid = parent_private->watch_readback_valid;
+ child_private->watch_readback = parent_private->watch_readback;
+
+ for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+ mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+ child_private->watch_mirror = parent_private->watch_mirror;
+}
+/* This is the implementation of linux_target_ops method
+ prepare_to_resume. If the watch regs have changed, update the
+ thread's copies. */
+
+static void
+mips_linux_prepare_to_resume (struct lwp_info *lwp)
+{
+ ptid_t ptid = ptid_of (get_lwp_thread (lwp));
+ struct process_info *proc = find_process_pid (ptid.pid ());
+ struct arch_process_info *priv = proc->priv->arch_private;
+
+ if (lwp->arch_private->watch_registers_changed)
+ {
+ /* Only update the watch registers if we have set or unset a
+ watchpoint already. */
+ if (mips_linux_watch_get_num_valid (&priv->watch_mirror) > 0)
+ {
+ /* Write the mirrored watch register values. */
+ int tid = ptid.lwp ();
+
+ if (-1 == ptrace (PTRACE_SET_WATCH_REGS, tid,
+ &priv->watch_mirror, NULL))
+ perror_with_name ("Couldn't write watch register");
+ }
+
+ lwp->arch_private->watch_registers_changed = 0;
+ }
+}
+
+static int
+mips_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_READ_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* This is the implementation of linux_target_ops method
+ insert_point. */
+
+static int
+mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+ struct arch_process_info *priv = proc->priv->arch_private;
+ struct pt_watch_regs regs;
+ long lwpid;
+ enum target_hw_bp_type watch_type;
+ uint32_t irw;
+
+ lwpid = lwpid_of (current_thread);
+ if (!mips_linux_read_watch_registers (lwpid,
+ &priv->watch_readback,
+ &priv->watch_readback_valid,
+ 0))
+ return -1;
+
+ if (len <= 0)
+ return -1;
+
+ regs = priv->watch_readback;
+ /* Add the current watches. */
+ mips_linux_watch_populate_regs (priv->current_watches, ®s);
+
+ /* Now try to add the new watch. */
+ watch_type = raw_bkpt_type_to_target_hw_bp_type (type);
+ irw = mips_linux_watch_type_to_irw (watch_type);
+ if (!mips_linux_watch_try_one_watch (®s, addr, len, irw))
+ return -1;
+
+ /* It fit. Stick it on the end of the list. */
+ mips_add_watchpoint (priv, addr, len, watch_type);
+
+ priv->watch_mirror = regs;
+
+ /* Only update the threads of this process. */
+ for_each_thread (proc->pid, update_watch_registers_callback);
+
+ return 0;
+}
+
+/* This is the implementation of linux_target_ops method
+ remove_point. */
+
+static int
+mips_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+ struct arch_process_info *priv = proc->priv->arch_private;
+
+ int deleted_one;
+ enum target_hw_bp_type watch_type;
+
+ struct mips_watchpoint **pw;
+ struct mips_watchpoint *w;
+
+ /* Search for a known watch that matches. Then unlink and free it. */
+ watch_type = raw_bkpt_type_to_target_hw_bp_type (type);
+ deleted_one = 0;
+ pw = &priv->current_watches;
+ while ((w = *pw))
+ {
+ if (w->addr == addr && w->len == len && w->type == watch_type)
+ {
+ *pw = w->next;
+ free (w);
+ deleted_one = 1;
+ break;
+ }
+ pw = &(w->next);
+ }
+
+ if (!deleted_one)
+ return -1; /* We don't know about it, fail doing nothing. */
+
+ /* At this point watch_readback is known to be valid because we
+ could not have added the watch without reading it. */
+ gdb_assert (priv->watch_readback_valid == 1);
+
+ priv->watch_mirror = priv->watch_readback;
+ mips_linux_watch_populate_regs (priv->current_watches,
+ &priv->watch_mirror);
+
+ /* Only update the threads of this process. */
+ for_each_thread (proc->pid, update_watch_registers_callback);
+
+ return 0;
+}
+
+/* This is the implementation of linux_target_ops method
+ stopped_by_watchpoint. The watchhi R and W bits indicate
+ the watch register triggered. */
+
+static int
+mips_stopped_by_watchpoint (void)
+{
+ struct process_info *proc = current_process ();
+ struct arch_process_info *priv = proc->priv->arch_private;
+ int n;
+ int num_valid;
+ long lwpid = lwpid_of (current_thread);
+
+ if (!mips_linux_read_watch_registers (lwpid,
+ &priv->watch_readback,
+ &priv->watch_readback_valid,
+ 1))
+ return 0;
+
+ num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback);
+
+ for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++)
+ if (mips_linux_watch_get_watchhi (&priv->watch_readback, n)
+ & (R_MASK | W_MASK))
+ return 1;
+
+ return 0;
+}
+
+/* This is the implementation of linux_target_ops method
+ stopped_data_address. */
+
+static CORE_ADDR
+mips_stopped_data_address (void)
+{
+ struct process_info *proc = current_process ();
+ struct arch_process_info *priv = proc->priv->arch_private;
+ int n;
+ int num_valid;
+ long lwpid = lwpid_of (current_thread);
+
+ /* On MIPS we don't know the low order 3 bits of the data address.
+ GDB does not support remote targets that can't report the
+ watchpoint address. So, make our best guess; return the starting
+ address of a watchpoint request which overlaps the one that
+ triggered. */
+
+ if (!mips_linux_read_watch_registers (lwpid,
+ &priv->watch_readback,
+ &priv->watch_readback_valid,
+ 0))
+ return 0;
+
+ num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback);
+
+ for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++)
+ if (mips_linux_watch_get_watchhi (&priv->watch_readback, n)
+ & (R_MASK | W_MASK))
+ {
+ CORE_ADDR t_low, t_hi;
+ int t_irw;
+ struct mips_watchpoint *watch;
+
+ t_low = mips_linux_watch_get_watchlo (&priv->watch_readback, n);
+ t_irw = t_low & IRW_MASK;
+ t_hi = (mips_linux_watch_get_watchhi (&priv->watch_readback, n)
+ | IRW_MASK);
+ t_low &= ~(CORE_ADDR)t_hi;
+
+ for (watch = priv->current_watches;
+ watch != NULL;
+ watch = watch->next)
+ {
+ CORE_ADDR addr = watch->addr;
+ CORE_ADDR last_byte = addr + watch->len - 1;
+
+ if ((t_irw & mips_linux_watch_type_to_irw (watch->type)) == 0)
+ {
+ /* Different type. */
+ continue;
+ }
+ /* Check for overlap of even a single byte. */
+ if (last_byte >= t_low && addr <= t_low + t_hi)
+ return addr;
+ }
+ }
+
+ /* Shouldn't happen. */
+ return 0;
+}
+
+/* Fetch the thread-local storage pointer for libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
+ return PS_ERR;
+
+ /* IDX is the bias from the thread pointer to the beginning of the
+ thread descriptor. It has to be subtracted due to implementation
+ quirks in libthread_db. */
+ *base = (void *) ((char *)*base - idx);
+
+ return PS_OK;
+}
+
+static void
+mips_collect_register (struct regcache *regcache,
+ int use_64bit, int regno, union mips_register *reg)
+{
+ union mips_register tmp_reg;
+
+ if (use_64bit)
+ {
+ collect_register (regcache, regno, &tmp_reg.reg64);
+ *reg = tmp_reg;
+ }
+ else
+ {
+ collect_register (regcache, regno, &tmp_reg.reg32);
+ reg->reg64 = tmp_reg.reg32;
+ }
+}
+
+static void
+mips_supply_register (struct regcache *regcache,
+ int use_64bit, int regno, const union mips_register *reg)
+{
+ int offset = 0;
+
+ /* For big-endian 32-bit targets, ignore the high four bytes of each
+ eight-byte slot. */
+ if (__BYTE_ORDER == __BIG_ENDIAN && !use_64bit)
+ offset = 4;
+
+ supply_register (regcache, regno, reg->buf + offset);
+}
+
+#ifdef HAVE_PTRACE_GETREGS
+
+static void
+mips_collect_register_32bit (struct regcache *regcache,
+ int use_64bit, int regno, unsigned char *buf)
+{
+ union mips_register tmp_reg;
+ int reg32;
+
+ mips_collect_register (regcache, use_64bit, regno, &tmp_reg);
+ reg32 = tmp_reg.reg64;
+ memcpy (buf, ®32, 4);
+}
+
+static void
+mips_supply_register_32bit (struct regcache *regcache,
+ int use_64bit, int regno, const unsigned char *buf)
+{
+ union mips_register tmp_reg;
+ int reg32;
+
+ memcpy (®32, buf, 4);
+ tmp_reg.reg64 = reg32;
+ mips_supply_register (regcache, use_64bit, regno, &tmp_reg);
+}
+
+static void
+mips_fill_gregset (struct regcache *regcache, void *buf)
+{
+ union mips_register *regset = (union mips_register *) buf;
+ int i, use_64bit;
+ const struct target_desc *tdesc = regcache->tdesc;
+
+ use_64bit = (register_size (tdesc, 0) == 8);
+
+ for (i = 1; i < 32; i++)
+ mips_collect_register (regcache, use_64bit, i, regset + i);
+
+ mips_collect_register (regcache, use_64bit,
+ find_regno (tdesc, "lo"), regset + 32);
+ mips_collect_register (regcache, use_64bit,
+ find_regno (tdesc, "hi"), regset + 33);
+ mips_collect_register (regcache, use_64bit,
+ find_regno (tdesc, "pc"), regset + 34);
+ mips_collect_register (regcache, use_64bit,
+ find_regno (tdesc, "badvaddr"), regset + 35);
+ mips_collect_register (regcache, use_64bit,
+ find_regno (tdesc, "status"), regset + 36);
+ mips_collect_register (regcache, use_64bit,
+ find_regno (tdesc, "cause"), regset + 37);
+
+ mips_collect_register (regcache, use_64bit,
+ find_regno (tdesc, "restart"), regset + 0);
+}
+
+static void
+mips_store_gregset (struct regcache *regcache, const void *buf)
+{
+ const union mips_register *regset = (const union mips_register *) buf;
+ int i, use_64bit;
+
+ use_64bit = (register_size (regcache->tdesc, 0) == 8);
+
+ supply_register_by_name_zeroed (regcache, "r0");
+
+ for (i = 1; i < 32; i++)
+ mips_supply_register (regcache, use_64bit, i, regset + i);
+
+ mips_supply_register (regcache, use_64bit,
+ find_regno (regcache->tdesc, "lo"), regset + 32);
+ mips_supply_register (regcache, use_64bit,
+ find_regno (regcache->tdesc, "hi"), regset + 33);
+ mips_supply_register (regcache, use_64bit,
+ find_regno (regcache->tdesc, "pc"), regset + 34);
+ mips_supply_register (regcache, use_64bit,
+ find_regno (regcache->tdesc, "badvaddr"), regset + 35);
+ mips_supply_register (regcache, use_64bit,
+ find_regno (regcache->tdesc, "status"), regset + 36);
+ mips_supply_register (regcache, use_64bit,
+ find_regno (regcache->tdesc, "cause"), regset + 37);
+
+ mips_supply_register (regcache, use_64bit,
+ find_regno (regcache->tdesc, "restart"), regset + 0);
+}
+
+static void
+mips_fill_fpregset (struct regcache *regcache, void *buf)
+{
+ union mips_register *regset = (union mips_register *) buf;
+ int i, use_64bit, first_fp, big_endian;
+
+ use_64bit = (register_size (regcache->tdesc, 0) == 8);
+ first_fp = find_regno (regcache->tdesc, "f0");
+ big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
+
+ /* See GDB for a discussion of this peculiar layout. */
+ for (i = 0; i < 32; i++)
+ if (use_64bit)
+ collect_register (regcache, first_fp + i, regset[i].buf);
+ else
+ collect_register (regcache, first_fp + i,
+ regset[i & ~1].buf + 4 * (big_endian != (i & 1)));
+
+ mips_collect_register_32bit (regcache, use_64bit,
+ find_regno (regcache->tdesc, "fcsr"), regset[32].buf);
+ mips_collect_register_32bit (regcache, use_64bit,
+ find_regno (regcache->tdesc, "fir"),
+ regset[32].buf + 4);
+}
+
+static void
+mips_store_fpregset (struct regcache *regcache, const void *buf)
+{
+ const union mips_register *regset = (const union mips_register *) buf;
+ int i, use_64bit, first_fp, big_endian;
+
+ use_64bit = (register_size (regcache->tdesc, 0) == 8);
+ first_fp = find_regno (regcache->tdesc, "f0");
+ big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
+
+ /* See GDB for a discussion of this peculiar layout. */
+ for (i = 0; i < 32; i++)
+ if (use_64bit)
+ supply_register (regcache, first_fp + i, regset[i].buf);
+ else
+ supply_register (regcache, first_fp + i,
+ regset[i & ~1].buf + 4 * (big_endian != (i & 1)));
+
+ mips_supply_register_32bit (regcache, use_64bit,
+ find_regno (regcache->tdesc, "fcsr"),
+ regset[32].buf);
+ mips_supply_register_32bit (regcache, use_64bit,
+ find_regno (regcache->tdesc, "fir"),
+ regset[32].buf + 4);
+}
+#endif /* HAVE_PTRACE_GETREGS */
+
+/* Take care of 32-bit registers with 64-bit ptrace, POKEUSER side. */
+
+static void
+mips_collect_ptrace_register (struct regcache *regcache,
+ int regno, char *buf)
+{
+ int use_64bit = sizeof (PTRACE_XFER_TYPE) == 8;
+
+ if (use_64bit && register_size (regcache->tdesc, regno) == 4)
+ {
+ union mips_register reg;
+
+ mips_collect_register (regcache, 0, regno, ®);
+ memcpy (buf, ®, sizeof (reg));
+ }
+ else
+ collect_register (regcache, regno, buf);
+}
+
+/* Take care of 32-bit registers with 64-bit ptrace, PEEKUSER side. */
+
+static void
+mips_supply_ptrace_register (struct regcache *regcache,
+ int regno, const char *buf)
+{
+ int use_64bit = sizeof (PTRACE_XFER_TYPE) == 8;
+
+ if (use_64bit && register_size (regcache->tdesc, regno) == 4)
+ {
+ union mips_register reg;
+
+ memcpy (®, buf, sizeof (reg));
+ mips_supply_register (regcache, 0, regno, ®);
+ }
+ else
+ supply_register (regcache, regno, buf);
+}
+
+static struct regset_info mips_regsets[] = {
+#ifdef HAVE_PTRACE_GETREGS
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, 38 * 8, GENERAL_REGS,
+ mips_fill_gregset, mips_store_gregset },
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, 33 * 8, FP_REGS,
+ mips_fill_fpregset, mips_store_fpregset },
+#endif /* HAVE_PTRACE_GETREGS */
+ NULL_REGSET
+};
+
+static struct regsets_info mips_regsets_info =
+ {
+ mips_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info mips_dsp_usrregs_info =
+ {
+ mips_dsp_num_regs,
+ mips_dsp_regmap,
+ };
+
+static struct usrregs_info mips_usrregs_info =
+ {
+ mips_num_regs,
+ mips_regmap,
+ };
+
+static struct regs_info dsp_regs_info =
+ {
+ mips_dsp_regset_bitmap,
+ &mips_dsp_usrregs_info,
+ &mips_regsets_info
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &mips_usrregs_info,
+ &mips_regsets_info
+ };
+
+static const struct regs_info *
+mips_regs_info (void)
+{
+ if (have_dsp)
+ return &dsp_regs_info;
+ else
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ mips_arch_setup,
+ mips_regs_info,
+ mips_cannot_fetch_register,
+ mips_cannot_store_register,
+ mips_fetch_register,
+ mips_get_pc,
+ mips_set_pc,
+ NULL, /* breakpoint_kind_from_pc */
+ mips_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
+ 0,
+ mips_breakpoint_at,
+ mips_supports_z_point_type,
+ mips_insert_point,
+ mips_remove_point,
+ mips_stopped_by_watchpoint,
+ mips_stopped_data_address,
+ mips_collect_ptrace_register,
+ mips_supply_ptrace_register,
+ NULL, /* siginfo_fixup */
+ mips_linux_new_process,
+ mips_linux_delete_process,
+ mips_linux_new_thread,
+ mips_linux_delete_thread,
+ mips_linux_new_fork,
+ mips_linux_prepare_to_resume
+};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+ init_registers_mips_linux ();
+ init_registers_mips_dsp_linux ();
+ init_registers_mips64_linux ();
+ init_registers_mips64_dsp_linux ();
+
+ initialize_regsets_info (&mips_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/Nios II specific low level interface, for the remote server for
- GDB.
- Copyright (C) 2008-2020 Free Software Foundation, Inc.
-
- Contributed by Mentor Graphics, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "elf/common.h"
-#include "nat/gdb_ptrace.h"
-#include <endian.h>
-#include "gdb_proc_service.h"
-#include <asm/ptrace.h>
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-/* The following definition must agree with the number of registers
- defined in "struct user_regs" in GLIBC
- (sysdeps/unix/sysv/linux/nios2/sys/user.h), and also with
- NIOS2_NUM_REGS in GDB proper. */
-
-#define nios2_num_regs 49
-
-/* Defined in auto-generated file nios2-linux.c. */
-
-void init_registers_nios2_linux (void);
-extern const struct target_desc *tdesc_nios2_linux;
-
-/* This union is used to convert between int and byte buffer
- representations of register contents. */
-
-union nios2_register
-{
- unsigned char buf[4];
- int reg32;
-};
-
-/* Return the ptrace ``address'' of register REGNO. */
-
-static int nios2_regmap[] = {
- -1, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48,
- 0
-};
-
-/* Implement the arch_setup linux_target_ops method. */
-
-static void
-nios2_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_nios2_linux;
-}
-
-/* Implement the cannot_fetch_register linux_target_ops method. */
-
-static int
-nios2_cannot_fetch_register (int regno)
-{
- if (nios2_regmap[regno] == -1)
- return 1;
-
- return 0;
-}
-
-/* Implement the cannot_store_register linux_target_ops method. */
-
-static int
-nios2_cannot_store_register (int regno)
-{
- if (nios2_regmap[regno] == -1)
- return 1;
-
- return 0;
-}
-
-/* Breakpoint support. Also see comments on nios2_breakpoint_from_pc
- in nios2-tdep.c. */
-
-#if defined(__nios2_arch__) && __nios2_arch__ == 2
-#define NIOS2_BREAKPOINT 0xb7fd0020
-#define CDX_BREAKPOINT 0xd7c9
-#else
-#define NIOS2_BREAKPOINT 0x003b6ffa
-#endif
-
-/* We only register the 4-byte breakpoint, even on R2 targets which also
- support 2-byte breakpoints. Since there is no supports_z_point_type
- function provided, gdbserver never inserts software breakpoints itself
- and instead relies on GDB to insert the breakpoint of the correct length
- via a memory write. */
-static const unsigned int nios2_breakpoint = NIOS2_BREAKPOINT;
-#define nios2_breakpoint_len 4
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-nios2_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = nios2_breakpoint_len;
- return (const gdb_byte *) &nios2_breakpoint;
-}
-
-/* Implement the breakpoint_at linux_target_ops method. */
-
-static int
-nios2_breakpoint_at (CORE_ADDR where)
-{
- unsigned int insn;
-
- /* For R2, first check for the 2-byte CDX trap.n breakpoint encoding. */
-#if defined(__nios2_arch__) && __nios2_arch__ == 2
- (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
- if (insn == CDX_BREAKPOINT)
- return 1;
-#endif
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == nios2_breakpoint)
- return 1;
- return 0;
-}
-
-/* Fetch the thread-local storage pointer for libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
- return PS_ERR;
-
- /* IDX is the bias from the thread pointer to the beginning of the
- thread descriptor. It has to be subtracted due to implementation
- quirks in libthread_db. */
- *base = (void *) ((char *) *base - idx);
-
- return PS_OK;
-}
-
-/* Helper functions to collect/supply a single register REGNO. */
-
-static void
-nios2_collect_register (struct regcache *regcache, int regno,
- union nios2_register *reg)
-{
- union nios2_register tmp_reg;
-
- collect_register (regcache, regno, &tmp_reg.reg32);
- reg->reg32 = tmp_reg.reg32;
-}
-
-static void
-nios2_supply_register (struct regcache *regcache, int regno,
- const union nios2_register *reg)
-{
- supply_register (regcache, regno, reg->buf);
-}
-
-/* We have only a single register set on Nios II. */
-
-static void
-nios2_fill_gregset (struct regcache *regcache, void *buf)
-{
- union nios2_register *regset = (union nios2_register *) buf;
- int i;
-
- for (i = 1; i < nios2_num_regs; i++)
- nios2_collect_register (regcache, i, regset + i);
-}
-
-static void
-nios2_store_gregset (struct regcache *regcache, const void *buf)
-{
- const union nios2_register *regset = (union nios2_register *) buf;
- int i;
-
- for (i = 0; i < nios2_num_regs; i++)
- nios2_supply_register (regcache, i, regset + i);
-}
-
-static struct regset_info nios2_regsets[] =
-{
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
- nios2_num_regs * 4, GENERAL_REGS,
- nios2_fill_gregset, nios2_store_gregset },
- NULL_REGSET
-};
-
-static struct regsets_info nios2_regsets_info =
- {
- nios2_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info nios2_usrregs_info =
- {
- nios2_num_regs,
- nios2_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &nios2_usrregs_info,
- &nios2_regsets_info
- };
-
-static const struct regs_info *
-nios2_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target =
-{
- nios2_arch_setup,
- nios2_regs_info,
- nios2_cannot_fetch_register,
- nios2_cannot_store_register,
- NULL,
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_kind_from_pc */
- nios2_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
- 0,
- nios2_breakpoint_at,
-};
-
-void
-initialize_low_arch (void)
-{
- init_registers_nios2_linux ();
-
- initialize_regsets_info (&nios2_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/Nios II specific low level interface, for the remote server for
+ GDB.
+ Copyright (C) 2008-2020 Free Software Foundation, Inc.
+
+ Contributed by Mentor Graphics, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "elf/common.h"
+#include "nat/gdb_ptrace.h"
+#include <endian.h>
+#include "gdb_proc_service.h"
+#include <asm/ptrace.h>
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+/* The following definition must agree with the number of registers
+ defined in "struct user_regs" in GLIBC
+ (sysdeps/unix/sysv/linux/nios2/sys/user.h), and also with
+ NIOS2_NUM_REGS in GDB proper. */
+
+#define nios2_num_regs 49
+
+/* Defined in auto-generated file nios2-linux.c. */
+
+void init_registers_nios2_linux (void);
+extern const struct target_desc *tdesc_nios2_linux;
+
+/* This union is used to convert between int and byte buffer
+ representations of register contents. */
+
+union nios2_register
+{
+ unsigned char buf[4];
+ int reg32;
+};
+
+/* Return the ptrace ``address'' of register REGNO. */
+
+static int nios2_regmap[] = {
+ -1, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48,
+ 0
+};
+
+/* Implement the arch_setup linux_target_ops method. */
+
+static void
+nios2_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_nios2_linux;
+}
+
+/* Implement the cannot_fetch_register linux_target_ops method. */
+
+static int
+nios2_cannot_fetch_register (int regno)
+{
+ if (nios2_regmap[regno] == -1)
+ return 1;
+
+ return 0;
+}
+
+/* Implement the cannot_store_register linux_target_ops method. */
+
+static int
+nios2_cannot_store_register (int regno)
+{
+ if (nios2_regmap[regno] == -1)
+ return 1;
+
+ return 0;
+}
+
+/* Breakpoint support. Also see comments on nios2_breakpoint_from_pc
+ in nios2-tdep.c. */
+
+#if defined(__nios2_arch__) && __nios2_arch__ == 2
+#define NIOS2_BREAKPOINT 0xb7fd0020
+#define CDX_BREAKPOINT 0xd7c9
+#else
+#define NIOS2_BREAKPOINT 0x003b6ffa
+#endif
+
+/* We only register the 4-byte breakpoint, even on R2 targets which also
+ support 2-byte breakpoints. Since there is no supports_z_point_type
+ function provided, gdbserver never inserts software breakpoints itself
+ and instead relies on GDB to insert the breakpoint of the correct length
+ via a memory write. */
+static const unsigned int nios2_breakpoint = NIOS2_BREAKPOINT;
+#define nios2_breakpoint_len 4
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+nios2_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = nios2_breakpoint_len;
+ return (const gdb_byte *) &nios2_breakpoint;
+}
+
+/* Implement the breakpoint_at linux_target_ops method. */
+
+static int
+nios2_breakpoint_at (CORE_ADDR where)
+{
+ unsigned int insn;
+
+ /* For R2, first check for the 2-byte CDX trap.n breakpoint encoding. */
+#if defined(__nios2_arch__) && __nios2_arch__ == 2
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
+ if (insn == CDX_BREAKPOINT)
+ return 1;
+#endif
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
+ if (insn == nios2_breakpoint)
+ return 1;
+ return 0;
+}
+
+/* Fetch the thread-local storage pointer for libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
+ return PS_ERR;
+
+ /* IDX is the bias from the thread pointer to the beginning of the
+ thread descriptor. It has to be subtracted due to implementation
+ quirks in libthread_db. */
+ *base = (void *) ((char *) *base - idx);
+
+ return PS_OK;
+}
+
+/* Helper functions to collect/supply a single register REGNO. */
+
+static void
+nios2_collect_register (struct regcache *regcache, int regno,
+ union nios2_register *reg)
+{
+ union nios2_register tmp_reg;
+
+ collect_register (regcache, regno, &tmp_reg.reg32);
+ reg->reg32 = tmp_reg.reg32;
+}
+
+static void
+nios2_supply_register (struct regcache *regcache, int regno,
+ const union nios2_register *reg)
+{
+ supply_register (regcache, regno, reg->buf);
+}
+
+/* We have only a single register set on Nios II. */
+
+static void
+nios2_fill_gregset (struct regcache *regcache, void *buf)
+{
+ union nios2_register *regset = (union nios2_register *) buf;
+ int i;
+
+ for (i = 1; i < nios2_num_regs; i++)
+ nios2_collect_register (regcache, i, regset + i);
+}
+
+static void
+nios2_store_gregset (struct regcache *regcache, const void *buf)
+{
+ const union nios2_register *regset = (union nios2_register *) buf;
+ int i;
+
+ for (i = 0; i < nios2_num_regs; i++)
+ nios2_supply_register (regcache, i, regset + i);
+}
+
+static struct regset_info nios2_regsets[] =
+{
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
+ nios2_num_regs * 4, GENERAL_REGS,
+ nios2_fill_gregset, nios2_store_gregset },
+ NULL_REGSET
+};
+
+static struct regsets_info nios2_regsets_info =
+ {
+ nios2_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info nios2_usrregs_info =
+ {
+ nios2_num_regs,
+ nios2_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &nios2_usrregs_info,
+ &nios2_regsets_info
+ };
+
+static const struct regs_info *
+nios2_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target =
+{
+ nios2_arch_setup,
+ nios2_regs_info,
+ nios2_cannot_fetch_register,
+ nios2_cannot_store_register,
+ NULL,
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_kind_from_pc */
+ nios2_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
+ 0,
+ nios2_breakpoint_at,
+};
+
+void
+initialize_low_arch (void)
+{
+ init_registers_nios2_linux ();
+
+ initialize_regsets_info (&nios2_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/PowerPC specific low level interface, for the in-process
- agent library for GDB.
-
- Copyright (C) 2016-2020 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 "arch/ppc-linux-tdesc.h"
-#include "linux-ppc-tdesc-init.h"
-#include <elf.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_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;
- case PPC_TDESC_ISA205_PPR_DSCR_VSX:
- return tdesc_powerpc_isa205_ppr_dscr_vsx64l;
- case PPC_TDESC_ISA207_VSX:
- return tdesc_powerpc_isa207_vsx64l;
- case PPC_TDESC_ISA207_HTM_VSX:
- return tdesc_powerpc_isa207_htm_vsx64l;
-#else
- case PPC_TDESC_BASE:
- return tdesc_powerpc_32l;
- case PPC_TDESC_ALTIVEC:
- return tdesc_powerpc_altivec32l;
- 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_ISA205_PPR_DSCR_VSX:
- return tdesc_powerpc_isa205_ppr_dscr_vsx32l;
- case PPC_TDESC_ISA207_VSX:
- return tdesc_powerpc_isa207_vsx32l;
- case PPC_TDESC_ISA207_HTM_VSX:
- return tdesc_powerpc_isa207_htm_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_vsx64l ();
- init_registers_powerpc_isa205_64l ();
- init_registers_powerpc_isa205_altivec64l ();
- init_registers_powerpc_isa205_vsx64l ();
- init_registers_powerpc_isa205_ppr_dscr_vsx64l ();
- init_registers_powerpc_isa207_vsx64l ();
- init_registers_powerpc_isa207_htm_vsx64l ();
-#else
- init_registers_powerpc_32l ();
- init_registers_powerpc_altivec32l ();
- init_registers_powerpc_vsx32l ();
- init_registers_powerpc_isa205_32l ();
- init_registers_powerpc_isa205_altivec32l ();
- init_registers_powerpc_isa205_vsx32l ();
- init_registers_powerpc_isa205_ppr_dscr_vsx32l ();
- init_registers_powerpc_isa207_vsx32l ();
- init_registers_powerpc_isa207_htm_vsx32l ();
- init_registers_powerpc_e500l ();
-#endif
-}
--- /dev/null
+/* GNU/Linux/PowerPC specific low level interface, for the in-process
+ agent library for GDB.
+
+ Copyright (C) 2016-2020 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 "arch/ppc-linux-tdesc.h"
+#include "linux-ppc-tdesc-init.h"
+#include <elf.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_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;
+ case PPC_TDESC_ISA205_PPR_DSCR_VSX:
+ return tdesc_powerpc_isa205_ppr_dscr_vsx64l;
+ case PPC_TDESC_ISA207_VSX:
+ return tdesc_powerpc_isa207_vsx64l;
+ case PPC_TDESC_ISA207_HTM_VSX:
+ return tdesc_powerpc_isa207_htm_vsx64l;
+#else
+ case PPC_TDESC_BASE:
+ return tdesc_powerpc_32l;
+ case PPC_TDESC_ALTIVEC:
+ return tdesc_powerpc_altivec32l;
+ 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_ISA205_PPR_DSCR_VSX:
+ return tdesc_powerpc_isa205_ppr_dscr_vsx32l;
+ case PPC_TDESC_ISA207_VSX:
+ return tdesc_powerpc_isa207_vsx32l;
+ case PPC_TDESC_ISA207_HTM_VSX:
+ return tdesc_powerpc_isa207_htm_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_vsx64l ();
+ init_registers_powerpc_isa205_64l ();
+ init_registers_powerpc_isa205_altivec64l ();
+ init_registers_powerpc_isa205_vsx64l ();
+ init_registers_powerpc_isa205_ppr_dscr_vsx64l ();
+ init_registers_powerpc_isa207_vsx64l ();
+ init_registers_powerpc_isa207_htm_vsx64l ();
+#else
+ init_registers_powerpc_32l ();
+ init_registers_powerpc_altivec32l ();
+ init_registers_powerpc_vsx32l ();
+ init_registers_powerpc_isa205_32l ();
+ init_registers_powerpc_isa205_altivec32l ();
+ init_registers_powerpc_isa205_vsx32l ();
+ init_registers_powerpc_isa205_ppr_dscr_vsx32l ();
+ init_registers_powerpc_isa207_vsx32l ();
+ init_registers_powerpc_isa207_htm_vsx32l ();
+ init_registers_powerpc_e500l ();
+#endif
+}
+++ /dev/null
-/* GNU/Linux/PowerPC specific low level interface, for the remote server for
- GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-#include "elf/common.h"
-#include <sys/uio.h>
-#include <elf.h>
-#include <asm/ptrace.h>
-
-#include "arch/ppc-linux-common.h"
-#include "arch/ppc-linux-tdesc.h"
-#include "nat/ppc-linux.h"
-#include "nat/linux-ptrace.h"
-#include "linux-ppc-tdesc-init.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)
-
-/* Holds the AT_HWCAP auxv entry. */
-
-static unsigned long ppc_hwcap;
-
-/* Holds the AT_HWCAP2 auxv entry. */
-
-static unsigned long ppc_hwcap2;
-
-
-#define ppc_num_regs 73
-
-#ifdef __powerpc64__
-/* We use a constant for FPSCR instead of PT_FPSCR, because
- many shipped PPC64 kernels had the wrong value in ptrace.h. */
-static int ppc_regmap[] =
- {PT_R0 * 8, PT_R1 * 8, PT_R2 * 8, PT_R3 * 8,
- PT_R4 * 8, PT_R5 * 8, PT_R6 * 8, PT_R7 * 8,
- PT_R8 * 8, PT_R9 * 8, PT_R10 * 8, PT_R11 * 8,
- PT_R12 * 8, PT_R13 * 8, PT_R14 * 8, PT_R15 * 8,
- PT_R16 * 8, PT_R17 * 8, PT_R18 * 8, PT_R19 * 8,
- PT_R20 * 8, PT_R21 * 8, PT_R22 * 8, PT_R23 * 8,
- PT_R24 * 8, PT_R25 * 8, PT_R26 * 8, PT_R27 * 8,
- PT_R28 * 8, PT_R29 * 8, PT_R30 * 8, PT_R31 * 8,
- PT_FPR0*8, PT_FPR0*8 + 8, PT_FPR0*8+16, PT_FPR0*8+24,
- PT_FPR0*8+32, PT_FPR0*8+40, PT_FPR0*8+48, PT_FPR0*8+56,
- PT_FPR0*8+64, PT_FPR0*8+72, PT_FPR0*8+80, PT_FPR0*8+88,
- PT_FPR0*8+96, PT_FPR0*8+104, PT_FPR0*8+112, PT_FPR0*8+120,
- PT_FPR0*8+128, PT_FPR0*8+136, PT_FPR0*8+144, PT_FPR0*8+152,
- PT_FPR0*8+160, PT_FPR0*8+168, PT_FPR0*8+176, PT_FPR0*8+184,
- PT_FPR0*8+192, PT_FPR0*8+200, PT_FPR0*8+208, PT_FPR0*8+216,
- PT_FPR0*8+224, PT_FPR0*8+232, PT_FPR0*8+240, PT_FPR0*8+248,
- PT_NIP * 8, PT_MSR * 8, PT_CCR * 8, PT_LNK * 8,
- PT_CTR * 8, PT_XER * 8, PT_FPR0*8 + 256,
- PT_ORIG_R3 * 8, PT_TRAP * 8 };
-#else
-/* Currently, don't check/send MQ. */
-static int ppc_regmap[] =
- {PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4,
- PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4,
- PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4,
- PT_R12 * 4, PT_R13 * 4, PT_R14 * 4, PT_R15 * 4,
- PT_R16 * 4, PT_R17 * 4, PT_R18 * 4, PT_R19 * 4,
- PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
- PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
- PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
- PT_FPR0*4, PT_FPR0*4 + 8, PT_FPR0*4+16, PT_FPR0*4+24,
- PT_FPR0*4+32, PT_FPR0*4+40, PT_FPR0*4+48, PT_FPR0*4+56,
- PT_FPR0*4+64, PT_FPR0*4+72, PT_FPR0*4+80, PT_FPR0*4+88,
- PT_FPR0*4+96, PT_FPR0*4+104, PT_FPR0*4+112, PT_FPR0*4+120,
- PT_FPR0*4+128, PT_FPR0*4+136, PT_FPR0*4+144, PT_FPR0*4+152,
- PT_FPR0*4+160, PT_FPR0*4+168, PT_FPR0*4+176, PT_FPR0*4+184,
- PT_FPR0*4+192, PT_FPR0*4+200, PT_FPR0*4+208, PT_FPR0*4+216,
- PT_FPR0*4+224, PT_FPR0*4+232, PT_FPR0*4+240, PT_FPR0*4+248,
- PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
- PT_CTR * 4, PT_XER * 4, PT_FPSCR * 4,
- PT_ORIG_R3 * 4, PT_TRAP * 4
- };
-
-static int ppc_regmap_e500[] =
- {PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4,
- PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4,
- PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4,
- PT_R12 * 4, PT_R13 * 4, PT_R14 * 4, PT_R15 * 4,
- PT_R16 * 4, PT_R17 * 4, PT_R18 * 4, PT_R19 * 4,
- PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
- PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
- PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
- -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,
- PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
- PT_CTR * 4, PT_XER * 4, -1,
- PT_ORIG_R3 * 4, PT_TRAP * 4
- };
-#endif
-
-/* Check whether the kernel provides a register set with number
- REGSET_ID of size REGSETSIZE for process/thread TID. */
-
-static int
-ppc_check_regset (int tid, int regset_id, int regsetsize)
-{
- void *buf = alloca (regsetsize);
- struct iovec iov;
-
- iov.iov_base = buf;
- iov.iov_len = regsetsize;
-
- if (ptrace (PTRACE_GETREGSET, tid, regset_id, &iov) >= 0
- || errno == ENODATA)
- return 1;
- return 0;
-}
-
-static int
-ppc_cannot_store_register (int regno)
-{
- const struct target_desc *tdesc = current_process ()->tdesc;
-
-#ifndef __powerpc64__
- /* Some kernels do not allow us to store fpscr. */
- if (!(ppc_hwcap & PPC_FEATURE_HAS_SPE)
- && regno == find_regno (tdesc, "fpscr"))
- return 2;
-#endif
-
- /* Some kernels do not allow us to store orig_r3 or trap. */
- if (regno == find_regno (tdesc, "orig_r3")
- || regno == find_regno (tdesc, "trap"))
- return 2;
-
- return 0;
-}
-
-static int
-ppc_cannot_fetch_register (int regno)
-{
- return 0;
-}
-
-static void
-ppc_collect_ptrace_register (struct regcache *regcache, int regno, char *buf)
-{
- memset (buf, 0, sizeof (long));
-
- if (__BYTE_ORDER == __LITTLE_ENDIAN)
- {
- /* Little-endian values always sit at the left end of the buffer. */
- collect_register (regcache, regno, buf);
- }
- else if (__BYTE_ORDER == __BIG_ENDIAN)
- {
- /* Big-endian values sit at the right end of the buffer. In case of
- registers whose sizes are smaller than sizeof (long), we must use a
- padding to access them correctly. */
- int size = register_size (regcache->tdesc, regno);
-
- if (size < sizeof (long))
- collect_register (regcache, regno, buf + sizeof (long) - size);
- else
- collect_register (regcache, regno, buf);
- }
- else
- perror_with_name ("Unexpected byte order");
-}
-
-static void
-ppc_supply_ptrace_register (struct regcache *regcache,
- int regno, const char *buf)
-{
- if (__BYTE_ORDER == __LITTLE_ENDIAN)
- {
- /* Little-endian values always sit at the left end of the buffer. */
- supply_register (regcache, regno, buf);
- }
- else if (__BYTE_ORDER == __BIG_ENDIAN)
- {
- /* Big-endian values sit at the right end of the buffer. In case of
- registers whose sizes are smaller than sizeof (long), we must use a
- padding to access them correctly. */
- int size = register_size (regcache->tdesc, regno);
-
- if (size < sizeof (long))
- supply_register (regcache, regno, buf + sizeof (long) - size);
- else
- supply_register (regcache, regno, buf);
- }
- else
- perror_with_name ("Unexpected byte order");
-}
-
-static CORE_ADDR
-ppc_get_pc (struct regcache *regcache)
-{
- if (register_size (regcache->tdesc, 0) == 4)
- {
- unsigned int pc;
- collect_register_by_name (regcache, "pc", &pc);
- return (CORE_ADDR) pc;
- }
- else
- {
- unsigned long pc;
- collect_register_by_name (regcache, "pc", &pc);
- return (CORE_ADDR) pc;
- }
-}
-
-static void
-ppc_set_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- if (register_size (regcache->tdesc, 0) == 4)
- {
- unsigned int newpc = pc;
- supply_register_by_name (regcache, "pc", &newpc);
- }
- else
- {
- unsigned long newpc = pc;
- supply_register_by_name (regcache, "pc", &newpc);
- }
-}
-
-#ifndef __powerpc64__
-static int ppc_regmap_adjusted;
-#endif
-
-
-/* Correct in either endianness.
- This instruction is "twge r2, r2", which GDB uses as a software
- breakpoint. */
-static const unsigned int ppc_breakpoint = 0x7d821008;
-#define ppc_breakpoint_len 4
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-ppc_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = ppc_breakpoint_len;
- return (const gdb_byte *) &ppc_breakpoint;
-}
-
-static int
-ppc_breakpoint_at (CORE_ADDR where)
-{
- unsigned int insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == ppc_breakpoint)
- return 1;
- /* If necessary, recognize more trap instructions here. GDB only uses
- the one. */
-
- return 0;
-}
-
-/* Implement supports_z_point_type target-ops.
- Returns true if type Z_TYPE breakpoint is supported.
-
- Handling software breakpoint at server side, so tracepoints
- and breakpoints can be inserted at the same location. */
-
-static int
-ppc_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_SW_BP:
- return 1;
- case Z_PACKET_HW_BP:
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_ACCESS_WP:
- default:
- return 0;
- }
-}
-
-/* Implement insert_point target-ops.
- Returns 0 on success, -1 on failure and 1 on unsupported. */
-
-static int
-ppc_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- switch (type)
- {
- case raw_bkpt_type_sw:
- return insert_memory_breakpoint (bp);
-
- case raw_bkpt_type_hw:
- case raw_bkpt_type_write_wp:
- case raw_bkpt_type_access_wp:
- default:
- /* Unsupported. */
- return 1;
- }
-}
-
-/* Implement remove_point target-ops.
- Returns 0 on success, -1 on failure and 1 on unsupported. */
-
-static int
-ppc_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- switch (type)
- {
- case raw_bkpt_type_sw:
- return remove_memory_breakpoint (bp);
-
- case raw_bkpt_type_hw:
- case raw_bkpt_type_write_wp:
- case raw_bkpt_type_access_wp:
- default:
- /* Unsupported. */
- return 1;
- }
-}
-
-/* Provide only a fill function for the general register set. ps_lgetregs
- will use this for NPTL support. */
-
-static void ppc_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
-
- for (i = 0; i < 32; i++)
- ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
-
- for (i = 64; i < 70; i++)
- ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
-
- for (i = 71; i < 73; i++)
- ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
-}
-
-/* Program Priority Register regset fill function. */
-
-static void
-ppc_fill_pprregset (struct regcache *regcache, void *buf)
-{
- char *ppr = (char *) buf;
-
- collect_register_by_name (regcache, "ppr", ppr);
-}
-
-/* Program Priority Register regset store function. */
-
-static void
-ppc_store_pprregset (struct regcache *regcache, const void *buf)
-{
- const char *ppr = (const char *) buf;
-
- supply_register_by_name (regcache, "ppr", ppr);
-}
-
-/* Data Stream Control Register regset fill function. */
-
-static void
-ppc_fill_dscrregset (struct regcache *regcache, void *buf)
-{
- char *dscr = (char *) buf;
-
- collect_register_by_name (regcache, "dscr", dscr);
-}
-
-/* Data Stream Control Register regset store function. */
-
-static void
-ppc_store_dscrregset (struct regcache *regcache, const void *buf)
-{
- const char *dscr = (const char *) buf;
-
- supply_register_by_name (regcache, "dscr", dscr);
-}
-
-/* Target Address Register regset fill function. */
-
-static void
-ppc_fill_tarregset (struct regcache *regcache, void *buf)
-{
- char *tar = (char *) buf;
-
- collect_register_by_name (regcache, "tar", tar);
-}
-
-/* Target Address Register regset store function. */
-
-static void
-ppc_store_tarregset (struct regcache *regcache, const void *buf)
-{
- const char *tar = (const char *) buf;
-
- supply_register_by_name (regcache, "tar", tar);
-}
-
-/* Event-Based Branching regset store function. Unless the inferior
- has a perf event open, ptrace can return in error when reading and
- writing to the regset, with ENODATA. For reading, the registers
- will correctly show as unavailable. For writing, gdbserver
- currently only caches any register writes from P and G packets and
- the stub always tries to write all the regsets when resuming the
- inferior, which would result in frequent warnings. For this
- reason, we don't define a fill function. This also means that the
- client-side regcache will be dirty if the user tries to write to
- the EBB registers. G packets that the client sends to write to
- unrelated registers will also include data for EBB registers, even
- if they are unavailable. */
-
-static void
-ppc_store_ebbregset (struct regcache *regcache, const void *buf)
-{
- const char *regset = (const char *) buf;
-
- /* The order in the kernel regset is: EBBRR, EBBHR, BESCR. In the
- .dat file is BESCR, EBBHR, EBBRR. */
- supply_register_by_name (regcache, "ebbrr", ®set[0]);
- supply_register_by_name (regcache, "ebbhr", ®set[8]);
- supply_register_by_name (regcache, "bescr", ®set[16]);
-}
-
-/* Performance Monitoring Unit regset fill function. */
-
-static void
-ppc_fill_pmuregset (struct regcache *regcache, void *buf)
-{
- char *regset = (char *) buf;
-
- /* The order in the kernel regset is SIAR, SDAR, SIER, MMCR2, MMCR0.
- In the .dat file is MMCR0, MMCR2, SIAR, SDAR, SIER. */
- collect_register_by_name (regcache, "siar", ®set[0]);
- collect_register_by_name (regcache, "sdar", ®set[8]);
- collect_register_by_name (regcache, "sier", ®set[16]);
- collect_register_by_name (regcache, "mmcr2", ®set[24]);
- collect_register_by_name (regcache, "mmcr0", ®set[32]);
-}
-
-/* Performance Monitoring Unit regset store function. */
-
-static void
-ppc_store_pmuregset (struct regcache *regcache, const void *buf)
-{
- const char *regset = (const char *) buf;
-
- supply_register_by_name (regcache, "siar", ®set[0]);
- supply_register_by_name (regcache, "sdar", ®set[8]);
- supply_register_by_name (regcache, "sier", ®set[16]);
- supply_register_by_name (regcache, "mmcr2", ®set[24]);
- supply_register_by_name (regcache, "mmcr0", ®set[32]);
-}
-
-/* Hardware Transactional Memory special-purpose register regset fill
- function. */
-
-static void
-ppc_fill_tm_sprregset (struct regcache *regcache, void *buf)
-{
- int i, base;
- char *regset = (char *) buf;
-
- base = find_regno (regcache->tdesc, "tfhar");
- for (i = 0; i < 3; i++)
- collect_register (regcache, base + i, ®set[i * 8]);
-}
-
-/* Hardware Transactional Memory special-purpose register regset store
- function. */
-
-static void
-ppc_store_tm_sprregset (struct regcache *regcache, const void *buf)
-{
- int i, base;
- const char *regset = (const char *) buf;
-
- base = find_regno (regcache->tdesc, "tfhar");
- for (i = 0; i < 3; i++)
- supply_register (regcache, base + i, ®set[i * 8]);
-}
-
-/* For the same reasons as the EBB regset, none of the HTM
- checkpointed regsets have a fill function. These registers are
- only available if the inferior is in a transaction. */
-
-/* Hardware Transactional Memory checkpointed general-purpose regset
- store function. */
-
-static void
-ppc_store_tm_cgprregset (struct regcache *regcache, const void *buf)
-{
- int i, base, size, endian_offset;
- const char *regset = (const char *) buf;
-
- base = find_regno (regcache->tdesc, "cr0");
- size = register_size (regcache->tdesc, base);
-
- gdb_assert (size == 4 || size == 8);
-
- for (i = 0; i < 32; i++)
- supply_register (regcache, base + i, ®set[i * size]);
-
- endian_offset = 0;
-
- if ((size == 8) && (__BYTE_ORDER == __BIG_ENDIAN))
- endian_offset = 4;
-
- supply_register_by_name (regcache, "ccr",
- ®set[PT_CCR * size + endian_offset]);
-
- supply_register_by_name (regcache, "cxer",
- ®set[PT_XER * size + endian_offset]);
-
- supply_register_by_name (regcache, "clr", ®set[PT_LNK * size]);
- supply_register_by_name (regcache, "cctr", ®set[PT_CTR * size]);
-}
-
-/* Hardware Transactional Memory checkpointed floating-point regset
- store function. */
-
-static void
-ppc_store_tm_cfprregset (struct regcache *regcache, const void *buf)
-{
- int i, base;
- const char *regset = (const char *) buf;
-
- base = find_regno (regcache->tdesc, "cf0");
-
- for (i = 0; i < 32; i++)
- supply_register (regcache, base + i, ®set[i * 8]);
-
- supply_register_by_name (regcache, "cfpscr", ®set[32 * 8]);
-}
-
-/* Hardware Transactional Memory checkpointed vector regset store
- function. */
-
-static void
-ppc_store_tm_cvrregset (struct regcache *regcache, const void *buf)
-{
- int i, base;
- const char *regset = (const char *) buf;
- int vscr_offset = 0;
-
- base = find_regno (regcache->tdesc, "cvr0");
-
- for (i = 0; i < 32; i++)
- supply_register (regcache, base + i, ®set[i * 16]);
-
- if (__BYTE_ORDER == __BIG_ENDIAN)
- vscr_offset = 12;
-
- supply_register_by_name (regcache, "cvscr",
- ®set[32 * 16 + vscr_offset]);
-
- supply_register_by_name (regcache, "cvrsave", ®set[33 * 16]);
-}
-
-/* Hardware Transactional Memory checkpointed vector-scalar regset
- store function. */
-
-static void
-ppc_store_tm_cvsxregset (struct regcache *regcache, const void *buf)
-{
- int i, base;
- const char *regset = (const char *) buf;
-
- base = find_regno (regcache->tdesc, "cvs0h");
- for (i = 0; i < 32; i++)
- supply_register (regcache, base + i, ®set[i * 8]);
-}
-
-/* Hardware Transactional Memory checkpointed Program Priority
- Register regset store function. */
-
-static void
-ppc_store_tm_cpprregset (struct regcache *regcache, const void *buf)
-{
- const char *cppr = (const char *) buf;
-
- supply_register_by_name (regcache, "cppr", cppr);
-}
-
-/* Hardware Transactional Memory checkpointed Data Stream Control
- Register regset store function. */
-
-static void
-ppc_store_tm_cdscrregset (struct regcache *regcache, const void *buf)
-{
- const char *cdscr = (const char *) buf;
-
- supply_register_by_name (regcache, "cdscr", cdscr);
-}
-
-/* Hardware Transactional Memory checkpointed Target Address Register
- regset store function. */
-
-static void
-ppc_store_tm_ctarregset (struct regcache *regcache, const void *buf)
-{
- const char *ctar = (const char *) buf;
-
- supply_register_by_name (regcache, "ctar", ctar);
-}
-
-static void
-ppc_fill_vsxregset (struct regcache *regcache, void *buf)
-{
- int i, base;
- char *regset = (char *) buf;
-
- base = find_regno (regcache->tdesc, "vs0h");
- for (i = 0; i < 32; i++)
- collect_register (regcache, base + i, ®set[i * 8]);
-}
-
-static void
-ppc_store_vsxregset (struct regcache *regcache, const void *buf)
-{
- int i, base;
- const char *regset = (const char *) buf;
-
- base = find_regno (regcache->tdesc, "vs0h");
- for (i = 0; i < 32; i++)
- supply_register (regcache, base + i, ®set[i * 8]);
-}
-
-static void
-ppc_fill_vrregset (struct regcache *regcache, void *buf)
-{
- int i, base;
- char *regset = (char *) buf;
- int vscr_offset = 0;
-
- base = find_regno (regcache->tdesc, "vr0");
- for (i = 0; i < 32; i++)
- collect_register (regcache, base + i, ®set[i * 16]);
-
- if (__BYTE_ORDER == __BIG_ENDIAN)
- vscr_offset = 12;
-
- collect_register_by_name (regcache, "vscr",
- ®set[32 * 16 + vscr_offset]);
-
- collect_register_by_name (regcache, "vrsave", ®set[33 * 16]);
-}
-
-static void
-ppc_store_vrregset (struct regcache *regcache, const void *buf)
-{
- int i, base;
- const char *regset = (const char *) buf;
- int vscr_offset = 0;
-
- base = find_regno (regcache->tdesc, "vr0");
- for (i = 0; i < 32; i++)
- supply_register (regcache, base + i, ®set[i * 16]);
-
- if (__BYTE_ORDER == __BIG_ENDIAN)
- vscr_offset = 12;
-
- supply_register_by_name (regcache, "vscr",
- ®set[32 * 16 + vscr_offset]);
- supply_register_by_name (regcache, "vrsave", ®set[33 * 16]);
-}
-
-struct gdb_evrregset_t
-{
- unsigned long evr[32];
- unsigned long long acc;
- unsigned long spefscr;
-};
-
-static void
-ppc_fill_evrregset (struct regcache *regcache, void *buf)
-{
- int i, ev0;
- struct gdb_evrregset_t *regset = (struct gdb_evrregset_t *) buf;
-
- ev0 = find_regno (regcache->tdesc, "ev0h");
- for (i = 0; i < 32; i++)
- collect_register (regcache, ev0 + i, ®set->evr[i]);
-
- collect_register_by_name (regcache, "acc", ®set->acc);
- collect_register_by_name (regcache, "spefscr", ®set->spefscr);
-}
-
-static void
-ppc_store_evrregset (struct regcache *regcache, const void *buf)
-{
- int i, ev0;
- const struct gdb_evrregset_t *regset = (const struct gdb_evrregset_t *) buf;
-
- ev0 = find_regno (regcache->tdesc, "ev0h");
- for (i = 0; i < 32; i++)
- supply_register (regcache, ev0 + i, ®set->evr[i]);
-
- supply_register_by_name (regcache, "acc", ®set->acc);
- supply_register_by_name (regcache, "spefscr", ®set->spefscr);
-}
-
-/* Support for hardware single step. */
-
-static int
-ppc_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static struct regset_info ppc_regsets[] = {
- /* List the extra register sets before GENERAL_REGS. That way we will
- fetch them every time, but still fall back to PTRACE_PEEKUSER for the
- general registers. Some kernels support these, but not the newer
- PPC_PTRACE_GETREGS. */
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CTAR, 0, EXTENDED_REGS,
- NULL, ppc_store_tm_ctarregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CDSCR, 0, EXTENDED_REGS,
- NULL, ppc_store_tm_cdscrregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CPPR, 0, EXTENDED_REGS,
- NULL, ppc_store_tm_cpprregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CVSX, 0, EXTENDED_REGS,
- NULL, ppc_store_tm_cvsxregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CVMX, 0, EXTENDED_REGS,
- NULL, ppc_store_tm_cvrregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CFPR, 0, EXTENDED_REGS,
- NULL, ppc_store_tm_cfprregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CGPR, 0, EXTENDED_REGS,
- NULL, ppc_store_tm_cgprregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_SPR, 0, EXTENDED_REGS,
- ppc_fill_tm_sprregset, ppc_store_tm_sprregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_EBB, 0, EXTENDED_REGS,
- NULL, ppc_store_ebbregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PMU, 0, EXTENDED_REGS,
- ppc_fill_pmuregset, ppc_store_pmuregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TAR, 0, EXTENDED_REGS,
- ppc_fill_tarregset, ppc_store_tarregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PPR, 0, EXTENDED_REGS,
- ppc_fill_pprregset, ppc_store_pprregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_DSCR, 0, EXTENDED_REGS,
- ppc_fill_dscrregset, ppc_store_dscrregset },
- { PTRACE_GETVSXREGS, PTRACE_SETVSXREGS, 0, 0, EXTENDED_REGS,
- ppc_fill_vsxregset, ppc_store_vsxregset },
- { PTRACE_GETVRREGS, PTRACE_SETVRREGS, 0, 0, EXTENDED_REGS,
- ppc_fill_vrregset, ppc_store_vrregset },
- { PTRACE_GETEVRREGS, PTRACE_SETEVRREGS, 0, 0, EXTENDED_REGS,
- ppc_fill_evrregset, ppc_store_evrregset },
- { 0, 0, 0, 0, GENERAL_REGS, ppc_fill_gregset, NULL },
- NULL_REGSET
-};
-
-static struct usrregs_info ppc_usrregs_info =
- {
- ppc_num_regs,
- ppc_regmap,
- };
-
-static struct regsets_info ppc_regsets_info =
- {
- ppc_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &ppc_usrregs_info,
- &ppc_regsets_info
- };
-
-static const struct regs_info *
-ppc_regs_info (void)
-{
- return ®s_info;
-}
-
-static void
-ppc_arch_setup (void)
-{
- const struct target_desc *tdesc;
- struct regset_info *regset;
- struct ppc_linux_features features = ppc_linux_no_features;
-
- int tid = lwpid_of (current_thread);
-
- features.wordsize = ppc_linux_target_wordsize (tid);
-
- if (features.wordsize == 4)
- tdesc = tdesc_powerpc_32l;
- else
- tdesc = tdesc_powerpc_64l;
-
- current_process ()->tdesc = tdesc;
-
- /* The value of current_process ()->tdesc needs to be set for this
- call. */
- ppc_hwcap = linux_get_hwcap (features.wordsize);
- ppc_hwcap2 = linux_get_hwcap2 (features.wordsize);
-
- features.isa205 = ppc_linux_has_isa205 (ppc_hwcap);
-
- if (ppc_hwcap & PPC_FEATURE_HAS_VSX)
- features.vsx = true;
-
- if (ppc_hwcap & PPC_FEATURE_HAS_ALTIVEC)
- features.altivec = true;
-
- if ((ppc_hwcap2 & PPC_FEATURE2_DSCR)
- && ppc_check_regset (tid, NT_PPC_DSCR, PPC_LINUX_SIZEOF_DSCRREGSET)
- && ppc_check_regset (tid, NT_PPC_PPR, PPC_LINUX_SIZEOF_PPRREGSET))
- {
- features.ppr_dscr = true;
- if ((ppc_hwcap2 & PPC_FEATURE2_ARCH_2_07)
- && (ppc_hwcap2 & PPC_FEATURE2_TAR)
- && (ppc_hwcap2 & PPC_FEATURE2_EBB)
- && ppc_check_regset (tid, NT_PPC_TAR,
- PPC_LINUX_SIZEOF_TARREGSET)
- && ppc_check_regset (tid, NT_PPC_EBB,
- PPC_LINUX_SIZEOF_EBBREGSET)
- && ppc_check_regset (tid, NT_PPC_PMU,
- PPC_LINUX_SIZEOF_PMUREGSET))
- {
- features.isa207 = true;
- if ((ppc_hwcap2 & PPC_FEATURE2_HTM)
- && ppc_check_regset (tid, NT_PPC_TM_SPR,
- PPC_LINUX_SIZEOF_TM_SPRREGSET))
- features.htm = true;
- }
- }
-
- tdesc = ppc_linux_match_description (features);
-
- /* On 32-bit machines, check for SPE registers.
- Set the low target's regmap field as appropriately. */
-#ifndef __powerpc64__
- if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
- tdesc = tdesc_powerpc_e500l;
-
- if (!ppc_regmap_adjusted)
- {
- if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
- ppc_usrregs_info.regmap = ppc_regmap_e500;
-
- /* If the FPSCR is 64-bit wide, we need to fetch the whole
- 64-bit slot and not just its second word. The PT_FPSCR
- supplied in a 32-bit GDB compilation doesn't reflect
- this. */
- if (register_size (tdesc, 70) == 8)
- ppc_regmap[70] = (48 + 2*32) * sizeof (long);
-
- ppc_regmap_adjusted = 1;
- }
-#endif
-
- current_process ()->tdesc = tdesc;
-
- for (regset = ppc_regsets; regset->size >= 0; regset++)
- switch (regset->get_request)
- {
- case PTRACE_GETVRREGS:
- regset->size = features.altivec ? PPC_LINUX_SIZEOF_VRREGSET : 0;
- break;
- case PTRACE_GETVSXREGS:
- regset->size = features.vsx ? PPC_LINUX_SIZEOF_VSXREGSET : 0;
- break;
- case PTRACE_GETEVRREGS:
- if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
- regset->size = 32 * 4 + 8 + 4;
- else
- regset->size = 0;
- break;
- case PTRACE_GETREGSET:
- switch (regset->nt_type)
- {
- case NT_PPC_PPR:
- regset->size = (features.ppr_dscr ?
- PPC_LINUX_SIZEOF_PPRREGSET : 0);
- break;
- case NT_PPC_DSCR:
- regset->size = (features.ppr_dscr ?
- PPC_LINUX_SIZEOF_DSCRREGSET : 0);
- break;
- case NT_PPC_TAR:
- regset->size = (features.isa207 ?
- PPC_LINUX_SIZEOF_TARREGSET : 0);
- break;
- case NT_PPC_EBB:
- regset->size = (features.isa207 ?
- PPC_LINUX_SIZEOF_EBBREGSET : 0);
- break;
- case NT_PPC_PMU:
- regset->size = (features.isa207 ?
- PPC_LINUX_SIZEOF_PMUREGSET : 0);
- break;
- case NT_PPC_TM_SPR:
- regset->size = (features.htm ?
- PPC_LINUX_SIZEOF_TM_SPRREGSET : 0);
- break;
- case NT_PPC_TM_CGPR:
- if (features.wordsize == 4)
- regset->size = (features.htm ?
- PPC32_LINUX_SIZEOF_CGPRREGSET : 0);
- else
- regset->size = (features.htm ?
- PPC64_LINUX_SIZEOF_CGPRREGSET : 0);
- break;
- case NT_PPC_TM_CFPR:
- regset->size = (features.htm ?
- PPC_LINUX_SIZEOF_CFPRREGSET : 0);
- break;
- case NT_PPC_TM_CVMX:
- regset->size = (features.htm ?
- PPC_LINUX_SIZEOF_CVMXREGSET : 0);
- break;
- case NT_PPC_TM_CVSX:
- regset->size = (features.htm ?
- PPC_LINUX_SIZEOF_CVSXREGSET : 0);
- break;
- case NT_PPC_TM_CPPR:
- regset->size = (features.htm ?
- PPC_LINUX_SIZEOF_CPPRREGSET : 0);
- break;
- case NT_PPC_TM_CDSCR:
- regset->size = (features.htm ?
- PPC_LINUX_SIZEOF_CDSCRREGSET : 0);
- break;
- case NT_PPC_TM_CTAR:
- regset->size = (features.htm ?
- PPC_LINUX_SIZEOF_CTARREGSET : 0);
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-}
-
-/* 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 (ptid_t (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
- CORE_ADDR phdr;
- Elf64_Ehdr ehdr;
-
- const struct target_desc *tdesc = current_process ()->tdesc;
- int wordsize = register_size (tdesc, 0);
-
- if (!linux_get_auxv (wordsize, 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;
- target_write_memory (*to, (unsigned char *) &insn, 4);
- *to += 4;
-
- /* Build a unconditional branch and copy LK bit. */
- insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
- target_write_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);
-
- target_write_memory (*to, (unsigned char *) &bdnz_insn, 4);
- *to += 4;
- target_write_memory (*to, (unsigned char *) &bf_insn, 4);
- *to += 4;
-
- /* Build a unconditional branch and copy LK bit. */
- insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
- target_write_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);
- target_write_memory (*to, (unsigned char *) &insn, 4);
- *to += 4;
-
- return;
- }
- }
-
- target_write_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 original 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. */
- target_write_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);
- target_write_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;
-}
-
-/* Emits a given buffer into the target at current_insn_ptr. Length
- is in units of 32-bit words. */
-
-static void
-emit_insns (uint32_t *buf, int n)
-{
- n = n * sizeof (uint32_t);
- target_write_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 - 32-bit
-
- | LR save area (SP + 4)
- 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 and 16-byte alignment.
- | 16 bytes.
- SP +- Back-chain (SP')
-
- initial frame size
- = 16 + (4 * 4) + 64
- = 96
-
- 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 the high part of TOP value.
- It was the first argument, pointer to regs.
- r4 is used for cache of the low part of TOP value.
- It was the second argument, pointer to the result.
- We should set *result = TOP after leaving this function.
-
- Note:
- * To restore stack at epilogue
- => sp = r31
- * To check stack is big enough for bytecode execution.
- => r30 - 8 > SP + 8
- * To return execution result.
- => 0(r4) = TOP
-
- */
-
-/* Regardless of endian, register 3 is always high part, 4 is low part.
- These defines are used when the register pair is stored/loaded.
- Likewise, to simplify code, have a similiar define for 5:6. */
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define TOP_FIRST "4"
-#define TOP_SECOND "3"
-#define TMP_FIRST "6"
-#define TMP_SECOND "5"
-#else
-#define TOP_FIRST "3"
-#define TOP_SECOND "4"
-#define TMP_FIRST "5"
-#define TMP_SECOND "6"
-#endif
-
-/* Emit prologue in inferior memory. See above comments. */
-
-static void
-ppc_emit_prologue (void)
-{
- EMIT_ASM (/* Save return address. */
- "mflr 0 \n"
- "stw 0, 4(1) \n"
- /* Adjust SP. 96 is the initial frame size. */
- "stwu 1, -96(1) \n"
- /* Save r30 and incoming arguments. */
- "stw 31, 96-4(1) \n"
- "stw 30, 96-8(1) \n"
- "stw 4, 96-12(1) \n"
- "stw 3, 96-16(1) \n"
- /* Point r31 to original r1 for access arguments. */
- "addi 31, 1, 96 \n"
- /* Set r30 to pointing stack-top. */
- "addi 30, 1, 64 \n"
- /* Initial r3/TOP to 0. */
- "li 3, 0 \n"
- "li 4, 0 \n");
-}
-
-/* Emit epilogue in inferior memory. See above comments. */
-
-static void
-ppc_emit_epilogue (void)
-{
- EMIT_ASM (/* *result = TOP */
- "lwz 5, -12(31) \n"
- "stw " TOP_FIRST ", 0(5) \n"
- "stw " TOP_SECOND ", 4(5) \n"
- /* Restore registers. */
- "lwz 31, -4(31) \n"
- "lwz 30, -8(31) \n"
- /* Restore SP. */
- "lwz 1, 0(1) \n"
- /* Restore LR. */
- "lwz 0, 4(1) \n"
- /* Return 0 for no-error. */
- "li 3, 0 \n"
- "mtlr 0 \n"
- "blr \n");
-}
-
-/* TOP = stack[--sp] + TOP */
-
-static void
-ppc_emit_add (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30)\n"
- "addc 4, 6, 4 \n"
- "adde 3, 5, 3 \n");
-}
-
-/* TOP = stack[--sp] - TOP */
-
-static void
-ppc_emit_sub (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "subfc 4, 4, 6 \n"
- "subfe 3, 3, 5 \n");
-}
-
-/* TOP = stack[--sp] * TOP */
-
-static void
-ppc_emit_mul (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "mulhwu 7, 6, 4 \n"
- "mullw 3, 6, 3 \n"
- "mullw 5, 4, 5 \n"
- "mullw 4, 6, 4 \n"
- "add 3, 5, 3 \n"
- "add 3, 7, 3 \n");
-}
-
-/* TOP = stack[--sp] << TOP */
-
-static void
-ppc_emit_lsh (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "subfic 3, 4, 32\n" /* r3 = 32 - TOP */
- "addi 7, 4, -32\n" /* r7 = TOP - 32 */
- "slw 5, 5, 4\n" /* Shift high part left */
- "slw 4, 6, 4\n" /* Shift low part left */
- "srw 3, 6, 3\n" /* Shift low to high if shift < 32 */
- "slw 7, 6, 7\n" /* Shift low to high if shift >= 32 */
- "or 3, 5, 3\n"
- "or 3, 7, 3\n"); /* Assemble high part */
-}
-
-/* Top = stack[--sp] >> TOP
- (Arithmetic shift right) */
-
-static void
-ppc_emit_rsh_signed (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "addi 7, 4, -32\n" /* r7 = TOP - 32 */
- "sraw 3, 5, 4\n" /* Shift high part right */
- "cmpwi 7, 1\n"
- "blt 0, 1f\n" /* If shift <= 32, goto 1: */
- "sraw 4, 5, 7\n" /* Shift high to low */
- "b 2f\n"
- "1:\n"
- "subfic 7, 4, 32\n" /* r7 = 32 - TOP */
- "srw 4, 6, 4\n" /* Shift low part right */
- "slw 5, 5, 7\n" /* Shift high to low */
- "or 4, 4, 5\n" /* Assemble low part */
- "2:\n");
-}
-
-/* Top = stack[--sp] >> TOP
- (Logical shift right) */
-
-static void
-ppc_emit_rsh_unsigned (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "subfic 3, 4, 32\n" /* r3 = 32 - TOP */
- "addi 7, 4, -32\n" /* r7 = TOP - 32 */
- "srw 6, 6, 4\n" /* Shift low part right */
- "slw 3, 5, 3\n" /* Shift high to low if shift < 32 */
- "srw 7, 5, 7\n" /* Shift high to low if shift >= 32 */
- "or 6, 6, 3\n"
- "srw 3, 5, 4\n" /* Shift high part right */
- "or 4, 6, 7\n"); /* Assemble low part */
-}
-
-/* Emit code for signed-extension specified by ARG. */
-
-static void
-ppc_emit_ext (int arg)
-{
- switch (arg)
- {
- case 8:
- EMIT_ASM ("extsb 4, 4\n"
- "srawi 3, 4, 31");
- break;
- case 16:
- EMIT_ASM ("extsh 4, 4\n"
- "srawi 3, 4, 31");
- break;
- case 32:
- EMIT_ASM ("srawi 3, 4, 31");
- break;
- default:
- emit_error = 1;
- }
-}
-
-/* Emit code for zero-extension specified by ARG. */
-
-static void
-ppc_emit_zero_ext (int arg)
-{
- switch (arg)
- {
- case 8:
- EMIT_ASM ("clrlwi 4,4,24\n"
- "li 3, 0\n");
- break;
- case 16:
- EMIT_ASM ("clrlwi 4,4,16\n"
- "li 3, 0\n");
- break;
- case 32:
- EMIT_ASM ("li 3, 0");
- break;
- default:
- emit_error = 1;
- }
-}
-
-/* TOP = !TOP
- i.e., TOP = (TOP == 0) ? 1 : 0; */
-
-static void
-ppc_emit_log_not (void)
-{
- EMIT_ASM ("or 4, 3, 4 \n"
- "cntlzw 4, 4 \n"
- "srwi 4, 4, 5 \n"
- "li 3, 0 \n");
-}
-
-/* TOP = stack[--sp] & TOP */
-
-static void
-ppc_emit_bit_and (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "and 4, 6, 4 \n"
- "and 3, 5, 3 \n");
-}
-
-/* TOP = stack[--sp] | TOP */
-
-static void
-ppc_emit_bit_or (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "or 4, 6, 4 \n"
- "or 3, 5, 3 \n");
-}
-
-/* TOP = stack[--sp] ^ TOP */
-
-static void
-ppc_emit_bit_xor (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "xor 4, 6, 4 \n"
- "xor 3, 5, 3 \n");
-}
-
-/* TOP = ~TOP
- i.e., TOP = ~(TOP | TOP) */
-
-static void
-ppc_emit_bit_not (void)
-{
- EMIT_ASM ("nor 3, 3, 3 \n"
- "nor 4, 4, 4 \n");
-}
-
-/* TOP = stack[--sp] == TOP */
-
-static void
-ppc_emit_equal (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "xor 4, 6, 4 \n"
- "xor 3, 5, 3 \n"
- "or 4, 3, 4 \n"
- "cntlzw 4, 4 \n"
- "srwi 4, 4, 5 \n"
- "li 3, 0 \n");
-}
-
-/* TOP = stack[--sp] < TOP
- (Signed comparison) */
-
-static void
-ppc_emit_less_signed (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "cmplw 6, 6, 4 \n"
- "cmpw 7, 5, 3 \n"
- /* CR6 bit 0 = low less and high equal */
- "crand 6*4+0, 6*4+0, 7*4+2\n"
- /* CR7 bit 0 = (low less and high equal) or high less */
- "cror 7*4+0, 7*4+0, 6*4+0\n"
- "mfcr 4 \n"
- "rlwinm 4, 4, 29, 31, 31 \n"
- "li 3, 0 \n");
-}
-
-/* TOP = stack[--sp] < TOP
- (Unsigned comparison) */
-
-static void
-ppc_emit_less_unsigned (void)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "cmplw 6, 6, 4 \n"
- "cmplw 7, 5, 3 \n"
- /* CR6 bit 0 = low less and high equal */
- "crand 6*4+0, 6*4+0, 7*4+2\n"
- /* CR7 bit 0 = (low less and high equal) or high less */
- "cror 7*4+0, 7*4+0, 6*4+0\n"
- "mfcr 4 \n"
- "rlwinm 4, 4, 29, 31, 31 \n"
- "li 3, 0 \n");
-}
-
-/* Access the memory address in TOP in size of SIZE.
- Zero-extend the read value. */
-
-static void
-ppc_emit_ref (int size)
-{
- switch (size)
- {
- case 1:
- EMIT_ASM ("lbz 4, 0(4)\n"
- "li 3, 0");
- break;
- case 2:
- EMIT_ASM ("lhz 4, 0(4)\n"
- "li 3, 0");
- break;
- case 4:
- EMIT_ASM ("lwz 4, 0(4)\n"
- "li 3, 0");
- break;
- case 8:
- if (__BYTE_ORDER == __LITTLE_ENDIAN)
- EMIT_ASM ("lwz 3, 4(4)\n"
- "lwz 4, 0(4)");
- else
- EMIT_ASM ("lwz 3, 0(4)\n"
- "lwz 4, 4(4)");
- break;
- }
-}
-
-/* TOP = NUM */
-
-static void
-ppc_emit_const (LONGEST num)
-{
- uint32_t buf[10];
- uint32_t *p = buf;
-
- p += gen_limm (p, 3, num >> 32 & 0xffffffff, 0);
- p += gen_limm (p, 4, num & 0xffffffff, 0);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (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
-ppc_emit_reg (int reg)
-{
- uint32_t buf[13];
- uint32_t *p = buf;
-
- /* fctx->regs is passed in r3 and then saved in -16(31). */
- p += GEN_LWZ (p, 3, 31, -16);
- p += GEN_LI (p, 4, reg); /* li r4, reg */
- p += gen_call (p, get_raw_reg_func_addr (), 0, 0);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-
- if (__BYTE_ORDER == __LITTLE_ENDIAN)
- {
- EMIT_ASM ("mr 5, 4\n"
- "mr 4, 3\n"
- "mr 3, 5\n");
- }
-}
-
-/* TOP = stack[--sp] */
-
-static void
-ppc_emit_pop (void)
-{
- EMIT_ASM ("lwzu " TOP_FIRST ", 8(30) \n"
- "lwz " TOP_SECOND ", 4(30) \n");
-}
-
-/* stack[sp++] = TOP
-
- Because we may use up bytecode stack, expand 8 doublewords more
- if needed. */
-
-static void
-ppc_emit_stack_flush (void)
-{
- /* Make sure bytecode stack is big enough before push.
- Otherwise, expand 64-byte more. */
-
- EMIT_ASM (" stw " TOP_FIRST ", 0(30) \n"
- " stw " TOP_SECOND ", 4(30)\n"
- " addi 5, 30, -(8 + 8) \n"
- " cmpw 7, 5, 1 \n"
- " bgt 7, 1f \n"
- " stwu 31, -64(1) \n"
- "1:addi 30, 30, -8 \n");
-}
-
-/* Swap TOP and stack[sp-1] */
-
-static void
-ppc_emit_swap (void)
-{
- EMIT_ASM ("lwz " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 12(30) \n"
- "stw " TOP_FIRST ", 8(30) \n"
- "stw " TOP_SECOND ", 12(30) \n"
- "mr 3, 5 \n"
- "mr 4, 6 \n");
-}
-
-/* Discard N elements in the stack. Also used for ppc64. */
-
-static void
-ppc_emit_stack_adjust (int n)
-{
- uint32_t buf[6];
- uint32_t *p = buf;
-
- n = n << 3;
- if ((n >> 15) != 0)
- {
- emit_error = 1;
- return;
- }
-
- p += GEN_ADDI (p, 30, 30, n);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* Call function FN. */
-
-static void
-ppc_emit_call (CORE_ADDR fn)
-{
- uint32_t buf[11];
- uint32_t *p = buf;
-
- p += gen_call (p, fn, 0, 0);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* FN's prototype is `LONGEST(*fn)(int)'.
- TOP = fn (arg1)
- */
-
-static void
-ppc_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[15];
- uint32_t *p = buf;
-
- /* Setup argument. arg1 is a 16-bit value. */
- p += gen_limm (p, 3, (uint32_t) arg1, 0);
- p += gen_call (p, fn, 0, 0);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-
- if (__BYTE_ORDER == __LITTLE_ENDIAN)
- {
- EMIT_ASM ("mr 5, 4\n"
- "mr 4, 3\n"
- "mr 3, 5\n");
- }
-}
-
-/* FN's prototype is `void(*fn)(int,LONGEST)'.
- fn (arg1, TOP)
-
- TOP should be preserved/restored before/after the call. */
-
-static void
-ppc_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[21];
- uint32_t *p = buf;
-
- /* Save TOP. 0(30) is next-empty. */
- p += GEN_STW (p, 3, 30, 0);
- p += GEN_STW (p, 4, 30, 4);
-
- /* Setup argument. arg1 is a 16-bit value. */
- if (__BYTE_ORDER == __LITTLE_ENDIAN)
- {
- p += GEN_MR (p, 5, 4);
- p += GEN_MR (p, 6, 3);
- }
- else
- {
- p += GEN_MR (p, 5, 3);
- p += GEN_MR (p, 6, 4);
- }
- p += gen_limm (p, 3, (uint32_t) arg1, 0);
- p += gen_call (p, fn, 0, 0);
-
- /* Restore TOP */
- p += GEN_LWZ (p, 3, 30, 0);
- p += GEN_LWZ (p, 4, 30, 4);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (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
-ppc_emit_if_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM ("or. 3, 3, 4 \n"
- "lwzu " TOP_FIRST ", 8(30) \n"
- "lwz " TOP_SECOND ", 4(30) \n"
- "1:bne 0, 1b \n");
-
- if (offset_p)
- *offset_p = 12;
- if (size_p)
- *size_p = 14;
-}
-
-/* Unconditional goto. Also used for ppc64. */
-
-static void
-ppc_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
-ppc_emit_eq_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "xor 4, 6, 4 \n"
- "xor 3, 5, 3 \n"
- "or. 3, 3, 4 \n"
- "lwzu " TOP_FIRST ", 8(30) \n"
- "lwz " TOP_SECOND ", 4(30) \n"
- "1:beq 0, 1b \n");
-
- if (offset_p)
- *offset_p = 28;
- if (size_p)
- *size_p = 14;
-}
-
-/* Goto if stack[--sp] != TOP */
-
-static void
-ppc_emit_ne_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "xor 4, 6, 4 \n"
- "xor 3, 5, 3 \n"
- "or. 3, 3, 4 \n"
- "lwzu " TOP_FIRST ", 8(30) \n"
- "lwz " TOP_SECOND ", 4(30) \n"
- "1:bne 0, 1b \n");
-
- if (offset_p)
- *offset_p = 28;
- if (size_p)
- *size_p = 14;
-}
-
-/* Goto if stack[--sp] < TOP */
-
-static void
-ppc_emit_lt_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "cmplw 6, 6, 4 \n"
- "cmpw 7, 5, 3 \n"
- /* CR6 bit 0 = low less and high equal */
- "crand 6*4+0, 6*4+0, 7*4+2\n"
- /* CR7 bit 0 = (low less and high equal) or high less */
- "cror 7*4+0, 7*4+0, 6*4+0\n"
- "lwzu " TOP_FIRST ", 8(30) \n"
- "lwz " TOP_SECOND ", 4(30)\n"
- "1:blt 7, 1b \n");
-
- if (offset_p)
- *offset_p = 32;
- if (size_p)
- *size_p = 14;
-}
-
-/* Goto if stack[--sp] <= TOP */
-
-static void
-ppc_emit_le_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "cmplw 6, 6, 4 \n"
- "cmpw 7, 5, 3 \n"
- /* CR6 bit 0 = low less/equal and high equal */
- "crandc 6*4+0, 7*4+2, 6*4+1\n"
- /* CR7 bit 0 = (low less/eq and high equal) or high less */
- "cror 7*4+0, 7*4+0, 6*4+0\n"
- "lwzu " TOP_FIRST ", 8(30) \n"
- "lwz " TOP_SECOND ", 4(30)\n"
- "1:blt 7, 1b \n");
-
- if (offset_p)
- *offset_p = 32;
- if (size_p)
- *size_p = 14;
-}
-
-/* Goto if stack[--sp] > TOP */
-
-static void
-ppc_emit_gt_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "cmplw 6, 6, 4 \n"
- "cmpw 7, 5, 3 \n"
- /* CR6 bit 0 = low greater and high equal */
- "crand 6*4+0, 6*4+1, 7*4+2\n"
- /* CR7 bit 0 = (low greater and high equal) or high greater */
- "cror 7*4+0, 7*4+1, 6*4+0\n"
- "lwzu " TOP_FIRST ", 8(30) \n"
- "lwz " TOP_SECOND ", 4(30)\n"
- "1:blt 7, 1b \n");
-
- if (offset_p)
- *offset_p = 32;
- if (size_p)
- *size_p = 14;
-}
-
-/* Goto if stack[--sp] >= TOP */
-
-static void
-ppc_emit_ge_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
- "lwz " TMP_SECOND ", 4(30) \n"
- "cmplw 6, 6, 4 \n"
- "cmpw 7, 5, 3 \n"
- /* CR6 bit 0 = low ge and high equal */
- "crandc 6*4+0, 7*4+2, 6*4+0\n"
- /* CR7 bit 0 = (low ge and high equal) or high greater */
- "cror 7*4+0, 7*4+1, 6*4+0\n"
- "lwzu " TOP_FIRST ", 8(30)\n"
- "lwz " TOP_SECOND ", 4(30)\n"
- "1:blt 7, 1b \n");
-
- if (offset_p)
- *offset_p = 32;
- 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.
- Also used for ppc64. */
-
-static void
-ppc_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
-{
- long 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)
- target_write_memory (from, (unsigned char *) &insn, 4);
-}
-
-/* Table of emit ops for 32-bit. */
-
-static struct emit_ops ppc_emit_ops_impl =
-{
- ppc_emit_prologue,
- ppc_emit_epilogue,
- ppc_emit_add,
- ppc_emit_sub,
- ppc_emit_mul,
- ppc_emit_lsh,
- ppc_emit_rsh_signed,
- ppc_emit_rsh_unsigned,
- ppc_emit_ext,
- ppc_emit_log_not,
- ppc_emit_bit_and,
- ppc_emit_bit_or,
- ppc_emit_bit_xor,
- ppc_emit_bit_not,
- ppc_emit_equal,
- ppc_emit_less_signed,
- ppc_emit_less_unsigned,
- ppc_emit_ref,
- ppc_emit_if_goto,
- ppc_emit_goto,
- ppc_write_goto_address,
- ppc_emit_const,
- ppc_emit_call,
- ppc_emit_reg,
- ppc_emit_pop,
- ppc_emit_stack_flush,
- ppc_emit_zero_ext,
- ppc_emit_swap,
- ppc_emit_stack_adjust,
- ppc_emit_int_call_1,
- ppc_emit_void_call_2,
- ppc_emit_eq_goto,
- ppc_emit_ne_goto,
- ppc_emit_lt_goto,
- ppc_emit_le_goto,
- ppc_emit_gt_goto,
- ppc_emit_ge_goto
-};
-
-#ifdef __powerpc64__
-
-/*
-
- Bytecode execution stack frame - 64-bit
-
- | 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
- * 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
-ppc64v1_emit_prologue (void)
-{
- /* On ELFv1, function pointers really point to function descriptor,
- so emit one here. We don't care about contents of words 1 and 2,
- so let them just overlap out code. */
- uint64_t opd = current_insn_ptr + 8;
- uint32_t buf[2];
-
- /* Mind the strict aliasing rules. */
- memcpy (buf, &opd, sizeof buf);
- emit_insns(buf, 2);
- 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 prologue in inferior memory. See above comments. */
-
-static void
-ppc64v2_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"
- "mfcr 3 \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"
- "mfcr 3 \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, 1);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (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
-ppc64v1_emit_reg (int reg)
-{
- uint32_t buf[15];
- uint32_t *p = buf;
-
- /* fctx->regs is passed in r3 and then saved in 176(1). */
- p += GEN_LD (p, 3, 31, -32);
- p += GEN_LI (p, 4, reg);
- p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
- p += gen_call (p, get_raw_reg_func_addr (), 1, 1);
- p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* Likewise, for ELFv2. */
-
-static void
-ppc64v2_emit_reg (int reg)
-{
- uint32_t buf[12];
- uint32_t *p = buf;
-
- /* fctx->regs is passed in r3 and then saved in 176(1). */
- p += GEN_LD (p, 3, 31, -32);
- p += GEN_LI (p, 4, reg);
- p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
- p += gen_call (p, get_raw_reg_func_addr (), 1, 0);
- p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (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"
- " stdu 31, -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");
-}
-
-/* Call function FN - ELFv1. */
-
-static void
-ppc64v1_emit_call (CORE_ADDR fn)
-{
- uint32_t buf[13];
- uint32_t *p = buf;
-
- p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
- p += gen_call (p, fn, 1, 1);
- p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* Call function FN - ELFv2. */
-
-static void
-ppc64v2_emit_call (CORE_ADDR fn)
-{
- uint32_t buf[10];
- uint32_t *p = buf;
-
- p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
- p += gen_call (p, fn, 1, 0);
- p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* FN's prototype is `LONGEST(*fn)(int)'.
- TOP = fn (arg1)
- */
-
-static void
-ppc64v1_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[13];
- uint32_t *p = buf;
-
- /* Setup argument. arg1 is a 16-bit value. */
- p += gen_limm (p, 3, arg1, 1);
- p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
- p += gen_call (p, fn, 1, 1);
- p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* Likewise for ELFv2. */
-
-static void
-ppc64v2_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[10];
- uint32_t *p = buf;
-
- /* Setup argument. arg1 is a 16-bit value. */
- p += gen_limm (p, 3, arg1, 1);
- p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
- p += gen_call (p, fn, 1, 0);
- p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (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
-ppc64v1_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[17];
- uint32_t *p = buf;
-
- /* Save TOP. 0(30) is next-empty. */
- p += GEN_STD (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, 1);
- p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
- p += gen_call (p, fn, 1, 1);
- p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
-
- /* Restore TOP */
- p += GEN_LD (p, 3, 30, 0);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* Likewise for ELFv2. */
-
-static void
-ppc64v2_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- uint32_t buf[14];
- uint32_t *p = buf;
-
- /* Save TOP. 0(30) is next-empty. */
- p += GEN_STD (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, 1);
- p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
- p += gen_call (p, fn, 1, 0);
- p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
-
- /* Restore TOP */
- p += GEN_LD (p, 3, 30, 0);
-
- emit_insns (buf, p - buf);
- gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
-}
-
-/* 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 = 8;
- if (size_p)
- *size_p = 14;
-}
-
-/* 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;
-}
-
-/* Table of emit ops for 64-bit ELFv1. */
-
-static struct emit_ops ppc64v1_emit_ops_impl =
-{
- ppc64v1_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,
- ppc_emit_goto,
- ppc_write_goto_address,
- ppc64_emit_const,
- ppc64v1_emit_call,
- ppc64v1_emit_reg,
- ppc64_emit_pop,
- ppc64_emit_stack_flush,
- ppc64_emit_zero_ext,
- ppc64_emit_swap,
- ppc_emit_stack_adjust,
- ppc64v1_emit_int_call_1,
- ppc64v1_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
-};
-
-/* Table of emit ops for 64-bit ELFv2. */
-
-static struct emit_ops ppc64v2_emit_ops_impl =
-{
- ppc64v2_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,
- ppc_emit_goto,
- ppc_write_goto_address,
- ppc64_emit_const,
- ppc64v2_emit_call,
- ppc64v2_emit_reg,
- ppc64_emit_pop,
- ppc64_emit_stack_flush,
- ppc64_emit_zero_ext,
- ppc64_emit_swap,
- ppc_emit_stack_adjust,
- ppc64v2_emit_int_call_1,
- ppc64v2_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
-};
-
-#endif
-
-/* Implementation of linux_target_ops method "emit_ops". */
-
-static struct emit_ops *
-ppc_emit_ops (void)
-{
-#ifdef __powerpc64__
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
-
- if (register_size (regcache->tdesc, 0) == 8)
- {
- if (is_elfv2_inferior ())
- return &ppc64v2_emit_ops_impl;
- else
- return &ppc64v1_emit_ops_impl;
- }
-#endif
- return &ppc_emit_ops_impl;
-}
-
-/* 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_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;
- if (tdesc == tdesc_powerpc_isa205_ppr_dscr_vsx64l)
- return PPC_TDESC_ISA205_PPR_DSCR_VSX;
- if (tdesc == tdesc_powerpc_isa207_vsx64l)
- return PPC_TDESC_ISA207_VSX;
- if (tdesc == tdesc_powerpc_isa207_htm_vsx64l)
- return PPC_TDESC_ISA207_HTM_VSX;
-#endif
-
- if (tdesc == tdesc_powerpc_32l)
- return PPC_TDESC_BASE;
- if (tdesc == tdesc_powerpc_altivec32l)
- return PPC_TDESC_ALTIVEC;
- 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_isa205_ppr_dscr_vsx32l)
- return PPC_TDESC_ISA205_PPR_DSCR_VSX;
- if (tdesc == tdesc_powerpc_isa207_vsx32l)
- return PPC_TDESC_ISA207_VSX;
- if (tdesc == tdesc_powerpc_isa207_htm_vsx32l)
- return PPC_TDESC_ISA207_HTM_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,
- ppc_cannot_fetch_register,
- ppc_cannot_store_register,
- NULL, /* fetch_register */
- ppc_get_pc,
- ppc_set_pc,
- NULL, /* breakpoint_kind_from_pc */
- ppc_sw_breakpoint_from_kind,
- NULL,
- 0,
- ppc_breakpoint_at,
- ppc_supports_z_point_type,
- ppc_insert_point,
- ppc_remove_point,
- NULL,
- NULL,
- ppc_collect_ptrace_register,
- ppc_supply_ptrace_register,
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- ppc_supports_tracepoints,
- ppc_get_thread_area,
- ppc_install_fast_tracepoint_jump_pad,
- ppc_emit_ops,
- 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
-initialize_low_arch (void)
-{
- /* Initialize the Linux target descriptions. */
-
- init_registers_powerpc_32l ();
- init_registers_powerpc_altivec32l ();
- init_registers_powerpc_vsx32l ();
- init_registers_powerpc_isa205_32l ();
- init_registers_powerpc_isa205_altivec32l ();
- init_registers_powerpc_isa205_vsx32l ();
- init_registers_powerpc_isa205_ppr_dscr_vsx32l ();
- init_registers_powerpc_isa207_vsx32l ();
- init_registers_powerpc_isa207_htm_vsx32l ();
- init_registers_powerpc_e500l ();
-#if __powerpc64__
- init_registers_powerpc_64l ();
- init_registers_powerpc_altivec64l ();
- init_registers_powerpc_vsx64l ();
- init_registers_powerpc_isa205_64l ();
- init_registers_powerpc_isa205_altivec64l ();
- init_registers_powerpc_isa205_vsx64l ();
- init_registers_powerpc_isa205_ppr_dscr_vsx64l ();
- init_registers_powerpc_isa207_vsx64l ();
- init_registers_powerpc_isa207_htm_vsx64l ();
-#endif
-
- initialize_regsets_info (&ppc_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/PowerPC specific low level interface, for the remote server for
+ GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+#include "elf/common.h"
+#include <sys/uio.h>
+#include <elf.h>
+#include <asm/ptrace.h>
+
+#include "arch/ppc-linux-common.h"
+#include "arch/ppc-linux-tdesc.h"
+#include "nat/ppc-linux.h"
+#include "nat/linux-ptrace.h"
+#include "linux-ppc-tdesc-init.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)
+
+/* Holds the AT_HWCAP auxv entry. */
+
+static unsigned long ppc_hwcap;
+
+/* Holds the AT_HWCAP2 auxv entry. */
+
+static unsigned long ppc_hwcap2;
+
+
+#define ppc_num_regs 73
+
+#ifdef __powerpc64__
+/* We use a constant for FPSCR instead of PT_FPSCR, because
+ many shipped PPC64 kernels had the wrong value in ptrace.h. */
+static int ppc_regmap[] =
+ {PT_R0 * 8, PT_R1 * 8, PT_R2 * 8, PT_R3 * 8,
+ PT_R4 * 8, PT_R5 * 8, PT_R6 * 8, PT_R7 * 8,
+ PT_R8 * 8, PT_R9 * 8, PT_R10 * 8, PT_R11 * 8,
+ PT_R12 * 8, PT_R13 * 8, PT_R14 * 8, PT_R15 * 8,
+ PT_R16 * 8, PT_R17 * 8, PT_R18 * 8, PT_R19 * 8,
+ PT_R20 * 8, PT_R21 * 8, PT_R22 * 8, PT_R23 * 8,
+ PT_R24 * 8, PT_R25 * 8, PT_R26 * 8, PT_R27 * 8,
+ PT_R28 * 8, PT_R29 * 8, PT_R30 * 8, PT_R31 * 8,
+ PT_FPR0*8, PT_FPR0*8 + 8, PT_FPR0*8+16, PT_FPR0*8+24,
+ PT_FPR0*8+32, PT_FPR0*8+40, PT_FPR0*8+48, PT_FPR0*8+56,
+ PT_FPR0*8+64, PT_FPR0*8+72, PT_FPR0*8+80, PT_FPR0*8+88,
+ PT_FPR0*8+96, PT_FPR0*8+104, PT_FPR0*8+112, PT_FPR0*8+120,
+ PT_FPR0*8+128, PT_FPR0*8+136, PT_FPR0*8+144, PT_FPR0*8+152,
+ PT_FPR0*8+160, PT_FPR0*8+168, PT_FPR0*8+176, PT_FPR0*8+184,
+ PT_FPR0*8+192, PT_FPR0*8+200, PT_FPR0*8+208, PT_FPR0*8+216,
+ PT_FPR0*8+224, PT_FPR0*8+232, PT_FPR0*8+240, PT_FPR0*8+248,
+ PT_NIP * 8, PT_MSR * 8, PT_CCR * 8, PT_LNK * 8,
+ PT_CTR * 8, PT_XER * 8, PT_FPR0*8 + 256,
+ PT_ORIG_R3 * 8, PT_TRAP * 8 };
+#else
+/* Currently, don't check/send MQ. */
+static int ppc_regmap[] =
+ {PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4,
+ PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4,
+ PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4,
+ PT_R12 * 4, PT_R13 * 4, PT_R14 * 4, PT_R15 * 4,
+ PT_R16 * 4, PT_R17 * 4, PT_R18 * 4, PT_R19 * 4,
+ PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
+ PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
+ PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
+ PT_FPR0*4, PT_FPR0*4 + 8, PT_FPR0*4+16, PT_FPR0*4+24,
+ PT_FPR0*4+32, PT_FPR0*4+40, PT_FPR0*4+48, PT_FPR0*4+56,
+ PT_FPR0*4+64, PT_FPR0*4+72, PT_FPR0*4+80, PT_FPR0*4+88,
+ PT_FPR0*4+96, PT_FPR0*4+104, PT_FPR0*4+112, PT_FPR0*4+120,
+ PT_FPR0*4+128, PT_FPR0*4+136, PT_FPR0*4+144, PT_FPR0*4+152,
+ PT_FPR0*4+160, PT_FPR0*4+168, PT_FPR0*4+176, PT_FPR0*4+184,
+ PT_FPR0*4+192, PT_FPR0*4+200, PT_FPR0*4+208, PT_FPR0*4+216,
+ PT_FPR0*4+224, PT_FPR0*4+232, PT_FPR0*4+240, PT_FPR0*4+248,
+ PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
+ PT_CTR * 4, PT_XER * 4, PT_FPSCR * 4,
+ PT_ORIG_R3 * 4, PT_TRAP * 4
+ };
+
+static int ppc_regmap_e500[] =
+ {PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4,
+ PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4,
+ PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4,
+ PT_R12 * 4, PT_R13 * 4, PT_R14 * 4, PT_R15 * 4,
+ PT_R16 * 4, PT_R17 * 4, PT_R18 * 4, PT_R19 * 4,
+ PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
+ PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
+ PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
+ -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,
+ PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
+ PT_CTR * 4, PT_XER * 4, -1,
+ PT_ORIG_R3 * 4, PT_TRAP * 4
+ };
+#endif
+
+/* Check whether the kernel provides a register set with number
+ REGSET_ID of size REGSETSIZE for process/thread TID. */
+
+static int
+ppc_check_regset (int tid, int regset_id, int regsetsize)
+{
+ void *buf = alloca (regsetsize);
+ struct iovec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = regsetsize;
+
+ if (ptrace (PTRACE_GETREGSET, tid, regset_id, &iov) >= 0
+ || errno == ENODATA)
+ return 1;
+ return 0;
+}
+
+static int
+ppc_cannot_store_register (int regno)
+{
+ const struct target_desc *tdesc = current_process ()->tdesc;
+
+#ifndef __powerpc64__
+ /* Some kernels do not allow us to store fpscr. */
+ if (!(ppc_hwcap & PPC_FEATURE_HAS_SPE)
+ && regno == find_regno (tdesc, "fpscr"))
+ return 2;
+#endif
+
+ /* Some kernels do not allow us to store orig_r3 or trap. */
+ if (regno == find_regno (tdesc, "orig_r3")
+ || regno == find_regno (tdesc, "trap"))
+ return 2;
+
+ return 0;
+}
+
+static int
+ppc_cannot_fetch_register (int regno)
+{
+ return 0;
+}
+
+static void
+ppc_collect_ptrace_register (struct regcache *regcache, int regno, char *buf)
+{
+ memset (buf, 0, sizeof (long));
+
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ {
+ /* Little-endian values always sit at the left end of the buffer. */
+ collect_register (regcache, regno, buf);
+ }
+ else if (__BYTE_ORDER == __BIG_ENDIAN)
+ {
+ /* Big-endian values sit at the right end of the buffer. In case of
+ registers whose sizes are smaller than sizeof (long), we must use a
+ padding to access them correctly. */
+ int size = register_size (regcache->tdesc, regno);
+
+ if (size < sizeof (long))
+ collect_register (regcache, regno, buf + sizeof (long) - size);
+ else
+ collect_register (regcache, regno, buf);
+ }
+ else
+ perror_with_name ("Unexpected byte order");
+}
+
+static void
+ppc_supply_ptrace_register (struct regcache *regcache,
+ int regno, const char *buf)
+{
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ {
+ /* Little-endian values always sit at the left end of the buffer. */
+ supply_register (regcache, regno, buf);
+ }
+ else if (__BYTE_ORDER == __BIG_ENDIAN)
+ {
+ /* Big-endian values sit at the right end of the buffer. In case of
+ registers whose sizes are smaller than sizeof (long), we must use a
+ padding to access them correctly. */
+ int size = register_size (regcache->tdesc, regno);
+
+ if (size < sizeof (long))
+ supply_register (regcache, regno, buf + sizeof (long) - size);
+ else
+ supply_register (regcache, regno, buf);
+ }
+ else
+ perror_with_name ("Unexpected byte order");
+}
+
+static CORE_ADDR
+ppc_get_pc (struct regcache *regcache)
+{
+ if (register_size (regcache->tdesc, 0) == 4)
+ {
+ unsigned int pc;
+ collect_register_by_name (regcache, "pc", &pc);
+ return (CORE_ADDR) pc;
+ }
+ else
+ {
+ unsigned long pc;
+ collect_register_by_name (regcache, "pc", &pc);
+ return (CORE_ADDR) pc;
+ }
+}
+
+static void
+ppc_set_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ if (register_size (regcache->tdesc, 0) == 4)
+ {
+ unsigned int newpc = pc;
+ supply_register_by_name (regcache, "pc", &newpc);
+ }
+ else
+ {
+ unsigned long newpc = pc;
+ supply_register_by_name (regcache, "pc", &newpc);
+ }
+}
+
+#ifndef __powerpc64__
+static int ppc_regmap_adjusted;
+#endif
+
+
+/* Correct in either endianness.
+ This instruction is "twge r2, r2", which GDB uses as a software
+ breakpoint. */
+static const unsigned int ppc_breakpoint = 0x7d821008;
+#define ppc_breakpoint_len 4
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+ppc_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = ppc_breakpoint_len;
+ return (const gdb_byte *) &ppc_breakpoint;
+}
+
+static int
+ppc_breakpoint_at (CORE_ADDR where)
+{
+ unsigned int insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
+ if (insn == ppc_breakpoint)
+ return 1;
+ /* If necessary, recognize more trap instructions here. GDB only uses
+ the one. */
+
+ return 0;
+}
+
+/* Implement supports_z_point_type target-ops.
+ Returns true if type Z_TYPE breakpoint is supported.
+
+ Handling software breakpoint at server side, so tracepoints
+ and breakpoints can be inserted at the same location. */
+
+static int
+ppc_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ return 1;
+ case Z_PACKET_HW_BP:
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_ACCESS_WP:
+ default:
+ return 0;
+ }
+}
+
+/* Implement insert_point target-ops.
+ Returns 0 on success, -1 on failure and 1 on unsupported. */
+
+static int
+ppc_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ switch (type)
+ {
+ case raw_bkpt_type_sw:
+ return insert_memory_breakpoint (bp);
+
+ case raw_bkpt_type_hw:
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+/* Implement remove_point target-ops.
+ Returns 0 on success, -1 on failure and 1 on unsupported. */
+
+static int
+ppc_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ switch (type)
+ {
+ case raw_bkpt_type_sw:
+ return remove_memory_breakpoint (bp);
+
+ case raw_bkpt_type_hw:
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+/* Provide only a fill function for the general register set. ps_lgetregs
+ will use this for NPTL support. */
+
+static void ppc_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+
+ for (i = 0; i < 32; i++)
+ ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
+
+ for (i = 64; i < 70; i++)
+ ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
+
+ for (i = 71; i < 73; i++)
+ ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
+}
+
+/* Program Priority Register regset fill function. */
+
+static void
+ppc_fill_pprregset (struct regcache *regcache, void *buf)
+{
+ char *ppr = (char *) buf;
+
+ collect_register_by_name (regcache, "ppr", ppr);
+}
+
+/* Program Priority Register regset store function. */
+
+static void
+ppc_store_pprregset (struct regcache *regcache, const void *buf)
+{
+ const char *ppr = (const char *) buf;
+
+ supply_register_by_name (regcache, "ppr", ppr);
+}
+
+/* Data Stream Control Register regset fill function. */
+
+static void
+ppc_fill_dscrregset (struct regcache *regcache, void *buf)
+{
+ char *dscr = (char *) buf;
+
+ collect_register_by_name (regcache, "dscr", dscr);
+}
+
+/* Data Stream Control Register regset store function. */
+
+static void
+ppc_store_dscrregset (struct regcache *regcache, const void *buf)
+{
+ const char *dscr = (const char *) buf;
+
+ supply_register_by_name (regcache, "dscr", dscr);
+}
+
+/* Target Address Register regset fill function. */
+
+static void
+ppc_fill_tarregset (struct regcache *regcache, void *buf)
+{
+ char *tar = (char *) buf;
+
+ collect_register_by_name (regcache, "tar", tar);
+}
+
+/* Target Address Register regset store function. */
+
+static void
+ppc_store_tarregset (struct regcache *regcache, const void *buf)
+{
+ const char *tar = (const char *) buf;
+
+ supply_register_by_name (regcache, "tar", tar);
+}
+
+/* Event-Based Branching regset store function. Unless the inferior
+ has a perf event open, ptrace can return in error when reading and
+ writing to the regset, with ENODATA. For reading, the registers
+ will correctly show as unavailable. For writing, gdbserver
+ currently only caches any register writes from P and G packets and
+ the stub always tries to write all the regsets when resuming the
+ inferior, which would result in frequent warnings. For this
+ reason, we don't define a fill function. This also means that the
+ client-side regcache will be dirty if the user tries to write to
+ the EBB registers. G packets that the client sends to write to
+ unrelated registers will also include data for EBB registers, even
+ if they are unavailable. */
+
+static void
+ppc_store_ebbregset (struct regcache *regcache, const void *buf)
+{
+ const char *regset = (const char *) buf;
+
+ /* The order in the kernel regset is: EBBRR, EBBHR, BESCR. In the
+ .dat file is BESCR, EBBHR, EBBRR. */
+ supply_register_by_name (regcache, "ebbrr", ®set[0]);
+ supply_register_by_name (regcache, "ebbhr", ®set[8]);
+ supply_register_by_name (regcache, "bescr", ®set[16]);
+}
+
+/* Performance Monitoring Unit regset fill function. */
+
+static void
+ppc_fill_pmuregset (struct regcache *regcache, void *buf)
+{
+ char *regset = (char *) buf;
+
+ /* The order in the kernel regset is SIAR, SDAR, SIER, MMCR2, MMCR0.
+ In the .dat file is MMCR0, MMCR2, SIAR, SDAR, SIER. */
+ collect_register_by_name (regcache, "siar", ®set[0]);
+ collect_register_by_name (regcache, "sdar", ®set[8]);
+ collect_register_by_name (regcache, "sier", ®set[16]);
+ collect_register_by_name (regcache, "mmcr2", ®set[24]);
+ collect_register_by_name (regcache, "mmcr0", ®set[32]);
+}
+
+/* Performance Monitoring Unit regset store function. */
+
+static void
+ppc_store_pmuregset (struct regcache *regcache, const void *buf)
+{
+ const char *regset = (const char *) buf;
+
+ supply_register_by_name (regcache, "siar", ®set[0]);
+ supply_register_by_name (regcache, "sdar", ®set[8]);
+ supply_register_by_name (regcache, "sier", ®set[16]);
+ supply_register_by_name (regcache, "mmcr2", ®set[24]);
+ supply_register_by_name (regcache, "mmcr0", ®set[32]);
+}
+
+/* Hardware Transactional Memory special-purpose register regset fill
+ function. */
+
+static void
+ppc_fill_tm_sprregset (struct regcache *regcache, void *buf)
+{
+ int i, base;
+ char *regset = (char *) buf;
+
+ base = find_regno (regcache->tdesc, "tfhar");
+ for (i = 0; i < 3; i++)
+ collect_register (regcache, base + i, ®set[i * 8]);
+}
+
+/* Hardware Transactional Memory special-purpose register regset store
+ function. */
+
+static void
+ppc_store_tm_sprregset (struct regcache *regcache, const void *buf)
+{
+ int i, base;
+ const char *regset = (const char *) buf;
+
+ base = find_regno (regcache->tdesc, "tfhar");
+ for (i = 0; i < 3; i++)
+ supply_register (regcache, base + i, ®set[i * 8]);
+}
+
+/* For the same reasons as the EBB regset, none of the HTM
+ checkpointed regsets have a fill function. These registers are
+ only available if the inferior is in a transaction. */
+
+/* Hardware Transactional Memory checkpointed general-purpose regset
+ store function. */
+
+static void
+ppc_store_tm_cgprregset (struct regcache *regcache, const void *buf)
+{
+ int i, base, size, endian_offset;
+ const char *regset = (const char *) buf;
+
+ base = find_regno (regcache->tdesc, "cr0");
+ size = register_size (regcache->tdesc, base);
+
+ gdb_assert (size == 4 || size == 8);
+
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, base + i, ®set[i * size]);
+
+ endian_offset = 0;
+
+ if ((size == 8) && (__BYTE_ORDER == __BIG_ENDIAN))
+ endian_offset = 4;
+
+ supply_register_by_name (regcache, "ccr",
+ ®set[PT_CCR * size + endian_offset]);
+
+ supply_register_by_name (regcache, "cxer",
+ ®set[PT_XER * size + endian_offset]);
+
+ supply_register_by_name (regcache, "clr", ®set[PT_LNK * size]);
+ supply_register_by_name (regcache, "cctr", ®set[PT_CTR * size]);
+}
+
+/* Hardware Transactional Memory checkpointed floating-point regset
+ store function. */
+
+static void
+ppc_store_tm_cfprregset (struct regcache *regcache, const void *buf)
+{
+ int i, base;
+ const char *regset = (const char *) buf;
+
+ base = find_regno (regcache->tdesc, "cf0");
+
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, base + i, ®set[i * 8]);
+
+ supply_register_by_name (regcache, "cfpscr", ®set[32 * 8]);
+}
+
+/* Hardware Transactional Memory checkpointed vector regset store
+ function. */
+
+static void
+ppc_store_tm_cvrregset (struct regcache *regcache, const void *buf)
+{
+ int i, base;
+ const char *regset = (const char *) buf;
+ int vscr_offset = 0;
+
+ base = find_regno (regcache->tdesc, "cvr0");
+
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, base + i, ®set[i * 16]);
+
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ vscr_offset = 12;
+
+ supply_register_by_name (regcache, "cvscr",
+ ®set[32 * 16 + vscr_offset]);
+
+ supply_register_by_name (regcache, "cvrsave", ®set[33 * 16]);
+}
+
+/* Hardware Transactional Memory checkpointed vector-scalar regset
+ store function. */
+
+static void
+ppc_store_tm_cvsxregset (struct regcache *regcache, const void *buf)
+{
+ int i, base;
+ const char *regset = (const char *) buf;
+
+ base = find_regno (regcache->tdesc, "cvs0h");
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, base + i, ®set[i * 8]);
+}
+
+/* Hardware Transactional Memory checkpointed Program Priority
+ Register regset store function. */
+
+static void
+ppc_store_tm_cpprregset (struct regcache *regcache, const void *buf)
+{
+ const char *cppr = (const char *) buf;
+
+ supply_register_by_name (regcache, "cppr", cppr);
+}
+
+/* Hardware Transactional Memory checkpointed Data Stream Control
+ Register regset store function. */
+
+static void
+ppc_store_tm_cdscrregset (struct regcache *regcache, const void *buf)
+{
+ const char *cdscr = (const char *) buf;
+
+ supply_register_by_name (regcache, "cdscr", cdscr);
+}
+
+/* Hardware Transactional Memory checkpointed Target Address Register
+ regset store function. */
+
+static void
+ppc_store_tm_ctarregset (struct regcache *regcache, const void *buf)
+{
+ const char *ctar = (const char *) buf;
+
+ supply_register_by_name (regcache, "ctar", ctar);
+}
+
+static void
+ppc_fill_vsxregset (struct regcache *regcache, void *buf)
+{
+ int i, base;
+ char *regset = (char *) buf;
+
+ base = find_regno (regcache->tdesc, "vs0h");
+ for (i = 0; i < 32; i++)
+ collect_register (regcache, base + i, ®set[i * 8]);
+}
+
+static void
+ppc_store_vsxregset (struct regcache *regcache, const void *buf)
+{
+ int i, base;
+ const char *regset = (const char *) buf;
+
+ base = find_regno (regcache->tdesc, "vs0h");
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, base + i, ®set[i * 8]);
+}
+
+static void
+ppc_fill_vrregset (struct regcache *regcache, void *buf)
+{
+ int i, base;
+ char *regset = (char *) buf;
+ int vscr_offset = 0;
+
+ base = find_regno (regcache->tdesc, "vr0");
+ for (i = 0; i < 32; i++)
+ collect_register (regcache, base + i, ®set[i * 16]);
+
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ vscr_offset = 12;
+
+ collect_register_by_name (regcache, "vscr",
+ ®set[32 * 16 + vscr_offset]);
+
+ collect_register_by_name (regcache, "vrsave", ®set[33 * 16]);
+}
+
+static void
+ppc_store_vrregset (struct regcache *regcache, const void *buf)
+{
+ int i, base;
+ const char *regset = (const char *) buf;
+ int vscr_offset = 0;
+
+ base = find_regno (regcache->tdesc, "vr0");
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, base + i, ®set[i * 16]);
+
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ vscr_offset = 12;
+
+ supply_register_by_name (regcache, "vscr",
+ ®set[32 * 16 + vscr_offset]);
+ supply_register_by_name (regcache, "vrsave", ®set[33 * 16]);
+}
+
+struct gdb_evrregset_t
+{
+ unsigned long evr[32];
+ unsigned long long acc;
+ unsigned long spefscr;
+};
+
+static void
+ppc_fill_evrregset (struct regcache *regcache, void *buf)
+{
+ int i, ev0;
+ struct gdb_evrregset_t *regset = (struct gdb_evrregset_t *) buf;
+
+ ev0 = find_regno (regcache->tdesc, "ev0h");
+ for (i = 0; i < 32; i++)
+ collect_register (regcache, ev0 + i, ®set->evr[i]);
+
+ collect_register_by_name (regcache, "acc", ®set->acc);
+ collect_register_by_name (regcache, "spefscr", ®set->spefscr);
+}
+
+static void
+ppc_store_evrregset (struct regcache *regcache, const void *buf)
+{
+ int i, ev0;
+ const struct gdb_evrregset_t *regset = (const struct gdb_evrregset_t *) buf;
+
+ ev0 = find_regno (regcache->tdesc, "ev0h");
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, ev0 + i, ®set->evr[i]);
+
+ supply_register_by_name (regcache, "acc", ®set->acc);
+ supply_register_by_name (regcache, "spefscr", ®set->spefscr);
+}
+
+/* Support for hardware single step. */
+
+static int
+ppc_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static struct regset_info ppc_regsets[] = {
+ /* List the extra register sets before GENERAL_REGS. That way we will
+ fetch them every time, but still fall back to PTRACE_PEEKUSER for the
+ general registers. Some kernels support these, but not the newer
+ PPC_PTRACE_GETREGS. */
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CTAR, 0, EXTENDED_REGS,
+ NULL, ppc_store_tm_ctarregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CDSCR, 0, EXTENDED_REGS,
+ NULL, ppc_store_tm_cdscrregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CPPR, 0, EXTENDED_REGS,
+ NULL, ppc_store_tm_cpprregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CVSX, 0, EXTENDED_REGS,
+ NULL, ppc_store_tm_cvsxregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CVMX, 0, EXTENDED_REGS,
+ NULL, ppc_store_tm_cvrregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CFPR, 0, EXTENDED_REGS,
+ NULL, ppc_store_tm_cfprregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CGPR, 0, EXTENDED_REGS,
+ NULL, ppc_store_tm_cgprregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_SPR, 0, EXTENDED_REGS,
+ ppc_fill_tm_sprregset, ppc_store_tm_sprregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_EBB, 0, EXTENDED_REGS,
+ NULL, ppc_store_ebbregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PMU, 0, EXTENDED_REGS,
+ ppc_fill_pmuregset, ppc_store_pmuregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TAR, 0, EXTENDED_REGS,
+ ppc_fill_tarregset, ppc_store_tarregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PPR, 0, EXTENDED_REGS,
+ ppc_fill_pprregset, ppc_store_pprregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_DSCR, 0, EXTENDED_REGS,
+ ppc_fill_dscrregset, ppc_store_dscrregset },
+ { PTRACE_GETVSXREGS, PTRACE_SETVSXREGS, 0, 0, EXTENDED_REGS,
+ ppc_fill_vsxregset, ppc_store_vsxregset },
+ { PTRACE_GETVRREGS, PTRACE_SETVRREGS, 0, 0, EXTENDED_REGS,
+ ppc_fill_vrregset, ppc_store_vrregset },
+ { PTRACE_GETEVRREGS, PTRACE_SETEVRREGS, 0, 0, EXTENDED_REGS,
+ ppc_fill_evrregset, ppc_store_evrregset },
+ { 0, 0, 0, 0, GENERAL_REGS, ppc_fill_gregset, NULL },
+ NULL_REGSET
+};
+
+static struct usrregs_info ppc_usrregs_info =
+ {
+ ppc_num_regs,
+ ppc_regmap,
+ };
+
+static struct regsets_info ppc_regsets_info =
+ {
+ ppc_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &ppc_usrregs_info,
+ &ppc_regsets_info
+ };
+
+static const struct regs_info *
+ppc_regs_info (void)
+{
+ return ®s_info;
+}
+
+static void
+ppc_arch_setup (void)
+{
+ const struct target_desc *tdesc;
+ struct regset_info *regset;
+ struct ppc_linux_features features = ppc_linux_no_features;
+
+ int tid = lwpid_of (current_thread);
+
+ features.wordsize = ppc_linux_target_wordsize (tid);
+
+ if (features.wordsize == 4)
+ tdesc = tdesc_powerpc_32l;
+ else
+ tdesc = tdesc_powerpc_64l;
+
+ current_process ()->tdesc = tdesc;
+
+ /* The value of current_process ()->tdesc needs to be set for this
+ call. */
+ ppc_hwcap = linux_get_hwcap (features.wordsize);
+ ppc_hwcap2 = linux_get_hwcap2 (features.wordsize);
+
+ features.isa205 = ppc_linux_has_isa205 (ppc_hwcap);
+
+ if (ppc_hwcap & PPC_FEATURE_HAS_VSX)
+ features.vsx = true;
+
+ if (ppc_hwcap & PPC_FEATURE_HAS_ALTIVEC)
+ features.altivec = true;
+
+ if ((ppc_hwcap2 & PPC_FEATURE2_DSCR)
+ && ppc_check_regset (tid, NT_PPC_DSCR, PPC_LINUX_SIZEOF_DSCRREGSET)
+ && ppc_check_regset (tid, NT_PPC_PPR, PPC_LINUX_SIZEOF_PPRREGSET))
+ {
+ features.ppr_dscr = true;
+ if ((ppc_hwcap2 & PPC_FEATURE2_ARCH_2_07)
+ && (ppc_hwcap2 & PPC_FEATURE2_TAR)
+ && (ppc_hwcap2 & PPC_FEATURE2_EBB)
+ && ppc_check_regset (tid, NT_PPC_TAR,
+ PPC_LINUX_SIZEOF_TARREGSET)
+ && ppc_check_regset (tid, NT_PPC_EBB,
+ PPC_LINUX_SIZEOF_EBBREGSET)
+ && ppc_check_regset (tid, NT_PPC_PMU,
+ PPC_LINUX_SIZEOF_PMUREGSET))
+ {
+ features.isa207 = true;
+ if ((ppc_hwcap2 & PPC_FEATURE2_HTM)
+ && ppc_check_regset (tid, NT_PPC_TM_SPR,
+ PPC_LINUX_SIZEOF_TM_SPRREGSET))
+ features.htm = true;
+ }
+ }
+
+ tdesc = ppc_linux_match_description (features);
+
+ /* On 32-bit machines, check for SPE registers.
+ Set the low target's regmap field as appropriately. */
+#ifndef __powerpc64__
+ if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
+ tdesc = tdesc_powerpc_e500l;
+
+ if (!ppc_regmap_adjusted)
+ {
+ if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
+ ppc_usrregs_info.regmap = ppc_regmap_e500;
+
+ /* If the FPSCR is 64-bit wide, we need to fetch the whole
+ 64-bit slot and not just its second word. The PT_FPSCR
+ supplied in a 32-bit GDB compilation doesn't reflect
+ this. */
+ if (register_size (tdesc, 70) == 8)
+ ppc_regmap[70] = (48 + 2*32) * sizeof (long);
+
+ ppc_regmap_adjusted = 1;
+ }
+#endif
+
+ current_process ()->tdesc = tdesc;
+
+ for (regset = ppc_regsets; regset->size >= 0; regset++)
+ switch (regset->get_request)
+ {
+ case PTRACE_GETVRREGS:
+ regset->size = features.altivec ? PPC_LINUX_SIZEOF_VRREGSET : 0;
+ break;
+ case PTRACE_GETVSXREGS:
+ regset->size = features.vsx ? PPC_LINUX_SIZEOF_VSXREGSET : 0;
+ break;
+ case PTRACE_GETEVRREGS:
+ if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
+ regset->size = 32 * 4 + 8 + 4;
+ else
+ regset->size = 0;
+ break;
+ case PTRACE_GETREGSET:
+ switch (regset->nt_type)
+ {
+ case NT_PPC_PPR:
+ regset->size = (features.ppr_dscr ?
+ PPC_LINUX_SIZEOF_PPRREGSET : 0);
+ break;
+ case NT_PPC_DSCR:
+ regset->size = (features.ppr_dscr ?
+ PPC_LINUX_SIZEOF_DSCRREGSET : 0);
+ break;
+ case NT_PPC_TAR:
+ regset->size = (features.isa207 ?
+ PPC_LINUX_SIZEOF_TARREGSET : 0);
+ break;
+ case NT_PPC_EBB:
+ regset->size = (features.isa207 ?
+ PPC_LINUX_SIZEOF_EBBREGSET : 0);
+ break;
+ case NT_PPC_PMU:
+ regset->size = (features.isa207 ?
+ PPC_LINUX_SIZEOF_PMUREGSET : 0);
+ break;
+ case NT_PPC_TM_SPR:
+ regset->size = (features.htm ?
+ PPC_LINUX_SIZEOF_TM_SPRREGSET : 0);
+ break;
+ case NT_PPC_TM_CGPR:
+ if (features.wordsize == 4)
+ regset->size = (features.htm ?
+ PPC32_LINUX_SIZEOF_CGPRREGSET : 0);
+ else
+ regset->size = (features.htm ?
+ PPC64_LINUX_SIZEOF_CGPRREGSET : 0);
+ break;
+ case NT_PPC_TM_CFPR:
+ regset->size = (features.htm ?
+ PPC_LINUX_SIZEOF_CFPRREGSET : 0);
+ break;
+ case NT_PPC_TM_CVMX:
+ regset->size = (features.htm ?
+ PPC_LINUX_SIZEOF_CVMXREGSET : 0);
+ break;
+ case NT_PPC_TM_CVSX:
+ regset->size = (features.htm ?
+ PPC_LINUX_SIZEOF_CVSXREGSET : 0);
+ break;
+ case NT_PPC_TM_CPPR:
+ regset->size = (features.htm ?
+ PPC_LINUX_SIZEOF_CPPRREGSET : 0);
+ break;
+ case NT_PPC_TM_CDSCR:
+ regset->size = (features.htm ?
+ PPC_LINUX_SIZEOF_CDSCRREGSET : 0);
+ break;
+ case NT_PPC_TM_CTAR:
+ regset->size = (features.htm ?
+ PPC_LINUX_SIZEOF_CTARREGSET : 0);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* 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 (ptid_t (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
+ CORE_ADDR phdr;
+ Elf64_Ehdr ehdr;
+
+ const struct target_desc *tdesc = current_process ()->tdesc;
+ int wordsize = register_size (tdesc, 0);
+
+ if (!linux_get_auxv (wordsize, 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;
+ target_write_memory (*to, (unsigned char *) &insn, 4);
+ *to += 4;
+
+ /* Build a unconditional branch and copy LK bit. */
+ insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
+ target_write_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);
+
+ target_write_memory (*to, (unsigned char *) &bdnz_insn, 4);
+ *to += 4;
+ target_write_memory (*to, (unsigned char *) &bf_insn, 4);
+ *to += 4;
+
+ /* Build a unconditional branch and copy LK bit. */
+ insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
+ target_write_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);
+ target_write_memory (*to, (unsigned char *) &insn, 4);
+ *to += 4;
+
+ return;
+ }
+ }
+
+ target_write_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 original 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. */
+ target_write_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);
+ target_write_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;
+}
+
+/* Emits a given buffer into the target at current_insn_ptr. Length
+ is in units of 32-bit words. */
+
+static void
+emit_insns (uint32_t *buf, int n)
+{
+ n = n * sizeof (uint32_t);
+ target_write_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 - 32-bit
+
+ | LR save area (SP + 4)
+ 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 and 16-byte alignment.
+ | 16 bytes.
+ SP +- Back-chain (SP')
+
+ initial frame size
+ = 16 + (4 * 4) + 64
+ = 96
+
+ 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 the high part of TOP value.
+ It was the first argument, pointer to regs.
+ r4 is used for cache of the low part of TOP value.
+ It was the second argument, pointer to the result.
+ We should set *result = TOP after leaving this function.
+
+ Note:
+ * To restore stack at epilogue
+ => sp = r31
+ * To check stack is big enough for bytecode execution.
+ => r30 - 8 > SP + 8
+ * To return execution result.
+ => 0(r4) = TOP
+
+ */
+
+/* Regardless of endian, register 3 is always high part, 4 is low part.
+ These defines are used when the register pair is stored/loaded.
+ Likewise, to simplify code, have a similiar define for 5:6. */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define TOP_FIRST "4"
+#define TOP_SECOND "3"
+#define TMP_FIRST "6"
+#define TMP_SECOND "5"
+#else
+#define TOP_FIRST "3"
+#define TOP_SECOND "4"
+#define TMP_FIRST "5"
+#define TMP_SECOND "6"
+#endif
+
+/* Emit prologue in inferior memory. See above comments. */
+
+static void
+ppc_emit_prologue (void)
+{
+ EMIT_ASM (/* Save return address. */
+ "mflr 0 \n"
+ "stw 0, 4(1) \n"
+ /* Adjust SP. 96 is the initial frame size. */
+ "stwu 1, -96(1) \n"
+ /* Save r30 and incoming arguments. */
+ "stw 31, 96-4(1) \n"
+ "stw 30, 96-8(1) \n"
+ "stw 4, 96-12(1) \n"
+ "stw 3, 96-16(1) \n"
+ /* Point r31 to original r1 for access arguments. */
+ "addi 31, 1, 96 \n"
+ /* Set r30 to pointing stack-top. */
+ "addi 30, 1, 64 \n"
+ /* Initial r3/TOP to 0. */
+ "li 3, 0 \n"
+ "li 4, 0 \n");
+}
+
+/* Emit epilogue in inferior memory. See above comments. */
+
+static void
+ppc_emit_epilogue (void)
+{
+ EMIT_ASM (/* *result = TOP */
+ "lwz 5, -12(31) \n"
+ "stw " TOP_FIRST ", 0(5) \n"
+ "stw " TOP_SECOND ", 4(5) \n"
+ /* Restore registers. */
+ "lwz 31, -4(31) \n"
+ "lwz 30, -8(31) \n"
+ /* Restore SP. */
+ "lwz 1, 0(1) \n"
+ /* Restore LR. */
+ "lwz 0, 4(1) \n"
+ /* Return 0 for no-error. */
+ "li 3, 0 \n"
+ "mtlr 0 \n"
+ "blr \n");
+}
+
+/* TOP = stack[--sp] + TOP */
+
+static void
+ppc_emit_add (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30)\n"
+ "addc 4, 6, 4 \n"
+ "adde 3, 5, 3 \n");
+}
+
+/* TOP = stack[--sp] - TOP */
+
+static void
+ppc_emit_sub (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "subfc 4, 4, 6 \n"
+ "subfe 3, 3, 5 \n");
+}
+
+/* TOP = stack[--sp] * TOP */
+
+static void
+ppc_emit_mul (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "mulhwu 7, 6, 4 \n"
+ "mullw 3, 6, 3 \n"
+ "mullw 5, 4, 5 \n"
+ "mullw 4, 6, 4 \n"
+ "add 3, 5, 3 \n"
+ "add 3, 7, 3 \n");
+}
+
+/* TOP = stack[--sp] << TOP */
+
+static void
+ppc_emit_lsh (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "subfic 3, 4, 32\n" /* r3 = 32 - TOP */
+ "addi 7, 4, -32\n" /* r7 = TOP - 32 */
+ "slw 5, 5, 4\n" /* Shift high part left */
+ "slw 4, 6, 4\n" /* Shift low part left */
+ "srw 3, 6, 3\n" /* Shift low to high if shift < 32 */
+ "slw 7, 6, 7\n" /* Shift low to high if shift >= 32 */
+ "or 3, 5, 3\n"
+ "or 3, 7, 3\n"); /* Assemble high part */
+}
+
+/* Top = stack[--sp] >> TOP
+ (Arithmetic shift right) */
+
+static void
+ppc_emit_rsh_signed (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "addi 7, 4, -32\n" /* r7 = TOP - 32 */
+ "sraw 3, 5, 4\n" /* Shift high part right */
+ "cmpwi 7, 1\n"
+ "blt 0, 1f\n" /* If shift <= 32, goto 1: */
+ "sraw 4, 5, 7\n" /* Shift high to low */
+ "b 2f\n"
+ "1:\n"
+ "subfic 7, 4, 32\n" /* r7 = 32 - TOP */
+ "srw 4, 6, 4\n" /* Shift low part right */
+ "slw 5, 5, 7\n" /* Shift high to low */
+ "or 4, 4, 5\n" /* Assemble low part */
+ "2:\n");
+}
+
+/* Top = stack[--sp] >> TOP
+ (Logical shift right) */
+
+static void
+ppc_emit_rsh_unsigned (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "subfic 3, 4, 32\n" /* r3 = 32 - TOP */
+ "addi 7, 4, -32\n" /* r7 = TOP - 32 */
+ "srw 6, 6, 4\n" /* Shift low part right */
+ "slw 3, 5, 3\n" /* Shift high to low if shift < 32 */
+ "srw 7, 5, 7\n" /* Shift high to low if shift >= 32 */
+ "or 6, 6, 3\n"
+ "srw 3, 5, 4\n" /* Shift high part right */
+ "or 4, 6, 7\n"); /* Assemble low part */
+}
+
+/* Emit code for signed-extension specified by ARG. */
+
+static void
+ppc_emit_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM ("extsb 4, 4\n"
+ "srawi 3, 4, 31");
+ break;
+ case 16:
+ EMIT_ASM ("extsh 4, 4\n"
+ "srawi 3, 4, 31");
+ break;
+ case 32:
+ EMIT_ASM ("srawi 3, 4, 31");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+/* Emit code for zero-extension specified by ARG. */
+
+static void
+ppc_emit_zero_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM ("clrlwi 4,4,24\n"
+ "li 3, 0\n");
+ break;
+ case 16:
+ EMIT_ASM ("clrlwi 4,4,16\n"
+ "li 3, 0\n");
+ break;
+ case 32:
+ EMIT_ASM ("li 3, 0");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+/* TOP = !TOP
+ i.e., TOP = (TOP == 0) ? 1 : 0; */
+
+static void
+ppc_emit_log_not (void)
+{
+ EMIT_ASM ("or 4, 3, 4 \n"
+ "cntlzw 4, 4 \n"
+ "srwi 4, 4, 5 \n"
+ "li 3, 0 \n");
+}
+
+/* TOP = stack[--sp] & TOP */
+
+static void
+ppc_emit_bit_and (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "and 4, 6, 4 \n"
+ "and 3, 5, 3 \n");
+}
+
+/* TOP = stack[--sp] | TOP */
+
+static void
+ppc_emit_bit_or (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "or 4, 6, 4 \n"
+ "or 3, 5, 3 \n");
+}
+
+/* TOP = stack[--sp] ^ TOP */
+
+static void
+ppc_emit_bit_xor (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "xor 4, 6, 4 \n"
+ "xor 3, 5, 3 \n");
+}
+
+/* TOP = ~TOP
+ i.e., TOP = ~(TOP | TOP) */
+
+static void
+ppc_emit_bit_not (void)
+{
+ EMIT_ASM ("nor 3, 3, 3 \n"
+ "nor 4, 4, 4 \n");
+}
+
+/* TOP = stack[--sp] == TOP */
+
+static void
+ppc_emit_equal (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "xor 4, 6, 4 \n"
+ "xor 3, 5, 3 \n"
+ "or 4, 3, 4 \n"
+ "cntlzw 4, 4 \n"
+ "srwi 4, 4, 5 \n"
+ "li 3, 0 \n");
+}
+
+/* TOP = stack[--sp] < TOP
+ (Signed comparison) */
+
+static void
+ppc_emit_less_signed (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "cmplw 6, 6, 4 \n"
+ "cmpw 7, 5, 3 \n"
+ /* CR6 bit 0 = low less and high equal */
+ "crand 6*4+0, 6*4+0, 7*4+2\n"
+ /* CR7 bit 0 = (low less and high equal) or high less */
+ "cror 7*4+0, 7*4+0, 6*4+0\n"
+ "mfcr 4 \n"
+ "rlwinm 4, 4, 29, 31, 31 \n"
+ "li 3, 0 \n");
+}
+
+/* TOP = stack[--sp] < TOP
+ (Unsigned comparison) */
+
+static void
+ppc_emit_less_unsigned (void)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "cmplw 6, 6, 4 \n"
+ "cmplw 7, 5, 3 \n"
+ /* CR6 bit 0 = low less and high equal */
+ "crand 6*4+0, 6*4+0, 7*4+2\n"
+ /* CR7 bit 0 = (low less and high equal) or high less */
+ "cror 7*4+0, 7*4+0, 6*4+0\n"
+ "mfcr 4 \n"
+ "rlwinm 4, 4, 29, 31, 31 \n"
+ "li 3, 0 \n");
+}
+
+/* Access the memory address in TOP in size of SIZE.
+ Zero-extend the read value. */
+
+static void
+ppc_emit_ref (int size)
+{
+ switch (size)
+ {
+ case 1:
+ EMIT_ASM ("lbz 4, 0(4)\n"
+ "li 3, 0");
+ break;
+ case 2:
+ EMIT_ASM ("lhz 4, 0(4)\n"
+ "li 3, 0");
+ break;
+ case 4:
+ EMIT_ASM ("lwz 4, 0(4)\n"
+ "li 3, 0");
+ break;
+ case 8:
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ EMIT_ASM ("lwz 3, 4(4)\n"
+ "lwz 4, 0(4)");
+ else
+ EMIT_ASM ("lwz 3, 0(4)\n"
+ "lwz 4, 4(4)");
+ break;
+ }
+}
+
+/* TOP = NUM */
+
+static void
+ppc_emit_const (LONGEST num)
+{
+ uint32_t buf[10];
+ uint32_t *p = buf;
+
+ p += gen_limm (p, 3, num >> 32 & 0xffffffff, 0);
+ p += gen_limm (p, 4, num & 0xffffffff, 0);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (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
+ppc_emit_reg (int reg)
+{
+ uint32_t buf[13];
+ uint32_t *p = buf;
+
+ /* fctx->regs is passed in r3 and then saved in -16(31). */
+ p += GEN_LWZ (p, 3, 31, -16);
+ p += GEN_LI (p, 4, reg); /* li r4, reg */
+ p += gen_call (p, get_raw_reg_func_addr (), 0, 0);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ {
+ EMIT_ASM ("mr 5, 4\n"
+ "mr 4, 3\n"
+ "mr 3, 5\n");
+ }
+}
+
+/* TOP = stack[--sp] */
+
+static void
+ppc_emit_pop (void)
+{
+ EMIT_ASM ("lwzu " TOP_FIRST ", 8(30) \n"
+ "lwz " TOP_SECOND ", 4(30) \n");
+}
+
+/* stack[sp++] = TOP
+
+ Because we may use up bytecode stack, expand 8 doublewords more
+ if needed. */
+
+static void
+ppc_emit_stack_flush (void)
+{
+ /* Make sure bytecode stack is big enough before push.
+ Otherwise, expand 64-byte more. */
+
+ EMIT_ASM (" stw " TOP_FIRST ", 0(30) \n"
+ " stw " TOP_SECOND ", 4(30)\n"
+ " addi 5, 30, -(8 + 8) \n"
+ " cmpw 7, 5, 1 \n"
+ " bgt 7, 1f \n"
+ " stwu 31, -64(1) \n"
+ "1:addi 30, 30, -8 \n");
+}
+
+/* Swap TOP and stack[sp-1] */
+
+static void
+ppc_emit_swap (void)
+{
+ EMIT_ASM ("lwz " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 12(30) \n"
+ "stw " TOP_FIRST ", 8(30) \n"
+ "stw " TOP_SECOND ", 12(30) \n"
+ "mr 3, 5 \n"
+ "mr 4, 6 \n");
+}
+
+/* Discard N elements in the stack. Also used for ppc64. */
+
+static void
+ppc_emit_stack_adjust (int n)
+{
+ uint32_t buf[6];
+ uint32_t *p = buf;
+
+ n = n << 3;
+ if ((n >> 15) != 0)
+ {
+ emit_error = 1;
+ return;
+ }
+
+ p += GEN_ADDI (p, 30, 30, n);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* Call function FN. */
+
+static void
+ppc_emit_call (CORE_ADDR fn)
+{
+ uint32_t buf[11];
+ uint32_t *p = buf;
+
+ p += gen_call (p, fn, 0, 0);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'.
+ TOP = fn (arg1)
+ */
+
+static void
+ppc_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[15];
+ uint32_t *p = buf;
+
+ /* Setup argument. arg1 is a 16-bit value. */
+ p += gen_limm (p, 3, (uint32_t) arg1, 0);
+ p += gen_call (p, fn, 0, 0);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ {
+ EMIT_ASM ("mr 5, 4\n"
+ "mr 4, 3\n"
+ "mr 3, 5\n");
+ }
+}
+
+/* FN's prototype is `void(*fn)(int,LONGEST)'.
+ fn (arg1, TOP)
+
+ TOP should be preserved/restored before/after the call. */
+
+static void
+ppc_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[21];
+ uint32_t *p = buf;
+
+ /* Save TOP. 0(30) is next-empty. */
+ p += GEN_STW (p, 3, 30, 0);
+ p += GEN_STW (p, 4, 30, 4);
+
+ /* Setup argument. arg1 is a 16-bit value. */
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ {
+ p += GEN_MR (p, 5, 4);
+ p += GEN_MR (p, 6, 3);
+ }
+ else
+ {
+ p += GEN_MR (p, 5, 3);
+ p += GEN_MR (p, 6, 4);
+ }
+ p += gen_limm (p, 3, (uint32_t) arg1, 0);
+ p += gen_call (p, fn, 0, 0);
+
+ /* Restore TOP */
+ p += GEN_LWZ (p, 3, 30, 0);
+ p += GEN_LWZ (p, 4, 30, 4);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (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
+ppc_emit_if_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM ("or. 3, 3, 4 \n"
+ "lwzu " TOP_FIRST ", 8(30) \n"
+ "lwz " TOP_SECOND ", 4(30) \n"
+ "1:bne 0, 1b \n");
+
+ if (offset_p)
+ *offset_p = 12;
+ if (size_p)
+ *size_p = 14;
+}
+
+/* Unconditional goto. Also used for ppc64. */
+
+static void
+ppc_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
+ppc_emit_eq_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "xor 4, 6, 4 \n"
+ "xor 3, 5, 3 \n"
+ "or. 3, 3, 4 \n"
+ "lwzu " TOP_FIRST ", 8(30) \n"
+ "lwz " TOP_SECOND ", 4(30) \n"
+ "1:beq 0, 1b \n");
+
+ if (offset_p)
+ *offset_p = 28;
+ if (size_p)
+ *size_p = 14;
+}
+
+/* Goto if stack[--sp] != TOP */
+
+static void
+ppc_emit_ne_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "xor 4, 6, 4 \n"
+ "xor 3, 5, 3 \n"
+ "or. 3, 3, 4 \n"
+ "lwzu " TOP_FIRST ", 8(30) \n"
+ "lwz " TOP_SECOND ", 4(30) \n"
+ "1:bne 0, 1b \n");
+
+ if (offset_p)
+ *offset_p = 28;
+ if (size_p)
+ *size_p = 14;
+}
+
+/* Goto if stack[--sp] < TOP */
+
+static void
+ppc_emit_lt_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "cmplw 6, 6, 4 \n"
+ "cmpw 7, 5, 3 \n"
+ /* CR6 bit 0 = low less and high equal */
+ "crand 6*4+0, 6*4+0, 7*4+2\n"
+ /* CR7 bit 0 = (low less and high equal) or high less */
+ "cror 7*4+0, 7*4+0, 6*4+0\n"
+ "lwzu " TOP_FIRST ", 8(30) \n"
+ "lwz " TOP_SECOND ", 4(30)\n"
+ "1:blt 7, 1b \n");
+
+ if (offset_p)
+ *offset_p = 32;
+ if (size_p)
+ *size_p = 14;
+}
+
+/* Goto if stack[--sp] <= TOP */
+
+static void
+ppc_emit_le_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "cmplw 6, 6, 4 \n"
+ "cmpw 7, 5, 3 \n"
+ /* CR6 bit 0 = low less/equal and high equal */
+ "crandc 6*4+0, 7*4+2, 6*4+1\n"
+ /* CR7 bit 0 = (low less/eq and high equal) or high less */
+ "cror 7*4+0, 7*4+0, 6*4+0\n"
+ "lwzu " TOP_FIRST ", 8(30) \n"
+ "lwz " TOP_SECOND ", 4(30)\n"
+ "1:blt 7, 1b \n");
+
+ if (offset_p)
+ *offset_p = 32;
+ if (size_p)
+ *size_p = 14;
+}
+
+/* Goto if stack[--sp] > TOP */
+
+static void
+ppc_emit_gt_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "cmplw 6, 6, 4 \n"
+ "cmpw 7, 5, 3 \n"
+ /* CR6 bit 0 = low greater and high equal */
+ "crand 6*4+0, 6*4+1, 7*4+2\n"
+ /* CR7 bit 0 = (low greater and high equal) or high greater */
+ "cror 7*4+0, 7*4+1, 6*4+0\n"
+ "lwzu " TOP_FIRST ", 8(30) \n"
+ "lwz " TOP_SECOND ", 4(30)\n"
+ "1:blt 7, 1b \n");
+
+ if (offset_p)
+ *offset_p = 32;
+ if (size_p)
+ *size_p = 14;
+}
+
+/* Goto if stack[--sp] >= TOP */
+
+static void
+ppc_emit_ge_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
+ "lwz " TMP_SECOND ", 4(30) \n"
+ "cmplw 6, 6, 4 \n"
+ "cmpw 7, 5, 3 \n"
+ /* CR6 bit 0 = low ge and high equal */
+ "crandc 6*4+0, 7*4+2, 6*4+0\n"
+ /* CR7 bit 0 = (low ge and high equal) or high greater */
+ "cror 7*4+0, 7*4+1, 6*4+0\n"
+ "lwzu " TOP_FIRST ", 8(30)\n"
+ "lwz " TOP_SECOND ", 4(30)\n"
+ "1:blt 7, 1b \n");
+
+ if (offset_p)
+ *offset_p = 32;
+ 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.
+ Also used for ppc64. */
+
+static void
+ppc_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ long 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)
+ target_write_memory (from, (unsigned char *) &insn, 4);
+}
+
+/* Table of emit ops for 32-bit. */
+
+static struct emit_ops ppc_emit_ops_impl =
+{
+ ppc_emit_prologue,
+ ppc_emit_epilogue,
+ ppc_emit_add,
+ ppc_emit_sub,
+ ppc_emit_mul,
+ ppc_emit_lsh,
+ ppc_emit_rsh_signed,
+ ppc_emit_rsh_unsigned,
+ ppc_emit_ext,
+ ppc_emit_log_not,
+ ppc_emit_bit_and,
+ ppc_emit_bit_or,
+ ppc_emit_bit_xor,
+ ppc_emit_bit_not,
+ ppc_emit_equal,
+ ppc_emit_less_signed,
+ ppc_emit_less_unsigned,
+ ppc_emit_ref,
+ ppc_emit_if_goto,
+ ppc_emit_goto,
+ ppc_write_goto_address,
+ ppc_emit_const,
+ ppc_emit_call,
+ ppc_emit_reg,
+ ppc_emit_pop,
+ ppc_emit_stack_flush,
+ ppc_emit_zero_ext,
+ ppc_emit_swap,
+ ppc_emit_stack_adjust,
+ ppc_emit_int_call_1,
+ ppc_emit_void_call_2,
+ ppc_emit_eq_goto,
+ ppc_emit_ne_goto,
+ ppc_emit_lt_goto,
+ ppc_emit_le_goto,
+ ppc_emit_gt_goto,
+ ppc_emit_ge_goto
+};
+
+#ifdef __powerpc64__
+
+/*
+
+ Bytecode execution stack frame - 64-bit
+
+ | 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
+ * 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
+ppc64v1_emit_prologue (void)
+{
+ /* On ELFv1, function pointers really point to function descriptor,
+ so emit one here. We don't care about contents of words 1 and 2,
+ so let them just overlap out code. */
+ uint64_t opd = current_insn_ptr + 8;
+ uint32_t buf[2];
+
+ /* Mind the strict aliasing rules. */
+ memcpy (buf, &opd, sizeof buf);
+ emit_insns(buf, 2);
+ 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 prologue in inferior memory. See above comments. */
+
+static void
+ppc64v2_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"
+ "mfcr 3 \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"
+ "mfcr 3 \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, 1);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (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
+ppc64v1_emit_reg (int reg)
+{
+ uint32_t buf[15];
+ uint32_t *p = buf;
+
+ /* fctx->regs is passed in r3 and then saved in 176(1). */
+ p += GEN_LD (p, 3, 31, -32);
+ p += GEN_LI (p, 4, reg);
+ p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
+ p += gen_call (p, get_raw_reg_func_addr (), 1, 1);
+ p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* Likewise, for ELFv2. */
+
+static void
+ppc64v2_emit_reg (int reg)
+{
+ uint32_t buf[12];
+ uint32_t *p = buf;
+
+ /* fctx->regs is passed in r3 and then saved in 176(1). */
+ p += GEN_LD (p, 3, 31, -32);
+ p += GEN_LI (p, 4, reg);
+ p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
+ p += gen_call (p, get_raw_reg_func_addr (), 1, 0);
+ p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (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"
+ " stdu 31, -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");
+}
+
+/* Call function FN - ELFv1. */
+
+static void
+ppc64v1_emit_call (CORE_ADDR fn)
+{
+ uint32_t buf[13];
+ uint32_t *p = buf;
+
+ p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
+ p += gen_call (p, fn, 1, 1);
+ p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* Call function FN - ELFv2. */
+
+static void
+ppc64v2_emit_call (CORE_ADDR fn)
+{
+ uint32_t buf[10];
+ uint32_t *p = buf;
+
+ p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
+ p += gen_call (p, fn, 1, 0);
+ p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'.
+ TOP = fn (arg1)
+ */
+
+static void
+ppc64v1_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[13];
+ uint32_t *p = buf;
+
+ /* Setup argument. arg1 is a 16-bit value. */
+ p += gen_limm (p, 3, arg1, 1);
+ p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
+ p += gen_call (p, fn, 1, 1);
+ p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* Likewise for ELFv2. */
+
+static void
+ppc64v2_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[10];
+ uint32_t *p = buf;
+
+ /* Setup argument. arg1 is a 16-bit value. */
+ p += gen_limm (p, 3, arg1, 1);
+ p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
+ p += gen_call (p, fn, 1, 0);
+ p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (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
+ppc64v1_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[17];
+ uint32_t *p = buf;
+
+ /* Save TOP. 0(30) is next-empty. */
+ p += GEN_STD (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, 1);
+ p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
+ p += gen_call (p, fn, 1, 1);
+ p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
+
+ /* Restore TOP */
+ p += GEN_LD (p, 3, 30, 0);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* Likewise for ELFv2. */
+
+static void
+ppc64v2_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ uint32_t buf[14];
+ uint32_t *p = buf;
+
+ /* Save TOP. 0(30) is next-empty. */
+ p += GEN_STD (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, 1);
+ p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
+ p += gen_call (p, fn, 1, 0);
+ p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
+
+ /* Restore TOP */
+ p += GEN_LD (p, 3, 30, 0);
+
+ emit_insns (buf, p - buf);
+ gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
+}
+
+/* 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 = 8;
+ if (size_p)
+ *size_p = 14;
+}
+
+/* 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;
+}
+
+/* Table of emit ops for 64-bit ELFv1. */
+
+static struct emit_ops ppc64v1_emit_ops_impl =
+{
+ ppc64v1_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,
+ ppc_emit_goto,
+ ppc_write_goto_address,
+ ppc64_emit_const,
+ ppc64v1_emit_call,
+ ppc64v1_emit_reg,
+ ppc64_emit_pop,
+ ppc64_emit_stack_flush,
+ ppc64_emit_zero_ext,
+ ppc64_emit_swap,
+ ppc_emit_stack_adjust,
+ ppc64v1_emit_int_call_1,
+ ppc64v1_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
+};
+
+/* Table of emit ops for 64-bit ELFv2. */
+
+static struct emit_ops ppc64v2_emit_ops_impl =
+{
+ ppc64v2_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,
+ ppc_emit_goto,
+ ppc_write_goto_address,
+ ppc64_emit_const,
+ ppc64v2_emit_call,
+ ppc64v2_emit_reg,
+ ppc64_emit_pop,
+ ppc64_emit_stack_flush,
+ ppc64_emit_zero_ext,
+ ppc64_emit_swap,
+ ppc_emit_stack_adjust,
+ ppc64v2_emit_int_call_1,
+ ppc64v2_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
+};
+
+#endif
+
+/* Implementation of linux_target_ops method "emit_ops". */
+
+static struct emit_ops *
+ppc_emit_ops (void)
+{
+#ifdef __powerpc64__
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+
+ if (register_size (regcache->tdesc, 0) == 8)
+ {
+ if (is_elfv2_inferior ())
+ return &ppc64v2_emit_ops_impl;
+ else
+ return &ppc64v1_emit_ops_impl;
+ }
+#endif
+ return &ppc_emit_ops_impl;
+}
+
+/* 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_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;
+ if (tdesc == tdesc_powerpc_isa205_ppr_dscr_vsx64l)
+ return PPC_TDESC_ISA205_PPR_DSCR_VSX;
+ if (tdesc == tdesc_powerpc_isa207_vsx64l)
+ return PPC_TDESC_ISA207_VSX;
+ if (tdesc == tdesc_powerpc_isa207_htm_vsx64l)
+ return PPC_TDESC_ISA207_HTM_VSX;
+#endif
+
+ if (tdesc == tdesc_powerpc_32l)
+ return PPC_TDESC_BASE;
+ if (tdesc == tdesc_powerpc_altivec32l)
+ return PPC_TDESC_ALTIVEC;
+ 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_isa205_ppr_dscr_vsx32l)
+ return PPC_TDESC_ISA205_PPR_DSCR_VSX;
+ if (tdesc == tdesc_powerpc_isa207_vsx32l)
+ return PPC_TDESC_ISA207_VSX;
+ if (tdesc == tdesc_powerpc_isa207_htm_vsx32l)
+ return PPC_TDESC_ISA207_HTM_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,
+ ppc_cannot_fetch_register,
+ ppc_cannot_store_register,
+ NULL, /* fetch_register */
+ ppc_get_pc,
+ ppc_set_pc,
+ NULL, /* breakpoint_kind_from_pc */
+ ppc_sw_breakpoint_from_kind,
+ NULL,
+ 0,
+ ppc_breakpoint_at,
+ ppc_supports_z_point_type,
+ ppc_insert_point,
+ ppc_remove_point,
+ NULL,
+ NULL,
+ ppc_collect_ptrace_register,
+ ppc_supply_ptrace_register,
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ ppc_supports_tracepoints,
+ ppc_get_thread_area,
+ ppc_install_fast_tracepoint_jump_pad,
+ ppc_emit_ops,
+ 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
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+
+ init_registers_powerpc_32l ();
+ init_registers_powerpc_altivec32l ();
+ init_registers_powerpc_vsx32l ();
+ init_registers_powerpc_isa205_32l ();
+ init_registers_powerpc_isa205_altivec32l ();
+ init_registers_powerpc_isa205_vsx32l ();
+ init_registers_powerpc_isa205_ppr_dscr_vsx32l ();
+ init_registers_powerpc_isa207_vsx32l ();
+ init_registers_powerpc_isa207_htm_vsx32l ();
+ init_registers_powerpc_e500l ();
+#if __powerpc64__
+ init_registers_powerpc_64l ();
+ init_registers_powerpc_altivec64l ();
+ init_registers_powerpc_vsx64l ();
+ init_registers_powerpc_isa205_64l ();
+ init_registers_powerpc_isa205_altivec64l ();
+ init_registers_powerpc_isa205_vsx64l ();
+ init_registers_powerpc_isa205_ppr_dscr_vsx64l ();
+ init_registers_powerpc_isa207_vsx64l ();
+ init_registers_powerpc_isa207_htm_vsx64l ();
+#endif
+
+ initialize_regsets_info (&ppc_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux S/390 specific low level interface, for the in-process
- agent library for GDB.
-
- Copyright (C) 2016-2020 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-s390-tdesc.h"
-#include <elf.h>
-#ifdef HAVE_GETAUXVAL
-#include <sys/auxv.h>
-#endif
-
-#define FT_FPR(x) (0x000 + (x) * 0x10)
-#define FT_VR(x) (0x000 + (x) * 0x10)
-#define FT_VR_L(x) (0x008 + (x) * 0x10)
-#define FT_GPR(x) (0x200 + (x) * 8)
-#define FT_GPR_U(x) (0x200 + (x) * 8)
-#define FT_GPR_L(x) (0x204 + (x) * 8)
-#define FT_GPR(x) (0x200 + (x) * 8)
-#define FT_ACR(x) (0x280 + (x) * 4)
-#define FT_PSWM 0x2c0
-#define FT_PSWM_U 0x2c0
-#define FT_PSWA 0x2c8
-#define FT_PSWA_L 0x2cc
-#define FT_FPC 0x2d0
-
-/* Mappings between registers collected by the jump pad and GDB's register
- array layout used by regcache.
-
- See linux-s390-low.c (s390_install_fast_tracepoint_jump_pad) for more
- details. */
-
-#ifndef __s390x__
-
-/* Used for s390-linux32, s390-linux32v1, s390-linux32v2. */
-
-static const int s390_linux32_ft_collect_regmap[] = {
- /* 32-bit PSWA and PSWM. */
- FT_PSWM_U, FT_PSWA_L,
- /* 32-bit GPRs (mapped to lower halves of 64-bit slots). */
- FT_GPR_L (0), FT_GPR_L (1), FT_GPR_L (2), FT_GPR_L (3),
- FT_GPR_L (4), FT_GPR_L (5), FT_GPR_L (6), FT_GPR_L (7),
- FT_GPR_L (8), FT_GPR_L (9), FT_GPR_L (10), FT_GPR_L (11),
- FT_GPR_L (12), FT_GPR_L (13), FT_GPR_L (14), FT_GPR_L (15),
- /* ACRs */
- FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
- FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
- FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
- FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
- /* FPRs (mapped to upper halves of 128-bit VR slots). */
- FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
- FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
- FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
- FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
- /* orig_r2, last_break, system_call */
- -1, -1, -1,
-};
-
-/* Used for s390-linux64, s390-linux64v1, s390-linux64v2, s390-vx-linux64. */
-
-static const int s390_linux64_ft_collect_regmap[] = {
- /* 32-bit PSWA and PSWM. */
- FT_PSWM_U, FT_PSWA_L,
- /* 32-bit halves of 64-bit GPRs. */
- FT_GPR_U (0), FT_GPR_L (0),
- FT_GPR_U (1), FT_GPR_L (1),
- FT_GPR_U (2), FT_GPR_L (2),
- FT_GPR_U (3), FT_GPR_L (3),
- FT_GPR_U (4), FT_GPR_L (4),
- FT_GPR_U (5), FT_GPR_L (5),
- FT_GPR_U (6), FT_GPR_L (6),
- FT_GPR_U (7), FT_GPR_L (7),
- FT_GPR_U (8), FT_GPR_L (8),
- FT_GPR_U (9), FT_GPR_L (9),
- FT_GPR_U (10), FT_GPR_L (10),
- FT_GPR_U (11), FT_GPR_L (11),
- FT_GPR_U (12), FT_GPR_L (12),
- FT_GPR_U (13), FT_GPR_L (13),
- FT_GPR_U (14), FT_GPR_L (14),
- FT_GPR_U (15), FT_GPR_L (15),
- /* ACRs */
- FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
- FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
- FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
- FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
- /* FPRs (mapped to upper halves of 128-bit VR slots). */
- FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
- FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
- FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
- FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
- /* orig_r2, last_break, system_call */
- -1, -1, -1,
- /* Lower halves of 128-bit VRs. */
- FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
- FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
- FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
- FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
- /* And the next 16 VRs. */
- FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
- FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
- FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
- FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
-};
-
-/* Used for s390-te-linux64, s390-tevx-linux64, and s390-gs-linux64. */
-
-static const int s390_te_linux64_ft_collect_regmap[] = {
- /* 32-bit PSWA and PSWM. */
- FT_PSWM_U, FT_PSWA_L,
- /* 32-bit halves of 64-bit GPRs. */
- FT_GPR_U (0), FT_GPR_L (0),
- FT_GPR_U (1), FT_GPR_L (1),
- FT_GPR_U (2), FT_GPR_L (2),
- FT_GPR_U (3), FT_GPR_L (3),
- FT_GPR_U (4), FT_GPR_L (4),
- FT_GPR_U (5), FT_GPR_L (5),
- FT_GPR_U (6), FT_GPR_L (6),
- FT_GPR_U (7), FT_GPR_L (7),
- FT_GPR_U (8), FT_GPR_L (8),
- FT_GPR_U (9), FT_GPR_L (9),
- FT_GPR_U (10), FT_GPR_L (10),
- FT_GPR_U (11), FT_GPR_L (11),
- FT_GPR_U (12), FT_GPR_L (12),
- FT_GPR_U (13), FT_GPR_L (13),
- FT_GPR_U (14), FT_GPR_L (14),
- FT_GPR_U (15), FT_GPR_L (15),
- /* ACRs */
- FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
- FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
- FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
- FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
- /* FPRs (mapped to upper halves of 128-bit VR slots). */
- FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
- FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
- FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
- FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
- /* orig_r2, last_break, system_call */
- -1, -1, -1,
- /* TDB */
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- /* Lower halves of 128-bit VRs. */
- FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
- FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
- FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
- FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
- /* And the next 16 VRs. */
- FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
- FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
- FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
- FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
-};
-
-#else /* __s390x__ */
-
-/* Used for s390x-linux64, s390x-linux64v1, s390x-linux64v2, s390x-vx-linux64. */
-
-static const int s390x_ft_collect_regmap[] = {
- /* 64-bit PSWA and PSWM. */
- FT_PSWM, FT_PSWA,
- /* 64-bit GPRs. */
- FT_GPR (0), FT_GPR (1), FT_GPR (2), FT_GPR (3),
- FT_GPR (4), FT_GPR (5), FT_GPR (6), FT_GPR (7),
- FT_GPR (8), FT_GPR (9), FT_GPR (10), FT_GPR (11),
- FT_GPR (12), FT_GPR (13), FT_GPR (14), FT_GPR (15),
- /* ACRs */
- FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
- FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
- FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
- FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
- /* FPRs (mapped to upper halves of 128-bit VR slots). */
- FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
- FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
- FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
- FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
- /* orig_r2, last_break, system_call */
- -1, -1, -1,
- /* Lower halves of 128-bit VRs. */
- FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
- FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
- FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
- FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
- /* And the next 16 VRs. */
- FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
- FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
- FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
- FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
-};
-
-/* Used for s390x-te-linux64, s390x-tevx-linux64, and
- s390x-gs-linux64. */
-
-static const int s390x_te_ft_collect_regmap[] = {
- /* 64-bit PSWA and PSWM. */
- FT_PSWM, FT_PSWA,
- /* 64-bit GPRs. */
- FT_GPR (0), FT_GPR (1), FT_GPR (2), FT_GPR (3),
- FT_GPR (4), FT_GPR (5), FT_GPR (6), FT_GPR (7),
- FT_GPR (8), FT_GPR (9), FT_GPR (10), FT_GPR (11),
- FT_GPR (12), FT_GPR (13), FT_GPR (14), FT_GPR (15),
- /* ACRs */
- FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
- FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
- FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
- FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
- /* FPRs (mapped to upper halves of 128-bit VR slots). */
- FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
- FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
- FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
- FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
- /* orig_r2, last_break, system_call */
- -1, -1, -1,
- /* TDB */
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- /* Lower halves of 128-bit VRs. */
- FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
- FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
- FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
- FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
- /* And the next 16 VRs. */
- FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
- FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
- FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
- FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
-};
-
-#endif
-
-/* Initialized by get_ipa_tdesc according to the tdesc in use. */
-
-static const int *s390_regmap;
-static int s390_regnum;
-
-/* Fill in REGCACHE with registers saved by the jump pad in BUF. */
-
-void
-supply_fast_tracepoint_registers (struct regcache *regcache,
- const unsigned char *buf)
-{
- int i;
- for (i = 0; i < s390_regnum; i++)
- if (s390_regmap[i] != -1)
- supply_register (regcache, i, ((char *) buf) + s390_regmap[i]);
-}
-
-ULONGEST
-get_raw_reg (const unsigned char *raw_regs, int regnum)
-{
- int offset;
- if (regnum >= s390_regnum)
- return 0;
- offset = s390_regmap[regnum];
- if (offset == -1)
- return 0;
-
- /* The regnums are variable, better to figure out size by FT offset. */
-
- /* 64-bit ones. */
- if (offset < FT_VR(16)
-#ifdef __s390x__
- || (offset >= FT_GPR(0) && offset < FT_ACR(0))
- || offset == FT_PSWM
- || offset == FT_PSWA
-#endif
- )
- return *(uint64_t *) (raw_regs + offset);
-
- if (offset >= FT_ACR(0 && offset < FT_PSWM)
- || offset == FT_FPC
-#ifndef __s390x__
- || (offset >= FT_GPR(0) && offset < FT_ACR(0))
- || offset == FT_PSWM_U
- || offset == FT_PSWA_L
-#endif
- )
- return *(uint32_t *) (raw_regs + offset);
-
- /* This leaves 128-bit VX. No way to return them. */
- return 0;
-}
-
-/* Return target_desc to use for IPA, given the tdesc index passed by
- gdbserver. For s390, it also sets s390_regmap and s390_regnum. */
-
-const struct target_desc *
-get_ipa_tdesc (int idx)
-{
-#define SET_REGMAP(regmap, skip_last) \
- do { \
- s390_regmap = regmap; \
- s390_regnum = (sizeof regmap / sizeof regmap[0]) - skip_last; \
- } while(0)
- switch (idx)
- {
-#ifdef __s390x__
- case S390_TDESC_64:
- /* Subtract number of VX regs. */
- SET_REGMAP(s390x_ft_collect_regmap, 32);
- return tdesc_s390x_linux64;
- case S390_TDESC_64V1:
- SET_REGMAP(s390x_ft_collect_regmap, 32);
- return tdesc_s390x_linux64v1;
- case S390_TDESC_64V2:
- SET_REGMAP(s390x_ft_collect_regmap, 32);
- return tdesc_s390x_linux64v2;
- case S390_TDESC_TE:
- SET_REGMAP(s390x_te_ft_collect_regmap, 32);
- return tdesc_s390x_te_linux64;
- case S390_TDESC_VX:
- SET_REGMAP(s390x_ft_collect_regmap, 0);
- return tdesc_s390x_vx_linux64;
- case S390_TDESC_TEVX:
- SET_REGMAP(s390x_te_ft_collect_regmap, 0);
- return tdesc_s390x_tevx_linux64;
- case S390_TDESC_GS:
- SET_REGMAP(s390x_te_ft_collect_regmap, 0);
- return tdesc_s390x_gs_linux64;
-#else
- case S390_TDESC_32:
- SET_REGMAP(s390_linux32_ft_collect_regmap, 0);
- return tdesc_s390_linux32;
- case S390_TDESC_32V1:
- SET_REGMAP(s390_linux32_ft_collect_regmap, 0);
- return tdesc_s390_linux32v1;
- case S390_TDESC_32V2:
- SET_REGMAP(s390_linux32_ft_collect_regmap, 0);
- return tdesc_s390_linux32v2;
- case S390_TDESC_64:
- SET_REGMAP(s390_linux64_ft_collect_regmap, 32);
- return tdesc_s390_linux64;
- case S390_TDESC_64V1:
- SET_REGMAP(s390_linux64_ft_collect_regmap, 32);
- return tdesc_s390_linux64v1;
- case S390_TDESC_64V2:
- SET_REGMAP(s390_linux64_ft_collect_regmap, 32);
- return tdesc_s390_linux64v2;
- case S390_TDESC_TE:
- SET_REGMAP(s390_te_linux64_ft_collect_regmap, 32);
- return tdesc_s390_te_linux64;
- case S390_TDESC_VX:
- SET_REGMAP(s390_linux64_ft_collect_regmap, 0);
- return tdesc_s390_vx_linux64;
- case S390_TDESC_TEVX:
- SET_REGMAP(s390_te_linux64_ft_collect_regmap, 0);
- return tdesc_s390_tevx_linux64;
- case S390_TDESC_GS:
- SET_REGMAP(s390_te_linux64_ft_collect_regmap, 0);
- return tdesc_s390_gs_linux64;
-#endif
- default:
- internal_error (__FILE__, __LINE__,
- "unknown ipa tdesc index: %d", idx);
-#ifdef __s390x__
- return tdesc_s390x_linux64;
-#else
- return tdesc_s390_linux32;
-#endif
- }
-}
-
-/* Allocate buffer for the jump pads. On 31-bit, JG reaches everywhere,
- so just allocate normally. On 64-bit, we have +/-4GiB of reach, and
- the executable is usually mapped at 0x80000000 - aim for somewhere
- below it. */
-
-void *
-alloc_jump_pad_buffer (size_t size)
-{
-#ifdef __s390x__
- uintptr_t addr;
- uintptr_t exec_base = getauxval (AT_PHDR);
- int pagesize;
- void *res;
-
- if (exec_base == 0)
- exec_base = 0x80000000;
-
- 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 *res = mmap (NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-
- if (res == MAP_FAILED)
- return NULL;
-
- return res;
-#endif
-}
-
-void
-initialize_low_tracepoint (void)
-{
-#ifdef __s390x__
- init_registers_s390x_linux64 ();
- init_registers_s390x_linux64v1 ();
- init_registers_s390x_linux64v2 ();
- init_registers_s390x_te_linux64 ();
- init_registers_s390x_vx_linux64 ();
- init_registers_s390x_tevx_linux64 ();
- init_registers_s390x_gs_linux64 ();
-#else
- init_registers_s390_linux32 ();
- init_registers_s390_linux32v1 ();
- init_registers_s390_linux32v2 ();
- init_registers_s390_linux64 ();
- init_registers_s390_linux64v1 ();
- init_registers_s390_linux64v2 ();
- init_registers_s390_te_linux64 ();
- init_registers_s390_vx_linux64 ();
- init_registers_s390_tevx_linux64 ();
- init_registers_s390_gs_linux64 ();
-#endif
-}
--- /dev/null
+/* GNU/Linux S/390 specific low level interface, for the in-process
+ agent library for GDB.
+
+ Copyright (C) 2016-2020 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-s390-tdesc.h"
+#include <elf.h>
+#ifdef HAVE_GETAUXVAL
+#include <sys/auxv.h>
+#endif
+
+#define FT_FPR(x) (0x000 + (x) * 0x10)
+#define FT_VR(x) (0x000 + (x) * 0x10)
+#define FT_VR_L(x) (0x008 + (x) * 0x10)
+#define FT_GPR(x) (0x200 + (x) * 8)
+#define FT_GPR_U(x) (0x200 + (x) * 8)
+#define FT_GPR_L(x) (0x204 + (x) * 8)
+#define FT_GPR(x) (0x200 + (x) * 8)
+#define FT_ACR(x) (0x280 + (x) * 4)
+#define FT_PSWM 0x2c0
+#define FT_PSWM_U 0x2c0
+#define FT_PSWA 0x2c8
+#define FT_PSWA_L 0x2cc
+#define FT_FPC 0x2d0
+
+/* Mappings between registers collected by the jump pad and GDB's register
+ array layout used by regcache.
+
+ See linux-s390-low.c (s390_install_fast_tracepoint_jump_pad) for more
+ details. */
+
+#ifndef __s390x__
+
+/* Used for s390-linux32, s390-linux32v1, s390-linux32v2. */
+
+static const int s390_linux32_ft_collect_regmap[] = {
+ /* 32-bit PSWA and PSWM. */
+ FT_PSWM_U, FT_PSWA_L,
+ /* 32-bit GPRs (mapped to lower halves of 64-bit slots). */
+ FT_GPR_L (0), FT_GPR_L (1), FT_GPR_L (2), FT_GPR_L (3),
+ FT_GPR_L (4), FT_GPR_L (5), FT_GPR_L (6), FT_GPR_L (7),
+ FT_GPR_L (8), FT_GPR_L (9), FT_GPR_L (10), FT_GPR_L (11),
+ FT_GPR_L (12), FT_GPR_L (13), FT_GPR_L (14), FT_GPR_L (15),
+ /* ACRs */
+ FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
+ FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
+ FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
+ FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
+ /* FPRs (mapped to upper halves of 128-bit VR slots). */
+ FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
+ FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
+ FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
+ FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
+ /* orig_r2, last_break, system_call */
+ -1, -1, -1,
+};
+
+/* Used for s390-linux64, s390-linux64v1, s390-linux64v2, s390-vx-linux64. */
+
+static const int s390_linux64_ft_collect_regmap[] = {
+ /* 32-bit PSWA and PSWM. */
+ FT_PSWM_U, FT_PSWA_L,
+ /* 32-bit halves of 64-bit GPRs. */
+ FT_GPR_U (0), FT_GPR_L (0),
+ FT_GPR_U (1), FT_GPR_L (1),
+ FT_GPR_U (2), FT_GPR_L (2),
+ FT_GPR_U (3), FT_GPR_L (3),
+ FT_GPR_U (4), FT_GPR_L (4),
+ FT_GPR_U (5), FT_GPR_L (5),
+ FT_GPR_U (6), FT_GPR_L (6),
+ FT_GPR_U (7), FT_GPR_L (7),
+ FT_GPR_U (8), FT_GPR_L (8),
+ FT_GPR_U (9), FT_GPR_L (9),
+ FT_GPR_U (10), FT_GPR_L (10),
+ FT_GPR_U (11), FT_GPR_L (11),
+ FT_GPR_U (12), FT_GPR_L (12),
+ FT_GPR_U (13), FT_GPR_L (13),
+ FT_GPR_U (14), FT_GPR_L (14),
+ FT_GPR_U (15), FT_GPR_L (15),
+ /* ACRs */
+ FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
+ FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
+ FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
+ FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
+ /* FPRs (mapped to upper halves of 128-bit VR slots). */
+ FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
+ FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
+ FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
+ FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
+ /* orig_r2, last_break, system_call */
+ -1, -1, -1,
+ /* Lower halves of 128-bit VRs. */
+ FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
+ FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
+ FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
+ FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
+ /* And the next 16 VRs. */
+ FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
+ FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
+ FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
+ FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
+};
+
+/* Used for s390-te-linux64, s390-tevx-linux64, and s390-gs-linux64. */
+
+static const int s390_te_linux64_ft_collect_regmap[] = {
+ /* 32-bit PSWA and PSWM. */
+ FT_PSWM_U, FT_PSWA_L,
+ /* 32-bit halves of 64-bit GPRs. */
+ FT_GPR_U (0), FT_GPR_L (0),
+ FT_GPR_U (1), FT_GPR_L (1),
+ FT_GPR_U (2), FT_GPR_L (2),
+ FT_GPR_U (3), FT_GPR_L (3),
+ FT_GPR_U (4), FT_GPR_L (4),
+ FT_GPR_U (5), FT_GPR_L (5),
+ FT_GPR_U (6), FT_GPR_L (6),
+ FT_GPR_U (7), FT_GPR_L (7),
+ FT_GPR_U (8), FT_GPR_L (8),
+ FT_GPR_U (9), FT_GPR_L (9),
+ FT_GPR_U (10), FT_GPR_L (10),
+ FT_GPR_U (11), FT_GPR_L (11),
+ FT_GPR_U (12), FT_GPR_L (12),
+ FT_GPR_U (13), FT_GPR_L (13),
+ FT_GPR_U (14), FT_GPR_L (14),
+ FT_GPR_U (15), FT_GPR_L (15),
+ /* ACRs */
+ FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
+ FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
+ FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
+ FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
+ /* FPRs (mapped to upper halves of 128-bit VR slots). */
+ FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
+ FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
+ FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
+ FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
+ /* orig_r2, last_break, system_call */
+ -1, -1, -1,
+ /* TDB */
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /* Lower halves of 128-bit VRs. */
+ FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
+ FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
+ FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
+ FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
+ /* And the next 16 VRs. */
+ FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
+ FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
+ FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
+ FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
+};
+
+#else /* __s390x__ */
+
+/* Used for s390x-linux64, s390x-linux64v1, s390x-linux64v2, s390x-vx-linux64. */
+
+static const int s390x_ft_collect_regmap[] = {
+ /* 64-bit PSWA and PSWM. */
+ FT_PSWM, FT_PSWA,
+ /* 64-bit GPRs. */
+ FT_GPR (0), FT_GPR (1), FT_GPR (2), FT_GPR (3),
+ FT_GPR (4), FT_GPR (5), FT_GPR (6), FT_GPR (7),
+ FT_GPR (8), FT_GPR (9), FT_GPR (10), FT_GPR (11),
+ FT_GPR (12), FT_GPR (13), FT_GPR (14), FT_GPR (15),
+ /* ACRs */
+ FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
+ FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
+ FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
+ FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
+ /* FPRs (mapped to upper halves of 128-bit VR slots). */
+ FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
+ FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
+ FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
+ FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
+ /* orig_r2, last_break, system_call */
+ -1, -1, -1,
+ /* Lower halves of 128-bit VRs. */
+ FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
+ FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
+ FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
+ FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
+ /* And the next 16 VRs. */
+ FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
+ FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
+ FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
+ FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
+};
+
+/* Used for s390x-te-linux64, s390x-tevx-linux64, and
+ s390x-gs-linux64. */
+
+static const int s390x_te_ft_collect_regmap[] = {
+ /* 64-bit PSWA and PSWM. */
+ FT_PSWM, FT_PSWA,
+ /* 64-bit GPRs. */
+ FT_GPR (0), FT_GPR (1), FT_GPR (2), FT_GPR (3),
+ FT_GPR (4), FT_GPR (5), FT_GPR (6), FT_GPR (7),
+ FT_GPR (8), FT_GPR (9), FT_GPR (10), FT_GPR (11),
+ FT_GPR (12), FT_GPR (13), FT_GPR (14), FT_GPR (15),
+ /* ACRs */
+ FT_ACR (0), FT_ACR (1), FT_ACR (2), FT_ACR (3),
+ FT_ACR (4), FT_ACR (5), FT_ACR (6), FT_ACR (7),
+ FT_ACR (8), FT_ACR (9), FT_ACR (10), FT_ACR (11),
+ FT_ACR (12), FT_ACR (13), FT_ACR (14), FT_ACR (15),
+ /* FPRs (mapped to upper halves of 128-bit VR slots). */
+ FT_FPR (0), FT_FPR (1), FT_FPR (2), FT_FPR (3),
+ FT_FPR (4), FT_FPR (5), FT_FPR (6), FT_FPR (7),
+ FT_FPR (8), FT_FPR (9), FT_FPR (10), FT_FPR (11),
+ FT_FPR (12), FT_FPR (13), FT_FPR (14), FT_FPR (15),
+ /* orig_r2, last_break, system_call */
+ -1, -1, -1,
+ /* TDB */
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /* Lower halves of 128-bit VRs. */
+ FT_VR_L (0), FT_VR_L (1), FT_VR_L (2), FT_VR_L (3),
+ FT_VR_L (4), FT_VR_L (5), FT_VR_L (6), FT_VR_L (7),
+ FT_VR_L (8), FT_VR_L (9), FT_VR_L (10), FT_VR_L (11),
+ FT_VR_L (12), FT_VR_L (13), FT_VR_L (14), FT_VR_L (15),
+ /* And the next 16 VRs. */
+ FT_VR (16), FT_VR (17), FT_VR (18), FT_VR (19),
+ FT_VR (20), FT_VR (21), FT_VR (22), FT_VR (23),
+ FT_VR (24), FT_VR (25), FT_VR (26), FT_VR (27),
+ FT_VR (28), FT_VR (29), FT_VR (30), FT_VR (31),
+};
+
+#endif
+
+/* Initialized by get_ipa_tdesc according to the tdesc in use. */
+
+static const int *s390_regmap;
+static int s390_regnum;
+
+/* Fill in REGCACHE with registers saved by the jump pad in BUF. */
+
+void
+supply_fast_tracepoint_registers (struct regcache *regcache,
+ const unsigned char *buf)
+{
+ int i;
+ for (i = 0; i < s390_regnum; i++)
+ if (s390_regmap[i] != -1)
+ supply_register (regcache, i, ((char *) buf) + s390_regmap[i]);
+}
+
+ULONGEST
+get_raw_reg (const unsigned char *raw_regs, int regnum)
+{
+ int offset;
+ if (regnum >= s390_regnum)
+ return 0;
+ offset = s390_regmap[regnum];
+ if (offset == -1)
+ return 0;
+
+ /* The regnums are variable, better to figure out size by FT offset. */
+
+ /* 64-bit ones. */
+ if (offset < FT_VR(16)
+#ifdef __s390x__
+ || (offset >= FT_GPR(0) && offset < FT_ACR(0))
+ || offset == FT_PSWM
+ || offset == FT_PSWA
+#endif
+ )
+ return *(uint64_t *) (raw_regs + offset);
+
+ if (offset >= FT_ACR(0 && offset < FT_PSWM)
+ || offset == FT_FPC
+#ifndef __s390x__
+ || (offset >= FT_GPR(0) && offset < FT_ACR(0))
+ || offset == FT_PSWM_U
+ || offset == FT_PSWA_L
+#endif
+ )
+ return *(uint32_t *) (raw_regs + offset);
+
+ /* This leaves 128-bit VX. No way to return them. */
+ return 0;
+}
+
+/* Return target_desc to use for IPA, given the tdesc index passed by
+ gdbserver. For s390, it also sets s390_regmap and s390_regnum. */
+
+const struct target_desc *
+get_ipa_tdesc (int idx)
+{
+#define SET_REGMAP(regmap, skip_last) \
+ do { \
+ s390_regmap = regmap; \
+ s390_regnum = (sizeof regmap / sizeof regmap[0]) - skip_last; \
+ } while(0)
+ switch (idx)
+ {
+#ifdef __s390x__
+ case S390_TDESC_64:
+ /* Subtract number of VX regs. */
+ SET_REGMAP(s390x_ft_collect_regmap, 32);
+ return tdesc_s390x_linux64;
+ case S390_TDESC_64V1:
+ SET_REGMAP(s390x_ft_collect_regmap, 32);
+ return tdesc_s390x_linux64v1;
+ case S390_TDESC_64V2:
+ SET_REGMAP(s390x_ft_collect_regmap, 32);
+ return tdesc_s390x_linux64v2;
+ case S390_TDESC_TE:
+ SET_REGMAP(s390x_te_ft_collect_regmap, 32);
+ return tdesc_s390x_te_linux64;
+ case S390_TDESC_VX:
+ SET_REGMAP(s390x_ft_collect_regmap, 0);
+ return tdesc_s390x_vx_linux64;
+ case S390_TDESC_TEVX:
+ SET_REGMAP(s390x_te_ft_collect_regmap, 0);
+ return tdesc_s390x_tevx_linux64;
+ case S390_TDESC_GS:
+ SET_REGMAP(s390x_te_ft_collect_regmap, 0);
+ return tdesc_s390x_gs_linux64;
+#else
+ case S390_TDESC_32:
+ SET_REGMAP(s390_linux32_ft_collect_regmap, 0);
+ return tdesc_s390_linux32;
+ case S390_TDESC_32V1:
+ SET_REGMAP(s390_linux32_ft_collect_regmap, 0);
+ return tdesc_s390_linux32v1;
+ case S390_TDESC_32V2:
+ SET_REGMAP(s390_linux32_ft_collect_regmap, 0);
+ return tdesc_s390_linux32v2;
+ case S390_TDESC_64:
+ SET_REGMAP(s390_linux64_ft_collect_regmap, 32);
+ return tdesc_s390_linux64;
+ case S390_TDESC_64V1:
+ SET_REGMAP(s390_linux64_ft_collect_regmap, 32);
+ return tdesc_s390_linux64v1;
+ case S390_TDESC_64V2:
+ SET_REGMAP(s390_linux64_ft_collect_regmap, 32);
+ return tdesc_s390_linux64v2;
+ case S390_TDESC_TE:
+ SET_REGMAP(s390_te_linux64_ft_collect_regmap, 32);
+ return tdesc_s390_te_linux64;
+ case S390_TDESC_VX:
+ SET_REGMAP(s390_linux64_ft_collect_regmap, 0);
+ return tdesc_s390_vx_linux64;
+ case S390_TDESC_TEVX:
+ SET_REGMAP(s390_te_linux64_ft_collect_regmap, 0);
+ return tdesc_s390_tevx_linux64;
+ case S390_TDESC_GS:
+ SET_REGMAP(s390_te_linux64_ft_collect_regmap, 0);
+ return tdesc_s390_gs_linux64;
+#endif
+ default:
+ internal_error (__FILE__, __LINE__,
+ "unknown ipa tdesc index: %d", idx);
+#ifdef __s390x__
+ return tdesc_s390x_linux64;
+#else
+ return tdesc_s390_linux32;
+#endif
+ }
+}
+
+/* Allocate buffer for the jump pads. On 31-bit, JG reaches everywhere,
+ so just allocate normally. On 64-bit, we have +/-4GiB of reach, and
+ the executable is usually mapped at 0x80000000 - aim for somewhere
+ below it. */
+
+void *
+alloc_jump_pad_buffer (size_t size)
+{
+#ifdef __s390x__
+ uintptr_t addr;
+ uintptr_t exec_base = getauxval (AT_PHDR);
+ int pagesize;
+ void *res;
+
+ if (exec_base == 0)
+ exec_base = 0x80000000;
+
+ 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 *res = mmap (NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (res == MAP_FAILED)
+ return NULL;
+
+ return res;
+#endif
+}
+
+void
+initialize_low_tracepoint (void)
+{
+#ifdef __s390x__
+ init_registers_s390x_linux64 ();
+ init_registers_s390x_linux64v1 ();
+ init_registers_s390x_linux64v2 ();
+ init_registers_s390x_te_linux64 ();
+ init_registers_s390x_vx_linux64 ();
+ init_registers_s390x_tevx_linux64 ();
+ init_registers_s390x_gs_linux64 ();
+#else
+ init_registers_s390_linux32 ();
+ init_registers_s390_linux32v1 ();
+ init_registers_s390_linux32v2 ();
+ init_registers_s390_linux64 ();
+ init_registers_s390_linux64v1 ();
+ init_registers_s390_linux64v2 ();
+ init_registers_s390_te_linux64 ();
+ init_registers_s390_vx_linux64 ();
+ init_registers_s390_tevx_linux64 ();
+ init_registers_s390_gs_linux64 ();
+#endif
+}
+++ /dev/null
-/* GNU/Linux S/390 specific low level interface, for the remote server
- for GDB.
- Copyright (C) 2001-2020 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/>. */
-
-/* This file is used for both 31-bit and 64-bit S/390 systems. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "elf/common.h"
-#include "ax.h"
-#include "tracepoint.h"
-
-#include <asm/ptrace.h>
-#include "nat/gdb_ptrace.h"
-#include <sys/uio.h>
-#include <elf.h>
-#include <inttypes.h>
-
-#include "linux-s390-tdesc.h"
-
-#ifndef HWCAP_S390_HIGH_GPRS
-#define HWCAP_S390_HIGH_GPRS 512
-#endif
-
-#ifndef HWCAP_S390_TE
-#define HWCAP_S390_TE 1024
-#endif
-
-#ifndef HWCAP_S390_VX
-#define HWCAP_S390_VX 2048
-#endif
-
-#ifndef HWCAP_S390_GS
-#define HWCAP_S390_GS 16384
-#endif
-
-#define s390_num_regs 52
-
-static int s390_regmap[] = {
- PT_PSWMASK, PT_PSWADDR,
-
- PT_GPR0, PT_GPR1, PT_GPR2, PT_GPR3,
- PT_GPR4, PT_GPR5, PT_GPR6, PT_GPR7,
- PT_GPR8, PT_GPR9, PT_GPR10, PT_GPR11,
- PT_GPR12, PT_GPR13, PT_GPR14, PT_GPR15,
-
- PT_ACR0, PT_ACR1, PT_ACR2, PT_ACR3,
- PT_ACR4, PT_ACR5, PT_ACR6, PT_ACR7,
- PT_ACR8, PT_ACR9, PT_ACR10, PT_ACR11,
- PT_ACR12, PT_ACR13, PT_ACR14, PT_ACR15,
-
- PT_FPC,
-
-#ifndef __s390x__
- PT_FPR0_HI, PT_FPR1_HI, PT_FPR2_HI, PT_FPR3_HI,
- PT_FPR4_HI, PT_FPR5_HI, PT_FPR6_HI, PT_FPR7_HI,
- PT_FPR8_HI, PT_FPR9_HI, PT_FPR10_HI, PT_FPR11_HI,
- PT_FPR12_HI, PT_FPR13_HI, PT_FPR14_HI, PT_FPR15_HI,
-#else
- PT_FPR0, PT_FPR1, PT_FPR2, PT_FPR3,
- PT_FPR4, PT_FPR5, PT_FPR6, PT_FPR7,
- PT_FPR8, PT_FPR9, PT_FPR10, PT_FPR11,
- PT_FPR12, PT_FPR13, PT_FPR14, PT_FPR15,
-#endif
-
- PT_ORIGGPR2,
-};
-
-#define s390_num_regs_3264 68
-
-#ifdef __s390x__
-static int s390_regmap_3264[] = {
- PT_PSWMASK, PT_PSWADDR,
-
- PT_GPR0, PT_GPR0, PT_GPR1, PT_GPR1,
- PT_GPR2, PT_GPR2, PT_GPR3, PT_GPR3,
- PT_GPR4, PT_GPR4, PT_GPR5, PT_GPR5,
- PT_GPR6, PT_GPR6, PT_GPR7, PT_GPR7,
- PT_GPR8, PT_GPR8, PT_GPR9, PT_GPR9,
- PT_GPR10, PT_GPR10, PT_GPR11, PT_GPR11,
- PT_GPR12, PT_GPR12, PT_GPR13, PT_GPR13,
- PT_GPR14, PT_GPR14, PT_GPR15, PT_GPR15,
-
- PT_ACR0, PT_ACR1, PT_ACR2, PT_ACR3,
- PT_ACR4, PT_ACR5, PT_ACR6, PT_ACR7,
- PT_ACR8, PT_ACR9, PT_ACR10, PT_ACR11,
- PT_ACR12, PT_ACR13, PT_ACR14, PT_ACR15,
-
- PT_FPC,
-
- PT_FPR0, PT_FPR1, PT_FPR2, PT_FPR3,
- PT_FPR4, PT_FPR5, PT_FPR6, PT_FPR7,
- PT_FPR8, PT_FPR9, PT_FPR10, PT_FPR11,
- PT_FPR12, PT_FPR13, PT_FPR14, PT_FPR15,
-
- PT_ORIGGPR2,
-};
-#else
-static int s390_regmap_3264[] = {
- PT_PSWMASK, PT_PSWADDR,
-
- -1, PT_GPR0, -1, PT_GPR1,
- -1, PT_GPR2, -1, PT_GPR3,
- -1, PT_GPR4, -1, PT_GPR5,
- -1, PT_GPR6, -1, PT_GPR7,
- -1, PT_GPR8, -1, PT_GPR9,
- -1, PT_GPR10, -1, PT_GPR11,
- -1, PT_GPR12, -1, PT_GPR13,
- -1, PT_GPR14, -1, PT_GPR15,
-
- PT_ACR0, PT_ACR1, PT_ACR2, PT_ACR3,
- PT_ACR4, PT_ACR5, PT_ACR6, PT_ACR7,
- PT_ACR8, PT_ACR9, PT_ACR10, PT_ACR11,
- PT_ACR12, PT_ACR13, PT_ACR14, PT_ACR15,
-
- PT_FPC,
-
- PT_FPR0_HI, PT_FPR1_HI, PT_FPR2_HI, PT_FPR3_HI,
- PT_FPR4_HI, PT_FPR5_HI, PT_FPR6_HI, PT_FPR7_HI,
- PT_FPR8_HI, PT_FPR9_HI, PT_FPR10_HI, PT_FPR11_HI,
- PT_FPR12_HI, PT_FPR13_HI, PT_FPR14_HI, PT_FPR15_HI,
-
- PT_ORIGGPR2,
-};
-#endif
-
-
-static int
-s390_cannot_fetch_register (int regno)
-{
- return 0;
-}
-
-static int
-s390_cannot_store_register (int regno)
-{
- return 0;
-}
-
-static void
-s390_collect_ptrace_register (struct regcache *regcache, int regno, char *buf)
-{
- int size = register_size (regcache->tdesc, regno);
- const struct regs_info *regs_info = (*the_low_target.regs_info) ();
- struct usrregs_info *usr = regs_info->usrregs;
- int regaddr = usr->regmap[regno];
-
- if (size < sizeof (long))
- {
- memset (buf, 0, sizeof (long));
-
- if ((regno ^ 1) < usr->num_regs
- && usr->regmap[regno ^ 1] == regaddr)
- {
- collect_register (regcache, regno & ~1, buf);
- collect_register (regcache, (regno & ~1) + 1,
- buf + sizeof (long) - size);
- }
- else if (regaddr == PT_PSWMASK)
- {
- /* Convert 4-byte PSW mask to 8 bytes by clearing bit 12 and copying
- the basic addressing mode bit from the PSW address. */
- gdb_byte *addr = (gdb_byte *) alloca (register_size (regcache->tdesc, regno ^ 1));
- collect_register (regcache, regno, buf);
- collect_register (regcache, regno ^ 1, addr);
- buf[1] &= ~0x8;
- buf[size] |= (addr[0] & 0x80);
- }
- else if (regaddr == PT_PSWADDR)
- {
- /* Convert 4-byte PSW address to 8 bytes by clearing the addressing
- mode bit (which gets copied to the PSW mask instead). */
- collect_register (regcache, regno, buf + sizeof (long) - size);
- buf[sizeof (long) - size] &= ~0x80;
- }
- else if ((regaddr >= PT_GPR0 && regaddr <= PT_GPR15)
- || regaddr == PT_ORIGGPR2)
- collect_register (regcache, regno, buf + sizeof (long) - size);
- else
- collect_register (regcache, regno, buf);
- }
- else if (regaddr != -1)
- collect_register (regcache, regno, buf);
-}
-
-static void
-s390_supply_ptrace_register (struct regcache *regcache,
- int regno, const char *buf)
-{
- int size = register_size (regcache->tdesc, regno);
- const struct regs_info *regs_info = (*the_low_target.regs_info) ();
- struct usrregs_info *usr = regs_info->usrregs;
- int regaddr = usr->regmap[regno];
-
- if (size < sizeof (long))
- {
- if ((regno ^ 1) < usr->num_regs
- && usr->regmap[regno ^ 1] == regaddr)
- {
- supply_register (regcache, regno & ~1, buf);
- supply_register (regcache, (regno & ~1) + 1,
- buf + sizeof (long) - size);
- }
- else if (regaddr == PT_PSWMASK)
- {
- /* Convert 8-byte PSW mask to 4 bytes by setting bit 12 and copying
- the basic addressing mode into the PSW address. */
- gdb_byte *mask = (gdb_byte *) alloca (size);
- gdb_byte *addr = (gdb_byte *) alloca (register_size (regcache->tdesc, regno ^ 1));
- memcpy (mask, buf, size);
- mask[1] |= 0x8;
- supply_register (regcache, regno, mask);
-
- collect_register (regcache, regno ^ 1, addr);
- addr[0] &= ~0x80;
- addr[0] |= (buf[size] & 0x80);
- supply_register (regcache, regno ^ 1, addr);
- }
- else if (regaddr == PT_PSWADDR)
- {
- /* Convert 8-byte PSW address to 4 bytes by truncating, but
- keeping the addressing mode bit (which was set from the mask). */
- gdb_byte *addr = (gdb_byte *) alloca (size);
- char amode;
- collect_register (regcache, regno, addr);
- amode = addr[0] & 0x80;
- memcpy (addr, buf + sizeof (long) - size, size);
- addr[0] &= ~0x80;
- addr[0] |= amode;
- supply_register (regcache, regno, addr);
- }
- else if ((regaddr >= PT_GPR0 && regaddr <= PT_GPR15)
- || regaddr == PT_ORIGGPR2)
- supply_register (regcache, regno, buf + sizeof (long) - size);
- else
- supply_register (regcache, regno, buf);
- }
- else if (regaddr != -1)
- supply_register (regcache, regno, buf);
-}
-
-/* Provide only a fill function for the general register set. ps_lgetregs
- will use this for NPTL support. */
-
-static void
-s390_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
- const struct regs_info *regs_info = (*the_low_target.regs_info) ();
- struct usrregs_info *usr = regs_info->usrregs;
-
- for (i = 0; i < usr->num_regs; i++)
- {
- if (usr->regmap[i] < PT_PSWMASK
- || usr->regmap[i] > PT_ACR15)
- continue;
-
- s390_collect_ptrace_register (regcache, i,
- (char *) buf + usr->regmap[i]);
- }
-}
-
-/* Fill and store functions for extended register sets. */
-
-#ifndef __s390x__
-static void
-s390_fill_gprs_high (struct regcache *regcache, void *buf)
-{
- int r0h = find_regno (regcache->tdesc, "r0h");
- int i;
-
- for (i = 0; i < 16; i++)
- collect_register (regcache, r0h + 2 * i, (char *) buf + 4 * i);
-}
-
-static void
-s390_store_gprs_high (struct regcache *regcache, const void *buf)
-{
- int r0h = find_regno (regcache->tdesc, "r0h");
- int i;
-
- for (i = 0; i < 16; i++)
- supply_register (regcache, r0h + 2 * i, (const char *) buf + 4 * i);
-}
-#endif
-
-static void
-s390_store_last_break (struct regcache *regcache, const void *buf)
-{
- const char *p;
-
- p = (const char *) buf + 8 - register_size (regcache->tdesc, 0);
- supply_register_by_name (regcache, "last_break", p);
-}
-
-static void
-s390_fill_system_call (struct regcache *regcache, void *buf)
-{
- collect_register_by_name (regcache, "system_call", buf);
-}
-
-static void
-s390_store_system_call (struct regcache *regcache, const void *buf)
-{
- supply_register_by_name (regcache, "system_call", buf);
-}
-
-static void
-s390_store_tdb (struct regcache *regcache, const void *buf)
-{
- int tdb0 = find_regno (regcache->tdesc, "tdb0");
- int tr0 = find_regno (regcache->tdesc, "tr0");
- int i;
-
- for (i = 0; i < 4; i++)
- supply_register (regcache, tdb0 + i, (const char *) buf + 8 * i);
-
- for (i = 0; i < 16; i++)
- supply_register (regcache, tr0 + i, (const char *) buf + 8 * (16 + i));
-}
-
-static void
-s390_fill_vxrs_low (struct regcache *regcache, void *buf)
-{
- int v0 = find_regno (regcache->tdesc, "v0l");
- int i;
-
- for (i = 0; i < 16; i++)
- collect_register (regcache, v0 + i, (char *) buf + 8 * i);
-}
-
-static void
-s390_store_vxrs_low (struct regcache *regcache, const void *buf)
-{
- int v0 = find_regno (regcache->tdesc, "v0l");
- int i;
-
- for (i = 0; i < 16; i++)
- supply_register (regcache, v0 + i, (const char *) buf + 8 * i);
-}
-
-static void
-s390_fill_vxrs_high (struct regcache *regcache, void *buf)
-{
- int v16 = find_regno (regcache->tdesc, "v16");
- int i;
-
- for (i = 0; i < 16; i++)
- collect_register (regcache, v16 + i, (char *) buf + 16 * i);
-}
-
-static void
-s390_store_vxrs_high (struct regcache *regcache, const void *buf)
-{
- int v16 = find_regno (regcache->tdesc, "v16");
- int i;
-
- for (i = 0; i < 16; i++)
- supply_register (regcache, v16 + i, (const char *) buf + 16 * i);
-}
-
-static void
-s390_store_gs (struct regcache *regcache, const void *buf)
-{
- int gsd = find_regno (regcache->tdesc, "gsd");
- int i;
-
- for (i = 0; i < 3; i++)
- supply_register (regcache, gsd + i, (const char *) buf + 8 * (i + 1));
-}
-
-static void
-s390_store_gsbc (struct regcache *regcache, const void *buf)
-{
- int bc_gsd = find_regno (regcache->tdesc, "bc_gsd");
- int i;
-
- for (i = 0; i < 3; i++)
- supply_register (regcache, bc_gsd + i, (const char *) buf + 8 * (i + 1));
-}
-
-static struct regset_info s390_regsets[] = {
- { 0, 0, 0, 0, GENERAL_REGS, s390_fill_gregset, NULL },
-#ifndef __s390x__
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_HIGH_GPRS, 0,
- EXTENDED_REGS, s390_fill_gprs_high, s390_store_gprs_high },
-#endif
- /* Last break address is read-only; no fill function. */
- { PTRACE_GETREGSET, -1, NT_S390_LAST_BREAK, 0, EXTENDED_REGS,
- NULL, s390_store_last_break },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_SYSTEM_CALL, 0,
- EXTENDED_REGS, s390_fill_system_call, s390_store_system_call },
- /* TDB is read-only. */
- { PTRACE_GETREGSET, -1, NT_S390_TDB, 0, EXTENDED_REGS,
- NULL, s390_store_tdb },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_VXRS_LOW, 0,
- EXTENDED_REGS, s390_fill_vxrs_low, s390_store_vxrs_low },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_VXRS_HIGH, 0,
- EXTENDED_REGS, s390_fill_vxrs_high, s390_store_vxrs_high },
- /* Guarded storage registers are read-only. */
- { PTRACE_GETREGSET, -1, NT_S390_GS_CB, 0, EXTENDED_REGS,
- NULL, s390_store_gs },
- { PTRACE_GETREGSET, -1, NT_S390_GS_BC, 0, EXTENDED_REGS,
- NULL, s390_store_gsbc },
- NULL_REGSET
-};
-
-
-static const gdb_byte s390_breakpoint[] = { 0, 1 };
-#define s390_breakpoint_len 2
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-s390_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = s390_breakpoint_len;
- return s390_breakpoint;
-}
-
-static CORE_ADDR
-s390_get_pc (struct regcache *regcache)
-{
- if (register_size (regcache->tdesc, 0) == 4)
- {
- unsigned int pswa;
- collect_register_by_name (regcache, "pswa", &pswa);
- return pswa & 0x7fffffff;
- }
- else
- {
- unsigned long pc;
- collect_register_by_name (regcache, "pswa", &pc);
- return pc;
- }
-}
-
-static void
-s390_set_pc (struct regcache *regcache, CORE_ADDR newpc)
-{
- if (register_size (regcache->tdesc, 0) == 4)
- {
- unsigned int pswa;
- collect_register_by_name (regcache, "pswa", &pswa);
- pswa = (pswa & 0x80000000) | (newpc & 0x7fffffff);
- supply_register_by_name (regcache, "pswa", &pswa);
- }
- else
- {
- unsigned long pc = newpc;
- supply_register_by_name (regcache, "pswa", &pc);
- }
-}
-
-/* Determine the word size for the given PID, in bytes. */
-
-#ifdef __s390x__
-static int
-s390_get_wordsize (int pid)
-{
- errno = 0;
- PTRACE_XFER_TYPE pswm = ptrace (PTRACE_PEEKUSER, pid,
- (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) 0);
- if (errno != 0)
- {
- warning (_("Couldn't determine word size, assuming 64-bit."));
- return 8;
- }
- /* Derive word size from extended addressing mode (PSW bit 31). */
- return pswm & (1L << 32) ? 8 : 4;
-}
-#else
-#define s390_get_wordsize(pid) 4
-#endif
-
-static int
-s390_check_regset (int pid, int regset, int regsize)
-{
- void *buf = alloca (regsize);
- struct iovec iov;
-
- iov.iov_base = buf;
- iov.iov_len = regsize;
-
- if (ptrace (PTRACE_GETREGSET, pid, (long) regset, (long) &iov) >= 0
- || errno == ENODATA)
- return 1;
- return 0;
-}
-
-/* For a 31-bit inferior, whether the kernel supports using the full
- 64-bit GPRs. */
-static int have_hwcap_s390_high_gprs = 0;
-static int have_hwcap_s390_vx = 0;
-
-static void
-s390_arch_setup (void)
-{
- const struct target_desc *tdesc;
- struct regset_info *regset;
-
- /* Determine word size and HWCAP. */
- int pid = pid_of (current_thread);
- int wordsize = s390_get_wordsize (pid);
- unsigned long hwcap = linux_get_hwcap (wordsize);
-
- /* Check whether the kernel supports extra register sets. */
- int have_regset_last_break
- = s390_check_regset (pid, NT_S390_LAST_BREAK, 8);
- int have_regset_system_call
- = s390_check_regset (pid, NT_S390_SYSTEM_CALL, 4);
- int have_regset_tdb
- = (s390_check_regset (pid, NT_S390_TDB, 256)
- && (hwcap & HWCAP_S390_TE) != 0);
- int have_regset_vxrs
- = (s390_check_regset (pid, NT_S390_VXRS_LOW, 128)
- && s390_check_regset (pid, NT_S390_VXRS_HIGH, 256)
- && (hwcap & HWCAP_S390_VX) != 0);
- int have_regset_gs
- = (s390_check_regset (pid, NT_S390_GS_CB, 32)
- && s390_check_regset (pid, NT_S390_GS_BC, 32)
- && (hwcap & HWCAP_S390_GS) != 0);
-
- {
-#ifdef __s390x__
- if (wordsize == 8)
- {
- if (have_regset_gs)
- tdesc = tdesc_s390x_gs_linux64;
- else if (have_regset_vxrs)
- tdesc = (have_regset_tdb ? tdesc_s390x_tevx_linux64 :
- tdesc_s390x_vx_linux64);
- else if (have_regset_tdb)
- tdesc = tdesc_s390x_te_linux64;
- else if (have_regset_system_call)
- tdesc = tdesc_s390x_linux64v2;
- else if (have_regset_last_break)
- tdesc = tdesc_s390x_linux64v1;
- else
- tdesc = tdesc_s390x_linux64;
- }
-
- /* For a 31-bit inferior, check whether the kernel supports
- using the full 64-bit GPRs. */
- else
-#endif
- if (hwcap & HWCAP_S390_HIGH_GPRS)
- {
- have_hwcap_s390_high_gprs = 1;
- if (have_regset_gs)
- tdesc = tdesc_s390_gs_linux64;
- else if (have_regset_vxrs)
- tdesc = (have_regset_tdb ? tdesc_s390_tevx_linux64 :
- tdesc_s390_vx_linux64);
- else if (have_regset_tdb)
- tdesc = tdesc_s390_te_linux64;
- else if (have_regset_system_call)
- tdesc = tdesc_s390_linux64v2;
- else if (have_regset_last_break)
- tdesc = tdesc_s390_linux64v1;
- else
- tdesc = tdesc_s390_linux64;
- }
- else
- {
- /* Assume 31-bit inferior process. */
- if (have_regset_system_call)
- tdesc = tdesc_s390_linux32v2;
- else if (have_regset_last_break)
- tdesc = tdesc_s390_linux32v1;
- else
- tdesc = tdesc_s390_linux32;
- }
-
- have_hwcap_s390_vx = have_regset_vxrs;
- }
-
- /* Update target_regsets according to available register sets. */
- for (regset = s390_regsets; regset->size >= 0; regset++)
- if (regset->get_request == PTRACE_GETREGSET)
- switch (regset->nt_type)
- {
-#ifndef __s390x__
- case NT_S390_HIGH_GPRS:
- regset->size = have_hwcap_s390_high_gprs ? 64 : 0;
- break;
-#endif
- case NT_S390_LAST_BREAK:
- regset->size = have_regset_last_break ? 8 : 0;
- break;
- case NT_S390_SYSTEM_CALL:
- regset->size = have_regset_system_call ? 4 : 0;
- break;
- case NT_S390_TDB:
- regset->size = have_regset_tdb ? 256 : 0;
- break;
- case NT_S390_VXRS_LOW:
- regset->size = have_regset_vxrs ? 128 : 0;
- break;
- case NT_S390_VXRS_HIGH:
- regset->size = have_regset_vxrs ? 256 : 0;
- break;
- case NT_S390_GS_CB:
- case NT_S390_GS_BC:
- regset->size = have_regset_gs ? 32 : 0;
- default:
- break;
- }
-
- current_process ()->tdesc = tdesc;
-}
-
-
-static int
-s390_breakpoint_at (CORE_ADDR pc)
-{
- unsigned char c[s390_breakpoint_len];
- read_inferior_memory (pc, c, s390_breakpoint_len);
- return memcmp (c, s390_breakpoint, s390_breakpoint_len) == 0;
-}
-
-/* Breakpoint/Watchpoint support. */
-
-/* The "supports_z_point_type" linux_target_ops method. */
-
-static int
-s390_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_SW_BP:
- return 1;
- default:
- return 0;
- }
-}
-
-/* Support for hardware single step. */
-
-static int
-s390_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static struct usrregs_info s390_usrregs_info =
- {
- s390_num_regs,
- s390_regmap,
- };
-
-static struct regsets_info s390_regsets_info =
- {
- s390_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &s390_usrregs_info,
- &s390_regsets_info
- };
-
-static struct usrregs_info s390_usrregs_info_3264 =
- {
- s390_num_regs_3264,
- s390_regmap_3264
- };
-
-static struct regsets_info s390_regsets_info_3264 =
- {
- s390_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct regs_info regs_info_3264 =
- {
- NULL, /* regset_bitmap */
- &s390_usrregs_info_3264,
- &s390_regsets_info_3264
- };
-
-static const struct regs_info *
-s390_regs_info (void)
-{
- if (have_hwcap_s390_high_gprs)
- {
-#ifdef __s390x__
- const struct target_desc *tdesc = current_process ()->tdesc;
-
- if (register_size (tdesc, 0) == 4)
- return ®s_info_3264;
-#else
- return ®s_info_3264;
-#endif
- }
- return ®s_info;
-}
-
-/* The "supports_tracepoints" linux_target_ops method. */
-
-static int
-s390_supports_tracepoints (void)
-{
- return 1;
-}
-
-/* Implementation of linux_target_ops method "get_thread_area". */
-
-static int
-s390_get_thread_area (int lwpid, CORE_ADDR *addrp)
-{
- CORE_ADDR res = ptrace (PTRACE_PEEKUSER, lwpid, (long) PT_ACR0, (long) 0);
-#ifdef __s390x__
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
-
- if (register_size (regcache->tdesc, 0) == 4)
- res &= 0xffffffffull;
-#endif
- *addrp = res;
- return 0;
-}
-
-
-/* Fast tracepoint support.
-
- The register save area on stack is identical for all targets:
-
- 0x000+i*0x10: VR0-VR31
- 0x200+i*8: GR0-GR15
- 0x280+i*4: AR0-AR15
- 0x2c0: PSWM [64-bit]
- 0x2c8: PSWA [64-bit]
- 0x2d0: FPC
-
- If we're on 31-bit linux, we just don't store the high parts of the GPRs.
- Likewise, if there's no VX support, we just store the FRs into the slots
- of low VR halves. The agent code is responsible for rearranging that
- into regcache. */
-
-/* Code sequence saving GPRs for 31-bit target with no high GPRs. There's
- one trick used at the very beginning: since there's no way to allocate
- stack space without destroying CC (lay instruction can do it, but it's
- only supported on later CPUs), we take 4 different execution paths for
- every possible value of CC, allocate stack space, save %r0, stuff the
- CC value in %r0 (shifted to match its position in PSWM high word),
- then branch to common path. */
-
-static const unsigned char s390_ft_entry_gpr_esa[] = {
- 0xa7, 0x14, 0x00, 0x1e, /* jo .Lcc3 */
- 0xa7, 0x24, 0x00, 0x14, /* jh .Lcc2 */
- 0xa7, 0x44, 0x00, 0x0a, /* jl .Lcc1 */
- /* CC = 0 */
- 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
- 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
- 0xa7, 0x08, 0x00, 0x00, /* lhi %r0, 0 */
- 0xa7, 0xf4, 0x00, 0x18, /* j .Lccdone */
- /* .Lcc1: */
- 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
- 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
- 0xa7, 0x08, 0x10, 0x00, /* lhi %r0, 0x1000 */
- 0xa7, 0xf4, 0x00, 0x10, /* j .Lccdone */
- /* .Lcc2: */
- 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
- 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
- 0xa7, 0x08, 0x20, 0x00, /* lhi %r0, 0x2000 */
- 0xa7, 0xf4, 0x00, 0x08, /* j .Lccdone */
- /* .Lcc3: */
- 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
- 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
- 0xa7, 0x08, 0x30, 0x00, /* lhi %r0, 0x3000 */
- /* .Lccdone: */
- 0x50, 0x10, 0xf2, 0x0c, /* st %r1, 0x20c(%r15) */
- 0x50, 0x20, 0xf2, 0x14, /* st %r2, 0x214(%r15) */
- 0x50, 0x30, 0xf2, 0x1c, /* st %r3, 0x21c(%r15) */
- 0x50, 0x40, 0xf2, 0x24, /* st %r4, 0x224(%r15) */
- 0x50, 0x50, 0xf2, 0x2c, /* st %r5, 0x22c(%r15) */
- 0x50, 0x60, 0xf2, 0x34, /* st %r6, 0x234(%r15) */
- 0x50, 0x70, 0xf2, 0x3c, /* st %r7, 0x23c(%r15) */
- 0x50, 0x80, 0xf2, 0x44, /* st %r8, 0x244(%r15) */
- 0x50, 0x90, 0xf2, 0x4c, /* st %r9, 0x24c(%r15) */
- 0x50, 0xa0, 0xf2, 0x54, /* st %r10, 0x254(%r15) */
- 0x50, 0xb0, 0xf2, 0x5c, /* st %r11, 0x25c(%r15) */
- 0x50, 0xc0, 0xf2, 0x64, /* st %r12, 0x264(%r15) */
- 0x50, 0xd0, 0xf2, 0x6c, /* st %r13, 0x26c(%r15) */
- 0x50, 0xe0, 0xf2, 0x74, /* st %r14, 0x274(%r15) */
- /* Compute original value of %r15 and store it. We use ahi instead
- of la to preserve the whole value, and not just the low 31 bits.
- This is not particularly important here, but essential in the
- zarch case where someone might be using the high word of %r15
- as an extra register. */
- 0x18, 0x1f, /* lr %r1, %r15 */
- 0xa7, 0x1a, 0x03, 0x00, /* ahi %r1, 0x300 */
- 0x50, 0x10, 0xf2, 0x7c, /* st %r1, 0x27c(%r15) */
-};
-
-/* Code sequence saving GPRs for 31-bit target with high GPRs and for 64-bit
- target. Same as above, except this time we can use load/store multiple,
- since the 64-bit regs are tightly packed. */
-
-static const unsigned char s390_ft_entry_gpr_zarch[] = {
- 0xa7, 0x14, 0x00, 0x21, /* jo .Lcc3 */
- 0xa7, 0x24, 0x00, 0x16, /* jh .Lcc2 */
- 0xa7, 0x44, 0x00, 0x0b, /* jl .Lcc1 */
- /* CC = 0 */
- 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
- 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
- 0xa7, 0x08, 0x00, 0x00, /* lhi %r0, 0 */
- 0xa7, 0xf4, 0x00, 0x1b, /* j .Lccdone */
- /* .Lcc1: */
- 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
- 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
- 0xa7, 0x08, 0x10, 0x00, /* lhi %r0, 0x1000 */
- 0xa7, 0xf4, 0x00, 0x12, /* j .Lccdone */
- /* .Lcc2: */
- 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
- 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
- 0xa7, 0x08, 0x20, 0x00, /* lhi %r0, 0x2000 */
- 0xa7, 0xf4, 0x00, 0x09, /* j .Lccdone */
- /* .Lcc3: */
- 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
- 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
- 0xa7, 0x08, 0x30, 0x00, /* lhi %r0, 0x3000 */
- /* .Lccdone: */
- 0xb9, 0x04, 0x00, 0x1f, /* lgr %r1, %r15 */
- 0xa7, 0x1b, 0x03, 0x00, /* aghi %r1, 0x300 */
- 0xe3, 0x10, 0xf2, 0x78, 0x00, 0x24, /* stg %r1, 0x278(%r15) */
-};
-
-/* Code sequence saving ARs, PSWM and FPC. PSWM has to be assembled from
- current PSWM (read by epsw) and CC from entry (in %r0). */
-
-static const unsigned char s390_ft_entry_misc[] = {
- 0x9b, 0x0f, 0xf2, 0x80, /* stam %a0, %a15, 0x20(%%r15) */
- 0xb9, 0x8d, 0x00, 0x23, /* epsw %r2, %r3 */
- 0xa7, 0x18, 0xcf, 0xff, /* lhi %r1, ~0x3000 */
- 0x14, 0x21, /* nr %r2, %r1 */
- 0x16, 0x20, /* or %r2, %r0 */
- 0x50, 0x20, 0xf2, 0xc0, /* st %r2, 0x2c0(%r15) */
- 0x50, 0x30, 0xf2, 0xc4, /* st %r3, 0x2c4(%r15) */
- 0xb2, 0x9c, 0xf2, 0xd0, /* stfpc 0x2d0(%r15) */
-};
-
-/* Code sequence saving FRs, used if VX not supported. */
-
-static const unsigned char s390_ft_entry_fr[] = {
- 0x60, 0x00, 0xf0, 0x00, /* std %f0, 0x000(%r15) */
- 0x60, 0x10, 0xf0, 0x10, /* std %f1, 0x010(%r15) */
- 0x60, 0x20, 0xf0, 0x20, /* std %f2, 0x020(%r15) */
- 0x60, 0x30, 0xf0, 0x30, /* std %f3, 0x030(%r15) */
- 0x60, 0x40, 0xf0, 0x40, /* std %f4, 0x040(%r15) */
- 0x60, 0x50, 0xf0, 0x50, /* std %f5, 0x050(%r15) */
- 0x60, 0x60, 0xf0, 0x60, /* std %f6, 0x060(%r15) */
- 0x60, 0x70, 0xf0, 0x70, /* std %f7, 0x070(%r15) */
- 0x60, 0x80, 0xf0, 0x80, /* std %f8, 0x080(%r15) */
- 0x60, 0x90, 0xf0, 0x90, /* std %f9, 0x090(%r15) */
- 0x60, 0xa0, 0xf0, 0xa0, /* std %f10, 0x0a0(%r15) */
- 0x60, 0xb0, 0xf0, 0xb0, /* std %f11, 0x0b0(%r15) */
- 0x60, 0xc0, 0xf0, 0xc0, /* std %f12, 0x0c0(%r15) */
- 0x60, 0xd0, 0xf0, 0xd0, /* std %f13, 0x0d0(%r15) */
- 0x60, 0xe0, 0xf0, 0xe0, /* std %f14, 0x0e0(%r15) */
- 0x60, 0xf0, 0xf0, 0xf0, /* std %f15, 0x0f0(%r15) */
-};
-
-/* Code sequence saving VRs, used if VX not supported. */
-
-static const unsigned char s390_ft_entry_vr[] = {
- 0xe7, 0x0f, 0xf0, 0x00, 0x00, 0x3e, /* vstm %v0, %v15, 0x000(%r15) */
- 0xe7, 0x0f, 0xf1, 0x00, 0x0c, 0x3e, /* vstm %v16, %v31, 0x100(%r15) */
-};
-
-/* Code sequence doing the collection call for 31-bit target. %r1 contains
- the address of the literal pool. */
-
-static const unsigned char s390_ft_main_31[] = {
- /* Load the literals into registers. */
- 0x58, 0x50, 0x10, 0x00, /* l %r5, 0x0(%r1) */
- 0x58, 0x20, 0x10, 0x04, /* l %r2, 0x4(%r1) */
- 0x58, 0x40, 0x10, 0x08, /* l %r4, 0x8(%r1) */
- 0x58, 0x60, 0x10, 0x0c, /* l %r6, 0xc(%r1) */
- /* Save original PSWA (tracepoint address | 0x80000000). */
- 0x50, 0x50, 0xf2, 0xcc, /* st %r5, 0x2cc(%r15) */
- /* Construct a collecting_t object at %r15+0x2e0. */
- 0x50, 0x20, 0xf2, 0xe0, /* st %r2, 0x2e0(%r15) */
- 0x9b, 0x00, 0xf2, 0xe4, /* stam %a0, %a0, 0x2e4(%r15) */
- /* Move its address to %r0. */
- 0x41, 0x00, 0xf2, 0xe0, /* la %r0, 0x2e0(%r15) */
- /* Take the lock. */
- /* .Lloop: */
- 0xa7, 0x18, 0x00, 0x00, /* lhi %r1, 0 */
- 0xba, 0x10, 0x60, 0x00, /* cs %r1, %r0, 0(%r6) */
- 0xa7, 0x74, 0xff, 0xfc, /* jne .Lloop */
- /* Address of the register save block to %r3. */
- 0x18, 0x3f, /* lr %r3, %r15 */
- /* Make a stack frame, so that we can call the collector. */
- 0xa7, 0xfa, 0xff, 0xa0, /* ahi %r15, -0x60 */
- /* Call it. */
- 0x0d, 0xe4, /* basr %r14, %r4 */
- /* And get rid of the stack frame again. */
- 0x41, 0xf0, 0xf0, 0x60, /* la %r15, 0x60(%r15) */
- /* Leave the lock. */
- 0x07, 0xf0, /* br %r0 */
- 0xa7, 0x18, 0x00, 0x00, /* lhi %r1, 0 */
- 0x50, 0x10, 0x60, 0x00, /* st %t1, 0(%r6) */
-};
-
-/* Code sequence doing the collection call for 64-bit target. %r1 contains
- the address of the literal pool. */
-
-static const unsigned char s390_ft_main_64[] = {
- /* Load the literals into registers. */
- 0xe3, 0x50, 0x10, 0x00, 0x00, 0x04, /* lg %r5, 0x00(%r1) */
- 0xe3, 0x20, 0x10, 0x08, 0x00, 0x04, /* lg %r2, 0x08(%r1) */
- 0xe3, 0x40, 0x10, 0x10, 0x00, 0x04, /* lg %r4, 0x10(%r1) */
- 0xe3, 0x60, 0x10, 0x18, 0x00, 0x04, /* lg %r6, 0x18(%r1) */
- /* Save original PSWA (tracepoint address). */
- 0xe3, 0x50, 0xf2, 0xc8, 0x00, 0x24, /* stg %r5, 0x2c8(%r15) */
- /* Construct a collecting_t object at %r15+0x2e0. */
- 0xe3, 0x20, 0xf2, 0xe0, 0x00, 0x24, /* stg %r2, 0x2e0(%r15) */
- 0x9b, 0x01, 0xf2, 0xe8, /* stam %a0, %a1, 0x2e8(%r15) */
- /* Move its address to %r0. */
- 0x41, 0x00, 0xf2, 0xe0, /* la %r0, 0x2e0(%r15) */
- /* Take the lock. */
- /* .Lloop: */
- 0xa7, 0x19, 0x00, 0x00, /* lghi %r1, 0 */
- 0xeb, 0x10, 0x60, 0x00, 0x00, 0x30, /* csg %r1, %r0, 0(%r6) */
- 0xa7, 0x74, 0xff, 0xfb, /* jne .Lloop */
- /* Address of the register save block to %r3. */
- 0xb9, 0x04, 0x00, 0x3f, /* lgr %r3, %r15 */
- /* Make a stack frame, so that we can call the collector. */
- 0xa7, 0xfb, 0xff, 0x60, /* aghi %r15, -0xa0 */
- /* Call it. */
- 0x0d, 0xe4, /* basr %r14, %r4 */
- /* And get rid of the stack frame again. */
- 0x41, 0xf0, 0xf0, 0xa0, /* la %r15, 0xa0(%r15) */
- /* Leave the lock. */
- 0x07, 0xf0, /* br %r0 */
- 0xa7, 0x19, 0x00, 0x00, /* lghi %r1, 0 */
- 0xe3, 0x10, 0x60, 0x00, 0x00, 0x24, /* stg %t1, 0(%r6) */
-};
-
-/* Code sequence restoring FRs, for targets with no VX support. */
-
-static const unsigned char s390_ft_exit_fr[] = {
- 0x68, 0x00, 0xf0, 0x00, /* ld %f0, 0x000(%r15) */
- 0x68, 0x10, 0xf0, 0x10, /* ld %f1, 0x010(%r15) */
- 0x68, 0x20, 0xf0, 0x20, /* ld %f2, 0x020(%r15) */
- 0x68, 0x30, 0xf0, 0x30, /* ld %f3, 0x030(%r15) */
- 0x68, 0x40, 0xf0, 0x40, /* ld %f4, 0x040(%r15) */
- 0x68, 0x50, 0xf0, 0x50, /* ld %f5, 0x050(%r15) */
- 0x68, 0x60, 0xf0, 0x60, /* ld %f6, 0x060(%r15) */
- 0x68, 0x70, 0xf0, 0x70, /* ld %f7, 0x070(%r15) */
- 0x68, 0x80, 0xf0, 0x80, /* ld %f8, 0x080(%r15) */
- 0x68, 0x90, 0xf0, 0x90, /* ld %f9, 0x090(%r15) */
- 0x68, 0xa0, 0xf0, 0xa0, /* ld %f10, 0x0a0(%r15) */
- 0x68, 0xb0, 0xf0, 0xb0, /* ld %f11, 0x0b0(%r15) */
- 0x68, 0xc0, 0xf0, 0xc0, /* ld %f12, 0x0c0(%r15) */
- 0x68, 0xd0, 0xf0, 0xd0, /* ld %f13, 0x0d0(%r15) */
- 0x68, 0xe0, 0xf0, 0xe0, /* ld %f14, 0x0e0(%r15) */
- 0x68, 0xf0, 0xf0, 0xf0, /* ld %f15, 0x0f0(%r15) */
-};
-
-/* Code sequence restoring VRs. */
-
-static const unsigned char s390_ft_exit_vr[] = {
- 0xe7, 0x0f, 0xf0, 0x00, 0x00, 0x36, /* vlm %v0, %v15, 0x000(%r15) */
- 0xe7, 0x0f, 0xf1, 0x00, 0x0c, 0x36, /* vlm %v16, %v31, 0x100(%r15) */
-};
-
-/* Code sequence restoring misc registers. As for PSWM, only CC should be
- modified by C code, so we use the alr instruction to restore it by
- manufacturing an operand that'll result in the original flags. */
-
-static const unsigned char s390_ft_exit_misc[] = {
- 0xb2, 0x9d, 0xf2, 0xd0, /* lfpc 0x2d0(%r15) */
- 0x58, 0x00, 0xf2, 0xc0, /* l %r0, 0x2c0(%r15) */
- /* Extract CC to high 2 bits of %r0. */
- 0x88, 0x00, 0x00, 0x0c, /* srl %r0, 12 */
- 0x89, 0x00, 0x00, 0x1e, /* sll %r0, 30 */
- /* Add %r0 to itself. Result will be nonzero iff CC bit 0 is set, and
- will have carry iff CC bit 1 is set - resulting in the same flags
- as the original. */
- 0x1e, 0x00, /* alr %r0, %r0 */
- 0x9a, 0x0f, 0xf2, 0x80, /* lam %a0, %a15, 0x280(%r15) */
-};
-
-/* Code sequence restoring GPRs, for 31-bit targets with no high GPRs. */
-
-static const unsigned char s390_ft_exit_gpr_esa[] = {
- 0x58, 0x00, 0xf2, 0x04, /* l %r0, 0x204(%r15) */
- 0x58, 0x10, 0xf2, 0x0c, /* l %r1, 0x20c(%r15) */
- 0x58, 0x20, 0xf2, 0x14, /* l %r2, 0x214(%r15) */
- 0x58, 0x30, 0xf2, 0x1c, /* l %r3, 0x21c(%r15) */
- 0x58, 0x40, 0xf2, 0x24, /* l %r4, 0x224(%r15) */
- 0x58, 0x50, 0xf2, 0x2c, /* l %r5, 0x22c(%r15) */
- 0x58, 0x60, 0xf2, 0x34, /* l %r6, 0x234(%r15) */
- 0x58, 0x70, 0xf2, 0x3c, /* l %r7, 0x23c(%r15) */
- 0x58, 0x80, 0xf2, 0x44, /* l %r8, 0x244(%r15) */
- 0x58, 0x90, 0xf2, 0x4c, /* l %r9, 0x24c(%r15) */
- 0x58, 0xa0, 0xf2, 0x54, /* l %r10, 0x254(%r15) */
- 0x58, 0xb0, 0xf2, 0x5c, /* l %r11, 0x25c(%r15) */
- 0x58, 0xc0, 0xf2, 0x64, /* l %r12, 0x264(%r15) */
- 0x58, 0xd0, 0xf2, 0x6c, /* l %r13, 0x26c(%r15) */
- 0x58, 0xe0, 0xf2, 0x74, /* l %r14, 0x274(%r15) */
- 0x58, 0xf0, 0xf2, 0x7c, /* l %r15, 0x27c(%r15) */
-};
-
-/* Code sequence restoring GPRs, for 64-bit targets and 31-bit targets
- with high GPRs. */
-
-static const unsigned char s390_ft_exit_gpr_zarch[] = {
- 0xeb, 0x0f, 0xf2, 0x00, 0x00, 0x04, /* lmg %r0, %r15, 0x200(%r15) */
-};
-
-/* Writes instructions to target, updating the to pointer. */
-
-static void
-append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf)
-{
- target_write_memory (*to, buf, len);
- *to += len;
-}
-
-/* Relocates an instruction from oldloc to *to, updating to. */
-
-static int
-s390_relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc, int is_64)
-{
- gdb_byte buf[6];
- int ilen;
- int op2;
- /* 0: no fixup, 1: PC16DBL fixup, 2: PC32DBL fixup. */
- int mode = 0;
- int is_bras = 0;
- read_inferior_memory (oldloc, buf, sizeof buf);
- if (buf[0] < 0x40)
- ilen = 2;
- else if (buf[0] < 0xc0)
- ilen = 4;
- else
- ilen = 6;
- switch (buf[0])
- {
- case 0x05: /* BALR */
- case 0x0c: /* BASSM */
- case 0x0d: /* BASR */
- case 0x45: /* BAL */
- case 0x4d: /* BAS */
- /* These save a return address and mess around with registers.
- We can't relocate them. */
- return 1;
- case 0x84: /* BRXH */
- case 0x85: /* BRXLE */
- mode = 1;
- break;
- case 0xa7:
- op2 = buf[1] & 0xf;
- /* BRC, BRAS, BRCT, BRCTG */
- if (op2 >= 4 && op2 <= 7)
- mode = 1;
- /* BRAS */
- if (op2 == 5)
- is_bras = 1;
- break;
- case 0xc0:
- op2 = buf[1] & 0xf;
- /* LARL, BRCL, BRASL */
- if (op2 == 0 || op2 == 4 || op2 == 5)
- mode = 2;
- /* BRASL */
- if (op2 == 5)
- is_bras = 1;
- break;
- case 0xc4:
- case 0xc6:
- /* PC-relative addressing instructions. */
- mode = 2;
- break;
- case 0xc5: /* BPRP */
- case 0xc7: /* BPP */
- /* Branch prediction - just skip it. */
- return 0;
- case 0xcc:
- op2 = buf[1] & 0xf;
- /* BRCTH */
- if (op2 == 6)
- mode = 2;
- break;
- case 0xec:
- op2 = buf[5];
- switch (op2)
- {
- case 0x44: /* BRXHG */
- case 0x45: /* BRXLG */
- case 0x64: /* CGRJ */
- case 0x65: /* CLGRJ */
- case 0x76: /* CRJ */
- case 0x77: /* CLRJ */
- mode = 1;
- break;
- }
- break;
- }
-
- if (mode != 0)
- {
- /* We'll have to relocate an instruction with a PC-relative field.
- First, compute the target. */
- int64_t loffset = 0;
- CORE_ADDR target;
- if (mode == 1)
- {
- int16_t soffset = 0;
- memcpy (&soffset, buf + 2, 2);
- loffset = soffset;
- }
- else if (mode == 2)
- {
- int32_t soffset = 0;
- memcpy (&soffset, buf + 2, 4);
- loffset = soffset;
- }
- target = oldloc + loffset * 2;
- if (!is_64)
- target &= 0x7fffffff;
-
- if (is_bras)
- {
- /* BRAS or BRASL was used. We cannot just relocate those, since
- they save the return address in a register. We can, however,
- replace them with a LARL+JG sequence. */
-
- /* Make the LARL. */
- int32_t soffset;
- buf[0] = 0xc0;
- buf[1] &= 0xf0;
- loffset = oldloc + ilen - *to;
- loffset >>= 1;
- soffset = loffset;
- if (soffset != loffset && is_64)
- return 1;
- memcpy (buf + 2, &soffset, 4);
- append_insns (to, 6, buf);
-
- /* Note: this is not fully correct. In 31-bit mode, LARL will write
- an address with the top bit 0, while BRAS/BRASL will write it
- with top bit 1. It should not matter much, since linux compilers
- use BR and not BSM to return from functions, but it could confuse
- some poor stack unwinder. */
-
- /* We'll now be writing a JG. */
- mode = 2;
- buf[0] = 0xc0;
- buf[1] = 0xf4;
- ilen = 6;
- }
-
- /* Compute the new offset and write it to the buffer. */
- loffset = target - *to;
- loffset >>= 1;
-
- if (mode == 1)
- {
- int16_t soffset = loffset;
- if (soffset != loffset)
- return 1;
- memcpy (buf + 2, &soffset, 2);
- }
- else if (mode == 2)
- {
- int32_t soffset = loffset;
- if (soffset != loffset && is_64)
- return 1;
- memcpy (buf + 2, &soffset, 4);
- }
- }
- append_insns (to, ilen, buf);
- return 0;
-}
-
-/* Implementation of linux_target_ops method
- "install_fast_tracepoint_jump_pad". */
-
-static int
-s390_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)
-{
- int i;
- int64_t loffset;
- int32_t offset;
- unsigned char jbuf[6] = { 0xc0, 0xf4, 0, 0, 0, 0 }; /* jg ... */
- CORE_ADDR buildaddr = *jump_entry;
-#ifdef __s390x__
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
- int is_64 = register_size (regcache->tdesc, 0) == 8;
- int is_zarch = is_64 || have_hwcap_s390_high_gprs;
- int has_vx = have_hwcap_s390_vx;
-#else
- int is_64 = 0, is_zarch = 0, has_vx = 0;
-#endif
- CORE_ADDR literals[4] = {
- tpaddr,
- tpoint,
- collector,
- lockaddr,
- };
-
- /* First, store the GPRs. */
- if (is_zarch)
- append_insns (&buildaddr, sizeof s390_ft_entry_gpr_zarch,
- s390_ft_entry_gpr_zarch);
- else
- append_insns (&buildaddr, sizeof s390_ft_entry_gpr_esa,
- s390_ft_entry_gpr_esa);
-
- /* Second, misc registers (ARs, PSWM, FPC). PSWA will be stored below. */
- append_insns (&buildaddr, sizeof s390_ft_entry_misc, s390_ft_entry_misc);
-
- /* Third, FRs or VRs. */
- if (has_vx)
- append_insns (&buildaddr, sizeof s390_ft_entry_vr, s390_ft_entry_vr);
- else
- append_insns (&buildaddr, sizeof s390_ft_entry_fr, s390_ft_entry_fr);
-
- /* Now, the main part of code - store PSWA, take lock, call collector,
- leave lock. First, we'll need to fetch 4 literals. */
- if (is_64) {
- unsigned char buf[] = {
- 0x07, 0x07, /* nopr %r7 */
- 0x07, 0x07, /* nopr %r7 */
- 0x07, 0x07, /* nopr %r7 */
- 0xa7, 0x15, 0x00, 0x12, /* bras %r1, .Lend */
- 0, 0, 0, 0, 0, 0, 0, 0, /* tpaddr */
- 0, 0, 0, 0, 0, 0, 0, 0, /* tpoint */
- 0, 0, 0, 0, 0, 0, 0, 0, /* collector */
- 0, 0, 0, 0, 0, 0, 0, 0, /* lockaddr */
- /* .Lend: */
- };
- /* Find the proper start place in buf, so that literals will be
- aligned. */
- int bufpos = (buildaddr + 2) & 7;
- /* Stuff the literals into the buffer. */
- for (i = 0; i < 4; i++) {
- uint64_t lit = literals[i];
- memcpy (&buf[sizeof buf - 32 + i * 8], &lit, 8);
- }
- append_insns (&buildaddr, sizeof buf - bufpos, buf + bufpos);
- append_insns (&buildaddr, sizeof s390_ft_main_64, s390_ft_main_64);
- } else {
- unsigned char buf[] = {
- 0x07, 0x07, /* nopr %r7 */
- 0xa7, 0x15, 0x00, 0x0a, /* bras %r1, .Lend */
- 0, 0, 0, 0, /* tpaddr */
- 0, 0, 0, 0, /* tpoint */
- 0, 0, 0, 0, /* collector */
- 0, 0, 0, 0, /* lockaddr */
- /* .Lend: */
- };
- /* Find the proper start place in buf, so that literals will be
- aligned. */
- int bufpos = (buildaddr + 2) & 3;
- /* First literal will be saved as the PSWA, make sure it has the high bit
- set. */
- literals[0] |= 0x80000000;
- /* Stuff the literals into the buffer. */
- for (i = 0; i < 4; i++) {
- uint32_t lit = literals[i];
- memcpy (&buf[sizeof buf - 16 + i * 4], &lit, 4);
- }
- append_insns (&buildaddr, sizeof buf - bufpos, buf + bufpos);
- append_insns (&buildaddr, sizeof s390_ft_main_31, s390_ft_main_31);
- }
-
- /* Restore FRs or VRs. */
- if (has_vx)
- append_insns (&buildaddr, sizeof s390_ft_exit_vr, s390_ft_exit_vr);
- else
- append_insns (&buildaddr, sizeof s390_ft_exit_fr, s390_ft_exit_fr);
-
- /* Restore misc registers. */
- append_insns (&buildaddr, sizeof s390_ft_exit_misc, s390_ft_exit_misc);
-
- /* Restore the GPRs. */
- if (is_zarch)
- append_insns (&buildaddr, sizeof s390_ft_exit_gpr_zarch,
- s390_ft_exit_gpr_zarch);
- else
- append_insns (&buildaddr, sizeof s390_ft_exit_gpr_esa,
- s390_ft_exit_gpr_esa);
-
- /* Now, adjust the original instruction to execute in the jump
- pad. */
- *adjusted_insn_addr = buildaddr;
- if (s390_relocate_instruction (&buildaddr, tpaddr, is_64))
- {
- sprintf (err, "E.Could not relocate instruction for tracepoint.");
- return 1;
- }
- *adjusted_insn_addr_end = buildaddr;
-
- /* Finally, write a jump back to the program. */
-
- loffset = (tpaddr + orig_size) - buildaddr;
- loffset >>= 1;
- offset = loffset;
- if (is_64 && offset != loffset)
- {
- sprintf (err,
- "E.Jump back from jump pad too far from tracepoint "
- "(offset 0x%" PRIx64 " > int33).", loffset);
- return 1;
- }
- memcpy (jbuf + 2, &offset, 4);
- append_insns (&buildaddr, sizeof jbuf, jbuf);
-
- /* 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. */
- loffset = *jump_entry - tpaddr;
- loffset >>= 1;
- offset = loffset;
- if (is_64 && offset != loffset)
- {
- sprintf (err,
- "E.Jump back from jump pad too far from tracepoint "
- "(offset 0x%" PRIx64 " > int33).", loffset);
- return 1;
- }
- memcpy (jbuf + 2, &offset, 4);
- memcpy (jjump_pad_insn, jbuf, sizeof jbuf);
- *jjump_pad_insn_size = sizeof jbuf;
-
- /* Return the end address of our pad. */
- *jump_entry = buildaddr;
-
- return 0;
-}
-
-/* Implementation of linux_target_ops method
- "get_min_fast_tracepoint_insn_len". */
-
-static int
-s390_get_min_fast_tracepoint_insn_len (void)
-{
- /* We only support using 6-byte jumps to reach the tracepoint code.
- If the tracepoint buffer were allocated sufficiently close (64kiB)
- to the executable code, and the traced instruction itself was close
- enough to the beginning, we could use 4-byte jumps, but this doesn't
- seem to be worth the effort. */
- return 6;
-}
-
-/* Implementation of linux_target_ops method "get_ipa_tdesc_idx". */
-
-static int
-s390_get_ipa_tdesc_idx (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
- const struct target_desc *tdesc = regcache->tdesc;
-
-#ifdef __s390x__
- if (tdesc == tdesc_s390x_linux64)
- return S390_TDESC_64;
- if (tdesc == tdesc_s390x_linux64v1)
- return S390_TDESC_64V1;
- if (tdesc == tdesc_s390x_linux64v2)
- return S390_TDESC_64V2;
- if (tdesc == tdesc_s390x_te_linux64)
- return S390_TDESC_TE;
- if (tdesc == tdesc_s390x_vx_linux64)
- return S390_TDESC_VX;
- if (tdesc == tdesc_s390x_tevx_linux64)
- return S390_TDESC_TEVX;
- if (tdesc == tdesc_s390x_gs_linux64)
- return S390_TDESC_GS;
-#endif
-
- if (tdesc == tdesc_s390_linux32)
- return S390_TDESC_32;
- if (tdesc == tdesc_s390_linux32v1)
- return S390_TDESC_32V1;
- if (tdesc == tdesc_s390_linux32v2)
- return S390_TDESC_32V2;
- if (tdesc == tdesc_s390_linux64)
- return S390_TDESC_64;
- if (tdesc == tdesc_s390_linux64v1)
- return S390_TDESC_64V1;
- if (tdesc == tdesc_s390_linux64v2)
- return S390_TDESC_64V2;
- if (tdesc == tdesc_s390_te_linux64)
- return S390_TDESC_TE;
- if (tdesc == tdesc_s390_vx_linux64)
- return S390_TDESC_VX;
- if (tdesc == tdesc_s390_tevx_linux64)
- return S390_TDESC_TEVX;
- if (tdesc == tdesc_s390_gs_linux64)
- return S390_TDESC_GS;
-
- return 0;
-}
-
-/* Appends given buffer to current_insn_ptr in the target. */
-
-static void
-add_insns (const unsigned char *start, int len)
-{
- CORE_ADDR buildaddr = current_insn_ptr;
-
- if (debug_threads)
- debug_printf ("Adding %d bytes of insn at %s\n",
- len, paddress (buildaddr));
-
- append_insns (&buildaddr, len, start);
- current_insn_ptr = buildaddr;
-}
-
-/* Register usage in emit:
-
- - %r0, %r1: temp
- - %r2: top of stack (high word for 31-bit)
- - %r3: low word of top of stack (for 31-bit)
- - %r4, %r5: temp
- - %r6, %r7, %r8: don't use
- - %r9: saved arg1
- - %r10: saved arg2
- - %r11: frame pointer
- - %r12: saved top of stack for void_call_2 (high word for 31-bit)
- - %r13: low word of saved top of stack (for 31-bit)
- - %r14: return address for calls
- - %r15: stack pointer
-
- */
-
-/* The "emit_prologue" emit_ops method for s390. */
-
-static void
-s390_emit_prologue (void)
-{
- static const unsigned char buf[] = {
- 0x90, 0x9f, 0xf0, 0x24, /* stm %r9, %r15, 0x24(%r15) */
- 0x18, 0x92, /* lr %r9, %r2 */
- 0x18, 0xa3, /* lr %r10, %r3 */
- 0x18, 0xbf, /* lr %r11, %r15 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_epilogue" emit_ops method for s390. */
-
-static void
-s390_emit_epilogue (void)
-{
- static const unsigned char buf[] = {
- 0x90, 0x23, 0xa0, 0x00, /* stm %r2, %r3, 0(%r10) */
- 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
- 0x98, 0x9f, 0xb0, 0x24, /* lm %r9, %r15, 0x24(%r11) */
- 0x07, 0xfe, /* br %r14 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_add" emit_ops method for s390. */
-
-static void
-s390_emit_add (void)
-{
- static const unsigned char buf[] = {
- 0x5e, 0x30, 0xf0, 0x04, /* al %r3, 4(%r15) */
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x98, /* al %r2, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_sub" emit_ops method for s390. */
-
-static void
-s390_emit_sub (void)
-{
- static const unsigned char buf[] = {
- 0x98, 0x45, 0xf0, 0x00, /* lm %r4, %r5, 0(%r15) */
- 0x1f, 0x53, /* slr %r5, %r3 */
- 0xb9, 0x99, 0x00, 0x42, /* slbr %r4, %r2 */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- 0x18, 0x35, /* lr %r3, %r5 */
- 0x18, 0x24, /* lr %r2, %r4 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_mul" emit_ops method for s390. */
-
-static void
-s390_emit_mul (void)
-{
- emit_error = 1;
-}
-
-/* The "emit_lsh" emit_ops method for s390. */
-
-static void
-s390_emit_lsh (void)
-{
- static const unsigned char buf[] = {
- 0x18, 0x43, /* lr %r4, %r3 */
- 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
- 0x8d, 0x20, 0x40, 0x00, /* sldl %r2, 0(%r4) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_rsh_signed" emit_ops method for s390. */
-
-static void
-s390_emit_rsh_signed (void)
-{
- static const unsigned char buf[] = {
- 0x18, 0x43, /* lr %r4, %r3 */
- 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
- 0x8e, 0x20, 0x40, 0x00, /* srda %r2, 0(%r4) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_rsh_unsigned" emit_ops method for s390. */
-
-static void
-s390_emit_rsh_unsigned (void)
-{
- static const unsigned char buf[] = {
- 0x18, 0x43, /* lr %r4, %r3 */
- 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
- 0x8c, 0x20, 0x40, 0x00, /* srdl %r2, 0(%r4) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_ext" emit_ops method for s390. */
-
-static void
-s390_emit_ext (int arg)
-{
- unsigned char buf[] = {
- 0x8d, 0x20, 0x00, (unsigned char) (64 - arg), /* sldl %r2, <64-arg> */
- 0x8e, 0x20, 0x00, (unsigned char) (64 - arg), /* srda %r2, <64-arg> */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_log_not" emit_ops method for s390. */
-
-static void
-s390_emit_log_not (void)
-{
- static const unsigned char buf[] = {
- 0x16, 0x23, /* or %r2, %r3 */
- 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
- 0xa7, 0x38, 0x00, 0x00, /* lhi %r3, 0 */
- 0xa7, 0x74, 0x00, 0x04, /* jne .Lskip */
- 0xa7, 0x38, 0x00, 0x01, /* lhi %r3, 1 */
- /* .Lskip: */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_and" emit_ops method for s390. */
-
-static void
-s390_emit_bit_and (void)
-{
- static const unsigned char buf[] = {
- 0x54, 0x20, 0xf0, 0x00, /* n %r2, 0(%r15) */
- 0x54, 0x30, 0xf0, 0x04, /* n %r3, 4(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_or" emit_ops method for s390. */
-
-static void
-s390_emit_bit_or (void)
-{
- static const unsigned char buf[] = {
- 0x56, 0x20, 0xf0, 0x00, /* o %r2, 0(%r15) */
- 0x56, 0x30, 0xf0, 0x04, /* o %r3, 4(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_xor" emit_ops method for s390. */
-
-static void
-s390_emit_bit_xor (void)
-{
- static const unsigned char buf[] = {
- 0x57, 0x20, 0xf0, 0x00, /* x %r2, 0(%r15) */
- 0x57, 0x30, 0xf0, 0x04, /* x %r3, 4(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_not" emit_ops method for s390. */
-
-static void
-s390_emit_bit_not (void)
-{
- static const unsigned char buf[] = {
- 0xa7, 0x48, 0xff, 0xff, /* lhi %r4, -1 */
- 0x17, 0x24, /* xr %r2, %r4 */
- 0x17, 0x34, /* xr %r3, %r4 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_equal" emit_ops method for s390. */
-
-static void
-s390_emit_equal (void)
-{
- s390_emit_bit_xor ();
- s390_emit_log_not ();
-}
-
-/* The "emit_less_signed" emit_ops method for s390. */
-
-static void
-s390_emit_less_signed (void)
-{
- static const unsigned char buf[] = {
- 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
- 0xa7, 0x24, 0x00, 0x0c, /* jh .Lless */
- 0xa7, 0x44, 0x00, 0x06, /* jl .Lhigh */
- 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
- 0xa7, 0x24, 0x00, 0x06, /* jh .Lless */
- /* .Lhigh: */
- 0xa7, 0x38, 0x00, 0x00, /* lhi %r3, 0 */
- 0xa7, 0xf4, 0x00, 0x04, /* j .Lend */
- /* .Lless: */
- 0xa7, 0x38, 0x00, 0x01, /* lhi %r3, 1 */
- /* .Lend: */
- 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_less_unsigned" emit_ops method for s390. */
-
-static void
-s390_emit_less_unsigned (void)
-{
- static const unsigned char buf[] = {
- 0x55, 0x20, 0xf0, 0x00, /* cl %r2, 0(%r15) */
- 0xa7, 0x24, 0x00, 0x0c, /* jh .Lless */
- 0xa7, 0x44, 0x00, 0x06, /* jl .Lhigh */
- 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
- 0xa7, 0x24, 0x00, 0x06, /* jh .Lless */
- /* .Lhigh: */
- 0xa7, 0x38, 0x00, 0x00, /* lhi %r3, 0 */
- 0xa7, 0xf4, 0x00, 0x04, /* j .Lend */
- /* .Lless: */
- 0xa7, 0x38, 0x00, 0x01, /* lhi %r3, 1 */
- /* .Lend: */
- 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_ref" emit_ops method for s390. */
-
-static void
-s390_emit_ref (int size)
-{
- static const unsigned char buf1[] = {
- 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
- 0x43, 0x30, 0x30, 0x00, /* ic %r3, 0(%r3) */
- };
- static const unsigned char buf2[] = {
- 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
- 0x48, 0x30, 0x30, 0x00, /* lh %r3, 0(%r3) */
- };
- static const unsigned char buf4[] = {
- 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
- 0x58, 0x30, 0x30, 0x00, /* l %r3, 0(%r3) */
- };
- static const unsigned char buf8[] = {
- 0x98, 0x23, 0x30, 0x00, /* lm %r2, %r3, 0(%r3) */
- };
- switch (size)
- {
- case 1:
- add_insns (buf1, sizeof buf1);
- break;
- case 2:
- add_insns (buf2, sizeof buf2);
- break;
- case 4:
- add_insns (buf4, sizeof buf4);
- break;
- case 8:
- add_insns (buf8, sizeof buf8);
- break;
- default:
- emit_error = 1;
- }
-}
-
-/* The "emit_if_goto" emit_ops method for s390. */
-
-static void
-s390_emit_if_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0x16, 0x23, /* or %r2, %r3 */
- 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00 /* jgne <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 12;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_goto" emit_ops method for s390 and s390x. */
-
-static void
-s390_emit_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 2;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "write_goto_address" emit_ops method for s390 and s390x. */
-
-static void
-s390_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
-{
- long diff = ((long) (to - (from - 2))) / 2;
- int sdiff = diff;
- unsigned char buf[sizeof sdiff];
-
- /* We're only doing 4-byte sizes at the moment. */
- if (size != sizeof sdiff || sdiff != diff)
- {
- emit_error = 1;
- return;
- }
-
- memcpy (buf, &sdiff, sizeof sdiff);
- target_write_memory (from, buf, sizeof sdiff);
-}
-
-/* Preparation for emitting a literal pool of given size. Loads the address
- of the pool into %r1, and jumps over it. Called should emit the pool data
- immediately afterwards. Used for both s390 and s390x. */
-
-static void
-s390_emit_litpool (int size)
-{
- static const unsigned char nop[] = {
- 0x07, 0x07,
- };
- unsigned char buf[] = {
- 0xa7, 0x15, 0x00,
- (unsigned char) ((size + 4) / 2), /* bras %r1, .Lend+size */
- /* .Lend: */
- };
- if (size == 4)
- {
- /* buf needs to start at even halfword for litpool to be aligned */
- if (current_insn_ptr & 2)
- add_insns (nop, sizeof nop);
- }
- else
- {
- while ((current_insn_ptr & 6) != 4)
- add_insns (nop, sizeof nop);
- }
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_const" emit_ops method for s390. */
-
-static void
-s390_emit_const (LONGEST num)
-{
- unsigned long long n = num;
- unsigned char buf_s[] = {
- /* lhi %r3, <num> */
- 0xa7, 0x38,
- (unsigned char) (num >> 8), (unsigned char) num,
- /* xr %r2, %r2 */
- 0x17, 0x22,
- };
- static const unsigned char buf_l[] = {
- 0x98, 0x23, 0x10, 0x00, /* lm %r2, %r3, 0(%r1) */
- };
- if (num < 0x8000 && num >= 0)
- {
- add_insns (buf_s, sizeof buf_s);
- }
- else
- {
- s390_emit_litpool (8);
- add_insns ((unsigned char *) &n, sizeof n);
- add_insns (buf_l, sizeof buf_l);
- }
-}
-
-/* The "emit_call" emit_ops method for s390. */
-
-static void
-s390_emit_call (CORE_ADDR fn)
-{
- unsigned int n = fn;
- static const unsigned char buf[] = {
- 0x58, 0x10, 0x10, 0x00, /* l %r1, 0(%r1) */
- 0xa7, 0xfa, 0xff, 0xa0, /* ahi %r15, -0x60 */
- 0x0d, 0xe1, /* basr %r14, %r1 */
- 0xa7, 0xfa, 0x00, 0x60, /* ahi %r15, 0x60 */
- };
- s390_emit_litpool (4);
- add_insns ((unsigned char *) &n, sizeof n);
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_reg" emit_ops method for s390. */
-
-static void
-s390_emit_reg (int reg)
-{
- unsigned char bufpre[] = {
- /* lr %r2, %r9 */
- 0x18, 0x29,
- /* lhi %r3, <reg> */
- 0xa7, 0x38, (unsigned char) (reg >> 8), (unsigned char) reg,
- };
- add_insns (bufpre, sizeof bufpre);
- s390_emit_call (get_raw_reg_func_addr ());
-}
-
-/* The "emit_pop" emit_ops method for s390. */
-
-static void
-s390_emit_pop (void)
-{
- static const unsigned char buf[] = {
- 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_stack_flush" emit_ops method for s390. */
-
-static void
-s390_emit_stack_flush (void)
-{
- static const unsigned char buf[] = {
- 0xa7, 0xfa, 0xff, 0xf8, /* ahi %r15, -8 */
- 0x90, 0x23, 0xf0, 0x00, /* stm %r2, %r3, 0(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_zero_ext" emit_ops method for s390. */
-
-static void
-s390_emit_zero_ext (int arg)
-{
- unsigned char buf[] = {
- 0x8d, 0x20, 0x00, (unsigned char) (64 - arg), /* sldl %r2, <64-arg> */
- 0x8c, 0x20, 0x00, (unsigned char) (64 - arg), /* srdl %r2, <64-arg> */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_swap" emit_ops method for s390. */
-
-static void
-s390_emit_swap (void)
-{
- static const unsigned char buf[] = {
- 0x98, 0x45, 0xf0, 0x00, /* lm %r4, %r5, 0(%r15) */
- 0x90, 0x23, 0xf0, 0x00, /* stm %r2, %r3, 0(%r15) */
- 0x18, 0x24, /* lr %r2, %r4 */
- 0x18, 0x35, /* lr %r3, %r5 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_stack_adjust" emit_ops method for s390. */
-
-static void
-s390_emit_stack_adjust (int n)
-{
- unsigned char buf[] = {
- /* ahi %r15, 8*n */
- 0xa7, 0xfa,
- (unsigned char ) (n * 8 >> 8), (unsigned char) (n * 8),
- };
- add_insns (buf, sizeof buf);
-}
-
-/* Sets %r2 to a 32-bit constant. */
-
-static void
-s390_emit_set_r2 (int arg1)
-{
- unsigned char buf_s[] = {
- /* lhi %r2, <arg1> */
- 0xa7, 0x28, (unsigned char) (arg1 >> 8), (unsigned char) arg1,
- };
- static const unsigned char buf_l[] = {
- 0x58, 0x20, 0x10, 0x00, /* l %r2, 0(%r1) */
- };
- if (arg1 < 0x8000 && arg1 >= -0x8000)
- {
- add_insns (buf_s, sizeof buf_s);
- }
- else
- {
- s390_emit_litpool (4);
- add_insns ((unsigned char *) &arg1, sizeof arg1);
- add_insns (buf_l, sizeof buf_l);
- }
-}
-
-/* The "emit_int_call_1" emit_ops method for s390. */
-
-static void
-s390_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- /* FN's prototype is `LONGEST(*fn)(int)'. */
- s390_emit_set_r2 (arg1);
- s390_emit_call (fn);
-}
-
-/* The "emit_void_call_2" emit_ops method for s390. */
-
-static void
-s390_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- /* FN's prototype is `void(*fn)(int,LONGEST)'. */
- static const unsigned char buf[] = {
- 0x18, 0xc2, /* lr %r12, %r2 */
- 0x18, 0xd3, /* lr %r13, %r3 */
- 0x18, 0x43, /* lr %r4, %r3 */
- 0x18, 0x32, /* lr %r3, %r2 */
- };
- static const unsigned char buf2[] = {
- 0x18, 0x2c, /* lr %r2, %r12 */
- 0x18, 0x3d, /* lr %r3, %r13 */
- };
- add_insns (buf, sizeof buf);
- s390_emit_set_r2 (arg1);
- s390_emit_call (fn);
- add_insns (buf2, sizeof buf2);
-}
-
-/* The "emit_eq_goto" emit_ops method for s390. */
-
-static void
-s390_emit_eq_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0x57, 0x20, 0xf0, 0x00, /* x %r2, 0(%r15) */
- 0x57, 0x30, 0xf0, 0x04, /* x %r3, 4(%r15) */
- 0x16, 0x23, /* or %r2, %r3 */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0x84, 0x00, 0x00, 0x00, 0x00, /* jge <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 20;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_ne_goto" emit_ops method for s390. */
-
-static void
-s390_emit_ne_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0x57, 0x20, 0xf0, 0x00, /* x %r2, 0(%r15) */
- 0x57, 0x30, 0xf0, 0x04, /* x %r3, 4(%r15) */
- 0x16, 0x23, /* or %r2, %r3 */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, /* jgne <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 20;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_lt_goto" emit_ops method for s390. */
-
-static void
-s390_emit_lt_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
- 0xa7, 0x24, 0x00, 0x0e, /* jh .Ltrue */
- 0xa7, 0x44, 0x00, 0x06, /* jl .Lfalse */
- 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
- 0xa7, 0x24, 0x00, 0x08, /* jh .Ltrue */
- /* .Lfalse: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
- /* .Ltrue: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
- /* .Lend: */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 42;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_le_goto" emit_ops method for s390. */
-
-static void
-s390_emit_le_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
- 0xa7, 0x24, 0x00, 0x0e, /* jh .Ltrue */
- 0xa7, 0x44, 0x00, 0x06, /* jl .Lfalse */
- 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
- 0xa7, 0xa4, 0x00, 0x08, /* jhe .Ltrue */
- /* .Lfalse: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
- /* .Ltrue: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
- /* .Lend: */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 42;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_gt_goto" emit_ops method for s390. */
-
-static void
-s390_emit_gt_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
- 0xa7, 0x44, 0x00, 0x0e, /* jl .Ltrue */
- 0xa7, 0x24, 0x00, 0x06, /* jh .Lfalse */
- 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
- 0xa7, 0x44, 0x00, 0x08, /* jl .Ltrue */
- /* .Lfalse: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
- /* .Ltrue: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
- /* .Lend: */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 42;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_ge_goto" emit_ops method for s390. */
-
-static void
-s390_emit_ge_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
- 0xa7, 0x44, 0x00, 0x0e, /* jl .Ltrue */
- 0xa7, 0x24, 0x00, 0x06, /* jh .Lfalse */
- 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
- 0xa7, 0xc4, 0x00, 0x08, /* jle .Ltrue */
- /* .Lfalse: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
- /* .Ltrue: */
- 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
- /* .Lend: */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 42;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_ops" structure for s390. Named _impl to avoid name
- collision with s390_emit_ops function. */
-
-static struct emit_ops s390_emit_ops_impl =
- {
- s390_emit_prologue,
- s390_emit_epilogue,
- s390_emit_add,
- s390_emit_sub,
- s390_emit_mul,
- s390_emit_lsh,
- s390_emit_rsh_signed,
- s390_emit_rsh_unsigned,
- s390_emit_ext,
- s390_emit_log_not,
- s390_emit_bit_and,
- s390_emit_bit_or,
- s390_emit_bit_xor,
- s390_emit_bit_not,
- s390_emit_equal,
- s390_emit_less_signed,
- s390_emit_less_unsigned,
- s390_emit_ref,
- s390_emit_if_goto,
- s390_emit_goto,
- s390_write_goto_address,
- s390_emit_const,
- s390_emit_call,
- s390_emit_reg,
- s390_emit_pop,
- s390_emit_stack_flush,
- s390_emit_zero_ext,
- s390_emit_swap,
- s390_emit_stack_adjust,
- s390_emit_int_call_1,
- s390_emit_void_call_2,
- s390_emit_eq_goto,
- s390_emit_ne_goto,
- s390_emit_lt_goto,
- s390_emit_le_goto,
- s390_emit_gt_goto,
- s390_emit_ge_goto
- };
-
-#ifdef __s390x__
-
-/* The "emit_prologue" emit_ops method for s390x. */
-
-static void
-s390x_emit_prologue (void)
-{
- static const unsigned char buf[] = {
- 0xeb, 0x9f, 0xf0, 0x48, 0x00, 0x24, /* stmg %r9, %r15, 0x48(%r15) */
- 0xb9, 0x04, 0x00, 0x92, /* lgr %r9, %r2 */
- 0xb9, 0x04, 0x00, 0xa3, /* lgr %r10, %r3 */
- 0xb9, 0x04, 0x00, 0xbf, /* lgr %r11, %r15 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_epilogue" emit_ops method for s390x. */
-
-static void
-s390x_emit_epilogue (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xa0, 0x00, 0x00, 0x24, /* stg %r2, 0(%r10) */
- 0xa7, 0x29, 0x00, 0x00, /* lghi %r2, 0 */
- 0xeb, 0x9f, 0xf0, 0x48, 0x00, 0x04, /* lmg %r9, %r15, 0x48(%r15) */
- 0x07, 0xfe, /* br %r14 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_add" emit_ops method for s390x. */
-
-static void
-s390x_emit_add (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x0a, /* alg %r2, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_sub" emit_ops method for s390x. */
-
-static void
-s390x_emit_sub (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
- 0xb9, 0x0b, 0x00, 0x32, /* slgr %r3, %r2 */
- 0xb9, 0x04, 0x00, 0x23, /* lgr %r2, %r3 */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_mul" emit_ops method for s390x. */
-
-static void
-s390x_emit_mul (void)
-{
- emit_error = 1;
-}
-
-/* The "emit_lsh" emit_ops method for s390x. */
-
-static void
-s390x_emit_lsh (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
- 0xeb, 0x23, 0x20, 0x00, 0x00, 0x0d, /* sllg %r2, %r3, 0(%r2) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_rsh_signed" emit_ops method for s390x. */
-
-static void
-s390x_emit_rsh_signed (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
- 0xeb, 0x23, 0x20, 0x00, 0x00, 0x0a, /* srag %r2, %r3, 0(%r2) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_rsh_unsigned" emit_ops method for s390x. */
-
-static void
-s390x_emit_rsh_unsigned (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
- 0xeb, 0x23, 0x20, 0x00, 0x00, 0x0c, /* srlg %r2, %r3, 0(%r2) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_ext" emit_ops method for s390x. */
-
-static void
-s390x_emit_ext (int arg)
-{
- unsigned char buf[] = {
- /* sllg %r2, %r2, <64-arg> */
- 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0d,
- /* srag %r2, %r2, <64-arg> */
- 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0a,
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_log_not" emit_ops method for s390x. */
-
-static void
-s390x_emit_log_not (void)
-{
- static const unsigned char buf[] = {
- 0xb9, 0x00, 0x00, 0x22, /* lpgr %r2, %r2 */
- 0xa7, 0x2b, 0xff, 0xff, /* aghi %r2, -1 */
- 0xeb, 0x22, 0x00, 0x3f, 0x00, 0x0c, /* srlg %r2, %r2, 63 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_and" emit_ops method for s390x. */
-
-static void
-s390x_emit_bit_and (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x80, /* ng %r2, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_or" emit_ops method for s390x. */
-
-static void
-s390x_emit_bit_or (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x81, /* og %r2, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_xor" emit_ops method for s390x. */
-
-static void
-s390x_emit_bit_xor (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x82, /* xg %r2, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_bit_not" emit_ops method for s390x. */
-
-static void
-s390x_emit_bit_not (void)
-{
- static const unsigned char buf[] = {
- 0xa7, 0x39, 0xff, 0xff, /* lghi %r3, -1 */
- 0xb9, 0x82, 0x00, 0x23, /* xgr %r2, %r3 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_equal" emit_ops method for s390x. */
-
-static void
-s390x_emit_equal (void)
-{
- s390x_emit_bit_xor ();
- s390x_emit_log_not ();
-}
-
-/* The "emit_less_signed" emit_ops method for s390x. */
-
-static void
-s390x_emit_less_signed (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
- 0xa7, 0x29, 0x00, 0x01, /* lghi %r2, 1 */
- 0xa7, 0x24, 0x00, 0x04, /* jh .Lend */
- 0xa7, 0x29, 0x00, 0x00, /* lghi %r2, 0 */
- /* .Lend: */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_less_unsigned" emit_ops method for s390x. */
-
-static void
-s390x_emit_less_unsigned (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x21, /* clg %r2, 0(%r15) */
- 0xa7, 0x29, 0x00, 0x01, /* lghi %r2, 1 */
- 0xa7, 0x24, 0x00, 0x04, /* jh .Lend */
- 0xa7, 0x29, 0x00, 0x00, /* lghi %r2, 0 */
- /* .Lend: */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_ref" emit_ops method for s390x. */
-
-static void
-s390x_emit_ref (int size)
-{
- static const unsigned char buf1[] = {
- 0xe3, 0x20, 0x20, 0x00, 0x00, 0x90, /* llgc %r2, 0(%r2) */
- };
- static const unsigned char buf2[] = {
- 0xe3, 0x20, 0x20, 0x00, 0x00, 0x91 /* llgh %r2, 0(%r2) */
- };
- static const unsigned char buf4[] = {
- 0xe3, 0x20, 0x20, 0x00, 0x00, 0x16, /* llgf %r2, 0(%r2) */
- };
- static const unsigned char buf8[] = {
- 0xe3, 0x20, 0x20, 0x00, 0x00, 0x04, /* lg %r2, 0(%r2) */
- };
- switch (size)
- {
- case 1:
- add_insns (buf1, sizeof buf1);
- break;
- case 2:
- add_insns (buf2, sizeof buf2);
- break;
- case 4:
- add_insns (buf4, sizeof buf4);
- break;
- case 8:
- add_insns (buf8, sizeof buf8);
- break;
- default:
- emit_error = 1;
- }
-}
-
-/* The "emit_if_goto" emit_ops method for s390x. */
-
-static void
-s390x_emit_if_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xb9, 0x02, 0x00, 0x22, /* ltgr %r2, %r2 */
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x04, /* lg %r2, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, /* jgne <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 16;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_const" emit_ops method for s390x. */
-
-static void
-s390x_emit_const (LONGEST num)
-{
- unsigned long long n = num;
- unsigned char buf_s[] = {
- /* lghi %r2, <num> */
- 0xa7, 0x29, (unsigned char) (num >> 8), (unsigned char) num,
- };
- static const unsigned char buf_l[] = {
- 0xe3, 0x20, 0x10, 0x00, 0x00, 0x04, /* lg %r2, 0(%r1) */
- };
- if (num < 0x8000 && num >= -0x8000)
- {
- add_insns (buf_s, sizeof buf_s);
- }
- else
- {
- s390_emit_litpool (8);
- add_insns ((unsigned char *) &n, sizeof n);
- add_insns (buf_l, sizeof buf_l);
- }
-}
-
-/* The "emit_call" emit_ops method for s390x. */
-
-static void
-s390x_emit_call (CORE_ADDR fn)
-{
- unsigned long n = fn;
- static const unsigned char buf[] = {
- 0xe3, 0x10, 0x10, 0x00, 0x00, 0x04, /* lg %r1, 0(%r1) */
- 0xa7, 0xfb, 0xff, 0x60, /* aghi %r15, -0xa0 */
- 0x0d, 0xe1, /* basr %r14, %r1 */
- 0xa7, 0xfb, 0x00, 0xa0, /* aghi %r15, 0xa0 */
- };
- s390_emit_litpool (8);
- add_insns ((unsigned char *) &n, sizeof n);
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_reg" emit_ops method for s390x. */
-
-static void
-s390x_emit_reg (int reg)
-{
- unsigned char buf[] = {
- /* lgr %r2, %r9 */
- 0xb9, 0x04, 0x00, 0x29,
- /* lghi %r3, <reg> */
- 0xa7, 0x39, (unsigned char) (reg >> 8), (unsigned char) reg,
- };
- add_insns (buf, sizeof buf);
- s390x_emit_call (get_raw_reg_func_addr ());
-}
-
-/* The "emit_pop" emit_ops method for s390x. */
-
-static void
-s390x_emit_pop (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x04, /* lg %r2, 0(%r15) */
- 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_stack_flush" emit_ops method for s390x. */
-
-static void
-s390x_emit_stack_flush (void)
-{
- static const unsigned char buf[] = {
- 0xa7, 0xfb, 0xff, 0xf8, /* aghi %r15, -8 */
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x24, /* stg %r2, 0(%r15) */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_zero_ext" emit_ops method for s390x. */
-
-static void
-s390x_emit_zero_ext (int arg)
-{
- unsigned char buf[] = {
- /* sllg %r2, %r2, <64-arg> */
- 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0d,
- /* srlg %r2, %r2, <64-arg> */
- 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0c,
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_swap" emit_ops method for s390x. */
-
-static void
-s390x_emit_swap (void)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x24, /* stg %r2, 0(%r15) */
- 0xb9, 0x04, 0x00, 0x23, /* lgr %r2, %r3 */
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_stack_adjust" emit_ops method for s390x. */
-
-static void
-s390x_emit_stack_adjust (int n)
-{
- unsigned char buf[] = {
- /* aghi %r15, 8*n */
- 0xa7, 0xfb,
- (unsigned char) (n * 8 >> 8), (unsigned char) (n * 8),
- };
- add_insns (buf, sizeof buf);
-}
-
-/* The "emit_int_call_1" emit_ops method for s390x. */
-
-static void
-s390x_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- /* FN's prototype is `LONGEST(*fn)(int)'. */
- s390x_emit_const (arg1);
- s390x_emit_call (fn);
-}
-
-/* The "emit_void_call_2" emit_ops method for s390x. */
-
-static void
-s390x_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- /* FN's prototype is `void(*fn)(int,LONGEST)'. */
- static const unsigned char buf[] = {
- 0xb9, 0x04, 0x00, 0x32, /* lgr %r3, %r2 */
- 0xb9, 0x04, 0x00, 0xc2, /* lgr %r12, %r2 */
- };
- static const unsigned char buf2[] = {
- 0xb9, 0x04, 0x00, 0x2c, /* lgr %r2, %r12 */
- };
- add_insns (buf, sizeof buf);
- s390x_emit_const (arg1);
- s390x_emit_call (fn);
- add_insns (buf2, sizeof buf2);
-}
-
-/* The "emit_eq_goto" emit_ops method for s390x. */
-
-static void
-s390x_emit_eq_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
- 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0x84, 0x00, 0x00, 0x00, 0x00, /* jge <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_ne_goto" emit_ops method for s390x. */
-
-static void
-s390x_emit_ne_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
- 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, /* jgne <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_lt_goto" emit_ops method for s390x. */
-
-static void
-s390x_emit_lt_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
- 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0x24, 0x00, 0x00, 0x00, 0x00, /* jgh <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_le_goto" emit_ops method for s390x. */
-
-static void
-s390x_emit_le_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
- 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0xa4, 0x00, 0x00, 0x00, 0x00, /* jghe <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_gt_goto" emit_ops method for s390x. */
-
-static void
-s390x_emit_gt_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
- 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0x44, 0x00, 0x00, 0x00, 0x00, /* jgl <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_ge_goto" emit_ops method for s390x. */
-
-static void
-s390x_emit_ge_goto (int *offset_p, int *size_p)
-{
- static const unsigned char buf[] = {
- 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
- 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
- 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
- 0xc0, 0xc4, 0x00, 0x00, 0x00, 0x00, /* jgle <fillme> */
- };
- add_insns (buf, sizeof buf);
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-/* The "emit_ops" structure for s390x. */
-
-static struct emit_ops s390x_emit_ops =
- {
- s390x_emit_prologue,
- s390x_emit_epilogue,
- s390x_emit_add,
- s390x_emit_sub,
- s390x_emit_mul,
- s390x_emit_lsh,
- s390x_emit_rsh_signed,
- s390x_emit_rsh_unsigned,
- s390x_emit_ext,
- s390x_emit_log_not,
- s390x_emit_bit_and,
- s390x_emit_bit_or,
- s390x_emit_bit_xor,
- s390x_emit_bit_not,
- s390x_emit_equal,
- s390x_emit_less_signed,
- s390x_emit_less_unsigned,
- s390x_emit_ref,
- s390x_emit_if_goto,
- s390_emit_goto,
- s390_write_goto_address,
- s390x_emit_const,
- s390x_emit_call,
- s390x_emit_reg,
- s390x_emit_pop,
- s390x_emit_stack_flush,
- s390x_emit_zero_ext,
- s390x_emit_swap,
- s390x_emit_stack_adjust,
- s390x_emit_int_call_1,
- s390x_emit_void_call_2,
- s390x_emit_eq_goto,
- s390x_emit_ne_goto,
- s390x_emit_lt_goto,
- s390x_emit_le_goto,
- s390x_emit_gt_goto,
- s390x_emit_ge_goto
- };
-#endif
-
-/* The "emit_ops" linux_target_ops method. */
-
-static struct emit_ops *
-s390_emit_ops (void)
-{
-#ifdef __s390x__
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
-
- if (register_size (regcache->tdesc, 0) == 8)
- return &s390x_emit_ops;
- else
-#endif
- return &s390_emit_ops_impl;
-}
-
-struct linux_target_ops the_low_target = {
- s390_arch_setup,
- s390_regs_info,
- s390_cannot_fetch_register,
- s390_cannot_store_register,
- NULL, /* fetch_register */
- s390_get_pc,
- s390_set_pc,
- NULL, /* breakpoint_kind_from_pc */
- s390_sw_breakpoint_from_kind,
- NULL,
- s390_breakpoint_len,
- s390_breakpoint_at,
- s390_supports_z_point_type,
- NULL,
- NULL,
- NULL,
- NULL,
- s390_collect_ptrace_register,
- s390_supply_ptrace_register,
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- s390_supports_tracepoints,
- s390_get_thread_area,
- s390_install_fast_tracepoint_jump_pad,
- s390_emit_ops,
- s390_get_min_fast_tracepoint_insn_len,
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- s390_supports_hardware_single_step,
- NULL, /* get_syscall_trapinfo */
- s390_get_ipa_tdesc_idx,
-};
-
-void
-initialize_low_arch (void)
-{
- /* Initialize the Linux target descriptions. */
-
- init_registers_s390_linux32 ();
- init_registers_s390_linux32v1 ();
- init_registers_s390_linux32v2 ();
- init_registers_s390_linux64 ();
- init_registers_s390_linux64v1 ();
- init_registers_s390_linux64v2 ();
- init_registers_s390_te_linux64 ();
- init_registers_s390_vx_linux64 ();
- init_registers_s390_tevx_linux64 ();
- init_registers_s390_gs_linux64 ();
-#ifdef __s390x__
- init_registers_s390x_linux64 ();
- init_registers_s390x_linux64v1 ();
- init_registers_s390x_linux64v2 ();
- init_registers_s390x_te_linux64 ();
- init_registers_s390x_vx_linux64 ();
- init_registers_s390x_tevx_linux64 ();
- init_registers_s390x_gs_linux64 ();
-#endif
-
- initialize_regsets_info (&s390_regsets_info);
- initialize_regsets_info (&s390_regsets_info_3264);
-}
--- /dev/null
+/* GNU/Linux S/390 specific low level interface, for the remote server
+ for GDB.
+ Copyright (C) 2001-2020 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/>. */
+
+/* This file is used for both 31-bit and 64-bit S/390 systems. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "elf/common.h"
+#include "ax.h"
+#include "tracepoint.h"
+
+#include <asm/ptrace.h>
+#include "nat/gdb_ptrace.h"
+#include <sys/uio.h>
+#include <elf.h>
+#include <inttypes.h>
+
+#include "linux-s390-tdesc.h"
+
+#ifndef HWCAP_S390_HIGH_GPRS
+#define HWCAP_S390_HIGH_GPRS 512
+#endif
+
+#ifndef HWCAP_S390_TE
+#define HWCAP_S390_TE 1024
+#endif
+
+#ifndef HWCAP_S390_VX
+#define HWCAP_S390_VX 2048
+#endif
+
+#ifndef HWCAP_S390_GS
+#define HWCAP_S390_GS 16384
+#endif
+
+#define s390_num_regs 52
+
+static int s390_regmap[] = {
+ PT_PSWMASK, PT_PSWADDR,
+
+ PT_GPR0, PT_GPR1, PT_GPR2, PT_GPR3,
+ PT_GPR4, PT_GPR5, PT_GPR6, PT_GPR7,
+ PT_GPR8, PT_GPR9, PT_GPR10, PT_GPR11,
+ PT_GPR12, PT_GPR13, PT_GPR14, PT_GPR15,
+
+ PT_ACR0, PT_ACR1, PT_ACR2, PT_ACR3,
+ PT_ACR4, PT_ACR5, PT_ACR6, PT_ACR7,
+ PT_ACR8, PT_ACR9, PT_ACR10, PT_ACR11,
+ PT_ACR12, PT_ACR13, PT_ACR14, PT_ACR15,
+
+ PT_FPC,
+
+#ifndef __s390x__
+ PT_FPR0_HI, PT_FPR1_HI, PT_FPR2_HI, PT_FPR3_HI,
+ PT_FPR4_HI, PT_FPR5_HI, PT_FPR6_HI, PT_FPR7_HI,
+ PT_FPR8_HI, PT_FPR9_HI, PT_FPR10_HI, PT_FPR11_HI,
+ PT_FPR12_HI, PT_FPR13_HI, PT_FPR14_HI, PT_FPR15_HI,
+#else
+ PT_FPR0, PT_FPR1, PT_FPR2, PT_FPR3,
+ PT_FPR4, PT_FPR5, PT_FPR6, PT_FPR7,
+ PT_FPR8, PT_FPR9, PT_FPR10, PT_FPR11,
+ PT_FPR12, PT_FPR13, PT_FPR14, PT_FPR15,
+#endif
+
+ PT_ORIGGPR2,
+};
+
+#define s390_num_regs_3264 68
+
+#ifdef __s390x__
+static int s390_regmap_3264[] = {
+ PT_PSWMASK, PT_PSWADDR,
+
+ PT_GPR0, PT_GPR0, PT_GPR1, PT_GPR1,
+ PT_GPR2, PT_GPR2, PT_GPR3, PT_GPR3,
+ PT_GPR4, PT_GPR4, PT_GPR5, PT_GPR5,
+ PT_GPR6, PT_GPR6, PT_GPR7, PT_GPR7,
+ PT_GPR8, PT_GPR8, PT_GPR9, PT_GPR9,
+ PT_GPR10, PT_GPR10, PT_GPR11, PT_GPR11,
+ PT_GPR12, PT_GPR12, PT_GPR13, PT_GPR13,
+ PT_GPR14, PT_GPR14, PT_GPR15, PT_GPR15,
+
+ PT_ACR0, PT_ACR1, PT_ACR2, PT_ACR3,
+ PT_ACR4, PT_ACR5, PT_ACR6, PT_ACR7,
+ PT_ACR8, PT_ACR9, PT_ACR10, PT_ACR11,
+ PT_ACR12, PT_ACR13, PT_ACR14, PT_ACR15,
+
+ PT_FPC,
+
+ PT_FPR0, PT_FPR1, PT_FPR2, PT_FPR3,
+ PT_FPR4, PT_FPR5, PT_FPR6, PT_FPR7,
+ PT_FPR8, PT_FPR9, PT_FPR10, PT_FPR11,
+ PT_FPR12, PT_FPR13, PT_FPR14, PT_FPR15,
+
+ PT_ORIGGPR2,
+};
+#else
+static int s390_regmap_3264[] = {
+ PT_PSWMASK, PT_PSWADDR,
+
+ -1, PT_GPR0, -1, PT_GPR1,
+ -1, PT_GPR2, -1, PT_GPR3,
+ -1, PT_GPR4, -1, PT_GPR5,
+ -1, PT_GPR6, -1, PT_GPR7,
+ -1, PT_GPR8, -1, PT_GPR9,
+ -1, PT_GPR10, -1, PT_GPR11,
+ -1, PT_GPR12, -1, PT_GPR13,
+ -1, PT_GPR14, -1, PT_GPR15,
+
+ PT_ACR0, PT_ACR1, PT_ACR2, PT_ACR3,
+ PT_ACR4, PT_ACR5, PT_ACR6, PT_ACR7,
+ PT_ACR8, PT_ACR9, PT_ACR10, PT_ACR11,
+ PT_ACR12, PT_ACR13, PT_ACR14, PT_ACR15,
+
+ PT_FPC,
+
+ PT_FPR0_HI, PT_FPR1_HI, PT_FPR2_HI, PT_FPR3_HI,
+ PT_FPR4_HI, PT_FPR5_HI, PT_FPR6_HI, PT_FPR7_HI,
+ PT_FPR8_HI, PT_FPR9_HI, PT_FPR10_HI, PT_FPR11_HI,
+ PT_FPR12_HI, PT_FPR13_HI, PT_FPR14_HI, PT_FPR15_HI,
+
+ PT_ORIGGPR2,
+};
+#endif
+
+
+static int
+s390_cannot_fetch_register (int regno)
+{
+ return 0;
+}
+
+static int
+s390_cannot_store_register (int regno)
+{
+ return 0;
+}
+
+static void
+s390_collect_ptrace_register (struct regcache *regcache, int regno, char *buf)
+{
+ int size = register_size (regcache->tdesc, regno);
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();
+ struct usrregs_info *usr = regs_info->usrregs;
+ int regaddr = usr->regmap[regno];
+
+ if (size < sizeof (long))
+ {
+ memset (buf, 0, sizeof (long));
+
+ if ((regno ^ 1) < usr->num_regs
+ && usr->regmap[regno ^ 1] == regaddr)
+ {
+ collect_register (regcache, regno & ~1, buf);
+ collect_register (regcache, (regno & ~1) + 1,
+ buf + sizeof (long) - size);
+ }
+ else if (regaddr == PT_PSWMASK)
+ {
+ /* Convert 4-byte PSW mask to 8 bytes by clearing bit 12 and copying
+ the basic addressing mode bit from the PSW address. */
+ gdb_byte *addr = (gdb_byte *) alloca (register_size (regcache->tdesc, regno ^ 1));
+ collect_register (regcache, regno, buf);
+ collect_register (regcache, regno ^ 1, addr);
+ buf[1] &= ~0x8;
+ buf[size] |= (addr[0] & 0x80);
+ }
+ else if (regaddr == PT_PSWADDR)
+ {
+ /* Convert 4-byte PSW address to 8 bytes by clearing the addressing
+ mode bit (which gets copied to the PSW mask instead). */
+ collect_register (regcache, regno, buf + sizeof (long) - size);
+ buf[sizeof (long) - size] &= ~0x80;
+ }
+ else if ((regaddr >= PT_GPR0 && regaddr <= PT_GPR15)
+ || regaddr == PT_ORIGGPR2)
+ collect_register (regcache, regno, buf + sizeof (long) - size);
+ else
+ collect_register (regcache, regno, buf);
+ }
+ else if (regaddr != -1)
+ collect_register (regcache, regno, buf);
+}
+
+static void
+s390_supply_ptrace_register (struct regcache *regcache,
+ int regno, const char *buf)
+{
+ int size = register_size (regcache->tdesc, regno);
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();
+ struct usrregs_info *usr = regs_info->usrregs;
+ int regaddr = usr->regmap[regno];
+
+ if (size < sizeof (long))
+ {
+ if ((regno ^ 1) < usr->num_regs
+ && usr->regmap[regno ^ 1] == regaddr)
+ {
+ supply_register (regcache, regno & ~1, buf);
+ supply_register (regcache, (regno & ~1) + 1,
+ buf + sizeof (long) - size);
+ }
+ else if (regaddr == PT_PSWMASK)
+ {
+ /* Convert 8-byte PSW mask to 4 bytes by setting bit 12 and copying
+ the basic addressing mode into the PSW address. */
+ gdb_byte *mask = (gdb_byte *) alloca (size);
+ gdb_byte *addr = (gdb_byte *) alloca (register_size (regcache->tdesc, regno ^ 1));
+ memcpy (mask, buf, size);
+ mask[1] |= 0x8;
+ supply_register (regcache, regno, mask);
+
+ collect_register (regcache, regno ^ 1, addr);
+ addr[0] &= ~0x80;
+ addr[0] |= (buf[size] & 0x80);
+ supply_register (regcache, regno ^ 1, addr);
+ }
+ else if (regaddr == PT_PSWADDR)
+ {
+ /* Convert 8-byte PSW address to 4 bytes by truncating, but
+ keeping the addressing mode bit (which was set from the mask). */
+ gdb_byte *addr = (gdb_byte *) alloca (size);
+ char amode;
+ collect_register (regcache, regno, addr);
+ amode = addr[0] & 0x80;
+ memcpy (addr, buf + sizeof (long) - size, size);
+ addr[0] &= ~0x80;
+ addr[0] |= amode;
+ supply_register (regcache, regno, addr);
+ }
+ else if ((regaddr >= PT_GPR0 && regaddr <= PT_GPR15)
+ || regaddr == PT_ORIGGPR2)
+ supply_register (regcache, regno, buf + sizeof (long) - size);
+ else
+ supply_register (regcache, regno, buf);
+ }
+ else if (regaddr != -1)
+ supply_register (regcache, regno, buf);
+}
+
+/* Provide only a fill function for the general register set. ps_lgetregs
+ will use this for NPTL support. */
+
+static void
+s390_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();
+ struct usrregs_info *usr = regs_info->usrregs;
+
+ for (i = 0; i < usr->num_regs; i++)
+ {
+ if (usr->regmap[i] < PT_PSWMASK
+ || usr->regmap[i] > PT_ACR15)
+ continue;
+
+ s390_collect_ptrace_register (regcache, i,
+ (char *) buf + usr->regmap[i]);
+ }
+}
+
+/* Fill and store functions for extended register sets. */
+
+#ifndef __s390x__
+static void
+s390_fill_gprs_high (struct regcache *regcache, void *buf)
+{
+ int r0h = find_regno (regcache->tdesc, "r0h");
+ int i;
+
+ for (i = 0; i < 16; i++)
+ collect_register (regcache, r0h + 2 * i, (char *) buf + 4 * i);
+}
+
+static void
+s390_store_gprs_high (struct regcache *regcache, const void *buf)
+{
+ int r0h = find_regno (regcache->tdesc, "r0h");
+ int i;
+
+ for (i = 0; i < 16; i++)
+ supply_register (regcache, r0h + 2 * i, (const char *) buf + 4 * i);
+}
+#endif
+
+static void
+s390_store_last_break (struct regcache *regcache, const void *buf)
+{
+ const char *p;
+
+ p = (const char *) buf + 8 - register_size (regcache->tdesc, 0);
+ supply_register_by_name (regcache, "last_break", p);
+}
+
+static void
+s390_fill_system_call (struct regcache *regcache, void *buf)
+{
+ collect_register_by_name (regcache, "system_call", buf);
+}
+
+static void
+s390_store_system_call (struct regcache *regcache, const void *buf)
+{
+ supply_register_by_name (regcache, "system_call", buf);
+}
+
+static void
+s390_store_tdb (struct regcache *regcache, const void *buf)
+{
+ int tdb0 = find_regno (regcache->tdesc, "tdb0");
+ int tr0 = find_regno (regcache->tdesc, "tr0");
+ int i;
+
+ for (i = 0; i < 4; i++)
+ supply_register (regcache, tdb0 + i, (const char *) buf + 8 * i);
+
+ for (i = 0; i < 16; i++)
+ supply_register (regcache, tr0 + i, (const char *) buf + 8 * (16 + i));
+}
+
+static void
+s390_fill_vxrs_low (struct regcache *regcache, void *buf)
+{
+ int v0 = find_regno (regcache->tdesc, "v0l");
+ int i;
+
+ for (i = 0; i < 16; i++)
+ collect_register (regcache, v0 + i, (char *) buf + 8 * i);
+}
+
+static void
+s390_store_vxrs_low (struct regcache *regcache, const void *buf)
+{
+ int v0 = find_regno (regcache->tdesc, "v0l");
+ int i;
+
+ for (i = 0; i < 16; i++)
+ supply_register (regcache, v0 + i, (const char *) buf + 8 * i);
+}
+
+static void
+s390_fill_vxrs_high (struct regcache *regcache, void *buf)
+{
+ int v16 = find_regno (regcache->tdesc, "v16");
+ int i;
+
+ for (i = 0; i < 16; i++)
+ collect_register (regcache, v16 + i, (char *) buf + 16 * i);
+}
+
+static void
+s390_store_vxrs_high (struct regcache *regcache, const void *buf)
+{
+ int v16 = find_regno (regcache->tdesc, "v16");
+ int i;
+
+ for (i = 0; i < 16; i++)
+ supply_register (regcache, v16 + i, (const char *) buf + 16 * i);
+}
+
+static void
+s390_store_gs (struct regcache *regcache, const void *buf)
+{
+ int gsd = find_regno (regcache->tdesc, "gsd");
+ int i;
+
+ for (i = 0; i < 3; i++)
+ supply_register (regcache, gsd + i, (const char *) buf + 8 * (i + 1));
+}
+
+static void
+s390_store_gsbc (struct regcache *regcache, const void *buf)
+{
+ int bc_gsd = find_regno (regcache->tdesc, "bc_gsd");
+ int i;
+
+ for (i = 0; i < 3; i++)
+ supply_register (regcache, bc_gsd + i, (const char *) buf + 8 * (i + 1));
+}
+
+static struct regset_info s390_regsets[] = {
+ { 0, 0, 0, 0, GENERAL_REGS, s390_fill_gregset, NULL },
+#ifndef __s390x__
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_HIGH_GPRS, 0,
+ EXTENDED_REGS, s390_fill_gprs_high, s390_store_gprs_high },
+#endif
+ /* Last break address is read-only; no fill function. */
+ { PTRACE_GETREGSET, -1, NT_S390_LAST_BREAK, 0, EXTENDED_REGS,
+ NULL, s390_store_last_break },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_SYSTEM_CALL, 0,
+ EXTENDED_REGS, s390_fill_system_call, s390_store_system_call },
+ /* TDB is read-only. */
+ { PTRACE_GETREGSET, -1, NT_S390_TDB, 0, EXTENDED_REGS,
+ NULL, s390_store_tdb },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_VXRS_LOW, 0,
+ EXTENDED_REGS, s390_fill_vxrs_low, s390_store_vxrs_low },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_S390_VXRS_HIGH, 0,
+ EXTENDED_REGS, s390_fill_vxrs_high, s390_store_vxrs_high },
+ /* Guarded storage registers are read-only. */
+ { PTRACE_GETREGSET, -1, NT_S390_GS_CB, 0, EXTENDED_REGS,
+ NULL, s390_store_gs },
+ { PTRACE_GETREGSET, -1, NT_S390_GS_BC, 0, EXTENDED_REGS,
+ NULL, s390_store_gsbc },
+ NULL_REGSET
+};
+
+
+static const gdb_byte s390_breakpoint[] = { 0, 1 };
+#define s390_breakpoint_len 2
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+s390_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = s390_breakpoint_len;
+ return s390_breakpoint;
+}
+
+static CORE_ADDR
+s390_get_pc (struct regcache *regcache)
+{
+ if (register_size (regcache->tdesc, 0) == 4)
+ {
+ unsigned int pswa;
+ collect_register_by_name (regcache, "pswa", &pswa);
+ return pswa & 0x7fffffff;
+ }
+ else
+ {
+ unsigned long pc;
+ collect_register_by_name (regcache, "pswa", &pc);
+ return pc;
+ }
+}
+
+static void
+s390_set_pc (struct regcache *regcache, CORE_ADDR newpc)
+{
+ if (register_size (regcache->tdesc, 0) == 4)
+ {
+ unsigned int pswa;
+ collect_register_by_name (regcache, "pswa", &pswa);
+ pswa = (pswa & 0x80000000) | (newpc & 0x7fffffff);
+ supply_register_by_name (regcache, "pswa", &pswa);
+ }
+ else
+ {
+ unsigned long pc = newpc;
+ supply_register_by_name (regcache, "pswa", &pc);
+ }
+}
+
+/* Determine the word size for the given PID, in bytes. */
+
+#ifdef __s390x__
+static int
+s390_get_wordsize (int pid)
+{
+ errno = 0;
+ PTRACE_XFER_TYPE pswm = ptrace (PTRACE_PEEKUSER, pid,
+ (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) 0);
+ if (errno != 0)
+ {
+ warning (_("Couldn't determine word size, assuming 64-bit."));
+ return 8;
+ }
+ /* Derive word size from extended addressing mode (PSW bit 31). */
+ return pswm & (1L << 32) ? 8 : 4;
+}
+#else
+#define s390_get_wordsize(pid) 4
+#endif
+
+static int
+s390_check_regset (int pid, int regset, int regsize)
+{
+ void *buf = alloca (regsize);
+ struct iovec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = regsize;
+
+ if (ptrace (PTRACE_GETREGSET, pid, (long) regset, (long) &iov) >= 0
+ || errno == ENODATA)
+ return 1;
+ return 0;
+}
+
+/* For a 31-bit inferior, whether the kernel supports using the full
+ 64-bit GPRs. */
+static int have_hwcap_s390_high_gprs = 0;
+static int have_hwcap_s390_vx = 0;
+
+static void
+s390_arch_setup (void)
+{
+ const struct target_desc *tdesc;
+ struct regset_info *regset;
+
+ /* Determine word size and HWCAP. */
+ int pid = pid_of (current_thread);
+ int wordsize = s390_get_wordsize (pid);
+ unsigned long hwcap = linux_get_hwcap (wordsize);
+
+ /* Check whether the kernel supports extra register sets. */
+ int have_regset_last_break
+ = s390_check_regset (pid, NT_S390_LAST_BREAK, 8);
+ int have_regset_system_call
+ = s390_check_regset (pid, NT_S390_SYSTEM_CALL, 4);
+ int have_regset_tdb
+ = (s390_check_regset (pid, NT_S390_TDB, 256)
+ && (hwcap & HWCAP_S390_TE) != 0);
+ int have_regset_vxrs
+ = (s390_check_regset (pid, NT_S390_VXRS_LOW, 128)
+ && s390_check_regset (pid, NT_S390_VXRS_HIGH, 256)
+ && (hwcap & HWCAP_S390_VX) != 0);
+ int have_regset_gs
+ = (s390_check_regset (pid, NT_S390_GS_CB, 32)
+ && s390_check_regset (pid, NT_S390_GS_BC, 32)
+ && (hwcap & HWCAP_S390_GS) != 0);
+
+ {
+#ifdef __s390x__
+ if (wordsize == 8)
+ {
+ if (have_regset_gs)
+ tdesc = tdesc_s390x_gs_linux64;
+ else if (have_regset_vxrs)
+ tdesc = (have_regset_tdb ? tdesc_s390x_tevx_linux64 :
+ tdesc_s390x_vx_linux64);
+ else if (have_regset_tdb)
+ tdesc = tdesc_s390x_te_linux64;
+ else if (have_regset_system_call)
+ tdesc = tdesc_s390x_linux64v2;
+ else if (have_regset_last_break)
+ tdesc = tdesc_s390x_linux64v1;
+ else
+ tdesc = tdesc_s390x_linux64;
+ }
+
+ /* For a 31-bit inferior, check whether the kernel supports
+ using the full 64-bit GPRs. */
+ else
+#endif
+ if (hwcap & HWCAP_S390_HIGH_GPRS)
+ {
+ have_hwcap_s390_high_gprs = 1;
+ if (have_regset_gs)
+ tdesc = tdesc_s390_gs_linux64;
+ else if (have_regset_vxrs)
+ tdesc = (have_regset_tdb ? tdesc_s390_tevx_linux64 :
+ tdesc_s390_vx_linux64);
+ else if (have_regset_tdb)
+ tdesc = tdesc_s390_te_linux64;
+ else if (have_regset_system_call)
+ tdesc = tdesc_s390_linux64v2;
+ else if (have_regset_last_break)
+ tdesc = tdesc_s390_linux64v1;
+ else
+ tdesc = tdesc_s390_linux64;
+ }
+ else
+ {
+ /* Assume 31-bit inferior process. */
+ if (have_regset_system_call)
+ tdesc = tdesc_s390_linux32v2;
+ else if (have_regset_last_break)
+ tdesc = tdesc_s390_linux32v1;
+ else
+ tdesc = tdesc_s390_linux32;
+ }
+
+ have_hwcap_s390_vx = have_regset_vxrs;
+ }
+
+ /* Update target_regsets according to available register sets. */
+ for (regset = s390_regsets; regset->size >= 0; regset++)
+ if (regset->get_request == PTRACE_GETREGSET)
+ switch (regset->nt_type)
+ {
+#ifndef __s390x__
+ case NT_S390_HIGH_GPRS:
+ regset->size = have_hwcap_s390_high_gprs ? 64 : 0;
+ break;
+#endif
+ case NT_S390_LAST_BREAK:
+ regset->size = have_regset_last_break ? 8 : 0;
+ break;
+ case NT_S390_SYSTEM_CALL:
+ regset->size = have_regset_system_call ? 4 : 0;
+ break;
+ case NT_S390_TDB:
+ regset->size = have_regset_tdb ? 256 : 0;
+ break;
+ case NT_S390_VXRS_LOW:
+ regset->size = have_regset_vxrs ? 128 : 0;
+ break;
+ case NT_S390_VXRS_HIGH:
+ regset->size = have_regset_vxrs ? 256 : 0;
+ break;
+ case NT_S390_GS_CB:
+ case NT_S390_GS_BC:
+ regset->size = have_regset_gs ? 32 : 0;
+ default:
+ break;
+ }
+
+ current_process ()->tdesc = tdesc;
+}
+
+
+static int
+s390_breakpoint_at (CORE_ADDR pc)
+{
+ unsigned char c[s390_breakpoint_len];
+ read_inferior_memory (pc, c, s390_breakpoint_len);
+ return memcmp (c, s390_breakpoint, s390_breakpoint_len) == 0;
+}
+
+/* Breakpoint/Watchpoint support. */
+
+/* The "supports_z_point_type" linux_target_ops method. */
+
+static int
+s390_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Support for hardware single step. */
+
+static int
+s390_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static struct usrregs_info s390_usrregs_info =
+ {
+ s390_num_regs,
+ s390_regmap,
+ };
+
+static struct regsets_info s390_regsets_info =
+ {
+ s390_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &s390_usrregs_info,
+ &s390_regsets_info
+ };
+
+static struct usrregs_info s390_usrregs_info_3264 =
+ {
+ s390_num_regs_3264,
+ s390_regmap_3264
+ };
+
+static struct regsets_info s390_regsets_info_3264 =
+ {
+ s390_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct regs_info regs_info_3264 =
+ {
+ NULL, /* regset_bitmap */
+ &s390_usrregs_info_3264,
+ &s390_regsets_info_3264
+ };
+
+static const struct regs_info *
+s390_regs_info (void)
+{
+ if (have_hwcap_s390_high_gprs)
+ {
+#ifdef __s390x__
+ const struct target_desc *tdesc = current_process ()->tdesc;
+
+ if (register_size (tdesc, 0) == 4)
+ return ®s_info_3264;
+#else
+ return ®s_info_3264;
+#endif
+ }
+ return ®s_info;
+}
+
+/* The "supports_tracepoints" linux_target_ops method. */
+
+static int
+s390_supports_tracepoints (void)
+{
+ return 1;
+}
+
+/* Implementation of linux_target_ops method "get_thread_area". */
+
+static int
+s390_get_thread_area (int lwpid, CORE_ADDR *addrp)
+{
+ CORE_ADDR res = ptrace (PTRACE_PEEKUSER, lwpid, (long) PT_ACR0, (long) 0);
+#ifdef __s390x__
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+
+ if (register_size (regcache->tdesc, 0) == 4)
+ res &= 0xffffffffull;
+#endif
+ *addrp = res;
+ return 0;
+}
+
+
+/* Fast tracepoint support.
+
+ The register save area on stack is identical for all targets:
+
+ 0x000+i*0x10: VR0-VR31
+ 0x200+i*8: GR0-GR15
+ 0x280+i*4: AR0-AR15
+ 0x2c0: PSWM [64-bit]
+ 0x2c8: PSWA [64-bit]
+ 0x2d0: FPC
+
+ If we're on 31-bit linux, we just don't store the high parts of the GPRs.
+ Likewise, if there's no VX support, we just store the FRs into the slots
+ of low VR halves. The agent code is responsible for rearranging that
+ into regcache. */
+
+/* Code sequence saving GPRs for 31-bit target with no high GPRs. There's
+ one trick used at the very beginning: since there's no way to allocate
+ stack space without destroying CC (lay instruction can do it, but it's
+ only supported on later CPUs), we take 4 different execution paths for
+ every possible value of CC, allocate stack space, save %r0, stuff the
+ CC value in %r0 (shifted to match its position in PSWM high word),
+ then branch to common path. */
+
+static const unsigned char s390_ft_entry_gpr_esa[] = {
+ 0xa7, 0x14, 0x00, 0x1e, /* jo .Lcc3 */
+ 0xa7, 0x24, 0x00, 0x14, /* jh .Lcc2 */
+ 0xa7, 0x44, 0x00, 0x0a, /* jl .Lcc1 */
+ /* CC = 0 */
+ 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
+ 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
+ 0xa7, 0x08, 0x00, 0x00, /* lhi %r0, 0 */
+ 0xa7, 0xf4, 0x00, 0x18, /* j .Lccdone */
+ /* .Lcc1: */
+ 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
+ 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
+ 0xa7, 0x08, 0x10, 0x00, /* lhi %r0, 0x1000 */
+ 0xa7, 0xf4, 0x00, 0x10, /* j .Lccdone */
+ /* .Lcc2: */
+ 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
+ 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
+ 0xa7, 0x08, 0x20, 0x00, /* lhi %r0, 0x2000 */
+ 0xa7, 0xf4, 0x00, 0x08, /* j .Lccdone */
+ /* .Lcc3: */
+ 0xa7, 0xfa, 0xfd, 0x00, /* ahi %r15, -0x300 */
+ 0x50, 0x00, 0xf2, 0x04, /* st %r0, 0x204(%r15) */
+ 0xa7, 0x08, 0x30, 0x00, /* lhi %r0, 0x3000 */
+ /* .Lccdone: */
+ 0x50, 0x10, 0xf2, 0x0c, /* st %r1, 0x20c(%r15) */
+ 0x50, 0x20, 0xf2, 0x14, /* st %r2, 0x214(%r15) */
+ 0x50, 0x30, 0xf2, 0x1c, /* st %r3, 0x21c(%r15) */
+ 0x50, 0x40, 0xf2, 0x24, /* st %r4, 0x224(%r15) */
+ 0x50, 0x50, 0xf2, 0x2c, /* st %r5, 0x22c(%r15) */
+ 0x50, 0x60, 0xf2, 0x34, /* st %r6, 0x234(%r15) */
+ 0x50, 0x70, 0xf2, 0x3c, /* st %r7, 0x23c(%r15) */
+ 0x50, 0x80, 0xf2, 0x44, /* st %r8, 0x244(%r15) */
+ 0x50, 0x90, 0xf2, 0x4c, /* st %r9, 0x24c(%r15) */
+ 0x50, 0xa0, 0xf2, 0x54, /* st %r10, 0x254(%r15) */
+ 0x50, 0xb0, 0xf2, 0x5c, /* st %r11, 0x25c(%r15) */
+ 0x50, 0xc0, 0xf2, 0x64, /* st %r12, 0x264(%r15) */
+ 0x50, 0xd0, 0xf2, 0x6c, /* st %r13, 0x26c(%r15) */
+ 0x50, 0xe0, 0xf2, 0x74, /* st %r14, 0x274(%r15) */
+ /* Compute original value of %r15 and store it. We use ahi instead
+ of la to preserve the whole value, and not just the low 31 bits.
+ This is not particularly important here, but essential in the
+ zarch case where someone might be using the high word of %r15
+ as an extra register. */
+ 0x18, 0x1f, /* lr %r1, %r15 */
+ 0xa7, 0x1a, 0x03, 0x00, /* ahi %r1, 0x300 */
+ 0x50, 0x10, 0xf2, 0x7c, /* st %r1, 0x27c(%r15) */
+};
+
+/* Code sequence saving GPRs for 31-bit target with high GPRs and for 64-bit
+ target. Same as above, except this time we can use load/store multiple,
+ since the 64-bit regs are tightly packed. */
+
+static const unsigned char s390_ft_entry_gpr_zarch[] = {
+ 0xa7, 0x14, 0x00, 0x21, /* jo .Lcc3 */
+ 0xa7, 0x24, 0x00, 0x16, /* jh .Lcc2 */
+ 0xa7, 0x44, 0x00, 0x0b, /* jl .Lcc1 */
+ /* CC = 0 */
+ 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
+ 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
+ 0xa7, 0x08, 0x00, 0x00, /* lhi %r0, 0 */
+ 0xa7, 0xf4, 0x00, 0x1b, /* j .Lccdone */
+ /* .Lcc1: */
+ 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
+ 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
+ 0xa7, 0x08, 0x10, 0x00, /* lhi %r0, 0x1000 */
+ 0xa7, 0xf4, 0x00, 0x12, /* j .Lccdone */
+ /* .Lcc2: */
+ 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
+ 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
+ 0xa7, 0x08, 0x20, 0x00, /* lhi %r0, 0x2000 */
+ 0xa7, 0xf4, 0x00, 0x09, /* j .Lccdone */
+ /* .Lcc3: */
+ 0xa7, 0xfb, 0xfd, 0x00, /* aghi %r15, -0x300 */
+ 0xeb, 0x0e, 0xf2, 0x00, 0x00, 0x24, /* stmg %r0, %r14, 0x200(%r15) */
+ 0xa7, 0x08, 0x30, 0x00, /* lhi %r0, 0x3000 */
+ /* .Lccdone: */
+ 0xb9, 0x04, 0x00, 0x1f, /* lgr %r1, %r15 */
+ 0xa7, 0x1b, 0x03, 0x00, /* aghi %r1, 0x300 */
+ 0xe3, 0x10, 0xf2, 0x78, 0x00, 0x24, /* stg %r1, 0x278(%r15) */
+};
+
+/* Code sequence saving ARs, PSWM and FPC. PSWM has to be assembled from
+ current PSWM (read by epsw) and CC from entry (in %r0). */
+
+static const unsigned char s390_ft_entry_misc[] = {
+ 0x9b, 0x0f, 0xf2, 0x80, /* stam %a0, %a15, 0x20(%%r15) */
+ 0xb9, 0x8d, 0x00, 0x23, /* epsw %r2, %r3 */
+ 0xa7, 0x18, 0xcf, 0xff, /* lhi %r1, ~0x3000 */
+ 0x14, 0x21, /* nr %r2, %r1 */
+ 0x16, 0x20, /* or %r2, %r0 */
+ 0x50, 0x20, 0xf2, 0xc0, /* st %r2, 0x2c0(%r15) */
+ 0x50, 0x30, 0xf2, 0xc4, /* st %r3, 0x2c4(%r15) */
+ 0xb2, 0x9c, 0xf2, 0xd0, /* stfpc 0x2d0(%r15) */
+};
+
+/* Code sequence saving FRs, used if VX not supported. */
+
+static const unsigned char s390_ft_entry_fr[] = {
+ 0x60, 0x00, 0xf0, 0x00, /* std %f0, 0x000(%r15) */
+ 0x60, 0x10, 0xf0, 0x10, /* std %f1, 0x010(%r15) */
+ 0x60, 0x20, 0xf0, 0x20, /* std %f2, 0x020(%r15) */
+ 0x60, 0x30, 0xf0, 0x30, /* std %f3, 0x030(%r15) */
+ 0x60, 0x40, 0xf0, 0x40, /* std %f4, 0x040(%r15) */
+ 0x60, 0x50, 0xf0, 0x50, /* std %f5, 0x050(%r15) */
+ 0x60, 0x60, 0xf0, 0x60, /* std %f6, 0x060(%r15) */
+ 0x60, 0x70, 0xf0, 0x70, /* std %f7, 0x070(%r15) */
+ 0x60, 0x80, 0xf0, 0x80, /* std %f8, 0x080(%r15) */
+ 0x60, 0x90, 0xf0, 0x90, /* std %f9, 0x090(%r15) */
+ 0x60, 0xa0, 0xf0, 0xa0, /* std %f10, 0x0a0(%r15) */
+ 0x60, 0xb0, 0xf0, 0xb0, /* std %f11, 0x0b0(%r15) */
+ 0x60, 0xc0, 0xf0, 0xc0, /* std %f12, 0x0c0(%r15) */
+ 0x60, 0xd0, 0xf0, 0xd0, /* std %f13, 0x0d0(%r15) */
+ 0x60, 0xe0, 0xf0, 0xe0, /* std %f14, 0x0e0(%r15) */
+ 0x60, 0xf0, 0xf0, 0xf0, /* std %f15, 0x0f0(%r15) */
+};
+
+/* Code sequence saving VRs, used if VX not supported. */
+
+static const unsigned char s390_ft_entry_vr[] = {
+ 0xe7, 0x0f, 0xf0, 0x00, 0x00, 0x3e, /* vstm %v0, %v15, 0x000(%r15) */
+ 0xe7, 0x0f, 0xf1, 0x00, 0x0c, 0x3e, /* vstm %v16, %v31, 0x100(%r15) */
+};
+
+/* Code sequence doing the collection call for 31-bit target. %r1 contains
+ the address of the literal pool. */
+
+static const unsigned char s390_ft_main_31[] = {
+ /* Load the literals into registers. */
+ 0x58, 0x50, 0x10, 0x00, /* l %r5, 0x0(%r1) */
+ 0x58, 0x20, 0x10, 0x04, /* l %r2, 0x4(%r1) */
+ 0x58, 0x40, 0x10, 0x08, /* l %r4, 0x8(%r1) */
+ 0x58, 0x60, 0x10, 0x0c, /* l %r6, 0xc(%r1) */
+ /* Save original PSWA (tracepoint address | 0x80000000). */
+ 0x50, 0x50, 0xf2, 0xcc, /* st %r5, 0x2cc(%r15) */
+ /* Construct a collecting_t object at %r15+0x2e0. */
+ 0x50, 0x20, 0xf2, 0xe0, /* st %r2, 0x2e0(%r15) */
+ 0x9b, 0x00, 0xf2, 0xe4, /* stam %a0, %a0, 0x2e4(%r15) */
+ /* Move its address to %r0. */
+ 0x41, 0x00, 0xf2, 0xe0, /* la %r0, 0x2e0(%r15) */
+ /* Take the lock. */
+ /* .Lloop: */
+ 0xa7, 0x18, 0x00, 0x00, /* lhi %r1, 0 */
+ 0xba, 0x10, 0x60, 0x00, /* cs %r1, %r0, 0(%r6) */
+ 0xa7, 0x74, 0xff, 0xfc, /* jne .Lloop */
+ /* Address of the register save block to %r3. */
+ 0x18, 0x3f, /* lr %r3, %r15 */
+ /* Make a stack frame, so that we can call the collector. */
+ 0xa7, 0xfa, 0xff, 0xa0, /* ahi %r15, -0x60 */
+ /* Call it. */
+ 0x0d, 0xe4, /* basr %r14, %r4 */
+ /* And get rid of the stack frame again. */
+ 0x41, 0xf0, 0xf0, 0x60, /* la %r15, 0x60(%r15) */
+ /* Leave the lock. */
+ 0x07, 0xf0, /* br %r0 */
+ 0xa7, 0x18, 0x00, 0x00, /* lhi %r1, 0 */
+ 0x50, 0x10, 0x60, 0x00, /* st %t1, 0(%r6) */
+};
+
+/* Code sequence doing the collection call for 64-bit target. %r1 contains
+ the address of the literal pool. */
+
+static const unsigned char s390_ft_main_64[] = {
+ /* Load the literals into registers. */
+ 0xe3, 0x50, 0x10, 0x00, 0x00, 0x04, /* lg %r5, 0x00(%r1) */
+ 0xe3, 0x20, 0x10, 0x08, 0x00, 0x04, /* lg %r2, 0x08(%r1) */
+ 0xe3, 0x40, 0x10, 0x10, 0x00, 0x04, /* lg %r4, 0x10(%r1) */
+ 0xe3, 0x60, 0x10, 0x18, 0x00, 0x04, /* lg %r6, 0x18(%r1) */
+ /* Save original PSWA (tracepoint address). */
+ 0xe3, 0x50, 0xf2, 0xc8, 0x00, 0x24, /* stg %r5, 0x2c8(%r15) */
+ /* Construct a collecting_t object at %r15+0x2e0. */
+ 0xe3, 0x20, 0xf2, 0xe0, 0x00, 0x24, /* stg %r2, 0x2e0(%r15) */
+ 0x9b, 0x01, 0xf2, 0xe8, /* stam %a0, %a1, 0x2e8(%r15) */
+ /* Move its address to %r0. */
+ 0x41, 0x00, 0xf2, 0xe0, /* la %r0, 0x2e0(%r15) */
+ /* Take the lock. */
+ /* .Lloop: */
+ 0xa7, 0x19, 0x00, 0x00, /* lghi %r1, 0 */
+ 0xeb, 0x10, 0x60, 0x00, 0x00, 0x30, /* csg %r1, %r0, 0(%r6) */
+ 0xa7, 0x74, 0xff, 0xfb, /* jne .Lloop */
+ /* Address of the register save block to %r3. */
+ 0xb9, 0x04, 0x00, 0x3f, /* lgr %r3, %r15 */
+ /* Make a stack frame, so that we can call the collector. */
+ 0xa7, 0xfb, 0xff, 0x60, /* aghi %r15, -0xa0 */
+ /* Call it. */
+ 0x0d, 0xe4, /* basr %r14, %r4 */
+ /* And get rid of the stack frame again. */
+ 0x41, 0xf0, 0xf0, 0xa0, /* la %r15, 0xa0(%r15) */
+ /* Leave the lock. */
+ 0x07, 0xf0, /* br %r0 */
+ 0xa7, 0x19, 0x00, 0x00, /* lghi %r1, 0 */
+ 0xe3, 0x10, 0x60, 0x00, 0x00, 0x24, /* stg %t1, 0(%r6) */
+};
+
+/* Code sequence restoring FRs, for targets with no VX support. */
+
+static const unsigned char s390_ft_exit_fr[] = {
+ 0x68, 0x00, 0xf0, 0x00, /* ld %f0, 0x000(%r15) */
+ 0x68, 0x10, 0xf0, 0x10, /* ld %f1, 0x010(%r15) */
+ 0x68, 0x20, 0xf0, 0x20, /* ld %f2, 0x020(%r15) */
+ 0x68, 0x30, 0xf0, 0x30, /* ld %f3, 0x030(%r15) */
+ 0x68, 0x40, 0xf0, 0x40, /* ld %f4, 0x040(%r15) */
+ 0x68, 0x50, 0xf0, 0x50, /* ld %f5, 0x050(%r15) */
+ 0x68, 0x60, 0xf0, 0x60, /* ld %f6, 0x060(%r15) */
+ 0x68, 0x70, 0xf0, 0x70, /* ld %f7, 0x070(%r15) */
+ 0x68, 0x80, 0xf0, 0x80, /* ld %f8, 0x080(%r15) */
+ 0x68, 0x90, 0xf0, 0x90, /* ld %f9, 0x090(%r15) */
+ 0x68, 0xa0, 0xf0, 0xa0, /* ld %f10, 0x0a0(%r15) */
+ 0x68, 0xb0, 0xf0, 0xb0, /* ld %f11, 0x0b0(%r15) */
+ 0x68, 0xc0, 0xf0, 0xc0, /* ld %f12, 0x0c0(%r15) */
+ 0x68, 0xd0, 0xf0, 0xd0, /* ld %f13, 0x0d0(%r15) */
+ 0x68, 0xe0, 0xf0, 0xe0, /* ld %f14, 0x0e0(%r15) */
+ 0x68, 0xf0, 0xf0, 0xf0, /* ld %f15, 0x0f0(%r15) */
+};
+
+/* Code sequence restoring VRs. */
+
+static const unsigned char s390_ft_exit_vr[] = {
+ 0xe7, 0x0f, 0xf0, 0x00, 0x00, 0x36, /* vlm %v0, %v15, 0x000(%r15) */
+ 0xe7, 0x0f, 0xf1, 0x00, 0x0c, 0x36, /* vlm %v16, %v31, 0x100(%r15) */
+};
+
+/* Code sequence restoring misc registers. As for PSWM, only CC should be
+ modified by C code, so we use the alr instruction to restore it by
+ manufacturing an operand that'll result in the original flags. */
+
+static const unsigned char s390_ft_exit_misc[] = {
+ 0xb2, 0x9d, 0xf2, 0xd0, /* lfpc 0x2d0(%r15) */
+ 0x58, 0x00, 0xf2, 0xc0, /* l %r0, 0x2c0(%r15) */
+ /* Extract CC to high 2 bits of %r0. */
+ 0x88, 0x00, 0x00, 0x0c, /* srl %r0, 12 */
+ 0x89, 0x00, 0x00, 0x1e, /* sll %r0, 30 */
+ /* Add %r0 to itself. Result will be nonzero iff CC bit 0 is set, and
+ will have carry iff CC bit 1 is set - resulting in the same flags
+ as the original. */
+ 0x1e, 0x00, /* alr %r0, %r0 */
+ 0x9a, 0x0f, 0xf2, 0x80, /* lam %a0, %a15, 0x280(%r15) */
+};
+
+/* Code sequence restoring GPRs, for 31-bit targets with no high GPRs. */
+
+static const unsigned char s390_ft_exit_gpr_esa[] = {
+ 0x58, 0x00, 0xf2, 0x04, /* l %r0, 0x204(%r15) */
+ 0x58, 0x10, 0xf2, 0x0c, /* l %r1, 0x20c(%r15) */
+ 0x58, 0x20, 0xf2, 0x14, /* l %r2, 0x214(%r15) */
+ 0x58, 0x30, 0xf2, 0x1c, /* l %r3, 0x21c(%r15) */
+ 0x58, 0x40, 0xf2, 0x24, /* l %r4, 0x224(%r15) */
+ 0x58, 0x50, 0xf2, 0x2c, /* l %r5, 0x22c(%r15) */
+ 0x58, 0x60, 0xf2, 0x34, /* l %r6, 0x234(%r15) */
+ 0x58, 0x70, 0xf2, 0x3c, /* l %r7, 0x23c(%r15) */
+ 0x58, 0x80, 0xf2, 0x44, /* l %r8, 0x244(%r15) */
+ 0x58, 0x90, 0xf2, 0x4c, /* l %r9, 0x24c(%r15) */
+ 0x58, 0xa0, 0xf2, 0x54, /* l %r10, 0x254(%r15) */
+ 0x58, 0xb0, 0xf2, 0x5c, /* l %r11, 0x25c(%r15) */
+ 0x58, 0xc0, 0xf2, 0x64, /* l %r12, 0x264(%r15) */
+ 0x58, 0xd0, 0xf2, 0x6c, /* l %r13, 0x26c(%r15) */
+ 0x58, 0xe0, 0xf2, 0x74, /* l %r14, 0x274(%r15) */
+ 0x58, 0xf0, 0xf2, 0x7c, /* l %r15, 0x27c(%r15) */
+};
+
+/* Code sequence restoring GPRs, for 64-bit targets and 31-bit targets
+ with high GPRs. */
+
+static const unsigned char s390_ft_exit_gpr_zarch[] = {
+ 0xeb, 0x0f, 0xf2, 0x00, 0x00, 0x04, /* lmg %r0, %r15, 0x200(%r15) */
+};
+
+/* Writes instructions to target, updating the to pointer. */
+
+static void
+append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf)
+{
+ target_write_memory (*to, buf, len);
+ *to += len;
+}
+
+/* Relocates an instruction from oldloc to *to, updating to. */
+
+static int
+s390_relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc, int is_64)
+{
+ gdb_byte buf[6];
+ int ilen;
+ int op2;
+ /* 0: no fixup, 1: PC16DBL fixup, 2: PC32DBL fixup. */
+ int mode = 0;
+ int is_bras = 0;
+ read_inferior_memory (oldloc, buf, sizeof buf);
+ if (buf[0] < 0x40)
+ ilen = 2;
+ else if (buf[0] < 0xc0)
+ ilen = 4;
+ else
+ ilen = 6;
+ switch (buf[0])
+ {
+ case 0x05: /* BALR */
+ case 0x0c: /* BASSM */
+ case 0x0d: /* BASR */
+ case 0x45: /* BAL */
+ case 0x4d: /* BAS */
+ /* These save a return address and mess around with registers.
+ We can't relocate them. */
+ return 1;
+ case 0x84: /* BRXH */
+ case 0x85: /* BRXLE */
+ mode = 1;
+ break;
+ case 0xa7:
+ op2 = buf[1] & 0xf;
+ /* BRC, BRAS, BRCT, BRCTG */
+ if (op2 >= 4 && op2 <= 7)
+ mode = 1;
+ /* BRAS */
+ if (op2 == 5)
+ is_bras = 1;
+ break;
+ case 0xc0:
+ op2 = buf[1] & 0xf;
+ /* LARL, BRCL, BRASL */
+ if (op2 == 0 || op2 == 4 || op2 == 5)
+ mode = 2;
+ /* BRASL */
+ if (op2 == 5)
+ is_bras = 1;
+ break;
+ case 0xc4:
+ case 0xc6:
+ /* PC-relative addressing instructions. */
+ mode = 2;
+ break;
+ case 0xc5: /* BPRP */
+ case 0xc7: /* BPP */
+ /* Branch prediction - just skip it. */
+ return 0;
+ case 0xcc:
+ op2 = buf[1] & 0xf;
+ /* BRCTH */
+ if (op2 == 6)
+ mode = 2;
+ break;
+ case 0xec:
+ op2 = buf[5];
+ switch (op2)
+ {
+ case 0x44: /* BRXHG */
+ case 0x45: /* BRXLG */
+ case 0x64: /* CGRJ */
+ case 0x65: /* CLGRJ */
+ case 0x76: /* CRJ */
+ case 0x77: /* CLRJ */
+ mode = 1;
+ break;
+ }
+ break;
+ }
+
+ if (mode != 0)
+ {
+ /* We'll have to relocate an instruction with a PC-relative field.
+ First, compute the target. */
+ int64_t loffset = 0;
+ CORE_ADDR target;
+ if (mode == 1)
+ {
+ int16_t soffset = 0;
+ memcpy (&soffset, buf + 2, 2);
+ loffset = soffset;
+ }
+ else if (mode == 2)
+ {
+ int32_t soffset = 0;
+ memcpy (&soffset, buf + 2, 4);
+ loffset = soffset;
+ }
+ target = oldloc + loffset * 2;
+ if (!is_64)
+ target &= 0x7fffffff;
+
+ if (is_bras)
+ {
+ /* BRAS or BRASL was used. We cannot just relocate those, since
+ they save the return address in a register. We can, however,
+ replace them with a LARL+JG sequence. */
+
+ /* Make the LARL. */
+ int32_t soffset;
+ buf[0] = 0xc0;
+ buf[1] &= 0xf0;
+ loffset = oldloc + ilen - *to;
+ loffset >>= 1;
+ soffset = loffset;
+ if (soffset != loffset && is_64)
+ return 1;
+ memcpy (buf + 2, &soffset, 4);
+ append_insns (to, 6, buf);
+
+ /* Note: this is not fully correct. In 31-bit mode, LARL will write
+ an address with the top bit 0, while BRAS/BRASL will write it
+ with top bit 1. It should not matter much, since linux compilers
+ use BR and not BSM to return from functions, but it could confuse
+ some poor stack unwinder. */
+
+ /* We'll now be writing a JG. */
+ mode = 2;
+ buf[0] = 0xc0;
+ buf[1] = 0xf4;
+ ilen = 6;
+ }
+
+ /* Compute the new offset and write it to the buffer. */
+ loffset = target - *to;
+ loffset >>= 1;
+
+ if (mode == 1)
+ {
+ int16_t soffset = loffset;
+ if (soffset != loffset)
+ return 1;
+ memcpy (buf + 2, &soffset, 2);
+ }
+ else if (mode == 2)
+ {
+ int32_t soffset = loffset;
+ if (soffset != loffset && is_64)
+ return 1;
+ memcpy (buf + 2, &soffset, 4);
+ }
+ }
+ append_insns (to, ilen, buf);
+ return 0;
+}
+
+/* Implementation of linux_target_ops method
+ "install_fast_tracepoint_jump_pad". */
+
+static int
+s390_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)
+{
+ int i;
+ int64_t loffset;
+ int32_t offset;
+ unsigned char jbuf[6] = { 0xc0, 0xf4, 0, 0, 0, 0 }; /* jg ... */
+ CORE_ADDR buildaddr = *jump_entry;
+#ifdef __s390x__
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+ int is_64 = register_size (regcache->tdesc, 0) == 8;
+ int is_zarch = is_64 || have_hwcap_s390_high_gprs;
+ int has_vx = have_hwcap_s390_vx;
+#else
+ int is_64 = 0, is_zarch = 0, has_vx = 0;
+#endif
+ CORE_ADDR literals[4] = {
+ tpaddr,
+ tpoint,
+ collector,
+ lockaddr,
+ };
+
+ /* First, store the GPRs. */
+ if (is_zarch)
+ append_insns (&buildaddr, sizeof s390_ft_entry_gpr_zarch,
+ s390_ft_entry_gpr_zarch);
+ else
+ append_insns (&buildaddr, sizeof s390_ft_entry_gpr_esa,
+ s390_ft_entry_gpr_esa);
+
+ /* Second, misc registers (ARs, PSWM, FPC). PSWA will be stored below. */
+ append_insns (&buildaddr, sizeof s390_ft_entry_misc, s390_ft_entry_misc);
+
+ /* Third, FRs or VRs. */
+ if (has_vx)
+ append_insns (&buildaddr, sizeof s390_ft_entry_vr, s390_ft_entry_vr);
+ else
+ append_insns (&buildaddr, sizeof s390_ft_entry_fr, s390_ft_entry_fr);
+
+ /* Now, the main part of code - store PSWA, take lock, call collector,
+ leave lock. First, we'll need to fetch 4 literals. */
+ if (is_64) {
+ unsigned char buf[] = {
+ 0x07, 0x07, /* nopr %r7 */
+ 0x07, 0x07, /* nopr %r7 */
+ 0x07, 0x07, /* nopr %r7 */
+ 0xa7, 0x15, 0x00, 0x12, /* bras %r1, .Lend */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* tpaddr */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* tpoint */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* collector */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* lockaddr */
+ /* .Lend: */
+ };
+ /* Find the proper start place in buf, so that literals will be
+ aligned. */
+ int bufpos = (buildaddr + 2) & 7;
+ /* Stuff the literals into the buffer. */
+ for (i = 0; i < 4; i++) {
+ uint64_t lit = literals[i];
+ memcpy (&buf[sizeof buf - 32 + i * 8], &lit, 8);
+ }
+ append_insns (&buildaddr, sizeof buf - bufpos, buf + bufpos);
+ append_insns (&buildaddr, sizeof s390_ft_main_64, s390_ft_main_64);
+ } else {
+ unsigned char buf[] = {
+ 0x07, 0x07, /* nopr %r7 */
+ 0xa7, 0x15, 0x00, 0x0a, /* bras %r1, .Lend */
+ 0, 0, 0, 0, /* tpaddr */
+ 0, 0, 0, 0, /* tpoint */
+ 0, 0, 0, 0, /* collector */
+ 0, 0, 0, 0, /* lockaddr */
+ /* .Lend: */
+ };
+ /* Find the proper start place in buf, so that literals will be
+ aligned. */
+ int bufpos = (buildaddr + 2) & 3;
+ /* First literal will be saved as the PSWA, make sure it has the high bit
+ set. */
+ literals[0] |= 0x80000000;
+ /* Stuff the literals into the buffer. */
+ for (i = 0; i < 4; i++) {
+ uint32_t lit = literals[i];
+ memcpy (&buf[sizeof buf - 16 + i * 4], &lit, 4);
+ }
+ append_insns (&buildaddr, sizeof buf - bufpos, buf + bufpos);
+ append_insns (&buildaddr, sizeof s390_ft_main_31, s390_ft_main_31);
+ }
+
+ /* Restore FRs or VRs. */
+ if (has_vx)
+ append_insns (&buildaddr, sizeof s390_ft_exit_vr, s390_ft_exit_vr);
+ else
+ append_insns (&buildaddr, sizeof s390_ft_exit_fr, s390_ft_exit_fr);
+
+ /* Restore misc registers. */
+ append_insns (&buildaddr, sizeof s390_ft_exit_misc, s390_ft_exit_misc);
+
+ /* Restore the GPRs. */
+ if (is_zarch)
+ append_insns (&buildaddr, sizeof s390_ft_exit_gpr_zarch,
+ s390_ft_exit_gpr_zarch);
+ else
+ append_insns (&buildaddr, sizeof s390_ft_exit_gpr_esa,
+ s390_ft_exit_gpr_esa);
+
+ /* Now, adjust the original instruction to execute in the jump
+ pad. */
+ *adjusted_insn_addr = buildaddr;
+ if (s390_relocate_instruction (&buildaddr, tpaddr, is_64))
+ {
+ sprintf (err, "E.Could not relocate instruction for tracepoint.");
+ return 1;
+ }
+ *adjusted_insn_addr_end = buildaddr;
+
+ /* Finally, write a jump back to the program. */
+
+ loffset = (tpaddr + orig_size) - buildaddr;
+ loffset >>= 1;
+ offset = loffset;
+ if (is_64 && offset != loffset)
+ {
+ sprintf (err,
+ "E.Jump back from jump pad too far from tracepoint "
+ "(offset 0x%" PRIx64 " > int33).", loffset);
+ return 1;
+ }
+ memcpy (jbuf + 2, &offset, 4);
+ append_insns (&buildaddr, sizeof jbuf, jbuf);
+
+ /* 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. */
+ loffset = *jump_entry - tpaddr;
+ loffset >>= 1;
+ offset = loffset;
+ if (is_64 && offset != loffset)
+ {
+ sprintf (err,
+ "E.Jump back from jump pad too far from tracepoint "
+ "(offset 0x%" PRIx64 " > int33).", loffset);
+ return 1;
+ }
+ memcpy (jbuf + 2, &offset, 4);
+ memcpy (jjump_pad_insn, jbuf, sizeof jbuf);
+ *jjump_pad_insn_size = sizeof jbuf;
+
+ /* Return the end address of our pad. */
+ *jump_entry = buildaddr;
+
+ return 0;
+}
+
+/* Implementation of linux_target_ops method
+ "get_min_fast_tracepoint_insn_len". */
+
+static int
+s390_get_min_fast_tracepoint_insn_len (void)
+{
+ /* We only support using 6-byte jumps to reach the tracepoint code.
+ If the tracepoint buffer were allocated sufficiently close (64kiB)
+ to the executable code, and the traced instruction itself was close
+ enough to the beginning, we could use 4-byte jumps, but this doesn't
+ seem to be worth the effort. */
+ return 6;
+}
+
+/* Implementation of linux_target_ops method "get_ipa_tdesc_idx". */
+
+static int
+s390_get_ipa_tdesc_idx (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+ const struct target_desc *tdesc = regcache->tdesc;
+
+#ifdef __s390x__
+ if (tdesc == tdesc_s390x_linux64)
+ return S390_TDESC_64;
+ if (tdesc == tdesc_s390x_linux64v1)
+ return S390_TDESC_64V1;
+ if (tdesc == tdesc_s390x_linux64v2)
+ return S390_TDESC_64V2;
+ if (tdesc == tdesc_s390x_te_linux64)
+ return S390_TDESC_TE;
+ if (tdesc == tdesc_s390x_vx_linux64)
+ return S390_TDESC_VX;
+ if (tdesc == tdesc_s390x_tevx_linux64)
+ return S390_TDESC_TEVX;
+ if (tdesc == tdesc_s390x_gs_linux64)
+ return S390_TDESC_GS;
+#endif
+
+ if (tdesc == tdesc_s390_linux32)
+ return S390_TDESC_32;
+ if (tdesc == tdesc_s390_linux32v1)
+ return S390_TDESC_32V1;
+ if (tdesc == tdesc_s390_linux32v2)
+ return S390_TDESC_32V2;
+ if (tdesc == tdesc_s390_linux64)
+ return S390_TDESC_64;
+ if (tdesc == tdesc_s390_linux64v1)
+ return S390_TDESC_64V1;
+ if (tdesc == tdesc_s390_linux64v2)
+ return S390_TDESC_64V2;
+ if (tdesc == tdesc_s390_te_linux64)
+ return S390_TDESC_TE;
+ if (tdesc == tdesc_s390_vx_linux64)
+ return S390_TDESC_VX;
+ if (tdesc == tdesc_s390_tevx_linux64)
+ return S390_TDESC_TEVX;
+ if (tdesc == tdesc_s390_gs_linux64)
+ return S390_TDESC_GS;
+
+ return 0;
+}
+
+/* Appends given buffer to current_insn_ptr in the target. */
+
+static void
+add_insns (const unsigned char *start, int len)
+{
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ if (debug_threads)
+ debug_printf ("Adding %d bytes of insn at %s\n",
+ len, paddress (buildaddr));
+
+ append_insns (&buildaddr, len, start);
+ current_insn_ptr = buildaddr;
+}
+
+/* Register usage in emit:
+
+ - %r0, %r1: temp
+ - %r2: top of stack (high word for 31-bit)
+ - %r3: low word of top of stack (for 31-bit)
+ - %r4, %r5: temp
+ - %r6, %r7, %r8: don't use
+ - %r9: saved arg1
+ - %r10: saved arg2
+ - %r11: frame pointer
+ - %r12: saved top of stack for void_call_2 (high word for 31-bit)
+ - %r13: low word of saved top of stack (for 31-bit)
+ - %r14: return address for calls
+ - %r15: stack pointer
+
+ */
+
+/* The "emit_prologue" emit_ops method for s390. */
+
+static void
+s390_emit_prologue (void)
+{
+ static const unsigned char buf[] = {
+ 0x90, 0x9f, 0xf0, 0x24, /* stm %r9, %r15, 0x24(%r15) */
+ 0x18, 0x92, /* lr %r9, %r2 */
+ 0x18, 0xa3, /* lr %r10, %r3 */
+ 0x18, 0xbf, /* lr %r11, %r15 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_epilogue" emit_ops method for s390. */
+
+static void
+s390_emit_epilogue (void)
+{
+ static const unsigned char buf[] = {
+ 0x90, 0x23, 0xa0, 0x00, /* stm %r2, %r3, 0(%r10) */
+ 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
+ 0x98, 0x9f, 0xb0, 0x24, /* lm %r9, %r15, 0x24(%r11) */
+ 0x07, 0xfe, /* br %r14 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_add" emit_ops method for s390. */
+
+static void
+s390_emit_add (void)
+{
+ static const unsigned char buf[] = {
+ 0x5e, 0x30, 0xf0, 0x04, /* al %r3, 4(%r15) */
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x98, /* al %r2, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_sub" emit_ops method for s390. */
+
+static void
+s390_emit_sub (void)
+{
+ static const unsigned char buf[] = {
+ 0x98, 0x45, 0xf0, 0x00, /* lm %r4, %r5, 0(%r15) */
+ 0x1f, 0x53, /* slr %r5, %r3 */
+ 0xb9, 0x99, 0x00, 0x42, /* slbr %r4, %r2 */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ 0x18, 0x35, /* lr %r3, %r5 */
+ 0x18, 0x24, /* lr %r2, %r4 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_mul" emit_ops method for s390. */
+
+static void
+s390_emit_mul (void)
+{
+ emit_error = 1;
+}
+
+/* The "emit_lsh" emit_ops method for s390. */
+
+static void
+s390_emit_lsh (void)
+{
+ static const unsigned char buf[] = {
+ 0x18, 0x43, /* lr %r4, %r3 */
+ 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
+ 0x8d, 0x20, 0x40, 0x00, /* sldl %r2, 0(%r4) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_rsh_signed" emit_ops method for s390. */
+
+static void
+s390_emit_rsh_signed (void)
+{
+ static const unsigned char buf[] = {
+ 0x18, 0x43, /* lr %r4, %r3 */
+ 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
+ 0x8e, 0x20, 0x40, 0x00, /* srda %r2, 0(%r4) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_rsh_unsigned" emit_ops method for s390. */
+
+static void
+s390_emit_rsh_unsigned (void)
+{
+ static const unsigned char buf[] = {
+ 0x18, 0x43, /* lr %r4, %r3 */
+ 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
+ 0x8c, 0x20, 0x40, 0x00, /* srdl %r2, 0(%r4) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_ext" emit_ops method for s390. */
+
+static void
+s390_emit_ext (int arg)
+{
+ unsigned char buf[] = {
+ 0x8d, 0x20, 0x00, (unsigned char) (64 - arg), /* sldl %r2, <64-arg> */
+ 0x8e, 0x20, 0x00, (unsigned char) (64 - arg), /* srda %r2, <64-arg> */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_log_not" emit_ops method for s390. */
+
+static void
+s390_emit_log_not (void)
+{
+ static const unsigned char buf[] = {
+ 0x16, 0x23, /* or %r2, %r3 */
+ 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
+ 0xa7, 0x38, 0x00, 0x00, /* lhi %r3, 0 */
+ 0xa7, 0x74, 0x00, 0x04, /* jne .Lskip */
+ 0xa7, 0x38, 0x00, 0x01, /* lhi %r3, 1 */
+ /* .Lskip: */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_and" emit_ops method for s390. */
+
+static void
+s390_emit_bit_and (void)
+{
+ static const unsigned char buf[] = {
+ 0x54, 0x20, 0xf0, 0x00, /* n %r2, 0(%r15) */
+ 0x54, 0x30, 0xf0, 0x04, /* n %r3, 4(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_or" emit_ops method for s390. */
+
+static void
+s390_emit_bit_or (void)
+{
+ static const unsigned char buf[] = {
+ 0x56, 0x20, 0xf0, 0x00, /* o %r2, 0(%r15) */
+ 0x56, 0x30, 0xf0, 0x04, /* o %r3, 4(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_xor" emit_ops method for s390. */
+
+static void
+s390_emit_bit_xor (void)
+{
+ static const unsigned char buf[] = {
+ 0x57, 0x20, 0xf0, 0x00, /* x %r2, 0(%r15) */
+ 0x57, 0x30, 0xf0, 0x04, /* x %r3, 4(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_not" emit_ops method for s390. */
+
+static void
+s390_emit_bit_not (void)
+{
+ static const unsigned char buf[] = {
+ 0xa7, 0x48, 0xff, 0xff, /* lhi %r4, -1 */
+ 0x17, 0x24, /* xr %r2, %r4 */
+ 0x17, 0x34, /* xr %r3, %r4 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_equal" emit_ops method for s390. */
+
+static void
+s390_emit_equal (void)
+{
+ s390_emit_bit_xor ();
+ s390_emit_log_not ();
+}
+
+/* The "emit_less_signed" emit_ops method for s390. */
+
+static void
+s390_emit_less_signed (void)
+{
+ static const unsigned char buf[] = {
+ 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
+ 0xa7, 0x24, 0x00, 0x0c, /* jh .Lless */
+ 0xa7, 0x44, 0x00, 0x06, /* jl .Lhigh */
+ 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
+ 0xa7, 0x24, 0x00, 0x06, /* jh .Lless */
+ /* .Lhigh: */
+ 0xa7, 0x38, 0x00, 0x00, /* lhi %r3, 0 */
+ 0xa7, 0xf4, 0x00, 0x04, /* j .Lend */
+ /* .Lless: */
+ 0xa7, 0x38, 0x00, 0x01, /* lhi %r3, 1 */
+ /* .Lend: */
+ 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_less_unsigned" emit_ops method for s390. */
+
+static void
+s390_emit_less_unsigned (void)
+{
+ static const unsigned char buf[] = {
+ 0x55, 0x20, 0xf0, 0x00, /* cl %r2, 0(%r15) */
+ 0xa7, 0x24, 0x00, 0x0c, /* jh .Lless */
+ 0xa7, 0x44, 0x00, 0x06, /* jl .Lhigh */
+ 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
+ 0xa7, 0x24, 0x00, 0x06, /* jh .Lless */
+ /* .Lhigh: */
+ 0xa7, 0x38, 0x00, 0x00, /* lhi %r3, 0 */
+ 0xa7, 0xf4, 0x00, 0x04, /* j .Lend */
+ /* .Lless: */
+ 0xa7, 0x38, 0x00, 0x01, /* lhi %r3, 1 */
+ /* .Lend: */
+ 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_ref" emit_ops method for s390. */
+
+static void
+s390_emit_ref (int size)
+{
+ static const unsigned char buf1[] = {
+ 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
+ 0x43, 0x30, 0x30, 0x00, /* ic %r3, 0(%r3) */
+ };
+ static const unsigned char buf2[] = {
+ 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
+ 0x48, 0x30, 0x30, 0x00, /* lh %r3, 0(%r3) */
+ };
+ static const unsigned char buf4[] = {
+ 0xa7, 0x28, 0x00, 0x00, /* lhi %r2, 0 */
+ 0x58, 0x30, 0x30, 0x00, /* l %r3, 0(%r3) */
+ };
+ static const unsigned char buf8[] = {
+ 0x98, 0x23, 0x30, 0x00, /* lm %r2, %r3, 0(%r3) */
+ };
+ switch (size)
+ {
+ case 1:
+ add_insns (buf1, sizeof buf1);
+ break;
+ case 2:
+ add_insns (buf2, sizeof buf2);
+ break;
+ case 4:
+ add_insns (buf4, sizeof buf4);
+ break;
+ case 8:
+ add_insns (buf8, sizeof buf8);
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+/* The "emit_if_goto" emit_ops method for s390. */
+
+static void
+s390_emit_if_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0x16, 0x23, /* or %r2, %r3 */
+ 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00 /* jgne <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 12;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_goto" emit_ops method for s390 and s390x. */
+
+static void
+s390_emit_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 2;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "write_goto_address" emit_ops method for s390 and s390x. */
+
+static void
+s390_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ long diff = ((long) (to - (from - 2))) / 2;
+ int sdiff = diff;
+ unsigned char buf[sizeof sdiff];
+
+ /* We're only doing 4-byte sizes at the moment. */
+ if (size != sizeof sdiff || sdiff != diff)
+ {
+ emit_error = 1;
+ return;
+ }
+
+ memcpy (buf, &sdiff, sizeof sdiff);
+ target_write_memory (from, buf, sizeof sdiff);
+}
+
+/* Preparation for emitting a literal pool of given size. Loads the address
+ of the pool into %r1, and jumps over it. Called should emit the pool data
+ immediately afterwards. Used for both s390 and s390x. */
+
+static void
+s390_emit_litpool (int size)
+{
+ static const unsigned char nop[] = {
+ 0x07, 0x07,
+ };
+ unsigned char buf[] = {
+ 0xa7, 0x15, 0x00,
+ (unsigned char) ((size + 4) / 2), /* bras %r1, .Lend+size */
+ /* .Lend: */
+ };
+ if (size == 4)
+ {
+ /* buf needs to start at even halfword for litpool to be aligned */
+ if (current_insn_ptr & 2)
+ add_insns (nop, sizeof nop);
+ }
+ else
+ {
+ while ((current_insn_ptr & 6) != 4)
+ add_insns (nop, sizeof nop);
+ }
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_const" emit_ops method for s390. */
+
+static void
+s390_emit_const (LONGEST num)
+{
+ unsigned long long n = num;
+ unsigned char buf_s[] = {
+ /* lhi %r3, <num> */
+ 0xa7, 0x38,
+ (unsigned char) (num >> 8), (unsigned char) num,
+ /* xr %r2, %r2 */
+ 0x17, 0x22,
+ };
+ static const unsigned char buf_l[] = {
+ 0x98, 0x23, 0x10, 0x00, /* lm %r2, %r3, 0(%r1) */
+ };
+ if (num < 0x8000 && num >= 0)
+ {
+ add_insns (buf_s, sizeof buf_s);
+ }
+ else
+ {
+ s390_emit_litpool (8);
+ add_insns ((unsigned char *) &n, sizeof n);
+ add_insns (buf_l, sizeof buf_l);
+ }
+}
+
+/* The "emit_call" emit_ops method for s390. */
+
+static void
+s390_emit_call (CORE_ADDR fn)
+{
+ unsigned int n = fn;
+ static const unsigned char buf[] = {
+ 0x58, 0x10, 0x10, 0x00, /* l %r1, 0(%r1) */
+ 0xa7, 0xfa, 0xff, 0xa0, /* ahi %r15, -0x60 */
+ 0x0d, 0xe1, /* basr %r14, %r1 */
+ 0xa7, 0xfa, 0x00, 0x60, /* ahi %r15, 0x60 */
+ };
+ s390_emit_litpool (4);
+ add_insns ((unsigned char *) &n, sizeof n);
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_reg" emit_ops method for s390. */
+
+static void
+s390_emit_reg (int reg)
+{
+ unsigned char bufpre[] = {
+ /* lr %r2, %r9 */
+ 0x18, 0x29,
+ /* lhi %r3, <reg> */
+ 0xa7, 0x38, (unsigned char) (reg >> 8), (unsigned char) reg,
+ };
+ add_insns (bufpre, sizeof bufpre);
+ s390_emit_call (get_raw_reg_func_addr ());
+}
+
+/* The "emit_pop" emit_ops method for s390. */
+
+static void
+s390_emit_pop (void)
+{
+ static const unsigned char buf[] = {
+ 0x98, 0x23, 0xf0, 0x00, /* lm %r2, %r3, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_stack_flush" emit_ops method for s390. */
+
+static void
+s390_emit_stack_flush (void)
+{
+ static const unsigned char buf[] = {
+ 0xa7, 0xfa, 0xff, 0xf8, /* ahi %r15, -8 */
+ 0x90, 0x23, 0xf0, 0x00, /* stm %r2, %r3, 0(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_zero_ext" emit_ops method for s390. */
+
+static void
+s390_emit_zero_ext (int arg)
+{
+ unsigned char buf[] = {
+ 0x8d, 0x20, 0x00, (unsigned char) (64 - arg), /* sldl %r2, <64-arg> */
+ 0x8c, 0x20, 0x00, (unsigned char) (64 - arg), /* srdl %r2, <64-arg> */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_swap" emit_ops method for s390. */
+
+static void
+s390_emit_swap (void)
+{
+ static const unsigned char buf[] = {
+ 0x98, 0x45, 0xf0, 0x00, /* lm %r4, %r5, 0(%r15) */
+ 0x90, 0x23, 0xf0, 0x00, /* stm %r2, %r3, 0(%r15) */
+ 0x18, 0x24, /* lr %r2, %r4 */
+ 0x18, 0x35, /* lr %r3, %r5 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_stack_adjust" emit_ops method for s390. */
+
+static void
+s390_emit_stack_adjust (int n)
+{
+ unsigned char buf[] = {
+ /* ahi %r15, 8*n */
+ 0xa7, 0xfa,
+ (unsigned char ) (n * 8 >> 8), (unsigned char) (n * 8),
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* Sets %r2 to a 32-bit constant. */
+
+static void
+s390_emit_set_r2 (int arg1)
+{
+ unsigned char buf_s[] = {
+ /* lhi %r2, <arg1> */
+ 0xa7, 0x28, (unsigned char) (arg1 >> 8), (unsigned char) arg1,
+ };
+ static const unsigned char buf_l[] = {
+ 0x58, 0x20, 0x10, 0x00, /* l %r2, 0(%r1) */
+ };
+ if (arg1 < 0x8000 && arg1 >= -0x8000)
+ {
+ add_insns (buf_s, sizeof buf_s);
+ }
+ else
+ {
+ s390_emit_litpool (4);
+ add_insns ((unsigned char *) &arg1, sizeof arg1);
+ add_insns (buf_l, sizeof buf_l);
+ }
+}
+
+/* The "emit_int_call_1" emit_ops method for s390. */
+
+static void
+s390_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ /* FN's prototype is `LONGEST(*fn)(int)'. */
+ s390_emit_set_r2 (arg1);
+ s390_emit_call (fn);
+}
+
+/* The "emit_void_call_2" emit_ops method for s390. */
+
+static void
+s390_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ /* FN's prototype is `void(*fn)(int,LONGEST)'. */
+ static const unsigned char buf[] = {
+ 0x18, 0xc2, /* lr %r12, %r2 */
+ 0x18, 0xd3, /* lr %r13, %r3 */
+ 0x18, 0x43, /* lr %r4, %r3 */
+ 0x18, 0x32, /* lr %r3, %r2 */
+ };
+ static const unsigned char buf2[] = {
+ 0x18, 0x2c, /* lr %r2, %r12 */
+ 0x18, 0x3d, /* lr %r3, %r13 */
+ };
+ add_insns (buf, sizeof buf);
+ s390_emit_set_r2 (arg1);
+ s390_emit_call (fn);
+ add_insns (buf2, sizeof buf2);
+}
+
+/* The "emit_eq_goto" emit_ops method for s390. */
+
+static void
+s390_emit_eq_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0x57, 0x20, 0xf0, 0x00, /* x %r2, 0(%r15) */
+ 0x57, 0x30, 0xf0, 0x04, /* x %r3, 4(%r15) */
+ 0x16, 0x23, /* or %r2, %r3 */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0x84, 0x00, 0x00, 0x00, 0x00, /* jge <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 20;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_ne_goto" emit_ops method for s390. */
+
+static void
+s390_emit_ne_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0x57, 0x20, 0xf0, 0x00, /* x %r2, 0(%r15) */
+ 0x57, 0x30, 0xf0, 0x04, /* x %r3, 4(%r15) */
+ 0x16, 0x23, /* or %r2, %r3 */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, /* jgne <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 20;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_lt_goto" emit_ops method for s390. */
+
+static void
+s390_emit_lt_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
+ 0xa7, 0x24, 0x00, 0x0e, /* jh .Ltrue */
+ 0xa7, 0x44, 0x00, 0x06, /* jl .Lfalse */
+ 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
+ 0xa7, 0x24, 0x00, 0x08, /* jh .Ltrue */
+ /* .Lfalse: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
+ /* .Ltrue: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
+ /* .Lend: */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 42;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_le_goto" emit_ops method for s390. */
+
+static void
+s390_emit_le_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
+ 0xa7, 0x24, 0x00, 0x0e, /* jh .Ltrue */
+ 0xa7, 0x44, 0x00, 0x06, /* jl .Lfalse */
+ 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
+ 0xa7, 0xa4, 0x00, 0x08, /* jhe .Ltrue */
+ /* .Lfalse: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
+ /* .Ltrue: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
+ /* .Lend: */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 42;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_gt_goto" emit_ops method for s390. */
+
+static void
+s390_emit_gt_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
+ 0xa7, 0x44, 0x00, 0x0e, /* jl .Ltrue */
+ 0xa7, 0x24, 0x00, 0x06, /* jh .Lfalse */
+ 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
+ 0xa7, 0x44, 0x00, 0x08, /* jl .Ltrue */
+ /* .Lfalse: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
+ /* .Ltrue: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
+ /* .Lend: */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 42;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_ge_goto" emit_ops method for s390. */
+
+static void
+s390_emit_ge_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0x59, 0x20, 0xf0, 0x00, /* c %r2, 0(%r15) */
+ 0xa7, 0x44, 0x00, 0x0e, /* jl .Ltrue */
+ 0xa7, 0x24, 0x00, 0x06, /* jh .Lfalse */
+ 0x55, 0x30, 0xf0, 0x04, /* cl %r3, 4(%r15) */
+ 0xa7, 0xc4, 0x00, 0x08, /* jle .Ltrue */
+ /* .Lfalse: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xa7, 0xf4, 0x00, 0x09, /* j .Lend */
+ /* .Ltrue: */
+ 0x98, 0x23, 0xf0, 0x08, /* lm %r2, %r3, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg <fillme> */
+ /* .Lend: */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 42;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_ops" structure for s390. Named _impl to avoid name
+ collision with s390_emit_ops function. */
+
+static struct emit_ops s390_emit_ops_impl =
+ {
+ s390_emit_prologue,
+ s390_emit_epilogue,
+ s390_emit_add,
+ s390_emit_sub,
+ s390_emit_mul,
+ s390_emit_lsh,
+ s390_emit_rsh_signed,
+ s390_emit_rsh_unsigned,
+ s390_emit_ext,
+ s390_emit_log_not,
+ s390_emit_bit_and,
+ s390_emit_bit_or,
+ s390_emit_bit_xor,
+ s390_emit_bit_not,
+ s390_emit_equal,
+ s390_emit_less_signed,
+ s390_emit_less_unsigned,
+ s390_emit_ref,
+ s390_emit_if_goto,
+ s390_emit_goto,
+ s390_write_goto_address,
+ s390_emit_const,
+ s390_emit_call,
+ s390_emit_reg,
+ s390_emit_pop,
+ s390_emit_stack_flush,
+ s390_emit_zero_ext,
+ s390_emit_swap,
+ s390_emit_stack_adjust,
+ s390_emit_int_call_1,
+ s390_emit_void_call_2,
+ s390_emit_eq_goto,
+ s390_emit_ne_goto,
+ s390_emit_lt_goto,
+ s390_emit_le_goto,
+ s390_emit_gt_goto,
+ s390_emit_ge_goto
+ };
+
+#ifdef __s390x__
+
+/* The "emit_prologue" emit_ops method for s390x. */
+
+static void
+s390x_emit_prologue (void)
+{
+ static const unsigned char buf[] = {
+ 0xeb, 0x9f, 0xf0, 0x48, 0x00, 0x24, /* stmg %r9, %r15, 0x48(%r15) */
+ 0xb9, 0x04, 0x00, 0x92, /* lgr %r9, %r2 */
+ 0xb9, 0x04, 0x00, 0xa3, /* lgr %r10, %r3 */
+ 0xb9, 0x04, 0x00, 0xbf, /* lgr %r11, %r15 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_epilogue" emit_ops method for s390x. */
+
+static void
+s390x_emit_epilogue (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xa0, 0x00, 0x00, 0x24, /* stg %r2, 0(%r10) */
+ 0xa7, 0x29, 0x00, 0x00, /* lghi %r2, 0 */
+ 0xeb, 0x9f, 0xf0, 0x48, 0x00, 0x04, /* lmg %r9, %r15, 0x48(%r15) */
+ 0x07, 0xfe, /* br %r14 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_add" emit_ops method for s390x. */
+
+static void
+s390x_emit_add (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x0a, /* alg %r2, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_sub" emit_ops method for s390x. */
+
+static void
+s390x_emit_sub (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
+ 0xb9, 0x0b, 0x00, 0x32, /* slgr %r3, %r2 */
+ 0xb9, 0x04, 0x00, 0x23, /* lgr %r2, %r3 */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_mul" emit_ops method for s390x. */
+
+static void
+s390x_emit_mul (void)
+{
+ emit_error = 1;
+}
+
+/* The "emit_lsh" emit_ops method for s390x. */
+
+static void
+s390x_emit_lsh (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
+ 0xeb, 0x23, 0x20, 0x00, 0x00, 0x0d, /* sllg %r2, %r3, 0(%r2) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_rsh_signed" emit_ops method for s390x. */
+
+static void
+s390x_emit_rsh_signed (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
+ 0xeb, 0x23, 0x20, 0x00, 0x00, 0x0a, /* srag %r2, %r3, 0(%r2) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_rsh_unsigned" emit_ops method for s390x. */
+
+static void
+s390x_emit_rsh_unsigned (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
+ 0xeb, 0x23, 0x20, 0x00, 0x00, 0x0c, /* srlg %r2, %r3, 0(%r2) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_ext" emit_ops method for s390x. */
+
+static void
+s390x_emit_ext (int arg)
+{
+ unsigned char buf[] = {
+ /* sllg %r2, %r2, <64-arg> */
+ 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0d,
+ /* srag %r2, %r2, <64-arg> */
+ 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0a,
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_log_not" emit_ops method for s390x. */
+
+static void
+s390x_emit_log_not (void)
+{
+ static const unsigned char buf[] = {
+ 0xb9, 0x00, 0x00, 0x22, /* lpgr %r2, %r2 */
+ 0xa7, 0x2b, 0xff, 0xff, /* aghi %r2, -1 */
+ 0xeb, 0x22, 0x00, 0x3f, 0x00, 0x0c, /* srlg %r2, %r2, 63 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_and" emit_ops method for s390x. */
+
+static void
+s390x_emit_bit_and (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x80, /* ng %r2, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_or" emit_ops method for s390x. */
+
+static void
+s390x_emit_bit_or (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x81, /* og %r2, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_xor" emit_ops method for s390x. */
+
+static void
+s390x_emit_bit_xor (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x82, /* xg %r2, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_bit_not" emit_ops method for s390x. */
+
+static void
+s390x_emit_bit_not (void)
+{
+ static const unsigned char buf[] = {
+ 0xa7, 0x39, 0xff, 0xff, /* lghi %r3, -1 */
+ 0xb9, 0x82, 0x00, 0x23, /* xgr %r2, %r3 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_equal" emit_ops method for s390x. */
+
+static void
+s390x_emit_equal (void)
+{
+ s390x_emit_bit_xor ();
+ s390x_emit_log_not ();
+}
+
+/* The "emit_less_signed" emit_ops method for s390x. */
+
+static void
+s390x_emit_less_signed (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
+ 0xa7, 0x29, 0x00, 0x01, /* lghi %r2, 1 */
+ 0xa7, 0x24, 0x00, 0x04, /* jh .Lend */
+ 0xa7, 0x29, 0x00, 0x00, /* lghi %r2, 0 */
+ /* .Lend: */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_less_unsigned" emit_ops method for s390x. */
+
+static void
+s390x_emit_less_unsigned (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x21, /* clg %r2, 0(%r15) */
+ 0xa7, 0x29, 0x00, 0x01, /* lghi %r2, 1 */
+ 0xa7, 0x24, 0x00, 0x04, /* jh .Lend */
+ 0xa7, 0x29, 0x00, 0x00, /* lghi %r2, 0 */
+ /* .Lend: */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_ref" emit_ops method for s390x. */
+
+static void
+s390x_emit_ref (int size)
+{
+ static const unsigned char buf1[] = {
+ 0xe3, 0x20, 0x20, 0x00, 0x00, 0x90, /* llgc %r2, 0(%r2) */
+ };
+ static const unsigned char buf2[] = {
+ 0xe3, 0x20, 0x20, 0x00, 0x00, 0x91 /* llgh %r2, 0(%r2) */
+ };
+ static const unsigned char buf4[] = {
+ 0xe3, 0x20, 0x20, 0x00, 0x00, 0x16, /* llgf %r2, 0(%r2) */
+ };
+ static const unsigned char buf8[] = {
+ 0xe3, 0x20, 0x20, 0x00, 0x00, 0x04, /* lg %r2, 0(%r2) */
+ };
+ switch (size)
+ {
+ case 1:
+ add_insns (buf1, sizeof buf1);
+ break;
+ case 2:
+ add_insns (buf2, sizeof buf2);
+ break;
+ case 4:
+ add_insns (buf4, sizeof buf4);
+ break;
+ case 8:
+ add_insns (buf8, sizeof buf8);
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+/* The "emit_if_goto" emit_ops method for s390x. */
+
+static void
+s390x_emit_if_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xb9, 0x02, 0x00, 0x22, /* ltgr %r2, %r2 */
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x04, /* lg %r2, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, /* jgne <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 16;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_const" emit_ops method for s390x. */
+
+static void
+s390x_emit_const (LONGEST num)
+{
+ unsigned long long n = num;
+ unsigned char buf_s[] = {
+ /* lghi %r2, <num> */
+ 0xa7, 0x29, (unsigned char) (num >> 8), (unsigned char) num,
+ };
+ static const unsigned char buf_l[] = {
+ 0xe3, 0x20, 0x10, 0x00, 0x00, 0x04, /* lg %r2, 0(%r1) */
+ };
+ if (num < 0x8000 && num >= -0x8000)
+ {
+ add_insns (buf_s, sizeof buf_s);
+ }
+ else
+ {
+ s390_emit_litpool (8);
+ add_insns ((unsigned char *) &n, sizeof n);
+ add_insns (buf_l, sizeof buf_l);
+ }
+}
+
+/* The "emit_call" emit_ops method for s390x. */
+
+static void
+s390x_emit_call (CORE_ADDR fn)
+{
+ unsigned long n = fn;
+ static const unsigned char buf[] = {
+ 0xe3, 0x10, 0x10, 0x00, 0x00, 0x04, /* lg %r1, 0(%r1) */
+ 0xa7, 0xfb, 0xff, 0x60, /* aghi %r15, -0xa0 */
+ 0x0d, 0xe1, /* basr %r14, %r1 */
+ 0xa7, 0xfb, 0x00, 0xa0, /* aghi %r15, 0xa0 */
+ };
+ s390_emit_litpool (8);
+ add_insns ((unsigned char *) &n, sizeof n);
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_reg" emit_ops method for s390x. */
+
+static void
+s390x_emit_reg (int reg)
+{
+ unsigned char buf[] = {
+ /* lgr %r2, %r9 */
+ 0xb9, 0x04, 0x00, 0x29,
+ /* lghi %r3, <reg> */
+ 0xa7, 0x39, (unsigned char) (reg >> 8), (unsigned char) reg,
+ };
+ add_insns (buf, sizeof buf);
+ s390x_emit_call (get_raw_reg_func_addr ());
+}
+
+/* The "emit_pop" emit_ops method for s390x. */
+
+static void
+s390x_emit_pop (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x04, /* lg %r2, 0(%r15) */
+ 0x41, 0xf0, 0xf0, 0x08, /* la %r15, 8(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_stack_flush" emit_ops method for s390x. */
+
+static void
+s390x_emit_stack_flush (void)
+{
+ static const unsigned char buf[] = {
+ 0xa7, 0xfb, 0xff, 0xf8, /* aghi %r15, -8 */
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x24, /* stg %r2, 0(%r15) */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_zero_ext" emit_ops method for s390x. */
+
+static void
+s390x_emit_zero_ext (int arg)
+{
+ unsigned char buf[] = {
+ /* sllg %r2, %r2, <64-arg> */
+ 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0d,
+ /* srlg %r2, %r2, <64-arg> */
+ 0xeb, 0x22, 0x00, (unsigned char) (64 - arg), 0x00, 0x0c,
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_swap" emit_ops method for s390x. */
+
+static void
+s390x_emit_swap (void)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x30, 0xf0, 0x00, 0x00, 0x04, /* lg %r3, 0(%r15) */
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x24, /* stg %r2, 0(%r15) */
+ 0xb9, 0x04, 0x00, 0x23, /* lgr %r2, %r3 */
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_stack_adjust" emit_ops method for s390x. */
+
+static void
+s390x_emit_stack_adjust (int n)
+{
+ unsigned char buf[] = {
+ /* aghi %r15, 8*n */
+ 0xa7, 0xfb,
+ (unsigned char) (n * 8 >> 8), (unsigned char) (n * 8),
+ };
+ add_insns (buf, sizeof buf);
+}
+
+/* The "emit_int_call_1" emit_ops method for s390x. */
+
+static void
+s390x_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ /* FN's prototype is `LONGEST(*fn)(int)'. */
+ s390x_emit_const (arg1);
+ s390x_emit_call (fn);
+}
+
+/* The "emit_void_call_2" emit_ops method for s390x. */
+
+static void
+s390x_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ /* FN's prototype is `void(*fn)(int,LONGEST)'. */
+ static const unsigned char buf[] = {
+ 0xb9, 0x04, 0x00, 0x32, /* lgr %r3, %r2 */
+ 0xb9, 0x04, 0x00, 0xc2, /* lgr %r12, %r2 */
+ };
+ static const unsigned char buf2[] = {
+ 0xb9, 0x04, 0x00, 0x2c, /* lgr %r2, %r12 */
+ };
+ add_insns (buf, sizeof buf);
+ s390x_emit_const (arg1);
+ s390x_emit_call (fn);
+ add_insns (buf2, sizeof buf2);
+}
+
+/* The "emit_eq_goto" emit_ops method for s390x. */
+
+static void
+s390x_emit_eq_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
+ 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0x84, 0x00, 0x00, 0x00, 0x00, /* jge <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_ne_goto" emit_ops method for s390x. */
+
+static void
+s390x_emit_ne_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
+ 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, /* jgne <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_lt_goto" emit_ops method for s390x. */
+
+static void
+s390x_emit_lt_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
+ 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0x24, 0x00, 0x00, 0x00, 0x00, /* jgh <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_le_goto" emit_ops method for s390x. */
+
+static void
+s390x_emit_le_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
+ 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0xa4, 0x00, 0x00, 0x00, 0x00, /* jghe <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_gt_goto" emit_ops method for s390x. */
+
+static void
+s390x_emit_gt_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
+ 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0x44, 0x00, 0x00, 0x00, 0x00, /* jgl <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_ge_goto" emit_ops method for s390x. */
+
+static void
+s390x_emit_ge_goto (int *offset_p, int *size_p)
+{
+ static const unsigned char buf[] = {
+ 0xe3, 0x20, 0xf0, 0x00, 0x00, 0x20, /* cg %r2, 0(%r15) */
+ 0xe3, 0x20, 0xf0, 0x08, 0x00, 0x04, /* lg %r2, 8(%r15) */
+ 0x41, 0xf0, 0xf0, 0x10, /* la %r15, 16(%r15) */
+ 0xc0, 0xc4, 0x00, 0x00, 0x00, 0x00, /* jgle <fillme> */
+ };
+ add_insns (buf, sizeof buf);
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+/* The "emit_ops" structure for s390x. */
+
+static struct emit_ops s390x_emit_ops =
+ {
+ s390x_emit_prologue,
+ s390x_emit_epilogue,
+ s390x_emit_add,
+ s390x_emit_sub,
+ s390x_emit_mul,
+ s390x_emit_lsh,
+ s390x_emit_rsh_signed,
+ s390x_emit_rsh_unsigned,
+ s390x_emit_ext,
+ s390x_emit_log_not,
+ s390x_emit_bit_and,
+ s390x_emit_bit_or,
+ s390x_emit_bit_xor,
+ s390x_emit_bit_not,
+ s390x_emit_equal,
+ s390x_emit_less_signed,
+ s390x_emit_less_unsigned,
+ s390x_emit_ref,
+ s390x_emit_if_goto,
+ s390_emit_goto,
+ s390_write_goto_address,
+ s390x_emit_const,
+ s390x_emit_call,
+ s390x_emit_reg,
+ s390x_emit_pop,
+ s390x_emit_stack_flush,
+ s390x_emit_zero_ext,
+ s390x_emit_swap,
+ s390x_emit_stack_adjust,
+ s390x_emit_int_call_1,
+ s390x_emit_void_call_2,
+ s390x_emit_eq_goto,
+ s390x_emit_ne_goto,
+ s390x_emit_lt_goto,
+ s390x_emit_le_goto,
+ s390x_emit_gt_goto,
+ s390x_emit_ge_goto
+ };
+#endif
+
+/* The "emit_ops" linux_target_ops method. */
+
+static struct emit_ops *
+s390_emit_ops (void)
+{
+#ifdef __s390x__
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+
+ if (register_size (regcache->tdesc, 0) == 8)
+ return &s390x_emit_ops;
+ else
+#endif
+ return &s390_emit_ops_impl;
+}
+
+struct linux_target_ops the_low_target = {
+ s390_arch_setup,
+ s390_regs_info,
+ s390_cannot_fetch_register,
+ s390_cannot_store_register,
+ NULL, /* fetch_register */
+ s390_get_pc,
+ s390_set_pc,
+ NULL, /* breakpoint_kind_from_pc */
+ s390_sw_breakpoint_from_kind,
+ NULL,
+ s390_breakpoint_len,
+ s390_breakpoint_at,
+ s390_supports_z_point_type,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ s390_collect_ptrace_register,
+ s390_supply_ptrace_register,
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ s390_supports_tracepoints,
+ s390_get_thread_area,
+ s390_install_fast_tracepoint_jump_pad,
+ s390_emit_ops,
+ s390_get_min_fast_tracepoint_insn_len,
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ s390_supports_hardware_single_step,
+ NULL, /* get_syscall_trapinfo */
+ s390_get_ipa_tdesc_idx,
+};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+
+ init_registers_s390_linux32 ();
+ init_registers_s390_linux32v1 ();
+ init_registers_s390_linux32v2 ();
+ init_registers_s390_linux64 ();
+ init_registers_s390_linux64v1 ();
+ init_registers_s390_linux64v2 ();
+ init_registers_s390_te_linux64 ();
+ init_registers_s390_vx_linux64 ();
+ init_registers_s390_tevx_linux64 ();
+ init_registers_s390_gs_linux64 ();
+#ifdef __s390x__
+ init_registers_s390x_linux64 ();
+ init_registers_s390x_linux64v1 ();
+ init_registers_s390x_linux64v2 ();
+ init_registers_s390x_te_linux64 ();
+ init_registers_s390x_vx_linux64 ();
+ init_registers_s390x_tevx_linux64 ();
+ init_registers_s390x_gs_linux64 ();
+#endif
+
+ initialize_regsets_info (&s390_regsets_info);
+ initialize_regsets_info (&s390_regsets_info_3264);
+}
+++ /dev/null
-/* GNU/Linux/SH specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-/* Defined in auto-generated file reg-sh.c. */
-void init_registers_sh (void);
-extern const struct target_desc *tdesc_sh;
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-#include <asm/ptrace.h>
-
-#define sh_num_regs 41
-
-/* Currently, don't check/send MQ. */
-static int sh_regmap[] = {
- 0, 4, 8, 12, 16, 20, 24, 28,
- 32, 36, 40, 44, 48, 52, 56, 60,
-
- REG_PC*4, REG_PR*4, REG_GBR*4, -1,
- REG_MACH*4, REG_MACL*4, REG_SR*4,
- REG_FPUL*4, REG_FPSCR*4,
-
- REG_FPREG0*4+0, REG_FPREG0*4+4, REG_FPREG0*4+8, REG_FPREG0*4+12,
- REG_FPREG0*4+16, REG_FPREG0*4+20, REG_FPREG0*4+24, REG_FPREG0*4+28,
- REG_FPREG0*4+32, REG_FPREG0*4+36, REG_FPREG0*4+40, REG_FPREG0*4+44,
- REG_FPREG0*4+48, REG_FPREG0*4+52, REG_FPREG0*4+56, REG_FPREG0*4+60,
-};
-
-static int
-sh_cannot_store_register (int regno)
-{
- return 0;
-}
-
-static int
-sh_cannot_fetch_register (int regno)
-{
- return 0;
-}
-
-/* Correct in either endianness, obviously. */
-static const unsigned short sh_breakpoint = 0xc3c3;
-#define sh_breakpoint_len 2
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-sh_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = sh_breakpoint_len;
- return (const gdb_byte *) &sh_breakpoint;
-}
-
-static int
-sh_breakpoint_at (CORE_ADDR where)
-{
- unsigned short insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
- if (insn == sh_breakpoint)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-/* Support for hardware single step. */
-
-static int
-sh_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-/* Provide only a fill function for the general register set. ps_lgetregs
- will use this for NPTL support. */
-
-static void sh_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
-
- for (i = 0; i < 23; i++)
- if (sh_regmap[i] != -1)
- collect_register (regcache, i, (char *) buf + sh_regmap[i]);
-}
-
-static struct regset_info sh_regsets[] = {
- { 0, 0, 0, 0, GENERAL_REGS, sh_fill_gregset, NULL },
- NULL_REGSET
-};
-
-static struct regsets_info sh_regsets_info =
- {
- sh_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info sh_usrregs_info =
- {
- sh_num_regs,
- sh_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &sh_usrregs_info,
- &sh_regsets_info
- };
-
-static const struct regs_info *
-sh_regs_info (void)
-{
- return ®s_info;
-}
-
-static void
-sh_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_sh;
-}
-
-struct linux_target_ops the_low_target = {
- sh_arch_setup,
- sh_regs_info,
- sh_cannot_fetch_register,
- sh_cannot_store_register,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_kind_from_pc */
- sh_sw_breakpoint_from_kind,
- NULL,
- 0,
- sh_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- sh_supports_hardware_single_step,
-};
-
-void
-initialize_low_arch (void)
-{
- init_registers_sh ();
-
- initialize_regsets_info (&sh_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/SH specific low level interface, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+/* Defined in auto-generated file reg-sh.c. */
+void init_registers_sh (void);
+extern const struct target_desc *tdesc_sh;
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+#include <asm/ptrace.h>
+
+#define sh_num_regs 41
+
+/* Currently, don't check/send MQ. */
+static int sh_regmap[] = {
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+
+ REG_PC*4, REG_PR*4, REG_GBR*4, -1,
+ REG_MACH*4, REG_MACL*4, REG_SR*4,
+ REG_FPUL*4, REG_FPSCR*4,
+
+ REG_FPREG0*4+0, REG_FPREG0*4+4, REG_FPREG0*4+8, REG_FPREG0*4+12,
+ REG_FPREG0*4+16, REG_FPREG0*4+20, REG_FPREG0*4+24, REG_FPREG0*4+28,
+ REG_FPREG0*4+32, REG_FPREG0*4+36, REG_FPREG0*4+40, REG_FPREG0*4+44,
+ REG_FPREG0*4+48, REG_FPREG0*4+52, REG_FPREG0*4+56, REG_FPREG0*4+60,
+};
+
+static int
+sh_cannot_store_register (int regno)
+{
+ return 0;
+}
+
+static int
+sh_cannot_fetch_register (int regno)
+{
+ return 0;
+}
+
+/* Correct in either endianness, obviously. */
+static const unsigned short sh_breakpoint = 0xc3c3;
+#define sh_breakpoint_len 2
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+sh_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = sh_breakpoint_len;
+ return (const gdb_byte *) &sh_breakpoint;
+}
+
+static int
+sh_breakpoint_at (CORE_ADDR where)
+{
+ unsigned short insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
+ if (insn == sh_breakpoint)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+/* Support for hardware single step. */
+
+static int
+sh_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+/* Provide only a fill function for the general register set. ps_lgetregs
+ will use this for NPTL support. */
+
+static void sh_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+
+ for (i = 0; i < 23; i++)
+ if (sh_regmap[i] != -1)
+ collect_register (regcache, i, (char *) buf + sh_regmap[i]);
+}
+
+static struct regset_info sh_regsets[] = {
+ { 0, 0, 0, 0, GENERAL_REGS, sh_fill_gregset, NULL },
+ NULL_REGSET
+};
+
+static struct regsets_info sh_regsets_info =
+ {
+ sh_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info sh_usrregs_info =
+ {
+ sh_num_regs,
+ sh_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &sh_usrregs_info,
+ &sh_regsets_info
+ };
+
+static const struct regs_info *
+sh_regs_info (void)
+{
+ return ®s_info;
+}
+
+static void
+sh_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_sh;
+}
+
+struct linux_target_ops the_low_target = {
+ sh_arch_setup,
+ sh_regs_info,
+ sh_cannot_fetch_register,
+ sh_cannot_store_register,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_kind_from_pc */
+ sh_sw_breakpoint_from_kind,
+ NULL,
+ 0,
+ sh_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ sh_supports_hardware_single_step,
+};
+
+void
+initialize_low_arch (void)
+{
+ init_registers_sh ();
+
+ initialize_regsets_info (&sh_regsets_info);
+}
+++ /dev/null
-/* Low level interface to ptrace, for the remote server for GDB.
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-#include "nat/gdb_ptrace.h"
-
-#include "gdb_proc_service.h"
-
-/* The stack pointer is offset from the stack frame by a BIAS of 2047
- (0x7ff) for 64-bit code. BIAS is likely to be defined on SPARC
- hosts, so undefine it first. */
-#undef BIAS
-#define BIAS 2047
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-#define INSN_SIZE 4
-
-#define SPARC_R_REGS_NUM 32
-#define SPARC_F_REGS_NUM 48
-#define SPARC_CONTROL_REGS_NUM 6
-
-#define sparc_num_regs \
- (SPARC_R_REGS_NUM + SPARC_F_REGS_NUM + SPARC_CONTROL_REGS_NUM)
-
-/* Each offset is multiplied by 8, because of the register size.
- These offsets apply to the buffer sent/filled by ptrace.
- Additionally, the array elements order corresponds to the .dat file, and the
- gdb's registers enumeration order. */
-
-static int sparc_regmap[] = {
- /* These offsets correspond to GET/SETREGSET. */
- -1, 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, /* g0 .. g7 */
- 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, /* o0 .. o5, sp, o7 */
- -1, -1, -1, -1, -1, -1, -1, -1, /* l0 .. l7 */
- -1, -1, -1, -1, -1, -1, -1, -1, /* i0 .. i5, fp, i7 */
-
- /* Floating point registers offsets correspond to GET/SETFPREGSET. */
- 0*4, 1*4, 2*4, 3*4, 4*4, 5*4, 6*4, 7*4, /* f0 .. f7 */
- 8*4, 9*4, 10*4, 11*4, 12*4, 13*4, 14*4, 15*4, /* f8 .. f15 */
- 16*4, 17*4, 18*4, 19*4, 20*4, 21*4, 22*4, 23*4, /* f16 .. f23 */
- 24*4, 25*4, 26*4, 27*4, 28*4, 29*4, 30*4, 31*4, /* f24 .. f31 */
-
- /* F32 offset starts next to f31: 31*4+4 = 16 * 8. */
- 16*8, 17*8, 18*8, 19*8, 20*8, 21*8, 22*8, 23*8, /* f32 .. f46 */
- 24*8, 25*8, 26*8, 27*8, 28*8, 29*8, 30*8, 31*8, /* f48 .. f62 */
-
- 17 *8, /* pc */
- 18 *8, /* npc */
- 16 *8, /* state */
- /* FSR offset also corresponds to GET/SETFPREGSET, ans is placed
- next to f62. */
- 32 *8, /* fsr */
- -1, /* fprs */
- /* Y register is 32-bits length, but gdb takes care of that. */
- 19 *8, /* y */
-
-};
-
-
-struct regs_range_t
-{
- int regno_start;
- int regno_end;
-};
-
-static const struct regs_range_t gregs_ranges[] = {
- { 0, 31 }, /* g0 .. i7 */
- { 80, 82 }, /* pc .. state */
- { 84, 85 } /* fprs .. y */
-};
-
-#define N_GREGS_RANGES (sizeof (gregs_ranges) / sizeof (struct regs_range_t))
-
-static const struct regs_range_t fpregs_ranges[] = {
- { 32, 79 }, /* f0 .. f62 */
- { 83, 83 } /* fsr */
-};
-
-#define N_FPREGS_RANGES (sizeof (fpregs_ranges) / sizeof (struct regs_range_t))
-
-/* Defined in auto-generated file reg-sparc64.c. */
-void init_registers_sparc64 (void);
-extern const struct target_desc *tdesc_sparc64;
-
-static int
-sparc_cannot_store_register (int regno)
-{
- return (regno >= sparc_num_regs || sparc_regmap[regno] == -1);
-}
-
-static int
-sparc_cannot_fetch_register (int regno)
-{
- return (regno >= sparc_num_regs || sparc_regmap[regno] == -1);
-}
-
-static void
-sparc_fill_gregset_to_stack (struct regcache *regcache, const void *buf)
-{
- int i;
- CORE_ADDR addr = 0;
- unsigned char tmp_reg_buf[8];
- const int l0_regno = find_regno (regcache->tdesc, "l0");
- const int i7_regno = l0_regno + 15;
-
- /* These registers have to be stored in the stack. */
- memcpy (&addr,
- ((char *) buf) + sparc_regmap[find_regno (regcache->tdesc, "sp")],
- sizeof (addr));
-
- addr += BIAS;
-
- for (i = l0_regno; i <= i7_regno; i++)
- {
- collect_register (regcache, i, tmp_reg_buf);
- (*the_target->write_memory) (addr, tmp_reg_buf, sizeof (tmp_reg_buf));
- addr += sizeof (tmp_reg_buf);
- }
-}
-
-static void
-sparc_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
- int range;
-
- for (range = 0; range < N_GREGS_RANGES; range++)
- for (i = gregs_ranges[range].regno_start;
- i <= gregs_ranges[range].regno_end; i++)
- if (sparc_regmap[i] != -1)
- collect_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
-
- sparc_fill_gregset_to_stack (regcache, buf);
-}
-
-static void
-sparc_fill_fpregset (struct regcache *regcache, void *buf)
-{
- int i;
- int range;
-
- for (range = 0; range < N_FPREGS_RANGES; range++)
- for (i = fpregs_ranges[range].regno_start;
- i <= fpregs_ranges[range].regno_end; i++)
- collect_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
-
-}
-
-static void
-sparc_store_gregset_from_stack (struct regcache *regcache, const void *buf)
-{
- int i;
- CORE_ADDR addr = 0;
- unsigned char tmp_reg_buf[8];
- const int l0_regno = find_regno (regcache->tdesc, "l0");
- const int i7_regno = l0_regno + 15;
-
- /* These registers have to be obtained from the stack. */
- memcpy (&addr,
- ((char *) buf) + sparc_regmap[find_regno (regcache->tdesc, "sp")],
- sizeof (addr));
-
- addr += BIAS;
-
- for (i = l0_regno; i <= i7_regno; i++)
- {
- (*the_target->read_memory) (addr, tmp_reg_buf, sizeof (tmp_reg_buf));
- supply_register (regcache, i, tmp_reg_buf);
- addr += sizeof (tmp_reg_buf);
- }
-}
-
-static void
-sparc_store_gregset (struct regcache *regcache, const void *buf)
-{
- int i;
- char zerobuf[8];
- int range;
-
- memset (zerobuf, 0, sizeof (zerobuf));
-
- for (range = 0; range < N_GREGS_RANGES; range++)
- for (i = gregs_ranges[range].regno_start;
- i <= gregs_ranges[range].regno_end; i++)
- if (sparc_regmap[i] != -1)
- supply_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
- else
- supply_register (regcache, i, zerobuf);
-
- sparc_store_gregset_from_stack (regcache, buf);
-}
-
-static void
-sparc_store_fpregset (struct regcache *regcache, const void *buf)
-{
- int i;
- int range;
-
- for (range = 0; range < N_FPREGS_RANGES; range++)
- for (i = fpregs_ranges[range].regno_start;
- i <= fpregs_ranges[range].regno_end;
- i++)
- supply_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
-}
-
-static const gdb_byte sparc_breakpoint[INSN_SIZE] = {
- 0x91, 0xd0, 0x20, 0x01
-};
-#define sparc_breakpoint_len INSN_SIZE
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const unsigned char *
-sparc_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = sparc_breakpoint_len;
- return sparc_breakpoint;
-}
-
-static int
-sparc_breakpoint_at (CORE_ADDR where)
-{
- unsigned char insn[INSN_SIZE];
-
- (*the_target->read_memory) (where, (unsigned char *) insn, sizeof (insn));
-
- if (memcmp (sparc_breakpoint, insn, sizeof (insn)) == 0)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only
- uses TRAP Always. */
-
- return 0;
-}
-
-static void
-sparc_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_sparc64;
-}
-
-static struct regset_info sparc_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
- GENERAL_REGS,
- sparc_fill_gregset, sparc_store_gregset },
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (fpregset_t),
- FP_REGS,
- sparc_fill_fpregset, sparc_store_fpregset },
- NULL_REGSET
-};
-
-static struct regsets_info sparc_regsets_info =
- {
- sparc_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info sparc_usrregs_info =
- {
- sparc_num_regs,
- /* No regmap needs to be provided since this impl. doesn't use
- USRREGS. */
- NULL
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &sparc_usrregs_info,
- &sparc_regsets_info
- };
-
-static const struct regs_info *
-sparc_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- sparc_arch_setup,
- sparc_regs_info,
- sparc_cannot_fetch_register,
- sparc_cannot_store_register,
- NULL, /* fetch_register */
- linux_get_pc_64bit,
- /* No sparc_set_pc is needed. */
- NULL,
- NULL, /* breakpoint_kind_from_pc */
- sparc_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
- 0,
- sparc_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, NULL, NULL, NULL,
- NULL, NULL
-};
-
-void
-initialize_low_arch (void)
-{
- /* Initialize the Linux target descriptions. */
- init_registers_sparc64 ();
-
- initialize_regsets_info (&sparc_regsets_info);
-}
--- /dev/null
+/* Low level interface to ptrace, for the remote server for GDB.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+#include "nat/gdb_ptrace.h"
+
+#include "gdb_proc_service.h"
+
+/* The stack pointer is offset from the stack frame by a BIAS of 2047
+ (0x7ff) for 64-bit code. BIAS is likely to be defined on SPARC
+ hosts, so undefine it first. */
+#undef BIAS
+#define BIAS 2047
+
+#ifdef HAVE_SYS_REG_H
+#include <sys/reg.h>
+#endif
+
+#define INSN_SIZE 4
+
+#define SPARC_R_REGS_NUM 32
+#define SPARC_F_REGS_NUM 48
+#define SPARC_CONTROL_REGS_NUM 6
+
+#define sparc_num_regs \
+ (SPARC_R_REGS_NUM + SPARC_F_REGS_NUM + SPARC_CONTROL_REGS_NUM)
+
+/* Each offset is multiplied by 8, because of the register size.
+ These offsets apply to the buffer sent/filled by ptrace.
+ Additionally, the array elements order corresponds to the .dat file, and the
+ gdb's registers enumeration order. */
+
+static int sparc_regmap[] = {
+ /* These offsets correspond to GET/SETREGSET. */
+ -1, 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, /* g0 .. g7 */
+ 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, /* o0 .. o5, sp, o7 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* l0 .. l7 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* i0 .. i5, fp, i7 */
+
+ /* Floating point registers offsets correspond to GET/SETFPREGSET. */
+ 0*4, 1*4, 2*4, 3*4, 4*4, 5*4, 6*4, 7*4, /* f0 .. f7 */
+ 8*4, 9*4, 10*4, 11*4, 12*4, 13*4, 14*4, 15*4, /* f8 .. f15 */
+ 16*4, 17*4, 18*4, 19*4, 20*4, 21*4, 22*4, 23*4, /* f16 .. f23 */
+ 24*4, 25*4, 26*4, 27*4, 28*4, 29*4, 30*4, 31*4, /* f24 .. f31 */
+
+ /* F32 offset starts next to f31: 31*4+4 = 16 * 8. */
+ 16*8, 17*8, 18*8, 19*8, 20*8, 21*8, 22*8, 23*8, /* f32 .. f46 */
+ 24*8, 25*8, 26*8, 27*8, 28*8, 29*8, 30*8, 31*8, /* f48 .. f62 */
+
+ 17 *8, /* pc */
+ 18 *8, /* npc */
+ 16 *8, /* state */
+ /* FSR offset also corresponds to GET/SETFPREGSET, ans is placed
+ next to f62. */
+ 32 *8, /* fsr */
+ -1, /* fprs */
+ /* Y register is 32-bits length, but gdb takes care of that. */
+ 19 *8, /* y */
+
+};
+
+
+struct regs_range_t
+{
+ int regno_start;
+ int regno_end;
+};
+
+static const struct regs_range_t gregs_ranges[] = {
+ { 0, 31 }, /* g0 .. i7 */
+ { 80, 82 }, /* pc .. state */
+ { 84, 85 } /* fprs .. y */
+};
+
+#define N_GREGS_RANGES (sizeof (gregs_ranges) / sizeof (struct regs_range_t))
+
+static const struct regs_range_t fpregs_ranges[] = {
+ { 32, 79 }, /* f0 .. f62 */
+ { 83, 83 } /* fsr */
+};
+
+#define N_FPREGS_RANGES (sizeof (fpregs_ranges) / sizeof (struct regs_range_t))
+
+/* Defined in auto-generated file reg-sparc64.c. */
+void init_registers_sparc64 (void);
+extern const struct target_desc *tdesc_sparc64;
+
+static int
+sparc_cannot_store_register (int regno)
+{
+ return (regno >= sparc_num_regs || sparc_regmap[regno] == -1);
+}
+
+static int
+sparc_cannot_fetch_register (int regno)
+{
+ return (regno >= sparc_num_regs || sparc_regmap[regno] == -1);
+}
+
+static void
+sparc_fill_gregset_to_stack (struct regcache *regcache, const void *buf)
+{
+ int i;
+ CORE_ADDR addr = 0;
+ unsigned char tmp_reg_buf[8];
+ const int l0_regno = find_regno (regcache->tdesc, "l0");
+ const int i7_regno = l0_regno + 15;
+
+ /* These registers have to be stored in the stack. */
+ memcpy (&addr,
+ ((char *) buf) + sparc_regmap[find_regno (regcache->tdesc, "sp")],
+ sizeof (addr));
+
+ addr += BIAS;
+
+ for (i = l0_regno; i <= i7_regno; i++)
+ {
+ collect_register (regcache, i, tmp_reg_buf);
+ (*the_target->write_memory) (addr, tmp_reg_buf, sizeof (tmp_reg_buf));
+ addr += sizeof (tmp_reg_buf);
+ }
+}
+
+static void
+sparc_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+ int range;
+
+ for (range = 0; range < N_GREGS_RANGES; range++)
+ for (i = gregs_ranges[range].regno_start;
+ i <= gregs_ranges[range].regno_end; i++)
+ if (sparc_regmap[i] != -1)
+ collect_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
+
+ sparc_fill_gregset_to_stack (regcache, buf);
+}
+
+static void
+sparc_fill_fpregset (struct regcache *regcache, void *buf)
+{
+ int i;
+ int range;
+
+ for (range = 0; range < N_FPREGS_RANGES; range++)
+ for (i = fpregs_ranges[range].regno_start;
+ i <= fpregs_ranges[range].regno_end; i++)
+ collect_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
+
+}
+
+static void
+sparc_store_gregset_from_stack (struct regcache *regcache, const void *buf)
+{
+ int i;
+ CORE_ADDR addr = 0;
+ unsigned char tmp_reg_buf[8];
+ const int l0_regno = find_regno (regcache->tdesc, "l0");
+ const int i7_regno = l0_regno + 15;
+
+ /* These registers have to be obtained from the stack. */
+ memcpy (&addr,
+ ((char *) buf) + sparc_regmap[find_regno (regcache->tdesc, "sp")],
+ sizeof (addr));
+
+ addr += BIAS;
+
+ for (i = l0_regno; i <= i7_regno; i++)
+ {
+ (*the_target->read_memory) (addr, tmp_reg_buf, sizeof (tmp_reg_buf));
+ supply_register (regcache, i, tmp_reg_buf);
+ addr += sizeof (tmp_reg_buf);
+ }
+}
+
+static void
+sparc_store_gregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+ char zerobuf[8];
+ int range;
+
+ memset (zerobuf, 0, sizeof (zerobuf));
+
+ for (range = 0; range < N_GREGS_RANGES; range++)
+ for (i = gregs_ranges[range].regno_start;
+ i <= gregs_ranges[range].regno_end; i++)
+ if (sparc_regmap[i] != -1)
+ supply_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
+ else
+ supply_register (regcache, i, zerobuf);
+
+ sparc_store_gregset_from_stack (regcache, buf);
+}
+
+static void
+sparc_store_fpregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+ int range;
+
+ for (range = 0; range < N_FPREGS_RANGES; range++)
+ for (i = fpregs_ranges[range].regno_start;
+ i <= fpregs_ranges[range].regno_end;
+ i++)
+ supply_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
+}
+
+static const gdb_byte sparc_breakpoint[INSN_SIZE] = {
+ 0x91, 0xd0, 0x20, 0x01
+};
+#define sparc_breakpoint_len INSN_SIZE
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const unsigned char *
+sparc_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = sparc_breakpoint_len;
+ return sparc_breakpoint;
+}
+
+static int
+sparc_breakpoint_at (CORE_ADDR where)
+{
+ unsigned char insn[INSN_SIZE];
+
+ (*the_target->read_memory) (where, (unsigned char *) insn, sizeof (insn));
+
+ if (memcmp (sparc_breakpoint, insn, sizeof (insn)) == 0)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only
+ uses TRAP Always. */
+
+ return 0;
+}
+
+static void
+sparc_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_sparc64;
+}
+
+static struct regset_info sparc_regsets[] = {
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
+ GENERAL_REGS,
+ sparc_fill_gregset, sparc_store_gregset },
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (fpregset_t),
+ FP_REGS,
+ sparc_fill_fpregset, sparc_store_fpregset },
+ NULL_REGSET
+};
+
+static struct regsets_info sparc_regsets_info =
+ {
+ sparc_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info sparc_usrregs_info =
+ {
+ sparc_num_regs,
+ /* No regmap needs to be provided since this impl. doesn't use
+ USRREGS. */
+ NULL
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &sparc_usrregs_info,
+ &sparc_regsets_info
+ };
+
+static const struct regs_info *
+sparc_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ sparc_arch_setup,
+ sparc_regs_info,
+ sparc_cannot_fetch_register,
+ sparc_cannot_store_register,
+ NULL, /* fetch_register */
+ linux_get_pc_64bit,
+ /* No sparc_set_pc is needed. */
+ NULL,
+ NULL, /* breakpoint_kind_from_pc */
+ sparc_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
+ 0,
+ sparc_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL
+};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+ init_registers_sparc64 ();
+
+ initialize_regsets_info (&sparc_regsets_info);
+}
+++ /dev/null
-/* Target dependent code for GDB on TI C6x systems.
-
- Copyright (C) 2010-2020 Free Software Foundation, Inc.
- Contributed by Andrew Jenner <andrew@codesourcery.com>
- Contributed by Yao Qi <yao@codesourcery.com>
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-#include "arch/tic6x.h"
-#include "tdesc.h"
-
-#include "nat/gdb_ptrace.h"
-#include <endian.h>
-
-#include "gdb_proc_service.h"
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-/* There are at most 69 registers accessible in ptrace. */
-#define TIC6X_NUM_REGS 69
-
-#include <asm/ptrace.h>
-
-/* Defined in auto-generated file tic6x-c64xp-linux.c. */
-void init_registers_tic6x_c64xp_linux (void);
-extern const struct target_desc *tdesc_tic6x_c64xp_linux;
-
-/* Defined in auto-generated file tic6x-c64x-linux.c. */
-void init_registers_tic6x_c64x_linux (void);
-extern const struct target_desc *tdesc_tic6x_c64x_linux;
-
-/* Defined in auto-generated file tic62x-c6xp-linux.c. */
-void init_registers_tic6x_c62x_linux (void);
-extern const struct target_desc *tdesc_tic6x_c62x_linux;
-
-union tic6x_register
-{
- unsigned char buf[4];
-
- int reg32;
-};
-
-/* Return the ptrace ``address'' of register REGNO. */
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-static int tic6x_regmap_c64xp[] = {
- /* A0 - A15 */
- 53, 52, 55, 54, 57, 56, 59, 58,
- 61, 60, 63, 62, 65, 64, 67, 66,
- /* B0 - B15 */
- 23, 22, 25, 24, 27, 26, 29, 28,
- 31, 30, 33, 32, 35, 34, 69, 68,
- /* CSR PC */
- 5, 4,
- /* A16 - A31 */
- 37, 36, 39, 38, 41, 40, 43, 42,
- 45, 44, 47, 46, 49, 48, 51, 50,
- /* B16 - B31 */
- 7, 6, 9, 8, 11, 10, 13, 12,
- 15, 14, 17, 16, 19, 18, 21, 20,
- /* TSR, ILC, RILC */
- 1, 2, 3
-};
-
-static int tic6x_regmap_c64x[] = {
- /* A0 - A15 */
- 51, 50, 53, 52, 55, 54, 57, 56,
- 59, 58, 61, 60, 63, 62, 65, 64,
- /* B0 - B15 */
- 21, 20, 23, 22, 25, 24, 27, 26,
- 29, 28, 31, 30, 33, 32, 67, 66,
- /* CSR PC */
- 3, 2,
- /* A16 - A31 */
- 35, 34, 37, 36, 39, 38, 41, 40,
- 43, 42, 45, 44, 47, 46, 49, 48,
- /* B16 - B31 */
- 5, 4, 7, 6, 9, 8, 11, 10,
- 13, 12, 15, 14, 17, 16, 19, 18,
- -1, -1, -1
-};
-
-static int tic6x_regmap_c62x[] = {
- /* A0 - A15 */
- 19, 18, 21, 20, 23, 22, 25, 24,
- 27, 26, 29, 28, 31, 30, 33, 32,
- /* B0 - B15 */
- 5, 4, 7, 6, 9, 8, 11, 10,
- 13, 12, 15, 14, 17, 16, 35, 34,
- /* CSR, PC */
- 3, 2,
- -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,
- -1, -1, -1
-};
-
-#else
-static int tic6x_regmap_c64xp[] = {
- /* A0 - A15 */
- 52, 53, 54, 55, 56, 57, 58, 59,
- 60, 61, 62, 63, 64, 65, 66, 67,
- /* B0 - B15 */
- 22, 23, 24, 25, 26, 27, 28, 29,
- 30, 31, 32, 33, 34, 35, 68, 69,
- /* CSR PC */
- 4, 5,
- /* A16 - A31 */
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51,
- /* B16 -B31 */
- 6, 7, 8, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 31,
- /* TSR, ILC, RILC */
- 0, 3, 2
-};
-
-static int tic6x_regmap_c64x[] = {
- /* A0 - A15 */
- 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 61, 62, 63, 64, 65,
- /* B0 - B15 */
- 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30, 31, 32, 33, 66, 67,
- /* CSR PC */
- 2, 3,
- /* A16 - A31 */
- 34, 35, 36, 37, 38, 39, 40, 41,
- 42, 43, 44, 45, 46, 47, 48, 49,
- /* B16 - B31 */
- 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19,
- -1, -1, -1
-};
-
-static int tic6x_regmap_c62x[] = {
- /* A0 - A15 */
- 18, 19, 20, 21, 22, 23, 24, 25,
- 26, 27, 28, 29, 30, 31, 32, 33,
- /* B0 - B15 */
- 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 34, 35,
- /* CSR PC */
- 2, 3,
- -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,
- -1, -1, -1
-};
-
-#endif
-
-extern struct linux_target_ops the_low_target;
-
-static int *tic6x_regmap;
-static unsigned int tic6x_breakpoint;
-#define tic6x_breakpoint_len 4
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-tic6x_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = tic6x_breakpoint_len;
- return (const gdb_byte *) &tic6x_breakpoint;
-}
-
-static struct usrregs_info tic6x_usrregs_info =
- {
- TIC6X_NUM_REGS,
- NULL, /* Set in tic6x_read_description. */
- };
-
-static const struct target_desc *
-tic6x_read_description (enum c6x_feature feature)
-{
- static target_desc *tdescs[C6X_LAST] = { };
- struct target_desc **tdesc = &tdescs[feature];
-
- if (*tdesc == NULL)
- {
- *tdesc = tic6x_create_target_description (feature);
- static const char *expedite_regs[] = { "A15", "PC", NULL };
- init_target_desc (*tdesc, expedite_regs);
- }
-
- return *tdesc;
-}
-
-static int
-tic6x_cannot_fetch_register (int regno)
-{
- return (tic6x_regmap[regno] == -1);
-}
-
-static int
-tic6x_cannot_store_register (int regno)
-{
- return (tic6x_regmap[regno] == -1);
-}
-
-static CORE_ADDR
-tic6x_get_pc (struct regcache *regcache)
-{
- union tic6x_register pc;
-
- collect_register_by_name (regcache, "PC", pc.buf);
- return pc.reg32;
-}
-
-static void
-tic6x_set_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- union tic6x_register newpc;
-
- newpc.reg32 = pc;
- supply_register_by_name (regcache, "PC", newpc.buf);
-}
-
-static int
-tic6x_breakpoint_at (CORE_ADDR where)
-{
- unsigned int insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == tic6x_breakpoint)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-/* Fetch the thread-local storage pointer for libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
- return PS_ERR;
-
- /* IDX is the bias from the thread pointer to the beginning of the
- thread descriptor. It has to be subtracted due to implementation
- quirks in libthread_db. */
- *base = (void *) ((char *) *base - idx);
-
- return PS_OK;
-}
-
-static void
-tic6x_collect_register (struct regcache *regcache, int regno,
- union tic6x_register *reg)
-{
- union tic6x_register tmp_reg;
-
- collect_register (regcache, regno, &tmp_reg.reg32);
- reg->reg32 = tmp_reg.reg32;
-}
-
-static void
-tic6x_supply_register (struct regcache *regcache, int regno,
- const union tic6x_register *reg)
-{
- int offset = 0;
-
- supply_register (regcache, regno, reg->buf + offset);
-}
-
-static void
-tic6x_fill_gregset (struct regcache *regcache, void *buf)
-{
- auto regset = static_cast<union tic6x_register *> (buf);
- int i;
-
- for (i = 0; i < TIC6X_NUM_REGS; i++)
- if (tic6x_regmap[i] != -1)
- tic6x_collect_register (regcache, i, regset + tic6x_regmap[i]);
-}
-
-static void
-tic6x_store_gregset (struct regcache *regcache, const void *buf)
-{
- const auto regset = static_cast<const union tic6x_register *> (buf);
- int i;
-
- for (i = 0; i < TIC6X_NUM_REGS; i++)
- if (tic6x_regmap[i] != -1)
- tic6x_supply_register (regcache, i, regset + tic6x_regmap[i]);
-}
-
-static struct regset_info tic6x_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, TIC6X_NUM_REGS * 4, GENERAL_REGS,
- tic6x_fill_gregset, tic6x_store_gregset },
- NULL_REGSET
-};
-
-static void
-tic6x_arch_setup (void)
-{
- register unsigned int csr asm ("B2");
- unsigned int cpuid;
- enum c6x_feature feature = C6X_CORE;
-
- /* Determine the CPU we're running on to find the register order. */
- __asm__ ("MVC .S2 CSR,%0" : "=r" (csr) :);
- cpuid = csr >> 24;
- switch (cpuid)
- {
- case 0x00: /* C62x */
- case 0x02: /* C67x */
- tic6x_regmap = tic6x_regmap_c62x;
- tic6x_breakpoint = 0x0000a122; /* BNOP .S2 0,5 */
- feature = C6X_CORE;
- break;
- case 0x03: /* C67x+ */
- tic6x_regmap = tic6x_regmap_c64x;
- tic6x_breakpoint = 0x0000a122; /* BNOP .S2 0,5 */
- feature = C6X_GP;
- break;
- case 0x0c: /* C64x */
- tic6x_regmap = tic6x_regmap_c64x;
- tic6x_breakpoint = 0x0000a122; /* BNOP .S2 0,5 */
- feature = C6X_GP;
- break;
- case 0x10: /* C64x+ */
- case 0x14: /* C674x */
- case 0x15: /* C66x */
- tic6x_regmap = tic6x_regmap_c64xp;
- tic6x_breakpoint = 0x56454314; /* illegal opcode */
- feature = C6X_C6XP;
- break;
- default:
- error ("Unknown CPU ID 0x%02x", cpuid);
- }
- tic6x_usrregs_info.regmap = tic6x_regmap;
-
- current_process ()->tdesc = tic6x_read_description (feature);
-}
-
-/* Support for hardware single step. */
-
-static int
-tic6x_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static struct regsets_info tic6x_regsets_info =
- {
- tic6x_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &tic6x_usrregs_info,
- &tic6x_regsets_info
- };
-
-static const struct regs_info *
-tic6x_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- tic6x_arch_setup,
- tic6x_regs_info,
- tic6x_cannot_fetch_register,
- tic6x_cannot_store_register,
- NULL, /* fetch_register */
- tic6x_get_pc,
- tic6x_set_pc,
- NULL, /* breakpoint_kind_from_pc */
- tic6x_sw_breakpoint_from_kind,
- NULL,
- 0,
- tic6x_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- tic6x_supports_hardware_single_step,
-};
-
-#if GDB_SELF_TEST
-#include "gdbsupport/selftest.h"
-
-namespace selftests {
-namespace tdesc {
-static void
-tic6x_tdesc_test ()
-{
- SELF_CHECK (*tdesc_tic6x_c62x_linux == *tic6x_read_description (C6X_CORE));
- SELF_CHECK (*tdesc_tic6x_c64x_linux == *tic6x_read_description (C6X_GP));
- SELF_CHECK (*tdesc_tic6x_c64xp_linux == *tic6x_read_description (C6X_C6XP));
-}
-}
-}
-#endif
-
-void
-initialize_low_arch (void)
-{
-#if GDB_SELF_TEST
- /* Initialize the Linux target descriptions. */
- init_registers_tic6x_c64xp_linux ();
- init_registers_tic6x_c64x_linux ();
- init_registers_tic6x_c62x_linux ();
-
- selftests::register_test ("tic6x-tdesc", selftests::tdesc::tic6x_tdesc_test);
-#endif
-
- initialize_regsets_info (&tic6x_regsets_info);
-}
--- /dev/null
+/* Target dependent code for GDB on TI C6x systems.
+
+ Copyright (C) 2010-2020 Free Software Foundation, Inc.
+ Contributed by Andrew Jenner <andrew@codesourcery.com>
+ Contributed by Yao Qi <yao@codesourcery.com>
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+#include "arch/tic6x.h"
+#include "tdesc.h"
+
+#include "nat/gdb_ptrace.h"
+#include <endian.h>
+
+#include "gdb_proc_service.h"
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+/* There are at most 69 registers accessible in ptrace. */
+#define TIC6X_NUM_REGS 69
+
+#include <asm/ptrace.h>
+
+/* Defined in auto-generated file tic6x-c64xp-linux.c. */
+void init_registers_tic6x_c64xp_linux (void);
+extern const struct target_desc *tdesc_tic6x_c64xp_linux;
+
+/* Defined in auto-generated file tic6x-c64x-linux.c. */
+void init_registers_tic6x_c64x_linux (void);
+extern const struct target_desc *tdesc_tic6x_c64x_linux;
+
+/* Defined in auto-generated file tic62x-c6xp-linux.c. */
+void init_registers_tic6x_c62x_linux (void);
+extern const struct target_desc *tdesc_tic6x_c62x_linux;
+
+union tic6x_register
+{
+ unsigned char buf[4];
+
+ int reg32;
+};
+
+/* Return the ptrace ``address'' of register REGNO. */
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static int tic6x_regmap_c64xp[] = {
+ /* A0 - A15 */
+ 53, 52, 55, 54, 57, 56, 59, 58,
+ 61, 60, 63, 62, 65, 64, 67, 66,
+ /* B0 - B15 */
+ 23, 22, 25, 24, 27, 26, 29, 28,
+ 31, 30, 33, 32, 35, 34, 69, 68,
+ /* CSR PC */
+ 5, 4,
+ /* A16 - A31 */
+ 37, 36, 39, 38, 41, 40, 43, 42,
+ 45, 44, 47, 46, 49, 48, 51, 50,
+ /* B16 - B31 */
+ 7, 6, 9, 8, 11, 10, 13, 12,
+ 15, 14, 17, 16, 19, 18, 21, 20,
+ /* TSR, ILC, RILC */
+ 1, 2, 3
+};
+
+static int tic6x_regmap_c64x[] = {
+ /* A0 - A15 */
+ 51, 50, 53, 52, 55, 54, 57, 56,
+ 59, 58, 61, 60, 63, 62, 65, 64,
+ /* B0 - B15 */
+ 21, 20, 23, 22, 25, 24, 27, 26,
+ 29, 28, 31, 30, 33, 32, 67, 66,
+ /* CSR PC */
+ 3, 2,
+ /* A16 - A31 */
+ 35, 34, 37, 36, 39, 38, 41, 40,
+ 43, 42, 45, 44, 47, 46, 49, 48,
+ /* B16 - B31 */
+ 5, 4, 7, 6, 9, 8, 11, 10,
+ 13, 12, 15, 14, 17, 16, 19, 18,
+ -1, -1, -1
+};
+
+static int tic6x_regmap_c62x[] = {
+ /* A0 - A15 */
+ 19, 18, 21, 20, 23, 22, 25, 24,
+ 27, 26, 29, 28, 31, 30, 33, 32,
+ /* B0 - B15 */
+ 5, 4, 7, 6, 9, 8, 11, 10,
+ 13, 12, 15, 14, 17, 16, 35, 34,
+ /* CSR, PC */
+ 3, 2,
+ -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,
+ -1, -1, -1
+};
+
+#else
+static int tic6x_regmap_c64xp[] = {
+ /* A0 - A15 */
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67,
+ /* B0 - B15 */
+ 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 68, 69,
+ /* CSR PC */
+ 4, 5,
+ /* A16 - A31 */
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ /* B16 -B31 */
+ 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 31,
+ /* TSR, ILC, RILC */
+ 0, 3, 2
+};
+
+static int tic6x_regmap_c64x[] = {
+ /* A0 - A15 */
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65,
+ /* B0 - B15 */
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 66, 67,
+ /* CSR PC */
+ 2, 3,
+ /* A16 - A31 */
+ 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49,
+ /* B16 - B31 */
+ 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ -1, -1, -1
+};
+
+static int tic6x_regmap_c62x[] = {
+ /* A0 - A15 */
+ 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33,
+ /* B0 - B15 */
+ 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 34, 35,
+ /* CSR PC */
+ 2, 3,
+ -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,
+ -1, -1, -1
+};
+
+#endif
+
+extern struct linux_target_ops the_low_target;
+
+static int *tic6x_regmap;
+static unsigned int tic6x_breakpoint;
+#define tic6x_breakpoint_len 4
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+tic6x_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = tic6x_breakpoint_len;
+ return (const gdb_byte *) &tic6x_breakpoint;
+}
+
+static struct usrregs_info tic6x_usrregs_info =
+ {
+ TIC6X_NUM_REGS,
+ NULL, /* Set in tic6x_read_description. */
+ };
+
+static const struct target_desc *
+tic6x_read_description (enum c6x_feature feature)
+{
+ static target_desc *tdescs[C6X_LAST] = { };
+ struct target_desc **tdesc = &tdescs[feature];
+
+ if (*tdesc == NULL)
+ {
+ *tdesc = tic6x_create_target_description (feature);
+ static const char *expedite_regs[] = { "A15", "PC", NULL };
+ init_target_desc (*tdesc, expedite_regs);
+ }
+
+ return *tdesc;
+}
+
+static int
+tic6x_cannot_fetch_register (int regno)
+{
+ return (tic6x_regmap[regno] == -1);
+}
+
+static int
+tic6x_cannot_store_register (int regno)
+{
+ return (tic6x_regmap[regno] == -1);
+}
+
+static CORE_ADDR
+tic6x_get_pc (struct regcache *regcache)
+{
+ union tic6x_register pc;
+
+ collect_register_by_name (regcache, "PC", pc.buf);
+ return pc.reg32;
+}
+
+static void
+tic6x_set_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ union tic6x_register newpc;
+
+ newpc.reg32 = pc;
+ supply_register_by_name (regcache, "PC", newpc.buf);
+}
+
+static int
+tic6x_breakpoint_at (CORE_ADDR where)
+{
+ unsigned int insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
+ if (insn == tic6x_breakpoint)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+/* Fetch the thread-local storage pointer for libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
+ return PS_ERR;
+
+ /* IDX is the bias from the thread pointer to the beginning of the
+ thread descriptor. It has to be subtracted due to implementation
+ quirks in libthread_db. */
+ *base = (void *) ((char *) *base - idx);
+
+ return PS_OK;
+}
+
+static void
+tic6x_collect_register (struct regcache *regcache, int regno,
+ union tic6x_register *reg)
+{
+ union tic6x_register tmp_reg;
+
+ collect_register (regcache, regno, &tmp_reg.reg32);
+ reg->reg32 = tmp_reg.reg32;
+}
+
+static void
+tic6x_supply_register (struct regcache *regcache, int regno,
+ const union tic6x_register *reg)
+{
+ int offset = 0;
+
+ supply_register (regcache, regno, reg->buf + offset);
+}
+
+static void
+tic6x_fill_gregset (struct regcache *regcache, void *buf)
+{
+ auto regset = static_cast<union tic6x_register *> (buf);
+ int i;
+
+ for (i = 0; i < TIC6X_NUM_REGS; i++)
+ if (tic6x_regmap[i] != -1)
+ tic6x_collect_register (regcache, i, regset + tic6x_regmap[i]);
+}
+
+static void
+tic6x_store_gregset (struct regcache *regcache, const void *buf)
+{
+ const auto regset = static_cast<const union tic6x_register *> (buf);
+ int i;
+
+ for (i = 0; i < TIC6X_NUM_REGS; i++)
+ if (tic6x_regmap[i] != -1)
+ tic6x_supply_register (regcache, i, regset + tic6x_regmap[i]);
+}
+
+static struct regset_info tic6x_regsets[] = {
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, TIC6X_NUM_REGS * 4, GENERAL_REGS,
+ tic6x_fill_gregset, tic6x_store_gregset },
+ NULL_REGSET
+};
+
+static void
+tic6x_arch_setup (void)
+{
+ register unsigned int csr asm ("B2");
+ unsigned int cpuid;
+ enum c6x_feature feature = C6X_CORE;
+
+ /* Determine the CPU we're running on to find the register order. */
+ __asm__ ("MVC .S2 CSR,%0" : "=r" (csr) :);
+ cpuid = csr >> 24;
+ switch (cpuid)
+ {
+ case 0x00: /* C62x */
+ case 0x02: /* C67x */
+ tic6x_regmap = tic6x_regmap_c62x;
+ tic6x_breakpoint = 0x0000a122; /* BNOP .S2 0,5 */
+ feature = C6X_CORE;
+ break;
+ case 0x03: /* C67x+ */
+ tic6x_regmap = tic6x_regmap_c64x;
+ tic6x_breakpoint = 0x0000a122; /* BNOP .S2 0,5 */
+ feature = C6X_GP;
+ break;
+ case 0x0c: /* C64x */
+ tic6x_regmap = tic6x_regmap_c64x;
+ tic6x_breakpoint = 0x0000a122; /* BNOP .S2 0,5 */
+ feature = C6X_GP;
+ break;
+ case 0x10: /* C64x+ */
+ case 0x14: /* C674x */
+ case 0x15: /* C66x */
+ tic6x_regmap = tic6x_regmap_c64xp;
+ tic6x_breakpoint = 0x56454314; /* illegal opcode */
+ feature = C6X_C6XP;
+ break;
+ default:
+ error ("Unknown CPU ID 0x%02x", cpuid);
+ }
+ tic6x_usrregs_info.regmap = tic6x_regmap;
+
+ current_process ()->tdesc = tic6x_read_description (feature);
+}
+
+/* Support for hardware single step. */
+
+static int
+tic6x_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static struct regsets_info tic6x_regsets_info =
+ {
+ tic6x_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &tic6x_usrregs_info,
+ &tic6x_regsets_info
+ };
+
+static const struct regs_info *
+tic6x_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ tic6x_arch_setup,
+ tic6x_regs_info,
+ tic6x_cannot_fetch_register,
+ tic6x_cannot_store_register,
+ NULL, /* fetch_register */
+ tic6x_get_pc,
+ tic6x_set_pc,
+ NULL, /* breakpoint_kind_from_pc */
+ tic6x_sw_breakpoint_from_kind,
+ NULL,
+ 0,
+ tic6x_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ tic6x_supports_hardware_single_step,
+};
+
+#if GDB_SELF_TEST
+#include "gdbsupport/selftest.h"
+
+namespace selftests {
+namespace tdesc {
+static void
+tic6x_tdesc_test ()
+{
+ SELF_CHECK (*tdesc_tic6x_c62x_linux == *tic6x_read_description (C6X_CORE));
+ SELF_CHECK (*tdesc_tic6x_c64x_linux == *tic6x_read_description (C6X_GP));
+ SELF_CHECK (*tdesc_tic6x_c64xp_linux == *tic6x_read_description (C6X_C6XP));
+}
+}
+}
+#endif
+
+void
+initialize_low_arch (void)
+{
+#if GDB_SELF_TEST
+ /* Initialize the Linux target descriptions. */
+ init_registers_tic6x_c64xp_linux ();
+ init_registers_tic6x_c64x_linux ();
+ init_registers_tic6x_c62x_linux ();
+
+ selftests::register_test ("tic6x-tdesc", selftests::tdesc::tic6x_tdesc_test);
+#endif
+
+ initialize_regsets_info (&tic6x_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/TILE-Gx specific low level interface, GDBserver.
-
- Copyright (C) 2012-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-#include "linux-low.h"
-
-#include <arch/abi.h>
-#include "nat/gdb_ptrace.h"
-
-/* Defined in auto-generated file reg-tilegx.c. */
-void init_registers_tilegx (void);
-extern const struct target_desc *tdesc_tilegx;
-
-/* Defined in auto-generated file reg-tilegx32.c. */
-void init_registers_tilegx32 (void);
-extern const struct target_desc *tdesc_tilegx32;
-
-#define tile_num_regs 65
-
-static int tile_regmap[] =
-{
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 56
-};
-
-static int
-tile_cannot_fetch_register (int regno)
-{
- if (regno >= 0 && regno < 56)
- return 0;
- else if (regno == 64)
- return 0;
- else
- return 1;
-}
-
-static int
-tile_cannot_store_register (int regno)
-{
- if (regno >= 0 && regno < 56)
- return 0;
- else if (regno == 64)
- return 0;
- else
- return 1;
-}
-
-static uint64_t tile_breakpoint = 0x400b3cae70166000ULL;
-#define tile_breakpoint_len 8
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-tile_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = tile_breakpoint_len;
- return (const gdb_byte *) &tile_breakpoint;
-}
-
-static int
-tile_breakpoint_at (CORE_ADDR where)
-{
- uint64_t insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn, 8);
- if (insn == tile_breakpoint)
- return 1;
-
- /* If necessary, recognize more trap instructions here. GDB only uses the
- one. */
- return 0;
-}
-
-static void
-tile_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
-
- for (i = 0; i < tile_num_regs; i++)
- if (tile_regmap[i] != -1)
- collect_register (regcache, i, ((uint_reg_t *) buf) + tile_regmap[i]);
-}
-
-static void
-tile_store_gregset (struct regcache *regcache, const void *buf)
-{
- int i;
-
- for (i = 0; i < tile_num_regs; i++)
- if (tile_regmap[i] != -1)
- supply_register (regcache, i, ((uint_reg_t *) buf) + tile_regmap[i]);
-}
-
-static struct regset_info tile_regsets[] =
-{
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, tile_num_regs * 8,
- GENERAL_REGS, tile_fill_gregset, tile_store_gregset },
- NULL_REGSET
-};
-
-static struct regsets_info tile_regsets_info =
- {
- tile_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct usrregs_info tile_usrregs_info =
- {
- tile_num_regs,
- tile_regmap,
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- &tile_usrregs_info,
- &tile_regsets_info,
- };
-
-static const struct regs_info *
-tile_regs_info (void)
-{
- return ®s_info;
-}
-
-static void
-tile_arch_setup (void)
-{
- int pid = pid_of (current_thread);
- unsigned int machine;
- int is_elf64 = linux_pid_exe_is_elf_64_file (pid, &machine);
-
- if (sizeof (void *) == 4)
- if (is_elf64 > 0)
- error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-
- if (!is_elf64)
- current_process ()->tdesc = tdesc_tilegx32;
- else
- current_process ()->tdesc = tdesc_tilegx;
-}
-
-/* Support for hardware single step. */
-
-static int
-tile_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-
-struct linux_target_ops the_low_target =
-{
- tile_arch_setup,
- tile_regs_info,
- tile_cannot_fetch_register,
- tile_cannot_store_register,
- NULL,
- linux_get_pc_64bit,
- linux_set_pc_64bit,
- NULL, /* breakpoint_kind_from_pc */
- tile_sw_breakpoint_from_kind,
- NULL,
- 0,
- tile_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- tile_supports_hardware_single_step,
-};
-
-void
-initialize_low_arch (void)
-{
- init_registers_tilegx32();
- init_registers_tilegx();
-
- initialize_regsets_info (&tile_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/TILE-Gx specific low level interface, GDBserver.
+
+ Copyright (C) 2012-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "linux-low.h"
+
+#include <arch/abi.h>
+#include "nat/gdb_ptrace.h"
+
+/* Defined in auto-generated file reg-tilegx.c. */
+void init_registers_tilegx (void);
+extern const struct target_desc *tdesc_tilegx;
+
+/* Defined in auto-generated file reg-tilegx32.c. */
+void init_registers_tilegx32 (void);
+extern const struct target_desc *tdesc_tilegx32;
+
+#define tile_num_regs 65
+
+static int tile_regmap[] =
+{
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 56
+};
+
+static int
+tile_cannot_fetch_register (int regno)
+{
+ if (regno >= 0 && regno < 56)
+ return 0;
+ else if (regno == 64)
+ return 0;
+ else
+ return 1;
+}
+
+static int
+tile_cannot_store_register (int regno)
+{
+ if (regno >= 0 && regno < 56)
+ return 0;
+ else if (regno == 64)
+ return 0;
+ else
+ return 1;
+}
+
+static uint64_t tile_breakpoint = 0x400b3cae70166000ULL;
+#define tile_breakpoint_len 8
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+tile_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = tile_breakpoint_len;
+ return (const gdb_byte *) &tile_breakpoint;
+}
+
+static int
+tile_breakpoint_at (CORE_ADDR where)
+{
+ uint64_t insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn, 8);
+ if (insn == tile_breakpoint)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses the
+ one. */
+ return 0;
+}
+
+static void
+tile_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+
+ for (i = 0; i < tile_num_regs; i++)
+ if (tile_regmap[i] != -1)
+ collect_register (regcache, i, ((uint_reg_t *) buf) + tile_regmap[i]);
+}
+
+static void
+tile_store_gregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+
+ for (i = 0; i < tile_num_regs; i++)
+ if (tile_regmap[i] != -1)
+ supply_register (regcache, i, ((uint_reg_t *) buf) + tile_regmap[i]);
+}
+
+static struct regset_info tile_regsets[] =
+{
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, tile_num_regs * 8,
+ GENERAL_REGS, tile_fill_gregset, tile_store_gregset },
+ NULL_REGSET
+};
+
+static struct regsets_info tile_regsets_info =
+ {
+ tile_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info tile_usrregs_info =
+ {
+ tile_num_regs,
+ tile_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &tile_usrregs_info,
+ &tile_regsets_info,
+ };
+
+static const struct regs_info *
+tile_regs_info (void)
+{
+ return ®s_info;
+}
+
+static void
+tile_arch_setup (void)
+{
+ int pid = pid_of (current_thread);
+ unsigned int machine;
+ int is_elf64 = linux_pid_exe_is_elf_64_file (pid, &machine);
+
+ if (sizeof (void *) == 4)
+ if (is_elf64 > 0)
+ error (_("Can't debug 64-bit process with 32-bit GDBserver"));
+
+ if (!is_elf64)
+ current_process ()->tdesc = tdesc_tilegx32;
+ else
+ current_process ()->tdesc = tdesc_tilegx;
+}
+
+/* Support for hardware single step. */
+
+static int
+tile_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+
+struct linux_target_ops the_low_target =
+{
+ tile_arch_setup,
+ tile_regs_info,
+ tile_cannot_fetch_register,
+ tile_cannot_store_register,
+ NULL,
+ linux_get_pc_64bit,
+ linux_set_pc_64bit,
+ NULL, /* breakpoint_kind_from_pc */
+ tile_sw_breakpoint_from_kind,
+ NULL,
+ 0,
+ tile_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ tile_supports_hardware_single_step,
+};
+
+void
+initialize_low_arch (void)
+{
+ init_registers_tilegx32();
+ init_registers_tilegx();
+
+ initialize_regsets_info (&tile_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/x86-64 specific low level interface, for the remote server
- for GDB.
- Copyright (C) 2002-2020 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 <signal.h>
-#include <limits.h>
-#include <inttypes.h>
-#include "linux-low.h"
-#include "i387-fp.h"
-#include "x86-low.h"
-#include "gdbsupport/x86-xstate.h"
-#include "nat/gdb_ptrace.h"
-
-#ifdef __x86_64__
-#include "nat/amd64-linux-siginfo.h"
-#endif
-
-#include "gdb_proc_service.h"
-/* Don't include elf/common.h if linux/elf.h got included by
- gdb_proc_service.h. */
-#ifndef ELFMAG0
-#include "elf/common.h"
-#endif
-
-#include "gdbsupport/agent.h"
-#include "tdesc.h"
-#include "tracepoint.h"
-#include "ax.h"
-#include "nat/linux-nat.h"
-#include "nat/x86-linux.h"
-#include "nat/x86-linux-dregs.h"
-#include "linux-x86-tdesc.h"
-
-#ifdef __x86_64__
-static struct target_desc *tdesc_amd64_linux_no_xml;
-#endif
-static struct target_desc *tdesc_i386_linux_no_xml;
-
-
-static unsigned char jump_insn[] = { 0xe9, 0, 0, 0, 0 };
-static unsigned char small_jump_insn[] = { 0x66, 0xe9, 0, 0 };
-
-/* Backward compatibility for gdb without XML support. */
-
-static const char *xmltarget_i386_linux_no_xml = "@<target>\
-<architecture>i386</architecture>\
-<osabi>GNU/Linux</osabi>\
-</target>";
-
-#ifdef __x86_64__
-static const char *xmltarget_amd64_linux_no_xml = "@<target>\
-<architecture>i386:x86-64</architecture>\
-<osabi>GNU/Linux</osabi>\
-</target>";
-#endif
-
-#include <sys/reg.h>
-#include <sys/procfs.h>
-#include <sys/uio.h>
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-/* This definition comes from prctl.h, but some kernels may not have it. */
-#ifndef PTRACE_ARCH_PRCTL
-#define PTRACE_ARCH_PRCTL 30
-#endif
-
-/* The following definitions come from prctl.h, but may be absent
- for certain configurations. */
-#ifndef ARCH_GET_FS
-#define ARCH_SET_GS 0x1001
-#define ARCH_SET_FS 0x1002
-#define ARCH_GET_FS 0x1003
-#define ARCH_GET_GS 0x1004
-#endif
-
-/* Per-process arch-specific data we want to keep. */
-
-struct arch_process_info
-{
- struct x86_debug_reg_state debug_reg_state;
-};
-
-#ifdef __x86_64__
-
-/* Mapping between the general-purpose registers in `struct user'
- format and GDB's register array layout.
- Note that the transfer layout uses 64-bit regs. */
-static /*const*/ int i386_regmap[] =
-{
- RAX * 8, RCX * 8, RDX * 8, RBX * 8,
- RSP * 8, RBP * 8, RSI * 8, RDI * 8,
- RIP * 8, EFLAGS * 8, CS * 8, SS * 8,
- DS * 8, ES * 8, FS * 8, GS * 8
-};
-
-#define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0]))
-
-/* So code below doesn't have to care, i386 or amd64. */
-#define ORIG_EAX ORIG_RAX
-#define REGSIZE 8
-
-static const int x86_64_regmap[] =
-{
- RAX * 8, RBX * 8, RCX * 8, RDX * 8,
- RSI * 8, RDI * 8, RBP * 8, RSP * 8,
- R8 * 8, R9 * 8, R10 * 8, R11 * 8,
- R12 * 8, R13 * 8, R14 * 8, R15 * 8,
- RIP * 8, EFLAGS * 8, CS * 8, SS * 8,
- DS * 8, ES * 8, FS * 8, GS * 8,
- -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, -1,
- ORIG_RAX * 8,
-#ifdef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
- 21 * 8, 22 * 8,
-#else
- -1, -1,
-#endif
- -1, -1, -1, -1, /* MPX registers BND0 ... BND3. */
- -1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */
- -1, -1, -1, -1, -1, -1, -1, -1, /* xmm16 ... xmm31 (AVX512) */
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, /* ymm16 ... ymm31 (AVX512) */
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, /* k0 ... k7 (AVX512) */
- -1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm31 (AVX512) */
- -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 /* pkru */
-};
-
-#define X86_64_NUM_REGS (sizeof (x86_64_regmap) / sizeof (x86_64_regmap[0]))
-#define X86_64_USER_REGS (GS + 1)
-
-#else /* ! __x86_64__ */
-
-/* Mapping between the general-purpose registers in `struct user'
- format and GDB's register array layout. */
-static /*const*/ int i386_regmap[] =
-{
- EAX * 4, ECX * 4, EDX * 4, EBX * 4,
- UESP * 4, EBP * 4, ESI * 4, EDI * 4,
- EIP * 4, EFL * 4, CS * 4, SS * 4,
- DS * 4, ES * 4, FS * 4, GS * 4
-};
-
-#define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0]))
-
-#define REGSIZE 4
-
-#endif
-
-#ifdef __x86_64__
-
-/* Returns true if the current inferior belongs to a x86-64 process,
- per the tdesc. */
-
-static int
-is_64bit_tdesc (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
-
- return register_size (regcache->tdesc, 0) == 8;
-}
-
-#endif
-
-\f
-/* Called by libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
-#ifdef __x86_64__
- int use_64bit = is_64bit_tdesc ();
-
- if (use_64bit)
- {
- switch (idx)
- {
- case FS:
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0)
- return PS_OK;
- break;
- case GS:
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
- return PS_OK;
- break;
- default:
- return PS_BADADDR;
- }
- return PS_ERR;
- }
-#endif
-
- {
- unsigned int desc[4];
-
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid,
- (void *) (intptr_t) idx, (unsigned long) &desc) < 0)
- return PS_ERR;
-
- /* Ensure we properly extend the value to 64-bits for x86_64. */
- *base = (void *) (uintptr_t) desc[1];
- return PS_OK;
- }
-}
-
-/* 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
-x86_get_thread_area (int lwpid, CORE_ADDR *addr)
-{
-#ifdef __x86_64__
- int use_64bit = is_64bit_tdesc ();
-
- if (use_64bit)
- {
- void *base;
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_FS) == 0)
- {
- *addr = (CORE_ADDR) (uintptr_t) base;
- return 0;
- }
-
- return -1;
- }
-#endif
-
- {
- struct lwp_info *lwp = find_lwp_pid (ptid_t (lwpid));
- struct thread_info *thr = get_lwp_thread (lwp);
- struct regcache *regcache = get_thread_regcache (thr, 1);
- unsigned int desc[4];
- ULONGEST gs = 0;
- const int reg_thread_area = 3; /* bits to scale down register value. */
- int idx;
-
- collect_register_by_name (regcache, "gs", &gs);
-
- idx = gs >> reg_thread_area;
-
- if (ptrace (PTRACE_GET_THREAD_AREA,
- lwpid_of (thr),
- (void *) (long) idx, (unsigned long) &desc) < 0)
- return -1;
-
- *addr = desc[1];
- return 0;
- }
-}
-
-
-\f
-static int
-x86_cannot_store_register (int regno)
-{
-#ifdef __x86_64__
- if (is_64bit_tdesc ())
- return 0;
-#endif
-
- return regno >= I386_NUM_REGS;
-}
-
-static int
-x86_cannot_fetch_register (int regno)
-{
-#ifdef __x86_64__
- if (is_64bit_tdesc ())
- return 0;
-#endif
-
- return regno >= I386_NUM_REGS;
-}
-
-static void
-x86_fill_gregset (struct regcache *regcache, void *buf)
-{
- int i;
-
-#ifdef __x86_64__
- if (register_size (regcache->tdesc, 0) == 8)
- {
- for (i = 0; i < X86_64_NUM_REGS; i++)
- if (x86_64_regmap[i] != -1)
- collect_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
-
-#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
- {
- unsigned long base;
- int lwpid = lwpid_of (current_thread);
-
- collect_register_by_name (regcache, "fs_base", &base);
- ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_FS);
-
- collect_register_by_name (regcache, "gs_base", &base);
- ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_GS);
- }
-#endif
-
- return;
- }
-
- /* 32-bit inferior registers need to be zero-extended.
- Callers would read uninitialized memory otherwise. */
- memset (buf, 0x00, X86_64_USER_REGS * 8);
-#endif
-
- for (i = 0; i < I386_NUM_REGS; i++)
- collect_register (regcache, i, ((char *) buf) + i386_regmap[i]);
-
- collect_register_by_name (regcache, "orig_eax",
- ((char *) buf) + ORIG_EAX * REGSIZE);
-
-#ifdef __x86_64__
- /* Sign extend EAX value to avoid potential syscall restart
- problems.
-
- See amd64_linux_collect_native_gregset() in gdb/amd64-linux-nat.c
- for a detailed explanation. */
- if (register_size (regcache->tdesc, 0) == 4)
- {
- void *ptr = ((gdb_byte *) buf
- + i386_regmap[find_regno (regcache->tdesc, "eax")]);
-
- *(int64_t *) ptr = *(int32_t *) ptr;
- }
-#endif
-}
-
-static void
-x86_store_gregset (struct regcache *regcache, const void *buf)
-{
- int i;
-
-#ifdef __x86_64__
- if (register_size (regcache->tdesc, 0) == 8)
- {
- for (i = 0; i < X86_64_NUM_REGS; i++)
- if (x86_64_regmap[i] != -1)
- supply_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
-
-#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
- {
- unsigned long base;
- int lwpid = lwpid_of (current_thread);
-
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_FS) == 0)
- supply_register_by_name (regcache, "fs_base", &base);
-
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_GS) == 0)
- supply_register_by_name (regcache, "gs_base", &base);
- }
-#endif
- return;
- }
-#endif
-
- for (i = 0; i < I386_NUM_REGS; i++)
- supply_register (regcache, i, ((char *) buf) + i386_regmap[i]);
-
- supply_register_by_name (regcache, "orig_eax",
- ((char *) buf) + ORIG_EAX * REGSIZE);
-}
-
-static void
-x86_fill_fpregset (struct regcache *regcache, void *buf)
-{
-#ifdef __x86_64__
- i387_cache_to_fxsave (regcache, buf);
-#else
- i387_cache_to_fsave (regcache, buf);
-#endif
-}
-
-static void
-x86_store_fpregset (struct regcache *regcache, const void *buf)
-{
-#ifdef __x86_64__
- i387_fxsave_to_cache (regcache, buf);
-#else
- i387_fsave_to_cache (regcache, buf);
-#endif
-}
-
-#ifndef __x86_64__
-
-static void
-x86_fill_fpxregset (struct regcache *regcache, void *buf)
-{
- i387_cache_to_fxsave (regcache, buf);
-}
-
-static void
-x86_store_fpxregset (struct regcache *regcache, const void *buf)
-{
- i387_fxsave_to_cache (regcache, buf);
-}
-
-#endif
-
-static void
-x86_fill_xstateregset (struct regcache *regcache, void *buf)
-{
- i387_cache_to_xsave (regcache, buf);
-}
-
-static void
-x86_store_xstateregset (struct regcache *regcache, const void *buf)
-{
- i387_xsave_to_cache (regcache, buf);
-}
-
-/* ??? The non-biarch i386 case stores all the i387 regs twice.
- Once in i387_.*fsave.* and once in i387_.*fxsave.*.
- This is, presumably, to handle the case where PTRACE_[GS]ETFPXREGS
- doesn't work. IWBN to avoid the duplication in the case where it
- does work. Maybe the arch_setup routine could check whether it works
- and update the supported regsets accordingly. */
-
-static struct regset_info x86_regsets[] =
-{
-#ifdef HAVE_PTRACE_GETREGS
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
- GENERAL_REGS,
- x86_fill_gregset, x86_store_gregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_X86_XSTATE, 0,
- EXTENDED_REGS, x86_fill_xstateregset, x86_store_xstateregset },
-# ifndef __x86_64__
-# ifdef HAVE_PTRACE_GETFPXREGS
- { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, 0, sizeof (elf_fpxregset_t),
- EXTENDED_REGS,
- x86_fill_fpxregset, x86_store_fpxregset },
-# endif
-# endif
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (elf_fpregset_t),
- FP_REGS,
- x86_fill_fpregset, x86_store_fpregset },
-#endif /* HAVE_PTRACE_GETREGS */
- NULL_REGSET
-};
-
-static CORE_ADDR
-x86_get_pc (struct regcache *regcache)
-{
- int use_64bit = register_size (regcache->tdesc, 0) == 8;
-
- if (use_64bit)
- {
- uint64_t pc;
-
- collect_register_by_name (regcache, "rip", &pc);
- return (CORE_ADDR) pc;
- }
- else
- {
- uint32_t pc;
-
- collect_register_by_name (regcache, "eip", &pc);
- return (CORE_ADDR) pc;
- }
-}
-
-static void
-x86_set_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- int use_64bit = register_size (regcache->tdesc, 0) == 8;
-
- if (use_64bit)
- {
- uint64_t newpc = pc;
-
- supply_register_by_name (regcache, "rip", &newpc);
- }
- else
- {
- uint32_t newpc = pc;
-
- supply_register_by_name (regcache, "eip", &newpc);
- }
-}
-\f
-static const gdb_byte x86_breakpoint[] = { 0xCC };
-#define x86_breakpoint_len 1
-
-static int
-x86_breakpoint_at (CORE_ADDR pc)
-{
- unsigned char c;
-
- (*the_target->read_memory) (pc, &c, 1);
- if (c == 0xCC)
- return 1;
-
- return 0;
-}
-\f
-/* Low-level function vector. */
-struct x86_dr_low_type x86_dr_low =
- {
- x86_linux_dr_set_control,
- x86_linux_dr_set_addr,
- x86_linux_dr_get_addr,
- x86_linux_dr_get_status,
- x86_linux_dr_get_control,
- sizeof (void *),
- };
-\f
-/* Breakpoint/Watchpoint support. */
-
-static int
-x86_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_SW_BP:
- case Z_PACKET_HW_BP:
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
- return 0;
- }
-}
-
-static int
-x86_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- struct process_info *proc = current_process ();
-
- switch (type)
- {
- case raw_bkpt_type_hw:
- case raw_bkpt_type_write_wp:
- case raw_bkpt_type_access_wp:
- {
- enum target_hw_bp_type hw_type
- = raw_bkpt_type_to_target_hw_bp_type (type);
- struct x86_debug_reg_state *state
- = &proc->priv->arch_private->debug_reg_state;
-
- return x86_dr_insert_watchpoint (state, hw_type, addr, size);
- }
-
- default:
- /* Unsupported. */
- return 1;
- }
-}
-
-static int
-x86_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- struct process_info *proc = current_process ();
-
- switch (type)
- {
- case raw_bkpt_type_hw:
- case raw_bkpt_type_write_wp:
- case raw_bkpt_type_access_wp:
- {
- enum target_hw_bp_type hw_type
- = raw_bkpt_type_to_target_hw_bp_type (type);
- struct x86_debug_reg_state *state
- = &proc->priv->arch_private->debug_reg_state;
-
- return x86_dr_remove_watchpoint (state, hw_type, addr, size);
- }
- default:
- /* Unsupported. */
- return 1;
- }
-}
-
-static int
-x86_stopped_by_watchpoint (void)
-{
- struct process_info *proc = current_process ();
- return x86_dr_stopped_by_watchpoint (&proc->priv->arch_private->debug_reg_state);
-}
-
-static CORE_ADDR
-x86_stopped_data_address (void)
-{
- struct process_info *proc = current_process ();
- CORE_ADDR addr;
- if (x86_dr_stopped_data_address (&proc->priv->arch_private->debug_reg_state,
- &addr))
- return addr;
- return 0;
-}
-\f
-/* Called when a new process is created. */
-
-static struct arch_process_info *
-x86_linux_new_process (void)
-{
- struct arch_process_info *info = XCNEW (struct arch_process_info);
-
- x86_low_init_dregs (&info->debug_reg_state);
-
- return info;
-}
-
-/* Called when a process is being deleted. */
-
-static void
-x86_linux_delete_process (struct arch_process_info *info)
-{
- xfree (info);
-}
-
-/* Target routine for linux_new_fork. */
-
-static void
-x86_linux_new_fork (struct process_info *parent, struct process_info *child)
-{
- /* These are allocated by linux_add_process. */
- gdb_assert (parent->priv != NULL
- && parent->priv->arch_private != NULL);
- gdb_assert (child->priv != NULL
- && child->priv->arch_private != NULL);
-
- /* Linux kernel before 2.6.33 commit
- 72f674d203cd230426437cdcf7dd6f681dad8b0d
- will inherit hardware debug registers from parent
- on fork/vfork/clone. Newer Linux kernels create such tasks with
- zeroed debug registers.
-
- GDB core assumes the child inherits the watchpoints/hw
- breakpoints of the parent, and will remove them all from the
- forked off process. Copy the debug registers mirrors into the
- new process so that all breakpoints and watchpoints can be
- removed together. The debug registers mirror will become zeroed
- in the end before detaching the forked off process, thus making
- this compatible with older Linux kernels too. */
-
- *child->priv->arch_private = *parent->priv->arch_private;
-}
-
-/* See nat/x86-dregs.h. */
-
-struct x86_debug_reg_state *
-x86_debug_reg_state (pid_t pid)
-{
- struct process_info *proc = find_process_pid (pid);
-
- return &proc->priv->arch_private->debug_reg_state;
-}
-\f
-/* When GDBSERVER is built as a 64-bit application on linux, the
- PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since
- debugging a 32-bit inferior with a 64-bit GDBSERVER should look the same
- as debugging it with a 32-bit GDBSERVER, we do the 32-bit <-> 64-bit
- conversion in-place ourselves. */
-
-/* Convert a ptrace/host siginfo object, into/from the siginfo in the
- layout of the inferiors' architecture. Returns true if any
- conversion was done; false otherwise. If DIRECTION is 1, then copy
- from INF to PTRACE. If DIRECTION is 0, copy from PTRACE to
- INF. */
-
-static int
-x86_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
-{
-#ifdef __x86_64__
- unsigned int machine;
- int tid = lwpid_of (current_thread);
- int is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
-
- /* Is the inferior 32-bit? If so, then fixup the siginfo object. */
- if (!is_64bit_tdesc ())
- return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
- FIXUP_32);
- /* No fixup for native x32 GDB. */
- else if (!is_elf64 && sizeof (void *) == 8)
- return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
- FIXUP_X32);
-#endif
-
- return 0;
-}
-\f
-static int use_xml;
-
-/* Format of XSAVE extended state is:
- struct
- {
- fxsave_bytes[0..463]
- sw_usable_bytes[464..511]
- xstate_hdr_bytes[512..575]
- avx_bytes[576..831]
- future_state etc
- };
-
- Same memory layout will be used for the coredump NT_X86_XSTATE
- representing the XSAVE extended state registers.
-
- The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
- extended state mask, which is the same as the extended control register
- 0 (the XFEATURE_ENABLED_MASK register), XCR0. We can use this mask
- together with the mask saved in the xstate_hdr_bytes to determine what
- states the processor/OS supports and what state, used or initialized,
- the process/thread is in. */
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
-/* Does the current host support the GETFPXREGS request? The header
- file may or may not define it, and even if it is defined, the
- kernel will return EIO if it's running on a pre-SSE processor. */
-int have_ptrace_getfpxregs =
-#ifdef HAVE_PTRACE_GETFPXREGS
- -1
-#else
- 0
-#endif
-;
-
-/* Get Linux/x86 target description from running target. */
-
-static const struct target_desc *
-x86_linux_read_description (void)
-{
- unsigned int machine;
- int is_elf64;
- int xcr0_features;
- int tid;
- static uint64_t xcr0;
- struct regset_info *regset;
-
- tid = lwpid_of (current_thread);
-
- is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
-
- if (sizeof (void *) == 4)
- {
- if (is_elf64 > 0)
- error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-#ifndef __x86_64__
- else if (machine == EM_X86_64)
- error (_("Can't debug x86-64 process with 32-bit GDBserver"));
-#endif
- }
-
-#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
- if (machine == EM_386 && have_ptrace_getfpxregs == -1)
- {
- elf_fpxregset_t fpxregs;
-
- if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
- {
- have_ptrace_getfpxregs = 0;
- have_ptrace_getregset = 0;
- return i386_linux_read_description (X86_XSTATE_X87);
- }
- else
- have_ptrace_getfpxregs = 1;
- }
-#endif
-
- if (!use_xml)
- {
- x86_xcr0 = X86_XSTATE_SSE_MASK;
-
- /* Don't use XML. */
-#ifdef __x86_64__
- if (machine == EM_X86_64)
- return tdesc_amd64_linux_no_xml;
- else
-#endif
- return tdesc_i386_linux_no_xml;
- }
-
- if (have_ptrace_getregset == -1)
- {
- uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
- struct iovec iov;
-
- iov.iov_base = xstateregs;
- iov.iov_len = sizeof (xstateregs);
-
- /* Check if PTRACE_GETREGSET works. */
- if (ptrace (PTRACE_GETREGSET, tid,
- (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
- have_ptrace_getregset = 0;
- else
- {
- have_ptrace_getregset = 1;
-
- /* Get XCR0 from XSAVE extended state. */
- xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
- / sizeof (uint64_t))];
-
- /* Use PTRACE_GETREGSET if it is available. */
- for (regset = x86_regsets;
- regset->fill_function != NULL; regset++)
- if (regset->get_request == PTRACE_GETREGSET)
- regset->size = X86_XSTATE_SIZE (xcr0);
- else if (regset->type != GENERAL_REGS)
- regset->size = 0;
- }
- }
-
- /* Check the native XCR0 only if PTRACE_GETREGSET is available. */
- xcr0_features = (have_ptrace_getregset
- && (xcr0 & X86_XSTATE_ALL_MASK));
-
- if (xcr0_features)
- x86_xcr0 = xcr0;
-
- if (machine == EM_X86_64)
- {
-#ifdef __x86_64__
- const target_desc *tdesc = NULL;
-
- if (xcr0_features)
- {
- tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
- !is_elf64);
- }
-
- if (tdesc == NULL)
- tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64);
- return tdesc;
-#endif
- }
- else
- {
- const target_desc *tdesc = NULL;
-
- if (xcr0_features)
- tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK);
-
- if (tdesc == NULL)
- tdesc = i386_linux_read_description (X86_XSTATE_SSE);
-
- return tdesc;
- }
-
- gdb_assert_not_reached ("failed to return tdesc");
-}
-
-/* Update all the target description of all processes; a new GDB
- connected, and it may or not support xml target descriptions. */
-
-static void
-x86_linux_update_xmltarget (void)
-{
- struct thread_info *saved_thread = current_thread;
-
- /* Before changing the register cache's internal layout, flush the
- contents of the current valid caches back to the threads, and
- release the current regcache objects. */
- regcache_release ();
-
- for_each_process ([] (process_info *proc) {
- int pid = proc->pid;
-
- /* Look up any thread of this process. */
- current_thread = find_any_thread_of_pid (pid);
-
- the_low_target.arch_setup ();
- });
-
- current_thread = saved_thread;
-}
-
-/* Process qSupported query, "xmlRegisters=". Update the buffer size for
- PTRACE_GETREGSET. */
-
-static void
-x86_linux_process_qsupported (char **features, int count)
-{
- int i;
-
- /* Return if gdb doesn't support XML. If gdb sends "xmlRegisters="
- with "i386" in qSupported query, it supports x86 XML target
- descriptions. */
- use_xml = 0;
- for (i = 0; i < count; i++)
- {
- const char *feature = features[i];
-
- if (startswith (feature, "xmlRegisters="))
- {
- char *copy = xstrdup (feature + 13);
-
- char *saveptr;
- for (char *p = strtok_r (copy, ",", &saveptr);
- p != NULL;
- p = strtok_r (NULL, ",", &saveptr))
- {
- if (strcmp (p, "i386") == 0)
- {
- use_xml = 1;
- break;
- }
- }
-
- free (copy);
- }
- }
- x86_linux_update_xmltarget ();
-}
-
-/* Common for x86/x86-64. */
-
-static struct regsets_info x86_regsets_info =
- {
- x86_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-#ifdef __x86_64__
-static struct regs_info amd64_linux_regs_info =
- {
- NULL, /* regset_bitmap */
- NULL, /* usrregs_info */
- &x86_regsets_info
- };
-#endif
-static struct usrregs_info i386_linux_usrregs_info =
- {
- I386_NUM_REGS,
- i386_regmap,
- };
-
-static struct regs_info i386_linux_regs_info =
- {
- NULL, /* regset_bitmap */
- &i386_linux_usrregs_info,
- &x86_regsets_info
- };
-
-static const struct regs_info *
-x86_linux_regs_info (void)
-{
-#ifdef __x86_64__
- if (is_64bit_tdesc ())
- return &amd64_linux_regs_info;
- else
-#endif
- return &i386_linux_regs_info;
-}
-
-/* Initialize the target description for the architecture of the
- inferior. */
-
-static void
-x86_arch_setup (void)
-{
- current_process ()->tdesc = x86_linux_read_description ();
-}
-
-/* Fill *SYSNO and *SYSRET with the syscall nr trapped and the syscall return
- code. This should only be called if LWP got a SYSCALL_SIGTRAP. */
-
-static void
-x86_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
-{
- int use_64bit = register_size (regcache->tdesc, 0) == 8;
-
- if (use_64bit)
- {
- long l_sysno;
-
- collect_register_by_name (regcache, "orig_rax", &l_sysno);
- *sysno = (int) l_sysno;
- }
- else
- collect_register_by_name (regcache, "orig_eax", sysno);
-}
-
-static int
-x86_supports_tracepoints (void)
-{
- return 1;
-}
-
-static void
-append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf)
-{
- target_write_memory (*to, buf, len);
- *to += len;
-}
-
-static int
-push_opcode (unsigned char *buf, const char *op)
-{
- unsigned char *buf_org = buf;
-
- while (1)
- {
- char *endptr;
- unsigned long ul = strtoul (op, &endptr, 16);
-
- if (endptr == op)
- break;
-
- *buf++ = ul;
- op = endptr;
- }
-
- return buf - buf_org;
-}
-
-#ifdef __x86_64__
-
-/* Build a jump pad that saves registers and calls a collection
- function. Writes a jump instruction to the jump pad to
- JJUMPAD_INSN. The caller is responsible to write it in at the
- tracepoint address. */
-
-static int
-amd64_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)
-{
- unsigned char buf[40];
- int i, offset;
- int64_t loffset;
-
- CORE_ADDR buildaddr = *jump_entry;
-
- /* Build the jump pad. */
-
- /* First, do tracepoint data collection. Save registers. */
- i = 0;
- /* Need to ensure stack pointer saved first. */
- buf[i++] = 0x54; /* push %rsp */
- buf[i++] = 0x55; /* push %rbp */
- buf[i++] = 0x57; /* push %rdi */
- buf[i++] = 0x56; /* push %rsi */
- buf[i++] = 0x52; /* push %rdx */
- buf[i++] = 0x51; /* push %rcx */
- buf[i++] = 0x53; /* push %rbx */
- buf[i++] = 0x50; /* push %rax */
- buf[i++] = 0x41; buf[i++] = 0x57; /* push %r15 */
- buf[i++] = 0x41; buf[i++] = 0x56; /* push %r14 */
- buf[i++] = 0x41; buf[i++] = 0x55; /* push %r13 */
- buf[i++] = 0x41; buf[i++] = 0x54; /* push %r12 */
- buf[i++] = 0x41; buf[i++] = 0x53; /* push %r11 */
- buf[i++] = 0x41; buf[i++] = 0x52; /* push %r10 */
- buf[i++] = 0x41; buf[i++] = 0x51; /* push %r9 */
- buf[i++] = 0x41; buf[i++] = 0x50; /* push %r8 */
- buf[i++] = 0x9c; /* pushfq */
- buf[i++] = 0x48; /* movabs <addr>,%rdi */
- buf[i++] = 0xbf;
- memcpy (buf + i, &tpaddr, 8);
- i += 8;
- buf[i++] = 0x57; /* push %rdi */
- append_insns (&buildaddr, i, buf);
-
- /* Stack space for the collecting_t object. */
- i = 0;
- i += push_opcode (&buf[i], "48 83 ec 18"); /* sub $0x18,%rsp */
- i += push_opcode (&buf[i], "48 b8"); /* mov <tpoint>,%rax */
- memcpy (buf + i, &tpoint, 8);
- i += 8;
- i += push_opcode (&buf[i], "48 89 04 24"); /* mov %rax,(%rsp) */
- i += push_opcode (&buf[i],
- "64 48 8b 04 25 00 00 00 00"); /* mov %fs:0x0,%rax */
- i += push_opcode (&buf[i], "48 89 44 24 08"); /* mov %rax,0x8(%rsp) */
- append_insns (&buildaddr, i, buf);
-
- /* spin-lock. */
- i = 0;
- i += push_opcode (&buf[i], "48 be"); /* movl <lockaddr>,%rsi */
- memcpy (&buf[i], (void *) &lockaddr, 8);
- i += 8;
- i += push_opcode (&buf[i], "48 89 e1"); /* mov %rsp,%rcx */
- i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
- i += push_opcode (&buf[i], "f0 48 0f b1 0e"); /* lock cmpxchg %rcx,(%rsi) */
- i += push_opcode (&buf[i], "48 85 c0"); /* test %rax,%rax */
- i += push_opcode (&buf[i], "75 f4"); /* jne <again> */
- append_insns (&buildaddr, i, buf);
-
- /* Set up the gdb_collect call. */
- /* At this point, (stack pointer + 0x18) is the base of our saved
- register block. */
-
- i = 0;
- i += push_opcode (&buf[i], "48 89 e6"); /* mov %rsp,%rsi */
- i += push_opcode (&buf[i], "48 83 c6 18"); /* add $0x18,%rsi */
-
- /* tpoint address may be 64-bit wide. */
- i += push_opcode (&buf[i], "48 bf"); /* movl <addr>,%rdi */
- memcpy (buf + i, &tpoint, 8);
- i += 8;
- append_insns (&buildaddr, i, buf);
-
- /* The collector function being in the shared library, may be
- >31-bits away off the jump pad. */
- i = 0;
- i += push_opcode (&buf[i], "48 b8"); /* mov $collector,%rax */
- memcpy (buf + i, &collector, 8);
- i += 8;
- i += push_opcode (&buf[i], "ff d0"); /* callq *%rax */
- append_insns (&buildaddr, i, buf);
-
- /* Clear the spin-lock. */
- i = 0;
- i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
- i += push_opcode (&buf[i], "48 a3"); /* mov %rax, lockaddr */
- memcpy (buf + i, &lockaddr, 8);
- i += 8;
- append_insns (&buildaddr, i, buf);
-
- /* Remove stack that had been used for the collect_t object. */
- i = 0;
- i += push_opcode (&buf[i], "48 83 c4 18"); /* add $0x18,%rsp */
- append_insns (&buildaddr, i, buf);
-
- /* Restore register state. */
- i = 0;
- buf[i++] = 0x48; /* add $0x8,%rsp */
- buf[i++] = 0x83;
- buf[i++] = 0xc4;
- buf[i++] = 0x08;
- buf[i++] = 0x9d; /* popfq */
- buf[i++] = 0x41; buf[i++] = 0x58; /* pop %r8 */
- buf[i++] = 0x41; buf[i++] = 0x59; /* pop %r9 */
- buf[i++] = 0x41; buf[i++] = 0x5a; /* pop %r10 */
- buf[i++] = 0x41; buf[i++] = 0x5b; /* pop %r11 */
- buf[i++] = 0x41; buf[i++] = 0x5c; /* pop %r12 */
- buf[i++] = 0x41; buf[i++] = 0x5d; /* pop %r13 */
- buf[i++] = 0x41; buf[i++] = 0x5e; /* pop %r14 */
- buf[i++] = 0x41; buf[i++] = 0x5f; /* pop %r15 */
- buf[i++] = 0x58; /* pop %rax */
- buf[i++] = 0x5b; /* pop %rbx */
- buf[i++] = 0x59; /* pop %rcx */
- buf[i++] = 0x5a; /* pop %rdx */
- buf[i++] = 0x5e; /* pop %rsi */
- buf[i++] = 0x5f; /* pop %rdi */
- buf[i++] = 0x5d; /* pop %rbp */
- buf[i++] = 0x5c; /* pop %rsp */
- append_insns (&buildaddr, i, buf);
-
- /* Now, adjust the original instruction to execute in the jump
- pad. */
- *adjusted_insn_addr = buildaddr;
- relocate_instruction (&buildaddr, tpaddr);
- *adjusted_insn_addr_end = buildaddr;
-
- /* Finally, write a jump back to the program. */
-
- loffset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn));
- if (loffset > INT_MAX || loffset < INT_MIN)
- {
- sprintf (err,
- "E.Jump back from jump pad too far from tracepoint "
- "(offset 0x%" PRIx64 " > int32).", loffset);
- return 1;
- }
-
- offset = (int) loffset;
- memcpy (buf, jump_insn, sizeof (jump_insn));
- memcpy (buf + 1, &offset, 4);
- append_insns (&buildaddr, sizeof (jump_insn), buf);
-
- /* 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. */
- loffset = *jump_entry - (tpaddr + sizeof (jump_insn));
- if (loffset > INT_MAX || loffset < INT_MIN)
- {
- sprintf (err,
- "E.Jump pad too far from tracepoint "
- "(offset 0x%" PRIx64 " > int32).", loffset);
- return 1;
- }
-
- offset = (int) loffset;
-
- memcpy (buf, jump_insn, sizeof (jump_insn));
- memcpy (buf + 1, &offset, 4);
- memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
- *jjump_pad_insn_size = sizeof (jump_insn);
-
- /* Return the end address of our pad. */
- *jump_entry = buildaddr;
-
- return 0;
-}
-
-#endif /* __x86_64__ */
-
-/* Build a jump pad that saves registers and calls a collection
- function. Writes a jump instruction to the jump pad to
- JJUMPAD_INSN. The caller is responsible to write it in at the
- tracepoint address. */
-
-static int
-i386_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)
-{
- unsigned char buf[0x100];
- int i, offset;
- CORE_ADDR buildaddr = *jump_entry;
-
- /* Build the jump pad. */
-
- /* First, do tracepoint data collection. Save registers. */
- i = 0;
- buf[i++] = 0x60; /* pushad */
- buf[i++] = 0x68; /* push tpaddr aka $pc */
- *((int *)(buf + i)) = (int) tpaddr;
- i += 4;
- buf[i++] = 0x9c; /* pushf */
- buf[i++] = 0x1e; /* push %ds */
- buf[i++] = 0x06; /* push %es */
- buf[i++] = 0x0f; /* push %fs */
- buf[i++] = 0xa0;
- buf[i++] = 0x0f; /* push %gs */
- buf[i++] = 0xa8;
- buf[i++] = 0x16; /* push %ss */
- buf[i++] = 0x0e; /* push %cs */
- append_insns (&buildaddr, i, buf);
-
- /* Stack space for the collecting_t object. */
- i = 0;
- i += push_opcode (&buf[i], "83 ec 08"); /* sub $0x8,%esp */
-
- /* Build the object. */
- i += push_opcode (&buf[i], "b8"); /* mov <tpoint>,%eax */
- memcpy (buf + i, &tpoint, 4);
- i += 4;
- i += push_opcode (&buf[i], "89 04 24"); /* mov %eax,(%esp) */
-
- i += push_opcode (&buf[i], "65 a1 00 00 00 00"); /* mov %gs:0x0,%eax */
- i += push_opcode (&buf[i], "89 44 24 04"); /* mov %eax,0x4(%esp) */
- append_insns (&buildaddr, i, buf);
-
- /* spin-lock. Note this is using cmpxchg, which leaves i386 behind.
- If we cared for it, this could be using xchg alternatively. */
-
- i = 0;
- i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
- i += push_opcode (&buf[i], "f0 0f b1 25"); /* lock cmpxchg
- %esp,<lockaddr> */
- memcpy (&buf[i], (void *) &lockaddr, 4);
- i += 4;
- i += push_opcode (&buf[i], "85 c0"); /* test %eax,%eax */
- i += push_opcode (&buf[i], "75 f2"); /* jne <again> */
- append_insns (&buildaddr, i, buf);
-
-
- /* Set up arguments to the gdb_collect call. */
- i = 0;
- i += push_opcode (&buf[i], "89 e0"); /* mov %esp,%eax */
- i += push_opcode (&buf[i], "83 c0 08"); /* add $0x08,%eax */
- i += push_opcode (&buf[i], "89 44 24 fc"); /* mov %eax,-0x4(%esp) */
- append_insns (&buildaddr, i, buf);
-
- i = 0;
- i += push_opcode (&buf[i], "83 ec 08"); /* sub $0x8,%esp */
- append_insns (&buildaddr, i, buf);
-
- i = 0;
- i += push_opcode (&buf[i], "c7 04 24"); /* movl <addr>,(%esp) */
- memcpy (&buf[i], (void *) &tpoint, 4);
- i += 4;
- append_insns (&buildaddr, i, buf);
-
- buf[0] = 0xe8; /* call <reladdr> */
- offset = collector - (buildaddr + sizeof (jump_insn));
- memcpy (buf + 1, &offset, 4);
- append_insns (&buildaddr, 5, buf);
- /* Clean up after the call. */
- buf[0] = 0x83; /* add $0x8,%esp */
- buf[1] = 0xc4;
- buf[2] = 0x08;
- append_insns (&buildaddr, 3, buf);
-
-
- /* Clear the spin-lock. This would need the LOCK prefix on older
- broken archs. */
- i = 0;
- i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
- i += push_opcode (&buf[i], "a3"); /* mov %eax, lockaddr */
- memcpy (buf + i, &lockaddr, 4);
- i += 4;
- append_insns (&buildaddr, i, buf);
-
-
- /* Remove stack that had been used for the collect_t object. */
- i = 0;
- i += push_opcode (&buf[i], "83 c4 08"); /* add $0x08,%esp */
- append_insns (&buildaddr, i, buf);
-
- i = 0;
- buf[i++] = 0x83; /* add $0x4,%esp (no pop of %cs, assume unchanged) */
- buf[i++] = 0xc4;
- buf[i++] = 0x04;
- buf[i++] = 0x17; /* pop %ss */
- buf[i++] = 0x0f; /* pop %gs */
- buf[i++] = 0xa9;
- buf[i++] = 0x0f; /* pop %fs */
- buf[i++] = 0xa1;
- buf[i++] = 0x07; /* pop %es */
- buf[i++] = 0x1f; /* pop %ds */
- buf[i++] = 0x9d; /* popf */
- buf[i++] = 0x83; /* add $0x4,%esp (pop of tpaddr aka $pc) */
- buf[i++] = 0xc4;
- buf[i++] = 0x04;
- buf[i++] = 0x61; /* popad */
- append_insns (&buildaddr, i, buf);
-
- /* Now, adjust the original instruction to execute in the jump
- pad. */
- *adjusted_insn_addr = buildaddr;
- relocate_instruction (&buildaddr, tpaddr);
- *adjusted_insn_addr_end = buildaddr;
-
- /* Write the jump back to the program. */
- offset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn));
- memcpy (buf, jump_insn, sizeof (jump_insn));
- memcpy (buf + 1, &offset, 4);
- append_insns (&buildaddr, sizeof (jump_insn), buf);
-
- /* 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. */
- if (orig_size == 4)
- {
- /* Create a trampoline. */
- *trampoline_size = sizeof (jump_insn);
- if (!claim_trampoline_space (*trampoline_size, trampoline))
- {
- /* No trampoline space available. */
- strcpy (err,
- "E.Cannot allocate trampoline space needed for fast "
- "tracepoints on 4-byte instructions.");
- return 1;
- }
-
- offset = *jump_entry - (*trampoline + sizeof (jump_insn));
- memcpy (buf, jump_insn, sizeof (jump_insn));
- memcpy (buf + 1, &offset, 4);
- target_write_memory (*trampoline, buf, sizeof (jump_insn));
-
- /* Use a 16-bit relative jump instruction to jump to the trampoline. */
- offset = (*trampoline - (tpaddr + sizeof (small_jump_insn))) & 0xffff;
- memcpy (buf, small_jump_insn, sizeof (small_jump_insn));
- memcpy (buf + 2, &offset, 2);
- memcpy (jjump_pad_insn, buf, sizeof (small_jump_insn));
- *jjump_pad_insn_size = sizeof (small_jump_insn);
- }
- else
- {
- /* Else use a 32-bit relative jump instruction. */
- offset = *jump_entry - (tpaddr + sizeof (jump_insn));
- memcpy (buf, jump_insn, sizeof (jump_insn));
- memcpy (buf + 1, &offset, 4);
- memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
- *jjump_pad_insn_size = sizeof (jump_insn);
- }
-
- /* Return the end address of our pad. */
- *jump_entry = buildaddr;
-
- return 0;
-}
-
-static int
-x86_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)
-{
-#ifdef __x86_64__
- if (is_64bit_tdesc ())
- return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
- collector, lockaddr,
- orig_size, jump_entry,
- trampoline, trampoline_size,
- jjump_pad_insn,
- jjump_pad_insn_size,
- adjusted_insn_addr,
- adjusted_insn_addr_end,
- err);
-#endif
-
- return i386_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
- collector, lockaddr,
- orig_size, jump_entry,
- trampoline, trampoline_size,
- jjump_pad_insn,
- jjump_pad_insn_size,
- adjusted_insn_addr,
- adjusted_insn_addr_end,
- err);
-}
-
-/* Return the minimum instruction length for fast tracepoints on x86/x86-64
- architectures. */
-
-static int
-x86_get_min_fast_tracepoint_insn_len (void)
-{
- static int warned_about_fast_tracepoints = 0;
-
-#ifdef __x86_64__
- /* On x86-64, 5-byte jump instructions with a 4-byte offset are always
- used for fast tracepoints. */
- if (is_64bit_tdesc ())
- return 5;
-#endif
-
- if (agent_loaded_p ())
- {
- char errbuf[IPA_BUFSIZ];
-
- errbuf[0] = '\0';
-
- /* On x86, if trampolines are available, then 4-byte jump instructions
- with a 2-byte offset may be used, otherwise 5-byte jump instructions
- with a 4-byte offset are used instead. */
- if (have_fast_tracepoint_trampoline_buffer (errbuf))
- return 4;
- else
- {
- /* GDB has no channel to explain to user why a shorter fast
- tracepoint is not possible, but at least make GDBserver
- mention that something has gone awry. */
- if (!warned_about_fast_tracepoints)
- {
- warning ("4-byte fast tracepoints not available; %s", errbuf);
- warned_about_fast_tracepoints = 1;
- }
- return 5;
- }
- }
- else
- {
- /* Indicate that the minimum length is currently unknown since the IPA
- has not loaded yet. */
- return 0;
- }
-}
-
-static void
-add_insns (unsigned char *start, int len)
-{
- CORE_ADDR buildaddr = current_insn_ptr;
-
- if (debug_threads)
- debug_printf ("Adding %d bytes of insn at %s\n",
- len, paddress (buildaddr));
-
- append_insns (&buildaddr, len, start);
- current_insn_ptr = buildaddr;
-}
-
-/* Our general strategy for emitting code is to avoid specifying raw
- bytes whenever possible, and instead copy a block of inline asm
- that is embedded in the function. This is a little messy, because
- we need to keep the compiler from discarding what looks like dead
- code, plus suppress various warnings. */
-
-#define EMIT_ASM(NAME, INSNS) \
- do \
- { \
- extern unsigned char start_ ## NAME, end_ ## NAME; \
- add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \
- __asm__ ("jmp end_" #NAME "\n" \
- "\t" "start_" #NAME ":" \
- "\t" INSNS "\n" \
- "\t" "end_" #NAME ":"); \
- } while (0)
-
-#ifdef __x86_64__
-
-#define EMIT_ASM32(NAME,INSNS) \
- do \
- { \
- extern unsigned char start_ ## NAME, end_ ## NAME; \
- add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \
- __asm__ (".code32\n" \
- "\t" "jmp end_" #NAME "\n" \
- "\t" "start_" #NAME ":\n" \
- "\t" INSNS "\n" \
- "\t" "end_" #NAME ":\n" \
- ".code64\n"); \
- } while (0)
-
-#else
-
-#define EMIT_ASM32(NAME,INSNS) EMIT_ASM(NAME,INSNS)
-
-#endif
-
-#ifdef __x86_64__
-
-static void
-amd64_emit_prologue (void)
-{
- EMIT_ASM (amd64_prologue,
- "pushq %rbp\n\t"
- "movq %rsp,%rbp\n\t"
- "sub $0x20,%rsp\n\t"
- "movq %rdi,-8(%rbp)\n\t"
- "movq %rsi,-16(%rbp)");
-}
-
-
-static void
-amd64_emit_epilogue (void)
-{
- EMIT_ASM (amd64_epilogue,
- "movq -16(%rbp),%rdi\n\t"
- "movq %rax,(%rdi)\n\t"
- "xor %rax,%rax\n\t"
- "leave\n\t"
- "ret");
-}
-
-static void
-amd64_emit_add (void)
-{
- EMIT_ASM (amd64_add,
- "add (%rsp),%rax\n\t"
- "lea 0x8(%rsp),%rsp");
-}
-
-static void
-amd64_emit_sub (void)
-{
- EMIT_ASM (amd64_sub,
- "sub %rax,(%rsp)\n\t"
- "pop %rax");
-}
-
-static void
-amd64_emit_mul (void)
-{
- emit_error = 1;
-}
-
-static void
-amd64_emit_lsh (void)
-{
- emit_error = 1;
-}
-
-static void
-amd64_emit_rsh_signed (void)
-{
- emit_error = 1;
-}
-
-static void
-amd64_emit_rsh_unsigned (void)
-{
- emit_error = 1;
-}
-
-static void
-amd64_emit_ext (int arg)
-{
- switch (arg)
- {
- case 8:
- EMIT_ASM (amd64_ext_8,
- "cbtw\n\t"
- "cwtl\n\t"
- "cltq");
- break;
- case 16:
- EMIT_ASM (amd64_ext_16,
- "cwtl\n\t"
- "cltq");
- break;
- case 32:
- EMIT_ASM (amd64_ext_32,
- "cltq");
- break;
- default:
- emit_error = 1;
- }
-}
-
-static void
-amd64_emit_log_not (void)
-{
- EMIT_ASM (amd64_log_not,
- "test %rax,%rax\n\t"
- "sete %cl\n\t"
- "movzbq %cl,%rax");
-}
-
-static void
-amd64_emit_bit_and (void)
-{
- EMIT_ASM (amd64_and,
- "and (%rsp),%rax\n\t"
- "lea 0x8(%rsp),%rsp");
-}
-
-static void
-amd64_emit_bit_or (void)
-{
- EMIT_ASM (amd64_or,
- "or (%rsp),%rax\n\t"
- "lea 0x8(%rsp),%rsp");
-}
-
-static void
-amd64_emit_bit_xor (void)
-{
- EMIT_ASM (amd64_xor,
- "xor (%rsp),%rax\n\t"
- "lea 0x8(%rsp),%rsp");
-}
-
-static void
-amd64_emit_bit_not (void)
-{
- EMIT_ASM (amd64_bit_not,
- "xorq $0xffffffffffffffff,%rax");
-}
-
-static void
-amd64_emit_equal (void)
-{
- EMIT_ASM (amd64_equal,
- "cmp %rax,(%rsp)\n\t"
- "je .Lamd64_equal_true\n\t"
- "xor %rax,%rax\n\t"
- "jmp .Lamd64_equal_end\n\t"
- ".Lamd64_equal_true:\n\t"
- "mov $0x1,%rax\n\t"
- ".Lamd64_equal_end:\n\t"
- "lea 0x8(%rsp),%rsp");
-}
-
-static void
-amd64_emit_less_signed (void)
-{
- EMIT_ASM (amd64_less_signed,
- "cmp %rax,(%rsp)\n\t"
- "jl .Lamd64_less_signed_true\n\t"
- "xor %rax,%rax\n\t"
- "jmp .Lamd64_less_signed_end\n\t"
- ".Lamd64_less_signed_true:\n\t"
- "mov $1,%rax\n\t"
- ".Lamd64_less_signed_end:\n\t"
- "lea 0x8(%rsp),%rsp");
-}
-
-static void
-amd64_emit_less_unsigned (void)
-{
- EMIT_ASM (amd64_less_unsigned,
- "cmp %rax,(%rsp)\n\t"
- "jb .Lamd64_less_unsigned_true\n\t"
- "xor %rax,%rax\n\t"
- "jmp .Lamd64_less_unsigned_end\n\t"
- ".Lamd64_less_unsigned_true:\n\t"
- "mov $1,%rax\n\t"
- ".Lamd64_less_unsigned_end:\n\t"
- "lea 0x8(%rsp),%rsp");
-}
-
-static void
-amd64_emit_ref (int size)
-{
- switch (size)
- {
- case 1:
- EMIT_ASM (amd64_ref1,
- "movb (%rax),%al");
- break;
- case 2:
- EMIT_ASM (amd64_ref2,
- "movw (%rax),%ax");
- break;
- case 4:
- EMIT_ASM (amd64_ref4,
- "movl (%rax),%eax");
- break;
- case 8:
- EMIT_ASM (amd64_ref8,
- "movq (%rax),%rax");
- break;
- }
-}
-
-static void
-amd64_emit_if_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_if_goto,
- "mov %rax,%rcx\n\t"
- "pop %rax\n\t"
- "cmp $0,%rcx\n\t"
- ".byte 0x0f, 0x85, 0x0, 0x0, 0x0, 0x0");
- if (offset_p)
- *offset_p = 10;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-amd64_emit_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_goto,
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0");
- if (offset_p)
- *offset_p = 1;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-amd64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
-{
- int diff = (to - (from + size));
- unsigned char buf[sizeof (int)];
-
- if (size != 4)
- {
- emit_error = 1;
- return;
- }
-
- memcpy (buf, &diff, sizeof (int));
- target_write_memory (from, buf, sizeof (int));
-}
-
-static void
-amd64_emit_const (LONGEST num)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr = current_insn_ptr;
-
- i = 0;
- buf[i++] = 0x48; buf[i++] = 0xb8; /* mov $<n>,%rax */
- memcpy (&buf[i], &num, sizeof (num));
- i += 8;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
-}
-
-static void
-amd64_emit_call (CORE_ADDR fn)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr;
- LONGEST offset64;
-
- /* The destination function being in the shared library, may be
- >31-bits away off the compiled code pad. */
-
- buildaddr = current_insn_ptr;
-
- offset64 = fn - (buildaddr + 1 /* call op */ + 4 /* 32-bit offset */);
-
- i = 0;
-
- if (offset64 > INT_MAX || offset64 < INT_MIN)
- {
- /* Offset is too large for a call. Use callq, but that requires
- a register, so avoid it if possible. Use r10, since it is
- call-clobbered, we don't have to push/pop it. */
- buf[i++] = 0x48; /* mov $fn,%r10 */
- buf[i++] = 0xba;
- memcpy (buf + i, &fn, 8);
- i += 8;
- buf[i++] = 0xff; /* callq *%r10 */
- buf[i++] = 0xd2;
- }
- else
- {
- int offset32 = offset64; /* we know we can't overflow here. */
-
- buf[i++] = 0xe8; /* call <reladdr> */
- memcpy (buf + i, &offset32, 4);
- i += 4;
- }
-
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
-}
-
-static void
-amd64_emit_reg (int reg)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr;
-
- /* Assume raw_regs is still in %rdi. */
- buildaddr = current_insn_ptr;
- i = 0;
- buf[i++] = 0xbe; /* mov $<n>,%esi */
- memcpy (&buf[i], ®, sizeof (reg));
- i += 4;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
- amd64_emit_call (get_raw_reg_func_addr ());
-}
-
-static void
-amd64_emit_pop (void)
-{
- EMIT_ASM (amd64_pop,
- "pop %rax");
-}
-
-static void
-amd64_emit_stack_flush (void)
-{
- EMIT_ASM (amd64_stack_flush,
- "push %rax");
-}
-
-static void
-amd64_emit_zero_ext (int arg)
-{
- switch (arg)
- {
- case 8:
- EMIT_ASM (amd64_zero_ext_8,
- "and $0xff,%rax");
- break;
- case 16:
- EMIT_ASM (amd64_zero_ext_16,
- "and $0xffff,%rax");
- break;
- case 32:
- EMIT_ASM (amd64_zero_ext_32,
- "mov $0xffffffff,%rcx\n\t"
- "and %rcx,%rax");
- break;
- default:
- emit_error = 1;
- }
-}
-
-static void
-amd64_emit_swap (void)
-{
- EMIT_ASM (amd64_swap,
- "mov %rax,%rcx\n\t"
- "pop %rax\n\t"
- "push %rcx");
-}
-
-static void
-amd64_emit_stack_adjust (int n)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr = current_insn_ptr;
-
- i = 0;
- buf[i++] = 0x48; /* lea $<n>(%rsp),%rsp */
- buf[i++] = 0x8d;
- buf[i++] = 0x64;
- buf[i++] = 0x24;
- /* This only handles adjustments up to 16, but we don't expect any more. */
- buf[i++] = n * 8;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
-}
-
-/* FN's prototype is `LONGEST(*fn)(int)'. */
-
-static void
-amd64_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr;
-
- buildaddr = current_insn_ptr;
- i = 0;
- buf[i++] = 0xbf; /* movl $<n>,%edi */
- memcpy (&buf[i], &arg1, sizeof (arg1));
- i += 4;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
- amd64_emit_call (fn);
-}
-
-/* FN's prototype is `void(*fn)(int,LONGEST)'. */
-
-static void
-amd64_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr;
-
- buildaddr = current_insn_ptr;
- i = 0;
- buf[i++] = 0xbf; /* movl $<n>,%edi */
- memcpy (&buf[i], &arg1, sizeof (arg1));
- i += 4;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
- EMIT_ASM (amd64_void_call_2_a,
- /* Save away a copy of the stack top. */
- "push %rax\n\t"
- /* Also pass top as the second argument. */
- "mov %rax,%rsi");
- amd64_emit_call (fn);
- EMIT_ASM (amd64_void_call_2_b,
- /* Restore the stack top, %rax may have been trashed. */
- "pop %rax");
-}
-
-static void
-amd64_emit_eq_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_eq,
- "cmp %rax,(%rsp)\n\t"
- "jne .Lamd64_eq_fallthru\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lamd64_eq_fallthru:\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax");
-
- if (offset_p)
- *offset_p = 13;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-amd64_emit_ne_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_ne,
- "cmp %rax,(%rsp)\n\t"
- "je .Lamd64_ne_fallthru\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lamd64_ne_fallthru:\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax");
-
- if (offset_p)
- *offset_p = 13;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-amd64_emit_lt_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_lt,
- "cmp %rax,(%rsp)\n\t"
- "jnl .Lamd64_lt_fallthru\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lamd64_lt_fallthru:\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax");
-
- if (offset_p)
- *offset_p = 13;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-amd64_emit_le_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_le,
- "cmp %rax,(%rsp)\n\t"
- "jnle .Lamd64_le_fallthru\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lamd64_le_fallthru:\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax");
-
- if (offset_p)
- *offset_p = 13;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-amd64_emit_gt_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_gt,
- "cmp %rax,(%rsp)\n\t"
- "jng .Lamd64_gt_fallthru\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lamd64_gt_fallthru:\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax");
-
- if (offset_p)
- *offset_p = 13;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-amd64_emit_ge_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM (amd64_ge,
- "cmp %rax,(%rsp)\n\t"
- "jnge .Lamd64_ge_fallthru\n\t"
- ".Lamd64_ge_jump:\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lamd64_ge_fallthru:\n\t"
- "lea 0x8(%rsp),%rsp\n\t"
- "pop %rax");
-
- if (offset_p)
- *offset_p = 13;
- if (size_p)
- *size_p = 4;
-}
-
-struct emit_ops amd64_emit_ops =
- {
- amd64_emit_prologue,
- amd64_emit_epilogue,
- amd64_emit_add,
- amd64_emit_sub,
- amd64_emit_mul,
- amd64_emit_lsh,
- amd64_emit_rsh_signed,
- amd64_emit_rsh_unsigned,
- amd64_emit_ext,
- amd64_emit_log_not,
- amd64_emit_bit_and,
- amd64_emit_bit_or,
- amd64_emit_bit_xor,
- amd64_emit_bit_not,
- amd64_emit_equal,
- amd64_emit_less_signed,
- amd64_emit_less_unsigned,
- amd64_emit_ref,
- amd64_emit_if_goto,
- amd64_emit_goto,
- amd64_write_goto_address,
- amd64_emit_const,
- amd64_emit_call,
- amd64_emit_reg,
- amd64_emit_pop,
- amd64_emit_stack_flush,
- amd64_emit_zero_ext,
- amd64_emit_swap,
- amd64_emit_stack_adjust,
- amd64_emit_int_call_1,
- amd64_emit_void_call_2,
- amd64_emit_eq_goto,
- amd64_emit_ne_goto,
- amd64_emit_lt_goto,
- amd64_emit_le_goto,
- amd64_emit_gt_goto,
- amd64_emit_ge_goto
- };
-
-#endif /* __x86_64__ */
-
-static void
-i386_emit_prologue (void)
-{
- EMIT_ASM32 (i386_prologue,
- "push %ebp\n\t"
- "mov %esp,%ebp\n\t"
- "push %ebx");
- /* At this point, the raw regs base address is at 8(%ebp), and the
- value pointer is at 12(%ebp). */
-}
-
-static void
-i386_emit_epilogue (void)
-{
- EMIT_ASM32 (i386_epilogue,
- "mov 12(%ebp),%ecx\n\t"
- "mov %eax,(%ecx)\n\t"
- "mov %ebx,0x4(%ecx)\n\t"
- "xor %eax,%eax\n\t"
- "pop %ebx\n\t"
- "pop %ebp\n\t"
- "ret");
-}
-
-static void
-i386_emit_add (void)
-{
- EMIT_ASM32 (i386_add,
- "add (%esp),%eax\n\t"
- "adc 0x4(%esp),%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_sub (void)
-{
- EMIT_ASM32 (i386_sub,
- "subl %eax,(%esp)\n\t"
- "sbbl %ebx,4(%esp)\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t");
-}
-
-static void
-i386_emit_mul (void)
-{
- emit_error = 1;
-}
-
-static void
-i386_emit_lsh (void)
-{
- emit_error = 1;
-}
-
-static void
-i386_emit_rsh_signed (void)
-{
- emit_error = 1;
-}
-
-static void
-i386_emit_rsh_unsigned (void)
-{
- emit_error = 1;
-}
-
-static void
-i386_emit_ext (int arg)
-{
- switch (arg)
- {
- case 8:
- EMIT_ASM32 (i386_ext_8,
- "cbtw\n\t"
- "cwtl\n\t"
- "movl %eax,%ebx\n\t"
- "sarl $31,%ebx");
- break;
- case 16:
- EMIT_ASM32 (i386_ext_16,
- "cwtl\n\t"
- "movl %eax,%ebx\n\t"
- "sarl $31,%ebx");
- break;
- case 32:
- EMIT_ASM32 (i386_ext_32,
- "movl %eax,%ebx\n\t"
- "sarl $31,%ebx");
- break;
- default:
- emit_error = 1;
- }
-}
-
-static void
-i386_emit_log_not (void)
-{
- EMIT_ASM32 (i386_log_not,
- "or %ebx,%eax\n\t"
- "test %eax,%eax\n\t"
- "sete %cl\n\t"
- "xor %ebx,%ebx\n\t"
- "movzbl %cl,%eax");
-}
-
-static void
-i386_emit_bit_and (void)
-{
- EMIT_ASM32 (i386_and,
- "and (%esp),%eax\n\t"
- "and 0x4(%esp),%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_bit_or (void)
-{
- EMIT_ASM32 (i386_or,
- "or (%esp),%eax\n\t"
- "or 0x4(%esp),%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_bit_xor (void)
-{
- EMIT_ASM32 (i386_xor,
- "xor (%esp),%eax\n\t"
- "xor 0x4(%esp),%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_bit_not (void)
-{
- EMIT_ASM32 (i386_bit_not,
- "xor $0xffffffff,%eax\n\t"
- "xor $0xffffffff,%ebx\n\t");
-}
-
-static void
-i386_emit_equal (void)
-{
- EMIT_ASM32 (i386_equal,
- "cmpl %ebx,4(%esp)\n\t"
- "jne .Li386_equal_false\n\t"
- "cmpl %eax,(%esp)\n\t"
- "je .Li386_equal_true\n\t"
- ".Li386_equal_false:\n\t"
- "xor %eax,%eax\n\t"
- "jmp .Li386_equal_end\n\t"
- ".Li386_equal_true:\n\t"
- "mov $1,%eax\n\t"
- ".Li386_equal_end:\n\t"
- "xor %ebx,%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_less_signed (void)
-{
- EMIT_ASM32 (i386_less_signed,
- "cmpl %ebx,4(%esp)\n\t"
- "jl .Li386_less_signed_true\n\t"
- "jne .Li386_less_signed_false\n\t"
- "cmpl %eax,(%esp)\n\t"
- "jl .Li386_less_signed_true\n\t"
- ".Li386_less_signed_false:\n\t"
- "xor %eax,%eax\n\t"
- "jmp .Li386_less_signed_end\n\t"
- ".Li386_less_signed_true:\n\t"
- "mov $1,%eax\n\t"
- ".Li386_less_signed_end:\n\t"
- "xor %ebx,%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_less_unsigned (void)
-{
- EMIT_ASM32 (i386_less_unsigned,
- "cmpl %ebx,4(%esp)\n\t"
- "jb .Li386_less_unsigned_true\n\t"
- "jne .Li386_less_unsigned_false\n\t"
- "cmpl %eax,(%esp)\n\t"
- "jb .Li386_less_unsigned_true\n\t"
- ".Li386_less_unsigned_false:\n\t"
- "xor %eax,%eax\n\t"
- "jmp .Li386_less_unsigned_end\n\t"
- ".Li386_less_unsigned_true:\n\t"
- "mov $1,%eax\n\t"
- ".Li386_less_unsigned_end:\n\t"
- "xor %ebx,%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_ref (int size)
-{
- switch (size)
- {
- case 1:
- EMIT_ASM32 (i386_ref1,
- "movb (%eax),%al");
- break;
- case 2:
- EMIT_ASM32 (i386_ref2,
- "movw (%eax),%ax");
- break;
- case 4:
- EMIT_ASM32 (i386_ref4,
- "movl (%eax),%eax");
- break;
- case 8:
- EMIT_ASM32 (i386_ref8,
- "movl 4(%eax),%ebx\n\t"
- "movl (%eax),%eax");
- break;
- }
-}
-
-static void
-i386_emit_if_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (i386_if_goto,
- "mov %eax,%ecx\n\t"
- "or %ebx,%ecx\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- "cmpl $0,%ecx\n\t"
- /* Don't trust the assembler to choose the right jump */
- ".byte 0x0f, 0x85, 0x0, 0x0, 0x0, 0x0");
-
- if (offset_p)
- *offset_p = 11; /* be sure that this matches the sequence above */
- if (size_p)
- *size_p = 4;
-}
-
-static void
-i386_emit_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (i386_goto,
- /* Don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0");
- if (offset_p)
- *offset_p = 1;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-i386_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
-{
- int diff = (to - (from + size));
- unsigned char buf[sizeof (int)];
-
- /* We're only doing 4-byte sizes at the moment. */
- if (size != 4)
- {
- emit_error = 1;
- return;
- }
-
- memcpy (buf, &diff, sizeof (int));
- target_write_memory (from, buf, sizeof (int));
-}
-
-static void
-i386_emit_const (LONGEST num)
-{
- unsigned char buf[16];
- int i, hi, lo;
- CORE_ADDR buildaddr = current_insn_ptr;
-
- i = 0;
- buf[i++] = 0xb8; /* mov $<n>,%eax */
- lo = num & 0xffffffff;
- memcpy (&buf[i], &lo, sizeof (lo));
- i += 4;
- hi = ((num >> 32) & 0xffffffff);
- if (hi)
- {
- buf[i++] = 0xbb; /* mov $<n>,%ebx */
- memcpy (&buf[i], &hi, sizeof (hi));
- i += 4;
- }
- else
- {
- buf[i++] = 0x31; buf[i++] = 0xdb; /* xor %ebx,%ebx */
- }
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
-}
-
-static void
-i386_emit_call (CORE_ADDR fn)
-{
- unsigned char buf[16];
- int i, offset;
- CORE_ADDR buildaddr;
-
- buildaddr = current_insn_ptr;
- i = 0;
- buf[i++] = 0xe8; /* call <reladdr> */
- offset = ((int) fn) - (buildaddr + 5);
- memcpy (buf + 1, &offset, 4);
- append_insns (&buildaddr, 5, buf);
- current_insn_ptr = buildaddr;
-}
-
-static void
-i386_emit_reg (int reg)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr;
-
- EMIT_ASM32 (i386_reg_a,
- "sub $0x8,%esp");
- buildaddr = current_insn_ptr;
- i = 0;
- buf[i++] = 0xb8; /* mov $<n>,%eax */
- memcpy (&buf[i], ®, sizeof (reg));
- i += 4;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
- EMIT_ASM32 (i386_reg_b,
- "mov %eax,4(%esp)\n\t"
- "mov 8(%ebp),%eax\n\t"
- "mov %eax,(%esp)");
- i386_emit_call (get_raw_reg_func_addr ());
- EMIT_ASM32 (i386_reg_c,
- "xor %ebx,%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-static void
-i386_emit_pop (void)
-{
- EMIT_ASM32 (i386_pop,
- "pop %eax\n\t"
- "pop %ebx");
-}
-
-static void
-i386_emit_stack_flush (void)
-{
- EMIT_ASM32 (i386_stack_flush,
- "push %ebx\n\t"
- "push %eax");
-}
-
-static void
-i386_emit_zero_ext (int arg)
-{
- switch (arg)
- {
- case 8:
- EMIT_ASM32 (i386_zero_ext_8,
- "and $0xff,%eax\n\t"
- "xor %ebx,%ebx");
- break;
- case 16:
- EMIT_ASM32 (i386_zero_ext_16,
- "and $0xffff,%eax\n\t"
- "xor %ebx,%ebx");
- break;
- case 32:
- EMIT_ASM32 (i386_zero_ext_32,
- "xor %ebx,%ebx");
- break;
- default:
- emit_error = 1;
- }
-}
-
-static void
-i386_emit_swap (void)
-{
- EMIT_ASM32 (i386_swap,
- "mov %eax,%ecx\n\t"
- "mov %ebx,%edx\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- "push %edx\n\t"
- "push %ecx");
-}
-
-static void
-i386_emit_stack_adjust (int n)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr = current_insn_ptr;
-
- i = 0;
- buf[i++] = 0x8d; /* lea $<n>(%esp),%esp */
- buf[i++] = 0x64;
- buf[i++] = 0x24;
- buf[i++] = n * 8;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
-}
-
-/* FN's prototype is `LONGEST(*fn)(int)'. */
-
-static void
-i386_emit_int_call_1 (CORE_ADDR fn, int arg1)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr;
-
- EMIT_ASM32 (i386_int_call_1_a,
- /* Reserve a bit of stack space. */
- "sub $0x8,%esp");
- /* Put the one argument on the stack. */
- buildaddr = current_insn_ptr;
- i = 0;
- buf[i++] = 0xc7; /* movl $<arg1>,(%esp) */
- buf[i++] = 0x04;
- buf[i++] = 0x24;
- memcpy (&buf[i], &arg1, sizeof (arg1));
- i += 4;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
- i386_emit_call (fn);
- EMIT_ASM32 (i386_int_call_1_c,
- "mov %edx,%ebx\n\t"
- "lea 0x8(%esp),%esp");
-}
-
-/* FN's prototype is `void(*fn)(int,LONGEST)'. */
-
-static void
-i386_emit_void_call_2 (CORE_ADDR fn, int arg1)
-{
- unsigned char buf[16];
- int i;
- CORE_ADDR buildaddr;
-
- EMIT_ASM32 (i386_void_call_2_a,
- /* Preserve %eax only; we don't have to worry about %ebx. */
- "push %eax\n\t"
- /* Reserve a bit of stack space for arguments. */
- "sub $0x10,%esp\n\t"
- /* Copy "top" to the second argument position. (Note that
- we can't assume function won't scribble on its
- arguments, so don't try to restore from this.) */
- "mov %eax,4(%esp)\n\t"
- "mov %ebx,8(%esp)");
- /* Put the first argument on the stack. */
- buildaddr = current_insn_ptr;
- i = 0;
- buf[i++] = 0xc7; /* movl $<arg1>,(%esp) */
- buf[i++] = 0x04;
- buf[i++] = 0x24;
- memcpy (&buf[i], &arg1, sizeof (arg1));
- i += 4;
- append_insns (&buildaddr, i, buf);
- current_insn_ptr = buildaddr;
- i386_emit_call (fn);
- EMIT_ASM32 (i386_void_call_2_b,
- "lea 0x10(%esp),%esp\n\t"
- /* Restore original stack top. */
- "pop %eax");
-}
-
-
-static void
-i386_emit_eq_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (eq,
- /* Check low half first, more likely to be decider */
- "cmpl %eax,(%esp)\n\t"
- "jne .Leq_fallthru\n\t"
- "cmpl %ebx,4(%esp)\n\t"
- "jne .Leq_fallthru\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Leq_fallthru:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx");
-
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-i386_emit_ne_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (ne,
- /* Check low half first, more likely to be decider */
- "cmpl %eax,(%esp)\n\t"
- "jne .Lne_jump\n\t"
- "cmpl %ebx,4(%esp)\n\t"
- "je .Lne_fallthru\n\t"
- ".Lne_jump:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lne_fallthru:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx");
-
- if (offset_p)
- *offset_p = 18;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-i386_emit_lt_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (lt,
- "cmpl %ebx,4(%esp)\n\t"
- "jl .Llt_jump\n\t"
- "jne .Llt_fallthru\n\t"
- "cmpl %eax,(%esp)\n\t"
- "jnl .Llt_fallthru\n\t"
- ".Llt_jump:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Llt_fallthru:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx");
-
- if (offset_p)
- *offset_p = 20;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-i386_emit_le_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (le,
- "cmpl %ebx,4(%esp)\n\t"
- "jle .Lle_jump\n\t"
- "jne .Lle_fallthru\n\t"
- "cmpl %eax,(%esp)\n\t"
- "jnle .Lle_fallthru\n\t"
- ".Lle_jump:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lle_fallthru:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx");
-
- if (offset_p)
- *offset_p = 20;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-i386_emit_gt_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (gt,
- "cmpl %ebx,4(%esp)\n\t"
- "jg .Lgt_jump\n\t"
- "jne .Lgt_fallthru\n\t"
- "cmpl %eax,(%esp)\n\t"
- "jng .Lgt_fallthru\n\t"
- ".Lgt_jump:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lgt_fallthru:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx");
-
- if (offset_p)
- *offset_p = 20;
- if (size_p)
- *size_p = 4;
-}
-
-static void
-i386_emit_ge_goto (int *offset_p, int *size_p)
-{
- EMIT_ASM32 (ge,
- "cmpl %ebx,4(%esp)\n\t"
- "jge .Lge_jump\n\t"
- "jne .Lge_fallthru\n\t"
- "cmpl %eax,(%esp)\n\t"
- "jnge .Lge_fallthru\n\t"
- ".Lge_jump:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx\n\t"
- /* jmp, but don't trust the assembler to choose the right jump */
- ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
- ".Lge_fallthru:\n\t"
- "lea 0x8(%esp),%esp\n\t"
- "pop %eax\n\t"
- "pop %ebx");
-
- if (offset_p)
- *offset_p = 20;
- if (size_p)
- *size_p = 4;
-}
-
-struct emit_ops i386_emit_ops =
- {
- i386_emit_prologue,
- i386_emit_epilogue,
- i386_emit_add,
- i386_emit_sub,
- i386_emit_mul,
- i386_emit_lsh,
- i386_emit_rsh_signed,
- i386_emit_rsh_unsigned,
- i386_emit_ext,
- i386_emit_log_not,
- i386_emit_bit_and,
- i386_emit_bit_or,
- i386_emit_bit_xor,
- i386_emit_bit_not,
- i386_emit_equal,
- i386_emit_less_signed,
- i386_emit_less_unsigned,
- i386_emit_ref,
- i386_emit_if_goto,
- i386_emit_goto,
- i386_write_goto_address,
- i386_emit_const,
- i386_emit_call,
- i386_emit_reg,
- i386_emit_pop,
- i386_emit_stack_flush,
- i386_emit_zero_ext,
- i386_emit_swap,
- i386_emit_stack_adjust,
- i386_emit_int_call_1,
- i386_emit_void_call_2,
- i386_emit_eq_goto,
- i386_emit_ne_goto,
- i386_emit_lt_goto,
- i386_emit_le_goto,
- i386_emit_gt_goto,
- i386_emit_ge_goto
- };
-
-
-static struct emit_ops *
-x86_emit_ops (void)
-{
-#ifdef __x86_64__
- if (is_64bit_tdesc ())
- return &amd64_emit_ops;
- else
-#endif
- return &i386_emit_ops;
-}
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-x86_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = x86_breakpoint_len;
- return x86_breakpoint;
-}
-
-static int
-x86_supports_range_stepping (void)
-{
- return 1;
-}
-
-/* Implementation of linux_target_ops method "supports_hardware_single_step".
- */
-
-static int
-x86_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static int
-x86_get_ipa_tdesc_idx (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 0);
- const struct target_desc *tdesc = regcache->tdesc;
-
-#ifdef __x86_64__
- return amd64_get_ipa_tdesc_idx (tdesc);
-#endif
-
- if (tdesc == tdesc_i386_linux_no_xml)
- return X86_TDESC_SSE;
-
- return i386_get_ipa_tdesc_idx (tdesc);
-}
-
-/* This is initialized assuming an amd64 target.
- x86_arch_setup will correct it for i386 or amd64 targets. */
-
-struct linux_target_ops the_low_target =
-{
- x86_arch_setup,
- x86_linux_regs_info,
- x86_cannot_fetch_register,
- x86_cannot_store_register,
- NULL, /* fetch_register */
- x86_get_pc,
- x86_set_pc,
- NULL, /* breakpoint_kind_from_pc */
- x86_sw_breakpoint_from_kind,
- NULL,
- 1,
- x86_breakpoint_at,
- x86_supports_z_point_type,
- x86_insert_point,
- x86_remove_point,
- x86_stopped_by_watchpoint,
- x86_stopped_data_address,
- /* collect_ptrace_register/supply_ptrace_register are not needed in the
- native i386 case (no registers smaller than an xfer unit), and are not
- used in the biarch case (HAVE_LINUX_USRREGS is not defined). */
- NULL,
- NULL,
- /* need to fix up i386 siginfo if host is amd64 */
- x86_siginfo_fixup,
- x86_linux_new_process,
- x86_linux_delete_process,
- x86_linux_new_thread,
- x86_linux_delete_thread,
- x86_linux_new_fork,
- x86_linux_prepare_to_resume,
- x86_linux_process_qsupported,
- x86_supports_tracepoints,
- x86_get_thread_area,
- x86_install_fast_tracepoint_jump_pad,
- x86_emit_ops,
- x86_get_min_fast_tracepoint_insn_len,
- x86_supports_range_stepping,
- NULL, /* breakpoint_kind_from_current_state */
- x86_supports_hardware_single_step,
- x86_get_syscall_trapinfo,
- x86_get_ipa_tdesc_idx,
-};
-
-void
-initialize_low_arch (void)
-{
- /* Initialize the Linux target descriptions. */
-#ifdef __x86_64__
- tdesc_amd64_linux_no_xml = allocate_target_description ();
- copy_target_description (tdesc_amd64_linux_no_xml,
- amd64_linux_read_description (X86_XSTATE_SSE_MASK,
- false));
- tdesc_amd64_linux_no_xml->xmltarget = xmltarget_amd64_linux_no_xml;
-#endif
-
- tdesc_i386_linux_no_xml = allocate_target_description ();
- copy_target_description (tdesc_i386_linux_no_xml,
- i386_linux_read_description (X86_XSTATE_SSE_MASK));
- tdesc_i386_linux_no_xml->xmltarget = xmltarget_i386_linux_no_xml;
-
- initialize_regsets_info (&x86_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/x86-64 specific low level interface, for the remote server
+ for GDB.
+ Copyright (C) 2002-2020 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 <signal.h>
+#include <limits.h>
+#include <inttypes.h>
+#include "linux-low.h"
+#include "i387-fp.h"
+#include "x86-low.h"
+#include "gdbsupport/x86-xstate.h"
+#include "nat/gdb_ptrace.h"
+
+#ifdef __x86_64__
+#include "nat/amd64-linux-siginfo.h"
+#endif
+
+#include "gdb_proc_service.h"
+/* Don't include elf/common.h if linux/elf.h got included by
+ gdb_proc_service.h. */
+#ifndef ELFMAG0
+#include "elf/common.h"
+#endif
+
+#include "gdbsupport/agent.h"
+#include "tdesc.h"
+#include "tracepoint.h"
+#include "ax.h"
+#include "nat/linux-nat.h"
+#include "nat/x86-linux.h"
+#include "nat/x86-linux-dregs.h"
+#include "linux-x86-tdesc.h"
+
+#ifdef __x86_64__
+static struct target_desc *tdesc_amd64_linux_no_xml;
+#endif
+static struct target_desc *tdesc_i386_linux_no_xml;
+
+
+static unsigned char jump_insn[] = { 0xe9, 0, 0, 0, 0 };
+static unsigned char small_jump_insn[] = { 0x66, 0xe9, 0, 0 };
+
+/* Backward compatibility for gdb without XML support. */
+
+static const char *xmltarget_i386_linux_no_xml = "@<target>\
+<architecture>i386</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";
+
+#ifdef __x86_64__
+static const char *xmltarget_amd64_linux_no_xml = "@<target>\
+<architecture>i386:x86-64</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";
+#endif
+
+#include <sys/reg.h>
+#include <sys/procfs.h>
+#include <sys/uio.h>
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+/* This definition comes from prctl.h, but some kernels may not have it. */
+#ifndef PTRACE_ARCH_PRCTL
+#define PTRACE_ARCH_PRCTL 30
+#endif
+
+/* The following definitions come from prctl.h, but may be absent
+ for certain configurations. */
+#ifndef ARCH_GET_FS
+#define ARCH_SET_GS 0x1001
+#define ARCH_SET_FS 0x1002
+#define ARCH_GET_FS 0x1003
+#define ARCH_GET_GS 0x1004
+#endif
+
+/* Per-process arch-specific data we want to keep. */
+
+struct arch_process_info
+{
+ struct x86_debug_reg_state debug_reg_state;
+};
+
+#ifdef __x86_64__
+
+/* Mapping between the general-purpose registers in `struct user'
+ format and GDB's register array layout.
+ Note that the transfer layout uses 64-bit regs. */
+static /*const*/ int i386_regmap[] =
+{
+ RAX * 8, RCX * 8, RDX * 8, RBX * 8,
+ RSP * 8, RBP * 8, RSI * 8, RDI * 8,
+ RIP * 8, EFLAGS * 8, CS * 8, SS * 8,
+ DS * 8, ES * 8, FS * 8, GS * 8
+};
+
+#define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0]))
+
+/* So code below doesn't have to care, i386 or amd64. */
+#define ORIG_EAX ORIG_RAX
+#define REGSIZE 8
+
+static const int x86_64_regmap[] =
+{
+ RAX * 8, RBX * 8, RCX * 8, RDX * 8,
+ RSI * 8, RDI * 8, RBP * 8, RSP * 8,
+ R8 * 8, R9 * 8, R10 * 8, R11 * 8,
+ R12 * 8, R13 * 8, R14 * 8, R15 * 8,
+ RIP * 8, EFLAGS * 8, CS * 8, SS * 8,
+ DS * 8, ES * 8, FS * 8, GS * 8,
+ -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, -1,
+ ORIG_RAX * 8,
+#ifdef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
+ 21 * 8, 22 * 8,
+#else
+ -1, -1,
+#endif
+ -1, -1, -1, -1, /* MPX registers BND0 ... BND3. */
+ -1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* xmm16 ... xmm31 (AVX512) */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* ymm16 ... ymm31 (AVX512) */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* k0 ... k7 (AVX512) */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm31 (AVX512) */
+ -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 /* pkru */
+};
+
+#define X86_64_NUM_REGS (sizeof (x86_64_regmap) / sizeof (x86_64_regmap[0]))
+#define X86_64_USER_REGS (GS + 1)
+
+#else /* ! __x86_64__ */
+
+/* Mapping between the general-purpose registers in `struct user'
+ format and GDB's register array layout. */
+static /*const*/ int i386_regmap[] =
+{
+ EAX * 4, ECX * 4, EDX * 4, EBX * 4,
+ UESP * 4, EBP * 4, ESI * 4, EDI * 4,
+ EIP * 4, EFL * 4, CS * 4, SS * 4,
+ DS * 4, ES * 4, FS * 4, GS * 4
+};
+
+#define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0]))
+
+#define REGSIZE 4
+
+#endif
+
+#ifdef __x86_64__
+
+/* Returns true if the current inferior belongs to a x86-64 process,
+ per the tdesc. */
+
+static int
+is_64bit_tdesc (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+
+ return register_size (regcache->tdesc, 0) == 8;
+}
+
+#endif
+
+\f
+/* Called by libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+#ifdef __x86_64__
+ int use_64bit = is_64bit_tdesc ();
+
+ if (use_64bit)
+ {
+ switch (idx)
+ {
+ case FS:
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0)
+ return PS_OK;
+ break;
+ case GS:
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
+ return PS_OK;
+ break;
+ default:
+ return PS_BADADDR;
+ }
+ return PS_ERR;
+ }
+#endif
+
+ {
+ unsigned int desc[4];
+
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid,
+ (void *) (intptr_t) idx, (unsigned long) &desc) < 0)
+ return PS_ERR;
+
+ /* Ensure we properly extend the value to 64-bits for x86_64. */
+ *base = (void *) (uintptr_t) desc[1];
+ return PS_OK;
+ }
+}
+
+/* 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
+x86_get_thread_area (int lwpid, CORE_ADDR *addr)
+{
+#ifdef __x86_64__
+ int use_64bit = is_64bit_tdesc ();
+
+ if (use_64bit)
+ {
+ void *base;
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_FS) == 0)
+ {
+ *addr = (CORE_ADDR) (uintptr_t) base;
+ return 0;
+ }
+
+ return -1;
+ }
+#endif
+
+ {
+ struct lwp_info *lwp = find_lwp_pid (ptid_t (lwpid));
+ struct thread_info *thr = get_lwp_thread (lwp);
+ struct regcache *regcache = get_thread_regcache (thr, 1);
+ unsigned int desc[4];
+ ULONGEST gs = 0;
+ const int reg_thread_area = 3; /* bits to scale down register value. */
+ int idx;
+
+ collect_register_by_name (regcache, "gs", &gs);
+
+ idx = gs >> reg_thread_area;
+
+ if (ptrace (PTRACE_GET_THREAD_AREA,
+ lwpid_of (thr),
+ (void *) (long) idx, (unsigned long) &desc) < 0)
+ return -1;
+
+ *addr = desc[1];
+ return 0;
+ }
+}
+
+
+\f
+static int
+x86_cannot_store_register (int regno)
+{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return 0;
+#endif
+
+ return regno >= I386_NUM_REGS;
+}
+
+static int
+x86_cannot_fetch_register (int regno)
+{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return 0;
+#endif
+
+ return regno >= I386_NUM_REGS;
+}
+
+static void
+x86_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+
+#ifdef __x86_64__
+ if (register_size (regcache->tdesc, 0) == 8)
+ {
+ for (i = 0; i < X86_64_NUM_REGS; i++)
+ if (x86_64_regmap[i] != -1)
+ collect_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
+
+#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
+ {
+ unsigned long base;
+ int lwpid = lwpid_of (current_thread);
+
+ collect_register_by_name (regcache, "fs_base", &base);
+ ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_FS);
+
+ collect_register_by_name (regcache, "gs_base", &base);
+ ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_GS);
+ }
+#endif
+
+ return;
+ }
+
+ /* 32-bit inferior registers need to be zero-extended.
+ Callers would read uninitialized memory otherwise. */
+ memset (buf, 0x00, X86_64_USER_REGS * 8);
+#endif
+
+ for (i = 0; i < I386_NUM_REGS; i++)
+ collect_register (regcache, i, ((char *) buf) + i386_regmap[i]);
+
+ collect_register_by_name (regcache, "orig_eax",
+ ((char *) buf) + ORIG_EAX * REGSIZE);
+
+#ifdef __x86_64__
+ /* Sign extend EAX value to avoid potential syscall restart
+ problems.
+
+ See amd64_linux_collect_native_gregset() in gdb/amd64-linux-nat.c
+ for a detailed explanation. */
+ if (register_size (regcache->tdesc, 0) == 4)
+ {
+ void *ptr = ((gdb_byte *) buf
+ + i386_regmap[find_regno (regcache->tdesc, "eax")]);
+
+ *(int64_t *) ptr = *(int32_t *) ptr;
+ }
+#endif
+}
+
+static void
+x86_store_gregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+
+#ifdef __x86_64__
+ if (register_size (regcache->tdesc, 0) == 8)
+ {
+ for (i = 0; i < X86_64_NUM_REGS; i++)
+ if (x86_64_regmap[i] != -1)
+ supply_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
+
+#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
+ {
+ unsigned long base;
+ int lwpid = lwpid_of (current_thread);
+
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_FS) == 0)
+ supply_register_by_name (regcache, "fs_base", &base);
+
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_GS) == 0)
+ supply_register_by_name (regcache, "gs_base", &base);
+ }
+#endif
+ return;
+ }
+#endif
+
+ for (i = 0; i < I386_NUM_REGS; i++)
+ supply_register (regcache, i, ((char *) buf) + i386_regmap[i]);
+
+ supply_register_by_name (regcache, "orig_eax",
+ ((char *) buf) + ORIG_EAX * REGSIZE);
+}
+
+static void
+x86_fill_fpregset (struct regcache *regcache, void *buf)
+{
+#ifdef __x86_64__
+ i387_cache_to_fxsave (regcache, buf);
+#else
+ i387_cache_to_fsave (regcache, buf);
+#endif
+}
+
+static void
+x86_store_fpregset (struct regcache *regcache, const void *buf)
+{
+#ifdef __x86_64__
+ i387_fxsave_to_cache (regcache, buf);
+#else
+ i387_fsave_to_cache (regcache, buf);
+#endif
+}
+
+#ifndef __x86_64__
+
+static void
+x86_fill_fpxregset (struct regcache *regcache, void *buf)
+{
+ i387_cache_to_fxsave (regcache, buf);
+}
+
+static void
+x86_store_fpxregset (struct regcache *regcache, const void *buf)
+{
+ i387_fxsave_to_cache (regcache, buf);
+}
+
+#endif
+
+static void
+x86_fill_xstateregset (struct regcache *regcache, void *buf)
+{
+ i387_cache_to_xsave (regcache, buf);
+}
+
+static void
+x86_store_xstateregset (struct regcache *regcache, const void *buf)
+{
+ i387_xsave_to_cache (regcache, buf);
+}
+
+/* ??? The non-biarch i386 case stores all the i387 regs twice.
+ Once in i387_.*fsave.* and once in i387_.*fxsave.*.
+ This is, presumably, to handle the case where PTRACE_[GS]ETFPXREGS
+ doesn't work. IWBN to avoid the duplication in the case where it
+ does work. Maybe the arch_setup routine could check whether it works
+ and update the supported regsets accordingly. */
+
+static struct regset_info x86_regsets[] =
+{
+#ifdef HAVE_PTRACE_GETREGS
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
+ GENERAL_REGS,
+ x86_fill_gregset, x86_store_gregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_X86_XSTATE, 0,
+ EXTENDED_REGS, x86_fill_xstateregset, x86_store_xstateregset },
+# ifndef __x86_64__
+# ifdef HAVE_PTRACE_GETFPXREGS
+ { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, 0, sizeof (elf_fpxregset_t),
+ EXTENDED_REGS,
+ x86_fill_fpxregset, x86_store_fpxregset },
+# endif
+# endif
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (elf_fpregset_t),
+ FP_REGS,
+ x86_fill_fpregset, x86_store_fpregset },
+#endif /* HAVE_PTRACE_GETREGS */
+ NULL_REGSET
+};
+
+static CORE_ADDR
+x86_get_pc (struct regcache *regcache)
+{
+ int use_64bit = register_size (regcache->tdesc, 0) == 8;
+
+ if (use_64bit)
+ {
+ uint64_t pc;
+
+ collect_register_by_name (regcache, "rip", &pc);
+ return (CORE_ADDR) pc;
+ }
+ else
+ {
+ uint32_t pc;
+
+ collect_register_by_name (regcache, "eip", &pc);
+ return (CORE_ADDR) pc;
+ }
+}
+
+static void
+x86_set_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ int use_64bit = register_size (regcache->tdesc, 0) == 8;
+
+ if (use_64bit)
+ {
+ uint64_t newpc = pc;
+
+ supply_register_by_name (regcache, "rip", &newpc);
+ }
+ else
+ {
+ uint32_t newpc = pc;
+
+ supply_register_by_name (regcache, "eip", &newpc);
+ }
+}
+\f
+static const gdb_byte x86_breakpoint[] = { 0xCC };
+#define x86_breakpoint_len 1
+
+static int
+x86_breakpoint_at (CORE_ADDR pc)
+{
+ unsigned char c;
+
+ (*the_target->read_memory) (pc, &c, 1);
+ if (c == 0xCC)
+ return 1;
+
+ return 0;
+}
+\f
+/* Low-level function vector. */
+struct x86_dr_low_type x86_dr_low =
+ {
+ x86_linux_dr_set_control,
+ x86_linux_dr_set_addr,
+ x86_linux_dr_get_addr,
+ x86_linux_dr_get_status,
+ x86_linux_dr_get_control,
+ sizeof (void *),
+ };
+\f
+/* Breakpoint/Watchpoint support. */
+
+static int
+x86_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ case Z_PACKET_HW_BP:
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+x86_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+
+ switch (type)
+ {
+ case raw_bkpt_type_hw:
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ {
+ enum target_hw_bp_type hw_type
+ = raw_bkpt_type_to_target_hw_bp_type (type);
+ struct x86_debug_reg_state *state
+ = &proc->priv->arch_private->debug_reg_state;
+
+ return x86_dr_insert_watchpoint (state, hw_type, addr, size);
+ }
+
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+static int
+x86_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+
+ switch (type)
+ {
+ case raw_bkpt_type_hw:
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ {
+ enum target_hw_bp_type hw_type
+ = raw_bkpt_type_to_target_hw_bp_type (type);
+ struct x86_debug_reg_state *state
+ = &proc->priv->arch_private->debug_reg_state;
+
+ return x86_dr_remove_watchpoint (state, hw_type, addr, size);
+ }
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+static int
+x86_stopped_by_watchpoint (void)
+{
+ struct process_info *proc = current_process ();
+ return x86_dr_stopped_by_watchpoint (&proc->priv->arch_private->debug_reg_state);
+}
+
+static CORE_ADDR
+x86_stopped_data_address (void)
+{
+ struct process_info *proc = current_process ();
+ CORE_ADDR addr;
+ if (x86_dr_stopped_data_address (&proc->priv->arch_private->debug_reg_state,
+ &addr))
+ return addr;
+ return 0;
+}
+\f
+/* Called when a new process is created. */
+
+static struct arch_process_info *
+x86_linux_new_process (void)
+{
+ struct arch_process_info *info = XCNEW (struct arch_process_info);
+
+ x86_low_init_dregs (&info->debug_reg_state);
+
+ return info;
+}
+
+/* Called when a process is being deleted. */
+
+static void
+x86_linux_delete_process (struct arch_process_info *info)
+{
+ xfree (info);
+}
+
+/* Target routine for linux_new_fork. */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->priv != NULL
+ && parent->priv->arch_private != NULL);
+ gdb_assert (child->priv != NULL
+ && child->priv->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->priv->arch_private = *parent->priv->arch_private;
+}
+
+/* See nat/x86-dregs.h. */
+
+struct x86_debug_reg_state *
+x86_debug_reg_state (pid_t pid)
+{
+ struct process_info *proc = find_process_pid (pid);
+
+ return &proc->priv->arch_private->debug_reg_state;
+}
+\f
+/* When GDBSERVER is built as a 64-bit application on linux, the
+ PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since
+ debugging a 32-bit inferior with a 64-bit GDBSERVER should look the same
+ as debugging it with a 32-bit GDBSERVER, we do the 32-bit <-> 64-bit
+ conversion in-place ourselves. */
+
+/* Convert a ptrace/host siginfo object, into/from the siginfo in the
+ layout of the inferiors' architecture. Returns true if any
+ conversion was done; false otherwise. If DIRECTION is 1, then copy
+ from INF to PTRACE. If DIRECTION is 0, copy from PTRACE to
+ INF. */
+
+static int
+x86_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
+{
+#ifdef __x86_64__
+ unsigned int machine;
+ int tid = lwpid_of (current_thread);
+ int is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+
+ /* Is the inferior 32-bit? If so, then fixup the siginfo object. */
+ if (!is_64bit_tdesc ())
+ return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
+ FIXUP_32);
+ /* No fixup for native x32 GDB. */
+ else if (!is_elf64 && sizeof (void *) == 8)
+ return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
+ FIXUP_X32);
+#endif
+
+ return 0;
+}
+\f
+static int use_xml;
+
+/* Format of XSAVE extended state is:
+ struct
+ {
+ fxsave_bytes[0..463]
+ sw_usable_bytes[464..511]
+ xstate_hdr_bytes[512..575]
+ avx_bytes[576..831]
+ future_state etc
+ };
+
+ Same memory layout will be used for the coredump NT_X86_XSTATE
+ representing the XSAVE extended state registers.
+
+ The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
+ extended state mask, which is the same as the extended control register
+ 0 (the XFEATURE_ENABLED_MASK register), XCR0. We can use this mask
+ together with the mask saved in the xstate_hdr_bytes to determine what
+ states the processor/OS supports and what state, used or initialized,
+ the process/thread is in. */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464
+
+/* Does the current host support the GETFPXREGS request? The header
+ file may or may not define it, and even if it is defined, the
+ kernel will return EIO if it's running on a pre-SSE processor. */
+int have_ptrace_getfpxregs =
+#ifdef HAVE_PTRACE_GETFPXREGS
+ -1
+#else
+ 0
+#endif
+;
+
+/* Get Linux/x86 target description from running target. */
+
+static const struct target_desc *
+x86_linux_read_description (void)
+{
+ unsigned int machine;
+ int is_elf64;
+ int xcr0_features;
+ int tid;
+ static uint64_t xcr0;
+ struct regset_info *regset;
+
+ tid = lwpid_of (current_thread);
+
+ is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+
+ if (sizeof (void *) == 4)
+ {
+ if (is_elf64 > 0)
+ error (_("Can't debug 64-bit process with 32-bit GDBserver"));
+#ifndef __x86_64__
+ else if (machine == EM_X86_64)
+ error (_("Can't debug x86-64 process with 32-bit GDBserver"));
+#endif
+ }
+
+#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
+ if (machine == EM_386 && have_ptrace_getfpxregs == -1)
+ {
+ elf_fpxregset_t fpxregs;
+
+ if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
+ {
+ have_ptrace_getfpxregs = 0;
+ have_ptrace_getregset = 0;
+ return i386_linux_read_description (X86_XSTATE_X87);
+ }
+ else
+ have_ptrace_getfpxregs = 1;
+ }
+#endif
+
+ if (!use_xml)
+ {
+ x86_xcr0 = X86_XSTATE_SSE_MASK;
+
+ /* Don't use XML. */
+#ifdef __x86_64__
+ if (machine == EM_X86_64)
+ return tdesc_amd64_linux_no_xml;
+ else
+#endif
+ return tdesc_i386_linux_no_xml;
+ }
+
+ if (have_ptrace_getregset == -1)
+ {
+ uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+ struct iovec iov;
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = sizeof (xstateregs);
+
+ /* Check if PTRACE_GETREGSET works. */
+ if (ptrace (PTRACE_GETREGSET, tid,
+ (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+ have_ptrace_getregset = 0;
+ else
+ {
+ have_ptrace_getregset = 1;
+
+ /* Get XCR0 from XSAVE extended state. */
+ xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+ / sizeof (uint64_t))];
+
+ /* Use PTRACE_GETREGSET if it is available. */
+ for (regset = x86_regsets;
+ regset->fill_function != NULL; regset++)
+ if (regset->get_request == PTRACE_GETREGSET)
+ regset->size = X86_XSTATE_SIZE (xcr0);
+ else if (regset->type != GENERAL_REGS)
+ regset->size = 0;
+ }
+ }
+
+ /* Check the native XCR0 only if PTRACE_GETREGSET is available. */
+ xcr0_features = (have_ptrace_getregset
+ && (xcr0 & X86_XSTATE_ALL_MASK));
+
+ if (xcr0_features)
+ x86_xcr0 = xcr0;
+
+ if (machine == EM_X86_64)
+ {
+#ifdef __x86_64__
+ const target_desc *tdesc = NULL;
+
+ if (xcr0_features)
+ {
+ tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
+ !is_elf64);
+ }
+
+ if (tdesc == NULL)
+ tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64);
+ return tdesc;
+#endif
+ }
+ else
+ {
+ const target_desc *tdesc = NULL;
+
+ if (xcr0_features)
+ tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK);
+
+ if (tdesc == NULL)
+ tdesc = i386_linux_read_description (X86_XSTATE_SSE);
+
+ return tdesc;
+ }
+
+ gdb_assert_not_reached ("failed to return tdesc");
+}
+
+/* Update all the target description of all processes; a new GDB
+ connected, and it may or not support xml target descriptions. */
+
+static void
+x86_linux_update_xmltarget (void)
+{
+ struct thread_info *saved_thread = current_thread;
+
+ /* Before changing the register cache's internal layout, flush the
+ contents of the current valid caches back to the threads, and
+ release the current regcache objects. */
+ regcache_release ();
+
+ for_each_process ([] (process_info *proc) {
+ int pid = proc->pid;
+
+ /* Look up any thread of this process. */
+ current_thread = find_any_thread_of_pid (pid);
+
+ the_low_target.arch_setup ();
+ });
+
+ current_thread = saved_thread;
+}
+
+/* Process qSupported query, "xmlRegisters=". Update the buffer size for
+ PTRACE_GETREGSET. */
+
+static void
+x86_linux_process_qsupported (char **features, int count)
+{
+ int i;
+
+ /* Return if gdb doesn't support XML. If gdb sends "xmlRegisters="
+ with "i386" in qSupported query, it supports x86 XML target
+ descriptions. */
+ use_xml = 0;
+ for (i = 0; i < count; i++)
+ {
+ const char *feature = features[i];
+
+ if (startswith (feature, "xmlRegisters="))
+ {
+ char *copy = xstrdup (feature + 13);
+
+ char *saveptr;
+ for (char *p = strtok_r (copy, ",", &saveptr);
+ p != NULL;
+ p = strtok_r (NULL, ",", &saveptr))
+ {
+ if (strcmp (p, "i386") == 0)
+ {
+ use_xml = 1;
+ break;
+ }
+ }
+
+ free (copy);
+ }
+ }
+ x86_linux_update_xmltarget ();
+}
+
+/* Common for x86/x86-64. */
+
+static struct regsets_info x86_regsets_info =
+ {
+ x86_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+#ifdef __x86_64__
+static struct regs_info amd64_linux_regs_info =
+ {
+ NULL, /* regset_bitmap */
+ NULL, /* usrregs_info */
+ &x86_regsets_info
+ };
+#endif
+static struct usrregs_info i386_linux_usrregs_info =
+ {
+ I386_NUM_REGS,
+ i386_regmap,
+ };
+
+static struct regs_info i386_linux_regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &i386_linux_usrregs_info,
+ &x86_regsets_info
+ };
+
+static const struct regs_info *
+x86_linux_regs_info (void)
+{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return &amd64_linux_regs_info;
+ else
+#endif
+ return &i386_linux_regs_info;
+}
+
+/* Initialize the target description for the architecture of the
+ inferior. */
+
+static void
+x86_arch_setup (void)
+{
+ current_process ()->tdesc = x86_linux_read_description ();
+}
+
+/* Fill *SYSNO and *SYSRET with the syscall nr trapped and the syscall return
+ code. This should only be called if LWP got a SYSCALL_SIGTRAP. */
+
+static void
+x86_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
+{
+ int use_64bit = register_size (regcache->tdesc, 0) == 8;
+
+ if (use_64bit)
+ {
+ long l_sysno;
+
+ collect_register_by_name (regcache, "orig_rax", &l_sysno);
+ *sysno = (int) l_sysno;
+ }
+ else
+ collect_register_by_name (regcache, "orig_eax", sysno);
+}
+
+static int
+x86_supports_tracepoints (void)
+{
+ return 1;
+}
+
+static void
+append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf)
+{
+ target_write_memory (*to, buf, len);
+ *to += len;
+}
+
+static int
+push_opcode (unsigned char *buf, const char *op)
+{
+ unsigned char *buf_org = buf;
+
+ while (1)
+ {
+ char *endptr;
+ unsigned long ul = strtoul (op, &endptr, 16);
+
+ if (endptr == op)
+ break;
+
+ *buf++ = ul;
+ op = endptr;
+ }
+
+ return buf - buf_org;
+}
+
+#ifdef __x86_64__
+
+/* Build a jump pad that saves registers and calls a collection
+ function. Writes a jump instruction to the jump pad to
+ JJUMPAD_INSN. The caller is responsible to write it in at the
+ tracepoint address. */
+
+static int
+amd64_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)
+{
+ unsigned char buf[40];
+ int i, offset;
+ int64_t loffset;
+
+ CORE_ADDR buildaddr = *jump_entry;
+
+ /* Build the jump pad. */
+
+ /* First, do tracepoint data collection. Save registers. */
+ i = 0;
+ /* Need to ensure stack pointer saved first. */
+ buf[i++] = 0x54; /* push %rsp */
+ buf[i++] = 0x55; /* push %rbp */
+ buf[i++] = 0x57; /* push %rdi */
+ buf[i++] = 0x56; /* push %rsi */
+ buf[i++] = 0x52; /* push %rdx */
+ buf[i++] = 0x51; /* push %rcx */
+ buf[i++] = 0x53; /* push %rbx */
+ buf[i++] = 0x50; /* push %rax */
+ buf[i++] = 0x41; buf[i++] = 0x57; /* push %r15 */
+ buf[i++] = 0x41; buf[i++] = 0x56; /* push %r14 */
+ buf[i++] = 0x41; buf[i++] = 0x55; /* push %r13 */
+ buf[i++] = 0x41; buf[i++] = 0x54; /* push %r12 */
+ buf[i++] = 0x41; buf[i++] = 0x53; /* push %r11 */
+ buf[i++] = 0x41; buf[i++] = 0x52; /* push %r10 */
+ buf[i++] = 0x41; buf[i++] = 0x51; /* push %r9 */
+ buf[i++] = 0x41; buf[i++] = 0x50; /* push %r8 */
+ buf[i++] = 0x9c; /* pushfq */
+ buf[i++] = 0x48; /* movabs <addr>,%rdi */
+ buf[i++] = 0xbf;
+ memcpy (buf + i, &tpaddr, 8);
+ i += 8;
+ buf[i++] = 0x57; /* push %rdi */
+ append_insns (&buildaddr, i, buf);
+
+ /* Stack space for the collecting_t object. */
+ i = 0;
+ i += push_opcode (&buf[i], "48 83 ec 18"); /* sub $0x18,%rsp */
+ i += push_opcode (&buf[i], "48 b8"); /* mov <tpoint>,%rax */
+ memcpy (buf + i, &tpoint, 8);
+ i += 8;
+ i += push_opcode (&buf[i], "48 89 04 24"); /* mov %rax,(%rsp) */
+ i += push_opcode (&buf[i],
+ "64 48 8b 04 25 00 00 00 00"); /* mov %fs:0x0,%rax */
+ i += push_opcode (&buf[i], "48 89 44 24 08"); /* mov %rax,0x8(%rsp) */
+ append_insns (&buildaddr, i, buf);
+
+ /* spin-lock. */
+ i = 0;
+ i += push_opcode (&buf[i], "48 be"); /* movl <lockaddr>,%rsi */
+ memcpy (&buf[i], (void *) &lockaddr, 8);
+ i += 8;
+ i += push_opcode (&buf[i], "48 89 e1"); /* mov %rsp,%rcx */
+ i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
+ i += push_opcode (&buf[i], "f0 48 0f b1 0e"); /* lock cmpxchg %rcx,(%rsi) */
+ i += push_opcode (&buf[i], "48 85 c0"); /* test %rax,%rax */
+ i += push_opcode (&buf[i], "75 f4"); /* jne <again> */
+ append_insns (&buildaddr, i, buf);
+
+ /* Set up the gdb_collect call. */
+ /* At this point, (stack pointer + 0x18) is the base of our saved
+ register block. */
+
+ i = 0;
+ i += push_opcode (&buf[i], "48 89 e6"); /* mov %rsp,%rsi */
+ i += push_opcode (&buf[i], "48 83 c6 18"); /* add $0x18,%rsi */
+
+ /* tpoint address may be 64-bit wide. */
+ i += push_opcode (&buf[i], "48 bf"); /* movl <addr>,%rdi */
+ memcpy (buf + i, &tpoint, 8);
+ i += 8;
+ append_insns (&buildaddr, i, buf);
+
+ /* The collector function being in the shared library, may be
+ >31-bits away off the jump pad. */
+ i = 0;
+ i += push_opcode (&buf[i], "48 b8"); /* mov $collector,%rax */
+ memcpy (buf + i, &collector, 8);
+ i += 8;
+ i += push_opcode (&buf[i], "ff d0"); /* callq *%rax */
+ append_insns (&buildaddr, i, buf);
+
+ /* Clear the spin-lock. */
+ i = 0;
+ i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
+ i += push_opcode (&buf[i], "48 a3"); /* mov %rax, lockaddr */
+ memcpy (buf + i, &lockaddr, 8);
+ i += 8;
+ append_insns (&buildaddr, i, buf);
+
+ /* Remove stack that had been used for the collect_t object. */
+ i = 0;
+ i += push_opcode (&buf[i], "48 83 c4 18"); /* add $0x18,%rsp */
+ append_insns (&buildaddr, i, buf);
+
+ /* Restore register state. */
+ i = 0;
+ buf[i++] = 0x48; /* add $0x8,%rsp */
+ buf[i++] = 0x83;
+ buf[i++] = 0xc4;
+ buf[i++] = 0x08;
+ buf[i++] = 0x9d; /* popfq */
+ buf[i++] = 0x41; buf[i++] = 0x58; /* pop %r8 */
+ buf[i++] = 0x41; buf[i++] = 0x59; /* pop %r9 */
+ buf[i++] = 0x41; buf[i++] = 0x5a; /* pop %r10 */
+ buf[i++] = 0x41; buf[i++] = 0x5b; /* pop %r11 */
+ buf[i++] = 0x41; buf[i++] = 0x5c; /* pop %r12 */
+ buf[i++] = 0x41; buf[i++] = 0x5d; /* pop %r13 */
+ buf[i++] = 0x41; buf[i++] = 0x5e; /* pop %r14 */
+ buf[i++] = 0x41; buf[i++] = 0x5f; /* pop %r15 */
+ buf[i++] = 0x58; /* pop %rax */
+ buf[i++] = 0x5b; /* pop %rbx */
+ buf[i++] = 0x59; /* pop %rcx */
+ buf[i++] = 0x5a; /* pop %rdx */
+ buf[i++] = 0x5e; /* pop %rsi */
+ buf[i++] = 0x5f; /* pop %rdi */
+ buf[i++] = 0x5d; /* pop %rbp */
+ buf[i++] = 0x5c; /* pop %rsp */
+ append_insns (&buildaddr, i, buf);
+
+ /* Now, adjust the original instruction to execute in the jump
+ pad. */
+ *adjusted_insn_addr = buildaddr;
+ relocate_instruction (&buildaddr, tpaddr);
+ *adjusted_insn_addr_end = buildaddr;
+
+ /* Finally, write a jump back to the program. */
+
+ loffset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn));
+ if (loffset > INT_MAX || loffset < INT_MIN)
+ {
+ sprintf (err,
+ "E.Jump back from jump pad too far from tracepoint "
+ "(offset 0x%" PRIx64 " > int32).", loffset);
+ return 1;
+ }
+
+ offset = (int) loffset;
+ memcpy (buf, jump_insn, sizeof (jump_insn));
+ memcpy (buf + 1, &offset, 4);
+ append_insns (&buildaddr, sizeof (jump_insn), buf);
+
+ /* 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. */
+ loffset = *jump_entry - (tpaddr + sizeof (jump_insn));
+ if (loffset > INT_MAX || loffset < INT_MIN)
+ {
+ sprintf (err,
+ "E.Jump pad too far from tracepoint "
+ "(offset 0x%" PRIx64 " > int32).", loffset);
+ return 1;
+ }
+
+ offset = (int) loffset;
+
+ memcpy (buf, jump_insn, sizeof (jump_insn));
+ memcpy (buf + 1, &offset, 4);
+ memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
+ *jjump_pad_insn_size = sizeof (jump_insn);
+
+ /* Return the end address of our pad. */
+ *jump_entry = buildaddr;
+
+ return 0;
+}
+
+#endif /* __x86_64__ */
+
+/* Build a jump pad that saves registers and calls a collection
+ function. Writes a jump instruction to the jump pad to
+ JJUMPAD_INSN. The caller is responsible to write it in at the
+ tracepoint address. */
+
+static int
+i386_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)
+{
+ unsigned char buf[0x100];
+ int i, offset;
+ CORE_ADDR buildaddr = *jump_entry;
+
+ /* Build the jump pad. */
+
+ /* First, do tracepoint data collection. Save registers. */
+ i = 0;
+ buf[i++] = 0x60; /* pushad */
+ buf[i++] = 0x68; /* push tpaddr aka $pc */
+ *((int *)(buf + i)) = (int) tpaddr;
+ i += 4;
+ buf[i++] = 0x9c; /* pushf */
+ buf[i++] = 0x1e; /* push %ds */
+ buf[i++] = 0x06; /* push %es */
+ buf[i++] = 0x0f; /* push %fs */
+ buf[i++] = 0xa0;
+ buf[i++] = 0x0f; /* push %gs */
+ buf[i++] = 0xa8;
+ buf[i++] = 0x16; /* push %ss */
+ buf[i++] = 0x0e; /* push %cs */
+ append_insns (&buildaddr, i, buf);
+
+ /* Stack space for the collecting_t object. */
+ i = 0;
+ i += push_opcode (&buf[i], "83 ec 08"); /* sub $0x8,%esp */
+
+ /* Build the object. */
+ i += push_opcode (&buf[i], "b8"); /* mov <tpoint>,%eax */
+ memcpy (buf + i, &tpoint, 4);
+ i += 4;
+ i += push_opcode (&buf[i], "89 04 24"); /* mov %eax,(%esp) */
+
+ i += push_opcode (&buf[i], "65 a1 00 00 00 00"); /* mov %gs:0x0,%eax */
+ i += push_opcode (&buf[i], "89 44 24 04"); /* mov %eax,0x4(%esp) */
+ append_insns (&buildaddr, i, buf);
+
+ /* spin-lock. Note this is using cmpxchg, which leaves i386 behind.
+ If we cared for it, this could be using xchg alternatively. */
+
+ i = 0;
+ i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
+ i += push_opcode (&buf[i], "f0 0f b1 25"); /* lock cmpxchg
+ %esp,<lockaddr> */
+ memcpy (&buf[i], (void *) &lockaddr, 4);
+ i += 4;
+ i += push_opcode (&buf[i], "85 c0"); /* test %eax,%eax */
+ i += push_opcode (&buf[i], "75 f2"); /* jne <again> */
+ append_insns (&buildaddr, i, buf);
+
+
+ /* Set up arguments to the gdb_collect call. */
+ i = 0;
+ i += push_opcode (&buf[i], "89 e0"); /* mov %esp,%eax */
+ i += push_opcode (&buf[i], "83 c0 08"); /* add $0x08,%eax */
+ i += push_opcode (&buf[i], "89 44 24 fc"); /* mov %eax,-0x4(%esp) */
+ append_insns (&buildaddr, i, buf);
+
+ i = 0;
+ i += push_opcode (&buf[i], "83 ec 08"); /* sub $0x8,%esp */
+ append_insns (&buildaddr, i, buf);
+
+ i = 0;
+ i += push_opcode (&buf[i], "c7 04 24"); /* movl <addr>,(%esp) */
+ memcpy (&buf[i], (void *) &tpoint, 4);
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+
+ buf[0] = 0xe8; /* call <reladdr> */
+ offset = collector - (buildaddr + sizeof (jump_insn));
+ memcpy (buf + 1, &offset, 4);
+ append_insns (&buildaddr, 5, buf);
+ /* Clean up after the call. */
+ buf[0] = 0x83; /* add $0x8,%esp */
+ buf[1] = 0xc4;
+ buf[2] = 0x08;
+ append_insns (&buildaddr, 3, buf);
+
+
+ /* Clear the spin-lock. This would need the LOCK prefix on older
+ broken archs. */
+ i = 0;
+ i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
+ i += push_opcode (&buf[i], "a3"); /* mov %eax, lockaddr */
+ memcpy (buf + i, &lockaddr, 4);
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+
+
+ /* Remove stack that had been used for the collect_t object. */
+ i = 0;
+ i += push_opcode (&buf[i], "83 c4 08"); /* add $0x08,%esp */
+ append_insns (&buildaddr, i, buf);
+
+ i = 0;
+ buf[i++] = 0x83; /* add $0x4,%esp (no pop of %cs, assume unchanged) */
+ buf[i++] = 0xc4;
+ buf[i++] = 0x04;
+ buf[i++] = 0x17; /* pop %ss */
+ buf[i++] = 0x0f; /* pop %gs */
+ buf[i++] = 0xa9;
+ buf[i++] = 0x0f; /* pop %fs */
+ buf[i++] = 0xa1;
+ buf[i++] = 0x07; /* pop %es */
+ buf[i++] = 0x1f; /* pop %ds */
+ buf[i++] = 0x9d; /* popf */
+ buf[i++] = 0x83; /* add $0x4,%esp (pop of tpaddr aka $pc) */
+ buf[i++] = 0xc4;
+ buf[i++] = 0x04;
+ buf[i++] = 0x61; /* popad */
+ append_insns (&buildaddr, i, buf);
+
+ /* Now, adjust the original instruction to execute in the jump
+ pad. */
+ *adjusted_insn_addr = buildaddr;
+ relocate_instruction (&buildaddr, tpaddr);
+ *adjusted_insn_addr_end = buildaddr;
+
+ /* Write the jump back to the program. */
+ offset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn));
+ memcpy (buf, jump_insn, sizeof (jump_insn));
+ memcpy (buf + 1, &offset, 4);
+ append_insns (&buildaddr, sizeof (jump_insn), buf);
+
+ /* 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. */
+ if (orig_size == 4)
+ {
+ /* Create a trampoline. */
+ *trampoline_size = sizeof (jump_insn);
+ if (!claim_trampoline_space (*trampoline_size, trampoline))
+ {
+ /* No trampoline space available. */
+ strcpy (err,
+ "E.Cannot allocate trampoline space needed for fast "
+ "tracepoints on 4-byte instructions.");
+ return 1;
+ }
+
+ offset = *jump_entry - (*trampoline + sizeof (jump_insn));
+ memcpy (buf, jump_insn, sizeof (jump_insn));
+ memcpy (buf + 1, &offset, 4);
+ target_write_memory (*trampoline, buf, sizeof (jump_insn));
+
+ /* Use a 16-bit relative jump instruction to jump to the trampoline. */
+ offset = (*trampoline - (tpaddr + sizeof (small_jump_insn))) & 0xffff;
+ memcpy (buf, small_jump_insn, sizeof (small_jump_insn));
+ memcpy (buf + 2, &offset, 2);
+ memcpy (jjump_pad_insn, buf, sizeof (small_jump_insn));
+ *jjump_pad_insn_size = sizeof (small_jump_insn);
+ }
+ else
+ {
+ /* Else use a 32-bit relative jump instruction. */
+ offset = *jump_entry - (tpaddr + sizeof (jump_insn));
+ memcpy (buf, jump_insn, sizeof (jump_insn));
+ memcpy (buf + 1, &offset, 4);
+ memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
+ *jjump_pad_insn_size = sizeof (jump_insn);
+ }
+
+ /* Return the end address of our pad. */
+ *jump_entry = buildaddr;
+
+ return 0;
+}
+
+static int
+x86_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)
+{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
+ collector, lockaddr,
+ orig_size, jump_entry,
+ trampoline, trampoline_size,
+ jjump_pad_insn,
+ jjump_pad_insn_size,
+ adjusted_insn_addr,
+ adjusted_insn_addr_end,
+ err);
+#endif
+
+ return i386_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
+ collector, lockaddr,
+ orig_size, jump_entry,
+ trampoline, trampoline_size,
+ jjump_pad_insn,
+ jjump_pad_insn_size,
+ adjusted_insn_addr,
+ adjusted_insn_addr_end,
+ err);
+}
+
+/* Return the minimum instruction length for fast tracepoints on x86/x86-64
+ architectures. */
+
+static int
+x86_get_min_fast_tracepoint_insn_len (void)
+{
+ static int warned_about_fast_tracepoints = 0;
+
+#ifdef __x86_64__
+ /* On x86-64, 5-byte jump instructions with a 4-byte offset are always
+ used for fast tracepoints. */
+ if (is_64bit_tdesc ())
+ return 5;
+#endif
+
+ if (agent_loaded_p ())
+ {
+ char errbuf[IPA_BUFSIZ];
+
+ errbuf[0] = '\0';
+
+ /* On x86, if trampolines are available, then 4-byte jump instructions
+ with a 2-byte offset may be used, otherwise 5-byte jump instructions
+ with a 4-byte offset are used instead. */
+ if (have_fast_tracepoint_trampoline_buffer (errbuf))
+ return 4;
+ else
+ {
+ /* GDB has no channel to explain to user why a shorter fast
+ tracepoint is not possible, but at least make GDBserver
+ mention that something has gone awry. */
+ if (!warned_about_fast_tracepoints)
+ {
+ warning ("4-byte fast tracepoints not available; %s", errbuf);
+ warned_about_fast_tracepoints = 1;
+ }
+ return 5;
+ }
+ }
+ else
+ {
+ /* Indicate that the minimum length is currently unknown since the IPA
+ has not loaded yet. */
+ return 0;
+ }
+}
+
+static void
+add_insns (unsigned char *start, int len)
+{
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ if (debug_threads)
+ debug_printf ("Adding %d bytes of insn at %s\n",
+ len, paddress (buildaddr));
+
+ append_insns (&buildaddr, len, start);
+ current_insn_ptr = buildaddr;
+}
+
+/* Our general strategy for emitting code is to avoid specifying raw
+ bytes whenever possible, and instead copy a block of inline asm
+ that is embedded in the function. This is a little messy, because
+ we need to keep the compiler from discarding what looks like dead
+ code, plus suppress various warnings. */
+
+#define EMIT_ASM(NAME, INSNS) \
+ do \
+ { \
+ extern unsigned char start_ ## NAME, end_ ## NAME; \
+ add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \
+ __asm__ ("jmp end_" #NAME "\n" \
+ "\t" "start_" #NAME ":" \
+ "\t" INSNS "\n" \
+ "\t" "end_" #NAME ":"); \
+ } while (0)
+
+#ifdef __x86_64__
+
+#define EMIT_ASM32(NAME,INSNS) \
+ do \
+ { \
+ extern unsigned char start_ ## NAME, end_ ## NAME; \
+ add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \
+ __asm__ (".code32\n" \
+ "\t" "jmp end_" #NAME "\n" \
+ "\t" "start_" #NAME ":\n" \
+ "\t" INSNS "\n" \
+ "\t" "end_" #NAME ":\n" \
+ ".code64\n"); \
+ } while (0)
+
+#else
+
+#define EMIT_ASM32(NAME,INSNS) EMIT_ASM(NAME,INSNS)
+
+#endif
+
+#ifdef __x86_64__
+
+static void
+amd64_emit_prologue (void)
+{
+ EMIT_ASM (amd64_prologue,
+ "pushq %rbp\n\t"
+ "movq %rsp,%rbp\n\t"
+ "sub $0x20,%rsp\n\t"
+ "movq %rdi,-8(%rbp)\n\t"
+ "movq %rsi,-16(%rbp)");
+}
+
+
+static void
+amd64_emit_epilogue (void)
+{
+ EMIT_ASM (amd64_epilogue,
+ "movq -16(%rbp),%rdi\n\t"
+ "movq %rax,(%rdi)\n\t"
+ "xor %rax,%rax\n\t"
+ "leave\n\t"
+ "ret");
+}
+
+static void
+amd64_emit_add (void)
+{
+ EMIT_ASM (amd64_add,
+ "add (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_sub (void)
+{
+ EMIT_ASM (amd64_sub,
+ "sub %rax,(%rsp)\n\t"
+ "pop %rax");
+}
+
+static void
+amd64_emit_mul (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_lsh (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_rsh_signed (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_rsh_unsigned (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM (amd64_ext_8,
+ "cbtw\n\t"
+ "cwtl\n\t"
+ "cltq");
+ break;
+ case 16:
+ EMIT_ASM (amd64_ext_16,
+ "cwtl\n\t"
+ "cltq");
+ break;
+ case 32:
+ EMIT_ASM (amd64_ext_32,
+ "cltq");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+amd64_emit_log_not (void)
+{
+ EMIT_ASM (amd64_log_not,
+ "test %rax,%rax\n\t"
+ "sete %cl\n\t"
+ "movzbq %cl,%rax");
+}
+
+static void
+amd64_emit_bit_and (void)
+{
+ EMIT_ASM (amd64_and,
+ "and (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_bit_or (void)
+{
+ EMIT_ASM (amd64_or,
+ "or (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_bit_xor (void)
+{
+ EMIT_ASM (amd64_xor,
+ "xor (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_bit_not (void)
+{
+ EMIT_ASM (amd64_bit_not,
+ "xorq $0xffffffffffffffff,%rax");
+}
+
+static void
+amd64_emit_equal (void)
+{
+ EMIT_ASM (amd64_equal,
+ "cmp %rax,(%rsp)\n\t"
+ "je .Lamd64_equal_true\n\t"
+ "xor %rax,%rax\n\t"
+ "jmp .Lamd64_equal_end\n\t"
+ ".Lamd64_equal_true:\n\t"
+ "mov $0x1,%rax\n\t"
+ ".Lamd64_equal_end:\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_less_signed (void)
+{
+ EMIT_ASM (amd64_less_signed,
+ "cmp %rax,(%rsp)\n\t"
+ "jl .Lamd64_less_signed_true\n\t"
+ "xor %rax,%rax\n\t"
+ "jmp .Lamd64_less_signed_end\n\t"
+ ".Lamd64_less_signed_true:\n\t"
+ "mov $1,%rax\n\t"
+ ".Lamd64_less_signed_end:\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_less_unsigned (void)
+{
+ EMIT_ASM (amd64_less_unsigned,
+ "cmp %rax,(%rsp)\n\t"
+ "jb .Lamd64_less_unsigned_true\n\t"
+ "xor %rax,%rax\n\t"
+ "jmp .Lamd64_less_unsigned_end\n\t"
+ ".Lamd64_less_unsigned_true:\n\t"
+ "mov $1,%rax\n\t"
+ ".Lamd64_less_unsigned_end:\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_ref (int size)
+{
+ switch (size)
+ {
+ case 1:
+ EMIT_ASM (amd64_ref1,
+ "movb (%rax),%al");
+ break;
+ case 2:
+ EMIT_ASM (amd64_ref2,
+ "movw (%rax),%ax");
+ break;
+ case 4:
+ EMIT_ASM (amd64_ref4,
+ "movl (%rax),%eax");
+ break;
+ case 8:
+ EMIT_ASM (amd64_ref8,
+ "movq (%rax),%rax");
+ break;
+ }
+}
+
+static void
+amd64_emit_if_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_if_goto,
+ "mov %rax,%rcx\n\t"
+ "pop %rax\n\t"
+ "cmp $0,%rcx\n\t"
+ ".byte 0x0f, 0x85, 0x0, 0x0, 0x0, 0x0");
+ if (offset_p)
+ *offset_p = 10;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_emit_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_goto,
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0");
+ if (offset_p)
+ *offset_p = 1;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ int diff = (to - (from + size));
+ unsigned char buf[sizeof (int)];
+
+ if (size != 4)
+ {
+ emit_error = 1;
+ return;
+ }
+
+ memcpy (buf, &diff, sizeof (int));
+ target_write_memory (from, buf, sizeof (int));
+}
+
+static void
+amd64_emit_const (LONGEST num)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0x48; buf[i++] = 0xb8; /* mov $<n>,%rax */
+ memcpy (&buf[i], &num, sizeof (num));
+ i += 8;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+amd64_emit_call (CORE_ADDR fn)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+ LONGEST offset64;
+
+ /* The destination function being in the shared library, may be
+ >31-bits away off the compiled code pad. */
+
+ buildaddr = current_insn_ptr;
+
+ offset64 = fn - (buildaddr + 1 /* call op */ + 4 /* 32-bit offset */);
+
+ i = 0;
+
+ if (offset64 > INT_MAX || offset64 < INT_MIN)
+ {
+ /* Offset is too large for a call. Use callq, but that requires
+ a register, so avoid it if possible. Use r10, since it is
+ call-clobbered, we don't have to push/pop it. */
+ buf[i++] = 0x48; /* mov $fn,%r10 */
+ buf[i++] = 0xba;
+ memcpy (buf + i, &fn, 8);
+ i += 8;
+ buf[i++] = 0xff; /* callq *%r10 */
+ buf[i++] = 0xd2;
+ }
+ else
+ {
+ int offset32 = offset64; /* we know we can't overflow here. */
+
+ buf[i++] = 0xe8; /* call <reladdr> */
+ memcpy (buf + i, &offset32, 4);
+ i += 4;
+ }
+
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+amd64_emit_reg (int reg)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ /* Assume raw_regs is still in %rdi. */
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xbe; /* mov $<n>,%esi */
+ memcpy (&buf[i], ®, sizeof (reg));
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ amd64_emit_call (get_raw_reg_func_addr ());
+}
+
+static void
+amd64_emit_pop (void)
+{
+ EMIT_ASM (amd64_pop,
+ "pop %rax");
+}
+
+static void
+amd64_emit_stack_flush (void)
+{
+ EMIT_ASM (amd64_stack_flush,
+ "push %rax");
+}
+
+static void
+amd64_emit_zero_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM (amd64_zero_ext_8,
+ "and $0xff,%rax");
+ break;
+ case 16:
+ EMIT_ASM (amd64_zero_ext_16,
+ "and $0xffff,%rax");
+ break;
+ case 32:
+ EMIT_ASM (amd64_zero_ext_32,
+ "mov $0xffffffff,%rcx\n\t"
+ "and %rcx,%rax");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+amd64_emit_swap (void)
+{
+ EMIT_ASM (amd64_swap,
+ "mov %rax,%rcx\n\t"
+ "pop %rax\n\t"
+ "push %rcx");
+}
+
+static void
+amd64_emit_stack_adjust (int n)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0x48; /* lea $<n>(%rsp),%rsp */
+ buf[i++] = 0x8d;
+ buf[i++] = 0x64;
+ buf[i++] = 0x24;
+ /* This only handles adjustments up to 16, but we don't expect any more. */
+ buf[i++] = n * 8;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'. */
+
+static void
+amd64_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xbf; /* movl $<n>,%edi */
+ memcpy (&buf[i], &arg1, sizeof (arg1));
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ amd64_emit_call (fn);
+}
+
+/* FN's prototype is `void(*fn)(int,LONGEST)'. */
+
+static void
+amd64_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xbf; /* movl $<n>,%edi */
+ memcpy (&buf[i], &arg1, sizeof (arg1));
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ EMIT_ASM (amd64_void_call_2_a,
+ /* Save away a copy of the stack top. */
+ "push %rax\n\t"
+ /* Also pass top as the second argument. */
+ "mov %rax,%rsi");
+ amd64_emit_call (fn);
+ EMIT_ASM (amd64_void_call_2_b,
+ /* Restore the stack top, %rax may have been trashed. */
+ "pop %rax");
+}
+
+static void
+amd64_emit_eq_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_eq,
+ "cmp %rax,(%rsp)\n\t"
+ "jne .Lamd64_eq_fallthru\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lamd64_eq_fallthru:\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax");
+
+ if (offset_p)
+ *offset_p = 13;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_emit_ne_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_ne,
+ "cmp %rax,(%rsp)\n\t"
+ "je .Lamd64_ne_fallthru\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lamd64_ne_fallthru:\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax");
+
+ if (offset_p)
+ *offset_p = 13;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_emit_lt_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_lt,
+ "cmp %rax,(%rsp)\n\t"
+ "jnl .Lamd64_lt_fallthru\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lamd64_lt_fallthru:\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax");
+
+ if (offset_p)
+ *offset_p = 13;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_emit_le_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_le,
+ "cmp %rax,(%rsp)\n\t"
+ "jnle .Lamd64_le_fallthru\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lamd64_le_fallthru:\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax");
+
+ if (offset_p)
+ *offset_p = 13;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_emit_gt_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_gt,
+ "cmp %rax,(%rsp)\n\t"
+ "jng .Lamd64_gt_fallthru\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lamd64_gt_fallthru:\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax");
+
+ if (offset_p)
+ *offset_p = 13;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_emit_ge_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_ge,
+ "cmp %rax,(%rsp)\n\t"
+ "jnge .Lamd64_ge_fallthru\n\t"
+ ".Lamd64_ge_jump:\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lamd64_ge_fallthru:\n\t"
+ "lea 0x8(%rsp),%rsp\n\t"
+ "pop %rax");
+
+ if (offset_p)
+ *offset_p = 13;
+ if (size_p)
+ *size_p = 4;
+}
+
+struct emit_ops amd64_emit_ops =
+ {
+ amd64_emit_prologue,
+ amd64_emit_epilogue,
+ amd64_emit_add,
+ amd64_emit_sub,
+ amd64_emit_mul,
+ amd64_emit_lsh,
+ amd64_emit_rsh_signed,
+ amd64_emit_rsh_unsigned,
+ amd64_emit_ext,
+ amd64_emit_log_not,
+ amd64_emit_bit_and,
+ amd64_emit_bit_or,
+ amd64_emit_bit_xor,
+ amd64_emit_bit_not,
+ amd64_emit_equal,
+ amd64_emit_less_signed,
+ amd64_emit_less_unsigned,
+ amd64_emit_ref,
+ amd64_emit_if_goto,
+ amd64_emit_goto,
+ amd64_write_goto_address,
+ amd64_emit_const,
+ amd64_emit_call,
+ amd64_emit_reg,
+ amd64_emit_pop,
+ amd64_emit_stack_flush,
+ amd64_emit_zero_ext,
+ amd64_emit_swap,
+ amd64_emit_stack_adjust,
+ amd64_emit_int_call_1,
+ amd64_emit_void_call_2,
+ amd64_emit_eq_goto,
+ amd64_emit_ne_goto,
+ amd64_emit_lt_goto,
+ amd64_emit_le_goto,
+ amd64_emit_gt_goto,
+ amd64_emit_ge_goto
+ };
+
+#endif /* __x86_64__ */
+
+static void
+i386_emit_prologue (void)
+{
+ EMIT_ASM32 (i386_prologue,
+ "push %ebp\n\t"
+ "mov %esp,%ebp\n\t"
+ "push %ebx");
+ /* At this point, the raw regs base address is at 8(%ebp), and the
+ value pointer is at 12(%ebp). */
+}
+
+static void
+i386_emit_epilogue (void)
+{
+ EMIT_ASM32 (i386_epilogue,
+ "mov 12(%ebp),%ecx\n\t"
+ "mov %eax,(%ecx)\n\t"
+ "mov %ebx,0x4(%ecx)\n\t"
+ "xor %eax,%eax\n\t"
+ "pop %ebx\n\t"
+ "pop %ebp\n\t"
+ "ret");
+}
+
+static void
+i386_emit_add (void)
+{
+ EMIT_ASM32 (i386_add,
+ "add (%esp),%eax\n\t"
+ "adc 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_sub (void)
+{
+ EMIT_ASM32 (i386_sub,
+ "subl %eax,(%esp)\n\t"
+ "sbbl %ebx,4(%esp)\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t");
+}
+
+static void
+i386_emit_mul (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_lsh (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_rsh_signed (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_rsh_unsigned (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM32 (i386_ext_8,
+ "cbtw\n\t"
+ "cwtl\n\t"
+ "movl %eax,%ebx\n\t"
+ "sarl $31,%ebx");
+ break;
+ case 16:
+ EMIT_ASM32 (i386_ext_16,
+ "cwtl\n\t"
+ "movl %eax,%ebx\n\t"
+ "sarl $31,%ebx");
+ break;
+ case 32:
+ EMIT_ASM32 (i386_ext_32,
+ "movl %eax,%ebx\n\t"
+ "sarl $31,%ebx");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+i386_emit_log_not (void)
+{
+ EMIT_ASM32 (i386_log_not,
+ "or %ebx,%eax\n\t"
+ "test %eax,%eax\n\t"
+ "sete %cl\n\t"
+ "xor %ebx,%ebx\n\t"
+ "movzbl %cl,%eax");
+}
+
+static void
+i386_emit_bit_and (void)
+{
+ EMIT_ASM32 (i386_and,
+ "and (%esp),%eax\n\t"
+ "and 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_bit_or (void)
+{
+ EMIT_ASM32 (i386_or,
+ "or (%esp),%eax\n\t"
+ "or 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_bit_xor (void)
+{
+ EMIT_ASM32 (i386_xor,
+ "xor (%esp),%eax\n\t"
+ "xor 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_bit_not (void)
+{
+ EMIT_ASM32 (i386_bit_not,
+ "xor $0xffffffff,%eax\n\t"
+ "xor $0xffffffff,%ebx\n\t");
+}
+
+static void
+i386_emit_equal (void)
+{
+ EMIT_ASM32 (i386_equal,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jne .Li386_equal_false\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "je .Li386_equal_true\n\t"
+ ".Li386_equal_false:\n\t"
+ "xor %eax,%eax\n\t"
+ "jmp .Li386_equal_end\n\t"
+ ".Li386_equal_true:\n\t"
+ "mov $1,%eax\n\t"
+ ".Li386_equal_end:\n\t"
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_less_signed (void)
+{
+ EMIT_ASM32 (i386_less_signed,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jl .Li386_less_signed_true\n\t"
+ "jne .Li386_less_signed_false\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jl .Li386_less_signed_true\n\t"
+ ".Li386_less_signed_false:\n\t"
+ "xor %eax,%eax\n\t"
+ "jmp .Li386_less_signed_end\n\t"
+ ".Li386_less_signed_true:\n\t"
+ "mov $1,%eax\n\t"
+ ".Li386_less_signed_end:\n\t"
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_less_unsigned (void)
+{
+ EMIT_ASM32 (i386_less_unsigned,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jb .Li386_less_unsigned_true\n\t"
+ "jne .Li386_less_unsigned_false\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jb .Li386_less_unsigned_true\n\t"
+ ".Li386_less_unsigned_false:\n\t"
+ "xor %eax,%eax\n\t"
+ "jmp .Li386_less_unsigned_end\n\t"
+ ".Li386_less_unsigned_true:\n\t"
+ "mov $1,%eax\n\t"
+ ".Li386_less_unsigned_end:\n\t"
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_ref (int size)
+{
+ switch (size)
+ {
+ case 1:
+ EMIT_ASM32 (i386_ref1,
+ "movb (%eax),%al");
+ break;
+ case 2:
+ EMIT_ASM32 (i386_ref2,
+ "movw (%eax),%ax");
+ break;
+ case 4:
+ EMIT_ASM32 (i386_ref4,
+ "movl (%eax),%eax");
+ break;
+ case 8:
+ EMIT_ASM32 (i386_ref8,
+ "movl 4(%eax),%ebx\n\t"
+ "movl (%eax),%eax");
+ break;
+ }
+}
+
+static void
+i386_emit_if_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (i386_if_goto,
+ "mov %eax,%ecx\n\t"
+ "or %ebx,%ecx\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ "cmpl $0,%ecx\n\t"
+ /* Don't trust the assembler to choose the right jump */
+ ".byte 0x0f, 0x85, 0x0, 0x0, 0x0, 0x0");
+
+ if (offset_p)
+ *offset_p = 11; /* be sure that this matches the sequence above */
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_emit_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (i386_goto,
+ /* Don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0");
+ if (offset_p)
+ *offset_p = 1;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ int diff = (to - (from + size));
+ unsigned char buf[sizeof (int)];
+
+ /* We're only doing 4-byte sizes at the moment. */
+ if (size != 4)
+ {
+ emit_error = 1;
+ return;
+ }
+
+ memcpy (buf, &diff, sizeof (int));
+ target_write_memory (from, buf, sizeof (int));
+}
+
+static void
+i386_emit_const (LONGEST num)
+{
+ unsigned char buf[16];
+ int i, hi, lo;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0xb8; /* mov $<n>,%eax */
+ lo = num & 0xffffffff;
+ memcpy (&buf[i], &lo, sizeof (lo));
+ i += 4;
+ hi = ((num >> 32) & 0xffffffff);
+ if (hi)
+ {
+ buf[i++] = 0xbb; /* mov $<n>,%ebx */
+ memcpy (&buf[i], &hi, sizeof (hi));
+ i += 4;
+ }
+ else
+ {
+ buf[i++] = 0x31; buf[i++] = 0xdb; /* xor %ebx,%ebx */
+ }
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+i386_emit_call (CORE_ADDR fn)
+{
+ unsigned char buf[16];
+ int i, offset;
+ CORE_ADDR buildaddr;
+
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xe8; /* call <reladdr> */
+ offset = ((int) fn) - (buildaddr + 5);
+ memcpy (buf + 1, &offset, 4);
+ append_insns (&buildaddr, 5, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+i386_emit_reg (int reg)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ EMIT_ASM32 (i386_reg_a,
+ "sub $0x8,%esp");
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xb8; /* mov $<n>,%eax */
+ memcpy (&buf[i], ®, sizeof (reg));
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ EMIT_ASM32 (i386_reg_b,
+ "mov %eax,4(%esp)\n\t"
+ "mov 8(%ebp),%eax\n\t"
+ "mov %eax,(%esp)");
+ i386_emit_call (get_raw_reg_func_addr ());
+ EMIT_ASM32 (i386_reg_c,
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_pop (void)
+{
+ EMIT_ASM32 (i386_pop,
+ "pop %eax\n\t"
+ "pop %ebx");
+}
+
+static void
+i386_emit_stack_flush (void)
+{
+ EMIT_ASM32 (i386_stack_flush,
+ "push %ebx\n\t"
+ "push %eax");
+}
+
+static void
+i386_emit_zero_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM32 (i386_zero_ext_8,
+ "and $0xff,%eax\n\t"
+ "xor %ebx,%ebx");
+ break;
+ case 16:
+ EMIT_ASM32 (i386_zero_ext_16,
+ "and $0xffff,%eax\n\t"
+ "xor %ebx,%ebx");
+ break;
+ case 32:
+ EMIT_ASM32 (i386_zero_ext_32,
+ "xor %ebx,%ebx");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+i386_emit_swap (void)
+{
+ EMIT_ASM32 (i386_swap,
+ "mov %eax,%ecx\n\t"
+ "mov %ebx,%edx\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ "push %edx\n\t"
+ "push %ecx");
+}
+
+static void
+i386_emit_stack_adjust (int n)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0x8d; /* lea $<n>(%esp),%esp */
+ buf[i++] = 0x64;
+ buf[i++] = 0x24;
+ buf[i++] = n * 8;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'. */
+
+static void
+i386_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ EMIT_ASM32 (i386_int_call_1_a,
+ /* Reserve a bit of stack space. */
+ "sub $0x8,%esp");
+ /* Put the one argument on the stack. */
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xc7; /* movl $<arg1>,(%esp) */
+ buf[i++] = 0x04;
+ buf[i++] = 0x24;
+ memcpy (&buf[i], &arg1, sizeof (arg1));
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ i386_emit_call (fn);
+ EMIT_ASM32 (i386_int_call_1_c,
+ "mov %edx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+/* FN's prototype is `void(*fn)(int,LONGEST)'. */
+
+static void
+i386_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ EMIT_ASM32 (i386_void_call_2_a,
+ /* Preserve %eax only; we don't have to worry about %ebx. */
+ "push %eax\n\t"
+ /* Reserve a bit of stack space for arguments. */
+ "sub $0x10,%esp\n\t"
+ /* Copy "top" to the second argument position. (Note that
+ we can't assume function won't scribble on its
+ arguments, so don't try to restore from this.) */
+ "mov %eax,4(%esp)\n\t"
+ "mov %ebx,8(%esp)");
+ /* Put the first argument on the stack. */
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xc7; /* movl $<arg1>,(%esp) */
+ buf[i++] = 0x04;
+ buf[i++] = 0x24;
+ memcpy (&buf[i], &arg1, sizeof (arg1));
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ i386_emit_call (fn);
+ EMIT_ASM32 (i386_void_call_2_b,
+ "lea 0x10(%esp),%esp\n\t"
+ /* Restore original stack top. */
+ "pop %eax");
+}
+
+
+static void
+i386_emit_eq_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (eq,
+ /* Check low half first, more likely to be decider */
+ "cmpl %eax,(%esp)\n\t"
+ "jne .Leq_fallthru\n\t"
+ "cmpl %ebx,4(%esp)\n\t"
+ "jne .Leq_fallthru\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Leq_fallthru:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx");
+
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_emit_ne_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (ne,
+ /* Check low half first, more likely to be decider */
+ "cmpl %eax,(%esp)\n\t"
+ "jne .Lne_jump\n\t"
+ "cmpl %ebx,4(%esp)\n\t"
+ "je .Lne_fallthru\n\t"
+ ".Lne_jump:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lne_fallthru:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx");
+
+ if (offset_p)
+ *offset_p = 18;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_emit_lt_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (lt,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jl .Llt_jump\n\t"
+ "jne .Llt_fallthru\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jnl .Llt_fallthru\n\t"
+ ".Llt_jump:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Llt_fallthru:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx");
+
+ if (offset_p)
+ *offset_p = 20;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_emit_le_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (le,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jle .Lle_jump\n\t"
+ "jne .Lle_fallthru\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jnle .Lle_fallthru\n\t"
+ ".Lle_jump:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lle_fallthru:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx");
+
+ if (offset_p)
+ *offset_p = 20;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_emit_gt_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (gt,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jg .Lgt_jump\n\t"
+ "jne .Lgt_fallthru\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jng .Lgt_fallthru\n\t"
+ ".Lgt_jump:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lgt_fallthru:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx");
+
+ if (offset_p)
+ *offset_p = 20;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_emit_ge_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (ge,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jge .Lge_jump\n\t"
+ "jne .Lge_fallthru\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jnge .Lge_fallthru\n\t"
+ ".Lge_jump:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ /* jmp, but don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t"
+ ".Lge_fallthru:\n\t"
+ "lea 0x8(%esp),%esp\n\t"
+ "pop %eax\n\t"
+ "pop %ebx");
+
+ if (offset_p)
+ *offset_p = 20;
+ if (size_p)
+ *size_p = 4;
+}
+
+struct emit_ops i386_emit_ops =
+ {
+ i386_emit_prologue,
+ i386_emit_epilogue,
+ i386_emit_add,
+ i386_emit_sub,
+ i386_emit_mul,
+ i386_emit_lsh,
+ i386_emit_rsh_signed,
+ i386_emit_rsh_unsigned,
+ i386_emit_ext,
+ i386_emit_log_not,
+ i386_emit_bit_and,
+ i386_emit_bit_or,
+ i386_emit_bit_xor,
+ i386_emit_bit_not,
+ i386_emit_equal,
+ i386_emit_less_signed,
+ i386_emit_less_unsigned,
+ i386_emit_ref,
+ i386_emit_if_goto,
+ i386_emit_goto,
+ i386_write_goto_address,
+ i386_emit_const,
+ i386_emit_call,
+ i386_emit_reg,
+ i386_emit_pop,
+ i386_emit_stack_flush,
+ i386_emit_zero_ext,
+ i386_emit_swap,
+ i386_emit_stack_adjust,
+ i386_emit_int_call_1,
+ i386_emit_void_call_2,
+ i386_emit_eq_goto,
+ i386_emit_ne_goto,
+ i386_emit_lt_goto,
+ i386_emit_le_goto,
+ i386_emit_gt_goto,
+ i386_emit_ge_goto
+ };
+
+
+static struct emit_ops *
+x86_emit_ops (void)
+{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return &amd64_emit_ops;
+ else
+#endif
+ return &i386_emit_ops;
+}
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+x86_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = x86_breakpoint_len;
+ return x86_breakpoint;
+}
+
+static int
+x86_supports_range_stepping (void)
+{
+ return 1;
+}
+
+/* Implementation of linux_target_ops method "supports_hardware_single_step".
+ */
+
+static int
+x86_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static int
+x86_get_ipa_tdesc_idx (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+ const struct target_desc *tdesc = regcache->tdesc;
+
+#ifdef __x86_64__
+ return amd64_get_ipa_tdesc_idx (tdesc);
+#endif
+
+ if (tdesc == tdesc_i386_linux_no_xml)
+ return X86_TDESC_SSE;
+
+ return i386_get_ipa_tdesc_idx (tdesc);
+}
+
+/* This is initialized assuming an amd64 target.
+ x86_arch_setup will correct it for i386 or amd64 targets. */
+
+struct linux_target_ops the_low_target =
+{
+ x86_arch_setup,
+ x86_linux_regs_info,
+ x86_cannot_fetch_register,
+ x86_cannot_store_register,
+ NULL, /* fetch_register */
+ x86_get_pc,
+ x86_set_pc,
+ NULL, /* breakpoint_kind_from_pc */
+ x86_sw_breakpoint_from_kind,
+ NULL,
+ 1,
+ x86_breakpoint_at,
+ x86_supports_z_point_type,
+ x86_insert_point,
+ x86_remove_point,
+ x86_stopped_by_watchpoint,
+ x86_stopped_data_address,
+ /* collect_ptrace_register/supply_ptrace_register are not needed in the
+ native i386 case (no registers smaller than an xfer unit), and are not
+ used in the biarch case (HAVE_LINUX_USRREGS is not defined). */
+ NULL,
+ NULL,
+ /* need to fix up i386 siginfo if host is amd64 */
+ x86_siginfo_fixup,
+ x86_linux_new_process,
+ x86_linux_delete_process,
+ x86_linux_new_thread,
+ x86_linux_delete_thread,
+ x86_linux_new_fork,
+ x86_linux_prepare_to_resume,
+ x86_linux_process_qsupported,
+ x86_supports_tracepoints,
+ x86_get_thread_area,
+ x86_install_fast_tracepoint_jump_pad,
+ x86_emit_ops,
+ x86_get_min_fast_tracepoint_insn_len,
+ x86_supports_range_stepping,
+ NULL, /* breakpoint_kind_from_current_state */
+ x86_supports_hardware_single_step,
+ x86_get_syscall_trapinfo,
+ x86_get_ipa_tdesc_idx,
+};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+#ifdef __x86_64__
+ tdesc_amd64_linux_no_xml = allocate_target_description ();
+ copy_target_description (tdesc_amd64_linux_no_xml,
+ amd64_linux_read_description (X86_XSTATE_SSE_MASK,
+ false));
+ tdesc_amd64_linux_no_xml->xmltarget = xmltarget_amd64_linux_no_xml;
+#endif
+
+ tdesc_i386_linux_no_xml = allocate_target_description ();
+ copy_target_description (tdesc_i386_linux_no_xml,
+ i386_linux_read_description (X86_XSTATE_SSE_MASK));
+ tdesc_i386_linux_no_xml->xmltarget = xmltarget_i386_linux_no_xml;
+
+ initialize_regsets_info (&x86_regsets_info);
+}
+++ /dev/null
-/* GNU/Linux/x86-64 specific target description, for the remote server
- for GDB.
- Copyright (C) 2017-2020 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 "tdesc.h"
-#include "linux-x86-tdesc.h"
-#include "arch/i386.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#endif
-#include "x86-tdesc.h"
-
-/* Return the right x86_linux_tdesc index for a given XCR0. Return
- X86_TDESC_LAST if can't find a match. */
-
-static enum x86_linux_tdesc
-xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32)
-{
- if (xcr0 & X86_XSTATE_PKRU)
- {
- if (is_x32)
- {
- /* No x32 MPX and PKU, fall back to avx_avx512. */
- return X86_TDESC_AVX_AVX512;
- }
- else
- return X86_TDESC_AVX_MPX_AVX512_PKU;
- }
- else if (xcr0 & X86_XSTATE_AVX512)
- return X86_TDESC_AVX_AVX512;
- else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK)
- {
- if (is_x32) /* No MPX on x32. */
- return X86_TDESC_AVX;
- else
- return X86_TDESC_AVX_MPX;
- }
- else if (xcr0 & X86_XSTATE_MPX)
- {
- if (is_x32) /* No MPX on x32. */
- return X86_TDESC_AVX;
- else
- return X86_TDESC_MPX;
- }
- else if (xcr0 & X86_XSTATE_AVX)
- return X86_TDESC_AVX;
- else if (xcr0 & X86_XSTATE_SSE)
- return X86_TDESC_SSE;
- else if (xcr0 & X86_XSTATE_X87)
- return X86_TDESC_MMX;
- else
- return X86_TDESC_LAST;
-}
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-
-static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { };
-
-/* Return the target description according to XCR0. */
-
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
-{
- enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false);
-
- if (idx == X86_TDESC_LAST)
- return NULL;
-
- struct target_desc **tdesc = &i386_tdescs[idx];
-
- if (*tdesc == NULL)
- {
- *tdesc = i386_create_target_description (xcr0, true, false);
-
- init_target_desc (*tdesc, i386_expedite_regs);
- }
-
- return *tdesc;;
-}
-#endif
-
-#ifdef __x86_64__
-
-static target_desc *amd64_tdescs[X86_TDESC_LAST] = { };
-static target_desc *x32_tdescs[X86_TDESC_LAST] = { };
-
-const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
-{
- enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32);
-
- if (idx == X86_TDESC_LAST)
- return NULL;
-
- struct target_desc **tdesc = NULL;
-
- if (is_x32)
- tdesc = &x32_tdescs[idx];
- else
- tdesc = &amd64_tdescs[idx];
-
- if (*tdesc == NULL)
- {
- *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
-
- init_target_desc (*tdesc, amd64_expedite_regs);
- }
- return *tdesc;
-}
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-int
-i386_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
- for (int i = 0; i < X86_TDESC_LAST; i++)
- {
- if (tdesc == i386_tdescs[i])
- return i;
- }
-
- /* If none tdesc is found, return the one with minimum features. */
- return X86_TDESC_MMX;
-}
-
-#if defined __x86_64__
-int
-amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
- for (int i = 0; i < X86_TDESC_LAST; i++)
- {
- if (tdesc == amd64_tdescs[i])
- return i;
- }
- for (int i = 0; i < X86_TDESC_LAST; i++)
- {
- if (tdesc == x32_tdescs[i])
- return i;
- }
-
- return X86_TDESC_SSE;
-}
-
-#endif
-#endif
--- /dev/null
+/* GNU/Linux/x86-64 specific target description, for the remote server
+ for GDB.
+ Copyright (C) 2017-2020 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 "tdesc.h"
+#include "linux-x86-tdesc.h"
+#include "arch/i386.h"
+#include "gdbsupport/x86-xstate.h"
+#ifdef __x86_64__
+#include "arch/amd64.h"
+#endif
+#include "x86-tdesc.h"
+
+/* Return the right x86_linux_tdesc index for a given XCR0. Return
+ X86_TDESC_LAST if can't find a match. */
+
+static enum x86_linux_tdesc
+xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32)
+{
+ if (xcr0 & X86_XSTATE_PKRU)
+ {
+ if (is_x32)
+ {
+ /* No x32 MPX and PKU, fall back to avx_avx512. */
+ return X86_TDESC_AVX_AVX512;
+ }
+ else
+ return X86_TDESC_AVX_MPX_AVX512_PKU;
+ }
+ else if (xcr0 & X86_XSTATE_AVX512)
+ return X86_TDESC_AVX_AVX512;
+ else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK)
+ {
+ if (is_x32) /* No MPX on x32. */
+ return X86_TDESC_AVX;
+ else
+ return X86_TDESC_AVX_MPX;
+ }
+ else if (xcr0 & X86_XSTATE_MPX)
+ {
+ if (is_x32) /* No MPX on x32. */
+ return X86_TDESC_AVX;
+ else
+ return X86_TDESC_MPX;
+ }
+ else if (xcr0 & X86_XSTATE_AVX)
+ return X86_TDESC_AVX;
+ else if (xcr0 & X86_XSTATE_SSE)
+ return X86_TDESC_SSE;
+ else if (xcr0 & X86_XSTATE_X87)
+ return X86_TDESC_MMX;
+ else
+ return X86_TDESC_LAST;
+}
+
+#if defined __i386__ || !defined IN_PROCESS_AGENT
+
+static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { };
+
+/* Return the target description according to XCR0. */
+
+const struct target_desc *
+i386_linux_read_description (uint64_t xcr0)
+{
+ enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false);
+
+ if (idx == X86_TDESC_LAST)
+ return NULL;
+
+ struct target_desc **tdesc = &i386_tdescs[idx];
+
+ if (*tdesc == NULL)
+ {
+ *tdesc = i386_create_target_description (xcr0, true, false);
+
+ init_target_desc (*tdesc, i386_expedite_regs);
+ }
+
+ return *tdesc;;
+}
+#endif
+
+#ifdef __x86_64__
+
+static target_desc *amd64_tdescs[X86_TDESC_LAST] = { };
+static target_desc *x32_tdescs[X86_TDESC_LAST] = { };
+
+const struct target_desc *
+amd64_linux_read_description (uint64_t xcr0, bool is_x32)
+{
+ enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32);
+
+ if (idx == X86_TDESC_LAST)
+ return NULL;
+
+ struct target_desc **tdesc = NULL;
+
+ if (is_x32)
+ tdesc = &x32_tdescs[idx];
+ else
+ tdesc = &amd64_tdescs[idx];
+
+ if (*tdesc == NULL)
+ {
+ *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
+
+ init_target_desc (*tdesc, amd64_expedite_regs);
+ }
+ return *tdesc;
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+int
+i386_get_ipa_tdesc_idx (const struct target_desc *tdesc)
+{
+ for (int i = 0; i < X86_TDESC_LAST; i++)
+ {
+ if (tdesc == i386_tdescs[i])
+ return i;
+ }
+
+ /* If none tdesc is found, return the one with minimum features. */
+ return X86_TDESC_MMX;
+}
+
+#if defined __x86_64__
+int
+amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc)
+{
+ for (int i = 0; i < X86_TDESC_LAST; i++)
+ {
+ if (tdesc == amd64_tdescs[i])
+ return i;
+ }
+ for (int i = 0; i < X86_TDESC_LAST; i++)
+ {
+ if (tdesc == x32_tdescs[i])
+ return i;
+ }
+
+ return X86_TDESC_SSE;
+}
+
+#endif
+#endif
+++ /dev/null
-/* GNU/Linux/Xtensa specific low level interface, for the remote server for GDB.
- Copyright (C) 2007-2020 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-
-#include "server.h"
-#include "linux-low.h"
-
-/* Defined in auto-generated file reg-xtensa.c. */
-void init_registers_xtensa (void);
-extern const struct target_desc *tdesc_xtensa;
-
-#include <asm/ptrace.h>
-#include <xtensa-config.h>
-#include "arch/xtensa.h"
-#include "gdb_proc_service.h"
-
-#include "xtensa-xtregs.c"
-
-enum regnum {
- R_PC=0, R_PS,
- R_LBEG, R_LEND, R_LCOUNT,
- R_SAR,
- R_WS, R_WB,
- R_THREADPTR,
- R_A0 = 64
-};
-
-static void
-xtensa_fill_gregset (struct regcache *regcache, void *buf)
-{
- elf_greg_t* rset = (elf_greg_t*)buf;
- const struct target_desc *tdesc = regcache->tdesc;
- int ar0_regnum;
- char *ptr;
- int i;
-
- /* Take care of AR registers. */
-
- ar0_regnum = find_regno (tdesc, "ar0");
- ptr = (char*)&rset[R_A0];
-
- for (i = ar0_regnum; i < ar0_regnum + XCHAL_NUM_AREGS; i++)
- {
- collect_register (regcache, i, ptr);
- ptr += register_size (tdesc, i);
- }
-
- if (XSHAL_ABI == XTHAL_ABI_CALL0)
- {
- int a0_regnum = find_regno (tdesc, "a0");
- ptr = (char *) &rset[R_A0 + 4 * rset[R_WB]];
-
- for (i = a0_regnum; i < a0_regnum + C0_NREGS; i++)
- {
- if ((4 * rset[R_WB] + i - a0_regnum) == XCHAL_NUM_AREGS)
- ptr = (char *) &rset[R_A0];
- collect_register (regcache, i, ptr);
- ptr += register_size (tdesc, i);
- }
- }
-
- /* Loop registers, if hardware has it. */
-
-#if XCHAL_HAVE_LOOPS
- collect_register_by_name (regcache, "lbeg", (char*)&rset[R_LBEG]);
- collect_register_by_name (regcache, "lend", (char*)&rset[R_LEND]);
- collect_register_by_name (regcache, "lcount", (char*)&rset[R_LCOUNT]);
-#endif
-
- collect_register_by_name (regcache, "sar", (char*)&rset[R_SAR]);
- collect_register_by_name (regcache, "pc", (char*)&rset[R_PC]);
- collect_register_by_name (regcache, "ps", (char*)&rset[R_PS]);
- collect_register_by_name (regcache, "windowbase", (char*)&rset[R_WB]);
- collect_register_by_name (regcache, "windowstart", (char*)&rset[R_WS]);
-
-#if XCHAL_HAVE_THREADPTR
- collect_register_by_name (regcache, "threadptr",
- (char *) &rset[R_THREADPTR]);
-#endif
-}
-
-static void
-xtensa_store_gregset (struct regcache *regcache, const void *buf)
-{
- const elf_greg_t* rset = (const elf_greg_t*)buf;
- const struct target_desc *tdesc = regcache->tdesc;
- int ar0_regnum;
- char *ptr;
- int i;
-
- /* Take care of AR registers. */
-
- ar0_regnum = find_regno (tdesc, "ar0");
- ptr = (char *)&rset[R_A0];
-
- for (i = ar0_regnum; i < ar0_regnum + XCHAL_NUM_AREGS; i++)
- {
- supply_register (regcache, i, ptr);
- ptr += register_size (tdesc, i);
- }
-
- if (XSHAL_ABI == XTHAL_ABI_CALL0)
- {
- int a0_regnum = find_regno (tdesc, "a0");
- ptr = (char *) &rset[R_A0 + (4 * rset[R_WB]) % XCHAL_NUM_AREGS];
-
- for (i = a0_regnum; i < a0_regnum + C0_NREGS; i++)
- {
- if ((4 * rset[R_WB] + i - a0_regnum) == XCHAL_NUM_AREGS)
- ptr = (char *) &rset[R_A0];
- supply_register (regcache, i, ptr);
- ptr += register_size (tdesc, i);
- }
- }
-
- /* Loop registers, if hardware has it. */
-
-#if XCHAL_HAVE_LOOPS
- supply_register_by_name (regcache, "lbeg", (char*)&rset[R_LBEG]);
- supply_register_by_name (regcache, "lend", (char*)&rset[R_LEND]);
- supply_register_by_name (regcache, "lcount", (char*)&rset[R_LCOUNT]);
-#endif
-
- supply_register_by_name (regcache, "sar", (char*)&rset[R_SAR]);
- supply_register_by_name (regcache, "pc", (char*)&rset[R_PC]);
- supply_register_by_name (regcache, "ps", (char*)&rset[R_PS]);
- supply_register_by_name (regcache, "windowbase", (char*)&rset[R_WB]);
- supply_register_by_name (regcache, "windowstart", (char*)&rset[R_WS]);
-
-#if XCHAL_HAVE_THREADPTR
- supply_register_by_name (regcache, "threadptr",
- (char *) &rset[R_THREADPTR]);
-#endif
-}
-
-/* Xtensa GNU/Linux PTRACE interface includes extended register set. */
-
-static void
-xtensa_fill_xtregset (struct regcache *regcache, void *buf)
-{
- const xtensa_regtable_t *ptr;
-
- for (ptr = xtensa_regmap_table; ptr->name; ptr++)
- {
- collect_register_by_name (regcache, ptr->name,
- (char*)buf + ptr->ptrace_offset);
- }
-}
-
-static void
-xtensa_store_xtregset (struct regcache *regcache, const void *buf)
-{
- const xtensa_regtable_t *ptr;
-
- for (ptr = xtensa_regmap_table; ptr->name; ptr++)
- {
- supply_register_by_name (regcache, ptr->name,
- (char*)buf + ptr->ptrace_offset);
- }
-}
-
-static struct regset_info xtensa_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
- GENERAL_REGS,
- xtensa_fill_gregset, xtensa_store_gregset },
- { PTRACE_GETXTREGS, PTRACE_SETXTREGS, 0, XTENSA_ELF_XTREG_SIZE,
- EXTENDED_REGS,
- xtensa_fill_xtregset, xtensa_store_xtregset },
- NULL_REGSET
-};
-
-#if XCHAL_HAVE_BE
-#define XTENSA_BREAKPOINT {0xd2,0x0f}
-#else
-#define XTENSA_BREAKPOINT {0x2d,0xf0}
-#endif
-
-static const gdb_byte xtensa_breakpoint[] = XTENSA_BREAKPOINT;
-#define xtensa_breakpoint_len 2
-
-/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-xtensa_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = xtensa_breakpoint_len;
- return xtensa_breakpoint;
-}
-
-static int
-xtensa_breakpoint_at (CORE_ADDR where)
-{
- unsigned long insn;
-
- (*the_target->read_memory) (where, (unsigned char *) &insn,
- xtensa_breakpoint_len);
- return memcmp((char *) &insn,
- xtensa_breakpoint, xtensa_breakpoint_len) == 0;
-}
-
-/* Called by libthread_db. */
-
-ps_err_e
-ps_get_thread_area (struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- xtensa_elf_gregset_t regs;
-
- if (ptrace (PTRACE_GETREGS, lwpid, NULL, ®s) != 0)
- return PS_ERR;
-
- /* IDX is the bias from the thread pointer to the beginning of the
- thread descriptor. It has to be subtracted due to implementation
- quirks in libthread_db. */
- *base = (void *) ((char *) regs.threadptr - idx);
-
- return PS_OK;
-}
-
-static struct regsets_info xtensa_regsets_info =
- {
- xtensa_regsets, /* regsets */
- 0, /* num_regsets */
- NULL, /* disabled_regsets */
- };
-
-static struct regs_info regs_info =
- {
- NULL, /* regset_bitmap */
- NULL, /* usrregs */
- &xtensa_regsets_info
- };
-
-static void
-xtensa_arch_setup (void)
-{
- current_process ()->tdesc = tdesc_xtensa;
-}
-
-/* Support for hardware single step. */
-
-static int
-xtensa_supports_hardware_single_step (void)
-{
- return 1;
-}
-
-static const struct regs_info *
-xtensa_regs_info (void)
-{
- return ®s_info;
-}
-
-struct linux_target_ops the_low_target = {
- xtensa_arch_setup,
- xtensa_regs_info,
- 0,
- 0,
- NULL, /* fetch_register */
- linux_get_pc_32bit,
- linux_set_pc_32bit,
- NULL, /* breakpoint_kind_from_pc */
- xtensa_sw_breakpoint_from_kind,
- NULL,
- 0,
- xtensa_breakpoint_at,
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* collect_ptrace_register */
- NULL, /* supply_ptrace_register */
- NULL, /* siginfo_fixup */
- NULL, /* new_process */
- NULL, /* delete_process */
- NULL, /* new_thread */
- NULL, /* delete_thread */
- NULL, /* new_fork */
- NULL, /* prepare_to_resume */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* supports_range_stepping */
- NULL, /* breakpoint_kind_from_current_state */
- xtensa_supports_hardware_single_step,
-};
-
-
-void
-initialize_low_arch (void)
-{
- /* Initialize the Linux target descriptions. */
- init_registers_xtensa ();
-
- initialize_regsets_info (&xtensa_regsets_info);
-}
--- /dev/null
+/* GNU/Linux/Xtensa specific low level interface, for the remote server for GDB.
+ Copyright (C) 2007-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#include "server.h"
+#include "linux-low.h"
+
+/* Defined in auto-generated file reg-xtensa.c. */
+void init_registers_xtensa (void);
+extern const struct target_desc *tdesc_xtensa;
+
+#include <asm/ptrace.h>
+#include <xtensa-config.h>
+#include "arch/xtensa.h"
+#include "gdb_proc_service.h"
+
+#include "xtensa-xtregs.c"
+
+enum regnum {
+ R_PC=0, R_PS,
+ R_LBEG, R_LEND, R_LCOUNT,
+ R_SAR,
+ R_WS, R_WB,
+ R_THREADPTR,
+ R_A0 = 64
+};
+
+static void
+xtensa_fill_gregset (struct regcache *regcache, void *buf)
+{
+ elf_greg_t* rset = (elf_greg_t*)buf;
+ const struct target_desc *tdesc = regcache->tdesc;
+ int ar0_regnum;
+ char *ptr;
+ int i;
+
+ /* Take care of AR registers. */
+
+ ar0_regnum = find_regno (tdesc, "ar0");
+ ptr = (char*)&rset[R_A0];
+
+ for (i = ar0_regnum; i < ar0_regnum + XCHAL_NUM_AREGS; i++)
+ {
+ collect_register (regcache, i, ptr);
+ ptr += register_size (tdesc, i);
+ }
+
+ if (XSHAL_ABI == XTHAL_ABI_CALL0)
+ {
+ int a0_regnum = find_regno (tdesc, "a0");
+ ptr = (char *) &rset[R_A0 + 4 * rset[R_WB]];
+
+ for (i = a0_regnum; i < a0_regnum + C0_NREGS; i++)
+ {
+ if ((4 * rset[R_WB] + i - a0_regnum) == XCHAL_NUM_AREGS)
+ ptr = (char *) &rset[R_A0];
+ collect_register (regcache, i, ptr);
+ ptr += register_size (tdesc, i);
+ }
+ }
+
+ /* Loop registers, if hardware has it. */
+
+#if XCHAL_HAVE_LOOPS
+ collect_register_by_name (regcache, "lbeg", (char*)&rset[R_LBEG]);
+ collect_register_by_name (regcache, "lend", (char*)&rset[R_LEND]);
+ collect_register_by_name (regcache, "lcount", (char*)&rset[R_LCOUNT]);
+#endif
+
+ collect_register_by_name (regcache, "sar", (char*)&rset[R_SAR]);
+ collect_register_by_name (regcache, "pc", (char*)&rset[R_PC]);
+ collect_register_by_name (regcache, "ps", (char*)&rset[R_PS]);
+ collect_register_by_name (regcache, "windowbase", (char*)&rset[R_WB]);
+ collect_register_by_name (regcache, "windowstart", (char*)&rset[R_WS]);
+
+#if XCHAL_HAVE_THREADPTR
+ collect_register_by_name (regcache, "threadptr",
+ (char *) &rset[R_THREADPTR]);
+#endif
+}
+
+static void
+xtensa_store_gregset (struct regcache *regcache, const void *buf)
+{
+ const elf_greg_t* rset = (const elf_greg_t*)buf;
+ const struct target_desc *tdesc = regcache->tdesc;
+ int ar0_regnum;
+ char *ptr;
+ int i;
+
+ /* Take care of AR registers. */
+
+ ar0_regnum = find_regno (tdesc, "ar0");
+ ptr = (char *)&rset[R_A0];
+
+ for (i = ar0_regnum; i < ar0_regnum + XCHAL_NUM_AREGS; i++)
+ {
+ supply_register (regcache, i, ptr);
+ ptr += register_size (tdesc, i);
+ }
+
+ if (XSHAL_ABI == XTHAL_ABI_CALL0)
+ {
+ int a0_regnum = find_regno (tdesc, "a0");
+ ptr = (char *) &rset[R_A0 + (4 * rset[R_WB]) % XCHAL_NUM_AREGS];
+
+ for (i = a0_regnum; i < a0_regnum + C0_NREGS; i++)
+ {
+ if ((4 * rset[R_WB] + i - a0_regnum) == XCHAL_NUM_AREGS)
+ ptr = (char *) &rset[R_A0];
+ supply_register (regcache, i, ptr);
+ ptr += register_size (tdesc, i);
+ }
+ }
+
+ /* Loop registers, if hardware has it. */
+
+#if XCHAL_HAVE_LOOPS
+ supply_register_by_name (regcache, "lbeg", (char*)&rset[R_LBEG]);
+ supply_register_by_name (regcache, "lend", (char*)&rset[R_LEND]);
+ supply_register_by_name (regcache, "lcount", (char*)&rset[R_LCOUNT]);
+#endif
+
+ supply_register_by_name (regcache, "sar", (char*)&rset[R_SAR]);
+ supply_register_by_name (regcache, "pc", (char*)&rset[R_PC]);
+ supply_register_by_name (regcache, "ps", (char*)&rset[R_PS]);
+ supply_register_by_name (regcache, "windowbase", (char*)&rset[R_WB]);
+ supply_register_by_name (regcache, "windowstart", (char*)&rset[R_WS]);
+
+#if XCHAL_HAVE_THREADPTR
+ supply_register_by_name (regcache, "threadptr",
+ (char *) &rset[R_THREADPTR]);
+#endif
+}
+
+/* Xtensa GNU/Linux PTRACE interface includes extended register set. */
+
+static void
+xtensa_fill_xtregset (struct regcache *regcache, void *buf)
+{
+ const xtensa_regtable_t *ptr;
+
+ for (ptr = xtensa_regmap_table; ptr->name; ptr++)
+ {
+ collect_register_by_name (regcache, ptr->name,
+ (char*)buf + ptr->ptrace_offset);
+ }
+}
+
+static void
+xtensa_store_xtregset (struct regcache *regcache, const void *buf)
+{
+ const xtensa_regtable_t *ptr;
+
+ for (ptr = xtensa_regmap_table; ptr->name; ptr++)
+ {
+ supply_register_by_name (regcache, ptr->name,
+ (char*)buf + ptr->ptrace_offset);
+ }
+}
+
+static struct regset_info xtensa_regsets[] = {
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
+ GENERAL_REGS,
+ xtensa_fill_gregset, xtensa_store_gregset },
+ { PTRACE_GETXTREGS, PTRACE_SETXTREGS, 0, XTENSA_ELF_XTREG_SIZE,
+ EXTENDED_REGS,
+ xtensa_fill_xtregset, xtensa_store_xtregset },
+ NULL_REGSET
+};
+
+#if XCHAL_HAVE_BE
+#define XTENSA_BREAKPOINT {0xd2,0x0f}
+#else
+#define XTENSA_BREAKPOINT {0x2d,0xf0}
+#endif
+
+static const gdb_byte xtensa_breakpoint[] = XTENSA_BREAKPOINT;
+#define xtensa_breakpoint_len 2
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+xtensa_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = xtensa_breakpoint_len;
+ return xtensa_breakpoint;
+}
+
+static int
+xtensa_breakpoint_at (CORE_ADDR where)
+{
+ unsigned long insn;
+
+ (*the_target->read_memory) (where, (unsigned char *) &insn,
+ xtensa_breakpoint_len);
+ return memcmp((char *) &insn,
+ xtensa_breakpoint, xtensa_breakpoint_len) == 0;
+}
+
+/* Called by libthread_db. */
+
+ps_err_e
+ps_get_thread_area (struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ xtensa_elf_gregset_t regs;
+
+ if (ptrace (PTRACE_GETREGS, lwpid, NULL, ®s) != 0)
+ return PS_ERR;
+
+ /* IDX is the bias from the thread pointer to the beginning of the
+ thread descriptor. It has to be subtracted due to implementation
+ quirks in libthread_db. */
+ *base = (void *) ((char *) regs.threadptr - idx);
+
+ return PS_OK;
+}
+
+static struct regsets_info xtensa_regsets_info =
+ {
+ xtensa_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ NULL, /* usrregs */
+ &xtensa_regsets_info
+ };
+
+static void
+xtensa_arch_setup (void)
+{
+ current_process ()->tdesc = tdesc_xtensa;
+}
+
+/* Support for hardware single step. */
+
+static int
+xtensa_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
+static const struct regs_info *
+xtensa_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ xtensa_arch_setup,
+ xtensa_regs_info,
+ 0,
+ 0,
+ NULL, /* fetch_register */
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
+ NULL, /* breakpoint_kind_from_pc */
+ xtensa_sw_breakpoint_from_kind,
+ NULL,
+ 0,
+ xtensa_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ xtensa_supports_hardware_single_step,
+};
+
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+ init_registers_xtensa ();
+
+ initialize_regsets_info (&xtensa_regsets_info);
+}
+++ /dev/null
-/* Copyright (C) 2010-2020 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 "lynx-low.h"
-#include <limits.h>
-#include <sys/ptrace.h>
-#include "gdbsupport/x86-xstate.h"
-#include "arch/i386.h"
-#include "x86-tdesc.h"
-
-/* The following two typedefs are defined in a .h file which is not
- in the standard include path (/sys/include/family/x86/ucontext.h),
- so we just duplicate them here.
-
- Unfortunately for us, the definition of this structure differs between
- LynxOS 5.x and LynxOS 178. Rather than duplicate the code, we use
- different definitions depending on the target. */
-
-#ifdef VMOS_DEV
-#define LYNXOS_178
-#endif
-
-/* General register context */
-typedef struct usr_econtext {
-
- uint32_t uec_fault;
- uint32_t uec_es;
- uint32_t uec_ds;
- uint32_t uec_edi;
- uint32_t uec_esi;
- uint32_t uec_ebp;
- uint32_t uec_temp;
- uint32_t uec_ebx;
- uint32_t uec_edx;
- uint32_t uec_ecx;
- uint32_t uec_eax;
- uint32_t uec_inum;
- uint32_t uec_ecode;
- uint32_t uec_eip;
- uint32_t uec_cs;
- uint32_t uec_eflags;
- uint32_t uec_esp;
- uint32_t uec_ss;
- uint32_t uec_fs;
- uint32_t uec_gs;
-} usr_econtext_t;
-
-#if defined(LYNXOS_178)
-
-/* Floating point register context */
-typedef struct usr_fcontext {
- uint32_t ufc_control;
- uint32_t ufc_status;
- uint32_t ufc_tag;
- uint8_t *ufc_inst_off;
- uint32_t ufc_inst_sel;
- uint8_t *ufc_data_off;
- uint32_t ufc_data_sel;
- struct ufp387_real {
- uint16_t umant4;
- uint16_t umant3;
- uint16_t umant2;
- uint16_t umant1;
- uint16_t us_and_e;
- } ufc_reg[8];
-} usr_fcontext_t;
-
-#else /* This is LynxOS 5.x. */
-
-/* Floating point and SIMD register context */
-typedef struct usr_fcontext {
- uint16_t ufc_control;
- uint16_t ufc_status;
- uint16_t ufc_tag;
- uint16_t ufc_opcode;
- uint8_t *ufc_inst_off;
- uint32_t ufc_inst_sel;
- uint8_t *ufc_data_off;
- uint32_t ufc_data_sel;
- uint32_t usse_mxcsr;
- uint32_t usse_mxcsr_mask;
- struct ufp387_real {
- uint16_t umant4;
- uint16_t umant3;
- uint16_t umant2;
- uint16_t umant1;
- uint16_t us_and_e;
- uint16_t ureserved_1;
- uint16_t ureserved_2;
- uint16_t ureserved_3;
- } ufc_reg[8];
- struct uxmm_register {
- uint16_t uchunk_1;
- uint16_t uchunk_2;
- uint16_t uchunk_3;
- uint16_t uchunk_4;
- uint16_t uchunk_5;
- uint16_t uchunk_6;
- uint16_t uchunk_7;
- uint16_t uchunk_8;
- } uxmm_reg[8];
- char ureserved[16][14];
-} usr_fcontext_t;
-
-#endif
-
-/* The index of various registers inside the regcache. */
-
-enum lynx_i386_gdb_regnum
-{
- I386_EAX_REGNUM,
- I386_ECX_REGNUM,
- I386_EDX_REGNUM,
- I386_EBX_REGNUM,
- I386_ESP_REGNUM,
- I386_EBP_REGNUM,
- I386_ESI_REGNUM,
- I386_EDI_REGNUM,
- I386_EIP_REGNUM,
- I386_EFLAGS_REGNUM,
- I386_CS_REGNUM,
- I386_SS_REGNUM,
- I386_DS_REGNUM,
- I386_ES_REGNUM,
- I386_FS_REGNUM,
- I386_GS_REGNUM,
- I386_ST0_REGNUM,
- I386_FCTRL_REGNUM = I386_ST0_REGNUM + 8,
- I386_FSTAT_REGNUM,
- I386_FTAG_REGNUM,
- I386_FISEG_REGNUM,
- I386_FIOFF_REGNUM,
- I386_FOSEG_REGNUM,
- I386_FOOFF_REGNUM,
- I386_FOP_REGNUM,
- I386_XMM0_REGNUM = 32,
- I386_MXCSR_REGNUM = I386_XMM0_REGNUM + 8,
- I386_SENTINEL_REGUM
-};
-
-/* The fill_function for the general-purpose register set. */
-
-static void
-lynx_i386_fill_gregset (struct regcache *regcache, char *buf)
-{
-#define lynx_i386_collect_gp(regnum, fld) \
- collect_register (regcache, regnum, \
- buf + offsetof (usr_econtext_t, uec_##fld))
-
- lynx_i386_collect_gp (I386_EAX_REGNUM, eax);
- lynx_i386_collect_gp (I386_ECX_REGNUM, ecx);
- lynx_i386_collect_gp (I386_EDX_REGNUM, edx);
- lynx_i386_collect_gp (I386_EBX_REGNUM, ebx);
- lynx_i386_collect_gp (I386_ESP_REGNUM, esp);
- lynx_i386_collect_gp (I386_EBP_REGNUM, ebp);
- lynx_i386_collect_gp (I386_ESI_REGNUM, esi);
- lynx_i386_collect_gp (I386_EDI_REGNUM, edi);
- lynx_i386_collect_gp (I386_EIP_REGNUM, eip);
- lynx_i386_collect_gp (I386_EFLAGS_REGNUM, eflags);
- lynx_i386_collect_gp (I386_CS_REGNUM, cs);
- lynx_i386_collect_gp (I386_SS_REGNUM, ss);
- lynx_i386_collect_gp (I386_DS_REGNUM, ds);
- lynx_i386_collect_gp (I386_ES_REGNUM, es);
- lynx_i386_collect_gp (I386_FS_REGNUM, fs);
- lynx_i386_collect_gp (I386_GS_REGNUM, gs);
-}
-
-/* The store_function for the general-purpose register set. */
-
-static void
-lynx_i386_store_gregset (struct regcache *regcache, const char *buf)
-{
-#define lynx_i386_supply_gp(regnum, fld) \
- supply_register (regcache, regnum, \
- buf + offsetof (usr_econtext_t, uec_##fld))
-
- lynx_i386_supply_gp (I386_EAX_REGNUM, eax);
- lynx_i386_supply_gp (I386_ECX_REGNUM, ecx);
- lynx_i386_supply_gp (I386_EDX_REGNUM, edx);
- lynx_i386_supply_gp (I386_EBX_REGNUM, ebx);
- lynx_i386_supply_gp (I386_ESP_REGNUM, esp);
- lynx_i386_supply_gp (I386_EBP_REGNUM, ebp);
- lynx_i386_supply_gp (I386_ESI_REGNUM, esi);
- lynx_i386_supply_gp (I386_EDI_REGNUM, edi);
- lynx_i386_supply_gp (I386_EIP_REGNUM, eip);
- lynx_i386_supply_gp (I386_EFLAGS_REGNUM, eflags);
- lynx_i386_supply_gp (I386_CS_REGNUM, cs);
- lynx_i386_supply_gp (I386_SS_REGNUM, ss);
- lynx_i386_supply_gp (I386_DS_REGNUM, ds);
- lynx_i386_supply_gp (I386_ES_REGNUM, es);
- lynx_i386_supply_gp (I386_FS_REGNUM, fs);
- lynx_i386_supply_gp (I386_GS_REGNUM, gs);
-}
-
-/* Extract the first 16 bits of register REGNUM in the REGCACHE,
- and store these 2 bytes at DEST.
-
- This is useful to collect certain 16bit registers which are known
- by GDBserver as 32bit registers (such as the Control Register
- for instance). */
-
-static void
-collect_16bit_register (struct regcache *regcache, int regnum, char *dest)
-{
- gdb_byte word[4];
-
- collect_register (regcache, regnum, word);
- memcpy (dest, word, 2);
-}
-
-/* The fill_function for the floating-point register set. */
-
-static void
-lynx_i386_fill_fpregset (struct regcache *regcache, char *buf)
-{
- int i;
-
- /* Collect %st0 .. %st7. */
- for (i = 0; i < 8; i++)
- collect_register (regcache, I386_ST0_REGNUM + i,
- buf + offsetof (usr_fcontext_t, ufc_reg)
- + i * sizeof (struct ufp387_real));
-
- /* Collect the other FPU registers. */
- collect_16bit_register (regcache, I386_FCTRL_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_control));
- collect_16bit_register (regcache, I386_FSTAT_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_status));
- collect_16bit_register (regcache, I386_FTAG_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_tag));
- collect_register (regcache, I386_FISEG_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_inst_sel));
- collect_register (regcache, I386_FIOFF_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_inst_off));
- collect_register (regcache, I386_FOSEG_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_data_sel));
- collect_register (regcache, I386_FOOFF_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_data_off));
-#if !defined(LYNXOS_178)
- collect_16bit_register (regcache, I386_FOP_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_opcode));
-
- /* Collect the XMM registers. */
- for (i = 0; i < 8; i++)
- collect_register (regcache, I386_XMM0_REGNUM + i,
- buf + offsetof (usr_fcontext_t, uxmm_reg)
- + i * sizeof (struct uxmm_register));
- collect_register (regcache, I386_MXCSR_REGNUM,
- buf + offsetof (usr_fcontext_t, usse_mxcsr));
-#endif
-}
-
-/* This is the supply counterpart for collect_16bit_register:
- It extracts a 2byte value from BUF, and uses that value to
- set REGNUM's value in the regcache.
-
- This is useful to supply the value of certain 16bit registers
- which are known by GDBserver as 32bit registers (such as the Control
- Register for instance). */
-
-static void
-supply_16bit_register (struct regcache *regcache, int regnum, const char *buf)
-{
- gdb_byte word[4];
-
- memcpy (word, buf, 2);
- memset (word + 2, 0, 2);
- supply_register (regcache, regnum, word);
-}
-
-/* The store_function for the floating-point register set. */
-
-static void
-lynx_i386_store_fpregset (struct regcache *regcache, const char *buf)
-{
- int i;
-
- /* Store the %st0 .. %st7 registers. */
- for (i = 0; i < 8; i++)
- supply_register (regcache, I386_ST0_REGNUM + i,
- buf + offsetof (usr_fcontext_t, ufc_reg)
- + i * sizeof (struct ufp387_real));
-
- /* Store the other FPU registers. */
- supply_16bit_register (regcache, I386_FCTRL_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_control));
- supply_16bit_register (regcache, I386_FSTAT_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_status));
- supply_16bit_register (regcache, I386_FTAG_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_tag));
- supply_register (regcache, I386_FISEG_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_inst_sel));
- supply_register (regcache, I386_FIOFF_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_inst_off));
- supply_register (regcache, I386_FOSEG_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_data_sel));
- supply_register (regcache, I386_FOOFF_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_data_off));
-#if !defined(LYNXOS_178)
- supply_16bit_register (regcache, I386_FOP_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_opcode));
-
- /* Store the XMM registers. */
- for (i = 0; i < 8; i++)
- supply_register (regcache, I386_XMM0_REGNUM + i,
- buf + offsetof (usr_fcontext_t, uxmm_reg)
- + i * sizeof (struct uxmm_register));
- supply_register (regcache, I386_MXCSR_REGNUM,
- buf + offsetof (usr_fcontext_t, usse_mxcsr));
-#endif
-}
-
-/* Implements the lynx_target_ops.arch_setup routine. */
-
-static void
-lynx_i386_arch_setup (void)
-{
- struct target_desc *tdesc
- = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
-
- init_target_desc (tdesc, i386_expedite_regs);
-
- lynx_tdesc = tdesc;
-}
-
-/* Description of all the x86-lynx register sets. */
-
-struct lynx_regset_info lynx_target_regsets[] = {
- /* General Purpose Registers. */
- {PTRACE_GETREGS, PTRACE_SETREGS, sizeof(usr_econtext_t),
- lynx_i386_fill_gregset, lynx_i386_store_gregset},
- /* Floating Point Registers. */
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof(usr_fcontext_t),
- lynx_i386_fill_fpregset, lynx_i386_store_fpregset },
- /* End of list marker. */
- {0, 0, -1, NULL, NULL }
-};
-
-/* The lynx_target_ops vector for x86-lynx. */
-
-struct lynx_target_ops the_low_target = {
- lynx_i386_arch_setup,
-};
--- /dev/null
+/* Copyright (C) 2010-2020 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 "lynx-low.h"
+#include <limits.h>
+#include <sys/ptrace.h>
+#include "gdbsupport/x86-xstate.h"
+#include "arch/i386.h"
+#include "x86-tdesc.h"
+
+/* The following two typedefs are defined in a .h file which is not
+ in the standard include path (/sys/include/family/x86/ucontext.h),
+ so we just duplicate them here.
+
+ Unfortunately for us, the definition of this structure differs between
+ LynxOS 5.x and LynxOS 178. Rather than duplicate the code, we use
+ different definitions depending on the target. */
+
+#ifdef VMOS_DEV
+#define LYNXOS_178
+#endif
+
+/* General register context */
+typedef struct usr_econtext {
+
+ uint32_t uec_fault;
+ uint32_t uec_es;
+ uint32_t uec_ds;
+ uint32_t uec_edi;
+ uint32_t uec_esi;
+ uint32_t uec_ebp;
+ uint32_t uec_temp;
+ uint32_t uec_ebx;
+ uint32_t uec_edx;
+ uint32_t uec_ecx;
+ uint32_t uec_eax;
+ uint32_t uec_inum;
+ uint32_t uec_ecode;
+ uint32_t uec_eip;
+ uint32_t uec_cs;
+ uint32_t uec_eflags;
+ uint32_t uec_esp;
+ uint32_t uec_ss;
+ uint32_t uec_fs;
+ uint32_t uec_gs;
+} usr_econtext_t;
+
+#if defined(LYNXOS_178)
+
+/* Floating point register context */
+typedef struct usr_fcontext {
+ uint32_t ufc_control;
+ uint32_t ufc_status;
+ uint32_t ufc_tag;
+ uint8_t *ufc_inst_off;
+ uint32_t ufc_inst_sel;
+ uint8_t *ufc_data_off;
+ uint32_t ufc_data_sel;
+ struct ufp387_real {
+ uint16_t umant4;
+ uint16_t umant3;
+ uint16_t umant2;
+ uint16_t umant1;
+ uint16_t us_and_e;
+ } ufc_reg[8];
+} usr_fcontext_t;
+
+#else /* This is LynxOS 5.x. */
+
+/* Floating point and SIMD register context */
+typedef struct usr_fcontext {
+ uint16_t ufc_control;
+ uint16_t ufc_status;
+ uint16_t ufc_tag;
+ uint16_t ufc_opcode;
+ uint8_t *ufc_inst_off;
+ uint32_t ufc_inst_sel;
+ uint8_t *ufc_data_off;
+ uint32_t ufc_data_sel;
+ uint32_t usse_mxcsr;
+ uint32_t usse_mxcsr_mask;
+ struct ufp387_real {
+ uint16_t umant4;
+ uint16_t umant3;
+ uint16_t umant2;
+ uint16_t umant1;
+ uint16_t us_and_e;
+ uint16_t ureserved_1;
+ uint16_t ureserved_2;
+ uint16_t ureserved_3;
+ } ufc_reg[8];
+ struct uxmm_register {
+ uint16_t uchunk_1;
+ uint16_t uchunk_2;
+ uint16_t uchunk_3;
+ uint16_t uchunk_4;
+ uint16_t uchunk_5;
+ uint16_t uchunk_6;
+ uint16_t uchunk_7;
+ uint16_t uchunk_8;
+ } uxmm_reg[8];
+ char ureserved[16][14];
+} usr_fcontext_t;
+
+#endif
+
+/* The index of various registers inside the regcache. */
+
+enum lynx_i386_gdb_regnum
+{
+ I386_EAX_REGNUM,
+ I386_ECX_REGNUM,
+ I386_EDX_REGNUM,
+ I386_EBX_REGNUM,
+ I386_ESP_REGNUM,
+ I386_EBP_REGNUM,
+ I386_ESI_REGNUM,
+ I386_EDI_REGNUM,
+ I386_EIP_REGNUM,
+ I386_EFLAGS_REGNUM,
+ I386_CS_REGNUM,
+ I386_SS_REGNUM,
+ I386_DS_REGNUM,
+ I386_ES_REGNUM,
+ I386_FS_REGNUM,
+ I386_GS_REGNUM,
+ I386_ST0_REGNUM,
+ I386_FCTRL_REGNUM = I386_ST0_REGNUM + 8,
+ I386_FSTAT_REGNUM,
+ I386_FTAG_REGNUM,
+ I386_FISEG_REGNUM,
+ I386_FIOFF_REGNUM,
+ I386_FOSEG_REGNUM,
+ I386_FOOFF_REGNUM,
+ I386_FOP_REGNUM,
+ I386_XMM0_REGNUM = 32,
+ I386_MXCSR_REGNUM = I386_XMM0_REGNUM + 8,
+ I386_SENTINEL_REGUM
+};
+
+/* The fill_function for the general-purpose register set. */
+
+static void
+lynx_i386_fill_gregset (struct regcache *regcache, char *buf)
+{
+#define lynx_i386_collect_gp(regnum, fld) \
+ collect_register (regcache, regnum, \
+ buf + offsetof (usr_econtext_t, uec_##fld))
+
+ lynx_i386_collect_gp (I386_EAX_REGNUM, eax);
+ lynx_i386_collect_gp (I386_ECX_REGNUM, ecx);
+ lynx_i386_collect_gp (I386_EDX_REGNUM, edx);
+ lynx_i386_collect_gp (I386_EBX_REGNUM, ebx);
+ lynx_i386_collect_gp (I386_ESP_REGNUM, esp);
+ lynx_i386_collect_gp (I386_EBP_REGNUM, ebp);
+ lynx_i386_collect_gp (I386_ESI_REGNUM, esi);
+ lynx_i386_collect_gp (I386_EDI_REGNUM, edi);
+ lynx_i386_collect_gp (I386_EIP_REGNUM, eip);
+ lynx_i386_collect_gp (I386_EFLAGS_REGNUM, eflags);
+ lynx_i386_collect_gp (I386_CS_REGNUM, cs);
+ lynx_i386_collect_gp (I386_SS_REGNUM, ss);
+ lynx_i386_collect_gp (I386_DS_REGNUM, ds);
+ lynx_i386_collect_gp (I386_ES_REGNUM, es);
+ lynx_i386_collect_gp (I386_FS_REGNUM, fs);
+ lynx_i386_collect_gp (I386_GS_REGNUM, gs);
+}
+
+/* The store_function for the general-purpose register set. */
+
+static void
+lynx_i386_store_gregset (struct regcache *regcache, const char *buf)
+{
+#define lynx_i386_supply_gp(regnum, fld) \
+ supply_register (regcache, regnum, \
+ buf + offsetof (usr_econtext_t, uec_##fld))
+
+ lynx_i386_supply_gp (I386_EAX_REGNUM, eax);
+ lynx_i386_supply_gp (I386_ECX_REGNUM, ecx);
+ lynx_i386_supply_gp (I386_EDX_REGNUM, edx);
+ lynx_i386_supply_gp (I386_EBX_REGNUM, ebx);
+ lynx_i386_supply_gp (I386_ESP_REGNUM, esp);
+ lynx_i386_supply_gp (I386_EBP_REGNUM, ebp);
+ lynx_i386_supply_gp (I386_ESI_REGNUM, esi);
+ lynx_i386_supply_gp (I386_EDI_REGNUM, edi);
+ lynx_i386_supply_gp (I386_EIP_REGNUM, eip);
+ lynx_i386_supply_gp (I386_EFLAGS_REGNUM, eflags);
+ lynx_i386_supply_gp (I386_CS_REGNUM, cs);
+ lynx_i386_supply_gp (I386_SS_REGNUM, ss);
+ lynx_i386_supply_gp (I386_DS_REGNUM, ds);
+ lynx_i386_supply_gp (I386_ES_REGNUM, es);
+ lynx_i386_supply_gp (I386_FS_REGNUM, fs);
+ lynx_i386_supply_gp (I386_GS_REGNUM, gs);
+}
+
+/* Extract the first 16 bits of register REGNUM in the REGCACHE,
+ and store these 2 bytes at DEST.
+
+ This is useful to collect certain 16bit registers which are known
+ by GDBserver as 32bit registers (such as the Control Register
+ for instance). */
+
+static void
+collect_16bit_register (struct regcache *regcache, int regnum, char *dest)
+{
+ gdb_byte word[4];
+
+ collect_register (regcache, regnum, word);
+ memcpy (dest, word, 2);
+}
+
+/* The fill_function for the floating-point register set. */
+
+static void
+lynx_i386_fill_fpregset (struct regcache *regcache, char *buf)
+{
+ int i;
+
+ /* Collect %st0 .. %st7. */
+ for (i = 0; i < 8; i++)
+ collect_register (regcache, I386_ST0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, ufc_reg)
+ + i * sizeof (struct ufp387_real));
+
+ /* Collect the other FPU registers. */
+ collect_16bit_register (regcache, I386_FCTRL_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_control));
+ collect_16bit_register (regcache, I386_FSTAT_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_status));
+ collect_16bit_register (regcache, I386_FTAG_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_tag));
+ collect_register (regcache, I386_FISEG_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_inst_sel));
+ collect_register (regcache, I386_FIOFF_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_inst_off));
+ collect_register (regcache, I386_FOSEG_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_data_sel));
+ collect_register (regcache, I386_FOOFF_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_data_off));
+#if !defined(LYNXOS_178)
+ collect_16bit_register (regcache, I386_FOP_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_opcode));
+
+ /* Collect the XMM registers. */
+ for (i = 0; i < 8; i++)
+ collect_register (regcache, I386_XMM0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, uxmm_reg)
+ + i * sizeof (struct uxmm_register));
+ collect_register (regcache, I386_MXCSR_REGNUM,
+ buf + offsetof (usr_fcontext_t, usse_mxcsr));
+#endif
+}
+
+/* This is the supply counterpart for collect_16bit_register:
+ It extracts a 2byte value from BUF, and uses that value to
+ set REGNUM's value in the regcache.
+
+ This is useful to supply the value of certain 16bit registers
+ which are known by GDBserver as 32bit registers (such as the Control
+ Register for instance). */
+
+static void
+supply_16bit_register (struct regcache *regcache, int regnum, const char *buf)
+{
+ gdb_byte word[4];
+
+ memcpy (word, buf, 2);
+ memset (word + 2, 0, 2);
+ supply_register (regcache, regnum, word);
+}
+
+/* The store_function for the floating-point register set. */
+
+static void
+lynx_i386_store_fpregset (struct regcache *regcache, const char *buf)
+{
+ int i;
+
+ /* Store the %st0 .. %st7 registers. */
+ for (i = 0; i < 8; i++)
+ supply_register (regcache, I386_ST0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, ufc_reg)
+ + i * sizeof (struct ufp387_real));
+
+ /* Store the other FPU registers. */
+ supply_16bit_register (regcache, I386_FCTRL_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_control));
+ supply_16bit_register (regcache, I386_FSTAT_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_status));
+ supply_16bit_register (regcache, I386_FTAG_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_tag));
+ supply_register (regcache, I386_FISEG_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_inst_sel));
+ supply_register (regcache, I386_FIOFF_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_inst_off));
+ supply_register (regcache, I386_FOSEG_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_data_sel));
+ supply_register (regcache, I386_FOOFF_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_data_off));
+#if !defined(LYNXOS_178)
+ supply_16bit_register (regcache, I386_FOP_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_opcode));
+
+ /* Store the XMM registers. */
+ for (i = 0; i < 8; i++)
+ supply_register (regcache, I386_XMM0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, uxmm_reg)
+ + i * sizeof (struct uxmm_register));
+ supply_register (regcache, I386_MXCSR_REGNUM,
+ buf + offsetof (usr_fcontext_t, usse_mxcsr));
+#endif
+}
+
+/* Implements the lynx_target_ops.arch_setup routine. */
+
+static void
+lynx_i386_arch_setup (void)
+{
+ struct target_desc *tdesc
+ = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
+
+ init_target_desc (tdesc, i386_expedite_regs);
+
+ lynx_tdesc = tdesc;
+}
+
+/* Description of all the x86-lynx register sets. */
+
+struct lynx_regset_info lynx_target_regsets[] = {
+ /* General Purpose Registers. */
+ {PTRACE_GETREGS, PTRACE_SETREGS, sizeof(usr_econtext_t),
+ lynx_i386_fill_gregset, lynx_i386_store_gregset},
+ /* Floating Point Registers. */
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof(usr_fcontext_t),
+ lynx_i386_fill_fpregset, lynx_i386_store_fpregset },
+ /* End of list marker. */
+ {0, 0, -1, NULL, NULL }
+};
+
+/* The lynx_target_ops vector for x86-lynx. */
+
+struct lynx_target_ops the_low_target = {
+ lynx_i386_arch_setup,
+};
+++ /dev/null
-/* Copyright (C) 2009-2020 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 "target.h"
-#include "lynx-low.h"
-
-#include <limits.h>
-#include <sys/ptrace.h>
-#include <sys/piddef.h> /* Provides PIDGET, TIDGET, BUILDPID, etc. */
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include "gdbsupport/gdb_wait.h"
-#include <signal.h>
-#include "gdbsupport/filestuff.h"
-#include "gdbsupport/common-inferior.h"
-#include "nat/fork-inferior.h"
-
-int using_threads = 1;
-
-const struct target_desc *lynx_tdesc;
-
-/* Per-process private data. */
-
-struct process_info_private
-{
- /* The PTID obtained from the last wait performed on this process.
- Initialized to null_ptid until the first wait is performed. */
- ptid_t last_wait_event_ptid;
-};
-
-/* Print a debug trace on standard output if debug_threads is set. */
-
-static void
-lynx_debug (char *string, ...)
-{
- va_list args;
-
- if (!debug_threads)
- return;
-
- va_start (args, string);
- fprintf (stderr, "DEBUG(lynx): ");
- vfprintf (stderr, string, args);
- fprintf (stderr, "\n");
- va_end (args);
-}
-
-/* Build a ptid_t given a PID and a LynxOS TID. */
-
-static ptid_t
-lynx_ptid_t (int pid, long tid)
-{
- /* brobecker/2010-06-21: It looks like the LWP field in ptids
- should be distinct for each thread (see write_ptid where it
- writes the thread ID from the LWP). So instead of storing
- the LynxOS tid in the tid field of the ptid, we store it in
- the lwp field. */
- return ptid_t (pid, tid, 0);
-}
-
-/* Return the process ID of the given PTID.
-
- This function has little reason to exist, it's just a wrapper around
- ptid_get_pid. But since we have a getter function for the lynxos
- ptid, it feels cleaner to have a getter for the pid as well. */
-
-static int
-lynx_ptid_get_pid (ptid_t ptid)
-{
- return ptid.pid ();
-}
-
-/* Return the LynxOS tid of the given PTID. */
-
-static long
-lynx_ptid_get_tid (ptid_t ptid)
-{
- /* See lynx_ptid_t: The LynxOS tid is stored inside the lwp field
- of the ptid. */
- return ptid.lwp ();
-}
-
-/* For a given PTID, return the associated PID as known by the LynxOS
- ptrace layer. */
-
-static int
-lynx_ptrace_pid_from_ptid (ptid_t ptid)
-{
- return BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
-}
-
-/* Return a string image of the ptrace REQUEST number. */
-
-static char *
-ptrace_request_to_str (int request)
-{
-#define CASE(X) case X: return #X
- switch (request)
- {
- CASE(PTRACE_TRACEME);
- CASE(PTRACE_PEEKTEXT);
- CASE(PTRACE_PEEKDATA);
- CASE(PTRACE_PEEKUSER);
- CASE(PTRACE_POKETEXT);
- CASE(PTRACE_POKEDATA);
- CASE(PTRACE_POKEUSER);
- CASE(PTRACE_CONT);
- CASE(PTRACE_KILL);
- CASE(PTRACE_SINGLESTEP);
- CASE(PTRACE_ATTACH);
- CASE(PTRACE_DETACH);
- CASE(PTRACE_GETREGS);
- CASE(PTRACE_SETREGS);
- CASE(PTRACE_GETFPREGS);
- CASE(PTRACE_SETFPREGS);
- CASE(PTRACE_READDATA);
- CASE(PTRACE_WRITEDATA);
- CASE(PTRACE_READTEXT);
- CASE(PTRACE_WRITETEXT);
- CASE(PTRACE_GETFPAREGS);
- CASE(PTRACE_SETFPAREGS);
- CASE(PTRACE_GETWINDOW);
- CASE(PTRACE_SETWINDOW);
- CASE(PTRACE_SYSCALL);
- CASE(PTRACE_DUMPCORE);
- CASE(PTRACE_SETWRBKPT);
- CASE(PTRACE_SETACBKPT);
- CASE(PTRACE_CLRBKPT);
- CASE(PTRACE_GET_UCODE);
-#ifdef PT_READ_GPR
- CASE(PT_READ_GPR);
-#endif
-#ifdef PT_WRITE_GPR
- CASE(PT_WRITE_GPR);
-#endif
-#ifdef PT_READ_FPR
- CASE(PT_READ_FPR);
-#endif
-#ifdef PT_WRITE_FPR
- CASE(PT_WRITE_FPR);
-#endif
-#ifdef PT_READ_VPR
- CASE(PT_READ_VPR);
-#endif
-#ifdef PT_WRITE_VPR
- CASE(PT_WRITE_VPR);
-#endif
-#ifdef PTRACE_PEEKUSP
- CASE(PTRACE_PEEKUSP);
-#endif
-#ifdef PTRACE_POKEUSP
- CASE(PTRACE_POKEUSP);
-#endif
- CASE(PTRACE_PEEKTHREAD);
- CASE(PTRACE_THREADUSER);
- CASE(PTRACE_FPREAD);
- CASE(PTRACE_FPWRITE);
- CASE(PTRACE_SETSIG);
- CASE(PTRACE_CONT_ONE);
- CASE(PTRACE_KILL_ONE);
- CASE(PTRACE_SINGLESTEP_ONE);
- CASE(PTRACE_GETLOADINFO);
- CASE(PTRACE_GETTRACESIG);
-#ifdef PTRACE_GETTHREADLIST
- CASE(PTRACE_GETTHREADLIST);
-#endif
- }
-#undef CASE
-
- return "<unknown-request>";
-}
-
-/* A wrapper around ptrace that allows us to print debug traces of
- ptrace calls if debug traces are activated. */
-
-static int
-lynx_ptrace (int request, ptid_t ptid, int addr, int data, int addr2)
-{
- int result;
- const int pid = lynx_ptrace_pid_from_ptid (ptid);
- int saved_errno;
-
- if (debug_threads)
- fprintf (stderr, "PTRACE (%s, pid=%d(pid=%d, tid=%d), addr=0x%x, "
- "data=0x%x, addr2=0x%x)",
- ptrace_request_to_str (request), pid, PIDGET (pid), TIDGET (pid),
- addr, data, addr2);
- result = ptrace (request, pid, addr, data, addr2);
- saved_errno = errno;
- if (debug_threads)
- fprintf (stderr, " -> %d (=0x%x)\n", result, result);
-
- errno = saved_errno;
- return result;
-}
-
-/* Call add_process with the given parameters, and initializes
- the process' private data. */
-
-static struct process_info *
-lynx_add_process (int pid, int attached)
-{
- struct process_info *proc;
-
- proc = add_process (pid, attached);
- proc->tdesc = lynx_tdesc;
- proc->priv = XCNEW (struct process_info_private);
- proc->priv->last_wait_event_ptid = null_ptid;
-
- return proc;
-}
-
-/* Callback used by fork_inferior to start tracing the inferior. */
-
-static void
-lynx_ptrace_fun ()
-{
- int pgrp;
-
- /* Switch child to its own process group so that signals won't
- directly affect GDBserver. */
- pgrp = getpid();
- if (pgrp < 0)
- trace_start_error_with_name ("pgrp");
- if (setpgid (0, pgrp) < 0)
- trace_start_error_with_name ("setpgid");
- if (ioctl (0, TIOCSPGRP, &pgrp) < 0)
- trace_start_error_with_name ("ioctl");
- if (lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0) < 0)
- trace_start_error_with_name ("lynx_ptrace");
-}
-
-/* Implement the create_inferior method of the target_ops vector. */
-
-static int
-lynx_create_inferior (const char *program,
- const std::vector<char *> &program_args)
-{
- int pid;
- std::string str_program_args = stringify_argv (program_args);
-
- lynx_debug ("lynx_create_inferior ()");
-
- pid = fork_inferior (program,
- str_program_args.c_str (),
- get_environ ()->envp (), lynx_ptrace_fun,
- NULL, NULL, NULL, NULL);
-
- post_fork_inferior (pid, program);
-
- lynx_add_process (pid, 0);
- /* Do not add the process thread just yet, as we do not know its tid.
- We will add it later, during the wait for the STOP event corresponding
- to the lynx_ptrace (PTRACE_TRACEME) call above. */
- return pid;
-}
-
-/* Assuming we've just attached to a running inferior whose pid is PID,
- add all threads running in that process. */
-
-static void
-lynx_add_threads_after_attach (int pid)
-{
- /* Ugh! There appears to be no way to get the list of threads
- in the program we just attached to. So get the list by calling
- the "ps" command. This is only needed now, as we will then
- keep the thread list up to date thanks to thread creation and
- exit notifications. */
- FILE *f;
- char buf[256];
- int thread_pid, thread_tid;
-
- f = popen ("ps atx", "r");
- if (f == NULL)
- perror_with_name ("Cannot get thread list");
-
- while (fgets (buf, sizeof (buf), f) != NULL)
- if ((sscanf (buf, "%d %d", &thread_pid, &thread_tid) == 2
- && thread_pid == pid))
- {
- ptid_t thread_ptid = lynx_ptid_t (pid, thread_tid);
-
- if (!find_thread_ptid (thread_ptid))
- {
- lynx_debug ("New thread: (pid = %d, tid = %d)",
- pid, thread_tid);
- add_thread (thread_ptid, NULL);
- }
- }
-
- pclose (f);
-}
-
-/* Implement the attach target_ops method. */
-
-static int
-lynx_attach (unsigned long pid)
-{
- ptid_t ptid = lynx_ptid_t (pid, 0);
-
- if (lynx_ptrace (PTRACE_ATTACH, ptid, 0, 0, 0) != 0)
- error ("Cannot attach to process %lu: %s (%d)\n", pid,
- safe_strerror (errno), errno);
-
- lynx_add_process (pid, 1);
- lynx_add_threads_after_attach (pid);
-
- return 0;
-}
-
-/* Implement the resume target_ops method. */
-
-static void
-lynx_resume (struct thread_resume *resume_info, size_t n)
-{
- ptid_t ptid = resume_info[0].thread;
- const int request
- = (resume_info[0].kind == resume_step
- ? (n == 1 ? PTRACE_SINGLESTEP_ONE : PTRACE_SINGLESTEP)
- : PTRACE_CONT);
- const int signal = resume_info[0].sig;
-
- /* If given a minus_one_ptid, then try using the current_process'
- private->last_wait_event_ptid. On most LynxOS versions,
- using any of the process' thread works well enough, but
- LynxOS 178 is a little more sensitive, and triggers some
- unexpected signals (Eg SIG61) when we resume the inferior
- using a different thread. */
- if (ptid == minus_one_ptid)
- ptid = current_process()->priv->last_wait_event_ptid;
-
- /* The ptid might still be minus_one_ptid; this can happen between
- the moment we create the inferior or attach to a process, and
- the moment we resume its execution for the first time. It is
- fine to use the current_thread's ptid in those cases. */
- if (ptid == minus_one_ptid)
- ptid = ptid_of (current_thread);
-
- regcache_invalidate_pid (ptid.pid ());
-
- errno = 0;
- lynx_ptrace (request, ptid, 1, signal, 0);
- if (errno)
- perror_with_name ("ptrace");
-}
-
-/* Resume the execution of the given PTID. */
-
-static void
-lynx_continue (ptid_t ptid)
-{
- struct thread_resume resume_info;
-
- resume_info.thread = ptid;
- resume_info.kind = resume_continue;
- resume_info.sig = 0;
-
- lynx_resume (&resume_info, 1);
-}
-
-/* A wrapper around waitpid that handles the various idiosyncrasies
- of LynxOS' waitpid. */
-
-static int
-lynx_waitpid (int pid, int *stat_loc)
-{
- int ret = 0;
-
- while (1)
- {
- ret = waitpid (pid, stat_loc, WNOHANG);
- if (ret < 0)
- {
- /* An ECHILD error is not indicative of a real problem.
- It happens for instance while waiting for the inferior
- to stop after attaching to it. */
- if (errno != ECHILD)
- perror_with_name ("waitpid (WNOHANG)");
- }
- if (ret > 0)
- break;
- /* No event with WNOHANG. See if there is one with WUNTRACED. */
- ret = waitpid (pid, stat_loc, WNOHANG | WUNTRACED);
- if (ret < 0)
- {
- /* An ECHILD error is not indicative of a real problem.
- It happens for instance while waiting for the inferior
- to stop after attaching to it. */
- if (errno != ECHILD)
- perror_with_name ("waitpid (WNOHANG|WUNTRACED)");
- }
- if (ret > 0)
- break;
- usleep (1000);
- }
- return ret;
-}
-
-/* Implement the wait target_ops method. */
-
-static ptid_t
-lynx_wait_1 (ptid_t ptid, struct target_waitstatus *status, int options)
-{
- int pid;
- int ret;
- int wstat;
- ptid_t new_ptid;
-
- if (ptid == minus_one_ptid)
- pid = lynx_ptid_get_pid (ptid_of (current_thread));
- else
- pid = BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
-
-retry:
-
- ret = lynx_waitpid (pid, &wstat);
- new_ptid = lynx_ptid_t (ret, ((union wait *) &wstat)->w_tid);
- find_process_pid (ret)->priv->last_wait_event_ptid = new_ptid;
-
- /* If this is a new thread, then add it now. The reason why we do
- this here instead of when handling new-thread events is because
- we need to add the thread associated to the "main" thread - even
- for non-threaded applications where the new-thread events are not
- generated. */
- if (!find_thread_ptid (new_ptid))
- {
- lynx_debug ("New thread: (pid = %d, tid = %d)",
- lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid));
- add_thread (new_ptid, NULL);
- }
-
- if (WIFSTOPPED (wstat))
- {
- status->kind = TARGET_WAITKIND_STOPPED;
- status->value.integer = gdb_signal_from_host (WSTOPSIG (wstat));
- lynx_debug ("process stopped with signal: %d",
- status->value.integer);
- }
- else if (WIFEXITED (wstat))
- {
- status->kind = TARGET_WAITKIND_EXITED;
- status->value.integer = WEXITSTATUS (wstat);
- lynx_debug ("process exited with code: %d", status->value.integer);
- }
- else if (WIFSIGNALED (wstat))
- {
- status->kind = TARGET_WAITKIND_SIGNALLED;
- status->value.integer = gdb_signal_from_host (WTERMSIG (wstat));
- lynx_debug ("process terminated with code: %d",
- status->value.integer);
- }
- else
- {
- /* Not sure what happened if we get here, or whether we can
- in fact get here. But if we do, handle the event the best
- we can. */
- status->kind = TARGET_WAITKIND_STOPPED;
- status->value.integer = gdb_signal_from_host (0);
- lynx_debug ("unknown event ????");
- }
-
- /* SIGTRAP events are generated for situations other than single-step/
- breakpoint events (Eg. new-thread events). Handle those other types
- of events, and resume the execution if necessary. */
- if (status->kind == TARGET_WAITKIND_STOPPED
- && status->value.integer == GDB_SIGNAL_TRAP)
- {
- const int realsig = lynx_ptrace (PTRACE_GETTRACESIG, new_ptid, 0, 0, 0);
-
- lynx_debug ("(realsig = %d)", realsig);
- switch (realsig)
- {
- case SIGNEWTHREAD:
- /* We just added the new thread above. No need to do anything
- further. Just resume the execution again. */
- lynx_continue (new_ptid);
- goto retry;
-
- case SIGTHREADEXIT:
- remove_thread (find_thread_ptid (new_ptid));
- lynx_continue (new_ptid);
- goto retry;
- }
- }
-
- return new_ptid;
-}
-
-/* A wrapper around lynx_wait_1 that also prints debug traces when
- such debug traces have been activated. */
-
-static ptid_t
-lynx_wait (ptid_t ptid, struct target_waitstatus *status, int options)
-{
- ptid_t new_ptid;
-
- lynx_debug ("lynx_wait (pid = %d, tid = %ld)",
- lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
- new_ptid = lynx_wait_1 (ptid, status, options);
- lynx_debug (" -> (pid=%d, tid=%ld, status->kind = %d)",
- lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid),
- status->kind);
- return new_ptid;
-}
-
-/* Implement the kill target_ops method. */
-
-static int
-lynx_kill (process_info *process)
-{
- ptid_t ptid = lynx_ptid_t (process->pid, 0);
- struct target_waitstatus status;
-
- lynx_ptrace (PTRACE_KILL, ptid, 0, 0, 0);
- lynx_wait (ptid, &status, 0);
- the_target->mourn (process);
- return 0;
-}
-
-/* Implement the detach target_ops method. */
-
-static int
-lynx_detach (process_info *process)
-{
- ptid_t ptid = lynx_ptid_t (process->pid, 0);
-
- lynx_ptrace (PTRACE_DETACH, ptid, 0, 0, 0);
- the_target->mourn (process);
- return 0;
-}
-
-/* Implement the mourn target_ops method. */
-
-static void
-lynx_mourn (struct process_info *proc)
-{
- for_each_thread (proc->pid, remove_thread);
-
- /* Free our private data. */
- free (proc->priv);
- proc->priv = NULL;
-
- remove_process (proc);
-}
-
-/* Implement the join target_ops method. */
-
-static void
-lynx_join (int pid)
-{
- /* The PTRACE_DETACH is sufficient to detach from the process.
- So no need to do anything extra. */
-}
-
-/* Implement the thread_alive target_ops method. */
-
-static int
-lynx_thread_alive (ptid_t ptid)
-{
- /* The list of threads is updated at the end of each wait, so it
- should be up to date. No need to re-fetch it. */
- return (find_thread_ptid (ptid) != NULL);
-}
-
-/* Implement the fetch_registers target_ops method. */
-
-static void
-lynx_fetch_registers (struct regcache *regcache, int regno)
-{
- struct lynx_regset_info *regset = lynx_target_regsets;
- ptid_t inferior_ptid = ptid_of (current_thread);
-
- lynx_debug ("lynx_fetch_registers (regno = %d)", regno);
-
- while (regset->size >= 0)
- {
- char *buf;
- int res;
-
- buf = xmalloc (regset->size);
- res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
- if (res < 0)
- perror ("ptrace");
- regset->store_function (regcache, buf);
- free (buf);
- regset++;
- }
-}
-
-/* Implement the store_registers target_ops method. */
-
-static void
-lynx_store_registers (struct regcache *regcache, int regno)
-{
- struct lynx_regset_info *regset = lynx_target_regsets;
- ptid_t inferior_ptid = ptid_of (current_thread);
-
- lynx_debug ("lynx_store_registers (regno = %d)", regno);
-
- while (regset->size >= 0)
- {
- char *buf;
- int res;
-
- buf = xmalloc (regset->size);
- res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
- if (res == 0)
- {
- /* Then overlay our cached registers on that. */
- regset->fill_function (regcache, buf);
- /* Only now do we write the register set. */
- res = lynx_ptrace (regset->set_request, inferior_ptid, (int) buf,
- 0, 0);
- }
- if (res < 0)
- perror ("ptrace");
- free (buf);
- regset++;
- }
-}
-
-/* Implement the read_memory target_ops method. */
-
-static int
-lynx_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- /* On LynxOS, memory reads needs to be performed in chunks the size
- of int types, and they should also be aligned accordingly. */
- int buf;
- const int xfer_size = sizeof (buf);
- CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
- ptid_t inferior_ptid = ptid_of (current_thread);
-
- while (addr < memaddr + len)
- {
- int skip = 0;
- int truncate = 0;
-
- errno = 0;
- if (addr < memaddr)
- skip = memaddr - addr;
- if (addr + xfer_size > memaddr + len)
- truncate = addr + xfer_size - memaddr - len;
- buf = lynx_ptrace (PTRACE_PEEKTEXT, inferior_ptid, addr, 0, 0);
- if (errno)
- return errno;
- memcpy (myaddr + (addr - memaddr) + skip, (gdb_byte *) &buf + skip,
- xfer_size - skip - truncate);
- addr += xfer_size;
- }
-
- return 0;
-}
-
-/* Implement the write_memory target_ops method. */
-
-static int
-lynx_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
-{
- /* On LynxOS, memory writes needs to be performed in chunks the size
- of int types, and they should also be aligned accordingly. */
- int buf;
- const int xfer_size = sizeof (buf);
- CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
- ptid_t inferior_ptid = ptid_of (current_thread);
-
- while (addr < memaddr + len)
- {
- int skip = 0;
- int truncate = 0;
-
- if (addr < memaddr)
- skip = memaddr - addr;
- if (addr + xfer_size > memaddr + len)
- truncate = addr + xfer_size - memaddr - len;
- if (skip > 0 || truncate > 0)
- {
- /* We need to read the memory at this address in order to preserve
- the data that we are not overwriting. */
- lynx_read_memory (addr, (unsigned char *) &buf, xfer_size);
- if (errno)
- return errno;
- }
- memcpy ((gdb_byte *) &buf + skip, myaddr + (addr - memaddr) + skip,
- xfer_size - skip - truncate);
- errno = 0;
- lynx_ptrace (PTRACE_POKETEXT, inferior_ptid, addr, buf, 0);
- if (errno)
- return errno;
- addr += xfer_size;
- }
-
- return 0;
-}
-
-/* Implement the kill_request target_ops method. */
-
-static void
-lynx_request_interrupt (void)
-{
- ptid_t inferior_ptid = ptid_of (get_first_thread ());
-
- kill (lynx_ptid_get_pid (inferior_ptid), SIGINT);
-}
-
-/* The LynxOS target_ops vector. */
-
-static process_stratum_target lynx_target_ops = {
- lynx_create_inferior,
- NULL, /* post_create_inferior */
- lynx_attach,
- lynx_kill,
- lynx_detach,
- lynx_mourn,
- lynx_join,
- lynx_thread_alive,
- lynx_resume,
- lynx_wait,
- lynx_fetch_registers,
- lynx_store_registers,
- NULL, /* prepare_to_access_memory */
- NULL, /* done_accessing_memory */
- lynx_read_memory,
- lynx_write_memory,
- NULL, /* look_up_symbols */
- lynx_request_interrupt,
- NULL, /* read_auxv */
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_sw_breakpoint */
- NULL, /* supports_stopped_by_sw_breakpoint */
- NULL, /* stopped_by_hw_breakpoint */
- NULL, /* supports_stopped_by_hw_breakpoint */
- target_can_do_hardware_single_step,
- NULL, /* stopped_by_watchpoint */
- NULL, /* stopped_data_address */
- NULL, /* read_offsets */
- NULL, /* get_tls_address */
- NULL, /* hostio_last_error */
- NULL, /* qxfer_osdata */
- NULL, /* qxfer_siginfo */
- NULL, /* supports_non_stop */
- NULL, /* async */
- NULL, /* start_non_stop */
- NULL, /* supports_multi_process */
- NULL, /* supports_fork_events */
- NULL, /* supports_vfork_events */
- NULL, /* supports_exec_events */
- NULL, /* handle_new_gdb_connection */
- NULL, /* handle_monitor_command */
-};
-
-void
-initialize_low (void)
-{
- set_target_ops (&lynx_target_ops);
- the_low_target.arch_setup ();
-}
-
--- /dev/null
+/* Copyright (C) 2009-2020 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 "target.h"
+#include "lynx-low.h"
+
+#include <limits.h>
+#include <sys/ptrace.h>
+#include <sys/piddef.h> /* Provides PIDGET, TIDGET, BUILDPID, etc. */
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include "gdbsupport/gdb_wait.h"
+#include <signal.h>
+#include "gdbsupport/filestuff.h"
+#include "gdbsupport/common-inferior.h"
+#include "nat/fork-inferior.h"
+
+int using_threads = 1;
+
+const struct target_desc *lynx_tdesc;
+
+/* Per-process private data. */
+
+struct process_info_private
+{
+ /* The PTID obtained from the last wait performed on this process.
+ Initialized to null_ptid until the first wait is performed. */
+ ptid_t last_wait_event_ptid;
+};
+
+/* Print a debug trace on standard output if debug_threads is set. */
+
+static void
+lynx_debug (char *string, ...)
+{
+ va_list args;
+
+ if (!debug_threads)
+ return;
+
+ va_start (args, string);
+ fprintf (stderr, "DEBUG(lynx): ");
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+}
+
+/* Build a ptid_t given a PID and a LynxOS TID. */
+
+static ptid_t
+lynx_ptid_t (int pid, long tid)
+{
+ /* brobecker/2010-06-21: It looks like the LWP field in ptids
+ should be distinct for each thread (see write_ptid where it
+ writes the thread ID from the LWP). So instead of storing
+ the LynxOS tid in the tid field of the ptid, we store it in
+ the lwp field. */
+ return ptid_t (pid, tid, 0);
+}
+
+/* Return the process ID of the given PTID.
+
+ This function has little reason to exist, it's just a wrapper around
+ ptid_get_pid. But since we have a getter function for the lynxos
+ ptid, it feels cleaner to have a getter for the pid as well. */
+
+static int
+lynx_ptid_get_pid (ptid_t ptid)
+{
+ return ptid.pid ();
+}
+
+/* Return the LynxOS tid of the given PTID. */
+
+static long
+lynx_ptid_get_tid (ptid_t ptid)
+{
+ /* See lynx_ptid_t: The LynxOS tid is stored inside the lwp field
+ of the ptid. */
+ return ptid.lwp ();
+}
+
+/* For a given PTID, return the associated PID as known by the LynxOS
+ ptrace layer. */
+
+static int
+lynx_ptrace_pid_from_ptid (ptid_t ptid)
+{
+ return BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
+}
+
+/* Return a string image of the ptrace REQUEST number. */
+
+static char *
+ptrace_request_to_str (int request)
+{
+#define CASE(X) case X: return #X
+ switch (request)
+ {
+ CASE(PTRACE_TRACEME);
+ CASE(PTRACE_PEEKTEXT);
+ CASE(PTRACE_PEEKDATA);
+ CASE(PTRACE_PEEKUSER);
+ CASE(PTRACE_POKETEXT);
+ CASE(PTRACE_POKEDATA);
+ CASE(PTRACE_POKEUSER);
+ CASE(PTRACE_CONT);
+ CASE(PTRACE_KILL);
+ CASE(PTRACE_SINGLESTEP);
+ CASE(PTRACE_ATTACH);
+ CASE(PTRACE_DETACH);
+ CASE(PTRACE_GETREGS);
+ CASE(PTRACE_SETREGS);
+ CASE(PTRACE_GETFPREGS);
+ CASE(PTRACE_SETFPREGS);
+ CASE(PTRACE_READDATA);
+ CASE(PTRACE_WRITEDATA);
+ CASE(PTRACE_READTEXT);
+ CASE(PTRACE_WRITETEXT);
+ CASE(PTRACE_GETFPAREGS);
+ CASE(PTRACE_SETFPAREGS);
+ CASE(PTRACE_GETWINDOW);
+ CASE(PTRACE_SETWINDOW);
+ CASE(PTRACE_SYSCALL);
+ CASE(PTRACE_DUMPCORE);
+ CASE(PTRACE_SETWRBKPT);
+ CASE(PTRACE_SETACBKPT);
+ CASE(PTRACE_CLRBKPT);
+ CASE(PTRACE_GET_UCODE);
+#ifdef PT_READ_GPR
+ CASE(PT_READ_GPR);
+#endif
+#ifdef PT_WRITE_GPR
+ CASE(PT_WRITE_GPR);
+#endif
+#ifdef PT_READ_FPR
+ CASE(PT_READ_FPR);
+#endif
+#ifdef PT_WRITE_FPR
+ CASE(PT_WRITE_FPR);
+#endif
+#ifdef PT_READ_VPR
+ CASE(PT_READ_VPR);
+#endif
+#ifdef PT_WRITE_VPR
+ CASE(PT_WRITE_VPR);
+#endif
+#ifdef PTRACE_PEEKUSP
+ CASE(PTRACE_PEEKUSP);
+#endif
+#ifdef PTRACE_POKEUSP
+ CASE(PTRACE_POKEUSP);
+#endif
+ CASE(PTRACE_PEEKTHREAD);
+ CASE(PTRACE_THREADUSER);
+ CASE(PTRACE_FPREAD);
+ CASE(PTRACE_FPWRITE);
+ CASE(PTRACE_SETSIG);
+ CASE(PTRACE_CONT_ONE);
+ CASE(PTRACE_KILL_ONE);
+ CASE(PTRACE_SINGLESTEP_ONE);
+ CASE(PTRACE_GETLOADINFO);
+ CASE(PTRACE_GETTRACESIG);
+#ifdef PTRACE_GETTHREADLIST
+ CASE(PTRACE_GETTHREADLIST);
+#endif
+ }
+#undef CASE
+
+ return "<unknown-request>";
+}
+
+/* A wrapper around ptrace that allows us to print debug traces of
+ ptrace calls if debug traces are activated. */
+
+static int
+lynx_ptrace (int request, ptid_t ptid, int addr, int data, int addr2)
+{
+ int result;
+ const int pid = lynx_ptrace_pid_from_ptid (ptid);
+ int saved_errno;
+
+ if (debug_threads)
+ fprintf (stderr, "PTRACE (%s, pid=%d(pid=%d, tid=%d), addr=0x%x, "
+ "data=0x%x, addr2=0x%x)",
+ ptrace_request_to_str (request), pid, PIDGET (pid), TIDGET (pid),
+ addr, data, addr2);
+ result = ptrace (request, pid, addr, data, addr2);
+ saved_errno = errno;
+ if (debug_threads)
+ fprintf (stderr, " -> %d (=0x%x)\n", result, result);
+
+ errno = saved_errno;
+ return result;
+}
+
+/* Call add_process with the given parameters, and initializes
+ the process' private data. */
+
+static struct process_info *
+lynx_add_process (int pid, int attached)
+{
+ struct process_info *proc;
+
+ proc = add_process (pid, attached);
+ proc->tdesc = lynx_tdesc;
+ proc->priv = XCNEW (struct process_info_private);
+ proc->priv->last_wait_event_ptid = null_ptid;
+
+ return proc;
+}
+
+/* Callback used by fork_inferior to start tracing the inferior. */
+
+static void
+lynx_ptrace_fun ()
+{
+ int pgrp;
+
+ /* Switch child to its own process group so that signals won't
+ directly affect GDBserver. */
+ pgrp = getpid();
+ if (pgrp < 0)
+ trace_start_error_with_name ("pgrp");
+ if (setpgid (0, pgrp) < 0)
+ trace_start_error_with_name ("setpgid");
+ if (ioctl (0, TIOCSPGRP, &pgrp) < 0)
+ trace_start_error_with_name ("ioctl");
+ if (lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0) < 0)
+ trace_start_error_with_name ("lynx_ptrace");
+}
+
+/* Implement the create_inferior method of the target_ops vector. */
+
+static int
+lynx_create_inferior (const char *program,
+ const std::vector<char *> &program_args)
+{
+ int pid;
+ std::string str_program_args = stringify_argv (program_args);
+
+ lynx_debug ("lynx_create_inferior ()");
+
+ pid = fork_inferior (program,
+ str_program_args.c_str (),
+ get_environ ()->envp (), lynx_ptrace_fun,
+ NULL, NULL, NULL, NULL);
+
+ post_fork_inferior (pid, program);
+
+ lynx_add_process (pid, 0);
+ /* Do not add the process thread just yet, as we do not know its tid.
+ We will add it later, during the wait for the STOP event corresponding
+ to the lynx_ptrace (PTRACE_TRACEME) call above. */
+ return pid;
+}
+
+/* Assuming we've just attached to a running inferior whose pid is PID,
+ add all threads running in that process. */
+
+static void
+lynx_add_threads_after_attach (int pid)
+{
+ /* Ugh! There appears to be no way to get the list of threads
+ in the program we just attached to. So get the list by calling
+ the "ps" command. This is only needed now, as we will then
+ keep the thread list up to date thanks to thread creation and
+ exit notifications. */
+ FILE *f;
+ char buf[256];
+ int thread_pid, thread_tid;
+
+ f = popen ("ps atx", "r");
+ if (f == NULL)
+ perror_with_name ("Cannot get thread list");
+
+ while (fgets (buf, sizeof (buf), f) != NULL)
+ if ((sscanf (buf, "%d %d", &thread_pid, &thread_tid) == 2
+ && thread_pid == pid))
+ {
+ ptid_t thread_ptid = lynx_ptid_t (pid, thread_tid);
+
+ if (!find_thread_ptid (thread_ptid))
+ {
+ lynx_debug ("New thread: (pid = %d, tid = %d)",
+ pid, thread_tid);
+ add_thread (thread_ptid, NULL);
+ }
+ }
+
+ pclose (f);
+}
+
+/* Implement the attach target_ops method. */
+
+static int
+lynx_attach (unsigned long pid)
+{
+ ptid_t ptid = lynx_ptid_t (pid, 0);
+
+ if (lynx_ptrace (PTRACE_ATTACH, ptid, 0, 0, 0) != 0)
+ error ("Cannot attach to process %lu: %s (%d)\n", pid,
+ safe_strerror (errno), errno);
+
+ lynx_add_process (pid, 1);
+ lynx_add_threads_after_attach (pid);
+
+ return 0;
+}
+
+/* Implement the resume target_ops method. */
+
+static void
+lynx_resume (struct thread_resume *resume_info, size_t n)
+{
+ ptid_t ptid = resume_info[0].thread;
+ const int request
+ = (resume_info[0].kind == resume_step
+ ? (n == 1 ? PTRACE_SINGLESTEP_ONE : PTRACE_SINGLESTEP)
+ : PTRACE_CONT);
+ const int signal = resume_info[0].sig;
+
+ /* If given a minus_one_ptid, then try using the current_process'
+ private->last_wait_event_ptid. On most LynxOS versions,
+ using any of the process' thread works well enough, but
+ LynxOS 178 is a little more sensitive, and triggers some
+ unexpected signals (Eg SIG61) when we resume the inferior
+ using a different thread. */
+ if (ptid == minus_one_ptid)
+ ptid = current_process()->priv->last_wait_event_ptid;
+
+ /* The ptid might still be minus_one_ptid; this can happen between
+ the moment we create the inferior or attach to a process, and
+ the moment we resume its execution for the first time. It is
+ fine to use the current_thread's ptid in those cases. */
+ if (ptid == minus_one_ptid)
+ ptid = ptid_of (current_thread);
+
+ regcache_invalidate_pid (ptid.pid ());
+
+ errno = 0;
+ lynx_ptrace (request, ptid, 1, signal, 0);
+ if (errno)
+ perror_with_name ("ptrace");
+}
+
+/* Resume the execution of the given PTID. */
+
+static void
+lynx_continue (ptid_t ptid)
+{
+ struct thread_resume resume_info;
+
+ resume_info.thread = ptid;
+ resume_info.kind = resume_continue;
+ resume_info.sig = 0;
+
+ lynx_resume (&resume_info, 1);
+}
+
+/* A wrapper around waitpid that handles the various idiosyncrasies
+ of LynxOS' waitpid. */
+
+static int
+lynx_waitpid (int pid, int *stat_loc)
+{
+ int ret = 0;
+
+ while (1)
+ {
+ ret = waitpid (pid, stat_loc, WNOHANG);
+ if (ret < 0)
+ {
+ /* An ECHILD error is not indicative of a real problem.
+ It happens for instance while waiting for the inferior
+ to stop after attaching to it. */
+ if (errno != ECHILD)
+ perror_with_name ("waitpid (WNOHANG)");
+ }
+ if (ret > 0)
+ break;
+ /* No event with WNOHANG. See if there is one with WUNTRACED. */
+ ret = waitpid (pid, stat_loc, WNOHANG | WUNTRACED);
+ if (ret < 0)
+ {
+ /* An ECHILD error is not indicative of a real problem.
+ It happens for instance while waiting for the inferior
+ to stop after attaching to it. */
+ if (errno != ECHILD)
+ perror_with_name ("waitpid (WNOHANG|WUNTRACED)");
+ }
+ if (ret > 0)
+ break;
+ usleep (1000);
+ }
+ return ret;
+}
+
+/* Implement the wait target_ops method. */
+
+static ptid_t
+lynx_wait_1 (ptid_t ptid, struct target_waitstatus *status, int options)
+{
+ int pid;
+ int ret;
+ int wstat;
+ ptid_t new_ptid;
+
+ if (ptid == minus_one_ptid)
+ pid = lynx_ptid_get_pid (ptid_of (current_thread));
+ else
+ pid = BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
+
+retry:
+
+ ret = lynx_waitpid (pid, &wstat);
+ new_ptid = lynx_ptid_t (ret, ((union wait *) &wstat)->w_tid);
+ find_process_pid (ret)->priv->last_wait_event_ptid = new_ptid;
+
+ /* If this is a new thread, then add it now. The reason why we do
+ this here instead of when handling new-thread events is because
+ we need to add the thread associated to the "main" thread - even
+ for non-threaded applications where the new-thread events are not
+ generated. */
+ if (!find_thread_ptid (new_ptid))
+ {
+ lynx_debug ("New thread: (pid = %d, tid = %d)",
+ lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid));
+ add_thread (new_ptid, NULL);
+ }
+
+ if (WIFSTOPPED (wstat))
+ {
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.integer = gdb_signal_from_host (WSTOPSIG (wstat));
+ lynx_debug ("process stopped with signal: %d",
+ status->value.integer);
+ }
+ else if (WIFEXITED (wstat))
+ {
+ status->kind = TARGET_WAITKIND_EXITED;
+ status->value.integer = WEXITSTATUS (wstat);
+ lynx_debug ("process exited with code: %d", status->value.integer);
+ }
+ else if (WIFSIGNALED (wstat))
+ {
+ status->kind = TARGET_WAITKIND_SIGNALLED;
+ status->value.integer = gdb_signal_from_host (WTERMSIG (wstat));
+ lynx_debug ("process terminated with code: %d",
+ status->value.integer);
+ }
+ else
+ {
+ /* Not sure what happened if we get here, or whether we can
+ in fact get here. But if we do, handle the event the best
+ we can. */
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.integer = gdb_signal_from_host (0);
+ lynx_debug ("unknown event ????");
+ }
+
+ /* SIGTRAP events are generated for situations other than single-step/
+ breakpoint events (Eg. new-thread events). Handle those other types
+ of events, and resume the execution if necessary. */
+ if (status->kind == TARGET_WAITKIND_STOPPED
+ && status->value.integer == GDB_SIGNAL_TRAP)
+ {
+ const int realsig = lynx_ptrace (PTRACE_GETTRACESIG, new_ptid, 0, 0, 0);
+
+ lynx_debug ("(realsig = %d)", realsig);
+ switch (realsig)
+ {
+ case SIGNEWTHREAD:
+ /* We just added the new thread above. No need to do anything
+ further. Just resume the execution again. */
+ lynx_continue (new_ptid);
+ goto retry;
+
+ case SIGTHREADEXIT:
+ remove_thread (find_thread_ptid (new_ptid));
+ lynx_continue (new_ptid);
+ goto retry;
+ }
+ }
+
+ return new_ptid;
+}
+
+/* A wrapper around lynx_wait_1 that also prints debug traces when
+ such debug traces have been activated. */
+
+static ptid_t
+lynx_wait (ptid_t ptid, struct target_waitstatus *status, int options)
+{
+ ptid_t new_ptid;
+
+ lynx_debug ("lynx_wait (pid = %d, tid = %ld)",
+ lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
+ new_ptid = lynx_wait_1 (ptid, status, options);
+ lynx_debug (" -> (pid=%d, tid=%ld, status->kind = %d)",
+ lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid),
+ status->kind);
+ return new_ptid;
+}
+
+/* Implement the kill target_ops method. */
+
+static int
+lynx_kill (process_info *process)
+{
+ ptid_t ptid = lynx_ptid_t (process->pid, 0);
+ struct target_waitstatus status;
+
+ lynx_ptrace (PTRACE_KILL, ptid, 0, 0, 0);
+ lynx_wait (ptid, &status, 0);
+ the_target->mourn (process);
+ return 0;
+}
+
+/* Implement the detach target_ops method. */
+
+static int
+lynx_detach (process_info *process)
+{
+ ptid_t ptid = lynx_ptid_t (process->pid, 0);
+
+ lynx_ptrace (PTRACE_DETACH, ptid, 0, 0, 0);
+ the_target->mourn (process);
+ return 0;
+}
+
+/* Implement the mourn target_ops method. */
+
+static void
+lynx_mourn (struct process_info *proc)
+{
+ for_each_thread (proc->pid, remove_thread);
+
+ /* Free our private data. */
+ free (proc->priv);
+ proc->priv = NULL;
+
+ remove_process (proc);
+}
+
+/* Implement the join target_ops method. */
+
+static void
+lynx_join (int pid)
+{
+ /* The PTRACE_DETACH is sufficient to detach from the process.
+ So no need to do anything extra. */
+}
+
+/* Implement the thread_alive target_ops method. */
+
+static int
+lynx_thread_alive (ptid_t ptid)
+{
+ /* The list of threads is updated at the end of each wait, so it
+ should be up to date. No need to re-fetch it. */
+ return (find_thread_ptid (ptid) != NULL);
+}
+
+/* Implement the fetch_registers target_ops method. */
+
+static void
+lynx_fetch_registers (struct regcache *regcache, int regno)
+{
+ struct lynx_regset_info *regset = lynx_target_regsets;
+ ptid_t inferior_ptid = ptid_of (current_thread);
+
+ lynx_debug ("lynx_fetch_registers (regno = %d)", regno);
+
+ while (regset->size >= 0)
+ {
+ char *buf;
+ int res;
+
+ buf = xmalloc (regset->size);
+ res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
+ if (res < 0)
+ perror ("ptrace");
+ regset->store_function (regcache, buf);
+ free (buf);
+ regset++;
+ }
+}
+
+/* Implement the store_registers target_ops method. */
+
+static void
+lynx_store_registers (struct regcache *regcache, int regno)
+{
+ struct lynx_regset_info *regset = lynx_target_regsets;
+ ptid_t inferior_ptid = ptid_of (current_thread);
+
+ lynx_debug ("lynx_store_registers (regno = %d)", regno);
+
+ while (regset->size >= 0)
+ {
+ char *buf;
+ int res;
+
+ buf = xmalloc (regset->size);
+ res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
+ if (res == 0)
+ {
+ /* Then overlay our cached registers on that. */
+ regset->fill_function (regcache, buf);
+ /* Only now do we write the register set. */
+ res = lynx_ptrace (regset->set_request, inferior_ptid, (int) buf,
+ 0, 0);
+ }
+ if (res < 0)
+ perror ("ptrace");
+ free (buf);
+ regset++;
+ }
+}
+
+/* Implement the read_memory target_ops method. */
+
+static int
+lynx_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ /* On LynxOS, memory reads needs to be performed in chunks the size
+ of int types, and they should also be aligned accordingly. */
+ int buf;
+ const int xfer_size = sizeof (buf);
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
+ ptid_t inferior_ptid = ptid_of (current_thread);
+
+ while (addr < memaddr + len)
+ {
+ int skip = 0;
+ int truncate = 0;
+
+ errno = 0;
+ if (addr < memaddr)
+ skip = memaddr - addr;
+ if (addr + xfer_size > memaddr + len)
+ truncate = addr + xfer_size - memaddr - len;
+ buf = lynx_ptrace (PTRACE_PEEKTEXT, inferior_ptid, addr, 0, 0);
+ if (errno)
+ return errno;
+ memcpy (myaddr + (addr - memaddr) + skip, (gdb_byte *) &buf + skip,
+ xfer_size - skip - truncate);
+ addr += xfer_size;
+ }
+
+ return 0;
+}
+
+/* Implement the write_memory target_ops method. */
+
+static int
+lynx_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ /* On LynxOS, memory writes needs to be performed in chunks the size
+ of int types, and they should also be aligned accordingly. */
+ int buf;
+ const int xfer_size = sizeof (buf);
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
+ ptid_t inferior_ptid = ptid_of (current_thread);
+
+ while (addr < memaddr + len)
+ {
+ int skip = 0;
+ int truncate = 0;
+
+ if (addr < memaddr)
+ skip = memaddr - addr;
+ if (addr + xfer_size > memaddr + len)
+ truncate = addr + xfer_size - memaddr - len;
+ if (skip > 0 || truncate > 0)
+ {
+ /* We need to read the memory at this address in order to preserve
+ the data that we are not overwriting. */
+ lynx_read_memory (addr, (unsigned char *) &buf, xfer_size);
+ if (errno)
+ return errno;
+ }
+ memcpy ((gdb_byte *) &buf + skip, myaddr + (addr - memaddr) + skip,
+ xfer_size - skip - truncate);
+ errno = 0;
+ lynx_ptrace (PTRACE_POKETEXT, inferior_ptid, addr, buf, 0);
+ if (errno)
+ return errno;
+ addr += xfer_size;
+ }
+
+ return 0;
+}
+
+/* Implement the kill_request target_ops method. */
+
+static void
+lynx_request_interrupt (void)
+{
+ ptid_t inferior_ptid = ptid_of (get_first_thread ());
+
+ kill (lynx_ptid_get_pid (inferior_ptid), SIGINT);
+}
+
+/* The LynxOS target_ops vector. */
+
+static process_stratum_target lynx_target_ops = {
+ lynx_create_inferior,
+ NULL, /* post_create_inferior */
+ lynx_attach,
+ lynx_kill,
+ lynx_detach,
+ lynx_mourn,
+ lynx_join,
+ lynx_thread_alive,
+ lynx_resume,
+ lynx_wait,
+ lynx_fetch_registers,
+ lynx_store_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
+ lynx_read_memory,
+ lynx_write_memory,
+ NULL, /* look_up_symbols */
+ lynx_request_interrupt,
+ NULL, /* read_auxv */
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_sw_breakpoint */
+ NULL, /* supports_stopped_by_sw_breakpoint */
+ NULL, /* stopped_by_hw_breakpoint */
+ NULL, /* supports_stopped_by_hw_breakpoint */
+ target_can_do_hardware_single_step,
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* read_offsets */
+ NULL, /* get_tls_address */
+ NULL, /* hostio_last_error */
+ NULL, /* qxfer_osdata */
+ NULL, /* qxfer_siginfo */
+ NULL, /* supports_non_stop */
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
+ NULL, /* handle_new_gdb_connection */
+ NULL, /* handle_monitor_command */
+};
+
+void
+initialize_low (void)
+{
+ set_target_ops (&lynx_target_ops);
+ the_low_target.arch_setup ();
+}
+
+++ /dev/null
-/* Copyright (C) 2009-2020 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 "lynx-low.h"
-#include <limits.h>
-#include <sys/ptrace.h>
-
-/* The following two typedefs are defined in a .h file which is not
- in the standard include path (/sys/include/family/ppc/ucontext.h),
- so we just duplicate them here. */
-
-/* General register context */
-typedef struct usr_econtext_s
-{
- uint32_t uec_iregs[32];
- uint32_t uec_inum;
- uint32_t uec_srr0;
- uint32_t uec_srr1;
- uint32_t uec_lr;
- uint32_t uec_ctr;
- uint32_t uec_cr;
- uint32_t uec_xer;
- uint32_t uec_dar;
- uint32_t uec_mq;
- uint32_t uec_msr;
- uint32_t uec_sregs[16];
- uint32_t uec_ss_count;
- uint32_t uec_ss_addr1;
- uint32_t uec_ss_addr2;
- uint32_t uec_ss_code1;
- uint32_t uec_ss_code2;
-} usr_econtext_t;
-
-/* Floating point register context */
-typedef struct usr_fcontext_s
-{
- uint64_t ufc_freg[32];
- uint32_t ufc_fpscr[2];
-} usr_fcontext_t;
-
-/* Index of for various registers inside the regcache. */
-#define R0_REGNUM 0
-#define F0_REGNUM 32
-#define PC_REGNUM 64
-#define MSR_REGNUM 65
-#define CR_REGNUM 66
-#define LR_REGNUM 67
-#define CTR_REGNUM 68
-#define XER_REGNUM 69
-#define FPSCR_REGNUM 70
-
-/* Defined in auto-generated file powerpc-32.c. */
-extern void init_registers_powerpc_32 (void);
-extern const struct target_desc *tdesc_powerpc_32;
-
-/* The fill_function for the general-purpose register set. */
-
-static void
-lynx_ppc_fill_gregset (struct regcache *regcache, char *buf)
-{
- int i;
-
- /* r0 - r31 */
- for (i = 0; i < 32; i++)
- collect_register (regcache, R0_REGNUM + i,
- buf + offsetof (usr_econtext_t, uec_iregs[i]));
-
- /* The other registers provided in the GP register context. */
- collect_register (regcache, PC_REGNUM,
- buf + offsetof (usr_econtext_t, uec_srr0));
- collect_register (regcache, MSR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_srr1));
- collect_register (regcache, CR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_cr));
- collect_register (regcache, LR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_lr));
- collect_register (regcache, CTR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_ctr));
- collect_register (regcache, XER_REGNUM,
- buf + offsetof (usr_econtext_t, uec_xer));
-}
-
-/* The store_function for the general-purpose register set. */
-
-static void
-lynx_ppc_store_gregset (struct regcache *regcache, const char *buf)
-{
- int i;
-
- /* r0 - r31 */
- for (i = 0; i < 32; i++)
- supply_register (regcache, R0_REGNUM + i,
- buf + offsetof (usr_econtext_t, uec_iregs[i]));
-
- /* The other registers provided in the GP register context. */
- supply_register (regcache, PC_REGNUM,
- buf + offsetof (usr_econtext_t, uec_srr0));
- supply_register (regcache, MSR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_srr1));
- supply_register (regcache, CR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_cr));
- supply_register (regcache, LR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_lr));
- supply_register (regcache, CTR_REGNUM,
- buf + offsetof (usr_econtext_t, uec_ctr));
- supply_register (regcache, XER_REGNUM,
- buf + offsetof (usr_econtext_t, uec_xer));
-}
-
-/* The fill_function for the floating-point register set. */
-
-static void
-lynx_ppc_fill_fpregset (struct regcache *regcache, char *buf)
-{
- int i;
-
- /* f0 - f31 */
- for (i = 0; i < 32; i++)
- collect_register (regcache, F0_REGNUM + i,
- buf + offsetof (usr_fcontext_t, ufc_freg[i]));
-
- /* fpscr */
- collect_register (regcache, FPSCR_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_fpscr));
-}
-
-/* The store_function for the floating-point register set. */
-
-static void
-lynx_ppc_store_fpregset (struct regcache *regcache, const char *buf)
-{
- int i;
-
- /* f0 - f31 */
- for (i = 0; i < 32; i++)
- supply_register (regcache, F0_REGNUM + i,
- buf + offsetof (usr_fcontext_t, ufc_freg[i]));
-
- /* fpscr */
- supply_register (regcache, FPSCR_REGNUM,
- buf + offsetof (usr_fcontext_t, ufc_fpscr));
-}
-
-/* Implements the lynx_target_ops.arch_setup routine. */
-
-static void
-lynx_ppc_arch_setup (void)
-{
- init_registers_powerpc_32 ();
- lynx_tdesc = tdesc_powerpc_32;
-}
-
-/* Description of all the powerpc-lynx register sets. */
-
-struct lynx_regset_info lynx_target_regsets[] = {
- /* General Purpose Registers. */
- {PTRACE_GETREGS, PTRACE_SETREGS, sizeof(usr_econtext_t),
- lynx_ppc_fill_gregset, lynx_ppc_store_gregset},
- /* Floating Point Registers. */
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof(usr_fcontext_t),
- lynx_ppc_fill_fpregset, lynx_ppc_store_fpregset },
- /* End of list marker. */
- {0, 0, -1, NULL, NULL }
-};
-
-/* The lynx_target_ops vector for powerpc-lynxos. */
-
-struct lynx_target_ops the_low_target = {
- lynx_ppc_arch_setup,
-};
--- /dev/null
+/* Copyright (C) 2009-2020 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 "lynx-low.h"
+#include <limits.h>
+#include <sys/ptrace.h>
+
+/* The following two typedefs are defined in a .h file which is not
+ in the standard include path (/sys/include/family/ppc/ucontext.h),
+ so we just duplicate them here. */
+
+/* General register context */
+typedef struct usr_econtext_s
+{
+ uint32_t uec_iregs[32];
+ uint32_t uec_inum;
+ uint32_t uec_srr0;
+ uint32_t uec_srr1;
+ uint32_t uec_lr;
+ uint32_t uec_ctr;
+ uint32_t uec_cr;
+ uint32_t uec_xer;
+ uint32_t uec_dar;
+ uint32_t uec_mq;
+ uint32_t uec_msr;
+ uint32_t uec_sregs[16];
+ uint32_t uec_ss_count;
+ uint32_t uec_ss_addr1;
+ uint32_t uec_ss_addr2;
+ uint32_t uec_ss_code1;
+ uint32_t uec_ss_code2;
+} usr_econtext_t;
+
+/* Floating point register context */
+typedef struct usr_fcontext_s
+{
+ uint64_t ufc_freg[32];
+ uint32_t ufc_fpscr[2];
+} usr_fcontext_t;
+
+/* Index of for various registers inside the regcache. */
+#define R0_REGNUM 0
+#define F0_REGNUM 32
+#define PC_REGNUM 64
+#define MSR_REGNUM 65
+#define CR_REGNUM 66
+#define LR_REGNUM 67
+#define CTR_REGNUM 68
+#define XER_REGNUM 69
+#define FPSCR_REGNUM 70
+
+/* Defined in auto-generated file powerpc-32.c. */
+extern void init_registers_powerpc_32 (void);
+extern const struct target_desc *tdesc_powerpc_32;
+
+/* The fill_function for the general-purpose register set. */
+
+static void
+lynx_ppc_fill_gregset (struct regcache *regcache, char *buf)
+{
+ int i;
+
+ /* r0 - r31 */
+ for (i = 0; i < 32; i++)
+ collect_register (regcache, R0_REGNUM + i,
+ buf + offsetof (usr_econtext_t, uec_iregs[i]));
+
+ /* The other registers provided in the GP register context. */
+ collect_register (regcache, PC_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr0));
+ collect_register (regcache, MSR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr1));
+ collect_register (regcache, CR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_cr));
+ collect_register (regcache, LR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_lr));
+ collect_register (regcache, CTR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_ctr));
+ collect_register (regcache, XER_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_xer));
+}
+
+/* The store_function for the general-purpose register set. */
+
+static void
+lynx_ppc_store_gregset (struct regcache *regcache, const char *buf)
+{
+ int i;
+
+ /* r0 - r31 */
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, R0_REGNUM + i,
+ buf + offsetof (usr_econtext_t, uec_iregs[i]));
+
+ /* The other registers provided in the GP register context. */
+ supply_register (regcache, PC_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr0));
+ supply_register (regcache, MSR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr1));
+ supply_register (regcache, CR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_cr));
+ supply_register (regcache, LR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_lr));
+ supply_register (regcache, CTR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_ctr));
+ supply_register (regcache, XER_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_xer));
+}
+
+/* The fill_function for the floating-point register set. */
+
+static void
+lynx_ppc_fill_fpregset (struct regcache *regcache, char *buf)
+{
+ int i;
+
+ /* f0 - f31 */
+ for (i = 0; i < 32; i++)
+ collect_register (regcache, F0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, ufc_freg[i]));
+
+ /* fpscr */
+ collect_register (regcache, FPSCR_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_fpscr));
+}
+
+/* The store_function for the floating-point register set. */
+
+static void
+lynx_ppc_store_fpregset (struct regcache *regcache, const char *buf)
+{
+ int i;
+
+ /* f0 - f31 */
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, F0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, ufc_freg[i]));
+
+ /* fpscr */
+ supply_register (regcache, FPSCR_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_fpscr));
+}
+
+/* Implements the lynx_target_ops.arch_setup routine. */
+
+static void
+lynx_ppc_arch_setup (void)
+{
+ init_registers_powerpc_32 ();
+ lynx_tdesc = tdesc_powerpc_32;
+}
+
+/* Description of all the powerpc-lynx register sets. */
+
+struct lynx_regset_info lynx_target_regsets[] = {
+ /* General Purpose Registers. */
+ {PTRACE_GETREGS, PTRACE_SETREGS, sizeof(usr_econtext_t),
+ lynx_ppc_fill_gregset, lynx_ppc_store_gregset},
+ /* Floating Point Registers. */
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof(usr_fcontext_t),
+ lynx_ppc_fill_fpregset, lynx_ppc_store_fpregset },
+ /* End of list marker. */
+ {0, 0, -1, NULL, NULL }
+};
+
+/* The lynx_target_ops vector for powerpc-lynxos. */
+
+struct lynx_target_ops the_low_target = {
+ lynx_ppc_arch_setup,
+};
+++ /dev/null
-/* Memory breakpoint operations for the remote server for GDB.
- Copyright (C) 2002-2020 Free Software Foundation, Inc.
-
- Contributed by MontaVista Software.
-
- 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 "regcache.h"
-#include "ax.h"
-
-#define MAX_BREAKPOINT_LEN 8
-
-/* Helper macro used in loops that append multiple items to a singly-linked
- list instead of inserting items at the head of the list, as, say, in the
- breakpoint lists. LISTPP is a pointer to the pointer that is the head of
- the new list. ITEMP is a pointer to the item to be added to the list.
- TAILP must be defined to be the same type as ITEMP, and initialized to
- NULL. */
-
-#define APPEND_TO_LIST(listpp, itemp, tailp) \
- do \
- { \
- if ((tailp) == NULL) \
- *(listpp) = (itemp); \
- else \
- (tailp)->next = (itemp); \
- (tailp) = (itemp); \
- } \
- while (0)
-
-/* GDB will never try to install multiple breakpoints at the same
- address. However, we can see GDB requesting to insert a breakpoint
- at an address is had already inserted one previously in a few
- situations.
-
- - The RSP documentation on Z packets says that to avoid potential
- problems with duplicate packets, the operations should be
- implemented in an idempotent way.
-
- - A breakpoint is set at ADDR, an address in a shared library.
- Then the shared library is unloaded. And then another, unrelated,
- breakpoint at ADDR is set. There is not breakpoint removal request
- between the first and the second breakpoint.
-
- - When GDB wants to update the target-side breakpoint conditions or
- commands, it re-inserts the breakpoint, with updated
- conditions/commands associated.
-
- Also, we need to keep track of internal breakpoints too, so we do
- need to be able to install multiple breakpoints at the same address
- transparently.
-
- We keep track of two different, and closely related structures. A
- raw breakpoint, which manages the low level, close to the metal
- aspect of a breakpoint. It holds the breakpoint address, and for
- software breakpoints, a buffer holding a copy of the instructions
- that would be in memory had not been a breakpoint there (we call
- that the shadow memory of the breakpoint). We occasionally need to
- temporarilly uninsert a breakpoint without the client knowing about
- it (e.g., to step over an internal breakpoint), so we keep an
- `inserted' state associated with this low level breakpoint
- structure. There can only be one such object for a given address.
- Then, we have (a bit higher level) breakpoints. This structure
- holds a callback to be called whenever a breakpoint is hit, a
- high-level type, and a link to a low level raw breakpoint. There
- can be many high-level breakpoints at the same address, and all of
- them will point to the same raw breakpoint, which is reference
- counted. */
-
-/* The low level, physical, raw breakpoint. */
-struct raw_breakpoint
-{
- struct raw_breakpoint *next;
-
- /* The low level type of the breakpoint (software breakpoint,
- watchpoint, etc.) */
- enum raw_bkpt_type raw_type;
-
- /* A reference count. Each high level breakpoint referencing this
- raw breakpoint accounts for one reference. */
- int refcount;
-
- /* The breakpoint's insertion address. There can only be one raw
- breakpoint for a given PC. */
- CORE_ADDR pc;
-
- /* The breakpoint's kind. This is target specific. Most
- architectures only use one specific instruction for breakpoints, while
- others may use more than one. E.g., on ARM, we need to use different
- breakpoint instructions on Thumb, Thumb-2, and ARM code. Likewise for
- hardware breakpoints -- some architectures (including ARM) need to
- setup debug registers differently depending on mode. */
- int kind;
-
- /* The breakpoint's shadow memory. */
- unsigned char old_data[MAX_BREAKPOINT_LEN];
-
- /* Positive if this breakpoint is currently inserted in the
- inferior. Negative if it was, but we've detected that it's now
- gone. Zero if not inserted. */
- int inserted;
-};
-
-/* The type of a breakpoint. */
-enum bkpt_type
- {
- /* A GDB breakpoint, requested with a Z0 packet. */
- gdb_breakpoint_Z0,
-
- /* A GDB hardware breakpoint, requested with a Z1 packet. */
- gdb_breakpoint_Z1,
-
- /* A GDB write watchpoint, requested with a Z2 packet. */
- gdb_breakpoint_Z2,
-
- /* A GDB read watchpoint, requested with a Z3 packet. */
- gdb_breakpoint_Z3,
-
- /* A GDB access watchpoint, requested with a Z4 packet. */
- gdb_breakpoint_Z4,
-
- /* A software single-step breakpoint. */
- single_step_breakpoint,
-
- /* Any other breakpoint type that doesn't require specific
- treatment goes here. E.g., an event breakpoint. */
- other_breakpoint,
- };
-
-struct point_cond_list
-{
- /* Pointer to the agent expression that is the breakpoint's
- conditional. */
- struct agent_expr *cond;
-
- /* Pointer to the next condition. */
- struct point_cond_list *next;
-};
-
-struct point_command_list
-{
- /* Pointer to the agent expression that is the breakpoint's
- commands. */
- struct agent_expr *cmd;
-
- /* Flag that is true if this command should run even while GDB is
- disconnected. */
- int persistence;
-
- /* Pointer to the next command. */
- struct point_command_list *next;
-};
-
-/* A high level (in gdbserver's perspective) breakpoint. */
-struct breakpoint
-{
- struct breakpoint *next;
-
- /* The breakpoint's type. */
- enum bkpt_type type;
-
- /* Link to this breakpoint's raw breakpoint. This is always
- non-NULL. */
- struct raw_breakpoint *raw;
-};
-
-/* Breakpoint requested by GDB. */
-
-struct gdb_breakpoint
-{
- struct breakpoint base;
-
- /* Pointer to the condition list that should be evaluated on
- the target or NULL if the breakpoint is unconditional or
- if GDB doesn't want us to evaluate the conditionals on the
- target's side. */
- struct point_cond_list *cond_list;
-
- /* Point to the list of commands to run when this is hit. */
- struct point_command_list *command_list;
-};
-
-/* Breakpoint used by GDBserver. */
-
-struct other_breakpoint
-{
- struct breakpoint base;
-
- /* Function to call when we hit this breakpoint. If it returns 1,
- the breakpoint shall be deleted; 0 or if this callback is NULL,
- it will be left inserted. */
- int (*handler) (CORE_ADDR);
-};
-
-/* Breakpoint for single step. */
-
-struct single_step_breakpoint
-{
- struct breakpoint base;
-
- /* Thread the reinsert breakpoint belongs to. */
- ptid_t ptid;
-};
-
-/* Return the breakpoint size from its kind. */
-
-static int
-bp_size (struct raw_breakpoint *bp)
-{
- int size = 0;
-
- the_target->sw_breakpoint_from_kind (bp->kind, &size);
- return size;
-}
-
-/* Return the breakpoint opcode from its kind. */
-
-static const gdb_byte *
-bp_opcode (struct raw_breakpoint *bp)
-{
- int size = 0;
-
- return the_target->sw_breakpoint_from_kind (bp->kind, &size);
-}
-
-/* See mem-break.h. */
-
-enum target_hw_bp_type
-raw_bkpt_type_to_target_hw_bp_type (enum raw_bkpt_type raw_type)
-{
- switch (raw_type)
- {
- case raw_bkpt_type_hw:
- return hw_execute;
- case raw_bkpt_type_write_wp:
- return hw_write;
- case raw_bkpt_type_read_wp:
- return hw_read;
- case raw_bkpt_type_access_wp:
- return hw_access;
- default:
- internal_error (__FILE__, __LINE__,
- "bad raw breakpoint type %d", (int) raw_type);
- }
-}
-
-/* See mem-break.h. */
-
-static enum bkpt_type
-Z_packet_to_bkpt_type (char z_type)
-{
- gdb_assert ('0' <= z_type && z_type <= '4');
-
- return (enum bkpt_type) (gdb_breakpoint_Z0 + (z_type - '0'));
-}
-
-/* See mem-break.h. */
-
-enum raw_bkpt_type
-Z_packet_to_raw_bkpt_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_SW_BP:
- return raw_bkpt_type_sw;
- case Z_PACKET_HW_BP:
- return raw_bkpt_type_hw;
- case Z_PACKET_WRITE_WP:
- return raw_bkpt_type_write_wp;
- case Z_PACKET_READ_WP:
- return raw_bkpt_type_read_wp;
- case Z_PACKET_ACCESS_WP:
- return raw_bkpt_type_access_wp;
- default:
- gdb_assert_not_reached ("unhandled Z packet type.");
- }
-}
-
-/* Return true if breakpoint TYPE is a GDB breakpoint. */
-
-static int
-is_gdb_breakpoint (enum bkpt_type type)
-{
- return (type == gdb_breakpoint_Z0
- || type == gdb_breakpoint_Z1
- || type == gdb_breakpoint_Z2
- || type == gdb_breakpoint_Z3
- || type == gdb_breakpoint_Z4);
-}
-
-bool
-any_persistent_commands (process_info *proc)
-{
- struct breakpoint *bp;
- struct point_command_list *cl;
-
- for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
- {
- if (is_gdb_breakpoint (bp->type))
- {
- struct gdb_breakpoint *gdb_bp = (struct gdb_breakpoint *) bp;
-
- for (cl = gdb_bp->command_list; cl != NULL; cl = cl->next)
- if (cl->persistence)
- return true;
- }
- }
-
- return false;
-}
-
-/* Find low-level breakpoint of type TYPE at address ADDR that is not
- insert-disabled. Returns NULL if not found. */
-
-static struct raw_breakpoint *
-find_enabled_raw_code_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if (bp->pc == addr
- && bp->raw_type == type
- && bp->inserted >= 0)
- return bp;
-
- return NULL;
-}
-
-/* Find low-level breakpoint of type TYPE at address ADDR. Returns
- NULL if not found. */
-
-static struct raw_breakpoint *
-find_raw_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type, int kind)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if (bp->pc == addr && bp->raw_type == type && bp->kind == kind)
- return bp;
-
- return NULL;
-}
-
-/* See mem-break.h. */
-
-int
-insert_memory_breakpoint (struct raw_breakpoint *bp)
-{
- unsigned char buf[MAX_BREAKPOINT_LEN];
- int err;
-
- /* Note that there can be fast tracepoint jumps installed in the
- same memory range, so to get at the original memory, we need to
- use read_inferior_memory, which masks those out. */
- err = read_inferior_memory (bp->pc, buf, bp_size (bp));
- if (err != 0)
- {
- if (debug_threads)
- debug_printf ("Failed to read shadow memory of"
- " breakpoint at 0x%s (%s).\n",
- paddress (bp->pc), safe_strerror (err));
- }
- else
- {
- memcpy (bp->old_data, buf, bp_size (bp));
-
- err = (*the_target->write_memory) (bp->pc, bp_opcode (bp),
- bp_size (bp));
- if (err != 0)
- {
- if (debug_threads)
- debug_printf ("Failed to insert breakpoint at 0x%s (%s).\n",
- paddress (bp->pc), safe_strerror (err));
- }
- }
- return err != 0 ? -1 : 0;
-}
-
-/* See mem-break.h */
-
-int
-remove_memory_breakpoint (struct raw_breakpoint *bp)
-{
- unsigned char buf[MAX_BREAKPOINT_LEN];
- int err;
-
- /* Since there can be trap breakpoints inserted in the same address
- range, we use `target_write_memory', which takes care of
- layering breakpoints on top of fast tracepoints, and on top of
- the buffer we pass it. This works because the caller has already
- either unlinked the breakpoint or marked it uninserted. Also
- note that we need to pass the current shadow contents, because
- target_write_memory updates any shadow memory with what we pass
- here, and we want that to be a nop. */
- memcpy (buf, bp->old_data, bp_size (bp));
- err = target_write_memory (bp->pc, buf, bp_size (bp));
- if (err != 0)
- {
- if (debug_threads)
- debug_printf ("Failed to uninsert raw breakpoint "
- "at 0x%s (%s) while deleting it.\n",
- paddress (bp->pc), safe_strerror (err));
- }
- return err != 0 ? -1 : 0;
-}
-
-/* Set a RAW breakpoint of type TYPE and kind KIND at WHERE. On
- success, a pointer to the new breakpoint is returned. On failure,
- returns NULL and writes the error code to *ERR. */
-
-static struct raw_breakpoint *
-set_raw_breakpoint_at (enum raw_bkpt_type type, CORE_ADDR where, int kind,
- int *err)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- if (type == raw_bkpt_type_sw || type == raw_bkpt_type_hw)
- {
- bp = find_enabled_raw_code_breakpoint_at (where, type);
- if (bp != NULL && bp->kind != kind)
- {
- /* A different kind than previously seen. The previous
- breakpoint must be gone then. */
- if (debug_threads)
- debug_printf ("Inconsistent breakpoint kind? Was %d, now %d.\n",
- bp->kind, kind);
- bp->inserted = -1;
- bp = NULL;
- }
- }
- else
- bp = find_raw_breakpoint_at (where, type, kind);
-
- gdb::unique_xmalloc_ptr<struct raw_breakpoint> bp_holder;
- if (bp == NULL)
- {
- bp_holder.reset (XCNEW (struct raw_breakpoint));
- bp = bp_holder.get ();
- bp->pc = where;
- bp->kind = kind;
- bp->raw_type = type;
- }
-
- if (!bp->inserted)
- {
- *err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp);
- if (*err != 0)
- {
- if (debug_threads)
- debug_printf ("Failed to insert breakpoint at 0x%s (%d).\n",
- paddress (where), *err);
-
- return NULL;
- }
-
- bp->inserted = 1;
- }
-
- /* If the breakpoint was allocated above, we know we want to keep it
- now. */
- bp_holder.release ();
-
- /* Link the breakpoint in, if this is the first reference. */
- if (++bp->refcount == 1)
- {
- bp->next = proc->raw_breakpoints;
- proc->raw_breakpoints = bp;
- }
- return bp;
-}
-
-/* Notice that breakpoint traps are always installed on top of fast
- tracepoint jumps. This is even if the fast tracepoint is installed
- at a later time compared to when the breakpoint was installed.
- This means that a stopping breakpoint or tracepoint has higher
- "priority". In turn, this allows having fast and slow tracepoints
- (and breakpoints) at the same address behave correctly. */
-
-
-/* A fast tracepoint jump. */
-
-struct fast_tracepoint_jump
-{
- struct fast_tracepoint_jump *next;
-
- /* A reference count. GDB can install more than one fast tracepoint
- at the same address (each with its own action list, for
- example). */
- int refcount;
-
- /* The fast tracepoint's insertion address. There can only be one
- of these for a given PC. */
- CORE_ADDR pc;
-
- /* Non-zero if this fast tracepoint jump is currently inserted in
- the inferior. */
- int inserted;
-
- /* The length of the jump instruction. */
- int length;
-
- /* A poor-man's flexible array member, holding both the jump
- instruction to insert, and a copy of the instruction that would
- be in memory had not been a jump there (the shadow memory of the
- tracepoint jump). */
- unsigned char insn_and_shadow[0];
-};
-
-/* Fast tracepoint FP's jump instruction to insert. */
-#define fast_tracepoint_jump_insn(fp) \
- ((fp)->insn_and_shadow + 0)
-
-/* The shadow memory of fast tracepoint jump FP. */
-#define fast_tracepoint_jump_shadow(fp) \
- ((fp)->insn_and_shadow + (fp)->length)
-
-
-/* Return the fast tracepoint jump set at WHERE. */
-
-static struct fast_tracepoint_jump *
-find_fast_tracepoint_jump_at (CORE_ADDR where)
-{
- struct process_info *proc = current_process ();
- struct fast_tracepoint_jump *jp;
-
- for (jp = proc->fast_tracepoint_jumps; jp != NULL; jp = jp->next)
- if (jp->pc == where)
- return jp;
-
- return NULL;
-}
-
-int
-fast_tracepoint_jump_here (CORE_ADDR where)
-{
- struct fast_tracepoint_jump *jp = find_fast_tracepoint_jump_at (where);
-
- return (jp != NULL);
-}
-
-int
-delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel)
-{
- struct fast_tracepoint_jump *bp, **bp_link;
- int ret;
- struct process_info *proc = current_process ();
-
- bp = proc->fast_tracepoint_jumps;
- bp_link = &proc->fast_tracepoint_jumps;
-
- while (bp)
- {
- if (bp == todel)
- {
- if (--bp->refcount == 0)
- {
- struct fast_tracepoint_jump *prev_bp_link = *bp_link;
- unsigned char *buf;
-
- /* Unlink it. */
- *bp_link = bp->next;
-
- /* Since there can be breakpoints inserted in the same
- address range, we use `target_write_memory', which
- takes care of layering breakpoints on top of fast
- tracepoints, and on top of the buffer we pass it.
- This works because we've already unlinked the fast
- tracepoint jump above. Also note that we need to
- pass the current shadow contents, because
- target_write_memory updates any shadow memory with
- what we pass here, and we want that to be a nop. */
- buf = (unsigned char *) alloca (bp->length);
- memcpy (buf, fast_tracepoint_jump_shadow (bp), bp->length);
- ret = target_write_memory (bp->pc, buf, bp->length);
- if (ret != 0)
- {
- /* Something went wrong, relink the jump. */
- *bp_link = prev_bp_link;
-
- if (debug_threads)
- debug_printf ("Failed to uninsert fast tracepoint jump "
- "at 0x%s (%s) while deleting it.\n",
- paddress (bp->pc), safe_strerror (ret));
- return ret;
- }
-
- free (bp);
- }
-
- return 0;
- }
- else
- {
- bp_link = &bp->next;
- bp = *bp_link;
- }
- }
-
- warning ("Could not find fast tracepoint jump in list.");
- return ENOENT;
-}
-
-void
-inc_ref_fast_tracepoint_jump (struct fast_tracepoint_jump *jp)
-{
- jp->refcount++;
-}
-
-struct fast_tracepoint_jump *
-set_fast_tracepoint_jump (CORE_ADDR where,
- unsigned char *insn, ULONGEST length)
-{
- struct process_info *proc = current_process ();
- struct fast_tracepoint_jump *jp;
- int err;
- unsigned char *buf;
-
- /* We refcount fast tracepoint jumps. Check if we already know
- about a jump at this address. */
- jp = find_fast_tracepoint_jump_at (where);
- if (jp != NULL)
- {
- jp->refcount++;
- return jp;
- }
-
- /* We don't, so create a new object. Double the length, because the
- flexible array member holds both the jump insn, and the
- shadow. */
- jp = (struct fast_tracepoint_jump *) xcalloc (1, sizeof (*jp) + (length * 2));
- jp->pc = where;
- jp->length = length;
- memcpy (fast_tracepoint_jump_insn (jp), insn, length);
- jp->refcount = 1;
- buf = (unsigned char *) alloca (length);
-
- /* Note that there can be trap breakpoints inserted in the same
- address range. To access the original memory contents, we use
- `read_inferior_memory', which masks out breakpoints. */
- err = read_inferior_memory (where, buf, length);
- if (err != 0)
- {
- if (debug_threads)
- debug_printf ("Failed to read shadow memory of"
- " fast tracepoint at 0x%s (%s).\n",
- paddress (where), safe_strerror (err));
- free (jp);
- return NULL;
- }
- memcpy (fast_tracepoint_jump_shadow (jp), buf, length);
-
- /* Link the jump in. */
- jp->inserted = 1;
- jp->next = proc->fast_tracepoint_jumps;
- proc->fast_tracepoint_jumps = jp;
-
- /* Since there can be trap breakpoints inserted in the same address
- range, we use use `target_write_memory', which takes care of
- layering breakpoints on top of fast tracepoints, on top of the
- buffer we pass it. This works because we've already linked in
- the fast tracepoint jump above. Also note that we need to pass
- the current shadow contents, because target_write_memory
- updates any shadow memory with what we pass here, and we want
- that to be a nop. */
- err = target_write_memory (where, buf, length);
- if (err != 0)
- {
- if (debug_threads)
- debug_printf ("Failed to insert fast tracepoint jump at 0x%s (%s).\n",
- paddress (where), safe_strerror (err));
-
- /* Unlink it. */
- proc->fast_tracepoint_jumps = jp->next;
- free (jp);
-
- return NULL;
- }
-
- return jp;
-}
-
-void
-uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc)
-{
- struct fast_tracepoint_jump *jp;
- int err;
-
- jp = find_fast_tracepoint_jump_at (pc);
- if (jp == NULL)
- {
- /* This can happen when we remove all breakpoints while handling
- a step-over. */
- if (debug_threads)
- debug_printf ("Could not find fast tracepoint jump at 0x%s "
- "in list (uninserting).\n",
- paddress (pc));
- return;
- }
-
- if (jp->inserted)
- {
- unsigned char *buf;
-
- jp->inserted = 0;
-
- /* Since there can be trap breakpoints inserted in the same
- address range, we use use `target_write_memory', which
- takes care of layering breakpoints on top of fast
- tracepoints, and on top of the buffer we pass it. This works
- because we've already marked the fast tracepoint fast
- tracepoint jump uninserted above. Also note that we need to
- pass the current shadow contents, because
- target_write_memory updates any shadow memory with what we
- pass here, and we want that to be a nop. */
- buf = (unsigned char *) alloca (jp->length);
- memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length);
- err = target_write_memory (jp->pc, buf, jp->length);
- if (err != 0)
- {
- jp->inserted = 1;
-
- if (debug_threads)
- debug_printf ("Failed to uninsert fast tracepoint jump at"
- " 0x%s (%s).\n",
- paddress (pc), safe_strerror (err));
- }
- }
-}
-
-void
-reinsert_fast_tracepoint_jumps_at (CORE_ADDR where)
-{
- struct fast_tracepoint_jump *jp;
- int err;
- unsigned char *buf;
-
- jp = find_fast_tracepoint_jump_at (where);
- if (jp == NULL)
- {
- /* This can happen when we remove breakpoints when a tracepoint
- hit causes a tracing stop, while handling a step-over. */
- if (debug_threads)
- debug_printf ("Could not find fast tracepoint jump at 0x%s "
- "in list (reinserting).\n",
- paddress (where));
- return;
- }
-
- if (jp->inserted)
- error ("Jump already inserted at reinsert time.");
-
- jp->inserted = 1;
-
- /* Since there can be trap breakpoints inserted in the same address
- range, we use `target_write_memory', which takes care of
- layering breakpoints on top of fast tracepoints, and on top of
- the buffer we pass it. This works because we've already marked
- the fast tracepoint jump inserted above. Also note that we need
- to pass the current shadow contents, because
- target_write_memory updates any shadow memory with what we pass
- here, and we want that to be a nop. */
- buf = (unsigned char *) alloca (jp->length);
- memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length);
- err = target_write_memory (where, buf, jp->length);
- if (err != 0)
- {
- jp->inserted = 0;
-
- if (debug_threads)
- debug_printf ("Failed to reinsert fast tracepoint jump at"
- " 0x%s (%s).\n",
- paddress (where), safe_strerror (err));
- }
-}
-
-/* Set a high-level breakpoint of type TYPE, with low level type
- RAW_TYPE and kind KIND, at WHERE. On success, a pointer to the new
- breakpoint is returned. On failure, returns NULL and writes the
- error code to *ERR. HANDLER is called when the breakpoint is hit.
- HANDLER should return 1 if the breakpoint should be deleted, 0
- otherwise. */
-
-static struct breakpoint *
-set_breakpoint (enum bkpt_type type, enum raw_bkpt_type raw_type,
- CORE_ADDR where, int kind,
- int (*handler) (CORE_ADDR), int *err)
-{
- struct process_info *proc = current_process ();
- struct breakpoint *bp;
- struct raw_breakpoint *raw;
-
- raw = set_raw_breakpoint_at (raw_type, where, kind, err);
-
- if (raw == NULL)
- {
- /* warn? */
- return NULL;
- }
-
- if (is_gdb_breakpoint (type))
- {
- struct gdb_breakpoint *gdb_bp = XCNEW (struct gdb_breakpoint);
-
- bp = (struct breakpoint *) gdb_bp;
- gdb_assert (handler == NULL);
- }
- else if (type == other_breakpoint)
- {
- struct other_breakpoint *other_bp = XCNEW (struct other_breakpoint);
-
- other_bp->handler = handler;
- bp = (struct breakpoint *) other_bp;
- }
- else if (type == single_step_breakpoint)
- {
- struct single_step_breakpoint *ss_bp
- = XCNEW (struct single_step_breakpoint);
-
- bp = (struct breakpoint *) ss_bp;
- }
- else
- gdb_assert_not_reached ("unhandled breakpoint type");
-
- bp->type = type;
- bp->raw = raw;
-
- bp->next = proc->breakpoints;
- proc->breakpoints = bp;
-
- return bp;
-}
-
-/* Set breakpoint of TYPE on address WHERE with handler HANDLER. */
-
-static struct breakpoint *
-set_breakpoint_type_at (enum bkpt_type type, CORE_ADDR where,
- int (*handler) (CORE_ADDR))
-{
- int err_ignored;
- CORE_ADDR placed_address = where;
- int breakpoint_kind = target_breakpoint_kind_from_pc (&placed_address);
-
- return set_breakpoint (type, raw_bkpt_type_sw,
- placed_address, breakpoint_kind, handler,
- &err_ignored);
-}
-
-/* See mem-break.h */
-
-struct breakpoint *
-set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
-{
- return set_breakpoint_type_at (other_breakpoint, where, handler);
-}
-
-
-static int
-delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel)
-{
- struct raw_breakpoint *bp, **bp_link;
- int ret;
-
- bp = proc->raw_breakpoints;
- bp_link = &proc->raw_breakpoints;
-
- while (bp)
- {
- if (bp == todel)
- {
- if (bp->inserted > 0)
- {
- struct raw_breakpoint *prev_bp_link = *bp_link;
-
- *bp_link = bp->next;
-
- ret = the_target->remove_point (bp->raw_type, bp->pc, bp->kind,
- bp);
- if (ret != 0)
- {
- /* Something went wrong, relink the breakpoint. */
- *bp_link = prev_bp_link;
-
- if (debug_threads)
- debug_printf ("Failed to uninsert raw breakpoint "
- "at 0x%s while deleting it.\n",
- paddress (bp->pc));
- return ret;
- }
- }
- else
- *bp_link = bp->next;
-
- free (bp);
- return 0;
- }
- else
- {
- bp_link = &bp->next;
- bp = *bp_link;
- }
- }
-
- warning ("Could not find raw breakpoint in list.");
- return ENOENT;
-}
-
-static int
-release_breakpoint (struct process_info *proc, struct breakpoint *bp)
-{
- int newrefcount;
- int ret;
-
- newrefcount = bp->raw->refcount - 1;
- if (newrefcount == 0)
- {
- ret = delete_raw_breakpoint (proc, bp->raw);
- if (ret != 0)
- return ret;
- }
- else
- bp->raw->refcount = newrefcount;
-
- free (bp);
-
- return 0;
-}
-
-static int
-delete_breakpoint_1 (struct process_info *proc, struct breakpoint *todel)
-{
- struct breakpoint *bp, **bp_link;
- int err;
-
- bp = proc->breakpoints;
- bp_link = &proc->breakpoints;
-
- while (bp)
- {
- if (bp == todel)
- {
- *bp_link = bp->next;
-
- err = release_breakpoint (proc, bp);
- if (err != 0)
- return err;
-
- bp = *bp_link;
- return 0;
- }
- else
- {
- bp_link = &bp->next;
- bp = *bp_link;
- }
- }
-
- warning ("Could not find breakpoint in list.");
- return ENOENT;
-}
-
-int
-delete_breakpoint (struct breakpoint *todel)
-{
- struct process_info *proc = current_process ();
- return delete_breakpoint_1 (proc, todel);
-}
-
-/* Locate a GDB breakpoint of type Z_TYPE and kind KIND placed at
- address ADDR and return a pointer to its structure. If KIND is -1,
- the breakpoint's kind is ignored. */
-
-static struct gdb_breakpoint *
-find_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind)
-{
- struct process_info *proc = current_process ();
- struct breakpoint *bp;
- enum bkpt_type type = Z_packet_to_bkpt_type (z_type);
-
- for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
- if (bp->type == type && bp->raw->pc == addr
- && (kind == -1 || bp->raw->kind == kind))
- return (struct gdb_breakpoint *) bp;
-
- return NULL;
-}
-
-static int
-z_type_supported (char z_type)
-{
- return (z_type >= '0' && z_type <= '4'
- && the_target->supports_z_point_type != NULL
- && the_target->supports_z_point_type (z_type));
-}
-
-/* Create a new GDB breakpoint of type Z_TYPE at ADDR with kind KIND.
- Returns a pointer to the newly created breakpoint on success. On
- failure returns NULL and sets *ERR to either -1 for error, or 1 if
- Z_TYPE breakpoints are not supported on this target. */
-
-static struct gdb_breakpoint *
-set_gdb_breakpoint_1 (char z_type, CORE_ADDR addr, int kind, int *err)
-{
- struct gdb_breakpoint *bp;
- enum bkpt_type type;
- enum raw_bkpt_type raw_type;
-
- /* If we see GDB inserting a second code breakpoint at the same
- address, then either: GDB is updating the breakpoint's conditions
- or commands; or, the first breakpoint must have disappeared due
- to a shared library unload. On targets where the shared
- libraries are handled by userspace, like SVR4, for example,
- GDBserver can't tell if a library was loaded or unloaded. Since
- we refcount raw breakpoints, we must be careful to make sure GDB
- breakpoints never contribute more than one reference. if we
- didn't do this, in case the previous breakpoint is gone due to a
- shared library unload, we'd just increase the refcount of the
- previous breakpoint at this address, but the trap was not planted
- in the inferior anymore, thus the breakpoint would never be hit.
- Note this must be careful to not create a window where
- breakpoints are removed from the target, for non-stop, in case
- the target can poke at memory while the program is running. */
- if (z_type == Z_PACKET_SW_BP
- || z_type == Z_PACKET_HW_BP)
- {
- bp = find_gdb_breakpoint (z_type, addr, -1);
-
- if (bp != NULL)
- {
- if (bp->base.raw->kind != kind)
- {
- /* A different kind than previously seen. The previous
- breakpoint must be gone then. */
- bp->base.raw->inserted = -1;
- delete_breakpoint ((struct breakpoint *) bp);
- bp = NULL;
- }
- else if (z_type == Z_PACKET_SW_BP)
- {
- /* Check if the breakpoint is actually gone from the
- target, due to an solib unload, for example. Might
- as well validate _all_ breakpoints. */
- validate_breakpoints ();
-
- /* Breakpoints that don't pass validation are
- deleted. */
- bp = find_gdb_breakpoint (z_type, addr, -1);
- }
- }
- }
- else
- {
- /* Data breakpoints for the same address but different kind are
- expected. GDB doesn't merge these. The backend gets to do
- that if it wants/can. */
- bp = find_gdb_breakpoint (z_type, addr, kind);
- }
-
- if (bp != NULL)
- {
- /* We already know about this breakpoint, there's nothing else
- to do - GDB's reference is already accounted for. Note that
- whether the breakpoint inserted is left as is - we may be
- stepping over it, for example, in which case we don't want to
- force-reinsert it. */
- return bp;
- }
-
- raw_type = Z_packet_to_raw_bkpt_type (z_type);
- type = Z_packet_to_bkpt_type (z_type);
- return (struct gdb_breakpoint *) set_breakpoint (type, raw_type, addr,
- kind, NULL, err);
-}
-
-static int
-check_gdb_bp_preconditions (char z_type, int *err)
-{
- /* As software/memory breakpoints work by poking at memory, we need
- to prepare to access memory. If that operation fails, we need to
- return error. Seeing an error, if this is the first breakpoint
- of that type that GDB tries to insert, GDB would then assume the
- breakpoint type is supported, but it may actually not be. So we
- need to check whether the type is supported at all before
- preparing to access memory. */
- if (!z_type_supported (z_type))
- {
- *err = 1;
- return 0;
- }
-
- return 1;
-}
-
-/* See mem-break.h. This is a wrapper for set_gdb_breakpoint_1 that
- knows to prepare to access memory for Z0 breakpoints. */
-
-struct gdb_breakpoint *
-set_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind, int *err)
-{
- struct gdb_breakpoint *bp;
-
- if (!check_gdb_bp_preconditions (z_type, err))
- return NULL;
-
- /* If inserting a software/memory breakpoint, need to prepare to
- access memory. */
- if (z_type == Z_PACKET_SW_BP)
- {
- if (prepare_to_access_memory () != 0)
- {
- *err = -1;
- return NULL;
- }
- }
-
- bp = set_gdb_breakpoint_1 (z_type, addr, kind, err);
-
- if (z_type == Z_PACKET_SW_BP)
- done_accessing_memory ();
-
- return bp;
-}
-
-/* Delete a GDB breakpoint of type Z_TYPE and kind KIND previously
- inserted at ADDR with set_gdb_breakpoint_at. Returns 0 on success,
- -1 on error, and 1 if Z_TYPE breakpoints are not supported on this
- target. */
-
-static int
-delete_gdb_breakpoint_1 (char z_type, CORE_ADDR addr, int kind)
-{
- struct gdb_breakpoint *bp;
- int err;
-
- bp = find_gdb_breakpoint (z_type, addr, kind);
- if (bp == NULL)
- return -1;
-
- /* Before deleting the breakpoint, make sure to free its condition
- and command lists. */
- clear_breakpoint_conditions_and_commands (bp);
- err = delete_breakpoint ((struct breakpoint *) bp);
- if (err != 0)
- return -1;
-
- return 0;
-}
-
-/* See mem-break.h. This is a wrapper for delete_gdb_breakpoint that
- knows to prepare to access memory for Z0 breakpoints. */
-
-int
-delete_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind)
-{
- int ret;
-
- if (!check_gdb_bp_preconditions (z_type, &ret))
- return ret;
-
- /* If inserting a software/memory breakpoint, need to prepare to
- access memory. */
- if (z_type == Z_PACKET_SW_BP)
- {
- int err;
-
- err = prepare_to_access_memory ();
- if (err != 0)
- return -1;
- }
-
- ret = delete_gdb_breakpoint_1 (z_type, addr, kind);
-
- if (z_type == Z_PACKET_SW_BP)
- done_accessing_memory ();
-
- return ret;
-}
-
-/* Clear all conditions associated with a breakpoint. */
-
-static void
-clear_breakpoint_conditions (struct gdb_breakpoint *bp)
-{
- struct point_cond_list *cond;
-
- if (bp->cond_list == NULL)
- return;
-
- cond = bp->cond_list;
-
- while (cond != NULL)
- {
- struct point_cond_list *cond_next;
-
- cond_next = cond->next;
- gdb_free_agent_expr (cond->cond);
- free (cond);
- cond = cond_next;
- }
-
- bp->cond_list = NULL;
-}
-
-/* Clear all commands associated with a breakpoint. */
-
-static void
-clear_breakpoint_commands (struct gdb_breakpoint *bp)
-{
- struct point_command_list *cmd;
-
- if (bp->command_list == NULL)
- return;
-
- cmd = bp->command_list;
-
- while (cmd != NULL)
- {
- struct point_command_list *cmd_next;
-
- cmd_next = cmd->next;
- gdb_free_agent_expr (cmd->cmd);
- free (cmd);
- cmd = cmd_next;
- }
-
- bp->command_list = NULL;
-}
-
-void
-clear_breakpoint_conditions_and_commands (struct gdb_breakpoint *bp)
-{
- clear_breakpoint_conditions (bp);
- clear_breakpoint_commands (bp);
-}
-
-/* Add condition CONDITION to GDBserver's breakpoint BP. */
-
-static void
-add_condition_to_breakpoint (struct gdb_breakpoint *bp,
- struct agent_expr *condition)
-{
- struct point_cond_list *new_cond;
-
- /* Create new condition. */
- new_cond = XCNEW (struct point_cond_list);
- new_cond->cond = condition;
-
- /* Add condition to the list. */
- new_cond->next = bp->cond_list;
- bp->cond_list = new_cond;
-}
-
-/* Add a target-side condition CONDITION to a breakpoint. */
-
-int
-add_breakpoint_condition (struct gdb_breakpoint *bp, const char **condition)
-{
- const char *actparm = *condition;
- struct agent_expr *cond;
-
- if (condition == NULL)
- return 1;
-
- if (bp == NULL)
- return 0;
-
- cond = gdb_parse_agent_expr (&actparm);
-
- if (cond == NULL)
- {
- warning ("Condition evaluation failed. Assuming unconditional.");
- return 0;
- }
-
- add_condition_to_breakpoint (bp, cond);
-
- *condition = actparm;
-
- return 1;
-}
-
-/* Evaluate condition (if any) at breakpoint BP. Return 1 if
- true and 0 otherwise. */
-
-static int
-gdb_condition_true_at_breakpoint_z_type (char z_type, CORE_ADDR addr)
-{
- /* Fetch registers for the current inferior. */
- struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1);
- ULONGEST value = 0;
- struct point_cond_list *cl;
- int err = 0;
- struct eval_agent_expr_context ctx;
-
- if (bp == NULL)
- return 0;
-
- /* Check if the breakpoint is unconditional. If it is,
- the condition always evaluates to TRUE. */
- if (bp->cond_list == NULL)
- return 1;
-
- ctx.regcache = get_thread_regcache (current_thread, 1);
- ctx.tframe = NULL;
- ctx.tpoint = NULL;
-
- /* Evaluate each condition in the breakpoint's list of conditions.
- Return true if any of the conditions evaluates to TRUE.
-
- If we failed to evaluate the expression, TRUE is returned. This
- forces GDB to reevaluate the conditions. */
- for (cl = bp->cond_list;
- cl && !value && !err; cl = cl->next)
- {
- /* Evaluate the condition. */
- err = gdb_eval_agent_expr (&ctx, cl->cond, &value);
- }
-
- if (err)
- return 1;
-
- return (value != 0);
-}
-
-int
-gdb_condition_true_at_breakpoint (CORE_ADDR where)
-{
- /* Only check code (software or hardware) breakpoints. */
- return (gdb_condition_true_at_breakpoint_z_type (Z_PACKET_SW_BP, where)
- || gdb_condition_true_at_breakpoint_z_type (Z_PACKET_HW_BP, where));
-}
-
-/* Add commands COMMANDS to GDBserver's breakpoint BP. */
-
-static void
-add_commands_to_breakpoint (struct gdb_breakpoint *bp,
- struct agent_expr *commands, int persist)
-{
- struct point_command_list *new_cmd;
-
- /* Create new command. */
- new_cmd = XCNEW (struct point_command_list);
- new_cmd->cmd = commands;
- new_cmd->persistence = persist;
-
- /* Add commands to the list. */
- new_cmd->next = bp->command_list;
- bp->command_list = new_cmd;
-}
-
-/* Add a target-side command COMMAND to the breakpoint at ADDR. */
-
-int
-add_breakpoint_commands (struct gdb_breakpoint *bp, const char **command,
- int persist)
-{
- const char *actparm = *command;
- struct agent_expr *cmd;
-
- if (command == NULL)
- return 1;
-
- if (bp == NULL)
- return 0;
-
- cmd = gdb_parse_agent_expr (&actparm);
-
- if (cmd == NULL)
- {
- warning ("Command evaluation failed. Disabling.");
- return 0;
- }
-
- add_commands_to_breakpoint (bp, cmd, persist);
-
- *command = actparm;
-
- return 1;
-}
-
-/* Return true if there are no commands to run at this location,
- which likely means we want to report back to GDB. */
-
-static int
-gdb_no_commands_at_breakpoint_z_type (char z_type, CORE_ADDR addr)
-{
- struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1);
-
- if (bp == NULL)
- return 1;
-
- if (debug_threads)
- debug_printf ("at 0x%s, type Z%c, bp command_list is 0x%s\n",
- paddress (addr), z_type,
- phex_nz ((uintptr_t) bp->command_list, 0));
- return (bp->command_list == NULL);
-}
-
-/* Return true if there are no commands to run at this location,
- which likely means we want to report back to GDB. */
-
-int
-gdb_no_commands_at_breakpoint (CORE_ADDR where)
-{
- /* Only check code (software or hardware) breakpoints. */
- return (gdb_no_commands_at_breakpoint_z_type (Z_PACKET_SW_BP, where)
- && gdb_no_commands_at_breakpoint_z_type (Z_PACKET_HW_BP, where));
-}
-
-/* Run a breakpoint's commands. Returns 0 if there was a problem
- running any command, 1 otherwise. */
-
-static int
-run_breakpoint_commands_z_type (char z_type, CORE_ADDR addr)
-{
- /* Fetch registers for the current inferior. */
- struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1);
- ULONGEST value = 0;
- struct point_command_list *cl;
- int err = 0;
- struct eval_agent_expr_context ctx;
-
- if (bp == NULL)
- return 1;
-
- ctx.regcache = get_thread_regcache (current_thread, 1);
- ctx.tframe = NULL;
- ctx.tpoint = NULL;
-
- for (cl = bp->command_list;
- cl && !value && !err; cl = cl->next)
- {
- /* Run the command. */
- err = gdb_eval_agent_expr (&ctx, cl->cmd, &value);
-
- /* If one command has a problem, stop digging the hole deeper. */
- if (err)
- return 0;
- }
-
- return 1;
-}
-
-void
-run_breakpoint_commands (CORE_ADDR where)
-{
- /* Only check code (software or hardware) breakpoints. If one
- command has a problem, stop digging the hole deeper. */
- if (run_breakpoint_commands_z_type (Z_PACKET_SW_BP, where))
- run_breakpoint_commands_z_type (Z_PACKET_HW_BP, where);
-}
-
-/* See mem-break.h. */
-
-int
-gdb_breakpoint_here (CORE_ADDR where)
-{
- /* Only check code (software or hardware) breakpoints. */
- return (find_gdb_breakpoint (Z_PACKET_SW_BP, where, -1) != NULL
- || find_gdb_breakpoint (Z_PACKET_HW_BP, where, -1) != NULL);
-}
-
-void
-set_single_step_breakpoint (CORE_ADDR stop_at, ptid_t ptid)
-{
- struct single_step_breakpoint *bp;
-
- gdb_assert (current_ptid.pid () == ptid.pid ());
-
- bp = (struct single_step_breakpoint *) set_breakpoint_type_at (single_step_breakpoint,
- stop_at, NULL);
- bp->ptid = ptid;
-}
-
-void
-delete_single_step_breakpoints (struct thread_info *thread)
-{
- struct process_info *proc = get_thread_process (thread);
- struct breakpoint *bp, **bp_link;
-
- bp = proc->breakpoints;
- bp_link = &proc->breakpoints;
-
- while (bp)
- {
- if (bp->type == single_step_breakpoint
- && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
- {
- struct thread_info *saved_thread = current_thread;
-
- current_thread = thread;
- *bp_link = bp->next;
- release_breakpoint (proc, bp);
- bp = *bp_link;
- current_thread = saved_thread;
- }
- else
- {
- bp_link = &bp->next;
- bp = *bp_link;
- }
- }
-}
-
-static void
-uninsert_raw_breakpoint (struct raw_breakpoint *bp)
-{
- if (bp->inserted < 0)
- {
- if (debug_threads)
- debug_printf ("Breakpoint at %s is marked insert-disabled.\n",
- paddress (bp->pc));
- }
- else if (bp->inserted > 0)
- {
- int err;
-
- bp->inserted = 0;
-
- err = the_target->remove_point (bp->raw_type, bp->pc, bp->kind, bp);
- if (err != 0)
- {
- bp->inserted = 1;
-
- if (debug_threads)
- debug_printf ("Failed to uninsert raw breakpoint at 0x%s.\n",
- paddress (bp->pc));
- }
- }
-}
-
-void
-uninsert_breakpoints_at (CORE_ADDR pc)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
- int found = 0;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if ((bp->raw_type == raw_bkpt_type_sw
- || bp->raw_type == raw_bkpt_type_hw)
- && bp->pc == pc)
- {
- found = 1;
-
- if (bp->inserted)
- uninsert_raw_breakpoint (bp);
- }
-
- if (!found)
- {
- /* This can happen when we remove all breakpoints while handling
- a step-over. */
- if (debug_threads)
- debug_printf ("Could not find breakpoint at 0x%s "
- "in list (uninserting).\n",
- paddress (pc));
- }
-}
-
-void
-uninsert_all_breakpoints (void)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if ((bp->raw_type == raw_bkpt_type_sw
- || bp->raw_type == raw_bkpt_type_hw)
- && bp->inserted)
- uninsert_raw_breakpoint (bp);
-}
-
-void
-uninsert_single_step_breakpoints (struct thread_info *thread)
-{
- struct process_info *proc = get_thread_process (thread);
- struct breakpoint *bp;
-
- for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
- {
- if (bp->type == single_step_breakpoint
- && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
- {
- gdb_assert (bp->raw->inserted > 0);
-
- /* Only uninsert the raw breakpoint if it only belongs to a
- reinsert breakpoint. */
- if (bp->raw->refcount == 1)
- {
- struct thread_info *saved_thread = current_thread;
-
- current_thread = thread;
- uninsert_raw_breakpoint (bp->raw);
- current_thread = saved_thread;
- }
- }
- }
-}
-
-static void
-reinsert_raw_breakpoint (struct raw_breakpoint *bp)
-{
- int err;
-
- if (bp->inserted)
- return;
-
- err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp);
- if (err == 0)
- bp->inserted = 1;
- else if (debug_threads)
- debug_printf ("Failed to reinsert breakpoint at 0x%s (%d).\n",
- paddress (bp->pc), err);
-}
-
-void
-reinsert_breakpoints_at (CORE_ADDR pc)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
- int found = 0;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if ((bp->raw_type == raw_bkpt_type_sw
- || bp->raw_type == raw_bkpt_type_hw)
- && bp->pc == pc)
- {
- found = 1;
-
- reinsert_raw_breakpoint (bp);
- }
-
- if (!found)
- {
- /* This can happen when we remove all breakpoints while handling
- a step-over. */
- if (debug_threads)
- debug_printf ("Could not find raw breakpoint at 0x%s "
- "in list (reinserting).\n",
- paddress (pc));
- }
-}
-
-int
-has_single_step_breakpoints (struct thread_info *thread)
-{
- struct process_info *proc = get_thread_process (thread);
- struct breakpoint *bp, **bp_link;
-
- bp = proc->breakpoints;
- bp_link = &proc->breakpoints;
-
- while (bp)
- {
- if (bp->type == single_step_breakpoint
- && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
- return 1;
- else
- {
- bp_link = &bp->next;
- bp = *bp_link;
- }
- }
-
- return 0;
-}
-
-void
-reinsert_all_breakpoints (void)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if ((bp->raw_type == raw_bkpt_type_sw
- || bp->raw_type == raw_bkpt_type_hw)
- && !bp->inserted)
- reinsert_raw_breakpoint (bp);
-}
-
-void
-reinsert_single_step_breakpoints (struct thread_info *thread)
-{
- struct process_info *proc = get_thread_process (thread);
- struct breakpoint *bp;
-
- for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
- {
- if (bp->type == single_step_breakpoint
- && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
- {
- gdb_assert (bp->raw->inserted > 0);
-
- if (bp->raw->refcount == 1)
- {
- struct thread_info *saved_thread = current_thread;
-
- current_thread = thread;
- reinsert_raw_breakpoint (bp->raw);
- current_thread = saved_thread;
- }
- }
- }
-}
-
-void
-check_breakpoints (CORE_ADDR stop_pc)
-{
- struct process_info *proc = current_process ();
- struct breakpoint *bp, **bp_link;
-
- bp = proc->breakpoints;
- bp_link = &proc->breakpoints;
-
- while (bp)
- {
- struct raw_breakpoint *raw = bp->raw;
-
- if ((raw->raw_type == raw_bkpt_type_sw
- || raw->raw_type == raw_bkpt_type_hw)
- && raw->pc == stop_pc)
- {
- if (!raw->inserted)
- {
- warning ("Hit a removed breakpoint?");
- return;
- }
-
- if (bp->type == other_breakpoint)
- {
- struct other_breakpoint *other_bp
- = (struct other_breakpoint *) bp;
-
- if (other_bp->handler != NULL && (*other_bp->handler) (stop_pc))
- {
- *bp_link = bp->next;
-
- release_breakpoint (proc, bp);
-
- bp = *bp_link;
- continue;
- }
- }
- }
-
- bp_link = &bp->next;
- bp = *bp_link;
- }
-}
-
-int
-breakpoint_here (CORE_ADDR addr)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if ((bp->raw_type == raw_bkpt_type_sw
- || bp->raw_type == raw_bkpt_type_hw)
- && bp->pc == addr)
- return 1;
-
- return 0;
-}
-
-int
-breakpoint_inserted_here (CORE_ADDR addr)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if ((bp->raw_type == raw_bkpt_type_sw
- || bp->raw_type == raw_bkpt_type_hw)
- && bp->pc == addr
- && bp->inserted)
- return 1;
-
- return 0;
-}
-
-/* See mem-break.h. */
-
-int
-software_breakpoint_inserted_here (CORE_ADDR addr)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if (bp->raw_type == raw_bkpt_type_sw
- && bp->pc == addr
- && bp->inserted)
- return 1;
-
- return 0;
-}
-
-/* See mem-break.h. */
-
-int
-hardware_breakpoint_inserted_here (CORE_ADDR addr)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp;
-
- for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
- if (bp->raw_type == raw_bkpt_type_hw
- && bp->pc == addr
- && bp->inserted)
- return 1;
-
- return 0;
-}
-
-/* See mem-break.h. */
-
-int
-single_step_breakpoint_inserted_here (CORE_ADDR addr)
-{
- struct process_info *proc = current_process ();
- struct breakpoint *bp;
-
- for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
- if (bp->type == single_step_breakpoint
- && bp->raw->pc == addr
- && bp->raw->inserted)
- return 1;
-
- return 0;
-}
-
-static int
-validate_inserted_breakpoint (struct raw_breakpoint *bp)
-{
- unsigned char *buf;
- int err;
-
- gdb_assert (bp->inserted);
- gdb_assert (bp->raw_type == raw_bkpt_type_sw);
-
- buf = (unsigned char *) alloca (bp_size (bp));
- err = (*the_target->read_memory) (bp->pc, buf, bp_size (bp));
- if (err || memcmp (buf, bp_opcode (bp), bp_size (bp)) != 0)
- {
- /* Tag it as gone. */
- bp->inserted = -1;
- return 0;
- }
-
- return 1;
-}
-
-static void
-delete_disabled_breakpoints (void)
-{
- struct process_info *proc = current_process ();
- struct breakpoint *bp, *next;
-
- for (bp = proc->breakpoints; bp != NULL; bp = next)
- {
- next = bp->next;
- if (bp->raw->inserted < 0)
- {
- /* If single_step_breakpoints become disabled, that means the
- manipulations (insertion and removal) of them are wrong. */
- gdb_assert (bp->type != single_step_breakpoint);
- delete_breakpoint_1 (proc, bp);
- }
- }
-}
-
-/* Check if breakpoints we inserted still appear to be inserted. They
- may disappear due to a shared library unload, and worse, a new
- shared library may be reloaded at the same address as the
- previously unloaded one. If that happens, we should make sure that
- the shadow memory of the old breakpoints isn't used when reading or
- writing memory. */
-
-void
-validate_breakpoints (void)
-{
- struct process_info *proc = current_process ();
- struct breakpoint *bp;
-
- for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
- {
- struct raw_breakpoint *raw = bp->raw;
-
- if (raw->raw_type == raw_bkpt_type_sw && raw->inserted > 0)
- validate_inserted_breakpoint (raw);
- }
-
- delete_disabled_breakpoints ();
-}
-
-void
-check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp = proc->raw_breakpoints;
- struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
- CORE_ADDR mem_end = mem_addr + mem_len;
- int disabled_one = 0;
-
- for (; jp != NULL; jp = jp->next)
- {
- CORE_ADDR bp_end = jp->pc + jp->length;
- CORE_ADDR start, end;
- int copy_offset, copy_len, buf_offset;
-
- gdb_assert (fast_tracepoint_jump_shadow (jp) >= buf + mem_len
- || buf >= fast_tracepoint_jump_shadow (jp) + (jp)->length);
-
- if (mem_addr >= bp_end)
- continue;
- if (jp->pc >= mem_end)
- continue;
-
- start = jp->pc;
- if (mem_addr > start)
- start = mem_addr;
-
- end = bp_end;
- if (end > mem_end)
- end = mem_end;
-
- copy_len = end - start;
- copy_offset = start - jp->pc;
- buf_offset = start - mem_addr;
-
- if (jp->inserted)
- memcpy (buf + buf_offset,
- fast_tracepoint_jump_shadow (jp) + copy_offset,
- copy_len);
- }
-
- for (; bp != NULL; bp = bp->next)
- {
- CORE_ADDR bp_end = bp->pc + bp_size (bp);
- CORE_ADDR start, end;
- int copy_offset, copy_len, buf_offset;
-
- if (bp->raw_type != raw_bkpt_type_sw)
- continue;
-
- gdb_assert (bp->old_data >= buf + mem_len
- || buf >= &bp->old_data[sizeof (bp->old_data)]);
-
- if (mem_addr >= bp_end)
- continue;
- if (bp->pc >= mem_end)
- continue;
-
- start = bp->pc;
- if (mem_addr > start)
- start = mem_addr;
-
- end = bp_end;
- if (end > mem_end)
- end = mem_end;
-
- copy_len = end - start;
- copy_offset = start - bp->pc;
- buf_offset = start - mem_addr;
-
- if (bp->inserted > 0)
- {
- if (validate_inserted_breakpoint (bp))
- memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len);
- else
- disabled_one = 1;
- }
- }
-
- if (disabled_one)
- delete_disabled_breakpoints ();
-}
-
-void
-check_mem_write (CORE_ADDR mem_addr, unsigned char *buf,
- const unsigned char *myaddr, int mem_len)
-{
- struct process_info *proc = current_process ();
- struct raw_breakpoint *bp = proc->raw_breakpoints;
- struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
- CORE_ADDR mem_end = mem_addr + mem_len;
- int disabled_one = 0;
-
- /* First fast tracepoint jumps, then breakpoint traps on top. */
-
- for (; jp != NULL; jp = jp->next)
- {
- CORE_ADDR jp_end = jp->pc + jp->length;
- CORE_ADDR start, end;
- int copy_offset, copy_len, buf_offset;
-
- gdb_assert (fast_tracepoint_jump_shadow (jp) >= myaddr + mem_len
- || myaddr >= fast_tracepoint_jump_shadow (jp) + (jp)->length);
- gdb_assert (fast_tracepoint_jump_insn (jp) >= buf + mem_len
- || buf >= fast_tracepoint_jump_insn (jp) + (jp)->length);
-
- if (mem_addr >= jp_end)
- continue;
- if (jp->pc >= mem_end)
- continue;
-
- start = jp->pc;
- if (mem_addr > start)
- start = mem_addr;
-
- end = jp_end;
- if (end > mem_end)
- end = mem_end;
-
- copy_len = end - start;
- copy_offset = start - jp->pc;
- buf_offset = start - mem_addr;
-
- memcpy (fast_tracepoint_jump_shadow (jp) + copy_offset,
- myaddr + buf_offset, copy_len);
- if (jp->inserted)
- memcpy (buf + buf_offset,
- fast_tracepoint_jump_insn (jp) + copy_offset, copy_len);
- }
-
- for (; bp != NULL; bp = bp->next)
- {
- CORE_ADDR bp_end = bp->pc + bp_size (bp);
- CORE_ADDR start, end;
- int copy_offset, copy_len, buf_offset;
-
- if (bp->raw_type != raw_bkpt_type_sw)
- continue;
-
- gdb_assert (bp->old_data >= myaddr + mem_len
- || myaddr >= &bp->old_data[sizeof (bp->old_data)]);
-
- if (mem_addr >= bp_end)
- continue;
- if (bp->pc >= mem_end)
- continue;
-
- start = bp->pc;
- if (mem_addr > start)
- start = mem_addr;
-
- end = bp_end;
- if (end > mem_end)
- end = mem_end;
-
- copy_len = end - start;
- copy_offset = start - bp->pc;
- buf_offset = start - mem_addr;
-
- memcpy (bp->old_data + copy_offset, myaddr + buf_offset, copy_len);
- if (bp->inserted > 0)
- {
- if (validate_inserted_breakpoint (bp))
- memcpy (buf + buf_offset, bp_opcode (bp) + copy_offset, copy_len);
- else
- disabled_one = 1;
- }
- }
-
- if (disabled_one)
- delete_disabled_breakpoints ();
-}
-
-/* Delete all breakpoints, and un-insert them from the inferior. */
-
-void
-delete_all_breakpoints (void)
-{
- struct process_info *proc = current_process ();
-
- while (proc->breakpoints)
- delete_breakpoint_1 (proc, proc->breakpoints);
-}
-
-/* Clear the "inserted" flag in all breakpoints. */
-
-void
-mark_breakpoints_out (struct process_info *proc)
-{
- struct raw_breakpoint *raw_bp;
-
- for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next)
- raw_bp->inserted = 0;
-}
-
-/* Release all breakpoints, but do not try to un-insert them from the
- inferior. */
-
-void
-free_all_breakpoints (struct process_info *proc)
-{
- mark_breakpoints_out (proc);
-
- /* Note: use PROC explicitly instead of deferring to
- delete_all_breakpoints --- CURRENT_INFERIOR may already have been
- released when we get here. There should be no call to
- current_process from here on. */
- while (proc->breakpoints)
- delete_breakpoint_1 (proc, proc->breakpoints);
-}
-
-/* Clone an agent expression. */
-
-static struct agent_expr *
-clone_agent_expr (const struct agent_expr *src_ax)
-{
- struct agent_expr *ax;
-
- ax = XCNEW (struct agent_expr);
- ax->length = src_ax->length;
- ax->bytes = (unsigned char *) xcalloc (ax->length, 1);
- memcpy (ax->bytes, src_ax->bytes, ax->length);
- return ax;
-}
-
-/* Deep-copy the contents of one breakpoint to another. */
-
-static struct breakpoint *
-clone_one_breakpoint (const struct breakpoint *src, ptid_t ptid)
-{
- struct breakpoint *dest;
- struct raw_breakpoint *dest_raw;
-
- /* Clone the raw breakpoint. */
- dest_raw = XCNEW (struct raw_breakpoint);
- dest_raw->raw_type = src->raw->raw_type;
- dest_raw->refcount = src->raw->refcount;
- dest_raw->pc = src->raw->pc;
- dest_raw->kind = src->raw->kind;
- memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
- dest_raw->inserted = src->raw->inserted;
-
- /* Clone the high-level breakpoint. */
- if (is_gdb_breakpoint (src->type))
- {
- struct gdb_breakpoint *gdb_dest = XCNEW (struct gdb_breakpoint);
- struct point_cond_list *current_cond;
- struct point_cond_list *new_cond;
- struct point_cond_list *cond_tail = NULL;
- struct point_command_list *current_cmd;
- struct point_command_list *new_cmd;
- struct point_command_list *cmd_tail = NULL;
-
- /* Clone the condition list. */
- for (current_cond = ((struct gdb_breakpoint *) src)->cond_list;
- current_cond != NULL;
- current_cond = current_cond->next)
- {
- new_cond = XCNEW (struct point_cond_list);
- new_cond->cond = clone_agent_expr (current_cond->cond);
- APPEND_TO_LIST (&gdb_dest->cond_list, new_cond, cond_tail);
- }
-
- /* Clone the command list. */
- for (current_cmd = ((struct gdb_breakpoint *) src)->command_list;
- current_cmd != NULL;
- current_cmd = current_cmd->next)
- {
- new_cmd = XCNEW (struct point_command_list);
- new_cmd->cmd = clone_agent_expr (current_cmd->cmd);
- new_cmd->persistence = current_cmd->persistence;
- APPEND_TO_LIST (&gdb_dest->command_list, new_cmd, cmd_tail);
- }
-
- dest = (struct breakpoint *) gdb_dest;
- }
- else if (src->type == other_breakpoint)
- {
- struct other_breakpoint *other_dest = XCNEW (struct other_breakpoint);
-
- other_dest->handler = ((struct other_breakpoint *) src)->handler;
- dest = (struct breakpoint *) other_dest;
- }
- else if (src->type == single_step_breakpoint)
- {
- struct single_step_breakpoint *ss_dest
- = XCNEW (struct single_step_breakpoint);
-
- dest = (struct breakpoint *) ss_dest;
- /* Since single-step breakpoint is thread specific, don't copy
- thread id from SRC, use ID instead. */
- ss_dest->ptid = ptid;
- }
- else
- gdb_assert_not_reached ("unhandled breakpoint type");
-
- dest->type = src->type;
- dest->raw = dest_raw;
-
- return dest;
-}
-
-/* See mem-break.h. */
-
-void
-clone_all_breakpoints (struct thread_info *child_thread,
- const struct thread_info *parent_thread)
-{
- const struct breakpoint *bp;
- struct breakpoint *new_bkpt;
- struct breakpoint *bkpt_tail = NULL;
- struct raw_breakpoint *raw_bkpt_tail = NULL;
- struct process_info *child_proc = get_thread_process (child_thread);
- struct process_info *parent_proc = get_thread_process (parent_thread);
- struct breakpoint **new_list = &child_proc->breakpoints;
- struct raw_breakpoint **new_raw_list = &child_proc->raw_breakpoints;
-
- for (bp = parent_proc->breakpoints; bp != NULL; bp = bp->next)
- {
- new_bkpt = clone_one_breakpoint (bp, ptid_of (child_thread));
- APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
- APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
- }
-}
--- /dev/null
+/* Memory breakpoint operations for the remote server for GDB.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ 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 "regcache.h"
+#include "ax.h"
+
+#define MAX_BREAKPOINT_LEN 8
+
+/* Helper macro used in loops that append multiple items to a singly-linked
+ list instead of inserting items at the head of the list, as, say, in the
+ breakpoint lists. LISTPP is a pointer to the pointer that is the head of
+ the new list. ITEMP is a pointer to the item to be added to the list.
+ TAILP must be defined to be the same type as ITEMP, and initialized to
+ NULL. */
+
+#define APPEND_TO_LIST(listpp, itemp, tailp) \
+ do \
+ { \
+ if ((tailp) == NULL) \
+ *(listpp) = (itemp); \
+ else \
+ (tailp)->next = (itemp); \
+ (tailp) = (itemp); \
+ } \
+ while (0)
+
+/* GDB will never try to install multiple breakpoints at the same
+ address. However, we can see GDB requesting to insert a breakpoint
+ at an address is had already inserted one previously in a few
+ situations.
+
+ - The RSP documentation on Z packets says that to avoid potential
+ problems with duplicate packets, the operations should be
+ implemented in an idempotent way.
+
+ - A breakpoint is set at ADDR, an address in a shared library.
+ Then the shared library is unloaded. And then another, unrelated,
+ breakpoint at ADDR is set. There is not breakpoint removal request
+ between the first and the second breakpoint.
+
+ - When GDB wants to update the target-side breakpoint conditions or
+ commands, it re-inserts the breakpoint, with updated
+ conditions/commands associated.
+
+ Also, we need to keep track of internal breakpoints too, so we do
+ need to be able to install multiple breakpoints at the same address
+ transparently.
+
+ We keep track of two different, and closely related structures. A
+ raw breakpoint, which manages the low level, close to the metal
+ aspect of a breakpoint. It holds the breakpoint address, and for
+ software breakpoints, a buffer holding a copy of the instructions
+ that would be in memory had not been a breakpoint there (we call
+ that the shadow memory of the breakpoint). We occasionally need to
+ temporarilly uninsert a breakpoint without the client knowing about
+ it (e.g., to step over an internal breakpoint), so we keep an
+ `inserted' state associated with this low level breakpoint
+ structure. There can only be one such object for a given address.
+ Then, we have (a bit higher level) breakpoints. This structure
+ holds a callback to be called whenever a breakpoint is hit, a
+ high-level type, and a link to a low level raw breakpoint. There
+ can be many high-level breakpoints at the same address, and all of
+ them will point to the same raw breakpoint, which is reference
+ counted. */
+
+/* The low level, physical, raw breakpoint. */
+struct raw_breakpoint
+{
+ struct raw_breakpoint *next;
+
+ /* The low level type of the breakpoint (software breakpoint,
+ watchpoint, etc.) */
+ enum raw_bkpt_type raw_type;
+
+ /* A reference count. Each high level breakpoint referencing this
+ raw breakpoint accounts for one reference. */
+ int refcount;
+
+ /* The breakpoint's insertion address. There can only be one raw
+ breakpoint for a given PC. */
+ CORE_ADDR pc;
+
+ /* The breakpoint's kind. This is target specific. Most
+ architectures only use one specific instruction for breakpoints, while
+ others may use more than one. E.g., on ARM, we need to use different
+ breakpoint instructions on Thumb, Thumb-2, and ARM code. Likewise for
+ hardware breakpoints -- some architectures (including ARM) need to
+ setup debug registers differently depending on mode. */
+ int kind;
+
+ /* The breakpoint's shadow memory. */
+ unsigned char old_data[MAX_BREAKPOINT_LEN];
+
+ /* Positive if this breakpoint is currently inserted in the
+ inferior. Negative if it was, but we've detected that it's now
+ gone. Zero if not inserted. */
+ int inserted;
+};
+
+/* The type of a breakpoint. */
+enum bkpt_type
+ {
+ /* A GDB breakpoint, requested with a Z0 packet. */
+ gdb_breakpoint_Z0,
+
+ /* A GDB hardware breakpoint, requested with a Z1 packet. */
+ gdb_breakpoint_Z1,
+
+ /* A GDB write watchpoint, requested with a Z2 packet. */
+ gdb_breakpoint_Z2,
+
+ /* A GDB read watchpoint, requested with a Z3 packet. */
+ gdb_breakpoint_Z3,
+
+ /* A GDB access watchpoint, requested with a Z4 packet. */
+ gdb_breakpoint_Z4,
+
+ /* A software single-step breakpoint. */
+ single_step_breakpoint,
+
+ /* Any other breakpoint type that doesn't require specific
+ treatment goes here. E.g., an event breakpoint. */
+ other_breakpoint,
+ };
+
+struct point_cond_list
+{
+ /* Pointer to the agent expression that is the breakpoint's
+ conditional. */
+ struct agent_expr *cond;
+
+ /* Pointer to the next condition. */
+ struct point_cond_list *next;
+};
+
+struct point_command_list
+{
+ /* Pointer to the agent expression that is the breakpoint's
+ commands. */
+ struct agent_expr *cmd;
+
+ /* Flag that is true if this command should run even while GDB is
+ disconnected. */
+ int persistence;
+
+ /* Pointer to the next command. */
+ struct point_command_list *next;
+};
+
+/* A high level (in gdbserver's perspective) breakpoint. */
+struct breakpoint
+{
+ struct breakpoint *next;
+
+ /* The breakpoint's type. */
+ enum bkpt_type type;
+
+ /* Link to this breakpoint's raw breakpoint. This is always
+ non-NULL. */
+ struct raw_breakpoint *raw;
+};
+
+/* Breakpoint requested by GDB. */
+
+struct gdb_breakpoint
+{
+ struct breakpoint base;
+
+ /* Pointer to the condition list that should be evaluated on
+ the target or NULL if the breakpoint is unconditional or
+ if GDB doesn't want us to evaluate the conditionals on the
+ target's side. */
+ struct point_cond_list *cond_list;
+
+ /* Point to the list of commands to run when this is hit. */
+ struct point_command_list *command_list;
+};
+
+/* Breakpoint used by GDBserver. */
+
+struct other_breakpoint
+{
+ struct breakpoint base;
+
+ /* Function to call when we hit this breakpoint. If it returns 1,
+ the breakpoint shall be deleted; 0 or if this callback is NULL,
+ it will be left inserted. */
+ int (*handler) (CORE_ADDR);
+};
+
+/* Breakpoint for single step. */
+
+struct single_step_breakpoint
+{
+ struct breakpoint base;
+
+ /* Thread the reinsert breakpoint belongs to. */
+ ptid_t ptid;
+};
+
+/* Return the breakpoint size from its kind. */
+
+static int
+bp_size (struct raw_breakpoint *bp)
+{
+ int size = 0;
+
+ the_target->sw_breakpoint_from_kind (bp->kind, &size);
+ return size;
+}
+
+/* Return the breakpoint opcode from its kind. */
+
+static const gdb_byte *
+bp_opcode (struct raw_breakpoint *bp)
+{
+ int size = 0;
+
+ return the_target->sw_breakpoint_from_kind (bp->kind, &size);
+}
+
+/* See mem-break.h. */
+
+enum target_hw_bp_type
+raw_bkpt_type_to_target_hw_bp_type (enum raw_bkpt_type raw_type)
+{
+ switch (raw_type)
+ {
+ case raw_bkpt_type_hw:
+ return hw_execute;
+ case raw_bkpt_type_write_wp:
+ return hw_write;
+ case raw_bkpt_type_read_wp:
+ return hw_read;
+ case raw_bkpt_type_access_wp:
+ return hw_access;
+ default:
+ internal_error (__FILE__, __LINE__,
+ "bad raw breakpoint type %d", (int) raw_type);
+ }
+}
+
+/* See mem-break.h. */
+
+static enum bkpt_type
+Z_packet_to_bkpt_type (char z_type)
+{
+ gdb_assert ('0' <= z_type && z_type <= '4');
+
+ return (enum bkpt_type) (gdb_breakpoint_Z0 + (z_type - '0'));
+}
+
+/* See mem-break.h. */
+
+enum raw_bkpt_type
+Z_packet_to_raw_bkpt_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ return raw_bkpt_type_sw;
+ case Z_PACKET_HW_BP:
+ return raw_bkpt_type_hw;
+ case Z_PACKET_WRITE_WP:
+ return raw_bkpt_type_write_wp;
+ case Z_PACKET_READ_WP:
+ return raw_bkpt_type_read_wp;
+ case Z_PACKET_ACCESS_WP:
+ return raw_bkpt_type_access_wp;
+ default:
+ gdb_assert_not_reached ("unhandled Z packet type.");
+ }
+}
+
+/* Return true if breakpoint TYPE is a GDB breakpoint. */
+
+static int
+is_gdb_breakpoint (enum bkpt_type type)
+{
+ return (type == gdb_breakpoint_Z0
+ || type == gdb_breakpoint_Z1
+ || type == gdb_breakpoint_Z2
+ || type == gdb_breakpoint_Z3
+ || type == gdb_breakpoint_Z4);
+}
+
+bool
+any_persistent_commands (process_info *proc)
+{
+ struct breakpoint *bp;
+ struct point_command_list *cl;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ if (is_gdb_breakpoint (bp->type))
+ {
+ struct gdb_breakpoint *gdb_bp = (struct gdb_breakpoint *) bp;
+
+ for (cl = gdb_bp->command_list; cl != NULL; cl = cl->next)
+ if (cl->persistence)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Find low-level breakpoint of type TYPE at address ADDR that is not
+ insert-disabled. Returns NULL if not found. */
+
+static struct raw_breakpoint *
+find_enabled_raw_code_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if (bp->pc == addr
+ && bp->raw_type == type
+ && bp->inserted >= 0)
+ return bp;
+
+ return NULL;
+}
+
+/* Find low-level breakpoint of type TYPE at address ADDR. Returns
+ NULL if not found. */
+
+static struct raw_breakpoint *
+find_raw_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type, int kind)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if (bp->pc == addr && bp->raw_type == type && bp->kind == kind)
+ return bp;
+
+ return NULL;
+}
+
+/* See mem-break.h. */
+
+int
+insert_memory_breakpoint (struct raw_breakpoint *bp)
+{
+ unsigned char buf[MAX_BREAKPOINT_LEN];
+ int err;
+
+ /* Note that there can be fast tracepoint jumps installed in the
+ same memory range, so to get at the original memory, we need to
+ use read_inferior_memory, which masks those out. */
+ err = read_inferior_memory (bp->pc, buf, bp_size (bp));
+ if (err != 0)
+ {
+ if (debug_threads)
+ debug_printf ("Failed to read shadow memory of"
+ " breakpoint at 0x%s (%s).\n",
+ paddress (bp->pc), safe_strerror (err));
+ }
+ else
+ {
+ memcpy (bp->old_data, buf, bp_size (bp));
+
+ err = (*the_target->write_memory) (bp->pc, bp_opcode (bp),
+ bp_size (bp));
+ if (err != 0)
+ {
+ if (debug_threads)
+ debug_printf ("Failed to insert breakpoint at 0x%s (%s).\n",
+ paddress (bp->pc), safe_strerror (err));
+ }
+ }
+ return err != 0 ? -1 : 0;
+}
+
+/* See mem-break.h */
+
+int
+remove_memory_breakpoint (struct raw_breakpoint *bp)
+{
+ unsigned char buf[MAX_BREAKPOINT_LEN];
+ int err;
+
+ /* Since there can be trap breakpoints inserted in the same address
+ range, we use `target_write_memory', which takes care of
+ layering breakpoints on top of fast tracepoints, and on top of
+ the buffer we pass it. This works because the caller has already
+ either unlinked the breakpoint or marked it uninserted. Also
+ note that we need to pass the current shadow contents, because
+ target_write_memory updates any shadow memory with what we pass
+ here, and we want that to be a nop. */
+ memcpy (buf, bp->old_data, bp_size (bp));
+ err = target_write_memory (bp->pc, buf, bp_size (bp));
+ if (err != 0)
+ {
+ if (debug_threads)
+ debug_printf ("Failed to uninsert raw breakpoint "
+ "at 0x%s (%s) while deleting it.\n",
+ paddress (bp->pc), safe_strerror (err));
+ }
+ return err != 0 ? -1 : 0;
+}
+
+/* Set a RAW breakpoint of type TYPE and kind KIND at WHERE. On
+ success, a pointer to the new breakpoint is returned. On failure,
+ returns NULL and writes the error code to *ERR. */
+
+static struct raw_breakpoint *
+set_raw_breakpoint_at (enum raw_bkpt_type type, CORE_ADDR where, int kind,
+ int *err)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ if (type == raw_bkpt_type_sw || type == raw_bkpt_type_hw)
+ {
+ bp = find_enabled_raw_code_breakpoint_at (where, type);
+ if (bp != NULL && bp->kind != kind)
+ {
+ /* A different kind than previously seen. The previous
+ breakpoint must be gone then. */
+ if (debug_threads)
+ debug_printf ("Inconsistent breakpoint kind? Was %d, now %d.\n",
+ bp->kind, kind);
+ bp->inserted = -1;
+ bp = NULL;
+ }
+ }
+ else
+ bp = find_raw_breakpoint_at (where, type, kind);
+
+ gdb::unique_xmalloc_ptr<struct raw_breakpoint> bp_holder;
+ if (bp == NULL)
+ {
+ bp_holder.reset (XCNEW (struct raw_breakpoint));
+ bp = bp_holder.get ();
+ bp->pc = where;
+ bp->kind = kind;
+ bp->raw_type = type;
+ }
+
+ if (!bp->inserted)
+ {
+ *err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp);
+ if (*err != 0)
+ {
+ if (debug_threads)
+ debug_printf ("Failed to insert breakpoint at 0x%s (%d).\n",
+ paddress (where), *err);
+
+ return NULL;
+ }
+
+ bp->inserted = 1;
+ }
+
+ /* If the breakpoint was allocated above, we know we want to keep it
+ now. */
+ bp_holder.release ();
+
+ /* Link the breakpoint in, if this is the first reference. */
+ if (++bp->refcount == 1)
+ {
+ bp->next = proc->raw_breakpoints;
+ proc->raw_breakpoints = bp;
+ }
+ return bp;
+}
+
+/* Notice that breakpoint traps are always installed on top of fast
+ tracepoint jumps. This is even if the fast tracepoint is installed
+ at a later time compared to when the breakpoint was installed.
+ This means that a stopping breakpoint or tracepoint has higher
+ "priority". In turn, this allows having fast and slow tracepoints
+ (and breakpoints) at the same address behave correctly. */
+
+
+/* A fast tracepoint jump. */
+
+struct fast_tracepoint_jump
+{
+ struct fast_tracepoint_jump *next;
+
+ /* A reference count. GDB can install more than one fast tracepoint
+ at the same address (each with its own action list, for
+ example). */
+ int refcount;
+
+ /* The fast tracepoint's insertion address. There can only be one
+ of these for a given PC. */
+ CORE_ADDR pc;
+
+ /* Non-zero if this fast tracepoint jump is currently inserted in
+ the inferior. */
+ int inserted;
+
+ /* The length of the jump instruction. */
+ int length;
+
+ /* A poor-man's flexible array member, holding both the jump
+ instruction to insert, and a copy of the instruction that would
+ be in memory had not been a jump there (the shadow memory of the
+ tracepoint jump). */
+ unsigned char insn_and_shadow[0];
+};
+
+/* Fast tracepoint FP's jump instruction to insert. */
+#define fast_tracepoint_jump_insn(fp) \
+ ((fp)->insn_and_shadow + 0)
+
+/* The shadow memory of fast tracepoint jump FP. */
+#define fast_tracepoint_jump_shadow(fp) \
+ ((fp)->insn_and_shadow + (fp)->length)
+
+
+/* Return the fast tracepoint jump set at WHERE. */
+
+static struct fast_tracepoint_jump *
+find_fast_tracepoint_jump_at (CORE_ADDR where)
+{
+ struct process_info *proc = current_process ();
+ struct fast_tracepoint_jump *jp;
+
+ for (jp = proc->fast_tracepoint_jumps; jp != NULL; jp = jp->next)
+ if (jp->pc == where)
+ return jp;
+
+ return NULL;
+}
+
+int
+fast_tracepoint_jump_here (CORE_ADDR where)
+{
+ struct fast_tracepoint_jump *jp = find_fast_tracepoint_jump_at (where);
+
+ return (jp != NULL);
+}
+
+int
+delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel)
+{
+ struct fast_tracepoint_jump *bp, **bp_link;
+ int ret;
+ struct process_info *proc = current_process ();
+
+ bp = proc->fast_tracepoint_jumps;
+ bp_link = &proc->fast_tracepoint_jumps;
+
+ while (bp)
+ {
+ if (bp == todel)
+ {
+ if (--bp->refcount == 0)
+ {
+ struct fast_tracepoint_jump *prev_bp_link = *bp_link;
+ unsigned char *buf;
+
+ /* Unlink it. */
+ *bp_link = bp->next;
+
+ /* Since there can be breakpoints inserted in the same
+ address range, we use `target_write_memory', which
+ takes care of layering breakpoints on top of fast
+ tracepoints, and on top of the buffer we pass it.
+ This works because we've already unlinked the fast
+ tracepoint jump above. Also note that we need to
+ pass the current shadow contents, because
+ target_write_memory updates any shadow memory with
+ what we pass here, and we want that to be a nop. */
+ buf = (unsigned char *) alloca (bp->length);
+ memcpy (buf, fast_tracepoint_jump_shadow (bp), bp->length);
+ ret = target_write_memory (bp->pc, buf, bp->length);
+ if (ret != 0)
+ {
+ /* Something went wrong, relink the jump. */
+ *bp_link = prev_bp_link;
+
+ if (debug_threads)
+ debug_printf ("Failed to uninsert fast tracepoint jump "
+ "at 0x%s (%s) while deleting it.\n",
+ paddress (bp->pc), safe_strerror (ret));
+ return ret;
+ }
+
+ free (bp);
+ }
+
+ return 0;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+ }
+
+ warning ("Could not find fast tracepoint jump in list.");
+ return ENOENT;
+}
+
+void
+inc_ref_fast_tracepoint_jump (struct fast_tracepoint_jump *jp)
+{
+ jp->refcount++;
+}
+
+struct fast_tracepoint_jump *
+set_fast_tracepoint_jump (CORE_ADDR where,
+ unsigned char *insn, ULONGEST length)
+{
+ struct process_info *proc = current_process ();
+ struct fast_tracepoint_jump *jp;
+ int err;
+ unsigned char *buf;
+
+ /* We refcount fast tracepoint jumps. Check if we already know
+ about a jump at this address. */
+ jp = find_fast_tracepoint_jump_at (where);
+ if (jp != NULL)
+ {
+ jp->refcount++;
+ return jp;
+ }
+
+ /* We don't, so create a new object. Double the length, because the
+ flexible array member holds both the jump insn, and the
+ shadow. */
+ jp = (struct fast_tracepoint_jump *) xcalloc (1, sizeof (*jp) + (length * 2));
+ jp->pc = where;
+ jp->length = length;
+ memcpy (fast_tracepoint_jump_insn (jp), insn, length);
+ jp->refcount = 1;
+ buf = (unsigned char *) alloca (length);
+
+ /* Note that there can be trap breakpoints inserted in the same
+ address range. To access the original memory contents, we use
+ `read_inferior_memory', which masks out breakpoints. */
+ err = read_inferior_memory (where, buf, length);
+ if (err != 0)
+ {
+ if (debug_threads)
+ debug_printf ("Failed to read shadow memory of"
+ " fast tracepoint at 0x%s (%s).\n",
+ paddress (where), safe_strerror (err));
+ free (jp);
+ return NULL;
+ }
+ memcpy (fast_tracepoint_jump_shadow (jp), buf, length);
+
+ /* Link the jump in. */
+ jp->inserted = 1;
+ jp->next = proc->fast_tracepoint_jumps;
+ proc->fast_tracepoint_jumps = jp;
+
+ /* Since there can be trap breakpoints inserted in the same address
+ range, we use use `target_write_memory', which takes care of
+ layering breakpoints on top of fast tracepoints, on top of the
+ buffer we pass it. This works because we've already linked in
+ the fast tracepoint jump above. Also note that we need to pass
+ the current shadow contents, because target_write_memory
+ updates any shadow memory with what we pass here, and we want
+ that to be a nop. */
+ err = target_write_memory (where, buf, length);
+ if (err != 0)
+ {
+ if (debug_threads)
+ debug_printf ("Failed to insert fast tracepoint jump at 0x%s (%s).\n",
+ paddress (where), safe_strerror (err));
+
+ /* Unlink it. */
+ proc->fast_tracepoint_jumps = jp->next;
+ free (jp);
+
+ return NULL;
+ }
+
+ return jp;
+}
+
+void
+uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc)
+{
+ struct fast_tracepoint_jump *jp;
+ int err;
+
+ jp = find_fast_tracepoint_jump_at (pc);
+ if (jp == NULL)
+ {
+ /* This can happen when we remove all breakpoints while handling
+ a step-over. */
+ if (debug_threads)
+ debug_printf ("Could not find fast tracepoint jump at 0x%s "
+ "in list (uninserting).\n",
+ paddress (pc));
+ return;
+ }
+
+ if (jp->inserted)
+ {
+ unsigned char *buf;
+
+ jp->inserted = 0;
+
+ /* Since there can be trap breakpoints inserted in the same
+ address range, we use use `target_write_memory', which
+ takes care of layering breakpoints on top of fast
+ tracepoints, and on top of the buffer we pass it. This works
+ because we've already marked the fast tracepoint fast
+ tracepoint jump uninserted above. Also note that we need to
+ pass the current shadow contents, because
+ target_write_memory updates any shadow memory with what we
+ pass here, and we want that to be a nop. */
+ buf = (unsigned char *) alloca (jp->length);
+ memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length);
+ err = target_write_memory (jp->pc, buf, jp->length);
+ if (err != 0)
+ {
+ jp->inserted = 1;
+
+ if (debug_threads)
+ debug_printf ("Failed to uninsert fast tracepoint jump at"
+ " 0x%s (%s).\n",
+ paddress (pc), safe_strerror (err));
+ }
+ }
+}
+
+void
+reinsert_fast_tracepoint_jumps_at (CORE_ADDR where)
+{
+ struct fast_tracepoint_jump *jp;
+ int err;
+ unsigned char *buf;
+
+ jp = find_fast_tracepoint_jump_at (where);
+ if (jp == NULL)
+ {
+ /* This can happen when we remove breakpoints when a tracepoint
+ hit causes a tracing stop, while handling a step-over. */
+ if (debug_threads)
+ debug_printf ("Could not find fast tracepoint jump at 0x%s "
+ "in list (reinserting).\n",
+ paddress (where));
+ return;
+ }
+
+ if (jp->inserted)
+ error ("Jump already inserted at reinsert time.");
+
+ jp->inserted = 1;
+
+ /* Since there can be trap breakpoints inserted in the same address
+ range, we use `target_write_memory', which takes care of
+ layering breakpoints on top of fast tracepoints, and on top of
+ the buffer we pass it. This works because we've already marked
+ the fast tracepoint jump inserted above. Also note that we need
+ to pass the current shadow contents, because
+ target_write_memory updates any shadow memory with what we pass
+ here, and we want that to be a nop. */
+ buf = (unsigned char *) alloca (jp->length);
+ memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length);
+ err = target_write_memory (where, buf, jp->length);
+ if (err != 0)
+ {
+ jp->inserted = 0;
+
+ if (debug_threads)
+ debug_printf ("Failed to reinsert fast tracepoint jump at"
+ " 0x%s (%s).\n",
+ paddress (where), safe_strerror (err));
+ }
+}
+
+/* Set a high-level breakpoint of type TYPE, with low level type
+ RAW_TYPE and kind KIND, at WHERE. On success, a pointer to the new
+ breakpoint is returned. On failure, returns NULL and writes the
+ error code to *ERR. HANDLER is called when the breakpoint is hit.
+ HANDLER should return 1 if the breakpoint should be deleted, 0
+ otherwise. */
+
+static struct breakpoint *
+set_breakpoint (enum bkpt_type type, enum raw_bkpt_type raw_type,
+ CORE_ADDR where, int kind,
+ int (*handler) (CORE_ADDR), int *err)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+ struct raw_breakpoint *raw;
+
+ raw = set_raw_breakpoint_at (raw_type, where, kind, err);
+
+ if (raw == NULL)
+ {
+ /* warn? */
+ return NULL;
+ }
+
+ if (is_gdb_breakpoint (type))
+ {
+ struct gdb_breakpoint *gdb_bp = XCNEW (struct gdb_breakpoint);
+
+ bp = (struct breakpoint *) gdb_bp;
+ gdb_assert (handler == NULL);
+ }
+ else if (type == other_breakpoint)
+ {
+ struct other_breakpoint *other_bp = XCNEW (struct other_breakpoint);
+
+ other_bp->handler = handler;
+ bp = (struct breakpoint *) other_bp;
+ }
+ else if (type == single_step_breakpoint)
+ {
+ struct single_step_breakpoint *ss_bp
+ = XCNEW (struct single_step_breakpoint);
+
+ bp = (struct breakpoint *) ss_bp;
+ }
+ else
+ gdb_assert_not_reached ("unhandled breakpoint type");
+
+ bp->type = type;
+ bp->raw = raw;
+
+ bp->next = proc->breakpoints;
+ proc->breakpoints = bp;
+
+ return bp;
+}
+
+/* Set breakpoint of TYPE on address WHERE with handler HANDLER. */
+
+static struct breakpoint *
+set_breakpoint_type_at (enum bkpt_type type, CORE_ADDR where,
+ int (*handler) (CORE_ADDR))
+{
+ int err_ignored;
+ CORE_ADDR placed_address = where;
+ int breakpoint_kind = target_breakpoint_kind_from_pc (&placed_address);
+
+ return set_breakpoint (type, raw_bkpt_type_sw,
+ placed_address, breakpoint_kind, handler,
+ &err_ignored);
+}
+
+/* See mem-break.h */
+
+struct breakpoint *
+set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
+{
+ return set_breakpoint_type_at (other_breakpoint, where, handler);
+}
+
+
+static int
+delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel)
+{
+ struct raw_breakpoint *bp, **bp_link;
+ int ret;
+
+ bp = proc->raw_breakpoints;
+ bp_link = &proc->raw_breakpoints;
+
+ while (bp)
+ {
+ if (bp == todel)
+ {
+ if (bp->inserted > 0)
+ {
+ struct raw_breakpoint *prev_bp_link = *bp_link;
+
+ *bp_link = bp->next;
+
+ ret = the_target->remove_point (bp->raw_type, bp->pc, bp->kind,
+ bp);
+ if (ret != 0)
+ {
+ /* Something went wrong, relink the breakpoint. */
+ *bp_link = prev_bp_link;
+
+ if (debug_threads)
+ debug_printf ("Failed to uninsert raw breakpoint "
+ "at 0x%s while deleting it.\n",
+ paddress (bp->pc));
+ return ret;
+ }
+ }
+ else
+ *bp_link = bp->next;
+
+ free (bp);
+ return 0;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+ }
+
+ warning ("Could not find raw breakpoint in list.");
+ return ENOENT;
+}
+
+static int
+release_breakpoint (struct process_info *proc, struct breakpoint *bp)
+{
+ int newrefcount;
+ int ret;
+
+ newrefcount = bp->raw->refcount - 1;
+ if (newrefcount == 0)
+ {
+ ret = delete_raw_breakpoint (proc, bp->raw);
+ if (ret != 0)
+ return ret;
+ }
+ else
+ bp->raw->refcount = newrefcount;
+
+ free (bp);
+
+ return 0;
+}
+
+static int
+delete_breakpoint_1 (struct process_info *proc, struct breakpoint *todel)
+{
+ struct breakpoint *bp, **bp_link;
+ int err;
+
+ bp = proc->breakpoints;
+ bp_link = &proc->breakpoints;
+
+ while (bp)
+ {
+ if (bp == todel)
+ {
+ *bp_link = bp->next;
+
+ err = release_breakpoint (proc, bp);
+ if (err != 0)
+ return err;
+
+ bp = *bp_link;
+ return 0;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+ }
+
+ warning ("Could not find breakpoint in list.");
+ return ENOENT;
+}
+
+int
+delete_breakpoint (struct breakpoint *todel)
+{
+ struct process_info *proc = current_process ();
+ return delete_breakpoint_1 (proc, todel);
+}
+
+/* Locate a GDB breakpoint of type Z_TYPE and kind KIND placed at
+ address ADDR and return a pointer to its structure. If KIND is -1,
+ the breakpoint's kind is ignored. */
+
+static struct gdb_breakpoint *
+find_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+ enum bkpt_type type = Z_packet_to_bkpt_type (z_type);
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ if (bp->type == type && bp->raw->pc == addr
+ && (kind == -1 || bp->raw->kind == kind))
+ return (struct gdb_breakpoint *) bp;
+
+ return NULL;
+}
+
+static int
+z_type_supported (char z_type)
+{
+ return (z_type >= '0' && z_type <= '4'
+ && the_target->supports_z_point_type != NULL
+ && the_target->supports_z_point_type (z_type));
+}
+
+/* Create a new GDB breakpoint of type Z_TYPE at ADDR with kind KIND.
+ Returns a pointer to the newly created breakpoint on success. On
+ failure returns NULL and sets *ERR to either -1 for error, or 1 if
+ Z_TYPE breakpoints are not supported on this target. */
+
+static struct gdb_breakpoint *
+set_gdb_breakpoint_1 (char z_type, CORE_ADDR addr, int kind, int *err)
+{
+ struct gdb_breakpoint *bp;
+ enum bkpt_type type;
+ enum raw_bkpt_type raw_type;
+
+ /* If we see GDB inserting a second code breakpoint at the same
+ address, then either: GDB is updating the breakpoint's conditions
+ or commands; or, the first breakpoint must have disappeared due
+ to a shared library unload. On targets where the shared
+ libraries are handled by userspace, like SVR4, for example,
+ GDBserver can't tell if a library was loaded or unloaded. Since
+ we refcount raw breakpoints, we must be careful to make sure GDB
+ breakpoints never contribute more than one reference. if we
+ didn't do this, in case the previous breakpoint is gone due to a
+ shared library unload, we'd just increase the refcount of the
+ previous breakpoint at this address, but the trap was not planted
+ in the inferior anymore, thus the breakpoint would never be hit.
+ Note this must be careful to not create a window where
+ breakpoints are removed from the target, for non-stop, in case
+ the target can poke at memory while the program is running. */
+ if (z_type == Z_PACKET_SW_BP
+ || z_type == Z_PACKET_HW_BP)
+ {
+ bp = find_gdb_breakpoint (z_type, addr, -1);
+
+ if (bp != NULL)
+ {
+ if (bp->base.raw->kind != kind)
+ {
+ /* A different kind than previously seen. The previous
+ breakpoint must be gone then. */
+ bp->base.raw->inserted = -1;
+ delete_breakpoint ((struct breakpoint *) bp);
+ bp = NULL;
+ }
+ else if (z_type == Z_PACKET_SW_BP)
+ {
+ /* Check if the breakpoint is actually gone from the
+ target, due to an solib unload, for example. Might
+ as well validate _all_ breakpoints. */
+ validate_breakpoints ();
+
+ /* Breakpoints that don't pass validation are
+ deleted. */
+ bp = find_gdb_breakpoint (z_type, addr, -1);
+ }
+ }
+ }
+ else
+ {
+ /* Data breakpoints for the same address but different kind are
+ expected. GDB doesn't merge these. The backend gets to do
+ that if it wants/can. */
+ bp = find_gdb_breakpoint (z_type, addr, kind);
+ }
+
+ if (bp != NULL)
+ {
+ /* We already know about this breakpoint, there's nothing else
+ to do - GDB's reference is already accounted for. Note that
+ whether the breakpoint inserted is left as is - we may be
+ stepping over it, for example, in which case we don't want to
+ force-reinsert it. */
+ return bp;
+ }
+
+ raw_type = Z_packet_to_raw_bkpt_type (z_type);
+ type = Z_packet_to_bkpt_type (z_type);
+ return (struct gdb_breakpoint *) set_breakpoint (type, raw_type, addr,
+ kind, NULL, err);
+}
+
+static int
+check_gdb_bp_preconditions (char z_type, int *err)
+{
+ /* As software/memory breakpoints work by poking at memory, we need
+ to prepare to access memory. If that operation fails, we need to
+ return error. Seeing an error, if this is the first breakpoint
+ of that type that GDB tries to insert, GDB would then assume the
+ breakpoint type is supported, but it may actually not be. So we
+ need to check whether the type is supported at all before
+ preparing to access memory. */
+ if (!z_type_supported (z_type))
+ {
+ *err = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* See mem-break.h. This is a wrapper for set_gdb_breakpoint_1 that
+ knows to prepare to access memory for Z0 breakpoints. */
+
+struct gdb_breakpoint *
+set_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind, int *err)
+{
+ struct gdb_breakpoint *bp;
+
+ if (!check_gdb_bp_preconditions (z_type, err))
+ return NULL;
+
+ /* If inserting a software/memory breakpoint, need to prepare to
+ access memory. */
+ if (z_type == Z_PACKET_SW_BP)
+ {
+ if (prepare_to_access_memory () != 0)
+ {
+ *err = -1;
+ return NULL;
+ }
+ }
+
+ bp = set_gdb_breakpoint_1 (z_type, addr, kind, err);
+
+ if (z_type == Z_PACKET_SW_BP)
+ done_accessing_memory ();
+
+ return bp;
+}
+
+/* Delete a GDB breakpoint of type Z_TYPE and kind KIND previously
+ inserted at ADDR with set_gdb_breakpoint_at. Returns 0 on success,
+ -1 on error, and 1 if Z_TYPE breakpoints are not supported on this
+ target. */
+
+static int
+delete_gdb_breakpoint_1 (char z_type, CORE_ADDR addr, int kind)
+{
+ struct gdb_breakpoint *bp;
+ int err;
+
+ bp = find_gdb_breakpoint (z_type, addr, kind);
+ if (bp == NULL)
+ return -1;
+
+ /* Before deleting the breakpoint, make sure to free its condition
+ and command lists. */
+ clear_breakpoint_conditions_and_commands (bp);
+ err = delete_breakpoint ((struct breakpoint *) bp);
+ if (err != 0)
+ return -1;
+
+ return 0;
+}
+
+/* See mem-break.h. This is a wrapper for delete_gdb_breakpoint that
+ knows to prepare to access memory for Z0 breakpoints. */
+
+int
+delete_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind)
+{
+ int ret;
+
+ if (!check_gdb_bp_preconditions (z_type, &ret))
+ return ret;
+
+ /* If inserting a software/memory breakpoint, need to prepare to
+ access memory. */
+ if (z_type == Z_PACKET_SW_BP)
+ {
+ int err;
+
+ err = prepare_to_access_memory ();
+ if (err != 0)
+ return -1;
+ }
+
+ ret = delete_gdb_breakpoint_1 (z_type, addr, kind);
+
+ if (z_type == Z_PACKET_SW_BP)
+ done_accessing_memory ();
+
+ return ret;
+}
+
+/* Clear all conditions associated with a breakpoint. */
+
+static void
+clear_breakpoint_conditions (struct gdb_breakpoint *bp)
+{
+ struct point_cond_list *cond;
+
+ if (bp->cond_list == NULL)
+ return;
+
+ cond = bp->cond_list;
+
+ while (cond != NULL)
+ {
+ struct point_cond_list *cond_next;
+
+ cond_next = cond->next;
+ gdb_free_agent_expr (cond->cond);
+ free (cond);
+ cond = cond_next;
+ }
+
+ bp->cond_list = NULL;
+}
+
+/* Clear all commands associated with a breakpoint. */
+
+static void
+clear_breakpoint_commands (struct gdb_breakpoint *bp)
+{
+ struct point_command_list *cmd;
+
+ if (bp->command_list == NULL)
+ return;
+
+ cmd = bp->command_list;
+
+ while (cmd != NULL)
+ {
+ struct point_command_list *cmd_next;
+
+ cmd_next = cmd->next;
+ gdb_free_agent_expr (cmd->cmd);
+ free (cmd);
+ cmd = cmd_next;
+ }
+
+ bp->command_list = NULL;
+}
+
+void
+clear_breakpoint_conditions_and_commands (struct gdb_breakpoint *bp)
+{
+ clear_breakpoint_conditions (bp);
+ clear_breakpoint_commands (bp);
+}
+
+/* Add condition CONDITION to GDBserver's breakpoint BP. */
+
+static void
+add_condition_to_breakpoint (struct gdb_breakpoint *bp,
+ struct agent_expr *condition)
+{
+ struct point_cond_list *new_cond;
+
+ /* Create new condition. */
+ new_cond = XCNEW (struct point_cond_list);
+ new_cond->cond = condition;
+
+ /* Add condition to the list. */
+ new_cond->next = bp->cond_list;
+ bp->cond_list = new_cond;
+}
+
+/* Add a target-side condition CONDITION to a breakpoint. */
+
+int
+add_breakpoint_condition (struct gdb_breakpoint *bp, const char **condition)
+{
+ const char *actparm = *condition;
+ struct agent_expr *cond;
+
+ if (condition == NULL)
+ return 1;
+
+ if (bp == NULL)
+ return 0;
+
+ cond = gdb_parse_agent_expr (&actparm);
+
+ if (cond == NULL)
+ {
+ warning ("Condition evaluation failed. Assuming unconditional.");
+ return 0;
+ }
+
+ add_condition_to_breakpoint (bp, cond);
+
+ *condition = actparm;
+
+ return 1;
+}
+
+/* Evaluate condition (if any) at breakpoint BP. Return 1 if
+ true and 0 otherwise. */
+
+static int
+gdb_condition_true_at_breakpoint_z_type (char z_type, CORE_ADDR addr)
+{
+ /* Fetch registers for the current inferior. */
+ struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1);
+ ULONGEST value = 0;
+ struct point_cond_list *cl;
+ int err = 0;
+ struct eval_agent_expr_context ctx;
+
+ if (bp == NULL)
+ return 0;
+
+ /* Check if the breakpoint is unconditional. If it is,
+ the condition always evaluates to TRUE. */
+ if (bp->cond_list == NULL)
+ return 1;
+
+ ctx.regcache = get_thread_regcache (current_thread, 1);
+ ctx.tframe = NULL;
+ ctx.tpoint = NULL;
+
+ /* Evaluate each condition in the breakpoint's list of conditions.
+ Return true if any of the conditions evaluates to TRUE.
+
+ If we failed to evaluate the expression, TRUE is returned. This
+ forces GDB to reevaluate the conditions. */
+ for (cl = bp->cond_list;
+ cl && !value && !err; cl = cl->next)
+ {
+ /* Evaluate the condition. */
+ err = gdb_eval_agent_expr (&ctx, cl->cond, &value);
+ }
+
+ if (err)
+ return 1;
+
+ return (value != 0);
+}
+
+int
+gdb_condition_true_at_breakpoint (CORE_ADDR where)
+{
+ /* Only check code (software or hardware) breakpoints. */
+ return (gdb_condition_true_at_breakpoint_z_type (Z_PACKET_SW_BP, where)
+ || gdb_condition_true_at_breakpoint_z_type (Z_PACKET_HW_BP, where));
+}
+
+/* Add commands COMMANDS to GDBserver's breakpoint BP. */
+
+static void
+add_commands_to_breakpoint (struct gdb_breakpoint *bp,
+ struct agent_expr *commands, int persist)
+{
+ struct point_command_list *new_cmd;
+
+ /* Create new command. */
+ new_cmd = XCNEW (struct point_command_list);
+ new_cmd->cmd = commands;
+ new_cmd->persistence = persist;
+
+ /* Add commands to the list. */
+ new_cmd->next = bp->command_list;
+ bp->command_list = new_cmd;
+}
+
+/* Add a target-side command COMMAND to the breakpoint at ADDR. */
+
+int
+add_breakpoint_commands (struct gdb_breakpoint *bp, const char **command,
+ int persist)
+{
+ const char *actparm = *command;
+ struct agent_expr *cmd;
+
+ if (command == NULL)
+ return 1;
+
+ if (bp == NULL)
+ return 0;
+
+ cmd = gdb_parse_agent_expr (&actparm);
+
+ if (cmd == NULL)
+ {
+ warning ("Command evaluation failed. Disabling.");
+ return 0;
+ }
+
+ add_commands_to_breakpoint (bp, cmd, persist);
+
+ *command = actparm;
+
+ return 1;
+}
+
+/* Return true if there are no commands to run at this location,
+ which likely means we want to report back to GDB. */
+
+static int
+gdb_no_commands_at_breakpoint_z_type (char z_type, CORE_ADDR addr)
+{
+ struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1);
+
+ if (bp == NULL)
+ return 1;
+
+ if (debug_threads)
+ debug_printf ("at 0x%s, type Z%c, bp command_list is 0x%s\n",
+ paddress (addr), z_type,
+ phex_nz ((uintptr_t) bp->command_list, 0));
+ return (bp->command_list == NULL);
+}
+
+/* Return true if there are no commands to run at this location,
+ which likely means we want to report back to GDB. */
+
+int
+gdb_no_commands_at_breakpoint (CORE_ADDR where)
+{
+ /* Only check code (software or hardware) breakpoints. */
+ return (gdb_no_commands_at_breakpoint_z_type (Z_PACKET_SW_BP, where)
+ && gdb_no_commands_at_breakpoint_z_type (Z_PACKET_HW_BP, where));
+}
+
+/* Run a breakpoint's commands. Returns 0 if there was a problem
+ running any command, 1 otherwise. */
+
+static int
+run_breakpoint_commands_z_type (char z_type, CORE_ADDR addr)
+{
+ /* Fetch registers for the current inferior. */
+ struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1);
+ ULONGEST value = 0;
+ struct point_command_list *cl;
+ int err = 0;
+ struct eval_agent_expr_context ctx;
+
+ if (bp == NULL)
+ return 1;
+
+ ctx.regcache = get_thread_regcache (current_thread, 1);
+ ctx.tframe = NULL;
+ ctx.tpoint = NULL;
+
+ for (cl = bp->command_list;
+ cl && !value && !err; cl = cl->next)
+ {
+ /* Run the command. */
+ err = gdb_eval_agent_expr (&ctx, cl->cmd, &value);
+
+ /* If one command has a problem, stop digging the hole deeper. */
+ if (err)
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+run_breakpoint_commands (CORE_ADDR where)
+{
+ /* Only check code (software or hardware) breakpoints. If one
+ command has a problem, stop digging the hole deeper. */
+ if (run_breakpoint_commands_z_type (Z_PACKET_SW_BP, where))
+ run_breakpoint_commands_z_type (Z_PACKET_HW_BP, where);
+}
+
+/* See mem-break.h. */
+
+int
+gdb_breakpoint_here (CORE_ADDR where)
+{
+ /* Only check code (software or hardware) breakpoints. */
+ return (find_gdb_breakpoint (Z_PACKET_SW_BP, where, -1) != NULL
+ || find_gdb_breakpoint (Z_PACKET_HW_BP, where, -1) != NULL);
+}
+
+void
+set_single_step_breakpoint (CORE_ADDR stop_at, ptid_t ptid)
+{
+ struct single_step_breakpoint *bp;
+
+ gdb_assert (current_ptid.pid () == ptid.pid ());
+
+ bp = (struct single_step_breakpoint *) set_breakpoint_type_at (single_step_breakpoint,
+ stop_at, NULL);
+ bp->ptid = ptid;
+}
+
+void
+delete_single_step_breakpoints (struct thread_info *thread)
+{
+ struct process_info *proc = get_thread_process (thread);
+ struct breakpoint *bp, **bp_link;
+
+ bp = proc->breakpoints;
+ bp_link = &proc->breakpoints;
+
+ while (bp)
+ {
+ if (bp->type == single_step_breakpoint
+ && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
+ {
+ struct thread_info *saved_thread = current_thread;
+
+ current_thread = thread;
+ *bp_link = bp->next;
+ release_breakpoint (proc, bp);
+ bp = *bp_link;
+ current_thread = saved_thread;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+ }
+}
+
+static void
+uninsert_raw_breakpoint (struct raw_breakpoint *bp)
+{
+ if (bp->inserted < 0)
+ {
+ if (debug_threads)
+ debug_printf ("Breakpoint at %s is marked insert-disabled.\n",
+ paddress (bp->pc));
+ }
+ else if (bp->inserted > 0)
+ {
+ int err;
+
+ bp->inserted = 0;
+
+ err = the_target->remove_point (bp->raw_type, bp->pc, bp->kind, bp);
+ if (err != 0)
+ {
+ bp->inserted = 1;
+
+ if (debug_threads)
+ debug_printf ("Failed to uninsert raw breakpoint at 0x%s.\n",
+ paddress (bp->pc));
+ }
+ }
+}
+
+void
+uninsert_breakpoints_at (CORE_ADDR pc)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+ int found = 0;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if ((bp->raw_type == raw_bkpt_type_sw
+ || bp->raw_type == raw_bkpt_type_hw)
+ && bp->pc == pc)
+ {
+ found = 1;
+
+ if (bp->inserted)
+ uninsert_raw_breakpoint (bp);
+ }
+
+ if (!found)
+ {
+ /* This can happen when we remove all breakpoints while handling
+ a step-over. */
+ if (debug_threads)
+ debug_printf ("Could not find breakpoint at 0x%s "
+ "in list (uninserting).\n",
+ paddress (pc));
+ }
+}
+
+void
+uninsert_all_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if ((bp->raw_type == raw_bkpt_type_sw
+ || bp->raw_type == raw_bkpt_type_hw)
+ && bp->inserted)
+ uninsert_raw_breakpoint (bp);
+}
+
+void
+uninsert_single_step_breakpoints (struct thread_info *thread)
+{
+ struct process_info *proc = get_thread_process (thread);
+ struct breakpoint *bp;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ if (bp->type == single_step_breakpoint
+ && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
+ {
+ gdb_assert (bp->raw->inserted > 0);
+
+ /* Only uninsert the raw breakpoint if it only belongs to a
+ reinsert breakpoint. */
+ if (bp->raw->refcount == 1)
+ {
+ struct thread_info *saved_thread = current_thread;
+
+ current_thread = thread;
+ uninsert_raw_breakpoint (bp->raw);
+ current_thread = saved_thread;
+ }
+ }
+ }
+}
+
+static void
+reinsert_raw_breakpoint (struct raw_breakpoint *bp)
+{
+ int err;
+
+ if (bp->inserted)
+ return;
+
+ err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp);
+ if (err == 0)
+ bp->inserted = 1;
+ else if (debug_threads)
+ debug_printf ("Failed to reinsert breakpoint at 0x%s (%d).\n",
+ paddress (bp->pc), err);
+}
+
+void
+reinsert_breakpoints_at (CORE_ADDR pc)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+ int found = 0;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if ((bp->raw_type == raw_bkpt_type_sw
+ || bp->raw_type == raw_bkpt_type_hw)
+ && bp->pc == pc)
+ {
+ found = 1;
+
+ reinsert_raw_breakpoint (bp);
+ }
+
+ if (!found)
+ {
+ /* This can happen when we remove all breakpoints while handling
+ a step-over. */
+ if (debug_threads)
+ debug_printf ("Could not find raw breakpoint at 0x%s "
+ "in list (reinserting).\n",
+ paddress (pc));
+ }
+}
+
+int
+has_single_step_breakpoints (struct thread_info *thread)
+{
+ struct process_info *proc = get_thread_process (thread);
+ struct breakpoint *bp, **bp_link;
+
+ bp = proc->breakpoints;
+ bp_link = &proc->breakpoints;
+
+ while (bp)
+ {
+ if (bp->type == single_step_breakpoint
+ && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
+ return 1;
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+ }
+
+ return 0;
+}
+
+void
+reinsert_all_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if ((bp->raw_type == raw_bkpt_type_sw
+ || bp->raw_type == raw_bkpt_type_hw)
+ && !bp->inserted)
+ reinsert_raw_breakpoint (bp);
+}
+
+void
+reinsert_single_step_breakpoints (struct thread_info *thread)
+{
+ struct process_info *proc = get_thread_process (thread);
+ struct breakpoint *bp;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ if (bp->type == single_step_breakpoint
+ && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
+ {
+ gdb_assert (bp->raw->inserted > 0);
+
+ if (bp->raw->refcount == 1)
+ {
+ struct thread_info *saved_thread = current_thread;
+
+ current_thread = thread;
+ reinsert_raw_breakpoint (bp->raw);
+ current_thread = saved_thread;
+ }
+ }
+ }
+}
+
+void
+check_breakpoints (CORE_ADDR stop_pc)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp, **bp_link;
+
+ bp = proc->breakpoints;
+ bp_link = &proc->breakpoints;
+
+ while (bp)
+ {
+ struct raw_breakpoint *raw = bp->raw;
+
+ if ((raw->raw_type == raw_bkpt_type_sw
+ || raw->raw_type == raw_bkpt_type_hw)
+ && raw->pc == stop_pc)
+ {
+ if (!raw->inserted)
+ {
+ warning ("Hit a removed breakpoint?");
+ return;
+ }
+
+ if (bp->type == other_breakpoint)
+ {
+ struct other_breakpoint *other_bp
+ = (struct other_breakpoint *) bp;
+
+ if (other_bp->handler != NULL && (*other_bp->handler) (stop_pc))
+ {
+ *bp_link = bp->next;
+
+ release_breakpoint (proc, bp);
+
+ bp = *bp_link;
+ continue;
+ }
+ }
+ }
+
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+}
+
+int
+breakpoint_here (CORE_ADDR addr)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if ((bp->raw_type == raw_bkpt_type_sw
+ || bp->raw_type == raw_bkpt_type_hw)
+ && bp->pc == addr)
+ return 1;
+
+ return 0;
+}
+
+int
+breakpoint_inserted_here (CORE_ADDR addr)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if ((bp->raw_type == raw_bkpt_type_sw
+ || bp->raw_type == raw_bkpt_type_hw)
+ && bp->pc == addr
+ && bp->inserted)
+ return 1;
+
+ return 0;
+}
+
+/* See mem-break.h. */
+
+int
+software_breakpoint_inserted_here (CORE_ADDR addr)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if (bp->raw_type == raw_bkpt_type_sw
+ && bp->pc == addr
+ && bp->inserted)
+ return 1;
+
+ return 0;
+}
+
+/* See mem-break.h. */
+
+int
+hardware_breakpoint_inserted_here (CORE_ADDR addr)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if (bp->raw_type == raw_bkpt_type_hw
+ && bp->pc == addr
+ && bp->inserted)
+ return 1;
+
+ return 0;
+}
+
+/* See mem-break.h. */
+
+int
+single_step_breakpoint_inserted_here (CORE_ADDR addr)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ if (bp->type == single_step_breakpoint
+ && bp->raw->pc == addr
+ && bp->raw->inserted)
+ return 1;
+
+ return 0;
+}
+
+static int
+validate_inserted_breakpoint (struct raw_breakpoint *bp)
+{
+ unsigned char *buf;
+ int err;
+
+ gdb_assert (bp->inserted);
+ gdb_assert (bp->raw_type == raw_bkpt_type_sw);
+
+ buf = (unsigned char *) alloca (bp_size (bp));
+ err = (*the_target->read_memory) (bp->pc, buf, bp_size (bp));
+ if (err || memcmp (buf, bp_opcode (bp), bp_size (bp)) != 0)
+ {
+ /* Tag it as gone. */
+ bp->inserted = -1;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+delete_disabled_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp, *next;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = next)
+ {
+ next = bp->next;
+ if (bp->raw->inserted < 0)
+ {
+ /* If single_step_breakpoints become disabled, that means the
+ manipulations (insertion and removal) of them are wrong. */
+ gdb_assert (bp->type != single_step_breakpoint);
+ delete_breakpoint_1 (proc, bp);
+ }
+ }
+}
+
+/* Check if breakpoints we inserted still appear to be inserted. They
+ may disappear due to a shared library unload, and worse, a new
+ shared library may be reloaded at the same address as the
+ previously unloaded one. If that happens, we should make sure that
+ the shadow memory of the old breakpoints isn't used when reading or
+ writing memory. */
+
+void
+validate_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ struct raw_breakpoint *raw = bp->raw;
+
+ if (raw->raw_type == raw_bkpt_type_sw && raw->inserted > 0)
+ validate_inserted_breakpoint (raw);
+ }
+
+ delete_disabled_breakpoints ();
+}
+
+void
+check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp = proc->raw_breakpoints;
+ struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
+ CORE_ADDR mem_end = mem_addr + mem_len;
+ int disabled_one = 0;
+
+ for (; jp != NULL; jp = jp->next)
+ {
+ CORE_ADDR bp_end = jp->pc + jp->length;
+ CORE_ADDR start, end;
+ int copy_offset, copy_len, buf_offset;
+
+ gdb_assert (fast_tracepoint_jump_shadow (jp) >= buf + mem_len
+ || buf >= fast_tracepoint_jump_shadow (jp) + (jp)->length);
+
+ if (mem_addr >= bp_end)
+ continue;
+ if (jp->pc >= mem_end)
+ continue;
+
+ start = jp->pc;
+ if (mem_addr > start)
+ start = mem_addr;
+
+ end = bp_end;
+ if (end > mem_end)
+ end = mem_end;
+
+ copy_len = end - start;
+ copy_offset = start - jp->pc;
+ buf_offset = start - mem_addr;
+
+ if (jp->inserted)
+ memcpy (buf + buf_offset,
+ fast_tracepoint_jump_shadow (jp) + copy_offset,
+ copy_len);
+ }
+
+ for (; bp != NULL; bp = bp->next)
+ {
+ CORE_ADDR bp_end = bp->pc + bp_size (bp);
+ CORE_ADDR start, end;
+ int copy_offset, copy_len, buf_offset;
+
+ if (bp->raw_type != raw_bkpt_type_sw)
+ continue;
+
+ gdb_assert (bp->old_data >= buf + mem_len
+ || buf >= &bp->old_data[sizeof (bp->old_data)]);
+
+ if (mem_addr >= bp_end)
+ continue;
+ if (bp->pc >= mem_end)
+ continue;
+
+ start = bp->pc;
+ if (mem_addr > start)
+ start = mem_addr;
+
+ end = bp_end;
+ if (end > mem_end)
+ end = mem_end;
+
+ copy_len = end - start;
+ copy_offset = start - bp->pc;
+ buf_offset = start - mem_addr;
+
+ if (bp->inserted > 0)
+ {
+ if (validate_inserted_breakpoint (bp))
+ memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len);
+ else
+ disabled_one = 1;
+ }
+ }
+
+ if (disabled_one)
+ delete_disabled_breakpoints ();
+}
+
+void
+check_mem_write (CORE_ADDR mem_addr, unsigned char *buf,
+ const unsigned char *myaddr, int mem_len)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp = proc->raw_breakpoints;
+ struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
+ CORE_ADDR mem_end = mem_addr + mem_len;
+ int disabled_one = 0;
+
+ /* First fast tracepoint jumps, then breakpoint traps on top. */
+
+ for (; jp != NULL; jp = jp->next)
+ {
+ CORE_ADDR jp_end = jp->pc + jp->length;
+ CORE_ADDR start, end;
+ int copy_offset, copy_len, buf_offset;
+
+ gdb_assert (fast_tracepoint_jump_shadow (jp) >= myaddr + mem_len
+ || myaddr >= fast_tracepoint_jump_shadow (jp) + (jp)->length);
+ gdb_assert (fast_tracepoint_jump_insn (jp) >= buf + mem_len
+ || buf >= fast_tracepoint_jump_insn (jp) + (jp)->length);
+
+ if (mem_addr >= jp_end)
+ continue;
+ if (jp->pc >= mem_end)
+ continue;
+
+ start = jp->pc;
+ if (mem_addr > start)
+ start = mem_addr;
+
+ end = jp_end;
+ if (end > mem_end)
+ end = mem_end;
+
+ copy_len = end - start;
+ copy_offset = start - jp->pc;
+ buf_offset = start - mem_addr;
+
+ memcpy (fast_tracepoint_jump_shadow (jp) + copy_offset,
+ myaddr + buf_offset, copy_len);
+ if (jp->inserted)
+ memcpy (buf + buf_offset,
+ fast_tracepoint_jump_insn (jp) + copy_offset, copy_len);
+ }
+
+ for (; bp != NULL; bp = bp->next)
+ {
+ CORE_ADDR bp_end = bp->pc + bp_size (bp);
+ CORE_ADDR start, end;
+ int copy_offset, copy_len, buf_offset;
+
+ if (bp->raw_type != raw_bkpt_type_sw)
+ continue;
+
+ gdb_assert (bp->old_data >= myaddr + mem_len
+ || myaddr >= &bp->old_data[sizeof (bp->old_data)]);
+
+ if (mem_addr >= bp_end)
+ continue;
+ if (bp->pc >= mem_end)
+ continue;
+
+ start = bp->pc;
+ if (mem_addr > start)
+ start = mem_addr;
+
+ end = bp_end;
+ if (end > mem_end)
+ end = mem_end;
+
+ copy_len = end - start;
+ copy_offset = start - bp->pc;
+ buf_offset = start - mem_addr;
+
+ memcpy (bp->old_data + copy_offset, myaddr + buf_offset, copy_len);
+ if (bp->inserted > 0)
+ {
+ if (validate_inserted_breakpoint (bp))
+ memcpy (buf + buf_offset, bp_opcode (bp) + copy_offset, copy_len);
+ else
+ disabled_one = 1;
+ }
+ }
+
+ if (disabled_one)
+ delete_disabled_breakpoints ();
+}
+
+/* Delete all breakpoints, and un-insert them from the inferior. */
+
+void
+delete_all_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+
+ while (proc->breakpoints)
+ delete_breakpoint_1 (proc, proc->breakpoints);
+}
+
+/* Clear the "inserted" flag in all breakpoints. */
+
+void
+mark_breakpoints_out (struct process_info *proc)
+{
+ struct raw_breakpoint *raw_bp;
+
+ for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next)
+ raw_bp->inserted = 0;
+}
+
+/* Release all breakpoints, but do not try to un-insert them from the
+ inferior. */
+
+void
+free_all_breakpoints (struct process_info *proc)
+{
+ mark_breakpoints_out (proc);
+
+ /* Note: use PROC explicitly instead of deferring to
+ delete_all_breakpoints --- CURRENT_INFERIOR may already have been
+ released when we get here. There should be no call to
+ current_process from here on. */
+ while (proc->breakpoints)
+ delete_breakpoint_1 (proc, proc->breakpoints);
+}
+
+/* Clone an agent expression. */
+
+static struct agent_expr *
+clone_agent_expr (const struct agent_expr *src_ax)
+{
+ struct agent_expr *ax;
+
+ ax = XCNEW (struct agent_expr);
+ ax->length = src_ax->length;
+ ax->bytes = (unsigned char *) xcalloc (ax->length, 1);
+ memcpy (ax->bytes, src_ax->bytes, ax->length);
+ return ax;
+}
+
+/* Deep-copy the contents of one breakpoint to another. */
+
+static struct breakpoint *
+clone_one_breakpoint (const struct breakpoint *src, ptid_t ptid)
+{
+ struct breakpoint *dest;
+ struct raw_breakpoint *dest_raw;
+
+ /* Clone the raw breakpoint. */
+ dest_raw = XCNEW (struct raw_breakpoint);
+ dest_raw->raw_type = src->raw->raw_type;
+ dest_raw->refcount = src->raw->refcount;
+ dest_raw->pc = src->raw->pc;
+ dest_raw->kind = src->raw->kind;
+ memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
+ dest_raw->inserted = src->raw->inserted;
+
+ /* Clone the high-level breakpoint. */
+ if (is_gdb_breakpoint (src->type))
+ {
+ struct gdb_breakpoint *gdb_dest = XCNEW (struct gdb_breakpoint);
+ struct point_cond_list *current_cond;
+ struct point_cond_list *new_cond;
+ struct point_cond_list *cond_tail = NULL;
+ struct point_command_list *current_cmd;
+ struct point_command_list *new_cmd;
+ struct point_command_list *cmd_tail = NULL;
+
+ /* Clone the condition list. */
+ for (current_cond = ((struct gdb_breakpoint *) src)->cond_list;
+ current_cond != NULL;
+ current_cond = current_cond->next)
+ {
+ new_cond = XCNEW (struct point_cond_list);
+ new_cond->cond = clone_agent_expr (current_cond->cond);
+ APPEND_TO_LIST (&gdb_dest->cond_list, new_cond, cond_tail);
+ }
+
+ /* Clone the command list. */
+ for (current_cmd = ((struct gdb_breakpoint *) src)->command_list;
+ current_cmd != NULL;
+ current_cmd = current_cmd->next)
+ {
+ new_cmd = XCNEW (struct point_command_list);
+ new_cmd->cmd = clone_agent_expr (current_cmd->cmd);
+ new_cmd->persistence = current_cmd->persistence;
+ APPEND_TO_LIST (&gdb_dest->command_list, new_cmd, cmd_tail);
+ }
+
+ dest = (struct breakpoint *) gdb_dest;
+ }
+ else if (src->type == other_breakpoint)
+ {
+ struct other_breakpoint *other_dest = XCNEW (struct other_breakpoint);
+
+ other_dest->handler = ((struct other_breakpoint *) src)->handler;
+ dest = (struct breakpoint *) other_dest;
+ }
+ else if (src->type == single_step_breakpoint)
+ {
+ struct single_step_breakpoint *ss_dest
+ = XCNEW (struct single_step_breakpoint);
+
+ dest = (struct breakpoint *) ss_dest;
+ /* Since single-step breakpoint is thread specific, don't copy
+ thread id from SRC, use ID instead. */
+ ss_dest->ptid = ptid;
+ }
+ else
+ gdb_assert_not_reached ("unhandled breakpoint type");
+
+ dest->type = src->type;
+ dest->raw = dest_raw;
+
+ return dest;
+}
+
+/* See mem-break.h. */
+
+void
+clone_all_breakpoints (struct thread_info *child_thread,
+ const struct thread_info *parent_thread)
+{
+ const struct breakpoint *bp;
+ struct breakpoint *new_bkpt;
+ struct breakpoint *bkpt_tail = NULL;
+ struct raw_breakpoint *raw_bkpt_tail = NULL;
+ struct process_info *child_proc = get_thread_process (child_thread);
+ struct process_info *parent_proc = get_thread_process (parent_thread);
+ struct breakpoint **new_list = &child_proc->breakpoints;
+ struct raw_breakpoint **new_raw_list = &child_proc->raw_breakpoints;
+
+ for (bp = parent_proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ new_bkpt = clone_one_breakpoint (bp, ptid_of (child_thread));
+ APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
+ APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
+ }
+}
+++ /dev/null
-/* Notification to GDB.
- Copyright (C) 1989-2020 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/>. */
-
-/* Async notifications to GDB. When the state of remote target is
- changed or something interesting to GDB happened, async
- notifications are used to tell GDB.
-
- Each type of notification is represented by an object
- 'struct notif_server', in which there is a queue for events to GDB
- represented by 'struct notif_event'. GDBserver writes (by means of
- 'write' field) each event in the queue into the buffer and send the
- contents in buffer to GDB. The contents in buffer is specified in
- RSP. See more in the comments to field 'queue' of
- 'struct notif_server'.
-
- Here is the workflow of sending events and managing queue:
- 1. At any time, when something interesting FOO happens, a object
- of 'struct notif_event' or its sub-class EVENT is created for FOO.
-
- 2. Enque EVENT to the 'queue' field of 'struct notif_server' for
- FOO and send corresponding notification packet to GDB if EVENT is
- the first one.
- #1 and #2 are done by function 'notif_push'.
-
- 3. EVENT is not deque'ed until the ack of FOO from GDB arrives.
- Before ack of FOO arrives, FOO happens again, a new object of
- EVENT is created and enque EVENT silently.
- Once GDB has a chance to ack to FOO, it sends an ack to GDBserver,
- and GDBserver repeatedly sends events to GDB and gets ack of FOO,
- until queue is empty. Then, GDBserver sends 'OK' to GDB that all
- queued notification events are done.
-
- # 3 is done by function 'handle_notif_ack'. */
-
-#include "server.h"
-#include "notif.h"
-
-static struct notif_server *notifs[] =
-{
- ¬if_stop,
-};
-
-/* Write another event or an OK, if there are no more left, to
- OWN_BUF. */
-
-void
-notif_write_event (struct notif_server *notif, char *own_buf)
-{
- if (!notif->queue.empty ())
- {
- struct notif_event *event = notif->queue.front ();
-
- notif->write (event, own_buf);
- }
- else
- write_ok (own_buf);
-}
-
-/* Handle the ack in buffer OWN_BUF,and packet length is PACKET_LEN.
- Return 1 if the ack is handled, and return 0 if the contents
- in OWN_BUF is not a ack. */
-
-int
-handle_notif_ack (char *own_buf, int packet_len)
-{
- size_t i;
- struct notif_server *np;
-
- for (i = 0; i < ARRAY_SIZE (notifs); i++)
- {
- const char *ack_name = notifs[i]->ack_name;
-
- if (startswith (own_buf, ack_name)
- && packet_len == strlen (ack_name))
- break;
- }
-
- if (i == ARRAY_SIZE (notifs))
- return 0;
-
- np = notifs[i];
-
- /* If we're waiting for GDB to acknowledge a pending event,
- consider that done. */
- if (!np->queue.empty ())
- {
- struct notif_event *head = np->queue.front ();
- np->queue.pop_front ();
-
- if (remote_debug)
- debug_printf ("%s: acking %d\n", np->ack_name,
- (int) np->queue.size ());
-
- delete head;
- }
-
- notif_write_event (np, own_buf);
-
- return 1;
-}
-
-/* Put EVENT to the queue of NOTIF. */
-
-void
-notif_event_enque (struct notif_server *notif,
- struct notif_event *event)
-{
- notif->queue.push_back (event);
-
- if (remote_debug)
- debug_printf ("pending events: %s %d\n", notif->notif_name,
- (int) notif->queue.size ());
-
-}
-
-/* Push one event NEW_EVENT of notification NP into NP->queue. */
-
-void
-notif_push (struct notif_server *np, struct notif_event *new_event)
-{
- bool is_first_event = np->queue.empty ();
-
- /* Something interesting. Tell GDB about it. */
- notif_event_enque (np, new_event);
-
- /* If this is the first stop reply in the queue, then inform GDB
- about it, by sending a corresponding notification. */
- if (is_first_event)
- {
- char buf[PBUFSIZ];
- char *p = buf;
-
- xsnprintf (p, PBUFSIZ, "%s:", np->notif_name);
- p += strlen (p);
-
- np->write (new_event, p);
- putpkt_notif (buf);
- }
-}
--- /dev/null
+/* Notification to GDB.
+ Copyright (C) 1989-2020 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/>. */
+
+/* Async notifications to GDB. When the state of remote target is
+ changed or something interesting to GDB happened, async
+ notifications are used to tell GDB.
+
+ Each type of notification is represented by an object
+ 'struct notif_server', in which there is a queue for events to GDB
+ represented by 'struct notif_event'. GDBserver writes (by means of
+ 'write' field) each event in the queue into the buffer and send the
+ contents in buffer to GDB. The contents in buffer is specified in
+ RSP. See more in the comments to field 'queue' of
+ 'struct notif_server'.
+
+ Here is the workflow of sending events and managing queue:
+ 1. At any time, when something interesting FOO happens, a object
+ of 'struct notif_event' or its sub-class EVENT is created for FOO.
+
+ 2. Enque EVENT to the 'queue' field of 'struct notif_server' for
+ FOO and send corresponding notification packet to GDB if EVENT is
+ the first one.
+ #1 and #2 are done by function 'notif_push'.
+
+ 3. EVENT is not deque'ed until the ack of FOO from GDB arrives.
+ Before ack of FOO arrives, FOO happens again, a new object of
+ EVENT is created and enque EVENT silently.
+ Once GDB has a chance to ack to FOO, it sends an ack to GDBserver,
+ and GDBserver repeatedly sends events to GDB and gets ack of FOO,
+ until queue is empty. Then, GDBserver sends 'OK' to GDB that all
+ queued notification events are done.
+
+ # 3 is done by function 'handle_notif_ack'. */
+
+#include "server.h"
+#include "notif.h"
+
+static struct notif_server *notifs[] =
+{
+ ¬if_stop,
+};
+
+/* Write another event or an OK, if there are no more left, to
+ OWN_BUF. */
+
+void
+notif_write_event (struct notif_server *notif, char *own_buf)
+{
+ if (!notif->queue.empty ())
+ {
+ struct notif_event *event = notif->queue.front ();
+
+ notif->write (event, own_buf);
+ }
+ else
+ write_ok (own_buf);
+}
+
+/* Handle the ack in buffer OWN_BUF,and packet length is PACKET_LEN.
+ Return 1 if the ack is handled, and return 0 if the contents
+ in OWN_BUF is not a ack. */
+
+int
+handle_notif_ack (char *own_buf, int packet_len)
+{
+ size_t i;
+ struct notif_server *np;
+
+ for (i = 0; i < ARRAY_SIZE (notifs); i++)
+ {
+ const char *ack_name = notifs[i]->ack_name;
+
+ if (startswith (own_buf, ack_name)
+ && packet_len == strlen (ack_name))
+ break;
+ }
+
+ if (i == ARRAY_SIZE (notifs))
+ return 0;
+
+ np = notifs[i];
+
+ /* If we're waiting for GDB to acknowledge a pending event,
+ consider that done. */
+ if (!np->queue.empty ())
+ {
+ struct notif_event *head = np->queue.front ();
+ np->queue.pop_front ();
+
+ if (remote_debug)
+ debug_printf ("%s: acking %d\n", np->ack_name,
+ (int) np->queue.size ());
+
+ delete head;
+ }
+
+ notif_write_event (np, own_buf);
+
+ return 1;
+}
+
+/* Put EVENT to the queue of NOTIF. */
+
+void
+notif_event_enque (struct notif_server *notif,
+ struct notif_event *event)
+{
+ notif->queue.push_back (event);
+
+ if (remote_debug)
+ debug_printf ("pending events: %s %d\n", notif->notif_name,
+ (int) notif->queue.size ());
+
+}
+
+/* Push one event NEW_EVENT of notification NP into NP->queue. */
+
+void
+notif_push (struct notif_server *np, struct notif_event *new_event)
+{
+ bool is_first_event = np->queue.empty ();
+
+ /* Something interesting. Tell GDB about it. */
+ notif_event_enque (np, new_event);
+
+ /* If this is the first stop reply in the queue, then inform GDB
+ about it, by sending a corresponding notification. */
+ if (is_first_event)
+ {
+ char buf[PBUFSIZ];
+ char *p = buf;
+
+ xsnprintf (p, PBUFSIZ, "%s:", np->notif_name);
+ p += strlen (p);
+
+ np->write (new_event, p);
+ putpkt_notif (buf);
+ }
+}
+++ /dev/null
-/* QNX Neutrino specific low level interface, for the remote server
- for GDB.
- Copyright (C) 2009-2020 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 "gdbthread.h"
-#include "nto-low.h"
-#include "hostio.h"
-#include "debug.h"
-
-#include <limits.h>
-#include <fcntl.h>
-#include <spawn.h>
-#include <sys/procfs.h>
-#include <sys/auxv.h>
-#include <sys/iomgr.h>
-#include <sys/neutrino.h>
-
-
-int using_threads = 1;
-
-const struct target_desc *nto_tdesc;
-
-static void
-nto_trace (const char *fmt, ...)
-{
- va_list arg_list;
-
- if (debug_threads == 0)
- return;
- fprintf (stderr, "nto:");
- va_start (arg_list, fmt);
- vfprintf (stderr, fmt, arg_list);
- va_end (arg_list);
-}
-
-#define TRACE nto_trace
-
-/* Structure holding neutrino specific information about
- inferior. */
-
-struct nto_inferior
-{
- char nto_procfs_path[PATH_MAX];
- int ctl_fd;
- pid_t pid;
- int exit_signo; /* For tracking exit status. */
-};
-
-static struct nto_inferior nto_inferior;
-
-static void
-init_nto_inferior (struct nto_inferior *nto_inferior)
-{
- memset (nto_inferior, 0, sizeof (struct nto_inferior));
- nto_inferior->ctl_fd = -1;
- nto_inferior->pid = -1;
-}
-
-static void
-do_detach (void)
-{
- if (nto_inferior.ctl_fd != -1)
- {
- nto_trace ("Closing fd\n");
- close (nto_inferior.ctl_fd);
- init_nto_inferior (&nto_inferior);
- }
-}
-
-/* Set current thread. Return 1 on success, 0 otherwise. */
-
-static int
-nto_set_thread (ptid_t ptid)
-{
- int res = 0;
-
- TRACE ("%s pid: %d tid: %ld\n", __func__, ptid.pid (),
- ptid.lwp ());
- if (nto_inferior.ctl_fd != -1
- && ptid != null_ptid
- && ptid != minus_one_ptid)
- {
- pthread_t tid = ptid.lwp ();
-
- if (EOK == devctl (nto_inferior.ctl_fd, DCMD_PROC_CURTHREAD, &tid,
- sizeof (tid), 0))
- res = 1;
- else
- TRACE ("%s: Error: failed to set current thread\n", __func__);
- }
- return res;
-}
-
-/* This function will determine all alive threads. Note that we do not list
- dead but unjoined threads even though they are still in the process' thread
- list.
-
- NTO_INFERIOR must not be NULL. */
-
-static void
-nto_find_new_threads (struct nto_inferior *nto_inferior)
-{
- pthread_t tid;
-
- TRACE ("%s pid:%d\n", __func__, nto_inferior->pid);
-
- if (nto_inferior->ctl_fd == -1)
- return;
-
- for (tid = 1;; ++tid)
- {
- procfs_status status;
- ptid_t ptid;
- int err;
-
- status.tid = tid;
- err = devctl (nto_inferior->ctl_fd, DCMD_PROC_TIDSTATUS, &status,
- sizeof (status), 0);
-
- if (err != EOK || status.tid == 0)
- break;
-
- /* All threads in between are gone. */
- while (tid != status.tid || status.state == STATE_DEAD)
- {
- struct thread_info *ti;
-
- ptid = ptid_t (nto_inferior->pid, tid, 0);
- ti = find_thread_ptid (ptid);
- if (ti != NULL)
- {
- TRACE ("Removing thread %d\n", tid);
- remove_thread (ti);
- }
- if (tid == status.tid)
- break;
- ++tid;
- }
-
- if (status.state != STATE_DEAD)
- {
- TRACE ("Adding thread %d\n", tid);
- ptid = ptid_t (nto_inferior->pid, tid, 0);
- if (!find_thread_ptid (ptid))
- add_thread (ptid, NULL);
- }
- }
-}
-
-/* Given pid, open procfs path. */
-
-static pid_t
-do_attach (pid_t pid)
-{
- procfs_status status;
- struct sigevent event;
-
- if (nto_inferior.ctl_fd != -1)
- {
- close (nto_inferior.ctl_fd);
- init_nto_inferior (&nto_inferior);
- }
- xsnprintf (nto_inferior.nto_procfs_path, PATH_MAX - 1, "/proc/%d/as", pid);
- nto_inferior.ctl_fd = open (nto_inferior.nto_procfs_path, O_RDWR);
- if (nto_inferior.ctl_fd == -1)
- {
- TRACE ("Failed to open %s\n", nto_inferior.nto_procfs_path);
- init_nto_inferior (&nto_inferior);
- return -1;
- }
- if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, &status, sizeof (status), 0)
- != EOK)
- {
- do_detach ();
- return -1;
- }
- nto_inferior.pid = pid;
- /* Define a sigevent for process stopped notification. */
- event.sigev_notify = SIGEV_SIGNAL_THREAD;
- event.sigev_signo = SIGUSR1;
- event.sigev_code = 0;
- event.sigev_value.sival_ptr = NULL;
- event.sigev_priority = -1;
- devctl (nto_inferior.ctl_fd, DCMD_PROC_EVENT, &event, sizeof (event), 0);
-
- if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
- 0) == EOK
- && (status.flags & _DEBUG_FLAG_STOPPED))
- {
- ptid_t ptid;
- struct process_info *proc;
-
- kill (pid, SIGCONT);
- ptid = ptid_t (status.pid, status.tid, 0);
- the_low_target.arch_setup ();
- proc = add_process (status.pid, 1);
- proc->tdesc = nto_tdesc;
- TRACE ("Adding thread: pid=%d tid=%ld\n", status.pid,
- ptid.lwp ());
- nto_find_new_threads (&nto_inferior);
- }
- else
- {
- do_detach ();
- return -1;
- }
-
- return pid;
-}
-
-/* Read or write LEN bytes from/to inferior's MEMADDR memory address
- into gdbservers's MYADDR buffer. Return number of bytes actually
- transfered. */
-
-static int
-nto_xfer_memory (off_t memaddr, unsigned char *myaddr, int len,
- int dowrite)
-{
- int nbytes = 0;
-
- if (lseek (nto_inferior.ctl_fd, memaddr, SEEK_SET) == memaddr)
- {
- if (dowrite)
- nbytes = write (nto_inferior.ctl_fd, myaddr, len);
- else
- nbytes = read (nto_inferior.ctl_fd, myaddr, len);
- if (nbytes < 0)
- nbytes = 0;
- }
- if (nbytes == 0)
- {
- int e = errno;
- TRACE ("Error in %s : errno=%d (%s)\n", __func__, e, safe_strerror (e));
- }
- return nbytes;
-}
-
-/* Insert or remove breakpoint or watchpoint at address ADDR.
- TYPE can be one of Neutrino breakpoint types. SIZE must be 0 for
- inserting the point, -1 for removing it.
-
- Return 0 on success, 1 otherwise. */
-
-static int
-nto_breakpoint (CORE_ADDR addr, int type, int size)
-{
- procfs_break brk;
-
- brk.type = type;
- brk.addr = addr;
- brk.size = size;
- if (devctl (nto_inferior.ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0)
- != EOK)
- return 1;
- return 0;
-}
-
-/* Read auxiliary vector from inferior's initial stack into gdbserver's
- MYADDR buffer, up to LEN bytes.
-
- Return number of bytes read. */
-
-static int
-nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack,
- unsigned char *myaddr,
- unsigned int len)
-{
- int data_ofs = 0;
- int anint;
- unsigned int len_read = 0;
-
- /* Skip over argc, argv and envp... Comment from ldd.c:
-
- The startup frame is set-up so that we have:
- auxv
- NULL
- ...
- envp2
- envp1 <----- void *frame + (argc + 2) * sizeof(char *)
- NULL
- ...
- argv2
- argv1
- argc <------ void * frame
-
- On entry to ldd, frame gives the address of argc on the stack. */
- if (nto_xfer_memory (initial_stack, (unsigned char *)&anint,
- sizeof (anint), 0) != sizeof (anint))
- return 0;
-
- /* Size of pointer is assumed to be 4 bytes (32 bit arch. ) */
- data_ofs += (anint + 2) * sizeof (void *); /* + 2 comes from argc itself and
- NULL terminating pointer in
- argv. */
-
- /* Now loop over env table: */
- while (nto_xfer_memory (initial_stack + data_ofs,
- (unsigned char *)&anint, sizeof (anint), 0)
- == sizeof (anint))
- {
- data_ofs += sizeof (anint);
- if (anint == 0)
- break;
- }
- initial_stack += data_ofs;
-
- memset (myaddr, 0, len);
- while (len_read <= len - sizeof (auxv_t))
- {
- auxv_t *auxv = (auxv_t *)myaddr;
-
- /* Search backwards until we have read AT_PHDR (num. 3),
- AT_PHENT (num 4), AT_PHNUM (num 5) */
- if (nto_xfer_memory (initial_stack, (unsigned char *)auxv,
- sizeof (auxv_t), 0) == sizeof (auxv_t))
- {
- if (auxv->a_type != AT_NULL)
- {
- auxv++;
- len_read += sizeof (auxv_t);
- }
- if (auxv->a_type == AT_PHNUM) /* That's all we need. */
- break;
- initial_stack += sizeof (auxv_t);
- }
- else
- break;
- }
- TRACE ("auxv: len_read: %d\n", len_read);
- return len_read;
-}
-
-/* Start inferior specified by PROGRAM, using PROGRAM_ARGS as its
- arguments. */
-
-static int
-nto_create_inferior (const char *program,
- const std::vector<char *> &program_args)
-{
- struct inheritance inherit;
- pid_t pid;
- sigset_t set;
- std::string str_program_args = stringify_argv (program_args);
-
- TRACE ("%s %s\n", __func__, program);
- /* Clear any pending SIGUSR1's but keep the behavior the same. */
- signal (SIGUSR1, signal (SIGUSR1, SIG_IGN));
-
- sigemptyset (&set);
- sigaddset (&set, SIGUSR1);
- sigprocmask (SIG_UNBLOCK, &set, NULL);
-
- memset (&inherit, 0, sizeof (inherit));
- inherit.flags |= SPAWN_SETGROUP | SPAWN_HOLD;
- inherit.pgroup = SPAWN_NEWPGROUP;
- pid = spawnp (program, 0, NULL, &inherit,
- (char *) str_program_args.c_str (), 0);
- sigprocmask (SIG_BLOCK, &set, NULL);
-
- if (pid == -1)
- return -1;
-
- if (do_attach (pid) != pid)
- return -1;
-
- return pid;
-}
-
-/* Attach to process PID. */
-
-static int
-nto_attach (unsigned long pid)
-{
- TRACE ("%s %ld\n", __func__, pid);
- if (do_attach (pid) != pid)
- error ("Unable to attach to %ld\n", pid);
- return 0;
-}
-
-/* Send signal to process PID. */
-
-static int
-nto_kill (process_info *proc)
-{
- int pid = proc->pid;
-
- TRACE ("%s %d\n", __func__, pid);
- kill (pid, SIGKILL);
- do_detach ();
- return 0;
-}
-
-/* Detach from process PID. */
-
-static int
-nto_detach (process_info *proc)
-{
- TRACE ("%s %d\n", __func__, proc->pid);
- do_detach ();
- return 0;
-}
-
-static void
-nto_mourn (struct process_info *process)
-{
- remove_process (process);
-}
-
-/* Check if the given thread is alive.
-
- Return 1 if alive, 0 otherwise. */
-
-static int
-nto_thread_alive (ptid_t ptid)
-{
- int res;
-
- TRACE ("%s pid:%d tid:%d\n", __func__, ptid.pid (),
- ptid.lwp ());
- if (SignalKill (0, ptid.pid (), ptid.lwp (),
- 0, 0, 0) == -1)
- res = 0;
- else
- res = 1;
- TRACE ("%s: %s\n", __func__, res ? "yes" : "no");
- return res;
-}
-
-/* Resume inferior's execution. */
-
-static void
-nto_resume (struct thread_resume *resume_info, size_t n)
-{
- /* We can only work in all-stop mode. */
- procfs_status status;
- procfs_run run;
- int err;
-
- TRACE ("%s\n", __func__);
- /* Workaround for aliasing rules violation. */
- sigset_t *run_fault = (sigset_t *) (void *) &run.fault;
-
- nto_set_thread (resume_info->thread);
-
- run.flags = _DEBUG_RUN_FAULT | _DEBUG_RUN_TRACE;
- if (resume_info->kind == resume_step)
- run.flags |= _DEBUG_RUN_STEP;
- run.flags |= _DEBUG_RUN_ARM;
-
- sigemptyset (run_fault);
- sigaddset (run_fault, FLTBPT);
- sigaddset (run_fault, FLTTRACE);
- sigaddset (run_fault, FLTILL);
- sigaddset (run_fault, FLTPRIV);
- sigaddset (run_fault, FLTBOUNDS);
- sigaddset (run_fault, FLTIOVF);
- sigaddset (run_fault, FLTIZDIV);
- sigaddset (run_fault, FLTFPE);
- sigaddset (run_fault, FLTPAGE);
- sigaddset (run_fault, FLTSTACK);
- sigaddset (run_fault, FLTACCESS);
-
- sigemptyset (&run.trace);
- if (resume_info->sig)
- {
- int signal_to_pass;
-
- devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
- 0);
- signal_to_pass = resume_info->sig;
- if (status.why & (_DEBUG_WHY_SIGNALLED | _DEBUG_WHY_FAULTED))
- {
- if (signal_to_pass != status.info.si_signo)
- {
- kill (status.pid, signal_to_pass);
- run.flags |= _DEBUG_RUN_CLRFLT | _DEBUG_RUN_CLRSIG;
- }
- else /* Let it kill the program without telling us. */
- sigdelset (&run.trace, signal_to_pass);
- }
- }
- else
- run.flags |= _DEBUG_RUN_CLRSIG | _DEBUG_RUN_CLRFLT;
-
- sigfillset (&run.trace);
-
- regcache_invalidate ();
-
- err = devctl (nto_inferior.ctl_fd, DCMD_PROC_RUN, &run, sizeof (run), 0);
- if (err != EOK)
- TRACE ("Error: %d \"%s\"\n", err, safe_strerror (err));
-}
-
-/* Wait for inferior's event.
-
- Return ptid of thread that caused the event. */
-
-static ptid_t
-nto_wait (ptid_t ptid,
- struct target_waitstatus *ourstatus, int target_options)
-{
- sigset_t set;
- siginfo_t info;
- procfs_status status;
- const int trace_mask = (_DEBUG_FLAG_TRACE_EXEC | _DEBUG_FLAG_TRACE_RD
- | _DEBUG_FLAG_TRACE_WR | _DEBUG_FLAG_TRACE_MODIFY);
-
- TRACE ("%s\n", __func__);
-
- ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
-
- sigemptyset (&set);
- sigaddset (&set, SIGUSR1);
-
- devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0);
- while (!(status.flags & _DEBUG_FLAG_ISTOP))
- {
- sigwaitinfo (&set, &info);
- devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
- 0);
- }
- nto_find_new_threads (&nto_inferior);
-
- if (status.flags & _DEBUG_FLAG_SSTEP)
- {
- TRACE ("SSTEP\n");
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig = GDB_SIGNAL_TRAP;
- }
- /* Was it a breakpoint? */
- else if (status.flags & trace_mask)
- {
- TRACE ("STOPPED\n");
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig = GDB_SIGNAL_TRAP;
- }
- else if (status.flags & _DEBUG_FLAG_ISTOP)
- {
- TRACE ("ISTOP\n");
- switch (status.why)
- {
- case _DEBUG_WHY_SIGNALLED:
- TRACE (" SIGNALLED\n");
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig =
- gdb_signal_from_host (status.info.si_signo);
- nto_inferior.exit_signo = ourstatus->value.sig;
- break;
- case _DEBUG_WHY_FAULTED:
- TRACE (" FAULTED\n");
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
- if (status.info.si_signo == SIGTRAP)
- {
- ourstatus->value.sig = 0;
- nto_inferior.exit_signo = 0;
- }
- else
- {
- ourstatus->value.sig =
- gdb_signal_from_host (status.info.si_signo);
- nto_inferior.exit_signo = ourstatus->value.sig;
- }
- break;
-
- case _DEBUG_WHY_TERMINATED:
- {
- int waitval = 0;
-
- TRACE (" TERMINATED\n");
- waitpid (ptid.pid (), &waitval, WNOHANG);
- if (nto_inferior.exit_signo)
- {
- /* Abnormal death. */
- ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
- ourstatus->value.sig = nto_inferior.exit_signo;
- }
- else
- {
- /* Normal death. */
- ourstatus->kind = TARGET_WAITKIND_EXITED;
- ourstatus->value.integer = WEXITSTATUS (waitval);
- }
- nto_inferior.exit_signo = 0;
- break;
- }
-
- case _DEBUG_WHY_REQUESTED:
- TRACE ("REQUESTED\n");
- /* We are assuming a requested stop is due to a SIGINT. */
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig = GDB_SIGNAL_INT;
- nto_inferior.exit_signo = 0;
- break;
- }
- }
-
- return ptid_t (status.pid, status.tid, 0);
-}
-
-/* Fetch inferior's registers for currently selected thread (CURRENT_INFERIOR).
- If REGNO is -1, fetch all registers, or REGNO register only otherwise. */
-
-static void
-nto_fetch_registers (struct regcache *regcache, int regno)
-{
- int regsize;
- procfs_greg greg;
-
- TRACE ("%s (regno=%d)\n", __func__, regno);
- if (regno >= the_low_target.num_regs)
- return;
-
- if (current_thread == NULL)
- {
- TRACE ("current_thread is NULL\n");
- return;
- }
- ptid_t ptid = ptid_of (current_thread);
- if (!nto_set_thread (ptid))
- return;
-
- if (devctl (nto_inferior.ctl_fd, DCMD_PROC_GETGREG, &greg, sizeof (greg),
- ®size) == EOK)
- {
- if (regno == -1) /* All registers. */
- {
- for (regno = 0; regno != the_low_target.num_regs; ++regno)
- {
- const unsigned int registeroffset
- = the_low_target.register_offset (regno);
- supply_register (regcache, regno,
- ((char *)&greg) + registeroffset);
- }
- }
- else
- {
- const unsigned int registeroffset
- = the_low_target.register_offset (regno);
- if (registeroffset == -1)
- return;
- supply_register (regcache, regno, ((char *)&greg) + registeroffset);
- }
- }
- else
- TRACE ("ERROR reading registers from inferior.\n");
-}
-
-/* Store registers for currently selected thread (CURRENT_INFERIOR).
- We always store all registers, regardless of REGNO. */
-
-static void
-nto_store_registers (struct regcache *regcache, int regno)
-{
- procfs_greg greg;
- int err;
-
- TRACE ("%s (regno:%d)\n", __func__, regno);
-
- if (current_thread == NULL)
- {
- TRACE ("current_thread is NULL\n");
- return;
- }
- ptid_t ptid = ptid_of (current_thread);
- if (!nto_set_thread (ptid))
- return;
-
- memset (&greg, 0, sizeof (greg));
- for (regno = 0; regno != the_low_target.num_regs; ++regno)
- {
- const unsigned int regoffset
- = the_low_target.register_offset (regno);
- collect_register (regcache, regno, ((char *)&greg) + regoffset);
- }
- err = devctl (nto_inferior.ctl_fd, DCMD_PROC_SETGREG, &greg, sizeof (greg),
- 0);
- if (err != EOK)
- TRACE ("Error: setting registers.\n");
-}
-
-/* Read LEN bytes from inferior's memory address MEMADDR into
- gdbserver's MYADDR buffer.
-
- Return 0 on success -1 otherwise. */
-
-static int
-nto_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- TRACE ("%s memaddr:0x%08lx, len:%d\n", __func__, memaddr, len);
-
- if (nto_xfer_memory (memaddr, myaddr, len, 0) != len)
- {
- TRACE ("Failed to read memory\n");
- return -1;
- }
-
- return 0;
-}
-
-/* Write LEN bytes from gdbserver's buffer MYADDR into inferior's
- memory at address MEMADDR.
-
- Return 0 on success -1 otherwise. */
-
-static int
-nto_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
-{
- int len_written;
-
- TRACE ("%s memaddr: 0x%08llx len: %d\n", __func__, memaddr, len);
- if ((len_written = nto_xfer_memory (memaddr, (unsigned char *)myaddr, len,
- 1))
- != len)
- {
- TRACE ("Wanted to write: %d but written: %d\n", len, len_written);
- return -1;
- }
-
- return 0;
-}
-
-/* Stop inferior. We always stop all threads. */
-
-static void
-nto_request_interrupt (void)
-{
- TRACE ("%s\n", __func__);
- nto_set_thread (ptid_t (nto_inferior.pid, 1, 0));
- if (EOK != devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, NULL, 0, 0))
- TRACE ("Error stopping inferior.\n");
-}
-
-/* Read auxiliary vector from inferior's memory into gdbserver's buffer
- MYADDR. We always read whole auxv.
-
- Return number of bytes stored in MYADDR buffer, 0 if OFFSET > 0
- or -1 on error. */
-
-static int
-nto_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
-{
- int err;
- CORE_ADDR initial_stack;
- procfs_info procinfo;
-
- TRACE ("%s\n", __func__);
- if (offset > 0)
- return 0;
-
- err = devctl (nto_inferior.ctl_fd, DCMD_PROC_INFO, &procinfo,
- sizeof procinfo, 0);
- if (err != EOK)
- return -1;
-
- initial_stack = procinfo.initial_stack;
-
- return nto_read_auxv_from_initial_stack (initial_stack, myaddr, len);
-}
-
-static int
-nto_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_SW_BP:
- case Z_PACKET_HW_BP:
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_READ_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
- return 0;
- }
-}
-
-/* Insert {break/watch}point at address ADDR. SIZE is not used. */
-
-static int
-nto_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- int wtype = _DEBUG_BREAK_HW; /* Always request HW. */
-
- TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, size);
- switch (type)
- {
- case raw_bkpt_type_sw:
- wtype = _DEBUG_BREAK_EXEC;
- break;
- case raw_bkpt_type_hw:
- wtype |= _DEBUG_BREAK_EXEC;
- break;
- case raw_bkpt_type_write_wp:
- wtype |= _DEBUG_BREAK_RW;
- break;
- case raw_bkpt_type_read_wp:
- wtype |= _DEBUG_BREAK_RD;
- break;
- case raw_bkpt_type_access_wp:
- wtype |= _DEBUG_BREAK_RW;
- break;
- default:
- return 1; /* Not supported. */
- }
- return nto_breakpoint (addr, wtype, 0);
-}
-
-/* Remove {break/watch}point at address ADDR. SIZE is not used. */
-
-static int
-nto_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- int wtype = _DEBUG_BREAK_HW; /* Always request HW. */
-
- TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, size);
- switch (type)
- {
- case raw_bkpt_type_sw:
- wtype = _DEBUG_BREAK_EXEC;
- break;
- case raw_bkpt_type_hw:
- wtype |= _DEBUG_BREAK_EXEC;
- break;
- case raw_bkpt_type_write_wp:
- wtype |= _DEBUG_BREAK_RW;
- break;
- case raw_bkpt_type_read_wp:
- wtype |= _DEBUG_BREAK_RD;
- break;
- case raw_bkpt_type_access_wp:
- wtype |= _DEBUG_BREAK_RW;
- break;
- default:
- return 1; /* Not supported. */
- }
- return nto_breakpoint (addr, wtype, -1);
-}
-
-/* Check if the reason of stop for current thread (CURRENT_INFERIOR) is
- a watchpoint.
-
- Return 1 if stopped by watchpoint, 0 otherwise. */
-
-static int
-nto_stopped_by_watchpoint (void)
-{
- int ret = 0;
-
- TRACE ("%s\n", __func__);
- if (nto_inferior.ctl_fd != -1 && current_thread != NULL)
- {
- ptid_t ptid = ptid_of (current_thread);
- if (nto_set_thread (ptid))
- {
- const int watchmask = _DEBUG_FLAG_TRACE_RD | _DEBUG_FLAG_TRACE_WR
- | _DEBUG_FLAG_TRACE_MODIFY;
- procfs_status status;
- int err;
-
- err = devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
- sizeof (status), 0);
- if (err == EOK && (status.flags & watchmask))
- ret = 1;
- }
- }
- TRACE ("%s: %s\n", __func__, ret ? "yes" : "no");
- return ret;
-}
-
-/* Get instruction pointer for CURRENT_INFERIOR thread.
-
- Return inferior's instruction pointer value, or 0 on error. */
-
-static CORE_ADDR
-nto_stopped_data_address (void)
-{
- CORE_ADDR ret = (CORE_ADDR)0;
-
- TRACE ("%s\n", __func__);
- if (nto_inferior.ctl_fd != -1 && current_thread != NULL)
- {
- ptid_t ptid = ptid_of (current_thread);
-
- if (nto_set_thread (ptid))
- {
- procfs_status status;
-
- if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
- sizeof (status), 0) == EOK)
- ret = status.ip;
- }
- }
- TRACE ("%s: 0x%08lx\n", __func__, ret);
- return ret;
-}
-
-/* We do not currently support non-stop. */
-
-static int
-nto_supports_non_stop (void)
-{
- TRACE ("%s\n", __func__);
- return 0;
-}
-
-/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-nto_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = the_low_target.breakpoint_len;
- return the_low_target.breakpoint;
-}
-
-
-static process_stratum_target nto_target_ops = {
- nto_create_inferior,
- NULL, /* post_create_inferior */
- nto_attach,
- nto_kill,
- nto_detach,
- nto_mourn,
- NULL, /* nto_join */
- nto_thread_alive,
- nto_resume,
- nto_wait,
- nto_fetch_registers,
- nto_store_registers,
- NULL, /* prepare_to_access_memory */
- NULL, /* done_accessing_memory */
- nto_read_memory,
- nto_write_memory,
- NULL, /* nto_look_up_symbols */
- nto_request_interrupt,
- nto_read_auxv,
- nto_supports_z_point_type,
- nto_insert_point,
- nto_remove_point,
- NULL, /* stopped_by_sw_breakpoint */
- NULL, /* supports_stopped_by_sw_breakpoint */
- NULL, /* stopped_by_hw_breakpoint */
- NULL, /* supports_stopped_by_hw_breakpoint */
- target_can_do_hardware_single_step,
- nto_stopped_by_watchpoint,
- nto_stopped_data_address,
- NULL, /* nto_read_offsets */
- NULL, /* thread_db_set_tls_address */
- hostio_last_error_from_errno,
- NULL, /* nto_qxfer_osdata */
- NULL, /* xfer_siginfo */
- nto_supports_non_stop,
- NULL, /* async */
- NULL, /* start_non_stop */
- NULL, /* supports_multi_process */
- NULL, /* supports_fork_events */
- NULL, /* supports_vfork_events */
- NULL, /* supports_exec_events */
- NULL, /* handle_new_gdb_connection */
- NULL, /* handle_monitor_command */
- NULL, /* core_of_thread */
- NULL, /* read_loadmap */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* read_pc */
- NULL, /* write_pc */
- NULL, /* thread_stopped */
- NULL, /* get_tib_address */
- NULL, /* pause_all */
- NULL, /* unpause_all */
- NULL, /* stabilize_threads */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* supports_disable_randomization */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* qxfer_libraries_svr4 */
- NULL, /* support_agent */
- NULL, /* enable_btrace */
- NULL, /* disable_btrace */
- NULL, /* read_btrace */
- NULL, /* read_btrace_conf */
- NULL, /* supports_range_stepping */
- NULL, /* pid_to_exec_file */
- NULL, /* multifs_open */
- NULL, /* multifs_unlink */
- NULL, /* multifs_readlink */
- NULL, /* breakpoint_kind_from_pc */
- nto_sw_breakpoint_from_kind,
-};
-
-
-/* Global function called by server.c. Initializes QNX Neutrino
- gdbserver. */
-
-void
-initialize_low (void)
-{
- sigset_t set;
-
- TRACE ("%s\n", __func__);
- set_target_ops (&nto_target_ops);
-
- /* We use SIGUSR1 to gain control after we block waiting for a process.
- We use sigwaitevent to wait. */
- sigemptyset (&set);
- sigaddset (&set, SIGUSR1);
- sigprocmask (SIG_BLOCK, &set, NULL);
-}
-
--- /dev/null
+/* QNX Neutrino specific low level interface, for the remote server
+ for GDB.
+ Copyright (C) 2009-2020 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 "gdbthread.h"
+#include "nto-low.h"
+#include "hostio.h"
+#include "debug.h"
+
+#include <limits.h>
+#include <fcntl.h>
+#include <spawn.h>
+#include <sys/procfs.h>
+#include <sys/auxv.h>
+#include <sys/iomgr.h>
+#include <sys/neutrino.h>
+
+
+int using_threads = 1;
+
+const struct target_desc *nto_tdesc;
+
+static void
+nto_trace (const char *fmt, ...)
+{
+ va_list arg_list;
+
+ if (debug_threads == 0)
+ return;
+ fprintf (stderr, "nto:");
+ va_start (arg_list, fmt);
+ vfprintf (stderr, fmt, arg_list);
+ va_end (arg_list);
+}
+
+#define TRACE nto_trace
+
+/* Structure holding neutrino specific information about
+ inferior. */
+
+struct nto_inferior
+{
+ char nto_procfs_path[PATH_MAX];
+ int ctl_fd;
+ pid_t pid;
+ int exit_signo; /* For tracking exit status. */
+};
+
+static struct nto_inferior nto_inferior;
+
+static void
+init_nto_inferior (struct nto_inferior *nto_inferior)
+{
+ memset (nto_inferior, 0, sizeof (struct nto_inferior));
+ nto_inferior->ctl_fd = -1;
+ nto_inferior->pid = -1;
+}
+
+static void
+do_detach (void)
+{
+ if (nto_inferior.ctl_fd != -1)
+ {
+ nto_trace ("Closing fd\n");
+ close (nto_inferior.ctl_fd);
+ init_nto_inferior (&nto_inferior);
+ }
+}
+
+/* Set current thread. Return 1 on success, 0 otherwise. */
+
+static int
+nto_set_thread (ptid_t ptid)
+{
+ int res = 0;
+
+ TRACE ("%s pid: %d tid: %ld\n", __func__, ptid.pid (),
+ ptid.lwp ());
+ if (nto_inferior.ctl_fd != -1
+ && ptid != null_ptid
+ && ptid != minus_one_ptid)
+ {
+ pthread_t tid = ptid.lwp ();
+
+ if (EOK == devctl (nto_inferior.ctl_fd, DCMD_PROC_CURTHREAD, &tid,
+ sizeof (tid), 0))
+ res = 1;
+ else
+ TRACE ("%s: Error: failed to set current thread\n", __func__);
+ }
+ return res;
+}
+
+/* This function will determine all alive threads. Note that we do not list
+ dead but unjoined threads even though they are still in the process' thread
+ list.
+
+ NTO_INFERIOR must not be NULL. */
+
+static void
+nto_find_new_threads (struct nto_inferior *nto_inferior)
+{
+ pthread_t tid;
+
+ TRACE ("%s pid:%d\n", __func__, nto_inferior->pid);
+
+ if (nto_inferior->ctl_fd == -1)
+ return;
+
+ for (tid = 1;; ++tid)
+ {
+ procfs_status status;
+ ptid_t ptid;
+ int err;
+
+ status.tid = tid;
+ err = devctl (nto_inferior->ctl_fd, DCMD_PROC_TIDSTATUS, &status,
+ sizeof (status), 0);
+
+ if (err != EOK || status.tid == 0)
+ break;
+
+ /* All threads in between are gone. */
+ while (tid != status.tid || status.state == STATE_DEAD)
+ {
+ struct thread_info *ti;
+
+ ptid = ptid_t (nto_inferior->pid, tid, 0);
+ ti = find_thread_ptid (ptid);
+ if (ti != NULL)
+ {
+ TRACE ("Removing thread %d\n", tid);
+ remove_thread (ti);
+ }
+ if (tid == status.tid)
+ break;
+ ++tid;
+ }
+
+ if (status.state != STATE_DEAD)
+ {
+ TRACE ("Adding thread %d\n", tid);
+ ptid = ptid_t (nto_inferior->pid, tid, 0);
+ if (!find_thread_ptid (ptid))
+ add_thread (ptid, NULL);
+ }
+ }
+}
+
+/* Given pid, open procfs path. */
+
+static pid_t
+do_attach (pid_t pid)
+{
+ procfs_status status;
+ struct sigevent event;
+
+ if (nto_inferior.ctl_fd != -1)
+ {
+ close (nto_inferior.ctl_fd);
+ init_nto_inferior (&nto_inferior);
+ }
+ xsnprintf (nto_inferior.nto_procfs_path, PATH_MAX - 1, "/proc/%d/as", pid);
+ nto_inferior.ctl_fd = open (nto_inferior.nto_procfs_path, O_RDWR);
+ if (nto_inferior.ctl_fd == -1)
+ {
+ TRACE ("Failed to open %s\n", nto_inferior.nto_procfs_path);
+ init_nto_inferior (&nto_inferior);
+ return -1;
+ }
+ if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, &status, sizeof (status), 0)
+ != EOK)
+ {
+ do_detach ();
+ return -1;
+ }
+ nto_inferior.pid = pid;
+ /* Define a sigevent for process stopped notification. */
+ event.sigev_notify = SIGEV_SIGNAL_THREAD;
+ event.sigev_signo = SIGUSR1;
+ event.sigev_code = 0;
+ event.sigev_value.sival_ptr = NULL;
+ event.sigev_priority = -1;
+ devctl (nto_inferior.ctl_fd, DCMD_PROC_EVENT, &event, sizeof (event), 0);
+
+ if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
+ 0) == EOK
+ && (status.flags & _DEBUG_FLAG_STOPPED))
+ {
+ ptid_t ptid;
+ struct process_info *proc;
+
+ kill (pid, SIGCONT);
+ ptid = ptid_t (status.pid, status.tid, 0);
+ the_low_target.arch_setup ();
+ proc = add_process (status.pid, 1);
+ proc->tdesc = nto_tdesc;
+ TRACE ("Adding thread: pid=%d tid=%ld\n", status.pid,
+ ptid.lwp ());
+ nto_find_new_threads (&nto_inferior);
+ }
+ else
+ {
+ do_detach ();
+ return -1;
+ }
+
+ return pid;
+}
+
+/* Read or write LEN bytes from/to inferior's MEMADDR memory address
+ into gdbservers's MYADDR buffer. Return number of bytes actually
+ transfered. */
+
+static int
+nto_xfer_memory (off_t memaddr, unsigned char *myaddr, int len,
+ int dowrite)
+{
+ int nbytes = 0;
+
+ if (lseek (nto_inferior.ctl_fd, memaddr, SEEK_SET) == memaddr)
+ {
+ if (dowrite)
+ nbytes = write (nto_inferior.ctl_fd, myaddr, len);
+ else
+ nbytes = read (nto_inferior.ctl_fd, myaddr, len);
+ if (nbytes < 0)
+ nbytes = 0;
+ }
+ if (nbytes == 0)
+ {
+ int e = errno;
+ TRACE ("Error in %s : errno=%d (%s)\n", __func__, e, safe_strerror (e));
+ }
+ return nbytes;
+}
+
+/* Insert or remove breakpoint or watchpoint at address ADDR.
+ TYPE can be one of Neutrino breakpoint types. SIZE must be 0 for
+ inserting the point, -1 for removing it.
+
+ Return 0 on success, 1 otherwise. */
+
+static int
+nto_breakpoint (CORE_ADDR addr, int type, int size)
+{
+ procfs_break brk;
+
+ brk.type = type;
+ brk.addr = addr;
+ brk.size = size;
+ if (devctl (nto_inferior.ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0)
+ != EOK)
+ return 1;
+ return 0;
+}
+
+/* Read auxiliary vector from inferior's initial stack into gdbserver's
+ MYADDR buffer, up to LEN bytes.
+
+ Return number of bytes read. */
+
+static int
+nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack,
+ unsigned char *myaddr,
+ unsigned int len)
+{
+ int data_ofs = 0;
+ int anint;
+ unsigned int len_read = 0;
+
+ /* Skip over argc, argv and envp... Comment from ldd.c:
+
+ The startup frame is set-up so that we have:
+ auxv
+ NULL
+ ...
+ envp2
+ envp1 <----- void *frame + (argc + 2) * sizeof(char *)
+ NULL
+ ...
+ argv2
+ argv1
+ argc <------ void * frame
+
+ On entry to ldd, frame gives the address of argc on the stack. */
+ if (nto_xfer_memory (initial_stack, (unsigned char *)&anint,
+ sizeof (anint), 0) != sizeof (anint))
+ return 0;
+
+ /* Size of pointer is assumed to be 4 bytes (32 bit arch. ) */
+ data_ofs += (anint + 2) * sizeof (void *); /* + 2 comes from argc itself and
+ NULL terminating pointer in
+ argv. */
+
+ /* Now loop over env table: */
+ while (nto_xfer_memory (initial_stack + data_ofs,
+ (unsigned char *)&anint, sizeof (anint), 0)
+ == sizeof (anint))
+ {
+ data_ofs += sizeof (anint);
+ if (anint == 0)
+ break;
+ }
+ initial_stack += data_ofs;
+
+ memset (myaddr, 0, len);
+ while (len_read <= len - sizeof (auxv_t))
+ {
+ auxv_t *auxv = (auxv_t *)myaddr;
+
+ /* Search backwards until we have read AT_PHDR (num. 3),
+ AT_PHENT (num 4), AT_PHNUM (num 5) */
+ if (nto_xfer_memory (initial_stack, (unsigned char *)auxv,
+ sizeof (auxv_t), 0) == sizeof (auxv_t))
+ {
+ if (auxv->a_type != AT_NULL)
+ {
+ auxv++;
+ len_read += sizeof (auxv_t);
+ }
+ if (auxv->a_type == AT_PHNUM) /* That's all we need. */
+ break;
+ initial_stack += sizeof (auxv_t);
+ }
+ else
+ break;
+ }
+ TRACE ("auxv: len_read: %d\n", len_read);
+ return len_read;
+}
+
+/* Start inferior specified by PROGRAM, using PROGRAM_ARGS as its
+ arguments. */
+
+static int
+nto_create_inferior (const char *program,
+ const std::vector<char *> &program_args)
+{
+ struct inheritance inherit;
+ pid_t pid;
+ sigset_t set;
+ std::string str_program_args = stringify_argv (program_args);
+
+ TRACE ("%s %s\n", __func__, program);
+ /* Clear any pending SIGUSR1's but keep the behavior the same. */
+ signal (SIGUSR1, signal (SIGUSR1, SIG_IGN));
+
+ sigemptyset (&set);
+ sigaddset (&set, SIGUSR1);
+ sigprocmask (SIG_UNBLOCK, &set, NULL);
+
+ memset (&inherit, 0, sizeof (inherit));
+ inherit.flags |= SPAWN_SETGROUP | SPAWN_HOLD;
+ inherit.pgroup = SPAWN_NEWPGROUP;
+ pid = spawnp (program, 0, NULL, &inherit,
+ (char *) str_program_args.c_str (), 0);
+ sigprocmask (SIG_BLOCK, &set, NULL);
+
+ if (pid == -1)
+ return -1;
+
+ if (do_attach (pid) != pid)
+ return -1;
+
+ return pid;
+}
+
+/* Attach to process PID. */
+
+static int
+nto_attach (unsigned long pid)
+{
+ TRACE ("%s %ld\n", __func__, pid);
+ if (do_attach (pid) != pid)
+ error ("Unable to attach to %ld\n", pid);
+ return 0;
+}
+
+/* Send signal to process PID. */
+
+static int
+nto_kill (process_info *proc)
+{
+ int pid = proc->pid;
+
+ TRACE ("%s %d\n", __func__, pid);
+ kill (pid, SIGKILL);
+ do_detach ();
+ return 0;
+}
+
+/* Detach from process PID. */
+
+static int
+nto_detach (process_info *proc)
+{
+ TRACE ("%s %d\n", __func__, proc->pid);
+ do_detach ();
+ return 0;
+}
+
+static void
+nto_mourn (struct process_info *process)
+{
+ remove_process (process);
+}
+
+/* Check if the given thread is alive.
+
+ Return 1 if alive, 0 otherwise. */
+
+static int
+nto_thread_alive (ptid_t ptid)
+{
+ int res;
+
+ TRACE ("%s pid:%d tid:%d\n", __func__, ptid.pid (),
+ ptid.lwp ());
+ if (SignalKill (0, ptid.pid (), ptid.lwp (),
+ 0, 0, 0) == -1)
+ res = 0;
+ else
+ res = 1;
+ TRACE ("%s: %s\n", __func__, res ? "yes" : "no");
+ return res;
+}
+
+/* Resume inferior's execution. */
+
+static void
+nto_resume (struct thread_resume *resume_info, size_t n)
+{
+ /* We can only work in all-stop mode. */
+ procfs_status status;
+ procfs_run run;
+ int err;
+
+ TRACE ("%s\n", __func__);
+ /* Workaround for aliasing rules violation. */
+ sigset_t *run_fault = (sigset_t *) (void *) &run.fault;
+
+ nto_set_thread (resume_info->thread);
+
+ run.flags = _DEBUG_RUN_FAULT | _DEBUG_RUN_TRACE;
+ if (resume_info->kind == resume_step)
+ run.flags |= _DEBUG_RUN_STEP;
+ run.flags |= _DEBUG_RUN_ARM;
+
+ sigemptyset (run_fault);
+ sigaddset (run_fault, FLTBPT);
+ sigaddset (run_fault, FLTTRACE);
+ sigaddset (run_fault, FLTILL);
+ sigaddset (run_fault, FLTPRIV);
+ sigaddset (run_fault, FLTBOUNDS);
+ sigaddset (run_fault, FLTIOVF);
+ sigaddset (run_fault, FLTIZDIV);
+ sigaddset (run_fault, FLTFPE);
+ sigaddset (run_fault, FLTPAGE);
+ sigaddset (run_fault, FLTSTACK);
+ sigaddset (run_fault, FLTACCESS);
+
+ sigemptyset (&run.trace);
+ if (resume_info->sig)
+ {
+ int signal_to_pass;
+
+ devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
+ 0);
+ signal_to_pass = resume_info->sig;
+ if (status.why & (_DEBUG_WHY_SIGNALLED | _DEBUG_WHY_FAULTED))
+ {
+ if (signal_to_pass != status.info.si_signo)
+ {
+ kill (status.pid, signal_to_pass);
+ run.flags |= _DEBUG_RUN_CLRFLT | _DEBUG_RUN_CLRSIG;
+ }
+ else /* Let it kill the program without telling us. */
+ sigdelset (&run.trace, signal_to_pass);
+ }
+ }
+ else
+ run.flags |= _DEBUG_RUN_CLRSIG | _DEBUG_RUN_CLRFLT;
+
+ sigfillset (&run.trace);
+
+ regcache_invalidate ();
+
+ err = devctl (nto_inferior.ctl_fd, DCMD_PROC_RUN, &run, sizeof (run), 0);
+ if (err != EOK)
+ TRACE ("Error: %d \"%s\"\n", err, safe_strerror (err));
+}
+
+/* Wait for inferior's event.
+
+ Return ptid of thread that caused the event. */
+
+static ptid_t
+nto_wait (ptid_t ptid,
+ struct target_waitstatus *ourstatus, int target_options)
+{
+ sigset_t set;
+ siginfo_t info;
+ procfs_status status;
+ const int trace_mask = (_DEBUG_FLAG_TRACE_EXEC | _DEBUG_FLAG_TRACE_RD
+ | _DEBUG_FLAG_TRACE_WR | _DEBUG_FLAG_TRACE_MODIFY);
+
+ TRACE ("%s\n", __func__);
+
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+
+ sigemptyset (&set);
+ sigaddset (&set, SIGUSR1);
+
+ devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0);
+ while (!(status.flags & _DEBUG_FLAG_ISTOP))
+ {
+ sigwaitinfo (&set, &info);
+ devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
+ 0);
+ }
+ nto_find_new_threads (&nto_inferior);
+
+ if (status.flags & _DEBUG_FLAG_SSTEP)
+ {
+ TRACE ("SSTEP\n");
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ }
+ /* Was it a breakpoint? */
+ else if (status.flags & trace_mask)
+ {
+ TRACE ("STOPPED\n");
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ }
+ else if (status.flags & _DEBUG_FLAG_ISTOP)
+ {
+ TRACE ("ISTOP\n");
+ switch (status.why)
+ {
+ case _DEBUG_WHY_SIGNALLED:
+ TRACE (" SIGNALLED\n");
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig =
+ gdb_signal_from_host (status.info.si_signo);
+ nto_inferior.exit_signo = ourstatus->value.sig;
+ break;
+ case _DEBUG_WHY_FAULTED:
+ TRACE (" FAULTED\n");
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ if (status.info.si_signo == SIGTRAP)
+ {
+ ourstatus->value.sig = 0;
+ nto_inferior.exit_signo = 0;
+ }
+ else
+ {
+ ourstatus->value.sig =
+ gdb_signal_from_host (status.info.si_signo);
+ nto_inferior.exit_signo = ourstatus->value.sig;
+ }
+ break;
+
+ case _DEBUG_WHY_TERMINATED:
+ {
+ int waitval = 0;
+
+ TRACE (" TERMINATED\n");
+ waitpid (ptid.pid (), &waitval, WNOHANG);
+ if (nto_inferior.exit_signo)
+ {
+ /* Abnormal death. */
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = nto_inferior.exit_signo;
+ }
+ else
+ {
+ /* Normal death. */
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = WEXITSTATUS (waitval);
+ }
+ nto_inferior.exit_signo = 0;
+ break;
+ }
+
+ case _DEBUG_WHY_REQUESTED:
+ TRACE ("REQUESTED\n");
+ /* We are assuming a requested stop is due to a SIGINT. */
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig = GDB_SIGNAL_INT;
+ nto_inferior.exit_signo = 0;
+ break;
+ }
+ }
+
+ return ptid_t (status.pid, status.tid, 0);
+}
+
+/* Fetch inferior's registers for currently selected thread (CURRENT_INFERIOR).
+ If REGNO is -1, fetch all registers, or REGNO register only otherwise. */
+
+static void
+nto_fetch_registers (struct regcache *regcache, int regno)
+{
+ int regsize;
+ procfs_greg greg;
+
+ TRACE ("%s (regno=%d)\n", __func__, regno);
+ if (regno >= the_low_target.num_regs)
+ return;
+
+ if (current_thread == NULL)
+ {
+ TRACE ("current_thread is NULL\n");
+ return;
+ }
+ ptid_t ptid = ptid_of (current_thread);
+ if (!nto_set_thread (ptid))
+ return;
+
+ if (devctl (nto_inferior.ctl_fd, DCMD_PROC_GETGREG, &greg, sizeof (greg),
+ ®size) == EOK)
+ {
+ if (regno == -1) /* All registers. */
+ {
+ for (regno = 0; regno != the_low_target.num_regs; ++regno)
+ {
+ const unsigned int registeroffset
+ = the_low_target.register_offset (regno);
+ supply_register (regcache, regno,
+ ((char *)&greg) + registeroffset);
+ }
+ }
+ else
+ {
+ const unsigned int registeroffset
+ = the_low_target.register_offset (regno);
+ if (registeroffset == -1)
+ return;
+ supply_register (regcache, regno, ((char *)&greg) + registeroffset);
+ }
+ }
+ else
+ TRACE ("ERROR reading registers from inferior.\n");
+}
+
+/* Store registers for currently selected thread (CURRENT_INFERIOR).
+ We always store all registers, regardless of REGNO. */
+
+static void
+nto_store_registers (struct regcache *regcache, int regno)
+{
+ procfs_greg greg;
+ int err;
+
+ TRACE ("%s (regno:%d)\n", __func__, regno);
+
+ if (current_thread == NULL)
+ {
+ TRACE ("current_thread is NULL\n");
+ return;
+ }
+ ptid_t ptid = ptid_of (current_thread);
+ if (!nto_set_thread (ptid))
+ return;
+
+ memset (&greg, 0, sizeof (greg));
+ for (regno = 0; regno != the_low_target.num_regs; ++regno)
+ {
+ const unsigned int regoffset
+ = the_low_target.register_offset (regno);
+ collect_register (regcache, regno, ((char *)&greg) + regoffset);
+ }
+ err = devctl (nto_inferior.ctl_fd, DCMD_PROC_SETGREG, &greg, sizeof (greg),
+ 0);
+ if (err != EOK)
+ TRACE ("Error: setting registers.\n");
+}
+
+/* Read LEN bytes from inferior's memory address MEMADDR into
+ gdbserver's MYADDR buffer.
+
+ Return 0 on success -1 otherwise. */
+
+static int
+nto_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ TRACE ("%s memaddr:0x%08lx, len:%d\n", __func__, memaddr, len);
+
+ if (nto_xfer_memory (memaddr, myaddr, len, 0) != len)
+ {
+ TRACE ("Failed to read memory\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Write LEN bytes from gdbserver's buffer MYADDR into inferior's
+ memory at address MEMADDR.
+
+ Return 0 on success -1 otherwise. */
+
+static int
+nto_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ int len_written;
+
+ TRACE ("%s memaddr: 0x%08llx len: %d\n", __func__, memaddr, len);
+ if ((len_written = nto_xfer_memory (memaddr, (unsigned char *)myaddr, len,
+ 1))
+ != len)
+ {
+ TRACE ("Wanted to write: %d but written: %d\n", len, len_written);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Stop inferior. We always stop all threads. */
+
+static void
+nto_request_interrupt (void)
+{
+ TRACE ("%s\n", __func__);
+ nto_set_thread (ptid_t (nto_inferior.pid, 1, 0));
+ if (EOK != devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, NULL, 0, 0))
+ TRACE ("Error stopping inferior.\n");
+}
+
+/* Read auxiliary vector from inferior's memory into gdbserver's buffer
+ MYADDR. We always read whole auxv.
+
+ Return number of bytes stored in MYADDR buffer, 0 if OFFSET > 0
+ or -1 on error. */
+
+static int
+nto_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
+{
+ int err;
+ CORE_ADDR initial_stack;
+ procfs_info procinfo;
+
+ TRACE ("%s\n", __func__);
+ if (offset > 0)
+ return 0;
+
+ err = devctl (nto_inferior.ctl_fd, DCMD_PROC_INFO, &procinfo,
+ sizeof procinfo, 0);
+ if (err != EOK)
+ return -1;
+
+ initial_stack = procinfo.initial_stack;
+
+ return nto_read_auxv_from_initial_stack (initial_stack, myaddr, len);
+}
+
+static int
+nto_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ case Z_PACKET_HW_BP:
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_READ_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Insert {break/watch}point at address ADDR. SIZE is not used. */
+
+static int
+nto_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ int wtype = _DEBUG_BREAK_HW; /* Always request HW. */
+
+ TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, size);
+ switch (type)
+ {
+ case raw_bkpt_type_sw:
+ wtype = _DEBUG_BREAK_EXEC;
+ break;
+ case raw_bkpt_type_hw:
+ wtype |= _DEBUG_BREAK_EXEC;
+ break;
+ case raw_bkpt_type_write_wp:
+ wtype |= _DEBUG_BREAK_RW;
+ break;
+ case raw_bkpt_type_read_wp:
+ wtype |= _DEBUG_BREAK_RD;
+ break;
+ case raw_bkpt_type_access_wp:
+ wtype |= _DEBUG_BREAK_RW;
+ break;
+ default:
+ return 1; /* Not supported. */
+ }
+ return nto_breakpoint (addr, wtype, 0);
+}
+
+/* Remove {break/watch}point at address ADDR. SIZE is not used. */
+
+static int
+nto_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ int wtype = _DEBUG_BREAK_HW; /* Always request HW. */
+
+ TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, size);
+ switch (type)
+ {
+ case raw_bkpt_type_sw:
+ wtype = _DEBUG_BREAK_EXEC;
+ break;
+ case raw_bkpt_type_hw:
+ wtype |= _DEBUG_BREAK_EXEC;
+ break;
+ case raw_bkpt_type_write_wp:
+ wtype |= _DEBUG_BREAK_RW;
+ break;
+ case raw_bkpt_type_read_wp:
+ wtype |= _DEBUG_BREAK_RD;
+ break;
+ case raw_bkpt_type_access_wp:
+ wtype |= _DEBUG_BREAK_RW;
+ break;
+ default:
+ return 1; /* Not supported. */
+ }
+ return nto_breakpoint (addr, wtype, -1);
+}
+
+/* Check if the reason of stop for current thread (CURRENT_INFERIOR) is
+ a watchpoint.
+
+ Return 1 if stopped by watchpoint, 0 otherwise. */
+
+static int
+nto_stopped_by_watchpoint (void)
+{
+ int ret = 0;
+
+ TRACE ("%s\n", __func__);
+ if (nto_inferior.ctl_fd != -1 && current_thread != NULL)
+ {
+ ptid_t ptid = ptid_of (current_thread);
+ if (nto_set_thread (ptid))
+ {
+ const int watchmask = _DEBUG_FLAG_TRACE_RD | _DEBUG_FLAG_TRACE_WR
+ | _DEBUG_FLAG_TRACE_MODIFY;
+ procfs_status status;
+ int err;
+
+ err = devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
+ sizeof (status), 0);
+ if (err == EOK && (status.flags & watchmask))
+ ret = 1;
+ }
+ }
+ TRACE ("%s: %s\n", __func__, ret ? "yes" : "no");
+ return ret;
+}
+
+/* Get instruction pointer for CURRENT_INFERIOR thread.
+
+ Return inferior's instruction pointer value, or 0 on error. */
+
+static CORE_ADDR
+nto_stopped_data_address (void)
+{
+ CORE_ADDR ret = (CORE_ADDR)0;
+
+ TRACE ("%s\n", __func__);
+ if (nto_inferior.ctl_fd != -1 && current_thread != NULL)
+ {
+ ptid_t ptid = ptid_of (current_thread);
+
+ if (nto_set_thread (ptid))
+ {
+ procfs_status status;
+
+ if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
+ sizeof (status), 0) == EOK)
+ ret = status.ip;
+ }
+ }
+ TRACE ("%s: 0x%08lx\n", __func__, ret);
+ return ret;
+}
+
+/* We do not currently support non-stop. */
+
+static int
+nto_supports_non_stop (void)
+{
+ TRACE ("%s\n", __func__);
+ return 0;
+}
+
+/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+nto_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = the_low_target.breakpoint_len;
+ return the_low_target.breakpoint;
+}
+
+
+static process_stratum_target nto_target_ops = {
+ nto_create_inferior,
+ NULL, /* post_create_inferior */
+ nto_attach,
+ nto_kill,
+ nto_detach,
+ nto_mourn,
+ NULL, /* nto_join */
+ nto_thread_alive,
+ nto_resume,
+ nto_wait,
+ nto_fetch_registers,
+ nto_store_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
+ nto_read_memory,
+ nto_write_memory,
+ NULL, /* nto_look_up_symbols */
+ nto_request_interrupt,
+ nto_read_auxv,
+ nto_supports_z_point_type,
+ nto_insert_point,
+ nto_remove_point,
+ NULL, /* stopped_by_sw_breakpoint */
+ NULL, /* supports_stopped_by_sw_breakpoint */
+ NULL, /* stopped_by_hw_breakpoint */
+ NULL, /* supports_stopped_by_hw_breakpoint */
+ target_can_do_hardware_single_step,
+ nto_stopped_by_watchpoint,
+ nto_stopped_data_address,
+ NULL, /* nto_read_offsets */
+ NULL, /* thread_db_set_tls_address */
+ hostio_last_error_from_errno,
+ NULL, /* nto_qxfer_osdata */
+ NULL, /* xfer_siginfo */
+ nto_supports_non_stop,
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
+ NULL, /* handle_new_gdb_connection */
+ NULL, /* handle_monitor_command */
+ NULL, /* core_of_thread */
+ NULL, /* read_loadmap */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* read_pc */
+ NULL, /* write_pc */
+ NULL, /* thread_stopped */
+ NULL, /* get_tib_address */
+ NULL, /* pause_all */
+ NULL, /* unpause_all */
+ NULL, /* stabilize_threads */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* supports_disable_randomization */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* qxfer_libraries_svr4 */
+ NULL, /* support_agent */
+ NULL, /* enable_btrace */
+ NULL, /* disable_btrace */
+ NULL, /* read_btrace */
+ NULL, /* read_btrace_conf */
+ NULL, /* supports_range_stepping */
+ NULL, /* pid_to_exec_file */
+ NULL, /* multifs_open */
+ NULL, /* multifs_unlink */
+ NULL, /* multifs_readlink */
+ NULL, /* breakpoint_kind_from_pc */
+ nto_sw_breakpoint_from_kind,
+};
+
+
+/* Global function called by server.c. Initializes QNX Neutrino
+ gdbserver. */
+
+void
+initialize_low (void)
+{
+ sigset_t set;
+
+ TRACE ("%s\n", __func__);
+ set_target_ops (&nto_target_ops);
+
+ /* We use SIGUSR1 to gain control after we block waiting for a process.
+ We use sigwaitevent to wait. */
+ sigemptyset (&set);
+ sigaddset (&set, SIGUSR1);
+ sigprocmask (SIG_BLOCK, &set, NULL);
+}
+
+++ /dev/null
-/* QNX Neutrino specific low level interface, for the remote server
- for GDB.
- Copyright (C) 2009-2020 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 "nto-low.h"
-#include "regdef.h"
-#include "regcache.h"
-
-#include <x86/context.h>
-#include "gdbsupport/x86-xstate.h"
-#include "arch/i386.h"
-#include "x86-tdesc.h"
-
-const unsigned char x86_breakpoint[] = { 0xCC };
-#define x86_breakpoint_len 1
-
-/* Returns offset in appropriate Neutrino's context structure.
- Defined in x86/context.h.
- GDBREGNO is index into regs_i386 array. It is autogenerated and
- hopefully doesn't change. */
-static int
-nto_x86_register_offset (int gdbregno)
-{
- if (gdbregno >= 0 && gdbregno < 16)
- {
- X86_CPU_REGISTERS *dummy = (void*)0;
- /* GPRs */
- switch (gdbregno)
- {
- case 0:
- return (int)&(dummy->eax);
- case 1:
- return (int)&(dummy->ecx);
- case 2:
- return (int)&(dummy->edx);
- case 3:
- return (int)&(dummy->ebx);
- case 4:
- return (int)&(dummy->esp);
- case 5:
- return (int)&(dummy->ebp);
- case 6:
- return (int)&(dummy->esi);
- case 7:
- return (int)&(dummy->edi);
- case 8:
- return (int)&(dummy->eip);
- case 9:
- return (int)&(dummy->efl);
- case 10:
- return (int)&(dummy->cs);
- case 11:
- return (int)&(dummy->ss);
-#ifdef __SEGMENTS__
- case 12:
- return (int)&(dummy->ds);
- case 13:
- return (int)&(dummy->es);
- case 14:
- return (int)&(dummy->fs);
- case 15:
- return (int)&(dummy->gs);
-#endif
- default:
- return -1;
- }
- }
- return -1;
-}
-
-static void
-nto_x86_arch_setup (void)
-{
- the_low_target.num_regs = 16;
- struct target_desc *tdesc
- = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
-
- init_target_desc (tdesc, i386_expedite_regs);
-
- nto_tdesc = tdesc;
-}
-
-struct nto_target_ops the_low_target =
-{
- nto_x86_arch_setup,
- 0, /* num_regs */
- nto_x86_register_offset,
- x86_breakpoint,
- x86_breakpoint_len
-};
-
-
-
--- /dev/null
+/* QNX Neutrino specific low level interface, for the remote server
+ for GDB.
+ Copyright (C) 2009-2020 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 "nto-low.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include <x86/context.h>
+#include "gdbsupport/x86-xstate.h"
+#include "arch/i386.h"
+#include "x86-tdesc.h"
+
+const unsigned char x86_breakpoint[] = { 0xCC };
+#define x86_breakpoint_len 1
+
+/* Returns offset in appropriate Neutrino's context structure.
+ Defined in x86/context.h.
+ GDBREGNO is index into regs_i386 array. It is autogenerated and
+ hopefully doesn't change. */
+static int
+nto_x86_register_offset (int gdbregno)
+{
+ if (gdbregno >= 0 && gdbregno < 16)
+ {
+ X86_CPU_REGISTERS *dummy = (void*)0;
+ /* GPRs */
+ switch (gdbregno)
+ {
+ case 0:
+ return (int)&(dummy->eax);
+ case 1:
+ return (int)&(dummy->ecx);
+ case 2:
+ return (int)&(dummy->edx);
+ case 3:
+ return (int)&(dummy->ebx);
+ case 4:
+ return (int)&(dummy->esp);
+ case 5:
+ return (int)&(dummy->ebp);
+ case 6:
+ return (int)&(dummy->esi);
+ case 7:
+ return (int)&(dummy->edi);
+ case 8:
+ return (int)&(dummy->eip);
+ case 9:
+ return (int)&(dummy->efl);
+ case 10:
+ return (int)&(dummy->cs);
+ case 11:
+ return (int)&(dummy->ss);
+#ifdef __SEGMENTS__
+ case 12:
+ return (int)&(dummy->ds);
+ case 13:
+ return (int)&(dummy->es);
+ case 14:
+ return (int)&(dummy->fs);
+ case 15:
+ return (int)&(dummy->gs);
+#endif
+ default:
+ return -1;
+ }
+ }
+ return -1;
+}
+
+static void
+nto_x86_arch_setup (void)
+{
+ the_low_target.num_regs = 16;
+ struct target_desc *tdesc
+ = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
+
+ init_target_desc (tdesc, i386_expedite_regs);
+
+ nto_tdesc = tdesc;
+}
+
+struct nto_target_ops the_low_target =
+{
+ nto_x86_arch_setup,
+ 0, /* num_regs */
+ nto_x86_register_offset,
+ x86_breakpoint,
+ x86_breakpoint_len
+};
+
+
+
+++ /dev/null
-/* libthread_db helper functions for the remote server for GDB.
- Copyright (C) 2002-2020 Free Software Foundation, Inc.
-
- Contributed by MontaVista Software.
-
- 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"
-
-/* This file is currently tied to GNU/Linux. It should scale well to
- another libthread_db implementation, with the appropriate gdbserver
- hooks, but for now this means we can use GNU/Linux's target data. */
-
-#include "linux-low.h"
-
-#include "gdb_proc_service.h"
-
-typedef struct ps_prochandle *gdb_ps_prochandle_t;
-typedef void *gdb_ps_read_buf_t;
-typedef const void *gdb_ps_write_buf_t;
-typedef size_t gdb_ps_size_t;
-
-#ifdef HAVE_LINUX_REGSETS
-#define HAVE_REGSETS
-#endif
-
-#ifdef HAVE_REGSETS
-static struct regset_info *
-gregset_info (void)
-{
- int i = 0;
- const struct regs_info *regs_info = (*the_low_target.regs_info) ();
- struct regsets_info *regsets_info = regs_info->regsets_info;
-
- while (regsets_info->regsets[i].size != -1)
- {
- if (regsets_info->regsets[i].type == GENERAL_REGS)
- break;
- i++;
- }
-
- return ®sets_info->regsets[i];
-}
-#endif
-
-/* Search for the symbol named NAME within the object named OBJ within
- the target process PH. If the symbol is found the address of the
- symbol is stored in SYM_ADDR. */
-
-ps_err_e
-ps_pglobal_lookup (gdb_ps_prochandle_t ph, const char *obj,
- const char *name, psaddr_t *sym_addr)
-{
- CORE_ADDR addr;
-
- if (thread_db_look_up_one_symbol (name, &addr) == 0)
- return PS_NOSYM;
-
- *sym_addr = (psaddr_t) (unsigned long) addr;
- return PS_OK;
-}
-
-/* Read SIZE bytes from the target process PH at address ADDR and copy
- them into BUF. */
-
-ps_err_e
-ps_pdread (gdb_ps_prochandle_t ph, psaddr_t addr,
- gdb_ps_read_buf_t buf, gdb_ps_size_t size)
-{
- if (read_inferior_memory ((uintptr_t) addr, (gdb_byte *) buf, size) != 0)
- return PS_ERR;
- return PS_OK;
-}
-
-/* Write SIZE bytes from BUF into the target process PH at address ADDR. */
-
-ps_err_e
-ps_pdwrite (gdb_ps_prochandle_t ph, psaddr_t addr,
- gdb_ps_write_buf_t buf, gdb_ps_size_t size)
-{
- if (target_write_memory ((uintptr_t) addr, (const gdb_byte *) buf, size)
- != 0)
- return PS_ERR;
- return PS_OK;
-}
-
-/* Get the general registers of LWP LWPID within the target process PH
- and store them in GREGSET. */
-
-ps_err_e
-ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset)
-{
-#ifdef HAVE_REGSETS
- struct lwp_info *lwp;
- struct thread_info *reg_thread, *saved_thread;
- struct regcache *regcache;
-
- lwp = find_lwp_pid (ptid_t (lwpid));
- if (lwp == NULL)
- return PS_ERR;
-
- reg_thread = get_lwp_thread (lwp);
- saved_thread = current_thread;
- current_thread = reg_thread;
- regcache = get_thread_regcache (current_thread, 1);
- gregset_info ()->fill_function (regcache, gregset);
-
- current_thread = saved_thread;
- return PS_OK;
-#else
- return PS_ERR;
-#endif
-}
-
-/* Set the general registers of LWP LWPID within the target process PH
- from GREGSET. */
-
-ps_err_e
-ps_lsetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prgregset_t gregset)
-{
- /* Unneeded. */
- return PS_ERR;
-}
-
-/* Get the floating-point registers of LWP LWPID within the target
- process PH and store them in FPREGSET. */
-
-ps_err_e
-ps_lgetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prfpregset_t *fpregset)
-{
- /* Unneeded. */
- return PS_ERR;
-}
-
-/* Set the floating-point registers of LWP LWPID within the target
- process PH from FPREGSET. */
-
-ps_err_e
-ps_lsetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prfpregset_t *fpregset)
-{
- /* Unneeded. */
- return PS_ERR;
-}
-
-/* Return overall process id of the target PH. Special for GNU/Linux
- -- not used on Solaris. */
-
-pid_t
-ps_getpid (gdb_ps_prochandle_t ph)
-{
- return pid_of (current_thread);
-}
--- /dev/null
+/* libthread_db helper functions for the remote server for GDB.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ 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"
+
+/* This file is currently tied to GNU/Linux. It should scale well to
+ another libthread_db implementation, with the appropriate gdbserver
+ hooks, but for now this means we can use GNU/Linux's target data. */
+
+#include "linux-low.h"
+
+#include "gdb_proc_service.h"
+
+typedef struct ps_prochandle *gdb_ps_prochandle_t;
+typedef void *gdb_ps_read_buf_t;
+typedef const void *gdb_ps_write_buf_t;
+typedef size_t gdb_ps_size_t;
+
+#ifdef HAVE_LINUX_REGSETS
+#define HAVE_REGSETS
+#endif
+
+#ifdef HAVE_REGSETS
+static struct regset_info *
+gregset_info (void)
+{
+ int i = 0;
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();
+ struct regsets_info *regsets_info = regs_info->regsets_info;
+
+ while (regsets_info->regsets[i].size != -1)
+ {
+ if (regsets_info->regsets[i].type == GENERAL_REGS)
+ break;
+ i++;
+ }
+
+ return ®sets_info->regsets[i];
+}
+#endif
+
+/* Search for the symbol named NAME within the object named OBJ within
+ the target process PH. If the symbol is found the address of the
+ symbol is stored in SYM_ADDR. */
+
+ps_err_e
+ps_pglobal_lookup (gdb_ps_prochandle_t ph, const char *obj,
+ const char *name, psaddr_t *sym_addr)
+{
+ CORE_ADDR addr;
+
+ if (thread_db_look_up_one_symbol (name, &addr) == 0)
+ return PS_NOSYM;
+
+ *sym_addr = (psaddr_t) (unsigned long) addr;
+ return PS_OK;
+}
+
+/* Read SIZE bytes from the target process PH at address ADDR and copy
+ them into BUF. */
+
+ps_err_e
+ps_pdread (gdb_ps_prochandle_t ph, psaddr_t addr,
+ gdb_ps_read_buf_t buf, gdb_ps_size_t size)
+{
+ if (read_inferior_memory ((uintptr_t) addr, (gdb_byte *) buf, size) != 0)
+ return PS_ERR;
+ return PS_OK;
+}
+
+/* Write SIZE bytes from BUF into the target process PH at address ADDR. */
+
+ps_err_e
+ps_pdwrite (gdb_ps_prochandle_t ph, psaddr_t addr,
+ gdb_ps_write_buf_t buf, gdb_ps_size_t size)
+{
+ if (target_write_memory ((uintptr_t) addr, (const gdb_byte *) buf, size)
+ != 0)
+ return PS_ERR;
+ return PS_OK;
+}
+
+/* Get the general registers of LWP LWPID within the target process PH
+ and store them in GREGSET. */
+
+ps_err_e
+ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset)
+{
+#ifdef HAVE_REGSETS
+ struct lwp_info *lwp;
+ struct thread_info *reg_thread, *saved_thread;
+ struct regcache *regcache;
+
+ lwp = find_lwp_pid (ptid_t (lwpid));
+ if (lwp == NULL)
+ return PS_ERR;
+
+ reg_thread = get_lwp_thread (lwp);
+ saved_thread = current_thread;
+ current_thread = reg_thread;
+ regcache = get_thread_regcache (current_thread, 1);
+ gregset_info ()->fill_function (regcache, gregset);
+
+ current_thread = saved_thread;
+ return PS_OK;
+#else
+ return PS_ERR;
+#endif
+}
+
+/* Set the general registers of LWP LWPID within the target process PH
+ from GREGSET. */
+
+ps_err_e
+ps_lsetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prgregset_t gregset)
+{
+ /* Unneeded. */
+ return PS_ERR;
+}
+
+/* Get the floating-point registers of LWP LWPID within the target
+ process PH and store them in FPREGSET. */
+
+ps_err_e
+ps_lgetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prfpregset_t *fpregset)
+{
+ /* Unneeded. */
+ return PS_ERR;
+}
+
+/* Set the floating-point registers of LWP LWPID within the target
+ process PH from FPREGSET. */
+
+ps_err_e
+ps_lsetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prfpregset_t *fpregset)
+{
+ /* Unneeded. */
+ return PS_ERR;
+}
+
+/* Return overall process id of the target PH. Special for GNU/Linux
+ -- not used on Solaris. */
+
+pid_t
+ps_getpid (gdb_ps_prochandle_t ph)
+{
+ return pid_of (current_thread);
+}
+++ /dev/null
-/* Register support routines for the remote server for GDB.
- Copyright (C) 2001-2020 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 "regdef.h"
-#include "gdbthread.h"
-#include "tdesc.h"
-#include "gdbsupport/rsp-low.h"
-#ifndef IN_PROCESS_AGENT
-
-struct regcache *
-get_thread_regcache (struct thread_info *thread, int fetch)
-{
- struct regcache *regcache;
-
- regcache = thread_regcache_data (thread);
-
- /* Threads' regcaches are created lazily, because biarch targets add
- the main thread/lwp before seeing it stop for the first time, and
- it is only after the target sees the thread stop for the first
- time that the target has a chance of determining the process's
- architecture. IOW, when we first add the process's main thread
- we don't know which architecture/tdesc its regcache should
- have. */
- if (regcache == NULL)
- {
- struct process_info *proc = get_thread_process (thread);
-
- gdb_assert (proc->tdesc != NULL);
-
- regcache = new_register_cache (proc->tdesc);
- set_thread_regcache_data (thread, regcache);
- }
-
- if (fetch && regcache->registers_valid == 0)
- {
- struct thread_info *saved_thread = current_thread;
-
- current_thread = thread;
- /* Invalidate all registers, to prevent stale left-overs. */
- memset (regcache->register_status, REG_UNAVAILABLE,
- regcache->tdesc->reg_defs.size ());
- fetch_inferior_registers (regcache, -1);
- current_thread = saved_thread;
- regcache->registers_valid = 1;
- }
-
- return regcache;
-}
-
-/* See gdbsupport/common-regcache.h. */
-
-struct regcache *
-get_thread_regcache_for_ptid (ptid_t ptid)
-{
- return get_thread_regcache (find_thread_ptid (ptid), 1);
-}
-
-void
-regcache_invalidate_thread (struct thread_info *thread)
-{
- struct regcache *regcache;
-
- regcache = thread_regcache_data (thread);
-
- if (regcache == NULL)
- return;
-
- if (regcache->registers_valid)
- {
- struct thread_info *saved_thread = current_thread;
-
- current_thread = thread;
- store_inferior_registers (regcache, -1);
- current_thread = saved_thread;
- }
-
- regcache->registers_valid = 0;
-}
-
-/* See regcache.h. */
-
-void
-regcache_invalidate_pid (int pid)
-{
- /* Only invalidate the regcaches of threads of this process. */
- for_each_thread (pid, regcache_invalidate_thread);
-}
-
-/* See regcache.h. */
-
-void
-regcache_invalidate (void)
-{
- /* Only update the threads of the current process. */
- int pid = current_thread->id.pid ();
-
- regcache_invalidate_pid (pid);
-}
-
-#endif
-
-struct regcache *
-init_register_cache (struct regcache *regcache,
- const struct target_desc *tdesc,
- unsigned char *regbuf)
-{
- if (regbuf == NULL)
- {
-#ifndef IN_PROCESS_AGENT
- /* Make sure to zero-initialize the register cache when it is
- created, in case there are registers the target never
- fetches. This way they'll read as zero instead of
- garbage. */
- regcache->tdesc = tdesc;
- regcache->registers
- = (unsigned char *) xcalloc (1, tdesc->registers_size);
- regcache->registers_owned = 1;
- regcache->register_status
- = (unsigned char *) xmalloc (tdesc->reg_defs.size ());
- memset ((void *) regcache->register_status, REG_UNAVAILABLE,
- tdesc->reg_defs.size ());
-#else
- gdb_assert_not_reached ("can't allocate memory from the heap");
-#endif
- }
- else
- {
- regcache->tdesc = tdesc;
- regcache->registers = regbuf;
- regcache->registers_owned = 0;
-#ifndef IN_PROCESS_AGENT
- regcache->register_status = NULL;
-#endif
- }
-
- regcache->registers_valid = 0;
-
- return regcache;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-struct regcache *
-new_register_cache (const struct target_desc *tdesc)
-{
- struct regcache *regcache = new struct regcache;
-
- gdb_assert (tdesc->registers_size != 0);
-
- return init_register_cache (regcache, tdesc, NULL);
-}
-
-void
-free_register_cache (struct regcache *regcache)
-{
- if (regcache)
- {
- if (regcache->registers_owned)
- free (regcache->registers);
- free (regcache->register_status);
- delete regcache;
- }
-}
-
-#endif
-
-void
-regcache_cpy (struct regcache *dst, struct regcache *src)
-{
- gdb_assert (src != NULL && dst != NULL);
- gdb_assert (src->tdesc == dst->tdesc);
- gdb_assert (src != dst);
-
- memcpy (dst->registers, src->registers, src->tdesc->registers_size);
-#ifndef IN_PROCESS_AGENT
- if (dst->register_status != NULL && src->register_status != NULL)
- memcpy (dst->register_status, src->register_status,
- src->tdesc->reg_defs.size ());
-#endif
- dst->registers_valid = src->registers_valid;
-}
-
-/* Return a reference to the description of register N. */
-
-static const struct reg &
-find_register_by_number (const struct target_desc *tdesc, int n)
-{
- return tdesc->reg_defs[n];
-}
-
-#ifndef IN_PROCESS_AGENT
-
-void
-registers_to_string (struct regcache *regcache, char *buf)
-{
- unsigned char *registers = regcache->registers;
- const struct target_desc *tdesc = regcache->tdesc;
-
- for (int i = 0; i < tdesc->reg_defs.size (); ++i)
- {
- if (regcache->register_status[i] == REG_VALID)
- {
- bin2hex (registers, buf, register_size (tdesc, i));
- buf += register_size (tdesc, i) * 2;
- }
- else
- {
- memset (buf, 'x', register_size (tdesc, i) * 2);
- buf += register_size (tdesc, i) * 2;
- }
- registers += register_size (tdesc, i);
- }
- *buf = '\0';
-}
-
-void
-registers_from_string (struct regcache *regcache, char *buf)
-{
- int len = strlen (buf);
- unsigned char *registers = regcache->registers;
- const struct target_desc *tdesc = regcache->tdesc;
-
- if (len != tdesc->registers_size * 2)
- {
- warning ("Wrong sized register packet (expected %d bytes, got %d)",
- 2 * tdesc->registers_size, len);
- if (len > tdesc->registers_size * 2)
- len = tdesc->registers_size * 2;
- }
- hex2bin (buf, registers, len / 2);
-}
-
-int
-find_regno (const struct target_desc *tdesc, const char *name)
-{
- for (int i = 0; i < tdesc->reg_defs.size (); ++i)
- {
- if (strcmp (name, find_register_by_number (tdesc, i).name) == 0)
- return i;
- }
- internal_error (__FILE__, __LINE__, "Unknown register %s requested",
- name);
-}
-
-static void
-free_register_cache_thread (struct thread_info *thread)
-{
- struct regcache *regcache = thread_regcache_data (thread);
-
- if (regcache != NULL)
- {
- regcache_invalidate_thread (thread);
- free_register_cache (regcache);
- set_thread_regcache_data (thread, NULL);
- }
-}
-
-void
-regcache_release (void)
-{
- /* Flush and release all pre-existing register caches. */
- for_each_thread (free_register_cache_thread);
-}
-#endif
-
-int
-register_cache_size (const struct target_desc *tdesc)
-{
- return tdesc->registers_size;
-}
-
-int
-register_size (const struct target_desc *tdesc, int n)
-{
- return find_register_by_number (tdesc, n).size / 8;
-}
-
-/* See gdbsupport/common-regcache.h. */
-
-int
-regcache_register_size (const struct regcache *regcache, int n)
-{
- return register_size (regcache->tdesc, n);
-}
-
-static unsigned char *
-register_data (const struct regcache *regcache, int n, int fetch)
-{
- return (regcache->registers
- + find_register_by_number (regcache->tdesc, n).offset / 8);
-}
-
-void
-supply_register (struct regcache *regcache, int n, const void *buf)
-{
- return regcache->raw_supply (n, buf);
-}
-
-/* See gdbsupport/common-regcache.h. */
-
-void
-regcache::raw_supply (int n, const void *buf)
-{
- if (buf)
- {
- memcpy (register_data (this, n, 0), buf, register_size (tdesc, n));
-#ifndef IN_PROCESS_AGENT
- if (register_status != NULL)
- register_status[n] = REG_VALID;
-#endif
- }
- else
- {
- memset (register_data (this, n, 0), 0, register_size (tdesc, n));
-#ifndef IN_PROCESS_AGENT
- if (register_status != NULL)
- register_status[n] = REG_UNAVAILABLE;
-#endif
- }
-}
-
-/* Supply register N with value zero to REGCACHE. */
-
-void
-supply_register_zeroed (struct regcache *regcache, int n)
-{
- memset (register_data (regcache, n, 0), 0,
- register_size (regcache->tdesc, n));
-#ifndef IN_PROCESS_AGENT
- if (regcache->register_status != NULL)
- regcache->register_status[n] = REG_VALID;
-#endif
-}
-
-#ifndef IN_PROCESS_AGENT
-
-/* Supply register called NAME with value zero to REGCACHE. */
-
-void
-supply_register_by_name_zeroed (struct regcache *regcache,
- const char *name)
-{
- supply_register_zeroed (regcache, find_regno (regcache->tdesc, name));
-}
-
-#endif
-
-/* Supply the whole register set whose contents are stored in BUF, to
- REGCACHE. If BUF is NULL, all the registers' values are recorded
- as unavailable. */
-
-void
-supply_regblock (struct regcache *regcache, const void *buf)
-{
- if (buf)
- {
- const struct target_desc *tdesc = regcache->tdesc;
-
- memcpy (regcache->registers, buf, tdesc->registers_size);
-#ifndef IN_PROCESS_AGENT
- {
- int i;
-
- for (i = 0; i < tdesc->reg_defs.size (); i++)
- regcache->register_status[i] = REG_VALID;
- }
-#endif
- }
- else
- {
- const struct target_desc *tdesc = regcache->tdesc;
-
- memset (regcache->registers, 0, tdesc->registers_size);
-#ifndef IN_PROCESS_AGENT
- {
- int i;
-
- for (i = 0; i < tdesc->reg_defs.size (); i++)
- regcache->register_status[i] = REG_UNAVAILABLE;
- }
-#endif
- }
-}
-
-#ifndef IN_PROCESS_AGENT
-
-void
-supply_register_by_name (struct regcache *regcache,
- const char *name, const void *buf)
-{
- supply_register (regcache, find_regno (regcache->tdesc, name), buf);
-}
-
-#endif
-
-void
-collect_register (struct regcache *regcache, int n, void *buf)
-{
- regcache->raw_collect (n, buf);
-}
-
-/* See gdbsupport/common-regcache.h. */
-
-void
-regcache::raw_collect (int n, void *buf) const
-{
- memcpy (buf, register_data (this, n, 1), register_size (tdesc, n));
-}
-
-enum register_status
-regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
- ULONGEST *val)
-{
- int size;
-
- gdb_assert (regcache != NULL);
- gdb_assert (regnum >= 0
- && regnum < regcache->tdesc->reg_defs.size ());
-
- size = register_size (regcache->tdesc, regnum);
-
- if (size > (int) sizeof (ULONGEST))
- error (_("That operation is not available on integers of more than"
- "%d bytes."),
- (int) sizeof (ULONGEST));
-
- *val = 0;
- collect_register (regcache, regnum, val);
-
- return REG_VALID;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-/* See regcache.h. */
-
-ULONGEST
-regcache_raw_get_unsigned_by_name (struct regcache *regcache,
- const char *name)
-{
- return regcache_raw_get_unsigned (regcache,
- find_regno (regcache->tdesc, name));
-}
-
-void
-collect_register_as_string (struct regcache *regcache, int n, char *buf)
-{
- bin2hex (register_data (regcache, n, 1), buf,
- register_size (regcache->tdesc, n));
-}
-
-void
-collect_register_by_name (struct regcache *regcache,
- const char *name, void *buf)
-{
- collect_register (regcache, find_regno (regcache->tdesc, name), buf);
-}
-
-/* Special handling for register PC. */
-
-CORE_ADDR
-regcache_read_pc (struct regcache *regcache)
-{
- CORE_ADDR pc_val;
-
- if (the_target->read_pc)
- pc_val = the_target->read_pc (regcache);
- else
- internal_error (__FILE__, __LINE__,
- "regcache_read_pc: Unable to find PC");
-
- return pc_val;
-}
-
-void
-regcache_write_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- if (the_target->write_pc)
- the_target->write_pc (regcache, pc);
- else
- internal_error (__FILE__, __LINE__,
- "regcache_write_pc: Unable to update PC");
-}
-
-#endif
-
-/* See gdbsupport/common-regcache.h. */
-
-enum register_status
-regcache::get_register_status (int regnum) const
-{
-#ifndef IN_PROCESS_AGENT
- gdb_assert (regnum >= 0 && regnum < tdesc->reg_defs.size ());
- return (enum register_status) (register_status[regnum]);
-#else
- return REG_VALID;
-#endif
-}
-
-/* See gdbsupport/common-regcache.h. */
-
-bool
-regcache::raw_compare (int regnum, const void *buf, int offset) const
-{
- gdb_assert (buf != NULL);
-
- const unsigned char *regbuf = register_data (this, regnum, 1);
- int size = register_size (tdesc, regnum);
- gdb_assert (size >= offset);
-
- return (memcmp (buf, regbuf + offset, size - offset) == 0);
-}
--- /dev/null
+/* Register support routines for the remote server for GDB.
+ Copyright (C) 2001-2020 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 "regdef.h"
+#include "gdbthread.h"
+#include "tdesc.h"
+#include "gdbsupport/rsp-low.h"
+#ifndef IN_PROCESS_AGENT
+
+struct regcache *
+get_thread_regcache (struct thread_info *thread, int fetch)
+{
+ struct regcache *regcache;
+
+ regcache = thread_regcache_data (thread);
+
+ /* Threads' regcaches are created lazily, because biarch targets add
+ the main thread/lwp before seeing it stop for the first time, and
+ it is only after the target sees the thread stop for the first
+ time that the target has a chance of determining the process's
+ architecture. IOW, when we first add the process's main thread
+ we don't know which architecture/tdesc its regcache should
+ have. */
+ if (regcache == NULL)
+ {
+ struct process_info *proc = get_thread_process (thread);
+
+ gdb_assert (proc->tdesc != NULL);
+
+ regcache = new_register_cache (proc->tdesc);
+ set_thread_regcache_data (thread, regcache);
+ }
+
+ if (fetch && regcache->registers_valid == 0)
+ {
+ struct thread_info *saved_thread = current_thread;
+
+ current_thread = thread;
+ /* Invalidate all registers, to prevent stale left-overs. */
+ memset (regcache->register_status, REG_UNAVAILABLE,
+ regcache->tdesc->reg_defs.size ());
+ fetch_inferior_registers (regcache, -1);
+ current_thread = saved_thread;
+ regcache->registers_valid = 1;
+ }
+
+ return regcache;
+}
+
+/* See gdbsupport/common-regcache.h. */
+
+struct regcache *
+get_thread_regcache_for_ptid (ptid_t ptid)
+{
+ return get_thread_regcache (find_thread_ptid (ptid), 1);
+}
+
+void
+regcache_invalidate_thread (struct thread_info *thread)
+{
+ struct regcache *regcache;
+
+ regcache = thread_regcache_data (thread);
+
+ if (regcache == NULL)
+ return;
+
+ if (regcache->registers_valid)
+ {
+ struct thread_info *saved_thread = current_thread;
+
+ current_thread = thread;
+ store_inferior_registers (regcache, -1);
+ current_thread = saved_thread;
+ }
+
+ regcache->registers_valid = 0;
+}
+
+/* See regcache.h. */
+
+void
+regcache_invalidate_pid (int pid)
+{
+ /* Only invalidate the regcaches of threads of this process. */
+ for_each_thread (pid, regcache_invalidate_thread);
+}
+
+/* See regcache.h. */
+
+void
+regcache_invalidate (void)
+{
+ /* Only update the threads of the current process. */
+ int pid = current_thread->id.pid ();
+
+ regcache_invalidate_pid (pid);
+}
+
+#endif
+
+struct regcache *
+init_register_cache (struct regcache *regcache,
+ const struct target_desc *tdesc,
+ unsigned char *regbuf)
+{
+ if (regbuf == NULL)
+ {
+#ifndef IN_PROCESS_AGENT
+ /* Make sure to zero-initialize the register cache when it is
+ created, in case there are registers the target never
+ fetches. This way they'll read as zero instead of
+ garbage. */
+ regcache->tdesc = tdesc;
+ regcache->registers
+ = (unsigned char *) xcalloc (1, tdesc->registers_size);
+ regcache->registers_owned = 1;
+ regcache->register_status
+ = (unsigned char *) xmalloc (tdesc->reg_defs.size ());
+ memset ((void *) regcache->register_status, REG_UNAVAILABLE,
+ tdesc->reg_defs.size ());
+#else
+ gdb_assert_not_reached ("can't allocate memory from the heap");
+#endif
+ }
+ else
+ {
+ regcache->tdesc = tdesc;
+ regcache->registers = regbuf;
+ regcache->registers_owned = 0;
+#ifndef IN_PROCESS_AGENT
+ regcache->register_status = NULL;
+#endif
+ }
+
+ regcache->registers_valid = 0;
+
+ return regcache;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+struct regcache *
+new_register_cache (const struct target_desc *tdesc)
+{
+ struct regcache *regcache = new struct regcache;
+
+ gdb_assert (tdesc->registers_size != 0);
+
+ return init_register_cache (regcache, tdesc, NULL);
+}
+
+void
+free_register_cache (struct regcache *regcache)
+{
+ if (regcache)
+ {
+ if (regcache->registers_owned)
+ free (regcache->registers);
+ free (regcache->register_status);
+ delete regcache;
+ }
+}
+
+#endif
+
+void
+regcache_cpy (struct regcache *dst, struct regcache *src)
+{
+ gdb_assert (src != NULL && dst != NULL);
+ gdb_assert (src->tdesc == dst->tdesc);
+ gdb_assert (src != dst);
+
+ memcpy (dst->registers, src->registers, src->tdesc->registers_size);
+#ifndef IN_PROCESS_AGENT
+ if (dst->register_status != NULL && src->register_status != NULL)
+ memcpy (dst->register_status, src->register_status,
+ src->tdesc->reg_defs.size ());
+#endif
+ dst->registers_valid = src->registers_valid;
+}
+
+/* Return a reference to the description of register N. */
+
+static const struct reg &
+find_register_by_number (const struct target_desc *tdesc, int n)
+{
+ return tdesc->reg_defs[n];
+}
+
+#ifndef IN_PROCESS_AGENT
+
+void
+registers_to_string (struct regcache *regcache, char *buf)
+{
+ unsigned char *registers = regcache->registers;
+ const struct target_desc *tdesc = regcache->tdesc;
+
+ for (int i = 0; i < tdesc->reg_defs.size (); ++i)
+ {
+ if (regcache->register_status[i] == REG_VALID)
+ {
+ bin2hex (registers, buf, register_size (tdesc, i));
+ buf += register_size (tdesc, i) * 2;
+ }
+ else
+ {
+ memset (buf, 'x', register_size (tdesc, i) * 2);
+ buf += register_size (tdesc, i) * 2;
+ }
+ registers += register_size (tdesc, i);
+ }
+ *buf = '\0';
+}
+
+void
+registers_from_string (struct regcache *regcache, char *buf)
+{
+ int len = strlen (buf);
+ unsigned char *registers = regcache->registers;
+ const struct target_desc *tdesc = regcache->tdesc;
+
+ if (len != tdesc->registers_size * 2)
+ {
+ warning ("Wrong sized register packet (expected %d bytes, got %d)",
+ 2 * tdesc->registers_size, len);
+ if (len > tdesc->registers_size * 2)
+ len = tdesc->registers_size * 2;
+ }
+ hex2bin (buf, registers, len / 2);
+}
+
+int
+find_regno (const struct target_desc *tdesc, const char *name)
+{
+ for (int i = 0; i < tdesc->reg_defs.size (); ++i)
+ {
+ if (strcmp (name, find_register_by_number (tdesc, i).name) == 0)
+ return i;
+ }
+ internal_error (__FILE__, __LINE__, "Unknown register %s requested",
+ name);
+}
+
+static void
+free_register_cache_thread (struct thread_info *thread)
+{
+ struct regcache *regcache = thread_regcache_data (thread);
+
+ if (regcache != NULL)
+ {
+ regcache_invalidate_thread (thread);
+ free_register_cache (regcache);
+ set_thread_regcache_data (thread, NULL);
+ }
+}
+
+void
+regcache_release (void)
+{
+ /* Flush and release all pre-existing register caches. */
+ for_each_thread (free_register_cache_thread);
+}
+#endif
+
+int
+register_cache_size (const struct target_desc *tdesc)
+{
+ return tdesc->registers_size;
+}
+
+int
+register_size (const struct target_desc *tdesc, int n)
+{
+ return find_register_by_number (tdesc, n).size / 8;
+}
+
+/* See gdbsupport/common-regcache.h. */
+
+int
+regcache_register_size (const struct regcache *regcache, int n)
+{
+ return register_size (regcache->tdesc, n);
+}
+
+static unsigned char *
+register_data (const struct regcache *regcache, int n, int fetch)
+{
+ return (regcache->registers
+ + find_register_by_number (regcache->tdesc, n).offset / 8);
+}
+
+void
+supply_register (struct regcache *regcache, int n, const void *buf)
+{
+ return regcache->raw_supply (n, buf);
+}
+
+/* See gdbsupport/common-regcache.h. */
+
+void
+regcache::raw_supply (int n, const void *buf)
+{
+ if (buf)
+ {
+ memcpy (register_data (this, n, 0), buf, register_size (tdesc, n));
+#ifndef IN_PROCESS_AGENT
+ if (register_status != NULL)
+ register_status[n] = REG_VALID;
+#endif
+ }
+ else
+ {
+ memset (register_data (this, n, 0), 0, register_size (tdesc, n));
+#ifndef IN_PROCESS_AGENT
+ if (register_status != NULL)
+ register_status[n] = REG_UNAVAILABLE;
+#endif
+ }
+}
+
+/* Supply register N with value zero to REGCACHE. */
+
+void
+supply_register_zeroed (struct regcache *regcache, int n)
+{
+ memset (register_data (regcache, n, 0), 0,
+ register_size (regcache->tdesc, n));
+#ifndef IN_PROCESS_AGENT
+ if (regcache->register_status != NULL)
+ regcache->register_status[n] = REG_VALID;
+#endif
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Supply register called NAME with value zero to REGCACHE. */
+
+void
+supply_register_by_name_zeroed (struct regcache *regcache,
+ const char *name)
+{
+ supply_register_zeroed (regcache, find_regno (regcache->tdesc, name));
+}
+
+#endif
+
+/* Supply the whole register set whose contents are stored in BUF, to
+ REGCACHE. If BUF is NULL, all the registers' values are recorded
+ as unavailable. */
+
+void
+supply_regblock (struct regcache *regcache, const void *buf)
+{
+ if (buf)
+ {
+ const struct target_desc *tdesc = regcache->tdesc;
+
+ memcpy (regcache->registers, buf, tdesc->registers_size);
+#ifndef IN_PROCESS_AGENT
+ {
+ int i;
+
+ for (i = 0; i < tdesc->reg_defs.size (); i++)
+ regcache->register_status[i] = REG_VALID;
+ }
+#endif
+ }
+ else
+ {
+ const struct target_desc *tdesc = regcache->tdesc;
+
+ memset (regcache->registers, 0, tdesc->registers_size);
+#ifndef IN_PROCESS_AGENT
+ {
+ int i;
+
+ for (i = 0; i < tdesc->reg_defs.size (); i++)
+ regcache->register_status[i] = REG_UNAVAILABLE;
+ }
+#endif
+ }
+}
+
+#ifndef IN_PROCESS_AGENT
+
+void
+supply_register_by_name (struct regcache *regcache,
+ const char *name, const void *buf)
+{
+ supply_register (regcache, find_regno (regcache->tdesc, name), buf);
+}
+
+#endif
+
+void
+collect_register (struct regcache *regcache, int n, void *buf)
+{
+ regcache->raw_collect (n, buf);
+}
+
+/* See gdbsupport/common-regcache.h. */
+
+void
+regcache::raw_collect (int n, void *buf) const
+{
+ memcpy (buf, register_data (this, n, 1), register_size (tdesc, n));
+}
+
+enum register_status
+regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
+ ULONGEST *val)
+{
+ int size;
+
+ gdb_assert (regcache != NULL);
+ gdb_assert (regnum >= 0
+ && regnum < regcache->tdesc->reg_defs.size ());
+
+ size = register_size (regcache->tdesc, regnum);
+
+ if (size > (int) sizeof (ULONGEST))
+ error (_("That operation is not available on integers of more than"
+ "%d bytes."),
+ (int) sizeof (ULONGEST));
+
+ *val = 0;
+ collect_register (regcache, regnum, val);
+
+ return REG_VALID;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* See regcache.h. */
+
+ULONGEST
+regcache_raw_get_unsigned_by_name (struct regcache *regcache,
+ const char *name)
+{
+ return regcache_raw_get_unsigned (regcache,
+ find_regno (regcache->tdesc, name));
+}
+
+void
+collect_register_as_string (struct regcache *regcache, int n, char *buf)
+{
+ bin2hex (register_data (regcache, n, 1), buf,
+ register_size (regcache->tdesc, n));
+}
+
+void
+collect_register_by_name (struct regcache *regcache,
+ const char *name, void *buf)
+{
+ collect_register (regcache, find_regno (regcache->tdesc, name), buf);
+}
+
+/* Special handling for register PC. */
+
+CORE_ADDR
+regcache_read_pc (struct regcache *regcache)
+{
+ CORE_ADDR pc_val;
+
+ if (the_target->read_pc)
+ pc_val = the_target->read_pc (regcache);
+ else
+ internal_error (__FILE__, __LINE__,
+ "regcache_read_pc: Unable to find PC");
+
+ return pc_val;
+}
+
+void
+regcache_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ if (the_target->write_pc)
+ the_target->write_pc (regcache, pc);
+ else
+ internal_error (__FILE__, __LINE__,
+ "regcache_write_pc: Unable to update PC");
+}
+
+#endif
+
+/* See gdbsupport/common-regcache.h. */
+
+enum register_status
+regcache::get_register_status (int regnum) const
+{
+#ifndef IN_PROCESS_AGENT
+ gdb_assert (regnum >= 0 && regnum < tdesc->reg_defs.size ());
+ return (enum register_status) (register_status[regnum]);
+#else
+ return REG_VALID;
+#endif
+}
+
+/* See gdbsupport/common-regcache.h. */
+
+bool
+regcache::raw_compare (int regnum, const void *buf, int offset) const
+{
+ gdb_assert (buf != NULL);
+
+ const unsigned char *regbuf = register_data (this, regnum, 1);
+ int size = register_size (tdesc, regnum);
+ gdb_assert (size >= offset);
+
+ return (memcmp (buf, regbuf + offset, size - offset) == 0);
+}
+++ /dev/null
-/* Remote utility routines for the remote server for GDB.
- Copyright (C) 1986-2020 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"
-#if HAVE_TERMIOS_H
-#include <termios.h>
-#endif
-#include "target.h"
-#include "gdbthread.h"
-#include "tdesc.h"
-#include "debug.h"
-#include "dll.h"
-#include "gdbsupport/rsp-low.h"
-#include "gdbsupport/netstuff.h"
-#include "gdbsupport/filestuff.h"
-#include "gdbsupport/gdb-sigmask.h"
-#include <ctype.h>
-#if HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#if HAVE_SYS_FILE_H
-#include <sys/file.h>
-#endif
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#if HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#if HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#if HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#if HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#if HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#include "gdbsupport/gdb_sys_time.h"
-#include <unistd.h>
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#include <sys/stat.h>
-
-#if USE_WIN32API
-#include <ws2tcpip.h>
-#endif
-
-#if __QNX__
-#include <sys/iomgr.h>
-#endif /* __QNX__ */
-
-#ifndef HAVE_SOCKLEN_T
-typedef int socklen_t;
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-#if USE_WIN32API
-# define INVALID_DESCRIPTOR INVALID_SOCKET
-#else
-# define INVALID_DESCRIPTOR -1
-#endif
-
-/* Extra value for readchar_callback. */
-enum {
- /* The callback is currently not scheduled. */
- NOT_SCHEDULED = -1
-};
-
-/* Status of the readchar callback.
- Either NOT_SCHEDULED or the callback id. */
-static int readchar_callback = NOT_SCHEDULED;
-
-static int readchar (void);
-static void reset_readchar (void);
-static void reschedule (void);
-
-/* A cache entry for a successfully looked-up symbol. */
-struct sym_cache
-{
- char *name;
- CORE_ADDR addr;
- struct sym_cache *next;
-};
-
-static int remote_is_stdio = 0;
-
-static gdb_fildes_t remote_desc = INVALID_DESCRIPTOR;
-static gdb_fildes_t listen_desc = INVALID_DESCRIPTOR;
-
-#ifdef USE_WIN32API
-# define read(fd, buf, len) recv (fd, (char *) buf, len, 0)
-# define write(fd, buf, len) send (fd, (char *) buf, len, 0)
-#endif
-
-int
-gdb_connected (void)
-{
- return remote_desc != INVALID_DESCRIPTOR;
-}
-
-/* Return true if the remote connection is over stdio. */
-
-int
-remote_connection_is_stdio (void)
-{
- return remote_is_stdio;
-}
-
-static void
-enable_async_notification (int fd)
-{
-#if defined(F_SETFL) && defined (FASYNC)
- int save_fcntl_flags;
-
- save_fcntl_flags = fcntl (fd, F_GETFL, 0);
- fcntl (fd, F_SETFL, save_fcntl_flags | FASYNC);
-#if defined (F_SETOWN)
- fcntl (fd, F_SETOWN, getpid ());
-#endif
-#endif
-}
-
-static int
-handle_accept_event (int err, gdb_client_data client_data)
-{
- struct sockaddr_storage sockaddr;
- socklen_t len = sizeof (sockaddr);
-
- if (debug_threads)
- debug_printf ("handling possible accept event\n");
-
- remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
- if (remote_desc == -1)
- perror_with_name ("Accept failed");
-
- /* Enable TCP keep alive process. */
- socklen_t tmp = 1;
- setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
- (char *) &tmp, sizeof (tmp));
-
- /* Tell TCP not to delay small packets. This greatly speeds up
- interactive response. */
- tmp = 1;
- setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
- (char *) &tmp, sizeof (tmp));
-
-#ifndef USE_WIN32API
- signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbserver simply
- exits when the remote side dies. */
-#endif
-
- if (run_once)
- {
-#ifndef USE_WIN32API
- close (listen_desc); /* No longer need this */
-#else
- closesocket (listen_desc); /* No longer need this */
-#endif
- }
-
- /* Even if !RUN_ONCE no longer notice new connections. Still keep the
- descriptor open for add_file_handler to wait for a new connection. */
- delete_file_handler (listen_desc);
-
- /* Convert IP address to string. */
- char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
-
- int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
- orig_host, sizeof (orig_host),
- orig_port, sizeof (orig_port),
- NI_NUMERICHOST | NI_NUMERICSERV);
-
- if (r != 0)
- fprintf (stderr, _("Could not obtain remote address: %s\n"),
- gai_strerror (r));
- else
- fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
- orig_host, orig_port);
-
- enable_async_notification (remote_desc);
-
- /* Register the event loop handler. */
- add_file_handler (remote_desc, handle_serial_event, NULL);
-
- /* We have a new GDB connection now. If we were disconnected
- tracing, there's a window where the target could report a stop
- event to the event loop, and since we have a connection now, we'd
- try to send vStopped notifications to GDB. But, don't do that
- until GDB as selected all-stop/non-stop, and has queried the
- threads' status ('?'). */
- target_async (0);
-
- return 0;
-}
-
-/* Prepare for a later connection to a remote debugger.
- NAME is the filename used for communication. */
-
-void
-remote_prepare (const char *name)
-{
- client_state &cs = get_client_state ();
-#ifdef USE_WIN32API
- static int winsock_initialized;
-#endif
- socklen_t tmp;
-
- remote_is_stdio = 0;
- if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
- {
- /* We need to record fact that we're using stdio sooner than the
- call to remote_open so start_inferior knows the connection is
- via stdio. */
- remote_is_stdio = 1;
- cs.transport_is_reliable = 1;
- return;
- }
-
- struct addrinfo hint;
- struct addrinfo *ainfo;
-
- memset (&hint, 0, sizeof (hint));
- /* Assume no prefix will be passed, therefore we should use
- AF_UNSPEC. */
- hint.ai_family = AF_UNSPEC;
- hint.ai_socktype = SOCK_STREAM;
- hint.ai_protocol = IPPROTO_TCP;
-
- parsed_connection_spec parsed
- = parse_connection_spec_without_prefix (name, &hint);
-
- if (parsed.port_str.empty ())
- {
- cs.transport_is_reliable = 0;
- return;
- }
-
-#ifdef USE_WIN32API
- if (!winsock_initialized)
- {
- WSADATA wsad;
-
- WSAStartup (MAKEWORD (1, 0), &wsad);
- winsock_initialized = 1;
- }
-#endif
-
- int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
- &hint, &ainfo);
-
- if (r != 0)
- error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
-
- scoped_free_addrinfo freeaddrinfo (ainfo);
-
- struct addrinfo *iter;
-
- for (iter = ainfo; iter != NULL; iter = iter->ai_next)
- {
- listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
- iter->ai_protocol);
-
- if (listen_desc >= 0)
- break;
- }
-
- if (iter == NULL)
- perror_with_name ("Can't open socket");
-
- /* Allow rapid reuse of this port. */
- tmp = 1;
- setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
- sizeof (tmp));
-
- switch (iter->ai_family)
- {
- case AF_INET:
- ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
- break;
- default:
- internal_error (__FILE__, __LINE__,
- _("Invalid 'ai_family' %d\n"), iter->ai_family);
- }
-
- if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
- perror_with_name ("Can't bind address");
-
- if (listen (listen_desc, 1) != 0)
- perror_with_name ("Can't listen on socket");
-
- cs.transport_is_reliable = 1;
-}
-
-/* Open a connection to a remote debugger.
- NAME is the filename used for communication. */
-
-void
-remote_open (const char *name)
-{
- const char *port_str;
-
- port_str = strchr (name, ':');
-#ifdef USE_WIN32API
- if (port_str == NULL)
- error ("Only HOST:PORT is supported on this platform.");
-#endif
-
- if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
- {
- fprintf (stderr, "Remote debugging using stdio\n");
-
- /* Use stdin as the handle of the connection.
- We only select on reads, for example. */
- remote_desc = fileno (stdin);
-
- enable_async_notification (remote_desc);
-
- /* Register the event loop handler. */
- add_file_handler (remote_desc, handle_serial_event, NULL);
- }
-#ifndef USE_WIN32API
- else if (port_str == NULL)
- {
- struct stat statbuf;
-
- if (stat (name, &statbuf) == 0
- && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
- remote_desc = open (name, O_RDWR);
- else
- {
- errno = EINVAL;
- remote_desc = -1;
- }
-
- if (remote_desc < 0)
- perror_with_name ("Could not open remote device");
-
-#if HAVE_TERMIOS_H
- {
- struct termios termios;
- tcgetattr (remote_desc, &termios);
-
- termios.c_iflag = 0;
- termios.c_oflag = 0;
- termios.c_lflag = 0;
- termios.c_cflag &= ~(CSIZE | PARENB);
- termios.c_cflag |= CLOCAL | CS8;
- termios.c_cc[VMIN] = 1;
- termios.c_cc[VTIME] = 0;
-
- tcsetattr (remote_desc, TCSANOW, &termios);
- }
-#endif
-
- fprintf (stderr, "Remote debugging using %s\n", name);
-
- enable_async_notification (remote_desc);
-
- /* Register the event loop handler. */
- add_file_handler (remote_desc, handle_serial_event, NULL);
- }
-#endif /* USE_WIN32API */
- else
- {
- char listen_port[GDB_NI_MAX_PORT];
- struct sockaddr_storage sockaddr;
- socklen_t len = sizeof (sockaddr);
-
- if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
- perror_with_name ("Can't determine port");
-
- int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
- NULL, 0,
- listen_port, sizeof (listen_port),
- NI_NUMERICSERV);
-
- if (r != 0)
- fprintf (stderr, _("Can't obtain port where we are listening: %s"),
- gai_strerror (r));
- else
- fprintf (stderr, _("Listening on port %s\n"), listen_port);
-
- fflush (stderr);
-
- /* Register the event loop handler. */
- add_file_handler (listen_desc, handle_accept_event, NULL);
- }
-}
-
-void
-remote_close (void)
-{
- delete_file_handler (remote_desc);
-
- disable_async_io ();
-
-#ifdef USE_WIN32API
- closesocket (remote_desc);
-#else
- if (! remote_connection_is_stdio ())
- close (remote_desc);
-#endif
- remote_desc = INVALID_DESCRIPTOR;
-
- reset_readchar ();
-}
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-void
-decode_address (CORE_ADDR *addrp, const char *start, int len)
-{
- CORE_ADDR addr;
- char ch;
- int i;
-
- addr = 0;
- for (i = 0; i < len; i++)
- {
- ch = start[i];
- addr = addr << 4;
- addr = addr | (fromhex (ch) & 0x0f);
- }
- *addrp = addr;
-}
-
-const char *
-decode_address_to_semicolon (CORE_ADDR *addrp, const char *start)
-{
- const char *end;
-
- end = start;
- while (*end != '\0' && *end != ';')
- end++;
-
- decode_address (addrp, start, end - start);
-
- if (*end == ';')
- end++;
- return end;
-}
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-/* Look for a sequence of characters which can be run-length encoded.
- If there are any, update *CSUM and *P. Otherwise, output the
- single character. Return the number of characters consumed. */
-
-static int
-try_rle (char *buf, int remaining, unsigned char *csum, char **p)
-{
- int n;
-
- /* Always output the character. */
- *csum += buf[0];
- *(*p)++ = buf[0];
-
- /* Don't go past '~'. */
- if (remaining > 97)
- remaining = 97;
-
- for (n = 1; n < remaining; n++)
- if (buf[n] != buf[0])
- break;
-
- /* N is the index of the first character not the same as buf[0].
- buf[0] is counted twice, so by decrementing N, we get the number
- of characters the RLE sequence will replace. */
- n--;
-
- if (n < 3)
- return 1;
-
- /* Skip the frame characters. The manual says to skip '+' and '-'
- also, but there's no reason to. Unfortunately these two unusable
- characters double the encoded length of a four byte zero
- value. */
- while (n + 29 == '$' || n + 29 == '#')
- n--;
-
- *csum += '*';
- *(*p)++ = '*';
- *csum += n + 29;
- *(*p)++ = n + 29;
-
- return n + 1;
-}
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-/* Write a PTID to BUF. Returns BUF+CHARACTERS_WRITTEN. */
-
-char *
-write_ptid (char *buf, ptid_t ptid)
-{
- client_state &cs = get_client_state ();
- int pid, tid;
-
- if (cs.multi_process)
- {
- pid = ptid.pid ();
- if (pid < 0)
- buf += sprintf (buf, "p-%x.", -pid);
- else
- buf += sprintf (buf, "p%x.", pid);
- }
- tid = ptid.lwp ();
- if (tid < 0)
- buf += sprintf (buf, "-%x", -tid);
- else
- buf += sprintf (buf, "%x", tid);
-
- return buf;
-}
-
-static ULONGEST
-hex_or_minus_one (const char *buf, const char **obuf)
-{
- ULONGEST ret;
-
- if (startswith (buf, "-1"))
- {
- ret = (ULONGEST) -1;
- buf += 2;
- }
- else
- buf = unpack_varlen_hex (buf, &ret);
-
- if (obuf)
- *obuf = buf;
-
- return ret;
-}
-
-/* Extract a PTID from BUF. If non-null, OBUF is set to the to one
- passed the last parsed char. Returns null_ptid on error. */
-ptid_t
-read_ptid (const char *buf, const char **obuf)
-{
- const char *p = buf;
- const char *pp;
- ULONGEST pid = 0, tid = 0;
-
- if (*p == 'p')
- {
- /* Multi-process ptid. */
- pp = unpack_varlen_hex (p + 1, &pid);
- if (*pp != '.')
- error ("invalid remote ptid: %s\n", p);
-
- p = pp + 1;
-
- tid = hex_or_minus_one (p, &pp);
-
- if (obuf)
- *obuf = pp;
- return ptid_t (pid, tid, 0);
- }
-
- /* No multi-process. Just a tid. */
- tid = hex_or_minus_one (p, &pp);
-
- /* Since GDB is not sending a process id (multi-process extensions
- are off), then there's only one process. Default to the first in
- the list. */
- pid = pid_of (get_first_process ());
-
- if (obuf)
- *obuf = pp;
- return ptid_t (pid, tid, 0);
-}
-
-/* Write COUNT bytes in BUF to the client.
- The result is the number of bytes written or -1 if error.
- This may return less than COUNT. */
-
-static int
-write_prim (const void *buf, int count)
-{
- if (remote_connection_is_stdio ())
- return write (fileno (stdout), buf, count);
- else
- return write (remote_desc, buf, count);
-}
-
-/* Read COUNT bytes from the client and store in BUF.
- The result is the number of bytes read or -1 if error.
- This may return less than COUNT. */
-
-static int
-read_prim (void *buf, int count)
-{
- if (remote_connection_is_stdio ())
- return read (fileno (stdin), buf, count);
- else
- return read (remote_desc, buf, count);
-}
-
-/* Send a packet to the remote machine, with error checking.
- The data of the packet is in BUF, and the length of the
- packet is in CNT. Returns >= 0 on success, -1 otherwise. */
-
-static int
-putpkt_binary_1 (char *buf, int cnt, int is_notif)
-{
- client_state &cs = get_client_state ();
- int i;
- unsigned char csum = 0;
- char *buf2;
- char *p;
- int cc;
-
- buf2 = (char *) xmalloc (strlen ("$") + cnt + strlen ("#nn") + 1);
-
- /* Copy the packet into buffer BUF2, encapsulating it
- and giving it a checksum. */
-
- p = buf2;
- if (is_notif)
- *p++ = '%';
- else
- *p++ = '$';
-
- for (i = 0; i < cnt;)
- i += try_rle (buf + i, cnt - i, &csum, &p);
-
- *p++ = '#';
- *p++ = tohex ((csum >> 4) & 0xf);
- *p++ = tohex (csum & 0xf);
-
- *p = '\0';
-
- /* Send it over and over until we get a positive ack. */
-
- do
- {
- if (write_prim (buf2, p - buf2) != p - buf2)
- {
- perror ("putpkt(write)");
- free (buf2);
- return -1;
- }
-
- if (cs.noack_mode || is_notif)
- {
- /* Don't expect an ack then. */
- if (remote_debug)
- {
- if (is_notif)
- debug_printf ("putpkt (\"%s\"); [notif]\n", buf2);
- else
- debug_printf ("putpkt (\"%s\"); [noack mode]\n", buf2);
- debug_flush ();
- }
- break;
- }
-
- if (remote_debug)
- {
- debug_printf ("putpkt (\"%s\"); [looking for ack]\n", buf2);
- debug_flush ();
- }
-
- cc = readchar ();
-
- if (cc < 0)
- {
- free (buf2);
- return -1;
- }
-
- if (remote_debug)
- {
- debug_printf ("[received '%c' (0x%x)]\n", cc, cc);
- debug_flush ();
- }
-
- /* Check for an input interrupt while we're here. */
- if (cc == '\003' && current_thread != NULL)
- (*the_target->request_interrupt) ();
- }
- while (cc != '+');
-
- free (buf2);
- return 1; /* Success! */
-}
-
-int
-putpkt_binary (char *buf, int cnt)
-{
- return putpkt_binary_1 (buf, cnt, 0);
-}
-
-/* Send a packet to the remote machine, with error checking. The data
- of the packet is in BUF, and the packet should be a NUL-terminated
- string. Returns >= 0 on success, -1 otherwise. */
-
-int
-putpkt (char *buf)
-{
- return putpkt_binary (buf, strlen (buf));
-}
-
-int
-putpkt_notif (char *buf)
-{
- return putpkt_binary_1 (buf, strlen (buf), 1);
-}
-
-/* Come here when we get an input interrupt from the remote side. This
- interrupt should only be active while we are waiting for the child to do
- something. Thus this assumes readchar:bufcnt is 0.
- About the only thing that should come through is a ^C, which
- will cause us to request child interruption. */
-
-static void
-input_interrupt (int unused)
-{
- fd_set readset;
- struct timeval immediate = { 0, 0 };
-
- /* Protect against spurious interrupts. This has been observed to
- be a problem under NetBSD 1.4 and 1.5. */
-
- FD_ZERO (&readset);
- FD_SET (remote_desc, &readset);
- if (select (remote_desc + 1, &readset, 0, 0, &immediate) > 0)
- {
- int cc;
- char c = 0;
-
- cc = read_prim (&c, 1);
-
- if (cc == 0)
- {
- fprintf (stderr, "client connection closed\n");
- return;
- }
- else if (cc != 1 || c != '\003')
- {
- fprintf (stderr, "input_interrupt, count = %d c = %d ", cc, c);
- if (isprint (c))
- fprintf (stderr, "('%c')\n", c);
- else
- fprintf (stderr, "('\\x%02x')\n", c & 0xff);
- return;
- }
-
- (*the_target->request_interrupt) ();
- }
-}
-
-/* Check if the remote side sent us an interrupt request (^C). */
-void
-check_remote_input_interrupt_request (void)
-{
- /* This function may be called before establishing communications,
- therefore we need to validate the remote descriptor. */
-
- if (remote_desc == INVALID_DESCRIPTOR)
- return;
-
- input_interrupt (0);
-}
-
-/* Asynchronous I/O support. SIGIO must be unblocked when waiting,
- in order to accept Control-C from the client, and must be blocked
- when talking to the client. */
-
-static void
-block_unblock_async_io (int block)
-{
-#ifndef USE_WIN32API
- sigset_t sigio_set;
-
- sigemptyset (&sigio_set);
- sigaddset (&sigio_set, SIGIO);
- gdb_sigmask (block ? SIG_BLOCK : SIG_UNBLOCK, &sigio_set, NULL);
-#endif
-}
-
-#ifdef __QNX__
-static void
-nto_comctrl (int enable)
-{
- struct sigevent event;
-
- if (enable)
- {
- event.sigev_notify = SIGEV_SIGNAL_THREAD;
- event.sigev_signo = SIGIO;
- event.sigev_code = 0;
- event.sigev_value.sival_ptr = NULL;
- event.sigev_priority = -1;
- ionotify (remote_desc, _NOTIFY_ACTION_POLLARM, _NOTIFY_COND_INPUT,
- &event);
- }
- else
- ionotify (remote_desc, _NOTIFY_ACTION_POLL, _NOTIFY_COND_INPUT, NULL);
-}
-#endif /* __QNX__ */
-
-
-/* Current state of asynchronous I/O. */
-static int async_io_enabled;
-
-/* Enable asynchronous I/O. */
-void
-enable_async_io (void)
-{
- if (async_io_enabled)
- return;
-
- block_unblock_async_io (0);
-
- async_io_enabled = 1;
-#ifdef __QNX__
- nto_comctrl (1);
-#endif /* __QNX__ */
-}
-
-/* Disable asynchronous I/O. */
-void
-disable_async_io (void)
-{
- if (!async_io_enabled)
- return;
-
- block_unblock_async_io (1);
-
- async_io_enabled = 0;
-#ifdef __QNX__
- nto_comctrl (0);
-#endif /* __QNX__ */
-
-}
-
-void
-initialize_async_io (void)
-{
- /* Make sure that async I/O starts blocked. */
- async_io_enabled = 1;
- disable_async_io ();
-
- /* Install the signal handler. */
-#ifndef USE_WIN32API
- signal (SIGIO, input_interrupt);
-#endif
-}
-
-/* Internal buffer used by readchar.
- These are global to readchar because reschedule_remote needs to be
- able to tell whether the buffer is empty. */
-
-static unsigned char readchar_buf[BUFSIZ];
-static int readchar_bufcnt = 0;
-static unsigned char *readchar_bufp;
-
-/* Returns next char from remote GDB. -1 if error. */
-
-static int
-readchar (void)
-{
- int ch;
-
- if (readchar_bufcnt == 0)
- {
- readchar_bufcnt = read_prim (readchar_buf, sizeof (readchar_buf));
-
- if (readchar_bufcnt <= 0)
- {
- if (readchar_bufcnt == 0)
- {
- if (remote_debug)
- debug_printf ("readchar: Got EOF\n");
- }
- else
- perror ("readchar");
-
- return -1;
- }
-
- readchar_bufp = readchar_buf;
- }
-
- readchar_bufcnt--;
- ch = *readchar_bufp++;
- reschedule ();
- return ch;
-}
-
-/* Reset the readchar state machine. */
-
-static void
-reset_readchar (void)
-{
- readchar_bufcnt = 0;
- if (readchar_callback != NOT_SCHEDULED)
- {
- delete_callback_event (readchar_callback);
- readchar_callback = NOT_SCHEDULED;
- }
-}
-
-/* Process remaining data in readchar_buf. */
-
-static int
-process_remaining (void *context)
-{
- int res;
-
- /* This is a one-shot event. */
- readchar_callback = NOT_SCHEDULED;
-
- if (readchar_bufcnt > 0)
- res = handle_serial_event (0, NULL);
- else
- res = 0;
-
- return res;
-}
-
-/* If there is still data in the buffer, queue another event to process it,
- we can't sleep in select yet. */
-
-static void
-reschedule (void)
-{
- if (readchar_bufcnt > 0 && readchar_callback == NOT_SCHEDULED)
- readchar_callback = append_callback_event (process_remaining, NULL);
-}
-
-/* Read a packet from the remote machine, with error checking,
- and store it in BUF. Returns length of packet, or negative if error. */
-
-int
-getpkt (char *buf)
-{
- client_state &cs = get_client_state ();
- char *bp;
- unsigned char csum, c1, c2;
- int c;
-
- while (1)
- {
- csum = 0;
-
- while (1)
- {
- c = readchar ();
-
- /* The '\003' may appear before or after each packet, so
- check for an input interrupt. */
- if (c == '\003')
- {
- (*the_target->request_interrupt) ();
- continue;
- }
-
- if (c == '$')
- break;
- if (remote_debug)
- {
- debug_printf ("[getpkt: discarding char '%c']\n", c);
- debug_flush ();
- }
-
- if (c < 0)
- return -1;
- }
-
- bp = buf;
- while (1)
- {
- c = readchar ();
- if (c < 0)
- return -1;
- if (c == '#')
- break;
- *bp++ = c;
- csum += c;
- }
- *bp = 0;
-
- c1 = fromhex (readchar ());
- c2 = fromhex (readchar ());
-
- if (csum == (c1 << 4) + c2)
- break;
-
- if (cs.noack_mode)
- {
- fprintf (stderr,
- "Bad checksum, sentsum=0x%x, csum=0x%x, "
- "buf=%s [no-ack-mode, Bad medium?]\n",
- (c1 << 4) + c2, csum, buf);
- /* Not much we can do, GDB wasn't expecting an ack/nac. */
- break;
- }
-
- fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
- (c1 << 4) + c2, csum, buf);
- if (write_prim ("-", 1) != 1)
- return -1;
- }
-
- if (!cs.noack_mode)
- {
- if (remote_debug)
- {
- debug_printf ("getpkt (\"%s\"); [sending ack] \n", buf);
- debug_flush ();
- }
-
- if (write_prim ("+", 1) != 1)
- return -1;
-
- if (remote_debug)
- {
- debug_printf ("[sent ack]\n");
- debug_flush ();
- }
- }
- else
- {
- if (remote_debug)
- {
- debug_printf ("getpkt (\"%s\"); [no ack sent] \n", buf);
- debug_flush ();
- }
- }
-
- /* The readchar above may have already read a '\003' out of the socket
- and moved it to the local buffer. For example, when GDB sends
- vCont;c immediately followed by interrupt (see
- gdb.base/interrupt-noterm.exp). As soon as we see the vCont;c, we'll
- resume the inferior and wait. Since we've already moved the '\003'
- to the local buffer, SIGIO won't help. In that case, if we don't
- check for interrupt after the vCont;c packet, the interrupt character
- would stay in the buffer unattended until after the next (unrelated)
- stop. */
- while (readchar_bufcnt > 0 && *readchar_bufp == '\003')
- {
- /* Consume the interrupt character in the buffer. */
- readchar ();
- (*the_target->request_interrupt) ();
- }
-
- return bp - buf;
-}
-
-void
-write_ok (char *buf)
-{
- buf[0] = 'O';
- buf[1] = 'K';
- buf[2] = '\0';
-}
-
-void
-write_enn (char *buf)
-{
- /* Some day, we should define the meanings of the error codes... */
- buf[0] = 'E';
- buf[1] = '0';
- buf[2] = '1';
- buf[3] = '\0';
-}
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-static char *
-outreg (struct regcache *regcache, int regno, char *buf)
-{
- if ((regno >> 12) != 0)
- *buf++ = tohex ((regno >> 12) & 0xf);
- if ((regno >> 8) != 0)
- *buf++ = tohex ((regno >> 8) & 0xf);
- *buf++ = tohex ((regno >> 4) & 0xf);
- *buf++ = tohex (regno & 0xf);
- *buf++ = ':';
- collect_register_as_string (regcache, regno, buf);
- buf += 2 * register_size (regcache->tdesc, regno);
- *buf++ = ';';
-
- return buf;
-}
-
-void
-prepare_resume_reply (char *buf, ptid_t ptid,
- struct target_waitstatus *status)
-{
- client_state &cs = get_client_state ();
- if (debug_threads)
- debug_printf ("Writing resume reply for %s:%d\n",
- target_pid_to_str (ptid), status->kind);
-
- switch (status->kind)
- {
- case TARGET_WAITKIND_STOPPED:
- case TARGET_WAITKIND_FORKED:
- case TARGET_WAITKIND_VFORKED:
- case TARGET_WAITKIND_VFORK_DONE:
- case TARGET_WAITKIND_EXECD:
- case TARGET_WAITKIND_THREAD_CREATED:
- case TARGET_WAITKIND_SYSCALL_ENTRY:
- case TARGET_WAITKIND_SYSCALL_RETURN:
- {
- struct thread_info *saved_thread;
- const char **regp;
- struct regcache *regcache;
-
- if ((status->kind == TARGET_WAITKIND_FORKED && cs.report_fork_events)
- || (status->kind == TARGET_WAITKIND_VFORKED
- && cs.report_vfork_events))
- {
- enum gdb_signal signal = GDB_SIGNAL_TRAP;
- const char *event = (status->kind == TARGET_WAITKIND_FORKED
- ? "fork" : "vfork");
-
- sprintf (buf, "T%02x%s:", signal, event);
- buf += strlen (buf);
- buf = write_ptid (buf, status->value.related_pid);
- strcat (buf, ";");
- }
- else if (status->kind == TARGET_WAITKIND_VFORK_DONE
- && cs.report_vfork_events)
- {
- enum gdb_signal signal = GDB_SIGNAL_TRAP;
-
- sprintf (buf, "T%02xvforkdone:;", signal);
- }
- else if (status->kind == TARGET_WAITKIND_EXECD && cs.report_exec_events)
- {
- enum gdb_signal signal = GDB_SIGNAL_TRAP;
- const char *event = "exec";
- char hexified_pathname[PATH_MAX * 2];
-
- sprintf (buf, "T%02x%s:", signal, event);
- buf += strlen (buf);
-
- /* Encode pathname to hexified format. */
- bin2hex ((const gdb_byte *) status->value.execd_pathname,
- hexified_pathname,
- strlen (status->value.execd_pathname));
-
- sprintf (buf, "%s;", hexified_pathname);
- xfree (status->value.execd_pathname);
- status->value.execd_pathname = NULL;
- buf += strlen (buf);
- }
- else if (status->kind == TARGET_WAITKIND_THREAD_CREATED
- && cs.report_thread_events)
- {
- enum gdb_signal signal = GDB_SIGNAL_TRAP;
-
- sprintf (buf, "T%02xcreate:;", signal);
- }
- else if (status->kind == TARGET_WAITKIND_SYSCALL_ENTRY
- || status->kind == TARGET_WAITKIND_SYSCALL_RETURN)
- {
- enum gdb_signal signal = GDB_SIGNAL_TRAP;
- const char *event = (status->kind == TARGET_WAITKIND_SYSCALL_ENTRY
- ? "syscall_entry" : "syscall_return");
-
- sprintf (buf, "T%02x%s:%x;", signal, event,
- status->value.syscall_number);
- }
- else
- sprintf (buf, "T%02x", status->value.sig);
-
- buf += strlen (buf);
-
- saved_thread = current_thread;
-
- switch_to_thread (the_target, ptid);
-
- regp = current_target_desc ()->expedite_regs;
-
- regcache = get_thread_regcache (current_thread, 1);
-
- if (the_target->stopped_by_watchpoint != NULL
- && (*the_target->stopped_by_watchpoint) ())
- {
- CORE_ADDR addr;
- int i;
-
- memcpy (buf, "watch:", 6);
- buf += 6;
-
- addr = (*the_target->stopped_data_address) ();
-
- /* Convert each byte of the address into two hexadecimal
- chars. Note that we take sizeof (void *) instead of
- sizeof (addr); this is to avoid sending a 64-bit
- address to a 32-bit GDB. */
- for (i = sizeof (void *) * 2; i > 0; i--)
- *buf++ = tohex ((addr >> (i - 1) * 4) & 0xf);
- *buf++ = ';';
- }
- else if (cs.swbreak_feature && target_stopped_by_sw_breakpoint ())
- {
- sprintf (buf, "swbreak:;");
- buf += strlen (buf);
- }
- else if (cs.hwbreak_feature && target_stopped_by_hw_breakpoint ())
- {
- sprintf (buf, "hwbreak:;");
- buf += strlen (buf);
- }
-
- while (*regp)
- {
- buf = outreg (regcache, find_regno (regcache->tdesc, *regp), buf);
- regp ++;
- }
- *buf = '\0';
-
- /* Formerly, if the debugger had not used any thread features
- we would not burden it with a thread status response. This
- was for the benefit of GDB 4.13 and older. However, in
- recent GDB versions the check (``if (cont_thread != 0)'')
- does not have the desired effect because of sillyness in
- the way that the remote protocol handles specifying a
- thread. Since thread support relies on qSymbol support
- anyway, assume GDB can handle threads. */
-
- if (using_threads && !disable_packet_Tthread)
- {
- /* This if (1) ought to be unnecessary. But remote_wait
- in GDB will claim this event belongs to inferior_ptid
- if we do not specify a thread, and there's no way for
- gdbserver to know what inferior_ptid is. */
- if (1 || cs.general_thread != ptid)
- {
- int core = -1;
- /* In non-stop, don't change the general thread behind
- GDB's back. */
- if (!non_stop)
- cs.general_thread = ptid;
- sprintf (buf, "thread:");
- buf += strlen (buf);
- buf = write_ptid (buf, ptid);
- strcat (buf, ";");
- buf += strlen (buf);
-
- core = target_core_of_thread (ptid);
-
- if (core != -1)
- {
- sprintf (buf, "core:");
- buf += strlen (buf);
- sprintf (buf, "%x", core);
- strcat (buf, ";");
- buf += strlen (buf);
- }
- }
- }
-
- if (dlls_changed)
- {
- strcpy (buf, "library:;");
- buf += strlen (buf);
- dlls_changed = 0;
- }
-
- current_thread = saved_thread;
- }
- break;
- case TARGET_WAITKIND_EXITED:
- if (cs.multi_process)
- sprintf (buf, "W%x;process:%x",
- status->value.integer, ptid.pid ());
- else
- sprintf (buf, "W%02x", status->value.integer);
- break;
- case TARGET_WAITKIND_SIGNALLED:
- if (cs.multi_process)
- sprintf (buf, "X%x;process:%x",
- status->value.sig, ptid.pid ());
- else
- sprintf (buf, "X%02x", status->value.sig);
- break;
- case TARGET_WAITKIND_THREAD_EXITED:
- sprintf (buf, "w%x;", status->value.integer);
- buf += strlen (buf);
- buf = write_ptid (buf, ptid);
- break;
- case TARGET_WAITKIND_NO_RESUMED:
- sprintf (buf, "N");
- break;
- default:
- error ("unhandled waitkind");
- break;
- }
-}
-
-void
-decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
-{
- int i = 0, j = 0;
- char ch;
- *mem_addr_ptr = *len_ptr = 0;
-
- while ((ch = from[i++]) != ',')
- {
- *mem_addr_ptr = *mem_addr_ptr << 4;
- *mem_addr_ptr |= fromhex (ch) & 0x0f;
- }
-
- for (j = 0; j < 4; j++)
- {
- if ((ch = from[i++]) == 0)
- break;
- *len_ptr = *len_ptr << 4;
- *len_ptr |= fromhex (ch) & 0x0f;
- }
-}
-
-void
-decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
- unsigned char **to_p)
-{
- int i = 0;
- char ch;
- *mem_addr_ptr = *len_ptr = 0;
-
- while ((ch = from[i++]) != ',')
- {
- *mem_addr_ptr = *mem_addr_ptr << 4;
- *mem_addr_ptr |= fromhex (ch) & 0x0f;
- }
-
- while ((ch = from[i++]) != ':')
- {
- *len_ptr = *len_ptr << 4;
- *len_ptr |= fromhex (ch) & 0x0f;
- }
-
- if (*to_p == NULL)
- *to_p = (unsigned char *) xmalloc (*len_ptr);
-
- hex2bin (&from[i++], *to_p, *len_ptr);
-}
-
-int
-decode_X_packet (char *from, int packet_len, CORE_ADDR *mem_addr_ptr,
- unsigned int *len_ptr, unsigned char **to_p)
-{
- int i = 0;
- char ch;
- *mem_addr_ptr = *len_ptr = 0;
-
- while ((ch = from[i++]) != ',')
- {
- *mem_addr_ptr = *mem_addr_ptr << 4;
- *mem_addr_ptr |= fromhex (ch) & 0x0f;
- }
-
- while ((ch = from[i++]) != ':')
- {
- *len_ptr = *len_ptr << 4;
- *len_ptr |= fromhex (ch) & 0x0f;
- }
-
- if (*to_p == NULL)
- *to_p = (unsigned char *) xmalloc (*len_ptr);
-
- if (remote_unescape_input ((const gdb_byte *) &from[i], packet_len - i,
- *to_p, *len_ptr) != *len_ptr)
- return -1;
-
- return 0;
-}
-
-/* Decode a qXfer write request. */
-
-int
-decode_xfer_write (char *buf, int packet_len, CORE_ADDR *offset,
- unsigned int *len, unsigned char *data)
-{
- char ch;
- char *b = buf;
-
- /* Extract the offset. */
- *offset = 0;
- while ((ch = *buf++) != ':')
- {
- *offset = *offset << 4;
- *offset |= fromhex (ch) & 0x0f;
- }
-
- /* Get encoded data. */
- packet_len -= buf - b;
- *len = remote_unescape_input ((const gdb_byte *) buf, packet_len,
- data, packet_len);
- return 0;
-}
-
-/* Decode the parameters of a qSearch:memory packet. */
-
-int
-decode_search_memory_packet (const char *buf, int packet_len,
- CORE_ADDR *start_addrp,
- CORE_ADDR *search_space_lenp,
- gdb_byte *pattern, unsigned int *pattern_lenp)
-{
- const char *p = buf;
-
- p = decode_address_to_semicolon (start_addrp, p);
- p = decode_address_to_semicolon (search_space_lenp, p);
- packet_len -= p - buf;
- *pattern_lenp = remote_unescape_input ((const gdb_byte *) p, packet_len,
- pattern, packet_len);
- return 0;
-}
-
-static void
-free_sym_cache (struct sym_cache *sym)
-{
- if (sym != NULL)
- {
- free (sym->name);
- free (sym);
- }
-}
-
-void
-clear_symbol_cache (struct sym_cache **symcache_p)
-{
- struct sym_cache *sym, *next;
-
- /* Check the cache first. */
- for (sym = *symcache_p; sym; sym = next)
- {
- next = sym->next;
- free_sym_cache (sym);
- }
-
- *symcache_p = NULL;
-}
-
-/* Get the address of NAME, and return it in ADDRP if found. if
- MAY_ASK_GDB is false, assume symbol cache misses are failures.
- Returns 1 if the symbol is found, 0 if it is not, -1 on error. */
-
-int
-look_up_one_symbol (const char *name, CORE_ADDR *addrp, int may_ask_gdb)
-{
- client_state &cs = get_client_state ();
- char *p, *q;
- int len;
- struct sym_cache *sym;
- struct process_info *proc;
-
- proc = current_process ();
-
- /* Check the cache first. */
- for (sym = proc->symbol_cache; sym; sym = sym->next)
- if (strcmp (name, sym->name) == 0)
- {
- *addrp = sym->addr;
- return 1;
- }
-
- /* It might not be an appropriate time to look up a symbol,
- e.g. while we're trying to fetch registers. */
- if (!may_ask_gdb)
- return 0;
-
- /* Send the request. */
- strcpy (cs.own_buf, "qSymbol:");
- bin2hex ((const gdb_byte *) name, cs.own_buf + strlen ("qSymbol:"),
- strlen (name));
- if (putpkt (cs.own_buf) < 0)
- return -1;
-
- /* FIXME: Eventually add buffer overflow checking (to getpkt?) */
- len = getpkt (cs.own_buf);
- if (len < 0)
- return -1;
-
- /* We ought to handle pretty much any packet at this point while we
- wait for the qSymbol "response". That requires re-entering the
- main loop. For now, this is an adequate approximation; allow
- GDB to read from memory and handle 'v' packets (for vFile transfers)
- while it figures out the address of the symbol. */
- while (1)
- {
- if (cs.own_buf[0] == 'm')
- {
- CORE_ADDR mem_addr;
- unsigned char *mem_buf;
- unsigned int mem_len;
-
- decode_m_packet (&cs.own_buf[1], &mem_addr, &mem_len);
- mem_buf = (unsigned char *) xmalloc (mem_len);
- if (read_inferior_memory (mem_addr, mem_buf, mem_len) == 0)
- bin2hex (mem_buf, cs.own_buf, mem_len);
- else
- write_enn (cs.own_buf);
- free (mem_buf);
- if (putpkt (cs.own_buf) < 0)
- return -1;
- }
- else if (cs.own_buf[0] == 'v')
- {
- int new_len = -1;
- handle_v_requests (cs.own_buf, len, &new_len);
- if (new_len != -1)
- putpkt_binary (cs.own_buf, new_len);
- else
- putpkt (cs.own_buf);
- }
- else
- break;
- len = getpkt (cs.own_buf);
- if (len < 0)
- return -1;
- }
-
- if (!startswith (cs.own_buf, "qSymbol:"))
- {
- warning ("Malformed response to qSymbol, ignoring: %s", cs.own_buf);
- return -1;
- }
-
- p = cs.own_buf + strlen ("qSymbol:");
- q = p;
- while (*q && *q != ':')
- q++;
-
- /* Make sure we found a value for the symbol. */
- if (p == q || *q == '\0')
- return 0;
-
- decode_address (addrp, p, q - p);
-
- /* Save the symbol in our cache. */
- sym = XNEW (struct sym_cache);
- sym->name = xstrdup (name);
- sym->addr = *addrp;
- sym->next = proc->symbol_cache;
- proc->symbol_cache = sym;
-
- return 1;
-}
-
-/* Relocate an instruction to execute at a different address. OLDLOC
- is the address in the inferior memory where the instruction to
- relocate is currently at. On input, TO points to the destination
- where we want the instruction to be copied (and possibly adjusted)
- to. On output, it points to one past the end of the resulting
- instruction(s). The effect of executing the instruction at TO
- shall be the same as if executing it at OLDLOC. For example, call
- instructions that implicitly push the return address on the stack
- should be adjusted to return to the instruction after OLDLOC;
- relative branches, and other PC-relative instructions need the
- offset adjusted; etc. Returns 0 on success, -1 on failure. */
-
-int
-relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
-{
- client_state &cs = get_client_state ();
- int len;
- ULONGEST written = 0;
-
- /* Send the request. */
- sprintf (cs.own_buf, "qRelocInsn:%s;%s", paddress (oldloc),
- paddress (*to));
- if (putpkt (cs.own_buf) < 0)
- return -1;
-
- /* FIXME: Eventually add buffer overflow checking (to getpkt?) */
- len = getpkt (cs.own_buf);
- if (len < 0)
- return -1;
-
- /* We ought to handle pretty much any packet at this point while we
- wait for the qRelocInsn "response". That requires re-entering
- the main loop. For now, this is an adequate approximation; allow
- GDB to access memory. */
- while (cs.own_buf[0] == 'm' || cs.own_buf[0] == 'M' || cs.own_buf[0] == 'X')
- {
- CORE_ADDR mem_addr;
- unsigned char *mem_buf = NULL;
- unsigned int mem_len;
-
- if (cs.own_buf[0] == 'm')
- {
- decode_m_packet (&cs.own_buf[1], &mem_addr, &mem_len);
- mem_buf = (unsigned char *) xmalloc (mem_len);
- if (read_inferior_memory (mem_addr, mem_buf, mem_len) == 0)
- bin2hex (mem_buf, cs.own_buf, mem_len);
- else
- write_enn (cs.own_buf);
- }
- else if (cs.own_buf[0] == 'X')
- {
- if (decode_X_packet (&cs.own_buf[1], len - 1, &mem_addr,
- &mem_len, &mem_buf) < 0
- || target_write_memory (mem_addr, mem_buf, mem_len) != 0)
- write_enn (cs.own_buf);
- else
- write_ok (cs.own_buf);
- }
- else
- {
- decode_M_packet (&cs.own_buf[1], &mem_addr, &mem_len, &mem_buf);
- if (target_write_memory (mem_addr, mem_buf, mem_len) == 0)
- write_ok (cs.own_buf);
- else
- write_enn (cs.own_buf);
- }
- free (mem_buf);
- if (putpkt (cs.own_buf) < 0)
- return -1;
- len = getpkt (cs.own_buf);
- if (len < 0)
- return -1;
- }
-
- if (cs.own_buf[0] == 'E')
- {
- warning ("An error occurred while relocating an instruction: %s",
- cs.own_buf);
- return -1;
- }
-
- if (!startswith (cs.own_buf, "qRelocInsn:"))
- {
- warning ("Malformed response to qRelocInsn, ignoring: %s",
- cs.own_buf);
- return -1;
- }
-
- unpack_varlen_hex (cs.own_buf + strlen ("qRelocInsn:"), &written);
-
- *to += written;
- return 0;
-}
-
-void
-monitor_output (const char *msg)
-{
- int len = strlen (msg);
- char *buf = (char *) xmalloc (len * 2 + 2);
-
- buf[0] = 'O';
- bin2hex ((const gdb_byte *) msg, buf + 1, len);
-
- putpkt (buf);
- free (buf);
-}
-
-#endif
--- /dev/null
+/* Remote utility routines for the remote server for GDB.
+ Copyright (C) 1986-2020 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"
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#include "target.h"
+#include "gdbthread.h"
+#include "tdesc.h"
+#include "debug.h"
+#include "dll.h"
+#include "gdbsupport/rsp-low.h"
+#include "gdbsupport/netstuff.h"
+#include "gdbsupport/filestuff.h"
+#include "gdbsupport/gdb-sigmask.h"
+#include <ctype.h>
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "gdbsupport/gdb_sys_time.h"
+#include <unistd.h>
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <sys/stat.h>
+
+#if USE_WIN32API
+#include <ws2tcpip.h>
+#endif
+
+#if __QNX__
+#include <sys/iomgr.h>
+#endif /* __QNX__ */
+
+#ifndef HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+#if USE_WIN32API
+# define INVALID_DESCRIPTOR INVALID_SOCKET
+#else
+# define INVALID_DESCRIPTOR -1
+#endif
+
+/* Extra value for readchar_callback. */
+enum {
+ /* The callback is currently not scheduled. */
+ NOT_SCHEDULED = -1
+};
+
+/* Status of the readchar callback.
+ Either NOT_SCHEDULED or the callback id. */
+static int readchar_callback = NOT_SCHEDULED;
+
+static int readchar (void);
+static void reset_readchar (void);
+static void reschedule (void);
+
+/* A cache entry for a successfully looked-up symbol. */
+struct sym_cache
+{
+ char *name;
+ CORE_ADDR addr;
+ struct sym_cache *next;
+};
+
+static int remote_is_stdio = 0;
+
+static gdb_fildes_t remote_desc = INVALID_DESCRIPTOR;
+static gdb_fildes_t listen_desc = INVALID_DESCRIPTOR;
+
+#ifdef USE_WIN32API
+# define read(fd, buf, len) recv (fd, (char *) buf, len, 0)
+# define write(fd, buf, len) send (fd, (char *) buf, len, 0)
+#endif
+
+int
+gdb_connected (void)
+{
+ return remote_desc != INVALID_DESCRIPTOR;
+}
+
+/* Return true if the remote connection is over stdio. */
+
+int
+remote_connection_is_stdio (void)
+{
+ return remote_is_stdio;
+}
+
+static void
+enable_async_notification (int fd)
+{
+#if defined(F_SETFL) && defined (FASYNC)
+ int save_fcntl_flags;
+
+ save_fcntl_flags = fcntl (fd, F_GETFL, 0);
+ fcntl (fd, F_SETFL, save_fcntl_flags | FASYNC);
+#if defined (F_SETOWN)
+ fcntl (fd, F_SETOWN, getpid ());
+#endif
+#endif
+}
+
+static int
+handle_accept_event (int err, gdb_client_data client_data)
+{
+ struct sockaddr_storage sockaddr;
+ socklen_t len = sizeof (sockaddr);
+
+ if (debug_threads)
+ debug_printf ("handling possible accept event\n");
+
+ remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
+ if (remote_desc == -1)
+ perror_with_name ("Accept failed");
+
+ /* Enable TCP keep alive process. */
+ socklen_t tmp = 1;
+ setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
+ (char *) &tmp, sizeof (tmp));
+
+ /* Tell TCP not to delay small packets. This greatly speeds up
+ interactive response. */
+ tmp = 1;
+ setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
+ (char *) &tmp, sizeof (tmp));
+
+#ifndef USE_WIN32API
+ signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbserver simply
+ exits when the remote side dies. */
+#endif
+
+ if (run_once)
+ {
+#ifndef USE_WIN32API
+ close (listen_desc); /* No longer need this */
+#else
+ closesocket (listen_desc); /* No longer need this */
+#endif
+ }
+
+ /* Even if !RUN_ONCE no longer notice new connections. Still keep the
+ descriptor open for add_file_handler to wait for a new connection. */
+ delete_file_handler (listen_desc);
+
+ /* Convert IP address to string. */
+ char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+ int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+ orig_host, sizeof (orig_host),
+ orig_port, sizeof (orig_port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (r != 0)
+ fprintf (stderr, _("Could not obtain remote address: %s\n"),
+ gai_strerror (r));
+ else
+ fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
+ orig_host, orig_port);
+
+ enable_async_notification (remote_desc);
+
+ /* Register the event loop handler. */
+ add_file_handler (remote_desc, handle_serial_event, NULL);
+
+ /* We have a new GDB connection now. If we were disconnected
+ tracing, there's a window where the target could report a stop
+ event to the event loop, and since we have a connection now, we'd
+ try to send vStopped notifications to GDB. But, don't do that
+ until GDB as selected all-stop/non-stop, and has queried the
+ threads' status ('?'). */
+ target_async (0);
+
+ return 0;
+}
+
+/* Prepare for a later connection to a remote debugger.
+ NAME is the filename used for communication. */
+
+void
+remote_prepare (const char *name)
+{
+ client_state &cs = get_client_state ();
+#ifdef USE_WIN32API
+ static int winsock_initialized;
+#endif
+ socklen_t tmp;
+
+ remote_is_stdio = 0;
+ if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
+ {
+ /* We need to record fact that we're using stdio sooner than the
+ call to remote_open so start_inferior knows the connection is
+ via stdio. */
+ remote_is_stdio = 1;
+ cs.transport_is_reliable = 1;
+ return;
+ }
+
+ struct addrinfo hint;
+ struct addrinfo *ainfo;
+
+ memset (&hint, 0, sizeof (hint));
+ /* Assume no prefix will be passed, therefore we should use
+ AF_UNSPEC. */
+ hint.ai_family = AF_UNSPEC;
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+
+ parsed_connection_spec parsed
+ = parse_connection_spec_without_prefix (name, &hint);
+
+ if (parsed.port_str.empty ())
+ {
+ cs.transport_is_reliable = 0;
+ return;
+ }
+
+#ifdef USE_WIN32API
+ if (!winsock_initialized)
+ {
+ WSADATA wsad;
+
+ WSAStartup (MAKEWORD (1, 0), &wsad);
+ winsock_initialized = 1;
+ }
+#endif
+
+ int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+ &hint, &ainfo);
+
+ if (r != 0)
+ error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
+
+ scoped_free_addrinfo freeaddrinfo (ainfo);
+
+ struct addrinfo *iter;
+
+ for (iter = ainfo; iter != NULL; iter = iter->ai_next)
+ {
+ listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
+ iter->ai_protocol);
+
+ if (listen_desc >= 0)
+ break;
+ }
+
+ if (iter == NULL)
+ perror_with_name ("Can't open socket");
+
+ /* Allow rapid reuse of this port. */
+ tmp = 1;
+ setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+ sizeof (tmp));
+
+ switch (iter->ai_family)
+ {
+ case AF_INET:
+ ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Invalid 'ai_family' %d\n"), iter->ai_family);
+ }
+
+ if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
+ perror_with_name ("Can't bind address");
+
+ if (listen (listen_desc, 1) != 0)
+ perror_with_name ("Can't listen on socket");
+
+ cs.transport_is_reliable = 1;
+}
+
+/* Open a connection to a remote debugger.
+ NAME is the filename used for communication. */
+
+void
+remote_open (const char *name)
+{
+ const char *port_str;
+
+ port_str = strchr (name, ':');
+#ifdef USE_WIN32API
+ if (port_str == NULL)
+ error ("Only HOST:PORT is supported on this platform.");
+#endif
+
+ if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
+ {
+ fprintf (stderr, "Remote debugging using stdio\n");
+
+ /* Use stdin as the handle of the connection.
+ We only select on reads, for example. */
+ remote_desc = fileno (stdin);
+
+ enable_async_notification (remote_desc);
+
+ /* Register the event loop handler. */
+ add_file_handler (remote_desc, handle_serial_event, NULL);
+ }
+#ifndef USE_WIN32API
+ else if (port_str == NULL)
+ {
+ struct stat statbuf;
+
+ if (stat (name, &statbuf) == 0
+ && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
+ remote_desc = open (name, O_RDWR);
+ else
+ {
+ errno = EINVAL;
+ remote_desc = -1;
+ }
+
+ if (remote_desc < 0)
+ perror_with_name ("Could not open remote device");
+
+#if HAVE_TERMIOS_H
+ {
+ struct termios termios;
+ tcgetattr (remote_desc, &termios);
+
+ termios.c_iflag = 0;
+ termios.c_oflag = 0;
+ termios.c_lflag = 0;
+ termios.c_cflag &= ~(CSIZE | PARENB);
+ termios.c_cflag |= CLOCAL | CS8;
+ termios.c_cc[VMIN] = 1;
+ termios.c_cc[VTIME] = 0;
+
+ tcsetattr (remote_desc, TCSANOW, &termios);
+ }
+#endif
+
+ fprintf (stderr, "Remote debugging using %s\n", name);
+
+ enable_async_notification (remote_desc);
+
+ /* Register the event loop handler. */
+ add_file_handler (remote_desc, handle_serial_event, NULL);
+ }
+#endif /* USE_WIN32API */
+ else
+ {
+ char listen_port[GDB_NI_MAX_PORT];
+ struct sockaddr_storage sockaddr;
+ socklen_t len = sizeof (sockaddr);
+
+ if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
+ perror_with_name ("Can't determine port");
+
+ int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+ NULL, 0,
+ listen_port, sizeof (listen_port),
+ NI_NUMERICSERV);
+
+ if (r != 0)
+ fprintf (stderr, _("Can't obtain port where we are listening: %s"),
+ gai_strerror (r));
+ else
+ fprintf (stderr, _("Listening on port %s\n"), listen_port);
+
+ fflush (stderr);
+
+ /* Register the event loop handler. */
+ add_file_handler (listen_desc, handle_accept_event, NULL);
+ }
+}
+
+void
+remote_close (void)
+{
+ delete_file_handler (remote_desc);
+
+ disable_async_io ();
+
+#ifdef USE_WIN32API
+ closesocket (remote_desc);
+#else
+ if (! remote_connection_is_stdio ())
+ close (remote_desc);
+#endif
+ remote_desc = INVALID_DESCRIPTOR;
+
+ reset_readchar ();
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+void
+decode_address (CORE_ADDR *addrp, const char *start, int len)
+{
+ CORE_ADDR addr;
+ char ch;
+ int i;
+
+ addr = 0;
+ for (i = 0; i < len; i++)
+ {
+ ch = start[i];
+ addr = addr << 4;
+ addr = addr | (fromhex (ch) & 0x0f);
+ }
+ *addrp = addr;
+}
+
+const char *
+decode_address_to_semicolon (CORE_ADDR *addrp, const char *start)
+{
+ const char *end;
+
+ end = start;
+ while (*end != '\0' && *end != ';')
+ end++;
+
+ decode_address (addrp, start, end - start);
+
+ if (*end == ';')
+ end++;
+ return end;
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+/* Look for a sequence of characters which can be run-length encoded.
+ If there are any, update *CSUM and *P. Otherwise, output the
+ single character. Return the number of characters consumed. */
+
+static int
+try_rle (char *buf, int remaining, unsigned char *csum, char **p)
+{
+ int n;
+
+ /* Always output the character. */
+ *csum += buf[0];
+ *(*p)++ = buf[0];
+
+ /* Don't go past '~'. */
+ if (remaining > 97)
+ remaining = 97;
+
+ for (n = 1; n < remaining; n++)
+ if (buf[n] != buf[0])
+ break;
+
+ /* N is the index of the first character not the same as buf[0].
+ buf[0] is counted twice, so by decrementing N, we get the number
+ of characters the RLE sequence will replace. */
+ n--;
+
+ if (n < 3)
+ return 1;
+
+ /* Skip the frame characters. The manual says to skip '+' and '-'
+ also, but there's no reason to. Unfortunately these two unusable
+ characters double the encoded length of a four byte zero
+ value. */
+ while (n + 29 == '$' || n + 29 == '#')
+ n--;
+
+ *csum += '*';
+ *(*p)++ = '*';
+ *csum += n + 29;
+ *(*p)++ = n + 29;
+
+ return n + 1;
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+/* Write a PTID to BUF. Returns BUF+CHARACTERS_WRITTEN. */
+
+char *
+write_ptid (char *buf, ptid_t ptid)
+{
+ client_state &cs = get_client_state ();
+ int pid, tid;
+
+ if (cs.multi_process)
+ {
+ pid = ptid.pid ();
+ if (pid < 0)
+ buf += sprintf (buf, "p-%x.", -pid);
+ else
+ buf += sprintf (buf, "p%x.", pid);
+ }
+ tid = ptid.lwp ();
+ if (tid < 0)
+ buf += sprintf (buf, "-%x", -tid);
+ else
+ buf += sprintf (buf, "%x", tid);
+
+ return buf;
+}
+
+static ULONGEST
+hex_or_minus_one (const char *buf, const char **obuf)
+{
+ ULONGEST ret;
+
+ if (startswith (buf, "-1"))
+ {
+ ret = (ULONGEST) -1;
+ buf += 2;
+ }
+ else
+ buf = unpack_varlen_hex (buf, &ret);
+
+ if (obuf)
+ *obuf = buf;
+
+ return ret;
+}
+
+/* Extract a PTID from BUF. If non-null, OBUF is set to the to one
+ passed the last parsed char. Returns null_ptid on error. */
+ptid_t
+read_ptid (const char *buf, const char **obuf)
+{
+ const char *p = buf;
+ const char *pp;
+ ULONGEST pid = 0, tid = 0;
+
+ if (*p == 'p')
+ {
+ /* Multi-process ptid. */
+ pp = unpack_varlen_hex (p + 1, &pid);
+ if (*pp != '.')
+ error ("invalid remote ptid: %s\n", p);
+
+ p = pp + 1;
+
+ tid = hex_or_minus_one (p, &pp);
+
+ if (obuf)
+ *obuf = pp;
+ return ptid_t (pid, tid, 0);
+ }
+
+ /* No multi-process. Just a tid. */
+ tid = hex_or_minus_one (p, &pp);
+
+ /* Since GDB is not sending a process id (multi-process extensions
+ are off), then there's only one process. Default to the first in
+ the list. */
+ pid = pid_of (get_first_process ());
+
+ if (obuf)
+ *obuf = pp;
+ return ptid_t (pid, tid, 0);
+}
+
+/* Write COUNT bytes in BUF to the client.
+ The result is the number of bytes written or -1 if error.
+ This may return less than COUNT. */
+
+static int
+write_prim (const void *buf, int count)
+{
+ if (remote_connection_is_stdio ())
+ return write (fileno (stdout), buf, count);
+ else
+ return write (remote_desc, buf, count);
+}
+
+/* Read COUNT bytes from the client and store in BUF.
+ The result is the number of bytes read or -1 if error.
+ This may return less than COUNT. */
+
+static int
+read_prim (void *buf, int count)
+{
+ if (remote_connection_is_stdio ())
+ return read (fileno (stdin), buf, count);
+ else
+ return read (remote_desc, buf, count);
+}
+
+/* Send a packet to the remote machine, with error checking.
+ The data of the packet is in BUF, and the length of the
+ packet is in CNT. Returns >= 0 on success, -1 otherwise. */
+
+static int
+putpkt_binary_1 (char *buf, int cnt, int is_notif)
+{
+ client_state &cs = get_client_state ();
+ int i;
+ unsigned char csum = 0;
+ char *buf2;
+ char *p;
+ int cc;
+
+ buf2 = (char *) xmalloc (strlen ("$") + cnt + strlen ("#nn") + 1);
+
+ /* Copy the packet into buffer BUF2, encapsulating it
+ and giving it a checksum. */
+
+ p = buf2;
+ if (is_notif)
+ *p++ = '%';
+ else
+ *p++ = '$';
+
+ for (i = 0; i < cnt;)
+ i += try_rle (buf + i, cnt - i, &csum, &p);
+
+ *p++ = '#';
+ *p++ = tohex ((csum >> 4) & 0xf);
+ *p++ = tohex (csum & 0xf);
+
+ *p = '\0';
+
+ /* Send it over and over until we get a positive ack. */
+
+ do
+ {
+ if (write_prim (buf2, p - buf2) != p - buf2)
+ {
+ perror ("putpkt(write)");
+ free (buf2);
+ return -1;
+ }
+
+ if (cs.noack_mode || is_notif)
+ {
+ /* Don't expect an ack then. */
+ if (remote_debug)
+ {
+ if (is_notif)
+ debug_printf ("putpkt (\"%s\"); [notif]\n", buf2);
+ else
+ debug_printf ("putpkt (\"%s\"); [noack mode]\n", buf2);
+ debug_flush ();
+ }
+ break;
+ }
+
+ if (remote_debug)
+ {
+ debug_printf ("putpkt (\"%s\"); [looking for ack]\n", buf2);
+ debug_flush ();
+ }
+
+ cc = readchar ();
+
+ if (cc < 0)
+ {
+ free (buf2);
+ return -1;
+ }
+
+ if (remote_debug)
+ {
+ debug_printf ("[received '%c' (0x%x)]\n", cc, cc);
+ debug_flush ();
+ }
+
+ /* Check for an input interrupt while we're here. */
+ if (cc == '\003' && current_thread != NULL)
+ (*the_target->request_interrupt) ();
+ }
+ while (cc != '+');
+
+ free (buf2);
+ return 1; /* Success! */
+}
+
+int
+putpkt_binary (char *buf, int cnt)
+{
+ return putpkt_binary_1 (buf, cnt, 0);
+}
+
+/* Send a packet to the remote machine, with error checking. The data
+ of the packet is in BUF, and the packet should be a NUL-terminated
+ string. Returns >= 0 on success, -1 otherwise. */
+
+int
+putpkt (char *buf)
+{
+ return putpkt_binary (buf, strlen (buf));
+}
+
+int
+putpkt_notif (char *buf)
+{
+ return putpkt_binary_1 (buf, strlen (buf), 1);
+}
+
+/* Come here when we get an input interrupt from the remote side. This
+ interrupt should only be active while we are waiting for the child to do
+ something. Thus this assumes readchar:bufcnt is 0.
+ About the only thing that should come through is a ^C, which
+ will cause us to request child interruption. */
+
+static void
+input_interrupt (int unused)
+{
+ fd_set readset;
+ struct timeval immediate = { 0, 0 };
+
+ /* Protect against spurious interrupts. This has been observed to
+ be a problem under NetBSD 1.4 and 1.5. */
+
+ FD_ZERO (&readset);
+ FD_SET (remote_desc, &readset);
+ if (select (remote_desc + 1, &readset, 0, 0, &immediate) > 0)
+ {
+ int cc;
+ char c = 0;
+
+ cc = read_prim (&c, 1);
+
+ if (cc == 0)
+ {
+ fprintf (stderr, "client connection closed\n");
+ return;
+ }
+ else if (cc != 1 || c != '\003')
+ {
+ fprintf (stderr, "input_interrupt, count = %d c = %d ", cc, c);
+ if (isprint (c))
+ fprintf (stderr, "('%c')\n", c);
+ else
+ fprintf (stderr, "('\\x%02x')\n", c & 0xff);
+ return;
+ }
+
+ (*the_target->request_interrupt) ();
+ }
+}
+
+/* Check if the remote side sent us an interrupt request (^C). */
+void
+check_remote_input_interrupt_request (void)
+{
+ /* This function may be called before establishing communications,
+ therefore we need to validate the remote descriptor. */
+
+ if (remote_desc == INVALID_DESCRIPTOR)
+ return;
+
+ input_interrupt (0);
+}
+
+/* Asynchronous I/O support. SIGIO must be unblocked when waiting,
+ in order to accept Control-C from the client, and must be blocked
+ when talking to the client. */
+
+static void
+block_unblock_async_io (int block)
+{
+#ifndef USE_WIN32API
+ sigset_t sigio_set;
+
+ sigemptyset (&sigio_set);
+ sigaddset (&sigio_set, SIGIO);
+ gdb_sigmask (block ? SIG_BLOCK : SIG_UNBLOCK, &sigio_set, NULL);
+#endif
+}
+
+#ifdef __QNX__
+static void
+nto_comctrl (int enable)
+{
+ struct sigevent event;
+
+ if (enable)
+ {
+ event.sigev_notify = SIGEV_SIGNAL_THREAD;
+ event.sigev_signo = SIGIO;
+ event.sigev_code = 0;
+ event.sigev_value.sival_ptr = NULL;
+ event.sigev_priority = -1;
+ ionotify (remote_desc, _NOTIFY_ACTION_POLLARM, _NOTIFY_COND_INPUT,
+ &event);
+ }
+ else
+ ionotify (remote_desc, _NOTIFY_ACTION_POLL, _NOTIFY_COND_INPUT, NULL);
+}
+#endif /* __QNX__ */
+
+
+/* Current state of asynchronous I/O. */
+static int async_io_enabled;
+
+/* Enable asynchronous I/O. */
+void
+enable_async_io (void)
+{
+ if (async_io_enabled)
+ return;
+
+ block_unblock_async_io (0);
+
+ async_io_enabled = 1;
+#ifdef __QNX__
+ nto_comctrl (1);
+#endif /* __QNX__ */
+}
+
+/* Disable asynchronous I/O. */
+void
+disable_async_io (void)
+{
+ if (!async_io_enabled)
+ return;
+
+ block_unblock_async_io (1);
+
+ async_io_enabled = 0;
+#ifdef __QNX__
+ nto_comctrl (0);
+#endif /* __QNX__ */
+
+}
+
+void
+initialize_async_io (void)
+{
+ /* Make sure that async I/O starts blocked. */
+ async_io_enabled = 1;
+ disable_async_io ();
+
+ /* Install the signal handler. */
+#ifndef USE_WIN32API
+ signal (SIGIO, input_interrupt);
+#endif
+}
+
+/* Internal buffer used by readchar.
+ These are global to readchar because reschedule_remote needs to be
+ able to tell whether the buffer is empty. */
+
+static unsigned char readchar_buf[BUFSIZ];
+static int readchar_bufcnt = 0;
+static unsigned char *readchar_bufp;
+
+/* Returns next char from remote GDB. -1 if error. */
+
+static int
+readchar (void)
+{
+ int ch;
+
+ if (readchar_bufcnt == 0)
+ {
+ readchar_bufcnt = read_prim (readchar_buf, sizeof (readchar_buf));
+
+ if (readchar_bufcnt <= 0)
+ {
+ if (readchar_bufcnt == 0)
+ {
+ if (remote_debug)
+ debug_printf ("readchar: Got EOF\n");
+ }
+ else
+ perror ("readchar");
+
+ return -1;
+ }
+
+ readchar_bufp = readchar_buf;
+ }
+
+ readchar_bufcnt--;
+ ch = *readchar_bufp++;
+ reschedule ();
+ return ch;
+}
+
+/* Reset the readchar state machine. */
+
+static void
+reset_readchar (void)
+{
+ readchar_bufcnt = 0;
+ if (readchar_callback != NOT_SCHEDULED)
+ {
+ delete_callback_event (readchar_callback);
+ readchar_callback = NOT_SCHEDULED;
+ }
+}
+
+/* Process remaining data in readchar_buf. */
+
+static int
+process_remaining (void *context)
+{
+ int res;
+
+ /* This is a one-shot event. */
+ readchar_callback = NOT_SCHEDULED;
+
+ if (readchar_bufcnt > 0)
+ res = handle_serial_event (0, NULL);
+ else
+ res = 0;
+
+ return res;
+}
+
+/* If there is still data in the buffer, queue another event to process it,
+ we can't sleep in select yet. */
+
+static void
+reschedule (void)
+{
+ if (readchar_bufcnt > 0 && readchar_callback == NOT_SCHEDULED)
+ readchar_callback = append_callback_event (process_remaining, NULL);
+}
+
+/* Read a packet from the remote machine, with error checking,
+ and store it in BUF. Returns length of packet, or negative if error. */
+
+int
+getpkt (char *buf)
+{
+ client_state &cs = get_client_state ();
+ char *bp;
+ unsigned char csum, c1, c2;
+ int c;
+
+ while (1)
+ {
+ csum = 0;
+
+ while (1)
+ {
+ c = readchar ();
+
+ /* The '\003' may appear before or after each packet, so
+ check for an input interrupt. */
+ if (c == '\003')
+ {
+ (*the_target->request_interrupt) ();
+ continue;
+ }
+
+ if (c == '$')
+ break;
+ if (remote_debug)
+ {
+ debug_printf ("[getpkt: discarding char '%c']\n", c);
+ debug_flush ();
+ }
+
+ if (c < 0)
+ return -1;
+ }
+
+ bp = buf;
+ while (1)
+ {
+ c = readchar ();
+ if (c < 0)
+ return -1;
+ if (c == '#')
+ break;
+ *bp++ = c;
+ csum += c;
+ }
+ *bp = 0;
+
+ c1 = fromhex (readchar ());
+ c2 = fromhex (readchar ());
+
+ if (csum == (c1 << 4) + c2)
+ break;
+
+ if (cs.noack_mode)
+ {
+ fprintf (stderr,
+ "Bad checksum, sentsum=0x%x, csum=0x%x, "
+ "buf=%s [no-ack-mode, Bad medium?]\n",
+ (c1 << 4) + c2, csum, buf);
+ /* Not much we can do, GDB wasn't expecting an ack/nac. */
+ break;
+ }
+
+ fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
+ (c1 << 4) + c2, csum, buf);
+ if (write_prim ("-", 1) != 1)
+ return -1;
+ }
+
+ if (!cs.noack_mode)
+ {
+ if (remote_debug)
+ {
+ debug_printf ("getpkt (\"%s\"); [sending ack] \n", buf);
+ debug_flush ();
+ }
+
+ if (write_prim ("+", 1) != 1)
+ return -1;
+
+ if (remote_debug)
+ {
+ debug_printf ("[sent ack]\n");
+ debug_flush ();
+ }
+ }
+ else
+ {
+ if (remote_debug)
+ {
+ debug_printf ("getpkt (\"%s\"); [no ack sent] \n", buf);
+ debug_flush ();
+ }
+ }
+
+ /* The readchar above may have already read a '\003' out of the socket
+ and moved it to the local buffer. For example, when GDB sends
+ vCont;c immediately followed by interrupt (see
+ gdb.base/interrupt-noterm.exp). As soon as we see the vCont;c, we'll
+ resume the inferior and wait. Since we've already moved the '\003'
+ to the local buffer, SIGIO won't help. In that case, if we don't
+ check for interrupt after the vCont;c packet, the interrupt character
+ would stay in the buffer unattended until after the next (unrelated)
+ stop. */
+ while (readchar_bufcnt > 0 && *readchar_bufp == '\003')
+ {
+ /* Consume the interrupt character in the buffer. */
+ readchar ();
+ (*the_target->request_interrupt) ();
+ }
+
+ return bp - buf;
+}
+
+void
+write_ok (char *buf)
+{
+ buf[0] = 'O';
+ buf[1] = 'K';
+ buf[2] = '\0';
+}
+
+void
+write_enn (char *buf)
+{
+ /* Some day, we should define the meanings of the error codes... */
+ buf[0] = 'E';
+ buf[1] = '0';
+ buf[2] = '1';
+ buf[3] = '\0';
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+static char *
+outreg (struct regcache *regcache, int regno, char *buf)
+{
+ if ((regno >> 12) != 0)
+ *buf++ = tohex ((regno >> 12) & 0xf);
+ if ((regno >> 8) != 0)
+ *buf++ = tohex ((regno >> 8) & 0xf);
+ *buf++ = tohex ((regno >> 4) & 0xf);
+ *buf++ = tohex (regno & 0xf);
+ *buf++ = ':';
+ collect_register_as_string (regcache, regno, buf);
+ buf += 2 * register_size (regcache->tdesc, regno);
+ *buf++ = ';';
+
+ return buf;
+}
+
+void
+prepare_resume_reply (char *buf, ptid_t ptid,
+ struct target_waitstatus *status)
+{
+ client_state &cs = get_client_state ();
+ if (debug_threads)
+ debug_printf ("Writing resume reply for %s:%d\n",
+ target_pid_to_str (ptid), status->kind);
+
+ switch (status->kind)
+ {
+ case TARGET_WAITKIND_STOPPED:
+ case TARGET_WAITKIND_FORKED:
+ case TARGET_WAITKIND_VFORKED:
+ case TARGET_WAITKIND_VFORK_DONE:
+ case TARGET_WAITKIND_EXECD:
+ case TARGET_WAITKIND_THREAD_CREATED:
+ case TARGET_WAITKIND_SYSCALL_ENTRY:
+ case TARGET_WAITKIND_SYSCALL_RETURN:
+ {
+ struct thread_info *saved_thread;
+ const char **regp;
+ struct regcache *regcache;
+
+ if ((status->kind == TARGET_WAITKIND_FORKED && cs.report_fork_events)
+ || (status->kind == TARGET_WAITKIND_VFORKED
+ && cs.report_vfork_events))
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = (status->kind == TARGET_WAITKIND_FORKED
+ ? "fork" : "vfork");
+
+ sprintf (buf, "T%02x%s:", signal, event);
+ buf += strlen (buf);
+ buf = write_ptid (buf, status->value.related_pid);
+ strcat (buf, ";");
+ }
+ else if (status->kind == TARGET_WAITKIND_VFORK_DONE
+ && cs.report_vfork_events)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+
+ sprintf (buf, "T%02xvforkdone:;", signal);
+ }
+ else if (status->kind == TARGET_WAITKIND_EXECD && cs.report_exec_events)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = "exec";
+ char hexified_pathname[PATH_MAX * 2];
+
+ sprintf (buf, "T%02x%s:", signal, event);
+ buf += strlen (buf);
+
+ /* Encode pathname to hexified format. */
+ bin2hex ((const gdb_byte *) status->value.execd_pathname,
+ hexified_pathname,
+ strlen (status->value.execd_pathname));
+
+ sprintf (buf, "%s;", hexified_pathname);
+ xfree (status->value.execd_pathname);
+ status->value.execd_pathname = NULL;
+ buf += strlen (buf);
+ }
+ else if (status->kind == TARGET_WAITKIND_THREAD_CREATED
+ && cs.report_thread_events)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+
+ sprintf (buf, "T%02xcreate:;", signal);
+ }
+ else if (status->kind == TARGET_WAITKIND_SYSCALL_ENTRY
+ || status->kind == TARGET_WAITKIND_SYSCALL_RETURN)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = (status->kind == TARGET_WAITKIND_SYSCALL_ENTRY
+ ? "syscall_entry" : "syscall_return");
+
+ sprintf (buf, "T%02x%s:%x;", signal, event,
+ status->value.syscall_number);
+ }
+ else
+ sprintf (buf, "T%02x", status->value.sig);
+
+ buf += strlen (buf);
+
+ saved_thread = current_thread;
+
+ switch_to_thread (the_target, ptid);
+
+ regp = current_target_desc ()->expedite_regs;
+
+ regcache = get_thread_regcache (current_thread, 1);
+
+ if (the_target->stopped_by_watchpoint != NULL
+ && (*the_target->stopped_by_watchpoint) ())
+ {
+ CORE_ADDR addr;
+ int i;
+
+ memcpy (buf, "watch:", 6);
+ buf += 6;
+
+ addr = (*the_target->stopped_data_address) ();
+
+ /* Convert each byte of the address into two hexadecimal
+ chars. Note that we take sizeof (void *) instead of
+ sizeof (addr); this is to avoid sending a 64-bit
+ address to a 32-bit GDB. */
+ for (i = sizeof (void *) * 2; i > 0; i--)
+ *buf++ = tohex ((addr >> (i - 1) * 4) & 0xf);
+ *buf++ = ';';
+ }
+ else if (cs.swbreak_feature && target_stopped_by_sw_breakpoint ())
+ {
+ sprintf (buf, "swbreak:;");
+ buf += strlen (buf);
+ }
+ else if (cs.hwbreak_feature && target_stopped_by_hw_breakpoint ())
+ {
+ sprintf (buf, "hwbreak:;");
+ buf += strlen (buf);
+ }
+
+ while (*regp)
+ {
+ buf = outreg (regcache, find_regno (regcache->tdesc, *regp), buf);
+ regp ++;
+ }
+ *buf = '\0';
+
+ /* Formerly, if the debugger had not used any thread features
+ we would not burden it with a thread status response. This
+ was for the benefit of GDB 4.13 and older. However, in
+ recent GDB versions the check (``if (cont_thread != 0)'')
+ does not have the desired effect because of sillyness in
+ the way that the remote protocol handles specifying a
+ thread. Since thread support relies on qSymbol support
+ anyway, assume GDB can handle threads. */
+
+ if (using_threads && !disable_packet_Tthread)
+ {
+ /* This if (1) ought to be unnecessary. But remote_wait
+ in GDB will claim this event belongs to inferior_ptid
+ if we do not specify a thread, and there's no way for
+ gdbserver to know what inferior_ptid is. */
+ if (1 || cs.general_thread != ptid)
+ {
+ int core = -1;
+ /* In non-stop, don't change the general thread behind
+ GDB's back. */
+ if (!non_stop)
+ cs.general_thread = ptid;
+ sprintf (buf, "thread:");
+ buf += strlen (buf);
+ buf = write_ptid (buf, ptid);
+ strcat (buf, ";");
+ buf += strlen (buf);
+
+ core = target_core_of_thread (ptid);
+
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
+ }
+ }
+
+ if (dlls_changed)
+ {
+ strcpy (buf, "library:;");
+ buf += strlen (buf);
+ dlls_changed = 0;
+ }
+
+ current_thread = saved_thread;
+ }
+ break;
+ case TARGET_WAITKIND_EXITED:
+ if (cs.multi_process)
+ sprintf (buf, "W%x;process:%x",
+ status->value.integer, ptid.pid ());
+ else
+ sprintf (buf, "W%02x", status->value.integer);
+ break;
+ case TARGET_WAITKIND_SIGNALLED:
+ if (cs.multi_process)
+ sprintf (buf, "X%x;process:%x",
+ status->value.sig, ptid.pid ());
+ else
+ sprintf (buf, "X%02x", status->value.sig);
+ break;
+ case TARGET_WAITKIND_THREAD_EXITED:
+ sprintf (buf, "w%x;", status->value.integer);
+ buf += strlen (buf);
+ buf = write_ptid (buf, ptid);
+ break;
+ case TARGET_WAITKIND_NO_RESUMED:
+ sprintf (buf, "N");
+ break;
+ default:
+ error ("unhandled waitkind");
+ break;
+ }
+}
+
+void
+decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
+{
+ int i = 0, j = 0;
+ char ch;
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch = from[i++]) != ',')
+ {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ for (j = 0; j < 4; j++)
+ {
+ if ((ch = from[i++]) == 0)
+ break;
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= fromhex (ch) & 0x0f;
+ }
+}
+
+void
+decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
+ unsigned char **to_p)
+{
+ int i = 0;
+ char ch;
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch = from[i++]) != ',')
+ {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ while ((ch = from[i++]) != ':')
+ {
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ if (*to_p == NULL)
+ *to_p = (unsigned char *) xmalloc (*len_ptr);
+
+ hex2bin (&from[i++], *to_p, *len_ptr);
+}
+
+int
+decode_X_packet (char *from, int packet_len, CORE_ADDR *mem_addr_ptr,
+ unsigned int *len_ptr, unsigned char **to_p)
+{
+ int i = 0;
+ char ch;
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch = from[i++]) != ',')
+ {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ while ((ch = from[i++]) != ':')
+ {
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ if (*to_p == NULL)
+ *to_p = (unsigned char *) xmalloc (*len_ptr);
+
+ if (remote_unescape_input ((const gdb_byte *) &from[i], packet_len - i,
+ *to_p, *len_ptr) != *len_ptr)
+ return -1;
+
+ return 0;
+}
+
+/* Decode a qXfer write request. */
+
+int
+decode_xfer_write (char *buf, int packet_len, CORE_ADDR *offset,
+ unsigned int *len, unsigned char *data)
+{
+ char ch;
+ char *b = buf;
+
+ /* Extract the offset. */
+ *offset = 0;
+ while ((ch = *buf++) != ':')
+ {
+ *offset = *offset << 4;
+ *offset |= fromhex (ch) & 0x0f;
+ }
+
+ /* Get encoded data. */
+ packet_len -= buf - b;
+ *len = remote_unescape_input ((const gdb_byte *) buf, packet_len,
+ data, packet_len);
+ return 0;
+}
+
+/* Decode the parameters of a qSearch:memory packet. */
+
+int
+decode_search_memory_packet (const char *buf, int packet_len,
+ CORE_ADDR *start_addrp,
+ CORE_ADDR *search_space_lenp,
+ gdb_byte *pattern, unsigned int *pattern_lenp)
+{
+ const char *p = buf;
+
+ p = decode_address_to_semicolon (start_addrp, p);
+ p = decode_address_to_semicolon (search_space_lenp, p);
+ packet_len -= p - buf;
+ *pattern_lenp = remote_unescape_input ((const gdb_byte *) p, packet_len,
+ pattern, packet_len);
+ return 0;
+}
+
+static void
+free_sym_cache (struct sym_cache *sym)
+{
+ if (sym != NULL)
+ {
+ free (sym->name);
+ free (sym);
+ }
+}
+
+void
+clear_symbol_cache (struct sym_cache **symcache_p)
+{
+ struct sym_cache *sym, *next;
+
+ /* Check the cache first. */
+ for (sym = *symcache_p; sym; sym = next)
+ {
+ next = sym->next;
+ free_sym_cache (sym);
+ }
+
+ *symcache_p = NULL;
+}
+
+/* Get the address of NAME, and return it in ADDRP if found. if
+ MAY_ASK_GDB is false, assume symbol cache misses are failures.
+ Returns 1 if the symbol is found, 0 if it is not, -1 on error. */
+
+int
+look_up_one_symbol (const char *name, CORE_ADDR *addrp, int may_ask_gdb)
+{
+ client_state &cs = get_client_state ();
+ char *p, *q;
+ int len;
+ struct sym_cache *sym;
+ struct process_info *proc;
+
+ proc = current_process ();
+
+ /* Check the cache first. */
+ for (sym = proc->symbol_cache; sym; sym = sym->next)
+ if (strcmp (name, sym->name) == 0)
+ {
+ *addrp = sym->addr;
+ return 1;
+ }
+
+ /* It might not be an appropriate time to look up a symbol,
+ e.g. while we're trying to fetch registers. */
+ if (!may_ask_gdb)
+ return 0;
+
+ /* Send the request. */
+ strcpy (cs.own_buf, "qSymbol:");
+ bin2hex ((const gdb_byte *) name, cs.own_buf + strlen ("qSymbol:"),
+ strlen (name));
+ if (putpkt (cs.own_buf) < 0)
+ return -1;
+
+ /* FIXME: Eventually add buffer overflow checking (to getpkt?) */
+ len = getpkt (cs.own_buf);
+ if (len < 0)
+ return -1;
+
+ /* We ought to handle pretty much any packet at this point while we
+ wait for the qSymbol "response". That requires re-entering the
+ main loop. For now, this is an adequate approximation; allow
+ GDB to read from memory and handle 'v' packets (for vFile transfers)
+ while it figures out the address of the symbol. */
+ while (1)
+ {
+ if (cs.own_buf[0] == 'm')
+ {
+ CORE_ADDR mem_addr;
+ unsigned char *mem_buf;
+ unsigned int mem_len;
+
+ decode_m_packet (&cs.own_buf[1], &mem_addr, &mem_len);
+ mem_buf = (unsigned char *) xmalloc (mem_len);
+ if (read_inferior_memory (mem_addr, mem_buf, mem_len) == 0)
+ bin2hex (mem_buf, cs.own_buf, mem_len);
+ else
+ write_enn (cs.own_buf);
+ free (mem_buf);
+ if (putpkt (cs.own_buf) < 0)
+ return -1;
+ }
+ else if (cs.own_buf[0] == 'v')
+ {
+ int new_len = -1;
+ handle_v_requests (cs.own_buf, len, &new_len);
+ if (new_len != -1)
+ putpkt_binary (cs.own_buf, new_len);
+ else
+ putpkt (cs.own_buf);
+ }
+ else
+ break;
+ len = getpkt (cs.own_buf);
+ if (len < 0)
+ return -1;
+ }
+
+ if (!startswith (cs.own_buf, "qSymbol:"))
+ {
+ warning ("Malformed response to qSymbol, ignoring: %s", cs.own_buf);
+ return -1;
+ }
+
+ p = cs.own_buf + strlen ("qSymbol:");
+ q = p;
+ while (*q && *q != ':')
+ q++;
+
+ /* Make sure we found a value for the symbol. */
+ if (p == q || *q == '\0')
+ return 0;
+
+ decode_address (addrp, p, q - p);
+
+ /* Save the symbol in our cache. */
+ sym = XNEW (struct sym_cache);
+ sym->name = xstrdup (name);
+ sym->addr = *addrp;
+ sym->next = proc->symbol_cache;
+ proc->symbol_cache = sym;
+
+ return 1;
+}
+
+/* Relocate an instruction to execute at a different address. OLDLOC
+ is the address in the inferior memory where the instruction to
+ relocate is currently at. On input, TO points to the destination
+ where we want the instruction to be copied (and possibly adjusted)
+ to. On output, it points to one past the end of the resulting
+ instruction(s). The effect of executing the instruction at TO
+ shall be the same as if executing it at OLDLOC. For example, call
+ instructions that implicitly push the return address on the stack
+ should be adjusted to return to the instruction after OLDLOC;
+ relative branches, and other PC-relative instructions need the
+ offset adjusted; etc. Returns 0 on success, -1 on failure. */
+
+int
+relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
+{
+ client_state &cs = get_client_state ();
+ int len;
+ ULONGEST written = 0;
+
+ /* Send the request. */
+ sprintf (cs.own_buf, "qRelocInsn:%s;%s", paddress (oldloc),
+ paddress (*to));
+ if (putpkt (cs.own_buf) < 0)
+ return -1;
+
+ /* FIXME: Eventually add buffer overflow checking (to getpkt?) */
+ len = getpkt (cs.own_buf);
+ if (len < 0)
+ return -1;
+
+ /* We ought to handle pretty much any packet at this point while we
+ wait for the qRelocInsn "response". That requires re-entering
+ the main loop. For now, this is an adequate approximation; allow
+ GDB to access memory. */
+ while (cs.own_buf[0] == 'm' || cs.own_buf[0] == 'M' || cs.own_buf[0] == 'X')
+ {
+ CORE_ADDR mem_addr;
+ unsigned char *mem_buf = NULL;
+ unsigned int mem_len;
+
+ if (cs.own_buf[0] == 'm')
+ {
+ decode_m_packet (&cs.own_buf[1], &mem_addr, &mem_len);
+ mem_buf = (unsigned char *) xmalloc (mem_len);
+ if (read_inferior_memory (mem_addr, mem_buf, mem_len) == 0)
+ bin2hex (mem_buf, cs.own_buf, mem_len);
+ else
+ write_enn (cs.own_buf);
+ }
+ else if (cs.own_buf[0] == 'X')
+ {
+ if (decode_X_packet (&cs.own_buf[1], len - 1, &mem_addr,
+ &mem_len, &mem_buf) < 0
+ || target_write_memory (mem_addr, mem_buf, mem_len) != 0)
+ write_enn (cs.own_buf);
+ else
+ write_ok (cs.own_buf);
+ }
+ else
+ {
+ decode_M_packet (&cs.own_buf[1], &mem_addr, &mem_len, &mem_buf);
+ if (target_write_memory (mem_addr, mem_buf, mem_len) == 0)
+ write_ok (cs.own_buf);
+ else
+ write_enn (cs.own_buf);
+ }
+ free (mem_buf);
+ if (putpkt (cs.own_buf) < 0)
+ return -1;
+ len = getpkt (cs.own_buf);
+ if (len < 0)
+ return -1;
+ }
+
+ if (cs.own_buf[0] == 'E')
+ {
+ warning ("An error occurred while relocating an instruction: %s",
+ cs.own_buf);
+ return -1;
+ }
+
+ if (!startswith (cs.own_buf, "qRelocInsn:"))
+ {
+ warning ("Malformed response to qRelocInsn, ignoring: %s",
+ cs.own_buf);
+ return -1;
+ }
+
+ unpack_varlen_hex (cs.own_buf + strlen ("qRelocInsn:"), &written);
+
+ *to += written;
+ return 0;
+}
+
+void
+monitor_output (const char *msg)
+{
+ int len = strlen (msg);
+ char *buf = (char *) xmalloc (len * 2 + 2);
+
+ buf[0] = 'O';
+ bin2hex ((const gdb_byte *) msg, buf + 1, len);
+
+ putpkt (buf);
+ free (buf);
+}
+
+#endif
+++ /dev/null
-/* Main code for remote server for GDB.
- Copyright (C) 1989-2020 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 "gdbthread.h"
-#include "gdbsupport/agent.h"
-#include "notif.h"
-#include "tdesc.h"
-#include "gdbsupport/rsp-low.h"
-#include "gdbsupport/signals-state-save-restore.h"
-#include <ctype.h>
-#include <unistd.h>
-#if HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#include "gdbsupport/gdb_vecs.h"
-#include "gdbsupport/gdb_wait.h"
-#include "gdbsupport/btrace-common.h"
-#include "gdbsupport/filestuff.h"
-#include "tracepoint.h"
-#include "dll.h"
-#include "hostio.h"
-#include <vector>
-#include "gdbsupport/common-inferior.h"
-#include "gdbsupport/job-control.h"
-#include "gdbsupport/environ.h"
-#include "filenames.h"
-#include "gdbsupport/pathstuff.h"
-#ifdef USE_XML
-#include "xml-builtin.h"
-#endif
-
-#include "gdbsupport/selftest.h"
-#include "gdbsupport/scope-exit.h"
-
-#define require_running_or_return(BUF) \
- if (!target_running ()) \
- { \
- write_enn (BUF); \
- return; \
- }
-
-#define require_running_or_break(BUF) \
- if (!target_running ()) \
- { \
- write_enn (BUF); \
- break; \
- }
-
-/* String containing the current directory (what getwd would return). */
-
-char *current_directory;
-
-/* The environment to pass to the inferior when creating it. */
-
-static gdb_environ our_environ;
-
-bool server_waiting;
-
-static bool extended_protocol;
-static bool response_needed;
-static bool exit_requested;
-
-/* --once: Exit after the first connection has closed. */
-bool run_once;
-
-/* Whether to report TARGET_WAITKIND_NO_RESUMED events. */
-static bool report_no_resumed;
-
-bool non_stop;
-
-static struct {
- /* Set the PROGRAM_PATH. Here we adjust the path of the provided
- binary if needed. */
- void set (gdb::unique_xmalloc_ptr<char> &&path)
- {
- m_path = std::move (path);
-
- /* Make sure we're using the absolute path of the inferior when
- creating it. */
- if (!contains_dir_separator (m_path.get ()))
- {
- int reg_file_errno;
-
- /* Check if the file is in our CWD. If it is, then we prefix
- its name with CURRENT_DIRECTORY. Otherwise, we leave the
- name as-is because we'll try searching for it in $PATH. */
- if (is_regular_file (m_path.get (), ®_file_errno))
- m_path = gdb_abspath (m_path.get ());
- }
- }
-
- /* Return the PROGRAM_PATH. */
- char *get ()
- { return m_path.get (); }
-
-private:
- /* The program name, adjusted if needed. */
- gdb::unique_xmalloc_ptr<char> m_path;
-} program_path;
-static std::vector<char *> program_args;
-static std::string wrapper_argv;
-
-/* The PID of the originally created or attached inferior. Used to
- send signals to the process when GDB sends us an asynchronous interrupt
- (user hitting Control-C in the client), and to wait for the child to exit
- when no longer debugging it. */
-
-unsigned long signal_pid;
-
-/* Set if you want to disable optional thread related packets support
- in gdbserver, for the sake of testing GDB against stubs that don't
- support them. */
-bool disable_packet_vCont;
-bool disable_packet_Tthread;
-bool disable_packet_qC;
-bool disable_packet_qfThreadInfo;
-
-static unsigned char *mem_buf;
-
-/* A sub-class of 'struct notif_event' for stop, holding information
- relative to a single stop reply. We keep a queue of these to
- push to GDB in non-stop mode. */
-
-struct vstop_notif : public notif_event
-{
- /* Thread or process that got the event. */
- ptid_t ptid;
-
- /* Event info. */
- struct target_waitstatus status;
-};
-
-/* The current btrace configuration. This is gdbserver's mirror of GDB's
- btrace configuration. */
-static struct btrace_config current_btrace_conf;
-
-/* The client remote protocol state. */
-
-static client_state g_client_state;
-
-client_state &
-get_client_state ()
-{
- client_state &cs = g_client_state;
- return cs;
-}
-
-
-/* Put a stop reply to the stop reply queue. */
-
-static void
-queue_stop_reply (ptid_t ptid, struct target_waitstatus *status)
-{
- struct vstop_notif *new_notif = new struct vstop_notif;
-
- new_notif->ptid = ptid;
- new_notif->status = *status;
-
- notif_event_enque (¬if_stop, new_notif);
-}
-
-static bool
-remove_all_on_match_ptid (struct notif_event *event, ptid_t filter_ptid)
-{
- struct vstop_notif *vstop_event = (struct vstop_notif *) event;
-
- return vstop_event->ptid.matches (filter_ptid);
-}
-
-/* See server.h. */
-
-void
-discard_queued_stop_replies (ptid_t ptid)
-{
- std::list<notif_event *>::iterator iter, next, end;
- end = notif_stop.queue.end ();
- for (iter = notif_stop.queue.begin (); iter != end; iter = next)
- {
- next = iter;
- ++next;
-
- if (remove_all_on_match_ptid (*iter, ptid))
- {
- delete *iter;
- notif_stop.queue.erase (iter);
- }
- }
-}
-
-static void
-vstop_notif_reply (struct notif_event *event, char *own_buf)
-{
- struct vstop_notif *vstop = (struct vstop_notif *) event;
-
- prepare_resume_reply (own_buf, vstop->ptid, &vstop->status);
-}
-
-/* Helper for in_queued_stop_replies. */
-
-static bool
-in_queued_stop_replies_ptid (struct notif_event *event, ptid_t filter_ptid)
-{
- struct vstop_notif *vstop_event = (struct vstop_notif *) event;
-
- if (vstop_event->ptid.matches (filter_ptid))
- return true;
-
- /* Don't resume fork children that GDB does not know about yet. */
- if ((vstop_event->status.kind == TARGET_WAITKIND_FORKED
- || vstop_event->status.kind == TARGET_WAITKIND_VFORKED)
- && vstop_event->status.value.related_pid.matches (filter_ptid))
- return true;
-
- return false;
-}
-
-/* See server.h. */
-
-int
-in_queued_stop_replies (ptid_t ptid)
-{
- for (notif_event *event : notif_stop.queue)
- {
- if (in_queued_stop_replies_ptid (event, ptid))
- return true;
- }
-
- return false;
-}
-
-struct notif_server notif_stop =
-{
- "vStopped", "Stop", {}, vstop_notif_reply,
-};
-
-static int
-target_running (void)
-{
- return get_first_thread () != NULL;
-}
-
-/* See gdbsupport/common-inferior.h. */
-
-const char *
-get_exec_wrapper ()
-{
- return !wrapper_argv.empty () ? wrapper_argv.c_str () : NULL;
-}
-
-/* See gdbsupport/common-inferior.h. */
-
-const char *
-get_exec_file (int err)
-{
- if (err && program_path.get () == NULL)
- error (_("No executable file specified."));
-
- return program_path.get ();
-}
-
-/* See server.h. */
-
-gdb_environ *
-get_environ ()
-{
- return &our_environ;
-}
-
-static int
-attach_inferior (int pid)
-{
- client_state &cs = get_client_state ();
- /* myattach should return -1 if attaching is unsupported,
- 0 if it succeeded, and call error() otherwise. */
-
- if (find_process_pid (pid) != nullptr)
- error ("Already attached to process %d\n", pid);
-
- if (myattach (pid) != 0)
- return -1;
-
- fprintf (stderr, "Attached; pid = %d\n", pid);
- fflush (stderr);
-
- /* FIXME - It may be that we should get the SIGNAL_PID from the
- attach function, so that it can be the main thread instead of
- whichever we were told to attach to. */
- signal_pid = pid;
-
- if (!non_stop)
- {
- cs.last_ptid = mywait (ptid_t (pid), &cs.last_status, 0, 0);
-
- /* GDB knows to ignore the first SIGSTOP after attaching to a running
- process using the "attach" command, but this is different; it's
- just using "target remote". Pretend it's just starting up. */
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED
- && cs.last_status.value.sig == GDB_SIGNAL_STOP)
- cs.last_status.value.sig = GDB_SIGNAL_TRAP;
-
- current_thread->last_resume_kind = resume_stop;
- current_thread->last_status = cs.last_status;
- }
-
- return 0;
-}
-
-/* Decode a qXfer read request. Return 0 if everything looks OK,
- or -1 otherwise. */
-
-static int
-decode_xfer_read (char *buf, CORE_ADDR *ofs, unsigned int *len)
-{
- /* After the read marker and annex, qXfer looks like a
- traditional 'm' packet. */
- decode_m_packet (buf, ofs, len);
-
- return 0;
-}
-
-static int
-decode_xfer (char *buf, char **object, char **rw, char **annex, char **offset)
-{
- /* Extract and NUL-terminate the object. */
- *object = buf;
- while (*buf && *buf != ':')
- buf++;
- if (*buf == '\0')
- return -1;
- *buf++ = 0;
-
- /* Extract and NUL-terminate the read/write action. */
- *rw = buf;
- while (*buf && *buf != ':')
- buf++;
- if (*buf == '\0')
- return -1;
- *buf++ = 0;
-
- /* Extract and NUL-terminate the annex. */
- *annex = buf;
- while (*buf && *buf != ':')
- buf++;
- if (*buf == '\0')
- return -1;
- *buf++ = 0;
-
- *offset = buf;
- return 0;
-}
-
-/* Write the response to a successful qXfer read. Returns the
- length of the (binary) data stored in BUF, corresponding
- to as much of DATA/LEN as we could fit. IS_MORE controls
- the first character of the response. */
-static int
-write_qxfer_response (char *buf, const gdb_byte *data, int len, int is_more)
-{
- int out_len;
-
- if (is_more)
- buf[0] = 'm';
- else
- buf[0] = 'l';
-
- return remote_escape_output (data, len, 1, (unsigned char *) buf + 1,
- &out_len, PBUFSIZ - 2) + 1;
-}
-
-/* Handle btrace enabling in BTS format. */
-
-static void
-handle_btrace_enable_bts (struct thread_info *thread)
-{
- if (thread->btrace != NULL)
- error (_("Btrace already enabled."));
-
- current_btrace_conf.format = BTRACE_FORMAT_BTS;
- thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf);
-}
-
-/* Handle btrace enabling in Intel Processor Trace format. */
-
-static void
-handle_btrace_enable_pt (struct thread_info *thread)
-{
- if (thread->btrace != NULL)
- error (_("Btrace already enabled."));
-
- current_btrace_conf.format = BTRACE_FORMAT_PT;
- thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf);
-}
-
-/* Handle btrace disabling. */
-
-static void
-handle_btrace_disable (struct thread_info *thread)
-{
-
- if (thread->btrace == NULL)
- error (_("Branch tracing not enabled."));
-
- if (target_disable_btrace (thread->btrace) != 0)
- error (_("Could not disable branch tracing."));
-
- thread->btrace = NULL;
-}
-
-/* Handle the "Qbtrace" packet. */
-
-static int
-handle_btrace_general_set (char *own_buf)
-{
- client_state &cs = get_client_state ();
- struct thread_info *thread;
- char *op;
-
- if (!startswith (own_buf, "Qbtrace:"))
- return 0;
-
- op = own_buf + strlen ("Qbtrace:");
-
- if (cs.general_thread == null_ptid
- || cs.general_thread == minus_one_ptid)
- {
- strcpy (own_buf, "E.Must select a single thread.");
- return -1;
- }
-
- thread = find_thread_ptid (cs.general_thread);
- if (thread == NULL)
- {
- strcpy (own_buf, "E.No such thread.");
- return -1;
- }
-
- try
- {
- if (strcmp (op, "bts") == 0)
- handle_btrace_enable_bts (thread);
- else if (strcmp (op, "pt") == 0)
- handle_btrace_enable_pt (thread);
- else if (strcmp (op, "off") == 0)
- handle_btrace_disable (thread);
- else
- error (_("Bad Qbtrace operation. Use bts, pt, or off."));
-
- write_ok (own_buf);
- }
- catch (const gdb_exception_error &exception)
- {
- sprintf (own_buf, "E.%s", exception.what ());
- }
-
- return 1;
-}
-
-/* Handle the "Qbtrace-conf" packet. */
-
-static int
-handle_btrace_conf_general_set (char *own_buf)
-{
- client_state &cs = get_client_state ();
- struct thread_info *thread;
- char *op;
-
- if (!startswith (own_buf, "Qbtrace-conf:"))
- return 0;
-
- op = own_buf + strlen ("Qbtrace-conf:");
-
- if (cs.general_thread == null_ptid
- || cs.general_thread == minus_one_ptid)
- {
- strcpy (own_buf, "E.Must select a single thread.");
- return -1;
- }
-
- thread = find_thread_ptid (cs.general_thread);
- if (thread == NULL)
- {
- strcpy (own_buf, "E.No such thread.");
- return -1;
- }
-
- if (startswith (op, "bts:size="))
- {
- unsigned long size;
- char *endp = NULL;
-
- errno = 0;
- size = strtoul (op + strlen ("bts:size="), &endp, 16);
- if (endp == NULL || *endp != 0 || errno != 0 || size > UINT_MAX)
- {
- strcpy (own_buf, "E.Bad size value.");
- return -1;
- }
-
- current_btrace_conf.bts.size = (unsigned int) size;
- }
- else if (strncmp (op, "pt:size=", strlen ("pt:size=")) == 0)
- {
- unsigned long size;
- char *endp = NULL;
-
- errno = 0;
- size = strtoul (op + strlen ("pt:size="), &endp, 16);
- if (endp == NULL || *endp != 0 || errno != 0 || size > UINT_MAX)
- {
- strcpy (own_buf, "E.Bad size value.");
- return -1;
- }
-
- current_btrace_conf.pt.size = (unsigned int) size;
- }
- else
- {
- strcpy (own_buf, "E.Bad Qbtrace configuration option.");
- return -1;
- }
-
- write_ok (own_buf);
- return 1;
-}
-
-/* Handle all of the extended 'Q' packets. */
-
-static void
-handle_general_set (char *own_buf)
-{
- client_state &cs = get_client_state ();
- if (startswith (own_buf, "QPassSignals:"))
- {
- int numsigs = (int) GDB_SIGNAL_LAST, i;
- const char *p = own_buf + strlen ("QPassSignals:");
- CORE_ADDR cursig;
-
- p = decode_address_to_semicolon (&cursig, p);
- for (i = 0; i < numsigs; i++)
- {
- if (i == cursig)
- {
- cs.pass_signals[i] = 1;
- if (*p == '\0')
- /* Keep looping, to clear the remaining signals. */
- cursig = -1;
- else
- p = decode_address_to_semicolon (&cursig, p);
- }
- else
- cs.pass_signals[i] = 0;
- }
- strcpy (own_buf, "OK");
- return;
- }
-
- if (startswith (own_buf, "QProgramSignals:"))
- {
- int numsigs = (int) GDB_SIGNAL_LAST, i;
- const char *p = own_buf + strlen ("QProgramSignals:");
- CORE_ADDR cursig;
-
- cs.program_signals_p = 1;
-
- p = decode_address_to_semicolon (&cursig, p);
- for (i = 0; i < numsigs; i++)
- {
- if (i == cursig)
- {
- cs.program_signals[i] = 1;
- if (*p == '\0')
- /* Keep looping, to clear the remaining signals. */
- cursig = -1;
- else
- p = decode_address_to_semicolon (&cursig, p);
- }
- else
- cs.program_signals[i] = 0;
- }
- strcpy (own_buf, "OK");
- return;
- }
-
- if (startswith (own_buf, "QCatchSyscalls:"))
- {
- const char *p = own_buf + sizeof ("QCatchSyscalls:") - 1;
- int enabled = -1;
- CORE_ADDR sysno;
- struct process_info *process;
-
- if (!target_running () || !target_supports_catch_syscall ())
- {
- write_enn (own_buf);
- return;
- }
-
- if (strcmp (p, "0") == 0)
- enabled = 0;
- else if (p[0] == '1' && (p[1] == ';' || p[1] == '\0'))
- enabled = 1;
- else
- {
- fprintf (stderr, "Unknown catch-syscalls mode requested: %s\n",
- own_buf);
- write_enn (own_buf);
- return;
- }
-
- process = current_process ();
- process->syscalls_to_catch.clear ();
-
- if (enabled)
- {
- p += 1;
- if (*p == ';')
- {
- p += 1;
- while (*p != '\0')
- {
- p = decode_address_to_semicolon (&sysno, p);
- process->syscalls_to_catch.push_back (sysno);
- }
- }
- else
- process->syscalls_to_catch.push_back (ANY_SYSCALL);
- }
-
- write_ok (own_buf);
- return;
- }
-
- if (strcmp (own_buf, "QEnvironmentReset") == 0)
- {
- our_environ = gdb_environ::from_host_environ ();
-
- write_ok (own_buf);
- return;
- }
-
- if (startswith (own_buf, "QEnvironmentHexEncoded:"))
- {
- const char *p = own_buf + sizeof ("QEnvironmentHexEncoded:") - 1;
- /* The final form of the environment variable. FINAL_VAR will
- hold the 'VAR=VALUE' format. */
- std::string final_var = hex2str (p);
- std::string var_name, var_value;
-
- if (remote_debug)
- {
- debug_printf (_("[QEnvironmentHexEncoded received '%s']\n"), p);
- debug_printf (_("[Environment variable to be set: '%s']\n"),
- final_var.c_str ());
- debug_flush ();
- }
-
- size_t pos = final_var.find ('=');
- if (pos == std::string::npos)
- {
- warning (_("Unexpected format for environment variable: '%s'"),
- final_var.c_str ());
- write_enn (own_buf);
- return;
- }
-
- var_name = final_var.substr (0, pos);
- var_value = final_var.substr (pos + 1, std::string::npos);
-
- our_environ.set (var_name.c_str (), var_value.c_str ());
-
- write_ok (own_buf);
- return;
- }
-
- if (startswith (own_buf, "QEnvironmentUnset:"))
- {
- const char *p = own_buf + sizeof ("QEnvironmentUnset:") - 1;
- std::string varname = hex2str (p);
-
- if (remote_debug)
- {
- debug_printf (_("[QEnvironmentUnset received '%s']\n"), p);
- debug_printf (_("[Environment variable to be unset: '%s']\n"),
- varname.c_str ());
- debug_flush ();
- }
-
- our_environ.unset (varname.c_str ());
-
- write_ok (own_buf);
- return;
- }
-
- if (strcmp (own_buf, "QStartNoAckMode") == 0)
- {
- if (remote_debug)
- {
- debug_printf ("[noack mode enabled]\n");
- debug_flush ();
- }
-
- cs.noack_mode = 1;
- write_ok (own_buf);
- return;
- }
-
- if (startswith (own_buf, "QNonStop:"))
- {
- char *mode = own_buf + 9;
- int req = -1;
- const char *req_str;
-
- if (strcmp (mode, "0") == 0)
- req = 0;
- else if (strcmp (mode, "1") == 0)
- req = 1;
- else
- {
- /* We don't know what this mode is, so complain to
- GDB. */
- fprintf (stderr, "Unknown non-stop mode requested: %s\n",
- own_buf);
- write_enn (own_buf);
- return;
- }
-
- req_str = req ? "non-stop" : "all-stop";
- if (start_non_stop (req) != 0)
- {
- fprintf (stderr, "Setting %s mode failed\n", req_str);
- write_enn (own_buf);
- return;
- }
-
- non_stop = (req != 0);
-
- if (remote_debug)
- debug_printf ("[%s mode enabled]\n", req_str);
-
- write_ok (own_buf);
- return;
- }
-
- if (startswith (own_buf, "QDisableRandomization:"))
- {
- char *packet = own_buf + strlen ("QDisableRandomization:");
- ULONGEST setting;
-
- unpack_varlen_hex (packet, &setting);
- cs.disable_randomization = setting;
-
- if (remote_debug)
- {
- debug_printf (cs.disable_randomization
- ? "[address space randomization disabled]\n"
- : "[address space randomization enabled]\n");
- }
-
- write_ok (own_buf);
- return;
- }
-
- if (target_supports_tracepoints ()
- && handle_tracepoint_general_set (own_buf))
- return;
-
- if (startswith (own_buf, "QAgent:"))
- {
- char *mode = own_buf + strlen ("QAgent:");
- int req = 0;
-
- if (strcmp (mode, "0") == 0)
- req = 0;
- else if (strcmp (mode, "1") == 0)
- req = 1;
- else
- {
- /* We don't know what this value is, so complain to GDB. */
- sprintf (own_buf, "E.Unknown QAgent value");
- return;
- }
-
- /* Update the flag. */
- use_agent = req;
- if (remote_debug)
- debug_printf ("[%s agent]\n", req ? "Enable" : "Disable");
- write_ok (own_buf);
- return;
- }
-
- if (handle_btrace_general_set (own_buf))
- return;
-
- if (handle_btrace_conf_general_set (own_buf))
- return;
-
- if (startswith (own_buf, "QThreadEvents:"))
- {
- char *mode = own_buf + strlen ("QThreadEvents:");
- enum tribool req = TRIBOOL_UNKNOWN;
-
- if (strcmp (mode, "0") == 0)
- req = TRIBOOL_FALSE;
- else if (strcmp (mode, "1") == 0)
- req = TRIBOOL_TRUE;
- else
- {
- /* We don't know what this mode is, so complain to GDB. */
- sprintf (own_buf, "E.Unknown thread-events mode requested: %s\n",
- mode);
- return;
- }
-
- cs.report_thread_events = (req == TRIBOOL_TRUE);
-
- if (remote_debug)
- {
- const char *req_str = cs.report_thread_events ? "enabled" : "disabled";
-
- debug_printf ("[thread events are now %s]\n", req_str);
- }
-
- write_ok (own_buf);
- return;
- }
-
- if (startswith (own_buf, "QStartupWithShell:"))
- {
- const char *value = own_buf + strlen ("QStartupWithShell:");
-
- if (strcmp (value, "1") == 0)
- startup_with_shell = true;
- else if (strcmp (value, "0") == 0)
- startup_with_shell = false;
- else
- {
- /* Unknown value. */
- fprintf (stderr, "Unknown value to startup-with-shell: %s\n",
- own_buf);
- write_enn (own_buf);
- return;
- }
-
- if (remote_debug)
- debug_printf (_("[Inferior will %s started with shell]"),
- startup_with_shell ? "be" : "not be");
-
- write_ok (own_buf);
- return;
- }
-
- if (startswith (own_buf, "QSetWorkingDir:"))
- {
- const char *p = own_buf + strlen ("QSetWorkingDir:");
-
- if (*p != '\0')
- {
- std::string path = hex2str (p);
-
- set_inferior_cwd (path.c_str ());
-
- if (remote_debug)
- debug_printf (_("[Set the inferior's current directory to %s]\n"),
- path.c_str ());
- }
- else
- {
- /* An empty argument means that we should clear out any
- previously set cwd for the inferior. */
- set_inferior_cwd (NULL);
-
- if (remote_debug)
- debug_printf (_("\
-[Unset the inferior's current directory; will use gdbserver's cwd]\n"));
- }
- write_ok (own_buf);
-
- return;
- }
-
- /* Otherwise we didn't know what packet it was. Say we didn't
- understand it. */
- own_buf[0] = 0;
-}
-
-static const char *
-get_features_xml (const char *annex)
-{
- const struct target_desc *desc = current_target_desc ();
-
- /* `desc->xmltarget' defines what to return when looking for the
- "target.xml" file. Its contents can either be verbatim XML code
- (prefixed with a '@') or else the name of the actual XML file to
- be used in place of "target.xml".
-
- This variable is set up from the auto-generated
- init_registers_... routine for the current target. */
-
- if (strcmp (annex, "target.xml") == 0)
- {
- const char *ret = tdesc_get_features_xml (desc);
-
- if (*ret == '@')
- return ret + 1;
- else
- annex = ret;
- }
-
-#ifdef USE_XML
- {
- int i;
-
- /* Look for the annex. */
- for (i = 0; xml_builtin[i][0] != NULL; i++)
- if (strcmp (annex, xml_builtin[i][0]) == 0)
- break;
-
- if (xml_builtin[i][0] != NULL)
- return xml_builtin[i][1];
- }
-#endif
-
- return NULL;
-}
-
-static void
-monitor_show_help (void)
-{
- monitor_output ("The following monitor commands are supported:\n");
- monitor_output (" set debug <0|1>\n");
- monitor_output (" Enable general debugging messages\n");
- monitor_output (" set debug-hw-points <0|1>\n");
- monitor_output (" Enable h/w breakpoint/watchpoint debugging messages\n");
- monitor_output (" set remote-debug <0|1>\n");
- monitor_output (" Enable remote protocol debugging messages\n");
- monitor_output (" set debug-format option1[,option2,...]\n");
- monitor_output (" Add additional information to debugging messages\n");
- monitor_output (" Options: all, none");
- monitor_output (", timestamp");
- monitor_output ("\n");
- monitor_output (" exit\n");
- monitor_output (" Quit GDBserver\n");
-}
-
-/* Read trace frame or inferior memory. Returns the number of bytes
- actually read, zero when no further transfer is possible, and -1 on
- error. Return of a positive value smaller than LEN does not
- indicate there's no more to be read, only the end of the transfer.
- E.g., when GDB reads memory from a traceframe, a first request may
- be served from a memory block that does not cover the whole request
- length. A following request gets the rest served from either
- another block (of the same traceframe) or from the read-only
- regions. */
-
-static int
-gdb_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- client_state &cs = get_client_state ();
- int res;
-
- if (cs.current_traceframe >= 0)
- {
- ULONGEST nbytes;
- ULONGEST length = len;
-
- if (traceframe_read_mem (cs.current_traceframe,
- memaddr, myaddr, len, &nbytes))
- return -1;
- /* Data read from trace buffer, we're done. */
- if (nbytes > 0)
- return nbytes;
- if (!in_readonly_region (memaddr, length))
- return -1;
- /* Otherwise we have a valid readonly case, fall through. */
- /* (assume no half-trace half-real blocks for now) */
- }
-
- res = prepare_to_access_memory ();
- if (res == 0)
- {
- if (set_desired_thread ())
- res = read_inferior_memory (memaddr, myaddr, len);
- else
- res = 1;
- done_accessing_memory ();
-
- return res == 0 ? len : -1;
- }
- else
- return -1;
-}
-
-/* Write trace frame or inferior memory. Actually, writing to trace
- frames is forbidden. */
-
-static int
-gdb_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
-{
- client_state &cs = get_client_state ();
- if (cs.current_traceframe >= 0)
- return EIO;
- else
- {
- int ret;
-
- ret = prepare_to_access_memory ();
- if (ret == 0)
- {
- if (set_desired_thread ())
- ret = target_write_memory (memaddr, myaddr, len);
- else
- ret = EIO;
- done_accessing_memory ();
- }
- return ret;
- }
-}
-
-/* Subroutine of handle_search_memory to simplify it. */
-
-static int
-handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
- gdb_byte *pattern, unsigned pattern_len,
- gdb_byte *search_buf,
- unsigned chunk_size, unsigned search_buf_size,
- CORE_ADDR *found_addrp)
-{
- /* Prime the search buffer. */
-
- if (gdb_read_memory (start_addr, search_buf, search_buf_size)
- != search_buf_size)
- {
- warning ("Unable to access %ld bytes of target "
- "memory at 0x%lx, halting search.",
- (long) search_buf_size, (long) start_addr);
- return -1;
- }
-
- /* Perform the search.
-
- The loop is kept simple by allocating [N + pattern-length - 1] bytes.
- When we've scanned N bytes we copy the trailing bytes to the start and
- read in another N bytes. */
-
- while (search_space_len >= pattern_len)
- {
- gdb_byte *found_ptr;
- unsigned nr_search_bytes = (search_space_len < search_buf_size
- ? search_space_len
- : search_buf_size);
-
- found_ptr = (gdb_byte *) memmem (search_buf, nr_search_bytes, pattern,
- pattern_len);
-
- if (found_ptr != NULL)
- {
- CORE_ADDR found_addr = start_addr + (found_ptr - search_buf);
- *found_addrp = found_addr;
- return 1;
- }
-
- /* Not found in this chunk, skip to next chunk. */
-
- /* Don't let search_space_len wrap here, it's unsigned. */
- if (search_space_len >= chunk_size)
- search_space_len -= chunk_size;
- else
- search_space_len = 0;
-
- if (search_space_len >= pattern_len)
- {
- unsigned keep_len = search_buf_size - chunk_size;
- CORE_ADDR read_addr = start_addr + chunk_size + keep_len;
- int nr_to_read;
-
- /* Copy the trailing part of the previous iteration to the front
- of the buffer for the next iteration. */
- memcpy (search_buf, search_buf + chunk_size, keep_len);
-
- nr_to_read = (search_space_len - keep_len < chunk_size
- ? search_space_len - keep_len
- : chunk_size);
-
- if (gdb_read_memory (read_addr, search_buf + keep_len,
- nr_to_read) != search_buf_size)
- {
- warning ("Unable to access %ld bytes of target memory "
- "at 0x%lx, halting search.",
- (long) nr_to_read, (long) read_addr);
- return -1;
- }
-
- start_addr += chunk_size;
- }
- }
-
- /* Not found. */
-
- return 0;
-}
-
-/* Handle qSearch:memory packets. */
-
-static void
-handle_search_memory (char *own_buf, int packet_len)
-{
- CORE_ADDR start_addr;
- CORE_ADDR search_space_len;
- gdb_byte *pattern;
- unsigned int pattern_len;
- /* NOTE: also defined in find.c testcase. */
-#define SEARCH_CHUNK_SIZE 16000
- const unsigned chunk_size = SEARCH_CHUNK_SIZE;
- /* Buffer to hold memory contents for searching. */
- gdb_byte *search_buf;
- unsigned search_buf_size;
- int found;
- CORE_ADDR found_addr;
- int cmd_name_len = sizeof ("qSearch:memory:") - 1;
-
- pattern = (gdb_byte *) malloc (packet_len);
- if (pattern == NULL)
- {
- error ("Unable to allocate memory to perform the search");
- strcpy (own_buf, "E00");
- return;
- }
- if (decode_search_memory_packet (own_buf + cmd_name_len,
- packet_len - cmd_name_len,
- &start_addr, &search_space_len,
- pattern, &pattern_len) < 0)
- {
- free (pattern);
- error ("Error in parsing qSearch:memory packet");
- strcpy (own_buf, "E00");
- return;
- }
-
- search_buf_size = chunk_size + pattern_len - 1;
-
- /* No point in trying to allocate a buffer larger than the search space. */
- if (search_space_len < search_buf_size)
- search_buf_size = search_space_len;
-
- search_buf = (gdb_byte *) malloc (search_buf_size);
- if (search_buf == NULL)
- {
- free (pattern);
- error ("Unable to allocate memory to perform the search");
- strcpy (own_buf, "E00");
- return;
- }
-
- found = handle_search_memory_1 (start_addr, search_space_len,
- pattern, pattern_len,
- search_buf, chunk_size, search_buf_size,
- &found_addr);
-
- if (found > 0)
- sprintf (own_buf, "1,%lx", (long) found_addr);
- else if (found == 0)
- strcpy (own_buf, "0");
- else
- strcpy (own_buf, "E00");
-
- free (search_buf);
- free (pattern);
-}
-
-/* Handle the "D" packet. */
-
-static void
-handle_detach (char *own_buf)
-{
- client_state &cs = get_client_state ();
-
- process_info *process;
-
- if (cs.multi_process)
- {
- /* skip 'D;' */
- int pid = strtol (&own_buf[2], NULL, 16);
-
- process = find_process_pid (pid);
- }
- else
- {
- process = (current_thread != nullptr
- ? get_thread_process (current_thread)
- : nullptr);
- }
-
- if (process == NULL)
- {
- write_enn (own_buf);
- return;
- }
-
- if ((tracing && disconnected_tracing) || any_persistent_commands (process))
- {
- if (tracing && disconnected_tracing)
- fprintf (stderr,
- "Disconnected tracing in effect, "
- "leaving gdbserver attached to the process\n");
-
- if (any_persistent_commands (process))
- fprintf (stderr,
- "Persistent commands are present, "
- "leaving gdbserver attached to the process\n");
-
- /* Make sure we're in non-stop/async mode, so we we can both
- wait for an async socket accept, and handle async target
- events simultaneously. There's also no point either in
- having the target stop all threads, when we're going to
- pass signals down without informing GDB. */
- if (!non_stop)
- {
- if (debug_threads)
- debug_printf ("Forcing non-stop mode\n");
-
- non_stop = true;
- start_non_stop (1);
- }
-
- process->gdb_detached = 1;
-
- /* Detaching implicitly resumes all threads. */
- target_continue_no_signal (minus_one_ptid);
-
- write_ok (own_buf);
- return;
- }
-
- fprintf (stderr, "Detaching from process %d\n", process->pid);
- stop_tracing ();
-
- /* We'll need this after PROCESS has been destroyed. */
- int pid = process->pid;
-
- if (detach_inferior (process) != 0)
- write_enn (own_buf);
- else
- {
- discard_queued_stop_replies (ptid_t (pid));
- write_ok (own_buf);
-
- if (extended_protocol || target_running ())
- {
- /* There is still at least one inferior remaining or
- we are in extended mode, so don't terminate gdbserver,
- and instead treat this like a normal program exit. */
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.integer = 0;
- cs.last_ptid = ptid_t (pid);
-
- current_thread = NULL;
- }
- else
- {
- putpkt (own_buf);
- remote_close ();
-
- /* If we are attached, then we can exit. Otherwise, we
- need to hang around doing nothing, until the child is
- gone. */
- join_inferior (pid);
- exit (0);
- }
- }
-}
-
-/* Parse options to --debug-format= and "monitor set debug-format".
- ARG is the text after "--debug-format=" or "monitor set debug-format".
- IS_MONITOR is non-zero if we're invoked via "monitor set debug-format".
- This triggers calls to monitor_output.
- The result is an empty string if all options were parsed ok, otherwise an
- error message which the caller must free.
-
- N.B. These commands affect all debug format settings, they are not
- cumulative. If a format is not specified, it is turned off.
- However, we don't go to extra trouble with things like
- "monitor set debug-format all,none,timestamp".
- Instead we just parse them one at a time, in order.
-
- The syntax for "monitor set debug" we support here is not identical
- to gdb's "set debug foo on|off" because we also use this function to
- parse "--debug-format=foo,bar". */
-
-static std::string
-parse_debug_format_options (const char *arg, int is_monitor)
-{
- /* First turn all debug format options off. */
- debug_timestamp = 0;
-
- /* First remove leading spaces, for "monitor set debug-format". */
- while (isspace (*arg))
- ++arg;
-
- std::vector<gdb::unique_xmalloc_ptr<char>> options
- = delim_string_to_char_ptr_vec (arg, ',');
-
- for (const gdb::unique_xmalloc_ptr<char> &option : options)
- {
- if (strcmp (option.get (), "all") == 0)
- {
- debug_timestamp = 1;
- if (is_monitor)
- monitor_output ("All extra debug format options enabled.\n");
- }
- else if (strcmp (option.get (), "none") == 0)
- {
- debug_timestamp = 0;
- if (is_monitor)
- monitor_output ("All extra debug format options disabled.\n");
- }
- else if (strcmp (option.get (), "timestamp") == 0)
- {
- debug_timestamp = 1;
- if (is_monitor)
- monitor_output ("Timestamps will be added to debug output.\n");
- }
- else if (*option == '\0')
- {
- /* An empty option, e.g., "--debug-format=foo,,bar", is ignored. */
- continue;
- }
- else
- return string_printf ("Unknown debug-format argument: \"%s\"\n",
- option.get ());
- }
-
- return std::string ();
-}
-
-/* Handle monitor commands not handled by target-specific handlers. */
-
-static void
-handle_monitor_command (char *mon, char *own_buf)
-{
- if (strcmp (mon, "set debug 1") == 0)
- {
- debug_threads = 1;
- monitor_output ("Debug output enabled.\n");
- }
- else if (strcmp (mon, "set debug 0") == 0)
- {
- debug_threads = 0;
- monitor_output ("Debug output disabled.\n");
- }
- else if (strcmp (mon, "set debug-hw-points 1") == 0)
- {
- show_debug_regs = 1;
- monitor_output ("H/W point debugging output enabled.\n");
- }
- else if (strcmp (mon, "set debug-hw-points 0") == 0)
- {
- show_debug_regs = 0;
- monitor_output ("H/W point debugging output disabled.\n");
- }
- else if (strcmp (mon, "set remote-debug 1") == 0)
- {
- remote_debug = 1;
- monitor_output ("Protocol debug output enabled.\n");
- }
- else if (strcmp (mon, "set remote-debug 0") == 0)
- {
- remote_debug = 0;
- monitor_output ("Protocol debug output disabled.\n");
- }
- else if (startswith (mon, "set debug-format "))
- {
- std::string error_msg
- = parse_debug_format_options (mon + sizeof ("set debug-format ") - 1,
- 1);
-
- if (!error_msg.empty ())
- {
- monitor_output (error_msg.c_str ());
- monitor_show_help ();
- write_enn (own_buf);
- }
- }
- else if (strcmp (mon, "set debug-file") == 0)
- debug_set_output (nullptr);
- else if (startswith (mon, "set debug-file "))
- debug_set_output (mon + sizeof ("set debug-file ") - 1);
- else if (strcmp (mon, "help") == 0)
- monitor_show_help ();
- else if (strcmp (mon, "exit") == 0)
- exit_requested = true;
- else
- {
- monitor_output ("Unknown monitor command.\n\n");
- monitor_show_help ();
- write_enn (own_buf);
- }
-}
-
-/* Associates a callback with each supported qXfer'able object. */
-
-struct qxfer
-{
- /* The object this handler handles. */
- const char *object;
-
- /* Request that the target transfer up to LEN 8-bit bytes of the
- target's OBJECT. The OFFSET, for a seekable object, specifies
- the starting point. The ANNEX can be used to provide additional
- data-specific information to the target.
-
- Return the number of bytes actually transfered, zero when no
- further transfer is possible, -1 on error, -2 when the transfer
- is not supported, and -3 on a verbose error message that should
- be preserved. Return of a positive value smaller than LEN does
- not indicate the end of the object, only the end of the transfer.
-
- One, and only one, of readbuf or writebuf must be non-NULL. */
- int (*xfer) (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len);
-};
-
-/* Handle qXfer:auxv:read. */
-
-static int
-handle_qxfer_auxv (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- if (the_target->read_auxv == NULL || writebuf != NULL)
- return -2;
-
- if (annex[0] != '\0' || current_thread == NULL)
- return -1;
-
- return (*the_target->read_auxv) (offset, readbuf, len);
-}
-
-/* Handle qXfer:exec-file:read. */
-
-static int
-handle_qxfer_exec_file (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- char *file;
- ULONGEST pid;
- int total_len;
-
- if (the_target->pid_to_exec_file == NULL || writebuf != NULL)
- return -2;
-
- if (annex[0] == '\0')
- {
- if (current_thread == NULL)
- return -1;
-
- pid = pid_of (current_thread);
- }
- else
- {
- annex = unpack_varlen_hex (annex, &pid);
- if (annex[0] != '\0')
- return -1;
- }
-
- if (pid <= 0)
- return -1;
-
- file = (*the_target->pid_to_exec_file) (pid);
- if (file == NULL)
- return -1;
-
- total_len = strlen (file);
-
- if (offset > total_len)
- return -1;
-
- if (offset + len > total_len)
- len = total_len - offset;
-
- memcpy (readbuf, file + offset, len);
- return len;
-}
-
-/* Handle qXfer:features:read. */
-
-static int
-handle_qxfer_features (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- const char *document;
- size_t total_len;
-
- if (writebuf != NULL)
- return -2;
-
- if (!target_running ())
- return -1;
-
- /* Grab the correct annex. */
- document = get_features_xml (annex);
- if (document == NULL)
- return -1;
-
- total_len = strlen (document);
-
- if (offset > total_len)
- return -1;
-
- if (offset + len > total_len)
- len = total_len - offset;
-
- memcpy (readbuf, document + offset, len);
- return len;
-}
-
-/* Handle qXfer:libraries:read. */
-
-static int
-handle_qxfer_libraries (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- if (writebuf != NULL)
- return -2;
-
- if (annex[0] != '\0' || current_thread == NULL)
- return -1;
-
- std::string document = "<library-list version=\"1.0\">\n";
-
- for (const dll_info &dll : all_dlls)
- document += string_printf
- (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
- dll.name.c_str (), paddress (dll.base_addr));
-
- document += "</library-list>\n";
-
- if (offset > document.length ())
- return -1;
-
- if (offset + len > document.length ())
- len = document.length () - offset;
-
- memcpy (readbuf, &document[offset], len);
-
- return len;
-}
-
-/* Handle qXfer:libraries-svr4:read. */
-
-static int
-handle_qxfer_libraries_svr4 (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- if (writebuf != NULL)
- return -2;
-
- if (current_thread == NULL || the_target->qxfer_libraries_svr4 == NULL)
- return -1;
-
- return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len);
-}
-
-/* Handle qXfer:osadata:read. */
-
-static int
-handle_qxfer_osdata (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- if (the_target->qxfer_osdata == NULL || writebuf != NULL)
- return -2;
-
- return (*the_target->qxfer_osdata) (annex, readbuf, NULL, offset, len);
-}
-
-/* Handle qXfer:siginfo:read and qXfer:siginfo:write. */
-
-static int
-handle_qxfer_siginfo (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- if (the_target->qxfer_siginfo == NULL)
- return -2;
-
- if (annex[0] != '\0' || current_thread == NULL)
- return -1;
-
- return (*the_target->qxfer_siginfo) (annex, readbuf, writebuf, offset, len);
-}
-
-/* Handle qXfer:statictrace:read. */
-
-static int
-handle_qxfer_statictrace (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- client_state &cs = get_client_state ();
- ULONGEST nbytes;
-
- if (writebuf != NULL)
- return -2;
-
- if (annex[0] != '\0' || current_thread == NULL
- || cs.current_traceframe == -1)
- return -1;
-
- if (traceframe_read_sdata (cs.current_traceframe, offset,
- readbuf, len, &nbytes))
- return -1;
- return nbytes;
-}
-
-/* Helper for handle_qxfer_threads_proper.
- Emit the XML to describe the thread of INF. */
-
-static void
-handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
-{
- ptid_t ptid = ptid_of (thread);
- char ptid_s[100];
- int core = target_core_of_thread (ptid);
- char core_s[21];
- const char *name = target_thread_name (ptid);
- int handle_len;
- gdb_byte *handle;
- bool handle_status = target_thread_handle (ptid, &handle, &handle_len);
-
- write_ptid (ptid_s, ptid);
-
- buffer_xml_printf (buffer, "<thread id=\"%s\"", ptid_s);
-
- if (core != -1)
- {
- sprintf (core_s, "%d", core);
- buffer_xml_printf (buffer, " core=\"%s\"", core_s);
- }
-
- if (name != NULL)
- buffer_xml_printf (buffer, " name=\"%s\"", name);
-
- if (handle_status)
- {
- char *handle_s = (char *) alloca (handle_len * 2 + 1);
- bin2hex (handle, handle_s, handle_len);
- buffer_xml_printf (buffer, " handle=\"%s\"", handle_s);
- }
-
- buffer_xml_printf (buffer, "/>\n");
-}
-
-/* Helper for handle_qxfer_threads. */
-
-static void
-handle_qxfer_threads_proper (struct buffer *buffer)
-{
- buffer_grow_str (buffer, "<threads>\n");
-
- for_each_thread ([&] (thread_info *thread)
- {
- handle_qxfer_threads_worker (thread, buffer);
- });
-
- buffer_grow_str0 (buffer, "</threads>\n");
-}
-
-/* Handle qXfer:threads:read. */
-
-static int
-handle_qxfer_threads (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- static char *result = 0;
- static unsigned int result_length = 0;
-
- if (writebuf != NULL)
- return -2;
-
- if (annex[0] != '\0')
- return -1;
-
- if (offset == 0)
- {
- struct buffer buffer;
- /* When asked for data at offset 0, generate everything and store into
- 'result'. Successive reads will be served off 'result'. */
- if (result)
- free (result);
-
- buffer_init (&buffer);
-
- handle_qxfer_threads_proper (&buffer);
-
- result = buffer_finish (&buffer);
- result_length = strlen (result);
- buffer_free (&buffer);
- }
-
- if (offset >= result_length)
- {
- /* We're out of data. */
- free (result);
- result = NULL;
- result_length = 0;
- return 0;
- }
-
- if (len > result_length - offset)
- len = result_length - offset;
-
- memcpy (readbuf, result + offset, len);
-
- return len;
-}
-
-/* Handle qXfer:traceframe-info:read. */
-
-static int
-handle_qxfer_traceframe_info (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- client_state &cs = get_client_state ();
- static char *result = 0;
- static unsigned int result_length = 0;
-
- if (writebuf != NULL)
- return -2;
-
- if (!target_running () || annex[0] != '\0' || cs.current_traceframe == -1)
- return -1;
-
- if (offset == 0)
- {
- struct buffer buffer;
-
- /* When asked for data at offset 0, generate everything and
- store into 'result'. Successive reads will be served off
- 'result'. */
- free (result);
-
- buffer_init (&buffer);
-
- traceframe_read_info (cs.current_traceframe, &buffer);
-
- result = buffer_finish (&buffer);
- result_length = strlen (result);
- buffer_free (&buffer);
- }
-
- if (offset >= result_length)
- {
- /* We're out of data. */
- free (result);
- result = NULL;
- result_length = 0;
- return 0;
- }
-
- if (len > result_length - offset)
- len = result_length - offset;
-
- memcpy (readbuf, result + offset, len);
- return len;
-}
-
-/* Handle qXfer:fdpic:read. */
-
-static int
-handle_qxfer_fdpic (const char *annex, gdb_byte *readbuf,
- const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
-{
- if (the_target->read_loadmap == NULL)
- return -2;
-
- if (current_thread == NULL)
- return -1;
-
- return (*the_target->read_loadmap) (annex, offset, readbuf, len);
-}
-
-/* Handle qXfer:btrace:read. */
-
-static int
-handle_qxfer_btrace (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- client_state &cs = get_client_state ();
- static struct buffer cache;
- struct thread_info *thread;
- enum btrace_read_type type;
- int result;
-
- if (writebuf != NULL)
- return -2;
-
- if (cs.general_thread == null_ptid
- || cs.general_thread == minus_one_ptid)
- {
- strcpy (cs.own_buf, "E.Must select a single thread.");
- return -3;
- }
-
- thread = find_thread_ptid (cs.general_thread);
- if (thread == NULL)
- {
- strcpy (cs.own_buf, "E.No such thread.");
- return -3;
- }
-
- if (thread->btrace == NULL)
- {
- strcpy (cs.own_buf, "E.Btrace not enabled.");
- return -3;
- }
-
- if (strcmp (annex, "all") == 0)
- type = BTRACE_READ_ALL;
- else if (strcmp (annex, "new") == 0)
- type = BTRACE_READ_NEW;
- else if (strcmp (annex, "delta") == 0)
- type = BTRACE_READ_DELTA;
- else
- {
- strcpy (cs.own_buf, "E.Bad annex.");
- return -3;
- }
-
- if (offset == 0)
- {
- buffer_free (&cache);
-
- try
- {
- result = target_read_btrace (thread->btrace, &cache, type);
- if (result != 0)
- memcpy (cs.own_buf, cache.buffer, cache.used_size);
- }
- catch (const gdb_exception_error &exception)
- {
- sprintf (cs.own_buf, "E.%s", exception.what ());
- result = -1;
- }
-
- if (result != 0)
- return -3;
- }
- else if (offset > cache.used_size)
- {
- buffer_free (&cache);
- return -3;
- }
-
- if (len > cache.used_size - offset)
- len = cache.used_size - offset;
-
- memcpy (readbuf, cache.buffer + offset, len);
-
- return len;
-}
-
-/* Handle qXfer:btrace-conf:read. */
-
-static int
-handle_qxfer_btrace_conf (const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
-{
- client_state &cs = get_client_state ();
- static struct buffer cache;
- struct thread_info *thread;
- int result;
-
- if (writebuf != NULL)
- return -2;
-
- if (annex[0] != '\0')
- return -1;
-
- if (cs.general_thread == null_ptid
- || cs.general_thread == minus_one_ptid)
- {
- strcpy (cs.own_buf, "E.Must select a single thread.");
- return -3;
- }
-
- thread = find_thread_ptid (cs.general_thread);
- if (thread == NULL)
- {
- strcpy (cs.own_buf, "E.No such thread.");
- return -3;
- }
-
- if (thread->btrace == NULL)
- {
- strcpy (cs.own_buf, "E.Btrace not enabled.");
- return -3;
- }
-
- if (offset == 0)
- {
- buffer_free (&cache);
-
- try
- {
- result = target_read_btrace_conf (thread->btrace, &cache);
- if (result != 0)
- memcpy (cs.own_buf, cache.buffer, cache.used_size);
- }
- catch (const gdb_exception_error &exception)
- {
- sprintf (cs.own_buf, "E.%s", exception.what ());
- result = -1;
- }
-
- if (result != 0)
- return -3;
- }
- else if (offset > cache.used_size)
- {
- buffer_free (&cache);
- return -3;
- }
-
- if (len > cache.used_size - offset)
- len = cache.used_size - offset;
-
- memcpy (readbuf, cache.buffer + offset, len);
-
- return len;
-}
-
-static const struct qxfer qxfer_packets[] =
- {
- { "auxv", handle_qxfer_auxv },
- { "btrace", handle_qxfer_btrace },
- { "btrace-conf", handle_qxfer_btrace_conf },
- { "exec-file", handle_qxfer_exec_file},
- { "fdpic", handle_qxfer_fdpic},
- { "features", handle_qxfer_features },
- { "libraries", handle_qxfer_libraries },
- { "libraries-svr4", handle_qxfer_libraries_svr4 },
- { "osdata", handle_qxfer_osdata },
- { "siginfo", handle_qxfer_siginfo },
- { "statictrace", handle_qxfer_statictrace },
- { "threads", handle_qxfer_threads },
- { "traceframe-info", handle_qxfer_traceframe_info },
- };
-
-static int
-handle_qxfer (char *own_buf, int packet_len, int *new_packet_len_p)
-{
- int i;
- char *object;
- char *rw;
- char *annex;
- char *offset;
-
- if (!startswith (own_buf, "qXfer:"))
- return 0;
-
- /* Grab the object, r/w and annex. */
- if (decode_xfer (own_buf + 6, &object, &rw, &annex, &offset) < 0)
- {
- write_enn (own_buf);
- return 1;
- }
-
- for (i = 0;
- i < sizeof (qxfer_packets) / sizeof (qxfer_packets[0]);
- i++)
- {
- const struct qxfer *q = &qxfer_packets[i];
-
- if (strcmp (object, q->object) == 0)
- {
- if (strcmp (rw, "read") == 0)
- {
- unsigned char *data;
- int n;
- CORE_ADDR ofs;
- unsigned int len;
-
- /* Grab the offset and length. */
- if (decode_xfer_read (offset, &ofs, &len) < 0)
- {
- write_enn (own_buf);
- return 1;
- }
-
- /* Read one extra byte, as an indicator of whether there is
- more. */
- if (len > PBUFSIZ - 2)
- len = PBUFSIZ - 2;
- data = (unsigned char *) malloc (len + 1);
- if (data == NULL)
- {
- write_enn (own_buf);
- return 1;
- }
- n = (*q->xfer) (annex, data, NULL, ofs, len + 1);
- if (n == -2)
- {
- free (data);
- return 0;
- }
- else if (n == -3)
- {
- /* Preserve error message. */
- }
- else if (n < 0)
- write_enn (own_buf);
- else if (n > len)
- *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
- else
- *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
-
- free (data);
- return 1;
- }
- else if (strcmp (rw, "write") == 0)
- {
- int n;
- unsigned int len;
- CORE_ADDR ofs;
- unsigned char *data;
-
- strcpy (own_buf, "E00");
- data = (unsigned char *) malloc (packet_len - (offset - own_buf));
- if (data == NULL)
- {
- write_enn (own_buf);
- return 1;
- }
- if (decode_xfer_write (offset, packet_len - (offset - own_buf),
- &ofs, &len, data) < 0)
- {
- free (data);
- write_enn (own_buf);
- return 1;
- }
-
- n = (*q->xfer) (annex, NULL, data, ofs, len);
- if (n == -2)
- {
- free (data);
- return 0;
- }
- else if (n == -3)
- {
- /* Preserve error message. */
- }
- else if (n < 0)
- write_enn (own_buf);
- else
- sprintf (own_buf, "%x", n);
-
- free (data);
- return 1;
- }
-
- return 0;
- }
- }
-
- return 0;
-}
-
-/* Compute 32 bit CRC from inferior memory.
-
- On success, return 32 bit CRC.
- On failure, return (unsigned long long) -1. */
-
-static unsigned long long
-crc32 (CORE_ADDR base, int len, unsigned int crc)
-{
- while (len--)
- {
- unsigned char byte = 0;
-
- /* Return failure if memory read fails. */
- if (read_inferior_memory (base, &byte, 1) != 0)
- return (unsigned long long) -1;
-
- crc = xcrc32 (&byte, 1, crc);
- base++;
- }
- return (unsigned long long) crc;
-}
-
-/* Add supported btrace packets to BUF. */
-
-static void
-supported_btrace_packets (char *buf)
-{
- strcat (buf, ";Qbtrace:bts+");
- strcat (buf, ";Qbtrace-conf:bts:size+");
- strcat (buf, ";Qbtrace:pt+");
- strcat (buf, ";Qbtrace-conf:pt:size+");
- strcat (buf, ";Qbtrace:off+");
- strcat (buf, ";qXfer:btrace:read+");
- strcat (buf, ";qXfer:btrace-conf:read+");
-}
-
-/* Handle all of the extended 'q' packets. */
-
-static void
-handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
-{
- client_state &cs = get_client_state ();
- static std::list<thread_info *>::const_iterator thread_iter;
-
- /* Reply the current thread id. */
- if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC)
- {
- ptid_t ptid;
- require_running_or_return (own_buf);
-
- if (cs.general_thread != null_ptid && cs.general_thread != minus_one_ptid)
- ptid = cs.general_thread;
- else
- {
- thread_iter = all_threads.begin ();
- ptid = (*thread_iter)->id;
- }
-
- sprintf (own_buf, "QC");
- own_buf += 2;
- write_ptid (own_buf, ptid);
- return;
- }
-
- if (strcmp ("qSymbol::", own_buf) == 0)
- {
- struct thread_info *save_thread = current_thread;
-
- /* For qSymbol, GDB only changes the current thread if the
- previous current thread was of a different process. So if
- the previous thread is gone, we need to pick another one of
- the same process. This can happen e.g., if we followed an
- exec in a non-leader thread. */
- if (current_thread == NULL)
- {
- current_thread
- = find_any_thread_of_pid (cs.general_thread.pid ());
-
- /* Just in case, if we didn't find a thread, then bail out
- instead of crashing. */
- if (current_thread == NULL)
- {
- write_enn (own_buf);
- current_thread = save_thread;
- return;
- }
- }
-
- /* GDB is suggesting new symbols have been loaded. This may
- mean a new shared library has been detected as loaded, so
- take the opportunity to check if breakpoints we think are
- inserted, still are. Note that it isn't guaranteed that
- we'll see this when a shared library is loaded, and nor will
- we see this for unloads (although breakpoints in unloaded
- libraries shouldn't trigger), as GDB may not find symbols for
- the library at all. We also re-validate breakpoints when we
- see a second GDB breakpoint for the same address, and or when
- we access breakpoint shadows. */
- validate_breakpoints ();
-
- if (target_supports_tracepoints ())
- tracepoint_look_up_symbols ();
-
- if (current_thread != NULL && the_target->look_up_symbols != NULL)
- (*the_target->look_up_symbols) ();
-
- current_thread = save_thread;
-
- strcpy (own_buf, "OK");
- return;
- }
-
- if (!disable_packet_qfThreadInfo)
- {
- if (strcmp ("qfThreadInfo", own_buf) == 0)
- {
- require_running_or_return (own_buf);
- thread_iter = all_threads.begin ();
-
- *own_buf++ = 'm';
- ptid_t ptid = (*thread_iter)->id;
- write_ptid (own_buf, ptid);
- thread_iter++;
- return;
- }
-
- if (strcmp ("qsThreadInfo", own_buf) == 0)
- {
- require_running_or_return (own_buf);
- if (thread_iter != all_threads.end ())
- {
- *own_buf++ = 'm';
- ptid_t ptid = (*thread_iter)->id;
- write_ptid (own_buf, ptid);
- thread_iter++;
- return;
- }
- else
- {
- sprintf (own_buf, "l");
- return;
- }
- }
- }
-
- if (the_target->read_offsets != NULL
- && strcmp ("qOffsets", own_buf) == 0)
- {
- CORE_ADDR text, data;
-
- require_running_or_return (own_buf);
- if (the_target->read_offsets (&text, &data))
- sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX",
- (long)text, (long)data, (long)data);
- else
- write_enn (own_buf);
-
- return;
- }
-
- /* Protocol features query. */
- if (startswith (own_buf, "qSupported")
- && (own_buf[10] == ':' || own_buf[10] == '\0'))
- {
- char *p = &own_buf[10];
- int gdb_supports_qRelocInsn = 0;
-
- /* Process each feature being provided by GDB. The first
- feature will follow a ':', and latter features will follow
- ';'. */
- if (*p == ':')
- {
- char **qsupported = NULL;
- int count = 0;
- int unknown = 0;
- int i;
-
- /* Two passes, to avoid nested strtok calls in
- target_process_qsupported. */
- char *saveptr;
- for (p = strtok_r (p + 1, ";", &saveptr);
- p != NULL;
- p = strtok_r (NULL, ";", &saveptr))
- {
- count++;
- qsupported = XRESIZEVEC (char *, qsupported, count);
- qsupported[count - 1] = xstrdup (p);
- }
-
- for (i = 0; i < count; i++)
- {
- p = qsupported[i];
- if (strcmp (p, "multiprocess+") == 0)
- {
- /* GDB supports and wants multi-process support if
- possible. */
- if (target_supports_multi_process ())
- cs.multi_process = 1;
- }
- else if (strcmp (p, "qRelocInsn+") == 0)
- {
- /* GDB supports relocate instruction requests. */
- gdb_supports_qRelocInsn = 1;
- }
- else if (strcmp (p, "swbreak+") == 0)
- {
- /* GDB wants us to report whether a trap is caused
- by a software breakpoint and for us to handle PC
- adjustment if necessary on this target. */
- if (target_supports_stopped_by_sw_breakpoint ())
- cs.swbreak_feature = 1;
- }
- else if (strcmp (p, "hwbreak+") == 0)
- {
- /* GDB wants us to report whether a trap is caused
- by a hardware breakpoint. */
- if (target_supports_stopped_by_hw_breakpoint ())
- cs.hwbreak_feature = 1;
- }
- else if (strcmp (p, "fork-events+") == 0)
- {
- /* GDB supports and wants fork events if possible. */
- if (target_supports_fork_events ())
- cs.report_fork_events = 1;
- }
- else if (strcmp (p, "vfork-events+") == 0)
- {
- /* GDB supports and wants vfork events if possible. */
- if (target_supports_vfork_events ())
- cs.report_vfork_events = 1;
- }
- else if (strcmp (p, "exec-events+") == 0)
- {
- /* GDB supports and wants exec events if possible. */
- if (target_supports_exec_events ())
- cs.report_exec_events = 1;
- }
- else if (strcmp (p, "vContSupported+") == 0)
- cs.vCont_supported = 1;
- else if (strcmp (p, "QThreadEvents+") == 0)
- ;
- else if (strcmp (p, "no-resumed+") == 0)
- {
- /* GDB supports and wants TARGET_WAITKIND_NO_RESUMED
- events. */
- report_no_resumed = true;
- }
- else
- {
- /* Move the unknown features all together. */
- qsupported[i] = NULL;
- qsupported[unknown] = p;
- unknown++;
- }
- }
-
- /* Give the target backend a chance to process the unknown
- features. */
- target_process_qsupported (qsupported, unknown);
-
- for (i = 0; i < count; i++)
- free (qsupported[i]);
- free (qsupported);
- }
-
- sprintf (own_buf,
- "PacketSize=%x;QPassSignals+;QProgramSignals+;"
- "QStartupWithShell+;QEnvironmentHexEncoded+;"
- "QEnvironmentReset+;QEnvironmentUnset+;"
- "QSetWorkingDir+",
- PBUFSIZ - 1);
-
- if (target_supports_catch_syscall ())
- strcat (own_buf, ";QCatchSyscalls+");
-
- if (the_target->qxfer_libraries_svr4 != NULL)
- strcat (own_buf, ";qXfer:libraries-svr4:read+"
- ";augmented-libraries-svr4-read+");
- else
- {
- /* We do not have any hook to indicate whether the non-SVR4 target
- backend supports qXfer:libraries:read, so always report it. */
- strcat (own_buf, ";qXfer:libraries:read+");
- }
-
- if (the_target->read_auxv != NULL)
- strcat (own_buf, ";qXfer:auxv:read+");
-
- if (the_target->qxfer_siginfo != NULL)
- strcat (own_buf, ";qXfer:siginfo:read+;qXfer:siginfo:write+");
-
- if (the_target->read_loadmap != NULL)
- strcat (own_buf, ";qXfer:fdpic:read+");
-
- /* We always report qXfer:features:read, as targets may
- install XML files on a subsequent call to arch_setup.
- If we reported to GDB on startup that we don't support
- qXfer:feature:read at all, we will never be re-queried. */
- strcat (own_buf, ";qXfer:features:read+");
-
- if (cs.transport_is_reliable)
- strcat (own_buf, ";QStartNoAckMode+");
-
- if (the_target->qxfer_osdata != NULL)
- strcat (own_buf, ";qXfer:osdata:read+");
-
- if (target_supports_multi_process ())
- strcat (own_buf, ";multiprocess+");
-
- if (target_supports_fork_events ())
- strcat (own_buf, ";fork-events+");
-
- if (target_supports_vfork_events ())
- strcat (own_buf, ";vfork-events+");
-
- if (target_supports_exec_events ())
- strcat (own_buf, ";exec-events+");
-
- if (target_supports_non_stop ())
- strcat (own_buf, ";QNonStop+");
-
- if (target_supports_disable_randomization ())
- strcat (own_buf, ";QDisableRandomization+");
-
- strcat (own_buf, ";qXfer:threads:read+");
-
- if (target_supports_tracepoints ())
- {
- strcat (own_buf, ";ConditionalTracepoints+");
- strcat (own_buf, ";TraceStateVariables+");
- strcat (own_buf, ";TracepointSource+");
- strcat (own_buf, ";DisconnectedTracing+");
- if (gdb_supports_qRelocInsn && target_supports_fast_tracepoints ())
- strcat (own_buf, ";FastTracepoints+");
- strcat (own_buf, ";StaticTracepoints+");
- strcat (own_buf, ";InstallInTrace+");
- strcat (own_buf, ";qXfer:statictrace:read+");
- strcat (own_buf, ";qXfer:traceframe-info:read+");
- strcat (own_buf, ";EnableDisableTracepoints+");
- strcat (own_buf, ";QTBuffer:size+");
- strcat (own_buf, ";tracenz+");
- }
-
- if (target_supports_hardware_single_step ()
- || target_supports_software_single_step () )
- {
- strcat (own_buf, ";ConditionalBreakpoints+");
- }
- strcat (own_buf, ";BreakpointCommands+");
-
- if (target_supports_agent ())
- strcat (own_buf, ";QAgent+");
-
- supported_btrace_packets (own_buf);
-
- if (target_supports_stopped_by_sw_breakpoint ())
- strcat (own_buf, ";swbreak+");
-
- if (target_supports_stopped_by_hw_breakpoint ())
- strcat (own_buf, ";hwbreak+");
-
- if (the_target->pid_to_exec_file != NULL)
- strcat (own_buf, ";qXfer:exec-file:read+");
-
- strcat (own_buf, ";vContSupported+");
-
- strcat (own_buf, ";QThreadEvents+");
-
- strcat (own_buf, ";no-resumed+");
-
- /* Reinitialize components as needed for the new connection. */
- hostio_handle_new_gdb_connection ();
- target_handle_new_gdb_connection ();
-
- return;
- }
-
- /* Thread-local storage support. */
- if (the_target->get_tls_address != NULL
- && startswith (own_buf, "qGetTLSAddr:"))
- {
- char *p = own_buf + 12;
- CORE_ADDR parts[2], address = 0;
- int i, err;
- ptid_t ptid = null_ptid;
-
- require_running_or_return (own_buf);
-
- for (i = 0; i < 3; i++)
- {
- char *p2;
- int len;
-
- if (p == NULL)
- break;
-
- p2 = strchr (p, ',');
- if (p2)
- {
- len = p2 - p;
- p2++;
- }
- else
- {
- len = strlen (p);
- p2 = NULL;
- }
-
- if (i == 0)
- ptid = read_ptid (p, NULL);
- else
- decode_address (&parts[i - 1], p, len);
- p = p2;
- }
-
- if (p != NULL || i < 3)
- err = 1;
- else
- {
- struct thread_info *thread = find_thread_ptid (ptid);
-
- if (thread == NULL)
- err = 2;
- else
- err = the_target->get_tls_address (thread, parts[0], parts[1],
- &address);
- }
-
- if (err == 0)
- {
- strcpy (own_buf, paddress(address));
- return;
- }
- else if (err > 0)
- {
- write_enn (own_buf);
- return;
- }
-
- /* Otherwise, pretend we do not understand this packet. */
- }
-
- /* Windows OS Thread Information Block address support. */
- if (the_target->get_tib_address != NULL
- && startswith (own_buf, "qGetTIBAddr:"))
- {
- const char *annex;
- int n;
- CORE_ADDR tlb;
- ptid_t ptid = read_ptid (own_buf + 12, &annex);
-
- n = (*the_target->get_tib_address) (ptid, &tlb);
- if (n == 1)
- {
- strcpy (own_buf, paddress(tlb));
- return;
- }
- else if (n == 0)
- {
- write_enn (own_buf);
- return;
- }
- return;
- }
-
- /* Handle "monitor" commands. */
- if (startswith (own_buf, "qRcmd,"))
- {
- char *mon = (char *) malloc (PBUFSIZ);
- int len = strlen (own_buf + 6);
-
- if (mon == NULL)
- {
- write_enn (own_buf);
- return;
- }
-
- if ((len % 2) != 0
- || hex2bin (own_buf + 6, (gdb_byte *) mon, len / 2) != len / 2)
- {
- write_enn (own_buf);
- free (mon);
- return;
- }
- mon[len / 2] = '\0';
-
- write_ok (own_buf);
-
- if (the_target->handle_monitor_command == NULL
- || (*the_target->handle_monitor_command) (mon) == 0)
- /* Default processing. */
- handle_monitor_command (mon, own_buf);
-
- free (mon);
- return;
- }
-
- if (startswith (own_buf, "qSearch:memory:"))
- {
- require_running_or_return (own_buf);
- handle_search_memory (own_buf, packet_len);
- return;
- }
-
- if (strcmp (own_buf, "qAttached") == 0
- || startswith (own_buf, "qAttached:"))
- {
- struct process_info *process;
-
- if (own_buf[sizeof ("qAttached") - 1])
- {
- int pid = strtoul (own_buf + sizeof ("qAttached:") - 1, NULL, 16);
- process = find_process_pid (pid);
- }
- else
- {
- require_running_or_return (own_buf);
- process = current_process ();
- }
-
- if (process == NULL)
- {
- write_enn (own_buf);
- return;
- }
-
- strcpy (own_buf, process->attached ? "1" : "0");
- return;
- }
-
- if (startswith (own_buf, "qCRC:"))
- {
- /* CRC check (compare-section). */
- const char *comma;
- ULONGEST base;
- int len;
- unsigned long long crc;
-
- require_running_or_return (own_buf);
- comma = unpack_varlen_hex (own_buf + 5, &base);
- if (*comma++ != ',')
- {
- write_enn (own_buf);
- return;
- }
- len = strtoul (comma, NULL, 16);
- crc = crc32 (base, len, 0xffffffff);
- /* Check for memory failure. */
- if (crc == (unsigned long long) -1)
- {
- write_enn (own_buf);
- return;
- }
- sprintf (own_buf, "C%lx", (unsigned long) crc);
- return;
- }
-
- if (handle_qxfer (own_buf, packet_len, new_packet_len_p))
- return;
-
- if (target_supports_tracepoints () && handle_tracepoint_query (own_buf))
- return;
-
- /* Otherwise we didn't know what packet it was. Say we didn't
- understand it. */
- own_buf[0] = 0;
-}
-
-static void gdb_wants_all_threads_stopped (void);
-static void resume (struct thread_resume *actions, size_t n);
-
-/* The callback that is passed to visit_actioned_threads. */
-typedef int (visit_actioned_threads_callback_ftype)
- (const struct thread_resume *, struct thread_info *);
-
-/* Call CALLBACK for any thread to which ACTIONS applies to. Returns
- true if CALLBACK returns true. Returns false if no matching thread
- is found or CALLBACK results false.
- Note: This function is itself a callback for find_thread. */
-
-static bool
-visit_actioned_threads (thread_info *thread,
- const struct thread_resume *actions,
- size_t num_actions,
- visit_actioned_threads_callback_ftype *callback)
-{
- for (size_t i = 0; i < num_actions; i++)
- {
- const struct thread_resume *action = &actions[i];
-
- if (action->thread == minus_one_ptid
- || action->thread == thread->id
- || ((action->thread.pid ()
- == thread->id.pid ())
- && action->thread.lwp () == -1))
- {
- if ((*callback) (action, thread))
- return true;
- }
- }
-
- return false;
-}
-
-/* Callback for visit_actioned_threads. If the thread has a pending
- status to report, report it now. */
-
-static int
-handle_pending_status (const struct thread_resume *resumption,
- struct thread_info *thread)
-{
- client_state &cs = get_client_state ();
- if (thread->status_pending_p)
- {
- thread->status_pending_p = 0;
-
- cs.last_status = thread->last_status;
- cs.last_ptid = thread->id;
- prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status);
- return 1;
- }
- return 0;
-}
-
-/* Parse vCont packets. */
-static void
-handle_v_cont (char *own_buf)
-{
- const char *p;
- int n = 0, i = 0;
- struct thread_resume *resume_info;
- struct thread_resume default_action { null_ptid };
-
- /* Count the number of semicolons in the packet. There should be one
- for every action. */
- p = &own_buf[5];
- while (p)
- {
- n++;
- p++;
- p = strchr (p, ';');
- }
-
- resume_info = (struct thread_resume *) malloc (n * sizeof (resume_info[0]));
- if (resume_info == NULL)
- goto err;
-
- p = &own_buf[5];
- while (*p)
- {
- p++;
-
- memset (&resume_info[i], 0, sizeof resume_info[i]);
-
- if (p[0] == 's' || p[0] == 'S')
- resume_info[i].kind = resume_step;
- else if (p[0] == 'r')
- resume_info[i].kind = resume_step;
- else if (p[0] == 'c' || p[0] == 'C')
- resume_info[i].kind = resume_continue;
- else if (p[0] == 't')
- resume_info[i].kind = resume_stop;
- else
- goto err;
-
- if (p[0] == 'S' || p[0] == 'C')
- {
- char *q;
- int sig = strtol (p + 1, &q, 16);
- if (p == q)
- goto err;
- p = q;
-
- if (!gdb_signal_to_host_p ((enum gdb_signal) sig))
- goto err;
- resume_info[i].sig = gdb_signal_to_host ((enum gdb_signal) sig);
- }
- else if (p[0] == 'r')
- {
- ULONGEST addr;
-
- p = unpack_varlen_hex (p + 1, &addr);
- resume_info[i].step_range_start = addr;
-
- if (*p != ',')
- goto err;
-
- p = unpack_varlen_hex (p + 1, &addr);
- resume_info[i].step_range_end = addr;
- }
- else
- {
- p = p + 1;
- }
-
- if (p[0] == 0)
- {
- resume_info[i].thread = minus_one_ptid;
- default_action = resume_info[i];
-
- /* Note: we don't increment i here, we'll overwrite this entry
- the next time through. */
- }
- else if (p[0] == ':')
- {
- const char *q;
- ptid_t ptid = read_ptid (p + 1, &q);
-
- if (p == q)
- goto err;
- p = q;
- if (p[0] != ';' && p[0] != 0)
- goto err;
-
- resume_info[i].thread = ptid;
-
- i++;
- }
- }
-
- if (i < n)
- resume_info[i] = default_action;
-
- resume (resume_info, n);
- free (resume_info);
- return;
-
-err:
- write_enn (own_buf);
- free (resume_info);
- return;
-}
-
-/* Resume target with ACTIONS, an array of NUM_ACTIONS elements. */
-
-static void
-resume (struct thread_resume *actions, size_t num_actions)
-{
- client_state &cs = get_client_state ();
- if (!non_stop)
- {
- /* Check if among the threads that GDB wants actioned, there's
- one with a pending status to report. If so, skip actually
- resuming/stopping and report the pending event
- immediately. */
-
- thread_info *thread_with_status = find_thread ([&] (thread_info *thread)
- {
- return visit_actioned_threads (thread, actions, num_actions,
- handle_pending_status);
- });
-
- if (thread_with_status != NULL)
- return;
-
- enable_async_io ();
- }
-
- (*the_target->resume) (actions, num_actions);
-
- if (non_stop)
- write_ok (cs.own_buf);
- else
- {
- cs.last_ptid = mywait (minus_one_ptid, &cs.last_status, 0, 1);
-
- if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED
- && !report_no_resumed)
- {
- /* The client does not support this stop reply. At least
- return error. */
- sprintf (cs.own_buf, "E.No unwaited-for children left.");
- disable_async_io ();
- return;
- }
-
- if (cs.last_status.kind != TARGET_WAITKIND_EXITED
- && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED
- && cs.last_status.kind != TARGET_WAITKIND_NO_RESUMED)
- current_thread->last_status = cs.last_status;
-
- /* From the client's perspective, all-stop mode always stops all
- threads implicitly (and the target backend has already done
- so by now). Tag all threads as "want-stopped", so we don't
- resume them implicitly without the client telling us to. */
- gdb_wants_all_threads_stopped ();
- prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status);
- disable_async_io ();
-
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
- target_mourn_inferior (cs.last_ptid);
- }
-}
-
-/* Attach to a new program. Return 1 if successful, 0 if failure. */
-static int
-handle_v_attach (char *own_buf)
-{
- client_state &cs = get_client_state ();
- int pid;
-
- pid = strtol (own_buf + 8, NULL, 16);
- if (pid != 0 && attach_inferior (pid) == 0)
- {
- /* Don't report shared library events after attaching, even if
- some libraries are preloaded. GDB will always poll the
- library list. Avoids the "stopped by shared library event"
- notice on the GDB side. */
- dlls_changed = 0;
-
- if (non_stop)
- {
- /* In non-stop, we don't send a resume reply. Stop events
- will follow up using the normal notification
- mechanism. */
- write_ok (own_buf);
- }
- else
- prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status);
-
- return 1;
- }
- else
- {
- write_enn (own_buf);
- return 0;
- }
-}
-
-/* Run a new program. Return 1 if successful, 0 if failure. */
-static int
-handle_v_run (char *own_buf)
-{
- client_state &cs = get_client_state ();
- char *p, *next_p;
- std::vector<char *> new_argv;
- char *new_program_name = NULL;
- int i, new_argc;
-
- new_argc = 0;
- for (p = own_buf + strlen ("vRun;"); p && *p; p = strchr (p, ';'))
- {
- p++;
- new_argc++;
- }
-
- for (i = 0, p = own_buf + strlen ("vRun;"); *p; p = next_p, ++i)
- {
- next_p = strchr (p, ';');
- if (next_p == NULL)
- next_p = p + strlen (p);
-
- if (i == 0 && p == next_p)
- {
- /* No program specified. */
- new_program_name = NULL;
- }
- else if (p == next_p)
- {
- /* Empty argument. */
- new_argv.push_back (xstrdup ("''"));
- }
- else
- {
- size_t len = (next_p - p) / 2;
- /* ARG is the unquoted argument received via the RSP. */
- char *arg = (char *) xmalloc (len + 1);
- /* FULL_ARGS will contain the quoted version of ARG. */
- char *full_arg = (char *) xmalloc ((len + 1) * 2);
- /* These are pointers used to navigate the strings above. */
- char *tmp_arg = arg;
- char *tmp_full_arg = full_arg;
- int need_quote = 0;
-
- hex2bin (p, (gdb_byte *) arg, len);
- arg[len] = '\0';
-
- while (*tmp_arg != '\0')
- {
- switch (*tmp_arg)
- {
- case '\n':
- /* Quote \n. */
- *tmp_full_arg = '\'';
- ++tmp_full_arg;
- need_quote = 1;
- break;
-
- case '\'':
- /* Quote single quote. */
- *tmp_full_arg = '\\';
- ++tmp_full_arg;
- break;
-
- default:
- break;
- }
-
- *tmp_full_arg = *tmp_arg;
- ++tmp_full_arg;
- ++tmp_arg;
- }
-
- if (need_quote)
- *tmp_full_arg++ = '\'';
-
- /* Finish FULL_ARG and push it into the vector containing
- the argv. */
- *tmp_full_arg = '\0';
- if (i == 0)
- new_program_name = full_arg;
- else
- new_argv.push_back (full_arg);
- xfree (arg);
- }
- if (*next_p)
- next_p++;
- }
- new_argv.push_back (NULL);
-
- if (new_program_name == NULL)
- {
- /* GDB didn't specify a program to run. Use the program from the
- last run with the new argument list. */
- if (program_path.get () == NULL)
- {
- write_enn (own_buf);
- free_vector_argv (new_argv);
- return 0;
- }
- }
- else
- program_path.set (gdb::unique_xmalloc_ptr<char> (new_program_name));
-
- /* Free the old argv and install the new one. */
- free_vector_argv (program_args);
- program_args = new_argv;
-
- create_inferior (program_path.get (), program_args);
-
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
- {
- prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status);
-
- /* In non-stop, sending a resume reply doesn't set the general
- thread, but GDB assumes a vRun sets it (this is so GDB can
- query which is the main thread of the new inferior. */
- if (non_stop)
- cs.general_thread = cs.last_ptid;
-
- return 1;
- }
- else
- {
- write_enn (own_buf);
- return 0;
- }
-}
-
-/* Kill process. Return 1 if successful, 0 if failure. */
-static int
-handle_v_kill (char *own_buf)
-{
- client_state &cs = get_client_state ();
- int pid;
- char *p = &own_buf[6];
- if (cs.multi_process)
- pid = strtol (p, NULL, 16);
- else
- pid = signal_pid;
-
- process_info *proc = find_process_pid (pid);
-
- if (proc != nullptr && kill_inferior (proc) == 0)
- {
- cs.last_status.kind = TARGET_WAITKIND_SIGNALLED;
- cs.last_status.value.sig = GDB_SIGNAL_KILL;
- cs.last_ptid = ptid_t (pid);
- discard_queued_stop_replies (cs.last_ptid);
- write_ok (own_buf);
- return 1;
- }
- else
- {
- write_enn (own_buf);
- return 0;
- }
-}
-
-/* Handle all of the extended 'v' packets. */
-void
-handle_v_requests (char *own_buf, int packet_len, int *new_packet_len)
-{
- client_state &cs = get_client_state ();
- if (!disable_packet_vCont)
- {
- if (strcmp (own_buf, "vCtrlC") == 0)
- {
- (*the_target->request_interrupt) ();
- write_ok (own_buf);
- return;
- }
-
- if (startswith (own_buf, "vCont;"))
- {
- handle_v_cont (own_buf);
- return;
- }
-
- if (startswith (own_buf, "vCont?"))
- {
- strcpy (own_buf, "vCont;c;C;t");
-
- if (target_supports_hardware_single_step ()
- || target_supports_software_single_step ()
- || !cs.vCont_supported)
- {
- /* If target supports single step either by hardware or by
- software, add actions s and S to the list of supported
- actions. On the other hand, if GDB doesn't request the
- supported vCont actions in qSupported packet, add s and
- S to the list too. */
- own_buf = own_buf + strlen (own_buf);
- strcpy (own_buf, ";s;S");
- }
-
- if (target_supports_range_stepping ())
- {
- own_buf = own_buf + strlen (own_buf);
- strcpy (own_buf, ";r");
- }
- return;
- }
- }
-
- if (startswith (own_buf, "vFile:")
- && handle_vFile (own_buf, packet_len, new_packet_len))
- return;
-
- if (startswith (own_buf, "vAttach;"))
- {
- if ((!extended_protocol || !cs.multi_process) && target_running ())
- {
- fprintf (stderr, "Already debugging a process\n");
- write_enn (own_buf);
- return;
- }
- handle_v_attach (own_buf);
- return;
- }
-
- if (startswith (own_buf, "vRun;"))
- {
- if ((!extended_protocol || !cs.multi_process) && target_running ())
- {
- fprintf (stderr, "Already debugging a process\n");
- write_enn (own_buf);
- return;
- }
- handle_v_run (own_buf);
- return;
- }
-
- if (startswith (own_buf, "vKill;"))
- {
- if (!target_running ())
- {
- fprintf (stderr, "No process to kill\n");
- write_enn (own_buf);
- return;
- }
- handle_v_kill (own_buf);
- return;
- }
-
- if (handle_notif_ack (own_buf, packet_len))
- return;
-
- /* Otherwise we didn't know what packet it was. Say we didn't
- understand it. */
- own_buf[0] = 0;
- return;
-}
-
-/* Resume thread and wait for another event. In non-stop mode,
- don't really wait here, but return immediatelly to the event
- loop. */
-static void
-myresume (char *own_buf, int step, int sig)
-{
- client_state &cs = get_client_state ();
- struct thread_resume resume_info[2];
- int n = 0;
- int valid_cont_thread;
-
- valid_cont_thread = (cs.cont_thread != null_ptid
- && cs.cont_thread != minus_one_ptid);
-
- if (step || sig || valid_cont_thread)
- {
- resume_info[0].thread = current_ptid;
- if (step)
- resume_info[0].kind = resume_step;
- else
- resume_info[0].kind = resume_continue;
- resume_info[0].sig = sig;
- n++;
- }
-
- if (!valid_cont_thread)
- {
- resume_info[n].thread = minus_one_ptid;
- resume_info[n].kind = resume_continue;
- resume_info[n].sig = 0;
- n++;
- }
-
- resume (resume_info, n);
-}
-
-/* Callback for for_each_thread. Make a new stop reply for each
- stopped thread. */
-
-static void
-queue_stop_reply_callback (thread_info *thread)
-{
- /* For now, assume targets that don't have this callback also don't
- manage the thread's last_status field. */
- if (the_target->thread_stopped == NULL)
- {
- struct vstop_notif *new_notif = new struct vstop_notif;
-
- new_notif->ptid = thread->id;
- new_notif->status = thread->last_status;
- /* Pass the last stop reply back to GDB, but don't notify
- yet. */
- notif_event_enque (¬if_stop, new_notif);
- }
- else
- {
- if (thread_stopped (thread))
- {
- if (debug_threads)
- {
- std::string status_string
- = target_waitstatus_to_string (&thread->last_status);
-
- debug_printf ("Reporting thread %s as already stopped with %s\n",
- target_pid_to_str (thread->id),
- status_string.c_str ());
- }
-
- gdb_assert (thread->last_status.kind != TARGET_WAITKIND_IGNORE);
-
- /* Pass the last stop reply back to GDB, but don't notify
- yet. */
- queue_stop_reply (thread->id, &thread->last_status);
- }
- }
-}
-
-/* Set this inferior threads's state as "want-stopped". We won't
- resume this thread until the client gives us another action for
- it. */
-
-static void
-gdb_wants_thread_stopped (thread_info *thread)
-{
- thread->last_resume_kind = resume_stop;
-
- if (thread->last_status.kind == TARGET_WAITKIND_IGNORE)
- {
- /* Most threads are stopped implicitly (all-stop); tag that with
- signal 0. */
- thread->last_status.kind = TARGET_WAITKIND_STOPPED;
- thread->last_status.value.sig = GDB_SIGNAL_0;
- }
-}
-
-/* Set all threads' states as "want-stopped". */
-
-static void
-gdb_wants_all_threads_stopped (void)
-{
- for_each_thread (gdb_wants_thread_stopped);
-}
-
-/* Callback for for_each_thread. If the thread is stopped with an
- interesting event, mark it as having a pending event. */
-
-static void
-set_pending_status_callback (thread_info *thread)
-{
- if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
- || (thread->last_status.value.sig != GDB_SIGNAL_0
- /* A breakpoint, watchpoint or finished step from a previous
- GDB run isn't considered interesting for a new GDB run.
- If we left those pending, the new GDB could consider them
- random SIGTRAPs. This leaves out real async traps. We'd
- have to peek into the (target-specific) siginfo to
- distinguish those. */
- && thread->last_status.value.sig != GDB_SIGNAL_TRAP))
- thread->status_pending_p = 1;
-}
-
-/* Status handler for the '?' packet. */
-
-static void
-handle_status (char *own_buf)
-{
- client_state &cs = get_client_state ();
-
- /* GDB is connected, don't forward events to the target anymore. */
- for_each_process ([] (process_info *process) {
- process->gdb_detached = 0;
- });
-
- /* In non-stop mode, we must send a stop reply for each stopped
- thread. In all-stop mode, just send one for the first stopped
- thread we find. */
-
- if (non_stop)
- {
- for_each_thread (queue_stop_reply_callback);
-
- /* The first is sent immediatly. OK is sent if there is no
- stopped thread, which is the same handling of the vStopped
- packet (by design). */
- notif_write_event (¬if_stop, cs.own_buf);
- }
- else
- {
- thread_info *thread = NULL;
-
- pause_all (0);
- stabilize_threads ();
- gdb_wants_all_threads_stopped ();
-
- /* We can only report one status, but we might be coming out of
- non-stop -- if more than one thread is stopped with
- interesting events, leave events for the threads we're not
- reporting now pending. They'll be reported the next time the
- threads are resumed. Start by marking all interesting events
- as pending. */
- for_each_thread (set_pending_status_callback);
-
- /* Prefer the last thread that reported an event to GDB (even if
- that was a GDB_SIGNAL_TRAP). */
- if (cs.last_status.kind != TARGET_WAITKIND_IGNORE
- && cs.last_status.kind != TARGET_WAITKIND_EXITED
- && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED)
- thread = find_thread_ptid (cs.last_ptid);
-
- /* If the last event thread is not found for some reason, look
- for some other thread that might have an event to report. */
- if (thread == NULL)
- thread = find_thread ([] (thread_info *thr_arg)
- {
- return thr_arg->status_pending_p;
- });
-
- /* If we're still out of luck, simply pick the first thread in
- the thread list. */
- if (thread == NULL)
- thread = get_first_thread ();
-
- if (thread != NULL)
- {
- struct thread_info *tp = (struct thread_info *) thread;
-
- /* We're reporting this event, so it's no longer
- pending. */
- tp->status_pending_p = 0;
-
- /* GDB assumes the current thread is the thread we're
- reporting the status for. */
- cs.general_thread = thread->id;
- set_desired_thread ();
-
- gdb_assert (tp->last_status.kind != TARGET_WAITKIND_IGNORE);
- prepare_resume_reply (own_buf, tp->id, &tp->last_status);
- }
- else
- strcpy (own_buf, "W00");
- }
-}
-
-static void
-gdbserver_version (void)
-{
- printf ("GNU gdbserver %s%s\n"
- "Copyright (C) 2020 Free Software Foundation, Inc.\n"
- "gdbserver is free software, covered by the "
- "GNU General Public License.\n"
- "This gdbserver was configured as \"%s\"\n",
- PKGVERSION, version, host_name);
-}
-
-static void
-gdbserver_usage (FILE *stream)
-{
- fprintf (stream, "Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n"
- "\tgdbserver [OPTIONS] --attach COMM PID\n"
- "\tgdbserver [OPTIONS] --multi COMM\n"
- "\n"
- "COMM may either be a tty device (for serial debugging),\n"
- "HOST:PORT to listen for a TCP connection, or '-' or 'stdio' to use \n"
- "stdin/stdout of gdbserver.\n"
- "PROG is the executable program. ARGS are arguments passed to inferior.\n"
- "PID is the process ID to attach to, when --attach is specified.\n"
- "\n"
- "Operating modes:\n"
- "\n"
- " --attach Attach to running process PID.\n"
- " --multi Start server without a specific program, and\n"
- " only quit when explicitly commanded.\n"
- " --once Exit after the first connection has closed.\n"
- " --help Print this message and then exit.\n"
- " --version Display version information and exit.\n"
- "\n"
- "Other options:\n"
- "\n"
- " --wrapper WRAPPER -- Run WRAPPER to start new programs.\n"
- " --disable-randomization\n"
- " Run PROG with address space randomization disabled.\n"
- " --no-disable-randomization\n"
- " Don't disable address space randomization when\n"
- " starting PROG.\n"
- " --startup-with-shell\n"
- " Start PROG using a shell. I.e., execs a shell that\n"
- " then execs PROG. (default)\n"
- " --no-startup-with-shell\n"
- " Exec PROG directly instead of using a shell.\n"
- " Disables argument globbing and variable substitution\n"
- " on UNIX-like systems.\n"
- "\n"
- "Debug options:\n"
- "\n"
- " --debug Enable general debugging output.\n"
- " --debug-format=OPT1[,OPT2,...]\n"
- " Specify extra content in debugging output.\n"
- " Options:\n"
- " all\n"
- " none\n"
- " timestamp\n"
- " --remote-debug Enable remote protocol debugging output.\n"
- " --disable-packet=OPT1[,OPT2,...]\n"
- " Disable support for RSP packets or features.\n"
- " Options:\n"
- " vCont, Tthread, qC, qfThreadInfo and \n"
- " threads (disable all threading packets).\n"
- "\n"
- "For more information, consult the GDB manual (available as on-line \n"
- "info or a printed manual).\n");
- if (REPORT_BUGS_TO[0] && stream == stdout)
- fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO);
-}
-
-static void
-gdbserver_show_disableable (FILE *stream)
-{
- fprintf (stream, "Disableable packets:\n"
- " vCont \tAll vCont packets\n"
- " qC \tQuerying the current thread\n"
- " qfThreadInfo\tThread listing\n"
- " Tthread \tPassing the thread specifier in the "
- "T stop reply packet\n"
- " threads \tAll of the above\n");
-}
-
-static void
-kill_inferior_callback (process_info *process)
-{
- kill_inferior (process);
- discard_queued_stop_replies (ptid_t (process->pid));
-}
-
-/* Call this when exiting gdbserver with possible inferiors that need
- to be killed or detached from. */
-
-static void
-detach_or_kill_for_exit (void)
-{
- /* First print a list of the inferiors we will be killing/detaching.
- This is to assist the user, for example, in case the inferior unexpectedly
- dies after we exit: did we screw up or did the inferior exit on its own?
- Having this info will save some head-scratching. */
-
- if (have_started_inferiors_p ())
- {
- fprintf (stderr, "Killing process(es):");
-
- for_each_process ([] (process_info *process) {
- if (!process->attached)
- fprintf (stderr, " %d", process->pid);
- });
-
- fprintf (stderr, "\n");
- }
- if (have_attached_inferiors_p ())
- {
- fprintf (stderr, "Detaching process(es):");
-
- for_each_process ([] (process_info *process) {
- if (process->attached)
- fprintf (stderr, " %d", process->pid);
- });
-
- fprintf (stderr, "\n");
- }
-
- /* Now we can kill or detach the inferiors. */
- for_each_process ([] (process_info *process) {
- int pid = process->pid;
-
- if (process->attached)
- detach_inferior (process);
- else
- kill_inferior (process);
-
- discard_queued_stop_replies (ptid_t (pid));
- });
-}
-
-/* Value that will be passed to exit(3) when gdbserver exits. */
-static int exit_code;
-
-/* Wrapper for detach_or_kill_for_exit that catches and prints
- errors. */
-
-static void
-detach_or_kill_for_exit_cleanup ()
-{
- try
- {
- detach_or_kill_for_exit ();
- }
- catch (const gdb_exception &exception)
- {
- fflush (stdout);
- fprintf (stderr, "Detach or kill failed: %s\n",
- exception.what ());
- exit_code = 1;
- }
-}
-
-/* Main function. This is called by the real "main" function,
- wrapped in a TRY_CATCH that handles any uncaught exceptions. */
-
-static void ATTRIBUTE_NORETURN
-captured_main (int argc, char *argv[])
-{
- int bad_attach;
- int pid;
- char *arg_end;
- const char *port = NULL;
- char **next_arg = &argv[1];
- volatile int multi_mode = 0;
- volatile int attach = 0;
- int was_running;
- bool selftest = false;
-#if GDB_SELF_TEST
- const char *selftest_filter = NULL;
-#endif
-
- current_directory = getcwd (NULL, 0);
- client_state &cs = get_client_state ();
-
- if (current_directory == NULL)
- {
- error (_("Could not find current working directory: %s"),
- safe_strerror (errno));
- }
-
- while (*next_arg != NULL && **next_arg == '-')
- {
- if (strcmp (*next_arg, "--version") == 0)
- {
- gdbserver_version ();
- exit (0);
- }
- else if (strcmp (*next_arg, "--help") == 0)
- {
- gdbserver_usage (stdout);
- exit (0);
- }
- else if (strcmp (*next_arg, "--attach") == 0)
- attach = 1;
- else if (strcmp (*next_arg, "--multi") == 0)
- multi_mode = 1;
- else if (strcmp (*next_arg, "--wrapper") == 0)
- {
- char **tmp;
-
- next_arg++;
-
- tmp = next_arg;
- while (*next_arg != NULL && strcmp (*next_arg, "--") != 0)
- {
- wrapper_argv += *next_arg;
- wrapper_argv += ' ';
- next_arg++;
- }
-
- if (!wrapper_argv.empty ())
- {
- /* Erase the last whitespace. */
- wrapper_argv.erase (wrapper_argv.end () - 1);
- }
-
- if (next_arg == tmp || *next_arg == NULL)
- {
- gdbserver_usage (stderr);
- exit (1);
- }
-
- /* Consume the "--". */
- *next_arg = NULL;
- }
- else if (strcmp (*next_arg, "--debug") == 0)
- debug_threads = 1;
- else if (startswith (*next_arg, "--debug-format="))
- {
- std::string error_msg
- = parse_debug_format_options ((*next_arg)
- + sizeof ("--debug-format=") - 1, 0);
-
- if (!error_msg.empty ())
- {
- fprintf (stderr, "%s", error_msg.c_str ());
- exit (1);
- }
- }
- else if (strcmp (*next_arg, "--remote-debug") == 0)
- remote_debug = 1;
- else if (startswith (*next_arg, "--debug-file="))
- debug_set_output ((*next_arg) + sizeof ("--debug-file=") -1);
- else if (strcmp (*next_arg, "--disable-packet") == 0)
- {
- gdbserver_show_disableable (stdout);
- exit (0);
- }
- else if (startswith (*next_arg, "--disable-packet="))
- {
- char *packets = *next_arg += sizeof ("--disable-packet=") - 1;
- char *saveptr;
- for (char *tok = strtok_r (packets, ",", &saveptr);
- tok != NULL;
- tok = strtok_r (NULL, ",", &saveptr))
- {
- if (strcmp ("vCont", tok) == 0)
- disable_packet_vCont = true;
- else if (strcmp ("Tthread", tok) == 0)
- disable_packet_Tthread = true;
- else if (strcmp ("qC", tok) == 0)
- disable_packet_qC = true;
- else if (strcmp ("qfThreadInfo", tok) == 0)
- disable_packet_qfThreadInfo = true;
- else if (strcmp ("threads", tok) == 0)
- {
- disable_packet_vCont = true;
- disable_packet_Tthread = true;
- disable_packet_qC = true;
- disable_packet_qfThreadInfo = true;
- }
- else
- {
- fprintf (stderr, "Don't know how to disable \"%s\".\n\n",
- tok);
- gdbserver_show_disableable (stderr);
- exit (1);
- }
- }
- }
- else if (strcmp (*next_arg, "-") == 0)
- {
- /* "-" specifies a stdio connection and is a form of port
- specification. */
- port = STDIO_CONNECTION_NAME;
- next_arg++;
- break;
- }
- else if (strcmp (*next_arg, "--disable-randomization") == 0)
- cs.disable_randomization = 1;
- else if (strcmp (*next_arg, "--no-disable-randomization") == 0)
- cs.disable_randomization = 0;
- else if (strcmp (*next_arg, "--startup-with-shell") == 0)
- startup_with_shell = true;
- else if (strcmp (*next_arg, "--no-startup-with-shell") == 0)
- startup_with_shell = false;
- else if (strcmp (*next_arg, "--once") == 0)
- run_once = true;
- else if (strcmp (*next_arg, "--selftest") == 0)
- selftest = true;
- else if (startswith (*next_arg, "--selftest="))
- {
- selftest = true;
-#if GDB_SELF_TEST
- selftest_filter = *next_arg + strlen ("--selftest=");
-#endif
- }
- else
- {
- fprintf (stderr, "Unknown argument: %s\n", *next_arg);
- exit (1);
- }
-
- next_arg++;
- continue;
- }
-
- if (port == NULL)
- {
- port = *next_arg;
- next_arg++;
- }
- if ((port == NULL || (!attach && !multi_mode && *next_arg == NULL))
- && !selftest)
- {
- gdbserver_usage (stderr);
- exit (1);
- }
-
- /* Remember stdio descriptors. LISTEN_DESC must not be listed, it will be
- opened by remote_prepare. */
- notice_open_fds ();
-
- save_original_signals_state (false);
-
- /* We need to know whether the remote connection is stdio before
- starting the inferior. Inferiors created in this scenario have
- stdin,stdout redirected. So do this here before we call
- start_inferior. */
- if (port != NULL)
- remote_prepare (port);
-
- bad_attach = 0;
- pid = 0;
-
- /* --attach used to come after PORT, so allow it there for
- compatibility. */
- if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0)
- {
- attach = 1;
- next_arg++;
- }
-
- if (attach
- && (*next_arg == NULL
- || (*next_arg)[0] == '\0'
- || (pid = strtoul (*next_arg, &arg_end, 0)) == 0
- || *arg_end != '\0'
- || next_arg[1] != NULL))
- bad_attach = 1;
-
- if (bad_attach)
- {
- gdbserver_usage (stderr);
- exit (1);
- }
-
- /* Gather information about the environment. */
- our_environ = gdb_environ::from_host_environ ();
-
- initialize_async_io ();
- initialize_low ();
- have_job_control ();
- initialize_event_loop ();
- if (target_supports_tracepoints ())
- initialize_tracepoint ();
-
- mem_buf = (unsigned char *) xmalloc (PBUFSIZ);
-
- if (selftest)
- {
-#if GDB_SELF_TEST
- selftests::run_tests (selftest_filter);
-#else
- printf (_("Selftests have been disabled for this build.\n"));
-#endif
- throw_quit ("Quit");
- }
-
- if (pid == 0 && *next_arg != NULL)
- {
- int i, n;
-
- n = argc - (next_arg - argv);
- program_path.set (make_unique_xstrdup (next_arg[0]));
- for (i = 1; i < n; i++)
- program_args.push_back (xstrdup (next_arg[i]));
- program_args.push_back (NULL);
-
- /* Wait till we are at first instruction in program. */
- create_inferior (program_path.get (), program_args);
-
- /* We are now (hopefully) stopped at the first instruction of
- the target process. This assumes that the target process was
- successfully created. */
- }
- else if (pid != 0)
- {
- if (attach_inferior (pid) == -1)
- error ("Attaching not supported on this target");
-
- /* Otherwise succeeded. */
- }
- else
- {
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.integer = 0;
- cs.last_ptid = minus_one_ptid;
- }
-
- SCOPE_EXIT { detach_or_kill_for_exit_cleanup (); };
-
- /* Don't report shared library events on the initial connection,
- even if some libraries are preloaded. Avoids the "stopped by
- shared library event" notice on gdb side. */
- dlls_changed = 0;
-
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
- was_running = 0;
- else
- was_running = 1;
-
- if (!was_running && !multi_mode)
- error ("No program to debug");
-
- while (1)
- {
- cs.noack_mode = 0;
- cs.multi_process = 0;
- cs.report_fork_events = 0;
- cs.report_vfork_events = 0;
- cs.report_exec_events = 0;
- /* Be sure we're out of tfind mode. */
- cs.current_traceframe = -1;
- cs.cont_thread = null_ptid;
- cs.swbreak_feature = 0;
- cs.hwbreak_feature = 0;
- cs.vCont_supported = 0;
-
- remote_open (port);
-
- try
- {
- /* Wait for events. This will return when all event sources
- are removed from the event loop. */
- start_event_loop ();
-
- /* If an exit was requested (using the "monitor exit"
- command), terminate now. */
- if (exit_requested)
- throw_quit ("Quit");
-
- /* The only other way to get here is for getpkt to fail:
-
- - If --once was specified, we're done.
-
- - If not in extended-remote mode, and we're no longer
- debugging anything, simply exit: GDB has disconnected
- after processing the last process exit.
-
- - Otherwise, close the connection and reopen it at the
- top of the loop. */
- if (run_once || (!extended_protocol && !target_running ()))
- throw_quit ("Quit");
-
- fprintf (stderr,
- "Remote side has terminated connection. "
- "GDBserver will reopen the connection.\n");
-
- /* Get rid of any pending statuses. An eventual reconnection
- (by the same GDB instance or another) will refresh all its
- state from scratch. */
- discard_queued_stop_replies (minus_one_ptid);
- for_each_thread ([] (thread_info *thread)
- {
- thread->status_pending_p = 0;
- });
-
- if (tracing)
- {
- if (disconnected_tracing)
- {
- /* Try to enable non-stop/async mode, so we we can
- both wait for an async socket accept, and handle
- async target events simultaneously. There's also
- no point either in having the target always stop
- all threads, when we're going to pass signals
- down without informing GDB. */
- if (!non_stop)
- {
- if (start_non_stop (1))
- non_stop = 1;
-
- /* Detaching implicitly resumes all threads;
- simply disconnecting does not. */
- }
- }
- else
- {
- fprintf (stderr,
- "Disconnected tracing disabled; "
- "stopping trace run.\n");
- stop_tracing ();
- }
- }
- }
- catch (const gdb_exception_error &exception)
- {
- fflush (stdout);
- fprintf (stderr, "gdbserver: %s\n", exception.what ());
-
- if (response_needed)
- {
- write_enn (cs.own_buf);
- putpkt (cs.own_buf);
- }
-
- if (run_once)
- throw_quit ("Quit");
- }
- }
-}
-
-/* Main function. */
-
-int
-main (int argc, char *argv[])
-{
-
- try
- {
- captured_main (argc, argv);
- }
- catch (const gdb_exception &exception)
- {
- if (exception.reason == RETURN_ERROR)
- {
- fflush (stdout);
- fprintf (stderr, "%s\n", exception.what ());
- fprintf (stderr, "Exiting\n");
- exit_code = 1;
- }
-
- exit (exit_code);
- }
-
- gdb_assert_not_reached ("captured_main should never return");
-}
-
-/* Process options coming from Z packets for a breakpoint. PACKET is
- the packet buffer. *PACKET is updated to point to the first char
- after the last processed option. */
-
-static void
-process_point_options (struct gdb_breakpoint *bp, const char **packet)
-{
- const char *dataptr = *packet;
- int persist;
-
- /* Check if data has the correct format. */
- if (*dataptr != ';')
- return;
-
- dataptr++;
-
- while (*dataptr)
- {
- if (*dataptr == ';')
- ++dataptr;
-
- if (*dataptr == 'X')
- {
- /* Conditional expression. */
- if (debug_threads)
- debug_printf ("Found breakpoint condition.\n");
- if (!add_breakpoint_condition (bp, &dataptr))
- dataptr = strchrnul (dataptr, ';');
- }
- else if (startswith (dataptr, "cmds:"))
- {
- dataptr += strlen ("cmds:");
- if (debug_threads)
- debug_printf ("Found breakpoint commands %s.\n", dataptr);
- persist = (*dataptr == '1');
- dataptr += 2;
- if (add_breakpoint_commands (bp, &dataptr, persist))
- dataptr = strchrnul (dataptr, ';');
- }
- else
- {
- fprintf (stderr, "Unknown token %c, ignoring.\n",
- *dataptr);
- /* Skip tokens until we find one that we recognize. */
- dataptr = strchrnul (dataptr, ';');
- }
- }
- *packet = dataptr;
-}
-
-/* Event loop callback that handles a serial event. The first byte in
- the serial buffer gets us here. We expect characters to arrive at
- a brisk pace, so we read the rest of the packet with a blocking
- getpkt call. */
-
-static int
-process_serial_event (void)
-{
- client_state &cs = get_client_state ();
- int signal;
- unsigned int len;
- CORE_ADDR mem_addr;
- unsigned char sig;
- int packet_len;
- int new_packet_len = -1;
-
- disable_async_io ();
-
- response_needed = false;
- packet_len = getpkt (cs.own_buf);
- if (packet_len <= 0)
- {
- remote_close ();
- /* Force an event loop break. */
- return -1;
- }
- response_needed = true;
-
- char ch = cs.own_buf[0];
- switch (ch)
- {
- case 'q':
- handle_query (cs.own_buf, packet_len, &new_packet_len);
- break;
- case 'Q':
- handle_general_set (cs.own_buf);
- break;
- case 'D':
- handle_detach (cs.own_buf);
- break;
- case '!':
- extended_protocol = true;
- write_ok (cs.own_buf);
- break;
- case '?':
- handle_status (cs.own_buf);
- break;
- case 'H':
- if (cs.own_buf[1] == 'c' || cs.own_buf[1] == 'g' || cs.own_buf[1] == 's')
- {
- require_running_or_break (cs.own_buf);
-
- ptid_t thread_id = read_ptid (&cs.own_buf[2], NULL);
-
- if (thread_id == null_ptid || thread_id == minus_one_ptid)
- thread_id = null_ptid;
- else if (thread_id.is_pid ())
- {
- /* The ptid represents a pid. */
- thread_info *thread = find_any_thread_of_pid (thread_id.pid ());
-
- if (thread == NULL)
- {
- write_enn (cs.own_buf);
- break;
- }
-
- thread_id = thread->id;
- }
- else
- {
- /* The ptid represents a lwp/tid. */
- if (find_thread_ptid (thread_id) == NULL)
- {
- write_enn (cs.own_buf);
- break;
- }
- }
-
- if (cs.own_buf[1] == 'g')
- {
- if (thread_id == null_ptid)
- {
- /* GDB is telling us to choose any thread. Check if
- the currently selected thread is still valid. If
- it is not, select the first available. */
- thread_info *thread = find_thread_ptid (cs.general_thread);
- if (thread == NULL)
- thread = get_first_thread ();
- thread_id = thread->id;
- }
-
- cs.general_thread = thread_id;
- set_desired_thread ();
- gdb_assert (current_thread != NULL);
- }
- else if (cs.own_buf[1] == 'c')
- cs.cont_thread = thread_id;
-
- write_ok (cs.own_buf);
- }
- else
- {
- /* Silently ignore it so that gdb can extend the protocol
- without compatibility headaches. */
- cs.own_buf[0] = '\0';
- }
- break;
- case 'g':
- require_running_or_break (cs.own_buf);
- if (cs.current_traceframe >= 0)
- {
- struct regcache *regcache
- = new_register_cache (current_target_desc ());
-
- if (fetch_traceframe_registers (cs.current_traceframe,
- regcache, -1) == 0)
- registers_to_string (regcache, cs.own_buf);
- else
- write_enn (cs.own_buf);
- free_register_cache (regcache);
- }
- else
- {
- struct regcache *regcache;
-
- if (!set_desired_thread ())
- write_enn (cs.own_buf);
- else
- {
- regcache = get_thread_regcache (current_thread, 1);
- registers_to_string (regcache, cs.own_buf);
- }
- }
- break;
- case 'G':
- require_running_or_break (cs.own_buf);
- if (cs.current_traceframe >= 0)
- write_enn (cs.own_buf);
- else
- {
- struct regcache *regcache;
-
- if (!set_desired_thread ())
- write_enn (cs.own_buf);
- else
- {
- regcache = get_thread_regcache (current_thread, 1);
- registers_from_string (regcache, &cs.own_buf[1]);
- write_ok (cs.own_buf);
- }
- }
- break;
- case 'm':
- {
- require_running_or_break (cs.own_buf);
- decode_m_packet (&cs.own_buf[1], &mem_addr, &len);
- int res = gdb_read_memory (mem_addr, mem_buf, len);
- if (res < 0)
- write_enn (cs.own_buf);
- else
- bin2hex (mem_buf, cs.own_buf, res);
- }
- break;
- case 'M':
- require_running_or_break (cs.own_buf);
- decode_M_packet (&cs.own_buf[1], &mem_addr, &len, &mem_buf);
- if (gdb_write_memory (mem_addr, mem_buf, len) == 0)
- write_ok (cs.own_buf);
- else
- write_enn (cs.own_buf);
- break;
- case 'X':
- require_running_or_break (cs.own_buf);
- if (decode_X_packet (&cs.own_buf[1], packet_len - 1,
- &mem_addr, &len, &mem_buf) < 0
- || gdb_write_memory (mem_addr, mem_buf, len) != 0)
- write_enn (cs.own_buf);
- else
- write_ok (cs.own_buf);
- break;
- case 'C':
- require_running_or_break (cs.own_buf);
- hex2bin (cs.own_buf + 1, &sig, 1);
- if (gdb_signal_to_host_p ((enum gdb_signal) sig))
- signal = gdb_signal_to_host ((enum gdb_signal) sig);
- else
- signal = 0;
- myresume (cs.own_buf, 0, signal);
- break;
- case 'S':
- require_running_or_break (cs.own_buf);
- hex2bin (cs.own_buf + 1, &sig, 1);
- if (gdb_signal_to_host_p ((enum gdb_signal) sig))
- signal = gdb_signal_to_host ((enum gdb_signal) sig);
- else
- signal = 0;
- myresume (cs.own_buf, 1, signal);
- break;
- case 'c':
- require_running_or_break (cs.own_buf);
- signal = 0;
- myresume (cs.own_buf, 0, signal);
- break;
- case 's':
- require_running_or_break (cs.own_buf);
- signal = 0;
- myresume (cs.own_buf, 1, signal);
- break;
- case 'Z': /* insert_ ... */
- /* Fallthrough. */
- case 'z': /* remove_ ... */
- {
- char *dataptr;
- ULONGEST addr;
- int kind;
- char type = cs.own_buf[1];
- int res;
- const int insert = ch == 'Z';
- const char *p = &cs.own_buf[3];
-
- p = unpack_varlen_hex (p, &addr);
- kind = strtol (p + 1, &dataptr, 16);
-
- if (insert)
- {
- struct gdb_breakpoint *bp;
-
- bp = set_gdb_breakpoint (type, addr, kind, &res);
- if (bp != NULL)
- {
- res = 0;
-
- /* GDB may have sent us a list of *point parameters to
- be evaluated on the target's side. Read such list
- here. If we already have a list of parameters, GDB
- is telling us to drop that list and use this one
- instead. */
- clear_breakpoint_conditions_and_commands (bp);
- const char *options = dataptr;
- process_point_options (bp, &options);
- }
- }
- else
- res = delete_gdb_breakpoint (type, addr, kind);
-
- if (res == 0)
- write_ok (cs.own_buf);
- else if (res == 1)
- /* Unsupported. */
- cs.own_buf[0] = '\0';
- else
- write_enn (cs.own_buf);
- break;
- }
- case 'k':
- response_needed = false;
- if (!target_running ())
- /* The packet we received doesn't make sense - but we can't
- reply to it, either. */
- return 0;
-
- fprintf (stderr, "Killing all inferiors\n");
-
- for_each_process (kill_inferior_callback);
-
- /* When using the extended protocol, we wait with no program
- running. The traditional protocol will exit instead. */
- if (extended_protocol)
- {
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.sig = GDB_SIGNAL_KILL;
- return 0;
- }
- else
- exit (0);
-
- case 'T':
- {
- require_running_or_break (cs.own_buf);
-
- ptid_t thread_id = read_ptid (&cs.own_buf[1], NULL);
- if (find_thread_ptid (thread_id) == NULL)
- {
- write_enn (cs.own_buf);
- break;
- }
-
- if (mythread_alive (thread_id))
- write_ok (cs.own_buf);
- else
- write_enn (cs.own_buf);
- }
- break;
- case 'R':
- response_needed = false;
-
- /* Restarting the inferior is only supported in the extended
- protocol. */
- if (extended_protocol)
- {
- if (target_running ())
- for_each_process (kill_inferior_callback);
-
- fprintf (stderr, "GDBserver restarting\n");
-
- /* Wait till we are at 1st instruction in prog. */
- if (program_path.get () != NULL)
- {
- create_inferior (program_path.get (), program_args);
-
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
- {
- /* Stopped at the first instruction of the target
- process. */
- cs.general_thread = cs.last_ptid;
- }
- else
- {
- /* Something went wrong. */
- cs.general_thread = null_ptid;
- }
- }
- else
- {
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.sig = GDB_SIGNAL_KILL;
- }
- return 0;
- }
- else
- {
- /* It is a request we don't understand. Respond with an
- empty packet so that gdb knows that we don't support this
- request. */
- cs.own_buf[0] = '\0';
- break;
- }
- case 'v':
- /* Extended (long) request. */
- handle_v_requests (cs.own_buf, packet_len, &new_packet_len);
- break;
-
- default:
- /* It is a request we don't understand. Respond with an empty
- packet so that gdb knows that we don't support this
- request. */
- cs.own_buf[0] = '\0';
- break;
- }
-
- if (new_packet_len != -1)
- putpkt_binary (cs.own_buf, new_packet_len);
- else
- putpkt (cs.own_buf);
-
- response_needed = false;
-
- if (exit_requested)
- return -1;
-
- return 0;
-}
-
-/* Event-loop callback for serial events. */
-
-int
-handle_serial_event (int err, gdb_client_data client_data)
-{
- if (debug_threads)
- debug_printf ("handling possible serial event\n");
-
- /* Really handle it. */
- if (process_serial_event () < 0)
- return -1;
-
- /* Be sure to not change the selected thread behind GDB's back.
- Important in the non-stop mode asynchronous protocol. */
- set_desired_thread ();
-
- return 0;
-}
-
-/* Push a stop notification on the notification queue. */
-
-static void
-push_stop_notification (ptid_t ptid, struct target_waitstatus *status)
-{
- struct vstop_notif *vstop_notif = new struct vstop_notif;
-
- vstop_notif->status = *status;
- vstop_notif->ptid = ptid;
- /* Push Stop notification. */
- notif_push (¬if_stop, vstop_notif);
-}
-
-/* Event-loop callback for target events. */
-
-int
-handle_target_event (int err, gdb_client_data client_data)
-{
- client_state &cs = get_client_state ();
- if (debug_threads)
- debug_printf ("handling possible target event\n");
-
- cs.last_ptid = mywait (minus_one_ptid, &cs.last_status,
- TARGET_WNOHANG, 1);
-
- if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED)
- {
- if (gdb_connected () && report_no_resumed)
- push_stop_notification (null_ptid, &cs.last_status);
- }
- else if (cs.last_status.kind != TARGET_WAITKIND_IGNORE)
- {
- int pid = cs.last_ptid.pid ();
- struct process_info *process = find_process_pid (pid);
- int forward_event = !gdb_connected () || process->gdb_detached;
-
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
- {
- mark_breakpoints_out (process);
- target_mourn_inferior (cs.last_ptid);
- }
- else if (cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED)
- ;
- else
- {
- /* We're reporting this thread as stopped. Update its
- "want-stopped" state to what the client wants, until it
- gets a new resume action. */
- current_thread->last_resume_kind = resume_stop;
- current_thread->last_status = cs.last_status;
- }
-
- if (forward_event)
- {
- if (!target_running ())
- {
- /* The last process exited. We're done. */
- exit (0);
- }
-
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED
- || cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED)
- ;
- else
- {
- /* A thread stopped with a signal, but gdb isn't
- connected to handle it. Pass it down to the
- inferior, as if it wasn't being traced. */
- enum gdb_signal signal;
-
- if (debug_threads)
- debug_printf ("GDB not connected; forwarding event %d for"
- " [%s]\n",
- (int) cs.last_status.kind,
- target_pid_to_str (cs.last_ptid));
-
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
- signal = cs.last_status.value.sig;
- else
- signal = GDB_SIGNAL_0;
- target_continue (cs.last_ptid, signal);
- }
- }
- else
- push_stop_notification (cs.last_ptid, &cs.last_status);
- }
-
- /* Be sure to not change the selected thread behind GDB's back.
- Important in the non-stop mode asynchronous protocol. */
- set_desired_thread ();
-
- return 0;
-}
-
-#if GDB_SELF_TEST
-namespace selftests
-{
-
-void
-reset ()
-{}
-
-} // namespace selftests
-#endif /* GDB_SELF_TEST */
--- /dev/null
+/* Main code for remote server for GDB.
+ Copyright (C) 1989-2020 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 "gdbthread.h"
+#include "gdbsupport/agent.h"
+#include "notif.h"
+#include "tdesc.h"
+#include "gdbsupport/rsp-low.h"
+#include "gdbsupport/signals-state-save-restore.h"
+#include <ctype.h>
+#include <unistd.h>
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include "gdbsupport/gdb_vecs.h"
+#include "gdbsupport/gdb_wait.h"
+#include "gdbsupport/btrace-common.h"
+#include "gdbsupport/filestuff.h"
+#include "tracepoint.h"
+#include "dll.h"
+#include "hostio.h"
+#include <vector>
+#include "gdbsupport/common-inferior.h"
+#include "gdbsupport/job-control.h"
+#include "gdbsupport/environ.h"
+#include "filenames.h"
+#include "gdbsupport/pathstuff.h"
+#ifdef USE_XML
+#include "xml-builtin.h"
+#endif
+
+#include "gdbsupport/selftest.h"
+#include "gdbsupport/scope-exit.h"
+
+#define require_running_or_return(BUF) \
+ if (!target_running ()) \
+ { \
+ write_enn (BUF); \
+ return; \
+ }
+
+#define require_running_or_break(BUF) \
+ if (!target_running ()) \
+ { \
+ write_enn (BUF); \
+ break; \
+ }
+
+/* String containing the current directory (what getwd would return). */
+
+char *current_directory;
+
+/* The environment to pass to the inferior when creating it. */
+
+static gdb_environ our_environ;
+
+bool server_waiting;
+
+static bool extended_protocol;
+static bool response_needed;
+static bool exit_requested;
+
+/* --once: Exit after the first connection has closed. */
+bool run_once;
+
+/* Whether to report TARGET_WAITKIND_NO_RESUMED events. */
+static bool report_no_resumed;
+
+bool non_stop;
+
+static struct {
+ /* Set the PROGRAM_PATH. Here we adjust the path of the provided
+ binary if needed. */
+ void set (gdb::unique_xmalloc_ptr<char> &&path)
+ {
+ m_path = std::move (path);
+
+ /* Make sure we're using the absolute path of the inferior when
+ creating it. */
+ if (!contains_dir_separator (m_path.get ()))
+ {
+ int reg_file_errno;
+
+ /* Check if the file is in our CWD. If it is, then we prefix
+ its name with CURRENT_DIRECTORY. Otherwise, we leave the
+ name as-is because we'll try searching for it in $PATH. */
+ if (is_regular_file (m_path.get (), ®_file_errno))
+ m_path = gdb_abspath (m_path.get ());
+ }
+ }
+
+ /* Return the PROGRAM_PATH. */
+ char *get ()
+ { return m_path.get (); }
+
+private:
+ /* The program name, adjusted if needed. */
+ gdb::unique_xmalloc_ptr<char> m_path;
+} program_path;
+static std::vector<char *> program_args;
+static std::string wrapper_argv;
+
+/* The PID of the originally created or attached inferior. Used to
+ send signals to the process when GDB sends us an asynchronous interrupt
+ (user hitting Control-C in the client), and to wait for the child to exit
+ when no longer debugging it. */
+
+unsigned long signal_pid;
+
+/* Set if you want to disable optional thread related packets support
+ in gdbserver, for the sake of testing GDB against stubs that don't
+ support them. */
+bool disable_packet_vCont;
+bool disable_packet_Tthread;
+bool disable_packet_qC;
+bool disable_packet_qfThreadInfo;
+
+static unsigned char *mem_buf;
+
+/* A sub-class of 'struct notif_event' for stop, holding information
+ relative to a single stop reply. We keep a queue of these to
+ push to GDB in non-stop mode. */
+
+struct vstop_notif : public notif_event
+{
+ /* Thread or process that got the event. */
+ ptid_t ptid;
+
+ /* Event info. */
+ struct target_waitstatus status;
+};
+
+/* The current btrace configuration. This is gdbserver's mirror of GDB's
+ btrace configuration. */
+static struct btrace_config current_btrace_conf;
+
+/* The client remote protocol state. */
+
+static client_state g_client_state;
+
+client_state &
+get_client_state ()
+{
+ client_state &cs = g_client_state;
+ return cs;
+}
+
+
+/* Put a stop reply to the stop reply queue. */
+
+static void
+queue_stop_reply (ptid_t ptid, struct target_waitstatus *status)
+{
+ struct vstop_notif *new_notif = new struct vstop_notif;
+
+ new_notif->ptid = ptid;
+ new_notif->status = *status;
+
+ notif_event_enque (¬if_stop, new_notif);
+}
+
+static bool
+remove_all_on_match_ptid (struct notif_event *event, ptid_t filter_ptid)
+{
+ struct vstop_notif *vstop_event = (struct vstop_notif *) event;
+
+ return vstop_event->ptid.matches (filter_ptid);
+}
+
+/* See server.h. */
+
+void
+discard_queued_stop_replies (ptid_t ptid)
+{
+ std::list<notif_event *>::iterator iter, next, end;
+ end = notif_stop.queue.end ();
+ for (iter = notif_stop.queue.begin (); iter != end; iter = next)
+ {
+ next = iter;
+ ++next;
+
+ if (remove_all_on_match_ptid (*iter, ptid))
+ {
+ delete *iter;
+ notif_stop.queue.erase (iter);
+ }
+ }
+}
+
+static void
+vstop_notif_reply (struct notif_event *event, char *own_buf)
+{
+ struct vstop_notif *vstop = (struct vstop_notif *) event;
+
+ prepare_resume_reply (own_buf, vstop->ptid, &vstop->status);
+}
+
+/* Helper for in_queued_stop_replies. */
+
+static bool
+in_queued_stop_replies_ptid (struct notif_event *event, ptid_t filter_ptid)
+{
+ struct vstop_notif *vstop_event = (struct vstop_notif *) event;
+
+ if (vstop_event->ptid.matches (filter_ptid))
+ return true;
+
+ /* Don't resume fork children that GDB does not know about yet. */
+ if ((vstop_event->status.kind == TARGET_WAITKIND_FORKED
+ || vstop_event->status.kind == TARGET_WAITKIND_VFORKED)
+ && vstop_event->status.value.related_pid.matches (filter_ptid))
+ return true;
+
+ return false;
+}
+
+/* See server.h. */
+
+int
+in_queued_stop_replies (ptid_t ptid)
+{
+ for (notif_event *event : notif_stop.queue)
+ {
+ if (in_queued_stop_replies_ptid (event, ptid))
+ return true;
+ }
+
+ return false;
+}
+
+struct notif_server notif_stop =
+{
+ "vStopped", "Stop", {}, vstop_notif_reply,
+};
+
+static int
+target_running (void)
+{
+ return get_first_thread () != NULL;
+}
+
+/* See gdbsupport/common-inferior.h. */
+
+const char *
+get_exec_wrapper ()
+{
+ return !wrapper_argv.empty () ? wrapper_argv.c_str () : NULL;
+}
+
+/* See gdbsupport/common-inferior.h. */
+
+const char *
+get_exec_file (int err)
+{
+ if (err && program_path.get () == NULL)
+ error (_("No executable file specified."));
+
+ return program_path.get ();
+}
+
+/* See server.h. */
+
+gdb_environ *
+get_environ ()
+{
+ return &our_environ;
+}
+
+static int
+attach_inferior (int pid)
+{
+ client_state &cs = get_client_state ();
+ /* myattach should return -1 if attaching is unsupported,
+ 0 if it succeeded, and call error() otherwise. */
+
+ if (find_process_pid (pid) != nullptr)
+ error ("Already attached to process %d\n", pid);
+
+ if (myattach (pid) != 0)
+ return -1;
+
+ fprintf (stderr, "Attached; pid = %d\n", pid);
+ fflush (stderr);
+
+ /* FIXME - It may be that we should get the SIGNAL_PID from the
+ attach function, so that it can be the main thread instead of
+ whichever we were told to attach to. */
+ signal_pid = pid;
+
+ if (!non_stop)
+ {
+ cs.last_ptid = mywait (ptid_t (pid), &cs.last_status, 0, 0);
+
+ /* GDB knows to ignore the first SIGSTOP after attaching to a running
+ process using the "attach" command, but this is different; it's
+ just using "target remote". Pretend it's just starting up. */
+ if (cs.last_status.kind == TARGET_WAITKIND_STOPPED
+ && cs.last_status.value.sig == GDB_SIGNAL_STOP)
+ cs.last_status.value.sig = GDB_SIGNAL_TRAP;
+
+ current_thread->last_resume_kind = resume_stop;
+ current_thread->last_status = cs.last_status;
+ }
+
+ return 0;
+}
+
+/* Decode a qXfer read request. Return 0 if everything looks OK,
+ or -1 otherwise. */
+
+static int
+decode_xfer_read (char *buf, CORE_ADDR *ofs, unsigned int *len)
+{
+ /* After the read marker and annex, qXfer looks like a
+ traditional 'm' packet. */
+ decode_m_packet (buf, ofs, len);
+
+ return 0;
+}
+
+static int
+decode_xfer (char *buf, char **object, char **rw, char **annex, char **offset)
+{
+ /* Extract and NUL-terminate the object. */
+ *object = buf;
+ while (*buf && *buf != ':')
+ buf++;
+ if (*buf == '\0')
+ return -1;
+ *buf++ = 0;
+
+ /* Extract and NUL-terminate the read/write action. */
+ *rw = buf;
+ while (*buf && *buf != ':')
+ buf++;
+ if (*buf == '\0')
+ return -1;
+ *buf++ = 0;
+
+ /* Extract and NUL-terminate the annex. */
+ *annex = buf;
+ while (*buf && *buf != ':')
+ buf++;
+ if (*buf == '\0')
+ return -1;
+ *buf++ = 0;
+
+ *offset = buf;
+ return 0;
+}
+
+/* Write the response to a successful qXfer read. Returns the
+ length of the (binary) data stored in BUF, corresponding
+ to as much of DATA/LEN as we could fit. IS_MORE controls
+ the first character of the response. */
+static int
+write_qxfer_response (char *buf, const gdb_byte *data, int len, int is_more)
+{
+ int out_len;
+
+ if (is_more)
+ buf[0] = 'm';
+ else
+ buf[0] = 'l';
+
+ return remote_escape_output (data, len, 1, (unsigned char *) buf + 1,
+ &out_len, PBUFSIZ - 2) + 1;
+}
+
+/* Handle btrace enabling in BTS format. */
+
+static void
+handle_btrace_enable_bts (struct thread_info *thread)
+{
+ if (thread->btrace != NULL)
+ error (_("Btrace already enabled."));
+
+ current_btrace_conf.format = BTRACE_FORMAT_BTS;
+ thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf);
+}
+
+/* Handle btrace enabling in Intel Processor Trace format. */
+
+static void
+handle_btrace_enable_pt (struct thread_info *thread)
+{
+ if (thread->btrace != NULL)
+ error (_("Btrace already enabled."));
+
+ current_btrace_conf.format = BTRACE_FORMAT_PT;
+ thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf);
+}
+
+/* Handle btrace disabling. */
+
+static void
+handle_btrace_disable (struct thread_info *thread)
+{
+
+ if (thread->btrace == NULL)
+ error (_("Branch tracing not enabled."));
+
+ if (target_disable_btrace (thread->btrace) != 0)
+ error (_("Could not disable branch tracing."));
+
+ thread->btrace = NULL;
+}
+
+/* Handle the "Qbtrace" packet. */
+
+static int
+handle_btrace_general_set (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ struct thread_info *thread;
+ char *op;
+
+ if (!startswith (own_buf, "Qbtrace:"))
+ return 0;
+
+ op = own_buf + strlen ("Qbtrace:");
+
+ if (cs.general_thread == null_ptid
+ || cs.general_thread == minus_one_ptid)
+ {
+ strcpy (own_buf, "E.Must select a single thread.");
+ return -1;
+ }
+
+ thread = find_thread_ptid (cs.general_thread);
+ if (thread == NULL)
+ {
+ strcpy (own_buf, "E.No such thread.");
+ return -1;
+ }
+
+ try
+ {
+ if (strcmp (op, "bts") == 0)
+ handle_btrace_enable_bts (thread);
+ else if (strcmp (op, "pt") == 0)
+ handle_btrace_enable_pt (thread);
+ else if (strcmp (op, "off") == 0)
+ handle_btrace_disable (thread);
+ else
+ error (_("Bad Qbtrace operation. Use bts, pt, or off."));
+
+ write_ok (own_buf);
+ }
+ catch (const gdb_exception_error &exception)
+ {
+ sprintf (own_buf, "E.%s", exception.what ());
+ }
+
+ return 1;
+}
+
+/* Handle the "Qbtrace-conf" packet. */
+
+static int
+handle_btrace_conf_general_set (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ struct thread_info *thread;
+ char *op;
+
+ if (!startswith (own_buf, "Qbtrace-conf:"))
+ return 0;
+
+ op = own_buf + strlen ("Qbtrace-conf:");
+
+ if (cs.general_thread == null_ptid
+ || cs.general_thread == minus_one_ptid)
+ {
+ strcpy (own_buf, "E.Must select a single thread.");
+ return -1;
+ }
+
+ thread = find_thread_ptid (cs.general_thread);
+ if (thread == NULL)
+ {
+ strcpy (own_buf, "E.No such thread.");
+ return -1;
+ }
+
+ if (startswith (op, "bts:size="))
+ {
+ unsigned long size;
+ char *endp = NULL;
+
+ errno = 0;
+ size = strtoul (op + strlen ("bts:size="), &endp, 16);
+ if (endp == NULL || *endp != 0 || errno != 0 || size > UINT_MAX)
+ {
+ strcpy (own_buf, "E.Bad size value.");
+ return -1;
+ }
+
+ current_btrace_conf.bts.size = (unsigned int) size;
+ }
+ else if (strncmp (op, "pt:size=", strlen ("pt:size=")) == 0)
+ {
+ unsigned long size;
+ char *endp = NULL;
+
+ errno = 0;
+ size = strtoul (op + strlen ("pt:size="), &endp, 16);
+ if (endp == NULL || *endp != 0 || errno != 0 || size > UINT_MAX)
+ {
+ strcpy (own_buf, "E.Bad size value.");
+ return -1;
+ }
+
+ current_btrace_conf.pt.size = (unsigned int) size;
+ }
+ else
+ {
+ strcpy (own_buf, "E.Bad Qbtrace configuration option.");
+ return -1;
+ }
+
+ write_ok (own_buf);
+ return 1;
+}
+
+/* Handle all of the extended 'Q' packets. */
+
+static void
+handle_general_set (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ if (startswith (own_buf, "QPassSignals:"))
+ {
+ int numsigs = (int) GDB_SIGNAL_LAST, i;
+ const char *p = own_buf + strlen ("QPassSignals:");
+ CORE_ADDR cursig;
+
+ p = decode_address_to_semicolon (&cursig, p);
+ for (i = 0; i < numsigs; i++)
+ {
+ if (i == cursig)
+ {
+ cs.pass_signals[i] = 1;
+ if (*p == '\0')
+ /* Keep looping, to clear the remaining signals. */
+ cursig = -1;
+ else
+ p = decode_address_to_semicolon (&cursig, p);
+ }
+ else
+ cs.pass_signals[i] = 0;
+ }
+ strcpy (own_buf, "OK");
+ return;
+ }
+
+ if (startswith (own_buf, "QProgramSignals:"))
+ {
+ int numsigs = (int) GDB_SIGNAL_LAST, i;
+ const char *p = own_buf + strlen ("QProgramSignals:");
+ CORE_ADDR cursig;
+
+ cs.program_signals_p = 1;
+
+ p = decode_address_to_semicolon (&cursig, p);
+ for (i = 0; i < numsigs; i++)
+ {
+ if (i == cursig)
+ {
+ cs.program_signals[i] = 1;
+ if (*p == '\0')
+ /* Keep looping, to clear the remaining signals. */
+ cursig = -1;
+ else
+ p = decode_address_to_semicolon (&cursig, p);
+ }
+ else
+ cs.program_signals[i] = 0;
+ }
+ strcpy (own_buf, "OK");
+ return;
+ }
+
+ if (startswith (own_buf, "QCatchSyscalls:"))
+ {
+ const char *p = own_buf + sizeof ("QCatchSyscalls:") - 1;
+ int enabled = -1;
+ CORE_ADDR sysno;
+ struct process_info *process;
+
+ if (!target_running () || !target_supports_catch_syscall ())
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ if (strcmp (p, "0") == 0)
+ enabled = 0;
+ else if (p[0] == '1' && (p[1] == ';' || p[1] == '\0'))
+ enabled = 1;
+ else
+ {
+ fprintf (stderr, "Unknown catch-syscalls mode requested: %s\n",
+ own_buf);
+ write_enn (own_buf);
+ return;
+ }
+
+ process = current_process ();
+ process->syscalls_to_catch.clear ();
+
+ if (enabled)
+ {
+ p += 1;
+ if (*p == ';')
+ {
+ p += 1;
+ while (*p != '\0')
+ {
+ p = decode_address_to_semicolon (&sysno, p);
+ process->syscalls_to_catch.push_back (sysno);
+ }
+ }
+ else
+ process->syscalls_to_catch.push_back (ANY_SYSCALL);
+ }
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (strcmp (own_buf, "QEnvironmentReset") == 0)
+ {
+ our_environ = gdb_environ::from_host_environ ();
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QEnvironmentHexEncoded:"))
+ {
+ const char *p = own_buf + sizeof ("QEnvironmentHexEncoded:") - 1;
+ /* The final form of the environment variable. FINAL_VAR will
+ hold the 'VAR=VALUE' format. */
+ std::string final_var = hex2str (p);
+ std::string var_name, var_value;
+
+ if (remote_debug)
+ {
+ debug_printf (_("[QEnvironmentHexEncoded received '%s']\n"), p);
+ debug_printf (_("[Environment variable to be set: '%s']\n"),
+ final_var.c_str ());
+ debug_flush ();
+ }
+
+ size_t pos = final_var.find ('=');
+ if (pos == std::string::npos)
+ {
+ warning (_("Unexpected format for environment variable: '%s'"),
+ final_var.c_str ());
+ write_enn (own_buf);
+ return;
+ }
+
+ var_name = final_var.substr (0, pos);
+ var_value = final_var.substr (pos + 1, std::string::npos);
+
+ our_environ.set (var_name.c_str (), var_value.c_str ());
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QEnvironmentUnset:"))
+ {
+ const char *p = own_buf + sizeof ("QEnvironmentUnset:") - 1;
+ std::string varname = hex2str (p);
+
+ if (remote_debug)
+ {
+ debug_printf (_("[QEnvironmentUnset received '%s']\n"), p);
+ debug_printf (_("[Environment variable to be unset: '%s']\n"),
+ varname.c_str ());
+ debug_flush ();
+ }
+
+ our_environ.unset (varname.c_str ());
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (strcmp (own_buf, "QStartNoAckMode") == 0)
+ {
+ if (remote_debug)
+ {
+ debug_printf ("[noack mode enabled]\n");
+ debug_flush ();
+ }
+
+ cs.noack_mode = 1;
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QNonStop:"))
+ {
+ char *mode = own_buf + 9;
+ int req = -1;
+ const char *req_str;
+
+ if (strcmp (mode, "0") == 0)
+ req = 0;
+ else if (strcmp (mode, "1") == 0)
+ req = 1;
+ else
+ {
+ /* We don't know what this mode is, so complain to
+ GDB. */
+ fprintf (stderr, "Unknown non-stop mode requested: %s\n",
+ own_buf);
+ write_enn (own_buf);
+ return;
+ }
+
+ req_str = req ? "non-stop" : "all-stop";
+ if (start_non_stop (req) != 0)
+ {
+ fprintf (stderr, "Setting %s mode failed\n", req_str);
+ write_enn (own_buf);
+ return;
+ }
+
+ non_stop = (req != 0);
+
+ if (remote_debug)
+ debug_printf ("[%s mode enabled]\n", req_str);
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QDisableRandomization:"))
+ {
+ char *packet = own_buf + strlen ("QDisableRandomization:");
+ ULONGEST setting;
+
+ unpack_varlen_hex (packet, &setting);
+ cs.disable_randomization = setting;
+
+ if (remote_debug)
+ {
+ debug_printf (cs.disable_randomization
+ ? "[address space randomization disabled]\n"
+ : "[address space randomization enabled]\n");
+ }
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (target_supports_tracepoints ()
+ && handle_tracepoint_general_set (own_buf))
+ return;
+
+ if (startswith (own_buf, "QAgent:"))
+ {
+ char *mode = own_buf + strlen ("QAgent:");
+ int req = 0;
+
+ if (strcmp (mode, "0") == 0)
+ req = 0;
+ else if (strcmp (mode, "1") == 0)
+ req = 1;
+ else
+ {
+ /* We don't know what this value is, so complain to GDB. */
+ sprintf (own_buf, "E.Unknown QAgent value");
+ return;
+ }
+
+ /* Update the flag. */
+ use_agent = req;
+ if (remote_debug)
+ debug_printf ("[%s agent]\n", req ? "Enable" : "Disable");
+ write_ok (own_buf);
+ return;
+ }
+
+ if (handle_btrace_general_set (own_buf))
+ return;
+
+ if (handle_btrace_conf_general_set (own_buf))
+ return;
+
+ if (startswith (own_buf, "QThreadEvents:"))
+ {
+ char *mode = own_buf + strlen ("QThreadEvents:");
+ enum tribool req = TRIBOOL_UNKNOWN;
+
+ if (strcmp (mode, "0") == 0)
+ req = TRIBOOL_FALSE;
+ else if (strcmp (mode, "1") == 0)
+ req = TRIBOOL_TRUE;
+ else
+ {
+ /* We don't know what this mode is, so complain to GDB. */
+ sprintf (own_buf, "E.Unknown thread-events mode requested: %s\n",
+ mode);
+ return;
+ }
+
+ cs.report_thread_events = (req == TRIBOOL_TRUE);
+
+ if (remote_debug)
+ {
+ const char *req_str = cs.report_thread_events ? "enabled" : "disabled";
+
+ debug_printf ("[thread events are now %s]\n", req_str);
+ }
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QStartupWithShell:"))
+ {
+ const char *value = own_buf + strlen ("QStartupWithShell:");
+
+ if (strcmp (value, "1") == 0)
+ startup_with_shell = true;
+ else if (strcmp (value, "0") == 0)
+ startup_with_shell = false;
+ else
+ {
+ /* Unknown value. */
+ fprintf (stderr, "Unknown value to startup-with-shell: %s\n",
+ own_buf);
+ write_enn (own_buf);
+ return;
+ }
+
+ if (remote_debug)
+ debug_printf (_("[Inferior will %s started with shell]"),
+ startup_with_shell ? "be" : "not be");
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QSetWorkingDir:"))
+ {
+ const char *p = own_buf + strlen ("QSetWorkingDir:");
+
+ if (*p != '\0')
+ {
+ std::string path = hex2str (p);
+
+ set_inferior_cwd (path.c_str ());
+
+ if (remote_debug)
+ debug_printf (_("[Set the inferior's current directory to %s]\n"),
+ path.c_str ());
+ }
+ else
+ {
+ /* An empty argument means that we should clear out any
+ previously set cwd for the inferior. */
+ set_inferior_cwd (NULL);
+
+ if (remote_debug)
+ debug_printf (_("\
+[Unset the inferior's current directory; will use gdbserver's cwd]\n"));
+ }
+ write_ok (own_buf);
+
+ return;
+ }
+
+ /* Otherwise we didn't know what packet it was. Say we didn't
+ understand it. */
+ own_buf[0] = 0;
+}
+
+static const char *
+get_features_xml (const char *annex)
+{
+ const struct target_desc *desc = current_target_desc ();
+
+ /* `desc->xmltarget' defines what to return when looking for the
+ "target.xml" file. Its contents can either be verbatim XML code
+ (prefixed with a '@') or else the name of the actual XML file to
+ be used in place of "target.xml".
+
+ This variable is set up from the auto-generated
+ init_registers_... routine for the current target. */
+
+ if (strcmp (annex, "target.xml") == 0)
+ {
+ const char *ret = tdesc_get_features_xml (desc);
+
+ if (*ret == '@')
+ return ret + 1;
+ else
+ annex = ret;
+ }
+
+#ifdef USE_XML
+ {
+ int i;
+
+ /* Look for the annex. */
+ for (i = 0; xml_builtin[i][0] != NULL; i++)
+ if (strcmp (annex, xml_builtin[i][0]) == 0)
+ break;
+
+ if (xml_builtin[i][0] != NULL)
+ return xml_builtin[i][1];
+ }
+#endif
+
+ return NULL;
+}
+
+static void
+monitor_show_help (void)
+{
+ monitor_output ("The following monitor commands are supported:\n");
+ monitor_output (" set debug <0|1>\n");
+ monitor_output (" Enable general debugging messages\n");
+ monitor_output (" set debug-hw-points <0|1>\n");
+ monitor_output (" Enable h/w breakpoint/watchpoint debugging messages\n");
+ monitor_output (" set remote-debug <0|1>\n");
+ monitor_output (" Enable remote protocol debugging messages\n");
+ monitor_output (" set debug-format option1[,option2,...]\n");
+ monitor_output (" Add additional information to debugging messages\n");
+ monitor_output (" Options: all, none");
+ monitor_output (", timestamp");
+ monitor_output ("\n");
+ monitor_output (" exit\n");
+ monitor_output (" Quit GDBserver\n");
+}
+
+/* Read trace frame or inferior memory. Returns the number of bytes
+ actually read, zero when no further transfer is possible, and -1 on
+ error. Return of a positive value smaller than LEN does not
+ indicate there's no more to be read, only the end of the transfer.
+ E.g., when GDB reads memory from a traceframe, a first request may
+ be served from a memory block that does not cover the whole request
+ length. A following request gets the rest served from either
+ another block (of the same traceframe) or from the read-only
+ regions. */
+
+static int
+gdb_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ client_state &cs = get_client_state ();
+ int res;
+
+ if (cs.current_traceframe >= 0)
+ {
+ ULONGEST nbytes;
+ ULONGEST length = len;
+
+ if (traceframe_read_mem (cs.current_traceframe,
+ memaddr, myaddr, len, &nbytes))
+ return -1;
+ /* Data read from trace buffer, we're done. */
+ if (nbytes > 0)
+ return nbytes;
+ if (!in_readonly_region (memaddr, length))
+ return -1;
+ /* Otherwise we have a valid readonly case, fall through. */
+ /* (assume no half-trace half-real blocks for now) */
+ }
+
+ res = prepare_to_access_memory ();
+ if (res == 0)
+ {
+ if (set_desired_thread ())
+ res = read_inferior_memory (memaddr, myaddr, len);
+ else
+ res = 1;
+ done_accessing_memory ();
+
+ return res == 0 ? len : -1;
+ }
+ else
+ return -1;
+}
+
+/* Write trace frame or inferior memory. Actually, writing to trace
+ frames is forbidden. */
+
+static int
+gdb_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ client_state &cs = get_client_state ();
+ if (cs.current_traceframe >= 0)
+ return EIO;
+ else
+ {
+ int ret;
+
+ ret = prepare_to_access_memory ();
+ if (ret == 0)
+ {
+ if (set_desired_thread ())
+ ret = target_write_memory (memaddr, myaddr, len);
+ else
+ ret = EIO;
+ done_accessing_memory ();
+ }
+ return ret;
+ }
+}
+
+/* Subroutine of handle_search_memory to simplify it. */
+
+static int
+handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
+ gdb_byte *pattern, unsigned pattern_len,
+ gdb_byte *search_buf,
+ unsigned chunk_size, unsigned search_buf_size,
+ CORE_ADDR *found_addrp)
+{
+ /* Prime the search buffer. */
+
+ if (gdb_read_memory (start_addr, search_buf, search_buf_size)
+ != search_buf_size)
+ {
+ warning ("Unable to access %ld bytes of target "
+ "memory at 0x%lx, halting search.",
+ (long) search_buf_size, (long) start_addr);
+ return -1;
+ }
+
+ /* Perform the search.
+
+ The loop is kept simple by allocating [N + pattern-length - 1] bytes.
+ When we've scanned N bytes we copy the trailing bytes to the start and
+ read in another N bytes. */
+
+ while (search_space_len >= pattern_len)
+ {
+ gdb_byte *found_ptr;
+ unsigned nr_search_bytes = (search_space_len < search_buf_size
+ ? search_space_len
+ : search_buf_size);
+
+ found_ptr = (gdb_byte *) memmem (search_buf, nr_search_bytes, pattern,
+ pattern_len);
+
+ if (found_ptr != NULL)
+ {
+ CORE_ADDR found_addr = start_addr + (found_ptr - search_buf);
+ *found_addrp = found_addr;
+ return 1;
+ }
+
+ /* Not found in this chunk, skip to next chunk. */
+
+ /* Don't let search_space_len wrap here, it's unsigned. */
+ if (search_space_len >= chunk_size)
+ search_space_len -= chunk_size;
+ else
+ search_space_len = 0;
+
+ if (search_space_len >= pattern_len)
+ {
+ unsigned keep_len = search_buf_size - chunk_size;
+ CORE_ADDR read_addr = start_addr + chunk_size + keep_len;
+ int nr_to_read;
+
+ /* Copy the trailing part of the previous iteration to the front
+ of the buffer for the next iteration. */
+ memcpy (search_buf, search_buf + chunk_size, keep_len);
+
+ nr_to_read = (search_space_len - keep_len < chunk_size
+ ? search_space_len - keep_len
+ : chunk_size);
+
+ if (gdb_read_memory (read_addr, search_buf + keep_len,
+ nr_to_read) != search_buf_size)
+ {
+ warning ("Unable to access %ld bytes of target memory "
+ "at 0x%lx, halting search.",
+ (long) nr_to_read, (long) read_addr);
+ return -1;
+ }
+
+ start_addr += chunk_size;
+ }
+ }
+
+ /* Not found. */
+
+ return 0;
+}
+
+/* Handle qSearch:memory packets. */
+
+static void
+handle_search_memory (char *own_buf, int packet_len)
+{
+ CORE_ADDR start_addr;
+ CORE_ADDR search_space_len;
+ gdb_byte *pattern;
+ unsigned int pattern_len;
+ /* NOTE: also defined in find.c testcase. */
+#define SEARCH_CHUNK_SIZE 16000
+ const unsigned chunk_size = SEARCH_CHUNK_SIZE;
+ /* Buffer to hold memory contents for searching. */
+ gdb_byte *search_buf;
+ unsigned search_buf_size;
+ int found;
+ CORE_ADDR found_addr;
+ int cmd_name_len = sizeof ("qSearch:memory:") - 1;
+
+ pattern = (gdb_byte *) malloc (packet_len);
+ if (pattern == NULL)
+ {
+ error ("Unable to allocate memory to perform the search");
+ strcpy (own_buf, "E00");
+ return;
+ }
+ if (decode_search_memory_packet (own_buf + cmd_name_len,
+ packet_len - cmd_name_len,
+ &start_addr, &search_space_len,
+ pattern, &pattern_len) < 0)
+ {
+ free (pattern);
+ error ("Error in parsing qSearch:memory packet");
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ search_buf_size = chunk_size + pattern_len - 1;
+
+ /* No point in trying to allocate a buffer larger than the search space. */
+ if (search_space_len < search_buf_size)
+ search_buf_size = search_space_len;
+
+ search_buf = (gdb_byte *) malloc (search_buf_size);
+ if (search_buf == NULL)
+ {
+ free (pattern);
+ error ("Unable to allocate memory to perform the search");
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ found = handle_search_memory_1 (start_addr, search_space_len,
+ pattern, pattern_len,
+ search_buf, chunk_size, search_buf_size,
+ &found_addr);
+
+ if (found > 0)
+ sprintf (own_buf, "1,%lx", (long) found_addr);
+ else if (found == 0)
+ strcpy (own_buf, "0");
+ else
+ strcpy (own_buf, "E00");
+
+ free (search_buf);
+ free (pattern);
+}
+
+/* Handle the "D" packet. */
+
+static void
+handle_detach (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+
+ process_info *process;
+
+ if (cs.multi_process)
+ {
+ /* skip 'D;' */
+ int pid = strtol (&own_buf[2], NULL, 16);
+
+ process = find_process_pid (pid);
+ }
+ else
+ {
+ process = (current_thread != nullptr
+ ? get_thread_process (current_thread)
+ : nullptr);
+ }
+
+ if (process == NULL)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ if ((tracing && disconnected_tracing) || any_persistent_commands (process))
+ {
+ if (tracing && disconnected_tracing)
+ fprintf (stderr,
+ "Disconnected tracing in effect, "
+ "leaving gdbserver attached to the process\n");
+
+ if (any_persistent_commands (process))
+ fprintf (stderr,
+ "Persistent commands are present, "
+ "leaving gdbserver attached to the process\n");
+
+ /* Make sure we're in non-stop/async mode, so we we can both
+ wait for an async socket accept, and handle async target
+ events simultaneously. There's also no point either in
+ having the target stop all threads, when we're going to
+ pass signals down without informing GDB. */
+ if (!non_stop)
+ {
+ if (debug_threads)
+ debug_printf ("Forcing non-stop mode\n");
+
+ non_stop = true;
+ start_non_stop (1);
+ }
+
+ process->gdb_detached = 1;
+
+ /* Detaching implicitly resumes all threads. */
+ target_continue_no_signal (minus_one_ptid);
+
+ write_ok (own_buf);
+ return;
+ }
+
+ fprintf (stderr, "Detaching from process %d\n", process->pid);
+ stop_tracing ();
+
+ /* We'll need this after PROCESS has been destroyed. */
+ int pid = process->pid;
+
+ if (detach_inferior (process) != 0)
+ write_enn (own_buf);
+ else
+ {
+ discard_queued_stop_replies (ptid_t (pid));
+ write_ok (own_buf);
+
+ if (extended_protocol || target_running ())
+ {
+ /* There is still at least one inferior remaining or
+ we are in extended mode, so don't terminate gdbserver,
+ and instead treat this like a normal program exit. */
+ cs.last_status.kind = TARGET_WAITKIND_EXITED;
+ cs.last_status.value.integer = 0;
+ cs.last_ptid = ptid_t (pid);
+
+ current_thread = NULL;
+ }
+ else
+ {
+ putpkt (own_buf);
+ remote_close ();
+
+ /* If we are attached, then we can exit. Otherwise, we
+ need to hang around doing nothing, until the child is
+ gone. */
+ join_inferior (pid);
+ exit (0);
+ }
+ }
+}
+
+/* Parse options to --debug-format= and "monitor set debug-format".
+ ARG is the text after "--debug-format=" or "monitor set debug-format".
+ IS_MONITOR is non-zero if we're invoked via "monitor set debug-format".
+ This triggers calls to monitor_output.
+ The result is an empty string if all options were parsed ok, otherwise an
+ error message which the caller must free.
+
+ N.B. These commands affect all debug format settings, they are not
+ cumulative. If a format is not specified, it is turned off.
+ However, we don't go to extra trouble with things like
+ "monitor set debug-format all,none,timestamp".
+ Instead we just parse them one at a time, in order.
+
+ The syntax for "monitor set debug" we support here is not identical
+ to gdb's "set debug foo on|off" because we also use this function to
+ parse "--debug-format=foo,bar". */
+
+static std::string
+parse_debug_format_options (const char *arg, int is_monitor)
+{
+ /* First turn all debug format options off. */
+ debug_timestamp = 0;
+
+ /* First remove leading spaces, for "monitor set debug-format". */
+ while (isspace (*arg))
+ ++arg;
+
+ std::vector<gdb::unique_xmalloc_ptr<char>> options
+ = delim_string_to_char_ptr_vec (arg, ',');
+
+ for (const gdb::unique_xmalloc_ptr<char> &option : options)
+ {
+ if (strcmp (option.get (), "all") == 0)
+ {
+ debug_timestamp = 1;
+ if (is_monitor)
+ monitor_output ("All extra debug format options enabled.\n");
+ }
+ else if (strcmp (option.get (), "none") == 0)
+ {
+ debug_timestamp = 0;
+ if (is_monitor)
+ monitor_output ("All extra debug format options disabled.\n");
+ }
+ else if (strcmp (option.get (), "timestamp") == 0)
+ {
+ debug_timestamp = 1;
+ if (is_monitor)
+ monitor_output ("Timestamps will be added to debug output.\n");
+ }
+ else if (*option == '\0')
+ {
+ /* An empty option, e.g., "--debug-format=foo,,bar", is ignored. */
+ continue;
+ }
+ else
+ return string_printf ("Unknown debug-format argument: \"%s\"\n",
+ option.get ());
+ }
+
+ return std::string ();
+}
+
+/* Handle monitor commands not handled by target-specific handlers. */
+
+static void
+handle_monitor_command (char *mon, char *own_buf)
+{
+ if (strcmp (mon, "set debug 1") == 0)
+ {
+ debug_threads = 1;
+ monitor_output ("Debug output enabled.\n");
+ }
+ else if (strcmp (mon, "set debug 0") == 0)
+ {
+ debug_threads = 0;
+ monitor_output ("Debug output disabled.\n");
+ }
+ else if (strcmp (mon, "set debug-hw-points 1") == 0)
+ {
+ show_debug_regs = 1;
+ monitor_output ("H/W point debugging output enabled.\n");
+ }
+ else if (strcmp (mon, "set debug-hw-points 0") == 0)
+ {
+ show_debug_regs = 0;
+ monitor_output ("H/W point debugging output disabled.\n");
+ }
+ else if (strcmp (mon, "set remote-debug 1") == 0)
+ {
+ remote_debug = 1;
+ monitor_output ("Protocol debug output enabled.\n");
+ }
+ else if (strcmp (mon, "set remote-debug 0") == 0)
+ {
+ remote_debug = 0;
+ monitor_output ("Protocol debug output disabled.\n");
+ }
+ else if (startswith (mon, "set debug-format "))
+ {
+ std::string error_msg
+ = parse_debug_format_options (mon + sizeof ("set debug-format ") - 1,
+ 1);
+
+ if (!error_msg.empty ())
+ {
+ monitor_output (error_msg.c_str ());
+ monitor_show_help ();
+ write_enn (own_buf);
+ }
+ }
+ else if (strcmp (mon, "set debug-file") == 0)
+ debug_set_output (nullptr);
+ else if (startswith (mon, "set debug-file "))
+ debug_set_output (mon + sizeof ("set debug-file ") - 1);
+ else if (strcmp (mon, "help") == 0)
+ monitor_show_help ();
+ else if (strcmp (mon, "exit") == 0)
+ exit_requested = true;
+ else
+ {
+ monitor_output ("Unknown monitor command.\n\n");
+ monitor_show_help ();
+ write_enn (own_buf);
+ }
+}
+
+/* Associates a callback with each supported qXfer'able object. */
+
+struct qxfer
+{
+ /* The object this handler handles. */
+ const char *object;
+
+ /* Request that the target transfer up to LEN 8-bit bytes of the
+ target's OBJECT. The OFFSET, for a seekable object, specifies
+ the starting point. The ANNEX can be used to provide additional
+ data-specific information to the target.
+
+ Return the number of bytes actually transfered, zero when no
+ further transfer is possible, -1 on error, -2 when the transfer
+ is not supported, and -3 on a verbose error message that should
+ be preserved. Return of a positive value smaller than LEN does
+ not indicate the end of the object, only the end of the transfer.
+
+ One, and only one, of readbuf or writebuf must be non-NULL. */
+ int (*xfer) (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len);
+};
+
+/* Handle qXfer:auxv:read. */
+
+static int
+handle_qxfer_auxv (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ if (the_target->read_auxv == NULL || writebuf != NULL)
+ return -2;
+
+ if (annex[0] != '\0' || current_thread == NULL)
+ return -1;
+
+ return (*the_target->read_auxv) (offset, readbuf, len);
+}
+
+/* Handle qXfer:exec-file:read. */
+
+static int
+handle_qxfer_exec_file (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ char *file;
+ ULONGEST pid;
+ int total_len;
+
+ if (the_target->pid_to_exec_file == NULL || writebuf != NULL)
+ return -2;
+
+ if (annex[0] == '\0')
+ {
+ if (current_thread == NULL)
+ return -1;
+
+ pid = pid_of (current_thread);
+ }
+ else
+ {
+ annex = unpack_varlen_hex (annex, &pid);
+ if (annex[0] != '\0')
+ return -1;
+ }
+
+ if (pid <= 0)
+ return -1;
+
+ file = (*the_target->pid_to_exec_file) (pid);
+ if (file == NULL)
+ return -1;
+
+ total_len = strlen (file);
+
+ if (offset > total_len)
+ return -1;
+
+ if (offset + len > total_len)
+ len = total_len - offset;
+
+ memcpy (readbuf, file + offset, len);
+ return len;
+}
+
+/* Handle qXfer:features:read. */
+
+static int
+handle_qxfer_features (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ const char *document;
+ size_t total_len;
+
+ if (writebuf != NULL)
+ return -2;
+
+ if (!target_running ())
+ return -1;
+
+ /* Grab the correct annex. */
+ document = get_features_xml (annex);
+ if (document == NULL)
+ return -1;
+
+ total_len = strlen (document);
+
+ if (offset > total_len)
+ return -1;
+
+ if (offset + len > total_len)
+ len = total_len - offset;
+
+ memcpy (readbuf, document + offset, len);
+ return len;
+}
+
+/* Handle qXfer:libraries:read. */
+
+static int
+handle_qxfer_libraries (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ if (writebuf != NULL)
+ return -2;
+
+ if (annex[0] != '\0' || current_thread == NULL)
+ return -1;
+
+ std::string document = "<library-list version=\"1.0\">\n";
+
+ for (const dll_info &dll : all_dlls)
+ document += string_printf
+ (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
+ dll.name.c_str (), paddress (dll.base_addr));
+
+ document += "</library-list>\n";
+
+ if (offset > document.length ())
+ return -1;
+
+ if (offset + len > document.length ())
+ len = document.length () - offset;
+
+ memcpy (readbuf, &document[offset], len);
+
+ return len;
+}
+
+/* Handle qXfer:libraries-svr4:read. */
+
+static int
+handle_qxfer_libraries_svr4 (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ if (writebuf != NULL)
+ return -2;
+
+ if (current_thread == NULL || the_target->qxfer_libraries_svr4 == NULL)
+ return -1;
+
+ return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len);
+}
+
+/* Handle qXfer:osadata:read. */
+
+static int
+handle_qxfer_osdata (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ if (the_target->qxfer_osdata == NULL || writebuf != NULL)
+ return -2;
+
+ return (*the_target->qxfer_osdata) (annex, readbuf, NULL, offset, len);
+}
+
+/* Handle qXfer:siginfo:read and qXfer:siginfo:write. */
+
+static int
+handle_qxfer_siginfo (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ if (the_target->qxfer_siginfo == NULL)
+ return -2;
+
+ if (annex[0] != '\0' || current_thread == NULL)
+ return -1;
+
+ return (*the_target->qxfer_siginfo) (annex, readbuf, writebuf, offset, len);
+}
+
+/* Handle qXfer:statictrace:read. */
+
+static int
+handle_qxfer_statictrace (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ client_state &cs = get_client_state ();
+ ULONGEST nbytes;
+
+ if (writebuf != NULL)
+ return -2;
+
+ if (annex[0] != '\0' || current_thread == NULL
+ || cs.current_traceframe == -1)
+ return -1;
+
+ if (traceframe_read_sdata (cs.current_traceframe, offset,
+ readbuf, len, &nbytes))
+ return -1;
+ return nbytes;
+}
+
+/* Helper for handle_qxfer_threads_proper.
+ Emit the XML to describe the thread of INF. */
+
+static void
+handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
+{
+ ptid_t ptid = ptid_of (thread);
+ char ptid_s[100];
+ int core = target_core_of_thread (ptid);
+ char core_s[21];
+ const char *name = target_thread_name (ptid);
+ int handle_len;
+ gdb_byte *handle;
+ bool handle_status = target_thread_handle (ptid, &handle, &handle_len);
+
+ write_ptid (ptid_s, ptid);
+
+ buffer_xml_printf (buffer, "<thread id=\"%s\"", ptid_s);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, " core=\"%s\"", core_s);
+ }
+
+ if (name != NULL)
+ buffer_xml_printf (buffer, " name=\"%s\"", name);
+
+ if (handle_status)
+ {
+ char *handle_s = (char *) alloca (handle_len * 2 + 1);
+ bin2hex (handle, handle_s, handle_len);
+ buffer_xml_printf (buffer, " handle=\"%s\"", handle_s);
+ }
+
+ buffer_xml_printf (buffer, "/>\n");
+}
+
+/* Helper for handle_qxfer_threads. */
+
+static void
+handle_qxfer_threads_proper (struct buffer *buffer)
+{
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for_each_thread ([&] (thread_info *thread)
+ {
+ handle_qxfer_threads_worker (thread, buffer);
+ });
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+/* Handle qXfer:threads:read. */
+
+static int
+handle_qxfer_threads (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (writebuf != NULL)
+ return -2;
+
+ if (annex[0] != '\0')
+ return -1;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_qxfer_threads_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (len > result_length - offset)
+ len = result_length - offset;
+
+ memcpy (readbuf, result + offset, len);
+
+ return len;
+}
+
+/* Handle qXfer:traceframe-info:read. */
+
+static int
+handle_qxfer_traceframe_info (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ client_state &cs = get_client_state ();
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (writebuf != NULL)
+ return -2;
+
+ if (!target_running () || annex[0] != '\0' || cs.current_traceframe == -1)
+ return -1;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+
+ /* When asked for data at offset 0, generate everything and
+ store into 'result'. Successive reads will be served off
+ 'result'. */
+ free (result);
+
+ buffer_init (&buffer);
+
+ traceframe_read_info (cs.current_traceframe, &buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (len > result_length - offset)
+ len = result_length - offset;
+
+ memcpy (readbuf, result + offset, len);
+ return len;
+}
+
+/* Handle qXfer:fdpic:read. */
+
+static int
+handle_qxfer_fdpic (const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+ if (the_target->read_loadmap == NULL)
+ return -2;
+
+ if (current_thread == NULL)
+ return -1;
+
+ return (*the_target->read_loadmap) (annex, offset, readbuf, len);
+}
+
+/* Handle qXfer:btrace:read. */
+
+static int
+handle_qxfer_btrace (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ client_state &cs = get_client_state ();
+ static struct buffer cache;
+ struct thread_info *thread;
+ enum btrace_read_type type;
+ int result;
+
+ if (writebuf != NULL)
+ return -2;
+
+ if (cs.general_thread == null_ptid
+ || cs.general_thread == minus_one_ptid)
+ {
+ strcpy (cs.own_buf, "E.Must select a single thread.");
+ return -3;
+ }
+
+ thread = find_thread_ptid (cs.general_thread);
+ if (thread == NULL)
+ {
+ strcpy (cs.own_buf, "E.No such thread.");
+ return -3;
+ }
+
+ if (thread->btrace == NULL)
+ {
+ strcpy (cs.own_buf, "E.Btrace not enabled.");
+ return -3;
+ }
+
+ if (strcmp (annex, "all") == 0)
+ type = BTRACE_READ_ALL;
+ else if (strcmp (annex, "new") == 0)
+ type = BTRACE_READ_NEW;
+ else if (strcmp (annex, "delta") == 0)
+ type = BTRACE_READ_DELTA;
+ else
+ {
+ strcpy (cs.own_buf, "E.Bad annex.");
+ return -3;
+ }
+
+ if (offset == 0)
+ {
+ buffer_free (&cache);
+
+ try
+ {
+ result = target_read_btrace (thread->btrace, &cache, type);
+ if (result != 0)
+ memcpy (cs.own_buf, cache.buffer, cache.used_size);
+ }
+ catch (const gdb_exception_error &exception)
+ {
+ sprintf (cs.own_buf, "E.%s", exception.what ());
+ result = -1;
+ }
+
+ if (result != 0)
+ return -3;
+ }
+ else if (offset > cache.used_size)
+ {
+ buffer_free (&cache);
+ return -3;
+ }
+
+ if (len > cache.used_size - offset)
+ len = cache.used_size - offset;
+
+ memcpy (readbuf, cache.buffer + offset, len);
+
+ return len;
+}
+
+/* Handle qXfer:btrace-conf:read. */
+
+static int
+handle_qxfer_btrace_conf (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ client_state &cs = get_client_state ();
+ static struct buffer cache;
+ struct thread_info *thread;
+ int result;
+
+ if (writebuf != NULL)
+ return -2;
+
+ if (annex[0] != '\0')
+ return -1;
+
+ if (cs.general_thread == null_ptid
+ || cs.general_thread == minus_one_ptid)
+ {
+ strcpy (cs.own_buf, "E.Must select a single thread.");
+ return -3;
+ }
+
+ thread = find_thread_ptid (cs.general_thread);
+ if (thread == NULL)
+ {
+ strcpy (cs.own_buf, "E.No such thread.");
+ return -3;
+ }
+
+ if (thread->btrace == NULL)
+ {
+ strcpy (cs.own_buf, "E.Btrace not enabled.");
+ return -3;
+ }
+
+ if (offset == 0)
+ {
+ buffer_free (&cache);
+
+ try
+ {
+ result = target_read_btrace_conf (thread->btrace, &cache);
+ if (result != 0)
+ memcpy (cs.own_buf, cache.buffer, cache.used_size);
+ }
+ catch (const gdb_exception_error &exception)
+ {
+ sprintf (cs.own_buf, "E.%s", exception.what ());
+ result = -1;
+ }
+
+ if (result != 0)
+ return -3;
+ }
+ else if (offset > cache.used_size)
+ {
+ buffer_free (&cache);
+ return -3;
+ }
+
+ if (len > cache.used_size - offset)
+ len = cache.used_size - offset;
+
+ memcpy (readbuf, cache.buffer + offset, len);
+
+ return len;
+}
+
+static const struct qxfer qxfer_packets[] =
+ {
+ { "auxv", handle_qxfer_auxv },
+ { "btrace", handle_qxfer_btrace },
+ { "btrace-conf", handle_qxfer_btrace_conf },
+ { "exec-file", handle_qxfer_exec_file},
+ { "fdpic", handle_qxfer_fdpic},
+ { "features", handle_qxfer_features },
+ { "libraries", handle_qxfer_libraries },
+ { "libraries-svr4", handle_qxfer_libraries_svr4 },
+ { "osdata", handle_qxfer_osdata },
+ { "siginfo", handle_qxfer_siginfo },
+ { "statictrace", handle_qxfer_statictrace },
+ { "threads", handle_qxfer_threads },
+ { "traceframe-info", handle_qxfer_traceframe_info },
+ };
+
+static int
+handle_qxfer (char *own_buf, int packet_len, int *new_packet_len_p)
+{
+ int i;
+ char *object;
+ char *rw;
+ char *annex;
+ char *offset;
+
+ if (!startswith (own_buf, "qXfer:"))
+ return 0;
+
+ /* Grab the object, r/w and annex. */
+ if (decode_xfer (own_buf + 6, &object, &rw, &annex, &offset) < 0)
+ {
+ write_enn (own_buf);
+ return 1;
+ }
+
+ for (i = 0;
+ i < sizeof (qxfer_packets) / sizeof (qxfer_packets[0]);
+ i++)
+ {
+ const struct qxfer *q = &qxfer_packets[i];
+
+ if (strcmp (object, q->object) == 0)
+ {
+ if (strcmp (rw, "read") == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+
+ /* Grab the offset and length. */
+ if (decode_xfer_read (offset, &ofs, &len) < 0)
+ {
+ write_enn (own_buf);
+ return 1;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = (unsigned char *) malloc (len + 1);
+ if (data == NULL)
+ {
+ write_enn (own_buf);
+ return 1;
+ }
+ n = (*q->xfer) (annex, data, NULL, ofs, len + 1);
+ if (n == -2)
+ {
+ free (data);
+ return 0;
+ }
+ else if (n == -3)
+ {
+ /* Preserve error message. */
+ }
+ else if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return 1;
+ }
+ else if (strcmp (rw, "write") == 0)
+ {
+ int n;
+ unsigned int len;
+ CORE_ADDR ofs;
+ unsigned char *data;
+
+ strcpy (own_buf, "E00");
+ data = (unsigned char *) malloc (packet_len - (offset - own_buf));
+ if (data == NULL)
+ {
+ write_enn (own_buf);
+ return 1;
+ }
+ if (decode_xfer_write (offset, packet_len - (offset - own_buf),
+ &ofs, &len, data) < 0)
+ {
+ free (data);
+ write_enn (own_buf);
+ return 1;
+ }
+
+ n = (*q->xfer) (annex, NULL, data, ofs, len);
+ if (n == -2)
+ {
+ free (data);
+ return 0;
+ }
+ else if (n == -3)
+ {
+ /* Preserve error message. */
+ }
+ else if (n < 0)
+ write_enn (own_buf);
+ else
+ sprintf (own_buf, "%x", n);
+
+ free (data);
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Compute 32 bit CRC from inferior memory.
+
+ On success, return 32 bit CRC.
+ On failure, return (unsigned long long) -1. */
+
+static unsigned long long
+crc32 (CORE_ADDR base, int len, unsigned int crc)
+{
+ while (len--)
+ {
+ unsigned char byte = 0;
+
+ /* Return failure if memory read fails. */
+ if (read_inferior_memory (base, &byte, 1) != 0)
+ return (unsigned long long) -1;
+
+ crc = xcrc32 (&byte, 1, crc);
+ base++;
+ }
+ return (unsigned long long) crc;
+}
+
+/* Add supported btrace packets to BUF. */
+
+static void
+supported_btrace_packets (char *buf)
+{
+ strcat (buf, ";Qbtrace:bts+");
+ strcat (buf, ";Qbtrace-conf:bts:size+");
+ strcat (buf, ";Qbtrace:pt+");
+ strcat (buf, ";Qbtrace-conf:pt:size+");
+ strcat (buf, ";Qbtrace:off+");
+ strcat (buf, ";qXfer:btrace:read+");
+ strcat (buf, ";qXfer:btrace-conf:read+");
+}
+
+/* Handle all of the extended 'q' packets. */
+
+static void
+handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
+{
+ client_state &cs = get_client_state ();
+ static std::list<thread_info *>::const_iterator thread_iter;
+
+ /* Reply the current thread id. */
+ if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC)
+ {
+ ptid_t ptid;
+ require_running_or_return (own_buf);
+
+ if (cs.general_thread != null_ptid && cs.general_thread != minus_one_ptid)
+ ptid = cs.general_thread;
+ else
+ {
+ thread_iter = all_threads.begin ();
+ ptid = (*thread_iter)->id;
+ }
+
+ sprintf (own_buf, "QC");
+ own_buf += 2;
+ write_ptid (own_buf, ptid);
+ return;
+ }
+
+ if (strcmp ("qSymbol::", own_buf) == 0)
+ {
+ struct thread_info *save_thread = current_thread;
+
+ /* For qSymbol, GDB only changes the current thread if the
+ previous current thread was of a different process. So if
+ the previous thread is gone, we need to pick another one of
+ the same process. This can happen e.g., if we followed an
+ exec in a non-leader thread. */
+ if (current_thread == NULL)
+ {
+ current_thread
+ = find_any_thread_of_pid (cs.general_thread.pid ());
+
+ /* Just in case, if we didn't find a thread, then bail out
+ instead of crashing. */
+ if (current_thread == NULL)
+ {
+ write_enn (own_buf);
+ current_thread = save_thread;
+ return;
+ }
+ }
+
+ /* GDB is suggesting new symbols have been loaded. This may
+ mean a new shared library has been detected as loaded, so
+ take the opportunity to check if breakpoints we think are
+ inserted, still are. Note that it isn't guaranteed that
+ we'll see this when a shared library is loaded, and nor will
+ we see this for unloads (although breakpoints in unloaded
+ libraries shouldn't trigger), as GDB may not find symbols for
+ the library at all. We also re-validate breakpoints when we
+ see a second GDB breakpoint for the same address, and or when
+ we access breakpoint shadows. */
+ validate_breakpoints ();
+
+ if (target_supports_tracepoints ())
+ tracepoint_look_up_symbols ();
+
+ if (current_thread != NULL && the_target->look_up_symbols != NULL)
+ (*the_target->look_up_symbols) ();
+
+ current_thread = save_thread;
+
+ strcpy (own_buf, "OK");
+ return;
+ }
+
+ if (!disable_packet_qfThreadInfo)
+ {
+ if (strcmp ("qfThreadInfo", own_buf) == 0)
+ {
+ require_running_or_return (own_buf);
+ thread_iter = all_threads.begin ();
+
+ *own_buf++ = 'm';
+ ptid_t ptid = (*thread_iter)->id;
+ write_ptid (own_buf, ptid);
+ thread_iter++;
+ return;
+ }
+
+ if (strcmp ("qsThreadInfo", own_buf) == 0)
+ {
+ require_running_or_return (own_buf);
+ if (thread_iter != all_threads.end ())
+ {
+ *own_buf++ = 'm';
+ ptid_t ptid = (*thread_iter)->id;
+ write_ptid (own_buf, ptid);
+ thread_iter++;
+ return;
+ }
+ else
+ {
+ sprintf (own_buf, "l");
+ return;
+ }
+ }
+ }
+
+ if (the_target->read_offsets != NULL
+ && strcmp ("qOffsets", own_buf) == 0)
+ {
+ CORE_ADDR text, data;
+
+ require_running_or_return (own_buf);
+ if (the_target->read_offsets (&text, &data))
+ sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX",
+ (long)text, (long)data, (long)data);
+ else
+ write_enn (own_buf);
+
+ return;
+ }
+
+ /* Protocol features query. */
+ if (startswith (own_buf, "qSupported")
+ && (own_buf[10] == ':' || own_buf[10] == '\0'))
+ {
+ char *p = &own_buf[10];
+ int gdb_supports_qRelocInsn = 0;
+
+ /* Process each feature being provided by GDB. The first
+ feature will follow a ':', and latter features will follow
+ ';'. */
+ if (*p == ':')
+ {
+ char **qsupported = NULL;
+ int count = 0;
+ int unknown = 0;
+ int i;
+
+ /* Two passes, to avoid nested strtok calls in
+ target_process_qsupported. */
+ char *saveptr;
+ for (p = strtok_r (p + 1, ";", &saveptr);
+ p != NULL;
+ p = strtok_r (NULL, ";", &saveptr))
+ {
+ count++;
+ qsupported = XRESIZEVEC (char *, qsupported, count);
+ qsupported[count - 1] = xstrdup (p);
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ p = qsupported[i];
+ if (strcmp (p, "multiprocess+") == 0)
+ {
+ /* GDB supports and wants multi-process support if
+ possible. */
+ if (target_supports_multi_process ())
+ cs.multi_process = 1;
+ }
+ else if (strcmp (p, "qRelocInsn+") == 0)
+ {
+ /* GDB supports relocate instruction requests. */
+ gdb_supports_qRelocInsn = 1;
+ }
+ else if (strcmp (p, "swbreak+") == 0)
+ {
+ /* GDB wants us to report whether a trap is caused
+ by a software breakpoint and for us to handle PC
+ adjustment if necessary on this target. */
+ if (target_supports_stopped_by_sw_breakpoint ())
+ cs.swbreak_feature = 1;
+ }
+ else if (strcmp (p, "hwbreak+") == 0)
+ {
+ /* GDB wants us to report whether a trap is caused
+ by a hardware breakpoint. */
+ if (target_supports_stopped_by_hw_breakpoint ())
+ cs.hwbreak_feature = 1;
+ }
+ else if (strcmp (p, "fork-events+") == 0)
+ {
+ /* GDB supports and wants fork events if possible. */
+ if (target_supports_fork_events ())
+ cs.report_fork_events = 1;
+ }
+ else if (strcmp (p, "vfork-events+") == 0)
+ {
+ /* GDB supports and wants vfork events if possible. */
+ if (target_supports_vfork_events ())
+ cs.report_vfork_events = 1;
+ }
+ else if (strcmp (p, "exec-events+") == 0)
+ {
+ /* GDB supports and wants exec events if possible. */
+ if (target_supports_exec_events ())
+ cs.report_exec_events = 1;
+ }
+ else if (strcmp (p, "vContSupported+") == 0)
+ cs.vCont_supported = 1;
+ else if (strcmp (p, "QThreadEvents+") == 0)
+ ;
+ else if (strcmp (p, "no-resumed+") == 0)
+ {
+ /* GDB supports and wants TARGET_WAITKIND_NO_RESUMED
+ events. */
+ report_no_resumed = true;
+ }
+ else
+ {
+ /* Move the unknown features all together. */
+ qsupported[i] = NULL;
+ qsupported[unknown] = p;
+ unknown++;
+ }
+ }
+
+ /* Give the target backend a chance to process the unknown
+ features. */
+ target_process_qsupported (qsupported, unknown);
+
+ for (i = 0; i < count; i++)
+ free (qsupported[i]);
+ free (qsupported);
+ }
+
+ sprintf (own_buf,
+ "PacketSize=%x;QPassSignals+;QProgramSignals+;"
+ "QStartupWithShell+;QEnvironmentHexEncoded+;"
+ "QEnvironmentReset+;QEnvironmentUnset+;"
+ "QSetWorkingDir+",
+ PBUFSIZ - 1);
+
+ if (target_supports_catch_syscall ())
+ strcat (own_buf, ";QCatchSyscalls+");
+
+ if (the_target->qxfer_libraries_svr4 != NULL)
+ strcat (own_buf, ";qXfer:libraries-svr4:read+"
+ ";augmented-libraries-svr4-read+");
+ else
+ {
+ /* We do not have any hook to indicate whether the non-SVR4 target
+ backend supports qXfer:libraries:read, so always report it. */
+ strcat (own_buf, ";qXfer:libraries:read+");
+ }
+
+ if (the_target->read_auxv != NULL)
+ strcat (own_buf, ";qXfer:auxv:read+");
+
+ if (the_target->qxfer_siginfo != NULL)
+ strcat (own_buf, ";qXfer:siginfo:read+;qXfer:siginfo:write+");
+
+ if (the_target->read_loadmap != NULL)
+ strcat (own_buf, ";qXfer:fdpic:read+");
+
+ /* We always report qXfer:features:read, as targets may
+ install XML files on a subsequent call to arch_setup.
+ If we reported to GDB on startup that we don't support
+ qXfer:feature:read at all, we will never be re-queried. */
+ strcat (own_buf, ";qXfer:features:read+");
+
+ if (cs.transport_is_reliable)
+ strcat (own_buf, ";QStartNoAckMode+");
+
+ if (the_target->qxfer_osdata != NULL)
+ strcat (own_buf, ";qXfer:osdata:read+");
+
+ if (target_supports_multi_process ())
+ strcat (own_buf, ";multiprocess+");
+
+ if (target_supports_fork_events ())
+ strcat (own_buf, ";fork-events+");
+
+ if (target_supports_vfork_events ())
+ strcat (own_buf, ";vfork-events+");
+
+ if (target_supports_exec_events ())
+ strcat (own_buf, ";exec-events+");
+
+ if (target_supports_non_stop ())
+ strcat (own_buf, ";QNonStop+");
+
+ if (target_supports_disable_randomization ())
+ strcat (own_buf, ";QDisableRandomization+");
+
+ strcat (own_buf, ";qXfer:threads:read+");
+
+ if (target_supports_tracepoints ())
+ {
+ strcat (own_buf, ";ConditionalTracepoints+");
+ strcat (own_buf, ";TraceStateVariables+");
+ strcat (own_buf, ";TracepointSource+");
+ strcat (own_buf, ";DisconnectedTracing+");
+ if (gdb_supports_qRelocInsn && target_supports_fast_tracepoints ())
+ strcat (own_buf, ";FastTracepoints+");
+ strcat (own_buf, ";StaticTracepoints+");
+ strcat (own_buf, ";InstallInTrace+");
+ strcat (own_buf, ";qXfer:statictrace:read+");
+ strcat (own_buf, ";qXfer:traceframe-info:read+");
+ strcat (own_buf, ";EnableDisableTracepoints+");
+ strcat (own_buf, ";QTBuffer:size+");
+ strcat (own_buf, ";tracenz+");
+ }
+
+ if (target_supports_hardware_single_step ()
+ || target_supports_software_single_step () )
+ {
+ strcat (own_buf, ";ConditionalBreakpoints+");
+ }
+ strcat (own_buf, ";BreakpointCommands+");
+
+ if (target_supports_agent ())
+ strcat (own_buf, ";QAgent+");
+
+ supported_btrace_packets (own_buf);
+
+ if (target_supports_stopped_by_sw_breakpoint ())
+ strcat (own_buf, ";swbreak+");
+
+ if (target_supports_stopped_by_hw_breakpoint ())
+ strcat (own_buf, ";hwbreak+");
+
+ if (the_target->pid_to_exec_file != NULL)
+ strcat (own_buf, ";qXfer:exec-file:read+");
+
+ strcat (own_buf, ";vContSupported+");
+
+ strcat (own_buf, ";QThreadEvents+");
+
+ strcat (own_buf, ";no-resumed+");
+
+ /* Reinitialize components as needed for the new connection. */
+ hostio_handle_new_gdb_connection ();
+ target_handle_new_gdb_connection ();
+
+ return;
+ }
+
+ /* Thread-local storage support. */
+ if (the_target->get_tls_address != NULL
+ && startswith (own_buf, "qGetTLSAddr:"))
+ {
+ char *p = own_buf + 12;
+ CORE_ADDR parts[2], address = 0;
+ int i, err;
+ ptid_t ptid = null_ptid;
+
+ require_running_or_return (own_buf);
+
+ for (i = 0; i < 3; i++)
+ {
+ char *p2;
+ int len;
+
+ if (p == NULL)
+ break;
+
+ p2 = strchr (p, ',');
+ if (p2)
+ {
+ len = p2 - p;
+ p2++;
+ }
+ else
+ {
+ len = strlen (p);
+ p2 = NULL;
+ }
+
+ if (i == 0)
+ ptid = read_ptid (p, NULL);
+ else
+ decode_address (&parts[i - 1], p, len);
+ p = p2;
+ }
+
+ if (p != NULL || i < 3)
+ err = 1;
+ else
+ {
+ struct thread_info *thread = find_thread_ptid (ptid);
+
+ if (thread == NULL)
+ err = 2;
+ else
+ err = the_target->get_tls_address (thread, parts[0], parts[1],
+ &address);
+ }
+
+ if (err == 0)
+ {
+ strcpy (own_buf, paddress(address));
+ return;
+ }
+ else if (err > 0)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ /* Otherwise, pretend we do not understand this packet. */
+ }
+
+ /* Windows OS Thread Information Block address support. */
+ if (the_target->get_tib_address != NULL
+ && startswith (own_buf, "qGetTIBAddr:"))
+ {
+ const char *annex;
+ int n;
+ CORE_ADDR tlb;
+ ptid_t ptid = read_ptid (own_buf + 12, &annex);
+
+ n = (*the_target->get_tib_address) (ptid, &tlb);
+ if (n == 1)
+ {
+ strcpy (own_buf, paddress(tlb));
+ return;
+ }
+ else if (n == 0)
+ {
+ write_enn (own_buf);
+ return;
+ }
+ return;
+ }
+
+ /* Handle "monitor" commands. */
+ if (startswith (own_buf, "qRcmd,"))
+ {
+ char *mon = (char *) malloc (PBUFSIZ);
+ int len = strlen (own_buf + 6);
+
+ if (mon == NULL)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ if ((len % 2) != 0
+ || hex2bin (own_buf + 6, (gdb_byte *) mon, len / 2) != len / 2)
+ {
+ write_enn (own_buf);
+ free (mon);
+ return;
+ }
+ mon[len / 2] = '\0';
+
+ write_ok (own_buf);
+
+ if (the_target->handle_monitor_command == NULL
+ || (*the_target->handle_monitor_command) (mon) == 0)
+ /* Default processing. */
+ handle_monitor_command (mon, own_buf);
+
+ free (mon);
+ return;
+ }
+
+ if (startswith (own_buf, "qSearch:memory:"))
+ {
+ require_running_or_return (own_buf);
+ handle_search_memory (own_buf, packet_len);
+ return;
+ }
+
+ if (strcmp (own_buf, "qAttached") == 0
+ || startswith (own_buf, "qAttached:"))
+ {
+ struct process_info *process;
+
+ if (own_buf[sizeof ("qAttached") - 1])
+ {
+ int pid = strtoul (own_buf + sizeof ("qAttached:") - 1, NULL, 16);
+ process = find_process_pid (pid);
+ }
+ else
+ {
+ require_running_or_return (own_buf);
+ process = current_process ();
+ }
+
+ if (process == NULL)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ strcpy (own_buf, process->attached ? "1" : "0");
+ return;
+ }
+
+ if (startswith (own_buf, "qCRC:"))
+ {
+ /* CRC check (compare-section). */
+ const char *comma;
+ ULONGEST base;
+ int len;
+ unsigned long long crc;
+
+ require_running_or_return (own_buf);
+ comma = unpack_varlen_hex (own_buf + 5, &base);
+ if (*comma++ != ',')
+ {
+ write_enn (own_buf);
+ return;
+ }
+ len = strtoul (comma, NULL, 16);
+ crc = crc32 (base, len, 0xffffffff);
+ /* Check for memory failure. */
+ if (crc == (unsigned long long) -1)
+ {
+ write_enn (own_buf);
+ return;
+ }
+ sprintf (own_buf, "C%lx", (unsigned long) crc);
+ return;
+ }
+
+ if (handle_qxfer (own_buf, packet_len, new_packet_len_p))
+ return;
+
+ if (target_supports_tracepoints () && handle_tracepoint_query (own_buf))
+ return;
+
+ /* Otherwise we didn't know what packet it was. Say we didn't
+ understand it. */
+ own_buf[0] = 0;
+}
+
+static void gdb_wants_all_threads_stopped (void);
+static void resume (struct thread_resume *actions, size_t n);
+
+/* The callback that is passed to visit_actioned_threads. */
+typedef int (visit_actioned_threads_callback_ftype)
+ (const struct thread_resume *, struct thread_info *);
+
+/* Call CALLBACK for any thread to which ACTIONS applies to. Returns
+ true if CALLBACK returns true. Returns false if no matching thread
+ is found or CALLBACK results false.
+ Note: This function is itself a callback for find_thread. */
+
+static bool
+visit_actioned_threads (thread_info *thread,
+ const struct thread_resume *actions,
+ size_t num_actions,
+ visit_actioned_threads_callback_ftype *callback)
+{
+ for (size_t i = 0; i < num_actions; i++)
+ {
+ const struct thread_resume *action = &actions[i];
+
+ if (action->thread == minus_one_ptid
+ || action->thread == thread->id
+ || ((action->thread.pid ()
+ == thread->id.pid ())
+ && action->thread.lwp () == -1))
+ {
+ if ((*callback) (action, thread))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Callback for visit_actioned_threads. If the thread has a pending
+ status to report, report it now. */
+
+static int
+handle_pending_status (const struct thread_resume *resumption,
+ struct thread_info *thread)
+{
+ client_state &cs = get_client_state ();
+ if (thread->status_pending_p)
+ {
+ thread->status_pending_p = 0;
+
+ cs.last_status = thread->last_status;
+ cs.last_ptid = thread->id;
+ prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status);
+ return 1;
+ }
+ return 0;
+}
+
+/* Parse vCont packets. */
+static void
+handle_v_cont (char *own_buf)
+{
+ const char *p;
+ int n = 0, i = 0;
+ struct thread_resume *resume_info;
+ struct thread_resume default_action { null_ptid };
+
+ /* Count the number of semicolons in the packet. There should be one
+ for every action. */
+ p = &own_buf[5];
+ while (p)
+ {
+ n++;
+ p++;
+ p = strchr (p, ';');
+ }
+
+ resume_info = (struct thread_resume *) malloc (n * sizeof (resume_info[0]));
+ if (resume_info == NULL)
+ goto err;
+
+ p = &own_buf[5];
+ while (*p)
+ {
+ p++;
+
+ memset (&resume_info[i], 0, sizeof resume_info[i]);
+
+ if (p[0] == 's' || p[0] == 'S')
+ resume_info[i].kind = resume_step;
+ else if (p[0] == 'r')
+ resume_info[i].kind = resume_step;
+ else if (p[0] == 'c' || p[0] == 'C')
+ resume_info[i].kind = resume_continue;
+ else if (p[0] == 't')
+ resume_info[i].kind = resume_stop;
+ else
+ goto err;
+
+ if (p[0] == 'S' || p[0] == 'C')
+ {
+ char *q;
+ int sig = strtol (p + 1, &q, 16);
+ if (p == q)
+ goto err;
+ p = q;
+
+ if (!gdb_signal_to_host_p ((enum gdb_signal) sig))
+ goto err;
+ resume_info[i].sig = gdb_signal_to_host ((enum gdb_signal) sig);
+ }
+ else if (p[0] == 'r')
+ {
+ ULONGEST addr;
+
+ p = unpack_varlen_hex (p + 1, &addr);
+ resume_info[i].step_range_start = addr;
+
+ if (*p != ',')
+ goto err;
+
+ p = unpack_varlen_hex (p + 1, &addr);
+ resume_info[i].step_range_end = addr;
+ }
+ else
+ {
+ p = p + 1;
+ }
+
+ if (p[0] == 0)
+ {
+ resume_info[i].thread = minus_one_ptid;
+ default_action = resume_info[i];
+
+ /* Note: we don't increment i here, we'll overwrite this entry
+ the next time through. */
+ }
+ else if (p[0] == ':')
+ {
+ const char *q;
+ ptid_t ptid = read_ptid (p + 1, &q);
+
+ if (p == q)
+ goto err;
+ p = q;
+ if (p[0] != ';' && p[0] != 0)
+ goto err;
+
+ resume_info[i].thread = ptid;
+
+ i++;
+ }
+ }
+
+ if (i < n)
+ resume_info[i] = default_action;
+
+ resume (resume_info, n);
+ free (resume_info);
+ return;
+
+err:
+ write_enn (own_buf);
+ free (resume_info);
+ return;
+}
+
+/* Resume target with ACTIONS, an array of NUM_ACTIONS elements. */
+
+static void
+resume (struct thread_resume *actions, size_t num_actions)
+{
+ client_state &cs = get_client_state ();
+ if (!non_stop)
+ {
+ /* Check if among the threads that GDB wants actioned, there's
+ one with a pending status to report. If so, skip actually
+ resuming/stopping and report the pending event
+ immediately. */
+
+ thread_info *thread_with_status = find_thread ([&] (thread_info *thread)
+ {
+ return visit_actioned_threads (thread, actions, num_actions,
+ handle_pending_status);
+ });
+
+ if (thread_with_status != NULL)
+ return;
+
+ enable_async_io ();
+ }
+
+ (*the_target->resume) (actions, num_actions);
+
+ if (non_stop)
+ write_ok (cs.own_buf);
+ else
+ {
+ cs.last_ptid = mywait (minus_one_ptid, &cs.last_status, 0, 1);
+
+ if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED
+ && !report_no_resumed)
+ {
+ /* The client does not support this stop reply. At least
+ return error. */
+ sprintf (cs.own_buf, "E.No unwaited-for children left.");
+ disable_async_io ();
+ return;
+ }
+
+ if (cs.last_status.kind != TARGET_WAITKIND_EXITED
+ && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED
+ && cs.last_status.kind != TARGET_WAITKIND_NO_RESUMED)
+ current_thread->last_status = cs.last_status;
+
+ /* From the client's perspective, all-stop mode always stops all
+ threads implicitly (and the target backend has already done
+ so by now). Tag all threads as "want-stopped", so we don't
+ resume them implicitly without the client telling us to. */
+ gdb_wants_all_threads_stopped ();
+ prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status);
+ disable_async_io ();
+
+ if (cs.last_status.kind == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
+ target_mourn_inferior (cs.last_ptid);
+ }
+}
+
+/* Attach to a new program. Return 1 if successful, 0 if failure. */
+static int
+handle_v_attach (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ int pid;
+
+ pid = strtol (own_buf + 8, NULL, 16);
+ if (pid != 0 && attach_inferior (pid) == 0)
+ {
+ /* Don't report shared library events after attaching, even if
+ some libraries are preloaded. GDB will always poll the
+ library list. Avoids the "stopped by shared library event"
+ notice on the GDB side. */
+ dlls_changed = 0;
+
+ if (non_stop)
+ {
+ /* In non-stop, we don't send a resume reply. Stop events
+ will follow up using the normal notification
+ mechanism. */
+ write_ok (own_buf);
+ }
+ else
+ prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status);
+
+ return 1;
+ }
+ else
+ {
+ write_enn (own_buf);
+ return 0;
+ }
+}
+
+/* Run a new program. Return 1 if successful, 0 if failure. */
+static int
+handle_v_run (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ char *p, *next_p;
+ std::vector<char *> new_argv;
+ char *new_program_name = NULL;
+ int i, new_argc;
+
+ new_argc = 0;
+ for (p = own_buf + strlen ("vRun;"); p && *p; p = strchr (p, ';'))
+ {
+ p++;
+ new_argc++;
+ }
+
+ for (i = 0, p = own_buf + strlen ("vRun;"); *p; p = next_p, ++i)
+ {
+ next_p = strchr (p, ';');
+ if (next_p == NULL)
+ next_p = p + strlen (p);
+
+ if (i == 0 && p == next_p)
+ {
+ /* No program specified. */
+ new_program_name = NULL;
+ }
+ else if (p == next_p)
+ {
+ /* Empty argument. */
+ new_argv.push_back (xstrdup ("''"));
+ }
+ else
+ {
+ size_t len = (next_p - p) / 2;
+ /* ARG is the unquoted argument received via the RSP. */
+ char *arg = (char *) xmalloc (len + 1);
+ /* FULL_ARGS will contain the quoted version of ARG. */
+ char *full_arg = (char *) xmalloc ((len + 1) * 2);
+ /* These are pointers used to navigate the strings above. */
+ char *tmp_arg = arg;
+ char *tmp_full_arg = full_arg;
+ int need_quote = 0;
+
+ hex2bin (p, (gdb_byte *) arg, len);
+ arg[len] = '\0';
+
+ while (*tmp_arg != '\0')
+ {
+ switch (*tmp_arg)
+ {
+ case '\n':
+ /* Quote \n. */
+ *tmp_full_arg = '\'';
+ ++tmp_full_arg;
+ need_quote = 1;
+ break;
+
+ case '\'':
+ /* Quote single quote. */
+ *tmp_full_arg = '\\';
+ ++tmp_full_arg;
+ break;
+
+ default:
+ break;
+ }
+
+ *tmp_full_arg = *tmp_arg;
+ ++tmp_full_arg;
+ ++tmp_arg;
+ }
+
+ if (need_quote)
+ *tmp_full_arg++ = '\'';
+
+ /* Finish FULL_ARG and push it into the vector containing
+ the argv. */
+ *tmp_full_arg = '\0';
+ if (i == 0)
+ new_program_name = full_arg;
+ else
+ new_argv.push_back (full_arg);
+ xfree (arg);
+ }
+ if (*next_p)
+ next_p++;
+ }
+ new_argv.push_back (NULL);
+
+ if (new_program_name == NULL)
+ {
+ /* GDB didn't specify a program to run. Use the program from the
+ last run with the new argument list. */
+ if (program_path.get () == NULL)
+ {
+ write_enn (own_buf);
+ free_vector_argv (new_argv);
+ return 0;
+ }
+ }
+ else
+ program_path.set (gdb::unique_xmalloc_ptr<char> (new_program_name));
+
+ /* Free the old argv and install the new one. */
+ free_vector_argv (program_args);
+ program_args = new_argv;
+
+ create_inferior (program_path.get (), program_args);
+
+ if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
+ {
+ prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status);
+
+ /* In non-stop, sending a resume reply doesn't set the general
+ thread, but GDB assumes a vRun sets it (this is so GDB can
+ query which is the main thread of the new inferior. */
+ if (non_stop)
+ cs.general_thread = cs.last_ptid;
+
+ return 1;
+ }
+ else
+ {
+ write_enn (own_buf);
+ return 0;
+ }
+}
+
+/* Kill process. Return 1 if successful, 0 if failure. */
+static int
+handle_v_kill (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ int pid;
+ char *p = &own_buf[6];
+ if (cs.multi_process)
+ pid = strtol (p, NULL, 16);
+ else
+ pid = signal_pid;
+
+ process_info *proc = find_process_pid (pid);
+
+ if (proc != nullptr && kill_inferior (proc) == 0)
+ {
+ cs.last_status.kind = TARGET_WAITKIND_SIGNALLED;
+ cs.last_status.value.sig = GDB_SIGNAL_KILL;
+ cs.last_ptid = ptid_t (pid);
+ discard_queued_stop_replies (cs.last_ptid);
+ write_ok (own_buf);
+ return 1;
+ }
+ else
+ {
+ write_enn (own_buf);
+ return 0;
+ }
+}
+
+/* Handle all of the extended 'v' packets. */
+void
+handle_v_requests (char *own_buf, int packet_len, int *new_packet_len)
+{
+ client_state &cs = get_client_state ();
+ if (!disable_packet_vCont)
+ {
+ if (strcmp (own_buf, "vCtrlC") == 0)
+ {
+ (*the_target->request_interrupt) ();
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "vCont;"))
+ {
+ handle_v_cont (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "vCont?"))
+ {
+ strcpy (own_buf, "vCont;c;C;t");
+
+ if (target_supports_hardware_single_step ()
+ || target_supports_software_single_step ()
+ || !cs.vCont_supported)
+ {
+ /* If target supports single step either by hardware or by
+ software, add actions s and S to the list of supported
+ actions. On the other hand, if GDB doesn't request the
+ supported vCont actions in qSupported packet, add s and
+ S to the list too. */
+ own_buf = own_buf + strlen (own_buf);
+ strcpy (own_buf, ";s;S");
+ }
+
+ if (target_supports_range_stepping ())
+ {
+ own_buf = own_buf + strlen (own_buf);
+ strcpy (own_buf, ";r");
+ }
+ return;
+ }
+ }
+
+ if (startswith (own_buf, "vFile:")
+ && handle_vFile (own_buf, packet_len, new_packet_len))
+ return;
+
+ if (startswith (own_buf, "vAttach;"))
+ {
+ if ((!extended_protocol || !cs.multi_process) && target_running ())
+ {
+ fprintf (stderr, "Already debugging a process\n");
+ write_enn (own_buf);
+ return;
+ }
+ handle_v_attach (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "vRun;"))
+ {
+ if ((!extended_protocol || !cs.multi_process) && target_running ())
+ {
+ fprintf (stderr, "Already debugging a process\n");
+ write_enn (own_buf);
+ return;
+ }
+ handle_v_run (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "vKill;"))
+ {
+ if (!target_running ())
+ {
+ fprintf (stderr, "No process to kill\n");
+ write_enn (own_buf);
+ return;
+ }
+ handle_v_kill (own_buf);
+ return;
+ }
+
+ if (handle_notif_ack (own_buf, packet_len))
+ return;
+
+ /* Otherwise we didn't know what packet it was. Say we didn't
+ understand it. */
+ own_buf[0] = 0;
+ return;
+}
+
+/* Resume thread and wait for another event. In non-stop mode,
+ don't really wait here, but return immediatelly to the event
+ loop. */
+static void
+myresume (char *own_buf, int step, int sig)
+{
+ client_state &cs = get_client_state ();
+ struct thread_resume resume_info[2];
+ int n = 0;
+ int valid_cont_thread;
+
+ valid_cont_thread = (cs.cont_thread != null_ptid
+ && cs.cont_thread != minus_one_ptid);
+
+ if (step || sig || valid_cont_thread)
+ {
+ resume_info[0].thread = current_ptid;
+ if (step)
+ resume_info[0].kind = resume_step;
+ else
+ resume_info[0].kind = resume_continue;
+ resume_info[0].sig = sig;
+ n++;
+ }
+
+ if (!valid_cont_thread)
+ {
+ resume_info[n].thread = minus_one_ptid;
+ resume_info[n].kind = resume_continue;
+ resume_info[n].sig = 0;
+ n++;
+ }
+
+ resume (resume_info, n);
+}
+
+/* Callback for for_each_thread. Make a new stop reply for each
+ stopped thread. */
+
+static void
+queue_stop_reply_callback (thread_info *thread)
+{
+ /* For now, assume targets that don't have this callback also don't
+ manage the thread's last_status field. */
+ if (the_target->thread_stopped == NULL)
+ {
+ struct vstop_notif *new_notif = new struct vstop_notif;
+
+ new_notif->ptid = thread->id;
+ new_notif->status = thread->last_status;
+ /* Pass the last stop reply back to GDB, but don't notify
+ yet. */
+ notif_event_enque (¬if_stop, new_notif);
+ }
+ else
+ {
+ if (thread_stopped (thread))
+ {
+ if (debug_threads)
+ {
+ std::string status_string
+ = target_waitstatus_to_string (&thread->last_status);
+
+ debug_printf ("Reporting thread %s as already stopped with %s\n",
+ target_pid_to_str (thread->id),
+ status_string.c_str ());
+ }
+
+ gdb_assert (thread->last_status.kind != TARGET_WAITKIND_IGNORE);
+
+ /* Pass the last stop reply back to GDB, but don't notify
+ yet. */
+ queue_stop_reply (thread->id, &thread->last_status);
+ }
+ }
+}
+
+/* Set this inferior threads's state as "want-stopped". We won't
+ resume this thread until the client gives us another action for
+ it. */
+
+static void
+gdb_wants_thread_stopped (thread_info *thread)
+{
+ thread->last_resume_kind = resume_stop;
+
+ if (thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+ {
+ /* Most threads are stopped implicitly (all-stop); tag that with
+ signal 0. */
+ thread->last_status.kind = TARGET_WAITKIND_STOPPED;
+ thread->last_status.value.sig = GDB_SIGNAL_0;
+ }
+}
+
+/* Set all threads' states as "want-stopped". */
+
+static void
+gdb_wants_all_threads_stopped (void)
+{
+ for_each_thread (gdb_wants_thread_stopped);
+}
+
+/* Callback for for_each_thread. If the thread is stopped with an
+ interesting event, mark it as having a pending event. */
+
+static void
+set_pending_status_callback (thread_info *thread)
+{
+ if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
+ || (thread->last_status.value.sig != GDB_SIGNAL_0
+ /* A breakpoint, watchpoint or finished step from a previous
+ GDB run isn't considered interesting for a new GDB run.
+ If we left those pending, the new GDB could consider them
+ random SIGTRAPs. This leaves out real async traps. We'd
+ have to peek into the (target-specific) siginfo to
+ distinguish those. */
+ && thread->last_status.value.sig != GDB_SIGNAL_TRAP))
+ thread->status_pending_p = 1;
+}
+
+/* Status handler for the '?' packet. */
+
+static void
+handle_status (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+
+ /* GDB is connected, don't forward events to the target anymore. */
+ for_each_process ([] (process_info *process) {
+ process->gdb_detached = 0;
+ });
+
+ /* In non-stop mode, we must send a stop reply for each stopped
+ thread. In all-stop mode, just send one for the first stopped
+ thread we find. */
+
+ if (non_stop)
+ {
+ for_each_thread (queue_stop_reply_callback);
+
+ /* The first is sent immediatly. OK is sent if there is no
+ stopped thread, which is the same handling of the vStopped
+ packet (by design). */
+ notif_write_event (¬if_stop, cs.own_buf);
+ }
+ else
+ {
+ thread_info *thread = NULL;
+
+ pause_all (0);
+ stabilize_threads ();
+ gdb_wants_all_threads_stopped ();
+
+ /* We can only report one status, but we might be coming out of
+ non-stop -- if more than one thread is stopped with
+ interesting events, leave events for the threads we're not
+ reporting now pending. They'll be reported the next time the
+ threads are resumed. Start by marking all interesting events
+ as pending. */
+ for_each_thread (set_pending_status_callback);
+
+ /* Prefer the last thread that reported an event to GDB (even if
+ that was a GDB_SIGNAL_TRAP). */
+ if (cs.last_status.kind != TARGET_WAITKIND_IGNORE
+ && cs.last_status.kind != TARGET_WAITKIND_EXITED
+ && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED)
+ thread = find_thread_ptid (cs.last_ptid);
+
+ /* If the last event thread is not found for some reason, look
+ for some other thread that might have an event to report. */
+ if (thread == NULL)
+ thread = find_thread ([] (thread_info *thr_arg)
+ {
+ return thr_arg->status_pending_p;
+ });
+
+ /* If we're still out of luck, simply pick the first thread in
+ the thread list. */
+ if (thread == NULL)
+ thread = get_first_thread ();
+
+ if (thread != NULL)
+ {
+ struct thread_info *tp = (struct thread_info *) thread;
+
+ /* We're reporting this event, so it's no longer
+ pending. */
+ tp->status_pending_p = 0;
+
+ /* GDB assumes the current thread is the thread we're
+ reporting the status for. */
+ cs.general_thread = thread->id;
+ set_desired_thread ();
+
+ gdb_assert (tp->last_status.kind != TARGET_WAITKIND_IGNORE);
+ prepare_resume_reply (own_buf, tp->id, &tp->last_status);
+ }
+ else
+ strcpy (own_buf, "W00");
+ }
+}
+
+static void
+gdbserver_version (void)
+{
+ printf ("GNU gdbserver %s%s\n"
+ "Copyright (C) 2020 Free Software Foundation, Inc.\n"
+ "gdbserver is free software, covered by the "
+ "GNU General Public License.\n"
+ "This gdbserver was configured as \"%s\"\n",
+ PKGVERSION, version, host_name);
+}
+
+static void
+gdbserver_usage (FILE *stream)
+{
+ fprintf (stream, "Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n"
+ "\tgdbserver [OPTIONS] --attach COMM PID\n"
+ "\tgdbserver [OPTIONS] --multi COMM\n"
+ "\n"
+ "COMM may either be a tty device (for serial debugging),\n"
+ "HOST:PORT to listen for a TCP connection, or '-' or 'stdio' to use \n"
+ "stdin/stdout of gdbserver.\n"
+ "PROG is the executable program. ARGS are arguments passed to inferior.\n"
+ "PID is the process ID to attach to, when --attach is specified.\n"
+ "\n"
+ "Operating modes:\n"
+ "\n"
+ " --attach Attach to running process PID.\n"
+ " --multi Start server without a specific program, and\n"
+ " only quit when explicitly commanded.\n"
+ " --once Exit after the first connection has closed.\n"
+ " --help Print this message and then exit.\n"
+ " --version Display version information and exit.\n"
+ "\n"
+ "Other options:\n"
+ "\n"
+ " --wrapper WRAPPER -- Run WRAPPER to start new programs.\n"
+ " --disable-randomization\n"
+ " Run PROG with address space randomization disabled.\n"
+ " --no-disable-randomization\n"
+ " Don't disable address space randomization when\n"
+ " starting PROG.\n"
+ " --startup-with-shell\n"
+ " Start PROG using a shell. I.e., execs a shell that\n"
+ " then execs PROG. (default)\n"
+ " --no-startup-with-shell\n"
+ " Exec PROG directly instead of using a shell.\n"
+ " Disables argument globbing and variable substitution\n"
+ " on UNIX-like systems.\n"
+ "\n"
+ "Debug options:\n"
+ "\n"
+ " --debug Enable general debugging output.\n"
+ " --debug-format=OPT1[,OPT2,...]\n"
+ " Specify extra content in debugging output.\n"
+ " Options:\n"
+ " all\n"
+ " none\n"
+ " timestamp\n"
+ " --remote-debug Enable remote protocol debugging output.\n"
+ " --disable-packet=OPT1[,OPT2,...]\n"
+ " Disable support for RSP packets or features.\n"
+ " Options:\n"
+ " vCont, Tthread, qC, qfThreadInfo and \n"
+ " threads (disable all threading packets).\n"
+ "\n"
+ "For more information, consult the GDB manual (available as on-line \n"
+ "info or a printed manual).\n");
+ if (REPORT_BUGS_TO[0] && stream == stdout)
+ fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO);
+}
+
+static void
+gdbserver_show_disableable (FILE *stream)
+{
+ fprintf (stream, "Disableable packets:\n"
+ " vCont \tAll vCont packets\n"
+ " qC \tQuerying the current thread\n"
+ " qfThreadInfo\tThread listing\n"
+ " Tthread \tPassing the thread specifier in the "
+ "T stop reply packet\n"
+ " threads \tAll of the above\n");
+}
+
+static void
+kill_inferior_callback (process_info *process)
+{
+ kill_inferior (process);
+ discard_queued_stop_replies (ptid_t (process->pid));
+}
+
+/* Call this when exiting gdbserver with possible inferiors that need
+ to be killed or detached from. */
+
+static void
+detach_or_kill_for_exit (void)
+{
+ /* First print a list of the inferiors we will be killing/detaching.
+ This is to assist the user, for example, in case the inferior unexpectedly
+ dies after we exit: did we screw up or did the inferior exit on its own?
+ Having this info will save some head-scratching. */
+
+ if (have_started_inferiors_p ())
+ {
+ fprintf (stderr, "Killing process(es):");
+
+ for_each_process ([] (process_info *process) {
+ if (!process->attached)
+ fprintf (stderr, " %d", process->pid);
+ });
+
+ fprintf (stderr, "\n");
+ }
+ if (have_attached_inferiors_p ())
+ {
+ fprintf (stderr, "Detaching process(es):");
+
+ for_each_process ([] (process_info *process) {
+ if (process->attached)
+ fprintf (stderr, " %d", process->pid);
+ });
+
+ fprintf (stderr, "\n");
+ }
+
+ /* Now we can kill or detach the inferiors. */
+ for_each_process ([] (process_info *process) {
+ int pid = process->pid;
+
+ if (process->attached)
+ detach_inferior (process);
+ else
+ kill_inferior (process);
+
+ discard_queued_stop_replies (ptid_t (pid));
+ });
+}
+
+/* Value that will be passed to exit(3) when gdbserver exits. */
+static int exit_code;
+
+/* Wrapper for detach_or_kill_for_exit that catches and prints
+ errors. */
+
+static void
+detach_or_kill_for_exit_cleanup ()
+{
+ try
+ {
+ detach_or_kill_for_exit ();
+ }
+ catch (const gdb_exception &exception)
+ {
+ fflush (stdout);
+ fprintf (stderr, "Detach or kill failed: %s\n",
+ exception.what ());
+ exit_code = 1;
+ }
+}
+
+/* Main function. This is called by the real "main" function,
+ wrapped in a TRY_CATCH that handles any uncaught exceptions. */
+
+static void ATTRIBUTE_NORETURN
+captured_main (int argc, char *argv[])
+{
+ int bad_attach;
+ int pid;
+ char *arg_end;
+ const char *port = NULL;
+ char **next_arg = &argv[1];
+ volatile int multi_mode = 0;
+ volatile int attach = 0;
+ int was_running;
+ bool selftest = false;
+#if GDB_SELF_TEST
+ const char *selftest_filter = NULL;
+#endif
+
+ current_directory = getcwd (NULL, 0);
+ client_state &cs = get_client_state ();
+
+ if (current_directory == NULL)
+ {
+ error (_("Could not find current working directory: %s"),
+ safe_strerror (errno));
+ }
+
+ while (*next_arg != NULL && **next_arg == '-')
+ {
+ if (strcmp (*next_arg, "--version") == 0)
+ {
+ gdbserver_version ();
+ exit (0);
+ }
+ else if (strcmp (*next_arg, "--help") == 0)
+ {
+ gdbserver_usage (stdout);
+ exit (0);
+ }
+ else if (strcmp (*next_arg, "--attach") == 0)
+ attach = 1;
+ else if (strcmp (*next_arg, "--multi") == 0)
+ multi_mode = 1;
+ else if (strcmp (*next_arg, "--wrapper") == 0)
+ {
+ char **tmp;
+
+ next_arg++;
+
+ tmp = next_arg;
+ while (*next_arg != NULL && strcmp (*next_arg, "--") != 0)
+ {
+ wrapper_argv += *next_arg;
+ wrapper_argv += ' ';
+ next_arg++;
+ }
+
+ if (!wrapper_argv.empty ())
+ {
+ /* Erase the last whitespace. */
+ wrapper_argv.erase (wrapper_argv.end () - 1);
+ }
+
+ if (next_arg == tmp || *next_arg == NULL)
+ {
+ gdbserver_usage (stderr);
+ exit (1);
+ }
+
+ /* Consume the "--". */
+ *next_arg = NULL;
+ }
+ else if (strcmp (*next_arg, "--debug") == 0)
+ debug_threads = 1;
+ else if (startswith (*next_arg, "--debug-format="))
+ {
+ std::string error_msg
+ = parse_debug_format_options ((*next_arg)
+ + sizeof ("--debug-format=") - 1, 0);
+
+ if (!error_msg.empty ())
+ {
+ fprintf (stderr, "%s", error_msg.c_str ());
+ exit (1);
+ }
+ }
+ else if (strcmp (*next_arg, "--remote-debug") == 0)
+ remote_debug = 1;
+ else if (startswith (*next_arg, "--debug-file="))
+ debug_set_output ((*next_arg) + sizeof ("--debug-file=") -1);
+ else if (strcmp (*next_arg, "--disable-packet") == 0)
+ {
+ gdbserver_show_disableable (stdout);
+ exit (0);
+ }
+ else if (startswith (*next_arg, "--disable-packet="))
+ {
+ char *packets = *next_arg += sizeof ("--disable-packet=") - 1;
+ char *saveptr;
+ for (char *tok = strtok_r (packets, ",", &saveptr);
+ tok != NULL;
+ tok = strtok_r (NULL, ",", &saveptr))
+ {
+ if (strcmp ("vCont", tok) == 0)
+ disable_packet_vCont = true;
+ else if (strcmp ("Tthread", tok) == 0)
+ disable_packet_Tthread = true;
+ else if (strcmp ("qC", tok) == 0)
+ disable_packet_qC = true;
+ else if (strcmp ("qfThreadInfo", tok) == 0)
+ disable_packet_qfThreadInfo = true;
+ else if (strcmp ("threads", tok) == 0)
+ {
+ disable_packet_vCont = true;
+ disable_packet_Tthread = true;
+ disable_packet_qC = true;
+ disable_packet_qfThreadInfo = true;
+ }
+ else
+ {
+ fprintf (stderr, "Don't know how to disable \"%s\".\n\n",
+ tok);
+ gdbserver_show_disableable (stderr);
+ exit (1);
+ }
+ }
+ }
+ else if (strcmp (*next_arg, "-") == 0)
+ {
+ /* "-" specifies a stdio connection and is a form of port
+ specification. */
+ port = STDIO_CONNECTION_NAME;
+ next_arg++;
+ break;
+ }
+ else if (strcmp (*next_arg, "--disable-randomization") == 0)
+ cs.disable_randomization = 1;
+ else if (strcmp (*next_arg, "--no-disable-randomization") == 0)
+ cs.disable_randomization = 0;
+ else if (strcmp (*next_arg, "--startup-with-shell") == 0)
+ startup_with_shell = true;
+ else if (strcmp (*next_arg, "--no-startup-with-shell") == 0)
+ startup_with_shell = false;
+ else if (strcmp (*next_arg, "--once") == 0)
+ run_once = true;
+ else if (strcmp (*next_arg, "--selftest") == 0)
+ selftest = true;
+ else if (startswith (*next_arg, "--selftest="))
+ {
+ selftest = true;
+#if GDB_SELF_TEST
+ selftest_filter = *next_arg + strlen ("--selftest=");
+#endif
+ }
+ else
+ {
+ fprintf (stderr, "Unknown argument: %s\n", *next_arg);
+ exit (1);
+ }
+
+ next_arg++;
+ continue;
+ }
+
+ if (port == NULL)
+ {
+ port = *next_arg;
+ next_arg++;
+ }
+ if ((port == NULL || (!attach && !multi_mode && *next_arg == NULL))
+ && !selftest)
+ {
+ gdbserver_usage (stderr);
+ exit (1);
+ }
+
+ /* Remember stdio descriptors. LISTEN_DESC must not be listed, it will be
+ opened by remote_prepare. */
+ notice_open_fds ();
+
+ save_original_signals_state (false);
+
+ /* We need to know whether the remote connection is stdio before
+ starting the inferior. Inferiors created in this scenario have
+ stdin,stdout redirected. So do this here before we call
+ start_inferior. */
+ if (port != NULL)
+ remote_prepare (port);
+
+ bad_attach = 0;
+ pid = 0;
+
+ /* --attach used to come after PORT, so allow it there for
+ compatibility. */
+ if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0)
+ {
+ attach = 1;
+ next_arg++;
+ }
+
+ if (attach
+ && (*next_arg == NULL
+ || (*next_arg)[0] == '\0'
+ || (pid = strtoul (*next_arg, &arg_end, 0)) == 0
+ || *arg_end != '\0'
+ || next_arg[1] != NULL))
+ bad_attach = 1;
+
+ if (bad_attach)
+ {
+ gdbserver_usage (stderr);
+ exit (1);
+ }
+
+ /* Gather information about the environment. */
+ our_environ = gdb_environ::from_host_environ ();
+
+ initialize_async_io ();
+ initialize_low ();
+ have_job_control ();
+ initialize_event_loop ();
+ if (target_supports_tracepoints ())
+ initialize_tracepoint ();
+
+ mem_buf = (unsigned char *) xmalloc (PBUFSIZ);
+
+ if (selftest)
+ {
+#if GDB_SELF_TEST
+ selftests::run_tests (selftest_filter);
+#else
+ printf (_("Selftests have been disabled for this build.\n"));
+#endif
+ throw_quit ("Quit");
+ }
+
+ if (pid == 0 && *next_arg != NULL)
+ {
+ int i, n;
+
+ n = argc - (next_arg - argv);
+ program_path.set (make_unique_xstrdup (next_arg[0]));
+ for (i = 1; i < n; i++)
+ program_args.push_back (xstrdup (next_arg[i]));
+ program_args.push_back (NULL);
+
+ /* Wait till we are at first instruction in program. */
+ create_inferior (program_path.get (), program_args);
+
+ /* We are now (hopefully) stopped at the first instruction of
+ the target process. This assumes that the target process was
+ successfully created. */
+ }
+ else if (pid != 0)
+ {
+ if (attach_inferior (pid) == -1)
+ error ("Attaching not supported on this target");
+
+ /* Otherwise succeeded. */
+ }
+ else
+ {
+ cs.last_status.kind = TARGET_WAITKIND_EXITED;
+ cs.last_status.value.integer = 0;
+ cs.last_ptid = minus_one_ptid;
+ }
+
+ SCOPE_EXIT { detach_or_kill_for_exit_cleanup (); };
+
+ /* Don't report shared library events on the initial connection,
+ even if some libraries are preloaded. Avoids the "stopped by
+ shared library event" notice on gdb side. */
+ dlls_changed = 0;
+
+ if (cs.last_status.kind == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
+ was_running = 0;
+ else
+ was_running = 1;
+
+ if (!was_running && !multi_mode)
+ error ("No program to debug");
+
+ while (1)
+ {
+ cs.noack_mode = 0;
+ cs.multi_process = 0;
+ cs.report_fork_events = 0;
+ cs.report_vfork_events = 0;
+ cs.report_exec_events = 0;
+ /* Be sure we're out of tfind mode. */
+ cs.current_traceframe = -1;
+ cs.cont_thread = null_ptid;
+ cs.swbreak_feature = 0;
+ cs.hwbreak_feature = 0;
+ cs.vCont_supported = 0;
+
+ remote_open (port);
+
+ try
+ {
+ /* Wait for events. This will return when all event sources
+ are removed from the event loop. */
+ start_event_loop ();
+
+ /* If an exit was requested (using the "monitor exit"
+ command), terminate now. */
+ if (exit_requested)
+ throw_quit ("Quit");
+
+ /* The only other way to get here is for getpkt to fail:
+
+ - If --once was specified, we're done.
+
+ - If not in extended-remote mode, and we're no longer
+ debugging anything, simply exit: GDB has disconnected
+ after processing the last process exit.
+
+ - Otherwise, close the connection and reopen it at the
+ top of the loop. */
+ if (run_once || (!extended_protocol && !target_running ()))
+ throw_quit ("Quit");
+
+ fprintf (stderr,
+ "Remote side has terminated connection. "
+ "GDBserver will reopen the connection.\n");
+
+ /* Get rid of any pending statuses. An eventual reconnection
+ (by the same GDB instance or another) will refresh all its
+ state from scratch. */
+ discard_queued_stop_replies (minus_one_ptid);
+ for_each_thread ([] (thread_info *thread)
+ {
+ thread->status_pending_p = 0;
+ });
+
+ if (tracing)
+ {
+ if (disconnected_tracing)
+ {
+ /* Try to enable non-stop/async mode, so we we can
+ both wait for an async socket accept, and handle
+ async target events simultaneously. There's also
+ no point either in having the target always stop
+ all threads, when we're going to pass signals
+ down without informing GDB. */
+ if (!non_stop)
+ {
+ if (start_non_stop (1))
+ non_stop = 1;
+
+ /* Detaching implicitly resumes all threads;
+ simply disconnecting does not. */
+ }
+ }
+ else
+ {
+ fprintf (stderr,
+ "Disconnected tracing disabled; "
+ "stopping trace run.\n");
+ stop_tracing ();
+ }
+ }
+ }
+ catch (const gdb_exception_error &exception)
+ {
+ fflush (stdout);
+ fprintf (stderr, "gdbserver: %s\n", exception.what ());
+
+ if (response_needed)
+ {
+ write_enn (cs.own_buf);
+ putpkt (cs.own_buf);
+ }
+
+ if (run_once)
+ throw_quit ("Quit");
+ }
+ }
+}
+
+/* Main function. */
+
+int
+main (int argc, char *argv[])
+{
+
+ try
+ {
+ captured_main (argc, argv);
+ }
+ catch (const gdb_exception &exception)
+ {
+ if (exception.reason == RETURN_ERROR)
+ {
+ fflush (stdout);
+ fprintf (stderr, "%s\n", exception.what ());
+ fprintf (stderr, "Exiting\n");
+ exit_code = 1;
+ }
+
+ exit (exit_code);
+ }
+
+ gdb_assert_not_reached ("captured_main should never return");
+}
+
+/* Process options coming from Z packets for a breakpoint. PACKET is
+ the packet buffer. *PACKET is updated to point to the first char
+ after the last processed option. */
+
+static void
+process_point_options (struct gdb_breakpoint *bp, const char **packet)
+{
+ const char *dataptr = *packet;
+ int persist;
+
+ /* Check if data has the correct format. */
+ if (*dataptr != ';')
+ return;
+
+ dataptr++;
+
+ while (*dataptr)
+ {
+ if (*dataptr == ';')
+ ++dataptr;
+
+ if (*dataptr == 'X')
+ {
+ /* Conditional expression. */
+ if (debug_threads)
+ debug_printf ("Found breakpoint condition.\n");
+ if (!add_breakpoint_condition (bp, &dataptr))
+ dataptr = strchrnul (dataptr, ';');
+ }
+ else if (startswith (dataptr, "cmds:"))
+ {
+ dataptr += strlen ("cmds:");
+ if (debug_threads)
+ debug_printf ("Found breakpoint commands %s.\n", dataptr);
+ persist = (*dataptr == '1');
+ dataptr += 2;
+ if (add_breakpoint_commands (bp, &dataptr, persist))
+ dataptr = strchrnul (dataptr, ';');
+ }
+ else
+ {
+ fprintf (stderr, "Unknown token %c, ignoring.\n",
+ *dataptr);
+ /* Skip tokens until we find one that we recognize. */
+ dataptr = strchrnul (dataptr, ';');
+ }
+ }
+ *packet = dataptr;
+}
+
+/* Event loop callback that handles a serial event. The first byte in
+ the serial buffer gets us here. We expect characters to arrive at
+ a brisk pace, so we read the rest of the packet with a blocking
+ getpkt call. */
+
+static int
+process_serial_event (void)
+{
+ client_state &cs = get_client_state ();
+ int signal;
+ unsigned int len;
+ CORE_ADDR mem_addr;
+ unsigned char sig;
+ int packet_len;
+ int new_packet_len = -1;
+
+ disable_async_io ();
+
+ response_needed = false;
+ packet_len = getpkt (cs.own_buf);
+ if (packet_len <= 0)
+ {
+ remote_close ();
+ /* Force an event loop break. */
+ return -1;
+ }
+ response_needed = true;
+
+ char ch = cs.own_buf[0];
+ switch (ch)
+ {
+ case 'q':
+ handle_query (cs.own_buf, packet_len, &new_packet_len);
+ break;
+ case 'Q':
+ handle_general_set (cs.own_buf);
+ break;
+ case 'D':
+ handle_detach (cs.own_buf);
+ break;
+ case '!':
+ extended_protocol = true;
+ write_ok (cs.own_buf);
+ break;
+ case '?':
+ handle_status (cs.own_buf);
+ break;
+ case 'H':
+ if (cs.own_buf[1] == 'c' || cs.own_buf[1] == 'g' || cs.own_buf[1] == 's')
+ {
+ require_running_or_break (cs.own_buf);
+
+ ptid_t thread_id = read_ptid (&cs.own_buf[2], NULL);
+
+ if (thread_id == null_ptid || thread_id == minus_one_ptid)
+ thread_id = null_ptid;
+ else if (thread_id.is_pid ())
+ {
+ /* The ptid represents a pid. */
+ thread_info *thread = find_any_thread_of_pid (thread_id.pid ());
+
+ if (thread == NULL)
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+
+ thread_id = thread->id;
+ }
+ else
+ {
+ /* The ptid represents a lwp/tid. */
+ if (find_thread_ptid (thread_id) == NULL)
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+ }
+
+ if (cs.own_buf[1] == 'g')
+ {
+ if (thread_id == null_ptid)
+ {
+ /* GDB is telling us to choose any thread. Check if
+ the currently selected thread is still valid. If
+ it is not, select the first available. */
+ thread_info *thread = find_thread_ptid (cs.general_thread);
+ if (thread == NULL)
+ thread = get_first_thread ();
+ thread_id = thread->id;
+ }
+
+ cs.general_thread = thread_id;
+ set_desired_thread ();
+ gdb_assert (current_thread != NULL);
+ }
+ else if (cs.own_buf[1] == 'c')
+ cs.cont_thread = thread_id;
+
+ write_ok (cs.own_buf);
+ }
+ else
+ {
+ /* Silently ignore it so that gdb can extend the protocol
+ without compatibility headaches. */
+ cs.own_buf[0] = '\0';
+ }
+ break;
+ case 'g':
+ require_running_or_break (cs.own_buf);
+ if (cs.current_traceframe >= 0)
+ {
+ struct regcache *regcache
+ = new_register_cache (current_target_desc ());
+
+ if (fetch_traceframe_registers (cs.current_traceframe,
+ regcache, -1) == 0)
+ registers_to_string (regcache, cs.own_buf);
+ else
+ write_enn (cs.own_buf);
+ free_register_cache (regcache);
+ }
+ else
+ {
+ struct regcache *regcache;
+
+ if (!set_desired_thread ())
+ write_enn (cs.own_buf);
+ else
+ {
+ regcache = get_thread_regcache (current_thread, 1);
+ registers_to_string (regcache, cs.own_buf);
+ }
+ }
+ break;
+ case 'G':
+ require_running_or_break (cs.own_buf);
+ if (cs.current_traceframe >= 0)
+ write_enn (cs.own_buf);
+ else
+ {
+ struct regcache *regcache;
+
+ if (!set_desired_thread ())
+ write_enn (cs.own_buf);
+ else
+ {
+ regcache = get_thread_regcache (current_thread, 1);
+ registers_from_string (regcache, &cs.own_buf[1]);
+ write_ok (cs.own_buf);
+ }
+ }
+ break;
+ case 'm':
+ {
+ require_running_or_break (cs.own_buf);
+ decode_m_packet (&cs.own_buf[1], &mem_addr, &len);
+ int res = gdb_read_memory (mem_addr, mem_buf, len);
+ if (res < 0)
+ write_enn (cs.own_buf);
+ else
+ bin2hex (mem_buf, cs.own_buf, res);
+ }
+ break;
+ case 'M':
+ require_running_or_break (cs.own_buf);
+ decode_M_packet (&cs.own_buf[1], &mem_addr, &len, &mem_buf);
+ if (gdb_write_memory (mem_addr, mem_buf, len) == 0)
+ write_ok (cs.own_buf);
+ else
+ write_enn (cs.own_buf);
+ break;
+ case 'X':
+ require_running_or_break (cs.own_buf);
+ if (decode_X_packet (&cs.own_buf[1], packet_len - 1,
+ &mem_addr, &len, &mem_buf) < 0
+ || gdb_write_memory (mem_addr, mem_buf, len) != 0)
+ write_enn (cs.own_buf);
+ else
+ write_ok (cs.own_buf);
+ break;
+ case 'C':
+ require_running_or_break (cs.own_buf);
+ hex2bin (cs.own_buf + 1, &sig, 1);
+ if (gdb_signal_to_host_p ((enum gdb_signal) sig))
+ signal = gdb_signal_to_host ((enum gdb_signal) sig);
+ else
+ signal = 0;
+ myresume (cs.own_buf, 0, signal);
+ break;
+ case 'S':
+ require_running_or_break (cs.own_buf);
+ hex2bin (cs.own_buf + 1, &sig, 1);
+ if (gdb_signal_to_host_p ((enum gdb_signal) sig))
+ signal = gdb_signal_to_host ((enum gdb_signal) sig);
+ else
+ signal = 0;
+ myresume (cs.own_buf, 1, signal);
+ break;
+ case 'c':
+ require_running_or_break (cs.own_buf);
+ signal = 0;
+ myresume (cs.own_buf, 0, signal);
+ break;
+ case 's':
+ require_running_or_break (cs.own_buf);
+ signal = 0;
+ myresume (cs.own_buf, 1, signal);
+ break;
+ case 'Z': /* insert_ ... */
+ /* Fallthrough. */
+ case 'z': /* remove_ ... */
+ {
+ char *dataptr;
+ ULONGEST addr;
+ int kind;
+ char type = cs.own_buf[1];
+ int res;
+ const int insert = ch == 'Z';
+ const char *p = &cs.own_buf[3];
+
+ p = unpack_varlen_hex (p, &addr);
+ kind = strtol (p + 1, &dataptr, 16);
+
+ if (insert)
+ {
+ struct gdb_breakpoint *bp;
+
+ bp = set_gdb_breakpoint (type, addr, kind, &res);
+ if (bp != NULL)
+ {
+ res = 0;
+
+ /* GDB may have sent us a list of *point parameters to
+ be evaluated on the target's side. Read such list
+ here. If we already have a list of parameters, GDB
+ is telling us to drop that list and use this one
+ instead. */
+ clear_breakpoint_conditions_and_commands (bp);
+ const char *options = dataptr;
+ process_point_options (bp, &options);
+ }
+ }
+ else
+ res = delete_gdb_breakpoint (type, addr, kind);
+
+ if (res == 0)
+ write_ok (cs.own_buf);
+ else if (res == 1)
+ /* Unsupported. */
+ cs.own_buf[0] = '\0';
+ else
+ write_enn (cs.own_buf);
+ break;
+ }
+ case 'k':
+ response_needed = false;
+ if (!target_running ())
+ /* The packet we received doesn't make sense - but we can't
+ reply to it, either. */
+ return 0;
+
+ fprintf (stderr, "Killing all inferiors\n");
+
+ for_each_process (kill_inferior_callback);
+
+ /* When using the extended protocol, we wait with no program
+ running. The traditional protocol will exit instead. */
+ if (extended_protocol)
+ {
+ cs.last_status.kind = TARGET_WAITKIND_EXITED;
+ cs.last_status.value.sig = GDB_SIGNAL_KILL;
+ return 0;
+ }
+ else
+ exit (0);
+
+ case 'T':
+ {
+ require_running_or_break (cs.own_buf);
+
+ ptid_t thread_id = read_ptid (&cs.own_buf[1], NULL);
+ if (find_thread_ptid (thread_id) == NULL)
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+
+ if (mythread_alive (thread_id))
+ write_ok (cs.own_buf);
+ else
+ write_enn (cs.own_buf);
+ }
+ break;
+ case 'R':
+ response_needed = false;
+
+ /* Restarting the inferior is only supported in the extended
+ protocol. */
+ if (extended_protocol)
+ {
+ if (target_running ())
+ for_each_process (kill_inferior_callback);
+
+ fprintf (stderr, "GDBserver restarting\n");
+
+ /* Wait till we are at 1st instruction in prog. */
+ if (program_path.get () != NULL)
+ {
+ create_inferior (program_path.get (), program_args);
+
+ if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
+ {
+ /* Stopped at the first instruction of the target
+ process. */
+ cs.general_thread = cs.last_ptid;
+ }
+ else
+ {
+ /* Something went wrong. */
+ cs.general_thread = null_ptid;
+ }
+ }
+ else
+ {
+ cs.last_status.kind = TARGET_WAITKIND_EXITED;
+ cs.last_status.value.sig = GDB_SIGNAL_KILL;
+ }
+ return 0;
+ }
+ else
+ {
+ /* It is a request we don't understand. Respond with an
+ empty packet so that gdb knows that we don't support this
+ request. */
+ cs.own_buf[0] = '\0';
+ break;
+ }
+ case 'v':
+ /* Extended (long) request. */
+ handle_v_requests (cs.own_buf, packet_len, &new_packet_len);
+ break;
+
+ default:
+ /* It is a request we don't understand. Respond with an empty
+ packet so that gdb knows that we don't support this
+ request. */
+ cs.own_buf[0] = '\0';
+ break;
+ }
+
+ if (new_packet_len != -1)
+ putpkt_binary (cs.own_buf, new_packet_len);
+ else
+ putpkt (cs.own_buf);
+
+ response_needed = false;
+
+ if (exit_requested)
+ return -1;
+
+ return 0;
+}
+
+/* Event-loop callback for serial events. */
+
+int
+handle_serial_event (int err, gdb_client_data client_data)
+{
+ if (debug_threads)
+ debug_printf ("handling possible serial event\n");
+
+ /* Really handle it. */
+ if (process_serial_event () < 0)
+ return -1;
+
+ /* Be sure to not change the selected thread behind GDB's back.
+ Important in the non-stop mode asynchronous protocol. */
+ set_desired_thread ();
+
+ return 0;
+}
+
+/* Push a stop notification on the notification queue. */
+
+static void
+push_stop_notification (ptid_t ptid, struct target_waitstatus *status)
+{
+ struct vstop_notif *vstop_notif = new struct vstop_notif;
+
+ vstop_notif->status = *status;
+ vstop_notif->ptid = ptid;
+ /* Push Stop notification. */
+ notif_push (¬if_stop, vstop_notif);
+}
+
+/* Event-loop callback for target events. */
+
+int
+handle_target_event (int err, gdb_client_data client_data)
+{
+ client_state &cs = get_client_state ();
+ if (debug_threads)
+ debug_printf ("handling possible target event\n");
+
+ cs.last_ptid = mywait (minus_one_ptid, &cs.last_status,
+ TARGET_WNOHANG, 1);
+
+ if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED)
+ {
+ if (gdb_connected () && report_no_resumed)
+ push_stop_notification (null_ptid, &cs.last_status);
+ }
+ else if (cs.last_status.kind != TARGET_WAITKIND_IGNORE)
+ {
+ int pid = cs.last_ptid.pid ();
+ struct process_info *process = find_process_pid (pid);
+ int forward_event = !gdb_connected () || process->gdb_detached;
+
+ if (cs.last_status.kind == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
+ {
+ mark_breakpoints_out (process);
+ target_mourn_inferior (cs.last_ptid);
+ }
+ else if (cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED)
+ ;
+ else
+ {
+ /* We're reporting this thread as stopped. Update its
+ "want-stopped" state to what the client wants, until it
+ gets a new resume action. */
+ current_thread->last_resume_kind = resume_stop;
+ current_thread->last_status = cs.last_status;
+ }
+
+ if (forward_event)
+ {
+ if (!target_running ())
+ {
+ /* The last process exited. We're done. */
+ exit (0);
+ }
+
+ if (cs.last_status.kind == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED
+ || cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED)
+ ;
+ else
+ {
+ /* A thread stopped with a signal, but gdb isn't
+ connected to handle it. Pass it down to the
+ inferior, as if it wasn't being traced. */
+ enum gdb_signal signal;
+
+ if (debug_threads)
+ debug_printf ("GDB not connected; forwarding event %d for"
+ " [%s]\n",
+ (int) cs.last_status.kind,
+ target_pid_to_str (cs.last_ptid));
+
+ if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
+ signal = cs.last_status.value.sig;
+ else
+ signal = GDB_SIGNAL_0;
+ target_continue (cs.last_ptid, signal);
+ }
+ }
+ else
+ push_stop_notification (cs.last_ptid, &cs.last_status);
+ }
+
+ /* Be sure to not change the selected thread behind GDB's back.
+ Important in the non-stop mode asynchronous protocol. */
+ set_desired_thread ();
+
+ return 0;
+}
+
+#if GDB_SELF_TEST
+namespace selftests
+{
+
+void
+reset ()
+{}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+++ /dev/null
-/* Symbol manipulating routines for the remote server for GDB.
-
- Copyright (C) 2014-2020 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 "gdbsupport/symbol.h"
-
-/* See gdbsupport/symbol.h. */
-
-int
-find_minimal_symbol_address (const char *name, CORE_ADDR *addr,
- struct objfile *objfile)
-{
- gdb_assert (objfile == NULL);
-
- return look_up_one_symbol (name, addr, 1) != 1;
-}
--- /dev/null
+/* Symbol manipulating routines for the remote server for GDB.
+
+ Copyright (C) 2014-2020 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 "gdbsupport/symbol.h"
+
+/* See gdbsupport/symbol.h. */
+
+int
+find_minimal_symbol_address (const char *name, CORE_ADDR *addr,
+ struct objfile *objfile)
+{
+ gdb_assert (objfile == NULL);
+
+ return look_up_one_symbol (name, addr, 1) != 1;
+}
+++ /dev/null
-/* Target operations for the remote server for GDB.
- Copyright (C) 2002-2020 Free Software Foundation, Inc.
-
- Contributed by MontaVista Software.
-
- 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 "tracepoint.h"
-#include "gdbsupport/byte-vector.h"
-
-process_stratum_target *the_target;
-
-int
-set_desired_thread ()
-{
- client_state &cs = get_client_state ();
- thread_info *found = find_thread_ptid (cs.general_thread);
-
- current_thread = found;
- return (current_thread != NULL);
-}
-
-/* The thread that was current before prepare_to_access_memory was
- called. done_accessing_memory uses this to restore the previous
- selected thread. */
-static ptid_t prev_general_thread;
-
-/* See target.h. */
-
-int
-prepare_to_access_memory (void)
-{
- client_state &cs = get_client_state ();
-
- /* The first thread found. */
- struct thread_info *first = NULL;
- /* The first stopped thread found. */
- struct thread_info *stopped = NULL;
- /* The current general thread, if found. */
- struct thread_info *current = NULL;
-
- /* Save the general thread value, since prepare_to_access_memory could change
- it. */
- prev_general_thread = cs.general_thread;
-
- if (the_target->prepare_to_access_memory != NULL)
- {
- int res;
-
- res = the_target->prepare_to_access_memory ();
- if (res != 0)
- return res;
- }
-
- for_each_thread (prev_general_thread.pid (), [&] (thread_info *thread)
- {
- if (mythread_alive (thread->id))
- {
- if (stopped == NULL && the_target->thread_stopped != NULL
- && thread_stopped (thread))
- stopped = thread;
-
- if (first == NULL)
- first = thread;
-
- if (current == NULL && prev_general_thread == thread->id)
- current = thread;
- }
- });
-
- /* The thread we end up choosing. */
- struct thread_info *thread;
-
- /* Prefer a stopped thread. If none is found, try the current
- thread. Otherwise, take the first thread in the process. If
- none is found, undo the effects of
- target->prepare_to_access_memory() and return error. */
- if (stopped != NULL)
- thread = stopped;
- else if (current != NULL)
- thread = current;
- else if (first != NULL)
- thread = first;
- else
- {
- done_accessing_memory ();
- return 1;
- }
-
- current_thread = thread;
- cs.general_thread = ptid_of (thread);
-
- return 0;
-}
-
-/* See target.h. */
-
-void
-done_accessing_memory (void)
-{
- client_state &cs = get_client_state ();
-
- if (the_target->done_accessing_memory != NULL)
- the_target->done_accessing_memory ();
-
- /* Restore the previous selected thread. */
- cs.general_thread = prev_general_thread;
- switch_to_thread (the_target, cs.general_thread);
-}
-
-int
-read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- int res;
- res = (*the_target->read_memory) (memaddr, myaddr, len);
- check_mem_read (memaddr, myaddr, len);
- return res;
-}
-
-/* See target/target.h. */
-
-int
-target_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
-{
- return read_inferior_memory (memaddr, myaddr, len);
-}
-
-/* See target/target.h. */
-
-int
-target_read_uint32 (CORE_ADDR memaddr, uint32_t *result)
-{
- return read_inferior_memory (memaddr, (gdb_byte *) result, sizeof (*result));
-}
-
-/* See target/target.h. */
-
-int
-target_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
- ssize_t len)
-{
- /* Make a copy of the data because check_mem_write may need to
- update it. */
- gdb::byte_vector buffer (myaddr, myaddr + len);
- check_mem_write (memaddr, buffer.data (), myaddr, len);
- return (*the_target->write_memory) (memaddr, buffer.data (), len);
-}
-
-ptid_t
-mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options,
- int connected_wait)
-{
- ptid_t ret;
-
- if (connected_wait)
- server_waiting = 1;
-
- ret = target_wait (ptid, ourstatus, options);
-
- /* We don't expose _LOADED events to gdbserver core. See the
- `dlls_changed' global. */
- if (ourstatus->kind == TARGET_WAITKIND_LOADED)
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
-
- /* If GDB is connected through TCP/serial, then GDBserver will most
- probably be running on its own terminal/console, so it's nice to
- print there why is GDBserver exiting. If however, GDB is
- connected through stdio, then there's no need to spam the GDB
- console with this -- the user will already see the exit through
- regular GDB output, in that same terminal. */
- if (!remote_connection_is_stdio ())
- {
- if (ourstatus->kind == TARGET_WAITKIND_EXITED)
- fprintf (stderr,
- "\nChild exited with status %d\n", ourstatus->value.integer);
- else if (ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
- fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
- gdb_signal_to_host (ourstatus->value.sig),
- gdb_signal_to_name (ourstatus->value.sig));
- }
-
- if (connected_wait)
- server_waiting = 0;
-
- return ret;
-}
-
-/* See target/target.h. */
-
-void
-target_stop_and_wait (ptid_t ptid)
-{
- struct target_waitstatus status;
- bool was_non_stop = non_stop;
- struct thread_resume resume_info;
-
- resume_info.thread = ptid;
- resume_info.kind = resume_stop;
- resume_info.sig = GDB_SIGNAL_0;
- (*the_target->resume) (&resume_info, 1);
-
- non_stop = true;
- mywait (ptid, &status, 0, 0);
- non_stop = was_non_stop;
-}
-
-/* See target/target.h. */
-
-ptid_t
-target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
-{
- return (*the_target->wait) (ptid, status, options);
-}
-
-/* See target/target.h. */
-
-void
-target_mourn_inferior (ptid_t ptid)
-{
- (*the_target->mourn) (find_process_pid (ptid.pid ()));
-}
-
-/* See target/target.h. */
-
-void
-target_continue_no_signal (ptid_t ptid)
-{
- struct thread_resume resume_info;
-
- resume_info.thread = ptid;
- resume_info.kind = resume_continue;
- resume_info.sig = GDB_SIGNAL_0;
- (*the_target->resume) (&resume_info, 1);
-}
-
-/* See target/target.h. */
-
-void
-target_continue (ptid_t ptid, enum gdb_signal signal)
-{
- struct thread_resume resume_info;
-
- resume_info.thread = ptid;
- resume_info.kind = resume_continue;
- resume_info.sig = gdb_signal_to_host (signal);
- (*the_target->resume) (&resume_info, 1);
-}
-
-/* See target/target.h. */
-
-int
-target_supports_multi_process (void)
-{
- return (the_target->supports_multi_process != NULL ?
- (*the_target->supports_multi_process) () : 0);
-}
-
-int
-start_non_stop (int nonstop)
-{
- if (the_target->start_non_stop == NULL)
- {
- if (nonstop)
- return -1;
- else
- return 0;
- }
-
- return (*the_target->start_non_stop) (nonstop);
-}
-
-void
-set_target_ops (process_stratum_target *target)
-{
- the_target = XNEW (process_stratum_target);
- memcpy (the_target, target, sizeof (*the_target));
-}
-
-/* Convert pid to printable format. */
-
-const char *
-target_pid_to_str (ptid_t ptid)
-{
- static char buf[80];
-
- if (ptid == minus_one_ptid)
- xsnprintf (buf, sizeof (buf), "<all threads>");
- else if (ptid == null_ptid)
- xsnprintf (buf, sizeof (buf), "<null thread>");
- else if (ptid.tid () != 0)
- xsnprintf (buf, sizeof (buf), "Thread %d.0x%lx",
- ptid.pid (), ptid.tid ());
- else if (ptid.lwp () != 0)
- xsnprintf (buf, sizeof (buf), "LWP %d.%ld",
- ptid.pid (), ptid.lwp ());
- else
- xsnprintf (buf, sizeof (buf), "Process %d",
- ptid.pid ());
-
- return buf;
-}
-
-int
-kill_inferior (process_info *proc)
-{
- gdb_agent_about_to_close (proc->pid);
-
- return (*the_target->kill) (proc);
-}
-
-/* Target can do hardware single step. */
-
-int
-target_can_do_hardware_single_step (void)
-{
- return 1;
-}
-
-/* Default implementation for breakpoint_kind_for_pc.
-
- The default behavior for targets that don't implement breakpoint_kind_for_pc
- is to use the size of a breakpoint as the kind. */
-
-int
-default_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
-{
- int size = 0;
-
- gdb_assert (the_target->sw_breakpoint_from_kind != NULL);
-
- (*the_target->sw_breakpoint_from_kind) (0, &size);
- return size;
-}
-
-/* Define it. */
-
-target_terminal_state target_terminal::m_terminal_state
- = target_terminal_state::is_ours;
-
-/* See target/target.h. */
-
-void
-target_terminal::init ()
-{
- /* Placeholder needed because of fork_inferior. Not necessary on
- GDBserver. */
-}
-
-/* See target/target.h. */
-
-void
-target_terminal::inferior ()
-{
- /* Placeholder needed because of fork_inferior. Not necessary on
- GDBserver. */
-}
-
-/* See target/target.h. */
-
-void
-target_terminal::ours ()
-{
- /* Placeholder needed because of fork_inferior. Not necessary on
- GDBserver. */
-}
-
-/* See target/target.h. */
-
-void
-target_terminal::ours_for_output (void)
-{
- /* Placeholder. */
-}
-
-/* See target/target.h. */
-
-void
-target_terminal::info (const char *arg, int from_tty)
-{
- /* Placeholder. */
-}
--- /dev/null
+/* Target operations for the remote server for GDB.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ 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 "tracepoint.h"
+#include "gdbsupport/byte-vector.h"
+
+process_stratum_target *the_target;
+
+int
+set_desired_thread ()
+{
+ client_state &cs = get_client_state ();
+ thread_info *found = find_thread_ptid (cs.general_thread);
+
+ current_thread = found;
+ return (current_thread != NULL);
+}
+
+/* The thread that was current before prepare_to_access_memory was
+ called. done_accessing_memory uses this to restore the previous
+ selected thread. */
+static ptid_t prev_general_thread;
+
+/* See target.h. */
+
+int
+prepare_to_access_memory (void)
+{
+ client_state &cs = get_client_state ();
+
+ /* The first thread found. */
+ struct thread_info *first = NULL;
+ /* The first stopped thread found. */
+ struct thread_info *stopped = NULL;
+ /* The current general thread, if found. */
+ struct thread_info *current = NULL;
+
+ /* Save the general thread value, since prepare_to_access_memory could change
+ it. */
+ prev_general_thread = cs.general_thread;
+
+ if (the_target->prepare_to_access_memory != NULL)
+ {
+ int res;
+
+ res = the_target->prepare_to_access_memory ();
+ if (res != 0)
+ return res;
+ }
+
+ for_each_thread (prev_general_thread.pid (), [&] (thread_info *thread)
+ {
+ if (mythread_alive (thread->id))
+ {
+ if (stopped == NULL && the_target->thread_stopped != NULL
+ && thread_stopped (thread))
+ stopped = thread;
+
+ if (first == NULL)
+ first = thread;
+
+ if (current == NULL && prev_general_thread == thread->id)
+ current = thread;
+ }
+ });
+
+ /* The thread we end up choosing. */
+ struct thread_info *thread;
+
+ /* Prefer a stopped thread. If none is found, try the current
+ thread. Otherwise, take the first thread in the process. If
+ none is found, undo the effects of
+ target->prepare_to_access_memory() and return error. */
+ if (stopped != NULL)
+ thread = stopped;
+ else if (current != NULL)
+ thread = current;
+ else if (first != NULL)
+ thread = first;
+ else
+ {
+ done_accessing_memory ();
+ return 1;
+ }
+
+ current_thread = thread;
+ cs.general_thread = ptid_of (thread);
+
+ return 0;
+}
+
+/* See target.h. */
+
+void
+done_accessing_memory (void)
+{
+ client_state &cs = get_client_state ();
+
+ if (the_target->done_accessing_memory != NULL)
+ the_target->done_accessing_memory ();
+
+ /* Restore the previous selected thread. */
+ cs.general_thread = prev_general_thread;
+ switch_to_thread (the_target, cs.general_thread);
+}
+
+int
+read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ int res;
+ res = (*the_target->read_memory) (memaddr, myaddr, len);
+ check_mem_read (memaddr, myaddr, len);
+ return res;
+}
+
+/* See target/target.h. */
+
+int
+target_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
+{
+ return read_inferior_memory (memaddr, myaddr, len);
+}
+
+/* See target/target.h. */
+
+int
+target_read_uint32 (CORE_ADDR memaddr, uint32_t *result)
+{
+ return read_inferior_memory (memaddr, (gdb_byte *) result, sizeof (*result));
+}
+
+/* See target/target.h. */
+
+int
+target_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ ssize_t len)
+{
+ /* Make a copy of the data because check_mem_write may need to
+ update it. */
+ gdb::byte_vector buffer (myaddr, myaddr + len);
+ check_mem_write (memaddr, buffer.data (), myaddr, len);
+ return (*the_target->write_memory) (memaddr, buffer.data (), len);
+}
+
+ptid_t
+mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options,
+ int connected_wait)
+{
+ ptid_t ret;
+
+ if (connected_wait)
+ server_waiting = 1;
+
+ ret = target_wait (ptid, ourstatus, options);
+
+ /* We don't expose _LOADED events to gdbserver core. See the
+ `dlls_changed' global. */
+ if (ourstatus->kind == TARGET_WAITKIND_LOADED)
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+ /* If GDB is connected through TCP/serial, then GDBserver will most
+ probably be running on its own terminal/console, so it's nice to
+ print there why is GDBserver exiting. If however, GDB is
+ connected through stdio, then there's no need to spam the GDB
+ console with this -- the user will already see the exit through
+ regular GDB output, in that same terminal. */
+ if (!remote_connection_is_stdio ())
+ {
+ if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+ fprintf (stderr,
+ "\nChild exited with status %d\n", ourstatus->value.integer);
+ else if (ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
+ fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
+ gdb_signal_to_host (ourstatus->value.sig),
+ gdb_signal_to_name (ourstatus->value.sig));
+ }
+
+ if (connected_wait)
+ server_waiting = 0;
+
+ return ret;
+}
+
+/* See target/target.h. */
+
+void
+target_stop_and_wait (ptid_t ptid)
+{
+ struct target_waitstatus status;
+ bool was_non_stop = non_stop;
+ struct thread_resume resume_info;
+
+ resume_info.thread = ptid;
+ resume_info.kind = resume_stop;
+ resume_info.sig = GDB_SIGNAL_0;
+ (*the_target->resume) (&resume_info, 1);
+
+ non_stop = true;
+ mywait (ptid, &status, 0, 0);
+ non_stop = was_non_stop;
+}
+
+/* See target/target.h. */
+
+ptid_t
+target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
+{
+ return (*the_target->wait) (ptid, status, options);
+}
+
+/* See target/target.h. */
+
+void
+target_mourn_inferior (ptid_t ptid)
+{
+ (*the_target->mourn) (find_process_pid (ptid.pid ()));
+}
+
+/* See target/target.h. */
+
+void
+target_continue_no_signal (ptid_t ptid)
+{
+ struct thread_resume resume_info;
+
+ resume_info.thread = ptid;
+ resume_info.kind = resume_continue;
+ resume_info.sig = GDB_SIGNAL_0;
+ (*the_target->resume) (&resume_info, 1);
+}
+
+/* See target/target.h. */
+
+void
+target_continue (ptid_t ptid, enum gdb_signal signal)
+{
+ struct thread_resume resume_info;
+
+ resume_info.thread = ptid;
+ resume_info.kind = resume_continue;
+ resume_info.sig = gdb_signal_to_host (signal);
+ (*the_target->resume) (&resume_info, 1);
+}
+
+/* See target/target.h. */
+
+int
+target_supports_multi_process (void)
+{
+ return (the_target->supports_multi_process != NULL ?
+ (*the_target->supports_multi_process) () : 0);
+}
+
+int
+start_non_stop (int nonstop)
+{
+ if (the_target->start_non_stop == NULL)
+ {
+ if (nonstop)
+ return -1;
+ else
+ return 0;
+ }
+
+ return (*the_target->start_non_stop) (nonstop);
+}
+
+void
+set_target_ops (process_stratum_target *target)
+{
+ the_target = XNEW (process_stratum_target);
+ memcpy (the_target, target, sizeof (*the_target));
+}
+
+/* Convert pid to printable format. */
+
+const char *
+target_pid_to_str (ptid_t ptid)
+{
+ static char buf[80];
+
+ if (ptid == minus_one_ptid)
+ xsnprintf (buf, sizeof (buf), "<all threads>");
+ else if (ptid == null_ptid)
+ xsnprintf (buf, sizeof (buf), "<null thread>");
+ else if (ptid.tid () != 0)
+ xsnprintf (buf, sizeof (buf), "Thread %d.0x%lx",
+ ptid.pid (), ptid.tid ());
+ else if (ptid.lwp () != 0)
+ xsnprintf (buf, sizeof (buf), "LWP %d.%ld",
+ ptid.pid (), ptid.lwp ());
+ else
+ xsnprintf (buf, sizeof (buf), "Process %d",
+ ptid.pid ());
+
+ return buf;
+}
+
+int
+kill_inferior (process_info *proc)
+{
+ gdb_agent_about_to_close (proc->pid);
+
+ return (*the_target->kill) (proc);
+}
+
+/* Target can do hardware single step. */
+
+int
+target_can_do_hardware_single_step (void)
+{
+ return 1;
+}
+
+/* Default implementation for breakpoint_kind_for_pc.
+
+ The default behavior for targets that don't implement breakpoint_kind_for_pc
+ is to use the size of a breakpoint as the kind. */
+
+int
+default_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+ int size = 0;
+
+ gdb_assert (the_target->sw_breakpoint_from_kind != NULL);
+
+ (*the_target->sw_breakpoint_from_kind) (0, &size);
+ return size;
+}
+
+/* Define it. */
+
+target_terminal_state target_terminal::m_terminal_state
+ = target_terminal_state::is_ours;
+
+/* See target/target.h. */
+
+void
+target_terminal::init ()
+{
+ /* Placeholder needed because of fork_inferior. Not necessary on
+ GDBserver. */
+}
+
+/* See target/target.h. */
+
+void
+target_terminal::inferior ()
+{
+ /* Placeholder needed because of fork_inferior. Not necessary on
+ GDBserver. */
+}
+
+/* See target/target.h. */
+
+void
+target_terminal::ours ()
+{
+ /* Placeholder needed because of fork_inferior. Not necessary on
+ GDBserver. */
+}
+
+/* See target/target.h. */
+
+void
+target_terminal::ours_for_output (void)
+{
+ /* Placeholder. */
+}
+
+/* See target/target.h. */
+
+void
+target_terminal::info (const char *arg, int from_tty)
+{
+ /* Placeholder. */
+}
+++ /dev/null
-/* Copyright (C) 2012-2020 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 "tdesc.h"
-#include "regdef.h"
-
-#ifndef IN_PROCESS_AGENT
-
-target_desc::~target_desc ()
-{
- xfree ((char *) arch);
- xfree ((char *) osabi);
-}
-
-bool target_desc::operator== (const target_desc &other) const
-{
- if (reg_defs != other.reg_defs)
- return false;
-
- /* Compare expedite_regs. */
- int i = 0;
- for (; expedite_regs[i] != NULL; i++)
- {
- if (strcmp (expedite_regs[i], other.expedite_regs[i]) != 0)
- return false;
- }
- if (other.expedite_regs[i] != NULL)
- return false;
-
- return true;
-}
-
-#endif
-
-void target_desc::accept (tdesc_element_visitor &v) const
-{
-#ifndef IN_PROCESS_AGENT
- v.visit_pre (this);
-
- for (const tdesc_feature_up &feature : features)
- feature->accept (v);
-
- v.visit_post (this);
-#endif
-}
-
-void
-init_target_desc (struct target_desc *tdesc,
- const char **expedite_regs)
-{
- int offset = 0;
-
- /* Go through all the features and populate reg_defs. */
- for (const tdesc_feature_up &feature : tdesc->features)
- for (const tdesc_reg_up &treg : feature->registers)
- {
- int regnum = treg->target_regnum;
-
- /* Register number will increase (possibly with gaps) or be zero. */
- gdb_assert (regnum == 0 || regnum >= tdesc->reg_defs.size ());
-
- if (regnum != 0)
- tdesc->reg_defs.resize (regnum, reg (offset));
-
- tdesc->reg_defs.emplace_back (treg->name.c_str (), offset,
- treg->bitsize);
- offset += treg->bitsize;
- }
-
- tdesc->registers_size = offset / 8;
-
- /* Make sure PBUFSIZ is large enough to hold a full register
- packet. */
- gdb_assert (2 * tdesc->registers_size + 32 <= PBUFSIZ);
-
-#ifndef IN_PROCESS_AGENT
- tdesc->expedite_regs = expedite_regs;
-#endif
-}
-
-struct target_desc *
-allocate_target_description (void)
-{
- return new target_desc ();
-}
-
-#ifndef IN_PROCESS_AGENT
-
-static const struct target_desc default_description {};
-
-void
-copy_target_description (struct target_desc *dest,
- const struct target_desc *src)
-{
- dest->reg_defs = src->reg_defs;
- dest->expedite_regs = src->expedite_regs;
- dest->registers_size = src->registers_size;
- dest->xmltarget = src->xmltarget;
-}
-
-const struct target_desc *
-current_target_desc (void)
-{
- if (current_thread == NULL)
- return &default_description;
-
- return current_process ()->tdesc;
-}
-
-/* See gdbsupport/tdesc.h. */
-
-const char *
-tdesc_architecture_name (const struct target_desc *target_desc)
-{
- return target_desc->arch;
-}
-
-/* See gdbsupport/tdesc.h. */
-
-void
-set_tdesc_architecture (struct target_desc *target_desc,
- const char *name)
-{
- target_desc->arch = xstrdup (name);
-}
-
-/* See gdbsupport/tdesc.h. */
-
-const char *
-tdesc_osabi_name (const struct target_desc *target_desc)
-{
- return target_desc->osabi;
-}
-
-/* See gdbsupport/tdesc.h. */
-
-void
-set_tdesc_osabi (struct target_desc *target_desc, const char *name)
-{
- target_desc->osabi = xstrdup (name);
-}
-
-/* See gdbsupport/tdesc.h. */
-
-const char *
-tdesc_get_features_xml (const target_desc *tdesc)
-{
- /* Either .xmltarget or .features is not NULL. */
- gdb_assert (tdesc->xmltarget != NULL
- || (!tdesc->features.empty ()
- && tdesc->arch != NULL));
-
- if (tdesc->xmltarget == NULL)
- {
- std::string buffer ("@");
- print_xml_feature v (&buffer);
- tdesc->accept (v);
- tdesc->xmltarget = xstrdup (buffer.c_str ());
- }
-
- return tdesc->xmltarget;
-}
-#endif
-
-/* See gdbsupport/tdesc.h. */
-
-struct tdesc_feature *
-tdesc_create_feature (struct target_desc *tdesc, const char *name)
-{
- struct tdesc_feature *new_feature = new tdesc_feature (name);
- tdesc->features.emplace_back (new_feature);
- return new_feature;
-}
-
-/* See gdbsupport/tdesc.h. */
-
-bool
-tdesc_contains_feature (const target_desc *tdesc, const std::string &feature)
-{
- gdb_assert (tdesc != nullptr);
-
- for (const tdesc_feature_up &f : tdesc->features)
- {
- if (f->name == feature)
- return true;
- }
-
- return false;
-}
--- /dev/null
+/* Copyright (C) 2012-2020 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 "tdesc.h"
+#include "regdef.h"
+
+#ifndef IN_PROCESS_AGENT
+
+target_desc::~target_desc ()
+{
+ xfree ((char *) arch);
+ xfree ((char *) osabi);
+}
+
+bool target_desc::operator== (const target_desc &other) const
+{
+ if (reg_defs != other.reg_defs)
+ return false;
+
+ /* Compare expedite_regs. */
+ int i = 0;
+ for (; expedite_regs[i] != NULL; i++)
+ {
+ if (strcmp (expedite_regs[i], other.expedite_regs[i]) != 0)
+ return false;
+ }
+ if (other.expedite_regs[i] != NULL)
+ return false;
+
+ return true;
+}
+
+#endif
+
+void target_desc::accept (tdesc_element_visitor &v) const
+{
+#ifndef IN_PROCESS_AGENT
+ v.visit_pre (this);
+
+ for (const tdesc_feature_up &feature : features)
+ feature->accept (v);
+
+ v.visit_post (this);
+#endif
+}
+
+void
+init_target_desc (struct target_desc *tdesc,
+ const char **expedite_regs)
+{
+ int offset = 0;
+
+ /* Go through all the features and populate reg_defs. */
+ for (const tdesc_feature_up &feature : tdesc->features)
+ for (const tdesc_reg_up &treg : feature->registers)
+ {
+ int regnum = treg->target_regnum;
+
+ /* Register number will increase (possibly with gaps) or be zero. */
+ gdb_assert (regnum == 0 || regnum >= tdesc->reg_defs.size ());
+
+ if (regnum != 0)
+ tdesc->reg_defs.resize (regnum, reg (offset));
+
+ tdesc->reg_defs.emplace_back (treg->name.c_str (), offset,
+ treg->bitsize);
+ offset += treg->bitsize;
+ }
+
+ tdesc->registers_size = offset / 8;
+
+ /* Make sure PBUFSIZ is large enough to hold a full register
+ packet. */
+ gdb_assert (2 * tdesc->registers_size + 32 <= PBUFSIZ);
+
+#ifndef IN_PROCESS_AGENT
+ tdesc->expedite_regs = expedite_regs;
+#endif
+}
+
+struct target_desc *
+allocate_target_description (void)
+{
+ return new target_desc ();
+}
+
+#ifndef IN_PROCESS_AGENT
+
+static const struct target_desc default_description {};
+
+void
+copy_target_description (struct target_desc *dest,
+ const struct target_desc *src)
+{
+ dest->reg_defs = src->reg_defs;
+ dest->expedite_regs = src->expedite_regs;
+ dest->registers_size = src->registers_size;
+ dest->xmltarget = src->xmltarget;
+}
+
+const struct target_desc *
+current_target_desc (void)
+{
+ if (current_thread == NULL)
+ return &default_description;
+
+ return current_process ()->tdesc;
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const char *
+tdesc_architecture_name (const struct target_desc *target_desc)
+{
+ return target_desc->arch;
+}
+
+/* See gdbsupport/tdesc.h. */
+
+void
+set_tdesc_architecture (struct target_desc *target_desc,
+ const char *name)
+{
+ target_desc->arch = xstrdup (name);
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const char *
+tdesc_osabi_name (const struct target_desc *target_desc)
+{
+ return target_desc->osabi;
+}
+
+/* See gdbsupport/tdesc.h. */
+
+void
+set_tdesc_osabi (struct target_desc *target_desc, const char *name)
+{
+ target_desc->osabi = xstrdup (name);
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const char *
+tdesc_get_features_xml (const target_desc *tdesc)
+{
+ /* Either .xmltarget or .features is not NULL. */
+ gdb_assert (tdesc->xmltarget != NULL
+ || (!tdesc->features.empty ()
+ && tdesc->arch != NULL));
+
+ if (tdesc->xmltarget == NULL)
+ {
+ std::string buffer ("@");
+ print_xml_feature v (&buffer);
+ tdesc->accept (v);
+ tdesc->xmltarget = xstrdup (buffer.c_str ());
+ }
+
+ return tdesc->xmltarget;
+}
+#endif
+
+/* See gdbsupport/tdesc.h. */
+
+struct tdesc_feature *
+tdesc_create_feature (struct target_desc *tdesc, const char *name)
+{
+ struct tdesc_feature *new_feature = new tdesc_feature (name);
+ tdesc->features.emplace_back (new_feature);
+ return new_feature;
+}
+
+/* See gdbsupport/tdesc.h. */
+
+bool
+tdesc_contains_feature (const target_desc *tdesc, const std::string &feature)
+{
+ gdb_assert (tdesc != nullptr);
+
+ for (const tdesc_feature_up &f : tdesc->features)
+ {
+ if (f->name == feature)
+ return true;
+ }
+
+ return false;
+}
+++ /dev/null
-/* Thread management interface, for the remote server for GDB.
- Copyright (C) 2002-2020 Free Software Foundation, Inc.
-
- Contributed by MontaVista Software.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "server.h"
-
-#include "linux-low.h"
-
-#include "debug.h"
-#include "gdb_proc_service.h"
-#include "nat/gdb_thread_db.h"
-#include "gdbsupport/gdb_vecs.h"
-#include "nat/linux-procfs.h"
-#include "gdbsupport/scoped_restore.h"
-
-#ifndef USE_LIBTHREAD_DB_DIRECTLY
-#include <dlfcn.h>
-#endif
-#include <limits.h>
-#include <ctype.h>
-
-struct thread_db
-{
- /* Structure that identifies the child process for the
- <proc_service.h> interface. */
- struct ps_prochandle proc_handle;
-
- /* Connection to the libthread_db library. */
- td_thragent_t *thread_agent;
-
- /* If this flag has been set, we've already asked GDB for all
- symbols we might need; assume symbol cache misses are
- failures. */
- int all_symbols_looked_up;
-
-#ifndef USE_LIBTHREAD_DB_DIRECTLY
- /* Handle of the libthread_db from dlopen. */
- void *handle;
-#endif
-
- /* Addresses of libthread_db functions. */
- td_ta_new_ftype *td_ta_new_p;
- td_ta_map_lwp2thr_ftype *td_ta_map_lwp2thr_p;
- td_thr_get_info_ftype *td_thr_get_info_p;
- td_ta_thr_iter_ftype *td_ta_thr_iter_p;
- td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
- td_thr_tlsbase_ftype *td_thr_tlsbase_p;
- td_symbol_list_ftype *td_symbol_list_p;
-};
-
-static char *libthread_db_search_path;
-
-static int find_one_thread (ptid_t);
-static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data);
-
-static const char *
-thread_db_err_str (td_err_e err)
-{
- static char buf[64];
-
- switch (err)
- {
- case TD_OK:
- return "generic 'call succeeded'";
- case TD_ERR:
- return "generic error";
- case TD_NOTHR:
- return "no thread to satisfy query";
- case TD_NOSV:
- return "no sync handle to satisfy query";
- case TD_NOLWP:
- return "no LWP to satisfy query";
- case TD_BADPH:
- return "invalid process handle";
- case TD_BADTH:
- return "invalid thread handle";
- case TD_BADSH:
- return "invalid synchronization handle";
- case TD_BADTA:
- return "invalid thread agent";
- case TD_BADKEY:
- return "invalid key";
- case TD_NOMSG:
- return "no event message for getmsg";
- case TD_NOFPREGS:
- return "FPU register set not available";
- case TD_NOLIBTHREAD:
- return "application not linked with libthread";
- case TD_NOEVENT:
- return "requested event is not supported";
- case TD_NOCAPAB:
- return "capability not available";
- case TD_DBERR:
- return "debugger service failed";
- case TD_NOAPLIC:
- return "operation not applicable to";
- case TD_NOTSD:
- return "no thread-specific data for this thread";
- case TD_MALLOC:
- return "malloc failed";
- case TD_PARTIALREG:
- return "only part of register set was written/read";
- case TD_NOXREGS:
- return "X register set not available for this thread";
-#ifdef HAVE_TD_VERSION
- case TD_VERSION:
- return "version mismatch between libthread_db and libpthread";
-#endif
- default:
- xsnprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
- return buf;
- }
-}
-
-#if 0
-static char *
-thread_db_state_str (td_thr_state_e state)
-{
- static char buf[64];
-
- switch (state)
- {
- case TD_THR_STOPPED:
- return "stopped by debugger";
- case TD_THR_RUN:
- return "runnable";
- case TD_THR_ACTIVE:
- return "active";
- case TD_THR_ZOMBIE:
- return "zombie";
- case TD_THR_SLEEP:
- return "sleeping";
- case TD_THR_STOPPED_ASLEEP:
- return "stopped by debugger AND blocked";
- default:
- xsnprintf (buf, sizeof (buf), "unknown thread_db state %d", state);
- return buf;
- }
-}
-#endif
-
-/* Get thread info about PTID, accessing memory via the current
- thread. */
-
-static int
-find_one_thread (ptid_t ptid)
-{
- td_thrhandle_t th;
- td_thrinfo_t ti;
- td_err_e err;
- struct lwp_info *lwp;
- struct thread_db *thread_db = current_process ()->priv->thread_db;
- int lwpid = ptid.lwp ();
-
- thread_info *thread = find_thread_ptid (ptid);
- lwp = get_thread_lwp (thread);
- if (lwp->thread_known)
- return 1;
-
- /* Get information about this thread. */
- err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, &th);
- if (err != TD_OK)
- error ("Cannot get thread handle for LWP %d: %s",
- lwpid, thread_db_err_str (err));
-
- err = thread_db->td_thr_get_info_p (&th, &ti);
- if (err != TD_OK)
- error ("Cannot get thread info for LWP %d: %s",
- lwpid, thread_db_err_str (err));
-
- if (debug_threads)
- debug_printf ("Found thread %ld (LWP %d)\n",
- (unsigned long) ti.ti_tid, ti.ti_lid);
-
- if (lwpid != ti.ti_lid)
- {
- warning ("PID mismatch! Expected %ld, got %ld",
- (long) lwpid, (long) ti.ti_lid);
- return 0;
- }
-
- /* If the new thread ID is zero, a final thread ID will be available
- later. Do not enable thread debugging yet. */
- if (ti.ti_tid == 0)
- return 0;
-
- lwp->thread_known = 1;
- lwp->th = th;
- lwp->thread_handle = ti.ti_tid;
-
- return 1;
-}
-
-/* Attach a thread. Return true on success. */
-
-static int
-attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
-{
- struct process_info *proc = current_process ();
- int pid = pid_of (proc);
- ptid_t ptid = ptid_t (pid, ti_p->ti_lid, 0);
- struct lwp_info *lwp;
- int err;
-
- if (debug_threads)
- debug_printf ("Attaching to thread %ld (LWP %d)\n",
- (unsigned long) ti_p->ti_tid, ti_p->ti_lid);
- err = linux_attach_lwp (ptid);
- if (err != 0)
- {
- std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
-
- warning ("Could not attach to thread %ld (LWP %d): %s",
- (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
-
- return 0;
- }
-
- lwp = find_lwp_pid (ptid);
- gdb_assert (lwp != NULL);
- lwp->thread_known = 1;
- lwp->th = *th_p;
- lwp->thread_handle = ti_p->ti_tid;
-
- return 1;
-}
-
-/* Attach thread if we haven't seen it yet.
- Increment *COUNTER if we have attached a new thread.
- Return false on failure. */
-
-static int
-maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p,
- int *counter)
-{
- struct lwp_info *lwp;
-
- lwp = find_lwp_pid (ptid_t (ti_p->ti_lid));
- if (lwp != NULL)
- return 1;
-
- if (!attach_thread (th_p, ti_p))
- return 0;
-
- if (counter != NULL)
- *counter += 1;
-
- return 1;
-}
-
-static int
-find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
-{
- td_thrinfo_t ti;
- td_err_e err;
- struct thread_db *thread_db = current_process ()->priv->thread_db;
-
- err = thread_db->td_thr_get_info_p (th_p, &ti);
- if (err != TD_OK)
- error ("Cannot get thread info: %s", thread_db_err_str (err));
-
- if (ti.ti_lid == -1)
- {
- /* A thread with kernel thread ID -1 is either a thread that
- exited and was joined, or a thread that is being created but
- hasn't started yet, and that is reusing the tcb/stack of a
- thread that previously exited and was joined. (glibc marks
- terminated and joined threads with kernel thread ID -1. See
- glibc PR17707. */
- if (debug_threads)
- debug_printf ("thread_db: skipping exited and "
- "joined thread (0x%lx)\n",
- (unsigned long) ti.ti_tid);
- return 0;
- }
-
- /* Check for zombies. */
- if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
- return 0;
-
- if (!maybe_attach_thread (th_p, &ti, (int *) data))
- {
- /* Terminate iteration early: we might be looking at stale data in
- the inferior. The thread_db_find_new_threads will retry. */
- return 1;
- }
-
- return 0;
-}
-
-static void
-thread_db_find_new_threads (void)
-{
- td_err_e err;
- ptid_t ptid = current_ptid;
- struct thread_db *thread_db = current_process ()->priv->thread_db;
- int loop, iteration;
-
- /* This function is only called when we first initialize thread_db.
- First locate the initial thread. If it is not ready for
- debugging yet, then stop. */
- if (find_one_thread (ptid) == 0)
- return;
-
- /* Require 4 successive iterations which do not find any new threads.
- The 4 is a heuristic: there is an inherent race here, and I have
- seen that 2 iterations in a row are not always sufficient to
- "capture" all threads. */
- for (loop = 0, iteration = 0; loop < 4; ++loop, ++iteration)
- {
- int new_thread_count = 0;
-
- /* Iterate over all user-space threads to discover new threads. */
- err = thread_db->td_ta_thr_iter_p (thread_db->thread_agent,
- find_new_threads_callback,
- &new_thread_count,
- TD_THR_ANY_STATE,
- TD_THR_LOWEST_PRIORITY,
- TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
- if (debug_threads)
- debug_printf ("Found %d threads in iteration %d.\n",
- new_thread_count, iteration);
-
- if (new_thread_count != 0)
- {
- /* Found new threads. Restart iteration from beginning. */
- loop = -1;
- }
- }
- if (err != TD_OK)
- error ("Cannot find new threads: %s", thread_db_err_str (err));
-}
-
-/* Cache all future symbols that thread_db might request. We can not
- request symbols at arbitrary states in the remote protocol, only
- when the client tells us that new symbols are available. So when
- we load the thread library, make sure to check the entire list. */
-
-static void
-thread_db_look_up_symbols (void)
-{
- struct thread_db *thread_db = current_process ()->priv->thread_db;
- const char **sym_list;
- CORE_ADDR unused;
-
- for (sym_list = thread_db->td_symbol_list_p (); *sym_list; sym_list++)
- look_up_one_symbol (*sym_list, &unused, 1);
-
- /* We're not interested in any other libraries loaded after this
- point, only in symbols in libpthread.so. */
- thread_db->all_symbols_looked_up = 1;
-}
-
-int
-thread_db_look_up_one_symbol (const char *name, CORE_ADDR *addrp)
-{
- struct thread_db *thread_db = current_process ()->priv->thread_db;
- int may_ask_gdb = !thread_db->all_symbols_looked_up;
-
- /* If we've passed the call to thread_db_look_up_symbols, then
- anything not in the cache must not exist; we're not interested
- in any libraries loaded after that point, only in symbols in
- libpthread.so. It might not be an appropriate time to look
- up a symbol, e.g. while we're trying to fetch registers. */
- return look_up_one_symbol (name, addrp, may_ask_gdb);
-}
-
-int
-thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
- CORE_ADDR load_module, CORE_ADDR *address)
-{
- psaddr_t addr;
- td_err_e err;
- struct lwp_info *lwp;
- struct thread_info *saved_thread;
- struct process_info *proc;
- struct thread_db *thread_db;
-
- proc = get_thread_process (thread);
- thread_db = proc->priv->thread_db;
-
- /* If the thread layer is not (yet) initialized, fail. */
- if (thread_db == NULL || !thread_db->all_symbols_looked_up)
- return TD_ERR;
-
- /* If td_thr_tls_get_addr is missing rather do not expect td_thr_tlsbase
- could work. */
- if (thread_db->td_thr_tls_get_addr_p == NULL
- || (load_module == 0 && thread_db->td_thr_tlsbase_p == NULL))
- return -1;
-
- lwp = get_thread_lwp (thread);
- if (!lwp->thread_known)
- find_one_thread (thread->id);
- if (!lwp->thread_known)
- return TD_NOTHR;
-
- saved_thread = current_thread;
- current_thread = thread;
-
- if (load_module != 0)
- {
- /* Note the cast through uintptr_t: this interface only works if
- a target address fits in a psaddr_t, which is a host pointer.
- So a 32-bit debugger can not access 64-bit TLS through this. */
- err = thread_db->td_thr_tls_get_addr_p (&lwp->th,
- (psaddr_t) (uintptr_t) load_module,
- offset, &addr);
- }
- else
- {
- /* This code path handles the case of -static -pthread executables:
- https://sourceware.org/ml/libc-help/2014-03/msg00024.html
- For older GNU libc r_debug.r_map is NULL. For GNU libc after
- PR libc/16831 due to GDB PR threads/16954 LOAD_MODULE is also NULL.
- The constant number 1 depends on GNU __libc_setup_tls
- initialization of l_tls_modid to 1. */
- err = thread_db->td_thr_tlsbase_p (&lwp->th, 1, &addr);
- addr = (char *) addr + offset;
- }
-
- current_thread = saved_thread;
- if (err == TD_OK)
- {
- *address = (CORE_ADDR) (uintptr_t) addr;
- return 0;
- }
- else
- return err;
-}
-
-/* See linux-low.h. */
-
-bool
-thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len)
-{
- struct thread_db *thread_db;
- struct lwp_info *lwp;
- thread_info *thread = find_thread_ptid (ptid);
-
- if (thread == NULL)
- return false;
-
- thread_db = get_thread_process (thread)->priv->thread_db;
-
- if (thread_db == NULL)
- return false;
-
- lwp = get_thread_lwp (thread);
-
- if (!lwp->thread_known && !find_one_thread (thread->id))
- return false;
-
- gdb_assert (lwp->thread_known);
-
- *handle = (gdb_byte *) &lwp->thread_handle;
- *handle_len = sizeof (lwp->thread_handle);
- return true;
-}
-
-#ifdef USE_LIBTHREAD_DB_DIRECTLY
-
-static int
-thread_db_load_search (void)
-{
- td_err_e err;
- struct thread_db *tdb;
- struct process_info *proc = current_process ();
-
- gdb_assert (proc->priv->thread_db == NULL);
-
- tdb = XCNEW (struct thread_db);
- proc->priv->thread_db = tdb;
-
- tdb->td_ta_new_p = &td_ta_new;
-
- /* Attempt to open a connection to the thread library. */
- err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent);
- if (err != TD_OK)
- {
- if (debug_threads)
- debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err));
- free (tdb);
- proc->priv->thread_db = NULL;
- return 0;
- }
-
- tdb->td_ta_map_lwp2thr_p = &td_ta_map_lwp2thr;
- tdb->td_thr_get_info_p = &td_thr_get_info;
- tdb->td_ta_thr_iter_p = &td_ta_thr_iter;
- tdb->td_symbol_list_p = &td_symbol_list;
-
- /* These are not essential. */
- tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr;
- tdb->td_thr_tlsbase_p = &td_thr_tlsbase;
-
- return 1;
-}
-
-#else
-
-static int
-try_thread_db_load_1 (void *handle)
-{
- td_err_e err;
- struct thread_db *tdb;
- struct process_info *proc = current_process ();
-
- gdb_assert (proc->priv->thread_db == NULL);
-
- tdb = XCNEW (struct thread_db);
- proc->priv->thread_db = tdb;
-
- tdb->handle = handle;
-
- /* Initialize pointers to the dynamic library functions we will use.
- Essential functions first. */
-
-#define CHK(required, a) \
- do \
- { \
- if ((a) == NULL) \
- { \
- if (debug_threads) \
- debug_printf ("dlsym: %s\n", dlerror ()); \
- if (required) \
- { \
- free (tdb); \
- proc->priv->thread_db = NULL; \
- return 0; \
- } \
- } \
- } \
- while (0)
-
-#define TDB_DLSYM(tdb, func) \
- tdb->func ## _p = (func ## _ftype *) dlsym (tdb->handle, #func)
-
- CHK (1, TDB_DLSYM (tdb, td_ta_new));
-
- /* Attempt to open a connection to the thread library. */
- err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent);
- if (err != TD_OK)
- {
- if (debug_threads)
- debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err));
- free (tdb);
- proc->priv->thread_db = NULL;
- return 0;
- }
-
- CHK (1, TDB_DLSYM (tdb, td_ta_map_lwp2thr));
- CHK (1, TDB_DLSYM (tdb, td_thr_get_info));
- CHK (1, TDB_DLSYM (tdb, td_ta_thr_iter));
- CHK (1, TDB_DLSYM (tdb, td_symbol_list));
-
- /* These are not essential. */
- CHK (0, TDB_DLSYM (tdb, td_thr_tls_get_addr));
- CHK (0, TDB_DLSYM (tdb, td_thr_tlsbase));
-
-#undef CHK
-#undef TDB_DLSYM
-
- return 1;
-}
-
-#ifdef HAVE_DLADDR
-
-/* Lookup a library in which given symbol resides.
- Note: this is looking in the GDBSERVER process, not in the inferior.
- Returns library name, or NULL. */
-
-static const char *
-dladdr_to_soname (const void *addr)
-{
- Dl_info info;
-
- if (dladdr (addr, &info) != 0)
- return info.dli_fname;
- return NULL;
-}
-
-#endif
-
-static int
-try_thread_db_load (const char *library)
-{
- void *handle;
-
- if (debug_threads)
- debug_printf ("Trying host libthread_db library: %s.\n",
- library);
- handle = dlopen (library, RTLD_NOW);
- if (handle == NULL)
- {
- if (debug_threads)
- debug_printf ("dlopen failed: %s.\n", dlerror ());
- return 0;
- }
-
-#ifdef HAVE_DLADDR
- if (debug_threads && strchr (library, '/') == NULL)
- {
- void *td_init;
-
- td_init = dlsym (handle, "td_init");
- if (td_init != NULL)
- {
- const char *const libpath = dladdr_to_soname (td_init);
-
- if (libpath != NULL)
- debug_printf ("Host %s resolved to: %s.\n", library, libpath);
- }
- }
-#endif
-
- if (try_thread_db_load_1 (handle))
- return 1;
-
- /* This library "refused" to work on current inferior. */
- dlclose (handle);
- return 0;
-}
-
-/* Handle $sdir in libthread-db-search-path.
- Look for libthread_db in the system dirs, or wherever a plain
- dlopen(file_without_path) will look.
- The result is true for success. */
-
-static int
-try_thread_db_load_from_sdir (void)
-{
- return try_thread_db_load (LIBTHREAD_DB_SO);
-}
-
-/* Try to load libthread_db from directory DIR of length DIR_LEN.
- The result is true for success. */
-
-static int
-try_thread_db_load_from_dir (const char *dir, size_t dir_len)
-{
- char path[PATH_MAX];
-
- if (dir_len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path))
- {
- char *cp = (char *) xmalloc (dir_len + 1);
-
- memcpy (cp, dir, dir_len);
- cp[dir_len] = '\0';
- warning (_("libthread-db-search-path component too long,"
- " ignored: %s."), cp);
- free (cp);
- return 0;
- }
-
- memcpy (path, dir, dir_len);
- path[dir_len] = '/';
- strcpy (path + dir_len + 1, LIBTHREAD_DB_SO);
- return try_thread_db_load (path);
-}
-
-/* Search libthread_db_search_path for libthread_db which "agrees"
- to work on current inferior.
- The result is true for success. */
-
-static int
-thread_db_load_search (void)
-{
- int rc = 0;
-
- if (libthread_db_search_path == NULL)
- libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH);
-
- std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec
- = dirnames_to_char_ptr_vec (libthread_db_search_path);
-
- for (const gdb::unique_xmalloc_ptr<char> &this_dir_up : dir_vec)
- {
- char *this_dir = this_dir_up.get ();
- const int pdir_len = sizeof ("$pdir") - 1;
- size_t this_dir_len;
-
- this_dir_len = strlen (this_dir);
-
- if (strncmp (this_dir, "$pdir", pdir_len) == 0
- && (this_dir[pdir_len] == '\0'
- || this_dir[pdir_len] == '/'))
- {
- /* We don't maintain a list of loaded libraries so we don't know
- where libpthread lives. We *could* fetch the info, but we don't
- do that yet. Ignore it. */
- }
- else if (strcmp (this_dir, "$sdir") == 0)
- {
- if (try_thread_db_load_from_sdir ())
- {
- rc = 1;
- break;
- }
- }
- else
- {
- if (try_thread_db_load_from_dir (this_dir, this_dir_len))
- {
- rc = 1;
- break;
- }
- }
- }
-
- if (debug_threads)
- debug_printf ("thread_db_load_search returning %d\n", rc);
- return rc;
-}
-
-#endif /* USE_LIBTHREAD_DB_DIRECTLY */
-
-int
-thread_db_init (void)
-{
- struct process_info *proc = current_process ();
-
- /* FIXME drow/2004-10-16: This is the "overall process ID", which
- GNU/Linux calls tgid, "thread group ID". When we support
- attaching to threads, the original thread may not be the correct
- thread. We would have to get the process ID from /proc for NPTL.
-
- This isn't the only place in gdbserver that assumes that the first
- process in the list is the thread group leader. */
-
- if (thread_db_load_search ())
- {
- /* It's best to avoid td_ta_thr_iter if possible. That walks
- data structures in the inferior's address space that may be
- corrupted, or, if the target is running, the list may change
- while we walk it. In the latter case, it's possible that a
- thread exits just at the exact time that causes GDBserver to
- get stuck in an infinite loop. As the kernel supports clone
- events and /proc/PID/task/ exists, then we already know about
- all threads in the process. When we need info out of
- thread_db on a given thread (e.g., for TLS), we'll use
- find_one_thread then. That uses thread_db entry points that
- do not walk libpthread's thread list, so should be safe, as
- well as more efficient. */
- if (!linux_proc_task_list_dir_exists (pid_of (proc)))
- thread_db_find_new_threads ();
- thread_db_look_up_symbols ();
- return 1;
- }
-
- return 0;
-}
-
-static void
-switch_to_process (struct process_info *proc)
-{
- int pid = pid_of (proc);
-
- current_thread = find_any_thread_of_pid (pid);
-}
-
-/* Disconnect from libthread_db and free resources. */
-
-static void
-disable_thread_event_reporting (struct process_info *proc)
-{
- struct thread_db *thread_db = proc->priv->thread_db;
- if (thread_db)
- {
- td_err_e (*td_ta_clear_event_p) (const td_thragent_t *ta,
- td_thr_events_t *event);
-
-#ifndef USE_LIBTHREAD_DB_DIRECTLY
- td_ta_clear_event_p
- = (td_ta_clear_event_ftype *) dlsym (thread_db->handle,
- "td_ta_clear_event");
-#else
- td_ta_clear_event_p = &td_ta_clear_event;
-#endif
-
- if (td_ta_clear_event_p != NULL)
- {
- struct thread_info *saved_thread = current_thread;
- td_thr_events_t events;
-
- switch_to_process (proc);
-
- /* Set the process wide mask saying we aren't interested
- in any events anymore. */
- td_event_fillset (&events);
- (*td_ta_clear_event_p) (thread_db->thread_agent, &events);
-
- current_thread = saved_thread;
- }
- }
-}
-
-void
-thread_db_detach (struct process_info *proc)
-{
- struct thread_db *thread_db = proc->priv->thread_db;
-
- if (thread_db)
- {
- disable_thread_event_reporting (proc);
- }
-}
-
-/* Disconnect from libthread_db and free resources. */
-
-void
-thread_db_mourn (struct process_info *proc)
-{
- struct thread_db *thread_db = proc->priv->thread_db;
- if (thread_db)
- {
- td_ta_delete_ftype *td_ta_delete_p;
-
-#ifndef USE_LIBTHREAD_DB_DIRECTLY
- td_ta_delete_p = (td_ta_delete_ftype *) dlsym (thread_db->handle, "td_ta_delete");
-#else
- td_ta_delete_p = &td_ta_delete;
-#endif
-
- if (td_ta_delete_p != NULL)
- (*td_ta_delete_p) (thread_db->thread_agent);
-
-#ifndef USE_LIBTHREAD_DB_DIRECTLY
- dlclose (thread_db->handle);
-#endif /* USE_LIBTHREAD_DB_DIRECTLY */
-
- free (thread_db);
- proc->priv->thread_db = NULL;
- }
-}
-
-/* Handle "set libthread-db-search-path" monitor command and return 1.
- For any other command, return 0. */
-
-int
-thread_db_handle_monitor_command (char *mon)
-{
- const char *cmd = "set libthread-db-search-path";
- size_t cmd_len = strlen (cmd);
-
- if (strncmp (mon, cmd, cmd_len) == 0
- && (mon[cmd_len] == '\0'
- || mon[cmd_len] == ' '))
- {
- const char *cp = mon + cmd_len;
-
- if (libthread_db_search_path != NULL)
- free (libthread_db_search_path);
-
- /* Skip leading space (if any). */
- while (isspace (*cp))
- ++cp;
-
- if (*cp == '\0')
- cp = LIBTHREAD_DB_SEARCH_PATH;
- libthread_db_search_path = xstrdup (cp);
-
- monitor_output ("libthread-db-search-path set to `");
- monitor_output (libthread_db_search_path);
- monitor_output ("'\n");
- return 1;
- }
-
- /* Tell server.c to perform default processing. */
- return 0;
-}
-
-/* See linux-low.h. */
-
-void
-thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid)
-{
- process_info *parent_proc = get_thread_process (parent_thr);
- struct thread_db *thread_db = parent_proc->priv->thread_db;
-
- /* If the thread layer isn't initialized, return. It may just
- be that the program uses clone, but does not use libthread_db. */
- if (thread_db == NULL || !thread_db->all_symbols_looked_up)
- return;
-
- /* find_one_thread calls into libthread_db which accesses memory via
- the current thread. Temporarily switch to a thread we know is
- stopped. */
- scoped_restore restore_current_thread
- = make_scoped_restore (¤t_thread, parent_thr);
-
- if (!find_one_thread (child_ptid))
- warning ("Cannot find thread after clone.");
-}
--- /dev/null
+/* Thread management interface, for the remote server for GDB.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+
+#include "linux-low.h"
+
+#include "debug.h"
+#include "gdb_proc_service.h"
+#include "nat/gdb_thread_db.h"
+#include "gdbsupport/gdb_vecs.h"
+#include "nat/linux-procfs.h"
+#include "gdbsupport/scoped_restore.h"
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+#include <dlfcn.h>
+#endif
+#include <limits.h>
+#include <ctype.h>
+
+struct thread_db
+{
+ /* Structure that identifies the child process for the
+ <proc_service.h> interface. */
+ struct ps_prochandle proc_handle;
+
+ /* Connection to the libthread_db library. */
+ td_thragent_t *thread_agent;
+
+ /* If this flag has been set, we've already asked GDB for all
+ symbols we might need; assume symbol cache misses are
+ failures. */
+ int all_symbols_looked_up;
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ /* Handle of the libthread_db from dlopen. */
+ void *handle;
+#endif
+
+ /* Addresses of libthread_db functions. */
+ td_ta_new_ftype *td_ta_new_p;
+ td_ta_map_lwp2thr_ftype *td_ta_map_lwp2thr_p;
+ td_thr_get_info_ftype *td_thr_get_info_p;
+ td_ta_thr_iter_ftype *td_ta_thr_iter_p;
+ td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
+ td_thr_tlsbase_ftype *td_thr_tlsbase_p;
+ td_symbol_list_ftype *td_symbol_list_p;
+};
+
+static char *libthread_db_search_path;
+
+static int find_one_thread (ptid_t);
+static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data);
+
+static const char *
+thread_db_err_str (td_err_e err)
+{
+ static char buf[64];
+
+ switch (err)
+ {
+ case TD_OK:
+ return "generic 'call succeeded'";
+ case TD_ERR:
+ return "generic error";
+ case TD_NOTHR:
+ return "no thread to satisfy query";
+ case TD_NOSV:
+ return "no sync handle to satisfy query";
+ case TD_NOLWP:
+ return "no LWP to satisfy query";
+ case TD_BADPH:
+ return "invalid process handle";
+ case TD_BADTH:
+ return "invalid thread handle";
+ case TD_BADSH:
+ return "invalid synchronization handle";
+ case TD_BADTA:
+ return "invalid thread agent";
+ case TD_BADKEY:
+ return "invalid key";
+ case TD_NOMSG:
+ return "no event message for getmsg";
+ case TD_NOFPREGS:
+ return "FPU register set not available";
+ case TD_NOLIBTHREAD:
+ return "application not linked with libthread";
+ case TD_NOEVENT:
+ return "requested event is not supported";
+ case TD_NOCAPAB:
+ return "capability not available";
+ case TD_DBERR:
+ return "debugger service failed";
+ case TD_NOAPLIC:
+ return "operation not applicable to";
+ case TD_NOTSD:
+ return "no thread-specific data for this thread";
+ case TD_MALLOC:
+ return "malloc failed";
+ case TD_PARTIALREG:
+ return "only part of register set was written/read";
+ case TD_NOXREGS:
+ return "X register set not available for this thread";
+#ifdef HAVE_TD_VERSION
+ case TD_VERSION:
+ return "version mismatch between libthread_db and libpthread";
+#endif
+ default:
+ xsnprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
+ return buf;
+ }
+}
+
+#if 0
+static char *
+thread_db_state_str (td_thr_state_e state)
+{
+ static char buf[64];
+
+ switch (state)
+ {
+ case TD_THR_STOPPED:
+ return "stopped by debugger";
+ case TD_THR_RUN:
+ return "runnable";
+ case TD_THR_ACTIVE:
+ return "active";
+ case TD_THR_ZOMBIE:
+ return "zombie";
+ case TD_THR_SLEEP:
+ return "sleeping";
+ case TD_THR_STOPPED_ASLEEP:
+ return "stopped by debugger AND blocked";
+ default:
+ xsnprintf (buf, sizeof (buf), "unknown thread_db state %d", state);
+ return buf;
+ }
+}
+#endif
+
+/* Get thread info about PTID, accessing memory via the current
+ thread. */
+
+static int
+find_one_thread (ptid_t ptid)
+{
+ td_thrhandle_t th;
+ td_thrinfo_t ti;
+ td_err_e err;
+ struct lwp_info *lwp;
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
+ int lwpid = ptid.lwp ();
+
+ thread_info *thread = find_thread_ptid (ptid);
+ lwp = get_thread_lwp (thread);
+ if (lwp->thread_known)
+ return 1;
+
+ /* Get information about this thread. */
+ err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, &th);
+ if (err != TD_OK)
+ error ("Cannot get thread handle for LWP %d: %s",
+ lwpid, thread_db_err_str (err));
+
+ err = thread_db->td_thr_get_info_p (&th, &ti);
+ if (err != TD_OK)
+ error ("Cannot get thread info for LWP %d: %s",
+ lwpid, thread_db_err_str (err));
+
+ if (debug_threads)
+ debug_printf ("Found thread %ld (LWP %d)\n",
+ (unsigned long) ti.ti_tid, ti.ti_lid);
+
+ if (lwpid != ti.ti_lid)
+ {
+ warning ("PID mismatch! Expected %ld, got %ld",
+ (long) lwpid, (long) ti.ti_lid);
+ return 0;
+ }
+
+ /* If the new thread ID is zero, a final thread ID will be available
+ later. Do not enable thread debugging yet. */
+ if (ti.ti_tid == 0)
+ return 0;
+
+ lwp->thread_known = 1;
+ lwp->th = th;
+ lwp->thread_handle = ti.ti_tid;
+
+ return 1;
+}
+
+/* Attach a thread. Return true on success. */
+
+static int
+attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
+{
+ struct process_info *proc = current_process ();
+ int pid = pid_of (proc);
+ ptid_t ptid = ptid_t (pid, ti_p->ti_lid, 0);
+ struct lwp_info *lwp;
+ int err;
+
+ if (debug_threads)
+ debug_printf ("Attaching to thread %ld (LWP %d)\n",
+ (unsigned long) ti_p->ti_tid, ti_p->ti_lid);
+ err = linux_attach_lwp (ptid);
+ if (err != 0)
+ {
+ std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+
+ warning ("Could not attach to thread %ld (LWP %d): %s",
+ (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
+
+ return 0;
+ }
+
+ lwp = find_lwp_pid (ptid);
+ gdb_assert (lwp != NULL);
+ lwp->thread_known = 1;
+ lwp->th = *th_p;
+ lwp->thread_handle = ti_p->ti_tid;
+
+ return 1;
+}
+
+/* Attach thread if we haven't seen it yet.
+ Increment *COUNTER if we have attached a new thread.
+ Return false on failure. */
+
+static int
+maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p,
+ int *counter)
+{
+ struct lwp_info *lwp;
+
+ lwp = find_lwp_pid (ptid_t (ti_p->ti_lid));
+ if (lwp != NULL)
+ return 1;
+
+ if (!attach_thread (th_p, ti_p))
+ return 0;
+
+ if (counter != NULL)
+ *counter += 1;
+
+ return 1;
+}
+
+static int
+find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
+{
+ td_thrinfo_t ti;
+ td_err_e err;
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
+
+ err = thread_db->td_thr_get_info_p (th_p, &ti);
+ if (err != TD_OK)
+ error ("Cannot get thread info: %s", thread_db_err_str (err));
+
+ if (ti.ti_lid == -1)
+ {
+ /* A thread with kernel thread ID -1 is either a thread that
+ exited and was joined, or a thread that is being created but
+ hasn't started yet, and that is reusing the tcb/stack of a
+ thread that previously exited and was joined. (glibc marks
+ terminated and joined threads with kernel thread ID -1. See
+ glibc PR17707. */
+ if (debug_threads)
+ debug_printf ("thread_db: skipping exited and "
+ "joined thread (0x%lx)\n",
+ (unsigned long) ti.ti_tid);
+ return 0;
+ }
+
+ /* Check for zombies. */
+ if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
+ return 0;
+
+ if (!maybe_attach_thread (th_p, &ti, (int *) data))
+ {
+ /* Terminate iteration early: we might be looking at stale data in
+ the inferior. The thread_db_find_new_threads will retry. */
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+thread_db_find_new_threads (void)
+{
+ td_err_e err;
+ ptid_t ptid = current_ptid;
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
+ int loop, iteration;
+
+ /* This function is only called when we first initialize thread_db.
+ First locate the initial thread. If it is not ready for
+ debugging yet, then stop. */
+ if (find_one_thread (ptid) == 0)
+ return;
+
+ /* Require 4 successive iterations which do not find any new threads.
+ The 4 is a heuristic: there is an inherent race here, and I have
+ seen that 2 iterations in a row are not always sufficient to
+ "capture" all threads. */
+ for (loop = 0, iteration = 0; loop < 4; ++loop, ++iteration)
+ {
+ int new_thread_count = 0;
+
+ /* Iterate over all user-space threads to discover new threads. */
+ err = thread_db->td_ta_thr_iter_p (thread_db->thread_agent,
+ find_new_threads_callback,
+ &new_thread_count,
+ TD_THR_ANY_STATE,
+ TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ if (debug_threads)
+ debug_printf ("Found %d threads in iteration %d.\n",
+ new_thread_count, iteration);
+
+ if (new_thread_count != 0)
+ {
+ /* Found new threads. Restart iteration from beginning. */
+ loop = -1;
+ }
+ }
+ if (err != TD_OK)
+ error ("Cannot find new threads: %s", thread_db_err_str (err));
+}
+
+/* Cache all future symbols that thread_db might request. We can not
+ request symbols at arbitrary states in the remote protocol, only
+ when the client tells us that new symbols are available. So when
+ we load the thread library, make sure to check the entire list. */
+
+static void
+thread_db_look_up_symbols (void)
+{
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
+ const char **sym_list;
+ CORE_ADDR unused;
+
+ for (sym_list = thread_db->td_symbol_list_p (); *sym_list; sym_list++)
+ look_up_one_symbol (*sym_list, &unused, 1);
+
+ /* We're not interested in any other libraries loaded after this
+ point, only in symbols in libpthread.so. */
+ thread_db->all_symbols_looked_up = 1;
+}
+
+int
+thread_db_look_up_one_symbol (const char *name, CORE_ADDR *addrp)
+{
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
+ int may_ask_gdb = !thread_db->all_symbols_looked_up;
+
+ /* If we've passed the call to thread_db_look_up_symbols, then
+ anything not in the cache must not exist; we're not interested
+ in any libraries loaded after that point, only in symbols in
+ libpthread.so. It might not be an appropriate time to look
+ up a symbol, e.g. while we're trying to fetch registers. */
+ return look_up_one_symbol (name, addrp, may_ask_gdb);
+}
+
+int
+thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
+ CORE_ADDR load_module, CORE_ADDR *address)
+{
+ psaddr_t addr;
+ td_err_e err;
+ struct lwp_info *lwp;
+ struct thread_info *saved_thread;
+ struct process_info *proc;
+ struct thread_db *thread_db;
+
+ proc = get_thread_process (thread);
+ thread_db = proc->priv->thread_db;
+
+ /* If the thread layer is not (yet) initialized, fail. */
+ if (thread_db == NULL || !thread_db->all_symbols_looked_up)
+ return TD_ERR;
+
+ /* If td_thr_tls_get_addr is missing rather do not expect td_thr_tlsbase
+ could work. */
+ if (thread_db->td_thr_tls_get_addr_p == NULL
+ || (load_module == 0 && thread_db->td_thr_tlsbase_p == NULL))
+ return -1;
+
+ lwp = get_thread_lwp (thread);
+ if (!lwp->thread_known)
+ find_one_thread (thread->id);
+ if (!lwp->thread_known)
+ return TD_NOTHR;
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ if (load_module != 0)
+ {
+ /* Note the cast through uintptr_t: this interface only works if
+ a target address fits in a psaddr_t, which is a host pointer.
+ So a 32-bit debugger can not access 64-bit TLS through this. */
+ err = thread_db->td_thr_tls_get_addr_p (&lwp->th,
+ (psaddr_t) (uintptr_t) load_module,
+ offset, &addr);
+ }
+ else
+ {
+ /* This code path handles the case of -static -pthread executables:
+ https://sourceware.org/ml/libc-help/2014-03/msg00024.html
+ For older GNU libc r_debug.r_map is NULL. For GNU libc after
+ PR libc/16831 due to GDB PR threads/16954 LOAD_MODULE is also NULL.
+ The constant number 1 depends on GNU __libc_setup_tls
+ initialization of l_tls_modid to 1. */
+ err = thread_db->td_thr_tlsbase_p (&lwp->th, 1, &addr);
+ addr = (char *) addr + offset;
+ }
+
+ current_thread = saved_thread;
+ if (err == TD_OK)
+ {
+ *address = (CORE_ADDR) (uintptr_t) addr;
+ return 0;
+ }
+ else
+ return err;
+}
+
+/* See linux-low.h. */
+
+bool
+thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len)
+{
+ struct thread_db *thread_db;
+ struct lwp_info *lwp;
+ thread_info *thread = find_thread_ptid (ptid);
+
+ if (thread == NULL)
+ return false;
+
+ thread_db = get_thread_process (thread)->priv->thread_db;
+
+ if (thread_db == NULL)
+ return false;
+
+ lwp = get_thread_lwp (thread);
+
+ if (!lwp->thread_known && !find_one_thread (thread->id))
+ return false;
+
+ gdb_assert (lwp->thread_known);
+
+ *handle = (gdb_byte *) &lwp->thread_handle;
+ *handle_len = sizeof (lwp->thread_handle);
+ return true;
+}
+
+#ifdef USE_LIBTHREAD_DB_DIRECTLY
+
+static int
+thread_db_load_search (void)
+{
+ td_err_e err;
+ struct thread_db *tdb;
+ struct process_info *proc = current_process ();
+
+ gdb_assert (proc->priv->thread_db == NULL);
+
+ tdb = XCNEW (struct thread_db);
+ proc->priv->thread_db = tdb;
+
+ tdb->td_ta_new_p = &td_ta_new;
+
+ /* Attempt to open a connection to the thread library. */
+ err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent);
+ if (err != TD_OK)
+ {
+ if (debug_threads)
+ debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err));
+ free (tdb);
+ proc->priv->thread_db = NULL;
+ return 0;
+ }
+
+ tdb->td_ta_map_lwp2thr_p = &td_ta_map_lwp2thr;
+ tdb->td_thr_get_info_p = &td_thr_get_info;
+ tdb->td_ta_thr_iter_p = &td_ta_thr_iter;
+ tdb->td_symbol_list_p = &td_symbol_list;
+
+ /* These are not essential. */
+ tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr;
+ tdb->td_thr_tlsbase_p = &td_thr_tlsbase;
+
+ return 1;
+}
+
+#else
+
+static int
+try_thread_db_load_1 (void *handle)
+{
+ td_err_e err;
+ struct thread_db *tdb;
+ struct process_info *proc = current_process ();
+
+ gdb_assert (proc->priv->thread_db == NULL);
+
+ tdb = XCNEW (struct thread_db);
+ proc->priv->thread_db = tdb;
+
+ tdb->handle = handle;
+
+ /* Initialize pointers to the dynamic library functions we will use.
+ Essential functions first. */
+
+#define CHK(required, a) \
+ do \
+ { \
+ if ((a) == NULL) \
+ { \
+ if (debug_threads) \
+ debug_printf ("dlsym: %s\n", dlerror ()); \
+ if (required) \
+ { \
+ free (tdb); \
+ proc->priv->thread_db = NULL; \
+ return 0; \
+ } \
+ } \
+ } \
+ while (0)
+
+#define TDB_DLSYM(tdb, func) \
+ tdb->func ## _p = (func ## _ftype *) dlsym (tdb->handle, #func)
+
+ CHK (1, TDB_DLSYM (tdb, td_ta_new));
+
+ /* Attempt to open a connection to the thread library. */
+ err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent);
+ if (err != TD_OK)
+ {
+ if (debug_threads)
+ debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err));
+ free (tdb);
+ proc->priv->thread_db = NULL;
+ return 0;
+ }
+
+ CHK (1, TDB_DLSYM (tdb, td_ta_map_lwp2thr));
+ CHK (1, TDB_DLSYM (tdb, td_thr_get_info));
+ CHK (1, TDB_DLSYM (tdb, td_ta_thr_iter));
+ CHK (1, TDB_DLSYM (tdb, td_symbol_list));
+
+ /* These are not essential. */
+ CHK (0, TDB_DLSYM (tdb, td_thr_tls_get_addr));
+ CHK (0, TDB_DLSYM (tdb, td_thr_tlsbase));
+
+#undef CHK
+#undef TDB_DLSYM
+
+ return 1;
+}
+
+#ifdef HAVE_DLADDR
+
+/* Lookup a library in which given symbol resides.
+ Note: this is looking in the GDBSERVER process, not in the inferior.
+ Returns library name, or NULL. */
+
+static const char *
+dladdr_to_soname (const void *addr)
+{
+ Dl_info info;
+
+ if (dladdr (addr, &info) != 0)
+ return info.dli_fname;
+ return NULL;
+}
+
+#endif
+
+static int
+try_thread_db_load (const char *library)
+{
+ void *handle;
+
+ if (debug_threads)
+ debug_printf ("Trying host libthread_db library: %s.\n",
+ library);
+ handle = dlopen (library, RTLD_NOW);
+ if (handle == NULL)
+ {
+ if (debug_threads)
+ debug_printf ("dlopen failed: %s.\n", dlerror ());
+ return 0;
+ }
+
+#ifdef HAVE_DLADDR
+ if (debug_threads && strchr (library, '/') == NULL)
+ {
+ void *td_init;
+
+ td_init = dlsym (handle, "td_init");
+ if (td_init != NULL)
+ {
+ const char *const libpath = dladdr_to_soname (td_init);
+
+ if (libpath != NULL)
+ debug_printf ("Host %s resolved to: %s.\n", library, libpath);
+ }
+ }
+#endif
+
+ if (try_thread_db_load_1 (handle))
+ return 1;
+
+ /* This library "refused" to work on current inferior. */
+ dlclose (handle);
+ return 0;
+}
+
+/* Handle $sdir in libthread-db-search-path.
+ Look for libthread_db in the system dirs, or wherever a plain
+ dlopen(file_without_path) will look.
+ The result is true for success. */
+
+static int
+try_thread_db_load_from_sdir (void)
+{
+ return try_thread_db_load (LIBTHREAD_DB_SO);
+}
+
+/* Try to load libthread_db from directory DIR of length DIR_LEN.
+ The result is true for success. */
+
+static int
+try_thread_db_load_from_dir (const char *dir, size_t dir_len)
+{
+ char path[PATH_MAX];
+
+ if (dir_len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path))
+ {
+ char *cp = (char *) xmalloc (dir_len + 1);
+
+ memcpy (cp, dir, dir_len);
+ cp[dir_len] = '\0';
+ warning (_("libthread-db-search-path component too long,"
+ " ignored: %s."), cp);
+ free (cp);
+ return 0;
+ }
+
+ memcpy (path, dir, dir_len);
+ path[dir_len] = '/';
+ strcpy (path + dir_len + 1, LIBTHREAD_DB_SO);
+ return try_thread_db_load (path);
+}
+
+/* Search libthread_db_search_path for libthread_db which "agrees"
+ to work on current inferior.
+ The result is true for success. */
+
+static int
+thread_db_load_search (void)
+{
+ int rc = 0;
+
+ if (libthread_db_search_path == NULL)
+ libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH);
+
+ std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec
+ = dirnames_to_char_ptr_vec (libthread_db_search_path);
+
+ for (const gdb::unique_xmalloc_ptr<char> &this_dir_up : dir_vec)
+ {
+ char *this_dir = this_dir_up.get ();
+ const int pdir_len = sizeof ("$pdir") - 1;
+ size_t this_dir_len;
+
+ this_dir_len = strlen (this_dir);
+
+ if (strncmp (this_dir, "$pdir", pdir_len) == 0
+ && (this_dir[pdir_len] == '\0'
+ || this_dir[pdir_len] == '/'))
+ {
+ /* We don't maintain a list of loaded libraries so we don't know
+ where libpthread lives. We *could* fetch the info, but we don't
+ do that yet. Ignore it. */
+ }
+ else if (strcmp (this_dir, "$sdir") == 0)
+ {
+ if (try_thread_db_load_from_sdir ())
+ {
+ rc = 1;
+ break;
+ }
+ }
+ else
+ {
+ if (try_thread_db_load_from_dir (this_dir, this_dir_len))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ }
+
+ if (debug_threads)
+ debug_printf ("thread_db_load_search returning %d\n", rc);
+ return rc;
+}
+
+#endif /* USE_LIBTHREAD_DB_DIRECTLY */
+
+int
+thread_db_init (void)
+{
+ struct process_info *proc = current_process ();
+
+ /* FIXME drow/2004-10-16: This is the "overall process ID", which
+ GNU/Linux calls tgid, "thread group ID". When we support
+ attaching to threads, the original thread may not be the correct
+ thread. We would have to get the process ID from /proc for NPTL.
+
+ This isn't the only place in gdbserver that assumes that the first
+ process in the list is the thread group leader. */
+
+ if (thread_db_load_search ())
+ {
+ /* It's best to avoid td_ta_thr_iter if possible. That walks
+ data structures in the inferior's address space that may be
+ corrupted, or, if the target is running, the list may change
+ while we walk it. In the latter case, it's possible that a
+ thread exits just at the exact time that causes GDBserver to
+ get stuck in an infinite loop. As the kernel supports clone
+ events and /proc/PID/task/ exists, then we already know about
+ all threads in the process. When we need info out of
+ thread_db on a given thread (e.g., for TLS), we'll use
+ find_one_thread then. That uses thread_db entry points that
+ do not walk libpthread's thread list, so should be safe, as
+ well as more efficient. */
+ if (!linux_proc_task_list_dir_exists (pid_of (proc)))
+ thread_db_find_new_threads ();
+ thread_db_look_up_symbols ();
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+switch_to_process (struct process_info *proc)
+{
+ int pid = pid_of (proc);
+
+ current_thread = find_any_thread_of_pid (pid);
+}
+
+/* Disconnect from libthread_db and free resources. */
+
+static void
+disable_thread_event_reporting (struct process_info *proc)
+{
+ struct thread_db *thread_db = proc->priv->thread_db;
+ if (thread_db)
+ {
+ td_err_e (*td_ta_clear_event_p) (const td_thragent_t *ta,
+ td_thr_events_t *event);
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ td_ta_clear_event_p
+ = (td_ta_clear_event_ftype *) dlsym (thread_db->handle,
+ "td_ta_clear_event");
+#else
+ td_ta_clear_event_p = &td_ta_clear_event;
+#endif
+
+ if (td_ta_clear_event_p != NULL)
+ {
+ struct thread_info *saved_thread = current_thread;
+ td_thr_events_t events;
+
+ switch_to_process (proc);
+
+ /* Set the process wide mask saying we aren't interested
+ in any events anymore. */
+ td_event_fillset (&events);
+ (*td_ta_clear_event_p) (thread_db->thread_agent, &events);
+
+ current_thread = saved_thread;
+ }
+ }
+}
+
+void
+thread_db_detach (struct process_info *proc)
+{
+ struct thread_db *thread_db = proc->priv->thread_db;
+
+ if (thread_db)
+ {
+ disable_thread_event_reporting (proc);
+ }
+}
+
+/* Disconnect from libthread_db and free resources. */
+
+void
+thread_db_mourn (struct process_info *proc)
+{
+ struct thread_db *thread_db = proc->priv->thread_db;
+ if (thread_db)
+ {
+ td_ta_delete_ftype *td_ta_delete_p;
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ td_ta_delete_p = (td_ta_delete_ftype *) dlsym (thread_db->handle, "td_ta_delete");
+#else
+ td_ta_delete_p = &td_ta_delete;
+#endif
+
+ if (td_ta_delete_p != NULL)
+ (*td_ta_delete_p) (thread_db->thread_agent);
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ dlclose (thread_db->handle);
+#endif /* USE_LIBTHREAD_DB_DIRECTLY */
+
+ free (thread_db);
+ proc->priv->thread_db = NULL;
+ }
+}
+
+/* Handle "set libthread-db-search-path" monitor command and return 1.
+ For any other command, return 0. */
+
+int
+thread_db_handle_monitor_command (char *mon)
+{
+ const char *cmd = "set libthread-db-search-path";
+ size_t cmd_len = strlen (cmd);
+
+ if (strncmp (mon, cmd, cmd_len) == 0
+ && (mon[cmd_len] == '\0'
+ || mon[cmd_len] == ' '))
+ {
+ const char *cp = mon + cmd_len;
+
+ if (libthread_db_search_path != NULL)
+ free (libthread_db_search_path);
+
+ /* Skip leading space (if any). */
+ while (isspace (*cp))
+ ++cp;
+
+ if (*cp == '\0')
+ cp = LIBTHREAD_DB_SEARCH_PATH;
+ libthread_db_search_path = xstrdup (cp);
+
+ monitor_output ("libthread-db-search-path set to `");
+ monitor_output (libthread_db_search_path);
+ monitor_output ("'\n");
+ return 1;
+ }
+
+ /* Tell server.c to perform default processing. */
+ return 0;
+}
+
+/* See linux-low.h. */
+
+void
+thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid)
+{
+ process_info *parent_proc = get_thread_process (parent_thr);
+ struct thread_db *thread_db = parent_proc->priv->thread_db;
+
+ /* If the thread layer isn't initialized, return. It may just
+ be that the program uses clone, but does not use libthread_db. */
+ if (thread_db == NULL || !thread_db->all_symbols_looked_up)
+ return;
+
+ /* find_one_thread calls into libthread_db which accesses memory via
+ the current thread. Temporarily switch to a thread we know is
+ stopped. */
+ scoped_restore restore_current_thread
+ = make_scoped_restore (¤t_thread, parent_thr);
+
+ if (!find_one_thread (child_ptid))
+ warning ("Cannot find thread after clone.");
+}
+++ /dev/null
-/* Tracepoint code for remote server for GDB.
- Copyright (C) 2009-2020 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 "tracepoint.h"
-#include "gdbthread.h"
-#include "gdbsupport/rsp-low.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <chrono>
-#include <inttypes.h>
-#include "ax.h"
-#include "tdesc.h"
-
-#define IPA_SYM_STRUCT_NAME ipa_sym_addresses
-#include "gdbsupport/agent.h"
-
-#define DEFAULT_TRACE_BUFFER_SIZE 5242880 /* 5*1024*1024 */
-
-/* This file is built for both GDBserver, and the in-process
- agent (IPA), a shared library that includes a tracing agent that is
- loaded by the inferior to support fast tracepoints. Fast
- tracepoints (or more accurately, jump based tracepoints) are
- implemented by patching the tracepoint location with a jump into a
- small trampoline function whose job is to save the register state,
- call the in-process tracing agent, and then execute the original
- instruction that was under the tracepoint jump (possibly adjusted,
- if PC-relative, or some such).
-
- The current synchronization design is pull based. That means,
- GDBserver does most of the work, by peeking/poking at the inferior
- agent's memory directly for downloading tracepoint and associated
- objects, and for uploading trace frames. Whenever the IPA needs
- something from GDBserver (trace buffer is full, tracing stopped for
- some reason, etc.) the IPA calls a corresponding hook function
- where GDBserver has placed a breakpoint.
-
- Each of the agents has its own trace buffer. When browsing the
- trace frames built from slow and fast tracepoints from GDB (tfind
- mode), there's no guarantee the user is seeing the trace frames in
- strict chronological creation order, although, GDBserver tries to
- keep the order relatively reasonable, by syncing the trace buffers
- at appropriate times.
-
-*/
-
-#ifdef IN_PROCESS_AGENT
-
-static void trace_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
-
-static void
-trace_vdebug (const char *fmt, ...)
-{
- char buf[1024];
- va_list ap;
-
- va_start (ap, fmt);
- vsprintf (buf, fmt, ap);
- fprintf (stderr, PROG "/tracepoint: %s\n", buf);
- va_end (ap);
-}
-
-#define trace_debug_1(level, fmt, args...) \
- do { \
- if (level <= debug_threads) \
- trace_vdebug ((fmt), ##args); \
- } while (0)
-
-#else
-
-#define trace_debug_1(level, fmt, args...) \
- do { \
- if (level <= debug_threads) \
- { \
- debug_printf ((fmt), ##args); \
- debug_printf ("\n"); \
- } \
- } while (0)
-
-#endif
-
-#define trace_debug(FMT, args...) \
- trace_debug_1 (1, FMT, ##args)
-
-/* Prefix exported symbols, for good citizenship. All the symbols
- that need exporting are defined in this module. Note that all
- these symbols must be tagged with IP_AGENT_EXPORT_*. */
-#ifdef IN_PROCESS_AGENT
-# define gdb_tp_heap_buffer IPA_SYM_EXPORTED_NAME (gdb_tp_heap_buffer)
-# define gdb_jump_pad_buffer IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer)
-# define gdb_jump_pad_buffer_end IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer_end)
-# define gdb_trampoline_buffer IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer)
-# define gdb_trampoline_buffer_end IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_end)
-# define gdb_trampoline_buffer_error IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_error)
-# define collecting IPA_SYM_EXPORTED_NAME (collecting)
-# define gdb_collect_ptr IPA_SYM_EXPORTED_NAME (gdb_collect_ptr)
-# define stop_tracing IPA_SYM_EXPORTED_NAME (stop_tracing)
-# define flush_trace_buffer IPA_SYM_EXPORTED_NAME (flush_trace_buffer)
-# define about_to_request_buffer_space IPA_SYM_EXPORTED_NAME (about_to_request_buffer_space)
-# define trace_buffer_is_full IPA_SYM_EXPORTED_NAME (trace_buffer_is_full)
-# define stopping_tracepoint IPA_SYM_EXPORTED_NAME (stopping_tracepoint)
-# define expr_eval_result IPA_SYM_EXPORTED_NAME (expr_eval_result)
-# define error_tracepoint IPA_SYM_EXPORTED_NAME (error_tracepoint)
-# define tracepoints IPA_SYM_EXPORTED_NAME (tracepoints)
-# define tracing IPA_SYM_EXPORTED_NAME (tracing)
-# define trace_buffer_ctrl IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl)
-# define trace_buffer_ctrl_curr IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl_curr)
-# define trace_buffer_lo IPA_SYM_EXPORTED_NAME (trace_buffer_lo)
-# define trace_buffer_hi IPA_SYM_EXPORTED_NAME (trace_buffer_hi)
-# define traceframe_read_count IPA_SYM_EXPORTED_NAME (traceframe_read_count)
-# define traceframe_write_count IPA_SYM_EXPORTED_NAME (traceframe_write_count)
-# define traceframes_created IPA_SYM_EXPORTED_NAME (traceframes_created)
-# define trace_state_variables IPA_SYM_EXPORTED_NAME (trace_state_variables)
-# define get_raw_reg_ptr IPA_SYM_EXPORTED_NAME (get_raw_reg_ptr)
-# define get_trace_state_variable_value_ptr \
- IPA_SYM_EXPORTED_NAME (get_trace_state_variable_value_ptr)
-# define set_trace_state_variable_value_ptr \
- IPA_SYM_EXPORTED_NAME (set_trace_state_variable_value_ptr)
-# define ust_loaded IPA_SYM_EXPORTED_NAME (ust_loaded)
-# define helper_thread_id IPA_SYM_EXPORTED_NAME (helper_thread_id)
-# define cmd_buf IPA_SYM_EXPORTED_NAME (cmd_buf)
-# define ipa_tdesc_idx IPA_SYM_EXPORTED_NAME (ipa_tdesc_idx)
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-/* Addresses of in-process agent's symbols GDBserver cares about. */
-
-struct ipa_sym_addresses
-{
- CORE_ADDR addr_gdb_tp_heap_buffer;
- CORE_ADDR addr_gdb_jump_pad_buffer;
- CORE_ADDR addr_gdb_jump_pad_buffer_end;
- CORE_ADDR addr_gdb_trampoline_buffer;
- CORE_ADDR addr_gdb_trampoline_buffer_end;
- CORE_ADDR addr_gdb_trampoline_buffer_error;
- CORE_ADDR addr_collecting;
- CORE_ADDR addr_gdb_collect_ptr;
- CORE_ADDR addr_stop_tracing;
- CORE_ADDR addr_flush_trace_buffer;
- CORE_ADDR addr_about_to_request_buffer_space;
- CORE_ADDR addr_trace_buffer_is_full;
- CORE_ADDR addr_stopping_tracepoint;
- CORE_ADDR addr_expr_eval_result;
- CORE_ADDR addr_error_tracepoint;
- CORE_ADDR addr_tracepoints;
- CORE_ADDR addr_tracing;
- CORE_ADDR addr_trace_buffer_ctrl;
- CORE_ADDR addr_trace_buffer_ctrl_curr;
- CORE_ADDR addr_trace_buffer_lo;
- CORE_ADDR addr_trace_buffer_hi;
- CORE_ADDR addr_traceframe_read_count;
- CORE_ADDR addr_traceframe_write_count;
- CORE_ADDR addr_traceframes_created;
- CORE_ADDR addr_trace_state_variables;
- CORE_ADDR addr_get_raw_reg_ptr;
- CORE_ADDR addr_get_trace_state_variable_value_ptr;
- CORE_ADDR addr_set_trace_state_variable_value_ptr;
- CORE_ADDR addr_ust_loaded;
- CORE_ADDR addr_ipa_tdesc_idx;
-};
-
-static struct
-{
- const char *name;
- int offset;
-} symbol_list[] = {
- IPA_SYM(gdb_tp_heap_buffer),
- IPA_SYM(gdb_jump_pad_buffer),
- IPA_SYM(gdb_jump_pad_buffer_end),
- IPA_SYM(gdb_trampoline_buffer),
- IPA_SYM(gdb_trampoline_buffer_end),
- IPA_SYM(gdb_trampoline_buffer_error),
- IPA_SYM(collecting),
- IPA_SYM(gdb_collect_ptr),
- IPA_SYM(stop_tracing),
- IPA_SYM(flush_trace_buffer),
- IPA_SYM(about_to_request_buffer_space),
- IPA_SYM(trace_buffer_is_full),
- IPA_SYM(stopping_tracepoint),
- IPA_SYM(expr_eval_result),
- IPA_SYM(error_tracepoint),
- IPA_SYM(tracepoints),
- IPA_SYM(tracing),
- IPA_SYM(trace_buffer_ctrl),
- IPA_SYM(trace_buffer_ctrl_curr),
- IPA_SYM(trace_buffer_lo),
- IPA_SYM(trace_buffer_hi),
- IPA_SYM(traceframe_read_count),
- IPA_SYM(traceframe_write_count),
- IPA_SYM(traceframes_created),
- IPA_SYM(trace_state_variables),
- IPA_SYM(get_raw_reg_ptr),
- IPA_SYM(get_trace_state_variable_value_ptr),
- IPA_SYM(set_trace_state_variable_value_ptr),
- IPA_SYM(ust_loaded),
- IPA_SYM(ipa_tdesc_idx),
-};
-
-static struct ipa_sym_addresses ipa_sym_addrs;
-
-static int read_inferior_integer (CORE_ADDR symaddr, int *val);
-
-/* Returns true if both the in-process agent library and the static
- tracepoints libraries are loaded in the inferior, and agent has
- capability on static tracepoints. */
-
-static int
-in_process_agent_supports_ust (void)
-{
- int loaded = 0;
-
- if (!agent_loaded_p ())
- {
- warning ("In-process agent not loaded");
- return 0;
- }
-
- if (agent_capability_check (AGENT_CAPA_STATIC_TRACE))
- {
- /* Agent understands static tracepoint, then check whether UST is in
- fact loaded in the inferior. */
- if (read_inferior_integer (ipa_sym_addrs.addr_ust_loaded, &loaded))
- {
- warning ("Error reading ust_loaded in lib");
- return 0;
- }
-
- return loaded;
- }
- else
- return 0;
-}
-
-static void
-write_e_ipa_not_loaded (char *buffer)
-{
- sprintf (buffer,
- "E.In-process agent library not loaded in process. "
- "Fast and static tracepoints unavailable.");
-}
-
-/* Write an error to BUFFER indicating that UST isn't loaded in the
- inferior. */
-
-static void
-write_e_ust_not_loaded (char *buffer)
-{
-#ifdef HAVE_UST
- sprintf (buffer,
- "E.UST library not loaded in process. "
- "Static tracepoints unavailable.");
-#else
- sprintf (buffer, "E.GDBserver was built without static tracepoints support");
-#endif
-}
-
-/* If the in-process agent library isn't loaded in the inferior, write
- an error to BUFFER, and return 1. Otherwise, return 0. */
-
-static int
-maybe_write_ipa_not_loaded (char *buffer)
-{
- if (!agent_loaded_p ())
- {
- write_e_ipa_not_loaded (buffer);
- return 1;
- }
- return 0;
-}
-
-/* If the in-process agent library and the ust (static tracepoints)
- library aren't loaded in the inferior, write an error to BUFFER,
- and return 1. Otherwise, return 0. */
-
-static int
-maybe_write_ipa_ust_not_loaded (char *buffer)
-{
- if (!agent_loaded_p ())
- {
- write_e_ipa_not_loaded (buffer);
- return 1;
- }
- else if (!in_process_agent_supports_ust ())
- {
- write_e_ust_not_loaded (buffer);
- return 1;
- }
- return 0;
-}
-
-/* Cache all future symbols that the tracepoints module might request.
- We can not request symbols at arbitrary states in the remote
- protocol, only when the client tells us that new symbols are
- available. So when we load the in-process library, make sure to
- check the entire list. */
-
-void
-tracepoint_look_up_symbols (void)
-{
- int i;
-
- if (agent_loaded_p ())
- return;
-
- for (i = 0; i < sizeof (symbol_list) / sizeof (symbol_list[0]); i++)
- {
- CORE_ADDR *addrp =
- (CORE_ADDR *) ((char *) &ipa_sym_addrs + symbol_list[i].offset);
-
- if (look_up_one_symbol (symbol_list[i].name, addrp, 1) == 0)
- {
- if (debug_threads)
- debug_printf ("symbol `%s' not found\n", symbol_list[i].name);
- return;
- }
- }
-
- agent_look_up_symbols (NULL);
-}
-
-#endif
-
-/* GDBserver places a breakpoint on the IPA's version (which is a nop)
- of the "stop_tracing" function. When this breakpoint is hit,
- tracing stopped in the IPA for some reason. E.g., due to
- tracepoint reaching the pass count, hitting conditional expression
- evaluation error, etc.
-
- The IPA's trace buffer is never in circular tracing mode: instead,
- GDBserver's is, and whenever the in-process buffer fills, it calls
- "flush_trace_buffer", which triggers an internal breakpoint.
- GDBserver reacts to this breakpoint by pulling the meanwhile
- collected data. Old frames discarding is always handled on the
- GDBserver side. */
-
-#ifdef IN_PROCESS_AGENT
-int
-read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- memcpy (myaddr, (void *) (uintptr_t) memaddr, len);
- return 0;
-}
-
-/* Call this in the functions where GDBserver places a breakpoint, so
- that the compiler doesn't try to be clever and skip calling the
- function at all. This is necessary, even if we tell the compiler
- to not inline said functions. */
-
-#if defined(__GNUC__)
-# define UNKNOWN_SIDE_EFFECTS() asm ("")
-#else
-# define UNKNOWN_SIDE_EFFECTS() do {} while (0)
-#endif
-
-/* This is needed for -Wmissing-declarations. */
-IP_AGENT_EXPORT_FUNC void stop_tracing (void);
-
-IP_AGENT_EXPORT_FUNC void
-stop_tracing (void)
-{
- /* GDBserver places breakpoint here. */
- UNKNOWN_SIDE_EFFECTS();
-}
-
-/* This is needed for -Wmissing-declarations. */
-IP_AGENT_EXPORT_FUNC void flush_trace_buffer (void);
-
-IP_AGENT_EXPORT_FUNC void
-flush_trace_buffer (void)
-{
- /* GDBserver places breakpoint here. */
- UNKNOWN_SIDE_EFFECTS();
-}
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-static int
-tracepoint_handler (CORE_ADDR address)
-{
- trace_debug ("tracepoint_handler: tracepoint at 0x%s hit",
- paddress (address));
- return 0;
-}
-
-/* Breakpoint at "stop_tracing" in the inferior lib. */
-struct breakpoint *stop_tracing_bkpt;
-static int stop_tracing_handler (CORE_ADDR);
-
-/* Breakpoint at "flush_trace_buffer" in the inferior lib. */
-struct breakpoint *flush_trace_buffer_bkpt;
-static int flush_trace_buffer_handler (CORE_ADDR);
-
-static void download_trace_state_variables (void);
-static void upload_fast_traceframes (void);
-
-static int run_inferior_command (char *cmd, int len);
-
-static int
-read_inferior_integer (CORE_ADDR symaddr, int *val)
-{
- return read_inferior_memory (symaddr, (unsigned char *) val,
- sizeof (*val));
-}
-
-struct tracepoint;
-static int tracepoint_send_agent (struct tracepoint *tpoint);
-
-static int
-read_inferior_uinteger (CORE_ADDR symaddr, unsigned int *val)
-{
- return read_inferior_memory (symaddr, (unsigned char *) val,
- sizeof (*val));
-}
-
-static int
-read_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR *val)
-{
- void *pval = (void *) (uintptr_t) val;
- int ret;
-
- ret = read_inferior_memory (symaddr, (unsigned char *) &pval, sizeof (pval));
- *val = (uintptr_t) pval;
- return ret;
-}
-
-static int
-write_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR val)
-{
- void *pval = (void *) (uintptr_t) val;
- return target_write_memory (symaddr,
- (unsigned char *) &pval, sizeof (pval));
-}
-
-static int
-write_inferior_integer (CORE_ADDR symaddr, int val)
-{
- return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
-}
-
-static int
-write_inferior_int8 (CORE_ADDR symaddr, int8_t val)
-{
- return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
-}
-
-static int
-write_inferior_uinteger (CORE_ADDR symaddr, unsigned int val)
-{
- return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
-}
-
-static CORE_ADDR target_malloc (ULONGEST size);
-
-#define COPY_FIELD_TO_BUF(BUF, OBJ, FIELD) \
- do { \
- memcpy (BUF, &(OBJ)->FIELD, sizeof ((OBJ)->FIELD)); \
- BUF += sizeof ((OBJ)->FIELD); \
- } while (0)
-
-#endif
-
-/* Base action. Concrete actions inherit this. */
-
-struct tracepoint_action
-{
- char type;
-};
-
-/* An 'M' (collect memory) action. */
-struct collect_memory_action
-{
- struct tracepoint_action base;
-
- ULONGEST addr;
- ULONGEST len;
- int32_t basereg;
-};
-
-/* An 'R' (collect registers) action. */
-
-struct collect_registers_action
-{
- struct tracepoint_action base;
-};
-
-/* An 'X' (evaluate expression) action. */
-
-struct eval_expr_action
-{
- struct tracepoint_action base;
-
- struct agent_expr *expr;
-};
-
-/* An 'L' (collect static trace data) action. */
-struct collect_static_trace_data_action
-{
- struct tracepoint_action base;
-};
-
-#ifndef IN_PROCESS_AGENT
-static CORE_ADDR
-m_tracepoint_action_download (const struct tracepoint_action *action)
-{
- CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_memory_action));
-
- target_write_memory (ipa_action, (unsigned char *) action,
- sizeof (struct collect_memory_action));
-
- return ipa_action;
-}
-static char *
-m_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
-{
- struct collect_memory_action *maction
- = (struct collect_memory_action *) action;
-
- COPY_FIELD_TO_BUF (buffer, maction, addr);
- COPY_FIELD_TO_BUF (buffer, maction, len);
- COPY_FIELD_TO_BUF (buffer, maction, basereg);
-
- return buffer;
-}
-
-static CORE_ADDR
-r_tracepoint_action_download (const struct tracepoint_action *action)
-{
- CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_registers_action));
-
- target_write_memory (ipa_action, (unsigned char *) action,
- sizeof (struct collect_registers_action));
-
- return ipa_action;
-}
-
-static char *
-r_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
-{
- return buffer;
-}
-
-static CORE_ADDR download_agent_expr (struct agent_expr *expr);
-
-static CORE_ADDR
-x_tracepoint_action_download (const struct tracepoint_action *action)
-{
- CORE_ADDR ipa_action = target_malloc (sizeof (struct eval_expr_action));
- CORE_ADDR expr;
-
- target_write_memory (ipa_action, (unsigned char *) action,
- sizeof (struct eval_expr_action));
- expr = download_agent_expr (((struct eval_expr_action *) action)->expr);
- write_inferior_data_pointer (ipa_action
- + offsetof (struct eval_expr_action, expr),
- expr);
-
- return ipa_action;
-}
-
-/* Copy agent expression AEXPR to buffer pointed by P. If AEXPR is NULL,
- copy 0 to P. Return updated header of buffer. */
-
-static char *
-agent_expr_send (char *p, const struct agent_expr *aexpr)
-{
- /* Copy the length of condition first, and then copy its
- content. */
- if (aexpr == NULL)
- {
- memset (p, 0, 4);
- p += 4;
- }
- else
- {
- memcpy (p, &aexpr->length, 4);
- p +=4;
-
- memcpy (p, aexpr->bytes, aexpr->length);
- p += aexpr->length;
- }
- return p;
-}
-
-static char *
-x_tracepoint_action_send ( char *buffer, const struct tracepoint_action *action)
-{
- struct eval_expr_action *eaction = (struct eval_expr_action *) action;
-
- return agent_expr_send (buffer, eaction->expr);
-}
-
-static CORE_ADDR
-l_tracepoint_action_download (const struct tracepoint_action *action)
-{
- CORE_ADDR ipa_action
- = target_malloc (sizeof (struct collect_static_trace_data_action));
-
- target_write_memory (ipa_action, (unsigned char *) action,
- sizeof (struct collect_static_trace_data_action));
-
- return ipa_action;
-}
-
-static char *
-l_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
-{
- return buffer;
-}
-
-static char *
-tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
-{
- switch (action->type)
- {
- case 'M':
- return m_tracepoint_action_send (buffer, action);
- case 'R':
- return r_tracepoint_action_send (buffer, action);
- case 'X':
- return x_tracepoint_action_send (buffer, action);
- case 'L':
- return l_tracepoint_action_send (buffer, action);
- }
- error ("Unknown trace action '%c'.", action->type);
-}
-
-static CORE_ADDR
-tracepoint_action_download (const struct tracepoint_action *action)
-{
- switch (action->type)
- {
- case 'M':
- return m_tracepoint_action_download (action);
- case 'R':
- return r_tracepoint_action_download (action);
- case 'X':
- return x_tracepoint_action_download (action);
- case 'L':
- return l_tracepoint_action_download (action);
- }
- error ("Unknown trace action '%c'.", action->type);
-}
-#endif
-
-/* This structure describes a piece of the source-level definition of
- the tracepoint. The contents are not interpreted by the target,
- but preserved verbatim for uploading upon reconnection. */
-
-struct source_string
-{
- /* The type of string, such as "cond" for a conditional. */
- char *type;
-
- /* The source-level string itself. For the sake of target
- debugging, we store it in plaintext, even though it is always
- transmitted in hex. */
- char *str;
-
- /* Link to the next one in the list. We link them in the order
- received, in case some make up an ordered list of commands or
- some such. */
- struct source_string *next;
-};
-
-enum tracepoint_type
-{
- /* Trap based tracepoint. */
- trap_tracepoint,
-
- /* A fast tracepoint implemented with a jump instead of a trap. */
- fast_tracepoint,
-
- /* A static tracepoint, implemented by a program call into a tracing
- library. */
- static_tracepoint
-};
-
-struct tracepoint_hit_ctx;
-
-typedef enum eval_result_type (*condfn) (unsigned char *,
- ULONGEST *);
-
-/* The definition of a tracepoint. */
-
-/* Tracepoints may have multiple locations, each at a different
- address. This can occur with optimizations, template
- instantiation, etc. Since the locations may be in different
- scopes, the conditions and actions may be different for each
- location. Our target version of tracepoints is more like GDB's
- notion of "breakpoint locations", but we have almost nothing that
- is not per-location, so we bother having two kinds of objects. The
- key consequence is that numbers are not unique, and that it takes
- both number and address to identify a tracepoint uniquely. */
-
-struct tracepoint
-{
- /* The number of the tracepoint, as specified by GDB. Several
- tracepoint objects here may share a number. */
- uint32_t number;
-
- /* Address at which the tracepoint is supposed to trigger. Several
- tracepoints may share an address. */
- CORE_ADDR address;
-
- /* Tracepoint type. */
- enum tracepoint_type type;
-
- /* True if the tracepoint is currently enabled. */
- int8_t enabled;
-
- /* The number of single steps that will be performed after each
- tracepoint hit. */
- uint64_t step_count;
-
- /* The number of times the tracepoint may be hit before it will
- terminate the entire tracing run. */
- uint64_t pass_count;
-
- /* Pointer to the agent expression that is the tracepoint's
- conditional, or NULL if the tracepoint is unconditional. */
- struct agent_expr *cond;
-
- /* The list of actions to take when the tracepoint triggers. */
- uint32_t numactions;
- struct tracepoint_action **actions;
-
- /* Count of the times we've hit this tracepoint during the run.
- Note that while-stepping steps are not counted as "hits". */
- uint64_t hit_count;
-
- /* Cached sum of the sizes of traceframes created by this point. */
- uint64_t traceframe_usage;
-
- CORE_ADDR compiled_cond;
-
- /* Link to the next tracepoint in the list. */
- struct tracepoint *next;
-
-#ifndef IN_PROCESS_AGENT
- /* The list of actions to take when the tracepoint triggers, in
- string/packet form. */
- char **actions_str;
-
- /* The collection of strings that describe the tracepoint as it was
- entered into GDB. These are not used by the target, but are
- reported back to GDB upon reconnection. */
- struct source_string *source_strings;
-
- /* The number of bytes displaced by fast tracepoints. It may subsume
- multiple instructions, for multi-byte fast tracepoints. This
- field is only valid for fast tracepoints. */
- uint32_t orig_size;
-
- /* Only for fast tracepoints. */
- CORE_ADDR obj_addr_on_target;
-
- /* Address range where the original instruction under a fast
- tracepoint was relocated to. (_end is actually one byte past
- the end). */
- CORE_ADDR adjusted_insn_addr;
- CORE_ADDR adjusted_insn_addr_end;
-
- /* The address range of the piece of the jump pad buffer that was
- assigned to this fast tracepoint. (_end is actually one byte
- past the end).*/
- CORE_ADDR jump_pad;
- CORE_ADDR jump_pad_end;
-
- /* The address range of the piece of the trampoline buffer that was
- assigned to this fast tracepoint. (_end is actually one byte
- past the end). */
- CORE_ADDR trampoline;
- CORE_ADDR trampoline_end;
-
- /* The list of actions to take while in a stepping loop. These
- fields are only valid for patch-based tracepoints. */
- int num_step_actions;
- struct tracepoint_action **step_actions;
- /* Same, but in string/packet form. */
- char **step_actions_str;
-
- /* Handle returned by the breakpoint or tracepoint module when we
- inserted the trap or jump, or hooked into a static tracepoint.
- NULL if we haven't inserted it yet. */
- void *handle;
-#endif
-
-};
-
-#ifndef IN_PROCESS_AGENT
-
-/* Given `while-stepping', a thread may be collecting data for more
- than one tracepoint simultaneously. On the other hand, the same
- tracepoint with a while-stepping action may be hit by more than one
- thread simultaneously (but not quite, each thread could be handling
- a different step). Each thread holds a list of these objects,
- representing the current step of each while-stepping action being
- collected. */
-
-struct wstep_state
-{
- struct wstep_state *next;
-
- /* The tracepoint number. */
- int tp_number;
- /* The tracepoint's address. */
- CORE_ADDR tp_address;
-
- /* The number of the current step in this 'while-stepping'
- action. */
- long current_step;
-};
-
-#endif
-
-EXTERN_C_PUSH
-
-/* The linked list of all tracepoints. Marked explicitly as used as
- the in-process library doesn't use it for the fast tracepoints
- support. */
-IP_AGENT_EXPORT_VAR struct tracepoint *tracepoints;
-
-/* The first tracepoint to exceed its pass count. */
-
-IP_AGENT_EXPORT_VAR struct tracepoint *stopping_tracepoint;
-
-/* True if the trace buffer is full or otherwise no longer usable. */
-
-IP_AGENT_EXPORT_VAR int trace_buffer_is_full;
-
-/* The first error that occurred during expression evaluation. */
-
-/* Stored as an int to avoid the IPA ABI being dependent on whatever
- the compiler decides to use for the enum's underlying type. Holds
- enum eval_result_type values. */
-IP_AGENT_EXPORT_VAR int expr_eval_result = expr_eval_no_error;
-
-EXTERN_C_POP
-
-#ifndef IN_PROCESS_AGENT
-
-/* Pointer to the last tracepoint in the list, new tracepoints are
- linked in at the end. */
-
-static struct tracepoint *last_tracepoint;
-
-static const char *eval_result_names[] =
- {
- "terror:in the attic", /* this should never be reported */
- "terror:empty expression",
- "terror:empty stack",
- "terror:stack overflow",
- "terror:stack underflow",
- "terror:unhandled opcode",
- "terror:unrecognized opcode",
- "terror:divide by zero"
- };
-
-#endif
-
-/* The tracepoint in which the error occurred. */
-
-EXTERN_C_PUSH
-IP_AGENT_EXPORT_VAR struct tracepoint *error_tracepoint;
-EXTERN_C_POP
-
-struct trace_state_variable
-{
- /* This is the name of the variable as used in GDB. The target
- doesn't use the name, but needs to have it for saving and
- reconnection purposes. */
- char *name;
-
- /* This number identifies the variable uniquely. Numbers may be
- assigned either by the target (in the case of builtin variables),
- or by GDB, and are presumed unique during the course of a trace
- experiment. */
- int number;
-
- /* The variable's initial value, a 64-bit signed integer always. */
- LONGEST initial_value;
-
- /* The variable's value, a 64-bit signed integer always. */
- LONGEST value;
-
- /* Pointer to a getter function, used to supply computed values. */
- LONGEST (*getter) (void);
-
- /* Link to the next variable. */
- struct trace_state_variable *next;
-};
-
-/* Linked list of all trace state variables. */
-
-#ifdef IN_PROCESS_AGENT
-struct trace_state_variable *alloced_trace_state_variables;
-#endif
-
-IP_AGENT_EXPORT_VAR struct trace_state_variable *trace_state_variables;
-
-/* The results of tracing go into a fixed-size space known as the
- "trace buffer". Because usage follows a limited number of
- patterns, we manage it ourselves rather than with malloc. Basic
- rules are that we create only one trace frame at a time, each is
- variable in size, they are never moved once created, and we only
- discard if we are doing a circular buffer, and then only the oldest
- ones. Each trace frame includes its own size, so we don't need to
- link them together, and the trace frame number is relative to the
- first one, so we don't need to record numbers. A trace frame also
- records the number of the tracepoint that created it. The data
- itself is a series of blocks, each introduced by a single character
- and with a defined format. Each type of block has enough
- type/length info to allow scanners to jump quickly from one block
- to the next without reading each byte in the block. */
-
-/* Trace buffer management would be simple - advance a free pointer
- from beginning to end, then stop - were it not for the circular
- buffer option, which is a useful way to prevent a trace run from
- stopping prematurely because the buffer filled up. In the circular
- case, the location of the first trace frame (trace_buffer_start)
- moves as old trace frames are discarded. Also, since we grow trace
- frames incrementally as actions are performed, we wrap around to
- the beginning of the trace buffer. This is per-block, so each
- block within a trace frame remains contiguous. Things get messy
- when the wrapped-around trace frame is the one being discarded; the
- free space ends up in two parts at opposite ends of the buffer. */
-
-#ifndef ATTR_PACKED
-# if defined(__GNUC__)
-# define ATTR_PACKED __attribute__ ((packed))
-# else
-# define ATTR_PACKED /* nothing */
-# endif
-#endif
-
-/* The data collected at a tracepoint hit. This object should be as
- small as possible, since there may be a great many of them. We do
- not need to keep a frame number, because they are all sequential
- and there are no deletions; so the Nth frame in the buffer is
- always frame number N. */
-
-struct traceframe
-{
- /* Number of the tracepoint that collected this traceframe. A value
- of 0 indicates the current end of the trace buffer. We make this
- a 16-bit field because it's never going to happen that GDB's
- numbering of tracepoints reaches 32,000. */
- int tpnum : 16;
-
- /* The size of the data in this trace frame. We limit this to 32
- bits, even on a 64-bit target, because it's just implausible that
- one is validly going to collect 4 gigabytes of data at a single
- tracepoint hit. */
- unsigned int data_size : 32;
-
- /* The base of the trace data, which is contiguous from this point. */
- unsigned char data[0];
-
-} ATTR_PACKED;
-
-/* The size of the EOB marker, in bytes. A traceframe with zeroed
- fields (and no data) marks the end of trace data. */
-#define TRACEFRAME_EOB_MARKER_SIZE offsetof (struct traceframe, data)
-
-/* This flag is true if the trace buffer is circular, meaning that
- when it fills, the oldest trace frames are discarded in order to
- make room. */
-
-#ifndef IN_PROCESS_AGENT
-static int circular_trace_buffer;
-#endif
-
-/* Size of the trace buffer. */
-
-static LONGEST trace_buffer_size;
-
-EXTERN_C_PUSH
-
-/* Pointer to the block of memory that traceframes all go into. */
-
-IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_lo;
-
-/* Pointer to the end of the trace buffer, more precisely to the byte
- after the end of the buffer. */
-
-IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_hi;
-
-EXTERN_C_POP
-
-/* Control structure holding the read/write/etc. pointers into the
- trace buffer. We need more than one of these to implement a
- transaction-like mechanism to guarantees that both GDBserver and the
- in-process agent can try to change the trace buffer
- simultaneously. */
-
-struct trace_buffer_control
-{
- /* Pointer to the first trace frame in the buffer. In the
- non-circular case, this is equal to trace_buffer_lo, otherwise it
- moves around in the buffer. */
- unsigned char *start;
-
- /* Pointer to the free part of the trace buffer. Note that we clear
- several bytes at and after this pointer, so that traceframe
- scans/searches terminate properly. */
- unsigned char *free;
-
- /* Pointer to the byte after the end of the free part. Note that
- this may be smaller than trace_buffer_free in the circular case,
- and means that the free part is in two pieces. Initially it is
- equal to trace_buffer_hi, then is generally equivalent to
- trace_buffer_start. */
- unsigned char *end_free;
-
- /* Pointer to the wraparound. If not equal to trace_buffer_hi, then
- this is the point at which the trace data breaks, and resumes at
- trace_buffer_lo. */
- unsigned char *wrap;
-};
-
-/* Same as above, to be used by GDBserver when updating the in-process
- agent. */
-struct ipa_trace_buffer_control
-{
- uintptr_t start;
- uintptr_t free;
- uintptr_t end_free;
- uintptr_t wrap;
-};
-
-
-/* We have possibly both GDBserver and an inferior thread accessing
- the same IPA trace buffer memory. The IPA is the producer (tries
- to put new frames in the buffer), while GDBserver occasionally
- consumes them, that is, flushes the IPA's buffer into its own
- buffer. Both sides need to update the trace buffer control
- pointers (current head, tail, etc.). We can't use a global lock to
- synchronize the accesses, as otherwise we could deadlock GDBserver
- (if the thread holding the lock stops for a signal, say). So
- instead of that, we use a transaction scheme where GDBserver writes
- always prevail over the IPAs writes, and, we have the IPA detect
- the commit failure/overwrite, and retry the whole attempt. This is
- mainly implemented by having a global token object that represents
- who wrote last to the buffer control structure. We need to freeze
- any inferior writing to the buffer while GDBserver touches memory,
- so that the inferior can correctly detect that GDBserver had been
- there, otherwise, it could mistakingly think its commit was
- successful; that's implemented by simply having GDBserver set a
- breakpoint the inferior hits if it is the critical region.
-
- There are three cycling trace buffer control structure copies
- (buffer head, tail, etc.), with the token object including an index
- indicating which is current live copy. The IPA tentatively builds
- an updated copy in a non-current control structure, while GDBserver
- always clobbers the current version directly. The IPA then tries
- to atomically "commit" its version; if GDBserver clobbered the
- structure meanwhile, that will fail, and the IPA restarts the
- allocation process.
-
- Listing the step in further detail, we have:
-
- In-process agent (producer):
-
- - passes by `about_to_request_buffer_space' breakpoint/lock
-
- - reads current token, extracts current trace buffer control index,
- and starts tentatively updating the rightmost one (0->1, 1->2,
- 2->0). Note that only one inferior thread is executing this code
- at any given time, due to an outer lock in the jump pads.
-
- - updates counters, and tries to commit the token.
-
- - passes by second `about_to_request_buffer_space' breakpoint/lock,
- leaving the sync region.
-
- - checks if the update was effective.
-
- - if trace buffer was found full, hits flush_trace_buffer
- breakpoint, and restarts later afterwards.
-
- GDBserver (consumer):
-
- - sets `about_to_request_buffer_space' breakpoint/lock.
-
- - updates the token unconditionally, using the current buffer
- control index, since it knows that the IP agent always writes to
- the rightmost, and due to the breakpoint, at most one IP thread
- can try to update the trace buffer concurrently to GDBserver, so
- there will be no danger of trace buffer control index wrap making
- the IPA write to the same index as GDBserver.
-
- - flushes the IP agent's trace buffer completely, and updates the
- current trace buffer control structure. GDBserver *always* wins.
-
- - removes the `about_to_request_buffer_space' breakpoint.
-
-The token is stored in the `trace_buffer_ctrl_curr' variable.
-Internally, it's bits are defined as:
-
- |-------------+-----+-------------+--------+-------------+--------------|
- | Bit offsets | 31 | 30 - 20 | 19 | 18-8 | 7-0 |
- |-------------+-----+-------------+--------+-------------+--------------|
- | What | GSB | PC (11-bit) | unused | CC (11-bit) | TBCI (8-bit) |
- |-------------+-----+-------------+--------+-------------+--------------|
-
- GSB - GDBserver Stamp Bit
- PC - Previous Counter
- CC - Current Counter
- TBCI - Trace Buffer Control Index
-
-
-An IPA update of `trace_buffer_ctrl_curr' does:
-
- - read CC from the current token, save as PC.
- - updates pointers
- - atomically tries to write PC+1,CC
-
-A GDBserver update of `trace_buffer_ctrl_curr' does:
-
- - reads PC and CC from the current token.
- - updates pointers
- - writes GSB,PC,CC
-*/
-
-/* These are the bits of `trace_buffer_ctrl_curr' that are reserved
- for the counters described below. The cleared bits are used to
- hold the index of the items of the `trace_buffer_ctrl' array that
- is "current". */
-#define GDBSERVER_FLUSH_COUNT_MASK 0xfffffff0
-
-/* `trace_buffer_ctrl_curr' contains two counters. The `previous'
- counter, and the `current' counter. */
-
-#define GDBSERVER_FLUSH_COUNT_MASK_PREV 0x7ff00000
-#define GDBSERVER_FLUSH_COUNT_MASK_CURR 0x0007ff00
-
-/* When GDBserver update the IP agent's `trace_buffer_ctrl_curr', it
- always stamps this bit as set. */
-#define GDBSERVER_UPDATED_FLUSH_COUNT_BIT 0x80000000
-
-#ifdef IN_PROCESS_AGENT
-IP_AGENT_EXPORT_VAR struct trace_buffer_control trace_buffer_ctrl[3];
-IP_AGENT_EXPORT_VAR unsigned int trace_buffer_ctrl_curr;
-
-# define TRACE_BUFFER_CTRL_CURR \
- (trace_buffer_ctrl_curr & ~GDBSERVER_FLUSH_COUNT_MASK)
-
-#else
-
-/* The GDBserver side agent only needs one instance of this object, as
- it doesn't need to sync with itself. Define it as array anyway so
- that the rest of the code base doesn't need to care for the
- difference. */
-struct trace_buffer_control trace_buffer_ctrl[1];
-# define TRACE_BUFFER_CTRL_CURR 0
-#endif
-
-/* These are convenience macros used to access the current trace
- buffer control in effect. */
-#define trace_buffer_start (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].start)
-#define trace_buffer_free (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].free)
-#define trace_buffer_end_free \
- (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].end_free)
-#define trace_buffer_wrap (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].wrap)
-
-
-/* Macro that returns a pointer to the first traceframe in the buffer. */
-
-#define FIRST_TRACEFRAME() ((struct traceframe *) trace_buffer_start)
-
-/* Macro that returns a pointer to the next traceframe in the buffer.
- If the computed location is beyond the wraparound point, subtract
- the offset of the wraparound. */
-
-#define NEXT_TRACEFRAME_1(TF) \
- (((unsigned char *) (TF)) + sizeof (struct traceframe) + (TF)->data_size)
-
-#define NEXT_TRACEFRAME(TF) \
- ((struct traceframe *) (NEXT_TRACEFRAME_1 (TF) \
- - ((NEXT_TRACEFRAME_1 (TF) >= trace_buffer_wrap) \
- ? (trace_buffer_wrap - trace_buffer_lo) \
- : 0)))
-
-/* The difference between these counters represents the total number
- of complete traceframes present in the trace buffer. The IP agent
- writes to the write count, GDBserver writes to read count. */
-
-IP_AGENT_EXPORT_VAR unsigned int traceframe_write_count;
-IP_AGENT_EXPORT_VAR unsigned int traceframe_read_count;
-
-/* Convenience macro. */
-
-#define traceframe_count \
- ((unsigned int) (traceframe_write_count - traceframe_read_count))
-
-/* The count of all traceframes created in the current run, including
- ones that were discarded to make room. */
-
-IP_AGENT_EXPORT_VAR int traceframes_created;
-
-#ifndef IN_PROCESS_AGENT
-
-/* Read-only regions are address ranges whose contents don't change,
- and so can be read from target memory even while looking at a trace
- frame. Without these, disassembly for instance will likely fail,
- because the program code is not usually collected into a trace
- frame. This data structure does not need to be very complicated or
- particularly efficient, it's only going to be used occasionally,
- and only by some commands. */
-
-struct readonly_region
-{
- /* The bounds of the region. */
- CORE_ADDR start, end;
-
- /* Link to the next one. */
- struct readonly_region *next;
-};
-
-/* Linked list of readonly regions. This list stays in effect from
- one tstart to the next. */
-
-static struct readonly_region *readonly_regions;
-
-#endif
-
-/* The global that controls tracing overall. */
-
-IP_AGENT_EXPORT_VAR int tracing;
-
-#ifndef IN_PROCESS_AGENT
-
-/* Controls whether tracing should continue after GDB disconnects. */
-
-int disconnected_tracing;
-
-/* The reason for the last tracing run to have stopped. We initialize
- to a distinct string so that GDB can distinguish between "stopped
- after running" and "stopped because never run" cases. */
-
-static const char *tracing_stop_reason = "tnotrun";
-
-static int tracing_stop_tpnum;
-
-/* 64-bit timestamps for the trace run's start and finish, expressed
- in microseconds from the Unix epoch. */
-
-LONGEST tracing_start_time;
-LONGEST tracing_stop_time;
-
-/* The (optional) user-supplied name of the user that started the run.
- This is an arbitrary string, and may be NULL. */
-
-char *tracing_user_name;
-
-/* Optional user-supplied text describing the run. This is
- an arbitrary string, and may be NULL. */
-
-char *tracing_notes;
-
-/* Optional user-supplied text explaining a tstop command. This is an
- arbitrary string, and may be NULL. */
-
-char *tracing_stop_note;
-
-#endif
-
-/* Functions local to this file. */
-
-/* Base "class" for tracepoint type specific data to be passed down to
- collect_data_at_tracepoint. */
-struct tracepoint_hit_ctx
-{
- enum tracepoint_type type;
-};
-
-#ifdef IN_PROCESS_AGENT
-
-/* Fast/jump tracepoint specific data to be passed down to
- collect_data_at_tracepoint. */
-struct fast_tracepoint_ctx
-{
- struct tracepoint_hit_ctx base;
-
- struct regcache regcache;
- int regcache_initted;
- unsigned char *regspace;
-
- unsigned char *regs;
- struct tracepoint *tpoint;
-};
-
-/* Static tracepoint specific data to be passed down to
- collect_data_at_tracepoint. */
-struct static_tracepoint_ctx
-{
- struct tracepoint_hit_ctx base;
-
- /* The regcache corresponding to the registers state at the time of
- the tracepoint hit. Initialized lazily, from REGS. */
- struct regcache regcache;
- int regcache_initted;
-
- /* The buffer space REGCACHE above uses. We use a separate buffer
- instead of letting the regcache malloc for both signal safety and
- performance reasons; this is allocated on the stack instead. */
- unsigned char *regspace;
-
- /* The register buffer as passed on by lttng/ust. */
- struct registers *regs;
-
- /* The "printf" formatter and the args the user passed to the marker
- call. We use this to be able to collect "static trace data"
- ($_sdata). */
- const char *fmt;
- va_list *args;
-
- /* The GDB tracepoint matching the probed marker that was "hit". */
- struct tracepoint *tpoint;
-};
-
-#else
-
-/* Static tracepoint specific data to be passed down to
- collect_data_at_tracepoint. */
-struct trap_tracepoint_ctx
-{
- struct tracepoint_hit_ctx base;
-
- struct regcache *regcache;
-};
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-static CORE_ADDR traceframe_get_pc (struct traceframe *tframe);
-static int traceframe_read_tsv (int num, LONGEST *val);
-#endif
-
-static int condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- struct tracepoint *tpoint);
-
-#ifndef IN_PROCESS_AGENT
-static void clear_readonly_regions (void);
-static void clear_installed_tracepoints (void);
-#endif
-
-static void collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- CORE_ADDR stop_pc,
- struct tracepoint *tpoint);
-#ifndef IN_PROCESS_AGENT
-static void collect_data_at_step (struct tracepoint_hit_ctx *ctx,
- CORE_ADDR stop_pc,
- struct tracepoint *tpoint, int current_step);
-static void compile_tracepoint_condition (struct tracepoint *tpoint,
- CORE_ADDR *jump_entry);
-#endif
-static void do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- CORE_ADDR stop_pc,
- struct tracepoint *tpoint,
- struct traceframe *tframe,
- struct tracepoint_action *taction);
-
-#ifndef IN_PROCESS_AGENT
-static struct tracepoint *fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR);
-
-static void install_tracepoint (struct tracepoint *, char *own_buf);
-static void download_tracepoint (struct tracepoint *);
-static int install_fast_tracepoint (struct tracepoint *, char *errbuf);
-static void clone_fast_tracepoint (struct tracepoint *to,
- const struct tracepoint *from);
-#endif
-
-static LONGEST get_timestamp (void);
-
-#if defined(__GNUC__)
-# define memory_barrier() asm volatile ("" : : : "memory")
-#else
-# define memory_barrier() do {} while (0)
-#endif
-
-/* We only build the IPA if this builtin is supported, and there are
- no uses of this in GDBserver itself, so we're safe in defining this
- unconditionally. */
-#define cmpxchg(mem, oldval, newval) \
- __sync_val_compare_and_swap (mem, oldval, newval)
-
-/* Record that an error occurred during expression evaluation. */
-
-static void
-record_tracepoint_error (struct tracepoint *tpoint, const char *which,
- enum eval_result_type rtype)
-{
- trace_debug ("Tracepoint %d at %s %s eval reports error %d",
- tpoint->number, paddress (tpoint->address), which, rtype);
-
-#ifdef IN_PROCESS_AGENT
- /* Only record the first error we get. */
- if (cmpxchg (&expr_eval_result,
- expr_eval_no_error,
- rtype) != expr_eval_no_error)
- return;
-#else
- if (expr_eval_result != expr_eval_no_error)
- return;
-#endif
-
- error_tracepoint = tpoint;
-}
-
-/* Trace buffer management. */
-
-static void
-clear_trace_buffer (void)
-{
- trace_buffer_start = trace_buffer_lo;
- trace_buffer_free = trace_buffer_lo;
- trace_buffer_end_free = trace_buffer_hi;
- trace_buffer_wrap = trace_buffer_hi;
- /* A traceframe with zeroed fields marks the end of trace data. */
- ((struct traceframe *) trace_buffer_free)->tpnum = 0;
- ((struct traceframe *) trace_buffer_free)->data_size = 0;
- traceframe_read_count = traceframe_write_count = 0;
- traceframes_created = 0;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-static void
-clear_inferior_trace_buffer (void)
-{
- CORE_ADDR ipa_trace_buffer_lo;
- CORE_ADDR ipa_trace_buffer_hi;
- struct traceframe ipa_traceframe = { 0 };
- struct ipa_trace_buffer_control ipa_trace_buffer_ctrl;
-
- read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_lo,
- &ipa_trace_buffer_lo);
- read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_hi,
- &ipa_trace_buffer_hi);
-
- ipa_trace_buffer_ctrl.start = ipa_trace_buffer_lo;
- ipa_trace_buffer_ctrl.free = ipa_trace_buffer_lo;
- ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_hi;
- ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
-
- /* A traceframe with zeroed fields marks the end of trace data. */
- target_write_memory (ipa_sym_addrs.addr_trace_buffer_ctrl,
- (unsigned char *) &ipa_trace_buffer_ctrl,
- sizeof (ipa_trace_buffer_ctrl));
-
- write_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr, 0);
-
- /* A traceframe with zeroed fields marks the end of trace data. */
- target_write_memory (ipa_trace_buffer_lo,
- (unsigned char *) &ipa_traceframe,
- sizeof (ipa_traceframe));
-
- write_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count, 0);
- write_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count, 0);
- write_inferior_integer (ipa_sym_addrs.addr_traceframes_created, 0);
-}
-
-#endif
-
-static void
-init_trace_buffer (LONGEST bufsize)
-{
- size_t alloc_size;
-
- trace_buffer_size = bufsize;
-
- /* Make sure to internally allocate at least space for the EOB
- marker. */
- alloc_size = (bufsize < TRACEFRAME_EOB_MARKER_SIZE
- ? TRACEFRAME_EOB_MARKER_SIZE : bufsize);
- trace_buffer_lo = (unsigned char *) xrealloc (trace_buffer_lo, alloc_size);
-
- trace_buffer_hi = trace_buffer_lo + trace_buffer_size;
-
- clear_trace_buffer ();
-}
-
-#ifdef IN_PROCESS_AGENT
-
-/* This is needed for -Wmissing-declarations. */
-IP_AGENT_EXPORT_FUNC void about_to_request_buffer_space (void);
-
-IP_AGENT_EXPORT_FUNC void
-about_to_request_buffer_space (void)
-{
- /* GDBserver places breakpoint here while it goes about to flush
- data at random times. */
- UNKNOWN_SIDE_EFFECTS();
-}
-
-#endif
-
-/* Carve out a piece of the trace buffer, returning NULL in case of
- failure. */
-
-static void *
-trace_buffer_alloc (size_t amt)
-{
- unsigned char *rslt;
- struct trace_buffer_control *tbctrl;
- unsigned int curr;
-#ifdef IN_PROCESS_AGENT
- unsigned int prev, prev_filtered;
- unsigned int commit_count;
- unsigned int commit;
- unsigned int readout;
-#else
- struct traceframe *oldest;
- unsigned char *new_start;
-#endif
-
- trace_debug ("Want to allocate %ld+%ld bytes in trace buffer",
- (long) amt, (long) sizeof (struct traceframe));
-
- /* Account for the EOB marker. */
- amt += TRACEFRAME_EOB_MARKER_SIZE;
-
-#ifdef IN_PROCESS_AGENT
- again:
- memory_barrier ();
-
- /* Read the current token and extract the index to try to write to,
- storing it in CURR. */
- prev = trace_buffer_ctrl_curr;
- prev_filtered = prev & ~GDBSERVER_FLUSH_COUNT_MASK;
- curr = prev_filtered + 1;
- if (curr > 2)
- curr = 0;
-
- about_to_request_buffer_space ();
-
- /* Start out with a copy of the current state. GDBserver may be
- midway writing to the PREV_FILTERED TBC, but, that's OK, we won't
- be able to commit anyway if that happens. */
- trace_buffer_ctrl[curr]
- = trace_buffer_ctrl[prev_filtered];
- trace_debug ("trying curr=%u", curr);
-#else
- /* The GDBserver's agent doesn't need all that syncing, and always
- updates TCB 0 (there's only one, mind you). */
- curr = 0;
-#endif
- tbctrl = &trace_buffer_ctrl[curr];
-
- /* Offsets are easier to grok for debugging than raw addresses,
- especially for the small trace buffer sizes that are useful for
- testing. */
- trace_debug ("Trace buffer [%d] start=%d free=%d endfree=%d wrap=%d hi=%d",
- curr,
- (int) (tbctrl->start - trace_buffer_lo),
- (int) (tbctrl->free - trace_buffer_lo),
- (int) (tbctrl->end_free - trace_buffer_lo),
- (int) (tbctrl->wrap - trace_buffer_lo),
- (int) (trace_buffer_hi - trace_buffer_lo));
-
- /* The algorithm here is to keep trying to get a contiguous block of
- the requested size, possibly discarding older traceframes to free
- up space. Since free space might come in one or two pieces,
- depending on whether discarded traceframes wrapped around at the
- high end of the buffer, we test both pieces after each
- discard. */
- while (1)
- {
- /* First, if we have two free parts, try the upper one first. */
- if (tbctrl->end_free < tbctrl->free)
- {
- if (tbctrl->free + amt <= trace_buffer_hi)
- /* We have enough in the upper part. */
- break;
- else
- {
- /* Our high part of free space wasn't enough. Give up
- on it for now, set wraparound. We will recover the
- space later, if/when the wrapped-around traceframe is
- discarded. */
- trace_debug ("Upper part too small, setting wraparound");
- tbctrl->wrap = tbctrl->free;
- tbctrl->free = trace_buffer_lo;
- }
- }
-
- /* The normal case. */
- if (tbctrl->free + amt <= tbctrl->end_free)
- break;
-
-#ifdef IN_PROCESS_AGENT
- /* The IP Agent's buffer is always circular. It isn't used
- currently, but `circular_trace_buffer' could represent
- GDBserver's mode. If we didn't find space, ask GDBserver to
- flush. */
-
- flush_trace_buffer ();
- memory_barrier ();
- if (tracing)
- {
- trace_debug ("gdbserver flushed buffer, retrying");
- goto again;
- }
-
- /* GDBserver cancelled the tracing. Bail out as well. */
- return NULL;
-#else
- /* If we're here, then neither part is big enough, and
- non-circular trace buffers are now full. */
- if (!circular_trace_buffer)
- {
- trace_debug ("Not enough space in the trace buffer");
- return NULL;
- }
-
- trace_debug ("Need more space in the trace buffer");
-
- /* If we have a circular buffer, we can try discarding the
- oldest traceframe and see if that helps. */
- oldest = FIRST_TRACEFRAME ();
- if (oldest->tpnum == 0)
- {
- /* Not good; we have no traceframes to free. Perhaps we're
- asking for a block that is larger than the buffer? In
- any case, give up. */
- trace_debug ("No traceframes to discard");
- return NULL;
- }
-
- /* We don't run this code in the in-process agent currently.
- E.g., we could leave the in-process agent in autonomous
- circular mode if we only have fast tracepoints. If we do
- that, then this bit becomes racy with GDBserver, which also
- writes to this counter. */
- --traceframe_write_count;
-
- new_start = (unsigned char *) NEXT_TRACEFRAME (oldest);
- /* If we freed the traceframe that wrapped around, go back
- to the non-wrap case. */
- if (new_start < tbctrl->start)
- {
- trace_debug ("Discarding past the wraparound");
- tbctrl->wrap = trace_buffer_hi;
- }
- tbctrl->start = new_start;
- tbctrl->end_free = tbctrl->start;
-
- trace_debug ("Discarded a traceframe\n"
- "Trace buffer [%d], start=%d free=%d "
- "endfree=%d wrap=%d hi=%d",
- curr,
- (int) (tbctrl->start - trace_buffer_lo),
- (int) (tbctrl->free - trace_buffer_lo),
- (int) (tbctrl->end_free - trace_buffer_lo),
- (int) (tbctrl->wrap - trace_buffer_lo),
- (int) (trace_buffer_hi - trace_buffer_lo));
-
- /* Now go back around the loop. The discard might have resulted
- in either one or two pieces of free space, so we want to try
- both before freeing any more traceframes. */
-#endif
- }
-
- /* If we get here, we know we can provide the asked-for space. */
-
- rslt = tbctrl->free;
-
- /* Adjust the request back down, now that we know we have space for
- the marker, but don't commit to AMT yet, we may still need to
- restart the operation if GDBserver touches the trace buffer
- (obviously only important in the in-process agent's version). */
- tbctrl->free += (amt - sizeof (struct traceframe));
-
- /* Or not. If GDBserver changed the trace buffer behind our back,
- we get to restart a new allocation attempt. */
-
-#ifdef IN_PROCESS_AGENT
- /* Build the tentative token. */
- commit_count = (((prev & GDBSERVER_FLUSH_COUNT_MASK_CURR) + 0x100)
- & GDBSERVER_FLUSH_COUNT_MASK_CURR);
- commit = (((prev & GDBSERVER_FLUSH_COUNT_MASK_CURR) << 12)
- | commit_count
- | curr);
-
- /* Try to commit it. */
- readout = cmpxchg (&trace_buffer_ctrl_curr, prev, commit);
- if (readout != prev)
- {
- trace_debug ("GDBserver has touched the trace buffer, restarting."
- " (prev=%08x, commit=%08x, readout=%08x)",
- prev, commit, readout);
- goto again;
- }
-
- /* Hold your horses here. Even if that change was committed,
- GDBserver could come in, and clobber it. We need to hold to be
- able to tell if GDBserver clobbers before or after we committed
- the change. Whenever GDBserver goes about touching the IPA
- buffer, it sets a breakpoint in this routine, so we have a sync
- point here. */
- about_to_request_buffer_space ();
-
- /* Check if the change has been effective, even if GDBserver stopped
- us at the breakpoint. */
-
- {
- unsigned int refetch;
-
- memory_barrier ();
-
- refetch = trace_buffer_ctrl_curr;
-
- if (refetch == commit
- || ((refetch & GDBSERVER_FLUSH_COUNT_MASK_PREV) >> 12) == commit_count)
- {
- /* effective */
- trace_debug ("change is effective: (prev=%08x, commit=%08x, "
- "readout=%08x, refetch=%08x)",
- prev, commit, readout, refetch);
- }
- else
- {
- trace_debug ("GDBserver has touched the trace buffer, not effective."
- " (prev=%08x, commit=%08x, readout=%08x, refetch=%08x)",
- prev, commit, readout, refetch);
- goto again;
- }
- }
-#endif
-
- /* We have a new piece of the trace buffer. Hurray! */
-
- /* Add an EOB marker just past this allocation. */
- ((struct traceframe *) tbctrl->free)->tpnum = 0;
- ((struct traceframe *) tbctrl->free)->data_size = 0;
-
- /* Adjust the request back down, now that we know we have space for
- the marker. */
- amt -= sizeof (struct traceframe);
-
- if (debug_threads)
- {
- trace_debug ("Allocated %d bytes", (int) amt);
- trace_debug ("Trace buffer [%d] start=%d free=%d "
- "endfree=%d wrap=%d hi=%d",
- curr,
- (int) (tbctrl->start - trace_buffer_lo),
- (int) (tbctrl->free - trace_buffer_lo),
- (int) (tbctrl->end_free - trace_buffer_lo),
- (int) (tbctrl->wrap - trace_buffer_lo),
- (int) (trace_buffer_hi - trace_buffer_lo));
- }
-
- return rslt;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-/* Return the total free space. This is not necessarily the largest
- block we can allocate, because of the two-part case. */
-
-static int
-free_space (void)
-{
- if (trace_buffer_free <= trace_buffer_end_free)
- return trace_buffer_end_free - trace_buffer_free;
- else
- return ((trace_buffer_end_free - trace_buffer_lo)
- + (trace_buffer_hi - trace_buffer_free));
-}
-
-/* An 'S' in continuation packets indicates remainder are for
- while-stepping. */
-
-static int seen_step_action_flag;
-
-/* Create a tracepoint (location) with given number and address. Add this
- new tracepoint to list and sort this list. */
-
-static struct tracepoint *
-add_tracepoint (int num, CORE_ADDR addr)
-{
- struct tracepoint *tpoint, **tp_next;
-
- tpoint = XNEW (struct tracepoint);
- tpoint->number = num;
- tpoint->address = addr;
- tpoint->numactions = 0;
- tpoint->actions = NULL;
- tpoint->actions_str = NULL;
- tpoint->cond = NULL;
- tpoint->num_step_actions = 0;
- tpoint->step_actions = NULL;
- tpoint->step_actions_str = NULL;
- /* Start all off as regular (slow) tracepoints. */
- tpoint->type = trap_tracepoint;
- tpoint->orig_size = -1;
- tpoint->source_strings = NULL;
- tpoint->compiled_cond = 0;
- tpoint->handle = NULL;
- tpoint->next = NULL;
-
- /* Find a place to insert this tracepoint into list in order to keep
- the tracepoint list still in the ascending order. There may be
- multiple tracepoints at the same address as TPOINT's, and this
- guarantees TPOINT is inserted after all the tracepoints which are
- set at the same address. For example, fast tracepoints A, B, C are
- set at the same address, and D is to be insert at the same place as
- well,
-
- -->| A |--> | B |-->| C |->...
-
- One jump pad was created for tracepoint A, B, and C, and the target
- address of A is referenced/used in jump pad. So jump pad will let
- inferior jump to A. If D is inserted in front of A, like this,
-
- -->| D |-->| A |--> | B |-->| C |->...
-
- without updating jump pad, D is not reachable during collect, which
- is wrong. As we can see, the order of B, C and D doesn't matter, but
- A should always be the `first' one. */
- for (tp_next = &tracepoints;
- (*tp_next) != NULL && (*tp_next)->address <= tpoint->address;
- tp_next = &(*tp_next)->next)
- ;
- tpoint->next = *tp_next;
- *tp_next = tpoint;
- last_tracepoint = tpoint;
-
- seen_step_action_flag = 0;
-
- return tpoint;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-/* Return the tracepoint with the given number and address, or NULL. */
-
-static struct tracepoint *
-find_tracepoint (int id, CORE_ADDR addr)
-{
- struct tracepoint *tpoint;
-
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- if (tpoint->number == id && tpoint->address == addr)
- return tpoint;
-
- return NULL;
-}
-
-/* Remove TPOINT from global list. */
-
-static void
-remove_tracepoint (struct tracepoint *tpoint)
-{
- struct tracepoint *tp, *tp_prev;
-
- for (tp = tracepoints, tp_prev = NULL; tp && tp != tpoint;
- tp_prev = tp, tp = tp->next)
- ;
-
- if (tp)
- {
- if (tp_prev)
- tp_prev->next = tp->next;
- else
- tracepoints = tp->next;
-
- xfree (tp);
- }
-}
-
-/* There may be several tracepoints with the same number (because they
- are "locations", in GDB parlance); return the next one after the
- given tracepoint, or search from the beginning of the list if the
- first argument is NULL. */
-
-static struct tracepoint *
-find_next_tracepoint_by_number (struct tracepoint *prev_tp, int num)
-{
- struct tracepoint *tpoint;
-
- if (prev_tp)
- tpoint = prev_tp->next;
- else
- tpoint = tracepoints;
- for (; tpoint; tpoint = tpoint->next)
- if (tpoint->number == num)
- return tpoint;
-
- return NULL;
-}
-
-#endif
-
-/* Append another action to perform when the tracepoint triggers. */
-
-static void
-add_tracepoint_action (struct tracepoint *tpoint, const char *packet)
-{
- const char *act;
-
- if (*packet == 'S')
- {
- seen_step_action_flag = 1;
- ++packet;
- }
-
- act = packet;
-
- while (*act)
- {
- const char *act_start = act;
- struct tracepoint_action *action = NULL;
-
- switch (*act)
- {
- case 'M':
- {
- struct collect_memory_action *maction =
- XNEW (struct collect_memory_action);
- ULONGEST basereg;
- int is_neg;
-
- maction->base.type = *act;
- action = &maction->base;
-
- ++act;
- is_neg = (*act == '-');
- if (*act == '-')
- ++act;
- act = unpack_varlen_hex (act, &basereg);
- ++act;
- act = unpack_varlen_hex (act, &maction->addr);
- ++act;
- act = unpack_varlen_hex (act, &maction->len);
- maction->basereg = (is_neg
- ? - (int) basereg
- : (int) basereg);
- trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)",
- pulongest (maction->len),
- paddress (maction->addr), maction->basereg);
- break;
- }
- case 'R':
- {
- struct collect_registers_action *raction =
- XNEW (struct collect_registers_action);
-
- raction->base.type = *act;
- action = &raction->base;
-
- trace_debug ("Want to collect registers");
- ++act;
- /* skip past hex digits of mask for now */
- while (isxdigit(*act))
- ++act;
- break;
- }
- case 'L':
- {
- struct collect_static_trace_data_action *raction =
- XNEW (struct collect_static_trace_data_action);
-
- raction->base.type = *act;
- action = &raction->base;
-
- trace_debug ("Want to collect static trace data");
- ++act;
- break;
- }
- case 'S':
- trace_debug ("Unexpected step action, ignoring");
- ++act;
- break;
- case 'X':
- {
- struct eval_expr_action *xaction = XNEW (struct eval_expr_action);
-
- xaction->base.type = *act;
- action = &xaction->base;
-
- trace_debug ("Want to evaluate expression");
- xaction->expr = gdb_parse_agent_expr (&act);
- break;
- }
- default:
- trace_debug ("unknown trace action '%c', ignoring...", *act);
- break;
- case '-':
- break;
- }
-
- if (action == NULL)
- break;
-
- if (seen_step_action_flag)
- {
- tpoint->num_step_actions++;
-
- tpoint->step_actions
- = XRESIZEVEC (struct tracepoint_action *, tpoint->step_actions,
- tpoint->num_step_actions);
- tpoint->step_actions_str
- = XRESIZEVEC (char *, tpoint->step_actions_str,
- tpoint->num_step_actions);
- tpoint->step_actions[tpoint->num_step_actions - 1] = action;
- tpoint->step_actions_str[tpoint->num_step_actions - 1]
- = savestring (act_start, act - act_start);
- }
- else
- {
- tpoint->numactions++;
- tpoint->actions
- = XRESIZEVEC (struct tracepoint_action *, tpoint->actions,
- tpoint->numactions);
- tpoint->actions_str
- = XRESIZEVEC (char *, tpoint->actions_str, tpoint->numactions);
- tpoint->actions[tpoint->numactions - 1] = action;
- tpoint->actions_str[tpoint->numactions - 1]
- = savestring (act_start, act - act_start);
- }
- }
-}
-
-#endif
-
-/* Find or create a trace state variable with the given number. */
-
-static struct trace_state_variable *
-get_trace_state_variable (int num)
-{
- struct trace_state_variable *tsv;
-
-#ifdef IN_PROCESS_AGENT
- /* Search for an existing variable. */
- for (tsv = alloced_trace_state_variables; tsv; tsv = tsv->next)
- if (tsv->number == num)
- return tsv;
-#endif
-
- /* Search for an existing variable. */
- for (tsv = trace_state_variables; tsv; tsv = tsv->next)
- if (tsv->number == num)
- return tsv;
-
- return NULL;
-}
-
-/* Find or create a trace state variable with the given number. */
-
-static struct trace_state_variable *
-create_trace_state_variable (int num, int gdb)
-{
- struct trace_state_variable *tsv;
-
- tsv = get_trace_state_variable (num);
- if (tsv != NULL)
- return tsv;
-
- /* Create a new variable. */
- tsv = XNEW (struct trace_state_variable);
- tsv->number = num;
- tsv->initial_value = 0;
- tsv->value = 0;
- tsv->getter = NULL;
- tsv->name = NULL;
-#ifdef IN_PROCESS_AGENT
- if (!gdb)
- {
- tsv->next = alloced_trace_state_variables;
- alloced_trace_state_variables = tsv;
- }
- else
-#endif
- {
- tsv->next = trace_state_variables;
- trace_state_variables = tsv;
- }
- return tsv;
-}
-
-/* This is needed for -Wmissing-declarations. */
-IP_AGENT_EXPORT_FUNC LONGEST get_trace_state_variable_value (int num);
-
-IP_AGENT_EXPORT_FUNC LONGEST
-get_trace_state_variable_value (int num)
-{
- struct trace_state_variable *tsv;
-
- tsv = get_trace_state_variable (num);
-
- if (!tsv)
- {
- trace_debug ("No trace state variable %d, skipping value get", num);
- return 0;
- }
-
- /* Call a getter function if we have one. While it's tempting to
- set up something to only call the getter once per tracepoint hit,
- it could run afoul of thread races. Better to let the getter
- handle it directly, if necessary to worry about it. */
- if (tsv->getter)
- tsv->value = (tsv->getter) ();
-
- trace_debug ("get_trace_state_variable_value(%d) ==> %s",
- num, plongest (tsv->value));
-
- return tsv->value;
-}
-
-/* This is needed for -Wmissing-declarations. */
-IP_AGENT_EXPORT_FUNC void set_trace_state_variable_value (int num,
- LONGEST val);
-
-IP_AGENT_EXPORT_FUNC void
-set_trace_state_variable_value (int num, LONGEST val)
-{
- struct trace_state_variable *tsv;
-
- tsv = get_trace_state_variable (num);
-
- if (!tsv)
- {
- trace_debug ("No trace state variable %d, skipping value set", num);
- return;
- }
-
- tsv->value = val;
-}
-
-LONGEST
-agent_get_trace_state_variable_value (int num)
-{
- return get_trace_state_variable_value (num);
-}
-
-void
-agent_set_trace_state_variable_value (int num, LONGEST val)
-{
- set_trace_state_variable_value (num, val);
-}
-
-static void
-set_trace_state_variable_name (int num, const char *name)
-{
- struct trace_state_variable *tsv;
-
- tsv = get_trace_state_variable (num);
-
- if (!tsv)
- {
- trace_debug ("No trace state variable %d, skipping name set", num);
- return;
- }
-
- tsv->name = (char *) name;
-}
-
-static void
-set_trace_state_variable_getter (int num, LONGEST (*getter) (void))
-{
- struct trace_state_variable *tsv;
-
- tsv = get_trace_state_variable (num);
-
- if (!tsv)
- {
- trace_debug ("No trace state variable %d, skipping getter set", num);
- return;
- }
-
- tsv->getter = getter;
-}
-
-/* Add a raw traceframe for the given tracepoint. */
-
-static struct traceframe *
-add_traceframe (struct tracepoint *tpoint)
-{
- struct traceframe *tframe;
-
- tframe
- = (struct traceframe *) trace_buffer_alloc (sizeof (struct traceframe));
-
- if (tframe == NULL)
- return NULL;
-
- tframe->tpnum = tpoint->number;
- tframe->data_size = 0;
-
- return tframe;
-}
-
-/* Add a block to the traceframe currently being worked on. */
-
-static unsigned char *
-add_traceframe_block (struct traceframe *tframe,
- struct tracepoint *tpoint, int amt)
-{
- unsigned char *block;
-
- if (!tframe)
- return NULL;
-
- block = (unsigned char *) trace_buffer_alloc (amt);
-
- if (!block)
- return NULL;
-
- gdb_assert (tframe->tpnum == tpoint->number);
-
- tframe->data_size += amt;
- tpoint->traceframe_usage += amt;
-
- return block;
-}
-
-/* Flag that the current traceframe is finished. */
-
-static void
-finish_traceframe (struct traceframe *tframe)
-{
- ++traceframe_write_count;
- ++traceframes_created;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-/* Given a traceframe number NUM, find the NUMth traceframe in the
- buffer. */
-
-static struct traceframe *
-find_traceframe (int num)
-{
- struct traceframe *tframe;
- int tfnum = 0;
-
- for (tframe = FIRST_TRACEFRAME ();
- tframe->tpnum != 0;
- tframe = NEXT_TRACEFRAME (tframe))
- {
- if (tfnum == num)
- return tframe;
- ++tfnum;
- }
-
- return NULL;
-}
-
-static CORE_ADDR
-get_traceframe_address (struct traceframe *tframe)
-{
- CORE_ADDR addr;
- struct tracepoint *tpoint;
-
- addr = traceframe_get_pc (tframe);
-
- if (addr)
- return addr;
-
- /* Fallback strategy, will be incorrect for while-stepping frames
- and multi-location tracepoints. */
- tpoint = find_next_tracepoint_by_number (NULL, tframe->tpnum);
- return tpoint->address;
-}
-
-/* Search for the next traceframe whose address is inside or outside
- the given range. */
-
-static struct traceframe *
-find_next_traceframe_in_range (CORE_ADDR lo, CORE_ADDR hi, int inside_p,
- int *tfnump)
-{
- client_state &cs = get_client_state ();
- struct traceframe *tframe;
- CORE_ADDR tfaddr;
-
- *tfnump = cs.current_traceframe + 1;
- tframe = find_traceframe (*tfnump);
- /* The search is not supposed to wrap around. */
- if (!tframe)
- {
- *tfnump = -1;
- return NULL;
- }
-
- for (; tframe->tpnum != 0; tframe = NEXT_TRACEFRAME (tframe))
- {
- tfaddr = get_traceframe_address (tframe);
- if (inside_p
- ? (lo <= tfaddr && tfaddr <= hi)
- : (lo > tfaddr || tfaddr > hi))
- return tframe;
- ++*tfnump;
- }
-
- *tfnump = -1;
- return NULL;
-}
-
-/* Search for the next traceframe recorded by the given tracepoint.
- Note that for multi-location tracepoints, this will find whatever
- location appears first. */
-
-static struct traceframe *
-find_next_traceframe_by_tracepoint (int num, int *tfnump)
-{
- client_state &cs = get_client_state ();
- struct traceframe *tframe;
-
- *tfnump = cs.current_traceframe + 1;
- tframe = find_traceframe (*tfnump);
- /* The search is not supposed to wrap around. */
- if (!tframe)
- {
- *tfnump = -1;
- return NULL;
- }
-
- for (; tframe->tpnum != 0; tframe = NEXT_TRACEFRAME (tframe))
- {
- if (tframe->tpnum == num)
- return tframe;
- ++*tfnump;
- }
-
- *tfnump = -1;
- return NULL;
-}
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-/* Clear all past trace state. */
-
-static void
-cmd_qtinit (char *packet)
-{
- client_state &cs = get_client_state ();
- struct trace_state_variable *tsv, *prev, *next;
-
- /* Can't do this command without a pid attached. */
- if (current_thread == NULL)
- {
- write_enn (packet);
- return;
- }
-
- /* Make sure we don't try to read from a trace frame. */
- cs.current_traceframe = -1;
-
- stop_tracing ();
-
- trace_debug ("Initializing the trace");
-
- clear_installed_tracepoints ();
- clear_readonly_regions ();
-
- tracepoints = NULL;
- last_tracepoint = NULL;
-
- /* Clear out any leftover trace state variables. Ones with target
- defined getters should be kept however. */
- prev = NULL;
- tsv = trace_state_variables;
- while (tsv)
- {
- trace_debug ("Looking at var %d", tsv->number);
- if (tsv->getter == NULL)
- {
- next = tsv->next;
- if (prev)
- prev->next = next;
- else
- trace_state_variables = next;
- trace_debug ("Deleting var %d", tsv->number);
- free (tsv);
- tsv = next;
- }
- else
- {
- prev = tsv;
- tsv = tsv->next;
- }
- }
-
- clear_trace_buffer ();
- clear_inferior_trace_buffer ();
-
- write_ok (packet);
-}
-
-/* Unprobe the UST marker at ADDRESS. */
-
-static void
-unprobe_marker_at (CORE_ADDR address)
-{
- char cmd[IPA_CMD_BUF_SIZE];
-
- sprintf (cmd, "unprobe_marker_at:%s", paddress (address));
- run_inferior_command (cmd, strlen (cmd) + 1);
-}
-
-/* Restore the program to its pre-tracing state. This routine may be called
- in error situations, so it needs to be careful about only restoring
- from known-valid bits. */
-
-static void
-clear_installed_tracepoints (void)
-{
- struct tracepoint *tpoint;
- struct tracepoint *prev_stpoint;
-
- pause_all (1);
-
- prev_stpoint = NULL;
-
- /* Restore any bytes overwritten by tracepoints. */
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- {
- /* Catch the case where we might try to remove a tracepoint that
- was never actually installed. */
- if (tpoint->handle == NULL)
- {
- trace_debug ("Tracepoint %d at 0x%s was "
- "never installed, nothing to clear",
- tpoint->number, paddress (tpoint->address));
- continue;
- }
-
- switch (tpoint->type)
- {
- case trap_tracepoint:
- {
- struct breakpoint *bp
- = (struct breakpoint *) tpoint->handle;
-
- delete_breakpoint (bp);
- }
- break;
- case fast_tracepoint:
- {
- struct fast_tracepoint_jump *jump
- = (struct fast_tracepoint_jump *) tpoint->handle;
-
- delete_fast_tracepoint_jump (jump);
- }
- break;
- case static_tracepoint:
- if (prev_stpoint != NULL
- && prev_stpoint->address == tpoint->address)
- /* Nothing to do. We already unprobed a tracepoint set at
- this marker address (and there can only be one probe
- per marker). */
- ;
- else
- {
- unprobe_marker_at (tpoint->address);
- prev_stpoint = tpoint;
- }
- break;
- }
-
- tpoint->handle = NULL;
- }
-
- unpause_all (1);
-}
-
-/* Parse a packet that defines a tracepoint. */
-
-static void
-cmd_qtdp (char *own_buf)
-{
- int tppacket;
- /* Whether there is a trailing hyphen at the end of the QTDP packet. */
- int trail_hyphen = 0;
- ULONGEST num;
- ULONGEST addr;
- ULONGEST count;
- struct tracepoint *tpoint;
- const char *packet = own_buf;
-
- packet += strlen ("QTDP:");
-
- /* A hyphen at the beginning marks a packet specifying actions for a
- tracepoint already supplied. */
- tppacket = 1;
- if (*packet == '-')
- {
- tppacket = 0;
- ++packet;
- }
- packet = unpack_varlen_hex (packet, &num);
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &addr);
- ++packet; /* skip a colon */
-
- /* See if we already have this tracepoint. */
- tpoint = find_tracepoint (num, addr);
-
- if (tppacket)
- {
- /* Duplicate tracepoints are never allowed. */
- if (tpoint)
- {
- trace_debug ("Tracepoint error: tracepoint %d"
- " at 0x%s already exists",
- (int) num, paddress (addr));
- write_enn (own_buf);
- return;
- }
-
- tpoint = add_tracepoint (num, addr);
-
- tpoint->enabled = (*packet == 'E');
- ++packet; /* skip 'E' */
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &count);
- tpoint->step_count = count;
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &count);
- tpoint->pass_count = count;
- /* See if we have any of the additional optional fields. */
- while (*packet == ':')
- {
- ++packet;
- if (*packet == 'F')
- {
- tpoint->type = fast_tracepoint;
- ++packet;
- packet = unpack_varlen_hex (packet, &count);
- tpoint->orig_size = count;
- }
- else if (*packet == 'S')
- {
- tpoint->type = static_tracepoint;
- ++packet;
- }
- else if (*packet == 'X')
- {
- tpoint->cond = gdb_parse_agent_expr (&packet);
- }
- else if (*packet == '-')
- break;
- else if (*packet == '\0')
- break;
- else
- trace_debug ("Unknown optional tracepoint field");
- }
- if (*packet == '-')
- {
- trail_hyphen = 1;
- trace_debug ("Also has actions\n");
- }
-
- trace_debug ("Defined %stracepoint %d at 0x%s, "
- "enabled %d step %" PRIu64 " pass %" PRIu64,
- tpoint->type == fast_tracepoint ? "fast "
- : tpoint->type == static_tracepoint ? "static " : "",
- tpoint->number, paddress (tpoint->address), tpoint->enabled,
- tpoint->step_count, tpoint->pass_count);
- }
- else if (tpoint)
- add_tracepoint_action (tpoint, packet);
- else
- {
- trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
- (int) num, paddress (addr));
- write_enn (own_buf);
- return;
- }
-
- /* Install tracepoint during tracing only once for each tracepoint location.
- For each tracepoint loc, GDB may send multiple QTDP packets, and we can
- determine the last QTDP packet for one tracepoint location by checking
- trailing hyphen in QTDP packet. */
- if (tracing && !trail_hyphen)
- {
- struct tracepoint *tp = NULL;
-
- /* Pause all threads temporarily while we patch tracepoints. */
- pause_all (0);
-
- /* download_tracepoint will update global `tracepoints'
- list, so it is unsafe to leave threads in jump pad. */
- stabilize_threads ();
-
- /* Freeze threads. */
- pause_all (1);
-
-
- if (tpoint->type != trap_tracepoint)
- {
- /* Find another fast or static tracepoint at the same address. */
- for (tp = tracepoints; tp; tp = tp->next)
- {
- if (tp->address == tpoint->address && tp->type == tpoint->type
- && tp->number != tpoint->number)
- break;
- }
-
- /* TPOINT is installed at the same address as TP. */
- if (tp)
- {
- if (tpoint->type == fast_tracepoint)
- clone_fast_tracepoint (tpoint, tp);
- else if (tpoint->type == static_tracepoint)
- tpoint->handle = (void *) -1;
- }
- }
-
- if (use_agent && tpoint->type == fast_tracepoint
- && agent_capability_check (AGENT_CAPA_FAST_TRACE))
- {
- /* Download and install fast tracepoint by agent. */
- if (tracepoint_send_agent (tpoint) == 0)
- write_ok (own_buf);
- else
- {
- write_enn (own_buf);
- remove_tracepoint (tpoint);
- }
- }
- else
- {
- download_tracepoint (tpoint);
-
- if (tpoint->type == trap_tracepoint || tp == NULL)
- {
- install_tracepoint (tpoint, own_buf);
- if (strcmp (own_buf, "OK") != 0)
- remove_tracepoint (tpoint);
- }
- else
- write_ok (own_buf);
- }
-
- unpause_all (1);
- return;
- }
-
- write_ok (own_buf);
-}
-
-static void
-cmd_qtdpsrc (char *own_buf)
-{
- ULONGEST num, addr, start, slen;
- struct tracepoint *tpoint;
- const char *packet = own_buf;
- const char *saved;
- char *srctype, *src;
- size_t nbytes;
- struct source_string *last, *newlast;
-
- packet += strlen ("QTDPsrc:");
-
- packet = unpack_varlen_hex (packet, &num);
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &addr);
- ++packet; /* skip a colon */
-
- /* See if we already have this tracepoint. */
- tpoint = find_tracepoint (num, addr);
-
- if (!tpoint)
- {
- trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
- (int) num, paddress (addr));
- write_enn (own_buf);
- return;
- }
-
- saved = packet;
- packet = strchr (packet, ':');
- srctype = (char *) xmalloc (packet - saved + 1);
- memcpy (srctype, saved, packet - saved);
- srctype[packet - saved] = '\0';
- ++packet;
- packet = unpack_varlen_hex (packet, &start);
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &slen);
- ++packet; /* skip a colon */
- src = (char *) xmalloc (slen + 1);
- nbytes = hex2bin (packet, (gdb_byte *) src, strlen (packet) / 2);
- src[nbytes] = '\0';
-
- newlast = XNEW (struct source_string);
- newlast->type = srctype;
- newlast->str = src;
- newlast->next = NULL;
- /* Always add a source string to the end of the list;
- this keeps sequences of actions/commands in the right
- order. */
- if (tpoint->source_strings)
- {
- for (last = tpoint->source_strings; last->next; last = last->next)
- ;
- last->next = newlast;
- }
- else
- tpoint->source_strings = newlast;
-
- write_ok (own_buf);
-}
-
-static void
-cmd_qtdv (char *own_buf)
-{
- ULONGEST num, val, builtin;
- char *varname;
- size_t nbytes;
- struct trace_state_variable *tsv;
- const char *packet = own_buf;
-
- packet += strlen ("QTDV:");
-
- packet = unpack_varlen_hex (packet, &num);
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &val);
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &builtin);
- ++packet; /* skip a colon */
-
- nbytes = strlen (packet) / 2;
- varname = (char *) xmalloc (nbytes + 1);
- nbytes = hex2bin (packet, (gdb_byte *) varname, nbytes);
- varname[nbytes] = '\0';
-
- tsv = create_trace_state_variable (num, 1);
- tsv->initial_value = (LONGEST) val;
- tsv->name = varname;
-
- set_trace_state_variable_value (num, (LONGEST) val);
-
- write_ok (own_buf);
-}
-
-static void
-cmd_qtenable_disable (char *own_buf, int enable)
-{
- const char *packet = own_buf;
- ULONGEST num, addr;
- struct tracepoint *tp;
-
- packet += strlen (enable ? "QTEnable:" : "QTDisable:");
- packet = unpack_varlen_hex (packet, &num);
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &addr);
-
- tp = find_tracepoint (num, addr);
-
- if (tp)
- {
- if ((enable && tp->enabled) || (!enable && !tp->enabled))
- {
- trace_debug ("Tracepoint %d at 0x%s is already %s",
- (int) num, paddress (addr),
- enable ? "enabled" : "disabled");
- write_ok (own_buf);
- return;
- }
-
- trace_debug ("%s tracepoint %d at 0x%s",
- enable ? "Enabling" : "Disabling",
- (int) num, paddress (addr));
-
- tp->enabled = enable;
-
- if (tp->type == fast_tracepoint || tp->type == static_tracepoint)
- {
- int ret;
- int offset = offsetof (struct tracepoint, enabled);
- CORE_ADDR obj_addr = tp->obj_addr_on_target + offset;
-
- ret = prepare_to_access_memory ();
- if (ret)
- {
- trace_debug ("Failed to temporarily stop inferior threads");
- write_enn (own_buf);
- return;
- }
-
- ret = write_inferior_int8 (obj_addr, enable);
- done_accessing_memory ();
-
- if (ret)
- {
- trace_debug ("Cannot write enabled flag into "
- "inferior process memory");
- write_enn (own_buf);
- return;
- }
- }
-
- write_ok (own_buf);
- }
- else
- {
- trace_debug ("Tracepoint %d at 0x%s not found",
- (int) num, paddress (addr));
- write_enn (own_buf);
- }
-}
-
-static void
-cmd_qtv (char *own_buf)
-{
- client_state &cs = get_client_state ();
- ULONGEST num;
- LONGEST val = 0;
- int err;
- char *packet = own_buf;
-
- packet += strlen ("qTV:");
- unpack_varlen_hex (packet, &num);
-
- if (cs.current_traceframe >= 0)
- {
- err = traceframe_read_tsv ((int) num, &val);
- if (err)
- {
- strcpy (own_buf, "U");
- return;
- }
- }
- /* Only make tsv's be undefined before the first trace run. After a
- trace run is over, the user might want to see the last value of
- the tsv, and it might not be available in a traceframe. */
- else if (!tracing && strcmp (tracing_stop_reason, "tnotrun") == 0)
- {
- strcpy (own_buf, "U");
- return;
- }
- else
- val = get_trace_state_variable_value (num);
-
- sprintf (own_buf, "V%s", phex_nz (val, 0));
-}
-
-/* Clear out the list of readonly regions. */
-
-static void
-clear_readonly_regions (void)
-{
- struct readonly_region *roreg;
-
- while (readonly_regions)
- {
- roreg = readonly_regions;
- readonly_regions = readonly_regions->next;
- free (roreg);
- }
-}
-
-/* Parse the collection of address ranges whose contents GDB believes
- to be unchanging and so can be read directly from target memory
- even while looking at a traceframe. */
-
-static void
-cmd_qtro (char *own_buf)
-{
- ULONGEST start, end;
- struct readonly_region *roreg;
- const char *packet = own_buf;
-
- trace_debug ("Want to mark readonly regions");
-
- clear_readonly_regions ();
-
- packet += strlen ("QTro");
-
- while (*packet == ':')
- {
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &start);
- ++packet; /* skip a comma */
- packet = unpack_varlen_hex (packet, &end);
-
- roreg = XNEW (struct readonly_region);
- roreg->start = start;
- roreg->end = end;
- roreg->next = readonly_regions;
- readonly_regions = roreg;
- trace_debug ("Added readonly region from 0x%s to 0x%s",
- paddress (roreg->start), paddress (roreg->end));
- }
-
- write_ok (own_buf);
-}
-
-/* Test to see if the given range is in our list of readonly ranges.
- We only test for being entirely within a range, GDB is not going to
- send a single memory packet that spans multiple regions. */
-
-int
-in_readonly_region (CORE_ADDR addr, ULONGEST length)
-{
- struct readonly_region *roreg;
-
- for (roreg = readonly_regions; roreg; roreg = roreg->next)
- if (roreg->start <= addr && (addr + length - 1) <= roreg->end)
- return 1;
-
- return 0;
-}
-
-static CORE_ADDR gdb_jump_pad_head;
-
-/* Return the address of the next free jump space. */
-
-static CORE_ADDR
-get_jump_space_head (void)
-{
- if (gdb_jump_pad_head == 0)
- {
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer,
- &gdb_jump_pad_head))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting jump_pad_buffer");
- }
- }
-
- return gdb_jump_pad_head;
-}
-
-/* Reserve USED bytes from the jump space. */
-
-static void
-claim_jump_space (ULONGEST used)
-{
- trace_debug ("claim_jump_space reserves %s bytes at %s",
- pulongest (used), paddress (gdb_jump_pad_head));
- gdb_jump_pad_head += used;
-}
-
-static CORE_ADDR trampoline_buffer_head = 0;
-static CORE_ADDR trampoline_buffer_tail;
-
-/* Reserve USED bytes from the trampoline buffer and return the
- address of the start of the reserved space in TRAMPOLINE. Returns
- non-zero if the space is successfully claimed. */
-
-int
-claim_trampoline_space (ULONGEST used, CORE_ADDR *trampoline)
-{
- if (!trampoline_buffer_head)
- {
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
- &trampoline_buffer_tail))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting trampoline_buffer");
- }
-
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
- &trampoline_buffer_head))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting trampoline_buffer_end");
- }
- }
-
- /* Start claiming space from the top of the trampoline space. If
- the space is located at the bottom of the virtual address space,
- this reduces the possibility that corruption will occur if a null
- pointer is used to write to memory. */
- if (trampoline_buffer_head - trampoline_buffer_tail < used)
- {
- trace_debug ("claim_trampoline_space failed to reserve %s bytes",
- pulongest (used));
- return 0;
- }
-
- trampoline_buffer_head -= used;
-
- trace_debug ("claim_trampoline_space reserves %s bytes at %s",
- pulongest (used), paddress (trampoline_buffer_head));
-
- *trampoline = trampoline_buffer_head;
- return 1;
-}
-
-/* Returns non-zero if there is space allocated for use in trampolines
- for fast tracepoints. */
-
-int
-have_fast_tracepoint_trampoline_buffer (char *buf)
-{
- CORE_ADDR trampoline_end, errbuf;
-
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
- &trampoline_end))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting trampoline_buffer_end");
- }
-
- if (buf)
- {
- buf[0] = '\0';
- strcpy (buf, "was claiming");
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_error,
- &errbuf))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting errbuf");
- }
-
- read_inferior_memory (errbuf, (unsigned char *) buf, 100);
- }
-
- return trampoline_end != 0;
-}
-
-/* Ask the IPA to probe the marker at ADDRESS. Returns -1 if running
- the command fails, or 0 otherwise. If the command ran
- successfully, but probing the marker failed, ERROUT will be filled
- with the error to reply to GDB, and -1 is also returned. This
- allows directly passing IPA errors to GDB. */
-
-static int
-probe_marker_at (CORE_ADDR address, char *errout)
-{
- char cmd[IPA_CMD_BUF_SIZE];
- int err;
-
- sprintf (cmd, "probe_marker_at:%s", paddress (address));
- err = run_inferior_command (cmd, strlen (cmd) + 1);
-
- if (err == 0)
- {
- if (*cmd == 'E')
- {
- strcpy (errout, cmd);
- return -1;
- }
- }
-
- return err;
-}
-
-static void
-clone_fast_tracepoint (struct tracepoint *to, const struct tracepoint *from)
-{
- to->jump_pad = from->jump_pad;
- to->jump_pad_end = from->jump_pad_end;
- to->trampoline = from->trampoline;
- to->trampoline_end = from->trampoline_end;
- to->adjusted_insn_addr = from->adjusted_insn_addr;
- to->adjusted_insn_addr_end = from->adjusted_insn_addr_end;
- to->handle = from->handle;
-
- gdb_assert (from->handle);
- inc_ref_fast_tracepoint_jump ((struct fast_tracepoint_jump *) from->handle);
-}
-
-#define MAX_JUMP_SIZE 20
-
-/* Install fast tracepoint. Return 0 if successful, otherwise return
- non-zero. */
-
-static int
-install_fast_tracepoint (struct tracepoint *tpoint, char *errbuf)
-{
- CORE_ADDR jentry, jump_entry;
- CORE_ADDR trampoline;
- CORE_ADDR collect;
- ULONGEST trampoline_size;
- int err = 0;
- /* The jump to the jump pad of the last fast tracepoint
- installed. */
- unsigned char fjump[MAX_JUMP_SIZE];
- ULONGEST fjump_size;
-
- if (tpoint->orig_size < target_get_min_fast_tracepoint_insn_len ())
- {
- trace_debug ("Requested a fast tracepoint on an instruction "
- "that is of less than the minimum length.");
- return 0;
- }
-
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_collect_ptr,
- &collect))
- {
- error ("error extracting gdb_collect_ptr");
- return 1;
- }
-
- jentry = jump_entry = get_jump_space_head ();
-
- trampoline = 0;
- trampoline_size = 0;
-
- /* Install the jump pad. */
- err = install_fast_tracepoint_jump_pad (tpoint->obj_addr_on_target,
- tpoint->address,
- collect,
- ipa_sym_addrs.addr_collecting,
- tpoint->orig_size,
- &jentry,
- &trampoline, &trampoline_size,
- fjump, &fjump_size,
- &tpoint->adjusted_insn_addr,
- &tpoint->adjusted_insn_addr_end,
- errbuf);
-
- if (err)
- return 1;
-
- /* Wire it in. */
- tpoint->handle = set_fast_tracepoint_jump (tpoint->address, fjump,
- fjump_size);
-
- if (tpoint->handle != NULL)
- {
- tpoint->jump_pad = jump_entry;
- tpoint->jump_pad_end = jentry;
- tpoint->trampoline = trampoline;
- tpoint->trampoline_end = trampoline + trampoline_size;
-
- /* Pad to 8-byte alignment. */
- jentry = ((jentry + 7) & ~0x7);
- claim_jump_space (jentry - jump_entry);
- }
-
- return 0;
-}
-
-
-/* Install tracepoint TPOINT, and write reply message in OWN_BUF. */
-
-static void
-install_tracepoint (struct tracepoint *tpoint, char *own_buf)
-{
- tpoint->handle = NULL;
- *own_buf = '\0';
-
- if (tpoint->type == trap_tracepoint)
- {
- /* Tracepoints are installed as memory breakpoints. Just go
- ahead and install the trap. The breakpoints module
- handles duplicated breakpoints, and the memory read
- routine handles un-patching traps from memory reads. */
- tpoint->handle = set_breakpoint_at (tpoint->address,
- tracepoint_handler);
- }
- else if (tpoint->type == fast_tracepoint || tpoint->type == static_tracepoint)
- {
- if (!agent_loaded_p ())
- {
- trace_debug ("Requested a %s tracepoint, but fast "
- "tracepoints aren't supported.",
- tpoint->type == static_tracepoint ? "static" : "fast");
- write_e_ipa_not_loaded (own_buf);
- return;
- }
- if (tpoint->type == static_tracepoint
- && !in_process_agent_supports_ust ())
- {
- trace_debug ("Requested a static tracepoint, but static "
- "tracepoints are not supported.");
- write_e_ust_not_loaded (own_buf);
- return;
- }
-
- if (tpoint->type == fast_tracepoint)
- install_fast_tracepoint (tpoint, own_buf);
- else
- {
- if (probe_marker_at (tpoint->address, own_buf) == 0)
- tpoint->handle = (void *) -1;
- }
-
- }
- else
- internal_error (__FILE__, __LINE__, "Unknown tracepoint type");
-
- if (tpoint->handle == NULL)
- {
- if (*own_buf == '\0')
- write_enn (own_buf);
- }
- else
- write_ok (own_buf);
-}
-
-static void download_tracepoint_1 (struct tracepoint *tpoint);
-
-static void
-cmd_qtstart (char *packet)
-{
- struct tracepoint *tpoint, *prev_ftpoint, *prev_stpoint;
- CORE_ADDR tpptr = 0, prev_tpptr = 0;
-
- trace_debug ("Starting the trace");
-
- /* Pause all threads temporarily while we patch tracepoints. */
- pause_all (0);
-
- /* Get threads out of jump pads. Safe to do here, since this is a
- top level command. And, required to do here, since we're
- deleting/rewriting jump pads. */
-
- stabilize_threads ();
-
- /* Freeze threads. */
- pause_all (1);
-
- /* Sync the fast tracepoints list in the inferior ftlib. */
- if (agent_loaded_p ())
- download_trace_state_variables ();
-
- /* No previous fast tpoint yet. */
- prev_ftpoint = NULL;
-
- /* No previous static tpoint yet. */
- prev_stpoint = NULL;
-
- *packet = '\0';
-
- if (agent_loaded_p ())
- {
- /* Tell IPA about the correct tdesc. */
- if (write_inferior_integer (ipa_sym_addrs.addr_ipa_tdesc_idx,
- target_get_ipa_tdesc_idx ()))
- error ("Error setting ipa_tdesc_idx variable in lib");
- }
-
- /* Start out empty. */
- if (agent_loaded_p ())
- write_inferior_data_pointer (ipa_sym_addrs.addr_tracepoints, 0);
-
- /* Download and install tracepoints. */
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- {
- /* Ensure all the hit counts start at zero. */
- tpoint->hit_count = 0;
- tpoint->traceframe_usage = 0;
-
- if (tpoint->type == trap_tracepoint)
- {
- /* Tracepoints are installed as memory breakpoints. Just go
- ahead and install the trap. The breakpoints module
- handles duplicated breakpoints, and the memory read
- routine handles un-patching traps from memory reads. */
- tpoint->handle = set_breakpoint_at (tpoint->address,
- tracepoint_handler);
- }
- else if (tpoint->type == fast_tracepoint
- || tpoint->type == static_tracepoint)
- {
- if (maybe_write_ipa_not_loaded (packet))
- {
- trace_debug ("Requested a %s tracepoint, but fast "
- "tracepoints aren't supported.",
- tpoint->type == static_tracepoint
- ? "static" : "fast");
- break;
- }
-
- if (tpoint->type == fast_tracepoint)
- {
- int use_agent_p
- = use_agent && agent_capability_check (AGENT_CAPA_FAST_TRACE);
-
- if (prev_ftpoint != NULL
- && prev_ftpoint->address == tpoint->address)
- {
- if (use_agent_p)
- tracepoint_send_agent (tpoint);
- else
- download_tracepoint_1 (tpoint);
-
- clone_fast_tracepoint (tpoint, prev_ftpoint);
- }
- else
- {
- /* Tracepoint is installed successfully? */
- int installed = 0;
-
- /* Download and install fast tracepoint by agent. */
- if (use_agent_p)
- installed = !tracepoint_send_agent (tpoint);
- else
- {
- download_tracepoint_1 (tpoint);
- installed = !install_fast_tracepoint (tpoint, packet);
- }
-
- if (installed)
- prev_ftpoint = tpoint;
- }
- }
- else
- {
- if (!in_process_agent_supports_ust ())
- {
- trace_debug ("Requested a static tracepoint, but static "
- "tracepoints are not supported.");
- break;
- }
-
- download_tracepoint_1 (tpoint);
- /* Can only probe a given marker once. */
- if (prev_stpoint != NULL
- && prev_stpoint->address == tpoint->address)
- tpoint->handle = (void *) -1;
- else
- {
- if (probe_marker_at (tpoint->address, packet) == 0)
- {
- tpoint->handle = (void *) -1;
-
- /* So that we can handle multiple static tracepoints
- at the same address easily. */
- prev_stpoint = tpoint;
- }
- }
- }
-
- prev_tpptr = tpptr;
- tpptr = tpoint->obj_addr_on_target;
-
- if (tpoint == tracepoints)
- /* First object in list, set the head pointer in the
- inferior. */
- write_inferior_data_pointer (ipa_sym_addrs.addr_tracepoints, tpptr);
- else
- write_inferior_data_pointer (prev_tpptr
- + offsetof (struct tracepoint, next),
- tpptr);
- }
-
- /* Any failure in the inner loop is sufficient cause to give
- up. */
- if (tpoint->handle == NULL)
- break;
- }
-
- /* Any error in tracepoint insertion is unacceptable; better to
- address the problem now, than end up with a useless or misleading
- trace run. */
- if (tpoint != NULL)
- {
- clear_installed_tracepoints ();
- if (*packet == '\0')
- write_enn (packet);
- unpause_all (1);
- return;
- }
-
- stopping_tracepoint = NULL;
- trace_buffer_is_full = 0;
- expr_eval_result = expr_eval_no_error;
- error_tracepoint = NULL;
- tracing_start_time = get_timestamp ();
-
- /* Tracing is now active, hits will now start being logged. */
- tracing = 1;
-
- if (agent_loaded_p ())
- {
- if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 1))
- {
- internal_error (__FILE__, __LINE__,
- "Error setting tracing variable in lib");
- }
-
- if (write_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint,
- 0))
- {
- internal_error (__FILE__, __LINE__,
- "Error clearing stopping_tracepoint variable"
- " in lib");
- }
-
- if (write_inferior_integer (ipa_sym_addrs.addr_trace_buffer_is_full, 0))
- {
- internal_error (__FILE__, __LINE__,
- "Error clearing trace_buffer_is_full variable"
- " in lib");
- }
-
- stop_tracing_bkpt = set_breakpoint_at (ipa_sym_addrs.addr_stop_tracing,
- stop_tracing_handler);
- if (stop_tracing_bkpt == NULL)
- error ("Error setting stop_tracing breakpoint");
-
- flush_trace_buffer_bkpt
- = set_breakpoint_at (ipa_sym_addrs.addr_flush_trace_buffer,
- flush_trace_buffer_handler);
- if (flush_trace_buffer_bkpt == NULL)
- error ("Error setting flush_trace_buffer breakpoint");
- }
-
- unpause_all (1);
-
- write_ok (packet);
-}
-
-/* End a tracing run, filling in a stop reason to report back to GDB,
- and removing the tracepoints from the code. */
-
-void
-stop_tracing (void)
-{
- if (!tracing)
- {
- trace_debug ("Tracing is already off, ignoring");
- return;
- }
-
- trace_debug ("Stopping the trace");
-
- /* Pause all threads before removing fast jumps from memory,
- breakpoints, and touching IPA state variables (inferior memory).
- Some thread may hit the internal tracing breakpoints, or be
- collecting this moment, but that's ok, we don't release the
- tpoint object's memory or the jump pads here (we only do that
- when we're sure we can move all threads out of the jump pads).
- We can't now, since we may be getting here due to the inferior
- agent calling us. */
- pause_all (1);
-
- /* Stop logging. Tracepoints can still be hit, but they will not be
- recorded. */
- tracing = 0;
- if (agent_loaded_p ())
- {
- if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 0))
- {
- internal_error (__FILE__, __LINE__,
- "Error clearing tracing variable in lib");
- }
- }
-
- tracing_stop_time = get_timestamp ();
- tracing_stop_reason = "t???";
- tracing_stop_tpnum = 0;
- if (stopping_tracepoint)
- {
- trace_debug ("Stopping the trace because "
- "tracepoint %d was hit %" PRIu64 " times",
- stopping_tracepoint->number,
- stopping_tracepoint->pass_count);
- tracing_stop_reason = "tpasscount";
- tracing_stop_tpnum = stopping_tracepoint->number;
- }
- else if (trace_buffer_is_full)
- {
- trace_debug ("Stopping the trace because the trace buffer is full");
- tracing_stop_reason = "tfull";
- }
- else if (expr_eval_result != expr_eval_no_error)
- {
- trace_debug ("Stopping the trace because of an expression eval error");
- tracing_stop_reason = eval_result_names[expr_eval_result];
- tracing_stop_tpnum = error_tracepoint->number;
- }
-#ifndef IN_PROCESS_AGENT
- else if (!gdb_connected ())
- {
- trace_debug ("Stopping the trace because GDB disconnected");
- tracing_stop_reason = "tdisconnected";
- }
-#endif
- else
- {
- trace_debug ("Stopping the trace because of a tstop command");
- tracing_stop_reason = "tstop";
- }
-
- stopping_tracepoint = NULL;
- error_tracepoint = NULL;
-
- /* Clear out the tracepoints. */
- clear_installed_tracepoints ();
-
- if (agent_loaded_p ())
- {
- /* Pull in fast tracepoint trace frames from the inferior lib
- buffer into our buffer, even if our buffer is already full,
- because we want to present the full number of created frames
- in addition to what fit in the trace buffer. */
- upload_fast_traceframes ();
- }
-
- if (stop_tracing_bkpt != NULL)
- {
- delete_breakpoint (stop_tracing_bkpt);
- stop_tracing_bkpt = NULL;
- }
-
- if (flush_trace_buffer_bkpt != NULL)
- {
- delete_breakpoint (flush_trace_buffer_bkpt);
- flush_trace_buffer_bkpt = NULL;
- }
-
- unpause_all (1);
-}
-
-static int
-stop_tracing_handler (CORE_ADDR addr)
-{
- trace_debug ("lib hit stop_tracing");
-
- /* Don't actually handle it here. When we stop tracing we remove
- breakpoints from the inferior, and that is not allowed in a
- breakpoint handler (as the caller is walking the breakpoint
- list). */
- return 0;
-}
-
-static int
-flush_trace_buffer_handler (CORE_ADDR addr)
-{
- trace_debug ("lib hit flush_trace_buffer");
- return 0;
-}
-
-static void
-cmd_qtstop (char *packet)
-{
- stop_tracing ();
- write_ok (packet);
-}
-
-static void
-cmd_qtdisconnected (char *own_buf)
-{
- ULONGEST setting;
- char *packet = own_buf;
-
- packet += strlen ("QTDisconnected:");
-
- unpack_varlen_hex (packet, &setting);
-
- write_ok (own_buf);
-
- disconnected_tracing = setting;
-}
-
-static void
-cmd_qtframe (char *own_buf)
-{
- client_state &cs = get_client_state ();
- ULONGEST frame, pc, lo, hi, num;
- int tfnum, tpnum;
- struct traceframe *tframe;
- const char *packet = own_buf;
-
- packet += strlen ("QTFrame:");
-
- if (startswith (packet, "pc:"))
- {
- packet += strlen ("pc:");
- unpack_varlen_hex (packet, &pc);
- trace_debug ("Want to find next traceframe at pc=0x%s", paddress (pc));
- tframe = find_next_traceframe_in_range (pc, pc, 1, &tfnum);
- }
- else if (startswith (packet, "range:"))
- {
- packet += strlen ("range:");
- packet = unpack_varlen_hex (packet, &lo);
- ++packet;
- unpack_varlen_hex (packet, &hi);
- trace_debug ("Want to find next traceframe in the range 0x%s to 0x%s",
- paddress (lo), paddress (hi));
- tframe = find_next_traceframe_in_range (lo, hi, 1, &tfnum);
- }
- else if (startswith (packet, "outside:"))
- {
- packet += strlen ("outside:");
- packet = unpack_varlen_hex (packet, &lo);
- ++packet;
- unpack_varlen_hex (packet, &hi);
- trace_debug ("Want to find next traceframe "
- "outside the range 0x%s to 0x%s",
- paddress (lo), paddress (hi));
- tframe = find_next_traceframe_in_range (lo, hi, 0, &tfnum);
- }
- else if (startswith (packet, "tdp:"))
- {
- packet += strlen ("tdp:");
- unpack_varlen_hex (packet, &num);
- tpnum = (int) num;
- trace_debug ("Want to find next traceframe for tracepoint %d", tpnum);
- tframe = find_next_traceframe_by_tracepoint (tpnum, &tfnum);
- }
- else
- {
- unpack_varlen_hex (packet, &frame);
- tfnum = (int) frame;
- if (tfnum == -1)
- {
- trace_debug ("Want to stop looking at traceframes");
- cs.current_traceframe = -1;
- write_ok (own_buf);
- return;
- }
- trace_debug ("Want to look at traceframe %d", tfnum);
- tframe = find_traceframe (tfnum);
- }
-
- if (tframe)
- {
- cs.current_traceframe = tfnum;
- sprintf (own_buf, "F%xT%x", tfnum, tframe->tpnum);
- }
- else
- sprintf (own_buf, "F-1");
-}
-
-static void
-cmd_qtstatus (char *packet)
-{
- char *stop_reason_rsp = NULL;
- char *buf1, *buf2, *buf3;
- const char *str;
- int slen;
-
- /* Translate the plain text of the notes back into hex for
- transmission. */
-
- str = (tracing_user_name ? tracing_user_name : "");
- slen = strlen (str);
- buf1 = (char *) alloca (slen * 2 + 1);
- bin2hex ((gdb_byte *) str, buf1, slen);
-
- str = (tracing_notes ? tracing_notes : "");
- slen = strlen (str);
- buf2 = (char *) alloca (slen * 2 + 1);
- bin2hex ((gdb_byte *) str, buf2, slen);
-
- str = (tracing_stop_note ? tracing_stop_note : "");
- slen = strlen (str);
- buf3 = (char *) alloca (slen * 2 + 1);
- bin2hex ((gdb_byte *) str, buf3, slen);
-
- trace_debug ("Returning trace status as %d, stop reason %s",
- tracing, tracing_stop_reason);
-
- if (agent_loaded_p ())
- {
- pause_all (1);
-
- upload_fast_traceframes ();
-
- unpause_all (1);
- }
-
- stop_reason_rsp = (char *) tracing_stop_reason;
-
- /* The user visible error string in terror needs to be hex encoded.
- We leave it as plain string in `tracing_stop_reason' to ease
- debugging. */
- if (startswith (stop_reason_rsp, "terror:"))
- {
- const char *result_name;
- int hexstr_len;
- char *p;
-
- result_name = stop_reason_rsp + strlen ("terror:");
- hexstr_len = strlen (result_name) * 2;
- p = stop_reason_rsp
- = (char *) alloca (strlen ("terror:") + hexstr_len + 1);
- strcpy (p, "terror:");
- p += strlen (p);
- bin2hex ((gdb_byte *) result_name, p, strlen (result_name));
- }
-
- /* If this was a forced stop, include any stop note that was supplied. */
- if (strcmp (stop_reason_rsp, "tstop") == 0)
- {
- stop_reason_rsp = (char *) alloca (strlen ("tstop:") + strlen (buf3) + 1);
- strcpy (stop_reason_rsp, "tstop:");
- strcat (stop_reason_rsp, buf3);
- }
-
- sprintf (packet,
- "T%d;"
- "%s:%x;"
- "tframes:%x;tcreated:%x;"
- "tfree:%x;tsize:%s;"
- "circular:%d;"
- "disconn:%d;"
- "starttime:%s;stoptime:%s;"
- "username:%s;notes:%s:",
- tracing ? 1 : 0,
- stop_reason_rsp, tracing_stop_tpnum,
- traceframe_count, traceframes_created,
- free_space (), phex_nz (trace_buffer_hi - trace_buffer_lo, 0),
- circular_trace_buffer,
- disconnected_tracing,
- phex_nz (tracing_start_time, sizeof (tracing_start_time)),
- phex_nz (tracing_stop_time, sizeof (tracing_stop_time)),
- buf1, buf2);
-}
-
-static void
-cmd_qtp (char *own_buf)
-{
- ULONGEST num, addr;
- struct tracepoint *tpoint;
- const char *packet = own_buf;
-
- packet += strlen ("qTP:");
-
- packet = unpack_varlen_hex (packet, &num);
- ++packet; /* skip a colon */
- packet = unpack_varlen_hex (packet, &addr);
-
- /* See if we already have this tracepoint. */
- tpoint = find_tracepoint (num, addr);
-
- if (!tpoint)
- {
- trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
- (int) num, paddress (addr));
- write_enn (own_buf);
- return;
- }
-
- sprintf (own_buf, "V%" PRIu64 ":%" PRIu64 "", tpoint->hit_count,
- tpoint->traceframe_usage);
-}
-
-/* State variables to help return all the tracepoint bits. */
-static struct tracepoint *cur_tpoint;
-static unsigned int cur_action;
-static unsigned int cur_step_action;
-static struct source_string *cur_source_string;
-static struct trace_state_variable *cur_tsv;
-
-/* Compose a response that is an imitation of the syntax by which the
- tracepoint was originally downloaded. */
-
-static void
-response_tracepoint (char *packet, struct tracepoint *tpoint)
-{
- char *buf;
-
- sprintf (packet, "T%x:%s:%c:%" PRIx64 ":%" PRIx64, tpoint->number,
- paddress (tpoint->address),
- (tpoint->enabled ? 'E' : 'D'), tpoint->step_count,
- tpoint->pass_count);
- if (tpoint->type == fast_tracepoint)
- sprintf (packet + strlen (packet), ":F%x", tpoint->orig_size);
- else if (tpoint->type == static_tracepoint)
- sprintf (packet + strlen (packet), ":S");
-
- if (tpoint->cond)
- {
- buf = gdb_unparse_agent_expr (tpoint->cond);
- sprintf (packet + strlen (packet), ":X%x,%s",
- tpoint->cond->length, buf);
- free (buf);
- }
-}
-
-/* Compose a response that is an imitation of the syntax by which the
- tracepoint action was originally downloaded (with the difference
- that due to the way we store the actions, this will output a packet
- per action, while GDB could have combined more than one action
- per-packet. */
-
-static void
-response_action (char *packet, struct tracepoint *tpoint,
- char *taction, int step)
-{
- sprintf (packet, "%c%x:%s:%s",
- (step ? 'S' : 'A'), tpoint->number, paddress (tpoint->address),
- taction);
-}
-
-/* Compose a response that is an imitation of the syntax by which the
- tracepoint source piece was originally downloaded. */
-
-static void
-response_source (char *packet,
- struct tracepoint *tpoint, struct source_string *src)
-{
- char *buf;
- int len;
-
- len = strlen (src->str);
- buf = (char *) alloca (len * 2 + 1);
- bin2hex ((gdb_byte *) src->str, buf, len);
-
- sprintf (packet, "Z%x:%s:%s:%x:%x:%s",
- tpoint->number, paddress (tpoint->address),
- src->type, 0, len, buf);
-}
-
-/* Return the first piece of tracepoint definition, and initialize the
- state machine that will iterate through all the tracepoint
- bits. */
-
-static void
-cmd_qtfp (char *packet)
-{
- trace_debug ("Returning first tracepoint definition piece");
-
- cur_tpoint = tracepoints;
- cur_action = cur_step_action = 0;
- cur_source_string = NULL;
-
- if (cur_tpoint)
- response_tracepoint (packet, cur_tpoint);
- else
- strcpy (packet, "l");
-}
-
-/* Return additional pieces of tracepoint definition. Each action and
- stepping action must go into its own packet, because of packet size
- limits, and so we use state variables to deliver one piece at a
- time. */
-
-static void
-cmd_qtsp (char *packet)
-{
- trace_debug ("Returning subsequent tracepoint definition piece");
-
- if (!cur_tpoint)
- {
- /* This case would normally never occur, but be prepared for
- GDB misbehavior. */
- strcpy (packet, "l");
- }
- else if (cur_action < cur_tpoint->numactions)
- {
- response_action (packet, cur_tpoint,
- cur_tpoint->actions_str[cur_action], 0);
- ++cur_action;
- }
- else if (cur_step_action < cur_tpoint->num_step_actions)
- {
- response_action (packet, cur_tpoint,
- cur_tpoint->step_actions_str[cur_step_action], 1);
- ++cur_step_action;
- }
- else if ((cur_source_string
- ? cur_source_string->next
- : cur_tpoint->source_strings))
- {
- if (cur_source_string)
- cur_source_string = cur_source_string->next;
- else
- cur_source_string = cur_tpoint->source_strings;
- response_source (packet, cur_tpoint, cur_source_string);
- }
- else
- {
- cur_tpoint = cur_tpoint->next;
- cur_action = cur_step_action = 0;
- cur_source_string = NULL;
- if (cur_tpoint)
- response_tracepoint (packet, cur_tpoint);
- else
- strcpy (packet, "l");
- }
-}
-
-/* Compose a response that is an imitation of the syntax by which the
- trace state variable was originally downloaded. */
-
-static void
-response_tsv (char *packet, struct trace_state_variable *tsv)
-{
- char *buf = (char *) "";
- int namelen;
-
- if (tsv->name)
- {
- namelen = strlen (tsv->name);
- buf = (char *) alloca (namelen * 2 + 1);
- bin2hex ((gdb_byte *) tsv->name, buf, namelen);
- }
-
- sprintf (packet, "%x:%s:%x:%s", tsv->number, phex_nz (tsv->initial_value, 0),
- tsv->getter ? 1 : 0, buf);
-}
-
-/* Return the first trace state variable definition, and initialize
- the state machine that will iterate through all the tsv bits. */
-
-static void
-cmd_qtfv (char *packet)
-{
- trace_debug ("Returning first trace state variable definition");
-
- cur_tsv = trace_state_variables;
-
- if (cur_tsv)
- response_tsv (packet, cur_tsv);
- else
- strcpy (packet, "l");
-}
-
-/* Return additional trace state variable definitions. */
-
-static void
-cmd_qtsv (char *packet)
-{
- trace_debug ("Returning additional trace state variable definition");
-
- if (cur_tsv)
- {
- cur_tsv = cur_tsv->next;
- if (cur_tsv)
- response_tsv (packet, cur_tsv);
- else
- strcpy (packet, "l");
- }
- else
- strcpy (packet, "l");
-}
-
-/* Return the first static tracepoint marker, and initialize the state
- machine that will iterate through all the static tracepoints
- markers. */
-
-static void
-cmd_qtfstm (char *packet)
-{
- if (!maybe_write_ipa_ust_not_loaded (packet))
- run_inferior_command (packet, strlen (packet) + 1);
-}
-
-/* Return additional static tracepoints markers. */
-
-static void
-cmd_qtsstm (char *packet)
-{
- if (!maybe_write_ipa_ust_not_loaded (packet))
- run_inferior_command (packet, strlen (packet) + 1);
-}
-
-/* Return the definition of the static tracepoint at a given address.
- Result packet is the same as qTsST's. */
-
-static void
-cmd_qtstmat (char *packet)
-{
- if (!maybe_write_ipa_ust_not_loaded (packet))
- run_inferior_command (packet, strlen (packet) + 1);
-}
-
-/* Sent the agent a command to close it. */
-
-void
-gdb_agent_about_to_close (int pid)
-{
- char buf[IPA_CMD_BUF_SIZE];
-
- if (!maybe_write_ipa_not_loaded (buf))
- {
- struct thread_info *saved_thread;
-
- saved_thread = current_thread;
-
- /* Find any thread which belongs to process PID. */
- current_thread = find_any_thread_of_pid (pid);
-
- strcpy (buf, "close");
-
- run_inferior_command (buf, strlen (buf) + 1);
-
- current_thread = saved_thread;
- }
-}
-
-/* Return the minimum instruction size needed for fast tracepoints as a
- hexadecimal number. */
-
-static void
-cmd_qtminftpilen (char *packet)
-{
- if (current_thread == NULL)
- {
- /* Indicate that the minimum length is currently unknown. */
- strcpy (packet, "0");
- return;
- }
-
- sprintf (packet, "%x", target_get_min_fast_tracepoint_insn_len ());
-}
-
-/* Respond to qTBuffer packet with a block of raw data from the trace
- buffer. GDB may ask for a lot, but we are allowed to reply with
- only as much as will fit within packet limits or whatever. */
-
-static void
-cmd_qtbuffer (char *own_buf)
-{
- ULONGEST offset, num, tot;
- unsigned char *tbp;
- const char *packet = own_buf;
-
- packet += strlen ("qTBuffer:");
-
- packet = unpack_varlen_hex (packet, &offset);
- ++packet; /* skip a comma */
- unpack_varlen_hex (packet, &num);
-
- trace_debug ("Want to get trace buffer, %d bytes at offset 0x%s",
- (int) num, phex_nz (offset, 0));
-
- tot = (trace_buffer_hi - trace_buffer_lo) - free_space ();
-
- /* If we're right at the end, reply specially that we're done. */
- if (offset == tot)
- {
- strcpy (own_buf, "l");
- return;
- }
-
- /* Object to any other out-of-bounds request. */
- if (offset > tot)
- {
- write_enn (own_buf);
- return;
- }
-
- /* Compute the pointer corresponding to the given offset, accounting
- for wraparound. */
- tbp = trace_buffer_start + offset;
- if (tbp >= trace_buffer_wrap)
- tbp -= (trace_buffer_wrap - trace_buffer_lo);
-
- /* Trim to the remaining bytes if we're close to the end. */
- if (num > tot - offset)
- num = tot - offset;
-
- /* Trim to available packet size. */
- if (num >= (PBUFSIZ - 16) / 2 )
- num = (PBUFSIZ - 16) / 2;
-
- bin2hex (tbp, own_buf, num);
-}
-
-static void
-cmd_bigqtbuffer_circular (char *own_buf)
-{
- ULONGEST val;
- char *packet = own_buf;
-
- packet += strlen ("QTBuffer:circular:");
-
- unpack_varlen_hex (packet, &val);
- circular_trace_buffer = val;
- trace_debug ("Trace buffer is now %s",
- circular_trace_buffer ? "circular" : "linear");
- write_ok (own_buf);
-}
-
-static void
-cmd_bigqtbuffer_size (char *own_buf)
-{
- ULONGEST val;
- LONGEST sval;
- char *packet = own_buf;
-
- /* Can't change the size during a tracing run. */
- if (tracing)
- {
- write_enn (own_buf);
- return;
- }
-
- packet += strlen ("QTBuffer:size:");
-
- /* -1 is sent as literal "-1". */
- if (strcmp (packet, "-1") == 0)
- sval = DEFAULT_TRACE_BUFFER_SIZE;
- else
- {
- unpack_varlen_hex (packet, &val);
- sval = (LONGEST) val;
- }
-
- init_trace_buffer (sval);
- trace_debug ("Trace buffer is now %s bytes",
- plongest (trace_buffer_size));
- write_ok (own_buf);
-}
-
-static void
-cmd_qtnotes (char *own_buf)
-{
- size_t nbytes;
- char *saved, *user, *notes, *stopnote;
- char *packet = own_buf;
-
- packet += strlen ("QTNotes:");
-
- while (*packet)
- {
- if (startswith (packet, "user:"))
- {
- packet += strlen ("user:");
- saved = packet;
- packet = strchr (packet, ';');
- nbytes = (packet - saved) / 2;
- user = (char *) xmalloc (nbytes + 1);
- nbytes = hex2bin (saved, (gdb_byte *) user, nbytes);
- user[nbytes] = '\0';
- ++packet; /* skip the semicolon */
- trace_debug ("User is '%s'", user);
- xfree (tracing_user_name);
- tracing_user_name = user;
- }
- else if (startswith (packet, "notes:"))
- {
- packet += strlen ("notes:");
- saved = packet;
- packet = strchr (packet, ';');
- nbytes = (packet - saved) / 2;
- notes = (char *) xmalloc (nbytes + 1);
- nbytes = hex2bin (saved, (gdb_byte *) notes, nbytes);
- notes[nbytes] = '\0';
- ++packet; /* skip the semicolon */
- trace_debug ("Notes is '%s'", notes);
- xfree (tracing_notes);
- tracing_notes = notes;
- }
- else if (startswith (packet, "tstop:"))
- {
- packet += strlen ("tstop:");
- saved = packet;
- packet = strchr (packet, ';');
- nbytes = (packet - saved) / 2;
- stopnote = (char *) xmalloc (nbytes + 1);
- nbytes = hex2bin (saved, (gdb_byte *) stopnote, nbytes);
- stopnote[nbytes] = '\0';
- ++packet; /* skip the semicolon */
- trace_debug ("tstop note is '%s'", stopnote);
- xfree (tracing_stop_note);
- tracing_stop_note = stopnote;
- }
- else
- break;
- }
-
- write_ok (own_buf);
-}
-
-int
-handle_tracepoint_general_set (char *packet)
-{
- if (strcmp ("QTinit", packet) == 0)
- {
- cmd_qtinit (packet);
- return 1;
- }
- else if (startswith (packet, "QTDP:"))
- {
- cmd_qtdp (packet);
- return 1;
- }
- else if (startswith (packet, "QTDPsrc:"))
- {
- cmd_qtdpsrc (packet);
- return 1;
- }
- else if (startswith (packet, "QTEnable:"))
- {
- cmd_qtenable_disable (packet, 1);
- return 1;
- }
- else if (startswith (packet, "QTDisable:"))
- {
- cmd_qtenable_disable (packet, 0);
- return 1;
- }
- else if (startswith (packet, "QTDV:"))
- {
- cmd_qtdv (packet);
- return 1;
- }
- else if (startswith (packet, "QTro:"))
- {
- cmd_qtro (packet);
- return 1;
- }
- else if (strcmp ("QTStart", packet) == 0)
- {
- cmd_qtstart (packet);
- return 1;
- }
- else if (strcmp ("QTStop", packet) == 0)
- {
- cmd_qtstop (packet);
- return 1;
- }
- else if (startswith (packet, "QTDisconnected:"))
- {
- cmd_qtdisconnected (packet);
- return 1;
- }
- else if (startswith (packet, "QTFrame:"))
- {
- cmd_qtframe (packet);
- return 1;
- }
- else if (startswith (packet, "QTBuffer:circular:"))
- {
- cmd_bigqtbuffer_circular (packet);
- return 1;
- }
- else if (startswith (packet, "QTBuffer:size:"))
- {
- cmd_bigqtbuffer_size (packet);
- return 1;
- }
- else if (startswith (packet, "QTNotes:"))
- {
- cmd_qtnotes (packet);
- return 1;
- }
-
- return 0;
-}
-
-int
-handle_tracepoint_query (char *packet)
-{
- if (strcmp ("qTStatus", packet) == 0)
- {
- cmd_qtstatus (packet);
- return 1;
- }
- else if (startswith (packet, "qTP:"))
- {
- cmd_qtp (packet);
- return 1;
- }
- else if (strcmp ("qTfP", packet) == 0)
- {
- cmd_qtfp (packet);
- return 1;
- }
- else if (strcmp ("qTsP", packet) == 0)
- {
- cmd_qtsp (packet);
- return 1;
- }
- else if (strcmp ("qTfV", packet) == 0)
- {
- cmd_qtfv (packet);
- return 1;
- }
- else if (strcmp ("qTsV", packet) == 0)
- {
- cmd_qtsv (packet);
- return 1;
- }
- else if (startswith (packet, "qTV:"))
- {
- cmd_qtv (packet);
- return 1;
- }
- else if (startswith (packet, "qTBuffer:"))
- {
- cmd_qtbuffer (packet);
- return 1;
- }
- else if (strcmp ("qTfSTM", packet) == 0)
- {
- cmd_qtfstm (packet);
- return 1;
- }
- else if (strcmp ("qTsSTM", packet) == 0)
- {
- cmd_qtsstm (packet);
- return 1;
- }
- else if (startswith (packet, "qTSTMat:"))
- {
- cmd_qtstmat (packet);
- return 1;
- }
- else if (strcmp ("qTMinFTPILen", packet) == 0)
- {
- cmd_qtminftpilen (packet);
- return 1;
- }
-
- return 0;
-}
-
-#endif
-#ifndef IN_PROCESS_AGENT
-
-/* Call this when thread TINFO has hit the tracepoint defined by
- TP_NUMBER and TP_ADDRESS, and that tracepoint has a while-stepping
- action. This adds a while-stepping collecting state item to the
- threads' collecting state list, so that we can keep track of
- multiple simultaneous while-stepping actions being collected by the
- same thread. This can happen in cases like:
-
- ff0001 INSN1 <-- TP1, while-stepping 10 collect $regs
- ff0002 INSN2
- ff0003 INSN3 <-- TP2, collect $regs
- ff0004 INSN4 <-- TP3, while-stepping 10 collect $regs
- ff0005 INSN5
-
- Notice that when instruction INSN5 is reached, the while-stepping
- actions of both TP1 and TP3 are still being collected, and that TP2
- had been collected meanwhile. The whole range of ff0001-ff0005
- should be single-stepped, due to at least TP1's while-stepping
- action covering the whole range. */
-
-static void
-add_while_stepping_state (struct thread_info *tinfo,
- int tp_number, CORE_ADDR tp_address)
-{
- struct wstep_state *wstep = XNEW (struct wstep_state);
-
- wstep->next = tinfo->while_stepping;
-
- wstep->tp_number = tp_number;
- wstep->tp_address = tp_address;
- wstep->current_step = 0;
-
- tinfo->while_stepping = wstep;
-}
-
-/* Release the while-stepping collecting state WSTEP. */
-
-static void
-release_while_stepping_state (struct wstep_state *wstep)
-{
- free (wstep);
-}
-
-/* Release all while-stepping collecting states currently associated
- with thread TINFO. */
-
-void
-release_while_stepping_state_list (struct thread_info *tinfo)
-{
- struct wstep_state *head;
-
- while (tinfo->while_stepping)
- {
- head = tinfo->while_stepping;
- tinfo->while_stepping = head->next;
- release_while_stepping_state (head);
- }
-}
-
-/* If TINFO was handling a 'while-stepping' action, the step has
- finished, so collect any step data needed, and check if any more
- steps are required. Return true if the thread was indeed
- collecting tracepoint data, false otherwise. */
-
-int
-tracepoint_finished_step (struct thread_info *tinfo, CORE_ADDR stop_pc)
-{
- struct tracepoint *tpoint;
- struct wstep_state *wstep;
- struct wstep_state **wstep_link;
- struct trap_tracepoint_ctx ctx;
-
- /* Pull in fast tracepoint trace frames from the inferior lib buffer into
- our buffer. */
- if (agent_loaded_p ())
- upload_fast_traceframes ();
-
- /* Check if we were indeed collecting data for one of more
- tracepoints with a 'while-stepping' count. */
- if (tinfo->while_stepping == NULL)
- return 0;
-
- if (!tracing)
- {
- /* We're not even tracing anymore. Stop this thread from
- collecting. */
- release_while_stepping_state_list (tinfo);
-
- /* The thread had stopped due to a single-step request indeed
- explained by a tracepoint. */
- return 1;
- }
-
- wstep = tinfo->while_stepping;
- wstep_link = &tinfo->while_stepping;
-
- trace_debug ("Thread %s finished a single-step for tracepoint %d at 0x%s",
- target_pid_to_str (tinfo->id),
- wstep->tp_number, paddress (wstep->tp_address));
-
- ctx.base.type = trap_tracepoint;
- ctx.regcache = get_thread_regcache (tinfo, 1);
-
- while (wstep != NULL)
- {
- tpoint = find_tracepoint (wstep->tp_number, wstep->tp_address);
- if (tpoint == NULL)
- {
- trace_debug ("NO TRACEPOINT %d at 0x%s FOR THREAD %s!",
- wstep->tp_number, paddress (wstep->tp_address),
- target_pid_to_str (tinfo->id));
-
- /* Unlink. */
- *wstep_link = wstep->next;
- release_while_stepping_state (wstep);
- wstep = *wstep_link;
- continue;
- }
-
- /* We've just finished one step. */
- ++wstep->current_step;
-
- /* Collect data. */
- collect_data_at_step ((struct tracepoint_hit_ctx *) &ctx,
- stop_pc, tpoint, wstep->current_step);
-
- if (wstep->current_step >= tpoint->step_count)
- {
- /* The requested numbers of steps have occurred. */
- trace_debug ("Thread %s done stepping for tracepoint %d at 0x%s",
- target_pid_to_str (tinfo->id),
- wstep->tp_number, paddress (wstep->tp_address));
-
- /* Unlink the wstep. */
- *wstep_link = wstep->next;
- release_while_stepping_state (wstep);
- wstep = *wstep_link;
-
- /* Only check the hit count now, which ensure that we do all
- our stepping before stopping the run. */
- if (tpoint->pass_count > 0
- && tpoint->hit_count >= tpoint->pass_count
- && stopping_tracepoint == NULL)
- stopping_tracepoint = tpoint;
- }
- else
- {
- /* Keep single-stepping until the requested numbers of steps
- have occurred. */
- wstep_link = &wstep->next;
- wstep = *wstep_link;
- }
-
- if (stopping_tracepoint
- || trace_buffer_is_full
- || expr_eval_result != expr_eval_no_error)
- {
- stop_tracing ();
- break;
- }
- }
-
- return 1;
-}
-
-/* Handle any internal tracing control breakpoint hits. That means,
- pull traceframes from the IPA to our buffer, and syncing both
- tracing agents when the IPA's tracing stops for some reason. */
-
-int
-handle_tracepoint_bkpts (struct thread_info *tinfo, CORE_ADDR stop_pc)
-{
- /* Pull in fast tracepoint trace frames from the inferior in-process
- agent's buffer into our buffer. */
-
- if (!agent_loaded_p ())
- return 0;
-
- upload_fast_traceframes ();
-
- /* Check if the in-process agent had decided we should stop
- tracing. */
- if (stop_pc == ipa_sym_addrs.addr_stop_tracing)
- {
- int ipa_trace_buffer_is_full;
- CORE_ADDR ipa_stopping_tracepoint;
- int ipa_expr_eval_result;
- CORE_ADDR ipa_error_tracepoint;
-
- trace_debug ("lib stopped at stop_tracing");
-
- read_inferior_integer (ipa_sym_addrs.addr_trace_buffer_is_full,
- &ipa_trace_buffer_is_full);
-
- read_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint,
- &ipa_stopping_tracepoint);
- write_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint, 0);
-
- read_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint,
- &ipa_error_tracepoint);
- write_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint, 0);
-
- read_inferior_integer (ipa_sym_addrs.addr_expr_eval_result,
- &ipa_expr_eval_result);
- write_inferior_integer (ipa_sym_addrs.addr_expr_eval_result, 0);
-
- trace_debug ("lib: trace_buffer_is_full: %d, "
- "stopping_tracepoint: %s, "
- "ipa_expr_eval_result: %d, "
- "error_tracepoint: %s, ",
- ipa_trace_buffer_is_full,
- paddress (ipa_stopping_tracepoint),
- ipa_expr_eval_result,
- paddress (ipa_error_tracepoint));
-
- if (debug_threads)
- {
- if (ipa_trace_buffer_is_full)
- trace_debug ("lib stopped due to full buffer.");
- if (ipa_stopping_tracepoint)
- trace_debug ("lib stopped due to tpoint");
- if (ipa_error_tracepoint)
- trace_debug ("lib stopped due to error");
- }
-
- if (ipa_stopping_tracepoint != 0)
- {
- stopping_tracepoint
- = fast_tracepoint_from_ipa_tpoint_address (ipa_stopping_tracepoint);
- }
- else if (ipa_expr_eval_result != expr_eval_no_error)
- {
- expr_eval_result = ipa_expr_eval_result;
- error_tracepoint
- = fast_tracepoint_from_ipa_tpoint_address (ipa_error_tracepoint);
- }
- stop_tracing ();
- return 1;
- }
- else if (stop_pc == ipa_sym_addrs.addr_flush_trace_buffer)
- {
- trace_debug ("lib stopped at flush_trace_buffer");
- return 1;
- }
-
- return 0;
-}
-
-/* Return true if TINFO just hit a tracepoint. Collect data if
- so. */
-
-int
-tracepoint_was_hit (struct thread_info *tinfo, CORE_ADDR stop_pc)
-{
- struct tracepoint *tpoint;
- int ret = 0;
- struct trap_tracepoint_ctx ctx;
-
- /* Not tracing, don't handle. */
- if (!tracing)
- return 0;
-
- ctx.base.type = trap_tracepoint;
- ctx.regcache = get_thread_regcache (tinfo, 1);
-
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- {
- /* Note that we collect fast tracepoints here as well. We'll
- step over the fast tracepoint jump later, which avoids the
- double collect. However, we don't collect for static
- tracepoints here, because UST markers are compiled in program,
- and probes will be executed in program. So static tracepoints
- are collected there. */
- if (tpoint->enabled && stop_pc == tpoint->address
- && tpoint->type != static_tracepoint)
- {
- trace_debug ("Thread %s at address of tracepoint %d at 0x%s",
- target_pid_to_str (tinfo->id),
- tpoint->number, paddress (tpoint->address));
-
- /* Test the condition if present, and collect if true. */
- if (!tpoint->cond
- || (condition_true_at_tracepoint
- ((struct tracepoint_hit_ctx *) &ctx, tpoint)))
- collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
- stop_pc, tpoint);
-
- if (stopping_tracepoint
- || trace_buffer_is_full
- || expr_eval_result != expr_eval_no_error)
- {
- stop_tracing ();
- }
- /* If the tracepoint had a 'while-stepping' action, then set
- the thread to collect this tracepoint on the following
- single-steps. */
- else if (tpoint->step_count > 0)
- {
- add_while_stepping_state (tinfo,
- tpoint->number, tpoint->address);
- }
-
- ret = 1;
- }
- }
-
- return ret;
-}
-
-#endif
-
-#if defined IN_PROCESS_AGENT && defined HAVE_UST
-struct ust_marker_data;
-static void collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- struct traceframe *tframe);
-#endif
-
-/* Create a trace frame for the hit of the given tracepoint in the
- given thread. */
-
-static void
-collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx, CORE_ADDR stop_pc,
- struct tracepoint *tpoint)
-{
- struct traceframe *tframe;
- int acti;
-
- /* Only count it as a hit when we actually collect data. */
- tpoint->hit_count++;
-
- /* If we've exceeded a defined pass count, record the event for
- later, and finish the collection for this hit. This test is only
- for nonstepping tracepoints, stepping tracepoints test at the end
- of their while-stepping loop. */
- if (tpoint->pass_count > 0
- && tpoint->hit_count >= tpoint->pass_count
- && tpoint->step_count == 0
- && stopping_tracepoint == NULL)
- stopping_tracepoint = tpoint;
-
- trace_debug ("Making new traceframe for tracepoint %d at 0x%s, hit %" PRIu64,
- tpoint->number, paddress (tpoint->address), tpoint->hit_count);
-
- tframe = add_traceframe (tpoint);
-
- if (tframe)
- {
- for (acti = 0; acti < tpoint->numactions; ++acti)
- {
-#ifndef IN_PROCESS_AGENT
- trace_debug ("Tracepoint %d at 0x%s about to do action '%s'",
- tpoint->number, paddress (tpoint->address),
- tpoint->actions_str[acti]);
-#endif
-
- do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
- tpoint->actions[acti]);
- }
-
- finish_traceframe (tframe);
- }
-
- if (tframe == NULL && tracing)
- trace_buffer_is_full = 1;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-static void
-collect_data_at_step (struct tracepoint_hit_ctx *ctx,
- CORE_ADDR stop_pc,
- struct tracepoint *tpoint, int current_step)
-{
- struct traceframe *tframe;
- int acti;
-
- trace_debug ("Making new step traceframe for "
- "tracepoint %d at 0x%s, step %d of %" PRIu64 ", hit %" PRIu64,
- tpoint->number, paddress (tpoint->address),
- current_step, tpoint->step_count,
- tpoint->hit_count);
-
- tframe = add_traceframe (tpoint);
-
- if (tframe)
- {
- for (acti = 0; acti < tpoint->num_step_actions; ++acti)
- {
- trace_debug ("Tracepoint %d at 0x%s about to do step action '%s'",
- tpoint->number, paddress (tpoint->address),
- tpoint->step_actions_str[acti]);
-
- do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
- tpoint->step_actions[acti]);
- }
-
- finish_traceframe (tframe);
- }
-
- if (tframe == NULL && tracing)
- trace_buffer_is_full = 1;
-}
-
-#endif
-
-#ifdef IN_PROCESS_AGENT
-/* The target description index for IPA. Passed from gdbserver, used
- to select ipa_tdesc. */
-EXTERN_C_PUSH
-IP_AGENT_EXPORT_VAR int ipa_tdesc_idx;
-EXTERN_C_POP
-#endif
-
-static struct regcache *
-get_context_regcache (struct tracepoint_hit_ctx *ctx)
-{
- struct regcache *regcache = NULL;
-#ifdef IN_PROCESS_AGENT
- const struct target_desc *ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
-
- if (ctx->type == fast_tracepoint)
- {
- struct fast_tracepoint_ctx *fctx = (struct fast_tracepoint_ctx *) ctx;
- if (!fctx->regcache_initted)
- {
- fctx->regcache_initted = 1;
- init_register_cache (&fctx->regcache, ipa_tdesc, fctx->regspace);
- supply_regblock (&fctx->regcache, NULL);
- supply_fast_tracepoint_registers (&fctx->regcache, fctx->regs);
- }
- regcache = &fctx->regcache;
- }
-#ifdef HAVE_UST
- if (ctx->type == static_tracepoint)
- {
- struct static_tracepoint_ctx *sctx
- = (struct static_tracepoint_ctx *) ctx;
-
- if (!sctx->regcache_initted)
- {
- sctx->regcache_initted = 1;
- init_register_cache (&sctx->regcache, ipa_tdesc, sctx->regspace);
- supply_regblock (&sctx->regcache, NULL);
- /* Pass down the tracepoint address, because REGS doesn't
- include the PC, but we know what it must have been. */
- supply_static_tracepoint_registers (&sctx->regcache,
- (const unsigned char *)
- sctx->regs,
- sctx->tpoint->address);
- }
- regcache = &sctx->regcache;
- }
-#endif
-#else
- if (ctx->type == trap_tracepoint)
- {
- struct trap_tracepoint_ctx *tctx = (struct trap_tracepoint_ctx *) ctx;
- regcache = tctx->regcache;
- }
-#endif
-
- gdb_assert (regcache != NULL);
-
- return regcache;
-}
-
-static void
-do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- CORE_ADDR stop_pc,
- struct tracepoint *tpoint,
- struct traceframe *tframe,
- struct tracepoint_action *taction)
-{
- enum eval_result_type err;
-
- switch (taction->type)
- {
- case 'M':
- {
- struct collect_memory_action *maction;
- struct eval_agent_expr_context ax_ctx;
-
- maction = (struct collect_memory_action *) taction;
- ax_ctx.regcache = NULL;
- ax_ctx.tframe = tframe;
- ax_ctx.tpoint = tpoint;
-
- trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)",
- pulongest (maction->len),
- paddress (maction->addr), maction->basereg);
- /* (should use basereg) */
- agent_mem_read (&ax_ctx, NULL, (CORE_ADDR) maction->addr,
- maction->len);
- break;
- }
- case 'R':
- {
- unsigned char *regspace;
- struct regcache tregcache;
- struct regcache *context_regcache;
- int regcache_size;
-
- trace_debug ("Want to collect registers");
-
- context_regcache = get_context_regcache (ctx);
- regcache_size = register_cache_size (context_regcache->tdesc);
-
- /* Collect all registers for now. */
- regspace = add_traceframe_block (tframe, tpoint, 1 + regcache_size);
- if (regspace == NULL)
- {
- trace_debug ("Trace buffer block allocation failed, skipping");
- break;
- }
- /* Identify a register block. */
- *regspace = 'R';
-
- /* Wrap the regblock in a register cache (in the stack, we
- don't want to malloc here). */
- init_register_cache (&tregcache, context_regcache->tdesc,
- regspace + 1);
-
- /* Copy the register data to the regblock. */
- regcache_cpy (&tregcache, context_regcache);
-
-#ifndef IN_PROCESS_AGENT
- /* On some platforms, trap-based tracepoints will have the PC
- pointing to the next instruction after the trap, but we
- don't want the user or GDB trying to guess whether the
- saved PC needs adjusting; so always record the adjusted
- stop_pc. Note that we can't use tpoint->address instead,
- since it will be wrong for while-stepping actions. This
- adjustment is a nop for fast tracepoints collected from the
- in-process lib (but not if GDBserver is collecting one
- preemptively), since the PC had already been adjusted to
- contain the tracepoint's address by the jump pad. */
- trace_debug ("Storing stop pc (0x%s) in regblock",
- paddress (stop_pc));
-
- /* This changes the regblock, not the thread's
- regcache. */
- regcache_write_pc (&tregcache, stop_pc);
-#endif
- }
- break;
- case 'X':
- {
- struct eval_expr_action *eaction;
- struct eval_agent_expr_context ax_ctx;
-
- eaction = (struct eval_expr_action *) taction;
- ax_ctx.regcache = get_context_regcache (ctx);
- ax_ctx.tframe = tframe;
- ax_ctx.tpoint = tpoint;
-
- trace_debug ("Want to evaluate expression");
-
- err = gdb_eval_agent_expr (&ax_ctx, eaction->expr, NULL);
-
- if (err != expr_eval_no_error)
- {
- record_tracepoint_error (tpoint, "action expression", err);
- return;
- }
- }
- break;
- case 'L':
- {
-#if defined IN_PROCESS_AGENT && defined HAVE_UST
- trace_debug ("Want to collect static trace data");
- collect_ust_data_at_tracepoint (ctx, tframe);
-#else
- trace_debug ("warning: collecting static trace data, "
- "but static tracepoints are not supported");
-#endif
- }
- break;
- default:
- trace_debug ("unknown trace action '%c', ignoring", taction->type);
- break;
- }
-}
-
-static int
-condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- struct tracepoint *tpoint)
-{
- ULONGEST value = 0;
- enum eval_result_type err;
-
- /* Presently, gdbserver doesn't run compiled conditions, only the
- IPA does. If the program stops at a fast tracepoint's address
- (e.g., due to a breakpoint, trap tracepoint, or stepping),
- gdbserver preemptively collect the fast tracepoint. Later, on
- resume, gdbserver steps over the fast tracepoint like it steps
- over breakpoints, so that the IPA doesn't see that fast
- tracepoint. This avoids double collects of fast tracepoints in
- that stopping scenario. Having gdbserver itself handle the fast
- tracepoint gives the user a consistent view of when fast or trap
- tracepoints are collected, compared to an alternative where only
- trap tracepoints are collected on stop, and fast tracepoints on
- resume. When a fast tracepoint is being processed by gdbserver,
- it is always the non-compiled condition expression that is
- used. */
-#ifdef IN_PROCESS_AGENT
- if (tpoint->compiled_cond)
- {
- struct fast_tracepoint_ctx *fctx = (struct fast_tracepoint_ctx *) ctx;
- err = ((condfn) (uintptr_t) (tpoint->compiled_cond)) (fctx->regs, &value);
- }
- else
-#endif
- {
- struct eval_agent_expr_context ax_ctx;
-
- ax_ctx.regcache = get_context_regcache (ctx);
- ax_ctx.tframe = NULL;
- ax_ctx.tpoint = tpoint;
-
- err = gdb_eval_agent_expr (&ax_ctx, tpoint->cond, &value);
- }
- if (err != expr_eval_no_error)
- {
- record_tracepoint_error (tpoint, "condition", err);
- /* The error case must return false. */
- return 0;
- }
-
- trace_debug ("Tracepoint %d at 0x%s condition evals to %s",
- tpoint->number, paddress (tpoint->address),
- pulongest (value));
- return (value ? 1 : 0);
-}
-
-/* Do memory copies for bytecodes. */
-/* Do the recording of memory blocks for actions and bytecodes. */
-
-int
-agent_mem_read (struct eval_agent_expr_context *ctx,
- unsigned char *to, CORE_ADDR from, ULONGEST len)
-{
- unsigned char *mspace;
- ULONGEST remaining = len;
- unsigned short blocklen;
-
- /* If a 'to' buffer is specified, use it. */
- if (to != NULL)
- {
- read_inferior_memory (from, to, len);
- return 0;
- }
-
- /* Otherwise, create a new memory block in the trace buffer. */
- while (remaining > 0)
- {
- size_t sp;
-
- blocklen = (remaining > 65535 ? 65535 : remaining);
- sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
- mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
- if (mspace == NULL)
- return 1;
- /* Identify block as a memory block. */
- *mspace = 'M';
- ++mspace;
- /* Record address and size. */
- memcpy (mspace, &from, sizeof (from));
- mspace += sizeof (from);
- memcpy (mspace, &blocklen, sizeof (blocklen));
- mspace += sizeof (blocklen);
- /* Record the memory block proper. */
- read_inferior_memory (from, mspace, blocklen);
- trace_debug ("%d bytes recorded", blocklen);
- remaining -= blocklen;
- from += blocklen;
- }
- return 0;
-}
-
-int
-agent_mem_read_string (struct eval_agent_expr_context *ctx,
- unsigned char *to, CORE_ADDR from, ULONGEST len)
-{
- unsigned char *buf, *mspace;
- ULONGEST remaining = len;
- unsigned short blocklen, i;
-
- /* To save a bit of space, block lengths are 16-bit, so break large
- requests into multiple blocks. Bordering on overkill for strings,
- but it could happen that someone specifies a large max length. */
- while (remaining > 0)
- {
- size_t sp;
-
- blocklen = (remaining > 65535 ? 65535 : remaining);
- /* We want working space to accumulate nonzero bytes, since
- traceframes must have a predecided size (otherwise it gets
- harder to wrap correctly for the circular case, etc). */
- buf = (unsigned char *) xmalloc (blocklen + 1);
- for (i = 0; i < blocklen; ++i)
- {
- /* Read the string one byte at a time, in case the string is
- at the end of a valid memory area - we don't want a
- correctly-terminated string to engender segvio
- complaints. */
- read_inferior_memory (from + i, buf + i, 1);
-
- if (buf[i] == '\0')
- {
- blocklen = i + 1;
- /* Make sure outer loop stops now too. */
- remaining = blocklen;
- break;
- }
- }
- sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
- mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
- if (mspace == NULL)
- {
- xfree (buf);
- return 1;
- }
- /* Identify block as a memory block. */
- *mspace = 'M';
- ++mspace;
- /* Record address and size. */
- memcpy ((void *) mspace, (void *) &from, sizeof (from));
- mspace += sizeof (from);
- memcpy ((void *) mspace, (void *) &blocklen, sizeof (blocklen));
- mspace += sizeof (blocklen);
- /* Copy the string contents. */
- memcpy ((void *) mspace, (void *) buf, blocklen);
- remaining -= blocklen;
- from += blocklen;
- xfree (buf);
- }
- return 0;
-}
-
-/* Record the value of a trace state variable. */
-
-int
-agent_tsv_read (struct eval_agent_expr_context *ctx, int n)
-{
- unsigned char *vspace;
- LONGEST val;
-
- vspace = add_traceframe_block (ctx->tframe, ctx->tpoint,
- 1 + sizeof (n) + sizeof (LONGEST));
- if (vspace == NULL)
- return 1;
- /* Identify block as a variable. */
- *vspace = 'V';
- /* Record variable's number and value. */
- memcpy (vspace + 1, &n, sizeof (n));
- val = get_trace_state_variable_value (n);
- memcpy (vspace + 1 + sizeof (n), &val, sizeof (val));
- trace_debug ("Variable %d recorded", n);
- return 0;
-}
-
-#ifndef IN_PROCESS_AGENT
-
-/* Callback for traceframe_walk_blocks, used to find a given block
- type in a traceframe. */
-
-static int
-match_blocktype (char blocktype, unsigned char *dataptr, void *data)
-{
- char *wantedp = (char *) data;
-
- if (*wantedp == blocktype)
- return 1;
-
- return 0;
-}
-
-/* Walk over all traceframe blocks of the traceframe buffer starting
- at DATABASE, of DATASIZE bytes long, and call CALLBACK for each
- block found, passing in DATA unmodified. If CALLBACK returns true,
- this returns a pointer to where the block is found. Returns NULL
- if no callback call returned true, indicating that all blocks have
- been walked. */
-
-static unsigned char *
-traceframe_walk_blocks (unsigned char *database, unsigned int datasize,
- int tfnum,
- int (*callback) (char blocktype,
- unsigned char *dataptr,
- void *data),
- void *data)
-{
- unsigned char *dataptr;
-
- if (datasize == 0)
- {
- trace_debug ("traceframe %d has no data", tfnum);
- return NULL;
- }
-
- /* Iterate through a traceframe's blocks, looking for a block of the
- requested type. */
- for (dataptr = database;
- dataptr < database + datasize;
- /* nothing */)
- {
- char blocktype;
- unsigned short mlen;
-
- if (dataptr == trace_buffer_wrap)
- {
- /* Adjust to reflect wrapping part of the frame around to
- the beginning. */
- datasize = dataptr - database;
- dataptr = database = trace_buffer_lo;
- }
-
- blocktype = *dataptr++;
-
- if ((*callback) (blocktype, dataptr, data))
- return dataptr;
-
- switch (blocktype)
- {
- case 'R':
- /* Skip over the registers block. */
- dataptr += current_target_desc ()->registers_size;
- break;
- case 'M':
- /* Skip over the memory block. */
- dataptr += sizeof (CORE_ADDR);
- memcpy (&mlen, dataptr, sizeof (mlen));
- dataptr += (sizeof (mlen) + mlen);
- break;
- case 'V':
- /* Skip over the TSV block. */
- dataptr += (sizeof (int) + sizeof (LONGEST));
- break;
- case 'S':
- /* Skip over the static trace data block. */
- memcpy (&mlen, dataptr, sizeof (mlen));
- dataptr += (sizeof (mlen) + mlen);
- break;
- default:
- trace_debug ("traceframe %d has unknown block type 0x%x",
- tfnum, blocktype);
- return NULL;
- }
- }
-
- return NULL;
-}
-
-/* Look for the block of type TYPE_WANTED in the traceframe starting
- at DATABASE of DATASIZE bytes long. TFNUM is the traceframe
- number. */
-
-static unsigned char *
-traceframe_find_block_type (unsigned char *database, unsigned int datasize,
- int tfnum, char type_wanted)
-{
- return traceframe_walk_blocks (database, datasize, tfnum,
- match_blocktype, &type_wanted);
-}
-
-static unsigned char *
-traceframe_find_regblock (struct traceframe *tframe, int tfnum)
-{
- unsigned char *regblock;
-
- regblock = traceframe_find_block_type (tframe->data,
- tframe->data_size,
- tfnum, 'R');
-
- if (regblock == NULL)
- trace_debug ("traceframe %d has no register data", tfnum);
-
- return regblock;
-}
-
-/* Get registers from a traceframe. */
-
-int
-fetch_traceframe_registers (int tfnum, struct regcache *regcache, int regnum)
-{
- unsigned char *dataptr;
- struct tracepoint *tpoint;
- struct traceframe *tframe;
-
- tframe = find_traceframe (tfnum);
-
- if (tframe == NULL)
- {
- trace_debug ("traceframe %d not found", tfnum);
- return 1;
- }
-
- dataptr = traceframe_find_regblock (tframe, tfnum);
- if (dataptr == NULL)
- {
- /* Mark registers unavailable. */
- supply_regblock (regcache, NULL);
-
- /* We can generally guess at a PC, although this will be
- misleading for while-stepping frames and multi-location
- tracepoints. */
- tpoint = find_next_tracepoint_by_number (NULL, tframe->tpnum);
- if (tpoint != NULL)
- regcache_write_pc (regcache, tpoint->address);
- }
- else
- supply_regblock (regcache, dataptr);
-
- return 0;
-}
-
-static CORE_ADDR
-traceframe_get_pc (struct traceframe *tframe)
-{
- struct regcache regcache;
- unsigned char *dataptr;
- const struct target_desc *tdesc = current_target_desc ();
-
- dataptr = traceframe_find_regblock (tframe, -1);
- if (dataptr == NULL)
- return 0;
-
- init_register_cache (®cache, tdesc, dataptr);
- return regcache_read_pc (®cache);
-}
-
-/* Read a requested block of memory from a trace frame. */
-
-int
-traceframe_read_mem (int tfnum, CORE_ADDR addr,
- unsigned char *buf, ULONGEST length,
- ULONGEST *nbytes)
-{
- struct traceframe *tframe;
- unsigned char *database, *dataptr;
- unsigned int datasize;
- CORE_ADDR maddr;
- unsigned short mlen;
-
- trace_debug ("traceframe_read_mem");
-
- tframe = find_traceframe (tfnum);
-
- if (!tframe)
- {
- trace_debug ("traceframe %d not found", tfnum);
- return 1;
- }
-
- datasize = tframe->data_size;
- database = dataptr = &tframe->data[0];
-
- /* Iterate through a traceframe's blocks, looking for memory. */
- while ((dataptr = traceframe_find_block_type (dataptr,
- datasize
- - (dataptr - database),
- tfnum, 'M')) != NULL)
- {
- memcpy (&maddr, dataptr, sizeof (maddr));
- dataptr += sizeof (maddr);
- memcpy (&mlen, dataptr, sizeof (mlen));
- dataptr += sizeof (mlen);
- trace_debug ("traceframe %d has %d bytes at %s",
- tfnum, mlen, paddress (maddr));
-
- /* If the block includes the first part of the desired range,
- return as much it has; GDB will re-request the remainder,
- which might be in a different block of this trace frame. */
- if (maddr <= addr && addr < (maddr + mlen))
- {
- ULONGEST amt = (maddr + mlen) - addr;
- if (amt > length)
- amt = length;
-
- memcpy (buf, dataptr + (addr - maddr), amt);
- *nbytes = amt;
- return 0;
- }
-
- /* Skip over this block. */
- dataptr += mlen;
- }
-
- trace_debug ("traceframe %d has no memory data for the desired region",
- tfnum);
-
- *nbytes = 0;
- return 0;
-}
-
-static int
-traceframe_read_tsv (int tsvnum, LONGEST *val)
-{
- client_state &cs = get_client_state ();
- int tfnum;
- struct traceframe *tframe;
- unsigned char *database, *dataptr;
- unsigned int datasize;
- int vnum;
- int found = 0;
-
- trace_debug ("traceframe_read_tsv");
-
- tfnum = cs.current_traceframe;
-
- if (tfnum < 0)
- {
- trace_debug ("no current traceframe");
- return 1;
- }
-
- tframe = find_traceframe (tfnum);
-
- if (tframe == NULL)
- {
- trace_debug ("traceframe %d not found", tfnum);
- return 1;
- }
-
- datasize = tframe->data_size;
- database = dataptr = &tframe->data[0];
-
- /* Iterate through a traceframe's blocks, looking for the last
- matched tsv. */
- while ((dataptr = traceframe_find_block_type (dataptr,
- datasize
- - (dataptr - database),
- tfnum, 'V')) != NULL)
- {
- memcpy (&vnum, dataptr, sizeof (vnum));
- dataptr += sizeof (vnum);
-
- trace_debug ("traceframe %d has variable %d", tfnum, vnum);
-
- /* Check that this is the variable we want. */
- if (tsvnum == vnum)
- {
- memcpy (val, dataptr, sizeof (*val));
- found = 1;
- }
-
- /* Skip over this block. */
- dataptr += sizeof (LONGEST);
- }
-
- if (!found)
- trace_debug ("traceframe %d has no data for variable %d",
- tfnum, tsvnum);
- return !found;
-}
-
-/* Read a requested block of static tracepoint data from a trace
- frame. */
-
-int
-traceframe_read_sdata (int tfnum, ULONGEST offset,
- unsigned char *buf, ULONGEST length,
- ULONGEST *nbytes)
-{
- struct traceframe *tframe;
- unsigned char *database, *dataptr;
- unsigned int datasize;
- unsigned short mlen;
-
- trace_debug ("traceframe_read_sdata");
-
- tframe = find_traceframe (tfnum);
-
- if (!tframe)
- {
- trace_debug ("traceframe %d not found", tfnum);
- return 1;
- }
-
- datasize = tframe->data_size;
- database = &tframe->data[0];
-
- /* Iterate through a traceframe's blocks, looking for static
- tracepoint data. */
- dataptr = traceframe_find_block_type (database, datasize,
- tfnum, 'S');
- if (dataptr != NULL)
- {
- memcpy (&mlen, dataptr, sizeof (mlen));
- dataptr += sizeof (mlen);
- if (offset < mlen)
- {
- if (offset + length > mlen)
- length = mlen - offset;
-
- memcpy (buf, dataptr, length);
- *nbytes = length;
- }
- else
- *nbytes = 0;
- return 0;
- }
-
- trace_debug ("traceframe %d has no static trace data", tfnum);
-
- *nbytes = 0;
- return 0;
-}
-
-/* Callback for traceframe_walk_blocks. Builds a traceframe-info
- object. DATA is pointer to a struct buffer holding the
- traceframe-info object being built. */
-
-static int
-build_traceframe_info_xml (char blocktype, unsigned char *dataptr, void *data)
-{
- struct buffer *buffer = (struct buffer *) data;
-
- switch (blocktype)
- {
- case 'M':
- {
- unsigned short mlen;
- CORE_ADDR maddr;
-
- memcpy (&maddr, dataptr, sizeof (maddr));
- dataptr += sizeof (maddr);
- memcpy (&mlen, dataptr, sizeof (mlen));
- dataptr += sizeof (mlen);
- buffer_xml_printf (buffer,
- "<memory start=\"0x%s\" length=\"0x%s\"/>\n",
- paddress (maddr), phex_nz (mlen, sizeof (mlen)));
- break;
- }
- case 'V':
- {
- int vnum;
-
- memcpy (&vnum, dataptr, sizeof (vnum));
- buffer_xml_printf (buffer, "<tvar id=\"%d\"/>\n", vnum);
- break;
- }
- case 'R':
- case 'S':
- {
- break;
- }
- default:
- warning ("Unhandled trace block type (%d) '%c ' "
- "while building trace frame info.",
- blocktype, blocktype);
- break;
- }
-
- return 0;
-}
-
-/* Build a traceframe-info object for traceframe number TFNUM into
- BUFFER. */
-
-int
-traceframe_read_info (int tfnum, struct buffer *buffer)
-{
- struct traceframe *tframe;
-
- trace_debug ("traceframe_read_info");
-
- tframe = find_traceframe (tfnum);
-
- if (!tframe)
- {
- trace_debug ("traceframe %d not found", tfnum);
- return 1;
- }
-
- buffer_grow_str (buffer, "<traceframe-info>\n");
- traceframe_walk_blocks (tframe->data, tframe->data_size,
- tfnum, build_traceframe_info_xml, buffer);
- buffer_grow_str0 (buffer, "</traceframe-info>\n");
- return 0;
-}
-
-/* Return the first fast tracepoint whose jump pad contains PC. */
-
-static struct tracepoint *
-fast_tracepoint_from_jump_pad_address (CORE_ADDR pc)
-{
- struct tracepoint *tpoint;
-
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- if (tpoint->type == fast_tracepoint)
- if (tpoint->jump_pad <= pc && pc < tpoint->jump_pad_end)
- return tpoint;
-
- return NULL;
-}
-
-/* Return the first fast tracepoint whose trampoline contains PC. */
-
-static struct tracepoint *
-fast_tracepoint_from_trampoline_address (CORE_ADDR pc)
-{
- struct tracepoint *tpoint;
-
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- {
- if (tpoint->type == fast_tracepoint
- && tpoint->trampoline <= pc && pc < tpoint->trampoline_end)
- return tpoint;
- }
-
- return NULL;
-}
-
-/* Return GDBserver's tracepoint that matches the IP Agent's
- tracepoint object that lives at IPA_TPOINT_OBJ in the IP Agent's
- address space. */
-
-static struct tracepoint *
-fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR ipa_tpoint_obj)
-{
- struct tracepoint *tpoint;
-
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- if (tpoint->type == fast_tracepoint)
- if (tpoint->obj_addr_on_target == ipa_tpoint_obj)
- return tpoint;
-
- return NULL;
-}
-
-#endif
-
-/* The type of the object that is used to synchronize fast tracepoint
- collection. */
-
-typedef struct collecting_t
-{
- /* The fast tracepoint number currently collecting. */
- uintptr_t tpoint;
-
- /* A number that GDBserver can use to identify the thread that is
- presently holding the collect lock. This need not (and usually
- is not) the thread id, as getting the current thread ID usually
- requires a system call, which we want to avoid like the plague.
- Usually this is thread's TCB, found in the TLS (pseudo-)
- register, which is readable with a single insn on several
- architectures. */
- uintptr_t thread_area;
-} collecting_t;
-
-#ifndef IN_PROCESS_AGENT
-
-void
-force_unlock_trace_buffer (void)
-{
- write_inferior_data_pointer (ipa_sym_addrs.addr_collecting, 0);
-}
-
-/* Check if the thread identified by THREAD_AREA which is stopped at
- STOP_PC, is presently locking the fast tracepoint collection, and
- if so, gather some status of said collection. Returns 0 if the
- thread isn't collecting or in the jump pad at all. 1, if in the
- jump pad (or within gdb_collect) and hasn't executed the adjusted
- original insn yet (can set a breakpoint there and run to it). 2,
- if presently executing the adjusted original insn --- in which
- case, if we want to move the thread out of the jump pad, we need to
- single-step it until this function returns 0. */
-
-fast_tpoint_collect_result
-fast_tracepoint_collecting (CORE_ADDR thread_area,
- CORE_ADDR stop_pc,
- struct fast_tpoint_collect_status *status)
-{
- CORE_ADDR ipa_collecting;
- CORE_ADDR ipa_gdb_jump_pad_buffer, ipa_gdb_jump_pad_buffer_end;
- CORE_ADDR ipa_gdb_trampoline_buffer;
- CORE_ADDR ipa_gdb_trampoline_buffer_end;
- struct tracepoint *tpoint;
- int needs_breakpoint;
-
- /* The thread THREAD_AREA is either:
-
- 0. not collecting at all, not within the jump pad, or within
- gdb_collect or one of its callees.
-
- 1. in the jump pad and haven't reached gdb_collect
-
- 2. within gdb_collect (out of the jump pad) (collect is set)
-
- 3. we're in the jump pad, after gdb_collect having returned,
- possibly executing the adjusted insns.
-
- For cases 1 and 3, `collecting' may or not be set. The jump pad
- doesn't have any complicated jump logic, so we can tell if the
- thread is executing the adjust original insn or not by just
- matching STOP_PC with known jump pad addresses. If we it isn't
- yet executing the original insn, set a breakpoint there, and let
- the thread run to it, so to quickly step over a possible (many
- insns) gdb_collect call. Otherwise, or when the breakpoint is
- hit, only a few (small number of) insns are left to be executed
- in the jump pad. Single-step the thread until it leaves the
- jump pad. */
-
- again:
- tpoint = NULL;
- needs_breakpoint = 0;
- trace_debug ("fast_tracepoint_collecting");
-
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer,
- &ipa_gdb_jump_pad_buffer))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting `gdb_jump_pad_buffer'");
- }
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer_end,
- &ipa_gdb_jump_pad_buffer_end))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting `gdb_jump_pad_buffer_end'");
- }
-
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
- &ipa_gdb_trampoline_buffer))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting `gdb_trampoline_buffer'");
- }
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
- &ipa_gdb_trampoline_buffer_end))
- {
- internal_error (__FILE__, __LINE__,
- "error extracting `gdb_trampoline_buffer_end'");
- }
-
- if (ipa_gdb_jump_pad_buffer <= stop_pc
- && stop_pc < ipa_gdb_jump_pad_buffer_end)
- {
- /* We can tell which tracepoint(s) the thread is collecting by
- matching the jump pad address back to the tracepoint. */
- tpoint = fast_tracepoint_from_jump_pad_address (stop_pc);
- if (tpoint == NULL)
- {
- warning ("in jump pad, but no matching tpoint?");
- return fast_tpoint_collect_result::not_collecting;
- }
- else
- {
- trace_debug ("in jump pad of tpoint (%d, %s); jump_pad(%s, %s); "
- "adj_insn(%s, %s)",
- tpoint->number, paddress (tpoint->address),
- paddress (tpoint->jump_pad),
- paddress (tpoint->jump_pad_end),
- paddress (tpoint->adjusted_insn_addr),
- paddress (tpoint->adjusted_insn_addr_end));
- }
-
- /* Definitely in the jump pad. May or may not need
- fast-exit-jump-pad breakpoint. */
- if (tpoint->jump_pad <= stop_pc
- && stop_pc < tpoint->adjusted_insn_addr)
- needs_breakpoint = 1;
- }
- else if (ipa_gdb_trampoline_buffer <= stop_pc
- && stop_pc < ipa_gdb_trampoline_buffer_end)
- {
- /* We can tell which tracepoint(s) the thread is collecting by
- matching the trampoline address back to the tracepoint. */
- tpoint = fast_tracepoint_from_trampoline_address (stop_pc);
- if (tpoint == NULL)
- {
- warning ("in trampoline, but no matching tpoint?");
- return fast_tpoint_collect_result::not_collecting;
- }
- else
- {
- trace_debug ("in trampoline of tpoint (%d, %s); trampoline(%s, %s)",
- tpoint->number, paddress (tpoint->address),
- paddress (tpoint->trampoline),
- paddress (tpoint->trampoline_end));
- }
-
- /* Have not reached jump pad yet, but treat the trampoline as a
- part of the jump pad that is before the adjusted original
- instruction. */
- needs_breakpoint = 1;
- }
- else
- {
- collecting_t ipa_collecting_obj;
-
- /* If `collecting' is set/locked, then the THREAD_AREA thread
- may or not be the one holding the lock. We have to read the
- lock to find out. */
-
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_collecting,
- &ipa_collecting))
- {
- trace_debug ("fast_tracepoint_collecting:"
- " failed reading 'collecting' in the inferior");
- return fast_tpoint_collect_result::not_collecting;
- }
-
- if (!ipa_collecting)
- {
- trace_debug ("fast_tracepoint_collecting: not collecting"
- " (and nobody is).");
- return fast_tpoint_collect_result::not_collecting;
- }
-
- /* Some thread is collecting. Check which. */
- if (read_inferior_memory (ipa_collecting,
- (unsigned char *) &ipa_collecting_obj,
- sizeof (ipa_collecting_obj)) != 0)
- goto again;
-
- if (ipa_collecting_obj.thread_area != thread_area)
- {
- trace_debug ("fast_tracepoint_collecting: not collecting "
- "(another thread is)");
- return fast_tpoint_collect_result::not_collecting;
- }
-
- tpoint
- = fast_tracepoint_from_ipa_tpoint_address (ipa_collecting_obj.tpoint);
- if (tpoint == NULL)
- {
- warning ("fast_tracepoint_collecting: collecting, "
- "but tpoint %s not found?",
- paddress ((CORE_ADDR) ipa_collecting_obj.tpoint));
- return fast_tpoint_collect_result::not_collecting;
- }
-
- /* The thread is within `gdb_collect', skip over the rest of
- fast tracepoint collection quickly using a breakpoint. */
- needs_breakpoint = 1;
- }
-
- /* The caller wants a bit of status detail. */
- if (status != NULL)
- {
- status->tpoint_num = tpoint->number;
- status->tpoint_addr = tpoint->address;
- status->adjusted_insn_addr = tpoint->adjusted_insn_addr;
- status->adjusted_insn_addr_end = tpoint->adjusted_insn_addr_end;
- }
-
- if (needs_breakpoint)
- {
- /* Hasn't executed the original instruction yet. Set breakpoint
- there, and wait till it's hit, then single-step until exiting
- the jump pad. */
-
- trace_debug ("\
-fast_tracepoint_collecting, returning continue-until-break at %s",
- paddress (tpoint->adjusted_insn_addr));
-
- return fast_tpoint_collect_result::before_insn; /* continue */
- }
- else
- {
- /* Just single-step until exiting the jump pad. */
-
- trace_debug ("fast_tracepoint_collecting, returning "
- "need-single-step (%s-%s)",
- paddress (tpoint->adjusted_insn_addr),
- paddress (tpoint->adjusted_insn_addr_end));
-
- return fast_tpoint_collect_result::at_insn; /* single-step */
- }
-}
-
-#endif
-
-#ifdef IN_PROCESS_AGENT
-
-/* The global fast tracepoint collect lock. Points to a collecting_t
- object built on the stack by the jump pad, if presently locked;
- NULL if it isn't locked. Note that this lock *must* be set while
- executing any *function other than the jump pad. See
- fast_tracepoint_collecting. */
-EXTERN_C_PUSH
-IP_AGENT_EXPORT_VAR collecting_t *collecting;
-EXTERN_C_POP
-
-/* This is needed for -Wmissing-declarations. */
-IP_AGENT_EXPORT_FUNC void gdb_collect (struct tracepoint *tpoint,
- unsigned char *regs);
-
-/* This routine, called from the jump pad (in asm) is designed to be
- called from the jump pads of fast tracepoints, thus it is on the
- critical path. */
-
-IP_AGENT_EXPORT_FUNC void
-gdb_collect (struct tracepoint *tpoint, unsigned char *regs)
-{
- struct fast_tracepoint_ctx ctx;
- const struct target_desc *ipa_tdesc;
-
- /* Don't do anything until the trace run is completely set up. */
- if (!tracing)
- return;
-
- ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
- ctx.base.type = fast_tracepoint;
- ctx.regs = regs;
- ctx.regcache_initted = 0;
- /* Wrap the regblock in a register cache (in the stack, we don't
- want to malloc here). */
- ctx.regspace = (unsigned char *) alloca (ipa_tdesc->registers_size);
- if (ctx.regspace == NULL)
- {
- trace_debug ("Trace buffer block allocation failed, skipping");
- return;
- }
-
- for (ctx.tpoint = tpoint;
- ctx.tpoint != NULL && ctx.tpoint->address == tpoint->address;
- ctx.tpoint = ctx.tpoint->next)
- {
- if (!ctx.tpoint->enabled)
- continue;
-
- /* Multiple tracepoints of different types, such as fast tracepoint and
- static tracepoint, can be set at the same address. */
- if (ctx.tpoint->type != tpoint->type)
- continue;
-
- /* Test the condition if present, and collect if true. */
- if (ctx.tpoint->cond == NULL
- || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
- ctx.tpoint))
- {
- collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
- ctx.tpoint->address, ctx.tpoint);
-
- /* Note that this will cause original insns to be written back
- to where we jumped from, but that's OK because we're jumping
- back to the next whole instruction. This will go badly if
- instruction restoration is not atomic though. */
- if (stopping_tracepoint
- || trace_buffer_is_full
- || expr_eval_result != expr_eval_no_error)
- {
- stop_tracing ();
- break;
- }
- }
- else
- {
- /* If there was a condition and it evaluated to false, the only
- way we would stop tracing is if there was an error during
- condition expression evaluation. */
- if (expr_eval_result != expr_eval_no_error)
- {
- stop_tracing ();
- break;
- }
- }
- }
-}
-
-/* These global variables points to the corresponding functions. This is
- necessary on powerpc64, where asking for function symbol address from gdb
- results in returning the actual code pointer, instead of the descriptor
- pointer. */
-
-typedef void (*gdb_collect_ptr_type) (struct tracepoint *, unsigned char *);
-typedef ULONGEST (*get_raw_reg_ptr_type) (const unsigned char *, int);
-typedef LONGEST (*get_trace_state_variable_value_ptr_type) (int);
-typedef void (*set_trace_state_variable_value_ptr_type) (int, LONGEST);
-
-EXTERN_C_PUSH
-IP_AGENT_EXPORT_VAR gdb_collect_ptr_type gdb_collect_ptr = gdb_collect;
-IP_AGENT_EXPORT_VAR get_raw_reg_ptr_type get_raw_reg_ptr = get_raw_reg;
-IP_AGENT_EXPORT_VAR get_trace_state_variable_value_ptr_type
- get_trace_state_variable_value_ptr = get_trace_state_variable_value;
-IP_AGENT_EXPORT_VAR set_trace_state_variable_value_ptr_type
- set_trace_state_variable_value_ptr = set_trace_state_variable_value;
-EXTERN_C_POP
-
-#endif
-
-#ifndef IN_PROCESS_AGENT
-
-CORE_ADDR
-get_raw_reg_func_addr (void)
-{
- CORE_ADDR res;
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_get_raw_reg_ptr, &res))
- {
- error ("error extracting get_raw_reg_ptr");
- return 0;
- }
- return res;
-}
-
-CORE_ADDR
-get_get_tsv_func_addr (void)
-{
- CORE_ADDR res;
- if (read_inferior_data_pointer (
- ipa_sym_addrs.addr_get_trace_state_variable_value_ptr, &res))
- {
- error ("error extracting get_trace_state_variable_value_ptr");
- return 0;
- }
- return res;
-}
-
-CORE_ADDR
-get_set_tsv_func_addr (void)
-{
- CORE_ADDR res;
- if (read_inferior_data_pointer (
- ipa_sym_addrs.addr_set_trace_state_variable_value_ptr, &res))
- {
- error ("error extracting set_trace_state_variable_value_ptr");
- return 0;
- }
- return res;
-}
-
-static void
-compile_tracepoint_condition (struct tracepoint *tpoint,
- CORE_ADDR *jump_entry)
-{
- CORE_ADDR entry_point = *jump_entry;
- enum eval_result_type err;
-
- trace_debug ("Starting condition compilation for tracepoint %d\n",
- tpoint->number);
-
- /* Initialize the global pointer to the code being built. */
- current_insn_ptr = *jump_entry;
-
- emit_prologue ();
-
- err = compile_bytecodes (tpoint->cond);
-
- if (err == expr_eval_no_error)
- {
- emit_epilogue ();
-
- /* Record the beginning of the compiled code. */
- tpoint->compiled_cond = entry_point;
-
- trace_debug ("Condition compilation for tracepoint %d complete\n",
- tpoint->number);
- }
- else
- {
- /* Leave the unfinished code in situ, but don't point to it. */
-
- tpoint->compiled_cond = 0;
-
- trace_debug ("Condition compilation for tracepoint %d failed, "
- "error code %d",
- tpoint->number, err);
- }
-
- /* Update the code pointer passed in. Note that we do this even if
- the compile fails, so that we can look at the partial results
- instead of letting them be overwritten. */
- *jump_entry = current_insn_ptr;
-
- /* Leave a gap, to aid dump decipherment. */
- *jump_entry += 16;
-}
-
-/* The base pointer of the IPA's heap. This is the only memory the
- IPA is allowed to use. The IPA should _not_ call the inferior's
- `malloc' during operation. That'd be slow, and, most importantly,
- it may not be safe. We may be collecting a tracepoint in a signal
- handler, for example. */
-static CORE_ADDR target_tp_heap;
-
-/* Allocate at least SIZE bytes of memory from the IPA heap, aligned
- to 8 bytes. */
-
-static CORE_ADDR
-target_malloc (ULONGEST size)
-{
- CORE_ADDR ptr;
-
- if (target_tp_heap == 0)
- {
- /* We have the pointer *address*, need what it points to. */
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_tp_heap_buffer,
- &target_tp_heap))
- {
- internal_error (__FILE__, __LINE__,
- "couldn't get target heap head pointer");
- }
- }
-
- ptr = target_tp_heap;
- target_tp_heap += size;
-
- /* Pad to 8-byte alignment. */
- target_tp_heap = ((target_tp_heap + 7) & ~0x7);
-
- return ptr;
-}
-
-static CORE_ADDR
-download_agent_expr (struct agent_expr *expr)
-{
- CORE_ADDR expr_addr;
- CORE_ADDR expr_bytes;
-
- expr_addr = target_malloc (sizeof (*expr));
- target_write_memory (expr_addr, (unsigned char *) expr, sizeof (*expr));
-
- expr_bytes = target_malloc (expr->length);
- write_inferior_data_pointer (expr_addr + offsetof (struct agent_expr, bytes),
- expr_bytes);
- target_write_memory (expr_bytes, expr->bytes, expr->length);
-
- return expr_addr;
-}
-
-/* Align V up to N bits. */
-#define UALIGN(V, N) (((V) + ((N) - 1)) & ~((N) - 1))
-
-/* Sync tracepoint with IPA, but leave maintenance of linked list to caller. */
-
-static void
-download_tracepoint_1 (struct tracepoint *tpoint)
-{
- struct tracepoint target_tracepoint;
- CORE_ADDR tpptr = 0;
-
- gdb_assert (tpoint->type == fast_tracepoint
- || tpoint->type == static_tracepoint);
-
- if (tpoint->cond != NULL && target_emit_ops () != NULL)
- {
- CORE_ADDR jentry, jump_entry;
-
- jentry = jump_entry = get_jump_space_head ();
-
- if (tpoint->cond != NULL)
- {
- /* Pad to 8-byte alignment. (needed?) */
- /* Actually this should be left for the target to
- decide. */
- jentry = UALIGN (jentry, 8);
-
- compile_tracepoint_condition (tpoint, &jentry);
- }
-
- /* Pad to 8-byte alignment. */
- jentry = UALIGN (jentry, 8);
- claim_jump_space (jentry - jump_entry);
- }
-
- target_tracepoint = *tpoint;
-
- tpptr = target_malloc (sizeof (*tpoint));
- tpoint->obj_addr_on_target = tpptr;
-
- /* Write the whole object. We'll fix up its pointers in a bit.
- Assume no next for now. This is fixed up above on the next
- iteration, if there's any. */
- target_tracepoint.next = NULL;
- /* Need to clear this here too, since we're downloading the
- tracepoints before clearing our own copy. */
- target_tracepoint.hit_count = 0;
-
- target_write_memory (tpptr, (unsigned char *) &target_tracepoint,
- sizeof (target_tracepoint));
-
- if (tpoint->cond)
- write_inferior_data_pointer (tpptr
- + offsetof (struct tracepoint, cond),
- download_agent_expr (tpoint->cond));
-
- if (tpoint->numactions)
- {
- int i;
- CORE_ADDR actions_array;
-
- /* The pointers array. */
- actions_array
- = target_malloc (sizeof (*tpoint->actions) * tpoint->numactions);
- write_inferior_data_pointer (tpptr + offsetof (struct tracepoint,
- actions),
- actions_array);
-
- /* Now for each pointer, download the action. */
- for (i = 0; i < tpoint->numactions; i++)
- {
- struct tracepoint_action *action = tpoint->actions[i];
- CORE_ADDR ipa_action = tracepoint_action_download (action);
-
- if (ipa_action != 0)
- write_inferior_data_pointer (actions_array
- + i * sizeof (*tpoint->actions),
- ipa_action);
- }
- }
-}
-
-#define IPA_PROTO_FAST_TRACE_FLAG 0
-#define IPA_PROTO_FAST_TRACE_ADDR_ON_TARGET 2
-#define IPA_PROTO_FAST_TRACE_JUMP_PAD 10
-#define IPA_PROTO_FAST_TRACE_FJUMP_SIZE 18
-#define IPA_PROTO_FAST_TRACE_FJUMP_INSN 22
-
-/* Send a command to agent to download and install tracepoint TPOINT. */
-
-static int
-tracepoint_send_agent (struct tracepoint *tpoint)
-{
- char buf[IPA_CMD_BUF_SIZE];
- char *p;
- int i, ret;
-
- p = buf;
- strcpy (p, "FastTrace:");
- p += 10;
-
- COPY_FIELD_TO_BUF (p, tpoint, number);
- COPY_FIELD_TO_BUF (p, tpoint, address);
- COPY_FIELD_TO_BUF (p, tpoint, type);
- COPY_FIELD_TO_BUF (p, tpoint, enabled);
- COPY_FIELD_TO_BUF (p, tpoint, step_count);
- COPY_FIELD_TO_BUF (p, tpoint, pass_count);
- COPY_FIELD_TO_BUF (p, tpoint, numactions);
- COPY_FIELD_TO_BUF (p, tpoint, hit_count);
- COPY_FIELD_TO_BUF (p, tpoint, traceframe_usage);
- COPY_FIELD_TO_BUF (p, tpoint, compiled_cond);
- COPY_FIELD_TO_BUF (p, tpoint, orig_size);
-
- /* condition */
- p = agent_expr_send (p, tpoint->cond);
-
- /* tracepoint_action */
- for (i = 0; i < tpoint->numactions; i++)
- {
- struct tracepoint_action *action = tpoint->actions[i];
-
- p[0] = action->type;
- p = tracepoint_action_send (&p[1], action);
- }
-
- get_jump_space_head ();
- /* Copy the value of GDB_JUMP_PAD_HEAD to command buffer, so that
- agent can use jump pad from it. */
- if (tpoint->type == fast_tracepoint)
- {
- memcpy (p, &gdb_jump_pad_head, 8);
- p += 8;
- }
-
- ret = run_inferior_command (buf, (int) (ptrdiff_t) (p - buf));
- if (ret)
- return ret;
-
- if (!startswith (buf, "OK"))
- return 1;
-
- /* The value of tracepoint's target address is stored in BUF. */
- memcpy (&tpoint->obj_addr_on_target,
- &buf[IPA_PROTO_FAST_TRACE_ADDR_ON_TARGET], 8);
-
- if (tpoint->type == fast_tracepoint)
- {
- unsigned char *insn
- = (unsigned char *) &buf[IPA_PROTO_FAST_TRACE_FJUMP_INSN];
- int fjump_size;
-
- trace_debug ("agent: read from cmd_buf 0x%x 0x%x\n",
- (unsigned int) tpoint->obj_addr_on_target,
- (unsigned int) gdb_jump_pad_head);
-
- memcpy (&gdb_jump_pad_head, &buf[IPA_PROTO_FAST_TRACE_JUMP_PAD], 8);
-
- /* This has been done in agent. We should also set up record for it. */
- memcpy (&fjump_size, &buf[IPA_PROTO_FAST_TRACE_FJUMP_SIZE], 4);
- /* Wire it in. */
- tpoint->handle
- = set_fast_tracepoint_jump (tpoint->address, insn, fjump_size);
- }
-
- return 0;
-}
-
-static void
-download_tracepoint (struct tracepoint *tpoint)
-{
- struct tracepoint *tp, *tp_prev;
-
- if (tpoint->type != fast_tracepoint
- && tpoint->type != static_tracepoint)
- return;
-
- download_tracepoint_1 (tpoint);
-
- /* Find the previous entry of TPOINT, which is fast tracepoint or
- static tracepoint. */
- tp_prev = NULL;
- for (tp = tracepoints; tp != tpoint; tp = tp->next)
- {
- if (tp->type == fast_tracepoint || tp->type == static_tracepoint)
- tp_prev = tp;
- }
-
- if (tp_prev)
- {
- CORE_ADDR tp_prev_target_next_addr;
-
- /* Insert TPOINT after TP_PREV in IPA. */
- if (read_inferior_data_pointer (tp_prev->obj_addr_on_target
- + offsetof (struct tracepoint, next),
- &tp_prev_target_next_addr))
- {
- internal_error (__FILE__, __LINE__,
- "error reading `tp_prev->next'");
- }
-
- /* tpoint->next = tp_prev->next */
- write_inferior_data_pointer (tpoint->obj_addr_on_target
- + offsetof (struct tracepoint, next),
- tp_prev_target_next_addr);
- /* tp_prev->next = tpoint */
- write_inferior_data_pointer (tp_prev->obj_addr_on_target
- + offsetof (struct tracepoint, next),
- tpoint->obj_addr_on_target);
- }
- else
- /* First object in list, set the head pointer in the
- inferior. */
- write_inferior_data_pointer (ipa_sym_addrs.addr_tracepoints,
- tpoint->obj_addr_on_target);
-
-}
-
-static void
-download_trace_state_variables (void)
-{
- CORE_ADDR ptr = 0, prev_ptr = 0;
- struct trace_state_variable *tsv;
-
- /* Start out empty. */
- write_inferior_data_pointer (ipa_sym_addrs.addr_trace_state_variables, 0);
-
- for (tsv = trace_state_variables; tsv != NULL; tsv = tsv->next)
- {
- struct trace_state_variable target_tsv;
-
- /* TSV's with a getter have been initialized equally in both the
- inferior and GDBserver. Skip them. */
- if (tsv->getter != NULL)
- continue;
-
- target_tsv = *tsv;
-
- prev_ptr = ptr;
- ptr = target_malloc (sizeof (*tsv));
-
- if (tsv == trace_state_variables)
- {
- /* First object in list, set the head pointer in the
- inferior. */
-
- write_inferior_data_pointer (ipa_sym_addrs.addr_trace_state_variables,
- ptr);
- }
- else
- {
- write_inferior_data_pointer (prev_ptr
- + offsetof (struct trace_state_variable,
- next),
- ptr);
- }
-
- /* Write the whole object. We'll fix up its pointers in a bit.
- Assume no next, fixup when needed. */
- target_tsv.next = NULL;
-
- target_write_memory (ptr, (unsigned char *) &target_tsv,
- sizeof (target_tsv));
-
- if (tsv->name != NULL)
- {
- size_t size = strlen (tsv->name) + 1;
- CORE_ADDR name_addr = target_malloc (size);
- target_write_memory (name_addr,
- (unsigned char *) tsv->name, size);
- write_inferior_data_pointer (ptr
- + offsetof (struct trace_state_variable,
- name),
- name_addr);
- }
-
- gdb_assert (tsv->getter == NULL);
- }
-
- if (prev_ptr != 0)
- {
- /* Fixup the next pointer in the last item in the list. */
- write_inferior_data_pointer (prev_ptr
- + offsetof (struct trace_state_variable,
- next), 0);
- }
-}
-
-/* Upload complete trace frames out of the IP Agent's trace buffer
- into GDBserver's trace buffer. This always uploads either all or
- no trace frames. This is the counter part of
- `trace_alloc_trace_buffer'. See its description of the atomic
- syncing mechanism. */
-
-static void
-upload_fast_traceframes (void)
-{
- unsigned int ipa_traceframe_read_count, ipa_traceframe_write_count;
- unsigned int ipa_traceframe_read_count_racy, ipa_traceframe_write_count_racy;
- CORE_ADDR tf;
- struct ipa_trace_buffer_control ipa_trace_buffer_ctrl;
- unsigned int curr_tbctrl_idx;
- unsigned int ipa_trace_buffer_ctrl_curr;
- unsigned int ipa_trace_buffer_ctrl_curr_old;
- CORE_ADDR ipa_trace_buffer_ctrl_addr;
- struct breakpoint *about_to_request_buffer_space_bkpt;
- CORE_ADDR ipa_trace_buffer_lo;
- CORE_ADDR ipa_trace_buffer_hi;
-
- if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count,
- &ipa_traceframe_read_count_racy))
- {
- /* This will happen in most targets if the current thread is
- running. */
- return;
- }
-
- if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count,
- &ipa_traceframe_write_count_racy))
- return;
-
- trace_debug ("ipa_traceframe_count (racy area): %d (w=%d, r=%d)",
- ipa_traceframe_write_count_racy
- - ipa_traceframe_read_count_racy,
- ipa_traceframe_write_count_racy,
- ipa_traceframe_read_count_racy);
-
- if (ipa_traceframe_write_count_racy == ipa_traceframe_read_count_racy)
- return;
-
- about_to_request_buffer_space_bkpt
- = set_breakpoint_at (ipa_sym_addrs.addr_about_to_request_buffer_space,
- NULL);
-
- if (read_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr,
- &ipa_trace_buffer_ctrl_curr))
- return;
-
- ipa_trace_buffer_ctrl_curr_old = ipa_trace_buffer_ctrl_curr;
-
- curr_tbctrl_idx = ipa_trace_buffer_ctrl_curr & ~GDBSERVER_FLUSH_COUNT_MASK;
-
- {
- unsigned int prev, counter;
-
- /* Update the token, with new counters, and the GDBserver stamp
- bit. Alway reuse the current TBC index. */
- prev = ipa_trace_buffer_ctrl_curr & GDBSERVER_FLUSH_COUNT_MASK_CURR;
- counter = (prev + 0x100) & GDBSERVER_FLUSH_COUNT_MASK_CURR;
-
- ipa_trace_buffer_ctrl_curr = (GDBSERVER_UPDATED_FLUSH_COUNT_BIT
- | (prev << 12)
- | counter
- | curr_tbctrl_idx);
- }
-
- if (write_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr,
- ipa_trace_buffer_ctrl_curr))
- return;
-
- trace_debug ("Lib: Committed %08x -> %08x",
- ipa_trace_buffer_ctrl_curr_old,
- ipa_trace_buffer_ctrl_curr);
-
- /* Re-read these, now that we've installed the
- `about_to_request_buffer_space' breakpoint/lock. A thread could
- have finished a traceframe between the last read of these
- counters and setting the breakpoint above. If we start
- uploading, we never want to leave this function with
- traceframe_read_count != 0, otherwise, GDBserver could end up
- incrementing the counter tokens more than once (due to event loop
- nesting), which would break the IP agent's "effective" detection
- (see trace_alloc_trace_buffer). */
- if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count,
- &ipa_traceframe_read_count))
- return;
- if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count,
- &ipa_traceframe_write_count))
- return;
-
- if (debug_threads)
- {
- trace_debug ("ipa_traceframe_count (blocked area): %d (w=%d, r=%d)",
- ipa_traceframe_write_count - ipa_traceframe_read_count,
- ipa_traceframe_write_count, ipa_traceframe_read_count);
-
- if (ipa_traceframe_write_count != ipa_traceframe_write_count_racy
- || ipa_traceframe_read_count != ipa_traceframe_read_count_racy)
- trace_debug ("note that ipa_traceframe_count's parts changed");
- }
-
- /* Get the address of the current TBC object (the IP agent has an
- array of 3 such objects). The index is stored in the TBC
- token. */
- ipa_trace_buffer_ctrl_addr = ipa_sym_addrs.addr_trace_buffer_ctrl;
- ipa_trace_buffer_ctrl_addr
- += sizeof (struct ipa_trace_buffer_control) * curr_tbctrl_idx;
-
- if (read_inferior_memory (ipa_trace_buffer_ctrl_addr,
- (unsigned char *) &ipa_trace_buffer_ctrl,
- sizeof (struct ipa_trace_buffer_control)))
- return;
-
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_lo,
- &ipa_trace_buffer_lo))
- return;
- if (read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_hi,
- &ipa_trace_buffer_hi))
- return;
-
- /* Offsets are easier to grok for debugging than raw addresses,
- especially for the small trace buffer sizes that are useful for
- testing. */
- trace_debug ("Lib: Trace buffer [%d] start=%d free=%d "
- "endfree=%d wrap=%d hi=%d",
- curr_tbctrl_idx,
- (int) (ipa_trace_buffer_ctrl.start - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.free - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.end_free - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_hi - ipa_trace_buffer_lo));
-
- /* Note that the IPA's buffer is always circular. */
-
-#define IPA_FIRST_TRACEFRAME() (ipa_trace_buffer_ctrl.start)
-
-#define IPA_NEXT_TRACEFRAME_1(TF, TFOBJ) \
- ((TF) + sizeof (struct traceframe) + (TFOBJ)->data_size)
-
-#define IPA_NEXT_TRACEFRAME(TF, TFOBJ) \
- (IPA_NEXT_TRACEFRAME_1 (TF, TFOBJ) \
- - ((IPA_NEXT_TRACEFRAME_1 (TF, TFOBJ) >= ipa_trace_buffer_ctrl.wrap) \
- ? (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo) \
- : 0))
-
- tf = IPA_FIRST_TRACEFRAME ();
-
- while (ipa_traceframe_write_count - ipa_traceframe_read_count)
- {
- struct tracepoint *tpoint;
- struct traceframe *tframe;
- unsigned char *block;
- struct traceframe ipa_tframe;
-
- if (read_inferior_memory (tf, (unsigned char *) &ipa_tframe,
- offsetof (struct traceframe, data)))
- error ("Uploading: couldn't read traceframe at %s\n", paddress (tf));
-
- if (ipa_tframe.tpnum == 0)
- {
- internal_error (__FILE__, __LINE__,
- "Uploading: No (more) fast traceframes, but"
- " ipa_traceframe_count == %u??\n",
- ipa_traceframe_write_count
- - ipa_traceframe_read_count);
- }
-
- /* Note that this will be incorrect for multi-location
- tracepoints... */
- tpoint = find_next_tracepoint_by_number (NULL, ipa_tframe.tpnum);
-
- tframe = add_traceframe (tpoint);
- if (tframe == NULL)
- {
- trace_buffer_is_full = 1;
- trace_debug ("Uploading: trace buffer is full");
- }
- else
- {
- /* Copy the whole set of blocks in one go for now. FIXME:
- split this in smaller blocks. */
- block = add_traceframe_block (tframe, tpoint,
- ipa_tframe.data_size);
- if (block != NULL)
- {
- if (read_inferior_memory (tf
- + offsetof (struct traceframe, data),
- block, ipa_tframe.data_size))
- error ("Uploading: Couldn't read traceframe data at %s\n",
- paddress (tf + offsetof (struct traceframe, data)));
- }
-
- trace_debug ("Uploading: traceframe didn't fit");
- finish_traceframe (tframe);
- }
-
- tf = IPA_NEXT_TRACEFRAME (tf, &ipa_tframe);
-
- /* If we freed the traceframe that wrapped around, go back
- to the non-wrap case. */
- if (tf < ipa_trace_buffer_ctrl.start)
- {
- trace_debug ("Lib: Discarding past the wraparound");
- ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
- }
- ipa_trace_buffer_ctrl.start = tf;
- ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_ctrl.start;
- ++ipa_traceframe_read_count;
-
- if (ipa_trace_buffer_ctrl.start == ipa_trace_buffer_ctrl.free
- && ipa_trace_buffer_ctrl.start == ipa_trace_buffer_ctrl.end_free)
- {
- trace_debug ("Lib: buffer is fully empty. "
- "Trace buffer [%d] start=%d free=%d endfree=%d",
- curr_tbctrl_idx,
- (int) (ipa_trace_buffer_ctrl.start
- - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.free
- - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.end_free
- - ipa_trace_buffer_lo));
-
- ipa_trace_buffer_ctrl.start = ipa_trace_buffer_lo;
- ipa_trace_buffer_ctrl.free = ipa_trace_buffer_lo;
- ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_hi;
- ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
- }
-
- trace_debug ("Uploaded a traceframe\n"
- "Lib: Trace buffer [%d] start=%d free=%d "
- "endfree=%d wrap=%d hi=%d",
- curr_tbctrl_idx,
- (int) (ipa_trace_buffer_ctrl.start - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.free - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.end_free
- - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo),
- (int) (ipa_trace_buffer_hi - ipa_trace_buffer_lo));
- }
-
- if (target_write_memory (ipa_trace_buffer_ctrl_addr,
- (unsigned char *) &ipa_trace_buffer_ctrl,
- sizeof (struct ipa_trace_buffer_control)))
- return;
-
- write_inferior_integer (ipa_sym_addrs.addr_traceframe_read_count,
- ipa_traceframe_read_count);
-
- trace_debug ("Done uploading traceframes [%d]\n", curr_tbctrl_idx);
-
- pause_all (1);
-
- delete_breakpoint (about_to_request_buffer_space_bkpt);
- about_to_request_buffer_space_bkpt = NULL;
-
- unpause_all (1);
-
- if (trace_buffer_is_full)
- stop_tracing ();
-}
-#endif
-
-#ifdef IN_PROCESS_AGENT
-
-IP_AGENT_EXPORT_VAR int ust_loaded;
-IP_AGENT_EXPORT_VAR char cmd_buf[IPA_CMD_BUF_SIZE];
-
-#ifdef HAVE_UST
-
-/* Static tracepoints. */
-
-/* UST puts a "struct tracepoint" in the global namespace, which
- conflicts with our tracepoint. Arguably, being a library, it
- shouldn't take ownership of such a generic name. We work around it
- here. */
-#define tracepoint ust_tracepoint
-#include <ust/ust.h>
-#undef tracepoint
-
-extern int serialize_to_text (char *outbuf, int bufsize,
- const char *fmt, va_list ap);
-
-#define GDB_PROBE_NAME "gdb"
-
-/* We dynamically search for the UST symbols instead of linking them
- in. This lets the user decide if the application uses static
- tracepoints, instead of always pulling libust.so in. This vector
- holds pointers to all functions we care about. */
-
-static struct
-{
- int (*serialize_to_text) (char *outbuf, int bufsize,
- const char *fmt, va_list ap);
-
- int (*ltt_probe_register) (struct ltt_available_probe *pdata);
- int (*ltt_probe_unregister) (struct ltt_available_probe *pdata);
-
- int (*ltt_marker_connect) (const char *channel, const char *mname,
- const char *pname);
- int (*ltt_marker_disconnect) (const char *channel, const char *mname,
- const char *pname);
-
- void (*marker_iter_start) (struct marker_iter *iter);
- void (*marker_iter_next) (struct marker_iter *iter);
- void (*marker_iter_stop) (struct marker_iter *iter);
- void (*marker_iter_reset) (struct marker_iter *iter);
-} ust_ops;
-
-#include <dlfcn.h>
-
-/* Cast through typeof to catch incompatible API changes. Since UST
- only builds with gcc, we can freely use gcc extensions here
- too. */
-#define GET_UST_SYM(SYM) \
- do \
- { \
- if (ust_ops.SYM == NULL) \
- ust_ops.SYM = (typeof (&SYM)) dlsym (RTLD_DEFAULT, #SYM); \
- if (ust_ops.SYM == NULL) \
- return 0; \
- } while (0)
-
-#define USTF(SYM) ust_ops.SYM
-
-/* Get pointers to all libust.so functions we care about. */
-
-static int
-dlsym_ust (void)
-{
- GET_UST_SYM (serialize_to_text);
-
- GET_UST_SYM (ltt_probe_register);
- GET_UST_SYM (ltt_probe_unregister);
- GET_UST_SYM (ltt_marker_connect);
- GET_UST_SYM (ltt_marker_disconnect);
-
- GET_UST_SYM (marker_iter_start);
- GET_UST_SYM (marker_iter_next);
- GET_UST_SYM (marker_iter_stop);
- GET_UST_SYM (marker_iter_reset);
-
- ust_loaded = 1;
- return 1;
-}
-
-/* Given an UST marker, return the matching gdb static tracepoint.
- The match is done by address. */
-
-static struct tracepoint *
-ust_marker_to_static_tracepoint (const struct marker *mdata)
-{
- struct tracepoint *tpoint;
-
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
- {
- if (tpoint->type != static_tracepoint)
- continue;
-
- if (tpoint->address == (uintptr_t) mdata->location)
- return tpoint;
- }
-
- return NULL;
-}
-
-/* The probe function we install on lttng/ust markers. Whenever a
- probed ust marker is hit, this function is called. This is similar
- to gdb_collect, only for static tracepoints, instead of fast
- tracepoints. */
-
-static void
-gdb_probe (const struct marker *mdata, void *probe_private,
- struct registers *regs, void *call_private,
- const char *fmt, va_list *args)
-{
- struct tracepoint *tpoint;
- struct static_tracepoint_ctx ctx;
- const struct target_desc *ipa_tdesc;
-
- /* Don't do anything until the trace run is completely set up. */
- if (!tracing)
- {
- trace_debug ("gdb_probe: not tracing\n");
- return;
- }
-
- ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
- ctx.base.type = static_tracepoint;
- ctx.regcache_initted = 0;
- ctx.regs = regs;
- ctx.fmt = fmt;
- ctx.args = args;
-
- /* Wrap the regblock in a register cache (in the stack, we don't
- want to malloc here). */
- ctx.regspace = alloca (ipa_tdesc->registers_size);
- if (ctx.regspace == NULL)
- {
- trace_debug ("Trace buffer block allocation failed, skipping");
- return;
- }
-
- tpoint = ust_marker_to_static_tracepoint (mdata);
- if (tpoint == NULL)
- {
- trace_debug ("gdb_probe: marker not known: "
- "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"",
- mdata->location, mdata->channel,
- mdata->name, mdata->format);
- return;
- }
-
- if (!tpoint->enabled)
- {
- trace_debug ("gdb_probe: tracepoint disabled");
- return;
- }
-
- ctx.tpoint = tpoint;
-
- trace_debug ("gdb_probe: collecting marker: "
- "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"",
- mdata->location, mdata->channel,
- mdata->name, mdata->format);
-
- /* Test the condition if present, and collect if true. */
- if (tpoint->cond == NULL
- || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
- tpoint))
- {
- collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
- tpoint->address, tpoint);
-
- if (stopping_tracepoint
- || trace_buffer_is_full
- || expr_eval_result != expr_eval_no_error)
- stop_tracing ();
- }
- else
- {
- /* If there was a condition and it evaluated to false, the only
- way we would stop tracing is if there was an error during
- condition expression evaluation. */
- if (expr_eval_result != expr_eval_no_error)
- stop_tracing ();
- }
-}
-
-/* Called if the gdb static tracepoint requested collecting "$_sdata",
- static tracepoint string data. This is a string passed to the
- tracing library by the user, at the time of the tracepoint marker
- call. E.g., in the UST marker call:
-
- trace_mark (ust, bar33, "str %s", "FOOBAZ");
-
- the collected data is "str FOOBAZ".
-*/
-
-static void
-collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- struct traceframe *tframe)
-{
- struct static_tracepoint_ctx *umd = (struct static_tracepoint_ctx *) ctx;
- unsigned char *bufspace;
- int size;
- va_list copy;
- unsigned short blocklen;
-
- if (umd == NULL)
- {
- trace_debug ("Wanted to collect static trace data, "
- "but there's no static trace data");
- return;
- }
-
- va_copy (copy, *umd->args);
- size = USTF(serialize_to_text) (NULL, 0, umd->fmt, copy);
- va_end (copy);
-
- trace_debug ("Want to collect ust data");
-
- /* 'S' + size + string */
- bufspace = add_traceframe_block (tframe, umd->tpoint,
- 1 + sizeof (blocklen) + size + 1);
- if (bufspace == NULL)
- {
- trace_debug ("Trace buffer block allocation failed, skipping");
- return;
- }
-
- /* Identify a static trace data block. */
- *bufspace = 'S';
-
- blocklen = size + 1;
- memcpy (bufspace + 1, &blocklen, sizeof (blocklen));
-
- va_copy (copy, *umd->args);
- USTF(serialize_to_text) ((char *) bufspace + 1 + sizeof (blocklen),
- size + 1, umd->fmt, copy);
- va_end (copy);
-
- trace_debug ("Storing static tracepoint data in regblock: %s",
- bufspace + 1 + sizeof (blocklen));
-}
-
-/* The probe to register with lttng/ust. */
-static struct ltt_available_probe gdb_ust_probe =
- {
- GDB_PROBE_NAME,
- NULL,
- gdb_probe,
- };
-
-#endif /* HAVE_UST */
-#endif /* IN_PROCESS_AGENT */
-
-#ifndef IN_PROCESS_AGENT
-
-/* Ask the in-process agent to run a command. Since we don't want to
- have to handle the IPA hitting breakpoints while running the
- command, we pause all threads, remove all breakpoints, and then set
- the helper thread re-running. We communicate with the helper
- thread by means of direct memory xfering, and a socket for
- synchronization. */
-
-static int
-run_inferior_command (char *cmd, int len)
-{
- int err = -1;
- int pid = current_ptid.pid ();
-
- trace_debug ("run_inferior_command: running: %s", cmd);
-
- pause_all (0);
- uninsert_all_breakpoints ();
-
- err = agent_run_command (pid, (const char *) cmd, len);
-
- reinsert_all_breakpoints ();
- unpause_all (0);
-
- return err;
-}
-
-#else /* !IN_PROCESS_AGENT */
-
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path)
-#endif
-
-/* Where we put the socked used for synchronization. */
-#define SOCK_DIR P_tmpdir
-
-/* Thread ID of the helper thread. GDBserver reads this to know which
- is the help thread. This is an LWP id on Linux. */
-EXTERN_C_PUSH
-IP_AGENT_EXPORT_VAR int helper_thread_id;
-EXTERN_C_POP
-
-static int
-init_named_socket (const char *name)
-{
- int result, fd;
- struct sockaddr_un addr;
-
- result = fd = socket (PF_UNIX, SOCK_STREAM, 0);
- if (result == -1)
- {
- warning ("socket creation failed: %s", safe_strerror (errno));
- return -1;
- }
-
- addr.sun_family = AF_UNIX;
-
- strncpy (addr.sun_path, name, UNIX_PATH_MAX);
- addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
-
- result = access (name, F_OK);
- if (result == 0)
- {
- /* File exists. */
- result = unlink (name);
- if (result == -1)
- {
- warning ("unlink failed: %s", safe_strerror (errno));
- close (fd);
- return -1;
- }
- warning ("socket %s already exists; overwriting", name);
- }
-
- result = bind (fd, (struct sockaddr *) &addr, sizeof (addr));
- if (result == -1)
- {
- warning ("bind failed: %s", safe_strerror (errno));
- close (fd);
- return -1;
- }
-
- result = listen (fd, 1);
- if (result == -1)
- {
- warning ("listen: %s", safe_strerror (errno));
- close (fd);
- return -1;
- }
-
- return fd;
-}
-
-static char agent_socket_name[UNIX_PATH_MAX];
-
-static int
-gdb_agent_socket_init (void)
-{
- int result, fd;
-
- result = xsnprintf (agent_socket_name, UNIX_PATH_MAX, "%s/gdb_ust%d",
- SOCK_DIR, getpid ());
- if (result >= UNIX_PATH_MAX)
- {
- trace_debug ("string overflow allocating socket name");
- return -1;
- }
-
- fd = init_named_socket (agent_socket_name);
- if (fd < 0)
- warning ("Error initializing named socket (%s) for communication with the "
- "ust helper thread. Check that directory exists and that it "
- "is writable.", agent_socket_name);
-
- return fd;
-}
-
-#ifdef HAVE_UST
-
-/* The next marker to be returned on a qTsSTM command. */
-static const struct marker *next_st;
-
-/* Returns the first known marker. */
-
-struct marker *
-first_marker (void)
-{
- struct marker_iter iter;
-
- USTF(marker_iter_reset) (&iter);
- USTF(marker_iter_start) (&iter);
-
- return iter.marker;
-}
-
-/* Returns the marker following M. */
-
-const struct marker *
-next_marker (const struct marker *m)
-{
- struct marker_iter iter;
-
- USTF(marker_iter_reset) (&iter);
- USTF(marker_iter_start) (&iter);
-
- for (; iter.marker != NULL; USTF(marker_iter_next) (&iter))
- {
- if (iter.marker == m)
- {
- USTF(marker_iter_next) (&iter);
- return iter.marker;
- }
- }
-
- return NULL;
-}
-
-/* Return an hexstr version of the STR C string, fit for sending to
- GDB. */
-
-static char *
-cstr_to_hexstr (const char *str)
-{
- int len = strlen (str);
- char *hexstr = xmalloc (len * 2 + 1);
- bin2hex ((gdb_byte *) str, hexstr, len);
- return hexstr;
-}
-
-/* Compose packet that is the response to the qTsSTM/qTfSTM/qTSTMat
- packets. */
-
-static void
-response_ust_marker (char *packet, const struct marker *st)
-{
- char *strid, *format, *tmp;
-
- next_st = next_marker (st);
-
- tmp = xmalloc (strlen (st->channel) + 1 +
- strlen (st->name) + 1);
- sprintf (tmp, "%s/%s", st->channel, st->name);
-
- strid = cstr_to_hexstr (tmp);
- free (tmp);
-
- format = cstr_to_hexstr (st->format);
-
- sprintf (packet, "m%s:%s:%s",
- paddress ((uintptr_t) st->location),
- strid,
- format);
-
- free (strid);
- free (format);
-}
-
-/* Return the first static tracepoint, and initialize the state
- machine that will iterate through all the static tracepoints. */
-
-static void
-cmd_qtfstm (char *packet)
-{
- trace_debug ("Returning first trace state variable definition");
-
- if (first_marker ())
- response_ust_marker (packet, first_marker ());
- else
- strcpy (packet, "l");
-}
-
-/* Return additional trace state variable definitions. */
-
-static void
-cmd_qtsstm (char *packet)
-{
- trace_debug ("Returning static tracepoint");
-
- if (next_st)
- response_ust_marker (packet, next_st);
- else
- strcpy (packet, "l");
-}
-
-/* Disconnect the GDB probe from a marker at a given address. */
-
-static void
-unprobe_marker_at (char *packet)
-{
- char *p = packet;
- ULONGEST address;
- struct marker_iter iter;
-
- p += sizeof ("unprobe_marker_at:") - 1;
-
- p = unpack_varlen_hex (p, &address);
-
- USTF(marker_iter_reset) (&iter);
- USTF(marker_iter_start) (&iter);
- for (; iter.marker != NULL; USTF(marker_iter_next) (&iter))
- if ((uintptr_t ) iter.marker->location == address)
- {
- int result;
-
- result = USTF(ltt_marker_disconnect) (iter.marker->channel,
- iter.marker->name,
- GDB_PROBE_NAME);
- if (result < 0)
- warning ("could not disable marker %s/%s",
- iter.marker->channel, iter.marker->name);
- break;
- }
-}
-
-/* Connect the GDB probe to a marker at a given address. */
-
-static int
-probe_marker_at (char *packet)
-{
- char *p = packet;
- ULONGEST address;
- struct marker_iter iter;
- struct marker *m;
-
- p += sizeof ("probe_marker_at:") - 1;
-
- p = unpack_varlen_hex (p, &address);
-
- USTF(marker_iter_reset) (&iter);
-
- for (USTF(marker_iter_start) (&iter), m = iter.marker;
- m != NULL;
- USTF(marker_iter_next) (&iter), m = iter.marker)
- if ((uintptr_t ) m->location == address)
- {
- int result;
-
- trace_debug ("found marker for address. "
- "ltt_marker_connect (marker = %s/%s)",
- m->channel, m->name);
-
- result = USTF(ltt_marker_connect) (m->channel, m->name,
- GDB_PROBE_NAME);
- if (result && result != -EEXIST)
- trace_debug ("ltt_marker_connect (marker = %s/%s, errno = %d)",
- m->channel, m->name, -result);
-
- if (result < 0)
- {
- sprintf (packet, "E.could not connect marker: channel=%s, name=%s",
- m->channel, m->name);
- return -1;
- }
-
- strcpy (packet, "OK");
- return 0;
- }
-
- sprintf (packet, "E.no marker found at 0x%s", paddress (address));
- return -1;
-}
-
-static int
-cmd_qtstmat (char *packet)
-{
- char *p = packet;
- ULONGEST address;
- struct marker_iter iter;
- struct marker *m;
-
- p += sizeof ("qTSTMat:") - 1;
-
- p = unpack_varlen_hex (p, &address);
-
- USTF(marker_iter_reset) (&iter);
-
- for (USTF(marker_iter_start) (&iter), m = iter.marker;
- m != NULL;
- USTF(marker_iter_next) (&iter), m = iter.marker)
- if ((uintptr_t ) m->location == address)
- {
- response_ust_marker (packet, m);
- return 0;
- }
-
- strcpy (packet, "l");
- return -1;
-}
-
-static void
-gdb_ust_init (void)
-{
- if (!dlsym_ust ())
- return;
-
- USTF(ltt_probe_register) (&gdb_ust_probe);
-}
-
-#endif /* HAVE_UST */
-
-#include <sys/syscall.h>
-
-static void
-gdb_agent_remove_socket (void)
-{
- unlink (agent_socket_name);
-}
-
-/* Helper thread of agent. */
-
-static void *
-gdb_agent_helper_thread (void *arg)
-{
- int listen_fd;
-
- atexit (gdb_agent_remove_socket);
-
- while (1)
- {
- listen_fd = gdb_agent_socket_init ();
-
- if (helper_thread_id == 0)
- helper_thread_id = syscall (SYS_gettid);
-
- if (listen_fd == -1)
- {
- warning ("could not create sync socket");
- break;
- }
-
- while (1)
- {
- socklen_t tmp;
- struct sockaddr_un sockaddr;
- int fd;
- char buf[1];
- int ret;
- int stop_loop = 0;
-
- tmp = sizeof (sockaddr);
-
- do
- {
- fd = accept (listen_fd, (struct sockaddr *) &sockaddr, &tmp);
- }
- /* It seems an ERESTARTSYS can escape out of accept. */
- while (fd == -512 || (fd == -1 && errno == EINTR));
-
- if (fd < 0)
- {
- warning ("Accept returned %d, error: %s",
- fd, safe_strerror (errno));
- break;
- }
-
- do
- {
- ret = read (fd, buf, 1);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1)
- {
- warning ("reading socket (fd=%d) failed with %s",
- fd, safe_strerror (errno));
- close (fd);
- break;
- }
-
- if (cmd_buf[0])
- {
- if (startswith (cmd_buf, "close"))
- {
- stop_loop = 1;
- }
-#ifdef HAVE_UST
- else if (strcmp ("qTfSTM", cmd_buf) == 0)
- {
- cmd_qtfstm (cmd_buf);
- }
- else if (strcmp ("qTsSTM", cmd_buf) == 0)
- {
- cmd_qtsstm (cmd_buf);
- }
- else if (startswith (cmd_buf, "unprobe_marker_at:"))
- {
- unprobe_marker_at (cmd_buf);
- }
- else if (startswith (cmd_buf, "probe_marker_at:"))
- {
- probe_marker_at (cmd_buf);
- }
- else if (startswith (cmd_buf, "qTSTMat:"))
- {
- cmd_qtstmat (cmd_buf);
- }
-#endif /* HAVE_UST */
- }
-
- /* Fix compiler's warning: ignoring return value of 'write'. */
- ret = write (fd, buf, 1);
- close (fd);
-
- if (stop_loop)
- {
- close (listen_fd);
- unlink (agent_socket_name);
-
- /* Sleep endlessly to wait the whole inferior stops. This
- thread can not exit because GDB or GDBserver may still need
- 'current_thread' (representing this thread) to access
- inferior memory. Otherwise, this thread exits earlier than
- other threads, and 'current_thread' is set to NULL. */
- while (1)
- sleep (10);
- }
- }
- }
-
- return NULL;
-}
-
-#include <signal.h>
-#include <pthread.h>
-
-EXTERN_C_PUSH
-IP_AGENT_EXPORT_VAR int gdb_agent_capability = AGENT_CAPA_STATIC_TRACE;
-EXTERN_C_POP
-
-static void
-gdb_agent_init (void)
-{
- int res;
- pthread_t thread;
- sigset_t new_mask;
- sigset_t orig_mask;
-
- /* We want the helper thread to be as transparent as possible, so
- have it inherit an all-signals-blocked mask. */
-
- sigfillset (&new_mask);
- res = pthread_sigmask (SIG_SETMASK, &new_mask, &orig_mask);
- if (res)
- perror_with_name ("pthread_sigmask (1)");
-
- res = pthread_create (&thread,
- NULL,
- gdb_agent_helper_thread,
- NULL);
-
- res = pthread_sigmask (SIG_SETMASK, &orig_mask, NULL);
- if (res)
- perror_with_name ("pthread_sigmask (2)");
-
- while (helper_thread_id == 0)
- usleep (1);
-
-#ifdef HAVE_UST
- gdb_ust_init ();
-#endif
-}
-
-#include <sys/mman.h>
-
-IP_AGENT_EXPORT_VAR char *gdb_tp_heap_buffer;
-IP_AGENT_EXPORT_VAR char *gdb_jump_pad_buffer;
-IP_AGENT_EXPORT_VAR char *gdb_jump_pad_buffer_end;
-IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer;
-IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer_end;
-IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer_error;
-
-/* Record the result of getting buffer space for fast tracepoint
- trampolines. Any error message is copied, since caller may not be
- using persistent storage. */
-
-void
-set_trampoline_buffer_space (CORE_ADDR begin, CORE_ADDR end, char *errmsg)
-{
- gdb_trampoline_buffer = (char *) (uintptr_t) begin;
- gdb_trampoline_buffer_end = (char *) (uintptr_t) end;
- if (errmsg)
- strncpy (gdb_trampoline_buffer_error, errmsg, 99);
- else
- strcpy (gdb_trampoline_buffer_error, "no buffer passed");
-}
-
-static void __attribute__ ((constructor))
-initialize_tracepoint_ftlib (void)
-{
- initialize_tracepoint ();
-
- gdb_agent_init ();
-}
-
-#ifndef HAVE_GETAUXVAL
-/* Retrieve the value of TYPE from the auxiliary vector. If TYPE is not
- found, 0 is returned. This function is provided if glibc is too old. */
-
-unsigned long
-getauxval (unsigned long type)
-{
- unsigned long data[2];
- FILE *f = fopen ("/proc/self/auxv", "r");
- unsigned long value = 0;
-
- if (f == NULL)
- return 0;
-
- while (fread (data, sizeof (data), 1, f) > 0)
- {
- if (data[0] == type)
- {
- value = data[1];
- break;
- }
- }
-
- fclose (f);
- return value;
-}
-#endif
-
-#endif /* IN_PROCESS_AGENT */
-
-/* Return a timestamp, expressed as microseconds of the usual Unix
- time. (As the result is a 64-bit number, it will not overflow any
- time soon.) */
-
-static LONGEST
-get_timestamp (void)
-{
- using namespace std::chrono;
-
- steady_clock::time_point now = steady_clock::now ();
- return duration_cast<microseconds> (now.time_since_epoch ()).count ();
-}
-
-void
-initialize_tracepoint (void)
-{
- /* Start with the default size. */
- init_trace_buffer (DEFAULT_TRACE_BUFFER_SIZE);
-
- /* Wire trace state variable 1 to be the timestamp. This will be
- uploaded to GDB upon connection and become one of its trace state
- variables. (In case you're wondering, if GDB already has a trace
- variable numbered 1, it will be renumbered.) */
- create_trace_state_variable (1, 0);
- set_trace_state_variable_name (1, "trace_timestamp");
- set_trace_state_variable_getter (1, get_timestamp);
-
-#ifdef IN_PROCESS_AGENT
- {
- int pagesize;
- size_t jump_pad_size;
-
- pagesize = sysconf (_SC_PAGE_SIZE);
- if (pagesize == -1)
- perror_with_name ("sysconf");
-
-#define SCRATCH_BUFFER_NPAGES 20
-
- jump_pad_size = pagesize * SCRATCH_BUFFER_NPAGES;
-
- gdb_tp_heap_buffer = (char *) xmalloc (5 * 1024 * 1024);
- gdb_jump_pad_buffer = (char *) alloc_jump_pad_buffer (jump_pad_size);
- if (gdb_jump_pad_buffer == NULL)
- perror_with_name ("mmap");
- gdb_jump_pad_buffer_end = gdb_jump_pad_buffer + jump_pad_size;
- }
-
- gdb_trampoline_buffer = gdb_trampoline_buffer_end = 0;
-
- /* It's not a fatal error for something to go wrong with trampoline
- buffer setup, but it can be mysterious, so create a channel to
- report back on what went wrong, using a fixed size since we may
- not be able to allocate space later when the problem occurs. */
- gdb_trampoline_buffer_error = (char *) xmalloc (IPA_BUFSIZ);
-
- strcpy (gdb_trampoline_buffer_error, "No errors reported");
-
- initialize_low_tracepoint ();
-#endif
-}
--- /dev/null
+/* Tracepoint code for remote server for GDB.
+ Copyright (C) 2009-2020 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 "tracepoint.h"
+#include "gdbthread.h"
+#include "gdbsupport/rsp-low.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <chrono>
+#include <inttypes.h>
+#include "ax.h"
+#include "tdesc.h"
+
+#define IPA_SYM_STRUCT_NAME ipa_sym_addresses
+#include "gdbsupport/agent.h"
+
+#define DEFAULT_TRACE_BUFFER_SIZE 5242880 /* 5*1024*1024 */
+
+/* This file is built for both GDBserver, and the in-process
+ agent (IPA), a shared library that includes a tracing agent that is
+ loaded by the inferior to support fast tracepoints. Fast
+ tracepoints (or more accurately, jump based tracepoints) are
+ implemented by patching the tracepoint location with a jump into a
+ small trampoline function whose job is to save the register state,
+ call the in-process tracing agent, and then execute the original
+ instruction that was under the tracepoint jump (possibly adjusted,
+ if PC-relative, or some such).
+
+ The current synchronization design is pull based. That means,
+ GDBserver does most of the work, by peeking/poking at the inferior
+ agent's memory directly for downloading tracepoint and associated
+ objects, and for uploading trace frames. Whenever the IPA needs
+ something from GDBserver (trace buffer is full, tracing stopped for
+ some reason, etc.) the IPA calls a corresponding hook function
+ where GDBserver has placed a breakpoint.
+
+ Each of the agents has its own trace buffer. When browsing the
+ trace frames built from slow and fast tracepoints from GDB (tfind
+ mode), there's no guarantee the user is seeing the trace frames in
+ strict chronological creation order, although, GDBserver tries to
+ keep the order relatively reasonable, by syncing the trace buffers
+ at appropriate times.
+
+*/
+
+#ifdef IN_PROCESS_AGENT
+
+static void trace_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
+
+static void
+trace_vdebug (const char *fmt, ...)
+{
+ char buf[1024];
+ va_list ap;
+
+ va_start (ap, fmt);
+ vsprintf (buf, fmt, ap);
+ fprintf (stderr, PROG "/tracepoint: %s\n", buf);
+ va_end (ap);
+}
+
+#define trace_debug_1(level, fmt, args...) \
+ do { \
+ if (level <= debug_threads) \
+ trace_vdebug ((fmt), ##args); \
+ } while (0)
+
+#else
+
+#define trace_debug_1(level, fmt, args...) \
+ do { \
+ if (level <= debug_threads) \
+ { \
+ debug_printf ((fmt), ##args); \
+ debug_printf ("\n"); \
+ } \
+ } while (0)
+
+#endif
+
+#define trace_debug(FMT, args...) \
+ trace_debug_1 (1, FMT, ##args)
+
+/* Prefix exported symbols, for good citizenship. All the symbols
+ that need exporting are defined in this module. Note that all
+ these symbols must be tagged with IP_AGENT_EXPORT_*. */
+#ifdef IN_PROCESS_AGENT
+# define gdb_tp_heap_buffer IPA_SYM_EXPORTED_NAME (gdb_tp_heap_buffer)
+# define gdb_jump_pad_buffer IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer)
+# define gdb_jump_pad_buffer_end IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer_end)
+# define gdb_trampoline_buffer IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer)
+# define gdb_trampoline_buffer_end IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_end)
+# define gdb_trampoline_buffer_error IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_error)
+# define collecting IPA_SYM_EXPORTED_NAME (collecting)
+# define gdb_collect_ptr IPA_SYM_EXPORTED_NAME (gdb_collect_ptr)
+# define stop_tracing IPA_SYM_EXPORTED_NAME (stop_tracing)
+# define flush_trace_buffer IPA_SYM_EXPORTED_NAME (flush_trace_buffer)
+# define about_to_request_buffer_space IPA_SYM_EXPORTED_NAME (about_to_request_buffer_space)
+# define trace_buffer_is_full IPA_SYM_EXPORTED_NAME (trace_buffer_is_full)
+# define stopping_tracepoint IPA_SYM_EXPORTED_NAME (stopping_tracepoint)
+# define expr_eval_result IPA_SYM_EXPORTED_NAME (expr_eval_result)
+# define error_tracepoint IPA_SYM_EXPORTED_NAME (error_tracepoint)
+# define tracepoints IPA_SYM_EXPORTED_NAME (tracepoints)
+# define tracing IPA_SYM_EXPORTED_NAME (tracing)
+# define trace_buffer_ctrl IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl)
+# define trace_buffer_ctrl_curr IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl_curr)
+# define trace_buffer_lo IPA_SYM_EXPORTED_NAME (trace_buffer_lo)
+# define trace_buffer_hi IPA_SYM_EXPORTED_NAME (trace_buffer_hi)
+# define traceframe_read_count IPA_SYM_EXPORTED_NAME (traceframe_read_count)
+# define traceframe_write_count IPA_SYM_EXPORTED_NAME (traceframe_write_count)
+# define traceframes_created IPA_SYM_EXPORTED_NAME (traceframes_created)
+# define trace_state_variables IPA_SYM_EXPORTED_NAME (trace_state_variables)
+# define get_raw_reg_ptr IPA_SYM_EXPORTED_NAME (get_raw_reg_ptr)
+# define get_trace_state_variable_value_ptr \
+ IPA_SYM_EXPORTED_NAME (get_trace_state_variable_value_ptr)
+# define set_trace_state_variable_value_ptr \
+ IPA_SYM_EXPORTED_NAME (set_trace_state_variable_value_ptr)
+# define ust_loaded IPA_SYM_EXPORTED_NAME (ust_loaded)
+# define helper_thread_id IPA_SYM_EXPORTED_NAME (helper_thread_id)
+# define cmd_buf IPA_SYM_EXPORTED_NAME (cmd_buf)
+# define ipa_tdesc_idx IPA_SYM_EXPORTED_NAME (ipa_tdesc_idx)
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+/* Addresses of in-process agent's symbols GDBserver cares about. */
+
+struct ipa_sym_addresses
+{
+ CORE_ADDR addr_gdb_tp_heap_buffer;
+ CORE_ADDR addr_gdb_jump_pad_buffer;
+ CORE_ADDR addr_gdb_jump_pad_buffer_end;
+ CORE_ADDR addr_gdb_trampoline_buffer;
+ CORE_ADDR addr_gdb_trampoline_buffer_end;
+ CORE_ADDR addr_gdb_trampoline_buffer_error;
+ CORE_ADDR addr_collecting;
+ CORE_ADDR addr_gdb_collect_ptr;
+ CORE_ADDR addr_stop_tracing;
+ CORE_ADDR addr_flush_trace_buffer;
+ CORE_ADDR addr_about_to_request_buffer_space;
+ CORE_ADDR addr_trace_buffer_is_full;
+ CORE_ADDR addr_stopping_tracepoint;
+ CORE_ADDR addr_expr_eval_result;
+ CORE_ADDR addr_error_tracepoint;
+ CORE_ADDR addr_tracepoints;
+ CORE_ADDR addr_tracing;
+ CORE_ADDR addr_trace_buffer_ctrl;
+ CORE_ADDR addr_trace_buffer_ctrl_curr;
+ CORE_ADDR addr_trace_buffer_lo;
+ CORE_ADDR addr_trace_buffer_hi;
+ CORE_ADDR addr_traceframe_read_count;
+ CORE_ADDR addr_traceframe_write_count;
+ CORE_ADDR addr_traceframes_created;
+ CORE_ADDR addr_trace_state_variables;
+ CORE_ADDR addr_get_raw_reg_ptr;
+ CORE_ADDR addr_get_trace_state_variable_value_ptr;
+ CORE_ADDR addr_set_trace_state_variable_value_ptr;
+ CORE_ADDR addr_ust_loaded;
+ CORE_ADDR addr_ipa_tdesc_idx;
+};
+
+static struct
+{
+ const char *name;
+ int offset;
+} symbol_list[] = {
+ IPA_SYM(gdb_tp_heap_buffer),
+ IPA_SYM(gdb_jump_pad_buffer),
+ IPA_SYM(gdb_jump_pad_buffer_end),
+ IPA_SYM(gdb_trampoline_buffer),
+ IPA_SYM(gdb_trampoline_buffer_end),
+ IPA_SYM(gdb_trampoline_buffer_error),
+ IPA_SYM(collecting),
+ IPA_SYM(gdb_collect_ptr),
+ IPA_SYM(stop_tracing),
+ IPA_SYM(flush_trace_buffer),
+ IPA_SYM(about_to_request_buffer_space),
+ IPA_SYM(trace_buffer_is_full),
+ IPA_SYM(stopping_tracepoint),
+ IPA_SYM(expr_eval_result),
+ IPA_SYM(error_tracepoint),
+ IPA_SYM(tracepoints),
+ IPA_SYM(tracing),
+ IPA_SYM(trace_buffer_ctrl),
+ IPA_SYM(trace_buffer_ctrl_curr),
+ IPA_SYM(trace_buffer_lo),
+ IPA_SYM(trace_buffer_hi),
+ IPA_SYM(traceframe_read_count),
+ IPA_SYM(traceframe_write_count),
+ IPA_SYM(traceframes_created),
+ IPA_SYM(trace_state_variables),
+ IPA_SYM(get_raw_reg_ptr),
+ IPA_SYM(get_trace_state_variable_value_ptr),
+ IPA_SYM(set_trace_state_variable_value_ptr),
+ IPA_SYM(ust_loaded),
+ IPA_SYM(ipa_tdesc_idx),
+};
+
+static struct ipa_sym_addresses ipa_sym_addrs;
+
+static int read_inferior_integer (CORE_ADDR symaddr, int *val);
+
+/* Returns true if both the in-process agent library and the static
+ tracepoints libraries are loaded in the inferior, and agent has
+ capability on static tracepoints. */
+
+static int
+in_process_agent_supports_ust (void)
+{
+ int loaded = 0;
+
+ if (!agent_loaded_p ())
+ {
+ warning ("In-process agent not loaded");
+ return 0;
+ }
+
+ if (agent_capability_check (AGENT_CAPA_STATIC_TRACE))
+ {
+ /* Agent understands static tracepoint, then check whether UST is in
+ fact loaded in the inferior. */
+ if (read_inferior_integer (ipa_sym_addrs.addr_ust_loaded, &loaded))
+ {
+ warning ("Error reading ust_loaded in lib");
+ return 0;
+ }
+
+ return loaded;
+ }
+ else
+ return 0;
+}
+
+static void
+write_e_ipa_not_loaded (char *buffer)
+{
+ sprintf (buffer,
+ "E.In-process agent library not loaded in process. "
+ "Fast and static tracepoints unavailable.");
+}
+
+/* Write an error to BUFFER indicating that UST isn't loaded in the
+ inferior. */
+
+static void
+write_e_ust_not_loaded (char *buffer)
+{
+#ifdef HAVE_UST
+ sprintf (buffer,
+ "E.UST library not loaded in process. "
+ "Static tracepoints unavailable.");
+#else
+ sprintf (buffer, "E.GDBserver was built without static tracepoints support");
+#endif
+}
+
+/* If the in-process agent library isn't loaded in the inferior, write
+ an error to BUFFER, and return 1. Otherwise, return 0. */
+
+static int
+maybe_write_ipa_not_loaded (char *buffer)
+{
+ if (!agent_loaded_p ())
+ {
+ write_e_ipa_not_loaded (buffer);
+ return 1;
+ }
+ return 0;
+}
+
+/* If the in-process agent library and the ust (static tracepoints)
+ library aren't loaded in the inferior, write an error to BUFFER,
+ and return 1. Otherwise, return 0. */
+
+static int
+maybe_write_ipa_ust_not_loaded (char *buffer)
+{
+ if (!agent_loaded_p ())
+ {
+ write_e_ipa_not_loaded (buffer);
+ return 1;
+ }
+ else if (!in_process_agent_supports_ust ())
+ {
+ write_e_ust_not_loaded (buffer);
+ return 1;
+ }
+ return 0;
+}
+
+/* Cache all future symbols that the tracepoints module might request.
+ We can not request symbols at arbitrary states in the remote
+ protocol, only when the client tells us that new symbols are
+ available. So when we load the in-process library, make sure to
+ check the entire list. */
+
+void
+tracepoint_look_up_symbols (void)
+{
+ int i;
+
+ if (agent_loaded_p ())
+ return;
+
+ for (i = 0; i < sizeof (symbol_list) / sizeof (symbol_list[0]); i++)
+ {
+ CORE_ADDR *addrp =
+ (CORE_ADDR *) ((char *) &ipa_sym_addrs + symbol_list[i].offset);
+
+ if (look_up_one_symbol (symbol_list[i].name, addrp, 1) == 0)
+ {
+ if (debug_threads)
+ debug_printf ("symbol `%s' not found\n", symbol_list[i].name);
+ return;
+ }
+ }
+
+ agent_look_up_symbols (NULL);
+}
+
+#endif
+
+/* GDBserver places a breakpoint on the IPA's version (which is a nop)
+ of the "stop_tracing" function. When this breakpoint is hit,
+ tracing stopped in the IPA for some reason. E.g., due to
+ tracepoint reaching the pass count, hitting conditional expression
+ evaluation error, etc.
+
+ The IPA's trace buffer is never in circular tracing mode: instead,
+ GDBserver's is, and whenever the in-process buffer fills, it calls
+ "flush_trace_buffer", which triggers an internal breakpoint.
+ GDBserver reacts to this breakpoint by pulling the meanwhile
+ collected data. Old frames discarding is always handled on the
+ GDBserver side. */
+
+#ifdef IN_PROCESS_AGENT
+int
+read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ memcpy (myaddr, (void *) (uintptr_t) memaddr, len);
+ return 0;
+}
+
+/* Call this in the functions where GDBserver places a breakpoint, so
+ that the compiler doesn't try to be clever and skip calling the
+ function at all. This is necessary, even if we tell the compiler
+ to not inline said functions. */
+
+#if defined(__GNUC__)
+# define UNKNOWN_SIDE_EFFECTS() asm ("")
+#else
+# define UNKNOWN_SIDE_EFFECTS() do {} while (0)
+#endif
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void stop_tracing (void);
+
+IP_AGENT_EXPORT_FUNC void
+stop_tracing (void)
+{
+ /* GDBserver places breakpoint here. */
+ UNKNOWN_SIDE_EFFECTS();
+}
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void flush_trace_buffer (void);
+
+IP_AGENT_EXPORT_FUNC void
+flush_trace_buffer (void)
+{
+ /* GDBserver places breakpoint here. */
+ UNKNOWN_SIDE_EFFECTS();
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+static int
+tracepoint_handler (CORE_ADDR address)
+{
+ trace_debug ("tracepoint_handler: tracepoint at 0x%s hit",
+ paddress (address));
+ return 0;
+}
+
+/* Breakpoint at "stop_tracing" in the inferior lib. */
+struct breakpoint *stop_tracing_bkpt;
+static int stop_tracing_handler (CORE_ADDR);
+
+/* Breakpoint at "flush_trace_buffer" in the inferior lib. */
+struct breakpoint *flush_trace_buffer_bkpt;
+static int flush_trace_buffer_handler (CORE_ADDR);
+
+static void download_trace_state_variables (void);
+static void upload_fast_traceframes (void);
+
+static int run_inferior_command (char *cmd, int len);
+
+static int
+read_inferior_integer (CORE_ADDR symaddr, int *val)
+{
+ return read_inferior_memory (symaddr, (unsigned char *) val,
+ sizeof (*val));
+}
+
+struct tracepoint;
+static int tracepoint_send_agent (struct tracepoint *tpoint);
+
+static int
+read_inferior_uinteger (CORE_ADDR symaddr, unsigned int *val)
+{
+ return read_inferior_memory (symaddr, (unsigned char *) val,
+ sizeof (*val));
+}
+
+static int
+read_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR *val)
+{
+ void *pval = (void *) (uintptr_t) val;
+ int ret;
+
+ ret = read_inferior_memory (symaddr, (unsigned char *) &pval, sizeof (pval));
+ *val = (uintptr_t) pval;
+ return ret;
+}
+
+static int
+write_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR val)
+{
+ void *pval = (void *) (uintptr_t) val;
+ return target_write_memory (symaddr,
+ (unsigned char *) &pval, sizeof (pval));
+}
+
+static int
+write_inferior_integer (CORE_ADDR symaddr, int val)
+{
+ return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
+}
+
+static int
+write_inferior_int8 (CORE_ADDR symaddr, int8_t val)
+{
+ return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
+}
+
+static int
+write_inferior_uinteger (CORE_ADDR symaddr, unsigned int val)
+{
+ return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
+}
+
+static CORE_ADDR target_malloc (ULONGEST size);
+
+#define COPY_FIELD_TO_BUF(BUF, OBJ, FIELD) \
+ do { \
+ memcpy (BUF, &(OBJ)->FIELD, sizeof ((OBJ)->FIELD)); \
+ BUF += sizeof ((OBJ)->FIELD); \
+ } while (0)
+
+#endif
+
+/* Base action. Concrete actions inherit this. */
+
+struct tracepoint_action
+{
+ char type;
+};
+
+/* An 'M' (collect memory) action. */
+struct collect_memory_action
+{
+ struct tracepoint_action base;
+
+ ULONGEST addr;
+ ULONGEST len;
+ int32_t basereg;
+};
+
+/* An 'R' (collect registers) action. */
+
+struct collect_registers_action
+{
+ struct tracepoint_action base;
+};
+
+/* An 'X' (evaluate expression) action. */
+
+struct eval_expr_action
+{
+ struct tracepoint_action base;
+
+ struct agent_expr *expr;
+};
+
+/* An 'L' (collect static trace data) action. */
+struct collect_static_trace_data_action
+{
+ struct tracepoint_action base;
+};
+
+#ifndef IN_PROCESS_AGENT
+static CORE_ADDR
+m_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_memory_action));
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct collect_memory_action));
+
+ return ipa_action;
+}
+static char *
+m_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ struct collect_memory_action *maction
+ = (struct collect_memory_action *) action;
+
+ COPY_FIELD_TO_BUF (buffer, maction, addr);
+ COPY_FIELD_TO_BUF (buffer, maction, len);
+ COPY_FIELD_TO_BUF (buffer, maction, basereg);
+
+ return buffer;
+}
+
+static CORE_ADDR
+r_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_registers_action));
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct collect_registers_action));
+
+ return ipa_action;
+}
+
+static char *
+r_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ return buffer;
+}
+
+static CORE_ADDR download_agent_expr (struct agent_expr *expr);
+
+static CORE_ADDR
+x_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action = target_malloc (sizeof (struct eval_expr_action));
+ CORE_ADDR expr;
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct eval_expr_action));
+ expr = download_agent_expr (((struct eval_expr_action *) action)->expr);
+ write_inferior_data_pointer (ipa_action
+ + offsetof (struct eval_expr_action, expr),
+ expr);
+
+ return ipa_action;
+}
+
+/* Copy agent expression AEXPR to buffer pointed by P. If AEXPR is NULL,
+ copy 0 to P. Return updated header of buffer. */
+
+static char *
+agent_expr_send (char *p, const struct agent_expr *aexpr)
+{
+ /* Copy the length of condition first, and then copy its
+ content. */
+ if (aexpr == NULL)
+ {
+ memset (p, 0, 4);
+ p += 4;
+ }
+ else
+ {
+ memcpy (p, &aexpr->length, 4);
+ p +=4;
+
+ memcpy (p, aexpr->bytes, aexpr->length);
+ p += aexpr->length;
+ }
+ return p;
+}
+
+static char *
+x_tracepoint_action_send ( char *buffer, const struct tracepoint_action *action)
+{
+ struct eval_expr_action *eaction = (struct eval_expr_action *) action;
+
+ return agent_expr_send (buffer, eaction->expr);
+}
+
+static CORE_ADDR
+l_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action
+ = target_malloc (sizeof (struct collect_static_trace_data_action));
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct collect_static_trace_data_action));
+
+ return ipa_action;
+}
+
+static char *
+l_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ return buffer;
+}
+
+static char *
+tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ switch (action->type)
+ {
+ case 'M':
+ return m_tracepoint_action_send (buffer, action);
+ case 'R':
+ return r_tracepoint_action_send (buffer, action);
+ case 'X':
+ return x_tracepoint_action_send (buffer, action);
+ case 'L':
+ return l_tracepoint_action_send (buffer, action);
+ }
+ error ("Unknown trace action '%c'.", action->type);
+}
+
+static CORE_ADDR
+tracepoint_action_download (const struct tracepoint_action *action)
+{
+ switch (action->type)
+ {
+ case 'M':
+ return m_tracepoint_action_download (action);
+ case 'R':
+ return r_tracepoint_action_download (action);
+ case 'X':
+ return x_tracepoint_action_download (action);
+ case 'L':
+ return l_tracepoint_action_download (action);
+ }
+ error ("Unknown trace action '%c'.", action->type);
+}
+#endif
+
+/* This structure describes a piece of the source-level definition of
+ the tracepoint. The contents are not interpreted by the target,
+ but preserved verbatim for uploading upon reconnection. */
+
+struct source_string
+{
+ /* The type of string, such as "cond" for a conditional. */
+ char *type;
+
+ /* The source-level string itself. For the sake of target
+ debugging, we store it in plaintext, even though it is always
+ transmitted in hex. */
+ char *str;
+
+ /* Link to the next one in the list. We link them in the order
+ received, in case some make up an ordered list of commands or
+ some such. */
+ struct source_string *next;
+};
+
+enum tracepoint_type
+{
+ /* Trap based tracepoint. */
+ trap_tracepoint,
+
+ /* A fast tracepoint implemented with a jump instead of a trap. */
+ fast_tracepoint,
+
+ /* A static tracepoint, implemented by a program call into a tracing
+ library. */
+ static_tracepoint
+};
+
+struct tracepoint_hit_ctx;
+
+typedef enum eval_result_type (*condfn) (unsigned char *,
+ ULONGEST *);
+
+/* The definition of a tracepoint. */
+
+/* Tracepoints may have multiple locations, each at a different
+ address. This can occur with optimizations, template
+ instantiation, etc. Since the locations may be in different
+ scopes, the conditions and actions may be different for each
+ location. Our target version of tracepoints is more like GDB's
+ notion of "breakpoint locations", but we have almost nothing that
+ is not per-location, so we bother having two kinds of objects. The
+ key consequence is that numbers are not unique, and that it takes
+ both number and address to identify a tracepoint uniquely. */
+
+struct tracepoint
+{
+ /* The number of the tracepoint, as specified by GDB. Several
+ tracepoint objects here may share a number. */
+ uint32_t number;
+
+ /* Address at which the tracepoint is supposed to trigger. Several
+ tracepoints may share an address. */
+ CORE_ADDR address;
+
+ /* Tracepoint type. */
+ enum tracepoint_type type;
+
+ /* True if the tracepoint is currently enabled. */
+ int8_t enabled;
+
+ /* The number of single steps that will be performed after each
+ tracepoint hit. */
+ uint64_t step_count;
+
+ /* The number of times the tracepoint may be hit before it will
+ terminate the entire tracing run. */
+ uint64_t pass_count;
+
+ /* Pointer to the agent expression that is the tracepoint's
+ conditional, or NULL if the tracepoint is unconditional. */
+ struct agent_expr *cond;
+
+ /* The list of actions to take when the tracepoint triggers. */
+ uint32_t numactions;
+ struct tracepoint_action **actions;
+
+ /* Count of the times we've hit this tracepoint during the run.
+ Note that while-stepping steps are not counted as "hits". */
+ uint64_t hit_count;
+
+ /* Cached sum of the sizes of traceframes created by this point. */
+ uint64_t traceframe_usage;
+
+ CORE_ADDR compiled_cond;
+
+ /* Link to the next tracepoint in the list. */
+ struct tracepoint *next;
+
+#ifndef IN_PROCESS_AGENT
+ /* The list of actions to take when the tracepoint triggers, in
+ string/packet form. */
+ char **actions_str;
+
+ /* The collection of strings that describe the tracepoint as it was
+ entered into GDB. These are not used by the target, but are
+ reported back to GDB upon reconnection. */
+ struct source_string *source_strings;
+
+ /* The number of bytes displaced by fast tracepoints. It may subsume
+ multiple instructions, for multi-byte fast tracepoints. This
+ field is only valid for fast tracepoints. */
+ uint32_t orig_size;
+
+ /* Only for fast tracepoints. */
+ CORE_ADDR obj_addr_on_target;
+
+ /* Address range where the original instruction under a fast
+ tracepoint was relocated to. (_end is actually one byte past
+ the end). */
+ CORE_ADDR adjusted_insn_addr;
+ CORE_ADDR adjusted_insn_addr_end;
+
+ /* The address range of the piece of the jump pad buffer that was
+ assigned to this fast tracepoint. (_end is actually one byte
+ past the end).*/
+ CORE_ADDR jump_pad;
+ CORE_ADDR jump_pad_end;
+
+ /* The address range of the piece of the trampoline buffer that was
+ assigned to this fast tracepoint. (_end is actually one byte
+ past the end). */
+ CORE_ADDR trampoline;
+ CORE_ADDR trampoline_end;
+
+ /* The list of actions to take while in a stepping loop. These
+ fields are only valid for patch-based tracepoints. */
+ int num_step_actions;
+ struct tracepoint_action **step_actions;
+ /* Same, but in string/packet form. */
+ char **step_actions_str;
+
+ /* Handle returned by the breakpoint or tracepoint module when we
+ inserted the trap or jump, or hooked into a static tracepoint.
+ NULL if we haven't inserted it yet. */
+ void *handle;
+#endif
+
+};
+
+#ifndef IN_PROCESS_AGENT
+
+/* Given `while-stepping', a thread may be collecting data for more
+ than one tracepoint simultaneously. On the other hand, the same
+ tracepoint with a while-stepping action may be hit by more than one
+ thread simultaneously (but not quite, each thread could be handling
+ a different step). Each thread holds a list of these objects,
+ representing the current step of each while-stepping action being
+ collected. */
+
+struct wstep_state
+{
+ struct wstep_state *next;
+
+ /* The tracepoint number. */
+ int tp_number;
+ /* The tracepoint's address. */
+ CORE_ADDR tp_address;
+
+ /* The number of the current step in this 'while-stepping'
+ action. */
+ long current_step;
+};
+
+#endif
+
+EXTERN_C_PUSH
+
+/* The linked list of all tracepoints. Marked explicitly as used as
+ the in-process library doesn't use it for the fast tracepoints
+ support. */
+IP_AGENT_EXPORT_VAR struct tracepoint *tracepoints;
+
+/* The first tracepoint to exceed its pass count. */
+
+IP_AGENT_EXPORT_VAR struct tracepoint *stopping_tracepoint;
+
+/* True if the trace buffer is full or otherwise no longer usable. */
+
+IP_AGENT_EXPORT_VAR int trace_buffer_is_full;
+
+/* The first error that occurred during expression evaluation. */
+
+/* Stored as an int to avoid the IPA ABI being dependent on whatever
+ the compiler decides to use for the enum's underlying type. Holds
+ enum eval_result_type values. */
+IP_AGENT_EXPORT_VAR int expr_eval_result = expr_eval_no_error;
+
+EXTERN_C_POP
+
+#ifndef IN_PROCESS_AGENT
+
+/* Pointer to the last tracepoint in the list, new tracepoints are
+ linked in at the end. */
+
+static struct tracepoint *last_tracepoint;
+
+static const char *eval_result_names[] =
+ {
+ "terror:in the attic", /* this should never be reported */
+ "terror:empty expression",
+ "terror:empty stack",
+ "terror:stack overflow",
+ "terror:stack underflow",
+ "terror:unhandled opcode",
+ "terror:unrecognized opcode",
+ "terror:divide by zero"
+ };
+
+#endif
+
+/* The tracepoint in which the error occurred. */
+
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR struct tracepoint *error_tracepoint;
+EXTERN_C_POP
+
+struct trace_state_variable
+{
+ /* This is the name of the variable as used in GDB. The target
+ doesn't use the name, but needs to have it for saving and
+ reconnection purposes. */
+ char *name;
+
+ /* This number identifies the variable uniquely. Numbers may be
+ assigned either by the target (in the case of builtin variables),
+ or by GDB, and are presumed unique during the course of a trace
+ experiment. */
+ int number;
+
+ /* The variable's initial value, a 64-bit signed integer always. */
+ LONGEST initial_value;
+
+ /* The variable's value, a 64-bit signed integer always. */
+ LONGEST value;
+
+ /* Pointer to a getter function, used to supply computed values. */
+ LONGEST (*getter) (void);
+
+ /* Link to the next variable. */
+ struct trace_state_variable *next;
+};
+
+/* Linked list of all trace state variables. */
+
+#ifdef IN_PROCESS_AGENT
+struct trace_state_variable *alloced_trace_state_variables;
+#endif
+
+IP_AGENT_EXPORT_VAR struct trace_state_variable *trace_state_variables;
+
+/* The results of tracing go into a fixed-size space known as the
+ "trace buffer". Because usage follows a limited number of
+ patterns, we manage it ourselves rather than with malloc. Basic
+ rules are that we create only one trace frame at a time, each is
+ variable in size, they are never moved once created, and we only
+ discard if we are doing a circular buffer, and then only the oldest
+ ones. Each trace frame includes its own size, so we don't need to
+ link them together, and the trace frame number is relative to the
+ first one, so we don't need to record numbers. A trace frame also
+ records the number of the tracepoint that created it. The data
+ itself is a series of blocks, each introduced by a single character
+ and with a defined format. Each type of block has enough
+ type/length info to allow scanners to jump quickly from one block
+ to the next without reading each byte in the block. */
+
+/* Trace buffer management would be simple - advance a free pointer
+ from beginning to end, then stop - were it not for the circular
+ buffer option, which is a useful way to prevent a trace run from
+ stopping prematurely because the buffer filled up. In the circular
+ case, the location of the first trace frame (trace_buffer_start)
+ moves as old trace frames are discarded. Also, since we grow trace
+ frames incrementally as actions are performed, we wrap around to
+ the beginning of the trace buffer. This is per-block, so each
+ block within a trace frame remains contiguous. Things get messy
+ when the wrapped-around trace frame is the one being discarded; the
+ free space ends up in two parts at opposite ends of the buffer. */
+
+#ifndef ATTR_PACKED
+# if defined(__GNUC__)
+# define ATTR_PACKED __attribute__ ((packed))
+# else
+# define ATTR_PACKED /* nothing */
+# endif
+#endif
+
+/* The data collected at a tracepoint hit. This object should be as
+ small as possible, since there may be a great many of them. We do
+ not need to keep a frame number, because they are all sequential
+ and there are no deletions; so the Nth frame in the buffer is
+ always frame number N. */
+
+struct traceframe
+{
+ /* Number of the tracepoint that collected this traceframe. A value
+ of 0 indicates the current end of the trace buffer. We make this
+ a 16-bit field because it's never going to happen that GDB's
+ numbering of tracepoints reaches 32,000. */
+ int tpnum : 16;
+
+ /* The size of the data in this trace frame. We limit this to 32
+ bits, even on a 64-bit target, because it's just implausible that
+ one is validly going to collect 4 gigabytes of data at a single
+ tracepoint hit. */
+ unsigned int data_size : 32;
+
+ /* The base of the trace data, which is contiguous from this point. */
+ unsigned char data[0];
+
+} ATTR_PACKED;
+
+/* The size of the EOB marker, in bytes. A traceframe with zeroed
+ fields (and no data) marks the end of trace data. */
+#define TRACEFRAME_EOB_MARKER_SIZE offsetof (struct traceframe, data)
+
+/* This flag is true if the trace buffer is circular, meaning that
+ when it fills, the oldest trace frames are discarded in order to
+ make room. */
+
+#ifndef IN_PROCESS_AGENT
+static int circular_trace_buffer;
+#endif
+
+/* Size of the trace buffer. */
+
+static LONGEST trace_buffer_size;
+
+EXTERN_C_PUSH
+
+/* Pointer to the block of memory that traceframes all go into. */
+
+IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_lo;
+
+/* Pointer to the end of the trace buffer, more precisely to the byte
+ after the end of the buffer. */
+
+IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_hi;
+
+EXTERN_C_POP
+
+/* Control structure holding the read/write/etc. pointers into the
+ trace buffer. We need more than one of these to implement a
+ transaction-like mechanism to guarantees that both GDBserver and the
+ in-process agent can try to change the trace buffer
+ simultaneously. */
+
+struct trace_buffer_control
+{
+ /* Pointer to the first trace frame in the buffer. In the
+ non-circular case, this is equal to trace_buffer_lo, otherwise it
+ moves around in the buffer. */
+ unsigned char *start;
+
+ /* Pointer to the free part of the trace buffer. Note that we clear
+ several bytes at and after this pointer, so that traceframe
+ scans/searches terminate properly. */
+ unsigned char *free;
+
+ /* Pointer to the byte after the end of the free part. Note that
+ this may be smaller than trace_buffer_free in the circular case,
+ and means that the free part is in two pieces. Initially it is
+ equal to trace_buffer_hi, then is generally equivalent to
+ trace_buffer_start. */
+ unsigned char *end_free;
+
+ /* Pointer to the wraparound. If not equal to trace_buffer_hi, then
+ this is the point at which the trace data breaks, and resumes at
+ trace_buffer_lo. */
+ unsigned char *wrap;
+};
+
+/* Same as above, to be used by GDBserver when updating the in-process
+ agent. */
+struct ipa_trace_buffer_control
+{
+ uintptr_t start;
+ uintptr_t free;
+ uintptr_t end_free;
+ uintptr_t wrap;
+};
+
+
+/* We have possibly both GDBserver and an inferior thread accessing
+ the same IPA trace buffer memory. The IPA is the producer (tries
+ to put new frames in the buffer), while GDBserver occasionally
+ consumes them, that is, flushes the IPA's buffer into its own
+ buffer. Both sides need to update the trace buffer control
+ pointers (current head, tail, etc.). We can't use a global lock to
+ synchronize the accesses, as otherwise we could deadlock GDBserver
+ (if the thread holding the lock stops for a signal, say). So
+ instead of that, we use a transaction scheme where GDBserver writes
+ always prevail over the IPAs writes, and, we have the IPA detect
+ the commit failure/overwrite, and retry the whole attempt. This is
+ mainly implemented by having a global token object that represents
+ who wrote last to the buffer control structure. We need to freeze
+ any inferior writing to the buffer while GDBserver touches memory,
+ so that the inferior can correctly detect that GDBserver had been
+ there, otherwise, it could mistakingly think its commit was
+ successful; that's implemented by simply having GDBserver set a
+ breakpoint the inferior hits if it is the critical region.
+
+ There are three cycling trace buffer control structure copies
+ (buffer head, tail, etc.), with the token object including an index
+ indicating which is current live copy. The IPA tentatively builds
+ an updated copy in a non-current control structure, while GDBserver
+ always clobbers the current version directly. The IPA then tries
+ to atomically "commit" its version; if GDBserver clobbered the
+ structure meanwhile, that will fail, and the IPA restarts the
+ allocation process.
+
+ Listing the step in further detail, we have:
+
+ In-process agent (producer):
+
+ - passes by `about_to_request_buffer_space' breakpoint/lock
+
+ - reads current token, extracts current trace buffer control index,
+ and starts tentatively updating the rightmost one (0->1, 1->2,
+ 2->0). Note that only one inferior thread is executing this code
+ at any given time, due to an outer lock in the jump pads.
+
+ - updates counters, and tries to commit the token.
+
+ - passes by second `about_to_request_buffer_space' breakpoint/lock,
+ leaving the sync region.
+
+ - checks if the update was effective.
+
+ - if trace buffer was found full, hits flush_trace_buffer
+ breakpoint, and restarts later afterwards.
+
+ GDBserver (consumer):
+
+ - sets `about_to_request_buffer_space' breakpoint/lock.
+
+ - updates the token unconditionally, using the current buffer
+ control index, since it knows that the IP agent always writes to
+ the rightmost, and due to the breakpoint, at most one IP thread
+ can try to update the trace buffer concurrently to GDBserver, so
+ there will be no danger of trace buffer control index wrap making
+ the IPA write to the same index as GDBserver.
+
+ - flushes the IP agent's trace buffer completely, and updates the
+ current trace buffer control structure. GDBserver *always* wins.
+
+ - removes the `about_to_request_buffer_space' breakpoint.
+
+The token is stored in the `trace_buffer_ctrl_curr' variable.
+Internally, it's bits are defined as:
+
+ |-------------+-----+-------------+--------+-------------+--------------|
+ | Bit offsets | 31 | 30 - 20 | 19 | 18-8 | 7-0 |
+ |-------------+-----+-------------+--------+-------------+--------------|
+ | What | GSB | PC (11-bit) | unused | CC (11-bit) | TBCI (8-bit) |
+ |-------------+-----+-------------+--------+-------------+--------------|
+
+ GSB - GDBserver Stamp Bit
+ PC - Previous Counter
+ CC - Current Counter
+ TBCI - Trace Buffer Control Index
+
+
+An IPA update of `trace_buffer_ctrl_curr' does:
+
+ - read CC from the current token, save as PC.
+ - updates pointers
+ - atomically tries to write PC+1,CC
+
+A GDBserver update of `trace_buffer_ctrl_curr' does:
+
+ - reads PC and CC from the current token.
+ - updates pointers
+ - writes GSB,PC,CC
+*/
+
+/* These are the bits of `trace_buffer_ctrl_curr' that are reserved
+ for the counters described below. The cleared bits are used to
+ hold the index of the items of the `trace_buffer_ctrl' array that
+ is "current". */
+#define GDBSERVER_FLUSH_COUNT_MASK 0xfffffff0
+
+/* `trace_buffer_ctrl_curr' contains two counters. The `previous'
+ counter, and the `current' counter. */
+
+#define GDBSERVER_FLUSH_COUNT_MASK_PREV 0x7ff00000
+#define GDBSERVER_FLUSH_COUNT_MASK_CURR 0x0007ff00
+
+/* When GDBserver update the IP agent's `trace_buffer_ctrl_curr', it
+ always stamps this bit as set. */
+#define GDBSERVER_UPDATED_FLUSH_COUNT_BIT 0x80000000
+
+#ifdef IN_PROCESS_AGENT
+IP_AGENT_EXPORT_VAR struct trace_buffer_control trace_buffer_ctrl[3];
+IP_AGENT_EXPORT_VAR unsigned int trace_buffer_ctrl_curr;
+
+# define TRACE_BUFFER_CTRL_CURR \
+ (trace_buffer_ctrl_curr & ~GDBSERVER_FLUSH_COUNT_MASK)
+
+#else
+
+/* The GDBserver side agent only needs one instance of this object, as
+ it doesn't need to sync with itself. Define it as array anyway so
+ that the rest of the code base doesn't need to care for the
+ difference. */
+struct trace_buffer_control trace_buffer_ctrl[1];
+# define TRACE_BUFFER_CTRL_CURR 0
+#endif
+
+/* These are convenience macros used to access the current trace
+ buffer control in effect. */
+#define trace_buffer_start (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].start)
+#define trace_buffer_free (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].free)
+#define trace_buffer_end_free \
+ (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].end_free)
+#define trace_buffer_wrap (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].wrap)
+
+
+/* Macro that returns a pointer to the first traceframe in the buffer. */
+
+#define FIRST_TRACEFRAME() ((struct traceframe *) trace_buffer_start)
+
+/* Macro that returns a pointer to the next traceframe in the buffer.
+ If the computed location is beyond the wraparound point, subtract
+ the offset of the wraparound. */
+
+#define NEXT_TRACEFRAME_1(TF) \
+ (((unsigned char *) (TF)) + sizeof (struct traceframe) + (TF)->data_size)
+
+#define NEXT_TRACEFRAME(TF) \
+ ((struct traceframe *) (NEXT_TRACEFRAME_1 (TF) \
+ - ((NEXT_TRACEFRAME_1 (TF) >= trace_buffer_wrap) \
+ ? (trace_buffer_wrap - trace_buffer_lo) \
+ : 0)))
+
+/* The difference between these counters represents the total number
+ of complete traceframes present in the trace buffer. The IP agent
+ writes to the write count, GDBserver writes to read count. */
+
+IP_AGENT_EXPORT_VAR unsigned int traceframe_write_count;
+IP_AGENT_EXPORT_VAR unsigned int traceframe_read_count;
+
+/* Convenience macro. */
+
+#define traceframe_count \
+ ((unsigned int) (traceframe_write_count - traceframe_read_count))
+
+/* The count of all traceframes created in the current run, including
+ ones that were discarded to make room. */
+
+IP_AGENT_EXPORT_VAR int traceframes_created;
+
+#ifndef IN_PROCESS_AGENT
+
+/* Read-only regions are address ranges whose contents don't change,
+ and so can be read from target memory even while looking at a trace
+ frame. Without these, disassembly for instance will likely fail,
+ because the program code is not usually collected into a trace
+ frame. This data structure does not need to be very complicated or
+ particularly efficient, it's only going to be used occasionally,
+ and only by some commands. */
+
+struct readonly_region
+{
+ /* The bounds of the region. */
+ CORE_ADDR start, end;
+
+ /* Link to the next one. */
+ struct readonly_region *next;
+};
+
+/* Linked list of readonly regions. This list stays in effect from
+ one tstart to the next. */
+
+static struct readonly_region *readonly_regions;
+
+#endif
+
+/* The global that controls tracing overall. */
+
+IP_AGENT_EXPORT_VAR int tracing;
+
+#ifndef IN_PROCESS_AGENT
+
+/* Controls whether tracing should continue after GDB disconnects. */
+
+int disconnected_tracing;
+
+/* The reason for the last tracing run to have stopped. We initialize
+ to a distinct string so that GDB can distinguish between "stopped
+ after running" and "stopped because never run" cases. */
+
+static const char *tracing_stop_reason = "tnotrun";
+
+static int tracing_stop_tpnum;
+
+/* 64-bit timestamps for the trace run's start and finish, expressed
+ in microseconds from the Unix epoch. */
+
+LONGEST tracing_start_time;
+LONGEST tracing_stop_time;
+
+/* The (optional) user-supplied name of the user that started the run.
+ This is an arbitrary string, and may be NULL. */
+
+char *tracing_user_name;
+
+/* Optional user-supplied text describing the run. This is
+ an arbitrary string, and may be NULL. */
+
+char *tracing_notes;
+
+/* Optional user-supplied text explaining a tstop command. This is an
+ arbitrary string, and may be NULL. */
+
+char *tracing_stop_note;
+
+#endif
+
+/* Functions local to this file. */
+
+/* Base "class" for tracepoint type specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct tracepoint_hit_ctx
+{
+ enum tracepoint_type type;
+};
+
+#ifdef IN_PROCESS_AGENT
+
+/* Fast/jump tracepoint specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct fast_tracepoint_ctx
+{
+ struct tracepoint_hit_ctx base;
+
+ struct regcache regcache;
+ int regcache_initted;
+ unsigned char *regspace;
+
+ unsigned char *regs;
+ struct tracepoint *tpoint;
+};
+
+/* Static tracepoint specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct static_tracepoint_ctx
+{
+ struct tracepoint_hit_ctx base;
+
+ /* The regcache corresponding to the registers state at the time of
+ the tracepoint hit. Initialized lazily, from REGS. */
+ struct regcache regcache;
+ int regcache_initted;
+
+ /* The buffer space REGCACHE above uses. We use a separate buffer
+ instead of letting the regcache malloc for both signal safety and
+ performance reasons; this is allocated on the stack instead. */
+ unsigned char *regspace;
+
+ /* The register buffer as passed on by lttng/ust. */
+ struct registers *regs;
+
+ /* The "printf" formatter and the args the user passed to the marker
+ call. We use this to be able to collect "static trace data"
+ ($_sdata). */
+ const char *fmt;
+ va_list *args;
+
+ /* The GDB tracepoint matching the probed marker that was "hit". */
+ struct tracepoint *tpoint;
+};
+
+#else
+
+/* Static tracepoint specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct trap_tracepoint_ctx
+{
+ struct tracepoint_hit_ctx base;
+
+ struct regcache *regcache;
+};
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+static CORE_ADDR traceframe_get_pc (struct traceframe *tframe);
+static int traceframe_read_tsv (int num, LONGEST *val);
+#endif
+
+static int condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct tracepoint *tpoint);
+
+#ifndef IN_PROCESS_AGENT
+static void clear_readonly_regions (void);
+static void clear_installed_tracepoints (void);
+#endif
+
+static void collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint);
+#ifndef IN_PROCESS_AGENT
+static void collect_data_at_step (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint, int current_step);
+static void compile_tracepoint_condition (struct tracepoint *tpoint,
+ CORE_ADDR *jump_entry);
+#endif
+static void do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint,
+ struct traceframe *tframe,
+ struct tracepoint_action *taction);
+
+#ifndef IN_PROCESS_AGENT
+static struct tracepoint *fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR);
+
+static void install_tracepoint (struct tracepoint *, char *own_buf);
+static void download_tracepoint (struct tracepoint *);
+static int install_fast_tracepoint (struct tracepoint *, char *errbuf);
+static void clone_fast_tracepoint (struct tracepoint *to,
+ const struct tracepoint *from);
+#endif
+
+static LONGEST get_timestamp (void);
+
+#if defined(__GNUC__)
+# define memory_barrier() asm volatile ("" : : : "memory")
+#else
+# define memory_barrier() do {} while (0)
+#endif
+
+/* We only build the IPA if this builtin is supported, and there are
+ no uses of this in GDBserver itself, so we're safe in defining this
+ unconditionally. */
+#define cmpxchg(mem, oldval, newval) \
+ __sync_val_compare_and_swap (mem, oldval, newval)
+
+/* Record that an error occurred during expression evaluation. */
+
+static void
+record_tracepoint_error (struct tracepoint *tpoint, const char *which,
+ enum eval_result_type rtype)
+{
+ trace_debug ("Tracepoint %d at %s %s eval reports error %d",
+ tpoint->number, paddress (tpoint->address), which, rtype);
+
+#ifdef IN_PROCESS_AGENT
+ /* Only record the first error we get. */
+ if (cmpxchg (&expr_eval_result,
+ expr_eval_no_error,
+ rtype) != expr_eval_no_error)
+ return;
+#else
+ if (expr_eval_result != expr_eval_no_error)
+ return;
+#endif
+
+ error_tracepoint = tpoint;
+}
+
+/* Trace buffer management. */
+
+static void
+clear_trace_buffer (void)
+{
+ trace_buffer_start = trace_buffer_lo;
+ trace_buffer_free = trace_buffer_lo;
+ trace_buffer_end_free = trace_buffer_hi;
+ trace_buffer_wrap = trace_buffer_hi;
+ /* A traceframe with zeroed fields marks the end of trace data. */
+ ((struct traceframe *) trace_buffer_free)->tpnum = 0;
+ ((struct traceframe *) trace_buffer_free)->data_size = 0;
+ traceframe_read_count = traceframe_write_count = 0;
+ traceframes_created = 0;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+static void
+clear_inferior_trace_buffer (void)
+{
+ CORE_ADDR ipa_trace_buffer_lo;
+ CORE_ADDR ipa_trace_buffer_hi;
+ struct traceframe ipa_traceframe = { 0 };
+ struct ipa_trace_buffer_control ipa_trace_buffer_ctrl;
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_lo,
+ &ipa_trace_buffer_lo);
+ read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_hi,
+ &ipa_trace_buffer_hi);
+
+ ipa_trace_buffer_ctrl.start = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.free = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_hi;
+ ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
+
+ /* A traceframe with zeroed fields marks the end of trace data. */
+ target_write_memory (ipa_sym_addrs.addr_trace_buffer_ctrl,
+ (unsigned char *) &ipa_trace_buffer_ctrl,
+ sizeof (ipa_trace_buffer_ctrl));
+
+ write_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr, 0);
+
+ /* A traceframe with zeroed fields marks the end of trace data. */
+ target_write_memory (ipa_trace_buffer_lo,
+ (unsigned char *) &ipa_traceframe,
+ sizeof (ipa_traceframe));
+
+ write_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count, 0);
+ write_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count, 0);
+ write_inferior_integer (ipa_sym_addrs.addr_traceframes_created, 0);
+}
+
+#endif
+
+static void
+init_trace_buffer (LONGEST bufsize)
+{
+ size_t alloc_size;
+
+ trace_buffer_size = bufsize;
+
+ /* Make sure to internally allocate at least space for the EOB
+ marker. */
+ alloc_size = (bufsize < TRACEFRAME_EOB_MARKER_SIZE
+ ? TRACEFRAME_EOB_MARKER_SIZE : bufsize);
+ trace_buffer_lo = (unsigned char *) xrealloc (trace_buffer_lo, alloc_size);
+
+ trace_buffer_hi = trace_buffer_lo + trace_buffer_size;
+
+ clear_trace_buffer ();
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void about_to_request_buffer_space (void);
+
+IP_AGENT_EXPORT_FUNC void
+about_to_request_buffer_space (void)
+{
+ /* GDBserver places breakpoint here while it goes about to flush
+ data at random times. */
+ UNKNOWN_SIDE_EFFECTS();
+}
+
+#endif
+
+/* Carve out a piece of the trace buffer, returning NULL in case of
+ failure. */
+
+static void *
+trace_buffer_alloc (size_t amt)
+{
+ unsigned char *rslt;
+ struct trace_buffer_control *tbctrl;
+ unsigned int curr;
+#ifdef IN_PROCESS_AGENT
+ unsigned int prev, prev_filtered;
+ unsigned int commit_count;
+ unsigned int commit;
+ unsigned int readout;
+#else
+ struct traceframe *oldest;
+ unsigned char *new_start;
+#endif
+
+ trace_debug ("Want to allocate %ld+%ld bytes in trace buffer",
+ (long) amt, (long) sizeof (struct traceframe));
+
+ /* Account for the EOB marker. */
+ amt += TRACEFRAME_EOB_MARKER_SIZE;
+
+#ifdef IN_PROCESS_AGENT
+ again:
+ memory_barrier ();
+
+ /* Read the current token and extract the index to try to write to,
+ storing it in CURR. */
+ prev = trace_buffer_ctrl_curr;
+ prev_filtered = prev & ~GDBSERVER_FLUSH_COUNT_MASK;
+ curr = prev_filtered + 1;
+ if (curr > 2)
+ curr = 0;
+
+ about_to_request_buffer_space ();
+
+ /* Start out with a copy of the current state. GDBserver may be
+ midway writing to the PREV_FILTERED TBC, but, that's OK, we won't
+ be able to commit anyway if that happens. */
+ trace_buffer_ctrl[curr]
+ = trace_buffer_ctrl[prev_filtered];
+ trace_debug ("trying curr=%u", curr);
+#else
+ /* The GDBserver's agent doesn't need all that syncing, and always
+ updates TCB 0 (there's only one, mind you). */
+ curr = 0;
+#endif
+ tbctrl = &trace_buffer_ctrl[curr];
+
+ /* Offsets are easier to grok for debugging than raw addresses,
+ especially for the small trace buffer sizes that are useful for
+ testing. */
+ trace_debug ("Trace buffer [%d] start=%d free=%d endfree=%d wrap=%d hi=%d",
+ curr,
+ (int) (tbctrl->start - trace_buffer_lo),
+ (int) (tbctrl->free - trace_buffer_lo),
+ (int) (tbctrl->end_free - trace_buffer_lo),
+ (int) (tbctrl->wrap - trace_buffer_lo),
+ (int) (trace_buffer_hi - trace_buffer_lo));
+
+ /* The algorithm here is to keep trying to get a contiguous block of
+ the requested size, possibly discarding older traceframes to free
+ up space. Since free space might come in one or two pieces,
+ depending on whether discarded traceframes wrapped around at the
+ high end of the buffer, we test both pieces after each
+ discard. */
+ while (1)
+ {
+ /* First, if we have two free parts, try the upper one first. */
+ if (tbctrl->end_free < tbctrl->free)
+ {
+ if (tbctrl->free + amt <= trace_buffer_hi)
+ /* We have enough in the upper part. */
+ break;
+ else
+ {
+ /* Our high part of free space wasn't enough. Give up
+ on it for now, set wraparound. We will recover the
+ space later, if/when the wrapped-around traceframe is
+ discarded. */
+ trace_debug ("Upper part too small, setting wraparound");
+ tbctrl->wrap = tbctrl->free;
+ tbctrl->free = trace_buffer_lo;
+ }
+ }
+
+ /* The normal case. */
+ if (tbctrl->free + amt <= tbctrl->end_free)
+ break;
+
+#ifdef IN_PROCESS_AGENT
+ /* The IP Agent's buffer is always circular. It isn't used
+ currently, but `circular_trace_buffer' could represent
+ GDBserver's mode. If we didn't find space, ask GDBserver to
+ flush. */
+
+ flush_trace_buffer ();
+ memory_barrier ();
+ if (tracing)
+ {
+ trace_debug ("gdbserver flushed buffer, retrying");
+ goto again;
+ }
+
+ /* GDBserver cancelled the tracing. Bail out as well. */
+ return NULL;
+#else
+ /* If we're here, then neither part is big enough, and
+ non-circular trace buffers are now full. */
+ if (!circular_trace_buffer)
+ {
+ trace_debug ("Not enough space in the trace buffer");
+ return NULL;
+ }
+
+ trace_debug ("Need more space in the trace buffer");
+
+ /* If we have a circular buffer, we can try discarding the
+ oldest traceframe and see if that helps. */
+ oldest = FIRST_TRACEFRAME ();
+ if (oldest->tpnum == 0)
+ {
+ /* Not good; we have no traceframes to free. Perhaps we're
+ asking for a block that is larger than the buffer? In
+ any case, give up. */
+ trace_debug ("No traceframes to discard");
+ return NULL;
+ }
+
+ /* We don't run this code in the in-process agent currently.
+ E.g., we could leave the in-process agent in autonomous
+ circular mode if we only have fast tracepoints. If we do
+ that, then this bit becomes racy with GDBserver, which also
+ writes to this counter. */
+ --traceframe_write_count;
+
+ new_start = (unsigned char *) NEXT_TRACEFRAME (oldest);
+ /* If we freed the traceframe that wrapped around, go back
+ to the non-wrap case. */
+ if (new_start < tbctrl->start)
+ {
+ trace_debug ("Discarding past the wraparound");
+ tbctrl->wrap = trace_buffer_hi;
+ }
+ tbctrl->start = new_start;
+ tbctrl->end_free = tbctrl->start;
+
+ trace_debug ("Discarded a traceframe\n"
+ "Trace buffer [%d], start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr,
+ (int) (tbctrl->start - trace_buffer_lo),
+ (int) (tbctrl->free - trace_buffer_lo),
+ (int) (tbctrl->end_free - trace_buffer_lo),
+ (int) (tbctrl->wrap - trace_buffer_lo),
+ (int) (trace_buffer_hi - trace_buffer_lo));
+
+ /* Now go back around the loop. The discard might have resulted
+ in either one or two pieces of free space, so we want to try
+ both before freeing any more traceframes. */
+#endif
+ }
+
+ /* If we get here, we know we can provide the asked-for space. */
+
+ rslt = tbctrl->free;
+
+ /* Adjust the request back down, now that we know we have space for
+ the marker, but don't commit to AMT yet, we may still need to
+ restart the operation if GDBserver touches the trace buffer
+ (obviously only important in the in-process agent's version). */
+ tbctrl->free += (amt - sizeof (struct traceframe));
+
+ /* Or not. If GDBserver changed the trace buffer behind our back,
+ we get to restart a new allocation attempt. */
+
+#ifdef IN_PROCESS_AGENT
+ /* Build the tentative token. */
+ commit_count = (((prev & GDBSERVER_FLUSH_COUNT_MASK_CURR) + 0x100)
+ & GDBSERVER_FLUSH_COUNT_MASK_CURR);
+ commit = (((prev & GDBSERVER_FLUSH_COUNT_MASK_CURR) << 12)
+ | commit_count
+ | curr);
+
+ /* Try to commit it. */
+ readout = cmpxchg (&trace_buffer_ctrl_curr, prev, commit);
+ if (readout != prev)
+ {
+ trace_debug ("GDBserver has touched the trace buffer, restarting."
+ " (prev=%08x, commit=%08x, readout=%08x)",
+ prev, commit, readout);
+ goto again;
+ }
+
+ /* Hold your horses here. Even if that change was committed,
+ GDBserver could come in, and clobber it. We need to hold to be
+ able to tell if GDBserver clobbers before or after we committed
+ the change. Whenever GDBserver goes about touching the IPA
+ buffer, it sets a breakpoint in this routine, so we have a sync
+ point here. */
+ about_to_request_buffer_space ();
+
+ /* Check if the change has been effective, even if GDBserver stopped
+ us at the breakpoint. */
+
+ {
+ unsigned int refetch;
+
+ memory_barrier ();
+
+ refetch = trace_buffer_ctrl_curr;
+
+ if (refetch == commit
+ || ((refetch & GDBSERVER_FLUSH_COUNT_MASK_PREV) >> 12) == commit_count)
+ {
+ /* effective */
+ trace_debug ("change is effective: (prev=%08x, commit=%08x, "
+ "readout=%08x, refetch=%08x)",
+ prev, commit, readout, refetch);
+ }
+ else
+ {
+ trace_debug ("GDBserver has touched the trace buffer, not effective."
+ " (prev=%08x, commit=%08x, readout=%08x, refetch=%08x)",
+ prev, commit, readout, refetch);
+ goto again;
+ }
+ }
+#endif
+
+ /* We have a new piece of the trace buffer. Hurray! */
+
+ /* Add an EOB marker just past this allocation. */
+ ((struct traceframe *) tbctrl->free)->tpnum = 0;
+ ((struct traceframe *) tbctrl->free)->data_size = 0;
+
+ /* Adjust the request back down, now that we know we have space for
+ the marker. */
+ amt -= sizeof (struct traceframe);
+
+ if (debug_threads)
+ {
+ trace_debug ("Allocated %d bytes", (int) amt);
+ trace_debug ("Trace buffer [%d] start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr,
+ (int) (tbctrl->start - trace_buffer_lo),
+ (int) (tbctrl->free - trace_buffer_lo),
+ (int) (tbctrl->end_free - trace_buffer_lo),
+ (int) (tbctrl->wrap - trace_buffer_lo),
+ (int) (trace_buffer_hi - trace_buffer_lo));
+ }
+
+ return rslt;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Return the total free space. This is not necessarily the largest
+ block we can allocate, because of the two-part case. */
+
+static int
+free_space (void)
+{
+ if (trace_buffer_free <= trace_buffer_end_free)
+ return trace_buffer_end_free - trace_buffer_free;
+ else
+ return ((trace_buffer_end_free - trace_buffer_lo)
+ + (trace_buffer_hi - trace_buffer_free));
+}
+
+/* An 'S' in continuation packets indicates remainder are for
+ while-stepping. */
+
+static int seen_step_action_flag;
+
+/* Create a tracepoint (location) with given number and address. Add this
+ new tracepoint to list and sort this list. */
+
+static struct tracepoint *
+add_tracepoint (int num, CORE_ADDR addr)
+{
+ struct tracepoint *tpoint, **tp_next;
+
+ tpoint = XNEW (struct tracepoint);
+ tpoint->number = num;
+ tpoint->address = addr;
+ tpoint->numactions = 0;
+ tpoint->actions = NULL;
+ tpoint->actions_str = NULL;
+ tpoint->cond = NULL;
+ tpoint->num_step_actions = 0;
+ tpoint->step_actions = NULL;
+ tpoint->step_actions_str = NULL;
+ /* Start all off as regular (slow) tracepoints. */
+ tpoint->type = trap_tracepoint;
+ tpoint->orig_size = -1;
+ tpoint->source_strings = NULL;
+ tpoint->compiled_cond = 0;
+ tpoint->handle = NULL;
+ tpoint->next = NULL;
+
+ /* Find a place to insert this tracepoint into list in order to keep
+ the tracepoint list still in the ascending order. There may be
+ multiple tracepoints at the same address as TPOINT's, and this
+ guarantees TPOINT is inserted after all the tracepoints which are
+ set at the same address. For example, fast tracepoints A, B, C are
+ set at the same address, and D is to be insert at the same place as
+ well,
+
+ -->| A |--> | B |-->| C |->...
+
+ One jump pad was created for tracepoint A, B, and C, and the target
+ address of A is referenced/used in jump pad. So jump pad will let
+ inferior jump to A. If D is inserted in front of A, like this,
+
+ -->| D |-->| A |--> | B |-->| C |->...
+
+ without updating jump pad, D is not reachable during collect, which
+ is wrong. As we can see, the order of B, C and D doesn't matter, but
+ A should always be the `first' one. */
+ for (tp_next = &tracepoints;
+ (*tp_next) != NULL && (*tp_next)->address <= tpoint->address;
+ tp_next = &(*tp_next)->next)
+ ;
+ tpoint->next = *tp_next;
+ *tp_next = tpoint;
+ last_tracepoint = tpoint;
+
+ seen_step_action_flag = 0;
+
+ return tpoint;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Return the tracepoint with the given number and address, or NULL. */
+
+static struct tracepoint *
+find_tracepoint (int id, CORE_ADDR addr)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ if (tpoint->number == id && tpoint->address == addr)
+ return tpoint;
+
+ return NULL;
+}
+
+/* Remove TPOINT from global list. */
+
+static void
+remove_tracepoint (struct tracepoint *tpoint)
+{
+ struct tracepoint *tp, *tp_prev;
+
+ for (tp = tracepoints, tp_prev = NULL; tp && tp != tpoint;
+ tp_prev = tp, tp = tp->next)
+ ;
+
+ if (tp)
+ {
+ if (tp_prev)
+ tp_prev->next = tp->next;
+ else
+ tracepoints = tp->next;
+
+ xfree (tp);
+ }
+}
+
+/* There may be several tracepoints with the same number (because they
+ are "locations", in GDB parlance); return the next one after the
+ given tracepoint, or search from the beginning of the list if the
+ first argument is NULL. */
+
+static struct tracepoint *
+find_next_tracepoint_by_number (struct tracepoint *prev_tp, int num)
+{
+ struct tracepoint *tpoint;
+
+ if (prev_tp)
+ tpoint = prev_tp->next;
+ else
+ tpoint = tracepoints;
+ for (; tpoint; tpoint = tpoint->next)
+ if (tpoint->number == num)
+ return tpoint;
+
+ return NULL;
+}
+
+#endif
+
+/* Append another action to perform when the tracepoint triggers. */
+
+static void
+add_tracepoint_action (struct tracepoint *tpoint, const char *packet)
+{
+ const char *act;
+
+ if (*packet == 'S')
+ {
+ seen_step_action_flag = 1;
+ ++packet;
+ }
+
+ act = packet;
+
+ while (*act)
+ {
+ const char *act_start = act;
+ struct tracepoint_action *action = NULL;
+
+ switch (*act)
+ {
+ case 'M':
+ {
+ struct collect_memory_action *maction =
+ XNEW (struct collect_memory_action);
+ ULONGEST basereg;
+ int is_neg;
+
+ maction->base.type = *act;
+ action = &maction->base;
+
+ ++act;
+ is_neg = (*act == '-');
+ if (*act == '-')
+ ++act;
+ act = unpack_varlen_hex (act, &basereg);
+ ++act;
+ act = unpack_varlen_hex (act, &maction->addr);
+ ++act;
+ act = unpack_varlen_hex (act, &maction->len);
+ maction->basereg = (is_neg
+ ? - (int) basereg
+ : (int) basereg);
+ trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)",
+ pulongest (maction->len),
+ paddress (maction->addr), maction->basereg);
+ break;
+ }
+ case 'R':
+ {
+ struct collect_registers_action *raction =
+ XNEW (struct collect_registers_action);
+
+ raction->base.type = *act;
+ action = &raction->base;
+
+ trace_debug ("Want to collect registers");
+ ++act;
+ /* skip past hex digits of mask for now */
+ while (isxdigit(*act))
+ ++act;
+ break;
+ }
+ case 'L':
+ {
+ struct collect_static_trace_data_action *raction =
+ XNEW (struct collect_static_trace_data_action);
+
+ raction->base.type = *act;
+ action = &raction->base;
+
+ trace_debug ("Want to collect static trace data");
+ ++act;
+ break;
+ }
+ case 'S':
+ trace_debug ("Unexpected step action, ignoring");
+ ++act;
+ break;
+ case 'X':
+ {
+ struct eval_expr_action *xaction = XNEW (struct eval_expr_action);
+
+ xaction->base.type = *act;
+ action = &xaction->base;
+
+ trace_debug ("Want to evaluate expression");
+ xaction->expr = gdb_parse_agent_expr (&act);
+ break;
+ }
+ default:
+ trace_debug ("unknown trace action '%c', ignoring...", *act);
+ break;
+ case '-':
+ break;
+ }
+
+ if (action == NULL)
+ break;
+
+ if (seen_step_action_flag)
+ {
+ tpoint->num_step_actions++;
+
+ tpoint->step_actions
+ = XRESIZEVEC (struct tracepoint_action *, tpoint->step_actions,
+ tpoint->num_step_actions);
+ tpoint->step_actions_str
+ = XRESIZEVEC (char *, tpoint->step_actions_str,
+ tpoint->num_step_actions);
+ tpoint->step_actions[tpoint->num_step_actions - 1] = action;
+ tpoint->step_actions_str[tpoint->num_step_actions - 1]
+ = savestring (act_start, act - act_start);
+ }
+ else
+ {
+ tpoint->numactions++;
+ tpoint->actions
+ = XRESIZEVEC (struct tracepoint_action *, tpoint->actions,
+ tpoint->numactions);
+ tpoint->actions_str
+ = XRESIZEVEC (char *, tpoint->actions_str, tpoint->numactions);
+ tpoint->actions[tpoint->numactions - 1] = action;
+ tpoint->actions_str[tpoint->numactions - 1]
+ = savestring (act_start, act - act_start);
+ }
+ }
+}
+
+#endif
+
+/* Find or create a trace state variable with the given number. */
+
+static struct trace_state_variable *
+get_trace_state_variable (int num)
+{
+ struct trace_state_variable *tsv;
+
+#ifdef IN_PROCESS_AGENT
+ /* Search for an existing variable. */
+ for (tsv = alloced_trace_state_variables; tsv; tsv = tsv->next)
+ if (tsv->number == num)
+ return tsv;
+#endif
+
+ /* Search for an existing variable. */
+ for (tsv = trace_state_variables; tsv; tsv = tsv->next)
+ if (tsv->number == num)
+ return tsv;
+
+ return NULL;
+}
+
+/* Find or create a trace state variable with the given number. */
+
+static struct trace_state_variable *
+create_trace_state_variable (int num, int gdb)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+ if (tsv != NULL)
+ return tsv;
+
+ /* Create a new variable. */
+ tsv = XNEW (struct trace_state_variable);
+ tsv->number = num;
+ tsv->initial_value = 0;
+ tsv->value = 0;
+ tsv->getter = NULL;
+ tsv->name = NULL;
+#ifdef IN_PROCESS_AGENT
+ if (!gdb)
+ {
+ tsv->next = alloced_trace_state_variables;
+ alloced_trace_state_variables = tsv;
+ }
+ else
+#endif
+ {
+ tsv->next = trace_state_variables;
+ trace_state_variables = tsv;
+ }
+ return tsv;
+}
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC LONGEST get_trace_state_variable_value (int num);
+
+IP_AGENT_EXPORT_FUNC LONGEST
+get_trace_state_variable_value (int num)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping value get", num);
+ return 0;
+ }
+
+ /* Call a getter function if we have one. While it's tempting to
+ set up something to only call the getter once per tracepoint hit,
+ it could run afoul of thread races. Better to let the getter
+ handle it directly, if necessary to worry about it. */
+ if (tsv->getter)
+ tsv->value = (tsv->getter) ();
+
+ trace_debug ("get_trace_state_variable_value(%d) ==> %s",
+ num, plongest (tsv->value));
+
+ return tsv->value;
+}
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void set_trace_state_variable_value (int num,
+ LONGEST val);
+
+IP_AGENT_EXPORT_FUNC void
+set_trace_state_variable_value (int num, LONGEST val)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping value set", num);
+ return;
+ }
+
+ tsv->value = val;
+}
+
+LONGEST
+agent_get_trace_state_variable_value (int num)
+{
+ return get_trace_state_variable_value (num);
+}
+
+void
+agent_set_trace_state_variable_value (int num, LONGEST val)
+{
+ set_trace_state_variable_value (num, val);
+}
+
+static void
+set_trace_state_variable_name (int num, const char *name)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping name set", num);
+ return;
+ }
+
+ tsv->name = (char *) name;
+}
+
+static void
+set_trace_state_variable_getter (int num, LONGEST (*getter) (void))
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping getter set", num);
+ return;
+ }
+
+ tsv->getter = getter;
+}
+
+/* Add a raw traceframe for the given tracepoint. */
+
+static struct traceframe *
+add_traceframe (struct tracepoint *tpoint)
+{
+ struct traceframe *tframe;
+
+ tframe
+ = (struct traceframe *) trace_buffer_alloc (sizeof (struct traceframe));
+
+ if (tframe == NULL)
+ return NULL;
+
+ tframe->tpnum = tpoint->number;
+ tframe->data_size = 0;
+
+ return tframe;
+}
+
+/* Add a block to the traceframe currently being worked on. */
+
+static unsigned char *
+add_traceframe_block (struct traceframe *tframe,
+ struct tracepoint *tpoint, int amt)
+{
+ unsigned char *block;
+
+ if (!tframe)
+ return NULL;
+
+ block = (unsigned char *) trace_buffer_alloc (amt);
+
+ if (!block)
+ return NULL;
+
+ gdb_assert (tframe->tpnum == tpoint->number);
+
+ tframe->data_size += amt;
+ tpoint->traceframe_usage += amt;
+
+ return block;
+}
+
+/* Flag that the current traceframe is finished. */
+
+static void
+finish_traceframe (struct traceframe *tframe)
+{
+ ++traceframe_write_count;
+ ++traceframes_created;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Given a traceframe number NUM, find the NUMth traceframe in the
+ buffer. */
+
+static struct traceframe *
+find_traceframe (int num)
+{
+ struct traceframe *tframe;
+ int tfnum = 0;
+
+ for (tframe = FIRST_TRACEFRAME ();
+ tframe->tpnum != 0;
+ tframe = NEXT_TRACEFRAME (tframe))
+ {
+ if (tfnum == num)
+ return tframe;
+ ++tfnum;
+ }
+
+ return NULL;
+}
+
+static CORE_ADDR
+get_traceframe_address (struct traceframe *tframe)
+{
+ CORE_ADDR addr;
+ struct tracepoint *tpoint;
+
+ addr = traceframe_get_pc (tframe);
+
+ if (addr)
+ return addr;
+
+ /* Fallback strategy, will be incorrect for while-stepping frames
+ and multi-location tracepoints. */
+ tpoint = find_next_tracepoint_by_number (NULL, tframe->tpnum);
+ return tpoint->address;
+}
+
+/* Search for the next traceframe whose address is inside or outside
+ the given range. */
+
+static struct traceframe *
+find_next_traceframe_in_range (CORE_ADDR lo, CORE_ADDR hi, int inside_p,
+ int *tfnump)
+{
+ client_state &cs = get_client_state ();
+ struct traceframe *tframe;
+ CORE_ADDR tfaddr;
+
+ *tfnump = cs.current_traceframe + 1;
+ tframe = find_traceframe (*tfnump);
+ /* The search is not supposed to wrap around. */
+ if (!tframe)
+ {
+ *tfnump = -1;
+ return NULL;
+ }
+
+ for (; tframe->tpnum != 0; tframe = NEXT_TRACEFRAME (tframe))
+ {
+ tfaddr = get_traceframe_address (tframe);
+ if (inside_p
+ ? (lo <= tfaddr && tfaddr <= hi)
+ : (lo > tfaddr || tfaddr > hi))
+ return tframe;
+ ++*tfnump;
+ }
+
+ *tfnump = -1;
+ return NULL;
+}
+
+/* Search for the next traceframe recorded by the given tracepoint.
+ Note that for multi-location tracepoints, this will find whatever
+ location appears first. */
+
+static struct traceframe *
+find_next_traceframe_by_tracepoint (int num, int *tfnump)
+{
+ client_state &cs = get_client_state ();
+ struct traceframe *tframe;
+
+ *tfnump = cs.current_traceframe + 1;
+ tframe = find_traceframe (*tfnump);
+ /* The search is not supposed to wrap around. */
+ if (!tframe)
+ {
+ *tfnump = -1;
+ return NULL;
+ }
+
+ for (; tframe->tpnum != 0; tframe = NEXT_TRACEFRAME (tframe))
+ {
+ if (tframe->tpnum == num)
+ return tframe;
+ ++*tfnump;
+ }
+
+ *tfnump = -1;
+ return NULL;
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+/* Clear all past trace state. */
+
+static void
+cmd_qtinit (char *packet)
+{
+ client_state &cs = get_client_state ();
+ struct trace_state_variable *tsv, *prev, *next;
+
+ /* Can't do this command without a pid attached. */
+ if (current_thread == NULL)
+ {
+ write_enn (packet);
+ return;
+ }
+
+ /* Make sure we don't try to read from a trace frame. */
+ cs.current_traceframe = -1;
+
+ stop_tracing ();
+
+ trace_debug ("Initializing the trace");
+
+ clear_installed_tracepoints ();
+ clear_readonly_regions ();
+
+ tracepoints = NULL;
+ last_tracepoint = NULL;
+
+ /* Clear out any leftover trace state variables. Ones with target
+ defined getters should be kept however. */
+ prev = NULL;
+ tsv = trace_state_variables;
+ while (tsv)
+ {
+ trace_debug ("Looking at var %d", tsv->number);
+ if (tsv->getter == NULL)
+ {
+ next = tsv->next;
+ if (prev)
+ prev->next = next;
+ else
+ trace_state_variables = next;
+ trace_debug ("Deleting var %d", tsv->number);
+ free (tsv);
+ tsv = next;
+ }
+ else
+ {
+ prev = tsv;
+ tsv = tsv->next;
+ }
+ }
+
+ clear_trace_buffer ();
+ clear_inferior_trace_buffer ();
+
+ write_ok (packet);
+}
+
+/* Unprobe the UST marker at ADDRESS. */
+
+static void
+unprobe_marker_at (CORE_ADDR address)
+{
+ char cmd[IPA_CMD_BUF_SIZE];
+
+ sprintf (cmd, "unprobe_marker_at:%s", paddress (address));
+ run_inferior_command (cmd, strlen (cmd) + 1);
+}
+
+/* Restore the program to its pre-tracing state. This routine may be called
+ in error situations, so it needs to be careful about only restoring
+ from known-valid bits. */
+
+static void
+clear_installed_tracepoints (void)
+{
+ struct tracepoint *tpoint;
+ struct tracepoint *prev_stpoint;
+
+ pause_all (1);
+
+ prev_stpoint = NULL;
+
+ /* Restore any bytes overwritten by tracepoints. */
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ /* Catch the case where we might try to remove a tracepoint that
+ was never actually installed. */
+ if (tpoint->handle == NULL)
+ {
+ trace_debug ("Tracepoint %d at 0x%s was "
+ "never installed, nothing to clear",
+ tpoint->number, paddress (tpoint->address));
+ continue;
+ }
+
+ switch (tpoint->type)
+ {
+ case trap_tracepoint:
+ {
+ struct breakpoint *bp
+ = (struct breakpoint *) tpoint->handle;
+
+ delete_breakpoint (bp);
+ }
+ break;
+ case fast_tracepoint:
+ {
+ struct fast_tracepoint_jump *jump
+ = (struct fast_tracepoint_jump *) tpoint->handle;
+
+ delete_fast_tracepoint_jump (jump);
+ }
+ break;
+ case static_tracepoint:
+ if (prev_stpoint != NULL
+ && prev_stpoint->address == tpoint->address)
+ /* Nothing to do. We already unprobed a tracepoint set at
+ this marker address (and there can only be one probe
+ per marker). */
+ ;
+ else
+ {
+ unprobe_marker_at (tpoint->address);
+ prev_stpoint = tpoint;
+ }
+ break;
+ }
+
+ tpoint->handle = NULL;
+ }
+
+ unpause_all (1);
+}
+
+/* Parse a packet that defines a tracepoint. */
+
+static void
+cmd_qtdp (char *own_buf)
+{
+ int tppacket;
+ /* Whether there is a trailing hyphen at the end of the QTDP packet. */
+ int trail_hyphen = 0;
+ ULONGEST num;
+ ULONGEST addr;
+ ULONGEST count;
+ struct tracepoint *tpoint;
+ const char *packet = own_buf;
+
+ packet += strlen ("QTDP:");
+
+ /* A hyphen at the beginning marks a packet specifying actions for a
+ tracepoint already supplied. */
+ tppacket = 1;
+ if (*packet == '-')
+ {
+ tppacket = 0;
+ ++packet;
+ }
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+ ++packet; /* skip a colon */
+
+ /* See if we already have this tracepoint. */
+ tpoint = find_tracepoint (num, addr);
+
+ if (tppacket)
+ {
+ /* Duplicate tracepoints are never allowed. */
+ if (tpoint)
+ {
+ trace_debug ("Tracepoint error: tracepoint %d"
+ " at 0x%s already exists",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ return;
+ }
+
+ tpoint = add_tracepoint (num, addr);
+
+ tpoint->enabled = (*packet == 'E');
+ ++packet; /* skip 'E' */
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &count);
+ tpoint->step_count = count;
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &count);
+ tpoint->pass_count = count;
+ /* See if we have any of the additional optional fields. */
+ while (*packet == ':')
+ {
+ ++packet;
+ if (*packet == 'F')
+ {
+ tpoint->type = fast_tracepoint;
+ ++packet;
+ packet = unpack_varlen_hex (packet, &count);
+ tpoint->orig_size = count;
+ }
+ else if (*packet == 'S')
+ {
+ tpoint->type = static_tracepoint;
+ ++packet;
+ }
+ else if (*packet == 'X')
+ {
+ tpoint->cond = gdb_parse_agent_expr (&packet);
+ }
+ else if (*packet == '-')
+ break;
+ else if (*packet == '\0')
+ break;
+ else
+ trace_debug ("Unknown optional tracepoint field");
+ }
+ if (*packet == '-')
+ {
+ trail_hyphen = 1;
+ trace_debug ("Also has actions\n");
+ }
+
+ trace_debug ("Defined %stracepoint %d at 0x%s, "
+ "enabled %d step %" PRIu64 " pass %" PRIu64,
+ tpoint->type == fast_tracepoint ? "fast "
+ : tpoint->type == static_tracepoint ? "static " : "",
+ tpoint->number, paddress (tpoint->address), tpoint->enabled,
+ tpoint->step_count, tpoint->pass_count);
+ }
+ else if (tpoint)
+ add_tracepoint_action (tpoint, packet);
+ else
+ {
+ trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ return;
+ }
+
+ /* Install tracepoint during tracing only once for each tracepoint location.
+ For each tracepoint loc, GDB may send multiple QTDP packets, and we can
+ determine the last QTDP packet for one tracepoint location by checking
+ trailing hyphen in QTDP packet. */
+ if (tracing && !trail_hyphen)
+ {
+ struct tracepoint *tp = NULL;
+
+ /* Pause all threads temporarily while we patch tracepoints. */
+ pause_all (0);
+
+ /* download_tracepoint will update global `tracepoints'
+ list, so it is unsafe to leave threads in jump pad. */
+ stabilize_threads ();
+
+ /* Freeze threads. */
+ pause_all (1);
+
+
+ if (tpoint->type != trap_tracepoint)
+ {
+ /* Find another fast or static tracepoint at the same address. */
+ for (tp = tracepoints; tp; tp = tp->next)
+ {
+ if (tp->address == tpoint->address && tp->type == tpoint->type
+ && tp->number != tpoint->number)
+ break;
+ }
+
+ /* TPOINT is installed at the same address as TP. */
+ if (tp)
+ {
+ if (tpoint->type == fast_tracepoint)
+ clone_fast_tracepoint (tpoint, tp);
+ else if (tpoint->type == static_tracepoint)
+ tpoint->handle = (void *) -1;
+ }
+ }
+
+ if (use_agent && tpoint->type == fast_tracepoint
+ && agent_capability_check (AGENT_CAPA_FAST_TRACE))
+ {
+ /* Download and install fast tracepoint by agent. */
+ if (tracepoint_send_agent (tpoint) == 0)
+ write_ok (own_buf);
+ else
+ {
+ write_enn (own_buf);
+ remove_tracepoint (tpoint);
+ }
+ }
+ else
+ {
+ download_tracepoint (tpoint);
+
+ if (tpoint->type == trap_tracepoint || tp == NULL)
+ {
+ install_tracepoint (tpoint, own_buf);
+ if (strcmp (own_buf, "OK") != 0)
+ remove_tracepoint (tpoint);
+ }
+ else
+ write_ok (own_buf);
+ }
+
+ unpause_all (1);
+ return;
+ }
+
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtdpsrc (char *own_buf)
+{
+ ULONGEST num, addr, start, slen;
+ struct tracepoint *tpoint;
+ const char *packet = own_buf;
+ const char *saved;
+ char *srctype, *src;
+ size_t nbytes;
+ struct source_string *last, *newlast;
+
+ packet += strlen ("QTDPsrc:");
+
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+ ++packet; /* skip a colon */
+
+ /* See if we already have this tracepoint. */
+ tpoint = find_tracepoint (num, addr);
+
+ if (!tpoint)
+ {
+ trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ return;
+ }
+
+ saved = packet;
+ packet = strchr (packet, ':');
+ srctype = (char *) xmalloc (packet - saved + 1);
+ memcpy (srctype, saved, packet - saved);
+ srctype[packet - saved] = '\0';
+ ++packet;
+ packet = unpack_varlen_hex (packet, &start);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &slen);
+ ++packet; /* skip a colon */
+ src = (char *) xmalloc (slen + 1);
+ nbytes = hex2bin (packet, (gdb_byte *) src, strlen (packet) / 2);
+ src[nbytes] = '\0';
+
+ newlast = XNEW (struct source_string);
+ newlast->type = srctype;
+ newlast->str = src;
+ newlast->next = NULL;
+ /* Always add a source string to the end of the list;
+ this keeps sequences of actions/commands in the right
+ order. */
+ if (tpoint->source_strings)
+ {
+ for (last = tpoint->source_strings; last->next; last = last->next)
+ ;
+ last->next = newlast;
+ }
+ else
+ tpoint->source_strings = newlast;
+
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtdv (char *own_buf)
+{
+ ULONGEST num, val, builtin;
+ char *varname;
+ size_t nbytes;
+ struct trace_state_variable *tsv;
+ const char *packet = own_buf;
+
+ packet += strlen ("QTDV:");
+
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &val);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &builtin);
+ ++packet; /* skip a colon */
+
+ nbytes = strlen (packet) / 2;
+ varname = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (packet, (gdb_byte *) varname, nbytes);
+ varname[nbytes] = '\0';
+
+ tsv = create_trace_state_variable (num, 1);
+ tsv->initial_value = (LONGEST) val;
+ tsv->name = varname;
+
+ set_trace_state_variable_value (num, (LONGEST) val);
+
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtenable_disable (char *own_buf, int enable)
+{
+ const char *packet = own_buf;
+ ULONGEST num, addr;
+ struct tracepoint *tp;
+
+ packet += strlen (enable ? "QTEnable:" : "QTDisable:");
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+
+ tp = find_tracepoint (num, addr);
+
+ if (tp)
+ {
+ if ((enable && tp->enabled) || (!enable && !tp->enabled))
+ {
+ trace_debug ("Tracepoint %d at 0x%s is already %s",
+ (int) num, paddress (addr),
+ enable ? "enabled" : "disabled");
+ write_ok (own_buf);
+ return;
+ }
+
+ trace_debug ("%s tracepoint %d at 0x%s",
+ enable ? "Enabling" : "Disabling",
+ (int) num, paddress (addr));
+
+ tp->enabled = enable;
+
+ if (tp->type == fast_tracepoint || tp->type == static_tracepoint)
+ {
+ int ret;
+ int offset = offsetof (struct tracepoint, enabled);
+ CORE_ADDR obj_addr = tp->obj_addr_on_target + offset;
+
+ ret = prepare_to_access_memory ();
+ if (ret)
+ {
+ trace_debug ("Failed to temporarily stop inferior threads");
+ write_enn (own_buf);
+ return;
+ }
+
+ ret = write_inferior_int8 (obj_addr, enable);
+ done_accessing_memory ();
+
+ if (ret)
+ {
+ trace_debug ("Cannot write enabled flag into "
+ "inferior process memory");
+ write_enn (own_buf);
+ return;
+ }
+ }
+
+ write_ok (own_buf);
+ }
+ else
+ {
+ trace_debug ("Tracepoint %d at 0x%s not found",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ }
+}
+
+static void
+cmd_qtv (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ ULONGEST num;
+ LONGEST val = 0;
+ int err;
+ char *packet = own_buf;
+
+ packet += strlen ("qTV:");
+ unpack_varlen_hex (packet, &num);
+
+ if (cs.current_traceframe >= 0)
+ {
+ err = traceframe_read_tsv ((int) num, &val);
+ if (err)
+ {
+ strcpy (own_buf, "U");
+ return;
+ }
+ }
+ /* Only make tsv's be undefined before the first trace run. After a
+ trace run is over, the user might want to see the last value of
+ the tsv, and it might not be available in a traceframe. */
+ else if (!tracing && strcmp (tracing_stop_reason, "tnotrun") == 0)
+ {
+ strcpy (own_buf, "U");
+ return;
+ }
+ else
+ val = get_trace_state_variable_value (num);
+
+ sprintf (own_buf, "V%s", phex_nz (val, 0));
+}
+
+/* Clear out the list of readonly regions. */
+
+static void
+clear_readonly_regions (void)
+{
+ struct readonly_region *roreg;
+
+ while (readonly_regions)
+ {
+ roreg = readonly_regions;
+ readonly_regions = readonly_regions->next;
+ free (roreg);
+ }
+}
+
+/* Parse the collection of address ranges whose contents GDB believes
+ to be unchanging and so can be read directly from target memory
+ even while looking at a traceframe. */
+
+static void
+cmd_qtro (char *own_buf)
+{
+ ULONGEST start, end;
+ struct readonly_region *roreg;
+ const char *packet = own_buf;
+
+ trace_debug ("Want to mark readonly regions");
+
+ clear_readonly_regions ();
+
+ packet += strlen ("QTro");
+
+ while (*packet == ':')
+ {
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &start);
+ ++packet; /* skip a comma */
+ packet = unpack_varlen_hex (packet, &end);
+
+ roreg = XNEW (struct readonly_region);
+ roreg->start = start;
+ roreg->end = end;
+ roreg->next = readonly_regions;
+ readonly_regions = roreg;
+ trace_debug ("Added readonly region from 0x%s to 0x%s",
+ paddress (roreg->start), paddress (roreg->end));
+ }
+
+ write_ok (own_buf);
+}
+
+/* Test to see if the given range is in our list of readonly ranges.
+ We only test for being entirely within a range, GDB is not going to
+ send a single memory packet that spans multiple regions. */
+
+int
+in_readonly_region (CORE_ADDR addr, ULONGEST length)
+{
+ struct readonly_region *roreg;
+
+ for (roreg = readonly_regions; roreg; roreg = roreg->next)
+ if (roreg->start <= addr && (addr + length - 1) <= roreg->end)
+ return 1;
+
+ return 0;
+}
+
+static CORE_ADDR gdb_jump_pad_head;
+
+/* Return the address of the next free jump space. */
+
+static CORE_ADDR
+get_jump_space_head (void)
+{
+ if (gdb_jump_pad_head == 0)
+ {
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer,
+ &gdb_jump_pad_head))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting jump_pad_buffer");
+ }
+ }
+
+ return gdb_jump_pad_head;
+}
+
+/* Reserve USED bytes from the jump space. */
+
+static void
+claim_jump_space (ULONGEST used)
+{
+ trace_debug ("claim_jump_space reserves %s bytes at %s",
+ pulongest (used), paddress (gdb_jump_pad_head));
+ gdb_jump_pad_head += used;
+}
+
+static CORE_ADDR trampoline_buffer_head = 0;
+static CORE_ADDR trampoline_buffer_tail;
+
+/* Reserve USED bytes from the trampoline buffer and return the
+ address of the start of the reserved space in TRAMPOLINE. Returns
+ non-zero if the space is successfully claimed. */
+
+int
+claim_trampoline_space (ULONGEST used, CORE_ADDR *trampoline)
+{
+ if (!trampoline_buffer_head)
+ {
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
+ &trampoline_buffer_tail))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting trampoline_buffer");
+ }
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &trampoline_buffer_head))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting trampoline_buffer_end");
+ }
+ }
+
+ /* Start claiming space from the top of the trampoline space. If
+ the space is located at the bottom of the virtual address space,
+ this reduces the possibility that corruption will occur if a null
+ pointer is used to write to memory. */
+ if (trampoline_buffer_head - trampoline_buffer_tail < used)
+ {
+ trace_debug ("claim_trampoline_space failed to reserve %s bytes",
+ pulongest (used));
+ return 0;
+ }
+
+ trampoline_buffer_head -= used;
+
+ trace_debug ("claim_trampoline_space reserves %s bytes at %s",
+ pulongest (used), paddress (trampoline_buffer_head));
+
+ *trampoline = trampoline_buffer_head;
+ return 1;
+}
+
+/* Returns non-zero if there is space allocated for use in trampolines
+ for fast tracepoints. */
+
+int
+have_fast_tracepoint_trampoline_buffer (char *buf)
+{
+ CORE_ADDR trampoline_end, errbuf;
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &trampoline_end))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting trampoline_buffer_end");
+ }
+
+ if (buf)
+ {
+ buf[0] = '\0';
+ strcpy (buf, "was claiming");
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_error,
+ &errbuf))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting errbuf");
+ }
+
+ read_inferior_memory (errbuf, (unsigned char *) buf, 100);
+ }
+
+ return trampoline_end != 0;
+}
+
+/* Ask the IPA to probe the marker at ADDRESS. Returns -1 if running
+ the command fails, or 0 otherwise. If the command ran
+ successfully, but probing the marker failed, ERROUT will be filled
+ with the error to reply to GDB, and -1 is also returned. This
+ allows directly passing IPA errors to GDB. */
+
+static int
+probe_marker_at (CORE_ADDR address, char *errout)
+{
+ char cmd[IPA_CMD_BUF_SIZE];
+ int err;
+
+ sprintf (cmd, "probe_marker_at:%s", paddress (address));
+ err = run_inferior_command (cmd, strlen (cmd) + 1);
+
+ if (err == 0)
+ {
+ if (*cmd == 'E')
+ {
+ strcpy (errout, cmd);
+ return -1;
+ }
+ }
+
+ return err;
+}
+
+static void
+clone_fast_tracepoint (struct tracepoint *to, const struct tracepoint *from)
+{
+ to->jump_pad = from->jump_pad;
+ to->jump_pad_end = from->jump_pad_end;
+ to->trampoline = from->trampoline;
+ to->trampoline_end = from->trampoline_end;
+ to->adjusted_insn_addr = from->adjusted_insn_addr;
+ to->adjusted_insn_addr_end = from->adjusted_insn_addr_end;
+ to->handle = from->handle;
+
+ gdb_assert (from->handle);
+ inc_ref_fast_tracepoint_jump ((struct fast_tracepoint_jump *) from->handle);
+}
+
+#define MAX_JUMP_SIZE 20
+
+/* Install fast tracepoint. Return 0 if successful, otherwise return
+ non-zero. */
+
+static int
+install_fast_tracepoint (struct tracepoint *tpoint, char *errbuf)
+{
+ CORE_ADDR jentry, jump_entry;
+ CORE_ADDR trampoline;
+ CORE_ADDR collect;
+ ULONGEST trampoline_size;
+ int err = 0;
+ /* The jump to the jump pad of the last fast tracepoint
+ installed. */
+ unsigned char fjump[MAX_JUMP_SIZE];
+ ULONGEST fjump_size;
+
+ if (tpoint->orig_size < target_get_min_fast_tracepoint_insn_len ())
+ {
+ trace_debug ("Requested a fast tracepoint on an instruction "
+ "that is of less than the minimum length.");
+ return 0;
+ }
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_collect_ptr,
+ &collect))
+ {
+ error ("error extracting gdb_collect_ptr");
+ return 1;
+ }
+
+ jentry = jump_entry = get_jump_space_head ();
+
+ trampoline = 0;
+ trampoline_size = 0;
+
+ /* Install the jump pad. */
+ err = install_fast_tracepoint_jump_pad (tpoint->obj_addr_on_target,
+ tpoint->address,
+ collect,
+ ipa_sym_addrs.addr_collecting,
+ tpoint->orig_size,
+ &jentry,
+ &trampoline, &trampoline_size,
+ fjump, &fjump_size,
+ &tpoint->adjusted_insn_addr,
+ &tpoint->adjusted_insn_addr_end,
+ errbuf);
+
+ if (err)
+ return 1;
+
+ /* Wire it in. */
+ tpoint->handle = set_fast_tracepoint_jump (tpoint->address, fjump,
+ fjump_size);
+
+ if (tpoint->handle != NULL)
+ {
+ tpoint->jump_pad = jump_entry;
+ tpoint->jump_pad_end = jentry;
+ tpoint->trampoline = trampoline;
+ tpoint->trampoline_end = trampoline + trampoline_size;
+
+ /* Pad to 8-byte alignment. */
+ jentry = ((jentry + 7) & ~0x7);
+ claim_jump_space (jentry - jump_entry);
+ }
+
+ return 0;
+}
+
+
+/* Install tracepoint TPOINT, and write reply message in OWN_BUF. */
+
+static void
+install_tracepoint (struct tracepoint *tpoint, char *own_buf)
+{
+ tpoint->handle = NULL;
+ *own_buf = '\0';
+
+ if (tpoint->type == trap_tracepoint)
+ {
+ /* Tracepoints are installed as memory breakpoints. Just go
+ ahead and install the trap. The breakpoints module
+ handles duplicated breakpoints, and the memory read
+ routine handles un-patching traps from memory reads. */
+ tpoint->handle = set_breakpoint_at (tpoint->address,
+ tracepoint_handler);
+ }
+ else if (tpoint->type == fast_tracepoint || tpoint->type == static_tracepoint)
+ {
+ if (!agent_loaded_p ())
+ {
+ trace_debug ("Requested a %s tracepoint, but fast "
+ "tracepoints aren't supported.",
+ tpoint->type == static_tracepoint ? "static" : "fast");
+ write_e_ipa_not_loaded (own_buf);
+ return;
+ }
+ if (tpoint->type == static_tracepoint
+ && !in_process_agent_supports_ust ())
+ {
+ trace_debug ("Requested a static tracepoint, but static "
+ "tracepoints are not supported.");
+ write_e_ust_not_loaded (own_buf);
+ return;
+ }
+
+ if (tpoint->type == fast_tracepoint)
+ install_fast_tracepoint (tpoint, own_buf);
+ else
+ {
+ if (probe_marker_at (tpoint->address, own_buf) == 0)
+ tpoint->handle = (void *) -1;
+ }
+
+ }
+ else
+ internal_error (__FILE__, __LINE__, "Unknown tracepoint type");
+
+ if (tpoint->handle == NULL)
+ {
+ if (*own_buf == '\0')
+ write_enn (own_buf);
+ }
+ else
+ write_ok (own_buf);
+}
+
+static void download_tracepoint_1 (struct tracepoint *tpoint);
+
+static void
+cmd_qtstart (char *packet)
+{
+ struct tracepoint *tpoint, *prev_ftpoint, *prev_stpoint;
+ CORE_ADDR tpptr = 0, prev_tpptr = 0;
+
+ trace_debug ("Starting the trace");
+
+ /* Pause all threads temporarily while we patch tracepoints. */
+ pause_all (0);
+
+ /* Get threads out of jump pads. Safe to do here, since this is a
+ top level command. And, required to do here, since we're
+ deleting/rewriting jump pads. */
+
+ stabilize_threads ();
+
+ /* Freeze threads. */
+ pause_all (1);
+
+ /* Sync the fast tracepoints list in the inferior ftlib. */
+ if (agent_loaded_p ())
+ download_trace_state_variables ();
+
+ /* No previous fast tpoint yet. */
+ prev_ftpoint = NULL;
+
+ /* No previous static tpoint yet. */
+ prev_stpoint = NULL;
+
+ *packet = '\0';
+
+ if (agent_loaded_p ())
+ {
+ /* Tell IPA about the correct tdesc. */
+ if (write_inferior_integer (ipa_sym_addrs.addr_ipa_tdesc_idx,
+ target_get_ipa_tdesc_idx ()))
+ error ("Error setting ipa_tdesc_idx variable in lib");
+ }
+
+ /* Start out empty. */
+ if (agent_loaded_p ())
+ write_inferior_data_pointer (ipa_sym_addrs.addr_tracepoints, 0);
+
+ /* Download and install tracepoints. */
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ /* Ensure all the hit counts start at zero. */
+ tpoint->hit_count = 0;
+ tpoint->traceframe_usage = 0;
+
+ if (tpoint->type == trap_tracepoint)
+ {
+ /* Tracepoints are installed as memory breakpoints. Just go
+ ahead and install the trap. The breakpoints module
+ handles duplicated breakpoints, and the memory read
+ routine handles un-patching traps from memory reads. */
+ tpoint->handle = set_breakpoint_at (tpoint->address,
+ tracepoint_handler);
+ }
+ else if (tpoint->type == fast_tracepoint
+ || tpoint->type == static_tracepoint)
+ {
+ if (maybe_write_ipa_not_loaded (packet))
+ {
+ trace_debug ("Requested a %s tracepoint, but fast "
+ "tracepoints aren't supported.",
+ tpoint->type == static_tracepoint
+ ? "static" : "fast");
+ break;
+ }
+
+ if (tpoint->type == fast_tracepoint)
+ {
+ int use_agent_p
+ = use_agent && agent_capability_check (AGENT_CAPA_FAST_TRACE);
+
+ if (prev_ftpoint != NULL
+ && prev_ftpoint->address == tpoint->address)
+ {
+ if (use_agent_p)
+ tracepoint_send_agent (tpoint);
+ else
+ download_tracepoint_1 (tpoint);
+
+ clone_fast_tracepoint (tpoint, prev_ftpoint);
+ }
+ else
+ {
+ /* Tracepoint is installed successfully? */
+ int installed = 0;
+
+ /* Download and install fast tracepoint by agent. */
+ if (use_agent_p)
+ installed = !tracepoint_send_agent (tpoint);
+ else
+ {
+ download_tracepoint_1 (tpoint);
+ installed = !install_fast_tracepoint (tpoint, packet);
+ }
+
+ if (installed)
+ prev_ftpoint = tpoint;
+ }
+ }
+ else
+ {
+ if (!in_process_agent_supports_ust ())
+ {
+ trace_debug ("Requested a static tracepoint, but static "
+ "tracepoints are not supported.");
+ break;
+ }
+
+ download_tracepoint_1 (tpoint);
+ /* Can only probe a given marker once. */
+ if (prev_stpoint != NULL
+ && prev_stpoint->address == tpoint->address)
+ tpoint->handle = (void *) -1;
+ else
+ {
+ if (probe_marker_at (tpoint->address, packet) == 0)
+ {
+ tpoint->handle = (void *) -1;
+
+ /* So that we can handle multiple static tracepoints
+ at the same address easily. */
+ prev_stpoint = tpoint;
+ }
+ }
+ }
+
+ prev_tpptr = tpptr;
+ tpptr = tpoint->obj_addr_on_target;
+
+ if (tpoint == tracepoints)
+ /* First object in list, set the head pointer in the
+ inferior. */
+ write_inferior_data_pointer (ipa_sym_addrs.addr_tracepoints, tpptr);
+ else
+ write_inferior_data_pointer (prev_tpptr
+ + offsetof (struct tracepoint, next),
+ tpptr);
+ }
+
+ /* Any failure in the inner loop is sufficient cause to give
+ up. */
+ if (tpoint->handle == NULL)
+ break;
+ }
+
+ /* Any error in tracepoint insertion is unacceptable; better to
+ address the problem now, than end up with a useless or misleading
+ trace run. */
+ if (tpoint != NULL)
+ {
+ clear_installed_tracepoints ();
+ if (*packet == '\0')
+ write_enn (packet);
+ unpause_all (1);
+ return;
+ }
+
+ stopping_tracepoint = NULL;
+ trace_buffer_is_full = 0;
+ expr_eval_result = expr_eval_no_error;
+ error_tracepoint = NULL;
+ tracing_start_time = get_timestamp ();
+
+ /* Tracing is now active, hits will now start being logged. */
+ tracing = 1;
+
+ if (agent_loaded_p ())
+ {
+ if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 1))
+ {
+ internal_error (__FILE__, __LINE__,
+ "Error setting tracing variable in lib");
+ }
+
+ if (write_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint,
+ 0))
+ {
+ internal_error (__FILE__, __LINE__,
+ "Error clearing stopping_tracepoint variable"
+ " in lib");
+ }
+
+ if (write_inferior_integer (ipa_sym_addrs.addr_trace_buffer_is_full, 0))
+ {
+ internal_error (__FILE__, __LINE__,
+ "Error clearing trace_buffer_is_full variable"
+ " in lib");
+ }
+
+ stop_tracing_bkpt = set_breakpoint_at (ipa_sym_addrs.addr_stop_tracing,
+ stop_tracing_handler);
+ if (stop_tracing_bkpt == NULL)
+ error ("Error setting stop_tracing breakpoint");
+
+ flush_trace_buffer_bkpt
+ = set_breakpoint_at (ipa_sym_addrs.addr_flush_trace_buffer,
+ flush_trace_buffer_handler);
+ if (flush_trace_buffer_bkpt == NULL)
+ error ("Error setting flush_trace_buffer breakpoint");
+ }
+
+ unpause_all (1);
+
+ write_ok (packet);
+}
+
+/* End a tracing run, filling in a stop reason to report back to GDB,
+ and removing the tracepoints from the code. */
+
+void
+stop_tracing (void)
+{
+ if (!tracing)
+ {
+ trace_debug ("Tracing is already off, ignoring");
+ return;
+ }
+
+ trace_debug ("Stopping the trace");
+
+ /* Pause all threads before removing fast jumps from memory,
+ breakpoints, and touching IPA state variables (inferior memory).
+ Some thread may hit the internal tracing breakpoints, or be
+ collecting this moment, but that's ok, we don't release the
+ tpoint object's memory or the jump pads here (we only do that
+ when we're sure we can move all threads out of the jump pads).
+ We can't now, since we may be getting here due to the inferior
+ agent calling us. */
+ pause_all (1);
+
+ /* Stop logging. Tracepoints can still be hit, but they will not be
+ recorded. */
+ tracing = 0;
+ if (agent_loaded_p ())
+ {
+ if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 0))
+ {
+ internal_error (__FILE__, __LINE__,
+ "Error clearing tracing variable in lib");
+ }
+ }
+
+ tracing_stop_time = get_timestamp ();
+ tracing_stop_reason = "t???";
+ tracing_stop_tpnum = 0;
+ if (stopping_tracepoint)
+ {
+ trace_debug ("Stopping the trace because "
+ "tracepoint %d was hit %" PRIu64 " times",
+ stopping_tracepoint->number,
+ stopping_tracepoint->pass_count);
+ tracing_stop_reason = "tpasscount";
+ tracing_stop_tpnum = stopping_tracepoint->number;
+ }
+ else if (trace_buffer_is_full)
+ {
+ trace_debug ("Stopping the trace because the trace buffer is full");
+ tracing_stop_reason = "tfull";
+ }
+ else if (expr_eval_result != expr_eval_no_error)
+ {
+ trace_debug ("Stopping the trace because of an expression eval error");
+ tracing_stop_reason = eval_result_names[expr_eval_result];
+ tracing_stop_tpnum = error_tracepoint->number;
+ }
+#ifndef IN_PROCESS_AGENT
+ else if (!gdb_connected ())
+ {
+ trace_debug ("Stopping the trace because GDB disconnected");
+ tracing_stop_reason = "tdisconnected";
+ }
+#endif
+ else
+ {
+ trace_debug ("Stopping the trace because of a tstop command");
+ tracing_stop_reason = "tstop";
+ }
+
+ stopping_tracepoint = NULL;
+ error_tracepoint = NULL;
+
+ /* Clear out the tracepoints. */
+ clear_installed_tracepoints ();
+
+ if (agent_loaded_p ())
+ {
+ /* Pull in fast tracepoint trace frames from the inferior lib
+ buffer into our buffer, even if our buffer is already full,
+ because we want to present the full number of created frames
+ in addition to what fit in the trace buffer. */
+ upload_fast_traceframes ();
+ }
+
+ if (stop_tracing_bkpt != NULL)
+ {
+ delete_breakpoint (stop_tracing_bkpt);
+ stop_tracing_bkpt = NULL;
+ }
+
+ if (flush_trace_buffer_bkpt != NULL)
+ {
+ delete_breakpoint (flush_trace_buffer_bkpt);
+ flush_trace_buffer_bkpt = NULL;
+ }
+
+ unpause_all (1);
+}
+
+static int
+stop_tracing_handler (CORE_ADDR addr)
+{
+ trace_debug ("lib hit stop_tracing");
+
+ /* Don't actually handle it here. When we stop tracing we remove
+ breakpoints from the inferior, and that is not allowed in a
+ breakpoint handler (as the caller is walking the breakpoint
+ list). */
+ return 0;
+}
+
+static int
+flush_trace_buffer_handler (CORE_ADDR addr)
+{
+ trace_debug ("lib hit flush_trace_buffer");
+ return 0;
+}
+
+static void
+cmd_qtstop (char *packet)
+{
+ stop_tracing ();
+ write_ok (packet);
+}
+
+static void
+cmd_qtdisconnected (char *own_buf)
+{
+ ULONGEST setting;
+ char *packet = own_buf;
+
+ packet += strlen ("QTDisconnected:");
+
+ unpack_varlen_hex (packet, &setting);
+
+ write_ok (own_buf);
+
+ disconnected_tracing = setting;
+}
+
+static void
+cmd_qtframe (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ ULONGEST frame, pc, lo, hi, num;
+ int tfnum, tpnum;
+ struct traceframe *tframe;
+ const char *packet = own_buf;
+
+ packet += strlen ("QTFrame:");
+
+ if (startswith (packet, "pc:"))
+ {
+ packet += strlen ("pc:");
+ unpack_varlen_hex (packet, &pc);
+ trace_debug ("Want to find next traceframe at pc=0x%s", paddress (pc));
+ tframe = find_next_traceframe_in_range (pc, pc, 1, &tfnum);
+ }
+ else if (startswith (packet, "range:"))
+ {
+ packet += strlen ("range:");
+ packet = unpack_varlen_hex (packet, &lo);
+ ++packet;
+ unpack_varlen_hex (packet, &hi);
+ trace_debug ("Want to find next traceframe in the range 0x%s to 0x%s",
+ paddress (lo), paddress (hi));
+ tframe = find_next_traceframe_in_range (lo, hi, 1, &tfnum);
+ }
+ else if (startswith (packet, "outside:"))
+ {
+ packet += strlen ("outside:");
+ packet = unpack_varlen_hex (packet, &lo);
+ ++packet;
+ unpack_varlen_hex (packet, &hi);
+ trace_debug ("Want to find next traceframe "
+ "outside the range 0x%s to 0x%s",
+ paddress (lo), paddress (hi));
+ tframe = find_next_traceframe_in_range (lo, hi, 0, &tfnum);
+ }
+ else if (startswith (packet, "tdp:"))
+ {
+ packet += strlen ("tdp:");
+ unpack_varlen_hex (packet, &num);
+ tpnum = (int) num;
+ trace_debug ("Want to find next traceframe for tracepoint %d", tpnum);
+ tframe = find_next_traceframe_by_tracepoint (tpnum, &tfnum);
+ }
+ else
+ {
+ unpack_varlen_hex (packet, &frame);
+ tfnum = (int) frame;
+ if (tfnum == -1)
+ {
+ trace_debug ("Want to stop looking at traceframes");
+ cs.current_traceframe = -1;
+ write_ok (own_buf);
+ return;
+ }
+ trace_debug ("Want to look at traceframe %d", tfnum);
+ tframe = find_traceframe (tfnum);
+ }
+
+ if (tframe)
+ {
+ cs.current_traceframe = tfnum;
+ sprintf (own_buf, "F%xT%x", tfnum, tframe->tpnum);
+ }
+ else
+ sprintf (own_buf, "F-1");
+}
+
+static void
+cmd_qtstatus (char *packet)
+{
+ char *stop_reason_rsp = NULL;
+ char *buf1, *buf2, *buf3;
+ const char *str;
+ int slen;
+
+ /* Translate the plain text of the notes back into hex for
+ transmission. */
+
+ str = (tracing_user_name ? tracing_user_name : "");
+ slen = strlen (str);
+ buf1 = (char *) alloca (slen * 2 + 1);
+ bin2hex ((gdb_byte *) str, buf1, slen);
+
+ str = (tracing_notes ? tracing_notes : "");
+ slen = strlen (str);
+ buf2 = (char *) alloca (slen * 2 + 1);
+ bin2hex ((gdb_byte *) str, buf2, slen);
+
+ str = (tracing_stop_note ? tracing_stop_note : "");
+ slen = strlen (str);
+ buf3 = (char *) alloca (slen * 2 + 1);
+ bin2hex ((gdb_byte *) str, buf3, slen);
+
+ trace_debug ("Returning trace status as %d, stop reason %s",
+ tracing, tracing_stop_reason);
+
+ if (agent_loaded_p ())
+ {
+ pause_all (1);
+
+ upload_fast_traceframes ();
+
+ unpause_all (1);
+ }
+
+ stop_reason_rsp = (char *) tracing_stop_reason;
+
+ /* The user visible error string in terror needs to be hex encoded.
+ We leave it as plain string in `tracing_stop_reason' to ease
+ debugging. */
+ if (startswith (stop_reason_rsp, "terror:"))
+ {
+ const char *result_name;
+ int hexstr_len;
+ char *p;
+
+ result_name = stop_reason_rsp + strlen ("terror:");
+ hexstr_len = strlen (result_name) * 2;
+ p = stop_reason_rsp
+ = (char *) alloca (strlen ("terror:") + hexstr_len + 1);
+ strcpy (p, "terror:");
+ p += strlen (p);
+ bin2hex ((gdb_byte *) result_name, p, strlen (result_name));
+ }
+
+ /* If this was a forced stop, include any stop note that was supplied. */
+ if (strcmp (stop_reason_rsp, "tstop") == 0)
+ {
+ stop_reason_rsp = (char *) alloca (strlen ("tstop:") + strlen (buf3) + 1);
+ strcpy (stop_reason_rsp, "tstop:");
+ strcat (stop_reason_rsp, buf3);
+ }
+
+ sprintf (packet,
+ "T%d;"
+ "%s:%x;"
+ "tframes:%x;tcreated:%x;"
+ "tfree:%x;tsize:%s;"
+ "circular:%d;"
+ "disconn:%d;"
+ "starttime:%s;stoptime:%s;"
+ "username:%s;notes:%s:",
+ tracing ? 1 : 0,
+ stop_reason_rsp, tracing_stop_tpnum,
+ traceframe_count, traceframes_created,
+ free_space (), phex_nz (trace_buffer_hi - trace_buffer_lo, 0),
+ circular_trace_buffer,
+ disconnected_tracing,
+ phex_nz (tracing_start_time, sizeof (tracing_start_time)),
+ phex_nz (tracing_stop_time, sizeof (tracing_stop_time)),
+ buf1, buf2);
+}
+
+static void
+cmd_qtp (char *own_buf)
+{
+ ULONGEST num, addr;
+ struct tracepoint *tpoint;
+ const char *packet = own_buf;
+
+ packet += strlen ("qTP:");
+
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+
+ /* See if we already have this tracepoint. */
+ tpoint = find_tracepoint (num, addr);
+
+ if (!tpoint)
+ {
+ trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ return;
+ }
+
+ sprintf (own_buf, "V%" PRIu64 ":%" PRIu64 "", tpoint->hit_count,
+ tpoint->traceframe_usage);
+}
+
+/* State variables to help return all the tracepoint bits. */
+static struct tracepoint *cur_tpoint;
+static unsigned int cur_action;
+static unsigned int cur_step_action;
+static struct source_string *cur_source_string;
+static struct trace_state_variable *cur_tsv;
+
+/* Compose a response that is an imitation of the syntax by which the
+ tracepoint was originally downloaded. */
+
+static void
+response_tracepoint (char *packet, struct tracepoint *tpoint)
+{
+ char *buf;
+
+ sprintf (packet, "T%x:%s:%c:%" PRIx64 ":%" PRIx64, tpoint->number,
+ paddress (tpoint->address),
+ (tpoint->enabled ? 'E' : 'D'), tpoint->step_count,
+ tpoint->pass_count);
+ if (tpoint->type == fast_tracepoint)
+ sprintf (packet + strlen (packet), ":F%x", tpoint->orig_size);
+ else if (tpoint->type == static_tracepoint)
+ sprintf (packet + strlen (packet), ":S");
+
+ if (tpoint->cond)
+ {
+ buf = gdb_unparse_agent_expr (tpoint->cond);
+ sprintf (packet + strlen (packet), ":X%x,%s",
+ tpoint->cond->length, buf);
+ free (buf);
+ }
+}
+
+/* Compose a response that is an imitation of the syntax by which the
+ tracepoint action was originally downloaded (with the difference
+ that due to the way we store the actions, this will output a packet
+ per action, while GDB could have combined more than one action
+ per-packet. */
+
+static void
+response_action (char *packet, struct tracepoint *tpoint,
+ char *taction, int step)
+{
+ sprintf (packet, "%c%x:%s:%s",
+ (step ? 'S' : 'A'), tpoint->number, paddress (tpoint->address),
+ taction);
+}
+
+/* Compose a response that is an imitation of the syntax by which the
+ tracepoint source piece was originally downloaded. */
+
+static void
+response_source (char *packet,
+ struct tracepoint *tpoint, struct source_string *src)
+{
+ char *buf;
+ int len;
+
+ len = strlen (src->str);
+ buf = (char *) alloca (len * 2 + 1);
+ bin2hex ((gdb_byte *) src->str, buf, len);
+
+ sprintf (packet, "Z%x:%s:%s:%x:%x:%s",
+ tpoint->number, paddress (tpoint->address),
+ src->type, 0, len, buf);
+}
+
+/* Return the first piece of tracepoint definition, and initialize the
+ state machine that will iterate through all the tracepoint
+ bits. */
+
+static void
+cmd_qtfp (char *packet)
+{
+ trace_debug ("Returning first tracepoint definition piece");
+
+ cur_tpoint = tracepoints;
+ cur_action = cur_step_action = 0;
+ cur_source_string = NULL;
+
+ if (cur_tpoint)
+ response_tracepoint (packet, cur_tpoint);
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional pieces of tracepoint definition. Each action and
+ stepping action must go into its own packet, because of packet size
+ limits, and so we use state variables to deliver one piece at a
+ time. */
+
+static void
+cmd_qtsp (char *packet)
+{
+ trace_debug ("Returning subsequent tracepoint definition piece");
+
+ if (!cur_tpoint)
+ {
+ /* This case would normally never occur, but be prepared for
+ GDB misbehavior. */
+ strcpy (packet, "l");
+ }
+ else if (cur_action < cur_tpoint->numactions)
+ {
+ response_action (packet, cur_tpoint,
+ cur_tpoint->actions_str[cur_action], 0);
+ ++cur_action;
+ }
+ else if (cur_step_action < cur_tpoint->num_step_actions)
+ {
+ response_action (packet, cur_tpoint,
+ cur_tpoint->step_actions_str[cur_step_action], 1);
+ ++cur_step_action;
+ }
+ else if ((cur_source_string
+ ? cur_source_string->next
+ : cur_tpoint->source_strings))
+ {
+ if (cur_source_string)
+ cur_source_string = cur_source_string->next;
+ else
+ cur_source_string = cur_tpoint->source_strings;
+ response_source (packet, cur_tpoint, cur_source_string);
+ }
+ else
+ {
+ cur_tpoint = cur_tpoint->next;
+ cur_action = cur_step_action = 0;
+ cur_source_string = NULL;
+ if (cur_tpoint)
+ response_tracepoint (packet, cur_tpoint);
+ else
+ strcpy (packet, "l");
+ }
+}
+
+/* Compose a response that is an imitation of the syntax by which the
+ trace state variable was originally downloaded. */
+
+static void
+response_tsv (char *packet, struct trace_state_variable *tsv)
+{
+ char *buf = (char *) "";
+ int namelen;
+
+ if (tsv->name)
+ {
+ namelen = strlen (tsv->name);
+ buf = (char *) alloca (namelen * 2 + 1);
+ bin2hex ((gdb_byte *) tsv->name, buf, namelen);
+ }
+
+ sprintf (packet, "%x:%s:%x:%s", tsv->number, phex_nz (tsv->initial_value, 0),
+ tsv->getter ? 1 : 0, buf);
+}
+
+/* Return the first trace state variable definition, and initialize
+ the state machine that will iterate through all the tsv bits. */
+
+static void
+cmd_qtfv (char *packet)
+{
+ trace_debug ("Returning first trace state variable definition");
+
+ cur_tsv = trace_state_variables;
+
+ if (cur_tsv)
+ response_tsv (packet, cur_tsv);
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional trace state variable definitions. */
+
+static void
+cmd_qtsv (char *packet)
+{
+ trace_debug ("Returning additional trace state variable definition");
+
+ if (cur_tsv)
+ {
+ cur_tsv = cur_tsv->next;
+ if (cur_tsv)
+ response_tsv (packet, cur_tsv);
+ else
+ strcpy (packet, "l");
+ }
+ else
+ strcpy (packet, "l");
+}
+
+/* Return the first static tracepoint marker, and initialize the state
+ machine that will iterate through all the static tracepoints
+ markers. */
+
+static void
+cmd_qtfstm (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Return additional static tracepoints markers. */
+
+static void
+cmd_qtsstm (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Return the definition of the static tracepoint at a given address.
+ Result packet is the same as qTsST's. */
+
+static void
+cmd_qtstmat (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Sent the agent a command to close it. */
+
+void
+gdb_agent_about_to_close (int pid)
+{
+ char buf[IPA_CMD_BUF_SIZE];
+
+ if (!maybe_write_ipa_not_loaded (buf))
+ {
+ struct thread_info *saved_thread;
+
+ saved_thread = current_thread;
+
+ /* Find any thread which belongs to process PID. */
+ current_thread = find_any_thread_of_pid (pid);
+
+ strcpy (buf, "close");
+
+ run_inferior_command (buf, strlen (buf) + 1);
+
+ current_thread = saved_thread;
+ }
+}
+
+/* Return the minimum instruction size needed for fast tracepoints as a
+ hexadecimal number. */
+
+static void
+cmd_qtminftpilen (char *packet)
+{
+ if (current_thread == NULL)
+ {
+ /* Indicate that the minimum length is currently unknown. */
+ strcpy (packet, "0");
+ return;
+ }
+
+ sprintf (packet, "%x", target_get_min_fast_tracepoint_insn_len ());
+}
+
+/* Respond to qTBuffer packet with a block of raw data from the trace
+ buffer. GDB may ask for a lot, but we are allowed to reply with
+ only as much as will fit within packet limits or whatever. */
+
+static void
+cmd_qtbuffer (char *own_buf)
+{
+ ULONGEST offset, num, tot;
+ unsigned char *tbp;
+ const char *packet = own_buf;
+
+ packet += strlen ("qTBuffer:");
+
+ packet = unpack_varlen_hex (packet, &offset);
+ ++packet; /* skip a comma */
+ unpack_varlen_hex (packet, &num);
+
+ trace_debug ("Want to get trace buffer, %d bytes at offset 0x%s",
+ (int) num, phex_nz (offset, 0));
+
+ tot = (trace_buffer_hi - trace_buffer_lo) - free_space ();
+
+ /* If we're right at the end, reply specially that we're done. */
+ if (offset == tot)
+ {
+ strcpy (own_buf, "l");
+ return;
+ }
+
+ /* Object to any other out-of-bounds request. */
+ if (offset > tot)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ /* Compute the pointer corresponding to the given offset, accounting
+ for wraparound. */
+ tbp = trace_buffer_start + offset;
+ if (tbp >= trace_buffer_wrap)
+ tbp -= (trace_buffer_wrap - trace_buffer_lo);
+
+ /* Trim to the remaining bytes if we're close to the end. */
+ if (num > tot - offset)
+ num = tot - offset;
+
+ /* Trim to available packet size. */
+ if (num >= (PBUFSIZ - 16) / 2 )
+ num = (PBUFSIZ - 16) / 2;
+
+ bin2hex (tbp, own_buf, num);
+}
+
+static void
+cmd_bigqtbuffer_circular (char *own_buf)
+{
+ ULONGEST val;
+ char *packet = own_buf;
+
+ packet += strlen ("QTBuffer:circular:");
+
+ unpack_varlen_hex (packet, &val);
+ circular_trace_buffer = val;
+ trace_debug ("Trace buffer is now %s",
+ circular_trace_buffer ? "circular" : "linear");
+ write_ok (own_buf);
+}
+
+static void
+cmd_bigqtbuffer_size (char *own_buf)
+{
+ ULONGEST val;
+ LONGEST sval;
+ char *packet = own_buf;
+
+ /* Can't change the size during a tracing run. */
+ if (tracing)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ packet += strlen ("QTBuffer:size:");
+
+ /* -1 is sent as literal "-1". */
+ if (strcmp (packet, "-1") == 0)
+ sval = DEFAULT_TRACE_BUFFER_SIZE;
+ else
+ {
+ unpack_varlen_hex (packet, &val);
+ sval = (LONGEST) val;
+ }
+
+ init_trace_buffer (sval);
+ trace_debug ("Trace buffer is now %s bytes",
+ plongest (trace_buffer_size));
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtnotes (char *own_buf)
+{
+ size_t nbytes;
+ char *saved, *user, *notes, *stopnote;
+ char *packet = own_buf;
+
+ packet += strlen ("QTNotes:");
+
+ while (*packet)
+ {
+ if (startswith (packet, "user:"))
+ {
+ packet += strlen ("user:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ user = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) user, nbytes);
+ user[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("User is '%s'", user);
+ xfree (tracing_user_name);
+ tracing_user_name = user;
+ }
+ else if (startswith (packet, "notes:"))
+ {
+ packet += strlen ("notes:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ notes = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) notes, nbytes);
+ notes[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("Notes is '%s'", notes);
+ xfree (tracing_notes);
+ tracing_notes = notes;
+ }
+ else if (startswith (packet, "tstop:"))
+ {
+ packet += strlen ("tstop:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ stopnote = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) stopnote, nbytes);
+ stopnote[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("tstop note is '%s'", stopnote);
+ xfree (tracing_stop_note);
+ tracing_stop_note = stopnote;
+ }
+ else
+ break;
+ }
+
+ write_ok (own_buf);
+}
+
+int
+handle_tracepoint_general_set (char *packet)
+{
+ if (strcmp ("QTinit", packet) == 0)
+ {
+ cmd_qtinit (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTDP:"))
+ {
+ cmd_qtdp (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTDPsrc:"))
+ {
+ cmd_qtdpsrc (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTEnable:"))
+ {
+ cmd_qtenable_disable (packet, 1);
+ return 1;
+ }
+ else if (startswith (packet, "QTDisable:"))
+ {
+ cmd_qtenable_disable (packet, 0);
+ return 1;
+ }
+ else if (startswith (packet, "QTDV:"))
+ {
+ cmd_qtdv (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTro:"))
+ {
+ cmd_qtro (packet);
+ return 1;
+ }
+ else if (strcmp ("QTStart", packet) == 0)
+ {
+ cmd_qtstart (packet);
+ return 1;
+ }
+ else if (strcmp ("QTStop", packet) == 0)
+ {
+ cmd_qtstop (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTDisconnected:"))
+ {
+ cmd_qtdisconnected (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTFrame:"))
+ {
+ cmd_qtframe (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTBuffer:circular:"))
+ {
+ cmd_bigqtbuffer_circular (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTBuffer:size:"))
+ {
+ cmd_bigqtbuffer_size (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTNotes:"))
+ {
+ cmd_qtnotes (packet);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+handle_tracepoint_query (char *packet)
+{
+ if (strcmp ("qTStatus", packet) == 0)
+ {
+ cmd_qtstatus (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTP:"))
+ {
+ cmd_qtp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfP", packet) == 0)
+ {
+ cmd_qtfp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsP", packet) == 0)
+ {
+ cmd_qtsp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfV", packet) == 0)
+ {
+ cmd_qtfv (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsV", packet) == 0)
+ {
+ cmd_qtsv (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTV:"))
+ {
+ cmd_qtv (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTBuffer:"))
+ {
+ cmd_qtbuffer (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfSTM", packet) == 0)
+ {
+ cmd_qtfstm (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsSTM", packet) == 0)
+ {
+ cmd_qtsstm (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTSTMat:"))
+ {
+ cmd_qtstmat (packet);
+ return 1;
+ }
+ else if (strcmp ("qTMinFTPILen", packet) == 0)
+ {
+ cmd_qtminftpilen (packet);
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+#ifndef IN_PROCESS_AGENT
+
+/* Call this when thread TINFO has hit the tracepoint defined by
+ TP_NUMBER and TP_ADDRESS, and that tracepoint has a while-stepping
+ action. This adds a while-stepping collecting state item to the
+ threads' collecting state list, so that we can keep track of
+ multiple simultaneous while-stepping actions being collected by the
+ same thread. This can happen in cases like:
+
+ ff0001 INSN1 <-- TP1, while-stepping 10 collect $regs
+ ff0002 INSN2
+ ff0003 INSN3 <-- TP2, collect $regs
+ ff0004 INSN4 <-- TP3, while-stepping 10 collect $regs
+ ff0005 INSN5
+
+ Notice that when instruction INSN5 is reached, the while-stepping
+ actions of both TP1 and TP3 are still being collected, and that TP2
+ had been collected meanwhile. The whole range of ff0001-ff0005
+ should be single-stepped, due to at least TP1's while-stepping
+ action covering the whole range. */
+
+static void
+add_while_stepping_state (struct thread_info *tinfo,
+ int tp_number, CORE_ADDR tp_address)
+{
+ struct wstep_state *wstep = XNEW (struct wstep_state);
+
+ wstep->next = tinfo->while_stepping;
+
+ wstep->tp_number = tp_number;
+ wstep->tp_address = tp_address;
+ wstep->current_step = 0;
+
+ tinfo->while_stepping = wstep;
+}
+
+/* Release the while-stepping collecting state WSTEP. */
+
+static void
+release_while_stepping_state (struct wstep_state *wstep)
+{
+ free (wstep);
+}
+
+/* Release all while-stepping collecting states currently associated
+ with thread TINFO. */
+
+void
+release_while_stepping_state_list (struct thread_info *tinfo)
+{
+ struct wstep_state *head;
+
+ while (tinfo->while_stepping)
+ {
+ head = tinfo->while_stepping;
+ tinfo->while_stepping = head->next;
+ release_while_stepping_state (head);
+ }
+}
+
+/* If TINFO was handling a 'while-stepping' action, the step has
+ finished, so collect any step data needed, and check if any more
+ steps are required. Return true if the thread was indeed
+ collecting tracepoint data, false otherwise. */
+
+int
+tracepoint_finished_step (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ struct tracepoint *tpoint;
+ struct wstep_state *wstep;
+ struct wstep_state **wstep_link;
+ struct trap_tracepoint_ctx ctx;
+
+ /* Pull in fast tracepoint trace frames from the inferior lib buffer into
+ our buffer. */
+ if (agent_loaded_p ())
+ upload_fast_traceframes ();
+
+ /* Check if we were indeed collecting data for one of more
+ tracepoints with a 'while-stepping' count. */
+ if (tinfo->while_stepping == NULL)
+ return 0;
+
+ if (!tracing)
+ {
+ /* We're not even tracing anymore. Stop this thread from
+ collecting. */
+ release_while_stepping_state_list (tinfo);
+
+ /* The thread had stopped due to a single-step request indeed
+ explained by a tracepoint. */
+ return 1;
+ }
+
+ wstep = tinfo->while_stepping;
+ wstep_link = &tinfo->while_stepping;
+
+ trace_debug ("Thread %s finished a single-step for tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->id),
+ wstep->tp_number, paddress (wstep->tp_address));
+
+ ctx.base.type = trap_tracepoint;
+ ctx.regcache = get_thread_regcache (tinfo, 1);
+
+ while (wstep != NULL)
+ {
+ tpoint = find_tracepoint (wstep->tp_number, wstep->tp_address);
+ if (tpoint == NULL)
+ {
+ trace_debug ("NO TRACEPOINT %d at 0x%s FOR THREAD %s!",
+ wstep->tp_number, paddress (wstep->tp_address),
+ target_pid_to_str (tinfo->id));
+
+ /* Unlink. */
+ *wstep_link = wstep->next;
+ release_while_stepping_state (wstep);
+ wstep = *wstep_link;
+ continue;
+ }
+
+ /* We've just finished one step. */
+ ++wstep->current_step;
+
+ /* Collect data. */
+ collect_data_at_step ((struct tracepoint_hit_ctx *) &ctx,
+ stop_pc, tpoint, wstep->current_step);
+
+ if (wstep->current_step >= tpoint->step_count)
+ {
+ /* The requested numbers of steps have occurred. */
+ trace_debug ("Thread %s done stepping for tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->id),
+ wstep->tp_number, paddress (wstep->tp_address));
+
+ /* Unlink the wstep. */
+ *wstep_link = wstep->next;
+ release_while_stepping_state (wstep);
+ wstep = *wstep_link;
+
+ /* Only check the hit count now, which ensure that we do all
+ our stepping before stopping the run. */
+ if (tpoint->pass_count > 0
+ && tpoint->hit_count >= tpoint->pass_count
+ && stopping_tracepoint == NULL)
+ stopping_tracepoint = tpoint;
+ }
+ else
+ {
+ /* Keep single-stepping until the requested numbers of steps
+ have occurred. */
+ wstep_link = &wstep->next;
+ wstep = *wstep_link;
+ }
+
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/* Handle any internal tracing control breakpoint hits. That means,
+ pull traceframes from the IPA to our buffer, and syncing both
+ tracing agents when the IPA's tracing stops for some reason. */
+
+int
+handle_tracepoint_bkpts (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ /* Pull in fast tracepoint trace frames from the inferior in-process
+ agent's buffer into our buffer. */
+
+ if (!agent_loaded_p ())
+ return 0;
+
+ upload_fast_traceframes ();
+
+ /* Check if the in-process agent had decided we should stop
+ tracing. */
+ if (stop_pc == ipa_sym_addrs.addr_stop_tracing)
+ {
+ int ipa_trace_buffer_is_full;
+ CORE_ADDR ipa_stopping_tracepoint;
+ int ipa_expr_eval_result;
+ CORE_ADDR ipa_error_tracepoint;
+
+ trace_debug ("lib stopped at stop_tracing");
+
+ read_inferior_integer (ipa_sym_addrs.addr_trace_buffer_is_full,
+ &ipa_trace_buffer_is_full);
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint,
+ &ipa_stopping_tracepoint);
+ write_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint, 0);
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint,
+ &ipa_error_tracepoint);
+ write_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint, 0);
+
+ read_inferior_integer (ipa_sym_addrs.addr_expr_eval_result,
+ &ipa_expr_eval_result);
+ write_inferior_integer (ipa_sym_addrs.addr_expr_eval_result, 0);
+
+ trace_debug ("lib: trace_buffer_is_full: %d, "
+ "stopping_tracepoint: %s, "
+ "ipa_expr_eval_result: %d, "
+ "error_tracepoint: %s, ",
+ ipa_trace_buffer_is_full,
+ paddress (ipa_stopping_tracepoint),
+ ipa_expr_eval_result,
+ paddress (ipa_error_tracepoint));
+
+ if (debug_threads)
+ {
+ if (ipa_trace_buffer_is_full)
+ trace_debug ("lib stopped due to full buffer.");
+ if (ipa_stopping_tracepoint)
+ trace_debug ("lib stopped due to tpoint");
+ if (ipa_error_tracepoint)
+ trace_debug ("lib stopped due to error");
+ }
+
+ if (ipa_stopping_tracepoint != 0)
+ {
+ stopping_tracepoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_stopping_tracepoint);
+ }
+ else if (ipa_expr_eval_result != expr_eval_no_error)
+ {
+ expr_eval_result = ipa_expr_eval_result;
+ error_tracepoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_error_tracepoint);
+ }
+ stop_tracing ();
+ return 1;
+ }
+ else if (stop_pc == ipa_sym_addrs.addr_flush_trace_buffer)
+ {
+ trace_debug ("lib stopped at flush_trace_buffer");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return true if TINFO just hit a tracepoint. Collect data if
+ so. */
+
+int
+tracepoint_was_hit (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ struct tracepoint *tpoint;
+ int ret = 0;
+ struct trap_tracepoint_ctx ctx;
+
+ /* Not tracing, don't handle. */
+ if (!tracing)
+ return 0;
+
+ ctx.base.type = trap_tracepoint;
+ ctx.regcache = get_thread_regcache (tinfo, 1);
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ /* Note that we collect fast tracepoints here as well. We'll
+ step over the fast tracepoint jump later, which avoids the
+ double collect. However, we don't collect for static
+ tracepoints here, because UST markers are compiled in program,
+ and probes will be executed in program. So static tracepoints
+ are collected there. */
+ if (tpoint->enabled && stop_pc == tpoint->address
+ && tpoint->type != static_tracepoint)
+ {
+ trace_debug ("Thread %s at address of tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->id),
+ tpoint->number, paddress (tpoint->address));
+
+ /* Test the condition if present, and collect if true. */
+ if (!tpoint->cond
+ || (condition_true_at_tracepoint
+ ((struct tracepoint_hit_ctx *) &ctx, tpoint)))
+ collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ stop_pc, tpoint);
+
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ }
+ /* If the tracepoint had a 'while-stepping' action, then set
+ the thread to collect this tracepoint on the following
+ single-steps. */
+ else if (tpoint->step_count > 0)
+ {
+ add_while_stepping_state (tinfo,
+ tpoint->number, tpoint->address);
+ }
+
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+#endif
+
+#if defined IN_PROCESS_AGENT && defined HAVE_UST
+struct ust_marker_data;
+static void collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct traceframe *tframe);
+#endif
+
+/* Create a trace frame for the hit of the given tracepoint in the
+ given thread. */
+
+static void
+collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx, CORE_ADDR stop_pc,
+ struct tracepoint *tpoint)
+{
+ struct traceframe *tframe;
+ int acti;
+
+ /* Only count it as a hit when we actually collect data. */
+ tpoint->hit_count++;
+
+ /* If we've exceeded a defined pass count, record the event for
+ later, and finish the collection for this hit. This test is only
+ for nonstepping tracepoints, stepping tracepoints test at the end
+ of their while-stepping loop. */
+ if (tpoint->pass_count > 0
+ && tpoint->hit_count >= tpoint->pass_count
+ && tpoint->step_count == 0
+ && stopping_tracepoint == NULL)
+ stopping_tracepoint = tpoint;
+
+ trace_debug ("Making new traceframe for tracepoint %d at 0x%s, hit %" PRIu64,
+ tpoint->number, paddress (tpoint->address), tpoint->hit_count);
+
+ tframe = add_traceframe (tpoint);
+
+ if (tframe)
+ {
+ for (acti = 0; acti < tpoint->numactions; ++acti)
+ {
+#ifndef IN_PROCESS_AGENT
+ trace_debug ("Tracepoint %d at 0x%s about to do action '%s'",
+ tpoint->number, paddress (tpoint->address),
+ tpoint->actions_str[acti]);
+#endif
+
+ do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
+ tpoint->actions[acti]);
+ }
+
+ finish_traceframe (tframe);
+ }
+
+ if (tframe == NULL && tracing)
+ trace_buffer_is_full = 1;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+static void
+collect_data_at_step (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint, int current_step)
+{
+ struct traceframe *tframe;
+ int acti;
+
+ trace_debug ("Making new step traceframe for "
+ "tracepoint %d at 0x%s, step %d of %" PRIu64 ", hit %" PRIu64,
+ tpoint->number, paddress (tpoint->address),
+ current_step, tpoint->step_count,
+ tpoint->hit_count);
+
+ tframe = add_traceframe (tpoint);
+
+ if (tframe)
+ {
+ for (acti = 0; acti < tpoint->num_step_actions; ++acti)
+ {
+ trace_debug ("Tracepoint %d at 0x%s about to do step action '%s'",
+ tpoint->number, paddress (tpoint->address),
+ tpoint->step_actions_str[acti]);
+
+ do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
+ tpoint->step_actions[acti]);
+ }
+
+ finish_traceframe (tframe);
+ }
+
+ if (tframe == NULL && tracing)
+ trace_buffer_is_full = 1;
+}
+
+#endif
+
+#ifdef IN_PROCESS_AGENT
+/* The target description index for IPA. Passed from gdbserver, used
+ to select ipa_tdesc. */
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR int ipa_tdesc_idx;
+EXTERN_C_POP
+#endif
+
+static struct regcache *
+get_context_regcache (struct tracepoint_hit_ctx *ctx)
+{
+ struct regcache *regcache = NULL;
+#ifdef IN_PROCESS_AGENT
+ const struct target_desc *ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
+
+ if (ctx->type == fast_tracepoint)
+ {
+ struct fast_tracepoint_ctx *fctx = (struct fast_tracepoint_ctx *) ctx;
+ if (!fctx->regcache_initted)
+ {
+ fctx->regcache_initted = 1;
+ init_register_cache (&fctx->regcache, ipa_tdesc, fctx->regspace);
+ supply_regblock (&fctx->regcache, NULL);
+ supply_fast_tracepoint_registers (&fctx->regcache, fctx->regs);
+ }
+ regcache = &fctx->regcache;
+ }
+#ifdef HAVE_UST
+ if (ctx->type == static_tracepoint)
+ {
+ struct static_tracepoint_ctx *sctx
+ = (struct static_tracepoint_ctx *) ctx;
+
+ if (!sctx->regcache_initted)
+ {
+ sctx->regcache_initted = 1;
+ init_register_cache (&sctx->regcache, ipa_tdesc, sctx->regspace);
+ supply_regblock (&sctx->regcache, NULL);
+ /* Pass down the tracepoint address, because REGS doesn't
+ include the PC, but we know what it must have been. */
+ supply_static_tracepoint_registers (&sctx->regcache,
+ (const unsigned char *)
+ sctx->regs,
+ sctx->tpoint->address);
+ }
+ regcache = &sctx->regcache;
+ }
+#endif
+#else
+ if (ctx->type == trap_tracepoint)
+ {
+ struct trap_tracepoint_ctx *tctx = (struct trap_tracepoint_ctx *) ctx;
+ regcache = tctx->regcache;
+ }
+#endif
+
+ gdb_assert (regcache != NULL);
+
+ return regcache;
+}
+
+static void
+do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint,
+ struct traceframe *tframe,
+ struct tracepoint_action *taction)
+{
+ enum eval_result_type err;
+
+ switch (taction->type)
+ {
+ case 'M':
+ {
+ struct collect_memory_action *maction;
+ struct eval_agent_expr_context ax_ctx;
+
+ maction = (struct collect_memory_action *) taction;
+ ax_ctx.regcache = NULL;
+ ax_ctx.tframe = tframe;
+ ax_ctx.tpoint = tpoint;
+
+ trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)",
+ pulongest (maction->len),
+ paddress (maction->addr), maction->basereg);
+ /* (should use basereg) */
+ agent_mem_read (&ax_ctx, NULL, (CORE_ADDR) maction->addr,
+ maction->len);
+ break;
+ }
+ case 'R':
+ {
+ unsigned char *regspace;
+ struct regcache tregcache;
+ struct regcache *context_regcache;
+ int regcache_size;
+
+ trace_debug ("Want to collect registers");
+
+ context_regcache = get_context_regcache (ctx);
+ regcache_size = register_cache_size (context_regcache->tdesc);
+
+ /* Collect all registers for now. */
+ regspace = add_traceframe_block (tframe, tpoint, 1 + regcache_size);
+ if (regspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ break;
+ }
+ /* Identify a register block. */
+ *regspace = 'R';
+
+ /* Wrap the regblock in a register cache (in the stack, we
+ don't want to malloc here). */
+ init_register_cache (&tregcache, context_regcache->tdesc,
+ regspace + 1);
+
+ /* Copy the register data to the regblock. */
+ regcache_cpy (&tregcache, context_regcache);
+
+#ifndef IN_PROCESS_AGENT
+ /* On some platforms, trap-based tracepoints will have the PC
+ pointing to the next instruction after the trap, but we
+ don't want the user or GDB trying to guess whether the
+ saved PC needs adjusting; so always record the adjusted
+ stop_pc. Note that we can't use tpoint->address instead,
+ since it will be wrong for while-stepping actions. This
+ adjustment is a nop for fast tracepoints collected from the
+ in-process lib (but not if GDBserver is collecting one
+ preemptively), since the PC had already been adjusted to
+ contain the tracepoint's address by the jump pad. */
+ trace_debug ("Storing stop pc (0x%s) in regblock",
+ paddress (stop_pc));
+
+ /* This changes the regblock, not the thread's
+ regcache. */
+ regcache_write_pc (&tregcache, stop_pc);
+#endif
+ }
+ break;
+ case 'X':
+ {
+ struct eval_expr_action *eaction;
+ struct eval_agent_expr_context ax_ctx;
+
+ eaction = (struct eval_expr_action *) taction;
+ ax_ctx.regcache = get_context_regcache (ctx);
+ ax_ctx.tframe = tframe;
+ ax_ctx.tpoint = tpoint;
+
+ trace_debug ("Want to evaluate expression");
+
+ err = gdb_eval_agent_expr (&ax_ctx, eaction->expr, NULL);
+
+ if (err != expr_eval_no_error)
+ {
+ record_tracepoint_error (tpoint, "action expression", err);
+ return;
+ }
+ }
+ break;
+ case 'L':
+ {
+#if defined IN_PROCESS_AGENT && defined HAVE_UST
+ trace_debug ("Want to collect static trace data");
+ collect_ust_data_at_tracepoint (ctx, tframe);
+#else
+ trace_debug ("warning: collecting static trace data, "
+ "but static tracepoints are not supported");
+#endif
+ }
+ break;
+ default:
+ trace_debug ("unknown trace action '%c', ignoring", taction->type);
+ break;
+ }
+}
+
+static int
+condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct tracepoint *tpoint)
+{
+ ULONGEST value = 0;
+ enum eval_result_type err;
+
+ /* Presently, gdbserver doesn't run compiled conditions, only the
+ IPA does. If the program stops at a fast tracepoint's address
+ (e.g., due to a breakpoint, trap tracepoint, or stepping),
+ gdbserver preemptively collect the fast tracepoint. Later, on
+ resume, gdbserver steps over the fast tracepoint like it steps
+ over breakpoints, so that the IPA doesn't see that fast
+ tracepoint. This avoids double collects of fast tracepoints in
+ that stopping scenario. Having gdbserver itself handle the fast
+ tracepoint gives the user a consistent view of when fast or trap
+ tracepoints are collected, compared to an alternative where only
+ trap tracepoints are collected on stop, and fast tracepoints on
+ resume. When a fast tracepoint is being processed by gdbserver,
+ it is always the non-compiled condition expression that is
+ used. */
+#ifdef IN_PROCESS_AGENT
+ if (tpoint->compiled_cond)
+ {
+ struct fast_tracepoint_ctx *fctx = (struct fast_tracepoint_ctx *) ctx;
+ err = ((condfn) (uintptr_t) (tpoint->compiled_cond)) (fctx->regs, &value);
+ }
+ else
+#endif
+ {
+ struct eval_agent_expr_context ax_ctx;
+
+ ax_ctx.regcache = get_context_regcache (ctx);
+ ax_ctx.tframe = NULL;
+ ax_ctx.tpoint = tpoint;
+
+ err = gdb_eval_agent_expr (&ax_ctx, tpoint->cond, &value);
+ }
+ if (err != expr_eval_no_error)
+ {
+ record_tracepoint_error (tpoint, "condition", err);
+ /* The error case must return false. */
+ return 0;
+ }
+
+ trace_debug ("Tracepoint %d at 0x%s condition evals to %s",
+ tpoint->number, paddress (tpoint->address),
+ pulongest (value));
+ return (value ? 1 : 0);
+}
+
+/* Do memory copies for bytecodes. */
+/* Do the recording of memory blocks for actions and bytecodes. */
+
+int
+agent_mem_read (struct eval_agent_expr_context *ctx,
+ unsigned char *to, CORE_ADDR from, ULONGEST len)
+{
+ unsigned char *mspace;
+ ULONGEST remaining = len;
+ unsigned short blocklen;
+
+ /* If a 'to' buffer is specified, use it. */
+ if (to != NULL)
+ {
+ read_inferior_memory (from, to, len);
+ return 0;
+ }
+
+ /* Otherwise, create a new memory block in the trace buffer. */
+ while (remaining > 0)
+ {
+ size_t sp;
+
+ blocklen = (remaining > 65535 ? 65535 : remaining);
+ sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
+ mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
+ if (mspace == NULL)
+ return 1;
+ /* Identify block as a memory block. */
+ *mspace = 'M';
+ ++mspace;
+ /* Record address and size. */
+ memcpy (mspace, &from, sizeof (from));
+ mspace += sizeof (from);
+ memcpy (mspace, &blocklen, sizeof (blocklen));
+ mspace += sizeof (blocklen);
+ /* Record the memory block proper. */
+ read_inferior_memory (from, mspace, blocklen);
+ trace_debug ("%d bytes recorded", blocklen);
+ remaining -= blocklen;
+ from += blocklen;
+ }
+ return 0;
+}
+
+int
+agent_mem_read_string (struct eval_agent_expr_context *ctx,
+ unsigned char *to, CORE_ADDR from, ULONGEST len)
+{
+ unsigned char *buf, *mspace;
+ ULONGEST remaining = len;
+ unsigned short blocklen, i;
+
+ /* To save a bit of space, block lengths are 16-bit, so break large
+ requests into multiple blocks. Bordering on overkill for strings,
+ but it could happen that someone specifies a large max length. */
+ while (remaining > 0)
+ {
+ size_t sp;
+
+ blocklen = (remaining > 65535 ? 65535 : remaining);
+ /* We want working space to accumulate nonzero bytes, since
+ traceframes must have a predecided size (otherwise it gets
+ harder to wrap correctly for the circular case, etc). */
+ buf = (unsigned char *) xmalloc (blocklen + 1);
+ for (i = 0; i < blocklen; ++i)
+ {
+ /* Read the string one byte at a time, in case the string is
+ at the end of a valid memory area - we don't want a
+ correctly-terminated string to engender segvio
+ complaints. */
+ read_inferior_memory (from + i, buf + i, 1);
+
+ if (buf[i] == '\0')
+ {
+ blocklen = i + 1;
+ /* Make sure outer loop stops now too. */
+ remaining = blocklen;
+ break;
+ }
+ }
+ sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
+ mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
+ if (mspace == NULL)
+ {
+ xfree (buf);
+ return 1;
+ }
+ /* Identify block as a memory block. */
+ *mspace = 'M';
+ ++mspace;
+ /* Record address and size. */
+ memcpy ((void *) mspace, (void *) &from, sizeof (from));
+ mspace += sizeof (from);
+ memcpy ((void *) mspace, (void *) &blocklen, sizeof (blocklen));
+ mspace += sizeof (blocklen);
+ /* Copy the string contents. */
+ memcpy ((void *) mspace, (void *) buf, blocklen);
+ remaining -= blocklen;
+ from += blocklen;
+ xfree (buf);
+ }
+ return 0;
+}
+
+/* Record the value of a trace state variable. */
+
+int
+agent_tsv_read (struct eval_agent_expr_context *ctx, int n)
+{
+ unsigned char *vspace;
+ LONGEST val;
+
+ vspace = add_traceframe_block (ctx->tframe, ctx->tpoint,
+ 1 + sizeof (n) + sizeof (LONGEST));
+ if (vspace == NULL)
+ return 1;
+ /* Identify block as a variable. */
+ *vspace = 'V';
+ /* Record variable's number and value. */
+ memcpy (vspace + 1, &n, sizeof (n));
+ val = get_trace_state_variable_value (n);
+ memcpy (vspace + 1 + sizeof (n), &val, sizeof (val));
+ trace_debug ("Variable %d recorded", n);
+ return 0;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Callback for traceframe_walk_blocks, used to find a given block
+ type in a traceframe. */
+
+static int
+match_blocktype (char blocktype, unsigned char *dataptr, void *data)
+{
+ char *wantedp = (char *) data;
+
+ if (*wantedp == blocktype)
+ return 1;
+
+ return 0;
+}
+
+/* Walk over all traceframe blocks of the traceframe buffer starting
+ at DATABASE, of DATASIZE bytes long, and call CALLBACK for each
+ block found, passing in DATA unmodified. If CALLBACK returns true,
+ this returns a pointer to where the block is found. Returns NULL
+ if no callback call returned true, indicating that all blocks have
+ been walked. */
+
+static unsigned char *
+traceframe_walk_blocks (unsigned char *database, unsigned int datasize,
+ int tfnum,
+ int (*callback) (char blocktype,
+ unsigned char *dataptr,
+ void *data),
+ void *data)
+{
+ unsigned char *dataptr;
+
+ if (datasize == 0)
+ {
+ trace_debug ("traceframe %d has no data", tfnum);
+ return NULL;
+ }
+
+ /* Iterate through a traceframe's blocks, looking for a block of the
+ requested type. */
+ for (dataptr = database;
+ dataptr < database + datasize;
+ /* nothing */)
+ {
+ char blocktype;
+ unsigned short mlen;
+
+ if (dataptr == trace_buffer_wrap)
+ {
+ /* Adjust to reflect wrapping part of the frame around to
+ the beginning. */
+ datasize = dataptr - database;
+ dataptr = database = trace_buffer_lo;
+ }
+
+ blocktype = *dataptr++;
+
+ if ((*callback) (blocktype, dataptr, data))
+ return dataptr;
+
+ switch (blocktype)
+ {
+ case 'R':
+ /* Skip over the registers block. */
+ dataptr += current_target_desc ()->registers_size;
+ break;
+ case 'M':
+ /* Skip over the memory block. */
+ dataptr += sizeof (CORE_ADDR);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += (sizeof (mlen) + mlen);
+ break;
+ case 'V':
+ /* Skip over the TSV block. */
+ dataptr += (sizeof (int) + sizeof (LONGEST));
+ break;
+ case 'S':
+ /* Skip over the static trace data block. */
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += (sizeof (mlen) + mlen);
+ break;
+ default:
+ trace_debug ("traceframe %d has unknown block type 0x%x",
+ tfnum, blocktype);
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+/* Look for the block of type TYPE_WANTED in the traceframe starting
+ at DATABASE of DATASIZE bytes long. TFNUM is the traceframe
+ number. */
+
+static unsigned char *
+traceframe_find_block_type (unsigned char *database, unsigned int datasize,
+ int tfnum, char type_wanted)
+{
+ return traceframe_walk_blocks (database, datasize, tfnum,
+ match_blocktype, &type_wanted);
+}
+
+static unsigned char *
+traceframe_find_regblock (struct traceframe *tframe, int tfnum)
+{
+ unsigned char *regblock;
+
+ regblock = traceframe_find_block_type (tframe->data,
+ tframe->data_size,
+ tfnum, 'R');
+
+ if (regblock == NULL)
+ trace_debug ("traceframe %d has no register data", tfnum);
+
+ return regblock;
+}
+
+/* Get registers from a traceframe. */
+
+int
+fetch_traceframe_registers (int tfnum, struct regcache *regcache, int regnum)
+{
+ unsigned char *dataptr;
+ struct tracepoint *tpoint;
+ struct traceframe *tframe;
+
+ tframe = find_traceframe (tfnum);
+
+ if (tframe == NULL)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ dataptr = traceframe_find_regblock (tframe, tfnum);
+ if (dataptr == NULL)
+ {
+ /* Mark registers unavailable. */
+ supply_regblock (regcache, NULL);
+
+ /* We can generally guess at a PC, although this will be
+ misleading for while-stepping frames and multi-location
+ tracepoints. */
+ tpoint = find_next_tracepoint_by_number (NULL, tframe->tpnum);
+ if (tpoint != NULL)
+ regcache_write_pc (regcache, tpoint->address);
+ }
+ else
+ supply_regblock (regcache, dataptr);
+
+ return 0;
+}
+
+static CORE_ADDR
+traceframe_get_pc (struct traceframe *tframe)
+{
+ struct regcache regcache;
+ unsigned char *dataptr;
+ const struct target_desc *tdesc = current_target_desc ();
+
+ dataptr = traceframe_find_regblock (tframe, -1);
+ if (dataptr == NULL)
+ return 0;
+
+ init_register_cache (®cache, tdesc, dataptr);
+ return regcache_read_pc (®cache);
+}
+
+/* Read a requested block of memory from a trace frame. */
+
+int
+traceframe_read_mem (int tfnum, CORE_ADDR addr,
+ unsigned char *buf, ULONGEST length,
+ ULONGEST *nbytes)
+{
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ CORE_ADDR maddr;
+ unsigned short mlen;
+
+ trace_debug ("traceframe_read_mem");
+
+ tframe = find_traceframe (tfnum);
+
+ if (!tframe)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = dataptr = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for memory. */
+ while ((dataptr = traceframe_find_block_type (dataptr,
+ datasize
+ - (dataptr - database),
+ tfnum, 'M')) != NULL)
+ {
+ memcpy (&maddr, dataptr, sizeof (maddr));
+ dataptr += sizeof (maddr);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += sizeof (mlen);
+ trace_debug ("traceframe %d has %d bytes at %s",
+ tfnum, mlen, paddress (maddr));
+
+ /* If the block includes the first part of the desired range,
+ return as much it has; GDB will re-request the remainder,
+ which might be in a different block of this trace frame. */
+ if (maddr <= addr && addr < (maddr + mlen))
+ {
+ ULONGEST amt = (maddr + mlen) - addr;
+ if (amt > length)
+ amt = length;
+
+ memcpy (buf, dataptr + (addr - maddr), amt);
+ *nbytes = amt;
+ return 0;
+ }
+
+ /* Skip over this block. */
+ dataptr += mlen;
+ }
+
+ trace_debug ("traceframe %d has no memory data for the desired region",
+ tfnum);
+
+ *nbytes = 0;
+ return 0;
+}
+
+static int
+traceframe_read_tsv (int tsvnum, LONGEST *val)
+{
+ client_state &cs = get_client_state ();
+ int tfnum;
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ int vnum;
+ int found = 0;
+
+ trace_debug ("traceframe_read_tsv");
+
+ tfnum = cs.current_traceframe;
+
+ if (tfnum < 0)
+ {
+ trace_debug ("no current traceframe");
+ return 1;
+ }
+
+ tframe = find_traceframe (tfnum);
+
+ if (tframe == NULL)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = dataptr = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for the last
+ matched tsv. */
+ while ((dataptr = traceframe_find_block_type (dataptr,
+ datasize
+ - (dataptr - database),
+ tfnum, 'V')) != NULL)
+ {
+ memcpy (&vnum, dataptr, sizeof (vnum));
+ dataptr += sizeof (vnum);
+
+ trace_debug ("traceframe %d has variable %d", tfnum, vnum);
+
+ /* Check that this is the variable we want. */
+ if (tsvnum == vnum)
+ {
+ memcpy (val, dataptr, sizeof (*val));
+ found = 1;
+ }
+
+ /* Skip over this block. */
+ dataptr += sizeof (LONGEST);
+ }
+
+ if (!found)
+ trace_debug ("traceframe %d has no data for variable %d",
+ tfnum, tsvnum);
+ return !found;
+}
+
+/* Read a requested block of static tracepoint data from a trace
+ frame. */
+
+int
+traceframe_read_sdata (int tfnum, ULONGEST offset,
+ unsigned char *buf, ULONGEST length,
+ ULONGEST *nbytes)
+{
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ unsigned short mlen;
+
+ trace_debug ("traceframe_read_sdata");
+
+ tframe = find_traceframe (tfnum);
+
+ if (!tframe)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for static
+ tracepoint data. */
+ dataptr = traceframe_find_block_type (database, datasize,
+ tfnum, 'S');
+ if (dataptr != NULL)
+ {
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += sizeof (mlen);
+ if (offset < mlen)
+ {
+ if (offset + length > mlen)
+ length = mlen - offset;
+
+ memcpy (buf, dataptr, length);
+ *nbytes = length;
+ }
+ else
+ *nbytes = 0;
+ return 0;
+ }
+
+ trace_debug ("traceframe %d has no static trace data", tfnum);
+
+ *nbytes = 0;
+ return 0;
+}
+
+/* Callback for traceframe_walk_blocks. Builds a traceframe-info
+ object. DATA is pointer to a struct buffer holding the
+ traceframe-info object being built. */
+
+static int
+build_traceframe_info_xml (char blocktype, unsigned char *dataptr, void *data)
+{
+ struct buffer *buffer = (struct buffer *) data;
+
+ switch (blocktype)
+ {
+ case 'M':
+ {
+ unsigned short mlen;
+ CORE_ADDR maddr;
+
+ memcpy (&maddr, dataptr, sizeof (maddr));
+ dataptr += sizeof (maddr);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += sizeof (mlen);
+ buffer_xml_printf (buffer,
+ "<memory start=\"0x%s\" length=\"0x%s\"/>\n",
+ paddress (maddr), phex_nz (mlen, sizeof (mlen)));
+ break;
+ }
+ case 'V':
+ {
+ int vnum;
+
+ memcpy (&vnum, dataptr, sizeof (vnum));
+ buffer_xml_printf (buffer, "<tvar id=\"%d\"/>\n", vnum);
+ break;
+ }
+ case 'R':
+ case 'S':
+ {
+ break;
+ }
+ default:
+ warning ("Unhandled trace block type (%d) '%c ' "
+ "while building trace frame info.",
+ blocktype, blocktype);
+ break;
+ }
+
+ return 0;
+}
+
+/* Build a traceframe-info object for traceframe number TFNUM into
+ BUFFER. */
+
+int
+traceframe_read_info (int tfnum, struct buffer *buffer)
+{
+ struct traceframe *tframe;
+
+ trace_debug ("traceframe_read_info");
+
+ tframe = find_traceframe (tfnum);
+
+ if (!tframe)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ buffer_grow_str (buffer, "<traceframe-info>\n");
+ traceframe_walk_blocks (tframe->data, tframe->data_size,
+ tfnum, build_traceframe_info_xml, buffer);
+ buffer_grow_str0 (buffer, "</traceframe-info>\n");
+ return 0;
+}
+
+/* Return the first fast tracepoint whose jump pad contains PC. */
+
+static struct tracepoint *
+fast_tracepoint_from_jump_pad_address (CORE_ADDR pc)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ if (tpoint->type == fast_tracepoint)
+ if (tpoint->jump_pad <= pc && pc < tpoint->jump_pad_end)
+ return tpoint;
+
+ return NULL;
+}
+
+/* Return the first fast tracepoint whose trampoline contains PC. */
+
+static struct tracepoint *
+fast_tracepoint_from_trampoline_address (CORE_ADDR pc)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ if (tpoint->type == fast_tracepoint
+ && tpoint->trampoline <= pc && pc < tpoint->trampoline_end)
+ return tpoint;
+ }
+
+ return NULL;
+}
+
+/* Return GDBserver's tracepoint that matches the IP Agent's
+ tracepoint object that lives at IPA_TPOINT_OBJ in the IP Agent's
+ address space. */
+
+static struct tracepoint *
+fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR ipa_tpoint_obj)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ if (tpoint->type == fast_tracepoint)
+ if (tpoint->obj_addr_on_target == ipa_tpoint_obj)
+ return tpoint;
+
+ return NULL;
+}
+
+#endif
+
+/* The type of the object that is used to synchronize fast tracepoint
+ collection. */
+
+typedef struct collecting_t
+{
+ /* The fast tracepoint number currently collecting. */
+ uintptr_t tpoint;
+
+ /* A number that GDBserver can use to identify the thread that is
+ presently holding the collect lock. This need not (and usually
+ is not) the thread id, as getting the current thread ID usually
+ requires a system call, which we want to avoid like the plague.
+ Usually this is thread's TCB, found in the TLS (pseudo-)
+ register, which is readable with a single insn on several
+ architectures. */
+ uintptr_t thread_area;
+} collecting_t;
+
+#ifndef IN_PROCESS_AGENT
+
+void
+force_unlock_trace_buffer (void)
+{
+ write_inferior_data_pointer (ipa_sym_addrs.addr_collecting, 0);
+}
+
+/* Check if the thread identified by THREAD_AREA which is stopped at
+ STOP_PC, is presently locking the fast tracepoint collection, and
+ if so, gather some status of said collection. Returns 0 if the
+ thread isn't collecting or in the jump pad at all. 1, if in the
+ jump pad (or within gdb_collect) and hasn't executed the adjusted
+ original insn yet (can set a breakpoint there and run to it). 2,
+ if presently executing the adjusted original insn --- in which
+ case, if we want to move the thread out of the jump pad, we need to
+ single-step it until this function returns 0. */
+
+fast_tpoint_collect_result
+fast_tracepoint_collecting (CORE_ADDR thread_area,
+ CORE_ADDR stop_pc,
+ struct fast_tpoint_collect_status *status)
+{
+ CORE_ADDR ipa_collecting;
+ CORE_ADDR ipa_gdb_jump_pad_buffer, ipa_gdb_jump_pad_buffer_end;
+ CORE_ADDR ipa_gdb_trampoline_buffer;
+ CORE_ADDR ipa_gdb_trampoline_buffer_end;
+ struct tracepoint *tpoint;
+ int needs_breakpoint;
+
+ /* The thread THREAD_AREA is either:
+
+ 0. not collecting at all, not within the jump pad, or within
+ gdb_collect or one of its callees.
+
+ 1. in the jump pad and haven't reached gdb_collect
+
+ 2. within gdb_collect (out of the jump pad) (collect is set)
+
+ 3. we're in the jump pad, after gdb_collect having returned,
+ possibly executing the adjusted insns.
+
+ For cases 1 and 3, `collecting' may or not be set. The jump pad
+ doesn't have any complicated jump logic, so we can tell if the
+ thread is executing the adjust original insn or not by just
+ matching STOP_PC with known jump pad addresses. If we it isn't
+ yet executing the original insn, set a breakpoint there, and let
+ the thread run to it, so to quickly step over a possible (many
+ insns) gdb_collect call. Otherwise, or when the breakpoint is
+ hit, only a few (small number of) insns are left to be executed
+ in the jump pad. Single-step the thread until it leaves the
+ jump pad. */
+
+ again:
+ tpoint = NULL;
+ needs_breakpoint = 0;
+ trace_debug ("fast_tracepoint_collecting");
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer,
+ &ipa_gdb_jump_pad_buffer))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_jump_pad_buffer'");
+ }
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer_end,
+ &ipa_gdb_jump_pad_buffer_end))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_jump_pad_buffer_end'");
+ }
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
+ &ipa_gdb_trampoline_buffer))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_trampoline_buffer'");
+ }
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &ipa_gdb_trampoline_buffer_end))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_trampoline_buffer_end'");
+ }
+
+ if (ipa_gdb_jump_pad_buffer <= stop_pc
+ && stop_pc < ipa_gdb_jump_pad_buffer_end)
+ {
+ /* We can tell which tracepoint(s) the thread is collecting by
+ matching the jump pad address back to the tracepoint. */
+ tpoint = fast_tracepoint_from_jump_pad_address (stop_pc);
+ if (tpoint == NULL)
+ {
+ warning ("in jump pad, but no matching tpoint?");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+ else
+ {
+ trace_debug ("in jump pad of tpoint (%d, %s); jump_pad(%s, %s); "
+ "adj_insn(%s, %s)",
+ tpoint->number, paddress (tpoint->address),
+ paddress (tpoint->jump_pad),
+ paddress (tpoint->jump_pad_end),
+ paddress (tpoint->adjusted_insn_addr),
+ paddress (tpoint->adjusted_insn_addr_end));
+ }
+
+ /* Definitely in the jump pad. May or may not need
+ fast-exit-jump-pad breakpoint. */
+ if (tpoint->jump_pad <= stop_pc
+ && stop_pc < tpoint->adjusted_insn_addr)
+ needs_breakpoint = 1;
+ }
+ else if (ipa_gdb_trampoline_buffer <= stop_pc
+ && stop_pc < ipa_gdb_trampoline_buffer_end)
+ {
+ /* We can tell which tracepoint(s) the thread is collecting by
+ matching the trampoline address back to the tracepoint. */
+ tpoint = fast_tracepoint_from_trampoline_address (stop_pc);
+ if (tpoint == NULL)
+ {
+ warning ("in trampoline, but no matching tpoint?");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+ else
+ {
+ trace_debug ("in trampoline of tpoint (%d, %s); trampoline(%s, %s)",
+ tpoint->number, paddress (tpoint->address),
+ paddress (tpoint->trampoline),
+ paddress (tpoint->trampoline_end));
+ }
+
+ /* Have not reached jump pad yet, but treat the trampoline as a
+ part of the jump pad that is before the adjusted original
+ instruction. */
+ needs_breakpoint = 1;
+ }
+ else
+ {
+ collecting_t ipa_collecting_obj;
+
+ /* If `collecting' is set/locked, then the THREAD_AREA thread
+ may or not be the one holding the lock. We have to read the
+ lock to find out. */
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_collecting,
+ &ipa_collecting))
+ {
+ trace_debug ("fast_tracepoint_collecting:"
+ " failed reading 'collecting' in the inferior");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ if (!ipa_collecting)
+ {
+ trace_debug ("fast_tracepoint_collecting: not collecting"
+ " (and nobody is).");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ /* Some thread is collecting. Check which. */
+ if (read_inferior_memory (ipa_collecting,
+ (unsigned char *) &ipa_collecting_obj,
+ sizeof (ipa_collecting_obj)) != 0)
+ goto again;
+
+ if (ipa_collecting_obj.thread_area != thread_area)
+ {
+ trace_debug ("fast_tracepoint_collecting: not collecting "
+ "(another thread is)");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ tpoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_collecting_obj.tpoint);
+ if (tpoint == NULL)
+ {
+ warning ("fast_tracepoint_collecting: collecting, "
+ "but tpoint %s not found?",
+ paddress ((CORE_ADDR) ipa_collecting_obj.tpoint));
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ /* The thread is within `gdb_collect', skip over the rest of
+ fast tracepoint collection quickly using a breakpoint. */
+ needs_breakpoint = 1;
+ }
+
+ /* The caller wants a bit of status detail. */
+ if (status != NULL)
+ {
+ status->tpoint_num = tpoint->number;
+ status->tpoint_addr = tpoint->address;
+ status->adjusted_insn_addr = tpoint->adjusted_insn_addr;
+ status->adjusted_insn_addr_end = tpoint->adjusted_insn_addr_end;
+ }
+
+ if (needs_breakpoint)
+ {
+ /* Hasn't executed the original instruction yet. Set breakpoint
+ there, and wait till it's hit, then single-step until exiting
+ the jump pad. */
+
+ trace_debug ("\
+fast_tracepoint_collecting, returning continue-until-break at %s",
+ paddress (tpoint->adjusted_insn_addr));
+
+ return fast_tpoint_collect_result::before_insn; /* continue */
+ }
+ else
+ {
+ /* Just single-step until exiting the jump pad. */
+
+ trace_debug ("fast_tracepoint_collecting, returning "
+ "need-single-step (%s-%s)",
+ paddress (tpoint->adjusted_insn_addr),
+ paddress (tpoint->adjusted_insn_addr_end));
+
+ return fast_tpoint_collect_result::at_insn; /* single-step */
+ }
+}
+
+#endif
+
+#ifdef IN_PROCESS_AGENT
+
+/* The global fast tracepoint collect lock. Points to a collecting_t
+ object built on the stack by the jump pad, if presently locked;
+ NULL if it isn't locked. Note that this lock *must* be set while
+ executing any *function other than the jump pad. See
+ fast_tracepoint_collecting. */
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR collecting_t *collecting;
+EXTERN_C_POP
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void gdb_collect (struct tracepoint *tpoint,
+ unsigned char *regs);
+
+/* This routine, called from the jump pad (in asm) is designed to be
+ called from the jump pads of fast tracepoints, thus it is on the
+ critical path. */
+
+IP_AGENT_EXPORT_FUNC void
+gdb_collect (struct tracepoint *tpoint, unsigned char *regs)
+{
+ struct fast_tracepoint_ctx ctx;
+ const struct target_desc *ipa_tdesc;
+
+ /* Don't do anything until the trace run is completely set up. */
+ if (!tracing)
+ return;
+
+ ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
+ ctx.base.type = fast_tracepoint;
+ ctx.regs = regs;
+ ctx.regcache_initted = 0;
+ /* Wrap the regblock in a register cache (in the stack, we don't
+ want to malloc here). */
+ ctx.regspace = (unsigned char *) alloca (ipa_tdesc->registers_size);
+ if (ctx.regspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ return;
+ }
+
+ for (ctx.tpoint = tpoint;
+ ctx.tpoint != NULL && ctx.tpoint->address == tpoint->address;
+ ctx.tpoint = ctx.tpoint->next)
+ {
+ if (!ctx.tpoint->enabled)
+ continue;
+
+ /* Multiple tracepoints of different types, such as fast tracepoint and
+ static tracepoint, can be set at the same address. */
+ if (ctx.tpoint->type != tpoint->type)
+ continue;
+
+ /* Test the condition if present, and collect if true. */
+ if (ctx.tpoint->cond == NULL
+ || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ ctx.tpoint))
+ {
+ collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ ctx.tpoint->address, ctx.tpoint);
+
+ /* Note that this will cause original insns to be written back
+ to where we jumped from, but that's OK because we're jumping
+ back to the next whole instruction. This will go badly if
+ instruction restoration is not atomic though. */
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ break;
+ }
+ }
+ else
+ {
+ /* If there was a condition and it evaluated to false, the only
+ way we would stop tracing is if there was an error during
+ condition expression evaluation. */
+ if (expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ break;
+ }
+ }
+ }
+}
+
+/* These global variables points to the corresponding functions. This is
+ necessary on powerpc64, where asking for function symbol address from gdb
+ results in returning the actual code pointer, instead of the descriptor
+ pointer. */
+
+typedef void (*gdb_collect_ptr_type) (struct tracepoint *, unsigned char *);
+typedef ULONGEST (*get_raw_reg_ptr_type) (const unsigned char *, int);
+typedef LONGEST (*get_trace_state_variable_value_ptr_type) (int);
+typedef void (*set_trace_state_variable_value_ptr_type) (int, LONGEST);
+
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR gdb_collect_ptr_type gdb_collect_ptr = gdb_collect;
+IP_AGENT_EXPORT_VAR get_raw_reg_ptr_type get_raw_reg_ptr = get_raw_reg;
+IP_AGENT_EXPORT_VAR get_trace_state_variable_value_ptr_type
+ get_trace_state_variable_value_ptr = get_trace_state_variable_value;
+IP_AGENT_EXPORT_VAR set_trace_state_variable_value_ptr_type
+ set_trace_state_variable_value_ptr = set_trace_state_variable_value;
+EXTERN_C_POP
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+CORE_ADDR
+get_raw_reg_func_addr (void)
+{
+ CORE_ADDR res;
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_get_raw_reg_ptr, &res))
+ {
+ error ("error extracting get_raw_reg_ptr");
+ return 0;
+ }
+ return res;
+}
+
+CORE_ADDR
+get_get_tsv_func_addr (void)
+{
+ CORE_ADDR res;
+ if (read_inferior_data_pointer (
+ ipa_sym_addrs.addr_get_trace_state_variable_value_ptr, &res))
+ {
+ error ("error extracting get_trace_state_variable_value_ptr");
+ return 0;
+ }
+ return res;
+}
+
+CORE_ADDR
+get_set_tsv_func_addr (void)
+{
+ CORE_ADDR res;
+ if (read_inferior_data_pointer (
+ ipa_sym_addrs.addr_set_trace_state_variable_value_ptr, &res))
+ {
+ error ("error extracting set_trace_state_variable_value_ptr");
+ return 0;
+ }
+ return res;
+}
+
+static void
+compile_tracepoint_condition (struct tracepoint *tpoint,
+ CORE_ADDR *jump_entry)
+{
+ CORE_ADDR entry_point = *jump_entry;
+ enum eval_result_type err;
+
+ trace_debug ("Starting condition compilation for tracepoint %d\n",
+ tpoint->number);
+
+ /* Initialize the global pointer to the code being built. */
+ current_insn_ptr = *jump_entry;
+
+ emit_prologue ();
+
+ err = compile_bytecodes (tpoint->cond);
+
+ if (err == expr_eval_no_error)
+ {
+ emit_epilogue ();
+
+ /* Record the beginning of the compiled code. */
+ tpoint->compiled_cond = entry_point;
+
+ trace_debug ("Condition compilation for tracepoint %d complete\n",
+ tpoint->number);
+ }
+ else
+ {
+ /* Leave the unfinished code in situ, but don't point to it. */
+
+ tpoint->compiled_cond = 0;
+
+ trace_debug ("Condition compilation for tracepoint %d failed, "
+ "error code %d",
+ tpoint->number, err);
+ }
+
+ /* Update the code pointer passed in. Note that we do this even if
+ the compile fails, so that we can look at the partial results
+ instead of letting them be overwritten. */
+ *jump_entry = current_insn_ptr;
+
+ /* Leave a gap, to aid dump decipherment. */
+ *jump_entry += 16;
+}
+
+/* The base pointer of the IPA's heap. This is the only memory the
+ IPA is allowed to use. The IPA should _not_ call the inferior's
+ `malloc' during operation. That'd be slow, and, most importantly,
+ it may not be safe. We may be collecting a tracepoint in a signal
+ handler, for example. */
+static CORE_ADDR target_tp_heap;
+
+/* Allocate at least SIZE bytes of memory from the IPA heap, aligned
+ to 8 bytes. */
+
+static CORE_ADDR
+target_malloc (ULONGEST size)
+{
+ CORE_ADDR ptr;
+
+ if (target_tp_heap == 0)
+ {
+ /* We have the pointer *address*, need what it points to. */
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_tp_heap_buffer,
+ &target_tp_heap))
+ {
+ internal_error (__FILE__, __LINE__,
+ "couldn't get target heap head pointer");
+ }
+ }
+
+ ptr = target_tp_heap;
+ target_tp_heap += size;
+
+ /* Pad to 8-byte alignment. */
+ target_tp_heap = ((target_tp_heap + 7) & ~0x7);
+
+ return ptr;
+}
+
+static CORE_ADDR
+download_agent_expr (struct agent_expr *expr)
+{
+ CORE_ADDR expr_addr;
+ CORE_ADDR expr_bytes;
+
+ expr_addr = target_malloc (sizeof (*expr));
+ target_write_memory (expr_addr, (unsigned char *) expr, sizeof (*expr));
+
+ expr_bytes = target_malloc (expr->length);
+ write_inferior_data_pointer (expr_addr + offsetof (struct agent_expr, bytes),
+ expr_bytes);
+ target_write_memory (expr_bytes, expr->bytes, expr->length);
+
+ return expr_addr;
+}
+
+/* Align V up to N bits. */
+#define UALIGN(V, N) (((V) + ((N) - 1)) & ~((N) - 1))
+
+/* Sync tracepoint with IPA, but leave maintenance of linked list to caller. */
+
+static void
+download_tracepoint_1 (struct tracepoint *tpoint)
+{
+ struct tracepoint target_tracepoint;
+ CORE_ADDR tpptr = 0;
+
+ gdb_assert (tpoint->type == fast_tracepoint
+ || tpoint->type == static_tracepoint);
+
+ if (tpoint->cond != NULL && target_emit_ops () != NULL)
+ {
+ CORE_ADDR jentry, jump_entry;
+
+ jentry = jump_entry = get_jump_space_head ();
+
+ if (tpoint->cond != NULL)
+ {
+ /* Pad to 8-byte alignment. (needed?) */
+ /* Actually this should be left for the target to
+ decide. */
+ jentry = UALIGN (jentry, 8);
+
+ compile_tracepoint_condition (tpoint, &jentry);
+ }
+
+ /* Pad to 8-byte alignment. */
+ jentry = UALIGN (jentry, 8);
+ claim_jump_space (jentry - jump_entry);
+ }
+
+ target_tracepoint = *tpoint;
+
+ tpptr = target_malloc (sizeof (*tpoint));
+ tpoint->obj_addr_on_target = tpptr;
+
+ /* Write the whole object. We'll fix up its pointers in a bit.
+ Assume no next for now. This is fixed up above on the next
+ iteration, if there's any. */
+ target_tracepoint.next = NULL;
+ /* Need to clear this here too, since we're downloading the
+ tracepoints before clearing our own copy. */
+ target_tracepoint.hit_count = 0;
+
+ target_write_memory (tpptr, (unsigned char *) &target_tracepoint,
+ sizeof (target_tracepoint));
+
+ if (tpoint->cond)
+ write_inferior_data_pointer (tpptr
+ + offsetof (struct tracepoint, cond),
+ download_agent_expr (tpoint->cond));
+
+ if (tpoint->numactions)
+ {
+ int i;
+ CORE_ADDR actions_array;
+
+ /* The pointers array. */
+ actions_array
+ = target_malloc (sizeof (*tpoint->actions) * tpoint->numactions);
+ write_inferior_data_pointer (tpptr + offsetof (struct tracepoint,
+ actions),
+ actions_array);
+
+ /* Now for each pointer, download the action. */
+ for (i = 0; i < tpoint->numactions; i++)
+ {
+ struct tracepoint_action *action = tpoint->actions[i];
+ CORE_ADDR ipa_action = tracepoint_action_download (action);
+
+ if (ipa_action != 0)
+ write_inferior_data_pointer (actions_array
+ + i * sizeof (*tpoint->actions),
+ ipa_action);
+ }
+ }
+}
+
+#define IPA_PROTO_FAST_TRACE_FLAG 0
+#define IPA_PROTO_FAST_TRACE_ADDR_ON_TARGET 2
+#define IPA_PROTO_FAST_TRACE_JUMP_PAD 10
+#define IPA_PROTO_FAST_TRACE_FJUMP_SIZE 18
+#define IPA_PROTO_FAST_TRACE_FJUMP_INSN 22
+
+/* Send a command to agent to download and install tracepoint TPOINT. */
+
+static int
+tracepoint_send_agent (struct tracepoint *tpoint)
+{
+ char buf[IPA_CMD_BUF_SIZE];
+ char *p;
+ int i, ret;
+
+ p = buf;
+ strcpy (p, "FastTrace:");
+ p += 10;
+
+ COPY_FIELD_TO_BUF (p, tpoint, number);
+ COPY_FIELD_TO_BUF (p, tpoint, address);
+ COPY_FIELD_TO_BUF (p, tpoint, type);
+ COPY_FIELD_TO_BUF (p, tpoint, enabled);
+ COPY_FIELD_TO_BUF (p, tpoint, step_count);
+ COPY_FIELD_TO_BUF (p, tpoint, pass_count);
+ COPY_FIELD_TO_BUF (p, tpoint, numactions);
+ COPY_FIELD_TO_BUF (p, tpoint, hit_count);
+ COPY_FIELD_TO_BUF (p, tpoint, traceframe_usage);
+ COPY_FIELD_TO_BUF (p, tpoint, compiled_cond);
+ COPY_FIELD_TO_BUF (p, tpoint, orig_size);
+
+ /* condition */
+ p = agent_expr_send (p, tpoint->cond);
+
+ /* tracepoint_action */
+ for (i = 0; i < tpoint->numactions; i++)
+ {
+ struct tracepoint_action *action = tpoint->actions[i];
+
+ p[0] = action->type;
+ p = tracepoint_action_send (&p[1], action);
+ }
+
+ get_jump_space_head ();
+ /* Copy the value of GDB_JUMP_PAD_HEAD to command buffer, so that
+ agent can use jump pad from it. */
+ if (tpoint->type == fast_tracepoint)
+ {
+ memcpy (p, &gdb_jump_pad_head, 8);
+ p += 8;
+ }
+
+ ret = run_inferior_command (buf, (int) (ptrdiff_t) (p - buf));
+ if (ret)
+ return ret;
+
+ if (!startswith (buf, "OK"))
+ return 1;
+
+ /* The value of tracepoint's target address is stored in BUF. */
+ memcpy (&tpoint->obj_addr_on_target,
+ &buf[IPA_PROTO_FAST_TRACE_ADDR_ON_TARGET], 8);
+
+ if (tpoint->type == fast_tracepoint)
+ {
+ unsigned char *insn
+ = (unsigned char *) &buf[IPA_PROTO_FAST_TRACE_FJUMP_INSN];
+ int fjump_size;
+
+ trace_debug ("agent: read from cmd_buf 0x%x 0x%x\n",
+ (unsigned int) tpoint->obj_addr_on_target,
+ (unsigned int) gdb_jump_pad_head);
+
+ memcpy (&gdb_jump_pad_head, &buf[IPA_PROTO_FAST_TRACE_JUMP_PAD], 8);
+
+ /* This has been done in agent. We should also set up record for it. */
+ memcpy (&fjump_size, &buf[IPA_PROTO_FAST_TRACE_FJUMP_SIZE], 4);
+ /* Wire it in. */
+ tpoint->handle
+ = set_fast_tracepoint_jump (tpoint->address, insn, fjump_size);
+ }
+
+ return 0;
+}
+
+static void
+download_tracepoint (struct tracepoint *tpoint)
+{
+ struct tracepoint *tp, *tp_prev;
+
+ if (tpoint->type != fast_tracepoint
+ && tpoint->type != static_tracepoint)
+ return;
+
+ download_tracepoint_1 (tpoint);
+
+ /* Find the previous entry of TPOINT, which is fast tracepoint or
+ static tracepoint. */
+ tp_prev = NULL;
+ for (tp = tracepoints; tp != tpoint; tp = tp->next)
+ {
+ if (tp->type == fast_tracepoint || tp->type == static_tracepoint)
+ tp_prev = tp;
+ }
+
+ if (tp_prev)
+ {
+ CORE_ADDR tp_prev_target_next_addr;
+
+ /* Insert TPOINT after TP_PREV in IPA. */
+ if (read_inferior_data_pointer (tp_prev->obj_addr_on_target
+ + offsetof (struct tracepoint, next),
+ &tp_prev_target_next_addr))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error reading `tp_prev->next'");
+ }
+
+ /* tpoint->next = tp_prev->next */
+ write_inferior_data_pointer (tpoint->obj_addr_on_target
+ + offsetof (struct tracepoint, next),
+ tp_prev_target_next_addr);
+ /* tp_prev->next = tpoint */
+ write_inferior_data_pointer (tp_prev->obj_addr_on_target
+ + offsetof (struct tracepoint, next),
+ tpoint->obj_addr_on_target);
+ }
+ else
+ /* First object in list, set the head pointer in the
+ inferior. */
+ write_inferior_data_pointer (ipa_sym_addrs.addr_tracepoints,
+ tpoint->obj_addr_on_target);
+
+}
+
+static void
+download_trace_state_variables (void)
+{
+ CORE_ADDR ptr = 0, prev_ptr = 0;
+ struct trace_state_variable *tsv;
+
+ /* Start out empty. */
+ write_inferior_data_pointer (ipa_sym_addrs.addr_trace_state_variables, 0);
+
+ for (tsv = trace_state_variables; tsv != NULL; tsv = tsv->next)
+ {
+ struct trace_state_variable target_tsv;
+
+ /* TSV's with a getter have been initialized equally in both the
+ inferior and GDBserver. Skip them. */
+ if (tsv->getter != NULL)
+ continue;
+
+ target_tsv = *tsv;
+
+ prev_ptr = ptr;
+ ptr = target_malloc (sizeof (*tsv));
+
+ if (tsv == trace_state_variables)
+ {
+ /* First object in list, set the head pointer in the
+ inferior. */
+
+ write_inferior_data_pointer (ipa_sym_addrs.addr_trace_state_variables,
+ ptr);
+ }
+ else
+ {
+ write_inferior_data_pointer (prev_ptr
+ + offsetof (struct trace_state_variable,
+ next),
+ ptr);
+ }
+
+ /* Write the whole object. We'll fix up its pointers in a bit.
+ Assume no next, fixup when needed. */
+ target_tsv.next = NULL;
+
+ target_write_memory (ptr, (unsigned char *) &target_tsv,
+ sizeof (target_tsv));
+
+ if (tsv->name != NULL)
+ {
+ size_t size = strlen (tsv->name) + 1;
+ CORE_ADDR name_addr = target_malloc (size);
+ target_write_memory (name_addr,
+ (unsigned char *) tsv->name, size);
+ write_inferior_data_pointer (ptr
+ + offsetof (struct trace_state_variable,
+ name),
+ name_addr);
+ }
+
+ gdb_assert (tsv->getter == NULL);
+ }
+
+ if (prev_ptr != 0)
+ {
+ /* Fixup the next pointer in the last item in the list. */
+ write_inferior_data_pointer (prev_ptr
+ + offsetof (struct trace_state_variable,
+ next), 0);
+ }
+}
+
+/* Upload complete trace frames out of the IP Agent's trace buffer
+ into GDBserver's trace buffer. This always uploads either all or
+ no trace frames. This is the counter part of
+ `trace_alloc_trace_buffer'. See its description of the atomic
+ syncing mechanism. */
+
+static void
+upload_fast_traceframes (void)
+{
+ unsigned int ipa_traceframe_read_count, ipa_traceframe_write_count;
+ unsigned int ipa_traceframe_read_count_racy, ipa_traceframe_write_count_racy;
+ CORE_ADDR tf;
+ struct ipa_trace_buffer_control ipa_trace_buffer_ctrl;
+ unsigned int curr_tbctrl_idx;
+ unsigned int ipa_trace_buffer_ctrl_curr;
+ unsigned int ipa_trace_buffer_ctrl_curr_old;
+ CORE_ADDR ipa_trace_buffer_ctrl_addr;
+ struct breakpoint *about_to_request_buffer_space_bkpt;
+ CORE_ADDR ipa_trace_buffer_lo;
+ CORE_ADDR ipa_trace_buffer_hi;
+
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count,
+ &ipa_traceframe_read_count_racy))
+ {
+ /* This will happen in most targets if the current thread is
+ running. */
+ return;
+ }
+
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count,
+ &ipa_traceframe_write_count_racy))
+ return;
+
+ trace_debug ("ipa_traceframe_count (racy area): %d (w=%d, r=%d)",
+ ipa_traceframe_write_count_racy
+ - ipa_traceframe_read_count_racy,
+ ipa_traceframe_write_count_racy,
+ ipa_traceframe_read_count_racy);
+
+ if (ipa_traceframe_write_count_racy == ipa_traceframe_read_count_racy)
+ return;
+
+ about_to_request_buffer_space_bkpt
+ = set_breakpoint_at (ipa_sym_addrs.addr_about_to_request_buffer_space,
+ NULL);
+
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr,
+ &ipa_trace_buffer_ctrl_curr))
+ return;
+
+ ipa_trace_buffer_ctrl_curr_old = ipa_trace_buffer_ctrl_curr;
+
+ curr_tbctrl_idx = ipa_trace_buffer_ctrl_curr & ~GDBSERVER_FLUSH_COUNT_MASK;
+
+ {
+ unsigned int prev, counter;
+
+ /* Update the token, with new counters, and the GDBserver stamp
+ bit. Alway reuse the current TBC index. */
+ prev = ipa_trace_buffer_ctrl_curr & GDBSERVER_FLUSH_COUNT_MASK_CURR;
+ counter = (prev + 0x100) & GDBSERVER_FLUSH_COUNT_MASK_CURR;
+
+ ipa_trace_buffer_ctrl_curr = (GDBSERVER_UPDATED_FLUSH_COUNT_BIT
+ | (prev << 12)
+ | counter
+ | curr_tbctrl_idx);
+ }
+
+ if (write_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr,
+ ipa_trace_buffer_ctrl_curr))
+ return;
+
+ trace_debug ("Lib: Committed %08x -> %08x",
+ ipa_trace_buffer_ctrl_curr_old,
+ ipa_trace_buffer_ctrl_curr);
+
+ /* Re-read these, now that we've installed the
+ `about_to_request_buffer_space' breakpoint/lock. A thread could
+ have finished a traceframe between the last read of these
+ counters and setting the breakpoint above. If we start
+ uploading, we never want to leave this function with
+ traceframe_read_count != 0, otherwise, GDBserver could end up
+ incrementing the counter tokens more than once (due to event loop
+ nesting), which would break the IP agent's "effective" detection
+ (see trace_alloc_trace_buffer). */
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count,
+ &ipa_traceframe_read_count))
+ return;
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count,
+ &ipa_traceframe_write_count))
+ return;
+
+ if (debug_threads)
+ {
+ trace_debug ("ipa_traceframe_count (blocked area): %d (w=%d, r=%d)",
+ ipa_traceframe_write_count - ipa_traceframe_read_count,
+ ipa_traceframe_write_count, ipa_traceframe_read_count);
+
+ if (ipa_traceframe_write_count != ipa_traceframe_write_count_racy
+ || ipa_traceframe_read_count != ipa_traceframe_read_count_racy)
+ trace_debug ("note that ipa_traceframe_count's parts changed");
+ }
+
+ /* Get the address of the current TBC object (the IP agent has an
+ array of 3 such objects). The index is stored in the TBC
+ token. */
+ ipa_trace_buffer_ctrl_addr = ipa_sym_addrs.addr_trace_buffer_ctrl;
+ ipa_trace_buffer_ctrl_addr
+ += sizeof (struct ipa_trace_buffer_control) * curr_tbctrl_idx;
+
+ if (read_inferior_memory (ipa_trace_buffer_ctrl_addr,
+ (unsigned char *) &ipa_trace_buffer_ctrl,
+ sizeof (struct ipa_trace_buffer_control)))
+ return;
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_lo,
+ &ipa_trace_buffer_lo))
+ return;
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_hi,
+ &ipa_trace_buffer_hi))
+ return;
+
+ /* Offsets are easier to grok for debugging than raw addresses,
+ especially for the small trace buffer sizes that are useful for
+ testing. */
+ trace_debug ("Lib: Trace buffer [%d] start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr_tbctrl_idx,
+ (int) (ipa_trace_buffer_ctrl.start - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.free - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.end_free - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_hi - ipa_trace_buffer_lo));
+
+ /* Note that the IPA's buffer is always circular. */
+
+#define IPA_FIRST_TRACEFRAME() (ipa_trace_buffer_ctrl.start)
+
+#define IPA_NEXT_TRACEFRAME_1(TF, TFOBJ) \
+ ((TF) + sizeof (struct traceframe) + (TFOBJ)->data_size)
+
+#define IPA_NEXT_TRACEFRAME(TF, TFOBJ) \
+ (IPA_NEXT_TRACEFRAME_1 (TF, TFOBJ) \
+ - ((IPA_NEXT_TRACEFRAME_1 (TF, TFOBJ) >= ipa_trace_buffer_ctrl.wrap) \
+ ? (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo) \
+ : 0))
+
+ tf = IPA_FIRST_TRACEFRAME ();
+
+ while (ipa_traceframe_write_count - ipa_traceframe_read_count)
+ {
+ struct tracepoint *tpoint;
+ struct traceframe *tframe;
+ unsigned char *block;
+ struct traceframe ipa_tframe;
+
+ if (read_inferior_memory (tf, (unsigned char *) &ipa_tframe,
+ offsetof (struct traceframe, data)))
+ error ("Uploading: couldn't read traceframe at %s\n", paddress (tf));
+
+ if (ipa_tframe.tpnum == 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "Uploading: No (more) fast traceframes, but"
+ " ipa_traceframe_count == %u??\n",
+ ipa_traceframe_write_count
+ - ipa_traceframe_read_count);
+ }
+
+ /* Note that this will be incorrect for multi-location
+ tracepoints... */
+ tpoint = find_next_tracepoint_by_number (NULL, ipa_tframe.tpnum);
+
+ tframe = add_traceframe (tpoint);
+ if (tframe == NULL)
+ {
+ trace_buffer_is_full = 1;
+ trace_debug ("Uploading: trace buffer is full");
+ }
+ else
+ {
+ /* Copy the whole set of blocks in one go for now. FIXME:
+ split this in smaller blocks. */
+ block = add_traceframe_block (tframe, tpoint,
+ ipa_tframe.data_size);
+ if (block != NULL)
+ {
+ if (read_inferior_memory (tf
+ + offsetof (struct traceframe, data),
+ block, ipa_tframe.data_size))
+ error ("Uploading: Couldn't read traceframe data at %s\n",
+ paddress (tf + offsetof (struct traceframe, data)));
+ }
+
+ trace_debug ("Uploading: traceframe didn't fit");
+ finish_traceframe (tframe);
+ }
+
+ tf = IPA_NEXT_TRACEFRAME (tf, &ipa_tframe);
+
+ /* If we freed the traceframe that wrapped around, go back
+ to the non-wrap case. */
+ if (tf < ipa_trace_buffer_ctrl.start)
+ {
+ trace_debug ("Lib: Discarding past the wraparound");
+ ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
+ }
+ ipa_trace_buffer_ctrl.start = tf;
+ ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_ctrl.start;
+ ++ipa_traceframe_read_count;
+
+ if (ipa_trace_buffer_ctrl.start == ipa_trace_buffer_ctrl.free
+ && ipa_trace_buffer_ctrl.start == ipa_trace_buffer_ctrl.end_free)
+ {
+ trace_debug ("Lib: buffer is fully empty. "
+ "Trace buffer [%d] start=%d free=%d endfree=%d",
+ curr_tbctrl_idx,
+ (int) (ipa_trace_buffer_ctrl.start
+ - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.free
+ - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.end_free
+ - ipa_trace_buffer_lo));
+
+ ipa_trace_buffer_ctrl.start = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.free = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_hi;
+ ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
+ }
+
+ trace_debug ("Uploaded a traceframe\n"
+ "Lib: Trace buffer [%d] start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr_tbctrl_idx,
+ (int) (ipa_trace_buffer_ctrl.start - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.free - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.end_free
+ - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_hi - ipa_trace_buffer_lo));
+ }
+
+ if (target_write_memory (ipa_trace_buffer_ctrl_addr,
+ (unsigned char *) &ipa_trace_buffer_ctrl,
+ sizeof (struct ipa_trace_buffer_control)))
+ return;
+
+ write_inferior_integer (ipa_sym_addrs.addr_traceframe_read_count,
+ ipa_traceframe_read_count);
+
+ trace_debug ("Done uploading traceframes [%d]\n", curr_tbctrl_idx);
+
+ pause_all (1);
+
+ delete_breakpoint (about_to_request_buffer_space_bkpt);
+ about_to_request_buffer_space_bkpt = NULL;
+
+ unpause_all (1);
+
+ if (trace_buffer_is_full)
+ stop_tracing ();
+}
+#endif
+
+#ifdef IN_PROCESS_AGENT
+
+IP_AGENT_EXPORT_VAR int ust_loaded;
+IP_AGENT_EXPORT_VAR char cmd_buf[IPA_CMD_BUF_SIZE];
+
+#ifdef HAVE_UST
+
+/* Static tracepoints. */
+
+/* UST puts a "struct tracepoint" in the global namespace, which
+ conflicts with our tracepoint. Arguably, being a library, it
+ shouldn't take ownership of such a generic name. We work around it
+ here. */
+#define tracepoint ust_tracepoint
+#include <ust/ust.h>
+#undef tracepoint
+
+extern int serialize_to_text (char *outbuf, int bufsize,
+ const char *fmt, va_list ap);
+
+#define GDB_PROBE_NAME "gdb"
+
+/* We dynamically search for the UST symbols instead of linking them
+ in. This lets the user decide if the application uses static
+ tracepoints, instead of always pulling libust.so in. This vector
+ holds pointers to all functions we care about. */
+
+static struct
+{
+ int (*serialize_to_text) (char *outbuf, int bufsize,
+ const char *fmt, va_list ap);
+
+ int (*ltt_probe_register) (struct ltt_available_probe *pdata);
+ int (*ltt_probe_unregister) (struct ltt_available_probe *pdata);
+
+ int (*ltt_marker_connect) (const char *channel, const char *mname,
+ const char *pname);
+ int (*ltt_marker_disconnect) (const char *channel, const char *mname,
+ const char *pname);
+
+ void (*marker_iter_start) (struct marker_iter *iter);
+ void (*marker_iter_next) (struct marker_iter *iter);
+ void (*marker_iter_stop) (struct marker_iter *iter);
+ void (*marker_iter_reset) (struct marker_iter *iter);
+} ust_ops;
+
+#include <dlfcn.h>
+
+/* Cast through typeof to catch incompatible API changes. Since UST
+ only builds with gcc, we can freely use gcc extensions here
+ too. */
+#define GET_UST_SYM(SYM) \
+ do \
+ { \
+ if (ust_ops.SYM == NULL) \
+ ust_ops.SYM = (typeof (&SYM)) dlsym (RTLD_DEFAULT, #SYM); \
+ if (ust_ops.SYM == NULL) \
+ return 0; \
+ } while (0)
+
+#define USTF(SYM) ust_ops.SYM
+
+/* Get pointers to all libust.so functions we care about. */
+
+static int
+dlsym_ust (void)
+{
+ GET_UST_SYM (serialize_to_text);
+
+ GET_UST_SYM (ltt_probe_register);
+ GET_UST_SYM (ltt_probe_unregister);
+ GET_UST_SYM (ltt_marker_connect);
+ GET_UST_SYM (ltt_marker_disconnect);
+
+ GET_UST_SYM (marker_iter_start);
+ GET_UST_SYM (marker_iter_next);
+ GET_UST_SYM (marker_iter_stop);
+ GET_UST_SYM (marker_iter_reset);
+
+ ust_loaded = 1;
+ return 1;
+}
+
+/* Given an UST marker, return the matching gdb static tracepoint.
+ The match is done by address. */
+
+static struct tracepoint *
+ust_marker_to_static_tracepoint (const struct marker *mdata)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ if (tpoint->type != static_tracepoint)
+ continue;
+
+ if (tpoint->address == (uintptr_t) mdata->location)
+ return tpoint;
+ }
+
+ return NULL;
+}
+
+/* The probe function we install on lttng/ust markers. Whenever a
+ probed ust marker is hit, this function is called. This is similar
+ to gdb_collect, only for static tracepoints, instead of fast
+ tracepoints. */
+
+static void
+gdb_probe (const struct marker *mdata, void *probe_private,
+ struct registers *regs, void *call_private,
+ const char *fmt, va_list *args)
+{
+ struct tracepoint *tpoint;
+ struct static_tracepoint_ctx ctx;
+ const struct target_desc *ipa_tdesc;
+
+ /* Don't do anything until the trace run is completely set up. */
+ if (!tracing)
+ {
+ trace_debug ("gdb_probe: not tracing\n");
+ return;
+ }
+
+ ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
+ ctx.base.type = static_tracepoint;
+ ctx.regcache_initted = 0;
+ ctx.regs = regs;
+ ctx.fmt = fmt;
+ ctx.args = args;
+
+ /* Wrap the regblock in a register cache (in the stack, we don't
+ want to malloc here). */
+ ctx.regspace = alloca (ipa_tdesc->registers_size);
+ if (ctx.regspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ return;
+ }
+
+ tpoint = ust_marker_to_static_tracepoint (mdata);
+ if (tpoint == NULL)
+ {
+ trace_debug ("gdb_probe: marker not known: "
+ "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"",
+ mdata->location, mdata->channel,
+ mdata->name, mdata->format);
+ return;
+ }
+
+ if (!tpoint->enabled)
+ {
+ trace_debug ("gdb_probe: tracepoint disabled");
+ return;
+ }
+
+ ctx.tpoint = tpoint;
+
+ trace_debug ("gdb_probe: collecting marker: "
+ "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"",
+ mdata->location, mdata->channel,
+ mdata->name, mdata->format);
+
+ /* Test the condition if present, and collect if true. */
+ if (tpoint->cond == NULL
+ || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ tpoint))
+ {
+ collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ tpoint->address, tpoint);
+
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ stop_tracing ();
+ }
+ else
+ {
+ /* If there was a condition and it evaluated to false, the only
+ way we would stop tracing is if there was an error during
+ condition expression evaluation. */
+ if (expr_eval_result != expr_eval_no_error)
+ stop_tracing ();
+ }
+}
+
+/* Called if the gdb static tracepoint requested collecting "$_sdata",
+ static tracepoint string data. This is a string passed to the
+ tracing library by the user, at the time of the tracepoint marker
+ call. E.g., in the UST marker call:
+
+ trace_mark (ust, bar33, "str %s", "FOOBAZ");
+
+ the collected data is "str FOOBAZ".
+*/
+
+static void
+collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct traceframe *tframe)
+{
+ struct static_tracepoint_ctx *umd = (struct static_tracepoint_ctx *) ctx;
+ unsigned char *bufspace;
+ int size;
+ va_list copy;
+ unsigned short blocklen;
+
+ if (umd == NULL)
+ {
+ trace_debug ("Wanted to collect static trace data, "
+ "but there's no static trace data");
+ return;
+ }
+
+ va_copy (copy, *umd->args);
+ size = USTF(serialize_to_text) (NULL, 0, umd->fmt, copy);
+ va_end (copy);
+
+ trace_debug ("Want to collect ust data");
+
+ /* 'S' + size + string */
+ bufspace = add_traceframe_block (tframe, umd->tpoint,
+ 1 + sizeof (blocklen) + size + 1);
+ if (bufspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ return;
+ }
+
+ /* Identify a static trace data block. */
+ *bufspace = 'S';
+
+ blocklen = size + 1;
+ memcpy (bufspace + 1, &blocklen, sizeof (blocklen));
+
+ va_copy (copy, *umd->args);
+ USTF(serialize_to_text) ((char *) bufspace + 1 + sizeof (blocklen),
+ size + 1, umd->fmt, copy);
+ va_end (copy);
+
+ trace_debug ("Storing static tracepoint data in regblock: %s",
+ bufspace + 1 + sizeof (blocklen));
+}
+
+/* The probe to register with lttng/ust. */
+static struct ltt_available_probe gdb_ust_probe =
+ {
+ GDB_PROBE_NAME,
+ NULL,
+ gdb_probe,
+ };
+
+#endif /* HAVE_UST */
+#endif /* IN_PROCESS_AGENT */
+
+#ifndef IN_PROCESS_AGENT
+
+/* Ask the in-process agent to run a command. Since we don't want to
+ have to handle the IPA hitting breakpoints while running the
+ command, we pause all threads, remove all breakpoints, and then set
+ the helper thread re-running. We communicate with the helper
+ thread by means of direct memory xfering, and a socket for
+ synchronization. */
+
+static int
+run_inferior_command (char *cmd, int len)
+{
+ int err = -1;
+ int pid = current_ptid.pid ();
+
+ trace_debug ("run_inferior_command: running: %s", cmd);
+
+ pause_all (0);
+ uninsert_all_breakpoints ();
+
+ err = agent_run_command (pid, (const char *) cmd, len);
+
+ reinsert_all_breakpoints ();
+ unpause_all (0);
+
+ return err;
+}
+
+#else /* !IN_PROCESS_AGENT */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path)
+#endif
+
+/* Where we put the socked used for synchronization. */
+#define SOCK_DIR P_tmpdir
+
+/* Thread ID of the helper thread. GDBserver reads this to know which
+ is the help thread. This is an LWP id on Linux. */
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR int helper_thread_id;
+EXTERN_C_POP
+
+static int
+init_named_socket (const char *name)
+{
+ int result, fd;
+ struct sockaddr_un addr;
+
+ result = fd = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (result == -1)
+ {
+ warning ("socket creation failed: %s", safe_strerror (errno));
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+
+ strncpy (addr.sun_path, name, UNIX_PATH_MAX);
+ addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
+
+ result = access (name, F_OK);
+ if (result == 0)
+ {
+ /* File exists. */
+ result = unlink (name);
+ if (result == -1)
+ {
+ warning ("unlink failed: %s", safe_strerror (errno));
+ close (fd);
+ return -1;
+ }
+ warning ("socket %s already exists; overwriting", name);
+ }
+
+ result = bind (fd, (struct sockaddr *) &addr, sizeof (addr));
+ if (result == -1)
+ {
+ warning ("bind failed: %s", safe_strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ result = listen (fd, 1);
+ if (result == -1)
+ {
+ warning ("listen: %s", safe_strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static char agent_socket_name[UNIX_PATH_MAX];
+
+static int
+gdb_agent_socket_init (void)
+{
+ int result, fd;
+
+ result = xsnprintf (agent_socket_name, UNIX_PATH_MAX, "%s/gdb_ust%d",
+ SOCK_DIR, getpid ());
+ if (result >= UNIX_PATH_MAX)
+ {
+ trace_debug ("string overflow allocating socket name");
+ return -1;
+ }
+
+ fd = init_named_socket (agent_socket_name);
+ if (fd < 0)
+ warning ("Error initializing named socket (%s) for communication with the "
+ "ust helper thread. Check that directory exists and that it "
+ "is writable.", agent_socket_name);
+
+ return fd;
+}
+
+#ifdef HAVE_UST
+
+/* The next marker to be returned on a qTsSTM command. */
+static const struct marker *next_st;
+
+/* Returns the first known marker. */
+
+struct marker *
+first_marker (void)
+{
+ struct marker_iter iter;
+
+ USTF(marker_iter_reset) (&iter);
+ USTF(marker_iter_start) (&iter);
+
+ return iter.marker;
+}
+
+/* Returns the marker following M. */
+
+const struct marker *
+next_marker (const struct marker *m)
+{
+ struct marker_iter iter;
+
+ USTF(marker_iter_reset) (&iter);
+ USTF(marker_iter_start) (&iter);
+
+ for (; iter.marker != NULL; USTF(marker_iter_next) (&iter))
+ {
+ if (iter.marker == m)
+ {
+ USTF(marker_iter_next) (&iter);
+ return iter.marker;
+ }
+ }
+
+ return NULL;
+}
+
+/* Return an hexstr version of the STR C string, fit for sending to
+ GDB. */
+
+static char *
+cstr_to_hexstr (const char *str)
+{
+ int len = strlen (str);
+ char *hexstr = xmalloc (len * 2 + 1);
+ bin2hex ((gdb_byte *) str, hexstr, len);
+ return hexstr;
+}
+
+/* Compose packet that is the response to the qTsSTM/qTfSTM/qTSTMat
+ packets. */
+
+static void
+response_ust_marker (char *packet, const struct marker *st)
+{
+ char *strid, *format, *tmp;
+
+ next_st = next_marker (st);
+
+ tmp = xmalloc (strlen (st->channel) + 1 +
+ strlen (st->name) + 1);
+ sprintf (tmp, "%s/%s", st->channel, st->name);
+
+ strid = cstr_to_hexstr (tmp);
+ free (tmp);
+
+ format = cstr_to_hexstr (st->format);
+
+ sprintf (packet, "m%s:%s:%s",
+ paddress ((uintptr_t) st->location),
+ strid,
+ format);
+
+ free (strid);
+ free (format);
+}
+
+/* Return the first static tracepoint, and initialize the state
+ machine that will iterate through all the static tracepoints. */
+
+static void
+cmd_qtfstm (char *packet)
+{
+ trace_debug ("Returning first trace state variable definition");
+
+ if (first_marker ())
+ response_ust_marker (packet, first_marker ());
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional trace state variable definitions. */
+
+static void
+cmd_qtsstm (char *packet)
+{
+ trace_debug ("Returning static tracepoint");
+
+ if (next_st)
+ response_ust_marker (packet, next_st);
+ else
+ strcpy (packet, "l");
+}
+
+/* Disconnect the GDB probe from a marker at a given address. */
+
+static void
+unprobe_marker_at (char *packet)
+{
+ char *p = packet;
+ ULONGEST address;
+ struct marker_iter iter;
+
+ p += sizeof ("unprobe_marker_at:") - 1;
+
+ p = unpack_varlen_hex (p, &address);
+
+ USTF(marker_iter_reset) (&iter);
+ USTF(marker_iter_start) (&iter);
+ for (; iter.marker != NULL; USTF(marker_iter_next) (&iter))
+ if ((uintptr_t ) iter.marker->location == address)
+ {
+ int result;
+
+ result = USTF(ltt_marker_disconnect) (iter.marker->channel,
+ iter.marker->name,
+ GDB_PROBE_NAME);
+ if (result < 0)
+ warning ("could not disable marker %s/%s",
+ iter.marker->channel, iter.marker->name);
+ break;
+ }
+}
+
+/* Connect the GDB probe to a marker at a given address. */
+
+static int
+probe_marker_at (char *packet)
+{
+ char *p = packet;
+ ULONGEST address;
+ struct marker_iter iter;
+ struct marker *m;
+
+ p += sizeof ("probe_marker_at:") - 1;
+
+ p = unpack_varlen_hex (p, &address);
+
+ USTF(marker_iter_reset) (&iter);
+
+ for (USTF(marker_iter_start) (&iter), m = iter.marker;
+ m != NULL;
+ USTF(marker_iter_next) (&iter), m = iter.marker)
+ if ((uintptr_t ) m->location == address)
+ {
+ int result;
+
+ trace_debug ("found marker for address. "
+ "ltt_marker_connect (marker = %s/%s)",
+ m->channel, m->name);
+
+ result = USTF(ltt_marker_connect) (m->channel, m->name,
+ GDB_PROBE_NAME);
+ if (result && result != -EEXIST)
+ trace_debug ("ltt_marker_connect (marker = %s/%s, errno = %d)",
+ m->channel, m->name, -result);
+
+ if (result < 0)
+ {
+ sprintf (packet, "E.could not connect marker: channel=%s, name=%s",
+ m->channel, m->name);
+ return -1;
+ }
+
+ strcpy (packet, "OK");
+ return 0;
+ }
+
+ sprintf (packet, "E.no marker found at 0x%s", paddress (address));
+ return -1;
+}
+
+static int
+cmd_qtstmat (char *packet)
+{
+ char *p = packet;
+ ULONGEST address;
+ struct marker_iter iter;
+ struct marker *m;
+
+ p += sizeof ("qTSTMat:") - 1;
+
+ p = unpack_varlen_hex (p, &address);
+
+ USTF(marker_iter_reset) (&iter);
+
+ for (USTF(marker_iter_start) (&iter), m = iter.marker;
+ m != NULL;
+ USTF(marker_iter_next) (&iter), m = iter.marker)
+ if ((uintptr_t ) m->location == address)
+ {
+ response_ust_marker (packet, m);
+ return 0;
+ }
+
+ strcpy (packet, "l");
+ return -1;
+}
+
+static void
+gdb_ust_init (void)
+{
+ if (!dlsym_ust ())
+ return;
+
+ USTF(ltt_probe_register) (&gdb_ust_probe);
+}
+
+#endif /* HAVE_UST */
+
+#include <sys/syscall.h>
+
+static void
+gdb_agent_remove_socket (void)
+{
+ unlink (agent_socket_name);
+}
+
+/* Helper thread of agent. */
+
+static void *
+gdb_agent_helper_thread (void *arg)
+{
+ int listen_fd;
+
+ atexit (gdb_agent_remove_socket);
+
+ while (1)
+ {
+ listen_fd = gdb_agent_socket_init ();
+
+ if (helper_thread_id == 0)
+ helper_thread_id = syscall (SYS_gettid);
+
+ if (listen_fd == -1)
+ {
+ warning ("could not create sync socket");
+ break;
+ }
+
+ while (1)
+ {
+ socklen_t tmp;
+ struct sockaddr_un sockaddr;
+ int fd;
+ char buf[1];
+ int ret;
+ int stop_loop = 0;
+
+ tmp = sizeof (sockaddr);
+
+ do
+ {
+ fd = accept (listen_fd, (struct sockaddr *) &sockaddr, &tmp);
+ }
+ /* It seems an ERESTARTSYS can escape out of accept. */
+ while (fd == -512 || (fd == -1 && errno == EINTR));
+
+ if (fd < 0)
+ {
+ warning ("Accept returned %d, error: %s",
+ fd, safe_strerror (errno));
+ break;
+ }
+
+ do
+ {
+ ret = read (fd, buf, 1);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1)
+ {
+ warning ("reading socket (fd=%d) failed with %s",
+ fd, safe_strerror (errno));
+ close (fd);
+ break;
+ }
+
+ if (cmd_buf[0])
+ {
+ if (startswith (cmd_buf, "close"))
+ {
+ stop_loop = 1;
+ }
+#ifdef HAVE_UST
+ else if (strcmp ("qTfSTM", cmd_buf) == 0)
+ {
+ cmd_qtfstm (cmd_buf);
+ }
+ else if (strcmp ("qTsSTM", cmd_buf) == 0)
+ {
+ cmd_qtsstm (cmd_buf);
+ }
+ else if (startswith (cmd_buf, "unprobe_marker_at:"))
+ {
+ unprobe_marker_at (cmd_buf);
+ }
+ else if (startswith (cmd_buf, "probe_marker_at:"))
+ {
+ probe_marker_at (cmd_buf);
+ }
+ else if (startswith (cmd_buf, "qTSTMat:"))
+ {
+ cmd_qtstmat (cmd_buf);
+ }
+#endif /* HAVE_UST */
+ }
+
+ /* Fix compiler's warning: ignoring return value of 'write'. */
+ ret = write (fd, buf, 1);
+ close (fd);
+
+ if (stop_loop)
+ {
+ close (listen_fd);
+ unlink (agent_socket_name);
+
+ /* Sleep endlessly to wait the whole inferior stops. This
+ thread can not exit because GDB or GDBserver may still need
+ 'current_thread' (representing this thread) to access
+ inferior memory. Otherwise, this thread exits earlier than
+ other threads, and 'current_thread' is set to NULL. */
+ while (1)
+ sleep (10);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#include <signal.h>
+#include <pthread.h>
+
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR int gdb_agent_capability = AGENT_CAPA_STATIC_TRACE;
+EXTERN_C_POP
+
+static void
+gdb_agent_init (void)
+{
+ int res;
+ pthread_t thread;
+ sigset_t new_mask;
+ sigset_t orig_mask;
+
+ /* We want the helper thread to be as transparent as possible, so
+ have it inherit an all-signals-blocked mask. */
+
+ sigfillset (&new_mask);
+ res = pthread_sigmask (SIG_SETMASK, &new_mask, &orig_mask);
+ if (res)
+ perror_with_name ("pthread_sigmask (1)");
+
+ res = pthread_create (&thread,
+ NULL,
+ gdb_agent_helper_thread,
+ NULL);
+
+ res = pthread_sigmask (SIG_SETMASK, &orig_mask, NULL);
+ if (res)
+ perror_with_name ("pthread_sigmask (2)");
+
+ while (helper_thread_id == 0)
+ usleep (1);
+
+#ifdef HAVE_UST
+ gdb_ust_init ();
+#endif
+}
+
+#include <sys/mman.h>
+
+IP_AGENT_EXPORT_VAR char *gdb_tp_heap_buffer;
+IP_AGENT_EXPORT_VAR char *gdb_jump_pad_buffer;
+IP_AGENT_EXPORT_VAR char *gdb_jump_pad_buffer_end;
+IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer;
+IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer_end;
+IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer_error;
+
+/* Record the result of getting buffer space for fast tracepoint
+ trampolines. Any error message is copied, since caller may not be
+ using persistent storage. */
+
+void
+set_trampoline_buffer_space (CORE_ADDR begin, CORE_ADDR end, char *errmsg)
+{
+ gdb_trampoline_buffer = (char *) (uintptr_t) begin;
+ gdb_trampoline_buffer_end = (char *) (uintptr_t) end;
+ if (errmsg)
+ strncpy (gdb_trampoline_buffer_error, errmsg, 99);
+ else
+ strcpy (gdb_trampoline_buffer_error, "no buffer passed");
+}
+
+static void __attribute__ ((constructor))
+initialize_tracepoint_ftlib (void)
+{
+ initialize_tracepoint ();
+
+ gdb_agent_init ();
+}
+
+#ifndef HAVE_GETAUXVAL
+/* Retrieve the value of TYPE from the auxiliary vector. If TYPE is not
+ found, 0 is returned. This function is provided if glibc is too old. */
+
+unsigned long
+getauxval (unsigned long type)
+{
+ unsigned long data[2];
+ FILE *f = fopen ("/proc/self/auxv", "r");
+ unsigned long value = 0;
+
+ if (f == NULL)
+ return 0;
+
+ while (fread (data, sizeof (data), 1, f) > 0)
+ {
+ if (data[0] == type)
+ {
+ value = data[1];
+ break;
+ }
+ }
+
+ fclose (f);
+ return value;
+}
+#endif
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Return a timestamp, expressed as microseconds of the usual Unix
+ time. (As the result is a 64-bit number, it will not overflow any
+ time soon.) */
+
+static LONGEST
+get_timestamp (void)
+{
+ using namespace std::chrono;
+
+ steady_clock::time_point now = steady_clock::now ();
+ return duration_cast<microseconds> (now.time_since_epoch ()).count ();
+}
+
+void
+initialize_tracepoint (void)
+{
+ /* Start with the default size. */
+ init_trace_buffer (DEFAULT_TRACE_BUFFER_SIZE);
+
+ /* Wire trace state variable 1 to be the timestamp. This will be
+ uploaded to GDB upon connection and become one of its trace state
+ variables. (In case you're wondering, if GDB already has a trace
+ variable numbered 1, it will be renumbered.) */
+ create_trace_state_variable (1, 0);
+ set_trace_state_variable_name (1, "trace_timestamp");
+ set_trace_state_variable_getter (1, get_timestamp);
+
+#ifdef IN_PROCESS_AGENT
+ {
+ int pagesize;
+ size_t jump_pad_size;
+
+ pagesize = sysconf (_SC_PAGE_SIZE);
+ if (pagesize == -1)
+ perror_with_name ("sysconf");
+
+#define SCRATCH_BUFFER_NPAGES 20
+
+ jump_pad_size = pagesize * SCRATCH_BUFFER_NPAGES;
+
+ gdb_tp_heap_buffer = (char *) xmalloc (5 * 1024 * 1024);
+ gdb_jump_pad_buffer = (char *) alloc_jump_pad_buffer (jump_pad_size);
+ if (gdb_jump_pad_buffer == NULL)
+ perror_with_name ("mmap");
+ gdb_jump_pad_buffer_end = gdb_jump_pad_buffer + jump_pad_size;
+ }
+
+ gdb_trampoline_buffer = gdb_trampoline_buffer_end = 0;
+
+ /* It's not a fatal error for something to go wrong with trampoline
+ buffer setup, but it can be mysterious, so create a channel to
+ report back on what went wrong, using a fixed size since we may
+ not be able to allocate space later when the problem occurs. */
+ gdb_trampoline_buffer_error = (char *) xmalloc (IPA_BUFSIZ);
+
+ strcpy (gdb_trampoline_buffer_error, "No errors reported");
+
+ initialize_low_tracepoint ();
+#endif
+}
+++ /dev/null
-/* General utility routines for the remote server for GDB.
- Copyright (C) 1986-2020 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"
-
-#ifdef IN_PROCESS_AGENT
-# define PREFIX "ipa: "
-# define TOOLNAME "GDBserver in-process agent"
-#else
-# define PREFIX "gdbserver: "
-# define TOOLNAME "GDBserver"
-#endif
-
-/* Generally useful subroutines used throughout the program. */
-
-void
-malloc_failure (long size)
-{
- fprintf (stderr,
- PREFIX "ran out of memory while trying to allocate %lu bytes\n",
- (unsigned long) size);
- exit (1);
-}
-
-/* Print the system error message for errno, and also mention STRING
- as the file name for which the error was encountered.
- Then return to command level. */
-
-void
-perror_with_name (const char *string)
-{
- const char *err;
- char *combined;
-
- err = safe_strerror (errno);
- if (err == NULL)
- err = "unknown error";
-
- combined = (char *) alloca (strlen (err) + strlen (string) + 3);
- strcpy (combined, string);
- strcat (combined, ": ");
- strcat (combined, err);
-
- error ("%s.", combined);
-}
-
-/* Print an error message and return to top level. */
-
-void
-verror (const char *string, va_list args)
-{
-#ifdef IN_PROCESS_AGENT
- fflush (stdout);
- vfprintf (stderr, string, args);
- fprintf (stderr, "\n");
- exit (1);
-#else
- throw_verror (GENERIC_ERROR, string, args);
-#endif
-}
-
-void
-vwarning (const char *string, va_list args)
-{
- fprintf (stderr, PREFIX);
- vfprintf (stderr, string, args);
- fprintf (stderr, "\n");
-}
-
-/* Report a problem internal to GDBserver, and exit. */
-
-void
-internal_verror (const char *file, int line, const char *fmt, va_list args)
-{
- fprintf (stderr, "\
-%s:%d: A problem internal to " TOOLNAME " has been detected.\n", file, line);
- vfprintf (stderr, fmt, args);
- fprintf (stderr, "\n");
- exit (1);
-}
-
-/* Report a problem internal to GDBserver. */
-
-void
-internal_vwarning (const char *file, int line, const char *fmt, va_list args)
-{
- fprintf (stderr, "\
-%s:%d: A problem internal to " TOOLNAME " has been detected.\n", file, line);
- vfprintf (stderr, fmt, args);
- fprintf (stderr, "\n");
-}
-
-/* Convert a CORE_ADDR into a HEX string, like %lx.
- The result is stored in a circular static buffer, NUMCELLS deep. */
-
-char *
-paddress (CORE_ADDR addr)
-{
- return phex_nz (addr, sizeof (CORE_ADDR));
-}
-
-/* Convert a file descriptor into a printable string. */
-
-char *
-pfildes (gdb_fildes_t fd)
-{
-#if USE_WIN32API
- return phex_nz (fd, sizeof (gdb_fildes_t));
-#else
- return plongest (fd);
-#endif
-}
--- /dev/null
+/* General utility routines for the remote server for GDB.
+ Copyright (C) 1986-2020 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"
+
+#ifdef IN_PROCESS_AGENT
+# define PREFIX "ipa: "
+# define TOOLNAME "GDBserver in-process agent"
+#else
+# define PREFIX "gdbserver: "
+# define TOOLNAME "GDBserver"
+#endif
+
+/* Generally useful subroutines used throughout the program. */
+
+void
+malloc_failure (long size)
+{
+ fprintf (stderr,
+ PREFIX "ran out of memory while trying to allocate %lu bytes\n",
+ (unsigned long) size);
+ exit (1);
+}
+
+/* Print the system error message for errno, and also mention STRING
+ as the file name for which the error was encountered.
+ Then return to command level. */
+
+void
+perror_with_name (const char *string)
+{
+ const char *err;
+ char *combined;
+
+ err = safe_strerror (errno);
+ if (err == NULL)
+ err = "unknown error";
+
+ combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+ strcpy (combined, string);
+ strcat (combined, ": ");
+ strcat (combined, err);
+
+ error ("%s.", combined);
+}
+
+/* Print an error message and return to top level. */
+
+void
+verror (const char *string, va_list args)
+{
+#ifdef IN_PROCESS_AGENT
+ fflush (stdout);
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ exit (1);
+#else
+ throw_verror (GENERIC_ERROR, string, args);
+#endif
+}
+
+void
+vwarning (const char *string, va_list args)
+{
+ fprintf (stderr, PREFIX);
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+}
+
+/* Report a problem internal to GDBserver, and exit. */
+
+void
+internal_verror (const char *file, int line, const char *fmt, va_list args)
+{
+ fprintf (stderr, "\
+%s:%d: A problem internal to " TOOLNAME " has been detected.\n", file, line);
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, "\n");
+ exit (1);
+}
+
+/* Report a problem internal to GDBserver. */
+
+void
+internal_vwarning (const char *file, int line, const char *fmt, va_list args)
+{
+ fprintf (stderr, "\
+%s:%d: A problem internal to " TOOLNAME " has been detected.\n", file, line);
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, "\n");
+}
+
+/* Convert a CORE_ADDR into a HEX string, like %lx.
+ The result is stored in a circular static buffer, NUMCELLS deep. */
+
+char *
+paddress (CORE_ADDR addr)
+{
+ return phex_nz (addr, sizeof (CORE_ADDR));
+}
+
+/* Convert a file descriptor into a printable string. */
+
+char *
+pfildes (gdb_fildes_t fd)
+{
+#if USE_WIN32API
+ return phex_nz (fd, sizeof (gdb_fildes_t));
+#else
+ return plongest (fd);
+#endif
+}
+++ /dev/null
-/* Copyright (C) 2007-2020 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 "win32-low.h"
-
-#ifndef CONTEXT_FLOATING_POINT
-#define CONTEXT_FLOATING_POINT 0
-#endif
-
-/* Defined in auto-generated file reg-arm.c. */
-void init_registers_arm (void);
-extern const struct target_desc *tdesc_arm;
-
-static void
-arm_get_thread_context (win32_thread_info *th)
-{
- th->context.ContextFlags = \
- CONTEXT_FULL | \
- CONTEXT_FLOATING_POINT;
-
- GetThreadContext (th->h, &th->context);
-}
-
-#define context_offset(x) ((int)&(((CONTEXT *)NULL)->x))
-static const int mappings[] = {
- context_offset (R0),
- context_offset (R1),
- context_offset (R2),
- context_offset (R3),
- context_offset (R4),
- context_offset (R5),
- context_offset (R6),
- context_offset (R7),
- context_offset (R8),
- context_offset (R9),
- context_offset (R10),
- context_offset (R11),
- context_offset (R12),
- context_offset (Sp),
- context_offset (Lr),
- context_offset (Pc),
- -1, /* f0 */
- -1, /* f1 */
- -1, /* f2 */
- -1, /* f3 */
- -1, /* f4 */
- -1, /* f5 */
- -1, /* f6 */
- -1, /* f7 */
- -1, /* fps */
- context_offset (Psr),
-};
-#undef context_offset
-
-/* Return a pointer into a CONTEXT field indexed by gdb register number.
- Return a pointer to an dummy register holding zero if there is no
- corresponding CONTEXT field for the given register number. */
-static char *
-regptr (CONTEXT* c, int r)
-{
- if (mappings[r] < 0)
- {
- static ULONG zero;
- /* Always force value to zero, in case the user tried to write
- to this register before. */
- zero = 0;
- return (char *) &zero;
- }
- else
- return (char *) c + mappings[r];
-}
-
-/* Fetch register from gdbserver regcache data. */
-static void
-arm_fetch_inferior_register (struct regcache *regcache,
- win32_thread_info *th, int r)
-{
- char *context_offset = regptr (&th->context, r);
- supply_register (regcache, r, context_offset);
-}
-
-/* Store a new register value into the thread context of TH. */
-static void
-arm_store_inferior_register (struct regcache *regcache,
- win32_thread_info *th, int r)
-{
- collect_register (regcache, r, regptr (&th->context, r));
-}
-
-static void
-arm_arch_setup (void)
-{
- init_registers_arm ();
- win32_tdesc = tdesc_arm;
-}
-
-/* Correct in either endianness. We do not support Thumb yet. */
-static const unsigned long arm_wince_breakpoint = 0xe6000010;
-#define arm_wince_breakpoint_len 4
-
-struct win32_target_ops the_low_target = {
- arm_arch_setup,
- sizeof (mappings) / sizeof (mappings[0]),
- NULL, /* initial_stuff */
- arm_get_thread_context,
- NULL, /* prepare_to_resume */
- NULL, /* thread_added */
- arm_fetch_inferior_register,
- arm_store_inferior_register,
- NULL, /* single_step */
- (const unsigned char *) &arm_wince_breakpoint,
- arm_wince_breakpoint_len,
- /* Watchpoint related functions. See target.h for comments. */
- NULL, /* supports_z_point_type */
- NULL, /* insert_point */
- NULL, /* remove_point */
- NULL, /* stopped_by_watchpoint */
- NULL /* stopped_data_address */
-};
--- /dev/null
+/* Copyright (C) 2007-2020 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 "win32-low.h"
+
+#ifndef CONTEXT_FLOATING_POINT
+#define CONTEXT_FLOATING_POINT 0
+#endif
+
+/* Defined in auto-generated file reg-arm.c. */
+void init_registers_arm (void);
+extern const struct target_desc *tdesc_arm;
+
+static void
+arm_get_thread_context (win32_thread_info *th)
+{
+ th->context.ContextFlags = \
+ CONTEXT_FULL | \
+ CONTEXT_FLOATING_POINT;
+
+ GetThreadContext (th->h, &th->context);
+}
+
+#define context_offset(x) ((int)&(((CONTEXT *)NULL)->x))
+static const int mappings[] = {
+ context_offset (R0),
+ context_offset (R1),
+ context_offset (R2),
+ context_offset (R3),
+ context_offset (R4),
+ context_offset (R5),
+ context_offset (R6),
+ context_offset (R7),
+ context_offset (R8),
+ context_offset (R9),
+ context_offset (R10),
+ context_offset (R11),
+ context_offset (R12),
+ context_offset (Sp),
+ context_offset (Lr),
+ context_offset (Pc),
+ -1, /* f0 */
+ -1, /* f1 */
+ -1, /* f2 */
+ -1, /* f3 */
+ -1, /* f4 */
+ -1, /* f5 */
+ -1, /* f6 */
+ -1, /* f7 */
+ -1, /* fps */
+ context_offset (Psr),
+};
+#undef context_offset
+
+/* Return a pointer into a CONTEXT field indexed by gdb register number.
+ Return a pointer to an dummy register holding zero if there is no
+ corresponding CONTEXT field for the given register number. */
+static char *
+regptr (CONTEXT* c, int r)
+{
+ if (mappings[r] < 0)
+ {
+ static ULONG zero;
+ /* Always force value to zero, in case the user tried to write
+ to this register before. */
+ zero = 0;
+ return (char *) &zero;
+ }
+ else
+ return (char *) c + mappings[r];
+}
+
+/* Fetch register from gdbserver regcache data. */
+static void
+arm_fetch_inferior_register (struct regcache *regcache,
+ win32_thread_info *th, int r)
+{
+ char *context_offset = regptr (&th->context, r);
+ supply_register (regcache, r, context_offset);
+}
+
+/* Store a new register value into the thread context of TH. */
+static void
+arm_store_inferior_register (struct regcache *regcache,
+ win32_thread_info *th, int r)
+{
+ collect_register (regcache, r, regptr (&th->context, r));
+}
+
+static void
+arm_arch_setup (void)
+{
+ init_registers_arm ();
+ win32_tdesc = tdesc_arm;
+}
+
+/* Correct in either endianness. We do not support Thumb yet. */
+static const unsigned long arm_wince_breakpoint = 0xe6000010;
+#define arm_wince_breakpoint_len 4
+
+struct win32_target_ops the_low_target = {
+ arm_arch_setup,
+ sizeof (mappings) / sizeof (mappings[0]),
+ NULL, /* initial_stuff */
+ arm_get_thread_context,
+ NULL, /* prepare_to_resume */
+ NULL, /* thread_added */
+ arm_fetch_inferior_register,
+ arm_store_inferior_register,
+ NULL, /* single_step */
+ (const unsigned char *) &arm_wince_breakpoint,
+ arm_wince_breakpoint_len,
+ /* Watchpoint related functions. See target.h for comments. */
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL /* stopped_data_address */
+};
+++ /dev/null
-/* Copyright (C) 2007-2020 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 "win32-low.h"
-#include "x86-low.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#endif
-#include "arch/i386.h"
-#include "tdesc.h"
-#include "x86-tdesc.h"
-
-#ifndef CONTEXT_EXTENDED_REGISTERS
-#define CONTEXT_EXTENDED_REGISTERS 0
-#endif
-
-#define FCS_REGNUM 27
-#define FOP_REGNUM 31
-
-#define FLAG_TRACE_BIT 0x100
-
-static struct x86_debug_reg_state debug_reg_state;
-
-static void
-update_debug_registers (thread_info *thread)
-{
- win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
-
- /* The actual update is done later just before resuming the lwp,
- we just mark that the registers need updating. */
- th->debug_registers_changed = 1;
-}
-
-/* Update the inferior's debug register REGNUM from STATE. */
-
-static void
-x86_dr_low_set_addr (int regnum, CORE_ADDR addr)
-{
- gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
-
- /* Only update the threads of this process. */
- for_each_thread (current_thread->id.pid (), update_debug_registers);
-}
-
-/* Update the inferior's DR7 debug control register from STATE. */
-
-static void
-x86_dr_low_set_control (unsigned long control)
-{
- /* Only update the threads of this process. */
- for_each_thread (current_thread->id.pid (), update_debug_registers);
-}
-
-/* Return the current value of a DR register of the current thread's
- context. */
-
-static DWORD64
-win32_get_current_dr (int dr)
-{
- win32_thread_info *th
- = (win32_thread_info *) thread_target_data (current_thread);
-
- win32_require_context (th);
-
-#define RET_DR(DR) \
- case DR: \
- return th->context.Dr ## DR
-
- switch (dr)
- {
- RET_DR (0);
- RET_DR (1);
- RET_DR (2);
- RET_DR (3);
- RET_DR (6);
- RET_DR (7);
- }
-
-#undef RET_DR
-
- gdb_assert_not_reached ("unhandled dr");
-}
-
-static CORE_ADDR
-x86_dr_low_get_addr (int regnum)
-{
- gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
-
- return win32_get_current_dr (regnum - DR_FIRSTADDR);
-}
-
-static unsigned long
-x86_dr_low_get_control (void)
-{
- return win32_get_current_dr (7);
-}
-
-/* Get the value of the DR6 debug status register from the inferior
- and record it in STATE. */
-
-static unsigned long
-x86_dr_low_get_status (void)
-{
- return win32_get_current_dr (6);
-}
-
-/* Low-level function vector. */
-struct x86_dr_low_type x86_dr_low =
- {
- x86_dr_low_set_control,
- x86_dr_low_set_addr,
- x86_dr_low_get_addr,
- x86_dr_low_get_status,
- x86_dr_low_get_control,
- sizeof (void *),
- };
-
-/* Breakpoint/watchpoint support. */
-
-static int
-i386_supports_z_point_type (char z_type)
-{
- switch (z_type)
- {
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
- return 0;
- }
-}
-
-static int
-i386_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- switch (type)
- {
- case raw_bkpt_type_write_wp:
- case raw_bkpt_type_access_wp:
- {
- enum target_hw_bp_type hw_type
- = raw_bkpt_type_to_target_hw_bp_type (type);
-
- return x86_dr_insert_watchpoint (&debug_reg_state,
- hw_type, addr, size);
- }
- default:
- /* Unsupported. */
- return 1;
- }
-}
-
-static int
-i386_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- switch (type)
- {
- case raw_bkpt_type_write_wp:
- case raw_bkpt_type_access_wp:
- {
- enum target_hw_bp_type hw_type
- = raw_bkpt_type_to_target_hw_bp_type (type);
-
- return x86_dr_remove_watchpoint (&debug_reg_state,
- hw_type, addr, size);
- }
- default:
- /* Unsupported. */
- return 1;
- }
-}
-
-static int
-x86_stopped_by_watchpoint (void)
-{
- return x86_dr_stopped_by_watchpoint (&debug_reg_state);
-}
-
-static CORE_ADDR
-x86_stopped_data_address (void)
-{
- CORE_ADDR addr;
- if (x86_dr_stopped_data_address (&debug_reg_state, &addr))
- return addr;
- return 0;
-}
-
-static void
-i386_initial_stuff (void)
-{
- x86_low_init_dregs (&debug_reg_state);
-}
-
-static void
-i386_get_thread_context (win32_thread_info *th)
-{
- /* Requesting the CONTEXT_EXTENDED_REGISTERS register set fails if
- the system doesn't support extended registers. */
- static DWORD extended_registers = CONTEXT_EXTENDED_REGISTERS;
-
- again:
- th->context.ContextFlags = (CONTEXT_FULL
- | CONTEXT_FLOATING_POINT
- | CONTEXT_DEBUG_REGISTERS
- | extended_registers);
-
- if (!GetThreadContext (th->h, &th->context))
- {
- DWORD e = GetLastError ();
-
- if (extended_registers && e == ERROR_INVALID_PARAMETER)
- {
- extended_registers = 0;
- goto again;
- }
-
- error ("GetThreadContext failure %ld\n", (long) e);
- }
-}
-
-static void
-i386_prepare_to_resume (win32_thread_info *th)
-{
- if (th->debug_registers_changed)
- {
- struct x86_debug_reg_state *dr = &debug_reg_state;
-
- win32_require_context (th);
-
- th->context.Dr0 = dr->dr_mirror[0];
- th->context.Dr1 = dr->dr_mirror[1];
- th->context.Dr2 = dr->dr_mirror[2];
- th->context.Dr3 = dr->dr_mirror[3];
- /* th->context.Dr6 = dr->dr_status_mirror;
- FIXME: should we set dr6 also ?? */
- th->context.Dr7 = dr->dr_control_mirror;
-
- th->debug_registers_changed = 0;
- }
-}
-
-static void
-i386_thread_added (win32_thread_info *th)
-{
- th->debug_registers_changed = 1;
-}
-
-static void
-i386_single_step (win32_thread_info *th)
-{
- th->context.EFlags |= FLAG_TRACE_BIT;
-}
-
-#ifndef __x86_64__
-
-/* An array of offset mappings into a Win32 Context structure.
- This is a one-to-one mapping which is indexed by gdb's register
- numbers. It retrieves an offset into the context structure where
- the 4 byte register is located.
- An offset value of -1 indicates that Win32 does not provide this
- register in it's CONTEXT structure. In this case regptr will return
- a pointer into a dummy register. */
-#define context_offset(x) ((int)&(((CONTEXT *)NULL)->x))
-static const int mappings[] = {
- context_offset (Eax),
- context_offset (Ecx),
- context_offset (Edx),
- context_offset (Ebx),
- context_offset (Esp),
- context_offset (Ebp),
- context_offset (Esi),
- context_offset (Edi),
- context_offset (Eip),
- context_offset (EFlags),
- context_offset (SegCs),
- context_offset (SegSs),
- context_offset (SegDs),
- context_offset (SegEs),
- context_offset (SegFs),
- context_offset (SegGs),
- context_offset (FloatSave.RegisterArea[0 * 10]),
- context_offset (FloatSave.RegisterArea[1 * 10]),
- context_offset (FloatSave.RegisterArea[2 * 10]),
- context_offset (FloatSave.RegisterArea[3 * 10]),
- context_offset (FloatSave.RegisterArea[4 * 10]),
- context_offset (FloatSave.RegisterArea[5 * 10]),
- context_offset (FloatSave.RegisterArea[6 * 10]),
- context_offset (FloatSave.RegisterArea[7 * 10]),
- context_offset (FloatSave.ControlWord),
- context_offset (FloatSave.StatusWord),
- context_offset (FloatSave.TagWord),
- context_offset (FloatSave.ErrorSelector),
- context_offset (FloatSave.ErrorOffset),
- context_offset (FloatSave.DataSelector),
- context_offset (FloatSave.DataOffset),
- context_offset (FloatSave.ErrorSelector),
- /* XMM0-7 */
- context_offset (ExtendedRegisters[10 * 16]),
- context_offset (ExtendedRegisters[11 * 16]),
- context_offset (ExtendedRegisters[12 * 16]),
- context_offset (ExtendedRegisters[13 * 16]),
- context_offset (ExtendedRegisters[14 * 16]),
- context_offset (ExtendedRegisters[15 * 16]),
- context_offset (ExtendedRegisters[16 * 16]),
- context_offset (ExtendedRegisters[17 * 16]),
- /* MXCSR */
- context_offset (ExtendedRegisters[24])
-};
-#undef context_offset
-
-#else /* __x86_64__ */
-
-#define context_offset(x) (offsetof (CONTEXT, x))
-static const int mappings[] =
-{
- context_offset (Rax),
- context_offset (Rbx),
- context_offset (Rcx),
- context_offset (Rdx),
- context_offset (Rsi),
- context_offset (Rdi),
- context_offset (Rbp),
- context_offset (Rsp),
- context_offset (R8),
- context_offset (R9),
- context_offset (R10),
- context_offset (R11),
- context_offset (R12),
- context_offset (R13),
- context_offset (R14),
- context_offset (R15),
- context_offset (Rip),
- context_offset (EFlags),
- context_offset (SegCs),
- context_offset (SegSs),
- context_offset (SegDs),
- context_offset (SegEs),
- context_offset (SegFs),
- context_offset (SegGs),
- context_offset (FloatSave.FloatRegisters[0]),
- context_offset (FloatSave.FloatRegisters[1]),
- context_offset (FloatSave.FloatRegisters[2]),
- context_offset (FloatSave.FloatRegisters[3]),
- context_offset (FloatSave.FloatRegisters[4]),
- context_offset (FloatSave.FloatRegisters[5]),
- context_offset (FloatSave.FloatRegisters[6]),
- context_offset (FloatSave.FloatRegisters[7]),
- context_offset (FloatSave.ControlWord),
- context_offset (FloatSave.StatusWord),
- context_offset (FloatSave.TagWord),
- context_offset (FloatSave.ErrorSelector),
- context_offset (FloatSave.ErrorOffset),
- context_offset (FloatSave.DataSelector),
- context_offset (FloatSave.DataOffset),
- context_offset (FloatSave.ErrorSelector)
- /* XMM0-7 */ ,
- context_offset (Xmm0),
- context_offset (Xmm1),
- context_offset (Xmm2),
- context_offset (Xmm3),
- context_offset (Xmm4),
- context_offset (Xmm5),
- context_offset (Xmm6),
- context_offset (Xmm7),
- context_offset (Xmm8),
- context_offset (Xmm9),
- context_offset (Xmm10),
- context_offset (Xmm11),
- context_offset (Xmm12),
- context_offset (Xmm13),
- context_offset (Xmm14),
- context_offset (Xmm15),
- /* MXCSR */
- context_offset (FloatSave.MxCsr)
-};
-#undef context_offset
-
-#endif /* __x86_64__ */
-
-/* Fetch register from gdbserver regcache data. */
-static void
-i386_fetch_inferior_register (struct regcache *regcache,
- win32_thread_info *th, int r)
-{
- char *context_offset = (char *) &th->context + mappings[r];
-
- long l;
- if (r == FCS_REGNUM)
- {
- l = *((long *) context_offset) & 0xffff;
- supply_register (regcache, r, (char *) &l);
- }
- else if (r == FOP_REGNUM)
- {
- l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1);
- supply_register (regcache, r, (char *) &l);
- }
- else
- supply_register (regcache, r, context_offset);
-}
-
-/* Store a new register value into the thread context of TH. */
-static void
-i386_store_inferior_register (struct regcache *regcache,
- win32_thread_info *th, int r)
-{
- char *context_offset = (char *) &th->context + mappings[r];
- collect_register (regcache, r, context_offset);
-}
-
-static const unsigned char i386_win32_breakpoint = 0xcc;
-#define i386_win32_breakpoint_len 1
-
-static void
-i386_arch_setup (void)
-{
- struct target_desc *tdesc;
-
-#ifdef __x86_64__
- tdesc = amd64_create_target_description (X86_XSTATE_SSE_MASK, false,
- false, false);
- const char **expedite_regs = amd64_expedite_regs;
-#else
- tdesc = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
- const char **expedite_regs = i386_expedite_regs;
-#endif
-
- init_target_desc (tdesc, expedite_regs);
-
- win32_tdesc = tdesc;
-}
-
-struct win32_target_ops the_low_target = {
- i386_arch_setup,
- sizeof (mappings) / sizeof (mappings[0]),
- i386_initial_stuff,
- i386_get_thread_context,
- i386_prepare_to_resume,
- i386_thread_added,
- i386_fetch_inferior_register,
- i386_store_inferior_register,
- i386_single_step,
- &i386_win32_breakpoint,
- i386_win32_breakpoint_len,
- i386_supports_z_point_type,
- i386_insert_point,
- i386_remove_point,
- x86_stopped_by_watchpoint,
- x86_stopped_data_address
-};
--- /dev/null
+/* Copyright (C) 2007-2020 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 "win32-low.h"
+#include "x86-low.h"
+#include "gdbsupport/x86-xstate.h"
+#ifdef __x86_64__
+#include "arch/amd64.h"
+#endif
+#include "arch/i386.h"
+#include "tdesc.h"
+#include "x86-tdesc.h"
+
+#ifndef CONTEXT_EXTENDED_REGISTERS
+#define CONTEXT_EXTENDED_REGISTERS 0
+#endif
+
+#define FCS_REGNUM 27
+#define FOP_REGNUM 31
+
+#define FLAG_TRACE_BIT 0x100
+
+static struct x86_debug_reg_state debug_reg_state;
+
+static void
+update_debug_registers (thread_info *thread)
+{
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+
+ /* The actual update is done later just before resuming the lwp,
+ we just mark that the registers need updating. */
+ th->debug_registers_changed = 1;
+}
+
+/* Update the inferior's debug register REGNUM from STATE. */
+
+static void
+x86_dr_low_set_addr (int regnum, CORE_ADDR addr)
+{
+ gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
+
+ /* Only update the threads of this process. */
+ for_each_thread (current_thread->id.pid (), update_debug_registers);
+}
+
+/* Update the inferior's DR7 debug control register from STATE. */
+
+static void
+x86_dr_low_set_control (unsigned long control)
+{
+ /* Only update the threads of this process. */
+ for_each_thread (current_thread->id.pid (), update_debug_registers);
+}
+
+/* Return the current value of a DR register of the current thread's
+ context. */
+
+static DWORD64
+win32_get_current_dr (int dr)
+{
+ win32_thread_info *th
+ = (win32_thread_info *) thread_target_data (current_thread);
+
+ win32_require_context (th);
+
+#define RET_DR(DR) \
+ case DR: \
+ return th->context.Dr ## DR
+
+ switch (dr)
+ {
+ RET_DR (0);
+ RET_DR (1);
+ RET_DR (2);
+ RET_DR (3);
+ RET_DR (6);
+ RET_DR (7);
+ }
+
+#undef RET_DR
+
+ gdb_assert_not_reached ("unhandled dr");
+}
+
+static CORE_ADDR
+x86_dr_low_get_addr (int regnum)
+{
+ gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
+
+ return win32_get_current_dr (regnum - DR_FIRSTADDR);
+}
+
+static unsigned long
+x86_dr_low_get_control (void)
+{
+ return win32_get_current_dr (7);
+}
+
+/* Get the value of the DR6 debug status register from the inferior
+ and record it in STATE. */
+
+static unsigned long
+x86_dr_low_get_status (void)
+{
+ return win32_get_current_dr (6);
+}
+
+/* Low-level function vector. */
+struct x86_dr_low_type x86_dr_low =
+ {
+ x86_dr_low_set_control,
+ x86_dr_low_set_addr,
+ x86_dr_low_get_addr,
+ x86_dr_low_get_status,
+ x86_dr_low_get_control,
+ sizeof (void *),
+ };
+
+/* Breakpoint/watchpoint support. */
+
+static int
+i386_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+i386_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ switch (type)
+ {
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ {
+ enum target_hw_bp_type hw_type
+ = raw_bkpt_type_to_target_hw_bp_type (type);
+
+ return x86_dr_insert_watchpoint (&debug_reg_state,
+ hw_type, addr, size);
+ }
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+static int
+i386_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ switch (type)
+ {
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ {
+ enum target_hw_bp_type hw_type
+ = raw_bkpt_type_to_target_hw_bp_type (type);
+
+ return x86_dr_remove_watchpoint (&debug_reg_state,
+ hw_type, addr, size);
+ }
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+static int
+x86_stopped_by_watchpoint (void)
+{
+ return x86_dr_stopped_by_watchpoint (&debug_reg_state);
+}
+
+static CORE_ADDR
+x86_stopped_data_address (void)
+{
+ CORE_ADDR addr;
+ if (x86_dr_stopped_data_address (&debug_reg_state, &addr))
+ return addr;
+ return 0;
+}
+
+static void
+i386_initial_stuff (void)
+{
+ x86_low_init_dregs (&debug_reg_state);
+}
+
+static void
+i386_get_thread_context (win32_thread_info *th)
+{
+ /* Requesting the CONTEXT_EXTENDED_REGISTERS register set fails if
+ the system doesn't support extended registers. */
+ static DWORD extended_registers = CONTEXT_EXTENDED_REGISTERS;
+
+ again:
+ th->context.ContextFlags = (CONTEXT_FULL
+ | CONTEXT_FLOATING_POINT
+ | CONTEXT_DEBUG_REGISTERS
+ | extended_registers);
+
+ if (!GetThreadContext (th->h, &th->context))
+ {
+ DWORD e = GetLastError ();
+
+ if (extended_registers && e == ERROR_INVALID_PARAMETER)
+ {
+ extended_registers = 0;
+ goto again;
+ }
+
+ error ("GetThreadContext failure %ld\n", (long) e);
+ }
+}
+
+static void
+i386_prepare_to_resume (win32_thread_info *th)
+{
+ if (th->debug_registers_changed)
+ {
+ struct x86_debug_reg_state *dr = &debug_reg_state;
+
+ win32_require_context (th);
+
+ th->context.Dr0 = dr->dr_mirror[0];
+ th->context.Dr1 = dr->dr_mirror[1];
+ th->context.Dr2 = dr->dr_mirror[2];
+ th->context.Dr3 = dr->dr_mirror[3];
+ /* th->context.Dr6 = dr->dr_status_mirror;
+ FIXME: should we set dr6 also ?? */
+ th->context.Dr7 = dr->dr_control_mirror;
+
+ th->debug_registers_changed = 0;
+ }
+}
+
+static void
+i386_thread_added (win32_thread_info *th)
+{
+ th->debug_registers_changed = 1;
+}
+
+static void
+i386_single_step (win32_thread_info *th)
+{
+ th->context.EFlags |= FLAG_TRACE_BIT;
+}
+
+#ifndef __x86_64__
+
+/* An array of offset mappings into a Win32 Context structure.
+ This is a one-to-one mapping which is indexed by gdb's register
+ numbers. It retrieves an offset into the context structure where
+ the 4 byte register is located.
+ An offset value of -1 indicates that Win32 does not provide this
+ register in it's CONTEXT structure. In this case regptr will return
+ a pointer into a dummy register. */
+#define context_offset(x) ((int)&(((CONTEXT *)NULL)->x))
+static const int mappings[] = {
+ context_offset (Eax),
+ context_offset (Ecx),
+ context_offset (Edx),
+ context_offset (Ebx),
+ context_offset (Esp),
+ context_offset (Ebp),
+ context_offset (Esi),
+ context_offset (Edi),
+ context_offset (Eip),
+ context_offset (EFlags),
+ context_offset (SegCs),
+ context_offset (SegSs),
+ context_offset (SegDs),
+ context_offset (SegEs),
+ context_offset (SegFs),
+ context_offset (SegGs),
+ context_offset (FloatSave.RegisterArea[0 * 10]),
+ context_offset (FloatSave.RegisterArea[1 * 10]),
+ context_offset (FloatSave.RegisterArea[2 * 10]),
+ context_offset (FloatSave.RegisterArea[3 * 10]),
+ context_offset (FloatSave.RegisterArea[4 * 10]),
+ context_offset (FloatSave.RegisterArea[5 * 10]),
+ context_offset (FloatSave.RegisterArea[6 * 10]),
+ context_offset (FloatSave.RegisterArea[7 * 10]),
+ context_offset (FloatSave.ControlWord),
+ context_offset (FloatSave.StatusWord),
+ context_offset (FloatSave.TagWord),
+ context_offset (FloatSave.ErrorSelector),
+ context_offset (FloatSave.ErrorOffset),
+ context_offset (FloatSave.DataSelector),
+ context_offset (FloatSave.DataOffset),
+ context_offset (FloatSave.ErrorSelector),
+ /* XMM0-7 */
+ context_offset (ExtendedRegisters[10 * 16]),
+ context_offset (ExtendedRegisters[11 * 16]),
+ context_offset (ExtendedRegisters[12 * 16]),
+ context_offset (ExtendedRegisters[13 * 16]),
+ context_offset (ExtendedRegisters[14 * 16]),
+ context_offset (ExtendedRegisters[15 * 16]),
+ context_offset (ExtendedRegisters[16 * 16]),
+ context_offset (ExtendedRegisters[17 * 16]),
+ /* MXCSR */
+ context_offset (ExtendedRegisters[24])
+};
+#undef context_offset
+
+#else /* __x86_64__ */
+
+#define context_offset(x) (offsetof (CONTEXT, x))
+static const int mappings[] =
+{
+ context_offset (Rax),
+ context_offset (Rbx),
+ context_offset (Rcx),
+ context_offset (Rdx),
+ context_offset (Rsi),
+ context_offset (Rdi),
+ context_offset (Rbp),
+ context_offset (Rsp),
+ context_offset (R8),
+ context_offset (R9),
+ context_offset (R10),
+ context_offset (R11),
+ context_offset (R12),
+ context_offset (R13),
+ context_offset (R14),
+ context_offset (R15),
+ context_offset (Rip),
+ context_offset (EFlags),
+ context_offset (SegCs),
+ context_offset (SegSs),
+ context_offset (SegDs),
+ context_offset (SegEs),
+ context_offset (SegFs),
+ context_offset (SegGs),
+ context_offset (FloatSave.FloatRegisters[0]),
+ context_offset (FloatSave.FloatRegisters[1]),
+ context_offset (FloatSave.FloatRegisters[2]),
+ context_offset (FloatSave.FloatRegisters[3]),
+ context_offset (FloatSave.FloatRegisters[4]),
+ context_offset (FloatSave.FloatRegisters[5]),
+ context_offset (FloatSave.FloatRegisters[6]),
+ context_offset (FloatSave.FloatRegisters[7]),
+ context_offset (FloatSave.ControlWord),
+ context_offset (FloatSave.StatusWord),
+ context_offset (FloatSave.TagWord),
+ context_offset (FloatSave.ErrorSelector),
+ context_offset (FloatSave.ErrorOffset),
+ context_offset (FloatSave.DataSelector),
+ context_offset (FloatSave.DataOffset),
+ context_offset (FloatSave.ErrorSelector)
+ /* XMM0-7 */ ,
+ context_offset (Xmm0),
+ context_offset (Xmm1),
+ context_offset (Xmm2),
+ context_offset (Xmm3),
+ context_offset (Xmm4),
+ context_offset (Xmm5),
+ context_offset (Xmm6),
+ context_offset (Xmm7),
+ context_offset (Xmm8),
+ context_offset (Xmm9),
+ context_offset (Xmm10),
+ context_offset (Xmm11),
+ context_offset (Xmm12),
+ context_offset (Xmm13),
+ context_offset (Xmm14),
+ context_offset (Xmm15),
+ /* MXCSR */
+ context_offset (FloatSave.MxCsr)
+};
+#undef context_offset
+
+#endif /* __x86_64__ */
+
+/* Fetch register from gdbserver regcache data. */
+static void
+i386_fetch_inferior_register (struct regcache *regcache,
+ win32_thread_info *th, int r)
+{
+ char *context_offset = (char *) &th->context + mappings[r];
+
+ long l;
+ if (r == FCS_REGNUM)
+ {
+ l = *((long *) context_offset) & 0xffff;
+ supply_register (regcache, r, (char *) &l);
+ }
+ else if (r == FOP_REGNUM)
+ {
+ l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1);
+ supply_register (regcache, r, (char *) &l);
+ }
+ else
+ supply_register (regcache, r, context_offset);
+}
+
+/* Store a new register value into the thread context of TH. */
+static void
+i386_store_inferior_register (struct regcache *regcache,
+ win32_thread_info *th, int r)
+{
+ char *context_offset = (char *) &th->context + mappings[r];
+ collect_register (regcache, r, context_offset);
+}
+
+static const unsigned char i386_win32_breakpoint = 0xcc;
+#define i386_win32_breakpoint_len 1
+
+static void
+i386_arch_setup (void)
+{
+ struct target_desc *tdesc;
+
+#ifdef __x86_64__
+ tdesc = amd64_create_target_description (X86_XSTATE_SSE_MASK, false,
+ false, false);
+ const char **expedite_regs = amd64_expedite_regs;
+#else
+ tdesc = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
+ const char **expedite_regs = i386_expedite_regs;
+#endif
+
+ init_target_desc (tdesc, expedite_regs);
+
+ win32_tdesc = tdesc;
+}
+
+struct win32_target_ops the_low_target = {
+ i386_arch_setup,
+ sizeof (mappings) / sizeof (mappings[0]),
+ i386_initial_stuff,
+ i386_get_thread_context,
+ i386_prepare_to_resume,
+ i386_thread_added,
+ i386_fetch_inferior_register,
+ i386_store_inferior_register,
+ i386_single_step,
+ &i386_win32_breakpoint,
+ i386_win32_breakpoint_len,
+ i386_supports_z_point_type,
+ i386_insert_point,
+ i386_remove_point,
+ x86_stopped_by_watchpoint,
+ x86_stopped_data_address
+};
+++ /dev/null
-/* Low level interface to Windows debugging, for gdbserver.
- Copyright (C) 2006-2020 Free Software Foundation, Inc.
-
- Contributed by Leo Zayas. Based on "win32-nat.c" from GDB.
-
- 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 "regcache.h"
-#include "gdb/fileio.h"
-#include "mem-break.h"
-#include "win32-low.h"
-#include "gdbthread.h"
-#include "dll.h"
-#include "hostio.h"
-#include <windows.h>
-#include <winnt.h>
-#include <imagehlp.h>
-#include <tlhelp32.h>
-#include <psapi.h>
-#include <process.h>
-#include "gdbsupport/gdb_tilde_expand.h"
-#include "gdbsupport/common-inferior.h"
-#include "gdbsupport/gdb_wait.h"
-
-#ifndef USE_WIN32API
-#include <sys/cygwin.h>
-#endif
-
-#define OUTMSG(X) do { printf X; fflush (stderr); } while (0)
-
-#define OUTMSG2(X) \
- do \
- { \
- if (debug_threads) \
- { \
- printf X; \
- fflush (stderr); \
- } \
- } while (0)
-
-#ifndef _T
-#define _T(x) TEXT (x)
-#endif
-
-#ifndef COUNTOF
-#define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0]))
-#endif
-
-#ifdef _WIN32_WCE
-# define GETPROCADDRESS(DLL, PROC) \
- ((winapi_ ## PROC) GetProcAddress (DLL, TEXT (#PROC)))
-#else
-# define GETPROCADDRESS(DLL, PROC) \
- ((winapi_ ## PROC) GetProcAddress (DLL, #PROC))
-#endif
-
-int using_threads = 1;
-
-/* Globals. */
-static int attaching = 0;
-static HANDLE current_process_handle = NULL;
-static DWORD current_process_id = 0;
-static DWORD main_thread_id = 0;
-static EXCEPTION_RECORD siginfo_er; /* Contents of $_siginfo */
-static enum gdb_signal last_sig = GDB_SIGNAL_0;
-
-/* The current debug event from WaitForDebugEvent. */
-static DEBUG_EVENT current_event;
-
-/* A status that hasn't been reported to the core yet, and so
- win32_wait should return it next, instead of fetching the next
- debug event off the win32 API. */
-static struct target_waitstatus cached_status;
-
-/* Non zero if an interrupt request is to be satisfied by suspending
- all threads. */
-static int soft_interrupt_requested = 0;
-
-/* Non zero if the inferior is stopped in a simulated breakpoint done
- by suspending all the threads. */
-static int faked_breakpoint = 0;
-
-const struct target_desc *win32_tdesc;
-
-#define NUM_REGS (the_low_target.num_regs)
-
-typedef BOOL (WINAPI *winapi_DebugActiveProcessStop) (DWORD dwProcessId);
-typedef BOOL (WINAPI *winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit);
-typedef BOOL (WINAPI *winapi_DebugBreakProcess) (HANDLE);
-typedef BOOL (WINAPI *winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);
-
-static ptid_t win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus,
- int options);
-static void win32_resume (struct thread_resume *resume_info, size_t n);
-#ifndef _WIN32_WCE
-static void win32_add_all_dlls (void);
-#endif
-
-/* Get the thread ID from the current selected inferior (the current
- thread). */
-static ptid_t
-current_thread_ptid (void)
-{
- return current_ptid;
-}
-
-/* The current debug event from WaitForDebugEvent. */
-static ptid_t
-debug_event_ptid (DEBUG_EVENT *event)
-{
- return ptid_t (event->dwProcessId, event->dwThreadId, 0);
-}
-
-/* Get the thread context of the thread associated with TH. */
-
-static void
-win32_get_thread_context (win32_thread_info *th)
-{
- memset (&th->context, 0, sizeof (CONTEXT));
- (*the_low_target.get_thread_context) (th);
-#ifdef _WIN32_WCE
- memcpy (&th->base_context, &th->context, sizeof (CONTEXT));
-#endif
-}
-
-/* Set the thread context of the thread associated with TH. */
-
-static void
-win32_set_thread_context (win32_thread_info *th)
-{
-#ifdef _WIN32_WCE
- /* Calling SuspendThread on a thread that is running kernel code
- will report that the suspending was successful, but in fact, that
- will often not be true. In those cases, the context returned by
- GetThreadContext will not be correct by the time the thread
- stops, hence we can't set that context back into the thread when
- resuming - it will most likely crash the inferior.
- Unfortunately, there is no way to know when the thread will
- really stop. To work around it, we'll only write the context
- back to the thread when either the user or GDB explicitly change
- it between stopping and resuming. */
- if (memcmp (&th->context, &th->base_context, sizeof (CONTEXT)) != 0)
-#endif
- SetThreadContext (th->h, &th->context);
-}
-
-/* Set the thread context of the thread associated with TH. */
-
-static void
-win32_prepare_to_resume (win32_thread_info *th)
-{
- if (the_low_target.prepare_to_resume != NULL)
- (*the_low_target.prepare_to_resume) (th);
-}
-
-/* See win32-low.h. */
-
-void
-win32_require_context (win32_thread_info *th)
-{
- if (th->context.ContextFlags == 0)
- {
- if (!th->suspended)
- {
- if (SuspendThread (th->h) == (DWORD) -1)
- {
- DWORD err = GetLastError ();
- OUTMSG (("warning: SuspendThread failed in thread_rec, "
- "(error %d): %s\n", (int) err, strwinerror (err)));
- }
- else
- th->suspended = 1;
- }
-
- win32_get_thread_context (th);
- }
-}
-
-/* Find a thread record given a thread id. If GET_CONTEXT is set then
- also retrieve the context for this thread. */
-static win32_thread_info *
-thread_rec (ptid_t ptid, int get_context)
-{
- thread_info *thread = find_thread_ptid (ptid);
- if (thread == NULL)
- return NULL;
-
- win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
- if (get_context)
- win32_require_context (th);
- return th;
-}
-
-/* Add a thread to the thread list. */
-static win32_thread_info *
-child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
-{
- win32_thread_info *th;
- ptid_t ptid = ptid_t (pid, tid, 0);
-
- if ((th = thread_rec (ptid, FALSE)))
- return th;
-
- th = XCNEW (win32_thread_info);
- th->tid = tid;
- th->h = h;
- th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb;
-
- add_thread (ptid, th);
-
- if (the_low_target.thread_added != NULL)
- (*the_low_target.thread_added) (th);
-
- return th;
-}
-
-/* Delete a thread from the list of threads. */
-static void
-delete_thread_info (thread_info *thread)
-{
- win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
-
- remove_thread (thread);
- CloseHandle (th->h);
- free (th);
-}
-
-/* Delete a thread from the list of threads. */
-static void
-child_delete_thread (DWORD pid, DWORD tid)
-{
- /* If the last thread is exiting, just return. */
- if (all_threads.size () == 1)
- return;
-
- thread_info *thread = find_thread_ptid (ptid_t (pid, tid));
- if (thread == NULL)
- return;
-
- delete_thread_info (thread);
-}
-
-/* These watchpoint related wrapper functions simply pass on the function call
- if the low target has registered a corresponding function. */
-
-static int
-win32_supports_z_point_type (char z_type)
-{
- return (the_low_target.supports_z_point_type != NULL
- && the_low_target.supports_z_point_type (z_type));
-}
-
-static int
-win32_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- if (the_low_target.insert_point != NULL)
- return the_low_target.insert_point (type, addr, size, bp);
- else
- /* Unsupported (see target.h). */
- return 1;
-}
-
-static int
-win32_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int size, struct raw_breakpoint *bp)
-{
- if (the_low_target.remove_point != NULL)
- return the_low_target.remove_point (type, addr, size, bp);
- else
- /* Unsupported (see target.h). */
- return 1;
-}
-
-static int
-win32_stopped_by_watchpoint (void)
-{
- if (the_low_target.stopped_by_watchpoint != NULL)
- return the_low_target.stopped_by_watchpoint ();
- else
- return 0;
-}
-
-static CORE_ADDR
-win32_stopped_data_address (void)
-{
- if (the_low_target.stopped_data_address != NULL)
- return the_low_target.stopped_data_address ();
- else
- return 0;
-}
-
-
-/* Transfer memory from/to the debugged process. */
-static int
-child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
- int write, process_stratum_target *target)
-{
- BOOL success;
- SIZE_T done = 0;
- DWORD lasterror = 0;
- uintptr_t addr = (uintptr_t) memaddr;
-
- if (write)
- {
- success = WriteProcessMemory (current_process_handle, (LPVOID) addr,
- (LPCVOID) our, len, &done);
- if (!success)
- lasterror = GetLastError ();
- FlushInstructionCache (current_process_handle, (LPCVOID) addr, len);
- }
- else
- {
- success = ReadProcessMemory (current_process_handle, (LPCVOID) addr,
- (LPVOID) our, len, &done);
- if (!success)
- lasterror = GetLastError ();
- }
- if (!success && lasterror == ERROR_PARTIAL_COPY && done > 0)
- return done;
- else
- return success ? done : -1;
-}
-
-/* Clear out any old thread list and reinitialize it to a pristine
- state. */
-static void
-child_init_thread_list (void)
-{
- for_each_thread (delete_thread_info);
-}
-
-/* Zero during the child initialization phase, and nonzero otherwise. */
-
-static int child_initialization_done = 0;
-
-static void
-do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
-{
- struct process_info *proc;
-
- last_sig = GDB_SIGNAL_0;
-
- current_process_handle = proch;
- current_process_id = pid;
- main_thread_id = 0;
-
- soft_interrupt_requested = 0;
- faked_breakpoint = 0;
-
- memset (¤t_event, 0, sizeof (current_event));
-
- proc = add_process (pid, attached);
- proc->tdesc = win32_tdesc;
- child_init_thread_list ();
- child_initialization_done = 0;
-
- if (the_low_target.initial_stuff != NULL)
- (*the_low_target.initial_stuff) ();
-
- cached_status.kind = TARGET_WAITKIND_IGNORE;
-
- /* Flush all currently pending debug events (thread and dll list) up
- to the initial breakpoint. */
- while (1)
- {
- struct target_waitstatus status;
-
- win32_wait (minus_one_ptid, &status, 0);
-
- /* Note win32_wait doesn't return thread events. */
- if (status.kind != TARGET_WAITKIND_LOADED)
- {
- cached_status = status;
- break;
- }
-
- {
- struct thread_resume resume;
-
- resume.thread = minus_one_ptid;
- resume.kind = resume_continue;
- resume.sig = 0;
-
- win32_resume (&resume, 1);
- }
- }
-
-#ifndef _WIN32_WCE
- /* Now that the inferior has been started and all DLLs have been mapped,
- we can iterate over all DLLs and load them in.
-
- We avoid doing it any earlier because, on certain versions of Windows,
- LOAD_DLL_DEBUG_EVENTs are sometimes not complete. In particular,
- we have seen on Windows 8.1 that the ntdll.dll load event does not
- include the DLL name, preventing us from creating an associated SO.
- A possible explanation is that ntdll.dll might be mapped before
- the SO info gets created by the Windows system -- ntdll.dll is
- the first DLL to be reported via LOAD_DLL_DEBUG_EVENT and other DLLs
- do not seem to suffer from that problem.
-
- Rather than try to work around this sort of issue, it is much
- simpler to just ignore DLL load/unload events during the startup
- phase, and then process them all in one batch now. */
- win32_add_all_dlls ();
-#endif
-
- child_initialization_done = 1;
-}
-
-/* Resume all artificially suspended threads if we are continuing
- execution. */
-static void
-continue_one_thread (thread_info *thread, int thread_id)
-{
- win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
-
- if (thread_id == -1 || thread_id == th->tid)
- {
- win32_prepare_to_resume (th);
-
- if (th->suspended)
- {
- if (th->context.ContextFlags)
- {
- win32_set_thread_context (th);
- th->context.ContextFlags = 0;
- }
-
- if (ResumeThread (th->h) == (DWORD) -1)
- {
- DWORD err = GetLastError ();
- OUTMSG (("warning: ResumeThread failed in continue_one_thread, "
- "(error %d): %s\n", (int) err, strwinerror (err)));
- }
- th->suspended = 0;
- }
- }
-}
-
-static BOOL
-child_continue (DWORD continue_status, int thread_id)
-{
- /* The inferior will only continue after the ContinueDebugEvent
- call. */
- for_each_thread ([&] (thread_info *thread)
- {
- continue_one_thread (thread, thread_id);
- });
- faked_breakpoint = 0;
-
- if (!ContinueDebugEvent (current_event.dwProcessId,
- current_event.dwThreadId,
- continue_status))
- return FALSE;
-
- return TRUE;
-}
-
-/* Fetch register(s) from the current thread context. */
-static void
-child_fetch_inferior_registers (struct regcache *regcache, int r)
-{
- int regno;
- win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
- if (r == -1 || r > NUM_REGS)
- child_fetch_inferior_registers (regcache, NUM_REGS);
- else
- for (regno = 0; regno < r; regno++)
- (*the_low_target.fetch_inferior_register) (regcache, th, regno);
-}
-
-/* Store a new register value into the current thread context. We don't
- change the program's context until later, when we resume it. */
-static void
-child_store_inferior_registers (struct regcache *regcache, int r)
-{
- int regno;
- win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
- if (r == -1 || r == 0 || r > NUM_REGS)
- child_store_inferior_registers (regcache, NUM_REGS);
- else
- for (regno = 0; regno < r; regno++)
- (*the_low_target.store_inferior_register) (regcache, th, regno);
-}
-
-/* Map the Windows error number in ERROR to a locale-dependent error
- message string and return a pointer to it. Typically, the values
- for ERROR come from GetLastError.
-
- The string pointed to shall not be modified by the application,
- but may be overwritten by a subsequent call to strwinerror
-
- The strwinerror function does not change the current setting
- of GetLastError. */
-
-char *
-strwinerror (DWORD error)
-{
- static char buf[1024];
- TCHAR *msgbuf;
- DWORD lasterr = GetLastError ();
- DWORD chars = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
- | FORMAT_MESSAGE_ALLOCATE_BUFFER,
- NULL,
- error,
- 0, /* Default language */
- (LPTSTR) &msgbuf,
- 0,
- NULL);
- if (chars != 0)
- {
- /* If there is an \r\n appended, zap it. */
- if (chars >= 2
- && msgbuf[chars - 2] == '\r'
- && msgbuf[chars - 1] == '\n')
- {
- chars -= 2;
- msgbuf[chars] = 0;
- }
-
- if (chars > ((COUNTOF (buf)) - 1))
- {
- chars = COUNTOF (buf) - 1;
- msgbuf [chars] = 0;
- }
-
-#ifdef UNICODE
- wcstombs (buf, msgbuf, chars + 1);
-#else
- strncpy (buf, msgbuf, chars + 1);
-#endif
- LocalFree (msgbuf);
- }
- else
- sprintf (buf, "unknown win32 error (%u)", (unsigned) error);
-
- SetLastError (lasterr);
- return buf;
-}
-
-static BOOL
-create_process (const char *program, char *args,
- DWORD flags, PROCESS_INFORMATION *pi)
-{
- const char *inferior_cwd = get_inferior_cwd ();
- BOOL ret;
-
-#ifdef _WIN32_WCE
- wchar_t *p, *wprogram, *wargs, *wcwd = NULL;
- size_t argslen;
-
- wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
- mbstowcs (wprogram, program, strlen (program) + 1);
-
- for (p = wprogram; *p; ++p)
- if (L'/' == *p)
- *p = L'\\';
-
- argslen = strlen (args);
- wargs = alloca ((argslen + 1) * sizeof (wchar_t));
- mbstowcs (wargs, args, argslen + 1);
-
- if (inferior_cwd != NULL)
- {
- std::string expanded_infcwd = gdb_tilde_expand (inferior_cwd);
- std::replace (expanded_infcwd.begin (), expanded_infcwd.end (),
- '/', '\\');
- wcwd = alloca ((expanded_infcwd.size () + 1) * sizeof (wchar_t));
- if (mbstowcs (wcwd, expanded_infcwd.c_str (),
- expanded_infcwd.size () + 1) == NULL)
- {
- error (_("\
-Could not convert the expanded inferior cwd to wide-char."));
- }
- }
-
- ret = CreateProcessW (wprogram, /* image name */
- wargs, /* command line */
- NULL, /* security, not supported */
- NULL, /* thread, not supported */
- FALSE, /* inherit handles, not supported */
- flags, /* start flags */
- NULL, /* environment, not supported */
- wcwd, /* current directory */
- NULL, /* start info, not supported */
- pi); /* proc info */
-#else
- STARTUPINFOA si = { sizeof (STARTUPINFOA) };
-
- ret = CreateProcessA (program, /* image name */
- args, /* command line */
- NULL, /* security */
- NULL, /* thread */
- TRUE, /* inherit handles */
- flags, /* start flags */
- NULL, /* environment */
- /* current directory */
- (inferior_cwd == NULL
- ? NULL
- : gdb_tilde_expand (inferior_cwd).c_str()),
- &si, /* start info */
- pi); /* proc info */
-#endif
-
- return ret;
-}
-
-/* Start a new process.
- PROGRAM is the program name.
- PROGRAM_ARGS is the vector containing the inferior's args.
- Returns the new PID on success, -1 on failure. Registers the new
- process with the process list. */
-static int
-win32_create_inferior (const char *program,
- const std::vector<char *> &program_args)
-{
- client_state &cs = get_client_state ();
-#ifndef USE_WIN32API
- char real_path[PATH_MAX];
- char *orig_path, *new_path, *path_ptr;
-#endif
- BOOL ret;
- DWORD flags;
- PROCESS_INFORMATION pi;
- DWORD err;
- std::string str_program_args = stringify_argv (program_args);
- char *args = (char *) str_program_args.c_str ();
-
- /* win32_wait needs to know we're not attaching. */
- attaching = 0;
-
- if (!program)
- error ("No executable specified, specify executable to debug.\n");
-
- flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
-
-#ifndef USE_WIN32API
- orig_path = NULL;
- path_ptr = getenv ("PATH");
- if (path_ptr)
- {
- int size = cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, NULL, 0);
- orig_path = (char *) alloca (strlen (path_ptr) + 1);
- new_path = (char *) alloca (size);
- strcpy (orig_path, path_ptr);
- cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, new_path, size);
- setenv ("PATH", new_path, 1);
- }
- cygwin_conv_path (CCP_POSIX_TO_WIN_A, program, real_path, PATH_MAX);
- program = real_path;
-#endif
-
- OUTMSG2 (("Command line is \"%s\"\n", args));
-
-#ifdef CREATE_NEW_PROCESS_GROUP
- flags |= CREATE_NEW_PROCESS_GROUP;
-#endif
-
- ret = create_process (program, args, flags, &pi);
- err = GetLastError ();
- if (!ret && err == ERROR_FILE_NOT_FOUND)
- {
- char *exename = (char *) alloca (strlen (program) + 5);
- strcat (strcpy (exename, program), ".exe");
- ret = create_process (exename, args, flags, &pi);
- err = GetLastError ();
- }
-
-#ifndef USE_WIN32API
- if (orig_path)
- setenv ("PATH", orig_path, 1);
-#endif
-
- if (!ret)
- {
- error ("Error creating process \"%s%s\", (error %d): %s\n",
- program, args, (int) err, strwinerror (err));
- }
- else
- {
- OUTMSG2 (("Process created: %s\n", (char *) args));
- }
-
-#ifndef _WIN32_WCE
- /* On Windows CE this handle can't be closed. The OS reuses
- it in the debug events, while the 9x/NT versions of Windows
- probably use a DuplicateHandle'd one. */
- CloseHandle (pi.hThread);
-#endif
-
- do_initial_child_stuff (pi.hProcess, pi.dwProcessId, 0);
-
- /* Wait till we are at 1st instruction in program, return new pid
- (assuming success). */
- cs.last_ptid = win32_wait (ptid_t (current_process_id), &cs.last_status, 0);
-
- /* Necessary for handle_v_kill. */
- signal_pid = current_process_id;
-
- return current_process_id;
-}
-
-/* Attach to a running process.
- PID is the process ID to attach to, specified by the user
- or a higher layer. */
-static int
-win32_attach (unsigned long pid)
-{
- HANDLE h;
- winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
- DWORD err;
-#ifdef _WIN32_WCE
- HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
-#else
- HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
-#endif
- DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
-
- h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
- if (h != NULL)
- {
- if (DebugActiveProcess (pid))
- {
- if (DebugSetProcessKillOnExit != NULL)
- DebugSetProcessKillOnExit (FALSE);
-
- /* win32_wait needs to know we're attaching. */
- attaching = 1;
- do_initial_child_stuff (h, pid, 1);
- return 0;
- }
-
- CloseHandle (h);
- }
-
- err = GetLastError ();
- error ("Attach to process failed (error %d): %s\n",
- (int) err, strwinerror (err));
-}
-
-/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */
-static void
-handle_output_debug_string (void)
-{
-#define READ_BUFFER_LEN 1024
- CORE_ADDR addr;
- char s[READ_BUFFER_LEN + 1] = { 0 };
- DWORD nbytes = current_event.u.DebugString.nDebugStringLength;
-
- if (nbytes == 0)
- return;
-
- if (nbytes > READ_BUFFER_LEN)
- nbytes = READ_BUFFER_LEN;
-
- addr = (CORE_ADDR) (size_t) current_event.u.DebugString.lpDebugStringData;
-
- if (current_event.u.DebugString.fUnicode)
- {
- /* The event tells us how many bytes, not chars, even
- in Unicode. */
- WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 };
- if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0)
- return;
- wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR));
- }
- else
- {
- if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0)
- return;
- }
-
- if (!startswith (s, "cYg"))
- {
- if (!server_waiting)
- {
- OUTMSG2(("%s", s));
- return;
- }
-
- monitor_output (s);
- }
-#undef READ_BUFFER_LEN
-}
-
-static void
-win32_clear_inferiors (void)
-{
- if (current_process_handle != NULL)
- CloseHandle (current_process_handle);
-
- for_each_thread (delete_thread_info);
- siginfo_er.ExceptionCode = 0;
- clear_inferiors ();
-}
-
-/* Implementation of target_ops::kill. */
-
-static int
-win32_kill (process_info *process)
-{
- TerminateProcess (current_process_handle, 0);
- for (;;)
- {
- if (!child_continue (DBG_CONTINUE, -1))
- break;
- if (!WaitForDebugEvent (¤t_event, INFINITE))
- break;
- if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
- break;
- else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
- handle_output_debug_string ();
- }
-
- win32_clear_inferiors ();
-
- remove_process (process);
- return 0;
-}
-
-/* Implementation of target_ops::detach. */
-
-static int
-win32_detach (process_info *process)
-{
- winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
- winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
-#ifdef _WIN32_WCE
- HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
-#else
- HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
-#endif
- DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop);
- DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
-
- if (DebugSetProcessKillOnExit == NULL
- || DebugActiveProcessStop == NULL)
- return -1;
-
- {
- struct thread_resume resume;
- resume.thread = minus_one_ptid;
- resume.kind = resume_continue;
- resume.sig = 0;
- win32_resume (&resume, 1);
- }
-
- if (!DebugActiveProcessStop (current_process_id))
- return -1;
-
- DebugSetProcessKillOnExit (FALSE);
- remove_process (process);
-
- win32_clear_inferiors ();
- return 0;
-}
-
-static void
-win32_mourn (struct process_info *process)
-{
- remove_process (process);
-}
-
-/* Implementation of target_ops::join. */
-
-static void
-win32_join (int pid)
-{
- HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
- if (h != NULL)
- {
- WaitForSingleObject (h, INFINITE);
- CloseHandle (h);
- }
-}
-
-/* Return 1 iff the thread with thread ID TID is alive. */
-static int
-win32_thread_alive (ptid_t ptid)
-{
- /* Our thread list is reliable; don't bother to poll target
- threads. */
- return find_thread_ptid (ptid) != NULL;
-}
-
-/* Resume the inferior process. RESUME_INFO describes how we want
- to resume. */
-static void
-win32_resume (struct thread_resume *resume_info, size_t n)
-{
- DWORD tid;
- enum gdb_signal sig;
- int step;
- win32_thread_info *th;
- DWORD continue_status = DBG_CONTINUE;
- ptid_t ptid;
-
- /* This handles the very limited set of resume packets that GDB can
- currently produce. */
-
- if (n == 1 && resume_info[0].thread == minus_one_ptid)
- tid = -1;
- else if (n > 1)
- tid = -1;
- else
- /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make
- the Windows resume code do the right thing for thread switching. */
- tid = current_event.dwThreadId;
-
- if (resume_info[0].thread != minus_one_ptid)
- {
- sig = gdb_signal_from_host (resume_info[0].sig);
- step = resume_info[0].kind == resume_step;
- }
- else
- {
- sig = GDB_SIGNAL_0;
- step = 0;
- }
-
- if (sig != GDB_SIGNAL_0)
- {
- if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
- {
- OUTMSG (("Cannot continue with signal %s here.\n",
- gdb_signal_to_string (sig)));
- }
- else if (sig == last_sig)
- continue_status = DBG_EXCEPTION_NOT_HANDLED;
- else
- OUTMSG (("Can only continue with received signal %s.\n",
- gdb_signal_to_string (last_sig)));
- }
-
- last_sig = GDB_SIGNAL_0;
-
- /* Get context for the currently selected thread. */
- ptid = debug_event_ptid (¤t_event);
- th = thread_rec (ptid, FALSE);
- if (th)
- {
- win32_prepare_to_resume (th);
-
- if (th->context.ContextFlags)
- {
- /* Move register values from the inferior into the thread
- context structure. */
- regcache_invalidate ();
-
- if (step)
- {
- if (the_low_target.single_step != NULL)
- (*the_low_target.single_step) (th);
- else
- error ("Single stepping is not supported "
- "in this configuration.\n");
- }
-
- win32_set_thread_context (th);
- th->context.ContextFlags = 0;
- }
- }
-
- /* Allow continuing with the same signal that interrupted us.
- Otherwise complain. */
-
- child_continue (continue_status, tid);
-}
-
-static void
-win32_add_one_solib (const char *name, CORE_ADDR load_addr)
-{
- char buf[MAX_PATH + 1];
- char buf2[MAX_PATH + 1];
-
-#ifdef _WIN32_WCE
- WIN32_FIND_DATA w32_fd;
- WCHAR wname[MAX_PATH + 1];
- mbstowcs (wname, name, MAX_PATH);
- HANDLE h = FindFirstFile (wname, &w32_fd);
-#else
- WIN32_FIND_DATAA w32_fd;
- HANDLE h = FindFirstFileA (name, &w32_fd);
-#endif
-
- /* The symbols in a dll are offset by 0x1000, which is the
- offset from 0 of the first byte in an image - because
- of the file header and the section alignment. */
- load_addr += 0x1000;
-
- if (h == INVALID_HANDLE_VALUE)
- strcpy (buf, name);
- else
- {
- FindClose (h);
- strcpy (buf, name);
-#ifndef _WIN32_WCE
- {
- char cwd[MAX_PATH + 1];
- char *p;
- if (GetCurrentDirectoryA (MAX_PATH + 1, cwd))
- {
- p = strrchr (buf, '\\');
- if (p)
- p[1] = '\0';
- SetCurrentDirectoryA (buf);
- GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p);
- SetCurrentDirectoryA (cwd);
- }
- }
-#endif
- }
-
-#ifndef _WIN32_WCE
- if (strcasecmp (buf, "ntdll.dll") == 0)
- {
- GetSystemDirectoryA (buf, sizeof (buf));
- strcat (buf, "\\ntdll.dll");
- }
-#endif
-
-#ifdef __CYGWIN__
- cygwin_conv_path (CCP_WIN_A_TO_POSIX, buf, buf2, sizeof (buf2));
-#else
- strcpy (buf2, buf);
-#endif
-
- loaded_dll (buf2, load_addr);
-}
-
-static char *
-get_image_name (HANDLE h, void *address, int unicode)
-{
- static char buf[(2 * MAX_PATH) + 1];
- DWORD size = unicode ? sizeof (WCHAR) : sizeof (char);
- char *address_ptr;
- int len = 0;
- char b[2];
- SIZE_T done;
-
- /* Attempt to read the name of the dll that was detected.
- This is documented to work only when actively debugging
- a program. It will not work for attached processes. */
- if (address == NULL)
- return NULL;
-
-#ifdef _WIN32_WCE
- /* Windows CE reports the address of the image name,
- instead of an address of a pointer into the image name. */
- address_ptr = address;
-#else
- /* See if we could read the address of a string, and that the
- address isn't null. */
- if (!ReadProcessMemory (h, address, &address_ptr,
- sizeof (address_ptr), &done)
- || done != sizeof (address_ptr)
- || !address_ptr)
- return NULL;
-#endif
-
- /* Find the length of the string */
- while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
- && (b[0] != 0 || b[size - 1] != 0) && done == size)
- continue;
-
- if (!unicode)
- ReadProcessMemory (h, address_ptr, buf, len, &done);
- else
- {
- WCHAR *unicode_address = XALLOCAVEC (WCHAR, len);
- ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR),
- &done);
-
- WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0);
- }
-
- return buf;
-}
-
-typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *,
- DWORD, LPDWORD);
-typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE,
- LPMODULEINFO, DWORD);
-typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE,
- LPSTR, DWORD);
-
-static winapi_EnumProcessModules win32_EnumProcessModules;
-static winapi_GetModuleInformation win32_GetModuleInformation;
-static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA;
-
-static BOOL
-load_psapi (void)
-{
- static int psapi_loaded = 0;
- static HMODULE dll = NULL;
-
- if (!psapi_loaded)
- {
- psapi_loaded = 1;
- dll = LoadLibrary (TEXT("psapi.dll"));
- if (!dll)
- return FALSE;
- win32_EnumProcessModules =
- GETPROCADDRESS (dll, EnumProcessModules);
- win32_GetModuleInformation =
- GETPROCADDRESS (dll, GetModuleInformation);
- win32_GetModuleFileNameExA =
- GETPROCADDRESS (dll, GetModuleFileNameExA);
- }
-
- return (win32_EnumProcessModules != NULL
- && win32_GetModuleInformation != NULL
- && win32_GetModuleFileNameExA != NULL);
-}
-
-#ifndef _WIN32_WCE
-
-/* Iterate over all DLLs currently mapped by our inferior, and
- add them to our list of solibs. */
-
-static void
-win32_add_all_dlls (void)
-{
- size_t i;
- HMODULE dh_buf[1];
- HMODULE *DllHandle = dh_buf;
- DWORD cbNeeded;
- BOOL ok;
-
- if (!load_psapi ())
- return;
-
- cbNeeded = 0;
- ok = (*win32_EnumProcessModules) (current_process_handle,
- DllHandle,
- sizeof (HMODULE),
- &cbNeeded);
-
- if (!ok || !cbNeeded)
- return;
-
- DllHandle = (HMODULE *) alloca (cbNeeded);
- if (!DllHandle)
- return;
-
- ok = (*win32_EnumProcessModules) (current_process_handle,
- DllHandle,
- cbNeeded,
- &cbNeeded);
- if (!ok)
- return;
-
- for (i = 1; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++)
- {
- MODULEINFO mi;
- char dll_name[MAX_PATH];
-
- if (!(*win32_GetModuleInformation) (current_process_handle,
- DllHandle[i],
- &mi,
- sizeof (mi)))
- continue;
- if ((*win32_GetModuleFileNameExA) (current_process_handle,
- DllHandle[i],
- dll_name,
- MAX_PATH) == 0)
- continue;
- win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll);
- }
-}
-#endif
-
-typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD);
-typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32);
-typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32);
-
-/* Handle a DLL load event.
-
- This function assumes that this event did not occur during inferior
- initialization, where their event info may be incomplete (see
- do_initial_child_stuff and win32_add_all_dlls for more info on
- how we handle DLL loading during that phase). */
-
-static void
-handle_load_dll (void)
-{
- LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
- char *dll_name;
-
- dll_name = get_image_name (current_process_handle,
- event->lpImageName, event->fUnicode);
- if (!dll_name)
- return;
-
- win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) event->lpBaseOfDll);
-}
-
-/* Handle a DLL unload event.
-
- This function assumes that this event did not occur during inferior
- initialization, where their event info may be incomplete (see
- do_initial_child_stuff and win32_add_one_solib for more info
- on how we handle DLL loading during that phase). */
-
-static void
-handle_unload_dll (void)
-{
- CORE_ADDR load_addr =
- (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll;
-
- /* The symbols in a dll are offset by 0x1000, which is the
- offset from 0 of the first byte in an image - because
- of the file header and the section alignment. */
- load_addr += 0x1000;
- unloaded_dll (NULL, load_addr);
-}
-
-static void
-handle_exception (struct target_waitstatus *ourstatus)
-{
- DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode;
-
- memcpy (&siginfo_er, ¤t_event.u.Exception.ExceptionRecord,
- sizeof siginfo_er);
-
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
-
- switch (code)
- {
- case EXCEPTION_ACCESS_VIOLATION:
- OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION"));
- ourstatus->value.sig = GDB_SIGNAL_SEGV;
- break;
- case STATUS_STACK_OVERFLOW:
- OUTMSG2 (("STATUS_STACK_OVERFLOW"));
- ourstatus->value.sig = GDB_SIGNAL_SEGV;
- break;
- case STATUS_FLOAT_DENORMAL_OPERAND:
- OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_FLOAT_INEXACT_RESULT:
- OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_FLOAT_INVALID_OPERATION:
- OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_FLOAT_OVERFLOW:
- OUTMSG2 (("STATUS_FLOAT_OVERFLOW"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_FLOAT_STACK_CHECK:
- OUTMSG2 (("STATUS_FLOAT_STACK_CHECK"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_FLOAT_UNDERFLOW:
- OUTMSG2 (("STATUS_FLOAT_UNDERFLOW"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_FLOAT_DIVIDE_BY_ZERO:
- OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_INTEGER_DIVIDE_BY_ZERO:
- OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case STATUS_INTEGER_OVERFLOW:
- OUTMSG2 (("STATUS_INTEGER_OVERFLOW"));
- ourstatus->value.sig = GDB_SIGNAL_FPE;
- break;
- case EXCEPTION_BREAKPOINT:
- OUTMSG2 (("EXCEPTION_BREAKPOINT"));
- ourstatus->value.sig = GDB_SIGNAL_TRAP;
-#ifdef _WIN32_WCE
- /* Remove the initial breakpoint. */
- check_breakpoints ((CORE_ADDR) (long) current_event
- .u.Exception.ExceptionRecord.ExceptionAddress);
-#endif
- break;
- case DBG_CONTROL_C:
- OUTMSG2 (("DBG_CONTROL_C"));
- ourstatus->value.sig = GDB_SIGNAL_INT;
- break;
- case DBG_CONTROL_BREAK:
- OUTMSG2 (("DBG_CONTROL_BREAK"));
- ourstatus->value.sig = GDB_SIGNAL_INT;
- break;
- case EXCEPTION_SINGLE_STEP:
- OUTMSG2 (("EXCEPTION_SINGLE_STEP"));
- ourstatus->value.sig = GDB_SIGNAL_TRAP;
- break;
- case EXCEPTION_ILLEGAL_INSTRUCTION:
- OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION"));
- ourstatus->value.sig = GDB_SIGNAL_ILL;
- break;
- case EXCEPTION_PRIV_INSTRUCTION:
- OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION"));
- ourstatus->value.sig = GDB_SIGNAL_ILL;
- break;
- case EXCEPTION_NONCONTINUABLE_EXCEPTION:
- OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION"));
- ourstatus->value.sig = GDB_SIGNAL_ILL;
- break;
- default:
- if (current_event.u.Exception.dwFirstChance)
- {
- ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
- return;
- }
- OUTMSG2 (("gdbserver: unknown target exception 0x%08x at 0x%s",
- (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode,
- phex_nz ((uintptr_t) current_event.u.Exception.ExceptionRecord.
- ExceptionAddress, sizeof (uintptr_t))));
- ourstatus->value.sig = GDB_SIGNAL_UNKNOWN;
- break;
- }
- OUTMSG2 (("\n"));
- last_sig = ourstatus->value.sig;
-}
-
-
-static void
-suspend_one_thread (thread_info *thread)
-{
- win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
-
- if (!th->suspended)
- {
- if (SuspendThread (th->h) == (DWORD) -1)
- {
- DWORD err = GetLastError ();
- OUTMSG (("warning: SuspendThread failed in suspend_one_thread, "
- "(error %d): %s\n", (int) err, strwinerror (err)));
- }
- else
- th->suspended = 1;
- }
-}
-
-static void
-fake_breakpoint_event (void)
-{
- OUTMSG2(("fake_breakpoint_event\n"));
-
- faked_breakpoint = 1;
-
- memset (¤t_event, 0, sizeof (current_event));
- current_event.dwThreadId = main_thread_id;
- current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
- current_event.u.Exception.ExceptionRecord.ExceptionCode
- = EXCEPTION_BREAKPOINT;
-
- for_each_thread (suspend_one_thread);
-}
-
-#ifdef _WIN32_WCE
-static int
-auto_delete_breakpoint (CORE_ADDR stop_pc)
-{
- return 1;
-}
-#endif
-
-/* Get the next event from the child. */
-
-static int
-get_child_debug_event (struct target_waitstatus *ourstatus)
-{
- ptid_t ptid;
-
- last_sig = GDB_SIGNAL_0;
- ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
-
- /* Check if GDB sent us an interrupt request. */
- check_remote_input_interrupt_request ();
-
- if (soft_interrupt_requested)
- {
- soft_interrupt_requested = 0;
- fake_breakpoint_event ();
- goto gotevent;
- }
-
-#ifndef _WIN32_WCE
- attaching = 0;
-#else
- if (attaching)
- {
- /* WinCE doesn't set an initial breakpoint automatically. To
- stop the inferior, we flush all currently pending debug
- events -- the thread list and the dll list are always
- reported immediatelly without delay, then, we suspend all
- threads and pretend we saw a trap at the current PC of the
- main thread.
-
- Contrary to desktop Windows, Windows CE *does* report the dll
- names on LOAD_DLL_DEBUG_EVENTs resulting from a
- DebugActiveProcess call. This limits the way we can detect
- if all the dlls have already been reported. If we get a real
- debug event before leaving attaching, the worst that will
- happen is the user will see a spurious breakpoint. */
-
- current_event.dwDebugEventCode = 0;
- if (!WaitForDebugEvent (¤t_event, 0))
- {
- OUTMSG2(("no attach events left\n"));
- fake_breakpoint_event ();
- attaching = 0;
- }
- else
- OUTMSG2(("got attach event\n"));
- }
- else
-#endif
- {
- /* Keep the wait time low enough for comfortable remote
- interruption, but high enough so gdbserver doesn't become a
- bottleneck. */
- if (!WaitForDebugEvent (¤t_event, 250))
- {
- DWORD e = GetLastError();
-
- if (e == ERROR_PIPE_NOT_CONNECTED)
- {
- /* This will happen if the loader fails to succesfully
- load the application, e.g., if the main executable
- tries to pull in a non-existing export from a
- DLL. */
- ourstatus->kind = TARGET_WAITKIND_EXITED;
- ourstatus->value.integer = 1;
- return 1;
- }
-
- return 0;
- }
- }
-
- gotevent:
-
- switch (current_event.dwDebugEventCode)
- {
- case CREATE_THREAD_DEBUG_EVENT:
- OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT "
- "for pid=%u tid=%x)\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
-
- /* Record the existence of this thread. */
- child_add_thread (current_event.dwProcessId,
- current_event.dwThreadId,
- current_event.u.CreateThread.hThread,
- current_event.u.CreateThread.lpThreadLocalBase);
- break;
-
- case EXIT_THREAD_DEBUG_EVENT:
- OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT "
- "for pid=%u tid=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
- child_delete_thread (current_event.dwProcessId,
- current_event.dwThreadId);
-
- current_thread = get_first_thread ();
- return 1;
-
- case CREATE_PROCESS_DEBUG_EVENT:
- OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT "
- "for pid=%u tid=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
- CloseHandle (current_event.u.CreateProcessInfo.hFile);
-
- current_process_handle = current_event.u.CreateProcessInfo.hProcess;
- main_thread_id = current_event.dwThreadId;
-
- /* Add the main thread. */
- child_add_thread (current_event.dwProcessId,
- main_thread_id,
- current_event.u.CreateProcessInfo.hThread,
- current_event.u.CreateProcessInfo.lpThreadLocalBase);
-
-#ifdef _WIN32_WCE
- if (!attaching)
- {
- /* Windows CE doesn't set the initial breakpoint
- automatically like the desktop versions of Windows do.
- We add it explicitly here. It will be removed as soon as
- it is hit. */
- set_breakpoint_at ((CORE_ADDR) (long) current_event.u
- .CreateProcessInfo.lpStartAddress,
- auto_delete_breakpoint);
- }
-#endif
- break;
-
- case EXIT_PROCESS_DEBUG_EVENT:
- OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT "
- "for pid=%u tid=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
- {
- DWORD exit_status = current_event.u.ExitProcess.dwExitCode;
- /* If the exit status looks like a fatal exception, but we
- don't recognize the exception's code, make the original
- exit status value available, to avoid losing information. */
- int exit_signal
- = WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1;
- if (exit_signal == -1)
- {
- ourstatus->kind = TARGET_WAITKIND_EXITED;
- ourstatus->value.integer = exit_status;
- }
- else
- {
- ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
- ourstatus->value.sig = gdb_signal_from_host (exit_signal);
- }
- }
- child_continue (DBG_CONTINUE, -1);
- CloseHandle (current_process_handle);
- current_process_handle = NULL;
- break;
-
- case LOAD_DLL_DEBUG_EVENT:
- OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT "
- "for pid=%u tid=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
- CloseHandle (current_event.u.LoadDll.hFile);
- if (! child_initialization_done)
- break;
- handle_load_dll ();
-
- ourstatus->kind = TARGET_WAITKIND_LOADED;
- ourstatus->value.sig = GDB_SIGNAL_TRAP;
- break;
-
- case UNLOAD_DLL_DEBUG_EVENT:
- OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT "
- "for pid=%u tid=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
- if (! child_initialization_done)
- break;
- handle_unload_dll ();
- ourstatus->kind = TARGET_WAITKIND_LOADED;
- ourstatus->value.sig = GDB_SIGNAL_TRAP;
- break;
-
- case EXCEPTION_DEBUG_EVENT:
- OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT "
- "for pid=%u tid=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
- handle_exception (ourstatus);
- break;
-
- case OUTPUT_DEBUG_STRING_EVENT:
- /* A message from the kernel (or Cygwin). */
- OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT "
- "for pid=%u tid=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId));
- handle_output_debug_string ();
- break;
-
- default:
- OUTMSG2 (("gdbserver: kernel event unknown "
- "for pid=%u tid=%x code=%x\n",
- (unsigned) current_event.dwProcessId,
- (unsigned) current_event.dwThreadId,
- (unsigned) current_event.dwDebugEventCode));
- break;
- }
-
- ptid = debug_event_ptid (¤t_event);
- current_thread = find_thread_ptid (ptid);
- return 1;
-}
-
-/* Wait for the inferior process to change state.
- STATUS will be filled in with a response code to send to GDB.
- Returns the signal which caused the process to stop. */
-static ptid_t
-win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
-{
- struct regcache *regcache;
-
- if (cached_status.kind != TARGET_WAITKIND_IGNORE)
- {
- /* The core always does a wait after creating the inferior, and
- do_initial_child_stuff already ran the inferior to the
- initial breakpoint (or an exit, if creating the process
- fails). Report it now. */
- *ourstatus = cached_status;
- cached_status.kind = TARGET_WAITKIND_IGNORE;
- return debug_event_ptid (¤t_event);
- }
-
- while (1)
- {
- if (!get_child_debug_event (ourstatus))
- continue;
-
- switch (ourstatus->kind)
- {
- case TARGET_WAITKIND_EXITED:
- OUTMSG2 (("Child exited with retcode = %x\n",
- ourstatus->value.integer));
- win32_clear_inferiors ();
- return ptid_t (current_event.dwProcessId);
- case TARGET_WAITKIND_STOPPED:
- case TARGET_WAITKIND_SIGNALLED:
- case TARGET_WAITKIND_LOADED:
- OUTMSG2 (("Child Stopped with signal = %d \n",
- ourstatus->value.sig));
-
- regcache = get_thread_regcache (current_thread, 1);
- child_fetch_inferior_registers (regcache, -1);
- return debug_event_ptid (¤t_event);
- default:
- OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
- /* fall-through */
- case TARGET_WAITKIND_SPURIOUS:
- /* do nothing, just continue */
- child_continue (DBG_CONTINUE, -1);
- break;
- }
- }
-}
-
-/* Fetch registers from the inferior process.
- If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */
-static void
-win32_fetch_inferior_registers (struct regcache *regcache, int regno)
-{
- child_fetch_inferior_registers (regcache, regno);
-}
-
-/* Store registers to the inferior process.
- If REGNO is -1, store all registers; otherwise, store at least REGNO. */
-static void
-win32_store_inferior_registers (struct regcache *regcache, int regno)
-{
- child_store_inferior_registers (regcache, regno);
-}
-
-/* Read memory from the inferior process. This should generally be
- called through read_inferior_memory, which handles breakpoint shadowing.
- Read LEN bytes at MEMADDR into a buffer at MYADDR. */
-static int
-win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- return child_xfer_memory (memaddr, (char *) myaddr, len, 0, 0) != len;
-}
-
-/* Write memory to the inferior process. This should generally be
- called through write_inferior_memory, which handles breakpoint shadowing.
- Write LEN bytes from the buffer at MYADDR to MEMADDR.
- Returns 0 on success and errno on failure. */
-static int
-win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
- int len)
-{
- return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len;
-}
-
-/* Send an interrupt request to the inferior process. */
-static void
-win32_request_interrupt (void)
-{
- winapi_DebugBreakProcess DebugBreakProcess;
- winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent;
-
-#ifdef _WIN32_WCE
- HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
-#else
- HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
-#endif
-
- GenerateConsoleCtrlEvent = GETPROCADDRESS (dll, GenerateConsoleCtrlEvent);
-
- if (GenerateConsoleCtrlEvent != NULL
- && GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, current_process_id))
- return;
-
- /* GenerateConsoleCtrlEvent can fail if process id being debugged is
- not a process group id.
- Fallback to XP/Vista 'DebugBreakProcess', which generates a
- breakpoint exception in the interior process. */
-
- DebugBreakProcess = GETPROCADDRESS (dll, DebugBreakProcess);
-
- if (DebugBreakProcess != NULL
- && DebugBreakProcess (current_process_handle))
- return;
-
- /* Last resort, suspend all threads manually. */
- soft_interrupt_requested = 1;
-}
-
-#ifdef _WIN32_WCE
-int
-win32_error_to_fileio_error (DWORD err)
-{
- switch (err)
- {
- case ERROR_BAD_PATHNAME:
- case ERROR_FILE_NOT_FOUND:
- case ERROR_INVALID_NAME:
- case ERROR_PATH_NOT_FOUND:
- return FILEIO_ENOENT;
- case ERROR_CRC:
- case ERROR_IO_DEVICE:
- case ERROR_OPEN_FAILED:
- return FILEIO_EIO;
- case ERROR_INVALID_HANDLE:
- return FILEIO_EBADF;
- case ERROR_ACCESS_DENIED:
- case ERROR_SHARING_VIOLATION:
- return FILEIO_EACCES;
- case ERROR_NOACCESS:
- return FILEIO_EFAULT;
- case ERROR_BUSY:
- return FILEIO_EBUSY;
- case ERROR_ALREADY_EXISTS:
- case ERROR_FILE_EXISTS:
- return FILEIO_EEXIST;
- case ERROR_BAD_DEVICE:
- return FILEIO_ENODEV;
- case ERROR_DIRECTORY:
- return FILEIO_ENOTDIR;
- case ERROR_FILENAME_EXCED_RANGE:
- case ERROR_INVALID_DATA:
- case ERROR_INVALID_PARAMETER:
- case ERROR_NEGATIVE_SEEK:
- return FILEIO_EINVAL;
- case ERROR_TOO_MANY_OPEN_FILES:
- return FILEIO_EMFILE;
- case ERROR_HANDLE_DISK_FULL:
- case ERROR_DISK_FULL:
- return FILEIO_ENOSPC;
- case ERROR_WRITE_PROTECT:
- return FILEIO_EROFS;
- case ERROR_NOT_SUPPORTED:
- return FILEIO_ENOSYS;
- }
-
- return FILEIO_EUNKNOWN;
-}
-
-static void
-wince_hostio_last_error (char *buf)
-{
- DWORD winerr = GetLastError ();
- int fileio_err = win32_error_to_fileio_error (winerr);
- sprintf (buf, "F-1,%x", fileio_err);
-}
-#endif
-
-/* Write Windows signal info. */
-
-static int
-win32_xfer_siginfo (const char *annex, unsigned char *readbuf,
- unsigned const char *writebuf, CORE_ADDR offset, int len)
-{
- if (siginfo_er.ExceptionCode == 0)
- return -1;
-
- if (readbuf == nullptr)
- return -1;
-
- if (offset > sizeof (siginfo_er))
- return -1;
-
- if (offset + len > sizeof (siginfo_er))
- len = sizeof (siginfo_er) - offset;
-
- memcpy (readbuf, (char *) &siginfo_er + offset, len);
-
- return len;
-}
-
-/* Write Windows OS Thread Information Block address. */
-
-static int
-win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr)
-{
- win32_thread_info *th;
- th = thread_rec (ptid, 0);
- if (th == NULL)
- return 0;
- if (addr != NULL)
- *addr = th->thread_local_base;
- return 1;
-}
-
-/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
-
-static const gdb_byte *
-win32_sw_breakpoint_from_kind (int kind, int *size)
-{
- *size = the_low_target.breakpoint_len;
- return the_low_target.breakpoint;
-}
-
-static process_stratum_target win32_target_ops = {
- win32_create_inferior,
- NULL, /* post_create_inferior */
- win32_attach,
- win32_kill,
- win32_detach,
- win32_mourn,
- win32_join,
- win32_thread_alive,
- win32_resume,
- win32_wait,
- win32_fetch_inferior_registers,
- win32_store_inferior_registers,
- NULL, /* prepare_to_access_memory */
- NULL, /* done_accessing_memory */
- win32_read_inferior_memory,
- win32_write_inferior_memory,
- NULL, /* lookup_symbols */
- win32_request_interrupt,
- NULL, /* read_auxv */
- win32_supports_z_point_type,
- win32_insert_point,
- win32_remove_point,
- NULL, /* stopped_by_sw_breakpoint */
- NULL, /* supports_stopped_by_sw_breakpoint */
- NULL, /* stopped_by_hw_breakpoint */
- NULL, /* supports_stopped_by_hw_breakpoint */
- target_can_do_hardware_single_step,
- win32_stopped_by_watchpoint,
- win32_stopped_data_address,
- NULL, /* read_offsets */
- NULL, /* get_tls_address */
-#ifdef _WIN32_WCE
- wince_hostio_last_error,
-#else
- hostio_last_error_from_errno,
-#endif
- NULL, /* qxfer_osdata */
- win32_xfer_siginfo,
- NULL, /* supports_non_stop */
- NULL, /* async */
- NULL, /* start_non_stop */
- NULL, /* supports_multi_process */
- NULL, /* supports_fork_events */
- NULL, /* supports_vfork_events */
- NULL, /* supports_exec_events */
- NULL, /* handle_new_gdb_connection */
- NULL, /* handle_monitor_command */
- NULL, /* core_of_thread */
- NULL, /* read_loadmap */
- NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
- NULL, /* read_pc */
- NULL, /* write_pc */
- NULL, /* thread_stopped */
- win32_get_tib_address,
- NULL, /* pause_all */
- NULL, /* unpause_all */
- NULL, /* stabilize_threads */
- NULL, /* install_fast_tracepoint_jump_pad */
- NULL, /* emit_ops */
- NULL, /* supports_disable_randomization */
- NULL, /* get_min_fast_tracepoint_insn_len */
- NULL, /* qxfer_libraries_svr4 */
- NULL, /* support_agent */
- NULL, /* enable_btrace */
- NULL, /* disable_btrace */
- NULL, /* read_btrace */
- NULL, /* read_btrace_conf */
- NULL, /* supports_range_stepping */
- NULL, /* pid_to_exec_file */
- NULL, /* multifs_open */
- NULL, /* multifs_unlink */
- NULL, /* multifs_readlink */
- NULL, /* breakpoint_kind_from_pc */
- win32_sw_breakpoint_from_kind,
-};
-
-/* Initialize the Win32 backend. */
-void
-initialize_low (void)
-{
- set_target_ops (&win32_target_ops);
- the_low_target.arch_setup ();
-}
--- /dev/null
+/* Low level interface to Windows debugging, for gdbserver.
+ Copyright (C) 2006-2020 Free Software Foundation, Inc.
+
+ Contributed by Leo Zayas. Based on "win32-nat.c" from GDB.
+
+ 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 "regcache.h"
+#include "gdb/fileio.h"
+#include "mem-break.h"
+#include "win32-low.h"
+#include "gdbthread.h"
+#include "dll.h"
+#include "hostio.h"
+#include <windows.h>
+#include <winnt.h>
+#include <imagehlp.h>
+#include <tlhelp32.h>
+#include <psapi.h>
+#include <process.h>
+#include "gdbsupport/gdb_tilde_expand.h"
+#include "gdbsupport/common-inferior.h"
+#include "gdbsupport/gdb_wait.h"
+
+#ifndef USE_WIN32API
+#include <sys/cygwin.h>
+#endif
+
+#define OUTMSG(X) do { printf X; fflush (stderr); } while (0)
+
+#define OUTMSG2(X) \
+ do \
+ { \
+ if (debug_threads) \
+ { \
+ printf X; \
+ fflush (stderr); \
+ } \
+ } while (0)
+
+#ifndef _T
+#define _T(x) TEXT (x)
+#endif
+
+#ifndef COUNTOF
+#define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0]))
+#endif
+
+#ifdef _WIN32_WCE
+# define GETPROCADDRESS(DLL, PROC) \
+ ((winapi_ ## PROC) GetProcAddress (DLL, TEXT (#PROC)))
+#else
+# define GETPROCADDRESS(DLL, PROC) \
+ ((winapi_ ## PROC) GetProcAddress (DLL, #PROC))
+#endif
+
+int using_threads = 1;
+
+/* Globals. */
+static int attaching = 0;
+static HANDLE current_process_handle = NULL;
+static DWORD current_process_id = 0;
+static DWORD main_thread_id = 0;
+static EXCEPTION_RECORD siginfo_er; /* Contents of $_siginfo */
+static enum gdb_signal last_sig = GDB_SIGNAL_0;
+
+/* The current debug event from WaitForDebugEvent. */
+static DEBUG_EVENT current_event;
+
+/* A status that hasn't been reported to the core yet, and so
+ win32_wait should return it next, instead of fetching the next
+ debug event off the win32 API. */
+static struct target_waitstatus cached_status;
+
+/* Non zero if an interrupt request is to be satisfied by suspending
+ all threads. */
+static int soft_interrupt_requested = 0;
+
+/* Non zero if the inferior is stopped in a simulated breakpoint done
+ by suspending all the threads. */
+static int faked_breakpoint = 0;
+
+const struct target_desc *win32_tdesc;
+
+#define NUM_REGS (the_low_target.num_regs)
+
+typedef BOOL (WINAPI *winapi_DebugActiveProcessStop) (DWORD dwProcessId);
+typedef BOOL (WINAPI *winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit);
+typedef BOOL (WINAPI *winapi_DebugBreakProcess) (HANDLE);
+typedef BOOL (WINAPI *winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);
+
+static ptid_t win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+ int options);
+static void win32_resume (struct thread_resume *resume_info, size_t n);
+#ifndef _WIN32_WCE
+static void win32_add_all_dlls (void);
+#endif
+
+/* Get the thread ID from the current selected inferior (the current
+ thread). */
+static ptid_t
+current_thread_ptid (void)
+{
+ return current_ptid;
+}
+
+/* The current debug event from WaitForDebugEvent. */
+static ptid_t
+debug_event_ptid (DEBUG_EVENT *event)
+{
+ return ptid_t (event->dwProcessId, event->dwThreadId, 0);
+}
+
+/* Get the thread context of the thread associated with TH. */
+
+static void
+win32_get_thread_context (win32_thread_info *th)
+{
+ memset (&th->context, 0, sizeof (CONTEXT));
+ (*the_low_target.get_thread_context) (th);
+#ifdef _WIN32_WCE
+ memcpy (&th->base_context, &th->context, sizeof (CONTEXT));
+#endif
+}
+
+/* Set the thread context of the thread associated with TH. */
+
+static void
+win32_set_thread_context (win32_thread_info *th)
+{
+#ifdef _WIN32_WCE
+ /* Calling SuspendThread on a thread that is running kernel code
+ will report that the suspending was successful, but in fact, that
+ will often not be true. In those cases, the context returned by
+ GetThreadContext will not be correct by the time the thread
+ stops, hence we can't set that context back into the thread when
+ resuming - it will most likely crash the inferior.
+ Unfortunately, there is no way to know when the thread will
+ really stop. To work around it, we'll only write the context
+ back to the thread when either the user or GDB explicitly change
+ it between stopping and resuming. */
+ if (memcmp (&th->context, &th->base_context, sizeof (CONTEXT)) != 0)
+#endif
+ SetThreadContext (th->h, &th->context);
+}
+
+/* Set the thread context of the thread associated with TH. */
+
+static void
+win32_prepare_to_resume (win32_thread_info *th)
+{
+ if (the_low_target.prepare_to_resume != NULL)
+ (*the_low_target.prepare_to_resume) (th);
+}
+
+/* See win32-low.h. */
+
+void
+win32_require_context (win32_thread_info *th)
+{
+ if (th->context.ContextFlags == 0)
+ {
+ if (!th->suspended)
+ {
+ if (SuspendThread (th->h) == (DWORD) -1)
+ {
+ DWORD err = GetLastError ();
+ OUTMSG (("warning: SuspendThread failed in thread_rec, "
+ "(error %d): %s\n", (int) err, strwinerror (err)));
+ }
+ else
+ th->suspended = 1;
+ }
+
+ win32_get_thread_context (th);
+ }
+}
+
+/* Find a thread record given a thread id. If GET_CONTEXT is set then
+ also retrieve the context for this thread. */
+static win32_thread_info *
+thread_rec (ptid_t ptid, int get_context)
+{
+ thread_info *thread = find_thread_ptid (ptid);
+ if (thread == NULL)
+ return NULL;
+
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+ if (get_context)
+ win32_require_context (th);
+ return th;
+}
+
+/* Add a thread to the thread list. */
+static win32_thread_info *
+child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
+{
+ win32_thread_info *th;
+ ptid_t ptid = ptid_t (pid, tid, 0);
+
+ if ((th = thread_rec (ptid, FALSE)))
+ return th;
+
+ th = XCNEW (win32_thread_info);
+ th->tid = tid;
+ th->h = h;
+ th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb;
+
+ add_thread (ptid, th);
+
+ if (the_low_target.thread_added != NULL)
+ (*the_low_target.thread_added) (th);
+
+ return th;
+}
+
+/* Delete a thread from the list of threads. */
+static void
+delete_thread_info (thread_info *thread)
+{
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+
+ remove_thread (thread);
+ CloseHandle (th->h);
+ free (th);
+}
+
+/* Delete a thread from the list of threads. */
+static void
+child_delete_thread (DWORD pid, DWORD tid)
+{
+ /* If the last thread is exiting, just return. */
+ if (all_threads.size () == 1)
+ return;
+
+ thread_info *thread = find_thread_ptid (ptid_t (pid, tid));
+ if (thread == NULL)
+ return;
+
+ delete_thread_info (thread);
+}
+
+/* These watchpoint related wrapper functions simply pass on the function call
+ if the low target has registered a corresponding function. */
+
+static int
+win32_supports_z_point_type (char z_type)
+{
+ return (the_low_target.supports_z_point_type != NULL
+ && the_low_target.supports_z_point_type (z_type));
+}
+
+static int
+win32_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ if (the_low_target.insert_point != NULL)
+ return the_low_target.insert_point (type, addr, size, bp);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+win32_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ if (the_low_target.remove_point != NULL)
+ return the_low_target.remove_point (type, addr, size, bp);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+win32_stopped_by_watchpoint (void)
+{
+ if (the_low_target.stopped_by_watchpoint != NULL)
+ return the_low_target.stopped_by_watchpoint ();
+ else
+ return 0;
+}
+
+static CORE_ADDR
+win32_stopped_data_address (void)
+{
+ if (the_low_target.stopped_data_address != NULL)
+ return the_low_target.stopped_data_address ();
+ else
+ return 0;
+}
+
+
+/* Transfer memory from/to the debugged process. */
+static int
+child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
+ int write, process_stratum_target *target)
+{
+ BOOL success;
+ SIZE_T done = 0;
+ DWORD lasterror = 0;
+ uintptr_t addr = (uintptr_t) memaddr;
+
+ if (write)
+ {
+ success = WriteProcessMemory (current_process_handle, (LPVOID) addr,
+ (LPCVOID) our, len, &done);
+ if (!success)
+ lasterror = GetLastError ();
+ FlushInstructionCache (current_process_handle, (LPCVOID) addr, len);
+ }
+ else
+ {
+ success = ReadProcessMemory (current_process_handle, (LPCVOID) addr,
+ (LPVOID) our, len, &done);
+ if (!success)
+ lasterror = GetLastError ();
+ }
+ if (!success && lasterror == ERROR_PARTIAL_COPY && done > 0)
+ return done;
+ else
+ return success ? done : -1;
+}
+
+/* Clear out any old thread list and reinitialize it to a pristine
+ state. */
+static void
+child_init_thread_list (void)
+{
+ for_each_thread (delete_thread_info);
+}
+
+/* Zero during the child initialization phase, and nonzero otherwise. */
+
+static int child_initialization_done = 0;
+
+static void
+do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
+{
+ struct process_info *proc;
+
+ last_sig = GDB_SIGNAL_0;
+
+ current_process_handle = proch;
+ current_process_id = pid;
+ main_thread_id = 0;
+
+ soft_interrupt_requested = 0;
+ faked_breakpoint = 0;
+
+ memset (¤t_event, 0, sizeof (current_event));
+
+ proc = add_process (pid, attached);
+ proc->tdesc = win32_tdesc;
+ child_init_thread_list ();
+ child_initialization_done = 0;
+
+ if (the_low_target.initial_stuff != NULL)
+ (*the_low_target.initial_stuff) ();
+
+ cached_status.kind = TARGET_WAITKIND_IGNORE;
+
+ /* Flush all currently pending debug events (thread and dll list) up
+ to the initial breakpoint. */
+ while (1)
+ {
+ struct target_waitstatus status;
+
+ win32_wait (minus_one_ptid, &status, 0);
+
+ /* Note win32_wait doesn't return thread events. */
+ if (status.kind != TARGET_WAITKIND_LOADED)
+ {
+ cached_status = status;
+ break;
+ }
+
+ {
+ struct thread_resume resume;
+
+ resume.thread = minus_one_ptid;
+ resume.kind = resume_continue;
+ resume.sig = 0;
+
+ win32_resume (&resume, 1);
+ }
+ }
+
+#ifndef _WIN32_WCE
+ /* Now that the inferior has been started and all DLLs have been mapped,
+ we can iterate over all DLLs and load them in.
+
+ We avoid doing it any earlier because, on certain versions of Windows,
+ LOAD_DLL_DEBUG_EVENTs are sometimes not complete. In particular,
+ we have seen on Windows 8.1 that the ntdll.dll load event does not
+ include the DLL name, preventing us from creating an associated SO.
+ A possible explanation is that ntdll.dll might be mapped before
+ the SO info gets created by the Windows system -- ntdll.dll is
+ the first DLL to be reported via LOAD_DLL_DEBUG_EVENT and other DLLs
+ do not seem to suffer from that problem.
+
+ Rather than try to work around this sort of issue, it is much
+ simpler to just ignore DLL load/unload events during the startup
+ phase, and then process them all in one batch now. */
+ win32_add_all_dlls ();
+#endif
+
+ child_initialization_done = 1;
+}
+
+/* Resume all artificially suspended threads if we are continuing
+ execution. */
+static void
+continue_one_thread (thread_info *thread, int thread_id)
+{
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+
+ if (thread_id == -1 || thread_id == th->tid)
+ {
+ win32_prepare_to_resume (th);
+
+ if (th->suspended)
+ {
+ if (th->context.ContextFlags)
+ {
+ win32_set_thread_context (th);
+ th->context.ContextFlags = 0;
+ }
+
+ if (ResumeThread (th->h) == (DWORD) -1)
+ {
+ DWORD err = GetLastError ();
+ OUTMSG (("warning: ResumeThread failed in continue_one_thread, "
+ "(error %d): %s\n", (int) err, strwinerror (err)));
+ }
+ th->suspended = 0;
+ }
+ }
+}
+
+static BOOL
+child_continue (DWORD continue_status, int thread_id)
+{
+ /* The inferior will only continue after the ContinueDebugEvent
+ call. */
+ for_each_thread ([&] (thread_info *thread)
+ {
+ continue_one_thread (thread, thread_id);
+ });
+ faked_breakpoint = 0;
+
+ if (!ContinueDebugEvent (current_event.dwProcessId,
+ current_event.dwThreadId,
+ continue_status))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Fetch register(s) from the current thread context. */
+static void
+child_fetch_inferior_registers (struct regcache *regcache, int r)
+{
+ int regno;
+ win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
+ if (r == -1 || r > NUM_REGS)
+ child_fetch_inferior_registers (regcache, NUM_REGS);
+ else
+ for (regno = 0; regno < r; regno++)
+ (*the_low_target.fetch_inferior_register) (regcache, th, regno);
+}
+
+/* Store a new register value into the current thread context. We don't
+ change the program's context until later, when we resume it. */
+static void
+child_store_inferior_registers (struct regcache *regcache, int r)
+{
+ int regno;
+ win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
+ if (r == -1 || r == 0 || r > NUM_REGS)
+ child_store_inferior_registers (regcache, NUM_REGS);
+ else
+ for (regno = 0; regno < r; regno++)
+ (*the_low_target.store_inferior_register) (regcache, th, regno);
+}
+
+/* Map the Windows error number in ERROR to a locale-dependent error
+ message string and return a pointer to it. Typically, the values
+ for ERROR come from GetLastError.
+
+ The string pointed to shall not be modified by the application,
+ but may be overwritten by a subsequent call to strwinerror
+
+ The strwinerror function does not change the current setting
+ of GetLastError. */
+
+char *
+strwinerror (DWORD error)
+{
+ static char buf[1024];
+ TCHAR *msgbuf;
+ DWORD lasterr = GetLastError ();
+ DWORD chars = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL,
+ error,
+ 0, /* Default language */
+ (LPTSTR) &msgbuf,
+ 0,
+ NULL);
+ if (chars != 0)
+ {
+ /* If there is an \r\n appended, zap it. */
+ if (chars >= 2
+ && msgbuf[chars - 2] == '\r'
+ && msgbuf[chars - 1] == '\n')
+ {
+ chars -= 2;
+ msgbuf[chars] = 0;
+ }
+
+ if (chars > ((COUNTOF (buf)) - 1))
+ {
+ chars = COUNTOF (buf) - 1;
+ msgbuf [chars] = 0;
+ }
+
+#ifdef UNICODE
+ wcstombs (buf, msgbuf, chars + 1);
+#else
+ strncpy (buf, msgbuf, chars + 1);
+#endif
+ LocalFree (msgbuf);
+ }
+ else
+ sprintf (buf, "unknown win32 error (%u)", (unsigned) error);
+
+ SetLastError (lasterr);
+ return buf;
+}
+
+static BOOL
+create_process (const char *program, char *args,
+ DWORD flags, PROCESS_INFORMATION *pi)
+{
+ const char *inferior_cwd = get_inferior_cwd ();
+ BOOL ret;
+
+#ifdef _WIN32_WCE
+ wchar_t *p, *wprogram, *wargs, *wcwd = NULL;
+ size_t argslen;
+
+ wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
+ mbstowcs (wprogram, program, strlen (program) + 1);
+
+ for (p = wprogram; *p; ++p)
+ if (L'/' == *p)
+ *p = L'\\';
+
+ argslen = strlen (args);
+ wargs = alloca ((argslen + 1) * sizeof (wchar_t));
+ mbstowcs (wargs, args, argslen + 1);
+
+ if (inferior_cwd != NULL)
+ {
+ std::string expanded_infcwd = gdb_tilde_expand (inferior_cwd);
+ std::replace (expanded_infcwd.begin (), expanded_infcwd.end (),
+ '/', '\\');
+ wcwd = alloca ((expanded_infcwd.size () + 1) * sizeof (wchar_t));
+ if (mbstowcs (wcwd, expanded_infcwd.c_str (),
+ expanded_infcwd.size () + 1) == NULL)
+ {
+ error (_("\
+Could not convert the expanded inferior cwd to wide-char."));
+ }
+ }
+
+ ret = CreateProcessW (wprogram, /* image name */
+ wargs, /* command line */
+ NULL, /* security, not supported */
+ NULL, /* thread, not supported */
+ FALSE, /* inherit handles, not supported */
+ flags, /* start flags */
+ NULL, /* environment, not supported */
+ wcwd, /* current directory */
+ NULL, /* start info, not supported */
+ pi); /* proc info */
+#else
+ STARTUPINFOA si = { sizeof (STARTUPINFOA) };
+
+ ret = CreateProcessA (program, /* image name */
+ args, /* command line */
+ NULL, /* security */
+ NULL, /* thread */
+ TRUE, /* inherit handles */
+ flags, /* start flags */
+ NULL, /* environment */
+ /* current directory */
+ (inferior_cwd == NULL
+ ? NULL
+ : gdb_tilde_expand (inferior_cwd).c_str()),
+ &si, /* start info */
+ pi); /* proc info */
+#endif
+
+ return ret;
+}
+
+/* Start a new process.
+ PROGRAM is the program name.
+ PROGRAM_ARGS is the vector containing the inferior's args.
+ Returns the new PID on success, -1 on failure. Registers the new
+ process with the process list. */
+static int
+win32_create_inferior (const char *program,
+ const std::vector<char *> &program_args)
+{
+ client_state &cs = get_client_state ();
+#ifndef USE_WIN32API
+ char real_path[PATH_MAX];
+ char *orig_path, *new_path, *path_ptr;
+#endif
+ BOOL ret;
+ DWORD flags;
+ PROCESS_INFORMATION pi;
+ DWORD err;
+ std::string str_program_args = stringify_argv (program_args);
+ char *args = (char *) str_program_args.c_str ();
+
+ /* win32_wait needs to know we're not attaching. */
+ attaching = 0;
+
+ if (!program)
+ error ("No executable specified, specify executable to debug.\n");
+
+ flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
+
+#ifndef USE_WIN32API
+ orig_path = NULL;
+ path_ptr = getenv ("PATH");
+ if (path_ptr)
+ {
+ int size = cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, NULL, 0);
+ orig_path = (char *) alloca (strlen (path_ptr) + 1);
+ new_path = (char *) alloca (size);
+ strcpy (orig_path, path_ptr);
+ cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, new_path, size);
+ setenv ("PATH", new_path, 1);
+ }
+ cygwin_conv_path (CCP_POSIX_TO_WIN_A, program, real_path, PATH_MAX);
+ program = real_path;
+#endif
+
+ OUTMSG2 (("Command line is \"%s\"\n", args));
+
+#ifdef CREATE_NEW_PROCESS_GROUP
+ flags |= CREATE_NEW_PROCESS_GROUP;
+#endif
+
+ ret = create_process (program, args, flags, &pi);
+ err = GetLastError ();
+ if (!ret && err == ERROR_FILE_NOT_FOUND)
+ {
+ char *exename = (char *) alloca (strlen (program) + 5);
+ strcat (strcpy (exename, program), ".exe");
+ ret = create_process (exename, args, flags, &pi);
+ err = GetLastError ();
+ }
+
+#ifndef USE_WIN32API
+ if (orig_path)
+ setenv ("PATH", orig_path, 1);
+#endif
+
+ if (!ret)
+ {
+ error ("Error creating process \"%s%s\", (error %d): %s\n",
+ program, args, (int) err, strwinerror (err));
+ }
+ else
+ {
+ OUTMSG2 (("Process created: %s\n", (char *) args));
+ }
+
+#ifndef _WIN32_WCE
+ /* On Windows CE this handle can't be closed. The OS reuses
+ it in the debug events, while the 9x/NT versions of Windows
+ probably use a DuplicateHandle'd one. */
+ CloseHandle (pi.hThread);
+#endif
+
+ do_initial_child_stuff (pi.hProcess, pi.dwProcessId, 0);
+
+ /* Wait till we are at 1st instruction in program, return new pid
+ (assuming success). */
+ cs.last_ptid = win32_wait (ptid_t (current_process_id), &cs.last_status, 0);
+
+ /* Necessary for handle_v_kill. */
+ signal_pid = current_process_id;
+
+ return current_process_id;
+}
+
+/* Attach to a running process.
+ PID is the process ID to attach to, specified by the user
+ or a higher layer. */
+static int
+win32_attach (unsigned long pid)
+{
+ HANDLE h;
+ winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
+ DWORD err;
+#ifdef _WIN32_WCE
+ HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
+#else
+ HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#endif
+ DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
+
+ h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+ if (h != NULL)
+ {
+ if (DebugActiveProcess (pid))
+ {
+ if (DebugSetProcessKillOnExit != NULL)
+ DebugSetProcessKillOnExit (FALSE);
+
+ /* win32_wait needs to know we're attaching. */
+ attaching = 1;
+ do_initial_child_stuff (h, pid, 1);
+ return 0;
+ }
+
+ CloseHandle (h);
+ }
+
+ err = GetLastError ();
+ error ("Attach to process failed (error %d): %s\n",
+ (int) err, strwinerror (err));
+}
+
+/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */
+static void
+handle_output_debug_string (void)
+{
+#define READ_BUFFER_LEN 1024
+ CORE_ADDR addr;
+ char s[READ_BUFFER_LEN + 1] = { 0 };
+ DWORD nbytes = current_event.u.DebugString.nDebugStringLength;
+
+ if (nbytes == 0)
+ return;
+
+ if (nbytes > READ_BUFFER_LEN)
+ nbytes = READ_BUFFER_LEN;
+
+ addr = (CORE_ADDR) (size_t) current_event.u.DebugString.lpDebugStringData;
+
+ if (current_event.u.DebugString.fUnicode)
+ {
+ /* The event tells us how many bytes, not chars, even
+ in Unicode. */
+ WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 };
+ if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0)
+ return;
+ wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR));
+ }
+ else
+ {
+ if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0)
+ return;
+ }
+
+ if (!startswith (s, "cYg"))
+ {
+ if (!server_waiting)
+ {
+ OUTMSG2(("%s", s));
+ return;
+ }
+
+ monitor_output (s);
+ }
+#undef READ_BUFFER_LEN
+}
+
+static void
+win32_clear_inferiors (void)
+{
+ if (current_process_handle != NULL)
+ CloseHandle (current_process_handle);
+
+ for_each_thread (delete_thread_info);
+ siginfo_er.ExceptionCode = 0;
+ clear_inferiors ();
+}
+
+/* Implementation of target_ops::kill. */
+
+static int
+win32_kill (process_info *process)
+{
+ TerminateProcess (current_process_handle, 0);
+ for (;;)
+ {
+ if (!child_continue (DBG_CONTINUE, -1))
+ break;
+ if (!WaitForDebugEvent (¤t_event, INFINITE))
+ break;
+ if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
+ break;
+ else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
+ handle_output_debug_string ();
+ }
+
+ win32_clear_inferiors ();
+
+ remove_process (process);
+ return 0;
+}
+
+/* Implementation of target_ops::detach. */
+
+static int
+win32_detach (process_info *process)
+{
+ winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
+ winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
+#ifdef _WIN32_WCE
+ HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
+#else
+ HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#endif
+ DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop);
+ DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
+
+ if (DebugSetProcessKillOnExit == NULL
+ || DebugActiveProcessStop == NULL)
+ return -1;
+
+ {
+ struct thread_resume resume;
+ resume.thread = minus_one_ptid;
+ resume.kind = resume_continue;
+ resume.sig = 0;
+ win32_resume (&resume, 1);
+ }
+
+ if (!DebugActiveProcessStop (current_process_id))
+ return -1;
+
+ DebugSetProcessKillOnExit (FALSE);
+ remove_process (process);
+
+ win32_clear_inferiors ();
+ return 0;
+}
+
+static void
+win32_mourn (struct process_info *process)
+{
+ remove_process (process);
+}
+
+/* Implementation of target_ops::join. */
+
+static void
+win32_join (int pid)
+{
+ HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+ if (h != NULL)
+ {
+ WaitForSingleObject (h, INFINITE);
+ CloseHandle (h);
+ }
+}
+
+/* Return 1 iff the thread with thread ID TID is alive. */
+static int
+win32_thread_alive (ptid_t ptid)
+{
+ /* Our thread list is reliable; don't bother to poll target
+ threads. */
+ return find_thread_ptid (ptid) != NULL;
+}
+
+/* Resume the inferior process. RESUME_INFO describes how we want
+ to resume. */
+static void
+win32_resume (struct thread_resume *resume_info, size_t n)
+{
+ DWORD tid;
+ enum gdb_signal sig;
+ int step;
+ win32_thread_info *th;
+ DWORD continue_status = DBG_CONTINUE;
+ ptid_t ptid;
+
+ /* This handles the very limited set of resume packets that GDB can
+ currently produce. */
+
+ if (n == 1 && resume_info[0].thread == minus_one_ptid)
+ tid = -1;
+ else if (n > 1)
+ tid = -1;
+ else
+ /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make
+ the Windows resume code do the right thing for thread switching. */
+ tid = current_event.dwThreadId;
+
+ if (resume_info[0].thread != minus_one_ptid)
+ {
+ sig = gdb_signal_from_host (resume_info[0].sig);
+ step = resume_info[0].kind == resume_step;
+ }
+ else
+ {
+ sig = GDB_SIGNAL_0;
+ step = 0;
+ }
+
+ if (sig != GDB_SIGNAL_0)
+ {
+ if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
+ {
+ OUTMSG (("Cannot continue with signal %s here.\n",
+ gdb_signal_to_string (sig)));
+ }
+ else if (sig == last_sig)
+ continue_status = DBG_EXCEPTION_NOT_HANDLED;
+ else
+ OUTMSG (("Can only continue with received signal %s.\n",
+ gdb_signal_to_string (last_sig)));
+ }
+
+ last_sig = GDB_SIGNAL_0;
+
+ /* Get context for the currently selected thread. */
+ ptid = debug_event_ptid (¤t_event);
+ th = thread_rec (ptid, FALSE);
+ if (th)
+ {
+ win32_prepare_to_resume (th);
+
+ if (th->context.ContextFlags)
+ {
+ /* Move register values from the inferior into the thread
+ context structure. */
+ regcache_invalidate ();
+
+ if (step)
+ {
+ if (the_low_target.single_step != NULL)
+ (*the_low_target.single_step) (th);
+ else
+ error ("Single stepping is not supported "
+ "in this configuration.\n");
+ }
+
+ win32_set_thread_context (th);
+ th->context.ContextFlags = 0;
+ }
+ }
+
+ /* Allow continuing with the same signal that interrupted us.
+ Otherwise complain. */
+
+ child_continue (continue_status, tid);
+}
+
+static void
+win32_add_one_solib (const char *name, CORE_ADDR load_addr)
+{
+ char buf[MAX_PATH + 1];
+ char buf2[MAX_PATH + 1];
+
+#ifdef _WIN32_WCE
+ WIN32_FIND_DATA w32_fd;
+ WCHAR wname[MAX_PATH + 1];
+ mbstowcs (wname, name, MAX_PATH);
+ HANDLE h = FindFirstFile (wname, &w32_fd);
+#else
+ WIN32_FIND_DATAA w32_fd;
+ HANDLE h = FindFirstFileA (name, &w32_fd);
+#endif
+
+ /* The symbols in a dll are offset by 0x1000, which is the
+ offset from 0 of the first byte in an image - because
+ of the file header and the section alignment. */
+ load_addr += 0x1000;
+
+ if (h == INVALID_HANDLE_VALUE)
+ strcpy (buf, name);
+ else
+ {
+ FindClose (h);
+ strcpy (buf, name);
+#ifndef _WIN32_WCE
+ {
+ char cwd[MAX_PATH + 1];
+ char *p;
+ if (GetCurrentDirectoryA (MAX_PATH + 1, cwd))
+ {
+ p = strrchr (buf, '\\');
+ if (p)
+ p[1] = '\0';
+ SetCurrentDirectoryA (buf);
+ GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p);
+ SetCurrentDirectoryA (cwd);
+ }
+ }
+#endif
+ }
+
+#ifndef _WIN32_WCE
+ if (strcasecmp (buf, "ntdll.dll") == 0)
+ {
+ GetSystemDirectoryA (buf, sizeof (buf));
+ strcat (buf, "\\ntdll.dll");
+ }
+#endif
+
+#ifdef __CYGWIN__
+ cygwin_conv_path (CCP_WIN_A_TO_POSIX, buf, buf2, sizeof (buf2));
+#else
+ strcpy (buf2, buf);
+#endif
+
+ loaded_dll (buf2, load_addr);
+}
+
+static char *
+get_image_name (HANDLE h, void *address, int unicode)
+{
+ static char buf[(2 * MAX_PATH) + 1];
+ DWORD size = unicode ? sizeof (WCHAR) : sizeof (char);
+ char *address_ptr;
+ int len = 0;
+ char b[2];
+ SIZE_T done;
+
+ /* Attempt to read the name of the dll that was detected.
+ This is documented to work only when actively debugging
+ a program. It will not work for attached processes. */
+ if (address == NULL)
+ return NULL;
+
+#ifdef _WIN32_WCE
+ /* Windows CE reports the address of the image name,
+ instead of an address of a pointer into the image name. */
+ address_ptr = address;
+#else
+ /* See if we could read the address of a string, and that the
+ address isn't null. */
+ if (!ReadProcessMemory (h, address, &address_ptr,
+ sizeof (address_ptr), &done)
+ || done != sizeof (address_ptr)
+ || !address_ptr)
+ return NULL;
+#endif
+
+ /* Find the length of the string */
+ while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
+ && (b[0] != 0 || b[size - 1] != 0) && done == size)
+ continue;
+
+ if (!unicode)
+ ReadProcessMemory (h, address_ptr, buf, len, &done);
+ else
+ {
+ WCHAR *unicode_address = XALLOCAVEC (WCHAR, len);
+ ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR),
+ &done);
+
+ WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0);
+ }
+
+ return buf;
+}
+
+typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *,
+ DWORD, LPDWORD);
+typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE,
+ LPMODULEINFO, DWORD);
+typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE,
+ LPSTR, DWORD);
+
+static winapi_EnumProcessModules win32_EnumProcessModules;
+static winapi_GetModuleInformation win32_GetModuleInformation;
+static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA;
+
+static BOOL
+load_psapi (void)
+{
+ static int psapi_loaded = 0;
+ static HMODULE dll = NULL;
+
+ if (!psapi_loaded)
+ {
+ psapi_loaded = 1;
+ dll = LoadLibrary (TEXT("psapi.dll"));
+ if (!dll)
+ return FALSE;
+ win32_EnumProcessModules =
+ GETPROCADDRESS (dll, EnumProcessModules);
+ win32_GetModuleInformation =
+ GETPROCADDRESS (dll, GetModuleInformation);
+ win32_GetModuleFileNameExA =
+ GETPROCADDRESS (dll, GetModuleFileNameExA);
+ }
+
+ return (win32_EnumProcessModules != NULL
+ && win32_GetModuleInformation != NULL
+ && win32_GetModuleFileNameExA != NULL);
+}
+
+#ifndef _WIN32_WCE
+
+/* Iterate over all DLLs currently mapped by our inferior, and
+ add them to our list of solibs. */
+
+static void
+win32_add_all_dlls (void)
+{
+ size_t i;
+ HMODULE dh_buf[1];
+ HMODULE *DllHandle = dh_buf;
+ DWORD cbNeeded;
+ BOOL ok;
+
+ if (!load_psapi ())
+ return;
+
+ cbNeeded = 0;
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ sizeof (HMODULE),
+ &cbNeeded);
+
+ if (!ok || !cbNeeded)
+ return;
+
+ DllHandle = (HMODULE *) alloca (cbNeeded);
+ if (!DllHandle)
+ return;
+
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ cbNeeded,
+ &cbNeeded);
+ if (!ok)
+ return;
+
+ for (i = 1; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++)
+ {
+ MODULEINFO mi;
+ char dll_name[MAX_PATH];
+
+ if (!(*win32_GetModuleInformation) (current_process_handle,
+ DllHandle[i],
+ &mi,
+ sizeof (mi)))
+ continue;
+ if ((*win32_GetModuleFileNameExA) (current_process_handle,
+ DllHandle[i],
+ dll_name,
+ MAX_PATH) == 0)
+ continue;
+ win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll);
+ }
+}
+#endif
+
+typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD);
+typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32);
+typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32);
+
+/* Handle a DLL load event.
+
+ This function assumes that this event did not occur during inferior
+ initialization, where their event info may be incomplete (see
+ do_initial_child_stuff and win32_add_all_dlls for more info on
+ how we handle DLL loading during that phase). */
+
+static void
+handle_load_dll (void)
+{
+ LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
+ char *dll_name;
+
+ dll_name = get_image_name (current_process_handle,
+ event->lpImageName, event->fUnicode);
+ if (!dll_name)
+ return;
+
+ win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) event->lpBaseOfDll);
+}
+
+/* Handle a DLL unload event.
+
+ This function assumes that this event did not occur during inferior
+ initialization, where their event info may be incomplete (see
+ do_initial_child_stuff and win32_add_one_solib for more info
+ on how we handle DLL loading during that phase). */
+
+static void
+handle_unload_dll (void)
+{
+ CORE_ADDR load_addr =
+ (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll;
+
+ /* The symbols in a dll are offset by 0x1000, which is the
+ offset from 0 of the first byte in an image - because
+ of the file header and the section alignment. */
+ load_addr += 0x1000;
+ unloaded_dll (NULL, load_addr);
+}
+
+static void
+handle_exception (struct target_waitstatus *ourstatus)
+{
+ DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode;
+
+ memcpy (&siginfo_er, ¤t_event.u.Exception.ExceptionRecord,
+ sizeof siginfo_er);
+
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+ switch (code)
+ {
+ case EXCEPTION_ACCESS_VIOLATION:
+ OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION"));
+ ourstatus->value.sig = GDB_SIGNAL_SEGV;
+ break;
+ case STATUS_STACK_OVERFLOW:
+ OUTMSG2 (("STATUS_STACK_OVERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_SEGV;
+ break;
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_INEXACT_RESULT:
+ OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_INVALID_OPERATION:
+ OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_OVERFLOW:
+ OUTMSG2 (("STATUS_FLOAT_OVERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_STACK_CHECK:
+ OUTMSG2 (("STATUS_FLOAT_STACK_CHECK"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_UNDERFLOW:
+ OUTMSG2 (("STATUS_FLOAT_UNDERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_INTEGER_DIVIDE_BY_ZERO:
+ OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_INTEGER_OVERFLOW:
+ OUTMSG2 (("STATUS_INTEGER_OVERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case EXCEPTION_BREAKPOINT:
+ OUTMSG2 (("EXCEPTION_BREAKPOINT"));
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+#ifdef _WIN32_WCE
+ /* Remove the initial breakpoint. */
+ check_breakpoints ((CORE_ADDR) (long) current_event
+ .u.Exception.ExceptionRecord.ExceptionAddress);
+#endif
+ break;
+ case DBG_CONTROL_C:
+ OUTMSG2 (("DBG_CONTROL_C"));
+ ourstatus->value.sig = GDB_SIGNAL_INT;
+ break;
+ case DBG_CONTROL_BREAK:
+ OUTMSG2 (("DBG_CONTROL_BREAK"));
+ ourstatus->value.sig = GDB_SIGNAL_INT;
+ break;
+ case EXCEPTION_SINGLE_STEP:
+ OUTMSG2 (("EXCEPTION_SINGLE_STEP"));
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ break;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION"));
+ ourstatus->value.sig = GDB_SIGNAL_ILL;
+ break;
+ case EXCEPTION_PRIV_INSTRUCTION:
+ OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION"));
+ ourstatus->value.sig = GDB_SIGNAL_ILL;
+ break;
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION"));
+ ourstatus->value.sig = GDB_SIGNAL_ILL;
+ break;
+ default:
+ if (current_event.u.Exception.dwFirstChance)
+ {
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+ return;
+ }
+ OUTMSG2 (("gdbserver: unknown target exception 0x%08x at 0x%s",
+ (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode,
+ phex_nz ((uintptr_t) current_event.u.Exception.ExceptionRecord.
+ ExceptionAddress, sizeof (uintptr_t))));
+ ourstatus->value.sig = GDB_SIGNAL_UNKNOWN;
+ break;
+ }
+ OUTMSG2 (("\n"));
+ last_sig = ourstatus->value.sig;
+}
+
+
+static void
+suspend_one_thread (thread_info *thread)
+{
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+
+ if (!th->suspended)
+ {
+ if (SuspendThread (th->h) == (DWORD) -1)
+ {
+ DWORD err = GetLastError ();
+ OUTMSG (("warning: SuspendThread failed in suspend_one_thread, "
+ "(error %d): %s\n", (int) err, strwinerror (err)));
+ }
+ else
+ th->suspended = 1;
+ }
+}
+
+static void
+fake_breakpoint_event (void)
+{
+ OUTMSG2(("fake_breakpoint_event\n"));
+
+ faked_breakpoint = 1;
+
+ memset (¤t_event, 0, sizeof (current_event));
+ current_event.dwThreadId = main_thread_id;
+ current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
+ current_event.u.Exception.ExceptionRecord.ExceptionCode
+ = EXCEPTION_BREAKPOINT;
+
+ for_each_thread (suspend_one_thread);
+}
+
+#ifdef _WIN32_WCE
+static int
+auto_delete_breakpoint (CORE_ADDR stop_pc)
+{
+ return 1;
+}
+#endif
+
+/* Get the next event from the child. */
+
+static int
+get_child_debug_event (struct target_waitstatus *ourstatus)
+{
+ ptid_t ptid;
+
+ last_sig = GDB_SIGNAL_0;
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+
+ /* Check if GDB sent us an interrupt request. */
+ check_remote_input_interrupt_request ();
+
+ if (soft_interrupt_requested)
+ {
+ soft_interrupt_requested = 0;
+ fake_breakpoint_event ();
+ goto gotevent;
+ }
+
+#ifndef _WIN32_WCE
+ attaching = 0;
+#else
+ if (attaching)
+ {
+ /* WinCE doesn't set an initial breakpoint automatically. To
+ stop the inferior, we flush all currently pending debug
+ events -- the thread list and the dll list are always
+ reported immediatelly without delay, then, we suspend all
+ threads and pretend we saw a trap at the current PC of the
+ main thread.
+
+ Contrary to desktop Windows, Windows CE *does* report the dll
+ names on LOAD_DLL_DEBUG_EVENTs resulting from a
+ DebugActiveProcess call. This limits the way we can detect
+ if all the dlls have already been reported. If we get a real
+ debug event before leaving attaching, the worst that will
+ happen is the user will see a spurious breakpoint. */
+
+ current_event.dwDebugEventCode = 0;
+ if (!WaitForDebugEvent (¤t_event, 0))
+ {
+ OUTMSG2(("no attach events left\n"));
+ fake_breakpoint_event ();
+ attaching = 0;
+ }
+ else
+ OUTMSG2(("got attach event\n"));
+ }
+ else
+#endif
+ {
+ /* Keep the wait time low enough for comfortable remote
+ interruption, but high enough so gdbserver doesn't become a
+ bottleneck. */
+ if (!WaitForDebugEvent (¤t_event, 250))
+ {
+ DWORD e = GetLastError();
+
+ if (e == ERROR_PIPE_NOT_CONNECTED)
+ {
+ /* This will happen if the loader fails to succesfully
+ load the application, e.g., if the main executable
+ tries to pull in a non-existing export from a
+ DLL. */
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = 1;
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ gotevent:
+
+ switch (current_event.dwDebugEventCode)
+ {
+ case CREATE_THREAD_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT "
+ "for pid=%u tid=%x)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+
+ /* Record the existence of this thread. */
+ child_add_thread (current_event.dwProcessId,
+ current_event.dwThreadId,
+ current_event.u.CreateThread.hThread,
+ current_event.u.CreateThread.lpThreadLocalBase);
+ break;
+
+ case EXIT_THREAD_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ child_delete_thread (current_event.dwProcessId,
+ current_event.dwThreadId);
+
+ current_thread = get_first_thread ();
+ return 1;
+
+ case CREATE_PROCESS_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ CloseHandle (current_event.u.CreateProcessInfo.hFile);
+
+ current_process_handle = current_event.u.CreateProcessInfo.hProcess;
+ main_thread_id = current_event.dwThreadId;
+
+ /* Add the main thread. */
+ child_add_thread (current_event.dwProcessId,
+ main_thread_id,
+ current_event.u.CreateProcessInfo.hThread,
+ current_event.u.CreateProcessInfo.lpThreadLocalBase);
+
+#ifdef _WIN32_WCE
+ if (!attaching)
+ {
+ /* Windows CE doesn't set the initial breakpoint
+ automatically like the desktop versions of Windows do.
+ We add it explicitly here. It will be removed as soon as
+ it is hit. */
+ set_breakpoint_at ((CORE_ADDR) (long) current_event.u
+ .CreateProcessInfo.lpStartAddress,
+ auto_delete_breakpoint);
+ }
+#endif
+ break;
+
+ case EXIT_PROCESS_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ {
+ DWORD exit_status = current_event.u.ExitProcess.dwExitCode;
+ /* If the exit status looks like a fatal exception, but we
+ don't recognize the exception's code, make the original
+ exit status value available, to avoid losing information. */
+ int exit_signal
+ = WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1;
+ if (exit_signal == -1)
+ {
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = exit_status;
+ }
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = gdb_signal_from_host (exit_signal);
+ }
+ }
+ child_continue (DBG_CONTINUE, -1);
+ CloseHandle (current_process_handle);
+ current_process_handle = NULL;
+ break;
+
+ case LOAD_DLL_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ CloseHandle (current_event.u.LoadDll.hFile);
+ if (! child_initialization_done)
+ break;
+ handle_load_dll ();
+
+ ourstatus->kind = TARGET_WAITKIND_LOADED;
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ break;
+
+ case UNLOAD_DLL_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ if (! child_initialization_done)
+ break;
+ handle_unload_dll ();
+ ourstatus->kind = TARGET_WAITKIND_LOADED;
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ break;
+
+ case EXCEPTION_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ handle_exception (ourstatus);
+ break;
+
+ case OUTPUT_DEBUG_STRING_EVENT:
+ /* A message from the kernel (or Cygwin). */
+ OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ handle_output_debug_string ();
+ break;
+
+ default:
+ OUTMSG2 (("gdbserver: kernel event unknown "
+ "for pid=%u tid=%x code=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ (unsigned) current_event.dwDebugEventCode));
+ break;
+ }
+
+ ptid = debug_event_ptid (¤t_event);
+ current_thread = find_thread_ptid (ptid);
+ return 1;
+}
+
+/* Wait for the inferior process to change state.
+ STATUS will be filled in with a response code to send to GDB.
+ Returns the signal which caused the process to stop. */
+static ptid_t
+win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
+{
+ struct regcache *regcache;
+
+ if (cached_status.kind != TARGET_WAITKIND_IGNORE)
+ {
+ /* The core always does a wait after creating the inferior, and
+ do_initial_child_stuff already ran the inferior to the
+ initial breakpoint (or an exit, if creating the process
+ fails). Report it now. */
+ *ourstatus = cached_status;
+ cached_status.kind = TARGET_WAITKIND_IGNORE;
+ return debug_event_ptid (¤t_event);
+ }
+
+ while (1)
+ {
+ if (!get_child_debug_event (ourstatus))
+ continue;
+
+ switch (ourstatus->kind)
+ {
+ case TARGET_WAITKIND_EXITED:
+ OUTMSG2 (("Child exited with retcode = %x\n",
+ ourstatus->value.integer));
+ win32_clear_inferiors ();
+ return ptid_t (current_event.dwProcessId);
+ case TARGET_WAITKIND_STOPPED:
+ case TARGET_WAITKIND_SIGNALLED:
+ case TARGET_WAITKIND_LOADED:
+ OUTMSG2 (("Child Stopped with signal = %d \n",
+ ourstatus->value.sig));
+
+ regcache = get_thread_regcache (current_thread, 1);
+ child_fetch_inferior_registers (regcache, -1);
+ return debug_event_ptid (¤t_event);
+ default:
+ OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
+ /* fall-through */
+ case TARGET_WAITKIND_SPURIOUS:
+ /* do nothing, just continue */
+ child_continue (DBG_CONTINUE, -1);
+ break;
+ }
+ }
+}
+
+/* Fetch registers from the inferior process.
+ If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */
+static void
+win32_fetch_inferior_registers (struct regcache *regcache, int regno)
+{
+ child_fetch_inferior_registers (regcache, regno);
+}
+
+/* Store registers to the inferior process.
+ If REGNO is -1, store all registers; otherwise, store at least REGNO. */
+static void
+win32_store_inferior_registers (struct regcache *regcache, int regno)
+{
+ child_store_inferior_registers (regcache, regno);
+}
+
+/* Read memory from the inferior process. This should generally be
+ called through read_inferior_memory, which handles breakpoint shadowing.
+ Read LEN bytes at MEMADDR into a buffer at MYADDR. */
+static int
+win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ return child_xfer_memory (memaddr, (char *) myaddr, len, 0, 0) != len;
+}
+
+/* Write memory to the inferior process. This should generally be
+ called through write_inferior_memory, which handles breakpoint shadowing.
+ Write LEN bytes from the buffer at MYADDR to MEMADDR.
+ Returns 0 on success and errno on failure. */
+static int
+win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len)
+{
+ return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len;
+}
+
+/* Send an interrupt request to the inferior process. */
+static void
+win32_request_interrupt (void)
+{
+ winapi_DebugBreakProcess DebugBreakProcess;
+ winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent;
+
+#ifdef _WIN32_WCE
+ HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
+#else
+ HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#endif
+
+ GenerateConsoleCtrlEvent = GETPROCADDRESS (dll, GenerateConsoleCtrlEvent);
+
+ if (GenerateConsoleCtrlEvent != NULL
+ && GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, current_process_id))
+ return;
+
+ /* GenerateConsoleCtrlEvent can fail if process id being debugged is
+ not a process group id.
+ Fallback to XP/Vista 'DebugBreakProcess', which generates a
+ breakpoint exception in the interior process. */
+
+ DebugBreakProcess = GETPROCADDRESS (dll, DebugBreakProcess);
+
+ if (DebugBreakProcess != NULL
+ && DebugBreakProcess (current_process_handle))
+ return;
+
+ /* Last resort, suspend all threads manually. */
+ soft_interrupt_requested = 1;
+}
+
+#ifdef _WIN32_WCE
+int
+win32_error_to_fileio_error (DWORD err)
+{
+ switch (err)
+ {
+ case ERROR_BAD_PATHNAME:
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_INVALID_NAME:
+ case ERROR_PATH_NOT_FOUND:
+ return FILEIO_ENOENT;
+ case ERROR_CRC:
+ case ERROR_IO_DEVICE:
+ case ERROR_OPEN_FAILED:
+ return FILEIO_EIO;
+ case ERROR_INVALID_HANDLE:
+ return FILEIO_EBADF;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ return FILEIO_EACCES;
+ case ERROR_NOACCESS:
+ return FILEIO_EFAULT;
+ case ERROR_BUSY:
+ return FILEIO_EBUSY;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ return FILEIO_EEXIST;
+ case ERROR_BAD_DEVICE:
+ return FILEIO_ENODEV;
+ case ERROR_DIRECTORY:
+ return FILEIO_ENOTDIR;
+ case ERROR_FILENAME_EXCED_RANGE:
+ case ERROR_INVALID_DATA:
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_NEGATIVE_SEEK:
+ return FILEIO_EINVAL;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return FILEIO_EMFILE;
+ case ERROR_HANDLE_DISK_FULL:
+ case ERROR_DISK_FULL:
+ return FILEIO_ENOSPC;
+ case ERROR_WRITE_PROTECT:
+ return FILEIO_EROFS;
+ case ERROR_NOT_SUPPORTED:
+ return FILEIO_ENOSYS;
+ }
+
+ return FILEIO_EUNKNOWN;
+}
+
+static void
+wince_hostio_last_error (char *buf)
+{
+ DWORD winerr = GetLastError ();
+ int fileio_err = win32_error_to_fileio_error (winerr);
+ sprintf (buf, "F-1,%x", fileio_err);
+}
+#endif
+
+/* Write Windows signal info. */
+
+static int
+win32_xfer_siginfo (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf, CORE_ADDR offset, int len)
+{
+ if (siginfo_er.ExceptionCode == 0)
+ return -1;
+
+ if (readbuf == nullptr)
+ return -1;
+
+ if (offset > sizeof (siginfo_er))
+ return -1;
+
+ if (offset + len > sizeof (siginfo_er))
+ len = sizeof (siginfo_er) - offset;
+
+ memcpy (readbuf, (char *) &siginfo_er + offset, len);
+
+ return len;
+}
+
+/* Write Windows OS Thread Information Block address. */
+
+static int
+win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr)
+{
+ win32_thread_info *th;
+ th = thread_rec (ptid, 0);
+ if (th == NULL)
+ return 0;
+ if (addr != NULL)
+ *addr = th->thread_local_base;
+ return 1;
+}
+
+/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+win32_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = the_low_target.breakpoint_len;
+ return the_low_target.breakpoint;
+}
+
+static process_stratum_target win32_target_ops = {
+ win32_create_inferior,
+ NULL, /* post_create_inferior */
+ win32_attach,
+ win32_kill,
+ win32_detach,
+ win32_mourn,
+ win32_join,
+ win32_thread_alive,
+ win32_resume,
+ win32_wait,
+ win32_fetch_inferior_registers,
+ win32_store_inferior_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
+ win32_read_inferior_memory,
+ win32_write_inferior_memory,
+ NULL, /* lookup_symbols */
+ win32_request_interrupt,
+ NULL, /* read_auxv */
+ win32_supports_z_point_type,
+ win32_insert_point,
+ win32_remove_point,
+ NULL, /* stopped_by_sw_breakpoint */
+ NULL, /* supports_stopped_by_sw_breakpoint */
+ NULL, /* stopped_by_hw_breakpoint */
+ NULL, /* supports_stopped_by_hw_breakpoint */
+ target_can_do_hardware_single_step,
+ win32_stopped_by_watchpoint,
+ win32_stopped_data_address,
+ NULL, /* read_offsets */
+ NULL, /* get_tls_address */
+#ifdef _WIN32_WCE
+ wince_hostio_last_error,
+#else
+ hostio_last_error_from_errno,
+#endif
+ NULL, /* qxfer_osdata */
+ win32_xfer_siginfo,
+ NULL, /* supports_non_stop */
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
+ NULL, /* handle_new_gdb_connection */
+ NULL, /* handle_monitor_command */
+ NULL, /* core_of_thread */
+ NULL, /* read_loadmap */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* read_pc */
+ NULL, /* write_pc */
+ NULL, /* thread_stopped */
+ win32_get_tib_address,
+ NULL, /* pause_all */
+ NULL, /* unpause_all */
+ NULL, /* stabilize_threads */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* supports_disable_randomization */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* qxfer_libraries_svr4 */
+ NULL, /* support_agent */
+ NULL, /* enable_btrace */
+ NULL, /* disable_btrace */
+ NULL, /* read_btrace */
+ NULL, /* read_btrace_conf */
+ NULL, /* supports_range_stepping */
+ NULL, /* pid_to_exec_file */
+ NULL, /* multifs_open */
+ NULL, /* multifs_unlink */
+ NULL, /* multifs_readlink */
+ NULL, /* breakpoint_kind_from_pc */
+ win32_sw_breakpoint_from_kind,
+};
+
+/* Initialize the Win32 backend. */
+void
+initialize_low (void)
+{
+ set_target_ops (&win32_target_ops);
+ the_low_target.arch_setup ();
+}
+++ /dev/null
-/* Compatibility routines for Windows CE.
- Copyright (C) 2007-2020 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 <windows.h>
-
-void
-perror (const char *s)
-{
- if (s && *s)
- fprintf (stderr, "%s: %s\n", s, strwinerror (GetLastError ()));
- else
- fprintf (stderr, "%s\n", strwinerror (GetLastError ()));
-}
-
-void
-to_back_slashes (char *path)
-{
- for (; *path; ++path)
- if ('/' == *path)
- *path = '\\';
-}
--- /dev/null
+/* Compatibility routines for Windows CE.
+ Copyright (C) 2007-2020 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 <windows.h>
+
+void
+perror (const char *s)
+{
+ if (s && *s)
+ fprintf (stderr, "%s: %s\n", s, strwinerror (GetLastError ()));
+ else
+ fprintf (stderr, "%s\n", strwinerror (GetLastError ()));
+}
+
+void
+to_back_slashes (char *path)
+{
+ for (; *path; ++path)
+ if ('/' == *path)
+ *path = '\\';
+}
+++ /dev/null
-/* Low level support for x86 (i386 and x86-64).
-
- Copyright (C) 2009-2020 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 "x86-low.h"
-
-/* Clear the reference counts and forget everything we knew about the
- debug registers. */
-
-void
-x86_low_init_dregs (struct x86_debug_reg_state *state)
-{
- int i;
-
- ALL_DEBUG_ADDRESS_REGISTERS (i)
- {
- state->dr_mirror[i] = 0;
- state->dr_ref_count[i] = 0;
- }
- state->dr_control_mirror = 0;
- state->dr_status_mirror = 0;
-}
--- /dev/null
+/* Low level support for x86 (i386 and x86-64).
+
+ Copyright (C) 2009-2020 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 "x86-low.h"
+
+/* Clear the reference counts and forget everything we knew about the
+ debug registers. */
+
+void
+x86_low_init_dregs (struct x86_debug_reg_state *state)
+{
+ int i;
+
+ ALL_DEBUG_ADDRESS_REGISTERS (i)
+ {
+ state->dr_mirror[i] = 0;
+ state->dr_ref_count[i] = 0;
+ }
+ state->dr_control_mirror = 0;
+ state->dr_status_mirror = 0;
+}
+++ /dev/null
-/* Table mapping between kernel xtregset and GDB register cache.
- Copyright (C) 2007-2020 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/>. */
-
-
-typedef struct {
- int gdb_regnum;
- int gdb_offset;
- int ptrace_cp_offset;
- int ptrace_offset;
- int size;
- int coproc;
- int dbnum;
- char* name
-;} xtensa_regtable_t;
-
-#define XTENSA_ELF_XTREG_SIZE 4
-
-const xtensa_regtable_t xtensa_regmap_table[] = {
- /* gnum,gofs,cpofs,ofs,siz,cp, dbnum, name */
- { 44, 176, 0, 0, 4, -1, 0x020c, "scompare1" },
- { 0 }
-};
--- /dev/null
+/* Table mapping between kernel xtregset and GDB register cache.
+ Copyright (C) 2007-2020 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/>. */
+
+
+typedef struct {
+ int gdb_regnum;
+ int gdb_offset;
+ int ptrace_cp_offset;
+ int ptrace_offset;
+ int size;
+ int coproc;
+ int dbnum;
+ char* name
+;} xtensa_regtable_t;
+
+#define XTENSA_ELF_XTREG_SIZE 4
+
+const xtensa_regtable_t xtensa_regmap_table[] = {
+ /* gnum,gofs,cpofs,ofs,siz,cp, dbnum, name */
+ { 44, 176, 0, 0, 4, -1, 0x020c, "scompare1" },
+ { 0 }
+};