- /* Target-dependent code for GNU/Linux AArch64.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
- Contributed by ARM Ltd.
- 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 "gdbarch.h"
- #include "glibc-tdep.h"
- #include "linux-tdep.h"
- #include "aarch64-tdep.h"
- #include "aarch64-linux-tdep.h"
- #include "osabi.h"
- #include "solib-svr4.h"
- #include "symtab.h"
- #include "tramp-frame.h"
- #include "trad-frame.h"
- #include "inferior.h"
- #include "regcache.h"
- #include "regset.h"
- #include "cli/cli-utils.h"
- #include "stap-probe.h"
- #include "parser-defs.h"
- #include "user-regs.h"
- #include <ctype.h>
- /* Signal frame handling.
- +------------+ ^
- | saved lr | |
- +->| saved fp |--+
- | | |
- | | |
- | +------------+
- | | saved lr |
- +--| saved fp |
- ^ | |
- | | |
- | +------------+
- ^ | |
- | | signal |
- | | | SIGTRAMP_FRAME (struct rt_sigframe)
- | | saved regs |
- +--| saved sp |--> interrupted_sp
- | | saved pc |--> interrupted_pc
- | | |
- | +------------+
- | | saved lr |--> default_restorer (movz x8, NR_sys_rt_sigreturn; svc 0)
- +--| saved fp |<- FP
- | | NORMAL_FRAME
- | |<- SP
- +------------+
- On signal delivery, the kernel will create a signal handler stack
- frame and setup the return address in LR to point at restorer stub.
- The signal stack frame is defined by:
- struct rt_sigframe
- {
- siginfo_t info;
- struct ucontext uc;
- };
- typedef struct
- {
- ... 128 bytes
- } siginfo_t;
- The ucontext has the following form:
- struct ucontext
- {
- unsigned long uc_flags;
- struct ucontext *uc_link;
- stack_t uc_stack;
- sigset_t uc_sigmask;
- struct sigcontext uc_mcontext;
- };
- typedef struct sigaltstack
- {
- void *ss_sp;
- int ss_flags;
- size_t ss_size;
- } stack_t;
- struct sigcontext
- {
- unsigned long fault_address;
- unsigned long regs[31];
- unsigned long sp; / * 31 * /
- unsigned long pc; / * 32 * /
- unsigned long pstate; / * 33 * /
- __u8 __reserved[4096]
- };
- The restorer stub will always have the form:
- d28015a8 movz x8, #0xad
- d4000001 svc #0x0
- This is a system call sys_rt_sigreturn.
- We detect signal frames by snooping the return code for the restorer
- instruction sequence.
- The handler then needs to recover the saved register set from
- ucontext.uc_mcontext. */
- /* These magic numbers need to reflect the layout of the kernel
- defined struct rt_sigframe and ucontext. */
- #define AARCH64_SIGCONTEXT_REG_SIZE 8
- #define AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET 128
- #define AARCH64_UCONTEXT_SIGCONTEXT_OFFSET 176
- #define AARCH64_SIGCONTEXT_XO_OFFSET 8
- /* Implement the "init" method of struct tramp_frame. */
- static void
- aarch64_linux_sigframe_init (const struct tramp_frame *self,
- struct frame_info *this_frame,
- struct trad_frame_cache *this_cache,
- CORE_ADDR func)
- {
- struct gdbarch *gdbarch = get_frame_arch (this_frame);
- CORE_ADDR sp = get_frame_register_unsigned (this_frame, AARCH64_SP_REGNUM);
- CORE_ADDR sigcontext_addr =
- sp
- + AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET
- + AARCH64_UCONTEXT_SIGCONTEXT_OFFSET;
- int i;
- for (i = 0; i < 31; i++)
- {
- trad_frame_set_reg_addr (this_cache,
- AARCH64_X0_REGNUM + i,
- sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
- + i * AARCH64_SIGCONTEXT_REG_SIZE);
- }
- trad_frame_set_reg_addr (this_cache, AARCH64_SP_REGNUM,
- sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
- + 31 * AARCH64_SIGCONTEXT_REG_SIZE);
- trad_frame_set_reg_addr (this_cache, AARCH64_PC_REGNUM,
- sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
- + 32 * AARCH64_SIGCONTEXT_REG_SIZE);
- trad_frame_set_id (this_cache, frame_id_build (sp, func));
- }
- static const struct tramp_frame aarch64_linux_rt_sigframe =
- {
- SIGTRAMP_FRAME,
- 4,
- {
- /* movz x8, 0x8b (S=1,o=10,h=0,i=0x8b,r=8)
- Soo1 0010 1hhi iiii iiii iiii iiir rrrr */
- {0xd2801168, -1},
- /* svc 0x0 (o=0, l=1)
- 1101 0100 oooi iiii iiii iiii iii0 00ll */
- {0xd4000001, -1},
- {TRAMP_SENTINEL_INSN, -1}
- },
- aarch64_linux_sigframe_init
- };
- /* Register maps. */
- static const struct regcache_map_entry aarch64_linux_gregmap[] =
- {
- { 31, AARCH64_X0_REGNUM, 8 }, /* x0 ... x30 */
- { 1, AARCH64_SP_REGNUM, 8 },
- { 1, AARCH64_PC_REGNUM, 8 },
- { 1, AARCH64_CPSR_REGNUM, 8 },
- { 0 }
- };
- static const struct regcache_map_entry aarch64_linux_fpregmap[] =
- {
- { 32, AARCH64_V0_REGNUM, 16 }, /* v0 ... v31 */
- { 1, AARCH64_FPSR_REGNUM, 4 },
- { 1, AARCH64_FPCR_REGNUM, 4 },
- { 0 }
- };
- /* Register set definitions. */
- const struct regset aarch64_linux_gregset =
- {
- aarch64_linux_gregmap,
- regcache_supply_regset, regcache_collect_regset
- };
- const struct regset aarch64_linux_fpregset =
- {
- aarch64_linux_fpregmap,
- regcache_supply_regset, regcache_collect_regset
- };
- /* Implement the "regset_from_core_section" gdbarch method. */
- static void
- aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
- iterate_over_regset_sections_cb *cb,
- void *cb_data,
- const struct regcache *regcache)
- {
- cb (".reg", AARCH64_LINUX_SIZEOF_GREGSET, &aarch64_linux_gregset,
- NULL, cb_data);
- cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, &aarch64_linux_fpregset,
- NULL, cb_data);
- }
- /* Implementation of `gdbarch_stap_is_single_operand', as defined in
- gdbarch.h. */
- static int
- aarch64_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
- {
- return (*s == '#' || isdigit (*s) /* Literal number. */
- || *s == '[' /* Register indirection. */
- || isalpha (*s)); /* Register value. */
- }
- /* This routine is used to parse a special token in AArch64's assembly.
- The special tokens parsed by it are:
- - Register displacement (e.g, [fp, #-8])
- It returns one if the special token has been parsed successfully,
- or zero if the current token is not considered special. */
- static int
- aarch64_stap_parse_special_token (struct gdbarch *gdbarch,
- struct stap_parse_info *p)
- {
- if (*p->arg == '[')
- {
- /* Temporary holder for lookahead. */
- const char *tmp = p->arg;
- char *endp;
- /* Used to save the register name. */
- const char *start;
- char *regname;
- int len;
- int got_minus = 0;
- long displacement;
- struct stoken str;
- ++tmp;
- start = tmp;
- /* Register name. */
- while (isalnum (*tmp))
- ++tmp;
- if (*tmp != ',')
- return 0;
- len = tmp - start;
- regname = alloca (len + 2);
- strncpy (regname, start, len);
- regname[len] = '\0';
- if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1)
- error (_("Invalid register name `%s' on expression `%s'."),
- regname, p->saved_arg);
- ++tmp;
- tmp = skip_spaces_const (tmp);
- /* Now we expect a number. It can begin with '#' or simply
- a digit. */
- if (*tmp == '#')
- ++tmp;
- if (*tmp == '-')
- {
- ++tmp;
- got_minus = 1;
- }
- else if (*tmp == '+')
- ++tmp;
- if (!isdigit (*tmp))
- return 0;
- displacement = strtol (tmp, &endp, 10);
- tmp = endp;
- /* Skipping last `]'. */
- if (*tmp++ != ']')
- return 0;
- /* The displacement. */
- write_exp_elt_opcode (&p->pstate, OP_LONG);
- write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long);
- write_exp_elt_longcst (&p->pstate, displacement);
- write_exp_elt_opcode (&p->pstate, OP_LONG);
- if (got_minus)
- write_exp_elt_opcode (&p->pstate, UNOP_NEG);
- /* The register name. */
- write_exp_elt_opcode (&p->pstate, OP_REGISTER);
- str.ptr = regname;
- str.length = len;
- write_exp_string (&p->pstate, str);
- write_exp_elt_opcode (&p->pstate, OP_REGISTER);
- write_exp_elt_opcode (&p->pstate, BINOP_ADD);
- /* Casting to the expected type. */
- write_exp_elt_opcode (&p->pstate, UNOP_CAST);
- write_exp_elt_type (&p->pstate, lookup_pointer_type (p->arg_type));
- write_exp_elt_opcode (&p->pstate, UNOP_CAST);
- write_exp_elt_opcode (&p->pstate, UNOP_IND);
- p->arg = tmp;
- }
- else
- return 0;
- return 1;
- }
- static void
- aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
- {
- static const char *const stap_integer_prefixes[] = { "#", "", NULL };
- static const char *const stap_register_prefixes[] = { "", NULL };
- static const char *const stap_register_indirection_prefixes[] = { "[",
- NULL };
- static const char *const stap_register_indirection_suffixes[] = { "]",
- NULL };
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- tdep->lowest_pc = 0x8000;
- linux_init_abi (info, gdbarch);
- 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);
- /* Shared library handling. */
- set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
- set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
- tramp_frame_prepend_unwinder (gdbarch, &aarch64_linux_rt_sigframe);
- /* Enable longjmp. */
- tdep->jb_pc = 11;
- set_gdbarch_iterate_over_regset_sections
- (gdbarch, aarch64_linux_iterate_over_regset_sections);
- /* SystemTap related. */
- set_gdbarch_stap_integer_prefixes (gdbarch, stap_integer_prefixes);
- set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes);
- set_gdbarch_stap_register_indirection_prefixes (gdbarch,
- stap_register_indirection_prefixes);
- set_gdbarch_stap_register_indirection_suffixes (gdbarch,
- stap_register_indirection_suffixes);
- set_gdbarch_stap_is_single_operand (gdbarch, aarch64_stap_is_single_operand);
- set_gdbarch_stap_parse_special_token (gdbarch,
- aarch64_stap_parse_special_token);
- }
- /* Provide a prototype to silence -Wmissing-prototypes. */
- extern initialize_file_ftype _initialize_aarch64_linux_tdep;
- void
- _initialize_aarch64_linux_tdep (void)
- {
- gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX,
- aarch64_linux_init_abi);
- }