OSDN Git Service

Add code for software singlestep of AM33 LIW instructions. Revise code
authorkevinb <kevinb>
Tue, 3 Oct 2006 22:49:05 +0000 (22:49 +0000)
committerkevinb <kevinb>
Tue, 3 Oct 2006 22:49:05 +0000 (22:49 +0000)
for handling am33's SETLB and Lcc instructions.

rda/unix/ChangeLog
rda/unix/linux-target.c
rda/unix/ptrace-target.c
rda/unix/server.h

index 80c57f8..4e02e01 100644 (file)
@@ -1,3 +1,16 @@
+2006-10-03  Kevin Buettner  <kevinb@redhat.com>
+
+       * server.h (struct ss_save): Add new field ``restore_action''.
+       * ptrace-target.c (handle_waitstatus): Invoke ``restore_action''.
+       * linux-target.c (LIR_REGNUM) [AM33_2_0_LINUX_TARGET]: Define.
+       (set_singlestep_breakpoint): Add ``restore_action'' parameter.
+       Fix all callers.
+       (am33_set_register, am33_restore_lir): New functions.
+       (am33_opcode_size): Change size of LIW instruction(s) to 0 so
+       that these will be handled explicitly in am33_singlestep.
+       (am33_singlestep): Handle LIW instructions.  Revise manner in
+       which Lcc and SETLB instructions are handled.
+
 2006-03-16  Kevin Buettner  <kevinb@redhat.com>
 
        * server.c (main): Change polling interval for gdbloop_poll()
index 8ec294d..28a1f37 100644 (file)
@@ -497,6 +497,7 @@ enum
   A1_REGNUM = 5,
   A2_REGNUM = 6,
   A3_REGNUM = 7,
+  LIR_REGNUM = 12,
   LAR_REGNUM = 13,
   MDR_REGNUM = 10,
   SP_REGNUM = 8,
@@ -3354,7 +3355,9 @@ decr_pc_after_break (struct gdbserv *serv, pid_t pid)
 
 static void
 set_singlestep_breakpoint (struct gdbserv *serv, ptrace_arg3_type addr,
-                          char *breakpoint_bytes, int breakpoint_length)
+                          char *breakpoint_bytes, int breakpoint_length,
+                          void (*restore_action) (struct gdbserv *serv,
+                                                  struct ss_save *save))
 {
   int i = 0;
   struct child_process *process = gdbserv_target_data (serv);
@@ -3393,6 +3396,8 @@ set_singlestep_breakpoint (struct gdbserv *serv, ptrace_arg3_type addr,
                   &process->ss_info[i].ss_addr,
                  breakpoint_bytes,
                  process->ss_info[i].ss_size);
+
+  process->ss_info[i].restore_action = restore_action;
   if (process->debug_backend)
     fprintf (stderr, "Singlestep breakpoint %d set at location %lx\n", i, addr);
 }
@@ -3551,15 +3556,15 @@ mips_singlestep (struct gdbserv *serv, pid_t pid, int sig)
       if (is_cond && targ != (mips_pc + 8))
        {
          set_singlestep_breakpoint (serv, mips_pc + 8, &bp_inst,
-                                     sizeof (bp_inst));
+                                     sizeof (bp_inst), NULL);
        }
       set_singlestep_breakpoint (serv, targ, &bp_inst,
-                                 sizeof (bp_inst));
+                                 sizeof (bp_inst), NULL);
     }
   else
     {
       set_singlestep_breakpoint (serv, mips_pc + 4, &bp_inst,
-                                 sizeof (bp_inst));
+                                 sizeof (bp_inst), NULL);
     }
 
   ptrace (PTRACE_CONT, pid, 1L, sig); 
@@ -3635,6 +3640,12 @@ am33_get_register (struct gdbserv *serv, pid_t pid, int regno)
   return debug_get_reg (serv, pid, regno);
 }
 
+static int
+am33_set_register (struct gdbserv *serv, pid_t pid, int regno, unsigned long regval)
+{
+  return write_reg_as_ulong (serv, pid, regno, regval);
+}
+
 
 /* Get the contents of An register.  */
 
@@ -3655,6 +3666,23 @@ am33_get_areg (struct gdbserv *serv, pid_t pid, int n)
   return 0;
 }
 
