Support software single step on ARM in GDBServer

This patch teaches GDBServer how to software single step on ARM
linux by sharing code with GDB.

The arm_get_next_pcs function in GDB is now shared with GDBServer.  So
that GDBServer can use the function to return the possible addresses of
the next PC.

A proper shared context was also needed so that we could share the code,
this context is described in the arm_get_next_pcs structure.

Testing :

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/ChangeLog:

	* Makefile.in (ALL_TARGET_OBS): Append arm-get-next-pcs.o,
	arm-linux.o.
	(ALLDEPFILES): Append arm-get-next-pcs.c, arm-linux.c
	(arm-linux.o): New rule.
	(arm-get-next-pcs.o): New rule.
	* arch/arm-get-next-pcs.c: New file.
	* arch/arm-get-next-pcs.h: New file.
	* arch/arm-linux.h: New file.
	* arch/arm-linux.c: New file.
	* arm.c: Include common-regcache.c.
	(thumb_advance_itstate): Moved from arm-tdep.c.
	(arm_instruction_changes_pc): Likewise.
	(thumb_instruction_changes_pc): Likewise.
	(thumb2_instruction_changes_pc): Likewise.
	(shifted_reg_val): Likewise.
	* arm.h (submask): Move macro from arm-tdep.h
	(bit): Likewise.
	(bits): Likewise.
	(sbits): Likewise.
	(BranchDest): Likewise.
	(thumb_advance_itstate): Moved declaration from arm-tdep.h
	(arm_instruction_changes_pc): Likewise.
	(thumb_instruction_changes_pc): Likewise.
	(thumb2_instruction_changes_pc): Likewise.
	(shifted_reg_val): Likewise.
	* arm-linux-tdep.c: Include arch/arm.h, arch/arm-get-next-pcs.h
	arch/arm-linux.h.
	(arm_linux_get_next_pcs_ops): New struct.
	(ARM_SIGCONTEXT_R0, ARM_UCONTEXT_SIGCONTEXT,
	ARM_OLD_RT_SIGFRAME_SIGINFO, ARM_OLD_RT_SIGFRAME_UCONTEXT,
	ARM_NEW_RT_SIGFRAME_UCONTEXT, ARM_NEW_SIGFRAME_MAGIC): Move stack
	layout defines to arch/arm-linux.h.
	(arm_linux_sigreturn_next_pc_offset): Move to arch/arm-linux.c.
	(arm_linux_software_single_step): Adjust for arm_get_next_pcs
	implementation.
	* arm-tdep.c: Include arch/arm-get-next-pcs.h.
	(arm_get_next_pcs_ops): New struct.
	(submask): Move macro to arm.h.
	(bit): Likewise.
	(bits): Likewise.
	(sbits): Likewise.
	(BranchDest): Likewise.
	(thumb_instruction_changes_pc): Move to arm.c
	(thumb2_instruction_changes_pc): Likewise.
	(arm_instruction_changes_pc): Likewise.
	(shifted_reg_val): Likewise.
	(thumb_advance_itstate): Likewise.
	(thumb_get_next_pc_raw): Move to arm-get-next-pcs.c.
	(arm_get_next_pc_raw): Likewise.
	(arm_get_next_pc): Likewise.
	(thumb_deal_with_atomic_sequence_raw): Likewise.
	(arm_deal_with_atomic_sequence_raw): Likewise.
	(arm_deal_with_atomic_sequence): Likewise.
	(arm_get_next_pcs_read_memory_unsigned_integer): New function.
	(arm_get_next_pcs_addr_bits_remove): Likewise.
	(arm_get_next_pcs_syscall_next_pc): Likewise.
	(arm_get_next_pcs_is_thumb): Likewise.
	(arm_software_single_step): Adjust for arm_get_next_pcs
	implementation.
	* arm-tdep.h: (arm_get_next_pc): Remove declaration.
	(arm_get_next_pcs_read_memory_unsigned_integer):
	New declaration.
	(arm_get_next_pcs_addr_bits_remove): Likewise.
	(arm_get_next_pcs_syscall_next_pc): Likewise.
	(arm_get_next_pcs_is_thumb): Likewise.
	(arm_deal_with_atomic_sequence: Remove declaration.
	* common/gdb_vecs.h: Add CORE_ADDR vector definition.
	* configure.tgt (aarch64*-*-linux): Add arm-get-next-pcs.o,
	arm-linux.o.
	(arm*-wince-pe): Add arm-get-next-pcs.o.
	(arm*-*-linux*): Add arm-get-next-pcs.o, arm-linux.o,
	arm-get-next-pcs.o
	(arm*-*-netbsd*,arm*-*-knetbsd*-gnu): Add arm-get-next-pcs.o.
	(arm*-*-openbsd*): Likewise.
	(arm*-*-symbianelf*): Likewise.
	(arm*-*-*): Likewise.
	* symtab.h: Move CORE_ADDR vector definition to gdb_vecs.h.

gdb/gdbserver/ChangeLog:

	* Makefile.in (SFILES): Append arch/arm-linux.c,
	arch/arm-get-next-pcs.c.
	(arm-linux.o): New rule.
	(arm-get-next-pcs.o): New rule.
	* configure.srv (arm*-*-linux*): Add arm-get-next-pcs.o,
	arm-linux.o.
	* linux-aarch32-low.c (arm_abi_breakpoint): Remove macro.  Moved
	to linux-aarch32-low.c.
	(arm_eabi_breakpoint, arm_breakpoint): Likewise.
	(arm_breakpoint_len, thumb_breakpoint): Likewise.
	(thumb_breakpoint_len, thumb2_breakpoint): Likewise.
	(thumb2_breakpoint_len): Likewise.
	(arm_is_thumb_mode): Make non-static.
	* linux-aarch32-low.h (arm_abi_breakpoint): New macro.  Moved
	from linux-aarch32-low.c.
	(arm_eabi_breakpoint, arm_breakpoint): Likewise.
	(arm_breakpoint_len, thumb_breakpoint): Likewise.
	(thumb_breakpoint_len, thumb2_breakpoint): Likewise.
	(thumb2_breakpoint_len): Likewise.
	(arm_is_thumb_mode): New declaration.
	* linux-arm-low.c: Include arch/arm-linux.h
	aarch/arm-get-next-pcs.h, sys/syscall.h.
	(get_next_pcs_ops): New struct.
	(get_next_pcs_addr_bits_remove): New function.
	(get_next_pcs_is_thumb): New function.
	(get_next_pcs_read_memory_unsigned_integer): Likewise.
	(arm_sigreturn_next_pc): Likewise.
	(get_next_pcs_syscall_next_pc): Likewise.
	(arm_gdbserver_get_next_pcs): Likewise.
	(struct linux_target_ops) <arm_gdbserver_get_next_pcs>:
	Initialize.
	* linux-low.h: Move CORE_ADDR vector definition to gdb_vecs.h.
	* server.h: Include gdb_vecs.h.
This commit is contained in:
Antoine Tremblay 2015-12-18 11:33:59 -05:00
parent 68ce205943
commit d9311bfaf5
22 changed files with 1924 additions and 1361 deletions

View File

@ -1,3 +1,83 @@
2015-12-18 Antoine Tremblay <antoine.tremblay@ericsson.com>
* Makefile.in (ALL_TARGET_OBS): Append arm-get-next-pcs.o,
arm-linux.o.
(ALLDEPFILES): Append arm-get-next-pcs.c, arm-linux.c
(arm-linux.o): New rule.
(arm-get-next-pcs.o): New rule.
* arch/arm-get-next-pcs.c: New file.
* arch/arm-get-next-pcs.h: New file.
* arch/arm-linux.h: New file.
* arch/arm-linux.c: New file.
* arm.c: Include common-regcache.c.
(thumb_advance_itstate): Moved from arm-tdep.c.
(arm_instruction_changes_pc): Likewise.
(thumb_instruction_changes_pc): Likewise.
(thumb2_instruction_changes_pc): Likewise.
(shifted_reg_val): Likewise.
* arm.h (submask): Move macro from arm-tdep.h
(bit): Likewise.
(bits): Likewise.
(sbits): Likewise.
(BranchDest): Likewise.
(thumb_advance_itstate): Moved declaration from arm-tdep.h
(arm_instruction_changes_pc): Likewise.
(thumb_instruction_changes_pc): Likewise.
(thumb2_instruction_changes_pc): Likewise.
(shifted_reg_val): Likewise.
* arm-linux-tdep.c: Include arch/arm.h, arch/arm-get-next-pcs.h
arch/arm-linux.h.
(arm_linux_get_next_pcs_ops): New struct.
(ARM_SIGCONTEXT_R0, ARM_UCONTEXT_SIGCONTEXT,
ARM_OLD_RT_SIGFRAME_SIGINFO, ARM_OLD_RT_SIGFRAME_UCONTEXT,
ARM_NEW_RT_SIGFRAME_UCONTEXT, ARM_NEW_SIGFRAME_MAGIC): Move stack
layout defines to arch/arm-linux.h.
(arm_linux_sigreturn_next_pc_offset): Move to arch/arm-linux.c.
(arm_linux_software_single_step): Adjust for arm_get_next_pcs
implementation.
* arm-tdep.c: Include arch/arm-get-next-pcs.h.
(arm_get_next_pcs_ops): New struct.
(submask): Move macro to arm.h.
(bit): Likewise.
(bits): Likewise.
(sbits): Likewise.
(BranchDest): Likewise.
(thumb_instruction_changes_pc): Move to arm.c
(thumb2_instruction_changes_pc): Likewise.
(arm_instruction_changes_pc): Likewise.
(shifted_reg_val): Likewise.
(thumb_advance_itstate): Likewise.
(thumb_get_next_pc_raw): Move to arm-get-next-pcs.c.
(arm_get_next_pc_raw): Likewise.
(arm_get_next_pc): Likewise.
(thumb_deal_with_atomic_sequence_raw): Likewise.
(arm_deal_with_atomic_sequence_raw): Likewise.
(arm_deal_with_atomic_sequence): Likewise.
(arm_get_next_pcs_read_memory_unsigned_integer): New function.
(arm_get_next_pcs_addr_bits_remove): Likewise.
(arm_get_next_pcs_syscall_next_pc): Likewise.
(arm_get_next_pcs_is_thumb): Likewise.
(arm_software_single_step): Adjust for arm_get_next_pcs
implementation.
* arm-tdep.h: (arm_get_next_pc): Remove declaration.
(arm_get_next_pcs_read_memory_unsigned_integer):
New declaration.
(arm_get_next_pcs_addr_bits_remove): Likewise.
(arm_get_next_pcs_syscall_next_pc): Likewise.
(arm_get_next_pcs_is_thumb): Likewise.
(arm_deal_with_atomic_sequence: Remove declaration.
* common/gdb_vecs.h: Add CORE_ADDR vector definition.
* configure.tgt (aarch64*-*-linux): Add arm-get-next-pcs.o,
arm-linux.o.
(arm*-wince-pe): Add arm-get-next-pcs.o.
(arm*-*-linux*): Add arm-get-next-pcs.o, arm-linux.o,
arm-get-next-pcs.o
(arm*-*-netbsd*,arm*-*-knetbsd*-gnu): Add arm-get-next-pcs.o.
(arm*-*-openbsd*): Likewise.
(arm*-*-symbianelf*): Likewise.
(arm*-*-*): Likewise.
* symtab.h: Move CORE_ADDR vector definition to gdb_vecs.h.
2015-12-18 Antoine Tremblay <antoine.tremblay@ericsson.com>
* Makefile.in (SFILES): Append common/common-regcache.c.

View File

@ -657,7 +657,8 @@ ALL_64_TARGET_OBS = \
# All other target-dependent objects files (used with --enable-targets=all).
ALL_TARGET_OBS = \
armbsd-tdep.o arm.o arm-linux-tdep.o arm-symbian-tdep.o \
armbsd-tdep.o arm.o arm-linux.o arm-linux-tdep.o \
arm-get-next-pcs.o arm-symbian-tdep.o \
armnbsd-tdep.o armobsd-tdep.o \
arm-tdep.o arm-wince-tdep.o \
avr-tdep.o \
@ -1661,8 +1662,9 @@ ALLDEPFILES = \
amd64-dicos-tdep.c \
amd64-linux-nat.c amd64-linux-tdep.c \
amd64-sol2-tdep.c \
arm.c \
arm-linux-nat.c arm-linux-tdep.c arm-symbian-tdep.c arm-tdep.c \
arm.c arm-get-next-pcs.c \
arm-linux.c arm-linux-nat.c arm-linux-tdep.c \
arm-symbian-tdep.c arm-tdep.c \
armnbsd-nat.c armbsd-tdep.c armnbsd-tdep.c armobsd-tdep.c \
avr-tdep.c \
bfin-linux-tdep.c bfin-tdep.c \
@ -2292,6 +2294,14 @@ arm.o: ${srcdir}/arch/arm.c
$(COMPILE) $(srcdir)/arch/arm.c
$(POSTCOMPILE)
arm-linux.o: ${srcdir}/arch/arm-linux.c
$(COMPILE) $(srcdir)/arch/arm-linux.c
$(POSTCOMPILE)
arm-get-next-pcs.o: ${srcdir}/arch/arm-get-next-pcs.c
$(COMPILE) $(srcdir)/arch/arm-get-next-pcs.c
$(POSTCOMPILE)
# gdb/nat/ dependencies
#
# Need to explicitly specify the compile rule as make will do nothing

926
gdb/arch/arm-get-next-pcs.c Normal file
View File

@ -0,0 +1,926 @@
/* Common code for ARM software single stepping support.
Copyright (C) 1988-2015 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 3 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, see <http://www.gnu.org/licenses/>. */
#include "common-defs.h"
#include "gdb_vecs.h"
#include "common-regcache.h"
#include "arm.h"
#include "arm-get-next-pcs.h"
/* See arm-get-next-pcs.h. */
void
arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
struct arm_get_next_pcs_ops *ops,
int byte_order,
int byte_order_for_code,
const gdb_byte *arm_thumb2_breakpoint,
struct regcache *regcache)
{
self->ops = ops;
self->byte_order = byte_order;
self->byte_order_for_code = byte_order_for_code;
self->arm_thumb2_breakpoint = arm_thumb2_breakpoint;
self->regcache = regcache;
}
/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
instruction and ending with a STREX{,B,H,D} instruction. If such a sequence
is found, attempt to step through it. The end of the sequence address is
added to the next_pcs list. */
static VEC (CORE_ADDR) *
thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
CORE_ADDR pc)
{
int byte_order_for_code = self->byte_order_for_code;
CORE_ADDR breaks[2] = {-1, -1};
CORE_ADDR loc = pc;
unsigned short insn1, insn2;
int insn_count;
int index;
int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
const int atomic_sequence_length = 16; /* Instruction sequence length. */
ULONGEST status, itstate;
VEC (CORE_ADDR) *next_pcs = NULL;
/* We currently do not support atomic sequences within an IT block. */
status = regcache_raw_get_unsigned (self->regcache, ARM_PS_REGNUM);
itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
if (itstate & 0x0f)
return NULL;
/* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */
insn1 = self->ops->read_mem_uint (loc, 2, byte_order_for_code);
loc += 2;
if (thumb_insn_size (insn1) != 4)
return NULL;
insn2 = self->ops->read_mem_uint (loc, 2, byte_order_for_code);
loc += 2;
if (!((insn1 & 0xfff0) == 0xe850
|| ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040)))
return NULL;
/* Assume that no atomic sequence is longer than "atomic_sequence_length"
instructions. */
for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
{
insn1 = self->ops->read_mem_uint (loc, 2,byte_order_for_code);
loc += 2;
if (thumb_insn_size (insn1) != 4)
{
/* Assume that there is at most one conditional branch in the
atomic sequence. If a conditional branch is found, put a
breakpoint in its destination address. */
if ((insn1 & 0xf000) == 0xd000 && bits (insn1, 8, 11) != 0x0f)
{
if (last_breakpoint > 0)
return NULL; /* More than one conditional branch found,
fallback to the standard code. */
breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1);
last_breakpoint++;
}
/* We do not support atomic sequences that use any *other*
instructions but conditional branches to change the PC.
Fall back to standard code to avoid losing control of
execution. */
else if (thumb_instruction_changes_pc (insn1))
return NULL;
}
else
{
insn2 = self->ops->read_mem_uint (loc, 2, byte_order_for_code);
loc += 2;
/* Assume that there is at most one conditional branch in the
atomic sequence. If a conditional branch is found, put a
breakpoint in its destination address. */
if ((insn1 & 0xf800) == 0xf000
&& (insn2 & 0xd000) == 0x8000
&& (insn1 & 0x0380) != 0x0380)
{
int sign, j1, j2, imm1, imm2;
unsigned int offset;
sign = sbits (insn1, 10, 10);
imm1 = bits (insn1, 0, 5);
imm2 = bits (insn2, 0, 10);
j1 = bit (insn2, 13);
j2 = bit (insn2, 11);
offset = (sign << 20) + (j2 << 19) + (j1 << 18);
offset += (imm1 << 12) + (imm2 << 1);
if (last_breakpoint > 0)
return 0; /* More than one conditional branch found,
fallback to the standard code. */
breaks[1] = loc + offset;
last_breakpoint++;
}
/* We do not support atomic sequences that use any *other*
instructions but conditional branches to change the PC.
Fall back to standard code to avoid losing control of
execution. */
else if (thumb2_instruction_changes_pc (insn1, insn2))
return NULL;
/* If we find a strex{,b,h,d}, we're done. */
if ((insn1 & 0xfff0) == 0xe840
|| ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040))
break;
}
}
/* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
if (insn_count == atomic_sequence_length)
return NULL;
/* Insert a breakpoint right after the end of the atomic sequence. */
breaks[0] = loc;
/* Check for duplicated breakpoints. Check also for a breakpoint
placed (branch instruction's destination) anywhere in sequence. */
if (last_breakpoint
&& (breaks[1] == breaks[0]
|| (breaks[1] >= pc && breaks[1] < loc)))
last_breakpoint = 0;
/* Adds the breakpoints to the list to be inserted. */
for (index = 0; index <= last_breakpoint; index++)
VEC_safe_push (CORE_ADDR, next_pcs, MAKE_THUMB_ADDR (breaks[index]));
return next_pcs;
}
/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
instruction and ending with a STREX{,B,H,D} instruction. If such a sequence
is found, attempt to step through it. The end of the sequence address is
added to the next_pcs list. */
static VEC (CORE_ADDR) *
arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
CORE_ADDR pc)
{
int byte_order_for_code = self->byte_order_for_code;
CORE_ADDR breaks[2] = {-1, -1};
CORE_ADDR loc = pc;
unsigned int insn;
int insn_count;
int index;
int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
const int atomic_sequence_length = 16; /* Instruction sequence length. */
VEC (CORE_ADDR) *next_pcs = NULL;
/* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.
Note that we do not currently support conditionally executed atomic
instructions. */
insn = self->ops->read_mem_uint (loc, 4, byte_order_for_code);
loc += 4;
if ((insn & 0xff9000f0) != 0xe1900090)
return NULL;
/* Assume that no atomic sequence is longer than "atomic_sequence_length"
instructions. */
for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
{
insn = self->ops->read_mem_uint (loc, 4, byte_order_for_code);
loc += 4;
/* Assume that there is at most one conditional branch in the atomic
sequence. If a conditional branch is found, put a breakpoint in
its destination address. */
if (bits (insn, 24, 27) == 0xa)
{
if (last_breakpoint > 0)
return NULL; /* More than one conditional branch found, fallback
to the standard single-step code. */
breaks[1] = BranchDest (loc - 4, insn);
last_breakpoint++;
}
/* We do not support atomic sequences that use any *other* instructions
but conditional branches to change the PC. Fall back to standard
code to avoid losing control of execution. */
else if (arm_instruction_changes_pc (insn))
return NULL;
/* If we find a strex{,b,h,d}, we're done. */
if ((insn & 0xff9000f0) == 0xe1800090)
break;
}
/* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
if (insn_count == atomic_sequence_length)
return NULL;
/* Insert a breakpoint right after the end of the atomic sequence. */
breaks[0] = loc;
/* Check for duplicated breakpoints. Check also for a breakpoint
placed (branch instruction's destination) anywhere in sequence. */
if (last_breakpoint
&& (breaks[1] == breaks[0]
|| (breaks[1] >= pc && breaks[1] < loc)))
last_breakpoint = 0;
/* Adds the breakpoints to the list to be inserted. */
for (index = 0; index <= last_breakpoint; index++)
VEC_safe_push (CORE_ADDR, next_pcs, breaks[index]);
return next_pcs;
}
/* See arm-get-next-pcs.h. */
VEC (CORE_ADDR) *
arm_get_next_pcs (struct arm_get_next_pcs *self, CORE_ADDR pc)
{
VEC (CORE_ADDR) *next_pcs = NULL;
if (self->ops->is_thumb (self))
{
next_pcs = thumb_deal_with_atomic_sequence_raw (self, pc);
if (next_pcs == NULL)
next_pcs = thumb_get_next_pcs_raw (self, pc);
}
else
{
next_pcs = arm_deal_with_atomic_sequence_raw (self, pc);
if (next_pcs == NULL)
next_pcs = arm_get_next_pcs_raw (self, pc);
}
return next_pcs;
}
/* See arm-get-next-pcs.h. */
VEC (CORE_ADDR) *
thumb_get_next_pcs_raw (struct arm_get_next_pcs *self,
CORE_ADDR pc)
{
int byte_order = self->byte_order;
int byte_order_for_code = self->byte_order_for_code;
unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */
unsigned short inst1;
CORE_ADDR nextpc = pc + 2; /* Default is next instruction. */
unsigned long offset;
ULONGEST status, itstate;
struct regcache *regcache = self->regcache;
VEC (CORE_ADDR) * next_pcs = NULL;
nextpc = MAKE_THUMB_ADDR (nextpc);
pc_val = MAKE_THUMB_ADDR (pc_val);
inst1 = self->ops->read_mem_uint (pc, 2, byte_order_for_code);
/* Thumb-2 conditional execution support. There are eight bits in
the CPSR which describe conditional execution state. Once
reconstructed (they're in a funny order), the low five bits
describe the low bit of the condition for each instruction and
how many instructions remain. The high three bits describe the
base condition. One of the low four bits will be set if an IT
block is active. These bits read as zero on earlier
processors. */
status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
/* If-Then handling. On GNU/Linux, where this routine is used, we
use an undefined instruction as a breakpoint. Unlike BKPT, IT
can disable execution of the undefined instruction. So we might
miss the breakpoint if we set it on a skipped conditional
instruction. Because conditional instructions can change the
flags, affecting the execution of further instructions, we may
need to set two breakpoints. */
if (self->arm_thumb2_breakpoint != NULL)
{
if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
{
/* An IT instruction. Because this instruction does not
modify the flags, we can accurately predict the next
executed instruction. */
itstate = inst1 & 0x00ff;
pc += thumb_insn_size (inst1);
while (itstate != 0 && ! condition_true (itstate >> 4, status))
{
inst1 = self->ops->read_mem_uint (pc, 2,byte_order_for_code);
pc += thumb_insn_size (inst1);
itstate = thumb_advance_itstate (itstate);
}
VEC_safe_push (CORE_ADDR, next_pcs, MAKE_THUMB_ADDR (pc));
return next_pcs;
}
else if (itstate != 0)
{
/* We are in a conditional block. Check the condition. */
if (! condition_true (itstate >> 4, status))
{
/* Advance to the next executed instruction. */
pc += thumb_insn_size (inst1);
itstate = thumb_advance_itstate (itstate);
while (itstate != 0 && ! condition_true (itstate >> 4, status))
{
inst1 = self->ops->read_mem_uint (pc, 2, byte_order_for_code);
pc += thumb_insn_size (inst1);
itstate = thumb_advance_itstate (itstate);
}
VEC_safe_push (CORE_ADDR, next_pcs, MAKE_THUMB_ADDR (pc));
return next_pcs;
}
else if ((itstate & 0x0f) == 0x08)
{
/* This is the last instruction of the conditional
block, and it is executed. We can handle it normally
because the following instruction is not conditional,
and we must handle it normally because it is
permitted to branch. Fall through. */
}
else
{
int cond_negated;
/* There are conditional instructions after this one.
If this instruction modifies the flags, then we can
not predict what the next executed instruction will
be. Fortunately, this instruction is architecturally
forbidden to branch; we know it will fall through.
Start by skipping past it. */
pc += thumb_insn_size (inst1);
itstate = thumb_advance_itstate (itstate);
/* Set a breakpoint on the following instruction. */
gdb_assert ((itstate & 0x0f) != 0);
VEC_safe_push (CORE_ADDR, next_pcs, MAKE_THUMB_ADDR (pc));
cond_negated = (itstate >> 4) & 1;
/* Skip all following instructions with the same
condition. If there is a later instruction in the IT
block with the opposite condition, set the other
breakpoint there. If not, then set a breakpoint on
the instruction after the IT block. */
do
{
inst1 = self->ops->read_mem_uint (pc, 2, byte_order_for_code);
pc += thumb_insn_size (inst1);
itstate = thumb_advance_itstate (itstate);
}
while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
VEC_safe_push (CORE_ADDR, next_pcs, MAKE_THUMB_ADDR (pc));
return next_pcs;
}
}
}
else if (itstate & 0x0f)
{
/* We are in a conditional block. Check the condition. */
int cond = itstate >> 4;
if (! condition_true (cond, status))
{
/* Advance to the next instruction. All the 32-bit
instructions share a common prefix. */
VEC_safe_push (CORE_ADDR, next_pcs,
MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)));
}
return next_pcs;
/* Otherwise, handle the instruction normally. */
}
if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
{
CORE_ADDR sp;
/* Fetch the saved PC from the stack. It's stored above
all of the other registers. */
offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
sp = regcache_raw_get_unsigned (regcache, ARM_SP_REGNUM);
nextpc = self->ops->read_mem_uint (sp + offset, 4, byte_order);
}
else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */
{
unsigned long cond = bits (inst1, 8, 11);
if (cond == 0x0f) /* 0x0f = SWI */
{
nextpc = self->ops->syscall_next_pc (self, pc);
}
else if (cond != 0x0f && condition_true (cond, status))
nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
}
else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */
{
nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
}
else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */
{
unsigned short inst2;
inst2 = self->ops->read_mem_uint (pc + 2, 2, byte_order_for_code);
/* Default to the next instruction. */
nextpc = pc + 4;
nextpc = MAKE_THUMB_ADDR (nextpc);
if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
{
/* Branches and miscellaneous control instructions. */
if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
{
/* B, BL, BLX. */
int j1, j2, imm1, imm2;
imm1 = sbits (inst1, 0, 10);
imm2 = bits (inst2, 0, 10);
j1 = bit (inst2, 13);
j2 = bit (inst2, 11);
offset = ((imm1 << 12) + (imm2 << 1));
offset ^= ((!j2) << 22) | ((!j1) << 23);
nextpc = pc_val + offset;
/* For BLX make sure to clear the low bits. */
if (bit (inst2, 12) == 0)
nextpc = nextpc & 0xfffffffc;
}
else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
{
/* SUBS PC, LR, #imm8. */
nextpc = regcache_raw_get_unsigned (regcache, ARM_LR_REGNUM);
nextpc -= inst2 & 0x00ff;
}
else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
{
/* Conditional branch. */
if (condition_true (bits (inst1, 6, 9), status))
{
int sign, j1, j2, imm1, imm2;
sign = sbits (inst1, 10, 10);
imm1 = bits (inst1, 0, 5);
imm2 = bits (inst2, 0, 10);
j1 = bit (inst2, 13);
j2 = bit (inst2, 11);
offset = (sign << 20) + (j2 << 19) + (j1 << 18);
offset += (imm1 << 12) + (imm2 << 1);
nextpc = pc_val + offset;
}
}
}
else if ((inst1 & 0xfe50) == 0xe810)
{
/* Load multiple or RFE. */
int rn, offset, load_pc = 1;
rn = bits (inst1, 0, 3);
if (bit (inst1, 7) && !bit (inst1, 8))
{
/* LDMIA or POP */
if (!bit (inst2, 15))
load_pc = 0;
offset = bitcount (inst2) * 4 - 4;
}
else if (!bit (inst1, 7) && bit (inst1, 8))
{
/* LDMDB */
if (!bit (inst2, 15))
load_pc = 0;
offset = -4;
}
else if (bit (inst1, 7) && bit (inst1, 8))
{
/* RFEIA */
offset = 0;
}
else if (!bit (inst1, 7) && !bit (inst1, 8))
{
/* RFEDB */
offset = -8;
}
else
load_pc = 0;
if (load_pc)
{
CORE_ADDR addr = regcache_raw_get_unsigned (regcache, rn);
nextpc = self->ops->read_mem_uint (addr + offset, 4, byte_order);
}
}
else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
{
/* MOV PC or MOVS PC. */
nextpc = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
nextpc = MAKE_THUMB_ADDR (nextpc);
}
else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
{
/* LDR PC. */
CORE_ADDR base;
int rn, load_pc = 1;
rn = bits (inst1, 0, 3);
base = regcache_raw_get_unsigned (regcache, rn);
if (rn == ARM_PC_REGNUM)
{
base = (base + 4) & ~(CORE_ADDR) 0x3;
if (bit (inst1, 7))
base += bits (inst2, 0, 11);
else
base -= bits (inst2, 0, 11);
}
else if (bit (inst1, 7))
base += bits (inst2, 0, 11);
else if (bit (inst2, 11))
{
if (bit (inst2, 10))
{
if (bit (inst2, 9))
base += bits (inst2, 0, 7);
else
base -= bits (inst2, 0, 7);
}
}
else if ((inst2 & 0x0fc0) == 0x0000)
{
int shift = bits (inst2, 4, 5), rm = bits (inst2, 0, 3);
base += regcache_raw_get_unsigned (regcache, rm) << shift;
}
else
/* Reserved. */
load_pc = 0;
if (load_pc)
nextpc
= self->ops->read_mem_uint (base, 4, byte_order);
}
else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
{
/* TBB. */
CORE_ADDR tbl_reg, table, offset, length;
tbl_reg = bits (inst1, 0, 3);
if (tbl_reg == 0x0f)
table = pc + 4; /* Regcache copy of PC isn't right yet. */
else
table = regcache_raw_get_unsigned (regcache, tbl_reg);
offset = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
length = 2 * self->ops->read_mem_uint (table + offset, 1, byte_order);
nextpc = pc_val + length;
}
else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
{
/* TBH. */
CORE_ADDR tbl_reg, table, offset, length;
tbl_reg = bits (inst1, 0, 3);
if (tbl_reg == 0x0f)
table = pc + 4; /* Regcache copy of PC isn't right yet. */
else
table = regcache_raw_get_unsigned (regcache, tbl_reg);
offset = 2 * regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
length = 2 * self->ops->read_mem_uint (table + offset, 2, byte_order);
nextpc = pc_val + length;
}
}
else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */
{
if (bits (inst1, 3, 6) == 0x0f)
nextpc = UNMAKE_THUMB_ADDR (pc_val);
else
nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
}
else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */
{
if (bits (inst1, 3, 6) == 0x0f)
nextpc = pc_val;
else
nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
nextpc = MAKE_THUMB_ADDR (nextpc);
}
else if ((inst1 & 0xf500) == 0xb100)
{
/* CBNZ or CBZ. */
int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1);
ULONGEST reg = regcache_raw_get_unsigned (regcache, bits (inst1, 0, 2));
if (bit (inst1, 11) && reg != 0)
nextpc = pc_val + imm;
else if (!bit (inst1, 11) && reg == 0)
nextpc = pc_val + imm;
}
VEC_safe_push (CORE_ADDR, next_pcs, nextpc);
return next_pcs;
}
/* Get the raw next possible addresses. PC in next_pcs is the current program
counter, which is assumed to be executing in ARM mode.
The values returned have the execution state of the next instruction
encoded in it. Use IS_THUMB_ADDR () to see whether the instruction is
in Thumb-State, and gdbarch_addr_bits_remove () to get the plain memory
address in GDB and arm_addr_bits_remove in GDBServer. */
VEC (CORE_ADDR) *
arm_get_next_pcs_raw (struct arm_get_next_pcs *self,
CORE_ADDR pc)
{
int byte_order = self->byte_order;
unsigned long pc_val;
unsigned long this_instr = 0;
unsigned long status;
CORE_ADDR nextpc;
struct regcache *regcache = self->regcache;
VEC (CORE_ADDR) *next_pcs = NULL;
pc_val = (unsigned long) pc;
this_instr = self->ops->read_mem_uint (pc, 4, byte_order);
status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */
if (bits (this_instr, 28, 31) == INST_NV)
switch (bits (this_instr, 24, 27))
{
case 0xa:
case 0xb:
{
/* Branch with Link and change to Thumb. */
nextpc = BranchDest (pc, this_instr);
nextpc |= bit (this_instr, 24) << 1;
nextpc = MAKE_THUMB_ADDR (nextpc);
break;
}
case 0xc:
case 0xd:
case 0xe:
/* Coprocessor register transfer. */
if (bits (this_instr, 12, 15) == 15)
error (_("Invalid update to pc in instruction"));
break;
}
else if (condition_true (bits (this_instr, 28, 31), status))
{
switch (bits (this_instr, 24, 27))
{
case 0x0:
case 0x1: /* data processing */
case 0x2:
case 0x3:
{
unsigned long operand1, operand2, result = 0;
unsigned long rn;
int c;
if (bits (this_instr, 12, 15) != 15)
break;
if (bits (this_instr, 22, 25) == 0
&& bits (this_instr, 4, 7) == 9) /* multiply */
error (_("Invalid update to pc in instruction"));
/* BX <reg>, BLX <reg> */
if (bits (this_instr, 4, 27) == 0x12fff1
|| bits (this_instr, 4, 27) == 0x12fff3)
{
rn = bits (this_instr, 0, 3);
nextpc = ((rn == ARM_PC_REGNUM)
? (pc_val + 8)
: regcache_raw_get_unsigned (regcache, rn));
VEC_safe_push (CORE_ADDR, next_pcs, nextpc);
return next_pcs;
}
/* Multiply into PC. */
c = (status & FLAG_C) ? 1 : 0;
rn = bits (this_instr, 16, 19);
operand1 = ((rn == ARM_PC_REGNUM)
? (pc_val + 8)
: regcache_raw_get_unsigned (regcache, rn));
if (bit (this_instr, 25))
{
unsigned long immval = bits (this_instr, 0, 7);
unsigned long rotate = 2 * bits (this_instr, 8, 11);
operand2 = ((immval >> rotate) | (immval << (32 - rotate)))
& 0xffffffff;
}
else /* operand 2 is a shifted register. */
operand2 = shifted_reg_val (regcache, this_instr, c,
pc_val, status);
switch (bits (this_instr, 21, 24))
{
case 0x0: /*and */
result = operand1 & operand2;
break;
case 0x1: /*eor */
result = operand1 ^ operand2;
break;
case 0x2: /*sub */
result = operand1 - operand2;
break;
case 0x3: /*rsb */
result = operand2 - operand1;
break;
case 0x4: /*add */
result = operand1 + operand2;
break;
case 0x5: /*adc */
result = operand1 + operand2 + c;
break;
case 0x6: /*sbc */
result = operand1 - operand2 + c;
break;
case 0x7: /*rsc */
result = operand2 - operand1 + c;
break;
case 0x8:
case 0x9:
case 0xa:
case 0xb: /* tst, teq, cmp, cmn */
result = (unsigned long) nextpc;
break;
case 0xc: /*orr */
result = operand1 | operand2;
break;
case 0xd: /*mov */
/* Always step into a function. */
result = operand2;
break;
case 0xe: /*bic */
result = operand1 & ~operand2;
break;
case 0xf: /*mvn */
result = ~operand2;
break;
}
nextpc = self->ops->addr_bits_remove (self, result);
break;
}
case 0x4:
case 0x5: /* data transfer */
case 0x6:
case 0x7:
if (bits (this_instr, 25, 27) == 0x3 && bit (this_instr, 4) == 1)
{
/* Media instructions and architecturally undefined
instructions. */
break;
}
if (bit (this_instr, 20))
{
/* load */
if (bits (this_instr, 12, 15) == 15)
{
/* rd == pc */
unsigned long rn;
unsigned long base;
if (bit (this_instr, 22))
error (_("Invalid update to pc in instruction"));
/* byte write to PC */
rn = bits (this_instr, 16, 19);
base = ((rn == ARM_PC_REGNUM)
? (pc_val + 8)
: regcache_raw_get_unsigned (regcache, rn));
if (bit (this_instr, 24))
{
/* pre-indexed */
int c = (status & FLAG_C) ? 1 : 0;
unsigned long offset =
(bit (this_instr, 25)
? shifted_reg_val (regcache, this_instr, c,
pc_val, status)
: bits (this_instr, 0, 11));
if (bit (this_instr, 23))
base += offset;
else
base -= offset;
}
nextpc
= (CORE_ADDR) self->ops->read_mem_uint ((CORE_ADDR) base,
4, byte_order);
}
}
break;
case 0x8:
case 0x9: /* block transfer */
if (bit (this_instr, 20))
{
/* LDM */
if (bit (this_instr, 15))
{
/* loading pc */
int offset = 0;
CORE_ADDR rn_val_offset = 0;
unsigned long rn_val
= regcache_raw_get_unsigned (regcache,
bits (this_instr, 16, 19));
if (bit (this_instr, 23))
{
/* up */
unsigned long reglist = bits (this_instr, 0, 14);
offset = bitcount (reglist) * 4;
if (bit (this_instr, 24)) /* pre */
offset += 4;
}
else if (bit (this_instr, 24))
offset = -4;
rn_val_offset = rn_val + offset;
nextpc = (CORE_ADDR) self->ops->read_mem_uint (rn_val_offset,
4, byte_order);
}
}
break;
case 0xb: /* branch & link */
case 0xa: /* branch */
{
nextpc = BranchDest (pc, this_instr);
break;
}
case 0xc:
case 0xd:
case 0xe: /* coproc ops */
break;
case 0xf: /* SWI */
{
nextpc = self->ops->syscall_next_pc (self, pc);
}
break;
default:
error (_("Bad bit-field extraction\n"));
return next_pcs;
}
}
VEC_safe_push (CORE_ADDR, next_pcs, nextpc);
return next_pcs;
}

