Skip to content

Commit fc17355

Browse files
committed
[idb_import] Split enum-operand and number-format settings
Enum-displayed operands are a small, cheap set, while the per-operand number formats can number in the hundreds of thousands and dominate import time on large databases. Gate them independently: - analysis.idb.applyOperandEnums controls enum displays. - analysis.idb.applyOperandFormats controls number formats. Add analysis.idb.skipDefaultOperandFormats (default true): when applying number formats, skip operands whose format already matches Binary Ninja's default rendering (hexadecimal). These make up the bulk of formatted operands and applying them would not change the displayed text, so skipping them greatly reduces the disassembly work without affecting the result.
1 parent ced97c8 commit fc17355

3 files changed

Lines changed: 113 additions & 24 deletions

File tree

plugins/idb_import/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ fn plugin_init() -> Result<(), ()> {
6464
match file_parser.parse(&mut file_reader) {
6565
Ok(idb_info) => {
6666
IDBMapper::new(idb_info)
67+
.with_operand_enums(load_settings.apply_operand_enums)
6768
.with_operand_formats(load_settings.apply_operand_formats)
69+
.with_skip_default_operand_formats(load_settings.skip_default_operand_formats)
6870
.map_to_view(&view);
6971
}
7072
Err(e) => {

plugins/idb_import/src/mapper.rs

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -207,23 +207,40 @@ fn apply_operand_format_display(view: &BinaryView, operand_format: &OperandForma
207207
/// The mapper can be re-used if mapping into multiple views.
208208
pub struct IDBMapper {
209209
info: IDBInfo,
210+
apply_operand_enums: bool,
210211
apply_operand_formats: bool,
212+
skip_default_operand_formats: bool,
211213
}
212214

213215
impl IDBMapper {
214216
pub fn new(info: IDBInfo) -> Self {
215217
Self {
216218
info,
219+
apply_operand_enums: false,
217220
apply_operand_formats: false,
221+
skip_default_operand_formats: true,
218222
}
219223
}
220224

225+
/// Enable displaying operands against the enumeration IDA assigned them.
226+
pub fn with_operand_enums(mut self, enabled: bool) -> Self {
227+
self.apply_operand_enums = enabled;
228+
self
229+
}
230+
221231
/// Enable applying the IDB's per-operand number formats to the disassembly.
222232
pub fn with_operand_formats(mut self, enabled: bool) -> Self {
223233
self.apply_operand_formats = enabled;
224234
self
225235
}
226236

237+
/// Skip per-operand number formats that already match Binary Ninja's default rendering
238+
/// (hexadecimal) when applying number formats.
239+
pub fn with_skip_default_operand_formats(mut self, enabled: bool) -> Self {
240+
self.skip_default_operand_formats = enabled;
241+
self
242+
}
243+
227244
pub fn map_to_view(&self, view: &BinaryView) {
228245
let Some(id0) = &self.info.id0 else {
229246
tracing::warn!("No ID0 data found, skipping mapping.");
@@ -397,36 +414,57 @@ impl IDBMapper {
397414

398415
// Per-operand number formats and enum displays need the containing function to exist, but
399416
// this import runs before functions are created. Rebase and stash them; the
400-
// analysis-completion handler in `lib.rs` applies them once functions are available. Gated
401-
// because applying them disassembles each affected instruction.
402-
if self.apply_operand_formats
403-
&& (!self.info.operand_formats.is_empty() || !self.info.operand_enums.is_empty())
404-
{
405-
tracing::info!(
406-
"Deferring {} operand formats and {} enum-displayed operands until analysis completes",
407-
self.info.operand_formats.len(),
408-
self.info.operand_enums.len()
409-
);
410-
let operand_formats = self
411-
.info
412-
.operand_formats
413-
.iter()
414-
.map(|operand_format| {
415-
let mut rebased = operand_format.clone();
416-
rebased.address = rebase(operand_format.address);
417-
rebased
418-
})
419-
.collect();
420-
let operand_enums = self
421-
.info
417+
// analysis-completion handler in `lib.rs` applies them once functions are available. Each
418+
// kind is gated by its own setting because applying them disassembles each instruction.
419+
let operand_enums: Vec<OperandEnumInfo> = if self.apply_operand_enums {
420+
self.info
422421
.operand_enums
423422
.iter()
424423
.map(|operand_enum| {
425424
let mut rebased = operand_enum.clone();
426425
rebased.address = rebase(operand_enum.address);
427426
rebased
428427
})
429-
.collect();
428+
.collect()
429+
} else {
430+
Vec::new()
431+
};
432+
let operand_formats: Vec<OperandFormatInfo> = if self.apply_operand_formats {
433+
self.info
434+
.operand_formats
435+
.iter()
436+
.filter_map(|operand_format| {
437+
// When skipping defaults, drop formats that match Binary Ninja's default
438+
// rendering (hexadecimal); they make up the bulk on large databases and
439+
// changing them would not alter the displayed text.
440+
let formats: Vec<_> = operand_format
441+
.formats
442+
.iter()
443+
.filter(|(_, format)| {
444+
!(self.skip_default_operand_formats
445+
&& operand_format_matches_default(*format))
446+
})
447+
.cloned()
448+
.collect();
449+
if formats.is_empty() {
450+
return None;
451+
}
452+
Some(OperandFormatInfo {
453+
address: rebase(operand_format.address),
454+
formats,
455+
})
456+
})
457+
.collect()
458+
} else {
459+
Vec::new()
460+
};
461+
462+
if !operand_enums.is_empty() || !operand_formats.is_empty() {
463+
tracing::info!(
464+
"Deferring {} enum-displayed operands and {} operand number formats until analysis completes",
465+
operand_enums.len(),
466+
operand_formats.len()
467+
);
430468
stash_operand_display(
431469
view,
432470
PendingOperandDisplay {
@@ -1041,6 +1079,13 @@ fn integer_token_value(kind: &InstructionTextTokenKind) -> Option<u64> {
10411079
}
10421080
}
10431081

1082+
/// Whether a number format already matches Binary Ninja's default integer rendering
1083+
/// (hexadecimal). Applying such a format as an override would not change the displayed text, so it
1084+
/// can be skipped to avoid disassembling the bulk of formatted operands on large databases.
1085+
fn operand_format_matches_default(format: OperandFormat) -> bool {
1086+
matches!(format, OperandFormat::Hex)
1087+
}
1088+
10441089
/// Map an IDA operand number format to the Binary Ninja integer display type.
10451090
fn integer_display_type(format: OperandFormat) -> IntegerDisplayType {
10461091
match format {

plugins/idb_import/src/settings.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@ use std::path::PathBuf;
66
#[derive(Debug, Clone)]
77
pub struct LoadSettings {
88
pub auto_load_file: Option<PathBuf>,
9+
pub apply_operand_enums: bool,
910
pub apply_operand_formats: bool,
11+
pub skip_default_operand_formats: bool,
1012
}
1113

1214
impl LoadSettings {
1315
pub const AUTO_LOAD_FILE_DEFAULT: &'static str = "";
1416
pub const AUTO_LOAD_FILE_SETTING: &'static str = "analysis.idb.autoLoadFile";
17+
pub const APPLY_OPERAND_ENUMS_SETTING: &'static str = "analysis.idb.applyOperandEnums";
1518
pub const APPLY_OPERAND_FORMATS_SETTING: &'static str = "analysis.idb.applyOperandFormats";
19+
pub const SKIP_DEFAULT_OPERAND_FORMATS_SETTING: &'static str =
20+
"analysis.idb.skipDefaultOperandFormats";
1621

1722
pub fn register() {
1823
let bn_settings = Settings::new();
@@ -26,8 +31,21 @@ impl LoadSettings {
2631
});
2732
bn_settings.register_setting_json(Self::AUTO_LOAD_FILE_SETTING, &file_props.to_string());
2833

34+
let operand_enums_props = json!({
35+
"title" : "Apply IDB Enum Operands",
36+
"type" : "boolean",
37+
"default" : false,
38+
"description" : "Display operands against the enumeration IDA assigned them. This is \
39+
applied once analysis has created the functions and only affects the handful of \
40+
operands IDA displays as enumeration members, so it is inexpensive."
41+
});
42+
bn_settings.register_setting_json(
43+
Self::APPLY_OPERAND_ENUMS_SETTING,
44+
&operand_enums_props.to_string(),
45+
);
46+
2947
let operand_formats_props = json!({
30-
"title" : "Apply IDB Operand Formats",
48+
"title" : "Apply IDB Operand Number Formats",
3149
"type" : "boolean",
3250
"default" : false,
3351
"description" : "Apply the per-operand number formats (hexadecimal, decimal, \
@@ -39,6 +57,20 @@ impl LoadSettings {
3957
Self::APPLY_OPERAND_FORMATS_SETTING,
4058
&operand_formats_props.to_string(),
4159
);
60+
61+
let skip_default_formats_props = json!({
62+
"title" : "Skip Default IDB Operand Number Formats",
63+
"type" : "boolean",
64+
"default" : true,
65+
"description" : "When applying IDB operand number formats, skip operands whose format \
66+
already matches Binary Ninja's default rendering (hexadecimal). On large databases \
67+
most formatted operands are plain hexadecimal, so skipping them greatly reduces \
68+
the work without changing the displayed result."
69+
});
70+
bn_settings.register_setting_json(
71+
Self::SKIP_DEFAULT_OPERAND_FORMATS_SETTING,
72+
&skip_default_formats_props.to_string(),
73+
);
4274
}
4375

4476
pub fn from_view_settings(view: &BinaryView) -> Self {
@@ -53,10 +85,18 @@ impl LoadSettings {
5385
load_settings.auto_load_file = Some(path);
5486
}
5587
}
88+
if settings.contains(Self::APPLY_OPERAND_ENUMS_SETTING) {
89+
load_settings.apply_operand_enums =
90+
settings.get_bool_with_opts(Self::APPLY_OPERAND_ENUMS_SETTING, &mut query_opts);
91+
}
5692
if settings.contains(Self::APPLY_OPERAND_FORMATS_SETTING) {
5793
load_settings.apply_operand_formats =
5894
settings.get_bool_with_opts(Self::APPLY_OPERAND_FORMATS_SETTING, &mut query_opts);
5995
}
96+
if settings.contains(Self::SKIP_DEFAULT_OPERAND_FORMATS_SETTING) {
97+
load_settings.skip_default_operand_formats = settings
98+
.get_bool_with_opts(Self::SKIP_DEFAULT_OPERAND_FORMATS_SETTING, &mut query_opts);
99+
}
60100
load_settings
61101
}
62102
}
@@ -65,7 +105,9 @@ impl Default for LoadSettings {
65105
fn default() -> Self {
66106
Self {
67107
auto_load_file: None,
108+
apply_operand_enums: false,
68109
apply_operand_formats: false,
110+
skip_default_operand_formats: true,
69111
}
70112
}
71113
}

0 commit comments

Comments
 (0)