aboutsummaryrefslogtreecommitdiff
path: root/src/protocol/commands/breakpoint.rs
blob: b0a0e9db3a1788589cfab60aff38b52caa6fba91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::protocol::common::hex::decode_hex;
use crate::protocol::common::hex::decode_hex_buf;

// Breakpoint packets are split up like this:
//
// Z0,addr,kind[;cond_list…][;cmds:persist,cmd_list…]
//  \_________/
//       |
//     BasicBreakpoint
//  \_______________________________________________/
//                          |
//                  BytecodeBreakpoint
//
// If the target does not implement the `Agent` extension, only the
// `BasicBreakpoint` part is parsed, which helps cut down on binary bloat.

#[derive(Debug)]
pub struct BasicBreakpoint<'a> {
    pub type_: u8,
    pub addr: &'a [u8],
    /// architecture dependent
    pub kind: &'a [u8],
}

impl<'a> BasicBreakpoint<'a> {
    pub fn from_slice(body: &'a mut [u8]) -> Option<BasicBreakpoint<'a>> {
        let mut body = body.splitn_mut(4, |b| matches!(*b, b',' | b';'));
        let type_ = decode_hex(body.next()?).ok()?;
        let addr = decode_hex_buf(body.next()?).ok()?;
        let kind = decode_hex_buf(body.next()?).ok()?;

        Some(BasicBreakpoint { type_, addr, kind })
    }
}

#[derive(Debug)]
pub struct BytecodeBreakpoint<'a> {
    pub base: BasicBreakpoint<'a>,
    pub conds: Option<BytecodeList<'a>>,
    pub cmds_persist: Option<(BytecodeList<'a>, bool)>,
}

impl<'a> BytecodeBreakpoint<'a> {
    pub fn from_slice(body: &'a mut [u8]) -> Option<BytecodeBreakpoint<'a>> {
        let mut body = body.splitn_mut(2, |b| *b == b';');

        let base = BasicBreakpoint::from_slice(body.next()?)?;

        let mut conds = None;
        let mut cmds_persist = None;

        if let Some(rest) = body.next() {
            let mut s = rest.split_mut(|b| *b == b':');
            let (raw_conds, raw_cmds) = match (s.next(), s.next()) {
                (Some(a), Some(b)) => (Some(strip_suffix_mut(a, b";cmds")?), Some(b)),
                (Some(a), None) => {
                    if a.starts_with(b"cmds") {
                        (None, Some(a))
                    } else {
                        (Some(a), None)
                    }
                }
                _ => return None,
            };

            if let Some(raw_conds) = raw_conds {
                conds = Some(BytecodeList(raw_conds));
            }

            if let Some(raw_cmds) = raw_cmds {
                let mut raw_cmds = raw_cmds.split_mut(|b| *b == b',');
                let raw_persist = decode_hex::<u8>(raw_cmds.next()?).ok()? != 0;
                let raw_cmds = raw_cmds.next()?;

                cmds_persist = Some((BytecodeList(raw_cmds), raw_persist));
            }
        }

        Some(BytecodeBreakpoint {
            base,
            conds,
            cmds_persist,
        })
    }
}

fn strip_suffix_mut<'a, T>(slice: &'a mut [T], suffix: &[T]) -> Option<&'a mut [T]>
where
    T: PartialEq,
{
    let (len, n) = (slice.len(), suffix.len());
    if n <= len {
        let (head, tail) = slice.split_at_mut(len - n);
        if tail == suffix {
            return Some(head);
        }
    }
    None
}

/// A lazily evaluated iterator over a series of bytecode expressions.
#[derive(Debug)]
pub struct BytecodeList<'a>(&'a mut [u8]);

impl<'a> BytecodeList<'a> {
    #[allow(dead_code)]
    pub fn into_iter(self) -> impl Iterator<Item = Option<&'a [u8]>> + 'a {
        self.0.split_mut(|b| *b == b'X').skip(1).map(|s| {
            let mut s = s.split_mut(|b| *b == b',');
            let _len = s.next()?;
            let code = decode_hex_buf(s.next()?).ok()?;
            Some(code as &[u8])
        })
    }
}