This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 2/4] Fix PR breakpoints/16494: add dprintf-style "agent-call"(gdbserver)


This patch add function "call_function_by_hand" to call a function with
arguments and get their format from fpieces that get from format string.
Like "call_function_by_hand" of GDB, it also need call a lot of functions
of arch.  So I add a lot interfaces to target_ops and linux_target_ops.
Now "call_function_by_hand" of gdbserver just support x86_32 and x86_64 linux.
I think arch functions should put to a file that just have relation with
arch but not OS because most of arch functions is copy from i386-tdep.c
and amd64-tdep.c.  So put arch functions to i386-low.c.
To make sure current arch is 32 or 64, I use:
  if (find_regno_without_fatal (regcache->tdesc, "r9") >= 0)
    {
      /* This is for x86_64.  */
It can work OK even if debug x86_32 program in x86_64 linux.

The code that will call "call_function_by_hand" is ax_printf.  If fn is
not 0, this "printf" is a agent-call printf.  It will get format sting
and push it to the stack of inferior and call function fn with "call_function_by_hand".

Thanks,
Hui

2014-03-04  Hui Zhu  <hui@codesourcery.com>

	PR breakpoint/16494
	* common/format.c (parse_format_string_1): New.
	(parse_format_string): Call "parse_format_string_1".
	* common/format.h (parse_format_string_1): New.

2014-03-04  Hui Zhu  <hui@codesourcery.com>

	PR breakpoint/16494
	* Makefile.in (OBS): Add infcall.o
	* ax.c (infcall.h): New include.
	(ax_printf): Call call_function_by_hand to printf.
	* i386-low.c (format.h): New include.
	(i386_low_frame_align, i386_low_frame_red_zone_size,
	i386_low_push_dummy_code, i386_low_breakpoint_from_pc,
	i386_low_push_dummy_call): New.
	* i386-low.h (i386_low_frame_align, i386_low_frame_red_zone_size,
	i386_low_push_dummy_code, i386_low_breakpoint_from_pc,
	i386_low_push_dummy_call): New.
	* infcall.c: New.
	* infcall.h: New.
	* linux-low.c (infcall.h): New include.
	(linux_wait_1): Call "is_inside_infcall" to make sure if this wait
	is inside inferior call and need retutrn.
	(linux_supports_infcall, linux_get_sp, linux_frame_align,
	linux_frame_red_zone_size, linux_push_dummy_code,
	linux_breakpoint_from_pc, linux_push_dummy_call,
	linux_save_infcall_control_state): New.
	(linux_target_op): Add new functions.
	* linux-low.h (linux_target_ops): Add supports_infcall, get_sp,
	frame_align, frame_red_zone_size, push_dummy_code,
	breakpoint_from_pc and push_dummy_call.
	* linux-x86-low.c (x86_supports_infcall, x86_get_sp): New.
	(the_low_target): Add functions.
	* regcache.c (regcache_dup, find_regno_without_fatal): New.
	* regcache.h (regcache_dup, find_regno_without_fatal): New.
	* server.c (handle_query): If current target inferior call, send
	";DprintfAgentCall+" to GDB.
	* target.h (format_piece): New.
	(target_ops): Add supports_infcall, get_sp, frame_align, inner_than,
	frame_red_zone_size, push_dummy_code, breakpoint_from_pc,
	push_dummy_call, save_infcall_control_state
	and restore_infcall_control_state.
	(target_supports_infcall, target_get_sp, target_frame_align,
	target_inner_than, target_frame_red_zone_size,
	target_push_dummy_code, target_breakpoint_from_pc,
	target_push_dummy_call,target_save_infcall_control_state,
	target_restore_infcall_control_state): New.

--- a/gdb/common/format.c
+++ b/gdb/common/format.c
@@ -27,25 +27,10 @@
#include "format.h" -struct format_piece *
-parse_format_string (const char **arg)
+void
+parse_format_string_1 (const char **arg, char *f)
 {
-  const char *s;
-  char *f, *string;
-  const char *prev_start;
-  const char *percent_loc;
-  char *sub_start, *current_substring;
-  struct format_piece *pieces;
-  int next_frag;
-  int max_pieces;
-  enum argclass this_argclass;
-
-  s = *arg;
-
-  /* Parse the format-control string and copy it into the string STRING,
-     processing some kinds of escape sequence.  */
-
-  f = string = (char *) alloca (strlen (s) + 1);
+  const char *s = *arg;
while (*s != '"' && *s != '\0')
     {
@@ -103,6 +88,22 @@ parse_format_string (const char **arg)
   /* Whether the format string ended with double-quote or zero, we're
      done with it; it's up to callers to complain about syntax.  */
   *arg = s;
+}
+
+struct format_piece *
+parse_format_string (const char **arg)
+{
+  char *f, *string;
+  const char *prev_start;
+  const char *percent_loc;
+  char *sub_start, *current_substring;
+  struct format_piece *pieces;
+  int next_frag;
+  int max_pieces;
+  enum argclass this_argclass;
+
+  string = (char *) alloca (strlen (*arg) + 1);
+  parse_format_string_1 (arg, string);
/* Need extra space for the '\0's. Doubling the size is sufficient. */ --- a/gdb/common/format.h
+++ b/gdb/common/format.h
@@ -48,6 +48,11 @@ struct format_piece
   enum argclass argclass;
 };
+/* Parse the format-control string and copy it into the string F,
+   processing some kinds of escape sequence.  */
+
+extern void parse_format_string_1 (const char **arg, char *f);
+
 /* Return an array of printf fragments found at the given string, and
    rewrite ARG with a pointer to the end of the format string.  */
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -177,7 +177,8 @@ OBS = agent.o ax.o inferiors.o regcache.
       target.o waitstatus.o utils.o debug.o version.o vec.o gdb_vecs.o \
       mem-break.o hostio.o event-loop.o tracepoint.o xml-utils.o \
       common-utils.o ptid.o buffer.o format.o filestuff.o dll.o notif.o \
-      tdesc.o print-utils.o rsp-low.o $(XML_BUILTIN) $(DEPFILES) $(LIBOBJS)
+      tdesc.o print-utils.o rsp-low.o infcall.o $(XML_BUILTIN) $(DEPFILES) \
+      $(LIBOBJS)
 GDBREPLAY_OBS = gdbreplay.o version.o
 GDBSERVER_LIBS = @GDBSERVER_LIBS@
 XM_CLIBS = @LIBS@
--- a/gdb/gdbserver/ax.c
+++ b/gdb/gdbserver/ax.c
@@ -21,6 +21,9 @@
 #include "format.h"
 #include "tracepoint.h"
 #include "rsp-low.h"
+#ifndef IN_PROCESS_AGENT
+#include "infcall.h"
+#endif
static void ax_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2); @@ -821,6 +824,45 @@ ax_printf (CORE_ADDR fn, CORE_ADDR chan,
   if (nargs != nargs_wanted)
     error (_("Wrong number of arguments for specified format-string"));
+#ifndef IN_PROCESS_AGENT
+  if (fn != 0)
+    {
+      char *call_format = (char *) alloca (strlen (format) + 1);
+      CORE_ADDR call_format_addr;
+      CORE_ADDR sp;
+      ULONGEST *call_args;
+
+      /* Push format string to the stack of inferior.  */
+      parse_format_string_1 (&format, call_format);
+      sp = push_inferior_stack((unsigned char *)call_format,
+			       strlen (call_format) + 1,
+			       &call_format_addr);
+
+      /* Setup call_args.  */
+      if (chan == 0)
+        {
+	  ++nargs;
+	  call_args = (ULONGEST *) alloca (sizeof (ULONGEST) * nargs);
+	  call_args[0] = call_format_addr;
+	  memcpy (call_args + 1, args, sizeof (ULONGEST) * (nargs - 1));
+	}
+      else
+	{
+	  nargs += 2;
+	  call_args = (ULONGEST *) alloca (sizeof (ULONGEST) * nargs);
+	  call_args[0] = call_format_addr;
+	  call_args[1] = (ULONGEST) chan;
+	  memcpy (call_args + 2, args, sizeof (ULONGEST) * (nargs - 2));
+	}
+
+      /* Call inferior function FN.  */
+      call_function_by_hand (sp, fn, fpieces, nargs, call_args);
+
+      free_format_pieces (fpieces);
+      return;
+    }
+#endif
+
   i = 0;
   for (fp = 0; fpieces[fp].string != NULL; fp++)
     {
--- a/gdb/gdbserver/i386-low.c
+++ b/gdb/gdbserver/i386-low.c
@@ -21,6 +21,7 @@
 #include "target.h"
 #include "i386-low.h"
 #include "break-common.h"
+#include "format.h"
/* Support for 8-byte wide hw watchpoints. */
 #ifndef TARGET_HAS_DR_LEN_8
@@ -634,3 +635,220 @@ i386_low_stopped_by_watchpoint (struct i
   CORE_ADDR addr = 0;
   return i386_low_stopped_data_address (state, &addr);
 }
+
+CORE_ADDR
+i386_low_frame_align (CORE_ADDR sp)
+{
+  return sp & -(CORE_ADDR)16;
+}
+
+int
+i386_low_frame_red_zone_size (struct regcache *regcache)
+{
+  if (find_regno_without_fatal (regcache->tdesc, "r9") >= 0)
+    {
+      /* This is for x86_64.  */
+      return 128;
+    }
+
+  return 0;
+}
+
+CORE_ADDR
+i386_low_push_dummy_code (CORE_ADDR sp, CORE_ADDR funaddr, ULONGEST *args,
+			  int nargs, CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+			  struct regcache *regcache)
+{
+  *bp_addr = sp - 1;
+  *real_pc = funaddr;
+
+  return sp - 16;
+}
+
+const gdb_byte *
+i386_low_breakpoint_from_pc (CORE_ADDR *pc, int *len)
+{
+  static gdb_byte break_insn[] = { 0xcc }; /* int 3 */
+
+  *len = sizeof (break_insn);
+  return break_insn;
+}
+
+CORE_ADDR
+i386_low_push_dummy_call (CORE_ADDR funaddr, struct regcache *regcache,
+			  CORE_ADDR bp_addr, struct format_piece *fpieces,
+			  int nargs, ULONGEST *args,
+			  CORE_ADDR sp)
+{
+  if (find_regno_without_fatal (regcache->tdesc, "r9") >= 0)
+    {
+      /* This is for x86_64.  */
+      int reg_num = 6;
+      int i;
+      unsigned long val;
+      int write_offset = 0;
+
+      /* Push arguments to registers.  */
+      if (nargs < reg_num)
+	reg_num = nargs;
+      for (i = 0; i < reg_num; i++)
+	{
+	  val = args[i];
+	  switch (i)
+	    {
+	    case 0:
+	      supply_register_by_name (regcache, "rdi", &val);
+	      break;
+	    case 1:
+	      supply_register_by_name (regcache, "rsi", &val);
+	      break;
+	    case 2:
+	      supply_register_by_name (regcache, "rdx", &val);
+	      break;
+	    case 3:
+	      supply_register_by_name (regcache, "rcx", &val);
+	      break;
+	    case 4:
+	      supply_register_by_name (regcache, "r8", &val);
+	      break;
+	    case 5:
+	      supply_register_by_name (regcache, "r9", &val);
+	      break;
+	    }
+	}
+
+      /* Allocate space for the arguments on the stack.  */
+      sp -= (nargs - reg_num) * 8;
+
+      /* The psABI says that "The end of the input argument area shall be
+         aligned on a 16 byte boundary."  */
+      sp &= ~0xf;
+
+      /* Write out the arguments to the stack.  */
+      for (i = 0; i < nargs - reg_num; i++)
+	{
+	  val = args[reg_num + i];
+
+	  write_inferior_memory (sp + write_offset, (unsigned char *) &val,
+				 sizeof (val));
+	  write_offset += sizeof (val);
+	}
+
+      /* The psABI says that "For calls that may call functions that use
+         varargs or stdargs (prototype-less calls or calls to functions
+         containing ellipsis (...) in the declaration) %al is used as
+         hidden argument to specify the number of SSE registers used.  */
+      val = 0;
+      supply_register_by_name (regcache, "rax", &val);
+
+      /* Store return address.  */
+      val = bp_addr;
+      sp -= 8;
+      write_inferior_memory (sp, (unsigned char *) &val, sizeof (val));
+
+      /* Finally, update the stack pointer...  */
+      val = sp;
+      supply_register_by_name (regcache, "rsp", &val);
+
+      /* ...and fake a frame pointer.  */
+      supply_register_by_name (regcache, "rbp", &val);
+
+      sp += 16;
+    }
+  else
+    {
+      /* This is for x86_32.  */
+      int write_pass, i, fp, args_space = 0;
+      unsigned int val;
+
+      /* Determine the total space required for stack space, then push
+         arguments in a second pass.  */
+      for (write_pass = 0; write_pass < 2; write_pass++)
+	{
+	  int args_space_used = 0;
+
+	  fp = -1;
+	  for (i = 0; i < nargs; i++)
+	    {
+	      int len;
+
+	      /* Get fp for current argument to get len.  */
+	      if (i == 0 || fpieces == NULL)
+	        {
+		  /* The first argument doesn't have fpieces.  */
+		  len = 4;
+		}
+	      else
+	        {
+		  for (fp++; fpieces[fp].string != NULL; fp++)
+	            {
+		      if (fpieces[fp].argclass != literal_piece)
+			break;
+		    }
+		  if (fpieces[fp].string == NULL)
+		    internal_error (__FILE__, __LINE__,
+				    "fpieces is not for current arguments.");
+		  if (fpieces[fp].argclass == long_long_arg)
+		    len = 8;
+		  else
+		    len = 4;
+		}
+
+	      if (write_pass)
+		{
+		  if (len == 8)
+		    {
+		      unsigned long long lval = (unsigned long long)args[i];
+
+		      write_inferior_memory (sp + args_space_used,
+					     (unsigned char *) &lval, len);
+		    }
+		  else
+		    {
+		      val = (unsigned long)args[i];
+		      write_inferior_memory (sp + args_space_used,
+					     (unsigned char *) &val, len);
+		    }
+		  /* The System V ABI says that:
+
+		     "An argument's size is increased, if necessary, to
+		     make it a multiple of [32-bit] words.  This may
+		     require tail padding, depending on the size of the
+		     argument."
+
+		     This makes sure the stack stays word-aligned.  */
+		  args_space_used += len;
+		}
+	      else
+		args_space += len;
+	    }
+
+	  if (!write_pass)
+	    {
+	      sp -= args_space;
+
+	      /* The original System V ABI only requires word alignment,
+	         but modern incarnations need 16-byte alignment in order
+	         to support SSE.  Since wasting a few bytes here isn't
+	         harmful we unconditionally enforce 16-byte alignment.  */
+	      sp &= ~0xf;
+	    }
+	}
+
+      /* Store return address.  */
+      val = bp_addr;
+      sp -= 4;
+      write_inferior_memory (sp, (unsigned char *) &val, sizeof (val));
+
+      /* Finally, update the stack pointer...  */
+      val = sp;
+      supply_register_by_name (regcache, "esp", &val);
+
+      /* ...and fake a frame pointer.  */
+      supply_register_by_name (regcache, "ebp", &val);
+
+      sp += 8;
+    }
+
+  return sp;
+}
--- a/gdb/gdbserver/i386-low.h
+++ b/gdb/gdbserver/i386-low.h
@@ -114,3 +114,23 @@ extern unsigned i386_dr_low_get_control
/* Return the value of the inferior's DR6 debug status register. */
 extern unsigned i386_dr_low_get_status (void);
+
+extern CORE_ADDR i386_low_frame_align (CORE_ADDR sp);
+
+extern int i386_low_frame_red_zone_size (struct regcache *regcache);
+
+extern CORE_ADDR i386_low_push_dummy_code (CORE_ADDR sp, CORE_ADDR funaddr,
+					   ULONGEST *args, int nargs,
+					   CORE_ADDR *real_pc,
+					   CORE_ADDR *bp_addr,
+					   struct regcache *regcache);
+
+extern const gdb_byte * i386_low_breakpoint_from_pc (CORE_ADDR *pc,
+						     int *len);
+
+extern CORE_ADDR i386_low_push_dummy_call (CORE_ADDR funaddr,
+					   struct regcache *regcache,
+					   CORE_ADDR bp_addr,
+					   struct format_piece *fpieces,
+					   int nargs, ULONGEST *args,
+					   CORE_ADDR sp);
--- /dev/null
+++ b/gdb/gdbserver/infcall.c
@@ -0,0 +1,167 @@
+/* Perform an inferior function call for remote server.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Hui Zhu <hui@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 "regcache.h"
+
+static int inside_infcall = 0;
+static CORE_ADDR inside_infcall_addr;
+
+int
+is_inside_infcall (CORE_ADDR addr)
+{
+  if (inside_infcall == 1 && inside_infcall_addr == addr)
+    return 1;
+  return 0;
+}
+
+/* Push data in BUFFER to current inferior's stack and set address of
+   stack to BUFFER_ADDR.
+   Return the value of sp after push.  */
+
+CORE_ADDR
+push_inferior_stack(const unsigned char *buffer, int len,
+		    CORE_ADDR *buffer_addr)
+{
+  CORE_ADDR sp = target_get_sp (get_thread_regcache (current_inferior,
+						     1));
+
+  /* Allocate space in stack.  */
+  if (target_inner_than (1, 2))
+    sp -= len;
+
+  write_inferior_memory (sp, buffer, len);
+  *buffer_addr = sp;
+
+  /* Allocate space in stack.  */
+  if (target_inner_than (2, 1))
+    sp += len;
+
+  return sp;
+}
+
+/* Call inferior's function FUNCTION with ARGS.
+   SP is current sp or the return value of function PUSH_INFERIOR_STACK.  */
+
+void
+call_function_by_hand (CORE_ADDR sp, CORE_ADDR function,
+		       struct format_piece *fpieces, int nargs,
+		       ULONGEST *args)
+{
+  struct regcache *regcache = get_thread_regcache (current_inferior, 1);
+  struct regcache *bak_regcache;
+  void *bak_inf_state;
+  CORE_ADDR real_pc;
+  CORE_ADDR bp_addr;
+
+  if (target_supports_infcall () == 0)
+    error (_("Current target doesn't support inferior function call"));
+
+  /* Backup regcache.  */
+  bak_regcache = regcache_dup (regcache);
+
+  /* Backup control state.  */
+  bak_inf_state = target_save_infcall_control_state ();
+
+  {
+    CORE_ADDR old_sp = sp;
+
+    if (the_target->frame_align)
+      {
+	sp = target_frame_align (old_sp);
+	if (target_inner_than (1, 2))
+	  sp -= target_frame_red_zone_size (regcache);
+	else
+	  sp += target_frame_red_zone_size (regcache);
+	if (sp == old_sp)
+	  {
+	    if (target_inner_than (1, 2))
+	      sp = target_frame_align (old_sp - 1);
+	    else
+	      sp = target_frame_align (old_sp + 1);
+	  }
+      }
+    else
+      sp = old_sp;
+  }
+
+  {
+    /* Determine the location of the breakpoint (and possibly other
+       stuff) that the called function will return to.
+       Just support dummy location on stack.  */
+    const gdb_byte *bp_bytes;
+    CORE_ADDR bp_addr_as_address;
+    int bp_size;
+
+    sp = target_push_dummy_code (sp, function, args, nargs, &real_pc,
+				 &bp_addr, regcache);
+    /* Write a legitimate instruction at the point where the infcall
+       breakpoint is going to be inserted.  While this instruction
+       is never going to be executed, a user investigating the
+       memory from GDB would see this instruction instead of random
+       uninitialized bytes.  We chose the breakpoint instruction
+       as it may look as the most logical one to the user and also
+       valgrind 3.7.0 needs it for proper vgdb inferior calls.
+
+       If software breakpoints are unsupported for this target we
+       leave the user visible memory content uninitialized.  */
+    bp_addr_as_address = bp_addr;
+    bp_bytes = target_breakpoint_from_pc (&bp_addr_as_address,
+					  &bp_size);
+    if (bp_bytes != NULL)
+      write_inferior_memory (bp_addr_as_address, bp_bytes, bp_size);
+  }
+
+  /* Because remote server cannot get the type of function, so doesn't
+     reserve space for the return structure to be written on the stack.  */
+
+  /* Create the dummy stack frame.  Pass in the call dummy address as,
+     presumably, the ABI code knows where, in the call dummy, the
+     return address should be pointed.  */
+  sp = target_push_dummy_call (function, regcache, bp_addr, fpieces,
+			       nargs, args, sp);
+
+  /* Set pc.  */
+  regcache_write_pc (regcache, real_pc);
+
+  /* Run the inferior until it stops.  */
+  {
+    struct thread_resume resume_info[1];
+    struct target_waitstatus ret_status;
+
+    inside_infcall = 1;
+    inside_infcall_addr = bp_addr;
+
+    resume_info[0].thread = current_ptid;
+    resume_info[0].kind = resume_continue;
+    resume_info[0].sig = 0;
+    (*the_target->resume) (resume_info, 1);
+    (*the_target->wait) (current_ptid, &ret_status, 0);
+
+    inside_infcall = 0;
+  }
+
+  /* Set infcall control state back.  */
+  target_restore_infcall_control_state (bak_inf_state);
+
+  /* Set bak_regacahe to current_inferior.  */
+  regcache_cpy (get_thread_regcache (current_inferior, 1), bak_regcache);
+  free_register_cache (bak_regcache);
+}
--- /dev/null
+++ b/gdb/gdbserver/infcall.h
@@ -0,0 +1,28 @@
+/* Perform an inferior function call for remote server.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Hui Zhu <hui@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/>.  */
+
+extern int is_inside_infcall (CORE_ADDR addr);
+
+extern CORE_ADDR push_inferior_stack(const unsigned char *buffer,
+				     int len, CORE_ADDR *buffer_addr);
+
+extern void call_function_by_hand (CORE_ADDR sp, CORE_ADDR function,
+				   struct format_piece *fpieces,
+				   int nargs, ULONGEST *args);
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -47,6 +47,7 @@
 #include "filestuff.h"
 #include "tracepoint.h"
 #include "hostio.h"
+#include "infcall.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
@@ -2789,6 +2790,9 @@ retry:
       goto retry;
     }
+ if (is_inside_infcall (event_child->stop_pc))
+    return null_ptid;
+
   /* 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);
@@ -5133,6 +5137,108 @@ linux_supports_range_stepping (void)
   return (*the_low_target.supports_range_stepping) ();
 }
+static int
+linux_supports_infcall (void)
+{
+  if (*the_low_target.supports_infcall == NULL)
+    return 0;
+
+  return (*the_low_target.supports_infcall) ();
+}
+
+static CORE_ADDR
+linux_get_sp (struct regcache *regcache)
+{
+  if (the_low_target.get_sp == NULL)
+    return 0;
+
+  return the_low_target.get_sp (regcache);
+}
+
+static CORE_ADDR
+linux_frame_align (CORE_ADDR sp)
+{
+  if (the_low_target.frame_align == NULL)
+    return sp;
+
+  return the_low_target.frame_align (sp);
+}
+
+static int
+linux_frame_red_zone_size (struct regcache *regcache)
+{
+  if (the_low_target.frame_red_zone_size == NULL)
+    return 0;
+
+  return the_low_target.frame_red_zone_size (regcache);
+}
+
+static CORE_ADDR
+linux_push_dummy_code (CORE_ADDR sp, CORE_ADDR funaddr, ULONGEST *args,
+		       int nargs, CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+		       struct regcache *regcache)
+{
+  if (the_low_target.push_dummy_code == NULL)
+    return sp;
+
+  return the_low_target.push_dummy_code (sp, funaddr, args, nargs,
+					 real_pc, bp_addr, regcache);
+}
+
+static const gdb_byte *
+linux_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+  if (the_low_target.breakpoint_from_pc == NULL)
+    return NULL;
+
+  return the_low_target.breakpoint_from_pc (pcptr, lenptr);
+}
+
+static CORE_ADDR linux_push_dummy_call (CORE_ADDR funaddr,
+					struct regcache *regcache,
+					CORE_ADDR bp_addr,
+					struct format_piece *fpieces,
+					int nargs, ULONGEST *args,
+					CORE_ADDR sp)
+{
+  if (the_low_target.push_dummy_call == NULL)
+    return sp;
+
+  return the_low_target.push_dummy_call (funaddr, regcache, bp_addr,
+					 fpieces, nargs, args, sp);
+}
+
+static void *
+linux_save_infcall_control_state (void)
+{
+  struct thread_info *current_thread;
+  struct lwp_info *bak_lwp;
+
+  current_thread = (struct thread_info *) find_inferior_id (&all_threads,
+							    current_ptid);
+  if (current_thread == NULL)
+    return NULL;
+
+  bak_lwp = (struct lwp_info *) xmalloc (sizeof (*bak_lwp));
+  memcpy (bak_lwp, get_thread_lwp (current_thread), sizeof (*bak_lwp));
+
+  return bak_lwp;
+}
+
+static void
+linux_restore_infcall_control_state (void *inf_state)
+{
+  struct thread_info *current_thread;
+  struct lwp_info *bak_lwp = inf_state;
+
+  current_thread = (struct thread_info *) find_inferior_id (&all_threads,
+							    current_ptid);
+  if (current_thread == NULL)
+    error ("Cannot find current thread.");
+
+  memcpy (get_thread_lwp (current_thread), bak_lwp, sizeof (*bak_lwp));
+}
+
 /* Enumerate spufs IDs for process PID.  */
 static int
 spu_enumerate_spu_ids (long pid, unsigned char *buf, CORE_ADDR offset, int len)
@@ -6053,6 +6159,16 @@ static struct target_ops linux_target_op
   NULL,
 #endif
   linux_supports_range_stepping,
+  linux_supports_infcall,
+  linux_get_sp,
+  linux_frame_align,
+  NULL,
+  linux_frame_red_zone_size,
+  linux_push_dummy_code,
+  linux_breakpoint_from_pc,
+  linux_push_dummy_call,
+  linux_save_infcall_control_state,
+  linux_restore_infcall_control_state,
 };
static void
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -219,6 +219,35 @@ struct linux_target_ops
/* Returns true if the low target supports range stepping. */
   int (*supports_range_stepping) (void);
+
+  /* Return true if target supports inferior function call.  */
+  int (*supports_infcall) (void);
+
+  /* Return sp of target.  */
+  CORE_ADDR (*get_sp) (struct regcache *regcache);
+
+  /* Return frame align sp of target.  */
+  CORE_ADDR (*frame_align) (CORE_ADDR sp);
+
+  /* Return the red zone size of target.  */
+  int (*frame_red_zone_size) (struct regcache *regcache);
+
+  /* Return the address for push dummy code in target.  */
+  CORE_ADDR (*push_dummy_code) (CORE_ADDR sp, CORE_ADDR funaddr,
+				ULONGEST *args, int nargs,
+				CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+				struct regcache *regcache);
+
+  /* According to the address in pcptr, return the breakpoint of target.  */
+  const gdb_byte * (*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
+  /* Push arguments to stack of target.  */
+  CORE_ADDR (*push_dummy_call) (CORE_ADDR funaddr,
+				struct regcache *regcache,
+				CORE_ADDR bp_addr,
+				struct format_piece *fpieces,
+				int nargs, ULONGEST *args,
+				CORE_ADDR sp);
 };
extern struct linux_target_ops the_low_target;
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3340,6 +3340,31 @@ x86_supports_range_stepping (void)
   return 1;
 }
