mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
This patch adds the user mode register PL3_SSP which is part of the
Intel(R) Control-Flow Enforcement Technology (CET) feature for support
of shadow stack.
For now, only native and remote debugging support for shadow stack
userspace on amd64 linux are covered by this patch including 64 bit and
x32 support. 32 bit support is not covered due to missing Linux kernel
support.
This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
which is failing in case the shadow stack pointer is unavailable.
Such a state is possible if shadow stack is disabled for the current thread
but supported by HW.
This test uses the Python unwinder inline-frame-cycle-unwind.py which fakes
the cyclic stack cycle by reading the pending frame's registers and adding
them to the unwinder:
~~~
for reg in pending_frame.architecture().registers("general"):
val = pending_frame.read_register(reg)
unwinder.add_saved_register(reg, val)
return unwinder
~~~
However, in case the python unwinder is used we add a register (pl3_ssp) that is
unavailable. This leads to a NOT_AVAILABLE_ERROR caught in
gdb/frame-unwind.c:frame_unwind_try_unwinder and it is continued with standard
unwinders. This destroys the faked cyclic behavior and the stack is
further unwinded after frame 5.
In the working scenario an error should be triggered:
~~~
bt
0 inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
1 normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
2 0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
3 normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
4 0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
5 normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5: backtrace when the unwind is broken at frame 5
~~~
To fix the Python unwinder, we simply skip the unavailable registers.
Also it makes the test gdb.dap/scopes.exp fail. The shadow stack feature is
disabled by default, so the pl3_ssp register which is added with my CET
shadow stack series will be shown as unavailable and we see a TCL error:
~~
>>> {"seq": 12, "type": "request", "command": "variables", "arguments": {"variablesReference": 2, "count": 85}}
Content-Length: 129^M
^M
{"request_seq": 12, "type": "response", "command": "variables", "success": false, "message": "value is not available", "seq": 25}FAIL: gdb.dap/scopes.exp: fetch all registers success
ERROR: tcl error sourcing /tmp/gdb/testsuite/gdb.dap/scopes.exp.
ERROR: tcl error code TCL LOOKUP DICT body
ERROR: key "body" not known in dictionary
while executing
"dict get $val body variables"
(file "/tmp/gdb/testsuite/gdb.dap/scopes.exp" line 152)
invoked from within
"source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
invoked from within
"catch "uplevel #0 source $test_file_name" msg"
UNRESOLVED: gdb.dap/scopes.exp: testcase '/tmp/gdb/testsuite/gdb.dap/scopes.exp' aborted due to Tcl error
~~
I am fixing this by enabling the test for CET shadow stack, in case we
detect that the HW supports it:
~~~
# If x86 shadow stack is supported we need to configure GLIBC_TUNABLES
# such that the feature is enabled and the register pl3_ssp is
# available. Otherwise the reqeust to fetch all registers will fail
# with "message": "value is not available".
if { [allow_ssp_tests] } {
append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK"
}
~~~
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Luis Machado <luis.machado@arm.com>
Approved-By: Andrew Burgess <aburgess@redhat.com>
130 lines
3.7 KiB
C
130 lines
3.7 KiB
C
/* Target description related code for GNU/Linux x86 (i386 and x86-64).
|
|
|
|
Copyright (C) 2024-2025 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
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 "nat/x86-linux-tdesc.h"
|
|
#ifdef __x86_64__
|
|
#include "arch/amd64.h"
|
|
#include "arch/amd64-linux-tdesc.h"
|
|
#endif
|
|
#include "arch/i386.h"
|
|
#include "arch/i386-linux-tdesc.h"
|
|
|
|
#include "nat/x86-linux.h"
|
|
#include "nat/gdb_ptrace.h"
|
|
#include "nat/x86-xstate.h"
|
|
#include "gdbsupport/x86-xstate.h"
|
|
|
|
#ifndef __x86_64__
|
|
#include <sys/procfs.h>
|
|
#include "nat/i386-linux.h"
|
|
#endif
|
|
|
|
#include <sys/uio.h>
|
|
#include <elf.h>
|
|
|
|
#ifndef IN_PROCESS_AGENT
|
|
|
|
/* See nat/x86-linux-tdesc.h. */
|
|
|
|
const target_desc *
|
|
x86_linux_tdesc_for_tid (int tid, uint64_t *xstate_bv_storage,
|
|
x86_xsave_layout *xsave_layout_storage)
|
|
{
|
|
#ifdef __x86_64__
|
|
x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
|
|
bool is_64bit = arch_size.is_64bit ();
|
|
bool is_x32 = arch_size.is_x32 ();
|
|
|
|
if (sizeof (void *) == 4 && is_64bit && !is_x32)
|
|
{
|
|
#ifdef GDBSERVER
|
|
error (_("Can't debug 64-bit process with 32-bit GDBserver"));
|
|
#else
|
|
error (_("Can't debug 64-bit process with 32-bit GDB"));
|
|
#endif
|
|
}
|
|
|
|
#elif HAVE_PTRACE_GETFPXREGS
|
|
if (have_ptrace_getfpxregs == TRIBOOL_UNKNOWN)
|
|
{
|
|
elf_fpxregset_t fpxregs;
|
|
|
|
if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
|
|
{
|
|
have_ptrace_getfpxregs = TRIBOOL_FALSE;
|
|
have_ptrace_getregset = TRIBOOL_FALSE;
|
|
}
|
|
else
|
|
have_ptrace_getfpxregs = TRIBOOL_TRUE;
|
|
}
|
|
|
|
if (have_ptrace_getfpxregs == TRIBOOL_FALSE)
|
|
return i386_linux_read_description (X86_XSTATE_X87_MASK);
|
|
#endif
|
|
|
|
if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
|
|
{
|
|
uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
|
|
struct iovec iov;
|
|
|
|
iov.iov_base = xstateregs;
|
|
iov.iov_len = sizeof (xstateregs);
|
|
|
|
/* Check if PTRACE_GETREGSET works. */
|
|
if (ptrace (PTRACE_GETREGSET, tid,
|
|
(unsigned int) NT_X86_XSTATE, &iov) < 0)
|
|
{
|
|
/* Can't fetch the xcr0 value so pick a simple default. This
|
|
default has x87 and sse bits set. These bits are ignored for
|
|
amd64 and x32 targets, but are checked for on i386. Without
|
|
these bits being set we generate a completely empty tdesc for
|
|
i386 which will be rejected by GDB. */
|
|
have_ptrace_getregset = TRIBOOL_FALSE;
|
|
*xstate_bv_storage = X86_XSTATE_SSE_MASK;
|
|
}
|
|
else
|
|
{
|
|
have_ptrace_getregset = TRIBOOL_TRUE;
|
|
|
|
/* Get XCR0 from XSAVE extended state. */
|
|
uint64_t xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
|
|
/ sizeof (uint64_t))];
|
|
|
|
*xsave_layout_storage
|
|
= 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 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 (xstate_bv_features_bits, is_x32);
|
|
else
|
|
#endif
|
|
return i386_linux_read_description (xstate_bv_features_bits);
|
|
}
|
|
|
|
#endif /* !IN_PROCESS_AGENT */
|