Re: [patch] "single step" atomic instruction sequences as a whole.

Luis Machado
2007-02-28T16:09:32Z

[-- Attachment #1: Type: text/plain, Size: 2874 bytes --]

Hi,

I refreshed Paul's patches to apply cleanly on HEAD. I did a test run
and it still works as expected.

->
(gdb) start
(gdb) x/g &puts
0x400001dc1d8 <puts>: 0x00000400000e99b0
(gdb) x/10i 0x00000400000e99b0 + 80
0x400000e9a00 <._IO_puts+80>: beq- cr7,0x400000e9a3c <._IO_puts+140>
0x400000e9a04 <._IO_puts+84>: li r0,1
0x400000e9a08 <._IO_puts+88>: lwarx r11,0,r3
0x400000e9a0c <._IO_puts+92>: cmpw r11,r9
0x400000e9a10 <._IO_puts+96>: bne- 0x400000e9a1c <._IO_puts+108>
0x400000e9a14 <._IO_puts+100>: stwcx. r0,0,r3
0x400000e9a18 <._IO_puts+104>: bne+ 0x400000e9a08 <._IO_puts+88>
0x400000e9a1c <._IO_puts+108>: isync
0x400000e9a20 <._IO_puts+112>: cmpwi cr7,r11,0
0x400000e9a24 <._IO_puts+116>: bne- cr7,0x400000e9b84 <._IO_puts+468>
(gdb) b *0x400000e9a04
Breakpoint 2 at 0x400000e9a04
(gdb) c
Continuing.

Breakpoint 2, 0x00000400000e9a04 in ._IO_puts () from /lib64/power5/libc.so.6
(gdb) si
0x00000400000e9a08 in ._IO_puts () from /lib64/power5/libc.so.6
(gdb)
Stepping over an atomic sequence of instructions beginning at 0x00000400000e9a08
0x00000400000e9a18 in ._IO_puts () from /lib64/power5/libc.so.6
(gdb) x/5i 0x00000400000e9a08
0x400000e9a08 <._IO_puts+88>: lwarx r11,0,r3
0x400000e9a0c <._IO_puts+92>: cmpw r11,r9
0x400000e9a10 <._IO_puts+96>: bne- 0x400000e9a1c <._IO_puts+108>
0x400000e9a14 <._IO_puts+100>: stwcx. r0,0,r3
0x400000e9a18 <._IO_puts+104>: bne+ 0x400000e9a08 <._IO_puts+88>
(gdb)
<-

Also, i haven't been able to reproduce the issue related at this post:
(http://sourceware.org/ml/gdb-patches/2006-09/msg00060.html)

This is the test run on my power machine. No Internal errors at this
time.

->
(gdb) start
Breakpoint 1 at 0x10000674: file atomic.c, line 9.
Starting program: /home/luis/binaries/atomic64
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
warning: Breakpoint address adjusted from 0x10010b58 to 0x100004f0.
main (argc=1, argv=0xfffff8351e8 "") at atomic.c:9
9 printf("atomic_step_test\n");
(gdb) n
atomic_step_test
10 atomic_set(&i,5);
(gdb)
11 printf("i=%d\n",atomic_read(&i));
(gdb)
i=5
12 atomic_dec(&i);
(gdb)
13 printf("i=%d\n",atomic_read(&i));
(gdb)
i=4
16 }
(gdb)
0x00000400000a0c8c in .generic_start_main ()
from /lib64/power5/libc.so.6
(gdb)
Single stepping until exit from function .generic_start_main,
which has no line number information.
0x00000400000150f0 in ._dl_fini () from /lib64/ld64.so.1
(gdb)
Single stepping until exit from function ._dl_fini,
which has no line number information.
Stepping over an atomic sequence of instructions beginning at
0x00000400000fa69c
Stepping over an atomic sequence of instructions beginning at
0x00000400000fa820

Program exited with code 04.
(gdb)
<-

Regards,
Luis

[-- Attachment #2: change-software-single-step.diff --]
[-- Type: text/x-patch, Size: 16370 bytes --]

20006-06-22 Paul Gilliam <pgilliam@us.ibm.com>

* gdbarch.sh: Change the return type of software_single_step from
void to int and reformatted some comments to <= 80 columns.
* gdbarch.c, gdbarch.h: Regenerated.
* alpha-tdep.c (alpha_software_single_step): Change the return type
from void to int and always return 1.
* alpha-tdep.h: Change the return type of alpha_software_single_step
from void to int.
* arm-tdep.c (arm_software_single_step): Change the return type from
void to int and always return 1.
* cris-tdep.c (cris_software_single_step): Change the return type
from void to int and always return 1.
* mips-tdep.c (mips_software_single_step): Change the return type
from void to int and always return 1.
* mips-tdep.h: Change the return type of mips_software_single_step
from void to int.
* rs6000-tdep.c (rs6000_software_single_step): Change the return type
from void to int and always return 1.
*rs6000-tdep.h: Change the return type of rs6000_software_single_step
from void to int.
* sparc-tdep.c (sparc_software_single_step): Change the return type
from void to int and always return 1.
* sparc-tdep.h: Change the return type of sparc_software_single_step
from void to int.
* wince.c (wince_software_single_step {three times}): Change the
return type from void to int and always return 1.
infrun.c (resume): Check the return value from SOFTWARE_SINGLE_STEP
and act accordingly. True means that the software_single_step
breakpoints where inserted; false means they where not.

Index: alpha-tdep.c
===================================================================
--- alpha-tdep.c.orig
+++ alpha-tdep.c
@@ -1518,7 +1518,7 @@ alpha_next_pc (CORE_ADDR pc)
return (pc + ALPHA_INSN_SIZE);
}

-void
+int
alpha_software_single_step (enum target_signal sig, int insert_breakpoints_p)
{
static CORE_ADDR next_pc;
@@ -1536,6 +1536,7 @@ alpha_software_single_step (enum target_
remove_single_step_breakpoints ();
write_pc (next_pc);
}
+ return 1;
}

\f
Index: alpha-tdep.h
===================================================================
--- alpha-tdep.h.orig
+++ alpha-tdep.h
@@ -107,7 +107,7 @@ struct gdbarch_tdep
};

