20006-06-22 Paul Gilliam * ppc-linux-tdep.c (ppc_atomic_single_step): New function. (ppc_linux_init_abi): Set software_single_step member of the gdbarch vector to the new ppc_atomic_single_step function. Index: ppc-linux-tdep.c =================================================================== --- ppc-linux-tdep.c.orig +++ ppc-linux-tdep.c @@ -931,6 +931,84 @@ ppc_linux_sigtramp_cache (struct frame_i 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(insn) (((insn & ~3) << 16) >> 16) +#define ABSOLUTE_P(insn) ((int) ((insn >> 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; + int insn = read_insn (loc); + int last_break = 0; + int i; + + + /* Assume all atomic sequences start with an lwarx instruction. */ + if ((insn & 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; + insn = read_insn (loc); + + /* Assume at most one conditional branch instruction between + the lwarx and stwcx instructions.*/ + if ((insn & BC_MASK) == BC_INSTRUCTION) + { + last_break = 1; + breaks[1] = IMMEDIATE_PART (insn); + if ( ! ABSOLUTE_P(insn)) + breaks[1] += loc; + continue; + } + + if ((insn & STWCX_MASK) == STWCX_INSTRUCTION) + break; + } + + /* Assume that the atomic sequence ends with a stwcx instruction + followed by a conditional branch instruction. */ + if ((insn & 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; + insn = read_insn (loc); + + if ((insn & 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\n"), + 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, @@ -1084,6 +1162,10 @@ ppc_linux_init_abi (struct gdbarch_info /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); + + /* Enable software_single_step in case someone tries to sngle step a + sequence of instructions that should be atomic. */ + set_gdbarch_software_single_step (gdbarch, ppc_atomic_single_step); } void