| /* String reading |
| |
| Copyright (C) 2022-2024 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 "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 ()); |
| } |
| |
| /* See target/target.h. */ |
| |
| std::string |
| to_string (gdb_thread_options options) |
| { |
| static constexpr gdb_thread_options::string_mapping mapping[] = { |
| MAP_ENUM_FLAG (GDB_THREAD_OPTION_CLONE), |
| MAP_ENUM_FLAG (GDB_THREAD_OPTION_EXIT), |
| }; |
| return options.to_string (mapping); |
| } |