+static int
+x86_supports_infcall (void)
+{
+  return 1;
+}
+
+static CORE_ADDR
+x86_get_sp (struct regcache *regcache)
+{
+  int use_64bit = register_size (regcache->tdesc, 0) == 8;
+
+  if (use_64bit)
+    {
+      unsigned long sp;
+      collect_register_by_name (regcache, "rsp", &sp);
+      return (CORE_ADDR) sp;
+    }
+  else
+    {
+      unsigned int sp;
+      collect_register_by_name (regcache, "esp", &sp);
+      return (CORE_ADDR) sp;
+    }
+}
+
 /* This is initialized assuming an amd64 target.
    x86_arch_setup will correct it for i386 or amd64 targets.  */
@@ -3378,6 +3403,13 @@ struct linux_target_ops the_low_target =
   x86_emit_ops,
   x86_get_min_fast_tracepoint_insn_len,
   x86_supports_range_stepping,
+  x86_supports_infcall,
+  x86_get_sp,
+  i386_low_frame_align,
+  i386_low_frame_red_zone_size,
+  i386_low_push_dummy_code,
+  i386_low_breakpoint_from_pc,
+  i386_low_push_dummy_call,
 };
void
--- a/gdb/gdbserver/regcache.c
+++ b/gdb/gdbserver/regcache.c
@@ -110,6 +110,16 @@ regcache_invalidate (void)
   find_inferior (&all_threads, regcache_invalidate_one, &pid);
 }
