X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Falpha-linux-tdep.c;h=aa0342164f2e0ea2bc13e24215e7ad81f385cb85;hb=441af85bd9c68dbc0c2a1dbe23bf07c6cb3c3f5d;hp=26f4db433f1738c77212fa296a92c69fa29db842;hpb=05816f706ec4cb2e789828fa8edb8ea30593933b;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/alpha-linux-tdep.c b/gdb/alpha-linux-tdep.c index 26f4db433f..aa0342164f 100644 --- a/gdb/alpha-linux-tdep.c +++ b/gdb/alpha-linux-tdep.c @@ -1,11 +1,11 @@ /* Target-dependent code for GNU/Linux on Alpha. - Copyright 2002 Free Software Foundation, Inc. + Copyright (C) 2002-2020 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, @@ -14,103 +14,379 @@ 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + along with this program. If not, see . */ #include "defs.h" #include "frame.h" -#include "gdbcore.h" -#include "value.h" - +#include "osabi.h" +#include "solib-svr4.h" +#include "symtab.h" +#include "regset.h" +#include "regcache.h" +#include "linux-tdep.h" #include "alpha-tdep.h" +#include "gdbarch.h" + +/* This enum represents the signals' numbers on the Alpha + 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 + { + /* SIGABRT is the same as in the generic implementation, but is + defined here because SIGIOT depends on it. */ + ALPHA_LINUX_SIGABRT = 6, + ALPHA_LINUX_SIGEMT = 7, + ALPHA_LINUX_SIGBUS = 10, + ALPHA_LINUX_SIGSYS = 12, + ALPHA_LINUX_SIGURG = 16, + ALPHA_LINUX_SIGSTOP = 17, + ALPHA_LINUX_SIGTSTP = 18, + ALPHA_LINUX_SIGCONT = 19, + ALPHA_LINUX_SIGCHLD = 20, + ALPHA_LINUX_SIGIO = 23, + ALPHA_LINUX_SIGINFO = 29, + ALPHA_LINUX_SIGUSR1 = 30, + ALPHA_LINUX_SIGUSR2 = 31, + ALPHA_LINUX_SIGPOLL = ALPHA_LINUX_SIGIO, + ALPHA_LINUX_SIGPWR = ALPHA_LINUX_SIGINFO, + ALPHA_LINUX_SIGIOT = ALPHA_LINUX_SIGABRT, + }; -/* Under GNU/Linux, signal handler invocations can be identified by the - designated code sequence that is used to return from a signal +/* Under GNU/Linux, signal handler invocations can be identified by + the designated code sequence that is used to return from a signal handler. In particular, the return address of a signal handler - points to the following sequence (the first instruction is quadword - aligned): - - bis $30,$30,$16 - addq $31,0x67,$0 - call_pal callsys - - Each instruction has a unique encoding, so we simply attempt to - match the instruction the pc is pointing to with any of the above - instructions. If there is a hit, we know the offset to the start - of the designated sequence and can then check whether we really are - executing in a designated sequence. If not, -1 is returned, - otherwise the offset from the start of the desingated sequence is - returned. - - There is a slight chance of false hits: code could jump into the - middle of the designated sequence, in which case there is no - guarantee that we are in the middle of a sigreturn syscall. Don't - think this will be a problem in praxis, though. */ -LONGEST -alpha_linux_sigtramp_offset (CORE_ADDR pc) -{ - unsigned int i[3], w; - long off; + points to a sequence that copies $sp to $16, loads $0 with the + appropriate syscall number, and finally enters the kernel. - if (read_memory_nobpt (pc, (char *) &w, 4) != 0) - return -1; + This is somewhat complicated in that: + (1) the expansion of the "mov" assembler macro has changed over + time, from "bis src,src,dst" to "bis zero,src,dst", + (2) the kernel has changed from using "addq" to "lda" to load the + syscall number, + (3) there is a "normal" sigreturn and an "rt" sigreturn which + has a different stack layout. */ - off = -1; - switch (w) +static long +alpha_linux_sigtramp_offset_1 (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + switch (alpha_read_insn (gdbarch, pc)) { - case 0x47de0410: - off = 0; - break; /* bis $30,$30,$16 */ - case 0x43ecf400: - off = 4; - break; /* addq $31,0x67,$0 */ - case 0x00000083: - off = 8; - break; /* call_pal callsys */ + case 0x47de0410: /* bis $30,$30,$16 */ + case 0x47fe0410: /* bis $31,$30,$16 */ + return 0; + + case 0x43ecf400: /* addq $31,103,$0 */ + case 0x201f0067: /* lda $0,103($31) */ + case 0x201f015f: /* lda $0,351($31) */ + return 4; + + case 0x00000083: /* call_pal callsys */ + return 8; + default: return -1; } +} + +static LONGEST +alpha_linux_sigtramp_offset (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + long i, off; + + if (pc & 3) + return -1; + + /* Guess where we might be in the sequence. */ + off = alpha_linux_sigtramp_offset_1 (gdbarch, pc); + if (off < 0) + return -1; + + /* Verify that the other two insns of the sequence are as we expect. */ pc -= off; - if (pc & 0x7) + for (i = 0; i < 12; i += 4) { - /* designated sequence is not quadword aligned */ - return -1; + if (i == off) + continue; + if (alpha_linux_sigtramp_offset_1 (gdbarch, pc + i) != i) + return -1; } - if (read_memory_nobpt (pc, (char *) i, sizeof (i)) != 0) - return -1; - if (i[0] == 0x47de0410 && i[1] == 0x43ecf400 && i[2] == 0x00000083) - return off; - - return -1; + return off; } static int -alpha_linux_pc_in_sigtramp (CORE_ADDR pc, char *func_name) +alpha_linux_pc_in_sigtramp (struct gdbarch *gdbarch, + CORE_ADDR pc, const char *func_name) { - return (alpha_linux_sigtramp_offset (pc) >= 0); + return alpha_linux_sigtramp_offset (gdbarch, pc) >= 0; } static CORE_ADDR -alpha_linux_sigcontext_addr (struct frame_info *frame) +alpha_linux_sigcontext_addr (struct frame_info *this_frame) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + CORE_ADDR pc; + ULONGEST sp; + long off; + + pc = get_frame_pc (this_frame); + sp = get_frame_register_unsigned (this_frame, ALPHA_SP_REGNUM); + + off = alpha_linux_sigtramp_offset (gdbarch, pc); + gdb_assert (off >= 0); + + /* __NR_rt_sigreturn has a couple of structures on the stack. This is: + + struct rt_sigframe { + struct siginfo info; + struct ucontext uc; + }; + + offsetof (struct rt_sigframe, uc.uc_mcontext); */ + + if (alpha_read_insn (gdbarch, pc - off + 4) == 0x201f015f) + return sp + 176; + + /* __NR_sigreturn has the sigcontext structure at the top of the stack. */ + return sp; +} + +/* Supply register REGNUM from the buffer specified by GREGS and LEN + in the general-purpose register set REGSET to register cache + REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ + +static void +alpha_linux_supply_gregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *gregs, size_t len) +{ + const gdb_byte *regs = (const gdb_byte *) gregs; + + gdb_assert (len >= 32 * 8); + alpha_supply_int_regs (regcache, regnum, regs, regs + 31 * 8, + len >= 33 * 8 ? regs + 32 * 8 : NULL); +} + +/* Collect register REGNUM from the register cache REGCACHE and store + it in the buffer specified by GREGS and LEN as described by the + general-purpose register set REGSET. If REGNUM is -1, do this for + all registers in REGSET. */ + +static void +alpha_linux_collect_gregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *gregs, size_t len) +{ + gdb_byte *regs = (gdb_byte *) gregs; + + gdb_assert (len >= 32 * 8); + alpha_fill_int_regs (regcache, regnum, regs, regs + 31 * 8, + len >= 33 * 8 ? regs + 32 * 8 : NULL); +} + +/* Supply register REGNUM from the buffer specified by FPREGS and LEN + in the floating-point register set REGSET to register cache + REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ + +static void +alpha_linux_supply_fpregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *fpregs, size_t len) +{ + const gdb_byte *regs = (const gdb_byte *) fpregs; + + gdb_assert (len >= 32 * 8); + alpha_supply_fp_regs (regcache, regnum, regs, regs + 31 * 8); +} + +/* Collect register REGNUM from the register cache REGCACHE and store + it in the buffer specified by FPREGS and LEN as described by the + general-purpose register set REGSET. If REGNUM is -1, do this for + all registers in REGSET. */ + +static void +alpha_linux_collect_fpregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *fpregs, size_t len) +{ + gdb_byte *regs = (gdb_byte *) fpregs; + + gdb_assert (len >= 32 * 8); + alpha_fill_fp_regs (regcache, regnum, regs, regs + 31 * 8); +} + +static const struct regset alpha_linux_gregset = +{ + NULL, + alpha_linux_supply_gregset, alpha_linux_collect_gregset +}; + +static const struct regset alpha_linux_fpregset = +{ + NULL, + alpha_linux_supply_fpregset, alpha_linux_collect_fpregset +}; + +/* Iterate over core file register note sections. */ + +static void +alpha_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, + iterate_over_regset_sections_cb *cb, + void *cb_data, + const struct regcache *regcache) { - return (frame->frame - 0x298); /* sizeof(struct sigcontext) */ + cb (".reg", 32 * 8, 32 * 8, &alpha_linux_gregset, NULL, cb_data); + cb (".reg2", 32 * 8, 32 * 8, &alpha_linux_fpregset, NULL, cb_data); +} + +/* Implementation of `gdbarch_gdb_signal_from_target', as defined in + gdbarch.h. */ + +static enum gdb_signal +alpha_linux_gdb_signal_from_target (struct gdbarch *gdbarch, + int signal) +{ + switch (signal) + { + case ALPHA_LINUX_SIGEMT: + return GDB_SIGNAL_EMT; + + case ALPHA_LINUX_SIGBUS: + return GDB_SIGNAL_BUS; + + case ALPHA_LINUX_SIGSYS: + return GDB_SIGNAL_SYS; + + case ALPHA_LINUX_SIGURG: + return GDB_SIGNAL_URG; + + case ALPHA_LINUX_SIGSTOP: + return GDB_SIGNAL_STOP; + + case ALPHA_LINUX_SIGTSTP: + return GDB_SIGNAL_TSTP; + + case ALPHA_LINUX_SIGCONT: + return GDB_SIGNAL_CONT; + + case ALPHA_LINUX_SIGCHLD: + return GDB_SIGNAL_CHLD; + + /* No way to differentiate between SIGIO and SIGPOLL. + Therefore, we just handle the first one. */ + case ALPHA_LINUX_SIGIO: + return GDB_SIGNAL_IO; + + /* No way to differentiate between SIGINFO and SIGPWR. + Therefore, we just handle the first one. */ + case ALPHA_LINUX_SIGINFO: + return GDB_SIGNAL_INFO; + + case ALPHA_LINUX_SIGUSR1: + return GDB_SIGNAL_USR1; + + case ALPHA_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 +alpha_linux_gdb_signal_to_target (struct gdbarch *gdbarch, + enum gdb_signal signal) +{ + switch (signal) + { + case GDB_SIGNAL_EMT: + return ALPHA_LINUX_SIGEMT; + + case GDB_SIGNAL_BUS: + return ALPHA_LINUX_SIGBUS; + + case GDB_SIGNAL_SYS: + return ALPHA_LINUX_SIGSYS; + + case GDB_SIGNAL_URG: + return ALPHA_LINUX_SIGURG; + + case GDB_SIGNAL_STOP: + return ALPHA_LINUX_SIGSTOP; + + case GDB_SIGNAL_TSTP: + return ALPHA_LINUX_SIGTSTP; + + case GDB_SIGNAL_CONT: + return ALPHA_LINUX_SIGCONT; + + case GDB_SIGNAL_CHLD: + return ALPHA_LINUX_SIGCHLD; + + case GDB_SIGNAL_IO: + return ALPHA_LINUX_SIGIO; + + case GDB_SIGNAL_INFO: + return ALPHA_LINUX_SIGINFO; + + case GDB_SIGNAL_USR1: + return ALPHA_LINUX_SIGUSR1; + + case GDB_SIGNAL_USR2: + return ALPHA_LINUX_SIGUSR2; + + case GDB_SIGNAL_POLL: + return ALPHA_LINUX_SIGPOLL; + + case GDB_SIGNAL_PWR: + return ALPHA_LINUX_SIGPWR; + } + + return linux_gdb_signal_to_target (gdbarch, signal); } static void -alpha_linux_init_abi (struct gdbarch_info info, - struct gdbarch *gdbarch) +alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { - struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + struct gdbarch_tdep *tdep; + + linux_init_abi (info, gdbarch); + + /* Hook into the DWARF CFI frame unwinder. */ + alpha_dwarf2_init_abi (info, gdbarch); - set_gdbarch_pc_in_sigtramp (gdbarch, alpha_linux_pc_in_sigtramp); + /* Hook into the MDEBUG frame unwinder. */ + alpha_mdebug_init_abi (info, gdbarch); + tdep = gdbarch_tdep (gdbarch); tdep->dynamic_sigtramp_offset = alpha_linux_sigtramp_offset; tdep->sigcontext_addr = alpha_linux_sigcontext_addr; - + tdep->pc_in_sigtramp = alpha_linux_pc_in_sigtramp; tdep->jb_pc = 2; tdep->jb_elt_size = 8; + + set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); + + set_solib_svr4_fetch_link_map_offsets + (gdbarch, svr4_lp64_fetch_link_map_offsets); + + /* Enable TLS support. */ + set_gdbarch_fetch_tls_load_module_address (gdbarch, + svr4_fetch_objfile_link_map); + + set_gdbarch_iterate_over_regset_sections + (gdbarch, alpha_linux_iterate_over_regset_sections); + + set_gdbarch_gdb_signal_from_target (gdbarch, + alpha_linux_gdb_signal_from_target); + set_gdbarch_gdb_signal_to_target (gdbarch, + alpha_linux_gdb_signal_to_target); } void