reoxide/
lib.rs

1//! The `reoxide` Rust crate offers two different things:
2//! 
3//! * Allow driving the Ghidra decompiler from Rust.
4//! * Allow writing plugins for the Ghidra decompiler using Rust.
5//! 
6//! Both require a working ReOxide setup, make sure you can run the
7//! `reoxide` command in the environment where you run your Rust builds
8//! from.
9//! 
10//! # Driving the Ghidra decompiler from Rust
11//!
12//! ```
13//! use std::env;
14//! use std::path::Path;
15//! 
16//! use reoxide::driver::*;
17//! 
18//! const PROGRAM: &[u8] = &[
19//!     0x55, 0x48, 0x89, 0xe5, 0x53, 0x48, 0x83, 0xec, 0x18, 0x89, 0x7d, 0xec, 0x83, 0x7d, 0xec, 0x00,
20//!     0x74, 0x06, 0x83, 0x7d, 0xec, 0x01, 0x75, 0x07, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xeb, 0x1e, 0x8b,
21//!     0x45, 0xec, 0x83, 0xe8, 0x01, 0x89, 0xc7, 0xe8, 0xfb, 0xfe, 0xff, 0xff, 0x89, 0xc3, 0x8b, 0x45,
22//!     0xec, 0x83, 0xe8, 0x02, 0x89, 0xc7, 0xe8, 0xec, 0xfe, 0xff, 0xff, 0x01, 0xd8, 0x48, 0x8b, 0x5d,
23//!     0xf8, 0xc9, 0xc3,
24//! ];
25//! 
26//! fn main() -> Result<(), DecompError> {
27//!     let ghidra_dir = env::var("GHIDRA_INSTALL_DIR").expect(
28//!         "Please provide an environment variable \
29//!         GHIDRA_INSTALL_DIR with the path to the Ghidra root directory. \
30//!         This is needed to find the SLEIGH specification files",
31//!     );
32//! 
33//!     initialize_decompiler(Path::new(&ghidra_dir), true)?;
34//!     let mut ctx = RawFileContext::from_bytes("x86:LE:64:default", &PROGRAM)?;
35//! 
36//!     let addr = 0x0;
37//!     ctx.define_function(addr, "fib")?;
38//!     let decomp = ctx.decompile_function(addr)?;
39//!     println!("{}", decomp);
40//! 
41//!     Ok(())
42//! }
43//! ```
44//! 
45//! The [`decompile`] example shows how to use the decompiler driver. When
46//! you write programs by using the decompiler driver, you have to
47//! tell the program where to find the `reoxide` shared library,
48//! `libreoxide.so`. The `build.rs` build step should automatically link
49//! the library correctly if it can run the `reoxide` command, but
50//! for running the example you might have to set the `LD_LIBRARY_PATH`
51//! manually:
52//!
53//! [`decompile`]: https://codeberg.org/ReOxide/reoxide/src/branch/main/rust-api/reoxide/examples/decompile.rs
54//! 
55//! ```sh
56//! LD_LIBRARY_PATH=$(reoxide print-ld-library-path)
57//! ```
58//! 
59//! Start off by importing the driver functions:
60//! 
61//! ```
62//! use reoxide::driver::*;
63//! ```
64//! 
65//! Currently the driver needs a full Ghidra installation for the processor
66//! definition files. You need to set the path to the root folder of
67//! the Ghidra installation, for example:
68//! 
69//! ```
70//! initialize_decompiler(Path::new("/opt/ghidra"), true).unwrap();
71//! ```
72//! 
73//! You can also enable or disable the ReOxide plugin system with the
74//! last parameter. Afterwards you initialize a decompiler context with
75//! the raw bytes of the program and the architecture specification string
76//! from Ghidra:
77//! 
78//! ```
79//! let mut ctx = RawFileContext::from_bytes("x86:LE:64:default", &PROGRAM).unwrap();
80//! ```
81//! 
82//! Decompilation works on the *function* level, so you first need to define
83//! a function at the address you want to decompile:
84//! 
85//! ```
86//! let addr = 0x0;
87//! ctx.define_function(addr, "fibonacci").unwrap();
88//! ```
89//! 
90//! Finally, you can decompile the function at the address:
91//! 
92//! ```
93//! let decomp = ctx.decompile_function(addr).unwrap();
94//! println!("{}", decomp);
95//! ```
96//!
97//! # Writing a ReOxide plugin
98//!
99//! Similar to the C++ plugin template, we also have a [`plugin template`]
100//! for Rust. The Rust API introduces a C API layer on top of the C++ codebase,
101//! which makes it more stable in terms of ABI. Currently you do not get the
102//! same full functionality as the C++ API, because the binding generator
103//! only generates code for functionality currently used by existing rules
104//! and actions. Moreover, the way Ghidra implements things does not map well
105//! to safe Rust code, which might cause the current API to not feel like
106//! idiomatic Rust and there might be soundness issues. These API bindings
107//! are therefore highly experimental.
108//!
109//! [`plugin template`]: https://codeberg.org/ReOxide/plugin-template-rust
110//!
111//! To start off, you need to mark the crate as a `cdylib` in the `Cargo.toml`:
112//!
113//! ```toml
114//! [package]
115//! name = "simple_plugin"
116//! version = "0.1.0"
117//! edition = "2024"
118//! 
119//! [lib]
120//! crate-type = ["cdylib"]
121//! 
122//! [dependencies]
123//! reoxide = "0.7.0"
124//! ```
125//!
126//! Before proceeding, we recommend that you read through the [`plugin development guide`]
127//! which covers the creation of C++ plugins. The Rust example here reimplements
128//! the C++ tutorial plugin in Rust.
129//! In your `lib.rs`, you declare a plugin with the [`plugin!`] proc macro.
130//! In a minimal example, you need a plugin context, which can be an empty
131//! struct. Then you can define a plugin like this:
132//!
133//! ```
134//! use reoxide::plugin::*;
135//! 
136//! pub struct SimplePlugin;
137//! 
138//! plugin!(
139//!     context = SimplePlugin,
140//!     rules = [],
141//!     actions = []
142//! );
143//! ```
144//!
145//! [`plugin development guide`]: https://reoxide.eu/plugins/creating-plugins
146//! [`plugin!`]: plugin::plugin
147//!
148//! This plugin does not do anything, because it does not contain any actions
149//! or rules. You can define an action with the [`action`] attribute:
150//!
151//! ```
152//! #[action(name = "my_action")]
153//! pub struct SimpleAction<'a> {
154//!     reox: &'a mut ReOxideInterface,
155//! }
156//! ```
157//!
158//! The `name` field for the [`action`] and [`rule`] attributes uniquely identifies
159//! your action in the Ghidra pipeline definition. You can then list the action
160//! in the plugin definition:
161//!
162//! ```
163//! plugin!(
164//!     context = SimplePlugin,
165//!     rules = [],
166//!     actions = [SimpleAction]
167//! );
168//! ```
169//!
170//! This will not compile, because all actions have to implement the [`Action trait`]
171//! as well. The trait works the same way as overriding the virtual functions in the
172//! C++ API does:
173//!
174//! ```
175//! impl<'a> Action<'a> for SimpleAction<'a> {
176//!     type PluginContext = SimplePlugin;
177//! 
178//!     fn new(context: CreationContext<'a, '_, Self::PluginContext>) -> Self {
179//!         SimpleAction {
180//!             reox: context.reoxide,
181//!         }
182//!     }
183//! 
184//!     fn apply(&mut self, _data: &mut Funcdata) -> ApplyResult {
185//!         self.reox.send_string("Hello from Rust Action!");
186//!         ApplyResult::NotApplied
187//!     }
188//! }
189//! ```
190//!
191//! [`action`]: plugin::action
192//! [`rule`]: plugin::rule
193//! [`Action trait`]: plugin::Action
194//!
195//! You can reimplement the `SimpleRule` from the C++ template in a similar manner.
196//! Here we also see the limits of the current Rust API, as we have to resort to
197//! using `unsafe` for retrieving the `FuncCallSpecs` pointer:
198//!
199//! ```rust
200//! pub struct SimpleRule<'a> {
201//!     _simple: &'a mut SimplePlugin,
202//!     reox: &'a mut ReOxideInterface,
203//! }
204//! 
205//! impl<'a> Rule<'a> for SimpleRule<'a> {
206//!     type PluginContext = SimplePlugin;
207//! 
208//!     fn new(context: CreationContext<'a, '_, Self::PluginContext>) -> Self {
209//!         SimpleRule {
210//!             _simple: context.plugin,
211//!             reox: context.reoxide,
212//!         }
213//!     }
214//! 
215//!     fn op_list(&self) -> Vec<OpCode> {
216//!         vec![OpCode::Call]
217//!     }
218//! 
219//!     fn apply(&mut self, op: &mut PcodeOp, _data: &mut Funcdata) -> ApplyResult {
220//!         self.reox.send_string("Hello from Rust Rule!");
221//! 
222//!         let vn = match op.get_in(0) {
223//!             Some(v) => v,
224//!             None => return ApplyResult::NotApplied,
225//!         };
226//! 
227//!         let spc = match vn.get_space() {
228//!             Some(s) => s,
229//!             None => return ApplyResult::NotApplied,
230//!         };
231//! 
232//!         if !matches!(spc.get_type(), Spacetype::Fspec) {
233//!             return ApplyResult::NotApplied;
234//!         }
235//! 
236//!         let fc_ptr = core::ptr::null::<FuncCallSpecs>().with_addr(vn.get_offset() as usize);
237//!         let fc = unsafe { &*fc_ptr };
238//!         if let Some(s) = fc.get_name() {
239//!             if let Ok(name) = s.from_utf8() {
240//!                 self.reox.send_string(name);
241//!             }
242//!         }
243//! 
244//!         ApplyResult::NotApplied
245//!     }
246//! }
247//! ```
248//!
249//! For building, the same rules apply as for the decompile driver: The `reoxide`
250//! program needs to be available in your environment.
251//! After building you have to manually copy the `libsimple_plugin.so` from the build
252//! directory to your ReOxide plugin directory, which you can find using the
253//! `reoxide` command:
254//!
255//! ```sh
256//! reoxide print-plugin-dir
257//! ```
258#[macro_use]
259mod util;
260pub mod cpp;
261mod generated;
262mod manual_bindings;
263pub mod plugin;
264pub mod driver;