#include "sim-regno.h"
#include "gdb/sim-ppc.h"
#include "reggroups.h"
+#include "dwarf2-frame.h"
#include "libbfd.h" /* for bfd_default_set_arch_mach */
#include "coff/internal.h" /* for libcoff.h */
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int sim_regno;
- gdb_assert (0 <= reg && reg <= NUM_REGS + NUM_PSEUDO_REGS);
+ gdb_assert (0 <= reg
+ && reg <= gdbarch_num_regs (current_gdbarch)
+ + gdbarch_num_pseudo_regs (current_gdbarch));
sim_regno = tdep->sim_regno[reg];
if (sim_regno >= 0)
return 0;
}
-
-/* Fill in fi->saved_regs */
-
-struct frame_extra_info
-{
- /* Functions calling alloca() change the value of the stack
- pointer. We need to use initial stack pointer (which is saved in
- r31 by gcc) in such cases. If a compiler emits traceback table,
- then we should use the alloca register specified in traceback
- table. FIXME. */
- CORE_ADDR initial_sp; /* initial stack pointer. */
-};
-
/* Get the ith function argument for the current function. */
static CORE_ADDR
rs6000_fetch_pointer_argument (struct frame_info *frame, int argi,
static unsigned char big_breakpoint[] = { 0x7d, 0x82, 0x10, 0x08 };
static unsigned char little_breakpoint[] = { 0x08, 0x10, 0x82, 0x7d };
*bp_size = 4;
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+ if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
return big_breakpoint;
else
return little_breakpoint;
}
-/* AIX does not support PT_STEP. Simulate it. */
+/* Instruction masks used during single-stepping of atomic sequences. */
+#define LWARX_MASK 0xfc0007fe
+#define LWARX_INSTRUCTION 0x7c000028
+#define LDARX_INSTRUCTION 0x7c0000A8
+#define STWCX_MASK 0xfc0007ff
+#define STWCX_INSTRUCTION 0x7c00012d
+#define STDCX_INSTRUCTION 0x7c0001ad
+#define BC_MASK 0xfc000000
+#define BC_INSTRUCTION 0x40000000
+
+/* Checks for an atomic sequence of instructions beginning with a LWARX/LDARX
+ instruction and ending with a STWCX/STDCX instruction. If such a sequence
+ is found, attempt to step through it. A breakpoint is placed at the end of
+ the sequence. */
+
+static int
+deal_with_atomic_sequence (struct regcache *regcache)
+{
+ CORE_ADDR pc = read_pc ();
+ CORE_ADDR breaks[2] = {-1, -1};
+ CORE_ADDR loc = pc;
+ CORE_ADDR branch_bp; /* Breakpoint at branch instruction's destination. */
+ CORE_ADDR closing_insn; /* Instruction that closes the atomic sequence. */
+ int insn = read_memory_integer (loc, PPC_INSN_SIZE);
+ int insn_count;
+ int index;
+ int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
+ const int atomic_sequence_length = 16; /* Instruction sequence length. */
+ int opcode; /* Branch instruction's OPcode. */
+ int bc_insn_count = 0; /* Conditional branch instruction count. */
+
+ /* Assume all atomic sequences start with a lwarx/ldarx instruction. */
+ if ((insn & LWARX_MASK) != LWARX_INSTRUCTION
+ && (insn & LWARX_MASK) != LDARX_INSTRUCTION)
+ return 0;
-void
-rs6000_software_single_step (enum target_signal signal,
- int insert_breakpoints_p)
+ /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+ instructions. */
+ for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+ {
+ loc += PPC_INSN_SIZE;
+ insn = read_memory_integer (loc, PPC_INSN_SIZE);
+
+ /* Assume that there is at most one conditional branch in the atomic
+ sequence. If a conditional branch is found, put a breakpoint in
+ its destination address. */
+ if ((insn & BC_MASK) == BC_INSTRUCTION)
+ {
+ if (bc_insn_count >= 1)
+ return 0; /* More than one conditional branch found, fallback
+ to the standard single-step code. */
+
+ opcode = insn >> 26;
+ branch_bp = branch_dest (opcode, insn, pc, breaks[0]);
+
+ if (branch_bp != -1)
+ {
+ breaks[1] = branch_bp;
+ bc_insn_count++;
+ last_breakpoint++;
+ }
+ }
+
+ if ((insn & STWCX_MASK) == STWCX_INSTRUCTION
+ || (insn & STWCX_MASK) == STDCX_INSTRUCTION)
+ break;
+ }
+
+ /* Assume that the atomic sequence ends with a stwcx/stdcx instruction. */
+ if ((insn & STWCX_MASK) != STWCX_INSTRUCTION
+ && (insn & STWCX_MASK) != STDCX_INSTRUCTION)
+ return 0;
+
+ closing_insn = loc;
+ loc += PPC_INSN_SIZE;
+ insn = read_memory_integer (loc, PPC_INSN_SIZE);
+
+ /* Insert a breakpoint right after the end of the atomic sequence. */
+ breaks[0] = loc;
+
+ /* Check for duplicated breakpoints. Check also for a breakpoint
+ placed (branch instruction's destination) at the stwcx/stdcx
+ instruction, this resets the reservation and take us back to the
+ lwarx/ldarx instruction at the beginning of the atomic sequence. */
+ if (last_breakpoint && ((breaks[1] == breaks[0])
+ || (breaks[1] == closing_insn)))
+ last_breakpoint = 0;
+
+ /* Effectively inserts the breakpoints. */
+ for (index = 0; index <= last_breakpoint; index++)
+ insert_single_step_breakpoint (breaks[index]);
+
+ return 1;
+}
+
+/* AIX does not support PT_STEP. Simulate it. */
+
+int
+rs6000_software_single_step (struct regcache *regcache)
{
CORE_ADDR dummy;
int breakp_sz;
CORE_ADDR breaks[2];
int opcode;
- if (insert_breakpoints_p)
- {
- loc = read_pc ();
+ loc = read_pc ();
- insn = read_memory_integer (loc, 4);
+ insn = read_memory_integer (loc, 4);
- breaks[0] = loc + breakp_sz;
- opcode = insn >> 26;
- breaks[1] = branch_dest (opcode, insn, loc, breaks[0]);
+ if (deal_with_atomic_sequence (regcache))
+ return 1;
+
+ breaks[0] = loc + breakp_sz;
+ opcode = insn >> 26;
+ breaks[1] = branch_dest (opcode, insn, loc, breaks[0]);
- /* Don't put two breakpoints on the same address. */
- if (breaks[1] == breaks[0])
- breaks[1] = -1;
+ /* Don't put two breakpoints on the same address. */
+ if (breaks[1] == breaks[0])
+ breaks[1] = -1;
- for (ii = 0; ii < 2; ++ii)
- {
- /* ignore invalid breakpoint. */
- if (breaks[ii] == -1)
- continue;
- insert_single_step_breakpoint (breaks[ii]);
- }
+ for (ii = 0; ii < 2; ++ii)
+ {
+ /* ignore invalid breakpoint. */
+ if (breaks[ii] == -1)
+ continue;
+ insert_single_step_breakpoint (breaks[ii]);
}
- else
- remove_single_step_breakpoints ();
errno = 0; /* FIXME, don't ignore errors! */
/* What errors? {read,write}_memory call error(). */
+ return 1;
}
struct value *arg = 0;
struct type *type;
- CORE_ADDR saved_sp;
+ ULONGEST saved_sp;
/* The calling convention this function implements assumes the
processor has floating-point registers. We shouldn't be using it
else
{
/* Argument can fit in one register. No problem. */
- int adj = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG ? reg_size - len : 0;
+ int adj = gdbarch_byte_order (current_gdbarch)
+ == BFD_ENDIAN_BIG ? reg_size - len : 0;
gdb_byte word[MAX_REGISTER_SIZE];
memset (word, 0, reg_size);
ran_out_of_registers_for_arguments:
- saved_sp = read_sp ();
+ regcache_cooked_read_unsigned (regcache, SP_REGNUM, &saved_sp);
/* Location for 8 parameters are always reserved. */
sp -= wordsize * 8;
regcache_raw_write_signed (regcache, tdep->ppc_toc_regnum, tocvalue);
}
- target_store_registers (-1);
+ target_store_registers (regcache, -1);
return sp;
}
int vector_p;
int general_p;
- if (REGISTER_NAME (regnum) == NULL
- || *REGISTER_NAME (regnum) == '\0')
+ if (gdbarch_register_name (current_gdbarch, regnum) == NULL
+ || *gdbarch_register_name (current_gdbarch, regnum) == '\0')
return 0;
if (group == all_reggroup)
return 1;
reg_index = ev_reg - tdep->ppc_ev0_regnum;
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+ if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
{
move (regcache, tdep->ppc_ev0_upper_regnum + reg_index, byte_buffer);
move (regcache, tdep->ppc_gp0_regnum + reg_index, byte_buffer + 4);
else
switch (num)
{
+ case 64:
+ return tdep->ppc_cr_regnum;
case 67:
return tdep->ppc_vrsave_regnum - 1; /* vscr */
case 99:
}
}
+/* Translate a .eh_frame register to DWARF register, or adjust a
+ .debug_frame register. */
+
+static int
+rs6000_adjust_frame_regnum (struct gdbarch *gdbarch, int num, int eh_frame_p)
+{
+ /* GCC releases before 3.4 use GCC internal register numbering in
+ .debug_frame (and .debug_info, et cetera). The numbering is
+ different from the standard SysV numbering for everything except
+ for GPRs and FPRs. We can not detect this problem in most cases
+ - to get accurate debug info for variables living in lr, ctr, v0,
+ et cetera, use a newer version of GCC. But we must detect
+ one important case - lr is in column 65 in .debug_frame output,
+ instead of 108.
+
+ GCC 3.4, and the "hammer" branch, have a related problem. They
+ record lr register saves in .debug_frame as 108, but still record
+ the return column as 65. We fix that up too.
+
+ We can do this because 65 is assigned to fpsr, and GCC never
+ generates debug info referring to it. To add support for
+ handwritten debug info that restores fpsr, we would need to add a
+ producer version check to this. */
+ if (!eh_frame_p)
+ {
+ if (num == 65)
+ return 108;
+ else
+ return num;
+ }
+
+ /* .eh_frame is GCC specific. For binary compatibility, it uses GCC
+ internal register numbering; translate that to the standard DWARF2
+ register numbering. */
+ if (0 <= num && num <= 63) /* r0-r31,fp0-fp31 */
+ return num;
+ else if (68 <= num && num <= 75) /* cr0-cr8 */
+ return num - 68 + 86;
+ else if (77 <= num && num <= 108) /* vr0-vr31 */
+ return num - 77 + 1124;
+ else
+ switch (num)
+ {
+ case 64: /* mq */
+ return 100;
+ case 65: /* lr */
+ return 108;
+ case 66: /* ctr */
+ return 109;
+ case 76: /* xer */
+ return 101;
+ case 109: /* vrsave */
+ return 356;
+ case 110: /* vscr */
+ return 67;
+ case 111: /* spe_acc */
+ return 99;
+ case 112: /* spefscr */
+ return 612;
+ default:
+ return num;
+ }
+}
\f
/* Support for CONVERT_FROM_FUNC_PTR_ADDR (ARCH, ADDR, TARG).
return addr;
/* ADDR is in the data space, so it's a special function pointer. */
- return read_memory_addr (addr, gdbarch_tdep (current_gdbarch)->wordsize);
+ return read_memory_addr (addr, gdbarch_tdep (gdbarch)->wordsize);
}
\f
if (!info->disassembler_options)
info->disassembler_options = "any";
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+ if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
return print_insn_big_powerpc (memaddr, info);
else
return print_insn_little_powerpc (memaddr, info);
if (make_frame)
{
fdata.frameless = 0;
- fdata.lr_offset = wordsize;
+ fdata.lr_offset = tdep->lr_frame_offset;
}
}
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_breakpoint_from_pc (gdbarch, rs6000_breakpoint_from_pc);
+ /* Handles single stepping of atomic sequences. */
+ set_gdbarch_software_single_step (gdbarch, deal_with_atomic_sequence);
+
/* Handle the 64-bit SVR4 minimal-symbol convention of using "FN"
for the descriptor and ".FN" for the entry-point -- a user
specifying "break FN" will unexpectedly end up with a breakpoint
(gdbarch, rs6000_in_solib_return_trampoline);
set_gdbarch_skip_trampoline_code (gdbarch, rs6000_skip_trampoline_code);
+ /* Hook in the DWARF CFI frame unwinder. */
+ frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
+ dwarf2_frame_set_adjust_regnum (gdbarch, rs6000_adjust_frame_regnum);
+
/* Hook in ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);