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