From 9e161cfbda048106891ce9d90d04e8b8ed94c16c Mon Sep 17 00:00:00 2001 From: novafacing Date: Tue, 19 Dec 2023 14:20:59 -0800 Subject: [PATCH] Implement tracer example --- qemu-plugin/Cargo.toml | 4 + qemu-plugin/examples/tracer/Cargo.toml | 13 +- qemu-plugin/examples/tracer/src/bin/tracer.rs | 237 ++++++++++++ qemu-plugin/examples/tracer/src/lib.rs | 336 +++++++++++++++--- qemu-plugin/src/lib.rs | 4 + qemu-plugin/src/weak.rs | 317 +++++++++++++++++ 6 files changed, 865 insertions(+), 46 deletions(-) create mode 100644 qemu-plugin/examples/tracer/src/bin/tracer.rs create mode 100644 qemu-plugin/src/weak.rs diff --git a/qemu-plugin/Cargo.toml b/qemu-plugin/Cargo.toml index 6818c4c..90394ad 100644 --- a/qemu-plugin/Cargo.toml +++ b/qemu-plugin/Cargo.toml @@ -16,3 +16,7 @@ anyhow = "1.0.75" once_cell = "1.19.0" qemu-plugin-sys = { version = "8.1.3-v3", workspace = true } thiserror = "1.0.51" + +[features] +# Define external symbols with weak definitions +weak = [] diff --git a/qemu-plugin/examples/tracer/Cargo.toml b/qemu-plugin/examples/tracer/Cargo.toml index 3d632fa..fbc5601 100644 --- a/qemu-plugin/examples/tracer/Cargo.toml +++ b/qemu-plugin/examples/tracer/Cargo.toml @@ -4,10 +4,19 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib"] +name = "tracer" +crate-type = ["cdylib", "lib"] [dependencies] -qemu-plugin.workspace = true +qemu = { workspace = true, features = ["plugins", "debug-info"] } +qemu-plugin = { workspace = true, features = ["weak"] } anyhow = "1.0.75" ffi = "0.1.0" ctor = "0.2.6" +tokio = { version = "1.35.0", features = ["full"] } +rand = "0.8.5" +serde_cbor = "0.11.2" +clap = { version = "4.4.11", features = ["derive", "string"] } +typed-builder = "0.18.0" +serde = { version = "1.0.193", features = ["derive"] } +memfd-exec = "0.2.1" diff --git a/qemu-plugin/examples/tracer/src/bin/tracer.rs b/qemu-plugin/examples/tracer/src/bin/tracer.rs new file mode 100644 index 0000000..3a45d4c --- /dev/null +++ b/qemu-plugin/examples/tracer/src/bin/tracer.rs @@ -0,0 +1,237 @@ +use anyhow::{anyhow, Error, Result}; +use clap::Parser; +use memfd_exec::{MemFdExecutable, Stdio}; +use qemu::QEMU_X86_64_LINUX_USER; +use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use serde_cbor::Deserializer; +use std::{ + fs::OpenOptions, + io::{stdout, BufRead, BufReader, Write}, + os::unix::net::UnixListener, + path::{Path, PathBuf}, +}; +use tokio::{ + fs::{read, remove_file, write}, + join, main, spawn, + task::spawn_blocking, +}; +use tracer::Event; + +#[cfg(debug_assertions)] +const PLUGIN: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../../target/debug/libtracer.so" +)); + +#[cfg(not(debug_assertions))] +const PLUGIN: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../../target/release/libtracer.so" +)); + +fn tmp(prefix: &str, suffix: &str) -> PathBuf { + PathBuf::from(format!( + "{}{}{}", + prefix, + thread_rng() + .sample_iter(&Alphanumeric) + .take(8) + .map(char::from) + .collect::(), + suffix + )) +} + +#[derive(Parser, Debug, Clone)] +/// Run QEMU with a plugin that logs events. To pass arguments to QEMU, use the QEMU environment +/// variables. +struct Args { + #[clap(short = 'i', long)] + /// Whether instructions should be logged + pub log_insns: bool, + #[clap(short = 'm', long)] + /// Whether memory accesses should be logged + pub log_mem: bool, + #[clap(short = 's', long)] + /// Whether syscalls should be logged + pub log_syscalls: bool, + #[clap(short = 'a', long)] + /// Whether all events should be logged + pub log_all: bool, + #[clap(short = 'I', long)] + /// An input file to use as the program's stdin, otherwise the driver's stdin is used + pub input_file: Option, + #[clap(short = 'O', long)] + /// An output file to write the trace to, otherwise stdout is used + pub output_file: Option, + /// The program to run + #[clap()] + pub program: PathBuf, + /// The arguments to the program + #[clap(num_args = 1.., last = true)] + pub args: Vec, +} + +impl Args { + fn to_plugin_args(&self) -> String { + format!( + "log_insns={},log_mem={},log_syscalls={}", + self.log_insns | self.log_all, + self.log_mem | self.log_all, + self.log_syscalls | self.log_all + ) + } + + fn to_qemu_args(&self, socket_path: &Path, plugin_path: &Path) -> Result> { + let mut qemu_args = vec![ + "-plugin".to_string(), + format!( + "{},{},socket_path={}", + plugin_path.display(), + self.to_plugin_args(), + socket_path.display() + ), + "--".to_string(), + self.program + .to_str() + .ok_or_else(|| anyhow!("Failed to convert program path to string"))? + .to_string(), + ]; + + qemu_args.extend(self.args.clone()); + + Ok(qemu_args) + } +} + +async fn run(input: Option>, args: Vec) -> Result<()> { + let mut exe = MemFdExecutable::new("qemu", QEMU_X86_64_LINUX_USER) + .args(args) + .stdin(if input.is_some() { + Stdio::piped() + } else { + Stdio::inherit() + }) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + if let Some(input) = input { + let mut stdin = exe.stdin.take().ok_or_else(|| anyhow!("No stdin"))?; + spawn_blocking(move || stdin.write_all(&input)); + } + + let stdout = exe.stdout.take().ok_or_else(|| anyhow!("No stdout"))?; + + let out_reader = spawn_blocking(move || { + let mut line = String::new(); + let mut out_reader = BufReader::new(stdout); + + loop { + line.clear(); + + if let 0 = out_reader.read_line(&mut line)? { + break; + } + + let line = line.trim(); + + if !line.is_empty() { + println!("{line}"); + } + } + + Ok::<(), Error>(()) + }); + + let stderr = exe.stderr.take().ok_or_else(|| anyhow!("No stderr"))?; + + let err_reader = spawn_blocking(move || { + let mut line = String::new(); + let mut err_reader = BufReader::new(stderr); + + loop { + line.clear(); + + if let 0 = err_reader.read_line(&mut line)? { + break; + } + + let line = line.trim(); + + if !line.is_empty() { + eprintln!("{line}"); + } + } + + Ok::<(), Error>(()) + }); + + let waiter = spawn_blocking(move || exe.wait()); + + let (out_res, err_res, waiter_res) = join!(out_reader, err_reader, waiter); + + out_res??; + err_res??; + waiter_res??; + + Ok(()) +} + +fn listen

