Index: linux-nat.c =================================================================== RCS file: /cvs/src/src/gdb/linux-nat.c,v retrieving revision 1.106 diff -u -p -r1.106 linux-nat.c --- linux-nat.c 25 Sep 2008 14:13:44 -0000 1.106 +++ linux-nat.c 1 Oct 2008 23:34:45 -0000 @@ -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 }; @@ -609,6 +625,41 @@ linux_test_for_tracefork (int original_p 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. */ @@ -629,11 +680,33 @@ linux_supports_tracevforkdone (int pid) } +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); @@ -641,15 +714,16 @@ linux_enable_event_reporting (ptid_t pti 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 @@ -664,6 +738,12 @@ linux_child_post_startup_inferior (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 @@ -890,6 +970,19 @@ linux_child_insert_exec_catchpoint (int 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 @@ -1310,6 +1403,9 @@ linux_nat_create_inferior (char *exec_fi 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. */ @@ -1940,6 +2036,27 @@ linux_handle_extended_wait (struct lwp_i 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); } @@ -2550,11 +2667,16 @@ linux_nat_filter_event (int lwpid, int s } /* 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, @@ -4000,6 +4122,8 @@ linux_target_install_ops (struct target_ 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; @@ -4007,6 +4131,9 @@ linux_target_install_ops (struct target_ 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; } Index: ppc-linux-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/ppc-linux-tdep.c,v retrieving revision 1.107 diff -u -p -r1.107 ppc-linux-tdep.c --- ppc-linux-tdep.c 26 Aug 2008 15:16:41 -0000 1.107 +++ ppc-linux-tdep.c 1 Oct 2008 23:34:45 -0000 @@ -47,6 +47,142 @@ #include "features/rs6000/powerpc-vsx64l.c" #include "features/rs6000/powerpc-e500l.c" +/* Total number of syscalls */ +#define N_SYSCALLS 301 + +/* Syscalls names for PPC 32-bit */ +static const char *syscalls_names[] = { + "restart_syscall", "exit", "fork", "read", + "write", "open", "close", "waitpid", "creat", + "link", "unlink", "execve", "chdir", "time", + "mknod", "chmod", "lchown", "break", "oldstat", + "lseek", "getpid", "mount", "umount", "setuid", + "getuid", "stime", "ptrace", "alarm", "oldfstat", + "pause", "utime", "stty", "gtty", "access", "nice", + "ftime", "sync", "kill", "rename", "mkdir", "rmdir", + "dup", "pipe", "times", "prof", "brk", "setgid", + "getgid", "signal", "geteuid", "getegid", "acct", + "umount2", "lock", "ioctl", "fcntl", "mpx", "setpgid", + "ulimit", "oldolduname", "umask", "chroot", "ustat", + "dup2", "getppid", "getpgrp", "setsid", "sigaction", + "sgetmask", "ssetmask", "setreuid", "setregid", + "sigsuspend", "sigpending", "sethostname", "setrlimit", + "getrlimit", "getrusage", "gettimeofday", "settimeofday", + "getgroups", "setgroups", "select", "symlink", "oldlstat", + "readlink", "uselib", "swapon", "reboot", "readdir", + "mmap", "munmap", "truncate", "ftruncate", "fchmod", + "fchown", "getpriority", "setpriority", "profil", + "statfs", "fstatfs", "ioperm", "socketcall", "syslog", + "setitimer", "getitimer", "stat", "lstat", "fstat", + "olduname", "iopl", "vhangup", "idle", "vm86", "wait4", + "swapoff", "sysinfo", "ipc", "fsync", "sigreturn", + "clone", "setdomainname", "uname", "modify_ldt", + "adjtimex", "mprotect", "sigprocmask", "create_module", + "init_module", "delete_module", "get_kernel_syms", + "quotactl", "getpgid", "fchdir", "bdflush", "sysfs", + "personality", "afs_syscall", "setfsuid", "setfsgid", + "_llseek", "getdents", "_newselect", "flock", "msync", + "readv", "writev", "getsid", "fdatasync", "_sysctl", + "mlock", "munlock", "mlockall", "munlockall", + "sched_setparam", "sched_getparam", "sched_setscheduler", + "sched_getscheduler", "sched_yield", + "sched_get_priority_max", "sched_get_priority_min", + "sched_rr_get_interval", "nanosleep", "mremap", + "setresuid", "getresuid", "query_module", "poll", + "nfsservctl", "setresgid", "getresgid", "prctl", + "rt_sigreturn", "rt_sigaction", "rt_sigprocmask", + "rt_sigpending", "rt_sigtimedwait", "rt_sigqueueinfo", + "rt_sigsuspend", "pread64", "pwrite64", "chown", + "getcwd", "capget", "capset", "sigaltstack", "sendfile", + "getpmsg", "putpmsg", "vfork", "ugetrlimit", "readahead", + "mmap2", "truncate64", "ftruncate64", "stat64", "lstat64", + "fstat64", "pciconfig_read", "pciconfig_write", + "pciconfig_iobase", "multiplexer", "getdents64", + "pivot_root", "fcntl64", "madvise", "mincore", "gettid", + "tkill", "setxattr", "lsetxattr", "fsetxattr", + "getxattr", "lgetxattr", "fgetxattr", "listxattr", + "llistxattr", "flistxattr", "removexattr", "lremovexattr", + "fremovexattr", "futex", "sched_setaffinity", + "sched_getaffinity", "", "tuxcall", "sendfile64", + "io_setup", "io_destroy", "io_getevents", "io_submit", + "io_cancel", "set_tid_address", "fadvise64", "exit_group", + "lookup_dcookie", "epoll_create", "epoll_ctl", + "epoll_wait", "remap_file_pages", "timer_create", + "timer_settime", "timer_gettime", "timer_getoverrun", + "timer_delete", "clock_settime", "clock_gettime", + "clock_getres", "clock_nanosleep", "swapcontext", + "tgkill", "utimes", "statfs64", "fstatfs64", "fadvise64_64", + "rtas", "sys_debug_setcontext", "", "", "mbind", + "get_mempolicy", "set_mempolicy", "mq_open", "mq_unlink", + "mq_timedsend", "mq_timedreceive", "mq_notify", + "mq_getsetattr", "kexec_load", "add_key", "request_key", + "keyctl", "waitid", "ioprio_set", "ioprio_get", + "inotify_init", "inotify_add_watch", "inotify_rm_watch", + "spu_run", "spu_create", "pselect6", "ppoll", "unshare", + "", "", "", "openat", "mkdirat", "mknodat", "fchownat", + "futimesat", "fstatat64", "unlinkat", "renameat", + "linkat", "symlinkat", "readlinkat", "fchmodat", + "faccessat", "", "" +}; + +/* Syscalls names for PPC 64-bit */ +static const char *syscalls_names64[] = { + "restart_syscall", "exit", "fork", "read", "write", "open", + "close", "waitpid", "creat", "link", "unlink", "execve", + "chdir", "time", "mknod", "chmod", "lchown", "break", "oldstat", + "lseek", "getpid", "mount", "umount", "setuid", "getuid", "stime", + "ptrace", "alarm", "oldfstat", "pause", "utime", "stty", "gtty", + "access", "nice", "ftime", "sync", "kill", "rename", "mkdir", + "rmdir", "dup", "pipe", "times", "prof", "brk", "setgid", + "getgid", "signal", "geteuid", "getegid", "acct", "umount2", + "lock", "ioctl", "fcntl", "mpx", "setpgid", "ulimit", + "oldolduname", "umask", "chroot", "ustat", "dup2", "getppid", + "getpgrp", "setsid", "sigaction", "sgetmask", "ssetmask", + "setreuid", "setregid", "sigsuspend", "sigpending", "sethostname", + "setrlimit", "getrlimit", "getrusage", "gettimeofday", "settimeofday", + "getgroups", "setgroups", "select", "symlink", "oldlstat", + "readlink", "uselib", "swapon", "reboot", "readdir", "mmap", + "munmap", "truncate", "ftruncate", "fchmod", "fchown", "getpriority", + "setpriority", "profil", "statfs", "fstatfs", "ioperm", "socketcall", + "syslog", "setitimer", "getitimer", "stat", "lstat", "fstat", + "olduname", "iopl", "vhangup", "idle", "vm86", "wait4", "swapoff", + "sysinfo", "ipc", "fsync", "sigreturn", "clone", "setdomainname", + "uname", "modify_ldt", "adjtimex", "mprotect", "sigprocmask", + "create_module", "init_module", "delete_module", "get_kernel_syms", + "quotactl", "getpgid", "fchdir", "bdflush", "sysfs", "personality", + "afs_syscall", "setfsuid", "setfsgid", "_llseek", "getdents", + "_newselect", "flock", "msync", "readv", "writev", "getsid", + "fdatasync", "_sysctl", "mlock", "munlock", "mlockall", + "munlockall", "sched_setparam", "sched_getparam", "sched_setscheduler", + "sched_getscheduler", "sched_yield", "sched_get_priority_max", + "sched_get_priority_min", "sched_rr_get_interval", "nanosleep", + "mremap", "setresuid", "getresuid", "query_module", "poll", + "nfsservctl", "setresgid", "getresgid", "prctl", "rt_sigreturn", + "rt_sigaction", "rt_sigprocmask", "rt_sigpending", "rt_sigtimedwait", + "rt_sigqueueinfo", "rt_sigsuspend", "pread64", "pwrite64", "chown", + "getcwd", "capget", "capset", "sigaltstack", "sendfile", "getpmsg", + "putpmsg", "vfork", "ugetrlimit", "readahead", "", "", "", "", "", "", + "pciconfig_read", "pciconfig_write", "pciconfig_iobase", "multiplexer", + "getdents64", "pivot_root", "", "madvise", "mincore", "gettid", "tkill", + "setxattr", "lsetxattr", "fsetxattr", "getxattr", "lgetxattr", "fgetxattr", + "listxattr", "llistxattr", "flistxattr", "removexattr", "lremovexattr", + "fremovexattr", "futex", "sched_setaffinity", "sched_getaffinity", + "", "tuxcall", "", "io_setup", "io_destroy", "io_getevents", "io_submit", + "io_cancel", "set_tid_address", "fadvise64", "exit_group", "lookup_dcookie", + "epoll_create", "epoll_ctl", "epoll_wait", "remap_file_pages", + "timer_create", "timer_settime", "timer_gettime", "timer_getoverrun", + "timer_delete", "clock_settime", "clock_gettime", "clock_getres", + "clock_nanosleep", "swapcontext", "tgkill", "utimes", "statfs64", + "fstatfs64", "", "rtas", "sys_debug_setcontext", "", "", "mbind", + "get_mempolicy", "set_mempolicy", "mq_open", "mq_unlink", "mq_timedsend", + "mq_timedreceive", "mq_notify", "mq_getsetattr", "kexec_load", "add_key", + "request_key", "keyctl", "waitid", "ioprio_set", "ioprio_get", + "inotify_init", "inotify_add_watch", "inotify_rm_watch", "spu_run", + "spu_create", "pselect6", "ppoll", "unshare", "", "", "", + "openat", "mkdirat", "mknodat", "fchownat", "futimesat", "newfstatat", + "unlinkat", "renameat", "linkat", "symlinkat", "readlinkat", + "fchmodat", "faccessat", "", "" +}; /* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint in much the same fashion as memory_remove_breakpoint in mem-break.c, @@ -1003,6 +1139,83 @@ ppc_linux_trap_reg_p (struct gdbarch *gd && 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; +} + +const char * +ppc_linux_syscall_name_from_number (struct gdbarch *gdbarch, + int syscall_number) +{ + if (syscall_number < 0 + || syscall_number >= N_SYSCALLS) + return NULL; + + return syscalls_names[syscall_number]; +} + +const char * +ppc64_linux_syscall_name_from_number (struct gdbarch *gdbarch, + int syscall_number) +{ + if (syscall_number < 0 + || syscall_number >= N_SYSCALLS) + return NULL; + + return syscalls_names64[syscall_number]; +} + +int +ppc_linux_syscall_number_from_name (struct gdbarch *gdbarch, + const char *syscall_name) +{ + int i; + + for (i = 0; i < N_SYSCALLS; i++) + if (strcmp (syscall_name, syscalls_names[i]) == 0) + return i; + + return UNKNOWN_SYSCALL; +} + +int +ppc64_linux_syscall_number_from_name (struct gdbarch *gdbarch, + const char *syscall_name) +{ + int i; + + for (i = 0; i < N_SYSCALLS; i++) + if (strcmp (syscall_name, syscalls_names64[i]) == 0) + return i; + + return UNKNOWN_SYSCALL; +} + static void ppc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc) { @@ -1074,8 +1287,13 @@ ppc_linux_init_abi (struct gdbarch_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); + if (tdep->wordsize == 4) { + 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); + /* Until November 2001, gcc did not comply with the 32 bit SysV R4 ABI requirement that structures less than or equal to 8 bytes should be returned in registers. Instead GCC was using @@ -1100,6 +1318,9 @@ ppc_linux_init_abi (struct gdbarch_info if (tdep->wordsize == 8) { + set_gdbarch_syscall_name_from_number (gdbarch, ppc64_linux_syscall_name_from_number); + set_gdbarch_syscall_number_from_name (gdbarch, ppc64_linux_syscall_number_from_name); + /* Handle PPC GNU/Linux 64-bit function pointers (which are really function descriptors). */ set_gdbarch_convert_from_func_ptr_addr