1/* The find command.
2
3   Copyright (C) 2008-2020 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "defs.h"
21#include "arch-utils.h"
22#include <ctype.h>
23#include "gdbcmd.h"
24#include "value.h"
25#include "target.h"
26#include "cli/cli-utils.h"
27#include <algorithm>
28#include "gdbsupport/byte-vector.h"
29
30/* Copied from bfd_put_bits.  */
31
32static void
33put_bits (bfd_uint64_t data, gdb::byte_vector &buf, int bits, bfd_boolean big_p)
34{
35  int i;
36  int bytes;
37
38  gdb_assert (bits % 8 == 0);
39
40  bytes = bits / 8;
41  size_t last = buf.size ();
42  buf.resize (last + bytes);
43  for (i = 0; i < bytes; i++)
44    {
45      int index = big_p ? bytes - i - 1 : i;
46
47      buf[last + index] = data & 0xff;
48      data >>= 8;
49    }
50}
51
52/* Subroutine of find_command to simplify it.
53   Parse the arguments of the "find" command.  */
54
55static gdb::byte_vector
56parse_find_args (const char *args, ULONGEST *max_countp,
57		 CORE_ADDR *start_addrp, ULONGEST *search_space_lenp,
58		 bfd_boolean big_p)
59{
60  /* Default to using the specified type.  */
61  char size = '\0';
62  ULONGEST max_count = ~(ULONGEST) 0;
63  /* Buffer to hold the search pattern.  */
64  gdb::byte_vector pattern_buf;
65  CORE_ADDR start_addr;
66  ULONGEST search_space_len;
67  const char *s = args;
68  struct value *v;
69
70  if (args == NULL)
71    error (_("Missing search parameters."));
72
73  /* Get search granularity and/or max count if specified.
74     They may be specified in either order, together or separately.  */
75
76  while (*s == '/')
77    {
78      ++s;
79
80      while (*s != '\0' && *s != '/' && !isspace (*s))
81	{
82	  if (isdigit (*s))
83	    {
84	      max_count = atoi (s);
85	      while (isdigit (*s))
86		++s;
87	      continue;
88	    }
89
90	  switch (*s)
91	    {
92	    case 'b':
93	    case 'h':
94	    case 'w':
95	    case 'g':
96	      size = *s++;
97	      break;
98	    default:
99	      error (_("Invalid size granularity."));
100	    }
101	}
102
103      s = skip_spaces (s);
104    }
105
106  /* Get the search range.  */
107
108  v = parse_to_comma_and_eval (&s);
109  start_addr = value_as_address (v);
110
111  if (*s == ',')
112    ++s;
113  s = skip_spaces (s);
114
115  if (*s == '+')
116    {
117      LONGEST len;
118
119      ++s;
120      v = parse_to_comma_and_eval (&s);
121      len = value_as_long (v);
122      if (len == 0)
123	{
124	  printf_filtered (_("Empty search range.\n"));
125	  return pattern_buf;
126	}
127      if (len < 0)
128	error (_("Invalid length."));
129      /* Watch for overflows.  */
130      if (len > CORE_ADDR_MAX
131	  || (start_addr + len - 1) < start_addr)
132	error (_("Search space too large."));
133      search_space_len = len;
134    }
135  else
136    {
137      CORE_ADDR end_addr;
138
139      v = parse_to_comma_and_eval (&s);
140      end_addr = value_as_address (v);
141      if (start_addr > end_addr)
142	error (_("Invalid search space, end precedes start."));
143      search_space_len = end_addr - start_addr + 1;
144      /* We don't support searching all of memory
145	 (i.e. start=0, end = 0xff..ff).
146	 Bail to avoid overflows later on.  */
147      if (search_space_len == 0)
148	error (_("Overflow in address range "
149		 "computation, choose smaller range."));
150    }
151
152  if (*s == ',')
153    ++s;
154
155  /* Fetch the search string.  */
156
157  while (*s != '\0')
158    {
159      LONGEST x;
160      struct type *t;
161
162      s = skip_spaces (s);
163
164      v = parse_to_comma_and_eval (&s);
165      t = value_type (v);
166
167      if (size != '\0')
168	{
169	  x = value_as_long (v);
170	  switch (size)
171	    {
172	    case 'b':
173	      pattern_buf.push_back (x);
174	      break;
175	    case 'h':
176	      put_bits (x, pattern_buf, 16, big_p);
177	      break;
178	    case 'w':
179	      put_bits (x, pattern_buf, 32, big_p);
180	      break;
181	    case 'g':
182	      put_bits (x, pattern_buf, 64, big_p);
183	      break;
184	    }
185	}
186      else
187	{
188	  const gdb_byte *contents = value_contents (v);
189	  pattern_buf.insert (pattern_buf.end (), contents,
190			      contents + TYPE_LENGTH (t));
191	}
192
193      if (*s == ',')
194	++s;
195      s = skip_spaces (s);
196    }
197
198  if (pattern_buf.empty ())
199    error (_("Missing search pattern."));
200
201  if (search_space_len < pattern_buf.size ())
202    error (_("Search space too small to contain pattern."));
203
204  *max_countp = max_count;
205  *start_addrp = start_addr;
206  *search_space_lenp = search_space_len;
207
208  return pattern_buf;
209}
210
211static void
212find_command (const char *args, int from_tty)
213{
214  struct gdbarch *gdbarch = get_current_arch ();
215  bfd_boolean big_p = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG;
216  /* Command line parameters.
217     These are initialized to avoid uninitialized warnings from -Wall.  */
218  ULONGEST max_count = 0;
219  CORE_ADDR start_addr = 0;
220  ULONGEST search_space_len = 0;
221  /* End of command line parameters.  */
222  unsigned int found_count;
223  CORE_ADDR last_found_addr;
224
225  gdb::byte_vector pattern_buf = parse_find_args (args, &max_count,
226						  &start_addr,
227						  &search_space_len,
228						  big_p);
229
230  /* Perform the search.  */
231
232  found_count = 0;
233  last_found_addr = 0;
234
235  while (search_space_len >= pattern_buf.size ()
236	 && found_count < max_count)
237    {
238      /* Offset from start of this iteration to the next iteration.  */
239      ULONGEST next_iter_incr;
240      CORE_ADDR found_addr;
241      int found = target_search_memory (start_addr, search_space_len,
242					pattern_buf.data (),
243					pattern_buf.size (),
244					&found_addr);
245
246      if (found <= 0)
247	break;
248
249      print_address (gdbarch, found_addr, gdb_stdout);
250      printf_filtered ("\n");
251      ++found_count;
252      last_found_addr = found_addr;
253
254      /* Begin next iteration at one byte past this match.  */
255      next_iter_incr = (found_addr - start_addr) + 1;
256
257      /* For robustness, we don't let search_space_len go -ve here.  */
258      if (search_space_len >= next_iter_incr)
259	search_space_len -= next_iter_incr;
260      else
261	search_space_len = 0;
262      start_addr += next_iter_incr;
263    }
264
265  /* Record and print the results.  */
266
267  set_internalvar_integer (lookup_internalvar ("numfound"), found_count);
268  if (found_count > 0)
269    {
270      struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
271
272      set_internalvar (lookup_internalvar ("_"),
273		       value_from_pointer (ptr_type, last_found_addr));
274    }
275
276  if (found_count == 0)
277    printf_filtered ("Pattern not found.\n");
278  else
279    printf_filtered ("%d pattern%s found.\n", found_count,
280		     found_count > 1 ? "s" : "");
281}
282
283void _initialize_mem_search ();
284void
285_initialize_mem_search ()
286{
287  add_cmd ("find", class_vars, find_command, _("\
288Search memory for a sequence of bytes.\n\
289Usage:\nfind \
290[/SIZE-CHAR] [/MAX-COUNT] START-ADDRESS, END-ADDRESS, EXPR1 [, EXPR2 ...]\n\
291find [/SIZE-CHAR] [/MAX-COUNT] START-ADDRESS, +LENGTH, EXPR1 [, EXPR2 ...]\n\
292SIZE-CHAR is one of b,h,w,g for 8,16,32,64 bit values respectively,\n\
293and if not specified the size is taken from the type of the expression\n\
294in the current language.\n\
295Note that this means for example that in the case of C-like languages\n\
296a search for an untyped 0x42 will search for \"(int) 0x42\"\n\
297which is typically four bytes, and a search for a string \"hello\" will\n\
298include the trailing '\\0'.  The null terminator can be removed from\n\
299searching by using casts, e.g.: {char[5]}\"hello\".\n\
300\n\
301The address of the last match is stored as the value of \"$_\".\n\
302Convenience variable \"$numfound\" is set to the number of matches."),
303	   &cmdlist);
304}
305