twiggy_parser/
parser.rs

1//! Parses binaries into `twiggy_ir::Items`.
2
3#![deny(missing_docs)]
4#![deny(missing_debug_implementations)]
5
6use std::ffi::OsStr;
7use std::fs;
8use std::io::Read;
9use std::path;
10
11use twiggy_ir as ir;
12use twiggy_traits as traits;
13
14#[cfg(feature = "dwarf")]
15mod object_parse;
16mod wasm_parse;
17
18const WASM_MAGIC_NUMBER: [u8; 4] = [0x00, 0x61, 0x73, 0x6D];
19
20/// Parse the file at the given path into IR items.
21pub fn read_and_parse<P: AsRef<path::Path>>(
22    path: P,
23    mode: traits::ParseMode,
24) -> anyhow::Result<ir::Items> {
25    let path = path.as_ref();
26    let mut file = fs::File::open(path)?;
27    let mut data = vec![];
28    file.read_to_end(&mut data)?;
29
30    match mode {
31        traits::ParseMode::Wasm => parse_wasm(&data),
32        #[cfg(feature = "dwarf")]
33        traits::ParseMode::Dwarf => parse_other(&data),
34        traits::ParseMode::Auto => parse_auto(path.extension(), &data),
35    }
36}
37
38/// Parse the given data into IR items.
39pub fn parse(data: &[u8]) -> anyhow::Result<ir::Items> {
40    parse_fallback(data)
41}
42
43/// A trait for parsing things into `ir::Item`s.
44pub(crate) trait Parse<'a> {
45    /// Any extra data needed to parse this type's items.
46    type ItemsExtra;
47
48    /// Parse `Self` into one or more `ir::Item`s and add them to the builder.
49    fn parse_items(
50        self,
51        items: &mut ir::ItemsBuilder,
52        extra: Self::ItemsExtra,
53    ) -> anyhow::Result<()>;
54
55    /// Any extra data needed to parse this type's edges.
56    type EdgesExtra;
57
58    /// Parse edges between items. This is only called *after* we have already
59    /// parsed items.
60    fn parse_edges(
61        self,
62        items: &mut ir::ItemsBuilder,
63        extra: Self::EdgesExtra,
64    ) -> anyhow::Result<()>;
65}
66
67fn parse_auto(extension: Option<&OsStr>, data: &[u8]) -> anyhow::Result<ir::Items> {
68    if sniff_wasm(extension, &data) {
69        parse_wasm(&data)
70    } else {
71        #[cfg(feature = "dwarf")]
72        let res = parse_other(&data);
73        #[cfg(not(feature = "dwarf"))]
74        let res = parse_fallback(&data);
75        res
76    }
77}
78
79fn sniff_wasm(extension: Option<&OsStr>, data: &[u8]) -> bool {
80    match extension.and_then(|s| s.to_str()) {
81        Some("wasm") => true,
82        _ => data.get(0..4) == Some(&WASM_MAGIC_NUMBER),
83    }
84}
85
86fn parse_wasm(data: &[u8]) -> anyhow::Result<ir::Items> {
87    let mut items = ir::ItemsBuilder::new(data.len() as u32);
88
89    let module1 = wasm_parse::ModuleReader::new(data);
90    module1.parse_items(&mut items, ())?;
91    let module2 = wasm_parse::ModuleReader::new(data);
92    module2.parse_edges(&mut items, ())?;
93
94    Ok(items.finish())
95}
96
97#[cfg(feature = "dwarf")]
98fn parse_other(data: &[u8]) -> anyhow::Result<ir::Items> {
99    object_parse::parse(&data)
100}
101
102fn parse_fallback(data: &[u8]) -> anyhow::Result<ir::Items> {
103    parse_wasm(data)
104}