View File

@ -0,0 +1,70 @@
/* Common code for ARM software single stepping support.
Copyright (C) 1988-2015 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 3 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, see <http://www.gnu.org/licenses/>. */
#ifndef ARM_GET_NEXT_PCS_H
#define ARM_GET_NEXT_PCS_H 1
/* Forward declaration. */
struct arm_get_next_pcs;
/* get_next_pcs operations. */
struct arm_get_next_pcs_ops
{
ULONGEST (*read_mem_uint) (CORE_ADDR memaddr, int len, int byte_order);
CORE_ADDR (*syscall_next_pc) (struct arm_get_next_pcs *self, CORE_ADDR pc);
CORE_ADDR (*addr_bits_remove) (struct arm_get_next_pcs *self, CORE_ADDR val);
int (*is_thumb) (struct arm_get_next_pcs *self);
};
/* Context for a get_next_pcs call on ARM. */
struct arm_get_next_pcs
{
/* Operations implementations. */
struct arm_get_next_pcs_ops *ops;
/* Byte order for data. */
int byte_order;
/* Byte order for code. */
int byte_order_for_code;
/* Thumb2 breakpoint instruction. */
const gdb_byte *arm_thumb2_breakpoint;
/* Registry cache. */
struct regcache *regcache;
};
/* Initialize arm_get_next_pcs. */
void arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
struct arm_get_next_pcs_ops *ops,
int byte_order,
int byte_order_for_code,
const gdb_byte *arm_thumb2_breakpoint,
struct regcache *regcache);
/* Find the next possible PCs after the current instruction executes. */
VEC (CORE_ADDR) *arm_get_next_pcs (struct arm_get_next_pcs *self,
CORE_ADDR pc);
/* Find the next possible PCs for thumb mode. */
VEC (CORE_ADDR) *thumb_get_next_pcs_raw (struct arm_get_next_pcs *self,
CORE_ADDR pc);
/* Find the next possible PCs for arm mode. */
VEC (CORE_ADDR) *arm_get_next_pcs_raw (struct arm_get_next_pcs *self,
CORE_ADDR pc);
#endif /* ARM_GET_NEXT_PCS_H */

