* lib/gdb.exp(gdb_step_for_stub): New function.
[deliverable/binutils-gdb.git] / gdb / mips-tdep.c
index 1c02dedbc0167b516b8a715fb78884e12800d5df..54d0fc530b3a3c470d964bcefa1a33166c35e19b 100644 (file)
@@ -41,18 +41,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 /* FIXME: Put this declaration in frame.h.  */
 extern struct obstack frame_cache_obstack;
 
-/* FIXME! this code assumes 4-byte instructions.  */
-#define MIPS_INSTLEN 4         /* Length of an instruction */
-#define MIPS16_INSTLEN 2       /* Length of an instruction on MIPS16*/
-#define MIPS_NUMREGS 32                /* Number of integer or float registers */
-typedef unsigned long t_inst;  /* Integer big enough to hold an instruction */
-
-/* MIPS16 function addresses are odd (bit 0 is set).  Here are some
-   macros to test, set, or clear bit 0 of addresses.  */
-#define IS_MIPS16_ADDR(addr)    ((addr) & 1)
-#define MAKE_MIPS16_ADDR(addr)  ((addr) | 1)
-#define UNMAKE_MIPS16_ADDR(addr) ((addr) & ~1)
-
 #if 0
 static int mips_in_lenient_prologue PARAMS ((CORE_ADDR, CORE_ADDR));
 #endif
@@ -316,6 +304,32 @@ mips16_decode_reg_save (inst, gen_mask)
     *gen_mask |= (1 << 31);
 }
 
+
+/* Fetch and return instruction from the specified location.  If the PC
+   is odd, assume it's a MIPS16 instruction; otherwise MIPS32.  */
+
+static t_inst
+mips_fetch_instruction (addr)
+    CORE_ADDR addr;
+{
+  char buf[MIPS_INSTLEN];
+  int instlen;
+  int status;
+
+  if (IS_MIPS16_ADDR (addr))
+    {
+      instlen = MIPS16_INSTLEN;
+      addr = UNMAKE_MIPS16_ADDR (addr);
+    }
+  else
+      instlen = MIPS_INSTLEN;
+  status = read_memory_nobpt (addr, buf, instlen);
+  if (status)
+    memory_error (status, addr);
+  return extract_unsigned_integer (buf, instlen);
+}
+
+
 /* Guaranteed to set fci->saved_regs to some values (it never leaves it
    NULL).  */
 
@@ -330,6 +344,7 @@ mips_find_saved_regs (fci)
   /* What registers have been saved?  Bitmasks.  */
   unsigned long gen_mask, float_mask;
   mips_extra_func_info_t proc_desc;
+  t_inst inst;
 
   fci->saved_regs = (struct frame_saved_regs *)
     obstack_alloc (&frame_cache_obstack, sizeof(struct frame_saved_regs));
@@ -406,34 +421,22 @@ mips_find_saved_regs (fci)
         claims are saved have been saved yet.  */
 
       CORE_ADDR addr;
-      int status;
-      char buf[MIPS_INSTLEN];
-      t_inst inst;
-      int instlen;
 
       /* Bitmasks; set if we have found a save for the register.  */
       unsigned long gen_save_found = 0;
       unsigned long float_save_found = 0;
+      int instlen;
 
       /* If the address is odd, assume this is MIPS16 code.  */
       addr = PROC_LOW_ADDR (proc_desc);
-      if (IS_MIPS16_ADDR (addr))
-       {
-         instlen = MIPS16_INSTLEN;
-         addr = UNMAKE_MIPS16_ADDR (addr);
-       }
-      else
-       instlen = MIPS_INSTLEN;
+      instlen = IS_MIPS16_ADDR (addr) ? MIPS16_INSTLEN : MIPS_INSTLEN;
 
       /* Scan through this function's instructions preceding the current
          PC, and look for those that save registers.  */
       while (addr < fci->pc)
        {
-         status = read_memory_nobpt (addr, buf, instlen);
-         if (status)
-           memory_error (status, addr);
-         inst = extract_unsigned_integer (buf, instlen);
-         if (instlen == MIPS16_INSTLEN)
+         inst = mips_fetch_instruction (addr);
+         if (IS_MIPS16_ADDR (addr))
            mips16_decode_reg_save (inst, &gen_save_found);
          else
            mips32_decode_reg_save (inst, &gen_save_found, &float_save_found);
@@ -452,6 +455,33 @@ mips_find_saved_regs (fci)
        fci->saved_regs->regs[ireg] = reg_position;
        reg_position -= MIPS_REGSIZE;
       }
