aboutsummaryrefslogtreecommitdiff
path: root/gdb/nat
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/nat')
-rw-r--r--gdb/nat/aarch64-hw-point.c6
-rw-r--r--gdb/nat/linux-namespaces.c167
-rw-r--r--gdb/nat/linux-namespaces.h11
-rw-r--r--gdb/nat/linux-procfs.c30
-rw-r--r--gdb/nat/linux-procfs.h14
-rw-r--r--gdb/nat/linux-ptrace.c4
-rw-r--r--gdb/nat/x86-gcc-cpuid.h153
-rw-r--r--gdb/nat/x86-linux-tdesc.c20
-rw-r--r--gdb/nat/x86-linux-tdesc.h7
-rw-r--r--gdb/nat/x86-linux.c59
-rw-r--r--gdb/nat/x86-linux.h4
11 files changed, 383 insertions, 92 deletions
diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c
index 6d8dce8..8c0854b 100644
--- a/gdb/nat/aarch64-hw-point.c
+++ b/gdb/nat/aarch64-hw-point.c
@@ -710,10 +710,8 @@ aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
itself. For instance, the access size of an stp instruction is 16.
So, if we use stp to store to address p, and set a watchpoint on
address p + 8, the reported ADDR_TRAP can be p + 8 (observed on
- RK3399 SOC). But it also can be p (observed on M1 SOC). Checking
- for this situation introduces the possibility of false positives,
- so we only do this for hw_write watchpoints. */
- const CORE_ADDR max_access_size = type == hw_write ? 16 : 8;
+ RK3399 SOC). But it also can be p (observed on M1 SOC). */
+ const CORE_ADDR max_access_size = 16;
const CORE_ADDR addr_watch_base = addr_watch_aligned -
(max_access_size - AARCH64_HWP_MAX_LEN_PER_REG);
if (!(addr_trap >= addr_watch_base
diff --git a/gdb/nat/linux-namespaces.c b/gdb/nat/linux-namespaces.c
index ea94fdd..2a65fb0 100644
--- a/gdb/nat/linux-namespaces.c
+++ b/gdb/nat/linux-namespaces.c
@@ -233,6 +233,12 @@ enum mnsh_msg_type
MNSH_RET_INT. */
MNSH_REQ_SETNS,
+ /* A request that the helper call lstat. The single
+ argument (the filename) should be passed in BUF, and
+ should include a terminating NUL character. The helper
+ should respond with a MNSH_RET_INTSTR. */
+ MNSH_REQ_LSTAT,
+
/* A request that the helper call open. Arguments should
be passed in BUF, INT1 and INT2. The filename (in BUF)
should include a terminating NUL character. The helper
@@ -265,62 +271,69 @@ enum mnsh_msg_type
MNSH_RET_INTSTR,
};
-/* Print a string representation of a message using debug_printf.
- This function is not async-signal-safe so should never be
- called from the helper. */
+/* Return a string representation of a message. This function is not
+ async-signal-safe so should never be called from the helper. */
-static void
+static std::string
mnsh_debug_print_message (enum mnsh_msg_type type,
int fd, int int1, int int2,
const void *buf, int bufsiz)
{
+ std::string res;
+
gdb_byte *c = (gdb_byte *) buf;
gdb_byte *cl = c + bufsiz;
switch (type)
{
case MNSH_MSG_ERROR:
- debug_printf ("ERROR");
+ res += "ERROR";
+ break;
+
+ case MNSH_REQ_LSTAT:
+ res += "LSTAT";
break;
case MNSH_REQ_SETNS:
- debug_printf ("SETNS");
+ res += "SETNS";
break;
case MNSH_REQ_OPEN:
- debug_printf ("OPEN");
+ res += "OPEN";
break;
case MNSH_REQ_UNLINK:
- debug_printf ("UNLINK");
+ res += "UNLINK";
break;
case MNSH_REQ_READLINK:
- debug_printf ("READLINK");
+ res += "READLINK";
break;
case MNSH_RET_INT:
- debug_printf ("INT");
+ res += "INT";
break;
case MNSH_RET_FD:
- debug_printf ("FD");
+ res += "FD";
break;
case MNSH_RET_INTSTR:
- debug_printf ("INTSTR");
+ res += "INTSTR";
break;
default:
- debug_printf ("unknown-packet-%d", type);
+ res += string_printf ("unknown-packet-%d", type);
}
- debug_printf (" %d %d %d \"", fd, int1, int2);
+ res += string_printf (" %d %d %d \"", fd, int1, int2);
for (; c < cl; c++)
- debug_printf (*c >= ' ' && *c <= '~' ? "%c" : "\\%o", *c);
+ res += string_printf (*c >= ' ' && *c <= '~' ? "%c" : "\\%o", *c);
- debug_printf ("\"");
+ res += "\"";
+
+ return res;
}
/* Forward declaration. */
@@ -390,12 +403,10 @@ mnsh_send_message (int sock, enum mnsh_msg_type type,
if (size < 0)
mnsh_maybe_mourn_peer ();
- if (debug_linux_namespaces)
- {
- debug_printf ("mnsh: send: ");
- mnsh_debug_print_message (type, fd, int1, int2, buf, bufsiz);
- debug_printf (" -> %s\n", pulongest (size));
- }
+ linux_namespaces_debug_printf
+ ("send: %s -> %s",
+ mnsh_debug_print_message (type, fd, int1, int2, buf, bufsiz).c_str (),
+ pulongest (size));
return size;
}
@@ -445,9 +456,8 @@ mnsh_recv_message (int sock, enum mnsh_msg_type *type,
size = recvmsg (sock, &msg, MSG_CMSG_CLOEXEC);
if (size < 0)
{
- if (debug_linux_namespaces)
- debug_printf ("namespace-helper: recv failed (%s)\n",
- pulongest (size));
+ linux_namespaces_debug_printf
+ ("namespace-helper: recv failed (%s)", pulongest (size));
mnsh_maybe_mourn_peer ();
@@ -457,9 +467,9 @@ mnsh_recv_message (int sock, enum mnsh_msg_type *type,
/* Check for truncation. */
if (size < fixed_size || (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)))
{
- if (debug_linux_namespaces)
- debug_printf ("namespace-helper: recv truncated (%s 0x%x)\n",
- pulongest (size), msg.msg_flags);
+ linux_namespaces_debug_printf
+ ("namespace-helper: recv truncated (%s 0x%x)",
+ pulongest (size), msg.msg_flags);
mnsh_maybe_mourn_peer ();
@@ -477,13 +487,10 @@ mnsh_recv_message (int sock, enum mnsh_msg_type *type,
else
*fd = -1;
- if (debug_linux_namespaces)
- {
- debug_printf ("mnsh: recv: ");
- mnsh_debug_print_message (*type, *fd, *int1, *int2, buf,
- size - fixed_size);
- debug_printf ("\n");
- }
+ linux_namespaces_debug_printf
+ ("recv: %s",
+ mnsh_debug_print_message (*type, *fd, *int1, *int2, buf,
+ size - fixed_size).c_str ());
/* Return the number of bytes of data in BUF. */
return size - fixed_size;
@@ -514,6 +521,20 @@ mnsh_handle_setns (int sock, int fd, int nstype)
return mnsh_return_int (sock, result, errno);
}
+
+/* Handle a MNSH_REQ_LSTAT message. Must be async-signal-safe. */
+
+static ssize_t
+mnsh_handle_lstat (int sock, const char *filename)
+{
+ struct stat sb;
+ int stat_ok = lstat (filename, &sb);
+
+ return mnsh_return_intstr (sock, stat_ok, &sb,
+ stat_ok == -1 ? 0 : sizeof (sb),
+ errno);
+}
+
/* Handle a MNSH_REQ_OPEN message. Must be async-signal-safe. */
static ssize_t
@@ -574,6 +595,11 @@ mnsh_main (int sock)
response = mnsh_handle_setns (sock, fd, int1);
break;
+ case MNSH_REQ_LSTAT:
+ if (size > 0 && buf[size - 1] == '\0')
+ response = mnsh_handle_lstat (sock, buf);
+ break;
+
case MNSH_REQ_OPEN:
if (size > 0 && buf[size - 1] == '\0')
response = mnsh_handle_open (sock, buf, int1, int2);
@@ -673,7 +699,7 @@ linux_mntns_get_helper (void)
mnsh_creator_pid = helper_creator;
/* Debug printing isn't async-signal-safe. */
- debug_linux_namespaces = 0;
+ debug_linux_namespaces = false;
mnsh_main (sv[1]);
}
@@ -685,9 +711,8 @@ linux_mntns_get_helper (void)
helper->sock = sv[0];
helper->nsid = ns->id;
- if (debug_linux_namespaces)
- debug_printf ("Started mount namespace helper process %d\n",
- helper->pid);
+ linux_namespaces_debug_printf
+ ("Started mount namespace helper process %d", helper->pid);
}
return helper;
@@ -765,6 +790,10 @@ mnsh_maybe_mourn_peer (void)
mnsh_send_message (helper->sock, MNSH_REQ_OPEN, -1, flags, mode, \
filename, strlen (filename) + 1)
+#define mnsh_send_lstat(helper, filename) \
+ mnsh_send_message (helper->sock, MNSH_REQ_LSTAT, -1, 0, 0, \
+ filename, strlen (filename) + 1)
+
#define mnsh_send_unlink(helper, filename) \
mnsh_send_message (helper->sock, MNSH_REQ_UNLINK, -1, 0, 0, \
filename, strlen (filename) + 1)
@@ -884,7 +913,7 @@ linux_mntns_access_fs (pid_t pid)
struct stat sb;
struct linux_mnsh *helper;
ssize_t size;
- int fd;
+ int fd, fd_user = -1;
if (pid == getpid ())
return MNSH_FS_DIRECT;
@@ -901,6 +930,8 @@ linux_mntns_access_fs (pid_t pid)
{
int save_errno = errno;
close (fd);
+ if (fd_user >= 0)
+ close (fd_user);
errno = save_errno;
};
@@ -910,6 +941,13 @@ linux_mntns_access_fs (pid_t pid)
if (sb.st_ino == ns->id)
return MNSH_FS_DIRECT;
+ struct linux_ns *ns_user = linux_ns_get_namespace (LINUX_NS_USER);
+ if (ns_user != nullptr)
+ {
+ const char *ns_filename = linux_ns_filename (ns_user, pid);
+ fd_user = gdb_open_cloexec (ns_filename, O_RDONLY, 0).release ();
+ }
+
helper = linux_mntns_get_helper ();
if (helper == NULL)
return MNSH_FS_ERROR;
@@ -918,6 +956,19 @@ linux_mntns_access_fs (pid_t pid)
{
int result, error;
+ /* Try to enter the user namespace first. The current user might
+ have elevated privileges within the user namespace, which would
+ then allow the attempt to enter the mount namespace to succeed. */
+ if (fd_user >= 0)
+ {
+ size = mnsh_send_setns (helper, fd_user, 0);
+ if (size < 0)
+ return MNSH_FS_ERROR;
+
+ if (mnsh_recv_int (helper, &result, &error) != 0)
+ return MNSH_FS_ERROR;
+ }
+
size = mnsh_send_setns (helper, fd, 0);
if (size < 0)
return MNSH_FS_ERROR;
@@ -948,6 +999,42 @@ linux_mntns_access_fs (pid_t pid)
/* See nat/linux-namespaces.h. */
int
+linux_mntns_lstat (pid_t pid, const char *filename,
+ struct stat *sb)
+{
+ enum mnsh_fs_code access = linux_mntns_access_fs (pid);
+
+ if (access == MNSH_FS_ERROR)
+ return -1;
+
+ if (access == MNSH_FS_DIRECT)
+ return lstat (filename, sb);
+
+ gdb_assert (access == MNSH_FS_HELPER);
+
+ struct linux_mnsh *helper = linux_mntns_get_helper ();
+
+ ssize_t size = mnsh_send_lstat (helper, filename);
+ if (size < 0)
+ return -1;
+
+ int stat_ok, error;
+ size = mnsh_recv_intstr (helper, &stat_ok, &error, sb, sizeof (*sb));
+
+ if (size < 0)
+ {
+ stat_ok = -1;
+ errno = error;
+ }
+ else
+ gdb_assert (stat_ok == -1 || size == sizeof (*sb));
+
+ return stat_ok;
+}
+
+/* See nat/linux-namespaces.h. */
+
+int
linux_mntns_open_cloexec (pid_t pid, const char *filename,
int flags, mode_t mode)
{
diff --git a/gdb/nat/linux-namespaces.h b/gdb/nat/linux-namespaces.h
index a548d83..a9a99f1 100644
--- a/gdb/nat/linux-namespaces.h
+++ b/gdb/nat/linux-namespaces.h
@@ -24,6 +24,12 @@
extern bool debug_linux_namespaces;
+/* Print a "linux-namespaces" debug statement. */
+
+#define linux_namespaces_debug_printf(fmt, ...) \
+ debug_prefixed_printf_cond (debug_linux_namespaces, "linux-namespaces", \
+ fmt, ##__VA_ARGS__)
+
/* Enumeration of Linux namespace types. */
enum linux_ns_type
@@ -58,6 +64,11 @@ enum linux_ns_type
extern int linux_ns_same (pid_t pid, enum linux_ns_type type);
+/* Like lstat(2), but in the mount namespace of process PID. */
+
+extern int linux_mntns_lstat (pid_t pid, const char *filename,
+ struct stat *sb);
+
/* Like gdb_open_cloexec, but in the mount namespace of process
PID. */
diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c
index a72df2a..d4f9af3 100644
--- a/gdb/nat/linux-procfs.c
+++ b/gdb/nat/linux-procfs.c
@@ -424,7 +424,7 @@ linux_proc_task_list_dir_exists (pid_t pid)
/* See linux-procfs.h. */
const char *
-linux_proc_pid_to_exec_file (int pid)
+linux_proc_pid_to_exec_file (int pid, bool is_local_fs)
{
static char buf[PATH_MAX];
char name[PATH_MAX];
@@ -437,9 +437,31 @@ linux_proc_pid_to_exec_file (int pid)
else
buf[len] = '\0';
- /* Use /proc/PID/exe if the actual file can't be read, but /proc/PID/exe
- can be. */
- if (access (buf, R_OK) != 0 && access (name, R_OK) == 0)
+ /* If the inferior and GDB can see the same filesystem, and NAME
+ cannot be read, maybe the file has been deleted, then we can
+ potentially use /proc/PID/exe instead.
+
+ GDB always interprets the results of this function within the
+ current sysroot (which is 'target:' by default). This means
+ that, if we return /proc/PID/exe, GDB will try to find this file
+ within the sysroot.
+
+ This doesn't make sense if GDB is using a sysroot like:
+ '/some/random/directory/', not only is it possible that NAME
+ could be found within the sysroot, it is unlikely that
+ /proc/PID/exe will exist within the sysroot.
+
+ Similarly, if the sysroot is 'target:', but the inferior is
+ running within a separate MNT namespace, then it is more than
+ likely that the inferior will also be running in a separate PID
+ namespace, in this case PID is the pid on the host system,
+ /proc/PID/exe will not be correct within the inferiors MNT/PID
+ namespace.
+
+ So, we can fallback to use /proc/PID/exe only if IS_LOCAL_FS is
+ true, this indicates that GDB and the inferior are using the same
+ MNT namespace, and GDB's sysroot is either empty, or 'target:'. */
+ if (is_local_fs && access (buf, R_OK) != 0 && access (name, R_OK) == 0)
strcpy (buf, name);
return buf;
diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h
index 0c80622..4b02d31 100644
--- a/gdb/nat/linux-procfs.h
+++ b/gdb/nat/linux-procfs.h
@@ -87,9 +87,19 @@ extern int linux_proc_task_list_dir_exists (pid_t pid);
/* Return the full absolute name of the executable file that was run
to create the process PID. The returned value persists until this
- function is next called. */
+ function is next called.
-extern const char *linux_proc_pid_to_exec_file (int pid);
+ LOCAL_FS should be true if the file returned from the function will
+ be searched for in the same filesystem as GDB itself is running.
+ In practice, this means LOCAL_FS should be true if PID and GDB are
+ running in the same MNT namespace and GDB's sysroot is either the
+ empty string, or is 'target:'.
+
+ When used from gdbserver, where there is no sysroot, the only check
+ that matters is that PID and gdbserver are running in the same MNT
+ namespace. */
+
+extern const char *linux_proc_pid_to_exec_file (int pid, bool local_fs);
/* Display possible problems on this system. Display them only once
per GDB execution. */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 0510b39..f73058b 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -23,6 +23,7 @@
#include <sys/procfs.h>
#endif
#include "gdbsupport/eintr.h"
+#include "gdbsupport/signals-state-save-restore.h"
/* Stores the ptrace options supported by the running kernel.
A value of -1 means we did not check for features yet. A value
@@ -148,6 +149,9 @@ linux_ptrace_test_ret_to_nx (void)
return;
case 0:
+ /* Set signal handlers to their default because it doesn't make sense
+ to call GDB-specific handlers any more in the child process. */
+ restore_original_signals_state ();
l = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) NULL,
(PTRACE_TYPE_ARG4) NULL);
if (l != 0)
diff --git a/gdb/nat/x86-gcc-cpuid.h b/gdb/nat/x86-gcc-cpuid.h
index 1498100..9e27fd8 100644
--- a/gdb/nat/x86-gcc-cpuid.h
+++ b/gdb/nat/x86-gcc-cpuid.h
@@ -1,5 +1,5 @@
/*
- * Helper cpuid.h file copied from gcc-6.0.0. Code in gdb should not
+ * Helper cpuid.h file copied from gcc-14.2.0. Code in gdb should not
* include this directly, but pull in x86-cpuid.h and use that func.
*/
@@ -55,7 +55,7 @@
#define bit_SSE (1 << 25)
#define bit_SSE2 (1 << 26)
-/* Extended Features */
+/* Extended Features (%eax == 0x80000001) */
/* %ecx */
#define bit_LAHF_LM (1 << 0)
#define bit_ABM (1 << 5)
@@ -68,29 +68,28 @@
#define bit_MWAITX (1 << 29)
/* %edx */
-#define bit_AVX5124VNNIW (1 << 2)
-#define bit_AVX5124FMAPS (1 << 3)
#define bit_MMXEXT (1 << 22)
#define bit_LM (1 << 29)
#define bit_3DNOWP (1 << 30)
-#define bit_3DNOW (1 << 31)
+#define bit_3DNOW (1u << 31)
-/* %ebx. */
+/* %ebx */
#define bit_CLZERO (1 << 0)
+#define bit_WBNOINVD (1 << 9)
-/* Extended Features (%eax == 7) */
+/* Extended Features Leaf (%eax == 7, %ecx == 0) */
/* %ebx */
#define bit_FSGSBASE (1 << 0)
-#define bit_BMI (1 << 3)
-#define bit_HLE (1 << 4)
+#define bit_SGX (1 << 2)
+#define bit_BMI (1 << 3)
+#define bit_HLE (1 << 4)
#define bit_AVX2 (1 << 5)
#define bit_BMI2 (1 << 8)
-#define bit_RTM (1 << 11)
-#define bit_MPX (1 << 14)
+#define bit_RTM (1 << 11)
#define bit_AVX512F (1 << 16)
#define bit_AVX512DQ (1 << 17)
#define bit_RDSEED (1 << 18)
-#define bit_ADX (1 << 19)
+#define bit_ADX (1 << 19)
#define bit_AVX512IFMA (1 << 21)
#define bit_CLFLUSHOPT (1 << 23)
#define bit_CLWB (1 << 24)
@@ -99,23 +98,85 @@
#define bit_AVX512CD (1 << 28)
#define bit_SHA (1 << 29)
#define bit_AVX512BW (1 << 30)
-#define bit_AVX512VL (1 << 31)
+#define bit_AVX512VL (1u << 31)
/* %ecx */
-#define bit_PREFETCHWT1 (1 << 0)
+#define bit_PREFETCHWT1 (1 << 0)
#define bit_AVX512VBMI (1 << 1)
-#define bit_PKU (1 << 3)
+#define bit_PKU (1 << 3)
#define bit_OSPKE (1 << 4)
+#define bit_WAITPKG (1 << 5)
+#define bit_AVX512VBMI2 (1 << 6)
+#define bit_SHSTK (1 << 7)
+#define bit_GFNI (1 << 8)
+#define bit_VAES (1 << 9)
+#define bit_VPCLMULQDQ (1 << 10)
+#define bit_AVX512VNNI (1 << 11)
+#define bit_AVX512BITALG (1 << 12)
+#define bit_AVX512VPOPCNTDQ (1 << 14)
+#define bit_RDPID (1 << 22)
+#define bit_KL (1 << 23)
+#define bit_CLDEMOTE (1 << 25)
+#define bit_MOVDIRI (1 << 27)
+#define bit_MOVDIR64B (1 << 28)
+#define bit_ENQCMD (1 << 29)
-/* XFEATURE_ENABLED_MASK register bits (%eax == 13, %ecx == 0) */
-#define bit_BNDREGS (1 << 3)
-#define bit_BNDCSR (1 << 4)
+/* %edx */
+#define bit_AVX5124VNNIW (1 << 2)
+#define bit_AVX5124FMAPS (1 << 3)
+#define bit_UINTR (1 << 5)
+#define bit_AVX512VP2INTERSECT (1 << 8)
+#define bit_SERIALIZE (1 << 14)
+#define bit_TSXLDTRK (1 << 16)
+#define bit_PCONFIG (1 << 18)
+#define bit_IBT (1 << 20)
+#define bit_AMX_BF16 (1 << 22)
+#define bit_AVX512FP16 (1 << 23)
+#define bit_AMX_TILE (1 << 24)
+#define bit_AMX_INT8 (1 << 25)
+
+/* Extended Features Sub-leaf (%eax == 7, %ecx == 1) */
+/* %eax */
+#define bit_SHA512 (1 << 0)
+#define bit_SM3 (1 << 1)
+#define bit_SM4 (1 << 2)
+#define bit_RAOINT (1 << 3)
+#define bit_AVXVNNI (1 << 4)
+#define bit_AVX512BF16 (1 << 5)
+#define bit_CMPCCXADD (1 << 7)
+#define bit_AMX_COMPLEX (1 << 8)
+#define bit_AMX_FP16 (1 << 21)
+#define bit_HRESET (1 << 22)
+#define bit_AVXIFMA (1 << 23)
-/* Extended State Enumeration Sub-leaf (%eax == 13, %ecx == 1) */
+/* %edx */
+#define bit_AVXVNNIINT8 (1 << 4)
+#define bit_AVXNECONVERT (1 << 5)
+#define bit_AVXVNNIINT16 (1 << 10)
+#define bit_PREFETCHI (1 << 14)
+#define bit_USER_MSR (1 << 15)
+#define bit_AVX10 (1 << 19)
+#define bit_APX_F (1 << 21)
+
+/* Extended State Enumeration Sub-leaf (%eax == 0xd, %ecx == 1) */
#define bit_XSAVEOPT (1 << 0)
#define bit_XSAVEC (1 << 1)
#define bit_XSAVES (1 << 3)
+/* PT sub leaf (%eax == 0x14, %ecx == 0) */
+/* %ebx */
+#define bit_PTWRITE (1 << 4)
+
+/* Keylocker leaf (%eax == 0x19) */
+/* %ebx */
+#define bit_AESKLE ( 1<<0 )
+#define bit_WIDEKL ( 1<<2 )
+
+/* AVX10 sub leaf (%eax == 0x24) */
+/* %ebx */
+#define bit_AVX10_256 (1 << 17)
+#define bit_AVX10_512 (1 << 18)
+
/* Signatures for different CPU implementations as returned in uses
of cpuid with level 0. */
#define signature_AMD_ebx 0x68747541
@@ -170,19 +231,40 @@
#define signature_VORTEX_ecx 0x436f5320
#define signature_VORTEX_edx 0x36387865
-#define __cpuid(level, a, b, c, d) \
- __asm__ ("cpuid\n\t" \
- : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
- : "0" (level))
+#define signature_SHANGHAI_ebx 0x68532020
+#define signature_SHANGHAI_ecx 0x20206961
+#define signature_SHANGHAI_edx 0x68676e61
-#define __cpuid_count(level, count, a, b, c, d) \
- __asm__ ("cpuid\n\t" \
- : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
- : "0" (level), "2" (count))
+#ifndef __x86_64__
+/* At least one cpu (Winchip 2) does not set %ebx and %ecx
+ for cpuid leaf 1. Forcibly zero the two registers before
+ calling cpuid as a precaution. */
+#define __cpuid(level, a, b, c, d) \
+ do { \
+ if (__builtin_constant_p (level) && (level) != 1) \
+ __asm__ __volatile__ ("cpuid\n\t" \
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
+ : "0" (level)); \
+ else \
+ __asm__ __volatile__ ("cpuid\n\t" \
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
+ : "0" (level), "1" (0), "2" (0)); \
+ } while (0)
+#else
+#define __cpuid(level, a, b, c, d) \
+ __asm__ __volatile__ ("cpuid\n\t" \
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
+ : "0" (level))
+#endif
+
+#define __cpuid_count(level, count, a, b, c, d) \
+ __asm__ __volatile__ ("cpuid\n\t" \
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
+ : "0" (level), "2" (count))
/* Return highest supported input value for cpuid instruction. ext can
- be either 0x0 or 0x8000000 to return highest supported value for
+ be either 0x0 or 0x80000000 to return highest supported value for
basic or extended cpuid information. Function returns 0 if cpuid
is not supported or whatever cpuid returns in eax register. If sig
pointer is non-null, then first four bytes of the signature
@@ -225,7 +307,7 @@ __get_cpuid_max (unsigned int __ext, unsigned int *__sig)
: "i" (0x00200000));
#endif
- if (!((__eax ^ __ebx) & 0x00200000))
+ if (__builtin_expect (!((__eax ^ __ebx) & 0x00200000), 0))
return 0;
#endif
@@ -249,8 +331,9 @@ __get_cpuid (unsigned int __leaf,
unsigned int *__ecx, unsigned int *__edx)
{
unsigned int __ext = __leaf & 0x80000000;
+ unsigned int __maxlevel = __get_cpuid_max (__ext, 0);
- if (__get_cpuid_max (__ext, 0) < __leaf)
+ if (__maxlevel == 0 || __maxlevel < __leaf)
return 0;
__cpuid (__leaf, *__eax, *__ebx, *__ecx, *__edx);
@@ -265,12 +348,20 @@ __get_cpuid_count (unsigned int __leaf, unsigned int __subleaf,
unsigned int *__ecx, unsigned int *__edx)
{
unsigned int __ext = __leaf & 0x80000000;
+ unsigned int __maxlevel = __get_cpuid_max (__ext, 0);
- if (__get_cpuid_max (__ext, 0) < __leaf)
+ if (__builtin_expect (__maxlevel == 0, 0) || __maxlevel < __leaf)
return 0;
__cpuid_count (__leaf, __subleaf, *__eax, *__ebx, *__ecx, *__edx);
return 1;
}
+static __inline void
+__cpuidex (int __cpuid_info[4], int __leaf, int __subleaf)
+{
+ __cpuid_count (__leaf, __subleaf, __cpuid_info[0], __cpuid_info[1],
+ __cpuid_info[2], __cpuid_info[3]);
+}
+
#endif /* GDB_NAT_X86_GCC_CPUID_H */
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index 80e4337..5bc36b6 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -43,7 +43,7 @@
/* See nat/x86-linux-tdesc.h. */
const target_desc *
-x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
+x86_linux_tdesc_for_tid (int tid, uint64_t *xstate_bv_storage,
x86_xsave_layout *xsave_layout_storage)
{
#ifdef __x86_64__
@@ -96,30 +96,34 @@ x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
these bits being set we generate a completely empty tdesc for
i386 which will be rejected by GDB. */
have_ptrace_getregset = TRIBOOL_FALSE;
- *xcr0_storage = X86_XSTATE_SSE_MASK;
+ *xstate_bv_storage = X86_XSTATE_SSE_MASK;
}
else
{
have_ptrace_getregset = TRIBOOL_TRUE;
/* Get XCR0 from XSAVE extended state. */
- *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+ uint64_t xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
/ sizeof (uint64_t))];
*xsave_layout_storage
- = x86_fetch_xsave_layout (*xcr0_storage, x86_xsave_length ());
+ = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+
+ *xstate_bv_storage = xcr0;
+ if (x86_check_ssp_support (tid))
+ *xstate_bv_storage |= X86_XSTATE_CET_U;
}
}
- /* Use cached xcr0 value. */
- uint64_t xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+ /* Use cached XSTATE_BV_STORAGE value. */
+ uint64_t xstate_bv_features_bits = *xstate_bv_storage & X86_XSTATE_ALL_MASK;
#ifdef __x86_64__
if (is_64bit)
- return amd64_linux_read_description (xcr0_features_bits, is_x32);
+ return amd64_linux_read_description (xstate_bv_features_bits, is_x32);
else
#endif
- return i386_linux_read_description (xcr0_features_bits);
+ return i386_linux_read_description (xstate_bv_features_bits);
}
#endif /* !IN_PROCESS_AGENT */
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
index 38c71f1..19aa84f 100644
--- a/gdb/nat/x86-linux-tdesc.h
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -27,9 +27,9 @@ struct x86_xsave_layout;
/* Return the target description for Linux thread TID.
- The storage pointed to by XCR0_STORAGE and XSAVE_LAYOUT_STORAGE must
+ The storage pointed to by XSTATE_BV_STORAGE and XSAVE_LAYOUT_STORAGE must
exist until the program (GDB or gdbserver) terminates, this storage is
- used to cache the xcr0 and xsave layout values. The values pointed to
+ used to cache the xstate_bv and xsave layout values. The values pointed to
by these arguments are only updated at most once, the first time this
function is called if the have_ptrace_getregset global is set to
TRIBOOL_UNKNOWN.
@@ -45,6 +45,7 @@ struct x86_xsave_layout;
returned. */
extern const target_desc *x86_linux_tdesc_for_tid
- (int tid, uint64_t *xcr0_storage, x86_xsave_layout *xsave_layout_storage);
+ (int tid, uint64_t *xstate_bv_storage,
+ x86_xsave_layout *xsave_layout_storage);
#endif /* GDB_NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index 0bdff73..5515826 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -17,6 +17,12 @@
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 "elf/common.h"
+#include "gdbsupport/common-defs.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/linux-ptrace.h"
+#include "nat/x86-cpuid.h"
+#include <sys/uio.h>
#include "x86-linux.h"
#include "x86-linux-dregs.h"
#include "nat/gdb_ptrace.h"
@@ -126,3 +132,56 @@ x86_linux_ptrace_get_arch_size (int tid)
return x86_linux_arch_size (false, false);
#endif
}
+
+/* See nat/x86-linux.h. */
+
+bool
+x86_check_ssp_support (const int tid)
+{
+ /* It's not enough to check shadow stack support with the ptrace call
+ below only, as we cannot distinguish between shadow stack not enabled
+ for the current thread and shadow stack is not supported by HW. In
+ both scenarios the ptrace call fails with ENODEV. In case shadow
+ stack is not enabled for the current thread, we still want to return
+ true. */
+ unsigned int eax, ebx, ecx, edx;
+ eax = ebx = ecx = edx = 0;
+
+ if (!__get_cpuid_count (7, 0, &eax, &ebx, &ecx, &edx))
+ return false;
+
+ if ((ecx & bit_SHSTK) == 0)
+ return false;
+
+ /* Further check for NT_X86_SHSTK kernel support. */
+ uint64_t ssp;
+ iovec iov {&ssp, sizeof (ssp) };
+
+ errno = 0;
+ int res = ptrace (PTRACE_GETREGSET, tid, NT_X86_SHSTK, &iov);
+ if (res < 0)
+ {
+ if (errno == EINVAL)
+ {
+ /* The errno EINVAL for a PTRACE_GETREGSET call indicates that
+ kernel support is not available. */
+ return false;
+ }
+ else if (errno == ENODEV)
+ {
+ /* At this point, since we already checked CPUID, the errno
+ ENODEV for a PTRACE_GETREGSET call indicates that shadow
+ stack is not enabled for the current thread. As it could be
+ enabled later, we still want to return true here. */
+ return true;
+ }
+ else
+ {
+ warning (_("Unknown ptrace error for NT_X86_SHSTK: %s"),
+ safe_strerror (errno));
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index dbdef08..1783aae 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -75,4 +75,8 @@ private:
extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int tid);
+/* Check shadow stack hardware and kernel support. */
+
+extern bool x86_check_ssp_support (const int tid);
+
#endif /* GDB_NAT_X86_LINUX_H */