57
gdb/arch/arm-linux.c Normal file
View File

@ -0,0 +1,57 @@
/* Common target dependent code for GNU/Linux on ARM systems.
Copyright (C) 1999-2015 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 3 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, see <http://www.gnu.org/licenses/>. */
#include "common-defs.h"
#include "arch/arm.h"
#include "arm-linux.h"
/* Calculate the offset from stack pointer of the pc register on the stack
in the case of a sigreturn or sigreturn_rt syscall. */
int
arm_linux_sigreturn_next_pc_offset (unsigned long sp,
unsigned long sp_data,
unsigned long svc_number,
int is_sigreturn)
{
/* Offset of R0 register. */
int r0_offset = 0;
/* Offset of PC register. */
int pc_offset = 0;
if (is_sigreturn)
{
if (sp_data == ARM_NEW_SIGFRAME_MAGIC)
r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
else
r0_offset = ARM_SIGCONTEXT_R0;
}
else
{
if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT;
else
r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT;
r0_offset += ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
}
pc_offset = r0_offset + INT_REGISTER_SIZE * ARM_PC_REGNUM;
return pc_offset;
}

74
gdb/arch/arm-linux.h Normal file
View File

@ -0,0 +1,74 @@
/* Common target dependent code for GNU/Linux on ARM systems.
Copyright (C) 1999-2015 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 3 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, see <http://www.gnu.org/licenses/>. */
#ifndef ARM_LINUX_H
#define ARM_LINUX_H
/* There are a couple of different possible stack layouts that
we need to support.
Before version 2.6.18, the kernel used completely independent
layouts for non-RT and RT signals. For non-RT signals the stack
began directly with a struct sigcontext. For RT signals the stack
began with two redundant pointers (to the siginfo and ucontext),
and then the siginfo and ucontext.
As of version 2.6.18, the non-RT signal frame layout starts with
a ucontext and the RT signal frame starts with a siginfo and then
a ucontext. Also, the ucontext now has a designated save area
for coprocessor registers.
For RT signals, it's easy to tell the difference: we look for
pinfo, the pointer to the siginfo. If it has the expected
value, we have an old layout. If it doesn't, we have the new
layout.
For non-RT signals, it's a bit harder. We need something in one
layout or the other with a recognizable offset and value. We can't
use the return trampoline, because ARM usually uses SA_RESTORER,
in which case the stack return trampoline is not filled in.
We can't use the saved stack pointer, because sigaltstack might
be in use. So for now we guess the new layout... */
/* There are three words (trap_no, error_code, oldmask) in
struct sigcontext before r0. */
#define ARM_SIGCONTEXT_R0 0xc
/* There are five words (uc_flags, uc_link, and three for uc_stack)
in the ucontext_t before the sigcontext. */
#define ARM_UCONTEXT_SIGCONTEXT 0x14
/* There are three elements in an rt_sigframe before the ucontext:
pinfo, puc, and info. The first two are pointers and the third
is a struct siginfo, with size 128 bytes. We could follow puc
to the ucontext, but it's simpler to skip the whole thing. */
#define ARM_OLD_RT_SIGFRAME_SIGINFO 0x8
#define ARM_OLD_RT_SIGFRAME_UCONTEXT 0x88
#define ARM_NEW_RT_SIGFRAME_UCONTEXT 0x80
#define ARM_NEW_SIGFRAME_MAGIC 0x5ac3c35a
int
arm_linux_sigreturn_next_pc_offset (unsigned long sp,
unsigned long sp_data,
unsigned long svc_number,
int is_sigreturn);
#endif /* ARM_LINUX_H */

