#include "gdb_assert.h"
#include "vec.h"
+#include "features/arm-with-m.c"
+
static int arm_debug;
/* Macros for setting and testing a bit in a minimal symbol that marks
{
"auto",
"arm",
- "thumb"
+ "thumb",
+ NULL
};
static const char *arm_fallback_mode_string = "auto";
int arm_apcs_32 = 1;
-/* Determine if FRAME is executing in Thumb mode. */
+/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */
static int
+arm_psr_thumb_bit (struct gdbarch *gdbarch)
+{
+ if (gdbarch_tdep (gdbarch)->is_m)
+ return XPSR_T;
+ else
+ return CPSR_T;
+}
+
+/* Determine if FRAME is executing in Thumb mode. */
+
+int
arm_frame_is_thumb (struct frame_info *frame)
{
CORE_ADDR cpsr;
+ ULONGEST t_bit = arm_psr_thumb_bit (get_frame_arch (frame));
/* Every ARM frame unwinder can unwind the T bit of the CPSR, either
directly (from a signal frame or dummy frame) or by interpreting
trust the unwinders. */
cpsr = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
- return (cpsr & CPSR_T) != 0;
+ return (cpsr & t_bit) != 0;
}
/* Callback for VEC_lower_bound. */
any executing frame; otherwise, prefer arm_frame_is_thumb. */
static int
-arm_pc_is_thumb (CORE_ADDR memaddr)
+arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
{
struct obj_section *sec;
struct minimal_symbol *sym;
if (strcmp (arm_force_mode_string, "thumb") == 0)
return 1;
+ /* ARM v6-M and v7-M are always in Thumb mode. */
+ if (gdbarch_tdep (gdbarch)->is_m)
+ return 1;
+
/* If there are mapping symbols, consult them. */
type = arm_find_mapping_symbol (memaddr, NULL);
if (type)
if (strncmp (name, "__aeabi_d2f", strlen ("__aeabi_d2f")) == 0)
return 1;
+ /* Internal functions related to thread-local storage. */
+ if (strncmp (name, "__tls_get_addr", strlen ("__tls_get_addr")) == 0)
+ return 1;
+ if (strncmp (name, "__aeabi_read_tp", strlen ("__aeabi_read_tp")) == 0)
+ return 1;
+
return 0;
}
#define BranchDest(addr,instr) \
((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
+/* Decode immediate value; implements ThumbExpandImmediate pseudo-op. */
+
+static unsigned int
+thumb_expand_immediate (unsigned int imm)
+{
+ unsigned int count = imm >> 7;
+
+ if (count < 8)
+ switch (count / 2)
+ {
+ case 0:
+ return imm & 0xff;
+ case 1:
+ return (imm & 0xff) | ((imm & 0xff) << 16);
+ case 2:
+ return ((imm & 0xff) << 8) | ((imm & 0xff) << 24);
+ case 3:
+ return (imm & 0xff) | ((imm & 0xff) << 8)
+ | ((imm & 0xff) << 16) | ((imm & 0xff) << 24);
+ }
+
+ return (0x80 | (imm & 0x7f)) << (32 - count);
+}
+
+/* Return 1 if the 16-bit Thumb instruction INST might change
+ control flow, 0 otherwise. */
+
+static int
+thumb_instruction_changes_pc (unsigned short inst)
+{
+ if ((inst & 0xff00) == 0xbd00) /* pop {rlist, pc} */
+ return 1;
+
+ if ((inst & 0xf000) == 0xd000) /* conditional branch */
+ return 1;
+
+ if ((inst & 0xf800) == 0xe000) /* unconditional branch */
+ return 1;
+
+ if ((inst & 0xff00) == 0x4700) /* bx REG, blx REG */
+ return 1;
+
+ if ((inst & 0xff87) == 0x4687) /* mov pc, REG */
+ return 1;
+
+ if ((inst & 0xf500) == 0xb100) /* CBNZ or CBZ. */
+ return 1;
+
+ return 0;
+}
+
+/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2
+ might change control flow, 0 otherwise. */
+
+static int
+thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2)
+{
+ if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
+ {
+ /* Branches and miscellaneous control instructions. */
+
+ if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
+ {
+ /* B, BL, BLX. */
+ return 1;
+ }
+ else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
+ {
+ /* SUBS PC, LR, #imm8. */
+ return 1;
+ }
+ else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
+ {
+ /* Conditional branch. */
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if ((inst1 & 0xfe50) == 0xe810)
+ {
+ /* Load multiple or RFE. */
+
+ if (bit (inst1, 7) && !bit (inst1, 8))
+ {
+ /* LDMIA or POP */
+ if (bit (inst2, 15))
+ return 1;
+ }
+ else if (!bit (inst1, 7) && bit (inst1, 8))
+ {
+ /* LDMDB */
+ if (bit (inst2, 15))
+ return 1;
+ }
+ else if (bit (inst1, 7) && bit (inst1, 8))
+ {
+ /* RFEIA */
+ return 1;
+ }
+ else if (!bit (inst1, 7) && !bit (inst1, 8))
+ {
+ /* RFEDB */
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+ {
+ /* MOV PC or MOVS PC. */
+ return 1;
+ }
+
+ if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
+ {
+ /* LDR PC. */
+ if (bits (inst1, 0, 3) == 15)
+ return 1;
+ if (bit (inst1, 7))
+ return 1;
+ if (bit (inst2, 11))
+ return 1;
+ if ((inst2 & 0x0fc0) == 0x0000)
+ return 1;
+
+ return 0;
+ }
+
+ if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
+ {
+ /* TBB. */
+ return 1;
+ }
+
+ if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
+ {
+ /* TBH. */
+ return 1;
+ }
+
+ return 0;
+}
+
/* 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.
struct pv_area *stack;
struct cleanup *back_to;
CORE_ADDR offset;
+ CORE_ADDR unrecognized_pc = 0;
for (i = 0; i < 16; i++)
regs[i] = pv_register (i, 0);
constant = read_memory_unsigned_integer (loc, 4, byte_order);
regs[bits (insn, 8, 10)] = pv_constant (constant);
}
- else if ((insn & 0xe000) == 0xe000 && cache == NULL)
+ else if ((insn & 0xe000) == 0xe000)
{
- /* Only recognize 32-bit instructions for prologue skipping. */
unsigned short inst2;
inst2 = read_memory_unsigned_integer (start + 2, 2,
if (!skip_prologue_function (nextpc))
break;
}
- else if ((insn & 0xfe50) == 0xe800 /* stm{db,ia} Rn[!], { registers } */
+
+ else if ((insn & 0xffd0) == 0xe900 /* stmdb Rn{!}, { registers } */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ {
+ pv_t addr = regs[bits (insn, 0, 3)];
+ int regno;
+
+ if (pv_area_store_would_trash (stack, addr))
+ break;
+
+ /* Calculate offsets of saved registers. */
+ for (regno = ARM_LR_REGNUM; regno >= 0; regno--)
+ if (inst2 & (1 << regno))
+ {
+ addr = pv_add_constant (addr, -4);
+ pv_area_store (stack, addr, 4, regs[regno]);
+ }
+
+ if (insn & 0x0020)
+ regs[bits (insn, 0, 3)] = addr;
+ }
+
+ else if ((insn & 0xff50) == 0xe940 /* strd Rt, Rt2, [Rn, #+/-imm]{!} */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ {
+ int regno1 = bits (inst2, 12, 15);
+ int regno2 = bits (inst2, 8, 11);
+ pv_t addr = regs[bits (insn, 0, 3)];
+
+ offset = inst2 & 0xff;
+ if (insn & 0x0080)
+ addr = pv_add_constant (addr, offset);
+ else
+ addr = pv_add_constant (addr, -offset);
+
+ if (pv_area_store_would_trash (stack, addr))
+ break;
+
+ pv_area_store (stack, addr, 4, regs[regno1]);
+ pv_area_store (stack, pv_add_constant (addr, 4),
+ 4, regs[regno2]);
+
+ if (insn & 0x0020)
+ regs[bits (insn, 0, 3)] = addr;
+ }
+
+ else if ((insn & 0xfff0) == 0xf8c0 /* str Rt,[Rn,+/-#imm]{!} */
+ && (inst2 & 0x0c00) == 0x0c00
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ {
+ int regno = bits (inst2, 12, 15);
+ pv_t addr = regs[bits (insn, 0, 3)];
+
+ offset = inst2 & 0xff;
+ if (inst2 & 0x0200)
+ addr = pv_add_constant (addr, offset);
+ else
+ addr = pv_add_constant (addr, -offset);
+
+ if (pv_area_store_would_trash (stack, addr))
+ break;
+
+ pv_area_store (stack, addr, 4, regs[regno]);
+
+ if (inst2 & 0x0100)
+ regs[bits (insn, 0, 3)] = addr;
+ }
+
+ else if ((insn & 0xfff0) == 0xf8c0 /* str.w Rt,[Rn,#imm] */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ {
+ int regno = bits (inst2, 12, 15);
+ pv_t addr;
+
+ offset = inst2 & 0xfff;
+ addr = pv_add_constant (regs[bits (insn, 0, 3)], offset);
+
+ if (pv_area_store_would_trash (stack, addr))
+ break;
+
+ pv_area_store (stack, addr, 4, regs[regno]);
+ }
+
+ else if ((insn & 0xffd0) == 0xf880 /* str{bh}.w Rt,[Rn,#imm] */
&& pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ /* Ignore stores of argument registers to the stack. */
;
- else if ((insn & 0xfe50) == 0xe840 /* strd Rt, Rt2, [Rn, #imm] */
+
+ else if ((insn & 0xffd0) == 0xf800 /* str{bh} Rt,[Rn,#+/-imm] */
+ && (inst2 & 0x0d00) == 0x0c00
&& pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ /* Ignore stores of argument registers to the stack. */
;
+
else if ((insn & 0xffd0) == 0xe890 /* ldmia Rn[!], { registers } */
- && (inst2 & 0x8000) == 0x0000
- && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ && (inst2 & 0x8000) == 0x0000
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ /* Ignore block loads from the stack, potentially copying
+ parameters from memory. */
;
- else if ((insn & 0xfbf0) == 0xf100 /* add.w Rd, Rn, #imm */
- && (inst2 & 0x8000) == 0x0000)
- /* Since we only recognize this for prologue skipping, do not bother
- to compute the constant. */
- regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)];
- else if ((insn & 0xfbf0) == 0xf1a0 /* sub.w Rd, Rn, #imm12 */
- && (inst2 & 0x8000) == 0x0000)
- /* Since we only recognize this for prologue skipping, do not bother
- to compute the constant. */
- regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)];
- else if ((insn & 0xfbf0) == 0xf2a0 /* sub.w Rd, Rn, #imm8 */
- && (inst2 & 0x8000) == 0x0000)
- /* Since we only recognize this for prologue skipping, do not bother
- to compute the constant. */
- regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)];
- else if ((insn & 0xff50) == 0xf850 /* ldr.w Rd, [Rn, #imm]{!} */
+
+ else if ((insn & 0xffb0) == 0xe950 /* ldrd Rt, Rt2, [Rn, #+/-imm] */
&& pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ /* Similarly ignore dual loads from the stack. */
;
- else if ((insn & 0xff50) == 0xe950 /* ldrd Rt, Rt2, [Rn, #imm]{!} */
+
+ else if ((insn & 0xfff0) == 0xf850 /* ldr Rt,[Rn,#+/-imm] */
+ && (inst2 & 0x0d00) == 0x0c00
&& pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ /* Similarly ignore single loads from the stack. */
;
- else if ((insn & 0xff50) == 0xf800 /* strb.w or strh.w */
+
+ else if ((insn & 0xfff0) == 0xf8d0 /* ldr.w Rt,[Rn,#imm] */
&& pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ /* Similarly ignore single loads from the stack. */
;
- else
+
+ else if ((insn & 0xfbf0) == 0xf100 /* add.w Rd, Rn, #imm */
+ && (inst2 & 0x8000) == 0x0000)
+ {
+ unsigned int imm = ((bits (insn, 10, 10) << 11)
+ | (bits (inst2, 12, 14) << 8)
+ | bits (inst2, 0, 7));
+
+ regs[bits (inst2, 8, 11)]
+ = pv_add_constant (regs[bits (insn, 0, 3)],
+ thumb_expand_immediate (imm));
+ }
+
+ else if ((insn & 0xfbf0) == 0xf200 /* addw Rd, Rn, #imm */
+ && (inst2 & 0x8000) == 0x0000)
+ {
+ unsigned int imm = ((bits (insn, 10, 10) << 11)
+ | (bits (inst2, 12, 14) << 8)
+ | bits (inst2, 0, 7));
+
+ regs[bits (inst2, 8, 11)]
+ = pv_add_constant (regs[bits (insn, 0, 3)], imm);
+ }
+
+ else if ((insn & 0xfbf0) == 0xf1a0 /* sub.w Rd, Rn, #imm */
+ && (inst2 & 0x8000) == 0x0000)
+ {
+ unsigned int imm = ((bits (insn, 10, 10) << 11)
+ | (bits (inst2, 12, 14) << 8)
+ | bits (inst2, 0, 7));
+
+ regs[bits (inst2, 8, 11)]
+ = pv_add_constant (regs[bits (insn, 0, 3)],
+ - (CORE_ADDR) thumb_expand_immediate (imm));
+ }
+
+ else if ((insn & 0xfbf0) == 0xf2a0 /* subw Rd, Rn, #imm */
+ && (inst2 & 0x8000) == 0x0000)
+ {
+ unsigned int imm = ((bits (insn, 10, 10) << 11)
+ | (bits (inst2, 12, 14) << 8)
+ | bits (inst2, 0, 7));
+
+ regs[bits (inst2, 8, 11)]
+ = pv_add_constant (regs[bits (insn, 0, 3)], - (CORE_ADDR) imm);
+ }
+
+ else if ((insn & 0xfbff) == 0xf04f) /* mov.w Rd, #const */
+ {
+ unsigned int imm = ((bits (insn, 10, 10) << 11)
+ | (bits (inst2, 12, 14) << 8)
+ | bits (inst2, 0, 7));
+
+ regs[bits (inst2, 8, 11)]
+ = pv_constant (thumb_expand_immediate (imm));
+ }
+
+ else if ((insn & 0xfbf0) == 0xf240) /* movw Rd, #const */
+ {
+ unsigned int imm = ((bits (insn, 0, 3) << 12)
+ | (bits (insn, 10, 10) << 11)
+ | (bits (inst2, 12, 14) << 8)
+ | bits (inst2, 0, 7));
+
+ regs[bits (inst2, 8, 11)] = pv_constant (imm);
+ }
+
+ else if (insn == 0xea5f /* mov.w Rd,Rm */
+ && (inst2 & 0xf0f0) == 0)
+ {
+ int dst_reg = (inst2 & 0x0f00) >> 8;
+ int src_reg = inst2 & 0xf;
+ regs[dst_reg] = regs[src_reg];
+ }
+
+ else if ((insn & 0xff7f) == 0xf85f) /* ldr.w Rt,<label> */
+ {
+ /* Constant pool loads. */
+ unsigned int constant;
+ CORE_ADDR loc;
+
+ offset = bits (insn, 0, 11);
+ if (insn & 0x0080)
+ loc = start + 4 + offset;
+ else
+ loc = start + 4 - offset;
+
+ constant = read_memory_unsigned_integer (loc, 4, byte_order);
+ regs[bits (inst2, 12, 15)] = pv_constant (constant);
+ }
+
+ else if ((insn & 0xff7f) == 0xe95f) /* ldrd Rt,Rt2,<label> */
{
- /* 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. */
+ /* Constant pool loads. */
+ unsigned int constant;
+ CORE_ADDR loc;
+
+ offset = bits (insn, 0, 7) << 2;
+ if (insn & 0x0080)
+ loc = start + 4 + offset;
+ else
+ loc = start + 4 - offset;
+
+ constant = read_memory_unsigned_integer (loc, 4, byte_order);
+ regs[bits (inst2, 12, 15)] = pv_constant (constant);
+
+ constant = read_memory_unsigned_integer (loc + 4, 4, byte_order);
+ regs[bits (inst2, 8, 11)] = pv_constant (constant);
+ }
+
+ else if (thumb2_instruction_changes_pc (insn, inst2))
+ {
+ /* Don't scan past anything that might change control flow. */
break;
}
+ else
+ {
+ /* The optimizer might shove anything into the prologue,
+ so we just skip what we don't recognize. */
+ unrecognized_pc = start;
+ }
start += 2;
}
- else
+ else if (thumb_instruction_changes_pc (insn))
{
- /* 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. */
+ /* Don't scan past anything that might change control flow. */
break;
}
+ else
+ {
+ /* The optimizer might shove anything into the prologue,
+ so we just skip what we don't recognize. */
+ unrecognized_pc = start;
+ }
start += 2;
}
fprintf_unfiltered (gdb_stdlog, "Prologue scan stopped at %s\n",
paddress (gdbarch, start));
+ if (unrecognized_pc == 0)
+ unrecognized_pc = start;
+
if (cache == NULL)
{
do_cleanups (back_to);
- return start;
+ return unrecognized_pc;
}
if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
cache->saved_regs[i].addr = offset;
do_cleanups (back_to);
- return start;
+ return unrecognized_pc;
}
/* Advance the PC across any function entry prologue instructions to
associate prologue code with the opening brace; so this
lets us skip the first line if we think it is the opening
brace. */
- if (arm_pc_is_thumb (func_addr))
+ if (arm_pc_is_thumb (gdbarch, func_addr))
analyzed_limit = thumb_analyze_prologue (gdbarch, func_addr,
post_prologue_pc, NULL);
else
/* Check if this is Thumb code. */
- if (arm_pc_is_thumb (pc))
+ if (arm_pc_is_thumb (gdbarch, pc))
return thumb_analyze_prologue (gdbarch, pc, limit_pc, NULL);
for (skip_pc = pc; skip_pc < limit_pc; skip_pc += 4)
if (find_pc_partial_function (block_addr, NULL, &prologue_start,
&prologue_end))
{
- struct symtab_and_line sal = find_pc_line (prologue_start, 0);
-
- if (sal.line == 0) /* no line info, use current PC */
- prologue_end = prev_pc;
- else if (sal.end < prologue_end) /* next line begins after fn end */
- prologue_end = sal.end; /* (probably means no prologue) */
+ /* See comment in arm_scan_prologue for an explanation of
+ this heuristics. */
+ if (prologue_end > prologue_start + 64)
+ {
+ prologue_end = prologue_start + 64;
+ }
}
else
/* We're in the boondocks: we have no idea where the start of the
if (prev_regnum == ARM_PS_REGNUM)
{
CORE_ADDR lr, cpsr;
+ ULONGEST t_bit = arm_psr_thumb_bit (gdbarch);
cpsr = get_frame_register_unsigned (this_frame, prev_regnum);
lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
if (IS_THUMB_ADDR (lr))
- cpsr |= CPSR_T;
+ cpsr |= t_bit;
else
- cpsr &= ~CPSR_T;
+ cpsr &= ~t_bit;
return frame_unwind_got_constant (this_frame, prev_regnum, cpsr);
}
{
struct gdbarch * gdbarch = get_frame_arch (this_frame);
CORE_ADDR lr, cpsr;
+ ULONGEST t_bit = arm_psr_thumb_bit (gdbarch);
switch (regnum)
{
cpsr = get_frame_register_unsigned (this_frame, regnum);
lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
if (IS_THUMB_ADDR (lr))
- cpsr |= CPSR_T;
+ cpsr |= t_bit;
else
- cpsr &= ~CPSR_T;
+ cpsr &= ~t_bit;
return frame_unwind_got_constant (this_frame, regnum, cpsr);
default:
}
}
-/* When arguments must be pushed onto the stack, they go on in reverse
- order. The code below implements a FILO (stack) to do this. */
+/* Return true if we are in the function's epilogue, i.e. after the
+ instruction that destroyed the function's stack frame. */
-struct stack_item
+static int
+thumb_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
{
- int len;
- struct stack_item *prev;
- void *data;
-};
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ unsigned int insn, insn2;
+ int found_return = 0, found_stack_adjust = 0;
+ CORE_ADDR func_start, func_end;
+ CORE_ADDR scan_pc;
+ gdb_byte buf[4];
-static struct stack_item *
-push_stack_item (struct stack_item *prev, const 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;
-}
+ if (!find_pc_partial_function (pc, NULL, &func_start, &func_end))
+ return 0;
-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;
-}
+ /* The epilogue is a sequence of instructions along the following lines:
+ - add stack frame size to SP or FP
+ - [if frame pointer used] restore SP from FP
+ - restore registers from SP [may include PC]
+ - a return-type instruction [if PC wasn't already restored]
-/* Return the alignment (in bytes) of the given type. */
+ In a first pass, we scan forward from the current PC and verify the
+ instructions we find as compatible with this sequence, ending in a
+ return instruction.
-static int
-arm_type_align (struct type *t)
-{
- int n;
- int align;
- int falign;
+ However, this is not sufficient to distinguish indirect function calls
+ within a function from indirect tail calls in the epilogue in some cases.
+ Therefore, if we didn't already find any SP-changing instruction during
+ forward scan, we add a backward scanning heuristic to ensure we actually
+ are in the epilogue. */
- t = check_typedef (t);
- switch (TYPE_CODE (t))
+ scan_pc = pc;
+ while (scan_pc < func_end && !found_return)
{
- default:
- /* Should never happen. */
- internal_error (__FILE__, __LINE__, _("unknown type alignment"));
- return 4;
+ if (target_read_memory (scan_pc, buf, 2))
+ break;
- case TYPE_CODE_PTR:
- case TYPE_CODE_ENUM:
- case TYPE_CODE_INT:
- case TYPE_CODE_FLT:
- case TYPE_CODE_SET:
+ scan_pc += 2;
+ insn = extract_unsigned_integer (buf, 2, byte_order_for_code);
+
+ if ((insn & 0xff80) == 0x4700) /* bx <Rm> */
+ found_return = 1;
+ else if (insn == 0x46f7) /* mov pc, lr */
+ found_return = 1;
+ else if (insn == 0x46bd) /* mov sp, r7 */
+ found_stack_adjust = 1;
+ else if ((insn & 0xff00) == 0xb000) /* add sp, imm or sub sp, imm */
+ found_stack_adjust = 1;
+ else if ((insn & 0xfe00) == 0xbc00) /* pop <registers> */
+ {
+ found_stack_adjust = 1;
+ if (insn & 0x0100) /* <registers> include PC. */
+ found_return = 1;
+ }
+ else if ((insn & 0xe000) == 0xe000) /* 32-bit Thumb-2 instruction */
+ {
+ if (target_read_memory (scan_pc, buf, 2))
+ break;
+
+ scan_pc += 2;
+ insn2 = extract_unsigned_integer (buf, 2, byte_order_for_code);
+
+ if (insn == 0xe8bd) /* ldm.w sp!, <registers> */
+ {
+ found_stack_adjust = 1;
+ if (insn2 & 0x8000) /* <registers> include PC. */
+ found_return = 1;
+ }
+ else if (insn == 0xf85d /* ldr.w <Rt>, [sp], #4 */
+ && (insn2 & 0x0fff) == 0x0b04)
+ {
+ found_stack_adjust = 1;
+ if ((insn2 & 0xf000) == 0xf000) /* <Rt> is PC. */
+ found_return = 1;
+ }
+ else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, <list> */
+ && (insn2 & 0x0e00) == 0x0a00)
+ found_stack_adjust = 1;
+ else
+ break;
+ }
+ else
+ break;
+ }
+
+ if (!found_return)
+ return 0;
+
+ /* Since any instruction in the epilogue sequence, with the possible
+ exception of return itself, updates the stack pointer, we need to
+ scan backwards for at most one instruction. Try either a 16-bit or
+ a 32-bit instruction. This is just a heuristic, so we do not worry
+ too much about false positives.*/
+
+ if (!found_stack_adjust)
+ {
+ if (pc - 4 < func_start)
+ return 0;
+ if (target_read_memory (pc - 4, buf, 4))
+ return 0;
+
+ insn = extract_unsigned_integer (buf, 2, byte_order_for_code);
+ insn2 = extract_unsigned_integer (buf + 2, 2, byte_order_for_code);
+
+ if (insn2 == 0x46bd) /* mov sp, r7 */
+ found_stack_adjust = 1;
+ else if ((insn2 & 0xff00) == 0xb000) /* add sp, imm or sub sp, imm */
+ found_stack_adjust = 1;
+ else if ((insn2 & 0xff00) == 0xbc00) /* pop <registers> without PC */
+ found_stack_adjust = 1;
+ else if (insn == 0xe8bd) /* ldm.w sp!, <registers> */
+ found_stack_adjust = 1;
+ else if (insn == 0xf85d /* ldr.w <Rt>, [sp], #4 */
+ && (insn2 & 0x0fff) == 0x0b04)
+ found_stack_adjust = 1;
+ else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, <list> */
+ && (insn2 & 0x0e00) == 0x0a00)
+ found_stack_adjust = 1;
+ }
+
+ return found_stack_adjust;
+}
+
+/* Return true if we are in the function's epilogue, i.e. after the
+ instruction that destroyed the function's stack frame. */
+
+static int
+arm_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ unsigned int insn;
+ int found_return, found_stack_adjust;
+ CORE_ADDR func_start, func_end;
+
+ if (arm_pc_is_thumb (gdbarch, pc))
+ return thumb_in_function_epilogue_p (gdbarch, pc);
+
+ if (!find_pc_partial_function (pc, NULL, &func_start, &func_end))
+ return 0;
+
+ /* We are in the epilogue if the previous instruction was a stack
+ adjustment and the next instruction is a possible return (bx, mov
+ pc, or pop). We could have to scan backwards to find the stack
+ adjustment, or forwards to find the return, but this is a decent
+ approximation. First scan forwards. */
+
+ found_return = 0;
+ insn = read_memory_unsigned_integer (pc, 4, byte_order_for_code);
+ if (bits (insn, 28, 31) != INST_NV)
+ {
+ if ((insn & 0x0ffffff0) == 0x012fff10)
+ /* BX. */
+ found_return = 1;
+ else if ((insn & 0x0ffffff0) == 0x01a0f000)
+ /* MOV PC. */
+ found_return = 1;
+ else if ((insn & 0x0fff0000) == 0x08bd0000
+ && (insn & 0x0000c000) != 0)
+ /* POP (LDMIA), including PC or LR. */
+ found_return = 1;
+ }
+
+ if (!found_return)
+ return 0;
+
+ /* Scan backwards. This is just a heuristic, so do not worry about
+ false positives from mode changes. */
+
+ if (pc < func_start + 4)
+ return 0;
+
+ insn = read_memory_unsigned_integer (pc - 4, 4, byte_order_for_code);
+ if (bits (insn, 28, 31) != INST_NV)
+ {
+ if ((insn & 0x0df0f000) == 0x0080d000)
+ /* ADD SP (register or immediate). */
+ found_stack_adjust = 1;
+ else if ((insn & 0x0df0f000) == 0x0040d000)
+ /* SUB SP (register or immediate). */
+ found_stack_adjust = 1;
+ else if ((insn & 0x0ffffff0) == 0x01a0d000)
+ /* MOV SP. */
+ found_return = 1;
+ else if ((insn & 0x0fff0000) == 0x08bd0000)
+ /* POP (LDMIA). */
+ found_stack_adjust = 1;
+ }
+
+ if (found_stack_adjust)
+ return 1;
+
+ return 0;
+}
+
+
+/* When arguments must be pushed onto the stack, they go on in reverse
+ order. The code below implements a FILO (stack) to do this. */
+
+struct stack_item
+{
+ int len;
+ struct stack_item *prev;
+ void *data;
+};
+
+static struct stack_item *
+push_stack_item (struct stack_item *prev, const 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;
+}
+
+
+/* Return the alignment (in bytes) of the given type. */
+
+static int
+arm_type_align (struct type *t)
+{
+ int n;
+ int align;
+ int falign;
+
+ t = check_typedef (t);
+ switch (TYPE_CODE (t))
+ {
+ default:
+ /* Should never happen. */
+ internal_error (__FILE__, __LINE__, _("unknown type alignment"));
+ return 4;
+
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_INT:
+ case TYPE_CODE_FLT:
+ case TYPE_CODE_SET:
case TYPE_CODE_RANGE:
case TYPE_CODE_BITSTRING:
case TYPE_CODE_REF:
/* Set the return address. For the ARM, the return breakpoint is
always at BP_ADDR. */
- if (arm_pc_is_thumb (bp_addr))
+ if (arm_pc_is_thumb (gdbarch, bp_addr))
bp_addr |= 1;
regcache_cooked_write_unsigned (regcache, ARM_LR_REGNUM, bp_addr);
the THUMB bit in it. */
if (TYPE_CODE_PTR == typecode
&& target_type != NULL
- && TYPE_CODE_FUNC == TYPE_CODE (target_type))
+ && TYPE_CODE_FUNC == TYPE_CODE (check_typedef (target_type)))
{
CORE_ADDR regval = extract_unsigned_integer (val, len, byte_order);
- if (arm_pc_is_thumb (regval))
+ if (arm_pc_is_thumb (gdbarch, regval))
{
bfd_byte *copy = alloca (len);
store_unsigned_integer (copy, len, byte_order,
else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */
{
unsigned long cond = bits (inst1, 8, 11);
- if (cond != 0x0f && condition_true (cond, status)) /* 0x0f = SWI */
+ if (cond == 0x0f) /* 0x0f = SWI */
+ {
+ struct gdbarch_tdep *tdep;
+ tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->syscall_next_pc != NULL)
+ nextpc = tdep->syscall_next_pc (frame);
+
+ }
+ else if (cond != 0x0f && condition_true (cond, status))
nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
}
else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */
else
nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
}
+ else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */
+ {
+ if (bits (inst1, 3, 6) == 0x0f)
+ nextpc = pc_val;
+ else
+ nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
+
+ nextpc = MAKE_THUMB_ADDR (nextpc);
+ }
else if ((inst1 & 0xf500) == 0xb100)
{
/* CBNZ or CBZ. */
case 0xc:
case 0xd:
case 0xe: /* coproc ops */
+ break;
case 0xf: /* SWI */
+ {
+ struct gdbarch_tdep *tdep;
+ tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->syscall_next_pc != NULL)
+ nextpc = tdep->syscall_next_pc (frame);
+
+ }
break;
default:
return bpaddr;
/* ARM mode does not have this problem. */
- if (!arm_pc_is_thumb (bpaddr))
+ if (!arm_pc_is_thumb (gdbarch, bpaddr))
return bpaddr;
/* We are setting a breakpoint in Thumb code that could potentially
displaced_in_arm_mode (struct regcache *regs)
{
ULONGEST ps;
+ ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regs));
regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
- return (ps & CPSR_T) == 0;
+ return (ps & t_bit) == 0;
}
/* Write to the PC as from a branch instruction. */
bx_write_pc (struct regcache *regs, ULONGEST val)
{
ULONGEST ps;
+ ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regs));
regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
if ((val & 1) == 1)
{
- regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps | CPSR_T);
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps | t_bit);
regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffe);
}
else if ((val & 2) == 0)
{
- regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM,
- ps & ~(ULONGEST) CPSR_T);
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps & ~t_bit);
regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val);
}
else
/* Unpredictable behaviour. Try to do something sensible (switch to ARM
mode, align dest to 4 bytes). */
warning (_("Single-stepping BX to non-word-aligned ARM instruction."));
- regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM,
- ps & ~(ULONGEST) CPSR_T);
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps & ~t_bit);
regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffc);
}
}
static int
gdb_print_insn_arm (bfd_vma memaddr, disassemble_info *info)
{
- if (arm_pc_is_thumb (memaddr))
+ struct gdbarch *gdbarch = info->application_data;
+
+ if (arm_pc_is_thumb (gdbarch, memaddr))
{
static asymbol *asym;
static combined_entry_type ce;
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- if (arm_pc_is_thumb (*pcptr))
+ if (arm_pc_is_thumb (gdbarch, *pcptr))
{
*pcptr = UNMAKE_THUMB_ADDR (*pcptr);
arm_breakpoint_from_pc (gdbarch, pcptr, kindptr);
- if (arm_pc_is_thumb (*pcptr) && *kindptr == 4)
+ if (arm_pc_is_thumb (gdbarch, *pcptr) && *kindptr == 4)
/* The documented magic value for a 32-bit Thumb-2 breakpoint, so
that this is not confused with a 32-bit ARM breakpoint. */
*kindptr = 3;
static void
arm_write_pc (struct regcache *regcache, CORE_ADDR pc)
{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
regcache_cooked_write_unsigned (regcache, ARM_PC_REGNUM, pc);
/* If necessary, set the T bit. */
if (arm_apcs_32)
{
- ULONGEST val;
+ ULONGEST val, t_bit;
regcache_cooked_read_unsigned (regcache, ARM_PS_REGNUM, &val);
- if (arm_pc_is_thumb (pc))
- regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM, val | CPSR_T);
+ t_bit = arm_psr_thumb_bit (gdbarch);
+ if (arm_pc_is_thumb (gdbarch, pc))
+ regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
+ val | t_bit);
else
regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
- val & ~(ULONGEST) CPSR_T);
+ val & ~t_bit);
}
}
enum arm_abi_kind arm_abi = arm_abi_global;
enum arm_float_model fp_model = arm_fp_model;
struct tdesc_arch_data *tdesc_data = NULL;
- int i;
+ int i, is_m = 0;
int have_vfp_registers = 0, have_vfp_pseudos = 0, have_neon_pseudos = 0;
int have_neon = 0;
int have_fpa_registers = 1;
+ const struct target_desc *tdesc = info.target_desc;
+
+ /* If we have an object to base this architecture on, try to determine
+ its ABI. */
+
+ if (arm_abi == ARM_ABI_AUTO && info.abfd != NULL)
+ {
+ int ei_osabi, e_flags;
+
+ switch (bfd_get_flavour (info.abfd))
+ {
+ case bfd_target_aout_flavour:
+ /* Assume it's an old APCS-style ABI. */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ case bfd_target_coff_flavour:
+ /* Assume it's an old APCS-style ABI. */
+ /* XXX WinCE? */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ 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. */
+ arm_abi = ARM_ABI_APCS;
+ }
+ else if (ei_osabi == ELFOSABI_NONE)
+ {
+ int eabi_ver = EF_ARM_EABI_VERSION (e_flags);
+ int attr_arch, attr_profile;
+
+ switch (eabi_ver)
+ {
+ case EF_ARM_EABI_UNKNOWN:
+ /* Assume GNU tools. */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ case EF_ARM_EABI_VER4:
+ case EF_ARM_EABI_VER5:
+ arm_abi = ARM_ABI_AAPCS;
+ /* EABI binaries default to VFP float ordering.
+ They may also contain build attributes that can
+ be used to identify if the VFP argument-passing
+ ABI is in use. */
+ if (fp_model == ARM_FLOAT_AUTO)
+ {
+#ifdef HAVE_ELF
+ switch (bfd_elf_get_obj_attr_int (info.abfd,
+ OBJ_ATTR_PROC,
+ Tag_ABI_VFP_args))
+ {
+ case 0:
+ /* "The user intended FP parameter/result
+ passing to conform to AAPCS, base
+ variant". */
+ fp_model = ARM_FLOAT_SOFT_VFP;
+ break;
+ case 1:
+ /* "The user intended FP parameter/result
+ passing to conform to AAPCS, VFP
+ variant". */
+ fp_model = ARM_FLOAT_VFP;
+ break;
+ case 2:
+ /* "The user intended FP parameter/result
+ passing to conform to tool chain-specific
+ conventions" - we don't know any such
+ conventions, so leave it as "auto". */
+ break;
+ default:
+ /* Attribute value not mentioned in the
+ October 2008 ABI, so leave it as
+ "auto". */
+ break;
+ }
+#else
+ fp_model = ARM_FLOAT_SOFT_VFP;
+#endif
+ }
+ break;
+
+ default:
+ /* Leave it as "auto". */
+ warning (_("unknown ARM EABI version 0x%x"), eabi_ver);
+ break;
+ }
+
+#ifdef HAVE_ELF
+ /* Detect M-profile programs. This only works if the
+ executable file includes build attributes; GCC does
+ copy them to the executable, but e.g. RealView does
+ not. */
+ attr_arch = bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch);
+ attr_profile = bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch_profile);
+ /* GCC specifies the profile for v6-M; RealView only
+ specifies the profile for architectures starting with
+ V7 (as opposed to architectures with a tag
+ numerically greater than TAG_CPU_ARCH_V7). */
+ if (!tdesc_has_registers (tdesc)
+ && (attr_arch == TAG_CPU_ARCH_V6_M
+ || attr_arch == TAG_CPU_ARCH_V6S_M
+ || attr_profile == 'M'))
+ tdesc = tdesc_arm_with_m;
+#endif
+ }
+
+ 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;
+ }
+ }
+
+ if (e_flags & EF_ARM_BE8)
+ info.byte_order_for_code = BFD_ENDIAN_LITTLE;
+
+ break;
+
+ default:
+ /* Leave it as "auto". */
+ break;
+ }
+ }
/* Check any target description for validity. */
- if (tdesc_has_registers (info.target_desc))
+ if (tdesc_has_registers (tdesc))
{
/* For most registers we require GDB's default names; but also allow
the numeric names for sp / lr / pc, as a convenience. */
const struct tdesc_feature *feature;
int valid_p;
- feature = tdesc_find_feature (info.target_desc,
+ feature = tdesc_find_feature (tdesc,
"org.gnu.gdb.arm.core");
if (feature == NULL)
- return NULL;
+ {
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.arm.m-profile");
+ if (feature == NULL)
+ return NULL;
+ else
+ is_m = 1;
+ }
tdesc_data = tdesc_data_alloc ();
valid_p &= tdesc_numbered_register_choices (feature, tdesc_data,
ARM_PC_REGNUM,
arm_pc_names);
- valid_p &= tdesc_numbered_register (feature, tdesc_data,
- ARM_PS_REGNUM, "cpsr");
+ if (is_m)
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ ARM_PS_REGNUM, "xpsr");
+ else
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ ARM_PS_REGNUM, "cpsr");
if (!valid_p)
{
return NULL;
}
- feature = tdesc_find_feature (info.target_desc,
+ feature = tdesc_find_feature (tdesc,
"org.gnu.gdb.arm.fpa");
if (feature != NULL)
{
else
have_fpa_registers = 0;
- feature = tdesc_find_feature (info.target_desc,
+ feature = tdesc_find_feature (tdesc,
"org.gnu.gdb.xscale.iwmmxt");
if (feature != NULL)
{
/* If we have a VFP unit, check whether the single precision registers
are present. If not, then we will synthesize them as pseudo
registers. */
- feature = tdesc_find_feature (info.target_desc,
+ feature = tdesc_find_feature (tdesc,
"org.gnu.gdb.arm.vfp");
if (feature != NULL)
{
/* If we have VFP, also check for NEON. The architecture allows
NEON without VFP (integer vector operations only), but GDB
does not support that. */
- feature = tdesc_find_feature (info.target_desc,
+ feature = tdesc_find_feature (tdesc,
"org.gnu.gdb.arm.neon");
if (feature != NULL)
{
}
}
- /* If we have an object to base this architecture on, try to determine
- its ABI. */
-
- if (arm_abi == ARM_ABI_AUTO && info.abfd != NULL)
- {
- int ei_osabi, e_flags;
-
- switch (bfd_get_flavour (info.abfd))
- {
- case bfd_target_aout_flavour:
- /* Assume it's an old APCS-style ABI. */
- arm_abi = ARM_ABI_APCS;
- break;
-
- case bfd_target_coff_flavour:
- /* Assume it's an old APCS-style ABI. */
- /* XXX WinCE? */
- arm_abi = ARM_ABI_APCS;
- break;
-
- 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. */
- arm_abi = ARM_ABI_APCS;
- }
- else if (ei_osabi == ELFOSABI_NONE)
- {
- int eabi_ver = EF_ARM_EABI_VERSION (e_flags);
-
- switch (eabi_ver)
- {
- case EF_ARM_EABI_UNKNOWN:
- /* Assume GNU tools. */
- arm_abi = ARM_ABI_APCS;
- break;
-
- case EF_ARM_EABI_VER4:
- case EF_ARM_EABI_VER5:
- arm_abi = ARM_ABI_AAPCS;
- /* EABI binaries default to VFP float ordering.
- They may also contain build attributes that can
- be used to identify if the VFP argument-passing
- ABI is in use. */
- if (fp_model == ARM_FLOAT_AUTO)
- {
-#ifdef HAVE_ELF
- switch (bfd_elf_get_obj_attr_int (info.abfd,
- OBJ_ATTR_PROC,
- Tag_ABI_VFP_args))
- {
- case 0:
- /* "The user intended FP parameter/result
- passing to conform to AAPCS, base
- variant". */
- fp_model = ARM_FLOAT_SOFT_VFP;
- break;
- case 1:
- /* "The user intended FP parameter/result
- passing to conform to AAPCS, VFP
- variant". */
- fp_model = ARM_FLOAT_VFP;
- break;
- case 2:
- /* "The user intended FP parameter/result
- passing to conform to tool chain-specific
- conventions" - we don't know any such
- conventions, so leave it as "auto". */
- break;
- default:
- /* Attribute value not mentioned in the
- October 2008 ABI, so leave it as
- "auto". */
- break;
- }
-#else
- fp_model = ARM_FLOAT_SOFT_VFP;
-#endif
- }
- break;
-
- default:
- /* Leave it as "auto". */
- warning (_("unknown ARM EABI version 0x%x"), eabi_ver);
- 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;
- }
- }
-
- if (e_flags & EF_ARM_BE8)
- info.byte_order_for_code = BFD_ENDIAN_LITTLE;
-
- break;
-
- default:
- /* Leave it as "auto". */
- break;
- }
- }
-
/* If there is already a candidate, use it. */
for (best_arch = gdbarch_list_lookup_by_info (arches, &info);
best_arch != NULL;
since gdbarches with a different target description are
automatically disqualified. */
+ /* Do check is_m, though, since it might come from the binary. */
+ if (is_m != gdbarch_tdep (best_arch->gdbarch)->is_m)
+ continue;
+
/* Found a match. */
break;
}
These are gdbarch discriminators, like the OSABI. */
tdep->arm_abi = arm_abi;
tdep->fp_model = fp_model;
+ tdep->is_m = is_m;
tdep->have_fpa_registers = have_fpa_registers;
tdep->have_vfp_registers = have_vfp_registers;
tdep->have_vfp_pseudos = have_vfp_pseudos;
/* Advance PC across function entry code. */
set_gdbarch_skip_prologue (gdbarch, arm_skip_prologue);
+ /* Detect whether PC is in function epilogue. */
+ set_gdbarch_in_function_epilogue_p (gdbarch, arm_in_function_epilogue_p);
+
/* Skip trampolines. */
set_gdbarch_skip_trampoline_code (gdbarch, arm_skip_stub);
{
set_tdesc_pseudo_register_name (gdbarch, arm_register_name);
- tdesc_use_registers (gdbarch, info.target_desc, tdesc_data);
+ tdesc_use_registers (gdbarch, tdesc, tdesc_data);
/* Override tdesc_register_type to adjust the types of VFP
registers for NEON. */
bfd_target_elf_flavour,
arm_elf_osabi_sniffer);
+ /* Initialize the standard target descriptions. */
+ initialize_tdesc_arm_with_m ();
+
/* Get the number of possible sets of register names defined in opcodes. */
num_disassembly_options = get_arm_regname_num_options ();