| /* 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 (¤t_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, |
| ¤t_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); |
| } |