[RFA] new command to search memory

Doug Evans
2008-02-14T02:20:06Z

I didn't get a response to the non-doc portions of this patch.
[Eli, thanks for the doc review, I made the suggested changes,
though I went a different route in shortening the length of the
line specifying the find command syntax. The end result is still
rather short.]

The libiberty patches need to go through gcc-patches, but I'm guessing
there's no point in bothering them until this patch is approved
at least in principal.

What can I do to help make progress here?

2008-02-13 Doug Evans <dje@google.com>

* include/libiberty.h: (lmemmem): Declare.

* libiberty/Makefile.in (CFILES): Add lmemmem.c.
(REQUIRED_OFILES): Add lmemmem.o.
(lmemmem.o): New rule.
* libiberty/configure.ac (funcs): Add memmem.
(AC_CHECK_FUNCS): Add memmem.
* libiberty/config.in: Regenerate.
* libiberty/configure: Regenerate.
* libiberty/lmemmem.c: New file.

New "find" command.
* gdb/Makefile.in (SFILES): Add findcmd.c.
(COMMON_OBJS): Add findcmd.o.
(findcmd.o): New rule.
* gdb/findcmd.c: New file.
* gdb/target.h (target_ops): New member to_search_memory.
(simple_search_memory): Declare.
(target_search_memory): Declare.
* gdb/target.c (simple_search_memory): New fn.
(default_search_memory): New fn.
(debug_to_search_memory): New fn.
(target_search_memory): New fn.
(update_current_target): Set to_search_memory.
(setup_target_debug): Set to_search_memory.
* gdb/remote.c (PACKET_qSearch_memory): New packet kind.
(remote_protocol_features): Add qSearch:memory.
(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/configure.ac: Check for memmem.
* gdbserver/configure: Regenerate.
* gdbserver/config.in: Regenerate.
* gdbserver/Makefile.in (SFILES): Add lmemmem.c.
(OBS): Add lmemmem.o.
(lmemmem.o): New rule.
* gdbserver/server.h (decode_search_memory_packet): Declare.
(lmemmem): 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.
* gdbserver/lmemmem.c: New file.
* doc/gdb.texinfo: Document "find" command, qSearch:memory packet.
* testsuite/gdb.base/find.exp: New file.
* testsuite/gdb.base/find.c: New file.

Index: include/libiberty.h
===================================================================
RCS file: /cvs/src/src/include/libiberty.h,v
retrieving revision 1.57
diff -u -p -u -p -r1.57 libiberty.h
--- include/libiberty.h 6 Sep 2007 17:22:36 -0000 1.57
+++ include/libiberty.h 14 Feb 2008 02:03:40 -0000
@@ -1,6 +1,6 @@
/* Function declarations for libiberty.

- Copyright 2001, 2002, 2005, 2007 Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2005, 2007, 2008 Free Software Foundation, Inc.

Note - certain prototypes declared in this header file are for
functions whoes implementation copyright does not belong to the
@@ -166,6 +166,10 @@ extern char *libiberty_concat_ptr;
(libiberty_concat_ptr = (char *) alloca (concat_length ACONCAT_PARAMS + 1), \
concat_copy2 ACONCAT_PARAMS)

+/* A well-defined memmem () that is always compiled in. */
+
+extern void * lmemmem (const void *, size_t, const void *, size_t);
+
/* Check whether two file descriptors refer to the same file. */

extern int fdmatch (int fd1, int fd2);
Index: libiberty/Makefile.in
===================================================================
RCS file: /cvs/src/src/libiberty/Makefile.in,v
retrieving revision 1.89
diff -u -p -u -p -r1.89 Makefile.in
--- libiberty/Makefile.in 25 Jul 2007 06:36:27 -0000 1.89
+++ libiberty/Makefile.in 14 Feb 2008 02:03:40 -0000
@@ -2,7 +2,7 @@
# Originally written by K. Richard Pixley <rich@cygnus.com>.
#
# Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
+# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software
# Foundation
#
# This file is part of the libiberty library.
@@ -133,6 +133,7 @@ CFILES = alloca.c argv.c asprintf.c atex
hashtab.c hex.c \
index.c insque.c \
lbasename.c \
+ lmemmem.c \
lrealpath.c \
make-relative-prefix.c \
make-temp-file.c md5.c memchr.c memcmp.c memcpy.c memmove.c \
@@ -164,7 +165,7 @@ REQUIRED_OFILES = ./regex.o ./cplus-dem.
./fnmatch.o ./fopen_unlocked.o \
./getopt.o ./getopt1.o ./getpwd.o ./getruntime.o \
./hashtab.o ./hex.o \
- ./lbasename.o ./lrealpath.o \
+ ./lbasename.o ./lmemmem.o ./lrealpath.o \
./make-relative-prefix.o ./make-temp-file.o \
./objalloc.o ./obstack.o \
./partition.o ./pexecute.o ./physmem.o \
@@ -748,6 +749,12 @@ $(CONFIGURED_OFILES): stamp-picdir
else true; fi
$(COMPILE.c) $(srcdir)/lbasename.c $(OUTPUT_OPTION)

+./lmemmem.o: $(srcdir)/lmemmem.c stamp-h
+ if [ x"$(PICFLAG)" != x ]; then \
+ $(COMPILE.c) $(PICFLAG) $(srcdir)/lmemmem.c -o pic/$@; \
+ else true; fi
+ $(COMPILE.c) $(srcdir)/lmemmem.c $(OUTPUT_OPTION)
+
./lrealpath.o: $(srcdir)/lrealpath.c stamp-h $(INCDIR)/ansidecl.h \
$(INCDIR)/libiberty.h
if [ x"$(PICFLAG)" != x ]; then \
Index: libiberty/config.in
===================================================================
RCS file: /cvs/src/src/libiberty/config.in,v
retrieving revision 1.38
diff -u -p -u -p -r1.38 config.in
--- libiberty/config.in 22 Jul 2005 03:16:32 -0000 1.38
+++ libiberty/config.in 14 Feb 2008 02:03:40 -0000
@@ -139,6 +139,9 @@
/* Define to 1 if you have the `memcpy' function. */
#undef HAVE_MEMCPY

+/* Define to 1 if you have the `memmem' function. */
+#undef HAVE_MEMMEM
+
/* Define to 1 if you have the `memmove' function. */
#undef HAVE_MEMMOVE

Index: libiberty/configure
===================================================================
RCS file: /cvs/src/src/libiberty/configure,v
retrieving revision 1.89
diff -u -p -u -p -r1.89 configure
--- libiberty/configure 17 Jul 2007 18:05:02 -0000 1.89
+++ libiberty/configure 14 Feb 2008 02:03:40 -0000
@@ -5062,6 +5062,7 @@ vars="sys_errlist sys_nerr sys_siglist"
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp"
checkfuncs="$checkfuncs getsysinfo table sysctl wait3 wait4 __fsetlocking"
+checkfuncs="$checkfuncs memmem"

# These are neither executed nor required, but they help keep
# autoheader happy without adding a bunch of text to acconfig.h.
Index: libiberty/configure.ac
===================================================================
RCS file: /cvs/src/src/libiberty/configure.ac,v
retrieving revision 1.37
diff -u -p -u -p -r1.37 configure.ac
--- libiberty/configure.ac 17 Jul 2007 18:05:02 -0000 1.37
+++ libiberty/configure.ac 14 Feb 2008 02:03:41 -0000
@@ -356,6 +356,7 @@ vars="sys_errlist sys_nerr sys_siglist"
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp"
checkfuncs="$checkfuncs getsysinfo table sysctl wait3 wait4 __fsetlocking"
+checkfuncs="$checkfuncs memmem"

# These are neither executed nor required, but they help keep
# autoheader happy without adding a bunch of text to acconfig.h.
Index: libiberty/lmemmem.c
===================================================================
RCS file: libiberty/lmemmem.c
diff -N libiberty/lmemmem.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libiberty/lmemmem.c 14 Feb 2008 02:03:41 -0000
@@ -0,0 +1,58 @@
+/* lmemmem -- search for a sequence of bytes
+ This function is in the public domain. */
+
+/*
+
+@deftypefn Supplemental void *lmemmem (const void *@var{haystack}, size_t @var{haystacklen},
+ const void *@var{needle}, size_t @var{needlelen})
+
+Search the area of memory at @var{haystack} for the bytes at @var{needle},
+and return the first occurrence.
+Returns a pointer to the beginning of the string or NULL if not found.
+
+@end deftypefn
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef HAVE_STRING_H
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* memmem */
+#endif
+#include <string.h>
+#endif
+#include <sys/types.h> /* size_t */
+#include "libiberty.h"
+
+void*
+lmemmem (const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen)
+{
+#ifdef HAVE_MEMMEM
+ return memmem (haystack, haystacklen, needle, needlelen);
+#else
+ size_t i,j;
+ const char *h = (const char *) haystack;
+ const char *n = (const char *) needle;
+
+ if (needlelen > haystacklen)
+ return NULL;
+ if (needlelen == 0)
+ return (void *) haystack; /* this is what glibc memmem does */
+
+ for (i = 0; i <= haystacklen - needlelen; ++i)
+ {
+ for (j = 0; j < needlelen; ++j)
+ {
+ if (h[i + j] != n[j])
+ break;
+ }
+ if (j == needlelen)
+ return (void*) (h + i);
+ }
+
+ return NULL;
+#endif
+}
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.982
diff -u -p -u -p -r1.982 Makefile.in
--- gdb/Makefile.in 11 Feb 2008 21:58:41 -0000 1.982
+++ gdb/Makefile.in 14 Feb 2008 02:03:41 -0000
@@ -600,9 +600,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 findvar.c findcmd.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 \
@@ -1043,6 +1042,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 \
@@ -2101,6 +2101,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: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.257
diff -u -p -u -p -r1.257 NEWS
--- gdb/NEWS 11 Feb 2008 21:58:41 -0000 1.257
+++ gdb/NEWS 14 Feb 2008 02:03:41 -0000
@@ -64,6 +64,10 @@ stored in two consecutive float register

* New commands

+find [/size-char] [/max-count] start-address, end-address|@search-space-size,
+ val1 [, val2, ...]
+ Search memory for a sequence of bytes.
+
set print frame-arguments (all|scalars|none)
show print frame-arguments
The value of this variable can be changed to control which argument
@@ -83,6 +87,9 @@ remote delete

* New remote packets

+qSearch:memory:
+ Search memory for a sequence of bytes.
+
vFile:open:
vFile:close:
vFile:pread:
Index: gdb/findcmd.c
===================================================================
RCS file: gdb/findcmd.c
diff -N gdb/findcmd.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/findcmd.c 14 Feb 2008 02:03:41 -0000
@@ -0,0 +1,399 @@
+/* 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"
+
+static void
+put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p);
+
+static int
+parse_search_string (char **strp, char **parsed_stringp);
+
+/* 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;
+ }
+}
+
+/* Parse a C/C++ string.
+ *STRP does not contain any embedded nulls.
+ *STRP points to the leading double quote (").
+ The result is the length of the string, and
+ *PARSED_STRINGP contains the parsed string, malloc'd, and
+ *STRP is updated to point to one past the trailing double quote.
+ We need to return the length in case the result has embedded nulls.
+ If there is an error while parsing the string, error() is called
+ so we don't return. */
+
+static int
+parse_search_string (char **strp, char **parsed_stringp)
+{
+ char *start = *strp;
+ char *s = start;
+ char *result_string = xmalloc (strlen (start));
+ char *r = result_string;
+ int len = 0;
+
+ gdb_assert (*s == '"');
+ ++s;
+
+ while (*s != '\0' && *s != '"')
+ {
+ if (*s == '\\')
+ {
+ int c;
+ ++s;
+ c = parse_escape (&s);
+ if (c >= 0)
+ {
+ *r++ = c;
+ ++len;
+ }
+ }
+ else if (*s == '"')
+ {
+ break;
+ }
+ else
+ {
+ *r++ = *s++;
+ ++len;
+ }
+ }
+
+ if (*s != '"')
+ error ("missing trailing double-quote(\") in string");
+ ++s;
+
+ *strp = s;
+ *parsed_stringp = result_string;
+ return len;
+}
+
+static void
+find_command (char *args, int from_tty)
+{
+ /* default to using the specified type */
+ char size = '\0';
+ ULONGEST max_count = ~(ULONGEST) 0;
+ CORE_ADDR start_addr;
+ ULONGEST search_space_len;
+ struct value *v;
+ char *s = args;
+ /* 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;
+ /* Length of the pattern. */
+ ULONGEST pattern_len;
+ /* Buffer to hold memory contents for searching. */
+ char *search_buf;
+ ULONGEST search_buf_size;
+ /* Where in search_buf to begin searching. */
+ char *search_buf_start;
+ struct cleanup *old_cleanups;
+ unsigned int found_count;
+ CORE_ADDR last_found_addr;
+ enum bfd_endian endian = gdbarch_byte_order (current_gdbarch);
+ /* If endian is unknown use big endian.
+ ??? Is there an established convention for what to pick? */
+ bfd_boolean big_p = endian != BFD_ENDIAN_LITTLE;
+
+ 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;
+
+ /* If we see a string, parse it ourselves rather than the normal
+ handling of downloading it to target memory. */
+
+ while (isspace (*s))
+ ++s;
+
+ if (*s == '"')
+ {
+ char *str;
+ int len = parse_search_string (&s, &str);
+
+ if ((pattern_buf_end - pattern_buf + len)
+ > pattern_buf_size)
+ {
+ size_t current_offset = pattern_buf_end - pattern_buf;
+ pattern_buf_size += len * 2; /* kiss */
+ pattern_buf = xrealloc (pattern_buf, pattern_buf_size);
+ pattern_buf_end = pattern_buf + current_offset;
+ }
+
+ memcpy (pattern_buf_end, str, len);
+ pattern_buf_end += len;
+
+ free (str);
+
+ /* Leave the pointer at the next comma, like the `else' clause
+ will do. */
+ while (isspace (*s))
+ ++s;
+
+ if (*s != '\0' && *s != ',')
+ error ("comma expected between expressions");
+ }
+ else /* Not a string, parse the expression the normal way. */
+ {
+ int val_bytes;
+
+ /* ??? Need to prevent (char*) "foo", it downloads string to target
+ and we don't want that. */
+ 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"));
+
+ /* 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\
+\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: gdb/remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.278
diff -u -p -u -p -r1.278 remote.c
--- gdb/remote.c 30 Jan 2008 00:51:49 -0000 1.278
+++ gdb/remote.c 14 Feb 2008 02:03:41 -0000
@@ -933,6 +933,7 @@ enum {
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
+ PACKET_qSearch_memory,
PACKET_vAttach,
PACKET_vRun,
PACKET_MAX
@@ -2459,6 +2460,8 @@ static struct protocol_feature remote_pr
PACKET_qXfer_spu_write },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
+ { "qSearch:memory", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qSearch_memory },
};

static void
@@ -6198,6 +6201,81 @@ remote_xfer_partial (struct target_ops *
return strlen ((char *) readbuf);
}

+static int
+remote_search_memory (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 after 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 (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 (&current_target,
+ 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)
+ 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)
@@ -7253,6 +7331,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;
}

/* Set up the extended remote vector by making a copy of the standard
@@ -7388,6 +7467,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
@@ -7648,6 +7728,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: gdb/target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.154
diff -u -p -u -p -r1.154 target.c
--- gdb/target.c 25 Jan 2008 00:09:49 -0000 1.154
+++ gdb/target.c 14 Feb 2008 02:03:41 -0000
@@ -88,6 +88,11 @@ static LONGEST target_xfer_partial (stru
void *readbuf, const void *writebuf,
ULONGEST offset, LONGEST len);

+static int
+default_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp);
+
static void init_dummy_target (void);

static struct target_ops debug_target;
@@ -160,6 +165,12 @@ static int debug_to_thread_alive (ptid_t

static void debug_to_stop (void);

+static int debug_to_search_memory (CORE_ADDR start_addr,
+ ULONGEST search_space_len,
+ const gdb_byte *pattern,
+ ULONGEST pattern_len,
+ CORE_ADDR *found_addrp);
+
/* NOTE: cagney/2004-09-29: Many targets reference this variable in
wierd and mysterious ways. Putting the variable here lets those
wierd and mysterious ways keep building while they are being
@@ -464,6 +475,7 @@ update_current_target (void)
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_thread_local_address, t);
/* Do not inherit to_read_description. */
+ INHERIT (to_search_memory, t);
INHERIT (to_magic, t);
/* Do not inherit to_memory_map. */
/* Do not inherit to_flash_erase. */
@@ -636,6 +648,8 @@ update_current_target (void)
(void (*) (void (*) (enum inferior_event_type, void*), void*))
tcomplain);
current_target.to_read_description = NULL;
+ de_fault (to_search_memory, default_search_memory);
+
#undef de_fault

/* Finally, position the target-stack beneath the squashed
@@ -1723,6 +1737,139 @@ target_read_description (struct target_o
return NULL;
}

+/* Utility to implement a basic search of memory. */
+
+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)
+{
+ /* ??? tunable?
+ 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 = lmemmem (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;
+}
+
+/* The default implementation of to_search_memory. */
+
+static int
+default_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp)
+{
+ return simple_search_memory (&current_target, start_addr, search_space_len,
+ pattern, pattern_len, found_addrp);
+}
+
+/* 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.
+
+ NOTE: May wish to give target ability to maintain state across successive
+ calls within one search request. Left for later. */
+
+int
+target_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp)
+{
+ return current_target.to_search_memory (start_addr, search_space_len,
+ pattern, pattern_len,
+ found_addrp);
+}
+
/* Look through the list of possible targets for a target that can
execute a run or attach command without any other data. This is
used to locate the default process stratum.
@@ -2677,6 +2824,19 @@ debug_to_pid_to_exec_file (int pid)
return exec_file;
}

+static int
+debug_to_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len,
+ const gdb_byte *pattern, ULONGEST pattern_len,
+ CORE_ADDR *found_addrp)
+{
+ int found = debug_target.to_search_memory (start_addr, search_space_len,
+ pattern, pattern_len,
+ found_addrp);
+ fprintf_unfiltered (gdb_stdlog, "target_search_memory (%s, ...) = %d\n",
+ hex_string (start_addr), found);
+ return found;
+}
+
static void
setup_target_debug (void)
{
@@ -2732,6 +2892,7 @@ setup_target_debug (void)
current_target.to_stop = debug_to_stop;
current_target.to_rcmd = debug_to_rcmd;
current_target.to_pid_to_exec_file = debug_to_pid_to_exec_file;
+ current_target.to_search_memory = debug_to_search_memory;
}
\f

Index: gdb/target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.109
diff -u -p -u -p -r1.109 target.h
--- gdb/target.h 1 Jan 2008 22:53:13 -0000 1.109
+++ gdb/target.h 14 Feb 2008 02:03:42 -0000
@@ -500,6 +500,16 @@ 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) (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?
*/
@@ -1107,6 +1117,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: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.467
diff -u -p -u -p -r1.467 gdb.texinfo
--- gdb/doc/gdb.texinfo 3 Feb 2008 18:55:27 -0000 1.467
+++ gdb/doc/gdb.texinfo 14 Feb 2008 02:03:43 -0000
@@ -5543,6 +5543,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
@@ -7521,6 +7522,90 @@ 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
+
+If the value size is not specified, it is taken from the
+value's type. This is useful when one wants to specify the search
+pattern as a mixture of types.
+
+@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"
+0x601048 <hello.1628>
+0x60104e <hello.1628+6>
+2 patterns found
+(gdb) find /b1 &hello[0], @@sizeof(hello), 'h', 0x65, 'l'
+0x601048 <hello.1628>
+1 pattern found
+(gdb) find &mixed, @@sizeof(mixed), (char) 'c', (short) 0x1234, (int) 0x87654321
+0x601054 <mixed.1633>
+1 pattern found
+(gdb) print $numfound
+$1 = 1
+(gdb) print $_
+$2 = (void *) 0x601054
+@end smallexample

