New "find" command.

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

	* gdbserver/server.h (decode_search_memory_packet): Declare.
	* gdbserver/remote-utils.c (decode_search_memory_packet): New fn.
	* gdbserver/server.c (handle_search_memory_1): New fn.
	(handle_search_memory): New fn.
	(handle_query): Process qSearch:memory packets.

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

	* testsuite/gdb.base/find.exp: New file.
	* testsuite/gdb.base/find.c: New file.
This commit is contained in:
Doug Evans 2008-05-09 17:02:03 +00:00
parent 7010a0c901
commit 08388c79d5
16 changed files with 1204 additions and 3 deletions

View File

@ -1,3 +1,22 @@
2008-05-09 Doug Evans <dje@google.com>
New "find" command.
* NEWS: Document find command and qSearch:memory packet.
* Makefile.in (SFILES): Add findcmd.c.
(COMMON_OBJS): Add findcmd.o.
(findcmd.o): New rule.
* findcmd.c: New file.
* target.h (target_ops): New member to_search_memory.
(simple_search_memory): Declare.
(target_search_memory): Declare.
* target.c (simple_search_memory): New fn.
(target_search_memory): New fn.
* remote.c (PACKET_qSearch_memory): New packet kind.
(remote_search_memory): New fn.
(init_remote_ops): Init to_search_memory.
(init_extended_remote_ops): Ditto.
(_initialize_remote): Add qSearch:memory packet config command.
2008-05-09 Eli Zaretskii <eliz@gnu.org>
* thread.c (_initialize_thread): Don't use commas and periods in

View File

@ -609,9 +609,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \
dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
elfread.c environ.c eval.c event-loop.c event-top.c expprint.c \
f-exp.y f-lang.c f-typeprint.c f-valprint.c findvar.c frame.c \
frame-base.c \
frame-unwind.c \
f-exp.y f-lang.c f-typeprint.c f-valprint.c findcmd.c findvar.c \
frame.c frame-base.c frame-unwind.c \
gdbarch.c arch-utils.c gdbtypes.c gnu-v2-abi.c gnu-v3-abi.c \
inf-loop.c \
infcall.c \
@ -1061,6 +1060,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
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 \
@ -2136,6 +2136,8 @@ fbsd-nat.o: fbsd-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) $(regcache_h) \
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) \

View File

@ -3,6 +3,11 @@
*** Changes since GDB 6.8
* New remote packets
qSearch:memory:
Search memory for a sequence of bytes.
* The "disassemble" command now supports an optional /m modifier to print mixed
source+assembly.
@ -35,6 +40,10 @@ have also been fixed.
* New commands
find [/size-char] [/max-count] start-address, end-address|+search-space-size,
val1 [, val2, ...]
Search memory for a sequence of bytes.
set debug timetstamp
show debug timestamp
Display timestamps with GDB debugging output.

View File

@ -1,3 +1,7 @@
2008-05-09 Doug Evans <dje@google.com>
* doc/gdb.texinfo: Document "find" command, qSearch:memory packet.
2008-05-05 Doug Evans <dje@google.com>
* gdb.texinfo (disassemble): Document /m modifier.

View File

