This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 2/4] 'catch syscall' feature -- Architecture-dependent part
- From: Sérgio Durigan Júnior <sergiodj at linux dot vnet dot ibm dot com>
- To: gdb-patches at sourceware dot org
- Date: Tue, 04 Nov 2008 02:31:24 -0200
- Subject: [PATCH 2/4] 'catch syscall' feature -- Architecture-dependent part
This is the architecture dependent part of the patch. It adds support
for PPC, PPC64 and i386 archs.
Regards,
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
gdb/ChangeLog
2008-11-04 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
* i386-linux-nat.c (i386_linux_resume): Select the proper request
to be made for ptrace() considering if we are catching syscalls
or not.
* i386-linux-tdep.c: Define the syscall's XML struct to hold
information about syscalls.
(init_sysinfo): New.
(i386_linux_get_syscall_number): New.
(i386_linux_syscall_name_from_number): New.
(i386_linux_syscall_number_from_name): New.
(i386_linux_get_syscalls_names): New.
(i386_linux_init_abi): Register the correct functions for
syscall catchpoint.
* linux-nat.c: Define some helpful variables to track wether we have
support for the needed ptrace() option.
(linux_test_for_tracesysgood): New.
(linux_supports_tracesysgood): New.
(linux_enable_tracesysgood): New.
(linux_passed_by_entrypoint): New.
(linux_enable_event_reporting): Save the current used ptrace()
options.
(linux_child_post_startup_inferior): Create the entry breakpoint.
(linux_child_insert_syscall_catchpoint): New.
(linux_child_remove_syscall_catchpoint): New.
(linux_nat_create_inferior): Properly set the flag which indicates
if the inferior have already passed through its entrypoint.
(linux_handle_extended_wait): Handle the case which the inferior stops
because it has called or returned from a syscall.
(linux_nat_filter_event): Likewise.
(linux_target_install_ops): Install the necessary functions to handle
syscall catchpoints and entry breakpoints.
* ppc-linux-tdep.c: Define the syscall's XML struct to hold
information about syscalls.
(ppc_linux_get_syscall_number): New.
(init_sysinfo): New.
(ppc_linux_syscall_name_from_number): New.
(ppc_linux_syscall_number_from_name): New.
(ppc_linux_get_syscalls_names): New.
(ppc_linux_init_abi): Register the correct functions for
syscall catchpoint.
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 146f5a6..c634275 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -748,7 +748,13 @@ i386_linux_resume (ptid_t ptid, int step, enum target_signal signal)
{
int pid = PIDGET (ptid);
- int request = PTRACE_CONT;
+ int request;
+
+ if (target_passed_by_entrypoint () > 0
+ && catch_syscall_enabled () > 0)
+ request = PTRACE_SYSCALL;
+ else
+ request = PTRACE_CONT;
if (step)
{
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 5284f4a..451ef86 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -36,6 +36,14 @@
#include "symtab.h"
#include "arch-utils.h"
#include "regset.h"
+#include "xml-syscall.h"
+
+/* Structure used to store information about the available syscalls in
+ the system. */
+static const struct syscalls_info *sysinfo = NULL;
+
+/* A flag to tell if we already initialized the structure above. */
+static int have_initialized_sysinfo = 0;
/* Supported register note sections. */
static struct core_regset_section i386_linux_regset_sections[] =
@@ -348,6 +356,72 @@ i386_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
}
+/* Initializes the syscalls_info structure according to the
+ architecture. */
+static void
+init_sysinfo (void)
+{
+ /* Did we already try to initialize the structure? */
+ if (have_initialized_sysinfo)
+ return;
+
+ sysinfo = xml_init_syscalls_info ("syscalls/i386-syscalls.xml");
+
+ have_initialized_sysinfo = 1;
+
+ if (sysinfo == NULL)
+ /* The initialization failed. Let's show a warning
+ message to the user (just this time) and leave. */
+ warning (_("Could not load the syscall XML file.\n\
+GDB will not be able to display syscalls names."));
+}
+
+LONGEST
+i386_linux_get_syscall_number (struct gdbarch *gdbarch,
+ ptid_t ptid)
+{
+ struct regcache *regcache = get_thread_regcache (ptid);
+ /* The content of a register. */
+ gdb_byte buf[4];
+ /* The result. */
+ LONGEST ret;
+
+ /* Getting the system call number from the register.
+ When dealing with x86 architecture, this information
+ is stored at %eax register. */
+ regcache_cooked_read (regcache, I386_LINUX_ORIG_EAX_REGNUM, buf);
+
+ ret = extract_signed_integer (buf, 4);
+
+ return ret;
+}
+
+const char *
+i386_linux_syscall_name_from_number (struct gdbarch *gdbarch,
+ int syscall_number)
+{
+ init_sysinfo ();
+
+ return xml_get_syscall_name (sysinfo, syscall_number);
+}
+
+int
+i386_linux_syscall_number_from_name (struct gdbarch *gdbarch,
+ const char *syscall_name)
+{
+ init_sysinfo ();
+
+ return xml_get_syscall_number (sysinfo, syscall_name);
+}
+
+const char **
+i386_linux_get_syscalls_names (struct gdbarch *gdbarch)
+{
+ init_sysinfo ();
+
+ return xml_list_of_syscalls (sysinfo);
+}
+
/* The register sets used in GNU/Linux ELF core-dumps are identical to
the register sets in `struct user' that are used for a.out
core-dumps. These are also used by ptrace(2). The corresponding
@@ -469,6 +543,16 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
simple_displaced_step_free_closure);
set_gdbarch_displaced_step_location (gdbarch,
displaced_step_at_entry_point);
+
+ /* Functions for 'catch syscall'. */
+ set_gdbarch_get_syscall_number (gdbarch,
+ i386_linux_get_syscall_number);
+ set_gdbarch_syscall_name_from_number (gdbarch,
+ i386_linux_syscall_name_from_number);
+ set_gdbarch_syscall_number_from_name (gdbarch,
+ i386_linux_syscall_number_from_name);
+ set_gdbarch_get_syscalls_names (gdbarch,
+ i386_linux_get_syscalls_names);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 56ec9cb..a63ee8d 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -57,6 +57,10 @@
# endif
#endif /* HAVE_PERSONALITY */
+/* To be used when one needs to know wether a
+ WSTOPSIG (status) is a syscall */
+#define TRAP_IS_SYSCALL (SIGTRAP | 0x80)
+
/* This comment documents high-level logic of this file.
Waiting for events in sync mode
@@ -269,17 +273,29 @@ struct simple_pid_list *stopped_pids;
static int linux_supports_tracefork_flag = -1;
+/* This variable is a tri-state flag: -1 for unknown, 0 if PTRACE_O_TRACESYSGOOD
+ can not be used, 1 if it can. */
+
+static int linux_supports_tracesysgood_flag = -1;
+
/* If we have PTRACE_O_TRACEFORK, this flag indicates whether we also have
PTRACE_O_TRACEVFORKDONE. */
static int linux_supports_tracevforkdone_flag = -1;
+/* If the inferior have passed through its entrypoint (AT_ENTRY),
+ then this flag is set to 1. Otherwise, its value is 0. */
+static int linux_passed_by_entrypoint_flag = 0;
+
/* Async mode support */
/* Zero if the async mode, although enabled, is masked, which means
linux_nat_wait should behave as if async mode was off. */
static int linux_nat_async_mask_value = 1;
+/* Stores the current used ptrace() options. */
+static int current_ptrace_options = 0;
+
/* The read/write ends of the pipe registered as waitable file in the
event loop. */
static int linux_nat_event_pipe[2] = { -1, -1 };
@@ -624,6 +640,41 @@ linux_test_for_tracefork (int original_pid)
linux_nat_async_events (async_events_original_state);
}
+/* Determine if PTRACE_O_TRACESYSGOOD can be used to follow syscalls.
+
+ We try to enable syscall tracing on ORIGINAL_PID. If this fails,
+ we know that the feature is not available. This may change the tracing
+ options for ORIGINAL_PID, but we'll be setting them shortly anyway. */
+
+static void
+linux_test_for_tracesysgood (int original_pid)
+{
+ int ret;
+ enum sigchld_state async_events_original_state;
+
+ async_events_original_state = linux_nat_async_events (sigchld_sync);
+
+ linux_supports_tracesysgood_flag = 0;
+
+ ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD);
+ if (ret != 0)
+ return;
+
+ linux_supports_tracesysgood_flag = 1;
+ linux_nat_async_events (async_events_original_state);
+}
+
+/* Determine wether we support PTRACE_O_TRACESYSGOOD option available.
+ This function also sets linux_supports_tracesysgood_flag. */
+
+static int
+linux_supports_tracesysgood (int pid)
+{
+ if (linux_supports_tracesysgood_flag == -1)
+ linux_test_for_tracesysgood (pid);
+ return linux_supports_tracesysgood_flag;
+}
+
/* Return non-zero iff we have tracefork functionality available.
This function also sets linux_supports_tracefork_flag. */
@@ -643,12 +694,34 @@ linux_supports_tracevforkdone (int pid)
return linux_supports_tracevforkdone_flag;
}
+static void
+linux_enable_tracesysgood (ptid_t ptid)
+{
+ int pid = ptid_get_lwp (ptid);
+
+ if (pid == 0)
+ pid = ptid_get_pid (ptid);
+
+ if (linux_supports_tracesysgood (pid) == 0)
+ return;
+
+ current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+ linux_passed_by_entrypoint_flag = 1;
+
+ ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options);
+}
+
+static int
+linux_passed_by_entrypoint (void)
+{
+ return linux_passed_by_entrypoint_flag;
+}
+
void
linux_enable_event_reporting (ptid_t ptid)
{
int pid = ptid_get_lwp (ptid);
- int options;
if (pid == 0)
pid = ptid_get_pid (ptid);
@@ -656,15 +729,16 @@ linux_enable_event_reporting (ptid_t ptid)
if (! linux_supports_tracefork (pid))
return;
- options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
- | PTRACE_O_TRACECLONE;
+ current_ptrace_options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE;
+
if (linux_supports_tracevforkdone (pid))
- options |= PTRACE_O_TRACEVFORKDONE;
+ current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
/* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
read-only process state. */
- ptrace (PTRACE_SETOPTIONS, pid, 0, options);
+ ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options);
}
static void
@@ -679,6 +753,12 @@ linux_child_post_startup_inferior (ptid_t ptid)
{
linux_enable_event_reporting (ptid);
check_for_thread_db ();
+ /* We have to create the entry breakpoint here because
+ if we have 'catch syscall' enabled, we ought to know
+ when to enable PTRACE_O_TRACESYSGOOD. Otherwise, we would
+ start catching syscalls from ld.so/libc (which is not
+ what we want). */
+ create_entry_breakpoint ();
}
static int
@@ -905,6 +985,19 @@ linux_child_insert_exec_catchpoint (int pid)
error (_("Your system does not support exec catchpoints."));
}
+static void
+linux_child_insert_syscall_catchpoint (int pid)
+{
+ if (! linux_supports_tracesysgood (pid))
+ error (_("Your system does not support syscall catchpoints."));
+}
+
+static int
+linux_child_remove_syscall_catchpoint (int pid)
+{
+ return 0;
+}
+
/* On GNU/Linux there are no real LWP's. The closest thing to LWP's
are processes sharing the same VM space. A multi-threaded process
is basically a group of such processes. However, such a grouping
@@ -1325,6 +1418,9 @@ linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
int personality_orig = 0, personality_set = 0;
#endif /* HAVE_PERSONALITY */
+ /* We are sarting, so we still have not passed through our entrypoint. */
+ linux_passed_by_entrypoint_flag = 0;
+
/* The fork_child mechanism is synchronous and calls target_wait, so
we have to mask the async mode. */
@@ -1955,6 +2051,27 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
return 0;
}
+ /* Used for 'catch syscall' feature. */
+ if (WSTOPSIG (status) == TRAP_IS_SYSCALL)
+ {
+ if (catch_syscall_enabled () == 0)
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ else
+ {
+ struct regcache *regcache = get_thread_regcache (lp->ptid);
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct thread_info *th_info = find_thread_pid (lp->ptid);
+
+ ourstatus->kind =
+ (th_info->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY) ?
+ TARGET_WAITKIND_SYSCALL_RETURN : TARGET_WAITKIND_SYSCALL_ENTRY;
+ th_info->syscall_state = ourstatus->kind;
+ ourstatus->value.syscall_number =
+ (int) gdbarch_get_syscall_number (gdbarch, lp->ptid);
+ }
+ return 0;
+ }
+
internal_error (__FILE__, __LINE__,
_("unknown ptrace event %d"), event);
}
@@ -2565,11 +2682,16 @@ linux_nat_filter_event (int lwpid, int status, int options)
}
/* Save the trap's siginfo in case we need it later. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
+ if (WIFSTOPPED (status)
+ && (WSTOPSIG (status) == SIGTRAP || WSTOPSIG (status) == TRAP_IS_SYSCALL))
save_siginfo (lp);
- /* Handle GNU/Linux's extended waitstatus for trace events. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ /* Handle GNU/Linux's extended waitstatus for trace events.
+ It is necessary to check if WSTOPSIG is signaling a that
+ the inferior is entering/exiting a system call. */
+ if (WIFSTOPPED (status)
+ && ((WSTOPSIG (status) == TRAP_IS_SYSCALL)
+ || (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
@@ -4022,6 +4144,8 @@ linux_target_install_ops (struct target_ops *t)
t->to_insert_fork_catchpoint = linux_child_insert_fork_catchpoint;
t->to_insert_vfork_catchpoint = linux_child_insert_vfork_catchpoint;
t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint;
+ t->to_insert_syscall_catchpoint = linux_child_insert_syscall_catchpoint;
+ t->to_remove_syscall_catchpoint = linux_child_remove_syscall_catchpoint;
t->to_pid_to_exec_file = linux_child_pid_to_exec_file;
t->to_post_startup_inferior = linux_child_post_startup_inferior;
t->to_post_attach = linux_child_post_attach;
@@ -4029,6 +4153,9 @@ linux_target_install_ops (struct target_ops *t)
t->to_find_memory_regions = linux_nat_find_memory_regions;
t->to_make_corefile_notes = linux_nat_make_corefile_notes;
+ t->to_enable_tracesysgood = linux_enable_tracesysgood;
+ t->to_passed_by_entrypoint = linux_passed_by_entrypoint;
+
super_xfer_partial = t->to_xfer_partial;
t->to_xfer_partial = linux_xfer_partial;
}
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index 2804857..9c620ec 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -38,6 +38,7 @@
#include "trad-frame.h"
#include "frame-unwind.h"
#include "tramp-frame.h"
+#include "xml-syscall.h"
#include "features/rs6000/powerpc-32l.c"
#include "features/rs6000/powerpc-altivec32l.c"
@@ -48,6 +49,13 @@
#include "features/rs6000/powerpc-e500l.c"
+/* Structure used to store information about the available syscalls in
+ the system. */
+static const struct syscalls_info *sysinfo = NULL;
+
+/* A flag to tell if we already initialized the structure above. */
+static int have_initialized_sysinfo = 0;
+
/* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint
in much the same fashion as memory_remove_breakpoint in mem-break.c,
but is careful not to write back the previous contents if the code
@@ -1003,6 +1011,88 @@ ppc_linux_trap_reg_p (struct gdbarch *gdbarch)
&& register_size (gdbarch, PPC_TRAP_REGNUM) > 0;
}
+/* Return the current system call's number present in the
+ r0 register. When the function fails, it returns -1. */
+LONGEST
+ppc_linux_get_syscall_number (struct gdbarch *gdbarch,
+ ptid_t ptid)
+{
+ struct regcache *regcache = get_thread_regcache (ptid);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ /* The content of a register */
+ gdb_byte *buf;
+ /* The result */
+ LONGEST ret;
+
+ /* Make sure we're in a 32- or 64-bit machine */
+ gdb_assert (tdep->wordsize == 4 || tdep->wordsize == 8);
+
+ buf = (gdb_byte *) xmalloc (tdep->wordsize * sizeof (gdb_byte));
+
+ /* Getting the system call number from the register.
+ When dealing with PowerPC architecture, this information
+ is stored at 0th register. */
+ regcache_cooked_read (regcache, tdep->ppc_gp0_regnum, buf);
+
+ ret = extract_signed_integer (buf, tdep->wordsize);
+ xfree (buf);
+
+ return ret;
+}
+
+/* Initializes the syscalls_info structure according to the
+ architecture. */
+static void
+init_sysinfo (struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep;
+
+ /* Did we already try to initialize the structure? */
+ if (have_initialized_sysinfo)
+ return;
+
+ tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->wordsize == 4)
+ sysinfo = xml_init_syscalls_info ("syscalls/ppc-syscalls.xml");
+ else
+ sysinfo = xml_init_syscalls_info ("syscalls/ppc64-syscalls.xml");
+
+ have_initialized_sysinfo = 1;
+
+ if (sysinfo == NULL)
+ /* The initialization failed. Let's show a warning
+ message to the user (just this time) and leave. */
+ warning (_("Could not load the syscall XML file.\n\
+GDB will not be able to display syscalls names."));
+}
+
+const char *
+ppc_linux_syscall_name_from_number (struct gdbarch *gdbarch,
+ int syscall_number)
+{
+ init_sysinfo (gdbarch);
+
+ return xml_get_syscall_name (sysinfo, syscall_number);
+}
+
+int
+ppc_linux_syscall_number_from_name (struct gdbarch *gdbarch,
+ const char *syscall_name)
+{
+ init_sysinfo (gdbarch);
+
+ return xml_get_syscall_number (sysinfo, syscall_name);
+}
+
+const char **
+ppc_linux_get_syscalls_names (struct gdbarch *gdbarch)
+{
+ init_sysinfo (gdbarch);
+
+ return xml_list_of_syscalls (sysinfo);
+}
+
static void
ppc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
{
@@ -1074,6 +1164,11 @@ ppc_linux_init_abi (struct gdbarch_info info,
/* Handle inferior calls during interrupted system calls. */
set_gdbarch_write_pc (gdbarch, ppc_linux_write_pc);
+ set_gdbarch_get_syscall_number (gdbarch, ppc_linux_get_syscall_number);
+ set_gdbarch_syscall_name_from_number (gdbarch, ppc_linux_syscall_name_from_number);
+ set_gdbarch_syscall_number_from_name (gdbarch, ppc_linux_syscall_number_from_name);
+ set_gdbarch_get_syscalls_names (gdbarch, ppc_linux_get_syscalls_names);
+
if (tdep->wordsize == 4)
{
/* Until November 2001, gcc did not comply with the 32 bit SysV