X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fsparc-linux-tdep.c;h=be3ff8d81418e7420701d420e8563ff6c401867f;hb=f81e7e2db6d1aaf47561e54356aee12b585533c2;hp=300db9aa7ddf234e4f19dd2b14e11e8f236073bb;hpb=197e01b6dcd118b70ed3621b62b2ff3fa929d50f;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/sparc-linux-tdep.c b/gdb/sparc-linux-tdep.c index 300db9aa7d..be3ff8d814 100644 --- a/gdb/sparc-linux-tdep.c +++ b/gdb/sparc-linux-tdep.c @@ -1,12 +1,12 @@ /* Target-dependent code for GNU/Linux SPARC. - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2003-2019 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 2 of the License, or + 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, @@ -15,14 +15,14 @@ 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see . */ #include "defs.h" -#include "floatformat.h" +#include "dwarf2-frame.h" #include "frame.h" #include "frame-unwind.h" +#include "gdbtypes.h" +#include "regset.h" #include "gdbarch.h" #include "gdbcore.h" #include "osabi.h" @@ -31,13 +31,18 @@ #include "symtab.h" #include "trad-frame.h" #include "tramp-frame.h" +#include "xml-syscall.h" +#include "linux-tdep.h" + +/* The syscall's XML filename for sparc 32-bit. */ +#define XML_SYSCALL_FILENAME_SPARC32 "syscalls/sparc-linux.xml" #include "sparc-tdep.h" /* Signal trampoline support. */ static void sparc32_linux_sigframe_init (const struct tramp_frame *self, - struct frame_info *next_frame, + struct frame_info *this_frame, struct trad_frame_cache *this_cache, CORE_ADDR func); @@ -63,9 +68,9 @@ static const struct tramp_frame sparc32_linux_sigframe = SIGTRAMP_FRAME, 4, { - { 0x821020d8, -1 }, /* mov __NR_sugreturn, %g1 */ - { 0x91d02010, -1 }, /* ta 0x10 */ - { TRAMP_SENTINEL_INSN, -1 } + { 0x821020d8, ULONGEST_MAX }, /* mov __NR_sigreturn, %g1 */ + { 0x91d02010, ULONGEST_MAX }, /* ta 0x10 */ + { TRAMP_SENTINEL_INSN, ULONGEST_MAX } }, sparc32_linux_sigframe_init }; @@ -78,23 +83,48 @@ static const struct tramp_frame sparc32_linux_rt_sigframe = SIGTRAMP_FRAME, 4, { - { 0x82102065, -1 }, /* mov __NR_rt_sigreturn, %g1 */ - { 0x91d02010, -1 }, /* ta 0x10 */ - { TRAMP_SENTINEL_INSN, -1 } + { 0x82102065, ULONGEST_MAX }, /* mov __NR_rt_sigreturn, %g1 */ + { 0x91d02010, ULONGEST_MAX }, /* ta 0x10 */ + { TRAMP_SENTINEL_INSN, ULONGEST_MAX } }, sparc32_linux_sigframe_init }; +/* This enum represents the signals' numbers on the SPARC + architecture. It just contains the signal definitions which are + different from the generic implementation. + + It is derived from the file , + from the Linux kernel tree. */ + +enum + { + SPARC_LINUX_SIGEMT = 7, + SPARC_LINUX_SIGBUS = 10, + SPARC_LINUX_SIGSYS = 12, + SPARC_LINUX_SIGURG = 16, + SPARC_LINUX_SIGSTOP = 17, + SPARC_LINUX_SIGTSTP = 18, + SPARC_LINUX_SIGCONT = 19, + SPARC_LINUX_SIGCHLD = 20, + SPARC_LINUX_SIGIO = 23, + SPARC_LINUX_SIGPOLL = SPARC_LINUX_SIGIO, + SPARC_LINUX_SIGLOST = 29, + SPARC_LINUX_SIGPWR = SPARC_LINUX_SIGLOST, + SPARC_LINUX_SIGUSR1 = 30, + SPARC_LINUX_SIGUSR2 = 31, + }; + static void sparc32_linux_sigframe_init (const struct tramp_frame *self, - struct frame_info *next_frame, + struct frame_info *this_frame, struct trad_frame_cache *this_cache, CORE_ADDR func) { - CORE_ADDR base, addr; + CORE_ADDR base, addr, sp_addr; int regnum; - base = frame_unwind_register_unsigned (next_frame, SPARC_O1_REGNUM); + base = get_frame_register_unsigned (this_frame, SPARC_O1_REGNUM); if (self == &sparc32_linux_rt_sigframe) base += 128; @@ -107,13 +137,16 @@ sparc32_linux_sigframe_init (const struct tramp_frame *self, /* Since %g0 is always zero, keep the identity encoding. */ addr = base + 20; + sp_addr = base + 16 + ((SPARC_SP_REGNUM - SPARC_G0_REGNUM) * 4); for (regnum = SPARC_G1_REGNUM; regnum <= SPARC_O7_REGNUM; regnum++) { trad_frame_set_reg_addr (this_cache, regnum, addr); addr += 4; } - base = addr = frame_unwind_register_unsigned (next_frame, SPARC_SP_REGNUM); + base = get_frame_register_unsigned (this_frame, SPARC_SP_REGNUM); + addr = get_frame_memory_unsigned (this_frame, sp_addr, 4); + for (regnum = SPARC_L0_REGNUM; regnum <= SPARC_I7_REGNUM; regnum++) { trad_frame_set_reg_addr (this_cache, regnum, addr); @@ -121,13 +154,282 @@ sparc32_linux_sigframe_init (const struct tramp_frame *self, } trad_frame_set_id (this_cache, frame_id_build (base, func)); } + +/* Return the address of a system call's alternative return + address. */ + +static CORE_ADDR +sparc32_linux_step_trap (struct frame_info *frame, unsigned long insn) +{ + if (insn == 0x91d02010) + { + ULONGEST sc_num = get_frame_register_unsigned (frame, SPARC_G1_REGNUM); + + /* __NR_rt_sigreturn is 101 and __NR_sigreturn is 216. */ + if (sc_num == 101 || sc_num == 216) + { + struct gdbarch *gdbarch = get_frame_arch (frame); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + + ULONGEST sp, pc_offset; + + sp = get_frame_register_unsigned (frame, SPARC_SP_REGNUM); + + /* The kernel puts the sigreturn registers on the stack, + and this is where the signal unwinding state is take from + when returning from a signal. + + For __NR_sigreturn, this register area sits 96 bytes from + the base of the stack. The saved PC sits 4 bytes into the + sigreturn register save area. + + For __NR_rt_sigreturn a siginfo_t, which is 128 bytes, sits + right before the sigreturn register save area. */ + + pc_offset = 96 + 4; + if (sc_num == 101) + pc_offset += 128; + + return read_memory_unsigned_integer (sp + pc_offset, 4, byte_order); + } + } + + return 0; +} + + +const struct sparc_gregmap sparc32_linux_core_gregmap = +{ + 32 * 4, /* %psr */ + 33 * 4, /* %pc */ + 34 * 4, /* %npc */ + 35 * 4, /* %y */ + -1, /* %wim */ + -1, /* %tbr */ + 1 * 4, /* %g1 */ + 16 * 4, /* %l0 */ + 4, /* y size */ +}; + + +static void +sparc32_linux_supply_core_gregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *gregs, size_t len) +{ + sparc32_supply_gregset (&sparc32_linux_core_gregmap, + regcache, regnum, gregs); +} + +static void +sparc32_linux_collect_core_gregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *gregs, size_t len) +{ + sparc32_collect_gregset (&sparc32_linux_core_gregmap, + regcache, regnum, gregs); +} + +static void +sparc32_linux_supply_core_fpregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *fpregs, size_t len) +{ + sparc32_supply_fpregset (&sparc32_bsd_fpregmap, regcache, regnum, fpregs); +} + +static void +sparc32_linux_collect_core_fpregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *fpregs, size_t len) +{ + sparc32_collect_fpregset (&sparc32_bsd_fpregmap, regcache, regnum, fpregs); +} + +/* Set the program counter for process PTID to PC. */ + +#define PSR_SYSCALL 0x00004000 + +static void +sparc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ()); + ULONGEST psr; + + regcache_cooked_write_unsigned (regcache, tdep->pc_regnum, pc); + regcache_cooked_write_unsigned (regcache, tdep->npc_regnum, pc + 4); + + /* Clear the "in syscall" bit to prevent the kernel from + messing with the PCs we just installed, if we happen to be + within an interrupted system call that the kernel wants to + restart. + + Note that after we return from the dummy call, the PSR et al. + registers will be automatically restored, and the kernel + continues to restart the system call at this point. */ + regcache_cooked_read_unsigned (regcache, SPARC32_PSR_REGNUM, &psr); + psr &= ~PSR_SYSCALL; + regcache_cooked_write_unsigned (regcache, SPARC32_PSR_REGNUM, psr); +} + +static LONGEST +sparc32_linux_get_syscall_number (struct gdbarch *gdbarch, + thread_info *thread) +{ + struct regcache *regcache = get_thread_regcache (thread); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + /* The content of a register. */ + gdb_byte buf[4]; + /* The result. */ + LONGEST ret; + + /* Getting the system call number from the register. + When dealing with the sparc architecture, this information + is stored at the %g1 register. */ + regcache->cooked_read (SPARC_G1_REGNUM, buf); + + ret = extract_signed_integer (buf, 4, byte_order); + + return ret; +} + +/* Implementation of `gdbarch_gdb_signal_from_target', as defined in + gdbarch.h. */ + +static enum gdb_signal +sparc32_linux_gdb_signal_from_target (struct gdbarch *gdbarch, + int signal) +{ + switch (signal) + { + case SPARC_LINUX_SIGEMT: + return GDB_SIGNAL_EMT; + + case SPARC_LINUX_SIGBUS: + return GDB_SIGNAL_BUS; + + case SPARC_LINUX_SIGSYS: + return GDB_SIGNAL_SYS; + + case SPARC_LINUX_SIGURG: + return GDB_SIGNAL_URG; + + case SPARC_LINUX_SIGSTOP: + return GDB_SIGNAL_STOP; + + case SPARC_LINUX_SIGTSTP: + return GDB_SIGNAL_TSTP; + + case SPARC_LINUX_SIGCONT: + return GDB_SIGNAL_CONT; + + case SPARC_LINUX_SIGCHLD: + return GDB_SIGNAL_CHLD; + + /* No way to differentiate between SIGIO and SIGPOLL. + Therefore, we just handle the first one. */ + case SPARC_LINUX_SIGIO: + return GDB_SIGNAL_IO; + + /* No way to differentiate between SIGLOST and SIGPWR. + Therefore, we just handle the first one. */ + case SPARC_LINUX_SIGLOST: + return GDB_SIGNAL_LOST; + + case SPARC_LINUX_SIGUSR1: + return GDB_SIGNAL_USR1; + + case SPARC_LINUX_SIGUSR2: + return GDB_SIGNAL_USR2; + } + + return linux_gdb_signal_from_target (gdbarch, signal); +} + +/* Implementation of `gdbarch_gdb_signal_to_target', as defined in + gdbarch.h. */ + +static int +sparc32_linux_gdb_signal_to_target (struct gdbarch *gdbarch, + enum gdb_signal signal) +{ + switch (signal) + { + case GDB_SIGNAL_EMT: + return SPARC_LINUX_SIGEMT; + + case GDB_SIGNAL_BUS: + return SPARC_LINUX_SIGBUS; + + case GDB_SIGNAL_SYS: + return SPARC_LINUX_SIGSYS; + + case GDB_SIGNAL_URG: + return SPARC_LINUX_SIGURG; + + case GDB_SIGNAL_STOP: + return SPARC_LINUX_SIGSTOP; + + case GDB_SIGNAL_TSTP: + return SPARC_LINUX_SIGTSTP; + + case GDB_SIGNAL_CONT: + return SPARC_LINUX_SIGCONT; + + case GDB_SIGNAL_CHLD: + return SPARC_LINUX_SIGCHLD; + + case GDB_SIGNAL_IO: + return SPARC_LINUX_SIGIO; + + case GDB_SIGNAL_POLL: + return SPARC_LINUX_SIGPOLL; + + case GDB_SIGNAL_LOST: + return SPARC_LINUX_SIGLOST; + + case GDB_SIGNAL_PWR: + return SPARC_LINUX_SIGPWR; + + case GDB_SIGNAL_USR1: + return SPARC_LINUX_SIGUSR1; + + case GDB_SIGNAL_USR2: + return SPARC_LINUX_SIGUSR2; + } + + return linux_gdb_signal_to_target (gdbarch, signal); +} + +static const struct regset sparc32_linux_gregset = + { + NULL, + sparc32_linux_supply_core_gregset, + sparc32_linux_collect_core_gregset + }; + +static const struct regset sparc32_linux_fpregset = + { + NULL, + sparc32_linux_supply_core_fpregset, + sparc32_linux_collect_core_fpregset + }; + static void sparc32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + linux_init_abi (info, gdbarch); + + tdep->gregset = &sparc32_linux_gregset; + tdep->sizeof_gregset = 152; + + tdep->fpregset = &sparc32_linux_fpregset; + tdep->sizeof_fpregset = 396; + tramp_frame_prepend_unwinder (gdbarch, &sparc32_linux_sigframe); tramp_frame_prepend_unwinder (gdbarch, &sparc32_linux_rt_sigframe); @@ -140,17 +442,28 @@ sparc32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) prologue analysis. */ tdep->plt_entry_size = 12; - /* GNU/Linux doesn't support the 128-bit `long double' from the psABI. */ - set_gdbarch_long_double_bit (gdbarch, 64); - set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_double_big); - /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); -} -/* Provide a prototype to silence -Wmissing-prototypes. */ -extern void _initialize_sparc_linux_tdep (void); + /* Make sure we can single-step over signal return system calls. */ + tdep->step_trap = sparc32_linux_step_trap; + + /* Hook in the DWARF CFI frame unwinder. */ + dwarf2_append_unwinders (gdbarch); + + set_gdbarch_write_pc (gdbarch, sparc_linux_write_pc); + + /* Functions for 'catch syscall'. */ + set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_SPARC32); + set_gdbarch_get_syscall_number (gdbarch, + sparc32_linux_get_syscall_number); + + set_gdbarch_gdb_signal_from_target (gdbarch, + sparc32_linux_gdb_signal_from_target); + set_gdbarch_gdb_signal_to_target (gdbarch, + sparc32_linux_gdb_signal_to_target); +} void _initialize_sparc_linux_tdep (void)