diff options
author | Andrew Burgess <aburgess@redhat.com> | 2024-01-25 14:25:57 +0000 |
---|---|---|
committer | Andrew Burgess <aburgess@redhat.com> | 2024-06-14 09:08:45 +0100 |
commit | bf616be99153b43c1077be9dbb7b081b4c080031 (patch) | |
tree | f18314f611599ff4767720c2c4b026030d822752 /gdbserver | |
parent | 1845af06191e7a457010b943d06628af731b4549 (diff) | |
download | gdb-bf616be99153b43c1077be9dbb7b081b4c080031.zip gdb-bf616be99153b43c1077be9dbb7b081b4c080031.tar.gz gdb-bf616be99153b43c1077be9dbb7b081b4c080031.tar.bz2 |
gdb/gdbserver: share some code relating to target description creation
This commit is part of a series to share more of the x86 target
description creation code between GDB and gdbserver.
Unlike previous commits which were mostly refactoring, this commit is
the first that makes a real change, though that change should mostly
be for gdbserver; I've largely adopted the "GDB" way of doing things
for gdbserver, and this fixes a real gdbserver bug.
On a x86-64 Linux target, running the test:
gdb.server/connect-with-no-symbol-file.exp
results in two core files being created. Both of these core files are
from the inferior process, created after gdbserver has detached.
In this test a gdbserver process is started and then, after gdbserver
has started, but before GDB attaches, we either delete the inferior
executable, or change its permissions so it can't be read. Only after
doing this do we attempt to connect with GDB.
As GDB connects to gdbserver, gdbserver attempts to figure out the
target description so that it can send the description to GDB, this
involves a call to x86_linux_read_description.
In x86_linux_read_description one of the first things we do is try to
figure out if the process is 32-bit or 64-bit. To do this we look up
the executable via the thread-id, and then attempt to read the
architecture size from the executable. This isn't going to work if
the executable has been deleted, or is no longer readable.
And so, as we can't read the executable, we default to an i386 target
and use an i386 target description.
A consequence of using an i386 target description is that addresses
are assumed to be 32-bits. Here's an example session that shows the
problems this causes. This is run on an x86-64 machine, and the test
binary (xx.x) is a standard 64-bit x86-64 binary:
shell_1$ gdbserver --once localhost :54321 /tmp/xx.x
shell_2$ gdb -q
(gdb) set sysroot
(gdb) shell chmod 000 /tmp/xx.x
(gdb) target remote :54321
Remote debugging using :54321
warning: /tmp/xx.x: Permission denied.
0xf7fd3110 in ?? ()
(gdb) show architecture
The target architecture is set to "auto" (currently "i386").
(gdb) p/x $pc
$1 = 0xf7fd3110
(gdb) info proc mappings
process 2412639
Mapped address spaces:
Start Addr End Addr Size Offset Perms objfile
0x400000 0x401000 0x1000 0x0 r--p /tmp/xx.x
0x401000 0x402000 0x1000 0x1000 r-xp /tmp/xx.x
0x402000 0x403000 0x1000 0x2000 r--p /tmp/xx.x
0x403000 0x405000 0x2000 0x2000 rw-p /tmp/xx.x
0xf7fcb000 0xf7fcf000 0x4000 0x0 r--p [vvar]
0xf7fcf000 0xf7fd1000 0x2000 0x0 r-xp [vdso]
0xf7fd1000 0xf7fd3000 0x2000 0x0 r--p /usr/lib64/ld-2.30.so
0xf7fd3000 0xf7ff3000 0x20000 0x2000 r-xp /usr/lib64/ld-2.30.so
0xf7ff3000 0xf7ffb000 0x8000 0x22000 r--p /usr/lib64/ld-2.30.so
0xf7ffc000 0xf7ffe000 0x2000 0x2a000 rw-p /usr/lib64/ld-2.30.so
0xf7ffe000 0xf7fff000 0x1000 0x0 rw-p
0xfffda000 0xfffff000 0x25000 0x0 rw-p [stack]
0xff600000 0xff601000 0x1000 0x0 r-xp [vsyscall]
(gdb) info inferiors
Num Description Connection Executable
* 1 process 2412639 1 (remote :54321)
(gdb) shell cat /proc/2412639/maps
00400000-00401000 r--p 00000000 fd:03 45907133 /tmp/xx.x
00401000-00402000 r-xp 00001000 fd:03 45907133 /tmp/xx.x
00402000-00403000 r--p 00002000 fd:03 45907133 /tmp/xx.x
00403000-00405000 rw-p 00002000 fd:03 45907133 /tmp/xx.x
7ffff7fcb000-7ffff7fcf000 r--p 00000000 00:00 0 [vvar]
7ffff7fcf000-7ffff7fd1000 r-xp 00000000 00:00 0 [vdso]
7ffff7fd1000-7ffff7fd3000 r--p 00000000 fd:00 143904 /usr/lib64/ld-2.30.so
7ffff7fd3000-7ffff7ff3000 r-xp 00002000 fd:00 143904 /usr/lib64/ld-2.30.so
7ffff7ff3000-7ffff7ffb000 r--p 00022000 fd:00 143904 /usr/lib64/ld-2.30.so
7ffff7ffc000-7ffff7ffe000 rw-p 0002a000 fd:00 143904 /usr/lib64/ld-2.30.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffda000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
(gdb)
Notice the difference between the mappings reported via GDB and those
reported directly from the kernel via /proc/PID/maps, the addresses of
every mapping is clamped to 32-bits for GDB, while the kernel reports
real 64-bit addresses.
Notice also that the $pc value is a 32-bit value. It appears to be
within one of the mappings reported by GDB, but is outside any of the
mappings reported from the kernel.
And this is where the problem arises. When gdbserver detaches from
the inferior we pass the inferior the address from which it should
resume. Due to the 32/64 bit confusion we tell the inferior to resume
from the 32-bit $pc value, which is not within any valid mapping, and
so, as soon as the inferior resumes, it segfaults.
If we look at how GDB (not gdbserver) figures out its target
description then we see an interesting difference. GDB doesn't try to
read the executable. Instead GDB uses ptrace to query the thread's
state, and uses this to figure out the if the thread is 32 or 64 bit.
If we update gdbserver to do it the "GDB" way then the above problem
is resolved, gdbserver now sees the process as 64-bit, and when we
detach from the inferior we give it the correct 64-bit address, and
the inferior no longer segfaults.
Now, I could just update the gdbserver code, but better, I think, to
share one copy of the code between GDB and gdbserver in gdb/nat/.
That is what this commit does.
The cores of x86_linux_read_description from gdbserver and
x86_linux_nat_target::read_description from GDB are moved into a new
file gdb/nat/x86-linux-tdesc.c and combined into a single function
x86_linux_tdesc_for_tid which is called from each location.
This new function does things mostly the GDB way, some changes are
needed to allow for the sharing; we now take some pointers for where
the shared code can cache the xcr0 and xsave layout values.
Another thing to note about this commit is how the functions
i386_linux_read_description and amd64_linux_read_description are
handled. For now I've left these function as implemented separately
in GDB and gdbserver. I've moved the declarations of these functions
into gdb/arch/{i386,amd64}-linux-tdesc.h, but the implementations are
left where they are.
A later commit in this series will make these functions shared too,
but doing this is not trivial, so I've left that for a separate
commit. Merging the declarations as I've done here ensures that
everyone implements the function to the same API, and once these
functions are shared (in a later commit) we'll want a shared
declaration anyway.
Reviewed-By: Felix Willgerodt <felix.willgerodt@intel.com>
Acked-By: John Baldwin <jhb@FreeBSD.org>
Diffstat (limited to 'gdbserver')
-rw-r--r-- | gdbserver/configure.srv | 2 | ||||
-rw-r--r-- | gdbserver/i387-fp.cc | 9 | ||||
-rw-r--r-- | gdbserver/i387-fp.h | 4 | ||||
-rw-r--r-- | gdbserver/linux-amd64-ipa.cc | 1 | ||||
-rw-r--r-- | gdbserver/linux-i386-ipa.cc | 1 | ||||
-rw-r--r-- | gdbserver/linux-x86-low.cc | 142 | ||||
-rw-r--r-- | gdbserver/linux-x86-tdesc.cc | 2 | ||||
-rw-r--r-- | gdbserver/linux-x86-tdesc.h | 7 |
8 files changed, 52 insertions, 116 deletions
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index 8e882d2..538845b 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -110,6 +110,7 @@ case "${gdbserver_host}" in srv_tgtobj="${srv_tgtobj} nat/x86-linux.o" srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o" srv_tgtobj="${srv_tgtobj} nat/i386-linux.o" + srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o" srv_linux_usrregs=yes srv_linux_regsets=yes srv_linux_thread_db=yes @@ -372,6 +373,7 @@ case "${gdbserver_host}" in srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o" srv_tgtobj="${srv_tgtobj} nat/x86-linux.o" srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o" + srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o" srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o" srv_linux_usrregs=yes # This is for i386 progs. srv_linux_regsets=yes diff --git a/gdbserver/i387-fp.cc b/gdbserver/i387-fp.cc index 62cafd8..4d8bcb5 100644 --- a/gdbserver/i387-fp.cc +++ b/gdbserver/i387-fp.cc @@ -21,7 +21,7 @@ #include "nat/x86-xstate.h" /* Default to SSE. */ -static unsigned long long x86_xcr0 = X86_XSTATE_SSE_MASK; +static uint64_t x86_xcr0 = X86_XSTATE_SSE_MASK; static const int num_mpx_bnd_registers = 4; static const int num_mpx_cfg_registers = 2; @@ -944,9 +944,8 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf) /* See i387-fp.h. */ -void -i387_set_xsave_mask (uint64_t xcr0, int len) +std::pair<uint64_t *, x86_xsave_layout *> +i387_get_xsave_storage () { - x86_xcr0 = xcr0; - xsave_layout = x86_fetch_xsave_layout (xcr0, len); + return { &x86_xcr0, &xsave_layout }; } diff --git a/gdbserver/i387-fp.h b/gdbserver/i387-fp.h index 450466e..4ee21da 100644 --- a/gdbserver/i387-fp.h +++ b/gdbserver/i387-fp.h @@ -19,6 +19,8 @@ #ifndef GDBSERVER_I387_FP_H #define GDBSERVER_I387_FP_H +struct x86_xsave_layout; + void i387_cache_to_fsave (struct regcache *regcache, void *buf); void i387_fsave_to_cache (struct regcache *regcache, const void *buf); @@ -30,6 +32,6 @@ void i387_xsave_to_cache (struct regcache *regcache, const void *buf); /* Set the XSAVE mask and fetch the XSAVE layout via CPUID. */ -void i387_set_xsave_mask (uint64_t xcr0, int len); +std::pair<uint64_t *, x86_xsave_layout *> i387_get_xsave_storage (); #endif /* GDBSERVER_I387_FP_H */ diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc index a634675..df5e6ac 100644 --- a/gdbserver/linux-amd64-ipa.cc +++ b/gdbserver/linux-amd64-ipa.cc @@ -22,6 +22,7 @@ #include "tracepoint.h" #include "linux-x86-tdesc.h" #include "gdbsupport/x86-xstate.h" +#include "arch/amd64-linux-tdesc.h" /* fast tracepoints collect registers. */ diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc index 8f14e09..aa346fc 100644 --- a/gdbserver/linux-i386-ipa.cc +++ b/gdbserver/linux-i386-ipa.cc @@ -22,6 +22,7 @@ #include "tracepoint.h" #include "linux-x86-tdesc.h" #include "gdbsupport/x86-xstate.h" +#include "arch/i386-linux-tdesc.h" /* GDB register numbers. */ diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc index 2d99a82..efb6797 100644 --- a/gdbserver/linux-x86-low.cc +++ b/gdbserver/linux-x86-low.cc @@ -29,10 +29,13 @@ #ifdef __x86_64__ #include "nat/amd64-linux-siginfo.h" +#include "arch/amd64-linux-tdesc.h" #else #include "nat/i386-linux.h" #endif +#include "arch/i386-linux-tdesc.h" + #include "gdb_proc_service.h" /* Don't include elf/common.h if linux/elf.h got included by gdb_proc_service.h. */ @@ -48,6 +51,7 @@ #include "nat/x86-linux.h" #include "nat/x86-linux-dregs.h" #include "linux-x86-tdesc.h" +#include "nat/x86-linux-tdesc.h" #ifdef __x86_64__ static target_desc_up tdesc_amd64_linux_no_xml; @@ -836,29 +840,9 @@ static int use_xml; /* Get Linux/x86 target description from running target. */ static const struct target_desc * -x86_linux_read_description (void) +x86_linux_read_description () { - unsigned int machine; - int is_elf64; - int xcr0_features; - int tid; - static uint64_t xcr0; - static int xsave_len; - struct regset_info *regset; - - tid = lwpid_of (current_thread); - - is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine); - - if (sizeof (void *) == 4) - { - if (is_elf64 > 0) - error (_("Can't debug 64-bit process with 32-bit GDBserver")); -#ifndef __x86_64__ - else if (machine == EM_X86_64) - error (_("Can't debug x86-64 process with 32-bit GDBserver")); -#endif - } + int tid = lwpid_of (current_thread); /* If we are not allowed to send an XML target description then we need to use the hard-wired target descriptions. This corresponds to GDB's @@ -868,103 +852,55 @@ x86_linux_read_description (void) generate some alternative target descriptions. */ if (!use_xml) { + 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) + error (_("Can't debug 64-bit process with 32-bit GDBserver")); + #ifdef __x86_64__ - if (machine == EM_X86_64) + if (is_64bit && !is_x32) return tdesc_amd64_linux_no_xml.get (); else #endif return tdesc_i386_linux_no_xml.get (); } -#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS - if (machine == EM_386 && have_ptrace_getfpxregs == TRIBOOL_UNKNOWN) - { - elf_fpxregset_t fpxregs; - - if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0) - { - have_ptrace_getfpxregs = TRIBOOL_FALSE; - have_ptrace_getregset = TRIBOOL_FALSE; - return i386_linux_read_description (X86_XSTATE_X87); - } - else - have_ptrace_getfpxregs = TRIBOOL_TRUE; - } -#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, (long) &iov) < 0) - have_ptrace_getregset = TRIBOOL_FALSE; - else - { - have_ptrace_getregset = TRIBOOL_TRUE; - - /* Get XCR0 from XSAVE extended state. */ - xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET - / sizeof (uint64_t))]; - - /* No MPX on x32. */ - if (machine == EM_X86_64 && !is_elf64) - xcr0 &= ~X86_XSTATE_MPX; - - xsave_len = x86_xsave_length (); - - /* Use PTRACE_GETREGSET if it is available. */ - for (regset = x86_regsets; - regset->fill_function != NULL; regset++) - if (regset->get_request == PTRACE_GETREGSET) - regset->size = xsave_len; - else if (regset->type != GENERAL_REGS) - regset->size = 0; - } - } + /* If have_ptrace_getregset is changed to true by calling + x86_linux_tdesc_for_tid then we will perform some additional + initialisation. */ + bool have_ptrace_getregset_was_unknown + = have_ptrace_getregset == TRIBOOL_UNKNOWN; - /* Check the native XCR0 only if PTRACE_GETREGSET is available. */ - xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE - && (xcr0 & X86_XSTATE_ALL_MASK)); + /* Get pointers to where we should store the xcr0 and xsave_layout + values. These will be filled in by x86_linux_tdesc_for_tid the first + time that the function is called. Subsequent calls will not modify + the stored values. */ + std::pair<uint64_t *, x86_xsave_layout *> storage + = i387_get_xsave_storage (); - if (xcr0_features) - i387_set_xsave_mask (xcr0, xsave_len); + const target_desc *tdesc + = x86_linux_tdesc_for_tid (tid, storage.first, storage.second); - if (machine == EM_X86_64) + if (have_ptrace_getregset_was_unknown + && have_ptrace_getregset == TRIBOOL_TRUE) { -#ifdef __x86_64__ - const target_desc *tdesc = NULL; + int xsave_len = x86_xsave_length (); - if (xcr0_features) + /* Use PTRACE_GETREGSET if it is available. */ + for (regset_info *regset = x86_regsets; + regset->fill_function != nullptr; + regset++) { - tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK, - !is_elf64); + if (regset->get_request == PTRACE_GETREGSET) + regset->size = xsave_len; + else if (regset->type != GENERAL_REGS) + regset->size = 0; } - - if (tdesc == NULL) - tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64); - return tdesc; -#endif - } - else - { - const target_desc *tdesc = NULL; - - if (xcr0_features) - tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK); - - if (tdesc == NULL) - tdesc = i386_linux_read_description (X86_XSTATE_SSE); - - return tdesc; } - gdb_assert_not_reached ("failed to return tdesc"); + return tdesc; } /* Update all the target description of all processes; a new GDB diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc index cd3b5d8..af3735a 100644 --- a/gdbserver/linux-x86-tdesc.cc +++ b/gdbserver/linux-x86-tdesc.cc @@ -23,8 +23,10 @@ #include "gdbsupport/x86-xstate.h" #ifdef __x86_64__ #include "arch/amd64.h" +#include "arch/amd64-linux-tdesc.h" #endif #include "x86-tdesc.h" +#include "arch/i386-linux-tdesc.h" /* Return the right x86_linux_tdesc index for a given XCR0. Return X86_TDESC_LAST if can't find a match. */ diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h index f9561b1..576aaf5 100644 --- a/gdbserver/linux-x86-tdesc.h +++ b/gdbserver/linux-x86-tdesc.h @@ -46,11 +46,4 @@ int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc); const struct target_desc *i386_get_ipa_tdesc (int idx); -#ifdef __x86_64__ -const struct target_desc *amd64_linux_read_description (uint64_t xcr0, - bool is_x32); -#endif - -const struct target_desc *i386_linux_read_description (uint64_t xcr0); - #endif /* GDBSERVER_LINUX_X86_TDESC_H */ |