+/* Restore LIR after singlestepping an Lcc instruction.  */
+static void
+am33_restore_lir (struct gdbserv *serv, struct ss_save *save)
+{
+  struct child_process *process = gdbserv_target_data (serv);
+  int pid = process->pid;
+  unsigned long lir = am33_get_register (serv, pid, LIR_REGNUM);
+  unsigned long addr;
+  int rot;
+
+  gdbserv_host_bytes_from_reg (serv, &addr, sizeof (addr), &save->ss_addr,
+                               sign_extend);
+  rot = addr & 0x3;
+  lir = (lir & ~(0xff << (rot * 8))) 
+        | (am33_read_byte (serv, addr) << (rot * 8));
+  am33_set_register (serv, pid, LIR_REGNUM, lir);
+}
 
 /* Table of instruction sizes, indexed by first byte of instruction,
    used to determine the address of the next instruction for single stepping.
@@ -3680,7 +3708,7 @@ static char am33_opcode_size[256] =
 /* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2,
 /* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 /* e */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-/* f */ 0, 2, 2, 2, 2, 2, 2, 1, 0, 3, 0, 4, 0, 6, 7, 1
+/* f */ 0, 2, 2, 2, 2, 2, 2, 0, 0, 3, 0, 4, 0, 6, 7, 1
 };
 
 /* Set breakpoint(s) to simulate a single step from the current PC.  */
@@ -3733,7 +3761,8 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
   displ = am33_opcode_size[opcode];
   if (displ != 0)
     {
-      set_singlestep_breakpoint (serv, pc + displ, &bp_inst, sizeof (bp_inst));
+      set_singlestep_breakpoint (serv, pc + displ, &bp_inst, sizeof (bp_inst),
+                                 NULL);
     }
   else
 
@@ -3755,11 +3784,30 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
         *  bxx (d8,PC)
         */
        displ = (signed char) am33_read_byte (serv, pc + 1);
-       set_singlestep_breakpoint (serv, pc + 2, &bp_inst, sizeof (bp_inst));
+       set_singlestep_breakpoint (serv, pc + 2, &bp_inst, sizeof (bp_inst), NULL);
        if (displ < 0 || displ > 2)
-         set_singlestep_breakpoint (serv, pc + displ, &bp_inst, sizeof (bp_inst));
+         set_singlestep_breakpoint (serv, pc + displ, &bp_inst,
+                                    sizeof (bp_inst), NULL);
        break;
 
+      case 0xf7:
+       /* Various LIW instructions.  */
+       opcode = am33_read_byte (serv, pc + 1);
+       if (opcode != 0xe0)
+         {
+           /* Not a MOV_Lcc.  Set breakpoint four bytes after current
+              instruction.  */
+           set_singlestep_breakpoint (serv, pc + 4, &bp_inst,
+                                      sizeof (bp_inst), NULL);
+           break;
+         }
+       /* If it is a MOV_Lcc, we'll fall through to the Lcc case below.
+          Advance PC so that the code below will work for this instruction
+          too.  */
+       pc += 3;
+       
+       /* Fall through...  */
+
       case 0xd0:
       case 0xd1:
       case 0xd2:
@@ -3771,23 +3819,71 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
       case 0xd8:
       case 0xd9:
       case 0xda:
-       /*
-        *  lxx (d8,PC)
-        */
-       if (pc != am33_get_register (serv, pid, LAR_REGNUM))
-         set_singlestep_breakpoint (serv,
-                                    am33_get_register (serv, pid, LAR_REGNUM),
-                                    &bp_inst, sizeof (bp_inst));
-       set_singlestep_breakpoint (serv, pc + 1, &bp_inst, sizeof (bp_inst));
+       /* Lcc: This is (conceptually) a conditional branch to the address
+          contained in LAR - 4.  A SETLB instruction should have been
+          executed to set up LAR and LIR.  LIR contains four bytes of
+          prefetched instructions beginning at LAR - 4.  (These will
+          be rotated depending on the low two bits of the address.)
+          In order to singlestep this instruction, we place
+          breakpoints at the instruction following Lcc in addition to
+          the branch target specified by LAR - 4.  However, we can't
+          expect that this latter breakpoint will actually do
+          anything useful since the processor uses the prefetched
+          instruction data contained in LIR.  Therefore, we also
+          place a breakpoint at correct byte of LIR.  The function
+          am33_restore_lir() will restore the correct LIR contents
+          after stopping at the breakpoint.
+          
+          Once the processor has stopped at the first instruction in
+          the prefetched data, the subsequent instructions are executed
+          from memory.  Thus, the effect of these instructions should be
+          identical to that of executing out of the LIR with the proviso
+          that the program doesn't change the LIR after executing the
+          SETLB instruction.  (I guess this would be an interesting way
+          to implement self modifying code...)  */
+
+
+       {
+         unsigned long lar = am33_get_register (serv, pid, LAR_REGNUM);
+         int rot = lar & 0x3; 
+         if (pc != lar - 4)
+           {
+             unsigned long lir = am33_get_register (serv, pid, LIR_REGNUM);
+             lir = (lir & ~(0xff << (rot * 8))) | (bp_inst << (rot * 8));
+             am33_set_register (serv, pid, LIR_REGNUM, lir);
+             set_singlestep_breakpoint (serv, lar - 4, &bp_inst,
+                                      sizeof (bp_inst), am33_restore_lir);
+           }
+         set_singlestep_breakpoint (serv, pc + 1, &bp_inst, sizeof (bp_inst),
+                                    NULL);
+       }
        break;
 
       case 0xdb:
