/* GNU/Linux/AArch64 specific low level interface, for the remote server for
GDB.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2018 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GDB.
#include <sys/uio.h>
#include "gdb_proc_service.h"
-
-/* Defined in auto-generated files. */
-void init_registers_aarch64 (void);
-extern const struct target_desc *tdesc_aarch64;
+#include "arch/aarch64.h"
+#include "linux-aarch64-tdesc.h"
#ifdef HAVE_SYS_REG_H
#include <sys/reg.h>
#endif
-#define AARCH64_X_REGS_NUM 31
-#define AARCH64_V_REGS_NUM 32
-#define AARCH64_X0_REGNO 0
-#define AARCH64_SP_REGNO 31
-#define AARCH64_PC_REGNO 32
-#define AARCH64_CPSR_REGNO 33
-#define AARCH64_V0_REGNO 34
-#define AARCH64_FPSR_REGNO (AARCH64_V0_REGNO + AARCH64_V_REGS_NUM)
-#define AARCH64_FPCR_REGNO (AARCH64_V0_REGNO + AARCH64_V_REGS_NUM + 1)
-
-#define AARCH64_NUM_REGS (AARCH64_V0_REGNO + AARCH64_V_REGS_NUM + 2)
-
/* Per-process arch-specific data we want to keep. */
struct arch_process_info
static void
aarch64_fill_gregset (struct regcache *regcache, void *buf)
{
- struct user_pt_regs *regset = buf;
+ struct user_pt_regs *regset = (struct user_pt_regs *) buf;
int i;
for (i = 0; i < AARCH64_X_REGS_NUM; i++)
- collect_register (regcache, AARCH64_X0_REGNO + i, ®set->regs[i]);
- collect_register (regcache, AARCH64_SP_REGNO, ®set->sp);
- collect_register (regcache, AARCH64_PC_REGNO, ®set->pc);
- collect_register (regcache, AARCH64_CPSR_REGNO, ®set->pstate);
+ collect_register (regcache, AARCH64_X0_REGNUM + i, ®set->regs[i]);
+ collect_register (regcache, AARCH64_SP_REGNUM, ®set->sp);
+ collect_register (regcache, AARCH64_PC_REGNUM, ®set->pc);
+ collect_register (regcache, AARCH64_CPSR_REGNUM, ®set->pstate);
}
static void
aarch64_store_gregset (struct regcache *regcache, const void *buf)
{
- const struct user_pt_regs *regset = buf;
+ const struct user_pt_regs *regset = (const struct user_pt_regs *) buf;
int i;
for (i = 0; i < AARCH64_X_REGS_NUM; i++)
- supply_register (regcache, AARCH64_X0_REGNO + i, ®set->regs[i]);
- supply_register (regcache, AARCH64_SP_REGNO, ®set->sp);
- supply_register (regcache, AARCH64_PC_REGNO, ®set->pc);
- supply_register (regcache, AARCH64_CPSR_REGNO, ®set->pstate);
+ supply_register (regcache, AARCH64_X0_REGNUM + i, ®set->regs[i]);
+ supply_register (regcache, AARCH64_SP_REGNUM, ®set->sp);
+ supply_register (regcache, AARCH64_PC_REGNUM, ®set->pc);
+ supply_register (regcache, AARCH64_CPSR_REGNUM, ®set->pstate);
}
static void
aarch64_fill_fpregset (struct regcache *regcache, void *buf)
{
- struct user_fpsimd_state *regset = buf;
+ struct user_fpsimd_state *regset = (struct user_fpsimd_state *) buf;
int i;
for (i = 0; i < AARCH64_V_REGS_NUM; i++)
- collect_register (regcache, AARCH64_V0_REGNO + i, ®set->vregs[i]);
- collect_register (regcache, AARCH64_FPSR_REGNO, ®set->fpsr);
- collect_register (regcache, AARCH64_FPCR_REGNO, ®set->fpcr);
+ collect_register (regcache, AARCH64_V0_REGNUM + i, ®set->vregs[i]);
+ collect_register (regcache, AARCH64_FPSR_REGNUM, ®set->fpsr);
+ collect_register (regcache, AARCH64_FPCR_REGNUM, ®set->fpcr);
}
static void
aarch64_store_fpregset (struct regcache *regcache, const void *buf)
{
- const struct user_fpsimd_state *regset = buf;
+ const struct user_fpsimd_state *regset
+ = (const struct user_fpsimd_state *) buf;
int i;
for (i = 0; i < AARCH64_V_REGS_NUM; i++)
- supply_register (regcache, AARCH64_V0_REGNO + i, ®set->vregs[i]);
- supply_register (regcache, AARCH64_FPSR_REGNO, ®set->fpsr);
- supply_register (regcache, AARCH64_FPCR_REGNO, ®set->fpcr);
+ supply_register (regcache, AARCH64_V0_REGNUM + i, ®set->vregs[i]);
+ supply_register (regcache, AARCH64_FPSR_REGNUM, ®set->fpsr);
+ supply_register (regcache, AARCH64_FPCR_REGNUM, ®set->fpcr);
}
/* Enable miscellaneous debugging output. The name is historical - it
aarch64_get_pc (struct regcache *regcache)
{
if (register_size (regcache->tdesc, 0) == 8)
- {
- unsigned long pc;
-
- collect_register_by_name (regcache, "pc", &pc);
- if (debug_threads)
- debug_printf ("stop pc is %08lx\n", pc);
- return pc;
- }
+ return linux_get_pc_64bit (regcache);
else
- {
- unsigned int pc;
-
- collect_register_by_name (regcache, "pc", &pc);
- if (debug_threads)
- debug_printf ("stop pc is %04x\n", pc);
- return pc;
- }
+ return linux_get_pc_32bit (regcache);
}
/* Implementation of linux_target_ops method "set_pc". */
aarch64_set_pc (struct regcache *regcache, CORE_ADDR pc)
{
if (register_size (regcache->tdesc, 0) == 8)
- {
- unsigned long newpc = pc;
- supply_register_by_name (regcache, "pc", &newpc);
- }
+ linux_set_pc_64bit (regcache, pc);
else
- {
- unsigned int newpc = pc;
- supply_register_by_name (regcache, "pc", &newpc);
- }
+ linux_set_pc_32bit (regcache, pc);
}
#define aarch64_breakpoint_len 4
static int
aarch64_breakpoint_at (CORE_ADDR where)
{
- gdb_byte insn[aarch64_breakpoint_len];
+ if (is_64bit_tdesc ())
+ {
+ gdb_byte insn[aarch64_breakpoint_len];
- (*the_target->read_memory) (where, (unsigned char *) &insn,
- aarch64_breakpoint_len);
- if (memcmp (insn, aarch64_breakpoint, aarch64_breakpoint_len) == 0)
- return 1;
+ (*the_target->read_memory) (where, (unsigned char *) &insn,
+ aarch64_breakpoint_len);
+ if (memcmp (insn, aarch64_breakpoint, aarch64_breakpoint_len) == 0)
+ return 1;
- return 0;
+ return 0;
+ }
+ else
+ return arm_breakpoint_at (where);
}
static void
switch (z_type)
{
case Z_PACKET_SW_BP:
- {
- if (!extended_protocol && is_64bit_tdesc ())
- {
- /* Only enable Z0 packet in non-multi-arch debugging. If
- extended protocol is used, don't enable Z0 packet because
- GDBserver may attach to 32-bit process. */
- return 1;
- }
- else
- {
- /* Disable Z0 packet so that GDBserver doesn't have to handle
- different breakpoint instructions (aarch64, arm, thumb etc)
- in multi-arch debugging. */
- return 0;
- }
- }
case Z_PACKET_HW_BP:
case Z_PACKET_WRITE_WP:
case Z_PACKET_READ_WP:
ret = -1;
}
else
- ret =
- aarch64_handle_breakpoint (targ_type, addr, len, 1 /* is_insert */,
- state);
+ {
+ if (len == 3)
+ {
+ /* LEN is 3 means the breakpoint is set on a 32-bit thumb
+ instruction. Set it to 2 to correctly encode length bit
+ mask in hardware/watchpoint control register. */
+ len = 2;
+ }
+ ret = aarch64_handle_breakpoint (targ_type, addr, len,
+ 1 /* is_insert */, state);
+ }
if (show_debug_regs)
aarch64_show_debug_reg_state (state, "insert_point", addr, len,
aarch64_handle_watchpoint (targ_type, addr, len, 0 /* is_insert */,
state);
else
- ret =
- aarch64_handle_breakpoint (targ_type, addr, len, 0 /* is_insert */,
- state);
+ {
+ if (len == 3)
+ {
+ /* LEN is 3 means the breakpoint is set on a 32-bit thumb
+ instruction. Set it to 2 to correctly encode length bit
+ mask in hardware/watchpoint control register. */
+ len = 2;
+ }
+ ret = aarch64_handle_breakpoint (targ_type, addr, len,
+ 0 /* is_insert */, state);
+ }
if (show_debug_regs)
aarch64_show_debug_reg_state (state, "remove_point", addr, len,
/* Fetch the thread-local storage pointer for libthread_db. */
ps_err_e
-ps_get_thread_area (const struct ps_prochandle *ph,
+ps_get_thread_area (struct ps_prochandle *ph,
lwpid_t lwpid, int idx, void **base)
{
return aarch64_ps_get_thread_area (ph, lwpid, idx, base,
/* Implementation of linux_target_ops method "siginfo_fixup". */
static int
-aarch64_linux_siginfo_fixup (siginfo_t *native, void *inf, int direction)
+aarch64_linux_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction)
{
/* Is the inferior 32-bit? If so, then fixup the siginfo object. */
if (!is_64bit_tdesc ())
return 0;
}
-/* Implementation of linux_target_ops method "linux_new_process". */
+/* Implementation of linux_target_ops method "new_process". */
static struct arch_process_info *
aarch64_linux_new_process (void)
return info;
}
+/* Implementation of linux_target_ops method "delete_process". */
+
+static void
+aarch64_linux_delete_process (struct arch_process_info *info)
+{
+ xfree (info);
+}
+
/* Implementation of linux_target_ops method "linux_new_fork". */
static void
*child->priv->arch_private = *parent->priv->arch_private;
}
-/* Return the right target description according to the ELF file of
- current thread. */
+/* Implementation of linux_target_ops method "arch_setup". */
-static const struct target_desc *
-aarch64_linux_read_description (void)
+static void
+aarch64_arch_setup (void)
{
unsigned int machine;
int is_elf64;
is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
if (is_elf64)
- return tdesc_aarch64;
+ current_process ()->tdesc = aarch64_linux_read_description ();
else
- return tdesc_arm_with_neon;
-}
-
-/* Implementation of linux_target_ops method "arch_setup". */
-
-static void
-aarch64_arch_setup (void)
-{
- current_process ()->tdesc = aarch64_linux_read_description ();
+ current_process ()->tdesc = tdesc_arm_with_neon;
aarch64_linux_get_debug_reg_capacity (lwpid_of (current_thread));
}
sizeof (struct user_fpsimd_state), FP_REGS,
aarch64_fill_fpregset, aarch64_store_fpregset
},
- { 0, 0, 0, -1, -1, NULL, NULL }
+ NULL_REGSET
};
static struct regsets_info aarch64_regsets_info =
return 0;
}
+/* Implementation of linux_target_ops method "get_syscall_trapinfo". */
+
+static void
+aarch64_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
+{
+ int use_64bit = register_size (regcache->tdesc, 0) == 8;
+
+ if (use_64bit)
+ {
+ long l_sysno;
+
+ collect_register_by_name (regcache, "x8", &l_sysno);
+ *sysno = (int) l_sysno;
+ }
+ else
+ collect_register_by_name (regcache, "r7", sysno);
+}
+
/* List of condition codes that we need. */
enum aarch64_condition_codes
LE = 0xd,
};
+enum aarch64_operand_type
+{
+ OPERAND_IMMEDIATE,
+ OPERAND_REGISTER,
+};
+
/* Representation of an operand. At this time, it only supports register
and immediate types. */
struct aarch64_operand
{
/* Type of the operand. */
- enum
- {
- OPERAND_IMMEDIATE,
- OPERAND_REGISTER,
- } type;
+ enum aarch64_operand_type type;
+
/* Value of the operand according to the type. */
union
{
/* Helper function for data processing instructions with register sources. */
static int
-emit_data_processing_reg (uint32_t *buf, enum aarch64_opcodes opcode,
+emit_data_processing_reg (uint32_t *buf, uint32_t opcode,
struct aarch64_register rd,
struct aarch64_register rn,
struct aarch64_register rm)
{
size_t byte_len = len * sizeof (uint32_t);
#if (__BYTE_ORDER == __BIG_ENDIAN)
- uint32_t *le_buf = xmalloc (byte_len);
+ uint32_t *le_buf = (uint32_t *) xmalloc (byte_len);
size_t i;
for (i = 0; i < len; i++)
{
struct aarch64_insn_relocation_data *insn_reloc
= (struct aarch64_insn_relocation_data *) data;
- int32_t new_offset
+ int64_t new_offset
= insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
if (can_encode_int32 (new_offset, 28))
{
struct aarch64_insn_relocation_data *insn_reloc
= (struct aarch64_insn_relocation_data *) data;
- int32_t new_offset
+ int64_t new_offset
= insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
if (can_encode_int32 (new_offset, 21))
{
struct aarch64_insn_relocation_data *insn_reloc
= (struct aarch64_insn_relocation_data *) data;
- int32_t new_offset
+ int64_t new_offset
= insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
if (can_encode_int32 (new_offset, 21))
{
struct aarch64_insn_relocation_data *insn_reloc
= (struct aarch64_insn_relocation_data *) data;
- int32_t new_offset
+ int64_t new_offset
= insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
if (can_encode_int32 (new_offset, 16))
{
uint32_t buf[256];
uint32_t *p = buf;
- int32_t offset;
+ int64_t offset;
int i;
uint32_t insn;
CORE_ADDR buildaddr = *jump_entry;
{
sprintf (err,
"E.Jump back from jump pad too far from tracepoint "
- "(offset 0x%" PRIx32 " cannot be encoded in 28 bits).",
+ "(offset 0x%" PRIx64 " cannot be encoded in 28 bits).",
offset);
return 1;
}
{
sprintf (err,
"E.Jump pad too far from tracepoint "
- "(offset 0x%" PRIx32 " cannot be encoded in 28 bits).",
+ "(offset 0x%" PRIx64 " cannot be encoded in 28 bits).",
offset);
return 1;
}
uint32_t *p = buf;
p += emit_pop (p, x1);
- p += emit_add (p, x0, x0, register_operand (x1));
+ p += emit_add (p, x0, x1, register_operand (x0));
emit_ops_insns (buf, p - buf);
}
uint32_t *p = buf;
p += emit_pop (p, x1);
- p += emit_sub (p, x0, x0, register_operand (x1));
+ p += emit_sub (p, x0, x1, register_operand (x0));
emit_ops_insns (buf, p - buf);
}
return 1;
}
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+aarch64_sw_breakpoint_from_kind (int kind, int *size)
+{
+ if (is_64bit_tdesc ())
+ {
+ *size = aarch64_breakpoint_len;
+ return aarch64_breakpoint;
+ }
+ else
+ return arm_sw_breakpoint_from_kind (kind, size);
+}
+
+/* Implementation of linux_target_ops method "breakpoint_kind_from_pc". */
+
+static int
+aarch64_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+ if (is_64bit_tdesc ())
+ return aarch64_breakpoint_len;
+ else
+ return arm_breakpoint_kind_from_pc (pcptr);
+}
+
+/* Implementation of the linux_target_ops method
+ "breakpoint_kind_from_current_state". */
+
+static int
+aarch64_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+ if (is_64bit_tdesc ())
+ return aarch64_breakpoint_len;
+ else
+ return arm_breakpoint_kind_from_current_state (pcptr);
+}
+
+/* Support for hardware single step. */
+
+static int
+aarch64_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
struct linux_target_ops the_low_target =
{
aarch64_arch_setup,
NULL, /* fetch_register */
aarch64_get_pc,
aarch64_set_pc,
- (const unsigned char *) &aarch64_breakpoint,
- aarch64_breakpoint_len,
- NULL, /* breakpoint_reinsert_addr */
+ aarch64_breakpoint_kind_from_pc,
+ aarch64_sw_breakpoint_from_kind,
+ NULL, /* get_next_pcs */
0, /* decr_pc_after_break */
aarch64_breakpoint_at,
aarch64_supports_z_point_type,
NULL, /* supply_ptrace_register */
aarch64_linux_siginfo_fixup,
aarch64_linux_new_process,
+ aarch64_linux_delete_process,
aarch64_linux_new_thread,
+ aarch64_linux_delete_thread,
aarch64_linux_new_fork,
aarch64_linux_prepare_to_resume,
NULL, /* process_qsupported */
aarch64_emit_ops,
aarch64_get_min_fast_tracepoint_insn_len,
aarch64_supports_range_stepping,
+ aarch64_breakpoint_kind_from_current_state,
+ aarch64_supports_hardware_single_step,
+ aarch64_get_syscall_trapinfo,
};
void
initialize_low_arch (void)
{
- init_registers_aarch64 ();
-
initialize_low_arch_aarch32 ();
initialize_regsets_info (&aarch64_regsets_info);
+
+#if GDB_SELF_TEST
+ initialize_low_tdesc ();
+#endif
}