@node Macros
@chapter C Preprocessor Macros
@@ -13332,6 +13417,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
@@ -24330,6 +24419,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
@@ -24471,6 +24580,11 @@ These are the currently defined stub fea
@tab @samp{-}
@tab Yes

+@item @samp{qSearch:memory}
+@tab No
+@tab @samp{-}
+@tab Yes
+
@end multitable

These are the currently defined stub features, in more detail:
@@ -24515,6 +24629,10 @@ The remote stub understands the @samp{qX
The remote stub understands the @samp{QPassSignals} packet
(@pxref{QPassSignals}).

+@item qSearch:memory
+The remote stub understands the @samp{qSearch:memory} packet
+(@pxref{qSearch memory}).
+
@end table

@item qSymbol::
Index: gdb/gdbserver/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v
retrieving revision 1.54
diff -u -p -u -p -r1.54 Makefile.in
--- gdb/gdbserver/Makefile.in 11 Feb 2008 22:00:31 -0000 1.54
+++ gdb/gdbserver/Makefile.in 14 Feb 2008 02:03:43 -0000
@@ -122,6 +122,7 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/
$(srcdir)/thread-db.c $(srcdir)/utils.c \
$(srcdir)/linux-arm-low.c $(srcdir)/linux-cris-low.c \
$(srcdir)/linux-crisv32-low.c $(srcdir)/linux-i386-low.c \
+ $(srcdir)/lmemmem.c \
$(srcdir)/i387-fp.c \
$(srcdir)/linux-ia64-low.c $(srcdir)/linux-low.c \
$(srcdir)/linux-m32r-low.c \
@@ -141,7 +142,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 hostio.o lmemmem.o \
$(XML_BUILTIN) \
$(DEPFILES)
GDBSERVER_LIBS = @GDBSERVER_LIBS@
@@ -294,6 +295,8 @@ utils.o: utils.c $(server_h)
signals.o: ../signals/signals.c $(server_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER

+lmemmem.o: lmemmem.c $(server_h)
+
i387-fp.o: i387-fp.c $(server_h)

linux_low_h = $(srcdir)/linux-low.h
Index: gdb/gdbserver/config.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/config.in,v
retrieving revision 1.20
diff -u -p -u -p -r1.20 config.in
--- gdb/gdbserver/config.in 16 Dec 2007 21:50:05 -0000 1.20
+++ gdb/gdbserver/config.in 14 Feb 2008 02:03:43 -0000
@@ -41,6 +41,9 @@
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H

+/* Define to 1 if you have the `memmem' function. */
+#undef HAVE_MEMMEM
+
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H

Index: gdb/gdbserver/configure
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure,v
retrieving revision 1.32
diff -u -p -u -p -r1.32 configure
--- gdb/gdbserver/configure 1 Feb 2008 00:08:25 -0000 1.32
+++ gdb/gdbserver/configure 14 Feb 2008 02:03:43 -0000
@@ -3101,7 +3101,7 @@ done

-for ac_func in pread pwrite pread64
+for ac_func in memmem pread pwrite pread64
do
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
echo "$as_me:$LINENO: checking for $ac_func" >&5
Index: gdb/gdbserver/configure.ac
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.ac,v
retrieving revision 1.20
diff -u -p -u -p -r1.20 configure.ac
--- gdb/gdbserver/configure.ac 1 Feb 2008 00:08:25 -0000 1.20
+++ gdb/gdbserver/configure.ac 14 Feb 2008 02:03:43 -0000
@@ -41,7 +41,7 @@ AC_CHECK_HEADERS(sgtty.h termio.h termio
errno.h fcntl.h signal.h sys/file.h malloc.h dnl
sys/ioctl.h netinet/in.h sys/socket.h netdb.h dnl
netinet/tcp.h arpa/inet.h sys/wait.h)
-AC_CHECK_FUNCS(pread pwrite pread64)
+AC_CHECK_FUNCS(memmem pread pwrite pread64)

have_errno=no
AC_MSG_CHECKING(for errno)
Index: gdb/gdbserver/lmemmem.c
===================================================================
RCS file: gdb/gdbserver/lmemmem.c
diff -N gdb/gdbserver/lmemmem.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/gdbserver/lmemmem.c 14 Feb 2008 02:03:43 -0000
@@ -0,0 +1,43 @@
+/* lmemmem -- search for a sequence of bytes
+ This function is in the public domain. */
+
+#include "config.h"
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* memmem */
+#endif
+#include <string.h>
+#endif
+
+void*
+lmemmem (const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen)
+{
+#ifdef HAVE_MEMMEM
+ return memmem (haystack, haystacklen, needle, needlelen);
+#else
+ size_t i,j;
+ const char *h = (const char *) haystack;
+ const char *n = (const char *) needle;
+
+ if (needlelen > haystacklen)
+ return NULL;
+ if (needlelen == 0)
+ return (void *) haystack; /* this is what glibc memmem does */
+
+ for (i = 0; i <= haystacklen - needlelen; ++i)
+ {
+ for (j = 0; j < needlelen; ++j)
+ {
+ if (h[i + j] != n[j])
+ break;
+ }
+ if (j == needlelen)
+ return (void*) (h + i);
+ }
+
+ return NULL;
+#endif
+}
Index: gdb/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
--- gdb/gdbserver/remote-utils.c 30 Jan 2008 00:51:50 -0000 1.54
+++ gdb/gdbserver/remote-utils.c 14 Feb 2008 02:03:43 -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: gdb/gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.63
diff -u -p -u -p -r1.63 server.c
--- gdb/gdbserver/server.c 30 Jan 2008 00:51:50 -0000 1.63
+++ gdb/gdbserver/server.c 14 Feb 2008 02:03:43 -0000
@@ -270,6 +270,157 @@ monitor_show_help (void)
monitor_output (" Enable remote protocol debugging messages\n");
}

+/* Subroutine of handle_search_memory to simplify it. */
+/* ??? Copied from simple_search_memory. Combine? */
+
+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 = lmemmem (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. */
+/* ??? Copied from simple_search_memory. Combine? */
+
+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;
+ /* ??? tunable?
+ 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 ()) \
{ \
@@ -568,6 +719,10 @@ handle_query (char *own_buf, int packet_
supports qXfer:libraries:read, so always report it. */
strcat (own_buf, ";qXfer:libraries:read+");

+ /* Do the searching here, so we don't have to send memory back to gdb
+ to be searched. */
+ strcat (own_buf, ";qSearch:memory+");
+
if (the_target->read_auxv != NULL)
strcat (own_buf, ";qXfer:auxv:read+");

@@ -692,6 +847,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: gdb/gdbserver/server.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.h,v
retrieving revision 1.40
diff -u -p -u -p -r1.40 server.h
--- gdb/gdbserver/server.h 1 Feb 2008 00:08:25 -0000 1.40
+++ gdb/gdbserver/server.h 14 Feb 2008 02:03:44 -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);
@@ -221,6 +225,10 @@ void error (const char *string,...) ATTR
void fatal (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2);
void warning (const char *string,...) ATTR_FORMAT (printf, 1, 2);

+/* Functions from lmemmem.c */
+
+void *lmemmem (const void *, size_t, const void *, size_t);
+
/* Functions from the register cache definition. */

void init_registers (void);
Index: gdb/testsuite/gdb.base/find.c
===================================================================
RCS file: gdb/testsuite/gdb.base/find.c
diff -N gdb/testsuite/gdb.base/find.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/find.c 14 Feb 2008 02:03:47 -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: gdb/testsuite/gdb.base/find.exp
===================================================================
RCS file: gdb/testsuite/gdb.base/find.exp
diff -N gdb/testsuite/gdb.base/find.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/find.exp 14 Feb 2008 02:03:47 -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), \"aaa\"" \
+ "${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, \"aaaa\"" \
+ "${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), \"aaa\"" \
+ "${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"
+}