-       /*
-        * setlb requires special attention. It loads the next four instruction
-        * bytes into the LIR register, so we can't insert a breakpoint in any
-        * of those locations.
-        */
-       set_singlestep_breakpoint (serv, pc + 5, &bp_inst, sizeof (bp_inst));
+       /* setlb:  Simulate execution of setlb instruction.  Advance
+          pc to next instruction and set a breakpoint there too to
+          make it appear that we executed this instruction.
+          
+          If we set a breakpoint on the following instruction and then
+          allow SETLB to execute, we'll end up with a breakpoint in the
+          LIR register.
+          
+          An earlier version of this code attempted to set the
+          breakpoint after the four bytes which are loaded into LIR. 
+          This is incorrect because that location might not be the
+          start of a new instruction.  */
+
+       am33_set_register (serv, pid, PC_REGNUM, pc + 1);
+       am33_set_register (serv, pid, LAR_REGNUM, pc + 5);
+       {
+         unsigned long lir = (unsigned long) am33_read_disp32 (serv, pc + 1);
+         int rot = (pc + 1) & 0x3; 
+         if (rot != 0)
+           lir = (lir << (rot * 8)) | (lir >> ((4 - rot) * 8));
+         am33_set_register (serv, pid, LIR_REGNUM, lir);
+       }
+       set_singlestep_breakpoint (serv, pc + 1, &bp_inst, sizeof (bp_inst),
+                                  NULL);
        break;
 
       case 0xcc:
@@ -3796,7 +3892,8 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
         * jmp (d16,PC) or call (d16,PC)
         */
        displ = am33_read_disp16(serv, pc + 1);
-       set_singlestep_breakpoint (serv, pc + displ, &bp_inst, sizeof (bp_inst));
+       set_singlestep_breakpoint (serv, pc + displ, &bp_inst,
+                                  sizeof (bp_inst), NULL);
        break;
 
       case 0xdc:
@@ -3805,7 +3902,8 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
         * jmp (d32,PC) or call (d32,PC)
         */
        displ = am33_read_disp32(serv, pc + 1);
-       set_singlestep_breakpoint (serv, pc + displ, &bp_inst, sizeof (bp_inst));
+       set_singlestep_breakpoint (serv, pc + displ, &bp_inst,
+                                  sizeof (bp_inst), NULL);
        break;
 
       case 0xde:
@@ -3813,7 +3911,7 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
         *  retf
         */
        set_singlestep_breakpoint (serv, am33_get_register (serv, pid, MDR_REGNUM),
-                                  &bp_inst, sizeof (bp_inst));
+                                  &bp_inst, sizeof (bp_inst), NULL);
        break;
 
       case 0xdf:
@@ -3825,7 +3923,7 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
          serv,
          am33_read_disp32 (serv,
                            am33_get_register (serv, pid, SP_REGNUM) + displ),
-                           &bp_inst, sizeof (bp_inst));
+         &bp_inst, sizeof (bp_inst), NULL);
        break;
 
       case 0xf0:
@@ -3838,7 +3936,7 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
            /* jmp (An) / calls (An) */
            set_singlestep_breakpoint (serv,
                                       am33_get_areg (serv, pid, opcode & 3),
-                                      &bp_inst, sizeof (bp_inst));
+                                      &bp_inst, sizeof (bp_inst), NULL);
 
          }
        else if (opcode == 0xfc)
