reoxide 0.7.0

Rust-bindings for the ReOxide decompiler extension framework
Documentation
//! The `reoxide` Rust crate offers two different things:
//! 
//! * Allow driving the Ghidra decompiler from Rust.
//! * Allow writing plugins for the Ghidra decompiler using Rust.
//! 
//! Both require a working ReOxide setup, make sure you can run the
//! `reoxide` command in the environment where you run your Rust builds
//! from.
//! 
//! # Driving the Ghidra decompiler from Rust
//!
//! ```
//! use std::env;
//! use std::path::Path;
//! 
//! use reoxide::driver::*;
//! 
//! const PROGRAM: &[u8] = &[
//!     0x55, 0x48, 0x89, 0xe5, 0x53, 0x48, 0x83, 0xec, 0x18, 0x89, 0x7d, 0xec, 0x83, 0x7d, 0xec, 0x00,
//!     0x74, 0x06, 0x83, 0x7d, 0xec, 0x01, 0x75, 0x07, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xeb, 0x1e, 0x8b,
//!     0x45, 0xec, 0x83, 0xe8, 0x01, 0x89, 0xc7, 0xe8, 0xfb, 0xfe, 0xff, 0xff, 0x89, 0xc3, 0x8b, 0x45,
//!     0xec, 0x83, 0xe8, 0x02, 0x89, 0xc7, 0xe8, 0xec, 0xfe, 0xff, 0xff, 0x01, 0xd8, 0x48, 0x8b, 0x5d,
//!     0xf8, 0xc9, 0xc3,
//! ];
//! 
//! fn main() -> Result<(), DecompError> {
//!     let ghidra_dir = env::var("GHIDRA_INSTALL_DIR").expect(
//!         "Please provide an environment variable \
//!         GHIDRA_INSTALL_DIR with the path to the Ghidra root directory. \
//!         This is needed to find the SLEIGH specification files",
//!     );
//! 
//!     initialize_decompiler(Path::new(&ghidra_dir), true)?;
//!     let mut ctx = RawFileContext::from_bytes("x86:LE:64:default", &PROGRAM)?;
//! 
//!     let addr = 0x0;
//!     ctx.define_function(addr, "fib")?;
//!     let decomp = ctx.decompile_function(addr)?;
//!     println!("{}", decomp);
//! 
//!     Ok(())
//! }
//! ```
//! 
//! The [`decompile`] example shows how to use the decompiler driver. When
//! you write programs by using the decompiler driver, you have to
//! tell the program where to find the `reoxide` shared library,
//! `libreoxide.so`. The `build.rs` build step should automatically link
//! the library correctly if it can run the `reoxide` command, but
//! for running the example you might have to set the `LD_LIBRARY_PATH`
//! manually:
//!
//! [`decompile`]: https://codeberg.org/ReOxide/reoxide/src/branch/main/rust-api/reoxide/examples/decompile.rs
//! 
//! ```sh
//! LD_LIBRARY_PATH=$(reoxide print-ld-library-path)
//! ```
//! 
//! Start off by importing the driver functions:
//! 
//! ```
//! use reoxide::driver::*;
//! ```
//! 
//! Currently the driver needs a full Ghidra installation for the processor
//! definition files. You need to set the path to the root folder of
//! the Ghidra installation, for example:
//! 
//! ```
//! initialize_decompiler(Path::new("/opt/ghidra"), true).unwrap();
//! ```
//! 
//! You can also enable or disable the ReOxide plugin system with the
//! last parameter. Afterwards you initialize a decompiler context with
//! the raw bytes of the program and the architecture specification string
//! from Ghidra:
//! 
//! ```
//! let mut ctx = RawFileContext::from_bytes("x86:LE:64:default", &PROGRAM).unwrap();
//! ```
//! 
//! Decompilation works on the *function* level, so you first need to define
//! a function at the address you want to decompile:
//! 
//! ```
//! let addr = 0x0;
//! ctx.define_function(addr, "fibonacci").unwrap();
//! ```
//! 
//! Finally, you can decompile the function at the address:
//! 
//! ```
//! let decomp = ctx.decompile_function(addr).unwrap();
//! println!("{}", decomp);
//! ```
//!
//! # Writing a ReOxide plugin
//!
//! Similar to the C++ plugin template, we also have a [`plugin template`]
//! for Rust. The Rust API introduces a C API layer on top of the C++ codebase,
//! which makes it more stable in terms of ABI. Currently you do not get the
//! same full functionality as the C++ API, because the binding generator
//! only generates code for functionality currently used by existing rules
//! and actions. Moreover, the way Ghidra implements things does not map well
//! to safe Rust code, which might cause the current API to not feel like
//! idiomatic Rust and there might be soundness issues. These API bindings
//! are therefore highly experimental.
//!
//! [`plugin template`]: https://codeberg.org/ReOxide/plugin-template-rust
//!
//! To start off, you need to mark the crate as a `cdylib` in the `Cargo.toml`:
//!
//! ```toml
//! [package]
//! name = "simple_plugin"
//! version = "0.1.0"
//! edition = "2024"
//! 
//! [lib]
//! crate-type = ["cdylib"]
//! 
//! [dependencies]
//! reoxide = "0.7.0"
//! ```
//!
//! Before proceeding, we recommend that you read through the [`plugin development guide`]
//! which covers the creation of C++ plugins. The Rust example here reimplements
//! the C++ tutorial plugin in Rust.
//! In your `lib.rs`, you declare a plugin with the [`plugin!`] proc macro.
//! In a minimal example, you need a plugin context, which can be an empty
//! struct. Then you can define a plugin like this:
//!
//! ```
//! use reoxide::plugin::*;
//! 
//! pub struct SimplePlugin;
//! 
//! plugin!(
//!     context = SimplePlugin,
//!     rules = [],
//!     actions = []
//! );
//! ```
//!
//! [`plugin development guide`]: https://reoxide.eu/plugins/creating-plugins
//! [`plugin!`]: plugin::plugin
//!
//! This plugin does not do anything, because it does not contain any actions
//! or rules. You can define an action with the [`action`] attribute:
//!
//! ```
//! #[action(name = "my_action")]
//! pub struct SimpleAction<'a> {
//!     reox: &'a mut ReOxideInterface,
//! }
//! ```
//!
//! The `name` field for the [`action`] and [`rule`] attributes uniquely identifies
//! your action in the Ghidra pipeline definition. You can then list the action
//! in the plugin definition:
//!
//! ```
//! plugin!(
//!     context = SimplePlugin,
//!     rules = [],
//!     actions = [SimpleAction]
//! );
//! ```
//!
//! This will not compile, because all actions have to implement the [`Action trait`]
//! as well. The trait works the same way as overriding the virtual functions in the
//! C++ API does:
//!
//! ```
//! impl<'a> Action<'a> for SimpleAction<'a> {
//!     type PluginContext = SimplePlugin;
//! 
//!     fn new(context: CreationContext<'a, '_, Self::PluginContext>) -> Self {
//!         SimpleAction {
//!             reox: context.reoxide,
//!         }
//!     }
//! 
//!     fn apply(&mut self, _data: &mut Funcdata) -> ApplyResult {
//!         self.reox.send_string("Hello from Rust Action!");
//!         ApplyResult::NotApplied
//!     }
//! }
//! ```
//!
//! [`action`]: plugin::action
//! [`rule`]: plugin::rule
//! [`Action trait`]: plugin::Action
//!
//! You can reimplement the `SimpleRule` from the C++ template in a similar manner.
//! Here we also see the limits of the current Rust API, as we have to resort to
//! using `unsafe` for retrieving the `FuncCallSpecs` pointer:
//!
//! ```rust
//! pub struct SimpleRule<'a> {
//!     _simple: &'a mut SimplePlugin,
//!     reox: &'a mut ReOxideInterface,
//! }
//! 
//! impl<'a> Rule<'a> for SimpleRule<'a> {
//!     type PluginContext = SimplePlugin;
//! 
//!     fn new(context: CreationContext<'a, '_, Self::PluginContext>) -> Self {
//!         SimpleRule {
//!             _simple: context.plugin,
//!             reox: context.reoxide,
//!         }
//!     }
//! 
//!     fn op_list(&self) -> Vec<OpCode> {
//!         vec![OpCode::Call]
//!     }
//! 
//!     fn apply(&mut self, op: &mut PcodeOp, _data: &mut Funcdata) -> ApplyResult {
//!         self.reox.send_string("Hello from Rust Rule!");
//! 
//!         let vn = match op.get_in(0) {
//!             Some(v) => v,
//!             None => return ApplyResult::NotApplied,
//!         };
//! 
//!         let spc = match vn.get_space() {
//!             Some(s) => s,
//!             None => return ApplyResult::NotApplied,
//!         };
//! 
//!         if !matches!(spc.get_type(), Spacetype::Fspec) {
//!             return ApplyResult::NotApplied;
//!         }
//! 
//!         let fc_ptr = core::ptr::null::<FuncCallSpecs>().with_addr(vn.get_offset() as usize);
//!         let fc = unsafe { &*fc_ptr };
//!         if let Some(s) = fc.get_name() {
//!             if let Ok(name) = s.from_utf8() {
//!                 self.reox.send_string(name);
//!             }
//!         }
//! 
//!         ApplyResult::NotApplied
//!     }
//! }
//! ```
//!
//! For building, the same rules apply as for the decompile driver: The `reoxide`
//! program needs to be available in your environment.
//! After building you have to manually copy the `libsimple_plugin.so` from the build
//! directory to your ReOxide plugin directory, which you can find using the
//! `reoxide` command:
//!
//! ```sh
//! reoxide print-plugin-dir
//! ```
#[macro_use]
mod util;
pub mod cpp;
mod generated;
mod manual_bindings;
pub mod plugin;
pub mod driver;