| //! Disassembly calling function for `wasm32` targets. | |
| use crate::Function; | |
| use std::collections::HashSet; | |
| use std::path::Path; | |
| pub(crate) fn disassemble_myself() -> HashSet<Function> { | |
| // Use `std::env::args` to find the path to our executable. Assume the | |
| // environment is configured such that we can read that file. Read it and | |
| // use the `wasmprinter` crate to transform the binary to text, then search | |
| // the text for appropriately named functions. | |
| let me = std::env::args() | |
| .next() | |
| .expect("failed to find current wasm file"); | |
| let me = Path::new(&me); | |
| let me = if me.exists() { | |
| // Old build-dir layout | |
| me.to_path_buf() | |
| } else { | |
| // Cargo's build-dir layout stores an artifact named | |
| // `<crate>-<hash>.wasm` at `<crate>/<hash>/out/<crate>-<hash>.wasm`. | |
| // The build directory is mounted as the WASI working directory, so | |
| // reconstruct that guest-visible path from the executable name. | |
| let file_name = me | |
| .file_name() | |
| .and_then(|name| name.to_str()) | |
| .expect("current wasm file has a file name"); | |
| let stem = file_name | |
| .strip_suffix(".wasm") | |
| .expect("current wasm file does not have a .wasm extension"); | |
| let (crate_name, hash) = stem | |
| .rsplit_once('-') | |
| .expect("current wasm file name does not contain an artifact hash"); | |
| Path::new(crate_name).join(hash).join("out").join(file_name) | |
| }; | |
| let output = wasmprinter::print_file(&me).unwrap(); | |
| let mut ret: HashSet<Function> = HashSet::new(); | |
| let mut lines = output.lines().map(|s| s.trim()); | |
| while let Some(line) = lines.next() { | |
| // If this isn't a function, we don't care about it. | |
| if !line.starts_with("(func ") { | |
| continue; | |
| } | |
| let mut function = Function { | |
| name: String::new(), | |
| instrs: Vec::new(), | |
| }; | |
| // Empty functions will end in `))` so there's nothing to do, otherwise | |
| // we'll have a bunch of following lines which are instructions. | |
| // | |
| // Lines that have an imbalanced `)` mark the end of a function. | |
| if !line.ends_with("))") { | |
| while let Some(line) = lines.next() { | |
| function.instrs.push(line.to_string()); | |
| if !line.starts_with("(") && line.ends_with(")") { | |
| break; | |
| } | |
| } | |
| } | |
| // The second element here split on whitespace should be the name of | |
| // the function, skipping the type/params/results | |
| function.name = line.split_whitespace().nth(1).unwrap().to_string(); | |
| if function.name.starts_with("$") { | |
| function.name = function.name[1..].to_string() | |
| } | |
| if !function.name.contains("stdarch_test_shim") { | |
| continue; | |
| } | |
| assert!(ret.insert(function)); | |
| } | |
| return ret; | |
| } |