blob: 5eef0afbd422f3de8642c5d5e5ecadf956092c16 [file] [log] [blame]
/* Target-dependent code for Fuchsia, architecture independent.
Copyright (C) 2009-2016 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/>. */
/* TODO: This is taken from linux-tdep.c.
Converting it to fuchsia is a work-in-progress. */
#include "defs.h"
#include "gdbtypes.h"
#include "fuchsia-tdep.h"
#include "auxv.h"
#include "target.h"
#include "gdbthread.h"
#include "gdbcore.h"
#include "regcache.h"
#include "regset.h"
#include "elf/common.h"
#include "elf-bfd.h" /* for elfcore_write_* */
#include "inferior.h"
#include "cli/cli-utils.h"
#include "arch-utils.h"
#include "gdb_obstack.h"
#include "observer.h"
#include "objfiles.h"
#include "infcall.h"
#include "gdbcmd.h"
#include "gdb_regex.h"
#include "solib.h"
#include "solib-svr4.h"
#include "solist.h"
#include "common/enum-flags.h"
#include <ctype.h>
static struct target_so_ops fuchsia_so_ops;
/* Signal numbers of the Fuchsia kernel.
TODO: Work-in-progress. */
enum
{
FUCHSIA_SIGHUP = 1,
FUCHSIA_SIGINT = 2,
FUCHSIA_SIGQUIT = 3,
FUCHSIA_SIGILL = 4,
FUCHSIA_SIGTRAP = 5,
FUCHSIA_SIGABRT = 6,
FUCHSIA_SIGIOT = 6,
FUCHSIA_SIGBUS = 7,
FUCHSIA_SIGFPE = 8,
FUCHSIA_SIGKILL = 9,
FUCHSIA_SIGUSR1 = 10,
FUCHSIA_SIGSEGV = 11,
FUCHSIA_SIGUSR2 = 12,
FUCHSIA_SIGPIPE = 13,
FUCHSIA_SIGALRM = 14,
FUCHSIA_SIGTERM = 15,
FUCHSIA_SIGSTKFLT = 16,
FUCHSIA_SIGCHLD = 17,
FUCHSIA_SIGCONT = 18,
FUCHSIA_SIGSTOP = 19,
FUCHSIA_SIGTSTP = 20,
FUCHSIA_SIGTTIN = 21,
FUCHSIA_SIGTTOU = 22,
FUCHSIA_SIGURG = 23,
FUCHSIA_SIGXCPU = 24,
FUCHSIA_SIGXFSZ = 25,
FUCHSIA_SIGVTALRM = 26,
FUCHSIA_SIGPROF = 27,
FUCHSIA_SIGWINCH = 28,
FUCHSIA_SIGIO = 29,
FUCHSIA_SIGPOLL = FUCHSIA_SIGIO,
FUCHSIA_SIGPWR = 30,
FUCHSIA_SIGSYS = 31,
FUCHSIA_SIGUNUSED = 31,
FUCHSIA_SIGRTMIN = 32,
FUCHSIA_SIGRTMAX = 64,
};
static struct gdbarch_data *fuchsia_gdbarch_data_handle;
struct fuchsia_gdbarch_data
{
struct type *siginfo_type;
};
static void *
init_fuchsia_gdbarch_data (struct gdbarch *gdbarch)
{
return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct fuchsia_gdbarch_data);
}
static struct fuchsia_gdbarch_data *
get_fuchsia_gdbarch_data (struct gdbarch *gdbarch)
{
return ((struct fuchsia_gdbarch_data *)
gdbarch_data (gdbarch, fuchsia_gdbarch_data_handle));
}
/* Per-inferior data key. */
static const struct inferior_data *fuchsia_inferior_data;
/* Fuchsia-specific cached data. This is used by GDB for caching
purposes for each inferior. This helps reduce the overhead of
transfering data from a remote target to the local host. */
struct fuchsia_info
{
// True if the exec displacement of this inferior has been calculated.
bool exec_displacement_known;
/* Cache of the inferior's vsyscall/vDSO mapping range. Only valid
if VSYSCALL_RANGE_P is positive. This is cached because getting
at this info requires an auxv lookup (which is itself cached),
and looking through the inferior's mappings (which change
throughout execution and therefore cannot be cached). */
struct mem_range vsyscall_range;
/* Zero if we haven't tried looking up the vsyscall's range before
yet. Positive if we tried looking it up, and found it. Negative
if we tried looking it up but failed. */
int vsyscall_range_p;
};
/* Frees whatever allocated space there is to be freed and sets INF's
fuchsia cache data pointer to NULL. */
static void
invalidate_fuchsia_cache_inf (struct inferior *inf)
{
struct fuchsia_info *info;
info = (struct fuchsia_info *) inferior_data (inf, fuchsia_inferior_data);
if (info != NULL)
{
xfree (info);
set_inferior_data (inf, fuchsia_inferior_data, NULL);
}
}
/* Handles the cleanup of the fuchsia cache for inferior INF. ARG is
ignored. Callback for the inferior_appeared and inferior_exit
events. */
static void
fuchsia_inferior_data_cleanup (struct inferior *inf, void *arg)
{
invalidate_fuchsia_cache_inf (inf);
}
/* Fetch the fuchsia cache info for INF. This function always returns a
valid INFO pointer. */
static struct fuchsia_info *
get_fuchsia_inferior_data (void)
{
struct fuchsia_info *info;
struct inferior *inf = current_inferior ();
info = (struct fuchsia_info *) inferior_data (inf, fuchsia_inferior_data);
if (info == NULL)
{
info = XCNEW (struct fuchsia_info);
set_inferior_data (inf, fuchsia_inferior_data, info);
}
return info;
}
/* siginfo is still a work-in-progress for fuchsia. */
static struct type *
fuchsia_get_siginfo_type (struct gdbarch *gdbarch)
{
struct fuchsia_gdbarch_data *fuchsia_gdbarch_data;
struct type *int_type;
struct type *siginfo_type;
fuchsia_gdbarch_data = get_fuchsia_gdbarch_data (gdbarch);
if (fuchsia_gdbarch_data->siginfo_type != NULL)
return fuchsia_gdbarch_data->siginfo_type;
int_type = arch_integer_type (gdbarch, gdbarch_int_bit (gdbarch),
0, "int");
/* struct siginfo */
siginfo_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
TYPE_NAME (siginfo_type) = xstrdup ("siginfo");
append_composite_type_field (siginfo_type, "si_signo", int_type);
append_composite_type_field (siginfo_type, "si_errno", int_type);
append_composite_type_field (siginfo_type, "si_code", int_type);
fuchsia_gdbarch_data->siginfo_type = siginfo_type;
return siginfo_type;
}
/* Implementation of `gdbarch_gdb_signal_from_target', as defined in
gdbarch.h. This function is not static because it is exported to
other -tdep files. */
enum gdb_signal
fuchsia_gdb_signal_from_target (struct gdbarch *gdbarch, int signal)
{
switch (signal)
{
case 0:
return GDB_SIGNAL_0;
case FUCHSIA_SIGHUP:
return GDB_SIGNAL_HUP;
case FUCHSIA_SIGINT:
return GDB_SIGNAL_INT;
case FUCHSIA_SIGQUIT:
return GDB_SIGNAL_QUIT;
case FUCHSIA_SIGILL:
return GDB_SIGNAL_ILL;
case FUCHSIA_SIGTRAP:
return GDB_SIGNAL_TRAP;
case FUCHSIA_SIGABRT:
return GDB_SIGNAL_ABRT;
case FUCHSIA_SIGBUS:
return GDB_SIGNAL_BUS;
case FUCHSIA_SIGFPE:
return GDB_SIGNAL_FPE;
case FUCHSIA_SIGKILL:
return GDB_SIGNAL_KILL;
case FUCHSIA_SIGUSR1:
return GDB_SIGNAL_USR1;
case FUCHSIA_SIGSEGV:
return GDB_SIGNAL_SEGV;
case FUCHSIA_SIGUSR2:
return GDB_SIGNAL_USR2;
case FUCHSIA_SIGPIPE:
return GDB_SIGNAL_PIPE;
case FUCHSIA_SIGALRM:
return GDB_SIGNAL_ALRM;
case FUCHSIA_SIGTERM:
return GDB_SIGNAL_TERM;
case FUCHSIA_SIGCHLD:
return GDB_SIGNAL_CHLD;
case FUCHSIA_SIGCONT:
return GDB_SIGNAL_CONT;
case FUCHSIA_SIGSTOP:
return GDB_SIGNAL_STOP;
case FUCHSIA_SIGTSTP:
return GDB_SIGNAL_TSTP;
case FUCHSIA_SIGTTIN:
return GDB_SIGNAL_TTIN;
case FUCHSIA_SIGTTOU:
return GDB_SIGNAL_TTOU;
case FUCHSIA_SIGURG:
return GDB_SIGNAL_URG;
case FUCHSIA_SIGXCPU:
return GDB_SIGNAL_XCPU;
case FUCHSIA_SIGXFSZ:
return GDB_SIGNAL_XFSZ;
case FUCHSIA_SIGVTALRM:
return GDB_SIGNAL_VTALRM;
case FUCHSIA_SIGPROF:
return GDB_SIGNAL_PROF;
case FUCHSIA_SIGWINCH:
return GDB_SIGNAL_WINCH;
/* No way to differentiate between SIGIO and SIGPOLL.
Therefore, we just handle the first one. */
case FUCHSIA_SIGIO:
return GDB_SIGNAL_IO;
case FUCHSIA_SIGPWR:
return GDB_SIGNAL_PWR;
case FUCHSIA_SIGSYS:
return GDB_SIGNAL_SYS;
/* SIGRTMIN and SIGRTMAX are not continuous in <gdb/signals.def>,
therefore we have to handle them here. */
case FUCHSIA_SIGRTMIN:
return GDB_SIGNAL_REALTIME_32;
case FUCHSIA_SIGRTMAX:
return GDB_SIGNAL_REALTIME_64;
}
if (signal >= FUCHSIA_SIGRTMIN + 1 && signal <= FUCHSIA_SIGRTMAX - 1)
{
int offset = signal - FUCHSIA_SIGRTMIN + 1;
return (enum gdb_signal) ((int) GDB_SIGNAL_REALTIME_33 + offset);
}
return GDB_SIGNAL_UNKNOWN;
}
/* Implementation of `gdbarch_gdb_signal_to_target', as defined in
gdbarch.h. This function is not static because it is exported to
other -tdep files. */
int
fuchsia_gdb_signal_to_target (struct gdbarch *gdbarch,
enum gdb_signal signal)
{
switch (signal)
{
case GDB_SIGNAL_0:
return 0;
case GDB_SIGNAL_HUP:
return FUCHSIA_SIGHUP;
case GDB_SIGNAL_INT:
return FUCHSIA_SIGINT;
case GDB_SIGNAL_QUIT:
return FUCHSIA_SIGQUIT;
case GDB_SIGNAL_ILL:
return FUCHSIA_SIGILL;
case GDB_SIGNAL_TRAP:
return FUCHSIA_SIGTRAP;
case GDB_SIGNAL_ABRT:
return FUCHSIA_SIGABRT;
case GDB_SIGNAL_FPE:
return FUCHSIA_SIGFPE;
case GDB_SIGNAL_KILL:
return FUCHSIA_SIGKILL;
case GDB_SIGNAL_BUS:
return FUCHSIA_SIGBUS;
case GDB_SIGNAL_SEGV:
return FUCHSIA_SIGSEGV;
case GDB_SIGNAL_SYS:
return FUCHSIA_SIGSYS;
case GDB_SIGNAL_PIPE:
return FUCHSIA_SIGPIPE;
case GDB_SIGNAL_ALRM:
return FUCHSIA_SIGALRM;
case GDB_SIGNAL_TERM:
return FUCHSIA_SIGTERM;
case GDB_SIGNAL_URG:
return FUCHSIA_SIGURG;
case GDB_SIGNAL_STOP:
return FUCHSIA_SIGSTOP;
case GDB_SIGNAL_TSTP:
return FUCHSIA_SIGTSTP;
case GDB_SIGNAL_CONT:
return FUCHSIA_SIGCONT;
case GDB_SIGNAL_CHLD:
return FUCHSIA_SIGCHLD;
case GDB_SIGNAL_TTIN:
return FUCHSIA_SIGTTIN;
case GDB_SIGNAL_TTOU:
return FUCHSIA_SIGTTOU;
case GDB_SIGNAL_IO:
return FUCHSIA_SIGIO;
case GDB_SIGNAL_XCPU:
return FUCHSIA_SIGXCPU;
case GDB_SIGNAL_XFSZ:
return FUCHSIA_SIGXFSZ;
case GDB_SIGNAL_VTALRM:
return FUCHSIA_SIGVTALRM;
case GDB_SIGNAL_PROF:
return FUCHSIA_SIGPROF;
case GDB_SIGNAL_WINCH:
return FUCHSIA_SIGWINCH;
case GDB_SIGNAL_USR1:
return FUCHSIA_SIGUSR1;
case GDB_SIGNAL_USR2:
return FUCHSIA_SIGUSR2;
case GDB_SIGNAL_PWR:
return FUCHSIA_SIGPWR;
case GDB_SIGNAL_POLL:
return FUCHSIA_SIGPOLL;
/* GDB_SIGNAL_REALTIME_32 is not continuous in <gdb/signals.def>,
therefore we have to handle it here. */
case GDB_SIGNAL_REALTIME_32:
return FUCHSIA_SIGRTMIN;
/* Same comment applies to _64. */
case GDB_SIGNAL_REALTIME_64:
return FUCHSIA_SIGRTMAX;
}
/* GDB_SIGNAL_REALTIME_33 to _64 are continuous. */
if (signal >= GDB_SIGNAL_REALTIME_33
&& signal <= GDB_SIGNAL_REALTIME_63)
{
int offset = signal - GDB_SIGNAL_REALTIME_33;
return FUCHSIA_SIGRTMIN + 1 + offset;
}
return -1;
}
/* Helper for fuchsia_vsyscall_range that does the real work of finding
the vsyscall's address range. */
static int
fuchsia_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
{
return 0; // TODO
}
/* Implementation of the "vsyscall_range" gdbarch hook. Handles
caching, and defers the real work to fuchsia_vsyscall_range_raw. */
static int
fuchsia_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
{
struct fuchsia_info *info = get_fuchsia_inferior_data ();
if (info->vsyscall_range_p == 0)
{
if (fuchsia_vsyscall_range_raw (gdbarch, &info->vsyscall_range))
info->vsyscall_range_p = 1;
else
info->vsyscall_range_p = -1;
}
if (info->vsyscall_range_p < 0)
return 0;
*range = info->vsyscall_range;
return 1;
}
/* Symbols for fuchsia_infcall_mmap's ARG_FLAGS; their Fuchsia MAP_* system
definitions would be dependent on compilation host. */
#define GDB_MMAP_MAP_PRIVATE 0x02 /* Changes are private. */
#define GDB_MMAP_MAP_ANONYMOUS 0x20 /* Don't use a file. */
/* See gdbarch.sh 'infcall_mmap'. */
static CORE_ADDR
fuchsia_infcall_mmap (CORE_ADDR size, unsigned prot)
{
struct objfile *objf;
/* Do there still exist any Fuchsia systems without "mmap64"?
"mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32. */
struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
struct value *addr_val;
struct gdbarch *gdbarch = get_objfile_arch (objf);
CORE_ADDR retval;
enum
{
ARG_ADDR, ARG_LENGTH, ARG_PROT, ARG_FLAGS, ARG_FD, ARG_OFFSET, ARG_LAST
};
struct value *arg[ARG_LAST];
arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
0);
/* Assuming sizeof (unsigned long) == sizeof (size_t). */
arg[ARG_LENGTH] = value_from_ulongest
(builtin_type (gdbarch)->builtin_unsigned_long, size);
gdb_assert ((prot & ~(GDB_MMAP_PROT_READ | GDB_MMAP_PROT_WRITE
| GDB_MMAP_PROT_EXEC))
== 0);
arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int, prot);
arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
GDB_MMAP_MAP_PRIVATE
| GDB_MMAP_MAP_ANONYMOUS);
arg[ARG_FD] = value_from_longest (builtin_type (gdbarch)->builtin_int, -1);
arg[ARG_OFFSET] = value_from_longest (builtin_type (gdbarch)->builtin_int64,
0);
addr_val = call_function_by_hand (mmap_val, ARG_LAST, arg);
retval = value_as_address (addr_val);
if (retval == (CORE_ADDR) -1)
error (_("Failed inferior mmap call for %s bytes, errno is changed."),
pulongest (size));
return retval;
}
/* See gdbarch.sh 'infcall_munmap'. */
static void
fuchsia_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
{
struct objfile *objf;
struct value *munmap_val = find_function_in_inferior ("munmap", &objf);
struct value *retval_val;
struct gdbarch *gdbarch = get_objfile_arch (objf);
LONGEST retval;
enum
{
ARG_ADDR, ARG_LENGTH, ARG_LAST
};
struct value *arg[ARG_LAST];
arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
addr);
/* Assuming sizeof (unsigned long) == sizeof (size_t). */
arg[ARG_LENGTH] = value_from_ulongest
(builtin_type (gdbarch)->builtin_unsigned_long, size);
retval_val = call_function_by_hand (munmap_val, ARG_LAST, arg);
retval = value_as_long (retval_val);
if (retval != 0)
warning (_("Failed inferior munmap call at %s for %s bytes, "
"errno is changed."),
hex_string (addr), pulongest (size));
}
/* See fuchsia-tdep.h. */
CORE_ADDR
fuchsia_displaced_step_location (struct gdbarch *gdbarch)
{
CORE_ADDR addr;
int bp_len;
// TODO: We're assuming this is called after the first
// dynamic linker breakpoint has been hit (where we know the
// executable has been loaded). Could try to use the interpreter's
// entry point here.
/* Determine entry point from target auxiliary vector. This avoids
the need for symbols. */
if (target_auxv_search (&current_target, AT_ENTRY, &addr) <= 0)
throw_error (NOT_SUPPORTED_ERROR,
_("Cannot find AT_ENTRY auxiliary vector entry."));
/* Make certain that the address points at real code, and not a
function descriptor. */
addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
&current_target);
/* Inferior calls also use the entry point as a breakpoint location.
We don't want displaced stepping to interfere with those
breakpoints, so leave space. */
gdbarch_breakpoint_from_pc (gdbarch, &addr, &bp_len);
addr += bp_len * 2;
return addr;
}
/* For Fuchsia we can't relocate the main executable until after we get
to the shlib event breakpoint: The main executable is not loaded until
the dynamic linker loads it. */
static void
fuchsia_handle_solib_event (void)
{
struct fuchsia_info *info = get_fuchsia_inferior_data ();
if (!info->exec_displacement_known)
{
// Presumably this is the first time we've hit the dynamic linker bkpt.
// It is now that the main executable has been loaded. To fetch the
// necessary info (e.g., AT_ENTRY) we need to flush the auxv cache so
// gdb will refetch it.
invalidate_auxv_cache_inf (current_inferior ());
svr4_relocate_main_executable ();
info->exec_displacement_known = true;
}
/* If fuchsia starts using probes, call svr4_handle_solib_event here. */
}
/* To be called from the various GDB_OSABI_FUCHSIA handlers for the
various Fuchsia architectures and machine types. */
void
fuchsia_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
set_gdbarch_gdb_signal_from_target (gdbarch,
fuchsia_gdb_signal_from_target);
set_gdbarch_gdb_signal_to_target (gdbarch,
fuchsia_gdb_signal_to_target);
set_gdbarch_vsyscall_range (gdbarch, fuchsia_vsyscall_range);
set_gdbarch_infcall_mmap (gdbarch, fuchsia_infcall_mmap);
set_gdbarch_infcall_munmap (gdbarch, fuchsia_infcall_munmap);
set_gdbarch_get_siginfo_type (gdbarch, fuchsia_get_siginfo_type);
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
/* Fuchsia uses SVR4-style shared libraries. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
/* Fuchsia uses its own dynamic linker. */
set_gdbarch_skip_solib_resolver (gdbarch, fuchsia_skip_solib_resolver);
/* Fuchsia uses SVR4-style shared libraries.
Note: This also calls set_solib_ops. */
set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_lp64_fetch_link_map_offsets);
/* But we need to use our own so_ops vtable to handle exec displacement
computation.
Initialize this lazily, to avoid an initialization order
dependency on solib-svr4.c's _initialize routine. */
if (fuchsia_so_ops.handle_event == NULL)
{
fuchsia_so_ops = svr4_so_ops;
fuchsia_so_ops.handle_event = fuchsia_handle_solib_event;
}
set_solib_ops (gdbarch, &fuchsia_so_ops);
}
/* Calling functions in shared libraries.
See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c.
This function:
1) decides whether a PLT has sent us into the linker to resolve
a function reference, and
2) if so, tells us where to set a temporary breakpoint that will
trigger when the dynamic linker is done. */
CORE_ADDR
fuchsia_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc)
{
// TODO: Update for fuchsia/musl.
/* The GNU dynamic linker is part of the GNU C library, and is used
by all GNU systems (GNU/Hurd, GNU/Linux). An unresolved PLT
entry points to "_dl_runtime_resolve", which calls "fixup" to
patch the PLT, and then passes control to the function.
We look for the symbol `_dl_runtime_resolve', and find `fixup' in
the same objfile. If we are at the entry point of `fixup', then
we set a breakpoint at the return address (at the top of the
stack), and continue.
It's kind of gross to do all these checks every time we're
called, since they don't change once the executable has gotten
started. But this is only a temporary hack --- upcoming versions
of GNU/Linux will provide a portable, efficient interface for
debugging programs that use shared libraries. */
struct bound_minimal_symbol resolver
= lookup_minimal_symbol_and_objfile ("_dl_runtime_resolve");
if (resolver.minsym)
{
/* The dynamic linker began using this name in early 2005. */
struct bound_minimal_symbol fixup
= lookup_minimal_symbol ("_dl_fixup", NULL, resolver.objfile);
/* This is the name used in older versions. */
if (! fixup.minsym)
fixup = lookup_minimal_symbol ("fixup", NULL, resolver.objfile);
if (fixup.minsym && BMSYMBOL_VALUE_ADDRESS (fixup) == pc)
return frame_unwind_caller_pc (get_current_frame ());
}
return 0;
}
/* Recognize Fuchsia object files. */
enum gdb_osabi
fuchsia_osabi_sniffer (bfd *abfd)
{
/* TODO: Until we have something like a fuchsia .note.ABI-tag, assume the
absence of .note.ABI-tag means fuchsia. */
if (bfd_get_section_by_name (abfd, ".note.ABI-tag") == NULL)
return GDB_OSABI_FUCHSIA;
return GDB_OSABI_UNKNOWN;
}
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_fuchsia_tdep;
void
_initialize_fuchsia_tdep (void)
{
fuchsia_gdbarch_data_handle =
gdbarch_data_register_post_init (init_fuchsia_gdbarch_data);
/* Set a cache per-inferior. */
fuchsia_inferior_data
= register_inferior_data_with_cleanup (NULL, fuchsia_inferior_data_cleanup);
/* Observers used to invalidate the cache when needed. */
observer_attach_inferior_exit (invalidate_fuchsia_cache_inf);
observer_attach_inferior_appeared (invalidate_fuchsia_cache_inf);
}