2008-08-09 Xuepeng Guo <xuepeng.guo@intel.com>

H.J. Lu  <hongjiu.lu@intel.com>
	    Mark Kettenis <kettenis@gnu.org>

	* amd64-tdep.c (amd64_frame_cache): Add saved_sp_reg.
	(amd64_init_frame_cache): Initialize saved_sp_reg.
	(amd64_analyze_stack_align): New.
	(amd64_analyze_prologue): Call it.
	(amd64_frame_cache): Use saved_sp_reg if it is invalid.  Don't set
	%rip to 8 when halfway aligning the stack.

	* amd64-tdep.h (amd64_regnum): Add AMD64_R9_REGNUM to
	AMD64_R14_REGNUM.

	* i386-tdep.c (i386_frame_cache): Remove stack_align.  Add
	saved_sp_reg.
	(i386_alloc_frame_cache): Remove stack_align.  Initialize
	saved_sp_reg to -1.
	(i386_analyze_stack_align): Rewrite.
	(i386_frame_cache): Use saved_sp_reg if it is valid.
This commit is contained in:
H.J. Lu 2008-08-09 16:27:39 +00:00
parent 216ff8b48e
commit e0c6219855
4 changed files with 338 additions and 39 deletions

View File

@ -1,3 +1,24 @@
2008-08-09 Xuepeng Guo <xuepeng.guo@intel.com>
H.J. Lu <hongjiu.lu@intel.com>
Mark Kettenis <kettenis@gnu.org>
* amd64-tdep.c (amd64_frame_cache): Add saved_sp_reg.
(amd64_init_frame_cache): Initialize saved_sp_reg.
(amd64_analyze_stack_align): New.
(amd64_analyze_prologue): Call it.
(amd64_frame_cache): Use saved_sp_reg if it is invalid. Don't set
%rip to 8 when halfway aligning the stack.
* amd64-tdep.h (amd64_regnum): Add AMD64_R9_REGNUM to
AMD64_R14_REGNUM.
* i386-tdep.c (i386_frame_cache): Remove stack_align. Add
saved_sp_reg.
(i386_alloc_frame_cache): Remove stack_align. Initialize
saved_sp_reg to -1.
(i386_analyze_stack_align): Rewrite.
(i386_frame_cache): Use saved_sp_reg if it is valid.
2008-08-09 Ulrich Weigand <uweigand@de.ibm.com>
* target.c: Include "solib.h".

View File

