addr2line-0.24.2/.cargo_vcs_info.json0000644000000001360000000000100127650ustar { "git": { "sha1": "621a3abe985b32f43dd1e8c10e003abe902c68e2" }, "path_in_vcs": "" }addr2line-0.24.2/CHANGELOG.md000064400000000000000000000306731046102023000133770ustar 00000000000000# `addr2line` Change Log -------------------------------------------------------------------------------- ## 0.24.2 (2024/10/04) ### Changed * Enabled caching of DWARF abbreviations. [#318](https://github.com/gimli-rs/addr2line/pull/318) * Changed the `addr2line` binary to prefer symbol names over DWARF names. [#332](https://github.com/gimli-rs/addr2line/pull/332) * Updated `gimli` dependency. ### Added * Added `Context::from_arc_dwarf`. [#327](https://github.com/gimli-rs/addr2line/pull/327) * Added benchmark comparison. [#315](https://github.com/gimli-rs/addr2line/pull/315) [#321](https://github.com/gimli-rs/addr2line/pull/321) [#322](https://github.com/gimli-rs/addr2line/pull/322) [#325](https://github.com/gimli-rs/addr2line/pull/325) * Added more tests. [#328](https://github.com/gimli-rs/addr2line/pull/328) [#330](https://github.com/gimli-rs/addr2line/pull/330) [#331](https://github.com/gimli-rs/addr2line/pull/331) [#333](https://github.com/gimli-rs/addr2line/pull/333) -------------------------------------------------------------------------------- ## 0.24.1 (2024/07/26) ### Changed * Fixed parsing of partial units, which are found in supplementary object files. [#313](https://github.com/gimli-rs/addr2line/pull/313) -------------------------------------------------------------------------------- ## 0.24.0 (2024/07/16) ### Breaking changes * Updated `gimli` dependency. ### Changed * Changed the order of ranges returned by `Context::find_location_range`, and fixed handling in rare situations. [#303](https://github.com/gimli-rs/addr2line/pull/303) [#304](https://github.com/gimli-rs/addr2line/pull/304) [#306](https://github.com/gimli-rs/addr2line/pull/306) * Improved the performance of `Context::find_location`. [#305](https://github.com/gimli-rs/addr2line/pull/305) ### Added * Added `LoaderReader`. [#307](https://github.com/gimli-rs/addr2line/pull/307) * Added `--all` option to `addr2line`. [#307](https://github.com/gimli-rs/addr2line/pull/307) -------------------------------------------------------------------------------- ## 0.23.0 (2024/05/26) ### Breaking changes * Updated `gimli` dependency. * Deleted `Context::new`, `Context::new_with_sup`, and `builtin_split_dwarf_loader`. Use `Context::from_dwarf` or `Loader::new` instead. This removes `object` from the public API. [#296](https://github.com/gimli-rs/addr2line/pull/296) ### Changed * Fixed handling of column 0 in the line table. [#290](https://github.com/gimli-rs/addr2line/pull/290) * Moved `addr2line` from `examples` to `bin`. Requires the `bin` feature. [#291](https://github.com/gimli-rs/addr2line/pull/291) * Split up `lib.rs` into smaller modules. [#292](https://github.com/gimli-rs/addr2line/pull/292) ### Added * Added `Loader`. Requires the `loader` feature. [#296](https://github.com/gimli-rs/addr2line/pull/296) [#297](https://github.com/gimli-rs/addr2line/pull/297) * Added unpacked Mach-O support to `Loader`. [#298](https://github.com/gimli-rs/addr2line/pull/298) -------------------------------------------------------------------------------- ## 0.22.0 (2024/04/11) ### Breaking changes * Updated `gimli` and `object` dependencies. -------------------------------------------------------------------------------- ## 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` 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`to `Option`. * [#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`. addr2line-0.24.2/Cargo.lock0000644000000404010000000000100107370ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] name = "addr2line" version = "0.24.2" dependencies = [ "backtrace", "clap", "compiler_builtins", "cpp_demangle", "fallible-iterator", "findshlibs", "gimli", "libtest-mimic", "memmap2", "object", "rustc-demangle", "rustc-std-workspace-alloc", "rustc-std-workspace-core", "smallvec", "typed-arena", ] [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "anstream" version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] name = "backtrace" version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line 0.24.1", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets", ] [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "shlex", ] [[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.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "terminal_size", ] [[package]] name = "clap_derive" version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "compiler_builtins" version = "0.1.131" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d18d2ba094b78965890b2912f45dc8cb6bb3aff315ef54755ec33223b6454502" [[package]] name = "cpp_demangle" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" dependencies = [ "cfg-if", ] [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "escape8259" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" [[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.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "compiler_builtins", "fallible-iterator", "rustc-std-workspace-alloc", "rustc-std-workspace-core", "stable_deref_trait", ] [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libtest-mimic" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc0bda45ed5b3a2904262c1bb91e526127aa70e7ef3758aba2ef93cf896b9b58" dependencies = [ "clap", "escape8259", "termcolor", "threadpool", ] [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] [[package]] name = "miniz_oxide" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "object" version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "flate2", "memchr", "ruzstd", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[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.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "ruzstd" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99c3938e133aac070997ddc684d4b393777d293ba170f2988c8fd5ea2ad4ce21" dependencies = [ "twox-hash", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[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.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", "windows-sys 0.59.0", ] [[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.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[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.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys 0.59.0", ] [[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.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" addr2line-0.24.2/Cargo.toml0000644000000056670000000000100110010ustar # 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.24.2" build = false include = [ "/CHANGELOG.md", "/Cargo.lock", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src", ] autobins = false autoexamples = false autotests = false autobenches = false 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 = 2 [profile.release] debug = 2 [lib] name = "addr2line" path = "src/lib.rs" [[bin]] name = "addr2line" path = "src/bin/addr2line.rs" required-features = ["bin"] [dependencies.alloc] version = "1.0.0" optional = true package = "rustc-std-workspace-alloc" [dependencies.clap] version = "4.3.21" features = ["wrap_help"] optional = true [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.31.1" features = ["read"] default-features = false [dependencies.memmap2] version = "0.9.4" optional = true [dependencies.object] version = "0.36.0" features = [ "read", "compression", ] optional = true default-features = false [dependencies.rustc-demangle] version = "0.1" optional = true [dependencies.smallvec] version = "1" optional = true default-features = false [dependencies.typed-arena] version = "2" optional = true [dev-dependencies.backtrace] version = "0.3.13" [dev-dependencies.findshlibs] version = "0.10" [dev-dependencies.libtest-mimic] version = "0.7.2" [features] all = ["bin"] bin = [ "loader", "rustc-demangle", "cpp_demangle", "fallible-iterator", "smallvec", "dep:clap", ] cargo-all = [] default = [ "rustc-demangle", "cpp_demangle", "loader", "fallible-iterator", "smallvec", ] loader = [ "std", "dep:object", "dep:memmap2", "dep:typed-arena", ] rustc-dep-of-std = [ "core", "alloc", "compiler_builtins", "gimli/rustc-dep-of-std", ] std = ["gimli/std"] addr2line-0.24.2/Cargo.toml.orig000064400000000000000000000052621046102023000144510ustar 00000000000000[package] name = "addr2line" version = "0.24.2" description = "A cross-platform symbolication library written in Rust, using `gimli`" documentation = "https://docs.rs/addr2line" keywords = ["DWARF", "debug", "elf", "symbolicate", "atos"] categories = ["development-tools::debugging"] license = "Apache-2.0 OR MIT" readme = "./README.md" repository = "https://github.com/gimli-rs/addr2line" edition = "2018" rust-version = "1.65" include = [ "/CHANGELOG.md", "/Cargo.lock", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src", ] [dependencies] gimli = { version = "0.31.1", default-features = false, features = ["read"] } fallible-iterator = { version = "0.3.0", default-features = false, optional = true } smallvec = { version = "1", default-features = false, optional = true } rustc-demangle = { version = "0.1", optional = true } cpp_demangle = { version = "0.4", default-features = false, features = ["alloc"], optional = true } # loader dependencies object = { version = "0.36.0", default-features = false, features = ["read", "compression"], optional = true } memmap2 = { version = "0.9.4", optional = true } typed-arena = { version = "2", optional = true } # bin dependencies clap = { version = "4.3.21", features = ["wrap_help"], optional = true } # Internal feature, only used when building as part of libstd, not part of the # stable interface of this crate. core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } alloc = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-alloc' } compiler_builtins = { version = '0.1.2', optional = true } [dev-dependencies] backtrace = "0.3.13" findshlibs = "0.10" libtest-mimic = "0.7.2" auxiliary = { path = "tests/auxiliary" } [profile.release] debug = true [profile.bench] debug = true codegen-units = 1 [features] default = ["rustc-demangle", "cpp_demangle", "loader", "fallible-iterator", "smallvec"] std = ["gimli/std"] loader = ["std", "dep:object", "dep:memmap2", "dep:typed-arena"] bin = ["loader", "rustc-demangle", "cpp_demangle", "fallible-iterator", "smallvec", "dep:clap"] all = ["bin"] # Use of --all-features is not supported. # This is a dummy feature to detect when --all-features is used. cargo-all = [] # Internal feature, only used when building as part of libstd, not part of the # stable interface of this crate. rustc-dep-of-std = ['core', 'alloc', 'compiler_builtins', 'gimli/rustc-dep-of-std'] [[test]] name = "testinput" harness = false required-features = ["bin"] [[test]] name = "correctness" required-features = ["loader", "fallible-iterator"] [[test]] name = "parse" required-features = ["loader"] [[bin]] name = "addr2line" required-features = ["bin"] addr2line-0.24.2/LICENSE-APACHE000064400000000000000000000251371046102023000135110ustar 00000000000000 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. addr2line-0.24.2/LICENSE-MIT000064400000000000000000000020551046102023000132130ustar 00000000000000Copyright (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. addr2line-0.24.2/README.md000064400000000000000000000046651046102023000130470ustar 00000000000000# 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) `addr2line` provides a cross-platform library for retrieving per-address debug information from files with DWARF debug information. Given an address, it can return the file name, line number, and function name associated with that address, as well as the inline call stack leading to that address. The crate has a CLI wrapper around the library which provides some of the functionality of the `addr2line` command line tool distributed with [GNU binutils](https://sourceware.org/binutils/docs/binutils/addr2line.html). # Quickstart - Add the [`addr2line` crate](https://crates.io/crates/addr2line) to your `Cargo.toml`. - Call [`addr2line::Loader::new`](https://docs.rs/addr2line/*/addr2line/struct.Loader.html#method.new) with the file path. - Use [`addr2line::Loader::find_location`](https://docs.rs/addr2line/*/addr2line/struct.Loader.html#method.find_location) or [`addr2line::Loader::find_frames`](https://docs.rs/addr2line/*/addr2line/struct.Loader.html#method.find_frames) to look up debug information for an address. If you want to provide your own file loading and memory management, use [`addr2line::Context`](https://docs.rs/addr2line/*/addr2line/struct.Context.html) instead of `addr2line::Loader`. # 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-addr2line` from the llvm project. Current benchmarks show a performance improvement in all cases: ![addr2line runtime](benchmark-time.svg) ## 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. addr2line-0.24.2/src/bin/addr2line.rs000064400000000000000000000230051046102023000153260ustar 00000000000000use fallible_iterator::FallibleIterator; use std::borrow::Cow; use std::io::{BufRead, Lines, StdinLock, Write}; use std::path::{Path, PathBuf}; use clap::{Arg, ArgAction, Command}; use addr2line::{Loader, LoaderReader, Location}; fn parse_uint_from_hex_string(string: &str) -> Option { 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>), All { iter: addr2line::LocationRangeIter<'a, LoaderReader<'a>>, max: u64, }, } impl<'a> Iterator for Addrs<'a> { type Item = Option; fn next(&mut self) -> Option> { let text = match self { Addrs::Args(vals) => vals.next().map(Cow::from), Addrs::Stdin(lines) => lines.next().map(Result::unwrap).map(Cow::from), Addrs::All { iter, max } => { for (addr, _len, _loc) in iter { if addr >= *max { *max = addr + 1; return Some(Some(addr)); } } return None; } }; 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, demangle: bool) { if let Some(name) = name { if demangle { print!("{}", addr2line::demangle_auto(Cow::from(name), language)); } else { print!("{}", name); } } else { print!("??"); } } 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("all") .long("all") .action(ArgAction::SetTrue) .conflicts_with("addrs") .help("Display all addresses that have line number information."), 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 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::("exe").unwrap(), sup: matches.get_one::("sup"), }; let ctx = Loader::new_with_sup(opts.exe, opts.sup).unwrap(); let stdin = std::io::stdin(); let addrs = if matches.get_flag("all") { Addrs::All { iter: ctx.find_location_range(0, !0).unwrap(), max: 0, } } else { matches .get_many::("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 mut frames = ctx.find_frames(probe).unwrap().peekable(); let mut first = true; while let Some(frame) = frames.next().unwrap() { if opts.pretty && !first { print!(" (inlined by) "); } first = false; if opts.do_functions { // Only use the symbol table if this isn't an inlined function. let symbol = if matches!(frames.peek(), Ok(None)) { ctx.find_symbol(probe) } else { None }; if symbol.is_some() { // Prefer the symbol table over the DWARF name because: // - the symbol can include a clone suffix // - llvm may omit the linkage name in the DWARF with -g1 print_function(symbol, None, opts.demangle); } else if let Some(func) = frame.function { print_function( func.raw_name().ok().as_deref(), func.language, opts.demangle, ); } else { print_function(None, 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| ctx.find_symbol(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(); } } addr2line-0.24.2/src/frame.rs000064400000000000000000000152761046102023000140170ustar 00000000000000use alloc::borrow::Cow; use alloc::string::String; use core::iter; use crate::{maybe_small, Error, Function, InlinedFunction, ResUnit}; /// A source location. pub struct Location<'a> { /// The file name. pub file: Option<&'a str>, /// The line number. pub line: Option, /// The column number. /// /// A value of `Some(0)` indicates the left edge. pub column: Option, } /// 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>, /// The name of the function. pub function: Option>, /// The source location corresponding to this frame. pub location: Option>, } /// 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>), Frames(FrameIterFrames<'ctx, R>), } struct FrameIterFrames<'ctx, R> where R: gimli::Reader, { unit: &'ctx ResUnit, sections: &'ctx gimli::Dwarf, function: &'ctx Function, inlined_functions: iter::Rev>>, next: Option>, } impl<'ctx, R> FrameIter<'ctx, R> where R: gimli::Reader + 'ctx, { pub(crate) fn new_empty() -> Self { FrameIter(FrameIterState::Empty) } pub(crate) fn new_location(location: Location<'ctx>) -> Self { FrameIter(FrameIterState::Location(Some(location))) } pub(crate) fn new_frames( unit: &'ctx ResUnit, sections: &'ctx gimli::Dwarf, function: &'ctx Function, inlined_functions: maybe_small::Vec<&'ctx InlinedFunction>, location: Option>, ) -> Self { FrameIter(FrameIterState::Frames(FrameIterFrames { unit, sections, function, inlined_functions: inlined_functions.into_iter().rev(), next: location, })) } /// Advances the iterator and returns the next frame. #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Result>, 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.file(call_file); } } 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>, Error> { self.next() } } /// A function name. pub struct FunctionName { /// The name of the function. pub name: R, /// The language of the compilation unit containing this function. pub language: Option, } impl FunctionName { /// The raw name of this function before demangling. pub fn raw_name(&self) -> Result, Error> { self.name.to_string_lossy() } /// The name of this function after demangling (if applicable). pub fn demangle(&self) -> Result, 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 { 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) -> 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) } addr2line-0.24.2/src/function.rs000064400000000000000000000510371046102023000145450ustar 00000000000000use alloc::boxed::Box; use alloc::vec::Vec; use core::cmp::Ordering; use crate::lazy::LazyResult; use crate::maybe_small; use crate::{Context, DebugFile, Error, RangeAttributes}; pub(crate) struct LazyFunctions(LazyResult>); impl LazyFunctions { pub(crate) fn new() -> Self { LazyFunctions(LazyResult::new()) } pub(crate) fn borrow(&self, unit: gimli::UnitRef) -> Result<&Functions, Error> { self.0 .borrow_with(|| Functions::parse(unit)) .as_ref() .map_err(Error::clone) } } pub(crate) struct Functions { /// List of all `DW_TAG_subprogram` details in the unit. pub(crate) functions: Box<[LazyFunction]>, /// 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 LazyFunction { dw_die_offset: gimli::UnitOffset, lazy: LazyResult>, } impl LazyFunction { fn new(dw_die_offset: gimli::UnitOffset) -> Self { LazyFunction { dw_die_offset, lazy: LazyResult::new(), } } pub(crate) fn borrow( &self, file: DebugFile, unit: gimli::UnitRef, ctx: &Context, ) -> Result<&Function, Error> { self.lazy .borrow_with(|| Function::parse(self.dw_die_offset, file, unit, ctx)) .as_ref() .map_err(Error::clone) } } pub(crate) struct Function { pub(crate) dw_die_offset: gimli::UnitOffset, pub(crate) name: Option, /// List of all `DW_TAG_inlined_subroutine` details in this function. inlined_functions: Box<[InlinedFunction]>, /// 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 { pub(crate) dw_die_offset: gimli::UnitOffset, pub(crate) name: Option, pub(crate) call_file: Option, pub(crate) call_line: u32, pub(crate) call_column: u32, } impl Functions { fn parse(unit: gimli::UnitRef) -> Result, 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(unit.address(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(unit.address(index)?); } gimli::AttributeValue::Udata(val) => { ranges.size = Some(val) } _ => {} }, gimli::DW_AT_ranges => { ranges.ranges_offset = unit.attr_ranges_offset(attr.value())?; } _ => {} }; } Err(e) => return Err(e), } } let function_index = functions.len(); let has_address = ranges.for_each_range(unit, |range| { addresses.push(FunctionAddress { range, function: function_index, }); })?; if has_address { functions.push(LazyFunction::new(dw_die_offset)); } } 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 { 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::UnitRef, ctx: &Context, ) -> Result<(), Error> { for function in &*self.functions { function.borrow(file, unit, ctx)?; } Ok(()) } } impl Function { fn parse( dw_die_offset: gimli::UnitOffset, file: DebugFile, unit: gimli::UnitRef, ctx: &Context, ) -> Result { 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) = unit.attr_string(attr.value()) { name = Some(val); } } gimli::DW_AT_name => { if name.is_none() { name = unit.attr_string(attr.value()).ok(); } } gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { if name.is_none() { name = name_attr(attr.value(), file, unit, ctx, 16)?; } } _ => {} }; } Err(e) => return Err(e), } } let mut state = InlinedState { entries, functions: Vec::new(), addresses: Vec::new(), file, unit, ctx, }; Function::parse_children(&mut state, depth, 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. state.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: state.functions.into_boxed_slice(), inlined_addresses: state.addresses.into_boxed_slice(), }) } fn parse_children( state: &mut InlinedState, depth: isize, inlined_depth: usize, ) -> Result<(), Error> { loop { let dw_die_offset = state.entries.next_offset(); let next_depth = state.entries.next_depth(); if next_depth <= depth { return Ok(()); } if let Some(abbrev) = state.entries.read_abbreviation()? { match abbrev.tag() { gimli::DW_TAG_subprogram => { Function::skip(&mut state.entries, abbrev, next_depth)?; } gimli::DW_TAG_inlined_subroutine => { InlinedFunction::parse( state, dw_die_offset, abbrev, next_depth, inlined_depth, )?; } _ => { state.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, ) -> maybe_small::Vec<&InlinedFunction> { // `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 } } impl InlinedFunction { fn parse( state: &mut InlinedState, dw_die_offset: gimli::UnitOffset, abbrev: &gimli::Abbreviation, depth: isize, inlined_depth: usize, ) -> Result<(), Error> { let unit = state.unit; 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 state.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(unit.address(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(unit.address(index)?); } gimli::AttributeValue::Udata(val) => ranges.size = Some(val), _ => {} }, gimli::DW_AT_ranges => { ranges.ranges_offset = unit.attr_ranges_offset(attr.value())?; } gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { if let Ok(val) = unit.attr_string(attr.value()) { name = Some(val); } } gimli::DW_AT_name => { if name.is_none() { name = unit.attr_string(attr.value()).ok(); } } gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { if name.is_none() { name = name_attr(attr.value(), state.file, unit, state.ctx, 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 = state.functions.len(); state.functions.push(InlinedFunction { dw_die_offset, name, call_file, call_line, call_column, }); ranges.for_each_range(unit, |range| { state.addresses.push(InlinedFunctionAddress { range, call_depth: inlined_depth, function: function_index, }); })?; Function::parse_children(state, depth, inlined_depth + 1) } } struct InlinedState<'a, R: gimli::Reader> { // Mutable fields. entries: gimli::EntriesRaw<'a, 'a, R>, functions: Vec>, addresses: Vec, // Constant fields. file: DebugFile, unit: gimli::UnitRef<'a, R>, ctx: &'a Context, } fn name_attr( attr: gimli::AttributeValue, mut file: DebugFile, unit: gimli::UnitRef, ctx: &Context, recursion_limit: usize, ) -> Result, Error> where R: gimli::Reader, { if recursion_limit == 0 { return Ok(None); } match attr { gimli::AttributeValue::UnitRef(offset) => { name_entry(file, unit, offset, ctx, recursion_limit) } gimli::AttributeValue::DebugInfoRef(dr) => { let sections = unit.dwarf; let (unit, offset) = ctx.find_unit(dr, file)?; let unit = gimli::UnitRef::new(sections, unit); name_entry(file, unit, offset, ctx, recursion_limit) } gimli::AttributeValue::DebugInfoRefSup(dr) => { if let Some(sup_sections) = unit.dwarf.sup.as_ref() { file = DebugFile::Supplementary; let (unit, offset) = ctx.find_unit(dr, file)?; let unit = gimli::UnitRef::new(sup_sections, unit); name_entry(file, unit, offset, ctx, recursion_limit) } else { Ok(None) } } _ => Ok(None), } } fn name_entry( file: DebugFile, unit: gimli::UnitRef, offset: gimli::UnitOffset, ctx: &Context, recursion_limit: usize, ) -> Result, 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) = unit.attr_string(attr.value()) { return Ok(Some(val)); } } gimli::DW_AT_name => { if let Ok(val) = unit.attr_string(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, recursion_limit - 1); } Ok(None) } addr2line-0.24.2/src/lazy.rs000064400000000000000000000020451046102023000136720ustar 00000000000000use core::cell::UnsafeCell; pub(crate) type LazyResult = LazyCell>; pub(crate) struct LazyCell { contents: UnsafeCell>, } impl LazyCell { pub(crate) fn new() -> LazyCell { LazyCell { contents: UnsafeCell::new(None), } } pub(crate) fn borrow(&self) -> Option<&T> { unsafe { &*self.contents.get() }.as_ref() } pub(crate) 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) } } } addr2line-0.24.2/src/lib.rs000064400000000000000000000355341046102023000134720ustar 00000000000000//! `addr2line` provides a cross-platform library for retrieving per-address debug information //! from files with DWARF debug information. Given an address, it can return the file name, //! line number, and function name associated with that address, as well as the inline call //! stack leading to that address. //! //! At the lowest level, the library uses a [`Context`] to cache parsed information so that //! multiple lookups are efficient. To create a `Context`, you first need to open and parse the //! file using an object file parser such as [`object`](https://github.com/gimli-rs/object), //! create a [`gimli::Dwarf`], and finally call [`Context::from_dwarf`]. //! //! Location information is obtained with [`Context::find_location`] or //! [`Context::find_location_range`]. Function information is obtained with //! [`Context::find_frames`], which returns a frame for each inline function. Each frame //! contains both name and location. //! //! The library also provides a [`Loader`] which internally memory maps the files, //! uses the `object` crate to do the parsing, and creates a `Context`. //! The `Context` is not exposed, but the `Loader` provides the same functionality //! via [`Loader::find_location`], [`Loader::find_location_range`], and //! [`Loader::find_frames`]. The `Loader` also provides [`Loader::find_symbol`] //! to use the symbol table instead of DWARF debugging information. //! The `Loader` will load Mach-O dSYM files and split DWARF files as needed. //! //! The crate has a CLI wrapper around the library which provides some of //! the functionality of the `addr2line` command line tool distributed with //! [GNU binutils](https://sourceware.org/binutils/docs/binutils/addr2line.html). #![deny(missing_docs)] #![no_std] #[cfg(feature = "cargo-all")] compile_error!("'--all-features' is not supported; use '--features all' instead"); #[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; use alloc::sync::Arc; use core::ops::ControlFlow; use crate::function::{Function, Functions, InlinedFunction, LazyFunctions}; use crate::line::{LazyLines, LineLocationRangeIter, Lines}; use crate::lookup::{LoopingLookup, SimpleLookup}; use crate::unit::{ResUnit, ResUnits, SupUnits}; #[cfg(feature = "smallvec")] mod maybe_small { pub type Vec = smallvec::SmallVec<[T; 16]>; pub type IntoIter = smallvec::IntoIter<[T; 16]>; } #[cfg(not(feature = "smallvec"))] mod maybe_small { pub type Vec = alloc::vec::Vec; pub type IntoIter = alloc::vec::IntoIter; } mod frame; pub use frame::{demangle, demangle_auto, Frame, FrameIter, FunctionName, Location}; mod function; mod lazy; mod line; #[cfg(feature = "loader")] mod loader; #[cfg(feature = "loader")] pub use loader::{Loader, LoaderReader}; mod lookup; pub use lookup::{LookupContinuation, LookupResult, SplitDwarfLoad}; mod unit; pub use unit::LocationRangeIter; type Error = gimli::Error; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum DebugFile { Primary, Supplementary, Dwo, } /// 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 { sections: Arc>, units: ResUnits, sup_units: SupUnits, } impl Context { /// Construct a new `Context` from DWARF sections. /// /// This method does not support using a supplementary object file. #[allow(clippy::too_many_arguments)] pub fn from_sections( debug_abbrev: gimli::DebugAbbrev, debug_addr: gimli::DebugAddr, debug_aranges: gimli::DebugAranges, debug_info: gimli::DebugInfo, debug_line: gimli::DebugLine, debug_line_str: gimli::DebugLineStr, debug_ranges: gimli::DebugRanges, debug_rnglists: gimli::DebugRngLists, debug_str: gimli::DebugStr, debug_str_offsets: gimli::DebugStrOffsets, default_section: R, ) -> Result { 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) -> Result, Error> { Self::from_arc_dwarf(Arc::new(sections)) } /// Construct a new `Context` from an existing [`gimli::Dwarf`] object. #[inline] pub fn from_arc_dwarf(sections: Arc>) -> Result, Error> { let units = ResUnits::parse(§ions)?; let sup_units = if let Some(sup) = sections.sup.as_ref() { SupUnits::parse(sup)? } else { SupUnits::default() }; Ok(Context { sections, units, sup_units, }) } } impl Context { /// Find the DWARF unit corresponding to the given virtual memory address. pub fn find_dwarf_and_unit( &self, probe: u64, ) -> LookupResult>, Buf = R>> { let mut units_iter = self.units.find(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, unit) = unit .dwarf_and_unit(self) // We've already been through both error cases here to get to this point. .unwrap() .unwrap(); Some(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>, Error> { for unit in self.units.find(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, Error> { self.units .find_location_range(probe_low, probe_high, &self.sections) } /// 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, Error>, Buf = R>> { let mut units_iter = self.units.find(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::new_frames( unit, &self.sections, function, inlined_functions, location, )) } Ok((None, Some(location))) => Ok(FrameIter::new_location(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::new_empty()), }, }) }) } else { LoopingLookup::new_complete(Ok(FrameIter::new_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> = todo!(); /// # let do_split_dwarf_load = |load: SplitDwarfLoad>| -> Option>>> { 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, impl FnOnce(Option>>) -> Result<(), gimli::Error> + '_, ), > { self.units .find(probe) .filter_map(move |unit| match unit.dwarf_and_unit(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(()) } } impl Context { // Find the unit containing the given offset, and convert the offset into a unit offset. fn find_unit( &self, offset: gimli::DebugInfoOffset, file: DebugFile, ) -> Result<(&gimli::Unit, gimli::UnitOffset), Error> { let unit = match file { DebugFile::Primary => self.units.find_offset(offset)?, DebugFile::Supplementary => self.sup_units.find_offset(offset)?, 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 RangeAttributes { low_pc: Option, high_pc: Option, size: Option, ranges_offset: Option::Offset>>, } impl Default for RangeAttributes { fn default() -> Self { RangeAttributes { low_pc: None, high_pc: None, size: None, ranges_offset: None, } } } impl RangeAttributes { fn for_each_range( &self, unit: gimli::UnitRef, mut f: F, ) -> Result { 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 = unit.ranges(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) { // If `begin` is a -1 tombstone, this will overflow and the check in // `add_range` will ignore it. let end = begin.wrapping_add(size); add_range(gimli::Range { begin, end }); } Ok(added_any) } } #[cfg(test)] mod tests { #[test] fn context_is_send() { fn assert_is_send() {} assert_is_send::>>(); } } addr2line-0.24.2/src/line.rs000064400000000000000000000217521046102023000136500ustar 00000000000000use alloc::boxed::Box; use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::cmp::Ordering; use core::mem; use core::num::NonZeroU64; use crate::lazy::LazyResult; use crate::{Error, Location}; pub(crate) struct LazyLines(LazyResult); impl LazyLines { pub(crate) fn new() -> Self { LazyLines(LazyResult::new()) } pub(crate) fn borrow( &self, dw_unit: gimli::UnitRef, ilnp: &gimli::IncompleteLineProgram, ) -> Result<&Lines, Error> { self.0 .borrow_with(|| Lines::parse(dw_unit, ilnp.clone())) .as_ref() .map_err(Error::clone) } } struct LineSequence { start: u64, end: u64, rows: Box<[LineRow]>, } struct LineRow { address: u64, file_index: u64, line: u32, column: u32, } pub(crate) struct Lines { files: Box<[String]>, sequences: Box<[LineSequence]>, } impl Lines { fn parse( dw_unit: gimli::UnitRef, ilnp: gimli::IncompleteLineProgram, ) -> Result { let mut sequences = Vec::new(); let mut sequence_rows = Vec::::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(); // Convert line and column to u32 to save a little memory. // We'll handle the special case of line 0 later, // and return left edge as column 0 in the public API. 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)?), 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)?); index += 1; } Ok(Self { files: files.into_boxed_slice(), sequences: sequences.into_boxed_slice(), }) } pub(crate) fn file(&self, index: u64) -> Option<&str> { self.files.get(index as usize).map(String::as_str) } pub(crate) fn ranges(&self) -> impl Iterator + '_ { self.sequences.iter().map(|sequence| gimli::Range { begin: sequence.start, end: sequence.end, }) } fn row_location(&self, row: &LineRow) -> Location<'_> { let file = self.files.get(row.file_index as usize).map(String::as_str); Location { file, line: if row.line != 0 { Some(row.line) } else { None }, // If row.line is specified then row.column always has meaning. column: if row.line != 0 { Some(row.column) } else { None }, } } pub(crate) fn find_location(&self, probe: u64) -> Result>, Error> { let seq_idx = self.sequences.binary_search_by(|sequence| { if probe < sequence.start { Ordering::Greater } else if probe >= sequence.end { Ordering::Less } else { Ordering::Equal } }); let seq_idx = match seq_idx { Ok(x) => x, Err(_) => return Ok(None), }; let sequence = &self.sequences[seq_idx]; let idx = sequence .rows .binary_search_by(|row| row.address.cmp(&probe)); let idx = match idx { Ok(x) => x, Err(0) => return Ok(None), Err(x) => x - 1, }; Ok(Some(self.row_location(&sequence.rows[idx]))) } pub(crate) fn find_location_range( &self, probe_low: u64, probe_high: u64, ) -> Result, Error> { // Find index for probe_low. let seq_idx = self.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(x) => x, // probe below sequence, but range could overlap }; let row_idx = if let Some(seq) = self.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(LineLocationRangeIter { lines: self, seq_idx, row_idx, probe_high, }) } } pub(crate) struct LineLocationRangeIter<'ctx> { lines: &'ctx Lines, seq_idx: usize, row_idx: usize, probe_high: u64, } impl<'ctx> Iterator for LineLocationRangeIter<'ctx> { type Item = (u64, u64, Location<'ctx>); fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> { while let Some(seq) = self.lines.sequences.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 nextaddr = seq .rows .get(self.row_idx + 1) .map(|row| row.address) .unwrap_or(seq.end); let item = ( row.address, nextaddr - row.address, self.lines.row_location(row), ); self.row_idx += 1; return Some(item); } None => { self.seq_idx += 1; self.row_idx = 0; } } } None } } fn render_file( dw_unit: gimli::UnitRef, file: &gimli::FileEntry, header: &gimli::LineProgramHeader, ) -> Result { 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, dw_unit.attr_string(directory)?.to_string_lossy()?.as_ref(), ); } } path_push( &mut path, dw_unit .attr_string(file.path_name())? .to_string_lossy()? .as_ref(), ); Ok(path) } 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(":\\") } addr2line-0.24.2/src/loader.rs000064400000000000000000000372241046102023000141700ustar 00000000000000use alloc::borrow::Cow; use alloc::boxed::Box; use alloc::sync::Arc; use alloc::vec::Vec; use std::ffi::OsStr; use std::fs::File; use std::path::{Path, PathBuf}; use memmap2::Mmap; use object::{Object, ObjectMapFile, ObjectSection, SymbolMap, SymbolMapName}; use typed_arena::Arena; use crate::lazy::LazyCell; use crate::{ Context, FrameIter, Location, LocationRangeIter, LookupContinuation, LookupResult, SplitDwarfLoad, }; /// The type used by [`Loader`] for reading DWARF data. /// /// This is used in the return types of the methods of [`Loader`]. // TODO: use impl Trait when stable pub type LoaderReader<'a> = gimli::EndianSlice<'a, gimli::RunTimeEndian>; type Error = Box; type Result = std::result::Result; /// A loader for the DWARF data required for a `Context`. /// /// For performance reasons, a [`Context`] normally borrows the input data. /// However, that means the input data must outlive the `Context`, which /// is inconvenient for long-lived `Context`s. /// This loader uses an arena to store the input data, together with the /// `Context` itself. This ensures that the input data lives as long as /// the `Context`. /// /// The loader performs some additional tasks: /// - Loads the symbol table from the executable file (see [`Self::find_symbol`]). /// - Loads Mach-O dSYM files that are located next to the executable file. /// - Locates and loads split DWARF files (DWO and DWP). pub struct Loader { internal: LoaderInternal<'static>, arena_data: Arena>, arena_mmap: Arena, } impl Loader { /// Load the DWARF data for an executable file and create a `Context`. #[inline] pub fn new(path: impl AsRef) -> Result { Self::new_with_sup(path, None::<&Path>) } /// Load the DWARF data for an executable file and create a `Context`. /// /// Optionally also use a supplementary object file. pub fn new_with_sup( path: impl AsRef, sup_path: Option>, ) -> Result { let arena_data = Arena::new(); let arena_mmap = Arena::new(); let internal = LoaderInternal::new( path.as_ref(), sup_path.as_ref().map(AsRef::as_ref), &arena_data, &arena_mmap, )?; Ok(Loader { // Convert to static lifetime to allow self-reference by `internal`. // `internal` is only accessed through `borrow_internal`, which ensures // that the static lifetime does not leak. internal: unsafe { core::mem::transmute::, LoaderInternal<'static>>(internal) }, arena_data, arena_mmap, }) } fn borrow_internal<'a, F, T>(&'a self, f: F) -> T where F: FnOnce(&'a LoaderInternal<'a>, &'a Arena>, &'a Arena) -> T, { // Do not leak the static lifetime. let internal = unsafe { core::mem::transmute::<&LoaderInternal<'static>, &'a LoaderInternal<'a>>(&self.internal) }; f(internal, &self.arena_data, &self.arena_mmap) } /// Get the base address used for relative virtual addresses. /// /// Currently this is only non-zero for PE. pub fn relative_address_base(&self) -> u64 { self.borrow_internal(|i, _data, _mmap| i.relative_address_base) } /// Find the source file and line corresponding to the given virtual memory address. /// /// This calls [`Context::find_location`] with the given address. pub fn find_location(&self, probe: u64) -> Result>> { self.borrow_internal(|i, data, mmap| i.find_location(probe, data, mmap)) } /// Return source file and lines for a range of addresses. /// /// This calls [`Context::find_location_range`] with the given range. pub fn find_location_range( &self, probe_low: u64, probe_high: u64, ) -> Result> { self.borrow_internal(|i, data, mmap| { i.find_location_range(probe_low, probe_high, data, mmap) }) } /// Return an iterator for the function frames corresponding to the given virtual /// memory address. /// /// This calls [`Context::find_frames`] with the given address. pub fn find_frames(&self, probe: u64) -> Result>> { self.borrow_internal(|i, data, mmap| i.find_frames(probe, data, mmap)) } /// Find the symbol table entry corresponding to the given virtual memory address. pub fn find_symbol(&self, probe: u64) -> Option<&str> { self.borrow_internal(|i, _data, _mmap| i.find_symbol(probe)) } } struct LoaderInternal<'a> { ctx: Context>, relative_address_base: u64, symbols: SymbolMap>, dwarf_package: Option>>, // Map from address to Mach-O object file path. object_map: object::ObjectMap<'a>, // A context for each Mach-O object file. objects: Vec>>>, } impl<'a> LoaderInternal<'a> { fn new( path: &Path, sup_path: Option<&Path>, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> Result { let file = File::open(path)?; let map = arena_mmap.alloc(unsafe { Mmap::map(&file)? }); let mut object = object::File::parse(&**map)?; let relative_address_base = object.relative_address_base(); let symbols = object.symbol_map(); let object_map = object.object_map(); let mut objects = Vec::new(); objects.resize_with(object_map.objects().len(), LazyCell::new); // Load supplementary object file. // TODO: use debuglink and debugaltlink let sup_map; let sup_object = if let Some(sup_path) = sup_path { let sup_file = File::open(sup_path)?; sup_map = arena_mmap.alloc(unsafe { Mmap::map(&sup_file)? }); Some(object::File::parse(&**sup_map)?) } else { None }; // Load Mach-O dSYM file, ignoring errors. if let Some(map) = (|| { let uuid = object.mach_uuid().ok()??; path.parent()?.read_dir().ok()?.find_map(|candidate| { let candidate = candidate.ok()?; let path = candidate.path(); if path.extension().and_then(OsStr::to_str) != Some("dSYM") { return None; } let path = path.join("Contents/Resources/DWARF"); path.read_dir().ok()?.find_map(|candidate| { let candidate = candidate.ok()?; let path = candidate.path(); let file = File::open(path).ok()?; let map = unsafe { Mmap::map(&file) }.ok()?; let object = object::File::parse(&*map).ok()?; if object.mach_uuid() == Ok(Some(uuid)) { Some(map) } else { None } }) }) })() { let map = arena_mmap.alloc(map); object = object::File::parse(&**map)?; } // Load the DWARF sections. let endian = if object.is_little_endian() { gimli::RunTimeEndian::Little } else { gimli::RunTimeEndian::Big }; let mut dwarf = gimli::Dwarf::load(|id| load_section(Some(id.name()), &object, endian, arena_data))?; if let Some(sup_object) = &sup_object { dwarf.load_sup(|id| load_section(Some(id.name()), sup_object, endian, arena_data))?; } dwarf.populate_abbreviations_cache(gimli::AbbreviationsCacheStrategy::Duplicates); let ctx = Context::from_dwarf(dwarf)?; // Load the DWP file, ignoring errors. let dwarf_package = (|| { let mut dwp_path = path.to_path_buf(); 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()); dwp_path.set_extension(dwp_extension); let dwp_file = File::open(&dwp_path).ok()?; let map = arena_mmap.alloc(unsafe { Mmap::map(&dwp_file) }.ok()?); let dwp_object = object::File::parse(&**map).ok()?; let endian = if dwp_object.is_little_endian() { gimli::RunTimeEndian::Little } else { gimli::RunTimeEndian::Big }; let empty = gimli::EndianSlice::new(&[][..], endian); gimli::DwarfPackage::load( |id| load_section(id.dwo_name(), &dwp_object, endian, arena_data), empty, ) .ok() })(); Ok(LoaderInternal { ctx, relative_address_base, symbols, dwarf_package, object_map, objects, }) } fn ctx( &self, probe: u64, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> (&Context>, u64) { self.object_ctx(probe, arena_data, arena_mmap) .unwrap_or((&self.ctx, probe)) } fn object_ctx( &self, probe: u64, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> Option<(&Context>, u64)> { let symbol = self.object_map.get(probe)?; let object_context = self.objects[symbol.object_index()] .borrow_with(|| { ObjectContext::new(symbol.object(&self.object_map), arena_data, arena_mmap) }) .as_ref()?; object_context.ctx(symbol.name(), probe - symbol.address()) } fn find_symbol(&self, probe: u64) -> Option<&str> { self.symbols.get(probe).map(|x| x.name()) } fn find_location( &'a self, probe: u64, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> Result>> { let (ctx, probe) = self.ctx(probe, arena_data, arena_mmap); Ok(ctx.find_location(probe)?) } fn find_location_range( &self, probe_low: u64, probe_high: u64, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> Result> { let (ctx, probe) = self.ctx(probe_low, arena_data, arena_mmap); // TODO: handle ranges that cover multiple objects let probe_high = probe + (probe_high - probe_low); Ok(ctx.find_location_range(probe, probe_high)?) } fn find_frames( &self, probe: u64, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> Result> { let (ctx, probe) = self.ctx(probe, arena_data, arena_mmap); let mut frames = ctx.find_frames(probe); loop { let (load, continuation) = match frames { LookupResult::Output(output) => return Ok(output?), LookupResult::Load { load, continuation } => (load, continuation), }; let r = self.load_dwo(load, arena_data, arena_mmap)?; frames = continuation.resume(r); } } fn load_dwo( &self, load: SplitDwarfLoad>, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> Result>>>> { // Load the DWO file from the DWARF package, if available. if let Some(dwp) = self.dwarf_package.as_ref() { if let Some(cu) = dwp.find_cu(load.dwo_id, &load.parent)? { return Ok(Some(Arc::new(cu))); } } // Determine the path to the DWO file. let mut path = PathBuf::new(); if let Some(p) = load.comp_dir.as_ref() { path.push(convert_path(p.slice())?); } let Some(p) = load.path.as_ref() else { return Ok(None); }; path.push(convert_path(p.slice())?); // Load the DWO file, ignoring errors. let dwo = (|| { let file = File::open(&path).ok()?; let map = arena_mmap.alloc(unsafe { Mmap::map(&file) }.ok()?); let object = object::File::parse(&**map).ok()?; let endian = if object.is_little_endian() { gimli::RunTimeEndian::Little } else { gimli::RunTimeEndian::Big }; let mut dwo_dwarf = gimli::Dwarf::load(|id| load_section(id.dwo_name(), &object, endian, arena_data)) .ok()?; let dwo_unit_header = dwo_dwarf.units().next().ok()??; let dwo_unit = dwo_dwarf.unit(dwo_unit_header).ok()?; if dwo_unit.dwo_id != Some(load.dwo_id) { return None; } dwo_dwarf.make_dwo(&load.parent); Some(Arc::new(dwo_dwarf)) })(); Ok(dwo) } } struct ObjectContext<'a> { ctx: Context>, symbols: SymbolMap>, } impl<'a> ObjectContext<'a> { fn new( object: &ObjectMapFile<'a>, arena_data: &'a Arena>, arena_mmap: &'a Arena, ) -> Option { let file = File::open(convert_path(object.path()).ok()?).ok()?; let map = &**arena_mmap.alloc(unsafe { Mmap::map(&file) }.ok()?); let data = if let Some(member_name) = object.member() { let archive = object::read::archive::ArchiveFile::parse(map).ok()?; let member = archive.members().find_map(|member| { let member = member.ok()?; if member.name() == member_name { Some(member) } else { None } })?; member.data(map).ok()? } else { map }; let object = object::File::parse(data).ok()?; let endian = if object.is_little_endian() { gimli::RunTimeEndian::Little } else { gimli::RunTimeEndian::Big }; let dwarf = gimli::Dwarf::load(|id| load_section(Some(id.name()), &object, endian, arena_data)) .ok()?; let ctx = Context::from_dwarf(dwarf).ok()?; let symbols = object.symbol_map(); Some(ObjectContext { ctx, symbols }) } fn ctx(&self, symbol_name: &[u8], probe: u64) -> Option<(&Context>, u64)> { self.symbols .symbols() .iter() .find(|symbol| symbol.name().as_bytes() == symbol_name) .map(|symbol| (&self.ctx, probe + symbol.address())) } } fn load_section<'input, Endian: gimli::Endianity>( name: Option<&'static str>, file: &object::File<'input>, endian: Endian, arena_data: &'input Arena>, ) -> Result> { let data = match name.and_then(|name| file.section_by_name(name)) { Some(section) => match section.uncompressed_data()? { Cow::Borrowed(b) => b, Cow::Owned(b) => arena_data.alloc(b), }, None => &[], }; Ok(gimli::EndianSlice::new(data, endian)) } #[cfg(unix)] fn convert_path(bytes: &[u8]) -> Result { use std::os::unix::ffi::OsStrExt; let s = OsStr::from_bytes(bytes); Ok(PathBuf::from(s)) } #[cfg(not(unix))] fn convert_path(bytes: &[u8]) -> Result { let s = std::str::from_utf8(bytes)?; Ok(PathBuf::from(s)) } addr2line-0.24.2/src/lookup.rs000064400000000000000000000212331046102023000142240ustar 00000000000000use alloc::sync::Arc; use core::marker::PhantomData; use core::ops::ControlFlow; /// This struct contains the information needed to find split DWARF data /// and to produce a `gimli::Dwarf` for it. pub struct SplitDwarfLoad { /// 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, /// A path on the filesystem, relative to `comp_dir` to find this dwo. pub path: Option, /// 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>, } /// 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 `Output` variant, or information about the split /// DWARF that is required and a continuation to invoke once it is available /// in the `Load` variant. /// /// This enum is intended to be used in a loop like so: /// ```no_run /// # use addr2line::*; /// # use std::sync::Arc; /// # let ctx: Context> = todo!(); /// # let do_split_dwarf_load = |load: SplitDwarfLoad>| -> Option>>> { 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 { /// The lookup requires split DWARF data to be loaded. Load { /// The information needed to find the split DWARF data. load: SplitDwarfLoad<::Buf>, /// The continuation to resume with the loaded split DWARF data. continuation: L, }, /// The lookup has completed and produced an output. Output(::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>>) -> LookupResult; } impl LookupResult { /// 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), }; } } pub(crate) fn map T>( self, f: F, ) -> LookupResult> { match self { LookupResult::Output(t) => LookupResult::Output(f(t)), LookupResult::Load { load, continuation } => LookupResult::Load { load, continuation: MappedLookup { original: continuation, mutator: f, }, }, } } pub(crate) fn unwrap(self) -> L::Output { match self { LookupResult::Output(t) => t, LookupResult::Load { .. } => unreachable!("Internal API misuse"), } } } pub(crate) struct SimpleLookup where F: FnOnce(Option>>) -> T, R: gimli::Reader, { f: F, phantom: PhantomData<(T, R)>, } impl SimpleLookup where F: FnOnce(Option>>) -> T, R: gimli::Reader, { pub(crate) fn new_complete(t: F::Output) -> LookupResult> { LookupResult::Output(t) } pub(crate) fn new_needs_load( load: SplitDwarfLoad, f: F, ) -> LookupResult> { LookupResult::Load { load, continuation: SimpleLookup { f, phantom: PhantomData, }, } } } impl LookupContinuation for SimpleLookup where F: FnOnce(Option>>) -> T, R: gimli::Reader, { type Output = T; type Buf = R; fn resume(self, v: Option>>) -> LookupResult { LookupResult::Output((self.f)(v)) } } pub(crate) struct MappedLookup where L: LookupContinuation, F: FnOnce(L::Output) -> T, { original: L, mutator: F, } impl LookupContinuation for MappedLookup where L: LookupContinuation, F: FnOnce(L::Output) -> T, { type Output = T; type Buf = L::Buf; fn resume(self, v: Option>>) -> LookupResult { 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. pub(crate) struct LoopingLookup where L: LookupContinuation, F: FnMut(L::Output) -> ControlFlow>, { continuation: L, mutator: F, } impl LoopingLookup where L: LookupContinuation, F: FnMut(L::Output) -> ControlFlow>, { pub(crate) fn new_complete(t: T) -> LookupResult { LookupResult::Output(t) } pub(crate) fn new_lookup(mut r: LookupResult, mut mutator: F) -> LookupResult { // 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 LookupContinuation for LoopingLookup where L: LookupContinuation, F: FnMut(L::Output) -> ControlFlow>, { type Output = T; type Buf = L::Buf; fn resume(self, v: Option>>) -> LookupResult { let r = self.continuation.resume(v); LoopingLookup::new_lookup(r, self.mutator) } } addr2line-0.24.2/src/unit.rs000064400000000000000000000514631046102023000137020ustar 00000000000000use alloc::boxed::Box; use alloc::sync::Arc; use alloc::vec::Vec; use core::cmp; use crate::lazy::LazyResult; use crate::{ Context, DebugFile, Error, Function, Functions, LazyFunctions, LazyLines, LineLocationRangeIter, Lines, Location, LookupContinuation, LookupResult, RangeAttributes, SimpleLookup, SplitDwarfLoad, }; pub(crate) struct UnitRange { unit_id: usize, min_begin: u64, range: gimli::Range, } pub(crate) struct ResUnit { offset: gimli::DebugInfoOffset, dw_unit: gimli::Unit, pub(crate) lang: Option, lines: LazyLines, functions: LazyFunctions, dwo: LazyResult>>>, } type UnitRef<'unit, R> = (DebugFile, gimli::UnitRef<'unit, R>); impl ResUnit { pub(crate) fn unit_ref<'a>(&'a self, sections: &'a gimli::Dwarf) -> gimli::UnitRef<'a, R> { gimli::UnitRef::new(sections, &self.dw_unit) } /// Returns the DWARF sections and the unit. /// /// Loads the DWO unit if necessary. pub(crate) fn dwarf_and_unit<'unit, 'ctx: 'unit>( &'unit self, ctx: &'ctx Context, ) -> LookupResult< SimpleLookup< Result, Error>, R, impl FnOnce(Option>>) -> Result, Error>, >, > { let map_dwo = move |dwo: &'unit Result>>, Error>| match dwo { Ok(Some(dwo)) => Ok((DebugFile::Dwo, dwo.unit_ref())), Ok(None) => Ok((DebugFile::Primary, self.unit_ref(&*ctx.sections))), Err(e) => Err(*e), }; let complete = |dwo| SimpleLookup::new_complete(map_dwo(dwo)); if let Some(dwo) = self.dwo.borrow() { return complete(dwo); } let dwo_id = match self.dw_unit.dwo_id { None => { return complete(self.dwo.borrow_with(|| Ok(None))); } 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) => { return complete(self.dwo.borrow_with(|| Err(e))); } }; let process_dwo = move |dwo_dwarf: Option>>| { 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(DwoUnit { sections: dwo_dwarf, dw_unit: dwo_unit, }))) }; SimpleLookup::new_needs_load( SplitDwarfLoad { dwo_id, comp_dir, path, parent: ctx.sections.clone(), }, move |dwo_dwarf| map_dwo(self.dwo.borrow_with(|| process_dwo(dwo_dwarf))), ) } pub(crate) fn parse_lines(&self, sections: &gimli::Dwarf) -> Result, 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(self.unit_ref(sections), ilnp).map(Some) } pub(crate) fn parse_functions<'unit, 'ctx: 'unit>( &'unit self, ctx: &'ctx Context, ) -> LookupResult, Error>, Buf = R>> { self.dwarf_and_unit(ctx).map(move |r| { let (_file, unit) = r?; self.functions.borrow(unit) }) } pub(crate) fn parse_inlined_functions<'unit, 'ctx: 'unit>( &'unit self, ctx: &'ctx Context, ) -> LookupResult, Buf = R> + 'unit> { self.dwarf_and_unit(ctx).map(move |r| { let (file, unit) = r?; self.functions .borrow(unit)? .parse_inlined_functions(file, unit, ctx) }) } pub(crate) fn find_location( &self, probe: u64, sections: &gimli::Dwarf, ) -> Result>, Error> { let Some(lines) = self.parse_lines(sections)? else { return Ok(None); }; lines.find_location(probe) } #[inline] pub(crate) fn find_location_range( &self, probe_low: u64, probe_high: u64, sections: &gimli::Dwarf, ) -> Result>, Error> { let Some(lines) = self.parse_lines(sections)? else { return Ok(None); }; lines.find_location_range(probe_low, probe_high).map(Some) } pub(crate) fn find_function_or_location<'unit, 'ctx: 'unit>( &'unit self, probe: u64, ctx: &'ctx Context, ) -> LookupResult< impl LookupContinuation< Output = Result<(Option<&'unit Function>, Option>), Error>, Buf = R, >, > { self.dwarf_and_unit(ctx).map(move |r| { let (file, unit) = r?; let functions = self.functions.borrow(unit)?; let function = match functions.find_address(probe) { Some(address) => { let function_index = functions.addresses[address].function; let function = &functions.functions[function_index]; Some(function.borrow(file, unit, ctx)?) } None => None, }; let location = self.find_location(probe, unit.dwarf)?; Ok((function, location)) }) } } pub(crate) struct ResUnits { ranges: Box<[UnitRange]>, units: Box<[ResUnit]>, } impl ResUnits { pub(crate) fn parse(sections: &gimli::Dwarf) -> Result { // 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. let mut need_unit_range = match header.type_() { gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue, gimli::UnitType::Partial => { // Partial units are only needed for references from other units. // They shouldn't have any address ranges. false } _ => true, }; let dw_unit = match sections.unit(header) { Ok(dw_unit) => dw_unit, Err(_) => continue, }; let dw_unit_ref = gimli::UnitRef::new(sections, &dw_unit); let mut lang = None; if need_unit_range { let mut entries = dw_unit_ref.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(dw_unit_ref.address(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(dw_unit_ref.address(index)?); } gimli::AttributeValue::Udata(val) => ranges.size = Some(val), _ => {} }, gimli::DW_AT_ranges => { ranges.ranges_offset = dw_unit_ref.attr_ranges_offset(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, min_begin: 0, }); need_unit_range = false; } } } } else { need_unit_range &= !ranges.for_each_range(dw_unit_ref, |range| { unit_ranges.push(UnitRange { range, unit_id, min_begin: 0, }); })?; } } let lines = LazyLines::new(); if need_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_ref.line_program { if let Ok(lines) = lines.borrow(dw_unit_ref, ilnp) { for range in lines.ranges() { unit_ranges.push(UnitRange { range, unit_id, min_begin: 0, }) } } } } res_units.push(ResUnit { offset, dw_unit, lang, lines, functions: LazyFunctions::new(), dwo: LazyResult::new(), }); } // Sort this for faster lookup in `Self::find_range`. unit_ranges.sort_by_key(|i| i.range.end); // Calculate the `min_begin` field now that we've determined the order of // CUs. let mut min = !0; for i in unit_ranges.iter_mut().rev() { min = min.min(i.range.begin); i.min_begin = min; } Ok(ResUnits { ranges: unit_ranges.into_boxed_slice(), units: res_units.into_boxed_slice(), }) } pub(crate) fn iter(&self) -> impl Iterator> { self.units.iter() } pub(crate) fn find_offset( &self, offset: gimli::DebugInfoOffset, ) -> Result<&gimli::Unit, Error> { 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) => Err(gimli::Error::NoEntryAtGivenOffset), Err(i) => Ok(&self.units[i - 1].dw_unit), } } /// 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. pub(crate) fn find(&self, probe: u64) -> impl Iterator> { self.find_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] pub(crate) fn find_range( &self, probe_low: u64, probe_high: u64, ) -> impl Iterator, &gimli::Range)> { // Find the position of the next range after a range which // ends at `probe_low` or lower. let pos = match self .ranges .binary_search_by_key(&probe_low, |i| i.range.end) { Ok(i) => i + 1, // Range `i` ends at exactly `probe_low`. Err(i) => i, // Range `i - 1` ends at a lower address. }; // Iterate from that position to find matching CUs. self.ranges[pos..] .iter() .take_while(move |i| { // We know that this CU's end is at least `probe_low` because // of our sorted array. debug_assert!(i.range.end >= probe_low); // Each entry keeps track of the minimum begin address for the // remainder of the array of unit ranges. If our probe is before // the minimum range begin of this entry, then it's guaranteed // to not fit in any subsequent entries, so we break out. probe_high > i.min_begin }) .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)) }) } pub(crate) fn find_location_range<'a>( &'a self, probe_low: u64, probe_high: u64, sections: &'a gimli::Dwarf, ) -> Result, Error> { let unit_iter = Box::new(self.find_range(probe_low, probe_high)); Ok(LocationRangeIter { unit_iter, iter: None, probe_low, probe_high, sections, }) } } /// A DWO unit has its own DWARF sections. struct DwoUnit { sections: Arc>, dw_unit: gimli::Unit, } impl DwoUnit { fn unit_ref(&self) -> gimli::UnitRef { gimli::UnitRef::new(&self.sections, &self.dw_unit) } } pub(crate) struct SupUnit { offset: gimli::DebugInfoOffset, dw_unit: gimli::Unit, } pub(crate) struct SupUnits { units: Box<[SupUnit]>, } impl Default for SupUnits { fn default() -> Self { SupUnits { units: Box::default(), } } } impl SupUnits { pub(crate) fn parse(sections: &gimli::Dwarf) -> Result { 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(SupUnits { units: sup_units.into_boxed_slice(), }) } pub(crate) fn find_offset( &self, offset: gimli::DebugInfoOffset, ) -> Result<&gimli::Unit, Error> { 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) => Err(gimli::Error::NoEntryAtGivenOffset), Err(i) => Ok(&self.units[i - 1].dw_unit), } } } /// 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, &'ctx gimli::Range)> + 'ctx>, iter: Option>, probe_low: u64, probe_high: u64, sections: &'ctx gimli::Dwarf, } impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> { fn next_loc(&mut self) -> Result)>, 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.next_loc().unwrap_or_default() } } #[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, Self::Error> { self.next_loc() } }