mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-20 01:50:24 +08:00
Add h/w watchpoint support to x86-linux, win32-i386.
* Makefile.in (SFILES): Add i386-low.c (i386_low_h): Define. (i386-low.o): Add dependencies. (linux-x86-low.o): Add i386-low.h dependency. (win32-i386-low.o): Ditto. * i386-low.c: New file. * i386-low.h: New file. * configure.srv (i[34567]86-*-cygwin*): Add i386-low.o to srv_tgtobj. (i[34567]86-*-linux*, i[34567]86-*-mingw*, x86_64-*-linux*): Ditto. * linux-low.c (linux_add_process): Initialize arch_private. (linux_remove_process): Free arch_private. (add_lwp): Initialize arch_private. (delete_lwp): Free arch_private. (linux_resume_one_lwp): Call the_low_target.prepare_to_resume if provided. * linux-low.h (process_info_private): New member arch_private. (lwp_info): New member arch_private. (linux_target_ops): New members new_process, new_thread, prepare_to_resume. (ptid_of): New macro. * linux-x86-low.c: Include stddef.h, i386-low.h. (arch_process_info): New struct. (arch_lwp_info): New struct. (x86_linux_dr_get, x86_linux_dr_set): New functions. (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. (i386_dr_low_get_status): New function. (x86_insert_point, x86_remove_point): New functions. (x86_stopped_by_watchpoint): New function. (x86_stopped_data_address): New function. (x86_linux_new_process, x86_linux_new_thread): New functions. (x86_linux_prepare_to_resume): New function. (the_low_target): Add entries for insert_point, remove_point, stopped_by_watchpoint, stopped_data_address, new_process, new_thread, prepare_to_resume. * server.c (debug_hw_points): New global. (monitor_show_help): Document set debug-hw-points. (handle_query): Process "set debug-hw-points". * server.h (debug_hw_points): Declare. (paddress): Declare. * utils.c (NUMCELLS, CELLSIZE): New macros. (get_sell, xsnprintf, paddress): New functions. * win32-arm-low.c (the_low_target): Add entries for insert_point, remove_point, stopped_by_watchpoint, stopped_data_address. * win32-i386-low.c: Include i386-low.h. (debug_reg_state): Replaces dr. (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. (i386_dr_low_get_status): New function. (i386_insert_point, i386_remove_point): New functions. (i386_stopped_by_watchpoint): New function. (i386_stopped_data_address): New function. (i386_initial_stuff): Update. (get_thread_context,set_thread_context,i386_thread_added): Update. (the_low_target): Add entries for insert_point, remove_point, stopped_by_watchpoint, stopped_data_address. * win32-low.c (win32_insert_watchpoint): New function. (win32_remove_watchpoint): New function. (win32_stopped_by_watchpoint): New function. (win32_stopped_data_address): New function. (win32_target_ops): Add entries for insert_watchpoint, remove_watchpoint, stopped_by_watchpoint, stopped_data_address. * win32-low.h (win32_target_ops): New members insert_point, remove_point, stopped_by_watchpoint, stopped_data_address.
This commit is contained in:
@ -18,10 +18,12 @@
|
||||
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 <stddef.h>
|
||||
#include <signal.h>
|
||||
#include "server.h"
|
||||
#include "linux-low.h"
|
||||
#include "i387-fp.h"
|
||||
#include "i386-low.h"
|
||||
|
||||
#include "gdb_proc_service.h"
|
||||
|
||||
@ -56,6 +58,21 @@ void init_registers_x86_64_linux (void);
|
||||
#define ARCH_GET_GS 0x1004
|
||||
#endif
|
||||
|
||||
/* Per-process arch-specific data we want to keep. */
|
||||
|
||||
struct arch_process_info
|
||||
{
|
||||
struct i386_debug_reg_state debug_reg_state;
|
||||
};
|
||||
|
||||
/* Per-thread arch-specific data we want to keep. */
|
||||
|
||||
struct arch_lwp_info
|
||||
{
|
||||
/* Non-zero if our copy differs from what's recorded in the thread. */
|
||||
int debug_registers_changed;
|
||||
};
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
/* Mapping between the general-purpose registers in `struct user'
|
||||
@ -317,6 +334,198 @@ x86_breakpoint_at (CORE_ADDR pc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Support for debug registers. */
|
||||
|
||||
static unsigned long
|
||||
x86_linux_dr_get (ptid_t ptid, int regnum)
|
||||
{
|
||||
int tid;
|
||||
unsigned long value;
|
||||
|
||||
tid = ptid_get_lwp (ptid);
|
||||
|
||||
errno = 0;
|
||||
value = ptrace (PTRACE_PEEKUSER, tid,
|
||||
offsetof (struct user, u_debugreg[regnum]), 0);
|
||||
if (errno != 0)
|
||||
error ("Couldn't read debug register");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
|
||||
{
|
||||
int tid;
|
||||
|
||||
tid = ptid_get_lwp (ptid);
|
||||
|
||||
errno = 0;
|
||||
ptrace (PTRACE_POKEUSER, tid,
|
||||
offsetof (struct user, u_debugreg[regnum]), value);
|
||||
if (errno != 0)
|
||||
error ("Couldn't write debug register");
|
||||
}
|
||||
|
||||
/* Update the inferior's debug register REGNUM from STATE. */
|
||||
|
||||
void
|
||||
i386_dr_low_set_addr (const struct i386_debug_reg_state *state, int regnum)
|
||||
{
|
||||
struct inferior_list_entry *lp;
|
||||
CORE_ADDR addr;
|
||||
/* Only need to update the threads of this process. */
|
||||
int pid = pid_of (get_thread_lwp (current_inferior));
|
||||
|
||||
if (! (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR))
|
||||
fatal ("Invalid debug register %d", regnum);
|
||||
|
||||
addr = state->dr_mirror[regnum];
|
||||
|
||||
for (lp = all_lwps.head; lp; lp = lp->next)
|
||||
{
|
||||
struct lwp_info *lwp = (struct lwp_info *) lp;
|
||||
|
||||
/* The actual update is done later, we just mark that the register
|
||||
needs updating. */
|
||||
if (pid_of (lwp) == pid)
|
||||
lwp->arch_private->debug_registers_changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the inferior's DR7 debug control register from STATE. */
|
||||
|
||||
void
|
||||
i386_dr_low_set_control (const struct i386_debug_reg_state *state)
|
||||
{
|
||||
struct inferior_list_entry *lp;
|
||||
/* Only need to update the threads of this process. */
|
||||
int pid = pid_of (get_thread_lwp (current_inferior));
|
||||
|
||||
for (lp = all_lwps.head; lp; lp = lp->next)
|
||||
{
|
||||
struct lwp_info *lwp = (struct lwp_info *) lp;
|
||||
|
||||
/* The actual update is done later, we just mark that the register
|
||||
needs updating. */
|
||||
if (pid_of (lwp) == pid)
|
||||
lwp->arch_private->debug_registers_changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the value of the DR6 debug status register from the inferior
|
||||
and record it in STATE. */
|
||||
|
||||
void
|
||||
i386_dr_low_get_status (struct i386_debug_reg_state *state)
|
||||
{
|
||||
struct lwp_info *lwp = get_thread_lwp (current_inferior);
|
||||
ptid_t ptid = ptid_of (lwp);
|
||||
|
||||
state->dr_status_mirror = x86_linux_dr_get (ptid, DR_STATUS);
|
||||
}
|
||||
|
||||
/* Watchpoint support. */
|
||||
|
||||
static int
|
||||
x86_insert_point (char type, CORE_ADDR addr, int len)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
switch (type)
|
||||
{
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
return i386_low_insert_watchpoint (&proc->private->arch_private->debug_reg_state,
|
||||
type, addr, len);
|
||||
default:
|
||||
/* Unsupported. */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
x86_remove_point (char type, CORE_ADDR addr, int len)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
switch (type)
|
||||
{
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
return i386_low_remove_watchpoint (&proc->private->arch_private->debug_reg_state,
|
||||
type, addr, len);
|
||||
default:
|
||||
/* Unsupported. */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
x86_stopped_by_watchpoint (void)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
return i386_low_stopped_by_watchpoint (&proc->private->arch_private->debug_reg_state);
|
||||
}
|
||||
|
||||
static CORE_ADDR
|
||||
x86_stopped_data_address (void)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
CORE_ADDR addr;
|
||||
if (i386_low_stopped_data_address (&proc->private->arch_private->debug_reg_state,
|
||||
&addr))
|
||||
return addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when a new process is created. */
|
||||
|
||||
static struct arch_process_info *
|
||||
x86_linux_new_process (void)
|
||||
{
|
||||
struct arch_process_info *info = xcalloc (1, sizeof (*info));
|
||||
|
||||
i386_low_init_dregs (&info->debug_reg_state);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/* Called when a new thread is detected. */
|
||||
|
||||
static struct arch_lwp_info *
|
||||
x86_linux_new_thread (void)
|
||||
{
|
||||
struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
|
||||
|
||||
info->debug_registers_changed = 1;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/* Called when resuming a thread.
|
||||
If the debug regs have changed, update the thread's copies. */
|
||||
|
||||
static void
|
||||
x86_linux_prepare_to_resume (struct lwp_info *lwp)
|
||||
{
|
||||
if (lwp->arch_private->debug_registers_changed)
|
||||
{
|
||||
int i;
|
||||
ptid_t ptid = ptid_of (lwp);
|
||||
int pid = ptid_get_pid (ptid);
|
||||
struct process_info *proc = find_process_pid (pid);
|
||||
struct i386_debug_reg_state *state = &proc->private->arch_private->debug_reg_state;
|
||||
|
||||
for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
|
||||
x86_linux_dr_set (ptid, i, state->dr_mirror[i]);
|
||||
|
||||
x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror);
|
||||
|
||||
lwp->arch_private->debug_registers_changed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* When GDBSERVER is built as a 64-bit application on linux, the
|
||||
PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since
|
||||
debugging a 32-bit inferior with a 64-bit GDBSERVER should look the same
|
||||
@ -630,10 +839,10 @@ struct linux_target_ops the_low_target =
|
||||
NULL,
|
||||
1,
|
||||
x86_breakpoint_at,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
x86_insert_point,
|
||||
x86_remove_point,
|
||||
x86_stopped_by_watchpoint,
|
||||
x86_stopped_data_address,
|
||||
/* collect_ptrace_register/supply_ptrace_register are not needed in the
|
||||
native i386 case (no registers smaller than an xfer unit), and are not
|
||||
used in the biarch case (HAVE_LINUX_USRREGS is not defined). */
|
||||
@ -641,4 +850,7 @@ struct linux_target_ops the_low_target =
|
||||
NULL,
|
||||
/* need to fix up i386 siginfo if host is amd64 */
|
||||
x86_siginfo_fixup,
|
||||
x86_linux_new_process,
|
||||
x86_linux_new_thread,
|
||||
x86_linux_prepare_to_resume
|
||||
};
|
||||
|
Reference in New Issue
Block a user