View File

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "common-defs.h"
#include "common-regcache.h"
#include "arm.h"
/* See arm.h. */
@ -87,3 +88,286 @@ condition_true (unsigned long cond, unsigned long status_reg)
}
return 1;
}
/* See arm.h. */
int
thumb_advance_itstate (unsigned int itstate)
{
/* Preserve IT[7:5], the first three bits of the condition. Shift
the upcoming condition flags left by one bit. */
itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
/* If we have finished the IT block, clear the state. */
if ((itstate & 0x0f) == 0)
itstate = 0;
return itstate;
}
/* See arm.h. */
int
arm_instruction_changes_pc (uint32_t this_instr)
{
if (bits (this_instr, 28, 31) == INST_NV)
/* Unconditional instructions. */
switch (bits (this_instr, 24, 27))
{
case 0xa:
case 0xb:
/* Branch with Link and change to Thumb. */
return 1;
case 0xc:
case 0xd:
case 0xe:
/* Coprocessor register transfer. */
if (bits (this_instr, 12, 15) == 15)
error (_("Invalid update to pc in instruction"));
return 0;
default:
return 0;
}
else
switch (bits (this_instr, 25, 27))
{
case 0x0:
if (bits (this_instr, 23, 24) == 2 && bit (this_instr, 20) == 0)
{
/* Multiplies and extra load/stores. */
if (bit (this_instr, 4) == 1 && bit (this_instr, 7) == 1)
/* Neither multiplies nor extension load/stores are allowed
to modify PC. */
return 0;
/* Otherwise, miscellaneous instructions. */
/* BX <reg>, BXJ <reg>, BLX <reg> */
if (bits (this_instr, 4, 27) == 0x12fff1
|| bits (this_instr, 4, 27) == 0x12fff2
|| bits (this_instr, 4, 27) == 0x12fff3)
return 1;
/* Other miscellaneous instructions are unpredictable if they
modify PC. */
return 0;
}
/* Data processing instruction. Fall through. */
case 0x1:
if (bits (this_instr, 12, 15) == 15)
return 1;
else
return 0;
case 0x2:
case 0x3:
/* Media instructions and architecturally undefined instructions. */
if (bits (this_instr, 25, 27) == 3 && bit (this_instr, 4) == 1)
return 0;
/* Stores. */
if (bit (this_instr, 20) == 0)
return 0;
/* Loads. */
if (bits (this_instr, 12, 15) == ARM_PC_REGNUM)
return 1;
else
return 0;
case 0x4:
/* Load/store multiple. */
if (bit (this_instr, 20) == 1 && bit (this_instr, 15) == 1)
return 1;
else
return 0;
case 0x5:
/* Branch and branch with link. */
return 1;
case 0x6:
case 0x7:
/* Coprocessor transfers or SWIs can not affect PC. */
return 0;
default:
internal_error (__FILE__, __LINE__, _("bad value in switch"));
}
}
/* See arm.h. */
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;
}
/* See arm.h. */
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;
}
/* See arm.h. */
unsigned long
shifted_reg_val (struct regcache *regcache, unsigned long inst,
int carry, unsigned long pc_val, unsigned long status_reg)
{
unsigned long res, shift;
int rm = bits (inst, 0, 3);
unsigned long shifttype = bits (inst, 5, 6);
if (bit (inst, 4))
{
int rs = bits (inst, 8, 11);
shift = (rs == 15
? pc_val + 8
: regcache_raw_get_unsigned (regcache, rs)) & 0xFF;
}
else
shift = bits (inst, 7, 11);
res = (rm == ARM_PC_REGNUM
? (pc_val + (bit (inst, 4) ? 12 : 8))
: regcache_raw_get_unsigned (regcache, rm));
switch (shifttype)
{
case 0: /* LSL */
res = shift >= 32 ? 0 : res << shift;
break;
case 1: /* LSR */
res = shift >= 32 ? 0 : res >> shift;
break;
case 2: /* ASR */
if (shift >= 32)
shift = 31;
res = ((res & 0x80000000L)
? ~((~res) >> shift) : res >> shift);
break;
case 3: /* ROR/RRX */
shift &= 31;
if (shift == 0)
res = (res >> 1) | (carry ? 0x80000000L : 0);
else
res = (res >> shift) | (res << (32 - shift));
break;
}
return res & 0xffffffff;
}

