From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15590 invoked by alias); 14 Feb 2008 02:20:06 -0000 Received: (qmail 15497 invoked by uid 22791); 14 Feb 2008 02:19:57 -0000 X-Spam-Check-By: sourceware.org Received: from smtp-out.google.com (HELO smtp-out.google.com) (216.239.45.13) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 14 Feb 2008 02:19:26 +0000 Received: from zps35.corp.google.com (zps35.corp.google.com [172.25.146.35]) by smtp-out.google.com with ESMTP id m1E2JGsY011990; Wed, 13 Feb 2008 18:19:17 -0800 Received: from localhost (ruffy.corp.google.com [172.18.118.116]) by zps35.corp.google.com with ESMTP id m1E2JGFG031168; Wed, 13 Feb 2008 18:19:16 -0800 Received: by localhost (Postfix, from userid 67641) id F3FE51C72F0; Wed, 13 Feb 2008 18:19:15 -0800 (PST) To: gdb-patches@sourceware.org, pkoning@equallogic.com Subject: [RFA] new command to search memory Message-Id: <20080214021915.F3FE51C72F0@localhost> Date: Thu, 14 Feb 2008 02:20:00 -0000 From: dje@google.com (Doug Evans) 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-02/txt/msg00223.txt.bz2 I didn't get a response to the non-doc portions of this patch. [Eli, thanks for the doc review, I made the suggested changes, though I went a different route in shortening the length of the line specifying the find command syntax. The end result is still rather short.] The libiberty patches need to go through gcc-patches, but I'm guessing there's no point in bothering them until this patch is approved at least in principal. What can I do to help make progress here? 2008-02-13 Doug Evans * include/libiberty.h: (lmemmem): Declare. * libiberty/Makefile.in (CFILES): Add lmemmem.c. (REQUIRED_OFILES): Add lmemmem.o. (lmemmem.o): New rule. * libiberty/configure.ac (funcs): Add memmem. (AC_CHECK_FUNCS): Add memmem. * libiberty/config.in: Regenerate. * libiberty/configure: Regenerate. * libiberty/lmemmem.c: New file. New "find" command. * gdb/Makefile.in (SFILES): Add findcmd.c. (COMMON_OBJS): Add findcmd.o. (findcmd.o): New rule. * gdb/findcmd.c: New file. * gdb/target.h (target_ops): New member to_search_memory. (simple_search_memory): Declare. (target_search_memory): Declare. * gdb/target.c (simple_search_memory): New fn. (default_search_memory): New fn. (debug_to_search_memory): New fn. (target_search_memory): New fn. (update_current_target): Set to_search_memory. (setup_target_debug): Set to_search_memory. * gdb/remote.c (PACKET_qSearch_memory): New packet kind. (remote_protocol_features): Add qSearch:memory. (remote_search_memory): New fn. (init_remote_ops): Init to_search_memory. (init_extended_remote_ops): Ditto. (_initialize_remote): Add qSearch:memory packet config command. * gdbserver/configure.ac: Check for memmem. * gdbserver/configure: Regenerate. * gdbserver/config.in: Regenerate. * gdbserver/Makefile.in (SFILES): Add lmemmem.c. (OBS): Add lmemmem.o. (lmemmem.o): New rule. * gdbserver/server.h (decode_search_memory_packet): Declare. (lmemmem): Declare. * gdbserver/remote-utils.c (decode_search_memory_packet): New fn. * gdbserver/server.c (handle_search_memory_1): New fn. (handle_search_memory): New fn. (handle_query): Process qSearch:memory packets. * gdbserver/lmemmem.c: New file. * doc/gdb.texinfo: Document "find" command, qSearch:memory packet. * testsuite/gdb.base/find.exp: New file. * testsuite/gdb.base/find.c: New file. Index: include/libiberty.h =================================================================== RCS file: /cvs/src/src/include/libiberty.h,v retrieving revision 1.57 diff -u -p -u -p -r1.57 libiberty.h --- include/libiberty.h 6 Sep 2007 17:22:36 -0000 1.57 +++ include/libiberty.h 14 Feb 2008 02:03:40 -0000 @@ -1,6 +1,6 @@ /* Function declarations for libiberty. - Copyright 2001, 2002, 2005, 2007 Free Software Foundation, Inc. + Copyright 2001, 2002, 2005, 2007, 2008 Free Software Foundation, Inc. Note - certain prototypes declared in this header file are for functions whoes implementation copyright does not belong to the @@ -166,6 +166,10 @@ extern char *libiberty_concat_ptr; (libiberty_concat_ptr = (char *) alloca (concat_length ACONCAT_PARAMS + 1), \ concat_copy2 ACONCAT_PARAMS) +/* A well-defined memmem () that is always compiled in. */ + +extern void * lmemmem (const void *, size_t, const void *, size_t); + /* Check whether two file descriptors refer to the same file. */ extern int fdmatch (int fd1, int fd2); Index: libiberty/Makefile.in =================================================================== RCS file: /cvs/src/src/libiberty/Makefile.in,v retrieving revision 1.89 diff -u -p -u -p -r1.89 Makefile.in --- libiberty/Makefile.in 25 Jul 2007 06:36:27 -0000 1.89 +++ libiberty/Makefile.in 14 Feb 2008 02:03:40 -0000 @@ -2,7 +2,7 @@ # Originally written by K. Richard Pixley . # # Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, -# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software +# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software # Foundation # # This file is part of the libiberty library. @@ -133,6 +133,7 @@ CFILES = alloca.c argv.c asprintf.c atex hashtab.c hex.c \ index.c insque.c \ lbasename.c \ + lmemmem.c \ lrealpath.c \ make-relative-prefix.c \ make-temp-file.c md5.c memchr.c memcmp.c memcpy.c memmove.c \ @@ -164,7 +165,7 @@ REQUIRED_OFILES = ./regex.o ./cplus-dem. ./fnmatch.o ./fopen_unlocked.o \ ./getopt.o ./getopt1.o ./getpwd.o ./getruntime.o \ ./hashtab.o ./hex.o \ - ./lbasename.o ./lrealpath.o \ + ./lbasename.o ./lmemmem.o ./lrealpath.o \ ./make-relative-prefix.o ./make-temp-file.o \ ./objalloc.o ./obstack.o \ ./partition.o ./pexecute.o ./physmem.o \ @@ -748,6 +749,12 @@ $(CONFIGURED_OFILES): stamp-picdir else true; fi $(COMPILE.c) $(srcdir)/lbasename.c $(OUTPUT_OPTION) +./lmemmem.o: $(srcdir)/lmemmem.c stamp-h + if [ x"$(PICFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(srcdir)/lmemmem.c -o pic/$@; \ + else true; fi + $(COMPILE.c) $(srcdir)/lmemmem.c $(OUTPUT_OPTION) + ./lrealpath.o: $(srcdir)/lrealpath.c stamp-h $(INCDIR)/ansidecl.h \ $(INCDIR)/libiberty.h if [ x"$(PICFLAG)" != x ]; then \ Index: libiberty/config.in =================================================================== RCS file: /cvs/src/src/libiberty/config.in,v retrieving revision 1.38 diff -u -p -u -p -r1.38 config.in --- libiberty/config.in 22 Jul 2005 03:16:32 -0000 1.38 +++ libiberty/config.in 14 Feb 2008 02:03:40 -0000 @@ -139,6 +139,9 @@ /* Define to 1 if you have the `memcpy' function. */ #undef HAVE_MEMCPY +/* Define to 1 if you have the `memmem' function. */ +#undef HAVE_MEMMEM + /* Define to 1 if you have the `memmove' function. */ #undef HAVE_MEMMOVE Index: libiberty/configure =================================================================== RCS file: /cvs/src/src/libiberty/configure,v retrieving revision 1.89 diff -u -p -u -p -r1.89 configure --- libiberty/configure 17 Jul 2007 18:05:02 -0000 1.89 +++ libiberty/configure 14 Feb 2008 02:03:40 -0000 @@ -5062,6 +5062,7 @@ vars="sys_errlist sys_nerr sys_siglist" checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday" checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp" checkfuncs="$checkfuncs getsysinfo table sysctl wait3 wait4 __fsetlocking" +checkfuncs="$checkfuncs memmem" # These are neither executed nor required, but they help keep # autoheader happy without adding a bunch of text to acconfig.h. Index: libiberty/configure.ac =================================================================== RCS file: /cvs/src/src/libiberty/configure.ac,v retrieving revision 1.37 diff -u -p -u -p -r1.37 configure.ac --- libiberty/configure.ac 17 Jul 2007 18:05:02 -0000 1.37 +++ libiberty/configure.ac 14 Feb 2008 02:03:41 -0000 @@ -356,6 +356,7 @@ vars="sys_errlist sys_nerr sys_siglist" checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday" checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp" checkfuncs="$checkfuncs getsysinfo table sysctl wait3 wait4 __fsetlocking" +checkfuncs="$checkfuncs memmem" # These are neither executed nor required, but they help keep # autoheader happy without adding a bunch of text to acconfig.h. Index: libiberty/lmemmem.c =================================================================== RCS file: libiberty/lmemmem.c diff -N libiberty/lmemmem.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ libiberty/lmemmem.c 14 Feb 2008 02:03:41 -0000 @@ -0,0 +1,58 @@ +/* lmemmem -- search for a sequence of bytes + This function is in the public domain. */ + +/* + +@deftypefn Supplemental void *lmemmem (const void *@var{haystack}, size_t @var{haystacklen}, + const void *@var{needle}, size_t @var{needlelen}) + +Search the area of memory at @var{haystack} for the bytes at @var{needle}, +and return the first occurrence. +Returns a pointer to the beginning of the string or NULL if not found. + +@end deftypefn + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_STRING_H +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* memmem */ +#endif +#include +#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.982 diff -u -p -u -p -r1.982 Makefile.in --- gdb/Makefile.in 11 Feb 2008 21:58:41 -0000 1.982 +++ gdb/Makefile.in 14 Feb 2008 02:03:41 -0000 @@ -600,9 +600,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \ dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \ elfread.c environ.c eval.c event-loop.c event-top.c expprint.c \ - f-exp.y f-lang.c f-typeprint.c f-valprint.c findvar.c frame.c \ - frame-base.c \ - frame-unwind.c \ + f-exp.y f-lang.c f-typeprint.c f-valprint.c findvar.c findcmd.c \ + frame.c frame-base.c frame-unwind.c \ gdbarch.c arch-utils.c gdbtypes.c gnu-v2-abi.c gnu-v3-abi.c \ inf-loop.c \ infcall.c \ @@ -1043,6 +1042,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $ event-loop.o event-top.o inf-loop.o completer.o \ gdbarch.o arch-utils.o gdbtypes.o osabi.o copying.o \ memattr.o mem-break.o target.o parse.o language.o buildsym.o \ + findcmd.o \ std-regs.o \ signals.o \ gdb-events.o \ @@ -2101,6 +2101,8 @@ fbsd-nat.o: fbsd-nat.c $(defs_h) $(gdbco f-exp.o: f-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \ $(parser_defs_h) $(language_h) $(f_lang_h) $(bfd_h) $(symfile_h) \ $(objfiles_h) $(block_h) +findcmd.o: findcmd.c $(defs_h) $(gdb_string_h) $(gdbcmd_h) $(value_h) \ + $(target_h) findvar.o: findvar.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(frame_h) \ $(value_h) $(gdbcore_h) $(inferior_h) $(target_h) $(gdb_string_h) \ $(gdb_assert_h) $(floatformat_h) $(symfile_h) $(regcache_h) \ Index: gdb/NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.257 diff -u -p -u -p -r1.257 NEWS --- gdb/NEWS 11 Feb 2008 21:58:41 -0000 1.257 +++ gdb/NEWS 14 Feb 2008 02:03:41 -0000 @@ -64,6 +64,10 @@ stored in two consecutive float register * New commands +find [/size-char] [/max-count] start-address, end-address|@search-space-size, + val1 [, val2, ...] + Search memory for a sequence of bytes. + set print frame-arguments (all|scalars|none) show print frame-arguments The value of this variable can be changed to control which argument @@ -83,6 +87,9 @@ remote delete * New remote packets +qSearch:memory: + Search memory for a sequence of bytes. + vFile:open: vFile:close: vFile:pread: Index: gdb/findcmd.c =================================================================== RCS file: gdb/findcmd.c diff -N gdb/findcmd.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ gdb/findcmd.c 14 Feb 2008 02:03:41 -0000 @@ -0,0 +1,399 @@ +/* The find command. + + Copyright (C) 2008 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#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. + They may be specified in either order, together or separately. */ + + while (*s == '/') + { + ++s; + + while (*s != '\0' && *s != '/' && !isspace (*s)) + { + if (isdigit (*s)) + { + max_count = atoi (s); + while (isdigit (*s)) + ++s; + continue; + } + + switch (*s) + { + case 'b': + case 'h': + case 'w': + case 'g': + size = *s++; + break; + default: + error (_("invalid size granularity")); + } + } + + while (isspace (*s)) + ++s; + } + + /* Get the search range. */ + + v = parse_to_comma_and_eval (&s); + start_addr = value_as_address (v); + + if (*s == ',') + ++s; + while (isspace (*s)) + ++s; + + if (*s == '@') + { + LONGEST len; + ++s; + v = parse_to_comma_and_eval (&s); + len = value_as_long (v); + if (len == 0) + { + printf_filtered (_("empty search range\n")); + return; + } + if (len < 0) + error (_("invalid length")); + /* Watch for overflows. */ + if (len > CORE_ADDR_MAX + || (start_addr + len - 1) < start_addr) + error (_("search space too large")); + search_space_len = len; + } + else + { + CORE_ADDR end_addr; + v = parse_to_comma_and_eval (&s); + end_addr = value_as_address (v); + if (start_addr > end_addr) + error (_("invalid search space, end preceeds start")); + search_space_len = end_addr - start_addr + 1; + /* We don't support searching all of memory + (i.e. start=0, end = 0xff..ff). + Bail to avoid overflows later on. */ + if (search_space_len == 0) + error (_("overflow in address range computation, choose smaller range")); + } + + if (*s == ',') + ++s; + + /* Fetch the search string. */ + + while (*s != '\0') + { + LONGEST x; + + /* If we see a string, parse it ourselves rather than the normal + handling of downloading it to target memory. */ + + while (isspace (*s)) + ++s; + + if (*s == '"') + { + char *str; + int len = parse_search_string (&s, &str); + + if ((pattern_buf_end - pattern_buf + len) + > pattern_buf_size) + { + size_t current_offset = pattern_buf_end - pattern_buf; + pattern_buf_size += len * 2; /* kiss */ + pattern_buf = xrealloc (pattern_buf, pattern_buf_size); + pattern_buf_end = pattern_buf + current_offset; + } + + memcpy (pattern_buf_end, str, len); + pattern_buf_end += len; + + free (str); + + /* Leave the pointer at the next comma, like the `else' clause + will do. */ + while (isspace (*s)) + ++s; + + if (*s != '\0' && *s != ',') + error ("comma expected between expressions"); + } + else /* Not a string, parse the expression the normal way. */ + { + int val_bytes; + + /* ??? Need to prevent (char*) "foo", it downloads string to target + and we don't want that. */ + v = parse_to_comma_and_eval (&s); + val_bytes = TYPE_LENGTH (value_type (v)); + + /* Keep it simple and assume size == 'g' when watching for when we + need to grow the pattern buf. */ + if ((pattern_buf_end - pattern_buf + max (val_bytes, sizeof (int64_t))) + > pattern_buf_size) + { + size_t current_offset = pattern_buf_end - pattern_buf; + pattern_buf_size *= 2; + pattern_buf = xrealloc (pattern_buf, pattern_buf_size); + pattern_buf_end = pattern_buf + current_offset; + } + + if (size != '\0') + { + x = value_as_long (v); + switch (size) + { + case 'b': + *pattern_buf_end++ = x; + break; + case 'h': + put_bits (x, pattern_buf_end, 16, big_p); + pattern_buf_end += sizeof (int16_t); + break; + case 'w': + put_bits (x, pattern_buf_end, 32, big_p); + pattern_buf_end += sizeof (int32_t); + break; + case 'g': + put_bits (x, pattern_buf_end, 64, big_p); + pattern_buf_end += sizeof (int64_t); + break; + } + } + else + { + memcpy (pattern_buf_end, value_contents_raw (v), val_bytes); + pattern_buf_end += val_bytes; + } + } + + if (*s == ',') + ++s; + while (isspace (*s)) + ++s; + } + + if (pattern_buf_end == pattern_buf) + error (_("missing search pattern")); + + pattern_len = pattern_buf_end - pattern_buf; + + if (search_space_len < pattern_len) + error (_("search space too small to contain pattern")); + + /* Perform the search. */ + + found_count = 0; + last_found_addr = 0; + + while (search_space_len >= pattern_len + && found_count < max_count) + { + /* offset from start of this iteration to the next iteration */ + ULONGEST next_iter_incr; + CORE_ADDR found_addr; + int found = target_search_memory (start_addr, search_space_len, + pattern_buf, pattern_len, &found_addr); + + if (found <= 0) + break; + + print_address (found_addr, gdb_stdout); + printf_filtered ("\n"); + ++found_count; + last_found_addr = found_addr; + + /* Begin next iteration at one byte past this match. */ + next_iter_incr = (found_addr - start_addr) + 1; + + /* For robustness, we don't let search_space_len go -ve here. */ + if (search_space_len >= next_iter_incr) + search_space_len -= next_iter_incr; + else + search_space_len = 0; + start_addr += next_iter_incr; + } + + /* Record and print the results. */ + + set_internalvar (lookup_internalvar ("numfound"), + value_from_longest (builtin_type_int, + (LONGEST) found_count)); + if (found_count > 0) + { + set_internalvar (lookup_internalvar ("_"), + value_from_pointer (builtin_type_void_data_ptr, + last_found_addr)); + } + + if (found_count == 0) + printf_filtered ("pattern not found\n"); + else + printf_filtered ("%d pattern%s found\n", found_count, + found_count > 1 ? "s" : ""); + + do_cleanups (old_cleanups); +} + +void +_initialize_mem_search (void) +{ + add_cmd ("find", class_vars, find_command, _("\ +Search memory for a sequence of bytes.\n\ +Usage:\n\ +find [/size-char] [/max-count] start-address, end-address, expr1 [, expr2 ...]\n\ +find [/size-char] [/max-count] start-address, @length, expr1 [, expr2 ...]\n\ +size-char is one of b,h,w,g for 8,16,32,64 bit values respectively,\n\ +and if not specified the size is taken from the type of the expression.\n\ +\n\ +The address of the last match is stored as the value of \"$_\".\n\ +Convenience variable \"$numfound\" is set to the number of matches."), + &cmdlist); +} Index: gdb/remote.c =================================================================== RCS file: /cvs/src/src/gdb/remote.c,v retrieving revision 1.278 diff -u -p -u -p -r1.278 remote.c --- gdb/remote.c 30 Jan 2008 00:51:49 -0000 1.278 +++ gdb/remote.c 14 Feb 2008 02:03:41 -0000 @@ -933,6 +933,7 @@ enum { PACKET_qGetTLSAddr, PACKET_qSupported, PACKET_QPassSignals, + PACKET_qSearch_memory, PACKET_vAttach, PACKET_vRun, PACKET_MAX @@ -2459,6 +2460,8 @@ static struct protocol_feature remote_pr PACKET_qXfer_spu_write }, { "QPassSignals", PACKET_DISABLE, remote_supported_packet, PACKET_QPassSignals }, + { "qSearch:memory", PACKET_DISABLE, remote_supported_packet, + PACKET_qSearch_memory }, }; static void @@ -6198,6 +6201,81 @@ remote_xfer_partial (struct target_ops * return strlen ((char *) readbuf); } +static int +remote_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len, + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp) +{ + struct remote_state *rs = get_remote_state (); + int max_size = get_memory_write_packet_size (); + struct packet_config *packet = + &remote_protocol_packets[PACKET_qSearch_memory]; + /* number of packet bytes used to encode the pattern, + this could be more than PATTERN_LEN due to escape characters */ + int escaped_pattern_len; + /* amount of pattern that was encodable in the packet */ + int used_pattern_len; + int i; + int found; + ULONGEST found_addr; + + /* Don't go to the target if we don't have to. + This is done after checking packet->support to avoid the possibility that + a success for this edge case means the facility works in general. */ + if (pattern_len > search_space_len) + return 0; + if (pattern_len == 0) + { + *found_addrp = start_addr; + return 1; + } + + if (packet->support == PACKET_DISABLE) + { + /* Target doesn't provided special support, fall back and use the + standard support (copy memory and do the search here). */ + return simple_search_memory (¤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) @@ -7253,6 +7331,7 @@ Specify the serial device it is connecte remote_ops.to_flash_erase = remote_flash_erase; remote_ops.to_flash_done = remote_flash_done; remote_ops.to_read_description = remote_read_description; + remote_ops.to_search_memory = remote_search_memory; } /* Set up the extended remote vector by making a copy of the standard @@ -7388,6 +7467,7 @@ Specify the serial device it is connecte remote_async_ops.to_flash_erase = remote_flash_erase; remote_async_ops.to_flash_done = remote_flash_done; remote_async_ops.to_read_description = remote_read_description; + remote_async_ops.to_search_memory = remote_search_memory; } /* Set up the async extended remote vector by making a copy of the standard @@ -7648,6 +7728,9 @@ Show the maximum size of the address (in add_packet_config_cmd (&remote_protocol_packets[PACKET_qSupported], "qSupported", "supported-packets", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qSearch_memory], + "qSearch:memory", "search-memory", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_open], "vFile:open", "hostio-open", 0); Index: gdb/target.c =================================================================== RCS file: /cvs/src/src/gdb/target.c,v retrieving revision 1.154 diff -u -p -u -p -r1.154 target.c --- gdb/target.c 25 Jan 2008 00:09:49 -0000 1.154 +++ gdb/target.c 14 Feb 2008 02:03:41 -0000 @@ -88,6 +88,11 @@ static LONGEST target_xfer_partial (stru void *readbuf, const void *writebuf, ULONGEST offset, LONGEST len); +static int +default_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len, + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp); + static void init_dummy_target (void); static struct target_ops debug_target; @@ -160,6 +165,12 @@ static int debug_to_thread_alive (ptid_t static void debug_to_stop (void); +static int debug_to_search_memory (CORE_ADDR start_addr, + ULONGEST search_space_len, + const gdb_byte *pattern, + ULONGEST pattern_len, + CORE_ADDR *found_addrp); + /* NOTE: cagney/2004-09-29: Many targets reference this variable in wierd and mysterious ways. Putting the variable here lets those wierd and mysterious ways keep building while they are being @@ -464,6 +475,7 @@ update_current_target (void) INHERIT (to_make_corefile_notes, t); INHERIT (to_get_thread_local_address, t); /* Do not inherit to_read_description. */ + INHERIT (to_search_memory, t); INHERIT (to_magic, t); /* Do not inherit to_memory_map. */ /* Do not inherit to_flash_erase. */ @@ -636,6 +648,8 @@ update_current_target (void) (void (*) (void (*) (enum inferior_event_type, void*), void*)) tcomplain); current_target.to_read_description = NULL; + de_fault (to_search_memory, default_search_memory); + #undef de_fault /* Finally, position the target-stack beneath the squashed @@ -1723,6 +1737,139 @@ target_read_description (struct target_o return NULL; } +/* Utility to implement a basic search of memory. */ + +int +simple_search_memory (struct target_ops* ops, + CORE_ADDR start_addr, ULONGEST search_space_len, + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp) +{ + /* ??? tunable? + NOTE: also defined in find.c testcase. */ +#define SEARCH_CHUNK_SIZE 16000 + const unsigned chunk_size = SEARCH_CHUNK_SIZE; + /* Buffer to hold memory contents for searching. */ + gdb_byte *search_buf; + unsigned search_buf_size; + struct cleanup *old_cleanups; + + search_buf_size = chunk_size + pattern_len - 1; + + /* No point in trying to allocate a buffer larger than the search space. */ + if (search_space_len < search_buf_size) + search_buf_size = search_space_len; + + search_buf = malloc (search_buf_size); + if (search_buf == NULL) + error (_("unable to allocate memory to perform the search")); + old_cleanups = make_cleanup (free_current_contents, &search_buf); + + /* Prime the search buffer. */ + + if (target_read (ops, TARGET_OBJECT_MEMORY, NULL, + search_buf, start_addr, search_buf_size) != search_buf_size) + { + warning (_("unable to access target memory at %s, halting search"), + hex_string (start_addr)); + do_cleanups (old_cleanups); + return -1; + } + + /* Perform the search. + + The loop is kept simple by allocating [N + pattern-length - 1] bytes. + When we've scanned N bytes we copy the trailing bytes to the start and + read in another N bytes. */ + + while (search_space_len >= pattern_len) + { + gdb_byte *found_ptr; + unsigned nr_search_bytes = min (search_space_len, search_buf_size); + + found_ptr = lmemmem (search_buf, nr_search_bytes, + pattern, pattern_len); + + if (found_ptr != NULL) + { + CORE_ADDR found_addr = start_addr + (found_ptr - search_buf); + *found_addrp = found_addr; + do_cleanups (old_cleanups); + return 1; + } + + /* Not found in this chunk, skip to next chunk. */ + + /* Don't let search_space_len wrap here, it's unsigned. */ + if (search_space_len >= chunk_size) + search_space_len -= chunk_size; + else + search_space_len = 0; + + if (search_space_len >= pattern_len) + { + unsigned keep_len = search_buf_size - chunk_size; + CORE_ADDR read_addr = start_addr + keep_len; + int nr_to_read; + + /* Copy the trailing part of the previous iteration to the front + of the buffer for the next iteration. */ + gdb_assert (keep_len == pattern_len - 1); + memcpy (search_buf, search_buf + chunk_size, keep_len); + + nr_to_read = min (search_space_len - keep_len, chunk_size); + + if (target_read (ops, TARGET_OBJECT_MEMORY, NULL, + search_buf + keep_len, read_addr, + nr_to_read) != nr_to_read) + { + warning (_("unable to access target memory at %s, halting search"), + hex_string (read_addr)); + do_cleanups (old_cleanups); + return -1; + } + + start_addr += chunk_size; + } + } + + /* Not found. */ + + do_cleanups (old_cleanups); + return 0; +} + +/* The default implementation of to_search_memory. */ + +static int +default_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len, + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp) +{ + return simple_search_memory (¤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 14 Feb 2008 02:03:42 -0000 @@ -500,6 +500,16 @@ struct target_ops was available. */ const struct target_desc *(*to_read_description) (struct target_ops *ops); + /* Search SEARCH_SPACE_LEN bytes beginning at START_ADDR for the + sequence of bytes in PATTERN with length PATTERN_LEN. + + The result is 1 if found, 0 if not found, and -1 if there was an error + requiring halting of the search (e.g. memory read error). + If the pattern is found the address is recorded in FOUND_ADDRP. */ + int (*to_search_memory) (CORE_ADDR start_addr, ULONGEST search_space_len, + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp); + int to_magic; /* Need sub-structure for target machine related rather than comm related? */ @@ -1107,6 +1117,19 @@ extern int target_stopped_data_address_p extern const struct target_desc *target_read_description (struct target_ops *); +/* Utility implementation of searching memory. */ +extern int +simple_search_memory (struct target_ops* ops, + CORE_ADDR start_addr, ULONGEST search_space_len, + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp); + +/* Main entry point for searching memory. */ +extern int +target_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len, + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp); + /* Command logging facility. */ #define target_log_command(p) \ Index: gdb/doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.467 diff -u -p -u -p -r1.467 gdb.texinfo --- gdb/doc/gdb.texinfo 3 Feb 2008 18:55:27 -0000 1.467 +++ gdb/doc/gdb.texinfo 14 Feb 2008 02:03:43 -0000 @@ -5543,6 +5543,7 @@ Table}. * Character Sets:: Debugging programs that use a different character set than GDB does * Caching Remote Data:: Data caching for remote targets +* Searching Memory:: Searching memory for a sequence of bytes @end menu @node Expressions @@ -7521,6 +7522,90 @@ state (dirty, bad, ok, etc.). This comm the data cache operation. @end table +@node Searching Memory +@section Search Memory +@cindex searching memory + +Memory can be searched for a particular sequence of bytes with the +@code{find} command. + +@table @code +@kindex find +@item find @r{[}/@var{sn}@r{]} @var{start_addr}, @@@var{len}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]} +@itemx find @r{[}/@var{sn}@r{]} @var{start_addr}, @var{end_addr}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]} +Search memory for the sequence of bytes specified by @var{val1}, @var{val2}, +etc. The search begins at address @var{start_addr} and continues for either +@var{len} bytes or through to @var{end_addr} inclusive. +@end table + +@var{s} and @var{n} are optional parameters. +They may be specified in either order, apart or together. + +@table @r +@item @var{s}, search query size +The size of each search query value. + +@table @code +@item b +Bytes. +@item h +Halfwords (two bytes). +@item w +Words (four bytes). +@item g +Giant words (eight bytes). +@end table + +If the value size is not specified, it is taken from the +value's type. This is useful when one wants to specify the search +pattern as a mixture of types. + +@item @var{n}, maximum number of finds +The maximum number of finds to print. The default is to print all finds. +@end table + +Strings may be specified for search values, quote them with double-quotes +(@code{"}). The string value is copied into the search pattern byte by byte, +regardless of the endianness of the target and the size specification. + +The address of each match found is printed as well as a count of the +number of matches found. + +The address of the last value found is stored in convenience variable +@samp{$_}. +A count of the number of matches is stored in @samp{$numfound}. + +For example, if stopped at the printf in this function + +@smallexample +void +hello () +@{ + static char hello[] = "hello-hello"; + static struct @{ char c; short s; int i; @} __attribute__ ((packed)) mixed + = @{ 'c', 0x1234, 0x87654321 @}; + printf ("%s\n", hello); +@} +@end smallexample + +You get during debugging + +@smallexample +(gdb) find &hello[0], @@sizeof(hello), "hello" +0x601048 +0x60104e +2 patterns found +(gdb) find /b1 &hello[0], @@sizeof(hello), 'h', 0x65, 'l' +0x601048 +1 pattern 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 @@ -13332,6 +13417,10 @@ are: @tab @code{qGetTLSAddr} @tab Displaying @code{__thread} variables +@item @code{search-memory} +@tab @code{qSearch:memory} +@tab @code{find} + @item @code{supported-packets} @tab @code{qSupported} @tab Remote communications parameters @@ -24330,6 +24419,26 @@ command by a @samp{,}, not a @samp{:}, c conventions above. Please don't use this packet as a model for new packets.) +@item qSearch:memory:@var{address};@var{length};@var{search-pattern} +@cindex searching memory, in remote debugging +@cindex @samp{qSearch:memory} packet +@anchor{qSearch memory} +Search @var{length} bytes at @var{address} for @var{search-pattern}. +@var{address} and @var{length} are encoded in hex. +@var{search-pattern} is a sequence of bytes, hex encoded. + +Reply: +@table @samp +@item 0 +The pattern was not found. +@item 1,address +The pattern was found at @var{address}. +@item E @var{NN} +A badly formed request or an error was encountered while searching memory. +@item +An empty reply indicates that @samp{qSearch:memory} is not recognized. +@end table + @item qSupported @r{[}:@var{gdbfeature} @r{[};@var{gdbfeature}@r{]}@dots{} @r{]} @cindex supported packets, remote query @cindex features of the remote protocol @@ -24471,6 +24580,11 @@ These are the currently defined stub fea @tab @samp{-} @tab Yes +@item @samp{qSearch:memory} +@tab No +@tab @samp{-} +@tab Yes + @end multitable These are the currently defined stub features, in more detail: @@ -24515,6 +24629,10 @@ The remote stub understands the @samp{qX The remote stub understands the @samp{QPassSignals} packet (@pxref{QPassSignals}). +@item qSearch:memory +The remote stub understands the @samp{qSearch:memory} packet +(@pxref{qSearch memory}). + @end table @item qSymbol:: Index: gdb/gdbserver/Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v retrieving revision 1.54 diff -u -p -u -p -r1.54 Makefile.in --- gdb/gdbserver/Makefile.in 11 Feb 2008 22:00:31 -0000 1.54 +++ gdb/gdbserver/Makefile.in 14 Feb 2008 02:03:43 -0000 @@ -122,6 +122,7 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/ $(srcdir)/thread-db.c $(srcdir)/utils.c \ $(srcdir)/linux-arm-low.c $(srcdir)/linux-cris-low.c \ $(srcdir)/linux-crisv32-low.c $(srcdir)/linux-i386-low.c \ + $(srcdir)/lmemmem.c \ $(srcdir)/i387-fp.c \ $(srcdir)/linux-ia64-low.c $(srcdir)/linux-low.c \ $(srcdir)/linux-m32r-low.c \ @@ -141,7 +142,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPAR OBS = inferiors.o regcache.o remote-utils.o server.o signals.o target.o \ utils.o version.o \ - mem-break.o hostio.o \ + mem-break.o hostio.o lmemmem.o \ $(XML_BUILTIN) \ $(DEPFILES) GDBSERVER_LIBS = @GDBSERVER_LIBS@ @@ -294,6 +295,8 @@ utils.o: utils.c $(server_h) signals.o: ../signals/signals.c $(server_h) $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER +lmemmem.o: lmemmem.c $(server_h) + i387-fp.o: i387-fp.c $(server_h) linux_low_h = $(srcdir)/linux-low.h Index: gdb/gdbserver/config.in =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/config.in,v retrieving revision 1.20 diff -u -p -u -p -r1.20 config.in --- gdb/gdbserver/config.in 16 Dec 2007 21:50:05 -0000 1.20 +++ gdb/gdbserver/config.in 14 Feb 2008 02:03:43 -0000 @@ -41,6 +41,9 @@ /* Define to 1 if you have the 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.32 diff -u -p -u -p -r1.32 configure --- gdb/gdbserver/configure 1 Feb 2008 00:08:25 -0000 1.32 +++ gdb/gdbserver/configure 14 Feb 2008 02:03:43 -0000 @@ -3101,7 +3101,7 @@ done -for ac_func in pread pwrite pread64 +for ac_func in memmem pread pwrite pread64 do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 Index: gdb/gdbserver/configure.ac =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/configure.ac,v retrieving revision 1.20 diff -u -p -u -p -r1.20 configure.ac --- gdb/gdbserver/configure.ac 1 Feb 2008 00:08:25 -0000 1.20 +++ gdb/gdbserver/configure.ac 14 Feb 2008 02:03:43 -0000 @@ -41,7 +41,7 @@ AC_CHECK_HEADERS(sgtty.h termio.h termio errno.h fcntl.h signal.h sys/file.h malloc.h dnl sys/ioctl.h netinet/in.h sys/socket.h netdb.h dnl netinet/tcp.h arpa/inet.h sys/wait.h) -AC_CHECK_FUNCS(pread pwrite pread64) +AC_CHECK_FUNCS(memmem pread pwrite pread64) have_errno=no AC_MSG_CHECKING(for errno) Index: gdb/gdbserver/lmemmem.c =================================================================== RCS file: gdb/gdbserver/lmemmem.c diff -N gdb/gdbserver/lmemmem.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ gdb/gdbserver/lmemmem.c 14 Feb 2008 02:03:43 -0000 @@ -0,0 +1,43 @@ +/* lmemmem -- search for a sequence of bytes + This function is in the public domain. */ + +#include "config.h" +#include + +#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.54 diff -u -p -u -p -r1.54 remote-utils.c --- gdb/gdbserver/remote-utils.c 30 Jan 2008 00:51:50 -0000 1.54 +++ gdb/gdbserver/remote-utils.c 14 Feb 2008 02:03:43 -0000 @@ -1080,6 +1080,24 @@ decode_xfer_write (char *buf, int packet return 0; } +/* Decode the parameters of a qSearch:memory packet. */ + +int +decode_search_memory_packet (const char *buf, int packet_len, + CORE_ADDR *start_addrp, + CORE_ADDR *search_space_lenp, + gdb_byte *pattern, unsigned int *pattern_lenp) +{ + const char *p = buf; + + p = decode_address_to_semicolon (start_addrp, p); + p = decode_address_to_semicolon (search_space_lenp, p); + packet_len -= p - buf; + *pattern_lenp = remote_unescape_input ((const gdb_byte *) p, packet_len, + pattern, packet_len); + return 0; +} + /* Ask GDB for the address of NAME, and return it in ADDRP if found. Returns 1 if the symbol is found, 0 if it is not, -1 on error. */ Index: gdb/gdbserver/server.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.c,v retrieving revision 1.63 diff -u -p -u -p -r1.63 server.c --- gdb/gdbserver/server.c 30 Jan 2008 00:51:50 -0000 1.63 +++ gdb/gdbserver/server.c 14 Feb 2008 02:03:43 -0000 @@ -270,6 +270,157 @@ monitor_show_help (void) monitor_output (" Enable remote protocol debugging messages\n"); } +/* Subroutine of handle_search_memory to simplify it. */ +/* ??? Copied from simple_search_memory. Combine? */ + +static int +handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len, + gdb_byte *pattern, unsigned pattern_len, + gdb_byte *search_buf, + unsigned chunk_size, unsigned search_buf_size, + CORE_ADDR *found_addrp) +{ + /* Prime the search buffer. */ + + if (read_inferior_memory (start_addr, search_buf, search_buf_size) != 0) + { + warning ("unable to access target memory at 0x%lx, halting search", + (long) start_addr); + return -1; + } + + /* Perform the search. + + The loop is kept simple by allocating [N + pattern-length - 1] bytes. + When we've scanned N bytes we copy the trailing bytes to the start and + read in another N bytes. */ + + while (search_space_len >= pattern_len) + { + gdb_byte *found_ptr; + unsigned nr_search_bytes = (search_space_len < search_buf_size + ? search_space_len + : search_buf_size); + + found_ptr = lmemmem (search_buf, nr_search_bytes, + pattern, pattern_len); + + if (found_ptr != NULL) + { + CORE_ADDR found_addr = start_addr + (found_ptr - search_buf); + *found_addrp = found_addr; + return 1; + } + + /* Not found in this chunk, skip to next chunk. */ + + /* Don't let search_space_len wrap here, it's unsigned. */ + if (search_space_len >= chunk_size) + search_space_len -= chunk_size; + else + search_space_len = 0; + + if (search_space_len >= pattern_len) + { + unsigned keep_len = search_buf_size - chunk_size; + CORE_ADDR read_addr = start_addr + keep_len; + int nr_to_read; + + /* Copy the trailing part of the previous iteration to the front + of the buffer for the next iteration. */ + memcpy (search_buf, search_buf + chunk_size, keep_len); + + nr_to_read = (search_space_len - keep_len < chunk_size + ? search_space_len - keep_len + : chunk_size); + + if (read_inferior_memory (read_addr, search_buf + keep_len, + nr_to_read) != 0) + { + warning ("unable to access target memory at 0x%lx, halting search", + (long) read_addr); + return -1; + } + + start_addr += chunk_size; + } + } + + /* Not found. */ + + return 0; +} + +/* Handle qSearch:memory packets. */ +/* ??? Copied from simple_search_memory. Combine? */ + +static void +handle_search_memory (char *own_buf, int packet_len) +{ + CORE_ADDR start_addr; + CORE_ADDR search_space_len; + gdb_byte *pattern; + unsigned int pattern_len; + /* ??? tunable? + NOTE: also defined in find.c testcase. */ +#define SEARCH_CHUNK_SIZE 16000 + const unsigned chunk_size = SEARCH_CHUNK_SIZE; + /* Buffer to hold memory contents for searching. */ + gdb_byte *search_buf; + unsigned search_buf_size; + int found; + CORE_ADDR found_addr; + int cmd_name_len = sizeof ("qSearch:memory:") - 1; + + pattern = malloc (packet_len); + if (pattern == NULL) + { + error ("unable to allocate memory to perform the search"); + strcpy (own_buf, "E00"); + return; + } + if (decode_search_memory_packet (own_buf + cmd_name_len, + packet_len - cmd_name_len, + &start_addr, &search_space_len, + pattern, &pattern_len) < 0) + { + free (pattern); + error ("error in parsing qSearch:memory packet"); + strcpy (own_buf, "E00"); + return; + } + + search_buf_size = chunk_size + pattern_len - 1; + + /* No point in trying to allocate a buffer larger than the search space. */ + if (search_space_len < search_buf_size) + search_buf_size = search_space_len; + + search_buf = malloc (search_buf_size); + if (search_buf == NULL) + { + free (pattern); + error ("unable to allocate memory to perform the search"); + strcpy (own_buf, "E00"); + return; + } + + found = handle_search_memory_1 (start_addr, search_space_len, + pattern, pattern_len, + search_buf, chunk_size, search_buf_size, + &found_addr); + + if (found > 0) + sprintf (own_buf, "1,%lx", (long) found_addr); + else if (found == 0) + strcpy (own_buf, "0"); + else + strcpy (own_buf, "E00"); + + free (search_buf); + free (pattern); +} + #define require_running(BUF) \ if (!target_running ()) \ { \ @@ -568,6 +719,10 @@ handle_query (char *own_buf, int packet_ supports qXfer:libraries:read, so always report it. */ strcat (own_buf, ";qXfer:libraries:read+"); + /* Do the searching here, so we don't have to send memory back to gdb + to be searched. */ + strcat (own_buf, ";qSearch:memory+"); + if (the_target->read_auxv != NULL) strcat (own_buf, ";qXfer:auxv:read+"); @@ -692,6 +847,13 @@ handle_query (char *own_buf, int packet_ return; } + if (strncmp ("qSearch:memory:", own_buf, sizeof ("qSearch:memory:") - 1) == 0) + { + require_running (own_buf); + handle_search_memory (own_buf, packet_len); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; Index: gdb/gdbserver/server.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.h,v retrieving revision 1.40 diff -u -p -u -p -r1.40 server.h --- gdb/gdbserver/server.h 1 Feb 2008 00:08:25 -0000 1.40 +++ gdb/gdbserver/server.h 14 Feb 2008 02:03:44 -0000 @@ -195,6 +195,10 @@ int decode_X_packet (char *from, int pac int decode_xfer_write (char *buf, int packet_len, char **annex, CORE_ADDR *offset, unsigned int *len, unsigned char *data); +int decode_search_memory_packet (const char *buf, int packet_len, + CORE_ADDR *start_addrp, + CORE_ADDR *search_space_lenp, + gdb_byte *pattern, unsigned int *pattern_lenp); int unhexify (char *bin, const char *hex, int count); int hexify (char *hex, const char *bin, int count); @@ -221,6 +225,10 @@ void error (const char *string,...) ATTR void fatal (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2); void warning (const char *string,...) ATTR_FORMAT (printf, 1, 2); +/* Functions from lmemmem.c */ + +void *lmemmem (const void *, size_t, const void *, size_t); + /* Functions from the register cache definition. */ void init_registers (void); Index: gdb/testsuite/gdb.base/find.c =================================================================== RCS file: gdb/testsuite/gdb.base/find.c diff -N gdb/testsuite/gdb.base/find.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ gdb/testsuite/gdb.base/find.c 14 Feb 2008 02:03:47 -0000 @@ -0,0 +1,62 @@ +/* Testcase for the find command. + This testcase is part of GDB, the GNU debugger. + + Copyright 2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + 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 14 Feb 2008 02:03:47 -0000 @@ -0,0 +1,191 @@ +# Copyright 2008 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@prep.ai.mit.edu + +# This tests the find command. + +if $tracelevel then { + strace $tracelevel +} + +set testfile "find" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug nowarnings}] != "" } { + untested find.exp + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +gdb_test "break $srcfile:stop_here" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "breakpoint function in file" + +gdb_run_cmd +gdb_expect { + -re "Breakpoint \[0-9\]+,.*stop_here.* at .*$srcfile:.*$gdb_prompt $" { + pass "run until function breakpoint" + } + -re "$gdb_prompt $" { + fail "run until function breakpoint" + } + timeout { + fail "run until function breakpoint (timeout)" + } +} + +# We've now got the target program in a state where we can test "find". + +set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*} +set history_prefix {[$][0-9]* = } +set newline {[\r\n]*} +set pattern_not_found "${newline}pattern not found" +set one_pattern_found "${newline}1 pattern found" +set two_patterns_found "${newline}2 patterns found" + +# Test string pattern. + +gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" "" + +gdb_test "find &int8_search_buf\[0\], @sizeof(int8_search_buf), \"aaa\"" \ + "${hex_number}.*${newline}${hex_number}.*${two_patterns_found}" \ + "find string pattern" + +# Test not finding pattern because search range too small, with +# potential find at the edge of the range. + +gdb_test "find &int8_search_buf\[0\], @10+3, \"aaaa\"" \ + "${pattern_not_found}" \ + "pattern not found at end of range" + +# Increase the search range by 1 and we should find the pattern. + +gdb_test "find &int8_search_buf\[0\], @10+3+1, \"aaaa\"" \ + "${hex_number}.*${one_pattern_found}" \ + "pattern found at end of range" + +# Test max-count, $_ and $numfound. + +gdb_test "find /1 &int8_search_buf\[0\], @sizeof(int8_search_buf), \"aaa\"" \ + "${hex_number}.*${one_pattern_found}" \ + "max-count" + +gdb_test "print \$_" \ + "${history_prefix}.*${hex_number}" \ + "\$_" + +gdb_test "print \$numfound" \ + "${history_prefix}1" \ + "\$numfound" + +# Test max-count with size-char. +# They can be specified in either order. + +gdb_test "find /1b &int8_search_buf\[0\], @sizeof(int8_search_buf), 0x61, 0x61, 0x61" \ + "${hex_number}.*${one_pattern_found}" \ + "size,max-count, /1b" + +gdb_test "find /b1 &int8_search_buf\[0\], @sizeof(int8_search_buf), 0x61, 0x61, 0x61" \ + "${hex_number}.*${one_pattern_found}" \ + "size,max-count, /b1" + +gdb_test "find /b /1 &int8_search_buf\[0\], @sizeof(int8_search_buf), 0x61, 0x61, 0x61" \ + "${hex_number}.*${one_pattern_found}" \ + "size,max-count, /b/1" + +gdb_test "find /1 /b &int8_search_buf\[0\], @sizeof(int8_search_buf), 0x61, 0x61, 0x61" \ + "${hex_number}.*${one_pattern_found}" \ + "size,max-count, /1/b" + +# Test specifying end address. + +gdb_test "find /b &int8_search_buf\[0\], &int8_search_buf\[0\]+sizeof(int8_search_buf), 0x61, 0x61, 0x61, 0x61" \ + "${hex_number}.*${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\], @100, (int8_t) 0x62, (int16_t) 0x6363, (int32_t) 0x64646464" \ + "${hex_number}${one_pattern_found}" \ + "find mixed-sized pattern" + +# Test search spanning a large range, in the particular case of native +# targets, test the search spanning multiple chunks. +# Remote targets may implement the search differently. + +set CHUNK_SIZE 16000 ;# see findcmd.c + +gdb_test "set *(int32_t*) &search_buf\[0*${CHUNK_SIZE}+100\] = 0x12345678" "" "" +gdb_test "set *(int32_t*) &search_buf\[1*${CHUNK_SIZE}+100\] = 0x12345678" "" "" + +gdb_test "find /w search_buf, @search_buf_size, 0x12345678" \ + "${hex_number}${newline}${hex_number}${two_patterns_found}" \ + "search spanning large range" + +# For native targets, test a pattern straddling a chunk boundary. + +if [isnative] { + gdb_test "set *(int32_t*) &search_buf\[${CHUNK_SIZE}-1\] = 0xfdb97531" "" "" + gdb_test "find /w search_buf, @search_buf_size, 0xfdb97531" \ + "${hex_number}${one_pattern_found}" \ + "find pattern straddling chunk boundary" +}