Fwd: [RFA] new command to search memory

Doug Evans
2008-04-28T14:07:41Z

[-- Attachment #1: Type: text/plain, Size: 819 bytes --]

Ping.

---------- Forwarded message ----------
From: Doug Evans <dje@google.com>
Date: Wed, Apr 16, 2008 at 7:46 PM
Subject: Re: [RFA] new command to search memory
To: gdb-patches@sourceware.org

Here's the revised "find" patch. It includes all requested changes to date.

Could someone look at target.c:target_search_memory and make sure it's
DTRT regarding handling targetdebug and falling back on a default?
Given that we're not using inheritance here, the current code seems
like the right way to go, but ...

Ok to check in?

There's still an outstanding issue of supporting this in gdbmi. I
split find_command into two parts so adding this to gdbmi should be
more straightforward.
And there's also an outstanding issue of if/how to support substring searches.
Both of these issues can wait I gather.

[-- Attachment #2: gdb-080416-findcmd-6.patch.txt --]
[-- Type: text/plain, Size: 47716 bytes --]

2008-04-16 Doug Evans <dje@google.com>

New "find" command.
* NEWS: Document find command and qSearch:memory packet.
* Makefile.in (SFILES): Add findcmd.c.
(COMMON_OBJS): Add findcmd.o.
(findcmd.o): New rule.
* findcmd.c: New file.
* target.h (target_ops): New member to_search_memory.
(simple_search_memory): Declare.
(target_search_memory): Declare.
* target.c (simple_search_memory): New fn.
(target_search_memory): New fn.
* remote.c (PACKET_qSearch_memory): New packet kind.
(remote_search_memory): New fn.
(init_remote_ops): Init to_search_memory.
(init_extended_remote_ops): Ditto.
(_initialize_remote): Add qSearch:memory packet config command.

* gdbserver/Makefile.in (INCGNULIB): New variable.
(INCLUDE_CFLAGS): Add $(INCGNULIB).
(OBS): Add memmem.o.
* gdbserver/server.h (decode_search_memory_packet): Declare.
* gdbserver/remote-utils.c (decode_search_memory_packet): New fn.
* gdbserver/server.c (handle_search_memory_1): New fn.
(handle_search_memory): New fn.
(handle_query): Process qSearch:memory packets.

* doc/gdb.texinfo: Document "find" command, qSearch:memory packet.

* testsuite/gdb.base/find.exp: New file.
* testsuite/gdb.base/find.c: New file.

Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1000
diff -u -p -u -p -r1.1000 Makefile.in
--- Makefile.in 14 Apr 2008 19:47:29 -0000 1.1000
+++ Makefile.in 17 Apr 2008 02:35:23 -0000
@@ -608,9 +608,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
elfread.c environ.c eval.c event-loop.c event-top.c expprint.c \
- f-exp.y f-lang.c f-typeprint.c f-valprint.c findvar.c frame.c \
- frame-base.c \
- frame-unwind.c \
+ f-exp.y f-lang.c f-typeprint.c f-valprint.c findcmd.c findvar.c \
+ frame.c frame-base.c frame-unwind.c \
gdbarch.c arch-utils.c gdbtypes.c gnu-v2-abi.c gnu-v3-abi.c \
inf-loop.c \
infcall.c \
@@ -1052,6 +1051,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
event-loop.o event-top.o inf-loop.o completer.o \
gdbarch.o arch-utils.o gdbtypes.o osabi.o copying.o \
memattr.o mem-break.o target.o parse.o language.o buildsym.o \
+ findcmd.o \
std-regs.o \
signals.o \
gdb-events.o \
@@ -2124,6 +2124,8 @@ fbsd-nat.o: fbsd-nat.c $(defs_h) $(gdbco
f-exp.o: f-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \
$(parser_defs_h) $(language_h) $(f_lang_h) $(bfd_h) $(symfile_h) \
$(objfiles_h) $(block_h)
+findcmd.o: findcmd.c $(defs_h) $(gdb_string_h) $(gdbcmd_h) $(value_h) \
+ $(target_h)
findvar.o: findvar.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(frame_h) \
$(value_h) $(gdbcore_h) $(inferior_h) $(target_h) $(gdb_string_h) \
$(gdb_assert_h) $(floatformat_h) $(symfile_h) $(regcache_h) \
Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.264
diff -u -p -u -p -r1.264 NEWS
--- NEWS 4 Apr 2008 15:51:15 -0000 1.264
+++ NEWS 17 Apr 2008 02:35:23 -0000
@@ -3,8 +3,13 @@

*** Changes since GDB 6.8

+* New remote packets
+
+qSearch:memory:
+ Search memory for a sequence of bytes.
+
* Watchpoints can now be set on unreadable memory locations, e.g. addresses
-which will be allocated using malloc later in program execution.
+ which will be allocated using malloc later in program execution.

* The qXfer:libraries:read remote procotol packet now allows passing a
list of section offsets.
@@ -30,6 +35,10 @@ show multiple-symbols
The value of this variable can be changed to adjust the debugger behavior
when an expression or a breakpoint location contains an ambiguous symbol
name (an overloaded function name, for instance).
+
+find [/size-char] [/max-count] start-address, end-address|@search-space-size,
+ val1 [, val2, ...]
+ Search memory for a sequence of bytes.

*** Changes in GDB 6.8

Index: findcmd.c
===================================================================
RCS file: findcmd.c
diff -N findcmd.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ findcmd.c 17 Apr 2008 02:35:23 -0000
@@ -0,0 +1,334 @@
+/* The find command.
+
+ Copyright (C) 2008 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 "defs.h"
+#include <ctype.h>
+#include "gdb_string.h"
+#include "gdbcmd.h"
+#include "value.h"
+#include "target.h"
+
+/* Copied from bfd_put_bits. */
+
+static void
+put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)
+{
+ int i;
+ int bytes;
+
+ gdb_assert (bits % 8 == 0);
+
+ bytes = bits / 8;
+ for (i = 0; i < bytes; i++)
+ {
+ int index = big_p ? bytes - i - 1 : i;
+
+ buf[index] = data & 0xff;
+ data >>= 8;
+ }
+}
+
+/* Subroutine of find_command to simplify it.
+ Parse the arguments of the "find" command. */
+
+static void
+parse_find_args (char *args, ULONGEST *max_countp,
+ char **pattern_bufp, ULONGEST *pattern_lenp,
+ CORE_ADDR *start_addrp, ULONGEST *search_space_lenp)
+{
+ /* Default to using the specified type. */
+ char size = '\0';
+ ULONGEST max_count = ~(ULONGEST) 0;
+ /* Buffer to hold the search pattern. */
+ char *pattern_buf;
+ /* Current size of search pattern buffer.
+ We realloc space as needed. */
+#define INITIAL_PATTERN_BUF_SIZE 100
+ ULONGEST pattern_buf_size = INITIAL_PATTERN_BUF_SIZE;
+ /* Pointer to one past the last in-use part of pattern_buf. */
+ char *pattern_buf_end;
+ ULONGEST pattern_len;
+ CORE_ADDR start_addr;
+ ULONGEST search_space_len;
+ char *s = args;
+ bfd_boolean big_p = gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG;
+ struct cleanup *old_cleanups;
+ struct value *v;
+
+ if (args == NULL)
+ error (_("missing search parameters"));
+
+ pattern_buf = xmalloc (pattern_buf_size);
+ pattern_buf_end = pattern_buf;
+ old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
+
+ /* Get search granularity and/or max count if specified.
+ They may be specified in either order, together or separately. */
+
+ while (*s == '/')
+ {
+ ++s;
+
+ while (*s != '\0' && *s != '/' && !isspace (*s))
+ {
+ if (isdigit (*s))
+ {
+ max_count = atoi (s);
+ while (isdigit (*s))
+ ++s;
+ continue;
+ }
+
+ switch (*s)
+ {
+ case 'b':
+ case 'h':
+ case 'w':
+ case 'g':
+ size = *s++;
+ break;
+ default:
+ error (_("invalid size granularity"));
+ }
+ }
+
+ while (isspace (*s))
+ ++s;
+ }
+
+ /* Get the search range. */
+
+ v = parse_to_comma_and_eval (&s);
+ start_addr = value_as_address (v);
+
+ if (*s == ',')
+ ++s;
+ while (isspace (*s))
+ ++s;
+
+ if (*s == '+')
+ {
+ LONGEST len;
+ ++s;
+ v = parse_to_comma_and_eval (&s);
+ len = value_as_long (v);
+ if (len == 0)
+ {
+ printf_filtered (_("empty search range\n"));
+ return;
+ }
+ if (len < 0)
+ error (_("invalid length"));
+ /* Watch for overflows. */
+ if (len > CORE_ADDR_MAX
+ || (start_addr + len - 1) < start_addr)
+ error (_("search space too large"));
+ search_space_len = len;
+ }
+ else
+ {
+ CORE_ADDR end_addr;
+ v = parse_to_comma_and_eval (&s);
+ end_addr = value_as_address (v);
+ if (start_addr > end_addr)
+ error (_("invalid search space, end preceeds start"));
+ search_space_len = end_addr - start_addr + 1;
+ /* We don't support searching all of memory
+ (i.e. start=0, end = 0xff..ff).
+ Bail to avoid overflows later on. */
+ if (search_space_len == 0)
+ error (_("overflow in address range computation, choose smaller range"));
+ }
+
+ if (*s == ',')
+ ++s;
+
+ /* Fetch the search string. */
+
+ while (*s != '\0')
+ {
+ LONGEST x;
+ int val_bytes;
+
+ /* If we see a string, parse it ourselves rather than the normal
+ handling of downloading it to target memory. */
+
+ while (isspace (*s))
+ ++s;
+
+ v = parse_to_comma_and_eval (&s);
+ val_bytes = TYPE_LENGTH (value_type (v));
+
+ /* Keep it simple and assume size == 'g' when watching for when we
+ need to grow the pattern buf. */
+ if ((pattern_buf_end - pattern_buf + max (val_bytes, sizeof (int64_t)))
+ > pattern_buf_size)
+ {
+ size_t current_offset = pattern_buf_end - pattern_buf;
+ pattern_buf_size *= 2;
+ pattern_buf = xrealloc (pattern_buf, pattern_buf_size);
+ pattern_buf_end = pattern_buf + current_offset;
+ }
+
+ if (size != '\0')
+ {
+ x = value_as_long (v);
+ switch (size)
+ {
+ case 'b':
+ *pattern_buf_end++ = x;
+ break;
+ case 'h':
+ put_bits (x, pattern_buf_end, 16, big_p);
+ pattern_buf_end += sizeof (int16_t);
+ break;
+ case 'w':
+ put_bits (x, pattern_buf_end, 32, big_p);
+ pattern_buf_end += sizeof (int32_t);
+ break;
+ case 'g':
+ put_bits (x, pattern_buf_end, 64, big_p);
+ pattern_buf_end += sizeof (int64_t);
+ break;
+ }
+ }
+ else
+ {
+ memcpy (pattern_buf_end, value_contents_raw (v), val_bytes);
+ pattern_buf_end += val_bytes;
+ }
+
+ if (*s == ',')
+ ++s;
+ while (isspace (*s))
+ ++s;
+ }
+
+ if (pattern_buf_end == pattern_buf)
+ error (_("missing search pattern"));
+
+ pattern_len = pattern_buf_end - pattern_buf;
+
+ if (search_space_len < pattern_len)
+ error (_("search space too small to contain pattern"));
+
+ *max_countp = max_count;
+ *pattern_bufp = pattern_buf;
+ *pattern_lenp = pattern_len;
+ *start_addrp = start_addr;
+ *search_space_lenp = search_space_len;
+
+ /* We successfully parsed the arguments, leave the freeing of PATTERN_BUF
+ to the caller now. */
+ discard_cleanups (old_cleanups);
+}
+
+static void
+find_command (char *args, int from_tty)
+{
+ /* Command line parameters.
+ These are initialized to avoid uninitialized warnings from -Wall. */
+ ULONGEST max_count = 0;
+ char *pattern_buf = 0;
+ ULONGEST pattern_len = 0;
+ CORE_ADDR start_addr = 0;
+ ULONGEST search_space_len = 0;
+ /* End of command line parameters. */
+ unsigned int found_count;
+ CORE_ADDR last_found_addr;
+ struct cleanup *old_cleanups;
+
+ parse_find_args (args, &max_count, &pattern_buf, &pattern_len,
+ &start_addr, &search_space_len);
+
+ old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
+
+ /* Perform the search. */
+
+ found_count = 0;
+ last_found_addr = 0;
+
+ while (search_space_len >= pattern_len
+ && found_count < max_count)
+ {
+ /* Offset from start of this iteration to the next iteration. */
+ ULONGEST next_iter_incr;
+ CORE_ADDR found_addr;
+ int found = target_search_memory (start_addr, search_space_len,
+ pattern_buf, pattern_len, &found_addr);
+
+ if (found <= 0)
+ break;
+
+ print_address (found_addr, gdb_stdout);
+ printf_filtered ("\n");
+ ++found_count;
+ last_found_addr = found_addr;
+
+ /* Begin next iteration at one byte past this match. */
+ next_iter_incr = (found_addr - start_addr) + 1;
+
+ /* For robustness, we don't let search_space_len go -ve here. */
+ if (search_space_len >= next_iter_incr)
+ search_space_len -= next_iter_incr;
+ else
+ search_space_len = 0;
+ start_addr += next_iter_incr;
+ }
+
+ /* Record and print the results. */
+
+ set_internalvar (lookup_internalvar ("numfound"),
+ value_from_longest (builtin_type_int,
+ (LONGEST) found_count));
+ if (found_count > 0)
+ {
+ set_internalvar (lookup_internalvar ("_"),
+ value_from_pointer (builtin_type_void_data_ptr,
+ last_found_addr));
+ }
+
+ if (found_count == 0)
+ printf_filtered ("pattern not found\n");
+ else
+ printf_filtered ("%d pattern%s found\n", found_count,
+ found_count > 1 ? "s" : "");
+
+ do_cleanups (old_cleanups);
+}
+
+void
+_initialize_mem_search (void)
+{
+ add_cmd ("find", class_vars, find_command, _("\
+Search memory for a sequence of bytes.\n\
+Usage:\n\
+find [/size-char] [/max-count] start-address, end-address, expr1 [, expr2 ...]\n\
+find [/size-char] [/max-count] start-address, +length, expr1 [, expr2 ...]\n\
+size-char is one of b,h,w,g for 8,16,32,64 bit values respectively,\n\
+and if not specified the size is taken from the type of the expression\n\
+in the current language.\n\
+Note that this means for example that in the case of C-like languages\n\
+a search for an untyped 0x42 will search for \"(int) 0x42\"\n\
+which is typically four bytes.\n\
+\n\
+The address of the last match is stored as the value of \"$_\".\n\
+Convenience variable \"$numfound\" is set to the number of matches."),
+ &cmdlist);
+}
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.286
diff -u -p -u -p -r1.286 remote.c
--- remote.c 21 Mar 2008 17:09:35 -0000 1.286
+++ remote.c 17 Apr 2008 02:35:24 -0000
@@ -935,6 +935,7 @@ enum {
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
+ PACKET_qSearch_memory,
PACKET_vAttach,
PACKET_vRun,
PACKET_MAX
@@ -6176,6 +6177,93 @@ remote_xfer_partial (struct target_ops *
return strlen ((char *) readbuf);
}

+static int
+remote_search_memory (struct target_ops* ops,
+ CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp)
+{
+ struct remote_state *rs = get_remote_state ();
+ int max_size = get_memory_write_packet_size ();
+ struct packet_config *packet =
+ &remote_protocol_packets[PACKET_qSearch_memory];
+ /* number of packet bytes used to encode the pattern,
+ this could be more than PATTERN_LEN due to escape characters */
+ int escaped_pattern_len;
+ /* amount of pattern that was encodable in the packet */
+ int used_pattern_len;
+ int i;
+ int found;
+ ULONGEST found_addr;
+
+ /* Don't go to the target if we don't have to.
+ This is done before checking packet->support to avoid the possibility that
+ a success for this edge case means the facility works in general. */
+ if (pattern_len > search_space_len)
+ return 0;
+ if (pattern_len == 0)
+ {
+ *found_addrp = start_addr;
+ return 1;
+ }
+
+ /* If we already know the packet isn't supported, fall back to the simple
+ way of searching memory. */
+
+ if (packet->support == PACKET_DISABLE)
+ {
+ /* Target doesn't provided special support, fall back and use the
+ standard support (copy memory and do the search here). */
+ return simple_search_memory (ops, start_addr, search_space_len,
+ pattern, pattern_len, found_addrp);
+ }
+
+ /* Insert header. */
+ i = snprintf (rs->buf, max_size,
+ "qSearch:memory:%s;%s;",
+ paddr_nz (start_addr),
+ phex_nz (search_space_len, sizeof (search_space_len)));
+ max_size -= (i + 1);
+
+ /* Escape as much data as fits into rs->buf. */
+ escaped_pattern_len =
+ remote_escape_output (pattern, pattern_len, (rs->buf + i),
+ &used_pattern_len, max_size);
+
+ /* Bail if the pattern is too large. */
+ if (used_pattern_len != pattern_len)
+ error ("pattern is too large to transmit to remote target");
+
+ if (putpkt_binary (rs->buf, i + escaped_pattern_len) < 0
+ || getpkt_sane (&rs->buf, &rs->buf_size, 0) < 0
+ || packet_ok (rs->buf, packet) != PACKET_OK)
+ {
+ /* The request may not have worked because the command is not
+ supported. If so, fall back to the simple way. */
+ if (packet->support == PACKET_DISABLE)
+ {
+ return simple_search_memory (ops, start_addr, search_space_len,
+ pattern, pattern_len, found_addrp);
+ }
+ return -1;
+ }
+
+ if (rs->buf[0] == '0')
+ found = 0;
+ else if (rs->buf[0] == '1')
+ {
+ found = 1;
+ if (rs->buf[1] != ',')
+ error (_("unknown qSearch:memory reply: %s"), rs->buf);
+ unpack_varlen_hex (rs->buf + 2, &found_addr);
+ *found_addrp = found_addr;
+ }
+ else
+ error (_("unknown qSearch:memory reply: %s"), rs->buf);
+
+ return found;
+}
+
static void
remote_rcmd (char *command,
struct ui_file *outbuf)
@@ -7237,6 +7325,7 @@ Specify the serial device it is connecte
remote_ops.to_flash_erase = remote_flash_erase;
remote_ops.to_flash_done = remote_flash_done;
remote_ops.to_read_description = remote_read_description;
+ remote_ops.to_search_memory = remote_search_memory;
remote_ops.to_can_async_p = remote_return_zero;
remote_ops.to_is_async_p = remote_return_zero;
}
@@ -7384,6 +7473,7 @@ Specify the serial device it is connecte
remote_async_ops.to_flash_erase = remote_flash_erase;
remote_async_ops.to_flash_done = remote_flash_done;
remote_async_ops.to_read_description = remote_read_description;
+ remote_async_ops.to_search_memory = remote_search_memory;
}

/* Set up the async extended remote vector by making a copy of the standard
@@ -7650,6 +7740,9 @@ Show the maximum size of the address (in
add_packet_config_cmd (&remote_protocol_packets[PACKET_qSupported],
"qSupported", "supported-packets", 0);

+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qSearch_memory],
+ "qSearch:memory", "search-memory", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_open],
"vFile:open", "hostio-open", 0);

Index: target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.159
diff -u -p -u -p -r1.159 target.c
--- target.c 28 Mar 2008 16:37:08 -0000 1.159
+++ target.c 17 Apr 2008 02:35:24 -0000
@@ -469,6 +469,7 @@ update_current_target (void)
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_thread_local_address, t);
/* Do not inherit to_read_description. */
+ /* Do not inherit to_search_memory. */
INHERIT (to_magic, t);
/* Do not inherit to_memory_map. */
/* Do not inherit to_flash_erase. */
@@ -1743,6 +1744,157 @@ target_read_description (struct target_o
return NULL;
}

+/* The default implementation of to_search_memory.
+ This implements a basic search of memory, reading target memory and
+ performing the search here (as opposed to performing the search in on the
+ target side with, for example, gdbserver). */
+
+int
+simple_search_memory (struct target_ops *ops,
+ CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp)
+{
+ /* NOTE: also defined in find.c testcase. */
+#define SEARCH_CHUNK_SIZE 16000
+ const unsigned chunk_size = SEARCH_CHUNK_SIZE;
+ /* Buffer to hold memory contents for searching. */
+ gdb_byte *search_buf;
+ unsigned search_buf_size;
+ struct cleanup *old_cleanups;
+
+ search_buf_size = chunk_size + pattern_len - 1;
+
+ /* No point in trying to allocate a buffer larger than the search space. */
+ if (search_space_len < search_buf_size)
+ search_buf_size = search_space_len;
+
+ search_buf = malloc (search_buf_size);
+ if (search_buf == NULL)
+ error (_("unable to allocate memory to perform the search"));
+ old_cleanups = make_cleanup (free_current_contents, &search_buf);
+
+ /* Prime the search buffer. */
+
+ if (target_read (ops, TARGET_OBJECT_MEMORY, NULL,
+ search_buf, start_addr, search_buf_size) != search_buf_size)
+ {
+ warning (_("unable to access target memory at %s, halting search"),
+ hex_string (start_addr));
+ do_cleanups (old_cleanups);
+ return -1;
+ }
+
+ /* Perform the search.
+
+ The loop is kept simple by allocating [N + pattern-length - 1] bytes.
+ When we've scanned N bytes we copy the trailing bytes to the start and
+ read in another N bytes. */
+
+ while (search_space_len >= pattern_len)
+ {
+ gdb_byte *found_ptr;
+ unsigned nr_search_bytes = min (search_space_len, search_buf_size);
+
+ found_ptr = memmem (search_buf, nr_search_bytes,
+ pattern, pattern_len);
+
+ if (found_ptr != NULL)
+ {
+ CORE_ADDR found_addr = start_addr + (found_ptr - search_buf);
+ *found_addrp = found_addr;
+ do_cleanups (old_cleanups);
+ return 1;
+ }
+
+ /* Not found in this chunk, skip to next chunk. */
+
+ /* Don't let search_space_len wrap here, it's unsigned. */
+ if (search_space_len >= chunk_size)
+ search_space_len -= chunk_size;
+ else
+ search_space_len = 0;
+
+ if (search_space_len >= pattern_len)
+ {
+ unsigned keep_len = search_buf_size - chunk_size;
+ CORE_ADDR read_addr = start_addr + keep_len;
+ int nr_to_read;
+
+ /* Copy the trailing part of the previous iteration to the front
+ of the buffer for the next iteration. */
+ gdb_assert (keep_len == pattern_len - 1);
+ memcpy (search_buf, search_buf + chunk_size, keep_len);
+
+ nr_to_read = min (search_space_len - keep_len, chunk_size);
+
+ if (target_read (ops, TARGET_OBJECT_MEMORY, NULL,
+ search_buf + keep_len, read_addr,
+ nr_to_read) != nr_to_read)
+ {
+ warning (_("unable to access target memory at %s, halting search"),
+ hex_string (read_addr));
+ do_cleanups (old_cleanups);
+ return -1;
+ }
+
+ start_addr += chunk_size;
+ }
+ }
+
+ /* Not found. */
+
+ do_cleanups (old_cleanups);
+ return 0;
+}
+
+/* Search SEARCH_SPACE_LEN bytes beginning at START_ADDR for the
+ sequence of bytes in PATTERN with length PATTERN_LEN.
+
+ The result is 1 if found, 0 if not found, and -1 if there was an error
+ requiring halting of the search (e.g. memory read error).
+ If the pattern is found the address is recorded in FOUND_ADDRP. */
+
+int
+target_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp)
+{
+ struct target_ops *t;
+ int found;
+
+ /* We don't use INHERIT to set current_target.to_search_memory,
+ so we have to scan the target stack and handle targetdebug
+ ourselves. */
+
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "target_search_memory (%s, ...)\n",
+ hex_string (start_addr));
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_search_memory != NULL)
+ break;
+
+ if (t != NULL)
+ {
+ found = t->to_search_memory (t, start_addr, search_space_len,
+ pattern, pattern_len, found_addrp);
+ }
+ else
+ {
+ /* If a special version of to_search_memory isn't available, use the
+ simple version. */
+ found = simple_search_memory (&current_target,
+ start_addr, search_space_len,
+ pattern, pattern_len, found_addrp);
+ }
+
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, " = %d\n", found);
+
+ return found;
+}
+
/* Look through the currently pushed targets. If none of them will
be able to restart the currently running process, issue an error
message. */
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.116
diff -u -p -u -p -r1.116 target.h
--- target.h 8 Apr 2008 17:02:23 -0000 1.116
+++ target.h 17 Apr 2008 02:35:24 -0000
@@ -497,6 +497,17 @@ struct target_ops
was available. */
const struct target_desc *(*to_read_description) (struct target_ops *ops);

