Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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: 7 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,11 @@ commands! {
"qTfV" => _qTfV::qTfV,
"qTsV" => _qTsV::qTsV,
}

wasm {
"qWasmCallStack" => _qWasmCallStack::qWasmCallStack,
"qWasmLocal" => _qWasmLocal::qWasmLocal,
"qWasmGlobal" => _qWasmGlobal::qWasmGlobal,
"qWasmStackValue" => _qWasmStackValue::qWasmStackValue,
}
}
20 changes: 20 additions & 0 deletions src/protocol/commands/_qWasmCallStack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use super::prelude::*;
use crate::protocol::common::thread_id::ThreadId;

#[derive(Debug)]
pub struct qWasmCallStack {
pub tid: ThreadId,
}

impl<'a> ParseCommand<'a> for qWasmCallStack {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() || body[0] != b':' {
return None;
}
let tid = &body[1..];
let tid = ThreadId::try_from(tid).ok()?;
Some(qWasmCallStack { tid })
}
}
27 changes: 27 additions & 0 deletions src/protocol/commands/_qWasmGlobal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qWasmGlobal {
pub frame: u32,
pub global: u32,
}

impl<'a> ParseCommand<'a> for qWasmGlobal {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() || body[0] != b':' {
return None;
}
let mut parts = body[1..].split(|b| *b == b';');
let frame = parts.next()?;
let frame = str::from_utf8(frame).ok()?.parse::<u32>().ok()?;
let global = parts.next()?;
let global = str::from_utf8(global).ok()?.parse::<u32>().ok()?;
if parts.next().is_some() {
// Too many parameters.
return None;
}
Some(qWasmGlobal { frame, global })
}
}
27 changes: 27 additions & 0 deletions src/protocol/commands/_qWasmLocal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qWasmLocal {
pub frame: u32,
pub local: u32,
}

impl<'a> ParseCommand<'a> for qWasmLocal {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() || body[0] != b':' {
return None;
}
let mut parts = body[1..].split(|b| *b == b';');
let frame = parts.next()?;
let frame = str::from_utf8(frame).ok()?.parse::<u32>().ok()?;
let local = parts.next()?;
let local = str::from_utf8(local).ok()?.parse::<u32>().ok()?;
if parts.next().is_some() {
// Too many parameters.
return None;
}
Some(qWasmLocal { frame, local })
}
}
27 changes: 27 additions & 0 deletions src/protocol/commands/_qWasmStackValue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qWasmStackValue {
pub frame: u32,
pub index: u32,
}

impl<'a> ParseCommand<'a> for qWasmStackValue {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() || body[0] != b':' {
return None;
}
let mut parts = body[1..].split(|b| *b == b';');
let frame = parts.next()?;
let frame = str::from_utf8(frame).ok()?.parse::<u32>().ok()?;
let index = parts.next()?;
let index = str::from_utf8(index).ok()?.parse::<u32>().ok()?;
if parts.next().is_some() {
// Too many parameters.
return None;
}
Some(qWasmStackValue { frame, index })
}
}
2 changes: 2 additions & 0 deletions src/stub/core_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod single_register_access;
mod target_xml;
mod thread_extra_info;
mod tracepoints;
mod wasm;
mod x_upcase_packet;

