#include "trad-frame.h"
#include "objfiles.h"
#include "dwarf2-frame.h"
+#include "gdbtypes.h"
+#include "prologue-value.h"
#include "arm-tdep.h"
#include "gdb/sim-arm.h"
return val & ~1;
}
-/* Immediately after a function call, return the saved pc. Can't
- always go through the frames for this because on some machines the
- new frame is not set up until the new function executes some
- instructions. */
+/* Analyze a Thumb prologue, looking for a recognizable stack frame
+ and frame pointer. Scan until we encounter a store that could
+ clobber the stack frame unexpectedly, or an unknown instruction. */
static CORE_ADDR
-arm_saved_pc_after_call (struct frame_info *frame)
+thumb_analyze_prologue (struct gdbarch *gdbarch,
+ CORE_ADDR start, CORE_ADDR limit,
+ struct arm_prologue_cache *cache)
{
- return ADDR_BITS_REMOVE (read_register (ARM_LR_REGNUM));
-}
-
-/* A typical Thumb prologue looks like this:
- push {r7, lr}
- add sp, sp, #-28
- add r7, sp, #12
- Sometimes the latter instruction may be replaced by:
- mov r7, sp
-
- or like this:
- push {r7, lr}
- mov r7, sp
- sub sp, #12
-
- or, on tpcs, like this:
- sub sp,#16
- push {r7, lr}
- (many instructions)
- mov r7, sp
- sub sp, #12
-
- There is always one instruction of three classes:
- 1 - push
- 2 - setting of r7
- 3 - adjusting of sp
-
- When we have found at least one of each class we are done with the prolog.
- Note that the "sub sp, #NN" before the push does not count.
- */
-
-static CORE_ADDR
-thumb_skip_prologue (CORE_ADDR pc, CORE_ADDR func_end)
-{
- CORE_ADDR current_pc;
- /* findmask:
- bit 0 - push { rlist }
- bit 1 - mov r7, sp OR add r7, sp, #imm (setting of r7)
- bit 2 - sub sp, #simm OR add sp, #simm (adjusting of sp)
- */
- int findmask = 0;
+ int i;
+ pv_t regs[16];
+ struct pv_area *stack;
+ struct cleanup *back_to;
+ CORE_ADDR offset;
- for (current_pc = pc;
- current_pc + 2 < func_end && current_pc < pc + 40;
- current_pc += 2)
+ for (i = 0; i < 16; i++)
+ regs[i] = pv_register (i, 0);
+ stack = make_pv_area (ARM_SP_REGNUM);
+ back_to = make_cleanup_free_pv_area (stack);
+
+ /* The call instruction saved PC in LR, and the current PC is not
+ interesting. Due to this file's conventions, we want the value
+ of LR at this function's entry, not at the call site, so we do
+ not record the save of the PC - when the ARM prologue analyzer
+ has also been converted to the pv mechanism, we could record the
+ save here and remove the hack in prev_register. */
+ regs[ARM_PC_REGNUM] = pv_unknown ();
+
+ while (start < limit)
{
- unsigned short insn = read_memory_unsigned_integer (current_pc, 2);
+ unsigned short insn;
+
+ insn = read_memory_unsigned_integer (start, 2);
if ((insn & 0xfe00) == 0xb400) /* push { rlist } */
{
- findmask |= 1; /* push found */
+ int regno;
+ int mask;
+ int stop = 0;
+
+ /* Bits 0-7 contain a mask for registers R0-R7. Bit 8 says
+ whether to save LR (R14). */
+ mask = (insn & 0xff) | ((insn & 0x100) << 6);
+
+ /* Calculate offsets of saved R0-R7 and LR. */
+ for (regno = ARM_LR_REGNUM; regno >= 0; regno--)
+ if (mask & (1 << regno))
+ {
+ if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
+ {
+ stop = 1;
+ break;
+ }
+
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
+ -4);
+ pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[regno]);
+ }
+
+ if (stop)
+ break;
}
else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR
sub sp, #simm */
{
- if ((findmask & 1) == 0) /* before push ? */
- continue;
+ offset = (insn & 0x7f) << 2; /* get scaled offset */
+ if (insn & 0x80) /* Check for SUB. */
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
+ -offset);
else
- findmask |= 4; /* add/sub sp found */
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
+ offset);
}
else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */
+ regs[THUMB_FP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
+ (insn & 0xff) << 2);
+ else if ((insn & 0xff00) == 0x4600) /* mov hi, lo or mov lo, hi */
{
- findmask |= 2; /* setting of r7 found */
+ int dst_reg = (insn & 0x7) + ((insn & 0x80) >> 4);
+ int src_reg = (insn & 0x78) >> 3;
+ regs[dst_reg] = regs[src_reg];
}
- else if (insn == 0x466f) /* mov r7, sp */
+ else if ((insn & 0xf800) == 0x9000) /* str rd, [sp, #off] */
{
- findmask |= 2; /* setting of r7 found */
+ /* Handle stores to the stack. Normally pushes are used,
+ but with GCC -mtpcs-frame, there may be other stores
+ in the prologue to create the frame. */
+ int regno = (insn >> 8) & 0x7;
+ pv_t addr;
+
+ offset = (insn & 0xff) << 2;
+ addr = pv_add_constant (regs[ARM_SP_REGNUM], offset);
+
+ if (pv_area_store_would_trash (stack, addr))
+ break;
+
+ pv_area_store (stack, addr, 4, regs[regno]);
}
- else if (findmask == (4+2+1))
+ else
{
- /* We have found one of each type of prologue instruction */
+ /* We don't know what this instruction is. We're finished
+ scanning. NOTE: Recognizing more safe-to-ignore
+ instructions here will improve support for optimized
+ code. */
break;
}
- else
- /* Something in the prolog that we don't care about or some
- instruction from outside the prolog scheduled here for
- optimization. */
- continue;
+
+ start += 2;
}
- return current_pc;
+ if (cache == NULL)
+ {
+ do_cleanups (back_to);
+ return start;
+ }
+
+ /* frameoffset is unused for this unwinder. */
+ cache->frameoffset = 0;
+
+ if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
+ {
+ /* Frame pointer is fp. Frame size is constant. */
+ cache->framereg = ARM_FP_REGNUM;
+ cache->framesize = -regs[ARM_FP_REGNUM].k;
+ }
+ else if (pv_is_register (regs[THUMB_FP_REGNUM], ARM_SP_REGNUM))
+ {
+ /* Frame pointer is r7. Frame size is constant. */
+ cache->framereg = THUMB_FP_REGNUM;
+ cache->framesize = -regs[THUMB_FP_REGNUM].k;
+ }
+ else if (pv_is_register (regs[ARM_SP_REGNUM], ARM_SP_REGNUM))
+ {
+ /* Try the stack pointer... this is a bit desperate. */
+ cache->framereg = ARM_SP_REGNUM;
+ cache->framesize = -regs[ARM_SP_REGNUM].k;
+ }
+ else
+ {
+ /* We're just out of luck. We don't know where the frame is. */
+ cache->framereg = -1;
+ cache->framesize = 0;
+ }
+
+ for (i = 0; i < 16; i++)
+ if (pv_area_find_reg (stack, gdbarch, i, &offset))
+ cache->saved_regs[i].addr = offset;
+
+ do_cleanups (back_to);
+ return start;
}
/* Advance the PC across any function entry prologue instructions to
}
}
- /* Check if this is Thumb code. */
- if (arm_pc_is_thumb (pc))
- return thumb_skip_prologue (pc, func_end);
-
/* Can't find the prologue end in the symbol table, try it the hard way
by disassembling the instructions. */
if (func_end == 0 || func_end > pc + 64)
func_end = pc + 64;
+ /* Check if this is Thumb code. */
+ if (arm_pc_is_thumb (pc))
+ return thumb_analyze_prologue (current_gdbarch, pc, func_end, NULL);
+
for (skip_pc = pc; skip_pc < func_end; skip_pc += 4)
{
inst = read_memory_unsigned_integer (skip_pc, 4);
prologue_end = min (prologue_end, prev_pc);
- /* Initialize the saved register map. When register H is copied to
- register L, we will put H in saved_reg[L]. */
- for (i = 0; i < 16; i++)
- saved_reg[i] = i;
-
- /* Search the prologue looking for instructions that set up the
- frame pointer, adjust the stack pointer, and save registers.
- Do this until all basic prolog instructions are found. */
-
- cache->framesize = 0;
- for (current_pc = prologue_start;
- (current_pc < prologue_end) && ((findmask & 7) != 7);
- current_pc += 2)
- {
- unsigned short insn;
- int regno;
- int offset;
-
- insn = read_memory_unsigned_integer (current_pc, 2);
-
- if ((insn & 0xfe00) == 0xb400) /* push { rlist } */
- {
- int mask;
- findmask |= 1; /* push found */
- /* Bits 0-7 contain a mask for registers R0-R7. Bit 8 says
- whether to save LR (R14). */
- mask = (insn & 0xff) | ((insn & 0x100) << 6);
-
- /* Calculate offsets of saved R0-R7 and LR. */
- for (regno = ARM_LR_REGNUM; regno >= 0; regno--)
- if (mask & (1 << regno))
- {
- cache->framesize += 4;
- cache->saved_regs[saved_reg[regno]].addr = -cache->framesize;
- /* Reset saved register map. */
- saved_reg[regno] = regno;
- }
- }
- else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR
- sub sp, #simm */
- {
- if ((findmask & 1) == 0) /* before push? */
- continue;
- else
- findmask |= 4; /* add/sub sp found */
-
- offset = (insn & 0x7f) << 2; /* get scaled offset */
- if (insn & 0x80) /* is it signed? (==subtracting) */
- {
- cache->frameoffset += offset;
- offset = -offset;
- }
- cache->framesize -= offset;
- }
- else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */
- {
- findmask |= 2; /* setting of r7 found */
- cache->framereg = THUMB_FP_REGNUM;
- /* get scaled offset */
- cache->frameoffset = (insn & 0xff) << 2;
- }
- else if (insn == 0x466f) /* mov r7, sp */
- {
- findmask |= 2; /* setting of r7 found */
- cache->framereg = THUMB_FP_REGNUM;
- cache->frameoffset = 0;
- saved_reg[THUMB_FP_REGNUM] = ARM_SP_REGNUM;
- }
- else if ((insn & 0xffc0) == 0x4640) /* mov r0-r7, r8-r15 */
- {
- int lo_reg = insn & 7; /* dest. register (r0-r7) */
- int hi_reg = ((insn >> 3) & 7) + 8; /* source register (r8-15) */
- saved_reg[lo_reg] = hi_reg; /* remember hi reg was saved */
- }
- else
- /* Something in the prolog that we don't care about or some
- instruction from outside the prolog scheduled here for
- optimization. */
- continue;
- }
+ thumb_analyze_prologue (current_gdbarch, prologue_start, prologue_end,
+ cache);
}
/* This function decodes an ARM function prologue to determine:
struct arm_prologue_cache *cache;
CORE_ADDR unwound_fp;
- cache = frame_obstack_zalloc (sizeof (struct arm_prologue_cache));
+ cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
arm_scan_prologue (next_frame, cache);
struct arm_prologue_cache *cache;
CORE_ADDR unwound_fp;
- cache = frame_obstack_zalloc (sizeof (struct arm_prologue_cache));
+ cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
cache->prev_sp = frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM);
{
CORE_ADDR pc;
pc = frame_unwind_register_unsigned (this_frame, ARM_PC_REGNUM);
- return IS_THUMB_ADDR (pc) ? UNMAKE_THUMB_ADDR (pc) : pc;
+ return arm_addr_bits_remove (pc);
}
static CORE_ADDR
else
return builtin_type_arm_ext_littlebyte_bigword;
}
+ else if (regnum == ARM_SP_REGNUM)
+ return builtin_type_void_data_ptr;
+ else if (regnum == ARM_PC_REGNUM)
+ return builtin_type_void_func_ptr;
else
- return builtin_type_int32;
+ return builtin_type_uint32;
}
/* Index within `registers' of the first byte of the space for
necessary) to point to the actual memory location where the
breakpoint should be inserted. */
-/* XXX ??? from old tm-arm.h: if we're using RDP, then we're inserting
- breakpoints and storing their handles instread of what was in
- memory. It is nice that this is the same size as a handle -
- otherwise remote-rdp will have to change. */
-
static const unsigned char *
arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
{
struct regcache *regcache, gdb_byte *readbuf,
const gdb_byte *writebuf)
{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
if (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
|| TYPE_CODE (valtype) == TYPE_CODE_UNION
|| TYPE_CODE (valtype) == TYPE_CODE_ARRAY)
{
- if (arm_return_in_memory (gdbarch, valtype))
+ if (tdep->struct_return == pcc_struct_return
+ || arm_return_in_memory (gdbarch, valtype))
return RETURN_VALUE_STRUCT_CONVENTION;
}
if (arm_abi == ARM_ABI_AUTO && info.abfd != NULL)
{
- int ei_osabi;
+ int ei_osabi, e_flags;
switch (bfd_get_flavour (info.abfd))
{
case bfd_target_elf_flavour:
ei_osabi = elf_elfheader (info.abfd)->e_ident[EI_OSABI];
+ e_flags = elf_elfheader (info.abfd)->e_flags;
+
if (ei_osabi == ELFOSABI_ARM)
{
/* GNU tools used to use this value, but do not for EABI
- objects. There's nowhere to tag an EABI version anyway,
- so assume APCS. */
+ objects. There's nowhere to tag an EABI version
+ anyway, so assume APCS. */
arm_abi = ARM_ABI_APCS;
}
else if (ei_osabi == ELFOSABI_NONE)
{
- int e_flags, eabi_ver;
-
- e_flags = elf_elfheader (info.abfd)->e_flags;
- eabi_ver = EF_ARM_EABI_VERSION (e_flags);
+ int eabi_ver = EF_ARM_EABI_VERSION (e_flags);
switch (eabi_ver)
{
break;
case EF_ARM_EABI_VER4:
+ case EF_ARM_EABI_VER5:
arm_abi = ARM_ABI_AAPCS;
/* EABI binaries default to VFP float ordering. */
if (fp_model == ARM_FLOAT_AUTO)
break;
default:
+ /* Leave it as "auto". */
warning (_("unknown ARM EABI version 0x%x"), eabi_ver);
- arm_abi = ARM_ABI_APCS;
+ break;
+ }
+ }
+
+ if (fp_model == ARM_FLOAT_AUTO)
+ {
+ int e_flags = elf_elfheader (info.abfd)->e_flags;
+
+ switch (e_flags & (EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT))
+ {
+ case 0:
+ /* Leave it as "auto". Strictly speaking this case
+ means FPA, but almost nobody uses that now, and
+ many toolchains fail to set the appropriate bits
+ for the floating-point model they use. */
+ break;
+ case EF_ARM_SOFT_FLOAT:
+ fp_model = ARM_FLOAT_SOFT_FPA;
+ break;
+ case EF_ARM_VFP_FLOAT:
+ fp_model = ARM_FLOAT_VFP;
+ break;
+ case EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT:
+ fp_model = ARM_FLOAT_SOFT_VFP;
break;
}
}
tdep->lowest_pc = 0x20;
tdep->jb_pc = -1; /* Longjump support not enabled by default. */
+ /* The default, for both APCS and AAPCS, is to return small
+ structures in registers. */
+ tdep->struct_return = reg_struct_return;
+
set_gdbarch_push_dummy_call (gdbarch, arm_push_dummy_call);
set_gdbarch_frame_align (gdbarch, arm_frame_align);
/* Advance PC across function entry code. */
set_gdbarch_skip_prologue (gdbarch, arm_skip_prologue);
- /* Get the PC when a frame might not be available. */
- set_gdbarch_deprecated_saved_pc_after_call (gdbarch, arm_saved_pc_after_call);
-
/* The stack grows downward. */
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);