Index: ppc-linux-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/ppc-linux-tdep.c,v retrieving revision 1.78 diff -a -u -r1.78 ppc-linux-tdep.c --- ppc-linux-tdep.c 18 Apr 2006 19:20:06 -0000 1.78 +++ ppc-linux-tdep.c 19 Jun 2006 21:40:01 -0000 @@ -927,6 +927,84 @@ trad_frame_set_id (this_cache, frame_id_build (base, func)); } +#define LWARX_MASK 0xfc0007fe +#define LWARX_INSTRUCTION 0x7C000028 +#define STWCX_MASK 0xfc0007ff +#define STWCX_INSTRUCTION 0x7c00012d +#define BC_MASK 0xfc000000 +#define BC_INSTRUCTION 0x40000000 +#define IMMEDIATE_PART(instr) (((instr & ~3) << 16) >> 16) +#define ABSOLUTE_P(instr) ((int) ((instr >> 1) & 1)) + +static int +ppc_atomic_single_step (enum target_signal sig, int insert_breakpoints_p) +{ + if (insert_breakpoints_p) + { + CORE_ADDR pc = read_pc (); + CORE_ADDR breaks[2] = {-1, -1}; + CORE_ADDR loc = pc; + long instr = read_memory_integer (loc, PPC_INSN_SIZE); + int last_break = 0; + int i; + + + /* Assume all atomic sequences start with an lwarx instruction. */ + if ((instr & LWARX_MASK) != LWARX_INSTRUCTION) + return 0; + + /* Assume that no atomic sequence is longer than 6 instructions. */ + for (i= 1; i < 5; ++i) + { + loc += PPC_INSN_SIZE; + instr = read_memory_integer (loc, PPC_INSN_SIZE); + + /* Assume at most one conditional branch instruction between + the lwarx and stwcx instructions.*/ + if ((instr & BC_MASK) == BC_INSTRUCTION) + { + last_break = 1; + breaks[1] = IMMEDIATE_PART (instr); + if ( ! ABSOLUTE_P(instr)) + breaks[1] += loc; + continue; + } + + if ((instr & STWCX_MASK) == STWCX_INSTRUCTION) + break; + } + + /* Assume that the atomic sequence ends with a stwcx instruction + followed by a conditional branch instruction. */ + if ((instr & STWCX_MASK) != STWCX_INSTRUCTION) + error (_("Tried to step over an atomic sequence of instructions but could not find the end of the sequence.")); + + loc += PPC_INSN_SIZE; + instr = read_memory_integer (loc, PPC_INSN_SIZE); + + if ((instr & BC_MASK) != BC_INSTRUCTION) + error (_("Tried to step over an atomic sequence of instructions but it did not end as expected.")); + + breaks[0] = loc; + + /* This should never happen, but make sure we don't but + two breakpoints on the same address. */ + if (last_break && breaks[1] == breaks[0]) + last_break = 0; + + for (i= 0; i < last_break; ++i) + insert_single_step_breakpoint (breaks[i]); + + printf_unfiltered (_("Stepping over an atomic sequence of instructions beginning at %s"), + core_addr_to_string (pc)); + gdb_flush (gdb_stdout); + } + else + remove_single_step_breakpoints (); + + return 1; +} + static void ppc32_linux_sigaction_cache_init (const struct tramp_frame *self, struct frame_info *next_frame, @@ -1080,6 +1158,11 @@ /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); + + /* Enable possibly_single_step_with_software in case someone tries to + sngle step a sequence of instructions that should be atomic. */ + set_gdbarch_possibly_single_step_with_software (gdbarch, + ppc_atomic_single_step); } void