aboutsummaryrefslogtreecommitdiff
path: root/gdb/arm-tdep.c
diff options
context:
space:
mode:
authorUlrich Weigand <uweigand@de.ibm.com>2010-10-08 13:31:07 +0000
committerUlrich Weigand <uweigand@de.ibm.com>2010-10-08 13:31:07 +0000
commitec3d575a7a6516a1b3065312d228d706de6c49c7 (patch)
tree00b9543ffa615e77ae47937f7ed95420f33e5948 /gdb/arm-tdep.c
parent4024ca9964423384c2bfe695a778c106afd2e97e (diff)
downloadfsf-binutils-gdb-ec3d575a7a6516a1b3065312d228d706de6c49c7.zip
fsf-binutils-gdb-ec3d575a7a6516a1b3065312d228d706de6c49c7.tar.gz
fsf-binutils-gdb-ec3d575a7a6516a1b3065312d228d706de6c49c7.tar.bz2
* arm-tdep.c (thumb_expand_immediate): New function.
(thumb_instruction_changes_pc): Likewise. (thumb2_instruction_changes_pc): Likewise. (thumb_analyze_prologue): Handle 32-bit Thumb instructions during prologue parsing. Improved support for optimized code. (thumb_scan_prologue): Do not reply on line-number information, use same heuristics as arm_scan_prologue insead. (skip_prologue_function): Accept functions "__tls_get_addr" and "__aeabi_read_tp".
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r--gdb/arm-tdep.c434
1 files changed, 392 insertions, 42 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index e2d02b4..19b9c85 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -476,6 +476,12 @@ skip_prologue_function (CORE_ADDR pc)
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;
}
@@ -488,6 +494,149 @@ skip_prologue_function (CORE_ADDR pc)
#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 & 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.
@@ -506,6 +655,7 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
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);
@@ -643,9 +793,8 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
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,
@@ -675,59 +824,257 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
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)
{
- /* 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. */
+ 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> */
+ {
+ /* 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;
}
@@ -736,10 +1083,13 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
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))
@@ -772,7 +1122,7 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
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
@@ -956,12 +1306,12 @@ thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc,
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