Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
17 changes: 8 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ bench = false
arbitrary = { version = "1.0", optional = true, features = ["derive"] }
byteorder = "1.2"
combine = "3.8.1"
gdbstub = { version = "0.6.2", optional = true }
gdbstub = { version = "0.7.10", git = "https://github.com/walnuthq/gdbstub", branch = "feat/sbpf-call-stack", optional = true }
hash32 = "0.3.1"
log = "0.4.2"
rand = { version = "0.8.5", features = ["small_rng"], optional = true }
Expand Down
23 changes: 14 additions & 9 deletions cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 50 additions & 13 deletions src/debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

use std::net::{TcpListener, TcpStream};

use gdbstub::common::Signal;
use gdbstub::common::{Signal, Tid};
use gdbstub::conn::ConnectionExt;
use gdbstub::stub::{state_machine, GdbStub, SingleThreadStopReason};

use gdbstub::arch::lldb::{Encoding, Format, Generic, Register};
use gdbstub::arch::RegId;

use gdbstub::target::{ext::monitor_cmd::MonitorCmd, Target, TargetError, TargetResult};
use gdbstub::target::{
ext::{monitor_cmd::MonitorCmd, sbpf::Sbpf},
Target, TargetError, TargetResult,
};
use gdbstub::{outputln, target};

use core::convert::{TryFrom, TryInto};
Expand Down Expand Up @@ -166,6 +169,11 @@ impl<'a, 'b, 'c, C: ContextObject> Target for Interpreter<'a, 'b, 'c, C> {
fn support_monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<'_, Self>> {
Some(self)
}

#[inline(always)]
fn support_sbpf(&mut self) -> Option<target::ext::sbpf::SbpfOps<'_, Self>> {
Some(self)
}
}

fn get_host_ptr<C: ContextObject>(
Expand Down Expand Up @@ -232,16 +240,19 @@ impl<'a, 'b, 'c, C: ContextObject> SingleThreadBase for Interpreter<'a, 'b, 'c,
Some(self)
}

fn read_addrs(&mut self, start_addr: u64, data: &mut [u8]) -> TargetResult<(), Self> {
fn read_addrs(&mut self, start_addr: u64, data: &mut [u8]) -> TargetResult<usize, Self> {
let mut read: usize = 0;
for (vm_addr, val) in (start_addr..).zip(data.iter_mut()) {
let host_ptr = match get_host_ptr(self, vm_addr) {
Ok(host_ptr) => host_ptr,
// The debugger is sometimes requesting more data than we have access to, just skip these
_ => continue,
// Report a partial read when the request crosses the end of a
// mapped guest-memory region.
_ => break,
};
*val = unsafe { *host_ptr };
read = read.saturating_add(1);
}
Ok(())
Ok(read)
}

fn write_addrs(&mut self, start_addr: u64, data: &[u8]) -> TargetResult<(), Self> {
Expand Down Expand Up @@ -444,7 +455,7 @@ impl<'a, 'b, 'c, C: ContextObject>
}

mod bpf_arch {
use gdbstub::arch::{Arch, SingleStepGdbBehavior};
use gdbstub::arch::Arch;

/// BPF-specific breakpoint kinds.
///
Expand All @@ -468,17 +479,11 @@ mod bpf_arch {
/// Implements `Arch` for BPF.
pub enum Bpf {}

#[allow(deprecated)]
impl Arch for Bpf {
type Usize = u64;
type Registers = reg::BpfRegs;
type RegId = reg::id::BpfRegId;
type BreakpointKind = BpfBreakpointKind;

#[inline(always)]
fn single_step_gdb_behavior() -> SingleStepGdbBehavior {
SingleStepGdbBehavior::Required
}
}

pub mod reg {
Expand Down Expand Up @@ -706,3 +711,35 @@ impl<'a, 'b, 'c, C: ContextObject> MonitorCmd for Interpreter<'a, 'b, 'c, C> {
Ok(())
}
}

impl<'a, 'b, 'c, C: ContextObject> Sbpf for Interpreter<'a, 'b, 'c, C> {
fn sbpf_call_stack(
&self,
_tid: Tid,
next_frame: &mut dyn FnMut(u64, u64),
) -> Result<(), Self::Error> {
// Return addresses live in interpreter-owned host memory and cannot be
// recovered through ordinary guest-memory reads.
next_frame(self.get_dbg_pc(), self.reg[ebpf::FRAME_PTR_REG]);

let call_depth: usize = self
.vm
.call_depth
.try_into()
.map_err(|_| "invalid SBPF call depth")?;
let call_frames = self
.call_frames
.get(..call_depth)
.ok_or("invalid SBPF call depth")?;
let pc_base = self.get_dbg_pc_base();
for frame in call_frames.iter().rev() {
let return_pc = frame
.target_pc
.checked_mul(ebpf::INSN_SIZE as u64)
.and_then(|pc| pc.checked_add(pc_base))
.ok_or("invalid SBPF return PC")?;
next_frame(return_pc, frame.frame_pointer);
}
Ok(())
}
}
10 changes: 7 additions & 3 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,24 @@ impl<'a, 'b, 'c, C: ContextObject> Interpreter<'a, 'b, 'c, C> {
/// Translate between the virtual machines' pc value and the pc value used by the debugger
#[cfg(feature = "debugger")]
pub fn get_dbg_pc(&self) -> u64 {
self.get_dbg_pc_base() + self.reg[11] * ebpf::INSN_SIZE as u64
}

#[cfg(feature = "debugger")]
pub(crate) fn get_dbg_pc_base(&self) -> u64 {
// V3+ ELFs place .text at its real vaddr (e.g. 0x1_0000_0000) in the on-disk
// sh_addr/p_vaddr, which is also what LLDB uses when it loads the .so.debug.
// Pre-V3 ELFs have .text at sh_addr=0 in the file (the runtime adds MM_REGION_SIZE
// itself), so reporting a file-offset-style PC happens to line up with LLDB's view.
let base = if self
if self
.executable
.get_sbpf_version()
.enable_lower_rodata_vaddr()
{
self.executable.get_text_section_vaddr()
} else {
self.executable.get_text_section_offset()
};
base + self.reg[11] * ebpf::INSN_SIZE as u64
}
}

fn push_frame(&mut self, config: &Config) -> bool {
Expand Down
Loading
Loading