diff options
author | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-07-07 12:13:57 +0000 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-07-07 12:13:57 +0000 |
commit | aa7c744796a067e1533b21e7d58aa30e6f3d12c8 (patch) | |
tree | 98ecb2c393bda1e81b72b416030ebe1f0904cedb /gdb/common | |
parent | 889003ed52d067a971c7e1ed8e0968d91264d273 (diff) | |
download | gdb-aa7c744796a067e1533b21e7d58aa30e6f3d12c8.zip gdb-aa7c744796a067e1533b21e7d58aa30e6f3d12c8.tar.gz gdb-aa7c744796a067e1533b21e7d58aa30e6f3d12c8.tar.bz2 |
gdb/
* common/linux-ptrace.c: Include gdb_assert.h.
<__i386__> (linux_ptrace_test_ret_to_nx_instr): New declaration.
<__i386__>: Include sys/reg.h, sys/mman.h, signal.h, sys/wait.h and
stdint.h.
(linux_ptrace_test_ret_to_nx, linux_ptrace_init_warnings): New
functions.
* common/linux-ptrace.h (linux_ptrace_init_warnings): New declarations.
* linux-nat.c (linux_child_post_attach)
(linux_child_post_startup_inferior): Call linux_ptrace_init_warnings.
gdb/gdbserver/
* gdbserver/linux-low.c (initialize_low): Call
linux_ptrace_init_warnings.
Diffstat (limited to 'gdb/common')
-rw-r--r-- | gdb/common/linux-ptrace.c | 124 | ||||
-rw-r--r-- | gdb/common/linux-ptrace.h | 1 |
2 files changed, 125 insertions, 0 deletions
diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c index 600dcb9..fdec5d6 100644 --- a/gdb/common/linux-ptrace.c +++ b/gdb/common/linux-ptrace.c @@ -26,6 +26,7 @@ #include "linux-ptrace.h" #include "linux-procfs.h" #include "buffer.h" +#include "gdb_assert.h" /* Find all possible reasons we could fail to attach PID and append these newline terminated reason strings to initialized BUFFER. '\0' termination @@ -47,3 +48,126 @@ linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer) "- the process has already terminated\n"), (int) pid); } + +#ifdef __i386__ + +/* Address of the 'ret' instruction in asm code block below. */ +extern void (linux_ptrace_test_ret_to_nx_instr) (void); + +#include <sys/reg.h> +#include <sys/mman.h> +#include <signal.h> +#include <sys/wait.h> +#include <stdint.h> + +#endif /* __i386__ */ + +/* Test broken off-trunk Linux kernel patchset for NX support on i386. It was + removed in Fedora kernel 88fa1f0332d188795ed73d7ac2b1564e11a0b4cd. */ + +static void +linux_ptrace_test_ret_to_nx (void) +{ +#ifdef __i386__ + pid_t child, got_pid; + gdb_byte *return_address, *pc; + long l; + int status; + + return_address = mmap (NULL, 2, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (return_address == MAP_FAILED) + { + warning (_("linux_ptrace_test_ret_to_nx: Cannot mmap: %s"), + strerror (errno)); + return; + } + + /* Put there 'int3'. */ + *return_address = 0xcc; + + child = fork (); + switch (child) + { + case -1: + warning (_("linux_ptrace_test_ret_to_nx: Cannot fork: %s"), + strerror (errno)); + return; + + case 0: + l = ptrace (PTRACE_TRACEME, 0, NULL, NULL); + if (l != 0) + warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: %s"), + strerror (errno)); + else + { + asm volatile ("pushl %0;" + ".globl linux_ptrace_test_ret_to_nx_instr;" + "linux_ptrace_test_ret_to_nx_instr:" + "ret" + : : "r" (return_address) : "%esp", "memory"); + gdb_assert_not_reached ("asm block did not terminate"); + } + + _exit (1); + } + + got_pid = waitpid (child, &status, 0); + gdb_assert (got_pid == child); + gdb_assert (WIFSTOPPED (status)); + + /* We may get SIGSEGV due to missing PROT_EXEC of the return_address. */ + gdb_assert (WSTOPSIG (status) == SIGTRAP || WSTOPSIG (status) == SIGSEGV); + + errno = 0; + l = ptrace (PTRACE_PEEKUSER, child, (void *) (uintptr_t) (EIP * 4), NULL); + gdb_assert (errno == 0); + pc = (void *) (uintptr_t) l; + + if (ptrace (PTRACE_KILL, child, NULL, NULL) != 0) + warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_KILL: %s"), + strerror (errno)); + else + { + int kill_status; + + got_pid = waitpid (child, &kill_status, 0); + gdb_assert (got_pid == child); + gdb_assert (WIFSIGNALED (kill_status)); + } + + /* + 1 is there as x86* stops after the 'int3' instruction. */ + if (WSTOPSIG (status) == SIGTRAP && pc == return_address + 1) + { + /* PASS */ + return; + } + + /* We may get SIGSEGV due to missing PROT_EXEC of the RETURN_ADDRESS page. */ + if (WSTOPSIG (status) == SIGSEGV && pc == return_address) + { + /* PASS */ + return; + } + + gdb_assert ((void (*) (void)) pc == &linux_ptrace_test_ret_to_nx_instr); + + warning (_("Cannot call inferior functions, you have broken " + "Linux kernel i386 NX (non-executable pages) support!")); +#endif /* __i386__ */ +} + +/* Display possible problems on this system. Display them only once per GDB + execution. */ + +void +linux_ptrace_init_warnings (void) +{ + static int warned = 0; + + if (warned) + return; + warned = 1; + + linux_ptrace_test_ret_to_nx (); +} diff --git a/gdb/common/linux-ptrace.h b/gdb/common/linux-ptrace.h index 6315b13..96ad33d 100644 --- a/gdb/common/linux-ptrace.h +++ b/gdb/common/linux-ptrace.h @@ -68,5 +68,6 @@ struct buffer; #endif extern void linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer); +extern void linux_ptrace_init_warnings (void); #endif /* COMMON_LINUX_PTRACE_H */ |