diff options
Diffstat (limited to 'vendor/addr2line')
-rw-r--r-- | vendor/addr2line/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/addr2line/CHANGELOG.md | 336 | ||||
-rw-r--r-- | vendor/addr2line/Cargo.lock | 704 | ||||
-rw-r--r-- | vendor/addr2line/Cargo.toml | 147 | ||||
-rw-r--r-- | vendor/addr2line/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | vendor/addr2line/LICENSE-MIT | 25 | ||||
-rw-r--r-- | vendor/addr2line/README.md | 48 | ||||
-rw-r--r-- | vendor/addr2line/bench.plot.r | 23 | ||||
-rwxr-xr-x | vendor/addr2line/benchmark.sh | 112 | ||||
-rw-r--r-- | vendor/addr2line/coverage.sh | 5 | ||||
-rw-r--r-- | vendor/addr2line/examples/addr2line.rs | 317 | ||||
-rw-r--r-- | vendor/addr2line/rustfmt.toml | 1 | ||||
-rw-r--r-- | vendor/addr2line/src/builtin_split_dwarf_loader.rs | 164 | ||||
-rw-r--r-- | vendor/addr2line/src/function.rs | 555 | ||||
-rw-r--r-- | vendor/addr2line/src/lazy.rs | 31 | ||||
-rw-r--r-- | vendor/addr2line/src/lib.rs | 1729 | ||||
-rw-r--r-- | vendor/addr2line/tests/correctness.rs | 126 | ||||
-rw-r--r-- | vendor/addr2line/tests/output_equivalence.rs | 135 | ||||
-rw-r--r-- | vendor/addr2line/tests/parse.rs | 114 |
19 files changed, 4774 insertions, 0 deletions
diff --git a/vendor/addr2line/.cargo-checksum.json b/vendor/addr2line/.cargo-checksum.json new file mode 100644 index 0000000..5b12782 --- /dev/null +++ b/vendor/addr2line/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"ef9fa958318e442f1da7d204494cefec75c144aa6d5d5c93b0a5d6fcdf4ef6c6","Cargo.lock":"20b23c454fc3127f08a1bcd2864bbf029793759e6411fba24d44d8f4b7831ad0","Cargo.toml":"d0f15fde73d42bdf00e93f960dff908447225bede9364cb1659e44740a536c04","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"e99d88d232bf57d70f0fb87f6b496d44b6653f99f8a63d250a54c61ea4bcde40","README.md":"76d28502bd2e83f6a9e3576bd45e9a7fe5308448c4b5384b0d249515b5f67a5c","bench.plot.r":"6a5d7a4d36ed6b3d9919be703a479bef47698bf947818b483ff03951df2d4e01","benchmark.sh":"b35f89b1ca2c1dc0476cdd07f0284b72d41920d1c7b6054072f50ffba296d78d","coverage.sh":"4677e81922d08a82e83068a911717a247c66af12e559f37b78b6be3337ac9f07","examples/addr2line.rs":"3c5eb5a6726634df6cf53e4d67ee9f90c9ac09838303947f45c3bea1e84548b5","rustfmt.toml":"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b","src/builtin_split_dwarf_loader.rs":"dc6979de81b35f82e97275e6be27ec61f3c4225ea10574a9e031813e00185174","src/function.rs":"68f047e0c78afe18ad165db255c8254ee74c35cd6df0cc07e400252981f661ed","src/lazy.rs":"0bf23f7098f1902f181e43c2ffa82a3f86df2c0dbcb9bc0ebce6a0168dd8b060","src/lib.rs":"9d6531f71fd138d31cc7596db9ab234198d0895a21ea9cb116434c19ec78b660","tests/correctness.rs":"4081f8019535305e3aa254c6a4e1436272dd873f9717c687ca0e66ea8d5871ed","tests/output_equivalence.rs":"b2cd7c59fa55808a2e66e9fe7f160d846867e3ecefe22c22a818f822c3c41f23","tests/parse.rs":"c2f7362e4679c1b4803b12ec6e8dca6da96aed7273fd210a857524a4182c30e7"},"package":"8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"}
\ No newline at end of file diff --git a/vendor/addr2line/CHANGELOG.md b/vendor/addr2line/CHANGELOG.md new file mode 100644 index 0000000..8754c71 --- /dev/null +++ b/vendor/addr2line/CHANGELOG.md @@ -0,0 +1,336 @@ +# `addr2line` Change Log + +-------------------------------------------------------------------------------- + +## 0.21.0 (2023/08/12) + +### Breaking changes + +* Updated `gimli`, `object`, and `fallible-iterator` dependencies. + +### Changed + +* The minimum supported rust version is 1.65.0. + +* Store boxed slices instead of `Vec` objects in `Context`. + [#278](https://github.com/gimli-rs/addr2line/pull/278) + +-------------------------------------------------------------------------------- + +## 0.20.0 (2023/04/15) + +### Breaking changes + +* The minimum supported rust version is 1.58.0. + +* Changed `Context::find_frames` to return `LookupResult`. + Use `LookupResult::skip_all_loads` to obtain the result without loading split DWARF. + [#260](https://github.com/gimli-rs/addr2line/pull/260) + +* Replaced `Context::find_dwarf_unit` with `Context::find_dwarf_and_unit`. + [#260](https://github.com/gimli-rs/addr2line/pull/260) + +* Updated `object` dependency. + +### Changed + +* Fix handling of file index 0 for DWARF 5. + [#264](https://github.com/gimli-rs/addr2line/pull/264) + +### Added + +* Added types and methods to support loading split DWARF: + `LookupResult`, `SplitDwarfLoad`, `SplitDwarfLoader`, `Context::preload_units`. + [#260](https://github.com/gimli-rs/addr2line/pull/260) + [#262](https://github.com/gimli-rs/addr2line/pull/262) + [#263](https://github.com/gimli-rs/addr2line/pull/263) + +-------------------------------------------------------------------------------- + +## 0.19.0 (2022/11/24) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +-------------------------------------------------------------------------------- + +## 0.18.0 (2022/07/16) + +### Breaking changes + +* Updated `object` dependency. + +### Changed + +* Fixed handling of relative path for `DW_AT_comp_dir`. + [#239](https://github.com/gimli-rs/addr2line/pull/239) + +* Fixed handling of `DW_FORM_addrx` for DWARF 5 support. + [#243](https://github.com/gimli-rs/addr2line/pull/243) + +* Fixed handling of units that are missing range information. + [#249](https://github.com/gimli-rs/addr2line/pull/249) + +-------------------------------------------------------------------------------- + +## 0.17.0 (2021/10/24) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +### Changed + +* Use `skip_attributes` to improve performance. + [#236](https://github.com/gimli-rs/addr2line/pull/236) + +-------------------------------------------------------------------------------- + +## 0.16.0 (2021/07/26) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +-------------------------------------------------------------------------------- + +## 0.15.2 (2021/06/04) + +### Fixed + +* Allow `Context` to be `Send`. + [#219](https://github.com/gimli-rs/addr2line/pull/219) + +-------------------------------------------------------------------------------- + +## 0.15.1 (2021/05/02) + +### Fixed + +* Don't ignore aranges with address 0. + [#217](https://github.com/gimli-rs/addr2line/pull/217) + +-------------------------------------------------------------------------------- + +## 0.15.0 (2021/05/02) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + [#215](https://github.com/gimli-rs/addr2line/pull/215) + +* Added `debug_aranges` parameter to `Context::from_sections`. + [#200](https://github.com/gimli-rs/addr2line/pull/200) + +### Added + +* Added `.debug_aranges` support. + [#200](https://github.com/gimli-rs/addr2line/pull/200) + +* Added supplementary object file support. + [#208](https://github.com/gimli-rs/addr2line/pull/208) + +### Fixed + +* Fixed handling of Windows paths in locations. + [#209](https://github.com/gimli-rs/addr2line/pull/209) + +* examples/addr2line: Flush stdout after each response. + [#210](https://github.com/gimli-rs/addr2line/pull/210) + +* examples/addr2line: Avoid copying every section. + [#213](https://github.com/gimli-rs/addr2line/pull/213) + +-------------------------------------------------------------------------------- + +## 0.14.1 (2020/12/31) + +### Fixed + +* Fix location lookup for skeleton units. + [#201](https://github.com/gimli-rs/addr2line/pull/201) + +### Added + +* Added `Context::find_location_range`. + [#196](https://github.com/gimli-rs/addr2line/pull/196) + [#199](https://github.com/gimli-rs/addr2line/pull/199) + +-------------------------------------------------------------------------------- + +## 0.14.0 (2020/10/27) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +### Fixed + +* Handle units that only have line information. + [#188](https://github.com/gimli-rs/addr2line/pull/188) + +* Handle DWARF units with version <= 4 and no `DW_AT_name`. + [#191](https://github.com/gimli-rs/addr2line/pull/191) + +* Fix handling of `DW_FORM_ref_addr`. + [#193](https://github.com/gimli-rs/addr2line/pull/193) + +-------------------------------------------------------------------------------- + +## 0.13.0 (2020/07/07) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +* Added `rustc-dep-of-std` feature. + [#166](https://github.com/gimli-rs/addr2line/pull/166) + +### Changed + +* Improve performance by parsing function contents lazily. + [#178](https://github.com/gimli-rs/addr2line/pull/178) + +* Don't skip `.debug_info` and `.debug_line` entries with a zero address. + [#182](https://github.com/gimli-rs/addr2line/pull/182) + +-------------------------------------------------------------------------------- + +## 0.12.2 (2020/06/21) + +### Fixed + +* Avoid linear search for `DW_FORM_ref_addr`. + [#175](https://github.com/gimli-rs/addr2line/pull/175) + +-------------------------------------------------------------------------------- + +## 0.12.1 (2020/05/19) + +### Fixed + +* Handle units with overlapping address ranges. + [#163](https://github.com/gimli-rs/addr2line/pull/163) + +* Don't assert for functions with overlapping address ranges. + [#168](https://github.com/gimli-rs/addr2line/pull/168) + +-------------------------------------------------------------------------------- + +## 0.12.0 (2020/05/12) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +* Added more optional features: `smallvec` and `fallible-iterator`. + [#160](https://github.com/gimli-rs/addr2line/pull/160) + +### Added + +* Added `Context::dwarf` and `Context::find_dwarf_unit`. + [#159](https://github.com/gimli-rs/addr2line/pull/159) + +### Changed + +* Removed `lazycell` dependency. + [#160](https://github.com/gimli-rs/addr2line/pull/160) + +-------------------------------------------------------------------------------- + +## 0.11.0 (2020/01/11) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +* [#130](https://github.com/gimli-rs/addr2line/pull/130) + Changed `Location::file` from `Option<String>` to `Option<&str>`. + This required adding lifetime parameters to `Location` and other structs that + contain it. + +* [#152](https://github.com/gimli-rs/addr2line/pull/152) + Changed `Location::line` and `Location::column` from `Option<u64>`to `Option<u32>`. + +* [#156](https://github.com/gimli-rs/addr2line/pull/156) + Deleted `alloc` feature, and fixed `no-std` builds with stable rust. + Removed default `Reader` parameter for `Context`, and added `ObjectContext` instead. + +### Added + +* [#134](https://github.com/gimli-rs/addr2line/pull/134) + Added `Context::from_dwarf`. + +### Changed + +* [#133](https://github.com/gimli-rs/addr2line/pull/133) + Fixed handling of units that can't be parsed. + +* [#155](https://github.com/gimli-rs/addr2line/pull/155) + Fixed `addr2line` output to match binutils. + +* [#130](https://github.com/gimli-rs/addr2line/pull/130) + Improved `.debug_line` parsing performance. + +* [#148](https://github.com/gimli-rs/addr2line/pull/148) + [#150](https://github.com/gimli-rs/addr2line/pull/150) + [#151](https://github.com/gimli-rs/addr2line/pull/151) + [#152](https://github.com/gimli-rs/addr2line/pull/152) + Improved `.debug_info` parsing performance. + +* [#137](https://github.com/gimli-rs/addr2line/pull/137) + [#138](https://github.com/gimli-rs/addr2line/pull/138) + [#139](https://github.com/gimli-rs/addr2line/pull/139) + [#140](https://github.com/gimli-rs/addr2line/pull/140) + [#146](https://github.com/gimli-rs/addr2line/pull/146) + Improved benchmarks. + +-------------------------------------------------------------------------------- + +## 0.10.0 (2019/07/07) + +### Breaking changes + +* [#127](https://github.com/gimli-rs/addr2line/pull/127) + Update `gimli`. + +-------------------------------------------------------------------------------- + +## 0.9.0 (2019/05/02) + +### Breaking changes + +* [#121](https://github.com/gimli-rs/addr2line/pull/121) + Update `gimli`, `object`, and `fallible-iterator` dependencies. + +### Added + +* [#121](https://github.com/gimli-rs/addr2line/pull/121) + Reexport `gimli`, `object`, and `fallible-iterator`. + +-------------------------------------------------------------------------------- + +## 0.8.0 (2019/02/06) + +### Breaking changes + +* [#107](https://github.com/gimli-rs/addr2line/pull/107) + Update `object` dependency to 0.11. This is part of the public API. + +### Added + +* [#101](https://github.com/gimli-rs/addr2line/pull/101) + Add `object` feature (enabled by default). Disable this feature to remove + the `object` dependency and `Context::new` API. + +* [#102](https://github.com/gimli-rs/addr2line/pull/102) + Add `std` (enabled by default) and `alloc` features. + +### Changed + +* [#108](https://github.com/gimli-rs/addr2line/issues/108) + `demangle` no longer outputs the hash for rust symbols. + +* [#109](https://github.com/gimli-rs/addr2line/issues/109) + Set default `R` for `Context<R>`. diff --git a/vendor/addr2line/Cargo.lock b/vendor/addr2line/Cargo.lock new file mode 100644 index 0000000..3c23002 --- /dev/null +++ b/vendor/addr2line/Cargo.lock @@ -0,0 +1,704 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.2", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +dependencies = [ + "backtrace", + "clap", + "compiler_builtins", + "cpp_demangle", + "fallible-iterator", + "findshlibs", + "gimli 0.28.0", + "libtest-mimic", + "memmap2", + "object 0.32.0", + "rustc-demangle", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "smallvec", + "typed-arena", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line 0.19.0", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.30.3", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "compiler_builtins" +version = "0.1.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571298a3cce7e2afbd3d61abb91a18667d5ab25993ec577a88ee8ac45f00cc3a" + +[[package]] +name = "cpp_demangle" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c76f98bdfc7f66172e6c7065f981ebb576ffc903fe4c0561d9f0c2509226dc6" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +dependencies = [ + "compiler_builtins", + "fallible-iterator", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.2", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.2", + "rustix 0.38.8", + "windows-sys", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libtest-mimic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d8de370f98a6cb8a4606618e53e802f93b094ddec0f96988eaec2c27e6e9ce7" +dependencies = [ + "clap", + "termcolor", + "threadpool", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +dependencies = [ + "flate2", + "memchr", + "ruzstd", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff66d57013a5686e1917ed6a025d54dd591fcda71a41fe07edf4d16726aefa86" + +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c" + +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.5", + "windows-sys", +] + +[[package]] +name = "ruzstd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc" +dependencies = [ + "byteorder", + "thiserror-core", + "twox-hash", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +dependencies = [ + "rustix 0.37.23", + "windows-sys", +] + +[[package]] +name = "thiserror-core" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d97345f6437bb2004cd58819d8a9ef8e36cdd7661c2abc4bbde0a7c40d9f497" +dependencies = [ + "thiserror-core-impl", +] + +[[package]] +name = "thiserror-core-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/vendor/addr2line/Cargo.toml b/vendor/addr2line/Cargo.toml new file mode 100644 index 0000000..b6b6b83 --- /dev/null +++ b/vendor/addr2line/Cargo.toml @@ -0,0 +1,147 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.65" +name = "addr2line" +version = "0.21.0" +exclude = [ + "/benches/*", + "/fixtures/*", + ".github", +] +description = "A cross-platform symbolication library written in Rust, using `gimli`" +documentation = "https://docs.rs/addr2line" +readme = "./README.md" +keywords = [ + "DWARF", + "debug", + "elf", + "symbolicate", + "atos", +] +categories = ["development-tools::debugging"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/gimli-rs/addr2line" + +[profile.bench] +codegen-units = 1 +debug = true + +[profile.release] +debug = true + +[[example]] +name = "addr2line" +required-features = ["default"] + +[[test]] +name = "output_equivalence" +harness = false +required-features = ["default"] + +[[test]] +name = "correctness" +required-features = ["default"] + +[[test]] +name = "parse" +required-features = ["std-object"] + +[dependencies.alloc] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-alloc" + +[dependencies.compiler_builtins] +version = "0.1.2" +optional = true + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" + +[dependencies.cpp_demangle] +version = "0.4" +features = ["alloc"] +optional = true +default-features = false + +[dependencies.fallible-iterator] +version = "0.3.0" +optional = true +default-features = false + +[dependencies.gimli] +version = "0.28.0" +features = ["read"] +default-features = false + +[dependencies.memmap2] +version = "0.5.5" +optional = true + +[dependencies.object] +version = "0.32.0" +features = ["read"] +optional = true +default-features = false + +[dependencies.rustc-demangle] +version = "0.1" +optional = true + +[dependencies.smallvec] +version = "1" +optional = true +default-features = false + +[dev-dependencies.backtrace] +version = "0.3.13" + +[dev-dependencies.clap] +version = "4.3.21" +features = ["wrap_help"] + +[dev-dependencies.findshlibs] +version = "0.10" + +[dev-dependencies.libtest-mimic] +version = "0.6.1" + +[dev-dependencies.typed-arena] +version = "2" + +[features] +default = [ + "rustc-demangle", + "cpp_demangle", + "std-object", + "fallible-iterator", + "smallvec", + "memmap2", +] +rustc-dep-of-std = [ + "core", + "alloc", + "compiler_builtins", + "gimli/rustc-dep-of-std", +] +std = ["gimli/std"] +std-object = [ + "std", + "object", + "object/std", + "object/compression", + "gimli/endian-reader", +] diff --git a/vendor/addr2line/LICENSE-APACHE b/vendor/addr2line/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/vendor/addr2line/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/addr2line/LICENSE-MIT b/vendor/addr2line/LICENSE-MIT new file mode 100644 index 0000000..3a03f1f --- /dev/null +++ b/vendor/addr2line/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016-2018 The gimli Developers + +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. diff --git a/vendor/addr2line/README.md b/vendor/addr2line/README.md new file mode 100644 index 0000000..dc6cb93 --- /dev/null +++ b/vendor/addr2line/README.md @@ -0,0 +1,48 @@ +# addr2line + +[![](https://img.shields.io/crates/v/addr2line.svg)](https://crates.io/crates/addr2line) +[![](https://img.shields.io/docsrs/addr2line.svg)](https://docs.rs/addr2line) +[![Coverage Status](https://coveralls.io/repos/github/gimli-rs/addr2line/badge.svg?branch=master)](https://coveralls.io/github/gimli-rs/addr2line?branch=master) + +A cross-platform library for retrieving per-address debug information +from files with DWARF debug information. + +`addr2line` uses [`gimli`](https://github.com/gimli-rs/gimli) to parse +the debug information, and exposes an interface for finding +the source file, line number, and wrapping function for instruction +addresses within the target program. These lookups can either be +performed programmatically through `Context::find_location` and +`Context::find_frames`, or via the included example binary, +`addr2line` (named and modelled after the equivalent utility from +[GNU binutils](https://sourceware.org/binutils/docs/binutils/addr2line.html)). + +# Quickstart + - Add the [`addr2line` crate](https://crates.io/crates/addr2line) to your `Cargo.toml` + - Load the file and parse it with [`addr2line::object::read::File::parse`](https://docs.rs/object/*/object/read/struct.File.html#method.parse) + - Pass the parsed file to [`addr2line::Context::new` ](https://docs.rs/addr2line/*/addr2line/struct.Context.html#method.new) + - Use [`addr2line::Context::find_location`](https://docs.rs/addr2line/*/addr2line/struct.Context.html#method.find_location) + or [`addr2line::Context::find_frames`](https://docs.rs/addr2line/*/addr2line/struct.Context.html#method.find_frames) + to look up debug information for an address + +# Performance + +`addr2line` optimizes for speed over memory by caching parsed information. +The DWARF information is parsed lazily where possible. + +The library aims to perform similarly to equivalent existing tools such +as `addr2line` from binutils, `eu-addr2line` from elfutils, and +`llvm-symbolize` from the llvm project, and in the past some benchmarking +was done that indicates a comparable performance. + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([`LICENSE-MIT`](./LICENSE-MIT) or https://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/vendor/addr2line/bench.plot.r b/vendor/addr2line/bench.plot.r new file mode 100644 index 0000000..ecbf248 --- /dev/null +++ b/vendor/addr2line/bench.plot.r @@ -0,0 +1,23 @@ +v <- read.table(file("stdin")) +t <- data.frame(prog=v[,1], funcs=(v[,2]=="func"), time=v[,3], mem=v[,4], stringsAsFactors=FALSE) + +t$prog <- as.character(t$prog) +t$prog[t$prog == "master"] <- "gimli-rs/addr2line" +t$funcs[t$funcs == TRUE] <- "With functions" +t$funcs[t$funcs == FALSE] <- "File/line only" +t$mem = t$mem / 1024.0 + +library(ggplot2) +p <- ggplot(data=t, aes(x=prog, y=time, fill=prog)) +p <- p + geom_bar(stat = "identity") +p <- p + facet_wrap(~ funcs) +p <- p + theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank()) +p <- p + ylab("time (s)") + ggtitle("addr2line runtime") +ggsave('time.png',plot=p,width=10,height=6) + +p <- ggplot(data=t, aes(x=prog, y=mem, fill=prog)) +p <- p + geom_bar(stat = "identity") +p <- p + facet_wrap(~ funcs) +p <- p + theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank()) +p <- p + ylab("memory (kB)") + ggtitle("addr2line memory usage") +ggsave('memory.png',plot=p,width=10,height=6) diff --git a/vendor/addr2line/benchmark.sh b/vendor/addr2line/benchmark.sh new file mode 100755 index 0000000..ca4c4f6 --- /dev/null +++ b/vendor/addr2line/benchmark.sh @@ -0,0 +1,112 @@ +#!/bin/bash +if [[ $# -le 1 ]]; then + echo "Usage: $0 <executable> [<addresses>] REFS..." + exit 1 +fi +target="$1" +shift + +addresses="" +if [[ -e "$1" ]]; then + addresses="$1" + shift +fi + +# path to "us" +# readlink -f, but more portable: +dirname=$(perl -e 'use Cwd "abs_path";print abs_path(shift)' "$(dirname "$0")") + +# https://stackoverflow.com/a/2358432/472927 +{ + # compile all refs + pushd "$dirname" > /dev/null + # if the user has some local changes, preserve them + nstashed=$(git stash list | wc -l) + echo "==> Stashing any local modifications" + git stash --keep-index > /dev/null + popstash() { + # https://stackoverflow.com/q/24520791/472927 + if [[ "$(git stash list | wc -l)" -ne "$nstashed" ]]; then + echo "==> Restoring stashed state" + git stash pop > /dev/null + fi + } + # if the user has added stuff to the index, abort + if ! git diff-index --quiet HEAD --; then + echo "Refusing to overwrite outstanding git changes" + popstash + exit 2 + fi + current=$(git symbolic-ref --short HEAD) + for ref in "$@"; do + echo "==> Compiling $ref" + git checkout -q "$ref" + commit=$(git rev-parse HEAD) + fn="target/release/addr2line-$commit" + if [[ ! -e "$fn" ]]; then + cargo build --release --example addr2line + cp target/release/examples/addr2line "$fn" + fi + if [[ "$ref" != "$commit" ]]; then + ln -sfn "addr2line-$commit" target/release/addr2line-"$ref" + fi + done + git checkout -q "$current" + popstash + popd > /dev/null + + # get us some addresses to look up + if [[ -z "$addresses" ]]; then + echo "==> Looking for benchmarking addresses (this may take a while)" + addresses=$(mktemp tmp.XXXXXXXXXX) + objdump -C -x --disassemble -l "$target" \ + | grep -P '0[048]:' \ + | awk '{print $1}' \ + | sed 's/:$//' \ + > "$addresses" + echo " -> Addresses stored in $addresses; you should re-use it next time" + fi + + run() { + func="$1" + name="$2" + cmd="$3" + args="$4" + printf "%s\t%s\t" "$name" "$func" + if [[ "$cmd" =~ llvm-symbolizer ]]; then + /usr/bin/time -f '%e\t%M' "$cmd" $args -obj="$target" < "$addresses" 2>&1 >/dev/null + else + /usr/bin/time -f '%e\t%M' "$cmd" $args -e "$target" < "$addresses" 2>&1 >/dev/null + fi + } + + # run without functions + log1=$(mktemp tmp.XXXXXXXXXX) + echo "==> Benchmarking" + run nofunc binutils addr2line >> "$log1" + #run nofunc elfutils eu-addr2line >> "$log1" + run nofunc llvm-sym llvm-symbolizer -functions=none >> "$log1" + for ref in "$@"; do + run nofunc "$ref" "$dirname/target/release/addr2line-$ref" >> "$log1" + done + cat "$log1" | column -t + + # run with functions + log2=$(mktemp tmp.XXXXXXXXXX) + echo "==> Benchmarking with -f" + run func binutils addr2line "-f -i" >> "$log2" + #run func elfutils eu-addr2line "-f -i" >> "$log2" + run func llvm-sym llvm-symbolizer "-functions=linkage -demangle=0" >> "$log2" + for ref in "$@"; do + run func "$ref" "$dirname/target/release/addr2line-$ref" "-f -i" >> "$log2" + done + cat "$log2" | column -t + cat "$log2" >> "$log1"; rm "$log2" + + echo "==> Plotting" + Rscript --no-readline --no-restore --no-save "$dirname/bench.plot.r" < "$log1" + + echo "==> Cleaning up" + rm "$log1" + exit 0 +} diff --git a/vendor/addr2line/coverage.sh b/vendor/addr2line/coverage.sh new file mode 100644 index 0000000..892c0b7 --- /dev/null +++ b/vendor/addr2line/coverage.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# Run tarpaulin and pycobertura to generate coverage.html. + +cargo tarpaulin --skip-clean --out Xml +pycobertura show --format html --output coverage.html cobertura.xml diff --git a/vendor/addr2line/examples/addr2line.rs b/vendor/addr2line/examples/addr2line.rs new file mode 100644 index 0000000..97fc802 --- /dev/null +++ b/vendor/addr2line/examples/addr2line.rs @@ -0,0 +1,317 @@ +use std::borrow::Cow; +use std::fs::File; +use std::io::{BufRead, Lines, StdinLock, Write}; +use std::path::{Path, PathBuf}; + +use clap::{Arg, ArgAction, Command}; +use fallible_iterator::FallibleIterator; +use object::{Object, ObjectSection, SymbolMap, SymbolMapName}; +use typed_arena::Arena; + +use addr2line::{Context, Location}; + +fn parse_uint_from_hex_string(string: &str) -> Option<u64> { + if string.len() > 2 && string.starts_with("0x") { + u64::from_str_radix(&string[2..], 16).ok() + } else { + u64::from_str_radix(string, 16).ok() + } +} + +enum Addrs<'a> { + Args(clap::parser::ValuesRef<'a, String>), + Stdin(Lines<StdinLock<'a>>), +} + +impl<'a> Iterator for Addrs<'a> { + type Item = Option<u64>; + + fn next(&mut self) -> Option<Option<u64>> { + let text = match *self { + Addrs::Args(ref mut vals) => vals.next().map(Cow::from), + Addrs::Stdin(ref mut lines) => lines.next().map(Result::unwrap).map(Cow::from), + }; + text.as_ref() + .map(Cow::as_ref) + .map(parse_uint_from_hex_string) + } +} + +fn print_loc(loc: Option<&Location<'_>>, basenames: bool, llvm: bool) { + if let Some(loc) = loc { + if let Some(ref file) = loc.file.as_ref() { + let path = if basenames { + Path::new(Path::new(file).file_name().unwrap()) + } else { + Path::new(file) + }; + print!("{}:", path.display()); + } else { + print!("??:"); + } + if llvm { + print!("{}:{}", loc.line.unwrap_or(0), loc.column.unwrap_or(0)); + } else if let Some(line) = loc.line { + print!("{}", line); + } else { + print!("?"); + } + println!(); + } else if llvm { + println!("??:0:0"); + } else { + println!("??:0"); + } +} + +fn print_function(name: Option<&str>, language: Option<gimli::DwLang>, demangle: bool) { + if let Some(name) = name { + if demangle { + print!("{}", addr2line::demangle_auto(Cow::from(name), language)); + } else { + print!("{}", name); + } + } else { + print!("??"); + } +} + +fn load_file_section<'input, 'arena, Endian: gimli::Endianity>( + id: gimli::SectionId, + file: &object::File<'input>, + endian: Endian, + arena_data: &'arena Arena<Cow<'input, [u8]>>, +) -> Result<gimli::EndianSlice<'arena, Endian>, ()> { + // TODO: Unify with dwarfdump.rs in gimli. + let name = id.name(); + match file.section_by_name(name) { + Some(section) => match section.uncompressed_data().unwrap() { + Cow::Borrowed(b) => Ok(gimli::EndianSlice::new(b, endian)), + Cow::Owned(b) => Ok(gimli::EndianSlice::new(arena_data.alloc(b.into()), endian)), + }, + None => Ok(gimli::EndianSlice::new(&[][..], endian)), + } +} + +fn find_name_from_symbols<'a>( + symbols: &'a SymbolMap<SymbolMapName<'_>>, + probe: u64, +) -> Option<&'a str> { + symbols.get(probe).map(|x| x.name()) +} + +struct Options<'a> { + do_functions: bool, + do_inlines: bool, + pretty: bool, + print_addrs: bool, + basenames: bool, + demangle: bool, + llvm: bool, + exe: &'a PathBuf, + sup: Option<&'a PathBuf>, +} + +fn main() { + let matches = Command::new("addr2line") + .version(env!("CARGO_PKG_VERSION")) + .about("A fast addr2line Rust port") + .max_term_width(100) + .args(&[ + Arg::new("exe") + .short('e') + .long("exe") + .value_name("filename") + .value_parser(clap::value_parser!(PathBuf)) + .help( + "Specify the name of the executable for which addresses should be translated.", + ) + .required(true), + Arg::new("sup") + .long("sup") + .value_name("filename") + .value_parser(clap::value_parser!(PathBuf)) + .help("Path to supplementary object file."), + Arg::new("functions") + .short('f') + .long("functions") + .action(ArgAction::SetTrue) + .help("Display function names as well as file and line number information."), + Arg::new("pretty").short('p').long("pretty-print") + .action(ArgAction::SetTrue) + .help( + "Make the output more human friendly: each location are printed on one line.", + ), + Arg::new("inlines").short('i').long("inlines") + .action(ArgAction::SetTrue) + .help( + "If the address belongs to a function that was inlined, the source information for \ + all enclosing scopes back to the first non-inlined function will also be printed.", + ), + Arg::new("addresses").short('a').long("addresses") + .action(ArgAction::SetTrue) + .help( + "Display the address before the function name, file and line number information.", + ), + Arg::new("basenames") + .short('s') + .long("basenames") + .action(ArgAction::SetTrue) + .help("Display only the base of each file name."), + Arg::new("demangle").short('C').long("demangle") + .action(ArgAction::SetTrue) + .help( + "Demangle function names. \ + Specifying a specific demangling style (like GNU addr2line) is not supported. \ + (TODO)" + ), + Arg::new("llvm") + .long("llvm") + .action(ArgAction::SetTrue) + .help("Display output in the same format as llvm-symbolizer."), + Arg::new("addrs") + .action(ArgAction::Append) + .help("Addresses to use instead of reading from stdin."), + ]) + .get_matches(); + + let arena_data = Arena::new(); + + let opts = Options { + do_functions: matches.get_flag("functions"), + do_inlines: matches.get_flag("inlines"), + pretty: matches.get_flag("pretty"), + print_addrs: matches.get_flag("addresses"), + basenames: matches.get_flag("basenames"), + demangle: matches.get_flag("demangle"), + llvm: matches.get_flag("llvm"), + exe: matches.get_one::<PathBuf>("exe").unwrap(), + sup: matches.get_one::<PathBuf>("sup"), + }; + + let file = File::open(opts.exe).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let object = &object::File::parse(&*map).unwrap(); + + let endian = if object.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + let mut load_section = |id: gimli::SectionId| -> Result<_, _> { + load_file_section(id, object, endian, &arena_data) + }; + + let sup_map; + let sup_object = if let Some(sup_path) = opts.sup { + let sup_file = File::open(sup_path).unwrap(); + sup_map = unsafe { memmap2::Mmap::map(&sup_file).unwrap() }; + Some(object::File::parse(&*sup_map).unwrap()) + } else { + None + }; + + let symbols = object.symbol_map(); + let mut dwarf = gimli::Dwarf::load(&mut load_section).unwrap(); + if let Some(ref sup_object) = sup_object { + let mut load_sup_section = |id: gimli::SectionId| -> Result<_, _> { + load_file_section(id, sup_object, endian, &arena_data) + }; + dwarf.load_sup(&mut load_sup_section).unwrap(); + } + + let mut split_dwarf_loader = addr2line::builtin_split_dwarf_loader::SplitDwarfLoader::new( + |data, endian| { + gimli::EndianSlice::new(arena_data.alloc(Cow::Owned(data.into_owned())), endian) + }, + Some(opts.exe.clone()), + ); + let ctx = Context::from_dwarf(dwarf).unwrap(); + + let stdin = std::io::stdin(); + let addrs = matches + .get_many::<String>("addrs") + .map(Addrs::Args) + .unwrap_or_else(|| Addrs::Stdin(stdin.lock().lines())); + + for probe in addrs { + if opts.print_addrs { + let addr = probe.unwrap_or(0); + if opts.llvm { + print!("0x{:x}", addr); + } else { + print!("0x{:016x}", addr); + } + if opts.pretty { + print!(": "); + } else { + println!(); + } + } + + if opts.do_functions || opts.do_inlines { + let mut printed_anything = false; + if let Some(probe) = probe { + let frames = ctx.find_frames(probe); + let frames = split_dwarf_loader.run(frames).unwrap(); + let mut frames = frames.enumerate(); + while let Some((i, frame)) = frames.next().unwrap() { + if opts.pretty && i != 0 { + print!(" (inlined by) "); + } + + if opts.do_functions { + if let Some(func) = frame.function { + print_function( + func.raw_name().ok().as_ref().map(AsRef::as_ref), + func.language, + opts.demangle, + ); + } else { + let name = find_name_from_symbols(&symbols, probe); + print_function(name, None, opts.demangle); + } + + if opts.pretty { + print!(" at "); + } else { + println!(); + } + } + + print_loc(frame.location.as_ref(), opts.basenames, opts.llvm); + + printed_anything = true; + + if !opts.do_inlines { + break; + } + } + } + + if !printed_anything { + if opts.do_functions { + let name = probe.and_then(|probe| find_name_from_symbols(&symbols, probe)); + print_function(name, None, opts.demangle); + + if opts.pretty { + print!(" at "); + } else { + println!(); + } + } + + print_loc(None, opts.basenames, opts.llvm); + } + } else { + let loc = probe.and_then(|probe| ctx.find_location(probe).unwrap()); + print_loc(loc.as_ref(), opts.basenames, opts.llvm); + } + + if opts.llvm { + println!(); + } + std::io::stdout().flush().unwrap(); + } +} diff --git a/vendor/addr2line/rustfmt.toml b/vendor/addr2line/rustfmt.toml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/vendor/addr2line/rustfmt.toml @@ -0,0 +1 @@ + diff --git a/vendor/addr2line/src/builtin_split_dwarf_loader.rs b/vendor/addr2line/src/builtin_split_dwarf_loader.rs new file mode 100644 index 0000000..4711931 --- /dev/null +++ b/vendor/addr2line/src/builtin_split_dwarf_loader.rs @@ -0,0 +1,164 @@ +use alloc::borrow::Cow; +use alloc::sync::Arc; +use std::fs::File; +use std::path::PathBuf; + +use object::Object; + +use crate::{LookupContinuation, LookupResult}; + +#[cfg(unix)] +fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>( + r: &R, +) -> Result<PathBuf, gimli::Error> { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + let bytes = r.to_slice()?; + let s = OsStr::from_bytes(&bytes); + Ok(PathBuf::from(s)) +} + +#[cfg(not(unix))] +fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>( + r: &R, +) -> Result<PathBuf, gimli::Error> { + let bytes = r.to_slice()?; + let s = std::str::from_utf8(&bytes).map_err(|_| gimli::Error::BadUtf8)?; + Ok(PathBuf::from(s)) +} + +fn load_section<'data: 'file, 'file, O, R, F>( + id: gimli::SectionId, + file: &'file O, + endian: R::Endian, + loader: &mut F, +) -> Result<R, gimli::Error> +where + O: object::Object<'data, 'file>, + R: gimli::Reader<Endian = gimli::RunTimeEndian>, + F: FnMut(Cow<'data, [u8]>, R::Endian) -> R, +{ + use object::ObjectSection; + + let data = id + .dwo_name() + .and_then(|dwo_name| { + file.section_by_name(dwo_name) + .and_then(|section| section.uncompressed_data().ok()) + }) + .unwrap_or(Cow::Borrowed(&[])); + Ok(loader(data, endian)) +} + +/// A simple builtin split DWARF loader. +pub struct SplitDwarfLoader<R, F> +where + R: gimli::Reader<Endian = gimli::RunTimeEndian>, + F: FnMut(Cow<'_, [u8]>, R::Endian) -> R, +{ + loader: F, + dwarf_package: Option<gimli::DwarfPackage<R>>, +} + +impl<R, F> SplitDwarfLoader<R, F> +where + R: gimli::Reader<Endian = gimli::RunTimeEndian>, + F: FnMut(Cow<'_, [u8]>, R::Endian) -> R, +{ + fn load_dwarf_package(loader: &mut F, path: Option<PathBuf>) -> Option<gimli::DwarfPackage<R>> { + let mut path = path.map(Ok).unwrap_or_else(std::env::current_exe).ok()?; + let dwp_extension = path + .extension() + .map(|previous_extension| { + let mut previous_extension = previous_extension.to_os_string(); + previous_extension.push(".dwp"); + previous_extension + }) + .unwrap_or_else(|| "dwp".into()); + path.set_extension(dwp_extension); + let file = File::open(&path).ok()?; + let map = unsafe { memmap2::Mmap::map(&file).ok()? }; + let dwp = object::File::parse(&*map).ok()?; + + let endian = if dwp.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + let empty = loader(Cow::Borrowed(&[]), endian); + gimli::DwarfPackage::load( + |section_id| load_section(section_id, &dwp, endian, loader), + empty, + ) + .ok() + } + + /// Create a new split DWARF loader. + pub fn new(mut loader: F, path: Option<PathBuf>) -> SplitDwarfLoader<R, F> { + let dwarf_package = SplitDwarfLoader::load_dwarf_package(&mut loader, path); + SplitDwarfLoader { + loader, + dwarf_package, + } + } + + /// Run the provided `LookupResult` to completion, loading any necessary + /// split DWARF along the way. + pub fn run<L>(&mut self, mut l: LookupResult<L>) -> L::Output + where + L: LookupContinuation<Buf = R>, + { + loop { + let (load, continuation) = match l { + LookupResult::Output(output) => break output, + LookupResult::Load { load, continuation } => (load, continuation), + }; + + let mut r: Option<Arc<gimli::Dwarf<_>>> = None; + if let Some(dwp) = self.dwarf_package.as_ref() { + if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) { + r = Some(Arc::new(cu)); + } + } + + if r.is_none() { + let mut path = PathBuf::new(); + if let Some(p) = load.comp_dir.as_ref() { + if let Ok(p) = convert_path(p) { + path.push(p); + } + } + + if let Some(p) = load.path.as_ref() { + if let Ok(p) = convert_path(p) { + path.push(p); + } + } + + if let Ok(file) = File::open(&path) { + if let Ok(map) = unsafe { memmap2::Mmap::map(&file) } { + if let Ok(file) = object::File::parse(&*map) { + let endian = if file.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + r = gimli::Dwarf::load(|id| { + load_section(id, &file, endian, &mut self.loader) + }) + .ok() + .map(|mut dwo_dwarf| { + dwo_dwarf.make_dwo(&load.parent); + Arc::new(dwo_dwarf) + }); + } + } + } + } + + l = continuation.resume(r); + } + } +} diff --git a/vendor/addr2line/src/function.rs b/vendor/addr2line/src/function.rs new file mode 100644 index 0000000..09c19e0 --- /dev/null +++ b/vendor/addr2line/src/function.rs @@ -0,0 +1,555 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::iter; + +use crate::lazy::LazyCell; +use crate::maybe_small; +use crate::{Context, DebugFile, Error, RangeAttributes}; + +pub(crate) struct Functions<R: gimli::Reader> { + /// List of all `DW_TAG_subprogram` details in the unit. + pub(crate) functions: Box< + [( + gimli::UnitOffset<R::Offset>, + LazyCell<Result<Function<R>, Error>>, + )], + >, + /// List of `DW_TAG_subprogram` address ranges in the unit. + pub(crate) addresses: Box<[FunctionAddress]>, +} + +/// A single address range for a function. +/// +/// It is possible for a function to have multiple address ranges; this +/// is handled by having multiple `FunctionAddress` entries with the same +/// `function` field. +pub(crate) struct FunctionAddress { + range: gimli::Range, + /// An index into `Functions::functions`. + pub(crate) function: usize, +} + +pub(crate) struct Function<R: gimli::Reader> { + pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>, + pub(crate) name: Option<R>, + /// List of all `DW_TAG_inlined_subroutine` details in this function. + inlined_functions: Box<[InlinedFunction<R>]>, + /// List of `DW_TAG_inlined_subroutine` address ranges in this function. + inlined_addresses: Box<[InlinedFunctionAddress]>, +} + +pub(crate) struct InlinedFunctionAddress { + range: gimli::Range, + call_depth: usize, + /// An index into `Function::inlined_functions`. + function: usize, +} + +pub(crate) struct InlinedFunction<R: gimli::Reader> { + pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>, + pub(crate) name: Option<R>, + pub(crate) call_file: Option<u64>, + pub(crate) call_line: u32, + pub(crate) call_column: u32, +} + +impl<R: gimli::Reader> Functions<R> { + pub(crate) fn parse( + unit: &gimli::Unit<R>, + sections: &gimli::Dwarf<R>, + ) -> Result<Functions<R>, Error> { + let mut functions = Vec::new(); + let mut addresses = Vec::new(); + let mut entries = unit.entries_raw(None)?; + while !entries.is_empty() { + let dw_die_offset = entries.next_offset(); + if let Some(abbrev) = entries.read_abbreviation()? { + if abbrev.tag() == gimli::DW_TAG_subprogram { + let mut ranges = RangeAttributes::default(); + for spec in abbrev.attributes() { + match entries.read_attribute(*spec) { + Ok(ref attr) => { + match attr.name() { + gimli::DW_AT_low_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => { + ranges.low_pc = Some(val) + } + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.low_pc = Some(sections.address(unit, index)?); + } + _ => {} + }, + gimli::DW_AT_high_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => { + ranges.high_pc = Some(val) + } + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.high_pc = Some(sections.address(unit, index)?); + } + gimli::AttributeValue::Udata(val) => { + ranges.size = Some(val) + } + _ => {} + }, + gimli::DW_AT_ranges => { + ranges.ranges_offset = + sections.attr_ranges_offset(unit, attr.value())?; + } + _ => {} + }; + } + Err(e) => return Err(e), + } + } + + let function_index = functions.len(); + if ranges.for_each_range(sections, unit, |range| { + addresses.push(FunctionAddress { + range, + function: function_index, + }); + })? { + functions.push((dw_die_offset, LazyCell::new())); + } + } else { + entries.skip_attributes(abbrev.attributes())?; + } + } + } + + // The binary search requires the addresses to be sorted. + // + // It also requires them to be non-overlapping. In practice, overlapping + // function ranges are unlikely, so we don't try to handle that yet. + // + // It's possible for multiple functions to have the same address range if the + // compiler can detect and remove functions with identical code. In that case + // we'll nondeterministically return one of them. + addresses.sort_by_key(|x| x.range.begin); + + Ok(Functions { + functions: functions.into_boxed_slice(), + addresses: addresses.into_boxed_slice(), + }) + } + + pub(crate) fn find_address(&self, probe: u64) -> Option<usize> { + self.addresses + .binary_search_by(|address| { + if probe < address.range.begin { + Ordering::Greater + } else if probe >= address.range.end { + Ordering::Less + } else { + Ordering::Equal + } + }) + .ok() + } + + pub(crate) fn parse_inlined_functions( + &self, + file: DebugFile, + unit: &gimli::Unit<R>, + ctx: &Context<R>, + sections: &gimli::Dwarf<R>, + ) -> Result<(), Error> { + for function in &*self.functions { + function + .1 + .borrow_with(|| Function::parse(function.0, file, unit, ctx, sections)) + .as_ref() + .map_err(Error::clone)?; + } + Ok(()) + } +} + +impl<R: gimli::Reader> Function<R> { + pub(crate) fn parse( + dw_die_offset: gimli::UnitOffset<R::Offset>, + file: DebugFile, + unit: &gimli::Unit<R>, + ctx: &Context<R>, + sections: &gimli::Dwarf<R>, + ) -> Result<Self, Error> { + let mut entries = unit.entries_raw(Some(dw_die_offset))?; + let depth = entries.next_depth(); + let abbrev = entries.read_abbreviation()?.unwrap(); + debug_assert_eq!(abbrev.tag(), gimli::DW_TAG_subprogram); + + let mut name = None; + for spec in abbrev.attributes() { + match entries.read_attribute(*spec) { + Ok(ref attr) => { + match attr.name() { + gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { + if let Ok(val) = sections.attr_string(unit, attr.value()) { + name = Some(val); + } + } + gimli::DW_AT_name => { + if name.is_none() { + name = sections.attr_string(unit, attr.value()).ok(); + } + } + gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { + if name.is_none() { + name = name_attr(attr.value(), file, unit, ctx, sections, 16)?; + } + } + _ => {} + }; + } + Err(e) => return Err(e), + } + } + + let mut inlined_functions = Vec::new(); + let mut inlined_addresses = Vec::new(); + Function::parse_children( + &mut entries, + depth, + file, + unit, + ctx, + sections, + &mut inlined_functions, + &mut inlined_addresses, + 0, + )?; + + // Sort ranges in "breadth-first traversal order", i.e. first by call_depth + // and then by range.begin. This allows finding the range containing an + // address at a certain depth using binary search. + // Note: Using DFS order, i.e. ordering by range.begin first and then by + // call_depth, would not work! Consider the two examples + // "[0..10 at depth 0], [0..2 at depth 1], [6..8 at depth 1]" and + // "[0..5 at depth 0], [0..2 at depth 1], [5..10 at depth 0], [6..8 at depth 1]". + // In this example, if you want to look up address 7 at depth 0, and you + // encounter [0..2 at depth 1], are you before or after the target range? + // You don't know. + inlined_addresses.sort_by(|r1, r2| { + if r1.call_depth < r2.call_depth { + Ordering::Less + } else if r1.call_depth > r2.call_depth { + Ordering::Greater + } else if r1.range.begin < r2.range.begin { + Ordering::Less + } else if r1.range.begin > r2.range.begin { + Ordering::Greater + } else { + Ordering::Equal + } + }); + + Ok(Function { + dw_die_offset, + name, + inlined_functions: inlined_functions.into_boxed_slice(), + inlined_addresses: inlined_addresses.into_boxed_slice(), + }) + } + + fn parse_children( + entries: &mut gimli::EntriesRaw<'_, '_, R>, + depth: isize, + file: DebugFile, + unit: &gimli::Unit<R>, + ctx: &Context<R>, + sections: &gimli::Dwarf<R>, + inlined_functions: &mut Vec<InlinedFunction<R>>, + inlined_addresses: &mut Vec<InlinedFunctionAddress>, + inlined_depth: usize, + ) -> Result<(), Error> { + loop { + let dw_die_offset = entries.next_offset(); + let next_depth = entries.next_depth(); + if next_depth <= depth { + return Ok(()); + } + if let Some(abbrev) = entries.read_abbreviation()? { + match abbrev.tag() { + gimli::DW_TAG_subprogram => { + Function::skip(entries, abbrev, next_depth)?; + } + gimli::DW_TAG_inlined_subroutine => { + InlinedFunction::parse( + dw_die_offset, + entries, + abbrev, + next_depth, + file, + unit, + ctx, + sections, + inlined_functions, + inlined_addresses, + inlined_depth, + )?; + } + _ => { + entries.skip_attributes(abbrev.attributes())?; + } + } + } + } + } + + fn skip( + entries: &mut gimli::EntriesRaw<'_, '_, R>, + abbrev: &gimli::Abbreviation, + depth: isize, + ) -> Result<(), Error> { + // TODO: use DW_AT_sibling + entries.skip_attributes(abbrev.attributes())?; + while entries.next_depth() > depth { + if let Some(abbrev) = entries.read_abbreviation()? { + entries.skip_attributes(abbrev.attributes())?; + } + } + Ok(()) + } + + /// Build the list of inlined functions that contain `probe`. + pub(crate) fn find_inlined_functions( + &self, + probe: u64, + ) -> iter::Rev<maybe_small::IntoIter<&InlinedFunction<R>>> { + // `inlined_functions` is ordered from outside to inside. + let mut inlined_functions = maybe_small::Vec::new(); + let mut inlined_addresses = &self.inlined_addresses[..]; + loop { + let current_depth = inlined_functions.len(); + // Look up (probe, current_depth) in inline_ranges. + // `inlined_addresses` is sorted in "breadth-first traversal order", i.e. + // by `call_depth` first, and then by `range.begin`. See the comment at + // the sort call for more information about why. + let search = inlined_addresses.binary_search_by(|range| { + if range.call_depth > current_depth { + Ordering::Greater + } else if range.call_depth < current_depth { + Ordering::Less + } else if range.range.begin > probe { + Ordering::Greater + } else if range.range.end <= probe { + Ordering::Less + } else { + Ordering::Equal + } + }); + if let Ok(index) = search { + let function_index = inlined_addresses[index].function; + inlined_functions.push(&self.inlined_functions[function_index]); + inlined_addresses = &inlined_addresses[index + 1..]; + } else { + break; + } + } + inlined_functions.into_iter().rev() + } +} + +impl<R: gimli::Reader> InlinedFunction<R> { + fn parse( + dw_die_offset: gimli::UnitOffset<R::Offset>, + entries: &mut gimli::EntriesRaw<'_, '_, R>, + abbrev: &gimli::Abbreviation, + depth: isize, + file: DebugFile, + unit: &gimli::Unit<R>, + ctx: &Context<R>, + sections: &gimli::Dwarf<R>, + inlined_functions: &mut Vec<InlinedFunction<R>>, + inlined_addresses: &mut Vec<InlinedFunctionAddress>, + inlined_depth: usize, + ) -> Result<(), Error> { + let mut ranges = RangeAttributes::default(); + let mut name = None; + let mut call_file = None; + let mut call_line = 0; + let mut call_column = 0; + for spec in abbrev.attributes() { + match entries.read_attribute(*spec) { + Ok(ref attr) => match attr.name() { + gimli::DW_AT_low_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.low_pc = Some(sections.address(unit, index)?); + } + _ => {} + }, + gimli::DW_AT_high_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.high_pc = Some(sections.address(unit, index)?); + } + gimli::AttributeValue::Udata(val) => ranges.size = Some(val), + _ => {} + }, + gimli::DW_AT_ranges => { + ranges.ranges_offset = sections.attr_ranges_offset(unit, attr.value())?; + } + gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { + if let Ok(val) = sections.attr_string(unit, attr.value()) { + name = Some(val); + } + } + gimli::DW_AT_name => { + if name.is_none() { + name = sections.attr_string(unit, attr.value()).ok(); + } + } + gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { + if name.is_none() { + name = name_attr(attr.value(), file, unit, ctx, sections, 16)?; + } + } + gimli::DW_AT_call_file => { + // There is a spec issue [1] with how DW_AT_call_file is specified in DWARF 5. + // Before, a file index of 0 would indicate no source file, however in + // DWARF 5 this could be a valid index into the file table. + // + // Implementations such as LLVM generates a file index of 0 when DWARF 5 is + // used. + // + // Thus, if we see a version of 5 or later, treat a file index of 0 as such. + // [1]: http://wiki.dwarfstd.org/index.php?title=DWARF5_Line_Table_File_Numbers + if let gimli::AttributeValue::FileIndex(fi) = attr.value() { + if fi > 0 || unit.header.version() >= 5 { + call_file = Some(fi); + } + } + } + gimli::DW_AT_call_line => { + call_line = attr.udata_value().unwrap_or(0) as u32; + } + gimli::DW_AT_call_column => { + call_column = attr.udata_value().unwrap_or(0) as u32; + } + _ => {} + }, + Err(e) => return Err(e), + } + } + + let function_index = inlined_functions.len(); + inlined_functions.push(InlinedFunction { + dw_die_offset, + name, + call_file, + call_line, + call_column, + }); + + ranges.for_each_range(sections, unit, |range| { + inlined_addresses.push(InlinedFunctionAddress { + range, + call_depth: inlined_depth, + function: function_index, + }); + })?; + + Function::parse_children( + entries, + depth, + file, + unit, + ctx, + sections, + inlined_functions, + inlined_addresses, + inlined_depth + 1, + ) + } +} + +fn name_attr<R>( + attr: gimli::AttributeValue<R>, + mut file: DebugFile, + unit: &gimli::Unit<R>, + ctx: &Context<R>, + sections: &gimli::Dwarf<R>, + recursion_limit: usize, +) -> Result<Option<R>, Error> +where + R: gimli::Reader, +{ + if recursion_limit == 0 { + return Ok(None); + } + + match attr { + gimli::AttributeValue::UnitRef(offset) => { + name_entry(file, unit, offset, ctx, sections, recursion_limit) + } + gimli::AttributeValue::DebugInfoRef(dr) => { + let (unit, offset) = ctx.find_unit(dr, file)?; + name_entry(file, unit, offset, ctx, sections, recursion_limit) + } + gimli::AttributeValue::DebugInfoRefSup(dr) => { + if let Some(sup_sections) = sections.sup.as_ref() { + file = DebugFile::Supplementary; + let (unit, offset) = ctx.find_unit(dr, file)?; + name_entry(file, unit, offset, ctx, sup_sections, recursion_limit) + } else { + Ok(None) + } + } + _ => Ok(None), + } +} + +fn name_entry<R>( + file: DebugFile, + unit: &gimli::Unit<R>, + offset: gimli::UnitOffset<R::Offset>, + ctx: &Context<R>, + sections: &gimli::Dwarf<R>, + recursion_limit: usize, +) -> Result<Option<R>, Error> +where + R: gimli::Reader, +{ + let mut entries = unit.entries_raw(Some(offset))?; + let abbrev = if let Some(abbrev) = entries.read_abbreviation()? { + abbrev + } else { + return Err(gimli::Error::NoEntryAtGivenOffset); + }; + + let mut name = None; + let mut next = None; + for spec in abbrev.attributes() { + match entries.read_attribute(*spec) { + Ok(ref attr) => match attr.name() { + gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { + if let Ok(val) = sections.attr_string(unit, attr.value()) { + return Ok(Some(val)); + } + } + gimli::DW_AT_name => { + if let Ok(val) = sections.attr_string(unit, attr.value()) { + name = Some(val); + } + } + gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { + next = Some(attr.value()); + } + _ => {} + }, + Err(e) => return Err(e), + } + } + + if name.is_some() { + return Ok(name); + } + + if let Some(next) = next { + return name_attr(next, file, unit, ctx, sections, recursion_limit - 1); + } + + Ok(None) +} diff --git a/vendor/addr2line/src/lazy.rs b/vendor/addr2line/src/lazy.rs new file mode 100644 index 0000000..2df2ed6 --- /dev/null +++ b/vendor/addr2line/src/lazy.rs @@ -0,0 +1,31 @@ +use core::cell::UnsafeCell; + +pub struct LazyCell<T> { + contents: UnsafeCell<Option<T>>, +} +impl<T> LazyCell<T> { + pub fn new() -> LazyCell<T> { + LazyCell { + contents: UnsafeCell::new(None), + } + } + + pub fn borrow(&self) -> Option<&T> { + unsafe { &*self.contents.get() }.as_ref() + } + + pub fn borrow_with(&self, closure: impl FnOnce() -> T) -> &T { + // First check if we're already initialized... + let ptr = self.contents.get(); + if let Some(val) = unsafe { &*ptr } { + return val; + } + // Note that while we're executing `closure` our `borrow_with` may + // be called recursively. This means we need to check again after + // the closure has executed. For that we use the `get_or_insert` + // method which will only perform mutation if we aren't already + // `Some`. + let val = closure(); + unsafe { (*ptr).get_or_insert(val) } + } +} diff --git a/vendor/addr2line/src/lib.rs b/vendor/addr2line/src/lib.rs new file mode 100644 index 0000000..3270aad --- /dev/null +++ b/vendor/addr2line/src/lib.rs @@ -0,0 +1,1729 @@ +//! This crate provides a cross-platform library and binary for translating addresses into +//! function names, file names and line numbers. Given an address in an executable or an +//! offset in a section of a relocatable object, it uses the debugging information to +//! figure out which file name and line number are associated with it. +//! +//! When used as a library, files must first be loaded using the +//! [`object`](https://github.com/gimli-rs/object) crate. +//! A context can then be created with [`Context::new`](./struct.Context.html#method.new). +//! The context caches some of the parsed information so that multiple lookups are +//! efficient. +//! Location information is obtained with +//! [`Context::find_location`](./struct.Context.html#method.find_location) or +//! [`Context::find_location_range`](./struct.Context.html#method.find_location_range). +//! Function information is obtained with +//! [`Context::find_frames`](./struct.Context.html#method.find_frames), which returns +//! a frame for each inline function. Each frame contains both name and location. +//! +//! The crate has an example CLI wrapper around the library which provides some of +//! the functionality of the `addr2line` command line tool distributed with [GNU +//! binutils](https://www.gnu.org/software/binutils/). +//! +//! Currently this library only provides information from the DWARF debugging information, +//! which is parsed using [`gimli`](https://github.com/gimli-rs/gimli). The example CLI +//! wrapper also uses symbol table information provided by the `object` crate. +#![deny(missing_docs)] +#![no_std] + +#[cfg(feature = "std")] +extern crate std; + +#[allow(unused_imports)] +#[macro_use] +extern crate alloc; + +#[cfg(feature = "fallible-iterator")] +pub extern crate fallible_iterator; +pub extern crate gimli; +#[cfg(feature = "object")] +pub extern crate object; + +use alloc::borrow::Cow; +use alloc::boxed::Box; +#[cfg(feature = "object")] +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use core::cmp::{self, Ordering}; +use core::iter; +use core::marker::PhantomData; +use core::mem; +use core::num::NonZeroU64; +use core::ops::ControlFlow; +use core::u64; + +use crate::function::{Function, Functions, InlinedFunction}; +use crate::lazy::LazyCell; + +#[cfg(feature = "smallvec")] +mod maybe_small { + pub type Vec<T> = smallvec::SmallVec<[T; 16]>; + pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>; +} +#[cfg(not(feature = "smallvec"))] +mod maybe_small { + pub type Vec<T> = alloc::vec::Vec<T>; + pub type IntoIter<T> = alloc::vec::IntoIter<T>; +} + +#[cfg(all(feature = "std", feature = "object", feature = "memmap2"))] +/// A simple builtin split DWARF loader. +pub mod builtin_split_dwarf_loader; +mod function; +mod lazy; + +type Error = gimli::Error; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum DebugFile { + Primary, + Supplementary, + Dwo, +} + +/// Operations that consult debug information may require additional files +/// to be loaded if split DWARF is being used. This enum returns the result +/// of the operation in the `Break` variant, or information about the split +/// DWARF that is required and a continuation to invoke once it is available +/// in the `Continue` variant. +/// +/// This enum is intended to be used in a loop like so: +/// ```no_run +/// # use addr2line::*; +/// # use std::sync::Arc; +/// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!(); +/// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None }; +/// const ADDRESS: u64 = 0xdeadbeef; +/// let mut r = ctx.find_frames(ADDRESS); +/// let result = loop { +/// match r { +/// LookupResult::Output(result) => break result, +/// LookupResult::Load { load, continuation } => { +/// let dwo = do_split_dwarf_load(load); +/// r = continuation.resume(dwo); +/// } +/// } +/// }; +/// ``` +pub enum LookupResult<L: LookupContinuation> { + /// The lookup requires split DWARF data to be loaded. + Load { + /// The information needed to find the split DWARF data. + load: SplitDwarfLoad<<L as LookupContinuation>::Buf>, + /// The continuation to resume with the loaded split DWARF data. + continuation: L, + }, + /// The lookup has completed and produced an output. + Output(<L as LookupContinuation>::Output), +} + +/// This trait represents a partially complete operation that can be resumed +/// once a load of needed split DWARF data is completed or abandoned by the +/// API consumer. +pub trait LookupContinuation: Sized { + /// The final output of this operation. + type Output; + /// The type of reader used. + type Buf: gimli::Reader; + + /// Resumes the operation with the provided data. + /// + /// After the caller loads the split DWARF data required, call this + /// method to resume the operation. The return value of this method + /// indicates if the computation has completed or if further data is + /// required. + /// + /// If the additional data cannot be located, or the caller does not + /// support split DWARF, `resume(None)` can be used to continue the + /// operation with the data that is available. + fn resume(self, input: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self>; +} + +impl<L: LookupContinuation> LookupResult<L> { + /// Callers that do not handle split DWARF can call `skip_all_loads` + /// to fast-forward to the end result. This result is produced with + /// the data that is available and may be less accurate than the + /// the results that would be produced if the caller did properly + /// support split DWARF. + pub fn skip_all_loads(mut self) -> L::Output { + loop { + self = match self { + LookupResult::Output(t) => return t, + LookupResult::Load { continuation, .. } => continuation.resume(None), + }; + } + } + + fn map<T, F: FnOnce(L::Output) -> T>(self, f: F) -> LookupResult<MappedLookup<T, L, F>> { + match self { + LookupResult::Output(t) => LookupResult::Output(f(t)), + LookupResult::Load { load, continuation } => LookupResult::Load { + load, + continuation: MappedLookup { + original: continuation, + mutator: f, + }, + }, + } + } + + fn unwrap(self) -> L::Output { + match self { + LookupResult::Output(t) => t, + LookupResult::Load { .. } => unreachable!("Internal API misuse"), + } + } +} + +/// The state necessary to perform address to line translation. +/// +/// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s +/// when performing lookups for many addresses in the same executable. +pub struct Context<R: gimli::Reader> { + sections: Arc<gimli::Dwarf<R>>, + unit_ranges: Box<[UnitRange]>, + units: Box<[ResUnit<R>]>, + sup_units: Box<[SupUnit<R>]>, +} + +/// The type of `Context` that supports the `new` method. +#[cfg(feature = "std-object")] +pub type ObjectContext = Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>; + +#[cfg(feature = "std-object")] +impl Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> { + /// Construct a new `Context`. + /// + /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`. + /// This means it is not thread safe, has no lifetime constraints (since it copies + /// the input data), and works for any endianity. + /// + /// Performance sensitive applications may want to use `Context::from_dwarf` + /// with a more specialised `gimli::Reader` implementation. + #[inline] + pub fn new<'data: 'file, 'file, O: object::Object<'data, 'file>>( + file: &'file O, + ) -> Result<Self, Error> { + Self::new_with_sup(file, None) + } + + /// Construct a new `Context`. + /// + /// Optionally also use a supplementary object file. + /// + /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`. + /// This means it is not thread safe, has no lifetime constraints (since it copies + /// the input data), and works for any endianity. + /// + /// Performance sensitive applications may want to use `Context::from_dwarf` + /// with a more specialised `gimli::Reader` implementation. + pub fn new_with_sup<'data: 'file, 'file, O: object::Object<'data, 'file>>( + file: &'file O, + sup_file: Option<&'file O>, + ) -> Result<Self, Error> { + let endian = if file.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + fn load_section<'data: 'file, 'file, O, Endian>( + id: gimli::SectionId, + file: &'file O, + endian: Endian, + ) -> Result<gimli::EndianRcSlice<Endian>, Error> + where + O: object::Object<'data, 'file>, + Endian: gimli::Endianity, + { + use object::ObjectSection; + + let data = file + .section_by_name(id.name()) + .and_then(|section| section.uncompressed_data().ok()) + .unwrap_or(Cow::Borrowed(&[])); + Ok(gimli::EndianRcSlice::new(Rc::from(&*data), endian)) + } + + let mut dwarf = gimli::Dwarf::load(|id| load_section(id, file, endian))?; + if let Some(sup_file) = sup_file { + dwarf.load_sup(|id| load_section(id, sup_file, endian))?; + } + Context::from_dwarf(dwarf) + } +} + +impl<R: gimli::Reader> Context<R> { + /// Construct a new `Context` from DWARF sections. + /// + /// This method does not support using a supplementary object file. + pub fn from_sections( + debug_abbrev: gimli::DebugAbbrev<R>, + debug_addr: gimli::DebugAddr<R>, + debug_aranges: gimli::DebugAranges<R>, + debug_info: gimli::DebugInfo<R>, + debug_line: gimli::DebugLine<R>, + debug_line_str: gimli::DebugLineStr<R>, + debug_ranges: gimli::DebugRanges<R>, + debug_rnglists: gimli::DebugRngLists<R>, + debug_str: gimli::DebugStr<R>, + debug_str_offsets: gimli::DebugStrOffsets<R>, + default_section: R, + ) -> Result<Self, Error> { + Self::from_dwarf(gimli::Dwarf { + debug_abbrev, + debug_addr, + debug_aranges, + debug_info, + debug_line, + debug_line_str, + debug_str, + debug_str_offsets, + debug_types: default_section.clone().into(), + locations: gimli::LocationLists::new( + default_section.clone().into(), + default_section.into(), + ), + ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists), + file_type: gimli::DwarfFileType::Main, + sup: None, + abbreviations_cache: gimli::AbbreviationsCache::new(), + }) + } + + /// Construct a new `Context` from an existing [`gimli::Dwarf`] object. + #[inline] + pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> { + let sections = Arc::new(sections); + let (unit_ranges, units) = Context::parse_units(§ions)?; + let sup_units = if let Some(sup) = sections.sup.as_ref() { + Context::parse_sup(sup)? + } else { + Vec::new() + }; + Ok(Context { + sections, + unit_ranges: unit_ranges.into_boxed_slice(), + units: units.into_boxed_slice(), + sup_units: sup_units.into_boxed_slice(), + }) + } + + /// Finds the CUs for the function address given. + /// + /// There might be multiple CUs whose range contains this address. + /// Weak symbols have shown up in the wild which cause this to happen + /// but otherwise this can happen if the CU has non-contiguous functions + /// but only reports a single range. + /// + /// Consequently we return an iterator for all CUs which may contain the + /// address, and the caller must check if there is actually a function or + /// location in the CU for that address. + fn find_units(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> { + self.find_units_range(probe, probe + 1) + .map(|(unit, _range)| unit) + } + + /// Finds the CUs covering the range of addresses given. + /// + /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple + /// ranges for the same unit. + #[inline] + fn find_units_range( + &self, + probe_low: u64, + probe_high: u64, + ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> { + // First up find the position in the array which could have our function + // address. + let pos = match self + .unit_ranges + .binary_search_by_key(&probe_high, |i| i.range.begin) + { + // Although unlikely, we could find an exact match. + Ok(i) => i + 1, + // No exact match was found, but this probe would fit at slot `i`. + // This means that slot `i` is bigger than `probe`, along with all + // indices greater than `i`, so we need to search all previous + // entries. + Err(i) => i, + }; + + // Once we have our index we iterate backwards from that position + // looking for a matching CU. + self.unit_ranges[..pos] + .iter() + .rev() + .take_while(move |i| { + // We know that this CU's start is beneath the probe already because + // of our sorted array. + debug_assert!(i.range.begin <= probe_high); + + // Each entry keeps track of the maximum end address seen so far, + // starting from the beginning of the array of unit ranges. We're + // iterating in reverse so if our probe is beyond the maximum range + // of this entry, then it's guaranteed to not fit in any prior + // entries, so we break out. + probe_low < i.max_end + }) + .filter_map(move |i| { + // If this CU doesn't actually contain this address, move to the + // next CU. + if probe_low >= i.range.end || probe_high <= i.range.begin { + return None; + } + Some((&self.units[i.unit_id], &i.range)) + }) + } + + /// Find the DWARF unit corresponding to the given virtual memory address. + pub fn find_dwarf_and_unit( + &self, + probe: u64, + ) -> LookupResult< + impl LookupContinuation<Output = Option<(&gimli::Dwarf<R>, &gimli::Unit<R>)>, Buf = R>, + > { + let mut units_iter = self.find_units(probe); + if let Some(unit) = units_iter.next() { + return LoopingLookup::new_lookup( + unit.find_function_or_location(probe, self), + move |r| { + ControlFlow::Break(match r { + Ok((Some(_), _)) | Ok((_, Some(_))) => { + let (_file, sections, unit) = unit + .dwarf_and_unit_dwo(self) + // We've already been through both error cases here to get to this point. + .unwrap() + .unwrap(); + Some((sections, unit)) + } + _ => match units_iter.next() { + Some(next_unit) => { + return ControlFlow::Continue( + next_unit.find_function_or_location(probe, self), + ); + } + None => None, + }, + }) + }, + ); + } + + LoopingLookup::new_complete(None) + } + + /// Find the source file and line corresponding to the given virtual memory address. + pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> { + for unit in self.find_units(probe) { + if let Some(location) = unit.find_location(probe, &self.sections)? { + return Ok(Some(location)); + } + } + Ok(None) + } + + /// Return source file and lines for a range of addresses. For each location it also + /// returns the address and size of the range of the underlying instructions. + pub fn find_location_range( + &self, + probe_low: u64, + probe_high: u64, + ) -> Result<LocationRangeIter<'_, R>, Error> { + LocationRangeIter::new(self, probe_low, probe_high) + } + + /// Return an iterator for the function frames corresponding to the given virtual + /// memory address. + /// + /// If the probe address is not for an inline function then only one frame is + /// returned. + /// + /// If the probe address is for an inline function then the first frame corresponds + /// to the innermost inline function. Subsequent frames contain the caller and call + /// location, until an non-inline caller is reached. + pub fn find_frames( + &self, + probe: u64, + ) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>> + { + let mut units_iter = self.find_units(probe); + if let Some(unit) = units_iter.next() { + LoopingLookup::new_lookup(unit.find_function_or_location(probe, self), move |r| { + ControlFlow::Break(match r { + Err(e) => Err(e), + Ok((Some(function), location)) => { + let inlined_functions = function.find_inlined_functions(probe); + Ok(FrameIter(FrameIterState::Frames(FrameIterFrames { + unit, + sections: &self.sections, + function, + inlined_functions, + next: location, + }))) + } + Ok((None, Some(location))) => { + Ok(FrameIter(FrameIterState::Location(Some(location)))) + } + Ok((None, None)) => match units_iter.next() { + Some(next_unit) => { + return ControlFlow::Continue( + next_unit.find_function_or_location(probe, self), + ); + } + None => Ok(FrameIter(FrameIterState::Empty)), + }, + }) + }) + } else { + LoopingLookup::new_complete(Ok(FrameIter(FrameIterState::Empty))) + } + } + + /// Preload units for `probe`. + /// + /// The iterator returns pairs of `SplitDwarfLoad`s containing the + /// information needed to locate and load split DWARF for `probe` and + /// a matching callback to invoke once that data is available. + /// + /// If this method is called, and all of the returned closures are invoked, + /// addr2line guarantees that any future API call for the address `probe` + /// will not require the loading of any split DWARF. + /// + /// ```no_run + /// # use addr2line::*; + /// # use std::sync::Arc; + /// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!(); + /// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None }; + /// const ADDRESS: u64 = 0xdeadbeef; + /// ctx.preload_units(ADDRESS).for_each(|(load, callback)| { + /// let dwo = do_split_dwarf_load(load); + /// callback(dwo); + /// }); + /// + /// let frames_iter = match ctx.find_frames(ADDRESS) { + /// LookupResult::Output(result) => result, + /// LookupResult::Load { .. } => unreachable!("addr2line promised we wouldn't get here"), + /// }; + /// + /// // ... + /// ``` + pub fn preload_units( + &'_ self, + probe: u64, + ) -> impl Iterator< + Item = ( + SplitDwarfLoad<R>, + impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_, + ), + > { + self.find_units(probe) + .filter_map(move |unit| match unit.dwarf_and_unit_dwo(self) { + LookupResult::Output(_) => None, + LookupResult::Load { load, continuation } => Some((load, |result| { + continuation.resume(result).unwrap().map(|_| ()) + })), + }) + } + + /// Initialize all line data structures. This is used for benchmarks. + #[doc(hidden)] + pub fn parse_lines(&self) -> Result<(), Error> { + for unit in self.units.iter() { + unit.parse_lines(&self.sections)?; + } + Ok(()) + } + + /// Initialize all function data structures. This is used for benchmarks. + #[doc(hidden)] + pub fn parse_functions(&self) -> Result<(), Error> { + for unit in self.units.iter() { + unit.parse_functions(self).skip_all_loads()?; + } + Ok(()) + } + + /// Initialize all inlined function data structures. This is used for benchmarks. + #[doc(hidden)] + pub fn parse_inlined_functions(&self) -> Result<(), Error> { + for unit in self.units.iter() { + unit.parse_inlined_functions(self).skip_all_loads()?; + } + Ok(()) + } +} + +struct UnitRange { + unit_id: usize, + max_end: u64, + range: gimli::Range, +} + +struct ResUnit<R: gimli::Reader> { + offset: gimli::DebugInfoOffset<R::Offset>, + dw_unit: gimli::Unit<R>, + lang: Option<gimli::DwLang>, + lines: LazyCell<Result<Lines, Error>>, + funcs: LazyCell<Result<Functions<R>, Error>>, + dwo: LazyCell<Result<Option<Box<(Arc<gimli::Dwarf<R>>, gimli::Unit<R>)>>, Error>>, +} + +struct SupUnit<R: gimli::Reader> { + offset: gimli::DebugInfoOffset<R::Offset>, + dw_unit: gimli::Unit<R>, +} + +impl<R: gimli::Reader> Context<R> { + fn parse_units(sections: &gimli::Dwarf<R>) -> Result<(Vec<UnitRange>, Vec<ResUnit<R>>), Error> { + // Find all the references to compilation units in .debug_aranges. + // Note that we always also iterate through all of .debug_info to + // find compilation units, because .debug_aranges may be missing some. + let mut aranges = Vec::new(); + let mut headers = sections.debug_aranges.headers(); + while let Some(header) = headers.next()? { + aranges.push((header.debug_info_offset(), header.offset())); + } + aranges.sort_by_key(|i| i.0); + + let mut unit_ranges = Vec::new(); + let mut res_units = Vec::new(); + let mut units = sections.units(); + while let Some(header) = units.next()? { + let unit_id = res_units.len(); + let offset = match header.offset().as_debug_info_offset() { + Some(offset) => offset, + None => continue, + }; + // We mainly want compile units, but we may need to follow references to entries + // within other units for function names. We don't need anything from type units. + match header.type_() { + gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue, + _ => {} + } + let dw_unit = match sections.unit(header) { + Ok(dw_unit) => dw_unit, + Err(_) => continue, + }; + + let mut lang = None; + let mut have_unit_range = false; + { + let mut entries = dw_unit.entries_raw(None)?; + + let abbrev = match entries.read_abbreviation()? { + Some(abbrev) => abbrev, + None => continue, + }; + + let mut ranges = RangeAttributes::default(); + for spec in abbrev.attributes() { + let attr = entries.read_attribute(*spec)?; + match attr.name() { + gimli::DW_AT_low_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.low_pc = Some(sections.address(&dw_unit, index)?); + } + _ => {} + }, + gimli::DW_AT_high_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.high_pc = Some(sections.address(&dw_unit, index)?); + } + gimli::AttributeValue::Udata(val) => ranges.size = Some(val), + _ => {} + }, + gimli::DW_AT_ranges => { + ranges.ranges_offset = + sections.attr_ranges_offset(&dw_unit, attr.value())?; + } + gimli::DW_AT_language => { + if let gimli::AttributeValue::Language(val) = attr.value() { + lang = Some(val); + } + } + _ => {} + } + } + + // Find the address ranges for the CU, using in order of preference: + // - DW_AT_ranges + // - .debug_aranges + // - DW_AT_low_pc/DW_AT_high_pc + // + // Using DW_AT_ranges before .debug_aranges is possibly an arbitrary choice, + // but the feeling is that DW_AT_ranges is more likely to be reliable or complete + // if it is present. + // + // .debug_aranges must be used before DW_AT_low_pc/DW_AT_high_pc because + // it has been observed on macOS that DW_AT_ranges was not emitted even for + // discontiguous CUs. + let i = match ranges.ranges_offset { + Some(_) => None, + None => aranges.binary_search_by_key(&offset, |x| x.0).ok(), + }; + if let Some(mut i) = i { + // There should be only one set per CU, but in practice multiple + // sets have been observed. This is probably a compiler bug, but + // either way we need to handle it. + while i > 0 && aranges[i - 1].0 == offset { + i -= 1; + } + for (_, aranges_offset) in aranges[i..].iter().take_while(|x| x.0 == offset) { + let aranges_header = sections.debug_aranges.header(*aranges_offset)?; + let mut aranges = aranges_header.entries(); + while let Some(arange) = aranges.next()? { + if arange.length() != 0 { + unit_ranges.push(UnitRange { + range: arange.range(), + unit_id, + max_end: 0, + }); + have_unit_range = true; + } + } + } + } else { + have_unit_range |= ranges.for_each_range(sections, &dw_unit, |range| { + unit_ranges.push(UnitRange { + range, + unit_id, + max_end: 0, + }); + })?; + } + } + + let lines = LazyCell::new(); + if !have_unit_range { + // The unit did not declare any ranges. + // Try to get some ranges from the line program sequences. + if let Some(ref ilnp) = dw_unit.line_program { + if let Ok(lines) = lines + .borrow_with(|| Lines::parse(&dw_unit, ilnp.clone(), sections)) + .as_ref() + { + for sequence in lines.sequences.iter() { + unit_ranges.push(UnitRange { + range: gimli::Range { + begin: sequence.start, + end: sequence.end, + }, + unit_id, + max_end: 0, + }) + } + } + } + } + + res_units.push(ResUnit { + offset, + dw_unit, + lang, + lines, + funcs: LazyCell::new(), + dwo: LazyCell::new(), + }); + } + + // Sort this for faster lookup in `find_unit_and_address` below. + unit_ranges.sort_by_key(|i| i.range.begin); + + // Calculate the `max_end` field now that we've determined the order of + // CUs. + let mut max = 0; + for i in unit_ranges.iter_mut() { + max = max.max(i.range.end); + i.max_end = max; + } + + Ok((unit_ranges, res_units)) + } + + fn parse_sup(sections: &gimli::Dwarf<R>) -> Result<Vec<SupUnit<R>>, Error> { + let mut sup_units = Vec::new(); + let mut units = sections.units(); + while let Some(header) = units.next()? { + let offset = match header.offset().as_debug_info_offset() { + Some(offset) => offset, + None => continue, + }; + let dw_unit = match sections.unit(header) { + Ok(dw_unit) => dw_unit, + Err(_) => continue, + }; + sup_units.push(SupUnit { dw_unit, offset }); + } + Ok(sup_units) + } + + // Find the unit containing the given offset, and convert the offset into a unit offset. + fn find_unit( + &self, + offset: gimli::DebugInfoOffset<R::Offset>, + file: DebugFile, + ) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> { + let unit = match file { + DebugFile::Primary => { + match self + .units + .binary_search_by_key(&offset.0, |unit| unit.offset.0) + { + // There is never a DIE at the unit offset or before the first unit. + Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset), + Err(i) => &self.units[i - 1].dw_unit, + } + } + DebugFile::Supplementary => { + match self + .sup_units + .binary_search_by_key(&offset.0, |unit| unit.offset.0) + { + // There is never a DIE at the unit offset or before the first unit. + Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset), + Err(i) => &self.sup_units[i - 1].dw_unit, + } + } + DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset), + }; + + let unit_offset = offset + .to_unit_offset(&unit.header) + .ok_or(gimli::Error::NoEntryAtGivenOffset)?; + Ok((unit, unit_offset)) + } +} + +struct Lines { + files: Box<[String]>, + sequences: Box<[LineSequence]>, +} + +impl Lines { + fn parse<R: gimli::Reader>( + dw_unit: &gimli::Unit<R>, + ilnp: gimli::IncompleteLineProgram<R, R::Offset>, + sections: &gimli::Dwarf<R>, + ) -> Result<Self, Error> { + let mut sequences = Vec::new(); + let mut sequence_rows = Vec::<LineRow>::new(); + let mut rows = ilnp.rows(); + while let Some((_, row)) = rows.next_row()? { + if row.end_sequence() { + if let Some(start) = sequence_rows.first().map(|x| x.address) { + let end = row.address(); + let mut rows = Vec::new(); + mem::swap(&mut rows, &mut sequence_rows); + sequences.push(LineSequence { + start, + end, + rows: rows.into_boxed_slice(), + }); + } + continue; + } + + let address = row.address(); + let file_index = row.file_index(); + let line = row.line().map(NonZeroU64::get).unwrap_or(0) as u32; + let column = match row.column() { + gimli::ColumnType::LeftEdge => 0, + gimli::ColumnType::Column(x) => x.get() as u32, + }; + + if let Some(last_row) = sequence_rows.last_mut() { + if last_row.address == address { + last_row.file_index = file_index; + last_row.line = line; + last_row.column = column; + continue; + } + } + + sequence_rows.push(LineRow { + address, + file_index, + line, + column, + }); + } + sequences.sort_by_key(|x| x.start); + + let mut files = Vec::new(); + let header = rows.header(); + match header.file(0) { + Some(file) => files.push(render_file(dw_unit, file, header, sections)?), + None => files.push(String::from("")), // DWARF version <= 4 may not have 0th index + } + let mut index = 1; + while let Some(file) = header.file(index) { + files.push(render_file(dw_unit, file, header, sections)?); + index += 1; + } + + Ok(Self { + files: files.into_boxed_slice(), + sequences: sequences.into_boxed_slice(), + }) + } +} + +fn render_file<R: gimli::Reader>( + dw_unit: &gimli::Unit<R>, + file: &gimli::FileEntry<R, R::Offset>, + header: &gimli::LineProgramHeader<R, R::Offset>, + sections: &gimli::Dwarf<R>, +) -> Result<String, gimli::Error> { + let mut path = if let Some(ref comp_dir) = dw_unit.comp_dir { + comp_dir.to_string_lossy()?.into_owned() + } else { + String::new() + }; + + // The directory index 0 is defined to correspond to the compilation unit directory. + if file.directory_index() != 0 { + if let Some(directory) = file.directory(header) { + path_push( + &mut path, + sections + .attr_string(dw_unit, directory)? + .to_string_lossy()? + .as_ref(), + ); + } + } + + path_push( + &mut path, + sections + .attr_string(dw_unit, file.path_name())? + .to_string_lossy()? + .as_ref(), + ); + + Ok(path) +} + +struct LineSequence { + start: u64, + end: u64, + rows: Box<[LineRow]>, +} + +struct LineRow { + address: u64, + file_index: u64, + line: u32, + column: u32, +} + +/// This struct contains the information needed to find split DWARF data +/// and to produce a `gimli::Dwarf<R>` for it. +pub struct SplitDwarfLoad<R> { + /// The dwo id, for looking up in a DWARF package, or for + /// verifying an unpacked dwo found on the file system + pub dwo_id: gimli::DwoId, + /// The compilation directory `path` is relative to. + pub comp_dir: Option<R>, + /// A path on the filesystem, relative to `comp_dir` to find this dwo. + pub path: Option<R>, + /// Once the split DWARF data is loaded, the loader is expected + /// to call [make_dwo(parent)](gimli::read::Dwarf::make_dwo) before + /// returning the data. + pub parent: Arc<gimli::Dwarf<R>>, +} + +struct SimpleLookup<T, R, F> +where + F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, + R: gimli::Reader, +{ + f: F, + phantom: PhantomData<(T, R)>, +} + +impl<T, R, F> SimpleLookup<T, R, F> +where + F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, + R: gimli::Reader, +{ + fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> { + LookupResult::Output(t) + } + + fn new_needs_load(load: SplitDwarfLoad<R>, f: F) -> LookupResult<SimpleLookup<T, R, F>> { + LookupResult::Load { + load, + continuation: SimpleLookup { + f, + phantom: PhantomData, + }, + } + } +} + +impl<T, R, F> LookupContinuation for SimpleLookup<T, R, F> +where + F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, + R: gimli::Reader, +{ + type Output = T; + type Buf = R; + + fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { + LookupResult::Output((self.f)(v)) + } +} + +struct MappedLookup<T, L, F> +where + L: LookupContinuation, + F: FnOnce(L::Output) -> T, +{ + original: L, + mutator: F, +} + +impl<T, L, F> LookupContinuation for MappedLookup<T, L, F> +where + L: LookupContinuation, + F: FnOnce(L::Output) -> T, +{ + type Output = T; + type Buf = L::Buf; + + fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { + match self.original.resume(v) { + LookupResult::Output(t) => LookupResult::Output((self.mutator)(t)), + LookupResult::Load { load, continuation } => LookupResult::Load { + load, + continuation: MappedLookup { + original: continuation, + mutator: self.mutator, + }, + }, + } + } +} + +/// Some functions (e.g. `find_frames`) require considering multiple +/// compilation units, each of which might require their own split DWARF +/// lookup (and thus produce a continuation). +/// +/// We store the underlying continuation here as well as a mutator function +/// that will either a) decide that the result of this continuation is +/// what is needed and mutate it to the final result or b) produce another +/// `LookupResult`. `new_lookup` will in turn eagerly drive any non-continuation +/// `LookupResult` with successive invocations of the mutator, until a new +/// continuation or a final result is produced. And finally, the impl of +/// `LookupContinuation::resume` will call `new_lookup` each time the +/// computation is resumed. +struct LoopingLookup<T, L, F> +where + L: LookupContinuation, + F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, +{ + continuation: L, + mutator: F, +} + +impl<T, L, F> LoopingLookup<T, L, F> +where + L: LookupContinuation, + F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, +{ + fn new_complete(t: T) -> LookupResult<Self> { + LookupResult::Output(t) + } + + fn new_lookup(mut r: LookupResult<L>, mut mutator: F) -> LookupResult<Self> { + // Drive the loop eagerly so that we only ever have to represent one state + // (the r == ControlFlow::Continue state) in LoopingLookup. + loop { + match r { + LookupResult::Output(l) => match mutator(l) { + ControlFlow::Break(t) => return LookupResult::Output(t), + ControlFlow::Continue(r2) => { + r = r2; + } + }, + LookupResult::Load { load, continuation } => { + return LookupResult::Load { + load, + continuation: LoopingLookup { + continuation, + mutator, + }, + }; + } + } + } + } +} + +impl<T, L, F> LookupContinuation for LoopingLookup<T, L, F> +where + L: LookupContinuation, + F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, +{ + type Output = T; + type Buf = L::Buf; + + fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { + let r = self.continuation.resume(v); + LoopingLookup::new_lookup(r, self.mutator) + } +} + +impl<R: gimli::Reader> ResUnit<R> { + fn dwarf_and_unit_dwo<'unit, 'ctx: 'unit>( + &'unit self, + ctx: &'ctx Context<R>, + ) -> LookupResult< + SimpleLookup< + Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>, + R, + impl FnOnce( + Option<Arc<gimli::Dwarf<R>>>, + ) + -> Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>, + >, + > { + loop { + break SimpleLookup::new_complete(match self.dwo.borrow() { + Some(Ok(Some(v))) => Ok((DebugFile::Dwo, &*v.0, &v.1)), + Some(Ok(None)) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)), + Some(Err(e)) => Err(*e), + None => { + let dwo_id = match self.dw_unit.dwo_id { + None => { + self.dwo.borrow_with(|| Ok(None)); + continue; + } + Some(dwo_id) => dwo_id, + }; + + let comp_dir = self.dw_unit.comp_dir.clone(); + + let dwo_name = self.dw_unit.dwo_name().and_then(|s| { + if let Some(s) = s { + Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?)) + } else { + Ok(None) + } + }); + + let path = match dwo_name { + Ok(v) => v, + Err(e) => { + self.dwo.borrow_with(|| Err(e)); + continue; + } + }; + + let process_dwo = move |dwo_dwarf: Option<Arc<gimli::Dwarf<R>>>| { + let dwo_dwarf = match dwo_dwarf { + None => return Ok(None), + Some(dwo_dwarf) => dwo_dwarf, + }; + let mut dwo_units = dwo_dwarf.units(); + let dwo_header = match dwo_units.next()? { + Some(dwo_header) => dwo_header, + None => return Ok(None), + }; + + let mut dwo_unit = dwo_dwarf.unit(dwo_header)?; + dwo_unit.copy_relocated_attributes(&self.dw_unit); + Ok(Some(Box::new((dwo_dwarf, dwo_unit)))) + }; + + return SimpleLookup::new_needs_load( + SplitDwarfLoad { + dwo_id, + comp_dir, + path, + parent: ctx.sections.clone(), + }, + move |dwo_dwarf| match self.dwo.borrow_with(|| process_dwo(dwo_dwarf)) { + Ok(Some(v)) => Ok((DebugFile::Dwo, &*v.0, &v.1)), + Ok(None) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)), + Err(e) => Err(*e), + }, + ); + } + }); + } + } + + fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> { + // NB: line information is always stored in the main debug file so this does not need + // to handle DWOs. + let ilnp = match self.dw_unit.line_program { + Some(ref ilnp) => ilnp, + None => return Ok(None), + }; + self.lines + .borrow_with(|| Lines::parse(&self.dw_unit, ilnp.clone(), sections)) + .as_ref() + .map(Some) + .map_err(Error::clone) + } + + fn parse_functions_dwarf_and_unit( + &self, + unit: &gimli::Unit<R>, + sections: &gimli::Dwarf<R>, + ) -> Result<&Functions<R>, Error> { + self.funcs + .borrow_with(|| Functions::parse(unit, sections)) + .as_ref() + .map_err(Error::clone) + } + + fn parse_functions<'unit, 'ctx: 'unit>( + &'unit self, + ctx: &'ctx Context<R>, + ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>> + { + self.dwarf_and_unit_dwo(ctx).map(move |r| { + let (_file, sections, unit) = r?; + self.parse_functions_dwarf_and_unit(unit, sections) + }) + } + fn parse_inlined_functions<'unit, 'ctx: 'unit>( + &'unit self, + ctx: &'ctx Context<R>, + ) -> LookupResult<impl LookupContinuation<Output = Result<(), Error>, Buf = R> + 'unit> { + self.dwarf_and_unit_dwo(ctx).map(move |r| { + let (file, sections, unit) = r?; + self.funcs + .borrow_with(|| Functions::parse(unit, sections)) + .as_ref() + .map_err(Error::clone)? + .parse_inlined_functions(file, unit, ctx, sections) + }) + } + + fn find_location( + &self, + probe: u64, + sections: &gimli::Dwarf<R>, + ) -> Result<Option<Location<'_>>, Error> { + if let Some(mut iter) = LocationRangeUnitIter::new(self, sections, probe, probe + 1)? { + match iter.next() { + None => Ok(None), + Some((_addr, _len, loc)) => Ok(Some(loc)), + } + } else { + Ok(None) + } + } + + #[inline] + fn find_location_range( + &self, + probe_low: u64, + probe_high: u64, + sections: &gimli::Dwarf<R>, + ) -> Result<Option<LocationRangeUnitIter<'_>>, Error> { + LocationRangeUnitIter::new(self, sections, probe_low, probe_high) + } + + fn find_function_or_location<'unit, 'ctx: 'unit>( + &'unit self, + probe: u64, + ctx: &'ctx Context<R>, + ) -> LookupResult< + impl LookupContinuation< + Output = Result<(Option<&'unit Function<R>>, Option<Location<'unit>>), Error>, + Buf = R, + >, + > { + self.dwarf_and_unit_dwo(ctx).map(move |r| { + let (file, sections, unit) = r?; + let functions = self.parse_functions_dwarf_and_unit(unit, sections)?; + let function = match functions.find_address(probe) { + Some(address) => { + let function_index = functions.addresses[address].function; + let (offset, ref function) = functions.functions[function_index]; + Some( + function + .borrow_with(|| Function::parse(offset, file, unit, ctx, sections)) + .as_ref() + .map_err(Error::clone)?, + ) + } + None => None, + }; + let location = self.find_location(probe, sections)?; + Ok((function, location)) + }) + } +} + +/// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`. +pub struct LocationRangeIter<'ctx, R: gimli::Reader> { + unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>, + iter: Option<LocationRangeUnitIter<'ctx>>, + + probe_low: u64, + probe_high: u64, + sections: &'ctx gimli::Dwarf<R>, +} + +impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> { + #[inline] + fn new(ctx: &'ctx Context<R>, probe_low: u64, probe_high: u64) -> Result<Self, Error> { + let sections = &ctx.sections; + let unit_iter = ctx.find_units_range(probe_low, probe_high); + Ok(Self { + unit_iter: Box::new(unit_iter), + iter: None, + probe_low, + probe_high, + sections, + }) + } + + fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> { + loop { + let iter = self.iter.take(); + match iter { + None => match self.unit_iter.next() { + Some((unit, range)) => { + self.iter = unit.find_location_range( + cmp::max(self.probe_low, range.begin), + cmp::min(self.probe_high, range.end), + self.sections, + )?; + } + None => return Ok(None), + }, + Some(mut iter) => { + if let item @ Some(_) = iter.next() { + self.iter = Some(iter); + return Ok(item); + } + } + } + } + } +} + +impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + type Item = (u64, u64, Location<'ctx>); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + match self.next_loc() { + Err(_) => None, + Ok(loc) => loc, + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + type Item = (u64, u64, Location<'ctx>); + type Error = Error; + + #[inline] + fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> { + self.next_loc() + } +} + +struct LocationRangeUnitIter<'ctx> { + lines: &'ctx Lines, + seqs: &'ctx [LineSequence], + seq_idx: usize, + row_idx: usize, + probe_high: u64, +} + +impl<'ctx> LocationRangeUnitIter<'ctx> { + fn new<R: gimli::Reader>( + resunit: &'ctx ResUnit<R>, + sections: &gimli::Dwarf<R>, + probe_low: u64, + probe_high: u64, + ) -> Result<Option<Self>, Error> { + let lines = resunit.parse_lines(sections)?; + + if let Some(lines) = lines { + // Find index for probe_low. + let seq_idx = lines.sequences.binary_search_by(|sequence| { + if probe_low < sequence.start { + Ordering::Greater + } else if probe_low >= sequence.end { + Ordering::Less + } else { + Ordering::Equal + } + }); + let seq_idx = match seq_idx { + Ok(x) => x, + Err(0) => 0, // probe below sequence, but range could overlap + Err(_) => lines.sequences.len(), + }; + + let row_idx = if let Some(seq) = lines.sequences.get(seq_idx) { + let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low)); + match idx { + Ok(x) => x, + Err(0) => 0, // probe below sequence, but range could overlap + Err(x) => x - 1, + } + } else { + 0 + }; + + Ok(Some(Self { + lines, + seqs: &*lines.sequences, + seq_idx, + row_idx, + probe_high, + })) + } else { + Ok(None) + } + } +} + +impl<'ctx> Iterator for LocationRangeUnitIter<'ctx> { + type Item = (u64, u64, Location<'ctx>); + + fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> { + while let Some(seq) = self.seqs.get(self.seq_idx) { + if seq.start >= self.probe_high { + break; + } + + match seq.rows.get(self.row_idx) { + Some(row) => { + if row.address >= self.probe_high { + break; + } + + let file = self + .lines + .files + .get(row.file_index as usize) + .map(String::as_str); + let nextaddr = seq + .rows + .get(self.row_idx + 1) + .map(|row| row.address) + .unwrap_or(seq.end); + + let item = ( + row.address, + nextaddr - row.address, + Location { + file, + line: if row.line != 0 { Some(row.line) } else { None }, + column: if row.column != 0 { + Some(row.column) + } else { + None + }, + }, + ); + self.row_idx += 1; + + return Some(item); + } + None => { + self.seq_idx += 1; + self.row_idx = 0; + } + } + } + None + } +} + +fn path_push(path: &mut String, p: &str) { + if has_unix_root(p) || has_windows_root(p) { + *path = p.to_string(); + } else { + let dir_separator = if has_windows_root(path.as_str()) { + '\\' + } else { + '/' + }; + + if !path.is_empty() && !path.ends_with(dir_separator) { + path.push(dir_separator); + } + *path += p; + } +} + +/// Check if the path in the given string has a unix style root +fn has_unix_root(p: &str) -> bool { + p.starts_with('/') +} + +/// Check if the path in the given string has a windows style root +fn has_windows_root(p: &str) -> bool { + p.starts_with('\\') || p.get(1..3) == Some(":\\") +} +struct RangeAttributes<R: gimli::Reader> { + low_pc: Option<u64>, + high_pc: Option<u64>, + size: Option<u64>, + ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>, +} + +impl<R: gimli::Reader> Default for RangeAttributes<R> { + fn default() -> Self { + RangeAttributes { + low_pc: None, + high_pc: None, + size: None, + ranges_offset: None, + } + } +} + +impl<R: gimli::Reader> RangeAttributes<R> { + fn for_each_range<F: FnMut(gimli::Range)>( + &self, + sections: &gimli::Dwarf<R>, + unit: &gimli::Unit<R>, + mut f: F, + ) -> Result<bool, Error> { + let mut added_any = false; + let mut add_range = |range: gimli::Range| { + if range.begin < range.end { + f(range); + added_any = true + } + }; + if let Some(ranges_offset) = self.ranges_offset { + let mut range_list = sections.ranges(unit, ranges_offset)?; + while let Some(range) = range_list.next()? { + add_range(range); + } + } else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) { + add_range(gimli::Range { begin, end }); + } else if let (Some(begin), Some(size)) = (self.low_pc, self.size) { + add_range(gimli::Range { + begin, + end: begin + size, + }); + } + Ok(added_any) + } +} + +/// An iterator over function frames. +pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>) +where + R: gimli::Reader; + +enum FrameIterState<'ctx, R> +where + R: gimli::Reader, +{ + Empty, + Location(Option<Location<'ctx>>), + Frames(FrameIterFrames<'ctx, R>), +} + +struct FrameIterFrames<'ctx, R> +where + R: gimli::Reader, +{ + unit: &'ctx ResUnit<R>, + sections: &'ctx gimli::Dwarf<R>, + function: &'ctx Function<R>, + inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>, + next: Option<Location<'ctx>>, +} + +impl<'ctx, R> FrameIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + /// Advances the iterator and returns the next frame. + pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> { + let frames = match &mut self.0 { + FrameIterState::Empty => return Ok(None), + FrameIterState::Location(location) => { + // We can't move out of a mutable reference, so use `take` instead. + let location = location.take(); + self.0 = FrameIterState::Empty; + return Ok(Some(Frame { + dw_die_offset: None, + function: None, + location, + })); + } + FrameIterState::Frames(frames) => frames, + }; + + let loc = frames.next.take(); + let func = match frames.inlined_functions.next() { + Some(func) => func, + None => { + let frame = Frame { + dw_die_offset: Some(frames.function.dw_die_offset), + function: frames.function.name.clone().map(|name| FunctionName { + name, + language: frames.unit.lang, + }), + location: loc, + }; + self.0 = FrameIterState::Empty; + return Ok(Some(frame)); + } + }; + + let mut next = Location { + file: None, + line: if func.call_line != 0 { + Some(func.call_line) + } else { + None + }, + column: if func.call_column != 0 { + Some(func.call_column) + } else { + None + }, + }; + if let Some(call_file) = func.call_file { + if let Some(lines) = frames.unit.parse_lines(frames.sections)? { + next.file = lines.files.get(call_file as usize).map(String::as_str); + } + } + frames.next = Some(next); + + Ok(Some(Frame { + dw_die_offset: Some(func.dw_die_offset), + function: func.name.clone().map(|name| FunctionName { + name, + language: frames.unit.lang, + }), + location: loc, + })) + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + type Item = Frame<'ctx, R>; + type Error = Error; + + #[inline] + fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> { + self.next() + } +} + +/// A function frame. +pub struct Frame<'ctx, R: gimli::Reader> { + /// The DWARF unit offset corresponding to the DIE of the function. + pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>, + /// The name of the function. + pub function: Option<FunctionName<R>>, + /// The source location corresponding to this frame. + pub location: Option<Location<'ctx>>, +} + +/// A function name. +pub struct FunctionName<R: gimli::Reader> { + /// The name of the function. + pub name: R, + /// The language of the compilation unit containing this function. + pub language: Option<gimli::DwLang>, +} + +impl<R: gimli::Reader> FunctionName<R> { + /// The raw name of this function before demangling. + pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> { + self.name.to_string_lossy() + } + + /// The name of this function after demangling (if applicable). + pub fn demangle(&self) -> Result<Cow<'_, str>, Error> { + self.raw_name().map(|x| demangle_auto(x, self.language)) + } +} + +/// Demangle a symbol name using the demangling scheme for the given language. +/// +/// Returns `None` if demangling failed or is not required. +#[allow(unused_variables)] +pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> { + match language { + #[cfg(feature = "rustc-demangle")] + gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name) + .ok() + .as_ref() + .map(|x| format!("{:#}", x)), + #[cfg(feature = "cpp_demangle")] + gimli::DW_LANG_C_plus_plus + | gimli::DW_LANG_C_plus_plus_03 + | gimli::DW_LANG_C_plus_plus_11 + | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name) + .ok() + .and_then(|x| x.demangle(&Default::default()).ok()), + _ => None, + } +} + +/// Apply 'best effort' demangling of a symbol name. +/// +/// If `language` is given, then only the demangling scheme for that language +/// is used. +/// +/// If `language` is `None`, then heuristics are used to determine how to +/// demangle the name. Currently, these heuristics are very basic. +/// +/// If demangling fails or is not required, then `name` is returned unchanged. +pub fn demangle_auto(name: Cow<'_, str>, language: Option<gimli::DwLang>) -> Cow<'_, str> { + match language { + Some(language) => demangle(name.as_ref(), language), + None => demangle(name.as_ref(), gimli::DW_LANG_Rust) + .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)), + } + .map(Cow::from) + .unwrap_or(name) +} + +/// A source location. +pub struct Location<'a> { + /// The file name. + pub file: Option<&'a str>, + /// The line number. + pub line: Option<u32>, + /// The column number. + pub column: Option<u32>, +} + +#[cfg(test)] +mod tests { + #[test] + fn context_is_send() { + fn assert_is_send<T: Send>() {} + assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>(); + } +} diff --git a/vendor/addr2line/tests/correctness.rs b/vendor/addr2line/tests/correctness.rs new file mode 100644 index 0000000..73ee462 --- /dev/null +++ b/vendor/addr2line/tests/correctness.rs @@ -0,0 +1,126 @@ +use addr2line::Context; +use fallible_iterator::FallibleIterator; +use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary}; +use object::Object; +use std::borrow::Cow; +use std::fs::File; +use std::sync::Arc; + +fn find_debuginfo() -> memmap2::Mmap { + let path = std::env::current_exe().unwrap(); + let file = File::open(&path).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let file = &object::File::parse(&*map).unwrap(); + if let Ok(uuid) = file.mach_uuid() { + for candidate in path.parent().unwrap().read_dir().unwrap() { + let path = candidate.unwrap().path(); + if !path.to_str().unwrap().ends_with(".dSYM") { + continue; + } + for candidate in path.join("Contents/Resources/DWARF").read_dir().unwrap() { + let path = candidate.unwrap().path(); + let file = File::open(&path).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let file = &object::File::parse(&*map).unwrap(); + if file.mach_uuid().unwrap() == uuid { + return map; + } + } + } + } + + return map; +} + +#[test] +fn correctness() { + let map = find_debuginfo(); + let file = &object::File::parse(&*map).unwrap(); + let module_base = file.relative_address_base(); + + let endian = if file.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + fn load_section<'data: 'file, 'file, O, Endian>( + id: gimli::SectionId, + file: &'file O, + endian: Endian, + ) -> Result<gimli::EndianArcSlice<Endian>, gimli::Error> + where + O: object::Object<'data, 'file>, + Endian: gimli::Endianity, + { + use object::ObjectSection; + + let data = file + .section_by_name(id.name()) + .and_then(|section| section.uncompressed_data().ok()) + .unwrap_or(Cow::Borrowed(&[])); + Ok(gimli::EndianArcSlice::new(Arc::from(&*data), endian)) + } + + let dwarf = gimli::Dwarf::load(|id| load_section(id, file, endian)).unwrap(); + let ctx = Context::from_dwarf(dwarf).unwrap(); + let mut split_dwarf_loader = addr2line::builtin_split_dwarf_loader::SplitDwarfLoader::new( + |data, endian| gimli::EndianArcSlice::new(Arc::from(&*data), endian), + None, + ); + + let mut bias = None; + TargetSharedLibrary::each(|lib| { + bias = Some((lib.virtual_memory_bias().0 as u64).wrapping_sub(module_base)); + IterationControl::Break + }); + + #[allow(unused_mut)] + let mut test = |sym: u64, expected_prefix: &str| { + let ip = sym.wrapping_sub(bias.unwrap()); + + let frames = ctx.find_frames(ip); + let frames = split_dwarf_loader.run(frames).unwrap(); + let frame = frames.last().unwrap().unwrap(); + let name = frame.function.as_ref().unwrap().demangle().unwrap(); + // Old rust versions generate DWARF with wrong linkage name, + // so only check the start. + if !name.starts_with(expected_prefix) { + panic!("incorrect name '{}', expected {:?}", name, expected_prefix); + } + }; + + test(test_function as u64, "correctness::test_function"); + test( + small::test_function as u64, + "correctness::small::test_function", + ); + test(auxiliary::foo as u64, "auxiliary::foo"); +} + +mod small { + pub fn test_function() { + println!("y"); + } +} + +fn test_function() { + println!("x"); +} + +#[test] +fn zero_function() { + let map = find_debuginfo(); + let file = &object::File::parse(&*map).unwrap(); + let ctx = Context::new(file).unwrap(); + for probe in 0..10 { + assert!( + ctx.find_frames(probe) + .skip_all_loads() + .unwrap() + .count() + .unwrap() + < 10 + ); + } +} diff --git a/vendor/addr2line/tests/output_equivalence.rs b/vendor/addr2line/tests/output_equivalence.rs new file mode 100644 index 0000000..ef026e3 --- /dev/null +++ b/vendor/addr2line/tests/output_equivalence.rs @@ -0,0 +1,135 @@ +use std::env; +use std::ffi::OsStr; +use std::path::Path; +use std::process::Command; + +use backtrace::Backtrace; +use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary}; +use libtest_mimic::{Arguments, Failed, Trial}; + +#[inline(never)] +fn make_trace() -> Vec<String> { + fn foo() -> Backtrace { + bar() + } + #[inline(never)] + fn bar() -> Backtrace { + baz() + } + #[inline(always)] + fn baz() -> Backtrace { + Backtrace::new_unresolved() + } + + let mut base_addr = None; + TargetSharedLibrary::each(|lib| { + base_addr = Some(lib.virtual_memory_bias().0 as isize); + IterationControl::Break + }); + let addrfix = -base_addr.unwrap(); + + let trace = foo(); + trace + .frames() + .iter() + .take(5) + .map(|x| format!("{:p}", (x.ip() as *const u8).wrapping_offset(addrfix))) + .collect() +} + +fn run_cmd<P: AsRef<OsStr>>(exe: P, me: &Path, flags: Option<&str>, trace: &str) -> String { + let mut cmd = Command::new(exe); + cmd.env("LC_ALL", "C"); // GNU addr2line is localized, we aren't + cmd.env("RUST_BACKTRACE", "1"); // if a child crashes, we want to know why + + if let Some(flags) = flags { + cmd.arg(flags); + } + cmd.arg("--exe").arg(me).arg(trace); + + let output = cmd.output().unwrap(); + + assert!(output.status.success()); + String::from_utf8(output.stdout).unwrap() +} + +fn run_test(flags: Option<&str>) -> Result<(), Failed> { + let me = env::current_exe().unwrap(); + let mut exe = me.clone(); + assert!(exe.pop()); + if exe.file_name().unwrap().to_str().unwrap() == "deps" { + assert!(exe.pop()); + } + exe.push("examples"); + exe.push("addr2line"); + + assert!(exe.is_file()); + + let trace = make_trace(); + + // HACK: GNU addr2line has a bug where looking up multiple addresses can cause the second + // lookup to fail. Workaround by doing one address at a time. + for addr in &trace { + let theirs = run_cmd("addr2line", &me, flags, addr); + let ours = run_cmd(&exe, &me, flags, addr); + + // HACK: GNU addr2line does not tidy up paths properly, causing double slashes to be printed. + // We consider our behavior to be correct, so we fix their output to match ours. + let theirs = theirs.replace("//", "/"); + + assert!( + theirs == ours, + "Output not equivalent: + +$ addr2line {0} --exe {1} {2} +{4} +$ {3} {0} --exe {1} {2} +{5} + + +", + flags.unwrap_or(""), + me.display(), + trace.join(" "), + exe.display(), + theirs, + ours + ); + } + Ok(()) +} + +static FLAGS: &str = "aipsf"; + +fn make_tests() -> Vec<Trial> { + (0..(1 << FLAGS.len())) + .map(|bits| { + if bits == 0 { + None + } else { + let mut param = String::new(); + param.push('-'); + for (i, flag) in FLAGS.chars().enumerate() { + if (bits & (1 << i)) != 0 { + param.push(flag); + } + } + Some(param) + } + }) + .map(|param| { + Trial::test( + format!("addr2line {}", param.as_ref().map_or("", String::as_str)), + move || run_test(param.as_ref().map(String::as_str)), + ) + }) + .collect() +} + +fn main() { + if !cfg!(target_os = "linux") { + return; + } + let args = Arguments::from_args(); + libtest_mimic::run(&args, make_tests()).exit(); +} diff --git a/vendor/addr2line/tests/parse.rs b/vendor/addr2line/tests/parse.rs new file mode 100644 index 0000000..4dafe38 --- /dev/null +++ b/vendor/addr2line/tests/parse.rs @@ -0,0 +1,114 @@ +use std::borrow::Cow; +use std::env; +use std::fs::File; +use std::path::{self, PathBuf}; + +use object::Object; + +fn release_fixture_path() -> PathBuf { + if let Ok(p) = env::var("ADDR2LINE_FIXTURE_PATH") { + return p.into(); + } + + let mut path = PathBuf::new(); + if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") { + path.push(dir); + } + path.push("fixtures"); + path.push("addr2line-release"); + path +} + +fn with_file<F: FnOnce(&object::File<'_>)>(target: &path::Path, f: F) { + let file = File::open(target).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let file = object::File::parse(&*map).unwrap(); + f(&file) +} + +fn dwarf_load<'a>(object: &object::File<'a>) -> gimli::Dwarf<Cow<'a, [u8]>> { + let load_section = |id: gimli::SectionId| -> Result<Cow<'a, [u8]>, gimli::Error> { + use object::ObjectSection; + + let data = object + .section_by_name(id.name()) + .and_then(|section| section.data().ok()) + .unwrap_or(&[][..]); + Ok(Cow::Borrowed(data)) + }; + gimli::Dwarf::load(&load_section).unwrap() +} + +fn dwarf_borrow<'a>( + dwarf: &'a gimli::Dwarf<Cow<'_, [u8]>>, +) -> gimli::Dwarf<gimli::EndianSlice<'a, gimli::LittleEndian>> { + let borrow_section: &dyn for<'b> Fn( + &'b Cow<'_, [u8]>, + ) -> gimli::EndianSlice<'b, gimli::LittleEndian> = + &|section| gimli::EndianSlice::new(section, gimli::LittleEndian); + dwarf.borrow(&borrow_section) +} + +#[test] +fn parse_base_rc() { + let target = release_fixture_path(); + + with_file(&target, |file| { + addr2line::ObjectContext::new(file).unwrap(); + }); +} + +#[test] +fn parse_base_slice() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let dwarf = dwarf_load(file); + let dwarf = dwarf_borrow(&dwarf); + addr2line::Context::from_dwarf(dwarf).unwrap(); + }); +} + +#[test] +fn parse_lines_rc() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let context = addr2line::ObjectContext::new(file).unwrap(); + context.parse_lines().unwrap(); + }); +} + +#[test] +fn parse_lines_slice() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let dwarf = dwarf_load(file); + let dwarf = dwarf_borrow(&dwarf); + let context = addr2line::Context::from_dwarf(dwarf).unwrap(); + context.parse_lines().unwrap(); + }); +} + +#[test] +fn parse_functions_rc() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let context = addr2line::ObjectContext::new(file).unwrap(); + context.parse_functions().unwrap(); + }); +} + +#[test] +fn parse_functions_slice() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let dwarf = dwarf_load(file); + let dwarf = dwarf_borrow(&dwarf); + let context = addr2line::Context::from_dwarf(dwarf).unwrap(); + context.parse_functions().unwrap(); + }); +} |