@@ -3847,7 +3945,7 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
            set_singlestep_breakpoint (
              serv,
              am33_read_disp32 (serv, am33_get_register (serv, pid, SP_REGNUM)),
-                               &bp_inst, sizeof (bp_inst));
+                               &bp_inst, sizeof (bp_inst), NULL);
       
          }
        else if (opcode == 0xfd)
@@ -3857,11 +3955,12 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
              serv,
              am33_read_disp32 (serv,
                                am33_get_register (serv, pid, SP_REGNUM) + 4),
-             &bp_inst, sizeof (bp_inst));
+             &bp_inst, sizeof (bp_inst), NULL);
 
          }
        else 
-         set_singlestep_breakpoint (serv, pc + 2, &bp_inst, sizeof (bp_inst));
+         set_singlestep_breakpoint (serv, pc + 2, &bp_inst, sizeof (bp_inst),
+                                    NULL);
 
        break;
 
@@ -3873,14 +3972,16 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
        if (opcode >= 0xe8 && opcode <= 0xeb)
          {
            displ = (signed char) am33_read_byte (serv, pc + 2);
-           set_singlestep_breakpoint (serv, pc + 3, &bp_inst, sizeof (bp_inst));
+           set_singlestep_breakpoint (serv, pc + 3, &bp_inst, sizeof (bp_inst),
+                                      NULL);
            if (displ < 0 || displ > 3)
              set_singlestep_breakpoint (serv, pc + displ,
-                                        &bp_inst, sizeof (bp_inst));
+                                        &bp_inst, sizeof (bp_inst), NULL);
       
          }
        else
-         set_singlestep_breakpoint (serv, pc + 3, &bp_inst, sizeof (bp_inst));
+         set_singlestep_breakpoint (serv, pc + 3, &bp_inst, sizeof (bp_inst),
+                                    NULL);
        break;
 
       case 0xfa:
@@ -3890,10 +3991,11 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
            /* calls (d16,PC) */
            displ = am33_read_disp16 (serv, pc + 2);
            set_singlestep_breakpoint (serv, pc + displ,
-                                      &bp_inst, sizeof (bp_inst));
+                                      &bp_inst, sizeof (bp_inst), NULL);
          }
        else
-         set_singlestep_breakpoint (serv, pc + 4, &bp_inst, sizeof (bp_inst));
+         set_singlestep_breakpoint (serv, pc + 4, &bp_inst, sizeof (bp_inst),
+                                    NULL);
        break;
 
       case 0xfc:
@@ -3902,10 +4004,12 @@ am33_singlestep (struct gdbserv *serv, pid_t pid, int sig)
          {
            /* calls (d32,PC) */
            displ = am33_read_disp32 (serv, pc + 2);
-           set_singlestep_breakpoint (serv, pc + displ, &bp_inst, sizeof (bp_inst));
+           set_singlestep_breakpoint (serv, pc + displ,
+                                      &bp_inst, sizeof (bp_inst), NULL);
          }
        else
-         set_singlestep_breakpoint (serv, pc + 6, &bp_inst, sizeof (bp_inst));
+         set_singlestep_breakpoint (serv, pc + 6,
+                                    &bp_inst, sizeof (bp_inst), NULL);
        break;
 
     }
index a51d6c8..1300e9a 100644 (file)
@@ -178,7 +178,14 @@ handle_waitstatus (struct child_process *process, union wait w)
                            &process->ss_info[i].ss_addr,
                            process->ss_info[i].ss_val,
                            process->ss_info[i].ss_size);
+
+           /* Perform additional actions associated with this breakpoint.  */
+           if (process->ss_info[i].restore_action)
+             process->ss_info[i].restore_action (process->serv,
+                                                 &process->ss_info[i]);
+
            process->ss_info[i].in_use = 0;
+
            if (process->debug_backend)
              {
                long addr;
index 8d8264e..d6accd6 100644 (file)
@@ -37,6 +37,10 @@ struct ss_save {
        struct gdbserv_reg ss_addr;
        char ss_val[8];
        int ss_size;
+       /* Additional actions that need to be taken after restoring certain
+          singlestep breakpoints.  This will be NULL if no action other
+          than the default is required.  */
+       void (*restore_action) (struct gdbserv *serv, struct ss_save *save);
 };
 #endif