diff --git a/Cargo.lock b/Cargo.lock index f30de413..6ebc33d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,7 +305,6 @@ dependencies = [ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -349,11 +348,6 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "indexmap" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "indicatif" version = "0.11.0" @@ -866,7 +860,6 @@ version = "0.1.0" dependencies = [ "addr2line 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "benfred-read-process-memory 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libproc 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1222,7 +1215,6 @@ dependencies = [ "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" "checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c" "checksum inferno 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49e6ff860d0c74e7dd60a16798053fafe9dff115870c44380b1241e553cb1b99" "checksum intervaltree 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "af39074dd8d5eff756ddea3d8f34c7ae287d4dadb6f29fb1b67ca6b3f5036482" diff --git a/build.rs b/build.rs index 58c49177..4c9076f6 100644 --- a/build.rs +++ b/build.rs @@ -4,14 +4,6 @@ fn main() { // copied from remoteprocess/build.rs because I couldn't find a way to share this match env::var("CARGO_CFG_TARGET_OS").unwrap().as_ref() { "windows" => println!("cargo:rustc-cfg=unwind"), - "macos" => { - // OSX native profiling doesn't work all that well right now, and - // its broken enough that I don't want to support it at the moment. - // only enable if a specific env variable is set - if std::env::var("PYSPY_ALLOW_NATIVE_PROFILING").is_ok() { - println!("cargo:rustc-cfg=unwind"); - } - }, "linux" => { // We only support native unwinding on x86_64 linux if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"{ diff --git a/remoteprocess/Cargo.lock b/remoteprocess/Cargo.lock index 654b33d5..f4ec0691 100644 --- a/remoteprocess/Cargo.lock +++ b/remoteprocess/Cargo.lock @@ -28,14 +28,6 @@ dependencies = [ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "arrayvec" version = "0.4.11" @@ -85,28 +77,6 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "bindgen" -version = "0.49.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "bitflags" version = "1.1.0" @@ -122,43 +92,11 @@ name = "cc" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "cexpr" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cfg-if" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "clang-sys" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clap" -version = "2.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cpp_demangle" version = "0.2.13" @@ -234,14 +172,6 @@ dependencies = [ "miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "gimli" version = "0.19.0" @@ -250,7 +180,6 @@ dependencies = [ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -277,11 +206,6 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "indexmap" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "intervaltree" version = "0.2.4" @@ -314,15 +238,6 @@ name = "libc" version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "libloading" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "libproc" version = "0.3.2" @@ -429,15 +344,6 @@ name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "object" version = "0.12.0" @@ -455,11 +361,6 @@ name = "parity-wasm" version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "plain" version = "0.2.3" @@ -524,9 +425,7 @@ version = "0.1.0" dependencies = [ "addr2line 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "benfred-read-process-memory 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bindgen 0.49.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libproc 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -585,11 +484,6 @@ name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "shlex" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "smallvec" version = "0.6.10" @@ -600,11 +494,6 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "syn" version = "0.15.42" @@ -634,14 +523,6 @@ dependencies = [ "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "thread_local" version = "0.3.6" @@ -655,11 +536,6 @@ name = "ucd-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unicode-width" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.1.0" @@ -675,30 +551,11 @@ name = "uuid" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "vec_map" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "which" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "winapi" version = "0.2.8" @@ -749,20 +606,15 @@ dependencies = [ "checksum addr2line 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95b06ae5a8a3bae54910c9029a52f83203ce2001c71b10b1faae3a337fee4ab5" "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "b5164d292487f037ece34ec0de2fcede2faa162f085dd96d2385ab81b12765ba" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum benfred-read-process-memory 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3bf2e237e12009727c3b0cc39eb34f6b29dbc2957f5d643f34ade1e5a7016c9" -"checksum bindgen 0.49.2 (registry+https://github.com/rust-lang/crates.io-index)" = "846a1fba6535362a01487ef6b10f0275faa12e5c5d835c5c1c627aabc46ccbd6" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" -"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" -"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cpp_demangle 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1d355451c6fb0dd4142db91e8dbd020d0d5466393957e771032bb9bf779504b4" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" @@ -771,18 +623,15 @@ dependencies = [ "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" "checksum flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "550934ad4808d5d39365e5d61727309bf18b3b02c6c56b729cb92e7dd84bc3d8" -"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "162d18ae5f2e3b90a993d202f1ba17a5633c2484426f8bcae201f86194bacd00" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum goblin 0.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7f55d53401eb2fd30afd025c570b1946b6966344acf21b42e31286f3bf89e6a8" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" "checksum intervaltree 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "af39074dd8d5eff756ddea3d8f34c7ae287d4dadb6f29fb1b67ca6b3f5036482" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" -"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" "checksum libproc 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "58d23e79d5a9fc3f86a75bcd31a5418f27104518aca33615802b0373380beab4" "checksum libproc 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfacafc8e193a2090dd8716eca0fd2b8116801b692648441c321a88600c96f2" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" @@ -796,10 +645,8 @@ dependencies = [ "checksum miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6c675792957b0d19933816c4e1d56663c341dd9bfa31cb2140ff2267c1d8ecf4" "checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum object 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df4af347f5ac3d0e83e78c26be33cd10e8e874dcb68517a909ad802ba50a90b5" "checksum parity-wasm 0.38.0 (registry+https://github.com/rust-lang/crates.io-index)" = "20d7e522a7f994cc4ae32970b1ce0d99ecf91b8e1df080517a26faa6d2e2ee62" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-maps 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f83fdad949003618365cc72532931c709cf68d98cd4310c1286566718abde698" @@ -813,24 +660,17 @@ dependencies = [ "checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde" "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" -"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/remoteprocess/Cargo.toml b/remoteprocess/Cargo.toml index 16fc4874..a892dfd5 100644 --- a/remoteprocess/Cargo.toml +++ b/remoteprocess/Cargo.toml @@ -10,7 +10,6 @@ license = "GPL-3.0" build="build.rs" [dependencies] -gimli = "0.19.0" libc = "0.2" log = "0.4" proc-maps = "0.1.6" diff --git a/remoteprocess/README.md b/remoteprocess/README.md index 2ed67360..d97fe8b0 100644 --- a/remoteprocess/README.md +++ b/remoteprocess/README.md @@ -59,11 +59,5 @@ processors, or from FreeBSD. ## Credits This crate heavily relies on the [gimli](https://github.com/gimli-rs/gimli) project. Gimli is an -amazing tool for parsing DWARF debugging information, and we are using it here for getting -stack traces and looking up filename and line numbers given an instruction pointeer. - -This crate includes code that was originally written as part of -[backtrace-rs](https://github.com/alexcrichton/backtrace-rs). In particular, we are using the OSX -symbolication code from backtrace-rs, which was modified here to support working with remote processes. This also requires a utility module ([dylib.rs](https://github.com/alexcrichton/backtrace-rs/blob/master/src/dylib.rs)) from -backtrace-rs to dynamically load the core symbolication framework on OSX, and which is also being -used on linux to load libunwind-ptrace if it is installed for a fallback stack unwinder. backtrace-rs is licensed under the [MIT license](https://opensource.org/licenses/MIT), a copy of which is included in the relevant files here. +amazing tool for parsing DWARF debugging information, and we are using it here for looking up filename +and line numbers given an instruction pointeer. \ No newline at end of file diff --git a/remoteprocess/build.rs b/remoteprocess/build.rs index 3000e8a5..af401866 100644 --- a/remoteprocess/build.rs +++ b/remoteprocess/build.rs @@ -3,14 +3,6 @@ use std::env; fn main() { match env::var("CARGO_CFG_TARGET_OS").unwrap().as_ref() { "windows" => println!("cargo:rustc-cfg=unwind"), - "macos" => { - // OSX native profiling doesn't work all that well right now, and - // its broken enough that I don't want to support it at the moment. - // only enable if a specific env variable is set - if std::env::var("PYSPY_ALLOW_NATIVE_PROFILING").is_ok() { - println!("cargo:rustc-cfg=unwind"); - } - }, "linux" => { // We only support native unwinding on x86_64 linux if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"{ diff --git a/remoteprocess/examples/trace.rs b/remoteprocess/examples/trace.rs index 0200e87a..b370535b 100644 --- a/remoteprocess/examples/trace.rs +++ b/remoteprocess/examples/trace.rs @@ -10,6 +10,7 @@ fn get_backtrace(pid: remoteprocess::Pid) -> Result<(), remoteprocess::Error> { let process = remoteprocess::Process::new(pid)?; // Create a stack unwind object, and use it to get the stack for each thread let unwinder = process.unwinder()?; + let symbolicator = process.symbolicator()?; for thread in process.threads()?.iter() { println!("Thread {} - {}", thread.id()?, if thread.active()? { "running" } else { "idle" }); @@ -24,7 +25,7 @@ fn get_backtrace(pid: remoteprocess::Pid) -> Result<(), remoteprocess::Error> { // Lookup the current stack frame containing a filename/function/linenumber etc // for the current address - unwinder.symbolicate(ip, true, &mut |sf| { + symbolicator.symbolicate(ip, true, &mut |sf| { println!("\t{}", sf); })?; } diff --git a/remoteprocess/examples/validate_libunwind.rs b/remoteprocess/examples/validate_libunwind.rs deleted file mode 100644 index b1d1c75a..00000000 --- a/remoteprocess/examples/validate_libunwind.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* Simple test to validate that the unwinding done by our gimli based unwinder -is exactly the same as that from libunwind-ptrace. - -This uses both unwinders, and compares the instruction pointer and -stack pointer for each frame and make sure they match up */ -extern crate remoteprocess; -extern crate env_logger; - -#[cfg(all(target_os="linux", unwind))] -extern crate goblin; -#[cfg(all(target_os="linux", unwind))] -extern crate nix; - -#[cfg(all(target_os="linux", unwind))] -#[macro_use] -extern crate log; - - -#[cfg(all(target_os="linux", unwind))] -fn libunwind_compare(pid: remoteprocess::Pid) -> Result<(), remoteprocess::Error> { - let process = remoteprocess::Process::new(pid)?; - let unwinder = process.unwinder()?; - let libunwinder = remoteprocess::LibUnwind::new()?; - - let _lock = process.lock()?; - - let thread = remoteprocess::Thread::new(pid)?; - - let mut gimli_cursor = unwinder.cursor(&thread)?; - let mut libunwind_cursor = libunwinder.cursor(pid)?; - - loop { - match libunwind_cursor.next() { - Some(lip) => { - let ip = match gimli_cursor.next() { - None => { panic!("gimli cursor exitted before libunwind"); }, - Some(Err(e)) => { panic!("gimli cursor errored {:?}", e); }, - Some(Ok(ip)) => ip - }; - - let lip = lip?; - if libunwind_cursor.sp()? != gimli_cursor.sp() { - panic!("gimli sp 0x{:016x} != libunwind sp 0x{:016x}", gimli_cursor.sp(), libunwind_cursor.sp()?); - } - if lip != ip { - panic!("gimli ip 0x{:016x} != libunwind ip 0x{:016x}", ip, lip); - } - - info!("ip 0x{:016x} sp 0x{:016x}", ip, gimli_cursor.sp()); - } - None => { - if let Some(state) = gimli_cursor.next() { - panic!("libunwind cursor is finished, but gimli cursor produced: {:#?}", state); - } else { - break; - } - } - } - } - - Ok(()) -} - -#[cfg(all(target_os="linux", unwind))] -fn main() { - env_logger::init(); - - let args: Vec = std::env::args().collect(); - - let pid = if args.len() > 1 { - args[1].parse().expect("invalid pid") - } else { - std::process::id() - }; - - libunwind_compare(pid as i32).unwrap(); -} - -#[cfg(not(all(target_os="linux", unwind)))] -fn main() { - panic!("This example only works on linux built with unwinding support"); -} diff --git a/remoteprocess/src/dwarf_unwind.rs b/remoteprocess/src/dwarf_unwind.rs deleted file mode 100644 index 9aca6839..00000000 --- a/remoteprocess/src/dwarf_unwind.rs +++ /dev/null @@ -1,334 +0,0 @@ -use gimli; -use super::{Error, Process, ProcessMemory}; - -pub type RcReader = gimli::EndianRcSlice; -pub type FrameDescriptionEntry = gimli::FrameDescriptionEntry; -pub type UninitializedUnwindContext = gimli::UninitializedUnwindContext; - -#[cfg(target_os="linux")] -use libc::c_ulonglong; - -use gimli::UnwindSection; - -#[cfg(target_os="macos")] -use std; - -/// Contains dwarf debugging information for a single binary -#[derive(Debug)] -pub struct UnwindInfo { - // eh_frame_hdr sections only exist on linux - #[cfg(target_os="linux")] - pub eh_frame_hdr: gimli::ParsedEhFrameHdr, - - // mach binaries don't contain an eh_frame_hdr section, so instead we - // build a table of address:fde from the eh_frame section - #[cfg(target_os="macos")] - pub frame_descriptions: Vec<(u64, FrameDescriptionEntry)>, - - pub eh_frame: gimli::EhFrame, - pub bases: gimli::BaseAddresses, -} - -impl UnwindInfo { - pub fn unwind(&self, reg: &mut Registers, process: &Process) -> Result { - // TODO: better registers abstraction - #[cfg(target_os="macos")] - let pc = reg.__rip - 1; - - #[cfg(target_os="linux")] - let pc = reg.rip - 1; - - debug!("dwarf unwind 0x{:016x}", pc); - - let fde = match self.get_fde(pc) { - Ok(fde) => fde, - Err(e) => { - #[cfg(target_os="macos")] - let bp = reg.__rbp; - - #[cfg(target_os="linux")] - let bp = reg.rbp; - return match bp { 0 => Ok(false), _ => Err(e.into()) }; - } - }; - - if !fde.contains(pc) { - warn!("FDE doesn't contain pc 0x{:016x}", pc); - #[cfg(target_os="linux")] - return Err(gimli::Error::NoUnwindInfoForAddress.into()); - - // TODO: on OSX figure out why this happens so much - #[cfg(target_os="macos")] - { - debug!("FDE contains error. TODO: handle this {:?}", reg); - return Ok(false); - } - } - - debug!("got fde covers range 0x{:016x}-0x{:016x}", fde.initial_address(), fde.initial_address() + fde.len()); - - // TODO: reuse context? - let mut ctx = UninitializedUnwindContext::new(); - let row = self.get_unwind_row(pc, &mut ctx, &fde)?; - let cfa = match *row.cfa() { - gimli::CfaRule::RegisterAndOffset { register, offset } => { - debug!("cfa rule register and offset: {:?}, {}", register, offset); - get_register(reg, register).wrapping_add(offset as u64) - }, - gimli::CfaRule::Expression(ref e) => { - evaluate_dwarf_expression(e, None, reg, process)? - } - }; - - debug!("cfa is 0x{:016x}", cfa); - for &(register, ref rule) in row.registers() { - let value = match *rule { - gimli::RegisterRule::Offset(offset) => process.copy_struct(cfa.wrapping_add(offset as u64) as usize)?, - gimli::RegisterRule::Register(r) => get_register(reg, r), - gimli::RegisterRule::SameValue => get_register(reg, register), - gimli::RegisterRule::ValOffset(offset) => cfa.wrapping_add(offset as u64), - gimli::RegisterRule::Expression(ref e) => { - process.copy_struct(evaluate_dwarf_expression(e, Some(cfa), reg, process)? as usize)? - }, - gimli::RegisterRule::ValExpression(ref e) => { - evaluate_dwarf_expression(e, Some(cfa), reg, process)? - }, - gimli::RegisterRule::Architectural => unimplemented!("Unhandled dwarf rule: Architectural"), - gimli::RegisterRule::Undefined => unimplemented!("Unhandled dwarf rule: Undefined"), - }; - set_register(reg, register, value); - } - - #[cfg(target_os="macos")] - { reg.__rsp = cfa; } - - #[cfg(target_os="linux")] - { reg.rsp = cfa; } - - Ok(true) - } - - #[cfg(target_os="macos")] - fn get_fde(&self, pc: u64) -> gimli::Result<&FrameDescriptionEntry> { - // Binary search frame description table to get the FDE on osx - if self.frame_descriptions.is_empty() { - return Err(gimli::Error::NoUnwindInfoForAddress); - } - let fde = match self.frame_descriptions.binary_search_by(|e| e.0.cmp(&pc)) { - Ok(i) => &self.frame_descriptions[i].1, - Err(v) => &self.frame_descriptions[if v > 0 { v - 1 } else { v }].1 - }; - Ok(fde) - } - - #[cfg(target_os="linux")] - fn get_fde(&self, pc: u64) -> gimli::Result { - // lookup FDE inside the eh_frame_hdr section on linux - self.eh_frame_hdr.table().unwrap() - .fde_for_address(&self.eh_frame, &self.bases, pc, gimli::EhFrame::cie_from_offset) - } - - fn get_unwind_row(&self, pc: u64, ctx: &mut UninitializedUnwindContext, fde: &FrameDescriptionEntry) - -> gimli::Result> { - let mut table = gimli::UnwindTable::new(&self.eh_frame, &self.bases, ctx, &fde)?; - while let Some(row) = table.next_row()? { - if row.contains(pc) { - return Ok(row.clone()); - } - } - error!("Failed to find unwind row for 0x{:016x}", pc); - Err(gimli::Error::NoUnwindInfoForAddress) - } - - /// Creates a new UnwindInfo object for OSX. This iterates over the eh_frame section - /// and builds a lookup table of all the frame description entries found in there. - /// This is expensive to compute, but is a one time cost (after which looking up the - /// FDE is pretty cheap). Note: on linux we use the eh_frame_hdr section instead - #[cfg(target_os="macos")] - pub fn new(eh_frame: &[u8], eh_frame_address: u64) -> gimli::Result { - let buf = std::rc::Rc::from(eh_frame); - let eh_frame = gimli::EhFrame::from(RcReader::new(buf, gimli::NativeEndian)); - let bases = gimli::BaseAddresses::default().set_eh_frame(eh_frame_address); - - // Get a vector of all the frame description entries - let frame_descriptions = { - let mut frame_descriptions = Vec::new(); - let mut iter = eh_frame.entries(&bases); - while let Some(entry) = iter.next()? { - match entry { - gimli::CieOrFde::Cie(_) => continue, - gimli::CieOrFde::Fde(partial) => { - - let fde = partial.parse(|_, bases, offset| eh_frame.cie_from_offset(bases, offset))?; - frame_descriptions.push((fde.initial_address(), fde)); - } - } - } - frame_descriptions.sort_unstable_by(|a, b| a.0.cmp(&b.0)); - frame_descriptions - }; - - Ok(UnwindInfo{eh_frame, bases, frame_descriptions}) - } -} - -fn evaluate_dwarf_expression(e: &gimli::Expression, - initial: Option, registers: &Registers, process: &Process) -> Result { - // TODO: this will require different code for 32bit - let mut eval = e.clone().evaluation(gimli::Encoding{format: gimli::Format::Dwarf64, address_size: 8, version: 0}); - - if let Some(initial) = initial { - eval.set_initial_value(initial); - } - - let mut result = eval.evaluate()?; - - while result != gimli::EvaluationResult::Complete { - match result { - gimli::EvaluationResult::RequiresRegister{register, base_type} => { - debug!("reguires register {:?} {:?}", register, base_type); - result = eval.resume_with_register(gimli::Value::Generic(get_register(registers, register as _)))?; - }, - gimli::EvaluationResult::RequiresMemory{address, size, space, base_type} => { - debug!("reguires memory addr=0x{:016x} size={} space={:?} base_type={:?}", address, size, space, base_type); - match size { - 8 => { - let value: u64 = process.copy_struct(address as usize)?; - result = eval.resume_with_memory(gimli::Value::Generic(value))?; - }, - 4 => { - let value: u32 = process.copy_struct(address as usize)?; - result = eval.resume_with_memory(gimli::Value::Generic(value as u64))?; - } - _ => { - // TODO: this probably will never happen - return Err(Error::Other(format!("Unhandled dwarf expression. Size {} isn't handled for RequiresMemory", size))); - } - } - - }, - other => { - return Err(Error::Other(format!("unhandled dwarf expression requirement{:?}", other))); - } - }; - } - - let results = eval.result(); - - if results.len() != 1 { - return Err(Error::Other(format!("Failed to evaluate_dwarf_expression, expected a single result - found {}", results.len()))); - } - - match &results[0] { - gimli::Piece{location: gimli::Location::Address{address}, ..} => Ok(address.clone()), - other => { - return Err(Error::Other(format!("Unhandled dwarf evaluation result {:#?}", other))); - } - } -} - -// TODO: refactor register handling code (make functions methods, add getrsp/get_ip etc) -#[cfg(target_os="macos")] -use mach::structs::x86_thread_state64_t; - -#[cfg(target_os="macos")] -pub type Registers = x86_thread_state64_t; - -// This is identical to libc::user_regs_struct, -// which seems to be be missing for the musl toolchain we're using -// TODO: file a PR? -#[cfg(target_os="linux")] -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct Registers { - pub r15: c_ulonglong, - pub r14: c_ulonglong, - pub r13: c_ulonglong, - pub r12: c_ulonglong, - pub rbp: c_ulonglong, - pub rbx: c_ulonglong, - pub r11: c_ulonglong, - pub r10: c_ulonglong, - pub r9: c_ulonglong, - pub r8: c_ulonglong, - pub rax: c_ulonglong, - pub rcx: c_ulonglong, - pub rdx: c_ulonglong, - pub rsi: c_ulonglong, - pub rdi: c_ulonglong, - pub orig_rax: c_ulonglong, - pub rip: c_ulonglong, - pub cs: c_ulonglong, - pub eflags: c_ulonglong, - pub rsp: c_ulonglong, - pub ss: c_ulonglong, - pub fs_base: c_ulonglong, - pub gs_base: c_ulonglong, - pub ds: c_ulonglong, - pub es: c_ulonglong, - pub fs: c_ulonglong, - pub gs: c_ulonglong, -} - -#[cfg(target_os="macos")] -fn get_register(regs: &x86_thread_state64_t, register: gimli::Register) -> u64 { - unsafe { - let regs = regs as *const _ as *const u64; - *regs.offset(register.0 as isize) - } -} -#[cfg(target_os="macos")] -fn set_register(regs: &mut x86_thread_state64_t, register: gimli::Register, value: u64) { - unsafe { - let regs = regs as *mut _ as *mut u64; - *regs.offset(register.0 as isize) = value - } -} - -#[cfg(target_os="linux")] -fn get_register(regs: &Registers, register: gimli::Register) -> u64 { - // ffs - match register.0 { - 0 => regs.rax, - 1 => regs.rdx, - 2 => regs.rcx, - 3 => regs.rbx, - 4 => regs.rsi, - 5 => regs.rdi, - 6 => regs.rbp, - 7 => regs.rsp, - 8 => regs.r8, - 9 => regs.r9, - 10 => regs.r10, - 11 => regs.r11, - 12 => regs.r12, - 13 => regs.r13, - 14 => regs.r14, - 15 => regs.r15, - 16 => regs.rip, - _ => panic!("unknown reg") - } -} - -#[cfg(target_os="linux")] -fn set_register(regs: &mut Registers, register: gimli::Register, value: u64) { - match register.0 { - 0 => regs.rax = value, - 1 => regs.rdx = value, - 2 => regs.rcx = value, - 3 => regs.rbx = value, - 4 => regs.rsi = value, - 5 => regs.rdi = value, - 6 => regs.rbp = value , - 7 => regs.rsp = value, - 8 => regs.r8 = value, - 9 => regs.r9 = value, - 10 => regs.r10 = value, - 11 => regs.r11 = value, - 12 => regs.r12 = value, - 13 => regs.r13 = value, - 14 => regs.r14 = value, - 15 => regs.r15 = value, - 16 => regs.rip = value, - _ => panic!("unknown reg") - } -} diff --git a/remoteprocess/src/dylib.rs b/remoteprocess/src/dylib.rs deleted file mode 100644 index 6a6cdd41..00000000 --- a/remoteprocess/src/dylib.rs +++ /dev/null @@ -1,114 +0,0 @@ -// this code is taken from backtrace-rs/src/dylib.rs -// https://github.com/alexcrichton/backtrace-rs/blob/master/src/dylib.rs -// -// backtrace-rs is licensed under the MIT license: -/* -Copyright (c) 2014 Alex Crichton - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -use libc::{c_void, self, c_char}; -use std::marker; -use std::mem; -use std::sync::atomic::{AtomicUsize, Ordering}; - -macro_rules! dlsym { - (extern { - $(fn $name:ident($($arg:ident: $t:ty),*) -> $ret:ty;)* - }) => ($( - #[allow(non_upper_case_globals)] - static $name: self::dylib::Symbol $ret> = - self::dylib::Symbol { - name: concat!(stringify!($name), "\0"), - addr: ::std::sync::atomic::AtomicUsize::new(0), - _marker: ::std::marker::PhantomData, - }; - )*) -} - -pub struct Dylib { - pub init: AtomicUsize, -} - -pub struct Symbol { - pub name: &'static str, - pub addr: AtomicUsize, - pub _marker: marker::PhantomData, -} - -impl Dylib { - pub unsafe fn get<'a, T>(&self, sym: &'a Symbol) -> Option<&'a T> { - self.load().and_then(|handle| { - sym.get(handle) - }) - } - - pub unsafe fn init(&self, path: &str) -> bool { - if self.init.load(Ordering::SeqCst) != 0 { - return true - } - assert!(path.as_bytes()[path.len() - 1] == 0); - let ptr = libc::dlopen(path.as_ptr() as *const c_char, libc::RTLD_LAZY); - if ptr.is_null() { - return false - } - match self.init.compare_and_swap(0, ptr as usize, Ordering::SeqCst) { - 0 => {} - _ => { libc::dlclose(ptr); } - } - return true - } - - unsafe fn load(&self) -> Option<*mut c_void> { - match self.init.load(Ordering::SeqCst) { - 0 => None, - n => Some(n as *mut c_void), - } - } -} - -impl Symbol { - unsafe fn get(&self, handle: *mut c_void) -> Option<&T> { - assert_eq!(mem::size_of::(), mem::size_of_val(&self.addr)); - if self.addr.load(Ordering::SeqCst) == 0 { - self.addr.store(fetch(handle, self.name.as_ptr()), Ordering::SeqCst) - } - if self.addr.load(Ordering::SeqCst) == 1 { - None - } else { - mem::transmute::<&AtomicUsize, Option<&T>>(&self.addr) - } - } -} - -unsafe fn fetch(handle: *mut c_void, name: *const u8) -> usize { - let ptr = libc::dlsym(handle, name as *const _); - if ptr.is_null() { - 1 - } else { - ptr as usize - } -} diff --git a/remoteprocess/src/freebsd/mod.rs b/remoteprocess/src/freebsd/mod.rs index 665c1551..8cad6922 100644 --- a/remoteprocess/src/freebsd/mod.rs +++ b/remoteprocess/src/freebsd/mod.rs @@ -3,8 +3,6 @@ mod procstat; mod ptrace; mod lock; -mod registers; - use libc::{pid_t, lwpid_t}; use read_process_memory::{CopyAddress, ProcessHandle}; @@ -14,7 +12,6 @@ use std::rc::{Rc, Weak}; use super::{ProcessMemory, Error}; use freebsd::lock::ProcessLock; -use freebsd::registers::Registers; pub type Pid = pid_t; pub type Tid = lwpid_t; @@ -124,16 +121,6 @@ impl Thread { pub fn lock(&self) -> Result, Error> { process_lock(self.pid, &self.lock) } - - pub fn registers(&self) -> Result { - let data: Registers = Registers::default(); - - let _lock = self.lock()?; - - ptrace::getregs(self.tid, &data as *const _ as _)?; - - Ok(data) - } } impl ProcessMemory for Process { diff --git a/remoteprocess/src/freebsd/registers.rs b/remoteprocess/src/freebsd/registers.rs deleted file mode 100644 index 12034c83..00000000 --- a/remoteprocess/src/freebsd/registers.rs +++ /dev/null @@ -1,82 +0,0 @@ -/// Extract to dwarf_unwinder eventually - -// sys/x86/include/reg.h -#[cfg(target_os="freebsd")] -#[derive(Debug, Default, Eq, PartialEq, Copy, Clone)] -pub struct Registers { - pub r15: u64, - pub r14: u64, - pub r13: u64, - pub r12: u64, - pub r11: u64, - pub r10: u64, - pub r9: u64, - pub r8: u64, - pub rdi: u64, - pub rsi: u64, - pub rbp: u64, - pub rbx: u64, - pub rdx: u64, - pub rcx: u64, - pub rax: u64, - pub trapno: u32, - pub fs: u16, - pub gs: u16, - pub err: u32, - pub es: u16, - pub ds: u16, - pub rip: u64, - pub cs: u64, - pub rflags: u64, - pub rsp: u64, - pub ss: u64, -} - - -#[cfg(target_os="freebsd")] -fn get_register(regs: &Registers, register: gimli::Register) -> u64 { - match register.0 { - 0 => regs.rax, - 1 => regs.rdx, - 2 => regs.rcx, - 3 => regs.rbx, - 4 => regs.rsi, - 5 => regs.rdi, - 6 => regs.rbp, - 7 => regs.rsp, - 8 => regs.r8, - 9 => regs.r9, - 10 => regs.r10, - 11 => regs.r11, - 12 => regs.r12, - 13 => regs.r13, - 14 => regs.r14, - 15 => regs.r15, - 16 => regs.rip, - _ => panic!("unknown reg") - } -} - -#[cfg(target_os="freebsd")] -fn set_register(regs: &mut Registers, register: gimli::Register, value: u64) { - match register.0 { - 0 => regs.rax = value, - 1 => regs.rdx = value, - 2 => regs.rcx = value, - 3 => regs.rbx = value, - 4 => regs.rsi = value, - 5 => regs.rdi = value, - 6 => regs.rbp = value , - 7 => regs.rsp = value, - 8 => regs.r8 = value, - 9 => regs.r9 = value, - 10 => regs.r10 = value, - 11 => regs.r11 = value, - 12 => regs.r12 = value, - 13 => regs.r13 = value, - 14 => regs.r14 = value, - 15 => regs.r15 = value, - 16 => regs.rip = value, - _ => panic!("unknown reg") - } -} diff --git a/remoteprocess/src/lib.rs b/remoteprocess/src/lib.rs index 96763222..2b5b6fed 100644 --- a/remoteprocess/src/lib.rs +++ b/remoteprocess/src/lib.rs @@ -54,7 +54,6 @@ extern crate proc_maps; extern crate goblin; extern crate benfred_read_process_memory as read_process_memory; extern crate memmap; -extern crate gimli; extern crate libc; #[macro_use] extern crate log; @@ -76,10 +75,6 @@ extern crate libproc; #[cfg(windows)] extern crate winapi; -#[cfg(all(target_os="macos", unwind))] -#[macro_use] -mod dylib; - #[cfg(target_os="macos")] mod osx; #[cfg(target_os="macos")] @@ -100,13 +95,9 @@ mod windows; #[cfg(target_os="windows")] pub use windows::*; -#[cfg(all(unix, unwind))] -mod dwarf_unwind; - #[derive(Debug)] pub enum Error { NoBinaryForAddress(u64), - GimliError(gimli::Error), GoblinError(::goblin::error::Error), IOError(std::io::Error), Other(String), @@ -114,8 +105,6 @@ pub enum Error { LibunwindError(linux::libunwind::Error), #[cfg(target_os="linux")] NixError(nix::Error), - #[cfg(target_os="macos")] - CompactUnwindError(osx::compact_unwind::CompactUnwindError), } impl std::fmt::Display for Error { @@ -124,7 +113,6 @@ impl std::fmt::Display for Error { Error::NoBinaryForAddress(addr) => { write!(f, "No binary found for address 0x{:016x}. Try reloading.", addr) }, - Error::GimliError(ref e) => e.fmt(f), Error::GoblinError(ref e) => e.fmt(f), Error::IOError(ref e) => e.fmt(f), Error::Other(ref e) => write!(f, "{}", e), @@ -132,8 +120,6 @@ impl std::fmt::Display for Error { Error::LibunwindError(ref e) => e.fmt(f), #[cfg(target_os="linux")] Error::NixError(ref e) => e.fmt(f), - #[cfg(target_os="macos")] - Error::CompactUnwindError(ref e) => e.fmt(f), } } } @@ -142,41 +128,29 @@ impl std::error::Error for Error { fn description(&self) -> &str { match *self { Error::NoBinaryForAddress(_) => "No binary found for address", - Error::GimliError(ref e) => e.description(), Error::GoblinError(ref e) => e.description(), Error::IOError(ref e) => e.description(), #[cfg(all(target_os="linux", unwind))] Error::LibunwindError(ref e) => e.description(), #[cfg(target_os="linux")] Error::NixError(ref e) => e.description(), - #[cfg(target_os="macos")] - Error::CompactUnwindError(ref e) => e.description(), Error::Other(ref e) => e, } } fn cause(&self) -> Option<&dyn std::error::Error> { match *self { - Error::GimliError(ref e) => Some(e), Error::GoblinError(ref e) => Some(e), Error::IOError(ref e) => Some(e), #[cfg(all(target_os="linux", unwind))] Error::LibunwindError(ref e) => Some(e), #[cfg(target_os="linux")] Error::NixError(ref e) => Some(e), - #[cfg(target_os="macos")] - Error::CompactUnwindError(ref e) => Some(e), _ => None, } } } -impl From for Error { - fn from(err: gimli::Error) -> Error { - Error::GimliError(err) - } -} - impl From for Error { fn from(err: goblin::error::Error) -> Error { Error::GoblinError(err) @@ -203,13 +177,6 @@ impl From for Error { } } -#[cfg(target_os="macos")] -impl From for Error { - fn from(err: osx::compact_unwind::CompactUnwindError) -> Error { - Error::CompactUnwindError(err) - } -} - #[derive(Debug, Clone)] pub struct StackFrame { pub line: Option, diff --git a/remoteprocess/src/linux/gimli_unwinder.rs b/remoteprocess/src/linux/gimli_unwinder.rs deleted file mode 100644 index e79cf99a..00000000 --- a/remoteprocess/src/linux/gimli_unwinder.rs +++ /dev/null @@ -1,288 +0,0 @@ -use std::fs::File; -use std::path::Path; -use std::rc::Rc; -use std::cell::RefCell; -use std::collections::BTreeMap; - -use goblin::Object; -use memmap::Mmap; -use proc_maps; - -use gimli::{EhFrame, BaseAddresses, Pointer, NativeEndian, EhFrameHdr}; -use goblin::elf::program_header::*; - -use gimli::EndianRcSlice; -type RcReader = EndianRcSlice; - -use super::super::{ProcessMemory, Error}; -use crate::dwarf_unwind::{UnwindInfo, Registers}; - -use crate::linux::symbolication::{SymbolData}; -use super::super::StackFrame; -use super::{Pid, Thread, Process}; - -pub struct Unwinder { - binaries: BTreeMap, - process: Process, - pid: Pid -} - -pub struct Cursor<'a> { - registers: Registers, - parent: &'a Unwinder, - initial_frame: bool, -} - -impl Unwinder { - pub fn new(pid: Pid) -> Result { - let process = Process::new(pid)?; - let mut ret = Unwinder{binaries: BTreeMap::new(), process, pid}; - ret.reload()?; - Ok(ret) - } - - pub fn reload(&mut self) -> Result<(), Error> { - info!("reloading process binaries"); - - // Get shared libraries from virtual memory mapped files - let maps = &proc_maps::get_process_maps(self.pid)?; - let shared_maps = maps.iter().filter(|m| m.is_exec() && !m.is_write() && m.is_read()); - - // Open them up and parse etc - for m in shared_maps { - // Get the filename if it exists from the map - let filename = match m.filename() { - Some(f) => f, - None => continue - }; - - // TODO: probably also want to check if the filename/size is the same - let address_key = (m.start() + m.size()) as u64; - if self.binaries.contains_key(&address_key) { - debug!("skipping {}", filename); - continue; - } - info!("loading debug info from {}", filename); - - // Memory-map the file, special casing [vdso] regions - let file; - let mmapped_file; - let vdso_data; - - let buffer = if Path::new(filename).exists() { - file = File::open(Path::new(filename))?; - mmapped_file = unsafe { Mmap::map(&file)? }; - &mmapped_file[..] - } else if filename != "[vsyscall]" { - // if the filename doesn't exist, its' almost certainly the vdso section - // read from the the target processses memory - vdso_data = self.process.copy(m.start(), m.size())?; - &vdso_data - } else { - // vsyscall region, can be ignored, but lets not keep on trying to do this - info!("skipping {} region", filename); - - // insert a stub for [vsyscall] so that we don't continually try to load it etc - self.binaries.insert(address_key, - BinaryInfo{unwind_info: None, offset: 0, address: m.start() as u64, size: m.size() as u64, - filename: filename.to_string(), symbols: RefCell::new(None)}); - continue; - }; - - debug!("loading file {} 0x{:X} 0x{:X}", filename, m.start(), buffer.len()); - match Object::parse(&buffer) { - Ok(Object::Elf(elf)) => { - trace!("filename {} elf {:#?}", filename, elf); - - let program_header = elf.program_headers - .iter() - .find(|ref header| header.p_type == PT_LOAD && header.p_flags & PF_X != 0); - - let obj_base = match program_header { - Some(hdr) => { m.start() as u64 - hdr.p_vaddr }, - None => { - warn!("Failed to find exectuable PT_LOAD header in {}", filename); - continue; - } - }; - - let unwind_info = match self.get_unwind_info(filename, &elf, buffer, obj_base) { - Ok(unwind) => Some(unwind), - Err(e) => { - warn!("Failed to get unwind info for '{}': {}", filename, e); - None - } - }; - - // the map key is the end address of this filename, which lets us do a relatively efficent range - // based lookup of the binary - self.binaries.insert(address_key, - BinaryInfo{unwind_info, offset: obj_base, address: m.start() as u64, size: m.size() as u64, - filename: filename.to_string(), symbols: RefCell::new(None)}); - }, - Ok(_) => { - warn!("unknown binary type for {}", filename); - continue; - } - Err(e) => { - warn!("Failed to parse {}: {:?}", filename, e); - continue; - } - } - } - Ok(()) - } - - fn get_unwind_info(&self, filename: &str, elf: &goblin::elf::Elf, buffer: &[u8], obj_base: u64) -> Result { - // get the eh_frame_hdr from the program headers - let eh_frame_hdr_addr; - let eh_frame_hdr = match elf.program_headers.iter().find(|x| x.p_type == PT_GNU_EH_FRAME) { - Some(hdr) => { - eh_frame_hdr_addr = obj_base + hdr.p_vaddr; - let data = Rc::from(&buffer[hdr.p_offset as usize..][..hdr.p_filesz as usize]); - let bases = BaseAddresses::default().set_eh_frame_hdr(eh_frame_hdr_addr); - EhFrameHdr::from(RcReader::new(data, NativeEndian)).parse(&bases, 8)? - }, - None => { - return Err(Error::Other("Failed to find eh_frame_hdr section in binary".to_owned())); - } - }; - - let eh_frame_addr = match eh_frame_hdr.eh_frame_ptr() { - Pointer::Direct(x) => x, - Pointer::Indirect(x) => { self.process.copy_struct(x as usize)? } - }; - - // get the appropiate eh_frame section from the section_headers and load it up with gimli - let eh_frame = match elf.section_headers.iter().filter(|x| x.sh_addr == eh_frame_addr - obj_base).next() { - Some(hdr) => { - debug!("Got eh_frame hdr {:?} from {}", hdr, filename); - let data = Rc::from(&buffer[hdr.sh_offset as usize..][..hdr.sh_size as usize]); - EhFrame::from(RcReader::new(data, NativeEndian)) - } - None => { - - return Err(Error::Other(format!("Failed to find eh_frame section (expected at {:016x})", - eh_frame_addr))); - } - }; - - let bases = BaseAddresses::default() - .set_eh_frame(eh_frame_addr) - .set_eh_frame_hdr(eh_frame_hdr_addr); - - Ok(UnwindInfo{eh_frame_hdr, eh_frame, bases}) - } - - pub fn cursor(&self, thread: &Thread) -> Result { - Ok(Cursor{registers: thread.registers()?, parent: self, initial_frame: true}) - } - - pub fn symbolicate(&self, addr: u64, line_info: bool, callback: &mut FnMut(&StackFrame)) -> Result<(), Error> { - let binary = match self.get_binary(addr) { - Some(binary) => binary, - None => { - return Err(Error::NoBinaryForAddress(addr)); - } - }; - if binary.filename != "[vdso]" { - let mut symbols = binary.symbols.borrow_mut(); - if symbols.is_none() { - info!("loading symbols from {}", binary.filename); - *symbols = Some(SymbolData::new(&binary.filename, binary.offset)); - } - match symbols.as_ref() { - Some(Ok(symbols)) => symbols.symbolicate(addr, line_info, callback), - _ => { - // we probably failed to load the symbols (maybe goblin v0.15 dependency causing error - // in gimli/object crate). Rather than fail add a stub - callback(&StackFrame{line: None, addr, function: None, filename: None, module: binary.filename.clone()}); - Ok(()) - } - } - } else { - // TODO: allow symbolication code to access vdso data - callback(&StackFrame{line: None, addr, function: None, filename: None, module: binary.filename.clone()}); - Ok(()) - } - } - - fn get_binary(&self, addr: u64) -> Option<&BinaryInfo> { - match self.binaries.range(addr..).next() { - Some((_, binary)) if binary.contains(addr) => Some(&binary), - Some(_) => None, - _ => None - } - } -} - -impl<'a> Cursor<'a> { - pub fn ip(&self) -> u64 { self.registers.rip } - pub fn sp(&self) -> u64 { self.registers.rsp } - pub fn bp(&self) -> u64 { self.registers.rbp } - pub fn bx(&self) -> u64 { self.registers.rbx } -} - - -impl<'a> Iterator for Cursor<'a> { - type Item = Result; - - fn next(&mut self) -> Option> { - if self.initial_frame { - self.initial_frame = false; - return Some(Ok(self.registers.rip)); - } - - if self.registers.rip <= 0x1000 { - return None; - } - - // Otherwise get the binary for the current instruction - let pc = self.registers.rip - 1; - let binary = match self.parent.get_binary(pc) { - Some(binary) => binary, - None => { - return Some(Err(Error::NoBinaryForAddress(pc))); - } - }; - - let mut old_reg = self.registers.clone(); - - - let unwind_info = match binary.unwind_info.as_ref() { - Some(unwind) => unwind, - None => { return Some(Err(Error::Other("Failed to load unwindinfo".to_owned()))); } - }; - - match unwind_info.unwind(&mut self.registers, &self.parent.process) { - Ok(true) => {}, - Ok(false) => return None, - Err(e) => return Some(Err(Error::from(e))), - }; - - // if the frame pointer and instruction pointer haven't updated, we're also done - // (discounting SP which will almost always update each unwind) - old_reg.rsp = self.registers.rsp; - if old_reg == self.registers { - return None; - } - - Some(Ok(self.registers.rip)) - } -} - -// Contains info for a binary on how to unwind/symbolicate a stack trace -struct BinaryInfo { - address: u64, - size: u64, - offset: u64, - filename: String, - unwind_info: Option, - symbols: RefCell>> -} - -impl BinaryInfo { - pub fn contains(&self, addr: u64) -> bool { - addr >= self.address && addr < (self.address + self.size) - } -} diff --git a/remoteprocess/src/linux/libunwind/mod.rs b/remoteprocess/src/linux/libunwind/mod.rs index 2d306e97..66f6bf79 100644 --- a/remoteprocess/src/linux/libunwind/mod.rs +++ b/remoteprocess/src/linux/libunwind/mod.rs @@ -1,12 +1,3 @@ -/// On linux, the most reliable way of unwinding a stack trace is going to be to use the libunwind-ptrace library -/// However, this isn't guaranteed to be installed on each system - and it doesn't seem like static linking it -/// is a viable solution. -/// -/// Also the performance of libunwind-ptrace seems to be quite a bit worse than that of the gimli based unwinder -/// we're using here. (around 10x slower when I was testing on my system) -/// -/// So instead of linking directly to libunwind and adding a hard dependency, let's load up at runtime instead. -/// (currently we're using libunwind mainly to validate the gimli unwider) use libc::{c_int, c_void, c_char, size_t, pid_t}; use std; @@ -31,37 +22,37 @@ pub enum Error { UNW_ENOINFO } -type Result = std::result::Result; +type Result = std::result::Result; -pub struct LibUnwind { +pub struct Unwinder { pub addr_space: unw_addr_space_t } -impl LibUnwind { - pub fn new() -> Result { +impl Unwinder { + pub fn new() -> Result { unsafe { let addr_space = create_addr_space(&_UPT_accessors as *const _ as *mut _, 0); // enabling caching provides a modest speedup - but is still much slower than the gimli unwinding set_caching_policy(addr_space, unw_caching_policy_t_UNW_CACHE_PER_THREAD); - Ok(LibUnwind{addr_space}) + Ok(Unwinder{addr_space}) } } - pub fn cursor(&self, pid: pid_t) -> Result { + pub fn cursor(&self, thread: &crate::Thread) -> Result { unsafe { - let upt = _UPT_create(pid as _); + let upt = _UPT_create(thread.id()? as _); let mut cursor = std::mem::uninitialized(); let ret = init_remote(&mut cursor, self.addr_space, upt); if ret != 0 { - return Err(Error::from(-ret)); + return Err(crate::Error::LibunwindError(Error::from(-ret))); } Ok(Cursor{cursor, upt, initial_frame: true}) } } } -impl Drop for LibUnwind { +impl Drop for Unwinder { fn drop(&mut self) { unsafe { destroy_addr_space(self.addr_space); @@ -82,7 +73,7 @@ impl Cursor { match get_reg(cursor, register, &mut value) { 0 => Ok(value), - err => Err(Error::from(-err)) + err => Err(crate::Error::LibunwindError(Error::from(-err))) } } @@ -114,7 +105,7 @@ impl Cursor { continue; }, err => { - return Err(Error::from(-err)); + return Err(crate::Error::LibunwindError(Error::from(-err))); } } } @@ -133,7 +124,7 @@ impl Iterator for Cursor { unsafe { match step(&mut self.cursor) { 0 => return None, - err if err < 0 => return Some(Err(Error::from(-err))), + err if err < 0 => return Some(Err(crate::Error::LibunwindError(Error::from(-err)))), _ => {} } }; diff --git a/remoteprocess/src/linux/mod.rs b/remoteprocess/src/linux/mod.rs index 9a775f8f..04813a33 100644 --- a/remoteprocess/src/linux/mod.rs +++ b/remoteprocess/src/linux/mod.rs @@ -1,12 +1,9 @@ #[cfg(unwind)] pub mod libunwind; #[cfg(unwind)] -mod gimli_unwinder; -#[cfg(unwind)] mod symbolication; + use libc::pid_t; -#[cfg(unwind)] -use libc::c_void; use nix::{self, sys::wait, sys::ptrace, {sched::{setns, CloneFlags}}}; use std::convert::TryInto; @@ -14,16 +11,13 @@ use std::io::Read; use std::os::unix::io::AsRawFd; use std::fs::File; -#[cfg(unwind)] -use crate::dwarf_unwind::Registers; use super::Error; -#[cfg(unwind)] -pub use self::gimli_unwinder::*; #[cfg(unwind)] pub use self::symbolication::*; + #[cfg(unwind)] -pub use self::libunwind::{LibUnwind}; +pub use self::libunwind::Unwinder; use read_process_memory::{CopyAddress, ProcessHandle}; @@ -111,7 +105,12 @@ impl Process { #[cfg(unwind)] pub fn unwinder(&self) -> Result { - Unwinder::new(self.pid) + Ok(Unwinder::new()?) + } + + #[cfg(unwind)] + pub fn symbolicator(&self) -> Result { + Ok(Symbolicator::new(self.pid)?) } } @@ -131,20 +130,6 @@ impl Thread { Ok(ThreadLock::new(self.tid)?) } - #[cfg(unwind)] - pub fn registers(&self) -> Result { - unsafe { - let mut data: Registers = std::mem::zeroed(); - // nix has marked this as deprecated (in favour of specific functions like attach) - // but hasn't yet exposed PTRACE_GETREGS as it's own function - #[allow(deprecated)] - ptrace::ptrace(ptrace::Request::PTRACE_GETREGS, self.tid, - std::ptr::null_mut(), - &mut data as *mut _ as * mut c_void)?; - Ok(data) - } - } - pub fn id(&self) -> Result { Ok(self.tid.as_raw()) } diff --git a/remoteprocess/src/linux/symbolication.rs b/remoteprocess/src/linux/symbolication.rs index c4733226..a9aaa2d2 100644 --- a/remoteprocess/src/linux/symbolication.rs +++ b/remoteprocess/src/linux/symbolication.rs @@ -1,11 +1,155 @@ use std::fs::File; +use std::path::Path; +use std::cell::RefCell; +use std::collections::BTreeMap; + use memmap; +use memmap::Mmap; use object::{self, Object}; use addr2line::Context; -use gimli; -use crate::{StackFrame, Error}; +use goblin; +use goblin::elf::program_header::*; +use crate::{StackFrame, Error, Process, Pid }; + +use crate::ProcessMemory; + + +pub struct Symbolicator { + binaries: BTreeMap, + process: Process, + pid: Pid +} + +impl Symbolicator { + pub fn new(pid: Pid) -> Result { + let process = Process::new(pid)?; + let mut ret = Symbolicator{binaries: BTreeMap::new(), process, pid}; + ret.reload()?; + Ok(ret) + } + + pub fn reload(&mut self) -> Result<(), Error> { + info!("reloading process binaries"); + + // Get shared libraries from virtual memory mapped files + let maps = &proc_maps::get_process_maps(self.pid)?; + let shared_maps = maps.iter().filter(|m| m.is_exec() && !m.is_write() && m.is_read()); + // Open them up and parse etc + for m in shared_maps { + // Get the filename if it exists from the map + let filename = match m.filename() { + Some(f) => f, + None => continue + }; + + // TODO: probably also want to check if the filename/size is the same + let address_key = (m.start() + m.size()) as u64; + if self.binaries.contains_key(&address_key) { + debug!("skipping {}", filename); + continue; + } + info!("loading debug info from {}", filename); + + // Memory-map the file, special casing [vdso] regions + let file; + let mmapped_file; + let vdso_data; + + let buffer = if Path::new(filename).exists() { + file = File::open(Path::new(filename))?; + mmapped_file = unsafe { Mmap::map(&file)? }; + &mmapped_file[..] + } else if filename != "[vsyscall]" { + // if the filename doesn't exist, its' almost certainly the vdso section + // read from the the target processses memory + vdso_data = self.process.copy(m.start(), m.size())?; + &vdso_data + } else { + // vsyscall region, can be ignored, but lets not keep on trying to do this + info!("skipping {} region", filename); + + // insert a stub for [vsyscall] so that we don't continually try to load it etc + self.binaries.insert(address_key, + BinaryInfo{offset: 0, address: m.start() as u64, size: m.size() as u64, + filename: filename.to_string(), symbols: RefCell::new(None)}); + continue; + }; + + debug!("loading file {} 0x{:X} 0x{:X}", filename, m.start(), buffer.len()); + match goblin::Object::parse(&buffer) { + Ok(goblin::Object::Elf(elf)) => { + trace!("filename {} elf {:#?}", filename, elf); + + let program_header = elf.program_headers + .iter() + .find(|ref header| header.p_type == PT_LOAD && header.p_flags & PF_X != 0); + + let obj_base = match program_header { + Some(hdr) => { m.start() as u64 - hdr.p_vaddr }, + None => { + warn!("Failed to find exectuable PT_LOAD header in {}", filename); + continue; + } + }; + + // the map key is the end address of this filename, which lets us do a relatively efficent range + // based lookup of the binary + self.binaries.insert(address_key, + BinaryInfo{offset: obj_base, address: m.start() as u64, size: m.size() as u64, + filename: filename.to_string(), symbols: RefCell::new(None)}); + }, + Ok(_) => { + warn!("unknown binary type for {}", filename); + continue; + } + Err(e) => { + warn!("Failed to parse {}: {:?}", filename, e); + continue; + } + } + } + Ok(()) + } + + pub fn symbolicate(&self, addr: u64, line_info: bool, callback: &mut FnMut(&StackFrame)) -> Result<(), Error> { + let binary = match self.get_binary(addr) { + Some(binary) => binary, + None => { + return Err(Error::NoBinaryForAddress(addr)); + } + }; + if binary.filename != "[vdso]" { + let mut symbols = binary.symbols.borrow_mut(); + if symbols.is_none() { + info!("loading symbols from {}", binary.filename); + *symbols = Some(SymbolData::new(&binary.filename, binary.offset)); + } + match symbols.as_ref() { + Some(Ok(symbols)) => symbols.symbolicate(addr, line_info, callback), + _ => { + // we probably failed to load the symbols (maybe goblin v0.15 dependency causing error + // in gimli/object crate). Rather than fail add a stub + callback(&StackFrame{line: None, addr, function: None, filename: None, module: binary.filename.clone()}); + Ok(()) + } + } + } else { + // TODO: allow symbolication code to access vdso data + callback(&StackFrame{line: None, addr, function: None, filename: None, module: binary.filename.clone()}); + Ok(()) + } + } + + fn get_binary(&self, addr: u64) -> Option<&BinaryInfo> { + match self.binaries.range(addr..).next() { + Some((_, binary)) if binary.contains(addr) => Some(&binary), + Some(_) => None, + _ => None + } + } +} pub struct SymbolData { // Contains symbol info for a single binary @@ -26,7 +170,8 @@ impl SymbolData { Ok(f) => f, Err(e) => { error!("failed to parse file for symbolication {}: {:?}", filename, e); - return Err(gimli::Error::OffsetOutOfBounds.into()); + // return Err(gimli::Error::OffsetOutOfBounds.into()); + return Err(Error::Other("Failed to parse file for symbolication".to_string())); } }; @@ -112,3 +257,19 @@ impl SymbolData { Ok(()) } } + + +// Contains info for a binary on how to unwind/symbolicate a stack trace +struct BinaryInfo { + address: u64, + size: u64, + offset: u64, + filename: String, + symbols: RefCell>> +} + +impl BinaryInfo { + pub fn contains(&self, addr: u64) -> bool { + addr >= self.address && addr < (self.address + self.size) + } +} \ No newline at end of file diff --git a/remoteprocess/src/osx/compact_unwind.rs b/remoteprocess/src/osx/compact_unwind.rs deleted file mode 100644 index e83e2c55..00000000 --- a/remoteprocess/src/osx/compact_unwind.rs +++ /dev/null @@ -1,322 +0,0 @@ -use std; -use mach::structs::x86_thread_state64_t; -use mach_o_sys::compact_unwind_encoding::{unwind_info_section_header, unwind_info_section_header_index_entry, - unwind_info_regular_second_level_page_header, - unwind_info_compressed_second_level_page_header, unwind_info_regular_second_level_entry, - UNWIND_SECOND_LEVEL_REGULAR, UNWIND_SECOND_LEVEL_COMPRESSED, UNWIND_SECTION_VERSION -}; - -use super::{Process, Error, ProcessMemory}; - -// these are defined mach-o/compact_unwind_encoding.h (and in mach_o_sys crate), but -// I'm finding it easier to define here (defined as an enum in that crate, and I prefer const u32) -const UNWIND_X86_64_MODE_MASK: u32 = 0xf000000; -const UNWIND_X86_64_MODE_RBP_FRAME: u32 = 0x1000000; -const UNWIND_X86_64_MODE_STACK_IMMD: u32 = 0x2000000; -const UNWIND_X86_64_MODE_STACK_IND: u32 = 0x3000000; -const UNWIND_X86_64_MODE_DWARF: u32 = 0x4000000; -const UNWIND_X86_64_RBP_FRAME_REGISTERS: u32 = 0x7fff; -const UNWIND_X86_64_RBP_FRAME_OFFSET: u32 = 0xff0000; -const UNWIND_X86_64_FRAMELESS_STACK_SIZE:u32 = 0x00FF0000; -const UNWIND_X86_64_FRAMELESS_STACK_ADJUST:u32 = 0xe000; -const UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT:u32 = 0x1c00; -const UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION:u32 = 0x3ff; -const UNWIND_X86_64_DWARF_SECTION_OFFSET:u32 = 0xffffff; - -const UNWIND_X86_64_REG_NONE: u32 = 0; -const UNWIND_X86_64_REG_RBX: u32 = 1; -const UNWIND_X86_64_REG_R12: u32 = 2; -const UNWIND_X86_64_REG_R13: u32 = 3; -const UNWIND_X86_64_REG_R14: u32 = 4; -const UNWIND_X86_64_REG_R15: u32 = 5; -const UNWIND_X86_64_REG_RBP: u32 = 6; - -pub struct CompactUnwindInfo { - pub encoding: u32, - pub func_start: u64, - pub func_end: u64 -} - -#[derive(Debug)] -pub enum CompactUnwindError { - UnknownMask(u32), - DwarfUnwind, - InvalidRegCount(u32), - InvalidRegIndex(u32), - InvalidHeaderVersion(u32), - PageOutOfBounds, - PCOutOfBounds, - UnknownPageKind(u32), - IOError(std::io::Error), -} - -pub fn compact_unwind(info: &CompactUnwindInfo, reg: &mut x86_thread_state64_t, process: &Process) -> Result<(), Error> { - match info.encoding & UNWIND_X86_64_MODE_MASK { - UNWIND_X86_64_MODE_RBP_FRAME => { compact_unwind_rbf(info, reg, process)?; }, - UNWIND_X86_64_MODE_STACK_IMMD => { compact_unwind_stack(false, info, reg, process)?; }, - UNWIND_X86_64_MODE_STACK_IND => { compact_unwind_stack(true, info, reg, process)?; }, - UNWIND_X86_64_MODE_DWARF => { return Err(CompactUnwindError::DwarfUnwind.into()) }, - mask => { return Err(CompactUnwindError::UnknownMask(mask).into()) } - }; - Ok(()) -} - -pub fn compact_unwind_rbf(info: &CompactUnwindInfo, reg: &mut x86_thread_state64_t, process: &Process) -> Result<(), Error> { - debug!("rbf unwind 0x{:016x}", reg.__rip); - let registers_offset = 8 * extract_from_mask(info.encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); - let mut frame_registers = extract_from_mask(info.encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); - // TODO: this next line can panic if registers_offset > reg.__rbp - let saved_registers: [u64; 5] = process.copy_struct(reg.__rbp as usize - registers_offset as usize)?; - for i in 0..5 { - match frame_registers & 0x7 { - UNWIND_X86_64_REG_NONE => { }, - UNWIND_X86_64_REG_RBX => { reg.__rbx = saved_registers[i]; }, - UNWIND_X86_64_REG_R12 => { reg.__r12 = saved_registers[i]; }, - UNWIND_X86_64_REG_R13 => { reg.__r13 = saved_registers[i]; }, - UNWIND_X86_64_REG_R14 => { reg.__r14 = saved_registers[i]; }, - UNWIND_X86_64_REG_R15 => { reg.__r15 = saved_registers[i]; }, - _ => { /* TODO return an error? */ } - } - frame_registers = frame_registers >> 3; - } - // TODO: if this fails show a better error message. (Usually rbp register is pointing to invalid memory) - let frame: [u64; 2] = process.copy_struct(reg.__rbp as usize)?; - reg.__rsp = reg.__rbp + 16; - reg.__rbp = frame[0]; - reg.__rip = frame[1]; - Ok(()) -} - -pub fn compact_unwind_stack(indirect_stack: bool, - info: &CompactUnwindInfo, - reg: &mut x86_thread_state64_t, - process: &Process) -> Result<(), Error> { - debug!("stacksize unwind 0x{:016x} (indirect={})", reg.__rip, indirect_stack); - - let reg_count = extract_from_mask(info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); - let stack_adjust = extract_from_mask(info.encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); - let mut reg_permutation = extract_from_mask(info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); - let mut stack_size = extract_from_mask(info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); - - if indirect_stack { - let offset: u32 = process.copy_struct(info.func_start as usize + stack_size as usize)?; - stack_size = offset + 8 * stack_adjust; - } else { - stack_size *= 8; - } - debug!("reg_count {} reg_perm {} stack_size {} stack_adjust {}", reg_count, reg_permutation, stack_size, stack_adjust); - - // decode register permutations. algorithm to encode is given in mach-o/compact_unwind_encoding.h - let reg_decoding = match reg_count { - // with 6 registers, the last perm should always be 0 - // (meaning that both 5/6 reg_count have the same decode instructions since we 0 initialize) - 5 | 6 => &[120, 24, 6, 2, 1][..], - 4 => &[60, 12, 3, 1][..], - 3 => &[20, 4, 1][..], - 2 => &[5, 1][..], - 1 => &[1][..], - 0 => &[][..], - _ => { return Err(CompactUnwindError::InvalidRegCount(reg_count).into()); } - }; - - let mut reg_perm = [0_u32; 6]; - for (i, v) in reg_decoding.iter().enumerate() { - reg_perm[i] = reg_permutation / v; - reg_permutation -= reg_perm[i] * v; - } - - let mut used_regs = [false; 6]; - let mut reg_index = [0_u32; 6]; - for i in 0..(reg_count as usize) { - let mut register_num = 0_u32 ; - for j in 0..6 { - if !used_regs[j] { - if register_num == reg_perm[i] { - used_regs[j] = true; - reg_index[i] = (j + 1) as u32; - break; - } else { - register_num += 1; - } - } - } - } - debug!("reg index {:?}", reg_index); - let save_offset = reg.__rsp as usize + stack_size as usize - 8; - - debug!("save_offset {} stack_size {}", save_offset, stack_size); - let saved_registers: [u64; 6] = process.copy_struct(save_offset - 8 * reg_count as usize)?; - for i in 0..(reg_count as usize) { - match reg_index[i] { - UNWIND_X86_64_REG_NONE => { }, - UNWIND_X86_64_REG_RBX => { reg.__rbx = saved_registers[i]; }, - UNWIND_X86_64_REG_R12 => { reg.__r12 = saved_registers[i]; }, - UNWIND_X86_64_REG_R13 => { reg.__r13 = saved_registers[i]; }, - UNWIND_X86_64_REG_R14 => { reg.__r14 = saved_registers[i]; }, - UNWIND_X86_64_REG_R15 => { reg.__r15 = saved_registers[i]; }, - UNWIND_X86_64_REG_RBP => { reg.__rbp = saved_registers[i]; }, - _ => { return Err(CompactUnwindError::InvalidRegIndex(reg_index[i]).into()) } - } - } - - // TODO: my initial version had a bug (read rsp from memory instead of incrementing by stack size) - // get a test that tests this (core dump of firefox?) - reg.__rip = process.copy_struct(save_offset)?; - reg.__rsp += stack_size as u64; - Ok(()) -} - -pub fn get_compact_unwind_info(unwind_info: &[u8], mach_address: u64, pc: u64) -> Result { - let unwind_header = unwind_info as * const _ as * const unwind_info_section_header; - - // Get a slice of unwind_info_section_header_index_entry - let index = unsafe { - if (*unwind_header).version != UNWIND_SECTION_VERSION as u32 { - return Err(CompactUnwindError::InvalidHeaderVersion((*unwind_header).version).into()); - } - - let index_buffer = &unwind_info[(*unwind_header).indexSectionOffset as usize..]; - std::slice::from_raw_parts(index_buffer.as_ptr() as *const unwind_info_section_header_index_entry, - (*unwind_header).indexCount as usize) - }; - - // get the unwind index entry for the address - let target_offset = (pc - mach_address as u64) as u32; - let i = match index.binary_search_by(|index| index.functionOffset.cmp(&target_offset)) { - Ok(v) => v, - Err(v) => if v > 0 { v - 1 } else { v } - }; - - // The last element in the index isn't valid (shows end range). If we've hit that then we can't find the - // unwind info for this address - if i + 1 >= index.len() { - return Err(CompactUnwindError::PageOutOfBounds.into()); - } - - let entry = &index[i]; - let next_entry = &index[i+1]; - - // figure out the type of the second level index - let second_level_buffer = &unwind_info[entry.secondLevelPagesSectionOffset as usize..]; - let page_kind = unsafe { *(second_level_buffer.as_ptr() as * const u32) }; - - match page_kind as u8 { - UNWIND_SECOND_LEVEL_REGULAR => { - let second_level_header = second_level_buffer as * const _ as * const unwind_info_regular_second_level_page_header; - let second_level_index = unsafe { - let entry_buffer = &second_level_buffer[(*second_level_header).entryPageOffset as usize..]; - std::slice::from_raw_parts(entry_buffer.as_ptr() as *const unwind_info_regular_second_level_entry, - (*second_level_header).entryCount as usize) - }; - - let element = match second_level_index.binary_search_by(|e| e.functionOffset.cmp(&target_offset)) { - Ok(v) => v, - Err(v) => if v > 0 { v - 1 } else { v } - }; - - let second_level_entry = second_level_index[element]; - let func_start = second_level_entry.functionOffset as u64 + mach_address as u64; - let func_end = if element + 1 < second_level_index.len() { - (second_level_index[element + 1].functionOffset) as u64 + mach_address as u64 - } else { - next_entry.functionOffset as u64 + mach_address as u64 - }; - - if pc < func_start || pc >= func_end { - return Err(CompactUnwindError::PCOutOfBounds.into()); - } - - return Ok(CompactUnwindInfo{encoding: second_level_entry.encoding, func_start, func_end: func_end}); - }, - UNWIND_SECOND_LEVEL_COMPRESSED => { - // Get the page index - let second_level_header = second_level_buffer as * const _ as * const unwind_info_compressed_second_level_page_header; - let second_level_index = unsafe { - let entry_buffer = &second_level_buffer[(*second_level_header).entryPageOffset as usize..]; - std::slice::from_raw_parts(entry_buffer.as_ptr() as *const u32, - (*second_level_header).entryCount as usize) - }; - - if entry.functionOffset > target_offset { - return Err(CompactUnwindError::PageOutOfBounds.into()); - } - - let second_level_offset = target_offset - entry.functionOffset; - let element = match second_level_index.binary_search_by(|e| (e & 0x00FFFFFF).cmp(&second_level_offset)) { - Ok(v) => v, - Err(v) => if v > 0 { v - 1 } else { v } - }; - - let second_level_entry = second_level_index[element]; - - // Get the function start/end from the index - let function_offset = mach_address + entry.functionOffset as u64; - let func_start = (second_level_entry & 0x00FFFFFF) as u64 + function_offset; - let func_end = if element + 1 < second_level_index.len() { - (second_level_index[element + 1] & 0x00FFFFFF) as u64 + function_offset - } else { - next_entry.functionOffset as u64 + mach_address as u64 - }; - - if pc < func_start || pc >= func_end { - return Err(CompactUnwindError::PCOutOfBounds.into()); - } - - let encoding_index = (second_level_entry >> 24) & 0xFF; - let encoding = unsafe { - let common_encoding_count = (*unwind_header).commonEncodingsArrayCount; - if encoding_index < common_encoding_count { - // encoding is stored in the unwind header array - let encodings_buffer = &unwind_info[(*unwind_header).commonEncodingsArraySectionOffset as usize..]; - let encodings = std::slice::from_raw_parts(encodings_buffer.as_ptr() as *const u32, - common_encoding_count as usize); - encodings[encoding_index as usize] - } else { - let encodings_buffer = &second_level_buffer[(*second_level_header).encodingsPageOffset as usize..]; - let encodings = std::slice::from_raw_parts(encodings_buffer.as_ptr() as *const u32, - (*second_level_header).encodingsCount as usize); - encodings[(encoding_index - common_encoding_count) as usize] - } - }; - return Ok(CompactUnwindInfo{encoding, func_start, func_end}); - }, - _ => { - return Err(CompactUnwindError::UnknownPageKind(page_kind).into()); - } - } -} - -/// Returns the offset into the eh_frame section if the compact encoding is for dwarf debugging entries -pub fn get_dwarf_offset(encoding: u32) -> Option { - if encoding & UNWIND_X86_64_MODE_MASK == UNWIND_X86_64_MODE_DWARF { - Some(encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET) - } else { - None - } -} - -// TODO: move into a utils module ? -fn extract_from_mask(value: u32, mask: u32) -> u32 { - (value >> mask.trailing_zeros()) & (((1 << mask.count_ones()))-1) -} - -impl std::fmt::Display for CompactUnwindError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match *self { - CompactUnwindError::UnknownMask(mask) => { write!(f, "Unknown compact encoding mask 0x{:x}", mask) }, - CompactUnwindError::DwarfUnwind => { write!(f, "encoding UNWIND_X86_64_MODE_DWARF can't be handle by compact_unwind") }, - CompactUnwindError::InvalidRegCount(count) => { write!(f, "invalid reg_count in frameless unwind {}", count) }, - CompactUnwindError::InvalidRegIndex(index) => { write!(f, "invalid reg_index in frameless unwind {}", index) }, - CompactUnwindError::InvalidHeaderVersion(ver) => { write!(f, "invalid unwind header version: {}", ver) }, - CompactUnwindError::PageOutOfBounds => { write!(f, "Compact page out of bounds") }, - CompactUnwindError::PCOutOfBounds => { write!(f, "PC isn't in bounds in compact unwind index") }, - CompactUnwindError::UnknownPageKind(page_kind) => { write!(f, "malformed unwind_info section: {}", page_kind) }, - CompactUnwindError::IOError(ref e) => e.fmt(f), - } - } -} - -impl std::error::Error for CompactUnwindError { - fn description(&self) -> &str { "CompactUnwindError" } - fn cause(&self) -> Option<&dyn std::error::Error> { None } -} \ No newline at end of file diff --git a/remoteprocess/src/osx/mod.rs b/remoteprocess/src/osx/mod.rs index 828b162e..f01a3039 100644 --- a/remoteprocess/src/osx/mod.rs +++ b/remoteprocess/src/osx/mod.rs @@ -1,17 +1,11 @@ -#[doc(hidden)] -pub mod compact_unwind; mod utils; -#[cfg(unwind)] -mod symbolication; mod mach_thread_bindings; -#[cfg(unwwind)] -mod unwinder; use std; use std::convert::TryInto; use mach; -use super::{ProcessMemory, Error}; +use super::Error; use mach::kern_return::{KERN_SUCCESS}; use mach::port::{mach_port_name_t, MACH_PORT_NULL}; use mach::traps::{task_for_pid, mach_task_self}; @@ -26,8 +20,6 @@ use mach::thread_status::x86_THREAD_STATE64; use mach::thread_act::{thread_get_state}; pub use self::utils::{TaskLock, ThreadLock}; -#[cfg(unwwind)] -pub use self::unwinder::Unwinder; use libproc::libproc::proc_pid::{pidpath, pidinfo, PIDInfo, PidInfoFlavor}; @@ -123,11 +115,6 @@ impl Process { } Ok(ret) } - - #[cfg(unwind)] - pub fn unwinder(&self) -> Result { - Ok(Unwinder::new(self.pid)?) - } } impl super::ProcessMemory for Process { diff --git a/remoteprocess/src/osx/symbolication.rs b/remoteprocess/src/osx/symbolication.rs deleted file mode 100644 index 1ed7d77a..00000000 --- a/remoteprocess/src/osx/symbolication.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Code to connect to CoreSymbolication library taken from backtrace-rs: -// https://github.com/alexcrichton/backtrace-rs/blob/master/src/symbolize/coresymbolication.rs -// backtrace-rs isn't targetting remote processes, so we can't just use as a dependency, -// however it's relatively trivial to extract the needed bits and make it work with -// a remote process -// backtrace-rs is licensed under the MIT license: - -/* -Copyright (c) 2014 Alex Crichton - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -use std::sync::atomic::AtomicUsize; - -use libc::{c_char, c_int, c_void}; -use dylib::{self, Dylib, Symbol as DylibSymbol}; - -#[repr(C)] -#[derive(Copy, Clone, PartialEq)] -pub struct CSTypeRef { - cpp_data: *const c_void, - cpp_obj: *const c_void -} - -const CS_NOW: u64 = 0x80000000; -const CSREF_NULL: CSTypeRef = CSTypeRef { - cpp_data: 0 as *const c_void, - cpp_obj: 0 as *const c_void, -}; - -static CORESYMBOLICATION: Dylib = Dylib { init: AtomicUsize::new(0) }; - -dlsym! { - extern { - fn CSSymbolicatorCreateWithPid(pid: c_int) -> CSTypeRef; - fn CSRelease(rf: CSTypeRef) -> c_void; - fn CSSymbolicatorGetSymbolWithAddressAtTime( - cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef; - fn CSSymbolicatorGetSourceInfoWithAddressAtTime( - cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef; - fn CSSourceInfoGetLineNumber(info: CSTypeRef) -> c_int; - fn CSSourceInfoGetPath(info: CSTypeRef) -> *const c_char; - fn CSSourceInfoGetSymbol(info: CSTypeRef) -> CSTypeRef; - fn CSSymbolGetMangledName(sym: CSTypeRef) -> *const c_char; - // fn CSSymbolGetSymbolOwner(sym: CSTypeRef) -> CSTypeRef; - // fn CSSymbolOwnerGetBaseAddress(symowner: CSTypeRef) -> *mut c_void; - } -} - -unsafe fn get(sym: &DylibSymbol) -> &T { - CORESYMBOLICATION.get(sym).unwrap() -} - -pub struct CoreSymbolication { - cs: CSTypeRef -} - -use std; - -pub struct Symbol { - filename: *const c_char, - name: *const c_char, - pub lineno: u32, -} - -impl Symbol { - pub fn name(&self) -> Option { - if !self.name.is_null() { - Some(unsafe { std::ffi::CStr::from_ptr(self.name) }.to_owned()) - } else { - None - } - } - - pub fn filename(&self) -> Option { - if !self.filename.is_null() { - Some(unsafe { std::ffi::CStr::from_ptr(self.filename) }.to_owned()) - } else { - None - } - } -} - - -impl CoreSymbolication { - pub unsafe fn new(pid: c_int) -> Option { - let path = "/System/Library/PrivateFrameworks/CoreSymbolication.framework\ - /Versions/A/CoreSymbolication\0"; - - if !CORESYMBOLICATION.init(path) { - // TODO: return an error here instead - return None; - } - - let cs = get(&CSSymbolicatorCreateWithPid)(pid); - if cs == CSREF_NULL { - return None; - } - - Some(CoreSymbolication{cs}) - } - - pub unsafe fn resolve(&self, addr: u64) -> Option { - let addr = addr as *const c_void; - let info = get(&CSSymbolicatorGetSourceInfoWithAddressAtTime)(self.cs, addr, CS_NOW); - - let sym = if info == CSREF_NULL { - get(&CSSymbolicatorGetSymbolWithAddressAtTime)(self.cs, addr, CS_NOW) - } else { - get(&CSSourceInfoGetSymbol)(info) - }; - - if sym == CSREF_NULL { - return None; - } - - let lineno = if info != CSREF_NULL { - get(&CSSourceInfoGetLineNumber)(info) as u32 - } else { - 0 - }; - - let filename = if info != CSREF_NULL { - get(&CSSourceInfoGetPath)(info) - } else { - std::ptr::null() - }; - let name = get(&CSSymbolGetMangledName)(sym); - Some(Symbol{name, lineno, filename}) - } -} - -impl Drop for CoreSymbolication { - fn drop(&mut self) { - unsafe { - get(&CSRelease)(self.cs); - } - } -} diff --git a/remoteprocess/src/osx/unwinder.rs b/remoteprocess/src/osx/unwinder.rs deleted file mode 100644 index cccf3111..00000000 --- a/remoteprocess/src/osx/unwinder.rs +++ /dev/null @@ -1,313 +0,0 @@ -use std; -use memmap; -use goblin; -use proc_maps; - -use std::collections::BTreeMap; -use std::fs::File; -use std::path::Path; -use std::cell::RefCell; - -use super::{Error, Thread, Pid, Process, ProcessMemory}; -use goblin::error::Error as GoblinError; -use mach::structs::x86_thread_state64_t; - -use super::super::StackFrame; - -use super::compact_unwind::{get_compact_unwind_info, get_dwarf_offset, compact_unwind}; -use super::symbolication; -use dwarf_unwind::UnwindInfo; - -pub struct Unwinder { - pub binaries: BTreeMap, - pub pid: Pid, - pub process: Process, - pub cs: symbolication::CoreSymbolication -} - -impl Unwinder { - pub fn new(pid: Pid) -> Result { - let process = Process::new(pid)?; - let binaries = BTreeMap::new(); - // TODO: no unwrap - let cs = unsafe { symbolication::CoreSymbolication::new(pid) }.unwrap(); - let mut unwinder = Unwinder{binaries, pid, process, cs}; - unwinder.reload()?; - Ok(unwinder) - } - - pub fn reload(&mut self) -> Result<(), GoblinError> { - info!("reloading binaries"); - // Get __TEXT dyld info for the process - let dyld = proc_maps::mac_maps::get_dyld_info(self.pid)? - .into_iter() - .filter(|dyld| dyld.segment.segname.starts_with(&[95_i8, 95, 84, 69, 88, 84])); - - let mut loaded = 0; - for library in dyld { - // if we've already loaded this thing up, we're good - let address_key = library.address as u64 + library.segment.vmsize; - if self.binaries.contains_key(&address_key) { - debug!("skipping {}", library.filename); - continue; - } - - match SharedLibrary::new(&library) { - Ok(library) => { - info!("loaded shared library {:?}", library); - loaded += 1; - self.binaries.insert(address_key, library); - } - Err(e) => { - error!("Failed to load shared library {}: {}", library.filename, e) - } - }; - } - // reload core symbolication framework too if necessary (otherwise - // we will fail to symbolicate modules that have been loaded since this) - if loaded > 0 { - let cs = unsafe { symbolication::CoreSymbolication::new(self.pid) }; - if let Some(cs) = cs { - self.cs = cs; - } - } - - Ok(()) - } - - pub fn get_binary(&self, pc: u64) -> Option<&SharedLibrary> { - match self.binaries.range(pc..).next() { - Some((_, binary)) if binary.contains(pc as usize) => Some(&binary), - Some(_) => None, - _ => None - } - } - - pub fn cursor(&self, thread: &Thread) -> Result { - Ok(Cursor{registers: thread.registers()?, parent: self, initial_frame: true}) - } - - pub fn symbolicate(&self, addr: u64, _line_info: bool, callback: &mut dyn FnMut(&StackFrame)) -> Result<(), Error> { - // Get the symbols for the current address - let symbol = unsafe { self.cs.resolve(addr) }; - - let binary = self.get_binary(addr); - let module = binary.map_or_else(|| "?".to_owned(), |b| b.filename.clone()); - let mut function = None; - let mut filename = None; - let mut line = None; - - if let Some(symbol) = symbol { - if let Some(name) = symbol.name() { - if let Ok(name) = name.to_str() { - function = Some(name.to_owned()); - } - } - if let Some(name) = symbol.filename() { - if let Ok(name) = name.to_str() { - filename = Some(name.to_owned()); - } - } - line = Some(symbol.lineno as u64); - } - callback(&StackFrame{function, filename, line, module, addr}); - Ok(()) - } -} - -#[derive(Debug)] -pub struct SharedLibrary { - pub filename: String, - pub address: usize, - pub size: usize, - pub mh_offset: usize, - pub buffer: memmap::Mmap, - pub unwind_info: Option, - pub eh_frame: Option, - pub dwarf_info: RefCell> -} - -impl SharedLibrary { - fn new(library: &proc_maps::mac_maps::DyldInfo) -> Result { - debug!("loading file {} 0x{:X}", library.filename, library.address); - let file = File::open(Path::new(&library.filename))?; - let buffer = unsafe { memmap::Mmap::map(&file)? }; - - // get the __eh_frame and __unwind_info sections from the mach binary - let mut eh_frame: Option = None; - let mut unwind_info: Option = None; - let mut mh_offset = 0; - match goblin::Object::parse(&buffer)? { - goblin::Object::Mach(mach) => { - let macho = match mach { - goblin::mach::Mach::Binary(macho) => macho, - goblin::mach::Mach::Fat(fat) => { - let arch = fat.iter_arches().find(|arch| - match arch { - Ok(arch) => arch.is_64(), - Err(_) => false - } - ).expect("Failed to find 64 bit arch in FAT archive")?; - debug!("got 64bit archive from fat archive @ {} ({} bytes)", arch.offset, arch.size); - mh_offset = arch.offset as usize; - let bytes = &buffer[arch.offset as usize..][..arch.size as usize]; - goblin::mach::MachO::parse(bytes, 0)? - } - }; - - // Get the text segment from the binary - let text = macho.segments - .iter() - .find(|s| { s.name().unwrap_or("") == "__TEXT" }) - .ok_or_else(|| GoblinError::Malformed(format!("Failed to find __TEXT section in {}", library.filename)))?; - - // Get the unwind_info and eh_frame sections - let sections = text.sections()?; - for (section, _) in sections.iter() { - match section.name() { - Ok("__eh_frame") => { eh_frame = Some(OffsetRange::from(section, mh_offset)) }, - Ok("__unwind_info") => { unwind_info = Some(OffsetRange::from(section, mh_offset)) }, - _ => {} - } - } - } - _ => { - return Err(GoblinError::Malformed(format!("Shared library {} is not a mach binary", library.filename)))?; - } - } - - Ok(SharedLibrary{filename: library.filename.clone(), - address: library.address, - size: library.segment.vmsize as usize, - buffer, - mh_offset, - unwind_info, - eh_frame, - dwarf_info: RefCell::new(None)}) - } - - pub fn contains(&self, addr: usize) -> bool { - addr >= self.address && addr < (self.address + self.size) - } -} - - -pub struct Cursor<'a> { - registers: x86_thread_state64_t, - parent: &'a Unwinder, - initial_frame: bool -} - -impl<'a> Cursor<'a> { - fn unwind(&mut self) -> Result, Error> { - let process = &self.parent.process; - if self.initial_frame { - self.initial_frame = false; - return Ok(Some(self.registers.__rip)); - } - - let check = |rip| { - match rip { - 0..=0x1000 => None, - _ => Some(rip) - } - }; - - let pc = self.registers.__rip - 1; - let binary = match self.parent.get_binary(pc) { - Some(binary) => binary, - None => { - // this seems to happen legitimately sometimes (in firefox, as confirmed by lldb also not knowing the - // binary for the same address). BUT could also mean that we need to reload the binaries - // return an error and attempt reloading just in case - return Err(Error::NoBinaryForAddress(pc)); - } - }; - - // Try doing a compact unwind first - if let Some(unwind_info) = &binary.unwind_info { - let unwind_buffer = &binary.buffer[unwind_info.offset as usize..][..unwind_info.size as usize]; - let info = get_compact_unwind_info(unwind_buffer, binary.address as u64, pc)?; - if info.encoding == 0 { - debug!("frameless unwind fallback 0x{:016x}. registers {:?}", pc, self.registers); - - let old_rip = self.registers.__rip; - - // If no encoding was given, assuming a frameless unwind with no stack size. - // I can't find any documentation on this, but this seems like it works - self.registers.__rip = process.copy_struct(self.registers.__rsp as usize)?; - self.registers.__rsp += 8; - - // except it doesn't always work =( hack (TODO: figure out what to do in this case!) - if old_rip == self.registers.__rip { - return Ok(None); - } - - return Ok(check(self.registers.__rip)); - - - } else if let Some(offset) = get_dwarf_offset(info.encoding) { - // we could do use the offset here to speed up the FDE lookup (and avoid building fde lookup table), - // but in the meantime lets fallback to the dwarf unwinding code below - // looking at the gimli code, and I don't see any easy way of leveraging this offset without exposing - // a private function =(. TODO: come back to this (very low priority) - debug!("compact dwarf unwind (offset = {})", offset); - - } else if info.encoding != 0 { - compact_unwind(&info, &mut self.registers, &process)?; - return Ok(check(self.registers.__rip)); - } - } - - if let Some(eh_frame_range) = &binary.eh_frame { - debug!("dwarf unwind 0x{:016x}", self.registers.__rip); - - let mut dwarf_info = binary.dwarf_info.borrow_mut(); - if dwarf_info.is_none() { - let eh_frame = &binary.buffer[eh_frame_range.offset as usize..][..eh_frame_range.size as usize]; - let eh_frame_address = binary.address as u64 + eh_frame_range.offset as u64 - binary.mh_offset as u64; - *dwarf_info = Some(UnwindInfo::new(eh_frame, eh_frame_address)?); - } - - let unwinder = dwarf_info.as_ref().unwrap(); - if !unwinder.unwind(&mut self.registers, &process)? { - return Ok(None); - } - return Ok(check(self.registers.__rip)); - } - - // TODO: return an error here? - info!("failed to do a compact unwind, and there is no dwarf debugging info present"); - Ok(None) - } - - pub fn ip(&self) -> u64 { self.registers.__rip } - pub fn sp(&self) -> u64 { self.registers.__rsp } - pub fn bp(&self) -> u64 { self.registers.__rbp } - pub fn bx(&self) -> u64 { self.registers.__rbx } -} - -impl<'a> Iterator for Cursor<'a> { - type Item = Result; - - fn next(&mut self) -> Option> { - match self.unwind() { - Ok(Some(addr)) => Some(Ok(addr)), - Err(e) => Some(Err(e)), - Ok(None) => None, - } - } -} - -// TODO: use std::ops::Range rather than define our own? -#[derive(Debug)] -pub struct OffsetRange { - pub offset: usize, - pub size: usize -} - -impl OffsetRange { - pub fn from(section: &goblin::mach::segment::Section, offset: usize) -> OffsetRange { - OffsetRange{offset: section.offset as usize + offset, size: section.size as usize} - } -} diff --git a/remoteprocess/src/windows/mod.rs b/remoteprocess/src/windows/mod.rs index 421dee2b..b59fe700 100644 --- a/remoteprocess/src/windows/mod.rs +++ b/remoteprocess/src/windows/mod.rs @@ -18,10 +18,12 @@ pub type Tid = Pid; use super::Error; mod unwinder; +mod symbolication; mod syscalls_x64; use self::syscalls_x64::{Syscall, lookup_syscall}; pub use self::unwinder::Unwinder; +pub use self::symbolication::Symbolicator; pub struct Process { pub pid: Pid, @@ -138,6 +140,10 @@ impl Process { pub fn unwinder(&self) -> Result { unwinder::Unwinder::new(self.handle.0) } + + pub fn symbolicator(&self) -> Result { + Symbolicator::new(self.handle.0) + } } impl Drop for Process { diff --git a/remoteprocess/src/windows/symbolication.rs b/remoteprocess/src/windows/symbolication.rs new file mode 100755 index 00000000..75c37221 --- /dev/null +++ b/remoteprocess/src/windows/symbolication.rs @@ -0,0 +1,173 @@ +use winapi::um::winnt::{HANDLE, WCHAR}; +use winapi::um::errhandlingapi::GetLastError; +use winapi::shared::minwindef::{TRUE, BOOL, DWORD, MAX_PATH}; +use winapi::shared::guiddef::GUID; +use winapi::shared::basetsd::DWORD64; +use winapi::um::dbghelp::{SymInitializeW, SymCleanup, + SymFromAddrW, SymGetLineFromAddrW64, MAX_SYM_NAME, SYMBOL_INFOW, IMAGEHLP_LINEW64}; +use std::os::windows::ffi::{OsStringExt}; + +use super::super::Error; +use super::super::StackFrame; +use libc::wcslen; + +pub struct Symbolicator { + pub handle: HANDLE +} + +impl Symbolicator { + pub fn new(handle: HANDLE) -> Result { + unsafe { + if SymInitializeW(handle, std::ptr::null_mut(), TRUE) == 0 { + return Err(Error::from(std::io::Error::last_os_error())); + }; + Ok(Symbolicator{handle}) + } + } + + pub fn reload(&mut self) -> Result<(), Error> { + info!("reloading symbol module list"); + unsafe { SymRefreshModuleList(self.handle); } + Ok(()) + } + + pub fn symbolicate(&self, addr: u64, line_info: bool, callback: &mut dyn FnMut(&StackFrame)) -> Result<(), Error> { + let function = unsafe { self.symbol_function(addr) }; + + let module = match unsafe { self.symbol_module (addr) } { + Ok(module) => module, + Err(Error::NoBinaryForAddress(_)) => { + unsafe { + SymRefreshModuleList(self.handle); + self.symbol_module(addr).unwrap_or_else(|_| "?".to_owned()) + } + }, + Err(_) => "?".to_owned() + }; + + let mut line = None; + let mut filename = None; + + if line_info { + if let Some((f, l)) = unsafe { self.symbol_filename(addr) } { + line = Some(l); + filename = Some(f); + } + } + callback(&StackFrame{function, filename, line, module, addr}); + Ok(()) + } + + // returns the corresponding function name for an address + pub unsafe fn symbol_function(&self, addr: u64) -> Option { + let mut buffer = std::mem::zeroed::(); + let symbol_info = &mut *(buffer.buffer.as_mut_ptr() as *mut SYMBOL_INFOW); + symbol_info.MaxNameLen = MAX_SYM_NAME as u32; + // there must be a way to get this + symbol_info.SizeOfStruct = 88; + + let mut displacement = 0; + let ret = SymFromAddrW(self.handle, addr, &mut displacement, symbol_info); + if ret != TRUE { + return None; + } + + let length = std::cmp::min(symbol_info.NameLen as usize, symbol_info.MaxNameLen as usize - 1); + let symbol = std::slice::from_raw_parts(symbol_info.Name.as_ptr() as *const u16, length); + let symbol = std::ffi::OsString::from_wide(symbol); + Some(symbol.to_string_lossy().to_owned().to_string()) + } + + // get the corresponding filename/linke + pub unsafe fn symbol_filename(&self, addr: u64) -> Option<(String, u64)> { + let mut displacement = 0; + let mut info = std::mem::zeroed::(); + info.SizeOfStruct = std::mem::size_of_val(&info) as u32; + if SymGetLineFromAddrW64(self.handle, addr, &mut displacement, &mut info) != TRUE { + return None; + } + let filename = std::slice::from_raw_parts(info.FileName, wcslen(info.FileName)); + let filename = std::ffi::OsString::from_wide(filename); + Some((filename.to_string_lossy().to_owned().to_string(), info.LineNumber.into())) + } + + // get the corresponding module name + pub unsafe fn symbol_module(&self, addr: u64) -> Result { + let mut info = std::mem::zeroed::(); + info.SizeOfStruct = std::mem::size_of_val(&info) as u32; + if SymGetModuleInfoW64(self.handle, addr, &mut info) != TRUE { + if GetLastError() == 126 { + return Err(Error::NoBinaryForAddress(addr)); + } + return Err(Error::IOError(std::io::Error::last_os_error())); + } + let filename = info.LoadedImageName.as_ptr(); + let filename = std::slice::from_raw_parts(filename, wcslen(filename)); + let filename = std::ffi::OsString::from_wide(filename); + + Ok(filename.to_string_lossy().to_owned().to_string()) + } +} + +impl Drop for Symbolicator { + fn drop(&mut self) { + unsafe { SymCleanup(self.handle); } + } +} + +#[repr(C, align(8))] +struct SymbolBuffer { + buffer: [u8; std::mem::size_of::() + MAX_SYM_NAME * 2] +} + +// missing from winapi-rs =( +#[allow(dead_code)] +#[repr(C)] +pub enum SYM_TYPE { + SymNone, + SymCoff, + SymCv, + SymPdb, + SymExport, + SymDeferred, + SymSym, + SymDia, + SymVirtual, + NumSymTypes, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct IMAGEHLP_MODULEW64 { + pub SizeOfStruct: DWORD, + pub BaseOfImage: DWORD64, + pub ImageSize: DWORD, + pub TimeDateStamp: DWORD, + pub CheckSum: DWORD, + pub NumSyms: DWORD, + pub SymType: SYM_TYPE, + pub ModuleName: [WCHAR; 32], + pub ImageName: [WCHAR; 256], + pub LoadedImageName: [WCHAR; 256], + pub LoadedPdbName: [WCHAR; 256], + pub CVSig: DWORD, + pub CVData: [WCHAR; MAX_PATH * 3], + pub PdbSig: DWORD, + pub PdbSig70: GUID, + pub PdbAge: DWORD, + pub PdbUnmatched: BOOL, + pub DbgUnmatched: BOOL, + pub LineNumbers: BOOL, + pub GlobalSymbols: BOOL, + pub TypeInfo: BOOL, + pub SourceIndexed: BOOL, + pub Publics: BOOL, + pub MachineType: DWORD, + pub Reserved: DWORD, +} + +#[link(name="dbghelp")] +extern "system" { + fn SymGetModuleInfoW64(process: HANDLE, addr: u64, info: *mut IMAGEHLP_MODULEW64) -> BOOL; + fn SymRefreshModuleList(process: HANDLE) -> BOOL; +} diff --git a/remoteprocess/src/windows/unwinder.rs b/remoteprocess/src/windows/unwinder.rs index b656d5e7..52b18a17 100755 --- a/remoteprocess/src/windows/unwinder.rs +++ b/remoteprocess/src/windows/unwinder.rs @@ -1,18 +1,11 @@ use winapi::um::processthreadsapi::{GetThreadContext, }; -use winapi::um::winnt::{HANDLE, CONTEXT, IMAGE_FILE_MACHINE_AMD64, WCHAR}; -use winapi::um::errhandlingapi::GetLastError; -use winapi::shared::minwindef::{TRUE, BOOL, DWORD, MAX_PATH}; -use winapi::shared::guiddef::GUID; -use winapi::shared::basetsd::DWORD64; -use winapi::um::dbghelp::{SymInitializeW, SymCleanup, - StackWalk64, STACKFRAME64, AddrModeFlat, ADDRESS64, - SymFromAddrW, SymGetLineFromAddrW64, MAX_SYM_NAME, SYMBOL_INFOW, IMAGEHLP_LINEW64}; -use std::os::windows::ffi::{OsStringExt}; +use winapi::um::winnt::{HANDLE, CONTEXT, IMAGE_FILE_MACHINE_AMD64}; + +use winapi::shared::minwindef::TRUE; +use winapi::um::dbghelp::{StackWalk64, STACKFRAME64, AddrModeFlat, ADDRESS64}; use super::Thread; use super::super::Error; -use super::super::StackFrame; -use libc::wcslen; pub struct Unwinder { pub handle: HANDLE @@ -27,106 +20,12 @@ pub struct Cursor { impl Unwinder { pub fn new(handle: HANDLE) -> Result { - unsafe { - if SymInitializeW(handle, std::ptr::null_mut(), TRUE) == 0 { - return Err(Error::from(std::io::Error::last_os_error())); - }; - Ok(Unwinder{handle}) - } - } - - pub fn reload(&mut self) -> Result<(), Error> { - info!("reloading symbol module list"); - unsafe { SymRefreshModuleList(self.handle); } - Ok(()) + Ok(Unwinder{handle}) } pub fn cursor(&self, thread: &Thread) -> Result { Cursor::new(thread.thread.0, self.handle) } - - pub fn symbolicate(&self, addr: u64, line_info: bool, callback: &mut FnMut(&StackFrame)) -> Result<(), Error> { - let function = unsafe { self.symbol_function(addr) }; - - let module = match unsafe { self.symbol_module (addr) } { - Ok(module) => module, - Err(Error::NoBinaryForAddress(_)) => { - unsafe { - SymRefreshModuleList(self.handle); - self.symbol_module(addr).unwrap_or_else(|_| "?".to_owned()) - } - }, - Err(_) => "?".to_owned() - }; - - let mut line = None; - let mut filename = None; - - if line_info { - if let Some((f, l)) = unsafe { self.symbol_filename(addr) } { - line = Some(l); - filename = Some(f); - } - } - callback(&StackFrame{function, filename, line, module, addr}); - Ok(()) - } - - // returns the corresponding function name for an address - pub unsafe fn symbol_function(&self, addr: u64) -> Option { - let mut buffer = std::mem::zeroed::(); - let symbol_info = &mut *(buffer.buffer.as_mut_ptr() as *mut SYMBOL_INFOW); - symbol_info.MaxNameLen = MAX_SYM_NAME as u32; - // there must be a way to get this - symbol_info.SizeOfStruct = 88; - - let mut displacement = 0; - let ret = SymFromAddrW(self.handle, addr, &mut displacement, symbol_info); - if ret != TRUE { - return None; - } - - let length = std::cmp::min(symbol_info.NameLen as usize, symbol_info.MaxNameLen as usize - 1); - let symbol = std::slice::from_raw_parts(symbol_info.Name.as_ptr() as *const u16, length); - let symbol = std::ffi::OsString::from_wide(symbol); - Some(symbol.to_string_lossy().to_owned().to_string()) - } - - // get the corresponding filename/linke - pub unsafe fn symbol_filename(&self, addr: u64) -> Option<(String, u64)> { - let mut displacement = 0; - let mut info = std::mem::zeroed::(); - info.SizeOfStruct = std::mem::size_of_val(&info) as u32; - if SymGetLineFromAddrW64(self.handle, addr, &mut displacement, &mut info) != TRUE { - return None; - } - let filename = std::slice::from_raw_parts(info.FileName, wcslen(info.FileName)); - let filename = std::ffi::OsString::from_wide(filename); - Some((filename.to_string_lossy().to_owned().to_string(), info.LineNumber.into())) - } - - // get the corresponding module name - pub unsafe fn symbol_module(&self, addr: u64) -> Result { - let mut info = std::mem::zeroed::(); - info.SizeOfStruct = std::mem::size_of_val(&info) as u32; - if SymGetModuleInfoW64(self.handle, addr, &mut info) != TRUE { - if GetLastError() == 126 { - return Err(Error::NoBinaryForAddress(addr)); - } - return Err(Error::IOError(std::io::Error::last_os_error())); - } - let filename = info.LoadedImageName.as_ptr(); - let filename = std::slice::from_raw_parts(filename, wcslen(filename)); - let filename = std::ffi::OsString::from_wide(filename); - - Ok(filename.to_string_lossy().to_owned().to_string()) - } -} - -impl Drop for Unwinder { - fn drop(&mut self) { - unsafe { SymCleanup(self.handle); } - } } impl Cursor { @@ -182,62 +81,5 @@ impl Iterator for Cursor { } } -#[repr(C, align(8))] -struct SymbolBuffer { - buffer: [u8; std::mem::size_of::() + MAX_SYM_NAME * 2] -} - #[repr(C, align(16))] -struct Context(CONTEXT); - -// missing from winapi-rs =( -#[allow(dead_code)] -#[repr(C)] -pub enum SYM_TYPE { - SymNone, - SymCoff, - SymCv, - SymPdb, - SymExport, - SymDeferred, - SymSym, - SymDia, - SymVirtual, - NumSymTypes, -} - -#[allow(non_snake_case)] -#[repr(C)] -pub struct IMAGEHLP_MODULEW64 { - pub SizeOfStruct: DWORD, - pub BaseOfImage: DWORD64, - pub ImageSize: DWORD, - pub TimeDateStamp: DWORD, - pub CheckSum: DWORD, - pub NumSyms: DWORD, - pub SymType: SYM_TYPE, - pub ModuleName: [WCHAR; 32], - pub ImageName: [WCHAR; 256], - pub LoadedImageName: [WCHAR; 256], - pub LoadedPdbName: [WCHAR; 256], - pub CVSig: DWORD, - pub CVData: [WCHAR; MAX_PATH * 3], - pub PdbSig: DWORD, - pub PdbSig70: GUID, - pub PdbAge: DWORD, - pub PdbUnmatched: BOOL, - pub DbgUnmatched: BOOL, - pub LineNumbers: BOOL, - pub GlobalSymbols: BOOL, - pub TypeInfo: BOOL, - pub SourceIndexed: BOOL, - pub Publics: BOOL, - pub MachineType: DWORD, - pub Reserved: DWORD, -} - -#[link(name="dbghelp")] -extern "system" { - fn SymGetModuleInfoW64(process: HANDLE, addr: u64, info: *mut IMAGEHLP_MODULEW64) -> BOOL; - fn SymRefreshModuleList(process: HANDLE) -> BOOL; -} +struct Context(CONTEXT); \ No newline at end of file diff --git a/src/native_stack_trace.rs b/src/native_stack_trace.rs index 583f47b8..a69c1387 100644 --- a/src/native_stack_trace.rs +++ b/src/native_stack_trace.rs @@ -16,9 +16,7 @@ pub struct NativeStack { libpython: Option, cython_maps: cython::SourceMaps, unwinder: remoteprocess::Unwinder, - // on linux, we also fallback to using libunwind if the main gimli based unwinder fails - #[cfg(target_os="linux")] - libunwinder: remoteprocess::LibUnwind, + symbolicator: remoteprocess::Symbolicator, // TODO: right now on windows if we don't hold on the process handle unwinding will fail #[allow(dead_code)] process: remoteprocess::Process, @@ -31,16 +29,11 @@ impl NativeStack { let process = remoteprocess::Process::new(pid)?; let unwinder = process.unwinder()?; + let symbolicator = process.symbolicator()?; - // Try to load up libunwind-ptrace on linux - #[cfg(target_os="linux")] - let libunwinder = remoteprocess::libunwind::LibUnwind::new()?; - - return Ok(NativeStack{cython_maps, unwinder, should_reload: false, + return Ok(NativeStack{cython_maps, unwinder, symbolicator, should_reload: false, python, libpython, - #[cfg(target_os="linux")] - libunwinder, process, symbol_cache: LruCache::new(4096) }); @@ -48,38 +41,15 @@ impl NativeStack { pub fn merge_native_thread(&mut self, frames: &Vec, thread: &remoteprocess::Thread) -> Result, Error> { if self.should_reload { - self.unwinder.reload()?; + self.symbolicator.reload()?; self.should_reload = false; } // get the native stack from the thread - #[cfg(not(target_os="linux"))] let native_stack = self.get_thread(thread)?; - // on linux, try again with libunwind if we fail with the gimli based unwinder - #[cfg(target_os="linux")] - let (native_stack, from_libunwind) = match self.get_thread(&thread) { - Ok(x) => (x, false), - Err(_) => (self.get_libunwind_thread(&thread)?, true) - }; - // TODO: merging the two stack together could happen outside of thread lock - #[cfg(not(target_os="linux"))] return self.merge_native_stack(frames, native_stack); - - #[cfg(target_os="linux")] - match self.merge_native_stack(frames, native_stack) { - Ok(merged) => return Ok(merged), - Err(e) => { - // if we got an error merging the stack traces, try again with libunwind - // (but only if we haven't used libunwind already) - if from_libunwind { - return Err(e); - } - let native_stack = self.get_libunwind_thread(&thread)?; - return self.merge_native_stack(frames, native_stack); - } - } } pub fn merge_native_stack(&mut self, frames: &Vec, native_stack: Vec) -> Result, Error> { let mut python_frame_index = 0; @@ -124,7 +94,7 @@ impl NativeStack { let mut symbolicated_count = 0; let mut first_frame = None; - self.unwinder.symbolicate(addr, !is_python_addr, &mut |frame: &remoteprocess::StackFrame| { + self.symbolicator.symbolicate(addr, !is_python_addr, &mut |frame: &remoteprocess::StackFrame| { symbolicated_count += 1; if symbolicated_count == 1 { first_frame = Some(frame.clone()); @@ -273,22 +243,7 @@ impl NativeStack { fn get_thread(&mut self, thread: &remoteprocess::Thread) -> Result, Error> { let mut stack = Vec::new(); - let mut cursor = self.unwinder.cursor(thread)?; - - while let Some(ip) = cursor.next() { - if let Err(remoteprocess::Error::NoBinaryForAddress(addr)) = ip { - debug!("don't have a binary for 0x{:x} - reloading", addr); - self.should_reload = true; - } - stack.push(ip?); - } - Ok(stack) - } - - #[cfg(target_os="linux")] - fn get_libunwind_thread(&self, thread: &remoteprocess::Thread) -> Result, Error> { - let mut stack = Vec::new(); - for ip in self.libunwinder.cursor(thread.id()? as i32)? { + for ip in self.unwinder.cursor(thread)? { stack.push(ip?); } Ok(stack) @@ -298,7 +253,7 @@ impl NativeStack { pub fn get_pthread_id(&self, thread: &remoteprocess::Thread, threadids: &HashSet) -> Result { let mut pthread_id = 0; - let mut cursor = self.libunwinder.cursor(thread.id()? as i32)?; + let mut cursor = self.unwinder.cursor(thread)?; while let Some(_) = cursor.next() { // the pthread_id is usually in the top-level frame of the thread, but on some configs // can be 2nd level. Handle this by taking the top-most rbx value that is one of the diff --git "a/tests/scripts/unicode\360\237\222\251.py" "b/tests/scripts/unicode\360\237\222\251.py" index caf7cf9e..66640a54 100755 --- "a/tests/scripts/unicode\360\237\222\251.py" +++ "b/tests/scripts/unicode\360\237\222\251.py" @@ -6,4 +6,4 @@ def sléép(seconds): time.sleep(seconds) if __name__ == "__main__": - sléép(1) + sléép(100)