@ -5590,6 +5590,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
@ -7653,6 +7654,104 @@ state (dirty, bad, ok, etc.). This command is useful for debugging
the data cache operation.
@end table
@node Searching Memory
@section Search Memory
@cindex searching memory
Memory can be searched for a particular sequence of bytes with the
@code{find} command.
@table @code
@kindex find
@item find @r{[}/@var{sn}@r{]} @var{start_addr}, +@var{len}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]}
@itemx find @r{[}/@var{sn}@r{]} @var{start_addr}, @var{end_addr}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]}
Search memory for the sequence of bytes specified by @var{val1}, @var{val2},
etc. The search begins at address @var{start_addr} and continues for either
@var{len} bytes or through to @var{end_addr} inclusive.
@end table
@var{s} and @var{n} are optional parameters.
They may be specified in either order, apart or together.
@table @r
@item @var{s}, search query size
The size of each search query value.
@table @code
@item b
bytes
@item h
halfwords (two bytes)
@item w
words (four bytes)
@item g
giant words (eight bytes)
@end table
All values are interpreted in the current language.
This means, for example, that if the current source language is C/C@t{++}
then searching for the string ``hello'' includes the trailing '\0'.
If the value size is not specified, it is taken from the
value's type in the current language.
This is useful when one wants to specify the search
pattern as a mixture of types.
Note that this means, for example, that in the case of C-like languages
a search for an untyped 0x42 will search for @samp{(int) 0x42}
which is typically four bytes.
@item @var{n}, maximum number of finds
The maximum number of matches to print. The default is to print all finds.
@end table
You can use strings as 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 @code{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
@noindent
you get during debugging:
@smallexample
(gdb) find &hello[0], +sizeof(hello), "hello"
0x804956d <hello.1620+6>
1 pattern found
(gdb) find &hello[0], +sizeof(hello), 'h', 'e', 'l', 'l', 'o'
0x8049567 <hello.1620>
0x804956d <hello.1620+6>
2 patterns found
(gdb) find /b1 &hello[0], +sizeof(hello), 'h', 0x65, 'l'
0x8049567 <hello.1620>
1 pattern found
(gdb) find &mixed, +sizeof(mixed), (char) 'c', (short) 0x1234, (int) 0x87654321
0x8049560 <mixed.1625>
1 pattern found
(gdb) print $numfound
$1 = 1
(gdb) print $_
$2 = (void *) 0x8049560
@end smallexample
@node Macros
@chapter C Preprocessor Macros
@ -13496,6 +13595,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
@ -24606,6 +24709,26 @@ command by a @samp{,}, not a @samp{:}, contrary to the naming
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

331
gdb/findcmd.c Normal file
View File

@ -0,0 +1,331 @@
/* The find command.
Copyright (C) 2008 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include <ctype.h>
#include "gdb_string.h"
#include "gdbcmd.h"
#include "value.h"
#include "target.h"
/* Copied from bfd_put_bits. */
static void
put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)
{
int i;
int bytes;
gdb_assert (bits % 8 == 0);
bytes = bits / 8;
for (i = 0; i < bytes; i++)
{
int index = big_p ? bytes - i - 1 : i;
buf[index] = data & 0xff;
data >>= 8;
}
}
/* Subroutine of find_command to simplify it.
Parse the arguments of the "find" command. */
static void
parse_find_args (char *args, ULONGEST *max_countp,
char **pattern_bufp, ULONGEST *pattern_lenp,
CORE_ADDR *start_addrp, ULONGEST *search_space_lenp)
{
/* Default to using the specified type. */
char size = '\0';
ULONGEST max_count = ~(ULONGEST) 0;
/* Buffer to hold the search pattern. */
char *pattern_buf;
/* Current size of search pattern buffer.
We realloc space as needed. */
#define INITIAL_PATTERN_BUF_SIZE 100
ULONGEST pattern_buf_size = INITIAL_PATTERN_BUF_SIZE;
/* Pointer to one past the last in-use part of pattern_buf. */
char *pattern_buf_end;
ULONGEST pattern_len;
CORE_ADDR start_addr;
ULONGEST search_space_len;
char *s = args;
bfd_boolean big_p = gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG;
struct cleanup *old_cleanups;
struct value *v;
if (args == NULL)
error (_("missing search parameters"));
pattern_buf = xmalloc (pattern_buf_size);
pattern_buf_end = pattern_buf;
old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
/* Get search granularity and/or max count if specified.
They may be specified in either order, together or separately. */
while (*s == '/')
{
++s;
while (*s != '\0' && *s != '/' && !isspace (*s))
{
if (isdigit (*s))
{
max_count = atoi (s);
while (isdigit (*s))
++s;
continue;
}
switch (*s)
{
case 'b':
case 'h':
case 'w':
case 'g':
size = *s++;
break;
default:
error (_("invalid size granularity"));
}
}
while (isspace (*s))
++s;
}
/* Get the search range. */
v = parse_to_comma_and_eval (&s);
start_addr = value_as_address (v);
if (*s == ',')
++s;
while (isspace (*s))
++s;
if (*s == '+')
{
LONGEST len;
++s;
v = parse_to_comma_and_eval (&s);
len = value_as_long (v);
if (len == 0)
{
printf_filtered (_("empty search range\n"));
return;
}
if (len < 0)
error (_("invalid length"));
/* Watch for overflows. */
if (len > CORE_ADDR_MAX
|| (start_addr + len - 1) < start_addr)
error (_("search space too large"));
search_space_len = len;
}
else
{
CORE_ADDR end_addr;
v = parse_to_comma_and_eval (&s);
end_addr = value_as_address (v);
if (start_addr > end_addr)
error (_("invalid search space, end preceeds start"));
search_space_len = end_addr - start_addr + 1;
/* We don't support searching all of memory
(i.e. start=0, end = 0xff..ff).
Bail to avoid overflows later on. */
if (search_space_len == 0)
error (_("overflow in address range computation, choose smaller range"));
}
if (*s == ',')
++s;
/* Fetch the search string. */
while (*s != '\0')
{
LONGEST x;
int val_bytes;
while (isspace (*s))
++s;
v = parse_to_comma_and_eval (&s);
val_bytes = TYPE_LENGTH (value_type (v));
/* Keep it simple and assume size == 'g' when watching for when we
need to grow the pattern buf. */
if ((pattern_buf_end - pattern_buf + max (val_bytes, sizeof (int64_t)))
> pattern_buf_size)
{
size_t current_offset = pattern_buf_end - pattern_buf;
pattern_buf_size *= 2;
pattern_buf = xrealloc (pattern_buf, pattern_buf_size);
pattern_buf_end = pattern_buf + current_offset;
}
if (size != '\0')
{
x = value_as_long (v);
switch (size)
{
case 'b':
*pattern_buf_end++ = x;
break;
case 'h':
put_bits (x, pattern_buf_end, 16, big_p);
pattern_buf_end += sizeof (int16_t);
break;
case 'w':
put_bits (x, pattern_buf_end, 32, big_p);
pattern_buf_end += sizeof (int32_t);
break;
case 'g':
put_bits (x, pattern_buf_end, 64, big_p);
pattern_buf_end += sizeof (int64_t);
break;
}
}
else
{
memcpy (pattern_buf_end, value_contents_raw (v), val_bytes);
pattern_buf_end += val_bytes;
}
if (*s == ',')
++s;
while (isspace (*s))
++s;
}
if (pattern_buf_end == pattern_buf)
error (_("missing search pattern"));
pattern_len = pattern_buf_end - pattern_buf;
if (search_space_len < pattern_len)
error (_("search space too small to contain pattern"));
*max_countp = max_count;
*pattern_bufp = pattern_buf;
*pattern_lenp = pattern_len;
*start_addrp = start_addr;
*search_space_lenp = search_space_len;
/* We successfully parsed the arguments, leave the freeing of PATTERN_BUF
to the caller now. */
discard_cleanups (old_cleanups);
}
static void
find_command (char *args, int from_tty)
{
/* Command line parameters.
These are initialized to avoid uninitialized warnings from -Wall. */
ULONGEST max_count = 0;
char *pattern_buf = 0;
ULONGEST pattern_len = 0;
CORE_ADDR start_addr = 0;
ULONGEST search_space_len = 0;
/* End of command line parameters. */
unsigned int found_count;
CORE_ADDR last_found_addr;
struct cleanup *old_cleanups;
parse_find_args (args, &max_count, &pattern_buf, &pattern_len,
&start_addr, &search_space_len);
old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
/* Perform the search. */
found_count = 0;
last_found_addr = 0;
while (search_space_len >= pattern_len
&& found_count < max_count)
{
/* Offset from start of this iteration to the next iteration. */
ULONGEST next_iter_incr;
CORE_ADDR found_addr;
int found = target_search_memory (start_addr, search_space_len,
pattern_buf, pattern_len, &found_addr);
if (found <= 0)
break;
print_address (found_addr, gdb_stdout);
printf_filtered ("\n");
++found_count;
last_found_addr = found_addr;
/* Begin next iteration at one byte past this match. */
next_iter_incr = (found_addr - start_addr) + 1;
/* For robustness, we don't let search_space_len go -ve here. */
if (search_space_len >= next_iter_incr)
search_space_len -= next_iter_incr;
else
search_space_len = 0;
start_addr += next_iter_incr;
}
/* Record and print the results. */
set_internalvar (lookup_internalvar ("numfound"),
value_from_longest (builtin_type_int,
(LONGEST) found_count));
if (found_count > 0)
{
set_internalvar (lookup_internalvar ("_"),
value_from_pointer (builtin_type_void_data_ptr,
last_found_addr));
}
if (found_count == 0)
printf_filtered ("pattern not found\n");
else
printf_filtered ("%d pattern%s found\n", found_count,
found_count > 1 ? "s" : "");
do_cleanups (old_cleanups);
}
void
_initialize_mem_search (void)
{
add_cmd ("find", class_vars, find_command, _("\
Search memory for a sequence of bytes.\n\
Usage:\n\
find [/size-char] [/max-count] start-address, end-address, expr1 [, expr2 ...]\n\
find [/size-char] [/max-count] start-address, +length, expr1 [, expr2 ...]\n\
size-char is one of b,h,w,g for 8,16,32,64 bit values respectively,\n\
and if not specified the size is taken from the type of the expression\n\
in the current language.\n\
Note that this means for example that in the case of C-like languages\n\
a search for an untyped 0x42 will search for \"(int) 0x42\"\n\
which is typically four bytes.\n\
\n\
The address of the last match is stored as the value of \"$_\".\n\
Convenience variable \"$numfound\" is set to the number of matches."),
&cmdlist);
}