+
+  /* The MIPS16 entry instruction saves $s0 and $s1 in the reverse order
+     of that normally used by gcc.  Therefore, we have to fetch the first
+     instruction of the function, and if it's an entry instruction that
+     saves $s0 or $s1, correct their saved addresses.  */
+  if (IS_MIPS16_ADDR (PROC_LOW_ADDR (proc_desc)))
+    {
+      inst = mips_fetch_instruction (PROC_LOW_ADDR (proc_desc));
+      if ((inst & 0xf81f) == 0xe809 && (inst & 0x700) != 0x700) /* entry */
+       {
+         int reg;
+         int sreg_count = (inst >> 6) & 3;
+         
+         /* Check if the ra register was pushed on the stack.  */
+         reg_position = fci->frame + PROC_REG_OFFSET (proc_desc);
+         if (inst & 0x20)
+           reg_position -= MIPS_REGSIZE;
+
+         /* Check if the s0 and s1 registers were pushed on the stack.  */
+         for (reg = 16; reg < sreg_count+16; reg++)
+           {
+             fci->saved_regs->regs[reg] = reg_position;
+             reg_position -= MIPS_REGSIZE;
+           }
+       }
+    }
+
   /* Fill in the offsets for the registers which float_mask says
      were saved.  */
   reg_position = fci->frame + PROC_FREG_OFFSET (proc_desc);
@@ -459,7 +489,7 @@ mips_find_saved_regs (fci)
   /* The freg_offset points to where the first *double* register
      is saved.  So skip to the high-order word. */
   if (! GDB_TARGET_IS_MIPS64)
-    reg_position += 4;
+    reg_position += MIPS_REGSIZE;
 
   /* Fill in the offsets for the float registers which float_mask says
      were saved.  */
