diff options
author | Tom Tromey <tromey@adacore.com> | 2022-04-13 06:42:19 -0600 |
---|---|---|
committer | Tom Tromey <tromey@adacore.com> | 2022-04-14 12:12:34 -0600 |
commit | b17c7ab3808f5b781537514b28695dafaa29cb28 (patch) | |
tree | 4d542aeb0071db24f24d08333ab99198ad4b0cf6 /gdb/target/target.c | |
parent | 9da74023eb9378315d6b7e1bae02f52cfecc8bd1 (diff) | |
download | gdb-b17c7ab3808f5b781537514b28695dafaa29cb28.zip gdb-b17c7ab3808f5b781537514b28695dafaa29cb28.tar.gz gdb-b17c7ab3808f5b781537514b28695dafaa29cb28.tar.bz2 |
Move target_read_string to target/target.c
This moves the two overloads of target_read_string to a new file,
target/target.c, and updates both gdb and gdbserver to build this.
Diffstat (limited to 'gdb/target/target.c')
-rw-r--r-- | gdb/target/target.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/gdb/target/target.c b/gdb/target/target.c new file mode 100644 index 0000000..0b165bc --- /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 ()); +} |