View File

@ -1,3 +1,11 @@
2008-05-09 Doug Evans <dje@google.com>
* gdbserver/server.h (decode_search_memory_packet): Declare.
* gdbserver/remote-utils.c (decode_search_memory_packet): New fn.
* gdbserver/server.c (handle_search_memory_1): New fn.
(handle_search_memory): New fn.
(handle_query): Process qSearch:memory packets.
2008-05-08 Ulrich Weigand <uweigand@de.ibm.com>
* regcache.c (registers_length): Remove.

View File

@ -1080,6 +1080,24 @@ decode_xfer_write (char *buf, int packet_len, char **annex, CORE_ADDR *offset,
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. */

View File

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

View File

@ -195,6 +195,10 @@ int decode_X_packet (char *from, int packet_len, CORE_ADDR * mem_addr_ptr,
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);

View File

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

View File

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

View File

@ -505,6 +505,17 @@ struct target_ops
int (*to_auxv_parse) (struct target_ops *ops, gdb_byte **readptr,
gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp);
/* Search SEARCH_SPACE_LEN bytes beginning at START_ADDR for the
sequence of bytes in PATTERN with length PATTERN_LEN.
The result is 1 if found, 0 if not found, and -1 if there was an error
requiring halting of the search (e.g. memory read error).
If the pattern is found the address is recorded in FOUND_ADDRP. */
int (*to_search_memory) (struct target_ops *ops,
CORE_ADDR start_addr, ULONGEST search_space_len,
const gdb_byte *pattern, ULONGEST pattern_len,
CORE_ADDR *found_addrp);
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@ -1102,6 +1113,21 @@ extern int target_stopped_data_address_p (struct target_ops *);
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) \