@@ -503,9 +533,9 @@ mips_addr_bits_remove (addr)
 {
 #if GDB_TARGET_IS_MIPS64
   if ((addr >> 32 == (CORE_ADDR)0xffffffff)
-      && (strcmp(target_shortname,"pmon")==0
-        || strcmp(target_shortname,"ddb")==0
-        || strcmp(target_shortname,"sim")==0))
+      && (strcmp (target_shortname,"pmon")==0
+        || strcmp (target_shortname,"ddb")==0
+        || strcmp (target_shortname,"sim")==0))
     {
       /* This hack is a work-around for existing boards using PMON,
         the simulator, and any other 64-bit targets that doesn't have
@@ -532,6 +562,20 @@ mips_addr_bits_remove (addr)
   return addr;
 }
 
+void
+mips_init_frame_pc_first (fromleaf, prev)
+     int fromleaf;
+     struct frame_info *prev;
+{
+  CORE_ADDR pc, tmp;
+
+  pc = ((fromleaf) ? SAVED_PC_AFTER_CALL (prev->next) :
+        prev->next ? FRAME_SAVED_PC (prev->next) : read_pc ());
+  tmp = mips_skip_stub (pc);
+  prev->pc =  tmp ? tmp : pc;
+}
+
+
 CORE_ADDR
 mips_frame_saved_pc(frame)
      struct frame_info *frame;
@@ -595,7 +639,7 @@ heuristic_proc_start(pc)
                else
                  warning("Hit heuristic-fence-post without finding");
                
-               warning("enclosing function for address 0x%s", paddr (pc));
+               warning("enclosing function for address 0x%s", paddr_nz (pc));
                if (!blurb_printed)
                  {
                    printf_filtered ("\
@@ -621,7 +665,7 @@ Otherwise, you told GDB there was a function where there isn't one, or\n\
                 addiu sp,-n
                 daddiu sp,-n
                 extend -n followed by 'addiu sp,+n' or 'daddiu sp,+n'  */
-           inst = read_memory_integer (UNMAKE_MIPS16_ADDR (start_pc), 2);
+           inst = mips_fetch_instruction (start_pc);
            if (((inst & 0xf81f) == 0xe809 && (inst & 0x700) != 0x700) /* entry */
                || (inst & 0xff80) == 0x6380    /* addiu sp,-n */
                || (inst & 0xff80) == 0xfb80    /* daddiu sp,-n */
@@ -647,7 +691,7 @@ Otherwise, you told GDB there was a function where there isn't one, or\n\
     return start_pc;
 }
 
-/* Fetch the immediate value from the current instruction.
+/* Fetch the immediate value from a MIPS16 instruction.
    If the previous instruction was an EXTEND, use it to extend
    the upper bits of the immediate value.  This is a helper function
    for mips16_heuristic_proc_desc.  */
@@ -701,19 +745,14 @@ mips16_heuristic_proc_desc(start_pc, limit_pc, next_frame, sp)
 
   for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS16_INSTLEN)
     {
-      char buf[MIPS16_INSTLEN];
-      int status, reg, offset;
+      int reg, offset;
 
       /* Save the previous instruction.  If it's an EXTEND, we'll extract
          the immediate offset extension from it in mips16_get_imm.  */
       prev_inst = inst;
 
-      /* Fetch the instruction.   */
-      status = read_memory_nobpt (UNMAKE_MIPS16_ADDR (cur_pc), buf,
-                                 MIPS16_INSTLEN);
-      if (status) memory_error (status, cur_pc);
-      inst = (unsigned short) extract_unsigned_integer (buf, MIPS16_INSTLEN);
-
+      /* Fetch and decode the instruction.   */
+      inst = (unsigned short) mips_fetch_instruction (cur_pc);
       if ((inst & 0xff00) == 0x6300            /* addiu sp */
          || (inst & 0xff00) == 0xfb00)         /* daddiu sp */
        {
@@ -786,10 +825,11 @@ mips16_heuristic_proc_desc(start_pc, limit_pc, next_frame, sp)
          PROC_FRAME_OFFSET(&temp_proc_desc) += 32;
 
          /* Check if a0-a3 were saved in the caller's argument save area.  */
-         for (reg = 4, offset = 32; reg < areg_count+4; reg++, offset += 4)
+         for (reg = 4, offset = 32; reg < areg_count+4; reg++)
            {
              PROC_REG_MASK(&temp_proc_desc) |= 1 << reg;
              temp_saved_regs.regs[reg] = sp + offset;
+             offset -= MIPS_REGSIZE;
            }
 
          /* Check if the ra register was pushed on the stack.  */
@@ -798,14 +838,15 @@ mips16_heuristic_proc_desc(start_pc, limit_pc, next_frame, sp)
            {
              PROC_REG_MASK(&temp_proc_desc) |= 1 << 31;
              temp_saved_regs.regs[31] = sp + offset;
-             offset -= 4;
+             offset -= MIPS_REGSIZE;
            }
 
          /* Check if the s0 and s1 registers were pushed on the stack.  */
-         for (reg = 16; reg < sreg_count+16; reg++, offset -= 4)
+         for (reg = 16; reg < sreg_count+16; reg++)
            {
              PROC_REG_MASK(&temp_proc_desc) |= 1 << reg;
              temp_saved_regs.regs[reg] = sp + offset;
+             offset -= MIPS_REGSIZE;
            }
        }
       else if ((inst & 0xf800) == 0x1800)      /* jal(x) */
@@ -825,14 +866,11 @@ restart:
   PROC_FRAME_OFFSET(&temp_proc_desc) = 0;
   for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSTLEN)
     {
-      char buf[MIPS_INSTLEN];
       unsigned long inst, high_word, low_word;
-      int status, reg;
+      int reg;
 
       /* Fetch the instruction.   */
-      status = (unsigned long) read_memory_nobpt (cur_pc, buf, MIPS_INSTLEN);
-      if (status) memory_error (status, cur_pc);
-      inst = (unsigned long) extract_unsigned_integer (buf, MIPS_INSTLEN);
+      inst = (unsigned long) mips_fetch_instruction (cur_pc);
 
       /* Save some code by pre-extracting some useful fields.  */
       high_word = (inst >> 16) & 0xffff;
@@ -1060,29 +1098,36 @@ CORE_ADDR
 mips_frame_chain(frame)
     struct frame_info *frame;
 {
-    mips_extra_func_info_t proc_desc;
-    CORE_ADDR saved_pc = FRAME_SAVED_PC(frame);
-
-    if (saved_pc == 0 || inside_entry_file (saved_pc))
-      return 0;
-
-    proc_desc = find_proc_desc(saved_pc, frame);
-    if (!proc_desc)
-      return 0;
-
-    cached_proc_desc = proc_desc;
-
-    /* If no frame pointer and frame size is zero, we must be at end
-       of stack (or otherwise hosed).  If we don't check frame size,
-       we loop forever if we see a zero size frame.  */
-    if (PROC_FRAME_REG (proc_desc) == SP_REGNUM
-       && PROC_FRAME_OFFSET (proc_desc) == 0
-       /* The previous frame from a sigtramp frame might be frameless
-          and have frame size zero.  */
-       && !frame->signal_handler_caller)
-      return 0;
-    else
-      return get_frame_pointer (frame, proc_desc);
+  mips_extra_func_info_t proc_desc;
+  CORE_ADDR tmp;
+  CORE_ADDR saved_pc = FRAME_SAVED_PC(frame);
+
+  if (saved_pc == 0 || inside_entry_file (saved_pc))
+    return 0;
+
+  /* Check if the PC is inside a call stub.  If it is, fetch the
+     PC of the caller of that stub.  */
+  if ((tmp = mips_skip_stub (saved_pc)) != 0)
+    saved_pc = tmp;
+
+  /* Look up the procedure descriptor for this PC.  */
+  proc_desc = find_proc_desc(saved_pc, frame);
+  if (!proc_desc)
+    return 0;
+
+  cached_proc_desc = proc_desc;
+
+  /* If no frame pointer and frame size is zero, we must be at end
+     of stack (or otherwise hosed).  If we don't check frame size,
+     we loop forever if we see a zero size frame.  */
+  if (PROC_FRAME_REG (proc_desc) == SP_REGNUM
+      && PROC_FRAME_OFFSET (proc_desc) == 0
+      /* The previous frame from a sigtramp frame might be frameless
+        and have frame size zero.  */
+      && !frame->signal_handler_caller)
+    return 0;
+  else
+    return get_frame_pointer (frame, proc_desc);
 }
 
 void
@@ -1181,7 +1226,7 @@ mips_push_arguments(nargs, args, sp, struct_return, struct_addr)
   int float_argreg;
   int argnum;
   int len = 0;
-  int stack_offset;
+  int stack_offset = 0;
 
   /* Macros to round N up or down to the next A boundary; A must be
      a power of two. */
@@ -1209,11 +1254,6 @@ mips_push_arguments(nargs, args, sp, struct_return, struct_addr)
   if (struct_return)
       write_register (argreg++, struct_addr);
 
-  /* The offset onto the stack at which we will start copying parameters
-     (after the registers are used up) begins at 16 in the old ABI.
-     This leaves room for the "home" area for register parameters.  */
-  stack_offset = MIPS_EABI ? 0 : MIPS_REGSIZE * 4;
-
   /* Now load as many as possible of the first arguments into
      registers, and push the rest onto the stack.  Loop thru args
      from first to last.  */
@@ -1228,9 +1268,11 @@ mips_push_arguments(nargs, args, sp, struct_return, struct_addr)
 
       /* The EABI passes structures that do not fit in a register by
         reference. In all other cases, pass the structure by value.  */
-      if (typecode == TYPE_CODE_STRUCT && MIPS_EABI && len > MIPS_REGSIZE)
+      if (MIPS_EABI && len > MIPS_REGSIZE &&
+         (typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION))
        {
          store_address (valbuf, MIPS_REGSIZE, VALUE_ADDRESS (arg));
+         typecode = TYPE_CODE_PTR;
          len = MIPS_REGSIZE;
          val = valbuf;
        }
@@ -1246,7 +1288,12 @@ mips_push_arguments(nargs, args, sp, struct_return, struct_addr)
       /* Floating point arguments passed in registers have to be
          treated specially.  On 32-bit architectures, doubles
         are passed in register pairs; the even register gets
-        the low word, and the odd register gets the high word.  */
+        the low word, and the odd register gets the high word.
+        On non-EABI processors, the first two floating point arguments are
+        also copied to general registers, because MIPS16 functions
+        don't use float registers for arguments.  This duplication of
+        arguments in general registers can't hurt non-MIPS16 functions
+        because those registers are normally skipped.  */
       if (typecode == TYPE_CODE_FLT
          && float_argreg <= MIPS_LAST_FP_ARG_REGNUM
          && mips_fpu != MIPS_FPU_NONE)
@@ -1256,44 +1303,95 @@ mips_push_arguments(nargs, args, sp, struct_return, struct_addr)
              int low_offset = TARGET_BYTE_ORDER == BIG_ENDIAN ? 4 : 0;
              unsigned long regval;
 
+             /* Write the low word of the double to the even register(s).  */
              regval = extract_unsigned_integer (val+low_offset, 4);
-             write_register (float_argreg++, regval);  /* low word */
+             write_register (float_argreg++, regval);
+             if (!MIPS_EABI)
+               write_register (argreg+1, regval);
+
+             /* Write the high word of the double to the odd register(s).  */
              regval = extract_unsigned_integer (val+4-low_offset, 4);
-             write_register (float_argreg++, regval);  /* high word */
+             write_register (float_argreg++, regval);
+             if (!MIPS_EABI)
+               {
+                 write_register (argreg, regval);
+                 argreg += 2;
+               }
 
            }
          else
            {
+             /* This is a floating point value that fits entirely
+                in a single register.  */
              CORE_ADDR regval = extract_address (val, len);
              write_register (float_argreg++, regval);
+             if (!MIPS_EABI)
+               {
+                 write_register (argreg, regval);
+                 argreg += GDB_TARGET_IS_MIPS64 ? 1 : 2;
+               }
            }