+struct regcache *
+regcache_dup (struct regcache *regcache)
+{
+  struct regcache *new = new_register_cache(regcache->tdesc);
+
+  regcache_cpy (new, regcache);
+
+  return new;
+}
+
 #endif
struct regcache *
@@ -250,17 +260,26 @@ find_register_by_name (const struct targ
 }
int
-find_regno (const struct target_desc *tdesc, const char *name)
+find_regno_without_fatal (const struct target_desc *tdesc,
+			  const char *name)
 {
   int i;
for (i = 0; i < tdesc->num_registers; i++)
     if (strcmp (name, tdesc->reg_defs[i].name) == 0)
       return i;
-  fatal ("Unknown register %s requested", name);
   return -1;
 }
+int
+find_regno (const struct target_desc *tdesc, const char *name)
+{
+  int ret = find_regno_without_fatal (tdesc, name);
+  if (ret < 0)
+    fatal ("Unknown register %s requested", name);
+  return ret;
+}
+
 struct reg *
 find_register_by_number (const struct target_desc *tdesc, int n)
 {
--- a/gdb/gdbserver/regcache.h
+++ b/gdb/gdbserver/regcache.h
@@ -72,6 +72,10 @@ void free_register_cache (struct regcach
void regcache_invalidate_thread (struct thread_info *); +/* Copy/duplicate the contents of a register cache. */
+
+struct regcache *regcache_dup (struct regcache *regcache);
+
 /* Invalidate cached registers for all threads of the current
    process.  */
@@ -103,6 +107,9 @@ int register_cache_size (const struct ta int register_size (const struct target_desc *tdesc, int n); +int find_regno_without_fatal (const struct target_desc *tdesc,
+			      const char *name);
+
 int find_regno (const struct target_desc *tdesc, const char *name);
void supply_register (struct regcache *regcache, int n, const void *buf);
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1941,6 +1941,9 @@ handle_query (char *own_buf, int packet_
       strcat (own_buf, ";ConditionalBreakpoints+");
       strcat (own_buf, ";BreakpointCommands+");
+ if (target_supports_infcall ())
+	strcat (own_buf, ";DprintfAgentCall+");
+
       if (target_supports_agent ())
 	strcat (own_buf, ";QAgent+");
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -29,6 +29,7 @@ struct emit_ops;
 struct btrace_target_info;
 struct buffer;
 struct process_info;
+struct format_piece;
/* This structure describes how to resume a particular thread (or all
    threads) based on the client's request.  If thread is -1, then this
@@ -368,6 +369,44 @@ struct target_ops
/* Return true if target supports range stepping. */
   int (*supports_range_stepping) (void);
+
+  /* Return true if target supports inferior function call.  */
+  int (*supports_infcall) (void);
+
+  /* Return sp of target.  */
+  CORE_ADDR (*get_sp) (struct regcache *regcache);
+
+  /* Return frame align sp of target.  */
+  CORE_ADDR (*frame_align) (CORE_ADDR sp);
+
+  /* Return true if lhs is inner than rhs in target.  */
+  int (*inner_than) (CORE_ADDR lhs, CORE_ADDR rhs);
+
+  /* Return the red zone size of target.  */
+  int (*frame_red_zone_size) (struct regcache *regcache);
+
+  /* Return the address for push dummy code in target.  */
+  CORE_ADDR (*push_dummy_code) (CORE_ADDR sp, CORE_ADDR funaddr,
+				ULONGEST *args, int nargs,
+				CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+				struct regcache *regcache);
+
+  /* According to the address in pcptr, return the breakpoint of target.  */
+  const gdb_byte * (*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
+  /* Setup stack to call a function.  */
+  CORE_ADDR (*push_dummy_call) (CORE_ADDR funaddr,
+				struct regcache *regcache,
+				CORE_ADDR bp_addr,
+				struct format_piece *fpieces,
+				int nargs, ULONGEST *args,
+				CORE_ADDR sp);
+
+  /* Save control state of current target and return.  */
+  void * (*save_infcall_control_state) (void);
+
+  /* Restore control state of current target in INF_STATE.  */
+  void (*restore_infcall_control_state) (void *inf_state);
 };
extern struct target_ops *the_target;
@@ -508,6 +547,51 @@ int kill_inferior (int);
   (the_target->supports_range_stepping ? \
    (*the_target->supports_range_stepping) () : 0)
+#define target_supports_infcall() \
+  (the_target->supports_infcall ? \
+   (*the_target->supports_infcall) () : 0)
+
+#define target_get_sp(regcache) \
+  (the_target->get_sp ? (*the_target->get_sp) (regcache) : 0)
+
+#define target_frame_align(sp) \
+  (the_target->frame_align ? (*the_target->frame_align) (sp) : sp)
+
+#define target_inner_than(lhs, rhs) \
+  (the_target->inner_than ? \
+   (*the_target->inner_than) (lhs, rhs) : (lhs < rhs))
+
+#define target_frame_red_zone_size(regcache) \
+  (the_target->frame_red_zone_size ? \
+   (*the_target->frame_red_zone_size) (regcache) : 0)
+
+#define target_push_dummy_code(sp, funaddr, args, nargs, real_pc, bp_addr, \
+			       regcache) \
+  (the_target->push_dummy_code ? \
+   (*the_target->push_dummy_code) (sp, funaddr, args, nargs, real_pc, \
+				   bp_addr, regcache) : sp)
+
+#define target_breakpoint_from_pc(pcptr, lenptr) \
+  (the_target->breakpoint_from_pc ? \
+   (*the_target->breakpoint_from_pc) (pcptr, lenptr) : NULL)
+
+#define target_push_dummy_call(funaddr, regcache, bp_addr, fpieces, \
+			       nargs, args, sp) \
+  (the_target->push_dummy_call ? \
+   (*the_target->push_dummy_call) (funaddr, regcache, bp_addr, fpieces, \
+				   nargs, args, sp) : sp)
+
+#define target_save_infcall_control_state() \
+  (the_target->save_infcall_control_state ? \
+   (*the_target->save_infcall_control_state) () : NULL)
+
+#define target_restore_infcall_control_state(inf_state)			\
+  do									\
+    {									\
+      if (the_target->restore_infcall_control_state)     		\
+	(*the_target->restore_infcall_control_state) (inf_state);	\
+    } while (0)
+
 /* Start non-stop mode, returns 0 on success, -1 on failure.   */
int start_non_stop (int nonstop);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]