X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Fcris-tdep.c;h=8f22ed61133215211c28d20699d45d4dd40eff60;hb=c2f05ac92e7fe9e3161ae09ea3f9e0937af42abc;hp=9159127c0a852256f2deba82f57cac6181bf1954;hpb=fa4e459858473e769eab90adb0982dca18a3440f;p=binutils-gdb.git diff --git a/gdb/cris-tdep.c b/gdb/cris-tdep.c index 9159127c0a8..8f22ed61133 100644 --- a/gdb/cris-tdep.c +++ b/gdb/cris-tdep.c @@ -35,6 +35,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* To get entry_point_address. */ #include "symfile.h" +#include "solib.h" /* Support for shared libraries. */ +#include "solib-svr4.h" /* For struct link_map_offsets. */ + + enum cris_num_regs { /* There are no floating point registers. Used in gdbserver low-linux.c. */ @@ -88,6 +92,7 @@ enum cris_regnums IRP_REGNUM = 26, SRP_REGNUM = 27, BAR_REGNUM = 28, + DCCR_REGNUM = 29, BRP_REGNUM = 30, USP_REGNUM = 31 }; @@ -511,7 +516,10 @@ cris_examine (CORE_ADDR ip, CORE_ADDR limit, struct frame_info *fi, insn_next = read_memory_unsigned_integer (ip, sizeof (short)); ip += sizeof (short); regno = cris_get_operand2 (insn_next); - if (regno == (SRP_REGNUM - NUM_GENREGS)) + + /* This check, meant to recognize srp, used to be regno == + (SRP_REGNUM - NUM_GENREGS), but that covers r11 also. */ + if (insn_next == 0xBE7E) { if (frameless_p) { @@ -591,6 +599,14 @@ cris_examine (CORE_ADDR ip, CORE_ADDR limit, struct frame_info *fi, return ip; } source_register = cris_get_operand1 (insn); + + /* FIXME? In the glibc solibs, the prologue might contain something + like (this example taken from relocate_doit): + move.d $pc,$r0 + sub.d 0xfffef426,$r0 + which isn't covered by the source_register check below. Question + is whether to add a check for this combo, or make better use of + the limit variable instead. */ if (source_register < ARG1_REGNUM || source_register > ARG4_REGNUM) { /* The prologue ended before the limit was reached. */ @@ -1860,6 +1876,11 @@ process_autoincrement (int size, unsigned short inst, inst_env_type *inst_env) } } +/* Just a forward declaration. */ + +unsigned long +get_data_from_address (unsigned short *inst, CORE_ADDR address); + /* Calculates the prefix value for the general case of offset addressing mode. */ @@ -1884,9 +1905,8 @@ bdap_prefix (unsigned short inst, inst_env_type *inst_env) /* The offset is an indirection of the contents of the operand1 register. */ inst_env->prefix_value += - read_memory_integer (inst_env->reg[cris_get_operand1 (inst)], - cris_get_size (inst)); - + get_data_from_address (&inst, inst_env->reg[cris_get_operand1 (inst)]); + if (cris_get_mode (inst) == AUTOINC_MODE) { process_autoincrement (cris_get_size (inst), inst, inst_env); @@ -2580,14 +2600,13 @@ move_to_preg_op (unsigned short inst, inst_env_type *inst_env) inst_env->invalid = 1; return; } - /* The increment depends on the size of the special register. - Register P0 to P3 has the size byte, register P4 to P7 has the - size word and register P8 to P15 has the size dword. */ - if (cris_get_operand2 (inst) < 4) + + /* The increment depends on the size of the special register. */ + if (cris_register_size (cris_get_operand2 (inst)) == 1) { process_autoincrement (INST_BYTE_SIZE, inst, inst_env); } - if (cris_get_operand2 (inst) < 8) + else if (cris_register_size (cris_get_operand2 (inst)) == 2) { process_autoincrement (INST_WORD_SIZE, inst, inst_env); } @@ -2636,14 +2655,13 @@ none_reg_mode_move_from_preg_op (unsigned short inst, inst_env_type *inst_env) inst_env->invalid = 1; return; } - /* The increment depends on the size of the special register. - Register P0 to P3 has the size byte, register P4 to P7 has - the size word and register P8 to P15 has the size dword. */ - if (cris_get_operand2 (inst) < 4) + + /* The increment depends on the size of the special register. */ + if (cris_register_size (cris_get_operand2 (inst)) == 1) { process_autoincrement (INST_BYTE_SIZE, inst, inst_env); } - if (cris_get_operand2 (inst) < 8) + else if (cris_register_size (cris_get_operand2 (inst)) == 2) { process_autoincrement (INST_WORD_SIZE, inst, inst_env); } @@ -3544,6 +3562,305 @@ cris_delayed_get_disassembler (bfd_vma addr, disassemble_info *info) return TARGET_PRINT_INSN (addr, info); } +/* Copied from . */ +typedef unsigned long elf_greg_t; + +/* Same as user_regs_struct struct in . */ +typedef elf_greg_t elf_gregset_t[35]; + +/* Unpack an elf_gregset_t into GDB's register cache. */ + +void +supply_gregset (elf_gregset_t *gregsetp) +{ + int i; + elf_greg_t *regp = *gregsetp; + static char zerobuf[4] = {0}; + + /* The kernel dumps all 32 registers as unsigned longs, but supply_register + knows about the actual size of each register so that's no problem. */ + for (i = 0; i < NUM_GENREGS + NUM_SPECREGS; i++) + { + supply_register (i, (char *)®p[i]); + } +} + +/* Use a local version of this function to get the correct types for + regsets, until multi-arch core support is ready. */ + +static void +fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, + int which, CORE_ADDR reg_addr) +{ + elf_gregset_t gregset; + + switch (which) + { + case 0: + if (core_reg_size != sizeof (gregset)) + { + warning ("wrong size gregset struct in core file"); + } + else + { + memcpy (&gregset, core_reg_sect, sizeof (gregset)); + supply_gregset (&gregset); + } + + default: + /* We've covered all the kinds of registers we know about here, + so this must be something we wouldn't know what to do with + anyway. Just ignore it. */ + break; + } +} + +static struct core_fns cris_elf_core_fns = +{ + bfd_target_elf_flavour, /* core_flavour */ + default_check_format, /* check_format */ + default_core_sniffer, /* core_sniffer */ + fetch_core_registers, /* core_read_registers */ + NULL /* next */ +}; + +/* Fetch (and possibly build) an appropriate link_map_offsets + structure for native Linux/CRIS targets using the struct offsets + defined in link.h (but without actual reference to that file). + + This makes it possible to access Linux/CRIS shared libraries from a + GDB that was not built on an Linux/CRIS host (for cross debugging). + + See gdb/solib-svr4.h for an explanation of these fields. */ + +struct link_map_offsets * +cris_linux_svr4_fetch_link_map_offsets (void) +{ + static struct link_map_offsets lmo; + static struct link_map_offsets *lmp = NULL; + + if (lmp == NULL) + { + lmp = &lmo; + + lmo.r_debug_size = 8; /* The actual size is 20 bytes, but + this is all we need. */ + lmo.r_map_offset = 4; + lmo.r_map_size = 4; + + lmo.link_map_size = 20; + + lmo.l_addr_offset = 0; + lmo.l_addr_size = 4; + + lmo.l_name_offset = 4; + lmo.l_name_size = 4; + + lmo.l_next_offset = 12; + lmo.l_next_size = 4; + + lmo.l_prev_offset = 16; + lmo.l_prev_size = 4; + } + + return lmp; +} + +static void +cris_fpless_backtrace (char *noargs, int from_tty) +{ + /* Points at the instruction after the jsr (except when in innermost frame + where it points at the original pc). */ + CORE_ADDR pc = 0; + + /* Temporary variable, used for parsing from the start of the function that + the pc is in, up to the pc. */ + CORE_ADDR tmp_pc = 0; + CORE_ADDR sp = 0; + + /* Information about current frame. */ + struct symtab_and_line sal; + char* func_name; + + /* Present instruction. */ + unsigned short insn; + + /* Next instruction, lookahead. */ + unsigned short insn_next; + + /* This is to store the offset between sp at start of function and until we + reach push srp (if any). */ + int sp_add_later = 0; + int push_srp_found = 0; + + int val = 0; + + /* Frame counter. */ + int frame = 0; + + /* For the innermost frame, we want to look at srp in case it's a leaf + function (since there's no push srp in that case). */ + int innermost_frame = 1; + + read_register_gen (PC_REGNUM, (char *) &pc); + read_register_gen (SP_REGNUM, (char *) &sp); + + /* We make an explicit return when we can't find an outer frame. */ + while (1) + { + /* Get file name and line number. */ + sal = find_pc_line (pc, 0); + + /* Get function name. */ + find_pc_partial_function (pc, &func_name, (CORE_ADDR *) NULL, + (CORE_ADDR *) NULL); + + /* Print information about current frame. */ + printf_unfiltered ("#%i 0x%08lx in %s", frame++, pc, func_name); + if (sal.symtab) + { + printf_unfiltered (" at %s:%i", sal.symtab->filename, sal.line); + } + printf_unfiltered ("\n"); + + /* Get the start address of this function. */ + tmp_pc = get_pc_function_start (pc); + + /* Mini parser, only meant to find push sp and sub ...,sp from the start + of the function, up to the pc. */ + while (tmp_pc < pc) + { + insn = read_memory_unsigned_integer (tmp_pc, sizeof (short)); + tmp_pc += sizeof (short); + if (insn == 0xE1FC) + { + /* push 32 bit instruction */ + insn_next = read_memory_unsigned_integer (tmp_pc, + sizeof (short)); + tmp_pc += sizeof (short); + + /* Recognize srp. */ + if (insn_next == 0xBE7E) + { + /* For subsequent (not this one though) push or sub which + affects sp, adjust sp immediately. */ + push_srp_found = 1; + + /* Note: this will break if we ever encounter a + push vr (1 byte) or push ccr (2 bytes). */ + sp_add_later += 4; + } + else + { + /* Some other register was pushed. */ + if (push_srp_found) + { + sp += 4; + } + else + { + sp_add_later += 4; + } + } + } + else if (cris_get_operand2 (insn) == SP_REGNUM + && cris_get_mode (insn) == 0x0000 + && cris_get_opcode (insn) == 0x000A) + { + /* subq ,sp */ + val = cris_get_quick_value (insn); + + if (push_srp_found) + { + sp += val; + } + else + { + sp_add_later += val; + } + + } + else if (cris_get_operand2 (insn) == SP_REGNUM + /* Autoincrement addressing mode. */ + && cris_get_mode (insn) == 0x0003 + /* Opcode. */ + && ((insn) & 0x03E0) >> 5 == 0x0004) + { + /* subu ,sp */ + val = get_data_from_address (&insn, tmp_pc); + + if (push_srp_found) + { + sp += val; + } + else + { + sp_add_later += val; + } + } + else if (cris_get_operand2 (insn) == SP_REGNUM + && ((insn & 0x0F00) >> 8) == 0x0001 + && (cris_get_signed_offset (insn) < 0)) + { + /* Immediate byte offset addressing prefix word with sp as base + register. Used for CRIS v8 i.e. ETRAX 100 and newer if + is between 64 and 128. + movem r,[sp=sp-] */ + val = -cris_get_signed_offset (insn); + insn_next = read_memory_unsigned_integer (tmp_pc, + sizeof (short)); + tmp_pc += sizeof (short); + + if (cris_get_mode (insn_next) == PREFIX_ASSIGN_MODE + && cris_get_opcode (insn_next) == 0x000F + && cris_get_size (insn_next) == 0x0003 + && cris_get_operand1 (insn_next) == SP_REGNUM) + { + if (push_srp_found) + { + sp += val; + } + else + { + sp_add_later += val; + } + } + } + } + + if (push_srp_found) + { + /* Reset flag. */ + push_srp_found = 0; + + /* sp should now point at where srp is stored on the stack. Update + the pc to the srp. */ + pc = read_memory_unsigned_integer (sp, 4); + } + else if (innermost_frame) + { + /* We couldn't find a push srp in the prologue, so this must be + a leaf function, and thus we use the srp register directly. + This should happen at most once, for the innermost function. */ + read_register_gen (SRP_REGNUM, (char *) &pc); + } + else + { + /* Couldn't find an outer frame. */ + return; + } + + /* Reset flag. (In case the innermost frame wasn't a leaf, we don't + want to look at the srp register later either). */ + innermost_frame = 0; + + /* Now, add the offset for everything up to, and including push srp, + that was held back during the prologue parsing. */ + sp += sp_add_later; + sp_add_later = 0; + } +} + void _initialize_cris_tdep (void) { @@ -3572,6 +3889,14 @@ _initialize_cris_tdep (void) "Set the current CRIS ABI version.", &setlist); c->function.sfunc = cris_abi_update; add_show_from_set (c, &showlist); + + c = add_cmd ("cris-fpless-backtrace", class_support, cris_fpless_backtrace, + "Display call chain using the subroutine return pointer.\n" + "Note that this displays the address after the jump to the " + "subroutine.", &cmdlist); + + add_core_fns (&cris_elf_core_fns); + } /* Prints out all target specific values. */ @@ -3604,7 +3929,7 @@ cris_version_update (char *ignore_args, int from_tty, usr_cmd_cris_version_valid = 1; /* Update the current architecture, if needed. */ - memset (&info, 0, sizeof info); + gdbarch_info_init (&info); if (!gdbarch_update_p (info)) internal_error (__FILE__, __LINE__, "cris_gdbarch_update: failed to update architecture."); } @@ -3622,7 +3947,7 @@ cris_mode_update (char *ignore_args, int from_tty, usr_cmd_cris_mode_valid = 1; /* Update the current architecture, if needed. */ - memset (&info, 0, sizeof info); + gdbarch_info_init (&info); if (!gdbarch_update_p (info)) internal_error (__FILE__, __LINE__, "cris_gdbarch_update: failed to update architecture."); } @@ -3640,7 +3965,7 @@ cris_abi_update (char *ignore_args, int from_tty, usr_cmd_cris_abi_valid = 1; /* Update the current architecture, if needed. */ - memset (&info, 0, sizeof info); + gdbarch_info_init (&info); if (!gdbarch_update_p (info)) internal_error (__FILE__, __LINE__, "cris_gdbarch_update: failed to update architecture."); } @@ -3788,11 +4113,11 @@ cris_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* INIT shall ensure that the INFO.BYTE_ORDER is non-zero. */ switch (info.byte_order) { - case LITTLE_ENDIAN: + case BFD_ENDIAN_LITTLE: /* Ok. */ break; - case BIG_ENDIAN: + case BFD_ENDIAN_BIG: internal_error (__FILE__, __LINE__, "cris_gdbarch_init: big endian byte order in info"); break; @@ -3829,9 +4154,6 @@ cris_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) which means we have to set this explicitly. */ set_gdbarch_long_double_bit (gdbarch, 64); - /* Floating point is IEEE compatible. */ - set_gdbarch_ieee_float (gdbarch, 1); - /* There are 32 registers (some of which may not be implemented). */ set_gdbarch_num_regs (gdbarch, 32); set_gdbarch_sp_regnum (gdbarch, 14); @@ -3986,5 +4308,9 @@ cris_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Helpful for backtracing and returning in a call dummy. */ set_gdbarch_save_dummy_frame_tos (gdbarch, generic_save_dummy_frame_tos); + /* Use target_specific function to define link map offsets. */ + set_solib_svr4_fetch_link_map_offsets + (gdbarch, cris_linux_svr4_fetch_link_map_offsets); + return gdbarch; }