-
-         /* If this is the old ABI, skip one or two general registers.  */
-         if (!MIPS_EABI)
-           argreg += GDB_TARGET_IS_MIPS64 ? 1 : 2;
        }
       else
        {
          /* Copy the argument to general registers or the stack in
             register-sized pieces.  Large arguments are split between
             registers and stack.  */
+         /* Note: structs whose size is not a multiple of MIPS_REGSIZE
+            are treated specially: Irix cc passes them in registers
+            where gcc sometimes puts them on the stack.  For maximum
+            compatibility, we will put them in both places.  */
+
+         int odd_sized_struct = ((len > MIPS_REGSIZE) && 
+                                 (len % MIPS_REGSIZE != 0));
          while (len > 0)
            {
              int partial_len = len < MIPS_REGSIZE ? len : MIPS_REGSIZE;
 
+             if (argreg > MIPS_LAST_ARG_REGNUM || odd_sized_struct)
+               {
+                 /* Write this portion of the argument to the stack.  */
+                 int longword_offset;
+
+                 longword_offset = 0;
+                 if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+                   if (MIPS_REGSIZE == 8 &&
+                       (typecode == TYPE_CODE_INT ||
+                        typecode == TYPE_CODE_PTR ||
+                        typecode == TYPE_CODE_FLT) && len <= 4)
+                     longword_offset = 4;
+                   else if ((typecode == TYPE_CODE_STRUCT ||
+                             typecode == TYPE_CODE_UNION) &&
+                            TYPE_LENGTH (arg_type) < MIPS_REGSIZE)
+                     longword_offset = MIPS_REGSIZE - len;
+
+                 write_memory (sp + stack_offset + longword_offset, 
+                               val, partial_len);
+               }
+
+             /* Note!!! This is NOT an else clause.
+                Odd sized structs may go thru BOTH paths.  */
              if (argreg <= MIPS_LAST_ARG_REGNUM)
                {
                  CORE_ADDR regval = extract_address (val, partial_len);
 
-                 /* It's a simple argument being passed in a general
-                    register.
-                    If the argument length is smaller than the register size,
-                    we have to adjust the argument on big endian targets.
-                    But don't do this adjustment on EABI targets. */
-                 if (TARGET_BYTE_ORDER == BIG_ENDIAN
-                     && partial_len < MIPS_REGSIZE
-                     && !MIPS_EABI)
-                   regval <<= ((MIPS_REGSIZE - partial_len) * TARGET_CHAR_BIT);
+                 /* A non-floating-point argument being passed in a 
+                    general register.  If a struct or union, and if
+                    small enough for a single register, we have to 
+                    adjust the alignment.
+
+                    It does not seem to be necessary to do the
+                    same for integral types.
+
+                    Also don't do this adjustment on EABI targets.  */
+
+                 if (!MIPS_EABI &&
+                     TYPE_LENGTH (arg_type) < MIPS_REGSIZE &&
+                     (typecode == TYPE_CODE_STRUCT ||
+                      typecode == TYPE_CODE_UNION))
+                   regval <<= ((MIPS_REGSIZE - partial_len) * 
+                               TARGET_CHAR_BIT);
+
                  write_register (argreg, regval);
                  argreg++;
     
@@ -1303,16 +1401,21 @@ mips_push_arguments(nargs, args, sp, struct_return, struct_addr)
                  if (!MIPS_EABI)
                    float_argreg = MIPS_LAST_FP_ARG_REGNUM + 1;
                }
-             else
-               {
-                 /* Write this portion of the argument to the stack.  */
-                 partial_len = len;
-                 write_memory (sp + stack_offset, val, partial_len);
-                 stack_offset += ROUND_UP (partial_len, MIPS_REGSIZE);
-               }
     
              len -= partial_len;
              val += partial_len;
+
+             /* The offset onto the stack at which we will start
+                copying parameters (after the registers are used up) 
+                begins at (4 * MIPS_REGSIZE) in the old ABI.  This 
+                leaves room for the "home" area for register parameters.
+
+                In the new EABI, the 8 register parameters do not 
+                have "home" stack space reserved for them, so the
+                stack offset does not get incremented until after
+                we have used up the 8 parameter registers.  */
+             if (!(MIPS_EABI && argnum < 8))
+               stack_offset += ROUND_UP (partial_len, MIPS_REGSIZE);
            }
        }
     }
