gdb/gdbserver/linux-arm-low.c - gdb
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
- #include "server.h"
- #include "linux-low.h"
- #ifndef ELFMAG0
- #include <elf.h>
- #endif
- #include <sys/ptrace.h>
- #include <signal.h>
- 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;
- void init_registers_arm_with_neon (void);
- extern const struct target_desc *tdesc_arm_with_neon;
- #ifndef PTRACE_GET_THREAD_AREA
- #define PTRACE_GET_THREAD_AREA 22
- #endif
- #ifndef PTRACE_GETWMMXREGS
- # define PTRACE_GETWMMXREGS 18
- # define PTRACE_SETWMMXREGS 19
- #endif
- #ifndef PTRACE_GETVFPREGS
- # define PTRACE_GETVFPREGS 27
- # define PTRACE_SETVFPREGS 28
- #endif
- #ifndef PTRACE_GETHBPREGS
- #define PTRACE_GETHBPREGS 29
- #define PTRACE_SETHBPREGS 30
- #endif
- static struct
- {
- unsigned char arch;
- unsigned char max_wp_length;
- unsigned char wp_count;
- unsigned char bp_count;
- } arm_linux_hwbp_cap;
- typedef enum
- {
- arm_hwbp_break = 0,
- arm_hwbp_load = 1,
- arm_hwbp_store = 2,
- arm_hwbp_access = 3
- } arm_hwbp_type;
- typedef unsigned int arm_hwbp_control_t;
- struct arm_linux_hw_breakpoint
- {
-
- unsigned int address;
-
- arm_hwbp_control_t control;
- };
- #define MAX_BPTS 32
- #define MAX_WPTS 32
- struct arch_process_info
- {
-
- struct arm_linux_hw_breakpoint bpts[MAX_BPTS];
-
- struct arm_linux_hw_breakpoint wpts[MAX_WPTS];
- };
- struct arch_lwp_info
- {
-
- char bpts_changed[MAX_BPTS];
- char wpts_changed[MAX_WPTS];
-
- CORE_ADDR stopped_data_address;
- };
- static unsigned long arm_hwcap;
- #define HWCAP_VFP 64
- #define HWCAP_IWMMXT 512
- #define HWCAP_NEON 4096
- #define HWCAP_VFPv3 8192
- #define HWCAP_VFPv3D16 16384
- #ifdef HAVE_SYS_REG_H
- #include <sys/reg.h>
- #endif
- #define arm_num_regs 26
- static int arm_regmap[] = {
- 0, 4, 8, 12, 16, 20, 24, 28,
- 32, 36, 40, 44, 48, 52, 56, 60,
- -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 64
- };
- static int
- arm_cannot_store_register (int regno)
- {
- return (regno >= arm_num_regs);
- }
- static int
- arm_cannot_fetch_register (int regno)
- {
- return (regno >= arm_num_regs);
- }
- static void
- arm_fill_gregset (struct regcache *regcache, void *buf)
- {
- int i;
- for (i = 0; i < arm_num_regs; i++)
- if (arm_regmap[i] != -1)
- collect_register (regcache, i, ((char *) buf) + arm_regmap[i]);
- }
- static void
- arm_store_gregset (struct regcache *regcache, const void *buf)
- {
- int i;
- char zerobuf[8];
- memset (zerobuf, 0, 8);
- for (i = 0; i < arm_num_regs; i++)
- if (arm_regmap[i] != -1)
- supply_register (regcache, i, ((char *) buf) + arm_regmap[i]);
- else
- supply_register (regcache, i, zerobuf);
- }
- static void
- arm_fill_wmmxregset (struct regcache *regcache, void *buf)
- {
- int i;
- if (!(arm_hwcap & HWCAP_IWMMXT))
- return;
- for (i = 0; i < 16; i++)
- collect_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
-
- for (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 (!(arm_hwcap & HWCAP_IWMMXT))
- return;
- for (i = 0; i < 16; i++)
- supply_register (regcache, arm_num_regs + i, (char *) buf + i * 8);
-
- for (i = 0; i < 6; i++)
- supply_register (regcache, arm_num_regs + i + 16,
- (char *) buf + 16 * 8 + i * 4);
- }
- static void
- arm_fill_vfpregset (struct regcache *regcache, void *buf)
- {
- int i, num, base;
- if (!(arm_hwcap & HWCAP_VFP))
- return;
- if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
- num = 32;
- else
- num = 16;
- base = find_regno (regcache->tdesc, "d0");
- for (i = 0; i < num; i++)
- collect_register (regcache, base + i, (char *) buf + i * 8);
- collect_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
- }
- static void
- arm_store_vfpregset (struct regcache *regcache, const void *buf)
- {
- int i, num, base;
- if (!(arm_hwcap & HWCAP_VFP))
- return;
- if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
- num = 32;
- else
- num = 16;
- base = find_regno (regcache->tdesc, "d0");
- for (i = 0; i < num; i++)
- supply_register (regcache, base + i, (char *) buf + i * 8);
- supply_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
- }
- extern int debug_threads;
- 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;
- }
- static void
- arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
- {
- unsigned long newpc = pc;
- supply_register_by_name (regcache, "pc", &newpc);
- }
- static const unsigned long arm_breakpoint = 0xef9f0001;
- #define arm_breakpoint_len 4
- static const unsigned short thumb_breakpoint = 0xde01;
- static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
- static const unsigned long arm_eabi_breakpoint = 0xe7f001f0;
- static int
- arm_breakpoint_at (CORE_ADDR where)
- {
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long cpsr;
- collect_register_by_name (regcache, "cpsr", &cpsr);
- if (cpsr & 0x20)
- {
-
- unsigned short insn;
- (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
- if (insn == thumb_breakpoint)
- return 1;
- if (insn == thumb2_breakpoint[0])
- {
- (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
- if (insn == thumb2_breakpoint[1])
- return 1;
- }
- }
- else
- {
-
- unsigned long insn;
- (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == arm_breakpoint)
- return 1;
- if (insn == arm_eabi_breakpoint)
- return 1;
- }
- return 0;
- }
- static CORE_ADDR
- arm_reinsert_addr (void)
- {
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
- collect_register_by_name (regcache, "lr", &pc);
- return pc;
- }
- ps_err_e
- ps_get_thread_area (const struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
- {
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
- return PS_ERR;
-
- *base = (void *) ((char *)*base - idx);
- return PS_OK;
- }
- static void
- arm_linux_init_hwbp_cap (int pid)
- {
- unsigned int val;
- if (ptrace (PTRACE_GETHBPREGS, pid, 0, &val) < 0)
- return;
- arm_linux_hwbp_cap.arch = (unsigned char)((val >> 24) & 0xff);
- if (arm_linux_hwbp_cap.arch == 0)
- return;
- arm_linux_hwbp_cap.max_wp_length = (unsigned char)((val >> 16) & 0xff);
- arm_linux_hwbp_cap.wp_count = (unsigned char)((val >> 8) & 0xff);
- arm_linux_hwbp_cap.bp_count = (unsigned char)(val & 0xff);
- if (arm_linux_hwbp_cap.wp_count > MAX_WPTS)
- internal_error (__FILE__, __LINE__, "Unsupported number of watchpoints");
- if (arm_linux_hwbp_cap.bp_count > MAX_BPTS)
- internal_error (__FILE__, __LINE__, "Unsupported number of breakpoints");
- }
- static int
- arm_linux_get_hw_breakpoint_count (void)
- {
- return arm_linux_hwbp_cap.bp_count;
- }
- static int
- arm_linux_get_hw_watchpoint_count (void)
- {
- return arm_linux_hwbp_cap.wp_count;
- }
- static int
- arm_linux_get_hw_watchpoint_max_length (void)
- {
- return arm_linux_hwbp_cap.max_wp_length;
- }
- static arm_hwbp_control_t
- arm_hwbp_control_initialize (unsigned byte_address_select,
- arm_hwbp_type hwbp_type,
- int enable)
- {
- gdb_assert ((byte_address_select & ~0xffU) == 0);
- gdb_assert (hwbp_type != arm_hwbp_break
- || ((byte_address_select & 0xfU) != 0));
- return (byte_address_select << 5) | (hwbp_type << 3) | (3 << 1) | enable;
- }
- static int
- arm_hwbp_control_is_enabled (arm_hwbp_control_t control)
- {
- return control & 0x1;
- }
- static int
- arm_hwbp_control_is_initialized (arm_hwbp_control_t control)
- {
- return control != 0;
- }
- static arm_hwbp_control_t
- arm_hwbp_control_disable (arm_hwbp_control_t control)
- {
- return control & ~0x1;
- }
- static int
- arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1,
- const struct arm_linux_hw_breakpoint *p2)
- {
- return p1->address == p2->address && p1->control == p2->control;
- }
- static int
- raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type)
- {
- switch (raw_type)
- {
- case raw_bkpt_type_hw:
- return arm_hwbp_break;
- case raw_bkpt_type_write_wp:
- return arm_hwbp_store;
- case raw_bkpt_type_read_wp:
- return arm_hwbp_load;
- case raw_bkpt_type_access_wp:
- return arm_hwbp_access;
- default:
- gdb_assert_not_reached ("unhandled raw type");
- }
- }
- static int
- arm_linux_hw_point_initialize (enum raw_bkpt_type raw_type, CORE_ADDR addr,
- int len, struct arm_linux_hw_breakpoint *p)
- {
- arm_hwbp_type hwbp_type;
- unsigned mask;
- hwbp_type = raw_bkpt_type_to_arm_hwbp_type (raw_type);
- if (hwbp_type == arm_hwbp_break)
- {
-
- switch (len)
- {
- case 2:
- case 3:
- mask = 0x3;
- addr &= ~1;
- break;
- case 4:
- mask = 0xf;
- addr &= ~3;
- break;
- default:
-
- return -2;
- }
- }
- else
- {
- CORE_ADDR max_wp_length = arm_linux_get_hw_watchpoint_max_length ();
- CORE_ADDR aligned_addr;
-
- if (len <= 0)
- return -2;
-
- if ((len & (len - 1)) != 0)
- return -2;
-
- aligned_addr = addr & ~(max_wp_length - 1);
- if (aligned_addr + max_wp_length < addr + len)
- return -2;
- mask = (1 << len) - 1;
- }
- p->address = (unsigned int) addr;
- p->control = arm_hwbp_control_initialize (mask, hwbp_type, 1);
- return hwbp_type != arm_hwbp_break;
- }
- struct update_registers_data
- {
- int watch;
- int i;
- };
- static int
- update_registers_callback (struct inferior_list_entry *entry, void *arg)
- {
- 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;
-
- if (pid_of (thread) == pid_of (current_thread))
- {
-
- if (data->watch)
- lwp->arch_private->wpts_changed[data->i] = 1;
- else
- lwp->arch_private->bpts_changed[data->i] = 1;
-
- if (!lwp->stopped)
- linux_stop_lwp (lwp);
- }
- return 0;
- }
- static int
- arm_supports_z_point_type (char z_type)
- {
- switch (z_type)
- {
- case Z_PACKET_HW_BP:
- case Z_PACKET_WRITE_WP:
- case Z_PACKET_READ_WP:
- case Z_PACKET_ACCESS_WP:
- return 1;
- default:
-
- return 0;
- }
- }
- static int
- arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
- {
- struct process_info *proc = current_process ();
- struct arm_linux_hw_breakpoint p, *pts;
- int watch, i, count;
- watch = arm_linux_hw_point_initialize (type, addr, len, &p);
- if (watch < 0)
- {
-
- return watch == -1 ? 1 : -1;
- }
- if (watch)
- {
- count = arm_linux_get_hw_watchpoint_count ();
- pts = proc->private->arch_private->wpts;
- }
- else
- {
- count = arm_linux_get_hw_breakpoint_count ();
- pts = proc->private->arch_private->bpts;
- }
- 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);
- return 0;
- }
-
- return -1;
- }
- static int
- arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
- int len, struct raw_breakpoint *bp)
- {
- struct process_info *proc = current_process ();
- struct arm_linux_hw_breakpoint p, *pts;
- int watch, i, count;
- watch = arm_linux_hw_point_initialize (type, addr, len, &p);
- if (watch < 0)
- {
-
- return -1;
- }
- if (watch)
- {
- count = arm_linux_get_hw_watchpoint_count ();
- pts = proc->private->arch_private->wpts;
- }
- else
- {
- count = arm_linux_get_hw_breakpoint_count ();
- pts = proc->private->arch_private->bpts;
- }
- 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);
- return 0;
- }
-
- return -1;
- }
- static int
- arm_stopped_by_watchpoint (void)
- {
- struct lwp_info *lwp = get_thread_lwp (current_thread);
- siginfo_t siginfo;
-
- if (arm_linux_get_hw_watchpoint_count () == 0)
- return 0;
-
- errno = 0;
- ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo);
- if (errno != 0)
- return 0;
-
- if (siginfo.si_signo != SIGTRAP
- || (siginfo.si_code & 0xffff) != 0x0004 )
- return 0;
-
- if (siginfo.si_errno >= 0)
- return 0;
-
- lwp->arch_private->stopped_data_address
- = (CORE_ADDR) (uintptr_t) siginfo.si_addr;
- return 1;
- }
- static CORE_ADDR
- arm_stopped_data_address (void)
- {
- struct lwp_info *lwp = get_thread_lwp (current_thread);
- return lwp->arch_private->stopped_data_address;
- }
- static struct arch_process_info *
- arm_new_process (void)
- {
- struct arch_process_info *info = xcalloc (1, sizeof (*info));
- return info;
- }
- static struct arch_lwp_info *
- arm_new_thread (void)
- {
- struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
- int i;
- for (i = 0; i < MAX_BPTS; i++)
- info->bpts_changed[i] = 1;
- for (i = 0; i < MAX_WPTS; i++)
- info->wpts_changed[i] = 1;
- return info;
- }
- static void
- arm_prepare_to_resume (struct lwp_info *lwp)
- {
- struct thread_info *thread = get_lwp_thread (lwp);
- int pid = lwpid_of (thread);
- struct process_info *proc = find_process_pid (pid_of (thread));
- struct arch_process_info *proc_info = proc->private->arch_private;
- struct arch_lwp_info *lwp_info = lwp->arch_private;
- int i;
- for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++)
- if (lwp_info->bpts_changed[i])
- {
- errno = 0;
- if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) ((i << 1) + 1),
- &proc_info->bpts[i].address) < 0)
- perror_with_name ("Unexpected error setting breakpoint address");
- if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) ((i << 1) + 2),
- &proc_info->bpts[i].control) < 0)
- perror_with_name ("Unexpected error setting breakpoint");
- lwp_info->bpts_changed[i] = 0;
- }
- for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++)
- if (lwp_info->wpts_changed[i])
- {
- errno = 0;
- if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) -((i << 1) + 1),
- &proc_info->wpts[i].address) < 0)
- perror_with_name ("Unexpected error setting watchpoint address");
- if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control))
- if (ptrace (PTRACE_SETHBPREGS, pid,
- (PTRACE_TYPE_ARG3) -((i << 1) + 2),
- &proc_info->wpts[i].control) < 0)
- perror_with_name ("Unexpected error setting watchpoint");
- lwp_info->wpts_changed[i] = 0;
- }
- }
- static int
- arm_get_hwcap (unsigned long *valp)
- {
- unsigned char *data = 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);
-
- arm_linux_init_hwbp_cap (pid);
- arm_hwcap = 0;
- if (arm_get_hwcap (&arm_hwcap) == 0)
- return tdesc_arm;
- if (arm_hwcap & HWCAP_IWMMXT)
- return tdesc_arm_with_iwmmxt;
- if (arm_hwcap & HWCAP_VFP)
- {
- const struct target_desc *result;
- char *buf;
-
- if (arm_hwcap & HWCAP_NEON)
- result = tdesc_arm_with_neon;
- else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
- result = tdesc_arm_with_vfpv3;
- else
- result = tdesc_arm_with_vfpv2;
-
- errno = 0;
- buf = xmalloc (32 * 8 + 4);
- if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0
- && errno == EIO)
- {
- arm_hwcap = 0;
- result = tdesc_arm;
- }
- free (buf);
- return result;
- }
-
- return tdesc_arm;
- }
- static void
- arm_arch_setup (void)
- {
- current_process ()->tdesc = arm_read_description ();
- }
- static struct regset_info arm_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
- GENERAL_REGS,
- arm_fill_gregset, arm_store_gregset },
- { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, 16 * 8 + 6 * 4,
- EXTENDED_REGS,
- arm_fill_wmmxregset, arm_store_wmmxregset },
- { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4,
- EXTENDED_REGS,
- arm_fill_vfpregset, arm_store_vfpregset },
- { 0, 0, 0, -1, -1, NULL, NULL }
- };
- static struct regsets_info arm_regsets_info =
- {
- arm_regsets,
- 0,
- NULL,
- };
- static struct usrregs_info arm_usrregs_info =
- {
- arm_num_regs,
- arm_regmap,
- };
- static struct regs_info regs_info =
- {
- NULL,
- &arm_usrregs_info,
- &arm_regsets_info
- };
- static const struct regs_info *
- arm_regs_info (void)
- {
- return ®s_info;
- }
- struct linux_target_ops the_low_target = {
- arm_arch_setup,
- arm_regs_info,
- arm_cannot_fetch_register,
- arm_cannot_store_register,
- NULL,
- arm_get_pc,
- arm_set_pc,
-
- #ifndef __ARM_EABI__
- (const unsigned char *) &arm_breakpoint,
- #else
- (const unsigned char *) &arm_eabi_breakpoint,
- #endif
- arm_breakpoint_len,
- arm_reinsert_addr,
- 0,
- arm_breakpoint_at,
- arm_supports_z_point_type,
- arm_insert_point,
- arm_remove_point,
- arm_stopped_by_watchpoint,
- arm_stopped_data_address,
- NULL,
- NULL,
- NULL,
- arm_new_process,
- arm_new_thread,
- arm_prepare_to_resume,
- };
- void
- initialize_low_arch (void)
- {
-
- init_registers_arm ();
- init_registers_arm_with_iwmmxt ();
- init_registers_arm_with_vfpv2 ();
- init_registers_arm_with_vfpv3 ();
- init_registers_arm_with_neon ();
- initialize_regsets_info (&arm_regsets_info);
- }