* [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
@ 2015-09-11 12:13 Antoine Tremblay
2015-09-11 12:13 ` [PATCH 1/7] Fix instruction skipping when using software single step " Antoine Tremblay
` (7 more replies)
0 siblings, 8 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:13 UTC (permalink / raw)
To: gdb-patches
This patch series adds support for tracepoints and software breakpoints on ARM
aarch32 on linux.
Some background :
Some specific issues for ARM aarch32 make it difficult for GDBServer to enable
these features namely : it has no hardware single step support and the
breakpoint data and size depends on the current instruction set used, thumb,
thumb2 or arm.
So GDBServer needs to know how to set a breakpoint of different size based on
the current instruction and registers and it needs to be able to do software
single stepping.
This patchset teaches this to GDBServer and enables support for tracepoints and
software breakpoints.
The patchset :
This patchset applies over :
https://sourceware.org/ml/gdb-patches/2015-09/msg00004.html
to avoid an immediate conflict...
The first 4 patches do some refactoring and some fixes to prepare for the main
patch : Add support for software single step on ARM aarch32-linux
This patch contains most of the work and resolves the main issues.
Subsequent patches add the functionality of tracepoints and software breakpoints
themselves.
This patchset was tested on Ubuntu 14.04 on ARMv7 and x86 no regressions found.
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 2/7] Move some integer operations to common.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
2015-09-11 12:13 ` [PATCH 1/7] Fix instruction skipping when using software single step " Antoine Tremblay
@ 2015-09-11 12:13 ` Antoine Tremblay
2015-09-11 14:24 ` Gary Benson
2015-09-11 12:14 ` [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (5 subsequent siblings)
7 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:13 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch is in preparation for sharing code between GDB and GDBServer to
enable software single stepping on ARM aarch32-linux.
It moves multiple functions related to extracting or storing a value based on
its endianness from findvar.c in gdb to a new file called int-utils.c in
common.
Definitions of these functions are also moved to defs.h to common-defs.h.
gdb/ChangeLog:
* Makefile.in: Add int-utils.o.
* common/common-defs.h: New functions defs from defs.h.
* common/int-utils.c: New file.
* common/int-utils.h: New file.
* defs.h: Move functions defs to common-defs.h.
* findvar.c (extract_signed_integer): Move to int-utils.c.
(extract_unsigned_integer): Likewise.
(extract_long_unsigned_integer): Likewise.
(store_signed_integer): Likewise.
(store_unsigned_integer): Likewise.
---
gdb/Makefile.in | 9 ++-
gdb/common/common-defs.h | 1 +
gdb/common/int-utils.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++
gdb/common/int-utils.h | 45 +++++++++++
gdb/defs.h | 16 ----
gdb/findvar.c | 176 -----------------------------------------
6 files changed, 252 insertions(+), 194 deletions(-)
create mode 100644 gdb/common/int-utils.c
create mode 100644 gdb/common/int-utils.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0d7cf97..e20c5a6 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -854,6 +854,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
infcall.c \
infcmd.c inflow.c infrun.c \
inline-frame.c \
+ common/int-utils.c \
interps.c \
jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c jv-varobj.c \
language.c linespec.c location.c minidebug.c \
@@ -985,7 +986,7 @@ i386-linux-nat.h common/common-defs.h common/errors.h common/common-types.h \
common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
common/common-exceptions.h target/target.h common/symbol.h \
common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
-common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
+common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h common/int-utils.h \
nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -1084,7 +1085,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
format.o registry.o btrace.o record-btrace.o waitstatus.o \
print-utils.o rsp-low.o errors.o common-debug.o debug.o \
- common-exceptions.o btrace-common.o fileio.o \
+ common-exceptions.o btrace-common.o fileio.o int-utils.o \
$(SUBDIR_GCC_COMPILE_OBS)
TSOBS = inflow.o
@@ -2265,6 +2266,10 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
fileio.o: ${srcdir}/common/fileio.c
$(COMPILE) $(srcdir)/common/fileio.c
$(POSTCOMPILE)
+int-utils.o: ${srcdir}/common/int-utils.c
+ $(COMPILE) $(srcdir)/common/int-utils.c
+ $(POSTCOMPILE)
+
#
# gdb/target/ dependencies
#
diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
index 2be0d7d..cb79234 100644
--- a/gdb/common/common-defs.h
+++ b/gdb/common/common-defs.h
@@ -49,6 +49,7 @@
#include "common-debug.h"
#include "cleanups.h"
#include "common-exceptions.h"
+#include "int-utils.h"
#ifdef __cplusplus
# define EXTERN_C extern "C"
diff --git a/gdb/common/int-utils.c b/gdb/common/int-utils.c
new file mode 100644
index 0000000..57d0dba
--- /dev/null
+++ b/gdb/common/int-utils.c
@@ -0,0 +1,199 @@
+/* Shared utility routines for integer endianness manipulations.
+ Copyright (C) 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"
+
+/* Basic byte-swapping routines. All 'extract' functions return a
+ host-format integer from a target-format integer at ADDR which is
+ LEN bytes long. */
+
+LONGEST
+extract_signed_integer (const gdb_byte *addr, int len,
+ enum bfd_endian byte_order)
+{
+ LONGEST retval;
+ const unsigned char *p;
+ const unsigned char *startaddr = addr;
+ const unsigned char *endaddr = startaddr + len;
+
+ if (len > (int) sizeof (LONGEST))
+ error (_("\
+That operation is not available on integers of more than %d bytes."),
+ (int) sizeof (LONGEST));
+
+ /* Start at the most significant end of the integer, and work towards
+ the least significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ p = startaddr;
+ /* Do the sign extension once at the start. */
+ retval = ((LONGEST) * p ^ 0x80) - 0x80;
+ for (++p; p < endaddr; ++p)
+ retval = (retval << 8) | *p;
+ }
+ else
+ {
+ p = endaddr - 1;
+ /* Do the sign extension once at the start. */
+ retval = ((LONGEST) * p ^ 0x80) - 0x80;
+ for (--p; p >= startaddr; --p)
+ retval = (retval << 8) | *p;
+ }
+ return retval;
+}
+
+ULONGEST
+extract_unsigned_integer (const gdb_byte *addr, int len,
+ enum bfd_endian byte_order)
+{
+ ULONGEST retval;
+ const unsigned char *p;
+ const unsigned char *startaddr = addr;
+ const unsigned char *endaddr = startaddr + len;
+
+ if (len > (int) sizeof (ULONGEST))
+ error (_("\
+That operation is not available on integers of more than %d bytes."),
+ (int) sizeof (ULONGEST));
+
+ /* Start at the most significant end of the integer, and work towards
+ the least significant. */
+ retval = 0;
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = startaddr; p < endaddr; ++p)
+ retval = (retval << 8) | *p;
+ }
+ else
+ {
+ for (p = endaddr - 1; p >= startaddr; --p)
+ retval = (retval << 8) | *p;
+ }
+ return retval;
+}
+
+/* Sometimes a long long unsigned integer can be extracted as a
+ LONGEST value. This is done so that we can print these values
+ better. If this integer can be converted to a LONGEST, this
+ function returns 1 and sets *PVAL. Otherwise it returns 0. */
+
+int
+extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
+ enum bfd_endian byte_order, LONGEST *pval)
+{
+ const gdb_byte *p;
+ const gdb_byte *first_addr;
+ int len;
+
+ len = orig_len;
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = addr;
+ len > (int) sizeof (LONGEST) && p < addr + orig_len;
+ p++)
+ {
+ if (*p == 0)
+ len--;
+ else
+ break;
+ }
+ first_addr = p;
+ }
+ else
+ {
+ first_addr = addr;
+ for (p = addr + orig_len - 1;
+ len > (int) sizeof (LONGEST) && p >= addr;
+ p--)
+ {
+ if (*p == 0)
+ len--;
+ else
+ break;
+ }
+ }
+
+ if (len <= (int) sizeof (LONGEST))
+ {
+ *pval = (LONGEST) extract_unsigned_integer (first_addr,
+ sizeof (LONGEST),
+ byte_order);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* All 'store' functions accept a host-format integer and store a
+ target-format integer at ADDR which is LEN bytes long. */
+
+void
+store_signed_integer (gdb_byte *addr, int len,
+ enum bfd_endian byte_order, LONGEST val)
+{
+ gdb_byte *p;
+ gdb_byte *startaddr = addr;
+ gdb_byte *endaddr = startaddr + len;
+
+ /* Start at the least significant end of the integer, and work towards
+ the most significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = endaddr - 1; p >= startaddr; --p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+ else
+ {
+ for (p = startaddr; p < endaddr; ++p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+}
+
+void
+store_unsigned_integer (gdb_byte *addr, int len,
+ enum bfd_endian byte_order, ULONGEST val)
+{
+ unsigned char *p;
+ unsigned char *startaddr = (unsigned char *) addr;
+ unsigned char *endaddr = startaddr + len;
+
+ /* Start at the least significant end of the integer, and work towards
+ the most significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = endaddr - 1; p >= startaddr; --p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+ else
+ {
+ for (p = startaddr; p < endaddr; ++p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+}
diff --git a/gdb/common/int-utils.h b/gdb/common/int-utils.h
new file mode 100644
index 0000000..c170348
--- /dev/null
+++ b/gdb/common/int-utils.h
@@ -0,0 +1,45 @@
+/* Shared utility routines for integer endianness manipulations.
+ Copyright (C) 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 INT_UTILS_H
+#define INT_UTILS_H 1
+
+#ifdef GDBSERVER
+/* Allow this enum without requiring bfd in gdbserver. */
+enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
+#else
+#include "bfd.h"
+#endif
+
+extern LONGEST extract_signed_integer (const gdb_byte *, int,
+ enum bfd_endian);
+
+extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
+ enum bfd_endian);
+
+extern int extract_long_unsigned_integer (const gdb_byte *, int,
+ enum bfd_endian, LONGEST *);
+
+extern void store_signed_integer (gdb_byte *, int,
+ enum bfd_endian, LONGEST);
+
+extern void store_unsigned_integer (gdb_byte *, int,
+ enum bfd_endian, ULONGEST);
+
+#endif /* INT_UTILS_H */
diff --git a/gdb/defs.h b/gdb/defs.h
index 03f7e8a..e292977 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -596,28 +596,12 @@ enum { MAX_REGISTER_SIZE = 64 };
/* In findvar.c. */
-extern LONGEST extract_signed_integer (const gdb_byte *, int,
- enum bfd_endian);
-
-extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
- enum bfd_endian);
-
-extern int extract_long_unsigned_integer (const gdb_byte *, int,
- enum bfd_endian, LONGEST *);
-
extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
struct type *type);
-extern void store_signed_integer (gdb_byte *, int,
- enum bfd_endian, LONGEST);
-
-extern void store_unsigned_integer (gdb_byte *, int,
- enum bfd_endian, ULONGEST);
-
extern void store_typed_address (gdb_byte *buf, struct type *type,
CORE_ADDR addr);
-\f
/* From valops.c */
extern int watchdog;
diff --git a/gdb/findvar.c b/gdb/findvar.c
index 1c077f7..2299ca4 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -46,124 +46,6 @@
you lose
#endif
-LONGEST
-extract_signed_integer (const gdb_byte *addr, int len,
- enum bfd_endian byte_order)
-{
- LONGEST retval;
- const unsigned char *p;
- const unsigned char *startaddr = addr;
- const unsigned char *endaddr = startaddr + len;
-
- if (len > (int) sizeof (LONGEST))
- error (_("\
-That operation is not available on integers of more than %d bytes."),
- (int) sizeof (LONGEST));
-
- /* Start at the most significant end of the integer, and work towards
- the least significant. */
- if (byte_order == BFD_ENDIAN_BIG)
- {
- p = startaddr;
- /* Do the sign extension once at the start. */
- retval = ((LONGEST) * p ^ 0x80) - 0x80;
- for (++p; p < endaddr; ++p)
- retval = (retval << 8) | *p;
- }
- else
- {
- p = endaddr - 1;
- /* Do the sign extension once at the start. */
- retval = ((LONGEST) * p ^ 0x80) - 0x80;
- for (--p; p >= startaddr; --p)
- retval = (retval << 8) | *p;
- }
- return retval;
-}
-
-ULONGEST
-extract_unsigned_integer (const gdb_byte *addr, int len,
- enum bfd_endian byte_order)
-{
- ULONGEST retval;
- const unsigned char *p;
- const unsigned char *startaddr = addr;
- const unsigned char *endaddr = startaddr + len;
-
- if (len > (int) sizeof (ULONGEST))
- error (_("\
-That operation is not available on integers of more than %d bytes."),
- (int) sizeof (ULONGEST));
-
- /* Start at the most significant end of the integer, and work towards
- the least significant. */
- retval = 0;
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = startaddr; p < endaddr; ++p)
- retval = (retval << 8) | *p;
- }
- else
- {
- for (p = endaddr - 1; p >= startaddr; --p)
- retval = (retval << 8) | *p;
- }
- return retval;
-}
-
-/* Sometimes a long long unsigned integer can be extracted as a
- LONGEST value. This is done so that we can print these values
- better. If this integer can be converted to a LONGEST, this
- function returns 1 and sets *PVAL. Otherwise it returns 0. */
-
-int
-extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
- enum bfd_endian byte_order, LONGEST *pval)
-{
- const gdb_byte *p;
- const gdb_byte *first_addr;
- int len;
-
- len = orig_len;
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = addr;
- len > (int) sizeof (LONGEST) && p < addr + orig_len;
- p++)
- {
- if (*p == 0)
- len--;
- else
- break;
- }
- first_addr = p;
- }
- else
- {
- first_addr = addr;
- for (p = addr + orig_len - 1;
- len > (int) sizeof (LONGEST) && p >= addr;
- p--)
- {
- if (*p == 0)
- len--;
- else
- break;
- }
- }
-
- if (len <= (int) sizeof (LONGEST))
- {
- *pval = (LONGEST) extract_unsigned_integer (first_addr,
- sizeof (LONGEST),
- byte_order);
- return 1;
- }
-
- return 0;
-}
-
-
/* Treat the bytes at BUF as a pointer of type TYPE, and return the
address it represents. */
CORE_ADDR
@@ -178,64 +60,6 @@ extract_typed_address (const gdb_byte *buf, struct type *type)
return gdbarch_pointer_to_address (get_type_arch (type), type, buf);
}
-/* All 'store' functions accept a host-format integer and store a
- target-format integer at ADDR which is LEN bytes long. */
-
-void
-store_signed_integer (gdb_byte *addr, int len,
- enum bfd_endian byte_order, LONGEST val)
-{
- gdb_byte *p;
- gdb_byte *startaddr = addr;
- gdb_byte *endaddr = startaddr + len;
-
- /* Start at the least significant end of the integer, and work towards
- the most significant. */
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = endaddr - 1; p >= startaddr; --p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
- else
- {
- for (p = startaddr; p < endaddr; ++p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
-}
-
-void
-store_unsigned_integer (gdb_byte *addr, int len,
- enum bfd_endian byte_order, ULONGEST val)
-{
- unsigned char *p;
- unsigned char *startaddr = (unsigned char *) addr;
- unsigned char *endaddr = startaddr + len;
-
- /* Start at the least significant end of the integer, and work towards
- the most significant. */
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = endaddr - 1; p >= startaddr; --p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
- else
- {
- for (p = startaddr; p < endaddr; ++p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
-}
/* Store the address ADDR as a pointer of type TYPE at BUF, in target
form. */
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 1/7] Fix instruction skipping when using software single step in GDBServer
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
@ 2015-09-11 12:13 ` Antoine Tremblay
2015-09-11 12:13 ` [PATCH 2/7] Move some integer operations to common Antoine Tremblay
` (6 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:13 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
Without this patch, when doing a software single step, with for example
a conditional breakpoint, gdbserver would wrongly avance the pc of
breakpoint_len and skips an instruction.
This is due to gdbserver assuming that it's hardware single stepping.
When it resumes from the breakpoint address it expects the trap to be
caused by ptrace and if it's rather caused by a software breakpoint
it assumes this is a permanent breakpoint and that it needs to skip
over it.
However when software single stepping, this breakpoint is legitimate as
it's the reinsert breakpoint gdbserver has put in place to break at
the next instruction. Thus gdbserver wrongly advances the pc and skips
an instruction.
This patch fixes this behavior so that gdbserver checks if it is a
reinsert breakpoint from software single stepping. If it is it won't
advance the pc. And if there's no reinsert breakpoint there we assume
then that it's a permanent breakpoint and advance the pc.
Here's a commented log of what would happen before and after the fix on
gdbserver :
/* Here there is a conditional breakpoint at 0x10428 that needs to be
stepped over. */
Need step over [LWP 11204]? yes, found breakpoint at 0x10428
...
/* e7f001f0 is a breakpoint instruction on arm
Here gdbserver writes the software breakpoint we would like to hit
*/
Writing e7f001f0 to 0x0001042c in process 11204
...
Resuming lwp 11220 (continue, signal 0, stop not expected)
pending reinsert at 0x10428
stop pc is 00010428
continue from pc 0x10428
...
/* Here gdbserver hit the software breakpoint that was in place
for the step over */
stop pc is 0001042c
pc is 0x1042c
step-over for LWP 11220.11220 executed software breakpoint
Finished step over.
Could not find fast tracepoint jump at 0x10428 in list (reinserting).
/* Here gdbserver writes back the original instruction */
Writing e50b3008 to 0x0001042c in process 11220
Step-over finished.
Need step over [LWP 11220]? No
/* Here because gdbserver assumes this is a permenant breakpoint it advances
the pc of breakpoint_len, in this case 4 bytes, so we have just skipped
the instruction that was written back here :
Writing e50b3008 to 0x0001042c in process 11220
*/
stop pc is 00010430
pc is 0x10430
Need step over [LWP 11220]? No, no breakpoint found at 0x10430
Proceeding, no step-over needed
proceed_one_lwp: lwp 11220
stop pc is 00010430
This patch fixes this situation and we get the right behavior :
Writing e50b3008 to 0x0001042c in process 11245
Hit a gdbserver breakpoint.
Hit a gdbserver breakpoint.
Step-over finished.
proceeding all threads.
Need step over [LWP 11245]? No
stop pc is 0001042c
pc is 0x1042c
Need step over [LWP 11245]? No, no breakpoint found at 0x1042c
Proceeding, no step-over needed
proceed_one_lwp: lwp 11245
stop pc is 0001042c
pc is 0x1042c
Resuming lwp 11245 (continue, signal 0, stop not expected)
stop pc is 0001042c
continue from pc 0x1042c
It also works if the value at 0x0001042c is a permanent breakpoint.
If so gdbserver will finish the step over, remove the reinserted breakpoint,
resume at that location and on the next SIGTRAP gdbserver will trigger
the advance PC condition as reinsert_breakpoint_inserted_here will be false.
I also tested this against bp-permanent.exp on arm (with a work in progress
software single step patchset) without any regressions.
It's also tested against x86 bp-permanent.exp without any regression.
So both software and hardware single step are tested.
gdb/gdbserver/ChangeLog:
* linux-low.c (linux_wait_1): Fix pc advance condition.
* mem-break.c (reinsert_breakpoint_inserted_here): New function.
* mem-break.h: Add reinsert_breakpoint_inserted_here.
---
gdb/gdbserver/linux-low.c | 23 +++++++++++++++--------
gdb/gdbserver/mem-break.c | 17 +++++++++++++++++
gdb/gdbserver/mem-break.h | 4 ++++
3 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 9ae0522..7d979f8 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2913,14 +2913,21 @@ linux_wait_1 (ptid_t ptid,
return ptid_of (current_thread);
}
- /* If step-over executes a breakpoint instruction, it means a
- gdb/gdbserver breakpoint had been planted on top of a permanent
- breakpoint. The PC has been adjusted by
- check_stopped_by_breakpoint to point at the breakpoint address.
- Advance the PC manually past the breakpoint, otherwise the
- program would keep trapping the permanent breakpoint forever. */
- if (!ptid_equal (step_over_bkpt, null_ptid)
- && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+ /* If step-over executes a breakpoint instruction, in the case of a
+ hardware single step it means a gdb/gdbserver breakpoint had been
+ planted on top of a permanent breakpoint, in the case of a software
+ single step it may just mean that gdbserver hit the reinsert breakpoint.
+ The PC has been adjusted by check_stopped_by_breakpoint to point at
+ the breakpoint address.
+ So in the case of the hardware single step advance the PC manually
+ past the breakpoint and in the case of software single step advance only
+ if it's not the reinsert_breakpoint we are hitting.
+ This avoids that a program would keep trapping a permanent breakpoint
+ forever. */
+ if ((!ptid_equal (step_over_bkpt, null_ptid)
+ && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT) &&
+ (event_child->stepping ||
+ !reinsert_breakpoint_inserted_here (event_child->stop_pc)))
{
unsigned int increment_pc = the_low_target.breakpoint_len;
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 9356741..fb43768 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -1660,6 +1660,23 @@ hardware_breakpoint_inserted_here (CORE_ADDR addr)
return 0;
}
+/* See mem-break.h. */
+
+int
+reinsert_breakpoint_inserted_here (CORE_ADDR addr)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ if (bp->type == reinsert_breakpoint
+ && bp->raw->pc == addr
+ && bp->raw->inserted)
+ return 1;
+
+ return 0;
+}
+
static int
validate_inserted_breakpoint (struct raw_breakpoint *bp)
{
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index b5a3208..b1bb91a 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -100,6 +100,10 @@ int software_breakpoint_inserted_here (CORE_ADDR addr);
int hardware_breakpoint_inserted_here (CORE_ADDR addr);
+/* Returns TRUE if there's any reinsert breakpoint at ADDR. */
+
+int reinsert_breakpoint_inserted_here (CORE_ADDR addr);
+
/* Clear all breakpoint conditions and commands associated with a
breakpoint. */
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 4/7] Make breakpoint and breakpoint_len local variables in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (5 preceding siblings ...)
2015-09-11 12:14 ` [PATCH 6/7] Support conditional breakpoints on targets that can software single step " Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-14 10:33 ` [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux " Yao Qi
7 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch is in preparation for software single stepping on ARM aarch32-linux.
It refactors the breakpoint and breakpoint_len global variables to be local
so that multiple types of breakpoints can be used for an arch.
One important implementation detail is the introduction of the pcfull field
in struct raw_breakpoint.
In order to be able to reinsert a breakpoint we need to remember what were the
flags encoded in the PC, however since functions that compare the program pc
to the breakpoint pc expect an unencoded memory address, we can't put the
encoded value directly in the pc field.
So this patch introduces the fullpc field that contains the flags encoded
in the pc so that we can properly reinsert a breakpoint that has its type
information encoded in the pc. fullpc shall only be used when
inserting/removing/reinserting a breakpoint, all other breakpoint->pc
references can remain the same.
Note this is for software breakpoints only, when using hardware breakpoints
then fullpc is not set or used.
No regressions on Ubuntu 14.04 on ARMv7 and x86.
gdbserver/ChangeLog:
* linux-low.c (initialize_low): Remove
breakpoint_data initialization.
* mem-break.c (struct raw_breakpoint): Add pcfull.
(insert_memory_breakpoint): Call breakpoint_from_pc.
(remove_memory_breakpoint): Likewise.
(set_raw_breakpoint_at): Likewise.
(set_breakpoint_at): Set default breakpoint size to 0.
(set_breakpoint_data): Remove.
(validate_inserted_breakpoint): Call breakpoint_from_pc.
(check_mem_read): Call breakpoint_from_pc.
(check_mem_write): Call breakpoint_from_pc.
(clone_one_breakpoint): Copy pcfull field.
---
gdb/gdbserver/linux-low.c | 6 ----
gdb/gdbserver/mem-break.c | 76 ++++++++++++++++++++++++++++++++++++-----------
2 files changed, 58 insertions(+), 24 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index ef6075b..402db9c 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -6997,16 +6997,10 @@ void
initialize_low (void)
{
struct sigaction sigchld_action;
- int breakpoint_len = 0;
- const unsigned char *breakpoint = NULL;
memset (&sigchld_action, 0, sizeof (sigchld_action));
set_target_ops (&linux_target_ops);
- breakpoint = the_target->breakpoint_from_pc (NULL, &breakpoint_len);
-
- set_breakpoint_data (breakpoint,
- breakpoint_len);
linux_init_signals ();
linux_ptrace_init_warnings ();
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index fb43768..1a87d01 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -21,8 +21,6 @@
#include "server.h"
#include "regcache.h"
#include "ax.h"
-const unsigned char *breakpoint_data;
-int breakpoint_len;
#define MAX_BREAKPOINT_LEN 8
@@ -100,6 +98,10 @@ struct raw_breakpoint
breakpoint for a given PC. */
CORE_ADDR pc;
+ /* The breakpoint's insertion address, possibly with flags encoded in the pc
+ (e.g. the instruction mode on ARM). */
+ CORE_ADDR pcfull;
+
/* The breakpoint's size. */
int size;
@@ -300,6 +302,12 @@ insert_memory_breakpoint (struct raw_breakpoint *bp)
{
unsigned char buf[MAX_BREAKPOINT_LEN];
int err;
+ const unsigned char *breakpoint_data;
+ int breakpoint_len;
+ CORE_ADDR pc;
+
+ pc = bp->pcfull;
+ breakpoint_data = the_target->breakpoint_from_pc (&pc, &breakpoint_len);
if (breakpoint_data == NULL)
return 1;
@@ -349,6 +357,11 @@ remove_memory_breakpoint (struct raw_breakpoint *bp)
{
unsigned char buf[MAX_BREAKPOINT_LEN];
int err;
+ int breakpoint_len;
+ CORE_ADDR pc;
+
+ pc = bp->pcfull;
+ the_target->breakpoint_from_pc (&pc, &breakpoint_len);
/* Since there can be trap breakpoints inserted in the same address
range, we use `write_inferior_memory', which takes care of
@@ -375,15 +388,27 @@ remove_memory_breakpoint (struct raw_breakpoint *bp)
returns NULL and writes the error code to *ERR. */
static struct raw_breakpoint *
-set_raw_breakpoint_at (enum raw_bkpt_type type, CORE_ADDR where, int size,
+set_raw_breakpoint_at (enum raw_bkpt_type type, const CORE_ADDR where, int size,
int *err)
{
struct process_info *proc = current_process ();
struct raw_breakpoint *bp;
+ CORE_ADDR pc;
+ int breakpoint_len;
+
+ /* pc could be modified by breakpoint_from_pc, use the modified
+ version to find breakpoints and use the full where pc for
+ insert_point so that arch specific data can be passed. */
+ pc = where;
+
+ the_target->breakpoint_from_pc (&pc, &breakpoint_len);
+
+ if (size == 0)
+ size = breakpoint_len;
if (type == raw_bkpt_type_sw || type == raw_bkpt_type_hw)
{
- bp = find_enabled_raw_code_breakpoint_at (where, type);
+ bp = find_enabled_raw_code_breakpoint_at (pc, type);
if (bp != NULL && bp->size != size)
{
/* A different size than previously seen. The previous
@@ -396,7 +421,7 @@ set_raw_breakpoint_at (enum raw_bkpt_type type, CORE_ADDR where, int size,
}
}
else
- bp = find_raw_breakpoint_at (where, type, size);
+ bp = find_raw_breakpoint_at (pc, type, size);
if (bp != NULL)
{
@@ -405,7 +430,8 @@ set_raw_breakpoint_at (enum raw_bkpt_type type, CORE_ADDR where, int size,
}
bp = XCNEW (struct raw_breakpoint);
- bp->pc = where;
+ bp->pc = pc;
+ bp->pcfull = where;
bp->size = size;
bp->refcount = 1;
bp->raw_type = type;
@@ -774,8 +800,9 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
{
int err_ignored;
+ /* default breakpoint_len will be initialized downstream. */
return set_breakpoint (other_breakpoint, raw_bkpt_type_sw,
- where, breakpoint_len, handler,
+ where, 0, handler,
&err_ignored);
}
@@ -1588,13 +1615,6 @@ check_breakpoints (CORE_ADDR stop_pc)
}
}
-void
-set_breakpoint_data (const unsigned char *bp_data, int bp_len)
-{
- breakpoint_data = bp_data;
- breakpoint_len = bp_len;
-}
-
int
breakpoint_here (CORE_ADDR addr)
{
@@ -1682,6 +1702,13 @@ validate_inserted_breakpoint (struct raw_breakpoint *bp)
{
unsigned char *buf;
int err;
+ const unsigned char *breakpoint_data;
+ int breakpoint_len;
+ CORE_ADDR raw_pc;
+
+ raw_pc = bp->pcfull;
+
+ breakpoint_data = the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
gdb_assert (bp->inserted);
gdb_assert (bp->raw_type == raw_bkpt_type_sw);
@@ -1779,10 +1806,15 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
for (; bp != NULL; bp = bp->next)
{
- CORE_ADDR bp_end = bp->pc + breakpoint_len;
- CORE_ADDR start, end;
+ int breakpoint_len;
+ CORE_ADDR raw_pc;
+ CORE_ADDR bp_end, start, end;
int copy_offset, copy_len, buf_offset;
+ raw_pc = bp->pcfull;
+ the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
+ bp_end = bp->pc + breakpoint_len;
+
if (bp->raw_type != raw_bkpt_type_sw)
continue;
@@ -1868,10 +1900,17 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf,
for (; bp != NULL; bp = bp->next)
{
- CORE_ADDR bp_end = bp->pc + breakpoint_len;
- CORE_ADDR start, end;
+ int breakpoint_len;
+ const unsigned char *breakpoint_data;
+ CORE_ADDR raw_pc;
+ CORE_ADDR bp_end, start, end;
int copy_offset, copy_len, buf_offset;
+ raw_pc = bp->pcfull;
+ breakpoint_data =
+ the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
+ bp_end = bp->pc + breakpoint_len;
+
if (bp->raw_type != raw_bkpt_type_sw)
continue;
@@ -1980,6 +2019,7 @@ clone_one_breakpoint (const struct breakpoint *src)
dest_raw->raw_type = src->raw->raw_type;
dest_raw->refcount = src->raw->refcount;
dest_raw->pc = src->raw->pc;
+ dest_raw->pcfull = src->raw->pcfull;
dest_raw->size = src->raw->size;
memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
dest_raw->inserted = src->raw->inserted;
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 3/7] Support multiple breakpoint types per target in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (2 preceding siblings ...)
2015-09-11 12:14 ` [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-11 12:14 ` [PATCH 5/7] Add support for software single step on ARM aarch32-linux " Antoine Tremblay
` (3 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch is in preparation for software single stepping on ARM
aarch32-linux. It refactors breakpoint and breakpoint_len into
breakpoint_from_pc to prepare the case where we have multiple types of
breakpoints.
breakpoint_from_pc returns the breakpoint for this PC as a string of bytes,
the length of the breakpoint and ajusts the PC to the real memory location in
case a flag was present in the PC.
No regressions, tested on Ubuntu 14.04 on ARMv7 and x86
Also since the target_ops have been changed compilation was tested on all
affected archs namely : aarch64, arm, bfin, cris, crisv32, m32r, m68k, mips, nios2,
ppc, s390, sh, sparc, tic6x, tile, x86, xtensa
gdbserver/ChangeLog:
* linux-aarch64-low.c
(aarch64_breakpoint_from_pc): New function.
* linux-arm-low.c (arm_breakpoint_from_pc): Likewise.
* linux-bfin-low.c (bfin_breakpoint_from_pc): Likewise.
* linux-cris-low.c (cris_breakpoint_from_pc): Likewise.
* linux-crisv32-low.c: (cris_breakpoint_from_pc): Likewise.
* linux-low.c (linux_wait_1): Add call to breakpoint_from_pc.
(linux_breakpoint_from_pc): New function.
(initialize_low): Add call to breakpoint_from_pc.
* linux-low.h: Add breakpoint_from_pc operation.
* linux-m32r-low.c (m32r_breakpoint_from_pc): New function.
* linux-m68k-low.c (m68k_breakpoint_from_pc): Likewise.
* linux-mips-low.c (mips_breakpoint_from_pc): Likewise.
* linux-nios2-low.c (nios2_breakpoint_from_pc): Likewise.
* linux-ppc-low.c (ppc_breakpoint_from_pc): Likewise.
* linux-s390-low.c (s390_breakpoint_from_pc): Likewise.
* linux-sh-low.c (sh_breakpoint_from_pc): Likewise.
* linux-sparc-low.c (sparc_breakpoint_from_pc): Likewise.
* linux-tic6x-low.c (tic6x_breakpoint_from_pc): Likewise.
* linux-tile-low.c (tile_breakpoint_from_pc): Likewise.
* linux-x86-low.c (x86_breakpoint_from_pc): Likewise.
* linux-xtensa-low.c(xtensa_breakpoint_from_pc): Likewise.
* target.h (struct target_ops): Add breakpoint_from_pc operation.
* win32-arm-low.c (arm_wince_breakpoint_from_pc): New Function.
* win32-i386-low.c(i386_win32_breakpoint_from_pc): Likewise.
---
gdb/gdbserver/linux-aarch64-low.c | 10 ++++++++--
gdb/gdbserver/linux-arm-low.c | 28 ++++++++++++++++------------
gdb/gdbserver/linux-bfin-low.c | 10 ++++++++--
gdb/gdbserver/linux-cris-low.c | 12 +++++++++---
gdb/gdbserver/linux-crisv32-low.c | 10 ++++++++--
gdb/gdbserver/linux-low.c | 28 +++++++++++++++++++++++++---
gdb/gdbserver/linux-low.h | 9 +++++++--
gdb/gdbserver/linux-m32r-low.c | 10 ++++++++--
gdb/gdbserver/linux-m68k-low.c | 10 ++++++++--
gdb/gdbserver/linux-mips-low.c | 10 ++++++++--
gdb/gdbserver/linux-nios2-low.c | 22 +++++++++++++---------
gdb/gdbserver/linux-ppc-low.c | 10 ++++++++--
gdb/gdbserver/linux-s390-low.c | 10 ++++++++--
gdb/gdbserver/linux-sh-low.c | 10 ++++++++--
gdb/gdbserver/linux-sparc-low.c | 9 +++++++--
gdb/gdbserver/linux-tic6x-low.c | 13 +++++++++----
gdb/gdbserver/linux-tile-low.c | 10 ++++++++--
gdb/gdbserver/linux-x86-low.c | 10 ++++++++--
gdb/gdbserver/linux-xtensa-low.c | 10 ++++++++--
gdb/gdbserver/target.h | 5 +++++
gdb/gdbserver/win32-arm-low.c | 10 ++++++++--
gdb/gdbserver/win32-i386-low.c | 10 ++++++++--
22 files changed, 203 insertions(+), 63 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index aebf1e3..0b2c5f1 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -200,6 +200,13 @@ aarch64_set_pc (struct regcache *regcache, CORE_ADDR pc)
(aarch64_default_breakpoint). */
static const gdb_byte aarch64_breakpoint[] = {0x00, 0x00, 0x20, 0xd4};
+static const unsigned char *
+aarch64_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = aarch64_breakpoint_len;
+ return (const unsigned char *) &aarch64_breakpoint;
+}
+
/* Implementation of linux_target_ops method "breakpoint_at". */
static int
@@ -569,8 +576,7 @@ struct linux_target_ops the_low_target =
NULL, /* fetch_register */
aarch64_get_pc,
aarch64_set_pc,
- (const unsigned char *) &aarch64_breakpoint,
- aarch64_breakpoint_len,
+ aarch64_breakpoint_from_pc,
NULL, /* breakpoint_reinsert_addr */
0, /* decr_pc_after_break */
aarch64_breakpoint_at,
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index a277bb6..367c704 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -913,6 +913,21 @@ arm_regs_info (void)
return ®s_info_arm;
}
+static const unsigned char *
+arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = arm_breakpoint_len;
+ /* Define an ARM-mode breakpoint; we only set breakpoints in the C
+ library, which is most likely to be ARM. If the kernel supports
+ clone events, we will never insert a breakpoint, so even a Thumb
+ C library will work; so will mixing EABI/non-EABI gdbserver and
+ application. */
+#ifndef __ARM_EABI__
+ return (const unsigned char *) &arm_breakpoint;
+#else
+ return (const unsigned char *) &arm_eabi_breakpoint;
+#endif
+}
struct linux_target_ops the_low_target = {
arm_arch_setup,
arm_regs_info,
@@ -921,18 +936,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
arm_get_pc,
arm_set_pc,
-
- /* Define an ARM-mode breakpoint; we only set breakpoints in the C
- library, which is most likely to be ARM. If the kernel supports
- clone events, we will never insert a breakpoint, so even a Thumb
- C library will work; so will mixing EABI/non-EABI gdbserver and
- application. */
-#ifndef __ARM_EABI__
- (const unsigned char *) &arm_breakpoint,
-#else
- (const unsigned char *) &arm_eabi_breakpoint,
-#endif
- arm_breakpoint_len,
+ arm_breakpoint_from_pc,
arm_reinsert_addr,
0,
arm_breakpoint_at,
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 4002f22..1c0e1e9 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -75,6 +75,13 @@ bfin_set_pc (struct regcache *regcache, CORE_ADDR pc)
#define bfin_breakpoint_len 2
static const unsigned char bfin_breakpoint[bfin_breakpoint_len] = {0xa1, 0x00};
+static const unsigned char *
+bfin_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = bfin_breakpoint_len;
+ return (const unsigned char *) &bfin_breakpoint;
+}
+
static int
bfin_breakpoint_at (CORE_ADDR where)
{
@@ -122,8 +129,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
bfin_get_pc,
bfin_set_pc,
- bfin_breakpoint,
- bfin_breakpoint_len,
+ bfin_breakpoint_from_pc,
NULL, /* breakpoint_reinsert_addr */
2,
bfin_breakpoint_at,
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index e0bfa1a..da5876d 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -62,7 +62,7 @@ cris_cannot_fetch_register (int regno)
extern int debug_threads;
static CORE_ADDR
-cris_get_pc (struct regcache *regcache, void)
+cris_get_pc (struct regcache *regcache)
{
unsigned long pc;
collect_register_by_name (regcache, "pc", &pc);
@@ -81,6 +81,13 @@ cris_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short cris_breakpoint = 0xe938;
#define cris_breakpoint_len 2
+static const unsigned char *
+cris_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = cris_breakpoint_len;
+ return (const unsigned char *) &cris_breakpoint;
+}
+
static int
cris_breakpoint_at (CORE_ADDR where)
{
@@ -140,8 +147,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
cris_get_pc,
cris_set_pc,
- (const unsigned char *) &cris_breakpoint,
- cris_breakpoint_len,
+ cris_breakpoint_from_pc,
cris_reinsert_addr,
0,
cris_breakpoint_at,
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 5120863..d2dba91 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -77,6 +77,13 @@ cris_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short cris_breakpoint = 0xe938;
#define cris_breakpoint_len 2
+static const unsigned char *
+cris_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = cris_breakpoint_len;
+ return (const unsigned char *) &cris_breakpoint;
+}
+
static int
cris_breakpoint_at (CORE_ADDR where)
{
@@ -420,8 +427,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
cris_get_pc,
cris_set_pc,
- (const unsigned char *) &cris_breakpoint,
- cris_breakpoint_len,
+ cris_breakpoint_from_pc,
cris_reinsert_addr,
0,
cris_breakpoint_at,
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 7d979f8..ef6075b 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2929,7 +2929,11 @@ linux_wait_1 (ptid_t ptid,
(event_child->stepping ||
!reinsert_breakpoint_inserted_here (event_child->stop_pc)))
{
- unsigned int increment_pc = the_low_target.breakpoint_len;
+ int increment_pc = 0;
+ CORE_ADDR stop_pc = event_child->stop_pc;
+
+ (*the_low_target.breakpoint_from_pc)
+ (&stop_pc, &increment_pc);
if (debug_threads)
{
@@ -6861,6 +6865,17 @@ current_lwp_ptid (void)
return ptid_of (current_thread);
}
+const unsigned char *
+linux_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+ if (the_low_target.breakpoint_from_pc != NULL)
+ {
+ return (*the_low_target.breakpoint_from_pc) (pcptr, lenptr);
+ }
+ else
+ return NULL;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_arch_setup,
@@ -6954,6 +6969,7 @@ static struct target_ops linux_target_ops = {
linux_mntns_open_cloexec,
linux_mntns_unlink,
linux_mntns_readlink,
+ linux_breakpoint_from_pc,
};
static void
@@ -6981,10 +6997,16 @@ void
initialize_low (void)
{
struct sigaction sigchld_action;
+ int breakpoint_len = 0;
+ const unsigned char *breakpoint = NULL;
+
memset (&sigchld_action, 0, sizeof (sigchld_action));
set_target_ops (&linux_target_ops);
- set_breakpoint_data (the_low_target.breakpoint,
- the_low_target.breakpoint_len);
+
+ breakpoint = the_target->breakpoint_from_pc (NULL, &breakpoint_len);
+
+ set_breakpoint_data (breakpoint,
+ breakpoint_len);
linux_init_signals ();
linux_ptrace_init_warnings ();
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index f8f6e78..c623150 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -141,8 +141,13 @@ struct linux_target_ops
CORE_ADDR (*get_pc) (struct regcache *regcache);
void (*set_pc) (struct regcache *regcache, CORE_ADDR newpc);
- const unsigned char *breakpoint;
- int breakpoint_len;
+
+ /* Return the raw breakpoint for this target based on PC. Note that the PC
+ can be NULL, the default breakpoint for the target should be returned in
+ this case. The PC is ajusted to the real memory location in case a flag was
+ present in the PC. */
+ const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
CORE_ADDR (*breakpoint_reinsert_addr) (void);
int decr_pc_after_break;
diff --git a/gdb/gdbserver/linux-m32r-low.c b/gdb/gdbserver/linux-m32r-low.c
index 8ffeda2..4712a32 100644
--- a/gdb/gdbserver/linux-m32r-low.c
+++ b/gdb/gdbserver/linux-m32r-low.c
@@ -73,6 +73,13 @@ m32r_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short m32r_breakpoint = 0x10f1;
#define m32r_breakpoint_len 2
+static const unsigned char *
+m32r_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = m32r_breakpoint_len;
+ return (const unsigned char *) &m32r_breakpoint;
+}
+
static int
m32r_breakpoint_at (CORE_ADDR where)
{
@@ -120,8 +127,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
m32r_get_pc,
m32r_set_pc,
- (const unsigned char *) &m32r_breakpoint,
- m32r_breakpoint_len,
+ m32r_breakpoint_from_pc,
NULL,
0,
m32r_breakpoint_at,
diff --git a/gdb/gdbserver/linux-m68k-low.c b/gdb/gdbserver/linux-m68k-low.c
index 39c9cc5..39a9753 100644
--- a/gdb/gdbserver/linux-m68k-low.c
+++ b/gdb/gdbserver/linux-m68k-low.c
@@ -125,6 +125,13 @@ static struct regset_info m68k_regsets[] = {
static const unsigned char m68k_breakpoint[] = { 0x4E, 0x4F };
#define m68k_breakpoint_len 2
+static const unsigned char *
+m68k_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = m68k_breakpoint_len;
+ return (unsigned char*) &m68k_breakpoint;
+}
+
static CORE_ADDR
m68k_get_pc (struct regcache *regcache)
{
@@ -215,8 +222,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
m68k_get_pc,
m68k_set_pc,
- m68k_breakpoint,
- m68k_breakpoint_len,
+ m68k_breakpoint_from_pc,
NULL,
2,
m68k_breakpoint_at,
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d1181b6..d5333ab 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -266,6 +266,13 @@ mips_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned int mips_breakpoint = 0x0005000d;
#define mips_breakpoint_len 4
+static const unsigned char *
+mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = mips_breakpoint_len;
+ return (const unsigned char *) &mips_breakpoint;
+}
+
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
@@ -881,8 +888,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
mips_get_pc,
mips_set_pc,
- (const unsigned char *) &mips_breakpoint,
- mips_breakpoint_len,
+ mips_breakpoint_from_pc,
mips_reinsert_addr,
0,
mips_breakpoint_at,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index 71542b4..bf9ecc2 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -119,7 +119,6 @@ nios2_set_pc (struct regcache *regcache, CORE_ADDR pc)
/* Breakpoint support. Also see comments on nios2_breakpoint_from_pc
in nios2-tdep.c. */
-
#if defined(__nios2_arch__) && __nios2_arch__ == 2
#define NIOS2_BREAKPOINT 0xb7fd0020
#define CDX_BREAKPOINT 0xd7c9
@@ -127,9 +126,21 @@ nios2_set_pc (struct regcache *regcache, CORE_ADDR pc)
#define NIOS2_BREAKPOINT 0x003b6ffa
#endif
+/* We only register the 4-byte breakpoint, even on R2 targets which also
+ support 2-byte breakpoints. Since there is no supports_z_point_type
+ function provided, gdbserver never inserts software breakpoints itself
+ and instead relies on GDB to insert the breakpoint of the correct length
+ via a memory write. */
static const unsigned int nios2_breakpoint = NIOS2_BREAKPOINT;
#define nios2_breakpoint_len 4
+static const unsigned char *
+nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = nios2_breakpoint_len;
+ return (const unsigned char *) &nios2_breakpoint;
+}
+
/* Implement the breakpoint_reinsert_addr linux_target_ops method. */
static CORE_ADDR
@@ -263,14 +274,7 @@ struct linux_target_ops the_low_target =
NULL,
nios2_get_pc,
nios2_set_pc,
-
- /* We only register the 4-byte breakpoint, even on R2 targets which also
- support 2-byte breakpoints. Since there is no supports_z_point_type
- function provided, gdbserver never inserts software breakpoints itself
- and instead relies on GDB to insert the breakpoint of the correct length
- via a memory write. */
- (const unsigned char *) &nios2_breakpoint,
- nios2_breakpoint_len,
+ nios2_breakpoint_from_pc,
nios2_reinsert_addr,
0,
nios2_breakpoint_at,
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 188fac0..4c71cd9 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -486,6 +486,13 @@ ppc_arch_setup (void)
static const unsigned int ppc_breakpoint = 0x7d821008;
#define ppc_breakpoint_len 4
+static const unsigned char *
+ppc_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = ppc_breakpoint_len;
+ return (const unsigned char *) &ppc_breakpoint;
+}
+
static int
ppc_breakpoint_at (CORE_ADDR where)
{
@@ -685,8 +692,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
ppc_get_pc,
ppc_set_pc,
- (const unsigned char *) &ppc_breakpoint,
- ppc_breakpoint_len,
+ ppc_breakpoint_from_pc,
NULL,
0,
ppc_breakpoint_at,
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index 8a0a689..f76f867 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -397,6 +397,13 @@ static struct regset_info s390_regsets[] = {
static const unsigned char s390_breakpoint[] = { 0, 1 };
#define s390_breakpoint_len 2
+static const unsigned char *
+s390_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = s390_breakpoint_len;
+ return (const unsigned char *) &s390_breakpoint;
+}
+
static CORE_ADDR
s390_get_pc (struct regcache *regcache)
{
@@ -665,8 +672,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
s390_get_pc,
s390_set_pc,
- s390_breakpoint,
- s390_breakpoint_len,
+ s390_breakpoint_from_pc,
NULL,
s390_breakpoint_len,
s390_breakpoint_at,
diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 218d4d3..5c11fd7 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -77,6 +77,13 @@ sh_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short sh_breakpoint = 0xc3c3;
#define sh_breakpoint_len 2
+static const unsigned char *
+sh_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = sh_breakpoint_len;
+ return (const unsigned char *) &sh_breakpoint;
+}
+
static int
sh_breakpoint_at (CORE_ADDR where)
{
@@ -148,8 +155,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
sh_get_pc,
sh_set_pc,
- (const unsigned char *) &sh_breakpoint,
- sh_breakpoint_len,
+ sh_breakpoint_from_pc,
NULL,
0,
sh_breakpoint_at,
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 796af8a..35820fb 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -240,6 +240,12 @@ static const unsigned char sparc_breakpoint[INSN_SIZE] = {
};
#define sparc_breakpoint_len INSN_SIZE
+static const unsigned char *
+sparc_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = sparc_breakpoint_len;
+ return (const unsigned char *) &sparc_breakpoint;
+}
static int
sparc_breakpoint_at (CORE_ADDR where)
@@ -323,8 +329,7 @@ struct linux_target_ops the_low_target = {
sparc_get_pc,
/* No sparc_set_pc is needed. */
NULL,
- (const unsigned char *) sparc_breakpoint,
- sparc_breakpoint_len,
+ sparc_breakpoint_from_pc,
sparc_reinsert_addr,
0,
sparc_breakpoint_at,
diff --git a/gdb/gdbserver/linux-tic6x-low.c b/gdb/gdbserver/linux-tic6x-low.c
index a2ac3ee..86b433c 100644
--- a/gdb/gdbserver/linux-tic6x-low.c
+++ b/gdb/gdbserver/linux-tic6x-low.c
@@ -171,6 +171,14 @@ extern struct linux_target_ops the_low_target;
static int *tic6x_regmap;
static unsigned int tic6x_breakpoint;
+#define tic6x_breakpoint_len 4
+
+static const unsigned char *
+tic6x_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = tic6x_breakpoint_len;
+ return (const unsigned char *) &tic6x_breakpoint;
+}
/* Forward definition. */
static struct usrregs_info tic6x_usrregs_info;
@@ -247,8 +255,6 @@ tic6x_set_pc (struct regcache *regcache, CORE_ADDR pc)
supply_register_by_name (regcache, "PC", newpc.buf);
}
-#define tic6x_breakpoint_len 4
-
static int
tic6x_breakpoint_at (CORE_ADDR where)
{
@@ -367,8 +373,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
tic6x_get_pc,
tic6x_set_pc,
- (const unsigned char *) &tic6x_breakpoint,
- tic6x_breakpoint_len,
+ tic6x_breakpoint_from_pc,
NULL,
0,
tic6x_breakpoint_at,
diff --git a/gdb/gdbserver/linux-tile-low.c b/gdb/gdbserver/linux-tile-low.c
index 6aaea6a..802812b 100644
--- a/gdb/gdbserver/linux-tile-low.c
+++ b/gdb/gdbserver/linux-tile-low.c
@@ -88,6 +88,13 @@ tile_set_pc (struct regcache *regcache, CORE_ADDR pc)
static uint64_t tile_breakpoint = 0x400b3cae70166000ULL;
#define tile_breakpoint_len 8
+static const unsigned char *
+tile_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = tile_breakpoint_len;
+ return (const unsigned char *) &tile_breakpoint;
+}
+
static int
tile_breakpoint_at (CORE_ADDR where)
{
@@ -182,8 +189,7 @@ struct linux_target_ops the_low_target =
NULL,
tile_get_pc,
tile_set_pc,
- (const unsigned char *) &tile_breakpoint,
- tile_breakpoint_len,
+ tile_breakpoint_from_pc,
NULL,
0,
tile_breakpoint_at,
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 20d4257..699eb4d 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3243,6 +3243,13 @@ x86_emit_ops (void)
return &i386_emit_ops;
}
+static const unsigned char *
+x86_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = x86_breakpoint_len;
+ return x86_breakpoint;
+}
+
static int
x86_supports_range_stepping (void)
{
@@ -3261,8 +3268,7 @@ struct linux_target_ops the_low_target =
NULL, /* fetch_register */
x86_get_pc,
x86_set_pc,
- x86_breakpoint,
- x86_breakpoint_len,
+ x86_breakpoint_from_pc,
NULL,
1,
x86_breakpoint_at,
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index debe467..1e2cf94 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -154,6 +154,13 @@ static struct regset_info xtensa_regsets[] = {
static const unsigned char xtensa_breakpoint[] = XTENSA_BREAKPOINT;
#define xtensa_breakpoint_len 2
+static const unsigned char *
+xtensa_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = xtensa_breakpoint_len;
+ return xtensa_breakpoint;
+}
+
static CORE_ADDR
xtensa_get_pc (struct regcache *regcache)
{
@@ -234,8 +241,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
xtensa_get_pc,
xtensa_set_pc,
- xtensa_breakpoint,
- xtensa_breakpoint_len,
+ xtensa_breakpoint_from_pc,
NULL,
0,
xtensa_breakpoint_at,
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 7df8df3..fc2dbd7 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -438,6 +438,11 @@ struct target_ops
readlink(2). */
ssize_t (*multifs_readlink) (int pid, const char *filename,
char *buf, size_t bufsiz);
+
+ /* Return the raw breakpoint for this target based on PC. Note that the PC
+ can be NULL, the default breakpoint for the target should be returned in
+ this case. */
+ const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
};
extern struct target_ops *the_target;
diff --git a/gdb/gdbserver/win32-arm-low.c b/gdb/gdbserver/win32-arm-low.c
index d4b2c6f..1b469a3 100644
--- a/gdb/gdbserver/win32-arm-low.c
+++ b/gdb/gdbserver/win32-arm-low.c
@@ -113,6 +113,13 @@ arm_arch_setup (void)
static const unsigned long arm_wince_breakpoint = 0xe6000010;
#define arm_wince_breakpoint_len 4
+static const unsigned char *
+arm_wince_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = arm_wince_breakpoint_len;
+ return (const unsigned char *) &arm_wince_breakpoint;
+}
+
struct win32_target_ops the_low_target = {
arm_arch_setup,
sizeof (mappings) / sizeof (mappings[0]),
@@ -123,8 +130,7 @@ struct win32_target_ops the_low_target = {
arm_fetch_inferior_register,
arm_store_inferior_register,
NULL, /* single_step */
- (const unsigned char *) &arm_wince_breakpoint,
- arm_wince_breakpoint_len,
+ arm_wince_breakpoint_from_pc,
/* Watchpoint related functions. See target.h for comments. */
NULL, /* supports_z_point_type */
NULL, /* insert_point */
diff --git a/gdb/gdbserver/win32-i386-low.c b/gdb/gdbserver/win32-i386-low.c
index 7c22f05..b9d60a7 100644
--- a/gdb/gdbserver/win32-i386-low.c
+++ b/gdb/gdbserver/win32-i386-low.c
@@ -444,6 +444,13 @@ i386_store_inferior_register (struct regcache *regcache,
static const unsigned char i386_win32_breakpoint = 0xcc;
#define i386_win32_breakpoint_len 1
+static const unsigned char *
+i386_win32_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = i386_win32_breakpoint_len;
+ return (const unsigned char *) &i386_win32_breakpoint;
+}
+
static void
i386_arch_setup (void)
{
@@ -466,8 +473,7 @@ struct win32_target_ops the_low_target = {
i386_fetch_inferior_register,
i386_store_inferior_register,
i386_single_step,
- &i386_win32_breakpoint,
- i386_win32_breakpoint_len,
+ i386_win32_breakpoint_from_pc,
i386_supports_z_point_type,
i386_insert_point,
i386_remove_point,
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
2015-09-11 12:13 ` [PATCH 1/7] Fix instruction skipping when using software single step " Antoine Tremblay
2015-09-11 12:13 ` [PATCH 2/7] Move some integer operations to common Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-11 12:30 ` Eli Zaretskii
2015-09-11 12:14 ` [PATCH 3/7] Support multiple breakpoint types per target " Antoine Tremblay
` (4 subsequent siblings)
7 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch adds support for tracepoints for ARM aarch32-linux in GDBServer.
It also adds support for software breakpoints via a z0 packet in GDBServer.
To enable this, this patch also introduces a new :K (kind) field in the QTDP
packet to encode the breakpoint kind, this is the same kind as a z0 packet.
This is the new qSupported feature : TracepointKinds
This field is decoded by the new breakpoint_from_length target ops in linux-low.
Also GDBServer always need to handle that the software breakpoint size can
coded as a kind field and thus always decode this to the real length of the
breakpoint when doing memory operations on the breakpoint using
check_breakpoint_from_length.
A note about tests :
Some tests expect to use a frame pointer register, however since there no such
thing on the ABI for ARM on linux, references to fp are replaced by sp the stack
pointer. Since this value is usually only meant to be collected there is no
impact on the tests.
Also, since some registers are not available on ARM some tests have been skipped
or adapted to work properly given that situation.
For example the pending.exp test checks if a trace frame is found, however since
some registers are not available some error output is generated about this issue
even if the trace frame is found. The test was made a little more lax so that
such output is accepted.
Other tests are left as failing since what they test is directly impacted by
those missing registers like reports.exp that will output errors like :
"Unable to access DWARF register number 8".
There is some known tests that are failing to due what seems to be a bug in the
ARM stack unwinder, for example collecting a string in collections.exp will not
work properly when calling tdump the first time since it can't compute the frame
id but will work when called the second time... Fixing this issue is left as
work to be done.
This issue also has ripple effects on some other tests like :
mi-trace-frame-collected.exp since it causes a "Register 13 is not available" to
be printed.
Other than that the patch was tested on Ubuntu 14.04 ARMv7 and x86 with no
regression.
Note that a lot of tests are flaky on ARMv7 and care was taken to test each
flaky test before and after the patch to ensure no regression was found.
gdb/ChangeLog:
* NEWS: Add news for tracepoints and software breakpoints.
gdb/gdbserver/ChangeLog:
* linux-arm-low.c (arm_breakpoint_from_length): New function.
(arm_supports_z_point_type): Add software breakpoint support.
(arm_supports_tracepoints): New function.
(struct linux_target_ops): Add arm_supports_tracepoints and
arm_breakpoint_from_length operations.
* linux-low.c (linux_supports_breakpoint_kinds): New function.
(linux_breakpoint_from_length): New function.
* linux-low.h: Add breakpoint_from_length operation.
* mem-break.c (check_breakpoint_from_length): New function.
(insert_memory_breakpoint): Call check_breakpoint_from_length.
(remove_memory_breakpoint): Likewise.
(set_breakpoint_at_with_length): New function.
(validate_inserted_breakpoint): Call check_breakpoint_from_length.
(check_mem_read): Likewise.
(check_mem_write): Likewise.
* mem-break.h: Add set_breakpoint_at_with_length.
* server.c (handle_query): Add TracepointKinds+.
* target.h (struct target_ops): Add breakpoint_from_length and
supports_breakpoint_kinds operations.
* tracepoint.c (struct tracepoint): Add breakpoint_len field.
(add_tracepoint): Add breakpoint_len init.
(cmd_qtdp): Handle :K kind parameter.
(install_tracepoint): Handle breakpoint_len.
(cmd_qtstart): Likewise.
* remote.c (remote_supports_tracepoint_kinds): Add
TracepointKinds packet.
(remote_download_tracepoint): Add breakpoint lenght to :K kind
parameter.
gdb/testsuite/ChangeLog:
* gdb.trace/backtrace.exp: Use sp for fp.
* gdb.trace/collection.exp: Use sp for fp and sip tests that
can't be supported on aarch32.
* gdb.trace/mi-trace-frame-collected.exp: Add aarch32 pc register.
* gdb.trace/pending.exp: Be more permissive on test output in
case of unrelated errors printed to stdout.
* gdb.trace/report.exp: Use sp for fp.
* gdb.trace/trace-break.exp: Add aarch32 sp register.
* gdb.trace/while-dyn.exp: Likewise.
gdb/doc/ChangeLog:
* doc/gdb.texinfo: Add documentation for QTDP kind parameter.
---
gdb/NEWS | 4 ++
gdb/doc/gdb.texinfo | 36 ++++++----
gdb/gdbserver/linux-arm-low.c | 32 ++++++++-
gdb/gdbserver/linux-low.c | 21 +++++-
gdb/gdbserver/linux-low.h | 4 ++
gdb/gdbserver/mem-break.c | 77 +++++++++++++++++++---
gdb/gdbserver/mem-break.h | 6 ++
gdb/gdbserver/server.c | 2 +
gdb/gdbserver/target.h | 12 ++++
gdb/gdbserver/tracepoint.c | 43 ++++++++++--
gdb/remote.c | 34 +++++++++-
gdb/testsuite/gdb.trace/backtrace.exp | 3 +
gdb/testsuite/gdb.trace/collection.exp | 18 ++++-
.../gdb.trace/mi-trace-frame-collected.exp | 2 +
gdb/testsuite/gdb.trace/pending.exp | 6 +-
gdb/testsuite/gdb.trace/report.exp | 4 ++
gdb/testsuite/gdb.trace/trace-break.exp | 2 +
gdb/testsuite/gdb.trace/while-dyn.exp | 2 +
18 files changed, 271 insertions(+), 37 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 0cf51e1..085d844 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
*** Changes since GDB 7.10
+* Support for tracepoints on aarch32-linux was added in GDBServer.
+
+* Support for software breakpoints on aarch32-linux was added in GDBServer.
+
* Support for tracepoints on aarch64-linux was added in GDBserver.
* The 'record instruction-history' command now indicates speculative execution
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index cd0abad..a604eed 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -36373,6 +36373,11 @@ These are the currently defined stub features and their properties:
@tab @samp{-}
@tab No
+@item @samp{TracepointKinds}
+@tab No
+@tab @samp{-}
+@tab No
+
@end multitable
These are the currently defined stub features, in more detail:
@@ -36578,6 +36583,9 @@ The remote stub reports the @samp{fork} stop reason for fork events.
The remote stub reports the @samp{vfork} stop reason for vfork events
and vforkdone events.
+@item TracepointKinds
+The remote stub reports the @samp{:K} kind parameter for @samp{QTDP} packets.
+
@end table
@item qSymbol::
@@ -37088,7 +37096,8 @@ details of XML target descriptions for each architecture.
@subsubsection @acronym{ARM} Breakpoint Kinds
@cindex breakpoint kinds, @acronym{ARM}
-These breakpoint kinds are defined for the @samp{Z0} and @samp{Z1} packets.
+These breakpoint kinds are defined for the @samp{Z0}, @samp{Z1}
+and @samp{QTDP} packets.
@table @r
@@ -37168,20 +37177,23 @@ tracepoints (@pxref{Tracepoints}).
@table @samp
-@item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}[:F@var{flen}][:X@var{len},@var{bytes}]@r{[}-@r{]}
+@item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}[:F@var{flen}][:X@var{len},@var{bytes}][:K@var{kind}]@r{[}-@r{]}
@cindex @samp{QTDP} packet
Create a new tracepoint, number @var{n}, at @var{addr}. If @var{ena}
is @samp{E}, then the tracepoint is enabled; if it is @samp{D}, then
-the tracepoint is disabled. The @var{step} gives the tracepoint's step
-count, and @var{pass} gives its pass count. If an @samp{F} is present,
-then the tracepoint is to be a fast tracepoint, and the @var{flen} is
-the number of bytes that the target should copy elsewhere to make room
-for the tracepoint. If an @samp{X} is present, it introduces a
-tracepoint condition, which consists of a hexadecimal length, followed
-by a comma and hex-encoded bytes, in a manner similar to action
-encodings as described below. If the trailing @samp{-} is present,
-further @samp{QTDP} packets will follow to specify this tracepoint's
-actions.
+the tracepoint is disabled. The @var{step} gives the tracepoint's
+step count, and @var{pass} gives its pass count. If an @samp{F} is
+present, then the tracepoint is to be a fast tracepoint, and the
+@var{flen} is the number of bytes that the target should copy
+elsewhere to make room for the tracepoint. If an @samp{X} is present,
+it introduces a tracepoint condition, which consists of a hexadecimal
+length, followed by a comma and hex-encoded bytes, in a manner similar
+to action encodings as described below. If a @samp{K} is present, it
+indicates a target specific breakpoint length. E.g., the arm and mips
+can insert either a 2 or 4 byte breakpoint. Some architectures have
+additional meanings for kind see : @ref{Architecture-Specific Protocol
+Details} . If the trailing @samp{-} is present, further @samp{QTDP}
+packets will follow to specify this tracepoint's actions.
Replies:
@table @samp
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index a9ed91d..86df966 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -438,6 +438,28 @@ arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
}
}
+/* Get the breakpoint from the remote length
+ 2 is thumb-16
+ 3 is thumb2-32
+ 4 is arm
+*/
+static const unsigned char *
+arm_breakpoint_from_length (int *len)
+{
+ switch (*len) {
+ case 2:
+ return thumb_breakpoint;
+ case 3:
+ *len = 4;
+ return thumb2_breakpoint;
+ case 4:
+ return arm_breakpoint;
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
/* Sets the breakpoints to the endianness in argument. */
static void
@@ -769,6 +791,7 @@ arm_supports_z_point_type (char z_type)
{
switch (z_type)
{
+ case Z_PACKET_SW_BP:
case Z_PACKET_HW_BP:
case Z_PACKET_WRITE_WP:
case Z_PACKET_READ_WP:
@@ -1167,6 +1190,12 @@ arm_regs_info (void)
return ®s_info_arm;
}
+static int
+arm_supports_tracepoints (void)
+{
+ return 1;
+}
+
struct linux_target_ops the_low_target = {
arm_arch_setup,
arm_regs_info,
@@ -1192,13 +1221,14 @@ struct linux_target_ops the_low_target = {
arm_new_fork,
arm_prepare_to_resume,
NULL, /* process_qsupported */
- NULL, /* supports_tracepoints */
+ arm_supports_tracepoints,
NULL, /* get_thread_area */
NULL, /* install_fast_tracepoint_jump_pad */
NULL, /* emit_ops */
NULL, /* get_min_fast_tracepoint_insn_len */
NULL, /* supports_range_stepping */
arm_supports_hardware_single_step,
+ arm_breakpoint_from_length
};
void
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index f19769d..495181d 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -313,8 +313,14 @@ supports_fast_tracepoints (void)
return the_low_target.install_fast_tracepoint_jump_pad != NULL;
}
-/* True if LWP is stopped in its stepping range. */
+/* True if a breakpoint/tracepoint can differ based on kind/length. */
+static int
+linux_supports_breakpoint_kinds (void)
+{
+ return the_low_target.breakpoint_from_length != NULL;
+}
+/* True if LWP is stopped in its stepping range. */
static int
lwp_in_step_range (struct lwp_info *lwp)
{
@@ -5998,6 +6004,17 @@ linux_supports_range_stepping (void)
return (*the_low_target.supports_range_stepping) ();
}
+static const unsigned char*
+linux_breakpoint_from_length (int *len)
+{
+ if (*the_low_target.breakpoint_from_length != NULL)
+ {
+ return (*the_low_target.breakpoint_from_length) (len);
+ }
+ else
+ return NULL;
+
+}
/* Enumerate spufs IDs for process PID. */
static int
spu_enumerate_spu_ids (long pid, unsigned char *buf, CORE_ADDR offset, int len)
@@ -7095,6 +7112,8 @@ static struct target_ops linux_target_ops = {
linux_mntns_readlink,
linux_breakpoint_from_pc,
linux_supports_software_single_step,
+ linux_breakpoint_from_length,
+ linux_supports_breakpoint_kinds
};
static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index b6f8175..913021a 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -243,6 +243,10 @@ struct linux_target_ops
/* Returns true if the low target supports hardware single step. */
int (*supports_hardware_single_step) (void);
+
+ /* Returns the proper breakpoint from size, the length can have target
+ specific meaning like the z0 or QTDP kind parameter */
+ const unsigned char *(*breakpoint_from_length) (int *length);
};
extern struct linux_target_ops the_low_target;
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 1a87d01..bc793e9 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -295,6 +295,36 @@ find_raw_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type, int size)
return NULL;
}
+int
+check_breakpoint_from_length (struct raw_breakpoint *bp,
+ const unsigned char **breakpoint_data,
+ int *breakpoint_len)
+{
+ /* If the architecture treats the size field of Z packets as a
+ 'kind' field, then we'll need to be able to know which is the
+ breakpoint instruction too. This is also true for tracepoint kind
+ field. */
+ if (bp->size != *breakpoint_len)
+ {
+ /* Get the arch dependent breakpoint */
+ if (*the_target->breakpoint_from_length != NULL)
+ {
+ /* Update magic coded size to the right size if needed. */
+ int size = bp->size;
+ *breakpoint_data =
+ (*the_target->breakpoint_from_length) (&size);
+ *breakpoint_len = size;
+ }
+ else {
+ if (debug_threads)
+ debug_printf ("Don't know breakpoints of size %d.\n",
+ bp->size);
+ return -1;
+ }
+ }
+
+ return 1;
+}
/* See mem-break.h. */
int
@@ -312,16 +342,9 @@ insert_memory_breakpoint (struct raw_breakpoint *bp)
if (breakpoint_data == NULL)
return 1;
- /* If the architecture treats the size field of Z packets as a
- 'kind' field, then we'll need to be able to know which is the
- breakpoint instruction too. */
- if (bp->size != breakpoint_len)
- {
- if (debug_threads)
- debug_printf ("Don't know how to insert breakpoints of size %d.\n",
- bp->size);
- return -1;
- }
+ if ((err = check_breakpoint_from_length (bp, &breakpoint_data,
+ &breakpoint_len)) < 0)
+ return err;
/* Note that there can be fast tracepoint jumps installed in the
same memory range, so to get at the original memory, we need to
@@ -356,6 +379,7 @@ int
remove_memory_breakpoint (struct raw_breakpoint *bp)
{
unsigned char buf[MAX_BREAKPOINT_LEN];
+ const unsigned char* breakpoint_data;
int err;
int breakpoint_len;
CORE_ADDR pc;
@@ -363,6 +387,10 @@ remove_memory_breakpoint (struct raw_breakpoint *bp)
pc = bp->pcfull;
the_target->breakpoint_from_pc (&pc, &breakpoint_len);
+ if ((err = check_breakpoint_from_length (bp, &breakpoint_data,
+ &breakpoint_len)) < 0)
+ return err;
+
/* Since there can be trap breakpoints inserted in the same address
range, we use `write_inferior_memory', which takes care of
layering breakpoints on top of fast tracepoints, and on top of
@@ -806,6 +834,17 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
&err_ignored);
}
+struct breakpoint *
+set_breakpoint_at_with_length (CORE_ADDR where, int (*handler) (CORE_ADDR),
+ int size)
+{
+ int err_ignored;
+
+ return set_breakpoint (other_breakpoint, raw_bkpt_type_sw,
+ where, size, handler,
+ &err_ignored);
+}
+
static int
delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel)
@@ -1710,6 +1749,14 @@ validate_inserted_breakpoint (struct raw_breakpoint *bp)
breakpoint_data = the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
+ if ((err = check_breakpoint_from_length (bp, &breakpoint_data,
+ &breakpoint_len)) < 0)
+ {
+ /* Tag it as gone. */
+ bp->inserted = -1;
+ return 0;
+ }
+
gdb_assert (bp->inserted);
gdb_assert (bp->raw_type == raw_bkpt_type_sw);
@@ -1808,11 +1855,16 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
{
int breakpoint_len;
CORE_ADDR raw_pc;
+ const unsigned char* breakpoint_data;
CORE_ADDR bp_end, start, end;
int copy_offset, copy_len, buf_offset;
raw_pc = bp->pcfull;
+
the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
+ check_breakpoint_from_length (bp, &breakpoint_data,
+ &breakpoint_len);
+
bp_end = bp->pc + breakpoint_len;
if (bp->raw_type != raw_bkpt_type_sw)
@@ -1907,8 +1959,13 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf,
int copy_offset, copy_len, buf_offset;
raw_pc = bp->pcfull;
+
breakpoint_data =
the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
+
+ check_breakpoint_from_length (bp, &breakpoint_data,
+ &breakpoint_len);
+
bp_end = bp->pc + breakpoint_len;
if (bp->raw_type != raw_bkpt_type_sw)
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index b1bb91a..ec28998 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -146,6 +146,12 @@ int gdb_breakpoint_here (CORE_ADDR where);
struct breakpoint *set_breakpoint_at (CORE_ADDR where,
int (*handler) (CORE_ADDR));
+/* Same as set_breakpoint_at but allow the length to be specified */
+struct breakpoint *set_breakpoint_at_with_length (CORE_ADDR where,
+ int (*handler)(CORE_ADDR),
+ int size);
+
+
/* Delete a breakpoint. */
int delete_breakpoint (struct breakpoint *bkpt);
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index fb0e843..40d4e78 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2190,6 +2190,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
strcat (own_buf, ";EnableDisableTracepoints+");
strcat (own_buf, ";QTBuffer:size+");
strcat (own_buf, ";tracenz+");
+ if (target_supports_breakpoint_kinds ())
+ strcat (own_buf, ";TracepointKinds+");
}
if (target_supports_hardware_single_step () ||
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index aa40c4a..a076fb4 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -446,6 +446,14 @@ struct target_ops
/* Returns true if the target can do software single step. */
int (*supports_software_single_step) (void);
+
+ /* Returns a breakpoint from a length, the length can have target specific
+ meaning like the z0 kind parameter. */
+ const unsigned char *(*breakpoint_from_length) (int *len);
+
+ /* Returns wether target supports breakpoints kinds like is sent
+ via a QTDP :K packet. */
+ int (*supports_breakpoint_kinds) (void);
};
extern struct target_ops *the_target;
@@ -628,6 +636,10 @@ int kill_inferior (int);
(the_target->supports_software_single_step ? \
(*the_target->supports_software_single_step) () : 0)
+#define target_supports_breakpoint_kinds() \
+ (the_target->supports_breakpoint_kinds ? \
+ (*the_target->supports_breakpoint_kinds) () : 0)
+
/* Start non-stop mode, returns 0 on success, -1 on failure. */
int start_non_stop (int nonstop);
diff --git a/gdb/gdbserver/tracepoint.c b/gdb/gdbserver/tracepoint.c
index fd010ae..6d1eece 100644
--- a/gdb/gdbserver/tracepoint.c
+++ b/gdb/gdbserver/tracepoint.c
@@ -754,6 +754,11 @@ struct tracepoint
/* Link to the next tracepoint in the list. */
struct tracepoint *next;
+ /* Optional length of the breakpoint to be used
+ note this can mean different things for different archs as z0
+ breakpoint command */
+ uint32_t breakpoint_len;
+
#ifndef IN_PROCESS_AGENT
/* The list of actions to take when the tracepoint triggers, in
string/packet form. */
@@ -1820,6 +1825,7 @@ add_tracepoint (int num, CORE_ADDR addr)
tpoint->compiled_cond = 0;
tpoint->handle = NULL;
tpoint->next = NULL;
+ tpoint->breakpoint_len = 0;
/* Find a place to insert this tracepoint into list in order to keep
the tracepoint list still in the ascending order. There may be
@@ -2487,6 +2493,7 @@ cmd_qtdp (char *own_buf)
ULONGEST num;
ULONGEST addr;
ULONGEST count;
+ ULONGEST breakpoint_len;
struct tracepoint *tpoint;
char *actparm;
char *packet = own_buf;
@@ -2553,6 +2560,12 @@ cmd_qtdp (char *own_buf)
tpoint->cond = gdb_parse_agent_expr (&actparm);
packet = actparm;
}
+ else if (*packet == 'K')
+ {
+ ++packet;
+ packet = unpack_varlen_hex (packet, &breakpoint_len);
+ tpoint->breakpoint_len = breakpoint_len;
+ }
else if (*packet == '-')
break;
else if (*packet == '\0')
@@ -2567,11 +2580,13 @@ cmd_qtdp (char *own_buf)
}
trace_debug ("Defined %stracepoint %d at 0x%s, "
- "enabled %d step %" PRIu64 " pass %" PRIu64,
+ "enabled %d step %" PRIu64 " pass %" PRIu64
+ " length %" PRIu32,
tpoint->type == fast_tracepoint ? "fast "
: tpoint->type == static_tracepoint ? "static " : "",
tpoint->number, paddress (tpoint->address), tpoint->enabled,
- tpoint->step_count, tpoint->pass_count);
+ tpoint->step_count,tpoint->pass_count,
+ tpoint->breakpoint_len);
}
else if (tpoint)
add_tracepoint_action (tpoint, packet);
@@ -3148,9 +3163,17 @@ install_tracepoint (struct tracepoint *tpoint, char *own_buf)
/* Tracepoints are installed as memory breakpoints. Just go
ahead and install the trap. The breakpoints module
handles duplicated breakpoints, and the memory read
- routine handles un-patching traps from memory reads. */
- tpoint->handle = set_breakpoint_at (tpoint->address,
+ routine handles un-patching traps from memory reads.
+ If breakpoint_len is not set, use the default breakpoint len values
+ otherwise what was set from the gdb client will be used.*/
+ if (tpoint->breakpoint_len == 0)
+ tpoint->handle = set_breakpoint_at (tpoint->address,
tracepoint_handler);
+ else
+ tpoint->handle =
+ set_breakpoint_at_with_length (tpoint->address,
+ tracepoint_handler,
+ tpoint->breakpoint_len);
}
else if (tpoint->type == fast_tracepoint || tpoint->type == static_tracepoint)
{
@@ -3243,8 +3266,16 @@ cmd_qtstart (char *packet)
ahead and install the trap. The breakpoints module
handles duplicated breakpoints, and the memory read
routine handles un-patching traps from memory reads. */
- tpoint->handle = set_breakpoint_at (tpoint->address,
- tracepoint_handler);
+
+
+ if (tpoint->breakpoint_len == 0)
+ tpoint->handle = set_breakpoint_at (tpoint->address,
+ tracepoint_handler);
+ else
+ tpoint->handle =
+ set_breakpoint_at_with_length (tpoint->address,
+ tracepoint_handler,
+ tpoint->breakpoint_len);
}
else if (tpoint->type == fast_tracepoint
|| tpoint->type == static_tracepoint)
diff --git a/gdb/remote.c b/gdb/remote.c
index 3afcaf4..67daf70 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -230,6 +230,8 @@ static int stop_reply_queue_length (void);
static void readahead_cache_invalidate (void);
+static int remote_supports_tracepoint_kinds (void);
+
/* For "remote". */
static struct cmd_list_element *remote_cmdlist;
@@ -1401,6 +1403,9 @@ enum {
/* Support for the Qbtrace-conf:pt:size packet. */
PACKET_Qbtrace_conf_pt_size,
+ /* Support target dependant tracepoint kinds. */
+ PACKET_TracepointKinds,
+
PACKET_MAX
};
@@ -4280,7 +4285,9 @@ static const struct protocol_feature remote_protocol_features[] = {
{ "vfork-events", PACKET_DISABLE, remote_supported_packet,
PACKET_vfork_event_feature },
{ "Qbtrace-conf:pt:size", PACKET_DISABLE, remote_supported_packet,
- PACKET_Qbtrace_conf_pt_size }
+ PACKET_Qbtrace_conf_pt_size },
+ { "TracepointKinds", PACKET_DISABLE, remote_supported_packet,
+ PACKET_TracepointKinds }
};
static char *remote_support_xml;
@@ -11282,6 +11289,12 @@ remote_can_run_breakpoint_commands (struct target_ops *self)
return packet_support (PACKET_BreakpointCommands) == PACKET_ENABLE;
}
+static int
+remote_supports_tracepoint_kinds (void)
+{
+ return packet_support (PACKET_TracepointKinds) == PACKET_ENABLE;
+}
+
static void
remote_trace_init (struct target_ops *self)
{
@@ -11370,6 +11383,7 @@ remote_download_tracepoint (struct target_ops *self, struct bp_location *loc)
char *pkt;
struct breakpoint *b = loc->owner;
struct tracepoint *t = (struct tracepoint *) b;
+ int bpsize;
encode_actions_rsp (loc, &tdp_actions, &stepping_actions);
old_chain = make_cleanup (free_actions_list_cleanup_wrapper,
@@ -11378,11 +11392,17 @@ remote_download_tracepoint (struct target_ops *self, struct bp_location *loc)
stepping_actions);
tpaddr = loc->address;
+
+ /* The length field should be set to the size of a breakpoint
+ instruction, even though we aren't inserting one ourselves. */
+ gdbarch_remote_breakpoint_from_pc (target_gdbarch(), &tpaddr, &bpsize);
+
sprintf_vma (addrbuf, tpaddr);
xsnprintf (buf, BUF_SIZE, "QTDP:%x:%s:%c:%lx:%x", b->number,
addrbuf, /* address */
(b->enable_state == bp_enabled ? 'E' : 'D'),
t->step_count, t->pass_count);
+
/* Fast tracepoints are mostly handled by the target, but we can
tell the target how big of an instruction block should be moved
around. */
@@ -11452,6 +11472,14 @@ remote_download_tracepoint (struct target_ops *self, struct bp_location *loc)
"ignoring tp %d cond"), b->number);
}
+ /* Tracepoint Kinds are modeled after the breakpoint Z0 kind packet.
+ Send the tracepoint kind if we support it. */
+ if (remote_supports_tracepoint_kinds ())
+ {
+ xsnprintf (buf + strlen (buf), BUF_SIZE - strlen (buf), ":K%x",
+ bpsize);
+ }
+
if (b->commands || *default_collect)
strcat (buf, "-");
putpkt (buf);
@@ -13272,6 +13300,10 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_pt_size],
"Qbtrace-conf:pt:size", "btrace-conf-pt-size", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_TracepointKinds],
+ "TracepointKinds",
+ "tracepoint-kinds", 0);
+
/* Assert that we've registered "set remote foo-packet" commands
for all packet configs. */
{
diff --git a/gdb/testsuite/gdb.trace/backtrace.exp b/gdb/testsuite/gdb.trace/backtrace.exp
index f69089b..6635de1 100644
--- a/gdb/testsuite/gdb.trace/backtrace.exp
+++ b/gdb/testsuite/gdb.trace/backtrace.exp
@@ -146,6 +146,9 @@ if [is_amd64_regs_target] {
} elseif [is_x86_like_target] {
set fpreg "\$ebp"
set spreg "\$esp"
+} elseif [is_aarch32_target] {
+ set fpreg "\$sp"
+ set spreg "\$sp"
} elseif [is_aarch64_target] {
set fpreg "\$x29"
set spreg "\$sp"
diff --git a/gdb/testsuite/gdb.trace/collection.exp b/gdb/testsuite/gdb.trace/collection.exp
index 69ad6ee..60c1278 100644
--- a/gdb/testsuite/gdb.trace/collection.exp
+++ b/gdb/testsuite/gdb.trace/collection.exp
@@ -44,6 +44,10 @@ if [is_amd64_regs_target] {
set fpreg "ebp"
set spreg "esp"
set pcreg "eip"
+} elseif [is_aarch32_target] {
+ set fpreg "sp"
+ set spreg "sp"
+ set pcreg "pc"
} elseif [is_aarch64_target] {
set fpreg "x29"
set spreg "sp"
@@ -696,11 +700,16 @@ proc gdb_trace_collection_test {} {
gdb_collect_locals_test local_test_func \
"locc, loci, locf, locd, locst, locar" \
"auto locals individually"
- gdb_collect_locals_test reglocal_test_func "\$locals" \
+
+ #Some registers can't be read on ARM , skip those tests
+ if { ![is_aarch32_target] } {
+ gdb_collect_locals_test reglocal_test_func "\$locals" \
"register locals collectively"
- gdb_collect_locals_test reglocal_test_func \
+ gdb_collect_locals_test reglocal_test_func \
"locc, loci, locf, locd, locst, locar" \
"register locals individually"
+ }
+
gdb_collect_locals_test statlocal_test_func "\$locals" \
"static locals collectively"
gdb_collect_locals_test statlocal_test_func \
@@ -782,7 +791,10 @@ proc gdb_trace_collection_test {} {
gdb_collect_expression_test globals_test_func \
"globalarr\[\(l6, l7\)\]" "7" "a\[\(b, c\)\]"
- gdb_collect_return_test
+ #This architecture has no method to collect a return address.
+ if { ![is_aarch32_target] } {
+ gdb_collect_return_test
+ }
gdb_collect_strings_test strings_test_func "locstr" "abcdef" "" \
"local string"
diff --git a/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp b/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp
index a7bed0b..cc36a2d 100644
--- a/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp
+++ b/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp
@@ -56,6 +56,8 @@ if [is_amd64_regs_target] {
set pcreg "rip"
} elseif [is_x86_like_target] {
set pcreg "eip"
+} elseif [is_aarch32_target] {
+ set pcreg "pc"
} elseif [is_aarch64_target] {
set pcreg "pc"
} else {
diff --git a/gdb/testsuite/gdb.trace/pending.exp b/gdb/testsuite/gdb.trace/pending.exp
index 9938c5a..33a8609 100644
--- a/gdb/testsuite/gdb.trace/pending.exp
+++ b/gdb/testsuite/gdb.trace/pending.exp
@@ -159,7 +159,7 @@ proc pending_tracepoint_works { trace_type } {
gdb_test "tstop" "\[\r\n\]+" "stop trace experiment"
- gdb_test "tfind start" "#0 .*" "tfind test frame 0"
+ gdb_test "tfind start" ".*Found trace frame 0.*" "tfind test frame 0"
gdb_test "tfind" "Found trace frame 1, tracepoint 1.*" \
"tfind test frame 1"
gdb_test "tfind" "Found trace frame 2, tracepoint 1.*" \
@@ -234,7 +234,7 @@ proc pending_tracepoint_resolved_during_trace { trace_type } \
\[0-9\]+\[\t \]+\(fast |\)tracepoint\[ \]+keep y.*pendfunc2.*" \
"tracepoint is resolved"
- gdb_test "tfind start" "#0 .*" "tfind test frame 0"
+ gdb_test "tfind start" ".*Found trace frame 0.*" "tfind test frame 0"
gdb_test "tfind" "Target failed to find requested trace frame..*" "tfind test frame"
}}
@@ -307,7 +307,7 @@ proc pending_tracepoint_installed_during_trace { trace_type } \
\[0-9\]+\[\t \]+\(fast |\)tracepoint\[ \]+keep y.*pendfunc2.*" \
"tracepoint is resolved"
- gdb_test "tfind start" "#0 $hex in pendfunc2 .*" "tfind test frame 0"
+ gdb_test "tfind start" ".*$hex in pendfunc2 .*" "tfind test frame 0"
gdb_test "tfind" "Target failed to find requested trace frame..*" "tfind test frame"
}}
diff --git a/gdb/testsuite/gdb.trace/report.exp b/gdb/testsuite/gdb.trace/report.exp
index b3e9000..e48f696 100644
--- a/gdb/testsuite/gdb.trace/report.exp
+++ b/gdb/testsuite/gdb.trace/report.exp
@@ -158,6 +158,10 @@ if [is_amd64_regs_target] {
set fpreg "ebp"
set spreg "esp"
set pcreg "eip"
+} elseif [is_aarch32_target] {
+ set fpreg "sp"
+ set spreg "sp"
+ set pcreg "pc"
} elseif [is_aarch64_target] {
set fpreg "x29"
set spreg "sp"
diff --git a/gdb/testsuite/gdb.trace/trace-break.exp b/gdb/testsuite/gdb.trace/trace-break.exp
index 1f57601..e7dd5d0 100644
--- a/gdb/testsuite/gdb.trace/trace-break.exp
+++ b/gdb/testsuite/gdb.trace/trace-break.exp
@@ -49,6 +49,8 @@ if [is_amd64_regs_target] {
set fpreg "ebp"
set spreg "esp"
set pcreg "eip"
+} elseif [is_aarch32_target] {
+ set fpreg "sp"
} elseif [is_aarch64_target] {
set fpreg "x29"
}
diff --git a/gdb/testsuite/gdb.trace/while-dyn.exp b/gdb/testsuite/gdb.trace/while-dyn.exp
index fe4535e..5b9713f 100644
--- a/gdb/testsuite/gdb.trace/while-dyn.exp
+++ b/gdb/testsuite/gdb.trace/while-dyn.exp
@@ -47,6 +47,8 @@ if [is_amd64_regs_target] {
set fpreg "\$rbp"
} elseif [is_x86_like_target] {
set fpreg "\$ebp"
+} elseif [is_aarch32_target] {
+ set fpreg "\$sp"
} elseif [is_aarch64_target] {
set fpreg "\$x29"
} else {
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 6/7] Support conditional breakpoints on targets that can software single step in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (4 preceding siblings ...)
2015-09-11 12:14 ` [PATCH 5/7] Add support for software single step on ARM aarch32-linux " Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-11 12:14 ` [PATCH 4/7] Make breakpoint and breakpoint_len local variables " Antoine Tremblay
2015-09-14 10:33 ` [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux " Yao Qi
7 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
First a bit of background :
Since this patch https://sourceware.org/ml/gdb-patches/2015-04/msg01110.html
conditional breakpoints have been disabled on targets that do not support
software single step properly.
This was due to the get_next_pcs beeing to simple to be used or broken entirely.
This in effect made the get_next_pcs callback implemented on those targets dead
code since there is no situation in which a step over will occur as there is also
no software breakpoint support on those targets.
In fact the only purpose of the code after this change is to populate the
function pointer so that can_hardware_single_step can return false since the
previous way to know if a target supported hardware single step was to check if
a software single step function was implemented.
This patch cleans this up a bit so that targets that do support software single
step properly can enable conditional breakpoints and that we do not keep dead
code. It also makes it clear that if a target can't hardware or software single
step memory breakpoints are not supported.
To do this, on linux a new low target callback supports_hardware_single_step is
implemented and is to return true if the target does support such a
feature. This way can_hardware_single_step will use that callback rather than
check if the get_next_pcs implementation is present.
This enables the bad get_next_pcs functions to be removed and to be able to
trust that if get_next_pcs is implemented it means that the target really
supports software single step and that we can use it for conditional breakpoints
for example.
Note that the hardware single step support was enabled as per the current
behavior, I did not check if tile for example really has ptrace singlestep
support but since the current implementation assumed it had I kept it that way.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
Also compilation was tested on aarch64, bfin, cris, crisv32,
m32r, mips, nios2, ppc, s390, sparc, tic6x, tile, xtensa
gdb/gdbserver/ChangeLog:
* linux-aarch64-low.c
(aarch64_supports_hardware_single_step): New function.
(struct linux_target_ops): New field supports_hardware_single_step.
* linux-arm-low.c (arm_supports_hardware_single_step): New function.
* linux-bfin-low.c (bfin_supports_hardware_single_step): Likewise.
* linux-cris-low.c (cris_get_next_pcs): Remove.
* linux-crisv32-low.c
(cris_supports_hardware_single_step): New function.
* linux-low.c (can_hardware_single_step): Use supports_hardware_single_step.
(can_software_single_step): New function.
(supports_breakpoints): Add single step support requirement.
(linux_resume_one_lwp_throw): Add error if target can't single step.
(start_step_over): Likewise.
(linux_supports_software_single_step): New function.
* linux-m32r-low.c (m32r_supports_hardware_single_step): New function.
* linux-mips-low.c (mips_get_next_pcs): Remove.
* linux-nios2-low.c (nios2_get_next_pcs): Remove.
* linux-ppc-low.c (ppc_supports_hardware_single_step): New function.
* linux-s390-low.c (s390_supports_hardware_single_step): Likewise.
* linux-sh-low.c (sh_supports_hardware_single_step): Likewise.
* linux-sparc-low.c (sparc_get_next_pcs): Remove.
* linux-tic6x-low.c (tic6x_supports_hardware_single_step): New function.
* linux-tile-low.c (tile_supports_hardware_single_step): Likewise.
* linux-x86-low.c (x86_supports_hardware_single_step): Likewise.
* linux-xtensa-low.c (xtensa_supports_hardware_single_step): Likewise.
* server.c (handle_query): Support conditional breakpoints for software
single step.
* target.c (target_can_do_software_single_step): New function.
* target.h (struct target_ops): New field target_can_do_software_single_step.
---
gdb/gdbserver/linux-aarch64-low.c | 9 +++++++
gdb/gdbserver/linux-arm-low.c | 9 +++++++
gdb/gdbserver/linux-bfin-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-cris-low.c | 18 +------------
gdb/gdbserver/linux-crisv32-low.c | 41 +++++++++++++++++-------------
gdb/gdbserver/linux-low.c | 53 +++++++++++++++++++++++++++++++--------
gdb/gdbserver/linux-low.h | 3 +++
gdb/gdbserver/linux-m32r-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-mips-low.c | 17 +------------
gdb/gdbserver/linux-nios2-low.c | 14 +----------
gdb/gdbserver/linux-ppc-low.c | 21 ++++++++++++++++
gdb/gdbserver/linux-s390-low.c | 21 ++++++++++++++++
gdb/gdbserver/linux-sh-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-sparc-low.c | 15 +----------
gdb/gdbserver/linux-tic6x-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-tile-low.c | 30 +++++++++++++++++++++-
gdb/gdbserver/linux-x86-low.c | 9 +++++++
gdb/gdbserver/linux-xtensa-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/server.c | 8 ++----
gdb/gdbserver/target.c | 7 ++++++
gdb/gdbserver/target.h | 9 +++++++
21 files changed, 329 insertions(+), 95 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 2956211..567ab98 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -571,6 +571,14 @@ aarch64_supports_range_stepping (void)
return 1;
}
+/* Support for hardware single step. */
+
+static int
+aarch64_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
struct linux_target_ops the_low_target =
{
aarch64_arch_setup,
@@ -603,6 +611,7 @@ struct linux_target_ops the_low_target =
NULL, /* emit_ops */
NULL, /* get_min_fast_tracepoint_insn_len */
aarch64_supports_range_stepping,
+ aarch64_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index eb98a36..a9ed91d 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -1112,6 +1112,14 @@ arm_arch_setup (void)
arm_set_byte_order ();
}
+/* Support for hardware single step. */
+
+static int
+arm_supports_hardware_single_step (void)
+{
+ return 0;
+}
+
/* Register sets without using PTRACE_GETREGSET. */
static struct regset_info arm_regsets[] = {
@@ -1190,6 +1198,7 @@ struct linux_target_ops the_low_target = {
NULL, /* emit_ops */
NULL, /* get_min_fast_tracepoint_insn_len */
NULL, /* supports_range_stepping */
+ arm_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 3032a34..18b8e43 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -103,6 +103,14 @@ bfin_arch_setup (void)
current_process ()->tdesc = tdesc_bfin;
}
+/* Support for hardware single step. */
+
+static int
+bfin_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct usrregs_info bfin_usrregs_info =
{
bfin_num_regs,
@@ -133,6 +141,26 @@ struct linux_target_ops the_low_target = {
NULL, /* get_next_pcs */
2,
bfin_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ bfin_supports_hardware_single_step,
};
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index cd1ae4d..b56827e 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -104,18 +104,6 @@ cris_breakpoint_at (CORE_ADDR where)
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static void
-cris_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- CORE_ADDR pc;
- collect_register_by_name (regcache, "srp", &pc);
- VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
-}
-
static void
cris_arch_setup (void)
{
@@ -149,13 +137,9 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_get_next_pcs,
+ NULL,
0,
cris_breakpoint_at,
- 0,
- 0,
- 0,
- 0,
};
void
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 05ad85a..4e9ebc1 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -100,22 +100,6 @@ cris_breakpoint_at (CORE_ADDR where)
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-
-/* FIXME: This function should not be needed, since we have PTRACE_SINGLESTEP
- for CRISv32. Without it, td_ta_event_getmsg in thread_db_create_event
- will fail when debugging multi-threaded applications. */
-static void
-cris_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- CORE_ADDR pc;
- collect_register_by_name (regcache, "srp", &pc);
- VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
-}
-
static void
cris_write_data_breakpoint (struct regcache *regcache,
int bp, unsigned long start, unsigned long end)
@@ -386,6 +370,14 @@ cris_arch_setup (void)
current_process ()->tdesc = tdesc_crisv32;
}
+/* Support for hardware single step. */
+
+static int
+cris_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct regset_info cris_regsets[] = {
{ PTRACE_GETREGS, PTRACE_SETREGS, 0, cris_num_regs * 4,
GENERAL_REGS, cris_fill_gregset, cris_store_gregset },
@@ -428,7 +420,7 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_get_next_pcs,
+ NULL,
0,
cris_breakpoint_at,
cris_supports_z_point_type,
@@ -436,6 +428,21 @@ struct linux_target_ops the_low_target = {
cris_remove_point,
cris_stopped_by_watchpoint,
cris_stopped_data_address,
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ cris_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 229df46..f19769d 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -272,22 +272,35 @@ static void complete_ongoing_step_over (void);
being stepped. */
ptid_t step_over_bkpt;
-/* True if the low target can hardware single-step. Such targets
- don't need a GET_NEXT_PCS callback. */
+/* True if the low target can hardware single-step. */
static int
can_hardware_single_step (void)
{
- return (the_low_target.get_next_pcs == NULL);
+ if (the_low_target.supports_hardware_single_step != NULL)
+ return the_low_target.supports_hardware_single_step ();
+ else
+ return 0;
+}
+
+/* True if the low target can software single-step. Such targets
+ implement the GET_NEXT_PCS callback. */
+
+static int
+can_software_single_step (void)
+{
+ return (the_low_target.get_next_pcs != NULL);
}
-/* True if the low target supports memory breakpoints. If so, we'll
- have a GET_PC implementation. */
+/* True if the low target supports memory breakpoints. If so, we'll have a
+ GET_PC implementation. We will also need to support hardware or software
+ single stepping. */
static int
supports_breakpoints (void)
{
- return (the_low_target.get_pc != NULL);
+ return (the_low_target.get_pc != NULL &&
+ (can_hardware_single_step () || can_software_single_step ()));
}
/* Returns true if this target can support fast tracepoints. This
@@ -4026,10 +4039,16 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
{
step = 1;
}
- else {
- install_software_single_step_breakpoints (lwp);
- step = 0;
- }
+ else if (can_software_single_step ())
+ {
+ install_software_single_step_breakpoints (lwp);
+ step = 0;
+ }
+ else
+ {
+ internal_error (__FILE__, __LINE__,
+ "stepping is not implemented on this target");
+ }
}
if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
@@ -4439,11 +4458,16 @@ start_step_over (struct lwp_info *lwp)
{
step = 1;
}
- else
+ else if (can_software_single_step ())
{
install_software_single_step_breakpoints (lwp);
step = 0;
}
+ else
+ {
+ internal_error (__FILE__, __LINE__,
+ "stepping is not implemented on this target");
+ }
current_thread = saved_thread;
@@ -5638,6 +5662,12 @@ linux_supports_hardware_single_step (void)
}
static int
+linux_supports_software_single_step (void)
+{
+ return can_software_single_step ();
+}
+
+static int
linux_stopped_by_watchpoint (void)
{
struct lwp_info *lwp = get_thread_lwp (current_thread);
@@ -7064,6 +7094,7 @@ static struct target_ops linux_target_ops = {
linux_mntns_unlink,
linux_mntns_readlink,
linux_breakpoint_from_pc,
+ linux_supports_software_single_step,
};
static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 597c36d..b6f8175 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -240,6 +240,9 @@ struct linux_target_ops
/* Returns true if the low target supports range stepping. */
int (*supports_range_stepping) (void);
+
+ /* Returns true if the low target supports hardware single step. */
+ int (*supports_hardware_single_step) (void);
};
extern struct linux_target_ops the_low_target;
diff --git a/gdb/gdbserver/linux-m32r-low.c b/gdb/gdbserver/linux-m32r-low.c
index 4712a32..3767f35 100644
--- a/gdb/gdbserver/linux-m32r-low.c
+++ b/gdb/gdbserver/linux-m32r-low.c
@@ -101,6 +101,14 @@ m32r_arch_setup (void)
current_process ()->tdesc = tdesc_m32r;
}
+/* Support for hardware single step. */
+
+static int
+m32r_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct usrregs_info m32r_usrregs_info =
{
m32r_num_regs,
@@ -131,6 +139,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
m32r_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ m32r_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d462f2c..e752b7e 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -274,21 +274,6 @@ mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
return (const unsigned char *) &mips_breakpoint;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static void
-mips_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- CORE_ADDR result;
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- union mips_register ra;
- collect_register_by_name (regcache, "r31", ra.buf);
- result = register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64;
-
- VEC_safe_push (CORE_ADDR, next_pcs->result, result);
-}
-
static int
mips_breakpoint_at (CORE_ADDR where)
{
@@ -893,7 +878,7 @@ struct linux_target_ops the_low_target = {
mips_get_pc,
mips_set_pc,
mips_breakpoint_from_pc,
- mips_get_next_pcs,
+ NULL, /* get_next_pcs */
0,
mips_breakpoint_at,
mips_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index a24eb23..cb3addd 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -146,18 +146,6 @@ nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
return (const unsigned char *) &nios2_breakpoint;
}
-/* Implement the get_next_pcs linux_target_ops method. */
-
-static void
-nios2_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- union nios2_register ra;
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
-
- collect_register_by_name (regcache, "ra", ra.buf);
- VEC_safe_push (CORE_ADDR, next_pcs->result, ra.reg32);
-}
-
/* Implement the breakpoint_at linux_target_ops method. */
static int
@@ -280,7 +268,7 @@ struct linux_target_ops the_low_target =
nios2_get_pc,
nios2_set_pc,
nios2_breakpoint_from_pc,
- nios2_get_next_pcs,
+ NULL, /* get_next_pcs */
0,
nios2_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index fa87e19..4939cfa 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -641,6 +641,14 @@ ppc_store_evrregset (struct regcache *regcache, const void *buf)
supply_register_by_name (regcache, "spefscr", ®set->spefscr);
}
+/* Support for hardware single step. */
+
+static int
+ppc_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct regset_info ppc_regsets[] = {
/* List the extra register sets before GENERAL_REGS. That way we will
fetch them every time, but still fall back to PTRACE_PEEKUSER for the
@@ -701,6 +709,19 @@ struct linux_target_ops the_low_target = {
NULL,
ppc_collect_ptrace_register,
ppc_supply_ptrace_register,
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ ppc_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index 44d3f26..794a621 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -611,6 +611,14 @@ s390_breakpoint_at (CORE_ADDR pc)
return memcmp (c, s390_breakpoint, s390_breakpoint_len) == 0;
}
+/* Support for hardware single step. */
+
+static int
+s390_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct usrregs_info s390_usrregs_info =
{
s390_num_regs,
@@ -687,6 +695,19 @@ struct linux_target_ops the_low_target = {
NULL,
s390_collect_ptrace_register,
s390_supply_ptrace_register,
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ s390_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 5c11fd7..6a210bf 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -98,6 +98,14 @@ sh_breakpoint_at (CORE_ADDR where)
return 0;
}
+/* Support for hardware single step. */
+
+static int
+sh_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
/* Provide only a fill function for the general register set. ps_lgetregs
will use this for NPTL support. */
@@ -159,6 +167,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
sh_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ sh_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index e804281..25546e3 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -264,19 +264,6 @@ sparc_breakpoint_at (CORE_ADDR where)
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static void
-sparc_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- CORE_ADDR lr;
- /* O7 is the equivalent to the 'lr' of other archs. */
- collect_register_by_name (regcache, "o7", &lr);
- VEC_safe_push (CORE_ADDR, next_pcs->result, lr);
-}
-
static void
sparc_arch_setup (void)
{
@@ -331,7 +318,7 @@ struct linux_target_ops the_low_target = {
/* No sparc_set_pc is needed. */
NULL,
sparc_breakpoint_from_pc,
- sparc_get_next_pcs,
+ NULL,
0,
sparc_breakpoint_at,
NULL, /* supports_z_point_type */
diff --git a/gdb/gdbserver/linux-tic6x-low.c b/gdb/gdbserver/linux-tic6x-low.c
index 86b433c..22bffe2 100644
--- a/gdb/gdbserver/linux-tic6x-low.c
+++ b/gdb/gdbserver/linux-tic6x-low.c
@@ -339,6 +339,14 @@ tic6x_arch_setup (void)
current_process ()->tdesc = tic6x_read_description ();
}
+/* Support for hardware single step. */
+
+static int
+tic6x_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct regsets_info tic6x_regsets_info =
{
tic6x_regsets, /* regsets */
@@ -377,6 +385,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
tic6x_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ tic6x_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-tile-low.c b/gdb/gdbserver/linux-tile-low.c
index 802812b..b712b81 100644
--- a/gdb/gdbserver/linux-tile-low.c
+++ b/gdb/gdbserver/linux-tile-low.c
@@ -179,6 +179,14 @@ tile_arch_setup (void)
current_process ()->tdesc = tdesc_tilegx;
}
+/* Support for hardware single step. */
+
+static int
+tile_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
struct linux_target_ops the_low_target =
{
@@ -190,9 +198,29 @@ struct linux_target_ops the_low_target =
tile_get_pc,
tile_set_pc,
tile_breakpoint_from_pc,
- NULL,
+ NULL, /* get_next_pcs */
0,
tile_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ tile_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 0731141..0c5b6a2 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3256,6 +3256,14 @@ x86_supports_range_stepping (void)
return 1;
}
+/* Support for hardware single step. */
+
+static int
+x86_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
/* This is initialized assuming an amd64 target.
x86_arch_setup will correct it for i386 or amd64 targets. */
@@ -3295,6 +3303,7 @@ struct linux_target_ops the_low_target =
x86_emit_ops,
x86_get_min_fast_tracepoint_insn_len,
x86_supports_range_stepping,
+ x86_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index 1e2cf94..432b1b7 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -227,6 +227,14 @@ xtensa_arch_setup (void)
current_process ()->tdesc = tdesc_xtensa;
}
+/* Support for hardware single step. */
+
+static int
+xtensa_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static const struct regs_info *
xtensa_regs_info (void)
{
@@ -245,6 +253,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
xtensa_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ xtensa_supports_hardware_single_step,
};
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 5c0d83d..fb0e843 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2192,13 +2192,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
strcat (own_buf, ";tracenz+");
}
- if (target_supports_hardware_single_step ())
+ if (target_supports_hardware_single_step () ||
+ target_supports_software_single_step () )
{
- /* Support target-side breakpoint conditions and commands.
- GDBserver needs to step over the breakpoint if the condition
- is false. GDBserver software single step is too simple, so
- disable conditional breakpoints if the target doesn't have
- hardware single step. */
strcat (own_buf, ";ConditionalBreakpoints+");
}
strcat (own_buf, ";BreakpointCommands+");
diff --git a/gdb/gdbserver/target.c b/gdb/gdbserver/target.c
index 17ff7a6..1ae42cc 100644
--- a/gdb/gdbserver/target.c
+++ b/gdb/gdbserver/target.c
@@ -224,3 +224,10 @@ target_can_do_hardware_single_step (void)
{
return 1;
}
+
+/* Target can do software single step. */
+int
+target_can_do_software_single_step (void)
+{
+ return 0;
+}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index fc2dbd7..aa40c4a 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -443,6 +443,9 @@ struct target_ops
can be NULL, the default breakpoint for the target should be returned in
this case. */
const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
+ /* Returns true if the target can do software single step. */
+ int (*supports_software_single_step) (void);
};
extern struct target_ops *the_target;
@@ -621,6 +624,10 @@ int kill_inferior (int);
(the_target->stopped_by_hw_breakpoint ? \
(*the_target->stopped_by_hw_breakpoint) () : 0)
+#define target_supports_software_single_step() \
+ (the_target->supports_software_single_step ? \
+ (*the_target->supports_software_single_step) () : 0)
+
/* Start non-stop mode, returns 0 on success, -1 on failure. */
int start_non_stop (int nonstop);
@@ -655,4 +662,6 @@ const char *target_pid_to_str (ptid_t);
int target_can_do_hardware_single_step (void);
+int target_can_do_software_single_step (void);
+
#endif /* TARGET_H */
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (3 preceding siblings ...)
2015-09-11 12:14 ` [PATCH 3/7] Support multiple breakpoint types per target " Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-14 11:00 ` Yao Qi
2015-09-11 12:14 ` [PATCH 6/7] Support conditional breakpoints on targets that can software single step " Antoine Tremblay
` (2 subsequent siblings)
7 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch teaches GDBserver how to software single step on ARM
aarch32-linux by sharing the code with GDB.
There's multiple things to consider to achieve that :
- GDBServer needs to know the next PCs to put a breakpoint on for stepping.
- GDBServer needs to handle multiple addresses to put a breakpoint on.
- GDBServer needs to know what kind of breakpoint to insert since breakpoints
can differ based on the instruction set used on ARM.
- GDBServer needs to know the correct endianness to write that breakpoint
properly.
The next PC issue is resolved by sharing arm_get_next_pcs function in GDB.
Since this function will return the possible addresses of the next PC.
Since there can be multiple addresses returned by the get_next_pcs function,
GDBServer need to handle this situation. To achieve this, the target ops :
breakpoint_reinsert_addr is replaced with get_next_pcs. This function fills the
get_next_pcs struct that contains a vector of all the pcs to put a breakpoint
on. arm_software_single_step then iterates on that vector and inserts
the right breakpoint.
The type of breakpoint issue in GDBServer is handled by the
breakpoint_from_pc target ops introduced in a previous patch, this
patch adds the arm_breakpoint_from_pc implementation so that the
proper breakpoint type is returned based on the current pc and
register values.
Also, to set the right breakpoint on ARM the endianness of the program is needed
so that we write the breakpoint bytes in the right order. To achieve this the
arm_set_byte_order function is called and reads from the ELF header the
endianness of the code.
So this patch teaches GDBserver how to read the elf header for 32 and 64 bit
ELF.
In order to share code between GDB and GDBServer, some code needs to be moved
around to the common subdir, here's how the files are arranged and their
function :
get-next-pcs.h : Common code for software single stepping support.
arm-common.h/c : Common code for generic ARM support.
arm-get-next-pcs.h/c : Common code for ARM software single stepping support.
arm-linux-common.h : Common code for GNU/Linux on ARM.
Note that the arm* files could potentially be in an common/arch/ directory but
as there's only one needed now I though it would be ok in common.
A proper shared context was also needed so that we could share the code, this
context is described as this data structure (expressed as a class
hierarchy) :
struct get_next_pcs
struct arch_get_next_pcs
struct gdb|gdbserver_arch_get_next_pcs
Where arch can by replaced by arm for this patch. This structure should be
flexible enough to accomodate any arch or os that would need a get_next_pcs
context.
For ARM this context contains a series of operations and variables so that the
shared functions can be called from GDB or GDBServer.
Limitations :
GDBServer can't step over a sigreturn or rt_sigreturn syscall since this would
require the DWARF information to identify the caller PC. As such the
get_next_pcs sigreturn_return_addr operation is set to NULL in GDBServer for the
moment.
Testing :
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
Also compilation was tested on aarch32, aarch64, arm, bfin, cris,
crisv32, mips, nios2, ppc, s390, sparc, x86 as those archs were modified.
gdb/ChangeLog:
* Makefile.in: Add arm-get-next-pcs.c/o arm-common.c/o.
* arm-linux-nat.c: Add includes for common/arm*.
* arm-linux-tdep.c (arm_linux_sigreturn_return_addr):
Move part of the code in arm_syscalL_next_pc.
(arm_linux_software_single_step): Refactor to use get_next_pcs context.
(arm_linux_init_abi): Change syscall_next_pc for sigreturn_return_addr.
* arm-tdep.c (thumb_instruction_changes_pc): Move to arm-get-next-pcs.c.
(thumb2_instruction_changes_pc): Likewise.
(arm_instruction_changes_pc): Likewise.
(condition_true): Move to arm-common.c.
(thumb_insn_size): Likewise.
(thumb_advance_itstate): Move to arm-get-next-pcs.c.
(thumb_get_next_pc_raw): Likewise.
(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_software_single_step): Refactor to use get_next_pcs context.
(arm_get_next_pcs_addr_bits_remove): New function.
(arm_get_next_pcs_collect_register_unsigned): New function.
* arm-tdep.h (struct gdbarch_tdep): Change syscall_next_pc for sigreturn_return_addr.
* arm-wince-tdep.c: Add include common/arm-common.h.
* armnbsd-tdep.c: Likewise.
* common/arm-common.c: New file.
* common/arm-common.h: New file.
* common/arm-get-next-pcs.c: New file.
* common/arm-get-next-pcs.h: New file.
* common/arm-linux-common.h: New file.
* common/common-defs.h: Add MAX_REGISTER_SIZE.
* common/get-next-pcs.h: New file.
* configure.tgt: Add to arm*-*-linux* arm-common.o arm-get-next-pcs.o.
* defs.h: Move MAX_REGISTER_SIZE to common-defs.h.
gdbserver/ChangeLog:
* Makefile.in: Add common/arm-get-next-pcs.c/o,
common/int-utils.c/o, common/arm-common.c/o. common/int-utils.c/o.
* configure.srv: Add arm-get-next-pcs.o, arm-common.o, int-utils.o.
* linux-arm-low.c (arm_addr_bits_remove): New function.
(arm_breakpoint_at): Use new breakpoint byte arrays.
(get_next_pcs_collect_register_unsigned): New function.
(read_memory_unsigned_integer): New function.
(arm_breakpoint_from_pc): Likewise.
(arm_set_breakpoints): Likewise.
(arm_set_byte_order): Likewise.
(arm_gdbserver_get_next_pcs): Likewise.
(struct linux_target_ops): Change arm_reinsert_addr to
arm_gdbserver_get_next_pcs.
* linux-cris-low.c (cris_get_next_pcs): New function.
* linux-crisv32-low.c (cris_get_next_pcs): New function.
* linux-low.c (can_hardware_single_step): Change
breakpoint_reinsert_addr to get_next_pcs.
(elf_64_read_header): New function.
(elf_64_file_read_header): Likewise.
(elf_32_header_p): Likewise.
(elf_32_read_header): Likewise.
(elf_32_file_read_header): Likewise.
(elf_64_file_p): Likewise.
(install_software_single_step_breakpoints): Likewise.
(linux_resume_one_lwp_throw): Try to software single step if
we can't hardware single step.
(start_step_over): Use install_software_single_step_breakpoints.
* linux-low.h: Add elf.h include.
* linux-mips-low.c (mips_get_next_pcs): New function.
* linux-nios2-low.c (nios2_get_next_pcs): Likewise.
* linux-ppc-low.c: Adjust elf includes.
* linux-s390-low.c: Likewise.
* linux-sparc-low.c (sparc_get_next_pcs): Likewise.
* linux-x86-low.c: Adjust elf includes.
---
gdb/Makefile.in | 16 +-
gdb/arm-linux-nat.c | 3 +
gdb/arm-linux-tdep.c | 148 ++--
gdb/arm-tdep.c | 1534 +++++--------------------------------
gdb/arm-tdep.h | 61 +-
gdb/arm-wince-tdep.c | 1 +
gdb/armnbsd-tdep.c | 1 +
gdb/common/arm-common.c | 90 +++
gdb/common/arm-common.h | 64 ++
gdb/common/arm-get-next-pcs.c | 1273 ++++++++++++++++++++++++++++++
gdb/common/arm-get-next-pcs.h | 112 +++
gdb/common/arm-linux-common.h | 72 ++
gdb/common/common-defs.h | 6 +
gdb/common/get-next-pcs.h | 36 +
gdb/configure.tgt | 3 +-
gdb/defs.h | 5 -
gdb/gdbserver/Makefile.in | 13 +-
gdb/gdbserver/configure.srv | 3 +
gdb/gdbserver/linux-aarch32-low.c | 5 -
gdb/gdbserver/linux-aarch64-low.c | 6 +-
gdb/gdbserver/linux-arm-low.c | 344 +++++++--
gdb/gdbserver/linux-bfin-low.c | 2 +-
gdb/gdbserver/linux-cris-low.c | 11 +-
gdb/gdbserver/linux-crisv32-low.c | 12 +-
gdb/gdbserver/linux-low.c | 146 +++-
gdb/gdbserver/linux-low.h | 18 +-
gdb/gdbserver/linux-mips-low.c | 12 +-
gdb/gdbserver/linux-nios2-low.c | 15 +-
gdb/gdbserver/linux-ppc-low.c | 2 -
gdb/gdbserver/linux-s390-low.c | 6 +-
gdb/gdbserver/linux-sparc-low.c | 9 +-
gdb/gdbserver/linux-x86-low.c | 2 +-
32 files changed, 2415 insertions(+), 1616 deletions(-)
create mode 100644 gdb/common/arm-common.c
create mode 100644 gdb/common/arm-common.h
create mode 100644 gdb/common/arm-get-next-pcs.c
create mode 100644 gdb/common/arm-get-next-pcs.h
create mode 100644 gdb/common/arm-linux-common.h
create mode 100644 gdb/common/get-next-pcs.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index e20c5a6..ffd78a9 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -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-linux-tdep.o arm-symbian-tdep.o \
+ armbsd-tdep.o arm-common.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,7 +1662,9 @@ ALLDEPFILES = \
amd64-dicos-tdep.c \
amd64-linux-nat.c amd64-linux-tdep.c \
amd64-sol2-tdep.c \
- arm-linux-nat.c arm-linux-tdep.c arm-symbian-tdep.c arm-tdep.c \
+ common/arm-common.c common/arm-get-next-pcs.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 \
@@ -2266,10 +2269,19 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
fileio.o: ${srcdir}/common/fileio.c
$(COMPILE) $(srcdir)/common/fileio.c
$(POSTCOMPILE)
+
+arm-get-next-pcs.o: ${srcdir}/common/arm-get-next-pcs.c
+ $(COMPILE) $(srcdir)/common/arm-get-next-pcs.c
+ $(POSTCOMPILE)
+
int-utils.o: ${srcdir}/common/int-utils.c
$(COMPILE) $(srcdir)/common/int-utils.c
$(POSTCOMPILE)
+arm-common.o: ${srcdir}/common/arm-common.c
+ $(COMPILE) $(srcdir)/common/arm-common.c
+ $(POSTCOMPILE)
+
#
# gdb/target/ dependencies
#
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
index a63b181..d6afafb 100644
--- a/gdb/arm-linux-nat.c
+++ b/gdb/arm-linux-nat.c
@@ -27,6 +27,9 @@
#include "observer.h"
#include "gdbthread.h"
+#include "common/arm-common.h"
+#include "common/arm-linux-common.h"
+#include "common/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
#include "aarch32-linux-nat.h"
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index b3ad868..88c1510 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -35,6 +35,9 @@
#include "auxv.h"
#include "xml-syscall.h"
+#include "common/arm-common.h"
+#include "common/arm-linux-common.h"
+#include "common/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
#include "linux-tdep.h"
@@ -54,42 +57,8 @@
#include "user-regs.h"
#include <ctype.h>
#include "elf/common.h"
-extern int arm_apcs_32;
-
-/* Under ARM GNU/Linux the traditional way of performing a breakpoint
- is to execute a particular software interrupt, rather than use a
- particular undefined instruction to provoke a trap. Upon exection
- of the software interrupt the kernel stops the inferior with a
- SIGTRAP, and wakes the debugger. */
-
-static const gdb_byte arm_linux_arm_le_breakpoint[] = { 0x01, 0x00, 0x9f, 0xef };
-
-static const gdb_byte arm_linux_arm_be_breakpoint[] = { 0xef, 0x9f, 0x00, 0x01 };
-
-/* However, the EABI syscall interface (new in Nov. 2005) does not look at
- the operand of the swi if old-ABI compatibility is disabled. Therefore,
- use an undefined instruction instead. This is supported as of kernel
- version 2.5.70 (May 2003), so should be a safe assumption for EABI
- binaries. */
-
-static const gdb_byte eabi_linux_arm_le_breakpoint[] = { 0xf0, 0x01, 0xf0, 0xe7 };
-
-static const gdb_byte eabi_linux_arm_be_breakpoint[] = { 0xe7, 0xf0, 0x01, 0xf0 };
-/* All the kernels which support Thumb support using a specific undefined
- instruction for the Thumb breakpoint. */
-
-static const gdb_byte arm_linux_thumb_be_breakpoint[] = {0xde, 0x01};
-
-static const gdb_byte arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
-
-/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
- we must use a length-appropriate breakpoint for 32-bit Thumb
- instructions. See also thumb_get_next_pc. */
-
-static const gdb_byte arm_linux_thumb2_be_breakpoint[] = { 0xf7, 0xf0, 0xa0, 0x00 };
-
-static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
+extern int arm_apcs_32;
/* Description of the longjmp buffer. The buffer is treated as an array of
elements of size ARM_LINUX_JB_ELEMENT_SIZE.
@@ -257,6 +226,14 @@ static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa
#define ARM_LDR_PC_SP_12 0xe49df00c
#define ARM_LDR_PC_SP_4 0xe49df004
+/* Operation function pointers for get_next_pcs. */
+struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = {
+ read_memory_unsigned_integer,
+ arm_get_next_pcs_collect_register_unsigned,
+ arm_get_next_pcs_sigreturn_return_addr,
+ arm_get_next_pcs_addr_bits_remove
+};
+
static void
arm_linux_sigtramp_cache (struct frame_info *this_frame,
struct trad_frame_cache *this_cache,
@@ -778,20 +755,19 @@ arm_linux_core_read_description (struct gdbarch *gdbarch,
return NULL;
}
-
/* Copy the value of next pc of sigreturn and rt_sigrturn into PC,
return 1. In addition, set IS_THUMB depending on whether we
will return to ARM or Thumb code. Return 0 if it is not a
rt_sigreturn/sigreturn syscall. */
static int
-arm_linux_sigreturn_return_addr (struct frame_info *frame,
+arm_linux_sigreturn_return_addr (struct frame_info* frame,
unsigned long svc_number,
CORE_ADDR *pc, int *is_thumb)
{
/* Is this a sigreturn or rt_sigreturn syscall? */
if (svc_number == 119 || svc_number == 173)
{
- if (get_frame_type (frame) == SIGTRAMP_FRAME)
+ if (get_frame_type (frame) == SIGTRAMP_FRAME)
{
ULONGEST t_bit = arm_psr_thumb_bit (frame_unwind_arch (frame));
CORE_ADDR cpsr
@@ -858,53 +834,6 @@ arm_linux_get_syscall_number (struct gdbarch *gdbarch,
return svc_number;
}
-/* When FRAME is at a syscall instruction, return the PC of the next
- instruction to be executed. */
-
-static CORE_ADDR
-arm_linux_syscall_next_pc (struct frame_info *frame)
-{
- CORE_ADDR pc = get_frame_pc (frame);
- CORE_ADDR return_addr = 0;
- int is_thumb = arm_frame_is_thumb (frame);
- ULONGEST svc_number = 0;
-
- if (is_thumb)
- {
- svc_number = get_frame_register_unsigned (frame, 7);
- return_addr = pc + 2;
- }
- else
- {
- struct gdbarch *gdbarch = get_frame_arch (frame);
- enum bfd_endian byte_order_for_code =
- gdbarch_byte_order_for_code (gdbarch);
- unsigned long this_instr =
- read_memory_unsigned_integer (pc, 4, byte_order_for_code);
-
- unsigned long svc_operand = (0x00ffffff & this_instr);
- if (svc_operand) /* OABI. */
- {
- svc_number = svc_operand - 0x900000;
- }
- else /* EABI. */
- {
- svc_number = get_frame_register_unsigned (frame, 7);
- }
-
- return_addr = pc + 4;
- }
-
- arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
-
- /* Addresses for calling Thumb functions have the bit 0 set. */
- if (is_thumb)
- return_addr |= 1;
-
- return return_addr;
-}
-
-
/* Insert a single step breakpoint at the next executed instruction. */
static int
@@ -912,22 +841,41 @@ arm_linux_software_single_step (struct frame_info *frame)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
struct address_space *aspace = get_frame_address_space (frame);
- CORE_ADDR next_pc;
-
- if (arm_deal_with_atomic_sequence (frame))
- return 1;
-
- next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+ struct arm_gdb_get_next_pcs next_pcs;
+ CORE_ADDR pc;
+ int 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 (next_pc > 0xffff0000)
- next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+ next_pcs.base.base.result = VEC_alloc (CORE_ADDR, 1);
+ next_pcs.gdbarch = gdbarch;
+ next_pcs.frame = frame;
+ next_pcs.base.ops = &arm_linux_get_next_pcs_ops;
+ next_pcs.base.byte_order = gdbarch_byte_order (gdbarch);
+ next_pcs.base.byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ next_pcs.base.is_thumb = arm_frame_is_thumb (frame);
+ next_pcs.base.arm_apcs_32 = arm_apcs_32;
+ next_pcs.base.base.pc = get_frame_pc (frame);
+ next_pcs.base.arm_linux_thumb2_breakpoint =
+ gdbarch_tdep (gdbarch)->thumb2_breakpoint;
+
+ arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs);
+
+ for (i = 0;
+ VEC_iterate (CORE_ADDR, next_pcs.base.base.result, 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);
+ }
- arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+ VEC_free (CORE_ADDR, next_pcs.base.base.result);
return 1;
}
@@ -1462,7 +1410,7 @@ arm_linux_init_abi (struct gdbarch_info info,
set_gdbarch_stap_parse_special_token (gdbarch,
arm_stap_parse_special_token);
- tdep->syscall_next_pc = arm_linux_syscall_next_pc;
+ tdep->sigreturn_return_addr = arm_linux_sigreturn_return_addr;
/* `catch syscall' */
set_xml_syscall_file_name (gdbarch, "syscalls/arm-linux.xml");
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index bcee29c..e99b49f 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -45,6 +45,8 @@
#include "user-regs.h"
#include "observer.h"
+#include "common/arm-common.h"
+#include "common/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "gdb/sim-arm.h"
@@ -235,7 +237,13 @@ static void arm_neon_quad_write (struct gdbarch *gdbarch,
struct regcache *regcache,
int regnum, const gdb_byte *buf);
-static int thumb_insn_size (unsigned short inst1);
+/* Operation function pointers for get_next_pcs. */
+struct arm_get_next_pcs_ops arm_get_next_pcs_ops = {
+ read_memory_unsigned_integer,
+ arm_get_next_pcs_collect_register_unsigned,
+ arm_get_next_pcs_sigreturn_return_addr,
+ arm_get_next_pcs_addr_bits_remove
+};
struct arm_prologue_cache
{
@@ -267,14 +275,7 @@ static CORE_ADDR arm_analyze_prologue (struct gdbarch *gdbarch,
#define DISPLACED_STEPPING_ARCH_VERSION 5
-/* Addresses for calling Thumb functions have the bit 0 set.
- Here are some macros to test, set, or clear bit 0 of addresses. */
-#define IS_THUMB_ADDR(addr) ((addr) & 1)
-#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
-#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
-
/* Set to true if the 32-bit mode is in use. */
-
int arm_apcs_32 = 1;
/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */
@@ -513,15 +514,6 @@ skip_prologue_function (struct gdbarch *gdbarch, CORE_ADDR pc, int is_thumb)
return 0;
}
-/* Support routines for instruction parsing. */
-#define submask(x) ((1L << ((x) + 1)) - 1)
-#define bit(obj,st) (((obj) >> (st)) & 1)
-#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
-#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)))
-
/* Extract the immediate from instruction movw/movt of encoding T. INSN1 is
the first 16-bit of instruction, and INSN2 is the second 16-bit of
instruction. */
@@ -561,128 +553,6 @@ thumb_expand_immediate (unsigned int imm)
return (0x80 | (imm & 0x7f)) << (32 - count);
}
-/* Return 1 if the 16-bit Thumb instruction INST might change
- control flow, 0 otherwise. */
-
-static int
-thumb_instruction_changes_pc (unsigned short inst)
-{
- if ((inst & 0xff00) == 0xbd00) /* pop {rlist, pc} */
- return 1;
-
- if ((inst & 0xf000) == 0xd000) /* conditional branch */
- return 1;
-
- if ((inst & 0xf800) == 0xe000) /* unconditional branch */
- return 1;
-
- if ((inst & 0xff00) == 0x4700) /* bx REG, blx REG */
- return 1;
-
- if ((inst & 0xff87) == 0x4687) /* mov pc, REG */
- return 1;
-
- if ((inst & 0xf500) == 0xb100) /* CBNZ or CBZ. */
- return 1;
-
- return 0;
-}
-
-/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2
- might change control flow, 0 otherwise. */
-
-static int
-thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2)
-{
- if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
- {
- /* Branches and miscellaneous control instructions. */
-
- if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
- {
- /* B, BL, BLX. */
- return 1;
- }
- else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
- {
- /* SUBS PC, LR, #imm8. */
- return 1;
- }
- else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
- {
- /* Conditional branch. */
- return 1;
- }
-
- return 0;
- }
-
- if ((inst1 & 0xfe50) == 0xe810)
- {
- /* Load multiple or RFE. */
-
- if (bit (inst1, 7) && !bit (inst1, 8))
- {
- /* LDMIA or POP */
- if (bit (inst2, 15))
- return 1;
- }
- else if (!bit (inst1, 7) && bit (inst1, 8))
- {
- /* LDMDB */
- if (bit (inst2, 15))
- return 1;
- }
- else if (bit (inst1, 7) && bit (inst1, 8))
- {
- /* RFEIA */
- return 1;
- }
- else if (!bit (inst1, 7) && !bit (inst1, 8))
- {
- /* RFEDB */
- return 1;
- }
-
- return 0;
- }
-
- if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
- {
- /* MOV PC or MOVS PC. */
- return 1;
- }
-
- if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
- {
- /* LDR PC. */
- if (bits (inst1, 0, 3) == 15)
- return 1;
- if (bit (inst1, 7))
- return 1;
- if (bit (inst2, 11))
- return 1;
- if ((inst2 & 0x0fc0) == 0x0000)
- return 1;
-
- return 0;
- }
-
- if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
- {
- /* TBB. */
- return 1;
- }
-
- if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
- {
- /* TBH. */
- return 1;
- }
-
- return 0;
-}
-
/* Return 1 if the 16-bit Thumb instruction INSN restores SP in
epilogue, 0 otherwise. */
@@ -1510,98 +1380,6 @@ thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc,
thumb_analyze_prologue (gdbarch, prologue_start, prologue_end, cache);
}
-/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */
-
-static 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"));
- }
-}
-
/* Return 1 if the ARM instruction INSN restores SP in epilogue, 0
otherwise. */
@@ -4256,1158 +4034,120 @@ convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr,
&d, dbl);
}
-static int
-condition_true (unsigned long cond, unsigned long status_reg)
-{
- if (cond == INST_AL || cond == INST_NV)
- return 1;
-
- switch (cond)
- {
- case INST_EQ:
- return ((status_reg & FLAG_Z) != 0);
- case INST_NE:
- return ((status_reg & FLAG_Z) == 0);
- case INST_CS:
- return ((status_reg & FLAG_C) != 0);
- case INST_CC:
- return ((status_reg & FLAG_C) == 0);
- case INST_MI:
- return ((status_reg & FLAG_N) != 0);
- case INST_PL:
- return ((status_reg & FLAG_N) == 0);
- case INST_VS:
- return ((status_reg & FLAG_V) != 0);
- case INST_VC:
- return ((status_reg & FLAG_V) == 0);
- case INST_HI:
- return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
- case INST_LS:
- return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
- case INST_GE:
- return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0));
- case INST_LT:
- return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
- case INST_GT:
- return (((status_reg & FLAG_Z) == 0)
- && (((status_reg & FLAG_N) == 0)
- == ((status_reg & FLAG_V) == 0)));
- case INST_LE:
- return (((status_reg & FLAG_Z) != 0)
- || (((status_reg & FLAG_N) == 0)
- != ((status_reg & FLAG_V) == 0)));
- }
- return 1;
-}
+/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
+ of the appropriate mode (as encoded in the PC value), even if this
+ differs from what would be expected according to the symbol tables. */
-static unsigned long
-shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
- unsigned long pc_val, unsigned long status_reg)
+void
+arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
+ struct address_space *aspace,
+ CORE_ADDR pc)
{
- unsigned long res, shift;
- int rm = bits (inst, 0, 3);
- unsigned long shifttype = bits (inst, 5, 6);
+ struct cleanup *old_chain
+ = make_cleanup_restore_integer (&arm_override_mode);
- if (bit (inst, 4))
- {
- int rs = bits (inst, 8, 11);
- shift = (rs == 15 ? pc_val + 8
- : get_frame_register_unsigned (frame, rs)) & 0xFF;
- }
- else
- shift = bits (inst, 7, 11);
+ arm_override_mode = IS_THUMB_ADDR (pc);
+ pc = gdbarch_addr_bits_remove (gdbarch, pc);
- res = (rm == ARM_PC_REGNUM
- ? (pc_val + (bit (inst, 4) ? 12 : 8))
- : get_frame_register_unsigned (frame, rm));
+ insert_single_step_breakpoint (gdbarch, aspace, pc);
- switch (shifttype)
- {
- case 0: /* LSL */
- res = shift >= 32 ? 0 : res << shift;
- break;
+ do_cleanups (old_chain);
+}
- case 1: /* LSR */
- res = shift >= 32 ? 0 : res >> shift;
- break;
+/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
+ the buffer to be NEW_LEN bytes ending at ENDADDR. Return
+ NULL if an error occurs. BUF is freed. */
- case 2: /* ASR */
- if (shift >= 32)
- shift = 31;
- res = ((res & 0x80000000L)
- ? ~((~res) >> shift) : res >> shift);
- break;
+static gdb_byte *
+extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
+ int old_len, int new_len)
+{
+ gdb_byte *new_buf;
+ int bytes_to_read = new_len - old_len;
- case 3: /* ROR/RRX */
- shift &= 31;
- if (shift == 0)
- res = (res >> 1) | (carry ? 0x80000000L : 0);
- else
- res = (res >> shift) | (res << (32 - shift));
- break;
+ new_buf = xmalloc (new_len);
+ memcpy (new_buf + bytes_to_read, buf, old_len);
+ xfree (buf);
+ if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
+ {
+ xfree (new_buf);
+ return NULL;
}
-
- return res & 0xffffffff;
+ return new_buf;
}
-/* Return number of 1-bits in VAL. */
+/* An IT block is at most the 2-byte IT instruction followed by
+ four 4-byte instructions. The furthest back we must search to
+ find an IT block that affects the current instruction is thus
+ 2 + 3 * 4 == 14 bytes. */
+#define MAX_IT_BLOCK_PREFIX 14
+
+/* Use a quick scan if there are more than this many bytes of
+ code. */
+#define IT_SCAN_THRESHOLD 32
-static int
-bitcount (unsigned long val)
+/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
+ A breakpoint in an IT block may not be hit, depending on the
+ condition flags. */
+static CORE_ADDR
+arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
{
- int nbits;
- for (nbits = 0; val != 0; nbits++)
- val &= val - 1; /* Delete rightmost 1-bit in val. */
- return nbits;
-}
+ gdb_byte *buf;
+ char map_type;
+ CORE_ADDR boundary, func_start;
+ int buf_len;
+ enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
+ int i, any, last_it, last_it_count;
-/* Return the size in bytes of the complete Thumb instruction whose
- first halfword is INST1. */
+ /* If we are using BKPT breakpoints, none of this is necessary. */
+ if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
+ return bpaddr;
-static int
-thumb_insn_size (unsigned short inst1)
-{
- if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
- return 4;
- else
- return 2;
-}
+ /* ARM mode does not have this problem. */
+ if (!arm_pc_is_thumb (gdbarch, bpaddr))
+ return bpaddr;
-static 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);
+ /* We are setting a breakpoint in Thumb code that could potentially
+ contain an IT block. The first step is to find how much Thumb
+ code there is; we do not need to read outside of known Thumb
+ sequences. */
+ map_type = arm_find_mapping_symbol (bpaddr, &boundary);
+ if (map_type == 0)
+ /* Thumb-2 code must have mapping symbols to have a chance. */
+ return bpaddr;
- /* If we have finished the IT block, clear the state. */
- if ((itstate & 0x0f) == 0)
- itstate = 0;
+ bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
- return itstate;
-}
+ if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
+ && func_start > boundary)
+ boundary = func_start;
-/* Find the next PC after the current instruction executes. In some
- cases we can not statically determine the answer (see the IT state
- handling in this function); in that case, a breakpoint may be
- inserted in addition to the returned PC, which will be used to set
- another breakpoint by our caller. */
+ /* Search for a candidate IT instruction. We have to do some fancy
+ footwork to distinguish a real IT instruction from the second
+ half of a 32-bit instruction, but there is no need for that if
+ there's no candidate. */
+ buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
+ if (buf_len == 0)
+ /* No room for an IT instruction. */
+ return bpaddr;
-static CORE_ADDR
-thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- 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;
-
- nextpc = MAKE_THUMB_ADDR (nextpc);
- pc_val = MAKE_THUMB_ADDR (pc_val);
-
- inst1 = read_memory_unsigned_integer (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 = get_frame_register_unsigned (frame, 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 (gdbarch_tdep (gdbarch)->thumb2_breakpoint != NULL)
+ buf = xmalloc (buf_len);
+ if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
+ return bpaddr;
+ any = 0;
+ for (i = 0; i < buf_len; i += 2)
{
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
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 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
-
- return MAKE_THUMB_ADDR (pc);
- }
- 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 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
-
- return MAKE_THUMB_ADDR (pc);
- }
- 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);
- arm_insert_single_step_breakpoint (gdbarch, aspace,
- 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 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
- while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
-
- return MAKE_THUMB_ADDR (pc);
- }
+ any = 1;
+ break;
}
}
- else if (itstate & 0x0f)
+ if (any == 0)
{
- /* 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. */
- return MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1));
-
- /* 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 = get_frame_register_unsigned (frame, ARM_SP_REGNUM);
- nextpc = read_memory_unsigned_integer (sp + offset, 4, byte_order);
- }
- else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */
- {
- unsigned long cond = bits (inst1, 8, 11);
- if (cond == 0x0f) /* 0x0f = SWI */
- {
- struct gdbarch_tdep *tdep;
- tdep = gdbarch_tdep (gdbarch);
-
- if (tdep->syscall_next_pc != NULL)
- nextpc = tdep->syscall_next_pc (frame);
-
- }
- 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 = read_memory_unsigned_integer (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 = get_frame_register_unsigned (frame, 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 = get_frame_register_unsigned (frame, rn);
- nextpc = get_frame_memory_unsigned (frame, addr + offset, 4);
- }
- }
- else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
- {
- /* MOV PC or MOVS PC. */
- nextpc = get_frame_register_unsigned (frame, 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 = get_frame_register_unsigned (frame, 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 += get_frame_register_unsigned (frame, rm) << shift;
- }
- else
- /* Reserved. */
- load_pc = 0;
-
- if (load_pc)
- nextpc = get_frame_memory_unsigned (frame, base, 4);
- }
- 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 = get_frame_register_unsigned (frame, tbl_reg);
-
- offset = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
- length = 2 * get_frame_memory_unsigned (frame, table + offset, 1);
- 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 = get_frame_register_unsigned (frame, tbl_reg);
-
- offset = 2 * get_frame_register_unsigned (frame, bits (inst2, 0, 3));
- length = 2 * get_frame_memory_unsigned (frame, table + offset, 2);
- 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 = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
- }
- else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */
- {
- if (bits (inst1, 3, 6) == 0x0f)
- nextpc = pc_val;
- else
- nextpc = get_frame_register_unsigned (frame, 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 = get_frame_register_unsigned (frame, 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;
- }
- return nextpc;
-}
-
-/* Get the raw next address. PC is the current program counter, in
- FRAME, which is assumed to be executing in ARM mode.
-
- The value returned has 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. */
-
-static CORE_ADDR
-arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- unsigned long pc_val;
- unsigned long this_instr;
- unsigned long status;
- CORE_ADDR nextpc;
-
- pc_val = (unsigned long) pc;
- this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code);
-
- status = get_frame_register_unsigned (frame, 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)
- : get_frame_register_unsigned (frame, rn));
-
- return nextpc;
- }
-
- /* Multiply into PC. */
- c = (status & FLAG_C) ? 1 : 0;
- rn = bits (this_instr, 16, 19);
- operand1 = ((rn == ARM_PC_REGNUM)
- ? (pc_val + 8)
- : get_frame_register_unsigned (frame, 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 (frame, 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;
- }
-
- /* In 26-bit APCS the bottom two bits of the result are
- ignored, and we always end up in ARM state. */
- if (!arm_apcs_32)
- nextpc = arm_addr_bits_remove (gdbarch, result);
- else
- nextpc = 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)
- : get_frame_register_unsigned (frame, 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 (frame, this_instr, c, pc_val, status)
- : bits (this_instr, 0, 11));
-
- if (bit (this_instr, 23))
- base += offset;
- else
- base -= offset;
- }
- nextpc =
- (CORE_ADDR) read_memory_unsigned_integer ((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;
- unsigned long rn_val
- = get_frame_register_unsigned (frame,
- 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;
-
- nextpc =
- (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR)
- (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 */
- {
- struct gdbarch_tdep *tdep;
- tdep = gdbarch_tdep (gdbarch);
-
- if (tdep->syscall_next_pc != NULL)
- nextpc = tdep->syscall_next_pc (frame);
-
- }
- break;
-
- default:
- fprintf_filtered (gdb_stderr, _("Bad bit-field extraction\n"));
- return (pc);
- }
- }
-
- return nextpc;
-}
-
-/* Determine next PC after current instruction executes. Will call either
- arm_get_next_pc_raw or thumb_get_next_pc_raw. Error out if infinite
- loop is detected. */
-
-CORE_ADDR
-arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
-{
- CORE_ADDR nextpc;
-
- if (arm_frame_is_thumb (frame))
- nextpc = thumb_get_next_pc_raw (frame, pc);
- else
- nextpc = arm_get_next_pc_raw (frame, pc);
-
- return nextpc;
-}
-
-/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
- of the appropriate mode (as encoded in the PC value), even if this
- differs from what would be expected according to the symbol tables. */
-
-void
-arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
- struct address_space *aspace,
- CORE_ADDR pc)
-{
- struct cleanup *old_chain
- = make_cleanup_restore_integer (&arm_override_mode);
-
- arm_override_mode = IS_THUMB_ADDR (pc);
- pc = gdbarch_addr_bits_remove (gdbarch, pc);
-
- insert_single_step_breakpoint (gdbarch, aspace, pc);
-
- do_cleanups (old_chain);
-}
-
-/* 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. A breakpoint is placed at the end of
- the sequence. */
-
-static int
-thumb_deal_with_atomic_sequence_raw (struct frame_info *frame)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- CORE_ADDR pc = get_frame_pc (frame);
- 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;
-
- /* We currently do not support atomic sequences within an IT block. */
- status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
- itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
- if (itstate & 0x0f)
- return 0;
-
- /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */
- insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
- loc += 2;
- if (thumb_insn_size (insn1) != 4)
- return 0;
-
- insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
- loc += 2;
- if (!((insn1 & 0xfff0) == 0xe850
- || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040)))
- return 0;
-
- /* Assume that no atomic sequence is longer than "atomic_sequence_length"
- instructions. */
- for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
- {
- insn1 = read_memory_unsigned_integer (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 0; /* 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 0;
- }
- else
- {
- insn2 = read_memory_unsigned_integer (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 0;
-
- /* 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 0;
-
- /* 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;
-
- /* Effectively inserts the breakpoints. */
- for (index = 0; index <= last_breakpoint; index++)
- arm_insert_single_step_breakpoint (gdbarch, aspace,
- MAKE_THUMB_ADDR (breaks[index]));
-
- return 1;
-}
-
-static int
-arm_deal_with_atomic_sequence_raw (struct frame_info *frame)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- CORE_ADDR pc = get_frame_pc (frame);
- 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. */
-
- /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.
- Note that we do not currently support conditionally executed atomic
- instructions. */
- insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code);
- loc += 4;
- if ((insn & 0xff9000f0) != 0xe1900090)
- return 0;
-
- /* Assume that no atomic sequence is longer than "atomic_sequence_length"
- instructions. */
- for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
- {
- insn = read_memory_unsigned_integer (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 0; /* 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 0;
-
- /* 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 0;
-
- /* 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;
-
- /* Effectively inserts the breakpoints. */
- for (index = 0; index <= last_breakpoint; index++)
- arm_insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
-
- return 1;
-}
-
-int
-arm_deal_with_atomic_sequence (struct frame_info *frame)
-{
- if (arm_frame_is_thumb (frame))
- return thumb_deal_with_atomic_sequence_raw (frame);
- else
- return arm_deal_with_atomic_sequence_raw (frame);
-}
-
-/* single_step() is called just before we want to resume the inferior,
- if we want to single-step it but there is no hardware or kernel
- single-step support. We find the target of the coming instruction
- and breakpoint it. */
-
-int
-arm_software_single_step (struct frame_info *frame)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- CORE_ADDR next_pc;
-
- if (arm_deal_with_atomic_sequence (frame))
- return 1;
-
- next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
- arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
-
- return 1;
-}
-
-/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
- the buffer to be NEW_LEN bytes ending at ENDADDR. Return
- NULL if an error occurs. BUF is freed. */
-
-static gdb_byte *
-extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
- int old_len, int new_len)
-{
- gdb_byte *new_buf;
- int bytes_to_read = new_len - old_len;
-
- new_buf = xmalloc (new_len);
- memcpy (new_buf + bytes_to_read, buf, old_len);
- xfree (buf);
- if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
- {
- xfree (new_buf);
- return NULL;
- }
- return new_buf;
-}
-
-/* An IT block is at most the 2-byte IT instruction followed by
- four 4-byte instructions. The furthest back we must search to
- find an IT block that affects the current instruction is thus
- 2 + 3 * 4 == 14 bytes. */
-#define MAX_IT_BLOCK_PREFIX 14
-
-/* Use a quick scan if there are more than this many bytes of
- code. */
-#define IT_SCAN_THRESHOLD 32
-
-/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
- A breakpoint in an IT block may not be hit, depending on the
- condition flags. */
-static CORE_ADDR
-arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
-{
- gdb_byte *buf;
- char map_type;
- CORE_ADDR boundary, func_start;
- int buf_len;
- enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
- int i, any, last_it, last_it_count;
-
- /* If we are using BKPT breakpoints, none of this is necessary. */
- if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
- return bpaddr;
-
- /* ARM mode does not have this problem. */
- if (!arm_pc_is_thumb (gdbarch, bpaddr))
- return bpaddr;
-
- /* We are setting a breakpoint in Thumb code that could potentially
- contain an IT block. The first step is to find how much Thumb
- code there is; we do not need to read outside of known Thumb
- sequences. */
- map_type = arm_find_mapping_symbol (bpaddr, &boundary);
- if (map_type == 0)
- /* Thumb-2 code must have mapping symbols to have a chance. */
- return bpaddr;
-
- bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
-
- if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
- && func_start > boundary)
- boundary = func_start;
-
- /* Search for a candidate IT instruction. We have to do some fancy
- footwork to distinguish a real IT instruction from the second
- half of a 32-bit instruction, but there is no need for that if
- there's no candidate. */
- buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
- if (buf_len == 0)
- /* No room for an IT instruction. */
- return bpaddr;
-
- buf = xmalloc (buf_len);
- if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
- return bpaddr;
- any = 0;
- for (i = 0; i < buf_len; i += 2)
- {
- unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
- if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
- {
- any = 1;
- break;
- }
- }
- if (any == 0)
- {
- xfree (buf);
- return bpaddr;
+ xfree (buf);
+ return bpaddr;
}
/* OK, the code bytes before this instruction contain at least one
@@ -7324,6 +6064,86 @@ thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
return 0;
}
+/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs. */
+
+CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *arm_next_pcs,
+ CORE_ADDR val)
+{
+ struct arm_gdb_get_next_pcs* next_pcs =
+ (struct arm_gdb_get_next_pcs*) arm_next_pcs;
+
+ return gdbarch_addr_bits_remove (next_pcs->gdbarch, val);
+}
+
+/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs. */
+
+ULONGEST
+arm_get_next_pcs_collect_register_unsigned (struct get_next_pcs *self, int n)
+{
+ struct arm_gdb_get_next_pcs* next_pcs =
+ (struct arm_gdb_get_next_pcs*) self;
+
+ return get_frame_register_unsigned (next_pcs->frame, n);
+}
+
+/* Wrapper over sigreturn_return_addr for use in get_next_pcs. */
+int
+arm_get_next_pcs_sigreturn_return_addr (struct arm_get_next_pcs *arm_next_pcs,
+ unsigned long svc_number,
+ CORE_ADDR *pc, int *is_thumb)
+{
+ struct arm_gdb_get_next_pcs* next_pcs =
+ (struct arm_gdb_get_next_pcs*) arm_next_pcs;
+ struct gdbarch_tdep *tdep;
+
+ tdep = gdbarch_tdep (next_pcs->gdbarch);
+ if (tdep->sigreturn_return_addr != NULL)
+ return tdep->sigreturn_return_addr (next_pcs->frame, svc_number,
+ pc, is_thumb);
+ return 0;
+}
+
+/* single_step() is called just before we want to resume the inferior,
+ if we want to single-step it but there is no hardware or kernel
+ single-step support. We find the target of the coming instructions
+ and breakpoint them. */
+
+int
+arm_software_single_step (struct frame_info *frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct address_space *aspace = get_frame_address_space (frame);
+ struct arm_gdb_get_next_pcs next_pcs;
+ CORE_ADDR pc;
+ int i;
+
+ next_pcs.base.base.result = VEC_alloc (CORE_ADDR, 1);
+ next_pcs.gdbarch = gdbarch;
+ next_pcs.frame = frame;
+ next_pcs.base.ops = &arm_get_next_pcs_ops;
+ next_pcs.base.byte_order = gdbarch_byte_order (gdbarch);
+ next_pcs.base.byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ next_pcs.base.is_thumb = arm_frame_is_thumb (frame);
+ next_pcs.base.arm_apcs_32 = arm_apcs_32;
+ next_pcs.base.base.pc = get_frame_pc (frame);
+ next_pcs.base.arm_linux_thumb2_breakpoint =
+ gdbarch_tdep (gdbarch)->thumb2_breakpoint;
+
+ arm_get_next_pcs ((struct arm_get_next_pcs*)&next_pcs);
+
+ for (i = 0;
+ VEC_iterate (CORE_ADDR, next_pcs.base.base.result, i, pc);
+ ++i)
+ {
+ arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
+ }
+
+ VEC_free (CORE_ADDR, next_pcs.base.base.result);
+
+ return 1;
+}
+
/* Cleanup/copy SVC (SWI) instructions. These two functions are overridden
for Linux, where some SVC instructions must be treated specially. */
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index 3e06f79..bda6a06 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -23,12 +23,12 @@
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"
-/* Size of integer registers. */
-#define INT_REGISTER_SIZE 4
-
/* Say how long FP registers are. Used for documentation purposes and
code readability in this header. IEEE extended doubles are 80
bits. DWORD aligned they use 96 bits. */
@@ -50,32 +50,6 @@ struct address_space;
#define NUM_GREGS 16 /* Number of general purpose registers. */
-/* Instruction condition field values. */
-#define INST_EQ 0x0
-#define INST_NE 0x1
-#define INST_CS 0x2
-#define INST_CC 0x3
-#define INST_MI 0x4
-#define INST_PL 0x5
-#define INST_VS 0x6
-#define INST_VC 0x7
-#define INST_HI 0x8
-#define INST_LS 0x9
-#define INST_GE 0xa
-#define INST_LT 0xb
-#define INST_GT 0xc
-#define INST_LE 0xd
-#define INST_AL 0xe
-#define INST_NV 0xf
-
-#define FLAG_N 0x80000000
-#define FLAG_Z 0x40000000
-#define FLAG_C 0x20000000
-#define FLAG_V 0x10000000
-
-#define CPSR_T 0x20
-
-#define XPSR_T 0x01000000
/* Type of floating-point code in use by inferior. There are really 3 models
that are traditionally supported (plus the endianness issue), but gcc can
@@ -164,9 +138,10 @@ struct gdbarch_tdep
struct type *neon_double_type;
struct type *neon_quad_type;
- /* Return the expected next PC if FRAME is stopped at a syscall
- instruction. */
- CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+ /* Copy the expected next PC after a sigreturn/rt_sigreturn syscall */
+ int (*sigreturn_return_addr) (struct frame_info* frame,
+ unsigned long svc_number,
+ CORE_ADDR *pc, int *is_thumb);
/* syscall record. */
int (*arm_syscall_record) (struct regcache *regcache, unsigned long svc_number);
@@ -279,11 +254,27 @@ 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 frame_info *, CORE_ADDR);
+
+/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs. */
+CORE_ADDR arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs
+ *arm_next_pcs, CORE_ADDR val);
+
+/* Wrapper over get_frame_register_unsigned for use in arm_get_next_pcs. */
+ULONGEST arm_get_next_pcs_collect_register_unsigned (struct get_next_pcs *self,
+ int n);
+
+/* Wrapper over sigreturn_return_addr for use in get_next_pcs. */
+int arm_get_next_pcs_sigreturn_return_addr (struct arm_get_next_pcs
+ *arm_next_pcs,
+ unsigned long svc_number,
+ CORE_ADDR *pc,
+ int *is_thumb);
+
+int arm_software_single_step (struct frame_info *frame);
+
void arm_insert_single_step_breakpoint (struct gdbarch *,
struct address_space *, CORE_ADDR);
-int arm_deal_with_atomic_sequence (struct frame_info *);
-int arm_software_single_step (struct frame_info *);
+
int arm_frame_is_thumb (struct frame_info *frame);
extern struct displaced_step_closure *
diff --git a/gdb/arm-wince-tdep.c b/gdb/arm-wince-tdep.c
index 72295ba..206c132 100644
--- a/gdb/arm-wince-tdep.c
+++ b/gdb/arm-wince-tdep.c
@@ -24,6 +24,7 @@
#include "target.h"
#include "frame.h"
+#include "common/arm-common.h"
#include "arm-tdep.h"
#include "windows-tdep.h"
diff --git a/gdb/armnbsd-tdep.c b/gdb/armnbsd-tdep.c
index 4c128c2..774687b 100644
--- a/gdb/armnbsd-tdep.c
+++ b/gdb/armnbsd-tdep.c
@@ -20,6 +20,7 @@
#include "defs.h"
#include "osabi.h"
+#include "common/arm-common.h"
#include "arm-tdep.h"
#include "solib-svr4.h"
diff --git a/gdb/common/arm-common.c b/gdb/common/arm-common.c
new file mode 100644
index 0000000..87a2c6c
--- /dev/null
+++ b/gdb/common/arm-common.c
@@ -0,0 +1,90 @@
+/* Common code for generic ARM support.
+
+ Copyright (C) 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 "arm-common.h"
+
+/* Return the size in bytes of the complete Thumb instruction whose
+ first halfword is INST1. */
+
+int
+thumb_insn_size (unsigned short inst1)
+{
+ if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+ return 4;
+ else
+ return 2;
+}
+
+
+/* Return number of 1-bits in VAL. */
+
+int
+bitcount (unsigned long val)
+{
+ int nbits;
+ for (nbits = 0; val != 0; nbits++)
+ val &= val - 1; /* Delete rightmost 1-bit in val. */
+ return nbits;
+}
+
+/* Returns true of the condition evaluates to true. */
+
+int
+condition_true (unsigned long cond, unsigned long status_reg)
+{
+ if (cond == INST_AL || cond == INST_NV)
+ return 1;
+
+ switch (cond)
+ {
+ case INST_EQ:
+ return ((status_reg & FLAG_Z) != 0);
+ case INST_NE:
+ return ((status_reg & FLAG_Z) == 0);
+ case INST_CS:
+ return ((status_reg & FLAG_C) != 0);
+ case INST_CC:
+ return ((status_reg & FLAG_C) == 0);
+ case INST_MI:
+ return ((status_reg & FLAG_N) != 0);
+ case INST_PL:
+ return ((status_reg & FLAG_N) == 0);
+ case INST_VS:
+ return ((status_reg & FLAG_V) != 0);
+ case INST_VC:
+ return ((status_reg & FLAG_V) == 0);
+ case INST_HI:
+ return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
+ case INST_LS:
+ return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
+ case INST_GE:
+ return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0));
+ case INST_LT:
+ return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
+ case INST_GT:
+ return (((status_reg & FLAG_Z) == 0)
+ && (((status_reg & FLAG_N) == 0)
+ == ((status_reg & FLAG_V) == 0)));
+ case INST_LE:
+ return (((status_reg & FLAG_Z) != 0)
+ || (((status_reg & FLAG_N) == 0)
+ != ((status_reg & FLAG_V) == 0)));
+ }
+ return 1;
+}
diff --git a/gdb/common/arm-common.h b/gdb/common/arm-common.h
new file mode 100644
index 0000000..21a3b71
--- /dev/null
+++ b/gdb/common/arm-common.h
@@ -0,0 +1,64 @@
+/* Common code for generic ARM support.
+
+ Copyright (C) 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_COMMON_H
+#define ARM_COMMON_H 1
+
+/* Instruction condition field values. */
+#define INST_EQ 0x0
+#define INST_NE 0x1
+#define INST_CS 0x2
+#define INST_CC 0x3
+#define INST_MI 0x4
+#define INST_PL 0x5
+#define INST_VS 0x6
+#define INST_VC 0x7
+#define INST_HI 0x8
+#define INST_LS 0x9
+#define INST_GE 0xa
+#define INST_LT 0xb
+#define INST_GT 0xc
+#define INST_LE 0xd
+#define INST_AL 0xe
+#define INST_NV 0xf
+
+#define FLAG_N 0x80000000
+#define FLAG_Z 0x40000000
+#define FLAG_C 0x20000000
+#define FLAG_V 0x10000000
+
+#define CPSR_T 0x20
+
+#define XPSR_T 0x01000000
+
+/* Size of integer registers. */
+#define INT_REGISTER_SIZE 4
+
+
+/* Return the size in bytes of the complete Thumb instruction whose
+ first halfword is INST1. */
+int thumb_insn_size (unsigned short inst1);
+
+/* Returns true of the condition evaluates to true. */
+int condition_true (unsigned long cond, unsigned long status_reg);
+
+/* Return number of 1-bits in VAL. */
+int bitcount (unsigned long val);
+
+#endif /* ARM_COMMON_H */
diff --git a/gdb/common/arm-get-next-pcs.c b/gdb/common/arm-get-next-pcs.c
new file mode 100644
index 0000000..59966dc
--- /dev/null
+++ b/gdb/common/arm-get-next-pcs.c
@@ -0,0 +1,1273 @@
+/* Common code for ARM software single stepping support.
+
+ Copyright (C) 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 "vec.h"
+
+#include "arm-common.h"
+#include "arm-get-next-pcs.h"
+
+enum arm_regnum {
+ ARM_SP_REGNUM = 13, /* Contains address of top of stack */
+ ARM_LR_REGNUM = 14, /* address to return to from a function call */
+ ARM_PC_REGNUM = 15, /* Contains program counter */
+ ARM_PS_REGNUM = 25
+};
+
+static 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;
+}
+
+/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */
+
+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"));
+ }
+}
+
+/* Return 1 if the 16-bit Thumb instruction INST might change
+ control flow, 0 otherwise. */
+
+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;
+}
+
+
+/* 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)
+{
+ 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;
+}
+
+/* When PC is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+CORE_ADDR
+arm_syscall_next_pc (struct arm_get_next_pcs* next_pcs)
+{
+ CORE_ADDR pc = next_pcs->base.pc;
+ CORE_ADDR return_addr = 0;
+ int is_thumb = next_pcs->is_thumb;
+ ULONGEST svc_number = 0;
+
+ if (is_thumb)
+ {
+ svc_number = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 7);
+ return_addr = pc + 2;
+ }
+ else
+ {
+ unsigned long this_instr = next_pcs->ops->read_memory_unsigned_integer
+ ((CORE_ADDR) pc, 4, next_pcs->byte_order_for_code);
+
+ unsigned long svc_operand = (0x00ffffff & this_instr);
+ if (svc_operand) /* OABI. */
+ {
+ svc_number = svc_operand - 0x900000;
+ }
+ else /* EABI. */
+ {
+ svc_number = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 7);
+ }
+
+ return_addr = pc + 4;
+ }
+
+ /* FIXME add support on gdbserver */
+ if (next_pcs->ops->sigreturn_return_addr != NULL)
+ next_pcs->ops->sigreturn_return_addr (next_pcs, svc_number,
+ &return_addr, &is_thumb);
+
+ /* Addresses for calling Thumb functions have the bit 0 set. */
+ if (is_thumb)
+ return_addr |= 1;
+
+ return return_addr;
+}
+
+
+/* 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 int
+thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs* next_pcs)
+{
+ enum bfd_endian byte_order_for_code = next_pcs->byte_order_for_code;
+ CORE_ADDR pc = next_pcs->base.pc;
+ 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;
+
+ /* We currently do not support atomic sequences within an IT block. */
+ status = next_pcs->ops->collect_register_unsigned
+ (&next_pcs->base, ARM_PS_REGNUM);
+ itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+ if (itstate & 0x0f)
+ return 0;
+
+ /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */
+ insn1 = next_pcs->ops->read_memory_unsigned_integer
+ (loc, 2, byte_order_for_code);
+ loc += 2;
+ if (thumb_insn_size (insn1) != 4)
+ return 0;
+
+ insn2 = next_pcs->ops->read_memory_unsigned_integer
+ (loc, 2, byte_order_for_code);
+ loc += 2;
+ if (!((insn1 & 0xfff0) == 0xe850
+ || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040)))
+ return 0;
+
+ /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+ instructions. */
+ for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+ {
+ insn1 = next_pcs->ops->read_memory_unsigned_integer
+ (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 0; /* 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 0;
+ }
+ else
+ {
+ insn2 = next_pcs->ops->read_memory_unsigned_integer
+ (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 0;
+
+ /* 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 0;
+
+ /* 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->base.result,
+ MAKE_THUMB_ADDR (breaks[index]));
+ }
+
+ return 1;
+}
+
+static int
+arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs* next_pcs)
+{
+ enum bfd_endian byte_order_for_code = next_pcs->byte_order_for_code;
+ CORE_ADDR pc = next_pcs->base.pc;
+ 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. */
+
+ /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.
+ Note that we do not currently support conditionally executed atomic
+ instructions. */
+ insn = next_pcs->ops->read_memory_unsigned_integer
+ (loc, 4, byte_order_for_code);
+ loc += 4;
+ if ((insn & 0xff9000f0) != 0xe1900090)
+ return 0;
+
+ /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+ instructions. */
+ for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+ {
+ insn = next_pcs->ops->read_memory_unsigned_integer
+ (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 0; /* 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 0;
+
+ /* 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 0;
+
+ /* 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->base.result,
+ breaks[index]);
+ }
+
+ return 1;
+}
+
+/* Find the next possible PCs after the current instruction executes. */
+
+void
+arm_get_next_pcs (struct arm_get_next_pcs *next_pcs)
+{
+ if (next_pcs->is_thumb) {
+ if (!thumb_deal_with_atomic_sequence_raw (next_pcs))
+ thumb_get_next_pcs_raw (next_pcs);
+ }
+ else {
+ if (!arm_deal_with_atomic_sequence_raw (next_pcs))
+ arm_get_next_pcs_raw (next_pcs);
+ }
+}
+
+static unsigned long
+shifted_reg_val (struct arm_get_next_pcs* next_pcs, 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
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rs)) & 0xFF;
+ }
+ else
+ shift = bits (inst, 7, 11);
+
+ res = (rm == ARM_PC_REGNUM
+ ? (pc_val + (bit (inst, 4) ? 12 : 8))
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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;
+}
+
+/* Find the next possible PCs after the current instruction executes. */
+
+void
+thumb_get_next_pcs_raw (struct arm_get_next_pcs* next_pcs)
+{
+ enum bfd_endian byte_order = next_pcs->byte_order;
+ enum bfd_endian byte_order_for_code = next_pcs->byte_order_for_code;
+ CORE_ADDR pc = next_pcs->base.pc;
+ 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;
+
+ nextpc = MAKE_THUMB_ADDR (nextpc);
+ pc_val = MAKE_THUMB_ADDR (pc_val);
+
+ inst1 =
+ next_pcs->ops->read_memory_unsigned_integer (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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 (next_pcs->arm_linux_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 = next_pcs->ops->read_memory_unsigned_integer
+ (pc, 2, byte_order_for_code);
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+
+ VEC_safe_push (CORE_ADDR, next_pcs->base.result,
+ MAKE_THUMB_ADDR (pc));
+ return;
+ }
+ 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 = next_pcs->ops->read_memory_unsigned_integer
+ (pc, 2, byte_order_for_code);
+
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+
+ VEC_safe_push (CORE_ADDR, next_pcs->base.result,
+ MAKE_THUMB_ADDR (pc));
+ return;
+ }
+ 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->base.result,
+ 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 = next_pcs->ops->read_memory_unsigned_integer
+ (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->base.result,
+ MAKE_THUMB_ADDR (pc));
+
+ return;
+ }
+ }
+ }
+ 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->base.result,
+ MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)));
+
+ return;
+
+ /* 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, ARM_SP_REGNUM);
+
+ nextpc = next_pcs->ops->read_memory_unsigned_integer
+ (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 = arm_syscall_next_pc (next_pcs);
+ }
+ 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 = next_pcs->ops->read_memory_unsigned_integer (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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rn);
+ nextpc = next_pcs->ops->read_memory_unsigned_integer
+ (addr + offset, 4, byte_order);
+ }
+ }
+ else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+ {
+ /* MOV PC or MOVS PC. */
+ nextpc = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 += next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rm) << shift;
+ }
+ else
+ /* Reserved. */
+ load_pc = 0;
+
+ if (load_pc)
+ nextpc = next_pcs->ops->read_memory_unsigned_integer
+ (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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, tbl_reg);
+
+ offset = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs,bits (inst2, 0, 3));
+ length = 2 * next_pcs->ops->read_memory_unsigned_integer
+ (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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, tbl_reg);
+
+ offset = 2 * next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, bits (inst2, 0, 3));
+ length = 2 * next_pcs->ops->read_memory_unsigned_integer
+ (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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, bits (inst1, 3, 6));
+ }
+ else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */
+ {
+ if (bits (inst1, 3, 6) == 0x0f)
+ nextpc = pc_val;
+ else
+ nextpc = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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->base.result, nextpc);
+ return;
+}
+
+
+/* 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. */
+
+void
+arm_get_next_pcs_raw (struct arm_get_next_pcs *next_pcs)
+{
+ enum bfd_endian byte_order = next_pcs->byte_order;
+ CORE_ADDR pc = next_pcs->base.pc;
+ unsigned long pc_val;
+ unsigned long this_instr = 0;
+ unsigned long status;
+ CORE_ADDR nextpc;
+
+ pc_val = (unsigned long) pc;
+ this_instr =
+ next_pcs->ops->read_memory_unsigned_integer (pc, 4, byte_order);
+
+ status = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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)
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rn));
+
+ VEC_safe_push (CORE_ADDR, next_pcs->base.result, nextpc);
+ return;
+ }
+
+ /* Multiply into PC. */
+ c = (status & FLAG_C) ? 1 : 0;
+ rn = bits (this_instr, 16, 19);
+ operand1 = ((rn == ARM_PC_REGNUM)
+ ? (pc_val + 8)
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 (next_pcs, 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;
+ }
+
+ /* In 26-bit APCS the bottom two bits of the result are
+ ignored, and we always end up in ARM state. */
+ if (!next_pcs->arm_apcs_32)
+ nextpc = next_pcs->ops->addr_bits_remove (next_pcs, result);
+ else
+ nextpc = 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)
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 (next_pcs, this_instr, c,
+ pc_val, status)
+ : bits (this_instr, 0, 11));
+
+ if (bit (this_instr, 23))
+ base += offset;
+ else
+ base -= offset;
+ }
+ nextpc =
+ (CORE_ADDR) next_pcs->ops->read_memory_unsigned_integer
+ ((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;
+ unsigned long rn_val
+ = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs,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;
+
+ nextpc =
+ (CORE_ADDR) next_pcs->ops->read_memory_unsigned_integer ((CORE_ADDR)
+ (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 = arm_syscall_next_pc (next_pcs);
+ }
+ break;
+
+ default:
+ error (_("Bad bit-field extraction\n"));
+ return;
+ }
+ }
+
+ VEC_safe_push (CORE_ADDR, next_pcs->base.result, nextpc);
+ return;
+}
+
diff --git a/gdb/common/arm-get-next-pcs.h b/gdb/common/arm-get-next-pcs.h
new file mode 100644
index 0000000..dcd06d9
--- /dev/null
+++ b/gdb/common/arm-get-next-pcs.h
@@ -0,0 +1,112 @@
+/* Common code for ARM software single stepping support.
+
+ Copyright (C) 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
+
+#include "get-next-pcs.h"
+
+/* Addresses for calling Thumb functions have the bit 0 set.
+ Here are some macros to test, set, or clear bit 0 of addresses. */
+#define IS_THUMB_ADDR(addr) ((addr) & 1)
+#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 arm_get_next_pcs;
+
+/* get_next_pcs operations. */
+struct arm_get_next_pcs_ops
+{
+ ULONGEST (*read_memory_unsigned_integer) (CORE_ADDR memaddr, int len,
+ enum bfd_endian byte_order);
+ ULONGEST (*collect_register_unsigned) (struct get_next_pcs* self, int n);
+ int (*sigreturn_return_addr) (struct arm_get_next_pcs* next_pcs,
+ unsigned long svc_number,
+ CORE_ADDR *pc, int *is_thumb);
+ CORE_ADDR (*addr_bits_remove) (struct arm_get_next_pcs *next_pcs,
+ CORE_ADDR val);
+};
+
+/* Context for a get_next_pcs call on ARM. */
+struct arm_get_next_pcs
+{
+ struct get_next_pcs base;
+ struct arm_get_next_pcs_ops *ops;
+ enum bfd_endian byte_order;
+ enum bfd_endian byte_order_for_code;
+ int is_thumb;
+ int arm_apcs_32;
+ const gdb_byte *arm_linux_thumb2_breakpoint;
+};
+
+/* Context for a get_next_pcs call on ARM in GDB. */
+struct arm_gdb_get_next_pcs
+{
+ struct arm_get_next_pcs base;
+ struct frame_info *frame;
+ struct gdbarch *gdbarch;
+};
+
+/* Context for a get_next_pcs call on ARM in GDBServer. */
+struct arm_gdbserver_get_next_pcs
+{
+ struct arm_get_next_pcs base;
+};
+
+/* Find the next possible PCs after the current instruction executes. */
+void arm_get_next_pcs (struct arm_get_next_pcs *next_pcs);
+
+/* Find the next possible PCs for thumb mode. */
+void thumb_get_next_pcs_raw (struct arm_get_next_pcs *next_pcs);
+
+/* Find the next possible PCs for arm mode. */
+void arm_get_next_pcs_raw (struct arm_get_next_pcs *next_pcs);
+
+/* When PC is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+CORE_ADDR arm_syscall_next_pc (struct arm_get_next_pcs* next_pcs);
+
+/* Checks for an atomic sequence of instructions and add the end of the
+ sequence to the next_pcs list. */
+int arm_deal_with_atomic_sequence (struct arm_get_next_pcs *next_pcs);
+
+/* 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);
+
+#endif /* ARM_GET_NEXT_PCS_H */
diff --git a/gdb/common/arm-linux-common.h b/gdb/common/arm-linux-common.h
new file mode 100644
index 0000000..c3f0ba2
--- /dev/null
+++ b/gdb/common/arm-linux-common.h
@@ -0,0 +1,72 @@
+/* Common code for GNU/Linux on ARM.
+
+ Copyright (C) 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_COMMON_H
+#define ARM_LINUX_COMMON_H 1
+
+/* Under ARM GNU/Linux the traditional way of performing a breakpoint
+ is to execute a particular software interrupt, rather than use a
+ particular undefined instruction to provoke a trap. Upon exection
+ of the software interrupt the kernel stops the inferior with a
+ SIGTRAP, and wakes the debugger. */
+
+static const gdb_byte arm_linux_arm_le_breakpoint[] =
+ { 0x01, 0x00, 0x9f, 0xef };
+
+static const gdb_byte arm_linux_arm_be_breakpoint[] =
+ { 0xef, 0x9f, 0x00, 0x01 };
+
+#define arm_linux_arm_breakpoint_size 4
+
+/* However, the EABI syscall interface (new in Nov. 2005) does not look at
+ the operand of the swi if old-ABI compatibility is disabled. Therefore,
+ use an undefined instruction instead. This is supported as of kernel
+ version 2.5.70 (May 2003), so should be a safe assumption for EABI
+ binaries. */
+
+static const gdb_byte eabi_linux_arm_le_breakpoint[] =
+ { 0xf0, 0x01, 0xf0, 0xe7 };
+
+static const gdb_byte eabi_linux_arm_be_breakpoint[] =
+ { 0xe7, 0xf0, 0x01, 0xf0 };
+
+/* All the kernels which support Thumb support using a specific undefined
+ instruction for the Thumb breakpoint. */
+
+static const gdb_byte arm_linux_thumb_be_breakpoint[] = {0xde, 0x01};
+
+static const gdb_byte arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
+
+#define arm_linux_thumb_breakpoint_size 2
+
+/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
+ we must use a length-appropriate breakpoint for 32-bit Thumb
+ instructions. See also thumb_get_next_pc. */
+
+static const gdb_byte arm_linux_thumb2_be_breakpoint[] =
+ { 0xf7, 0xf0, 0xa0, 0x00 };
+
+static const gdb_byte arm_linux_thumb2_le_breakpoint[] =
+ { 0xf0, 0xf7, 0x00, 0xa0 };
+
+#define arm_linux_thumb2_breakpoint_size 4
+
+
+#endif /* ARM_LINUX_COMMON_H */
diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
index cb79234..9fa2d68 100644
--- a/gdb/common/common-defs.h
+++ b/gdb/common/common-defs.h
@@ -61,4 +61,10 @@
# define EXTERN_C_POP
#endif
+/* * Maximum size of a register. Something small, but large enough for
+ all known ISAs. If it turns out to be too small, make it bigger. */
+
+enum { MAX_REGISTER_SIZE = 64 };
+
+
#endif /* COMMON_DEFS_H */
diff --git a/gdb/common/get-next-pcs.h b/gdb/common/get-next-pcs.h
new file mode 100644
index 0000000..cf59aca
--- /dev/null
+++ b/gdb/common/get-next-pcs.h
@@ -0,0 +1,36 @@
+/* Common code for software single stepping support.
+
+ Copyright (C) 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 GET_NEXT_PCS_H
+#define GET_NEXT_PCS_H 1
+
+#if !defined (SYMTAB_H)
+DEF_VEC_I(CORE_ADDR);
+#endif
+
+struct get_next_pcs
+{
+ /* Resulting vector of possible next addresses. */
+ VEC (CORE_ADDR) *result;
+ /* Base PC from which to get the next pcs. */
+ CORE_ADDR pc;
+ struct regcache* regcache;
+};
+
+#endif /* GET_NEXT_PCS_H */
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index c42b4df..a2265f1 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -89,7 +89,8 @@ arm*-wince-pe | arm*-*-mingw32ce*)
;;
arm*-*-linux*)
# Target: ARM based machine running GNU/Linux
- gdb_target_obs="arm-tdep.o arm-linux-tdep.o glibc-tdep.o \
+ gdb_target_obs="arm-common.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
;;
diff --git a/gdb/defs.h b/gdb/defs.h
index e292977..5a6d032 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -589,11 +589,6 @@ extern double atof (const char *); /* X3.159-1989 4.10.1.1 */
/* Dynamic target-system-dependent parameters for GDB. */
#include "gdbarch.h"
-/* * Maximum size of a register. Something small, but large enough for
- all known ISAs. If it turns out to be too small, make it bigger. */
-
-enum { MAX_REGISTER_SIZE = 64 };
-
/* In findvar.c. */
extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index b715a32..85d3915 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -180,7 +180,9 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
$(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c \
$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
$(srcdir)/common/btrace-common.c \
- $(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c
+ $(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c \
+ $(srcdir)/common/arm-get-next-pcs.c $(srcdir)/common/int-utils.c \
+ $(srcdir)/common/arm-common.c
DEPFILES = @GDBSERVER_DEPFILES@
@@ -576,6 +578,15 @@ waitstatus.o: ../target/waitstatus.c
fileio.o: ../common/fileio.c
$(COMPILE) $<
$(POSTCOMPILE)
+arm-get-next-pcs.o: ../common/arm-get-next-pcs.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
+int-utils.o: ../common/int-utils.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
+arm-common.o: ../common/arm-common.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
# Native object files rules from ../nat
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index aa232f8..8da37e2 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -68,6 +68,9 @@ case "${target}" in
srv_regobj="${srv_regobj} arm-with-neon.o"
srv_tgtobj="$srv_linux_obj linux-arm-low.o"
srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
+ srv_tgtobj="${srv_tgtobj} arm-get-next-pcs.o"
+ srv_tgtobj="${srv_tgtobj} arm-common.o"
+ srv_tgtobj="${srv_tgtobj} int-utils.o"
srv_xmlfiles="arm-with-iwmmxt.xml"
srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv2.xml"
srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv3.xml"
diff --git a/gdb/gdbserver/linux-aarch32-low.c b/gdb/gdbserver/linux-aarch32-low.c
index 5876b13..026eafc 100644
--- a/gdb/gdbserver/linux-aarch32-low.c
+++ b/gdb/gdbserver/linux-aarch32-low.c
@@ -21,11 +21,6 @@
#include "linux-aarch32-low.h"
#include <sys/ptrace.h>
-/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
- On Bionic elf.h and linux/elf.h have conflicting definitions. */
-#ifndef ELFMAG0
-#include <elf.h>
-#endif
/* Some older versions of GNU/Linux and Android do not define
the following macros. */
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 0b2c5f1..2956211 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -24,7 +24,11 @@
#include "nat/aarch64-linux.h"
#include "nat/aarch64-linux-hw-point.h"
#include "linux-aarch32-low.h"
+/* Don't include elf/common.h if linux/elf.h got included by
+ linux-low.h or gdb_proc_service.h. */
+#ifndef ELFMAG0
#include "elf/common.h"
+#endif
#include <signal.h>
#include <sys/user.h>
@@ -577,7 +581,7 @@ struct linux_target_ops the_low_target =
aarch64_get_pc,
aarch64_set_pc,
aarch64_breakpoint_from_pc,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0, /* decr_pc_after_break */
aarch64_breakpoint_at,
aarch64_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 367c704..eb98a36 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -22,14 +22,14 @@
#include "linux-aarch32-low.h"
#include <sys/uio.h>
-/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
- On Bionic elf.h and linux/elf.h have conflicting definitions. */
-#ifndef ELFMAG0
-#include <elf.h>
-#endif
#include "nat/gdb_ptrace.h"
#include <signal.h>
+#include "common/int-utils.h"
+#include "common/arm-common.h"
+#include "common/arm-linux-common.h"
+#include "common/arm-get-next-pcs.h"
+
/* Defined in auto-generated files. */
void init_registers_arm (void);
extern const struct target_desc *tdesc_arm;
@@ -97,6 +97,13 @@ struct arm_linux_hw_breakpoint
#define MAX_BPTS 32
#define MAX_WPTS 32
+/* Only arm 32-bit mode is supported for now */
+int arm_apcs_32 = 1;
+
+/* M-Profile is not supported at the moment this is left
+ for future use. */
+int arm_is_m = 0;
+
/* Per-process arch-specific data we want to keep. */
struct arch_process_info
{
@@ -136,6 +143,27 @@ static int arm_regmap[] = {
64
};
+/* Forward declarations needed for get_next_pcs ops. */
+static ULONGEST
+read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
+ enum bfd_endian byte_order);
+
+static ULONGEST
+get_next_pcs_collect_register_unsigned (struct get_next_pcs* self, int n);
+
+static CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+ CORE_ADDR val);
+
+/* get_next_pcs operations. */
+struct arm_get_next_pcs_ops get_next_pcs_ops = {
+ read_memory_unsigned_integer,
+ get_next_pcs_collect_register_unsigned,
+ NULL,
+ arm_get_next_pcs_addr_bits_remove
+};
+
+
static int
arm_cannot_store_register (int regno)
{
@@ -198,6 +226,28 @@ arm_fill_vfpregset (struct regcache *regcache, void *buf)
arm_fill_vfpregset_num (regcache, buf, num);
}
+/* Remove useless bits from addresses in a running program. */
+static CORE_ADDR
+arm_addr_bits_remove (CORE_ADDR val)
+{
+ /* On M-profile devices, do not strip the low bit from EXC_RETURN
+ (the magic exception return address). */
+ if (arm_is_m && (val & 0xfffffff0) == 0xfffffff0)
+ return val;
+
+ if (arm_apcs_32)
+ return UNMAKE_THUMB_ADDR (val);
+ else
+ return (val & 0x03fffffc);
+}
+
+/* Wrapper for arm_addr_bits_remove in the get_next_pcs context*/
+static CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
+{
+ return arm_addr_bits_remove (val);
+}
+
static void
arm_store_vfpregset (struct regcache *regcache, const void *buf)
{
@@ -233,20 +283,27 @@ arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
supply_register_by_name (regcache, "pc", &newpc);
}
-/* Correct in either endianness. */
-static const unsigned long arm_breakpoint = 0xef9f0001;
-#define arm_breakpoint_len 4
-static const unsigned short thumb_breakpoint = 0xde01;
-static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
+/* Default breakpoint definitions. */
+static const gdb_byte *thumb_breakpoint = arm_linux_thumb_le_breakpoint;
+static const gdb_byte *thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
+static const gdb_byte *arm_eabi_breakpoint = eabi_linux_arm_le_breakpoint;
+static const gdb_byte *arm_abi_breakpoint = arm_linux_arm_le_breakpoint;
+
+/* Only supports the gdbserver EABI for breakpoint insertion */
+#ifndef __ARM_EABI__
+static const gdb_byte *arm_breakpoint = arm_linux_arm_le_breakpoint;
+#else
+static const gdb_byte *arm_breakpoint = eabi_linux_arm_le_breakpoint;
+#endif
+
+/* Byte order for data , defaults to LE. */
+enum bfd_endian byte_order = BFD_ENDIAN_LITTLE;;
+/* Byte order for code , defaults to LE. */
+enum bfd_endian byte_order_for_code = BFD_ENDIAN_LITTLE;
-/* 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. */
-static const unsigned long arm_eabi_breakpoint = 0xe7f001f0;
+/* Returns 1 if the currnet instruction mode is thumb. */
-static int
-arm_breakpoint_at (CORE_ADDR where)
+static int arm_is_thumb_mode()
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
unsigned long cpsr;
@@ -254,47 +311,233 @@ arm_breakpoint_at (CORE_ADDR where)
collect_register_by_name (regcache, "cpsr", &cpsr);
if (cpsr & 0x20)
+ return 1;
+ else
+ return 0;
+}
+
+/* Returns 1 if there is a software breakpoint at location. */
+
+static int
+arm_breakpoint_at (CORE_ADDR where)
+{
+ if (arm_is_thumb_mode())
{
/* Thumb mode. */
- unsigned short insn;
+ gdb_byte insn[2];
- (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
- if (insn == thumb_breakpoint)
+ (*the_target->read_memory) (where, (gdb_byte *) &insn, 2);
+ if (insn[0] == thumb_breakpoint[0] && insn[1] == thumb_breakpoint[1])
return 1;
- if (insn == thumb2_breakpoint[0])
+ if (insn[0] == thumb2_breakpoint[0] && insn[1] == thumb2_breakpoint[1])
{
- (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
- if (insn == thumb2_breakpoint[1])
+ (*the_target->read_memory) (where + 2, (gdb_byte *) &insn, 2);
+ if (insn[0] == thumb2_breakpoint[2] &&
+ insn[1] == thumb2_breakpoint[3])
return 1;
}
}
else
{
/* ARM mode. */
- unsigned long insn;
+ gdb_byte insn[4];
+ int i;
+ int result = 1;
(*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == arm_breakpoint)
- return 1;
+ for (i = 0; i < 4; i++)
+ {
+ if (insn[i] != arm_abi_breakpoint[i])
+ result = 0;
+ }
- if (insn == arm_eabi_breakpoint)
- return 1;
+ if (!result)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ if (insn[i] != arm_eabi_breakpoint[i])
+ result = 0;
+ }
+ }
+ return result;
}
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static CORE_ADDR
-arm_reinsert_addr (void)
+/* Wrapper to collect_register for get_next_pcs. */
+
+static ULONGEST
+get_next_pcs_collect_register_unsigned (struct get_next_pcs* self, int n)
{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
- collect_register_by_name (regcache, "lr", &pc);
- return pc;
+ struct arm_gdbserver_get_next_pcs *next_pcs =
+ (struct arm_gdbserver_get_next_pcs*) self;
+ gdb_byte buf[MAX_REGISTER_SIZE];
+ int size = register_size (next_pcs->base.base.regcache->tdesc, n);
+
+ collect_register (next_pcs->base.base.regcache, n, &buf);
+ return extract_unsigned_integer (buf, size, next_pcs->base.byte_order);
+}
+
+static ULONGEST
+read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
+ enum bfd_endian byte_order)
+{
+ gdb_byte buf[sizeof (ULONGEST)];
+ (*the_target->read_memory) (memaddr, (unsigned char *) &buf, len);
+ return extract_unsigned_integer (buf, len, byte_order);
+}
+
+/* Determine the type and size of breakpoint to insert at PCPTR. Uses
+ the program counter value to determine whether a 16-bit or 32-bit
+ breakpoint should be used. It returns a pointer to a string of
+ bytes that encode a breakpoint instruction, stores the length of
+ the string to *lenptr, and adjusts the program counter (if
+ necessary) to point to the actual memory location where the
+ breakpoint should be inserted. */
+
+static const unsigned char *
+arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+ /* Default if no pc is set to arm breakpoint. */
+ if (pcptr == NULL)
+ {
+ *lenptr = arm_linux_arm_breakpoint_size;
+ return arm_breakpoint;
+ }
+
+ if (IS_THUMB_ADDR (*pcptr))
+ {
+ *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+ /* If we have a separate 32-bit breakpoint instruction for Thumb-2,
+ check whether we are replacing a 32-bit instruction. */
+ if (thumb2_breakpoint != NULL)
+ {
+ gdb_byte buf[2];
+ if ((*the_target->read_memory) (*pcptr, buf, 2) == 0)
+ {
+ unsigned short inst1;
+ inst1 = extract_unsigned_integer (buf, 2, byte_order_for_code);
+ if (thumb_insn_size (inst1) == 4)
+ {
+ *lenptr = arm_linux_thumb2_breakpoint_size;
+ return thumb2_breakpoint;
+ }
+ }
+ }
+
+ *lenptr = arm_linux_thumb_breakpoint_size;
+ return thumb_breakpoint;
+ }
+ else
+ {
+ *lenptr = arm_linux_arm_breakpoint_size;
+ return arm_breakpoint;
+ }
+}
+
+/* Sets the breakpoints to the endianness in argument. */
+
+static void
+arm_set_breakpoints (enum bfd_endian endian)
+{
+ switch (endian) {
+ case BFD_ENDIAN_LITTLE:
+ thumb_breakpoint = arm_linux_thumb_le_breakpoint;
+ thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
+
+ arm_eabi_breakpoint = eabi_linux_arm_le_breakpoint;
+
+ arm_abi_breakpoint = arm_linux_arm_le_breakpoint;
+
+ /* Only supports the gdbserver EABI for breakpoint insertion */
+#ifndef __ARM_EABI__
+ arm_breakpoint = arm_linux_arm_le_breakpoint;
+#else
+ arm_breakpoint = eabi_linux_arm_le_breakpoint;
+#endif
+ break;
+ case BFD_ENDIAN_BIG:
+ thumb_breakpoint = arm_linux_thumb_be_breakpoint;
+ thumb2_breakpoint = arm_linux_thumb2_be_breakpoint;
+
+ arm_eabi_breakpoint = eabi_linux_arm_be_breakpoint;
+
+ arm_abi_breakpoint = arm_linux_arm_be_breakpoint;
+
+ /* Only supports the gdbserver EABI for breakpoint insertion */
+#ifndef __ARM_EABI__
+ arm_breakpoint = arm_linux_arm_be_breakpoint;
+#else
+ arm_breakpoint = eabi_linux_arm_be_breakpoint;
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+/* Sets the byte order based on ELF endianness flags. */
+
+static void
+arm_set_byte_order (void)
+{
+ int tid = lwpid_of (current_thread);
+ char file[PATH_MAX];
+ unsigned int machine;
+ Elf64_Ehdr header_64;
+ Elf32_Ehdr header_32;
+ int is64;
+
+ sprintf (file, "/proc/%d/exe", tid);
+
+ is64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+ if (is64)
+ elf_64_file_read_header (file, &header_64, &machine);
+ else
+ elf_32_file_read_header (file, &header_32, &machine);
+
+ switch (is64 ? header_64.e_ident[EI_DATA] : header_32.e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ byte_order = BFD_ENDIAN_LITTLE;
+ byte_order_for_code = BFD_ENDIAN_LITTLE;
+ break;
+ case ELFDATA2MSB:
+ byte_order = BFD_ENDIAN_BIG;
+ if ((is64 ? header_64.e_flags : header_32.e_flags) & EF_ARM_BE8)
+ {
+ byte_order_for_code = BFD_ENDIAN_LITTLE;
+ }
+ else
+ {
+ byte_order_for_code = BFD_ENDIAN_BIG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ arm_set_breakpoints (byte_order_for_code);
+}
+
+/* Fetch the next possible PCs after the current instruction executes. */
+
+static void
+arm_gdbserver_get_next_pcs (struct get_next_pcs *base_next_pcs)
+{
+ struct arm_gdbserver_get_next_pcs next_pcs;
+
+ next_pcs.base.ops = &get_next_pcs_ops;
+ next_pcs.base.base = *base_next_pcs;
+ next_pcs.base.byte_order = byte_order;
+ next_pcs.base.byte_order_for_code = byte_order_for_code;
+ next_pcs.base.is_thumb = arm_is_thumb_mode();
+ next_pcs.base.arm_apcs_32 = arm_apcs_32;
+ next_pcs.base.arm_linux_thumb2_breakpoint = thumb2_breakpoint;
+
+ arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs);
}
/* Fetch the thread-local storage pointer for libthread_db. */
@@ -864,6 +1107,9 @@ arm_arch_setup (void)
have_ptrace_getregset = 1;
else
have_ptrace_getregset = 0;
+
+ /* Fetch the global byte order from ELF. */
+ arm_set_byte_order ();
}
/* Register sets without using PTRACE_GETREGSET. */
@@ -913,21 +1159,6 @@ arm_regs_info (void)
return ®s_info_arm;
}
-static const unsigned char *
-arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
-{
- *len = arm_breakpoint_len;
- /* Define an ARM-mode breakpoint; we only set breakpoints in the C
- library, which is most likely to be ARM. If the kernel supports
- clone events, we will never insert a breakpoint, so even a Thumb
- C library will work; so will mixing EABI/non-EABI gdbserver and
- application. */
-#ifndef __ARM_EABI__
- return (const unsigned char *) &arm_breakpoint;
-#else
- return (const unsigned char *) &arm_eabi_breakpoint;
-#endif
-}
struct linux_target_ops the_low_target = {
arm_arch_setup,
arm_regs_info,
@@ -937,7 +1168,7 @@ struct linux_target_ops the_low_target = {
arm_get_pc,
arm_set_pc,
arm_breakpoint_from_pc,
- arm_reinsert_addr,
+ arm_gdbserver_get_next_pcs,
0,
arm_breakpoint_at,
arm_supports_z_point_type,
@@ -952,6 +1183,13 @@ struct linux_target_ops the_low_target = {
arm_new_thread,
arm_new_fork,
arm_prepare_to_resume,
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
};
void
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 1c0e1e9..3032a34 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -130,7 +130,7 @@ struct linux_target_ops the_low_target = {
bfin_get_pc,
bfin_set_pc,
bfin_breakpoint_from_pc,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
2,
bfin_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index da5876d..cd1ae4d 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -19,6 +19,7 @@
#include "server.h"
#include "linux-low.h"
#include "nat/gdb_ptrace.h"
+#include "common/get-next-pcs.h"
/* Defined in auto-generated file reg-cris.c. */
void init_registers_cris (void);
@@ -106,13 +107,13 @@ cris_breakpoint_at (CORE_ADDR where)
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
-static CORE_ADDR
-cris_reinsert_addr (void)
+static void
+cris_get_next_pcs (struct get_next_pcs *next_pcs)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
+ CORE_ADDR pc;
collect_register_by_name (regcache, "srp", &pc);
- return pc;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
}
static void
@@ -148,7 +149,7 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_reinsert_addr,
+ cris_get_next_pcs,
0,
cris_breakpoint_at,
0,
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index d2dba91..05ad85a 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -19,6 +19,7 @@
#include "server.h"
#include "linux-low.h"
#include "nat/gdb_ptrace.h"
+#include "common/get-next-pcs.h"
/* Defined in auto-generated file reg-crisv32.c. */
void init_registers_crisv32 (void);
@@ -106,14 +107,13 @@ cris_breakpoint_at (CORE_ADDR where)
/* FIXME: This function should not be needed, since we have PTRACE_SINGLESTEP
for CRISv32. Without it, td_ta_event_getmsg in thread_db_create_event
will fail when debugging multi-threaded applications. */
-
-static CORE_ADDR
-cris_reinsert_addr (void)
+static void
+cris_get_next_pcs (struct get_next_pcs *next_pcs)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
+ CORE_ADDR pc;
collect_register_by_name (regcache, "srp", &pc);
- return pc;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
}
static void
@@ -428,7 +428,7 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_reinsert_addr,
+ cris_get_next_pcs,
0,
cris_breakpoint_at,
cris_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 402db9c..229df46 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -46,14 +46,8 @@
#include "filestuff.h"
#include "tracepoint.h"
#include "hostio.h"
-#ifndef ELFMAG0
-/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
- then ELFMAG0 will have been defined. If it didn't get included by
- gdb_proc_service.h then including it will likely introduce a duplicate
- definition of elf_fpregset_t. */
-#include <elf.h>
-#endif
#include "nat/linux-namespaces.h"
+#include "common/get-next-pcs.h"
#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
@@ -279,12 +273,12 @@ static void complete_ongoing_step_over (void);
ptid_t step_over_bkpt;
/* True if the low target can hardware single-step. Such targets
- don't need a BREAKPOINT_REINSERT_ADDR callback. */
+ don't need a GET_NEXT_PCS callback. */
static int
can_hardware_single_step (void)
{
- return (the_low_target.breakpoint_reinsert_addr == NULL);
+ return (the_low_target.get_next_pcs == NULL);
}
/* True if the low target supports memory breakpoints. If so, we'll
@@ -351,28 +345,100 @@ elf_64_header_p (const Elf64_Ehdr *header, unsigned int *machine)
return -1;
}
-/* Return non-zero if FILE is a 64-bit ELF file,
- zero if the file is not a 64-bit ELF file,
- and -1 if the file is not accessible or doesn't exist. */
+/* Returns -1 if the file is not ELF64, fills the Elf64 header otherwise
+ and return non-zero. */
static int
-elf_64_file_p (const char *file, unsigned int *machine)
+elf_64_read_header (int fd, Elf64_Ehdr *header, unsigned int *machine)
+{
+ if (read (fd, header, sizeof (*header)) != sizeof (*header))
+ {
+ return 0;
+ }
+
+ return elf_64_header_p (header, machine);
+}
+
+/* Returns -1 if the file is not ELF64 or not accessible,
+ fills the Elf64 header otherwise and return non-zero. */
+
+int
+elf_64_file_read_header (const char *file, Elf64_Ehdr *header,
+ unsigned int *machine)
{
- Elf64_Ehdr header;
int fd;
+ int result;
fd = open (file, O_RDONLY);
if (fd < 0)
return -1;
- if (read (fd, &header, sizeof (header)) != sizeof (header))
+ result = elf_64_read_header (fd, header, machine);
+ close (fd);
+
+ return result;
+}
+
+/* Return non-zero if HEADER is a 32-bit ELF file. */
+
+static int
+elf_32_header_p (const Elf32_Ehdr *header, unsigned int *machine)
+{
+ if (header->e_ident[EI_MAG0] == ELFMAG0
+ && header->e_ident[EI_MAG1] == ELFMAG1
+ && header->e_ident[EI_MAG2] == ELFMAG2
+ && header->e_ident[EI_MAG3] == ELFMAG3)
+ {
+ *machine = header->e_machine;
+ return header->e_ident[EI_CLASS] == ELFCLASS32;
+
+ }
+ *machine = EM_NONE;
+ return -1;
+}
+
+/* Returns -1 if the file is not ELF32, fills the Elf32 header otherwise
+ and return non-zero. */
+
+static int
+elf_32_read_header (int fd, Elf32_Ehdr *header, unsigned int *machine)
+{
+ if (read (fd, header, sizeof (*header)) != sizeof (*header))
{
- close (fd);
return 0;
}
+
+ return elf_32_header_p (header, machine);
+}
+
+/* Returns -1 if the file is not ELF32 or not accessible,
+ fills the Elf32 header otherwise and return non-zero. */
+int
+elf_32_file_read_header (const char *file, Elf32_Ehdr *header,
+ unsigned int *machine)
+{
+ int fd;
+ int result;
+
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ result = elf_32_read_header (fd, header, machine);
close (fd);
- return elf_64_header_p (&header, machine);
+ return result;
+}
+/* Return non-zero if FILE is a 64-bit ELF file,
+ zero if the file is not a 64-bit ELF file,
+ and -1 if the file is not accessible or doesn't exist. */
+
+static int
+elf_64_file_p (const char *file, unsigned int *machine)
+{
+ Elf64_Ehdr header;
+ int result = elf_64_file_read_header (file, &header, machine);
+ return result;
}
/* Accepts an integer PID; Returns true if the executable PID is
@@ -3783,6 +3849,29 @@ enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info)
lwp->pending_signals = p_sig;
}
+/* Install breakpoints for software single stepping. */
+
+static void
+install_software_single_step_breakpoints (struct lwp_info *lwp)
+{
+ struct get_next_pcs next_pcs;
+ int i;
+ CORE_ADDR pc;
+
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+ next_pcs.pc = get_pc(lwp);
+ next_pcs.regcache = regcache;
+ next_pcs.result = VEC_alloc (CORE_ADDR, 1);
+ (*the_low_target.get_next_pcs) (&next_pcs);
+
+ for (i = 0; VEC_iterate (CORE_ADDR, next_pcs.result, i, pc); ++i)
+ {
+ set_reinsert_breakpoint (pc);
+ }
+
+ VEC_free (CORE_ADDR, next_pcs.result);
+}
+
/* Resume execution of LWP. If STEP is nonzero, single-step it. If
SIGNAL is nonzero, give it that signal. */
@@ -3928,14 +4017,20 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
address, continue, and carry on catching this while-stepping
action only when that breakpoint is hit. A future
enhancement. */
- if (thread->while_stepping != NULL
- && can_hardware_single_step ())
- {
- if (debug_threads)
- debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
- lwpid_of (thread));
- step = 1;
+ if (thread->while_stepping != NULL) {
+ if (debug_threads)
+ debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
+ lwpid_of (thread));
+
+ if (can_hardware_single_step ())
+ {
+ step = 1;
+ }
+ else {
+ install_software_single_step_breakpoints (lwp);
+ step = 0;
}
+ }
if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
{
@@ -4346,8 +4441,7 @@ start_step_over (struct lwp_info *lwp)
}
else
{
- CORE_ADDR raddr = (*the_low_target.breakpoint_reinsert_addr) ();
- set_reinsert_breakpoint (raddr);
+ install_software_single_step_breakpoints (lwp);
step = 0;
}
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index c623150..597c36d 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -27,8 +27,19 @@
#include "nat/linux-ptrace.h"
#include "target/waitstatus.h" /* For enum target_stop_reason. */
+#ifndef ELFMAG0
+/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
+ then ELFMAG0 will have been defined. If it didn't get included by
+ gdb_proc_service.h then including it will likely introduce a duplicate
+ definition of elf_fpregset_t. */
+#include <elf.h>
+#endif
+
#define PTRACE_XFER_TYPE long
+/* Forward declaration for get_next_pcs. */
+struct get_next_pcs;
+
#ifdef HAVE_LINUX_REGSETS
typedef void (*regset_fill_func) (struct regcache *, void *);
typedef void (*regset_store_func) (struct regcache *, const void *);
@@ -148,7 +159,8 @@ struct linux_target_ops
present in the PC. */
const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
- CORE_ADDR (*breakpoint_reinsert_addr) (void);
+ /* Find the next possible PCs after the current instruction executes. */
+ void (*get_next_pcs) (struct get_next_pcs *next_pcs);
int decr_pc_after_break;
int (*breakpoint_at) (CORE_ADDR pc);
@@ -353,6 +365,10 @@ struct lwp_info
};
int linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine);
+int elf_64_file_read_header (const char *file, Elf64_Ehdr *header,
+ unsigned int *machine);
+int elf_32_file_read_header (const char *file, Elf32_Ehdr *header,
+ unsigned int *machine);
/* Attach to PTID. Returns 0 on success, non-zero otherwise (an
errno). */
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d5333ab..d462f2c 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -24,6 +24,7 @@
#include "nat/mips-linux-watch.h"
#include "gdb_proc_service.h"
+#include "common/get-next-pcs.h"
/* Defined in auto-generated file mips-linux.c. */
void init_registers_mips_linux (void);
@@ -276,13 +277,16 @@ mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
-static CORE_ADDR
-mips_reinsert_addr (void)
+static void
+mips_get_next_pcs (struct get_next_pcs *next_pcs)
{
+ CORE_ADDR result;
struct regcache *regcache = get_thread_regcache (current_thread, 1);
union mips_register ra;
collect_register_by_name (regcache, "r31", ra.buf);
- return register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64;
+ result = register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64;
+
+ VEC_safe_push (CORE_ADDR, next_pcs->result, result);
}
static int
@@ -889,7 +893,7 @@ struct linux_target_ops the_low_target = {
mips_get_pc,
mips_set_pc,
mips_breakpoint_from_pc,
- mips_reinsert_addr,
+ mips_get_next_pcs,
0,
mips_breakpoint_at,
mips_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index bf9ecc2..a24eb23 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -21,11 +21,16 @@
#include "server.h"
#include "linux-low.h"
+/* Don't include elf/common.h if linux/elf.h got included by
+ linux-low.h or gdb_proc_service.h. */
+#ifndef ELFMAG0
#include "elf/common.h"
+#endif
#include "nat/gdb_ptrace.h"
#include <endian.h>
#include "gdb_proc_service.h"
#include <asm/ptrace.h>
+#include "common/get-next-pcs.h"
#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 25
@@ -141,16 +146,16 @@ nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
return (const unsigned char *) &nios2_breakpoint;
}
-/* Implement the breakpoint_reinsert_addr linux_target_ops method. */
+/* Implement the get_next_pcs linux_target_ops method. */
-static CORE_ADDR
-nios2_reinsert_addr (void)
+static void
+nios2_get_next_pcs (struct get_next_pcs *next_pcs)
{
union nios2_register ra;
struct regcache *regcache = get_thread_regcache (current_thread, 1);
collect_register_by_name (regcache, "ra", ra.buf);
- return ra.reg32;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, ra.reg32);
}
/* Implement the breakpoint_at linux_target_ops method. */
@@ -275,7 +280,7 @@ struct linux_target_ops the_low_target =
nios2_get_pc,
nios2_set_pc,
nios2_breakpoint_from_pc,
- nios2_reinsert_addr,
+ nios2_get_next_pcs,
0,
nios2_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 4c71cd9..fa87e19 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -19,8 +19,6 @@
#include "server.h"
#include "linux-low.h"
-
-#include <elf.h>
#include <asm/ptrace.h>
#include "nat/ppc-linux.h"
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index f76f867..44d3f26 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -21,12 +21,16 @@
#include "server.h"
#include "linux-low.h"
+
+/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
+ On Bionic elf.h and linux/elf.h have conflicting definitions. */
+#ifndef ELFMAG0
#include "elf/common.h"
+#endif
#include <asm/ptrace.h>
#include "nat/gdb_ptrace.h"
#include <sys/uio.h>
-#include <elf.h>
#ifndef HWCAP_S390_HIGH_GPRS
#define HWCAP_S390_HIGH_GPRS 512
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 35820fb..e804281 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -22,6 +22,7 @@
#include "nat/gdb_ptrace.h"
#include "gdb_proc_service.h"
+#include "common/get-next-pcs.h"
/* The stack pointer is offset from the stack frame by a BIAS of 2047
(0x7ff) for 64-bit code. BIAS is likely to be defined on SPARC
@@ -266,14 +267,14 @@ sparc_breakpoint_at (CORE_ADDR where)
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
-static CORE_ADDR
-sparc_reinsert_addr (void)
+static void
+sparc_get_next_pcs (struct get_next_pcs *next_pcs)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
CORE_ADDR lr;
/* O7 is the equivalent to the 'lr' of other archs. */
collect_register_by_name (regcache, "o7", &lr);
- return lr;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, lr);
}
static void
@@ -330,7 +331,7 @@ struct linux_target_ops the_low_target = {
/* No sparc_set_pc is needed. */
NULL,
sparc_breakpoint_from_pc,
- sparc_reinsert_addr,
+ sparc_get_next_pcs,
0,
sparc_breakpoint_at,
NULL, /* supports_z_point_type */
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 699eb4d..0731141 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -29,7 +29,7 @@
#include "gdb_proc_service.h"
/* Don't include elf/common.h if linux/elf.h got included by
- gdb_proc_service.h. */
+ linux-low.h or gdb_proc_service.h. */
#ifndef ELFMAG0
#include "elf/common.h"
#endif
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
2015-09-11 12:14 ` [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
@ 2015-09-11 12:30 ` Eli Zaretskii
2015-09-11 12:43 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Eli Zaretskii @ 2015-09-11 12:30 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches, antoine.tremblay
> From: Antoine Tremblay <antoine.tremblay@ericsson.com>
> CC: Antoine Tremblay <antoine.tremblay@ericsson.com>
> Date: Fri, 11 Sep 2015 08:13:23 -0400
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 0cf51e1..085d844 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,10 @@
>
> *** Changes since GDB 7.10
>
> +* Support for tracepoints on aarch32-linux was added in GDBServer.
> +
> +* Support for software breakpoints on aarch32-linux was added in GDBServer.
> +
> * Support for tracepoints on aarch64-linux was added in GDBserver.
>
> * The 'record instruction-history' command now indicates speculative execution
This is OK.
> -@item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}[:F@var{flen}][:X@var{len},@var{bytes}]@r{[}-@r{]}
> +@item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}[:F@var{flen}][:X@var{len},@var{bytes}][:K@var{kind}]@r{[}-@r{]}
> @cindex @samp{QTDP} packet
> Create a new tracepoint, number @var{n}, at @var{addr}. If @var{ena}
> is @samp{E}, then the tracepoint is enabled; if it is @samp{D}, then
> -the tracepoint is disabled. The @var{step} gives the tracepoint's step
> -count, and @var{pass} gives its pass count. If an @samp{F} is present,
> -then the tracepoint is to be a fast tracepoint, and the @var{flen} is
> -the number of bytes that the target should copy elsewhere to make room
> -for the tracepoint. If an @samp{X} is present, it introduces a
> -tracepoint condition, which consists of a hexadecimal length, followed
> -by a comma and hex-encoded bytes, in a manner similar to action
> -encodings as described below. If the trailing @samp{-} is present,
> -further @samp{QTDP} packets will follow to specify this tracepoint's
> -actions.
> +the tracepoint is disabled. The @var{step} gives the tracepoint's
> +step count, and @var{pass} gives its pass count. If an @samp{F} is
> +present, then the tracepoint is to be a fast tracepoint, and the
> +@var{flen} is the number of bytes that the target should copy
> +elsewhere to make room for the tracepoint. If an @samp{X} is present,
> +it introduces a tracepoint condition, which consists of a hexadecimal
> +length, followed by a comma and hex-encoded bytes, in a manner similar
Please in the future try not to re-fill previous text: it makes harder
for me to find your changes among a potentially large paragraph, such
as this one.
> +to action encodings as described below. If a @samp{K} is present, it
> +indicates a target specific breakpoint length. E.g., the arm and mips
> +can insert either a 2 or 4 byte breakpoint. Some architectures have
> +additional meanings for kind see : @ref{Architecture-Specific Protocol
> +Details} .
There's no need for the colon ':' after "see", and please delete the
space before the period that ends the last sentence above.
Otherwise, the documentation parts are OK. Thanks.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
2015-09-11 12:30 ` Eli Zaretskii
@ 2015-09-11 12:43 ` Antoine Tremblay
0 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:43 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On 09/11/2015 08:30 AM, Eli Zaretskii wrote:
>> -@item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}[:F@var{flen}][:X@var{len},@var{bytes}]@r{[}-@r{]}
>> +@item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}[:F@var{flen}][:X@var{len},@var{bytes}][:K@var{kind}]@r{[}-@r{]}
>> @cindex @samp{QTDP} packet
>> Create a new tracepoint, number @var{n}, at @var{addr}. If @var{ena}
>> is @samp{E}, then the tracepoint is enabled; if it is @samp{D}, then
>> -the tracepoint is disabled. The @var{step} gives the tracepoint's step
>> -count, and @var{pass} gives its pass count. If an @samp{F} is present,
>> -then the tracepoint is to be a fast tracepoint, and the @var{flen} is
>> -the number of bytes that the target should copy elsewhere to make room
>> -for the tracepoint. If an @samp{X} is present, it introduces a
>> -tracepoint condition, which consists of a hexadecimal length, followed
>> -by a comma and hex-encoded bytes, in a manner similar to action
>> -encodings as described below. If the trailing @samp{-} is present,
>> -further @samp{QTDP} packets will follow to specify this tracepoint's
>> -actions.
>> +the tracepoint is disabled. The @var{step} gives the tracepoint's
>> +step count, and @var{pass} gives its pass count. If an @samp{F} is
>> +present, then the tracepoint is to be a fast tracepoint, and the
>> +@var{flen} is the number of bytes that the target should copy
>> +elsewhere to make room for the tracepoint. If an @samp{X} is present,
>> +it introduces a tracepoint condition, which consists of a hexadecimal
>> +length, followed by a comma and hex-encoded bytes, in a manner similar
>
> Please in the future try not to re-fill previous text: it makes harder
> for me to find your changes among a potentially large paragraph, such
> as this one.
OK sorry about that.
>
>> +to action encodings as described below. If a @samp{K} is present, it
>> +indicates a target specific breakpoint length. E.g., the arm and mips
>> +can insert either a 2 or 4 byte breakpoint. Some architectures have
>> +additional meanings for kind see : @ref{Architecture-Specific Protocol
>> +Details} .
>
> There's no need for the colon ':' after "see", and please delete the
> space before the period that ends the last sentence above.
Done
>
> Otherwise, the documentation parts are OK. Thanks.
>
Thanks.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-11 12:13 ` [PATCH 2/7] Move some integer operations to common Antoine Tremblay
@ 2015-09-11 14:24 ` Gary Benson
2015-09-11 17:16 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Gary Benson @ 2015-09-11 14:24 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Hi Antoine,
Please don't introduce "#ifdef GDBSERVER" conditionals into common
code, I spent some time removing them. I know I didn't get them
all, but the remaining two are on my hit list.
To work around this... do you really need to deal with endianness in
gdbserver? It's running on the target so if you're pulling numbers
from target memory in target endianness then could you use or
generalize target_read_uint32 for your needs?
Thanks,
Gary
Antoine Tremblay wrote:
> This patch is in preparation for sharing code between GDB and GDBServer to
> enable software single stepping on ARM aarch32-linux.
>
> It moves multiple functions related to extracting or storing a value based on
> its endianness from findvar.c in gdb to a new file called int-utils.c in
> common.
>
> Definitions of these functions are also moved to defs.h to common-defs.h.
>
> gdb/ChangeLog:
> * Makefile.in: Add int-utils.o.
> * common/common-defs.h: New functions defs from defs.h.
> * common/int-utils.c: New file.
> * common/int-utils.h: New file.
> * defs.h: Move functions defs to common-defs.h.
> * findvar.c (extract_signed_integer): Move to int-utils.c.
> (extract_unsigned_integer): Likewise.
> (extract_long_unsigned_integer): Likewise.
> (store_signed_integer): Likewise.
> (store_unsigned_integer): Likewise.
> ---
> gdb/Makefile.in | 9 ++-
> gdb/common/common-defs.h | 1 +
> gdb/common/int-utils.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++
> gdb/common/int-utils.h | 45 +++++++++++
> gdb/defs.h | 16 ----
> gdb/findvar.c | 176 -----------------------------------------
> 6 files changed, 252 insertions(+), 194 deletions(-)
> create mode 100644 gdb/common/int-utils.c
> create mode 100644 gdb/common/int-utils.h
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 0d7cf97..e20c5a6 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -854,6 +854,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
> infcall.c \
> infcmd.c inflow.c infrun.c \
> inline-frame.c \
> + common/int-utils.c \
> interps.c \
> jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c jv-varobj.c \
> language.c linespec.c location.c minidebug.c \
> @@ -985,7 +986,7 @@ i386-linux-nat.h common/common-defs.h common/errors.h common/common-types.h \
> common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
> common/common-exceptions.h target/target.h common/symbol.h \
> common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
> -common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
> +common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h common/int-utils.h \
> nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h
>
> # Header files that already have srcdir in them, or which are in objdir.
> @@ -1084,7 +1085,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
> common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
> format.o registry.o btrace.o record-btrace.o waitstatus.o \
> print-utils.o rsp-low.o errors.o common-debug.o debug.o \
> - common-exceptions.o btrace-common.o fileio.o \
> + common-exceptions.o btrace-common.o fileio.o int-utils.o \
> $(SUBDIR_GCC_COMPILE_OBS)
>
> TSOBS = inflow.o
> @@ -2265,6 +2266,10 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
> fileio.o: ${srcdir}/common/fileio.c
> $(COMPILE) $(srcdir)/common/fileio.c
> $(POSTCOMPILE)
> +int-utils.o: ${srcdir}/common/int-utils.c
> + $(COMPILE) $(srcdir)/common/int-utils.c
> + $(POSTCOMPILE)
> +
> #
> # gdb/target/ dependencies
> #
> diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
> index 2be0d7d..cb79234 100644
> --- a/gdb/common/common-defs.h
> +++ b/gdb/common/common-defs.h
> @@ -49,6 +49,7 @@
> #include "common-debug.h"
> #include "cleanups.h"
> #include "common-exceptions.h"
> +#include "int-utils.h"
>
> #ifdef __cplusplus
> # define EXTERN_C extern "C"
> diff --git a/gdb/common/int-utils.c b/gdb/common/int-utils.c
> new file mode 100644
> index 0000000..57d0dba
> --- /dev/null
> +++ b/gdb/common/int-utils.c
> @@ -0,0 +1,199 @@
> +/* Shared utility routines for integer endianness manipulations.
> + Copyright (C) 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"
> +
> +/* Basic byte-swapping routines. All 'extract' functions return a
> + host-format integer from a target-format integer at ADDR which is
> + LEN bytes long. */
> +
> +LONGEST
> +extract_signed_integer (const gdb_byte *addr, int len,
> + enum bfd_endian byte_order)
> +{
> + LONGEST retval;
> + const unsigned char *p;
> + const unsigned char *startaddr = addr;
> + const unsigned char *endaddr = startaddr + len;
> +
> + if (len > (int) sizeof (LONGEST))
> + error (_("\
> +That operation is not available on integers of more than %d bytes."),
> + (int) sizeof (LONGEST));
> +
> + /* Start at the most significant end of the integer, and work towards
> + the least significant. */
> + if (byte_order == BFD_ENDIAN_BIG)
> + {
> + p = startaddr;
> + /* Do the sign extension once at the start. */
> + retval = ((LONGEST) * p ^ 0x80) - 0x80;
> + for (++p; p < endaddr; ++p)
> + retval = (retval << 8) | *p;
> + }
> + else
> + {
> + p = endaddr - 1;
> + /* Do the sign extension once at the start. */
> + retval = ((LONGEST) * p ^ 0x80) - 0x80;
> + for (--p; p >= startaddr; --p)
> + retval = (retval << 8) | *p;
> + }
> + return retval;
> +}
> +
> +ULONGEST
> +extract_unsigned_integer (const gdb_byte *addr, int len,
> + enum bfd_endian byte_order)
> +{
> + ULONGEST retval;
> + const unsigned char *p;
> + const unsigned char *startaddr = addr;
> + const unsigned char *endaddr = startaddr + len;
> +
> + if (len > (int) sizeof (ULONGEST))
> + error (_("\
> +That operation is not available on integers of more than %d bytes."),
> + (int) sizeof (ULONGEST));
> +
> + /* Start at the most significant end of the integer, and work towards
> + the least significant. */
> + retval = 0;
> + if (byte_order == BFD_ENDIAN_BIG)
> + {
> + for (p = startaddr; p < endaddr; ++p)
> + retval = (retval << 8) | *p;
> + }
> + else
> + {
> + for (p = endaddr - 1; p >= startaddr; --p)
> + retval = (retval << 8) | *p;
> + }
> + return retval;
> +}
> +
> +/* Sometimes a long long unsigned integer can be extracted as a
> + LONGEST value. This is done so that we can print these values
> + better. If this integer can be converted to a LONGEST, this
> + function returns 1 and sets *PVAL. Otherwise it returns 0. */
> +
> +int
> +extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
> + enum bfd_endian byte_order, LONGEST *pval)
> +{
> + const gdb_byte *p;
> + const gdb_byte *first_addr;
> + int len;
> +
> + len = orig_len;
> + if (byte_order == BFD_ENDIAN_BIG)
> + {
> + for (p = addr;
> + len > (int) sizeof (LONGEST) && p < addr + orig_len;
> + p++)
> + {
> + if (*p == 0)
> + len--;
> + else
> + break;
> + }
> + first_addr = p;
> + }
> + else
> + {
> + first_addr = addr;
> + for (p = addr + orig_len - 1;
> + len > (int) sizeof (LONGEST) && p >= addr;
> + p--)
> + {
> + if (*p == 0)
> + len--;
> + else
> + break;
> + }
> + }
> +
> + if (len <= (int) sizeof (LONGEST))
> + {
> + *pval = (LONGEST) extract_unsigned_integer (first_addr,
> + sizeof (LONGEST),
> + byte_order);
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +/* All 'store' functions accept a host-format integer and store a
> + target-format integer at ADDR which is LEN bytes long. */
> +
> +void
> +store_signed_integer (gdb_byte *addr, int len,
> + enum bfd_endian byte_order, LONGEST val)
> +{
> + gdb_byte *p;
> + gdb_byte *startaddr = addr;
> + gdb_byte *endaddr = startaddr + len;
> +
> + /* Start at the least significant end of the integer, and work towards
> + the most significant. */
> + if (byte_order == BFD_ENDIAN_BIG)
> + {
> + for (p = endaddr - 1; p >= startaddr; --p)
> + {
> + *p = val & 0xff;
> + val >>= 8;
> + }
> + }
> + else
> + {
> + for (p = startaddr; p < endaddr; ++p)
> + {
> + *p = val & 0xff;
> + val >>= 8;
> + }
> + }
> +}
> +
> +void
> +store_unsigned_integer (gdb_byte *addr, int len,
> + enum bfd_endian byte_order, ULONGEST val)
> +{
> + unsigned char *p;
> + unsigned char *startaddr = (unsigned char *) addr;
> + unsigned char *endaddr = startaddr + len;
> +
> + /* Start at the least significant end of the integer, and work towards
> + the most significant. */
> + if (byte_order == BFD_ENDIAN_BIG)
> + {
> + for (p = endaddr - 1; p >= startaddr; --p)
> + {
> + *p = val & 0xff;
> + val >>= 8;
> + }
> + }
> + else
> + {
> + for (p = startaddr; p < endaddr; ++p)
> + {
> + *p = val & 0xff;
> + val >>= 8;
> + }
> + }
> +}
> diff --git a/gdb/common/int-utils.h b/gdb/common/int-utils.h
> new file mode 100644
> index 0000000..c170348
> --- /dev/null
> +++ b/gdb/common/int-utils.h
> @@ -0,0 +1,45 @@
> +/* Shared utility routines for integer endianness manipulations.
> + Copyright (C) 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 INT_UTILS_H
> +#define INT_UTILS_H 1
> +
> +#ifdef GDBSERVER
> +/* Allow this enum without requiring bfd in gdbserver. */
> +enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
> +#else
> +#include "bfd.h"
> +#endif
> +
> +extern LONGEST extract_signed_integer (const gdb_byte *, int,
> + enum bfd_endian);
> +
> +extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
> + enum bfd_endian);
> +
> +extern int extract_long_unsigned_integer (const gdb_byte *, int,
> + enum bfd_endian, LONGEST *);
> +
> +extern void store_signed_integer (gdb_byte *, int,
> + enum bfd_endian, LONGEST);
> +
> +extern void store_unsigned_integer (gdb_byte *, int,
> + enum bfd_endian, ULONGEST);
> +
> +#endif /* INT_UTILS_H */
> diff --git a/gdb/defs.h b/gdb/defs.h
> index 03f7e8a..e292977 100644
> --- a/gdb/defs.h
> +++ b/gdb/defs.h
> @@ -596,28 +596,12 @@ enum { MAX_REGISTER_SIZE = 64 };
>
> /* In findvar.c. */
>
> -extern LONGEST extract_signed_integer (const gdb_byte *, int,
> - enum bfd_endian);
> -
> -extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
> - enum bfd_endian);
> -
> -extern int extract_long_unsigned_integer (const gdb_byte *, int,
> - enum bfd_endian, LONGEST *);
> -
> extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
> struct type *type);
>
> -extern void store_signed_integer (gdb_byte *, int,
> - enum bfd_endian, LONGEST);
> -
> -extern void store_unsigned_integer (gdb_byte *, int,
> - enum bfd_endian, ULONGEST);
> -
> extern void store_typed_address (gdb_byte *buf, struct type *type,
> CORE_ADDR addr);
>
> -\f
> /* From valops.c */
>
> extern int watchdog;
> diff --git a/gdb/findvar.c b/gdb/findvar.c
> index 1c077f7..2299ca4 100644
> --- a/gdb/findvar.c
> +++ b/gdb/findvar.c
> @@ -46,124 +46,6 @@
> you lose
> #endif
>
> -LONGEST
> -extract_signed_integer (const gdb_byte *addr, int len,
> - enum bfd_endian byte_order)
> -{
> - LONGEST retval;
> - const unsigned char *p;
> - const unsigned char *startaddr = addr;
> - const unsigned char *endaddr = startaddr + len;
> -
> - if (len > (int) sizeof (LONGEST))
> - error (_("\
> -That operation is not available on integers of more than %d bytes."),
> - (int) sizeof (LONGEST));
> -
> - /* Start at the most significant end of the integer, and work towards
> - the least significant. */
> - if (byte_order == BFD_ENDIAN_BIG)
> - {
> - p = startaddr;
> - /* Do the sign extension once at the start. */
> - retval = ((LONGEST) * p ^ 0x80) - 0x80;
> - for (++p; p < endaddr; ++p)
> - retval = (retval << 8) | *p;
> - }
> - else
> - {
> - p = endaddr - 1;
> - /* Do the sign extension once at the start. */
> - retval = ((LONGEST) * p ^ 0x80) - 0x80;
> - for (--p; p >= startaddr; --p)
> - retval = (retval << 8) | *p;
> - }
> - return retval;
> -}
> -
> -ULONGEST
> -extract_unsigned_integer (const gdb_byte *addr, int len,
> - enum bfd_endian byte_order)
> -{
> - ULONGEST retval;
> - const unsigned char *p;
> - const unsigned char *startaddr = addr;
> - const unsigned char *endaddr = startaddr + len;
> -
> - if (len > (int) sizeof (ULONGEST))
> - error (_("\
> -That operation is not available on integers of more than %d bytes."),
> - (int) sizeof (ULONGEST));
> -
> - /* Start at the most significant end of the integer, and work towards
> - the least significant. */
> - retval = 0;
> - if (byte_order == BFD_ENDIAN_BIG)
> - {
> - for (p = startaddr; p < endaddr; ++p)
> - retval = (retval << 8) | *p;
> - }
> - else
> - {
> - for (p = endaddr - 1; p >= startaddr; --p)
> - retval = (retval << 8) | *p;
> - }
> - return retval;
> -}
> -
> -/* Sometimes a long long unsigned integer can be extracted as a
> - LONGEST value. This is done so that we can print these values
> - better. If this integer can be converted to a LONGEST, this
> - function returns 1 and sets *PVAL. Otherwise it returns 0. */
> -
> -int
> -extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
> - enum bfd_endian byte_order, LONGEST *pval)
> -{
> - const gdb_byte *p;
> - const gdb_byte *first_addr;
> - int len;
> -
> - len = orig_len;
> - if (byte_order == BFD_ENDIAN_BIG)
> - {
> - for (p = addr;
> - len > (int) sizeof (LONGEST) && p < addr + orig_len;
> - p++)
> - {
> - if (*p == 0)
> - len--;
> - else
> - break;
> - }
> - first_addr = p;
> - }
> - else
> - {
> - first_addr = addr;
> - for (p = addr + orig_len - 1;
> - len > (int) sizeof (LONGEST) && p >= addr;
> - p--)
> - {
> - if (*p == 0)
> - len--;
> - else
> - break;
> - }
> - }
> -
> - if (len <= (int) sizeof (LONGEST))
> - {
> - *pval = (LONGEST) extract_unsigned_integer (first_addr,
> - sizeof (LONGEST),
> - byte_order);
> - return 1;
> - }
> -
> - return 0;
> -}
> -
> -
> /* Treat the bytes at BUF as a pointer of type TYPE, and return the
> address it represents. */
> CORE_ADDR
> @@ -178,64 +60,6 @@ extract_typed_address (const gdb_byte *buf, struct type *type)
> return gdbarch_pointer_to_address (get_type_arch (type), type, buf);
> }
>
> -/* All 'store' functions accept a host-format integer and store a
> - target-format integer at ADDR which is LEN bytes long. */
> -
> -void
> -store_signed_integer (gdb_byte *addr, int len,
> - enum bfd_endian byte_order, LONGEST val)
> -{
> - gdb_byte *p;
> - gdb_byte *startaddr = addr;
> - gdb_byte *endaddr = startaddr + len;
> -
> - /* Start at the least significant end of the integer, and work towards
> - the most significant. */
> - if (byte_order == BFD_ENDIAN_BIG)
> - {
> - for (p = endaddr - 1; p >= startaddr; --p)
> - {
> - *p = val & 0xff;
> - val >>= 8;
> - }
> - }
> - else
> - {
> - for (p = startaddr; p < endaddr; ++p)
> - {
> - *p = val & 0xff;
> - val >>= 8;
> - }
> - }
> -}
> -
> -void
> -store_unsigned_integer (gdb_byte *addr, int len,
> - enum bfd_endian byte_order, ULONGEST val)
> -{
> - unsigned char *p;
> - unsigned char *startaddr = (unsigned char *) addr;
> - unsigned char *endaddr = startaddr + len;
> -
> - /* Start at the least significant end of the integer, and work towards
> - the most significant. */
> - if (byte_order == BFD_ENDIAN_BIG)
> - {
> - for (p = endaddr - 1; p >= startaddr; --p)
> - {
> - *p = val & 0xff;
> - val >>= 8;
> - }
> - }
> - else
> - {
> - for (p = startaddr; p < endaddr; ++p)
> - {
> - *p = val & 0xff;
> - val >>= 8;
> - }
> - }
> -}
>
> /* Store the address ADDR as a pointer of type TYPE at BUF, in target
> form. */
> --
> 1.9.1
--
http://gbenson.net/
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-11 14:24 ` Gary Benson
@ 2015-09-11 17:16 ` Antoine Tremblay
2015-09-11 17:32 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 17:16 UTC (permalink / raw)
To: Gary Benson; +Cc: gdb-patches
On 09/11/2015 10:24 AM, Gary Benson wrote:
> Hi Antoine,
>
> Please don't introduce "#ifdef GDBSERVER" conditionals into common
> code, I spent some time removing them. I know I didn't get them
> all, but the remaining two are on my hit list.
>
Humm what is the issue that makes this a bad idea if I may ?
> To work around this... do you really need to deal with endianness in
> gdbserver? It's running on the target so if you're pulling numbers
> from target memory in target endianness then could you use or
> generalize target_read_uint32 for your needs?
>
Yes unfortunately I do need the endianness since the program can be in a
different endianness then GDBServer the code section can even be
different than the data section...
I could however :
- Remove int-utils.h from common-defs.h and move to defs.h for GDB
- Move the int-utils.h in server.h for GDBServer which is kinda the
GDBServer's equivalent of defs.h with the BFD defines before the include...
Does that sound ok ?
> Antoine Tremblay wrote:
>> This patch is in preparation for sharing code between GDB and GDBServer to
>> enable software single stepping on ARM aarch32-linux.
>>
>> It moves multiple functions related to extracting or storing a value based on
>> its endianness from findvar.c in gdb to a new file called int-utils.c in
>> common.
>>
>> Definitions of these functions are also moved to defs.h to common-defs.h.
>>
>> gdb/ChangeLog:
>> * Makefile.in: Add int-utils.o.
>> * common/common-defs.h: New functions defs from defs.h.
>> * common/int-utils.c: New file.
>> * common/int-utils.h: New file.
>> * defs.h: Move functions defs to common-defs.h.
>> * findvar.c (extract_signed_integer): Move to int-utils.c.
>> (extract_unsigned_integer): Likewise.
>> (extract_long_unsigned_integer): Likewise.
>> (store_signed_integer): Likewise.
>> (store_unsigned_integer): Likewise.
>> ---
>> gdb/Makefile.in | 9 ++-
>> gdb/common/common-defs.h | 1 +
>> gdb/common/int-utils.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++
>> gdb/common/int-utils.h | 45 +++++++++++
>> gdb/defs.h | 16 ----
>> gdb/findvar.c | 176 -----------------------------------------
>> 6 files changed, 252 insertions(+), 194 deletions(-)
>> create mode 100644 gdb/common/int-utils.c
>> create mode 100644 gdb/common/int-utils.h
>>
>> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
>> index 0d7cf97..e20c5a6 100644
>> --- a/gdb/Makefile.in
>> +++ b/gdb/Makefile.in
>> @@ -854,6 +854,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
>> infcall.c \
>> infcmd.c inflow.c infrun.c \
>> inline-frame.c \
>> + common/int-utils.c \
>> interps.c \
>> jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c jv-varobj.c \
>> language.c linespec.c location.c minidebug.c \
>> @@ -985,7 +986,7 @@ i386-linux-nat.h common/common-defs.h common/errors.h common/common-types.h \
>> common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
>> common/common-exceptions.h target/target.h common/symbol.h \
>> common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
>> -common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
>> +common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h common/int-utils.h \
>> nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h
>>
>> # Header files that already have srcdir in them, or which are in objdir.
>> @@ -1084,7 +1085,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
>> common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
>> format.o registry.o btrace.o record-btrace.o waitstatus.o \
>> print-utils.o rsp-low.o errors.o common-debug.o debug.o \
>> - common-exceptions.o btrace-common.o fileio.o \
>> + common-exceptions.o btrace-common.o fileio.o int-utils.o \
>> $(SUBDIR_GCC_COMPILE_OBS)
>>
>> TSOBS = inflow.o
>> @@ -2265,6 +2266,10 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
>> fileio.o: ${srcdir}/common/fileio.c
>> $(COMPILE) $(srcdir)/common/fileio.c
>> $(POSTCOMPILE)
>> +int-utils.o: ${srcdir}/common/int-utils.c
>> + $(COMPILE) $(srcdir)/common/int-utils.c
>> + $(POSTCOMPILE)
>> +
>> #
>> # gdb/target/ dependencies
>> #
>> diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
>> index 2be0d7d..cb79234 100644
>> --- a/gdb/common/common-defs.h
>> +++ b/gdb/common/common-defs.h
>> @@ -49,6 +49,7 @@
>> #include "common-debug.h"
>> #include "cleanups.h"
>> #include "common-exceptions.h"
>> +#include "int-utils.h"
>>
>> #ifdef __cplusplus
>> # define EXTERN_C extern "C"
>> diff --git a/gdb/common/int-utils.c b/gdb/common/int-utils.c
>> new file mode 100644
>> index 0000000..57d0dba
>> --- /dev/null
>> +++ b/gdb/common/int-utils.c
>> @@ -0,0 +1,199 @@
>> +/* Shared utility routines for integer endianness manipulations.
>> + Copyright (C) 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"
>> +
>> +/* Basic byte-swapping routines. All 'extract' functions return a
>> + host-format integer from a target-format integer at ADDR which is
>> + LEN bytes long. */
>> +
>> +LONGEST
>> +extract_signed_integer (const gdb_byte *addr, int len,
>> + enum bfd_endian byte_order)
>> +{
>> + LONGEST retval;
>> + const unsigned char *p;
>> + const unsigned char *startaddr = addr;
>> + const unsigned char *endaddr = startaddr + len;
>> +
>> + if (len > (int) sizeof (LONGEST))
>> + error (_("\
>> +That operation is not available on integers of more than %d bytes."),
>> + (int) sizeof (LONGEST));
>> +
>> + /* Start at the most significant end of the integer, and work towards
>> + the least significant. */
>> + if (byte_order == BFD_ENDIAN_BIG)
>> + {
>> + p = startaddr;
>> + /* Do the sign extension once at the start. */
>> + retval = ((LONGEST) * p ^ 0x80) - 0x80;
>> + for (++p; p < endaddr; ++p)
>> + retval = (retval << 8) | *p;
>> + }
>> + else
>> + {
>> + p = endaddr - 1;
>> + /* Do the sign extension once at the start. */
>> + retval = ((LONGEST) * p ^ 0x80) - 0x80;
>> + for (--p; p >= startaddr; --p)
>> + retval = (retval << 8) | *p;
>> + }
>> + return retval;
>> +}
>> +
>> +ULONGEST
>> +extract_unsigned_integer (const gdb_byte *addr, int len,
>> + enum bfd_endian byte_order)
>> +{
>> + ULONGEST retval;
>> + const unsigned char *p;
>> + const unsigned char *startaddr = addr;
>> + const unsigned char *endaddr = startaddr + len;
>> +
>> + if (len > (int) sizeof (ULONGEST))
>> + error (_("\
>> +That operation is not available on integers of more than %d bytes."),
>> + (int) sizeof (ULONGEST));
>> +
>> + /* Start at the most significant end of the integer, and work towards
>> + the least significant. */
>> + retval = 0;
>> + if (byte_order == BFD_ENDIAN_BIG)
>> + {
>> + for (p = startaddr; p < endaddr; ++p)
>> + retval = (retval << 8) | *p;
>> + }
>> + else
>> + {
>> + for (p = endaddr - 1; p >= startaddr; --p)
>> + retval = (retval << 8) | *p;
>> + }
>> + return retval;
>> +}
>> +
>> +/* Sometimes a long long unsigned integer can be extracted as a
>> + LONGEST value. This is done so that we can print these values
>> + better. If this integer can be converted to a LONGEST, this
>> + function returns 1 and sets *PVAL. Otherwise it returns 0. */
>> +
>> +int
>> +extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
>> + enum bfd_endian byte_order, LONGEST *pval)
>> +{
>> + const gdb_byte *p;
>> + const gdb_byte *first_addr;
>> + int len;
>> +
>> + len = orig_len;
>> + if (byte_order == BFD_ENDIAN_BIG)
>> + {
>> + for (p = addr;
>> + len > (int) sizeof (LONGEST) && p < addr + orig_len;
>> + p++)
>> + {
>> + if (*p == 0)
>> + len--;
>> + else
>> + break;
>> + }
>> + first_addr = p;
>> + }
>> + else
>> + {
>> + first_addr = addr;
>> + for (p = addr + orig_len - 1;
>> + len > (int) sizeof (LONGEST) && p >= addr;
>> + p--)
>> + {
>> + if (*p == 0)
>> + len--;
>> + else
>> + break;
>> + }
>> + }
>> +
>> + if (len <= (int) sizeof (LONGEST))
>> + {
>> + *pval = (LONGEST) extract_unsigned_integer (first_addr,
>> + sizeof (LONGEST),
>> + byte_order);
>> + return 1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/* All 'store' functions accept a host-format integer and store a
>> + target-format integer at ADDR which is LEN bytes long. */
>> +
>> +void
>> +store_signed_integer (gdb_byte *addr, int len,
>> + enum bfd_endian byte_order, LONGEST val)
>> +{
>> + gdb_byte *p;
>> + gdb_byte *startaddr = addr;
>> + gdb_byte *endaddr = startaddr + len;
>> +
>> + /* Start at the least significant end of the integer, and work towards
>> + the most significant. */
>> + if (byte_order == BFD_ENDIAN_BIG)
>> + {
>> + for (p = endaddr - 1; p >= startaddr; --p)
>> + {
>> + *p = val & 0xff;
>> + val >>= 8;
>> + }
>> + }
>> + else
>> + {
>> + for (p = startaddr; p < endaddr; ++p)
>> + {
>> + *p = val & 0xff;
>> + val >>= 8;
>> + }
>> + }
>> +}
>> +
>> +void
>> +store_unsigned_integer (gdb_byte *addr, int len,
>> + enum bfd_endian byte_order, ULONGEST val)
>> +{
>> + unsigned char *p;
>> + unsigned char *startaddr = (unsigned char *) addr;
>> + unsigned char *endaddr = startaddr + len;
>> +
>> + /* Start at the least significant end of the integer, and work towards
>> + the most significant. */
>> + if (byte_order == BFD_ENDIAN_BIG)
>> + {
>> + for (p = endaddr - 1; p >= startaddr; --p)
>> + {
>> + *p = val & 0xff;
>> + val >>= 8;
>> + }
>> + }
>> + else
>> + {
>> + for (p = startaddr; p < endaddr; ++p)
>> + {
>> + *p = val & 0xff;
>> + val >>= 8;
>> + }
>> + }
>> +}
>> diff --git a/gdb/common/int-utils.h b/gdb/common/int-utils.h
>> new file mode 100644
>> index 0000000..c170348
>> --- /dev/null
>> +++ b/gdb/common/int-utils.h
>> @@ -0,0 +1,45 @@
>> +/* Shared utility routines for integer endianness manipulations.
>> + Copyright (C) 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 INT_UTILS_H
>> +#define INT_UTILS_H 1
>> +
>> +#ifdef GDBSERVER
>> +/* Allow this enum without requiring bfd in gdbserver. */
>> +enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
>> +#else
>> +#include "bfd.h"
>> +#endif
>> +
>> +extern LONGEST extract_signed_integer (const gdb_byte *, int,
>> + enum bfd_endian);
>> +
>> +extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
>> + enum bfd_endian);
>> +
>> +extern int extract_long_unsigned_integer (const gdb_byte *, int,
>> + enum bfd_endian, LONGEST *);
>> +
>> +extern void store_signed_integer (gdb_byte *, int,
>> + enum bfd_endian, LONGEST);
>> +
>> +extern void store_unsigned_integer (gdb_byte *, int,
>> + enum bfd_endian, ULONGEST);
>> +
>> +#endif /* INT_UTILS_H */
>> diff --git a/gdb/defs.h b/gdb/defs.h
>> index 03f7e8a..e292977 100644
>> --- a/gdb/defs.h
>> +++ b/gdb/defs.h
>> @@ -596,28 +596,12 @@ enum { MAX_REGISTER_SIZE = 64 };
>>
>> /* In findvar.c. */
>>
>> -extern LONGEST extract_signed_integer (const gdb_byte *, int,
>> - enum bfd_endian);
>> -
>> -extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
>> - enum bfd_endian);
>> -
>> -extern int extract_long_unsigned_integer (const gdb_byte *, int,
>> - enum bfd_endian, LONGEST *);
>> -
>> extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
>> struct type *type);
>>
>> -extern void store_signed_integer (gdb_byte *, int,
>> - enum bfd_endian, LONGEST);
>> -
>> -extern void store_unsigned_integer (gdb_byte *, int,
>> - enum bfd_endian, ULONGEST);
>> -
>> extern void store_typed_address (gdb_byte *buf, struct type *type,
>> CORE_ADDR addr);
>>
>> -\f
>> /* From valops.c */
>>
>> extern int watchdog;
>> diff --git a/gdb/findvar.c b/gdb/findvar.c
>> index 1c077f7..2299ca4 100644
>> --- a/gdb/findvar.c
>> +++ b/gdb/findvar.c
>> @@ -46,124 +46,6 @@
>> you lose
>> #endif
>>
>> -LONGEST
>> -extract_signed_integer (const gdb_byte *addr, int len,
>> - enum bfd_endian byte_order)
>> -{
>> - LONGEST retval;
>> - const unsigned char *p;
>> - const unsigned char *startaddr = addr;
>> - const unsigned char *endaddr = startaddr + len;
>> -
>> - if (len > (int) sizeof (LONGEST))
>> - error (_("\
>> -That operation is not available on integers of more than %d bytes."),
>> - (int) sizeof (LONGEST));
>> -
>> - /* Start at the most significant end of the integer, and work towards
>> - the least significant. */
>> - if (byte_order == BFD_ENDIAN_BIG)
>> - {
>> - p = startaddr;
>> - /* Do the sign extension once at the start. */
>> - retval = ((LONGEST) * p ^ 0x80) - 0x80;
>> - for (++p; p < endaddr; ++p)
>> - retval = (retval << 8) | *p;
>> - }
>> - else
>> - {
>> - p = endaddr - 1;
>> - /* Do the sign extension once at the start. */
>> - retval = ((LONGEST) * p ^ 0x80) - 0x80;
>> - for (--p; p >= startaddr; --p)
>> - retval = (retval << 8) | *p;
>> - }
>> - return retval;
>> -}
>> -
>> -ULONGEST
>> -extract_unsigned_integer (const gdb_byte *addr, int len,
>> - enum bfd_endian byte_order)
>> -{
>> - ULONGEST retval;
>> - const unsigned char *p;
>> - const unsigned char *startaddr = addr;
>> - const unsigned char *endaddr = startaddr + len;
>> -
>> - if (len > (int) sizeof (ULONGEST))
>> - error (_("\
>> -That operation is not available on integers of more than %d bytes."),
>> - (int) sizeof (ULONGEST));
>> -
>> - /* Start at the most significant end of the integer, and work towards
>> - the least significant. */
>> - retval = 0;
>> - if (byte_order == BFD_ENDIAN_BIG)
>> - {
>> - for (p = startaddr; p < endaddr; ++p)
>> - retval = (retval << 8) | *p;
>> - }
>> - else
>> - {
>> - for (p = endaddr - 1; p >= startaddr; --p)
>> - retval = (retval << 8) | *p;
>> - }
>> - return retval;
>> -}
>> -
>> -/* Sometimes a long long unsigned integer can be extracted as a
>> - LONGEST value. This is done so that we can print these values
>> - better. If this integer can be converted to a LONGEST, this
>> - function returns 1 and sets *PVAL. Otherwise it returns 0. */
>> -
>> -int
>> -extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
>> - enum bfd_endian byte_order, LONGEST *pval)
>> -{
>> - const gdb_byte *p;
>> - const gdb_byte *first_addr;
>> - int len;
>> -
>> - len = orig_len;
>> - if (byte_order == BFD_ENDIAN_BIG)
>> - {
>> - for (p = addr;
>> - len > (int) sizeof (LONGEST) && p < addr + orig_len;
>> - p++)
>> - {
>> - if (*p == 0)
>> - len--;
>> - else
>> - break;
>> - }
>> - first_addr = p;
>> - }
>> - else
>> - {
>> - first_addr = addr;
>> - for (p = addr + orig_len - 1;
>> - len > (int) sizeof (LONGEST) && p >= addr;
>> - p--)
>> - {
>> - if (*p == 0)
>> - len--;
>> - else
>> - break;
>> - }
>> - }
>> -
>> - if (len <= (int) sizeof (LONGEST))
>> - {
>> - *pval = (LONGEST) extract_unsigned_integer (first_addr,
>> - sizeof (LONGEST),
>> - byte_order);
>> - return 1;
>> - }
>> -
>> - return 0;
>> -}
>> -
>> -
>> /* Treat the bytes at BUF as a pointer of type TYPE, and return the
>> address it represents. */
>> CORE_ADDR
>> @@ -178,64 +60,6 @@ extract_typed_address (const gdb_byte *buf, struct type *type)
>> return gdbarch_pointer_to_address (get_type_arch (type), type, buf);
>> }
>>
>> -/* All 'store' functions accept a host-format integer and store a
>> - target-format integer at ADDR which is LEN bytes long. */
>> -
>> -void
>> -store_signed_integer (gdb_byte *addr, int len,
>> - enum bfd_endian byte_order, LONGEST val)
>> -{
>> - gdb_byte *p;
>> - gdb_byte *startaddr = addr;
>> - gdb_byte *endaddr = startaddr + len;
>> -
>> - /* Start at the least significant end of the integer, and work towards
>> - the most significant. */
>> - if (byte_order == BFD_ENDIAN_BIG)
>> - {
>> - for (p = endaddr - 1; p >= startaddr; --p)
>> - {
>> - *p = val & 0xff;
>> - val >>= 8;
>> - }
>> - }
>> - else
>> - {
>> - for (p = startaddr; p < endaddr; ++p)
>> - {
>> - *p = val & 0xff;
>> - val >>= 8;
>> - }
>> - }
>> -}
>> -
>> -void
>> -store_unsigned_integer (gdb_byte *addr, int len,
>> - enum bfd_endian byte_order, ULONGEST val)
>> -{
>> - unsigned char *p;
>> - unsigned char *startaddr = (unsigned char *) addr;
>> - unsigned char *endaddr = startaddr + len;
>> -
>> - /* Start at the least significant end of the integer, and work towards
>> - the most significant. */
>> - if (byte_order == BFD_ENDIAN_BIG)
>> - {
>> - for (p = endaddr - 1; p >= startaddr; --p)
>> - {
>> - *p = val & 0xff;
>> - val >>= 8;
>> - }
>> - }
>> - else
>> - {
>> - for (p = startaddr; p < endaddr; ++p)
>> - {
>> - *p = val & 0xff;
>> - val >>= 8;
>> - }
>> - }
>> -}
>>
>> /* Store the address ADDR as a pointer of type TYPE at BUF, in target
>> form. */
>> --
>> 1.9.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-11 17:16 ` Antoine Tremblay
@ 2015-09-11 17:32 ` Antoine Tremblay
2015-09-14 9:24 ` Gary Benson
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 17:32 UTC (permalink / raw)
To: Gary Benson; +Cc: gdb-patches
On 09/11/2015 01:16 PM, Antoine Tremblay wrote:
>
>
> On 09/11/2015 10:24 AM, Gary Benson wrote:
>> Hi Antoine,
>>
>> Please don't introduce "#ifdef GDBSERVER" conditionals into common
>> code, I spent some time removing them. I know I didn't get them
>> all, but the remaining two are on my hit list.
>>
>
> Humm what is the issue that makes this a bad idea if I may ?
>
>
>> To work around this... do you really need to deal with endianness in
>> gdbserver? It's running on the target so if you're pulling numbers
>> from target memory in target endianness then could you use or
>> generalize target_read_uint32 for your needs?
>>
>
> Yes unfortunately I do need the endianness since the program can be in a
> different endianness then GDBServer the code section can even be
> different than the data section...
>
> I could however :
>
> - Remove int-utils.h from common-defs.h and move to defs.h for GDB
> - Move the int-utils.h in server.h for GDBServer which is kinda the
> GDBServer's equivalent of defs.h with the BFD defines before the include...
>
> Does that sound ok ?
>
Actually this doesn't work since there's an int-utils.c that needs to
include again either bfh.h or define the enum with the same #ifdef
GSBSERVER ... it would just be moved to the .c ...
I would need to move the code to a .h only...
At this point I need to understand more the argument about the #ifndef
GDBSERVER...?
Or any other suggestions are welcome...
>> Antoine Tremblay wrote:
>>> This patch is in preparation for sharing code between GDB and
>>> GDBServer to
>>> enable software single stepping on ARM aarch32-linux.
>>>
>>> It moves multiple functions related to extracting or storing a value
>>> based on
>>> its endianness from findvar.c in gdb to a new file called int-utils.c in
>>> common.
>>>
>>> Definitions of these functions are also moved to defs.h to
>>> common-defs.h.
>>>
>>> gdb/ChangeLog:
>>> * Makefile.in: Add int-utils.o.
>>> * common/common-defs.h: New functions defs from defs.h.
>>> * common/int-utils.c: New file.
>>> * common/int-utils.h: New file.
>>> * defs.h: Move functions defs to common-defs.h.
>>> * findvar.c (extract_signed_integer): Move to int-utils.c.
>>> (extract_unsigned_integer): Likewise.
>>> (extract_long_unsigned_integer): Likewise.
>>> (store_signed_integer): Likewise.
>>> (store_unsigned_integer): Likewise.
>>> ---
>>> gdb/Makefile.in | 9 ++-
>>> gdb/common/common-defs.h | 1 +
>>> gdb/common/int-utils.c | 199
>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>> gdb/common/int-utils.h | 45 +++++++++++
>>> gdb/defs.h | 16 ----
>>> gdb/findvar.c | 176
>>> -----------------------------------------
>>> 6 files changed, 252 insertions(+), 194 deletions(-)
>>> create mode 100644 gdb/common/int-utils.c
>>> create mode 100644 gdb/common/int-utils.h
>>>
>>> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
>>> index 0d7cf97..e20c5a6 100644
>>> --- a/gdb/Makefile.in
>>> +++ b/gdb/Makefile.in
>>> @@ -854,6 +854,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c
>>> ada-valprint.c ada-tasks.c \
>>> infcall.c \
>>> infcmd.c inflow.c infrun.c \
>>> inline-frame.c \
>>> + common/int-utils.c \
>>> interps.c \
>>> jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c jv-varobj.c \
>>> language.c linespec.c location.c minidebug.c \
>>> @@ -985,7 +986,7 @@ i386-linux-nat.h common/common-defs.h
>>> common/errors.h common/common-types.h \
>>> common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
>>> common/common-exceptions.h target/target.h common/symbol.h \
>>> common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
>>> -common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
>>> +common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h
>>> common/int-utils.h \
>>> nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h
>>>
>>> # Header files that already have srcdir in them, or which are in
>>> objdir.
>>> @@ -1084,7 +1085,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
>>> common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
>>> format.o registry.o btrace.o record-btrace.o waitstatus.o \
>>> print-utils.o rsp-low.o errors.o common-debug.o debug.o \
>>> - common-exceptions.o btrace-common.o fileio.o \
>>> + common-exceptions.o btrace-common.o fileio.o int-utils.o \
>>> $(SUBDIR_GCC_COMPILE_OBS)
>>>
>>> TSOBS = inflow.o
>>> @@ -2265,6 +2266,10 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
>>> fileio.o: ${srcdir}/common/fileio.c
>>> $(COMPILE) $(srcdir)/common/fileio.c
>>> $(POSTCOMPILE)
>>> +int-utils.o: ${srcdir}/common/int-utils.c
>>> + $(COMPILE) $(srcdir)/common/int-utils.c
>>> + $(POSTCOMPILE)
>>> +
>>> #
>>> # gdb/target/ dependencies
>>> #
>>> diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
>>> index 2be0d7d..cb79234 100644
>>> --- a/gdb/common/common-defs.h
>>> +++ b/gdb/common/common-defs.h
>>> @@ -49,6 +49,7 @@
>>> #include "common-debug.h"
>>> #include "cleanups.h"
>>> #include "common-exceptions.h"
>>> +#include "int-utils.h"
>>>
>>> #ifdef __cplusplus
>>> # define EXTERN_C extern "C"
>>> diff --git a/gdb/common/int-utils.c b/gdb/common/int-utils.c
>>> new file mode 100644
>>> index 0000000..57d0dba
>>> --- /dev/null
>>> +++ b/gdb/common/int-utils.c
>>> @@ -0,0 +1,199 @@
>>> +/* Shared utility routines for integer endianness manipulations.
>>> + Copyright (C) 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"
>>> +
>>> +/* Basic byte-swapping routines. All 'extract' functions return a
>>> + host-format integer from a target-format integer at ADDR which is
>>> + LEN bytes long. */
>>> +
>>> +LONGEST
>>> +extract_signed_integer (const gdb_byte *addr, int len,
>>> + enum bfd_endian byte_order)
>>> +{
>>> + LONGEST retval;
>>> + const unsigned char *p;
>>> + const unsigned char *startaddr = addr;
>>> + const unsigned char *endaddr = startaddr + len;
>>> +
>>> + if (len > (int) sizeof (LONGEST))
>>> + error (_("\
>>> +That operation is not available on integers of more than %d bytes."),
>>> + (int) sizeof (LONGEST));
>>> +
>>> + /* Start at the most significant end of the integer, and work towards
>>> + the least significant. */
>>> + if (byte_order == BFD_ENDIAN_BIG)
>>> + {
>>> + p = startaddr;
>>> + /* Do the sign extension once at the start. */
>>> + retval = ((LONGEST) * p ^ 0x80) - 0x80;
>>> + for (++p; p < endaddr; ++p)
>>> + retval = (retval << 8) | *p;
>>> + }
>>> + else
>>> + {
>>> + p = endaddr - 1;
>>> + /* Do the sign extension once at the start. */
>>> + retval = ((LONGEST) * p ^ 0x80) - 0x80;
>>> + for (--p; p >= startaddr; --p)
>>> + retval = (retval << 8) | *p;
>>> + }
>>> + return retval;
>>> +}
>>> +
>>> +ULONGEST
>>> +extract_unsigned_integer (const gdb_byte *addr, int len,
>>> + enum bfd_endian byte_order)
>>> +{
>>> + ULONGEST retval;
>>> + const unsigned char *p;
>>> + const unsigned char *startaddr = addr;
>>> + const unsigned char *endaddr = startaddr + len;
>>> +
>>> + if (len > (int) sizeof (ULONGEST))
>>> + error (_("\
>>> +That operation is not available on integers of more than %d bytes."),
>>> + (int) sizeof (ULONGEST));
>>> +
>>> + /* Start at the most significant end of the integer, and work towards
>>> + the least significant. */
>>> + retval = 0;
>>> + if (byte_order == BFD_ENDIAN_BIG)
>>> + {
>>> + for (p = startaddr; p < endaddr; ++p)
>>> + retval = (retval << 8) | *p;
>>> + }
>>> + else
>>> + {
>>> + for (p = endaddr - 1; p >= startaddr; --p)
>>> + retval = (retval << 8) | *p;
>>> + }
>>> + return retval;
>>> +}
>>> +
>>> +/* Sometimes a long long unsigned integer can be extracted as a
>>> + LONGEST value. This is done so that we can print these values
>>> + better. If this integer can be converted to a LONGEST, this
>>> + function returns 1 and sets *PVAL. Otherwise it returns 0. */
>>> +
>>> +int
>>> +extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
>>> + enum bfd_endian byte_order, LONGEST *pval)
>>> +{
>>> + const gdb_byte *p;
>>> + const gdb_byte *first_addr;
>>> + int len;
>>> +
>>> + len = orig_len;
>>> + if (byte_order == BFD_ENDIAN_BIG)
>>> + {
>>> + for (p = addr;
>>> + len > (int) sizeof (LONGEST) && p < addr + orig_len;
>>> + p++)
>>> + {
>>> + if (*p == 0)
>>> + len--;
>>> + else
>>> + break;
>>> + }
>>> + first_addr = p;
>>> + }
>>> + else
>>> + {
>>> + first_addr = addr;
>>> + for (p = addr + orig_len - 1;
>>> + len > (int) sizeof (LONGEST) && p >= addr;
>>> + p--)
>>> + {
>>> + if (*p == 0)
>>> + len--;
>>> + else
>>> + break;
>>> + }
>>> + }
>>> +
>>> + if (len <= (int) sizeof (LONGEST))
>>> + {
>>> + *pval = (LONGEST) extract_unsigned_integer (first_addr,
>>> + sizeof (LONGEST),
>>> + byte_order);
>>> + return 1;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/* All 'store' functions accept a host-format integer and store a
>>> + target-format integer at ADDR which is LEN bytes long. */
>>> +
>>> +void
>>> +store_signed_integer (gdb_byte *addr, int len,
>>> + enum bfd_endian byte_order, LONGEST val)
>>> +{
>>> + gdb_byte *p;
>>> + gdb_byte *startaddr = addr;
>>> + gdb_byte *endaddr = startaddr + len;
>>> +
>>> + /* Start at the least significant end of the integer, and work
>>> towards
>>> + the most significant. */
>>> + if (byte_order == BFD_ENDIAN_BIG)
>>> + {
>>> + for (p = endaddr - 1; p >= startaddr; --p)
>>> + {
>>> + *p = val & 0xff;
>>> + val >>= 8;
>>> + }
>>> + }
>>> + else
>>> + {
>>> + for (p = startaddr; p < endaddr; ++p)
>>> + {
>>> + *p = val & 0xff;
>>> + val >>= 8;
>>> + }
>>> + }
>>> +}
>>> +
>>> +void
>>> +store_unsigned_integer (gdb_byte *addr, int len,
>>> + enum bfd_endian byte_order, ULONGEST val)
>>> +{
>>> + unsigned char *p;
>>> + unsigned char *startaddr = (unsigned char *) addr;
>>> + unsigned char *endaddr = startaddr + len;
>>> +
>>> + /* Start at the least significant end of the integer, and work
>>> towards
>>> + the most significant. */
>>> + if (byte_order == BFD_ENDIAN_BIG)
>>> + {
>>> + for (p = endaddr - 1; p >= startaddr; --p)
>>> + {
>>> + *p = val & 0xff;
>>> + val >>= 8;
>>> + }
>>> + }
>>> + else
>>> + {
>>> + for (p = startaddr; p < endaddr; ++p)
>>> + {
>>> + *p = val & 0xff;
>>> + val >>= 8;
>>> + }
>>> + }
>>> +}
>>> diff --git a/gdb/common/int-utils.h b/gdb/common/int-utils.h
>>> new file mode 100644
>>> index 0000000..c170348
>>> --- /dev/null
>>> +++ b/gdb/common/int-utils.h
>>> @@ -0,0 +1,45 @@
>>> +/* Shared utility routines for integer endianness manipulations.
>>> + Copyright (C) 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 INT_UTILS_H
>>> +#define INT_UTILS_H 1
>>> +
>>> +#ifdef GDBSERVER
>>> +/* Allow this enum without requiring bfd in gdbserver. */
>>> +enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE,
>>> BFD_ENDIAN_UNKNOWN };
>>> +#else
>>> +#include "bfd.h"
>>> +#endif
>>> +
>>> +extern LONGEST extract_signed_integer (const gdb_byte *, int,
>>> + enum bfd_endian);
>>> +
>>> +extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
>>> + enum bfd_endian);
>>> +
>>> +extern int extract_long_unsigned_integer (const gdb_byte *, int,
>>> + enum bfd_endian, LONGEST *);
>>> +
>>> +extern void store_signed_integer (gdb_byte *, int,
>>> + enum bfd_endian, LONGEST);
>>> +
>>> +extern void store_unsigned_integer (gdb_byte *, int,
>>> + enum bfd_endian, ULONGEST);
>>> +
>>> +#endif /* INT_UTILS_H */
>>> diff --git a/gdb/defs.h b/gdb/defs.h
>>> index 03f7e8a..e292977 100644
>>> --- a/gdb/defs.h
>>> +++ b/gdb/defs.h
>>> @@ -596,28 +596,12 @@ enum { MAX_REGISTER_SIZE = 64 };
>>>
>>> /* In findvar.c. */
>>>
>>> -extern LONGEST extract_signed_integer (const gdb_byte *, int,
>>> - enum bfd_endian);
>>> -
>>> -extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
>>> - enum bfd_endian);
>>> -
>>> -extern int extract_long_unsigned_integer (const gdb_byte *, int,
>>> - enum bfd_endian, LONGEST *);
>>> -
>>> extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
>>> struct type *type);
>>>
>>> -extern void store_signed_integer (gdb_byte *, int,
>>> - enum bfd_endian, LONGEST);
>>> -
>>> -extern void store_unsigned_integer (gdb_byte *, int,
>>> - enum bfd_endian, ULONGEST);
>>> -
>>> extern void store_typed_address (gdb_byte *buf, struct type *type,
>>> CORE_ADDR addr);
>>>
>>> -\f
>>> /* From valops.c */
>>>
>>> extern int watchdog;
>>> diff --git a/gdb/findvar.c b/gdb/findvar.c
>>> index 1c077f7..2299ca4 100644
>>> --- a/gdb/findvar.c
>>> +++ b/gdb/findvar.c
>>> @@ -46,124 +46,6 @@
>>> you lose
>>> #endif
>>>
>>> -LONGEST
>>> -extract_signed_integer (const gdb_byte *addr, int len,
>>> - enum bfd_endian byte_order)
>>> -{
>>> - LONGEST retval;
>>> - const unsigned char *p;
>>> - const unsigned char *startaddr = addr;
>>> - const unsigned char *endaddr = startaddr + len;
>>> -
>>> - if (len > (int) sizeof (LONGEST))
>>> - error (_("\
>>> -That operation is not available on integers of more than %d bytes."),
>>> - (int) sizeof (LONGEST));
>>> -
>>> - /* Start at the most significant end of the integer, and work towards
>>> - the least significant. */
>>> - if (byte_order == BFD_ENDIAN_BIG)
>>> - {
>>> - p = startaddr;
>>> - /* Do the sign extension once at the start. */
>>> - retval = ((LONGEST) * p ^ 0x80) - 0x80;
>>> - for (++p; p < endaddr; ++p)
>>> - retval = (retval << 8) | *p;
>>> - }
>>> - else
>>> - {
>>> - p = endaddr - 1;
>>> - /* Do the sign extension once at the start. */
>>> - retval = ((LONGEST) * p ^ 0x80) - 0x80;
>>> - for (--p; p >= startaddr; --p)
>>> - retval = (retval << 8) | *p;
>>> - }
>>> - return retval;
>>> -}
>>> -
>>> -ULONGEST
>>> -extract_unsigned_integer (const gdb_byte *addr, int len,
>>> - enum bfd_endian byte_order)
>>> -{
>>> - ULONGEST retval;
>>> - const unsigned char *p;
>>> - const unsigned char *startaddr = addr;
>>> - const unsigned char *endaddr = startaddr + len;
>>> -
>>> - if (len > (int) sizeof (ULONGEST))
>>> - error (_("\
>>> -That operation is not available on integers of more than %d bytes."),
>>> - (int) sizeof (ULONGEST));
>>> -
>>> - /* Start at the most significant end of the integer, and work towards
>>> - the least significant. */
>>> - retval = 0;
>>> - if (byte_order == BFD_ENDIAN_BIG)
>>> - {
>>> - for (p = startaddr; p < endaddr; ++p)
>>> - retval = (retval << 8) | *p;
>>> - }
>>> - else
>>> - {
>>> - for (p = endaddr - 1; p >= startaddr; --p)
>>> - retval = (retval << 8) | *p;
>>> - }
>>> - return retval;
>>> -}
>>> -
>>> -/* Sometimes a long long unsigned integer can be extracted as a
>>> - LONGEST value. This is done so that we can print these values
>>> - better. If this integer can be converted to a LONGEST, this
>>> - function returns 1 and sets *PVAL. Otherwise it returns 0. */
>>> -
>>> -int
>>> -extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
>>> - enum bfd_endian byte_order, LONGEST *pval)
>>> -{
>>> - const gdb_byte *p;
>>> - const gdb_byte *first_addr;
>>> - int len;
>>> -
>>> - len = orig_len;
>>> - if (byte_order == BFD_ENDIAN_BIG)
>>> - {
>>> - for (p = addr;
>>> - len > (int) sizeof (LONGEST) && p < addr + orig_len;
>>> - p++)
>>> - {
>>> - if (*p == 0)
>>> - len--;
>>> - else
>>> - break;
>>> - }
>>> - first_addr = p;
>>> - }
>>> - else
>>> - {
>>> - first_addr = addr;
>>> - for (p = addr + orig_len - 1;
>>> - len > (int) sizeof (LONGEST) && p >= addr;
>>> - p--)
>>> - {
>>> - if (*p == 0)
>>> - len--;
>>> - else
>>> - break;
>>> - }
>>> - }
>>> -
>>> - if (len <= (int) sizeof (LONGEST))
>>> - {
>>> - *pval = (LONGEST) extract_unsigned_integer (first_addr,
>>> - sizeof (LONGEST),
>>> - byte_order);
>>> - return 1;
>>> - }
>>> -
>>> - return 0;
>>> -}
>>> -
>>> -
>>> /* Treat the bytes at BUF as a pointer of type TYPE, and return the
>>> address it represents. */
>>> CORE_ADDR
>>> @@ -178,64 +60,6 @@ extract_typed_address (const gdb_byte *buf,
>>> struct type *type)
>>> return gdbarch_pointer_to_address (get_type_arch (type), type, buf);
>>> }
>>>
>>> -/* All 'store' functions accept a host-format integer and store a
>>> - target-format integer at ADDR which is LEN bytes long. */
>>> -
>>> -void
>>> -store_signed_integer (gdb_byte *addr, int len,
>>> - enum bfd_endian byte_order, LONGEST val)
>>> -{
>>> - gdb_byte *p;
>>> - gdb_byte *startaddr = addr;
>>> - gdb_byte *endaddr = startaddr + len;
>>> -
>>> - /* Start at the least significant end of the integer, and work
>>> towards
>>> - the most significant. */
>>> - if (byte_order == BFD_ENDIAN_BIG)
>>> - {
>>> - for (p = endaddr - 1; p >= startaddr; --p)
>>> - {
>>> - *p = val & 0xff;
>>> - val >>= 8;
>>> - }
>>> - }
>>> - else
>>> - {
>>> - for (p = startaddr; p < endaddr; ++p)
>>> - {
>>> - *p = val & 0xff;
>>> - val >>= 8;
>>> - }
>>> - }
>>> -}
>>> -
>>> -void
>>> -store_unsigned_integer (gdb_byte *addr, int len,
>>> - enum bfd_endian byte_order, ULONGEST val)
>>> -{
>>> - unsigned char *p;
>>> - unsigned char *startaddr = (unsigned char *) addr;
>>> - unsigned char *endaddr = startaddr + len;
>>> -
>>> - /* Start at the least significant end of the integer, and work
>>> towards
>>> - the most significant. */
>>> - if (byte_order == BFD_ENDIAN_BIG)
>>> - {
>>> - for (p = endaddr - 1; p >= startaddr; --p)
>>> - {
>>> - *p = val & 0xff;
>>> - val >>= 8;
>>> - }
>>> - }
>>> - else
>>> - {
>>> - for (p = startaddr; p < endaddr; ++p)
>>> - {
>>> - *p = val & 0xff;
>>> - val >>= 8;
>>> - }
>>> - }
>>> -}
>>>
>>> /* Store the address ADDR as a pointer of type TYPE at BUF, in target
>>> form. */
>>> --
>>> 1.9.1
>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-11 17:32 ` Antoine Tremblay
@ 2015-09-14 9:24 ` Gary Benson
2015-09-14 15:20 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Gary Benson @ 2015-09-14 9:24 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay wrote:
> On 09/11/2015 01:16 PM, Antoine Tremblay wrote:
> > On 09/11/2015 10:24 AM, Gary Benson wrote:
> > > Please don't introduce "#ifdef GDBSERVER" conditionals into
> > > common code, I spent some time removing them. I know I didn't
> > > get them all, but the remaining two are on my hit list.
> >
> > Humm what is the issue that makes this a bad idea if I may ?
The way the common code is built is currently kind of weird and ugly.
Even though the code is shared, there's still places you have to do
the same work twice, for example if you add a new .c or .h file to
common, for example, you have to add it to both GDB's and gdbserver's
Makefiles. If you add stuff that needs configure checks, you have to
add those twice too. Also we build gnulib twice. Also, the way the
compiler is invoked means that you can accidentally #include GDB- or
gdbserver-specific headers in common code. It's all very error-prone.
For the future we'd like to move the common code into its own toplevel
directory with it's own Makefile, it's own ./configure, etc. It would
be built once, to a libgdbcommon.a or something that GDB and gdbserver
would statically link. We can't do that if gdb/{common,nat,target}
have conditional code.
Thanks,
Gary
--
http://gbenson.net/
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (6 preceding siblings ...)
2015-09-11 12:14 ` [PATCH 4/7] Make breakpoint and breakpoint_len local variables " Antoine Tremblay
@ 2015-09-14 10:33 ` Yao Qi
2015-09-14 13:23 ` Antoine Tremblay
7 siblings, 1 reply; 32+ messages in thread
From: Yao Qi @ 2015-09-14 10:33 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
Hi Antoine,
Thanks for the work!
> This patch series adds support for tracepoints and software breakpoints on ARM
> aarch32 on linux.
Term "aarch32" is only used on armv8, means 32-bit execution state.
What your patches do is to add software single step and tracepoint point
for ARM (ARMv7 and pre-ARMv7). Don't need to say "aarch32".
Your patch series include too much things, and we need to split them,
>
> Some background :
>
> Some specific issues for ARM aarch32 make it difficult for GDBServer to enable
> these features namely : it has no hardware single step support and the
> breakpoint data and size depends on the current instruction set used, thumb,
> thumb2 or arm.
>
> So GDBServer needs to know how to set a breakpoint of different size based on
> the current instruction and registers and it needs to be able to do software
> single stepping.
The first part can be teaching GDBserver to choose the right breakpiont
instruction (of different size and encoding) based on the address.
Then, the second part can be teaching GDBserver to do software single stepping.
>
> This patchset teaches this to GDBServer and enables support for tracepoints and
> software breakpoints.
Support tracepoint on ARM (with software single step in GDBserver side)
can be the third part.
>
> The patchset :
>
> This patchset applies over :
> https://sourceware.org/ml/gdb-patches/2015-09/msg00004.html
> to avoid an immediate conflict...
I'll push it in, if there are no regressions on x86 and aarch64.
>
> This patchset was tested on Ubuntu 14.04 on ARMv7 and x86 no regressions found.
How do you test them on ARMv7? I assume you tested your patches against
gdbserver, but did you test them against compile options -marm and
-mthumb respectively? I do it in this way,
$ make check RUNTESTFLAGS='--target_board=YOUR_BOARD\{-marm,-mthumb\}'
YOUR_BOARD can be unix, native-gdbserver or whatever you are using.
Sometime, I also pass -march=armv4t to exercise thumb-1 instructions,
but I don't know whether your gcc has such multi-lib or not.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-11 12:14 ` [PATCH 5/7] Add support for software single step on ARM aarch32-linux " Antoine Tremblay
@ 2015-09-14 11:00 ` Yao Qi
2015-09-14 12:41 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Yao Qi @ 2015-09-14 11:00 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> - GDBServer needs to know the correct endianness to write that breakpoint
> properly.
Why does GDBserver need to care about endianess? Isn't GDBserver of the
same endianess as the inferior?
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-14 11:00 ` Yao Qi
@ 2015-09-14 12:41 ` Antoine Tremblay
2015-09-14 16:10 ` Yao Qi
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-14 12:41 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 09/14/2015 07:00 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> - GDBServer needs to know the correct endianness to write that breakpoint
>> properly.
>
> Why does GDBserver need to care about endianess? Isn't GDBserver of the
> same endianess as the inferior?
>
Usually it will be , however see commit :
dcd4a3a4e7fc3912194d1346d2dfc6252f70b456
Which I will partially quote here :
"tdep->arm_breakpoint, tdep->thumb_breakpoint, tdep->thumb2_breakpoint
should be set le_ variants in case of arm BE8 code. Those instruciton
sequences are writen to target with simple write_memory, without
regarding gdbarch_byte_order_for_code. But in BE8 case even data
memory is in big endian form, instructions are still in little endian
form."
So in BE8 code the instructions are not of the same endianness as the
data memory...
Also even if unlikely you could have a BE program being debugged in a LE
GDBServer assuming the proper BE libs are also present on the system.
Thus, GDB makes no assumptions about the endianness being the same as
it's own and I don't think GDBServer should either.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
2015-09-14 10:33 ` [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux " Yao Qi
@ 2015-09-14 13:23 ` Antoine Tremblay
2015-09-15 14:02 ` Yao Qi
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-14 13:23 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 09/14/2015 06:33 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
> Hi Antoine,
> Thanks for the work!
>
>> This patch series adds support for tracepoints and software breakpoints on ARM
>> aarch32 on linux.
>
> Term "aarch32" is only used on armv8, means 32-bit execution state.
> What your patches do is to add software single step and tracepoint point
> for ARM (ARMv7 and pre-ARMv7). Don't need to say "aarch32".
>
Yes I was a bit confused about that I thought aarch32 meant all old
32-bit instruction sets where it seems to refer the the 32 bits
instructions in armv8 only.
I could rephrase to ARM 32 bits or up to ARMv7 ? Or just ARM ? I don't
want to bring confusion with aarch64 since it supports hardware single step.
> Your patch series include too much things, and we need to split them,
>
>>
>> Some background :
>>
>> Some specific issues for ARM aarch32 make it difficult for GDBServer to enable
>> these features namely : it has no hardware single step support and the
>> breakpoint data and size depends on the current instruction set used, thumb,
>> thumb2 or arm.
>>
>> So GDBServer needs to know how to set a breakpoint of different size based on
>> the current instruction and registers and it needs to be able to do software
>> single stepping.
>
> The first part can be teaching GDBserver to choose the right breakpiont
> instruction (of different size and encoding) based on the address.
> Then, the second part can be teaching GDBserver to do software single stepping.
>
Yes I could separate the two, my main reason to go with them both was
that I could not test the breakpoints if I did not have software single
step.
I'll separate them.
>>
>> This patchset teaches this to GDBServer and enables support for tracepoints and
>> software breakpoints.
>
> Support tracepoint on ARM (with software single step in GDBserver side)
> can be the third part.
This is already a separate patch, namely 7/7.
Did you meant to create a patchset for it ?
I guess I could separate the patch in maybe 2 or 3 but isn't it better
to keep it inside the patchset otherwise we will have to sync 3
patchsets versions since they depend on each other ?
>>
>> The patchset :
>>
>> This patchset applies over :
>> https://sourceware.org/ml/gdb-patches/2015-09/msg00004.html
>> to avoid an immediate conflict...
>
> I'll push it in, if there are no regressions on x86 and aarch64.
>
OK thanks :)
>>
>> This patchset was tested on Ubuntu 14.04 on ARMv7 and x86 no regressions found.
>
> How do you test them on ARMv7? I assume you tested your patches against
> gdbserver, but did you test them against compile options -marm and
> -mthumb respectively? I do it in this way,
>
> $ make check RUNTESTFLAGS='--target_board=YOUR_BOARD\{-marm,-mthumb\}'
Yes indeed with native-gdbserver and native-extended-gdbserver, But I
had only checked manually with -march and -mthumb with specific tests.
I'll add this to my buildbot in general and make it more clear what test
configs were used.
>
> YOUR_BOARD can be unix, native-gdbserver or whatever you are using.
> Sometime, I also pass -march=armv4t to exercise thumb-1 instructions,
> but I don't know whether your gcc has such multi-lib or not.
>
It does support it at least according to the man so I'll add this one too.
Thanks !
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-14 9:24 ` Gary Benson
@ 2015-09-14 15:20 ` Antoine Tremblay
2015-09-14 15:28 ` [PATCH 2/7 v2] " Antoine Tremblay
2015-09-21 9:10 ` [PATCH 2/7] " Gary Benson
0 siblings, 2 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-14 15:20 UTC (permalink / raw)
To: Gary Benson; +Cc: gdb-patches
On 09/14/2015 05:24 AM, Gary Benson wrote:
> Antoine Tremblay wrote:
>> On 09/11/2015 01:16 PM, Antoine Tremblay wrote:
>>> On 09/11/2015 10:24 AM, Gary Benson wrote:
>>>> Please don't introduce "#ifdef GDBSERVER" conditionals into
>>>> common code, I spent some time removing them. I know I didn't
>>>> get them all, but the remaining two are on my hit list.
>>>
>>> Humm what is the issue that makes this a bad idea if I may ?
>
> The way the common code is built is currently kind of weird and ugly.
> Even though the code is shared, there's still places you have to do
> the same work twice, for example if you add a new .c or .h file to
> common, for example, you have to add it to both GDB's and gdbserver's
> Makefiles. If you add stuff that needs configure checks, you have to
> add those twice too. Also we build gnulib twice. Also, the way the
> compiler is invoked means that you can accidentally #include GDB- or
> gdbserver-specific headers in common code. It's all very error-prone.
>
> For the future we'd like to move the common code into its own toplevel
> directory with it's own Makefile, it's own ./configure, etc. It would
> be built once, to a libgdbcommon.a or something that GDB and gdbserver
> would statically link. We can't do that if gdb/{common,nat,target}
> have conditional code.
>
Ok, thanks for clarifying this for me.
So if there were a libgdbcommon I would need to include bfd into it and
make it a requirement of that lib so that both GDB and GDBServer can use it.
So I've made bfd.h a requirement of GDBServer, and when there will be a
libgdbcommon we can have the whole lib as a requirement there.
See patch v2 in next mail...
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 2/7 v2] Move some integer operations to common.
2015-09-14 15:20 ` Antoine Tremblay
@ 2015-09-14 15:28 ` Antoine Tremblay
2015-09-21 9:10 ` [PATCH 2/7] " Gary Benson
1 sibling, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-14 15:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
In this v2 :
- Removed #ifndef GDBSERVER to include bfd.h or not
- Add bfd.h include directory to gdbserver makefile and always include bfd.h
---
This patch is in preparation for sharing code between GDB and GDBServer to
enable software single stepping on ARM aarch32-linux.
It moves multiple functions related to extracting or storing a value based on
its endianness from findvar.c in gdb to a new file called int-utils.c in
common.
Definitions of these functions are also moved to defs.h to common-defs.h.
gdb/ChangeLog:
* Makefile.in: Add int-utils.o.
* common/common-defs.h: New functions defs from defs.h.
* common/int-utils.c: New file.
* common/int-utils.h: New file.
* defs.h: Move functions defs to common-defs.h.
* findvar.c (extract_signed_integer): Move to int-utils.c.
(extract_unsigned_integer): Likewise.
(extract_long_unsigned_integer): Likewise.
(store_signed_integer): Likewise.
(store_unsigned_integer): Likewise.
gdb/gdbserver/ChangeLog:
* Makefile.in: Add bfd include directory to cflags.
---
gdb/Makefile.in | 9 ++-
gdb/common/common-defs.h | 1 +
gdb/common/int-utils.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++
gdb/common/int-utils.h | 40 ++++++++++
gdb/defs.h | 16 ----
gdb/findvar.c | 176 ----------------------------------------
gdb/gdbserver/Makefile.in | 6 +-
7 files changed, 252 insertions(+), 195 deletions(-)
create mode 100644 gdb/common/int-utils.c
create mode 100644 gdb/common/int-utils.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0d7cf97..e20c5a6 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -854,6 +854,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
infcall.c \
infcmd.c inflow.c infrun.c \
inline-frame.c \
+ common/int-utils.c \
interps.c \
jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c jv-varobj.c \
language.c linespec.c location.c minidebug.c \
@@ -985,7 +986,7 @@ i386-linux-nat.h common/common-defs.h common/errors.h common/common-types.h \
common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
common/common-exceptions.h target/target.h common/symbol.h \
common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
-common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
+common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h common/int-utils.h \
nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -1084,7 +1085,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
format.o registry.o btrace.o record-btrace.o waitstatus.o \
print-utils.o rsp-low.o errors.o common-debug.o debug.o \
- common-exceptions.o btrace-common.o fileio.o \
+ common-exceptions.o btrace-common.o fileio.o int-utils.o \
$(SUBDIR_GCC_COMPILE_OBS)
TSOBS = inflow.o
@@ -2265,6 +2266,10 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
fileio.o: ${srcdir}/common/fileio.c
$(COMPILE) $(srcdir)/common/fileio.c
$(POSTCOMPILE)
+int-utils.o: ${srcdir}/common/int-utils.c
+ $(COMPILE) $(srcdir)/common/int-utils.c
+ $(POSTCOMPILE)
+
#
# gdb/target/ dependencies
#
diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
index 2be0d7d..cb79234 100644
--- a/gdb/common/common-defs.h
+++ b/gdb/common/common-defs.h
@@ -49,6 +49,7 @@
#include "common-debug.h"
#include "cleanups.h"
#include "common-exceptions.h"
+#include "int-utils.h"
#ifdef __cplusplus
# define EXTERN_C extern "C"
diff --git a/gdb/common/int-utils.c b/gdb/common/int-utils.c
new file mode 100644
index 0000000..57d0dba
--- /dev/null
+++ b/gdb/common/int-utils.c
@@ -0,0 +1,199 @@
+/* Shared utility routines for integer endianness manipulations.
+ Copyright (C) 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"
+
+/* Basic byte-swapping routines. All 'extract' functions return a
+ host-format integer from a target-format integer at ADDR which is
+ LEN bytes long. */
+
+LONGEST
+extract_signed_integer (const gdb_byte *addr, int len,
+ enum bfd_endian byte_order)
+{
+ LONGEST retval;
+ const unsigned char *p;
+ const unsigned char *startaddr = addr;
+ const unsigned char *endaddr = startaddr + len;
+
+ if (len > (int) sizeof (LONGEST))
+ error (_("\
+That operation is not available on integers of more than %d bytes."),
+ (int) sizeof (LONGEST));
+
+ /* Start at the most significant end of the integer, and work towards
+ the least significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ p = startaddr;
+ /* Do the sign extension once at the start. */
+ retval = ((LONGEST) * p ^ 0x80) - 0x80;
+ for (++p; p < endaddr; ++p)
+ retval = (retval << 8) | *p;
+ }
+ else
+ {
+ p = endaddr - 1;
+ /* Do the sign extension once at the start. */
+ retval = ((LONGEST) * p ^ 0x80) - 0x80;
+ for (--p; p >= startaddr; --p)
+ retval = (retval << 8) | *p;
+ }
+ return retval;
+}
+
+ULONGEST
+extract_unsigned_integer (const gdb_byte *addr, int len,
+ enum bfd_endian byte_order)
+{
+ ULONGEST retval;
+ const unsigned char *p;
+ const unsigned char *startaddr = addr;
+ const unsigned char *endaddr = startaddr + len;
+
+ if (len > (int) sizeof (ULONGEST))
+ error (_("\
+That operation is not available on integers of more than %d bytes."),
+ (int) sizeof (ULONGEST));
+
+ /* Start at the most significant end of the integer, and work towards
+ the least significant. */
+ retval = 0;
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = startaddr; p < endaddr; ++p)
+ retval = (retval << 8) | *p;
+ }
+ else
+ {
+ for (p = endaddr - 1; p >= startaddr; --p)
+ retval = (retval << 8) | *p;
+ }
+ return retval;
+}
+
+/* Sometimes a long long unsigned integer can be extracted as a
+ LONGEST value. This is done so that we can print these values
+ better. If this integer can be converted to a LONGEST, this
+ function returns 1 and sets *PVAL. Otherwise it returns 0. */
+
+int
+extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
+ enum bfd_endian byte_order, LONGEST *pval)
+{
+ const gdb_byte *p;
+ const gdb_byte *first_addr;
+ int len;
+
+ len = orig_len;
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = addr;
+ len > (int) sizeof (LONGEST) && p < addr + orig_len;
+ p++)
+ {
+ if (*p == 0)
+ len--;
+ else
+ break;
+ }
+ first_addr = p;
+ }
+ else
+ {
+ first_addr = addr;
+ for (p = addr + orig_len - 1;
+ len > (int) sizeof (LONGEST) && p >= addr;
+ p--)
+ {
+ if (*p == 0)
+ len--;
+ else
+ break;
+ }
+ }
+
+ if (len <= (int) sizeof (LONGEST))
+ {
+ *pval = (LONGEST) extract_unsigned_integer (first_addr,
+ sizeof (LONGEST),
+ byte_order);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* All 'store' functions accept a host-format integer and store a
+ target-format integer at ADDR which is LEN bytes long. */
+
+void
+store_signed_integer (gdb_byte *addr, int len,
+ enum bfd_endian byte_order, LONGEST val)
+{
+ gdb_byte *p;
+ gdb_byte *startaddr = addr;
+ gdb_byte *endaddr = startaddr + len;
+
+ /* Start at the least significant end of the integer, and work towards
+ the most significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = endaddr - 1; p >= startaddr; --p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+ else
+ {
+ for (p = startaddr; p < endaddr; ++p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+}
+
+void
+store_unsigned_integer (gdb_byte *addr, int len,
+ enum bfd_endian byte_order, ULONGEST val)
+{
+ unsigned char *p;
+ unsigned char *startaddr = (unsigned char *) addr;
+ unsigned char *endaddr = startaddr + len;
+
+ /* Start at the least significant end of the integer, and work towards
+ the most significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = endaddr - 1; p >= startaddr; --p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+ else
+ {
+ for (p = startaddr; p < endaddr; ++p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+}
diff --git a/gdb/common/int-utils.h b/gdb/common/int-utils.h
new file mode 100644
index 0000000..fc82c0f
--- /dev/null
+++ b/gdb/common/int-utils.h
@@ -0,0 +1,40 @@
+/* Shared utility routines for integer endianness manipulations.
+ Copyright (C) 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 INT_UTILS_H
+#define INT_UTILS_H 1
+
+#include "bfd.h"
+
+extern LONGEST extract_signed_integer (const gdb_byte *, int,
+ enum bfd_endian);
+
+extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
+ enum bfd_endian);
+
+extern int extract_long_unsigned_integer (const gdb_byte *, int,
+ enum bfd_endian, LONGEST *);
+
+extern void store_signed_integer (gdb_byte *, int,
+ enum bfd_endian, LONGEST);
+
+extern void store_unsigned_integer (gdb_byte *, int,
+ enum bfd_endian, ULONGEST);
+
+#endif /* INT_UTILS_H */
diff --git a/gdb/defs.h b/gdb/defs.h
index 03f7e8a..e292977 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -596,28 +596,12 @@ enum { MAX_REGISTER_SIZE = 64 };
/* In findvar.c. */
-extern LONGEST extract_signed_integer (const gdb_byte *, int,
- enum bfd_endian);
-
-extern ULONGEST extract_unsigned_integer (const gdb_byte *, int,
- enum bfd_endian);
-
-extern int extract_long_unsigned_integer (const gdb_byte *, int,
- enum bfd_endian, LONGEST *);
-
extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
struct type *type);
-extern void store_signed_integer (gdb_byte *, int,
- enum bfd_endian, LONGEST);
-
-extern void store_unsigned_integer (gdb_byte *, int,
- enum bfd_endian, ULONGEST);
-
extern void store_typed_address (gdb_byte *buf, struct type *type,
CORE_ADDR addr);
-\f
/* From valops.c */
extern int watchdog;
diff --git a/gdb/findvar.c b/gdb/findvar.c
index 1c077f7..2299ca4 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -46,124 +46,6 @@
you lose
#endif
-LONGEST
-extract_signed_integer (const gdb_byte *addr, int len,
- enum bfd_endian byte_order)
-{
- LONGEST retval;
- const unsigned char *p;
- const unsigned char *startaddr = addr;
- const unsigned char *endaddr = startaddr + len;
-
- if (len > (int) sizeof (LONGEST))
- error (_("\
-That operation is not available on integers of more than %d bytes."),
- (int) sizeof (LONGEST));
-
- /* Start at the most significant end of the integer, and work towards
- the least significant. */
- if (byte_order == BFD_ENDIAN_BIG)
- {
- p = startaddr;
- /* Do the sign extension once at the start. */
- retval = ((LONGEST) * p ^ 0x80) - 0x80;
- for (++p; p < endaddr; ++p)
- retval = (retval << 8) | *p;
- }
- else
- {
- p = endaddr - 1;
- /* Do the sign extension once at the start. */
- retval = ((LONGEST) * p ^ 0x80) - 0x80;
- for (--p; p >= startaddr; --p)
- retval = (retval << 8) | *p;
- }
- return retval;
-}
-
-ULONGEST
-extract_unsigned_integer (const gdb_byte *addr, int len,
- enum bfd_endian byte_order)
-{
- ULONGEST retval;
- const unsigned char *p;
- const unsigned char *startaddr = addr;
- const unsigned char *endaddr = startaddr + len;
-
- if (len > (int) sizeof (ULONGEST))
- error (_("\
-That operation is not available on integers of more than %d bytes."),
- (int) sizeof (ULONGEST));
-
- /* Start at the most significant end of the integer, and work towards
- the least significant. */
- retval = 0;
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = startaddr; p < endaddr; ++p)
- retval = (retval << 8) | *p;
- }
- else
- {
- for (p = endaddr - 1; p >= startaddr; --p)
- retval = (retval << 8) | *p;
- }
- return retval;
-}
-
-/* Sometimes a long long unsigned integer can be extracted as a
- LONGEST value. This is done so that we can print these values
- better. If this integer can be converted to a LONGEST, this
- function returns 1 and sets *PVAL. Otherwise it returns 0. */
-
-int
-extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
- enum bfd_endian byte_order, LONGEST *pval)
-{
- const gdb_byte *p;
- const gdb_byte *first_addr;
- int len;
-
- len = orig_len;
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = addr;
- len > (int) sizeof (LONGEST) && p < addr + orig_len;
- p++)
- {
- if (*p == 0)
- len--;
- else
- break;
- }
- first_addr = p;
- }
- else
- {
- first_addr = addr;
- for (p = addr + orig_len - 1;
- len > (int) sizeof (LONGEST) && p >= addr;
- p--)
- {
- if (*p == 0)
- len--;
- else
- break;
- }
- }
-
- if (len <= (int) sizeof (LONGEST))
- {
- *pval = (LONGEST) extract_unsigned_integer (first_addr,
- sizeof (LONGEST),
- byte_order);
- return 1;
- }
-
- return 0;
-}
-
-
/* Treat the bytes at BUF as a pointer of type TYPE, and return the
address it represents. */
CORE_ADDR
@@ -178,64 +60,6 @@ extract_typed_address (const gdb_byte *buf, struct type *type)
return gdbarch_pointer_to_address (get_type_arch (type), type, buf);
}
-/* All 'store' functions accept a host-format integer and store a
- target-format integer at ADDR which is LEN bytes long. */
-
-void
-store_signed_integer (gdb_byte *addr, int len,
- enum bfd_endian byte_order, LONGEST val)
-{
- gdb_byte *p;
- gdb_byte *startaddr = addr;
- gdb_byte *endaddr = startaddr + len;
-
- /* Start at the least significant end of the integer, and work towards
- the most significant. */
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = endaddr - 1; p >= startaddr; --p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
- else
- {
- for (p = startaddr; p < endaddr; ++p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
-}
-
-void
-store_unsigned_integer (gdb_byte *addr, int len,
- enum bfd_endian byte_order, ULONGEST val)
-{
- unsigned char *p;
- unsigned char *startaddr = (unsigned char *) addr;
- unsigned char *endaddr = startaddr + len;
-
- /* Start at the least significant end of the integer, and work towards
- the most significant. */
- if (byte_order == BFD_ENDIAN_BIG)
- {
- for (p = endaddr - 1; p >= startaddr; --p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
- else
- {
- for (p = startaddr; p < endaddr; ++p)
- {
- *p = val & 0xff;
- val >>= 8;
- }
- }
-}
/* Store the address ADDR as a pointer of type TYPE at BUF, in target
form. */
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index b715a32..429ea9e 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -92,6 +92,10 @@ INCLUDE_DEP = $$(INCLUDE_DIR)
LIBIBERTY_BUILDDIR = build-libiberty-gdbserver
LIBIBERTY = $(LIBIBERTY_BUILDDIR)/libiberty.a
+# Where is the BFD library? Typically in ../../bfd.
+BFD_DIR = ../../bfd
+BFD_CFLAGS = -I$(BFD_DIR)
+
# Where is ust? These will be empty if ust was not available.
ustlibs = @ustlibs@
ustinc = @ustinc@
@@ -136,7 +140,7 @@ CPPFLAGS = @CPPFLAGS@
# INTERNAL_CFLAGS is the aggregate of all other *CFLAGS macros.
INTERNAL_CFLAGS_BASE = ${CFLAGS} ${GLOBAL_CFLAGS} \
- ${PROFILE_CFLAGS} ${INCLUDE_CFLAGS} ${CPPFLAGS}
+ ${PROFILE_CFLAGS} ${INCLUDE_CFLAGS} ${BFD_CFLAGS} ${CPPFLAGS}
INTERNAL_WARN_CFLAGS = ${INTERNAL_CFLAGS_BASE} $(WARN_CFLAGS)
INTERNAL_CFLAGS = ${INTERNAL_WARN_CFLAGS} $(WERROR_CFLAGS) -DGDBSERVER
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-14 12:41 ` Antoine Tremblay
@ 2015-09-14 16:10 ` Yao Qi
2015-09-14 17:28 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Yao Qi @ 2015-09-14 16:10 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: Yao Qi, gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> Usually it will be , however see commit :
> dcd4a3a4e7fc3912194d1346d2dfc6252f70b456
>
> Which I will partially quote here :
>
> "tdep->arm_breakpoint, tdep->thumb_breakpoint, tdep->thumb2_breakpoint
> should be set le_ variants in case of arm BE8 code. Those instruciton
> sequences are writen to target with simple write_memory, without
> regarding gdbarch_byte_order_for_code. But in BE8 case even data
> memory is in big endian form, instructions are still in little endian
> form."
>
> So in BE8 code the instructions are not of the same endianness as the
> data memory...
Do you want to support BE8 in GDBserver in your patches? If yes, please
split them out of your patch set. Current GDBserver doesn't consider
the difference of data endianness and instruction endianness, so you
don't have to worry about it too much, unless you really want to fix
problems on this.
>
> Also even if unlikely you could have a BE program being debugged in a
> LE GDBServer assuming the proper BE libs are also present on the
> system.
I don't think it is practical to do so...
>
> Thus, GDB makes no assumptions about the endianness being the same as
> it's own and I don't think GDBServer should either.
GDB makes no assumptions because GDB and inferior program may run on
different machine of different endianness. However, GDBserver and the
inferior program are running on the same machine, it can assume that.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-14 16:10 ` Yao Qi
@ 2015-09-14 17:28 ` Antoine Tremblay
2015-09-15 7:22 ` Yao Qi
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-14 17:28 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 09/14/2015 12:10 PM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> Usually it will be , however see commit :
>> dcd4a3a4e7fc3912194d1346d2dfc6252f70b456
>>
>> Which I will partially quote here :
>>
>> "tdep->arm_breakpoint, tdep->thumb_breakpoint, tdep->thumb2_breakpoint
>> should be set le_ variants in case of arm BE8 code. Those instruciton
>> sequences are writen to target with simple write_memory, without
>> regarding gdbarch_byte_order_for_code. But in BE8 case even data
>> memory is in big endian form, instructions are still in little endian
>> form."
>>
>> So in BE8 code the instructions are not of the same endianness as the
>> data memory...
>
> Do you want to support BE8 in GDBserver in your patches? If yes, please
> split them out of your patch set. Current GDBserver doesn't consider
> the difference of data endianness and instruction endianness, so you
> don't have to worry about it too much, unless you really want to fix
> problems on this.
>
Well my goal is to have feature parity between GDB and GDBServer as much
as possible, so yes I would like to support BE8 in GDBServer.
Since BE8 support entails endianness awareness I can't split them out
logically based on the BE8 feature, that would be writing the breakpoint
handling code code without endianness support and then rewriting it
completely with a patch labeled BE8 that would teach endianness to these
features. This would be a major overhead with no value imho.
Maybe I misunderstood what you meant ?
I think it's better to include endianness awareness in GDBServer from
patch1 and not redo the work. In that case BE8 support becomes only 1
variable in a function and thus does not need it's own patch.
>>
>> Also even if unlikely you could have a BE program being debugged in a
>> LE GDBServer assuming the proper BE libs are also present on the
>> system.
>
> I don't think it is practical to do so...
>
Me neither but I would say it's a matter of context and that if
supporting it is easy and needed by another feature anyway, why not ?
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-14 17:28 ` Antoine Tremblay
@ 2015-09-15 7:22 ` Yao Qi
2015-09-15 12:33 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Yao Qi @ 2015-09-15 7:22 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: Yao Qi, gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> Well my goal is to have feature parity between GDB and GDBServer as
> much as possible, so yes I would like to support BE8 in GDBServer.
This feature parity should be addressed separately, not by this patch
series.
>
> Since BE8 support entails endianness awareness I can't split them out
> logically based on the BE8 feature, that would be writing the
> breakpoint handling code code without endianness support and then
> rewriting it completely with a patch labeled BE8 that would teach
> endianness to these features. This would be a major overhead with no
> value imho.
No, the value is we can make progress and address one specific problem
in one time. Patches are easy to review and discuss in this way.
I don't think BE8 GDBserver is fully supported, and I don't know anyone
running GDBserver on BE8.
>
> Maybe I misunderstood what you meant ?
>
> I think it's better to include endianness awareness in GDBServer from
> patch1 and not redo the work. In that case BE8 support becomes only 1
> variable in a function and thus does not need it's own patch.
From reviewer's point of view, I'd like each patch series is
self-contained and independent. Your patch series is about supporting
software breakpoint, software single step and tracepoint in ARM linux
GDBserver. Why do we need to worry about the different endianness of
GDBserver and inferior in your patch series? Your code may be right,
and we may need them sooner or later, but we can't do all of them in one
go.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-15 7:22 ` Yao Qi
@ 2015-09-15 12:33 ` Antoine Tremblay
2015-09-15 16:49 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-15 12:33 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
> From reviewer's point of view, I'd like each patch series is
> self-contained and independent. Your patch series is about supporting
> software breakpoint, software single step and tracepoint in ARM linux
> GDBserver. Why do we need to worry about the different endianness of
> GDBserver and inferior in your patch series? Your code may be right,
> and we may need them sooner or later, but we can't do all of them in one
> go.
>
Alright, here's what I propose in this case, since we may need this
support at one point but we do not now.
I could keep the shared breakpoints definitions , shared operation
signatures and other context that carries the endianness.
(Note that most of this context/signatures just can't go away since it
GDB needs it and the code is shared. )
But I will remove the BE8 detection and ELF endianness detection from
GDBServer.
So this way while the architecture of GDBServer will be able to handle
it from the start the feature itself won't be until a separate patch.
Does that sound ok ?
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
2015-09-14 13:23 ` Antoine Tremblay
@ 2015-09-15 14:02 ` Yao Qi
2015-09-15 14:08 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Yao Qi @ 2015-09-15 14:02 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: Yao Qi, gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> Yes I could separate the two, my main reason to go with them both was
> that I could not test the breakpoints if I did not have software
> single step.
>
> I'll separate them.
Thanks.
>
>>>
>>> This patchset teaches this to GDBServer and enables support for
>>> tracepoints and
>>> software breakpoints.
>>
>> Support tracepoint on ARM (with software single step in GDBserver side)
>> can be the third part.
>
> This is already a separate patch, namely 7/7.
>
Patch 7/7 does more than supporting tracepoint. Tracepoint support
should be done in one patch, and this patch is only about tracepoint support.
> Did you meant to create a patchset for it ?
>
> I guess I could separate the patch in maybe 2 or 3 but isn't it better
> to keep it inside the patchset otherwise we will have to sync 3
> patchsets versions since they depend on each other ?
How about starting from software breakpoint on ARM-linux GDBserver? It
doesn't depends on tracepoints. After this patch set is reviewed and
committed, you can post tracepoint patch set.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer.
2015-09-15 14:02 ` Yao Qi
@ 2015-09-15 14:08 ` Antoine Tremblay
0 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-15 14:08 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 09/15/2015 10:01 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> Yes I could separate the two, my main reason to go with them both was
>> that I could not test the breakpoints if I did not have software
>> single step.
>>
>> I'll separate them.
>
> Thanks.
>
>>
>>>>
>>>> This patchset teaches this to GDBServer and enables support for
>>>> tracepoints and
>>>> software breakpoints.
>>>
>>> Support tracepoint on ARM (with software single step in GDBserver side)
>>> can be the third part.
>>
>> This is already a separate patch, namely 7/7.
>>
>
> Patch 7/7 does more than supporting tracepoint. Tracepoint support
> should be done in one patch, and this patch is only about tracepoint support.
>
Right I can separate the software breakpoint.
>> Did you meant to create a patchset for it ?
>>
>> I guess I could separate the patch in maybe 2 or 3 but isn't it better
>> to keep it inside the patchset otherwise we will have to sync 3
>> patchsets versions since they depend on each other ?
>
> How about starting from software breakpoint on ARM-linux GDBserver? It
> doesn't depends on tracepoints. After this patch set is reviewed and
> committed, you can post tracepoint patch set.
>
Alright that can be done.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-15 12:33 ` Antoine Tremblay
@ 2015-09-15 16:49 ` Antoine Tremblay
0 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-15 16:49 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 09/15/2015 08:33 AM, Antoine Tremblay wrote:
>
>> From reviewer's point of view, I'd like each patch series is
>> self-contained and independent. Your patch series is about supporting
>> software breakpoint, software single step and tracepoint in ARM linux
>> GDBserver. Why do we need to worry about the different endianness of
>> GDBserver and inferior in your patch series? Your code may be right,
>> and we may need them sooner or later, but we can't do all of them in one
>> go.
>>
>
> Alright, here's what I propose in this case, since we may need this
> support at one point but we do not now.
>
> I could keep the shared breakpoints definitions , shared operation
> signatures and other context that carries the endianness.
>
> (Note that most of this context/signatures just can't go away since it
> GDB needs it and the code is shared. )
>
> But I will remove the BE8 detection and ELF endianness detection from
> GDBServer.
>
> So this way while the architecture of GDBServer will be able to handle
> it from the start the feature itself won't be until a separate patch.
>
> Does that sound ok ?
Just FYI after more thought I've decided to remove the shared
breakpoints too so the only thing about endianness that will be there is
what is needed to keep the get_next_pcs operations working with GDB.
I'll post a "v2" with only software breakpoints & software single step soon.
It should address your comments properly.
Thanks for the review :)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-14 15:20 ` Antoine Tremblay
2015-09-14 15:28 ` [PATCH 2/7 v2] " Antoine Tremblay
@ 2015-09-21 9:10 ` Gary Benson
2015-09-21 9:16 ` Pedro Alves
1 sibling, 1 reply; 32+ messages in thread
From: Gary Benson @ 2015-09-21 9:10 UTC (permalink / raw)
To: Antoine Tremblay, Pedro Alves; +Cc: gdb-patches
Hi Antoine, Pedro,
Antoine Tremblay wrote:
> On 09/14/2015 05:24 AM, Gary Benson wrote:
> > Antoine Tremblay wrote:
> > > On 09/11/2015 01:16 PM, Antoine Tremblay wrote:
> > > > On 09/11/2015 10:24 AM, Gary Benson wrote:
> > > > > Please don't introduce "#ifdef GDBSERVER" conditionals into
> > > > > common code, I spent some time removing them. I know I
> > > > > didn't get them all, but the remaining two are on my hit
> > > > > list.
> > > >
> > > > Humm what is the issue that makes this a bad idea if I may ?
> >
> > The way the common code is built is currently kind of weird and
> > ugly. Even though the code is shared, there's still places you
> > have to do the same work twice, for example if you add a new .c or
> > .h file to common, for example, you have to add it to both GDB's
> > and gdbserver's Makefiles. If you add stuff that needs configure
> > checks, you have to add those twice too. Also we build gnulib
> > twice. Also, the way the compiler is invoked means that you can
> > accidentally #include GDB- or gdbserver-specific headers in common
> > code. It's all very error-prone.
> >
> > For the future we'd like to move the common code into its own
> > toplevel directory with it's own Makefile, it's own ./configure,
> > etc. It would be built once, to a libgdbcommon.a or something
> > that GDB and gdbserver would statically link. We can't do that
> > if gdb/{common,nat,target} have conditional code.
>
> Ok, thanks for clarifying this for me.
>
> So if there were a libgdbcommon I would need to include bfd into it
> and make it a requirement of that lib so that both GDB and GDBServer
> can use it.
>
> So I've made bfd.h a requirement of GDBServer, and when there will
> be a libgdbcommon we can have the whole lib as a requirement there.
>
> See patch v2 in next mail...
I don't think this will be acceptable. If I understand correctly,
gdbserver supports some platforms that GDB (and BFD) does not, and
this patch would prevent gdbserver being built on those platforms.
Even if I'm wrong here, I've previously found it useful to build
gdbserver alone, and I think this would break that too.
Pedro knows more about these kinds of setups, I've copied him in.
Thanks,
Gary
--
http://gbenson.net/
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-21 9:10 ` [PATCH 2/7] " Gary Benson
@ 2015-09-21 9:16 ` Pedro Alves
2015-09-21 17:49 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Pedro Alves @ 2015-09-21 9:16 UTC (permalink / raw)
To: Gary Benson, Antoine Tremblay; +Cc: gdb-patches
On 09/21/2015 10:10 AM, Gary Benson wrote:
> Hi Antoine, Pedro,
>
> Antoine Tremblay wrote:
>> So I've made bfd.h a requirement of GDBServer, and when there will
>> be a libgdbcommon we can have the whole lib as a requirement there.
>>
>> See patch v2 in next mail...
>
> I don't think this will be acceptable. If I understand correctly,
> gdbserver supports some platforms that GDB (and BFD) does not, and
> this patch would prevent gdbserver being built on those platforms.
> Even if I'm wrong here, I've previously found it useful to build
> gdbserver alone, and I think this would break that too.
>
> Pedro knows more about these kinds of setups, I've copied him in.
>
(Without looking at the patch in detail),
Gary's right. bfd.h is a generated file, generated at bfd build time.
Anton, try building only gdbserver in a clean directory,
separate from gdb, and it will fail with your patch.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-21 9:16 ` Pedro Alves
@ 2015-09-21 17:49 ` Antoine Tremblay
2015-09-22 16:06 ` Doug Evans
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-21 17:49 UTC (permalink / raw)
To: Pedro Alves, Gary Benson; +Cc: gdb-patches
On 09/21/2015 05:16 AM, Pedro Alves wrote:
> On 09/21/2015 10:10 AM, Gary Benson wrote:
>> Hi Antoine, Pedro,
>>
>> Antoine Tremblay wrote:
>
>>> So I've made bfd.h a requirement of GDBServer, and when there will
>>> be a libgdbcommon we can have the whole lib as a requirement there.
>>>
>>> See patch v2 in next mail...
>>
>> I don't think this will be acceptable. If I understand correctly,
>> gdbserver supports some platforms that GDB (and BFD) does not, and
>> this patch would prevent gdbserver being built on those platforms.
>> Even if I'm wrong here, I've previously found it useful to build
>> gdbserver alone, and I think this would break that too.
>>
>> Pedro knows more about these kinds of setups, I've copied him in.
>>
>
> (Without looking at the patch in detail),
>
> Gary's right. bfd.h is a generated file, generated at bfd build time.
> Anton, try building only gdbserver in a clean directory,
> separate from gdb, and it will fail with your patch.
>
Ok I was worried this would not work..
I've removed much of the endianness dependencies from my patchset but I
still have a dependency on the bfd endiannness enum to share code with
GDB's read_memory_unsigned_integer.
So I will do a wrapper around read_memory_unsigned_integer in GDB that
takes an int and transfers it to the real read_memory_unsigned_integer
as the proper enum (by implicit conversion). And use an int when
referring to the enum in shared code.
Unless there's an objection to this method ?
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-21 17:49 ` Antoine Tremblay
@ 2015-09-22 16:06 ` Doug Evans
2015-09-22 17:50 ` Antoine Tremblay
0 siblings, 1 reply; 32+ messages in thread
From: Doug Evans @ 2015-09-22 16:06 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: Pedro Alves, Gary Benson, gdb-patches
On Mon, Sep 21, 2015 at 10:49 AM, Antoine Tremblay
<antoine.tremblay@ericsson.com> wrote:
>
>
> On 09/21/2015 05:16 AM, Pedro Alves wrote:
>>
>> On 09/21/2015 10:10 AM, Gary Benson wrote:
>>>
>>> Hi Antoine, Pedro,
>>>
>>> Antoine Tremblay wrote:
>>
>>
>>>> So I've made bfd.h a requirement of GDBServer, and when there will
>>>> be a libgdbcommon we can have the whole lib as a requirement there.
>>>>
>>>> See patch v2 in next mail...
>>>
>>>
>>> I don't think this will be acceptable. If I understand correctly,
>>> gdbserver supports some platforms that GDB (and BFD) does not, and
>>> this patch would prevent gdbserver being built on those platforms.
>>> Even if I'm wrong here, I've previously found it useful to build
>>> gdbserver alone, and I think this would break that too.
>>>
>>> Pedro knows more about these kinds of setups, I've copied him in.
>>>
>>
>> (Without looking at the patch in detail),
>>
>> Gary's right. bfd.h is a generated file, generated at bfd build time.
>> Anton, try building only gdbserver in a clean directory,
>> separate from gdb, and it will fail with your patch.
>>
> Ok I was worried this would not work..
>
> I've removed much of the endianness dependencies from my patchset but I
> still have a dependency on the bfd endiannness enum to share code with GDB's
> read_memory_unsigned_integer.
>
> So I will do a wrapper around read_memory_unsigned_integer in GDB that takes
> an int and transfers it to the real read_memory_unsigned_integer as the
> proper enum (by implicit conversion). And use an int when referring to the
> enum in shared code.
>
> Unless there's an objection to this method ?
I'd rather not discard the enum.
The first question I have is: where do we want to be in the long term?
I totally support moving more and more application independent code
into application independent places.
It's really a shame that something as simple as this is getting in the way.
[Ideally, I'd also like to remove bfd dependencies wherever possible,
but that can be a bit problematic. So I'm setting aside this
possibility. E.g., using an enum without bfd in the name.]
The next question I have is: Is there anything in what we need that
needs to be in a generated header?
Can we ask the bfd folks to move things like bfd_endian to a
non-generated header?
[bfd.h can still include it]
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 2/7] Move some integer operations to common.
2015-09-22 16:06 ` Doug Evans
@ 2015-09-22 17:50 ` Antoine Tremblay
0 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-22 17:50 UTC (permalink / raw)
To: Doug Evans; +Cc: Pedro Alves, Gary Benson, gdb-patches
On 09/22/2015 12:05 PM, Doug Evans wrote:
> On Mon, Sep 21, 2015 at 10:49 AM, Antoine Tremblay
> <antoine.tremblay@ericsson.com> wrote:
>>
>>
>> On 09/21/2015 05:16 AM, Pedro Alves wrote:
>>>
>>> On 09/21/2015 10:10 AM, Gary Benson wrote:
>>>>
>>>> Hi Antoine, Pedro,
>>>>
>>>> Antoine Tremblay wrote:
>>>
>>>
>>>>> So I've made bfd.h a requirement of GDBServer, and when there will
>>>>> be a libgdbcommon we can have the whole lib as a requirement there.
>>>>>
>>>>> See patch v2 in next mail...
>>>>
>>>>
>>>> I don't think this will be acceptable. If I understand correctly,
>>>> gdbserver supports some platforms that GDB (and BFD) does not, and
>>>> this patch would prevent gdbserver being built on those platforms.
>>>> Even if I'm wrong here, I've previously found it useful to build
>>>> gdbserver alone, and I think this would break that too.
>>>>
>>>> Pedro knows more about these kinds of setups, I've copied him in.
>>>>
>>>
>>> (Without looking at the patch in detail),
>>>
>>> Gary's right. bfd.h is a generated file, generated at bfd build time.
>>> Anton, try building only gdbserver in a clean directory,
>>> separate from gdb, and it will fail with your patch.
>>>
>> Ok I was worried this would not work..
>>
>> I've removed much of the endianness dependencies from my patchset but I
>> still have a dependency on the bfd endiannness enum to share code with GDB's
>> read_memory_unsigned_integer.
>>
>> So I will do a wrapper around read_memory_unsigned_integer in GDB that takes
>> an int and transfers it to the real read_memory_unsigned_integer as the
>> proper enum (by implicit conversion). And use an int when referring to the
>> enum in shared code.
>>
>> Unless there's an objection to this method ?
>
> I'd rather not discard the enum.
>
Yes me too, that's why I had these previous solutions.
> The first question I have is: where do we want to be in the long term?
> I totally support moving more and more application independent code
> into application independent places.
> It's really a shame that something as simple as this is getting in the way.
>
> [Ideally, I'd also like to remove bfd dependencies wherever possible,
> but that can be a bit problematic. So I'm setting aside this
> possibility. E.g., using an enum without bfd in the name.]
>
To me this sounds like a case by case basis, you can't move to
application independent code without accepting to import some
dependencies from the applications or removing those dependencies.
So it seems not easy to me to set a future direction to this kind of
balance. But I'm still new to GDB, I'm sure others may have more ideas
on this.
> The next question I have is: Is there anything in what we need that
> needs to be in a generated header?
> Can we ask the bfd folks to move things like bfd_endian to a
> non-generated header?
> [bfd.h can still include it]
>
Quickly looking at how bfd.h is done it seems to be possible to move
some stuff to a static file however I wonder if the current problem
would prove enough justification for that work.
It would also introduce a bfd version dependency in common code to check
for this static header. And it could be quite an ugly #ifdef changing
ints to enum in case the header is present.
One thing to consider too is that this patchset has now changed a bit
and this enum is no longer used in GDBServer itself at all.
Since the enum is only used in GDB's context and that the common code is
only a carrier for this context, this is not a risk type wise.
It's also not a risk for C++ conversion, as this is a pure C enum that
won't be converted to C++ unless bfd is which I doubt may happen ?
It could also be possible to require a make headers in bfd to build
GDBServer and possibly make that more convenient in the Makefile ?
I'm not sure it's worth it as long as GDBServer doesn't use the enum
itself, however I'm curious if others would find that acceptable.
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2015-09-22 17:50 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
2015-09-11 12:13 ` [PATCH 1/7] Fix instruction skipping when using software single step " Antoine Tremblay
2015-09-11 12:13 ` [PATCH 2/7] Move some integer operations to common Antoine Tremblay
2015-09-11 14:24 ` Gary Benson
2015-09-11 17:16 ` Antoine Tremblay
2015-09-11 17:32 ` Antoine Tremblay
2015-09-14 9:24 ` Gary Benson
2015-09-14 15:20 ` Antoine Tremblay
2015-09-14 15:28 ` [PATCH 2/7 v2] " Antoine Tremblay
2015-09-21 9:10 ` [PATCH 2/7] " Gary Benson
2015-09-21 9:16 ` Pedro Alves
2015-09-21 17:49 ` Antoine Tremblay
2015-09-22 16:06 ` Doug Evans
2015-09-22 17:50 ` Antoine Tremblay
2015-09-11 12:14 ` [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
2015-09-11 12:30 ` Eli Zaretskii
2015-09-11 12:43 ` Antoine Tremblay
2015-09-11 12:14 ` [PATCH 3/7] Support multiple breakpoint types per target " Antoine Tremblay
2015-09-11 12:14 ` [PATCH 5/7] Add support for software single step on ARM aarch32-linux " Antoine Tremblay
2015-09-14 11:00 ` Yao Qi
2015-09-14 12:41 ` Antoine Tremblay
2015-09-14 16:10 ` Yao Qi
2015-09-14 17:28 ` Antoine Tremblay
2015-09-15 7:22 ` Yao Qi
2015-09-15 12:33 ` Antoine Tremblay
2015-09-15 16:49 ` Antoine Tremblay
2015-09-11 12:14 ` [PATCH 6/7] Support conditional breakpoints on targets that can software single step " Antoine Tremblay
2015-09-11 12:14 ` [PATCH 4/7] Make breakpoint and breakpoint_len local variables " Antoine Tremblay
2015-09-14 10:33 ` [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux " Yao Qi
2015-09-14 13:23 ` Antoine Tremblay
2015-09-15 14:02 ` Yao Qi
2015-09-15 14:08 ` Antoine Tremblay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).