@@ -1325,7 +1428,7 @@ mips_push_arguments(nargs, args, sp, struct_return, struct_addr)
   return sp;
 }
 
-void
+static void
 mips_push_register(CORE_ADDR *sp, int regno)
 {
   char buffer[MAX_REGISTER_RAW_SIZE];
@@ -1482,17 +1585,16 @@ mips_print_register (regnum, all)
       return;
     }
 
-  /* If an even floating pointer register, also print as double. */
+  /* If an even floating point register, also print as double. */
   if (regnum >= FP0_REGNUM && regnum < FP0_REGNUM+MIPS_NUMREGS
       && !((regnum-FP0_REGNUM) & 1))
     {
-      char dbuffer[MAX_REGISTER_RAW_SIZE]; 
+      char dbuffer[2 * MAX_REGISTER_RAW_SIZE]; 
 
       read_relative_register_raw_bytes (regnum, dbuffer);
-      read_relative_register_raw_bytes (regnum+1, dbuffer+4); /* FIXME!! */
-#ifdef REGISTER_CONVERT_TO_TYPE
-      REGISTER_CONVERT_TO_TYPE(regnum, builtin_type_double, dbuffer);
-#endif
+      read_relative_register_raw_bytes (regnum+1, dbuffer+MIPS_REGSIZE);
+      REGISTER_CONVERT_TO_TYPE (regnum, builtin_type_double, dbuffer);
+
       printf_filtered ("(d%d: ", regnum-FP0_REGNUM);
       val_print (builtin_type_double, dbuffer, 0,
                 gdb_stdout, 0, 1, 0, Val_pretty_default);
@@ -1632,14 +1734,9 @@ mips32_skip_prologue (pc, lenient)
        or in the gcc frame.  */
     for (end_pc = pc + 100; pc < end_pc; pc += MIPS_INSTLEN)
       {
-       char buf[MIPS_INSTLEN];
-       int status;
        unsigned long high_word;
 
-       status = read_memory_nobpt (pc, buf, MIPS_INSTLEN);
-       if (status)
-         memory_error (status, pc);
-       inst = (unsigned long)extract_unsigned_integer (buf, MIPS_INSTLEN);
+       inst = mips_fetch_instruction (pc);
        high_word = (inst >> 16) & 0xffff;
 
 #if 0
@@ -1724,6 +1821,8 @@ mips16_skip_prologue (pc, lenient)
      int lenient;
 {
     CORE_ADDR end_pc;
+    int extend_bytes = 0;
+    int prev_extend_bytes;
 
     /* Table of instructions likely to be found in a function prologue.  */
     static struct
@@ -1751,23 +1850,10 @@ mips16_skip_prologue (pc, lenient)
        or in the gcc frame.  */
     for (end_pc = pc + 100; pc < end_pc; pc += MIPS16_INSTLEN)
       {
-       char buf[MIPS16_INSTLEN];
-       int status;
        unsigned short inst;
-       int extend_bytes = 0;
-       int prev_extend_bytes;
        int i;
 
-       status = read_memory_nobpt (UNMAKE_MIPS16_ADDR (pc), buf,
-                                   MIPS16_INSTLEN);
-       if (status)
-         memory_error (status, pc);
-       inst = (unsigned long)extract_unsigned_integer (buf, MIPS16_INSTLEN);
-
-#if 0
-       if (lenient && is_delayed (inst))
-         continue;
-#endif
+       inst = mips_fetch_instruction (pc);
 
        /* Normally we ignore an extend instruction.  However, if it is
           not followed by a valid prologue instruction, we must adjust
@@ -1857,23 +1943,26 @@ mips_extract_return_value (valtype, regbuf, valbuf)
 {
   int regnum;
   int offset = 0;
+  int len = TYPE_LENGTH (valtype);
   
   regnum = 2;
   if (TYPE_CODE (valtype) == TYPE_CODE_FLT
-       && (mips_fpu == MIPS_FPU_DOUBLE
-          || (mips_fpu == MIPS_FPU_SINGLE && TYPE_LENGTH (valtype) <= 4))) /* FIXME!! */
+      && (mips_fpu == MIPS_FPU_DOUBLE
+         || (mips_fpu == MIPS_FPU_SINGLE && len <= MIPS_REGSIZE)))
     regnum = FP0_REGNUM;
 
-  if (TARGET_BYTE_ORDER == BIG_ENDIAN
-      && TYPE_CODE (valtype) != TYPE_CODE_FLT
-      && TYPE_LENGTH (valtype) < REGISTER_RAW_SIZE (regnum))
-    offset = REGISTER_RAW_SIZE (regnum) - TYPE_LENGTH (valtype);
-
-  memcpy (valbuf, regbuf + REGISTER_BYTE (regnum) + offset,
-         TYPE_LENGTH (valtype));
-#ifdef REGISTER_CONVERT_TO_TYPE
-  REGISTER_CONVERT_TO_TYPE(regnum, valtype, valbuf);
-#endif
+  if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+    { /* "un-left-justify" the value from the register */
+      if (len < REGISTER_RAW_SIZE (regnum))
+       offset = REGISTER_RAW_SIZE (regnum) - len;
+      if (len > REGISTER_RAW_SIZE (regnum)     &&      /* odd-size structs */
+         len < REGISTER_RAW_SIZE (regnum) * 2 &&
+         (TYPE_CODE (valtype) == TYPE_CODE_STRUCT ||
+          TYPE_CODE (valtype) == TYPE_CODE_UNION))
+       offset = 2 * REGISTER_RAW_SIZE (regnum) - len;
+    }
+  memcpy (valbuf, regbuf + REGISTER_BYTE (regnum) + offset, len);
+  REGISTER_CONVERT_TO_TYPE (regnum, valtype, valbuf);
 }
 
 /* Given a return value in `regbuf' with a type `valtype', 
@@ -1884,21 +1973,31 @@ mips_store_return_value (valtype, valbuf)
     char *valbuf;
 {
   int regnum;
+  int offset = 0;
+  int len = TYPE_LENGTH (valtype);
   char raw_buffer[MAX_REGISTER_RAW_SIZE];
   
   regnum = 2;
   if (TYPE_CODE (valtype) == TYPE_CODE_FLT
-       && (mips_fpu == MIPS_FPU_DOUBLE
-          || (mips_fpu == MIPS_FPU_SINGLE && TYPE_LENGTH (valtype) <= 4))) /* FIXME!! */
+      && (mips_fpu == MIPS_FPU_DOUBLE
+         || (mips_fpu == MIPS_FPU_SINGLE && len <= MIPS_REGSIZE)))
     regnum = FP0_REGNUM;
 
-  memcpy(raw_buffer, valbuf, TYPE_LENGTH (valtype));
-
-#ifdef REGISTER_CONVERT_FROM_TYPE
+  if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+    { /* "left-justify" the value in the register */
+      if (len < REGISTER_RAW_SIZE (regnum))
+       offset = REGISTER_RAW_SIZE (regnum) - len;
+      if (len > REGISTER_RAW_SIZE (regnum)     &&      /* odd-size structs */
+         len < REGISTER_RAW_SIZE (regnum) * 2 &&
+         (TYPE_CODE (valtype) == TYPE_CODE_STRUCT ||
+          TYPE_CODE (valtype) == TYPE_CODE_UNION))
+       offset = 2 * REGISTER_RAW_SIZE (regnum) - len;
+    }
+  memcpy(raw_buffer + offset, valbuf, len);
   REGISTER_CONVERT_FROM_TYPE(regnum, valtype, raw_buffer);
-#endif
-
-  write_register_bytes(REGISTER_BYTE (regnum), raw_buffer, TYPE_LENGTH (valtype));
+  write_register_bytes(REGISTER_BYTE (regnum), raw_buffer, 
+                      len > REGISTER_RAW_SIZE (regnum) ? 
+                      len : REGISTER_RAW_SIZE (regnum));
 }
 
 /* Exported procedure: Is PC in the signal trampoline code */