(listen_sock: UnixListener, outfile: Option

) -> Result<()> +where + P: AsRef, +{ + let mut outfile_stream = if let Some(outfile) = outfile.as_ref() { + Box::new(OpenOptions::new().create(true).append(true).open(outfile)?) as Box + } else { + Box::new(stdout()) as Box + }; + + let (mut stream, _) = listen_sock.accept()?; + let it = Deserializer::from_reader(&mut stream).into_iter::(); + + for event in it { + outfile_stream.write_all(format!("{:?}\n", event?).as_bytes())?; + } + + Ok(()) +} + +#[main] +async fn main() -> Result<()> { + let args = Args::parse(); + + let socket_path = tmp("/tmp/qemu-", ".sock"); + let plugin_path = tmp("/tmp/qemu-", ".so"); + + write(&plugin_path, PLUGIN).await?; + + let input = if let Some(input_file) = args.input_file.as_ref() { + let Ok(input_file) = input_file.canonicalize() else { + return Err(anyhow!("Failed to canonicalize input file")); + }; + + Some(read(input_file).await?) + } else { + None + }; + + let listen_sock = UnixListener::bind(&socket_path)?; + + let qemu_args = args.to_qemu_args(&socket_path, &plugin_path)?; + let qemu_task = spawn(async move { run(input, qemu_args).await }); + + let socket_task = spawn_blocking(move || listen(listen_sock, args.output_file.as_ref())); + + let (qemu_res, socket_res) = join!(qemu_task, socket_task); + + remove_file(&plugin_path).await?; + remove_file(&socket_path).await?; + + qemu_res??; + + socket_res??; + + Ok(()) +} diff --git a/qemu-plugin/examples/tracer/src/lib.rs b/qemu-plugin/examples/tracer/src/lib.rs index 0cc1818..ec03a9f 100644 --- a/qemu-plugin/examples/tracer/src/lib.rs +++ b/qemu-plugin/examples/tracer/src/lib.rs @@ -1,28 +1,117 @@ -use anyhow::Result; +use anyhow::{anyhow, Error, Result}; use ctor::ctor; use qemu_plugin::{ - install::{Args, Info}, + install::{Args, Info, Value}, plugin::{HasCallbacks, Plugin, Register, PLUGIN}, - qemu_plugin_register_atexit_cb, PluginId, TranslationBlock, + Instruction, MemRW, MemoryInfo, PluginId, TranslationBlock, VCPUIndex, }; -use std::sync::{Arc, Mutex}; +use serde::{Deserialize, Serialize}; +use serde_cbor::to_writer; +use std::{ + collections::HashMap, + os::unix::net::UnixStream, + path::PathBuf, + sync::{Arc, Mutex}, +}; +use typed_builder::TypedBuilder; -struct Tracer { - tbs: Arc>, - insns: Arc>, +#[derive(TypedBuilder, Clone, Debug, Deserialize, Serialize)] +pub struct InstructionEvent { + pub vaddr: u64, + pub haddr: u64, + pub disas: String, + pub symbol: Option, + pub data: Vec, } -impl Register for Tracer { - fn register(&mut self, id: PluginId, _args: &Args, _info: &Info) -> Result<()> { - let tbs = self.tbs.clone(); - let insns = self.insns.clone(); +impl TryFrom<&Instruction<'_>> for InstructionEvent { + type Error = Error; - qemu_plugin_register_atexit_cb(id, move |_| { - println!("tbs: {}", tbs.lock().unwrap()); - println!("insns: {}", insns.lock().unwrap()); - })?; + fn try_from(value: &Instruction) -> Result { + Ok(Self::builder() + .vaddr(value.vaddr()) + .haddr(value.haddr()) + .disas(value.disas()?) + .symbol(value.symbol()?) + .data(value.data()) + .build()) + } +} - Ok(()) +#[derive(TypedBuilder, Clone, Debug, Deserialize, Serialize)] +pub struct MemoryEvent { + pub vaddr: u64, + pub haddr: Option, + pub haddr_is_io: Option, + pub haddr_device_name: Option, + pub size_shift: usize, + pub size_bytes: usize, + pub sign_extended: bool, + pub is_store: bool, + pub big_endian: bool, +} + +impl MemoryEvent { + fn try_from(value: &MemoryInfo, vaddr: u64) -> Result { + let haddr = value.hwaddr(vaddr); + Ok(Self::builder() + .vaddr(vaddr) + .haddr(haddr.as_ref().map(|h| h.hwaddr())) + .haddr_is_io(haddr.as_ref().map(|h| h.is_io())) + .haddr_device_name(haddr.and_then(|h| h.device_name().ok().flatten())) + .size_shift(value.size_shift()) + .size_bytes(match value.size_shift() { + 0 => 1, + 1 => 2, + 2 => 4, + 3 => 8, + _ => 0, + }) + .sign_extended(value.sign_extended()) + .is_store(value.is_store()) + .big_endian(value.big_endian()) + .build()) + } +} + +#[derive(TypedBuilder, Clone, Debug, PartialEq, Eq, Hash)] +pub struct SyscallSource { + plugin_id: PluginId, + vcpu_index: VCPUIndex, +} + +#[derive(TypedBuilder, Clone, Debug, Deserialize, Serialize)] +pub struct SyscallEvent { + pub num: i64, + pub return_value: i64, + pub args: [u64; 8], +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum Event { + Instruction(InstructionEvent), + Memory(MemoryEvent), + Syscall(SyscallEvent), +} + +#[derive(TypedBuilder, Clone, Debug)] +struct Tracer { + pub syscalls: Arc>>, + #[builder(default)] + pub tx: Arc>>, + #[builder(default)] + pub log_insns: bool, + #[builder(default)] + pub log_mem: bool, + #[builder(default)] + pub log_syscalls: bool, +} + +impl Tracer { + pub fn new() -> Self { + Self::builder() + .syscalls(Arc::new(Mutex::new(HashMap::new()))) + .build() } } @@ -32,37 +121,199 @@ impl HasCallbacks for Tracer { _id: PluginId, tb: TranslationBlock, ) -> Result<()> { - let tbs = self.tbs.clone(); - tb.register_execute_callback(move |_| { - tbs.lock() - .map(|mut tbs| { - *tbs += 1; - }) - .map_err(|e| eprintln!("Failed to lock tbs: {:?}", e)) - .ok(); - }); - tb.instructions().enumerate().try_for_each(|(idx, insn)| { - let insns = self.insns.clone(); - insn.register_execute_callback(move |_| { - insns - .lock() - .map(|mut insns| { - *insns += 1; - }) - .map_err(|e| eprintln!("Failed to lock insns: {:?}", e)) - .ok(); - }); + tb.instructions().try_for_each(|insn| { + let event = InstructionEvent::try_from(&insn)?; - if idx == 0 { - println!("====TB: {:08x}", insn.vaddr()); + if self.log_insns { + let tx = self.tx.clone(); + + insn.register_execute_callback(move |_| { + tx.lock() + .map_err(|e| anyhow!("Failed to lock tx: {}", e)) + .and_then(|tx| { + to_writer( + tx.as_ref().ok_or_else(|| anyhow!("No tx"))?, + &Event::Instruction(event.clone()), + ) + .map_err(|e| anyhow!(e)) + }) + .expect("Failed to send instruction event"); + }); } - println!("{:08x}: {}", insn.vaddr(), insn.disas()?); - Ok::<(), anyhow::Error>(()) + if self.log_mem { + let tx = self.tx.clone(); + + insn.register_memory_access_callback( + move |_, info, vaddr| { + tx.lock() + .map_err(|e| anyhow!("Failed to lock tx: {}", e)) + .and_then(|tx| { + to_writer( + tx.as_ref().ok_or_else(|| anyhow!("No tx"))?, + &Event::Memory(MemoryEvent::try_from(&info, vaddr)?), + ) + .map_err(|e| anyhow!(e)) + }) + .expect("Failed to send memory event"); + }, + MemRW::QEMU_PLUGIN_MEM_RW, + ); + } + + Ok::<(), Error>(()) })?; Ok(()) } + + fn on_syscall( + &mut self, + id: PluginId, + vcpu_index: VCPUIndex, + num: i64, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + ) -> Result<()> { + if !self.log_syscalls { + return Ok(()); + } + + let event = SyscallEvent::builder() + .num(num) + .return_value(-1) + .args([a1, a2, a3, a4, a5, a6, a7, a8]) + .build(); + + let mut syscalls = self + .syscalls + .lock() + .map_err(|e| anyhow!("Failed to lock syscalls: {e}"))?; + + syscalls.insert( + SyscallSource::builder() + .plugin_id(id) + .vcpu_index(vcpu_index) + .build(), + event, + ); + + Ok(()) + } + + fn on_syscall_return( + &mut self, + id: PluginId, + vcpu_index: VCPUIndex, + _: i64, + ret: i64, + ) -> Result<()> { + if !self.log_syscalls { + return Ok(()); + } + + let mut syscalls = self + .syscalls + .lock() + .map_err(|e| anyhow!("Failed to lock syscalls: {e}"))?; + + // Remove and return the syscall event + let mut event = syscalls + .remove( + &SyscallSource::builder() + .plugin_id(id) + .vcpu_index(vcpu_index) + .build(), + ) + .ok_or_else(|| anyhow!("No syscall event found"))?; + + // Update the return value + event.return_value = ret; + + // Send the event + let tx = self + .tx + .lock() + .map_err(|e| anyhow!("Failed to lock tx: {e}"))?; + let tx_stream = tx.as_ref().ok_or_else(|| anyhow!("No tx"))?; + + to_writer(tx_stream, &Event::Syscall(event)).map_err(|e| anyhow!(e))?; + + Ok(()) + } +} + +#[derive(TypedBuilder, Clone, Debug)] +pub struct PluginArgs { + pub log_insns: bool, + pub log_mem: bool, + pub log_syscalls: bool, + pub socket_path: PathBuf, +} + +impl TryFrom<&Args> for PluginArgs { + type Error = Error; + + fn try_from(value: &Args) -> Result { + Ok(Self::builder() + .log_insns( + value + .parsed + .get("log_insns") + .map(|li| if let Value::Bool(v) = li { *v } else { false }) + .unwrap_or_default(), + ) + .log_mem( + value + .parsed + .get("log_mem") + .map(|lm| if let Value::Bool(v) = lm { *v } else { false }) + .unwrap_or_default(), + ) + .log_syscalls( + value + .parsed + .get("log_syscalls") + .map(|ls| if let Value::Bool(v) = ls { *v } else { false }) + .unwrap_or_default(), + ) + .socket_path( + value + .parsed + .get("socket_path") + .and_then(|sp| { + if let Value::String(v) = sp { + Some(PathBuf::from(v)) + } else { + None + } + }) + .ok_or_else(|| anyhow!("No socket path provided"))?, + ) + .build()) + } +} + +impl Register for Tracer { + fn register(&mut self, _: PluginId, args: &Args, _: &Info) -> Result<()> { + let plugin_args = PluginArgs::try_from(args)?; + + self.tx = Arc::new(Mutex::new(Some(UnixStream::connect( + plugin_args.socket_path, + )?))); + + self.log_insns = plugin_args.log_insns; + self.log_mem = plugin_args.log_mem; + self.log_syscalls = plugin_args.log_syscalls; + + Ok(()) + } } impl Plugin for Tracer {} @@ -70,10 +321,7 @@ impl Plugin for Tracer {} #[ctor] fn init() { PLUGIN - .set(Mutex::new(Box::new(Tracer { - tbs: Arc::new(Mutex::new(0)), - insns: Arc::new(Mutex::new(0)), - }))) + .set(Mutex::new(Box::new(Tracer::new()))) .map_err(|_| anyhow::anyhow!("Failed to set plugin")) .expect("Failed to set plugin"); } diff --git a/qemu-plugin/src/lib.rs b/qemu-plugin/src/lib.rs index 4e44be4..b8b576b 100644 --- a/qemu-plugin/src/lib.rs +++ b/qemu-plugin/src/lib.rs @@ -71,6 +71,10 @@ //! ``` #![deny(missing_docs)] +#![cfg_attr(feature = "weak", feature(linkage))] + +#[cfg(feature = "weak")] +mod weak; use crate::error::{Error, Result}; use qemu_plugin_sys::{ diff --git a/qemu-plugin/src/weak.rs b/qemu-plugin/src/weak.rs new file mode 100644 index 0000000..a3bdd29 --- /dev/null +++ b/qemu-plugin/src/weak.rs @@ -0,0 +1,317 @@ +use std::ptr::{null, null_mut}; + +use qemu_plugin_sys::*; + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_uninstall(_: qemu_plugin_id_t, _: qemu_plugin_simple_cb_t) {} +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_reset(_: qemu_plugin_id_t, _: qemu_plugin_simple_cb_t) {} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_init_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_vcpu_simple_cb_t, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_exit_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_vcpu_simple_cb_t, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_idle_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_vcpu_simple_cb_t, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_resume_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_vcpu_simple_cb_t, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_tb_trans_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_vcpu_tb_trans_cb_t, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_tb_exec_cb( + _: *mut qemu_plugin_tb, + _: qemu_plugin_vcpu_udata_cb_t, + _: qemu_plugin_cb_flags, + _: *mut ::std::os::raw::c_void, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_tb_exec_inline( + _: *mut qemu_plugin_tb, + _: qemu_plugin_op, + _: *mut ::std::os::raw::c_void, + _: u64, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_insn_exec_cb( + _: *mut qemu_plugin_insn, + _: qemu_plugin_vcpu_udata_cb_t, + _: qemu_plugin_cb_flags, + _: *mut ::std::os::raw::c_void, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_insn_exec_inline( + _: *mut qemu_plugin_insn, + _: qemu_plugin_op, + _: *mut ::std::os::raw::c_void, + _: u64, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_tb_n_insns(_: *const qemu_plugin_tb) -> usize { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_tb_vaddr(_: *const qemu_plugin_tb) -> u64 { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_tb_get_insn( + _: *const qemu_plugin_tb, + _: usize, +) -> *mut qemu_plugin_insn { + null_mut() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_insn_data( + _: *const qemu_plugin_insn, +) -> *const ::std::os::raw::c_void { + null() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_insn_size(_: *const qemu_plugin_insn) -> usize { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_insn_vaddr(_: *const qemu_plugin_insn) -> u64 { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_insn_haddr( + _: *const qemu_plugin_insn, +) -> *mut ::std::os::raw::c_void { + null_mut() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_mem_size_shift(_: qemu_plugin_meminfo_t) -> ::std::os::raw::c_uint { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_mem_is_sign_extended(_: qemu_plugin_meminfo_t) -> bool { + false +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_mem_is_big_endian(_: qemu_plugin_meminfo_t) -> bool { + false +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_mem_is_store(_: qemu_plugin_meminfo_t) -> bool { + false +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_get_hwaddr( + _: qemu_plugin_meminfo_t, + _: u64, +) -> *mut qemu_plugin_hwaddr { + null_mut() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_hwaddr_is_io(_: *const qemu_plugin_hwaddr) -> bool { + false +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_hwaddr_phys_addr(_: *const qemu_plugin_hwaddr) -> u64 { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_hwaddr_device_name( + _: *const qemu_plugin_hwaddr, +) -> *const ::std::os::raw::c_char { + null() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_mem_cb( + _: *mut qemu_plugin_insn, + _: qemu_plugin_vcpu_mem_cb_t, + _: qemu_plugin_cb_flags, + _: qemu_plugin_mem_rw, + _: *mut ::std::os::raw::c_void, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_mem_inline( + _: *mut qemu_plugin_insn, + _: qemu_plugin_mem_rw, + _: qemu_plugin_op, + _: *mut ::std::os::raw::c_void, + _: u64, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_syscall_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_vcpu_syscall_cb_t, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_vcpu_syscall_ret_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_vcpu_syscall_ret_cb_t, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_insn_disas( + _: *const qemu_plugin_insn, +) -> *mut ::std::os::raw::c_char { + null_mut() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_insn_symbol( + _: *const qemu_plugin_insn, +) -> *const ::std::os::raw::c_char { + null() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_vcpu_for_each(_: qemu_plugin_id_t, _: qemu_plugin_vcpu_simple_cb_t) {} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_flush_cb(_: qemu_plugin_id_t, _: qemu_plugin_simple_cb_t) {} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_register_atexit_cb( + _: qemu_plugin_id_t, + _: qemu_plugin_udata_cb_t, + _: *mut ::std::os::raw::c_void, +) { +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_n_vcpus() -> ::std::os::raw::c_int { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_n_max_vcpus() -> ::std::os::raw::c_int { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_outs(_: *const ::std::os::raw::c_char) {} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_bool_parse( + _: *const ::std::os::raw::c_char, + _: *const ::std::os::raw::c_char, + _: *mut bool, +) -> bool { + false +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_path_to_binary() -> *const ::std::os::raw::c_char { + null() +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_start_code() -> u64 { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_end_code() -> u64 { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn qemu_plugin_entry_code() -> u64 { + 0 +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn g_free(_: *mut ::std::ffi::c_void) {}