View File

@ -94,6 +94,18 @@ enum gdb_regnum {
#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
/* Support routines for instruction parsing. */
#define submask(x) ((1L << ((x) + 1)) - 1)
#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
#define bit(obj,st) (((obj) >> (st)) & 1)
#define sbits(obj,st,fn) \
((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
#define BranchDest(addr,instr) \
((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
/* Forward declaration. */
struct regcache;
/* Return the size in bytes of the complete Thumb instruction whose
first halfword is INST1. */
int thumb_insn_size (unsigned short inst1);
@ -104,4 +116,26 @@ int condition_true (unsigned long cond, unsigned long status_reg);
/* Return number of 1-bits in VAL. */
int bitcount (unsigned long val);
/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */
int arm_instruction_changes_pc (uint32_t this_instr);
/* Return 1 if the 16-bit Thumb instruction INST might change
control flow, 0 otherwise. */
int thumb_instruction_changes_pc (unsigned short inst);
/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2
might change control flow, 0 otherwise. */
int thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2);
/* Advance the state of the IT block and return that state. */
int thumb_advance_itstate (unsigned int itstate);
/* Decode shifted register value. */
unsigned long shifted_reg_val (struct regcache *regcache,
unsigned long inst,
int carry,
unsigned long pc_val,
unsigned long status_reg);
#endif

View File

@ -35,6 +35,9 @@
#include "auxv.h"
#include "xml-syscall.h"
#include "arch/arm.h"
#include "arch/arm-get-next-pcs.h"
#include "arch/arm-linux.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
#include "linux-tdep.h"
@ -262,6 +265,14 @@ static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa
/* Syscall number for rt_sigreturn. */
#define ARM_RT_SIGRETURN 173
/* Operation function pointers for get_next_pcs. */
static struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = {
arm_get_next_pcs_read_memory_unsigned_integer,
arm_get_next_pcs_syscall_next_pc,
arm_get_next_pcs_addr_bits_remove,
arm_get_next_pcs_is_thumb
};
static void
arm_linux_sigtramp_cache (struct frame_info *this_frame,
struct trad_frame_cache *this_cache,
@ -283,51 +294,7 @@ arm_linux_sigtramp_cache (struct frame_info *this_frame,
trad_frame_set_id (this_cache, frame_id_build (sp, func));
}
/* There are a couple of different possible stack layouts that
we need to support.
Before version 2.6.18, the kernel used completely independent
layouts for non-RT and RT signals. For non-RT signals the stack
began directly with a struct sigcontext. For RT signals the stack
began with two redundant pointers (to the siginfo and ucontext),
and then the siginfo and ucontext.
As of version 2.6.18, the non-RT signal frame layout starts with
a ucontext and the RT signal frame starts with a siginfo and then
a ucontext. Also, the ucontext now has a designated save area
for coprocessor registers.
For RT signals, it's easy to tell the difference: we look for
pinfo, the pointer to the siginfo. If it has the expected
value, we have an old layout. If it doesn't, we have the new
layout.
For non-RT signals, it's a bit harder. We need something in one
layout or the other with a recognizable offset and value. We can't
use the return trampoline, because ARM usually uses SA_RESTORER,
in which case the stack return trampoline is not filled in.
We can't use the saved stack pointer, because sigaltstack might
be in use. So for now we guess the new layout... */
/* There are three words (trap_no, error_code, oldmask) in
struct sigcontext before r0. */
#define ARM_SIGCONTEXT_R0 0xc
/* There are five words (uc_flags, uc_link, and three for uc_stack)
in the ucontext_t before the sigcontext. */
#define ARM_UCONTEXT_SIGCONTEXT 0x14
/* There are three elements in an rt_sigframe before the ucontext:
pinfo, puc, and info. The first two are pointers and the third
is a struct siginfo, with size 128 bytes. We could follow puc
to the ucontext, but it's simpler to skip the whole thing. */
#define ARM_OLD_RT_SIGFRAME_SIGINFO 0x8
#define ARM_OLD_RT_SIGFRAME_UCONTEXT 0x88
#define ARM_NEW_RT_SIGFRAME_UCONTEXT 0x80
#define ARM_NEW_SIGFRAME_MAGIC 0x5ac3c35a
/* See arm-linux.h for stack layout details. */
static void
arm_linux_sigreturn_init (const struct tramp_frame *self,
struct frame_info *this_frame,
@ -810,41 +777,6 @@ arm_linux_sigreturn_return_addr (struct frame_info *frame,
return 0;
}
/* Calculate the offset from stack pointer of the pc register on the stack
in the case of a sigreturn or sigreturn_rt syscall. */
static int
arm_linux_sigreturn_next_pc_offset (unsigned long sp,
unsigned long sp_data,
unsigned long svc_number,
int is_sigreturn)
{
/* Offset of R0 register. */
int r0_offset = 0;
/* Offset of PC register. */
int pc_offset = 0;
if (is_sigreturn)
{
if (sp_data == ARM_NEW_SIGFRAME_MAGIC)
r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
else
r0_offset = ARM_SIGCONTEXT_R0;
}
else
{
if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT;
else
r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT;
r0_offset += ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
}
pc_offset = r0_offset + INT_REGISTER_SIZE * ARM_PC_REGNUM;
return pc_offset;
}
/* Find the value of the next PC after a sigreturn or rt_sigreturn syscall
based on current processor state. */
static CORE_ADDR
@ -984,28 +916,41 @@ arm_linux_software_single_step (struct frame_info *frame)
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = get_regcache_arch (regcache);
struct address_space *aspace = get_regcache_aspace (regcache);
CORE_ADDR next_pc;
if (arm_deal_with_atomic_sequence (regcache))
return 1;
struct arm_get_next_pcs next_pcs_ctx;
CORE_ADDR pc;
int i;
VEC (CORE_ADDR) *next_pcs = NULL;
struct cleanup *old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs);
/* If the target does have hardware single step, GDB doesn't have
to bother software single step. */
if (target_can_do_single_step () == 1)
return 0;
next_pc = arm_get_next_pc (regcache, regcache_read_pc (regcache));
arm_get_next_pcs_ctor (&next_pcs_ctx,
&arm_linux_get_next_pcs_ops,
gdbarch_byte_order (gdbarch),
gdbarch_byte_order_for_code (gdbarch),
gdbarch_tdep (gdbarch)->thumb2_breakpoint,
regcache);
/* The Linux kernel offers some user-mode helpers in a high page. We can
not read this page (as of 2.6.23), and even if we could then we couldn't
set breakpoints in it, and even if we could then the atomic operations
would fail when interrupted. They are all called as functions and return
to the address in LR, so step to there instead. */
if (next_pc > 0xffff0000)
next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
next_pcs = arm_get_next_pcs (&next_pcs_ctx, regcache_read_pc (regcache));
arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
{
/* The Linux kernel offers some user-mode helpers in a high page. We can
not read this page (as of 2.6.23), and even if we could then we
couldn't set breakpoints in it, and even if we could then the atomic
operations would fail when interrupted. They are all called as
functions and return to the address in LR, so step to there
instead. */
if (pc > 0xffff0000)
pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
}
do_cleanups (old_chain);
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,9 @@
struct gdbarch;
struct regset;
struct address_space;
struct get_next_pcs;
struct arm_get_next_pcs;
struct gdb_get_next_pcs;
#include "arch/arm.h"
@ -250,10 +253,21 @@ extern void
ULONGEST val, enum pc_write_style write_pc);
CORE_ADDR arm_skip_stub (struct frame_info *, CORE_ADDR);
CORE_ADDR arm_get_next_pc (struct regcache *regcache, CORE_ADDR pc);
ULONGEST arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
int len,
int byte_order);
CORE_ADDR arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
CORE_ADDR val);
CORE_ADDR arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
CORE_ADDR pc);
int arm_get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
void arm_insert_single_step_breakpoint (struct gdbarch *,
struct address_space *, CORE_ADDR);
int arm_deal_with_atomic_sequence (struct regcache *);
int arm_software_single_step (struct frame_info *);
int arm_is_thumb (struct regcache *regcache);
int arm_frame_is_thumb (struct frame_info *frame);

View File

@ -31,6 +31,8 @@ DEF_VEC_P (const_char_ptr);
DEF_VEC_I (int);
DEF_VEC_I (CORE_ADDR);
extern void free_char_ptr_vec (VEC (char_ptr) *char_ptr_vec);
extern struct cleanup *

View File

@ -44,7 +44,8 @@ aarch64*-*-elf)
aarch64*-*-linux*)
# Target: AArch64 linux
gdb_target_obs="aarch64-tdep.o aarch64-linux-tdep.o aarch64-insn.o \
arm.o arm-tdep.o arm-linux-tdep.o \
arm.o arm-linux.o arm-get-next-pcs.o arm-tdep.o \
arm-linux-tdep.o \
glibc-tdep.o linux-tdep.o solib-svr4.o \
symfile-mem.o linux-record.o"
build_gdbserver=yes
@ -84,31 +85,34 @@ am33_2.0*-*-linux*)
arm*-wince-pe | arm*-*-mingw32ce*)
# Target: ARM based machine running Windows CE (win32)
gdb_target_obs="arm.o arm-tdep.o arm-wince-tdep.o windows-tdep.o"
gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o \
arm-wince-tdep.o windows-tdep.o"
build_gdbserver=yes
;;
arm*-*-linux*)
# Target: ARM based machine running GNU/Linux
gdb_target_obs="arm.o arm-tdep.o arm-linux-tdep.o glibc-tdep.o \
gdb_target_obs="arm.o arm-linux.o arm-get-next-pcs.o arm-tdep.o \
arm-linux-tdep.o glibc-tdep.o \
solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o"
build_gdbserver=yes
;;
arm*-*-netbsd* | arm*-*-knetbsd*-gnu)
# Target: NetBSD/arm
gdb_target_obs="arm.o arm-tdep.o armnbsd-tdep.o solib-svr4.o"
gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o armnbsd-tdep.o \
solib-svr4.o"
;;
arm*-*-openbsd*)
# Target: OpenBSD/arm
gdb_target_obs="arm.o arm-tdep.o armbsd-tdep.o armobsd-tdep.o \
obsd-tdep.o solib-svr4.o"
gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o armbsd-tdep.o \
armobsd-tdep.o obsd-tdep.o solib-svr4.o"
;;
arm*-*-symbianelf*)
# Target: SymbianOS/arm
gdb_target_obs="arm.o arm-tdep.o arm-symbian-tdep.o"
gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o arm-symbian-tdep.o"
;;
arm*-*-*)
# Target: ARM embedded system
gdb_target_obs="arm.o arm-tdep.o"
gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o"
gdb_sim=../sim/arm/libsim.a
;;