@@ -2126,8 +2225,19 @@ unsigned char *mips_breakpoint_from_pc (pcptr, lenptr)
       else
        {
          static char big_breakpoint[] = BIG_BREAKPOINT;
+         static char pmon_big_breakpoint[] = PMON_BIG_BREAKPOINT;
+         static char idt_big_breakpoint[] = IDT_BIG_BREAKPOINT;
+
          *lenptr = sizeof(big_breakpoint);
-         return big_breakpoint;
+
+         if (strcmp (target_shortname, "mips") == 0)
+           return idt_big_breakpoint;
+         else if (strcmp (target_shortname, "ddb") == 0
+                  || strcmp (target_shortname, "pmon") == 0
+                  || strcmp (target_shortname, "lsi") == 0)
+           return pmon_big_breakpoint;
+         else
+           return big_breakpoint;
        }
     }
   else
@@ -2142,8 +2252,19 @@ unsigned char *mips_breakpoint_from_pc (pcptr, lenptr)
       else
        {
          static char little_breakpoint[] = LITTLE_BREAKPOINT;
+         static char pmon_little_breakpoint[] = PMON_LITTLE_BREAKPOINT;
+         static char idt_little_breakpoint[] = IDT_LITTLE_BREAKPOINT;
+
          *lenptr = sizeof(little_breakpoint);
-         return little_breakpoint;
+
+         if (strcmp (target_shortname, "mips") == 0)
+           return idt_little_breakpoint;
+         else if (strcmp (target_shortname, "ddb") == 0
+                  || strcmp (target_shortname, "pmon") == 0
+                  || strcmp (target_shortname, "lsi") == 0)
+           return pmon_little_breakpoint;
+         else
+           return little_breakpoint;
        }
     }
 }
