wasm_nm/
lib.rs

1/*!
2
3[![](https://docs.rs/wasm-nm/badge.svg)](https://docs.rs/wasm-nm/) [![](https://img.shields.io/crates/v/wasm-nm.svg)](https://crates.io/crates/wasm-nm) [![](https://img.shields.io/crates/d/wasm-nm.png)](https://crates.io/crates/wasm-nm) [![Build Status](https://travis-ci.org/fitzgen/wasm-nm.png?branch=master)](https://travis-ci.org/fitzgen/wasm-nm)
4
5List the symbols within a wasm file.
6
7* [Library](#library)
8* [Executable](#executable)
9* [License](#license)
10* [Contributing](#contributing)
11
12## Executable
13
14To install the `wasm-nm` executable, run
15
16```text
17$ cargo install wasm-nm
18```
19
20For information on using the `wasm-nm` executable, run
21
22```text
23$ wasm-nm --help
24```
25
26### Using `wasm-nm` as a Size Profiler
27
28`wasm-nm` can function as a rudimentary size profiler for `.wasm` files.
29
30The `-z` option enables printing a function's code size. The unix `sort` utility
31can be used to sort the symbols by size. The `rustfilt` utility can be used to
32demangle Rust symbols (`cargo install rustfilt`).
33
34```text
35$ wasm-nm -z path/to/something.wasm | sort -n -u -r | rustfilt | head
363578 p dlmalloc::dlmalloc::Dlmalloc::malloc::hb37c2fafc9847520
373307 e quicksilver
381427 p <str as core::fmt::Debug>::fmt::h0cf4ea19d7121472
391287 p std::panicking::rust_panic_with_hook::h52b2005910c55f47
401268 p core::fmt::Formatter::pad::hdb2be9f507201bd1
411248 p core::str::slice_error_fail::h09ffe3974e261c49
421064 p core::fmt::write::h914fcaafc6fb200a
43987 p core::fmt::Formatter::pad_integral::h2f2f83d99c318b28
44945 p <&'a T as core::fmt::Debug>::fmt::h4a5a01d440d30f67
45918 p dlmalloc::dlmalloc::Dlmalloc::free::h8185738df2a87b48
46```
47
48## Library
49
50To use `wasm-nm` as a library, add this to your `Cargo.toml`:
51
52```toml
53[dependencies.wasm-nm]
54# Do not build the executable.
55default-features = false
56```
57
58See [docs.rs/wasm-nm][docs] for API documentation.
59
60[docs]: https://docs.rs/wasm-nm
61
62## License
63
64Licensed under either of
65
66 * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
67
68 * [MIT license](http://opensource.org/licenses/MIT)
69
70at your option.
71
72## Contributing
73
74See
75[CONTRIBUTING.md](https://github.com/fitzgen/wasm-nm/blob/master/CONTRIBUTING.md)
76for hacking.
77
78Unless you explicitly state otherwise, any contribution intentionally submitted
79for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
80dual licensed as above, without any additional terms or conditions.
81
82 */
83
84#![deny(missing_docs)]
85#![deny(missing_debug_implementations)]
86
87extern crate failure;
88extern crate parity_wasm;
89
90use parity_wasm::elements::{Deserialize, FuncBody, ImportEntry, Internal, Module, Section,
91                            Serialize, VarUint32, VarUint7};
92use std::borrow::Cow;
93use std::collections::HashMap;
94use std::fmt;
95use std::iter;
96use std::io;
97use std::slice;
98use std::str;
99
100/// Options for controlling which symbols are iterated over.
101#[derive(Clone, Debug)]
102pub struct Options {
103    /// Should imported symbols be iterated over?
104    pub imports: bool,
105
106    /// Should exported symbols be iterated over?
107    pub exports: bool,
108
109    /// Should private symbols be iterated over?
110    pub privates: bool,
111
112    /// Should the symbols' sizes be computed?
113    pub sizes: bool,
114}
115
116impl Default for Options {
117    fn default() -> Options {
118        Options {
119            imports: true,
120            exports: true,
121            privates: true,
122            sizes: false,
123        }
124    }
125}
126
127impl Options {
128    /// Construct options for iterating over *none* of the symbol kinds.
129    pub fn nothing() -> Options {
130        Options {
131            imports: false,
132            exports: false,
133            privates: false,
134            sizes: false,
135        }
136    }
137}
138
139/// Get the symbols in the given wasm file.
140pub fn symbols<R>(opts: Options, reader: &mut R) -> Result<Symbols, failure::Error>
141where
142    R: io::Read,
143{
144    let module = Module::deserialize(reader)?;
145    Ok(Symbols { opts, module })
146}
147
148/// The set of symbols in a wasm file.
149pub struct Symbols {
150    opts: Options,
151    module: Module,
152}
153
154impl fmt::Debug for Symbols {
155    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156        f.debug_struct("Symbols")
157            .field("opts", &self.opts)
158            .field("module", &"...")
159            .finish()
160    }
161}
162
163// Cribbed from wasm-gc; waiting for the name section support to be upstreamed
164// into parity-wasm.
165fn decode_name_map<'a>(
166    mut bytes: &'a [u8],
167    num_imports: usize,
168) -> Result<HashMap<u32, Cow<'a, str>>, failure::Error> {
169    while !bytes.is_empty() {
170        let name_type = u8::from(VarUint7::deserialize(&mut bytes)?);
171        let name_payload_len = u32::from(VarUint32::deserialize(&mut bytes)?);
172        let (these_bytes, rest) = bytes.split_at(name_payload_len as usize);
173
174        if name_type == 1 {
175            bytes = these_bytes;
176        } else {
177            bytes = rest;
178            continue;
179        }
180
181        let count = u32::from(VarUint32::deserialize(&mut bytes)?);
182        let mut names = HashMap::with_capacity(count as usize);
183        for _ in 0..count {
184            let index =
185                u32::from(VarUint32::deserialize(&mut bytes)?).saturating_sub(num_imports as u32);
186            let name_len = u32::from(VarUint32::deserialize(&mut bytes)?);
187            let (name, rest) = bytes.split_at(name_len as usize);
188            bytes = rest;
189            let name = String::from_utf8_lossy(name);
190            names.insert(index, name);
191        }
192        return Ok(names);
193    }
194
195    return Ok(Default::default());
196}
197
198impl Symbols {
199    /// Iterate over the symbols.
200    pub fn iter(&self) -> SymbolsIter {
201        // Find the set of function indices that are exported.
202        let exports = self.module
203            .export_section()
204            .map_or(HashMap::new(), |section| {
205                section
206                    .entries()
207                    .iter()
208                    .filter_map(|entry| match *entry.internal() {
209                        Internal::Function(idx) => Some((idx, entry.field())),
210                        _ => None,
211                    })
212                    .collect()
213            });
214
215        let num_imports = self.module
216            .import_section()
217            .map_or(0, |imports| imports.entries().len());
218
219        let names = self.module
220            .sections()
221            .iter()
222            .filter_map(|section| match *section {
223                Section::Custom(ref custom) if custom.name() == "name" => Some(custom),
224                _ => None,
225            })
226            .next()
227            .and_then(|name_section| decode_name_map(name_section.payload(), num_imports).ok());
228
229        SymbolsIter {
230            symbols: self,
231            state: SymbolsIterState::new(self),
232            exports,
233            names,
234        }
235    }
236}
237
238/// An iterator returned by `Symbols::iter`, which iterates over the symbols in
239/// a wasm file.
240#[derive(Debug)]
241pub struct SymbolsIter<'a> {
242    symbols: &'a Symbols,
243    state: SymbolsIterState<'a>,
244    exports: HashMap<u32, &'a str>,
245    names: Option<HashMap<u32, Cow<'a, str>>>,
246}
247
248#[derive(Debug)]
249enum SymbolsIterState<'a> {
250    Imports(slice::Iter<'a, ImportEntry>),
251    Functions(iter::Enumerate<slice::Iter<'a, FuncBody>>),
252    Finished,
253}
254
255impl<'a> SymbolsIterState<'a> {
256    fn new(symbols: &'a Symbols) -> SymbolsIterState<'a> {
257        SymbolsIterState::Imports(if let Some(section) = symbols.module.import_section() {
258            section.entries().iter()
259        } else {
260            [].iter()
261        })
262    }
263}
264
265fn function_size(index: usize, module: &Module) -> Option<usize> {
266    module
267        .code_section()
268        .and_then(|section| section.bodies().iter().nth(index))
269        .and_then(|body| {
270            let mut encoded = vec![];
271            if let Err(_) = body.code().clone().serialize(&mut encoded) {
272                return None;
273            }
274            Some(encoded.len())
275        })
276}
277
278impl<'a> Iterator for SymbolsIter<'a> {
279    type Item = Symbol<'a>;
280
281    fn next(&mut self) -> Option<Symbol<'a>> {
282        loop {
283            self.state = match self.state {
284                SymbolsIterState::Finished => return None,
285                SymbolsIterState::Imports(ref mut imports) => match (
286                    self.symbols.opts.imports,
287                    imports.next(),
288                ) {
289                    (true, Some(import)) => {
290                        return Some(Symbol::Import {
291                            name: import.field(),
292                        })
293                    }
294                    (false, _) | (true, None) => SymbolsIterState::Functions(
295                        if let Some(section) = self.symbols.module.code_section() {
296                            section.bodies().iter().enumerate()
297                        } else {
298                            [].iter().enumerate()
299                        },
300                    ),
301                },
302                SymbolsIterState::Functions(ref mut functions) => {
303                    let (i, function) = match functions.next() {
304                        Some(next) => next,
305                        _ => break,
306                    };
307                    match (i, function, self.exports.get(&(i as u32))) {
308                        (i, _, Some(export)) if self.symbols.opts.exports => {
309                            return Some(Symbol::Export {
310                                name: export,
311                                size: if self.symbols.opts.sizes {
312                                    function_size(i, &self.symbols.module)
313                                } else {
314                                    None
315                                },
316                            });
317                        }
318                        (i, _function, None) if self.symbols.opts.privates => {
319                            let i = i as u32;
320                            let name = self.names.as_ref().and_then(|names| names.get(&i).cloned());
321                            return Some(Symbol::Private {
322                                index: i,
323                                name,
324                                size: if self.symbols.opts.sizes {
325                                    function_size(i as usize, &self.symbols.module)
326                                } else {
327                                    None
328                                },
329                            });
330                        }
331                        _ => {
332                            continue;
333                        }
334                    }
335                }
336            };
337        }
338
339        self.state = SymbolsIterState::Finished;
340        None
341    }
342}
343
344/// A symbol from a wasm file.
345#[derive(Clone, Debug)]
346pub enum Symbol<'a> {
347    /// An imported symbol.
348    Import {
349        /// The symbol's name.
350        name: &'a str,
351    },
352
353    /// An exported symbol.
354    Export {
355        /// The symbol's name.
356        name: &'a str,
357        /// The symbol's size, if available.
358        size: Option<usize>,
359    },
360
361    /// A private function that is not exported.
362    Private {
363        /// The function table index for this private function.
364        index: u32,
365        /// The name from the name section, if that information exists.
366        name: Option<Cow<'a, str>>,
367        /// The symbol's size, if available.
368        size: Option<usize>,
369    },
370}
371
372impl<'a> fmt::Display for Symbol<'a> {
373    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
374        match *self {
375            Symbol::Import { name } | Symbol::Export { name, .. } => f.write_str(name),
376            Symbol::Private {
377                name: Some(ref name),
378                ..
379            } => f.write_str(&name),
380            Symbol::Private { index, .. } => write!(f, "function[{}]", index),
381        }
382    }
383}