| /* Target dependent code for ARC700, for GDB, the GNU debugger. |
| |
| Copyright 2005 Free Software Foundation, Inc. |
| |
| Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) |
| |
| Authors: |
| Soam Vasani <soam.vasani@codito.com> |
| Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| */ |
| #include <string.h> |
| |
| #include "defs.h" |
| #include "arch-utils.h" |
| #include "dis-asm.h" |
| #include "gdbtypes.h" |
| #include "frame.h" |
| #include "frame-unwind.h" |
| #include "target.h" |
| #include "breakpoint.h" |
| #include "inferior.h" |
| #include "regcache.h" |
| #include "reggroups.h" |
| #include "trad-frame.h" |
| #include "dwarf2-frame.h" |
| #include "gdbtypes.h" |
| #include "gdb_assert.h" |
| #include "gdbcore.h" |
| #include "observer.h" |
| #include "osabi.h" |
| |
| #include "opcode/arc.h" |
| |
| #include "arc-tdep.h" |
| |
| |
| //#define ARC_DEBUG 1 |
| |
| #if ARC_DEBUG |
| # define ENTERMSG printf ("--- entered %s:%s()\n", __FILE__, __FUNCTION__) |
| # define ENTERARGS(fmt, args...) printf ("--- entered %s:%s(" fmt ")\n", __FILE__, __FUNCTION__, args) |
| # define LEAVEMSG printf ("--- exited %s:%s()\n", __FILE__, __FUNCTION__) |
| #else |
| # define ENTERMSG |
| # define ENTERARGS(fmt, args...) |
| # define LEAVEMSG |
| #endif |
| |
| #define ARC_STATUS32_A1 0x8 |
| #define ARC_STATUS32_A2 0x10 |
| #define ARC_STATUS32_AE 0x20 |
| #define ARC_STATUS32_L 0x100 |
| |
| |
| static CORE_ADDR arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp); |
| /* The frame unwind cache for the ARC |
| */ |
| |
| struct arc_unwind_cache |
| { |
| /* blink offset in stack */ |
| int blink_offset ; |
| |
| /* Caller's PC */ |
| CORE_ADDR prev_pc; |
| |
| |
| /* The stack pointer at the time this frame was created; i.e. the |
| caller's stack pointer when this function was called. It is used |
| to identify this frame. */ |
| |
| CORE_ADDR prev_sp; |
| /* The frame base */ |
| CORE_ADDR frame_base; |
| /* Frame size */ |
| int framesize; |
| |
| /* offset of sp from the stack frame base */ |
| LONGEST sp_offset; |
| /* offset of fp from the stack frame base */ |
| LONGEST fp_offset; |
| /* Is this a leaf function */ |
| int is_leaf ; |
| /* Is there a frame pointer */ |
| int uses_fp; |
| |
| |
| /* Offsets for each register in the stack frame */ |
| struct trad_frame_saved_reg *saved_regs; |
| }; |
| |
| |
| |
| |
| /* Function Prototypes */ |
| |
| static CORE_ADDR arc_unwind_sp (struct gdbarch *gdbarch, |
| struct frame_info *next_frame); |
| |
| static CORE_ADDR arc_unwind_pc (struct gdbarch *gdbarch, |
| struct frame_info *next_frame); |
| |
| static struct arc_unwind_cache * arc_frame_unwind_cache (struct frame_info *next_frame, |
| void ** this_prologue); |
| |
| static CORE_ADDR arc_scan_prologue (CORE_ADDR pc, |
| struct frame_info *next_frame, |
| struct arc_unwind_cache *info); |
| |
| |
| static int arc_binutils_reg_to_regnum (int reg); |
| |
| extern struct arcDisState arcAnalyzeInstr ( bfd_vma address,disassemble_info* info ); |
| extern struct arcDisState a4AnalyzeInstr ( bfd_vma address,disassemble_info* info ); |
| |
| /* defined in opcodes, but there's no header file with this prototype... */ |
| disassembler_ftype arcompact_get_disassembler (void *); |
| |
| /* Standard register type for the ARC platform . |
| * It would be builtin_type_uint32 until |
| * we consider the DSP extensions |
| */ |
| |
| static struct type * |
| arc_register_type (struct gdbarch *gdbarch, int regnum) |
| { |
| return builtin_type_uint32; |
| } |
| |
| |
| void printFrameInfo(struct arc_unwind_cache * info) |
| { |
| #ifdef ARC_DEBUG |
| printf("-------------------\n"); |
| printf("%lx \n",info ); |
| printf("prev_sp = %lx \n",info->prev_sp); |
| printf("prev_pc = %lx \n",info->prev_pc); |
| printf("frame_base is %lx \n",info->frame_base); |
| printf("framesize is %lx \n",info->framesize); |
| printf("Blink offset %lx \n",info->blink_offset); |
| printf("sp_offset = %lx \n",info->sp_offset ); |
| printf("fp_offset is %lx \n",info->fp_offset); |
| printf("is_leaf = %d, uses_fp=%d",info->is_leaf, info->uses_fp); |
| #endif |
| } |
| |
| /* Print the instruction state returned |
| by the disassembler . Used for internal |
| debugging only |
| */ |
| |
| void printInsnState(struct arcDisState state) |
| { |
| #ifdef ARC_DEBUG |
| printf("---------------------------------\n"); |
| printf("Instruction Length %d\n", state.instructionLen); |
| printf("Opcode [0x%x] : Cond [%x]\n", state._opcode, state._cond); |
| printf("Words 1 [%lx] : 2 [%lx]\n", state.words[0], state.words[1]); |
| printf("ea present [%x] : memload [%x]\n", state._ea_present, state._mem_load); |
| printf("Load length [%d]:\n", state._load_len); |
| printf("Address writeback [%d]\n", state._addrWriteBack); |
| printf("ea reg1 is [%x] offset [%x] \n", state.ea_reg1, state._offset); |
| printf("ea reg2 is [%x] \n", state.ea_reg2); |
| printf("operands buffer is %s \n", state.operandBuffer); |
| printf("SourceType is %d \n",state.sourceType); |
| printf("Flow is %d\n",state.flow); |
| printf("Branch is %d,'%c'\n",state.isBranch, state.isBranch); |
| #endif |
| } |
| |
| |
| /* Scan the prologue and update the |
| * corresponding frame cache for the frame unwinder for unwinding |
| * frames without debug info . In such a situation GDB attempts to |
| * parse the prologue for this purpose . This currently would attempt |
| * to parse the prologue generated by our gcc 2.95 .(We should support |
| * Metaware generated binaries at some suitable point of time ) |
| * This function is called with the pc where gdb stopped , the next_frame |
| * to be filled in (if need be?) and the existing cached info . |
| |
| * scan_prologue is called by our unwinder as well |
| * as from skip_prologue in the case that it cannot detect |
| * the end of the prologue. next_frame is set to NULL if we are called from |
| * arc_skip_prologue in an attempt to discover the end of the prologue. In |
| * such a case we don't fill the frame info that is passed to us :-) |
| |
| * Todos. |
| * 1. Support 32 bit normal frames generated by GCC 2.95 . |
| * 2. Support 16 and 32 bit mixed frames generated by GCC 2.95 |
| * 3. Support 32 bit normal variadic function frames by GCC 2.95 |
| * 4. Support 32 bit normal frames from GCC 3.4.x with variadic args |
| * 5. Support 16 and 32 bit normal frames from GCC 3.4.x with variadic args |
| * 6. Support 16 and 32 bit mixed frames generated by GCC 3.4.x |
| * 7. Support Metaware generated prologues .( The difference is |
| * in the use of thunks to identify the saving and restoring of |
| * callee saves :-) May have to do some hackery even in next_pc. |
| * since the call is going to create its own set of problems |
| * with our stack setup :-( |
| * We attempt to use the disassembler interface from the opcodes |
| * library to do our disassembling . |
| |
| * The usual 32 bit normal |
| * gcc -O0 prologue looks like this. |
| |
| * Complete Prologue for all GCC frames (Cases #1 to #6 in Todos above) |
| |
| * sub sp, sp, limm ; space for variadic arguments. |
| * st.a blink, [sp,-4] ; push blink (if not a leaf function) |
| * sub sp, sp , limm ; (optional space creation for callee saves ) |
| * st r13, [sp] ; pushes of all callee saves. |
| * st r14, [sp,4] ; pushes of more callee saves. |
| * XXXX |
| * st.a fp , [sp,-4] ; push fp (if fp has to be saved ) |
| * mov fp , sp ; Set the current frame up correctly |
| * sub sp , sp , #immediate ; Create space for local vars on the stack. |
| */ |
| |
| |
| |
| /* Macros to be used with disassembling the prologue |
| * and update the frame info.The *FI macros are to update |
| * the frame info and the ACT macros are to actually do the |
| * action on a corresponding match. |
| * |
| */ |
| #define CHECKOPDSTRING(targetcheck,searchstring) \ |
| if(strstr(targetcheck,searchstring) == targetcheck) \ |
| {continue;} |
| |
| #define CHECKOPDSTRINGANDACT(targetcheck,searchstring,action) \ |
| if(strstr(targetcheck,searchstring) == targetcheck) \ |
| {\ |
| action;\ |
| continue;} |
| |
| |
| /* The frame info changes by changing the decrementing |
| the sp_offset and setting the leaf function to be NIL; |
| Also the offset of the blink register from the previous |
| value of sp is calculated. Finally this can be updated |
| as |
| info->blink_offset = info-> prev_sp + info->blink_offset ; |
| Also the addition below is coz the offsets are usually negative |
| */ |
| |
| #define PUSHBLINKACT do { \ |
| if(info) \ |
| { \ |
| info->sp_offset += current_instr._offset; \ |
| info->blink_offset = info->sp_offset ; \ |
| info->is_leaf = 0;\ |
| }}while(0); |
| |
| |
| #define ISPUSHBLINK(state) CHECKOPDSTRING(state.operandBuffer,"blink") |
| #define ISPUSHBLINKFI(state) CHECKOPDSTRINGANDACT(state.operandBuffer,"blink",PUSHBLINKACT) |
| |
| |
| #define PUSHFPACT do { \ |
| if(info) \ |
| { \ |
| info->sp_offset += current_instr._offset ; \ |
| info->fp_offset = -info->sp_offset; \ |
| }}while(0); |
| |
| #define ISPUSHFP(state) CHECKOPDSTRING(state.operandBuffer,"fp") |
| #define ISPUSHFPFI(state) CHECKOPDSTRINGANDACT(state.operandBuffer,"fp",PUSHFPACT) |
| #define ISINSTRUCTION(insnname,searchstring) !strcmp(insnname,searchstring) |
| |
| |
| |
| #define UPDATEFPACT do {\ |
| if(info) {\ |
| info->uses_fp = 1;\ |
| }}while(0); |
| |
| |
| |
| #define ISUPDATEFPFI(state) \ |
| if(ISINSTRUCTION(state.instrBuffer,"mov")) \ |
| { \ |
| CHECKOPDSTRINGANDACT(state.operandBuffer,"fp,sp",UPDATEFPACT); \ |
| } |
| |
| |
| |
| #define ISUPDATEFP(state) \ |
| if(ISINSTRUCTION(state.instrBuffer,"mov")) \ |
| { \ |
| CHECKOPDSTRING(state.operandBuffer,"fp,sp") \ |
| } |
| |
| |
| |
| #define ISSUBSP(state) \ |
| if(ISINSTRUCTION(state.instrBuffer,"sub"))\ |
| { \ |
| CHECKOPDSTRING(state.operandBuffer,"sp,sp") \ |
| } |
| |
| #define UPDATESTACKSPACE(state) do { \ |
| if(info){ \ |
| /* Eat up sp,sp */ \ |
| int tmp = atoi(state.operandBuffer + 6); \ |
| info->sp_offset -= tmp; \ |
| }}while(0); |
| |
| |
| #define ISSUBSPFI(state) \ |
| if(ISINSTRUCTION(state.instrBuffer,"sub") \ |
| || ISINSTRUCTION(state.instrBuffer,"sub_s"))\ |
| { \ |
| CHECKOPDSTRINGANDACT(state.operandBuffer,"sp,sp",UPDATESTACKSPACE(state)) \ |
| } |
| |
| /*Function to scan the prologue of a A4 binary |
| |
| ARCtangent-A4 Prolog |
| The stack back-trace data structure is a 16-byte structure which is |
| used to save the return register (blink, 4 bytes), the frame pointer |
| register (fp, 4-bytes) and 8-bytes is reserved. |
| |
| The compiler-generated prolog code does the following: |
| --> Allocates space for register arguments in case of variadic function |
| (functions with variable argument lists) |
| --> Saves the return address register (blink) |
| --> Saves the caller's frame pointer (fp), if required, and |
| sets the new frame pointer to this location |
| --> Decrements the stack pointer to account for the new stack frame |
| --> Saves required non-volatile general-purpose registers into the |
| register save area |
| --> Stores the arguments above the stack back-trace data structure |
| |
| |
| Demo Patterns: |
| st blink,[sp,4] ; Saves the return address |
| st fp,[sp] ; Saves the callers frame pointer |
| mov fp,sp ; Saves |
| sub sp,sp,24 |
| |
| 0xa 538e7e20 sub sp,sp,32 ; Space for variadic args |
| 0x2 100e3e04 st blink,[sp,4] ; Saves the return address |
| 0x2 100e3600 st fp,[sp] ; Saves the callers frame pointer |
| 0xc 636e3800 mov fp,sp ; Resets stack pointer to fp |
| 0xa 538e7e18 sub sp,sp,24 ; Decrements sp to add for new |
| ; stack frame |
| 0x2 100d81fc st r0,[fp,-4] ; Stores the args |
| 0x2 100d83f8 st r1,[fp,-8] ; ----"------- |
| ... |
| |
| */ |
| |
| /* FIXMEA: |
| called from arc_skip_prologue as |
| skip_pc = arc_scan_prologue(pc,NULL,NULL); |
| Then it is supposed to return the first valid pc |
| after the prologue |
| |
| Prologue analysis does the rest... |
| Currently our scan prologue does not |
| support getting input for the frame unwinder |
| |
| |
| pc = frame_func_unwind(next_frame); |
| arc_scan_prologue (pc, next_frame, info); |
| |
| */ |
| |
| #ifdef ARC4_JTAG |
| static CORE_ADDR |
| a4_scan_prologue (CORE_ADDR pc, struct frame_info *next_frame, |
| struct arc_unwind_cache *info) |
| { |
| /* End of prologue */ |
| CORE_ADDR prologue_ends_pc = pc; |
| int i = 0; |
| struct arcDisState current_instr, instr_in_delay; |
| int insn_length; |
| |
| /* Initializations to use the opcodes |
| * library . |
| */ |
| |
| struct disassemble_info di; |
| |
| unsigned int saved_regs_mask = 0; |
| /* An arbitrary length on the length of the |
| prologue. If next_frame is NULL this means that there was |
| no debug info and we are called from arc_skip_prologue |
| */ |
| /*FIXMEANOW: pc + 64 is probably the max size of the prologue*/ |
| CORE_ADDR final_pc = (next_frame)?frame_pc_unwind(next_frame):pc+(16*4); |
| |
| |
| |
| |
| if (info) |
| { |
| /* All functions start as leaf functions until |
| we identify push blink |
| */ |
| info->is_leaf = 1; |
| } |
| |
| |
| |
| /* Initializations to use the opcodes |
| * library . |
| */ |
| init_disassemble_info(&di, gdb_stderr, fprintf_unfiltered); |
| di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch; |
| di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach; |
| di.endian = gdbarch_byte_order(current_gdbarch); |
| di.read_memory_func = target_read_memory; |
| |
| |
| for (prologue_ends_pc= pc; |
| prologue_ends_pc< final_pc; |
| prologue_ends_pc += current_instr.instructionLen ) /*FIXMEA: This could as |
| well be 4 */ |
| { |
| current_instr = a4AnalyzeInstr(prologue_ends_pc, &di); |
| printInsnState(current_instr); |
| #ifdef ARC_DEBUG |
| printf("Prologue PC: %d\n", prologue_ends_pc); |
| printf("Final PC: %d\n", final_pc); |
| #endif |
| |
| |
| if (current_instr._opcode == 0x2) |
| { |
| // Saves the return address st blink,[sp,4] 0x100e3e04 |
| // Save the callers fp st fp,[sp] 0x100e3600 |
| // Saves the args st rX,[fp, #imm] 0x100d8xxx |
| if (current_instr.ea_reg1 == 28) |
| { |
| if( strstr(current_instr.operandBuffer,"blink") == current_instr.operandBuffer) |
| { |
| if(info) |
| { |
| info->sp_offset += current_instr._offset; |
| // info->blink_offset = info->sp_offset ; |
| info->blink_offset = current_instr._offset; |
| info->is_leaf = 0; |
| |
| #ifdef ARC_DEBUG |
| printf("Blink instruction:\n"); |
| printFrameInfo(info); |
| #endif |
| } |
| continue; |
| } |
| else |
| if(strstr(current_instr.operandBuffer,"fp") == current_instr.operandBuffer) |
| { |
| if(info) |
| { |
| /* info->sp_offset += current_instr._offset ; */ |
| /* info->fp_offset = info->sp_offset; */ |
| info->fp_offset = 0; |
| } |
| continue; |
| } |
| } |
| else if (current_instr.ea_reg1 == 27) |
| { |
| /* Saving of arguments onto the stack using the |
| frame pointer (r27). */ |
| if(info) |
| { |
| // Save regs offsets |
| } |
| #ifdef ARC_DEBUG |
| printf(" Saving registers onto stack\n%s\n",current_instr.operandBuffer); |
| #endif |
| continue; |
| } |
| // ISPUSHBLINK(current_instr); |
| } |
| else if (current_instr._opcode == 0xc) |
| { |
| // Resets stack pointer to fp |
| // 0x636e3800 |
| // 636e3800 mov fp,sp |
| if (current_instr.words[0] == 0x636e3800) |
| { |
| if (info) |
| { |
| info->uses_fp = 1; |
| } |
| continue; |
| } |
| } |
| else if (current_instr._opcode == 0xa) |
| { |
| // Decrements stackpointer to add for new stack frame |
| // 0x538e7e18 sub sp,sp,#imm |
| // 538e7e20 sub sp,sp,32 |
| if( current_instr.words[0] == 0x538e7e20) |
| { |
| //sub sp,sp, 32 //// variadic |
| if (info) |
| { |
| int tmp = atoi(current_instr.operandBuffer + 6); |
| info->sp_offset -= tmp; |
| } |
| continue; |
| } |
| else if((current_instr.words[0] & 0xffffff00) == 0x538e7e00) |
| { |
| // sub sp,sp,xx |
| if(info) |
| { |
| int tmp = atoi(current_instr.operandBuffer + 6); |
| info->sp_offset -= tmp; |
| } |
| continue; |
| } |
| } |
| |
| /* Found a instruction that is not in |
| the prologue*/ |
| #ifdef ARC_DEBUG |
| printf("End of Prologue reached \n"); |
| #endif |
| break; |
| } |
| |
| /* Means we were called from skip_prologue */ |
| if((next_frame == NULL)&& (info == NULL)) |
| { |
| return prologue_ends_pc; |
| } |
| |
| |
| info->framesize = -info->sp_offset; |
| /* Compute the previous frame's stack pointer (which is also the |
| frame's ID's stack address), and this frame's base pointer. */ |
| if(info->uses_fp) |
| { |
| |
| ULONGEST this_base; |
| /* The SP was moved to the FP. This indicates that a new frame |
| was created. Get THIS frame's FP value by unwinding it from |
| the next frame. */ |
| frame_unwind_unsigned_register(next_frame, ARC_FP_REGNUM, |
| &this_base); |
| info->frame_base = this_base; |
| info->saved_regs[ARC_FP_REGNUM].addr = info->frame_base; |
| |
| /* The previous sp is the current frame base + the offset of the |
| fp in the current frame */ |
| info->prev_sp = info->frame_base + info->fp_offset; |
| for(i = 13; i < 26 ; i++ ) |
| { |
| if(saved_regs_mask & (1 << i)) |
| info->saved_regs[i].addr += info->frame_base ; |
| } |
| |
| printFrameInfo(info); |
| |
| } |
| else |
| { |
| ULONGEST this_base; |
| /* Assume that the FP is this frame's SP but with that pushed |
| stack space added back. */ |
| frame_unwind_unsigned_register (next_frame, ARC_SP_REGNUM, &this_base); |
| info->frame_base = this_base; |
| |
| /* In such a case it would be the previous SP + the size of the current frame */ |
| info->prev_sp = info->frame_base + info->framesize; |
| |
| } |
| |
| |
| if(!info->is_leaf) |
| { |
| |
| /* Usually blink is saved before the callee save registers and |
| below the space created for variadic arguments . We maintain |
| info->blink_offset as negative when we stored it initially |
| */ |
| info->saved_regs[ARC_BLINK_REGNUM].addr = info->prev_sp + info->blink_offset; |
| #ifdef ARC_DEBUG |
| printf("blink offset is [%x] \n",info->blink_offset); |
| #endif |
| } |
| |
| /* The PC is found in blink (the actual register or located on the stack). */ |
| // FIXMEA: |
| //info->saved_regs[ARC_STATUS_REGNUM] |= (info->saved_regs[ARC_BLINK_REGNUM] & 0xffffff)>>2; |
| info->saved_regs[ARC_STATUS_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM]; |
| return prologue_ends_pc; |
| |
| } |
| #endif |
| |
| static CORE_ADDR |
| arc_scan_prologue (CORE_ADDR pc, struct frame_info *next_frame, |
| struct arc_unwind_cache *info) |
| { |
| #ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf("\narc_scan_prologue called\n"); |
| #endif |
| #else |
| /* End of prologue */ |
| CORE_ADDR prologue_ends_pc = pc; |
| int i = 0; |
| struct arcDisState current_instr, instr_in_delay; |
| int insn_length; |
| |
| /* Initializations to use the opcodes |
| * library . |
| */ |
| |
| struct disassemble_info di; |
| |
| unsigned int saved_regs_mask = 0; |
| /* An arbitrary length on the length of the |
| prologue. If next_frame is NULL this means that there was |
| no debug info and we are called from arc_skip_prologue |
| */ |
| CORE_ADDR final_pc = (next_frame)?frame_pc_unwind(next_frame):pc+64; |
| |
| |
| |
| |
| if (info) |
| { |
| /* All functions start as leaf functions until |
| we identify push blink |
| */ |
| info->is_leaf = 1; |
| |
| } |
| |
| |
| |
| /* Initializations to use the opcodes |
| * library . |
| */ |
| init_disassemble_info(&di, gdb_stderr, fprintf_unfiltered); |
| di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch; |
| di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach; |
| di.endian = gdbarch_byte_order(current_gdbarch); |
| di.read_memory_func = target_read_memory; |
| |
| |
| for(prologue_ends_pc= pc; prologue_ends_pc< final_pc; |
| prologue_ends_pc += current_instr.instructionLen ) |
| { |
| current_instr = arcAnalyzeInstr(prologue_ends_pc, &di); |
| printInsnState(current_instr); |
| /* Might be a push or a pop */ |
| if(current_instr._opcode == 0x3) |
| { |
| if(current_instr._addrWriteBack) |
| { |
| /* This is a st.a */ |
| if((current_instr.ea_reg1 == 28) && |
| (current_instr._offset == -4)) |
| { |
| |
| /* This is a push something at sp */ |
| /* Is it a push of the blink */ |
| ISPUSHBLINKFI(current_instr); |
| /* Is it a push for fp */ |
| ISPUSHFPFI(current_instr); |
| |
| } |
| |
| } |
| else |
| { |
| /* Is this a store of some register onto |
| the stack using the stack pointer.*/ |
| if(current_instr.ea_reg1 == 28) |
| { |
| if(current_instr.sourceType == ARC_REGISTER ) |
| { |
| /* R13..R26 are the callee saved registers. [R27 (fp) |
| is also a callee saved register, but it's usually |
| pushed using st.a and so handled in the st.a case |
| above.] */ |
| if((current_instr.source_operand.registerNum > 12 |
| && current_instr.source_operand.registerNum <= 26)) |
| { |
| if(info) |
| { |
| printFrameInfo(info); |
| /* Save up the offsets for the correct instruction */ |
| info->saved_regs[current_instr.source_operand.registerNum].addr |
| = - info->sp_offset - current_instr._offset; |
| saved_regs_mask |= (1 << current_instr.source_operand.registerNum); |
| } |
| continue; |
| } |
| |
| } |
| |
| } |
| /* Is this the store of some register on the |
| stack using the frame pointer. We check |
| for argument registers getting saved and |
| restored. |
| */ |
| if(current_instr.ea_reg1 == 27) |
| if((current_instr.source_operand.registerNum <= 7)) |
| { |
| /* Saving argument registers.Don't save them in saved_regs, just skip. |
| */ |
| continue; |
| } |
| |
| |
| |
| } |
| } |
| |
| if(current_instr._opcode == 0x4) |
| { |
| /* A major opcode 0x4 instruction */ |
| /* We are usually interested in a |
| mov or a sub */ |
| ISUPDATEFPFI(current_instr); |
| ISSUBSPFI(current_instr); |
| } |
| if(current_instr._opcode == 0x18) |
| { |
| /* sub_s sp,sp,constant */ |
| ISSUBSPFI(current_instr); |
| /* push_s blink */ |
| if(strcmp(current_instr.instrBuffer,"push_s") == 0) |
| { |
| if(strcmp(current_instr.operandBuffer,"blink") == 0) |
| { |
| if(info) |
| { |
| info->sp_offset += 4; |
| info->blink_offset = info->sp_offset ; |
| info->is_leaf = 0; |
| } |
| continue; |
| } |
| } |
| } |
| |
| /* If we reach here . we have |
| * reached end of the prologue |
| */ |
| break; |
| |
| } |
| |
| /* Means we were called from skip_prologue */ |
| if((next_frame == NULL)&& (info == NULL)) |
| { |
| return prologue_ends_pc; |
| } |
| |
| |
| info->framesize = -info->sp_offset; |
| /* Compute the previous frame's stack pointer (which is also the |
| frame's ID's stack address), and this frame's base pointer. */ |
| if(info->uses_fp) |
| { |
| |
| ULONGEST this_base; |
| /* The SP was moved to the FP. This indicates that a new frame |
| was created. Get THIS frame's FP value by unwinding it from |
| the next frame. */ |
| frame_unwind_unsigned_register(next_frame, ARC_FP_REGNUM, |
| &this_base); |
| info->frame_base = this_base; |
| info->saved_regs[ARC_FP_REGNUM].addr = info->frame_base; |
| |
| /* The previous sp is the current frame base + the offset of the |
| fp in the current frame */ |
| info->prev_sp = info->frame_base + info->fp_offset; |
| for(i = 13; i < 26 ; i++ ) |
| { |
| if(saved_regs_mask & (1 << i)) |
| info->saved_regs[i].addr += info->frame_base ; |
| } |
| |
| printFrameInfo(info); |
| |
| } |
| else |
| { |
| ULONGEST this_base; |
| /* Assume that the FP is this frame's SP but with that pushed |
| stack space added back. */ |
| frame_unwind_unsigned_register (next_frame, ARC_SP_REGNUM, &this_base); |
| info->frame_base = this_base; |
| |
| /* In such a case it would be the previous SP + the size of the current frame */ |
| info->prev_sp = info->frame_base + info->framesize; |
| |
| } |
| |
| |
| if(!info->is_leaf) |
| { |
| |
| /* Usually blink is saved before the callee save registers and |
| below the space created for variadic arguments . We maintain |
| info->blink_offset as negative when we stored it initially |
| */ |
| info->saved_regs[ARC_BLINK_REGNUM].addr = info->prev_sp + info->blink_offset; |
| #ifdef ARC_DEBUG |
| printf("blink offset is [%x] \n",info->blink_offset); |
| #endif |
| } |
| |
| /* The PC is found in blink (the actual register or located on the stack). */ |
| info->saved_regs[PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM]; |
| /*info->saved_regs[ARC_PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM];*/ |
| return prologue_ends_pc; |
| #endif |
| } |
| |
| |
| /* Skip the prologue for the function at pc. |
| * This is done by checking from the line |
| * information picked up during dwarf reading |
| * FIXME: more stuff to be added when we |
| * parse the prologue. |
| */ |
| |
| static CORE_ADDR |
| arc_skip_prologue (CORE_ADDR pc) |
| { |
| //#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_skip_prologue called\n"); |
| #endif |
| // FIXMEA: cleanup #else |
| unsigned long inst; |
| unsigned long addend = 4; |
| CORE_ADDR skip_pc = pc; |
| CORE_ADDR func_addr, func_end = 0; |
| char *func_name; |
| struct symtab_and_line sal; |
| |
| /* If we're in a dummy frame, don't even try to skip the prologue. */ |
| if (deprecated_pc_in_call_dummy (pc)) |
| return pc; |
| |
| /* See what the symbol table says. */ |
| |
| if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) |
| { |
| struct symbol *sym; |
| |
| /* Found a function. */ |
| sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL, NULL); |
| if (sym && SYMBOL_LANGUAGE (sym) != language_asm) |
| { |
| /* Don't use this trick for assembly source files. */ |
| sal = find_pc_line (func_addr, 0); |
| if ((sal.line != 0) && (sal.end < func_end)) |
| return sal.end; |
| } |
| } |
| |
| #ifdef ARC4_JTAG |
| skip_pc = a4_scan_prologue(pc, NULL, NULL); |
| #else |
| skip_pc = arc_scan_prologue(pc,NULL,NULL); |
| #endif |
| return skip_pc; /* End of prologue */ |
| |
| //#endif |
| } |
| |
| /* Breakpoint from pc. Return whatever is in the tdep |
| * structure. The tdep structure is changed depending |
| * on the correct target / architecture chosen. |
| */ |
| |
| static const unsigned char * |
| arc_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr) |
| { |
| |
| struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
| *lenptr = tdep->arc_breakpoint_size; |
| return tdep->arc_breakpoint_insn; |
| } |
| |
| |
| /* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that |
| dummy frame. The frame ID's base needs to match the TOS value |
| saved by save_dummy_frame_tos(), and the PC match the dummy frame's |
| breakpoint. */ |
| |
| static struct frame_id |
| arc_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame) |
| { |
| return frame_id_build (arc_unwind_sp (gdbarch, next_frame), |
| frame_pc_unwind (next_frame)); |
| } |
| |
| /* The workhorse : frame_unwind_cache for the ARC700 platform . |
| */ |
| static struct arc_unwind_cache * |
| arc_frame_unwind_cache (struct frame_info *next_frame, |
| void **this_prologue_cache) |
| { |
| //#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_frame_unwind_cache called\n "); |
| #endif |
| //#else |
| CORE_ADDR pc; |
| struct arc_unwind_cache *info; |
| int i; |
| |
| |
| if ((*this_prologue_cache)) |
| return (*this_prologue_cache); |
| |
| info = FRAME_OBSTACK_ZALLOC (struct arc_unwind_cache); |
| (*this_prologue_cache) = info; |
| info->saved_regs = trad_frame_alloc_saved_regs (next_frame); |
| |
| /* Zero all fields. */ |
| info->blink_offset = 0; |
| info->prev_pc = 0; |
| info->prev_sp = 0; |
| info->frame_base = 0; |
| info->framesize = 0; |
| info->sp_offset = 0; |
| info->fp_offset = 0; |
| info->prev_pc = 0; |
| info->is_leaf = 0; |
| info->uses_fp = 0; |
| |
| /* Prologue analysis does the rest... */ |
| /* Currently our scan prologue does not |
| * support getting input for the frame unwinder |
| */ |
| |
| pc = frame_func_unwind(next_frame); |
| #ifdef ARC4_JTAG |
| a4_scan_prologue (pc, next_frame, info); |
| #else |
| arc_scan_prologue (pc, next_frame, info); |
| #endif |
| |
| return info; |
| //#endif |
| } |
| |
| |
| |
| /* |
| * Construct frame id for the normal frame |
| */ |
| |
| static void |
| arc_frame_this_id (struct frame_info *next_frame, |
| void **this_prologue_cache, |
| struct frame_id *this_id) |
| { |
| // FIXMEA: cleanup #ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\n arc_frame_this_id called()\n "); |
| #endif |
| //#else |
| |
| struct arc_unwind_cache *info |
| = arc_frame_unwind_cache (next_frame, this_prologue_cache); |
| CORE_ADDR base; |
| CORE_ADDR func; |
| struct frame_id id; |
| |
| /* The FUNC is easy. */ |
| func = frame_func_unwind (next_frame); |
| |
| /* This is meant to halt the backtrace at the entry point (_start). */ |
| if (func <= gdbarch_tdep (current_gdbarch)->lowest_pc) |
| return; |
| |
| /* Hopefully the prologue analysis either correctly determined the |
| frame's base (which is the SP from the previous frame), or set |
| that base to "NULL". */ |
| base = info->prev_sp; |
| if (base == 0) |
| return; |
| |
| id = frame_id_build (base, func); |
| |
| (*this_id) = id; |
| //#endif |
| |
| } |
| |
| |
| /* |
| * Unwind and obtain the register information |
| */ |
| static void |
| arc_frame_prev_register (struct frame_info *next_frame, |
| void **this_prologue_cache, |
| int regnum, int *optimizedp, |
| enum lval_type *lvalp, CORE_ADDR *addrp, |
| int *realnump, void *bufferp) |
| { |
| // FIXMEA: |
| //#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\n arc_frame_prev_register() called for regnum %d\n ",regnum ); |
| #endif |
| //#else |
| struct arc_unwind_cache *info |
| = arc_frame_unwind_cache (next_frame, this_prologue_cache); |
| |
| |
| /* If we are asked to unwind the PC, then we need to return blink |
| instead. The saved value of PC points into this frame's |
| prologue, not the next frame's resume location. */ |
| #ifdef ARC4_JTAG |
| if (regnum == ARC_STATUS_REGNUM) |
| #else |
| if (regnum == PC_REGNUM) |
| #endif |
| regnum = ARC_BLINK_REGNUM; |
| |
| /* SP is generally not saved to the stack, but this frame is |
| identified by NEXT_FRAME's stack pointer at the time of the call. |
| The value was already reconstructed into PREV_SP. */ |
| if (regnum == ARC_SP_REGNUM) |
| { |
| *lvalp = not_lval; |
| if (bufferp) |
| store_unsigned_integer (bufferp, 4, info->prev_sp); |
| return; |
| } |
| |
| |
| trad_frame_get_prev_register (next_frame, info->saved_regs, regnum, |
| optimizedp, lvalp, addrp, realnump, bufferp); |
| |
| |
| #ifdef ARC_DEBUG |
| printf("-*-*-*\n Regnum =%d, realnump=%d,%d \n",regnum, (char *)(bufferp), *((char*)bufferp)); |
| #endif |
| //#endif |
| } |
| |
| |
| |
| |
| static const struct frame_unwind arc_frame_unwind = { |
| NORMAL_FRAME, |
| arc_frame_this_id, |
| arc_frame_prev_register |
| }; |
| |
| const struct frame_unwind * |
| arc_frame_sniffer (struct frame_info *next_frame) |
| { |
| return &arc_frame_unwind; |
| } |
| |
| |
| /* read-only registers */ |
| static int |
| arc_cannot_store_register (int regno) |
| { |
| if( |
| #ifndef ARC4_JTAG |
| regno == ARC_EFA_REGNUM || |
| regno == ARC_ERET_REGNUM || |
| regno == ARC_STATUS32_L1_REGNUM || |
| regno == ARC_STATUS32_L2_REGNUM || |
| regno == ARC_ERSTATUS_REGNUM || |
| #endif |
| regno == ARC_ILINK1_REGNUM || |
| regno == ARC_ILINK2_REGNUM |
| ) |
| { |
| /* No warning should be printed. arc_cannot_store_register being |
| called does not imply that someone is actually writing to regnum. */ |
| |
| /*warning("writing to read-only register: %s\n", arc_register_name(regno));*/ |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Returns true if the insn at PC is a branch. *fall_thru is the address of |
| the next insn. *target is the branch target. */ |
| static int |
| arc_next_pc(CORE_ADDR pc, CORE_ADDR *fall_thru, CORE_ADDR *target) |
| { |
| #ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_next_pc called\n"); |
| #endif |
| #else |
| struct arcDisState instr, instr_d; |
| int insn_length; |
| struct disassemble_info di; |
| int two_targets = 0; |
| |
| init_disassemble_info(&di, NULL, NULL); |
| di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch; |
| di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach; |
| di.endian = gdbarch_byte_order(current_gdbarch); |
| di.read_memory_func = target_read_memory; |
| |
| instr = arcAnalyzeInstr(pc, &di); |
| |
| *fall_thru = pc + instr.instructionLen; |
| |
| #ifdef ARC_DEBUG |
| printf("--- arc_next_pc(%x) = %x, isBranch = %d, tcnt = %d [%x], flow = %s (%d), " |
| "reg for indirect jump = %d, nullifyMode = %s\n", |
| pc, *fall_thru, instr.isBranch, instr.tcnt, instr.targets[0], |
| (instr.flow == direct_jump || instr.flow == direct_call) ? "direct" : "indirect", |
| instr.flow, |
| instr.register_for_indirect_jump, |
| (instr.nullifyMode == BR_exec_always ? "delay slot" : "no delay")); |
| #endif |
| |
| if(instr.isBranch) |
| { |
| two_targets = 1; |
| |
| if(instr.flow == direct_jump || instr.flow == direct_call) |
| *target = instr.targets[0]; |
| else |
| regcache_cooked_read(current_regcache, |
| arc_binutils_reg_to_regnum(instr.register_for_indirect_jump), |
| target); |
| } |
| |
| /* for instructions with delay slots, the fall thru is not the instruction |
| immediately after the branch instruction, but the one after that */ |
| if(instr.isBranch && instr.nullifyMode == BR_exec_always) |
| { |
| instr_d = arcAnalyzeInstr(*fall_thru, &di); |
| *fall_thru += instr_d.instructionLen; |
| } |
| |
| /* zero-overhead loops: |
| if(status32[L] == 0 && next_pc == lp_end && lp_count > 1) |
| next_pc = lp_start; |
| */ |
| { |
| unsigned int lp_end, lp_start, lp_count, status32; |
| |
| regcache_cooked_read(current_regcache, ARC_LP_START_REGNUM, &lp_start); |
| regcache_cooked_read(current_regcache, ARC_LP_END_REGNUM, &lp_end); |
| regcache_cooked_read(current_regcache, ARC_LP_COUNT_REGNUM, &lp_count); |
| #ifndef ARC4_JTAG |
| regcache_cooked_read(current_regcache, ARC_STATUS32_REGNUM, &status32); |
| #endif |
| |
| if( !(status32 & ARC_STATUS32_L) && *fall_thru == lp_end && lp_count > 1) |
| { |
| two_targets = 1; |
| *target = lp_start; |
| } |
| } |
| |
| return two_targets; |
| #endif // |
| } |
| |
| /* this is called with insert_breakpoints_p = 1 before single-stepping and |
| with insert_breakpoints_p = 0 after the step */ |
| void |
| arc_software_single_step(enum target_signal ignore, int insert_breakpoints_p) |
| { |
| #ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_software_single_step called\n" ); |
| #endif |
| #else |
| typedef char binsn_quantum[BREAKPOINT_MAX]; |
| |
| static CORE_ADDR fall_thru, branch_target; |
| static binsn_quantum break_mem[2]; |
| static char two_breakpoints; |
| CORE_ADDR pc; |
| |
| { |
| #ifdef ARC_DEBUG |
| unsigned int efa, ret; |
| regcache_cooked_read(current_regcache, ARC_EFA_REGNUM, &efa); |
| // regcache_cooked_read(current_regcache, ARC_RET_REGNUM, &ret); |
| |
| printf("--- arc_software_single_step, efa = %x, ret = %x, (%s)\n", efa, ret, |
| (insert_breakpoints_p ? "add" : "remove")); |
| #endif |
| } |
| |
| if (insert_breakpoints_p) |
| { |
| pc = read_pc (); |
| two_breakpoints = arc_next_pc (pc, &fall_thru, &branch_target); |
| |
| if (two_breakpoints && branch_target == pc) |
| { |
| warning ("Cannot single-step branch-to-self or single instruction zero overhead loop,\n" |
| " Stepping across it."); |
| /* Don't insert/remove the branch-target breakpoint. */ |
| two_breakpoints = 0; |
| } |
| |
| target_insert_breakpoint (fall_thru, break_mem[0]); |
| if(two_breakpoints) |
| target_insert_breakpoint (branch_target, break_mem[1]); |
| } |
| else |
| { |
| target_remove_breakpoint (fall_thru, break_mem[0]); |
| if(two_breakpoints) |
| target_remove_breakpoint (branch_target, break_mem[1]); |
| } |
| #endif |
| } |
| |
| /* |
| * mapping from binutils/gcc register number to |
| * GDB register number ("regnum") |
| */ |
| static int |
| arc_binutils_reg_to_regnum (int reg) |
| { |
| #ifdef ARC4_JTAG |
| if (reg >= 0 && reg <= 26) |
| return reg; |
| else if (reg == 27) /* fp */ |
| return ARC_FP_REGNUM; |
| else if (reg == 28) /* sp */ |
| return ARC_SP_REGNUM; |
| else if (reg == 29) /* ilink1 */ |
| return ARC_ILINK1_REGNUM; |
| else if (reg == 30) /* ilink2 */ |
| return ARC_ILINK2_REGNUM; |
| else if (reg == 31) /* blink */ |
| return ARC_BLINK_REGNUM; |
| |
| #else |
| /* from gcc/config/arc/arc.h */ |
| |
| if (reg >= 0 && reg <= 26) |
| return reg; |
| else if (reg == 27) /* fp */ |
| return ARC_FP_REGNUM; |
| else if (reg == 28) /* sp */ |
| return ARC_SP_REGNUM; |
| else if (reg == 29) /* ilink1 */ |
| return ARC_ILINK1_REGNUM; |
| else if (reg == 30) /* ilink2 */ |
| return ARC_ILINK2_REGNUM; |
| else if (reg == 31) /* blink */ |
| return ARC_BLINK_REGNUM; |
| else if (reg >= 32 && reg <= 59) /* reserved */ |
| ; |
| else if (reg == 60) /* lp_count */ |
| return ARC_LP_COUNT_REGNUM; |
| else if (reg == 61) /* reserved */ |
| ; |
| else if (reg == 62) /* no such register */ |
| ; |
| /* else if (reg == 63) /\* PCL *\/ */ |
| /* return ARC_RET_REGNUM; */ |
| |
| #endif |
| warning ("Unmapped register #%d encountered\n", reg); |
| return -1; |
| } |
| |
| |
| static void |
| arc_add_reggroups (struct gdbarch *gdbarch) |
| { |
| reggroup_add (gdbarch, general_reggroup); |
| reggroup_add (gdbarch, all_reggroup); |
| reggroup_add (gdbarch, system_reggroup); |
| } |
| |
| int |
| arc_register_reggroup_p (struct gdbarch *gdbarch, int regnum, |
| struct reggroup *group) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
| int tdep_answer; |
| |
| tdep_answer = tdep->register_reggroup_p (regnum, group); |
| if(tdep_answer != -1) |
| return tdep_answer; |
| |
| if (group == all_reggroup) |
| return 1; |
| else if (group == save_reggroup || group == restore_reggroup) |
| { |
| /* don't save/restore read-only registers. */ |
| return (!arc_cannot_store_register(regnum)); |
| } |
| else if (group == general_reggroup) |
| { |
| #ifndef ARC4_JTAG |
| if (regnum == ARC_STATUS32_REGNUM) |
| return 0; |
| #endif |
| return 1; |
| } |
| else |
| { |
| internal_error(__FILE__, __LINE__, "bad register group"); |
| } |
| } |
| |
| |
| |
| static void |
| arc_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, |
| struct dwarf2_frame_state_reg *reg) |
| { |
| #ifdef ARC4_JTAG |
| // FIXMEA: Clean up. if ( debug_arc_jtag_target_message) |
| #ifdef ARC_DEBUG |
| printf ("\n arc_dwarf2_frame_init_reg called.\n Regno no:%d,0x%x\n",regnum,regnum); |
| #endif |
| /* The return address column. */ |
| if (regnum == ARC_STATUS_REGNUM) |
| reg->how = DWARF2_FRAME_REG_RA; |
| |
| /* The call frame address. */ |
| if (regnum == ARC_SP_REGNUM) |
| reg->how = DWARF2_FRAME_REG_CFA; |
| |
| #else |
| /* The return address column. */ |
| if (regnum == PC_REGNUM) |
| reg->how = DWARF2_FRAME_REG_RA; |
| |
| /* The call frame address. */ |
| if (regnum == ARC_SP_REGNUM) |
| reg->how = DWARF2_FRAME_REG_CFA; |
| #endif |
| } |
| |
| static CORE_ADDR |
| arc_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) |
| { |
| ULONGEST pc; |
| #ifdef ARC4_JTAG |
| frame_unwind_unsigned_register (next_frame, ARC_STATUS_REGNUM, &pc); |
| pc = pc & 0x00ffffff; |
| pc = pc << 2; |
| #else |
| frame_unwind_unsigned_register (next_frame, PC_REGNUM, &pc); |
| #endif |
| return pc; |
| } |
| |
| static CORE_ADDR |
| arc_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) |
| { |
| ULONGEST sp; |
| frame_unwind_unsigned_register (next_frame, SP_REGNUM, &sp); |
| return sp; |
| } |
| |
| |
| |
| static void |
| arc_extract_return_value (struct type *type, struct regcache *regcache, |
| void *valbuf) |
| { |
| //#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_extract_return_value called\n"); |
| #endif |
| //#else |
| ULONGEST val; |
| int len = TYPE_LENGTH (type); |
| |
| if (len <= 4) |
| { |
| /* Get the return value from R0. */ |
| regcache_cooked_read_unsigned (regcache, ARC_RETURN1_REGNUM, &val); |
| store_unsigned_integer (valbuf, len, val); |
| } |
| else if (len <= 8) |
| { |
| /* Get the return value from R0 and R1. */ |
| /* R0 holds the lower-order bytes */ |
| regcache_cooked_read_unsigned (regcache, ARC_RETURN1_REGNUM, &val); |
| store_unsigned_integer (valbuf, 4, val); |
| regcache_cooked_read_unsigned (regcache, ARC_RETURN2_REGNUM, &val); |
| store_unsigned_integer ((char *)valbuf + 4, len - 4, val); |
| } |
| else |
| error ("arc_extract_return_value: type length too large"); |
| //#endif |
| } |
| |
| static void |
| arc_store_return_value (struct type *type, struct regcache *regcache, |
| const void *valbuf) |
| { |
| //#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_store_return_value called\n "); |
| #endif |
| //#else |
| ULONGEST val; |
| int len = TYPE_LENGTH (type); |
| |
| if (len <= 4) |
| { |
| /* Put the return value in R0. */ |
| val = extract_unsigned_integer (valbuf, len); |
| regcache_cooked_write_unsigned (regcache, ARC_RETURN1_REGNUM, val); |
| } |
| else if (len <= 8) |
| { |
| /* Put the return value in R10 and R11. */ |
| val = extract_unsigned_integer (valbuf, 4); |
| regcache_cooked_write_unsigned (regcache, ARC_RETURN1_REGNUM, val); |
| val = extract_unsigned_integer ((char *)valbuf + 4, len - 4); |
| regcache_cooked_write_unsigned (regcache, ARC_RETURN2_REGNUM, val); |
| } |
| else |
| error ("arc_store_return_value: type length too large."); |
| //#endif |
| } |
| |
| |
| static enum return_value_convention |
| arc_return_value (struct gdbarch *gdbarch, struct type *valtype, |
| struct regcache *regcache, void *readbuf, |
| const void *writebuf) |
| { |
| //#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_return_value called"); |
| #endif |
| //#else |
| /* This will change with the ABI */ |
| int struct_return = (TYPE_CODE (valtype) == TYPE_CODE_STRUCT || |
| TYPE_CODE (valtype) == TYPE_CODE_UNION || |
| TYPE_LENGTH (valtype) > 8); |
| |
| |
| if (writebuf != NULL) |
| { |
| gdb_assert (!struct_return); |
| arc_store_return_value (valtype, regcache, writebuf); |
| } |
| |
| if (readbuf != NULL) |
| { |
| gdb_assert (!struct_return); |
| arc_extract_return_value (valtype, regcache, readbuf); |
| } |
| |
| if (struct_return) |
| return RETURN_VALUE_STRUCT_CONVENTION; |
| else |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| //#endif |
| } |
| |
| /* Signal Trampoline Frame Unwinder. These |
| * unwinders allow frame unwinding to happen |
| * from within signal handlers. |
| */ |
| |
| static struct arc_unwind_cache * |
| arc_sigtramp_frame_cache (struct frame_info *next_frame, |
| void **this_cache) |
| { |
| // FIXMEA: cleanup#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_sigtramp_frame_cache called"); |
| #endif |
| //#else |
| struct arc_unwind_cache *cache; |
| struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
| CORE_ADDR addr; |
| char buf[4]; |
| |
| if (*this_cache) |
| return *this_cache; |
| |
| cache = FRAME_OBSTACK_ZALLOC (struct arc_unwind_cache); |
| (*this_cache) = cache; |
| cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); |
| |
| /* Zero all fields. */ |
| cache->blink_offset = 0; |
| cache->prev_pc = 0; |
| cache->prev_sp = 0; |
| cache->frame_base = 0; |
| cache->framesize = 0; |
| cache->sp_offset = 0; |
| cache->fp_offset = 0; |
| cache->prev_pc = 0; |
| cache->is_leaf = 0; |
| cache->uses_fp = 0; |
| |
| |
| frame_unwind_register (next_frame, SP_REGNUM, buf); |
| cache->frame_base = extract_unsigned_integer (buf, 4); |
| |
| addr = tdep->sigcontext_addr (next_frame); |
| if (tdep->sc_reg_offset) |
| { |
| int i; |
| |
| for (i = 0; i < tdep->sc_num_regs; i++) |
| if (tdep->sc_reg_offset[i] != -1) |
| cache->saved_regs[i].addr = addr + tdep->sc_reg_offset[i]; |
| } |
| |
| return cache; |
| //#endif |
| } |
| |
| static void |
| arc_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache, |
| struct frame_id *this_id) |
| { |
| //FIXMEA: cleanup #ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_sigtramp_frame_this_id called"); |
| #endif |
| //#else |
| struct arc_unwind_cache *cache = |
| arc_sigtramp_frame_cache (next_frame, this_cache); |
| |
| (*this_id) = frame_id_build (cache->frame_base, frame_pc_unwind (next_frame)); |
| //#endif |
| } |
| |
| static void |
| arc_sigtramp_frame_prev_register (struct frame_info *next_frame, |
| void **this_cache, |
| int regnum, int *optimizedp, |
| enum lval_type *lvalp, CORE_ADDR *addrp, |
| int *realnump, void *valuep) |
| { |
| // FIXMEA: cleanup#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_sigtramp_frame_prev_register called"); |
| #endif |
| //#else |
| struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
| |
| /* Make sure we've initialized the cache. */ |
| struct arc_unwind_cache *cache = |
| arc_sigtramp_frame_cache (next_frame, this_cache); |
| |
| /* on a signal, the PC is in ret */ |
| #ifdef ARC4_JTAG |
| if (regnum == ARC_STATUS_REGNUM) |
| #else |
| if(regnum == PC_REGNUM) |
| #endif |
| regnum = tdep->pc_regnum_in_sigcontext; |
| |
| trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum, |
| optimizedp, lvalp, addrp, realnump, valuep); |
| //#endif |
| } |
| |
| |
| static const struct frame_unwind arc_sigtramp_frame_unwind = |
| { |
| SIGTRAMP_FRAME, |
| arc_sigtramp_frame_this_id, |
| arc_sigtramp_frame_prev_register |
| }; |
| |
| const struct frame_unwind * |
| arc_sigtramp_frame_sniffer (struct frame_info *next_frame) |
| { |
| //FIXMEA: cleanup#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_sigtramp_frame_sniffer called() "); |
| #endif |
| //#else |
| CORE_ADDR pc = frame_pc_unwind (next_frame); |
| struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame)); |
| |
| /* We shouldn't even bother if we don't have a sigcontext_addr |
| handler. */ |
| if (tdep->sigcontext_addr == NULL) |
| return NULL; |
| |
| if (tdep->sigtramp_p != NULL) |
| { |
| if (tdep->sigtramp_p (next_frame)) |
| { |
| return &arc_sigtramp_frame_unwind; |
| } |
| } |
| |
| return NULL; |
| //#endif |
| } |
| |
| |
| |
| /* Allow calls to be made to functions defined in the debuggee. |
| a.k.a dummy calls |
| */ |
| /* When arguments must be pushed onto the stack, they go on in reverse |
| order. The below implements a FILO (stack) to do this. |
| Copied from d10v-tdep.c. */ |
| |
| struct stack_item |
| { |
| int len; |
| struct stack_item *prev; |
| void *data; |
| }; |
| |
| static struct stack_item * |
| push_stack_item (struct stack_item *prev, void *contents, int len) |
| { |
| struct stack_item *si; |
| si = xmalloc (sizeof (struct stack_item)); |
| si->data = xmalloc (len); |
| si->len = len; |
| si->prev = prev; |
| memcpy (si->data, contents, len); |
| return si; |
| } |
| |
| static struct stack_item * |
| pop_stack_item (struct stack_item *si) |
| { |
| struct stack_item *dead = si; |
| si = si->prev; |
| xfree (dead->data); |
| xfree (dead); |
| return si; |
| } |
| |
| |
| |
| |
| /* arc_push_dummy_call : |
| * gdbarch : gdbarch structure for the backend to use if needed. |
| * function : |
| * regcache : |
| * bp_addr : Return address for the breakpoint. |
| * sp : Current value of sp. |
| * struct_return: struct_return is 1 if structures are returned by |
| * the function. |
| * struct_addr: Hidden address for returning a struct. |
| */ |
| |
| |
| static CORE_ADDR |
| arc_push_dummy_call(struct gdbarch *gdbarch, struct value *function, |
| struct regcache *regcache, CORE_ADDR bp_addr, int nargs, |
| struct value **args, CORE_ADDR sp, int struct_return, |
| CORE_ADDR struct_addr) |
| |
| { |
| //#ifdef ARC4_JTAG |
| #ifdef ARC_DEBUG |
| printf ("\narc_push_dummy_call called"); |
| #endif |
| // #else |
| int stack_alloc; |
| int stack_offset; |
| int argreg; |
| int argnum; |
| |
| CORE_ADDR regval; |
| struct stack_item *si = NULL; |
| |
| |
| /* Push the return address. */ |
| #ifdef ARC4_JTAG |
| CORE_ADDR modified_bp_addr; |
| modified_bp_addr = arc_debug_fetch_regs(ARC_STATUS_REGNUM); |
| regcache_raw_collect(regcache, ARC_STATUS_REGNUM, &modified_bp_addr); |
| modified_bp_addr = modified_bp_addr & 0xff000000; |
| bp_addr = bp_addr >>2; |
| modified_bp_addr |= bp_addr; |
| regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, modified_bp_addr); |
| #else |
| regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr); |
| #endif |
| |
| /* Are we returning a value using a structure return or a normal value |
| return? struct_addr is the address of the reserved space for the return |
| structure to be written on the stack. |
| */ |
| /* FIXME:: Ramana :: What about 4 byte structures returned in r0 as |
| claimed by Metaware. |
| */ |
| |
| /* Now load as many as possible of the first arguments into registers, |
| and push the rest onto the stack. */ |
| argreg = ARC_ARG0_REGNUM; |
| |
| if (struct_return) |
| { |
| regcache_cooked_write_unsigned (regcache, ARC_ARG0_REGNUM, struct_addr); |
| argreg++; |
| #ifdef ARC4_JTAG |
| sp = sp - 16; |
| #endif |
| } |
| |
| stack_offset = 0; |
| |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| int len; |
| char *val; |
| int reg_demand; |
| int i; |
| |
| len = TYPE_LENGTH (VALUE_TYPE (args[argnum])); |
| val = (char *) VALUE_CONTENTS (args[argnum]); |
| |
| /* How may registers worth of storage do we need for this argument? */ |
| reg_demand = (len / 4) + (len % 4 != 0 ? 1 : 0); |
| |
| if (argreg + reg_demand - 1 <= ARC_ARG7_REGNUM) |
| { |
| /* Data passed by value. Fits in available register(s). */ |
| for (i = 0; i < reg_demand; i++) |
| { |
| regcache_cooked_write_unsigned (regcache, argreg, |
| *(unsigned long *) val); |
| argreg++; |
| val += 4; |
| } |
| } |
| else if (argreg <= ARC_ARG7_REGNUM) |
| { |
| /* Data passed by value. Does not fit in available register(s). |
| Use the register(s) first, then the stack. */ |
| for (i = 0; i < reg_demand; i++) |
| { |
| if (argreg <= ARC_ARG7_REGNUM) |
| { |
| regcache_cooked_write_unsigned (regcache, argreg, |
| *(unsigned long *) val); |
| argreg++; |
| val += 4; |
| } |
| else |
| { |
| /* Push item for later so that pushed arguments |
| come in the right order. */ |
| si = push_stack_item (si, val, 4); |
| val += 4; |
| } |
| } |
| } |
| else if (len > (2 * 4)) |
| { |
| /* FIXME */ |
| internal_error (__FILE__, __LINE__, "We don't do this"); |
| } |
| else |
| { |
| /* Data passed by value. No available registers. Put it on |
| the stack. */ |
| si = push_stack_item (si, val, len); |
| } |
| } |
| |
| while (si) |
| { |
| /* fp_arg must be word-aligned (i.e., don't += len) to match |
| the function prologue. */ |
| sp = (sp - si->len) & ~3; |
| #ifdef ARC4_JTAG |
| write_memory (sp + 16, si->data, si->len); |
| #else |
| write_memory (sp, si->data, si->len); |
| #endif |
| si = pop_stack_item (si); |
| } |
| |
| /* Finally, update the SP register. */ |
| regcache_cooked_write_unsigned (regcache, ARC_SP_REGNUM, sp); |
| |
| return sp; |
| //#endif |
| } |
| |
| /* Align Frame */ |
| static CORE_ADDR |
| arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) |
| { |
| /* Align to the normal alignment on the stack). */ |
| return sp & ~3; |
| } |
| |
| |
| /* Print interesting information about the floating point processor |
| (if present) or emulator. */ |
| static void |
| arc_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, |
| struct frame_info *frame, const char *args) |
| { |
| printf("Software FPU \n"); |
| } |
| |
| |
| /* Set the main_name to "_main" if required. |
| This is set as an observer of inferior_created. */ |
| static void |
| arc_set_main_name (struct target_ops *objfile, int from_tty) |
| { |
| struct minimal_symbol *umainsym, *mainsym; |
| |
| /* Old ARC toolchains prepend an underscore to symbol names. If there is |
| an _main but no main, then we're probably debugging a binary that was |
| made with the old toolchain. */ |
| umainsym = lookup_minimal_symbol ("_main", NULL, NULL); |
| mainsym = lookup_minimal_symbol ("main", NULL, NULL); |
| if(umainsym && !mainsym) |
| { |
| set_main_name ("_main"); |
| } |
| |
| /* If we don't have any symbols, the default, i.e. "main", will get used. */ |
| } |
| |
| |
| /* The following piece of code is borrowed from d10v */ |
| static void |
| a4_address_to_pointer (struct type *type, void *buf, CORE_ADDR addr) |
| { |
| #ifdef ARC4_JTAG |
| if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC |
| || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD) |
| store_unsigned_integer (buf, TYPE_LENGTH (type), (addr>>2) & 0xffffff); |
| else |
| store_unsigned_integer (buf, TYPE_LENGTH (type), addr); |
| #endif |
| } |
| |
| static CORE_ADDR |
| a4_pointer_to_address (struct type *type, const void *buf) |
| { |
| #ifdef ARC4_JTAG |
| CORE_ADDR addr = extract_unsigned_integer (buf, TYPE_LENGTH(type)); |
| /* Is it a code address? */ |
| if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC |
| || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD |
| || TYPE_CODE_SPACE (TYPE_TARGET_TYPE (type))) |
| return ((addr<<2) & 0x2ffffff); |
| else |
| return addr; |
| #endif |
| } |
| |
| static struct gdbarch * |
| arc_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
| { |
| struct gdbarch_tdep *tdep; |
| struct gdbarch *gdbarch; |
| |
| tdep = xmalloc (sizeof (struct gdbarch_tdep)); |
| gdbarch = gdbarch_alloc (&info, tdep); |
| |
| /* Fixme :: Worry about default initialization of breakpoints |
| for the ARC platform. In our case currently this is handled |
| out of arc-linux-tdep.c for default arc linux breakpoints. |
| */ |
| |
| info.osabi = CONFIG_OSABI; |
| gdbarch_init_osabi(info, gdbarch); |
| |
| /* Put stuff in gdbarch. */ |
| |
| /* Characters are unsigned by default */ |
| set_gdbarch_char_signed (gdbarch, 0); |
| |
| set_gdbarch_print_float_info (gdbarch, arc_print_float_info); |
| set_gdbarch_sp_regnum (gdbarch, ARC_SP_REGNUM); |
| set_gdbarch_register_type (gdbarch, arc_register_type); |
| |
| set_gdbarch_cannot_store_register (gdbarch, arc_cannot_store_register); |
| |
| |
| /* Advance PC across function entry code. */ |
| set_gdbarch_skip_prologue (gdbarch, arc_skip_prologue); |
| |
| |
| /* Hook in the Dwarf-2 frame sniffer. */ |
| set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arc_binutils_reg_to_regnum); |
| dwarf2_frame_set_init_reg (gdbarch, arc_dwarf2_frame_init_reg); |
| frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer); |
| |
| /* signal frames */ |
| frame_unwind_append_sniffer (gdbarch, arc_sigtramp_frame_sniffer); |
| |
| |
| |
| /* The stack grows downward. */ |
| set_gdbarch_inner_than (gdbarch, core_addr_lessthan); |
| |
| set_gdbarch_unwind_pc (gdbarch, arc_unwind_pc); |
| set_gdbarch_unwind_sp (gdbarch, arc_unwind_sp); |
| set_gdbarch_unwind_dummy_id (gdbarch, arc_unwind_dummy_id); |
| frame_unwind_append_sniffer (gdbarch, arc_frame_sniffer); |
| |
| |
| set_gdbarch_return_value (gdbarch, arc_return_value); |
| |
| /* Add the arc register groups. */ |
| arc_add_reggroups (gdbarch); |
| set_gdbarch_register_reggroup_p (gdbarch, arc_register_reggroup_p); |
| |
| /* Breakpoint manipulation. */ |
| set_gdbarch_breakpoint_from_pc (gdbarch, arc_breakpoint_from_pc); |
| set_gdbarch_frame_align(gdbarch,arc_frame_align); |
| |
| /* Dummy Frame handling */ |
| set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call); |
| set_gdbarch_call_dummy_location (gdbarch,AT_ENTRY_POINT); |
| |
| /* Disassembly. */ |
| { |
| /* the arc libopcodes wants abfd so that it can find out what CPU |
| extensions are there */ |
| bfd abfd; |
| abfd.sections = NULL; |
| |
| |
| #ifndef ARC4_JTAG |
| set_gdbarch_print_insn(gdbarch, arcompact_get_disassembler(&abfd)); |
| #else |
| set_gdbarch_print_insn(gdbarch, arc_get_disassembler(&abfd)); |
| #endif |
| } |
| |
| #ifdef ARC4_JTAG |
| set_gdbarch_address_to_pointer (gdbarch, a4_address_to_pointer); |
| set_gdbarch_pointer_to_address (gdbarch, a4_pointer_to_address); |
| #endif |
| //#ifndef ARC4_JTAG |
| /* Set main_name to _main if necessary. Ideally we'd want a hook that |
| gets called when symbols are loaded, but it seems there isn't one; so |
| we'll use this. This will not work if the user does "target remote |
| ..." and then "add-symbol-file ..." */ |
| observer_attach_inferior_created (arc_set_main_name); |
| //#endif |
| |
| #ifdef ARC4_JTAG |
| // set_gdbarch_write_pc (gdbarch, a4_write_pc); |
| #endif |
| |
| CONFIG_INIT_TDEP (gdbarch); |
| |
| return gdbarch; |
| } |
| |
| static void |
| arc_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file) |
| { |
| } |
| |
| void |
| _initialize_arc_tdep (void) |
| { |
| gdbarch_register (bfd_arch_arc, arc_gdbarch_init, arc_dump_tdep); |
| } |
| |