View File

@ -1,3 +1,39 @@
2015-12-18 Antoine Tremblay <antoine.tremblay@ericsson.com>
* Makefile.in (SFILES): Append arch/arm-linux.c,
arch/arm-get-next-pcs.c.
(arm-linux.o): New rule.
(arm-get-next-pcs.o): New rule.
* configure.srv (arm*-*-linux*): Add arm-get-next-pcs.o,
arm-linux.o.
* linux-aarch32-low.c (arm_abi_breakpoint): Remove macro. Moved
to linux-aarch32-low.c.
(arm_eabi_breakpoint, arm_breakpoint): Likewise.
(arm_breakpoint_len, thumb_breakpoint): Likewise.
(thumb_breakpoint_len, thumb2_breakpoint): Likewise.
(thumb2_breakpoint_len): Likewise.
(arm_is_thumb_mode): Make non-static.
* linux-aarch32-low.h (arm_abi_breakpoint): New macro. Moved
from linux-aarch32-low.c.
(arm_eabi_breakpoint, arm_breakpoint): Likewise.
(arm_breakpoint_len, thumb_breakpoint): Likewise.
(thumb_breakpoint_len, thumb2_breakpoint): Likewise.
(thumb2_breakpoint_len): Likewise.
(arm_is_thumb_mode): New declaration.
* linux-arm-low.c: Include arch/arm-linux.h
aarch/arm-get-next-pcs.h, sys/syscall.h.
(get_next_pcs_ops): New struct.
(get_next_pcs_addr_bits_remove): New function.
(get_next_pcs_is_thumb): New function.
(get_next_pcs_read_memory_unsigned_integer): Likewise.
(arm_sigreturn_next_pc): Likewise.
(get_next_pcs_syscall_next_pc): Likewise.
(arm_gdbserver_get_next_pcs): Likewise.
(struct linux_target_ops) <arm_gdbserver_get_next_pcs>:
Initialize.
* linux-low.h: Move CORE_ADDR vector definition to gdb_vecs.h.
* server.h: Include gdb_vecs.h.
2015-12-18 Antoine Tremblay <antoine.tremblay@ericsson.com>
* Makefile.in (SFILES): Append common/common-regcache.c.

