From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21058 invoked by alias); 28 Jan 2008 18:11:56 -0000 Received: (qmail 20970 invoked by uid 22791); 28 Jan 2008 18:11:39 -0000 X-Spam-Check-By: sourceware.org Received: from smtp-out.google.com (HELO smtp-out.google.com) (216.239.33.17) by sourceware.org (qpsmtpd/0.31) with ESMTP; Mon, 28 Jan 2008 18:11:04 +0000 Received: from zps75.corp.google.com (zps75.corp.google.com [172.25.146.75]) by smtp-out.google.com with ESMTP id m0SIAjxk021221 for ; Mon, 28 Jan 2008 18:10:46 GMT Received: from wa-out-1112.google.com (wafj32.prod.google.com [10.114.186.32]) by zps75.corp.google.com with ESMTP id m0SIASnU021170 for ; Mon, 28 Jan 2008 10:10:44 -0800 Received: by wa-out-1112.google.com with SMTP id j32so2888813waf.19 for ; Mon, 28 Jan 2008 10:10:44 -0800 (PST) Received: by 10.114.204.7 with SMTP id b7mr3596301wag.124.1201543844682; Mon, 28 Jan 2008 10:10:44 -0800 (PST) Received: by 10.115.107.7 with HTTP; Mon, 28 Jan 2008 10:10:44 -0800 (PST) Message-ID: Date: Mon, 28 Jan 2008 18:15:00 -0000 From: "Doug Evans" To: gdb-patches@sourceware.org, pkoning@equallogic.com Subject: Re: RFC: new command to search memory In-Reply-To: <20080115221425.AF8591C7245@localhost> MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline References: <20080115221425.AF8591C7245@localhost> X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-01/txt/msg00648.txt.bz2 Ping ... On Jan 15, 2008 2:14 PM, Doug Evans 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 > 0x60104e > 2 patterns found > (gdb) find &mixed, @sizeof(mixed), (char) 'c', (short) 0x1234, (int) 0x87654321 > 0x601054 > 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 > > * 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 . > # > # 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 /* 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 . */ > + > +#include "defs.h" > +#include > +#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 > +0x60104e > +2 patterns found > +(gdb) find &mixed, @@sizeof(mixed), (char) 'c', (short) 0x1234, (int) 0x87654321 > +0x601054 > +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 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 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 > + > +#ifdef HAVE_STRING_H > +#ifndef _GNU_SOURCE > +#define _GNU_SOURCE /* memmem */ > +#endif > +#include > +#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 . > + > + Please email any bugs, comments, and/or additions to this file to: > + bug-gdb@gnu.org */ > + > +#include > +#include > + > +#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 . > + > +# 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}.*${newline}${hex_number}.*${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}.*${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}.*${one_pattern_found}" \ > + "max-count first" > + > +gdb_test "find /b /1 &int8_search_buf\[0\], @sizeof(int8_search_buf), 0x61, 0x61, 0x61" \ > + "${hex_number}.*${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}.*${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}.*${one_pattern_found}" \ > + "find 16-bit pattern" > + > +gdb_test "find &int16_search_buf\[0\], @sizeof(int16_search_buf), (int16_t) 0x1234" \ > + "${hex_number}.*${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}.*${one_pattern_found}" \ > + "find 32-bit pattern" > + > +gdb_test "find /w &int32_search_buf\[0\], @sizeof(int32_search_buf), 0x12345678" \ > + "${hex_number}.*${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}.*${one_pattern_found}" \ > + "find 64-bit pattern" > + > +gdb_test "find /g &int64_search_buf\[0\], @sizeof(int64_search_buf), 0xfedcba9876543210LL" \ > + "${hex_number}.*${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" > +} > > >