panda/library_mode/
mod.rs

1#[cfg(feature = "libpanda")]
2mod qcows;
3
4use crate::PandaArgs;
5use std::fmt;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8#[cfg(feature = "libpanda")]
9use std::{ffi::CString, mem::transmute, os::raw::c_char, sync::Mutex};
10
11#[cfg(feature = "libpanda")]
12use crate::{
13    inventory,
14    sys::{self, panda_init, panda_run, panda_set_library_mode},
15    InternalCallback, PPPCallbackSetup,
16};
17
18/// Architecture of the guest system
19#[allow(non_camel_case_types)]
20#[derive(Copy, Clone, Debug, PartialEq)]
21pub enum Arch {
22    i386,
23    x86_64,
24    Arm,
25    Mips,
26    AArch64,
27}
28
29// TODO: tie architecture to architecture being compiled for?
30impl fmt::Display for Arch {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        write!(
33            f,
34            "{}",
35            match self {
36                Self::i386 => "i386",
37                Self::x86_64 => "x86_64",
38                Self::Arm => "arm",
39                Self::Mips => "mips",
40                Self::AArch64 => "aarch64",
41            }
42        )
43    }
44}
45
46/// Builder for creating PANDA instances. Only for use in libpanda mode.
47#[derive(Default)]
48#[allow(dead_code)]
49pub struct Panda {
50    expect_prompt: Option<String>,
51    generic_qcow: Option<String>,
52    os_version: Option<String>,
53    qcow: Option<String>,
54    raw_monitor: bool,
55    graphics: bool,
56    os: String,
57    mem: Option<String>,
58    arch: Option<Arch>,
59    extra_args: Vec<String>,
60    replay: Option<String>,
61    configurable: bool,
62}
63
64static LIBRARY_STARTED: AtomicBool = AtomicBool::new(false);
65
66#[cfg(feature = "libpanda")]
67lazy_static::lazy_static! {
68    static ref AFTER_INIT_FUNCS: Mutex<Vec<Box<dyn FnOnce() + Send + Sync + 'static>>>
69            = Mutex::new(Vec::new());
70}
71
72impl Panda {
73    /// Get whether or not libpanda has started running yet
74    fn is_started() -> bool {
75        LIBRARY_STARTED.load(Ordering::Relaxed)
76    }
77
78    /// Create a new PANDA instance.
79    ///
80    /// ### Example
81    /// ```rust
82    /// # use panda::prelude::*;
83    /// Panda::new()
84    ///     .generic("x86_64")
85    ///     .run();
86    /// ```
87    pub fn new() -> Self {
88        Self {
89            os: "linux".into(),
90            ..Default::default()
91        }
92    }
93
94    /// Add an argument for PANDA
95    ///
96    /// ### Example
97    /// ```rust
98    /// # use panda::prelude::*;
99    /// Panda::new()
100    ///     .arg("-nomonitor")
101    ///     .run();
102    /// ```
103    pub fn arg<S: Into<String>>(&mut self, arg: S) -> &mut Self {
104        self.extra_args.push(arg.into());
105
106        self
107    }
108
109    /// Add a set of extra arguments for PANDA
110    ///
111    /// ### Example
112    /// ```rust
113    /// # use panda::prelude::*;
114    /// Panda::new()
115    ///     .args(&["-panda", "callstack_instr"])
116    ///     .run();
117    /// ```
118    pub fn args<I, S>(&mut self, args: I) -> &mut Self
119    where
120        I: IntoIterator<Item = S>,
121        S: AsRef<str>,
122    {
123        for arg in args {
124            self.arg(arg.as_ref());
125        }
126
127        self
128    }
129
130    /// Sets the architecture of the guest
131    ///
132    /// ### Example
133    /// ```rust
134    /// # use panda::{prelude::*, Arch};
135    /// Panda::new()
136    ///     .arch(Arch::i386)
137    ///     .run();
138    /// ```
139    pub fn arch(&mut self, arch: Arch) -> &mut Self {
140        self.arch = Some(arch);
141
142        self
143    }
144
145    /// Set the machine to PANDA's configurable machine
146    ///
147    /// ### Example
148    /// ```rust
149    /// # use panda::{prelude::*, Arch};
150    /// Panda::new()
151    ///     .configurable()
152    ///     .run();
153    /// ```
154    pub fn configurable(&mut self) -> &mut Self {
155        self.configurable = true;
156
157        self
158    }
159
160    // Don't pass `-nographic` to QEMU, allowing you use to use a monitor
161    ///
162    /// ### Example
163    /// ```rust
164    /// # use panda::prelude::*;
165    /// Panda::new()
166    ///     .enable_graphics()
167    ///     .run();
168    /// ```
169    pub fn enable_graphics(&mut self) -> &mut Self {
170        self.graphics = true;
171
172        self
173    }
174
175    /// Regular expression describing the prompt exposed by the guest on a serial console. Used in
176    /// order to know when running a command has finished with its output.
177    pub fn expect_prompt<S: Into<String>>(&mut self, prompt_regex: S) -> &mut Self {
178        self.expect_prompt = Some(prompt_regex.into());
179
180        self
181    }
182
183    /// Set the available memory. If restoring from a snapshot or viewing a replay, this must be
184    /// the same as when the replay/snapshot was taken.
185    pub fn mem<S: Into<String>>(&mut self, mem: S) -> &mut Self {
186        self.mem = Some(mem.into());
187
188        self
189    }
190
191    /// Use generic PANDA Qcow for run
192    ///
193    /// ### Example
194    /// ```rust
195    /// # use panda::prelude::*;
196    /// Panda::new()
197    ///     .generic("x86_64")
198    ///     .run();
199    /// ```
200    pub fn generic<S: Into<String>>(&mut self, generic: S) -> &mut Self {
201        self.generic_qcow = Some(generic.into());
202
203        self
204    }
205
206    /// Run the given replay in the PANDA instance. Equivalent to `-replay [name]` from the PANDA
207    /// command line.
208    ///
209    /// ### Example
210    /// ```rust
211    /// # use panda::prelude::*;
212    /// Panda::new()
213    ///     .replay("grep_recording")
214    ///     .run();
215    /// ```
216    pub fn replay<S: Into<String>>(&mut self, replay: S) -> &mut Self {
217        self.replay = Some(replay.into());
218
219        self
220    }
221
222    /// Load a plugin with args provided by a `PandaArgs` struct.
223    ///
224    /// ### Example
225    ///
226    /// ```rust
227    /// use panda::prelude::*;
228    ///
229    /// #[derive(PandaArgs)]
230    /// #[name = "stringsearch"]
231    /// struct StringSearch {
232    ///     str: String
233    /// }
234    ///
235    /// fn main() {
236    ///     Panda::new()
237    ///         .generic("x86_64")
238    ///         .replay("test")
239    ///         .plugin_args(&StringSearch {
240    ///             str: "test".into()
241    ///         })
242    ///         .run();
243    /// }
244    /// ```
245    pub fn plugin_args<T: PandaArgs>(&mut self, args: &T) -> &mut Self {
246        self.arg("-panda").arg(args.to_panda_args_str())
247    }
248
249    #[cfg(feature = "libpanda")]
250    fn get_args(&self) -> Vec<String> {
251        let generic_info = self
252            .generic_qcow
253            .as_ref()
254            .map(|generic| qcows::get_supported_image(generic));
255
256        let qcow_path = self.qcow.clone().map(Some).unwrap_or_else(|| {
257            self.generic_qcow
258                .as_ref()
259                .map(|generic| qcows::get_generic_path(generic).display().to_string())
260        });
261
262        let _arch = self
263            .arch
264            .or_else(|| generic_info.as_ref().map(|x| x.arch))
265            .unwrap_or(Arch::x86_64);
266
267        let mem = self
268            .mem
269            .as_ref()
270            .map(|x| &x[..])
271            .or_else(|| generic_info.as_ref().map(|x| x.default_mem))
272            .unwrap_or("128M")
273            .to_owned();
274
275        let mut args = vec![
276            "".into(), // filler, argv[0] == path of executable, n/a
277            "-L".into(),
278            std::env::var("PANDA_PATH")
279                .expect("PANDA_PATH not set. Set it to panda's build folder.")
280                + "/pc-bios",
281            "-m".into(),
282            mem,
283        ];
284
285        if let Some(qcow) = qcow_path {
286            args.push(qcow)
287        }
288
289        if let Some(generic) = generic_info {
290            args.push("-os".into());
291            args.push(generic.os.into());
292        }
293
294        if self.configurable {
295            args.push("-M".into());
296            args.push("configurable".into());
297        }
298
299        if !self.graphics {
300            args.push("-nographic".into());
301        }
302
303        if let Some(replay) = &self.replay {
304            args.push("-replay".into());
305            args.push(replay.clone());
306        }
307
308        args.extend(self.extra_args.clone().into_iter());
309
310        args
311    }
312
313    /// Start the PANDA instance with the given settings. This is a blocking operation.
314    ///
315    /// ### Example
316    /// ```rust
317    /// # use panda::prelude::*;
318    /// Panda::new()
319    ///     .generic("x86_64")
320    ///     .run();
321    /// ```
322    pub fn run(&mut self) {
323        #[cfg(not(feature = "libpanda"))]
324        {
325            panic!("Panda::run cannot be used without the libpanda feature");
326        }
327        #[cfg(feature = "libpanda")]
328        {
329            let args = self.get_args();
330
331            println!("Running with args: {:?}", args);
332
333            let args: Vec<_> = args.into_iter().map(|x| CString::new(x).unwrap()).collect();
334            let args_ptrs: Vec<_> = args.iter().map(|s| s.as_ptr()).collect();
335
336            std::env::set_var("PANDA_DIR", std::env::var("PANDA_PATH").unwrap());
337
338            let x = &mut 0i8;
339            let empty = &mut (x as *mut c_char);
340            unsafe {
341                for cb in inventory::iter::<InternalCallback> {
342                    sys::panda_register_callback(
343                        self as *mut _ as _,
344                        cb.cb_type,
345                        ::core::mem::transmute(cb.fn_pointer),
346                    );
347                }
348
349                if LIBRARY_STARTED.swap(true, Ordering::Relaxed) {
350                    panic!("libpanda cannot be run twice in the same process");
351                }
352                panda_set_library_mode(true);
353                panda_init(args_ptrs.len() as i32, transmute(args_ptrs.as_ptr()), empty);
354
355                for cb in inventory::iter::<PPPCallbackSetup> {
356                    cb.0();
357                }
358
359                let mut init_funcs = Vec::new();
360                core::mem::swap(&mut *AFTER_INIT_FUNCS.lock().unwrap(), &mut init_funcs);
361                for init_func in init_funcs {
362                    init_func()
363                }
364
365                panda_run();
366                LIBRARY_STARTED.store(false, Ordering::Relaxed);
367            }
368        }
369    }
370
371    /// Queue up a function that should run before libpanda has started but after
372    /// the libpanda has been initialized. If run under a plugin context (e.g. no
373    /// libpanda), or libpanda is currently running, then the function will run immediately.
374    ///
375    /// This is useful for functions that may require waiting until things like arguments
376    /// or OS has been set, such as setting up an OSI callback.
377    pub fn run_after_init(func: impl FnOnce() + Send + Sync + 'static) {
378        if cfg!(feature = "libpanda") && !Panda::is_started() {
379            #[cfg(feature = "libpanda")]
380            {
381                AFTER_INIT_FUNCS.lock().unwrap().push(Box::new(func));
382            }
383        } else {
384            func()
385        }
386    }
387}