summaryrefslogtreecommitdiff
path: root/gdb/target/target.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/target/target.c')
-rw-r--r--gdb/target/target.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/gdb/target/target.c b/gdb/target/target.c
new file mode 100644
index 00000000000..0b165bc05fe
--- /dev/null
+++ b/gdb/target/target.c
@@ -0,0 +1,190 @@
+/* String reading
+
+ Copyright (C) 2022 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 "gdbsupport/common-defs.h"
+#include "target/target.h"
+
+/* Read LEN bytes of target memory at address MEMADDR, placing the
+ results in GDB's memory at MYADDR. Returns a count of the bytes
+ actually read, and optionally a target_xfer_status value in the
+ location pointed to by ERRPTR if ERRPTR is non-null. */
+
+static int
+partial_memory_read (CORE_ADDR memaddr, gdb_byte *myaddr,
+ int len, int *errptr)
+{
+ int nread; /* Number of bytes actually read. */
+ int errcode; /* Error from last read. */
+
+ /* First try a complete read. */
+ errcode = target_read_memory (memaddr, myaddr, len);
+ if (errcode == 0)
+ {
+ /* Got it all. */
+ nread = len;
+ }
+ else
+ {
+ /* Loop, reading one byte at a time until we get as much as we can. */
+ for (errcode = 0, nread = 0; len > 0 && errcode == 0; nread++, len--)
+ {
+ errcode = target_read_memory (memaddr++, myaddr++, 1);
+ }
+ /* If an error, the last read was unsuccessful, so adjust count. */
+ if (errcode != 0)
+ {
+ nread--;
+ }
+ }
+ if (errptr != NULL)
+ {
+ *errptr = errcode;
+ }
+ return (nread);
+}
+
+/* See target/target.h. */
+
+int
+target_read_string (CORE_ADDR addr, int len, int width,
+ unsigned int fetchlimit,
+ gdb::unique_xmalloc_ptr<gdb_byte> *buffer,
+ int *bytes_read)
+{
+ int errcode; /* Errno returned from bad reads. */
+ unsigned int nfetch; /* Chars to fetch / chars fetched. */
+ gdb_byte *bufptr; /* Pointer to next available byte in
+ buffer. */
+
+ /* Loop until we either have all the characters, or we encounter
+ some error, such as bumping into the end of the address space. */
+
+ buffer->reset (nullptr);
+
+ if (len > 0)
+ {
+ /* We want fetchlimit chars, so we might as well read them all in
+ one operation. */
+ unsigned int fetchlen = std::min ((unsigned) len, fetchlimit);
+
+ buffer->reset ((gdb_byte *) xmalloc (fetchlen * width));
+ bufptr = buffer->get ();
+
+ nfetch = partial_memory_read (addr, bufptr, fetchlen * width, &errcode)
+ / width;
+ addr += nfetch * width;
+ bufptr += nfetch * width;
+ }
+ else if (len == -1)
+ {
+ unsigned long bufsize = 0;
+ unsigned int chunksize; /* Size of each fetch, in chars. */
+ int found_nul; /* Non-zero if we found the nul char. */
+ gdb_byte *limit; /* First location past end of fetch buffer. */
+
+ found_nul = 0;
+ /* We are looking for a NUL terminator to end the fetching, so we
+ might as well read in blocks that are large enough to be efficient,
+ but not so large as to be slow if fetchlimit happens to be large.
+ So we choose the minimum of 8 and fetchlimit. We used to use 200
+ instead of 8 but 200 is way too big for remote debugging over a
+ serial line. */
+ chunksize = std::min (8u, fetchlimit);
+
+ do
+ {
+ nfetch = std::min ((unsigned long) chunksize, fetchlimit - bufsize);
+
+ if (*buffer == NULL)
+ buffer->reset ((gdb_byte *) xmalloc (nfetch * width));
+ else
+ buffer->reset ((gdb_byte *) xrealloc (buffer->release (),
+ (nfetch + bufsize) * width));
+
+ bufptr = buffer->get () + bufsize * width;
+ bufsize += nfetch;
+
+ /* Read as much as we can. */
+ nfetch = partial_memory_read (addr, bufptr, nfetch * width, &errcode)
+ / width;
+
+ /* Scan this chunk for the null character that terminates the string
+ to print. If found, we don't need to fetch any more. Note
+ that bufptr is explicitly left pointing at the next character
+ after the null character, or at the next character after the end
+ of the buffer. */
+
+ limit = bufptr + nfetch * width;
+ while (bufptr < limit)
+ {
+ bool found_nonzero = false;
+
+ for (int i = 0; !found_nonzero && i < width; ++i)
+ if (bufptr[i] != 0)
+ found_nonzero = true;
+
+ addr += width;
+ bufptr += width;
+ if (!found_nonzero)
+ {
+ /* We don't care about any error which happened after
+ the NUL terminator. */
+ errcode = 0;
+ found_nul = 1;
+ break;
+ }
+ }
+ }
+ while (errcode == 0 /* no error */
+ && bufptr - buffer->get () < fetchlimit * width /* no overrun */
+ && !found_nul); /* haven't found NUL yet */
+ }
+ else
+ { /* Length of string is really 0! */
+ /* We always allocate *buffer. */
+ buffer->reset ((gdb_byte *) xmalloc (1));
+ bufptr = buffer->get ();
+ errcode = 0;
+ }
+
+ /* bufptr and addr now point immediately beyond the last byte which we
+ consider part of the string (including a '\0' which ends the string). */
+ *bytes_read = bufptr - buffer->get ();
+
+ return errcode;
+}
+
+/* See target/target.h. */
+
+gdb::unique_xmalloc_ptr<char>
+target_read_string (CORE_ADDR memaddr, int len, int *bytes_read)
+{
+ gdb::unique_xmalloc_ptr<gdb_byte> buffer;
+
+ int ignore;
+ if (bytes_read == nullptr)
+ bytes_read = &ignore;
+
+ /* Note that the endian-ness does not matter here. */
+ int errcode = target_read_string (memaddr, -1, 1, len, &buffer, bytes_read);
+ if (errcode != 0)
+ return {};
+
+ return gdb::unique_xmalloc_ptr<char> ((char *) buffer.release ());
+}