extern unsigned int alpha_read_insn (CORE_ADDR pc);
-extern void alpha_software_single_step (enum target_signal, int);
+extern int alpha_software_single_step (enum target_signal, int);
extern CORE_ADDR alpha_after_prologue (CORE_ADDR pc);

extern void alpha_mdebug_init_abi (struct gdbarch_info, struct gdbarch *);
Index: arm-tdep.c
===================================================================
--- arm-tdep.c.orig
+++ arm-tdep.c
@@ -1905,7 +1905,7 @@ arm_get_next_pc (CORE_ADDR pc)
single_step() is also called just after the inferior stops. If we
had set up a simulated single-step, we undo our damage. */

-static void
+static int
arm_software_single_step (enum target_signal sig, int insert_bpt)
{
/* NOTE: This may insert the wrong breakpoint instruction when
@@ -1920,6 +1920,8 @@ arm_software_single_step (enum target_si
}
else
remove_single_step_breakpoints ();
+
+ return 1
}

#include "bfd-in2.h"
Index: cris-tdep.c
===================================================================
--- cris-tdep.c.orig
+++ cris-tdep.c
@@ -2117,7 +2117,7 @@ find_step_target (inst_env_type *inst_en
digs through the opcodes in order to find all possible targets.
Either one ordinary target or two targets for branches may be found. */

-static void
+static int
cris_software_single_step (enum target_signal ignore, int insert_breakpoints)
{
inst_env_type inst_env;
@@ -2150,6 +2150,8 @@ cris_software_single_step (enum target_s
}
else
remove_single_step_breakpoints ();
+
+ return 1;
}

/* Calculates the prefix value for quick offset addressing mode. */
Index: gdbarch.c
===================================================================
--- gdbarch.c.orig
+++ gdbarch.c
@@ -3289,14 +3289,14 @@ gdbarch_software_single_step_p (struct g
return gdbarch->software_single_step != NULL;
}

-void
+int
gdbarch_software_single_step (struct gdbarch *gdbarch, enum target_signal sig, int insert_breakpoints_p)
{
gdb_assert (gdbarch != NULL);
gdb_assert (gdbarch->software_single_step != NULL);
if (gdbarch_debug >= 2)
fprintf_unfiltered (gdb_stdlog, "gdbarch_software_single_step called\n");
- gdbarch->software_single_step (sig, insert_breakpoints_p);
+ return gdbarch->software_single_step (sig, insert_breakpoints_p);
}

void
Index: gdbarch.h
===================================================================
--- gdbarch.h.orig
+++ gdbarch.h
@@ -1144,14 +1144,16 @@ extern void set_gdbarch_smash_text_addre
#define SMASH_TEXT_ADDRESS(addr) (gdbarch_smash_text_address (current_gdbarch, addr))
#endif

-/* FIXME/cagney/2001-01-18: This should be split in two. A target method that indicates if
- the target needs software single step. An ISA method to implement it.
+/* FIXME/cagney/2001-01-18: This should be split in two. A target method that
+ indicates if the target needs software single step. An ISA method to
+ implement it.

- FIXME/cagney/2001-01-18: This should be replaced with something that inserts breakpoints
- using the breakpoint system instead of blatting memory directly (as with rs6000).
+ FIXME/cagney/2001-01-18: This should be replaced with something that inserts
+ breakpoints using the breakpoint system instead of blatting memory directly
+ (as with rs6000).

- FIXME/cagney/2001-01-18: The logic is backwards. It should be asking if the target can
- single step. If not, then implement single step using breakpoints. */
+ FIXME/cagney/2001-01-18: The logic is backwards. It should be asking if the
+ target can single step. If not, then implement single step using breakpoints. */

#if defined (SOFTWARE_SINGLE_STEP)
/* Legacy for systems yet to multi-arch SOFTWARE_SINGLE_STEP */
@@ -1168,8 +1170,8 @@ extern int gdbarch_software_single_step_
#define SOFTWARE_SINGLE_STEP_P() (gdbarch_software_single_step_p (current_gdbarch))
#endif

-typedef void (gdbarch_software_single_step_ftype) (enum target_signal sig, int insert_breakpoints_p);
-extern void gdbarch_software_single_step (struct gdbarch *gdbarch, enum target_signal sig, int insert_breakpoints_p);
+typedef int (gdbarch_software_single_step_ftype) (enum target_signal sig, int insert_breakpoints_p);
+extern int gdbarch_software_single_step (struct gdbarch *gdbarch, enum target_signal sig, int insert_breakpoints_p);
extern void set_gdbarch_software_single_step (struct gdbarch *gdbarch, gdbarch_software_single_step_ftype *software_single_step);
#if !defined (GDB_TM_FILE) && defined (SOFTWARE_SINGLE_STEP)
#error "Non multi-arch definition of SOFTWARE_SINGLE_STEP"
Index: gdbarch.sh
===================================================================
--- gdbarch.sh.orig
+++ gdbarch.sh
@@ -614,15 +614,19 @@ f:=:CORE_ADDR:addr_bits_remove:CORE_ADDR
# It is not at all clear why SMASH_TEXT_ADDRESS is not folded into
# ADDR_BITS_REMOVE.
f:=:CORE_ADDR:smash_text_address:CORE_ADDR addr:addr::core_addr_identity::0
-# FIXME/cagney/2001-01-18: This should be split in two. A target method that indicates if
-# the target needs software single step. An ISA method to implement it.
+
+# FIXME/cagney/2001-01-18: This should be split in two. A target method that
+# indicates if the target needs software single step. An ISA method to
+# implement it.
#
-# FIXME/cagney/2001-01-18: This should be replaced with something that inserts breakpoints
-# using the breakpoint system instead of blatting memory directly (as with rs6000).
+# FIXME/cagney/2001-01-18: This should be replaced with something that inserts
+# breakpoints using the breakpoint system instead of blatting memory directly
+# (as with rs6000).
#
-# FIXME/cagney/2001-01-18: The logic is backwards. It should be asking if the target can
-# single step. If not, then implement single step using breakpoints.
-F:=:void:software_single_step:enum target_signal sig, int insert_breakpoints_p:sig, insert_breakpoints_p
+# FIXME/cagney/2001-01-18: The logic is backwards. It should be asking if the
+# target can single step. If not, then implement single step using breakpoints.
+F:=:int:software_single_step:enum target_signal sig, int insert_breakpoints_p:sig, insert_breakpoints_p
+
# Return non-zero if the processor is executing a delay slot and a
# further single-step is needed before the instruction finishes.
M::int:single_step_through_delay:struct frame_info *frame:frame
Index: infrun.c
===================================================================
--- infrun.c.orig
+++ infrun.c
@@ -559,14 +559,16 @@ resume (int step, enum target_signal sig
if (SOFTWARE_SINGLE_STEP_P () && step)
{
/* Do it the hard way, w/temp breakpoints */
- SOFTWARE_SINGLE_STEP (sig, 1 /*insert-breakpoints */ );
- /* ...and don't ask hardware to do it. */
- 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;
- singlestep_pc = read_pc ();
+ if (SOFTWARE_SINGLE_STEP (sig, 1 /*insert-breakpoints */ ))
+ {
+ /* ...and don't ask hardware to do it. */
+ 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;
+ singlestep_pc = read_pc ();
+ }
}

/* If there were any forks/vforks/execs that were caught and are
@@ -1390,7 +1392,7 @@ handle_inferior_event (struct execution_
(LONGEST) ecs->ws.value.integer));
gdb_flush (gdb_stdout);
target_mourn_inferior ();
- singlestep_breakpoints_inserted_p = 0; /*SOFTWARE_SINGLE_STEP_P() */
+ singlestep_breakpoints_inserted_p = 0; /* SOFTWARE_SINGLE_STEP_P() */
stop_print_frame = 0;
stop_stepping (ecs);
return;
@@ -1410,7 +1412,7 @@ handle_inferior_event (struct execution_
target_mourn_inferior ();

print_stop_reason (SIGNAL_EXITED, stop_signal);
- singlestep_breakpoints_inserted_p = 0; /*SOFTWARE_SINGLE_STEP_P() */
+ singlestep_breakpoints_inserted_p = 0; /* SOFTWARE_SINGLE_STEP_P() */
stop_stepping (ecs);
return;

@@ -1581,7 +1583,7 @@ handle_inferior_event (struct execution_
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepping_past_singlestep_breakpoint\n");
/* Pull the single step breakpoints out of the target. */
- SOFTWARE_SINGLE_STEP (0, 0);
+ (void) SOFTWARE_SINGLE_STEP (0, 0);
singlestep_breakpoints_inserted_p = 0;

ecs->random_signal = 0;
@@ -1690,7 +1692,7 @@ handle_inferior_event (struct execution_
if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
{
/* Pull the single step breakpoints out of the target. */
- SOFTWARE_SINGLE_STEP (0, 0);
+ (void) SOFTWARE_SINGLE_STEP (0, 0);
singlestep_breakpoints_inserted_p = 0;
}

@@ -1761,7 +1763,7 @@ handle_inferior_event (struct execution_
if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
{
/* Pull the single step breakpoints out of the target. */
- SOFTWARE_SINGLE_STEP (0, 0);
+ (void) SOFTWARE_SINGLE_STEP (0, 0);
singlestep_breakpoints_inserted_p = 0;
}

Index: mips-tdep.c
===================================================================
--- mips-tdep.c.orig
+++ mips-tdep.c
@@ -2200,7 +2200,7 @@ mips_addr_bits_remove (CORE_ADDR addr)
single_step is also called just after the inferior stops. If we had
set up a simulated single-step, we undo our damage. */

-void
+int
mips_software_single_step (enum target_signal sig, int insert_breakpoints_p)
{
CORE_ADDR pc, next_pc;
@@ -2214,6 +2214,8 @@ mips_software_single_step (enum target_s
}
else
remove_single_step_breakpoints ();
+
+ return 1;
}

/* Test whether the PC points to the return instruction at the
Index: mips-tdep.h
===================================================================
--- mips-tdep.h.orig
+++ mips-tdep.h
@@ -103,7 +103,7 @@ enum
};

/* Single step based on where the current instruction will take us. */
-extern void mips_software_single_step (enum target_signal, int);
+extern int mips_software_single_step (enum target_signal, int);

/* Tell if the program counter value in MEMADDR is in a MIPS16
function. */
Index: rs6000-tdep.c
===================================================================
--- rs6000-tdep.c.orig
+++ rs6000-tdep.c
@@ -704,7 +704,7 @@ rs6000_breakpoint_from_pc (CORE_ADDR *bp

/* AIX does not support PT_STEP. Simulate it. */

-void
+int
rs6000_software_single_step (enum target_signal signal,
int insert_breakpoints_p)
{
@@ -743,6 +743,8 @@ rs6000_software_single_step (enum target

errno = 0; /* FIXME, don't ignore errors! */
/* What errors? {read,write}_memory call error(). */
+
+ return 1;
}

Index: rs6000-tdep.h
===================================================================
--- rs6000-tdep.h.orig
+++ rs6000-tdep.h
@@ -21,6 +21,6 @@

#include "defs.h"

-extern void rs6000_software_single_step (enum target_signal signal,
- int insert_breakpoints_p);
+extern int rs6000_software_single_step (enum target_signal signal,
+ int insert_breakpoints_p);

Index: sparc-tdep.c
===================================================================
--- sparc-tdep.c.orig
+++ sparc-tdep.c
@@ -1179,7 +1179,7 @@ sparc_step_trap (unsigned long insn)
return 0;
}

-void
+int
sparc_software_single_step (enum target_signal sig, int insert_breakpoints_p)
{
struct gdbarch *arch = current_gdbarch;
@@ -1209,6 +1209,8 @@ sparc_software_single_step (enum target_
}
else
remove_single_step_breakpoints ();
+
+ return 1;
}

static void
Index: sparc-tdep.h
===================================================================
--- sparc-tdep.h.orig
+++ sparc-tdep.h
@@ -167,8 +167,8 @@ extern struct sparc_frame_cache *

\f

-extern void sparc_software_single_step (enum target_signal sig,
- int insert_breakpoints_p);
+extern int sparc_software_single_step (enum target_signal sig,
+ int insert_breakpoints_p);

extern void sparc_supply_rwindow (struct regcache *regcache,
CORE_ADDR sp, int regnum);
Index: wince.c
===================================================================
--- wince.c.orig
+++ wince.c
@@ -839,7 +839,7 @@ undoSStep (thread_info * th)
}
}

-void
+int
wince_software_single_step (enum target_signal ignore,
int insert_breakpoints_p)
{
@@ -851,14 +851,15 @@ wince_software_single_step (enum target_
if (!insert_breakpoints_p)
{
undoSStep (th);
- return;
+ return 1;
}

th->stepped = 1;
pc = read_register (PC_REGNUM);
th->step_pc = mips_next_pc (pc);
insert_single_step_breakpoint (th->step_pc);
- return;
+
+ return 1;
}
#elif SHx
/* Renesas SH architecture instruction encoding masks */
@@ -980,7 +981,7 @@ undoSStep (thread_info * th)
instruction and setting a breakpoint on the "next" instruction
which would be executed. This code hails from sh-stub.c.
*/
-void
+int
wince_software_single_step (enum target_signal ignore,
int insert_breakpoints_p)
{
@@ -990,13 +991,14 @@ wince_software_single_step (enum target_
if (!insert_breakpoints_p)
{
undoSStep (th);
- return;
+ return 1;
}

th->stepped = 1;
th->step_pc = sh_get_next_pc (&th->context);
insert_single_step_breakpoint (th->step_pc);
- return;
+
+ return 1;
}
#elif defined (ARM)
#undef check_for_step
@@ -1027,7 +1029,7 @@ undoSStep (thread_info * th)
}
}

-void
+int
wince_software_single_step (enum target_signal ignore,
int insert_breakpoints_p)
{
@@ -1039,14 +1041,15 @@ wince_software_single_step (enum target_
if (!insert_breakpoints_p)
{
undoSStep (th);
- return;
+ return 1;
}

th->stepped = 1;
pc = read_register (PC_REGNUM);
th->step_pc = arm_get_next_pc (pc);
insert_single_step_breakpoint (th->step_pc);
- return;
+
+ return 1;
}
#endif

[-- Attachment #3: ppc-atomic.single-step.diff --]
[-- Type: text/x-patch, Size: 3409 bytes --]

20006-06-22 Paul Gilliam <pgilliam@us.ibm.com>

* 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

[-- Attachment #4: rs6000-atomic-single-step.diff --]
[-- Type: text/x-patch, Size: 2855 bytes --]

--- old_rs6000-tdep.c 2006-06-22 13:16:03.000000000 -0700
+++ rs6000-tdep.c 2006-06-22 13:41:10.000000000 -0700
@@ -701,8 +701,81 @@
return little_breakpoint;
}

+#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
+deal_with_atomic_sequence (enum target_signal sig)
+{
+ CORE_ADDR pc = read_pc ();
+ CORE_ADDR breaks[2] = {-1, -1};
+ CORE_ADDR loc = pc;
+ int insn = read_memory_integer (loc, 4);
+ int last_break = 0;
+ int i;
+
+
+ /* Assume all atomic sequences start with an lwarx instruction. */
+ if ((insn & LWARX_MASK) != LWARX_INSTRUCTION)
+ return 0;

-/* AIX does not support PT_STEP. Simulate it. */
+ /* Assume that no atomic sequence is longer than 6 instructions. */
+ for (i= 1; i < 5; ++i)
+ {
+ loc += PPC_INSN_SIZE;
+ insn = read_memory_integer (loc, 4);
+
+ /* 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_memory_integer (loc, 4);
+
+ 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);
+
+ return 1;
+}
+
+/* AIX does not support PT_STEP. Simulate it, dealing with any sequence of
+ instructions that must be atomic. */

int
rs6000_software_single_step (enum target_signal signal,
@@ -722,6 +795,9 @@

insn = read_memory_integer (loc, 4);

+ if (deal_with_atomic_sequence (signal))
+ return 1;
+
breaks[0] = loc + breakp_sz;
opcode = insn >> 26;
breaks[1] = branch_dest (opcode, insn, loc, breaks[0]);