@ -680,6 +680,7 @@ struct amd64_frame_cache
/* Saved registers. */
CORE_ADDR saved_regs[AMD64_NUM_SAVED_REGS];
CORE_ADDR saved_sp;
int saved_sp_reg;
/* Do we have a frame? */
int frameless_p;
@ -702,6 +703,7 @@ amd64_init_frame_cache (struct amd64_frame_cache *cache)
for (i = 0; i < AMD64_NUM_SAVED_REGS; i++)
cache->saved_regs[i] = -1;
cache->saved_sp = 0;
cache->saved_sp_reg = -1;
/* Frameless until proven otherwise. */
cache->frameless_p = 1;
@ -719,6 +721,179 @@ amd64_alloc_frame_cache (void)
return cache;
}
/* GCC 4.4 and later, can put code in the prologue to realign the
stack pointer. Check whether PC points to such code, and update
CACHE accordingly. Return the first instruction after the code
sequence or CURRENT_PC, whichever is smaller. If we don't
recognize the code, return PC. */
static CORE_ADDR
amd64_analyze_stack_align (CORE_ADDR pc, CORE_ADDR current_pc,
struct amd64_frame_cache *cache)
{
/* There are 2 code sequences to re-align stack before the frame
gets set up:
1. Use a caller-saved saved register:
leaq 8(%rsp), %reg
andq $-XXX, %rsp
pushq -8(%reg)
2. Use a callee-saved saved register:
pushq %reg
leaq 16(%rsp), %reg
andq $-XXX, %rsp
pushq -8(%reg)
"andq $-XXX, %rsp" can be either 4 bytes or 7 bytes:
0x48 0x83 0xe4 0xf0 andq $-16, %rsp
0x48 0x81 0xe4 0x00 0xff 0xff 0xff andq $-256, %rsp
*/
gdb_byte buf[18];
int reg, r;
int offset, offset_and;
static int regnums[16] = {
AMD64_RAX_REGNUM, /* %rax */
AMD64_RCX_REGNUM, /* %rcx */
AMD64_RDX_REGNUM, /* %rdx */
AMD64_RBX_REGNUM, /* %rbx */
AMD64_RSP_REGNUM, /* %rsp */
AMD64_RBP_REGNUM, /* %rbp */
AMD64_RSI_REGNUM, /* %rsi */
AMD64_RDI_REGNUM, /* %rdi */
AMD64_R8_REGNUM, /* %r8 */
AMD64_R9_REGNUM, /* %r9 */
AMD64_R10_REGNUM, /* %r10 */
AMD64_R11_REGNUM, /* %r11 */
AMD64_R12_REGNUM, /* %r12 */
AMD64_R13_REGNUM, /* %r13 */
AMD64_R14_REGNUM, /* %r14 */
AMD64_R15_REGNUM, /* %r15 */
};
if (target_read_memory (pc, buf, sizeof buf))
return pc;
/* Check caller-saved saved register. The first instruction has
to be "leaq 8(%rsp), %reg". */
if ((buf[0] & 0xfb) == 0x48
&& buf[1] == 0x8d
&& buf[3] == 0x24
&& buf[4] == 0x8)
{
/* MOD must be binary 10 and R/M must be binary 100. */
if ((buf[2] & 0xc7) != 0x44)
return pc;
/* REG has register number. */
reg = (buf[2] >> 3) & 7;
/* Check the REX.R bit. */
if (buf[0] == 0x4c)
reg += 8;
offset = 5;
}
else
{
/* Check callee-saved saved register. The first instruction
has to be "pushq %reg". */
reg = 0;
if ((buf[0] & 0xf8) == 0x50)
offset = 0;
else if ((buf[0] & 0xf6) == 0x40
&& (buf[1] & 0xf8) == 0x50)
{
/* Check the REX.B bit. */
if ((buf[0] & 1) != 0)
reg = 8;
offset = 1;
}
else
return pc;
/* Get register. */
reg += buf[offset] & 0x7;
offset++;
/* The next instruction has to be "leaq 16(%rsp), %reg". */
if ((buf[offset] & 0xfb) != 0x48
|| buf[offset + 1] != 0x8d
|| buf[offset + 3] != 0x24
|| buf[offset + 4] != 0x10)
return pc;
/* MOD must be binary 10 and R/M must be binary 100. */
if ((buf[offset + 2] & 0xc7) != 0x44)
return pc;
/* REG has register number. */
r = (buf[offset + 2] >> 3) & 7;
/* Check the REX.R bit. */
if (buf[offset] == 0x4c)
r += 8;
/* Registers in pushq and leaq have to be the same. */
if (reg != r)
return pc;
offset += 5;
}
/* Rigister can't be %rsp nor %rbp. */
if (reg == 4 || reg == 5)
return pc;
/* The next instruction has to be "andq $-XXX, %rsp". */
if (buf[offset] != 0x48
|| buf[offset + 2] != 0xe4
|| (buf[offset + 1] != 0x81 && buf[offset + 1] != 0x83))
return pc;
offset_and = offset;
offset += buf[offset + 1] == 0x81 ? 7 : 4;
/* The next instruction has to be "pushq -8(%reg)". */
r = 0;
if (buf[offset] == 0xff)
offset++;
else if ((buf[offset] & 0xf6) == 0x40
&& buf[offset + 1] == 0xff)
{
/* Check the REX.B bit. */
if ((buf[offset] & 0x1) != 0)
r = 8;
offset += 2;
}
else
return pc;
/* 8bit -8 is 0xf8. REG must be binary 110 and MOD must be binary
01. */
if (buf[offset + 1] != 0xf8
|| (buf[offset] & 0xf8) != 0x70)
return pc;
/* R/M has register. */
r += buf[offset] & 7;
/* Registers in leaq and pushq have to be the same. */
if (reg != r)
return pc;
if (current_pc > pc + offset_and)
cache->saved_sp_reg = regnums[reg];
return min (pc + offset + 2, current_pc);
}
/* Do a limited analysis of the prologue at PC and update CACHE
accordingly. Bail out early if CURRENT_PC is reached. Return the
address where the analysis stopped.
@ -742,6 +917,8 @@ amd64_analyze_prologue (CORE_ADDR pc, CORE_ADDR current_pc,
if (current_pc <= pc)
return current_pc;
pc = amd64_analyze_stack_align (pc, current_pc, cache);
op = read_memory_unsigned_integer (pc, 1);
if (op == 0x55) /* pushq %rbp */
@ -804,6 +981,13 @@ amd64_frame_cache (struct frame_info *this_frame, void **this_cache)
if (cache->pc != 0)
amd64_analyze_prologue (cache->pc, get_frame_pc (this_frame), cache);
if (cache->saved_sp_reg != -1)
{
/* Stack pointer has been saved. */
get_frame_register (this_frame, cache->saved_sp_reg, buf);
cache->saved_sp = extract_unsigned_integer(buf, 8);
}
if (cache->frameless_p)
{
/* We didn't find a valid frame. If we're at the start of a
@ -813,8 +997,20 @@ amd64_frame_cache (struct frame_info *this_frame, void **this_cache)
at the stack pointer. For truly "frameless" functions this
might work too. */
get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
cache->base = extract_unsigned_integer (buf, 8) + cache->sp_offset;
if (cache->saved_sp_reg != -1)
{
/* We're halfway aligning the stack. */
cache->base = ((cache->saved_sp - 8) & 0xfffffffffffffff0LL) - 8;
cache->saved_regs[AMD64_RIP_REGNUM] = cache->saved_sp - 8;
/* This will be added back below. */
cache->saved_regs[AMD64_RIP_REGNUM] -= cache->base;
}
else
{
get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
cache->base = extract_unsigned_integer (buf, 8) + cache->sp_offset;
}
}
else
{
@ -828,8 +1024,10 @@ amd64_frame_cache (struct frame_info *this_frame, void **this_cache)
/* For normal frames, %rip is stored at 8(%rbp). If we don't have a
frame we find it at the same offset from the reconstructed base
address. */
cache->saved_regs[AMD64_RIP_REGNUM] = 8;
address. If we're halfway aligning the stack, %rip is handled
differently (see above). */
if (!cache->frameless_p || cache->saved_sp_reg == -1)
cache->saved_regs[AMD64_RIP_REGNUM] = 8;
/* Adjust all the saved registers such that they contain addresses
instead of offsets. */

View File

@ -39,8 +39,14 @@ enum amd64_regnum
AMD64_RDI_REGNUM, /* %rdi */
AMD64_RBP_REGNUM, /* %rbp */
AMD64_RSP_REGNUM, /* %rsp */
AMD64_R8_REGNUM = 8, /* %r8 */
AMD64_R15_REGNUM = 15, /* %r15 */
AMD64_R8_REGNUM, /* %r8 */
AMD64_R9_REGNUM, /* %r9 */
AMD64_R10_REGNUM, /* %r10 */
AMD64_R11_REGNUM, /* %r11 */
AMD64_R12_REGNUM, /* %r12 */
AMD64_R13_REGNUM, /* %r13 */
AMD64_R14_REGNUM, /* %r14 */
AMD64_R15_REGNUM, /* %r15 */
AMD64_RIP_REGNUM, /* %rip */
AMD64_EFLAGS_REGNUM, /* %eflags */
AMD64_CS_REGNUM, /* %cs */

View File

@ -518,7 +518,7 @@ struct i386_frame_cache
/* Saved registers. */
CORE_ADDR saved_regs[I386_NUM_SAVED_REGS];
CORE_ADDR saved_sp;
int stack_align;
int saved_sp_reg;
int pc_in_eax;
/* Stack space reserved for local variables. */
@ -545,7 +545,7 @@ i386_alloc_frame_cache (void)
for (i = 0; i < I386_NUM_SAVED_REGS; i++)
cache->saved_regs[i] = -1;
cache->saved_sp = 0;
cache->stack_align = 0;
cache->saved_sp_reg = -1;
cache->pc_in_eax = 0;
/* Frameless until proven otherwise. */
@ -707,37 +707,111 @@ static CORE_ADDR
i386_analyze_stack_align (CORE_ADDR pc, CORE_ADDR current_pc,
struct i386_frame_cache *cache)
{
/* The register used by the compiler to perform the stack re-alignment
is, in order of preference, either %ecx, %edx, or %eax. GCC should
never use %ebx as it always treats it as callee-saved, whereas
the compiler can only use caller-saved registers. */
static const gdb_byte insns_ecx[10] = {
0x8d, 0x4c, 0x24, 0x04, /* leal 4(%esp), %ecx */
0x83, 0xe4, 0xf0, /* andl $-16, %esp */
0xff, 0x71, 0xfc /* pushl -4(%ecx) */
};
static const gdb_byte insns_edx[10] = {
0x8d, 0x54, 0x24, 0x04, /* leal 4(%esp), %edx */
0x83, 0xe4, 0xf0, /* andl $-16, %esp */
0xff, 0x72, 0xfc /* pushl -4(%edx) */
};
static const gdb_byte insns_eax[10] = {
0x8d, 0x44, 0x24, 0x04, /* leal 4(%esp), %eax */
0x83, 0xe4, 0xf0, /* andl $-16, %esp */
0xff, 0x70, 0xfc /* pushl -4(%eax) */
};
gdb_byte buf[10];
/* There are 2 code sequences to re-align stack before the frame
gets set up:
if (target_read_memory (pc, buf, sizeof buf)
|| (memcmp (buf, insns_ecx, sizeof buf) != 0
&& memcmp (buf, insns_edx, sizeof buf) != 0
&& memcmp (buf, insns_eax, sizeof buf) != 0))
1. Use a caller-saved saved register:
leal 4(%esp), %reg
andl $-XXX, %esp
pushl -4(%reg)
2. Use a callee-saved saved register:
pushl %reg
leal 8(%esp), %reg
andl $-XXX, %esp
pushl -4(%reg)
"andl $-XXX, %esp" can be either 3 bytes or 6 bytes:
0x83 0xe4 0xf0 andl $-16, %esp
0x81 0xe4 0x00 0xff 0xff 0xff andl $-256, %esp
*/
gdb_byte buf[14];
int reg;
int offset, offset_and;
static int regnums[8] = {
I386_EAX_REGNUM, /* %eax */
I386_ECX_REGNUM, /* %ecx */
I386_EDX_REGNUM, /* %edx */
I386_EBX_REGNUM, /* %ebx */
I386_ESP_REGNUM, /* %esp */
I386_EBP_REGNUM, /* %ebp */
I386_ESI_REGNUM, /* %esi */
I386_EDI_REGNUM /* %edi */
};
if (target_read_memory (pc, buf, sizeof buf))
return pc;
if (current_pc > pc + 4)
cache->stack_align = 1;
/* Check caller-saved saved register. The first instruction has
to be "leal 4(%esp), %reg". */
if (buf[0] == 0x8d && buf[2] == 0x24 && buf[3] == 0x4)
{
/* MOD must be binary 10 and R/M must be binary 100. */
if ((buf[1] & 0xc7) != 0x44)
return pc;
return min (pc + 10, current_pc);
/* REG has register number. */
reg = (buf[1] >> 3) & 7;
offset = 4;
}
else
{
/* Check callee-saved saved register. The first instruction
has to be "pushl %reg". */
if ((buf[0] & 0xf8) != 0x50)
return pc;
/* Get register. */
reg = buf[0] & 0x7;
/* The next instruction has to be "leal 8(%esp), %reg". */
if (buf[1] != 0x8d || buf[3] != 0x24 || buf[4] != 0x8)
return pc;
/* MOD must be binary 10 and R/M must be binary 100. */
if ((buf[2] & 0xc7) != 0x44)
return pc;
/* REG has register number. Registers in pushl and leal have to
be the same. */
if (reg != ((buf[2] >> 3) & 7))
return pc;
offset = 5;
}
/* Rigister can't be %esp nor %ebp. */
if (reg == 4 || reg == 5)
return pc;
/* The next instruction has to be "andl $-XXX, %esp". */
if (buf[offset + 1] != 0xe4
|| (buf[offset] != 0x81 && buf[offset] != 0x83))
return pc;
offset_and = offset;
offset += buf[offset] == 0x81 ? 6 : 3;
/* The next instruction has to be "pushl -4(%reg)". 8bit -4 is
0xfc. REG must be binary 110 and MOD must be binary 01. */
if (buf[offset] != 0xff
|| buf[offset + 2] != 0xfc
|| (buf[offset + 1] & 0xf8) != 0x70)
return pc;
/* R/M has register. Registers in leal and pushl have to be the
same. */
if (reg != (buf[offset + 1] & 7))
return pc;
if (current_pc > pc + offset_and)
cache->saved_sp_reg = regnums[reg];
return min (pc + offset + 3, current_pc);
}
/* Maximum instruction length we need to handle. */
@ -1241,10 +1315,10 @@ i386_frame_cache (struct frame_info *this_frame, void **this_cache)
if (cache->pc != 0)
i386_analyze_prologue (cache->pc, get_frame_pc (this_frame), cache);
if (cache->stack_align)
if (cache->saved_sp_reg != -1)
{
/* Saved stack pointer has been saved in %ecx. */
get_frame_register (this_frame, I386_ECX_REGNUM, buf);
/* Saved stack pointer has been saved. */
get_frame_register (this_frame, cache->saved_sp_reg, buf);
cache->saved_sp = extract_unsigned_integer(buf, 4);
}
@ -1258,7 +1332,7 @@ i386_frame_cache (struct frame_info *this_frame, void **this_cache)
frame by looking at the stack pointer. For truly "frameless"
functions this might work too. */
if (cache->stack_align)
if (cache->saved_sp_reg != -1)
{
/* We're halfway aligning the stack. */
cache->base = ((cache->saved_sp - 4) & 0xfffffff0) - 4;