Skip to content

Commit ef1ffbc

Browse files
committed
[Rust] Add builder API for creating workflows
`Workflow::new` is replaced by `Workflow::build` that returns a `Builder` type that supports the operations that mutate a workflow, such as registering activities. `Workflow::instance` is replaced by `Workflow::get` to make clear that it is intended to look up an existing workflow. `Workflow::cloned` is introduced to wrap the common pattern of retrieving an existing workflow and cloning it with the same name in order to modify it. `Builder::activity_before` / `Builder::activity_after` are introduced to wrap the common pattern of registering an activity then inserting it before or after a given activity.
1 parent 75a2db1 commit ef1ffbc

File tree

6 files changed

+279
-225
lines changed

6 files changed

+279
-225
lines changed

plugins/svd/src/lib.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use binaryninja::logger::Logger;
99
use binaryninja::workflow::{Activity, AnalysisContext, Workflow};
1010
use log::LevelFilter;
1111

12-
pub const LOADER_ACTIVITY_NAME: &str = "analysis.svd.loader";
1312
const LOADER_ACTIVITY_CONFIG: &str = r#"{
1413
"name": "analysis.svd.loader",
1514
"title" : "SVD Loader",
@@ -62,19 +61,25 @@ impl Command for LoadSVDFile {
6261
#[allow(non_snake_case)]
6362
#[cfg(not(feature = "demo"))]
6463
pub extern "C" fn CorePluginInit() -> bool {
65-
plugin_init();
64+
if plugin_init().is_err() {
65+
log::error!("Failed to initialize SVD plug-in");
66+
return false;
67+
}
6668
true
6769
}
6870

6971
#[no_mangle]
7072
#[allow(non_snake_case)]
7173
#[cfg(feature = "demo")]
7274
pub extern "C" fn SVDPluginInit() -> bool {
73-
plugin_init();
75+
if plugin_init().is_err() {
76+
log::error!("Failed to initialize SVD plug-in");
77+
return false;
78+
}
7479
true
7580
}
7681

77-
fn plugin_init() {
82+
fn plugin_init() -> Result<(), ()> {
7883
Logger::new("SVD").with_level(LevelFilter::Debug).init();
7984

8085
binaryninja::command::register_command(
@@ -113,12 +118,10 @@ fn plugin_init() {
113118
};
114119

115120
// Register new workflow activity to load svd information.
116-
let old_module_meta_workflow = Workflow::instance("core.module.metaAnalysis");
117-
let module_meta_workflow = old_module_meta_workflow.clone_to("core.module.metaAnalysis");
118121
let loader_activity = Activity::new_with_action(LOADER_ACTIVITY_CONFIG, loader_activity);
119-
module_meta_workflow
120-
.register_activity(&loader_activity)
121-
.unwrap();
122-
module_meta_workflow.insert("core.module.loadDebugInfo", [LOADER_ACTIVITY_NAME]);
123-
module_meta_workflow.register().unwrap();
122+
Workflow::cloned("core.module.metaAnalysis")
123+
.ok_or(())?
124+
.activity_before(&loader_activity, "core.module.loadDebugInfo")?
125+
.register()?;
126+
Ok(())
124127
}

plugins/warp/src/plugin.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ pub extern "C" fn CorePluginInit() -> bool {
102102
// Register our highlight render layer.
103103
HighlightRenderLayer::register();
104104

105-
workflow::insert_workflow();
105+
if workflow::insert_workflow().is_err() {
106+
log::error!("Failed to register WARP workflow");
107+
return false;
108+
}
106109

107110
// TODO: Make the retrieval of containers wait on this to be done.
108111
// TODO: We could also have a mechanism for lazily loading the files using the chunk header target.

plugins/warp/src/plugin/workflow.rs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use warp::r#type::class::function::{Location, RegisterLocation, StackLocation};
1919
use warp::signature::function::{Function, FunctionGUID};
2020
use warp::target::Target;
2121

22-
pub const APPLY_ACTIVITY_NAME: &str = "analysis.warp.apply";
2322
const APPLY_ACTIVITY_CONFIG: &str = r#"{
2423
"name": "analysis.warp.apply",
2524
"title" : "WARP Apply Matched",
@@ -30,7 +29,6 @@ const APPLY_ACTIVITY_CONFIG: &str = r#"{
3029
}
3130
}"#;
3231

33-
pub const MATCHER_ACTIVITY_NAME: &str = "analysis.warp.matcher";
3432
const MATCHER_ACTIVITY_CONFIG: &str = r#"{
3533
"name": "analysis.warp.matcher",
3634
"title" : "WARP Matcher",
@@ -189,7 +187,7 @@ pub fn run_matcher(view: &BinaryView) {
189187
view.update_analysis();
190188
}
191189

192-
pub fn insert_workflow() {
190+
pub fn insert_workflow() -> Result<(), ()> {
193191
// TODO: Note: because of symbol persistence function symbol is applied in `insert_cached_function_match`.
194192
// TODO: Comments are also applied there, they are "user" like, persisted and make undo actions.
195193
// "Hey look, it's a plier" ~ Josh 2025
@@ -262,26 +260,29 @@ pub fn insert_workflow() {
262260
let guid_activity = Activity::new_with_action(GUID_ACTIVITY_CONFIG, guid_activity);
263261
let apply_activity = Activity::new_with_action(APPLY_ACTIVITY_CONFIG, apply_activity);
264262

265-
let add_function_activities = |workflow: &Workflow| {
266-
let new_workflow = workflow.clone_to(&workflow.name());
267-
new_workflow.register_activity(&guid_activity).unwrap();
268-
new_workflow.register_activity(&apply_activity).unwrap();
269-
new_workflow.insert_after("core.function.runFunctionRecognizers", [GUID_ACTIVITY_NAME]);
270-
new_workflow.insert_after("core.function.generateMediumLevelIL", [APPLY_ACTIVITY_NAME]);
271-
new_workflow.register().unwrap();
263+
let add_function_activities = |workflow: Option<Ref<Workflow>>| -> Result<(), ()> {
264+
let Some(workflow) = workflow else {
265+
return Ok(());
266+
};
267+
268+
workflow
269+
.clone_to(&workflow.name())
270+
.activity_after(&guid_activity, "core.function.runFunctionRecognizers")?
271+
.activity_after(&apply_activity, "core.function.generateMediumLevelIL")?
272+
.register()?;
273+
Ok(())
272274
};
273275

274-
add_function_activities(&Workflow::instance("core.function.metaAnalysis"));
276+
add_function_activities(Workflow::get("core.function.metaAnalysis"))?;
275277
// TODO: Remove this once the objectivec workflow is registered on the meta workflow.
276-
add_function_activities(&Workflow::instance("core.function.objectiveC"));
278+
add_function_activities(Workflow::get("core.function.objectiveC"))?;
277279

278-
let old_module_meta_workflow = Workflow::instance("core.module.metaAnalysis");
279-
let module_meta_workflow = old_module_meta_workflow.clone_to("core.module.metaAnalysis");
280-
let matcher_activity = Activity::new_with_action(MATCHER_ACTIVITY_CONFIG, matcher_activity);
281280
// Matcher activity must have core.module.update as subactivity otherwise analysis will sometimes never retrigger.
282-
module_meta_workflow
283-
.register_activity(&matcher_activity)
284-
.unwrap();
285-
module_meta_workflow.insert("core.module.finishUpdate", [MATCHER_ACTIVITY_NAME]);
286-
module_meta_workflow.register().unwrap();
281+
let matcher_activity = Activity::new_with_action(MATCHER_ACTIVITY_CONFIG, matcher_activity);
282+
Workflow::cloned("core.module.metaAnalysis")
283+
.ok_or(())?
284+
.activity_before(&matcher_activity, "core.module.finishUpdate")?
285+
.register()?;
286+
287+
Ok(())
287288
}

rust/examples/workflow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub fn main() {
5252
binaryninja::headless::Session::new().expect("Failed to initialize session");
5353

5454
println!("Registering workflow...");
55-
let old_meta_workflow = Workflow::instance("core.function.metaAnalysis");
55+
let old_meta_workflow = Workflow::get("core.function.metaAnalysis");
5656
let meta_workflow = old_meta_workflow.clone_to("core.function.metaAnalysis");
5757
let activity = Activity::new_with_action(RUST_ACTIVITY_CONFIG, example_activity);
5858
meta_workflow.register_activity(&activity).unwrap();

0 commit comments

Comments
 (0)