This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: RFC: new command to search memory
- From: "Doug Evans" <dje at google dot com>
- To: gdb-patches at sourceware dot org, pkoning at equallogic dot com
- Date: Mon, 28 Jan 2008 10:10:44 -0800
- Subject: Re: RFC: new command to search memory
- References: <20080115221425.AF8591C7245@localhost>
Ping ...
On Jan 15, 2008 2:14 PM, Doug Evans <dje@google.com> wrote:
> ref: http://sourceware.org/ml/gdb/2007-12/msg00161.html
> I had some time so as an exercise I came up with this.
> The basic syntax I came up with is:
>
> find [/size] [/max_count] start_addr, @len, val1 [, val2, ...]
> find [/size] [/max_count] start_addr, end_addr, val1 [, val2, ...]
>
> Examples:
>
> 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);
> }
>
> (gdb) find &hello[0], @sizeof(hello), "hello"
> 0x601048 <hello.1628>
> 0x60104e <hello.1628+6>
> 2 patterns 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
>
> Instead of linking with libiberty I just put a copy of lmemmem.c in
> with gdbserver.
>
> I realize the libiberty mods need to go through gcc-patches.
> I'll forward that part of this patch on if/when appropriate.
>
> 2008-01-15 Doug Evans <dje@sebabeach.org>
>
> * 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 15 Jan 2008 21:53:23 -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 15 Jan 2008 21:53:23 -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 15 Jan 2008 21:53:23 -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 15 Jan 2008 21:53:23 -0000
> @@ -5137,7 +5137,7 @@ if test "x" = "y"; then
>
> for ac_func in asprintf atexit basename bcmp bcopy bsearch bzero calloc clock \
> getcwd getpagesize gettimeofday index insque mkstemps memchr memcmp memcpy \
> - memmove mempcpy memset putenv random rename rindex sigsetmask \
> + memmem memmove mempcpy memset putenv random rename rindex sigsetmask \
> strcasecmp setenv stpcpy stpncpy strchr strdup strncasecmp strndup strrchr strstr \
> strtod strtol strtoul strverscmp tmpnam vasprintf vfprintf vprintf \
> vsprintf waitpid getrusage on_exit psignal strerror strsignal \
> 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 15 Jan 2008 21:53:23 -0000
> @@ -362,7 +362,7 @@ checkfuncs="$checkfuncs getsysinfo table
> if test "x" = "y"; then
> AC_CHECK_FUNCS(asprintf atexit basename bcmp bcopy bsearch bzero calloc clock \
> getcwd getpagesize gettimeofday index insque mkstemps memchr memcmp memcpy \
> - memmove mempcpy memset putenv random rename rindex sigsetmask \
> + memmem memmove mempcpy memset putenv random rename rindex sigsetmask \
> strcasecmp setenv stpcpy stpncpy strchr strdup strncasecmp strndup strrchr strstr \
> strtod strtol strtoul strverscmp tmpnam vasprintf vfprintf vprintf \
> vsprintf waitpid getrusage on_exit psignal strerror strsignal \
> 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 15 Jan 2008 21:53:23 -0000
> @@ -0,0 +1,52 @@
> +/* 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
> +#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.974
> diff -u -p -u -p -r1.974 Makefile.in
> --- ./gdb/Makefile.in 11 Jan 2008 13:34:14 -0000 1.974
> +++ ./gdb/Makefile.in 15 Jan 2008 21:53:23 -0000
> @@ -599,9 +599,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 \
> @@ -1042,6 +1041,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 \
> @@ -2097,6 +2097,7 @@ 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) $(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.251
> diff -u -p -u -p -r1.251 NEWS
> --- ./gdb/NEWS 5 Jan 2008 21:50:43 -0000 1.251
> +++ ./gdb/NEWS 15 Jan 2008 21:53:23 -0000
> @@ -3,6 +3,17 @@
>
> *** Changes since GDB 6.7
>
> +* New command
> +
> +find [/size-char] [/max-count] start-address, end-address|@search-space-size,
> + val1 [, val2, ...]
> + Search memory for a sequence of bytes.
> +
> +* New remote packet
> +
> +qSearch:memory:
> + Search memory for a sequence of bytes.
> +
> * Change in command line behavior -- corefiles vs. process ids.
>
> When the '-p NUMBER' or '--pid NUMBER' options are used, and
> 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 15 Jan 2008 21:53:24 -0000
> @@ -0,0 +1,401 @@
> +/* 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. */
> +
> + while (*s == '/')
> + {
> + ++s;
> +
> + if (isdigit (*s))
> + {
> + /* copied from decode_format */
> + max_count = atoi (s);
> + while (isdigit (*s))
> + ++s;
> + if (*s != '\0' && !isspace (*s))
> + error (_("invalid max-count"));
> + }
> + else
> + {
> + switch (*s)
> + {
> + case 'b':
> + case 'h':
> + case 'w':
> + case 'g':
> + size = *s++;
> + break;
> + default:
> + error (_("invalid size granularity"));
> + }
> + if (*s != '\0' && !isspace (*s))
> + 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.275
> diff -u -p -u -p -r1.275 remote.c
> --- ./gdb/remote.c 1 Jan 2008 22:53:12 -0000 1.275
> +++ ./gdb/remote.c 15 Jan 2008 21:53:24 -0000
> @@ -920,6 +920,7 @@ enum {
> PACKET_qGetTLSAddr,
> PACKET_qSupported,
> PACKET_QPassSignals,
> + PACKET_qSearch_memory,
> PACKET_MAX
> };
>
> @@ -2402,6 +2403,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
> @@ -5909,6 +5912,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 (¤t_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)
> @@ -6964,6 +7042,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
> @@ -7097,6 +7176,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
> @@ -7355,6 +7435,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.153
> diff -u -p -u -p -r1.153 target.c
> --- ./gdb/target.c 11 Jan 2008 00:12:43 -0000 1.153
> +++ ./gdb/target.c 15 Jan 2008 21:53:24 -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 (¤t_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;
> }
>
>
> 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 15 Jan 2008 21:53:24 -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.457
> diff -u -p -u -p -r1.457 gdb.texinfo
> --- ./gdb/doc/gdb.texinfo 12 Jan 2008 08:36:09 -0000 1.457
> +++ ./gdb/doc/gdb.texinfo 15 Jan 2008 21:53:25 -0000
> @@ -5491,6 +5491,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
> @@ -7469,6 +7470,73 @@ 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{size}@r{]} @r{[}/@var{max_count}@r{]} @var{start_addr}, @@@var{len}, @var{val1} @r{[}, @var{val2}, ...@r{]}
> +@itemx find @r{[}/@var{size}@r{]} @r{[}/@var{max_count}@r{]} @var{start_addr}, @var{end_addr}, @var{val1} @r{[}, @var{val2}, ...@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.
> +
> +@var{size} specifies how to interpret the search pattern and is
> +'b' for 8-bit values, 'h' for 16-bit values, 'w' for 32-bit values,
> +and 'g' for 64-bit values. If the value size is not explicitly
> +specified, it is taken from the value's type. The latter is useful
> +when one wants to specify the search pattern as a mixture of types.
> +
> +@var{max_count} specifies the maximum number of finds to print.
> +The default is to print all finds.
> +
> +@var{size} and @var{max_count} may be specified in either order.
> +
> +Strings may be specified for search values, quote them normally.
> +The string value is copied into the search pattern byte by byte,
> +regardless of the endianness of the target and the size specification.
> +@end table
> +
> +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{$numfound}.
> +A count of the number of matches is stored in @samp{$_}.
> +
> +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 &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
> @@ -13205,6 +13273,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
> @@ -24151,6 +24223,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 search memory
> +@cindex @samp{qSearch:memory} packet
> +@anchor{qSearch memory}
> +Search LENGTH bytes at ADDRESS for SEARCH-PATTERN.
> +ADDRESS and LENGTH are encoded in hex.
> +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 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
> @@ -24292,6 +24384,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:
> @@ -24336,6 +24433,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.52
> diff -u -p -u -p -r1.52 Makefile.in
> --- ./gdb/gdbserver/Makefile.in 1 Jan 2008 22:53:14 -0000 1.52
> +++ ./gdb/gdbserver/Makefile.in 15 Jan 2008 21:53:25 -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 \
> @@ -139,7 +140,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@
> @@ -291,6 +292,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 15 Jan 2008 21:53:25 -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.31
> diff -u -p -u -p -r1.31 configure
> --- ./gdb/gdbserver/configure 16 Dec 2007 21:50:05 -0000 1.31
> +++ ./gdb/gdbserver/configure 15 Jan 2008 21:53:25 -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.19
> diff -u -p -u -p -r1.19 configure.ac
> --- ./gdb/gdbserver/configure.ac 16 Dec 2007 21:50:05 -0000 1.19
> +++ ./gdb/gdbserver/configure.ac 15 Jan 2008 21:53:25 -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 15 Jan 2008 21:53:25 -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.53
> diff -u -p -u -p -r1.53 remote-utils.c
> --- ./gdb/gdbserver/remote-utils.c 1 Jan 2008 22:53:14 -0000 1.53
> +++ ./gdb/gdbserver/remote-utils.c 15 Jan 2008 21:53:25 -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.62
> diff -u -p -u -p -r1.62 server.c
> --- ./gdb/gdbserver/server.c 1 Jan 2008 22:53:14 -0000 1.62
> +++ ./gdb/gdbserver/server.c 15 Jan 2008 21:53:25 -0000
> @@ -254,6 +254,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);
> +}
> +
> /* Handle all of the extended 'q' packets. */
> void
> handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> @@ -533,6 +684,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+");
>
> @@ -653,6 +808,12 @@ handle_query (char *own_buf, int packet_
> return;
> }
>
> + if (strncmp ("qSearch:memory:", own_buf, sizeof ("qSearch:memory:") - 1) == 0)
> + {
> + 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.39
> diff -u -p -u -p -r1.39 server.h
> --- ./gdb/gdbserver/server.h 1 Jan 2008 22:53:14 -0000 1.39
> +++ ./gdb/gdbserver/server.h 15 Jan 2008 21:53:25 -0000
> @@ -192,6 +192,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);
> @@ -218,6 +222,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 15 Jan 2008 21:53: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: 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 15 Jan 2008 21:53:25 -0000
> @@ -0,0 +1,165 @@
> +# 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 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 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 first"
> +
> +gdb_test "print \$numfound" \
> + "${history_prefix}1" \
> + "numfound"
> +
> +# Test max-count with size-char.
> +# They can be specified in either order.
> +
> +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}" \
> + "max-count first"
> +
> +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}" \
> + "max-count second"
> +
> +# 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\], @search_buf_size, (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"
> +}
>
>
>