+ /* Search SEARCH_SPACE_LEN bytes beginning at START_ADDR for the
+ sequence of bytes in PATTERN with length PATTERN_LEN.
+
+ The result is 1 if found, 0 if not found, and -1 if there was an error
+ requiring halting of the search (e.g. memory read error).
+ If the pattern is found the address is recorded in FOUND_ADDRP. */
+ int (*to_search_memory) (struct target_ops *ops,
+ CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1095,6 +1106,19 @@ extern int target_stopped_data_address_p

extern const struct target_desc *target_read_description (struct target_ops *);

+/* Utility implementation of searching memory. */
+extern int
+simple_search_memory (struct target_ops* ops,
+ CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp);
+
+/* Main entry point for searching memory. */
+extern int
+target_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp);
+
/* Command logging facility. */

#define target_log_command(p) \
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.481
diff -u -p -u -p -r1.481 gdb.texinfo
--- doc/gdb.texinfo 16 Apr 2008 13:14:18 -0000 1.481
+++ doc/gdb.texinfo 17 Apr 2008 02:35:24 -0000
@@ -5534,6 +5534,7 @@ Table}.
* Character Sets:: Debugging programs that use a different
character set than GDB does
* Caching Remote Data:: Data caching for remote targets
+* Searching Memory:: Searching memory for a sequence of bytes
@end menu

