mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
76727919ce
This converts observers from using a special source-generating script to be plain C++. This version of the patch takes advantage of C++11 by using std::function and variadic templates; incorporates Pedro's patches; and renames the header file to "observable.h" (this change eliminates the need for a clean rebuild). Note that Pedro's patches used a template lambda in tui-hooks.c, but this failed to compile on some buildbot instances (presumably due to differing C++ versions); I replaced this with an ordinary template function. Regression tested on the buildbot. gdb/ChangeLog 2018-03-19 Pedro Alves <palves@redhat.com> Tom Tromey <tom@tromey.com> * unittests/observable-selftests.c: New file. * common/observable.h: New file. * observable.h: New file. * ada-lang.c, ada-tasks.c, agent.c, aix-thread.c, annotate.c, arm-tdep.c, auto-load.c, auxv.c, break-catch-syscall.c, breakpoint.c, bsd-uthread.c, cli/cli-interp.c, cli/cli-setshow.c, corefile.c, dummy-frame.c, event-loop.c, event-top.c, exec.c, extension.c, frame.c, gdbarch.c, guile/scm-breakpoint.c, infcall.c, infcmd.c, inferior.c, inflow.c, infrun.c, jit.c, linux-tdep.c, linux-thread-db.c, m68klinux-tdep.c, mi/mi-cmd-break.c, mi/mi-interp.c, mi/mi-main.c, objfiles.c, ppc-linux-nat.c, ppc-linux-tdep.c, printcmd.c, procfs.c, python/py-breakpoint.c, python/py-finishbreakpoint.c, python/py-inferior.c, python/py-unwind.c, ravenscar-thread.c, record-btrace.c, record-full.c, record.c, regcache.c, remote.c, riscv-tdep.c, sol-thread.c, solib-aix.c, solib-spu.c, solib.c, spu-multiarch.c, spu-tdep.c, stack.c, symfile-mem.c, symfile.c, symtab.c, thread.c, top.c, tracepoint.c, tui/tui-hooks.c, tui/tui-interp.c, valops.c: Update all users. * tui/tui-hooks.c (tui_bp_created_observer) (tui_bp_deleted_observer, tui_bp_modified_observer) (tui_inferior_exit_observer, tui_before_prompt_observer) (tui_normal_stop_observer, tui_register_changed_observer): Remove. (tui_observers_token): New global. (attach_or_detach, tui_attach_detach_observers): New functions. (tui_install_hooks, tui_remove_hooks): Use tui_attach_detach_observers. * record-btrace.c (record_btrace_thread_observer): Remove. (record_btrace_thread_observer_token): New global. * observer.sh: Remove. * observer.c: Rename to observable.c. * observable.c (namespace gdb_observers): Define new objects. (observer_debug): Move into gdb_observers namespace. (struct observer, struct observer_list, xalloc_observer_list_node) (xfree_observer_list_node, generic_observer_attach) (generic_observer_detach, generic_observer_notify): Remove. (_initialize_observer): Update. Don't include observer.inc. * Makefile.in (generated_files): Remove observer.h, observer.inc. (clean mostlyclean): Likewise. (observer.h, observer.inc): Remove targets. (SUBDIR_UNITTESTS_SRCS): Add observable-selftests.c. (COMMON_SFILES): Use observable.c, not observer.c. * .gitignore: Remove observer.h. gdb/doc/ChangeLog 2018-03-19 Tom Tromey <tom@tromey.com> * observer.texi: Remove. gdb/testsuite/ChangeLog 2018-03-19 Tom Tromey <tom@tromey.com> * gdb.gdb/observer.exp: Remove.
418 lines
13 KiB
C
418 lines
13 KiB
C
/* Cell SPU GNU/Linux multi-architecture debugging support.
|
|
Copyright (C) 2009-2018 Free Software Foundation, Inc.
|
|
|
|
Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
|
|
|
|
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 "gdbcore.h"
|
|
#include "gdbcmd.h"
|
|
#include "arch-utils.h"
|
|
#include "observable.h"
|
|
#include "inferior.h"
|
|
#include "regcache.h"
|
|
#include "symfile.h"
|
|
#include "objfiles.h"
|
|
#include "solib.h"
|
|
#include "solist.h"
|
|
|
|
#include "ppc-tdep.h"
|
|
#include "ppc-linux-tdep.h"
|
|
#include "spu-tdep.h"
|
|
|
|
/* This module's target vector. */
|
|
static struct target_ops spu_ops;
|
|
|
|
/* Number of SPE objects loaded into the current inferior. */
|
|
static int spu_nr_solib;
|
|
|
|
/* Stand-alone SPE executable? */
|
|
#define spu_standalone_p() \
|
|
(symfile_objfile && symfile_objfile->obfd \
|
|
&& bfd_get_arch (symfile_objfile->obfd) == bfd_arch_spu)
|
|
|
|
/* PPU side system calls. */
|
|
#define INSTR_SC 0x44000002
|
|
#define NR_spu_run 0x0116
|
|
|
|
/* If the PPU thread is currently stopped on a spu_run system call,
|
|
return to FD and ADDR the file handle and NPC parameter address
|
|
used with the system call. Return non-zero if successful. */
|
|
static int
|
|
parse_spufs_run (ptid_t ptid, int *fd, CORE_ADDR *addr)
|
|
{
|
|
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
|
|
struct gdbarch_tdep *tdep;
|
|
struct regcache *regcache;
|
|
gdb_byte buf[4];
|
|
ULONGEST regval;
|
|
|
|
/* If we're not on PPU, there's nothing to detect. */
|
|
if (gdbarch_bfd_arch_info (target_gdbarch ())->arch != bfd_arch_powerpc)
|
|
return 0;
|
|
|
|
/* If we're called too early (e.g. after fork), we cannot
|
|
access the inferior yet. */
|
|
if (find_inferior_ptid (ptid) == NULL)
|
|
return 0;
|
|
|
|
/* Get PPU-side registers. */
|
|
regcache = get_thread_arch_regcache (ptid, target_gdbarch ());
|
|
tdep = gdbarch_tdep (target_gdbarch ());
|
|
|
|
/* Fetch instruction preceding current NIP. */
|
|
{
|
|
scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
|
|
inferior_ptid = ptid;
|
|
regval = target_read_memory (regcache_read_pc (regcache) - 4, buf, 4);
|
|
}
|
|
if (regval != 0)
|
|
return 0;
|
|
/* It should be a "sc" instruction. */
|
|
if (extract_unsigned_integer (buf, 4, byte_order) != INSTR_SC)
|
|
return 0;
|
|
/* System call number should be NR_spu_run. */
|
|
regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum, ®val);
|
|
if (regval != NR_spu_run)
|
|
return 0;
|
|
|
|
/* Register 3 contains fd, register 4 the NPC param pointer. */
|
|
regcache_cooked_read_unsigned (regcache, PPC_ORIG_R3_REGNUM, ®val);
|
|
*fd = (int) regval;
|
|
regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 4, ®val);
|
|
*addr = (CORE_ADDR) regval;
|
|
return 1;
|
|
}
|
|
|
|
/* Find gdbarch for SPU context SPUFS_FD. */
|
|
static struct gdbarch *
|
|
spu_gdbarch (int spufs_fd)
|
|
{
|
|
struct gdbarch_info info;
|
|
gdbarch_info_init (&info);
|
|
info.bfd_arch_info = bfd_lookup_arch (bfd_arch_spu, bfd_mach_spu);
|
|
info.byte_order = BFD_ENDIAN_BIG;
|
|
info.osabi = GDB_OSABI_LINUX;
|
|
info.id = &spufs_fd;
|
|
return gdbarch_find_by_info (info);
|
|
}
|
|
|
|
/* Override the to_thread_architecture routine. */
|
|
static struct gdbarch *
|
|
spu_thread_architecture (struct target_ops *ops, ptid_t ptid)
|
|
{
|
|
int spufs_fd;
|
|
CORE_ADDR spufs_addr;
|
|
|
|
if (parse_spufs_run (ptid, &spufs_fd, &spufs_addr))
|
|
return spu_gdbarch (spufs_fd);
|
|
|
|
target_ops *beneath = find_target_beneath (ops);
|
|
return beneath->to_thread_architecture (beneath, ptid);
|
|
}
|
|
|
|
/* Override the to_region_ok_for_hw_watchpoint routine. */
|
|
static int
|
|
spu_region_ok_for_hw_watchpoint (struct target_ops *self,
|
|
CORE_ADDR addr, int len)
|
|
{
|
|
struct target_ops *ops_beneath = find_target_beneath (self);
|
|
|
|
/* We cannot watch SPU local store. */
|
|
if (SPUADDR_SPU (addr) != -1)
|
|
return 0;
|
|
|
|
return ops_beneath->to_region_ok_for_hw_watchpoint (ops_beneath, addr, len);
|
|
}
|
|
|
|
/* Override the to_fetch_registers routine. */
|
|
static void
|
|
spu_fetch_registers (struct target_ops *ops,
|
|
struct regcache *regcache, int regno)
|
|
{
|
|
struct gdbarch *gdbarch = regcache->arch ();
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
struct target_ops *ops_beneath = find_target_beneath (ops);
|
|
int spufs_fd;
|
|
CORE_ADDR spufs_addr;
|
|
|
|
/* Since we use functions that rely on inferior_ptid, we need to set and
|
|
restore it. */
|
|
scoped_restore save_ptid
|
|
= make_scoped_restore (&inferior_ptid, regcache_get_ptid (regcache));
|
|
|
|
/* This version applies only if we're currently in spu_run. */
|
|
if (gdbarch_bfd_arch_info (gdbarch)->arch != bfd_arch_spu)
|
|
{
|
|
ops_beneath->to_fetch_registers (ops_beneath, regcache, regno);
|
|
return;
|
|
}
|
|
|
|
/* We must be stopped on a spu_run system call. */
|
|
if (!parse_spufs_run (inferior_ptid, &spufs_fd, &spufs_addr))
|
|
return;
|
|
|
|
/* The ID register holds the spufs file handle. */
|
|
if (regno == -1 || regno == SPU_ID_REGNUM)
|
|
{
|
|
gdb_byte buf[4];
|
|
store_unsigned_integer (buf, 4, byte_order, spufs_fd);
|
|
regcache_raw_supply (regcache, SPU_ID_REGNUM, buf);
|
|
}
|
|
|
|
/* The NPC register is found in PPC memory at SPUFS_ADDR. */
|
|
if (regno == -1 || regno == SPU_PC_REGNUM)
|
|
{
|
|
gdb_byte buf[4];
|
|
|
|
if (target_read (ops_beneath, TARGET_OBJECT_MEMORY, NULL,
|
|
buf, spufs_addr, sizeof buf) == sizeof buf)
|
|
regcache_raw_supply (regcache, SPU_PC_REGNUM, buf);
|
|
}
|
|
|
|
/* The GPRs are found in the "regs" spufs file. */
|
|
if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
|
|
{
|
|
gdb_byte buf[16 * SPU_NUM_GPRS];
|
|
char annex[32];
|
|
int i;
|
|
|
|
xsnprintf (annex, sizeof annex, "%d/regs", spufs_fd);
|
|
if (target_read (ops_beneath, TARGET_OBJECT_SPU, annex,
|
|
buf, 0, sizeof buf) == sizeof buf)
|
|
for (i = 0; i < SPU_NUM_GPRS; i++)
|
|
regcache_raw_supply (regcache, i, buf + i*16);
|
|
}
|
|
}
|
|
|
|
/* Override the to_store_registers routine. */
|
|
static void
|
|
spu_store_registers (struct target_ops *ops,
|
|
struct regcache *regcache, int regno)
|
|
{
|
|
struct gdbarch *gdbarch = regcache->arch ();
|
|
struct target_ops *ops_beneath = find_target_beneath (ops);
|
|
int spufs_fd;
|
|
CORE_ADDR spufs_addr;
|
|
|
|
/* Since we use functions that rely on inferior_ptid, we need to set and
|
|
restore it. */
|
|
scoped_restore save_ptid
|
|
= make_scoped_restore (&inferior_ptid, regcache_get_ptid (regcache));
|
|
|
|
/* This version applies only if we're currently in spu_run. */
|
|
if (gdbarch_bfd_arch_info (gdbarch)->arch != bfd_arch_spu)
|
|
{
|
|
ops_beneath->to_store_registers (ops_beneath, regcache, regno);
|
|
return;
|
|
}
|
|
|
|
/* We must be stopped on a spu_run system call. */
|
|
if (!parse_spufs_run (inferior_ptid, &spufs_fd, &spufs_addr))
|
|
return;
|
|
|
|
/* The NPC register is found in PPC memory at SPUFS_ADDR. */
|
|
if (regno == -1 || regno == SPU_PC_REGNUM)
|
|
{
|
|
gdb_byte buf[4];
|
|
regcache_raw_collect (regcache, SPU_PC_REGNUM, buf);
|
|
|
|
target_write (ops_beneath, TARGET_OBJECT_MEMORY, NULL,
|
|
buf, spufs_addr, sizeof buf);
|
|
}
|
|
|
|
/* The GPRs are found in the "regs" spufs file. */
|
|
if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
|
|
{
|
|
gdb_byte buf[16 * SPU_NUM_GPRS];
|
|
char annex[32];
|
|
int i;
|
|
|
|
for (i = 0; i < SPU_NUM_GPRS; i++)
|
|
regcache_raw_collect (regcache, i, buf + i*16);
|
|
|
|
xsnprintf (annex, sizeof annex, "%d/regs", spufs_fd);
|
|
target_write (ops_beneath, TARGET_OBJECT_SPU, annex,
|
|
buf, 0, sizeof buf);
|
|
}
|
|
}
|
|
|
|
/* Override the to_xfer_partial routine. */
|
|
static enum target_xfer_status
|
|
spu_xfer_partial (struct target_ops *ops, enum target_object object,
|
|
const char *annex, gdb_byte *readbuf,
|
|
const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
|
|
ULONGEST *xfered_len)
|
|
{
|
|
struct target_ops *ops_beneath = find_target_beneath (ops);
|
|
|
|
/* Use the "mem" spufs file to access SPU local store. */
|
|
if (object == TARGET_OBJECT_MEMORY)
|
|
{
|
|
int fd = SPUADDR_SPU (offset);
|
|
CORE_ADDR addr = SPUADDR_ADDR (offset);
|
|
char mem_annex[32], lslr_annex[32];
|
|
gdb_byte buf[32];
|
|
ULONGEST lslr;
|
|
enum target_xfer_status ret;
|
|
|
|
if (fd >= 0)
|
|
{
|
|
xsnprintf (mem_annex, sizeof mem_annex, "%d/mem", fd);
|
|
ret = ops_beneath->to_xfer_partial (ops_beneath, TARGET_OBJECT_SPU,
|
|
mem_annex, readbuf, writebuf,
|
|
addr, len, xfered_len);
|
|
if (ret == TARGET_XFER_OK)
|
|
return ret;
|
|
|
|
/* SPU local store access wraps the address around at the
|
|
local store limit. We emulate this here. To avoid needing
|
|
an extra access to retrieve the LSLR, we only do that after
|
|
trying the original address first, and getting end-of-file. */
|
|
xsnprintf (lslr_annex, sizeof lslr_annex, "%d/lslr", fd);
|
|
memset (buf, 0, sizeof buf);
|
|
if (ops_beneath->to_xfer_partial (ops_beneath, TARGET_OBJECT_SPU,
|
|
lslr_annex, buf, NULL,
|
|
0, sizeof buf, xfered_len)
|
|
!= TARGET_XFER_OK)
|
|
return ret;
|
|
|
|
lslr = strtoulst ((char *) buf, NULL, 16);
|
|
return ops_beneath->to_xfer_partial (ops_beneath, TARGET_OBJECT_SPU,
|
|
mem_annex, readbuf, writebuf,
|
|
addr & lslr, len, xfered_len);
|
|
}
|
|
}
|
|
|
|
return ops_beneath->to_xfer_partial (ops_beneath, object, annex,
|
|
readbuf, writebuf, offset, len, xfered_len);
|
|
}
|
|
|
|
/* Override the to_search_memory routine. */
|
|
static int
|
|
spu_search_memory (struct target_ops* ops,
|
|
CORE_ADDR start_addr, ULONGEST search_space_len,
|
|
const gdb_byte *pattern, ULONGEST pattern_len,
|
|
CORE_ADDR *found_addrp)
|
|
{
|
|
struct target_ops *ops_beneath = find_target_beneath (ops);
|
|
|
|
/* For SPU local store, always fall back to the simple method. */
|
|
if (SPUADDR_SPU (start_addr) >= 0)
|
|
return simple_search_memory (ops,
|
|
start_addr, search_space_len,
|
|
pattern, pattern_len, found_addrp);
|
|
|
|
return ops_beneath->to_search_memory (ops_beneath,
|
|
start_addr, search_space_len,
|
|
pattern, pattern_len, found_addrp);
|
|
}
|
|
|
|
|
|
/* Push and pop the SPU multi-architecture support target. */
|
|
|
|
static void
|
|
spu_multiarch_activate (void)
|
|
{
|
|
/* If GDB was configured without SPU architecture support,
|
|
we cannot install SPU multi-architecture support either. */
|
|
if (spu_gdbarch (-1) == NULL)
|
|
return;
|
|
|
|
push_target (&spu_ops);
|
|
|
|
/* Make sure the thread architecture is re-evaluated. */
|
|
registers_changed ();
|
|
}
|
|
|
|
static void
|
|
spu_multiarch_deactivate (void)
|
|
{
|
|
unpush_target (&spu_ops);
|
|
|
|
/* Make sure the thread architecture is re-evaluated. */
|
|
registers_changed ();
|
|
}
|
|
|
|
static void
|
|
spu_multiarch_inferior_created (struct target_ops *ops, int from_tty)
|
|
{
|
|
if (spu_standalone_p ())
|
|
spu_multiarch_activate ();
|
|
}
|
|
|
|
static void
|
|
spu_multiarch_solib_loaded (struct so_list *so)
|
|
{
|
|
if (!spu_standalone_p ())
|
|
if (so->abfd && bfd_get_arch (so->abfd) == bfd_arch_spu)
|
|
if (spu_nr_solib++ == 0)
|
|
spu_multiarch_activate ();
|
|
}
|
|
|
|
static void
|
|
spu_multiarch_solib_unloaded (struct so_list *so)
|
|
{
|
|
if (!spu_standalone_p ())
|
|
if (so->abfd && bfd_get_arch (so->abfd) == bfd_arch_spu)
|
|
if (--spu_nr_solib == 0)
|
|
spu_multiarch_deactivate ();
|
|
}
|
|
|
|
static void
|
|
spu_mourn_inferior (struct target_ops *ops)
|
|
{
|
|
struct target_ops *ops_beneath = find_target_beneath (ops);
|
|
|
|
ops_beneath->to_mourn_inferior (ops_beneath);
|
|
spu_multiarch_deactivate ();
|
|
}
|
|
|
|
|
|
/* Initialize the SPU multi-architecture support target. */
|
|
|
|
static void
|
|
init_spu_ops (void)
|
|
{
|
|
spu_ops.to_shortname = "spu";
|
|
spu_ops.to_longname = "SPU multi-architecture support.";
|
|
spu_ops.to_doc = "SPU multi-architecture support.";
|
|
spu_ops.to_mourn_inferior = spu_mourn_inferior;
|
|
spu_ops.to_fetch_registers = spu_fetch_registers;
|
|
spu_ops.to_store_registers = spu_store_registers;
|
|
spu_ops.to_xfer_partial = spu_xfer_partial;
|
|
spu_ops.to_search_memory = spu_search_memory;
|
|
spu_ops.to_region_ok_for_hw_watchpoint = spu_region_ok_for_hw_watchpoint;
|
|
spu_ops.to_thread_architecture = spu_thread_architecture;
|
|
spu_ops.to_stratum = arch_stratum;
|
|
spu_ops.to_magic = OPS_MAGIC;
|
|
}
|
|
|
|
void
|
|
_initialize_spu_multiarch (void)
|
|
{
|
|
/* Install ourselves on the target stack. */
|
|
init_spu_ops ();
|
|
complete_target_initialization (&spu_ops);
|
|
|
|
/* Install observers to watch for SPU objects. */
|
|
gdb::observers::inferior_created.attach (spu_multiarch_inferior_created);
|
|
gdb::observers::solib_loaded.attach (spu_multiarch_solib_loaded);
|
|
gdb::observers::solib_unloaded.attach (spu_multiarch_solib_unloaded);
|
|
}
|
|
|