* Makefile.in (arm-tdep.o): Update dependencies.
	* arm-tdep.c (thumb_skip_prologue): Remove.
	(thumb_analyze_prologue): New function.
	(arm_skip_prologue): Use thumb_analyze_prologue.
	(thumb_scan_prologue): Ditto.
gdb/testsuite/
	* gdb.arch/thumb-prologue.c, gdb.arch/thumb-prologue.exp: New files.
This commit is contained in:
Daniel Jacobowitz 2006-11-22 18:51:58 +00:00
parent e821645dee
commit 29d73ae48c
6 changed files with 304 additions and 141 deletions

View File

@ -1,3 +1,11 @@
2006-11-22 Daniel Jacobowitz <dan@codesourcery.com>
* Makefile.in (arm-tdep.o): Update dependencies.
* arm-tdep.c (thumb_skip_prologue): Remove.
(thumb_analyze_prologue): New function.
(arm_skip_prologue): Use thumb_analyze_prologue.
(thumb_scan_prologue): Ditto.
2006-11-22 Ulrich Weigand <uweigand@de.ibm.com>
* configure.tgt: Enable gdbserver for SPU target.

View File

@ -1807,7 +1807,7 @@ arm-tdep.o: arm-tdep.c $(defs_h) $(frame_h) $(inferior_h) $(gdbcmd_h) \
$(frame_unwind_h) $(frame_base_h) $(trad_frame_h) $(arm_tdep_h) \
$(gdb_sim_arm_h) $(elf_bfd_h) $(coff_internal_h) $(elf_arm_h) \
$(gdb_assert_h) $(bfd_in2_h) $(libcoff_h) $(objfiles_h) \
$(dwarf2_frame_h) $(gdbtypes_h)
$(dwarf2_frame_h) $(gdbtypes_h) $(prologue_value_h)
auxv.o: auxv.c $(defs_h) $(target_h) $(gdbtypes_h) $(command_h) \
$(inferior_h) $(valprint_h) $(gdb_assert_h) $(auxv_h) \
$(elf_common_h)

View File

@ -41,6 +41,7 @@
#include "objfiles.h"
#include "dwarf2-frame.h"
#include "gdbtypes.h"
#include "prologue-value.h"
#include "arm-tdep.h"
#include "gdb/sim-arm.h"
@ -212,84 +213,156 @@ arm_smash_text_address (CORE_ADDR val)
return val & ~1;
}
/* 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.
*/
/* 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
thumb_skip_prologue (CORE_ADDR pc, CORE_ADDR func_end)
thumb_analyze_prologue (struct gdbarch *gdbarch,
CORE_ADDR start, CORE_ADDR limit,
struct arm_prologue_cache *cache)
{
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 */
}
else if (findmask == (4+2+1))
{
/* We have found one of each type of prologue instruction */
break;
/* 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
/* Something in the prolog that we don't care about or some
instruction from outside the prolog scheduled here for
optimization. */
continue;
{
/* 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;
}
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
@ -337,10 +410,6 @@ arm_skip_prologue (CORE_ADDR pc)
}
}
/* 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. */
@ -348,6 +417,10 @@ arm_skip_prologue (CORE_ADDR pc)
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);
@ -462,86 +535,8 @@ thumb_scan_prologue (CORE_ADDR prev_pc, struct arm_prologue_cache *cache)
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:

View File

@ -1,3 +1,7 @@
2006-11-22 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.arch/thumb-prologue.c, gdb.arch/thumb-prologue.exp: New files.
2006-11-22 Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
* gdb.asm/asm-source.exp: Add "spu*-*-*" target.

View File

@ -0,0 +1,96 @@
/* Unwinder test program.
Copyright 2006 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
void tpcs_frame (void);
int
main (void)
{
tpcs_frame ();
return 0;
}
/* Normally Thumb functions use r7 as the frame pointer. However,
with the GCC option -mtpcs-frame, they may use fp instead. */
asm(".text\n"
" .align 2\n"
" .thumb_func\n"
" .code 16\n"
"tpcs_frame_1:\n"
" sub sp, #16\n"
" push {r7}\n"
" add r7, sp, #20\n"
" str r7, [sp, #8]\n"
" mov r7, pc\n"
" str r7, [sp, #16]\n"
" mov r7, fp\n"
" str r7, [sp, #4]\n"
" mov r7, lr\n"
" str r7, [sp, #12]\n"
" add r7, sp, #16\n"
" mov fp, r7\n"
" mov r7, sl\n"
" push {r7}\n"
/* Trap. */
" .short 0xdffe\n"
" pop {r2}\n"
" mov sl, r2\n"
" pop {r7}\n"
" pop {r1, r2}\n"
" mov fp, r1\n"
" mov sp, r2\n"
" bx lr\n"
" .align 2\n"
" .thumb_func\n"
" .code 16\n"
"tpcs_frame:\n"
" sub sp, #16\n"
" push {r7}\n"
" add r7, sp, #20\n"
" str r7, [sp, #8]\n"
" mov r7, pc\n"
" str r7, [sp, #16]\n"
" mov r7, fp\n"
" str r7, [sp, #4]\n"
" mov r7, lr\n"
" str r7, [sp, #12]\n"
" add r7, sp, #16\n"
" mov fp, r7\n"
" mov r7, sl\n"
" push {r7}\n"
/* Clobber saved regs. */
" mov r7, #0\n"
" mov lr, r7\n"
" bl tpcs_frame_1\n"
" pop {r2}\n"
" mov sl, r2\n"
" pop {r7}\n"
" pop {r1, r2}\n"
" mov fp, r1\n"
" mov sp, r2\n"
" bx lr\n"
);

View File

@ -0,0 +1,60 @@
# Copyright 2006 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Test ARM/Thumb prologue analyzer.
if {![istarget arm*-*]} then {
verbose "Skipping ARM prologue tests."
return
}
set testfile "thumb-prologue"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
# Don't use "debug", so that we don't have line information for the assembly
# fragments.
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {"additional_flags=-mthumb"}] != "" } {
untested "ARM prologue tests"
return -1
}
gdb_exit
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
#
# Run to `main' where we begin our tests.
#
if ![runto_main] then {
untested "ARM prologue tests"
return -1
}
# Testcase for TPCS prologue.
gdb_test "continue" "Program received signal SIG.*" "continue to TPCS"
gdb_test "backtrace 10" \
"#0\[ \t\]*$hex in tpcs_frame_1 .*\r\n#1\[ \t\]*$hex in tpcs_frame .*\r\n#2\[ \t\]*$hex in main.*" \
"backtrace in TPCS"
gdb_test "info frame" \
".*Saved registers:.*r7 at.*r10 at.*r11 at.*lr at.*pc at .*" \
"saved registers in TPCS"