| /* String reading | |
| Copyright (C) 2022-2023 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 ()); | |
| } |