View File

@ -1,3 +1,8 @@
2008-05-09 Doug Evans <dje@google.com>
* testsuite/gdb.base/find.exp: New file.
* testsuite/gdb.base/find.c: New file.
2008-05-08 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.base/commands.exp (watchpoint_command_test): Handle

View File

@ -0,0 +1,62 @@
/* Testcase for the find command.
This testcase is part of GDB, the GNU debugger.
Copyright 2008 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Please email any bugs, comments, and/or additions to this file to:
bug-gdb@gnu.org */
#include <stdlib.h>
#include <stdint.h>
#define CHUNK_SIZE 16000 /* same as findcmd.c's */
#define BUF_SIZE (2 * CHUNK_SIZE) /* at least two chunks */
static int8_t int8_search_buf[100];
static int16_t int16_search_buf[100];
static int32_t int32_search_buf[100];
static int64_t int64_search_buf[100];
static char *search_buf;
static int search_buf_size;
static int x;
static void
stop_here ()
{
x = 1; // stop here
}
static void
init_bufs ()
{
search_buf_size = BUF_SIZE;
search_buf = malloc (search_buf_size);
if (search_buf == NULL)
exit (1);
memset (search_buf, 'x', search_buf_size);
}
int
main ()
{
init_bufs ();
stop_here ();
return 0;
}

View File