View File

@ -181,7 +181,8 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
$(srcdir)/common/btrace-common.c \
$(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c \
$(srcdir)/arch/arm.c $(srcdir)/common/common-regcache.c
$(srcdir)/arch/arm.c $(srcdir)/common/common-regcache.c \
$(srcdir)/arch/arm-linux.c $(srcdir)/arch/arm-get-next-pcs.c
DEPFILES = @GDBSERVER_DEPFILES@
@ -592,6 +593,12 @@ common-regcache.o: ../common/common-regcache.c
arm.o: ../arch/arm.c
$(COMPILE) $<
$(POSTCOMPILE)
arm-linux.o: ../arch/arm-linux.c
$(COMPILE) $<
$(POSTCOMPILE)
arm-get-next-pcs.o: ../arch/arm-get-next-pcs.c
$(COMPILE) $<
$(POSTCOMPILE)
# Native object files rules from ../nat

View File

@ -72,6 +72,8 @@ case "${target}" in
srv_tgtobj="$srv_linux_obj linux-arm-low.o"
srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
srv_tgtobj="${srv_tgtobj} arm.o"
srv_tgtobj="${srv_tgtobj} arm-linux.o"
srv_tgtobj="${srv_tgtobj} arm-get-next-pcs.o"
srv_xmlfiles="arm-with-iwmmxt.xml"
srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv2.xml"
srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv3.xml"

View File

@ -137,30 +137,9 @@ struct regs_info regs_info_aarch32 =
&aarch32_regsets_info
};
/* Correct in either endianness. */
#define arm_abi_breakpoint 0xef9f0001UL
/* For new EABI binaries. We recognize it regardless of which ABI
is used for gdbserver, so single threaded debugging should work
OK, but for multi-threaded debugging we only insert the current
ABI's breakpoint instruction. For now at least. */
#define arm_eabi_breakpoint 0xe7f001f0UL
#if (defined __ARM_EABI__ || defined __aarch64__)
static const unsigned long arm_breakpoint = arm_eabi_breakpoint;
#else
static const unsigned long arm_breakpoint = arm_abi_breakpoint;
#endif
#define arm_breakpoint_len 4
static const unsigned short thumb_breakpoint = 0xde01;
#define thumb_breakpoint_len 2
static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
#define thumb2_breakpoint_len 4
/* Returns 1 if the current instruction set is thumb, 0 otherwise. */
static int
int
arm_is_thumb_mode (void)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);

