Skip to content
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

Merged
merged 7 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions examples/armv4t/gdb/exec_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use gdbstub::common::Pid;
use gdbstub::target;
use gdbstub::target::TargetResult;

use super::copy_range_to_buf;
use crate::emu::Emu;

impl target::ext::exec_file::ExecFile for Emu {
Expand All @@ -13,10 +14,6 @@ impl target::ext::exec_file::ExecFile for Emu {
buf: &mut [u8],
) -> TargetResult<usize, Self> {
let filename = b"/test.elf";
let len = filename.len();
let data = &filename[len.min(offset as usize)..len.min(offset as usize + length)];
let buf = &mut buf[..data.len()];
buf.copy_from_slice(data);
Ok(data.len())
Ok(copy_range_to_buf(filename, offset, length, buf))
}
}
48 changes: 21 additions & 27 deletions examples/armv4t/gdb/host_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use std::io::{Read, Seek, Write};

use gdbstub::target;
use gdbstub::target::ext::host_io::{
FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoOutput, HostIoResult,
HostIoStat, HostIoToken,
FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoResult, HostIoStat,
};

use super::copy_range_to_buf;
use crate::emu::Emu;
use crate::TEST_PROGRAM_ELF;

Expand Down Expand Up @@ -133,16 +133,13 @@ impl target::ext::host_io::HostIoPread for Emu {
fn pread<'a>(
&mut self,
fd: u32,
count: u32,
offset: u32,
output: HostIoOutput<'a>,
) -> HostIoResult<HostIoToken<'a>, Self> {
count: usize,
offset: u64,
buf: &mut [u8],
) -> HostIoResult<usize, Self> {
if fd < FD_RESERVED {
if fd == 0 {
let len = TEST_PROGRAM_ELF.len();
return Ok(output.write(
&TEST_PROGRAM_ELF[len.min(offset as usize)..len.min((offset + count) as usize)],
));
return Ok(copy_range_to_buf(TEST_PROGRAM_ELF, offset, count, buf));
} else {
return Err(HostIoError::Errno(HostIoErrno::EBADF));
}
Expand All @@ -153,10 +150,9 @@ impl target::ext::host_io::HostIoPread for Emu {
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
};

let mut buffer = vec![0; count as usize];
file.seek(std::io::SeekFrom::Start(offset as u64))?;
let n = file.read(&mut buffer)?;
Ok(output.write(&buffer[..n]))
file.seek(std::io::SeekFrom::Start(offset))?;
let n = file.read(buf)?;
Ok(n)
}
}

Expand Down Expand Up @@ -246,29 +242,27 @@ impl target::ext::host_io::HostIoUnlink for Emu {
}

impl target::ext::host_io::HostIoReadlink for Emu {
fn readlink<'a>(
&mut self,
filename: &[u8],
output: HostIoOutput<'a>,
) -> HostIoResult<HostIoToken<'a>, Self> {
fn readlink<'a>(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self> {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
if filename == b"/proc/1/exe" {
// Support `info proc exe` command
return Ok(output.write(b"/test.elf"));
let exe = b"/test.elf";
return Ok(copy_range_to_buf(exe, 0, exe.len(), buf));
} else if filename == b"/proc/1/cwd" {
// Support `info proc cwd` command
return Ok(output.write(b"/"));
let cwd = b"/";
return Ok(copy_range_to_buf(cwd, 0, cwd.len(), buf));
} else if filename.starts_with(b"/proc") {
return Err(HostIoError::Errno(HostIoErrno::ENOENT));
}

let path =
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;
Ok(output.write(
std::fs::read_link(path)?
.to_str()
.ok_or(HostIoError::Errno(HostIoErrno::ENOENT))?
.as_bytes(),
))
let link = std::fs::read_link(path)?;
let data = link
.to_str()
.ok_or(HostIoError::Errno(HostIoErrno::ENOENT))?
.as_bytes();
Ok(copy_range_to_buf(data, 0, data.len(), buf))
}
}

Expand Down
14 changes: 12 additions & 2 deletions examples/armv4t/gdb/memory_map.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
use gdbstub::target;
use gdbstub::target::TargetResult;

use super::copy_range_to_buf;
use crate::emu::Emu;

