-
-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pass PacketBuf as an argument of API #72
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, looking good. Definitely simplified a lot of the callback-code. Thanks again for pointing out that relevant line in the spec - I'm glad we can simplify this API!
Left a couple of inline comments, but on the whole, this is looking good.
src/target/ext/host_io.rs
Outdated
filename: &[u8], | ||
output: HostIoOutput<'a>, | ||
) -> HostIoResult<HostIoToken<'a>, Self>; | ||
fn readlink<'a>(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting... it seems like in this case, it would actually be impossible to re-use the entire PacketBuf
to send the response. This implies that #70 might not be possible for all cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be not impossible with the current code structure, but we can always find a point where the data in buf
is used and we don't start to use buf
to prepare the response, so it's still possible?
If we are allowed to use multiple mutable reference ( like C ), we can assure it will safe to do it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure what you mean... but the point I was trying to make is that so long as the API signature has a filename: &[u8]
that points somewhere into the middle of the PacketBuf, buf: &mut [u8]
cannot point to the start of the packet buf, as you would have an aliased mutable pointer, which would be very bad in Rust.
Off the top of my head, if we really wanted to enable this use-case, we could do something like:
struct ReadLinkPacket<'a> {
filename_start: usize,
filename_end: usize,
buf: &'a mut [u8]
}
impl<'a> ReadLinkPacket<'a> {
fn filename(&self) -> &[u8] {
&self.buf[self.filename_start..self.filename_end]
}
fn into_buf(self) -> &'a mut [u8] {
self.buf
}
}
fn readlink(&mut self, filename_and_buf: ReadLinkPacket<'_>) -> HostIoResult<usize, Self> {
let filename = filename_and_buf.filename();
let link = std::fs::read_link(filename) // etc...
let buf = filename_and_buf.into_buf();
// compose response using buf
}
Also, while thinking about this, I stumbled across an interesting observation: if you check the man page for the underlying readlink
syscall, you'll find that the filename buffer and the output buffer are actually marked restrict
, which means it wouldn't be possible to read the resolved link path directly into the buffer containing the filename. i.e: you must either allocate a new buffer to read the resolved path into, or use memory disjoint from the filename
buf, regardless if you were using Rust or C.
FWIW, I think the current implementation that doesn't use 100% of the PacketBuf is totally reasonable, especially since the underlying packet doesn't even have a mechanism of relaying "partial reads", and assumes that the packetbuf has enough space to fit the response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's take a look at how GDB implements it:
#if !defined (PATH_MAX) || (PATH_MAX > (PBUFSIZ / 2 + 1))
# define HOSTIO_PATH_MAX (PBUFSIZ / 2 + 1)
#else
# define HOSTIO_PATH_MAX PATH_MAX
#endif
static void
handle_readlink (char *own_buf, int *new_packet_len)
{
char filename[HOSTIO_PATH_MAX], linkname[HOSTIO_PATH_MAX];
char *p;
int ret, bytes_sent;
p = own_buf + strlen ("vFile:readlink:");
if (require_filename (&p, filename)
|| require_end (p))
{
hostio_packet_error (own_buf);
return;
}
if (hostio_fs_pid != 0 && the_target->multifs_readlink != NULL)
ret = the_target->multifs_readlink (hostio_fs_pid, filename,
linkname,
sizeof (linkname) - 1);
else
ret = readlink (filename, linkname, sizeof (linkname) - 1);
if (ret == -1)
{
hostio_error (own_buf);
return;
}
bytes_sent = hostio_reply_with_data (own_buf, linkname, ret, new_packet_len);
/* If the response does not fit into a single packet, do not attempt
to return a partial response, but simply fail. */
if (bytes_sent < ret)
sprintf (own_buf, "F-1,%x", FILEIO_ENAMETOOLONG);
}
So we have two ways to implement it:
- Copy the filename into a temporary buf allocated on the stack and pass this buf to
ops.readlink
. - Allocate two global buf with the same size:
PacketBufIn
andPacketBufOut
. This will cost twice of the memory, but the implementation will be very easy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Copy the filename into a temporary buf allocated on the stack and pass this buf to
ops.readlink
.
This forces gdbstub
to arbitrarily decide what a reasonable "max path" size would be, which is something I've avoided. This is actually one of those cases where a callback-based output function would be ideal, since it gives the calling code the ability to allocate a stack-allocated buffer of whatever size it deems appropriate, and pass it back into gdbstub
code.
- Allocate two global buf with the same size:
PacketBufIn
andPacketBufOut
. This will cost twice of the memory, but the implementation will be very easy.
I feel like this is something I brought up in the past (or maybe I just thought of it and didn't write it down), but yet, this would be the easiest way to handle these sorts of cases. The gdbstub
build could be modified to accept an optional PacketBufOut
, and the handler code would check for it's existence when deciding which PacketBuf
to pass to the target implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With sufficient plumbing, we could make this a bit "smarter" by making the
gdbstub
constructor take a const-generic value for the PacketBuf len, and then use that value as a stack allocated buf
Then I think it's not a bad idea? We can implement this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned in #72 (comment), I would like to shelve this discussion for now, and if you feel strongly about this limitation, feel free to open a new tracking issue for it, linking back to this thread + summarizing the various options both you and I have proposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i.e: please give this comment a thumbs up if you're good to merge the PR as is, and I'll give it one final review to approve the code, at which point we'll merge this into dev/0.6
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can continue this discussion in #70?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, yes, I am a silly man who is reviewing code far too late at night. Yes, we can certainly just continue discussing this at #70
I'll review this code one last time tomorrow morning, and probably merge it then as well 👍
Do I need to change the type of all the And I really don't like the |
Are you referring to the
From what I gather, most instances of repetitive Notably, I don't think we want this helper function to be part of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a few nits, but I think this is about ready to merge (feel free to un-draft)
We don't pass the entire |
You've seen the reference /* If the response does not fit into a single packet, do not attempt
to return a partial response, but simply fail. */
if (bytes_sent < ret)
sprintf (own_buf, "F-1,%x", FILEIO_ENAMETOOLONG); i.e: this detail is something the user should handle themselves. ...that said, I think this is an important point worth stressing in the |
The reference is about the situation that the length of |
I see... this ties back into the discussion we are having here: #72 (comment) Let's keep the conversation focused, and continue discussing this on that particular comment thread. |
What I want to say here is that if we don't use entire |
Co-authored-by: Daniel Prilik <[email protected]>
Yep, I totally get that. The question now becomes whether or not those situations are "reasonable", and whether this particular PR should try to address these situations. IMO, I think the answer to both of those questions is "no", and that we ought to merge this PR as is. If this is an edge case you're particularly worried about, we can continue this discussion over at #70 (summarizing our discussion in #72 (comment)) about this limitation, and reconsider how/whether this should be fixed at some later point (e.g: once a real-world example of the issue crops up, and/or at some point before committing to a hypothetical stable 1.0.0 API) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a few last minute things I noticed...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM!
I'll go ahead and merge it (unless you have any last-minute objections)
Description
(length: usize, offset: u64, buf: &mut [u8])
APIMemoryMap
andTargetXmlOverride
&mut [u8]
PacketBuf instead of the current callback-based approach.usize
forlength
andoffset
parameters, instead ofTarget::Arch::Usize
Originally posted by @daniel5151 in #69 (comment)
API Stability
Checklist
cargo build
compiles withouterrors
orwarnings
cargo clippy
runs withouterrors
orwarnings
cargo fmt
was runexamples/armv4t
examples/armv4t
withRUST_LOG=trace
+ any relevant GDB output under the "Validation" section below./scripts/test_dead_code_elim.sh
and/or./example_no_std/check_size.sh
)Arch
implementation