191 lines
5.7 KiB
C
191 lines
5.7 KiB
C
|
/* 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 ());
|
||
|
}
|