2010-10-08 Ulrich Weigand <uweigand@de.ibm.com>
Daniel Jacobowitz <dan@codesourcery.com> * arm-tdep.c (thumb_in_function_epilogue_p) (arm_in_function_epilogue_p): New. (arm_gdbarch_init): Install arm_in_function_epilogue_p as gdbarch_in_function_epilogue_p callback.
This commit is contained in:
parent
682ffa1ca8
commit
4024ca9964
2 changed files with 208 additions and 0 deletions
|
@ -1,3 +1,11 @@
|
|||
2010-10-08 Ulrich Weigand <uweigand@de.ibm.com>
|
||||
Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* arm-tdep.c (thumb_in_function_epilogue_p)
|
||||
(arm_in_function_epilogue_p): New.
|
||||
(arm_gdbarch_init): Install arm_in_function_epilogue_p as
|
||||
gdbarch_in_function_epilogue_p callback.
|
||||
|
||||
2010-10-07 Doug Evans <dje@google.com>
|
||||
|
||||
* cc-with-index.sh (output_file): Default to a.out.
|
||||
|
|
200
gdb/arm-tdep.c
200
gdb/arm-tdep.c
|
@ -1706,6 +1706,203 @@ arm_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
|
|||
}
|
||||
}
|
||||
|
||||
/* Return true if we are in the function's epilogue, i.e. after the
|
||||
instruction that destroyed the function's stack frame. */
|
||||
|
||||
static int
|
||||
thumb_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, insn2;
|
||||
int found_return = 0, found_stack_adjust = 0;
|
||||
CORE_ADDR func_start, func_end;
|
||||
CORE_ADDR scan_pc;
|
||||
gdb_byte buf[4];
|
||||
|
||||
if (!find_pc_partial_function (pc, NULL, &func_start, &func_end))
|
||||
return 0;
|
||||
|
||||
/* 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]
|
||||
|
||||
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.
|
||||
|
||||
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. */
|
||||
|
||||
scan_pc = pc;
|
||||
while (scan_pc < func_end && !found_return)
|
||||
{
|
||||
if (target_read_memory (scan_pc, buf, 2))
|
||||
break;
|
||||
|
||||
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. */
|
||||
|
||||
|
@ -6881,6 +7078,9 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
|||
/* 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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue