about summary refs log tree commit diff
path: root/library/stdarch/crates/stdarch-test/src/disassembly.rs
diff options
context:
space:
mode:
authorgnzlbg <gonzalobg88@gmail.com>2019-07-08 23:21:37 +0200
committergnzlbg <gnzlbg@users.noreply.github.com>2019-07-09 01:37:07 +0200
commit686b813f5d8ac504fb2f254731d0d681147d415e (patch)
tree034107a6b30e4447047cfc0cd160e8606907da5d /library/stdarch/crates/stdarch-test/src/disassembly.rs
parent127f13f10fb0d34eb6000f2c5b16e0cbc9a469ea (diff)
downloadrust-686b813f5d8ac504fb2f254731d0d681147d415e.tar.gz
rust-686b813f5d8ac504fb2f254731d0d681147d415e.zip
Update repo name
Diffstat (limited to 'library/stdarch/crates/stdarch-test/src/disassembly.rs')
-rw-r--r--library/stdarch/crates/stdarch-test/src/disassembly.rs182
1 files changed, 182 insertions, 0 deletions
diff --git a/library/stdarch/crates/stdarch-test/src/disassembly.rs b/library/stdarch/crates/stdarch-test/src/disassembly.rs
new file mode 100644
index 00000000000..23ebd92e740
--- /dev/null
+++ b/library/stdarch/crates/stdarch-test/src/disassembly.rs
@@ -0,0 +1,182 @@
+//! Disassembly calling function for most targets.
+
+use std::{env, collections::HashSet, process::Command, str};
+use crate::Function;
+
+// Extracts the "shim" name from the `symbol`.
+fn normalize(mut symbol: &str) -> String {
+    // Remove trailing colon:
+    if symbol.ends_with(':') {
+        symbol = &symbol[..symbol.len() - 1];
+    }
+    if symbol.ends_with('>') {
+        symbol = &symbol[..symbol.len() - 1];
+    }
+    if let Some(idx) = symbol.find('<') {
+        symbol = &symbol[idx + 1..];
+    }
+
+    let mut symbol = rustc_demangle::demangle(symbol).to_string();
+    symbol = match symbol.rfind("::h") {
+        Some(i) => symbol[..i].to_string(),
+        None => symbol.to_string(),
+    };
+
+    // Remove Rust paths
+    if let Some(last_colon) = symbol.rfind(':') {
+        symbol = (&symbol[last_colon + 1..]).to_string();
+    }
+
+    // Normalize to no leading underscore to handle platforms that may
+    // inject extra ones in symbol names.
+    while symbol.starts_with('_') {
+        symbol.remove(0);
+    }
+    symbol
+}
+
+pub(crate) fn disassemble_myself() -> HashSet<Function> {
+    let me = env::current_exe().expect("failed to get current exe");
+
+    let disassembly = if cfg!(target_arch = "x86_64")
+        && cfg!(target_os = "windows")
+        && cfg!(target_env = "msvc")
+    {
+        let mut cmd = cc::windows_registry::find(
+            "x86_64-pc-windows-msvc",
+            "dumpbin.exe",
+        ).expect("failed to find `dumpbin` tool");
+        let output = cmd
+            .arg("/DISASM")
+            .arg(&me)
+            .output()
+            .expect("failed to execute dumpbin");
+        println!(
+            "{}\n{}",
+            output.status,
+            String::from_utf8_lossy(&output.stderr)
+        );
+        assert!(output.status.success());
+        String::from_utf8(output.stdout)
+    } else if cfg!(target_os = "windows") {
+        panic!("disassembly unimplemented")
+    } else if cfg!(target_os = "macos") {
+        let output = Command::new("otool")
+            .arg("-vt")
+            .arg(&me)
+            .output()
+            .expect("failed to execute otool");
+        println!(
+            "{}\n{}",
+            output.status,
+            String::from_utf8_lossy(&output.stderr)
+        );
+        assert!(output.status.success());
+
+        String::from_utf8(output.stdout)
+    } else {
+        let objdump =
+            env::var("OBJDUMP").unwrap_or_else(|_| "objdump".to_string());
+        let output = Command::new(objdump.clone())
+            .arg("--disassemble")
+            .arg(&me)
+            .output()
+            .unwrap_or_else(|_| panic!(
+                "failed to execute objdump. OBJDUMP={}",
+                objdump
+            ));
+        println!(
+            "{}\n{}",
+            output.status,
+            String::from_utf8_lossy(&output.stderr)
+        );
+        assert!(output.status.success());
+
+        String::from_utf8(output.stdout)
+    }.expect("failed to convert to utf8");
+
+    parse(&disassembly)
+}
+
+fn parse(output: &str) -> HashSet<Function> {
+    let mut lines = output.lines();
+
+    for line in output.lines().take(100) {
+        println!("{}", line);
+    }
+
+    let mut functions = HashSet::new();
+    let mut cached_header = None;
+    while let Some(header) = cached_header.take().or_else(|| lines.next()) {
+        if !header.ends_with(':') || !header.contains("stdarch_test_shim") {
+            continue
+        }
+        let symbol = normalize(header);
+        let mut instructions = Vec::new();
+        while let Some(instruction) = lines.next() {
+            if instruction.ends_with(':') {
+                cached_header = Some(instruction);
+                break;
+            }
+            if instruction.is_empty() {
+                cached_header = None;
+                break;
+            }
+            let parts = if cfg!(target_os = "macos") {
+                // Each line of instructions should look like:
+                //
+                //      $addr    $instruction...
+                instruction
+                    .split_whitespace()
+                    .skip(1)
+                    .map(std::string::ToString::to_string)
+                    .collect::<Vec<String>>()
+            } else if cfg!(target_env = "msvc") {
+                // Each line looks like:
+                //
+                // >  $addr: ab cd ef     $instr..
+                // >         00 12          # this line os optional
+                if instruction.starts_with("       ") {
+                    continue;
+                }
+                instruction
+                    .split_whitespace()
+                    .skip(1)
+                    .skip_while(|s| {
+                        s.len() == 2 && usize::from_str_radix(s, 16).is_ok()
+                    }).map(std::string::ToString::to_string)
+                    .skip_while(|s| *s == "lock") // skip x86-specific prefix
+                    .collect::<Vec<String>>()
+            } else {
+                // objdump
+                // Each line of instructions should look like:
+                //
+                //      $rel_offset: ab cd ef 00    $instruction...
+                let expected_len
+                    = if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
+                        8
+                    } else {
+                        2
+                    };
+
+                instruction
+                    .split_whitespace()
+                    .skip(1)
+                    .skip_while(|s| {
+                        s.len() == expected_len
+                            && usize::from_str_radix(s, 16).is_ok()
+                    })
+                    .skip_while(|s| *s == "lock") // skip x86-specific prefix
+                    .map(std::string::ToString::to_string)
+                    .collect::<Vec<String>>()
+            };
+            instructions.push(parts.join(" "));
+        }
+        let function = Function {
+            name: symbol,
+            instrs: instructions
+        };
+        assert!(functions.insert(function));
+    }
+    functions
+}