@node Expressions
@@ -7597,6 +7598,101 @@ state (dirty, bad, ok, etc.). This comm
the data cache operation.
@end table

+@node Searching Memory
+@section Search Memory
+@cindex searching memory
+
+Memory can be searched for a particular sequence of bytes with the
+@code{find} command.
+
+@table @code
+@kindex find
+@item find @r{[}/@var{sn}@r{]} @var{start_addr}, +@var{len}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]}
+@itemx find @r{[}/@var{sn}@r{]} @var{start_addr}, @var{end_addr}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]}
+Search memory for the sequence of bytes specified by @var{val1}, @var{val2},
+etc. The search begins at address @var{start_addr} and continues for either
+@var{len} bytes or through to @var{end_addr} inclusive.
+@end table
+
+@var{s} and @var{n} are optional parameters.
+They may be specified in either order, apart or together.
+
+@table @r
+@item @var{s}, search query size
+The size of each search query value.
+
+@table @code
+@item b
+Bytes.
+@item h
+Halfwords (two bytes).
+@item w
+Words (four bytes).
+@item g
+Giant words (eight bytes).
+@end table
+
+All values are interpreted in the current language.
+This means, for example, that if the current source language is C/C++
+then searching for the string "hello" includes the trailing '\0'.
+
+If the value size is not specified, it is taken from the
+value's type in the current language.
+This is useful when one wants to specify the search
+pattern as a mixture of types.
+Note that this means, for example, that in the case of C-like languages
+a search for an untyped 0x42 will search for "(int) 0x42"
+which is typically four bytes.
+
+@item @var{n}, maximum number of finds
+The maximum number of finds to print. The default is to print all finds.
+@end table
+
+Strings may be specified for search values, quote them with double-quotes
+(@code{"}). The string value is copied into the search pattern byte by byte,
+regardless of the endianness of the target and the size specification.
+
+The address of each match found is printed as well as a count of the
+number of matches found.
+
+The address of the last value found is stored in convenience variable
+@samp{$_}.
+A count of the number of matches is stored in @samp{$numfound}.
+
+For example, if stopped at the printf in this function:
+
+@smallexample
+void
+hello ()
+@{
+ static char hello[] = "hello-hello";
+ static struct @{ char c; short s; int i; @} __attribute__ ((packed)) mixed
+ = @{ 'c', 0x1234, 0x87654321 @};
+ printf ("%s\n", hello);
+@}
+@end smallexample
+
+You get during debugging:
+
+@smallexample
+(gdb) find &hello[0], +sizeof(hello), "hello"
+0x804956d <hello.1620+6>
+1 pattern found
+(gdb) find &hello[0], +sizeof(hello), 'h', 'e', 'l', 'l', 'o'
+0x8049567 <hello.1620>
+0x804956d <hello.1620+6>
+2 patterns found
+(gdb) find /b1 &hello[0], +sizeof(hello), 'h', 0x65, 'l'
+0x8049567 <hello.1620>
+1 pattern found
+(gdb) find &mixed, +sizeof(mixed), (char) 'c', (short) 0x1234, (int) 0x87654321
+0x8049560 <mixed.1625>
+1 pattern found
+(gdb) print $numfound
+$1 = 1
+(gdb) print $_
+$2 = (void *) 0x8049560
+@end smallexample

@node Macros
@chapter C Preprocessor Macros
@@ -13434,6 +13530,10 @@ are:
@tab @code{qGetTLSAddr}
@tab Displaying @code{__thread} variables

+@item @code{search-memory}
+@tab @code{qSearch:memory}
+@tab @code{find}
+
@item @code{supported-packets}
@tab @code{qSupported}
@tab Remote communications parameters
@@ -24465,6 +24565,26 @@ command by a @samp{,}, not a @samp{:}, c
conventions above. Please don't use this packet as a model for new
packets.)

+@item qSearch:memory:@var{address};@var{length};@var{search-pattern}
+@cindex searching memory, in remote debugging
+@cindex @samp{qSearch:memory} packet
+@anchor{qSearch memory}
+Search @var{length} bytes at @var{address} for @var{search-pattern}.
+@var{address} and @var{length} are encoded in hex.
+@var{search-pattern} is a sequence of bytes, hex encoded.
+
+Reply:
+@table @samp
+@item 0
+The pattern was not found.
+@item 1,address
+The pattern was found at @var{address}.
+@item E @var{NN}
+A badly formed request or an error was encountered while searching memory.
+@item
+An empty reply indicates that @samp{qSearch:memory} is not recognized.
+@end table
+
@item qSupported @r{[}:@var{gdbfeature} @r{[};@var{gdbfeature}@r{]}@dots{} @r{]}
@cindex supported packets, remote query
@cindex features of the remote protocol
Index: gdbserver/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v
retrieving revision 1.57
diff -u -p -u -p -r1.57 Makefile.in
--- gdbserver/Makefile.in 14 Apr 2008 18:04:00 -0000 1.57
+++ gdbserver/Makefile.in 17 Apr 2008 02:35:24 -0000
@@ -81,11 +81,15 @@ BFD_CFLAGS = -I$(BFD_DIR) -I$(BFD_SRC)
READLINE_DIR = ${srcdir}/../readline
READLINE_DEP = $$(READLINE_DIR)

+# gnulib
+INCGNULIB = -I$(srcdir)/../gnulib -I../gnulib
+
# All the includes used for CFLAGS and for lint.
# -I. for config files.
# -I${srcdir} for our headers.
# -I$(srcdir)/../regformats for regdef.h.
-INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../regformats -I$(INCLUDE_DIR)
+INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../regformats -I$(INCLUDE_DIR) \
+ $(INCGNULIB)

# M{H,T}_CFLAGS, if defined, has host- and target-dependent CFLAGS
# from the config/ directory.
@@ -143,7 +147,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPAR

OBS = inferiors.o regcache.o remote-utils.o server.o signals.o target.o \
utils.o version.o \
- mem-break.o hostio.o \
+ mem-break.o memmem.o hostio.o \
$(XML_BUILTIN) \
$(DEPFILES) $(LIBOBJS)
GDBSERVER_LIBS = @GDBSERVER_LIBS@
Index: gdbserver/remote-utils.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v
retrieving revision 1.54
diff -u -p -u -p -r1.54 remote-utils.c
--- gdbserver/remote-utils.c 30 Jan 2008 00:51:50 -0000 1.54
+++ gdbserver/remote-utils.c 17 Apr 2008 02:35:24 -0000
@@ -1080,6 +1080,24 @@ decode_xfer_write (char *buf, int packet
return 0;
}

+/* Decode the parameters of a qSearch:memory packet. */
+
+int
+decode_search_memory_packet (const char *buf, int packet_len,
+ CORE_ADDR *start_addrp,
+ CORE_ADDR *search_space_lenp,
+ gdb_byte *pattern, unsigned int *pattern_lenp)
+{
+ const char *p = buf;
+
+ p = decode_address_to_semicolon (start_addrp, p);
+ p = decode_address_to_semicolon (search_space_lenp, p);
+ packet_len -= p - buf;
+ *pattern_lenp = remote_unescape_input ((const gdb_byte *) p, packet_len,
+ pattern, packet_len);
+ return 0;
+}
+
/* Ask GDB for the address of NAME, and return it in ADDRP if found.
Returns 1 if the symbol is found, 0 if it is not, -1 on error. */

Index: gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.70
diff -u -p -u -p -r1.70 server.c
--- gdbserver/server.c 4 Apr 2008 19:19:11 -0000 1.70
+++ gdbserver/server.c 17 Apr 2008 02:35:24 -0000
@@ -314,6 +314,154 @@ monitor_show_help (void)
monitor_output (" Quit GDBserver\n");
}