View File

@ -15,6 +15,27 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Correct in either endianness. */
#define arm_abi_breakpoint 0xef9f0001UL
/* For new EABI binaries. We recognize it regardless of which ABI
is used for gdbserver, so single threaded debugging should work
OK, but for multi-threaded debugging we only insert the current
ABI's breakpoint instruction. For now at least. */
#define arm_eabi_breakpoint 0xe7f001f0UL
#ifndef __ARM_EABI__
static const unsigned long arm_breakpoint = arm_abi_breakpoint;
#else
static const unsigned long arm_breakpoint = arm_eabi_breakpoint;
#endif
#define arm_breakpoint_len 4
static const unsigned short thumb_breakpoint = 0xde01;
#define thumb_breakpoint_len 2
static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
#define thumb2_breakpoint_len 4
extern struct regs_info regs_info_aarch32;
void arm_fill_gregset (struct regcache *regcache, void *buf);
@ -31,4 +52,6 @@ int arm_breakpoint_at (CORE_ADDR where);
void initialize_low_arch_aarch32 (void);
void init_registers_arm_with_neon (void);
int arm_is_thumb_mode (void);
extern const struct target_desc *tdesc_arm_with_neon;

View File

@ -19,6 +19,8 @@
#include "server.h"
#include "linux-low.h"
#include "arch/arm.h"
#include "arch/arm-linux.h"
#include "arch/arm-get-next-pcs.h"
#include "linux-aarch32-low.h"
#include <sys/uio.h>
@ -29,6 +31,7 @@
#endif
#include "nat/gdb_ptrace.h"
#include <signal.h>
#include <sys/syscall.h>
/* Defined in auto-generated files. */
void init_registers_arm (void);
@ -136,6 +139,27 @@ static int arm_regmap[] = {
64
};
/* Forward declarations needed for get_next_pcs ops. */
static ULONGEST get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
int len,
int byte_order);
static CORE_ADDR get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
CORE_ADDR val);
static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
CORE_ADDR pc);
static int get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
/* get_next_pcs operations. */
static struct arm_get_next_pcs_ops get_next_pcs_ops = {
get_next_pcs_read_memory_unsigned_integer,
get_next_pcs_syscall_next_pc,
get_next_pcs_addr_bits_remove,
get_next_pcs_is_thumb
};
static int
arm_cannot_store_register (int regno)
{
@ -198,6 +222,13 @@ arm_fill_vfpregset (struct regcache *regcache, void *buf)
arm_fill_vfpregset_num (regcache, buf, num);
}
/* Wrapper of UNMAKE_THUMB_ADDR for get_next_pcs. */
static CORE_ADDR
get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
{
return UNMAKE_THUMB_ADDR (val);
}
static void
arm_store_vfpregset (struct regcache *regcache, const void *buf)
{
@ -233,6 +264,27 @@ arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
supply_register_by_name (regcache, "pc", &newpc);
}
/* Wrapper of arm_is_thumb_mode for get_next_pcs. */
static int
get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
{
return arm_is_thumb_mode ();
}
/* Read memory from the inferiror.
BYTE_ORDER is ignored and there to keep compatiblity with GDB's
read_memory_unsigned_integer. */
static ULONGEST
get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
int len,
int byte_order)
{
ULONGEST res;
(*the_target->read_memory) (memaddr, (unsigned char *) &res, len);
return res;
}
/* Fetch the thread-local storage pointer for libthread_db. */
ps_err_e
@ -717,6 +769,77 @@ arm_prepare_to_resume (struct lwp_info *lwp)
}
}
/* Find the next pc for a sigreturn or rt_sigreturn syscall.
See arm-linux.h for stack layout details. */
static CORE_ADDR
arm_sigreturn_next_pc (struct regcache *regcache, int svc_number)
{
unsigned long sp;
unsigned long sp_data;
/* Offset of PC register. */
int pc_offset = 0;
CORE_ADDR next_pc = 0;
gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn);
collect_register_by_name (regcache, "sp", &sp);
(*the_target->read_memory) (sp, (unsigned char *) &sp_data, 4);
pc_offset = arm_linux_sigreturn_next_pc_offset
(sp, sp_data, svc_number, __NR_sigreturn == svc_number ? 1 : 0);
(*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4);
return next_pc;
}
/* When PC is at a syscall instruction, return the PC of the next
instruction to be executed. */
static CORE_ADDR
get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
{
CORE_ADDR next_pc = 0;
int is_thumb = arm_is_thumb_mode ();
ULONGEST svc_number = 0;
struct regcache *regcache = self->regcache;
if (is_thumb)
{
collect_register (regcache, 7, &svc_number);
next_pc = pc + 2;
}
else
{
unsigned long this_instr;
unsigned long svc_operand;
(*the_target->read_memory) (pc, (unsigned char *) &this_instr, 4);
svc_operand = (0x00ffffff & this_instr);
if (svc_operand) /* OABI. */
{
svc_number = svc_operand - 0x900000;
}
else /* EABI. */
{
collect_register (regcache, 7, &svc_number);
}
next_pc = pc + 4;
}
/* This is a sigreturn or sigreturn_rt syscall. */
if (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn)
{
next_pc = arm_sigreturn_next_pc (regcache, svc_number);
}
/* Addresses for calling Thumb functions have the bit 0 set. */
if (is_thumb)
next_pc = MAKE_THUMB_ADDR (next_pc);
return next_pc;
}
static int
arm_get_hwcap (unsigned long *valp)
@ -806,6 +929,27 @@ arm_arch_setup (void)
have_ptrace_getregset = 0;
}
/* Fetch the next possible PCs after the current instruction executes. */
static VEC (CORE_ADDR) *
arm_gdbserver_get_next_pcs (CORE_ADDR pc, struct regcache *regcache)
{
struct arm_get_next_pcs next_pcs_ctx;
VEC (CORE_ADDR) *next_pcs = NULL;
arm_get_next_pcs_ctor (&next_pcs_ctx,
&get_next_pcs_ops,
/* Byte order is ignored assumed as host. */
0,
0,
(const gdb_byte *) &thumb2_breakpoint,
regcache);
next_pcs = arm_get_next_pcs (&next_pcs_ctx, pc);
return next_pcs;
}
/* Support for hardware single step. */
static int
@ -871,7 +1015,7 @@ struct linux_target_ops the_low_target = {
arm_set_pc,
arm_breakpoint_kind_from_pc,
arm_sw_breakpoint_from_kind,
NULL, /* get_next_pcs */
arm_gdbserver_get_next_pcs,
0,
arm_breakpoint_at,
arm_supports_z_point_type,

View File

@ -124,8 +124,6 @@ struct process_info_private
struct lwp_info;
DEF_VEC_I (CORE_ADDR);
struct linux_target_ops
{
/* Architecture-specific setup. */

View File

@ -123,6 +123,7 @@ extern void discard_queued_stop_replies (ptid_t ptid);
#include "utils.h"
#include "debug.h"
#include "gdb_vecs.h"
/* Maximum number of bytes to read/write at once. The value here
is chosen to fill up a packet (the headers account for the 32). */

View File

@ -1613,8 +1613,6 @@ void iterate_over_symtabs (const char *name,
void *data),
void *data);
DEF_VEC_I (CORE_ADDR);
VEC (CORE_ADDR) *find_pcs_for_symtab_line (struct symtab *symtab, int line,
struct linetable_entry **best_entry);