This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 2/4] Fix PR breakpoints/16494: add dprintf-style "agent-call"(gdbserver)
- From: Hui Zhu <hui_zhu at mentor dot com>
- To: gdb-patches ml <gdb-patches at sourceware dot org>
- Date: Wed, 5 Mar 2014 00:29:52 +0800
- Subject: [PATCH 2/4] Fix PR breakpoints/16494: add dprintf-style "agent-call"(gdbserver)
- Authentication-results: sourceware.org; auth=none
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);