+/* Subroutine of handle_search_memory to simplify it. */
+
+static int
+handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
+ gdb_byte *pattern, unsigned pattern_len,
+ gdb_byte *search_buf,
+ unsigned chunk_size, unsigned search_buf_size,
+ CORE_ADDR *found_addrp)
+{
+ /* Prime the search buffer. */
+
+ if (read_inferior_memory (start_addr, search_buf, search_buf_size) != 0)
+ {
+ warning ("unable to access target memory at 0x%lx, halting search",
+ (long) start_addr);
+ return -1;
+ }
+
+ /* Perform the search.
+
+ The loop is kept simple by allocating [N + pattern-length - 1] bytes.
+ When we've scanned N bytes we copy the trailing bytes to the start and
+ read in another N bytes. */
+
+ while (search_space_len >= pattern_len)
+ {
+ gdb_byte *found_ptr;
+ unsigned nr_search_bytes = (search_space_len < search_buf_size
+ ? search_space_len
+ : search_buf_size);
+
+ found_ptr = memmem (search_buf, nr_search_bytes,
+ pattern, pattern_len);
+
+ if (found_ptr != NULL)
+ {
+ CORE_ADDR found_addr = start_addr + (found_ptr - search_buf);
+ *found_addrp = found_addr;
+ return 1;
+ }
+
+ /* Not found in this chunk, skip to next chunk. */
+
+ /* Don't let search_space_len wrap here, it's unsigned. */
+ if (search_space_len >= chunk_size)
+ search_space_len -= chunk_size;
+ else
+ search_space_len = 0;
+
+ if (search_space_len >= pattern_len)
+ {
+ unsigned keep_len = search_buf_size - chunk_size;
+ CORE_ADDR read_addr = start_addr + keep_len;
+ int nr_to_read;
+
+ /* Copy the trailing part of the previous iteration to the front
+ of the buffer for the next iteration. */
+ memcpy (search_buf, search_buf + chunk_size, keep_len);
+
+ nr_to_read = (search_space_len - keep_len < chunk_size
+ ? search_space_len - keep_len
+ : chunk_size);
+
+ if (read_inferior_memory (read_addr, search_buf + keep_len,
+ nr_to_read) != 0)
+ {
+ warning ("unable to access target memory at 0x%lx, halting search",
+ (long) read_addr);
+ return -1;
+ }
+
+ start_addr += chunk_size;
+ }
+ }
+
+ /* Not found. */
+
+ return 0;
+}
+
+/* Handle qSearch:memory packets. */
+
+static void
+handle_search_memory (char *own_buf, int packet_len)
+{
+ CORE_ADDR start_addr;
+ CORE_ADDR search_space_len;
+ gdb_byte *pattern;
+ unsigned int pattern_len;
+ /* NOTE: also defined in find.c testcase. */
+#define SEARCH_CHUNK_SIZE 16000
+ const unsigned chunk_size = SEARCH_CHUNK_SIZE;
+ /* Buffer to hold memory contents for searching. */
+ gdb_byte *search_buf;
+ unsigned search_buf_size;
+ int found;
+ CORE_ADDR found_addr;
+ int cmd_name_len = sizeof ("qSearch:memory:") - 1;
+
+ pattern = malloc (packet_len);
+ if (pattern == NULL)
+ {
+ error ("unable to allocate memory to perform the search");
+ strcpy (own_buf, "E00");
+ return;
+ }
+ if (decode_search_memory_packet (own_buf + cmd_name_len,
+ packet_len - cmd_name_len,
+ &start_addr, &search_space_len,
+ pattern, &pattern_len) < 0)
+ {
+ free (pattern);
+ error ("error in parsing qSearch:memory packet");
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ search_buf_size = chunk_size + pattern_len - 1;
+
+ /* No point in trying to allocate a buffer larger than the search space. */
+ if (search_space_len < search_buf_size)
+ search_buf_size = search_space_len;
+
+ search_buf = malloc (search_buf_size);
+ if (search_buf == NULL)
+ {
+ free (pattern);
+ error ("unable to allocate memory to perform the search");
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ found = handle_search_memory_1 (start_addr, search_space_len,
+ pattern, pattern_len,
+ search_buf, chunk_size, search_buf_size,
+ &found_addr);
+
+ if (found > 0)
+ sprintf (own_buf, "1,%lx", (long) found_addr);
+ else if (found == 0)
+ strcpy (own_buf, "0");
+ else
+ strcpy (own_buf, "E00");
+
+ free (search_buf);
+ free (pattern);
+}
+
#define require_running(BUF) \
if (!target_running ()) \
{ \
@@ -731,6 +879,13 @@ handle_query (char *own_buf, int packet_
return;
}

+ if (strncmp ("qSearch:memory:", own_buf, sizeof ("qSearch:memory:") - 1) == 0)
+ {
+ require_running (own_buf);
+ handle_search_memory (own_buf, packet_len);
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
Index: gdbserver/server.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.h,v
retrieving revision 1.41
diff -u -p -u -p -r1.41 server.h
--- gdbserver/server.h 27 Feb 2008 03:27:40 -0000 1.41
+++ gdbserver/server.h 17 Apr 2008 02:35:24 -0000
@@ -195,6 +195,10 @@ int decode_X_packet (char *from, int pac
int decode_xfer_write (char *buf, int packet_len, char **annex,
CORE_ADDR *offset, unsigned int *len,
unsigned char *data);
+int decode_search_memory_packet (const char *buf, int packet_len,
+ CORE_ADDR *start_addrp,
+ CORE_ADDR *search_space_lenp,
+ gdb_byte *pattern, unsigned int *pattern_lenp);

int unhexify (char *bin, const char *hex, int count);
int hexify (char *hex, const char *bin, int count);
Index: testsuite/gdb.base/find.c
===================================================================
RCS file: testsuite/gdb.base/find.c
diff -N testsuite/gdb.base/find.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.base/find.c 17 Apr 2008 02:35:25 -0000
@@ -0,0 +1,62 @@
+/* Testcase for the find command.
+ This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2008 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+
+ Please email any bugs, comments, and/or additions to this file to:
+ bug-gdb@gnu.org */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#define CHUNK_SIZE 16000 /* same as findcmd.c's */
+#define BUF_SIZE (2 * CHUNK_SIZE) /* at least two chunks */
+
+static int8_t int8_search_buf[100];
+static int16_t int16_search_buf[100];
+static int32_t int32_search_buf[100];
+static int64_t int64_search_buf[100];
+
+static char *search_buf;
+static int search_buf_size;
+
+static int x;
+
+static void
+stop_here ()
+{
+ x = 1; // stop here
+}
+
+static void
+init_bufs ()
+{
+ search_buf_size = BUF_SIZE;
+ search_buf = malloc (search_buf_size);
+ if (search_buf == NULL)
+ exit (1);
+ memset (search_buf, 'x', search_buf_size);
+}
+
+int
+main ()
+{
+ init_bufs ();
+
+ stop_here ();
+
+ return 0;
+}
Index: testsuite/gdb.base/find.exp
===================================================================
RCS file: testsuite/gdb.base/find.exp
diff -N testsuite/gdb.base/find.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.base/find.exp 17 Apr 2008 02:35:25 -0000
@@ -0,0 +1,191 @@
+# Copyright 2008 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 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/>.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@prep.ai.mit.edu
+
+# This tests the find command.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+set testfile "find"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug nowarnings}] != "" } {
+ untested find.exp
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+gdb_test "break $srcfile:stop_here" \
+ "Breakpoint.*at.* file .*$srcfile, line.*" \
+ "breakpoint function in file"
+
+gdb_run_cmd
+gdb_expect {
+ -re "Breakpoint \[0-9\]+,.*stop_here.* at .*$srcfile:.*$gdb_prompt $" {
+ pass "run until function breakpoint"
+ }
+ -re "$gdb_prompt $" {
+ fail "run until function breakpoint"
+ }
+ timeout {
+ fail "run until function breakpoint (timeout)"
+ }
+}
+
+# We've now got the target program in a state where we can test "find".
+
+set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}
+set history_prefix {[$][0-9]* = }
+set newline {[\r\n]*}
+set pattern_not_found "${newline}pattern not found"
+set one_pattern_found "${newline}1 pattern found"
+set two_patterns_found "${newline}2 patterns found"
+
+# Test string pattern.
+
+gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" ""
+
+gdb_test "find &int8_search_buf\[0\], +sizeof(int8_search_buf), 'a', 'a', 'a'" \
+ "${hex_number}.*<int8_search_buf\\+10>${newline}${hex_number}.*<int8_search_buf\\+11>${two_patterns_found}" \
+ "find string pattern"
+
+# Test not finding pattern because search range too small, with
+# potential find at the edge of the range.
+
+gdb_test "find &int8_search_buf\[0\], +10+3, \"aaaa\"" \
+ "${pattern_not_found}" \
+ "pattern not found at end of range"
+
+# Increase the search range by 1 and we should find the pattern.
+
+gdb_test "find &int8_search_buf\[0\], +10+3+1, 'a', 'a', 'a', 'a'" \
+ "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+ "pattern found at end of range"
+
+# Test max-count, $_ and $numfound.
+
+gdb_test "find /1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 'a', 'a', 'a'" \
+ "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+ "max-count"
+
+gdb_test "print \$_" \
+ "${history_prefix}.*${hex_number}" \
+ "\$_"
+
+gdb_test "print \$numfound" \
+ "${history_prefix}1" \
+ "\$numfound"
+
+# Test max-count with size-char.
+# They can be specified in either order.
+
+gdb_test "find /1b &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+ "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+ "size,max-count, /1b"
+
+gdb_test "find /b1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+ "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+ "size,max-count, /b1"
+
+gdb_test "find /b /1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+ "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+ "size,max-count, /b/1"
+
+gdb_test "find /1 /b &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+ "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+ "size,max-count, /1/b"
+
+# Test specifying end address.
+
+gdb_test "find /b &int8_search_buf\[0\], &int8_search_buf\[0\]+sizeof(int8_search_buf), 0x61, 0x61, 0x61, 0x61" \
+ "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+ "find byte pattern with end address"
+
+# Test 16-bit pattern.
+
+gdb_test "set int16_search_buf\[10\] = 0x1234" "" ""
+
+gdb_test "find /h &int16_search_buf\[0\], +sizeof(int16_search_buf), 0x1234" \
+ "${hex_number}.*<int16_search_buf\\+20>${one_pattern_found}" \
+ "find 16-bit pattern"
+
+gdb_test "find &int16_search_buf\[0\], +sizeof(int16_search_buf), (int16_t) 0x1234" \
+ "${hex_number}.*<int16_search_buf\\+20>${one_pattern_found}" \
+ "find 16-bit pattern"
+
+# Test 32-bit pattern.
+
+gdb_test "set int32_search_buf\[10\] = 0x12345678" "" ""
+
+gdb_test "find &int32_search_buf\[0\], +sizeof(int32_search_buf), (int32_t) 0x12345678" \
+ "${hex_number}.*<int32_search_buf\\+40>${one_pattern_found}" \
+ "find 32-bit pattern"
+
+gdb_test "find /w &int32_search_buf\[0\], +sizeof(int32_search_buf), 0x12345678" \
+ "${hex_number}.*<int32_search_buf\\+40>${one_pattern_found}" \
+ "find 32-bit pattern"
+
+# Test 64-bit pattern.
+
+gdb_test "set int64_search_buf\[10\] = 0xfedcba9876543210LL" "" ""
+
+gdb_test "find &int64_search_buf\[0\], +sizeof(int64_search_buf), (int64_t) 0xfedcba9876543210LL" \
+ "${hex_number}.*<int64_search_buf\\+80>${one_pattern_found}" \
+ "find 64-bit pattern"
+
+gdb_test "find /g &int64_search_buf\[0\], +sizeof(int64_search_buf), 0xfedcba9876543210LL" \
+ "${hex_number}.*<int64_search_buf\\+80>${one_pattern_found}" \
+ "find 64-bit pattern"
+
+# Test mixed-sized patterns.
+
+gdb_test "set *(int8_t*) &search_buf\[10\] = 0x62" "" ""
+gdb_test "set *(int16_t*) &search_buf\[11\] = 0x6363" "" ""
+gdb_test "set *(int32_t*) &search_buf\[13\] = 0x64646464" "" ""
+
+gdb_test "find &search_buf\[0\], +100, (int8_t) 0x62, (int16_t) 0x6363, (int32_t) 0x64646464" \
+ "${hex_number}${one_pattern_found}" \
+ "find mixed-sized pattern"
+
+# Test search spanning a large range, in the particular case of native
+# targets, test the search spanning multiple chunks.
+# Remote targets may implement the search differently.
+
+set CHUNK_SIZE 16000 ;# see findcmd.c
+
+gdb_test "set *(int32_t*) &search_buf\[0*${CHUNK_SIZE}+100\] = 0x12345678" "" ""
+gdb_test "set *(int32_t*) &search_buf\[1*${CHUNK_SIZE}+100\] = 0x12345678" "" ""
+
+gdb_test "find /w search_buf, +search_buf_size, 0x12345678" \
+ "${hex_number}${newline}${hex_number}${two_patterns_found}" \
+ "search spanning large range"
+
+# For native targets, test a pattern straddling a chunk boundary.
+
+if [isnative] {
+ gdb_test "set *(int32_t*) &search_buf\[${CHUNK_SIZE}-1\] = 0xfdb97531" "" ""
+ gdb_test "find /w search_buf, +search_buf_size, 0xfdb97531" \
+ "${hex_number}${one_pattern_found}" \
+ "find pattern straddling chunk boundary"
+}