mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-22 19:09:31 +08:00
Implement displaced stepping.
gdb/ * gdbarch.sh (max_insn_length): New 'variable'. (displaced_step_copy, displaced_step_fixup) (displaced_step_free_closure, displaced_step_location): New functions. (struct displaced_step_closure): Add forward declaration. * gdbarch.c, gdbarch.h: Regenerated. * arch-utils.c: #include "objfiles.h". (simple_displaced_step_copy_insn) (simple_displaced_step_free_closure) (displaced_step_at_entry_point): New functions. * arch-utils.h (simple_displaced_step_copy_insn) (simple_displaced_step_free_closure) (displaced_step_at_entry_point): New prototypes. * i386-tdep.c (I386_MAX_INSN_LEN): Rename to... (I386_MAX_MATCHED_INSN_LEN): ... this. (i386_absolute_jmp_p, i386_absolute_call_p) (i386_ret_p, i386_call_p, i386_breakpoint_p, i386_syscall_p) (i386_displaced_step_fixup): New functions. (struct i386_insn, i386_match_insn): Update. (i386_gdbarch_init): Set gdbarch_max_insn_length. * i386-tdep.h (I386_MAX_INSN_LEN): New. (i386_displaced_step_fixup): New prototype. * i386-linux-tdep.c (i386_linux_init_abi): Include "arch-utils.h". Register gdbarch_displaced_step_copy, gdbarch_displaced_step_fixup, gdbarch_displaced_step_free_closure, and gdbarch_displaced_step_location functions. * infrun.c (debug_displaced): New variable. (show_debug_displaced): New function. (struct displaced_step_request): New struct. (displaced_step_request_queue, displaced_step_ptid) (displaced_step_gdbarch, displaced_step_closure) (displaced_step_original, displaced_step_copy) (displaced_step_saved_copy, can_use_displaced_stepping): New variables. (show_can_use_displaced_stepping, use_displaced_stepping) (displaced_step_clear, cleanup_displaced_step_closure) (displaced_step_dump_bytes, displaced_step_prepare) (displaced_step_clear_cleanup, write_memory_ptid) (displaced_step_fixup): New functions. (resume): Call displaced_step_prepare. (proceed): Call read_pc once, and remember the value. If using displaced stepping, don't remove breakpoints. (handle_inferior_event): Call displaced_step_fixup. Add some debugging output. When we try to step over a breakpoint, but get a signal to deliver to the thread instead, ensure the step-resume breakpoint is actually inserted. If a thread hop is needed, and displaced stepping is enabled, don't remove breakpoints. (init_wait_for_inferior): Call displaced_step_clear. (_initialize_infrun): Add "set debug displaced" command. Add "maint set can-use-displaced-stepping" command. Clear displaced_step_ptid. * inferior.h (debug_displaced): Declare variable. (displaced_step_dump_bytes): Declare function. * Makefile.in (arch-utils.o, i386-linux-tdep.o): Update dependencies. gdb/testsuite/ * gdb.asm/asmsrc1.s: Add scratch space. gdb/doc/ * gdb.texinfo (Debugging Output): Document "set/show debug displaced". (Maintenance Commands): Document "maint set/show can-use-displaced-stepping".
This commit is contained in:
@ -1,3 +1,68 @@
|
|||||||
|
2008-05-02 Jim Blandy <jimb@codesourcery.com>
|
||||||
|
Pedro Alves <pedro@codesourcery.com>
|
||||||
|
|
||||||
|
Implement displaced stepping.
|
||||||
|
|
||||||
|
* gdbarch.sh (max_insn_length): New 'variable'.
|
||||||
|
(displaced_step_copy, displaced_step_fixup)
|
||||||
|
(displaced_step_free_closure, displaced_step_location): New
|
||||||
|
functions.
|
||||||
|
(struct displaced_step_closure): Add forward declaration.
|
||||||
|
* gdbarch.c, gdbarch.h: Regenerated.
|
||||||
|
|
||||||
|
* arch-utils.c: #include "objfiles.h".
|
||||||
|
(simple_displaced_step_copy_insn)
|
||||||
|
(simple_displaced_step_free_closure)
|
||||||
|
(displaced_step_at_entry_point): New functions.
|
||||||
|
* arch-utils.h (simple_displaced_step_copy_insn)
|
||||||
|
(simple_displaced_step_free_closure)
|
||||||
|
(displaced_step_at_entry_point): New prototypes.
|
||||||
|
|
||||||
|
* i386-tdep.c (I386_MAX_INSN_LEN): Rename to...
|
||||||
|
(I386_MAX_MATCHED_INSN_LEN): ... this.
|
||||||
|
(i386_absolute_jmp_p, i386_absolute_call_p)
|
||||||
|
(i386_ret_p, i386_call_p, i386_breakpoint_p, i386_syscall_p)
|
||||||
|
(i386_displaced_step_fixup): New functions.
|
||||||
|
(struct i386_insn, i386_match_insn): Update.
|
||||||
|
(i386_gdbarch_init): Set gdbarch_max_insn_length.
|
||||||
|
* i386-tdep.h (I386_MAX_INSN_LEN): New.
|
||||||
|
(i386_displaced_step_fixup): New prototype.
|
||||||
|
* i386-linux-tdep.c (i386_linux_init_abi): Include "arch-utils.h".
|
||||||
|
Register gdbarch_displaced_step_copy,
|
||||||
|
gdbarch_displaced_step_fixup, gdbarch_displaced_step_free_closure,
|
||||||
|
and gdbarch_displaced_step_location functions.
|
||||||
|
|
||||||
|
* infrun.c (debug_displaced): New variable.
|
||||||
|
(show_debug_displaced): New function.
|
||||||
|
(struct displaced_step_request): New struct.
|
||||||
|
(displaced_step_request_queue, displaced_step_ptid)
|
||||||
|
(displaced_step_gdbarch, displaced_step_closure)
|
||||||
|
(displaced_step_original, displaced_step_copy)
|
||||||
|
(displaced_step_saved_copy, can_use_displaced_stepping): New
|
||||||
|
variables.
|
||||||
|
(show_can_use_displaced_stepping, use_displaced_stepping)
|
||||||
|
(displaced_step_clear, cleanup_displaced_step_closure)
|
||||||
|
(displaced_step_dump_bytes, displaced_step_prepare)
|
||||||
|
(displaced_step_clear_cleanup, write_memory_ptid)
|
||||||
|
(displaced_step_fixup): New functions.
|
||||||
|
(resume): Call displaced_step_prepare.
|
||||||
|
(proceed): Call read_pc once, and remember the value. If using
|
||||||
|
displaced stepping, don't remove breakpoints.
|
||||||
|
(handle_inferior_event): Call displaced_step_fixup. Add some
|
||||||
|
debugging output. When we try to step over a breakpoint, but get
|
||||||
|
a signal to deliver to the thread instead, ensure the step-resume
|
||||||
|
breakpoint is actually inserted. If a thread hop is needed, and
|
||||||
|
displaced stepping is enabled, don't remove breakpoints.
|
||||||
|
(init_wait_for_inferior): Call displaced_step_clear.
|
||||||
|
(_initialize_infrun): Add "set debug displaced" command. Add
|
||||||
|
"maint set can-use-displaced-stepping" command. Clear
|
||||||
|
displaced_step_ptid.
|
||||||
|
* inferior.h (debug_displaced): Declare variable.
|
||||||
|
(displaced_step_dump_bytes): Declare function.
|
||||||
|
|
||||||
|
* Makefile.in (arch-utils.o, i386-linux-tdep.o): Update
|
||||||
|
dependencies.
|
||||||
|
|
||||||
2008-05-02 Daniel Jacobowitz <dan@codesourcery.com>
|
2008-05-02 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* arm-tdep.c (arm_mode_strings, arm_fallback_mode_string)
|
* arm-tdep.c (arm_mode_strings, arm_fallback_mode_string)
|
||||||
|
@ -1917,7 +1917,7 @@ annotate.o: annotate.c $(defs_h) $(annotate_h) $(value_h) $(target_h) \
|
|||||||
arch-utils.o: arch-utils.c $(defs_h) $(arch_utils_h) $(buildsym_h) \
|
arch-utils.o: arch-utils.c $(defs_h) $(arch_utils_h) $(buildsym_h) \
|
||||||
$(gdbcmd_h) $(inferior_h) $(gdb_string_h) $(regcache_h) \
|
$(gdbcmd_h) $(inferior_h) $(gdb_string_h) $(regcache_h) \
|
||||||
$(gdb_assert_h) $(sim_regno_h) $(gdbcore_h) $(osabi_h) $(version_h) \
|
$(gdb_assert_h) $(sim_regno_h) $(gdbcore_h) $(osabi_h) $(version_h) \
|
||||||
$(floatformat_h) $(target_descriptions_h)
|
$(floatformat_h) $(target_descriptions_h) $(objfiles_h)
|
||||||
arm-linux-nat.o: arm-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \
|
arm-linux-nat.o: arm-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \
|
||||||
$(gdb_string_h) $(regcache_h) $(arm_tdep_h) $(gregset_h) \
|
$(gdb_string_h) $(regcache_h) $(arm_tdep_h) $(gregset_h) \
|
||||||
$(target_h) $(linux_nat_h) $(gdb_proc_service_h) $(arm_linux_tdep_h) \
|
$(target_h) $(linux_nat_h) $(gdb_proc_service_h) $(arm_linux_tdep_h) \
|
||||||
@ -2257,7 +2257,8 @@ i386-linux-nat.o: i386-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \
|
|||||||
i386-linux-tdep.o: i386-linux-tdep.c $(defs_h) $(gdbcore_h) $(frame_h) \
|
i386-linux-tdep.o: i386-linux-tdep.c $(defs_h) $(gdbcore_h) $(frame_h) \
|
||||||
$(value_h) $(regcache_h) $(inferior_h) $(osabi_h) $(reggroups_h) \
|
$(value_h) $(regcache_h) $(inferior_h) $(osabi_h) $(reggroups_h) \
|
||||||
$(dwarf2_frame_h) $(gdb_string_h) $(i386_tdep_h) \
|
$(dwarf2_frame_h) $(gdb_string_h) $(i386_tdep_h) \
|
||||||
$(i386_linux_tdep_h) $(glibc_tdep_h) $(solib_svr4_h) $(symtab_h)
|
$(i386_linux_tdep_h) $(glibc_tdep_h) $(solib_svr4_h) $(symtab_h) \
|
||||||
|
$(arch_utils_h)
|
||||||
i386-nat.o: i386-nat.c $(defs_h) $(breakpoint_h) $(command_h) $(gdbcmd_h) \
|
i386-nat.o: i386-nat.c $(defs_h) $(breakpoint_h) $(command_h) $(gdbcmd_h) \
|
||||||
$(target_h)
|
$(target_h)
|
||||||
i386nbsd-nat.o: i386nbsd-nat.c $(defs_h) $(gdbcore_h) $(regcache_h) \
|
i386nbsd-nat.o: i386nbsd-nat.c $(defs_h) $(gdbcore_h) $(regcache_h) \
|
||||||
|
@ -31,12 +31,64 @@
|
|||||||
#include "gdbcore.h"
|
#include "gdbcore.h"
|
||||||
#include "osabi.h"
|
#include "osabi.h"
|
||||||
#include "target-descriptions.h"
|
#include "target-descriptions.h"
|
||||||
|
#include "objfiles.h"
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include "floatformat.h"
|
#include "floatformat.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct displaced_step_closure *
|
||||||
|
simple_displaced_step_copy_insn (struct gdbarch *gdbarch,
|
||||||
|
CORE_ADDR from, CORE_ADDR to,
|
||||||
|
struct regcache *regs)
|
||||||
|
{
|
||||||
|
size_t len = gdbarch_max_insn_length (gdbarch);
|
||||||
|
gdb_byte *buf = xmalloc (len);
|
||||||
|
|
||||||
|
read_memory (from, buf, len);
|
||||||
|
write_memory (to, buf, len);
|
||||||
|
|
||||||
|
if (debug_displaced)
|
||||||
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "displaced: copy 0x%s->0x%s: ",
|
||||||
|
paddr_nz (from), paddr_nz (to));
|
||||||
|
displaced_step_dump_bytes (gdb_stdlog, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (struct displaced_step_closure *) buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
simple_displaced_step_free_closure (struct gdbarch *gdbarch,
|
||||||
|
struct displaced_step_closure *closure)
|
||||||
|
{
|
||||||
|
xfree (closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CORE_ADDR
|
||||||
|
displaced_step_at_entry_point (struct gdbarch *gdbarch)
|
||||||
|
{
|
||||||
|
CORE_ADDR addr;
|
||||||
|
int bp_len;
|
||||||
|
|
||||||
|
addr = entry_point_address ();
|
||||||
|
|
||||||
|
/* Make certain that the address points at real code, and not a
|
||||||
|
function descriptor. */
|
||||||
|
addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr, ¤t_target);
|
||||||
|
|
||||||
|
/* Inferior calls also use the entry point as a breakpoint location.
|
||||||
|
We don't want displaced stepping to interfere with those
|
||||||
|
breakpoints, so leave space. */
|
||||||
|
gdbarch_breakpoint_from_pc (gdbarch, &addr, &bp_len);
|
||||||
|
addr += bp_len * 2;
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
legacy_register_sim_regno (struct gdbarch *gdbarch, int regnum)
|
legacy_register_sim_regno (struct gdbarch *gdbarch, int regnum)
|
||||||
{
|
{
|
||||||
|
@ -30,6 +30,30 @@ struct gdbarch_info;
|
|||||||
/* gdbarch trace variable */
|
/* gdbarch trace variable */
|
||||||
extern int gdbarch_debug;
|
extern int gdbarch_debug;
|
||||||
|
|
||||||
|
/* An implementation of gdbarch_displaced_step_copy_insn for
|
||||||
|
processors that don't need to modify the instruction before
|
||||||
|
single-stepping the displaced copy.
|
||||||
|
|
||||||
|
Simply copy gdbarch_max_insn_length (ARCH) bytes from FROM to TO.
|
||||||
|
The closure is an array of that many bytes containing the
|
||||||
|
instruction's bytes, allocated with xmalloc. */
|
||||||
|
extern struct displaced_step_closure *
|
||||||
|
simple_displaced_step_copy_insn (struct gdbarch *gdbarch,
|
||||||
|
CORE_ADDR from, CORE_ADDR to,
|
||||||
|
struct regcache *regs);
|
||||||
|
|
||||||
|
/* Simple implementation of gdbarch_displaced_step_free_closure: Call
|
||||||
|
xfree.
|
||||||
|
This is appropriate for use with simple_displaced_step_copy_insn. */
|
||||||
|
extern void
|
||||||
|
simple_displaced_step_free_closure (struct gdbarch *gdbarch,
|
||||||
|
struct displaced_step_closure *closure);
|
||||||
|
|
||||||
|
/* Possible value for gdbarch_displaced_step_location:
|
||||||
|
Place displaced instructions at the program's entry point,
|
||||||
|
leaving space for inferior function call return breakpoints. */
|
||||||
|
extern CORE_ADDR displaced_step_at_entry_point (struct gdbarch *gdbarch);
|
||||||
|
|
||||||
/* The only possible cases for inner_than. */
|
/* The only possible cases for inner_than. */
|
||||||
extern int core_addr_lessthan (CORE_ADDR lhs, CORE_ADDR rhs);
|
extern int core_addr_lessthan (CORE_ADDR lhs, CORE_ADDR rhs);
|
||||||
extern int core_addr_greaterthan (CORE_ADDR lhs, CORE_ADDR rhs);
|
extern int core_addr_greaterthan (CORE_ADDR lhs, CORE_ADDR rhs);
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
2008-05-02 Pedro Alves <pedro@codesourcery.com>
|
||||||
|
|
||||||
|
* gdb.texinfo (Debugging Output): Document "set/show debug
|
||||||
|
displaced".
|
||||||
|
(Maintenance Commands): Document "maint set/show
|
||||||
|
can-use-displaced-stepping".
|
||||||
|
|
||||||
2008-05-02 Daniel Jacobowitz <dan@codesourcery.com>
|
2008-05-02 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* gdb.texinfo (ARM): Document set/show arm fallback-mode
|
* gdb.texinfo (ARM): Document set/show arm fallback-mode
|
||||||
|
@ -16483,6 +16483,13 @@ Display debugging messages about inner workings of the AIX thread
|
|||||||
module.
|
module.
|
||||||
@item show debug aix-thread
|
@item show debug aix-thread
|
||||||
Show the current state of AIX thread debugging info display.
|
Show the current state of AIX thread debugging info display.
|
||||||
|
@item set debug displaced
|
||||||
|
@cindex displaced stepping debugging info
|
||||||
|
Turns on or off display of @value{GDBN} debugging info for the
|
||||||
|
displaced stepping support. The default is off.
|
||||||
|
@item show debug displaced
|
||||||
|
Displays the current state of displaying @value{GDBN} debugging info
|
||||||
|
related to displaced stepping.
|
||||||
@item set debug event
|
@item set debug event
|
||||||
@cindex event debugging info
|
@cindex event debugging info
|
||||||
Turns on or off display of @value{GDBN} event debugging info. The
|
Turns on or off display of @value{GDBN} event debugging info. The
|
||||||
@ -23159,6 +23166,19 @@ Shared library events.
|
|||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
@kindex maint set can-use-displaced-stepping
|
||||||
|
@kindex maint show can-use-displaced-stepping
|
||||||
|
@cindex displaced stepping support
|
||||||
|
@cindex out-of-line single-stepping
|
||||||
|
@item maint set can-use-displaced-stepping
|
||||||
|
@itemx maint show can-use-displaced-stepping
|
||||||
|
Control whether or not @value{GDBN} will do @dfn{displaced stepping}
|
||||||
|
if the target supports it. The default is on. Displaced stepping is
|
||||||
|
a way to single-step over breakpoints without removing them from the
|
||||||
|
inferior, by executing an out-of-line copy of the instruction that was
|
||||||
|
originally at the breakpoint location. It is also known as
|
||||||
|
out-of-line single-stepping.
|
||||||
|
|
||||||
@kindex maint check-symtabs
|
@kindex maint check-symtabs
|
||||||
@item maint check-symtabs
|
@item maint check-symtabs
|
||||||
Check the consistency of psymtabs and symtabs.
|
Check the consistency of psymtabs and symtabs.
|
||||||
|
152
gdb/gdbarch.c
152
gdb/gdbarch.c
@ -226,6 +226,11 @@ struct gdbarch
|
|||||||
int vtable_function_descriptors;
|
int vtable_function_descriptors;
|
||||||
int vbit_in_delta;
|
int vbit_in_delta;
|
||||||
gdbarch_skip_permanent_breakpoint_ftype *skip_permanent_breakpoint;
|
gdbarch_skip_permanent_breakpoint_ftype *skip_permanent_breakpoint;
|
||||||
|
ULONGEST max_insn_length;
|
||||||
|
gdbarch_displaced_step_copy_insn_ftype *displaced_step_copy_insn;
|
||||||
|
gdbarch_displaced_step_fixup_ftype *displaced_step_fixup;
|
||||||
|
gdbarch_displaced_step_free_closure_ftype *displaced_step_free_closure;
|
||||||
|
gdbarch_displaced_step_location_ftype *displaced_step_location;
|
||||||
gdbarch_overlay_update_ftype *overlay_update;
|
gdbarch_overlay_update_ftype *overlay_update;
|
||||||
gdbarch_core_read_description_ftype *core_read_description;
|
gdbarch_core_read_description_ftype *core_read_description;
|
||||||
gdbarch_static_transform_name_ftype *static_transform_name;
|
gdbarch_static_transform_name_ftype *static_transform_name;
|
||||||
@ -350,6 +355,11 @@ struct gdbarch startup_gdbarch =
|
|||||||
0, /* vtable_function_descriptors */
|
0, /* vtable_function_descriptors */
|
||||||
0, /* vbit_in_delta */
|
0, /* vbit_in_delta */
|
||||||
0, /* skip_permanent_breakpoint */
|
0, /* skip_permanent_breakpoint */
|
||||||
|
0, /* max_insn_length */
|
||||||
|
0, /* displaced_step_copy_insn */
|
||||||
|
0, /* displaced_step_fixup */
|
||||||
|
NULL, /* displaced_step_free_closure */
|
||||||
|
NULL, /* displaced_step_location */
|
||||||
0, /* overlay_update */
|
0, /* overlay_update */
|
||||||
0, /* core_read_description */
|
0, /* core_read_description */
|
||||||
0, /* static_transform_name */
|
0, /* static_transform_name */
|
||||||
@ -435,6 +445,9 @@ gdbarch_alloc (const struct gdbarch_info *info,
|
|||||||
gdbarch->coff_make_msymbol_special = default_coff_make_msymbol_special;
|
gdbarch->coff_make_msymbol_special = default_coff_make_msymbol_special;
|
||||||
gdbarch->name_of_malloc = "malloc";
|
gdbarch->name_of_malloc = "malloc";
|
||||||
gdbarch->register_reggroup_p = default_register_reggroup_p;
|
gdbarch->register_reggroup_p = default_register_reggroup_p;
|
||||||
|
gdbarch->displaced_step_fixup = NULL;
|
||||||
|
gdbarch->displaced_step_free_closure = NULL;
|
||||||
|
gdbarch->displaced_step_location = NULL;
|
||||||
gdbarch->target_signal_from_host = default_target_signal_from_host;
|
gdbarch->target_signal_from_host = default_target_signal_from_host;
|
||||||
gdbarch->target_signal_to_host = default_target_signal_to_host;
|
gdbarch->target_signal_to_host = default_target_signal_to_host;
|
||||||
/* gdbarch_alloc() */
|
/* gdbarch_alloc() */
|
||||||
@ -592,6 +605,13 @@ verify_gdbarch (struct gdbarch *gdbarch)
|
|||||||
/* Skip verify of vtable_function_descriptors, invalid_p == 0 */
|
/* Skip verify of vtable_function_descriptors, invalid_p == 0 */
|
||||||
/* Skip verify of vbit_in_delta, invalid_p == 0 */
|
/* Skip verify of vbit_in_delta, invalid_p == 0 */
|
||||||
/* Skip verify of skip_permanent_breakpoint, has predicate */
|
/* Skip verify of skip_permanent_breakpoint, has predicate */
|
||||||
|
/* Skip verify of max_insn_length, has predicate */
|
||||||
|
/* Skip verify of displaced_step_copy_insn, has predicate */
|
||||||
|
/* Skip verify of displaced_step_fixup, has predicate */
|
||||||
|
if ((! gdbarch->displaced_step_free_closure) != (! gdbarch->displaced_step_copy_insn))
|
||||||
|
fprintf_unfiltered (log, "\n\tdisplaced_step_free_closure");
|
||||||
|
if ((! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn))
|
||||||
|
fprintf_unfiltered (log, "\n\tdisplaced_step_location");
|
||||||
/* Skip verify of overlay_update, has predicate */
|
/* Skip verify of overlay_update, has predicate */
|
||||||
/* Skip verify of core_read_description, has predicate */
|
/* Skip verify of core_read_description, has predicate */
|
||||||
/* Skip verify of static_transform_name, has predicate */
|
/* Skip verify of static_transform_name, has predicate */
|
||||||
@ -716,6 +736,24 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
|||||||
fprintf_unfiltered (file,
|
fprintf_unfiltered (file,
|
||||||
"gdbarch_dump: deprecated_function_start_offset = 0x%s\n",
|
"gdbarch_dump: deprecated_function_start_offset = 0x%s\n",
|
||||||
paddr_nz (gdbarch->deprecated_function_start_offset));
|
paddr_nz (gdbarch->deprecated_function_start_offset));
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: gdbarch_displaced_step_copy_insn_p() = %d\n",
|
||||||
|
gdbarch_displaced_step_copy_insn_p (gdbarch));
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: displaced_step_copy_insn = <0x%lx>\n",
|
||||||
|
(long) gdbarch->displaced_step_copy_insn);
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: gdbarch_displaced_step_fixup_p() = %d\n",
|
||||||
|
gdbarch_displaced_step_fixup_p (gdbarch));
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: displaced_step_fixup = <0x%lx>\n",
|
||||||
|
(long) gdbarch->displaced_step_fixup);
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: displaced_step_free_closure = <0x%lx>\n",
|
||||||
|
(long) gdbarch->displaced_step_free_closure);
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: displaced_step_location = <0x%lx>\n",
|
||||||
|
(long) gdbarch->displaced_step_location);
|
||||||
fprintf_unfiltered (file,
|
fprintf_unfiltered (file,
|
||||||
"gdbarch_dump: double_bit = %s\n",
|
"gdbarch_dump: double_bit = %s\n",
|
||||||
paddr_d (gdbarch->double_bit));
|
paddr_d (gdbarch->double_bit));
|
||||||
@ -818,6 +856,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
|||||||
fprintf_unfiltered (file,
|
fprintf_unfiltered (file,
|
||||||
"gdbarch_dump: long_long_bit = %s\n",
|
"gdbarch_dump: long_long_bit = %s\n",
|
||||||
paddr_d (gdbarch->long_long_bit));
|
paddr_d (gdbarch->long_long_bit));
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: gdbarch_max_insn_length_p() = %d\n",
|
||||||
|
gdbarch_max_insn_length_p (gdbarch));
|
||||||
|
fprintf_unfiltered (file,
|
||||||
|
"gdbarch_dump: max_insn_length = %s\n",
|
||||||
|
paddr_d (gdbarch->max_insn_length));
|
||||||
fprintf_unfiltered (file,
|
fprintf_unfiltered (file,
|
||||||
"gdbarch_dump: memory_insert_breakpoint = <0x%lx>\n",
|
"gdbarch_dump: memory_insert_breakpoint = <0x%lx>\n",
|
||||||
(long) gdbarch->memory_insert_breakpoint);
|
(long) gdbarch->memory_insert_breakpoint);
|
||||||
@ -2906,6 +2950,114 @@ set_gdbarch_skip_permanent_breakpoint (struct gdbarch *gdbarch,
|
|||||||
gdbarch->skip_permanent_breakpoint = skip_permanent_breakpoint;
|
gdbarch->skip_permanent_breakpoint = skip_permanent_breakpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
gdbarch_max_insn_length_p (struct gdbarch *gdbarch)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
return gdbarch->max_insn_length != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONGEST
|
||||||
|
gdbarch_max_insn_length (struct gdbarch *gdbarch)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
/* Check variable changed from pre-default. */
|
||||||
|
gdb_assert (gdbarch->max_insn_length != 0);
|
||||||
|
if (gdbarch_debug >= 2)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "gdbarch_max_insn_length called\n");
|
||||||
|
return gdbarch->max_insn_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_gdbarch_max_insn_length (struct gdbarch *gdbarch,
|
||||||
|
ULONGEST max_insn_length)
|
||||||
|
{
|
||||||
|
gdbarch->max_insn_length = max_insn_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
gdbarch_displaced_step_copy_insn_p (struct gdbarch *gdbarch)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
return gdbarch->displaced_step_copy_insn != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct displaced_step_closure *
|
||||||
|
gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
gdb_assert (gdbarch->displaced_step_copy_insn != NULL);
|
||||||
|
if (gdbarch_debug >= 2)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_copy_insn called\n");
|
||||||
|
return gdbarch->displaced_step_copy_insn (gdbarch, from, to, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch,
|
||||||
|
gdbarch_displaced_step_copy_insn_ftype displaced_step_copy_insn)
|
||||||
|
{
|
||||||
|
gdbarch->displaced_step_copy_insn = displaced_step_copy_insn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
return gdbarch->displaced_step_fixup != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
gdb_assert (gdbarch->displaced_step_fixup != NULL);
|
||||||
|
/* Do not check predicate: gdbarch->displaced_step_fixup != NULL, allow call. */
|
||||||
|
if (gdbarch_debug >= 2)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_fixup called\n");
|
||||||
|
gdbarch->displaced_step_fixup (gdbarch, closure, from, to, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch,
|
||||||
|
gdbarch_displaced_step_fixup_ftype displaced_step_fixup)
|
||||||
|
{
|
||||||
|
gdbarch->displaced_step_fixup = displaced_step_fixup;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch, struct displaced_step_closure *closure)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
gdb_assert (gdbarch->displaced_step_free_closure != NULL);
|
||||||
|
if (gdbarch_debug >= 2)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_free_closure called\n");
|
||||||
|
gdbarch->displaced_step_free_closure (gdbarch, closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch,
|
||||||
|
gdbarch_displaced_step_free_closure_ftype displaced_step_free_closure)
|
||||||
|
{
|
||||||
|
gdbarch->displaced_step_free_closure = displaced_step_free_closure;
|
||||||
|
}
|
||||||
|
|
||||||
|
CORE_ADDR
|
||||||
|
gdbarch_displaced_step_location (struct gdbarch *gdbarch)
|
||||||
|
{
|
||||||
|
gdb_assert (gdbarch != NULL);
|
||||||
|
gdb_assert (gdbarch->displaced_step_location != NULL);
|
||||||
|
if (gdbarch_debug >= 2)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_location called\n");
|
||||||
|
return gdbarch->displaced_step_location (gdbarch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_gdbarch_displaced_step_location (struct gdbarch *gdbarch,
|
||||||
|
gdbarch_displaced_step_location_ftype displaced_step_location)
|
||||||
|
{
|
||||||
|
gdbarch->displaced_step_location = displaced_step_location;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
gdbarch_overlay_update_p (struct gdbarch *gdbarch)
|
gdbarch_overlay_update_p (struct gdbarch *gdbarch)
|
||||||
{
|
{
|
||||||
|
@ -50,6 +50,7 @@ struct target_ops;
|
|||||||
struct obstack;
|
struct obstack;
|
||||||
struct bp_target_info;
|
struct bp_target_info;
|
||||||
struct target_desc;
|
struct target_desc;
|
||||||
|
struct displaced_step_closure;
|
||||||
|
|
||||||
extern struct gdbarch *current_gdbarch;
|
extern struct gdbarch *current_gdbarch;
|
||||||
|
|
||||||
@ -663,6 +664,95 @@ typedef void (gdbarch_skip_permanent_breakpoint_ftype) (struct regcache *regcach
|
|||||||
extern void gdbarch_skip_permanent_breakpoint (struct gdbarch *gdbarch, struct regcache *regcache);
|
extern void gdbarch_skip_permanent_breakpoint (struct gdbarch *gdbarch, struct regcache *regcache);
|
||||||
extern void set_gdbarch_skip_permanent_breakpoint (struct gdbarch *gdbarch, gdbarch_skip_permanent_breakpoint_ftype *skip_permanent_breakpoint);
|
extern void set_gdbarch_skip_permanent_breakpoint (struct gdbarch *gdbarch, gdbarch_skip_permanent_breakpoint_ftype *skip_permanent_breakpoint);
|
||||||
|
|
||||||
|
/* The maximum length of an instruction on this architecture. */
|
||||||
|
|
||||||
|
extern int gdbarch_max_insn_length_p (struct gdbarch *gdbarch);
|
||||||
|
|
||||||
|
extern ULONGEST gdbarch_max_insn_length (struct gdbarch *gdbarch);
|
||||||
|
extern void set_gdbarch_max_insn_length (struct gdbarch *gdbarch, ULONGEST max_insn_length);
|
||||||
|
|
||||||
|
/* Copy the instruction at FROM to TO, and make any adjustments
|
||||||
|
necessary to single-step it at that address.
|
||||||
|
|
||||||
|
REGS holds the state the thread's registers will have before
|
||||||
|
executing the copied instruction; the PC in REGS will refer to FROM,
|
||||||
|
not the copy at TO. The caller should update it to point at TO later.
|
||||||
|
|
||||||
|
Return a pointer to data of the architecture's choice to be passed
|
||||||
|
to gdbarch_displaced_step_fixup. Or, return NULL to indicate that
|
||||||
|
the instruction's effects have been completely simulated, with the
|
||||||
|
resulting state written back to REGS.
|
||||||
|
|
||||||
|
For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
see the comments in infrun.c.
|
||||||
|
|
||||||
|
The TO area is only guaranteed to have space for
|
||||||
|
gdbarch_max_insn_length (arch) bytes, so this function must not
|
||||||
|
write more bytes than that to that area.
|
||||||
|
|
||||||
|
If you do not provide this function, GDB assumes that the
|
||||||
|
architecture does not support displaced stepping.
|
||||||
|
|
||||||
|
If your architecture doesn't need to adjust instructions before
|
||||||
|
single-stepping them, consider using simple_displaced_step_copy_insn
|
||||||
|
here. */
|
||||||
|
|
||||||
|
extern int gdbarch_displaced_step_copy_insn_p (struct gdbarch *gdbarch);
|
||||||
|
|
||||||
|
typedef struct displaced_step_closure * (gdbarch_displaced_step_copy_insn_ftype) (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
|
||||||
|
extern struct displaced_step_closure * gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
|
||||||
|
extern void set_gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch, gdbarch_displaced_step_copy_insn_ftype *displaced_step_copy_insn);
|
||||||
|
|
||||||
|
/* Fix up the state resulting from successfully single-stepping a
|
||||||
|
displaced instruction, to give the result we would have gotten from
|
||||||
|
stepping the instruction in its original location.
|
||||||
|
|
||||||
|
REGS is the register state resulting from single-stepping the
|
||||||
|
displaced instruction.
|
||||||
|
|
||||||
|
CLOSURE is the result from the matching call to
|
||||||
|
gdbarch_displaced_step_copy_insn.
|
||||||
|
|
||||||
|
If you provide gdbarch_displaced_step_copy_insn.but not this
|
||||||
|
function, then GDB assumes that no fixup is needed after
|
||||||
|
single-stepping the instruction.
|
||||||
|
|
||||||
|
For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
see the comments in infrun.c. */
|
||||||
|
|
||||||
|
extern int gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch);
|
||||||
|
|
||||||
|
typedef void (gdbarch_displaced_step_fixup_ftype) (struct gdbarch *gdbarch, struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
|
||||||
|
extern void gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
|
||||||
|
extern void set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, gdbarch_displaced_step_fixup_ftype *displaced_step_fixup);
|
||||||
|
|
||||||
|
/* Free a closure returned by gdbarch_displaced_step_copy_insn.
|
||||||
|
|
||||||
|
If you provide gdbarch_displaced_step_copy_insn, you must provide
|
||||||
|
this function as well.
|
||||||
|
|
||||||
|
If your architecture uses closures that don't need to be freed, then
|
||||||
|
you can use simple_displaced_step_free_closure here.
|
||||||
|
|
||||||
|
For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
see the comments in infrun.c. */
|
||||||
|
|
||||||
|
typedef void (gdbarch_displaced_step_free_closure_ftype) (struct gdbarch *gdbarch, struct displaced_step_closure *closure);
|
||||||
|
extern void gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch, struct displaced_step_closure *closure);
|
||||||
|
extern void set_gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch, gdbarch_displaced_step_free_closure_ftype *displaced_step_free_closure);
|
||||||
|
|
||||||
|
/* Return the address of an appropriate place to put displaced
|
||||||
|
instructions while we step over them. There need only be one such
|
||||||
|
place, since we're only stepping one thread over a breakpoint at a
|
||||||
|
time.
|
||||||
|
|
||||||
|
For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
see the comments in infrun.c. */
|
||||||
|
|
||||||
|
typedef CORE_ADDR (gdbarch_displaced_step_location_ftype) (struct gdbarch *gdbarch);
|
||||||
|
extern CORE_ADDR gdbarch_displaced_step_location (struct gdbarch *gdbarch);
|
||||||
|
extern void set_gdbarch_displaced_step_location (struct gdbarch *gdbarch, gdbarch_displaced_step_location_ftype *displaced_step_location);
|
||||||
|
|
||||||
/* Refresh overlay mapped state for section OSECT. */
|
/* Refresh overlay mapped state for section OSECT. */
|
||||||
|
|
||||||
extern int gdbarch_overlay_update_p (struct gdbarch *gdbarch);
|
extern int gdbarch_overlay_update_p (struct gdbarch *gdbarch);
|
||||||
|
@ -616,6 +616,75 @@ v:int:vbit_in_delta:::0:0::0
|
|||||||
# Advance PC to next instruction in order to skip a permanent breakpoint.
|
# Advance PC to next instruction in order to skip a permanent breakpoint.
|
||||||
F:void:skip_permanent_breakpoint:struct regcache *regcache:regcache
|
F:void:skip_permanent_breakpoint:struct regcache *regcache:regcache
|
||||||
|
|
||||||
|
# The maximum length of an instruction on this architecture.
|
||||||
|
V:ULONGEST:max_insn_length:::0:0
|
||||||
|
|
||||||
|
# Copy the instruction at FROM to TO, and make any adjustments
|
||||||
|
# necessary to single-step it at that address.
|
||||||
|
#
|
||||||
|
# REGS holds the state the thread's registers will have before
|
||||||
|
# executing the copied instruction; the PC in REGS will refer to FROM,
|
||||||
|
# not the copy at TO. The caller should update it to point at TO later.
|
||||||
|
#
|
||||||
|
# Return a pointer to data of the architecture's choice to be passed
|
||||||
|
# to gdbarch_displaced_step_fixup. Or, return NULL to indicate that
|
||||||
|
# the instruction's effects have been completely simulated, with the
|
||||||
|
# resulting state written back to REGS.
|
||||||
|
#
|
||||||
|
# For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
# see the comments in infrun.c.
|
||||||
|
#
|
||||||
|
# The TO area is only guaranteed to have space for
|
||||||
|
# gdbarch_max_insn_length (arch) bytes, so this function must not
|
||||||
|
# write more bytes than that to that area.
|
||||||
|
#
|
||||||
|
# If you do not provide this function, GDB assumes that the
|
||||||
|
# architecture does not support displaced stepping.
|
||||||
|
#
|
||||||
|
# If your architecture doesn't need to adjust instructions before
|
||||||
|
# single-stepping them, consider using simple_displaced_step_copy_insn
|
||||||
|
# here.
|
||||||
|
M:struct displaced_step_closure *:displaced_step_copy_insn:CORE_ADDR from, CORE_ADDR to, struct regcache *regs:from, to, regs
|
||||||
|
|
||||||
|
# Fix up the state resulting from successfully single-stepping a
|
||||||
|
# displaced instruction, to give the result we would have gotten from
|
||||||
|
# stepping the instruction in its original location.
|
||||||
|
#
|
||||||
|
# REGS is the register state resulting from single-stepping the
|
||||||
|
# displaced instruction.
|
||||||
|
#
|
||||||
|
# CLOSURE is the result from the matching call to
|
||||||
|
# gdbarch_displaced_step_copy_insn.
|
||||||
|
#
|
||||||
|
# If you provide gdbarch_displaced_step_copy_insn.but not this
|
||||||
|
# function, then GDB assumes that no fixup is needed after
|
||||||
|
# single-stepping the instruction.
|
||||||
|
#
|
||||||
|
# For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
# see the comments in infrun.c.
|
||||||
|
M:void:displaced_step_fixup:struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs:closure, from, to, regs::NULL
|
||||||
|
|
||||||
|
# Free a closure returned by gdbarch_displaced_step_copy_insn.
|
||||||
|
#
|
||||||
|
# If you provide gdbarch_displaced_step_copy_insn, you must provide
|
||||||
|
# this function as well.
|
||||||
|
#
|
||||||
|
# If your architecture uses closures that don't need to be freed, then
|
||||||
|
# you can use simple_displaced_step_free_closure here.
|
||||||
|
#
|
||||||
|
# For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
# see the comments in infrun.c.
|
||||||
|
m:void:displaced_step_free_closure:struct displaced_step_closure *closure:closure::NULL::(! gdbarch->displaced_step_free_closure) != (! gdbarch->displaced_step_copy_insn)
|
||||||
|
|
||||||
|
# Return the address of an appropriate place to put displaced
|
||||||
|
# instructions while we step over them. There need only be one such
|
||||||
|
# place, since we're only stepping one thread over a breakpoint at a
|
||||||
|
# time.
|
||||||
|
#
|
||||||
|
# For a general explanation of displaced stepping and how GDB uses it,
|
||||||
|
# see the comments in infrun.c.
|
||||||
|
m:CORE_ADDR:displaced_step_location:void:::NULL::(! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn)
|
||||||
|
|
||||||
# Refresh overlay mapped state for section OSECT.
|
# Refresh overlay mapped state for section OSECT.
|
||||||
F:void:overlay_update:struct obj_section *osect:osect
|
F:void:overlay_update:struct obj_section *osect:osect
|
||||||
|
|
||||||
@ -742,6 +811,7 @@ struct target_ops;
|
|||||||
struct obstack;
|
struct obstack;
|
||||||
struct bp_target_info;
|
struct bp_target_info;
|
||||||
struct target_desc;
|
struct target_desc;
|
||||||
|
struct displaced_step_closure;
|
||||||
|
|
||||||
extern struct gdbarch *current_gdbarch;
|
extern struct gdbarch *current_gdbarch;
|
||||||
EOF
|
EOF
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "glibc-tdep.h"
|
#include "glibc-tdep.h"
|
||||||
#include "solib-svr4.h"
|
#include "solib-svr4.h"
|
||||||
#include "symtab.h"
|
#include "symtab.h"
|
||||||
|
#include "arch-utils.h"
|
||||||
|
|
||||||
/* Return the name of register REG. */
|
/* Return the name of register REG. */
|
||||||
|
|
||||||
@ -446,6 +447,15 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
|||||||
/* Enable TLS support. */
|
/* Enable TLS support. */
|
||||||
set_gdbarch_fetch_tls_load_module_address (gdbarch,
|
set_gdbarch_fetch_tls_load_module_address (gdbarch,
|
||||||
svr4_fetch_objfile_link_map);
|
svr4_fetch_objfile_link_map);
|
||||||
|
|
||||||
|
/* Displaced stepping. */
|
||||||
|
set_gdbarch_displaced_step_copy_insn (gdbarch,
|
||||||
|
simple_displaced_step_copy_insn);
|
||||||
|
set_gdbarch_displaced_step_fixup (gdbarch, i386_displaced_step_fixup);
|
||||||
|
set_gdbarch_displaced_step_free_closure (gdbarch,
|
||||||
|
simple_displaced_step_free_closure);
|
||||||
|
set_gdbarch_displaced_step_location (gdbarch,
|
||||||
|
displaced_step_at_entry_point);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Provide a prototype to silence -Wmissing-prototypes. */
|
/* Provide a prototype to silence -Wmissing-prototypes. */
|
||||||
|
230
gdb/i386-tdep.c
230
gdb/i386-tdep.c
@ -275,6 +275,225 @@ i386_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pc, int *len)
|
|||||||
*len = sizeof (break_insn);
|
*len = sizeof (break_insn);
|
||||||
return break_insn;
|
return break_insn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Displaced instruction handling. */
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
i386_absolute_jmp_p (gdb_byte *insn)
|
||||||
|
{
|
||||||
|
/* jmp far (absolute address in operand) */
|
||||||
|
if (insn[0] == 0xea)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (insn[0] == 0xff)
|
||||||
|
{
|
||||||
|
/* jump near, absolute indirect (/4) */
|
||||||
|
if ((insn[1] & 0x38) == 0x20)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* jump far, absolute indirect (/5) */
|
||||||
|
if ((insn[1] & 0x38) == 0x28)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
i386_absolute_call_p (gdb_byte *insn)
|
||||||
|
{
|
||||||
|
/* call far, absolute */
|
||||||
|
if (insn[0] == 0x9a)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (insn[0] == 0xff)
|
||||||
|
{
|
||||||
|
/* Call near, absolute indirect (/2) */
|
||||||
|
if ((insn[1] & 0x38) == 0x10)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Call far, absolute indirect (/3) */
|
||||||
|
if ((insn[1] & 0x38) == 0x18)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
i386_ret_p (gdb_byte *insn)
|
||||||
|
{
|
||||||
|
switch (insn[0])
|
||||||
|
{
|
||||||
|
case 0xc2: /* ret near, pop N bytes */
|
||||||
|
case 0xc3: /* ret near */
|
||||||
|
case 0xca: /* ret far, pop N bytes */
|
||||||
|
case 0xcb: /* ret far */
|
||||||
|
case 0xcf: /* iret */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
i386_call_p (gdb_byte *insn)
|
||||||
|
{
|
||||||
|
if (i386_absolute_call_p (insn))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* call near, relative */
|
||||||
|
if (insn[0] == 0xe8)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
i386_breakpoint_p (gdb_byte *insn)
|
||||||
|
{
|
||||||
|
return insn[0] == 0xcc; /* int 3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return non-zero if INSN is a system call, and set *LENGTHP to its
|
||||||
|
length in bytes. Otherwise, return zero. */
|
||||||
|
static int
|
||||||
|
i386_syscall_p (gdb_byte *insn, ULONGEST *lengthp)
|
||||||
|
{
|
||||||
|
if (insn[0] == 0xcd)
|
||||||
|
{
|
||||||
|
*lengthp = 2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix up the state of registers and memory after having single-stepped
|
||||||
|
a displaced instruction. */
|
||||||
|
void
|
||||||
|
i386_displaced_step_fixup (struct gdbarch *gdbarch,
|
||||||
|
struct displaced_step_closure *closure,
|
||||||
|
CORE_ADDR from, CORE_ADDR to,
|
||||||
|
struct regcache *regs)
|
||||||
|
{
|
||||||
|
/* The offset we applied to the instruction's address.
|
||||||
|
This could well be negative (when viewed as a signed 32-bit
|
||||||
|
value), but ULONGEST won't reflect that, so take care when
|
||||||
|
applying it. */
|
||||||
|
ULONGEST insn_offset = to - from;
|
||||||
|
|
||||||
|
/* Since we use simple_displaced_step_copy_insn, our closure is a
|
||||||
|
copy of the instruction. */
|
||||||
|
gdb_byte *insn = (gdb_byte *) closure;
|
||||||
|
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: fixup (0x%s, 0x%s), "
|
||||||
|
"insn = 0x%02x 0x%02x ...\n",
|
||||||
|
paddr_nz (from), paddr_nz (to), insn[0], insn[1]);
|
||||||
|
|
||||||
|
/* The list of issues to contend with here is taken from
|
||||||
|
resume_execution in arch/i386/kernel/kprobes.c, Linux 2.6.20.
|
||||||
|
Yay for Free Software! */
|
||||||
|
|
||||||
|
/* Relocate the %eip, if necessary. */
|
||||||
|
|
||||||
|
/* Except in the case of absolute or indirect jump or call
|
||||||
|
instructions, or a return instruction, the new eip is relative to
|
||||||
|
the displaced instruction; make it relative. Well, signal
|
||||||
|
handler returns don't need relocation either, but we use the
|
||||||
|
value of %eip to recognize those; see below. */
|
||||||
|
if (! i386_absolute_jmp_p (insn)
|
||||||
|
&& ! i386_absolute_call_p (insn)
|
||||||
|
&& ! i386_ret_p (insn))
|
||||||
|
{
|
||||||
|
ULONGEST orig_eip;
|
||||||
|
ULONGEST insn_len;
|
||||||
|
|
||||||
|
regcache_cooked_read_unsigned (regs, I386_EIP_REGNUM, &orig_eip);
|
||||||
|
|
||||||
|
/* A signal trampoline system call changes the %eip, resuming
|
||||||
|
execution of the main program after the signal handler has
|
||||||
|
returned. That makes them like 'return' instructions; we
|
||||||
|
shouldn't relocate %eip.
|
||||||
|
|
||||||
|
But most system calls don't, and we do need to relocate %eip.
|
||||||
|
|
||||||
|
Our heuristic for distinguishing these cases: if stepping
|
||||||
|
over the system call instruction left control directly after
|
||||||
|
the instruction, the we relocate --- control almost certainly
|
||||||
|
doesn't belong in the displaced copy. Otherwise, we assume
|
||||||
|
the instruction has put control where it belongs, and leave
|
||||||
|
it unrelocated. Goodness help us if there are PC-relative
|
||||||
|
system calls. */
|
||||||
|
if (i386_syscall_p (insn, &insn_len)
|
||||||
|
&& orig_eip != to + insn_len)
|
||||||
|
{
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: syscall changed %%eip; "
|
||||||
|
"not relocating\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ULONGEST eip = (orig_eip - insn_offset) & 0xffffffffUL;
|
||||||
|
|
||||||
|
/* If we have stepped over a breakpoint, set the %eip to
|
||||||
|
point at the breakpoint instruction itself.
|
||||||
|
|
||||||
|
(gdbarch_decr_pc_after_break was never something the core
|
||||||
|
of GDB should have been concerned with; arch-specific
|
||||||
|
code should be making PC values consistent before
|
||||||
|
presenting them to GDB.) */
|
||||||
|
if (i386_breakpoint_p (insn))
|
||||||
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: stepped breakpoint\n");
|
||||||
|
eip--;
|
||||||
|
}
|
||||||
|
|
||||||
|
regcache_cooked_write_unsigned (regs, I386_EIP_REGNUM, eip);
|
||||||
|
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: "
|
||||||
|
"relocated %%eip from 0x%s to 0x%s\n",
|
||||||
|
paddr_nz (orig_eip), paddr_nz (eip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the instruction was PUSHFL, then the TF bit will be set in the
|
||||||
|
pushed value, and should be cleared. We'll leave this for later,
|
||||||
|
since GDB already messes up the TF flag when stepping over a
|
||||||
|
pushfl. */
|
||||||
|
|
||||||
|
/* If the instruction was a call, the return address now atop the
|
||||||
|
stack is the address following the copied instruction. We need
|
||||||
|
to make it the address following the original instruction. */
|
||||||
|
if (i386_call_p (insn))
|
||||||
|
{
|
||||||
|
ULONGEST esp;
|
||||||
|
ULONGEST retaddr;
|
||||||
|
const ULONGEST retaddr_len = 4;
|
||||||
|
|
||||||
|
regcache_cooked_read_unsigned (regs, I386_ESP_REGNUM, &esp);
|
||||||
|
retaddr = read_memory_unsigned_integer (esp, retaddr_len);
|
||||||
|
retaddr = (retaddr - insn_offset) & 0xffffffffUL;
|
||||||
|
write_memory_unsigned_integer (esp, retaddr_len, retaddr);
|
||||||
|
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: relocated return addr at 0x%s "
|
||||||
|
"to 0x%s\n",
|
||||||
|
paddr_nz (esp),
|
||||||
|
paddr_nz (retaddr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef I386_REGNO_TO_SYMMETRY
|
#ifdef I386_REGNO_TO_SYMMETRY
|
||||||
#error "The Sequent Symmetry is no longer supported."
|
#error "The Sequent Symmetry is no longer supported."
|
||||||
@ -521,14 +740,14 @@ i386_analyze_stack_align (CORE_ADDR pc, CORE_ADDR current_pc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Maximum instruction length we need to handle. */
|
/* Maximum instruction length we need to handle. */
|
||||||
#define I386_MAX_INSN_LEN 6
|
#define I386_MAX_MATCHED_INSN_LEN 6
|
||||||
|
|
||||||
/* Instruction description. */
|
/* Instruction description. */
|
||||||
struct i386_insn
|
struct i386_insn
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
gdb_byte insn[I386_MAX_INSN_LEN];
|
gdb_byte insn[I386_MAX_MATCHED_INSN_LEN];
|
||||||
gdb_byte mask[I386_MAX_INSN_LEN];
|
gdb_byte mask[I386_MAX_MATCHED_INSN_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Search for the instruction at PC in the list SKIP_INSNS. Return
|
/* Search for the instruction at PC in the list SKIP_INSNS. Return
|
||||||
@ -547,12 +766,12 @@ i386_match_insn (CORE_ADDR pc, struct i386_insn *skip_insns)
|
|||||||
{
|
{
|
||||||
if ((op & insn->mask[0]) == insn->insn[0])
|
if ((op & insn->mask[0]) == insn->insn[0])
|
||||||
{
|
{
|
||||||
gdb_byte buf[I386_MAX_INSN_LEN - 1];
|
gdb_byte buf[I386_MAX_MATCHED_INSN_LEN - 1];
|
||||||
int insn_matched = 1;
|
int insn_matched = 1;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
gdb_assert (insn->len > 1);
|
gdb_assert (insn->len > 1);
|
||||||
gdb_assert (insn->len <= I386_MAX_INSN_LEN);
|
gdb_assert (insn->len <= I386_MAX_MATCHED_INSN_LEN);
|
||||||
|
|
||||||
target_read_memory (pc + 1, buf, insn->len - 1);
|
target_read_memory (pc + 1, buf, insn->len - 1);
|
||||||
for (i = 1; i < insn->len; i++)
|
for (i = 1; i < insn->len; i++)
|
||||||
@ -2375,6 +2594,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
|||||||
|
|
||||||
set_gdbarch_breakpoint_from_pc (gdbarch, i386_breakpoint_from_pc);
|
set_gdbarch_breakpoint_from_pc (gdbarch, i386_breakpoint_from_pc);
|
||||||
set_gdbarch_decr_pc_after_break (gdbarch, 1);
|
set_gdbarch_decr_pc_after_break (gdbarch, 1);
|
||||||
|
set_gdbarch_max_insn_length (gdbarch, I386_MAX_INSN_LEN);
|
||||||
|
|
||||||
set_gdbarch_frame_args_skip (gdbarch, 8);
|
set_gdbarch_frame_args_skip (gdbarch, 8);
|
||||||
|
|
||||||
|
@ -164,6 +164,10 @@ extern struct type *i386_sse_type (struct gdbarch *gdbarch);
|
|||||||
#define I386_SEL_UPL 0x0003 /* User Privilige Level. */
|
#define I386_SEL_UPL 0x0003 /* User Privilige Level. */
|
||||||
#define I386_SEL_KPL 0x0000 /* Kernel Privilige Level. */
|
#define I386_SEL_KPL 0x0000 /* Kernel Privilige Level. */
|
||||||
|
|
||||||
|
/* The length of the longest i386 instruction (according to
|
||||||
|
include/asm-i386/kprobes.h in Linux 2.6. */
|
||||||
|
#define I386_MAX_INSN_LEN (16)
|
||||||
|
|
||||||
/* Functions exported from i386-tdep.c. */
|
/* Functions exported from i386-tdep.c. */
|
||||||
extern CORE_ADDR i386_pe_skip_trampoline_code (CORE_ADDR pc, char *name);
|
extern CORE_ADDR i386_pe_skip_trampoline_code (CORE_ADDR pc, char *name);
|
||||||
|
|
||||||
@ -195,6 +199,12 @@ extern const struct regset *
|
|||||||
i386_regset_from_core_section (struct gdbarch *gdbarch,
|
i386_regset_from_core_section (struct gdbarch *gdbarch,
|
||||||
const char *sect_name, size_t sect_size);
|
const char *sect_name, size_t sect_size);
|
||||||
|
|
||||||
|
|
||||||
|
extern void i386_displaced_step_fixup (struct gdbarch *gdbarch,
|
||||||
|
struct displaced_step_closure *closure,
|
||||||
|
CORE_ADDR from, CORE_ADDR to,
|
||||||
|
struct regcache *regs);
|
||||||
|
|
||||||
/* Initialize a basic ELF architecture variant. */
|
/* Initialize a basic ELF architecture variant. */
|
||||||
extern void i386_elf_init_abi (struct gdbarch_info, struct gdbarch *);
|
extern void i386_elf_init_abi (struct gdbarch_info, struct gdbarch *);
|
||||||
|
|
||||||
|
@ -387,6 +387,14 @@ extern struct regcache *stop_registers;
|
|||||||
than forked. */
|
than forked. */
|
||||||
|
|
||||||
extern int attach_flag;
|
extern int attach_flag;
|
||||||
|
|
||||||
|
/* True if we are debugging displaced stepping. */
|
||||||
|
extern int debug_displaced;
|
||||||
|
|
||||||
|
/* Dump LEN bytes at BUF in hex to FILE, followed by a newline. */
|
||||||
|
void displaced_step_dump_bytes (struct ui_file *file,
|
||||||
|
const gdb_byte *buf, size_t len);
|
||||||
|
|
||||||
|
|
||||||
/* Possible values for gdbarch_call_dummy_location. */
|
/* Possible values for gdbarch_call_dummy_location. */
|
||||||
#define ON_STACK 1
|
#define ON_STACK 1
|
||||||
|
526
gdb/infrun.c
526
gdb/infrun.c
@ -103,6 +103,14 @@ int sync_execution = 0;
|
|||||||
|
|
||||||
static ptid_t previous_inferior_ptid;
|
static ptid_t previous_inferior_ptid;
|
||||||
|
|
||||||
|
int debug_displaced = 0;
|
||||||
|
static void
|
||||||
|
show_debug_displaced (struct ui_file *file, int from_tty,
|
||||||
|
struct cmd_list_element *c, const char *value)
|
||||||
|
{
|
||||||
|
fprintf_filtered (file, _("Displace stepping debugging is %s.\n"), value);
|
||||||
|
}
|
||||||
|
|
||||||
static int debug_infrun = 0;
|
static int debug_infrun = 0;
|
||||||
static void
|
static void
|
||||||
show_debug_infrun (struct ui_file *file, int from_tty,
|
show_debug_infrun (struct ui_file *file, int from_tty,
|
||||||
@ -459,6 +467,377 @@ static int stepping_past_singlestep_breakpoint;
|
|||||||
stepping the thread user has selected. */
|
stepping the thread user has selected. */
|
||||||
static ptid_t deferred_step_ptid;
|
static ptid_t deferred_step_ptid;
|
||||||
|
|
||||||
|
/* Displaced stepping. */
|
||||||
|
|
||||||
|
/* In non-stop debugging mode, we must take special care to manage
|
||||||
|
breakpoints properly; in particular, the traditional strategy for
|
||||||
|
stepping a thread past a breakpoint it has hit is unsuitable.
|
||||||
|
'Displaced stepping' is a tactic for stepping one thread past a
|
||||||
|
breakpoint it has hit while ensuring that other threads running
|
||||||
|
concurrently will hit the breakpoint as they should.
|
||||||
|
|
||||||
|
The traditional way to step a thread T off a breakpoint in a
|
||||||
|
multi-threaded program in all-stop mode is as follows:
|
||||||
|
|
||||||
|
a0) Initially, all threads are stopped, and breakpoints are not
|
||||||
|
inserted.
|
||||||
|
a1) We single-step T, leaving breakpoints uninserted.
|
||||||
|
a2) We insert breakpoints, and resume all threads.
|
||||||
|
|
||||||
|
In non-stop debugging, however, this strategy is unsuitable: we
|
||||||
|
don't want to have to stop all threads in the system in order to
|
||||||
|
continue or step T past a breakpoint. Instead, we use displaced
|
||||||
|
stepping:
|
||||||
|
|
||||||
|
n0) Initially, T is stopped, other threads are running, and
|
||||||
|
breakpoints are inserted.
|
||||||
|
n1) We copy the instruction "under" the breakpoint to a separate
|
||||||
|
location, outside the main code stream, making any adjustments
|
||||||
|
to the instruction, register, and memory state as directed by
|
||||||
|
T's architecture.
|
||||||
|
n2) We single-step T over the instruction at its new location.
|
||||||
|
n3) We adjust the resulting register and memory state as directed
|
||||||
|
by T's architecture. This includes resetting T's PC to point
|
||||||
|
back into the main instruction stream.
|
||||||
|
n4) We resume T.
|
||||||
|
|
||||||
|
This approach depends on the following gdbarch methods:
|
||||||
|
|
||||||
|
- gdbarch_max_insn_length and gdbarch_displaced_step_location
|
||||||
|
indicate where to copy the instruction, and how much space must
|
||||||
|
be reserved there. We use these in step n1.
|
||||||
|
|
||||||
|
- gdbarch_displaced_step_copy_insn copies a instruction to a new
|
||||||
|
address, and makes any necessary adjustments to the instruction,
|
||||||
|
register contents, and memory. We use this in step n1.
|
||||||
|
|
||||||
|
- gdbarch_displaced_step_fixup adjusts registers and memory after
|
||||||
|
we have successfuly single-stepped the instruction, to yield the
|
||||||
|
same effect the instruction would have had if we had executed it
|
||||||
|
at its original address. We use this in step n3.
|
||||||
|
|
||||||
|
- gdbarch_displaced_step_free_closure provides cleanup.
|
||||||
|
|
||||||
|
The gdbarch_displaced_step_copy_insn and
|
||||||
|
gdbarch_displaced_step_fixup functions must be written so that
|
||||||
|
copying an instruction with gdbarch_displaced_step_copy_insn,
|
||||||
|
single-stepping across the copied instruction, and then applying
|
||||||
|
gdbarch_displaced_insn_fixup should have the same effects on the
|
||||||
|
thread's memory and registers as stepping the instruction in place
|
||||||
|
would have. Exactly which responsibilities fall to the copy and
|
||||||
|
which fall to the fixup is up to the author of those functions.
|
||||||
|
|
||||||
|
See the comments in gdbarch.sh for details.
|
||||||
|
|
||||||
|
Note that displaced stepping and software single-step cannot
|
||||||
|
currently be used in combination, although with some care I think
|
||||||
|
they could be made to. Software single-step works by placing
|
||||||
|
breakpoints on all possible subsequent instructions; if the
|
||||||
|
displaced instruction is a PC-relative jump, those breakpoints
|
||||||
|
could fall in very strange places --- on pages that aren't
|
||||||
|
executable, or at addresses that are not proper instruction
|
||||||
|
boundaries. (We do generally let other threads run while we wait
|
||||||
|
to hit the software single-step breakpoint, and they might
|
||||||
|
encounter such a corrupted instruction.) One way to work around
|
||||||
|
this would be to have gdbarch_displaced_step_copy_insn fully
|
||||||
|
simulate the effect of PC-relative instructions (and return NULL)
|
||||||
|
on architectures that use software single-stepping.
|
||||||
|
|
||||||
|
In non-stop mode, we can have independent and simultaneous step
|
||||||
|
requests, so more than one thread may need to simultaneously step
|
||||||
|
over a breakpoint. The current implementation assumes there is
|
||||||
|
only one scratch space per process. In this case, we have to
|
||||||
|
serialize access to the scratch space. If thread A wants to step
|
||||||
|
over a breakpoint, but we are currently waiting for some other
|
||||||
|
thread to complete a displaced step, we leave thread A stopped and
|
||||||
|
place it in the displaced_step_request_queue. Whenever a displaced
|
||||||
|
step finishes, we pick the next thread in the queue and start a new
|
||||||
|
displaced step operation on it. See displaced_step_prepare and
|
||||||
|
displaced_step_fixup for details. */
|
||||||
|
|
||||||
|
/* If this is not null_ptid, this is the thread carrying out a
|
||||||
|
displaced single-step. This thread's state will require fixing up
|
||||||
|
once it has completed its step. */
|
||||||
|
static ptid_t displaced_step_ptid;
|
||||||
|
|
||||||
|
struct displaced_step_request
|
||||||
|
{
|
||||||
|
ptid_t ptid;
|
||||||
|
struct displaced_step_request *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A queue of pending displaced stepping requests. */
|
||||||
|
struct displaced_step_request *displaced_step_request_queue;
|
||||||
|
|
||||||
|
/* The architecture the thread had when we stepped it. */
|
||||||
|
static struct gdbarch *displaced_step_gdbarch;
|
||||||
|
|
||||||
|
/* The closure provided gdbarch_displaced_step_copy_insn, to be used
|
||||||
|
for post-step cleanup. */
|
||||||
|
static struct displaced_step_closure *displaced_step_closure;
|
||||||
|
|
||||||
|
/* The address of the original instruction, and the copy we made. */
|
||||||
|
static CORE_ADDR displaced_step_original, displaced_step_copy;
|
||||||
|
|
||||||
|
/* Saved contents of copy area. */
|
||||||
|
static gdb_byte *displaced_step_saved_copy;
|
||||||
|
|
||||||
|
/* When this is non-zero, we are allowed to use displaced stepping, if
|
||||||
|
the architecture supports it. When this is zero, we use
|
||||||
|
traditional the hold-and-step approach. */
|
||||||
|
int can_use_displaced_stepping = 1;
|
||||||
|
static void
|
||||||
|
show_can_use_displaced_stepping (struct ui_file *file, int from_tty,
|
||||||
|
struct cmd_list_element *c,
|
||||||
|
const char *value)
|
||||||
|
{
|
||||||
|
fprintf_filtered (file, _("\
|
||||||
|
Debugger's willingness to use displaced stepping to step over "
|
||||||
|
"breakpoints is %s.\n"), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return non-zero if displaced stepping is enabled, and can be used
|
||||||
|
with GDBARCH. */
|
||||||
|
static int
|
||||||
|
use_displaced_stepping (struct gdbarch *gdbarch)
|
||||||
|
{
|
||||||
|
return (can_use_displaced_stepping
|
||||||
|
&& gdbarch_displaced_step_copy_insn_p (gdbarch));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean out any stray displaced stepping state. */
|
||||||
|
static void
|
||||||
|
displaced_step_clear (void)
|
||||||
|
{
|
||||||
|
/* Indicate that there is no cleanup pending. */
|
||||||
|
displaced_step_ptid = null_ptid;
|
||||||
|
|
||||||
|
if (displaced_step_closure)
|
||||||
|
{
|
||||||
|
gdbarch_displaced_step_free_closure (displaced_step_gdbarch,
|
||||||
|
displaced_step_closure);
|
||||||
|
displaced_step_closure = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_displaced_step_closure (void *ptr)
|
||||||
|
{
|
||||||
|
struct displaced_step_closure *closure = ptr;
|
||||||
|
|
||||||
|
gdbarch_displaced_step_free_closure (current_gdbarch, closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump LEN bytes at BUF in hex to FILE, followed by a newline. */
|
||||||
|
void
|
||||||
|
displaced_step_dump_bytes (struct ui_file *file,
|
||||||
|
const gdb_byte *buf,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
fprintf_unfiltered (file, "%02x ", buf[i]);
|
||||||
|
fputs_unfiltered ("\n", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare to single-step, using displaced stepping.
|
||||||
|
|
||||||
|
Note that we cannot use displaced stepping when we have a signal to
|
||||||
|
deliver. If we have a signal to deliver and an instruction to step
|
||||||
|
over, then after the step, there will be no indication from the
|
||||||
|
target whether the thread entered a signal handler or ignored the
|
||||||
|
signal and stepped over the instruction successfully --- both cases
|
||||||
|
result in a simple SIGTRAP. In the first case we mustn't do a
|
||||||
|
fixup, and in the second case we must --- but we can't tell which.
|
||||||
|
Comments in the code for 'random signals' in handle_inferior_event
|
||||||
|
explain how we handle this case instead.
|
||||||
|
|
||||||
|
Returns 1 if preparing was successful -- this thread is going to be
|
||||||
|
stepped now; or 0 if displaced stepping this thread got queued. */
|
||||||
|
static int
|
||||||
|
displaced_step_prepare (ptid_t ptid)
|
||||||
|
{
|
||||||
|
struct cleanup *old_cleanups;
|
||||||
|
struct regcache *regcache = get_thread_regcache (ptid);
|
||||||
|
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||||
|
CORE_ADDR original, copy;
|
||||||
|
ULONGEST len;
|
||||||
|
struct displaced_step_closure *closure;
|
||||||
|
|
||||||
|
/* We should never reach this function if the architecture does not
|
||||||
|
support displaced stepping. */
|
||||||
|
gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
|
||||||
|
|
||||||
|
/* For the first cut, we're displaced stepping one thread at a
|
||||||
|
time. */
|
||||||
|
|
||||||
|
if (!ptid_equal (displaced_step_ptid, null_ptid))
|
||||||
|
{
|
||||||
|
/* Already waiting for a displaced step to finish. Defer this
|
||||||
|
request and place in queue. */
|
||||||
|
struct displaced_step_request *req, *new_req;
|
||||||
|
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: defering step of %s\n",
|
||||||
|
target_pid_to_str (ptid));
|
||||||
|
|
||||||
|
new_req = xmalloc (sizeof (*new_req));
|
||||||
|
new_req->ptid = ptid;
|
||||||
|
new_req->next = NULL;
|
||||||
|
|
||||||
|
if (displaced_step_request_queue)
|
||||||
|
{
|
||||||
|
for (req = displaced_step_request_queue;
|
||||||
|
req && req->next;
|
||||||
|
req = req->next)
|
||||||
|
;
|
||||||
|
req->next = new_req;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
displaced_step_request_queue = new_req;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: stepping %s now\n",
|
||||||
|
target_pid_to_str (ptid));
|
||||||
|
}
|
||||||
|
|
||||||
|
displaced_step_clear ();
|
||||||
|
|
||||||
|
original = read_pc_pid (ptid);
|
||||||
|
|
||||||
|
copy = gdbarch_displaced_step_location (gdbarch);
|
||||||
|
len = gdbarch_max_insn_length (gdbarch);
|
||||||
|
|
||||||
|
/* Save the original contents of the copy area. */
|
||||||
|
displaced_step_saved_copy = xmalloc (len);
|
||||||
|
old_cleanups = make_cleanup (free_current_contents,
|
||||||
|
&displaced_step_saved_copy);
|
||||||
|
read_memory (copy, displaced_step_saved_copy, len);
|
||||||
|
if (debug_displaced)
|
||||||
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "displaced: saved 0x%s: ",
|
||||||
|
paddr_nz (copy));
|
||||||
|
displaced_step_dump_bytes (gdb_stdlog, displaced_step_saved_copy, len);
|
||||||
|
};
|
||||||
|
|
||||||
|
closure = gdbarch_displaced_step_copy_insn (gdbarch,
|
||||||
|
original, copy, regcache);
|
||||||
|
|
||||||
|
/* We don't support the fully-simulated case at present. */
|
||||||
|
gdb_assert (closure);
|
||||||
|
|
||||||
|
make_cleanup (cleanup_displaced_step_closure, closure);
|
||||||
|
|
||||||
|
/* Resume execution at the copy. */
|
||||||
|
write_pc_pid (copy, ptid);
|
||||||
|
|
||||||
|
discard_cleanups (old_cleanups);
|
||||||
|
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "displaced: displaced pc to 0x%s\n",
|
||||||
|
paddr_nz (copy));
|
||||||
|
|
||||||
|
/* Save the information we need to fix things up if the step
|
||||||
|
succeeds. */
|
||||||
|
displaced_step_ptid = ptid;
|
||||||
|
displaced_step_gdbarch = gdbarch;
|
||||||
|
displaced_step_closure = closure;
|
||||||
|
displaced_step_original = original;
|
||||||
|
displaced_step_copy = copy;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
displaced_step_clear_cleanup (void *ignore)
|
||||||
|
{
|
||||||
|
displaced_step_clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||||
|
{
|
||||||
|
struct cleanup *ptid_cleanup = save_inferior_ptid ();
|
||||||
|
inferior_ptid = ptid;
|
||||||
|
write_memory (memaddr, myaddr, len);
|
||||||
|
do_cleanups (ptid_cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
|
||||||
|
{
|
||||||
|
struct cleanup *old_cleanups;
|
||||||
|
|
||||||
|
/* Was this event for the pid we displaced? */
|
||||||
|
if (ptid_equal (displaced_step_ptid, null_ptid)
|
||||||
|
|| ! ptid_equal (displaced_step_ptid, event_ptid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
old_cleanups = make_cleanup (displaced_step_clear_cleanup, 0);
|
||||||
|
|
||||||
|
/* Restore the contents of the copy area. */
|
||||||
|
{
|
||||||
|
ULONGEST len = gdbarch_max_insn_length (displaced_step_gdbarch);
|
||||||
|
write_memory_ptid (displaced_step_ptid, displaced_step_copy,
|
||||||
|
displaced_step_saved_copy, len);
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "displaced: restored 0x%s\n",
|
||||||
|
paddr_nz (displaced_step_copy));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Did the instruction complete successfully? */
|
||||||
|
if (signal == TARGET_SIGNAL_TRAP)
|
||||||
|
{
|
||||||
|
/* Fix up the resulting state. */
|
||||||
|
gdbarch_displaced_step_fixup (displaced_step_gdbarch,
|
||||||
|
displaced_step_closure,
|
||||||
|
displaced_step_original,
|
||||||
|
displaced_step_copy,
|
||||||
|
get_thread_regcache (displaced_step_ptid));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Since the instruction didn't complete, all we can do is
|
||||||
|
relocate the PC. */
|
||||||
|
CORE_ADDR pc = read_pc_pid (event_ptid);
|
||||||
|
pc = displaced_step_original + (pc - displaced_step_copy);
|
||||||
|
write_pc_pid (pc, event_ptid);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_cleanups (old_cleanups);
|
||||||
|
|
||||||
|
/* Are there any pending displaced stepping requests? If so, run
|
||||||
|
one now. */
|
||||||
|
if (displaced_step_request_queue)
|
||||||
|
{
|
||||||
|
struct displaced_step_request *head;
|
||||||
|
ptid_t ptid;
|
||||||
|
|
||||||
|
head = displaced_step_request_queue;
|
||||||
|
ptid = head->ptid;
|
||||||
|
displaced_step_request_queue = head->next;
|
||||||
|
xfree (head);
|
||||||
|
|
||||||
|
if (debug_displaced)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"displaced: stepping queued %s now\n",
|
||||||
|
target_pid_to_str (ptid));
|
||||||
|
|
||||||
|
|
||||||
|
displaced_step_ptid = null_ptid;
|
||||||
|
displaced_step_prepare (ptid);
|
||||||
|
target_resume (ptid, 1, TARGET_SIGNAL_0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Resuming. */
|
||||||
|
|
||||||
/* Things to clean up if we QUIT out of resume (). */
|
/* Things to clean up if we QUIT out of resume (). */
|
||||||
static void
|
static void
|
||||||
@ -510,14 +889,14 @@ resume (int step, enum target_signal sig)
|
|||||||
{
|
{
|
||||||
int should_resume = 1;
|
int should_resume = 1;
|
||||||
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
|
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
|
||||||
|
CORE_ADDR pc = read_pc ();
|
||||||
QUIT;
|
QUIT;
|
||||||
|
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: resume (step=%d, signal=%d)\n",
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
step, sig);
|
"infrun: resume (step=%d, signal=%d), "
|
||||||
|
"stepping_over_breakpoint=%d\n",
|
||||||
/* FIXME: calling breakpoint_here_p (read_pc ()) three times! */
|
step, sig, stepping_over_breakpoint);
|
||||||
|
|
||||||
|
|
||||||
/* Some targets (e.g. Solaris x86) have a kernel bug when stepping
|
/* Some targets (e.g. Solaris x86) have a kernel bug when stepping
|
||||||
over an instruction that causes a page fault without triggering
|
over an instruction that causes a page fault without triggering
|
||||||
@ -535,7 +914,7 @@ resume (int step, enum target_signal sig)
|
|||||||
removed or inserted, as appropriate. The exception is if we're sitting
|
removed or inserted, as appropriate. The exception is if we're sitting
|
||||||
at a permanent breakpoint; we need to step over it, but permanent
|
at a permanent breakpoint; we need to step over it, but permanent
|
||||||
breakpoints can't be removed. So we have to test for it here. */
|
breakpoints can't be removed. So we have to test for it here. */
|
||||||
if (breakpoint_here_p (read_pc ()) == permanent_breakpoint_here)
|
if (breakpoint_here_p (pc) == permanent_breakpoint_here)
|
||||||
{
|
{
|
||||||
if (gdbarch_skip_permanent_breakpoint_p (current_gdbarch))
|
if (gdbarch_skip_permanent_breakpoint_p (current_gdbarch))
|
||||||
gdbarch_skip_permanent_breakpoint (current_gdbarch,
|
gdbarch_skip_permanent_breakpoint (current_gdbarch,
|
||||||
@ -547,6 +926,24 @@ how to step past a permanent breakpoint on this architecture. Try using\n\
|
|||||||
a command like `return' or `jump' to continue execution."));
|
a command like `return' or `jump' to continue execution."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If enabled, step over breakpoints by executing a copy of the
|
||||||
|
instruction at a different address.
|
||||||
|
|
||||||
|
We can't use displaced stepping when we have a signal to deliver;
|
||||||
|
the comments for displaced_step_prepare explain why. The
|
||||||
|
comments in the handle_inferior event for dealing with 'random
|
||||||
|
signals' explain what we do instead. */
|
||||||
|
if (use_displaced_stepping (current_gdbarch)
|
||||||
|
&& stepping_over_breakpoint
|
||||||
|
&& sig == TARGET_SIGNAL_0)
|
||||||
|
{
|
||||||
|
if (!displaced_step_prepare (inferior_ptid))
|
||||||
|
/* Got placed in displaced stepping queue. Will be resumed
|
||||||
|
later when all the currently queued displaced stepping
|
||||||
|
requests finish. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (step && gdbarch_software_single_step_p (current_gdbarch))
|
if (step && gdbarch_software_single_step_p (current_gdbarch))
|
||||||
{
|
{
|
||||||
/* Do it the hard way, w/temp breakpoints */
|
/* Do it the hard way, w/temp breakpoints */
|
||||||
@ -558,7 +955,7 @@ a command like `return' or `jump' to continue execution."));
|
|||||||
`wait_for_inferior' */
|
`wait_for_inferior' */
|
||||||
singlestep_breakpoints_inserted_p = 1;
|
singlestep_breakpoints_inserted_p = 1;
|
||||||
singlestep_ptid = inferior_ptid;
|
singlestep_ptid = inferior_ptid;
|
||||||
singlestep_pc = read_pc ();
|
singlestep_pc = pc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,15 +1039,30 @@ a command like `return' or `jump' to continue execution."));
|
|||||||
/* Most targets can step a breakpoint instruction, thus
|
/* Most targets can step a breakpoint instruction, thus
|
||||||
executing it normally. But if this one cannot, just
|
executing it normally. But if this one cannot, just
|
||||||
continue and we will hit it anyway. */
|
continue and we will hit it anyway. */
|
||||||
if (step && breakpoint_inserted_here_p (read_pc ()))
|
if (step && breakpoint_inserted_here_p (pc))
|
||||||
step = 0;
|
step = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (debug_displaced
|
||||||
|
&& use_displaced_stepping (current_gdbarch)
|
||||||
|
&& stepping_over_breakpoint)
|
||||||
|
{
|
||||||
|
CORE_ADDR actual_pc = read_pc_pid (resume_ptid);
|
||||||
|
gdb_byte buf[4];
|
||||||
|
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "displaced: run 0x%s: ",
|
||||||
|
paddr_nz (actual_pc));
|
||||||
|
read_memory (actual_pc, buf, sizeof (buf));
|
||||||
|
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
|
||||||
|
}
|
||||||
|
|
||||||
target_resume (resume_ptid, step, sig);
|
target_resume (resume_ptid, step, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
discard_cleanups (old_cleanups);
|
discard_cleanups (old_cleanups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Proceeding. */
|
||||||
|
|
||||||
/* Clear out all variables saying what to do when inferior is continued.
|
/* Clear out all variables saying what to do when inferior is continued.
|
||||||
First do this, then set the ones you want, then call `proceed'. */
|
First do this, then set the ones you want, then call `proceed'. */
|
||||||
@ -787,17 +1199,20 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
|||||||
|
|
||||||
if (oneproc)
|
if (oneproc)
|
||||||
{
|
{
|
||||||
/* We will get a trace trap after one instruction.
|
|
||||||
Continue it automatically and insert breakpoints then. */
|
|
||||||
stepping_over_breakpoint = 1;
|
stepping_over_breakpoint = 1;
|
||||||
/* FIXME: if breakpoints are always inserted, we'll trap
|
/* If displaced stepping is enabled, we can step over the
|
||||||
if trying to single-step over breakpoint. Disable
|
breakpoint without hitting it, so leave all breakpoints
|
||||||
all breakpoints. In future, we'd need to invent some
|
inserted. Otherwise we need to disable all breakpoints, step
|
||||||
smart way of stepping over breakpoint instruction without
|
one instruction, and then re-add them when that step is
|
||||||
hitting breakpoint. */
|
finished. */
|
||||||
|
if (!use_displaced_stepping (current_gdbarch))
|
||||||
remove_breakpoints ();
|
remove_breakpoints ();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
/* We can insert breakpoints if we're not trying to step over one,
|
||||||
|
or if we are stepping over one but we're using displaced stepping
|
||||||
|
to do so. */
|
||||||
|
if (! stepping_over_breakpoint || use_displaced_stepping (current_gdbarch))
|
||||||
insert_breakpoints ();
|
insert_breakpoints ();
|
||||||
|
|
||||||
if (siggnal != TARGET_SIGNAL_DEFAULT)
|
if (siggnal != TARGET_SIGNAL_DEFAULT)
|
||||||
@ -908,7 +1323,10 @@ init_wait_for_inferior (void)
|
|||||||
deferred_step_ptid = null_ptid;
|
deferred_step_ptid = null_ptid;
|
||||||
|
|
||||||
target_last_wait_ptid = minus_one_ptid;
|
target_last_wait_ptid = minus_one_ptid;
|
||||||
|
|
||||||
|
displaced_step_clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This enum encodes possible reasons for doing a target_wait, so that
|
/* This enum encodes possible reasons for doing a target_wait, so that
|
||||||
wfi can call target_wait in one place. (Ultimately the call will be
|
wfi can call target_wait in one place. (Ultimately the call will be
|
||||||
@ -1580,10 +1998,31 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Do we need to clean up the state of a thread that has completed a
|
||||||
|
displaced single-step? (Doing so usually affects the PC, so do
|
||||||
|
it here, before we set stop_pc.) */
|
||||||
|
displaced_step_fixup (ecs->ptid, stop_signal);
|
||||||
|
|
||||||
stop_pc = read_pc_pid (ecs->ptid);
|
stop_pc = read_pc_pid (ecs->ptid);
|
||||||
|
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n", paddr_nz (stop_pc));
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n",
|
||||||
|
paddr_nz (stop_pc));
|
||||||
|
if (STOPPED_BY_WATCHPOINT (&ecs->ws))
|
||||||
|
{
|
||||||
|
CORE_ADDR addr;
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "infrun: stopped by watchpoint\n");
|
||||||
|
|
||||||
|
if (target_stopped_data_address (¤t_target, &addr))
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"infrun: stopped data address = 0x%s\n",
|
||||||
|
paddr_nz (addr));
|
||||||
|
else
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"infrun: (no data address available)\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (stepping_past_singlestep_breakpoint)
|
if (stepping_past_singlestep_breakpoint)
|
||||||
{
|
{
|
||||||
@ -1731,7 +2170,7 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
|
|
||||||
if (thread_hop_needed)
|
if (thread_hop_needed)
|
||||||
{
|
{
|
||||||
int remove_status;
|
int remove_status = 0;
|
||||||
|
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: thread_hop_needed\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: thread_hop_needed\n");
|
||||||
@ -1746,7 +2185,11 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
singlestep_breakpoints_inserted_p = 0;
|
singlestep_breakpoints_inserted_p = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the arch can displace step, don't remove the
|
||||||
|
breakpoints. */
|
||||||
|
if (!use_displaced_stepping (current_gdbarch))
|
||||||
remove_status = remove_breakpoints ();
|
remove_status = remove_breakpoints ();
|
||||||
|
|
||||||
/* Did we fail to remove breakpoints? If so, try
|
/* Did we fail to remove breakpoints? If so, try
|
||||||
to set the PC past the bp. (There's at least
|
to set the PC past the bp. (There's at least
|
||||||
one situation in which we can fail to remove
|
one situation in which we can fail to remove
|
||||||
@ -1810,9 +2253,6 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
&& (HAVE_STEPPABLE_WATCHPOINT
|
&& (HAVE_STEPPABLE_WATCHPOINT
|
||||||
|| gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
|
|| gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
|
||||||
{
|
{
|
||||||
if (debug_infrun)
|
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n");
|
|
||||||
|
|
||||||
/* At this point, we are stopped at an instruction which has
|
/* At this point, we are stopped at an instruction which has
|
||||||
attempted to write to a piece of memory under control of
|
attempted to write to a piece of memory under control of
|
||||||
a watchpoint. The instruction hasn't actually executed
|
a watchpoint. The instruction hasn't actually executed
|
||||||
@ -1915,10 +2355,14 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
when we're trying to execute a breakpoint instruction on a
|
when we're trying to execute a breakpoint instruction on a
|
||||||
non-executable stack. This happens for call dummy breakpoints
|
non-executable stack. This happens for call dummy breakpoints
|
||||||
for architectures like SPARC that place call dummies on the
|
for architectures like SPARC that place call dummies on the
|
||||||
stack. */
|
stack.
|
||||||
|
|
||||||
|
If we're doing a displaced step past a breakpoint, then the
|
||||||
|
breakpoint is always inserted at the original instruction;
|
||||||
|
non-standard signals can't be explained by the breakpoint. */
|
||||||
if (stop_signal == TARGET_SIGNAL_TRAP
|
if (stop_signal == TARGET_SIGNAL_TRAP
|
||||||
|| (breakpoint_inserted_here_p (stop_pc)
|
|| (! stepping_over_breakpoint
|
||||||
|
&& breakpoint_inserted_here_p (stop_pc)
|
||||||
&& (stop_signal == TARGET_SIGNAL_ILL
|
&& (stop_signal == TARGET_SIGNAL_ILL
|
||||||
|| stop_signal == TARGET_SIGNAL_SEGV
|
|| stop_signal == TARGET_SIGNAL_SEGV
|
||||||
|| stop_signal == TARGET_SIGNAL_EMT))
|
|| stop_signal == TARGET_SIGNAL_EMT))
|
||||||
@ -2045,7 +2489,7 @@ process_event_stop_test:
|
|||||||
{
|
{
|
||||||
/* We were just starting a new sequence, attempting to
|
/* We were just starting a new sequence, attempting to
|
||||||
single-step off of a breakpoint and expecting a SIGTRAP.
|
single-step off of a breakpoint and expecting a SIGTRAP.
|
||||||
Intead this signal arrives. This signal will take us out
|
Instead this signal arrives. This signal will take us out
|
||||||
of the stepping range so GDB needs to remember to, when
|
of the stepping range so GDB needs to remember to, when
|
||||||
the signal handler returns, resume stepping off that
|
the signal handler returns, resume stepping off that
|
||||||
breakpoint. */
|
breakpoint. */
|
||||||
@ -2053,6 +2497,10 @@ process_event_stop_test:
|
|||||||
code paths as single-step - set a breakpoint at the
|
code paths as single-step - set a breakpoint at the
|
||||||
signal return address and then, once hit, step off that
|
signal return address and then, once hit, step off that
|
||||||
breakpoint. */
|
breakpoint. */
|
||||||
|
if (debug_infrun)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"infrun: signal arrived while stepping over "
|
||||||
|
"breakpoint\n");
|
||||||
|
|
||||||
insert_step_resume_breakpoint_at_frame (get_current_frame ());
|
insert_step_resume_breakpoint_at_frame (get_current_frame ());
|
||||||
ecs->step_after_step_resume_breakpoint = 1;
|
ecs->step_after_step_resume_breakpoint = 1;
|
||||||
@ -2076,6 +2524,11 @@ process_event_stop_test:
|
|||||||
Note that this is only needed for a signal delivered
|
Note that this is only needed for a signal delivered
|
||||||
while in the single-step range. Nested signals aren't a
|
while in the single-step range. Nested signals aren't a
|
||||||
problem as they eventually all return. */
|
problem as they eventually all return. */
|
||||||
|
if (debug_infrun)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"infrun: signal may take us out of "
|
||||||
|
"single-step range\n");
|
||||||
|
|
||||||
insert_step_resume_breakpoint_at_frame (get_current_frame ());
|
insert_step_resume_breakpoint_at_frame (get_current_frame ());
|
||||||
keep_going (ecs);
|
keep_going (ecs);
|
||||||
return;
|
return;
|
||||||
@ -2905,6 +3358,10 @@ keep_going (struct execution_control_state *ecs)
|
|||||||
|
|
||||||
if (ecs->stepping_over_breakpoint)
|
if (ecs->stepping_over_breakpoint)
|
||||||
{
|
{
|
||||||
|
if (! use_displaced_stepping (current_gdbarch))
|
||||||
|
/* Since we can't do a displaced step, we have to remove
|
||||||
|
the breakpoint while we step it. To keep things
|
||||||
|
simple, we remove them all. */
|
||||||
remove_breakpoints ();
|
remove_breakpoints ();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -4011,6 +4468,14 @@ When non-zero, inferior specific debugging is enabled."),
|
|||||||
show_debug_infrun,
|
show_debug_infrun,
|
||||||
&setdebuglist, &showdebuglist);
|
&setdebuglist, &showdebuglist);
|
||||||
|
|
||||||
|
add_setshow_boolean_cmd ("displaced", class_maintenance, &debug_displaced, _("\
|
||||||
|
Set displaced stepping debugging."), _("\
|
||||||
|
Show displaced stepping debugging."), _("\
|
||||||
|
When non-zero, displaced stepping specific debugging is enabled."),
|
||||||
|
NULL,
|
||||||
|
show_debug_displaced,
|
||||||
|
&setdebuglist, &showdebuglist);
|
||||||
|
|
||||||
numsigs = (int) TARGET_SIGNAL_LAST;
|
numsigs = (int) TARGET_SIGNAL_LAST;
|
||||||
signal_stop = (unsigned char *) xmalloc (sizeof (signal_stop[0]) * numsigs);
|
signal_stop = (unsigned char *) xmalloc (sizeof (signal_stop[0]) * numsigs);
|
||||||
signal_print = (unsigned char *)
|
signal_print = (unsigned char *)
|
||||||
@ -4106,9 +4571,22 @@ function is skipped and the step command stops at a different source line."),
|
|||||||
show_step_stop_if_no_debug,
|
show_step_stop_if_no_debug,
|
||||||
&setlist, &showlist);
|
&setlist, &showlist);
|
||||||
|
|
||||||
|
add_setshow_boolean_cmd ("can-use-displaced-stepping", class_maintenance,
|
||||||
|
&can_use_displaced_stepping, _("\
|
||||||
|
Set debugger's willingness to use displaced stepping."), _("\
|
||||||
|
Show debugger's willingness to use displaced stepping."), _("\
|
||||||
|
If zero, gdb will not use to use displaced stepping to step over\n\
|
||||||
|
breakpoints, even if such is supported by the target."),
|
||||||
|
NULL,
|
||||||
|
show_can_use_displaced_stepping,
|
||||||
|
&maintenance_set_cmdlist,
|
||||||
|
&maintenance_show_cmdlist);
|
||||||
|
|
||||||
|
|
||||||
/* ptid initializations */
|
/* ptid initializations */
|
||||||
null_ptid = ptid_build (0, 0, 0);
|
null_ptid = ptid_build (0, 0, 0);
|
||||||
minus_one_ptid = ptid_build (-1, 0, 0);
|
minus_one_ptid = ptid_build (-1, 0, 0);
|
||||||
inferior_ptid = null_ptid;
|
inferior_ptid = null_ptid;
|
||||||
target_last_wait_ptid = minus_one_ptid;
|
target_last_wait_ptid = minus_one_ptid;
|
||||||
|
displaced_step_ptid = null_ptid;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
2008-05-02 Jim Blandy <jimb@codesourcery.com>
|
||||||
|
|
||||||
|
* gdb.asm/asmsrc1.s: Add scratch space.
|
||||||
|
|
||||||
2007-05-01 Daniel Jacobowitz <dan@codesourcery.com>
|
2007-05-01 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* gdb.arch/thumb-prologue.exp: Do not expect a saved PC.
|
* gdb.arch/thumb-prologue.exp: Do not expect a saved PC.
|
||||||
|
@ -16,6 +16,18 @@
|
|||||||
gdbasm_exit0
|
gdbasm_exit0
|
||||||
gdbasm_end _start
|
gdbasm_end _start
|
||||||
|
|
||||||
|
comment "Displaced stepping requires scratch space at _start"
|
||||||
|
comment "at least as large as the largest instruction. No"
|
||||||
|
comment "breakpoints should be set within the scratch space."
|
||||||
|
gdbasm_several_nops
|
||||||
|
gdbasm_several_nops
|
||||||
|
gdbasm_several_nops
|
||||||
|
gdbasm_several_nops
|
||||||
|
gdbasm_several_nops
|
||||||
|
gdbasm_several_nops
|
||||||
|
gdbasm_several_nops
|
||||||
|
gdbasm_several_nops
|
||||||
|
|
||||||
comment "main routine for assembly source debugging test"
|
comment "main routine for assembly source debugging test"
|
||||||
comment "This particular testcase uses macros in <arch>.inc to achieve"
|
comment "This particular testcase uses macros in <arch>.inc to achieve"
|
||||||
comment "machine independence."
|
comment "machine independence."
|
||||||
|
Reference in New Issue
Block a user