Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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: 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 use 'a {
"qWasmCallStack" => _qWasmCallStack::qWasmCallStack,
"qWasmLocal" => _qWasmLocal::qWasmLocal<'a>,
"qWasmGlobal" => _qWasmGlobal::qWasmGlobal<'a>,
"qWasmStackValue" => _qWasmStackValue::qWasmStackValue<'a>,
}
}
21 changes: 21 additions & 0 deletions src/protocol/commands/_qWasmCallStack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use super::prelude::*;
use crate::protocol::common::thread_id::ThreadId;
use crate::protocol::ConcreteThreadId;

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

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 = ConcreteThreadId::try_from(ThreadId::try_from(tid).ok()?).ok()?;
Some(qWasmCallStack { tid })
}
}
35 changes: 35 additions & 0 deletions src/protocol/commands/_qWasmGlobal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qWasmGlobal<'a> {
pub frame: usize,
pub global: usize,
pub buf: &'a mut [u8],
}

impl<'a> ParseCommand<'a> for qWasmGlobal<'a> {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let (buf, body_range) = buf.into_raw_buf();
let body = buf.get(body_range.start..body_range.end)?;

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::<usize>().ok()?;
let global = parts.next()?;
let global = str::from_utf8(global).ok()?.parse::<usize>().ok()?;
if parts.next().is_some() {
// Too many parameters.
return None;
}

Some(qWasmGlobal {
frame,
global,
buf,
})
}
}
35 changes: 35 additions & 0 deletions src/protocol/commands/_qWasmLocal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qWasmLocal<'a> {
pub frame: usize,
pub local: usize,
pub buf: &'a mut [u8],
}

impl<'a> ParseCommand<'a> for qWasmLocal<'a> {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let (buf, body_range) = buf.into_raw_buf();
let body = buf.get(body_range.start..body_range.end)?;

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::<usize>().ok()?;
let local = parts.next()?;
let local = str::from_utf8(local).ok()?.parse::<usize>().ok()?;
if parts.next().is_some() {
// Too many parameters.
return None;
}

Some(qWasmLocal {
frame,
local,
buf,
})
}
}
35 changes: 35 additions & 0 deletions src/protocol/commands/_qWasmStackValue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qWasmStackValue<'a> {
pub frame: usize,
pub index: usize,
pub buf: &'a mut [u8],
}

impl<'a> ParseCommand<'a> for qWasmStackValue<'a> {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let (buf, body_range) = buf.into_raw_buf();
let body = buf.get(body_range.start..body_range.end)?;

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::<usize>().ok()?;
let index = parts.next()?;
let index = str::from_utf8(index).ok()?.parse::<usize>().ok()?;
if parts.next().is_some() {
// Too many parameters.
return None;
}

Some(qWasmStackValue {
frame,
index,
buf,
})
}
}
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
51 changes: 51 additions & 0 deletions src/stub/core_impl/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use super::prelude::*;
use crate::protocol::commands::ext::Wasm;

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(());
ops.wasm_call_stack(cmd.tid.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 len = ops
.read_wasm_local(self.current_mem_tid, cmd.frame, cmd.local, cmd.buf)
.map_err(Error::TargetError)?;
res.write_hex_buf(&cmd.buf[0..len])?;
}
Wasm::qWasmGlobal(cmd) => {
let len = ops
.read_wasm_global(self.current_mem_tid, cmd.frame, cmd.global, cmd.buf)
.map_err(Error::TargetError)?;
res.write_hex_buf(&cmd.buf[0..len])?;
}
Wasm::qWasmStackValue(cmd) => {
let len = ops
.read_wasm_stack(self.current_mem_tid, cmd.frame, cmd.index, cmd.buf)
.map_err(Error::TargetError)?;
res.write_hex_buf(&cmd.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;
102 changes: 102 additions & 0 deletions src/target/ext/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Provide Wasm-specific actions for the target.
//!
//! ### Address Encoding
//!
//! The gdbstub extension to the Wasm target architecture uses a
//! specific encoding for addresses, both for commands in this
//! extension trait and for commands in the base protocol (e.g., for
//! reading and writing memory and setting breakpoints). The need for
//! this scheme arises from the fact that Wasm is natively
//! "multimemory": there can be many code modules, and many linear
//! memories, and each is a native entity (rather than mapped into a
//! larger single address space) in the VM definition. The gdbstub
//! protocol extensions map these native entities into an address
//! space where the upper 32 bits encode the index of a particular
//! Wasm code module or linear (data) memory and the lower 32 bits
//! encode an offset.
//!
//! See the [LLDB source code] (particularly `WasmAddressType` and
//! `wasm_addr_t`) for a description of the encoding of the PC values.
//!
//! [LLDB souce code]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/Process/wasm/ProcessWasm.h
use crate::common::Tid;
use crate::target::Target;

/// Target Extension - perform Wasm-specific actions.
pub trait Wasm: Target {
/// Get the Wasm call stack for a given thread.
///
/// The addresses provided for the PC at each frame shouuld be
/// encoded as per the [Wasm address encoding].
///
/// 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.
///
/// [Wasm address encoding]: `self#Address_Encoding`
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`.
///
/// `buf` will be long enough to allow for the larget possible
/// supported Wasm value (i.e., at least a `v128` SIMD
/// value). Values should be encoded in little-endian format with
/// their native length (e.g., 4 bytes for a Wasm `i32` or `f32`
/// type, or 8 bytes for a Wasm `i64` or `f64` type).
fn read_wasm_local(
&self,
tid: Tid,
frame: usize,
local: usize,
buf: &mut [u8],
) -> 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`.
///
/// `buf` will be long enough to allow for the larget possible
/// supported Wasm value (i.e., at least a `v128` SIMD
/// value). Values should be encoded in little-endian format with
/// their native length (e.g., 4 bytes for a Wasm `i32` or `f32`
/// type, or 8 bytes for a Wasm `i64` or `f64` type).
fn read_wasm_stack(
&self,
tid: Tid,
frame: usize,
index: usize,
buf: &mut [u8],
) -> 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`.
///
/// `buf` will be long enough to allow for the larget possible
/// supported Wasm value (i.e., at least a `v128` SIMD
/// value). Values should be encoded in little-endian format with
/// their native length (e.g., 4 bytes for a Wasm `i32` or `f32`
/// type, or 8 bytes for a Wasm `i64` or `f64` type).
fn read_wasm_global(
&self,
tid: Tid,
frame: usize,
global: usize,
buf: &mut [u8],
) -> 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