@@ -2162,9 +2283,189 @@ mips_about_to_return (pc)
        as $a3), then a "jr" using that register.  This second case
        is almost impossible to distinguish from an indirect jump
        used for switch statements, so we don't even try.  */
-    return read_memory_integer (UNMAKE_MIPS16_ADDR (pc), 2) == 0xe820; /* jr $ra */
+    return mips_fetch_instruction (pc) == 0xe820;      /* jr $ra */
   else
-    return read_memory_integer (pc, 4) == 0x3e00008;   /* jr $ra */
+    return mips_fetch_instruction (pc) == 0x3e00008;   /* jr $ra */
+}
+
+
+/* If PC is in a mips16 call or return stub, return the address of the target
+   PC, which is either the callee or the caller.  There are several
+   cases which must be handled:
+
+   * If the PC is in __mips16_ret_{d,s}f, this is a return stub and the
+     target PC is in $31 ($ra).
+   * If the PC is in __mips16_call_stub_{1..10}, this is a call stub
+     and the target PC is in $2.
+   * If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
+     before the jal instruction, this is effectively a call stub
+     and the the target PC is in $2.  Otherwise this is effectively
+     a return stub and the target PC is in $18.
+
+   See the source code for the stubs in gcc/config/mips/mips16.S for
+   gory details.
+
+   This function implements the SKIP_TRAMPOLINE_CODE macro.
+*/
+
+CORE_ADDR
+mips_skip_stub (pc)
+     CORE_ADDR pc;
+{
+  char *name;
+  CORE_ADDR start_addr;
+
+  /* Find the starting address and name of the function containing the PC.  */
+  if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0)
+    return 0;
+
+  /* If the PC is in __mips16_ret_{d,s}f, this is a return stub and the
+     target PC is in $31 ($ra).  */
+  if (strcmp (name, "__mips16_ret_sf") == 0
+      || strcmp (name, "__mips16_ret_df") == 0)
+    return read_register (RA_REGNUM);
+
+  if (strncmp (name, "__mips16_call_stub_", 19) == 0)
+    {
+      /* If the PC is in __mips16_call_stub_{1..10}, this is a call stub
+         and the target PC is in $2.  */
+      if (name[19] >= '0' && name[19] <= '9')
+       return read_register (2);
+
+      /* If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
+        before the jal instruction, this is effectively a call stub
+        and the the target PC is in $2.  Otherwise this is effectively
+        a return stub and the target PC is in $18.  */
+      else if (name[19] == 's' || name[19] == 'd')
+       {
+         if (pc == start_addr)
+           {
+             /* Check if the target of the stub is a compiler-generated
+                stub.  Such a stub for a function bar might have a name
+                like __fn_stub_bar, and might look like this:
+                     mfc1    $4,$f13
+                     mfc1    $5,$f12
+                     mfc1    $6,$f15
+                     mfc1    $7,$f14
+                     la      $1,bar   (becomes a lui/addiu pair)
+                     jr      $1
+                So scan down to the lui/addi and extract the target
+                address from those two instructions.  */
+
+             CORE_ADDR target_pc = read_register (2);
+             t_inst inst;
+             int i;
+
+             /* See if the name of the target function is  __fn_stub_*.  */
+             if (find_pc_partial_function (target_pc, &name, NULL, NULL) == 0)
+               return target_pc;
+             if (strncmp (name, "__fn_stub_", 10) != 0
+                 && strcmp (name, "etext") != 0
+                 && strcmp (name, "_etext") != 0)
+               return target_pc;
+
+             /* Scan through this _fn_stub_ code for the lui/addiu pair.
+                The limit on the search is arbitrarily set to 20
+                instructions.  FIXME.  */
+             for (i = 0, pc = 0; i < 20; i++, target_pc += MIPS_INSTLEN)
+               {
+                  inst = mips_fetch_instruction (target_pc);
+                  if ((inst & 0xffff0000) == 0x3c010000)       /* lui $at */
+                     pc = (inst << 16) & 0xffff0000;           /* high word */
+                  else if ((inst & 0xffff0000) == 0x24210000)  /* addiu $at */
+                     return pc | (inst & 0xffff);              /* low word */
+               }
+
+             /* Couldn't find the lui/addui pair, so return stub address.  */
+             return target_pc;
+           }
+         else
+           /* This is the 'return' part of a call stub.  The return
+              address is in $r18.  */
+           return read_register (18);
+       }
+    }
+  return 0;    /* not a stub */
+}
+
+
+/* Return non-zero if the PC is inside a call thunk (aka stub or trampoline).
+   This implements the IN_SOLIB_CALL_TRAMPOLINE macro.  */
+
+int
+mips_in_call_stub (pc, name)
+     CORE_ADDR pc;
+     char *name;
+{
+  CORE_ADDR start_addr;
+
+  /* Find the starting address of the function containing the PC.  If the
+     caller didn't give us a name, look it up at the same time.  */
+  if (find_pc_partial_function (pc, name ? NULL : &name, &start_addr, NULL) == 0)
+    return 0;
+
+  if (strncmp (name, "__mips16_call_stub_", 19) == 0)
+    {
+      /* If the PC is in __mips16_call_stub_{1..10}, this is a call stub.  */
+      if (name[19] >= '0' && name[19] <= '9')
+       return 1;
+      /* If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
+        before the jal instruction, this is effectively a call stub.  */
+      else if (name[19] == 's' || name[19] == 'd')
+       return pc == start_addr;
+    }
+
+  return 0;    /* not a stub */
+}
+
+
+/* Return non-zero if the PC is inside a return thunk (aka stub or trampoline).
+   This implements the IN_SOLIB_RETURN_TRAMPOLINE macro.  */
+
+int
+mips_in_return_stub (pc, name)
+     CORE_ADDR pc;
+     char *name;
+{
+  CORE_ADDR start_addr;
+
+  /* Find the starting address of the function containing the PC.  */
+  if (find_pc_partial_function (pc, NULL, &start_addr, NULL) == 0)
+    return 0;
+
+  /* If the PC is in __mips16_ret_{d,s}f, this is a return stub.  */
+  if (strcmp (name, "__mips16_ret_sf") == 0
+      || strcmp (name, "__mips16_ret_df") == 0)
+    return 1;
+
+  /* If the PC is in __mips16_call_stub_{s,d}f_{0..10} but not at the start,
+      i.e. after the jal instruction, this is effectively a return stub.  */
+  if (strncmp (name, "__mips16_call_stub_", 19) == 0
+      && (name[19] == 's' || name[19] == 'd')
+      && pc != start_addr)
+    return 1;
+
+  return 0;    /* not a stub */
+}
+
+
+/* Return non-zero if the PC is in a library helper function that should
+   be ignored.  This implements the IGNORE_HELPER_CALL macro.  */
+
+int
+mips_ignore_helper (pc)
+     CORE_ADDR pc;
+{
+  char *name;
+
+  /* Find the starting address and name of the function containing the PC.  */
+  if (find_pc_partial_function (pc, &name, NULL, NULL) == 0)
+    return 0;
+
+  /* If the PC is in __mips16_ret_{d,s}f, this is a library helper function
+     that we want to ignore.  */
+  return (strcmp (name, "__mips16_ret_sf") == 0
+         || strcmp (name, "__mips16_ret_df") == 0);
 }
 
 
This page took 0.035222 seconds and 4 git commands to generate.