pub(crate) use resume::FinishExecStatus;
Expand Down Expand Up @@ -223,6 +224,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
Command::LibrariesSvr4(cmd) => self.handle_libraries_svr4(res, target, cmd),
Command::Libraries(cmd) => self.handle_libraries(res, target, cmd),
Command::Tracepoints(cmd) => self.handle_tracepoints(res, target, cmd),
Command::Wasm(cmd) => self.handle_wasm(res, target, cmd),
// in the worst case, the command could not be parsed...
Command::Unknown(cmd) => {
// HACK: if the user accidentally sends a resume command to a
Expand Down
61 changes: 61 additions & 0 deletions src/stub/core_impl/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use super::prelude::*;
use crate::protocol::{commands::ext::Wasm, IdKind};

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_wasm(
&mut self,
res: &mut ResponseWriter<'_, C>,
target: &mut T,
command: Wasm,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
let ops = match target.support_wasm() {
Some(ops) => ops,
None => return Ok(HandlerStatus::Handled),
};

crate::__dead_code_marker!("wasm", "impl");

match command {
Wasm::qWasmCallStack(cmd) => {
let mut error: Result<(), Error<T::Error, C::Error>> = Ok(());
let tid = match cmd.tid.tid {
IdKind::All => {
return Err(Error::NonFatalError(1));
}
IdKind::Any => self.current_mem_tid,
IdKind::WithId(id) => id,
};
ops.wasm_call_stack(tid, &mut |pc| {
if let Err(e) = res.write_hex_buf(&pc.to_le_bytes()) {
error = Err(e.into());
}
})
.map_err(Error::TargetError)?;
error?;
}
Wasm::qWasmLocal(cmd) => {
let mut buf = [0u8; 16];
let len = ops
.read_wasm_local(self.current_mem_tid, cmd.frame, cmd.local, &mut buf)
.map_err(Error::TargetError)?;
res.write_hex_buf(&buf[0..len])?;
}
Wasm::qWasmGlobal(cmd) => {
let mut buf = [0u8; 16];
let len = ops
.read_wasm_global(self.current_mem_tid, cmd.frame, cmd.global, &mut buf)
.map_err(Error::TargetError)?;
res.write_hex_buf(&buf[0..len])?;
}
Wasm::qWasmStackValue(cmd) => {
let mut buf = [0u8; 16];
let len = ops
.read_wasm_stack(self.current_mem_tid, cmd.frame, cmd.index, &mut buf)
.map_err(Error::TargetError)?;
res.write_hex_buf(&buf[0..len])?;
}
};

Ok(HandlerStatus::Handled)
}
}
1 change: 1 addition & 0 deletions src/target/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,4 @@ pub mod section_offsets;
pub mod target_description_xml_override;
pub mod thread_extra_info;
pub mod tracepoints;
pub mod wasm;
63 changes: 63 additions & 0 deletions src/target/ext/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Provide Wasm-specific actions for the target.
use crate::{common::Tid, target::Target};

/// Target Extension - perform Wasm-specific actions.
pub trait Wasm: Target {
/// Get the Wasm call stack for a given thread.
///
/// See the [LLDB Wasm Extension Documentation] for a description
/// of the format of the PC values.
///
/// [LLDB Wasm Extension Documentation]: https://lldb.llvm.org/resources/lldbgdbremote.html#wasm-packets
///
/// To avoid allocation, the call stack PCs should be returned to
/// the caller by calling the given callback, in order from
/// innermost (most recently called) frame to outermost.
fn wasm_call_stack(&self, tid: Tid, next_pc: &mut dyn FnMut(u64)) -> Result<(), Self::Error>;

/// Get the Wasm local for a given thread, frame index, and local
/// index.
///
/// The Wasm local's value should be placed into `buf`, and the
/// length should be returned. If the Wasm local or frame does not
/// exist, this method should return `0`.
fn read_wasm_local(
&self,
tid: Tid,
frame: u32,
local: u32,
buf: &mut [u8; 16],
) -> Result<usize, Self::Error>;

/// Get the Wasm operand-stack value for a given thread, frame
/// index, and stack index. Top-of-stack is index 0, and values
/// below that have incrementing indices.
///
/// The Wasm operand's value should be placed into `buf`, and the
/// length should be returned. If the Wasm local or frame does not
/// exist, this method should return `0`.
fn read_wasm_stack(
&self,
tid: Tid,
frame: u32,
index: u32,
buf: &mut [u8; 16],
) -> Result<usize, Self::Error>;

/// Get the Wasm global value for a given thread, frame, and
/// global index. The global index is relative to the module whose
/// function corresponds to that frame.
///
/// The Wasm global's value should be placed into `buf`, and the
/// length should be returned. If the Wasm local or frame does not
/// exist, this method should return `0`.
fn read_wasm_global(
&self,
tid: Tid,
frame: u32,
global: u32,
buf: &mut [u8; 16],
) -> Result<usize, Self::Error>;
}

define_ext!(WasmOps, Wasm);
6 changes: 6 additions & 0 deletions src/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,12 @@ pub trait Target {
fn support_libraries(&mut self) -> Option<ext::libraries::LibrariesOps<'_, Self>> {
None
}

/// Support for WebAssembly (Wasm)-specific commands.
#[inline(always)]
fn support_wasm(&mut self) -> Option<ext::wasm::WasmOps<'_, Self>> {
None
}
}

macro_rules! __delegate {
Expand Down
Loading