impl target::ext::memory_map::MemoryMap for Emu {
fn memory_map_xml(&self) -> &str {
fn memory_map_xml(
&self,
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
// Sample memory map, with RAM coverying the whole
// memory space.
r#"<?xml version="1.0"?>
let memory_map = r#"<?xml version="1.0"?>
<!DOCTYPE memory-map
PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
"http://sourceware.org/gdb/gdb-memory-map.dtd">
<memory-map>
<memory type="ram" start="0x0" length="0x100000000"/>
</memory-map>"#
.trim()
.as_bytes();
Ok(copy_range_to_buf(memory_map, offset, length, buf))
}
}
12 changes: 12 additions & 0 deletions examples/armv4t/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
}
}

/// Copy a range of `data` (start at `offset` with a size of `length`) to `buf`.
/// Return the size of data copied. Return 0 if encounter EOF.
bet4it marked this conversation as resolved.
Show resolved Hide resolved
///
/// Mainly used by qXfer:_object_:read commands.
pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
let len = data.len();
let data = &data[len.min(offset as usize)..len.min(offset as usize + length)];
let buf = &mut buf[..data.len()];
buf.copy_from_slice(data);
data.len()
}

impl Target for Emu {
// As an example, I've defined a custom architecture based off
// `gdbstub_arch::arm::Armv4t`. The implementation is in the `custom_arch`
Expand Down
15 changes: 13 additions & 2 deletions examples/armv4t/gdb/target_description_xml_override.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
use gdbstub::target;
use gdbstub::target::TargetResult;

use super::copy_range_to_buf;
use crate::emu::Emu;

impl target::ext::target_description_xml_override::TargetDescriptionXmlOverride for Emu {
fn target_description_xml(&self) -> &str {
r#"<?xml version="1.0"?>
fn target_description_xml(
&self,
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
let xml = r#"<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>armv4t</architecture>
Expand Down Expand Up @@ -67,5 +74,9 @@ impl target::ext::target_description_xml_override::TargetDescriptionXmlOverride
</feature>
</target>
"#
.trim()
.as_bytes();

Ok(copy_range_to_buf(xml, offset, length, buf))
}
}
40 changes: 28 additions & 12 deletions src/gdbstub_impl/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,21 +126,37 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
HandlerStatus::NeedsOk
}
Base::qXferFeaturesRead(cmd) => {
#[allow(clippy::redundant_closure)]
let xml = target
.target_description_xml_override()
.map(|ops| ops.target_description_xml())
.or_else(|| T::Arch::target_description_xml());

match xml {
Some(xml) => {
let xml = xml.trim().as_bytes();
res.write_binary_range(xml, cmd.offset, cmd.len)?;
}
let ret = if let Some(ops) = target.target_description_xml_override() {
ops.target_description_xml(cmd.offset, cmd.length, cmd.buf)
.handle_error()?
} else if let Some(xml) = T::Arch::target_description_xml() {
let xml = xml.trim().as_bytes();
let xml_len = xml.len();

let start = xml_len.min(cmd.offset as usize);
let end = xml_len.min(cmd.offset as usize + cmd.length);

// LLVM isn't smart enough to realize that `end` will always be greater than
// `start`, and fails to elide the `slice_index_order_fail` check unless we
// include this seemingly useless call to `max`.
let data = &xml[start..end.max(start)];

let n = data.len().min(cmd.buf.len());
cmd.buf[..n].copy_from_slice(&data[..n]);
n
} else {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
// If the target hasn't provided their own XML, then the initial response to
// "qSupported" wouldn't have included "qXfer:features:read", and gdb wouldn't
// send this packet unless it was explicitly marked as supported.
None => return Err(Error::PacketUnexpected),
return Err(Error::PacketUnexpected);
};

if ret == 0 {
res.write_str("l")?;
} else {
res.write_str("m")?;
// TODO: add more specific error variant?
res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?;
}
HandlerStatus::Handled
}
Expand Down
48 changes: 11 additions & 37 deletions src/gdbstub_impl/ext/host_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::prelude::*;
use crate::protocol::commands::ext::HostIo;

use crate::arch::Arch;
use crate::target::ext::host_io::{HostIoError, HostIoOutput, HostIoStat};
use crate::target::ext::host_io::{HostIoError, HostIoStat};

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_host_io(
Expand Down Expand Up @@ -52,31 +52,16 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
HandlerStatus::Handled
}
HostIo::vFilePread(cmd) if ops.enable_pread().is_some() => {
let count = <T::Arch as Arch>::Usize::from_be_bytes(cmd.count)
.ok_or(Error::TargetMismatch)?;
let offset = <T::Arch as Arch>::Usize::from_be_bytes(cmd.offset)
.ok_or(Error::TargetMismatch)?;
let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
let mut callback = |data: &[u8]| {
let e = (|| {
let ops = ops.enable_pread().unwrap();
handle_hostio_result! {
if let Ok(ret) = ops.pread(cmd.fd, cmd.count, cmd.offset, cmd.buf) => {
res.write_str("F")?;
res.write_num(data.len())?;
res.write_num(ret)?;
res.write_str(";")?;
res.write_binary(data)?;
Ok(())
})();

if let Err(e) = e {
err = Err(e)
res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?;
}
};

let ops = ops.enable_pread().unwrap();
handle_hostio_result! {
if let Ok(_) = ops.pread(cmd.fd, count, offset, HostIoOutput::new(&mut callback)) => {}
};
err?;

HandlerStatus::Handled
}
HostIo::vFilePwrite(cmd) if ops.enable_pwrite().is_some() => {
Expand Down Expand Up @@ -126,27 +111,16 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
HandlerStatus::Handled
}
HostIo::vFileReadlink(cmd) if ops.enable_readlink().is_some() => {
let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
let mut callback = |data: &[u8]| {
let e = (|| {
let ops = ops.enable_readlink().unwrap();
handle_hostio_result! {
if let Ok(ret) = ops.readlink(cmd.filename, cmd.buf) => {
res.write_str("F")?;
res.write_num(data.len())?;
res.write_num(ret)?;
res.write_str(";")?;
res.write_binary(data)?;
Ok(())
})();

if let Err(e) = e {
err = Err(e)
res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?;
}
};

let ops = ops.enable_readlink().unwrap();
handle_hostio_result! {
if let Ok(_) = ops.readlink(cmd.filename, HostIoOutput::new(&mut callback)) => {}
};
err?;

HandlerStatus::Handled
}
HostIo::vFileSetfs(cmd) if ops.enable_setfs().is_some() => {
Expand Down
12 changes: 10 additions & 2 deletions src/gdbstub_impl/ext/memory_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {

let handler_status = match command {
MemoryMap::qXferMemoryMapRead(cmd) => {
let xml = ops.memory_map_xml().trim().as_bytes();
res.write_binary_range(xml, cmd.offset, cmd.len)?;
let ret = ops
.memory_map_xml(cmd.offset, cmd.length, cmd.buf)
.handle_error()?;
if ret == 0 {
res.write_str("l")?;
} else {
res.write_str("m")?;
// TODO: add more specific error variant?
res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?;
}
HandlerStatus::Handled
}
};
Expand Down
6 changes: 3 additions & 3 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ commands! {
"QStartNoAckMode" => _QStartNoAckMode::QStartNoAckMode,
"qsThreadInfo" => _qsThreadInfo::qsThreadInfo,
"qSupported" => _qSupported::qSupported<'a>,
"qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead,
"qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead<'a>,
"s" => _s::s<'a>,
"T" => _t_upcase::T,
"vCont" => _vCont::vCont<'a>,
Expand Down Expand Up @@ -221,8 +221,8 @@ commands! {
"bs" => _bs::bs,
}

memory_map {
"qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead,
memory_map use 'a {
"qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead<'a>,
}

exec_file use 'a {
Expand Down
19 changes: 12 additions & 7 deletions src/protocol/commands/_qXfer_features_read.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qXferFeaturesRead {
pub offset: usize,
pub len: usize,
pub struct qXferFeaturesRead<'a> {
pub offset: u64,
pub length: usize,

pub buf: &'a mut [u8],
}

impl<'a> ParseCommand<'a> for qXferFeaturesRead {
impl<'a> ParseCommand<'a> for qXferFeaturesRead<'a> {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
let (buf, body_range) = buf.into_raw_buf();
let body = buf.get_mut(body_range.start..body_range.end)?;

if body.is_empty() {
return None;
Expand All @@ -22,8 +25,10 @@ impl<'a> ParseCommand<'a> for qXferFeaturesRead {

let mut body = body.next()?.split(|b| *b == b',');
let offset = decode_hex(body.next()?).ok()?;
let len = decode_hex(body.next()?).ok()?;
let length = decode_hex(body.next()?).ok()?;

drop(body);

Some(qXferFeaturesRead { offset, len })
Some(qXferFeaturesRead { offset, length, buf })
}
}
Loading