This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH] Fix ARM machine state testcase failures
- From: Luis Machado <lgustavo at codesourcery dot com>
- To: "'gdb-patches at sourceware dot org'" <gdb-patches at sourceware dot org>
- Date: Thu, 16 Oct 2014 13:48:29 -0300
- Subject: [PATCH] Fix ARM machine state testcase failures
- Authentication-results: sourceware.org; auth=none
- Reply-to: <lgustavo at codesourcery dot com>
Hi,
When running GDB's reverse debugging testsuite against a few ARM
multilibs, i noticed failures in the machinestate* testcases.
Further investigation showed that push and pop instruction encodings A1
and A2 were not being handled properly, thus we missed saving important
contents from registers and memory. When going backwards, such contents
were not restored and thus we ended up with a corrupted state that did
not correspond to the real values we had at a particular point in time.
Attached is a patch that fixes around 36 failures for both
gdb.reverse/machinestate.exp and gdb.reverse/machinestate-precsave.exp
testcases, making them fully pass. This is for both armv7 and armv4. I
still see failures for armv4 thumb though, so it needs a bit more
investigation.
I see no regressions due to this patch for armv7, armv7 thumb, armv4 and
armv4 thumb.
Ok for trunk?
Regards,
Luis
2014-10-16 Luis Machado <lgustavo@codesourcery.com>
* arm-tdep.c (POP_A2, PUSH_A2): New #defines.
(arm_record_ld_st_imm_offset): Handle single-register ARM
mode push and pop instructions.
(POP_A1, PUSH_A1, PUSH_POP_SP_REG_MASK): New #defines.
(arm_record_ld_st_multiple): Handle multi-register ARM
mode push and pop instructions.
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index e2559ec..c488906 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -11473,6 +11473,10 @@ arm_record_data_proc_imm (insn_decode_record *arm_insn_r)
return 0;
}
+/* Masks for PUSH and POP instructions that take a single register. */
+#define POP_A2 0x049d0004 /* POP<c> <registers> */
+#define PUSH_A2 0x052d0004 /* PUSH<c> <registers> */
+
/* Handling opcode 010 insns. */
static int
@@ -11489,7 +11493,39 @@ arm_record_ld_st_imm_offset (insn_decode_record *arm_insn_r)
arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
arm_insn_r->decode = bits (arm_insn_r->arm_insn, 4, 7);
- if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ if ((arm_insn_r->arm_insn & POP_A2) == POP_A2)
+ {
+ /* Handle single-register pop. */
+ int regno = bits (arm_insn_r->arm_insn, 12, 15);
+
+ record_buf[0] = regno;
+ arm_insn_r->reg_rec_count = 1;
+
+ /* The stack pointer register is always modified. Check if we have
+ already saved it though. */
+ if (regno != ARM_SP_REGNUM)
+ {
+ record_buf[1] = ARM_SP_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ }
+ else if ((arm_insn_r->arm_insn & PUSH_A2) == PUSH_A2)
+ {
+ /* Handle single-register push. */
+
+ /* The stack pointer register is always updated. */
+ record_buf[0] = ARM_SP_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+
+ /* The pushed register goes into the stack, so we need to record that
+ chunk of memory as well. */
+ regcache_raw_read_unsigned (reg_cache, ARM_SP_REGNUM, &u_regval);
+ tgt_mem_addr = (uint32_t) u_regval;
+ record_buf_mem[0] = INT_REGISTER_SIZE;
+ record_buf_mem[1] = tgt_mem_addr - INT_REGISTER_SIZE;
+ arm_insn_r->mem_rec_count = 1;
+ }
+ else if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
{
reg_dest = bits (arm_insn_r->arm_insn, 12, 15);
/* LDR insn has a capability to do branching, if
@@ -11847,6 +11883,11 @@ arm_record_ld_st_reg_offset (insn_decode_record *arm_insn_r)
return 0;
}
+/* Masks for PUSH and POP instructions that take multiple registers. */
+#define POP_A1 0x08bd0000 /* POP<c> <registers> */
+#define PUSH_A1 0x092d0000 /* PUSH<c> <registers> */
+#define PUSH_POP_SP_REG_MASK 0x00002000
+
/* Handling opcode 100 insns. */
static int
@@ -11861,12 +11902,66 @@ arm_record_ld_st_multiple (insn_decode_record *arm_insn_r)
ULONGEST u_regval[2] = {0};
+ if ((arm_insn_r->arm_insn & POP_A1) == POP_A1)
+ {
+ /* Handle multi-register pop. */
+ int regno = 0;
+
+ register_bits = bits (arm_insn_r->arm_insn, 0, 15);
+
+ /* Record which registers are going to be modified. */
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ record_buf[arm_insn_r->reg_rec_count++] = regno;
+ register_bits = register_bits >> 1;
+ regno++;
+ }
+
+ /* The stack pointer register is always modified. Check if we have
+ already saved it though. */
+ if ((bits (arm_insn_r->arm_insn, 0, 15) & PUSH_POP_SP_REG_MASK) == 0)
+ {
+ record_buf[arm_insn_r->reg_rec_count] = ARM_SP_REGNUM;
+ arm_insn_r->reg_rec_count++;
+ }
+ }
+ else if ((arm_insn_r->arm_insn & PUSH_A1) == PUSH_A1)
+ {
+ /* Handle multi-register push. */
+ int mem_chunks = 0;
+ uint32_t sp_addr;
+ ULONGEST u_regval;
+
+ register_bits = bits (arm_insn_r->arm_insn, 0, 15);
+ regcache_raw_read_unsigned (reg_cache, ARM_SP_REGNUM, &u_regval);
+ sp_addr = (uint32_t) u_regval;
+
+ /* The stack pointer register is always modified. */
+ record_buf[0] = ARM_SP_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+
+ /* The pushed registers go into the stack, so we need to record those
+ chunks of memory as well. Count how many registers are going to be
+ saved. */
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ mem_chunks++;
+
+ register_bits = register_bits >> 1;
+ }
+
+ /* Record the chunks in one continuous block. */
+ record_buf_mem[0] = mem_chunks * INT_REGISTER_SIZE;
+ record_buf_mem[1] = sp_addr - mem_chunks * INT_REGISTER_SIZE;
+ arm_insn_r->mem_rec_count = 1;
+ }
/* This mode is exclusively for load and store multiple. */
/* Handle incremenrt after/before and decrment after.before mode;
Rn is changing depending on W bit, but as of now we store Rn too
without optimization. */
-
- if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ else if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
{
/* LDM (1,2,3) where LDM (3) changes CPSR too. */