mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-30 21:44:19 +08:00
d1e93af64a
PR 27147 shows that on sparc64, GDB is unable to properly unwind:
Expected result (from GDB 9.2):
#0 0x0000000000108de4 in puts ()
#1 0x0000000000100950 in hello () at gdb-test.c:4
#2 0x0000000000100968 in main () at gdb-test.c:8
Actual result (from GDB latest git):
#0 0x0000000000108de4 in puts ()
#1 0x0000000000100950 in hello () at gdb-test.c:4
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
The first failing commit is 5b6d1e4fa4
("Multi-target support"). The cause
of the change in behavior is due to (thanks for Andrew Burgess for finding
this):
- inferior_ptid is no longer set on entry of target_ops::wait, whereas
it was set to something valid previously
- deep down in linux_nat_target::wait (see stack trace below), we fetch
the registers of the event thread
- on sparc64, fetching registers involves reading memory (in
sparc_supply_rwindow, see stack trace below)
- reading memory (target_ops::xfer_partial) relies on inferior_ptid
being set to the thread from which we want to read memory
This is where things go wrong:
#0 linux_nat_target::xfer_partial (this=0x10000fa2c40 <the_sparc64_linux_nat_target>, object=TARGET_OBJECT_MEMORY, annex=0x0, readbuf=0x7feffe3b000 "", writebuf=0x0, offset=8791798050744, len=8, xfered_len=0x7feffe3ae88) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:3697
#1 0x00000100007f5b10 in raw_memory_xfer_partial (ops=0x10000fa2c40 <the_sparc64_linux_nat_target>, readbuf=0x7feffe3b000 "", writebuf=0x0, memaddr=8791798050744, len=8, xfered_len=0x7feffe3ae88) at /home/simark/src/binutils-gdb/gdb/target.c:912
#2 0x00000100007f60e8 in memory_xfer_partial_1 (ops=0x10000fa2c40 <the_sparc64_linux_nat_target>, object=TARGET_OBJECT_MEMORY, readbuf=0x7feffe3b000 "", writebuf=0x0, memaddr=8791798050744, len=8, xfered_len=0x7feffe3ae88) at /home/simark/src/binutils-gdb/gdb/target.c:1043
#3 0x00000100007f61b4 in memory_xfer_partial (ops=0x10000fa2c40 <the_sparc64_linux_nat_target>, object=TARGET_OBJECT_MEMORY, readbuf=0x7feffe3b000 "", writebuf=0x0, memaddr=8791798050744, len=8, xfered_len=0x7feffe3ae88) at /home/simark/src/binutils-gdb/gdb/target.c:1072
#4 0x00000100007f6538 in target_xfer_partial (ops=0x10000fa2c40 <the_sparc64_linux_nat_target>, object=TARGET_OBJECT_MEMORY, annex=0x0, readbuf=0x7feffe3b000 "", writebuf=0x0, offset=8791798050744, len=8, xfered_len=0x7feffe3ae88) at /home/simark/src/binutils-gdb/gdb/target.c:1129
#5 0x00000100007f7094 in target_read_partial (ops=0x10000fa2c40 <the_sparc64_linux_nat_target>, object=TARGET_OBJECT_MEMORY, annex=0x0, buf=0x7feffe3b000 "", offset=8791798050744, len=8, xfered_len=0x7feffe3ae88) at /home/simark/src/binutils-gdb/gdb/target.c:1375
#6 0x00000100007f721c in target_read (ops=0x10000fa2c40 <the_sparc64_linux_nat_target>, object=TARGET_OBJECT_MEMORY, annex=0x0, buf=0x7feffe3b000 "", offset=8791798050744, len=8) at /home/simark/src/binutils-gdb/gdb/target.c:1415
#7 0x00000100007f69d4 in target_read_memory (memaddr=8791798050744, myaddr=0x7feffe3b000 "", len=8) at /home/simark/src/binutils-gdb/gdb/target.c:1218
#8 0x0000010000758520 in sparc_supply_rwindow (regcache=0x10000fea4f0, sp=8791798050736, regnum=-1) at /home/simark/src/binutils-gdb/gdb/sparc-tdep.c:1960
#9 0x000001000076208c in sparc64_supply_gregset (gregmap=0x10000be3190 <sparc64_linux_ptrace_gregmap>, regcache=0x10000fea4f0, regnum=-1, gregs=0x7feffe3b230) at /home/simark/src/binutils-gdb/gdb/sparc64-tdep.c:1974
#10 0x0000010000751b64 in sparc_fetch_inferior_registers (regcache=0x10000fea4f0, regnum=80) at /home/simark/src/binutils-gdb/gdb/sparc-nat.c:170
#11 0x0000010000759d68 in sparc64_linux_nat_target::fetch_registers (this=0x10000fa2c40 <the_sparc64_linux_nat_target>, regcache=0x10000fea4f0, regnum=80) at /home/simark/src/binutils-gdb/gdb/sparc64-linux-nat.c:38
#12 0x00000100008146ec in target_fetch_registers (regcache=0x10000fea4f0, regno=80) at /home/simark/src/binutils-gdb/gdb/target.c:3287
#13 0x00000100006a8c5c in regcache::raw_update (this=0x10000fea4f0, regnum=80) at /home/simark/src/binutils-gdb/gdb/regcache.c:584
#14 0x00000100006a8d94 in readable_regcache::raw_read (this=0x10000fea4f0, regnum=80, buf=0x7feffe3b7c0 "") at /home/simark/src/binutils-gdb/gdb/regcache.c:598
#15 0x00000100006a93b8 in readable_regcache::cooked_read (this=0x10000fea4f0, regnum=80, buf=0x7feffe3b7c0 "") at /home/simark/src/binutils-gdb/gdb/regcache.c:690
#16 0x00000100006b288c in readable_regcache::cooked_read<unsigned long, void> (this=0x10000fea4f0, regnum=80, val=0x7feffe3b948) at /home/simark/src/binutils-gdb/gdb/regcache.c:777
#17 0x00000100006a9b44 in regcache_cooked_read_unsigned (regcache=0x10000fea4f0, regnum=80, val=0x7feffe3b948) at /home/simark/src/binutils-gdb/gdb/regcache.c:791
#18 0x00000100006abf3c in regcache_read_pc (regcache=0x10000fea4f0) at /home/simark/src/binutils-gdb/gdb/regcache.c:1295
#19 0x0000010000507920 in save_stop_reason (lp=0x10000fc5b10) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:2612
#20 0x00000100005095a4 in linux_nat_filter_event (lwpid=520983, status=1407) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:3050
#21 0x0000010000509f9c in linux_nat_wait_1 (ptid=..., ourstatus=0x7feffe3c8f0, target_options=...) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:3194
#22 0x000001000050b1d0 in linux_nat_target::wait (this=0x10000fa2c40 <the_sparc64_linux_nat_target>, ptid=..., ourstatus=0x7feffe3c8f0, target_options=...) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:3432
#23 0x00000100007f8ac0 in target_wait (ptid=..., status=0x7feffe3c8f0, options=...) at /home/simark/src/binutils-gdb/gdb/target.c:2000
#24 0x00000100004ac17c in do_target_wait_1 (inf=0x1000116d280, ptid=..., status=0x7feffe3c8f0, options=...) at /home/simark/src/binutils-gdb/gdb/infrun.c:3464
#25 0x00000100004ac3b8 in operator() (__closure=0x7feffe3c678, inf=0x1000116d280) at /home/simark/src/binutils-gdb/gdb/infrun.c:3527
#26 0x00000100004ac7cc in do_target_wait (wait_ptid=..., ecs=0x7feffe3c8c8, options=...) at /home/simark/src/binutils-gdb/gdb/infrun.c:3540
#27 0x00000100004ad8c4 in fetch_inferior_event () at /home/simark/src/binutils-gdb/gdb/infrun.c:3880
#28 0x0000010000485568 in inferior_event_handler (event_type=INF_REG_EVENT) at /home/simark/src/binutils-gdb/gdb/inf-loop.c:42
#29 0x000001000050d394 in handle_target_event (error=0, client_data=0x0) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:4060
#30 0x0000010000ab5c8c in handle_file_event (file_ptr=0x10001207270, ready_mask=1) at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:575
#31 0x0000010000ab6334 in gdb_wait_for_event (block=0) at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:701
#32 0x0000010000ab487c in gdb_do_one_event () at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:212
#33 0x0000010000542668 in start_event_loop () at /home/simark/src/binutils-gdb/gdb/main.c:348
#34 0x000001000054287c in captured_command_loop () at /home/simark/src/binutils-gdb/gdb/main.c:408
#35 0x0000010000544e84 in captured_main (data=0x7feffe3d188) at /home/simark/src/binutils-gdb/gdb/main.c:1242
#36 0x0000010000544f2c in gdb_main (args=0x7feffe3d188) at /home/simark/src/binutils-gdb/gdb/main.c:1257
#37 0x00000100000c1f14 in main (argc=4, argv=0x7feffe3d548) at /home/simark/src/binutils-gdb/gdb/gdb.c:32
There is a target_read_memory call in sparc_supply_rwindow, whose return
value is not checked. That call fails, because inferior_ptid does not
contain a valid ptid, and uninitialized buffer contents is used.
Ultimately it results in a corrupt stop_pc.
target_ops::fetch_registers can be (and should remain, in my opinion)
independent of inferior_ptid, because the ptid of the thread from which
to fetch registers can be obtained from the regcache. In other words,
implementations of target_ops::fetch_registers should not rely on
inferior_ptid having a sensible value on entry.
The sparc64_linux_nat_target::fetch_registers case is special, because it calls
a target method that is dependent on the inferior_ptid value
(target_read_inferior, and ultimately target_ops::xfer_partial). So I would
say it's the responsibility of sparc64_linux_nat_target::fetch_registers to set
up inferior_ptid correctly prior to calling target_read_inferior.
This patch makes sparc64_linux_nat_target::fetch_registers (and
store_registers, since it works the same) temporarily set inferior_ptid. If we
ever make target_ops::xfer_partial independent of inferior_ptid, setting
inferior_ptid won't be necessary, we'll simply pass down the ptid as a
parameter in some way.
I chose to set/restore inferior_ptid in sparc_fetch_inferior_registers, because
I am not convinced that doing so in an inner location (in sparc_supply_rwindow
for instance) would always be correct. We have access to the ptid in
sparc_supply_rwindow (from the regcache), so we _could_ set inferior_ptid
there. However, I don't want to just set inferior_ptid, as that would make it
not desync'ed with `current_thread ()` and `current_inferior ()`. It's
preferable to use switch_to_thread instead, as that switches all the global
"current" stuff in a coherent way. But doing so requires a `thread_info *`,
and getting a `thread_info *` from a ptid requires a `process_stratum_target
*`. We could use `current_inferior()->process_target()` in
sparc_supply_rwindow for this (using target_read_memory uses the current
inferior's target stack anyway). However, sparc_supply_rwindow is also used in
the context of BSD uthreads, where a thread stratum target defines threads. I
presume the ptid in the regcache would be the ptid of the uthread, defined by
the thread stratum target (bsd_uthread_target). Using
`current_inferior()->process_target()` would look up a ptid defined by the
thread stratum target using the process stratum target. I don't think it would
give good results. So I prefer playing it safe and looking up the thread
earlier, in sparc_fetch_inferior_registers.
I added some assertions (in sparc_supply_rwindow and others) to verify
that the regcache's ptid matches inferior_ptid. That verifies that the
caller has properly set the correct global context. This would have
caught (though a failed assertion) the current problem.
gdb/ChangeLog:
PR gdb/27147
* sparc-nat.h (sparc_fetch_inferior_registers): Add
process_stratum_target parameter,
sparc_store_inferior_registers): update callers.
* sparc-nat.c (sparc_fetch_inferior_registers,
sparc_store_inferior_registers): Add process_stratum_target
parameter. Switch current thread before calling
sparc_supply_gregset / sparc_collect_rwindow.
(sparc_store_inferior_registers): Likewise.
* sparc-obsd-tdep.c (sparc32obsd_supply_uthread): Add assertion.
(sparc32obsd_collect_uthread): Likewise.
* sparc-tdep.c (sparc_supply_rwindow, sparc_collect_rwindow):
Add assertion.
* sparc64-obsd-tdep.c (sparc64obsd_collect_uthread,
sparc64obsd_supply_uthread): Add assertion.
Change-Id: I16c658cd70896cea604516714f7e2428fbaf4301
339 lines
10 KiB
C
339 lines
10 KiB
C
/* Native-dependent code for SPARC.
|
||
|
||
Copyright (C) 2003-2021 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 "defs.h"
|
||
#include "inferior.h"
|
||
#include "regcache.h"
|
||
#include "target.h"
|
||
|
||
#include <signal.h>
|
||
#include <sys/ptrace.h>
|
||
#include "gdbsupport/gdb_wait.h"
|
||
#ifdef HAVE_MACHINE_REG_H
|
||
#include <machine/reg.h>
|
||
#endif
|
||
|
||
#include "sparc-tdep.h"
|
||
#include "sparc-nat.h"
|
||
#include "inf-ptrace.h"
|
||
|
||
/* With some trickery we can use the code in this file for most (if
|
||
not all) ptrace(2) based SPARC systems, which includes SunOS 4,
|
||
GNU/Linux and the various SPARC BSD's.
|
||
|
||
First, we need a data structure for use with ptrace(2). SunOS has
|
||
`struct regs' and `struct fp_status' in <machine/reg.h>. BSD's
|
||
have `struct reg' and `struct fpreg' in <machine/reg.h>. GNU/Linux
|
||
has the same structures as SunOS 4, but they're in <asm/reg.h>,
|
||
which is a kernel header. As a general rule we avoid including
|
||
GNU/Linux kernel headers. Fortunately GNU/Linux has a `gregset_t'
|
||
and a `fpregset_t' that are equivalent to `struct regs' and `struct
|
||
fp_status' in <sys/ucontext.h>, which is automatically included by
|
||
<signal.h>. Settling on using the `gregset_t' and `fpregset_t'
|
||
typedefs, providing them for the other systems, therefore solves
|
||
the puzzle. */
|
||
|
||
#ifdef HAVE_MACHINE_REG_H
|
||
#ifdef HAVE_STRUCT_REG
|
||
typedef struct reg gregset_t;
|
||
typedef struct fpreg fpregset_t;
|
||
#else
|
||
typedef struct regs gregset_t;
|
||
typedef struct fp_status fpregset_t;
|
||
#endif
|
||
#endif
|
||
|
||
/* Second, we need to remap the BSD ptrace(2) requests to their SunOS
|
||
equivalents. GNU/Linux already follows SunOS here. */
|
||
|
||
#ifndef PTRACE_GETREGS
|
||
#define PTRACE_GETREGS PT_GETREGS
|
||
#endif
|
||
|
||
#ifndef PTRACE_SETREGS
|
||
#define PTRACE_SETREGS PT_SETREGS
|
||
#endif
|
||
|
||
#ifndef PTRACE_GETFPREGS
|
||
#define PTRACE_GETFPREGS PT_GETFPREGS
|
||
#endif
|
||
|
||
#ifndef PTRACE_SETFPREGS
|
||
#define PTRACE_SETFPREGS PT_SETFPREGS
|
||
#endif
|
||
|
||
static PTRACE_TYPE_RET
|
||
gdb_ptrace (PTRACE_TYPE_ARG1 request, ptid_t ptid, PTRACE_TYPE_ARG3 addr)
|
||
{
|
||
#ifdef __NetBSD__
|
||
/* Support for NetBSD threads: unlike other ptrace implementations in this
|
||
file, NetBSD requires that we pass both the pid and lwp. */
|
||
return ptrace (request, ptid.pid (), addr, ptid.lwp ());
|
||
#else
|
||
pid_t pid = get_ptrace_pid (ptid);
|
||
return ptrace (request, pid, addr, 0);
|
||
#endif
|
||
}
|
||
|
||
/* Register set description. */
|
||
const struct sparc_gregmap *sparc_gregmap;
|
||
const struct sparc_fpregmap *sparc_fpregmap;
|
||
void (*sparc_supply_gregset) (const struct sparc_gregmap *,
|
||
struct regcache *, int , const void *);
|
||
void (*sparc_collect_gregset) (const struct sparc_gregmap *,
|
||
const struct regcache *, int, void *);
|
||
void (*sparc_supply_fpregset) (const struct sparc_fpregmap *,
|
||
struct regcache *, int , const void *);
|
||
void (*sparc_collect_fpregset) (const struct sparc_fpregmap *,
|
||
const struct regcache *, int , void *);
|
||
int (*sparc_gregset_supplies_p) (struct gdbarch *, int);
|
||
int (*sparc_fpregset_supplies_p) (struct gdbarch *, int);
|
||
|
||
/* Determine whether `gregset_t' contains register REGNUM. */
|
||
|
||
int
|
||
sparc32_gregset_supplies_p (struct gdbarch *gdbarch, int regnum)
|
||
{
|
||
/* Integer registers. */
|
||
if ((regnum >= SPARC_G1_REGNUM && regnum <= SPARC_G7_REGNUM)
|
||
|| (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM)
|
||
|| (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_L7_REGNUM)
|
||
|| (regnum >= SPARC_I0_REGNUM && regnum <= SPARC_I7_REGNUM))
|
||
return 1;
|
||
|
||
/* Control registers. */
|
||
if (regnum == SPARC32_PC_REGNUM
|
||
|| regnum == SPARC32_NPC_REGNUM
|
||
|| regnum == SPARC32_PSR_REGNUM
|
||
|| regnum == SPARC32_Y_REGNUM)
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Determine whether `fpregset_t' contains register REGNUM. */
|
||
|
||
int
|
||
sparc32_fpregset_supplies_p (struct gdbarch *gdbarch, int regnum)
|
||
{
|
||
/* Floating-point registers. */
|
||
if (regnum >= SPARC_F0_REGNUM && regnum <= SPARC_F31_REGNUM)
|
||
return 1;
|
||
|
||
/* Control registers. */
|
||
if (regnum == SPARC32_FSR_REGNUM)
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Fetch register REGNUM from the inferior. If REGNUM is -1, do this
|
||
for all registers (including the floating-point registers). */
|
||
|
||
void
|
||
sparc_fetch_inferior_registers (process_stratum_target *proc_target,
|
||
regcache *regcache, int regnum)
|
||
{
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
ptid_t ptid = regcache->ptid ();
|
||
|
||
if (regnum == SPARC_G0_REGNUM)
|
||
{
|
||
gdb_byte zero[8] = { 0 };
|
||
|
||
regcache->raw_supply (SPARC_G0_REGNUM, &zero);
|
||
return;
|
||
}
|
||
|
||
if (regnum == -1 || sparc_gregset_supplies_p (gdbarch, regnum))
|
||
{
|
||
gregset_t regs;
|
||
|
||
if (gdb_ptrace (PTRACE_GETREGS, ptid, (PTRACE_TYPE_ARG3) ®s) == -1)
|
||
perror_with_name (_("Couldn't get registers"));
|
||
|
||
/* Deep down, sparc_supply_rwindow reads memory, so needs the global
|
||
thread context to be set. */
|
||
thread_info *thread = find_thread_ptid (proc_target, ptid);
|
||
scoped_restore_current_thread restore_thread;
|
||
switch_to_thread (thread);
|
||
|
||
sparc_supply_gregset (sparc_gregmap, regcache, -1, ®s);
|
||
if (regnum != -1)
|
||
return;
|
||
}
|
||
|
||
if (regnum == -1 || sparc_fpregset_supplies_p (gdbarch, regnum))
|
||
{
|
||
fpregset_t fpregs;
|
||
|
||
if (gdb_ptrace (PTRACE_GETFPREGS, ptid, (PTRACE_TYPE_ARG3) &fpregs) == -1)
|
||
perror_with_name (_("Couldn't get floating point status"));
|
||
|
||
sparc_supply_fpregset (sparc_fpregmap, regcache, -1, &fpregs);
|
||
}
|
||
}
|
||
|
||
void
|
||
sparc_store_inferior_registers (process_stratum_target *proc_target,
|
||
regcache *regcache, int regnum)
|
||
{
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
ptid_t ptid = regcache->ptid ();
|
||
|
||
if (regnum == -1 || sparc_gregset_supplies_p (gdbarch, regnum))
|
||
{
|
||
gregset_t regs;
|
||
|
||
if (gdb_ptrace (PTRACE_GETREGS, ptid, (PTRACE_TYPE_ARG3) ®s) == -1)
|
||
perror_with_name (_("Couldn't get registers"));
|
||
|
||
sparc_collect_gregset (sparc_gregmap, regcache, regnum, ®s);
|
||
|
||
if (gdb_ptrace (PTRACE_SETREGS, ptid, (PTRACE_TYPE_ARG3) ®s) == -1)
|
||
perror_with_name (_("Couldn't write registers"));
|
||
|
||
/* Deal with the stack regs. */
|
||
if (regnum == -1 || regnum == SPARC_SP_REGNUM
|
||
|| (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM))
|
||
{
|
||
ULONGEST sp;
|
||
|
||
regcache_cooked_read_unsigned (regcache, SPARC_SP_REGNUM, &sp);
|
||
|
||
/* Deep down, sparc_collect_rwindow writes memory, so needs the global
|
||
thread context to be set. */
|
||
thread_info *thread = find_thread_ptid (proc_target, ptid);
|
||
scoped_restore_current_thread restore_thread;
|
||
switch_to_thread (thread);
|
||
|
||
sparc_collect_rwindow (regcache, sp, regnum);
|
||
}
|
||
|
||
if (regnum != -1)
|
||
return;
|
||
}
|
||
|
||
if (regnum == -1 || sparc_fpregset_supplies_p (gdbarch, regnum))
|
||
{
|
||
fpregset_t fpregs, saved_fpregs;
|
||
|
||
if (gdb_ptrace (PTRACE_GETFPREGS, ptid, (PTRACE_TYPE_ARG3) &fpregs) == -1)
|
||
perror_with_name (_("Couldn't get floating-point registers"));
|
||
|
||
memcpy (&saved_fpregs, &fpregs, sizeof (fpregs));
|
||
sparc_collect_fpregset (sparc_fpregmap, regcache, regnum, &fpregs);
|
||
|
||
/* Writing the floating-point registers will fail on NetBSD with
|
||
EINVAL if the inferior process doesn't have an FPU state
|
||
(i.e. if it didn't use the FPU yet). Therefore we don't try
|
||
to write the registers if nothing changed. */
|
||
if (memcmp (&saved_fpregs, &fpregs, sizeof (fpregs)) != 0)
|
||
{
|
||
if (gdb_ptrace (PTRACE_SETFPREGS, ptid,
|
||
(PTRACE_TYPE_ARG3) &fpregs) == -1)
|
||
perror_with_name (_("Couldn't write floating-point registers"));
|
||
}
|
||
|
||
if (regnum != -1)
|
||
return;
|
||
}
|
||
}
|
||
|
||
|
||
/* Implement the to_xfer_partial target_ops method for
|
||
TARGET_OBJECT_WCOOKIE. Fetch StackGhost Per-Process XOR cookie. */
|
||
|
||
enum target_xfer_status
|
||
sparc_xfer_wcookie (enum target_object object,
|
||
const char *annex, gdb_byte *readbuf,
|
||
const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
|
||
ULONGEST *xfered_len)
|
||
{
|
||
unsigned long wcookie = 0;
|
||
char *buf = (char *)&wcookie;
|
||
|
||
gdb_assert (object == TARGET_OBJECT_WCOOKIE);
|
||
gdb_assert (readbuf && writebuf == NULL);
|
||
|
||
if (offset == sizeof (unsigned long))
|
||
return TARGET_XFER_EOF; /* Signal EOF. */
|
||
if (offset > sizeof (unsigned long))
|
||
return TARGET_XFER_E_IO;
|
||
|
||
#ifdef PT_WCOOKIE
|
||
/* If PT_WCOOKIE is defined (by <sys/ptrace.h>), assume we're
|
||
running on an OpenBSD release that uses StackGhost (3.1 or
|
||
later). Since release 3.6, OpenBSD uses a fully randomized
|
||
cookie. */
|
||
{
|
||
int pid = inferior_ptid.pid ();
|
||
|
||
/* Sanity check. The proper type for a cookie is register_t, but
|
||
we can't assume that this type exists on all systems supported
|
||
by the code in this file. */
|
||
gdb_assert (sizeof (wcookie) == sizeof (register_t));
|
||
|
||
/* Fetch the cookie. */
|
||
if (ptrace (PT_WCOOKIE, pid, (PTRACE_TYPE_ARG3) &wcookie, 0) == -1)
|
||
{
|
||
if (errno != EINVAL)
|
||
perror_with_name (_("Couldn't get StackGhost cookie"));
|
||
|
||
/* Although PT_WCOOKIE is defined on OpenBSD 3.1 and later,
|
||
the request wasn't implemented until after OpenBSD 3.4. If
|
||
the kernel doesn't support the PT_WCOOKIE request, assume
|
||
we're running on a kernel that uses non-randomized cookies. */
|
||
wcookie = 0x3;
|
||
}
|
||
}
|
||
#endif /* PT_WCOOKIE */
|
||
|
||
if (len > sizeof (unsigned long) - offset)
|
||
len = sizeof (unsigned long) - offset;
|
||
|
||
memcpy (readbuf, buf + offset, len);
|
||
*xfered_len = (ULONGEST) len;
|
||
return TARGET_XFER_OK;
|
||
}
|
||
|
||
|
||
void _initialize_sparc_nat ();
|
||
void
|
||
_initialize_sparc_nat ()
|
||
{
|
||
/* Default to using SunOS 4 register sets. */
|
||
if (sparc_gregmap == NULL)
|
||
sparc_gregmap = &sparc32_sunos4_gregmap;
|
||
if (sparc_fpregmap == NULL)
|
||
sparc_fpregmap = &sparc32_sunos4_fpregmap;
|
||
if (sparc_supply_gregset == NULL)
|
||
sparc_supply_gregset = sparc32_supply_gregset;
|
||
if (sparc_collect_gregset == NULL)
|
||
sparc_collect_gregset = sparc32_collect_gregset;
|
||
if (sparc_supply_fpregset == NULL)
|
||
sparc_supply_fpregset = sparc32_supply_fpregset;
|
||
if (sparc_collect_fpregset == NULL)
|
||
sparc_collect_fpregset = sparc32_collect_fpregset;
|
||
if (sparc_gregset_supplies_p == NULL)
|
||
sparc_gregset_supplies_p = sparc32_gregset_supplies_p;
|
||
if (sparc_fpregset_supplies_p == NULL)
|
||
sparc_fpregset_supplies_p = sparc32_fpregset_supplies_p;
|
||
}
|