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;