diff --git a/contrib/gdb/gdb/infrun.c b/contrib/gdb/gdb/infrun.c index e84a4c7..ba1fcde 100644 --- a/contrib/gdb/gdb/infrun.c +++ b/contrib/gdb/gdb/infrun.c @@ -45,6 +45,9 @@ #include "observer.h" #include "language.h" #include "gdb_assert.h" +#include "gdbarch.h" + +extern struct gdbarch *current_gdbarch; /* Prototypes for local functions */ @@ -560,8 +563,17 @@ resume (int step, enum target_signal sig) if (breakpoint_here_p (read_pc ()) == permanent_breakpoint_here) SKIP_PERMANENT_BREAKPOINT (); - if (SOFTWARE_SINGLE_STEP_P () && step) - { + if (step) { +#ifdef __ppc__ + if (deal_with_atomic_sequence(0 ,0)) { + step = 0; + /* and do not pull these breakpoints until after a `wait' in + `wait_for_inferior' */ + singlestep_breakpoints_inserted_p = 1; + singlestep_ptid = inferior_ptid; + } else +#endif + if (SOFTWARE_SINGLE_STEP_P ()) { /* Do it the hard way, w/temp breakpoints */ SOFTWARE_SINGLE_STEP (sig, 1 /*insert-breakpoints */ ); /* ...and don't ask hardware to do it. */ @@ -571,6 +583,7 @@ resume (int step, enum target_signal sig) singlestep_breakpoints_inserted_p = 1; singlestep_ptid = inferior_ptid; } + } /* Handle any optimized stores to the inferior NOW... */ #ifdef DO_DEFERRED_STORES @@ -1770,6 +1783,9 @@ handle_inferior_event (struct execution_control_state *ecs) if (stop_signal == TARGET_SIGNAL_TRAP) { /* Pull the single step breakpoints out of the target. */ +#ifdef __ppc__ + clear_atomic_sequence_break(); +#endif SOFTWARE_SINGLE_STEP (0, 0); singlestep_breakpoints_inserted_p = 0; @@ -1831,6 +1847,9 @@ handle_inferior_event (struct execution_control_state *ecs) if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p) { /* Pull the single step breakpoints out of the target. */ +#ifdef __ppc__ + clear_atomic_sequence_break(); +#endif SOFTWARE_SINGLE_STEP (0, 0); singlestep_breakpoints_inserted_p = 0; } @@ -1950,10 +1969,15 @@ handle_inferior_event (struct execution_control_state *ecs) flush_cached_frames (); } - if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p) + if (singlestep_breakpoints_inserted_p) { /* Pull the single step breakpoints out of the target. */ - SOFTWARE_SINGLE_STEP (0, 0); +#ifdef __ppc__ + clear_atomic_sequence_break(); +#endif + if (SOFTWARE_SINGLE_STEP_P ()) + SOFTWARE_SINGLE_STEP (0, 0); singlestep_breakpoints_inserted_p = 0; } diff --git a/contrib/gdb/gdb/ppc-tdep.h b/contrib/gdb/gdb/ppc-tdep.h index 1c1c9ef..6fa76d9 100644 --- a/contrib/gdb/gdb/ppc-tdep.h +++ b/contrib/gdb/gdb/ppc-tdep.h @@ -110,4 +110,7 @@ struct gdbarch_tdep link register is saved. */ }; +/* Instruction size. */ +#define PPC_INSN_SIZE 4 + #endif diff --git a/contrib/gdb/gdb/rs6000-tdep.c b/contrib/gdb/gdb/rs6000-tdep.c index 5eef895..d9fe119 100644 --- a/contrib/gdb/gdb/rs6000-tdep.c +++ b/contrib/gdb/gdb/rs6000-tdep.c @@ -328,6 +328,118 @@ rs6000_breakpoint_from_pc (CORE_ADDR *bp_addr, int *bp_size) return little_breakpoint; } +/* Instruction masks used during single-stepping of atomic sequences. */ +#define LWARX_MASK 0xfc0007fe +#define LWARX_INSTRUCTION 0x7c000028 +#define LDARX_INSTRUCTION 0x7c0000A8 +#define STWCX_MASK 0xfc0007ff +#define STWCX_INSTRUCTION 0x7c00012d +#define STDCX_INSTRUCTION 0x7c0001ad +#define BC_MASK 0xfc000000 +#define BC_INSTRUCTION 0x40000000 + +/* Checks for an atomic sequence of instructions beginning with a LWARX/LDARX + instruction and ending with a STWCX/STDCX instruction. If such a sequence + is found, attempt to step through it. A breakpoint is placed at the end of + the sequence. */ + +int +deal_with_atomic_sequence (enum target_signal signal, + int insert_breakpoints_p) +{ + CORE_ADDR pc; + CORE_ADDR breaks[2] = {-1, -1}; + CORE_ADDR loc; + CORE_ADDR branch_bp; /* Breakpoint at branch instruction's destination. */ + CORE_ADDR closing_insn; /* Instruction that closes the atomic sequence. */ + int insn; + int insn_count; + int index; + int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ + const int atomic_sequence_length = 16; /* Instruction sequence length. */ + int opcode; /* Branch instruction's OPcode. */ + int bc_insn_count = 0; /* Conditional branch instruction count. */ + + loc = pc = read_pc (); + insn = read_memory_integer (loc, PPC_INSN_SIZE); + + /* Assume all atomic sequences start with a lwarx/ldarx instruction. */ + if ((insn & LWARX_MASK) != LWARX_INSTRUCTION + && (insn & LWARX_MASK) != LDARX_INSTRUCTION) + return 0; + + /* Assume that no atomic sequence is longer than "atomic_sequence_length" + instructions. */ + for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) + { + loc += PPC_INSN_SIZE; + insn = read_memory_integer (loc, PPC_INSN_SIZE); + + /* Assume that there is at most one conditional branch in the atomic + sequence. If a conditional branch is found, put a breakpoint in + its destination address. */ + if ((insn & BC_MASK) == BC_INSTRUCTION) + { + if (bc_insn_count >= 1) + return 0; /* More than one conditional branch found, fallback + to the standard single-step code. */ + + opcode = insn >> 26; + branch_bp = branch_dest (opcode, insn, pc, breaks[0]); + + if (branch_bp != -1) + { + breaks[1] = branch_bp; + bc_insn_count++; + last_breakpoint++; + } + } + + if ((insn & STWCX_MASK) == STWCX_INSTRUCTION + || (insn & STWCX_MASK) == STDCX_INSTRUCTION) + break; + } + + /* Assume that the atomic sequence ends with a stwcx/stdcx instruction. */ + if ((insn & STWCX_MASK) != STWCX_INSTRUCTION + && (insn & STWCX_MASK) != STDCX_INSTRUCTION) + return 0; + + closing_insn = loc; + loc += PPC_INSN_SIZE; + insn = read_memory_integer (loc, PPC_INSN_SIZE); + + /* Insert a breakpoint right after the end of the atomic sequence. */ + breaks[0] = loc; + + /* Check for duplicated breakpoints. Check also for a breakpoint + placed (branch instruction's destination) at the stwcx/stdcx + instruction, this resets the reservation and take us back to the + lwarx/ldarx instruction at the beginning of the atomic sequence. */ + if (last_breakpoint && ((breaks[1] == breaks[0]) + || (breaks[1] == closing_insn))) + last_breakpoint = 0; + + /* Effectively inserts the breakpoints. */ + for (index = 0; index <= last_breakpoint; index++) + { + target_insert_breakpoint (breaks[index], stepBreaks[index].data); + stepBreaks[index].address = breaks[index]; + } + + return 1; +} + +int +clear_atomic_sequence_break (void) +{ + int index; + + for (index = 0; index < 2; ++index) + if (stepBreaks[index].address != 0) + target_remove_breakpoint (stepBreaks[index].address, + stepBreaks[index].data); +} /* AIX does not support PT_STEP. Simulate it. */ @@ -349,6 +461,9 @@ rs6000_software_single_step (enum target_signal signal, loc = read_pc (); insn = read_memory_integer (loc, 4); + + if (deal_with_atomic_sequence (signal, insert_breakpoints_p)) + return; breaks[0] = loc + breakp_sz; opcode = insn >> 26;