Skip to main content

probe_names/
probe_names.rs

1//! Tally a database's name list by weak/public binding and by which `NameFlags` spellings disagree,
2//! printing examples of each. Verifies whether a fixture can distinguish the name accessors.
3//!
4//!   `cargo run -p idakit --example probe_names -- <db.i64> [max-names]`
5
6use idakit::prelude::*;
7
8fn main() -> Result<(), Box<dyn std::error::Error>> {
9    let mut args = std::env::args().skip(1);
10    let bin = args
11        .next()
12        .expect("usage: probe_names <db.i64> [max-names]");
13    let max: usize = args.next().and_then(|s| s.parse().ok()).unwrap_or(50_000);
14
15    Ida::run(move |ida| -> Result<(), Error> {
16        ida.call(move |idb| -> Result<(), Error> {
17            idb.open(&bin).call()?;
18
19            let (mut total, mut weak, mut public, mut mangled) = (0usize, 0usize, 0usize, 0usize);
20            let mut demangles = 0usize;
21            let mut substitutes = 0usize;
22            let mut short_differs = 0usize;
23            let mut short_vs_long = 0usize;
24            let mut samples: Vec<String> = Vec::new();
25            let mut first_weak: Option<usize> = None;
26            let mut first_public: Option<usize> = None;
27
28            for Name { address, name } in idb.names().take(max) {
29                total += 1;
30                let is_weak = idb.is_weak_name(address);
31                weak += usize::from(is_weak);
32                if is_weak && first_weak.is_none() {
33                    first_weak = Some(total - 1);
34                }
35                if idb.is_public_name(address) && first_public.is_none() {
36                    first_public = Some(total - 1);
37                }
38                public += usize::from(idb.is_public_name(address));
39                if name.starts_with("_Z") {
40                    mangled += 1;
41                }
42                if idb.demangle(&name).is_some() {
43                    demangles += 1;
44                }
45
46                let raw = idb.name_with(address, NameFlags::empty());
47                let visible = idb.visible_name(address);
48                if raw != visible {
49                    substitutes += 1;
50                    if samples.len() < 4 {
51                        samples.push(format!(
52                            "  subst {:#x}\n    raw     {raw:?}\n    visible {visible:?}",
53                            address.get()
54                        ));
55                    }
56                }
57
58                // The mutant that survives collapses `short_name` to plain VISIBLE, so this is
59                // the exact comparison that has to differ somewhere for a test to catch it.
60                let short = idb.short_name(address);
61                if short != visible {
62                    short_differs += 1;
63                }
64                if short != idb.long_name(address) {
65                    short_vs_long += 1;
66                }
67
68                if is_weak && name.starts_with("_Z") && samples.len() < 8 {
69                    samples.push(format!(
70                        "  weak+mangled {:#x}\n    raw   {name:?}\n    short {short:?}",
71                        address.get()
72                    ));
73                }
74            }
75
76            println!("=== {total} names scanned");
77            println!("weak            {weak}  (first at index {first_weak:?})");
78            println!("public          {public}  (first at index {first_public:?})");
79            println!("raw '_Z' prefix {mangled}");
80            println!("demangle(name)  {demangles}");
81            println!("raw != visible  {substitutes}");
82            println!("short != visible {short_differs}   <- kills the short_name mutant");
83            println!("short != long    {short_vs_long}");
84            println!("=== samples");
85            for s in &samples {
86                println!("{s}");
87            }
88
89            idb.close(false);
90            Ok(())
91        })??;
92        Ok(())
93    })??;
94
95    println!("PROBE_NAMES OK");
96    Ok(())
97}