/* GNU/Linux/ARM specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2015 Free Software Foundation, Inc.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
This file is part of GDB.
#include "arch/arm-linux.h"
#include "arch/arm-get-next-pcs.h"
#include "linux-aarch32-low.h"
+#include "linux-aarch32-tdesc.h"
+#include "linux-arm-tdesc.h"
#include <sys/uio.h>
/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
#include <signal.h>
#include <sys/syscall.h>
-/* Defined in auto-generated files. */
-void init_registers_arm (void);
-extern const struct target_desc *tdesc_arm;
-
-void init_registers_arm_with_iwmmxt (void);
-extern const struct target_desc *tdesc_arm_with_iwmmxt;
-
-void init_registers_arm_with_vfpv2 (void);
-extern const struct target_desc *tdesc_arm_with_vfpv2;
-
-void init_registers_arm_with_vfpv3 (void);
-extern const struct target_desc *tdesc_arm_with_vfpv3;
-
#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 22
#endif
static CORE_ADDR get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
CORE_ADDR val);
-static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
- CORE_ADDR pc);
+static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self);
static int get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
get_next_pcs_read_memory_unsigned_integer,
get_next_pcs_syscall_next_pc,
get_next_pcs_addr_bits_remove,
- get_next_pcs_is_thumb
+ get_next_pcs_is_thumb,
+ arm_linux_get_next_pcs_fixup,
};
static int
static void
arm_fill_wmmxregset (struct regcache *regcache, void *buf)
{
- int i;
-
- if (regcache->tdesc != tdesc_arm_with_iwmmxt)
+ if (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT)
return;
- for (i = 0; i < 16; i++)
+ for (int i = 0; i < 16; i++)
collect_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
/* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */
- for (i = 0; i < 6; i++)
+ for (int i = 0; i < 6; i++)
collect_register (regcache, arm_num_regs + i + 16,
(char *) buf + 16 * 8 + i * 4);
}
static void
arm_store_wmmxregset (struct regcache *regcache, const void *buf)
{
- int i;
-
- if (regcache->tdesc != tdesc_arm_with_iwmmxt)
+ if (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT)
return;
- for (i = 0; i < 16; i++)
+ for (int i = 0; i < 16; i++)
supply_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
/* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */
- for (i = 0; i < 6; i++)
+ for (int i = 0; i < 6; i++)
supply_register (regcache, arm_num_regs + i + 16,
(char *) buf + 16 * 8 + i * 4);
}
{
int num;
- if (regcache->tdesc == tdesc_arm_with_neon
- || regcache->tdesc == tdesc_arm_with_vfpv3)
+ if (is_aarch32_linux_description (regcache->tdesc))
num = 32;
- else if (regcache->tdesc == tdesc_arm_with_vfpv2)
- num = 16;
else
- return;
+ {
+ arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc);
+
+ if (fp_type == ARM_FP_TYPE_VFPV3)
+ num = 32;
+ else if (fp_type == ARM_FP_TYPE_VFPV2)
+ num = 16;
+ else
+ return;
+ }
arm_fill_vfpregset_num (regcache, buf, num);
}
{
int num;
- if (regcache->tdesc == tdesc_arm_with_neon
- || regcache->tdesc == tdesc_arm_with_vfpv3)
+ if (is_aarch32_linux_description (regcache->tdesc))
num = 32;
- else if (regcache->tdesc == tdesc_arm_with_vfpv2)
- num = 16;
else
- return;
-
- arm_store_vfpregset_num (regcache, buf, num);
-}
-
-extern int debug_threads;
+ {
+ arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc);
-static CORE_ADDR
-arm_get_pc (struct regcache *regcache)
-{
- unsigned long pc;
- collect_register_by_name (regcache, "pc", &pc);
- if (debug_threads)
- debug_printf ("stop pc is %08lx\n", pc);
- return pc;
-}
+ if (fp_type == ARM_FP_TYPE_VFPV3)
+ num = 32;
+ else if (fp_type == ARM_FP_TYPE_VFPV2)
+ num = 16;
+ else
+ return;
+ }
-static void
-arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- unsigned long newpc = pc;
- supply_register_by_name (regcache, "pc", &newpc);
+ arm_store_vfpregset_num (regcache, buf, num);
}
/* Wrapper of arm_is_thumb_mode for get_next_pcs. */
return arm_is_thumb_mode ();
}
-/* Read memory from the inferiror.
+/* Read memory from the inferior.
BYTE_ORDER is ignored and there to keep compatiblity with GDB's
read_memory_unsigned_integer. */
static ULONGEST
{
ULONGEST res;
- (*the_target->read_memory) (memaddr, (unsigned char *) &res, len);
+ res = 0;
+ target_read_memory (memaddr, (unsigned char *) &res, len);
+
return res;
}
/* 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)
{
if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
/* Callback to mark a watch-/breakpoint to be updated in all threads of
the current process. */
-struct update_registers_data
-{
- int watch;
- int i;
-};
-
-static int
-update_registers_callback (struct inferior_list_entry *entry, void *arg)
+static void
+update_registers_callback (thread_info *thread, int watch, int i)
{
- struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
- struct update_registers_data *data = (struct update_registers_data *) arg;
-
- /* Only update the threads of the current process. */
- if (pid_of (thread) == pid_of (current_thread))
- {
- /* The actual update is done later just before resuming the lwp,
- we just mark that the registers need updating. */
- if (data->watch)
- lwp->arch_private->wpts_changed[data->i] = 1;
- else
- lwp->arch_private->bpts_changed[data->i] = 1;
- /* If the lwp isn't stopped, force it to momentarily pause, so
- we can update its breakpoint registers. */
- if (!lwp->stopped)
- linux_stop_lwp (lwp);
- }
+ /* The actual update is done later just before resuming the lwp,
+ we just mark that the registers need updating. */
+ if (watch)
+ lwp->arch_private->wpts_changed[i] = 1;
+ else
+ lwp->arch_private->bpts_changed[i] = 1;
- return 0;
+ /* If the lwp isn't stopped, force it to momentarily pause, so
+ we can update its breakpoint registers. */
+ if (!lwp->stopped)
+ linux_stop_lwp (lwp);
}
static int
for (i = 0; i < count; i++)
if (!arm_hwbp_control_is_enabled (pts[i].control))
{
- struct update_registers_data data = { watch, i };
pts[i] = p;
- find_inferior (&all_threads, update_registers_callback, &data);
+
+ /* Only update the threads of the current process. */
+ for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
+ {
+ update_registers_callback (thread, watch, i);
+ });
+
return 0;
}
for (i = 0; i < count; i++)
if (arm_linux_hw_breakpoint_equal (&p, pts + i))
{
- struct update_registers_data data = { watch, i };
pts[i].control = arm_hwbp_control_disable (pts[i].control);
- find_inferior (&all_threads, update_registers_callback, &data);
+
+ /* Only update the threads of the current process. */
+ for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
+ {
+ update_registers_callback (thread, watch, i);
+ });
+
return 0;
}
return info;
}
+/* Called when a process is being deleted. */
+
+static void
+arm_delete_process (struct arch_process_info *info)
+{
+ xfree (info);
+}
+
/* Called when a new thread is detected. */
static void
arm_new_thread (struct lwp_info *lwp)
lwp->arch_private = info;
}
+/* Function to call when a thread is being deleted. */
+
+static void
+arm_delete_thread (struct arch_lwp_info *arch_lwp)
+{
+ xfree (arch_lwp);
+}
+
static void
arm_new_fork (struct process_info *parent, struct process_info *child)
{
/* Mark all the hardware breakpoints and watchpoints as changed to
make sure that the registers will be updated. */
- child_lwp = find_lwp_pid (ptid_of (child));
+ child_lwp = find_lwp_pid (ptid_t (child->pid));
child_lwp_info = child_lwp->arch_private;
for (i = 0; i < MAX_BPTS; i++)
child_lwp_info->bpts_changed[i] = 1;
}
}
-/* Find the next pc for a sigreturn or rt_sigreturn syscall.
+/* Find the next pc for a sigreturn or rt_sigreturn syscall. In
+ addition, set IS_THUMB depending on whether we will return to ARM
+ or Thumb code.
See arm-linux.h for stack layout details. */
static CORE_ADDR
-arm_sigreturn_next_pc (struct regcache *regcache, int svc_number)
+arm_sigreturn_next_pc (struct regcache *regcache, int svc_number,
+ int *is_thumb)
{
unsigned long sp;
unsigned long sp_data;
/* Offset of PC register. */
int pc_offset = 0;
CORE_ADDR next_pc = 0;
+ uint32_t cpsr;
gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn);
(*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4);
+ /* Set IS_THUMB according the CPSR saved on the stack. */
+ (*the_target->read_memory) (sp + pc_offset + 4, (unsigned char *) &cpsr, 4);
+ *is_thumb = ((cpsr & CPSR_T) != 0);
+
return next_pc;
}
/* When PC is at a syscall instruction, return the PC of the next
instruction to be executed. */
static CORE_ADDR
-get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
+get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
{
CORE_ADDR next_pc = 0;
+ CORE_ADDR pc = regcache_read_pc (self->regcache);
int is_thumb = arm_is_thumb_mode ();
ULONGEST svc_number = 0;
struct regcache *regcache = self->regcache;
unsigned long this_instr;
unsigned long svc_operand;
- (*the_target->read_memory) (pc, (unsigned char *) &this_instr, 4);
+ target_read_memory (pc, (unsigned char *) &this_instr, 4);
svc_operand = (0x00ffffff & this_instr);
if (svc_operand) /* OABI. */
/* This is a sigreturn or sigreturn_rt syscall. */
if (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn)
{
- next_pc = arm_sigreturn_next_pc (regcache, svc_number);
+ /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
+ update IS_THUMB. */
+ next_pc = arm_sigreturn_next_pc (regcache, svc_number, &is_thumb);
}
/* Addresses for calling Thumb functions have the bit 0 set. */
return next_pc;
}
-static int
-arm_get_hwcap (unsigned long *valp)
-{
- unsigned char *data = (unsigned char *) alloca (8);
- int offset = 0;
-
- while ((*the_target->read_auxv) (offset, data, 8) == 8)
- {
- unsigned int *data_p = (unsigned int *)data;
- if (data_p[0] == AT_HWCAP)
- {
- *valp = data_p[1];
- return 1;
- }
-
- offset += 8;
- }
-
- *valp = 0;
- return 0;
-}
-
static const struct target_desc *
arm_read_description (void)
{
- int pid = lwpid_of (current_thread);
- unsigned long arm_hwcap = 0;
-
- /* Query hardware watchpoint/breakpoint capabilities. */
- arm_linux_init_hwbp_cap (pid);
-
- if (arm_get_hwcap (&arm_hwcap) == 0)
- return tdesc_arm;
+ unsigned long arm_hwcap = linux_get_hwcap (4);
if (arm_hwcap & HWCAP_IWMMXT)
- return tdesc_arm_with_iwmmxt;
+ return arm_linux_read_description (ARM_FP_TYPE_IWMMXT);
if (arm_hwcap & HWCAP_VFP)
{
- const struct target_desc *result;
- char *buf;
+ /* Make sure that the kernel supports reading VFP registers. Support was
+ added in 2.6.30. */
+ int pid = lwpid_of (current_thread);
+ errno = 0;
+ char *buf = (char *) alloca (ARM_VFP3_REGS_SIZE);
+ if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 && errno == EIO)
+ return arm_linux_read_description (ARM_FP_TYPE_NONE);
/* NEON implies either no VFP, or VFPv3-D32. We only support
it with VFP. */
if (arm_hwcap & HWCAP_NEON)
- result = tdesc_arm_with_neon;
+ return aarch32_linux_read_description ();
else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
- result = tdesc_arm_with_vfpv3;
+ return arm_linux_read_description (ARM_FP_TYPE_VFPV3);
else
- result = tdesc_arm_with_vfpv2;
-
- /* Now make sure that the kernel supports reading these
- registers. Support was added in 2.6.30. */
- errno = 0;
- buf = (char *) xmalloc (32 * 8 + 4);
- if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0
- && errno == EIO)
- result = tdesc_arm;
-
- free (buf);
-
- return result;
+ return arm_linux_read_description (ARM_FP_TYPE_VFPV2);
}
/* The default configuration uses legacy FPA registers, probably
simulated. */
- return tdesc_arm;
+ return arm_linux_read_description (ARM_FP_TYPE_NONE);
}
static void
int gpregs[18];
struct iovec iov;
+ /* Query hardware watchpoint/breakpoint capabilities. */
+ arm_linux_init_hwbp_cap (tid);
+
current_process ()->tdesc = arm_read_description ();
iov.iov_base = gpregs;
/* Fetch the next possible PCs after the current instruction executes. */
-static VEC (CORE_ADDR) *
-arm_gdbserver_get_next_pcs (CORE_ADDR pc, struct regcache *regcache)
+static std::vector<CORE_ADDR>
+arm_gdbserver_get_next_pcs (struct regcache *regcache)
{
struct arm_get_next_pcs next_pcs_ctx;
- VEC (CORE_ADDR) *next_pcs = NULL;
arm_get_next_pcs_ctor (&next_pcs_ctx,
&get_next_pcs_ops,
/* Byte order is ignored assumed as host. */
0,
0,
- (const gdb_byte *) &thumb2_breakpoint,
+ 1,
regcache);
- next_pcs = arm_get_next_pcs (&next_pcs_ctx, pc);
-
- return next_pcs;
+ return arm_get_next_pcs (&next_pcs_ctx);
}
/* Support for hardware single step. */
return 0;
}
+/* Implementation of linux_target_ops method "get_syscall_trapinfo". */
+
+static void
+arm_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
+{
+ if (arm_is_thumb_mode ())
+ collect_register_by_name (regcache, "r7", sysno);
+ else
+ {
+ unsigned long pc;
+ unsigned long insn;
+
+ collect_register_by_name (regcache, "pc", &pc);
+
+ if ((*the_target->read_memory) (pc - 4, (unsigned char *) &insn, 4))
+ *sysno = UNKNOWN_SYSCALL;
+ else
+ {
+ unsigned long svc_operand = (0x00ffffff & insn);
+
+ if (svc_operand)
+ {
+ /* OABI */
+ *sysno = svc_operand - 0x900000;
+ }
+ else
+ {
+ /* EABI */
+ collect_register_by_name (regcache, "r7", sysno);
+ }
+ }
+ }
+}
+
/* Register sets without using PTRACE_GETREGSET. */
static struct regset_info arm_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
- GENERAL_REGS,
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0,
+ ARM_CORE_REGS_SIZE + ARM_INT_REGISTER_SIZE, GENERAL_REGS,
arm_fill_gregset, arm_store_gregset },
- { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, 16 * 8 + 6 * 4,
- EXTENDED_REGS,
+ { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, IWMMXT_REGS_SIZE, EXTENDED_REGS,
arm_fill_wmmxregset, arm_store_wmmxregset },
- { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4,
- EXTENDED_REGS,
+ { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, ARM_VFP3_REGS_SIZE, EXTENDED_REGS,
arm_fill_vfpregset, arm_store_vfpregset },
NULL_REGSET
};
const struct target_desc *tdesc = current_process ()->tdesc;
if (have_ptrace_getregset == 1
- && (tdesc == tdesc_arm_with_neon || tdesc == tdesc_arm_with_vfpv3))
+ && (is_aarch32_linux_description (tdesc)
+ || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
return ®s_info_aarch32;
- else
- return ®s_info_arm;
+
+ return ®s_info_arm;
}
struct linux_target_ops the_low_target = {
arm_cannot_fetch_register,
arm_cannot_store_register,
NULL, /* fetch_register */
- arm_get_pc,
- arm_set_pc,
+ linux_get_pc_32bit,
+ linux_set_pc_32bit,
arm_breakpoint_kind_from_pc,
arm_sw_breakpoint_from_kind,
arm_gdbserver_get_next_pcs,
NULL, /* supply_ptrace_register */
NULL, /* siginfo_fixup */
arm_new_process,
+ arm_delete_process,
arm_new_thread,
+ arm_delete_thread,
arm_new_fork,
arm_prepare_to_resume,
NULL, /* process_qsupported */
NULL, /* get_min_fast_tracepoint_insn_len */
NULL, /* supports_range_stepping */
arm_breakpoint_kind_from_current_state,
- arm_supports_hardware_single_step
+ arm_supports_hardware_single_step,
+ arm_get_syscall_trapinfo,
};
void
initialize_low_arch (void)
{
- /* Initialize the Linux target descriptions. */
- init_registers_arm ();
- init_registers_arm_with_iwmmxt ();
- init_registers_arm_with_vfpv2 ();
- init_registers_arm_with_vfpv3 ();
-
initialize_low_arch_aarch32 ();
-
initialize_regsets_info (&arm_regsets_info);
}