@ -0,0 +1,191 @@
# Copyright 2008 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Please email any bugs, comments, and/or additions to this file to:
# bug-gdb@prep.ai.mit.edu
# This tests the find command.
if $tracelevel then {
strace $tracelevel
}
set testfile "find"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug nowarnings}] != "" } {
untested find.exp
return -1
}
gdb_exit
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
gdb_test "break $srcfile:stop_here" \
"Breakpoint.*at.* file .*$srcfile, line.*" \
"breakpoint function in file"
gdb_run_cmd
gdb_expect {
-re "Breakpoint \[0-9\]+,.*stop_here.* at .*$srcfile:.*$gdb_prompt $" {
pass "run until function breakpoint"
}
-re "$gdb_prompt $" {
fail "run until function breakpoint"
}
timeout {
fail "run until function breakpoint (timeout)"
}
}
# We've now got the target program in a state where we can test "find".
set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}
set history_prefix {[$][0-9]* = }
set newline {[\r\n]*}
set pattern_not_found "${newline}pattern not found"
set one_pattern_found "${newline}1 pattern found"
set two_patterns_found "${newline}2 patterns found"
# Test string pattern.
gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" ""
gdb_test "find &int8_search_buf\[0\], +sizeof(int8_search_buf), 'a', 'a', 'a'" \
"${hex_number}.*<int8_search_buf\\+10>${newline}${hex_number}.*<int8_search_buf\\+11>${two_patterns_found}" \
"find string pattern"
# Test not finding pattern because search range too small, with
# potential find at the edge of the range.
gdb_test "find &int8_search_buf\[0\], +10+3, \"aaaa\"" \
"${pattern_not_found}" \
"pattern not found at end of range"
# Increase the search range by 1 and we should find the pattern.
gdb_test "find &int8_search_buf\[0\], +10+3+1, 'a', 'a', 'a', 'a'" \
"${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
"pattern found at end of range"
# Test max-count, $_ and $numfound.
gdb_test "find /1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 'a', 'a', 'a'" \
"${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
"max-count"
gdb_test "print \$_" \
"${history_prefix}.*${hex_number}" \
"\$_"
gdb_test "print \$numfound" \
"${history_prefix}1" \
"\$numfound"
# Test max-count with size-char.
# They can be specified in either order.
gdb_test "find /1b &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
"${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
"size,max-count, /1b"
gdb_test "find /b1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
"${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
"size,max-count, /b1"
gdb_test "find /b /1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
"${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
"size,max-count, /b/1"
gdb_test "find /1 /b &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
"${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
"size,max-count, /1/b"
# Test specifying end address.
gdb_test "find /b &int8_search_buf\[0\], &int8_search_buf\[0\]+sizeof(int8_search_buf), 0x61, 0x61, 0x61, 0x61" \
"${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
"find byte pattern with end address"
# Test 16-bit pattern.
gdb_test "set int16_search_buf\[10\] = 0x1234" "" ""
gdb_test "find /h &int16_search_buf\[0\], +sizeof(int16_search_buf), 0x1234" \
"${hex_number}.*<int16_search_buf\\+20>${one_pattern_found}" \
"find 16-bit pattern"
gdb_test "find &int16_search_buf\[0\], +sizeof(int16_search_buf), (int16_t) 0x1234" \
"${hex_number}.*<int16_search_buf\\+20>${one_pattern_found}" \
"find 16-bit pattern"
# Test 32-bit pattern.
gdb_test "set int32_search_buf\[10\] = 0x12345678" "" ""
gdb_test "find &int32_search_buf\[0\], +sizeof(int32_search_buf), (int32_t) 0x12345678" \
"${hex_number}.*<int32_search_buf\\+40>${one_pattern_found}" \
"find 32-bit pattern"
gdb_test "find /w &int32_search_buf\[0\], +sizeof(int32_search_buf), 0x12345678" \
"${hex_number}.*<int32_search_buf\\+40>${one_pattern_found}" \
"find 32-bit pattern"
# Test 64-bit pattern.
gdb_test "set int64_search_buf\[10\] = 0xfedcba9876543210LL" "" ""
gdb_test "find &int64_search_buf\[0\], +sizeof(int64_search_buf), (int64_t) 0xfedcba9876543210LL" \
"${hex_number}.*<int64_search_buf\\+80>${one_pattern_found}" \
"find 64-bit pattern"
gdb_test "find /g &int64_search_buf\[0\], +sizeof(int64_search_buf), 0xfedcba9876543210LL" \
"${hex_number}.*<int64_search_buf\\+80>${one_pattern_found}" \
"find 64-bit pattern"
# Test mixed-sized patterns.
gdb_test "set *(int8_t*) &search_buf\[10\] = 0x62" "" ""
gdb_test "set *(int16_t*) &search_buf\[11\] = 0x6363" "" ""
gdb_test "set *(int32_t*) &search_buf\[13\] = 0x64646464" "" ""
gdb_test "find &search_buf\[0\], +100, (int8_t) 0x62, (int16_t) 0x6363, (int32_t) 0x64646464" \
"${hex_number}${one_pattern_found}" \
"find mixed-sized pattern"
# Test search spanning a large range, in the particular case of native
# targets, test the search spanning multiple chunks.
# Remote targets may implement the search differently.
set CHUNK_SIZE 16000 ;# see findcmd.c
gdb_test "set *(int32_t*) &search_buf\[0*${CHUNK_SIZE}+100\] = 0x12345678" "" ""
gdb_test "set *(int32_t*) &search_buf\[1*${CHUNK_SIZE}+100\] = 0x12345678" "" ""
gdb_test "find /w search_buf, +search_buf_size, 0x12345678" \
"${hex_number}${newline}${hex_number}${two_patterns_found}" \
"search spanning large range"
# For native targets, test a pattern straddling a chunk boundary.
if [isnative] {
gdb_test "set *(int32_t*) &search_buf\[${CHUNK_SIZE}-1\] = 0xfdb97531" "" ""
gdb_test "find /w search_buf, +search_buf_size, 0xfdb97531" \
"${hex_number}${one_pattern_found}" \
"find pattern straddling chunk boundary"
}