gimli-0.31.1/.cargo_vcs_info.json0000644000000001360000000000100122170ustar { "git": { "sha1": "7e9d923a98c5eeed4d7a8b8cb32475d1ce16ced2" }, "path_in_vcs": "" }gimli-0.31.1/CHANGELOG.md000064400000000000000000001174631046102023000126340ustar 00000000000000# `gimli` Change Log -------------------------------------------------------------------------------- ## 0.31.1 Released 2024/10/04. ### Changed * Changed `read::Evaluation::evaluate` to validate `DW_OP_deref_size`. [#739](https://github.com/gimli-rs/gimli/pull/739) * Changed `write::LineProgram` to allow use of file index 0 for DWARF version 5. [#740](https://github.com/gimli-rs/gimli/pull/740) * Improved the workaround for reading zero length entries in `.debug_frame`. [#741](https://github.com/gimli-rs/gimli/pull/741) * Implemented `Default` for `read::DwarfSections` and `read::DwarfPackageSections`. [#742](https://github.com/gimli-rs/gimli/pull/742) * Changed `read::ArangeEntryIter` to handle tombstones in `.debug_aranges`. [#743](https://github.com/gimli-rs/gimli/pull/743) * Improved handling handling of 0 for tombstones in `DW_LNE_set_address` and address pairs in ranges and locations. [#750](https://github.com/gimli-rs/gimli/pull/750) * Changed the `read::ArrayLike` trait implementation to use const generics. [#752](https://github.com/gimli-rs/gimli/pull/752) ### Added * Added `MIPS::HI` and `MIPS::LO`. [#749](https://github.com/gimli-rs/gimli/pull/749) -------------------------------------------------------------------------------- ## 0.31.0 Released 2024/07/16. ### Breaking changes * Deleted support for segment selectors. [#720](https://github.com/gimli-rs/gimli/pull/720) * Added `read::FileEntry::source` and deleted `Copy` implementation. [#728](https://github.com/gimli-rs/gimli/pull/728) * Changed `read::LineRow::execute` to return a `Result`. [#731](https://github.com/gimli-rs/gimli/pull/731) * Deleted `Display` implementation for `read::LineInstruction`. [#734](https://github.com/gimli-rs/gimli/pull/734) * Changed `read::Error` to be non-exhaustive. ### Changed * Fixed `Hash` implementation for `read::EndianReader`. [#723](https://github.com/gimli-rs/gimli/pull/723) * Changed `read::EhFrameHdr::parse` to validate the FDE count encoding. [#725](https://github.com/gimli-rs/gimli/pull/725) * Changed address overflow to be an error for `read::UnwindTableRow`, `read::LineRow`, and `read::ArangeEntry`. [#730](https://github.com/gimli-rs/gimli/pull/730) [#731](https://github.com/gimli-rs/gimli/pull/731) [#732](https://github.com/gimli-rs/gimli/pull/732) * Changed wrapping addition for 32-bit addresses to wrap at 32 bits instead of at 64 bits. [#733](https://github.com/gimli-rs/gimli/pull/733) * Added earlier validation of address sizes. [#733](https://github.com/gimli-rs/gimli/pull/733) ### Added * Added `read::IndexSectionId::section_id`. [#719](https://github.com/gimli-rs/gimli/pull/719) * Added `read::FrameDescriptionEntry::end_address`. [#727](https://github.com/gimli-rs/gimli/pull/727) * Added support for `DW_LNCT_LLVM_source`. [#728](https://github.com/gimli-rs/gimli/pull/728) -------------------------------------------------------------------------------- ## 0.30.0 Released 2024/05/26. ### Breaking changes * Added context to some `read::Error` variants. [#703](https://github.com/gimli-rs/gimli/pull/703) * Changed type of `read::UnitIndexSection::section` to `IndexSectionId`. [#716](https://github.com/gimli-rs/gimli/pull/716) ### Changed * Fixed `write::Operation::ImplicitPointer::size`. [#712](https://github.com/gimli-rs/gimli/pull/712) * Changed `read::RngListIter` and `read::LocListIter` to skip ranges where the end is before the beginning, instead of returning an error. [#715](https://github.com/gimli-rs/gimli/pull/715) * Fixed clippy warnings. [#713](https://github.com/gimli-rs/gimli/pull/713) ### Added * Added `read::UnitRef`. [#711](https://github.com/gimli-rs/gimli/pull/711) -------------------------------------------------------------------------------- ## 0.29.0 Released 2024/04/11. ### Breaking changes * Changed `Reader` type parameter to `ReaderOffset` for `read::UnwindContext` and related types. Replaced `Expression` with `UnwindExpression` in unwind information types. [#703](https://github.com/gimli-rs/gimli/pull/703) ### Changed * Changed `write::Sections::for_each` and `for_each_mut` to specify section lifetime. [#699](https://github.com/gimli-rs/gimli/pull/699) * Fixed writing unwind information with an LSDA encoding that is not `DW_EH_PE_absptr`. [#704](https://github.com/gimli-rs/gimli/pull/704) * Fixed parsing for an empty DWP index. [#706](https://github.com/gimli-rs/gimli/pull/706) * Improved error handling in `read::Unit::dwo_name`. [#693](https://github.com/gimli-rs/gimli/pull/693) * Fixed warnings. [#692](https://github.com/gimli-rs/gimli/pull/692) [#694](https://github.com/gimli-rs/gimli/pull/694) [#695](https://github.com/gimli-rs/gimli/pull/695) [#696](https://github.com/gimli-rs/gimli/pull/696) ### Added * Added MIPS register definitions. [#690](https://github.com/gimli-rs/gimli/pull/690) * Added PowerPC register definitions. [#691](https://github.com/gimli-rs/gimli/pull/691) * Added `read::DwarfSections` and `read::DwarfPackageSections`. [#698](https://github.com/gimli-rs/gimli/pull/698) * Implemented `BitOr` for `DwEhPe`. [#709](https://github.com/gimli-rs/gimli/pull/709) * Added `read::Relocate`, `read::RelocateReader`, and `write::RelocateWriter`. [#709](https://github.com/gimli-rs/gimli/pull/709) -------------------------------------------------------------------------------- ## 0.28.1 Released 2023/11/24. ### Changed * Changed `read::AbbreviationsCache` to require manual population using `Dwarf::populate_abbreviations_cache`. [#679](https://github.com/gimli-rs/gimli/pull/679) * Changed the default `read::UnwindContextStorage` to use `Box` instead of `Vec` so that its memory usage is limited. [#687](https://github.com/gimli-rs/gimli/pull/687) * Changed `read::UnwindTable::new` to always reset the context, because previous errors may have left the context in an invalid state. [#684](https://github.com/gimli-rs/gimli/pull/684) * Changed the `Debug` implementation for `read::EndianSlice` to limit the number of bytes it displays. [#686](https://github.com/gimli-rs/gimli/pull/686) ### Added * Added more AArch64 register definitions. [#680](https://github.com/gimli-rs/gimli/pull/680) * Added `read::Unit::new_with_abbreviations`. [#677](https://github.com/gimli-rs/gimli/pull/677) * Added `read::Evaluation::value_result`. [#676](https://github.com/gimli-rs/gimli/pull/676) -------------------------------------------------------------------------------- ## 0.28.0 Released 2023/08/12. ### Breaking changes * Deleted `impl From for &[u8]`. Use `EndianSlice::slice` instead. [#669](https://github.com/gimli-rs/gimli/pull/669) * Deleted `impl Index for EndianSlice` and `impl Index> for EndianSlice`. [#669](https://github.com/gimli-rs/gimli/pull/669) * Replaced `impl From for u64` with `Pointer::pointer`. [#670](https://github.com/gimli-rs/gimli/pull/670) * Updated `fallible-iterator` to 0.3.0. [#672](https://github.com/gimli-rs/gimli/pull/672) * Changed some optional dependencies to use the `dep:` feature syntax. [#672](https://github.com/gimli-rs/gimli/pull/672) * Added `non_exhaustive` attribute to `read::RegisterRule`, `read::CallFrameInstruction`, and `write::CallFrameInstruction`. [#673](https://github.com/gimli-rs/gimli/pull/673) ### Changed * The minimum supported rust version for the `read` feature and its dependencies increased to 1.60.0. * The minimum supported rust version for other features increased to 1.65.0. ### Added * Added `Vendor`, `read::DebugFrame::set_vendor`, and `read::EhFrame::set_vendor`. [#673](https://github.com/gimli-rs/gimli/pull/673) * Added more ARM and AArch64 register definitions, and `DW_CFA_AARCH64_negate_ra_state` support. [#673](https://github.com/gimli-rs/gimli/pull/673) -------------------------------------------------------------------------------- ## 0.27.3 Released 2023/06/14. ### Changed * Excluded test fixtures from published package. [#661](https://github.com/gimli-rs/gimli/pull/661) ### Added * Added `FallibleIterator` implementation for `read::OperationIter`. [#649](https://github.com/gimli-rs/gimli/pull/649) * Added `DW_AT_GNU_deleted` constant. [#658](https://github.com/gimli-rs/gimli/pull/658) -------------------------------------------------------------------------------- ## 0.27.2 Released 2023/02/15. ### Added * Added support for tombstones in `read::LineRows`. [#642](https://github.com/gimli-rs/gimli/pull/642) -------------------------------------------------------------------------------- ## 0.27.1 Released 2023/01/23. ### Added * Added `SectionId::xcoff_name` and `read::Section::xcoff_section_name`. [#635](https://github.com/gimli-rs/gimli/pull/635) * Added `read::Dwarf::make_dwo` and `read::Unit::dwo_name`. [#637](https://github.com/gimli-rs/gimli/pull/637) ### Changed * Changed `read::DwarfPackage::sections` to handle supplementary files. [#638](https://github.com/gimli-rs/gimli/pull/638) -------------------------------------------------------------------------------- ## 0.27.0 Released 2022/11/23. ### Breaking changes * Added `read::Dwarf::abbreviations_cache` to cache abbreviations at offset 0. Changed `read::Dwarf::abbreviations` to return `Result>`, and changed `read::Unit::abbreviations` to `Arc`. [#628](https://github.com/gimli-rs/gimli/pull/628) ### Added * Added LoongArch register definitions. [#624](https://github.com/gimli-rs/gimli/pull/624) * Added support for tombstones in `read::LocListIter` and `read::RngListIter`. [#631](https://github.com/gimli-rs/gimli/pull/631) -------------------------------------------------------------------------------- ## 0.26.2 Released 2022/07/16. ### Changed * Fixed CFI personality encoding when writing. [#609](https://github.com/gimli-rs/gimli/pull/609) * Fixed use of raw pointer for mutation, detected by Miri. [#614](https://github.com/gimli-rs/gimli/pull/614) * Fixed `DW_OP_GNU_implicit_pointer` handling for DWARF version 2. [#618](https://github.com/gimli-rs/gimli/pull/618) ### Added * Added `read::EhHdrTable::iter`. [#619](https://github.com/gimli-rs/gimli/pull/619) -------------------------------------------------------------------------------- ## 0.26.1 Released 2021/11/02. ### Changed * Fixed segmentation fault in `ArrayVec>::into_vec`, which may be used by `read::Evaluation::result`. This regression was introduced in 0.26.0. [#601](https://github.com/gimli-rs/gimli/pull/601) -------------------------------------------------------------------------------- ## 0.26.0 Released 2021/10/24. ### Breaking changes * Removed `read::UninitializedUnwindContext`. Use `Box` instead. [#593](https://github.com/gimli-rs/gimli/pull/593) * Renamed `read::Error::CfiStackFull` to `StackFull`. [#595](https://github.com/gimli-rs/gimli/pull/595) * Added `UnwindContextStorage` type parameter to `read::UnwindContext`, `read::UnwindTable`, `read::UnwindTableRow`, and `read::RegisterRuleMap`. [#595](https://github.com/gimli-rs/gimli/pull/595) * Added `EvaluationStorage` type parameter to `read::Evaluation`. [#595](https://github.com/gimli-rs/gimli/pull/595) * Added `read::SectionId::DebugCuIndex` and `read::SectionId::DebugTuIndex`. [#588](https://github.com/gimli-rs/gimli/pull/588) ### Changed * Fixed `DW_EH_PE_pcrel` handling in default `write::Writer::write_eh_pointer` implementation. [#576](https://github.com/gimli-rs/gimli/pull/576) * Fixed `read::AttributeSpecification::size` for some forms. [#597](https://github.com/gimli-rs/gimli/pull/597) * Display more unit details in dwarfdump. [#584](https://github.com/gimli-rs/gimli/pull/584) ### Added * Added `write::DebuggingInformationEntry::delete_child`. [#570](https://github.com/gimli-rs/gimli/pull/570) * Added ARM and AArch64 register definitions. [#574](https://github.com/gimli-rs/gimli/pull/574) [#577](https://github.com/gimli-rs/gimli/pull/577) * Added RISC-V register definitions. [#579](https://github.com/gimli-rs/gimli/pull/579) * Added `read::DwarfPackage`, `read::DebugCuIndex`, and `read::DebugTuIndex`. [#588](https://github.com/gimli-rs/gimli/pull/588) * Added `read-core` feature to allow building without `liballoc`. [#596](https://github.com/gimli-rs/gimli/pull/596) * Added `read::EntriesRaw::skip_attributes`. [#597](https://github.com/gimli-rs/gimli/pull/597) -------------------------------------------------------------------------------- ## 0.25.0 Released 2021/07/26. ### Breaking changes * `read::FrameDescriptionEntry::unwind_info_for_address` now returns a reference instead of cloning. [#557](https://github.com/gimli-rs/gimli/pull/557) * `read::AttributeValue::RangeListsRef` now contains a `RawRangeListsOffset` to allow handling of GNU split DWARF extensions. Use `read::Dwarf::ranges_offset_from_raw` to handle it. [#568](https://github.com/gimli-rs/gimli/pull/568) [#569](https://github.com/gimli-rs/gimli/pull/569) * Added `read::Unit::dwo_id`. [#569](https://github.com/gimli-rs/gimli/pull/569) ### Changed * `.debug_aranges` parsing now accepts version 3. [#560](https://github.com/gimli-rs/gimli/pull/560) * `read::Dwarf::attr_ranges_offset` and its callers now handle GNU split DWARF extensions. [#568](https://github.com/gimli-rs/gimli/pull/568) [#569](https://github.com/gimli-rs/gimli/pull/569) ### Added * Added `read::DebugLineStr::new`. [#556](https://github.com/gimli-rs/gimli/pull/556) * Added `read::UnwindTable::into_current_row`. [#557](https://github.com/gimli-rs/gimli/pull/557) * Added more `DW_LANG` constants. [#565](https://github.com/gimli-rs/gimli/pull/565) * dwarfdump: added DWO parent support. [#568](https://github.com/gimli-rs/gimli/pull/568) * Added `read::Dwarf` methods: `ranges_offset_from_raw`, `raw_ranges`, and `raw_locations`. [#568](https://github.com/gimli-rs/gimli/pull/568) [#569](https://github.com/gimli-rs/gimli/pull/569) -------------------------------------------------------------------------------- ## 0.24.0 Released 2021/05/01. ### Breaking changes * Minimum Rust version increased to 1.42.0. * Added `read::Dwarf::debug_aranges`. [#539](https://github.com/gimli-rs/gimli/pull/539) * Replaced `read::DebugAranges::items` with `read::DebugAranges::headers`. [#539](https://github.com/gimli-rs/gimli/pull/539) * Added `read::Operation::Wasm*`. [#546](https://github.com/gimli-rs/gimli/pull/546) * `read::LineRow::line` now returns `Option`. The `read::ColumnType::Column` variant now contains a `NonZeroU64`. [#551](https://github.com/gimli-rs/gimli/pull/551) * Replaced `read::Dwarf::debug_str_sup` with `read::Dwarf::sup`. Deleted `sup` parameter of `read::Dwarf::load`. Added `read::Dwarf::load_sup`. [#554](https://github.com/gimli-rs/gimli/pull/554) ### Added * dwarfdump: Supplementary object file support. [#552](https://github.com/gimli-rs/gimli/pull/552) ### Changed * Support `DW_FORM_addrx*` for `DW_AT_low_pc`/`DW_AT_high_pc` in `read::Dwarf`. [#541](https://github.com/gimli-rs/gimli/pull/541) * Performance improvement in `EndianReader`. [#549](https://github.com/gimli-rs/gimli/pull/549) -------------------------------------------------------------------------------- ## 0.23.0 Released 2020/10/27. ### Breaking changes * Added more variants to `read::UnitType`. Added `read::AttributeValue::DwoId` [#521](https://github.com/gimli-rs/gimli/pull/521) * Replaced `CompilationUnitHeader` and `TypeUnitHeader` with `UnitHeader`. Replaced `CompilationUnitHeadersIter` with `DebugInfoUnitHeadersIter`. Replaced `TypeUnitHeadersIter` with `DebugTypesUnitHeadersIter`. [#523](https://github.com/gimli-rs/gimli/pull/523) ### Added * Added read support for split DWARF. [#527](https://github.com/gimli-rs/gimli/pull/527) [#529](https://github.com/gimli-rs/gimli/pull/529) * Added `read::Dwarf::attr_address`. [#524](https://github.com/gimli-rs/gimli/pull/524) * Added read support for `DW_AT_GNU_addr_base` and `DW_AT_GNU_ranges_base`. [#525](https://github.com/gimli-rs/gimli/pull/525) * dwarfdump: Display index values for attributes. [#526](https://github.com/gimli-rs/gimli/pull/526) * Added `name_to_register`. [#532](https://github.com/gimli-rs/gimli/pull/532) -------------------------------------------------------------------------------- ## 0.22.0 Released 2020/07/03. ### Breaking changes * Fixed `UnitHeader::size_of_header` for DWARF 5 units. [#518](https://github.com/gimli-rs/gimli/pull/518) ### Added * Added fuzz targets in CI. [#512](https://github.com/gimli-rs/gimli/pull/512) * Added read support for `DW_OP_GNU_addr_index` and `DW_OP_GNU_const_index`. [#516](https://github.com/gimli-rs/gimli/pull/516) * Added `.dwo` support to dwarfdump. [#516](https://github.com/gimli-rs/gimli/pull/516) * Added `SectionId::dwo_name` and `Section::dwo_section_name`. [#517](https://github.com/gimli-rs/gimli/pull/517) ### Fixed * Fixed panic when reading `DW_FORM_indirect` combined with `DW_FORM_implicit_const`. [#502](https://github.com/gimli-rs/gimli/pull/502) * Fixed panic for `read::Abbreviations::get(0)`. [#505](https://github.com/gimli-rs/gimli/pull/505) * Fixed arithmetic overflow when reading `.debug_line`. [#508](https://github.com/gimli-rs/gimli/pull/508) * Fixed arithmetic overflow when reading CFI. [#509](https://github.com/gimli-rs/gimli/pull/509) * Fixed arithmetic overflow and division by zero when reading `.debug_aranges`. [#510](https://github.com/gimli-rs/gimli/pull/510) * Don't return error from `read::Unit::new` when `DW_AT_name` or `DW_AT_comp_dir` is missing. [#515](https://github.com/gimli-rs/gimli/pull/515) -------------------------------------------------------------------------------- ## 0.21.0 Released 2020/05/12. ### Breaking changes * Minimum Rust version increased to 1.38.0. * Replaced `read::Operation::Literal` with `Operation::UnsignedConstant` and `Operation::SignedConstant`. Changed `read::Operation::Bra` and `read::Operation::Skip` to contain the target offset instead of the bytecode. [#479](https://github.com/gimli-rs/gimli/pull/479) * Changed `write::Expression` to support references. Existing users can convert to use `Expression::raw`. [#479](https://github.com/gimli-rs/gimli/pull/479) * Replaced `write::AttributeValue::AnyUnitEntryRef` with `DebugInfoRef`. Renamed `write::AttributeValue::ThisUnitEntryRef` to `UnitRef`. [#479](https://github.com/gimli-rs/gimli/pull/479) * Added more optional features: `endian-reader` and `fallible-iterator`. [#495](https://github.com/gimli-rs/gimli/pull/495) [#498](https://github.com/gimli-rs/gimli/pull/498) ### Added * Added `read::Expression::operations` [#479](https://github.com/gimli-rs/gimli/pull/479) ### Fixed * Fixed newlines in `dwarfdump` example. [#470](https://github.com/gimli-rs/gimli/pull/470) * Ignore zero terminators when reading `.debug_frame` sections. [#486](https://github.com/gimli-rs/gimli/pull/486) * Increase the number of CFI register rules supported by `read::UnwindContext`. [#487](https://github.com/gimli-rs/gimli/pull/487) * Fixed version handling and return register encoding when reading `.eh_frame` sections. [#493](https://github.com/gimli-rs/gimli/pull/493) ### Changed * Added `EhFrame` and `DebugFrame` to `write::Sections`. [#492](https://github.com/gimli-rs/gimli/pull/492) * Improved performance of `write::LineProgram::generate_row`. [#476](https://github.com/gimli-rs/gimli/pull/476) * Removed use of the `byteorder`, `arrayvec` and `smallvec` crates. [#494](https://github.com/gimli-rs/gimli/pull/494) [#496](https://github.com/gimli-rs/gimli/pull/496) [#497](https://github.com/gimli-rs/gimli/pull/497) -------------------------------------------------------------------------------- ## 0.20.0 Released 2020/01/11. ### Breaking changes * Changed type of `DwTag`, `DwAt`, and `DwForm` constants. [#451](https://github.com/gimli-rs/gimli/pull/451) * Added `read/write::AttributeValue::DebugMacroRef`, and returned where required in `read::Attribute::value`. Added `SectionId::DebugMacro`. [#454](https://github.com/gimli-rs/gimli/pull/454) * Deleted `alloc` feature, and fixed `no-std` builds with stable rust. [#459](https://github.com/gimli-rs/gimli/pull/459) * Deleted `read::Error::description`, and changed `` to display what was previously the description. [#462](https://github.com/gimli-rs/gimli/pull/462) ### Added * Added GNU view constants. [#434](https://github.com/gimli-rs/gimli/pull/434) * Added `read::EntriesRaw` for low level DIE parsing. [#455](https://github.com/gimli-rs/gimli/pull/455) * Added `examples/simple-line.rs`. [#460](https://github.com/gimli-rs/gimli/pull/460) ### Fixed * Fixed handling of CFI augmentations without data. [#438](https://github.com/gimli-rs/gimli/pull/438) * dwarfdump: fix panic for malformed expressions. [#447](https://github.com/gimli-rs/gimli/pull/447) * dwarfdump: fix handling of Mach-O relocations. [#449](https://github.com/gimli-rs/gimli/pull/449) ### Changed * Improved abbreviation parsing performance. [#451](https://github.com/gimli-rs/gimli/pull/451) -------------------------------------------------------------------------------- ## 0.19.0 Released 2019/07/08. ### Breaking changes * Small API changes related to `.debug_loc` and `.debug_loclists`: added `read::RawLocListEntry::AddressOrOffsetPair` enum variant, added `write::Sections::debug_loc/debug_loclists` public members, and replaced `write::AttributeValue::LocationListsRef` with `LocationListRef`. [#425](https://github.com/gimli-rs/gimli/pull/425) ### Added * Added `read::Attribute::exprloc_value` and `read::AttributeValue::exprloc_value`. [#422](https://github.com/gimli-rs/gimli/pull/422) * Added support for writing `.debug_loc` and `.debug_loclists` sections. [#425](https://github.com/gimli-rs/gimli/pull/425) * Added `-G` flag to `dwarfdump` example to display global offsets. [#427](https://github.com/gimli-rs/gimli/pull/427) * Added `examples/simple.rs`. [#429](https://github.com/gimli-rs/gimli/pull/429) ### Fixed * `write::LineProgram::from` no longer requires `DW_AT_name` or `DW_AT_comp_dir` attributes to be present in the unit DIE. [#430](https://github.com/gimli-rs/gimli/pull/430) -------------------------------------------------------------------------------- ## 0.18.0 Released 2019/04/25. The focus of this release has been on improving support for reading CFI, and adding support for writing CFI. ### Breaking changes * For types which have an `Offset` type parameter, the default `Offset` has changed from `usize` to `R::Offset`. [#392](https://github.com/gimli-rs/gimli/pull/392) * Added an `Offset` type parameter to the `read::Unit` type to allow variance. [#393](https://github.com/gimli-rs/gimli/pull/393) * Changed the `UninitializedUnwindContext::initialize` method to borrow `self`, and return `&mut UnwindContext`. Deleted the `InitializedUnwindContext` type. [#395](https://github.com/gimli-rs/gimli/pull/395) * Deleted the `UnwindSection` type parameters from the `CommonInformationEntry`, `FrameDescriptionEntry`, `UninitializedUnwindContext`, `UnwindContext`, and `UnwindTable` types. [#399](https://github.com/gimli-rs/gimli/pull/399) * Changed the signature of the `get_cie` callback parameter for various functions. The signature now matches the `UnwindSection::cie_from_offset` method, so that method can be used as the parameter. [#400](https://github.com/gimli-rs/gimli/pull/400) * Reduced the number of lifetime parameters for the `UnwindTable` type. [#400](https://github.com/gimli-rs/gimli/pull/400) * Updated `fallible-iterator` to version 0.2.0. [#407](https://github.com/gimli-rs/gimli/pull/407) * Added a parameter to the `Error::UnexpectedEof` enum variant. [#408](https://github.com/gimli-rs/gimli/pull/408) ### Added * Update to 2018 edition. [#391](https://github.com/gimli-rs/gimli/pull/391) * Added the `FrameDescriptionEntry::unwind_info_for_address` method. [#396](https://github.com/gimli-rs/gimli/pull/396) * Added the `FrameDescriptionEntry::rows` method. [#396](https://github.com/gimli-rs/gimli/pull/396) * Added the `EhHdrTable::unwind_info_for_address` method. [#400](https://github.com/gimli-rs/gimli/pull/400) * Added the `EhHdrTable::fde_for_address` method and deprecated the `EhHdrTable::lookup_and_parse` method. [#400](https://github.com/gimli-rs/gimli/pull/400) * Added the `EhHdrTable::pointer_to_offset` method. [#400](https://github.com/gimli-rs/gimli/pull/400) * Added the `UnwindSection::fde_for_address` method. [#396](https://github.com/gimli-rs/gimli/pull/396) * Added the `UnwindSection::fde_from_offset` method. [#400](https://github.com/gimli-rs/gimli/pull/400) * Added the `UnwindSection::partial_fde_from_offset` method. [#400](https://github.com/gimli-rs/gimli/pull/400) * Added the `Section::id` method. [#406](https://github.com/gimli-rs/gimli/pull/406) * Added the `Dwarf::load` method, and corresponding methods for individual sections. [#406](https://github.com/gimli-rs/gimli/pull/406) * Added the `Dwarf::borrow` method, and corresponding methods for individual sections. [#406](https://github.com/gimli-rs/gimli/pull/406) * Added the `Dwarf::format_error` method. [#408](https://github.com/gimli-rs/gimli/pull/408) * Added the `Dwarf::die_ranges` method. [#417](https://github.com/gimli-rs/gimli/pull/417) * Added the `Dwarf::unit_ranges` method. [#417](https://github.com/gimli-rs/gimli/pull/417) * Added support for writing `.debug_frame` and `.eh_frame` sections. [#412](https://github.com/gimli-rs/gimli/pull/412) [#419](https://github.com/gimli-rs/gimli/pull/419) ### Fixed * The `code_alignment_factor` is now used when evaluating CFI instructions that advance the location. [#401](https://github.com/gimli-rs/gimli/pull/401) * Fixed parsing of pointers encoded with `DW_EH_PE_funcrel`. [#402](https://github.com/gimli-rs/gimli/pull/402) * Use the FDE address encoding from the augmentation when parsing `DW_CFA_set_loc`. [#403](https://github.com/gimli-rs/gimli/pull/403) * Fixed setting of `.eh_frame` base addresses in dwarfdump. [#410](https://github.com/gimli-rs/gimli/pull/410) ## 0.17.0 Released 2019/02/21. The focus of this release has been on improving DWARF 5 support, and adding support for writing DWARF. ### Breaking changes * Changed register values to a `Register` type instead of `u8`/`u64`. [#328](https://github.com/gimli-rs/gimli/pull/328) * Replaced `BaseAddresses::set_cfi` with `set_eh_frame_hdr` and `set_eh_frame`. Replaced `BaseAddresses::set_data` with `set_got`. You should now use the same `BaseAddresses` value for parsing both `.eh_frame` and `.eh_frame_hdr`. [#351](https://github.com/gimli-rs/gimli/pull/351) * Renamed many types and functions related to `.debug_line`. Renamed `LineNumberProgram` to `LineProgram`. Renamed `IncompleteLineNumberProgram` to `IncompleteLineProgram`. Renamed `CompleteLineNumberProgram` to `CompleteLineProgram`. Renamed `LineNumberProgramHeader` to `LineProgramHeader`. Renamed `LineNumberRow` to `LineRow`. Renamed `StateMachine` to `LineRows`. Renamed `Opcode` to `LineInstruction`. Renamed `OpcodesIter` to `LineInstructions`. Renamed `LineNumberSequence` to `LineSequence`. [#359](https://github.com/gimli-rs/gimli/pull/359) * Added `Offset` type parameter to `AttributeValue`, `LineProgram`, `IncompleteLineProgram`, `CompleteLineProgram`, `LineRows`, `LineInstruction`, and `FileEntry`. [#324](https://github.com/gimli-rs/gimli/pull/324) * Changed `FileEntry::path_name`, `FileEntry::directory`, and `LineProgramHeader::directory` to return an `AttributeValue` instead of a `Reader`. [#366](https://github.com/gimli-rs/gimli/pull/366) * Renamed `FileEntry::last_modification` to `FileEntry::timestamp` and renamed `FileEntry::length` to `FileEntry::size`. [#366](https://github.com/gimli-rs/gimli/pull/366) * Added an `Encoding` type. Changed many functions that previously accepted `Format`, version or address size parameters to accept an `Encoding` parameter instead. Notable changes are `LocationLists::locations`, `RangeLists::ranges`, and `Expression::evaluation`. [#364](https://github.com/gimli-rs/gimli/pull/364) * Changed return type of `LocationLists::new` and `RangeLists::new`. [#370](https://github.com/gimli-rs/gimli/pull/370) * Added parameters to `LocationsLists::locations` and `RangeLists::ranges` to support `.debug_addr`. [#358](https://github.com/gimli-rs/gimli/pull/358) * Added more `AttributeValue` variants: `DebugAddrBase`, `DebugAddrIndex`, `DebugLocListsBase`, `DebugLocListsIndex`, `DebugRngListsBase`, `DebugRngListsIndex`, `DebugStrOffsetsBase`, `DebugStrOffsetsIndex`, `DebugLineStrRef`. [#358](https://github.com/gimli-rs/gimli/pull/358) * Changed `AttributeValue::Data*` attributes to native endian integers instead of byte arrays. [#365](https://github.com/gimli-rs/gimli/pull/365) * Replaced `EvaluationResult::TextBase` with `EvaluationResult::RequiresRelocatedAddress`. The handling of `TextBase` was incorrect. [#335](https://github.com/gimli-rs/gimli/pull/335) * Added `EvaluationResult::IndexedAddress` for operations that require an address from `.debug_addr`. [#358](https://github.com/gimli-rs/gimli/pull/358) * Added `Reader::read_slice`. Added a default implementation of `Reader::read_u8_array` which uses this. [#358](https://github.com/gimli-rs/gimli/pull/358) ### Added * Added initial support for writing DWARF. This is targeted at supporting line number information only. [#340](https://github.com/gimli-rs/gimli/pull/340) [#344](https://github.com/gimli-rs/gimli/pull/344) [#346](https://github.com/gimli-rs/gimli/pull/346) [#361](https://github.com/gimli-rs/gimli/pull/361) [#362](https://github.com/gimli-rs/gimli/pull/362) [#365](https://github.com/gimli-rs/gimli/pull/365) [#368](https://github.com/gimli-rs/gimli/pull/368) [#382](https://github.com/gimli-rs/gimli/pull/382) * Added `read` and `write` Cargo features. Both are enabled by default. [#343](https://github.com/gimli-rs/gimli/pull/343) * Added support for reading DWARF 5 `.debug_line` and `.debug_line_str` sections. [#366](https://github.com/gimli-rs/gimli/pull/366) * Added support for reading DWARF 5 `.debug_str_offsets` sections, including parsing `DW_FORM_strx*` attributes. [#358](https://github.com/gimli-rs/gimli/pull/358) * Added support for reading DWARF 5 `.debug_addr` sections, including parsing `DW_FORM_addrx*` attributes and evaluating `DW_OP_addrx` and `DW_OP_constx` operations. [#358](https://github.com/gimli-rs/gimli/pull/358) * Added support for reading DWARF 5 indexed addresses and offsets in `.debug_loclists` and `.debug_rnglists`, including parsing `DW_FORM_rnglistx` and `DW_FORM_loclistx` attributes. [#358](https://github.com/gimli-rs/gimli/pull/358) * Added high level `Dwarf` and `Unit` types. Existing code does not need to switch to using these types, but doing so will make DWARF 5 support simpler. [#352](https://github.com/gimli-rs/gimli/pull/352) [#380](https://github.com/gimli-rs/gimli/pull/380) [#381](https://github.com/gimli-rs/gimli/pull/381) * Added `EhFrame::set_address_size` and `DebugFrame::set_address_size` methods to allow parsing non-native CFI sections. The default address size is still the native size. [#325](https://github.com/gimli-rs/gimli/pull/325) * Added architecture specific definitions for `Register` values and names. Changed dwarfdump to print them. [#328](https://github.com/gimli-rs/gimli/pull/328) * Added support for reading relocatable DWARF sections. [#337](https://github.com/gimli-rs/gimli/pull/337) * Added parsing of `DW_FORM_data16`. [#366](https://github.com/gimli-rs/gimli/pull/366) ### Fixed * Fixed parsing DWARF 5 ranges with `start == end == 0`. [#323](https://github.com/gimli-rs/gimli/pull/323) * Changed `LineRows` to be covariant in its `Reader` type parameter. [#324](https://github.com/gimli-rs/gimli/pull/324) * Fixed handling of empty units in dwarfdump. [#330](https://github.com/gimli-rs/gimli/pull/330) * Fixed `UnitHeader::length_including_self` for `Dwarf64`. [#342](https://github.com/gimli-rs/gimli/pull/342) * Fixed parsing of `DW_CFA_set_loc`. [#355](https://github.com/gimli-rs/gimli/pull/355) * Fixed handling of multiple headers in `.debug_loclists` and `.debug_rnglists`. [#370](https://github.com/gimli-rs/gimli/pull/370) -------------------------------------------------------------------------------- ## 0.16.1 Released 2018/08/28. ### Added * Added `EhFrameHdr::lookup_and_parse`. [#316][] * Added support for `DW_CFA_GNU_args_size`. [#319][] ### Fixed * Implement `Send`/`Sync` for `SubRange`. [#305][] * Fixed `alloc` support on nightly. [#306][] [#310][] [#305]: https://github.com/gimli-rs/gimli/pull/305 [#306]: https://github.com/gimli-rs/gimli/pull/306 [#310]: https://github.com/gimli-rs/gimli/pull/310 [#316]: https://github.com/gimli-rs/gimli/pull/316 [#319]: https://github.com/gimli-rs/gimli/pull/319 -------------------------------------------------------------------------------- ## 0.16.0 Released 2018/06/01. ### Added * Added support for building in `#![no_std]` environments, when the `alloc` crate is available. Disable the "std" feature and enable the "alloc" feature. [#138][] [#271][] * Added support for DWARF 5 `.debug_rnglists` and `.debug_loclists` sections. [#272][] * Added support for DWARF 5 `DW_FORM_ref_sup` and `DW_FORM_strp_sup` attribute forms. [#288][] * Added support for DWARF 5 operations on typed values. [#293][] * A `dwarf-validate` example program that checks the integrity of the given DWARF and its references between sections. [#290][] * Added the `EndianReader` type, an easy way to define a custom `Reader` implementation with a reference to a generic buffer of bytes and an associated endianity. [#298][] [#302][] ### Changed * Various speed improvements for evaluating `.debug_line` line number programs. [#276][] * The example `dwarfdump` clone is a [whole lot faster now][dwarfdump-faster]. [#282][] [#284][] [#285][] ### Deprecated * `EndianBuf` has been renamed to `EndianSlice`, use that name instead. [#295][] ### Fixed * Evaluating the `DW_CFA_restore_state` opcode properly maintains the current location. Previously it would incorrectly restore the old location when popping from evaluation stack. [#274][] [#271]: https://github.com/gimli-rs/gimli/issues/271 [#138]: https://github.com/gimli-rs/gimli/issues/138 [#274]: https://github.com/gimli-rs/gimli/issues/274 [#272]: https://github.com/gimli-rs/gimli/issues/272 [#276]: https://github.com/gimli-rs/gimli/issues/276 [#282]: https://github.com/gimli-rs/gimli/issues/282 [#285]: https://github.com/gimli-rs/gimli/issues/285 [#284]: https://github.com/gimli-rs/gimli/issues/284 [#288]: https://github.com/gimli-rs/gimli/issues/288 [#290]: https://github.com/gimli-rs/gimli/issues/290 [#293]: https://github.com/gimli-rs/gimli/issues/293 [#295]: https://github.com/gimli-rs/gimli/issues/295 [#298]: https://github.com/gimli-rs/gimli/issues/298 [#302]: https://github.com/gimli-rs/gimli/issues/302 [dwarfdump-faster]: https://robert.ocallahan.org/2018/03/speeding-up-dwarfdump-with-rust.html -------------------------------------------------------------------------------- ## 0.15.0 Released 2017/12/01. ### Added * Added the `EndianBuf::to_string()` method. [#233][] * Added more robust error handling in our example `dwarfdump` clone. [#234][] * Added `FrameDescriptionEntry::initial_address` method. [#237][] * Added `FrameDescriptionEntry::len` method. [#237][] * Added the `FrameDescriptionEntry::entry_len` method. [#241][] * Added the `CommonInformationEntry::offset` method. [#241][] * Added the `CommonInformationEntry::entry_len` method. [#241][] * Added the `CommonInformationEntry::version` method. [#241][] * Added the `CommonInformationEntry::augmentation` method. [#241][] * Added the `CommonInformationEntry::code_alignment_factor` method. [#241][] * Added the `CommonInformationEntry::data_alignment_factor` method. [#241][] * Added the `CommonInformationEntry::return_address_register` method. [#241][] * Added support for printing `.eh_frame` sections to our example `dwarfdump` clone. [#241][] * Added support for parsing the `.eh_frame_hdr` section. On Linux, the `.eh_frame_hdr` section provides a pointer to the already-mapped-in-memory `.eh_frame` data, so that it doesn't need to be duplicated, and a binary search table of its entries for faster unwinding information lookups. [#250][] * Added support for parsing DWARF 5 compilation unit headers. [#257][] * Added support for DWARF 5's `DW_FORM_implicit_const`. [#257][] ### Changed * Unwinding methods now give ownership of the unwinding context back to the caller if errors are encountered, not just on the success path. This allows recovering from errors in signal-safe code, where constructing a new unwinding context is not an option because it requires allocation. This is a **breaking change** affecting `UnwindSection::unwind_info_for_address` and `UninitializedUnwindContext::initialize`. [#241][] * `CfaRule` and `RegisterRule` now expose their `DW_OP` expressions as `Expression`. This is a minor **breaking change**. [#241][] * The `Error::UnknownVersion` variant now contains the unknown version number. This is a minor **breaking change**. [#245][] * `EvaluationResult::RequiresEntryValue` requires an `Expression` instead of a `Reader` now. This is a minor **breaking change**. [#256][] [#233]: https://github.com/gimli-rs/gimli/pull/233 [#234]: https://github.com/gimli-rs/gimli/pull/234 [#237]: https://github.com/gimli-rs/gimli/pull/237 [#241]: https://github.com/gimli-rs/gimli/pull/241 [#245]: https://github.com/gimli-rs/gimli/pull/245 [#250]: https://github.com/gimli-rs/gimli/pull/250 [#256]: https://github.com/gimli-rs/gimli/pull/256 [#257]: https://github.com/gimli-rs/gimli/pull/257 -------------------------------------------------------------------------------- ## 0.14.0 Released 2017/08/08. ### Added * All `pub` types now `derive(Hash)`. [#192][] * All the constants from DWARF 5 are now defined. [#193][] * Added support for the `DW_OP_GNU_parameter_ref` GNU extension to parsing and evaluation DWARF opcodes. [#208][] * Improved LEB128 parsing performance. [#216][] * Improved `.debug_{aranges,pubnames,pubtypes}` parsing performance. [#218][] * Added the ability to choose endianity dynamically at run time, rather than only statically at compile time. [#219][] ### Changed * The biggest change of this release is that `gimli` no longer requires the object file's section be fully loaded into memory. This enables using `gimli` on 32 bit platforms where there often isn't enough contiguous virtual memory address space to load debugging information into. The default behavior is still geared for 64 bit platforms, where address space overfloweth, and you can still load the whole sections of the object file (or the entire object file) into memory. This is abstracted over with the `gimli::Reader` trait. This manifests as small (but many) breaking changes to much of the public API. [#182][] ### Fixed * The `DW_END_*` constants for defining endianity of a compilation unit were previously incorrect. [#193][] * The `DW_OP_addr` opcode is relative to the base address of the `.text` section of the binary, but we were incorrectly treating it as an absolute value. [#210][] [GitHub]: https://github.com/gimli-rs/gimli [crates.io]: https://crates.io/crates/gimli [contributing]: https://github.com/gimli-rs/gimli/blob/master/CONTRIBUTING.md [easy]: https://github.com/gimli-rs/gimli/issues?q=is%3Aopen+is%3Aissue+label%3Aeasy [#192]: https://github.com/gimli-rs/gimli/pull/192 [#193]: https://github.com/gimli-rs/gimli/pull/193 [#182]: https://github.com/gimli-rs/gimli/issues/182 [#208]: https://github.com/gimli-rs/gimli/pull/208 [#210]: https://github.com/gimli-rs/gimli/pull/210 [#216]: https://github.com/gimli-rs/gimli/pull/216 [#218]: https://github.com/gimli-rs/gimli/pull/218 [#219]: https://github.com/gimli-rs/gimli/pull/219 gimli-0.31.1/Cargo.toml0000644000000044710000000000100102230ustar # 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.60" name = "gimli" version = "0.31.1" build = false include = [ "/CHANGELOG.md", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src", ] autobins = false autoexamples = false autotests = false autobenches = false description = "A library for reading and writing the DWARF debugging format." documentation = "https://docs.rs/gimli" readme = "README.md" keywords = [ "DWARF", "debug", "ELF", "eh_frame", ] categories = [ "development-tools::debugging", "development-tools::profiling", "parser-implementations", ] license = "MIT OR Apache-2.0" repository = "https://github.com/gimli-rs/gimli" resolver = "2" [profile.bench] codegen-units = 1 debug = 2 split-debuginfo = "packed" [profile.test] split-debuginfo = "packed" [lib] name = "gimli" path = "src/lib.rs" [dependencies.alloc] version = "1.0.0" optional = true package = "rustc-std-workspace-alloc" [dependencies.compiler_builtins] version = "0.1.2" optional = true [dependencies.core] version = "1.0.0" optional = true package = "rustc-std-workspace-core" [dependencies.fallible-iterator] version = "0.3.0" optional = true default-features = false [dependencies.indexmap] version = "2.0.0" optional = true [dependencies.stable_deref_trait] version = "1.1.0" optional = true default-features = false [dev-dependencies.test-assembler] version = "0.1.3" [features] default = [ "read-all", "write", ] endian-reader = [ "read", "dep:stable_deref_trait", ] fallible-iterator = ["dep:fallible-iterator"] read = ["read-core"] read-all = [ "read", "std", "fallible-iterator", "endian-reader", ] read-core = [] rustc-dep-of-std = [ "dep:core", "dep:alloc", "dep:compiler_builtins", ] std = [ "fallible-iterator?/std", "stable_deref_trait?/std", ] write = ["dep:indexmap"] gimli-0.31.1/Cargo.toml.orig000064400000000000000000000036221046102023000137010ustar 00000000000000[package] name = "gimli" version = "0.31.1" categories = ["development-tools::debugging", "development-tools::profiling", "parser-implementations"] description = "A library for reading and writing the DWARF debugging format." documentation = "https://docs.rs/gimli" edition = "2018" include = [ "/CHANGELOG.md", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src", ] keywords = ["DWARF", "debug", "ELF", "eh_frame"] license = "MIT OR Apache-2.0" readme = "./README.md" repository = "https://github.com/gimli-rs/gimli" rust-version = "1.60" [dependencies] fallible-iterator = { version = "0.3.0", default-features = false, optional = true } indexmap = { version = "2.0.0", optional = true } stable_deref_trait = { version = "1.1.0", default-features = false, 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] test-assembler = "0.1.3" [features] read-core = [] read = ["read-core"] read-all = ["read", "std", "fallible-iterator", "endian-reader"] endian-reader = ["read", "dep:stable_deref_trait"] fallible-iterator = ["dep:fallible-iterator"] write = ["dep:indexmap"] std = ["fallible-iterator?/std", "stable_deref_trait?/std"] default = ["read-all", "write"] # Internal feature, only used when building as part of libstd, not part of the # stable interface of this crate. rustc-dep-of-std = ["dep:core", "dep:alloc", "dep:compiler_builtins"] [profile.test] split-debuginfo = "packed" [profile.bench] debug = true codegen-units = 1 split-debuginfo = "packed" [workspace] members = ["crates/examples"] default-members = [".", "crates/examples"] resolver = "2" gimli-0.31.1/LICENSE-APACHE000064400000000000000000000251371046102023000127430ustar 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. gimli-0.31.1/LICENSE-MIT000064400000000000000000000020571046102023000124470ustar 00000000000000Copyright (c) 2015 The Rust Project 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. gimli-0.31.1/README.md000064400000000000000000000060151046102023000122700ustar 00000000000000# `gimli` [![](https://img.shields.io/crates/v/gimli.svg) ![](https://img.shields.io/crates/d/gimli.svg)](https://crates.io/crates/gimli) [![](https://docs.rs/gimli/badge.svg)](https://docs.rs/gimli/) [![Build Status](https://github.com/gimli-rs/gimli/workflows/Rust/badge.svg)](https://github.com/gimli-rs/gimli/actions) [![Coverage Status](https://coveralls.io/repos/github/gimli-rs/gimli/badge.svg?branch=master)](https://coveralls.io/github/gimli-rs/gimli?branch=master) `gimli` is a library for reading and writing the [DWARF debugging format](https://dwarfstd.org/). * **Zero copy:** everything is just a reference to the original input buffer. No copies of the input data get made. * **Lazy:** you can iterate compilation units without parsing their contents. Parse only as many debugging information entry (DIE) trees as you iterate over. `gimli` also uses `DW_AT_sibling` references to avoid parsing a DIE's children to find its next sibling, when possible. * **Cross-platform:** `gimli` makes no assumptions about what kind of object file you're working with. The flipside to that is that it's up to you to provide an ELF loader on Linux or Mach-O loader on macOS. * Unsure which object file parser to use? Try the cross-platform [`object`](https://github.com/gimli-rs/object) crate. See the [`gimli-examples`](./crates/examples/src/bin) crate for usage with `gimli`. ## Install Add this to your `Cargo.toml`: ```toml [dependencies] gimli = "0.31.1" ``` The minimum supported Rust version is: * 1.60.0 for the `read` feature and its dependencies. * 1.65.0 for other features. ## Documentation * [Documentation on docs.rs](https://docs.rs/gimli/) * Example programs: * [A simple `.debug_info` parser](./crates/examples/src/bin/simple.rs) * [A simple `.debug_line` parser](./crates/examples/src/bin/simple_line.rs) * [A `dwarfdump` clone](./crates/examples/src/bin/dwarfdump.rs) * [An `addr2line` clone](https://github.com/gimli-rs/addr2line) * [`ddbug`](https://github.com/gimli-rs/ddbug), a utility giving insight into code generation by making debugging information readable. * [`dwprod`](https://github.com/fitzgen/dwprod), a tiny utility to list the compilers used to create each compilation unit within a shared library or executable (via `DW_AT_producer`). * [`dwarf-validate`](./crates/examples/src/bin/dwarf-validate.rs), a program to validate the integrity of some DWARF and its references between sections and compilation units. ## 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. ## Contribution See [CONTRIBUTING.md](./CONTRIBUTING.md) for hacking. 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. gimli-0.31.1/src/arch.rs000064400000000000000000000611461046102023000130710ustar 00000000000000use crate::common::Register; macro_rules! registers { ($struct_name:ident, { $($name:ident = ($val:expr, $disp:expr)),+ $(,)? } $(, aliases { $($alias_name:ident = ($alias_val:expr, $alias_disp:expr)),+ $(,)? })?) => { #[allow(missing_docs)] impl $struct_name { $( pub const $name: Register = Register($val); )+ $( $(pub const $alias_name: Register = Register($alias_val);)+ )* } impl $struct_name { /// The name of a register, or `None` if the register number is unknown. /// /// Only returns the primary name for registers that alias with others. pub fn register_name(register: Register) -> Option<&'static str> { match register { $( Self::$name => Some($disp), )+ _ => return None, } } /// Converts a register name into a register number. pub fn name_to_register(value: &str) -> Option { match value { $( $disp => Some(Self::$name), )+ $( $($alias_disp => Some(Self::$alias_name),)+ )* _ => return None, } } } }; } /// ARM architecture specific definitions. /// /// See [DWARF for the ARM Architecture]( /// https://github.com/ARM-software/abi-aa/blob/main/aadwarf32/aadwarf32.rst). #[derive(Debug, Clone, Copy)] pub struct Arm; registers!(Arm, { R0 = (0, "R0"), R1 = (1, "R1"), R2 = (2, "R2"), R3 = (3, "R3"), R4 = (4, "R4"), R5 = (5, "R5"), R6 = (6, "R6"), R7 = (7, "R7"), R8 = (8, "R8"), R9 = (9, "R9"), R10 = (10, "R10"), R11 = (11, "R11"), R12 = (12, "R12"), R13 = (13, "R13"), R14 = (14, "R14"), R15 = (15, "R15"), WCGR0 = (104, "wCGR0"), WCGR1 = (105, "wCGR1"), WCGR2 = (106, "wCGR2"), WCGR3 = (107, "wCGR3"), WCGR4 = (108, "wCGR4"), WCGR5 = (109, "wCGR5"), WCGR6 = (110, "wCGR6"), WCGR7 = (111, "wCGR7"), WR0 = (112, "wR0"), WR1 = (113, "wR1"), WR2 = (114, "wR2"), WR3 = (115, "wR3"), WR4 = (116, "wR4"), WR5 = (117, "wR5"), WR6 = (118, "wR6"), WR7 = (119, "wR7"), WR8 = (120, "wR8"), WR9 = (121, "wR9"), WR10 = (122, "wR10"), WR11 = (123, "wR11"), WR12 = (124, "wR12"), WR13 = (125, "wR13"), WR14 = (126, "wR14"), WR15 = (127, "wR15"), SPSR = (128, "SPSR"), SPSR_FIQ = (129, "SPSR_FIQ"), SPSR_IRQ = (130, "SPSR_IRQ"), SPSR_ABT = (131, "SPSR_ABT"), SPSR_UND = (132, "SPSR_UND"), SPSR_SVC = (133, "SPSR_SVC"), RA_AUTH_CODE = (143, "RA_AUTH_CODE"), R8_USR = (144, "R8_USR"), R9_USR = (145, "R9_USR"), R10_USR = (146, "R10_USR"), R11_USR = (147, "R11_USR"), R12_USR = (148, "R12_USR"), R13_USR = (149, "R13_USR"), R14_USR = (150, "R14_USR"), R8_FIQ = (151, "R8_FIQ"), R9_FIQ = (152, "R9_FIQ"), R10_FIQ = (153, "R10_FIQ"), R11_FIQ = (154, "R11_FIQ"), R12_FIQ = (155, "R12_FIQ"), R13_FIQ = (156, "R13_FIQ"), R14_FIQ = (157, "R14_FIQ"), R13_IRQ = (158, "R13_IRQ"), R14_IRQ = (159, "R14_IRQ"), R13_ABT = (160, "R13_ABT"), R14_ABT = (161, "R14_ABT"), R13_UND = (162, "R13_UND"), R14_UND = (163, "R14_UND"), R13_SVC = (164, "R13_SVC"), R14_SVC = (165, "R14_SVC"), WC0 = (192, "wC0"), WC1 = (193, "wC1"), WC2 = (194, "wC2"), WC3 = (195, "wC3"), WC4 = (196, "wC4"), WC5 = (197, "wC5"), WC6 = (198, "wC6"), WC7 = (199, "wC7"), D0 = (256, "D0"), D1 = (257, "D1"), D2 = (258, "D2"), D3 = (259, "D3"), D4 = (260, "D4"), D5 = (261, "D5"), D6 = (262, "D6"), D7 = (263, "D7"), D8 = (264, "D8"), D9 = (265, "D9"), D10 = (266, "D10"), D11 = (267, "D11"), D12 = (268, "D12"), D13 = (269, "D13"), D14 = (270, "D14"), D15 = (271, "D15"), D16 = (272, "D16"), D17 = (273, "D17"), D18 = (274, "D18"), D19 = (275, "D19"), D20 = (276, "D20"), D21 = (277, "D21"), D22 = (278, "D22"), D23 = (279, "D23"), D24 = (280, "D24"), D25 = (281, "D25"), D26 = (282, "D26"), D27 = (283, "D27"), D28 = (284, "D28"), D29 = (285, "D29"), D30 = (286, "D30"), D31 = (287, "D31"), TPIDRURO = (320, "TPIDRURO"), TPIDRURW = (321, "TPIDRURW"), TPIDPR = (322, "TPIDPR"), HTPIDPR = (323, "HTPIDPR"), }, aliases { SP = (13, "SP"), LR = (14, "LR"), PC = (15, "PC"), ACC0 = (104, "ACC0"), ACC1 = (105, "ACC1"), ACC2 = (106, "ACC2"), ACC3 = (107, "ACC3"), ACC4 = (108, "ACC4"), ACC5 = (109, "ACC5"), ACC6 = (110, "ACC6"), ACC7 = (111, "ACC7"), S0 = (256, "S0"), S1 = (256, "S1"), S2 = (257, "S2"), S3 = (257, "S3"), S4 = (258, "S4"), S5 = (258, "S5"), S6 = (259, "S6"), S7 = (259, "S7"), S8 = (260, "S8"), S9 = (260, "S9"), S10 = (261, "S10"), S11 = (261, "S11"), S12 = (262, "S12"), S13 = (262, "S13"), S14 = (263, "S14"), S15 = (263, "S15"), S16 = (264, "S16"), S17 = (264, "S17"), S18 = (265, "S18"), S19 = (265, "S19"), S20 = (266, "S20"), S21 = (266, "S21"), S22 = (267, "S22"), S23 = (267, "S23"), S24 = (268, "S24"), S25 = (268, "S25"), S26 = (269, "S26"), S27 = (269, "S27"), S28 = (270, "S28"), S29 = (270, "S29"), S30 = (271, "S30"), S31 = (271, "S31"), }); /// ARM 64-bit (AArch64) architecture specific definitions. /// /// See [DWARF for the ARM 64-bit Architecture]( /// https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst). #[derive(Debug, Clone, Copy)] pub struct AArch64; registers!(AArch64, { X0 = (0, "X0"), X1 = (1, "X1"), X2 = (2, "X2"), X3 = (3, "X3"), X4 = (4, "X4"), X5 = (5, "X5"), X6 = (6, "X6"), X7 = (7, "X7"), X8 = (8, "X8"), X9 = (9, "X9"), X10 = (10, "X10"), X11 = (11, "X11"), X12 = (12, "X12"), X13 = (13, "X13"), X14 = (14, "X14"), X15 = (15, "X15"), X16 = (16, "X16"), X17 = (17, "X17"), X18 = (18, "X18"), X19 = (19, "X19"), X20 = (20, "X20"), X21 = (21, "X21"), X22 = (22, "X22"), X23 = (23, "X23"), X24 = (24, "X24"), X25 = (25, "X25"), X26 = (26, "X26"), X27 = (27, "X27"), X28 = (28, "X28"), X29 = (29, "X29"), X30 = (30, "X30"), SP = (31, "SP"), PC = (32, "PC"), ELR_MODE = (33, "ELR_mode"), RA_SIGN_STATE = (34, "RA_SIGN_STATE"), TPIDRRO_EL0 = (35, "TPIDRRO_EL0"), TPIDR_EL0 = (36, "TPIDR_EL0"), TPIDR_EL1 = (37, "TPIDR_EL1"), TPIDR_EL2 = (38, "TPIDR_EL2"), TPIDR_EL3 = (39, "TPIDR_EL3"), VG = (46, "VG"), FFR = (47, "FFR"), P0 = (48, "P0"), P1 = (49, "P1"), P2 = (50, "P2"), P3 = (51, "P3"), P4 = (52, "P4"), P5 = (53, "P5"), P6 = (54, "P6"), P7 = (55, "P7"), P8 = (56, "P8"), P9 = (57, "P9"), P10 = (58, "P10"), P11 = (59, "P11"), P12 = (60, "P12"), P13 = (61, "P13"), P14 = (62, "P14"), P15 = (63, "P15"), V0 = (64, "V0"), V1 = (65, "V1"), V2 = (66, "V2"), V3 = (67, "V3"), V4 = (68, "V4"), V5 = (69, "V5"), V6 = (70, "V6"), V7 = (71, "V7"), V8 = (72, "V8"), V9 = (73, "V9"), V10 = (74, "V10"), V11 = (75, "V11"), V12 = (76, "V12"), V13 = (77, "V13"), V14 = (78, "V14"), V15 = (79, "V15"), V16 = (80, "V16"), V17 = (81, "V17"), V18 = (82, "V18"), V19 = (83, "V19"), V20 = (84, "V20"), V21 = (85, "V21"), V22 = (86, "V22"), V23 = (87, "V23"), V24 = (88, "V24"), V25 = (89, "V25"), V26 = (90, "V26"), V27 = (91, "V27"), V28 = (92, "V28"), V29 = (93, "V29"), V30 = (94, "V30"), V31 = (95, "V31"), Z0 = (96, "Z0"), Z1 = (97, "Z1"), Z2 = (98, "Z2"), Z3 = (99, "Z3"), Z4 = (100, "Z4"), Z5 = (101, "Z5"), Z6 = (102, "Z6"), Z7 = (103, "Z7"), Z8 = (104, "Z8"), Z9 = (105, "Z9"), Z10 = (106, "Z10"), Z11 = (107, "Z11"), Z12 = (108, "Z12"), Z13 = (109, "Z13"), Z14 = (110, "Z14"), Z15 = (111, "Z15"), Z16 = (112, "Z16"), Z17 = (113, "Z17"), Z18 = (114, "Z18"), Z19 = (115, "Z19"), Z20 = (116, "Z20"), Z21 = (117, "Z21"), Z22 = (118, "Z22"), Z23 = (119, "Z23"), Z24 = (120, "Z24"), Z25 = (121, "Z25"), Z26 = (122, "Z26"), Z27 = (123, "Z27"), Z28 = (124, "Z28"), Z29 = (125, "Z29"), Z30 = (126, "Z30"), Z31 = (127, "Z31"), }); /// LoongArch architecture specific definitions. /// /// See [LoongArch ELF psABI specification](https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html). #[derive(Debug, Clone, Copy)] pub struct LoongArch; registers!(LoongArch, { R0 = (0, "$r0"), R1 = (1, "$r1"), R2 = (2, "$r2"), R3 = (3, "$r3"), R4 = (4, "$r4"), R5 = (5, "$r5"), R6 = (6, "$r6"), R7 = (7, "$r7"), R8 = (8, "$r8"), R9 = (9, "$r9"), R10 = (10, "$r10"), R11 = (11, "$r11"), R12 = (12, "$r12"), R13 = (13, "$r13"), R14 = (14, "$r14"), R15 = (15, "$r15"), R16 = (16, "$r16"), R17 = (17, "$r17"), R18 = (18, "$r18"), R19 = (19, "$r19"), R20 = (20, "$r20"), R21 = (21, "$r21"), R22 = (22, "$r22"), R23 = (23, "$r23"), R24 = (24, "$r24"), R25 = (25, "$r25"), R26 = (26, "$r26"), R27 = (27, "$r27"), R28 = (28, "$r28"), R29 = (29, "$r29"), R30 = (30, "$r30"), R31 = (31, "$r31"), F0 = (32, "$f0"), F1 = (33, "$f1"), F2 = (34, "$f2"), F3 = (35, "$f3"), F4 = (36, "$f4"), F5 = (37, "$f5"), F6 = (38, "$f6"), F7 = (39, "$f7"), F8 = (40, "$f8"), F9 = (41, "$f9"), F10 = (42, "$f10"), F11 = (43, "$f11"), F12 = (44, "$f12"), F13 = (45, "$f13"), F14 = (46, "$f14"), F15 = (47, "$f15"), F16 = (48, "$f16"), F17 = (49, "$f17"), F18 = (50, "$f18"), F19 = (51, "$f19"), F20 = (52, "$f20"), F21 = (53, "$f21"), F22 = (54, "$f22"), F23 = (55, "$f23"), F24 = (56, "$f24"), F25 = (57, "$f25"), F26 = (58, "$f26"), F27 = (59, "$f27"), F28 = (60, "$f28"), F29 = (61, "$f29"), F30 = (62, "$f30"), F31 = (63, "$f31"), FCC0 = (64, "$fcc0"), FCC1 = (65, "$fcc1"), FCC2 = (66, "$fcc2"), FCC3 = (67, "$fcc3"), FCC4 = (68, "$fcc4"), FCC5 = (69, "$fcc5"), FCC6 = (70, "$fcc6"), FCC7 = (71, "$fcc7"), }, aliases { ZERO = (0, "$zero"), RA = (1, "$ra"), TP = (2, "$tp"), SP = (3, "$sp"), A0 = (4, "$a0"), A1 = (5, "$a1"), A2 = (6, "$a2"), A3 = (7, "$a3"), A4 = (8, "$a4"), A5 = (9, "$a5"), A6 = (10, "$a6"), A7 = (11, "$a7"), T0 = (12, "$t0"), T1 = (13, "$t1"), T2 = (14, "$t2"), T3 = (15, "$t3"), T4 = (16, "$t4"), T5 = (17, "$t5"), T6 = (18, "$t6"), T7 = (19, "$t7"), T8 = (20, "$t8"), FP = (22, "$fp"), S0 = (23, "$s0"), S1 = (24, "$s1"), S2 = (25, "$s2"), S3 = (26, "$s3"), S4 = (27, "$s4"), S5 = (28, "$s5"), S6 = (29, "$s6"), S7 = (30, "$s7"), S8 = (31, "$s8"), FA0 = (32, "$fa0"), FA1 = (33, "$fa1"), FA2 = (34, "$fa2"), FA3 = (35, "$fa3"), FA4 = (36, "$fa4"), FA5 = (37, "$fa5"), FA6 = (38, "$fa6"), FA7 = (39, "$fa7"), FT0 = (40, "$ft0"), FT1 = (41, "$ft1"), FT2 = (42, "$ft2"), FT3 = (43, "$ft3"), FT4 = (44, "$ft4"), FT5 = (45, "$ft5"), FT6 = (46, "$ft6"), FT7 = (47, "$ft7"), FT8 = (48, "$ft8"), FT9 = (49, "$ft9"), FT10 = (50, "$ft10"), FT11 = (51, "$ft11"), FT12 = (52, "$ft12"), FT13 = (53, "$ft13"), FT14 = (54, "$ft14"), FT15 = (55, "$ft15"), FS0 = (56, "$fs0"), FS1 = (57, "$fs1"), FS2 = (58, "$fs2"), FS3 = (59, "$fs3"), FS4 = (60, "$fs4"), FS5 = (61, "$fs5"), FS6 = (62, "$fs6"), FS7 = (63, "$fs7"), }); /// MIPS architecture specific definitions. /// /// See [MIPS Details](https://en.wikibooks.org/wiki/MIPS_Assembly/MIPS_Details). #[derive(Debug, Clone, Copy)] pub struct MIPS; registers!(MIPS, { R0 = (0, "$0"), R1 = (1, "$1"), R2 = (2, "$2"), R3 = (3, "$3"), R4 = (4, "$4"), R5 = (5, "$5"), R6 = (6, "$6"), R7 = (7, "$7"), R8 = (8, "$8"), R9 = (9, "$9"), R10 = (10, "$10"), R11 = (11, "$11"), R12 = (12, "$12"), R13 = (13, "$13"), R14 = (14, "$14"), R15 = (15, "$15"), R16 = (16, "$16"), R17 = (17, "$17"), R18 = (18, "$18"), R19 = (19, "$19"), R20 = (20, "$20"), R21 = (21, "$21"), R22 = (22, "$22"), R23 = (23, "$23"), R24 = (24, "$24"), R25 = (25, "$25"), R26 = (26, "$26"), R27 = (27, "$27"), R28 = (28, "$28"), R29 = (29, "$29"), R30 = (30, "$30"), R31 = (31, "$31"), F0 = (32, "$f0"), F1 = (33, "$f1"), F2 = (34, "$f2"), F3 = (35, "$f3"), F4 = (36, "$f4"), F5 = (37, "$f5"), F6 = (38, "$f6"), F7 = (39, "$f7"), F8 = (40, "$f8"), F9 = (41, "$f9"), F10 = (42, "$f10"), F11 = (43, "$f11"), F12 = (44, "$f12"), F13 = (45, "$f13"), F14 = (46, "$f14"), F15 = (47, "$f15"), F16 = (48, "$f16"), F17 = (49, "$f17"), F18 = (50, "$f18"), F19 = (51, "$f19"), F20 = (52, "$f20"), F21 = (53, "$f21"), F22 = (54, "$f22"), F23 = (55, "$f23"), F24 = (56, "$f24"), F25 = (57, "$f25"), F26 = (58, "$f26"), F27 = (59, "$f27"), F28 = (60, "$f28"), F29 = (61, "$f29"), F30 = (62, "$f30"), F31 = (63, "$f31"), HI = (64, "$hi"), LO = (65, "$lo"), }, aliases { ZERO = (0, "$zero"), AT = (1, "$at"), V0 = (2, "$v0"), V1 = (3, "$v1"), A0 = (4, "$a0"), A1 = (5, "$a1"), A2 = (6, "$a2"), A3 = (7, "$a3"), T0 = (8, "$t0"), T1 = (9, "$t1"), T2 = (10, "$t2"), T3 = (11, "$t3"), T4 = (12, "$t4"), T5 = (13, "$t5"), T6 = (14, "$t6"), T7 = (15, "$t7"), S0 = (16, "$s0"), S1 = (17, "$s1"), S2 = (18, "$s2"), S3 = (19, "$s3"), S4 = (20, "$s4"), S5 = (21, "$s5"), S6 = (22, "$s6"), S7 = (23, "$s7"), T8 = (24, "$t8"), T9 = (25, "$t9"), K0 = (26, "$k0"), K1 = (27, "$k1"), GP = (28, "$gp"), SP = (29, "$sp"), FP = (30, "$fp"), RA = (31, "$ra"), S8 = (30, "$s8") }); /// RISC-V architecture specific definitions. /// /// See [RISC-V ELF psABI specification](https://github.com/riscv/riscv-elf-psabi-doc). #[derive(Debug, Clone, Copy)] pub struct RiscV; registers!(RiscV, { X0 = (0, "x0"), X1 = (1, "x1"), X2 = (2, "x2"), X3 = (3, "x3"), X4 = (4, "x4"), X5 = (5, "x5"), X6 = (6, "x6"), X7 = (7, "x7"), X8 = (8, "x8"), X9 = (9, "x9"), X10 = (10, "x10"), X11 = (11, "x11"), X12 = (12, "x12"), X13 = (13, "x13"), X14 = (14, "x14"), X15 = (15, "x15"), X16 = (16, "x16"), X17 = (17, "x17"), X18 = (18, "x18"), X19 = (19, "x19"), X20 = (20, "x20"), X21 = (21, "x21"), X22 = (22, "x22"), X23 = (23, "x23"), X24 = (24, "x24"), X25 = (25, "x25"), X26 = (26, "x26"), X27 = (27, "x27"), X28 = (28, "x28"), X29 = (29, "x29"), X30 = (30, "x30"), X31 = (31, "x31"), F0 = (32, "f0"), F1 = (33, "f1"), F2 = (34, "f2"), F3 = (35, "f3"), F4 = (36, "f4"), F5 = (37, "f5"), F6 = (38, "f6"), F7 = (39, "f7"), F8 = (40, "f8"), F9 = (41, "f9"), F10 = (42, "f10"), F11 = (43, "f11"), F12 = (44, "f12"), F13 = (45, "f13"), F14 = (46, "f14"), F15 = (47, "f15"), F16 = (48, "f16"), F17 = (49, "f17"), F18 = (50, "f18"), F19 = (51, "f19"), F20 = (52, "f20"), F21 = (53, "f21"), F22 = (54, "f22"), F23 = (55, "f23"), F24 = (56, "f24"), F25 = (57, "f25"), F26 = (58, "f26"), F27 = (59, "f27"), F28 = (60, "f28"), F29 = (61, "f29"), F30 = (62, "f30"), F31 = (63, "f31"), }, aliases { ZERO = (0, "zero"), RA = (1, "ra"), SP = (2, "sp"), GP = (3, "gp"), TP = (4, "tp"), T0 = (5, "t0"), T1 = (6, "t1"), T2 = (7, "t2"), S0 = (8, "s0"), S1 = (9, "s1"), A0 = (10, "a0"), A1 = (11, "a1"), A2 = (12, "a2"), A3 = (13, "a3"), A4 = (14, "a4"), A5 = (15, "a5"), A6 = (16, "a6"), A7 = (17, "a7"), S2 = (18, "s2"), S3 = (19, "s3"), S4 = (20, "s4"), S5 = (21, "s5"), S6 = (22, "s6"), S7 = (23, "s7"), S8 = (24, "s8"), S9 = (25, "s9"), S10 = (26, "s10"), S11 = (27, "s11"), T3 = (28, "t3"), T4 = (29, "t4"), T5 = (30, "t5"), T6 = (31, "t6"), FT0 = (32, "ft0"), FT1 = (33, "ft1"), FT2 = (34, "ft2"), FT3 = (35, "ft3"), FT4 = (36, "ft4"), FT5 = (37, "ft5"), FT6 = (38, "ft6"), FT7 = (39, "ft7"), FS0 = (40, "fs0"), FS1 = (41, "fs1"), FA0 = (42, "fa0"), FA1 = (43, "fa1"), FA2 = (44, "fa2"), FA3 = (45, "fa3"), FA4 = (46, "fa4"), FA5 = (47, "fa5"), FA6 = (48, "fa6"), FA7 = (49, "fa7"), FS2 = (50, "fs2"), FS3 = (51, "fs3"), FS4 = (52, "fs4"), FS5 = (53, "fs5"), FS6 = (54, "fs6"), FS7 = (55, "fs7"), FS8 = (56, "fs8"), FS9 = (57, "fs9"), FS10 = (58, "fs10"), FS11 = (59, "fs11"), FT8 = (60, "ft8"), FT9 = (61, "ft9"), FT10 = (62, "ft10"), FT11 = (63, "ft11"), }); /// Intel i386 architecture specific definitions. /// /// See Intel386 psABi version 1.1 at the [X86 psABI wiki](https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI). #[derive(Debug, Clone, Copy)] pub struct X86; registers!(X86, { EAX = (0, "eax"), ECX = (1, "ecx"), EDX = (2, "edx"), EBX = (3, "ebx"), ESP = (4, "esp"), EBP = (5, "ebp"), ESI = (6, "esi"), EDI = (7, "edi"), // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. RA = (8, "RA"), ST0 = (11, "st0"), ST1 = (12, "st1"), ST2 = (13, "st2"), ST3 = (14, "st3"), ST4 = (15, "st4"), ST5 = (16, "st5"), ST6 = (17, "st6"), ST7 = (18, "st7"), XMM0 = (21, "xmm0"), XMM1 = (22, "xmm1"), XMM2 = (23, "xmm2"), XMM3 = (24, "xmm3"), XMM4 = (25, "xmm4"), XMM5 = (26, "xmm5"), XMM6 = (27, "xmm6"), XMM7 = (28, "xmm7"), MM0 = (29, "mm0"), MM1 = (30, "mm1"), MM2 = (31, "mm2"), MM3 = (32, "mm3"), MM4 = (33, "mm4"), MM5 = (34, "mm5"), MM6 = (35, "mm6"), MM7 = (36, "mm7"), MXCSR = (39, "mxcsr"), ES = (40, "es"), CS = (41, "cs"), SS = (42, "ss"), DS = (43, "ds"), FS = (44, "fs"), GS = (45, "gs"), TR = (48, "tr"), LDTR = (49, "ldtr"), FS_BASE = (93, "fs.base"), GS_BASE = (94, "gs.base"), }); /// AMD64 architecture specific definitions. /// /// See x86-64 psABI version 1.0 at the [X86 psABI wiki](https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI). #[derive(Debug, Clone, Copy)] pub struct X86_64; registers!(X86_64, { RAX = (0, "rax"), RDX = (1, "rdx"), RCX = (2, "rcx"), RBX = (3, "rbx"), RSI = (4, "rsi"), RDI = (5, "rdi"), RBP = (6, "rbp"), RSP = (7, "rsp"), R8 = (8, "r8"), R9 = (9, "r9"), R10 = (10, "r10"), R11 = (11, "r11"), R12 = (12, "r12"), R13 = (13, "r13"), R14 = (14, "r14"), R15 = (15, "r15"), // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. RA = (16, "RA"), XMM0 = (17, "xmm0"), XMM1 = (18, "xmm1"), XMM2 = (19, "xmm2"), XMM3 = (20, "xmm3"), XMM4 = (21, "xmm4"), XMM5 = (22, "xmm5"), XMM6 = (23, "xmm6"), XMM7 = (24, "xmm7"), XMM8 = (25, "xmm8"), XMM9 = (26, "xmm9"), XMM10 = (27, "xmm10"), XMM11 = (28, "xmm11"), XMM12 = (29, "xmm12"), XMM13 = (30, "xmm13"), XMM14 = (31, "xmm14"), XMM15 = (32, "xmm15"), ST0 = (33, "st0"), ST1 = (34, "st1"), ST2 = (35, "st2"), ST3 = (36, "st3"), ST4 = (37, "st4"), ST5 = (38, "st5"), ST6 = (39, "st6"), ST7 = (40, "st7"), MM0 = (41, "mm0"), MM1 = (42, "mm1"), MM2 = (43, "mm2"), MM3 = (44, "mm3"), MM4 = (45, "mm4"), MM5 = (46, "mm5"), MM6 = (47, "mm6"), MM7 = (48, "mm7"), RFLAGS = (49, "rFLAGS"), ES = (50, "es"), CS = (51, "cs"), SS = (52, "ss"), DS = (53, "ds"), FS = (54, "fs"), GS = (55, "gs"), FS_BASE = (58, "fs.base"), GS_BASE = (59, "gs.base"), TR = (62, "tr"), LDTR = (63, "ldtr"), MXCSR = (64, "mxcsr"), FCW = (65, "fcw"), FSW = (66, "fsw"), XMM16 = (67, "xmm16"), XMM17 = (68, "xmm17"), XMM18 = (69, "xmm18"), XMM19 = (70, "xmm19"), XMM20 = (71, "xmm20"), XMM21 = (72, "xmm21"), XMM22 = (73, "xmm22"), XMM23 = (74, "xmm23"), XMM24 = (75, "xmm24"), XMM25 = (76, "xmm25"), XMM26 = (77, "xmm26"), XMM27 = (78, "xmm27"), XMM28 = (79, "xmm28"), XMM29 = (80, "xmm29"), XMM30 = (81, "xmm30"), XMM31 = (82, "xmm31"), K0 = (118, "k0"), K1 = (119, "k1"), K2 = (120, "k2"), K3 = (121, "k3"), K4 = (122, "k4"), K5 = (123, "k5"), K6 = (124, "k6"), K7 = (125, "k7"), }); /// PowerPC 64bit /// /// See [64-bit ELF ABI Specification for OpenPOWER Architecture](https://openpowerfoundation.org/specifications/64bitelfabi/). #[derive(Debug, Clone, Copy)] pub struct PowerPc64; registers!(PowerPc64, { R0 = (0, "r0"), R1 = (1, "r1"), R2 = (2, "r2"), R3 = (3, "r3"), R4 = (4, "r4"), R5 = (5, "r5"), R6 = (6, "r6"), R7 = (7, "r7"), R8 = (8, "r8"), R9 = (9, "r9"), R10 = (10, "r10"), R11 = (11, "r11"), R12 = (12, "r12"), R13 = (13, "r13"), R14 = (14, "r14"), R15 = (15, "r15"), R16 = (16, "r16"), R17 = (17, "r17"), R18 = (18, "r18"), R19 = (19, "r19"), R20 = (20, "r20"), R21 = (21, "r21"), R22 = (22, "r22"), R23 = (23, "r23"), R24 = (24, "r24"), R25 = (25, "r25"), R26 = (26, "r26"), R27 = (27, "r27"), R28 = (28, "r28"), R29 = (29, "r29"), R30 = (30, "r30"), R31 = (31, "r31"), F0 = (32, "f0"), F1 = (33, "f1"), F2 = (34, "f2"), F3 = (35, "f3"), F4 = (36, "f4"), F5 = (37, "f5"), F6 = (38, "f6"), F7 = (39, "f7"), F8 = (40, "f8"), F9 = (41, "f9"), F10 = (42, "f10"), F11 = (43, "f11"), F12 = (44, "f12"), F13 = (45, "f13"), F14 = (46, "f14"), F15 = (47, "f15"), F16 = (48, "f16"), F17 = (49, "f17"), F18 = (50, "f18"), F19 = (51, "f19"), F20 = (52, "f20"), F21 = (53, "f21"), F22 = (54, "f22"), F23 = (55, "f23"), F24 = (56, "f24"), F25 = (57, "f25"), F26 = (58, "f26"), F27 = (59, "f27"), F28 = (60, "f28"), F29 = (61, "f29"), F30 = (62, "f30"), F31 = (63, "f31"), LR = (65, "lr"), CTR = (66, "ctr"), CR0 = (68, "cr0"), CR1 = (69, "cr1"), CR2 = (70, "cr2"), CR3 = (71, "cr3"), CR4 = (72, "cr4"), CR5 = (73, "cr5"), CR6 = (74, "cr6"), CR7 = (75, "cr7"), XER = (76, "xer"), VR0 = (77, "vr0"), VR1 = (78, "vr1"), VR2 = (79, "vr2"), VR3 = (80, "vr3"), VR4 = (81, "vr4"), VR5 = (82, "vr5"), VR6 = (83, "vr6"), VR7 = (84, "vr7"), VR8 = (85, "vr8"), VR9 = (86, "vr9"), VR10 = (87, "vr10"), VR11 = (88, "vr11"), VR12 = (89, "vr12"), VR13 = (90, "vr13"), VR14 = (91, "vr14"), VR15 = (92, "vr15"), VR16 = (93, "vr16"), VR17 = (94, "vr17"), VR18 = (95, "vr18"), VR19 = (96, "vr19"), VR20 = (97, "vr20"), VR21 = (98, "vr21"), VR22 = (99, "vr22"), VR23 = (100, "vr23"), VR24 = (101, "vr24"), VR25 = (102, "vr25"), VR26 = (103, "vr26"), VR27 = (104, "vr27"), VR28 = (105, "vr28"), VR29 = (106, "vr29"), VR30 = (107, "vr30"), VR31 = (108, "vr31"), VSCR = (110, "vscr"), TFHAR = (114, "tfhar"), TFIAR = (115, "tfiar"), TEXASR = (116, "texasr"), }); #[cfg(test)] mod tests { #[test] #[cfg(feature = "std")] fn test_aarch64_registers() { use super::*; use std::collections::HashSet; let mut names = HashSet::new(); for n in (0..=39).chain(46..=127) { let name = AArch64::register_name(Register(n)) .unwrap_or_else(|| panic!("Register {} should have a name.", n)); assert!(names.insert(name)); } } #[test] #[cfg(feature = "std")] fn test_power64_registers() { use super::*; use std::collections::HashSet; let mut names = HashSet::new(); for n in (0..=63).chain(68..=75).chain(77..=108) { let name = PowerPc64::register_name(Register(n)) .unwrap_or_else(|| panic!("Register {} should have a name.", n)); assert!(names.insert(name)); } } } gimli-0.31.1/src/common.rs000064400000000000000000000330241046102023000134360ustar 00000000000000/// Whether the format of a compilation unit is 32- or 64-bit. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Format { /// 64-bit DWARF Dwarf64 = 8, /// 32-bit DWARF Dwarf32 = 4, } impl Format { /// Return the serialized size of an initial length field for the format. #[inline] pub fn initial_length_size(self) -> u8 { match self { Format::Dwarf32 => 4, Format::Dwarf64 => 12, } } /// Return the natural word size for the format #[inline] pub fn word_size(self) -> u8 { match self { Format::Dwarf32 => 4, Format::Dwarf64 => 8, } } } /// Which vendor extensions to support. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Vendor { /// A default set of extensions, including some common GNU extensions. Default, /// AAarch64 extensions. AArch64, } /// Encoding parameters that are commonly used for multiple DWARF sections. /// /// This is intended to be small enough to pass by value. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] // `address_size` and `format` are used more often than `version`, so keep // them first. #[repr(C)] pub struct Encoding { /// The size of an address. pub address_size: u8, /// Whether the DWARF format is 32- or 64-bit. pub format: Format, /// The DWARF version of the header. pub version: u16, } /// Encoding parameters for a line number program. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LineEncoding { /// The size in bytes of the smallest target machine instruction. pub minimum_instruction_length: u8, /// The maximum number of individual operations that may be encoded in an /// instruction. pub maximum_operations_per_instruction: u8, /// The initial value of the `is_stmt` register. pub default_is_stmt: bool, /// The minimum value which a special opcode can add to the line register. pub line_base: i8, /// The range of values which a special opcode can add to the line register. pub line_range: u8, } impl Default for LineEncoding { fn default() -> Self { // Values from LLVM. LineEncoding { minimum_instruction_length: 1, maximum_operations_per_instruction: 1, default_is_stmt: true, line_base: -5, line_range: 14, } } } /// A DWARF register number. /// /// The meaning of this value is ABI dependent. This is generally encoded as /// a ULEB128, but supported architectures need 16 bits at most. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Register(pub u16); /// An offset into the `.debug_abbrev` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DebugAbbrevOffset(pub T); /// An offset to a set of entries in the `.debug_addr` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugAddrBase(pub T); /// An index into a set of addresses in the `.debug_addr` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugAddrIndex(pub T); /// An offset into the `.debug_aranges` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugArangesOffset(pub T); /// An offset into the `.debug_info` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct DebugInfoOffset(pub T); /// An offset into the `.debug_line` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugLineOffset(pub T); /// An offset into the `.debug_line_str` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugLineStrOffset(pub T); /// An offset into either the `.debug_loc` section or the `.debug_loclists` section, /// depending on the version of the unit the offset was contained in. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LocationListsOffset(pub T); /// An offset to a set of location list offsets in the `.debug_loclists` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugLocListsBase(pub T); /// An index into a set of location list offsets in the `.debug_loclists` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugLocListsIndex(pub T); /// An offset into the `.debug_macinfo` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DebugMacinfoOffset(pub T); /// An offset into the `.debug_macro` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DebugMacroOffset(pub T); /// An offset into either the `.debug_ranges` section or the `.debug_rnglists` section, /// depending on the version of the unit the offset was contained in. /// /// If this is from a DWARF 4 DWO file, then it must additionally be offset by the /// value of `DW_AT_GNU_ranges_base`. You can use `Dwarf::ranges_offset_from_raw` to do this. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RawRangeListsOffset(pub T); /// An offset into either the `.debug_ranges` section or the `.debug_rnglists` section, /// depending on the version of the unit the offset was contained in. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RangeListsOffset(pub T); /// An offset to a set of range list offsets in the `.debug_rnglists` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugRngListsBase(pub T); /// An index into a set of range list offsets in the `.debug_rnglists` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugRngListsIndex(pub T); /// An offset into the `.debug_str` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugStrOffset(pub T); /// An offset to a set of entries in the `.debug_str_offsets` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugStrOffsetsBase(pub T); /// An index into a set of entries in the `.debug_str_offsets` section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DebugStrOffsetsIndex(pub T); /// An offset into the `.debug_types` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct DebugTypesOffset(pub T); /// A type signature as used in the `.debug_types` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DebugTypeSignature(pub u64); /// An offset into the `.debug_frame` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DebugFrameOffset(pub T); impl From for DebugFrameOffset { #[inline] fn from(o: T) -> Self { DebugFrameOffset(o) } } /// An offset into the `.eh_frame` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EhFrameOffset(pub T); impl From for EhFrameOffset { #[inline] fn from(o: T) -> Self { EhFrameOffset(o) } } /// An offset into the `.debug_info` or `.debug_types` sections. #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum UnitSectionOffset { /// An offset into the `.debug_info` section. DebugInfoOffset(DebugInfoOffset), /// An offset into the `.debug_types` section. DebugTypesOffset(DebugTypesOffset), } impl From> for UnitSectionOffset { fn from(offset: DebugInfoOffset) -> Self { UnitSectionOffset::DebugInfoOffset(offset) } } impl From> for UnitSectionOffset { fn from(offset: DebugTypesOffset) -> Self { UnitSectionOffset::DebugTypesOffset(offset) } } impl UnitSectionOffset where T: Clone, { /// Returns the `DebugInfoOffset` inside, or `None` otherwise. pub fn as_debug_info_offset(&self) -> Option> { match self { UnitSectionOffset::DebugInfoOffset(offset) => Some(offset.clone()), UnitSectionOffset::DebugTypesOffset(_) => None, } } /// Returns the `DebugTypesOffset` inside, or `None` otherwise. pub fn as_debug_types_offset(&self) -> Option> { match self { UnitSectionOffset::DebugInfoOffset(_) => None, UnitSectionOffset::DebugTypesOffset(offset) => Some(offset.clone()), } } } /// An identifier for a DWARF section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum SectionId { /// The `.debug_abbrev` section. DebugAbbrev, /// The `.debug_addr` section. DebugAddr, /// The `.debug_aranges` section. DebugAranges, /// The `.debug_cu_index` section. DebugCuIndex, /// The `.debug_frame` section. DebugFrame, /// The `.eh_frame` section. EhFrame, /// The `.eh_frame_hdr` section. EhFrameHdr, /// The `.debug_info` section. DebugInfo, /// The `.debug_line` section. DebugLine, /// The `.debug_line_str` section. DebugLineStr, /// The `.debug_loc` section. DebugLoc, /// The `.debug_loclists` section. DebugLocLists, /// The `.debug_macinfo` section. DebugMacinfo, /// The `.debug_macro` section. DebugMacro, /// The `.debug_pubnames` section. DebugPubNames, /// The `.debug_pubtypes` section. DebugPubTypes, /// The `.debug_ranges` section. DebugRanges, /// The `.debug_rnglists` section. DebugRngLists, /// The `.debug_str` section. DebugStr, /// The `.debug_str_offsets` section. DebugStrOffsets, /// The `.debug_tu_index` section. DebugTuIndex, /// The `.debug_types` section. DebugTypes, } impl SectionId { /// Returns the ELF section name for this kind. pub fn name(self) -> &'static str { match self { SectionId::DebugAbbrev => ".debug_abbrev", SectionId::DebugAddr => ".debug_addr", SectionId::DebugAranges => ".debug_aranges", SectionId::DebugCuIndex => ".debug_cu_index", SectionId::DebugFrame => ".debug_frame", SectionId::EhFrame => ".eh_frame", SectionId::EhFrameHdr => ".eh_frame_hdr", SectionId::DebugInfo => ".debug_info", SectionId::DebugLine => ".debug_line", SectionId::DebugLineStr => ".debug_line_str", SectionId::DebugLoc => ".debug_loc", SectionId::DebugLocLists => ".debug_loclists", SectionId::DebugMacinfo => ".debug_macinfo", SectionId::DebugMacro => ".debug_macro", SectionId::DebugPubNames => ".debug_pubnames", SectionId::DebugPubTypes => ".debug_pubtypes", SectionId::DebugRanges => ".debug_ranges", SectionId::DebugRngLists => ".debug_rnglists", SectionId::DebugStr => ".debug_str", SectionId::DebugStrOffsets => ".debug_str_offsets", SectionId::DebugTuIndex => ".debug_tu_index", SectionId::DebugTypes => ".debug_types", } } /// Returns the ELF section name for this kind, when found in a .dwo or .dwp file. pub fn dwo_name(self) -> Option<&'static str> { Some(match self { SectionId::DebugAbbrev => ".debug_abbrev.dwo", SectionId::DebugCuIndex => ".debug_cu_index", SectionId::DebugInfo => ".debug_info.dwo", SectionId::DebugLine => ".debug_line.dwo", // The debug_loc section can be present in the dwo when using the // GNU split-dwarf extension to DWARF4. SectionId::DebugLoc => ".debug_loc.dwo", SectionId::DebugLocLists => ".debug_loclists.dwo", SectionId::DebugMacinfo => ".debug_macinfo.dwo", SectionId::DebugMacro => ".debug_macro.dwo", SectionId::DebugRngLists => ".debug_rnglists.dwo", SectionId::DebugStr => ".debug_str.dwo", SectionId::DebugStrOffsets => ".debug_str_offsets.dwo", SectionId::DebugTuIndex => ".debug_tu_index", SectionId::DebugTypes => ".debug_types.dwo", _ => return None, }) } /// Returns the XCOFF section name for this kind. pub fn xcoff_name(self) -> Option<&'static str> { Some(match self { SectionId::DebugAbbrev => ".dwabrev", SectionId::DebugAranges => ".dwarnge", SectionId::DebugFrame => ".dwframe", SectionId::DebugInfo => ".dwinfo", SectionId::DebugLine => ".dwline", SectionId::DebugLoc => ".dwloc", SectionId::DebugMacinfo => ".dwmac", SectionId::DebugPubNames => ".dwpbnms", SectionId::DebugPubTypes => ".dwpbtyp", SectionId::DebugRanges => ".dwrnges", SectionId::DebugStr => ".dwstr", _ => return None, }) } /// Returns true if this is a mergeable string section. /// /// This is useful for determining the correct section flags. pub fn is_string(self) -> bool { matches!(self, SectionId::DebugStr | SectionId::DebugLineStr) } } /// An optionally-provided implementation-defined compilation unit ID to enable /// split DWARF and linking a split compilation unit back together. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DwoId(pub u64); /// The "type" of file with DWARF debugging information. This determines, among other things, /// which files DWARF sections should be loaded from. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DwarfFileType { /// A normal executable or object file. Main, /// A .dwo split DWARF file. Dwo, // TODO: Supplementary files, .dwps? } impl Default for DwarfFileType { fn default() -> Self { DwarfFileType::Main } } gimli-0.31.1/src/constants.rs000064400000000000000000001170241046102023000141650ustar 00000000000000// This file originally from https://github.com/philipc/rust-dwarf/ and // distributed under either MIT or Apache 2.0 licenses. // // Copyright 2016 The rust-dwarf Developers // // 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 // // https://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. //! Constant definitions. //! //! The DWARF spec's `DW_AT_*` type is represented as `struct DwAt(u16)`, //! `DW_FORM_*` as `DwForm(u16)`, etc. //! //! There are also exported const definitions for each constant. #![allow(non_upper_case_globals)] #![allow(missing_docs)] use core::{fmt, ops}; // The `dw!` macro turns this: // // dw!(DwFoo(u32) { // DW_FOO_bar = 0, // DW_FOO_baz = 1, // DW_FOO_bang = 2, // }); // // into this: // // #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] // pub struct DwFoo(pub u32); // // pub const DW_FOO_bar: DwFoo = DwFoo(0); // pub const DW_FOO_baz: DwFoo = DwFoo(1); // pub const DW_FOO_bang: DwFoo = DwFoo(2); // // impl DwFoo { // pub fn static_string(&self) -> Option<&'static str> { // ... // } // } // // impl fmt::Display for DwFoo { // fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { // ... // } // } macro_rules! dw { ($(#[$meta:meta])* $struct_name:ident($struct_type:ty) { $($name:ident = $val:expr),+ $(,)? } $(, aliases { $($alias_name:ident = $alias_val:expr),+ $(,)? })? ) => { $(#[$meta])* #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct $struct_name(pub $struct_type); $( pub const $name: $struct_name = $struct_name($val); )+ $($( pub const $alias_name: $struct_name = $struct_name($alias_val); )+)* impl $struct_name { pub fn static_string(&self) -> Option<&'static str> { Some(match *self { $( $name => stringify!($name), )+ _ => return None, }) } } impl fmt::Display for $struct_name { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { if let Some(s) = self.static_string() { f.pad(s) } else { #[cfg(feature = "read")] { f.pad(&format!("Unknown {}: {}", stringify!($struct_name), self.0)) } #[cfg(not(feature = "read"))] { write!(f, "Unknown {}: {}", stringify!($struct_name), self.0) } } } } }; } dw!( /// The section type field in a `.dwp` unit index. /// /// This is used for version 5 and later. /// /// See Section 7.3.5. DwSect(u32) { DW_SECT_INFO = 1, DW_SECT_ABBREV = 3, DW_SECT_LINE = 4, DW_SECT_LOCLISTS = 5, DW_SECT_STR_OFFSETS = 6, DW_SECT_MACRO = 7, DW_SECT_RNGLISTS = 8, }); dw!( /// The section type field in a `.dwp` unit index with version 2. DwSectV2(u32) { DW_SECT_V2_INFO = 1, DW_SECT_V2_TYPES = 2, DW_SECT_V2_ABBREV = 3, DW_SECT_V2_LINE = 4, DW_SECT_V2_LOC = 5, DW_SECT_V2_STR_OFFSETS = 6, DW_SECT_V2_MACINFO = 7, DW_SECT_V2_MACRO = 8, }); dw!( /// The unit type field in a unit header. /// /// See Section 7.5.1, Table 7.2. DwUt(u8) { DW_UT_compile = 0x01, DW_UT_type = 0x02, DW_UT_partial = 0x03, DW_UT_skeleton = 0x04, DW_UT_split_compile = 0x05, DW_UT_split_type = 0x06, DW_UT_lo_user = 0x80, DW_UT_hi_user = 0xff, }); dw!( /// The opcode for a call frame instruction. /// /// Section 7.24: /// > Call frame instructions are encoded in one or more bytes. The primary /// > opcode is encoded in the high order two bits of the first byte (that is, /// > opcode = byte >> 6). An operand or extended opcode may be encoded in the /// > low order 6 bits. Additional operands are encoded in subsequent bytes. DwCfa(u8) { DW_CFA_advance_loc = 0x01 << 6, DW_CFA_offset = 0x02 << 6, DW_CFA_restore = 0x03 << 6, DW_CFA_nop = 0, DW_CFA_set_loc = 0x01, DW_CFA_advance_loc1 = 0x02, DW_CFA_advance_loc2 = 0x03, DW_CFA_advance_loc4 = 0x04, DW_CFA_offset_extended = 0x05, DW_CFA_restore_extended = 0x06, DW_CFA_undefined = 0x07, DW_CFA_same_value = 0x08, DW_CFA_register = 0x09, DW_CFA_remember_state = 0x0a, DW_CFA_restore_state = 0x0b, DW_CFA_def_cfa = 0x0c, DW_CFA_def_cfa_register = 0x0d, DW_CFA_def_cfa_offset = 0x0e, DW_CFA_def_cfa_expression = 0x0f, DW_CFA_expression = 0x10, DW_CFA_offset_extended_sf = 0x11, DW_CFA_def_cfa_sf = 0x12, DW_CFA_def_cfa_offset_sf = 0x13, DW_CFA_val_offset = 0x14, DW_CFA_val_offset_sf = 0x15, DW_CFA_val_expression = 0x16, DW_CFA_lo_user = 0x1c, DW_CFA_hi_user = 0x3f, DW_CFA_MIPS_advance_loc8 = 0x1d, DW_CFA_GNU_window_save = 0x2d, DW_CFA_GNU_args_size = 0x2e, DW_CFA_GNU_negative_offset_extended = 0x2f, }, aliases { DW_CFA_AARCH64_negate_ra_state = 0x2d, }); dw!( /// The child determination encodings for DIE attributes. /// /// See Section 7.5.3, Table 7.4. DwChildren(u8) { DW_CHILDREN_no = 0, DW_CHILDREN_yes = 1, }); dw!( /// The tag encodings for DIE attributes. /// /// See Section 7.5.3, Table 7.3. DwTag(u16) { DW_TAG_null = 0x00, DW_TAG_array_type = 0x01, DW_TAG_class_type = 0x02, DW_TAG_entry_point = 0x03, DW_TAG_enumeration_type = 0x04, DW_TAG_formal_parameter = 0x05, DW_TAG_imported_declaration = 0x08, DW_TAG_label = 0x0a, DW_TAG_lexical_block = 0x0b, DW_TAG_member = 0x0d, DW_TAG_pointer_type = 0x0f, DW_TAG_reference_type = 0x10, DW_TAG_compile_unit = 0x11, DW_TAG_string_type = 0x12, DW_TAG_structure_type = 0x13, DW_TAG_subroutine_type = 0x15, DW_TAG_typedef = 0x16, DW_TAG_union_type = 0x17, DW_TAG_unspecified_parameters = 0x18, DW_TAG_variant = 0x19, DW_TAG_common_block = 0x1a, DW_TAG_common_inclusion = 0x1b, DW_TAG_inheritance = 0x1c, DW_TAG_inlined_subroutine = 0x1d, DW_TAG_module = 0x1e, DW_TAG_ptr_to_member_type = 0x1f, DW_TAG_set_type = 0x20, DW_TAG_subrange_type = 0x21, DW_TAG_with_stmt = 0x22, DW_TAG_access_declaration = 0x23, DW_TAG_base_type = 0x24, DW_TAG_catch_block = 0x25, DW_TAG_const_type = 0x26, DW_TAG_constant = 0x27, DW_TAG_enumerator = 0x28, DW_TAG_file_type = 0x29, DW_TAG_friend = 0x2a, DW_TAG_namelist = 0x2b, DW_TAG_namelist_item = 0x2c, DW_TAG_packed_type = 0x2d, DW_TAG_subprogram = 0x2e, DW_TAG_template_type_parameter = 0x2f, DW_TAG_template_value_parameter = 0x30, DW_TAG_thrown_type = 0x31, DW_TAG_try_block = 0x32, DW_TAG_variant_part = 0x33, DW_TAG_variable = 0x34, DW_TAG_volatile_type = 0x35, // DWARF 3. DW_TAG_dwarf_procedure = 0x36, DW_TAG_restrict_type = 0x37, DW_TAG_interface_type = 0x38, DW_TAG_namespace = 0x39, DW_TAG_imported_module = 0x3a, DW_TAG_unspecified_type = 0x3b, DW_TAG_partial_unit = 0x3c, DW_TAG_imported_unit = 0x3d, DW_TAG_condition = 0x3f, DW_TAG_shared_type = 0x40, // DWARF 4. DW_TAG_type_unit = 0x41, DW_TAG_rvalue_reference_type = 0x42, DW_TAG_template_alias = 0x43, // DWARF 5. DW_TAG_coarray_type = 0x44, DW_TAG_generic_subrange = 0x45, DW_TAG_dynamic_type = 0x46, DW_TAG_atomic_type = 0x47, DW_TAG_call_site = 0x48, DW_TAG_call_site_parameter = 0x49, DW_TAG_skeleton_unit = 0x4a, DW_TAG_immutable_type = 0x4b, DW_TAG_lo_user = 0x4080, DW_TAG_hi_user = 0xffff, // SGI/MIPS extensions. DW_TAG_MIPS_loop = 0x4081, // HP extensions. DW_TAG_HP_array_descriptor = 0x4090, DW_TAG_HP_Bliss_field = 0x4091, DW_TAG_HP_Bliss_field_set = 0x4092, // GNU extensions. DW_TAG_format_label = 0x4101, DW_TAG_function_template = 0x4102, DW_TAG_class_template = 0x4103, DW_TAG_GNU_BINCL = 0x4104, DW_TAG_GNU_EINCL = 0x4105, DW_TAG_GNU_template_template_param = 0x4106, DW_TAG_GNU_template_parameter_pack = 0x4107, DW_TAG_GNU_formal_parameter_pack = 0x4108, DW_TAG_GNU_call_site = 0x4109, DW_TAG_GNU_call_site_parameter = 0x410a, DW_TAG_APPLE_property = 0x4200, // SUN extensions. DW_TAG_SUN_function_template = 0x4201, DW_TAG_SUN_class_template = 0x4202, DW_TAG_SUN_struct_template = 0x4203, DW_TAG_SUN_union_template = 0x4204, DW_TAG_SUN_indirect_inheritance = 0x4205, DW_TAG_SUN_codeflags = 0x4206, DW_TAG_SUN_memop_info = 0x4207, DW_TAG_SUN_omp_child_func = 0x4208, DW_TAG_SUN_rtti_descriptor = 0x4209, DW_TAG_SUN_dtor_info = 0x420a, DW_TAG_SUN_dtor = 0x420b, DW_TAG_SUN_f90_interface = 0x420c, DW_TAG_SUN_fortran_vax_structure = 0x420d, // ALTIUM extensions. DW_TAG_ALTIUM_circ_type = 0x5101, DW_TAG_ALTIUM_mwa_circ_type = 0x5102, DW_TAG_ALTIUM_rev_carry_type = 0x5103, DW_TAG_ALTIUM_rom = 0x5111, // Extensions for UPC. DW_TAG_upc_shared_type = 0x8765, DW_TAG_upc_strict_type = 0x8766, DW_TAG_upc_relaxed_type = 0x8767, // PGI (STMicroelectronics) extensions. DW_TAG_PGI_kanji_type = 0xa000, DW_TAG_PGI_interface_block = 0xa020, // Borland extensions. DW_TAG_BORLAND_property = 0xb000, DW_TAG_BORLAND_Delphi_string = 0xb001, DW_TAG_BORLAND_Delphi_dynamic_array = 0xb002, DW_TAG_BORLAND_Delphi_set = 0xb003, DW_TAG_BORLAND_Delphi_variant = 0xb004, }); dw!( /// The attribute encodings for DIE attributes. /// /// See Section 7.5.4, Table 7.5. DwAt(u16) { DW_AT_null = 0x00, DW_AT_sibling = 0x01, DW_AT_location = 0x02, DW_AT_name = 0x03, DW_AT_ordering = 0x09, DW_AT_byte_size = 0x0b, DW_AT_bit_offset = 0x0c, DW_AT_bit_size = 0x0d, DW_AT_stmt_list = 0x10, DW_AT_low_pc = 0x11, DW_AT_high_pc = 0x12, DW_AT_language = 0x13, DW_AT_discr = 0x15, DW_AT_discr_value = 0x16, DW_AT_visibility = 0x17, DW_AT_import = 0x18, DW_AT_string_length = 0x19, DW_AT_common_reference = 0x1a, DW_AT_comp_dir = 0x1b, DW_AT_const_value = 0x1c, DW_AT_containing_type = 0x1d, DW_AT_default_value = 0x1e, DW_AT_inline = 0x20, DW_AT_is_optional = 0x21, DW_AT_lower_bound = 0x22, DW_AT_producer = 0x25, DW_AT_prototyped = 0x27, DW_AT_return_addr = 0x2a, DW_AT_start_scope = 0x2c, DW_AT_bit_stride = 0x2e, DW_AT_upper_bound = 0x2f, DW_AT_abstract_origin = 0x31, DW_AT_accessibility = 0x32, DW_AT_address_class = 0x33, DW_AT_artificial = 0x34, DW_AT_base_types = 0x35, DW_AT_calling_convention = 0x36, DW_AT_count = 0x37, DW_AT_data_member_location = 0x38, DW_AT_decl_column = 0x39, DW_AT_decl_file = 0x3a, DW_AT_decl_line = 0x3b, DW_AT_declaration = 0x3c, DW_AT_discr_list = 0x3d, DW_AT_encoding = 0x3e, DW_AT_external = 0x3f, DW_AT_frame_base = 0x40, DW_AT_friend = 0x41, DW_AT_identifier_case = 0x42, DW_AT_macro_info = 0x43, DW_AT_namelist_item = 0x44, DW_AT_priority = 0x45, DW_AT_segment = 0x46, DW_AT_specification = 0x47, DW_AT_static_link = 0x48, DW_AT_type = 0x49, DW_AT_use_location = 0x4a, DW_AT_variable_parameter = 0x4b, DW_AT_virtuality = 0x4c, DW_AT_vtable_elem_location = 0x4d, // DWARF 3. DW_AT_allocated = 0x4e, DW_AT_associated = 0x4f, DW_AT_data_location = 0x50, DW_AT_byte_stride = 0x51, DW_AT_entry_pc = 0x52, DW_AT_use_UTF8 = 0x53, DW_AT_extension = 0x54, DW_AT_ranges = 0x55, DW_AT_trampoline = 0x56, DW_AT_call_column = 0x57, DW_AT_call_file = 0x58, DW_AT_call_line = 0x59, DW_AT_description = 0x5a, DW_AT_binary_scale = 0x5b, DW_AT_decimal_scale = 0x5c, DW_AT_small = 0x5d, DW_AT_decimal_sign = 0x5e, DW_AT_digit_count = 0x5f, DW_AT_picture_string = 0x60, DW_AT_mutable = 0x61, DW_AT_threads_scaled = 0x62, DW_AT_explicit = 0x63, DW_AT_object_pointer = 0x64, DW_AT_endianity = 0x65, DW_AT_elemental = 0x66, DW_AT_pure = 0x67, DW_AT_recursive = 0x68, // DWARF 4. DW_AT_signature = 0x69, DW_AT_main_subprogram = 0x6a, DW_AT_data_bit_offset = 0x6b, DW_AT_const_expr = 0x6c, DW_AT_enum_class = 0x6d, DW_AT_linkage_name = 0x6e, // DWARF 5. DW_AT_string_length_bit_size = 0x6f, DW_AT_string_length_byte_size = 0x70, DW_AT_rank = 0x71, DW_AT_str_offsets_base = 0x72, DW_AT_addr_base = 0x73, DW_AT_rnglists_base = 0x74, DW_AT_dwo_name = 0x76, DW_AT_reference = 0x77, DW_AT_rvalue_reference = 0x78, DW_AT_macros = 0x79, DW_AT_call_all_calls = 0x7a, DW_AT_call_all_source_calls = 0x7b, DW_AT_call_all_tail_calls = 0x7c, DW_AT_call_return_pc = 0x7d, DW_AT_call_value = 0x7e, DW_AT_call_origin = 0x7f, DW_AT_call_parameter = 0x80, DW_AT_call_pc = 0x81, DW_AT_call_tail_call = 0x82, DW_AT_call_target = 0x83, DW_AT_call_target_clobbered = 0x84, DW_AT_call_data_location = 0x85, DW_AT_call_data_value = 0x86, DW_AT_noreturn = 0x87, DW_AT_alignment = 0x88, DW_AT_export_symbols = 0x89, DW_AT_deleted = 0x8a, DW_AT_defaulted = 0x8b, DW_AT_loclists_base = 0x8c, DW_AT_lo_user = 0x2000, DW_AT_hi_user = 0x3fff, // SGI/MIPS extensions. DW_AT_MIPS_fde = 0x2001, DW_AT_MIPS_loop_begin = 0x2002, DW_AT_MIPS_tail_loop_begin = 0x2003, DW_AT_MIPS_epilog_begin = 0x2004, DW_AT_MIPS_loop_unroll_factor = 0x2005, DW_AT_MIPS_software_pipeline_depth = 0x2006, DW_AT_MIPS_linkage_name = 0x2007, DW_AT_MIPS_stride = 0x2008, DW_AT_MIPS_abstract_name = 0x2009, DW_AT_MIPS_clone_origin = 0x200a, DW_AT_MIPS_has_inlines = 0x200b, DW_AT_MIPS_stride_byte = 0x200c, DW_AT_MIPS_stride_elem = 0x200d, DW_AT_MIPS_ptr_dopetype = 0x200e, DW_AT_MIPS_allocatable_dopetype = 0x200f, DW_AT_MIPS_assumed_shape_dopetype = 0x2010, // This one appears to have only been implemented by Open64 for // fortran and may conflict with other extensions. DW_AT_MIPS_assumed_size = 0x2011, // TODO: HP/CPQ extensions. // These conflict with the MIPS extensions. DW_AT_INTEL_other_endian = 0x2026, // GNU extensions DW_AT_sf_names = 0x2101, DW_AT_src_info = 0x2102, DW_AT_mac_info = 0x2103, DW_AT_src_coords = 0x2104, DW_AT_body_begin = 0x2105, DW_AT_body_end = 0x2106, DW_AT_GNU_vector = 0x2107, DW_AT_GNU_guarded_by = 0x2108, DW_AT_GNU_pt_guarded_by = 0x2109, DW_AT_GNU_guarded = 0x210a, DW_AT_GNU_pt_guarded = 0x210b, DW_AT_GNU_locks_excluded = 0x210c, DW_AT_GNU_exclusive_locks_required = 0x210d, DW_AT_GNU_shared_locks_required = 0x210e, DW_AT_GNU_odr_signature = 0x210f, DW_AT_GNU_template_name = 0x2110, DW_AT_GNU_call_site_value = 0x2111, DW_AT_GNU_call_site_data_value = 0x2112, DW_AT_GNU_call_site_target = 0x2113, DW_AT_GNU_call_site_target_clobbered = 0x2114, DW_AT_GNU_tail_call = 0x2115, DW_AT_GNU_all_tail_call_sites = 0x2116, DW_AT_GNU_all_call_sites = 0x2117, DW_AT_GNU_all_source_call_sites = 0x2118, DW_AT_GNU_macros = 0x2119, DW_AT_GNU_deleted = 0x211a, // Extensions for Fission proposal. DW_AT_GNU_dwo_name = 0x2130, DW_AT_GNU_dwo_id = 0x2131, DW_AT_GNU_ranges_base = 0x2132, DW_AT_GNU_addr_base = 0x2133, DW_AT_GNU_pubnames = 0x2134, DW_AT_GNU_pubtypes = 0x2135, DW_AT_GNU_discriminator = 0x2136, DW_AT_GNU_locviews = 0x2137, DW_AT_GNU_entry_view = 0x2138, // Conflict with Sun. // DW_AT_VMS_rtnbeg_pd_address = 0x2201, // Sun extensions. DW_AT_SUN_template = 0x2201, DW_AT_SUN_alignment = 0x2202, DW_AT_SUN_vtable = 0x2203, DW_AT_SUN_count_guarantee = 0x2204, DW_AT_SUN_command_line = 0x2205, DW_AT_SUN_vbase = 0x2206, DW_AT_SUN_compile_options = 0x2207, DW_AT_SUN_language = 0x2208, DW_AT_SUN_browser_file = 0x2209, DW_AT_SUN_vtable_abi = 0x2210, DW_AT_SUN_func_offsets = 0x2211, DW_AT_SUN_cf_kind = 0x2212, DW_AT_SUN_vtable_index = 0x2213, DW_AT_SUN_omp_tpriv_addr = 0x2214, DW_AT_SUN_omp_child_func = 0x2215, DW_AT_SUN_func_offset = 0x2216, DW_AT_SUN_memop_type_ref = 0x2217, DW_AT_SUN_profile_id = 0x2218, DW_AT_SUN_memop_signature = 0x2219, DW_AT_SUN_obj_dir = 0x2220, DW_AT_SUN_obj_file = 0x2221, DW_AT_SUN_original_name = 0x2222, DW_AT_SUN_hwcprof_signature = 0x2223, DW_AT_SUN_amd64_parmdump = 0x2224, DW_AT_SUN_part_link_name = 0x2225, DW_AT_SUN_link_name = 0x2226, DW_AT_SUN_pass_with_const = 0x2227, DW_AT_SUN_return_with_const = 0x2228, DW_AT_SUN_import_by_name = 0x2229, DW_AT_SUN_f90_pointer = 0x222a, DW_AT_SUN_pass_by_ref = 0x222b, DW_AT_SUN_f90_allocatable = 0x222c, DW_AT_SUN_f90_assumed_shape_array = 0x222d, DW_AT_SUN_c_vla = 0x222e, DW_AT_SUN_return_value_ptr = 0x2230, DW_AT_SUN_dtor_start = 0x2231, DW_AT_SUN_dtor_length = 0x2232, DW_AT_SUN_dtor_state_initial = 0x2233, DW_AT_SUN_dtor_state_final = 0x2234, DW_AT_SUN_dtor_state_deltas = 0x2235, DW_AT_SUN_import_by_lname = 0x2236, DW_AT_SUN_f90_use_only = 0x2237, DW_AT_SUN_namelist_spec = 0x2238, DW_AT_SUN_is_omp_child_func = 0x2239, DW_AT_SUN_fortran_main_alias = 0x223a, DW_AT_SUN_fortran_based = 0x223b, DW_AT_ALTIUM_loclist = 0x2300, DW_AT_use_GNAT_descriptive_type = 0x2301, DW_AT_GNAT_descriptive_type = 0x2302, DW_AT_GNU_numerator = 0x2303, DW_AT_GNU_denominator = 0x2304, DW_AT_GNU_bias = 0x2305, DW_AT_upc_threads_scaled = 0x3210, // PGI (STMicroelectronics) extensions. DW_AT_PGI_lbase = 0x3a00, DW_AT_PGI_soffset = 0x3a01, DW_AT_PGI_lstride = 0x3a02, // Borland extensions. DW_AT_BORLAND_property_read = 0x3b11, DW_AT_BORLAND_property_write = 0x3b12, DW_AT_BORLAND_property_implements = 0x3b13, DW_AT_BORLAND_property_index = 0x3b14, DW_AT_BORLAND_property_default = 0x3b15, DW_AT_BORLAND_Delphi_unit = 0x3b20, DW_AT_BORLAND_Delphi_class = 0x3b21, DW_AT_BORLAND_Delphi_record = 0x3b22, DW_AT_BORLAND_Delphi_metaclass = 0x3b23, DW_AT_BORLAND_Delphi_constructor = 0x3b24, DW_AT_BORLAND_Delphi_destructor = 0x3b25, DW_AT_BORLAND_Delphi_anonymous_method = 0x3b26, DW_AT_BORLAND_Delphi_interface = 0x3b27, DW_AT_BORLAND_Delphi_ABI = 0x3b28, DW_AT_BORLAND_Delphi_return = 0x3b29, DW_AT_BORLAND_Delphi_frameptr = 0x3b30, DW_AT_BORLAND_closure = 0x3b31, // LLVM project extensions. DW_AT_LLVM_include_path = 0x3e00, DW_AT_LLVM_config_macros = 0x3e01, DW_AT_LLVM_isysroot = 0x3e02, // Apple extensions. DW_AT_APPLE_optimized = 0x3fe1, DW_AT_APPLE_flags = 0x3fe2, DW_AT_APPLE_isa = 0x3fe3, DW_AT_APPLE_block = 0x3fe4, DW_AT_APPLE_major_runtime_vers = 0x3fe5, DW_AT_APPLE_runtime_class = 0x3fe6, DW_AT_APPLE_omit_frame_ptr = 0x3fe7, DW_AT_APPLE_property_name = 0x3fe8, DW_AT_APPLE_property_getter = 0x3fe9, DW_AT_APPLE_property_setter = 0x3fea, DW_AT_APPLE_property_attribute = 0x3feb, DW_AT_APPLE_objc_complete_type = 0x3fec, DW_AT_APPLE_property = 0x3fed }); dw!( /// The attribute form encodings for DIE attributes. /// /// See Section 7.5.6, Table 7.6. DwForm(u16) { DW_FORM_null = 0x00, DW_FORM_addr = 0x01, DW_FORM_block2 = 0x03, DW_FORM_block4 = 0x04, DW_FORM_data2 = 0x05, DW_FORM_data4 = 0x06, DW_FORM_data8 = 0x07, DW_FORM_string = 0x08, DW_FORM_block = 0x09, DW_FORM_block1 = 0x0a, DW_FORM_data1 = 0x0b, DW_FORM_flag = 0x0c, DW_FORM_sdata = 0x0d, DW_FORM_strp = 0x0e, DW_FORM_udata = 0x0f, DW_FORM_ref_addr = 0x10, DW_FORM_ref1 = 0x11, DW_FORM_ref2 = 0x12, DW_FORM_ref4 = 0x13, DW_FORM_ref8 = 0x14, DW_FORM_ref_udata = 0x15, DW_FORM_indirect = 0x16, // DWARF 4. DW_FORM_sec_offset = 0x17, DW_FORM_exprloc = 0x18, DW_FORM_flag_present = 0x19, DW_FORM_ref_sig8 = 0x20, // DWARF 5. DW_FORM_strx = 0x1a, DW_FORM_addrx = 0x1b, DW_FORM_ref_sup4 = 0x1c, DW_FORM_strp_sup = 0x1d, DW_FORM_data16 = 0x1e, DW_FORM_line_strp = 0x1f, DW_FORM_implicit_const = 0x21, DW_FORM_loclistx = 0x22, DW_FORM_rnglistx = 0x23, DW_FORM_ref_sup8 = 0x24, DW_FORM_strx1 = 0x25, DW_FORM_strx2 = 0x26, DW_FORM_strx3 = 0x27, DW_FORM_strx4 = 0x28, DW_FORM_addrx1 = 0x29, DW_FORM_addrx2 = 0x2a, DW_FORM_addrx3 = 0x2b, DW_FORM_addrx4 = 0x2c, // Extensions for Fission proposal DW_FORM_GNU_addr_index = 0x1f01, DW_FORM_GNU_str_index = 0x1f02, // Alternate debug sections proposal (output of "dwz" tool). DW_FORM_GNU_ref_alt = 0x1f20, DW_FORM_GNU_strp_alt = 0x1f21 }); dw!( /// The encodings of the constants used in the `DW_AT_encoding` attribute. /// /// See Section 7.8, Table 7.11. DwAte(u8) { DW_ATE_address = 0x01, DW_ATE_boolean = 0x02, DW_ATE_complex_float = 0x03, DW_ATE_float = 0x04, DW_ATE_signed = 0x05, DW_ATE_signed_char = 0x06, DW_ATE_unsigned = 0x07, DW_ATE_unsigned_char = 0x08, // DWARF 3. DW_ATE_imaginary_float = 0x09, DW_ATE_packed_decimal = 0x0a, DW_ATE_numeric_string = 0x0b, DW_ATE_edited = 0x0c, DW_ATE_signed_fixed = 0x0d, DW_ATE_unsigned_fixed = 0x0e, DW_ATE_decimal_float = 0x0f , // DWARF 4. DW_ATE_UTF = 0x10, DW_ATE_UCS = 0x11, DW_ATE_ASCII = 0x12, DW_ATE_lo_user = 0x80, DW_ATE_hi_user = 0xff, }); dw!( /// The encodings of the constants used in location list entries. /// /// See Section 7.7.3, Table 7.10. DwLle(u8) { DW_LLE_end_of_list = 0x00, DW_LLE_base_addressx = 0x01, DW_LLE_startx_endx = 0x02, DW_LLE_startx_length = 0x03, DW_LLE_offset_pair = 0x04, DW_LLE_default_location = 0x05, DW_LLE_base_address = 0x06, DW_LLE_start_end = 0x07, DW_LLE_start_length = 0x08, DW_LLE_GNU_view_pair = 0x09, }); dw!( /// The encodings of the constants used in the `DW_AT_decimal_sign` attribute. /// /// See Section 7.8, Table 7.12. DwDs(u8) { DW_DS_unsigned = 0x01, DW_DS_leading_overpunch = 0x02, DW_DS_trailing_overpunch = 0x03, DW_DS_leading_separate = 0x04, DW_DS_trailing_separate = 0x05, }); dw!( /// The encodings of the constants used in the `DW_AT_endianity` attribute. /// /// See Section 7.8, Table 7.13. DwEnd(u8) { DW_END_default = 0x00, DW_END_big = 0x01, DW_END_little = 0x02, DW_END_lo_user = 0x40, DW_END_hi_user = 0xff, }); dw!( /// The encodings of the constants used in the `DW_AT_accessibility` attribute. /// /// See Section 7.9, Table 7.14. DwAccess(u8) { DW_ACCESS_public = 0x01, DW_ACCESS_protected = 0x02, DW_ACCESS_private = 0x03, }); dw!( /// The encodings of the constants used in the `DW_AT_visibility` attribute. /// /// See Section 7.10, Table 7.15. DwVis(u8) { DW_VIS_local = 0x01, DW_VIS_exported = 0x02, DW_VIS_qualified = 0x03, }); dw!( /// The encodings of the constants used in the `DW_AT_virtuality` attribute. /// /// See Section 7.11, Table 7.16. DwVirtuality(u8) { DW_VIRTUALITY_none = 0x00, DW_VIRTUALITY_virtual = 0x01, DW_VIRTUALITY_pure_virtual = 0x02, }); dw!( /// The encodings of the constants used in the `DW_AT_language` attribute. /// /// See Section 7.12, Table 7.17. DwLang(u16) { DW_LANG_C89 = 0x0001, DW_LANG_C = 0x0002, DW_LANG_Ada83 = 0x0003, DW_LANG_C_plus_plus = 0x0004, DW_LANG_Cobol74 = 0x0005, DW_LANG_Cobol85 = 0x0006, DW_LANG_Fortran77 = 0x0007, DW_LANG_Fortran90 = 0x0008, DW_LANG_Pascal83 = 0x0009, DW_LANG_Modula2 = 0x000a, DW_LANG_Java = 0x000b, DW_LANG_C99 = 0x000c, DW_LANG_Ada95 = 0x000d, DW_LANG_Fortran95 = 0x000e, DW_LANG_PLI = 0x000f, DW_LANG_ObjC = 0x0010, DW_LANG_ObjC_plus_plus = 0x0011, DW_LANG_UPC = 0x0012, DW_LANG_D = 0x0013, DW_LANG_Python = 0x0014, DW_LANG_OpenCL = 0x0015, DW_LANG_Go = 0x0016, DW_LANG_Modula3 = 0x0017, DW_LANG_Haskell = 0x0018, DW_LANG_C_plus_plus_03 = 0x0019, DW_LANG_C_plus_plus_11 = 0x001a, DW_LANG_OCaml = 0x001b, DW_LANG_Rust = 0x001c, DW_LANG_C11 = 0x001d, DW_LANG_Swift = 0x001e, DW_LANG_Julia = 0x001f, DW_LANG_Dylan = 0x0020, DW_LANG_C_plus_plus_14 = 0x0021, DW_LANG_Fortran03 = 0x0022, DW_LANG_Fortran08 = 0x0023, DW_LANG_RenderScript = 0x0024, DW_LANG_BLISS = 0x0025, DW_LANG_Kotlin = 0x0026, DW_LANG_Zig = 0x0027, DW_LANG_Crystal = 0x0028, DW_LANG_C_plus_plus_17 = 0x002a, DW_LANG_C_plus_plus_20 = 0x002b, DW_LANG_C17 = 0x002c, DW_LANG_Fortran18 = 0x002d, DW_LANG_Ada2005 = 0x002e, DW_LANG_Ada2012 = 0x002f, DW_LANG_lo_user = 0x8000, DW_LANG_hi_user = 0xffff, DW_LANG_Mips_Assembler = 0x8001, DW_LANG_GOOGLE_RenderScript = 0x8e57, DW_LANG_SUN_Assembler = 0x9001, DW_LANG_ALTIUM_Assembler = 0x9101, DW_LANG_BORLAND_Delphi = 0xb000, }); impl DwLang { /// Get the default DW_AT_lower_bound for this language. pub fn default_lower_bound(self) -> Option { match self { DW_LANG_C89 | DW_LANG_C | DW_LANG_C_plus_plus | DW_LANG_Java | DW_LANG_C99 | DW_LANG_ObjC | DW_LANG_ObjC_plus_plus | DW_LANG_UPC | DW_LANG_D | DW_LANG_Python | DW_LANG_OpenCL | DW_LANG_Go | DW_LANG_Haskell | DW_LANG_C_plus_plus_03 | DW_LANG_C_plus_plus_11 | DW_LANG_OCaml | DW_LANG_Rust | DW_LANG_C11 | DW_LANG_Swift | DW_LANG_Dylan | DW_LANG_C_plus_plus_14 | DW_LANG_RenderScript | DW_LANG_BLISS => Some(0), DW_LANG_Ada83 | DW_LANG_Cobol74 | DW_LANG_Cobol85 | DW_LANG_Fortran77 | DW_LANG_Fortran90 | DW_LANG_Pascal83 | DW_LANG_Modula2 | DW_LANG_Ada95 | DW_LANG_Fortran95 | DW_LANG_PLI | DW_LANG_Modula3 | DW_LANG_Julia | DW_LANG_Fortran03 | DW_LANG_Fortran08 => Some(1), _ => None, } } } dw!( /// The encodings of the constants used in the `DW_AT_address_class` attribute. /// /// There is only one value that is common to all target architectures. /// See Section 7.13. DwAddr(u64) { DW_ADDR_none = 0x00, }); dw!( /// The encodings of the constants used in the `DW_AT_identifier_case` attribute. /// /// See Section 7.14, Table 7.18. DwId(u8) { DW_ID_case_sensitive = 0x00, DW_ID_up_case = 0x01, DW_ID_down_case = 0x02, DW_ID_case_insensitive = 0x03, }); dw!( /// The encodings of the constants used in the `DW_AT_calling_convention` attribute. /// /// See Section 7.15, Table 7.19. DwCc(u8) { DW_CC_normal = 0x01, DW_CC_program = 0x02, DW_CC_nocall = 0x03, DW_CC_pass_by_reference = 0x04, DW_CC_pass_by_value = 0x05, DW_CC_lo_user = 0x40, DW_CC_hi_user = 0xff, }); dw!( /// The encodings of the constants used in the `DW_AT_inline` attribute. /// /// See Section 7.16, Table 7.20. DwInl(u8) { DW_INL_not_inlined = 0x00, DW_INL_inlined = 0x01, DW_INL_declared_not_inlined = 0x02, DW_INL_declared_inlined = 0x03, }); dw!( /// The encodings of the constants used in the `DW_AT_ordering` attribute. /// /// See Section 7.17, Table 7.17. DwOrd(u8) { DW_ORD_row_major = 0x00, DW_ORD_col_major = 0x01, }); dw!( /// The encodings of the constants used in the `DW_AT_discr_list` attribute. /// /// See Section 7.18, Table 7.22. DwDsc(u8) { DW_DSC_label = 0x00, DW_DSC_range = 0x01, }); dw!( /// Name index attribute encodings. /// /// See Section 7.19, Table 7.23. DwIdx(u16) { DW_IDX_compile_unit = 1, DW_IDX_type_unit = 2, DW_IDX_die_offset = 3, DW_IDX_parent = 4, DW_IDX_type_hash = 5, DW_IDX_lo_user = 0x2000, DW_IDX_hi_user = 0x3fff, }); dw!( /// The encodings of the constants used in the `DW_AT_defaulted` attribute. /// /// See Section 7.20, Table 7.24. DwDefaulted(u8) { DW_DEFAULTED_no = 0x00, DW_DEFAULTED_in_class = 0x01, DW_DEFAULTED_out_of_class = 0x02, }); dw!( /// The encodings for the standard opcodes for line number information. /// /// See Section 7.22, Table 7.25. DwLns(u8) { DW_LNS_copy = 0x01, DW_LNS_advance_pc = 0x02, DW_LNS_advance_line = 0x03, DW_LNS_set_file = 0x04, DW_LNS_set_column = 0x05, DW_LNS_negate_stmt = 0x06, DW_LNS_set_basic_block = 0x07, DW_LNS_const_add_pc = 0x08, DW_LNS_fixed_advance_pc = 0x09, DW_LNS_set_prologue_end = 0x0a, DW_LNS_set_epilogue_begin = 0x0b, DW_LNS_set_isa = 0x0c, }); dw!( /// The encodings for the extended opcodes for line number information. /// /// See Section 7.22, Table 7.26. DwLne(u8) { DW_LNE_end_sequence = 0x01, DW_LNE_set_address = 0x02, DW_LNE_define_file = 0x03, DW_LNE_set_discriminator = 0x04, DW_LNE_lo_user = 0x80, DW_LNE_hi_user = 0xff, }); dw!( /// The encodings for the line number header entry formats. /// /// See Section 7.22, Table 7.27. DwLnct(u16) { DW_LNCT_path = 0x1, DW_LNCT_directory_index = 0x2, DW_LNCT_timestamp = 0x3, DW_LNCT_size = 0x4, DW_LNCT_MD5 = 0x5, // DW_LNCT_source = 0x6, DW_LNCT_lo_user = 0x2000, // We currently only implement the LLVM embedded source code extension for DWARF v5. DW_LNCT_LLVM_source = 0x2001, DW_LNCT_hi_user = 0x3fff, }); dw!( /// The encodings for macro information entry types. /// /// See Section 7.23, Table 7.28. DwMacro(u8) { DW_MACRO_define = 0x01, DW_MACRO_undef = 0x02, DW_MACRO_start_file = 0x03, DW_MACRO_end_file = 0x04, DW_MACRO_define_strp = 0x05, DW_MACRO_undef_strp = 0x06, DW_MACRO_import = 0x07, DW_MACRO_define_sup = 0x08, DW_MACRO_undef_sup = 0x09, DW_MACRO_import_sup = 0x0a, DW_MACRO_define_strx = 0x0b, DW_MACRO_undef_strx = 0x0c, DW_MACRO_lo_user = 0xe0, DW_MACRO_hi_user = 0xff, }); dw!( /// Range list entry encoding values. /// /// See Section 7.25, Table 7.30. DwRle(u8) { DW_RLE_end_of_list = 0x00, DW_RLE_base_addressx = 0x01, DW_RLE_startx_endx = 0x02, DW_RLE_startx_length = 0x03, DW_RLE_offset_pair = 0x04, DW_RLE_base_address = 0x05, DW_RLE_start_end = 0x06, DW_RLE_start_length = 0x07, }); dw!( /// The encodings for DWARF expression operations. /// /// See Section 7.7.1, Table 7.9. DwOp(u8) { DW_OP_addr = 0x03, DW_OP_deref = 0x06, DW_OP_const1u = 0x08, DW_OP_const1s = 0x09, DW_OP_const2u = 0x0a, DW_OP_const2s = 0x0b, DW_OP_const4u = 0x0c, DW_OP_const4s = 0x0d, DW_OP_const8u = 0x0e, DW_OP_const8s = 0x0f, DW_OP_constu = 0x10, DW_OP_consts = 0x11, DW_OP_dup = 0x12, DW_OP_drop = 0x13, DW_OP_over = 0x14, DW_OP_pick = 0x15, DW_OP_swap = 0x16, DW_OP_rot = 0x17, DW_OP_xderef = 0x18, DW_OP_abs = 0x19, DW_OP_and = 0x1a, DW_OP_div = 0x1b, DW_OP_minus = 0x1c, DW_OP_mod = 0x1d, DW_OP_mul = 0x1e, DW_OP_neg = 0x1f, DW_OP_not = 0x20, DW_OP_or = 0x21, DW_OP_plus = 0x22, DW_OP_plus_uconst = 0x23, DW_OP_shl = 0x24, DW_OP_shr = 0x25, DW_OP_shra = 0x26, DW_OP_xor = 0x27, DW_OP_bra = 0x28, DW_OP_eq = 0x29, DW_OP_ge = 0x2a, DW_OP_gt = 0x2b, DW_OP_le = 0x2c, DW_OP_lt = 0x2d, DW_OP_ne = 0x2e, DW_OP_skip = 0x2f, DW_OP_lit0 = 0x30, DW_OP_lit1 = 0x31, DW_OP_lit2 = 0x32, DW_OP_lit3 = 0x33, DW_OP_lit4 = 0x34, DW_OP_lit5 = 0x35, DW_OP_lit6 = 0x36, DW_OP_lit7 = 0x37, DW_OP_lit8 = 0x38, DW_OP_lit9 = 0x39, DW_OP_lit10 = 0x3a, DW_OP_lit11 = 0x3b, DW_OP_lit12 = 0x3c, DW_OP_lit13 = 0x3d, DW_OP_lit14 = 0x3e, DW_OP_lit15 = 0x3f, DW_OP_lit16 = 0x40, DW_OP_lit17 = 0x41, DW_OP_lit18 = 0x42, DW_OP_lit19 = 0x43, DW_OP_lit20 = 0x44, DW_OP_lit21 = 0x45, DW_OP_lit22 = 0x46, DW_OP_lit23 = 0x47, DW_OP_lit24 = 0x48, DW_OP_lit25 = 0x49, DW_OP_lit26 = 0x4a, DW_OP_lit27 = 0x4b, DW_OP_lit28 = 0x4c, DW_OP_lit29 = 0x4d, DW_OP_lit30 = 0x4e, DW_OP_lit31 = 0x4f, DW_OP_reg0 = 0x50, DW_OP_reg1 = 0x51, DW_OP_reg2 = 0x52, DW_OP_reg3 = 0x53, DW_OP_reg4 = 0x54, DW_OP_reg5 = 0x55, DW_OP_reg6 = 0x56, DW_OP_reg7 = 0x57, DW_OP_reg8 = 0x58, DW_OP_reg9 = 0x59, DW_OP_reg10 = 0x5a, DW_OP_reg11 = 0x5b, DW_OP_reg12 = 0x5c, DW_OP_reg13 = 0x5d, DW_OP_reg14 = 0x5e, DW_OP_reg15 = 0x5f, DW_OP_reg16 = 0x60, DW_OP_reg17 = 0x61, DW_OP_reg18 = 0x62, DW_OP_reg19 = 0x63, DW_OP_reg20 = 0x64, DW_OP_reg21 = 0x65, DW_OP_reg22 = 0x66, DW_OP_reg23 = 0x67, DW_OP_reg24 = 0x68, DW_OP_reg25 = 0x69, DW_OP_reg26 = 0x6a, DW_OP_reg27 = 0x6b, DW_OP_reg28 = 0x6c, DW_OP_reg29 = 0x6d, DW_OP_reg30 = 0x6e, DW_OP_reg31 = 0x6f, DW_OP_breg0 = 0x70, DW_OP_breg1 = 0x71, DW_OP_breg2 = 0x72, DW_OP_breg3 = 0x73, DW_OP_breg4 = 0x74, DW_OP_breg5 = 0x75, DW_OP_breg6 = 0x76, DW_OP_breg7 = 0x77, DW_OP_breg8 = 0x78, DW_OP_breg9 = 0x79, DW_OP_breg10 = 0x7a, DW_OP_breg11 = 0x7b, DW_OP_breg12 = 0x7c, DW_OP_breg13 = 0x7d, DW_OP_breg14 = 0x7e, DW_OP_breg15 = 0x7f, DW_OP_breg16 = 0x80, DW_OP_breg17 = 0x81, DW_OP_breg18 = 0x82, DW_OP_breg19 = 0x83, DW_OP_breg20 = 0x84, DW_OP_breg21 = 0x85, DW_OP_breg22 = 0x86, DW_OP_breg23 = 0x87, DW_OP_breg24 = 0x88, DW_OP_breg25 = 0x89, DW_OP_breg26 = 0x8a, DW_OP_breg27 = 0x8b, DW_OP_breg28 = 0x8c, DW_OP_breg29 = 0x8d, DW_OP_breg30 = 0x8e, DW_OP_breg31 = 0x8f, DW_OP_regx = 0x90, DW_OP_fbreg = 0x91, DW_OP_bregx = 0x92, DW_OP_piece = 0x93, DW_OP_deref_size = 0x94, DW_OP_xderef_size = 0x95, DW_OP_nop = 0x96, DW_OP_push_object_address = 0x97, DW_OP_call2 = 0x98, DW_OP_call4 = 0x99, DW_OP_call_ref = 0x9a, DW_OP_form_tls_address = 0x9b, DW_OP_call_frame_cfa = 0x9c, DW_OP_bit_piece = 0x9d, DW_OP_implicit_value = 0x9e, DW_OP_stack_value = 0x9f, DW_OP_implicit_pointer = 0xa0, DW_OP_addrx = 0xa1, DW_OP_constx = 0xa2, DW_OP_entry_value = 0xa3, DW_OP_const_type = 0xa4, DW_OP_regval_type = 0xa5, DW_OP_deref_type = 0xa6, DW_OP_xderef_type = 0xa7, DW_OP_convert = 0xa8, DW_OP_reinterpret = 0xa9, // GNU extensions DW_OP_GNU_push_tls_address = 0xe0, DW_OP_GNU_implicit_pointer = 0xf2, DW_OP_GNU_entry_value = 0xf3, DW_OP_GNU_const_type = 0xf4, DW_OP_GNU_regval_type = 0xf5, DW_OP_GNU_deref_type = 0xf6, DW_OP_GNU_convert = 0xf7, DW_OP_GNU_reinterpret = 0xf9, DW_OP_GNU_parameter_ref = 0xfa, DW_OP_GNU_addr_index = 0xfb, DW_OP_GNU_const_index = 0xfc, // Wasm extensions DW_OP_WASM_location = 0xed, }); dw!( /// Pointer encoding used by `.eh_frame`. /// /// The four lower bits describe the /// format of the pointer, the upper four bits describe how the encoding should /// be applied. /// /// Defined in `` DwEhPe(u8) { // Format of pointer encoding. // "Unsigned value is encoded using the Little Endian Base 128" DW_EH_PE_uleb128 = 0x1, // "A 2 bytes unsigned value." DW_EH_PE_udata2 = 0x2, // "A 4 bytes unsigned value." DW_EH_PE_udata4 = 0x3, // "An 8 bytes unsigned value." DW_EH_PE_udata8 = 0x4, // "Signed value is encoded using the Little Endian Base 128" DW_EH_PE_sleb128 = 0x9, // "A 2 bytes signed value." DW_EH_PE_sdata2 = 0x0a, // "A 4 bytes signed value." DW_EH_PE_sdata4 = 0x0b, // "An 8 bytes signed value." DW_EH_PE_sdata8 = 0x0c, // How the pointer encoding should be applied. // `DW_EH_PE_pcrel` pointers are relative to their own location. DW_EH_PE_pcrel = 0x10, // "Value is relative to the beginning of the .text section." DW_EH_PE_textrel = 0x20, // "Value is relative to the beginning of the .got or .eh_frame_hdr section." DW_EH_PE_datarel = 0x30, // "Value is relative to the beginning of the function." DW_EH_PE_funcrel = 0x40, // "Value is aligned to an address unit sized boundary." DW_EH_PE_aligned = 0x50, // This bit can be set for any of the above encoding applications. When set, // the encoded value is the address of the real pointer result, not the // pointer result itself. // // This isn't defined in the DWARF or the `.eh_frame` standards, but is // generated by both GNU/Linux and macOS tooling. DW_EH_PE_indirect = 0x80, // These constants apply to both the lower and upper bits. // "The Value is a literal pointer whose size is determined by the // architecture." DW_EH_PE_absptr = 0x0, // The absence of a pointer and encoding. DW_EH_PE_omit = 0xff, }); const DW_EH_PE_FORMAT_MASK: u8 = 0b0000_1111; // Ignores indirection bit. const DW_EH_PE_APPLICATION_MASK: u8 = 0b0111_0000; impl ops::BitOr for DwEhPe { type Output = DwEhPe; fn bitor(self, rhs: DwEhPe) -> DwEhPe { DwEhPe(self.0 | rhs.0) } } impl DwEhPe { /// Get the pointer encoding's format. #[inline] pub fn format(self) -> DwEhPe { DwEhPe(self.0 & DW_EH_PE_FORMAT_MASK) } /// Get the pointer encoding's application. #[inline] pub fn application(self) -> DwEhPe { DwEhPe(self.0 & DW_EH_PE_APPLICATION_MASK) } /// Is this encoding the absent pointer encoding? #[inline] pub fn is_absent(self) -> bool { self == DW_EH_PE_omit } /// Is this coding indirect? If so, its encoded value is the address of the /// real pointer result, not the pointer result itself. #[inline] pub fn is_indirect(self) -> bool { self.0 & DW_EH_PE_indirect.0 != 0 } /// Is this a known, valid pointer encoding? pub fn is_valid_encoding(self) -> bool { if self.is_absent() { return true; } match self.format() { DW_EH_PE_absptr | DW_EH_PE_uleb128 | DW_EH_PE_udata2 | DW_EH_PE_udata4 | DW_EH_PE_udata8 | DW_EH_PE_sleb128 | DW_EH_PE_sdata2 | DW_EH_PE_sdata4 | DW_EH_PE_sdata8 => {} _ => return false, } match self.application() { DW_EH_PE_absptr | DW_EH_PE_pcrel | DW_EH_PE_textrel | DW_EH_PE_datarel | DW_EH_PE_funcrel | DW_EH_PE_aligned => {} _ => return false, } true } } #[cfg(test)] mod tests { use super::*; #[test] fn test_dw_eh_pe_format() { let encoding = DW_EH_PE_pcrel | DW_EH_PE_uleb128; assert_eq!(encoding.format(), DW_EH_PE_uleb128); } #[test] fn test_dw_eh_pe_application() { let encoding = DW_EH_PE_pcrel | DW_EH_PE_uleb128; assert_eq!(encoding.application(), DW_EH_PE_pcrel); } #[test] fn test_dw_eh_pe_is_absent() { assert!(!DW_EH_PE_absptr.is_absent()); assert!(DW_EH_PE_omit.is_absent()); } #[test] fn test_dw_eh_pe_is_valid_encoding_ok() { let encoding = DW_EH_PE_uleb128 | DW_EH_PE_pcrel; assert!(encoding.is_valid_encoding()); assert!(DW_EH_PE_absptr.is_valid_encoding()); assert!(DW_EH_PE_omit.is_valid_encoding()); } #[test] fn test_dw_eh_pe_is_valid_encoding_bad_format() { let encoding = DwEhPe((DW_EH_PE_sdata8.0 + 1) | DW_EH_PE_pcrel.0); assert!(!encoding.is_valid_encoding()); } #[test] fn test_dw_eh_pe_is_valid_encoding_bad_application() { let encoding = DwEhPe(DW_EH_PE_sdata8.0 | (DW_EH_PE_aligned.0 + 1)); assert!(!encoding.is_valid_encoding()); } } gimli-0.31.1/src/endianity.rs000064400000000000000000000136041046102023000141340ustar 00000000000000//! Types for compile-time and run-time endianity. use core::convert::TryInto; use core::fmt::Debug; /// A trait describing the endianity of some buffer. pub trait Endianity: Debug + Default + Clone + Copy + PartialEq + Eq { /// Return true for big endian byte order. fn is_big_endian(self) -> bool; /// Return true for little endian byte order. #[inline] fn is_little_endian(self) -> bool { !self.is_big_endian() } /// Reads an unsigned 16 bit integer from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 2`. #[inline] fn read_u16(self, buf: &[u8]) -> u16 { let bytes: &[u8; 2] = buf[..2].try_into().unwrap(); if self.is_big_endian() { u16::from_be_bytes(*bytes) } else { u16::from_le_bytes(*bytes) } } /// Reads an unsigned 32 bit integer from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 4`. #[inline] fn read_u32(self, buf: &[u8]) -> u32 { let bytes: &[u8; 4] = buf[..4].try_into().unwrap(); if self.is_big_endian() { u32::from_be_bytes(*bytes) } else { u32::from_le_bytes(*bytes) } } /// Reads an unsigned 64 bit integer from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 8`. #[inline] fn read_u64(self, buf: &[u8]) -> u64 { let bytes: &[u8; 8] = buf[..8].try_into().unwrap(); if self.is_big_endian() { u64::from_be_bytes(*bytes) } else { u64::from_le_bytes(*bytes) } } /// Read an unsigned n-bytes integer u64. /// /// # Panics /// /// Panics when `buf.len() < 1` or `buf.len() > 8`. #[inline] fn read_uint(&mut self, buf: &[u8]) -> u64 { let mut tmp = [0; 8]; if self.is_big_endian() { tmp[8 - buf.len()..].copy_from_slice(buf); } else { tmp[..buf.len()].copy_from_slice(buf); } self.read_u64(&tmp) } /// Reads a signed 16 bit integer from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 2`. #[inline] fn read_i16(self, buf: &[u8]) -> i16 { self.read_u16(buf) as i16 } /// Reads a signed 32 bit integer from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 4`. #[inline] fn read_i32(self, buf: &[u8]) -> i32 { self.read_u32(buf) as i32 } /// Reads a signed 64 bit integer from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 8`. #[inline] fn read_i64(self, buf: &[u8]) -> i64 { self.read_u64(buf) as i64 } /// Reads a 32 bit floating point number from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 8`. #[inline] fn read_f32(self, buf: &[u8]) -> f32 { f32::from_bits(self.read_u32(buf)) } /// Reads a 32 bit floating point number from `buf`. /// /// # Panics /// /// Panics when `buf.len() < 8`. #[inline] fn read_f64(self, buf: &[u8]) -> f64 { f64::from_bits(self.read_u64(buf)) } /// Writes an unsigned 16 bit integer `n` to `buf`. /// /// # Panics /// /// Panics when `buf.len() < 2`. #[inline] fn write_u16(self, buf: &mut [u8], n: u16) { let bytes = if self.is_big_endian() { n.to_be_bytes() } else { n.to_le_bytes() }; buf[..2].copy_from_slice(&bytes); } /// Writes an unsigned 32 bit integer `n` to `buf`. /// /// # Panics /// /// Panics when `buf.len() < 4`. #[inline] fn write_u32(self, buf: &mut [u8], n: u32) { let bytes = if self.is_big_endian() { n.to_be_bytes() } else { n.to_le_bytes() }; buf[..4].copy_from_slice(&bytes); } /// Writes an unsigned 64 bit integer `n` to `buf`. /// /// # Panics /// /// Panics when `buf.len() < 8`. #[inline] fn write_u64(self, buf: &mut [u8], n: u64) { let bytes = if self.is_big_endian() { n.to_be_bytes() } else { n.to_le_bytes() }; buf[..8].copy_from_slice(&bytes); } } /// Byte order that is selectable at runtime. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum RunTimeEndian { /// Little endian byte order. Little, /// Big endian byte order. Big, } impl Default for RunTimeEndian { #[cfg(target_endian = "little")] #[inline] fn default() -> RunTimeEndian { RunTimeEndian::Little } #[cfg(target_endian = "big")] #[inline] fn default() -> RunTimeEndian { RunTimeEndian::Big } } impl Endianity for RunTimeEndian { #[inline] fn is_big_endian(self) -> bool { self != RunTimeEndian::Little } } /// Little endian byte order. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LittleEndian; impl Default for LittleEndian { #[inline] fn default() -> LittleEndian { LittleEndian } } impl Endianity for LittleEndian { #[inline] fn is_big_endian(self) -> bool { false } } /// Big endian byte order. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BigEndian; impl Default for BigEndian { #[inline] fn default() -> BigEndian { BigEndian } } impl Endianity for BigEndian { #[inline] fn is_big_endian(self) -> bool { true } } /// The native endianity for the target platform. #[cfg(target_endian = "little")] pub type NativeEndian = LittleEndian; #[cfg(target_endian = "little")] #[allow(non_upper_case_globals)] #[doc(hidden)] pub const NativeEndian: LittleEndian = LittleEndian; /// The native endianity for the target platform. #[cfg(target_endian = "big")] pub type NativeEndian = BigEndian; #[cfg(target_endian = "big")] #[allow(non_upper_case_globals)] #[doc(hidden)] pub const NativeEndian: BigEndian = BigEndian; gimli-0.31.1/src/leb128.rs000064400000000000000000000437251046102023000131540ustar 00000000000000//! Read and write DWARF's "Little Endian Base 128" (LEB128) variable length //! integer encoding. //! //! The implementation is a direct translation of the psuedocode in the DWARF 4 //! standard's appendix C. //! //! Read and write signed integers: //! //! ``` //! # #[cfg(all(feature = "read", feature = "write"))] { //! use gimli::{EndianSlice, NativeEndian, leb128}; //! //! let mut buf = [0; 1024]; //! //! // Write to anything that implements `std::io::Write`. //! { //! let mut writable = &mut buf[..]; //! leb128::write::signed(&mut writable, -12345).expect("Should write number"); //! } //! //! // Read from anything that implements `gimli::Reader`. //! let mut readable = EndianSlice::new(&buf[..], NativeEndian); //! let val = leb128::read::signed(&mut readable).expect("Should read number"); //! assert_eq!(val, -12345); //! # } //! ``` //! //! Or read and write unsigned integers: //! //! ``` //! # #[cfg(all(feature = "read", feature = "write"))] { //! use gimli::{EndianSlice, NativeEndian, leb128}; //! //! let mut buf = [0; 1024]; //! //! { //! let mut writable = &mut buf[..]; //! leb128::write::unsigned(&mut writable, 98765).expect("Should write number"); //! } //! //! let mut readable = EndianSlice::new(&buf[..], NativeEndian); //! let val = leb128::read::unsigned(&mut readable).expect("Should read number"); //! assert_eq!(val, 98765); //! # } //! ``` const CONTINUATION_BIT: u8 = 1 << 7; #[cfg(feature = "read-core")] const SIGN_BIT: u8 = 1 << 6; #[inline] fn low_bits_of_byte(byte: u8) -> u8 { byte & !CONTINUATION_BIT } #[inline] #[allow(dead_code)] fn low_bits_of_u64(val: u64) -> u8 { let byte = val & u64::from(u8::MAX); low_bits_of_byte(byte as u8) } /// A module for reading signed and unsigned integers that have been LEB128 /// encoded. #[cfg(feature = "read-core")] pub mod read { use super::{low_bits_of_byte, CONTINUATION_BIT, SIGN_BIT}; use crate::read::{Error, Reader, Result}; /// Read bytes until the LEB128 continuation bit is not set. pub fn skip(r: &mut R) -> Result<()> { loop { let byte = r.read_u8()?; if byte & CONTINUATION_BIT == 0 { return Ok(()); } } } /// Read an unsigned LEB128 number from the given `Reader` and /// return it or an error if reading failed. pub fn unsigned(r: &mut R) -> Result { let mut result = 0; let mut shift = 0; loop { let byte = r.read_u8()?; if shift == 63 && byte != 0x00 && byte != 0x01 { return Err(Error::BadUnsignedLeb128); } let low_bits = u64::from(low_bits_of_byte(byte)); result |= low_bits << shift; if byte & CONTINUATION_BIT == 0 { return Ok(result); } shift += 7; } } /// Read an LEB128 u16 from the given `Reader` and /// return it or an error if reading failed. pub fn u16(r: &mut R) -> Result { let byte = r.read_u8()?; let mut result = u16::from(low_bits_of_byte(byte)); if byte & CONTINUATION_BIT == 0 { return Ok(result); } let byte = r.read_u8()?; result |= u16::from(low_bits_of_byte(byte)) << 7; if byte & CONTINUATION_BIT == 0 { return Ok(result); } let byte = r.read_u8()?; if byte > 0x03 { return Err(Error::BadUnsignedLeb128); } result += u16::from(byte) << 14; Ok(result) } /// Read a signed LEB128 number from the given `Reader` and /// return it or an error if reading failed. pub fn signed(r: &mut R) -> Result { let mut result = 0; let mut shift = 0; let size = 64; let mut byte; loop { byte = r.read_u8()?; if shift == 63 && byte != 0x00 && byte != 0x7f { return Err(Error::BadSignedLeb128); } let low_bits = i64::from(low_bits_of_byte(byte)); result |= low_bits << shift; shift += 7; if byte & CONTINUATION_BIT == 0 { break; } } if shift < size && (SIGN_BIT & byte) == SIGN_BIT { // Sign extend the result. result |= !0 << shift; } Ok(result) } } /// A module for writing integers encoded as LEB128. #[cfg(feature = "write")] pub mod write { use super::{low_bits_of_u64, CONTINUATION_BIT}; use std::io; /// Write the given unsigned number using the LEB128 encoding to the given /// `std::io::Write`able. Returns the number of bytes written to `w`, or an /// error if writing failed. pub fn unsigned(w: &mut W, mut val: u64) -> Result where W: io::Write, { let mut bytes_written = 0; loop { let mut byte = low_bits_of_u64(val); val >>= 7; if val != 0 { // More bytes to come, so set the continuation bit. byte |= CONTINUATION_BIT; } let buf = [byte]; w.write_all(&buf)?; bytes_written += 1; if val == 0 { return Ok(bytes_written); } } } /// Return the size of the LEB128 encoding of the given unsigned number. pub fn uleb128_size(mut val: u64) -> usize { let mut size = 0; loop { val >>= 7; size += 1; if val == 0 { return size; } } } /// Write the given signed number using the LEB128 encoding to the given /// `std::io::Write`able. Returns the number of bytes written to `w`, or an /// error if writing failed. pub fn signed(w: &mut W, mut val: i64) -> Result where W: io::Write, { let mut bytes_written = 0; loop { let mut byte = val as u8; // Keep the sign bit for testing val >>= 6; let done = val == 0 || val == -1; if done { byte &= !CONTINUATION_BIT; } else { // Remove the sign bit val >>= 1; // More bytes to come, so set the continuation bit. byte |= CONTINUATION_BIT; } let buf = [byte]; w.write_all(&buf)?; bytes_written += 1; if done { return Ok(bytes_written); } } } /// Return the size of the LEB128 encoding of the given signed number. pub fn sleb128_size(mut val: i64) -> usize { let mut size = 0; loop { val >>= 6; let done = val == 0 || val == -1; val >>= 1; size += 1; if done { return size; } } } } #[cfg(test)] #[cfg(all(feature = "read", feature = "write"))] mod tests { use super::{low_bits_of_byte, low_bits_of_u64, read, write, CONTINUATION_BIT}; use crate::endianity::NativeEndian; use crate::read::{EndianSlice, Error, ReaderOffsetId}; trait ResultExt { fn map_eof(self, input: &[u8]) -> Self; } impl ResultExt for Result { fn map_eof(self, input: &[u8]) -> Self { match self { Err(Error::UnexpectedEof(id)) => { let id = ReaderOffsetId(id.0 - input.as_ptr() as u64); Err(Error::UnexpectedEof(id)) } r => r, } } } #[test] fn test_low_bits_of_byte() { for i in 0..127 { assert_eq!(i, low_bits_of_byte(i)); assert_eq!(i, low_bits_of_byte(i | CONTINUATION_BIT)); } } #[test] fn test_low_bits_of_u64() { for i in 0u64..127 { assert_eq!(i as u8, low_bits_of_u64(1 << 16 | i)); assert_eq!( i as u8, low_bits_of_u64(i << 16 | i | (u64::from(CONTINUATION_BIT))) ); } } // Examples from the DWARF 4 standard, section 7.6, figure 22. #[test] fn test_read_unsigned() { let buf = [2u8]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 2, read::unsigned(&mut readable).expect("Should read number") ); let buf = [127u8]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 127, read::unsigned(&mut readable).expect("Should read number") ); let buf = [CONTINUATION_BIT, 1]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 128, read::unsigned(&mut readable).expect("Should read number") ); let buf = [1u8 | CONTINUATION_BIT, 1]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 129, read::unsigned(&mut readable).expect("Should read number") ); let buf = [2u8 | CONTINUATION_BIT, 1]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 130, read::unsigned(&mut readable).expect("Should read number") ); let buf = [57u8 | CONTINUATION_BIT, 100]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 12857, read::unsigned(&mut readable).expect("Should read number") ); } // Examples from the DWARF 4 standard, section 7.6, figure 23. #[test] fn test_read_signed() { let buf = [2u8]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!(2, read::signed(&mut readable).expect("Should read number")); let buf = [0x7eu8]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!(-2, read::signed(&mut readable).expect("Should read number")); let buf = [127u8 | CONTINUATION_BIT, 0]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 127, read::signed(&mut readable).expect("Should read number") ); let buf = [1u8 | CONTINUATION_BIT, 0x7f]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( -127, read::signed(&mut readable).expect("Should read number") ); let buf = [CONTINUATION_BIT, 1]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 128, read::signed(&mut readable).expect("Should read number") ); let buf = [CONTINUATION_BIT, 0x7f]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( -128, read::signed(&mut readable).expect("Should read number") ); let buf = [1u8 | CONTINUATION_BIT, 1]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( 129, read::signed(&mut readable).expect("Should read number") ); let buf = [0x7fu8 | CONTINUATION_BIT, 0x7e]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( -129, read::signed(&mut readable).expect("Should read number") ); } #[test] fn test_read_signed_63_bits() { let buf = [ CONTINUATION_BIT, CONTINUATION_BIT, CONTINUATION_BIT, CONTINUATION_BIT, CONTINUATION_BIT, CONTINUATION_BIT, CONTINUATION_BIT, CONTINUATION_BIT, 0x40, ]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( -0x4000_0000_0000_0000, read::signed(&mut readable).expect("Should read number") ); } #[test] fn test_read_unsigned_not_enough_data() { let buf = [CONTINUATION_BIT]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( read::unsigned(&mut readable).map_eof(&buf), Err(Error::UnexpectedEof(ReaderOffsetId(1))) ); } #[test] fn test_read_signed_not_enough_data() { let buf = [CONTINUATION_BIT]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( read::signed(&mut readable).map_eof(&buf), Err(Error::UnexpectedEof(ReaderOffsetId(1))) ); } #[test] fn test_write_unsigned_not_enough_space() { let mut buf = [0; 1]; let mut writable = &mut buf[..]; match write::unsigned(&mut writable, 128) { Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::WriteZero), otherwise => panic!("Unexpected: {:?}", otherwise), } } #[test] fn test_write_signed_not_enough_space() { let mut buf = [0; 1]; let mut writable = &mut buf[..]; match write::signed(&mut writable, 128) { Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::WriteZero), otherwise => panic!("Unexpected: {:?}", otherwise), } } #[test] fn dogfood_signed() { fn inner(i: i64) { let mut buf = [0u8; 1024]; { let mut writable = &mut buf[..]; write::signed(&mut writable, i).expect("Should write signed number"); } let mut readable = EndianSlice::new(&buf[..], NativeEndian); let result = read::signed(&mut readable).expect("Should be able to read it back again"); assert_eq!(i, result); } for i in -513..513 { inner(i); } inner(i64::MIN); } #[test] fn dogfood_unsigned() { for i in 0..1025 { let mut buf = [0u8; 1024]; { let mut writable = &mut buf[..]; write::unsigned(&mut writable, i).expect("Should write signed number"); } let mut readable = EndianSlice::new(&buf[..], NativeEndian); let result = read::unsigned(&mut readable).expect("Should be able to read it back again"); assert_eq!(i, result); } } #[test] fn test_read_unsigned_overflow() { let buf = [ 2u8 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 1, ]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert!(read::unsigned(&mut readable).is_err()); } #[test] fn test_read_signed_overflow() { let buf = [ 2u8 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 1, ]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert!(read::signed(&mut readable).is_err()); } #[test] fn test_read_multiple() { let buf = [2u8 | CONTINUATION_BIT, 1u8, 1u8]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); assert_eq!( read::unsigned(&mut readable).expect("Should read first number"), 130u64 ); assert_eq!( read::unsigned(&mut readable).expect("Should read first number"), 1u64 ); } #[test] fn test_read_u16() { for (buf, val) in [ (&[2][..], 2), (&[0x7f][..], 0x7f), (&[0x80, 1][..], 0x80), (&[0x81, 1][..], 0x81), (&[0x82, 1][..], 0x82), (&[0xff, 0x7f][..], 0x3fff), (&[0x80, 0x80, 1][..], 0x4000), (&[0xff, 0xff, 1][..], 0x7fff), (&[0xff, 0xff, 3][..], 0xffff), ] .iter() { let mut readable = EndianSlice::new(buf, NativeEndian); assert_eq!(*val, read::u16(&mut readable).expect("Should read number")); } for buf in [ &[0x80][..], &[0x80, 0x80][..], &[0x80, 0x80, 4][..], &[0x80, 0x80, 0x80, 3][..], ] .iter() { let mut readable = EndianSlice::new(buf, NativeEndian); assert!(read::u16(&mut readable).is_err(), "{:?}", buf); } } } gimli-0.31.1/src/lib.rs000064400000000000000000000042001046102023000127060ustar 00000000000000//! `gimli` is a library for reading and writing the //! [DWARF debugging format](https://dwarfstd.org/). //! //! See the [read](./read/index.html) and [write](./write/index.html) modules //! for examples and API documentation. //! //! ## Cargo Features //! //! Cargo features that can be enabled with `gimli`: //! //! * `std`: Enabled by default. Use the `std` library. Disabling this feature //! allows using `gimli` in embedded environments that do not have access to //! `std`. Note that even when `std` is disabled, `gimli` still requires an //! implementation of the `alloc` crate. //! //! * `read`: Enabled by default. Enables the `read` module. Use of `std` is //! optional. //! //! * `write`: Enabled by default. Enables the `write` module. Always uses //! the `std` library. #![deny(missing_docs)] #![deny(missing_debug_implementations)] // Selectively enable rust 2018 warnings #![warn(bare_trait_objects)] #![warn(unused_extern_crates)] #![warn(ellipsis_inclusive_range_patterns)] #![warn(elided_lifetimes_in_paths)] #![warn(explicit_outlives_requirements)] // Style. #![allow(clippy::bool_to_int_with_if)] #![allow(clippy::collapsible_else_if)] #![allow(clippy::comparison_chain)] #![allow(clippy::manual_range_contains)] #![allow(clippy::needless_late_init)] #![allow(clippy::too_many_arguments)] // False positives with `fallible_iterator`. #![allow(clippy::should_implement_trait)] // False positives. #![allow(clippy::derive_partial_eq_without_eq)] #![no_std] #[allow(unused_imports)] #[cfg(any(feature = "read", feature = "write"))] #[macro_use] extern crate alloc; #[cfg(any(feature = "std", feature = "write"))] #[macro_use] extern crate std; #[cfg(feature = "endian-reader")] pub use stable_deref_trait::{CloneStableDeref, StableDeref}; mod common; pub use crate::common::*; mod arch; pub use crate::arch::*; pub mod constants; // For backwards compat. pub use crate::constants::*; mod endianity; pub use crate::endianity::*; pub mod leb128; #[cfg(feature = "read-core")] pub mod read; // For backwards compat. #[cfg(feature = "read-core")] pub use crate::read::*; #[cfg(feature = "write")] pub mod write; #[cfg(test)] mod test_util; gimli-0.31.1/src/read/abbrev.rs000064400000000000000000001065001046102023000143220ustar 00000000000000//! Functions for parsing DWARF debugging abbreviations. use alloc::collections::btree_map; use alloc::sync::Arc; use alloc::vec::Vec; use core::convert::TryFrom; use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::ops::Deref; use crate::common::{DebugAbbrevOffset, Encoding, SectionId}; use crate::constants; use crate::endianity::Endianity; use crate::read::{ DebugInfoUnitHeadersIter, EndianSlice, Error, Reader, ReaderOffset, Result, Section, UnitHeader, }; /// The `DebugAbbrev` struct represents the abbreviations describing /// `DebuggingInformationEntry`s' attribute names and forms found in the /// `.debug_abbrev` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugAbbrev { debug_abbrev_section: R, } impl<'input, Endian> DebugAbbrev> where Endian: Endianity, { /// Construct a new `DebugAbbrev` instance from the data in the `.debug_abbrev` /// section. /// /// It is the caller's responsibility to read the `.debug_abbrev` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugAbbrev, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_abbrev_section_somehow = || &buf; /// let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); /// ``` pub fn new(debug_abbrev_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_abbrev_section, endian)) } } impl DebugAbbrev { /// Parse the abbreviations at the given `offset` within this /// `.debug_abbrev` section. /// /// The `offset` should generally be retrieved from a unit header. pub fn abbreviations( &self, debug_abbrev_offset: DebugAbbrevOffset, ) -> Result { let input = &mut self.debug_abbrev_section.clone(); input.skip(debug_abbrev_offset.0)?; Abbreviations::parse(input) } } impl DebugAbbrev { /// Create a `DebugAbbrev` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAbbrev where F: FnMut(&'a T) -> R, { borrow(&self.debug_abbrev_section).into() } } impl Section for DebugAbbrev { fn id() -> SectionId { SectionId::DebugAbbrev } fn reader(&self) -> &R { &self.debug_abbrev_section } } impl From for DebugAbbrev { fn from(debug_abbrev_section: R) -> Self { DebugAbbrev { debug_abbrev_section, } } } /// The strategy to use for caching abbreviations. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum AbbreviationsCacheStrategy { /// Cache abbreviations that are used more than once. /// /// This is useful if the units in the `.debug_info` section will be parsed only once. Duplicates, /// Cache all abbreviations. /// /// This is useful if the units in the `.debug_info` section will be parsed more than once. All, } /// A cache of previously parsed `Abbreviations`. #[derive(Debug, Default)] pub struct AbbreviationsCache { abbreviations: btree_map::BTreeMap>>, } impl AbbreviationsCache { /// Create an empty abbreviations cache. pub fn new() -> Self { Self::default() } /// Parse abbreviations and store them in the cache. /// /// This will iterate over the given units to determine the abbreviations /// offsets. Any existing cache entries are discarded. /// /// Errors during parsing abbreviations are also stored in the cache. /// Errors during iterating over the units are ignored. pub fn populate( &mut self, strategy: AbbreviationsCacheStrategy, debug_abbrev: &DebugAbbrev, mut units: DebugInfoUnitHeadersIter, ) { let mut offsets = Vec::new(); match strategy { AbbreviationsCacheStrategy::Duplicates => { while let Ok(Some(unit)) = units.next() { offsets.push(unit.debug_abbrev_offset()); } offsets.sort_unstable_by_key(|offset| offset.0); let mut prev_offset = R::Offset::from_u8(0); let mut count = 0; offsets.retain(|offset| { if count == 0 || prev_offset != offset.0 { prev_offset = offset.0; count = 1; } else { count += 1; } count == 2 }); } AbbreviationsCacheStrategy::All => { while let Ok(Some(unit)) = units.next() { offsets.push(unit.debug_abbrev_offset()); } offsets.sort_unstable_by_key(|offset| offset.0); offsets.dedup(); } } self.abbreviations = offsets .into_iter() .map(|offset| { ( offset.0.into_u64(), debug_abbrev.abbreviations(offset).map(Arc::new), ) }) .collect(); } /// Set an entry in the abbreviations cache. /// /// This is only required if you want to manually populate the cache. pub fn set( &mut self, offset: DebugAbbrevOffset, abbreviations: Arc, ) { self.abbreviations .insert(offset.0.into_u64(), Ok(abbreviations)); } /// Parse the abbreviations at the given offset. /// /// This uses the cache if possible, but does not update it. pub fn get( &self, debug_abbrev: &DebugAbbrev, offset: DebugAbbrevOffset, ) -> Result> { match self.abbreviations.get(&offset.0.into_u64()) { Some(entry) => entry.clone(), None => debug_abbrev.abbreviations(offset).map(Arc::new), } } } /// A set of type abbreviations. /// /// Construct an `Abbreviations` instance with the /// [`abbreviations()`](struct.UnitHeader.html#method.abbreviations) /// method. #[derive(Debug, Default, Clone)] pub struct Abbreviations { vec: Vec, map: btree_map::BTreeMap, } impl Abbreviations { /// Construct a new, empty set of abbreviations. fn empty() -> Abbreviations { Abbreviations { vec: Vec::new(), map: btree_map::BTreeMap::new(), } } /// Insert an abbreviation into the set. /// /// Returns `Ok` if it is the first abbreviation in the set with its code, /// `Err` if the code is a duplicate and there already exists an /// abbreviation in the set with the given abbreviation's code. fn insert(&mut self, abbrev: Abbreviation) -> ::core::result::Result<(), ()> { let code_usize = abbrev.code as usize; if code_usize as u64 == abbrev.code { // Optimize for sequential abbreviation codes by storing them // in a Vec, as long as the map doesn't already contain them. // A potential further optimization would be to allow some // holes in the Vec, but there's no need for that yet. if code_usize - 1 < self.vec.len() { return Err(()); } else if code_usize - 1 == self.vec.len() { if !self.map.is_empty() && self.map.contains_key(&abbrev.code) { return Err(()); } else { self.vec.push(abbrev); return Ok(()); } } } match self.map.entry(abbrev.code) { btree_map::Entry::Occupied(_) => Err(()), btree_map::Entry::Vacant(entry) => { entry.insert(abbrev); Ok(()) } } } /// Get the abbreviation associated with the given code. #[inline] pub fn get(&self, code: u64) -> Option<&Abbreviation> { if let Ok(code) = usize::try_from(code) { let index = code.checked_sub(1)?; if index < self.vec.len() { return Some(&self.vec[index]); } } self.map.get(&code) } /// Parse a series of abbreviations, terminated by a null abbreviation. fn parse(input: &mut R) -> Result { let mut abbrevs = Abbreviations::empty(); while let Some(abbrev) = Abbreviation::parse(input)? { if abbrevs.insert(abbrev).is_err() { return Err(Error::DuplicateAbbreviationCode); } } Ok(abbrevs) } } /// An abbreviation describes the shape of a `DebuggingInformationEntry`'s type: /// its code, tag type, whether it has children, and its set of attributes. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Abbreviation { code: u64, tag: constants::DwTag, has_children: constants::DwChildren, attributes: Attributes, } impl Abbreviation { /// Construct a new `Abbreviation`. /// /// ### Panics /// /// Panics if `code` is `0`. pub(crate) fn new( code: u64, tag: constants::DwTag, has_children: constants::DwChildren, attributes: Attributes, ) -> Abbreviation { assert_ne!(code, 0); Abbreviation { code, tag, has_children, attributes, } } /// Get this abbreviation's code. #[inline] pub fn code(&self) -> u64 { self.code } /// Get this abbreviation's tag. #[inline] pub fn tag(&self) -> constants::DwTag { self.tag } /// Return true if this abbreviation's type has children, false otherwise. #[inline] pub fn has_children(&self) -> bool { self.has_children == constants::DW_CHILDREN_yes } /// Get this abbreviation's attributes. #[inline] pub fn attributes(&self) -> &[AttributeSpecification] { &self.attributes[..] } /// Parse an abbreviation's tag. fn parse_tag(input: &mut R) -> Result { let val = input.read_uleb128_u16()?; if val == 0 { Err(Error::AbbreviationTagZero) } else { Ok(constants::DwTag(val)) } } /// Parse an abbreviation's "does the type have children?" byte. fn parse_has_children(input: &mut R) -> Result { let val = input.read_u8()?; let val = constants::DwChildren(val); if val == constants::DW_CHILDREN_no || val == constants::DW_CHILDREN_yes { Ok(val) } else { Err(Error::BadHasChildren) } } /// Parse a series of attribute specifications, terminated by a null attribute /// specification. fn parse_attributes(input: &mut R) -> Result { let mut attrs = Attributes::new(); while let Some(attr) = AttributeSpecification::parse(input)? { attrs.push(attr); } Ok(attrs) } /// Parse an abbreviation. Return `None` for the null abbreviation, `Some` /// for an actual abbreviation. fn parse(input: &mut R) -> Result> { let code = input.read_uleb128()?; if code == 0 { return Ok(None); } let tag = Self::parse_tag(input)?; let has_children = Self::parse_has_children(input)?; let attributes = Self::parse_attributes(input)?; let abbrev = Abbreviation::new(code, tag, has_children, attributes); Ok(Some(abbrev)) } } /// A list of attributes found in an `Abbreviation` #[derive(Clone)] pub(crate) enum Attributes { Inline { buf: [AttributeSpecification; MAX_ATTRIBUTES_INLINE], len: usize, }, Heap(Vec), } // Length of 5 based on benchmark results for both x86-64 and i686. const MAX_ATTRIBUTES_INLINE: usize = 5; impl Attributes { /// Returns a new empty list of attributes fn new() -> Attributes { let default = AttributeSpecification::new(constants::DW_AT_null, constants::DW_FORM_null, None); Attributes::Inline { buf: [default; 5], len: 0, } } /// Pushes a new value onto this list of attributes. fn push(&mut self, attr: AttributeSpecification) { match self { Attributes::Heap(list) => list.push(attr), Attributes::Inline { buf, len: MAX_ATTRIBUTES_INLINE, } => { let mut list = buf.to_vec(); list.push(attr); *self = Attributes::Heap(list); } Attributes::Inline { buf, len } => { buf[*len] = attr; *len += 1; } } } } impl Debug for Attributes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl PartialEq for Attributes { fn eq(&self, other: &Attributes) -> bool { **self == **other } } impl Eq for Attributes {} impl Deref for Attributes { type Target = [AttributeSpecification]; fn deref(&self) -> &[AttributeSpecification] { match self { Attributes::Inline { buf, len } => &buf[..*len], Attributes::Heap(list) => list, } } } impl FromIterator for Attributes { fn from_iter(iter: I) -> Attributes where I: IntoIterator, { let mut list = Attributes::new(); for item in iter { list.push(item); } list } } impl From> for Attributes { fn from(list: Vec) -> Attributes { Attributes::Heap(list) } } /// The description of an attribute in an abbreviated type. It is a pair of name /// and form. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AttributeSpecification { name: constants::DwAt, form: constants::DwForm, implicit_const_value: i64, } impl AttributeSpecification { /// Construct a new `AttributeSpecification` from the given name and form /// and implicit const value. #[inline] pub fn new( name: constants::DwAt, form: constants::DwForm, implicit_const_value: Option, ) -> AttributeSpecification { debug_assert!( (form == constants::DW_FORM_implicit_const && implicit_const_value.is_some()) || (form != constants::DW_FORM_implicit_const && implicit_const_value.is_none()) ); AttributeSpecification { name, form, implicit_const_value: implicit_const_value.unwrap_or(0), } } /// Get the attribute's name. #[inline] pub fn name(&self) -> constants::DwAt { self.name } /// Get the attribute's form. #[inline] pub fn form(&self) -> constants::DwForm { self.form } /// Get the attribute's implicit const value. #[inline] pub fn implicit_const_value(&self) -> Option { if self.form == constants::DW_FORM_implicit_const { Some(self.implicit_const_value) } else { None } } /// Return the size of the attribute, in bytes. /// /// Note that because some attributes are variably sized, the size cannot /// always be known without parsing, in which case we return `None`. pub fn size(&self, header: &UnitHeader) -> Option { get_attribute_size(self.form, header.encoding()).map(usize::from) } /// Parse an attribute's form. fn parse_form(input: &mut R) -> Result { let val = input.read_uleb128_u16()?; if val == 0 { Err(Error::AttributeFormZero) } else { Ok(constants::DwForm(val)) } } /// Parse an attribute specification. Returns `None` for the null attribute /// specification, `Some` for an actual attribute specification. fn parse(input: &mut R) -> Result> { let name = input.read_uleb128_u16()?; if name == 0 { // Parse the null attribute specification. let form = input.read_uleb128_u16()?; return if form == 0 { Ok(None) } else { Err(Error::ExpectedZero) }; } let name = constants::DwAt(name); let form = Self::parse_form(input)?; let implicit_const_value = if form == constants::DW_FORM_implicit_const { Some(input.read_sleb128()?) } else { None }; let spec = AttributeSpecification::new(name, form, implicit_const_value); Ok(Some(spec)) } } #[inline] pub(crate) fn get_attribute_size(form: constants::DwForm, encoding: Encoding) -> Option { match form { constants::DW_FORM_addr => Some(encoding.address_size), constants::DW_FORM_implicit_const | constants::DW_FORM_flag_present => Some(0), constants::DW_FORM_data1 | constants::DW_FORM_flag | constants::DW_FORM_strx1 | constants::DW_FORM_ref1 | constants::DW_FORM_addrx1 => Some(1), constants::DW_FORM_data2 | constants::DW_FORM_ref2 | constants::DW_FORM_addrx2 | constants::DW_FORM_strx2 => Some(2), constants::DW_FORM_addrx3 | constants::DW_FORM_strx3 => Some(3), constants::DW_FORM_data4 | constants::DW_FORM_ref_sup4 | constants::DW_FORM_ref4 | constants::DW_FORM_strx4 | constants::DW_FORM_addrx4 => Some(4), constants::DW_FORM_data8 | constants::DW_FORM_ref8 | constants::DW_FORM_ref_sig8 | constants::DW_FORM_ref_sup8 => Some(8), constants::DW_FORM_data16 => Some(16), constants::DW_FORM_sec_offset | constants::DW_FORM_GNU_ref_alt | constants::DW_FORM_strp | constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt | constants::DW_FORM_line_strp => Some(encoding.format.word_size()), constants::DW_FORM_ref_addr => { // This is an offset, but DWARF version 2 specifies that DW_FORM_ref_addr // has the same size as an address on the target system. This was changed // in DWARF version 3. Some(if encoding.version == 2 { encoding.address_size } else { encoding.format.word_size() }) } // Variably sized forms. constants::DW_FORM_block | constants::DW_FORM_block1 | constants::DW_FORM_block2 | constants::DW_FORM_block4 | constants::DW_FORM_exprloc | constants::DW_FORM_ref_udata | constants::DW_FORM_string | constants::DW_FORM_sdata | constants::DW_FORM_udata | constants::DW_FORM_indirect => None, // We don't know the size of unknown forms. _ => None, } } #[cfg(test)] pub(crate) mod tests { use super::*; use crate::constants; use crate::endianity::LittleEndian; use crate::read::{EndianSlice, Error}; use crate::test_util::GimliSectionMethods; #[cfg(target_pointer_width = "32")] use core::u32; use test_assembler::Section; pub trait AbbrevSectionMethods { fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self; fn abbrev_null(self) -> Self; fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self; fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self; fn abbrev_attr_null(self) -> Self; } impl AbbrevSectionMethods for Section { fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self { self.uleb(code).uleb(tag.0.into()).D8(children.0) } fn abbrev_null(self) -> Self { self.D8(0) } fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self { self.uleb(name.0.into()).uleb(form.0.into()) } fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self { self.uleb(name.0.into()) .uleb(constants::DW_FORM_implicit_const.0.into()) .sleb(value) } fn abbrev_attr_null(self) -> Self { self.D8(0).D8(0) } } #[test] fn test_debug_abbrev_ok() { let extra_start = [1, 2, 3, 4]; let expected_rest = [5, 6, 7, 8]; #[rustfmt::skip] let buf = Section::new() .append_bytes(&extra_start) .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) .abbrev_attr_null() .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) .abbrev_attr_null() .abbrev_null() .append_bytes(&expected_rest) .get_contents() .unwrap(); let abbrev1 = Abbreviation::new( 1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes, vec![ AttributeSpecification::new( constants::DW_AT_producer, constants::DW_FORM_strp, None, ), AttributeSpecification::new( constants::DW_AT_language, constants::DW_FORM_data2, None, ), ] .into(), ); let abbrev2 = Abbreviation::new( 2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no, vec![AttributeSpecification::new( constants::DW_AT_name, constants::DW_FORM_string, None, )] .into(), ); let debug_abbrev = DebugAbbrev::new(&buf, LittleEndian); let debug_abbrev_offset = DebugAbbrevOffset(extra_start.len()); let abbrevs = debug_abbrev .abbreviations(debug_abbrev_offset) .expect("Should parse abbreviations"); assert_eq!(abbrevs.get(1), Some(&abbrev1)); assert_eq!(abbrevs.get(2), Some(&abbrev2)); } #[test] fn test_abbreviations_insert() { fn abbrev(code: u16) -> Abbreviation { Abbreviation::new( code.into(), constants::DwTag(code), constants::DW_CHILDREN_no, vec![].into(), ) } fn assert_abbrev(abbrevs: &Abbreviations, code: u16) { let abbrev = abbrevs.get(code.into()).unwrap(); assert_eq!(abbrev.tag(), constants::DwTag(code)); } // Sequential insert. let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(1)).unwrap(); abbrevs.insert(abbrev(2)).unwrap(); assert_eq!(abbrevs.vec.len(), 2); assert!(abbrevs.map.is_empty()); assert_abbrev(&abbrevs, 1); assert_abbrev(&abbrevs, 2); // Out of order insert. let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(2)).unwrap(); abbrevs.insert(abbrev(3)).unwrap(); assert!(abbrevs.vec.is_empty()); assert_abbrev(&abbrevs, 2); assert_abbrev(&abbrevs, 3); // Mixed order insert. let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(1)).unwrap(); abbrevs.insert(abbrev(3)).unwrap(); abbrevs.insert(abbrev(2)).unwrap(); assert_eq!(abbrevs.vec.len(), 2); assert_abbrev(&abbrevs, 1); assert_abbrev(&abbrevs, 2); assert_abbrev(&abbrevs, 3); // Duplicate code in vec. let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(1)).unwrap(); abbrevs.insert(abbrev(2)).unwrap(); assert_eq!(abbrevs.insert(abbrev(1)), Err(())); assert_eq!(abbrevs.insert(abbrev(2)), Err(())); // Duplicate code in map when adding to map. let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(2)).unwrap(); assert_eq!(abbrevs.insert(abbrev(2)), Err(())); // Duplicate code in map when adding to vec. let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(2)).unwrap(); abbrevs.insert(abbrev(1)).unwrap(); assert_eq!(abbrevs.insert(abbrev(2)), Err(())); // 32-bit usize conversions. let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(2)).unwrap(); } #[test] #[cfg(target_pointer_width = "32")] fn test_abbreviations_insert_32() { fn abbrev(code: u64) -> Abbreviation { Abbreviation::new( code, constants::DwTag(code as u16), constants::DW_CHILDREN_no, vec![].into(), ) } fn assert_abbrev(abbrevs: &Abbreviations, code: u64) { let abbrev = abbrevs.get(code).unwrap(); assert_eq!(abbrev.tag(), constants::DwTag(code as u16)); } let mut abbrevs = Abbreviations::empty(); abbrevs.insert(abbrev(1)).unwrap(); let wrap_code = (u32::MAX as u64 + 1) + 1; // `get` should not treat the wrapped code as `1`. assert_eq!(abbrevs.get(wrap_code), None); // `insert` should not treat the wrapped code as `1`. abbrevs.insert(abbrev(wrap_code)).unwrap(); assert_abbrev(&abbrevs, 1); assert_abbrev(&abbrevs, wrap_code); } #[test] fn test_parse_abbreviations_ok() { let expected_rest = [1, 2, 3, 4]; #[rustfmt::skip] let buf = Section::new() .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) .abbrev_attr_null() .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) .abbrev_attr_null() .abbrev_null() .append_bytes(&expected_rest) .get_contents() .unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); let abbrev1 = Abbreviation::new( 1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes, vec![ AttributeSpecification::new( constants::DW_AT_producer, constants::DW_FORM_strp, None, ), AttributeSpecification::new( constants::DW_AT_language, constants::DW_FORM_data2, None, ), ] .into(), ); let abbrev2 = Abbreviation::new( 2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no, vec![AttributeSpecification::new( constants::DW_AT_name, constants::DW_FORM_string, None, )] .into(), ); let abbrevs = Abbreviations::parse(rest).expect("Should parse abbreviations"); assert_eq!(abbrevs.get(1), Some(&abbrev1)); assert_eq!(abbrevs.get(2), Some(&abbrev2)); assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_abbreviations_duplicate() { let expected_rest = [1, 2, 3, 4]; #[rustfmt::skip] let buf = Section::new() .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) .abbrev_attr_null() .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) .abbrev_attr_null() .abbrev_null() .append_bytes(&expected_rest) .get_contents() .unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match Abbreviations::parse(buf) { Err(Error::DuplicateAbbreviationCode) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_abbreviation_tag_ok() { let buf = [0x01, 0x02]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let tag = Abbreviation::parse_tag(rest).expect("Should parse tag"); assert_eq!(tag, constants::DW_TAG_array_type); assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian)); } #[test] fn test_parse_abbreviation_tag_zero() { let buf = [0x00]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match Abbreviation::parse_tag(buf) { Err(Error::AbbreviationTagZero) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_abbreviation_has_children() { let buf = [0x00, 0x01, 0x02]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let val = Abbreviation::parse_has_children(rest).expect("Should parse children"); assert_eq!(val, constants::DW_CHILDREN_no); let val = Abbreviation::parse_has_children(rest).expect("Should parse children"); assert_eq!(val, constants::DW_CHILDREN_yes); match Abbreviation::parse_has_children(rest) { Err(Error::BadHasChildren) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_abbreviation_ok() { let expected_rest = [0x01, 0x02, 0x03, 0x04]; let buf = Section::new() .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) .abbrev_attr_null() .append_bytes(&expected_rest) .get_contents() .unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); let expect = Some(Abbreviation::new( 1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no, vec![AttributeSpecification::new( constants::DW_AT_name, constants::DW_FORM_string, None, )] .into(), )); let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation"); assert_eq!(abbrev, expect); assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_abbreviation_implicit_const_ok() { let expected_rest = [0x01, 0x02, 0x03, 0x04]; let buf = Section::new() .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) .abbrev_attr_implicit_const(constants::DW_AT_name, -42) .abbrev_attr_null() .append_bytes(&expected_rest) .get_contents() .unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); let expect = Some(Abbreviation::new( 1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no, vec![AttributeSpecification::new( constants::DW_AT_name, constants::DW_FORM_implicit_const, Some(-42), )] .into(), )); let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation"); assert_eq!(abbrev, expect); assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_abbreviation_implicit_const_no_const() { let buf = Section::new() .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_implicit_const) .get_contents() .unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match Abbreviation::parse(buf) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] fn test_parse_null_abbreviation_ok() { let expected_rest = [0x01, 0x02, 0x03, 0x04]; let buf = Section::new() .abbrev_null() .append_bytes(&expected_rest) .get_contents() .unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); let abbrev = Abbreviation::parse(rest).expect("Should parse null abbreviation"); assert!(abbrev.is_none()); assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_attribute_form_ok() { let buf = [0x01, 0x02]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let tag = AttributeSpecification::parse_form(rest).expect("Should parse form"); assert_eq!(tag, constants::DW_FORM_addr); assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian)); } #[test] fn test_parse_attribute_form_zero() { let buf = [0x00]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match AttributeSpecification::parse_form(buf) { Err(Error::AttributeFormZero) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_null_attribute_specification_ok() { let buf = [0x00, 0x00, 0x01]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let attr = AttributeSpecification::parse(rest).expect("Should parse null attribute specification"); assert!(attr.is_none()); assert_eq!(*rest, EndianSlice::new(&buf[2..], LittleEndian)); } #[test] fn test_parse_attribute_specifications_name_zero() { let buf = [0x00, 0x01, 0x00, 0x00]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match AttributeSpecification::parse(buf) { Err(Error::ExpectedZero) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_attribute_specifications_form_zero() { let buf = [0x01, 0x00, 0x00, 0x00]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match AttributeSpecification::parse(buf) { Err(Error::AttributeFormZero) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_get_abbrev_zero() { let mut abbrevs = Abbreviations::empty(); abbrevs .insert(Abbreviation::new( 1, constants::DwTag(1), constants::DW_CHILDREN_no, vec![].into(), )) .unwrap(); assert!(abbrevs.get(0).is_none()); } } gimli-0.31.1/src/read/addr.rs000064400000000000000000000074641046102023000140040ustar 00000000000000use crate::common::{DebugAddrBase, DebugAddrIndex, SectionId}; use crate::read::{Reader, ReaderOffset, Result, Section}; /// The raw contents of the `.debug_addr` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugAddr { section: R, } impl DebugAddr { // TODO: add an iterator over the sets of addresses in the section. // This is not needed for common usage of the section though. /// Returns the address at the given `base` and `index`. /// /// A set of addresses in the `.debug_addr` section consists of a header /// followed by a series of addresses. /// /// The `base` must be the `DW_AT_addr_base` value from the compilation unit DIE. /// This is an offset that points to the first address following the header. /// /// The `index` is the value of a `DW_FORM_addrx` attribute. /// /// The `address_size` must be the size of the address for the compilation unit. /// This value must also match the header. However, note that we do not parse the /// header to validate this, since locating the header is unreliable, and the GNU /// extensions do not emit it. pub fn get_address( &self, address_size: u8, base: DebugAddrBase, index: DebugAddrIndex, ) -> Result { let input = &mut self.section.clone(); input.skip(base.0)?; input.skip(R::Offset::from_u64( index.0.into_u64() * u64::from(address_size), )?)?; input.read_address(address_size) } } impl DebugAddr { /// Create a `DebugAddr` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAddr where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugAddr { fn id() -> SectionId { SectionId::DebugAddr } fn reader(&self) -> &R { &self.section } } impl From for DebugAddr { fn from(section: R) -> Self { DebugAddr { section } } } #[cfg(test)] mod tests { use super::*; use crate::read::EndianSlice; use crate::test_util::GimliSectionMethods; use crate::{Format, LittleEndian}; use test_assembler::{Endian, Label, LabelMaker, Section}; #[test] fn test_get_address() { for format in [Format::Dwarf32, Format::Dwarf64] { for address_size in [4, 8] { let zero = Label::new(); let length = Label::new(); let start = Label::new(); let first = Label::new(); let end = Label::new(); let mut section = Section::with_endian(Endian::Little) .mark(&zero) .initial_length(format, &length, &start) .D16(5) .D8(address_size) .D8(0) .mark(&first); for i in 0..20 { section = section.word(address_size, 1000 + i); } section = section.mark(&end); length.set_const((&end - &start) as u64); let section = section.get_contents().unwrap(); let debug_addr = DebugAddr::from(EndianSlice::new(§ion, LittleEndian)); let base = DebugAddrBase((&first - &zero) as usize); assert_eq!( debug_addr.get_address(address_size, base, DebugAddrIndex(0)), Ok(1000) ); assert_eq!( debug_addr.get_address(address_size, base, DebugAddrIndex(19)), Ok(1019) ); } } } } gimli-0.31.1/src/read/aranges.rs000064400000000000000000000576161046102023000145160ustar 00000000000000use crate::common::{DebugArangesOffset, DebugInfoOffset, Encoding, SectionId}; use crate::endianity::Endianity; use crate::read::{ EndianSlice, Error, Range, Reader, ReaderAddress, ReaderOffset, Result, Section, }; /// The `DebugAranges` struct represents the DWARF address range information /// found in the `.debug_aranges` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugAranges { section: R, } impl<'input, Endian> DebugAranges> where Endian: Endianity, { /// Construct a new `DebugAranges` instance from the data in the `.debug_aranges` /// section. /// /// It is the caller's responsibility to read the `.debug_aranges` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugAranges, LittleEndian}; /// /// # let buf = []; /// # let read_debug_aranges_section = || &buf; /// let debug_aranges = /// DebugAranges::new(read_debug_aranges_section(), LittleEndian); /// ``` pub fn new(section: &'input [u8], endian: Endian) -> Self { DebugAranges { section: EndianSlice::new(section, endian), } } } impl DebugAranges { /// Iterate the sets of entries in the `.debug_aranges` section. /// /// Each set of entries belongs to a single unit. pub fn headers(&self) -> ArangeHeaderIter { ArangeHeaderIter { input: self.section.clone(), offset: DebugArangesOffset(R::Offset::from_u8(0)), } } /// Get the header at the given offset. pub fn header(&self, offset: DebugArangesOffset) -> Result> { let mut input = self.section.clone(); input.skip(offset.0)?; ArangeHeader::parse(&mut input, offset) } } impl DebugAranges { /// Create a `DebugAranges` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAranges where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugAranges { fn id() -> SectionId { SectionId::DebugAranges } fn reader(&self) -> &R { &self.section } } impl From for DebugAranges { fn from(section: R) -> Self { DebugAranges { section } } } /// An iterator over the headers of a `.debug_aranges` section. #[derive(Clone, Debug)] pub struct ArangeHeaderIter { input: R, offset: DebugArangesOffset, } impl ArangeHeaderIter { /// Advance the iterator to the next header. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { return Ok(None); } let len = self.input.len(); match ArangeHeader::parse(&mut self.input, self.offset) { Ok(header) => { self.offset.0 += len - self.input.len(); Ok(Some(header)) } Err(e) => { self.input.empty(); Err(e) } } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for ArangeHeaderIter { type Item = ArangeHeader; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { ArangeHeaderIter::next(self) } } /// A header for a set of entries in the `.debug_arange` section. /// /// These entries all belong to a single unit. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ArangeHeader::Offset> where R: Reader, Offset: ReaderOffset, { offset: DebugArangesOffset, encoding: Encoding, length: Offset, debug_info_offset: DebugInfoOffset, entries: R, } impl ArangeHeader where R: Reader, Offset: ReaderOffset, { fn parse(input: &mut R, offset: DebugArangesOffset) -> Result { let (length, format) = input.read_initial_length()?; let mut rest = input.split(length)?; // Check the version. The DWARF 5 spec says that this is always 2, but version 3 // has been observed in the wild, potentially due to a bug; see // https://github.com/gimli-rs/gimli/issues/559 for more information. // lldb allows versions 2 through 5, possibly by mistake. let version = rest.read_u16()?; if version != 2 && version != 3 { return Err(Error::UnknownVersion(u64::from(version))); } let debug_info_offset = rest.read_offset(format).map(DebugInfoOffset)?; let address_size = rest.read_address_size()?; let segment_size = rest.read_u8()?; if segment_size != 0 { return Err(Error::UnsupportedSegmentSize); } // unit_length + version + offset + address_size + segment_size let header_length = format.initial_length_size() + 2 + format.word_size() + 1 + 1; // The first tuple following the header in each set begins at an offset that is // a multiple of the size of a single tuple (that is, twice the size of an address). let tuple_length = address_size .checked_mul(2) .ok_or(Error::UnsupportedAddressSize(address_size))?; if tuple_length == 0 { return Err(Error::UnsupportedAddressSize(address_size)); } let padding = if header_length % tuple_length == 0 { 0 } else { tuple_length - header_length % tuple_length }; rest.skip(R::Offset::from_u8(padding))?; let encoding = Encoding { format, version, address_size, }; Ok(ArangeHeader { offset, encoding, length, debug_info_offset, entries: rest, }) } /// Return the offset of this header within the `.debug_aranges` section. #[inline] pub fn offset(&self) -> DebugArangesOffset { self.offset } /// Return the length of this set of entries, including the header. #[inline] pub fn length(&self) -> Offset { self.length } /// Return the encoding parameters for this set of entries. #[inline] pub fn encoding(&self) -> Encoding { self.encoding } /// Return the offset into the .debug_info section for this set of arange entries. #[inline] pub fn debug_info_offset(&self) -> DebugInfoOffset { self.debug_info_offset } /// Return the arange entries in this set. #[inline] pub fn entries(&self) -> ArangeEntryIter { ArangeEntryIter { input: self.entries.clone(), encoding: self.encoding, } } } /// An iterator over the aranges from a `.debug_aranges` section. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[derive(Debug, Clone)] pub struct ArangeEntryIter { input: R, encoding: Encoding, } impl ArangeEntryIter { /// Advance the iterator and return the next arange. /// /// Returns the newly parsed arange as `Ok(Some(arange))`. Returns `Ok(None)` /// when iteration is complete and all aranges have already been parsed and /// yielded. If an error occurs while parsing the next arange, then this error /// is returned as `Err(e)`, and all subsequent calls return `Ok(None)`. pub fn next(&mut self) -> Result> { loop { let raw_entry = match self.next_raw()? { Some(entry) => entry, None => return Ok(None), }; let entry = self.convert_raw(raw_entry)?; if entry.is_some() { return Ok(entry); } } } /// Advance the iterator and return the next arange without validating it. /// /// The returned entry will have `range.end` set to 0. /// This will return tombstone entries as well. pub fn next_raw(&mut self) -> Result> { if self.input.is_empty() { return Ok(None); } match ArangeEntry::parse(&mut self.input, self.encoding) { Ok(Some(entry)) => Ok(Some(entry)), Ok(None) => { self.input.empty(); Ok(None) } Err(e) => { self.input.empty(); Err(e) } } } /// Convert a raw range into a range. /// /// The raw range should have been obtained from `next_raw`. #[doc(hidden)] pub fn convert_raw(&self, mut entry: ArangeEntry) -> Result> { // Skip tombstone entries. // DWARF specifies a tombstone value of -1, but many linkers use 0. // However, 0 may be a valid address, so the caller must handle that case. let address_size = self.encoding.address_size; let tombstone_address = !0 >> (64 - self.encoding.address_size * 8); if entry.range.begin == tombstone_address { return Ok(None); } // Calculate end now so that we can handle overflow. entry.range.end = entry.range.begin.add_sized(entry.length, address_size)?; Ok(Some(entry)) } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for ArangeEntryIter { type Item = ArangeEntry; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { ArangeEntryIter::next(self) } } /// A single parsed arange. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct ArangeEntry { range: Range, length: u64, } impl ArangeEntry { /// Parse a single arange. Return `None` for the null arange, `Some` for an actual arange. fn parse(input: &mut R, encoding: Encoding) -> Result> { let address_size = encoding.address_size; let tuple_length = R::Offset::from_u8(2 * address_size); if tuple_length > input.len() { input.empty(); return Ok(None); } let begin = input.read_address(address_size)?; let length = input.read_address(address_size)?; let range = Range { begin, end: 0 }; match (begin, length) { // This is meant to be a null terminator, but in practice it can occur // before the end, possibly due to a linker omitting a function and // leaving an unrelocated entry. (0, 0) => Self::parse(input, encoding), _ => Ok(Some(ArangeEntry { range, length })), } } /// Return the beginning address of this arange. #[inline] pub fn address(&self) -> u64 { self.range.begin } /// Return the length of this arange. #[inline] pub fn length(&self) -> u64 { self.length } /// Return the range. #[inline] pub fn range(&self) -> Range { self.range } } #[cfg(test)] mod tests { use super::*; use crate::common::{DebugInfoOffset, Format}; use crate::endianity::LittleEndian; use crate::read::EndianSlice; #[test] fn test_iterate_headers() { #[rustfmt::skip] let buf = [ // 32-bit length = 28. 0x1c, 0x00, 0x00, 0x00, // Version. 0x02, 0x00, // Offset. 0x01, 0x02, 0x03, 0x04, // Address size. 0x04, // Segment size. 0x00, // Dummy padding and arange tuples. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 32-bit length = 36. 0x24, 0x00, 0x00, 0x00, // Version. 0x02, 0x00, // Offset. 0x11, 0x12, 0x13, 0x14, // Address size. 0x04, // Segment size. 0x00, // Dummy padding and arange tuples. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let debug_aranges = DebugAranges::new(&buf, LittleEndian); let mut headers = debug_aranges.headers(); let header = headers .next() .expect("should parse header ok") .expect("should have a header"); assert_eq!(header.offset(), DebugArangesOffset(0)); assert_eq!(header.debug_info_offset(), DebugInfoOffset(0x0403_0201)); let header = headers .next() .expect("should parse header ok") .expect("should have a header"); assert_eq!(header.offset(), DebugArangesOffset(0x20)); assert_eq!(header.debug_info_offset(), DebugInfoOffset(0x1413_1211)); } #[test] fn test_parse_header_ok() { #[rustfmt::skip] let buf = [ // 32-bit length = 28 (8 bytes header, 4 bytes padding, 16 bytes tuple data). 0x1c, 0x00, 0x00, 0x00, // Version. 0x02, 0x00, // Offset. 0x01, 0x02, 0x03, 0x04, // Address size. 0x08, // Segment size. 0x00, // Length to here = 12, tuple length = 16. // Padding to tuple length multiple = 4. 0x10, 0x00, 0x00, 0x00, // Dummy arange tuple data. 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy next arange. 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let header = ArangeHeader::parse(rest, DebugArangesOffset(0x10)).expect("should parse header ok"); assert_eq!( *rest, EndianSlice::new(&buf[buf.len() - 16..], LittleEndian) ); assert_eq!( header, ArangeHeader { offset: DebugArangesOffset(0x10), encoding: Encoding { format: Format::Dwarf32, version: 2, address_size: 8, }, length: 0x1c, debug_info_offset: DebugInfoOffset(0x0403_0201), entries: EndianSlice::new(&buf[buf.len() - 32..buf.len() - 16], LittleEndian), } ); } #[test] fn test_parse_header_overflow_error() { #[rustfmt::skip] let buf = [ // 32-bit length = 32. 0x20, 0x00, 0x00, 0x00, // Version. 0x02, 0x00, // Offset. 0x01, 0x02, 0x03, 0x04, // Address size. 0xff, // Segment size. 0x00, // Length to here = 12, tuple length = 20. // Padding to tuple length multiple = 4. 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy arange tuple data. 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy next arange. 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10)) .expect_err("should fail to parse header"); assert_eq!(error, Error::UnsupportedAddressSize(0xff)); } #[test] fn test_parse_header_div_by_zero_error() { #[rustfmt::skip] let buf = [ // 32-bit length = 32. 0x20, 0x00, 0x00, 0x00, // Version. 0x02, 0x00, // Offset. 0x01, 0x02, 0x03, 0x04, // Address size = 0. Could cause a division by zero if we aren't // careful. 0x00, // Segment size. 0x00, // Length to here = 12, tuple length = 20. // Padding to tuple length multiple = 4. 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy arange tuple data. 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy next arange. 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10)) .expect_err("should fail to parse header"); assert_eq!(error, Error::UnsupportedAddressSize(0)); } #[test] fn test_parse_entry_ok() { let encoding = Encoding { format: Format::Dwarf32, version: 2, address_size: 4, }; let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next().expect("should parse entry ok"); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) ); assert_eq!( entry, Some(ArangeEntry { range: Range { begin: 0x0403_0201, end: 0x0403_0201 + 0x0807_0605, }, length: 0x0807_0605, }) ); } #[test] fn test_parse_entry_zero() { let encoding = Encoding { format: Format::Dwarf32, version: 2, address_size: 4, }; #[rustfmt::skip] let buf = [ // Zero tuple. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Address. 0x01, 0x02, 0x03, 0x04, // Length. 0x05, 0x06, 0x07, 0x08, // Next tuple. 0x09 ]; let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next().expect("should parse entry ok"); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) ); assert_eq!( entry, Some(ArangeEntry { range: Range { begin: 0x0403_0201, end: 0x0403_0201 + 0x0807_0605, }, length: 0x0807_0605, }) ); } #[test] fn test_parse_entry_overflow_32() { let encoding = Encoding { format: Format::Dwarf32, version: 2, address_size: 4, }; #[rustfmt::skip] let buf = [ // Address. 0x01, 0x02, 0x03, 0x84, // Length. 0x05, 0x06, 0x07, 0x88, // Next tuple. 0x09 ]; let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next(); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) ); assert_eq!(entry, Err(Error::AddressOverflow)); } #[test] fn test_parse_entry_overflow_64() { let encoding = Encoding { format: Format::Dwarf32, version: 2, address_size: 8, }; #[rustfmt::skip] let buf = [ // Address. 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x80, // Length. 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x80, // Next tuple. 0x09 ]; let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next(); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) ); assert_eq!(entry, Err(Error::AddressOverflow)); } #[test] fn test_parse_entry_tombstone_32() { let encoding = Encoding { format: Format::Dwarf32, version: 2, address_size: 4, }; #[rustfmt::skip] let buf = [ // Address. 0xff, 0xff, 0xff, 0xff, // Length. 0x05, 0x06, 0x07, 0x08, // Address. 0x01, 0x02, 0x03, 0x04, // Length. 0x05, 0x06, 0x07, 0x08, // Next tuple. 0x09 ]; let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next_raw().unwrap(); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 9..], LittleEndian) ); assert_eq!( entry, Some(ArangeEntry { range: Range { begin: 0xffff_ffff, end: 0, }, length: 0x0807_0605, }) ); let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next().unwrap(); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) ); assert_eq!( entry, Some(ArangeEntry { range: Range { begin: 0x0403_0201, end: 0x0403_0201 + 0x0807_0605, }, length: 0x0807_0605, }) ); } #[test] fn test_parse_entry_tombstone_64() { let encoding = Encoding { format: Format::Dwarf32, version: 2, address_size: 8, }; #[rustfmt::skip] let buf = [ // Address. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Length. 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, // Address. 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, // Length. 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, // Next tuple. 0x09 ]; let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next_raw().unwrap(); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 17..], LittleEndian) ); assert_eq!( entry, Some(ArangeEntry { range: Range { begin: 0xffff_ffff_ffff_ffff, end: 0, }, length: 0x0807_0605, }) ); let mut iter = ArangeEntryIter { input: EndianSlice::new(&buf, LittleEndian), encoding, }; let entry = iter.next().unwrap(); assert_eq!( iter.input, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) ); assert_eq!( entry, Some(ArangeEntry { range: Range { begin: 0x0403_0201, end: 0x0403_0201 + 0x0807_0605, }, length: 0x0807_0605, }) ); } } gimli-0.31.1/src/read/cfi.rs000064400000000000000000010353201046102023000136240ustar 00000000000000#[cfg(feature = "read")] use alloc::boxed::Box; use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::mem; use core::num::Wrapping; use super::util::{ArrayLike, ArrayVec}; use crate::common::{ DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId, Vendor, }; use crate::constants::{self, DwEhPe}; use crate::endianity::Endianity; use crate::read::{ EndianSlice, Error, Expression, Reader, ReaderAddress, ReaderOffset, Result, Section, StoreOnHeap, }; /// `DebugFrame` contains the `.debug_frame` section's frame unwinding /// information required to unwind to and recover registers from older frames on /// the stack. For example, this is useful for a debugger that wants to print /// locals in a backtrace. /// /// Most interesting methods are defined in the /// [`UnwindSection`](trait.UnwindSection.html) trait. /// /// ### Differences between `.debug_frame` and `.eh_frame` /// /// While the `.debug_frame` section's information has a lot of overlap with the /// `.eh_frame` section's information, the `.eh_frame` information tends to only /// encode the subset of information needed for exception handling. Often, only /// one of `.eh_frame` or `.debug_frame` will be present in an object file. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct DebugFrame { section: R, address_size: u8, vendor: Vendor, } impl DebugFrame { /// Set the size of a target address in bytes. /// /// This defaults to the native word size. /// This is only used if the CIE version is less than 4. pub fn set_address_size(&mut self, address_size: u8) { self.address_size = address_size } /// Set the vendor extensions to use. /// /// This defaults to `Vendor::Default`. pub fn set_vendor(&mut self, vendor: Vendor) { self.vendor = vendor; } } impl<'input, Endian> DebugFrame> where Endian: Endianity, { /// Construct a new `DebugFrame` instance from the data in the /// `.debug_frame` section. /// /// It is the caller's responsibility to read the section and present it as /// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O /// loader on macOS, etc. /// /// ``` /// use gimli::{DebugFrame, NativeEndian}; /// /// // Use with `.debug_frame` /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_frame_section_somehow = || &buf; /// let debug_frame = DebugFrame::new(read_debug_frame_section_somehow(), NativeEndian); /// ``` pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl Section for DebugFrame { fn id() -> SectionId { SectionId::DebugFrame } fn reader(&self) -> &R { &self.section } } impl From for DebugFrame { fn from(section: R) -> Self { // Default to native word size. DebugFrame { section, address_size: mem::size_of::() as u8, vendor: Vendor::Default, } } } /// `EhFrameHdr` contains the information about the `.eh_frame_hdr` section. /// /// A pointer to the start of the `.eh_frame` data, and optionally, a binary /// search table of pointers to the `.eh_frame` records that are found in this section. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct EhFrameHdr(R); /// `ParsedEhFrameHdr` contains the parsed information from the `.eh_frame_hdr` section. #[derive(Clone, Debug)] pub struct ParsedEhFrameHdr { address_size: u8, section: R, eh_frame_ptr: Pointer, fde_count: u64, table_enc: DwEhPe, table: R, } impl<'input, Endian> EhFrameHdr> where Endian: Endianity, { /// Constructs a new `EhFrameHdr` instance from the data in the `.eh_frame_hdr` section. pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl EhFrameHdr { /// Parses this `EhFrameHdr` to a `ParsedEhFrameHdr`. pub fn parse(&self, bases: &BaseAddresses, address_size: u8) -> Result> { let mut reader = self.0.clone(); let version = reader.read_u8()?; if version != 1 { return Err(Error::UnknownVersion(u64::from(version))); } let eh_frame_ptr_enc = parse_pointer_encoding(&mut reader)?; let fde_count_enc = parse_pointer_encoding(&mut reader)?; let table_enc = parse_pointer_encoding(&mut reader)?; let parameters = PointerEncodingParameters { bases: &bases.eh_frame_hdr, func_base: None, address_size, section: &self.0, }; // Omitting this pointer is not valid (defeats the purpose of .eh_frame_hdr entirely) if eh_frame_ptr_enc == constants::DW_EH_PE_omit { return Err(Error::CannotParseOmitPointerEncoding); } let eh_frame_ptr = parse_encoded_pointer(eh_frame_ptr_enc, ¶meters, &mut reader)?; let fde_count; if fde_count_enc == constants::DW_EH_PE_omit || table_enc == constants::DW_EH_PE_omit { fde_count = 0 } else { if fde_count_enc != fde_count_enc.format() { return Err(Error::UnsupportedPointerEncoding); } fde_count = parse_encoded_value(fde_count_enc, ¶meters, &mut reader)?; } Ok(ParsedEhFrameHdr { address_size, section: self.0.clone(), eh_frame_ptr, fde_count, table_enc, table: reader, }) } } impl Section for EhFrameHdr { fn id() -> SectionId { SectionId::EhFrameHdr } fn reader(&self) -> &R { &self.0 } } impl From for EhFrameHdr { fn from(section: R) -> Self { EhFrameHdr(section) } } impl ParsedEhFrameHdr { /// Returns the address of the binary's `.eh_frame` section. pub fn eh_frame_ptr(&self) -> Pointer { self.eh_frame_ptr } /// Retrieves the CFI binary search table, if there is one. pub fn table(&self) -> Option> { // There are two big edge cases here: // * You search the table for an invalid address. As this is just a binary // search table, we always have to return a valid result for that (unless // you specify an address that is lower than the first address in the // table). Since this means that you have to recheck that the FDE contains // your address anyways, we just return the first FDE even when the address // is too low. After all, we're just doing a normal binary search. // * This falls apart when the table is empty - there is no entry we could // return. We conclude that an empty table is not really a table at all. if self.fde_count == 0 { None } else { Some(EhHdrTable { hdr: self }) } } } /// An iterator for `.eh_frame_hdr` section's binary search table. /// /// Each table entry consists of a tuple containing an `initial_location` and `address`. /// The `initial location` represents the first address that the targeted FDE /// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. /// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. #[derive(Debug)] pub struct EhHdrTableIter<'a, 'bases, R: Reader> { hdr: &'a ParsedEhFrameHdr, table: R, bases: &'bases BaseAddresses, remain: u64, } impl<'a, 'bases, R: Reader> EhHdrTableIter<'a, 'bases, R> { /// Yield the next entry in the `EhHdrTableIter`. pub fn next(&mut self) -> Result> { if self.remain == 0 { return Ok(None); } let parameters = PointerEncodingParameters { bases: &self.bases.eh_frame_hdr, func_base: None, address_size: self.hdr.address_size, section: &self.hdr.section, }; self.remain -= 1; let from = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; let to = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; Ok(Some((from, to))) } /// Yield the nth entry in the `EhHdrTableIter` pub fn nth(&mut self, n: usize) -> Result> { use core::convert::TryFrom; let size = match self.hdr.table_enc.format() { constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => { return Err(Error::VariableLengthSearchTable); } constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2, constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4, constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8, _ => return Err(Error::UnknownPointerEncoding(self.hdr.table_enc)), }; let row_size = size * 2; let n = u64::try_from(n).map_err(|_| Error::UnsupportedOffset)?; self.remain = self.remain.saturating_sub(n); self.table.skip(R::Offset::from_u64(n * row_size)?)?; self.next() } } #[cfg(feature = "fallible-iterator")] impl<'a, 'bases, R: Reader> fallible_iterator::FallibleIterator for EhHdrTableIter<'a, 'bases, R> { type Item = (Pointer, Pointer); type Error = Error; fn next(&mut self) -> Result> { EhHdrTableIter::next(self) } fn size_hint(&self) -> (usize, Option) { use core::convert::TryInto; ( self.remain.try_into().unwrap_or(0), self.remain.try_into().ok(), ) } fn nth(&mut self, n: usize) -> Result> { EhHdrTableIter::nth(self, n) } } /// The CFI binary search table that is an optional part of the `.eh_frame_hdr` section. #[derive(Debug, Clone)] pub struct EhHdrTable<'a, R: Reader> { hdr: &'a ParsedEhFrameHdr, } impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { /// Return an iterator that can walk the `.eh_frame_hdr` table. /// /// Each table entry consists of a tuple containing an `initial_location` and `address`. /// The `initial location` represents the first address that the targeted FDE /// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. /// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. pub fn iter<'bases>(&self, bases: &'bases BaseAddresses) -> EhHdrTableIter<'_, 'bases, R> { EhHdrTableIter { hdr: self.hdr, bases, remain: self.hdr.fde_count, table: self.hdr.table.clone(), } } /// *Probably* returns a pointer to the FDE for the given address. /// /// This performs a binary search, so if there is no FDE for the given address, /// this function **will** return a pointer to any other FDE that's close by. /// /// To be sure, you **must** call `contains` on the FDE. pub fn lookup(&self, address: u64, bases: &BaseAddresses) -> Result { let size = match self.hdr.table_enc.format() { constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => { return Err(Error::VariableLengthSearchTable); } constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2, constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4, constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8, _ => return Err(Error::UnknownPointerEncoding(self.hdr.table_enc)), }; let row_size = size * 2; let mut len = self.hdr.fde_count; let mut reader = self.hdr.table.clone(); let parameters = PointerEncodingParameters { bases: &bases.eh_frame_hdr, func_base: None, address_size: self.hdr.address_size, section: &self.hdr.section, }; while len > 1 { let head = reader.split(R::Offset::from_u64((len / 2) * row_size)?)?; let tail = reader.clone(); let pivot = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader)?.direct()?; match pivot.cmp(&address) { Ordering::Equal => { reader = tail; break; } Ordering::Less => { reader = tail; len = len - (len / 2); } Ordering::Greater => { reader = head; len /= 2; } } } reader.skip(R::Offset::from_u64(size)?)?; parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader) } /// Convert a `Pointer` to a section offset. /// /// This does not support indirect pointers. pub fn pointer_to_offset(&self, ptr: Pointer) -> Result> { let ptr = ptr.direct()?; let eh_frame_ptr = self.hdr.eh_frame_ptr().direct()?; // Calculate the offset in the EhFrame section R::Offset::from_u64(ptr - eh_frame_ptr).map(EhFrameOffset) } /// Returns a parsed FDE for the given address, or `NoUnwindInfoForAddress` /// if there are none. /// /// You must provide a function to get its associated CIE. See /// `PartialFrameDescriptionEntry::parse` for more information. /// /// # Example /// /// ``` /// # use gimli::{BaseAddresses, EhFrame, ParsedEhFrameHdr, EndianSlice, NativeEndian, Error, UnwindSection}; /// # fn foo() -> Result<(), Error> { /// # let eh_frame: EhFrame> = unreachable!(); /// # let eh_frame_hdr: ParsedEhFrameHdr> = unimplemented!(); /// # let addr = 0; /// # let bases = unimplemented!(); /// let table = eh_frame_hdr.table().unwrap(); /// let fde = table.fde_for_address(&eh_frame, &bases, addr, EhFrame::cie_from_offset)?; /// # Ok(()) /// # } /// ``` pub fn fde_for_address( &self, frame: &EhFrame, bases: &BaseAddresses, address: u64, get_cie: F, ) -> Result> where F: FnMut( &EhFrame, &BaseAddresses, EhFrameOffset, ) -> Result>, { let fdeptr = self.lookup(address, bases)?; let offset = self.pointer_to_offset(fdeptr)?; let entry = frame.fde_from_offset(bases, offset, get_cie)?; if entry.contains(address) { Ok(entry) } else { Err(Error::NoUnwindInfoForAddress) } } #[inline] #[doc(hidden)] #[deprecated(note = "Method renamed to fde_for_address; use that instead.")] pub fn lookup_and_parse( &self, address: u64, bases: &BaseAddresses, frame: EhFrame, get_cie: F, ) -> Result> where F: FnMut( &EhFrame, &BaseAddresses, EhFrameOffset, ) -> Result>, { self.fde_for_address(&frame, bases, address, get_cie) } /// Returns the frame unwind information for the given address, /// or `NoUnwindInfoForAddress` if there are none. /// /// You must provide a function to get the associated CIE. See /// `PartialFrameDescriptionEntry::parse` for more information. pub fn unwind_info_for_address<'ctx, F, S>( &self, frame: &EhFrame, bases: &BaseAddresses, ctx: &'ctx mut UnwindContext, address: u64, get_cie: F, ) -> Result<&'ctx UnwindTableRow> where F: FnMut( &EhFrame, &BaseAddresses, EhFrameOffset, ) -> Result>, S: UnwindContextStorage, { let fde = self.fde_for_address(frame, bases, address, get_cie)?; fde.unwind_info_for_address(frame, bases, ctx, address) } } /// `EhFrame` contains the frame unwinding information needed during exception /// handling found in the `.eh_frame` section. /// /// Most interesting methods are defined in the /// [`UnwindSection`](trait.UnwindSection.html) trait. /// /// See /// [`DebugFrame`](./struct.DebugFrame.html#differences-between-debug_frame-and-eh_frame) /// for some discussion on the differences between `.debug_frame` and /// `.eh_frame`. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct EhFrame { section: R, address_size: u8, vendor: Vendor, } impl EhFrame { /// Set the size of a target address in bytes. /// /// This defaults to the native word size. pub fn set_address_size(&mut self, address_size: u8) { self.address_size = address_size } /// Set the vendor extensions to use. /// /// This defaults to `Vendor::Default`. pub fn set_vendor(&mut self, vendor: Vendor) { self.vendor = vendor; } } impl<'input, Endian> EhFrame> where Endian: Endianity, { /// Construct a new `EhFrame` instance from the data in the /// `.eh_frame` section. /// /// It is the caller's responsibility to read the section and present it as /// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O /// loader on macOS, etc. /// /// ``` /// use gimli::{EhFrame, EndianSlice, NativeEndian}; /// /// // Use with `.eh_frame` /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_eh_frame_section_somehow = || &buf; /// let eh_frame = EhFrame::new(read_eh_frame_section_somehow(), NativeEndian); /// ``` pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl Section for EhFrame { fn id() -> SectionId { SectionId::EhFrame } fn reader(&self) -> &R { &self.section } } impl From for EhFrame { fn from(section: R) -> Self { // Default to native word size. EhFrame { section, address_size: mem::size_of::() as u8, vendor: Vendor::Default, } } } // This has to be `pub` to silence a warning (that is deny(..)'d by default) in // rustc. Eventually, not having this `pub` will become a hard error. #[doc(hidden)] #[allow(missing_docs)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CieOffsetEncoding { U32, U64, } /// An offset into an `UnwindSection`. // // Needed to avoid conflicting implementations of `Into`. pub trait UnwindOffset: Copy + Debug + Eq + From where T: ReaderOffset, { /// Convert an `UnwindOffset` into a `T`. fn into(self) -> T; } impl UnwindOffset for DebugFrameOffset where T: ReaderOffset, { #[inline] fn into(self) -> T { self.0 } } impl UnwindOffset for EhFrameOffset where T: ReaderOffset, { #[inline] fn into(self) -> T { self.0 } } /// This trait completely encapsulates everything that is different between /// `.eh_frame` and `.debug_frame`, as well as all the bits that can change /// between DWARF versions. #[doc(hidden)] pub trait _UnwindSectionPrivate { /// Get the underlying section data. fn section(&self) -> &R; /// Returns true if the section allows a zero terminator. fn has_zero_terminator() -> bool; /// Return true if the given offset if the CIE sentinel, false otherwise. fn is_cie(format: Format, id: u64) -> bool; /// Return the CIE offset/ID encoding used by this unwind section with the /// given DWARF format. fn cie_offset_encoding(format: Format) -> CieOffsetEncoding; /// For `.eh_frame`, CIE offsets are relative to the current position. For /// `.debug_frame`, they are relative to the start of the section. We always /// internally store them relative to the section, so we handle translating /// `.eh_frame`'s relative offsets in this method. If the offset calculation /// underflows, return `None`. fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option; /// Does this version of this unwind section encode address and segment /// sizes in its CIEs? fn has_address_and_segment_sizes(version: u8) -> bool; /// The address size to use if `has_address_and_segment_sizes` returns false. fn address_size(&self) -> u8; /// The vendor extensions to use. fn vendor(&self) -> Vendor; } /// A section holding unwind information: either `.debug_frame` or /// `.eh_frame`. See [`DebugFrame`](./struct.DebugFrame.html) and /// [`EhFrame`](./struct.EhFrame.html) respectively. pub trait UnwindSection: Clone + Debug + _UnwindSectionPrivate { /// The offset type associated with this CFI section. Either /// `DebugFrameOffset` or `EhFrameOffset`. type Offset: UnwindOffset; /// Iterate over the `CommonInformationEntry`s and `FrameDescriptionEntry`s /// in this `.debug_frame` section. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). fn entries<'bases>(&self, bases: &'bases BaseAddresses) -> CfiEntriesIter<'bases, Self, R> { CfiEntriesIter { section: self.clone(), bases, input: self.section().clone(), } } /// Parse the `CommonInformationEntry` at the given offset. fn cie_from_offset( &self, bases: &BaseAddresses, offset: Self::Offset, ) -> Result> { let offset = UnwindOffset::into(offset); let input = &mut self.section().clone(); input.skip(offset)?; CommonInformationEntry::parse(bases, self, input) } /// Parse the `PartialFrameDescriptionEntry` at the given offset. fn partial_fde_from_offset<'bases>( &self, bases: &'bases BaseAddresses, offset: Self::Offset, ) -> Result> { let offset = UnwindOffset::into(offset); let input = &mut self.section().clone(); input.skip(offset)?; PartialFrameDescriptionEntry::parse_partial(self, bases, input) } /// Parse the `FrameDescriptionEntry` at the given offset. fn fde_from_offset( &self, bases: &BaseAddresses, offset: Self::Offset, get_cie: F, ) -> Result> where F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result>, { let partial = self.partial_fde_from_offset(bases, offset)?; partial.parse(get_cie) } /// Find the `FrameDescriptionEntry` for the given address. /// /// If found, the FDE is returned. If not found, /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. /// If parsing fails, the error is returned. /// /// You must provide a function to get its associated CIE. See /// `PartialFrameDescriptionEntry::parse` for more information. /// /// Note: this iterates over all FDEs. If available, it is possible /// to do a binary search with `EhFrameHdr::fde_for_address` instead. fn fde_for_address( &self, bases: &BaseAddresses, address: u64, mut get_cie: F, ) -> Result> where F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result>, { let mut entries = self.entries(bases); while let Some(entry) = entries.next()? { match entry { CieOrFde::Cie(_) => {} CieOrFde::Fde(partial) => { let fde = partial.parse(&mut get_cie)?; if fde.contains(address) { return Ok(fde); } } } } Err(Error::NoUnwindInfoForAddress) } /// Find the frame unwind information for the given address. /// /// If found, the unwind information is returned. If not found, /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or /// CFI evaluation fails, the error is returned. /// /// ``` /// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindContext, /// UnwindSection}; /// /// # fn foo() -> gimli::Result<()> { /// # let read_eh_frame_section = || unimplemented!(); /// // Get the `.eh_frame` section from the object file. Alternatively, /// // use `EhFrame` with the `.eh_frame` section of the object file. /// let eh_frame = EhFrame::new(read_eh_frame_section(), NativeEndian); /// /// # let get_frame_pc = || unimplemented!(); /// // Get the address of the PC for a frame you'd like to unwind. /// let address = get_frame_pc(); /// /// // This context is reusable, which cuts down on heap allocations. /// let ctx = UnwindContext::new(); /// /// // Optionally provide base addresses for any relative pointers. If a /// // base address isn't provided and a pointer is found that is relative to /// // it, we will return an `Err`. /// # let address_of_text_section_in_memory = unimplemented!(); /// # let address_of_got_section_in_memory = unimplemented!(); /// let bases = BaseAddresses::default() /// .set_text(address_of_text_section_in_memory) /// .set_got(address_of_got_section_in_memory); /// /// let unwind_info = eh_frame.unwind_info_for_address( /// &bases, /// &mut ctx, /// address, /// EhFrame::cie_from_offset, /// )?; /// /// # let do_stuff_with = |_| unimplemented!(); /// do_stuff_with(unwind_info); /// # let _ = ctx; /// # unreachable!() /// # } /// ``` #[inline] fn unwind_info_for_address<'ctx, F, S>( &self, bases: &BaseAddresses, ctx: &'ctx mut UnwindContext, address: u64, get_cie: F, ) -> Result<&'ctx UnwindTableRow> where F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result>, S: UnwindContextStorage, { let fde = self.fde_for_address(bases, address, get_cie)?; fde.unwind_info_for_address(self, bases, ctx, address) } } impl _UnwindSectionPrivate for DebugFrame { fn section(&self) -> &R { &self.section } fn has_zero_terminator() -> bool { false } fn is_cie(format: Format, id: u64) -> bool { match format { Format::Dwarf32 => id == 0xffff_ffff, Format::Dwarf64 => id == 0xffff_ffff_ffff_ffff, } } fn cie_offset_encoding(format: Format) -> CieOffsetEncoding { match format { Format::Dwarf32 => CieOffsetEncoding::U32, Format::Dwarf64 => CieOffsetEncoding::U64, } } fn resolve_cie_offset(&self, _: R::Offset, offset: R::Offset) -> Option { Some(offset) } fn has_address_and_segment_sizes(version: u8) -> bool { version == 4 } fn address_size(&self) -> u8 { self.address_size } fn vendor(&self) -> Vendor { self.vendor } } impl UnwindSection for DebugFrame { type Offset = DebugFrameOffset; } impl _UnwindSectionPrivate for EhFrame { fn section(&self) -> &R { &self.section } fn has_zero_terminator() -> bool { true } fn is_cie(_: Format, id: u64) -> bool { id == 0 } fn cie_offset_encoding(_format: Format) -> CieOffsetEncoding { // `.eh_frame` offsets are always 4 bytes, regardless of the DWARF // format. CieOffsetEncoding::U32 } fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option { base.checked_sub(offset) } fn has_address_and_segment_sizes(_version: u8) -> bool { false } fn address_size(&self) -> u8 { self.address_size } fn vendor(&self) -> Vendor { self.vendor } } impl UnwindSection for EhFrame { type Offset = EhFrameOffset; } /// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers. /// /// During CIE/FDE parsing, if a relative pointer is encountered for a base /// address that is unknown, an Err will be returned. /// /// ``` /// use gimli::BaseAddresses; /// /// # fn foo() { /// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!(); /// # let address_of_eh_frame_section_in_memory = unimplemented!(); /// # let address_of_text_section_in_memory = unimplemented!(); /// # let address_of_got_section_in_memory = unimplemented!(); /// # let address_of_the_start_of_current_func = unimplemented!(); /// let bases = BaseAddresses::default() /// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory) /// .set_eh_frame(address_of_eh_frame_section_in_memory) /// .set_text(address_of_text_section_in_memory) /// .set_got(address_of_got_section_in_memory); /// # let _ = bases; /// # } /// ``` #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct BaseAddresses { /// The base addresses to use for pointers in the `.eh_frame_hdr` section. pub eh_frame_hdr: SectionBaseAddresses, /// The base addresses to use for pointers in the `.eh_frame` section. pub eh_frame: SectionBaseAddresses, } /// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers /// in a particular section. /// /// See `BaseAddresses` for methods that are helpful in setting these addresses. #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct SectionBaseAddresses { /// The address of the section containing the pointer. pub section: Option, /// The base address for text relative pointers. /// This is generally the address of the `.text` section. pub text: Option, /// The base address for data relative pointers. /// /// For pointers in the `.eh_frame_hdr` section, this is the address /// of the `.eh_frame_hdr` section /// /// For pointers in the `.eh_frame` section, this is generally the /// global pointer, such as the address of the `.got` section. pub data: Option, } impl BaseAddresses { /// Set the `.eh_frame_hdr` section base address. #[inline] pub fn set_eh_frame_hdr(mut self, addr: u64) -> Self { self.eh_frame_hdr.section = Some(addr); self.eh_frame_hdr.data = Some(addr); self } /// Set the `.eh_frame` section base address. #[inline] pub fn set_eh_frame(mut self, addr: u64) -> Self { self.eh_frame.section = Some(addr); self } /// Set the `.text` section base address. #[inline] pub fn set_text(mut self, addr: u64) -> Self { self.eh_frame_hdr.text = Some(addr); self.eh_frame.text = Some(addr); self } /// Set the `.got` section base address. #[inline] pub fn set_got(mut self, addr: u64) -> Self { self.eh_frame.data = Some(addr); self } } /// An iterator over CIE and FDE entries in a `.debug_frame` or `.eh_frame` /// section. /// /// Some pointers may be encoded relative to various base addresses. Use the /// [`BaseAddresses`](./struct.BaseAddresses.html) parameter to provide them. By /// default, none are provided. If a relative pointer is encountered for a base /// address that is unknown, an `Err` will be returned and iteration will abort. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). /// /// ``` /// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindSection}; /// /// # fn foo() -> gimli::Result<()> { /// # let read_eh_frame_somehow = || unimplemented!(); /// let eh_frame = EhFrame::new(read_eh_frame_somehow(), NativeEndian); /// /// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!(); /// # let address_of_eh_frame_section_in_memory = unimplemented!(); /// # let address_of_text_section_in_memory = unimplemented!(); /// # let address_of_got_section_in_memory = unimplemented!(); /// # let address_of_the_start_of_current_func = unimplemented!(); /// // Provide base addresses for relative pointers. /// let bases = BaseAddresses::default() /// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory) /// .set_eh_frame(address_of_eh_frame_section_in_memory) /// .set_text(address_of_text_section_in_memory) /// .set_got(address_of_got_section_in_memory); /// /// let mut entries = eh_frame.entries(&bases); /// /// # let do_stuff_with = |_| unimplemented!(); /// while let Some(entry) = entries.next()? { /// do_stuff_with(entry) /// } /// # unreachable!() /// # } /// ``` #[derive(Clone, Debug)] pub struct CfiEntriesIter<'bases, Section, R> where R: Reader, Section: UnwindSection, { section: Section, bases: &'bases BaseAddresses, input: R, } impl<'bases, Section, R> CfiEntriesIter<'bases, Section, R> where R: Reader, Section: UnwindSection, { /// Advance the iterator to the next entry. pub fn next(&mut self) -> Result>> { loop { if self.input.is_empty() { return Ok(None); } match parse_cfi_entry(self.bases, &self.section, &mut self.input) { Ok(Some(entry)) => return Ok(Some(entry)), Err(e) => { self.input.empty(); return Err(e); } Ok(None) => { if Section::has_zero_terminator() { self.input.empty(); return Ok(None); } // Hack: If we get to here, then we're reading `.debug_frame` and // encountered a length of 0. This is a compiler or linker bug // (originally seen for NASM, fixed in 2.15rc9). // Skip this value and try again. continue; } } } } } #[cfg(feature = "fallible-iterator")] impl<'bases, Section, R> fallible_iterator::FallibleIterator for CfiEntriesIter<'bases, Section, R> where R: Reader, Section: UnwindSection, { type Item = CieOrFde<'bases, Section, R>; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { CfiEntriesIter::next(self) } } /// Either a `CommonInformationEntry` (CIE) or a `FrameDescriptionEntry` (FDE). #[derive(Clone, Debug, PartialEq, Eq)] pub enum CieOrFde<'bases, Section, R> where R: Reader, Section: UnwindSection, { /// This CFI entry is a `CommonInformationEntry`. Cie(CommonInformationEntry), /// This CFI entry is a `FrameDescriptionEntry`, however fully parsing it /// requires parsing its CIE first, so it is left in a partially parsed /// state. Fde(PartialFrameDescriptionEntry<'bases, Section, R>), } fn parse_cfi_entry<'bases, Section, R>( bases: &'bases BaseAddresses, section: &Section, input: &mut R, ) -> Result>> where R: Reader, Section: UnwindSection, { let offset = input.offset_from(section.section()); let (length, format) = input.read_initial_length()?; if length.into_u64() == 0 { return Ok(None); } let mut rest = input.split(length)?; let cie_offset_base = rest.offset_from(section.section()); let cie_id_or_offset = match Section::cie_offset_encoding(format) { CieOffsetEncoding::U32 => rest.read_u32().map(u64::from)?, CieOffsetEncoding::U64 => rest.read_u64()?, }; if Section::is_cie(format, cie_id_or_offset) { let cie = CommonInformationEntry::parse_rest(offset, length, format, bases, section, rest)?; Ok(Some(CieOrFde::Cie(cie))) } else { let cie_offset = R::Offset::from_u64(cie_id_or_offset)?; let cie_offset = match section.resolve_cie_offset(cie_offset_base, cie_offset) { None => return Err(Error::OffsetOutOfBounds), Some(cie_offset) => cie_offset, }; let fde = PartialFrameDescriptionEntry { offset, length, format, cie_offset: cie_offset.into(), rest, section: section.clone(), bases, }; Ok(Some(CieOrFde::Fde(fde))) } } /// We support the z-style augmentation [defined by `.eh_frame`][ehframe]. /// /// [ehframe]: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct Augmentation { /// > A 'L' may be present at any position after the first character of the /// > string. This character may only be present if 'z' is the first character /// > of the string. If present, it indicates the presence of one argument in /// > the Augmentation Data of the CIE, and a corresponding argument in the /// > Augmentation Data of the FDE. The argument in the Augmentation Data of /// > the CIE is 1-byte and represents the pointer encoding used for the /// > argument in the Augmentation Data of the FDE, which is the address of a /// > language-specific data area (LSDA). The size of the LSDA pointer is /// > specified by the pointer encoding used. lsda: Option, /// > A 'P' may be present at any position after the first character of the /// > string. This character may only be present if 'z' is the first character /// > of the string. If present, it indicates the presence of two arguments in /// > the Augmentation Data of the CIE. The first argument is 1-byte and /// > represents the pointer encoding used for the second argument, which is /// > the address of a personality routine handler. The size of the /// > personality routine pointer is specified by the pointer encoding used. personality: Option<(constants::DwEhPe, Pointer)>, /// > A 'R' may be present at any position after the first character of the /// > string. This character may only be present if 'z' is the first character /// > of the string. If present, The Augmentation Data shall include a 1 byte /// > argument that represents the pointer encoding for the address pointers /// > used in the FDE. fde_address_encoding: Option, /// True if this CIE's FDEs are trampolines for signal handlers. is_signal_trampoline: bool, } impl Augmentation { fn parse( augmentation_str: &mut R, bases: &BaseAddresses, address_size: u8, section: &Section, input: &mut R, ) -> Result where R: Reader, Section: UnwindSection, { debug_assert!( !augmentation_str.is_empty(), "Augmentation::parse should only be called if we have an augmentation" ); let mut augmentation = Augmentation::default(); let mut parsed_first = false; let mut data = None; while !augmentation_str.is_empty() { let ch = augmentation_str.read_u8()?; match ch { b'z' => { if parsed_first { return Err(Error::UnknownAugmentation); } let augmentation_length = input.read_uleb128().and_then(R::Offset::from_u64)?; data = Some(input.split(augmentation_length)?); } b'L' => { let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; let encoding = parse_pointer_encoding(rest)?; augmentation.lsda = Some(encoding); } b'P' => { let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; let encoding = parse_pointer_encoding(rest)?; let parameters = PointerEncodingParameters { bases: &bases.eh_frame, func_base: None, address_size, section: section.section(), }; let personality = parse_encoded_pointer(encoding, ¶meters, rest)?; augmentation.personality = Some((encoding, personality)); } b'R' => { let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; let encoding = parse_pointer_encoding(rest)?; augmentation.fde_address_encoding = Some(encoding); } b'S' => augmentation.is_signal_trampoline = true, _ => return Err(Error::UnknownAugmentation), } parsed_first = true; } Ok(augmentation) } } /// Parsed augmentation data for a `FrameDescriptEntry`. #[derive(Clone, Debug, Default, PartialEq, Eq)] struct AugmentationData { lsda: Option, } impl AugmentationData { fn parse( augmentation: &Augmentation, encoding_parameters: &PointerEncodingParameters<'_, R>, input: &mut R, ) -> Result { // In theory, we should be iterating over the original augmentation // string, interpreting each character, and reading the appropriate bits // out of the augmentation data as we go. However, the only character // that defines augmentation data in the FDE is the 'L' character, so we // can just check for its presence directly. let aug_data_len = input.read_uleb128().and_then(R::Offset::from_u64)?; let rest = &mut input.split(aug_data_len)?; let mut augmentation_data = AugmentationData::default(); if let Some(encoding) = augmentation.lsda { let lsda = parse_encoded_pointer(encoding, encoding_parameters, rest)?; augmentation_data.lsda = Some(lsda); } Ok(augmentation_data) } } /// > A Common Information Entry holds information that is shared among many /// > Frame Description Entries. There is at least one CIE in every non-empty /// > `.debug_frame` section. #[derive(Clone, Debug, PartialEq, Eq)] pub struct CommonInformationEntry::Offset> where R: Reader, Offset: ReaderOffset, { /// The offset of this entry from the start of its containing section. offset: Offset, /// > A constant that gives the number of bytes of the CIE structure, not /// > including the length field itself (see Section 7.2.2). The size of the /// > length field plus the value of length must be an integral multiple of /// > the address size. length: Offset, format: Format, /// > A version number (see Section 7.23). This number is specific to the /// > call frame information and is independent of the DWARF version number. version: u8, /// The parsed augmentation, if any. augmentation: Option, /// > The size of a target address in this CIE and any FDEs that use it, in /// > bytes. If a compilation unit exists for this frame, its address size /// > must match the address size here. address_size: u8, /// "A constant that is factored out of all advance location instructions /// (see Section 6.4.2.1)." code_alignment_factor: u64, /// > A constant that is factored out of certain offset instructions (see /// > below). The resulting value is (operand * data_alignment_factor). data_alignment_factor: i64, /// > An unsigned LEB128 constant that indicates which column in the rule /// > table represents the return address of the function. Note that this /// > column might not correspond to an actual machine register. return_address_register: Register, /// > A sequence of rules that are interpreted to create the initial setting /// > of each column in the table. /// /// > The default rule for all columns before interpretation of the initial /// > instructions is the undefined rule. However, an ABI authoring body or a /// > compilation system authoring body may specify an alternate default /// > value for any or all columns. /// /// This is followed by `DW_CFA_nop` padding until the end of `length` bytes /// in the input. initial_instructions: R, } impl CommonInformationEntry { fn parse>( bases: &BaseAddresses, section: &Section, input: &mut R, ) -> Result> { match parse_cfi_entry(bases, section, input)? { Some(CieOrFde::Cie(cie)) => Ok(cie), Some(CieOrFde::Fde(_)) => Err(Error::NotCieId), None => Err(Error::NoEntryAtGivenOffset), } } fn parse_rest>( offset: R::Offset, length: R::Offset, format: Format, bases: &BaseAddresses, section: &Section, mut rest: R, ) -> Result> { let version = rest.read_u8()?; // Version 1 of `.debug_frame` corresponds to DWARF 2, and then for // DWARF 3 and 4, I think they decided to just match the standard's // version. match version { 1 | 3 | 4 => (), _ => return Err(Error::UnknownVersion(u64::from(version))), } let mut augmentation_string = rest.read_null_terminated_slice()?; let address_size = if Section::has_address_and_segment_sizes(version) { let address_size = rest.read_address_size()?; let segment_size = rest.read_u8()?; if segment_size != 0 { return Err(Error::UnsupportedSegmentSize); } address_size } else { section.address_size() }; let code_alignment_factor = rest.read_uleb128()?; let data_alignment_factor = rest.read_sleb128()?; let return_address_register = if version == 1 { Register(rest.read_u8()?.into()) } else { rest.read_uleb128().and_then(Register::from_u64)? }; let augmentation = if augmentation_string.is_empty() { None } else { Some(Augmentation::parse( &mut augmentation_string, bases, address_size, section, &mut rest, )?) }; let entry = CommonInformationEntry { offset, length, format, version, augmentation, address_size, code_alignment_factor, data_alignment_factor, return_address_register, initial_instructions: rest, }; Ok(entry) } } /// # Signal Safe Methods /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations. impl CommonInformationEntry { /// Get the offset of this entry from the start of its containing section. pub fn offset(&self) -> R::Offset { self.offset } /// Return the encoding parameters for this CIE. pub fn encoding(&self) -> Encoding { Encoding { format: self.format, version: u16::from(self.version), address_size: self.address_size, } } /// The size of addresses (in bytes) in this CIE. pub fn address_size(&self) -> u8 { self.address_size } /// Iterate over this CIE's initial instructions. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn instructions<'a, Section>( &self, section: &'a Section, bases: &'a BaseAddresses, ) -> CallFrameInstructionIter<'a, R> where Section: UnwindSection, { CallFrameInstructionIter { input: self.initial_instructions.clone(), address_encoding: None, parameters: PointerEncodingParameters { bases: &bases.eh_frame, func_base: None, address_size: self.address_size, section: section.section(), }, vendor: section.vendor(), } } /// > A constant that gives the number of bytes of the CIE structure, not /// > including the length field itself (see Section 7.2.2). The size of the /// > length field plus the value of length must be an integral multiple of /// > the address size. pub fn entry_len(&self) -> R::Offset { self.length } /// > A version number (see Section 7.23). This number is specific to the /// > call frame information and is independent of the DWARF version number. pub fn version(&self) -> u8 { self.version } /// Get the augmentation data, if any exists. /// /// The only augmentation understood by `gimli` is that which is defined by /// `.eh_frame`. pub fn augmentation(&self) -> Option<&Augmentation> { self.augmentation.as_ref() } /// True if this CIE's FDEs have a LSDA. pub fn has_lsda(&self) -> bool { self.augmentation.map_or(false, |a| a.lsda.is_some()) } /// Return the encoding of the LSDA address for this CIE's FDEs. pub fn lsda_encoding(&self) -> Option { self.augmentation.and_then(|a| a.lsda) } /// Return the encoding and address of the personality routine handler /// for this CIE's FDEs. pub fn personality_with_encoding(&self) -> Option<(constants::DwEhPe, Pointer)> { self.augmentation.as_ref().and_then(|a| a.personality) } /// Return the address of the personality routine handler /// for this CIE's FDEs. pub fn personality(&self) -> Option { self.augmentation .as_ref() .and_then(|a| a.personality) .map(|(_, p)| p) } /// Return the encoding of the addresses for this CIE's FDEs. pub fn fde_address_encoding(&self) -> Option { self.augmentation.and_then(|a| a.fde_address_encoding) } /// True if this CIE's FDEs are trampolines for signal handlers. pub fn is_signal_trampoline(&self) -> bool { self.augmentation.map_or(false, |a| a.is_signal_trampoline) } /// > A constant that is factored out of all advance location instructions /// > (see Section 6.4.2.1). pub fn code_alignment_factor(&self) -> u64 { self.code_alignment_factor } /// > A constant that is factored out of certain offset instructions (see /// > below). The resulting value is (operand * data_alignment_factor). pub fn data_alignment_factor(&self) -> i64 { self.data_alignment_factor } /// > An unsigned ... constant that indicates which column in the rule /// > table represents the return address of the function. Note that this /// > column might not correspond to an actual machine register. pub fn return_address_register(&self) -> Register { self.return_address_register } } /// A partially parsed `FrameDescriptionEntry`. /// /// Fully parsing this FDE requires first parsing its CIE. #[derive(Clone, Debug, PartialEq, Eq)] pub struct PartialFrameDescriptionEntry<'bases, Section, R> where R: Reader, Section: UnwindSection, { offset: R::Offset, length: R::Offset, format: Format, cie_offset: Section::Offset, rest: R, section: Section, bases: &'bases BaseAddresses, } impl<'bases, Section, R> PartialFrameDescriptionEntry<'bases, Section, R> where R: Reader, Section: UnwindSection, { fn parse_partial( section: &Section, bases: &'bases BaseAddresses, input: &mut R, ) -> Result> { match parse_cfi_entry(bases, section, input)? { Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), Some(CieOrFde::Fde(partial)) => Ok(partial), None => Err(Error::NoEntryAtGivenOffset), } } /// Fully parse this FDE. /// /// You must provide a function get its associated CIE (either by parsing it /// on demand, or looking it up in some table mapping offsets to CIEs that /// you've already parsed, etc.) pub fn parse(&self, get_cie: F) -> Result> where F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result>, { FrameDescriptionEntry::parse_rest( self.offset, self.length, self.format, self.cie_offset, self.rest.clone(), &self.section, self.bases, get_cie, ) } /// Get the offset of this entry from the start of its containing section. pub fn offset(&self) -> R::Offset { self.offset } /// Get the offset of this FDE's CIE. pub fn cie_offset(&self) -> Section::Offset { self.cie_offset } /// > A constant that gives the number of bytes of the header and /// > instruction stream for this function, not including the length field /// > itself (see Section 7.2.2). The size of the length field plus the value /// > of length must be an integral multiple of the address size. pub fn entry_len(&self) -> R::Offset { self.length } } /// A `FrameDescriptionEntry` is a set of CFA instructions for an address range. #[derive(Clone, Debug, PartialEq, Eq)] pub struct FrameDescriptionEntry::Offset> where R: Reader, Offset: ReaderOffset, { /// The start of this entry within its containing section. offset: Offset, /// > A constant that gives the number of bytes of the header and /// > instruction stream for this function, not including the length field /// > itself (see Section 7.2.2). The size of the length field plus the value /// > of length must be an integral multiple of the address size. length: Offset, format: Format, /// "A constant offset into the .debug_frame section that denotes the CIE /// that is associated with this FDE." /// /// This is the CIE at that offset. cie: CommonInformationEntry, /// > The address of the first location associated with this table entry. If /// > the segment_size field of this FDE's CIE is non-zero, the initial /// > location is preceded by a segment selector of the given length. initial_address: u64, /// "The number of bytes of program instructions described by this entry." address_range: u64, /// The parsed augmentation data, if we have any. augmentation: Option, /// "A sequence of table defining instructions that are described below." /// /// This is followed by `DW_CFA_nop` padding until `length` bytes of the /// input are consumed. instructions: R, } impl FrameDescriptionEntry { fn parse_rest( offset: R::Offset, length: R::Offset, format: Format, cie_pointer: Section::Offset, mut rest: R, section: &Section, bases: &BaseAddresses, mut get_cie: F, ) -> Result> where Section: UnwindSection, F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result>, { let cie = get_cie(section, bases, cie_pointer)?; let mut parameters = PointerEncodingParameters { bases: &bases.eh_frame, func_base: None, address_size: cie.address_size, section: section.section(), }; let (initial_address, address_range) = Self::parse_addresses(&mut rest, &cie, ¶meters)?; parameters.func_base = Some(initial_address); let aug_data = if let Some(ref augmentation) = cie.augmentation { Some(AugmentationData::parse( augmentation, ¶meters, &mut rest, )?) } else { None }; let entry = FrameDescriptionEntry { offset, length, format, cie, initial_address, address_range, augmentation: aug_data, instructions: rest, }; Ok(entry) } fn parse_addresses( input: &mut R, cie: &CommonInformationEntry, parameters: &PointerEncodingParameters<'_, R>, ) -> Result<(u64, u64)> { let encoding = cie.augmentation().and_then(|a| a.fde_address_encoding); if let Some(encoding) = encoding { // Ignore indirection. let initial_address = parse_encoded_pointer(encoding, parameters, input)?.pointer(); let address_range = parse_encoded_value(encoding, parameters, input)?; Ok((initial_address, address_range)) } else { let initial_address = input.read_address(cie.address_size)?; let address_range = input.read_address(cie.address_size)?; Ok((initial_address, address_range)) } } /// Return the table of unwind information for this FDE. #[inline] pub fn rows<'a, 'ctx, Section, S>( &self, section: &'a Section, bases: &'a BaseAddresses, ctx: &'ctx mut UnwindContext, ) -> Result> where Section: UnwindSection, S: UnwindContextStorage, { UnwindTable::new(section, bases, ctx, self) } /// Find the frame unwind information for the given address. /// /// If found, the unwind information is returned along with the reset /// context in the form `Ok((unwind_info, context))`. If not found, /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or /// CFI evaluation fails, the error is returned. pub fn unwind_info_for_address<'ctx, Section, S>( &self, section: &Section, bases: &BaseAddresses, ctx: &'ctx mut UnwindContext, address: u64, ) -> Result<&'ctx UnwindTableRow> where Section: UnwindSection, S: UnwindContextStorage, { let mut table = self.rows(section, bases, ctx)?; while let Some(row) = table.next_row()? { if row.contains(address) { return Ok(table.ctx.row()); } } Err(Error::NoUnwindInfoForAddress) } } /// # Signal Safe Methods /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations. #[allow(clippy::len_without_is_empty)] impl FrameDescriptionEntry { /// Get the offset of this entry from the start of its containing section. pub fn offset(&self) -> R::Offset { self.offset } /// Get a reference to this FDE's CIE. pub fn cie(&self) -> &CommonInformationEntry { &self.cie } /// > A constant that gives the number of bytes of the header and /// > instruction stream for this function, not including the length field /// > itself (see Section 7.2.2). The size of the length field plus the value /// > of length must be an integral multiple of the address size. pub fn entry_len(&self) -> R::Offset { self.length } /// Iterate over this FDE's instructions. /// /// Will not include the CIE's initial instructions, if you want those do /// `fde.cie().instructions()` first. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn instructions<'a, Section>( &self, section: &'a Section, bases: &'a BaseAddresses, ) -> CallFrameInstructionIter<'a, R> where Section: UnwindSection, { CallFrameInstructionIter { input: self.instructions.clone(), address_encoding: self.cie.augmentation().and_then(|a| a.fde_address_encoding), parameters: PointerEncodingParameters { bases: &bases.eh_frame, func_base: None, address_size: self.cie.address_size, section: section.section(), }, vendor: section.vendor(), } } /// The first address for which this entry has unwind information for. pub fn initial_address(&self) -> u64 { self.initial_address } /// One more than the last address that this entry has unwind information for. /// /// This uses wrapping arithmetic, so the result may be less than /// `initial_address`. pub fn end_address(&self) -> u64 { self.initial_address .wrapping_add_sized(self.address_range, self.cie.address_size) } /// The number of bytes of instructions that this entry has unwind /// information for. pub fn len(&self) -> u64 { self.address_range } /// Return `true` if the given address is within this FDE, `false` /// otherwise. /// /// This is equivalent to `entry.initial_address() <= address < /// entry.initial_address() + entry.len()`. pub fn contains(&self, address: u64) -> bool { self.initial_address() <= address && address < self.end_address() } /// The address of this FDE's language-specific data area (LSDA), if it has /// any. pub fn lsda(&self) -> Option { self.augmentation.as_ref().and_then(|a| a.lsda) } /// Return true if this FDE's function is a trampoline for a signal handler. #[inline] pub fn is_signal_trampoline(&self) -> bool { self.cie().is_signal_trampoline() } /// Return the address of the FDE's function's personality routine /// handler. The personality routine does language-specific clean up when /// unwinding the stack frames with the intent to not run them again. #[inline] pub fn personality(&self) -> Option { self.cie().personality() } } /// Specification of what storage should be used for [`UnwindContext`]. /// #[cfg_attr( feature = "read", doc = " Normally you would only need to use [`StoreOnHeap`], which places the stack on the heap using [`Box`]. This is the default storage type parameter for [`UnwindContext`]. You may want to supply your own storage type for one of the following reasons: 1. In rare cases you may run into failed unwinds due to the fixed stack size used by [`StoreOnHeap`], so you may want to try a larger `Box`. If denial of service is not a concern, then you could also try a `Vec`-based stack which can grow as needed. 2. You may want to avoid heap allocations entirely. You can use a fixed-size stack with in-line arrays, which will place the entire storage in-line into [`UnwindContext`]. " )] /// /// Here's an implementation which uses a fixed-size stack and allocates everything in-line, /// which will cause `UnwindContext` to be large: /// /// ```rust,no_run /// # use gimli::*; /// # /// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry>) /// # -> gimli::Result<()> { /// # let eh_frame: gimli::EhFrame<_> = unreachable!(); /// # let bases = unimplemented!(); /// # /// struct StoreOnStack; /// /// impl UnwindContextStorage for StoreOnStack { /// type Rules = [(Register, RegisterRule); 192]; /// type Stack = [UnwindTableRow; 4]; /// } /// /// let mut ctx = UnwindContext::<_, StoreOnStack>::new_in(); /// /// // Initialize the context by evaluating the CIE's initial instruction program, /// // and generate the unwind table. /// let mut table = some_fde.rows(&eh_frame, &bases, &mut ctx)?; /// while let Some(row) = table.next_row()? { /// // Do stuff with each row... /// # let _ = row; /// } /// # unreachable!() /// # } /// ``` pub trait UnwindContextStorage: Sized { /// The storage used for register rules in a unwind table row. /// /// Note that this is nested within the stack. type Rules: ArrayLike)>; /// The storage used for unwind table row stack. type Stack: ArrayLike>; } #[cfg(feature = "read")] const MAX_RULES: usize = 192; #[cfg(feature = "read")] const MAX_UNWIND_STACK_DEPTH: usize = 4; #[cfg(feature = "read")] impl UnwindContextStorage for StoreOnHeap { type Rules = [(Register, RegisterRule); MAX_RULES]; type Stack = Box<[UnwindTableRow; MAX_UNWIND_STACK_DEPTH]>; } /// Common context needed when evaluating the call frame unwinding information. /// /// By default, this structure is small and allocates its internal storage /// on the heap using [`Box`] during [`UnwindContext::new`]. /// /// This can be overridden by providing a custom [`UnwindContextStorage`] type parameter. /// When using a custom storage with in-line arrays, the [`UnwindContext`] type itself /// will be big, so in that case it's recommended to place [`UnwindContext`] on the /// heap, e.g. using `Box::new(UnwindContext::::new_in())`. /// /// To avoid re-allocating the context multiple times when evaluating multiple /// CFI programs, the same [`UnwindContext`] can be reused for multiple unwinds. /// /// ``` /// use gimli::{UnwindContext, UnwindTable}; /// /// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry>) /// # -> gimli::Result<()> { /// # let eh_frame: gimli::EhFrame<_> = unreachable!(); /// # let bases = unimplemented!(); /// // An uninitialized context. /// let mut ctx = UnwindContext::new(); /// /// // Initialize the context by evaluating the CIE's initial instruction program, /// // and generate the unwind table. /// let mut table = some_fde.rows(&eh_frame, &bases, &mut ctx)?; /// while let Some(row) = table.next_row()? { /// // Do stuff with each row... /// # let _ = row; /// } /// # unreachable!() /// # } /// ``` #[derive(Clone, PartialEq, Eq)] pub struct UnwindContext where T: ReaderOffset, S: UnwindContextStorage, { // Stack of rows. The last row is the row currently being built by the // program. There is always at least one row. The vast majority of CFI // programs will only ever have one row on the stack. stack: ArrayVec, // If we are evaluating an FDE's instructions, then `is_initialized` will be // `true`. If `initial_rule` is `Some`, then the initial register rules are either // all default rules or have just 1 non-default rule, stored in `initial_rule`. // If it's `None`, `stack[0]` will contain the initial register rules // described by the CIE's initial instructions. These rules are used by // `DW_CFA_restore`. Otherwise, when we are currently evaluating a CIE's // initial instructions, `is_initialized` will be `false` and initial rules // cannot be read. initial_rule: Option<(Register, RegisterRule)>, is_initialized: bool, } impl Debug for UnwindContext where T: ReaderOffset, S: UnwindContextStorage, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("UnwindContext") .field("stack", &self.stack) .field("initial_rule", &self.initial_rule) .field("is_initialized", &self.is_initialized) .finish() } } impl Default for UnwindContext where T: ReaderOffset, S: UnwindContextStorage, { fn default() -> Self { Self::new_in() } } #[cfg(feature = "read")] impl UnwindContext { /// Construct a new call frame unwinding context. pub fn new() -> Self { Self::new_in() } } /// # Signal Safe Methods /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations, if an non-allocating storage is used. impl UnwindContext where T: ReaderOffset, S: UnwindContextStorage, { /// Construct a new call frame unwinding context. pub fn new_in() -> Self { let mut ctx = UnwindContext { stack: Default::default(), initial_rule: None, is_initialized: false, }; ctx.reset(); ctx } /// Run the CIE's initial instructions and initialize this `UnwindContext`. fn initialize( &mut self, section: &Section, bases: &BaseAddresses, cie: &CommonInformationEntry, ) -> Result<()> where R: Reader, Section: UnwindSection, { // Always reset because previous initialization failure may leave dirty state. self.reset(); let mut table = UnwindTable::new_for_cie(section, bases, self, cie); while table.next_row()?.is_some() {} self.save_initial_rules()?; Ok(()) } fn reset(&mut self) { self.stack.clear(); self.stack.try_push(UnwindTableRow::default()).unwrap(); debug_assert!(self.stack[0].is_default()); self.initial_rule = None; self.is_initialized = false; } fn row(&self) -> &UnwindTableRow { self.stack.last().unwrap() } fn row_mut(&mut self) -> &mut UnwindTableRow { self.stack.last_mut().unwrap() } fn save_initial_rules(&mut self) -> Result<()> { debug_assert!(!self.is_initialized); self.initial_rule = match *self.stack.last().unwrap().registers.rules { // All rules are default (undefined). In this case just synthesize // an undefined rule. [] => Some((Register(0), RegisterRule::Undefined)), [ref rule] => Some(rule.clone()), _ => { let rules = self.stack.last().unwrap().clone(); self.stack .try_insert(0, rules) .map_err(|_| Error::StackFull)?; None } }; self.is_initialized = true; Ok(()) } fn start_address(&self) -> u64 { self.row().start_address } fn set_start_address(&mut self, start_address: u64) { let row = self.row_mut(); row.start_address = start_address; } fn set_register_rule(&mut self, register: Register, rule: RegisterRule) -> Result<()> { let row = self.row_mut(); row.registers.set(register, rule) } /// Returns `None` if we have not completed evaluation of a CIE's initial /// instructions. fn get_initial_rule(&self, register: Register) -> Option> { if !self.is_initialized { return None; } Some(match self.initial_rule { None => self.stack[0].registers.get(register), Some((r, ref rule)) if r == register => rule.clone(), _ => RegisterRule::Undefined, }) } fn set_cfa(&mut self, cfa: CfaRule) { self.row_mut().cfa = cfa; } fn cfa_mut(&mut self) -> &mut CfaRule { &mut self.row_mut().cfa } fn push_row(&mut self) -> Result<()> { let new_row = self.row().clone(); self.stack.try_push(new_row).map_err(|_| Error::StackFull) } fn pop_row(&mut self) -> Result<()> { let min_size = if self.is_initialized && self.initial_rule.is_none() { 2 } else { 1 }; if self.stack.len() <= min_size { return Err(Error::PopWithEmptyStack); } self.stack.pop().unwrap(); Ok(()) } } /// The `UnwindTable` iteratively evaluates a `FrameDescriptionEntry`'s /// `CallFrameInstruction` program, yielding the each row one at a time. /// /// > 6.4.1 Structure of Call Frame Information /// > /// > DWARF supports virtual unwinding by defining an architecture independent /// > basis for recording how procedures save and restore registers during their /// > lifetimes. This basis must be augmented on some machines with specific /// > information that is defined by an architecture specific ABI authoring /// > committee, a hardware vendor, or a compiler producer. The body defining a /// > specific augmentation is referred to below as the “augmenter.” /// > /// > Abstractly, this mechanism describes a very large table that has the /// > following structure: /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// >
LOCCFAR0R1...RN
L0
L1
...
LN
/// > /// > The first column indicates an address for every location that contains code /// > in a program. (In shared objects, this is an object-relative offset.) The /// > remaining columns contain virtual unwinding rules that are associated with /// > the indicated location. /// > /// > The CFA column defines the rule which computes the Canonical Frame Address /// > value; it may be either a register and a signed offset that are added /// > together, or a DWARF expression that is evaluated. /// > /// > The remaining columns are labeled by register number. This includes some /// > registers that have special designation on some architectures such as the PC /// > and the stack pointer register. (The actual mapping of registers for a /// > particular architecture is defined by the augmenter.) The register columns /// > contain rules that describe whether a given register has been saved and the /// > rule to find the value for the register in the previous frame. /// > /// > ... /// > /// > This table would be extremely large if actually constructed as /// > described. Most of the entries at any point in the table are identical to /// > the ones above them. The whole table can be represented quite compactly by /// > recording just the differences starting at the beginning address of each /// > subroutine in the program. #[derive(Debug)] pub struct UnwindTable<'a, 'ctx, R, S = StoreOnHeap> where R: Reader, S: UnwindContextStorage, { code_alignment_factor: Wrapping, data_alignment_factor: Wrapping, address_size: u8, next_start_address: u64, last_end_address: u64, returned_last_row: bool, current_row_valid: bool, instructions: CallFrameInstructionIter<'a, R>, ctx: &'ctx mut UnwindContext, } /// # Signal Safe Methods /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations. impl<'a, 'ctx, R, S> UnwindTable<'a, 'ctx, R, S> where R: Reader, S: UnwindContextStorage, { /// Construct a new `UnwindTable` for the given /// `FrameDescriptionEntry`'s CFI unwinding program. pub fn new>( section: &'a Section, bases: &'a BaseAddresses, ctx: &'ctx mut UnwindContext, fde: &FrameDescriptionEntry, ) -> Result { ctx.initialize(section, bases, fde.cie())?; Ok(Self::new_for_fde(section, bases, ctx, fde)) } fn new_for_fde>( section: &'a Section, bases: &'a BaseAddresses, ctx: &'ctx mut UnwindContext, fde: &FrameDescriptionEntry, ) -> Self { assert!(ctx.stack.len() >= 1); UnwindTable { code_alignment_factor: Wrapping(fde.cie().code_alignment_factor()), data_alignment_factor: Wrapping(fde.cie().data_alignment_factor()), address_size: fde.cie().address_size, next_start_address: fde.initial_address(), last_end_address: fde.end_address(), returned_last_row: false, current_row_valid: false, instructions: fde.instructions(section, bases), ctx, } } fn new_for_cie>( section: &'a Section, bases: &'a BaseAddresses, ctx: &'ctx mut UnwindContext, cie: &CommonInformationEntry, ) -> Self { assert!(ctx.stack.len() >= 1); UnwindTable { code_alignment_factor: Wrapping(cie.code_alignment_factor()), data_alignment_factor: Wrapping(cie.data_alignment_factor()), address_size: cie.address_size, next_start_address: 0, last_end_address: 0, returned_last_row: false, current_row_valid: false, instructions: cie.instructions(section, bases), ctx, } } /// Evaluate call frame instructions until the next row of the table is /// completed, and return it. /// /// Unfortunately, this cannot be used with `FallibleIterator` because of /// the restricted lifetime of the yielded item. pub fn next_row(&mut self) -> Result>> { assert!(self.ctx.stack.len() >= 1); self.ctx.set_start_address(self.next_start_address); self.current_row_valid = false; loop { match self.instructions.next() { Err(e) => return Err(e), Ok(None) => { if self.returned_last_row { return Ok(None); } let row = self.ctx.row_mut(); row.end_address = self.last_end_address; self.returned_last_row = true; self.current_row_valid = true; return Ok(Some(row)); } Ok(Some(instruction)) => { if self.evaluate(instruction)? { self.current_row_valid = true; return Ok(Some(self.ctx.row())); } } }; } } /// Returns the current row with the lifetime of the context. pub fn into_current_row(self) -> Option<&'ctx UnwindTableRow> { if self.current_row_valid { Some(self.ctx.row()) } else { None } } /// Evaluate one call frame instruction. Return `Ok(true)` if the row is /// complete, `Ok(false)` otherwise. fn evaluate(&mut self, instruction: CallFrameInstruction) -> Result { use crate::CallFrameInstruction::*; match instruction { // Instructions that complete the current row and advance the // address for the next row. SetLoc { address } => { if address < self.ctx.start_address() { return Err(Error::InvalidAddressRange); } self.next_start_address = address; self.ctx.row_mut().end_address = self.next_start_address; return Ok(true); } AdvanceLoc { delta } => { let delta = Wrapping(u64::from(delta)) * self.code_alignment_factor; self.next_start_address = self .ctx .start_address() .add_sized(delta.0, self.address_size)?; self.ctx.row_mut().end_address = self.next_start_address; return Ok(true); } // Instructions that modify the CFA. DefCfa { register, offset } => { self.ctx.set_cfa(CfaRule::RegisterAndOffset { register, offset: offset as i64, }); } DefCfaSf { register, factored_offset, } => { let data_align = self.data_alignment_factor; self.ctx.set_cfa(CfaRule::RegisterAndOffset { register, offset: (Wrapping(factored_offset) * data_align).0, }); } DefCfaRegister { register } => { if let CfaRule::RegisterAndOffset { register: ref mut reg, .. } = *self.ctx.cfa_mut() { *reg = register; } else { return Err(Error::CfiInstructionInInvalidContext); } } DefCfaOffset { offset } => { if let CfaRule::RegisterAndOffset { offset: ref mut off, .. } = *self.ctx.cfa_mut() { *off = offset as i64; } else { return Err(Error::CfiInstructionInInvalidContext); } } DefCfaOffsetSf { factored_offset } => { if let CfaRule::RegisterAndOffset { offset: ref mut off, .. } = *self.ctx.cfa_mut() { let data_align = self.data_alignment_factor; *off = (Wrapping(factored_offset) * data_align).0; } else { return Err(Error::CfiInstructionInInvalidContext); } } DefCfaExpression { expression } => { self.ctx.set_cfa(CfaRule::Expression(expression)); } // Instructions that define register rules. Undefined { register } => { self.ctx .set_register_rule(register, RegisterRule::Undefined)?; } SameValue { register } => { self.ctx .set_register_rule(register, RegisterRule::SameValue)?; } Offset { register, factored_offset, } => { let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; self.ctx .set_register_rule(register, RegisterRule::Offset(offset.0))?; } OffsetExtendedSf { register, factored_offset, } => { let offset = Wrapping(factored_offset) * self.data_alignment_factor; self.ctx .set_register_rule(register, RegisterRule::Offset(offset.0))?; } ValOffset { register, factored_offset, } => { let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; self.ctx .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; } ValOffsetSf { register, factored_offset, } => { let offset = Wrapping(factored_offset) * self.data_alignment_factor; self.ctx .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; } Register { dest_register, src_register, } => { self.ctx .set_register_rule(dest_register, RegisterRule::Register(src_register))?; } Expression { register, expression, } => { let expression = RegisterRule::Expression(expression); self.ctx.set_register_rule(register, expression)?; } ValExpression { register, expression, } => { let expression = RegisterRule::ValExpression(expression); self.ctx.set_register_rule(register, expression)?; } Restore { register } => { let initial_rule = if let Some(rule) = self.ctx.get_initial_rule(register) { rule } else { // Can't restore the initial rule when we are // evaluating the initial rules! return Err(Error::CfiInstructionInInvalidContext); }; self.ctx.set_register_rule(register, initial_rule)?; } // Row push and pop instructions. RememberState => { self.ctx.push_row()?; } RestoreState => { // Pop state while preserving current location. let start_address = self.ctx.start_address(); self.ctx.pop_row()?; self.ctx.set_start_address(start_address); } // GNU Extension. Save the size somewhere so the unwinder can use // it when restoring IP ArgsSize { size } => { self.ctx.row_mut().saved_args_size = size; } // AArch64 extension. NegateRaState => { let register = crate::AArch64::RA_SIGN_STATE; let value = match self.ctx.row().register(register) { RegisterRule::Undefined => 0, RegisterRule::Constant(value) => value, _ => return Err(Error::CfiInstructionInInvalidContext), }; self.ctx .set_register_rule(register, RegisterRule::Constant(value ^ 1))?; } // No operation. Nop => {} }; Ok(false) } } // We tend to have very few register rules: usually only a couple. Even if we // have a rule for every register, on x86-64 with SSE and everything we're // talking about ~100 rules. So rather than keeping the rules in a hash map, or // a vector indexed by register number (which would lead to filling lots of // empty entries), we store them as a vec of (register number, register rule) // pairs. // // Additionally, because every register's default rule is implicitly // `RegisterRule::Undefined`, we never store a register's rule in this vec if it // is undefined and save a little bit more space and do a little fewer // comparisons that way. // // The maximum number of rules preallocated by libunwind is 97 for AArch64, 128 // for ARM, and even 188 for MIPS. It is extremely unlikely to encounter this // many register rules in practice. // // See: // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-x86_64/dwarf-config.h#L36 // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-aarch64/dwarf-config.h#L32 // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-arm/dwarf-config.h#L31 // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-mips/dwarf-config.h#L31 struct RegisterRuleMap where T: ReaderOffset, S: UnwindContextStorage, { rules: ArrayVec, } impl Debug for RegisterRuleMap where T: ReaderOffset, S: UnwindContextStorage, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RegisterRuleMap") .field("rules", &self.rules) .finish() } } impl Clone for RegisterRuleMap where T: ReaderOffset, S: UnwindContextStorage, { fn clone(&self) -> Self { Self { rules: self.rules.clone(), } } } impl Default for RegisterRuleMap where T: ReaderOffset, S: UnwindContextStorage, { fn default() -> Self { RegisterRuleMap { rules: Default::default(), } } } /// # Signal Safe Methods /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations. impl RegisterRuleMap where T: ReaderOffset, S: UnwindContextStorage, { fn is_default(&self) -> bool { self.rules.is_empty() } fn get(&self, register: Register) -> RegisterRule { self.rules .iter() .find(|rule| rule.0 == register) .map(|r| { debug_assert!(r.1.is_defined()); r.1.clone() }) .unwrap_or(RegisterRule::Undefined) } fn set(&mut self, register: Register, rule: RegisterRule) -> Result<()> { if !rule.is_defined() { let idx = self .rules .iter() .enumerate() .find(|&(_, r)| r.0 == register) .map(|(i, _)| i); if let Some(idx) = idx { self.rules.swap_remove(idx); } return Ok(()); } for &mut (reg, ref mut old_rule) in &mut *self.rules { debug_assert!(old_rule.is_defined()); if reg == register { *old_rule = rule; return Ok(()); } } self.rules .try_push((register, rule)) .map_err(|_| Error::TooManyRegisterRules) } fn iter(&self) -> RegisterRuleIter<'_, T> { RegisterRuleIter(self.rules.iter()) } } impl<'a, R, S> FromIterator<&'a (Register, RegisterRule)> for RegisterRuleMap where R: 'a + ReaderOffset, S: UnwindContextStorage, { fn from_iter(iter: T) -> Self where T: IntoIterator)>, { let iter = iter.into_iter(); let mut rules = RegisterRuleMap::default(); for &(reg, ref rule) in iter.filter(|r| r.1.is_defined()) { rules.set(reg, rule.clone()).expect( "This is only used in tests, impl isn't exposed publicly. If you trip this, fix your test", ); } rules } } impl PartialEq for RegisterRuleMap where T: ReaderOffset + PartialEq, S: UnwindContextStorage, { fn eq(&self, rhs: &Self) -> bool { for &(reg, ref rule) in &*self.rules { debug_assert!(rule.is_defined()); if *rule != rhs.get(reg) { return false; } } for &(reg, ref rhs_rule) in &*rhs.rules { debug_assert!(rhs_rule.is_defined()); if *rhs_rule != self.get(reg) { return false; } } true } } impl Eq for RegisterRuleMap where T: ReaderOffset + Eq, S: UnwindContextStorage, { } /// An unordered iterator for register rules. #[derive(Debug, Clone)] pub struct RegisterRuleIter<'iter, T>(::core::slice::Iter<'iter, (Register, RegisterRule)>) where T: ReaderOffset; impl<'iter, T: ReaderOffset> Iterator for RegisterRuleIter<'iter, T> { type Item = &'iter (Register, RegisterRule); fn next(&mut self) -> Option { self.0.next() } } /// A row in the virtual unwind table that describes how to find the values of /// the registers in the *previous* frame for a range of PC addresses. #[derive(PartialEq, Eq)] pub struct UnwindTableRow where T: ReaderOffset, S: UnwindContextStorage, { start_address: u64, end_address: u64, saved_args_size: u64, cfa: CfaRule, registers: RegisterRuleMap, } impl Debug for UnwindTableRow where T: ReaderOffset, S: UnwindContextStorage, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("UnwindTableRow") .field("start_address", &self.start_address) .field("end_address", &self.end_address) .field("saved_args_size", &self.saved_args_size) .field("cfa", &self.cfa) .field("registers", &self.registers) .finish() } } impl Clone for UnwindTableRow where T: ReaderOffset, S: UnwindContextStorage, { fn clone(&self) -> Self { Self { start_address: self.start_address, end_address: self.end_address, saved_args_size: self.saved_args_size, cfa: self.cfa.clone(), registers: self.registers.clone(), } } } impl Default for UnwindTableRow where T: ReaderOffset, S: UnwindContextStorage, { fn default() -> Self { UnwindTableRow { start_address: 0, end_address: 0, saved_args_size: 0, cfa: Default::default(), registers: Default::default(), } } } impl UnwindTableRow where T: ReaderOffset, S: UnwindContextStorage, { fn is_default(&self) -> bool { self.start_address == 0 && self.end_address == 0 && self.cfa.is_default() && self.registers.is_default() } /// Get the starting PC address that this row applies to. pub fn start_address(&self) -> u64 { self.start_address } /// Get the end PC address where this row's register rules become /// unapplicable. /// /// In other words, this row describes how to recover the last frame's /// registers for all PCs where `row.start_address() <= PC < /// row.end_address()`. This row does NOT describe how to recover registers /// when `PC == row.end_address()`. pub fn end_address(&self) -> u64 { self.end_address } /// Return `true` if the given `address` is within this row's address range, /// `false` otherwise. pub fn contains(&self, address: u64) -> bool { self.start_address <= address && address < self.end_address } /// Returns the amount of args currently on the stack. /// /// When unwinding, if the personality function requested a change in IP, /// the SP needs to be adjusted by saved_args_size. pub fn saved_args_size(&self) -> u64 { self.saved_args_size } /// Get the canonical frame address (CFA) recovery rule for this row. pub fn cfa(&self) -> &CfaRule { &self.cfa } /// Get the register recovery rule for the given register number. /// /// The register number mapping is architecture dependent. For example, in /// the x86-64 ABI the register number mapping is defined in Figure 3.36: /// /// > Figure 3.36: DWARF Register Number Mapping /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// > /// >
Register Name Number Abbreviation
General Purpose Register RAX 0 %rax
General Purpose Register RDX 1 %rdx
General Purpose Register RCX 2 %rcx
General Purpose Register RBX 3 %rbx
General Purpose Register RSI 4 %rsi
General Purpose Register RDI 5 %rdi
General Purpose Register RBP 6 %rbp
Stack Pointer Register RSP 7 %rsp
Extended Integer Registers 8-15 8-15 %r8-%r15
Return Address RA 16
Vector Registers 0–7 17-24 %xmm0–%xmm7
Extended Vector Registers 8–15 25-32 %xmm8–%xmm15
Floating Point Registers 0–7 33-40 %st0–%st7
MMX Registers 0–7 41-48 %mm0–%mm7
Flag Register 49 %rFLAGS
Segment Register ES 50 %es
Segment Register CS 51 %cs
Segment Register SS 52 %ss
Segment Register DS 53 %ds
Segment Register FS 54 %fs
Segment Register GS 55 %gs
Reserved 56-57
FS Base address 58 %fs.base
GS Base address 59 %gs.base
Reserved 60-61
Task Register 62 %tr
LDT Register 63 %ldtr
128-bit Media Control and Status 64 %mxcsr
x87 Control Word 65 %fcw
x87 Status Word 66 %fsw
Upper Vector Registers 16–31 67-82 %xmm16–%xmm31
Reserved 83-117
Vector Mask Registers 0–7 118-125 %k0–%k7
Reserved 126-129
pub fn register(&self, register: Register) -> RegisterRule { self.registers.get(register) } /// Iterate over all defined register `(number, rule)` pairs. /// /// The rules are not iterated in any guaranteed order. Any register that /// does not make an appearance in the iterator implicitly has the rule /// `RegisterRule::Undefined`. /// /// ``` /// # use gimli::{EndianSlice, LittleEndian, UnwindTableRow}; /// # fn foo<'input>(unwind_table_row: UnwindTableRow) { /// for &(register, ref rule) in unwind_table_row.registers() { /// // ... /// # drop(register); drop(rule); /// } /// # } /// ``` pub fn registers(&self) -> RegisterRuleIter<'_, T> { self.registers.iter() } } /// The canonical frame address (CFA) recovery rules. #[derive(Clone, Debug, PartialEq, Eq)] pub enum CfaRule { /// The CFA is given offset from the given register's value. RegisterAndOffset { /// The register containing the base value. register: Register, /// The offset from the register's base value. offset: i64, }, /// The CFA is obtained by evaluating a DWARF expression program. Expression(UnwindExpression), } impl Default for CfaRule { fn default() -> Self { CfaRule::RegisterAndOffset { register: Register(0), offset: 0, } } } impl CfaRule { fn is_default(&self) -> bool { match *self { CfaRule::RegisterAndOffset { register, offset } => { register == Register(0) && offset == 0 } _ => false, } } } /// An entry in the abstract CFI table that describes how to find the value of a /// register. /// /// "The register columns contain rules that describe whether a given register /// has been saved and the rule to find the value for the register in the /// previous frame." #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum RegisterRule { /// > A register that has this rule has no recoverable value in the previous /// > frame. (By convention, it is not preserved by a callee.) Undefined, /// > This register has not been modified from the previous frame. (By /// > convention, it is preserved by the callee, but the callee has not /// > modified it.) SameValue, /// "The previous value of this register is saved at the address CFA+N where /// CFA is the current CFA value and N is a signed offset." Offset(i64), /// "The previous value of this register is the value CFA+N where CFA is the /// current CFA value and N is a signed offset." ValOffset(i64), /// "The previous value of this register is stored in another register /// numbered R." Register(Register), /// "The previous value of this register is located at the address produced /// by executing the DWARF expression." Expression(UnwindExpression), /// "The previous value of this register is the value produced by executing /// the DWARF expression." ValExpression(UnwindExpression), /// "The rule is defined externally to this specification by the augmenter." Architectural, /// This is a pseudo-register with a constant value. Constant(u64), } impl RegisterRule { fn is_defined(&self) -> bool { !matches!(*self, RegisterRule::Undefined) } } /// A parsed call frame instruction. #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum CallFrameInstruction { // 6.4.2.1 Row Creation Methods /// > 1. DW_CFA_set_loc /// > /// > The DW_CFA_set_loc instruction takes a single operand that represents /// > a target address. The required action is to create a new table row /// > using the specified address as the location. All other values in the /// > new row are initially identical to the current row. The new location /// > value is always greater than the current one. If the segment_size /// > field of this FDE's CIE is non- zero, the initial location is preceded /// > by a segment selector of the given length. SetLoc { /// The target address. address: u64, }, /// The `AdvanceLoc` instruction is used for all of `DW_CFA_advance_loc` and /// `DW_CFA_advance_loc{1,2,4}`. /// /// > 2. DW_CFA_advance_loc /// > /// > The DW_CFA_advance instruction takes a single operand (encoded with /// > the opcode) that represents a constant delta. The required action is /// > to create a new table row with a location value that is computed by /// > taking the current entry’s location value and adding the value of /// > delta * code_alignment_factor. All other values in the new row are /// > initially identical to the current row. AdvanceLoc { /// The delta to be added to the current address. delta: u32, }, // 6.4.2.2 CFA Definition Methods /// > 1. DW_CFA_def_cfa /// > /// > The DW_CFA_def_cfa instruction takes two unsigned LEB128 operands /// > representing a register number and a (non-factored) offset. The /// > required action is to define the current CFA rule to use the provided /// > register and offset. DefCfa { /// The target register's number. register: Register, /// The non-factored offset. offset: u64, }, /// > 2. DW_CFA_def_cfa_sf /// > /// > The DW_CFA_def_cfa_sf instruction takes two operands: an unsigned /// > LEB128 value representing a register number and a signed LEB128 /// > factored offset. This instruction is identical to DW_CFA_def_cfa /// > except that the second operand is signed and factored. The resulting /// > offset is factored_offset * data_alignment_factor. DefCfaSf { /// The target register's number. register: Register, /// The factored offset. factored_offset: i64, }, /// > 3. DW_CFA_def_cfa_register /// > /// > The DW_CFA_def_cfa_register instruction takes a single unsigned LEB128 /// > operand representing a register number. The required action is to /// > define the current CFA rule to use the provided register (but to keep /// > the old offset). This operation is valid only if the current CFA rule /// > is defined to use a register and offset. DefCfaRegister { /// The target register's number. register: Register, }, /// > 4. DW_CFA_def_cfa_offset /// > /// > The DW_CFA_def_cfa_offset instruction takes a single unsigned LEB128 /// > operand representing a (non-factored) offset. The required action is /// > to define the current CFA rule to use the provided offset (but to keep /// > the old register). This operation is valid only if the current CFA /// > rule is defined to use a register and offset. DefCfaOffset { /// The non-factored offset. offset: u64, }, /// > 5. DW_CFA_def_cfa_offset_sf /// > /// > The DW_CFA_def_cfa_offset_sf instruction takes a signed LEB128 operand /// > representing a factored offset. This instruction is identical to /// > DW_CFA_def_cfa_offset except that the operand is signed and /// > factored. The resulting offset is factored_offset * /// > data_alignment_factor. This operation is valid only if the current CFA /// > rule is defined to use a register and offset. DefCfaOffsetSf { /// The factored offset. factored_offset: i64, }, /// > 6. DW_CFA_def_cfa_expression /// > /// > The DW_CFA_def_cfa_expression instruction takes a single operand /// > encoded as a DW_FORM_exprloc value representing a DWARF /// > expression. The required action is to establish that expression as the /// > means by which the current CFA is computed. DefCfaExpression { /// The location of the DWARF expression. expression: UnwindExpression, }, // 6.4.2.3 Register Rule Instructions /// > 1. DW_CFA_undefined /// > /// > The DW_CFA_undefined instruction takes a single unsigned LEB128 /// > operand that represents a register number. The required action is to /// > set the rule for the specified register to “undefined.” Undefined { /// The target register's number. register: Register, }, /// > 2. DW_CFA_same_value /// > /// > The DW_CFA_same_value instruction takes a single unsigned LEB128 /// > operand that represents a register number. The required action is to /// > set the rule for the specified register to “same value.” SameValue { /// The target register's number. register: Register, }, /// The `Offset` instruction represents both `DW_CFA_offset` and /// `DW_CFA_offset_extended`. /// /// > 3. DW_CFA_offset /// > /// > The DW_CFA_offset instruction takes two operands: a register number /// > (encoded with the opcode) and an unsigned LEB128 constant representing /// > a factored offset. The required action is to change the rule for the /// > register indicated by the register number to be an offset(N) rule /// > where the value of N is factored offset * data_alignment_factor. Offset { /// The target register's number. register: Register, /// The factored offset. factored_offset: u64, }, /// > 5. DW_CFA_offset_extended_sf /// > /// > The DW_CFA_offset_extended_sf instruction takes two operands: an /// > unsigned LEB128 value representing a register number and a signed /// > LEB128 factored offset. This instruction is identical to /// > DW_CFA_offset_extended except that the second operand is signed and /// > factored. The resulting offset is factored_offset * /// > data_alignment_factor. OffsetExtendedSf { /// The target register's number. register: Register, /// The factored offset. factored_offset: i64, }, /// > 6. DW_CFA_val_offset /// > /// > The DW_CFA_val_offset instruction takes two unsigned LEB128 operands /// > representing a register number and a factored offset. The required /// > action is to change the rule for the register indicated by the /// > register number to be a val_offset(N) rule where the value of N is /// > factored_offset * data_alignment_factor. ValOffset { /// The target register's number. register: Register, /// The factored offset. factored_offset: u64, }, /// > 7. DW_CFA_val_offset_sf /// > /// > The DW_CFA_val_offset_sf instruction takes two operands: an unsigned /// > LEB128 value representing a register number and a signed LEB128 /// > factored offset. This instruction is identical to DW_CFA_val_offset /// > except that the second operand is signed and factored. The resulting /// > offset is factored_offset * data_alignment_factor. ValOffsetSf { /// The target register's number. register: Register, /// The factored offset. factored_offset: i64, }, /// > 8. DW_CFA_register /// > /// > The DW_CFA_register instruction takes two unsigned LEB128 operands /// > representing register numbers. The required action is to set the rule /// > for the first register to be register(R) where R is the second /// > register. Register { /// The number of the register whose rule is being changed. dest_register: Register, /// The number of the register where the other register's value can be /// found. src_register: Register, }, /// > 9. DW_CFA_expression /// > /// > The DW_CFA_expression instruction takes two operands: an unsigned /// > LEB128 value representing a register number, and a DW_FORM_block value /// > representing a DWARF expression. The required action is to change the /// > rule for the register indicated by the register number to be an /// > expression(E) rule where E is the DWARF expression. That is, the DWARF /// > expression computes the address. The value of the CFA is pushed on the /// > DWARF evaluation stack prior to execution of the DWARF expression. Expression { /// The target register's number. register: Register, /// The location of the DWARF expression. expression: UnwindExpression, }, /// > 10. DW_CFA_val_expression /// > /// > The DW_CFA_val_expression instruction takes two operands: an unsigned /// > LEB128 value representing a register number, and a DW_FORM_block value /// > representing a DWARF expression. The required action is to change the /// > rule for the register indicated by the register number to be a /// > val_expression(E) rule where E is the DWARF expression. That is, the /// > DWARF expression computes the value of the given register. The value /// > of the CFA is pushed on the DWARF evaluation stack prior to execution /// > of the DWARF expression. ValExpression { /// The target register's number. register: Register, /// The location of the DWARF expression. expression: UnwindExpression, }, /// The `Restore` instruction represents both `DW_CFA_restore` and /// `DW_CFA_restore_extended`. /// /// > 11. DW_CFA_restore /// > /// > The DW_CFA_restore instruction takes a single operand (encoded with /// > the opcode) that represents a register number. The required action is /// > to change the rule for the indicated register to the rule assigned it /// > by the initial_instructions in the CIE. Restore { /// The register to be reset. register: Register, }, // 6.4.2.4 Row State Instructions /// > 1. DW_CFA_remember_state /// > /// > The DW_CFA_remember_state instruction takes no operands. The required /// > action is to push the set of rules for every register onto an implicit /// > stack. RememberState, /// > 2. DW_CFA_restore_state /// > /// > The DW_CFA_restore_state instruction takes no operands. The required /// > action is to pop the set of rules off the implicit stack and place /// > them in the current row. RestoreState, /// > DW_CFA_GNU_args_size /// > /// > GNU Extension /// > /// > The DW_CFA_GNU_args_size instruction takes an unsigned LEB128 operand /// > representing an argument size. This instruction specifies the total of /// > the size of the arguments which have been pushed onto the stack. ArgsSize { /// The size of the arguments which have been pushed onto the stack size: u64, }, /// > DW_CFA_AARCH64_negate_ra_state /// > /// > AArch64 Extension /// > /// > The DW_CFA_AARCH64_negate_ra_state operation negates bit 0 of the /// > RA_SIGN_STATE pseudo-register. It does not take any operands. The /// > DW_CFA_AARCH64_negate_ra_state must not be mixed with other DWARF Register /// > Rule Instructions on the RA_SIGN_STATE pseudo-register in one Common /// > Information Entry (CIE) and Frame Descriptor Entry (FDE) program sequence. NegateRaState, // 6.4.2.5 Padding Instruction /// > 1. DW_CFA_nop /// > /// > The DW_CFA_nop instruction has no operands and no required actions. It /// > is used as padding to make a CIE or FDE an appropriate size. Nop, } const CFI_INSTRUCTION_HIGH_BITS_MASK: u8 = 0b1100_0000; const CFI_INSTRUCTION_LOW_BITS_MASK: u8 = !CFI_INSTRUCTION_HIGH_BITS_MASK; impl CallFrameInstruction { fn parse>( input: &mut R, address_encoding: Option, parameters: &PointerEncodingParameters<'_, R>, vendor: Vendor, ) -> Result> { let instruction = input.read_u8()?; let high_bits = instruction & CFI_INSTRUCTION_HIGH_BITS_MASK; if high_bits == constants::DW_CFA_advance_loc.0 { let delta = instruction & CFI_INSTRUCTION_LOW_BITS_MASK; return Ok(CallFrameInstruction::AdvanceLoc { delta: u32::from(delta), }); } if high_bits == constants::DW_CFA_offset.0 { let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); let offset = input.read_uleb128()?; return Ok(CallFrameInstruction::Offset { register, factored_offset: offset, }); } if high_bits == constants::DW_CFA_restore.0 { let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); return Ok(CallFrameInstruction::Restore { register }); } debug_assert_eq!(high_bits, 0); let instruction = constants::DwCfa(instruction); match instruction { constants::DW_CFA_nop => Ok(CallFrameInstruction::Nop), constants::DW_CFA_set_loc => { let address = if let Some(encoding) = address_encoding { parse_encoded_pointer(encoding, parameters, input)?.direct()? } else { input.read_address(parameters.address_size)? }; Ok(CallFrameInstruction::SetLoc { address }) } constants::DW_CFA_advance_loc1 => { let delta = input.read_u8()?; Ok(CallFrameInstruction::AdvanceLoc { delta: u32::from(delta), }) } constants::DW_CFA_advance_loc2 => { let delta = input.read_u16()?; Ok(CallFrameInstruction::AdvanceLoc { delta: u32::from(delta), }) } constants::DW_CFA_advance_loc4 => { let delta = input.read_u32()?; Ok(CallFrameInstruction::AdvanceLoc { delta }) } constants::DW_CFA_offset_extended => { let register = input.read_uleb128().and_then(Register::from_u64)?; let offset = input.read_uleb128()?; Ok(CallFrameInstruction::Offset { register, factored_offset: offset, }) } constants::DW_CFA_restore_extended => { let register = input.read_uleb128().and_then(Register::from_u64)?; Ok(CallFrameInstruction::Restore { register }) } constants::DW_CFA_undefined => { let register = input.read_uleb128().and_then(Register::from_u64)?; Ok(CallFrameInstruction::Undefined { register }) } constants::DW_CFA_same_value => { let register = input.read_uleb128().and_then(Register::from_u64)?; Ok(CallFrameInstruction::SameValue { register }) } constants::DW_CFA_register => { let dest = input.read_uleb128().and_then(Register::from_u64)?; let src = input.read_uleb128().and_then(Register::from_u64)?; Ok(CallFrameInstruction::Register { dest_register: dest, src_register: src, }) } constants::DW_CFA_remember_state => Ok(CallFrameInstruction::RememberState), constants::DW_CFA_restore_state => Ok(CallFrameInstruction::RestoreState), constants::DW_CFA_def_cfa => { let register = input.read_uleb128().and_then(Register::from_u64)?; let offset = input.read_uleb128()?; Ok(CallFrameInstruction::DefCfa { register, offset }) } constants::DW_CFA_def_cfa_register => { let register = input.read_uleb128().and_then(Register::from_u64)?; Ok(CallFrameInstruction::DefCfaRegister { register }) } constants::DW_CFA_def_cfa_offset => { let offset = input.read_uleb128()?; Ok(CallFrameInstruction::DefCfaOffset { offset }) } constants::DW_CFA_def_cfa_expression => { let length = input.read_uleb128().and_then(R::Offset::from_u64)?; let offset = input.offset_from(parameters.section); input.skip(length)?; Ok(CallFrameInstruction::DefCfaExpression { expression: UnwindExpression { offset, length }, }) } constants::DW_CFA_expression => { let register = input.read_uleb128().and_then(Register::from_u64)?; let length = input.read_uleb128().and_then(R::Offset::from_u64)?; let offset = input.offset_from(parameters.section); input.skip(length)?; Ok(CallFrameInstruction::Expression { register, expression: UnwindExpression { offset, length }, }) } constants::DW_CFA_offset_extended_sf => { let register = input.read_uleb128().and_then(Register::from_u64)?; let offset = input.read_sleb128()?; Ok(CallFrameInstruction::OffsetExtendedSf { register, factored_offset: offset, }) } constants::DW_CFA_def_cfa_sf => { let register = input.read_uleb128().and_then(Register::from_u64)?; let offset = input.read_sleb128()?; Ok(CallFrameInstruction::DefCfaSf { register, factored_offset: offset, }) } constants::DW_CFA_def_cfa_offset_sf => { let offset = input.read_sleb128()?; Ok(CallFrameInstruction::DefCfaOffsetSf { factored_offset: offset, }) } constants::DW_CFA_val_offset => { let register = input.read_uleb128().and_then(Register::from_u64)?; let offset = input.read_uleb128()?; Ok(CallFrameInstruction::ValOffset { register, factored_offset: offset, }) } constants::DW_CFA_val_offset_sf => { let register = input.read_uleb128().and_then(Register::from_u64)?; let offset = input.read_sleb128()?; Ok(CallFrameInstruction::ValOffsetSf { register, factored_offset: offset, }) } constants::DW_CFA_val_expression => { let register = input.read_uleb128().and_then(Register::from_u64)?; let length = input.read_uleb128().and_then(R::Offset::from_u64)?; let offset = input.offset_from(parameters.section); input.skip(length)?; Ok(CallFrameInstruction::ValExpression { register, expression: UnwindExpression { offset, length }, }) } constants::DW_CFA_GNU_args_size => { let size = input.read_uleb128()?; Ok(CallFrameInstruction::ArgsSize { size }) } constants::DW_CFA_AARCH64_negate_ra_state if vendor == Vendor::AArch64 => { Ok(CallFrameInstruction::NegateRaState) } otherwise => Err(Error::UnknownCallFrameInstruction(otherwise)), } } } /// A lazy iterator parsing call frame instructions. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[derive(Clone, Debug)] pub struct CallFrameInstructionIter<'a, R: Reader> { input: R, address_encoding: Option, parameters: PointerEncodingParameters<'a, R>, vendor: Vendor, } impl<'a, R: Reader> CallFrameInstructionIter<'a, R> { /// Parse the next call frame instruction. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { return Ok(None); } match CallFrameInstruction::parse( &mut self.input, self.address_encoding, &self.parameters, self.vendor, ) { Ok(instruction) => Ok(Some(instruction)), Err(e) => { self.input.empty(); Err(e) } } } } #[cfg(feature = "fallible-iterator")] impl<'a, R: Reader> fallible_iterator::FallibleIterator for CallFrameInstructionIter<'a, R> { type Item = CallFrameInstruction; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { CallFrameInstructionIter::next(self) } } /// The location of a DWARF expression within an unwind section. /// /// This is stored as an offset and length within the section instead of as a /// `Reader` to avoid lifetime issues when reusing [`UnwindContext`]. /// /// # Example /// ``` /// # use gimli::{EhFrame, EndianSlice, NativeEndian, Error, FrameDescriptionEntry, UnwindExpression, EvaluationResult}; /// # fn foo() -> Result<(), Error> { /// # let eh_frame: EhFrame> = unreachable!(); /// # let fde: FrameDescriptionEntry> = unimplemented!(); /// # let unwind_expression: UnwindExpression<_> = unimplemented!(); /// let expression = unwind_expression.get(&eh_frame)?; /// let mut evaluation = expression.evaluation(fde.cie().encoding()); /// let mut result = evaluation.evaluate()?; /// loop { /// match result { /// EvaluationResult::Complete => break, /// // Provide information to the evaluation. /// _ => { unimplemented!()} /// } /// } /// let value = evaluation.value_result(); /// # Ok(()) /// # } /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct UnwindExpression { /// The offset of the expression within the section. pub offset: T, /// The length of the expression. pub length: T, } impl UnwindExpression { /// Get the expression from the section. /// /// The offset and length were previously validated when the /// `UnwindExpression` was created, so this should not fail. pub fn get(&self, section: &S) -> Result> where R: Reader, S: UnwindSection, { let input = &mut section.section().clone(); input.skip(self.offset)?; let data = input.split(self.length)?; Ok(Expression(data)) } } /// Parse a `DW_EH_PE_*` pointer encoding. #[doc(hidden)] #[inline] fn parse_pointer_encoding(input: &mut R) -> Result { let eh_pe = input.read_u8()?; let eh_pe = constants::DwEhPe(eh_pe); if eh_pe.is_valid_encoding() { Ok(eh_pe) } else { Err(Error::UnknownPointerEncoding(eh_pe)) } } /// A decoded pointer. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Pointer { /// This value is the decoded pointer value. Direct(u64), /// This value is *not* the pointer value, but points to the address of /// where the real pointer value lives. In other words, deref this pointer /// to get the real pointer value. /// /// Chase this pointer at your own risk: do you trust the DWARF data it came /// from? Indirect(u64), } impl Default for Pointer { #[inline] fn default() -> Self { Pointer::Direct(0) } } impl Pointer { #[inline] fn new(encoding: constants::DwEhPe, address: u64) -> Pointer { if encoding.is_indirect() { Pointer::Indirect(address) } else { Pointer::Direct(address) } } /// Return the direct pointer value. #[inline] pub fn direct(self) -> Result { match self { Pointer::Direct(p) => Ok(p), Pointer::Indirect(_) => Err(Error::UnsupportedPointerEncoding), } } /// Return the pointer value, discarding indirectness information. #[inline] pub fn pointer(self) -> u64 { match self { Pointer::Direct(p) | Pointer::Indirect(p) => p, } } } #[derive(Clone, Debug)] struct PointerEncodingParameters<'a, R: Reader> { bases: &'a SectionBaseAddresses, func_base: Option, address_size: u8, section: &'a R, } fn parse_encoded_pointer( encoding: constants::DwEhPe, parameters: &PointerEncodingParameters<'_, R>, input: &mut R, ) -> Result { // TODO: check this once only in parse_pointer_encoding if !encoding.is_valid_encoding() { return Err(Error::UnknownPointerEncoding(encoding)); } if encoding == constants::DW_EH_PE_omit { return Err(Error::CannotParseOmitPointerEncoding); } let base = match encoding.application() { constants::DW_EH_PE_absptr => 0, constants::DW_EH_PE_pcrel => { if let Some(section_base) = parameters.bases.section { let offset_from_section = input.offset_from(parameters.section); section_base .wrapping_add_sized(offset_from_section.into_u64(), parameters.address_size) } else { return Err(Error::PcRelativePointerButSectionBaseIsUndefined); } } constants::DW_EH_PE_textrel => { if let Some(text) = parameters.bases.text { text } else { return Err(Error::TextRelativePointerButTextBaseIsUndefined); } } constants::DW_EH_PE_datarel => { if let Some(data) = parameters.bases.data { data } else { return Err(Error::DataRelativePointerButDataBaseIsUndefined); } } constants::DW_EH_PE_funcrel => { if let Some(func) = parameters.func_base { func } else { return Err(Error::FuncRelativePointerInBadContext); } } constants::DW_EH_PE_aligned => return Err(Error::UnsupportedPointerEncoding), _ => unreachable!(), }; let offset = parse_encoded_value(encoding, parameters, input)?; Ok(Pointer::new( encoding, base.wrapping_add_sized(offset, parameters.address_size), )) } fn parse_encoded_value( encoding: constants::DwEhPe, parameters: &PointerEncodingParameters<'_, R>, input: &mut R, ) -> Result { match encoding.format() { // Unsigned variants. constants::DW_EH_PE_absptr => input.read_address(parameters.address_size), constants::DW_EH_PE_uleb128 => input.read_uleb128(), constants::DW_EH_PE_udata2 => input.read_u16().map(u64::from), constants::DW_EH_PE_udata4 => input.read_u32().map(u64::from), constants::DW_EH_PE_udata8 => input.read_u64(), // Signed variants. Here we sign extend the values (happens by // default when casting a signed integer to a larger range integer // in Rust), return them as u64, and rely on wrapping addition to do // the right thing when adding these offsets to their bases. constants::DW_EH_PE_sleb128 => input.read_sleb128().map(|a| a as u64), constants::DW_EH_PE_sdata2 => input.read_i16().map(|a| a as u64), constants::DW_EH_PE_sdata4 => input.read_i32().map(|a| a as u64), constants::DW_EH_PE_sdata8 => input.read_i64().map(|a| a as u64), // That was all of the valid encoding formats. _ => unreachable!(), } } #[cfg(test)] mod tests { use super::*; use super::{parse_cfi_entry, AugmentationData, RegisterRuleMap, UnwindContext}; use crate::common::Format; use crate::constants; use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian}; use crate::read::{ EndianSlice, Error, Pointer, ReaderOffsetId, Result, Section as ReadSection, }; use crate::test_util::GimliSectionMethods; use alloc::boxed::Box; use alloc::vec::Vec; use core::marker::PhantomData; use core::mem; use test_assembler::{Endian, Label, LabelMaker, LabelOrNum, Section, ToLabelOrNum}; // Ensure each test tries to read the same section kind that it wrote. #[derive(Clone, Copy)] struct SectionKind
(PhantomData
); impl SectionKind { fn endian<'input, E>(self) -> Endian where E: Endianity, T: UnwindSection>, T::Offset: UnwindOffset, { if E::default().is_big_endian() { Endian::Big } else { Endian::Little } } fn section<'input, E>(self, contents: &'input [u8]) -> T where E: Endianity, T: UnwindSection> + ReadSection>, T::Offset: UnwindOffset, { EndianSlice::new(contents, E::default()).into() } } fn debug_frame_le<'a>() -> SectionKind>> { SectionKind(PhantomData) } fn debug_frame_be<'a>() -> SectionKind>> { SectionKind(PhantomData) } fn eh_frame_le<'a>() -> SectionKind>> { SectionKind(PhantomData) } fn parse_fde( section: Section, input: &mut R, get_cie: F, ) -> Result> where R: Reader, Section: UnwindSection, O: UnwindOffset, F: FnMut(&Section, &BaseAddresses, O) -> Result>, { let bases = Default::default(); match parse_cfi_entry(&bases, §ion, input) { Ok(Some(CieOrFde::Fde(partial))) => partial.parse(get_cie), Ok(_) => Err(Error::NoEntryAtGivenOffset), Err(e) => Err(e), } } // Mixin methods for `Section` to help define binary test data. trait CfiSectionMethods: GimliSectionMethods { fn cie<'aug, 'input, E, T>( self, _kind: SectionKind, augmentation: Option<&'aug str>, cie: &mut CommonInformationEntry>, ) -> Self where E: Endianity, T: UnwindSection>, T::Offset: UnwindOffset; fn fde<'a, 'input, E, T, L>( self, _kind: SectionKind, cie_offset: L, fde: &mut FrameDescriptionEntry>, ) -> Self where E: Endianity, T: UnwindSection>, T::Offset: UnwindOffset, L: ToLabelOrNum<'a, u64>; } impl CfiSectionMethods for Section { fn cie<'aug, 'input, E, T>( self, _kind: SectionKind, augmentation: Option<&'aug str>, cie: &mut CommonInformationEntry>, ) -> Self where E: Endianity, T: UnwindSection>, T::Offset: UnwindOffset, { cie.offset = self.size() as _; let length = Label::new(); let start = Label::new(); let end = Label::new(); let section = match cie.format { Format::Dwarf32 => self.D32(&length).mark(&start).D32(0xffff_ffff), Format::Dwarf64 => { let section = self.D32(0xffff_ffff); section.D64(&length).mark(&start).D64(0xffff_ffff_ffff_ffff) } }; let mut section = section.D8(cie.version); if let Some(augmentation) = augmentation { section = section.append_bytes(augmentation.as_bytes()); } // Null terminator for augmentation string. let section = section.D8(0); let section = if T::has_address_and_segment_sizes(cie.version) { section.D8(cie.address_size).D8(0) } else { section }; let section = section .uleb(cie.code_alignment_factor) .sleb(cie.data_alignment_factor) .uleb(cie.return_address_register.0.into()) .append_bytes(cie.initial_instructions.slice()) .mark(&end); cie.length = (&end - &start) as usize; length.set_const(cie.length as u64); section } fn fde<'a, 'input, E, T, L>( self, _kind: SectionKind, cie_offset: L, fde: &mut FrameDescriptionEntry>, ) -> Self where E: Endianity, T: UnwindSection>, T::Offset: UnwindOffset, L: ToLabelOrNum<'a, u64>, { fde.offset = self.size() as _; let length = Label::new(); let start = Label::new(); let end = Label::new(); assert_eq!(fde.format, fde.cie.format); let section = match T::cie_offset_encoding(fde.format) { CieOffsetEncoding::U32 => { let section = self.D32(&length).mark(&start); match cie_offset.to_labelornum() { LabelOrNum::Label(ref l) => section.D32(l), LabelOrNum::Num(o) => section.D32(o as u32), } } CieOffsetEncoding::U64 => { let section = self.D32(0xffff_ffff); section.D64(&length).mark(&start).D64(cie_offset) } }; let section = match fde.cie.address_size { 4 => section .D32(fde.initial_address() as u32) .D32(fde.len() as u32), 8 => section.D64(fde.initial_address()).D64(fde.len()), x => panic!("Unsupported address size: {}", x), }; let section = if let Some(ref augmentation) = fde.augmentation { let cie_aug = fde .cie .augmentation .expect("FDE has augmentation, but CIE doesn't"); if let Some(lsda) = augmentation.lsda { // We only support writing `DW_EH_PE_absptr` here. assert_eq!( cie_aug .lsda .expect("FDE has lsda, but CIE doesn't") .format(), constants::DW_EH_PE_absptr ); // Augmentation data length let section = section.uleb(u64::from(fde.cie.address_size)); match fde.cie.address_size { 4 => section.D32({ let x: u64 = lsda.pointer(); x as u32 }), 8 => section.D64({ let x: u64 = lsda.pointer(); x }), x => panic!("Unsupported address size: {}", x), } } else { // Even if we don't have any augmentation data, if there is // an augmentation defined, we need to put the length in. section.uleb(0) } } else { section }; let section = section.append_bytes(fde.instructions.slice()).mark(&end); fde.length = (&end - &start) as usize; length.set_const(fde.length as u64); section } } trait ResultExt { fn map_eof(self, input: &[u8]) -> Self; } impl ResultExt for Result { fn map_eof(self, input: &[u8]) -> Self { match self { Err(Error::UnexpectedEof(id)) => { let id = ReaderOffsetId(id.0 - input.as_ptr() as u64); Err(Error::UnexpectedEof(id)) } r => r, } } } fn assert_parse_cie<'input, E>( kind: SectionKind>>, section: Section, address_size: u8, expected: Result<( EndianSlice<'input, E>, CommonInformationEntry>, )>, ) where E: Endianity, { let section = section.get_contents().unwrap(); let mut debug_frame = kind.section(§ion); debug_frame.set_address_size(address_size); let input = &mut EndianSlice::new(§ion, E::default()); let bases = Default::default(); let result = CommonInformationEntry::parse(&bases, &debug_frame, input); let result = result.map(|cie| (*input, cie)).map_eof(§ion); assert_eq!(result, expected); } #[test] fn test_parse_cie_incomplete_length_32() { let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()).L16(5); assert_parse_cie( kind, section, 8, Err(Error::UnexpectedEof(ReaderOffsetId(0))), ); } #[test] fn test_parse_cie_incomplete_length_64() { let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()) .L32(0xffff_ffff) .L32(12345); assert_parse_cie( kind, section, 8, Err(Error::UnexpectedEof(ReaderOffsetId(4))), ); } #[test] fn test_parse_cie_incomplete_id_32() { let kind = debug_frame_be(); let section = Section::with_endian(kind.endian()) // The length is not large enough to contain the ID. .B32(3) .B32(0xffff_ffff); assert_parse_cie( kind, section, 8, Err(Error::UnexpectedEof(ReaderOffsetId(4))), ); } #[test] fn test_parse_cie_bad_id_32() { let kind = debug_frame_be(); let section = Section::with_endian(kind.endian()) // Initial length .B32(4) // Not the CIE Id. .B32(0xbad1_bad2); assert_parse_cie(kind, section, 8, Err(Error::NotCieId)); } #[test] fn test_parse_cie_32_bad_version() { let mut cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 99, augmentation: None, address_size: 4, code_alignment_factor: 1, data_alignment_factor: 2, return_address_register: Register(3), initial_instructions: EndianSlice::new(&[], LittleEndian), }; let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); assert_parse_cie(kind, section, 4, Err(Error::UnknownVersion(99))); } #[test] fn test_parse_cie_unknown_augmentation() { let length = Label::new(); let start = Label::new(); let end = Label::new(); let augmentation = "replicant"; let expected_rest = [1, 2, 3]; let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()) // Initial length .L32(&length) .mark(&start) // CIE Id .L32(0xffff_ffff) // Version .D8(4) // Augmentation .append_bytes(augmentation.as_bytes()) // Null terminator .D8(0) // Extra augmented data that we can't understand. .L32(1) .L32(2) .L32(3) .L32(4) .L32(5) .L32(6) .mark(&end) .append_bytes(&expected_rest); let expected_length = (&end - &start) as u64; length.set_const(expected_length); assert_parse_cie(kind, section, 8, Err(Error::UnknownAugmentation)); } fn test_parse_cie(format: Format, version: u8, address_size: u8) { let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); let mut cie = CommonInformationEntry { offset: 0, length: 0, format, version, augmentation: None, address_size, code_alignment_factor: 16, data_alignment_factor: 32, return_address_register: Register(1), initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), }; let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()) .cie(kind, None, &mut cie) .append_bytes(&expected_rest); assert_parse_cie( kind, section, address_size, Ok((EndianSlice::new(&expected_rest, LittleEndian), cie)), ); } #[test] fn test_parse_cie_32_ok() { test_parse_cie(Format::Dwarf32, 1, 4); test_parse_cie(Format::Dwarf32, 1, 8); test_parse_cie(Format::Dwarf32, 4, 4); test_parse_cie(Format::Dwarf32, 4, 8); } #[test] fn test_parse_cie_64_ok() { test_parse_cie(Format::Dwarf64, 1, 4); test_parse_cie(Format::Dwarf64, 1, 8); test_parse_cie(Format::Dwarf64, 4, 4); test_parse_cie(Format::Dwarf64, 4, 8); } #[test] fn test_parse_cie_length_too_big() { let expected_instrs: Vec<_> = (0..13).map(|_| constants::DW_CFA_nop.0).collect(); let mut cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 4, code_alignment_factor: 0, data_alignment_factor: 0, return_address_register: Register(3), initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), }; let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); let mut contents = section.get_contents().unwrap(); // Overwrite the length to be too big. contents[0] = 0; contents[1] = 0; contents[2] = 0; contents[3] = 255; let debug_frame = DebugFrame::new(&contents, LittleEndian); let bases = Default::default(); assert_eq!( CommonInformationEntry::parse( &bases, &debug_frame, &mut EndianSlice::new(&contents, LittleEndian) ) .map_eof(&contents), Err(Error::UnexpectedEof(ReaderOffsetId(4))) ); } #[test] fn test_parse_fde_incomplete_length_32() { let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()).L16(5); let section = section.get_contents().unwrap(); let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, LittleEndian); assert_eq!( parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), Err(Error::UnexpectedEof(ReaderOffsetId(0))) ); } #[test] fn test_parse_fde_incomplete_length_64() { let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()) .L32(0xffff_ffff) .L32(12345); let section = section.get_contents().unwrap(); let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, LittleEndian); assert_eq!( parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), Err(Error::UnexpectedEof(ReaderOffsetId(4))) ); } #[test] fn test_parse_fde_incomplete_cie_pointer_32() { let kind = debug_frame_be(); let section = Section::with_endian(kind.endian()) // The length is not large enough to contain the CIE pointer. .B32(3) .B32(1994); let section = section.get_contents().unwrap(); let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, BigEndian); assert_eq!( parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), Err(Error::UnexpectedEof(ReaderOffsetId(4))) ); } #[test] fn test_parse_fde_32_ok() { let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let cie_offset = 0xbad0_bad1; let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); let cie = CommonInformationEntry { offset: 0, length: 100, format: Format::Dwarf32, version: 4, augmentation: None, // DWARF32 with a 64 bit address size! Holy moly! address_size: 8, code_alignment_factor: 3, data_alignment_factor: 2, return_address_register: Register(1), initial_instructions: EndianSlice::new(&[], LittleEndian), }; let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0xfeed_beef, address_range: 39, augmentation: None, instructions: EndianSlice::new(&expected_instrs, LittleEndian), }; let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()) .fde(kind, cie_offset, &mut fde) .append_bytes(&expected_rest); let section = section.get_contents().unwrap(); let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, LittleEndian); let get_cie = |_: &_, _: &_, offset| { assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); Ok(cie.clone()) }; assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_fde_64_ok() { let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let cie_offset = 0xbad0_bad1; let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); let cie = CommonInformationEntry { offset: 0, length: 100, format: Format::Dwarf64, version: 4, augmentation: None, address_size: 8, code_alignment_factor: 3, data_alignment_factor: 2, return_address_register: Register(1), initial_instructions: EndianSlice::new(&[], LittleEndian), }; let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf64, cie: cie.clone(), initial_address: 0xfeed_beef, address_range: 999, augmentation: None, instructions: EndianSlice::new(&expected_instrs, LittleEndian), }; let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()) .fde(kind, cie_offset, &mut fde) .append_bytes(&expected_rest); let section = section.get_contents().unwrap(); let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, LittleEndian); let get_cie = |_: &_, _: &_, offset| { assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); Ok(cie.clone()) }; assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_entry_on_cie_32_ok() { let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); let mut cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 4, code_alignment_factor: 16, data_alignment_factor: 32, return_address_register: Register(1), initial_instructions: EndianSlice::new(&expected_instrs, BigEndian), }; let kind = debug_frame_be(); let section = Section::with_endian(kind.endian()) .cie(kind, None, &mut cie) .append_bytes(&expected_rest); let section = section.get_contents().unwrap(); let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, BigEndian); let bases = Default::default(); assert_eq!( parse_cfi_entry(&bases, &debug_frame, rest), Ok(Some(CieOrFde::Cie(cie))) ); assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); } #[test] fn test_parse_cfi_entry_on_fde_32_ok() { let cie_offset = 0x1234_5678; let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); let cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 4, code_alignment_factor: 16, data_alignment_factor: 32, return_address_register: Register(1), initial_instructions: EndianSlice::new(&[], BigEndian), }; let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0xfeed_beef, address_range: 39, augmentation: None, instructions: EndianSlice::new(&expected_instrs, BigEndian), }; let kind = debug_frame_be(); let section = Section::with_endian(kind.endian()) .fde(kind, cie_offset, &mut fde) .append_bytes(&expected_rest); let section = section.get_contents().unwrap(); let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, BigEndian); let bases = Default::default(); match parse_cfi_entry(&bases, &debug_frame, rest) { Ok(Some(CieOrFde::Fde(partial))) => { assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); assert_eq!(partial.length, fde.length); assert_eq!(partial.format, fde.format); assert_eq!(partial.cie_offset, DebugFrameOffset(cie_offset as usize)); let get_cie = |_: &_, _: &_, offset| { assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); Ok(cie.clone()) }; assert_eq!(partial.parse(get_cie), Ok(fde)); } otherwise => panic!("Unexpected result: {:#?}", otherwise), } } #[test] fn test_cfi_entries_iter() { let expected_instrs1: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); let expected_instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); let expected_instrs3: Vec<_> = (0..12).map(|_| constants::DW_CFA_nop.0).collect(); let expected_instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); let mut cie1 = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 4, code_alignment_factor: 1, data_alignment_factor: 2, return_address_register: Register(3), initial_instructions: EndianSlice::new(&expected_instrs1, BigEndian), }; let mut cie2 = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 4, code_alignment_factor: 3, data_alignment_factor: 2, return_address_register: Register(1), initial_instructions: EndianSlice::new(&expected_instrs2, BigEndian), }; let cie1_location = Label::new(); let cie2_location = Label::new(); // Write the CIEs first so that their length gets set before we clone // them into the FDEs and our equality assertions down the line end up // with all the CIEs always having he correct length. let kind = debug_frame_be(); let section = Section::with_endian(kind.endian()) .mark(&cie1_location) .cie(kind, None, &mut cie1) .mark(&cie2_location) .cie(kind, None, &mut cie2); let mut fde1 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie1.clone(), initial_address: 0xfeed_beef, address_range: 39, augmentation: None, instructions: EndianSlice::new(&expected_instrs3, BigEndian), }; let mut fde2 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie2.clone(), initial_address: 0xfeed_face, address_range: 9000, augmentation: None, instructions: EndianSlice::new(&expected_instrs4, BigEndian), }; let section = section .fde(kind, &cie1_location, &mut fde1) .fde(kind, &cie2_location, &mut fde2); section.start().set_const(0); let cie1_offset = cie1_location.value().unwrap() as usize; let cie2_offset = cie2_location.value().unwrap() as usize; let contents = section.get_contents().unwrap(); let debug_frame = kind.section(&contents); let bases = Default::default(); let mut entries = debug_frame.entries(&bases); assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie1.clone())))); assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie2.clone())))); match entries.next() { Ok(Some(CieOrFde::Fde(partial))) => { assert_eq!(partial.length, fde1.length); assert_eq!(partial.format, fde1.format); assert_eq!(partial.cie_offset, DebugFrameOffset(cie1_offset)); let get_cie = |_: &_, _: &_, offset| { assert_eq!(offset, DebugFrameOffset(cie1_offset)); Ok(cie1.clone()) }; assert_eq!(partial.parse(get_cie), Ok(fde1)); } otherwise => panic!("Unexpected result: {:#?}", otherwise), } match entries.next() { Ok(Some(CieOrFde::Fde(partial))) => { assert_eq!(partial.length, fde2.length); assert_eq!(partial.format, fde2.format); assert_eq!(partial.cie_offset, DebugFrameOffset(cie2_offset)); let get_cie = |_: &_, _: &_, offset| { assert_eq!(offset, DebugFrameOffset(cie2_offset)); Ok(cie2.clone()) }; assert_eq!(partial.parse(get_cie), Ok(fde2)); } otherwise => panic!("Unexpected result: {:#?}", otherwise), } assert_eq!(entries.next(), Ok(None)); } #[test] fn test_parse_cie_from_offset() { let filler = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let instrs: Vec<_> = (0..5).map(|_| constants::DW_CFA_nop.0).collect(); let mut cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf64, version: 4, augmentation: None, address_size: 4, code_alignment_factor: 4, data_alignment_factor: 8, return_address_register: Register(12), initial_instructions: EndianSlice::new(&instrs, LittleEndian), }; let cie_location = Label::new(); let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()) .append_bytes(&filler) .mark(&cie_location) .cie(kind, None, &mut cie) .append_bytes(&filler); section.start().set_const(0); let cie_offset = DebugFrameOffset(cie_location.value().unwrap() as usize); let contents = section.get_contents().unwrap(); let debug_frame = kind.section(&contents); let bases = Default::default(); assert_eq!(debug_frame.cie_from_offset(&bases, cie_offset), Ok(cie)); } fn parse_cfi_instruction( input: &mut R, address_size: u8, ) -> Result> { let section = input.clone(); let parameters = &PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size, section: §ion, }; CallFrameInstruction::parse(input, None, parameters, Vendor::Default) } #[test] fn test_parse_cfi_instruction_advance_loc() { let expected_rest = [1, 2, 3, 4]; let expected_delta = 42; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_advance_loc.0 | expected_delta) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::AdvanceLoc { delta: u32::from(expected_delta), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_offset() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 3; let expected_offset = 1997; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_offset.0 | expected_reg) .uleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Offset { register: Register(expected_reg.into()), factored_offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_restore() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 3; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_restore.0 | expected_reg) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Restore { register: Register(expected_reg.into()), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_nop() { let expected_rest = [1, 2, 3, 4]; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_nop.0) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Nop) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_set_loc() { let expected_rest = [1, 2, 3, 4]; let expected_addr = 0xdead_beef; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_set_loc.0) .L64(expected_addr) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::SetLoc { address: expected_addr, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_set_loc_encoding() { let text_base = 0xfeed_face; let addr_offset = 0xbeef; let expected_addr = text_base + addr_offset; let expected_rest = [1, 2, 3, 4]; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_set_loc.0) .L64(addr_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); let parameters = &PointerEncodingParameters { bases: &BaseAddresses::default().set_text(text_base).eh_frame, func_base: None, address_size: 8, section: &EndianSlice::new(&[], LittleEndian), }; assert_eq!( CallFrameInstruction::parse( input, Some(constants::DW_EH_PE_textrel), parameters, Vendor::Default ), Ok(CallFrameInstruction::SetLoc { address: expected_addr, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_advance_loc1() { let expected_rest = [1, 2, 3, 4]; let expected_delta = 8; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_advance_loc1.0) .D8(expected_delta) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::AdvanceLoc { delta: u32::from(expected_delta), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_advance_loc2() { let expected_rest = [1, 2, 3, 4]; let expected_delta = 500; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_advance_loc2.0) .L16(expected_delta) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::AdvanceLoc { delta: u32::from(expected_delta), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_advance_loc4() { let expected_rest = [1, 2, 3, 4]; let expected_delta = 1 << 20; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_advance_loc4.0) .L32(expected_delta) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::AdvanceLoc { delta: expected_delta, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_offset_extended() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 7; let expected_offset = 33; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_offset_extended.0) .uleb(expected_reg.into()) .uleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Offset { register: Register(expected_reg), factored_offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_restore_extended() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 7; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_restore_extended.0) .uleb(expected_reg.into()) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Restore { register: Register(expected_reg), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_undefined() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 7; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_undefined.0) .uleb(expected_reg.into()) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Undefined { register: Register(expected_reg), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_same_value() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 7; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_same_value.0) .uleb(expected_reg.into()) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::SameValue { register: Register(expected_reg), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_register() { let expected_rest = [1, 2, 3, 4]; let expected_dest_reg = 7; let expected_src_reg = 8; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_register.0) .uleb(expected_dest_reg.into()) .uleb(expected_src_reg.into()) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Register { dest_register: Register(expected_dest_reg), src_register: Register(expected_src_reg), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_remember_state() { let expected_rest = [1, 2, 3, 4]; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_remember_state.0) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::RememberState) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_restore_state() { let expected_rest = [1, 2, 3, 4]; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_restore_state.0) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::RestoreState) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_def_cfa() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 2; let expected_offset = 0; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_def_cfa.0) .uleb(expected_reg.into()) .uleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::DefCfa { register: Register(expected_reg), offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_def_cfa_register() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 2; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_def_cfa_register.0) .uleb(expected_reg.into()) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::DefCfaRegister { register: Register(expected_reg), }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_def_cfa_offset() { let expected_rest = [1, 2, 3, 4]; let expected_offset = 23; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_def_cfa_offset.0) .uleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::DefCfaOffset { offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_def_cfa_expression() { let expected_rest = [1, 2, 3, 4]; let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; let length = Label::new(); let start = Label::new(); let end = Label::new(); let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_def_cfa_expression.0) .D8(&length) .mark(&start) .append_bytes(&expected_expr) .mark(&end) .append_bytes(&expected_rest); length.set_const((&end - &start) as u64); let expected_expression = UnwindExpression { offset: (&start - §ion.start()) as usize, length: (&end - &start) as usize, }; let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::DefCfaExpression { expression: expected_expression, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_expression() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 99; let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; let length = Label::new(); let start = Label::new(); let end = Label::new(); let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_expression.0) .uleb(expected_reg.into()) .D8(&length) .mark(&start) .append_bytes(&expected_expr) .mark(&end) .append_bytes(&expected_rest); length.set_const((&end - &start) as u64); let expected_expression = UnwindExpression { offset: (&start - §ion.start()) as usize, length: (&end - &start) as usize, }; let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Expression { register: Register(expected_reg), expression: expected_expression, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_offset_extended_sf() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 7; let expected_offset = -33; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_offset_extended_sf.0) .uleb(expected_reg.into()) .sleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::OffsetExtendedSf { register: Register(expected_reg), factored_offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_def_cfa_sf() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 2; let expected_offset = -9999; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_def_cfa_sf.0) .uleb(expected_reg.into()) .sleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::DefCfaSf { register: Register(expected_reg), factored_offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_def_cfa_offset_sf() { let expected_rest = [1, 2, 3, 4]; let expected_offset = -123; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_def_cfa_offset_sf.0) .sleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::DefCfaOffsetSf { factored_offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_val_offset() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 50; let expected_offset = 23; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_val_offset.0) .uleb(expected_reg.into()) .uleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::ValOffset { register: Register(expected_reg), factored_offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_val_offset_sf() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 50; let expected_offset = -23; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_val_offset_sf.0) .uleb(expected_reg.into()) .sleb(expected_offset) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::ValOffsetSf { register: Register(expected_reg), factored_offset: expected_offset, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_val_expression() { let expected_rest = [1, 2, 3, 4]; let expected_reg = 50; let expected_expr = [2, 2, 1, 1, 5, 5]; let length = Label::new(); let start = Label::new(); let end = Label::new(); let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_val_expression.0) .uleb(expected_reg.into()) .D8(&length) .mark(&start) .append_bytes(&expected_expr) .mark(&end) .append_bytes(&expected_rest); length.set_const((&end - &start) as u64); let expected_expression = UnwindExpression { offset: (&start - §ion.start()) as usize, length: (&end - &start) as usize, }; let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::ValExpression { register: Register(expected_reg), expression: expected_expression, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_negate_ra_state() { let expected_rest = [1, 2, 3, 4]; let section = Section::with_endian(Endian::Little) .D8(constants::DW_CFA_AARCH64_negate_ra_state.0) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); let parameters = &PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 8, section: &EndianSlice::default(), }; assert_eq!( CallFrameInstruction::parse(input, None, parameters, Vendor::AArch64), Ok(CallFrameInstruction::NegateRaState) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_cfi_instruction_unknown_instruction() { let expected_rest = [1, 2, 3, 4]; let unknown_instr = constants::DwCfa(0b0011_1111); let section = Section::with_endian(Endian::Little) .D8(unknown_instr.0) .append_bytes(&expected_rest); let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Err(Error::UnknownCallFrameInstruction(unknown_instr)) ); } #[test] fn test_call_frame_instruction_iter_ok() { let expected_reg = 50; let expected_expr = [2, 2, 1, 1, 5, 5]; let expected_delta = 230; let length = Label::new(); let start = Label::new(); let end = Label::new(); let section = Section::with_endian(Endian::Big) .D8(constants::DW_CFA_val_expression.0) .uleb(expected_reg.into()) .D8(&length) .mark(&start) .append_bytes(&expected_expr) .mark(&end) .D8(constants::DW_CFA_advance_loc1.0) .D8(expected_delta); length.set_const((&end - &start) as u64); let expected_expression = UnwindExpression { offset: (&start - §ion.start()) as usize, length: (&end - &start) as usize, }; let contents = section.get_contents().unwrap(); let input = EndianSlice::new(&contents, BigEndian); let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 8, section: &input, }; let mut iter = CallFrameInstructionIter { input, address_encoding: None, parameters, vendor: Vendor::Default, }; assert_eq!( iter.next(), Ok(Some(CallFrameInstruction::ValExpression { register: Register(expected_reg), expression: expected_expression, })) ); assert_eq!( iter.next(), Ok(Some(CallFrameInstruction::AdvanceLoc { delta: u32::from(expected_delta), })) ); assert_eq!(iter.next(), Ok(None)); } #[test] fn test_call_frame_instruction_iter_err() { // DW_CFA_advance_loc1 without an operand. let section = Section::with_endian(Endian::Big).D8(constants::DW_CFA_advance_loc1.0); let contents = section.get_contents().unwrap(); let input = EndianSlice::new(&contents, BigEndian); let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 8, section: &EndianSlice::default(), }; let mut iter = CallFrameInstructionIter { input, address_encoding: None, parameters, vendor: Vendor::Default, }; assert_eq!( iter.next().map_eof(&contents), Err(Error::UnexpectedEof(ReaderOffsetId(1))) ); assert_eq!(iter.next(), Ok(None)); } fn assert_eval<'a, I>( mut initial_ctx: UnwindContext, expected_ctx: UnwindContext, cie: CommonInformationEntry>, fde: Option>>, instructions: I, ) where I: AsRef<[(Result, CallFrameInstruction)]>, { { let section = &DebugFrame::from(EndianSlice::default()); let bases = &BaseAddresses::default(); let mut table = match fde { Some(fde) => UnwindTable::new_for_fde(section, bases, &mut initial_ctx, &fde), None => UnwindTable::new_for_cie(section, bases, &mut initial_ctx, &cie), }; for (expected_result, instruction) in instructions.as_ref() { assert_eq!(*expected_result, table.evaluate(instruction.clone())); } } assert_eq!(expected_ctx, initial_ctx); } fn make_test_cie<'a>() -> CommonInformationEntry> { CommonInformationEntry { offset: 0, format: Format::Dwarf64, length: 0, return_address_register: Register(0), version: 4, address_size: mem::size_of::() as u8, initial_instructions: EndianSlice::new(&[], LittleEndian), augmentation: None, data_alignment_factor: 2, code_alignment_factor: 3, } } #[test] fn test_eval_set_loc() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected.row_mut().end_address = 42; let instructions = [(Ok(true), CallFrameInstruction::SetLoc { address: 42 })]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_set_loc_backwards() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); ctx.row_mut().start_address = 999; let expected = ctx.clone(); let instructions = [( Err(Error::InvalidAddressRange), CallFrameInstruction::SetLoc { address: 42 }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_advance_loc() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); ctx.row_mut().start_address = 3; let mut expected = ctx.clone(); expected.row_mut().end_address = 3 + 2 * cie.code_alignment_factor; let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 2 })]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_advance_loc_overflow_32() { let mut cie = make_test_cie(); cie.address_size = 4; let mut ctx = UnwindContext::new(); ctx.row_mut().start_address = u32::MAX.into(); let expected = ctx.clone(); let instructions = [( Err(Error::AddressOverflow), CallFrameInstruction::AdvanceLoc { delta: 42 }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_advance_loc_overflow_64() { let mut cie = make_test_cie(); cie.address_size = 8; let mut ctx = UnwindContext::new(); ctx.row_mut().start_address = u64::MAX; let expected = ctx.clone(); let instructions = [( Err(Error::AddressOverflow), CallFrameInstruction::AdvanceLoc { delta: 42 }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_def_cfa() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected.set_cfa(CfaRule::RegisterAndOffset { register: Register(42), offset: 36, }); let instructions = [( Ok(false), CallFrameInstruction::DefCfa { register: Register(42), offset: 36, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_def_cfa_sf() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected.set_cfa(CfaRule::RegisterAndOffset { register: Register(42), offset: 36 * cie.data_alignment_factor as i64, }); let instructions = [( Ok(false), CallFrameInstruction::DefCfaSf { register: Register(42), factored_offset: 36, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_def_cfa_register() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); ctx.set_cfa(CfaRule::RegisterAndOffset { register: Register(3), offset: 8, }); let mut expected = ctx.clone(); expected.set_cfa(CfaRule::RegisterAndOffset { register: Register(42), offset: 8, }); let instructions = [( Ok(false), CallFrameInstruction::DefCfaRegister { register: Register(42), }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_def_cfa_register_invalid_context() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); ctx.set_cfa(CfaRule::Expression(UnwindExpression { offset: 0, length: 0, })); let expected = ctx.clone(); let instructions = [( Err(Error::CfiInstructionInInvalidContext), CallFrameInstruction::DefCfaRegister { register: Register(42), }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_def_cfa_offset() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); ctx.set_cfa(CfaRule::RegisterAndOffset { register: Register(3), offset: 8, }); let mut expected = ctx.clone(); expected.set_cfa(CfaRule::RegisterAndOffset { register: Register(3), offset: 42, }); let instructions = [(Ok(false), CallFrameInstruction::DefCfaOffset { offset: 42 })]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_def_cfa_offset_invalid_context() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); ctx.set_cfa(CfaRule::Expression(UnwindExpression { offset: 10, length: 11, })); let expected = ctx.clone(); let instructions = [( Err(Error::CfiInstructionInInvalidContext), CallFrameInstruction::DefCfaOffset { offset: 1993 }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_def_cfa_expression() { let expr = UnwindExpression { offset: 10, length: 11, }; let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected.set_cfa(CfaRule::Expression(expr)); let instructions = [( Ok(false), CallFrameInstruction::DefCfaExpression { expression: expr }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_undefined() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule(Register(5), RegisterRule::Undefined) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::Undefined { register: Register(5), }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_same_value() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule(Register(0), RegisterRule::SameValue) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::SameValue { register: Register(0), }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_offset() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule( Register(2), RegisterRule::Offset(3 * cie.data_alignment_factor), ) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::Offset { register: Register(2), factored_offset: 3, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_offset_extended_sf() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule( Register(4), RegisterRule::Offset(-3 * cie.data_alignment_factor), ) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::OffsetExtendedSf { register: Register(4), factored_offset: -3, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_val_offset() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule( Register(5), RegisterRule::ValOffset(7 * cie.data_alignment_factor), ) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::ValOffset { register: Register(5), factored_offset: 7, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_val_offset_sf() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule( Register(5), RegisterRule::ValOffset(-7 * cie.data_alignment_factor), ) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::ValOffsetSf { register: Register(5), factored_offset: -7, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_expression() { let expr = UnwindExpression { offset: 10, length: 11, }; let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule(Register(9), RegisterRule::Expression(expr)) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::Expression { register: Register(9), expression: expr, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_val_expression() { let expr = UnwindExpression { offset: 10, length: 11, }; let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule(Register(9), RegisterRule::ValExpression(expr)) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::ValExpression { register: Register(9), expression: expr, }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_restore() { let cie = make_test_cie(); let fde = FrameDescriptionEntry { offset: 0, format: Format::Dwarf64, length: 0, address_range: 0, augmentation: None, initial_address: 0, cie: cie.clone(), instructions: EndianSlice::new(&[], LittleEndian), }; let mut ctx = UnwindContext::new(); ctx.set_register_rule(Register(0), RegisterRule::Offset(1)) .unwrap(); ctx.save_initial_rules().unwrap(); let expected = ctx.clone(); ctx.set_register_rule(Register(0), RegisterRule::Offset(2)) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::Restore { register: Register(0), }, )]; assert_eval(ctx, expected, cie, Some(fde), instructions); } #[test] fn test_eval_restore_havent_saved_initial_context() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let expected = ctx.clone(); let instructions = [( Err(Error::CfiInstructionInInvalidContext), CallFrameInstruction::Restore { register: Register(0), }, )]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_remember_state() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected.push_row().unwrap(); let instructions = [(Ok(false), CallFrameInstruction::RememberState)]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_restore_state() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); ctx.set_start_address(1); ctx.set_register_rule(Register(0), RegisterRule::SameValue) .unwrap(); let mut expected = ctx.clone(); ctx.push_row().unwrap(); ctx.set_start_address(2); ctx.set_register_rule(Register(0), RegisterRule::Offset(16)) .unwrap(); // Restore state should preserve current location. expected.set_start_address(2); let instructions = [ // First one pops just fine. (Ok(false), CallFrameInstruction::RestoreState), // Second pop would try to pop out of bounds. ( Err(Error::PopWithEmptyStack), CallFrameInstruction::RestoreState, ), ]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_negate_ra_state() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(1)) .unwrap(); let instructions = [(Ok(false), CallFrameInstruction::NegateRaState)]; assert_eval(ctx, expected, cie, None, instructions); let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(0)) .unwrap(); let instructions = [ (Ok(false), CallFrameInstruction::NegateRaState), (Ok(false), CallFrameInstruction::NegateRaState), ]; assert_eval(ctx, expected, cie, None, instructions); // NegateRaState can't be used with other instructions. let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected .set_register_rule( crate::AArch64::RA_SIGN_STATE, RegisterRule::Offset(cie.data_alignment_factor as i64), ) .unwrap(); let instructions = [ ( Ok(false), CallFrameInstruction::Offset { register: crate::AArch64::RA_SIGN_STATE, factored_offset: 1, }, ), ( Err(Error::CfiInstructionInInvalidContext), CallFrameInstruction::NegateRaState, ), ]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_eval_nop() { let cie = make_test_cie(); let ctx = UnwindContext::new(); let expected = ctx.clone(); let instructions = [(Ok(false), CallFrameInstruction::Nop)]; assert_eval(ctx, expected, cie, None, instructions); } #[test] fn test_unwind_table_cie_no_rule() { let initial_instructions = Section::with_endian(Endian::Little) // The CFA is -12 from register 4. .D8(constants::DW_CFA_def_cfa_sf.0) .uleb(4) .sleb(-12) .append_repeated(constants::DW_CFA_nop.0, 4); let initial_instructions = initial_instructions.get_contents().unwrap(); let cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 8, code_alignment_factor: 1, data_alignment_factor: 1, return_address_register: Register(3), initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), }; let instructions = Section::with_endian(Endian::Little) // A bunch of nop padding. .append_repeated(constants::DW_CFA_nop.0, 8); let instructions = instructions.get_contents().unwrap(); let fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0, address_range: 100, augmentation: None, instructions: EndianSlice::new(&instructions, LittleEndian), }; let section = &DebugFrame::from(EndianSlice::default()); let bases = &BaseAddresses::default(); let mut ctx = Box::new(UnwindContext::new()); let mut table = fde .rows(section, bases, &mut ctx) .expect("Should run initial program OK"); assert!(table.ctx.is_initialized); let expected_initial_rule = (Register(0), RegisterRule::Undefined); assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); { let row = table.next_row().expect("Should evaluate first row OK"); let expected = UnwindTableRow { start_address: 0, end_address: 100, saved_args_size: 0, cfa: CfaRule::RegisterAndOffset { register: Register(4), offset: -12, }, registers: [].iter().collect(), }; assert_eq!(Some(&expected), row); } // All done! assert_eq!(Ok(None), table.next_row()); assert_eq!(Ok(None), table.next_row()); } #[test] fn test_unwind_table_cie_single_rule() { let initial_instructions = Section::with_endian(Endian::Little) // The CFA is -12 from register 4. .D8(constants::DW_CFA_def_cfa_sf.0) .uleb(4) .sleb(-12) // Register 3 is 4 from the CFA. .D8(constants::DW_CFA_offset.0 | 3) .uleb(4) .append_repeated(constants::DW_CFA_nop.0, 4); let initial_instructions = initial_instructions.get_contents().unwrap(); let cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 8, code_alignment_factor: 1, data_alignment_factor: 1, return_address_register: Register(3), initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), }; let instructions = Section::with_endian(Endian::Little) // A bunch of nop padding. .append_repeated(constants::DW_CFA_nop.0, 8); let instructions = instructions.get_contents().unwrap(); let fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0, address_range: 100, augmentation: None, instructions: EndianSlice::new(&instructions, LittleEndian), }; let section = &DebugFrame::from(EndianSlice::default()); let bases = &BaseAddresses::default(); let mut ctx = Box::new(UnwindContext::new()); let mut table = fde .rows(section, bases, &mut ctx) .expect("Should run initial program OK"); assert!(table.ctx.is_initialized); let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); { let row = table.next_row().expect("Should evaluate first row OK"); let expected = UnwindTableRow { start_address: 0, end_address: 100, saved_args_size: 0, cfa: CfaRule::RegisterAndOffset { register: Register(4), offset: -12, }, registers: [(Register(3), RegisterRule::Offset(4))].iter().collect(), }; assert_eq!(Some(&expected), row); } // All done! assert_eq!(Ok(None), table.next_row()); assert_eq!(Ok(None), table.next_row()); } #[test] fn test_unwind_table_cie_invalid_rule() { let initial_instructions1 = Section::with_endian(Endian::Little) // Test that stack length is reset. .D8(constants::DW_CFA_remember_state.0) // Test that stack value is reset (different register from that used later). .D8(constants::DW_CFA_offset.0 | 4) .uleb(8) // Invalid due to missing operands. .D8(constants::DW_CFA_offset.0); let initial_instructions1 = initial_instructions1.get_contents().unwrap(); let cie1 = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 8, code_alignment_factor: 1, data_alignment_factor: 1, return_address_register: Register(3), initial_instructions: EndianSlice::new(&initial_instructions1, LittleEndian), }; let initial_instructions2 = Section::with_endian(Endian::Little) // Register 3 is 4 from the CFA. .D8(constants::DW_CFA_offset.0 | 3) .uleb(4) .append_repeated(constants::DW_CFA_nop.0, 4); let initial_instructions2 = initial_instructions2.get_contents().unwrap(); let cie2 = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 8, code_alignment_factor: 1, data_alignment_factor: 1, return_address_register: Register(3), initial_instructions: EndianSlice::new(&initial_instructions2, LittleEndian), }; let fde1 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie1.clone(), initial_address: 0, address_range: 100, augmentation: None, instructions: EndianSlice::new(&[], LittleEndian), }; let fde2 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie2.clone(), initial_address: 0, address_range: 100, augmentation: None, instructions: EndianSlice::new(&[], LittleEndian), }; let section = &DebugFrame::from(EndianSlice::default()); let bases = &BaseAddresses::default(); let mut ctx = Box::new(UnwindContext::new()); let table = fde1 .rows(section, bases, &mut ctx) .map_eof(&initial_instructions1); assert_eq!(table.err(), Some(Error::UnexpectedEof(ReaderOffsetId(4)))); assert!(!ctx.is_initialized); assert_eq!(ctx.stack.len(), 2); assert_eq!(ctx.initial_rule, None); let _table = fde2 .rows(section, bases, &mut ctx) .expect("Should run initial program OK"); assert!(ctx.is_initialized); assert_eq!(ctx.stack.len(), 1); let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); assert_eq!(ctx.initial_rule, Some(expected_initial_rule)); } #[test] fn test_unwind_table_next_row() { #[allow(clippy::identity_op)] let initial_instructions = Section::with_endian(Endian::Little) // The CFA is -12 from register 4. .D8(constants::DW_CFA_def_cfa_sf.0) .uleb(4) .sleb(-12) // Register 0 is 8 from the CFA. .D8(constants::DW_CFA_offset.0 | 0) .uleb(8) // Register 3 is 4 from the CFA. .D8(constants::DW_CFA_offset.0 | 3) .uleb(4) .append_repeated(constants::DW_CFA_nop.0, 4); let initial_instructions = initial_instructions.get_contents().unwrap(); let cie = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 8, code_alignment_factor: 1, data_alignment_factor: 1, return_address_register: Register(3), initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), }; let instructions = Section::with_endian(Endian::Little) // Initial instructions form a row, advance the address by 1. .D8(constants::DW_CFA_advance_loc1.0) .D8(1) // Register 0 is -16 from the CFA. .D8(constants::DW_CFA_offset_extended_sf.0) .uleb(0) .sleb(-16) // Finish this row, advance the address by 32. .D8(constants::DW_CFA_advance_loc1.0) .D8(32) // Register 3 is -4 from the CFA. .D8(constants::DW_CFA_offset_extended_sf.0) .uleb(3) .sleb(-4) // Finish this row, advance the address by 64. .D8(constants::DW_CFA_advance_loc1.0) .D8(64) // Register 5 is 4 from the CFA. .D8(constants::DW_CFA_offset.0 | 5) .uleb(4) // A bunch of nop padding. .append_repeated(constants::DW_CFA_nop.0, 8); let instructions = instructions.get_contents().unwrap(); let fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0, address_range: 100, augmentation: None, instructions: EndianSlice::new(&instructions, LittleEndian), }; let section = &DebugFrame::from(EndianSlice::default()); let bases = &BaseAddresses::default(); let mut ctx = Box::new(UnwindContext::new()); let mut table = fde .rows(section, bases, &mut ctx) .expect("Should run initial program OK"); assert!(table.ctx.is_initialized); assert!(table.ctx.initial_rule.is_none()); let expected_initial_rules: RegisterRuleMap<_> = [ (Register(0), RegisterRule::Offset(8)), (Register(3), RegisterRule::Offset(4)), ] .iter() .collect(); assert_eq!(table.ctx.stack[0].registers, expected_initial_rules); { let row = table.next_row().expect("Should evaluate first row OK"); let expected = UnwindTableRow { start_address: 0, end_address: 1, saved_args_size: 0, cfa: CfaRule::RegisterAndOffset { register: Register(4), offset: -12, }, registers: [ (Register(0), RegisterRule::Offset(8)), (Register(3), RegisterRule::Offset(4)), ] .iter() .collect(), }; assert_eq!(Some(&expected), row); } { let row = table.next_row().expect("Should evaluate second row OK"); let expected = UnwindTableRow { start_address: 1, end_address: 33, saved_args_size: 0, cfa: CfaRule::RegisterAndOffset { register: Register(4), offset: -12, }, registers: [ (Register(0), RegisterRule::Offset(-16)), (Register(3), RegisterRule::Offset(4)), ] .iter() .collect(), }; assert_eq!(Some(&expected), row); } { let row = table.next_row().expect("Should evaluate third row OK"); let expected = UnwindTableRow { start_address: 33, end_address: 97, saved_args_size: 0, cfa: CfaRule::RegisterAndOffset { register: Register(4), offset: -12, }, registers: [ (Register(0), RegisterRule::Offset(-16)), (Register(3), RegisterRule::Offset(-4)), ] .iter() .collect(), }; assert_eq!(Some(&expected), row); } { let row = table.next_row().expect("Should evaluate fourth row OK"); let expected = UnwindTableRow { start_address: 97, end_address: 100, saved_args_size: 0, cfa: CfaRule::RegisterAndOffset { register: Register(4), offset: -12, }, registers: [ (Register(0), RegisterRule::Offset(-16)), (Register(3), RegisterRule::Offset(-4)), (Register(5), RegisterRule::Offset(4)), ] .iter() .collect(), }; assert_eq!(Some(&expected), row); } // All done! assert_eq!(Ok(None), table.next_row()); assert_eq!(Ok(None), table.next_row()); } #[test] fn test_unwind_info_for_address_ok() { let instrs1 = Section::with_endian(Endian::Big) // The CFA is -12 from register 4. .D8(constants::DW_CFA_def_cfa_sf.0) .uleb(4) .sleb(-12); let instrs1 = instrs1.get_contents().unwrap(); let instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); let instrs3 = Section::with_endian(Endian::Big) // Initial instructions form a row, advance the address by 100. .D8(constants::DW_CFA_advance_loc1.0) .D8(100) // Register 0 is -16 from the CFA. .D8(constants::DW_CFA_offset_extended_sf.0) .uleb(0) .sleb(-16); let instrs3 = instrs3.get_contents().unwrap(); let instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); let mut cie1 = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 8, code_alignment_factor: 1, data_alignment_factor: 1, return_address_register: Register(3), initial_instructions: EndianSlice::new(&instrs1, BigEndian), }; let mut cie2 = CommonInformationEntry { offset: 0, length: 0, format: Format::Dwarf32, version: 4, augmentation: None, address_size: 4, code_alignment_factor: 1, data_alignment_factor: 1, return_address_register: Register(1), initial_instructions: EndianSlice::new(&instrs2, BigEndian), }; let cie1_location = Label::new(); let cie2_location = Label::new(); // Write the CIEs first so that their length gets set before we clone // them into the FDEs and our equality assertions down the line end up // with all the CIEs always having he correct length. let kind = debug_frame_be(); let section = Section::with_endian(kind.endian()) .mark(&cie1_location) .cie(kind, None, &mut cie1) .mark(&cie2_location) .cie(kind, None, &mut cie2); let mut fde1 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie1.clone(), initial_address: 0xfeed_beef, address_range: 200, augmentation: None, instructions: EndianSlice::new(&instrs3, BigEndian), }; let mut fde2 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie2.clone(), initial_address: 0xfeed_face, address_range: 9000, augmentation: None, instructions: EndianSlice::new(&instrs4, BigEndian), }; let section = section .fde(kind, &cie1_location, &mut fde1) .fde(kind, &cie2_location, &mut fde2); section.start().set_const(0); let contents = section.get_contents().unwrap(); let debug_frame = kind.section(&contents); // Get the second row of the unwind table in `instrs3`. let bases = Default::default(); let mut ctx = Box::new(UnwindContext::new()); let result = debug_frame.unwind_info_for_address( &bases, &mut ctx, 0xfeed_beef + 150, DebugFrame::cie_from_offset, ); assert!(result.is_ok()); let unwind_info = result.unwrap(); assert_eq!( *unwind_info, UnwindTableRow { start_address: fde1.initial_address() + 100, end_address: fde1.end_address(), saved_args_size: 0, cfa: CfaRule::RegisterAndOffset { register: Register(4), offset: -12, }, registers: [(Register(0), RegisterRule::Offset(-16))].iter().collect(), } ); } #[test] fn test_unwind_info_for_address_not_found() { let debug_frame = DebugFrame::new(&[], NativeEndian); let bases = Default::default(); let mut ctx = Box::new(UnwindContext::new()); let result = debug_frame.unwind_info_for_address( &bases, &mut ctx, 0xbadb_ad99, DebugFrame::cie_from_offset, ); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress); } #[test] fn test_eh_frame_hdr_unknown_version() { let bases = BaseAddresses::default(); let buf = &[42]; let result = EhFrameHdr::new(buf, NativeEndian).parse(&bases, 8); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::UnknownVersion(42)); } #[test] fn test_eh_frame_hdr_omit_ehptr() { let section = Section::with_endian(Endian::Little) .L8(1) .L8(0xff) .L8(0x03) .L8(0x0b) .L32(2) .L32(10) .L32(1) .L32(20) .L32(2) .L32(0); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::CannotParseOmitPointerEncoding); } #[test] fn test_eh_frame_hdr_omit_count() { let section = Section::with_endian(Endian::Little) .L8(1) .L8(0x0b) .L8(0xff) .L8(0x0b) .L32(0x12345); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(result.is_ok()); let result = result.unwrap(); assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); assert!(result.table().is_none()); } #[test] fn test_eh_frame_hdr_omit_table() { let section = Section::with_endian(Endian::Little) .L8(1) .L8(0x0b) .L8(0x03) .L8(0xff) .L32(0x12345) .L32(2); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(result.is_ok()); let result = result.unwrap(); assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); assert!(result.table().is_none()); } #[test] fn test_eh_frame_hdr_varlen_table() { let section = Section::with_endian(Endian::Little) .L8(1) .L8(0x0b) .L8(0x03) .L8(0x01) .L32(0x12345) .L32(2); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(result.is_ok()); let result = result.unwrap(); assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); let table = result.table(); assert!(table.is_some()); let table = table.unwrap(); assert_eq!( table.lookup(0, &bases), Err(Error::VariableLengthSearchTable) ); } #[test] fn test_eh_frame_hdr_indirect_length() { let section = Section::with_endian(Endian::Little) .L8(1) .L8(0x0b) .L8(0x83) .L8(0x0b) .L32(0x12345) .L32(2); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::UnsupportedPointerEncoding); } #[test] fn test_eh_frame_hdr_indirect_ptrs() { let section = Section::with_endian(Endian::Little) .L8(1) .L8(0x8b) .L8(0x03) .L8(0x8b) .L32(0x12345) .L32(2) .L32(10) .L32(1) .L32(20) .L32(2); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(result.is_ok()); let result = result.unwrap(); assert_eq!(result.eh_frame_ptr(), Pointer::Indirect(0x12345)); let table = result.table(); assert!(table.is_some()); let table = table.unwrap(); assert_eq!( table.lookup(0, &bases), Err(Error::UnsupportedPointerEncoding) ); } #[test] fn test_eh_frame_hdr_good() { let section = Section::with_endian(Endian::Little) .L8(1) .L8(0x0b) .L8(0x03) .L8(0x0b) .L32(0x12345) .L32(2) .L32(10) .L32(1) .L32(20) .L32(2); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(result.is_ok()); let result = result.unwrap(); assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); let table = result.table(); assert!(table.is_some()); let table = table.unwrap(); assert_eq!(table.lookup(0, &bases), Ok(Pointer::Direct(1))); assert_eq!(table.lookup(9, &bases), Ok(Pointer::Direct(1))); assert_eq!(table.lookup(10, &bases), Ok(Pointer::Direct(1))); assert_eq!(table.lookup(11, &bases), Ok(Pointer::Direct(1))); assert_eq!(table.lookup(19, &bases), Ok(Pointer::Direct(1))); assert_eq!(table.lookup(20, &bases), Ok(Pointer::Direct(2))); assert_eq!(table.lookup(21, &bases), Ok(Pointer::Direct(2))); assert_eq!(table.lookup(100_000, &bases), Ok(Pointer::Direct(2))); } #[test] fn test_eh_frame_fde_for_address_good() { // First, setup eh_frame // Write the CIE first so that its length gets set before we clone it // into the FDE. let mut cie = make_test_cie(); cie.format = Format::Dwarf32; cie.version = 1; let start_of_cie = Label::new(); let end_of_cie = Label::new(); let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .append_repeated(0, 16) .mark(&start_of_cie) .cie(kind, None, &mut cie) .mark(&end_of_cie); let mut fde1 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 9, address_range: 4, augmentation: None, instructions: EndianSlice::new(&[], LittleEndian), }; let mut fde2 = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 20, address_range: 8, augmentation: None, instructions: EndianSlice::new(&[], LittleEndian), }; let start_of_fde1 = Label::new(); let start_of_fde2 = Label::new(); let section = section // +4 for the FDE length before the CIE offset. .mark(&start_of_fde1) .fde(kind, (&start_of_fde1 - &start_of_cie + 4) as u64, &mut fde1) .mark(&start_of_fde2) .fde(kind, (&start_of_fde2 - &start_of_cie + 4) as u64, &mut fde2); section.start().set_const(0); let section = section.get_contents().unwrap(); let eh_frame = kind.section(§ion); // Setup eh_frame_hdr let section = Section::with_endian(kind.endian()) .L8(1) .L8(0x0b) .L8(0x03) .L8(0x0b) .L32(0x12345) .L32(2) .L32(10) .L32(0x12345 + start_of_fde1.value().unwrap() as u32) .L32(20) .L32(0x12345 + start_of_fde2.value().unwrap() as u32); let section = section.get_contents().unwrap(); let bases = BaseAddresses::default(); let eh_frame_hdr = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); assert!(eh_frame_hdr.is_ok()); let eh_frame_hdr = eh_frame_hdr.unwrap(); let table = eh_frame_hdr.table(); assert!(table.is_some()); let table = table.unwrap(); let bases = Default::default(); let mut iter = table.iter(&bases); assert_eq!( iter.next(), Ok(Some(( Pointer::Direct(10), Pointer::Direct(0x12345 + start_of_fde1.value().unwrap()) ))) ); assert_eq!( iter.next(), Ok(Some(( Pointer::Direct(20), Pointer::Direct(0x12345 + start_of_fde2.value().unwrap()) ))) ); assert_eq!(iter.next(), Ok(None)); assert_eq!( table.iter(&bases).nth(0), Ok(Some(( Pointer::Direct(10), Pointer::Direct(0x12345 + start_of_fde1.value().unwrap()) ))) ); assert_eq!( table.iter(&bases).nth(1), Ok(Some(( Pointer::Direct(20), Pointer::Direct(0x12345 + start_of_fde2.value().unwrap()) ))) ); assert_eq!(table.iter(&bases).nth(2), Ok(None)); let f = |_: &_, _: &_, o: EhFrameOffset| { assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); Ok(cie.clone()) }; assert_eq!( table.fde_for_address(&eh_frame, &bases, 9, f), Ok(fde1.clone()) ); assert_eq!( table.fde_for_address(&eh_frame, &bases, 10, f), Ok(fde1.clone()) ); assert_eq!(table.fde_for_address(&eh_frame, &bases, 11, f), Ok(fde1)); assert_eq!( table.fde_for_address(&eh_frame, &bases, 19, f), Err(Error::NoUnwindInfoForAddress) ); assert_eq!( table.fde_for_address(&eh_frame, &bases, 20, f), Ok(fde2.clone()) ); assert_eq!(table.fde_for_address(&eh_frame, &bases, 21, f), Ok(fde2)); assert_eq!( table.fde_for_address(&eh_frame, &bases, 100_000, f), Err(Error::NoUnwindInfoForAddress) ); } #[test] fn test_eh_frame_stops_at_zero_length() { let mut cie = make_test_cie(); let kind = eh_frame_le(); let section = Section::with_endian(Endian::Little) .L32(0) .cie(kind, None, &mut cie) .L32(0); let contents = section.get_contents().unwrap(); let eh_frame = kind.section(&contents); let bases = Default::default(); let mut entries = eh_frame.entries(&bases); assert_eq!(entries.next(), Ok(None)); assert_eq!( eh_frame.cie_from_offset(&bases, EhFrameOffset(0)), Err(Error::NoEntryAtGivenOffset) ); } #[test] fn test_debug_frame_skips_zero_length() { let mut cie = make_test_cie(); let kind = debug_frame_le(); let section = Section::with_endian(Endian::Little) .L32(0) .cie(kind, None, &mut cie) .L32(0); let contents = section.get_contents().unwrap(); let debug_frame = kind.section(&contents); let bases = Default::default(); let mut entries = debug_frame.entries(&bases); assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie)))); assert_eq!(entries.next(), Ok(None)); assert_eq!( debug_frame.cie_from_offset(&bases, DebugFrameOffset(0)), Err(Error::NoEntryAtGivenOffset) ); } fn resolve_cie_offset(buf: &[u8], cie_offset: usize) -> Result { let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf64, cie: make_test_cie(), initial_address: 0xfeed_beef, address_range: 39, augmentation: None, instructions: EndianSlice::new(&[], LittleEndian), }; let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .append_bytes(buf) .fde(kind, cie_offset as u64, &mut fde) .append_bytes(buf); let section = section.get_contents().unwrap(); let eh_frame = kind.section(§ion); let input = &mut EndianSlice::new(§ion[buf.len()..], LittleEndian); let bases = Default::default(); match parse_cfi_entry(&bases, &eh_frame, input) { Ok(Some(CieOrFde::Fde(partial))) => Ok(partial.cie_offset.0), Err(e) => Err(e), otherwise => panic!("Unexpected result: {:#?}", otherwise), } } #[test] fn test_eh_frame_resolve_cie_offset_ok() { let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; let cie_offset = 2; // + 4 for size of length field assert_eq!( resolve_cie_offset(&buf, buf.len() + 4 - cie_offset), Ok(cie_offset) ); } #[test] fn test_eh_frame_resolve_cie_offset_out_of_bounds() { let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; assert_eq!( resolve_cie_offset(&buf, buf.len() + 4 + 2), Err(Error::OffsetOutOfBounds) ); } #[test] fn test_eh_frame_resolve_cie_offset_underflow() { let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; assert_eq!( resolve_cie_offset(&buf, usize::MAX), Err(Error::OffsetOutOfBounds) ); } #[test] fn test_eh_frame_fde_ok() { let mut cie = make_test_cie(); cie.format = Format::Dwarf32; cie.version = 1; let start_of_cie = Label::new(); let end_of_cie = Label::new(); // Write the CIE first so that its length gets set before we clone it // into the FDE. let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .append_repeated(0, 16) .mark(&start_of_cie) .cie(kind, None, &mut cie) .mark(&end_of_cie); let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0xfeed_beef, address_range: 999, augmentation: None, instructions: EndianSlice::new(&[], LittleEndian), }; let section = section // +4 for the FDE length before the CIE offset. .fde(kind, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde); section.start().set_const(0); let section = section.get_contents().unwrap(); let eh_frame = kind.section(§ion); let section = EndianSlice::new(§ion, LittleEndian); let mut offset = None; let result = parse_fde( eh_frame, &mut section.range_from(end_of_cie.value().unwrap() as usize..), |_, _, o| { offset = Some(o); assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); Ok(cie.clone()) }, ); match result { Ok(actual) => assert_eq!(actual, fde), otherwise => panic!("Unexpected result {:?}", otherwise), } assert!(offset.is_some()); } #[test] fn test_eh_frame_fde_out_of_bounds() { let mut cie = make_test_cie(); cie.version = 1; let end_of_cie = Label::new(); let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf64, cie: cie.clone(), initial_address: 0xfeed_beef, address_range: 999, augmentation: None, instructions: EndianSlice::new(&[], LittleEndian), }; let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .cie(kind, None, &mut cie) .mark(&end_of_cie) .fde(kind, 99_999_999_999_999, &mut fde); section.start().set_const(0); let section = section.get_contents().unwrap(); let eh_frame = kind.section(§ion); let section = EndianSlice::new(§ion, LittleEndian); let result = parse_fde( eh_frame, &mut section.range_from(end_of_cie.value().unwrap() as usize..), UnwindSection::cie_from_offset, ); assert_eq!(result, Err(Error::OffsetOutOfBounds)); } #[test] fn test_augmentation_parse_not_z_augmentation() { let augmentation = &mut EndianSlice::new(b"wtf", NativeEndian); let bases = Default::default(); let address_size = 8; let section = EhFrame::new(&[], NativeEndian); let input = &mut EndianSlice::new(&[], NativeEndian); assert_eq!( Augmentation::parse(augmentation, &bases, address_size, §ion, input), Err(Error::UnknownAugmentation) ); } #[test] fn test_augmentation_parse_just_signal_trampoline() { let aug_str = &mut EndianSlice::new(b"S", LittleEndian); let bases = Default::default(); let address_size = 8; let section = EhFrame::new(&[], LittleEndian); let input = &mut EndianSlice::new(&[], LittleEndian); let augmentation = Augmentation { is_signal_trampoline: true, ..Default::default() }; assert_eq!( Augmentation::parse(aug_str, &bases, address_size, §ion, input), Ok(augmentation) ); } #[test] fn test_augmentation_parse_unknown_part_of_z_augmentation() { // The 'Z' character is not defined by the z-style augmentation. let bases = Default::default(); let address_size = 8; let section = Section::with_endian(Endian::Little) .uleb(4) .append_repeated(4, 4) .get_contents() .unwrap(); let section = EhFrame::new(§ion, LittleEndian); let input = &mut section.section().clone(); let augmentation = &mut EndianSlice::new(b"zZ", LittleEndian); assert_eq!( Augmentation::parse(augmentation, &bases, address_size, §ion, input), Err(Error::UnknownAugmentation) ); } #[test] #[allow(non_snake_case)] fn test_augmentation_parse_L() { let bases = Default::default(); let address_size = 8; let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; let section = Section::with_endian(Endian::Little) .uleb(1) .D8(constants::DW_EH_PE_uleb128.0) .append_bytes(&rest) .get_contents() .unwrap(); let section = EhFrame::new(§ion, LittleEndian); let input = &mut section.section().clone(); let aug_str = &mut EndianSlice::new(b"zL", LittleEndian); let augmentation = Augmentation { lsda: Some(constants::DW_EH_PE_uleb128), ..Default::default() }; assert_eq!( Augmentation::parse(aug_str, &bases, address_size, §ion, input), Ok(augmentation) ); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] #[allow(non_snake_case)] fn test_augmentation_parse_P() { let bases = Default::default(); let address_size = 8; let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; let section = Section::with_endian(Endian::Little) .uleb(9) .D8(constants::DW_EH_PE_udata8.0) .L64(0xf00d_f00d) .append_bytes(&rest) .get_contents() .unwrap(); let section = EhFrame::new(§ion, LittleEndian); let input = &mut section.section().clone(); let aug_str = &mut EndianSlice::new(b"zP", LittleEndian); let augmentation = Augmentation { personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0xf00d_f00d))), ..Default::default() }; assert_eq!( Augmentation::parse(aug_str, &bases, address_size, §ion, input), Ok(augmentation) ); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] #[allow(non_snake_case)] fn test_augmentation_parse_R() { let bases = Default::default(); let address_size = 8; let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; let section = Section::with_endian(Endian::Little) .uleb(1) .D8(constants::DW_EH_PE_udata4.0) .append_bytes(&rest) .get_contents() .unwrap(); let section = EhFrame::new(§ion, LittleEndian); let input = &mut section.section().clone(); let aug_str = &mut EndianSlice::new(b"zR", LittleEndian); let augmentation = Augmentation { fde_address_encoding: Some(constants::DW_EH_PE_udata4), ..Default::default() }; assert_eq!( Augmentation::parse(aug_str, &bases, address_size, §ion, input), Ok(augmentation) ); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] #[allow(non_snake_case)] fn test_augmentation_parse_S() { let bases = Default::default(); let address_size = 8; let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; let section = Section::with_endian(Endian::Little) .uleb(0) .append_bytes(&rest) .get_contents() .unwrap(); let section = EhFrame::new(§ion, LittleEndian); let input = &mut section.section().clone(); let aug_str = &mut EndianSlice::new(b"zS", LittleEndian); let augmentation = Augmentation { is_signal_trampoline: true, ..Default::default() }; assert_eq!( Augmentation::parse(aug_str, &bases, address_size, §ion, input), Ok(augmentation) ); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] fn test_augmentation_parse_all() { let bases = Default::default(); let address_size = 8; let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; let section = Section::with_endian(Endian::Little) .uleb(1 + 9 + 1) // L .D8(constants::DW_EH_PE_uleb128.0) // P .D8(constants::DW_EH_PE_udata8.0) .L64(0x1bad_f00d) // R .D8(constants::DW_EH_PE_uleb128.0) .append_bytes(&rest) .get_contents() .unwrap(); let section = EhFrame::new(§ion, LittleEndian); let input = &mut section.section().clone(); let aug_str = &mut EndianSlice::new(b"zLPRS", LittleEndian); let augmentation = Augmentation { lsda: Some(constants::DW_EH_PE_uleb128), personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0x1bad_f00d))), fde_address_encoding: Some(constants::DW_EH_PE_uleb128), is_signal_trampoline: true, }; assert_eq!( Augmentation::parse(aug_str, &bases, address_size, §ion, input), Ok(augmentation) ); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] fn test_eh_frame_fde_no_augmentation() { let instrs = [1, 2, 3, 4]; let cie_offset = 1; let mut cie = make_test_cie(); cie.format = Format::Dwarf32; cie.version = 1; let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0xfeed_face, address_range: 9000, augmentation: None, instructions: EndianSlice::new(&instrs, LittleEndian), }; let rest = [1, 2, 3, 4]; let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .fde(kind, cie_offset, &mut fde) .append_bytes(&rest) .get_contents() .unwrap(); let section = kind.section(§ion); let input = &mut section.section().clone(); let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); assert_eq!(result, Ok(fde)); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] fn test_eh_frame_fde_empty_augmentation() { let instrs = [1, 2, 3, 4]; let cie_offset = 1; let mut cie = make_test_cie(); cie.format = Format::Dwarf32; cie.version = 1; cie.augmentation = Some(Augmentation::default()); let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0xfeed_face, address_range: 9000, augmentation: Some(AugmentationData::default()), instructions: EndianSlice::new(&instrs, LittleEndian), }; let rest = [1, 2, 3, 4]; let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .fde(kind, cie_offset, &mut fde) .append_bytes(&rest) .get_contents() .unwrap(); let section = kind.section(§ion); let input = &mut section.section().clone(); let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); assert_eq!(result, Ok(fde)); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] fn test_eh_frame_fde_lsda_augmentation() { let instrs = [1, 2, 3, 4]; let cie_offset = 1; let mut cie = make_test_cie(); cie.format = Format::Dwarf32; cie.version = 1; cie.augmentation = Some(Augmentation::default()); cie.augmentation.as_mut().unwrap().lsda = Some(constants::DW_EH_PE_absptr); let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0xfeed_face, address_range: 9000, augmentation: Some(AugmentationData { lsda: Some(Pointer::Direct(0x1122_3344)), }), instructions: EndianSlice::new(&instrs, LittleEndian), }; let rest = [1, 2, 3, 4]; let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .fde(kind, cie_offset, &mut fde) .append_bytes(&rest) .get_contents() .unwrap(); let section = kind.section(§ion); let input = &mut section.section().clone(); let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); assert_eq!(result, Ok(fde)); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] fn test_eh_frame_fde_lsda_function_relative() { let instrs = [1, 2, 3, 4]; let cie_offset = 1; let mut cie = make_test_cie(); cie.format = Format::Dwarf32; cie.version = 1; cie.augmentation = Some(Augmentation::default()); cie.augmentation.as_mut().unwrap().lsda = Some(constants::DW_EH_PE_funcrel | constants::DW_EH_PE_absptr); let mut fde = FrameDescriptionEntry { offset: 0, length: 0, format: Format::Dwarf32, cie: cie.clone(), initial_address: 0xfeed_face, address_range: 9000, augmentation: Some(AugmentationData { lsda: Some(Pointer::Direct(0xbeef)), }), instructions: EndianSlice::new(&instrs, LittleEndian), }; let rest = [1, 2, 3, 4]; let kind = eh_frame_le(); let section = Section::with_endian(kind.endian()) .append_repeated(10, 10) .fde(kind, cie_offset, &mut fde) .append_bytes(&rest) .get_contents() .unwrap(); let section = kind.section(§ion); let input = &mut section.section().range_from(10..); // Adjust the FDE's augmentation to be relative to the function. fde.augmentation.as_mut().unwrap().lsda = Some(Pointer::Direct(0xfeed_face + 0xbeef)); let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); assert_eq!(result, Ok(fde)); assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); } #[test] fn test_eh_frame_cie_personality_function_relative_bad_context() { let instrs = [1, 2, 3, 4]; let length = Label::new(); let start = Label::new(); let end = Label::new(); let aug_len = Label::new(); let aug_start = Label::new(); let aug_end = Label::new(); let section = Section::with_endian(Endian::Little) // Length .L32(&length) .mark(&start) // CIE ID .L32(0) // Version .D8(1) // Augmentation .append_bytes(b"zP\0") // Code alignment factor .uleb(1) // Data alignment factor .sleb(1) // Return address register .uleb(1) // Augmentation data length. This is a uleb, be we rely on the value // being less than 2^7 and therefore a valid uleb (can't use Label // with uleb). .D8(&aug_len) .mark(&aug_start) // Augmentation data. Personality encoding and then encoded pointer. .D8(constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_uleb128.0) .uleb(1) .mark(&aug_end) // Initial instructions .append_bytes(&instrs) .mark(&end); length.set_const((&end - &start) as u64); aug_len.set_const((&aug_end - &aug_start) as u64); let section = section.get_contents().unwrap(); let section = EhFrame::new(§ion, LittleEndian); let bases = BaseAddresses::default(); let mut iter = section.entries(&bases); assert_eq!(iter.next(), Err(Error::FuncRelativePointerInBadContext)); } #[test] fn register_rule_map_eq() { // Different order, but still equal. let map1: RegisterRuleMap = [ (Register(0), RegisterRule::SameValue), (Register(3), RegisterRule::Offset(1)), ] .iter() .collect(); let map2: RegisterRuleMap = [ (Register(3), RegisterRule::Offset(1)), (Register(0), RegisterRule::SameValue), ] .iter() .collect(); assert_eq!(map1, map2); assert_eq!(map2, map1); // Not equal. let map3: RegisterRuleMap = [ (Register(0), RegisterRule::SameValue), (Register(2), RegisterRule::Offset(1)), ] .iter() .collect(); let map4: RegisterRuleMap = [ (Register(3), RegisterRule::Offset(1)), (Register(0), RegisterRule::SameValue), ] .iter() .collect(); assert!(map3 != map4); assert!(map4 != map3); // One has undefined explicitly set, other implicitly has undefined. let mut map5 = RegisterRuleMap::::default(); map5.set(Register(0), RegisterRule::SameValue).unwrap(); map5.set(Register(0), RegisterRule::Undefined).unwrap(); let map6 = RegisterRuleMap::::default(); assert_eq!(map5, map6); assert_eq!(map6, map5); } #[test] fn iter_register_rules() { let row = UnwindTableRow:: { registers: [ (Register(0), RegisterRule::SameValue), (Register(1), RegisterRule::Offset(1)), (Register(2), RegisterRule::ValOffset(2)), ] .iter() .collect(), ..Default::default() }; let mut found0 = false; let mut found1 = false; let mut found2 = false; for &(register, ref rule) in row.registers() { match register.0 { 0 => { assert!(!found0); found0 = true; assert_eq!(*rule, RegisterRule::SameValue); } 1 => { assert!(!found1); found1 = true; assert_eq!(*rule, RegisterRule::Offset(1)); } 2 => { assert!(!found2); found2 = true; assert_eq!(*rule, RegisterRule::ValOffset(2)); } x => panic!("Unexpected register rule: ({}, {:?})", x, rule), } } assert!(found0); assert!(found1); assert!(found2); } #[test] #[cfg(target_pointer_width = "64")] fn size_of_unwind_ctx() { use core::mem; let size = mem::size_of::>(); let max_size = 30968; if size > max_size { assert_eq!(size, max_size); } } #[test] #[cfg(target_pointer_width = "64")] fn size_of_register_rule_map() { use core::mem; let size = mem::size_of::>(); let max_size = 6152; if size > max_size { assert_eq!(size, max_size); } } #[test] fn test_parse_pointer_encoding_ok() { use crate::endianity::NativeEndian; let expected = constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_pcrel; let input = [expected.0, 1, 2, 3, 4]; let input = &mut EndianSlice::new(&input, NativeEndian); assert_eq!(parse_pointer_encoding(input), Ok(expected)); assert_eq!(*input, EndianSlice::new(&[1, 2, 3, 4], NativeEndian)); } #[test] fn test_parse_pointer_encoding_bad_encoding() { use crate::endianity::NativeEndian; let expected = constants::DwEhPe((constants::DW_EH_PE_sdata8.0 + 1) | constants::DW_EH_PE_pcrel.0); let input = [expected.0, 1, 2, 3, 4]; let input = &mut EndianSlice::new(&input, NativeEndian); assert_eq!( Err(Error::UnknownPointerEncoding(expected)), parse_pointer_encoding(input) ); } #[test] fn test_parse_encoded_pointer_absptr() { let encoding = constants::DW_EH_PE_absptr; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .L32(0xf00d_f00d) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0xf00d_f00d)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_pcrel() { let encoding = constants::DW_EH_PE_pcrel; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .append_repeated(0, 0x10) .L32(0x1) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input.range_from(0x10..); let parameters = PointerEncodingParameters { bases: &BaseAddresses::default().set_eh_frame(0x100).eh_frame, func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x111)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_pcrel_undefined() { let encoding = constants::DW_EH_PE_pcrel; let input = Section::with_endian(Endian::Little).L32(0x1); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Err(Error::PcRelativePointerButSectionBaseIsUndefined) ); } #[test] fn test_parse_encoded_pointer_textrel() { let encoding = constants::DW_EH_PE_textrel; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .L32(0x1) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &BaseAddresses::default().set_text(0x10).eh_frame, func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x11)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_textrel_undefined() { let encoding = constants::DW_EH_PE_textrel; let input = Section::with_endian(Endian::Little).L32(0x1); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Err(Error::TextRelativePointerButTextBaseIsUndefined) ); } #[test] fn test_parse_encoded_pointer_datarel() { let encoding = constants::DW_EH_PE_datarel; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .L32(0x1) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &BaseAddresses::default().set_got(0x10).eh_frame, func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x11)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_datarel_undefined() { let encoding = constants::DW_EH_PE_datarel; let input = Section::with_endian(Endian::Little).L32(0x1); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Err(Error::DataRelativePointerButDataBaseIsUndefined) ); } #[test] fn test_parse_encoded_pointer_funcrel() { let encoding = constants::DW_EH_PE_funcrel; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .L32(0x1) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: Some(0x10), address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x11)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_funcrel_undefined() { let encoding = constants::DW_EH_PE_funcrel; let input = Section::with_endian(Endian::Little).L32(0x1); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Err(Error::FuncRelativePointerInBadContext) ); } #[test] fn test_parse_encoded_pointer_uleb128() { let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_uleb128; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .uleb(0x12_3456) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x12_3456)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_udata2() { let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata2; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .L16(0x1234) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x1234)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_udata4() { let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata4; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .L32(0x1234_5678) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x1234_5678)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_udata8() { let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata8; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .L64(0x1234_5678_1234_5678) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 8, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x1234_5678_1234_5678)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_sleb128() { let encoding = constants::DW_EH_PE_textrel | constants::DW_EH_PE_sleb128; let expected_rest = [1, 2, 3, 4]; let input = Section::with_endian(Endian::Little) .sleb(-0x1111) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &BaseAddresses::default().set_text(0x1111_1111).eh_frame, func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(0x1111_0000)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_sdata2() { let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata2; let expected_rest = [1, 2, 3, 4]; let expected = 0x111_i16; let input = Section::with_endian(Endian::Little) .L16(expected as u16) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(expected as u64)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_sdata4() { let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata4; let expected_rest = [1, 2, 3, 4]; let expected = 0x111_1111_i32; let input = Section::with_endian(Endian::Little) .L32(expected as u32) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(expected as u64)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_sdata8() { let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata8; let expected_rest = [1, 2, 3, 4]; let expected = -0x11_1111_1222_2222_i64; let input = Section::with_endian(Endian::Little) .L64(expected as u64) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 8, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Direct(expected as u64)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_parse_encoded_pointer_omit() { let encoding = constants::DW_EH_PE_omit; let input = Section::with_endian(Endian::Little).L32(0x1); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Err(Error::CannotParseOmitPointerEncoding) ); assert_eq!(rest, input); } #[test] fn test_parse_encoded_pointer_bad_encoding() { let encoding = constants::DwEhPe(constants::DW_EH_PE_sdata8.0 + 1); let input = Section::with_endian(Endian::Little).L32(0x1); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Err(Error::UnknownPointerEncoding(encoding)) ); } #[test] fn test_parse_encoded_pointer_aligned() { // FIXME: support this encoding! let encoding = constants::DW_EH_PE_aligned; let input = Section::with_endian(Endian::Little).L32(0x1); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Err(Error::UnsupportedPointerEncoding) ); } #[test] fn test_parse_encoded_pointer_indirect() { let expected_rest = [1, 2, 3, 4]; let encoding = constants::DW_EH_PE_indirect; let input = Section::with_endian(Endian::Little) .L32(0x1234_5678) .append_bytes(&expected_rest); let input = input.get_contents().unwrap(); let input = EndianSlice::new(&input, LittleEndian); let mut rest = input; let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 4, section: &input, }; assert_eq!( parse_encoded_pointer(encoding, ¶meters, &mut rest), Ok(Pointer::Indirect(0x1234_5678)) ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } #[test] fn test_unwind_context_reuse() { fn unwind_one(ctx: &mut UnwindContext, data: &[u8]) { let debug_frame = DebugFrame::new(data, NativeEndian); let bases = Default::default(); let result = debug_frame.unwind_info_for_address( &bases, ctx, 0xbadb_ad99, DebugFrame::cie_from_offset, ); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress); } // Use the same context for two different data lifetimes. let mut ctx: UnwindContext = UnwindContext::new(); { let data1 = vec![]; unwind_one(&mut ctx, &data1); } { let data2 = vec![]; unwind_one(&mut ctx, &data2); } } } gimli-0.31.1/src/read/dwarf.rs000064400000000000000000001727621046102023000142010ustar 00000000000000use alloc::string::String; use alloc::sync::Arc; use crate::common::{ DebugAddrBase, DebugAddrIndex, DebugInfoOffset, DebugLineStrOffset, DebugLocListsBase, DebugLocListsIndex, DebugRngListsBase, DebugRngListsIndex, DebugStrOffset, DebugStrOffsetsBase, DebugStrOffsetsIndex, DebugTypeSignature, DebugTypesOffset, DwarfFileType, DwoId, Encoding, LocationListsOffset, RangeListsOffset, RawRangeListsOffset, SectionId, UnitSectionOffset, }; use crate::constants; use crate::read::{ Abbreviations, AbbreviationsCache, AbbreviationsCacheStrategy, AttributeValue, DebugAbbrev, DebugAddr, DebugAranges, DebugCuIndex, DebugInfo, DebugInfoUnitHeadersIter, DebugLine, DebugLineStr, DebugLoc, DebugLocLists, DebugRanges, DebugRngLists, DebugStr, DebugStrOffsets, DebugTuIndex, DebugTypes, DebugTypesUnitHeadersIter, DebuggingInformationEntry, EntriesCursor, EntriesRaw, EntriesTree, Error, IncompleteLineProgram, IndexSectionId, LocListIter, LocationLists, Range, RangeLists, RawLocListIter, RawRngListIter, Reader, ReaderOffset, ReaderOffsetId, Result, RngListIter, Section, UnitHeader, UnitIndex, UnitIndexSectionIterator, UnitOffset, UnitType, }; /// All of the commonly used DWARF sections. /// /// This is useful for storing sections when `T` does not implement `Reader`. /// It can be used to create a `Dwarf` that references the data in `self`. /// If `T` does implement `Reader`, then use `Dwarf` directly. /// /// ## Example Usage /// /// It can be useful to load DWARF sections into owned data structures, /// such as `Vec`. However, we do not implement the `Reader` trait /// for `Vec`, because it would be very inefficient, but this trait /// is required for all of the methods that parse the DWARF data. /// So we first load the DWARF sections into `Vec`s, and then use /// `borrow` to create `Reader`s that reference the data. /// /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; /// // Read the DWARF sections into `Vec`s with whatever object loader you're using. /// let dwarf_sections: gimli::DwarfSections> = gimli::DwarfSections::load(loader)?; /// // Create references to the DWARF sections. /// let dwarf: gimli::Dwarf<_> = dwarf_sections.borrow(|section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// # unreachable!() /// # } /// ``` #[derive(Debug, Default)] pub struct DwarfSections { /// The `.debug_abbrev` section. pub debug_abbrev: DebugAbbrev, /// The `.debug_addr` section. pub debug_addr: DebugAddr, /// The `.debug_aranges` section. pub debug_aranges: DebugAranges, /// The `.debug_info` section. pub debug_info: DebugInfo, /// The `.debug_line` section. pub debug_line: DebugLine, /// The `.debug_line_str` section. pub debug_line_str: DebugLineStr, /// The `.debug_str` section. pub debug_str: DebugStr, /// The `.debug_str_offsets` section. pub debug_str_offsets: DebugStrOffsets, /// The `.debug_types` section. pub debug_types: DebugTypes, /// The `.debug_loc` section. pub debug_loc: DebugLoc, /// The `.debug_loclists` section. pub debug_loclists: DebugLocLists, /// The `.debug_ranges` section. pub debug_ranges: DebugRanges, /// The `.debug_rnglists` section. pub debug_rnglists: DebugRngLists, } impl DwarfSections { /// Try to load the DWARF sections using the given loader function. /// /// `section` loads a DWARF section from the object file. /// It should return an empty section if the section does not exist. pub fn load(mut section: F) -> core::result::Result where F: FnMut(SectionId) -> core::result::Result, { Ok(DwarfSections { // Section types are inferred. debug_abbrev: Section::load(&mut section)?, debug_addr: Section::load(&mut section)?, debug_aranges: Section::load(&mut section)?, debug_info: Section::load(&mut section)?, debug_line: Section::load(&mut section)?, debug_line_str: Section::load(&mut section)?, debug_str: Section::load(&mut section)?, debug_str_offsets: Section::load(&mut section)?, debug_types: Section::load(&mut section)?, debug_loc: Section::load(&mut section)?, debug_loclists: Section::load(&mut section)?, debug_ranges: Section::load(&mut section)?, debug_rnglists: Section::load(&mut section)?, }) } /// Create a `Dwarf` structure that references the data in `self`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> Dwarf where F: FnMut(&'a T) -> R, { Dwarf::from_sections(DwarfSections { debug_abbrev: self.debug_abbrev.borrow(&mut borrow), debug_addr: self.debug_addr.borrow(&mut borrow), debug_aranges: self.debug_aranges.borrow(&mut borrow), debug_info: self.debug_info.borrow(&mut borrow), debug_line: self.debug_line.borrow(&mut borrow), debug_line_str: self.debug_line_str.borrow(&mut borrow), debug_str: self.debug_str.borrow(&mut borrow), debug_str_offsets: self.debug_str_offsets.borrow(&mut borrow), debug_types: self.debug_types.borrow(&mut borrow), debug_loc: self.debug_loc.borrow(&mut borrow), debug_loclists: self.debug_loclists.borrow(&mut borrow), debug_ranges: self.debug_ranges.borrow(&mut borrow), debug_rnglists: self.debug_rnglists.borrow(&mut borrow), }) } /// Create a `Dwarf` structure that references the data in `self` and `sup`. /// /// This is like `borrow`, but also includes the supplementary object file. /// This is useful when `R` implements `Reader` but `T` does not. /// /// ## Example Usage /// /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; /// # let sup_loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; /// // Read the DWARF sections into `Vec`s with whatever object loader you're using. /// let dwarf_sections: gimli::DwarfSections> = gimli::DwarfSections::load(loader)?; /// let dwarf_sup_sections: gimli::DwarfSections> = gimli::DwarfSections::load(sup_loader)?; /// // Create references to the DWARF sections. /// let dwarf = dwarf_sections.borrow_with_sup(&dwarf_sup_sections, |section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// # unreachable!() /// # } /// ``` pub fn borrow_with_sup<'a, F, R>(&'a self, sup: &'a Self, mut borrow: F) -> Dwarf where F: FnMut(&'a T) -> R, { let mut dwarf = self.borrow(&mut borrow); dwarf.set_sup(sup.borrow(&mut borrow)); dwarf } } /// All of the commonly used DWARF sections, and other common information. #[derive(Debug, Default)] pub struct Dwarf { /// The `.debug_abbrev` section. pub debug_abbrev: DebugAbbrev, /// The `.debug_addr` section. pub debug_addr: DebugAddr, /// The `.debug_aranges` section. pub debug_aranges: DebugAranges, /// The `.debug_info` section. pub debug_info: DebugInfo, /// The `.debug_line` section. pub debug_line: DebugLine, /// The `.debug_line_str` section. pub debug_line_str: DebugLineStr, /// The `.debug_str` section. pub debug_str: DebugStr, /// The `.debug_str_offsets` section. pub debug_str_offsets: DebugStrOffsets, /// The `.debug_types` section. pub debug_types: DebugTypes, /// The location lists in the `.debug_loc` and `.debug_loclists` sections. pub locations: LocationLists, /// The range lists in the `.debug_ranges` and `.debug_rnglists` sections. pub ranges: RangeLists, /// The type of this file. pub file_type: DwarfFileType, /// The DWARF sections for a supplementary object file. pub sup: Option>>, /// A cache of previously parsed abbreviations for units in this file. pub abbreviations_cache: AbbreviationsCache, } impl Dwarf { /// Try to load the DWARF sections using the given loader function. /// /// `section` loads a DWARF section from the object file. /// It should return an empty section if the section does not exist. /// /// After loading, the user should set the `file_type` field and /// call `load_sup` if required. pub fn load(section: F) -> core::result::Result where F: FnMut(SectionId) -> core::result::Result, { let sections = DwarfSections::load(section)?; Ok(Self::from_sections(sections)) } /// Load the DWARF sections from the supplementary object file. /// /// `section` operates the same as for `load`. /// /// Sets `self.sup`, replacing any previous value. pub fn load_sup(&mut self, section: F) -> core::result::Result<(), E> where F: FnMut(SectionId) -> core::result::Result, { self.set_sup(Self::load(section)?); Ok(()) } /// Create a `Dwarf` structure from the given sections. /// /// The caller should set the `file_type` and `sup` fields if required. fn from_sections(sections: DwarfSections) -> Self { Dwarf { debug_abbrev: sections.debug_abbrev, debug_addr: sections.debug_addr, debug_aranges: sections.debug_aranges, debug_info: sections.debug_info, debug_line: sections.debug_line, debug_line_str: sections.debug_line_str, debug_str: sections.debug_str, debug_str_offsets: sections.debug_str_offsets, debug_types: sections.debug_types, locations: LocationLists::new(sections.debug_loc, sections.debug_loclists), ranges: RangeLists::new(sections.debug_ranges, sections.debug_rnglists), file_type: DwarfFileType::Main, sup: None, abbreviations_cache: AbbreviationsCache::new(), } } /// Create a `Dwarf` structure that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// ## Example Usage /// /// It can be useful to load DWARF sections into owned data structures, /// such as `Vec`. However, we do not implement the `Reader` trait /// for `Vec`, because it would be very inefficient, but this trait /// is required for all of the methods that parse the DWARF data. /// So we first load the DWARF sections into `Vec`s, and then use /// `borrow` to create `Reader`s that reference the data. /// /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; /// # let sup_loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; /// // Read the DWARF sections into `Vec`s with whatever object loader you're using. /// let mut owned_dwarf: gimli::Dwarf> = gimli::Dwarf::load(loader)?; /// owned_dwarf.load_sup(sup_loader)?; /// // Create references to the DWARF sections. /// let dwarf = owned_dwarf.borrow(|section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// # unreachable!() /// # } /// ``` #[deprecated(note = "use `DwarfSections::borrow` instead")] pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> Dwarf where F: FnMut(&'a T) -> R, { Dwarf { debug_abbrev: self.debug_abbrev.borrow(&mut borrow), debug_addr: self.debug_addr.borrow(&mut borrow), debug_aranges: self.debug_aranges.borrow(&mut borrow), debug_info: self.debug_info.borrow(&mut borrow), debug_line: self.debug_line.borrow(&mut borrow), debug_line_str: self.debug_line_str.borrow(&mut borrow), debug_str: self.debug_str.borrow(&mut borrow), debug_str_offsets: self.debug_str_offsets.borrow(&mut borrow), debug_types: self.debug_types.borrow(&mut borrow), locations: self.locations.borrow(&mut borrow), ranges: self.ranges.borrow(&mut borrow), file_type: self.file_type, sup: self.sup().map(|sup| Arc::new(sup.borrow(borrow))), abbreviations_cache: AbbreviationsCache::new(), } } /// Store the DWARF sections for the supplementary object file. pub fn set_sup(&mut self, sup: Dwarf) { self.sup = Some(Arc::new(sup)); } /// Return a reference to the DWARF sections for the supplementary object file. pub fn sup(&self) -> Option<&Dwarf> { self.sup.as_ref().map(Arc::as_ref) } } impl Dwarf { /// Parse abbreviations and store them in the cache. /// /// This will iterate over the units in `self.debug_info` to determine the /// abbreviations offsets. /// /// Errors during parsing abbreviations are also stored in the cache. /// Errors during iterating over the units are ignored. pub fn populate_abbreviations_cache(&mut self, strategy: AbbreviationsCacheStrategy) { self.abbreviations_cache .populate(strategy, &self.debug_abbrev, self.debug_info.units()); } /// Iterate the unit headers in the `.debug_info` section. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[inline] pub fn units(&self) -> DebugInfoUnitHeadersIter { self.debug_info.units() } /// Construct a new `Unit` from the given unit header. #[inline] pub fn unit(&self, header: UnitHeader) -> Result> { Unit::new(self, header) } /// Iterate the type-unit headers in the `.debug_types` section. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[inline] pub fn type_units(&self) -> DebugTypesUnitHeadersIter { self.debug_types.units() } /// Parse the abbreviations for a compilation unit. #[inline] pub fn abbreviations(&self, unit: &UnitHeader) -> Result> { self.abbreviations_cache .get(&self.debug_abbrev, unit.debug_abbrev_offset()) } /// Return the string offset at the given index. #[inline] pub fn string_offset( &self, unit: &Unit, index: DebugStrOffsetsIndex, ) -> Result> { self.debug_str_offsets .get_str_offset(unit.header.format(), unit.str_offsets_base, index) } /// Return the string at the given offset in `.debug_str`. #[inline] pub fn string(&self, offset: DebugStrOffset) -> Result { self.debug_str.get_str(offset) } /// Return the string at the given offset in `.debug_line_str`. #[inline] pub fn line_string(&self, offset: DebugLineStrOffset) -> Result { self.debug_line_str.get_str(offset) } /// Return the string at the given offset in the `.debug_str` /// in the supplementary object file. #[inline] pub fn sup_string(&self, offset: DebugStrOffset) -> Result { if let Some(sup) = self.sup() { sup.debug_str.get_str(offset) } else { Err(Error::ExpectedStringAttributeValue) } } /// Return an attribute value as a string slice. /// /// If the attribute value is one of: /// /// - an inline `DW_FORM_string` string /// - a `DW_FORM_strp` reference to an offset into the `.debug_str` section /// - a `DW_FORM_strp_sup` reference to an offset into a supplementary /// object file /// - a `DW_FORM_line_strp` reference to an offset into the `.debug_line_str` /// section /// - a `DW_FORM_strx` index into the `.debug_str_offsets` entries for the unit /// /// then return the attribute's string value. Returns an error if the attribute /// value does not have a string form, or if a string form has an invalid value. pub fn attr_string(&self, unit: &Unit, attr: AttributeValue) -> Result { match attr { AttributeValue::String(string) => Ok(string), AttributeValue::DebugStrRef(offset) => self.string(offset), AttributeValue::DebugStrRefSup(offset) => self.sup_string(offset), AttributeValue::DebugLineStrRef(offset) => self.line_string(offset), AttributeValue::DebugStrOffsetsIndex(index) => { let offset = self.string_offset(unit, index)?; self.string(offset) } _ => Err(Error::ExpectedStringAttributeValue), } } /// Return the address at the given index. pub fn address(&self, unit: &Unit, index: DebugAddrIndex) -> Result { self.debug_addr .get_address(unit.encoding().address_size, unit.addr_base, index) } /// Try to return an attribute value as an address. /// /// If the attribute value is one of: /// /// - a `DW_FORM_addr` /// - a `DW_FORM_addrx` index into the `.debug_addr` entries for the unit /// /// then return the address. /// Returns `None` for other forms. pub fn attr_address(&self, unit: &Unit, attr: AttributeValue) -> Result> { match attr { AttributeValue::Addr(addr) => Ok(Some(addr)), AttributeValue::DebugAddrIndex(index) => self.address(unit, index).map(Some), _ => Ok(None), } } /// Return the range list offset for the given raw offset. /// /// This handles adding `DW_AT_GNU_ranges_base` if required. pub fn ranges_offset_from_raw( &self, unit: &Unit, offset: RawRangeListsOffset, ) -> RangeListsOffset { if self.file_type == DwarfFileType::Dwo && unit.header.version() < 5 { RangeListsOffset(offset.0.wrapping_add(unit.rnglists_base.0)) } else { RangeListsOffset(offset.0) } } /// Return the range list offset at the given index. pub fn ranges_offset( &self, unit: &Unit, index: DebugRngListsIndex, ) -> Result> { self.ranges .get_offset(unit.encoding(), unit.rnglists_base, index) } /// Iterate over the `RangeListEntry`s starting at the given offset. pub fn ranges( &self, unit: &Unit, offset: RangeListsOffset, ) -> Result> { self.ranges.ranges( offset, unit.encoding(), unit.low_pc, &self.debug_addr, unit.addr_base, ) } /// Iterate over the `RawRngListEntry`ies starting at the given offset. pub fn raw_ranges( &self, unit: &Unit, offset: RangeListsOffset, ) -> Result> { self.ranges.raw_ranges(offset, unit.encoding()) } /// Try to return an attribute value as a range list offset. /// /// If the attribute value is one of: /// /// - a `DW_FORM_sec_offset` reference to the `.debug_ranges` or `.debug_rnglists` sections /// - a `DW_FORM_rnglistx` index into the `.debug_rnglists` entries for the unit /// /// then return the range list offset of the range list. /// Returns `None` for other forms. pub fn attr_ranges_offset( &self, unit: &Unit, attr: AttributeValue, ) -> Result>> { match attr { AttributeValue::RangeListsRef(offset) => { Ok(Some(self.ranges_offset_from_raw(unit, offset))) } AttributeValue::DebugRngListsIndex(index) => self.ranges_offset(unit, index).map(Some), _ => Ok(None), } } /// Try to return an attribute value as a range list entry iterator. /// /// If the attribute value is one of: /// /// - a `DW_FORM_sec_offset` reference to the `.debug_ranges` or `.debug_rnglists` sections /// - a `DW_FORM_rnglistx` index into the `.debug_rnglists` entries for the unit /// /// then return an iterator over the entries in the range list. /// Returns `None` for other forms. pub fn attr_ranges( &self, unit: &Unit, attr: AttributeValue, ) -> Result>> { match self.attr_ranges_offset(unit, attr)? { Some(offset) => Ok(Some(self.ranges(unit, offset)?)), None => Ok(None), } } /// Return an iterator for the address ranges of a `DebuggingInformationEntry`. /// /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges`. pub fn die_ranges( &self, unit: &Unit, entry: &DebuggingInformationEntry<'_, '_, R>, ) -> Result> { let mut low_pc = None; let mut high_pc = None; let mut size = None; let mut attrs = entry.attrs(); while let Some(attr) = attrs.next()? { match attr.name() { constants::DW_AT_low_pc => { low_pc = Some( self.attr_address(unit, attr.value())? .ok_or(Error::UnsupportedAttributeForm)?, ); } constants::DW_AT_high_pc => match attr.value() { AttributeValue::Udata(val) => size = Some(val), attr => { high_pc = Some( self.attr_address(unit, attr)? .ok_or(Error::UnsupportedAttributeForm)?, ); } }, constants::DW_AT_ranges => { if let Some(list) = self.attr_ranges(unit, attr.value())? { return Ok(RangeIter(RangeIterInner::List(list))); } } _ => {} } } let range = low_pc.and_then(|begin| { let end = size.map(|size| begin + size).or(high_pc); // TODO: perhaps return an error if `end` is `None` end.map(|end| Range { begin, end }) }); Ok(RangeIter(RangeIterInner::Single(range))) } /// Return an iterator for the address ranges of a `Unit`. /// /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges` of the /// root `DebuggingInformationEntry`. pub fn unit_ranges(&self, unit: &Unit) -> Result> { let mut cursor = unit.header.entries(&unit.abbreviations); cursor.next_dfs()?; let root = cursor.current().ok_or(Error::MissingUnitDie)?; self.die_ranges(unit, root) } /// Return the location list offset at the given index. pub fn locations_offset( &self, unit: &Unit, index: DebugLocListsIndex, ) -> Result> { self.locations .get_offset(unit.encoding(), unit.loclists_base, index) } /// Iterate over the `LocationListEntry`s starting at the given offset. pub fn locations( &self, unit: &Unit, offset: LocationListsOffset, ) -> Result> { match self.file_type { DwarfFileType::Main => self.locations.locations( offset, unit.encoding(), unit.low_pc, &self.debug_addr, unit.addr_base, ), DwarfFileType::Dwo => self.locations.locations_dwo( offset, unit.encoding(), unit.low_pc, &self.debug_addr, unit.addr_base, ), } } /// Iterate over the raw `LocationListEntry`s starting at the given offset. pub fn raw_locations( &self, unit: &Unit, offset: LocationListsOffset, ) -> Result> { match self.file_type { DwarfFileType::Main => self.locations.raw_locations(offset, unit.encoding()), DwarfFileType::Dwo => self.locations.raw_locations_dwo(offset, unit.encoding()), } } /// Try to return an attribute value as a location list offset. /// /// If the attribute value is one of: /// /// - a `DW_FORM_sec_offset` reference to the `.debug_loc` or `.debug_loclists` sections /// - a `DW_FORM_loclistx` index into the `.debug_loclists` entries for the unit /// /// then return the location list offset of the location list. /// Returns `None` for other forms. pub fn attr_locations_offset( &self, unit: &Unit, attr: AttributeValue, ) -> Result>> { match attr { AttributeValue::LocationListsRef(offset) => Ok(Some(offset)), AttributeValue::DebugLocListsIndex(index) => { self.locations_offset(unit, index).map(Some) } _ => Ok(None), } } /// Try to return an attribute value as a location list entry iterator. /// /// If the attribute value is one of: /// /// - a `DW_FORM_sec_offset` reference to the `.debug_loc` or `.debug_loclists` sections /// - a `DW_FORM_loclistx` index into the `.debug_loclists` entries for the unit /// /// then return an iterator over the entries in the location list. /// Returns `None` for other forms. pub fn attr_locations( &self, unit: &Unit, attr: AttributeValue, ) -> Result>> { match self.attr_locations_offset(unit, attr)? { Some(offset) => Ok(Some(self.locations(unit, offset)?)), None => Ok(None), } } /// Call `Reader::lookup_offset_id` for each section, and return the first match. /// /// The first element of the tuple is `true` for supplementary sections. pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(bool, SectionId, R::Offset)> { None.or_else(|| self.debug_abbrev.lookup_offset_id(id)) .or_else(|| self.debug_addr.lookup_offset_id(id)) .or_else(|| self.debug_aranges.lookup_offset_id(id)) .or_else(|| self.debug_info.lookup_offset_id(id)) .or_else(|| self.debug_line.lookup_offset_id(id)) .or_else(|| self.debug_line_str.lookup_offset_id(id)) .or_else(|| self.debug_str.lookup_offset_id(id)) .or_else(|| self.debug_str_offsets.lookup_offset_id(id)) .or_else(|| self.debug_types.lookup_offset_id(id)) .or_else(|| self.locations.lookup_offset_id(id)) .or_else(|| self.ranges.lookup_offset_id(id)) .map(|(id, offset)| (false, id, offset)) .or_else(|| { self.sup() .and_then(|sup| sup.lookup_offset_id(id)) .map(|(_, id, offset)| (true, id, offset)) }) } /// Returns a string representation of the given error. /// /// This uses information from the DWARF sections to provide more information in some cases. pub fn format_error(&self, err: Error) -> String { #[allow(clippy::single_match)] match err { Error::UnexpectedEof(id) => match self.lookup_offset_id(id) { Some((sup, section, offset)) => { return format!( "{} at {}{}+0x{:x}", err, section.name(), if sup { "(sup)" } else { "" }, offset.into_u64(), ); } None => {} }, _ => {} } err.description().into() } } impl Dwarf { /// Assuming `self` was loaded from a .dwo, take the appropriate /// sections from `parent` (which contains the skeleton unit for this /// dwo) such as `.debug_addr` and merge them into this `Dwarf`. pub fn make_dwo(&mut self, parent: &Dwarf) { self.file_type = DwarfFileType::Dwo; // These sections are always taken from the parent file and not the dwo. self.debug_addr = parent.debug_addr.clone(); // .debug_rnglists comes from the DWO, .debug_ranges comes from the // parent file. self.ranges .set_debug_ranges(parent.ranges.debug_ranges().clone()); self.sup.clone_from(&parent.sup); } } /// The sections from a `.dwp` file. /// /// This is useful for storing sections when `T` does not implement `Reader`. /// It can be used to create a `DwarfPackage` that references the data in `self`. /// If `T` does implement `Reader`, then use `DwarfPackage` directly. /// /// ## Example Usage /// /// It can be useful to load DWARF sections into owned data structures, /// such as `Vec`. However, we do not implement the `Reader` trait /// for `Vec`, because it would be very inefficient, but this trait /// is required for all of the methods that parse the DWARF data. /// So we first load the DWARF sections into `Vec`s, and then use /// `borrow` to create `Reader`s that reference the data. /// /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; /// // Read the DWARF sections into `Vec`s with whatever object loader you're using. /// let dwp_sections: gimli::DwarfPackageSections> = gimli::DwarfPackageSections::load(loader)?; /// // Create references to the DWARF sections. /// let dwp: gimli::DwarfPackage<_> = dwp_sections.borrow( /// |section| gimli::EndianSlice::new(§ion, gimli::LittleEndian), /// gimli::EndianSlice::new(&[], gimli::LittleEndian), /// )?; /// # unreachable!() /// # } /// ``` #[derive(Debug, Default)] pub struct DwarfPackageSections { /// The `.debug_cu_index` section. pub cu_index: DebugCuIndex, /// The `.debug_tu_index` section. pub tu_index: DebugTuIndex, /// The `.debug_abbrev.dwo` section. pub debug_abbrev: DebugAbbrev, /// The `.debug_info.dwo` section. pub debug_info: DebugInfo, /// The `.debug_line.dwo` section. pub debug_line: DebugLine, /// The `.debug_str.dwo` section. pub debug_str: DebugStr, /// The `.debug_str_offsets.dwo` section. pub debug_str_offsets: DebugStrOffsets, /// The `.debug_loc.dwo` section. /// /// Only present when using GNU split-dwarf extension to DWARF 4. pub debug_loc: DebugLoc, /// The `.debug_loclists.dwo` section. pub debug_loclists: DebugLocLists, /// The `.debug_rnglists.dwo` section. pub debug_rnglists: DebugRngLists, /// The `.debug_types.dwo` section. /// /// Only present when using GNU split-dwarf extension to DWARF 4. pub debug_types: DebugTypes, } impl DwarfPackageSections { /// Try to load the `.dwp` sections using the given loader function. /// /// `section` loads a DWARF section from the object file. /// It should return an empty section if the section does not exist. pub fn load(mut section: F) -> core::result::Result where F: FnMut(SectionId) -> core::result::Result, E: From, { Ok(DwarfPackageSections { // Section types are inferred. cu_index: Section::load(&mut section)?, tu_index: Section::load(&mut section)?, debug_abbrev: Section::load(&mut section)?, debug_info: Section::load(&mut section)?, debug_line: Section::load(&mut section)?, debug_str: Section::load(&mut section)?, debug_str_offsets: Section::load(&mut section)?, debug_loc: Section::load(&mut section)?, debug_loclists: Section::load(&mut section)?, debug_rnglists: Section::load(&mut section)?, debug_types: Section::load(&mut section)?, }) } /// Create a `DwarfPackage` structure that references the data in `self`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F, empty: R) -> Result> where F: FnMut(&'a T) -> R, R: Reader, { DwarfPackage::from_sections( DwarfPackageSections { cu_index: self.cu_index.borrow(&mut borrow), tu_index: self.tu_index.borrow(&mut borrow), debug_abbrev: self.debug_abbrev.borrow(&mut borrow), debug_info: self.debug_info.borrow(&mut borrow), debug_line: self.debug_line.borrow(&mut borrow), debug_str: self.debug_str.borrow(&mut borrow), debug_str_offsets: self.debug_str_offsets.borrow(&mut borrow), debug_loc: self.debug_loc.borrow(&mut borrow), debug_loclists: self.debug_loclists.borrow(&mut borrow), debug_rnglists: self.debug_rnglists.borrow(&mut borrow), debug_types: self.debug_types.borrow(&mut borrow), }, empty, ) } } /// The sections from a `.dwp` file, with parsed indices. #[derive(Debug)] pub struct DwarfPackage { /// The compilation unit index in the `.debug_cu_index` section. pub cu_index: UnitIndex, /// The type unit index in the `.debug_tu_index` section. pub tu_index: UnitIndex, /// The `.debug_abbrev.dwo` section. pub debug_abbrev: DebugAbbrev, /// The `.debug_info.dwo` section. pub debug_info: DebugInfo, /// The `.debug_line.dwo` section. pub debug_line: DebugLine, /// The `.debug_str.dwo` section. pub debug_str: DebugStr, /// The `.debug_str_offsets.dwo` section. pub debug_str_offsets: DebugStrOffsets, /// The `.debug_loc.dwo` section. /// /// Only present when using GNU split-dwarf extension to DWARF 4. pub debug_loc: DebugLoc, /// The `.debug_loclists.dwo` section. pub debug_loclists: DebugLocLists, /// The `.debug_rnglists.dwo` section. pub debug_rnglists: DebugRngLists, /// The `.debug_types.dwo` section. /// /// Only present when using GNU split-dwarf extension to DWARF 4. pub debug_types: DebugTypes, /// An empty section. /// /// Used when creating `Dwarf`. pub empty: R, } impl DwarfPackage { /// Try to load the `.dwp` sections using the given loader function. /// /// `section` loads a DWARF section from the object file. /// It should return an empty section if the section does not exist. pub fn load(section: F, empty: R) -> core::result::Result where F: FnMut(SectionId) -> core::result::Result, E: From, { let sections = DwarfPackageSections::load(section)?; Ok(Self::from_sections(sections, empty)?) } /// Create a `DwarfPackage` structure from the given sections. fn from_sections(sections: DwarfPackageSections, empty: R) -> Result { Ok(DwarfPackage { cu_index: sections.cu_index.index()?, tu_index: sections.tu_index.index()?, debug_abbrev: sections.debug_abbrev, debug_info: sections.debug_info, debug_line: sections.debug_line, debug_str: sections.debug_str, debug_str_offsets: sections.debug_str_offsets, debug_loc: sections.debug_loc, debug_loclists: sections.debug_loclists, debug_rnglists: sections.debug_rnglists, debug_types: sections.debug_types, empty, }) } /// Find the compilation unit with the given DWO identifier and return its section /// contributions. /// /// ## Example Usage /// /// ```rust,no_run /// # fn example( /// # dwarf: &gimli::Dwarf, /// # dwp: &gimli::DwarfPackage, /// # dwo_id: gimli::DwoId, /// # ) -> Result<(), gimli::Error> { /// if let Some(dwo) = dwp.find_cu(dwo_id, dwarf)? { /// let dwo_header = dwo.units().next()?.expect("DWO should have one unit"); /// let dwo_unit = dwo.unit(dwo_header)?; /// // Do something with `dwo_unit`. /// } /// # unreachable!() /// # } pub fn find_cu(&self, id: DwoId, parent: &Dwarf) -> Result>> { let row = match self.cu_index.find(id.0) { Some(row) => row, None => return Ok(None), }; self.cu_sections(row, parent).map(Some) } /// Find the type unit with the given type signature and return its section /// contributions. pub fn find_tu( &self, signature: DebugTypeSignature, parent: &Dwarf, ) -> Result>> { let row = match self.tu_index.find(signature.0) { Some(row) => row, None => return Ok(None), }; self.tu_sections(row, parent).map(Some) } /// Return the section contributions of the compilation unit at the given index. /// /// The index must be in the range `1..cu_index.unit_count`. /// /// This function should only be needed by low level parsers. pub fn cu_sections(&self, index: u32, parent: &Dwarf) -> Result> { self.sections(self.cu_index.sections(index)?, parent) } /// Return the section contributions of the compilation unit at the given index. /// /// The index must be in the range `1..tu_index.unit_count`. /// /// This function should only be needed by low level parsers. pub fn tu_sections(&self, index: u32, parent: &Dwarf) -> Result> { self.sections(self.tu_index.sections(index)?, parent) } /// Return the section contributions of a unit. /// /// This function should only be needed by low level parsers. pub fn sections( &self, sections: UnitIndexSectionIterator<'_, R>, parent: &Dwarf, ) -> Result> { let mut abbrev_offset = 0; let mut abbrev_size = 0; let mut info_offset = 0; let mut info_size = 0; let mut line_offset = 0; let mut line_size = 0; let mut loc_offset = 0; let mut loc_size = 0; let mut loclists_offset = 0; let mut loclists_size = 0; let mut str_offsets_offset = 0; let mut str_offsets_size = 0; let mut rnglists_offset = 0; let mut rnglists_size = 0; let mut types_offset = 0; let mut types_size = 0; for section in sections { match section.section { IndexSectionId::DebugAbbrev => { abbrev_offset = section.offset; abbrev_size = section.size; } IndexSectionId::DebugInfo => { info_offset = section.offset; info_size = section.size; } IndexSectionId::DebugLine => { line_offset = section.offset; line_size = section.size; } IndexSectionId::DebugLoc => { loc_offset = section.offset; loc_size = section.size; } IndexSectionId::DebugLocLists => { loclists_offset = section.offset; loclists_size = section.size; } IndexSectionId::DebugStrOffsets => { str_offsets_offset = section.offset; str_offsets_size = section.size; } IndexSectionId::DebugRngLists => { rnglists_offset = section.offset; rnglists_size = section.size; } IndexSectionId::DebugTypes => { types_offset = section.offset; types_size = section.size; } IndexSectionId::DebugMacro | IndexSectionId::DebugMacinfo => { // These are valid but we can't parse these yet. } } } let debug_abbrev = self.debug_abbrev.dwp_range(abbrev_offset, abbrev_size)?; let debug_info = self.debug_info.dwp_range(info_offset, info_size)?; let debug_line = self.debug_line.dwp_range(line_offset, line_size)?; let debug_loc = self.debug_loc.dwp_range(loc_offset, loc_size)?; let debug_loclists = self .debug_loclists .dwp_range(loclists_offset, loclists_size)?; let debug_str_offsets = self .debug_str_offsets .dwp_range(str_offsets_offset, str_offsets_size)?; let debug_rnglists = self .debug_rnglists .dwp_range(rnglists_offset, rnglists_size)?; let debug_types = self.debug_types.dwp_range(types_offset, types_size)?; let debug_str = self.debug_str.clone(); let debug_addr = parent.debug_addr.clone(); let debug_ranges = parent.ranges.debug_ranges().clone(); let debug_aranges = self.empty.clone().into(); let debug_line_str = self.empty.clone().into(); Ok(Dwarf { debug_abbrev, debug_addr, debug_aranges, debug_info, debug_line, debug_line_str, debug_str, debug_str_offsets, debug_types, locations: LocationLists::new(debug_loc, debug_loclists), ranges: RangeLists::new(debug_ranges, debug_rnglists), file_type: DwarfFileType::Dwo, sup: parent.sup.clone(), abbreviations_cache: AbbreviationsCache::new(), }) } } /// All of the commonly used information for a unit in the `.debug_info` or `.debug_types` /// sections. #[derive(Debug)] pub struct Unit::Offset> where R: Reader, Offset: ReaderOffset, { /// The header of the unit. pub header: UnitHeader, /// The parsed abbreviations for the unit. pub abbreviations: Arc, /// The `DW_AT_name` attribute of the unit. pub name: Option, /// The `DW_AT_comp_dir` attribute of the unit. pub comp_dir: Option, /// The `DW_AT_low_pc` attribute of the unit. Defaults to 0. pub low_pc: u64, /// The `DW_AT_str_offsets_base` attribute of the unit. Defaults to 0. pub str_offsets_base: DebugStrOffsetsBase, /// The `DW_AT_addr_base` attribute of the unit. Defaults to 0. pub addr_base: DebugAddrBase, /// The `DW_AT_loclists_base` attribute of the unit. Defaults to 0. pub loclists_base: DebugLocListsBase, /// The `DW_AT_rnglists_base` attribute of the unit. Defaults to 0. pub rnglists_base: DebugRngListsBase, /// The line number program of the unit. pub line_program: Option>, /// The DWO ID of a skeleton unit or split compilation unit. pub dwo_id: Option, } impl Unit { /// Construct a new `Unit` from the given unit header. #[inline] pub fn new(dwarf: &Dwarf, header: UnitHeader) -> Result { let abbreviations = dwarf.abbreviations(&header)?; Self::new_with_abbreviations(dwarf, header, abbreviations) } /// Construct a new `Unit` from the given unit header and abbreviations. /// /// The abbreviations for this call can be obtained using `dwarf.abbreviations(&header)`. /// The caller may implement caching to reuse the `Abbreviations` across units with the /// same `header.debug_abbrev_offset()` value. #[inline] pub fn new_with_abbreviations( dwarf: &Dwarf, header: UnitHeader, abbreviations: Arc, ) -> Result { let mut unit = Unit { abbreviations, name: None, comp_dir: None, low_pc: 0, str_offsets_base: DebugStrOffsetsBase::default_for_encoding_and_file( header.encoding(), dwarf.file_type, ), // NB: Because the .debug_addr section never lives in a .dwo, we can assume its base is always 0 or provided. addr_base: DebugAddrBase(R::Offset::from_u8(0)), loclists_base: DebugLocListsBase::default_for_encoding_and_file( header.encoding(), dwarf.file_type, ), rnglists_base: DebugRngListsBase::default_for_encoding_and_file( header.encoding(), dwarf.file_type, ), line_program: None, dwo_id: match header.type_() { UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => Some(dwo_id), _ => None, }, header, }; let mut name = None; let mut comp_dir = None; let mut line_program_offset = None; let mut low_pc_attr = None; { let mut cursor = unit.header.entries(&unit.abbreviations); cursor.next_dfs()?; let root = cursor.current().ok_or(Error::MissingUnitDie)?; let mut attrs = root.attrs(); while let Some(attr) = attrs.next()? { match attr.name() { constants::DW_AT_name => { name = Some(attr.value()); } constants::DW_AT_comp_dir => { comp_dir = Some(attr.value()); } constants::DW_AT_low_pc => { low_pc_attr = Some(attr.value()); } constants::DW_AT_stmt_list => { if let AttributeValue::DebugLineRef(offset) = attr.value() { line_program_offset = Some(offset); } } constants::DW_AT_str_offsets_base => { if let AttributeValue::DebugStrOffsetsBase(base) = attr.value() { unit.str_offsets_base = base; } } constants::DW_AT_addr_base | constants::DW_AT_GNU_addr_base => { if let AttributeValue::DebugAddrBase(base) = attr.value() { unit.addr_base = base; } } constants::DW_AT_loclists_base => { if let AttributeValue::DebugLocListsBase(base) = attr.value() { unit.loclists_base = base; } } constants::DW_AT_rnglists_base | constants::DW_AT_GNU_ranges_base => { if let AttributeValue::DebugRngListsBase(base) = attr.value() { unit.rnglists_base = base; } } constants::DW_AT_GNU_dwo_id => { if unit.dwo_id.is_none() { if let AttributeValue::DwoId(dwo_id) = attr.value() { unit.dwo_id = Some(dwo_id); } } } _ => {} } } } unit.name = match name { Some(val) => dwarf.attr_string(&unit, val).ok(), None => None, }; unit.comp_dir = match comp_dir { Some(val) => dwarf.attr_string(&unit, val).ok(), None => None, }; unit.line_program = match line_program_offset { Some(offset) => Some(dwarf.debug_line.program( offset, unit.header.address_size(), unit.comp_dir.clone(), unit.name.clone(), )?), None => None, }; if let Some(low_pc_attr) = low_pc_attr { if let Some(addr) = dwarf.attr_address(&unit, low_pc_attr)? { unit.low_pc = addr; } } Ok(unit) } /// Return a reference to this unit and its associated `Dwarf`. pub fn unit_ref<'a>(&'a self, dwarf: &'a Dwarf) -> UnitRef<'a, R> { UnitRef::new(dwarf, self) } /// Return the encoding parameters for this unit. #[inline] pub fn encoding(&self) -> Encoding { self.header.encoding() } /// Read the `DebuggingInformationEntry` at the given offset. pub fn entry( &self, offset: UnitOffset, ) -> Result> { self.header.entry(&self.abbreviations, offset) } /// Navigate this unit's `DebuggingInformationEntry`s. #[inline] pub fn entries(&self) -> EntriesCursor<'_, '_, R> { self.header.entries(&self.abbreviations) } /// Navigate this unit's `DebuggingInformationEntry`s /// starting at the given offset. #[inline] pub fn entries_at_offset( &self, offset: UnitOffset, ) -> Result> { self.header.entries_at_offset(&self.abbreviations, offset) } /// Navigate this unit's `DebuggingInformationEntry`s as a tree /// starting at the given offset. #[inline] pub fn entries_tree( &self, offset: Option>, ) -> Result> { self.header.entries_tree(&self.abbreviations, offset) } /// Read the raw data that defines the Debugging Information Entries. #[inline] pub fn entries_raw( &self, offset: Option>, ) -> Result> { self.header.entries_raw(&self.abbreviations, offset) } /// Copy attributes that are subject to relocation from another unit. This is intended /// to be used to copy attributes from a skeleton compilation unit to the corresponding /// split compilation unit. pub fn copy_relocated_attributes(&mut self, other: &Unit) { self.low_pc = other.low_pc; self.addr_base = other.addr_base; if self.header.version() < 5 { self.rnglists_base = other.rnglists_base; } } /// Find the dwo name (if any) for this unit, automatically handling the differences /// between the standardized DWARF 5 split DWARF format and the pre-DWARF 5 GNU /// extension. /// /// The returned value is relative to this unit's `comp_dir`. pub fn dwo_name(&self) -> Result>> { let mut entries = self.entries(); entries.next_entry()?; let entry = entries.current().ok_or(Error::MissingUnitDie)?; if self.header.version() < 5 { entry.attr_value(constants::DW_AT_GNU_dwo_name) } else { entry.attr_value(constants::DW_AT_dwo_name) } } } /// A reference to a `Unit` and its associated `Dwarf`. /// /// These often need to be passed around together, so this struct makes that easier. /// /// It implements `Deref` to `Unit`, so you can use it as if it were a `Unit`. /// It also implements methods that correspond to methods on `Dwarf` that take a `Unit`. #[derive(Debug)] pub struct UnitRef<'a, R: Reader> { /// The `Dwarf` that contains the unit. pub dwarf: &'a Dwarf, /// The `Unit` being referenced. pub unit: &'a Unit, } impl<'a, R: Reader> Clone for UnitRef<'a, R> { fn clone(&self) -> Self { *self } } impl<'a, R: Reader> Copy for UnitRef<'a, R> {} impl<'a, R: Reader> core::ops::Deref for UnitRef<'a, R> { type Target = Unit; fn deref(&self) -> &Self::Target { self.unit } } impl<'a, R: Reader> UnitRef<'a, R> { /// Construct a new `UnitRef` from a `Dwarf` and a `Unit`. pub fn new(dwarf: &'a Dwarf, unit: &'a Unit) -> Self { UnitRef { dwarf, unit } } /// Return the string offset at the given index. #[inline] pub fn string_offset( &self, index: DebugStrOffsetsIndex, ) -> Result> { self.dwarf.string_offset(self.unit, index) } /// Return the string at the given offset in `.debug_str`. #[inline] pub fn string(&self, offset: DebugStrOffset) -> Result { self.dwarf.string(offset) } /// Return the string at the given offset in `.debug_line_str`. #[inline] pub fn line_string(&self, offset: DebugLineStrOffset) -> Result { self.dwarf.line_string(offset) } /// Return the string at the given offset in the `.debug_str` /// in the supplementary object file. #[inline] pub fn sup_string(&self, offset: DebugStrOffset) -> Result { self.dwarf.sup_string(offset) } /// Return an attribute value as a string slice. /// /// See [`Dwarf::attr_string`] for more information. pub fn attr_string(&self, attr: AttributeValue) -> Result { self.dwarf.attr_string(self.unit, attr) } /// Return the address at the given index. pub fn address(&self, index: DebugAddrIndex) -> Result { self.dwarf.address(self.unit, index) } /// Try to return an attribute value as an address. /// /// See [`Dwarf::attr_address`] for more information. pub fn attr_address(&self, attr: AttributeValue) -> Result> { self.dwarf.attr_address(self.unit, attr) } /// Return the range list offset for the given raw offset. /// /// This handles adding `DW_AT_GNU_ranges_base` if required. pub fn ranges_offset_from_raw( &self, offset: RawRangeListsOffset, ) -> RangeListsOffset { self.dwarf.ranges_offset_from_raw(self.unit, offset) } /// Return the range list offset at the given index. pub fn ranges_offset( &self, index: DebugRngListsIndex, ) -> Result> { self.dwarf.ranges_offset(self.unit, index) } /// Iterate over the `RangeListEntry`s starting at the given offset. pub fn ranges(&self, offset: RangeListsOffset) -> Result> { self.dwarf.ranges(self.unit, offset) } /// Iterate over the `RawRngListEntry`ies starting at the given offset. pub fn raw_ranges(&self, offset: RangeListsOffset) -> Result> { self.dwarf.raw_ranges(self.unit, offset) } /// Try to return an attribute value as a range list offset. /// /// See [`Dwarf::attr_ranges_offset`] for more information. pub fn attr_ranges_offset( &self, attr: AttributeValue, ) -> Result>> { self.dwarf.attr_ranges_offset(self.unit, attr) } /// Try to return an attribute value as a range list entry iterator. /// /// See [`Dwarf::attr_ranges`] for more information. pub fn attr_ranges(&self, attr: AttributeValue) -> Result>> { self.dwarf.attr_ranges(self.unit, attr) } /// Return an iterator for the address ranges of a `DebuggingInformationEntry`. /// /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges`. pub fn die_ranges(&self, entry: &DebuggingInformationEntry<'_, '_, R>) -> Result> { self.dwarf.die_ranges(self.unit, entry) } /// Return an iterator for the address ranges of the `Unit`. /// /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges` of the /// root `DebuggingInformationEntry`. pub fn unit_ranges(&self) -> Result> { self.dwarf.unit_ranges(self.unit) } /// Return the location list offset at the given index. pub fn locations_offset( &self, index: DebugLocListsIndex, ) -> Result> { self.dwarf.locations_offset(self.unit, index) } /// Iterate over the `LocationListEntry`s starting at the given offset. pub fn locations(&self, offset: LocationListsOffset) -> Result> { self.dwarf.locations(self.unit, offset) } /// Iterate over the raw `LocationListEntry`s starting at the given offset. pub fn raw_locations( &self, offset: LocationListsOffset, ) -> Result> { self.dwarf.raw_locations(self.unit, offset) } /// Try to return an attribute value as a location list offset. /// /// See [`Dwarf::attr_locations_offset`] for more information. pub fn attr_locations_offset( &self, attr: AttributeValue, ) -> Result>> { self.dwarf.attr_locations_offset(self.unit, attr) } /// Try to return an attribute value as a location list entry iterator. /// /// See [`Dwarf::attr_locations`] for more information. pub fn attr_locations(&self, attr: AttributeValue) -> Result>> { self.dwarf.attr_locations(self.unit, attr) } } impl UnitSectionOffset { /// Convert an offset to be relative to the start of the given unit, /// instead of relative to the start of the section. /// /// Returns `None` if the offset is not within the unit entries. pub fn to_unit_offset(&self, unit: &Unit) -> Option> where R: Reader, { let (offset, unit_offset) = match (self, unit.header.offset()) { ( UnitSectionOffset::DebugInfoOffset(offset), UnitSectionOffset::DebugInfoOffset(unit_offset), ) => (offset.0, unit_offset.0), ( UnitSectionOffset::DebugTypesOffset(offset), UnitSectionOffset::DebugTypesOffset(unit_offset), ) => (offset.0, unit_offset.0), _ => return None, }; let offset = match offset.checked_sub(unit_offset) { Some(offset) => UnitOffset(offset), None => return None, }; if !unit.header.is_valid_offset(offset) { return None; } Some(offset) } } impl UnitOffset { /// Convert an offset to be relative to the start of the .debug_info section, /// instead of relative to the start of the given compilation unit. /// /// Does not check that the offset is valid. pub fn to_unit_section_offset(&self, unit: &Unit) -> UnitSectionOffset where R: Reader, { match unit.header.offset() { UnitSectionOffset::DebugInfoOffset(unit_offset) => { DebugInfoOffset(unit_offset.0 + self.0).into() } UnitSectionOffset::DebugTypesOffset(unit_offset) => { DebugTypesOffset(unit_offset.0 + self.0).into() } } } } /// An iterator for the address ranges of a `DebuggingInformationEntry`. /// /// Returned by `Dwarf::die_ranges` and `Dwarf::unit_ranges`. #[derive(Debug)] pub struct RangeIter(RangeIterInner); #[derive(Debug)] enum RangeIterInner { Single(Option), List(RngListIter), } impl Default for RangeIter { fn default() -> Self { RangeIter(RangeIterInner::Single(None)) } } impl RangeIter { /// Advance the iterator to the next range. pub fn next(&mut self) -> Result> { match self.0 { RangeIterInner::Single(ref mut range) => Ok(range.take()), RangeIterInner::List(ref mut list) => list.next(), } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for RangeIter { type Item = Range; type Error = Error; #[inline] fn next(&mut self) -> ::core::result::Result, Self::Error> { RangeIter::next(self) } } #[cfg(test)] mod tests { use super::*; use crate::read::EndianSlice; use crate::{Endianity, LittleEndian}; /// Ensure that `Dwarf` is covariant wrt R. #[test] fn test_dwarf_variance() { /// This only needs to compile. fn _f<'a: 'b, 'b, E: Endianity>(x: Dwarf>) -> Dwarf> { x } } /// Ensure that `Unit` is covariant wrt R. #[test] fn test_dwarf_unit_variance() { /// This only needs to compile. fn _f<'a: 'b, 'b, E: Endianity>(x: Unit>) -> Unit> { x } } #[test] fn test_send() { fn assert_is_send() {} assert_is_send::>>(); assert_is_send::>>(); } #[test] fn test_format_error() { let dwarf_sections = DwarfSections::load(|_| -> Result<_> { Ok(vec![1, 2]) }).unwrap(); let sup_sections = DwarfSections::load(|_| -> Result<_> { Ok(vec![1, 2]) }).unwrap(); let dwarf = dwarf_sections.borrow_with_sup(&sup_sections, |section| { EndianSlice::new(section, LittleEndian) }); match dwarf.debug_str.get_str(DebugStrOffset(1)) { Ok(r) => panic!("Unexpected str {:?}", r), Err(e) => { assert_eq!( dwarf.format_error(e), "Hit the end of input before it was expected at .debug_str+0x1" ); } } match dwarf.sup().unwrap().debug_str.get_str(DebugStrOffset(1)) { Ok(r) => panic!("Unexpected str {:?}", r), Err(e) => { assert_eq!( dwarf.format_error(e), "Hit the end of input before it was expected at .debug_str(sup)+0x1" ); } } assert_eq!(dwarf.format_error(Error::Io), Error::Io.description()); } } gimli-0.31.1/src/read/endian_reader.rs000064400000000000000000000437401046102023000156470ustar 00000000000000//! Defining custom `Reader`s quickly. use alloc::borrow::Cow; use alloc::rc::Rc; use alloc::string::String; use alloc::sync::Arc; use core::fmt::Debug; use core::hash::{Hash, Hasher}; use core::ops::{Deref, Index, Range, RangeFrom, RangeTo}; use core::slice; use core::str; use stable_deref_trait::CloneStableDeref; use crate::endianity::Endianity; use crate::read::{Error, Reader, ReaderOffsetId, Result}; /// A reference counted, non-thread-safe slice of bytes and associated /// endianity. /// /// ``` /// # #[cfg(feature = "std")] { /// use std::rc::Rc; /// /// let buf = Rc::from(&[1, 2, 3, 4][..]); /// let reader = gimli::EndianRcSlice::new(buf, gimli::NativeEndian); /// # let _ = reader; /// # } /// ``` pub type EndianRcSlice = EndianReader>; /// An atomically reference counted, thread-safe slice of bytes and associated /// endianity. /// /// ``` /// # #[cfg(feature = "std")] { /// use std::sync::Arc; /// /// let buf = Arc::from(&[1, 2, 3, 4][..]); /// let reader = gimli::EndianArcSlice::new(buf, gimli::NativeEndian); /// # let _ = reader; /// # } /// ``` pub type EndianArcSlice = EndianReader>; /// An easy way to define a custom `Reader` implementation with a reference to a /// generic buffer of bytes and an associated endianity. /// /// Note that the whole original buffer is kept alive in memory even if there is /// only one reader that references only a handful of bytes from that original /// buffer. That is, `EndianReader` will not do any copying, moving, or /// compacting in order to free up unused regions of the original buffer. If you /// require this kind of behavior, it is up to you to implement `Reader` /// directly by-hand. /// /// # Example /// /// Say you have an `mmap`ed file that you want to serve as a `gimli::Reader`. /// You can wrap that `mmap`ed file up in a `MmapFile` type and use /// `EndianReader>` or `EndianReader>` as readers as /// long as `MmapFile` dereferences to the underlying `[u8]` data. /// /// ``` /// use std::io; /// use std::ops::Deref; /// use std::path::Path; /// use std::slice; /// use std::sync::Arc; /// /// /// A type that represents an `mmap`ed file. /// #[derive(Debug)] /// pub struct MmapFile { /// ptr: *const u8, /// len: usize, /// } /// /// impl MmapFile { /// pub fn new(path: &Path) -> io::Result { /// // Call `mmap` and check for errors and all that... /// # unimplemented!() /// } /// } /// /// impl Drop for MmapFile { /// fn drop(&mut self) { /// // Call `munmap` to clean up after ourselves... /// # unimplemented!() /// } /// } /// /// // And `MmapFile` can deref to a slice of the `mmap`ed region of memory. /// impl Deref for MmapFile { /// type Target = [u8]; /// fn deref(&self) -> &[u8] { /// unsafe { /// slice::from_raw_parts(self.ptr, self.len) /// } /// } /// } /// /// /// A type that represents a shared `mmap`ed file. /// #[derive(Debug, Clone)] /// pub struct ArcMmapFile(Arc); /// /// // And `ArcMmapFile` can deref to a slice of the `mmap`ed region of memory. /// impl Deref for ArcMmapFile { /// type Target = [u8]; /// fn deref(&self) -> &[u8] { /// &self.0 /// } /// } /// /// // These are both valid for any `Rc` or `Arc`. /// unsafe impl gimli::StableDeref for ArcMmapFile {} /// unsafe impl gimli::CloneStableDeref for ArcMmapFile {} /// /// /// A `gimli::Reader` that is backed by an `mmap`ed file! /// pub type MmapFileReader = gimli::EndianReader; /// # fn test(_: &MmapFileReader) { } /// ``` #[derive(Debug, Clone, Copy)] pub struct EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { range: SubRange, endian: Endian, } impl PartialEq> for EndianReader where Endian: Endianity, T1: CloneStableDeref + Debug, T2: CloneStableDeref + Debug, { fn eq(&self, rhs: &EndianReader) -> bool { self.bytes() == rhs.bytes() } } impl Eq for EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { } impl Hash for EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { fn hash(&self, state: &mut H) { // This must match the `PartialEq` implementation. self.bytes().hash(state); } } // This is separated out from `EndianReader` so that we can avoid running afoul // of borrowck. We need to `read_slice(&mut self, ...) -> &[u8]` and then call // `self.endian.read_whatever` on the result. The problem is that the returned // slice keeps the `&mut self` borrow active, so we wouldn't be able to access // `self.endian`. Splitting the sub-range out from the endian lets us work // around this, making it so that only the `self.range` borrow is held active, // not all of `self`. // // This also serves to encapsulate the unsafe code concerning `CloneStableDeref`. // The `bytes` member is held so that the bytes live long enough, and the // `CloneStableDeref` ensures these bytes never move. The `ptr` and `len` // members point inside `bytes`, and are updated during read operations. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct SubRange where T: CloneStableDeref + Debug, { bytes: T, ptr: *const u8, len: usize, } unsafe impl Send for SubRange where T: CloneStableDeref + Debug + Send {} unsafe impl Sync for SubRange where T: CloneStableDeref + Debug + Sync {} impl SubRange where T: CloneStableDeref + Debug, { #[inline] fn new(bytes: T) -> Self { let ptr = bytes.as_ptr(); let len = bytes.len(); SubRange { bytes, ptr, len } } #[inline] fn bytes(&self) -> &[u8] { // Safe because `T` implements `CloneStableDeref`, `bytes` can't be modified, // and all operations that modify `ptr` and `len` ensure they stay in range. unsafe { slice::from_raw_parts(self.ptr, self.len) } } #[inline] fn len(&self) -> usize { self.len } #[inline] fn truncate(&mut self, len: usize) { assert!(len <= self.len); self.len = len; } #[inline] fn skip(&mut self, len: usize) { assert!(len <= self.len); self.ptr = unsafe { self.ptr.add(len) }; self.len -= len; } #[inline] fn read_slice(&mut self, len: usize) -> Option<&[u8]> { if self.len() < len { None } else { // Same as for `bytes()`. let bytes = unsafe { slice::from_raw_parts(self.ptr, len) }; self.skip(len); Some(bytes) } } } impl EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { /// Construct a new `EndianReader` with the given bytes. #[inline] pub fn new(bytes: T, endian: Endian) -> EndianReader { EndianReader { range: SubRange::new(bytes), endian, } } /// Return a reference to the raw bytes underlying this reader. #[inline] pub fn bytes(&self) -> &[u8] { self.range.bytes() } } /// # Range Methods /// /// Unfortunately, `std::ops::Index` *must* return a reference, so we can't /// implement `Index>` to return a new `EndianReader` the way we /// would like to. Instead, we abandon fancy indexing operators and have these /// plain old methods. impl EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { /// Take the given `start..end` range of the underlying buffer and return a /// new `EndianReader`. /// /// ``` /// # #[cfg(feature = "std")] { /// use gimli::{EndianReader, LittleEndian}; /// use std::sync::Arc; /// /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); /// let reader = EndianReader::new(buf.clone(), LittleEndian); /// assert_eq!(reader.range(1..3), /// EndianReader::new(&buf[1..3], LittleEndian)); /// # } /// ``` /// /// # Panics /// /// Panics if the range is out of bounds. pub fn range(&self, idx: Range) -> EndianReader { let mut r = self.clone(); r.range.skip(idx.start); r.range.truncate(idx.len()); r } /// Take the given `start..` range of the underlying buffer and return a new /// `EndianReader`. /// /// ``` /// # #[cfg(feature = "std")] { /// use gimli::{EndianReader, LittleEndian}; /// use std::sync::Arc; /// /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); /// let reader = EndianReader::new(buf.clone(), LittleEndian); /// assert_eq!(reader.range_from(2..), /// EndianReader::new(&buf[2..], LittleEndian)); /// # } /// ``` /// /// # Panics /// /// Panics if the range is out of bounds. pub fn range_from(&self, idx: RangeFrom) -> EndianReader { let mut r = self.clone(); r.range.skip(idx.start); r } /// Take the given `..end` range of the underlying buffer and return a new /// `EndianReader`. /// /// ``` /// # #[cfg(feature = "std")] { /// use gimli::{EndianReader, LittleEndian}; /// use std::sync::Arc; /// /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); /// let reader = EndianReader::new(buf.clone(), LittleEndian); /// assert_eq!(reader.range_to(..3), /// EndianReader::new(&buf[..3], LittleEndian)); /// # } /// ``` /// /// # Panics /// /// Panics if the range is out of bounds. pub fn range_to(&self, idx: RangeTo) -> EndianReader { let mut r = self.clone(); r.range.truncate(idx.end); r } } impl Index for EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { type Output = u8; fn index(&self, idx: usize) -> &Self::Output { &self.bytes()[idx] } } impl Index> for EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { type Output = [u8]; fn index(&self, idx: RangeFrom) -> &Self::Output { &self.bytes()[idx] } } impl Deref for EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { type Target = [u8]; fn deref(&self) -> &Self::Target { self.bytes() } } impl Reader for EndianReader where Endian: Endianity, T: CloneStableDeref + Debug, { type Endian = Endian; type Offset = usize; #[inline] fn endian(&self) -> Endian { self.endian } #[inline] fn len(&self) -> usize { self.range.len() } #[inline] fn empty(&mut self) { self.range.truncate(0); } #[inline] fn truncate(&mut self, len: usize) -> Result<()> { if self.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { self.range.truncate(len); Ok(()) } } #[inline] fn offset_from(&self, base: &EndianReader) -> usize { let base_ptr = base.bytes().as_ptr() as usize; let ptr = self.bytes().as_ptr() as usize; debug_assert!(base_ptr <= ptr); debug_assert!(ptr + self.bytes().len() <= base_ptr + base.bytes().len()); ptr - base_ptr } #[inline] fn offset_id(&self) -> ReaderOffsetId { ReaderOffsetId(self.bytes().as_ptr() as u64) } #[inline] fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option { let id = id.0; let self_id = self.bytes().as_ptr() as u64; let self_len = self.bytes().len() as u64; if id >= self_id && id <= self_id + self_len { Some((id - self_id) as usize) } else { None } } #[inline] fn find(&self, byte: u8) -> Result { self.bytes() .iter() .position(|x| *x == byte) .ok_or_else(|| Error::UnexpectedEof(self.offset_id())) } #[inline] fn skip(&mut self, len: usize) -> Result<()> { if self.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { self.range.skip(len); Ok(()) } } #[inline] fn split(&mut self, len: usize) -> Result { if self.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { let mut r = self.clone(); r.range.truncate(len); self.range.skip(len); Ok(r) } } #[inline] fn to_slice(&self) -> Result> { Ok(self.bytes().into()) } #[inline] fn to_string(&self) -> Result> { match str::from_utf8(self.bytes()) { Ok(s) => Ok(s.into()), _ => Err(Error::BadUtf8), } } #[inline] fn to_string_lossy(&self) -> Result> { Ok(String::from_utf8_lossy(self.bytes())) } #[inline] fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { match self.range.read_slice(buf.len()) { Some(slice) => { buf.copy_from_slice(slice); Ok(()) } None => Err(Error::UnexpectedEof(self.offset_id())), } } } #[cfg(test)] mod tests { use super::*; use crate::endianity::NativeEndian; use crate::read::Reader; fn native_reader + Debug>( bytes: T, ) -> EndianReader { EndianReader::new(bytes, NativeEndian) } const BUF: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; #[test] fn test_reader_split() { let mut reader = native_reader(BUF); let left = reader.split(3).unwrap(); assert_eq!(left, native_reader(&BUF[..3])); assert_eq!(reader, native_reader(&BUF[3..])); } #[test] fn test_reader_split_out_of_bounds() { let mut reader = native_reader(BUF); assert!(reader.split(30).is_err()); } #[test] fn bytes_and_len_and_range_and_eq() { let reader = native_reader(BUF); assert_eq!(reader.len(), BUF.len()); assert_eq!(reader.bytes(), BUF); assert_eq!(reader, native_reader(BUF)); let range = reader.range(2..8); let buf_range = &BUF[2..8]; assert_eq!(range.len(), buf_range.len()); assert_eq!(range.bytes(), buf_range); assert_ne!(range, native_reader(BUF)); assert_eq!(range, native_reader(buf_range)); let range_from = range.range_from(1..); let buf_range_from = &buf_range[1..]; assert_eq!(range_from.len(), buf_range_from.len()); assert_eq!(range_from.bytes(), buf_range_from); assert_ne!(range_from, native_reader(BUF)); assert_eq!(range_from, native_reader(buf_range_from)); let range_to = range_from.range_to(..4); let buf_range_to = &buf_range_from[..4]; assert_eq!(range_to.len(), buf_range_to.len()); assert_eq!(range_to.bytes(), buf_range_to); assert_ne!(range_to, native_reader(BUF)); assert_eq!(range_to, native_reader(buf_range_to)); } #[test] fn find() { let mut reader = native_reader(BUF); reader.skip(2).unwrap(); assert_eq!( reader.find(5), Ok(BUF[2..].iter().position(|x| *x == 5).unwrap()) ); } #[test] fn indexing() { let mut reader = native_reader(BUF); reader.skip(2).unwrap(); assert_eq!(reader[0], BUF[2]); } #[test] #[should_panic] fn indexing_out_of_bounds() { let mut reader = native_reader(BUF); reader.skip(2).unwrap(); let _ = reader[900]; } #[test] fn endian() { let reader = native_reader(BUF); assert_eq!(reader.endian(), NativeEndian); } #[test] fn empty() { let mut reader = native_reader(BUF); assert!(!reader.is_empty()); reader.empty(); assert!(reader.is_empty()); assert!(reader.bytes().is_empty()); } #[test] fn truncate() { let reader = native_reader(BUF); let mut reader = reader.range(2..8); reader.truncate(2).unwrap(); assert_eq!(reader.bytes(), &BUF[2..4]); } #[test] fn offset_from() { let reader = native_reader(BUF); let sub = reader.range(2..8); assert_eq!(sub.offset_from(&reader), 2); } #[test] fn skip() { let mut reader = native_reader(BUF); reader.skip(2).unwrap(); assert_eq!(reader.bytes(), &BUF[2..]); } #[test] fn to_slice() { assert_eq!( native_reader(BUF).range(2..5).to_slice(), Ok(Cow::from(&BUF[2..5])) ); } #[test] fn to_string_ok() { let buf = b"hello, world!"; let reader = native_reader(&buf[..]); let reader = reader.range_from(7..); assert_eq!(reader.to_string(), Ok(Cow::from("world!"))); } // The rocket emoji (🚀 = [0xf0, 0x9f, 0x9a, 0x80]) but rotated left by one // to make it invalid UTF-8. const BAD_UTF8: &[u8] = &[0x9f, 0x9a, 0x80, 0xf0]; #[test] fn to_string_err() { let reader = native_reader(BAD_UTF8); assert!(reader.to_string().is_err()); } #[test] fn to_string_lossy() { let reader = native_reader(BAD_UTF8); assert_eq!(reader.to_string_lossy(), Ok(Cow::from("����"))); } #[test] fn read_u8_array() { let mut reader = native_reader(BAD_UTF8); reader.skip(1).unwrap(); let arr: [u8; 2] = reader.read_u8_array().unwrap(); assert_eq!(arr, &BAD_UTF8[1..3]); assert_eq!(reader.bytes(), &BAD_UTF8[3..]); } } gimli-0.31.1/src/read/endian_slice.rs000064400000000000000000000235171046102023000155040ustar 00000000000000//! Working with byte slices that have an associated endianity. #[cfg(feature = "read")] use alloc::borrow::Cow; #[cfg(feature = "read")] use alloc::string::String; use core::fmt; use core::ops::{Deref, Range, RangeFrom, RangeTo}; use core::str; use crate::endianity::Endianity; use crate::read::{Error, Reader, ReaderOffsetId, Result}; /// A `&[u8]` slice with endianity metadata. /// /// This implements the `Reader` trait, which is used for all reading of DWARF sections. #[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct EndianSlice<'input, Endian> where Endian: Endianity, { slice: &'input [u8], endian: Endian, } impl<'input, Endian> EndianSlice<'input, Endian> where Endian: Endianity, { /// Construct a new `EndianSlice` with the given slice and endianity. #[inline] pub fn new(slice: &'input [u8], endian: Endian) -> EndianSlice<'input, Endian> { EndianSlice { slice, endian } } /// Return a reference to the raw slice. #[inline] #[doc(hidden)] #[deprecated(note = "Method renamed to EndianSlice::slice; use that instead.")] pub fn buf(&self) -> &'input [u8] { self.slice } /// Return a reference to the raw slice. #[inline] pub fn slice(&self) -> &'input [u8] { self.slice } /// Split the slice in two at the given index, resulting in the tuple where /// the first item has range [0, idx), and the second has range [idx, /// len). Panics if the index is out of bounds. #[inline] pub fn split_at( &self, idx: usize, ) -> (EndianSlice<'input, Endian>, EndianSlice<'input, Endian>) { (self.range_to(..idx), self.range_from(idx..)) } /// Find the first occurrence of a byte in the slice, and return its index. #[inline] pub fn find(&self, byte: u8) -> Option { self.slice.iter().position(|ch| *ch == byte) } /// Return the offset of the start of the slice relative to the start /// of the given slice. #[inline] pub fn offset_from(&self, base: EndianSlice<'input, Endian>) -> usize { let base_ptr = base.slice.as_ptr() as usize; let ptr = self.slice.as_ptr() as usize; debug_assert!(base_ptr <= ptr); debug_assert!(ptr + self.slice.len() <= base_ptr + base.slice.len()); ptr - base_ptr } /// Converts the slice to a string using `str::from_utf8`. /// /// Returns an error if the slice contains invalid characters. #[inline] pub fn to_string(&self) -> Result<&'input str> { str::from_utf8(self.slice).map_err(|_| Error::BadUtf8) } /// Converts the slice to a string, including invalid characters, /// using `String::from_utf8_lossy`. #[cfg(feature = "read")] #[inline] pub fn to_string_lossy(&self) -> Cow<'input, str> { String::from_utf8_lossy(self.slice) } #[inline] fn read_slice(&mut self, len: usize) -> Result<&'input [u8]> { if self.slice.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { let val = &self.slice[..len]; self.slice = &self.slice[len..]; Ok(val) } } } /// # Range Methods /// /// Unfortunately, `std::ops::Index` *must* return a reference, so we can't /// implement `Index>` to return a new `EndianSlice` the way we would /// like to. Instead, we abandon fancy indexing operators and have these plain /// old methods. impl<'input, Endian> EndianSlice<'input, Endian> where Endian: Endianity, { /// Take the given `start..end` range of the underlying slice and return a /// new `EndianSlice`. /// /// ``` /// use gimli::{EndianSlice, LittleEndian}; /// /// let slice = &[0x01, 0x02, 0x03, 0x04]; /// let endian_slice = EndianSlice::new(slice, LittleEndian); /// assert_eq!(endian_slice.range(1..3), /// EndianSlice::new(&slice[1..3], LittleEndian)); /// ``` pub fn range(&self, idx: Range) -> EndianSlice<'input, Endian> { EndianSlice { slice: &self.slice[idx], endian: self.endian, } } /// Take the given `start..` range of the underlying slice and return a new /// `EndianSlice`. /// /// ``` /// use gimli::{EndianSlice, LittleEndian}; /// /// let slice = &[0x01, 0x02, 0x03, 0x04]; /// let endian_slice = EndianSlice::new(slice, LittleEndian); /// assert_eq!(endian_slice.range_from(2..), /// EndianSlice::new(&slice[2..], LittleEndian)); /// ``` pub fn range_from(&self, idx: RangeFrom) -> EndianSlice<'input, Endian> { EndianSlice { slice: &self.slice[idx], endian: self.endian, } } /// Take the given `..end` range of the underlying slice and return a new /// `EndianSlice`. /// /// ``` /// use gimli::{EndianSlice, LittleEndian}; /// /// let slice = &[0x01, 0x02, 0x03, 0x04]; /// let endian_slice = EndianSlice::new(slice, LittleEndian); /// assert_eq!(endian_slice.range_to(..3), /// EndianSlice::new(&slice[..3], LittleEndian)); /// ``` pub fn range_to(&self, idx: RangeTo) -> EndianSlice<'input, Endian> { EndianSlice { slice: &self.slice[idx], endian: self.endian, } } } impl<'input, Endian> Deref for EndianSlice<'input, Endian> where Endian: Endianity, { type Target = [u8]; fn deref(&self) -> &Self::Target { self.slice } } impl<'input, Endian: Endianity> fmt::Debug for EndianSlice<'input, Endian> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { fmt.debug_tuple("EndianSlice") .field(&self.endian) .field(&DebugBytes(self.slice)) .finish() } } struct DebugBytes<'input>(&'input [u8]); impl<'input> core::fmt::Debug for DebugBytes<'input> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { let mut list = fmt.debug_list(); list.entries(self.0.iter().take(8).copied().map(DebugByte)); if self.0.len() > 8 { list.entry(&DebugLen(self.0.len())); } list.finish() } } struct DebugByte(u8); impl fmt::Debug for DebugByte { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "0x{:02x}", self.0) } } struct DebugLen(usize); impl fmt::Debug for DebugLen { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "...; {}", self.0) } } impl<'input, Endian> Reader for EndianSlice<'input, Endian> where Endian: Endianity, { type Endian = Endian; type Offset = usize; #[inline] fn endian(&self) -> Endian { self.endian } #[inline] fn len(&self) -> usize { self.slice.len() } #[inline] fn is_empty(&self) -> bool { self.slice.is_empty() } #[inline] fn empty(&mut self) { self.slice = &[]; } #[inline] fn truncate(&mut self, len: usize) -> Result<()> { if self.slice.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { self.slice = &self.slice[..len]; Ok(()) } } #[inline] fn offset_from(&self, base: &Self) -> usize { self.offset_from(*base) } #[inline] fn offset_id(&self) -> ReaderOffsetId { ReaderOffsetId(self.slice.as_ptr() as u64) } #[inline] fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option { let id = id.0; let self_id = self.slice.as_ptr() as u64; let self_len = self.slice.len() as u64; if id >= self_id && id <= self_id + self_len { Some((id - self_id) as usize) } else { None } } #[inline] fn find(&self, byte: u8) -> Result { self.find(byte) .ok_or_else(|| Error::UnexpectedEof(self.offset_id())) } #[inline] fn skip(&mut self, len: usize) -> Result<()> { if self.slice.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { self.slice = &self.slice[len..]; Ok(()) } } #[inline] fn split(&mut self, len: usize) -> Result { let slice = self.read_slice(len)?; Ok(EndianSlice::new(slice, self.endian)) } #[cfg(not(feature = "read"))] fn cannot_implement() -> super::reader::seal_if_no_alloc::Sealed { super::reader::seal_if_no_alloc::Sealed } #[cfg(feature = "read")] #[inline] fn to_slice(&self) -> Result> { Ok(self.slice.into()) } #[cfg(feature = "read")] #[inline] fn to_string(&self) -> Result> { match str::from_utf8(self.slice) { Ok(s) => Ok(s.into()), _ => Err(Error::BadUtf8), } } #[cfg(feature = "read")] #[inline] fn to_string_lossy(&self) -> Result> { Ok(String::from_utf8_lossy(self.slice)) } #[inline] fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { let slice = self.read_slice(buf.len())?; buf.copy_from_slice(slice); Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::endianity::NativeEndian; #[test] fn test_endian_slice_split_at() { let endian = NativeEndian; let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; let eb = EndianSlice::new(slice, endian); assert_eq!( eb.split_at(3), ( EndianSlice::new(&slice[..3], endian), EndianSlice::new(&slice[3..], endian) ) ); } #[test] #[should_panic] fn test_endian_slice_split_at_out_of_bounds() { let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; let eb = EndianSlice::new(slice, NativeEndian); eb.split_at(30); } } gimli-0.31.1/src/read/index.rs000064400000000000000000000526071046102023000142000ustar 00000000000000use core::slice; use crate::common::SectionId; use crate::constants; use crate::endianity::Endianity; use crate::read::{EndianSlice, Error, Reader, ReaderOffset, Result, Section}; /// The data in the `.debug_cu_index` section of a `.dwp` file. /// /// This section contains the compilation unit index. #[derive(Debug, Default, Clone, Copy)] pub struct DebugCuIndex { section: R, } impl<'input, Endian> DebugCuIndex> where Endian: Endianity, { /// Construct a new `DebugCuIndex` instance from the data in the `.debug_cu_index` /// section. pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl DebugCuIndex { /// Create a `DebugCuIndex` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfPackageSections::borrow`. pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugCuIndex where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugCuIndex { fn id() -> SectionId { SectionId::DebugCuIndex } fn reader(&self) -> &R { &self.section } } impl From for DebugCuIndex { fn from(section: R) -> Self { DebugCuIndex { section } } } impl DebugCuIndex { /// Parse the index header. pub fn index(self) -> Result> { UnitIndex::parse(self.section) } } /// The data in the `.debug_tu_index` section of a `.dwp` file. /// /// This section contains the type unit index. #[derive(Debug, Default, Clone, Copy)] pub struct DebugTuIndex { section: R, } impl<'input, Endian> DebugTuIndex> where Endian: Endianity, { /// Construct a new `DebugTuIndex` instance from the data in the `.debug_tu_index` /// section. pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl DebugTuIndex { /// Create a `DebugTuIndex` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfPackageSections::borrow`. pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTuIndex where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugTuIndex { fn id() -> SectionId { SectionId::DebugTuIndex } fn reader(&self) -> &R { &self.section } } impl From for DebugTuIndex { fn from(section: R) -> Self { DebugTuIndex { section } } } impl DebugTuIndex { /// Parse the index header. pub fn index(self) -> Result> { UnitIndex::parse(self.section) } } const SECTION_COUNT_MAX: u8 = 8; /// The partially parsed index from a `DebugCuIndex` or `DebugTuIndex`. #[derive(Debug, Clone)] pub struct UnitIndex { version: u16, section_count: u32, unit_count: u32, slot_count: u32, hash_ids: R, hash_rows: R, // Only `section_count` values are valid. sections: [IndexSectionId; SECTION_COUNT_MAX as usize], offsets: R, sizes: R, } impl UnitIndex { fn parse(mut input: R) -> Result> { if input.is_empty() { return Ok(UnitIndex { version: 0, section_count: 0, unit_count: 0, slot_count: 0, hash_ids: input.clone(), hash_rows: input.clone(), sections: [IndexSectionId::DebugAbbrev; SECTION_COUNT_MAX as usize], offsets: input.clone(), sizes: input.clone(), }); } // GNU split-dwarf extension to DWARF 4 uses a 32-bit version, // but DWARF 5 uses a 16-bit version followed by 16-bit padding. let mut original_input = input.clone(); let version; if input.read_u32()? == 2 { version = 2 } else { version = original_input.read_u16()?; if version != 5 { return Err(Error::UnknownVersion(version.into())); } } let section_count = input.read_u32()?; let unit_count = input.read_u32()?; let slot_count = input.read_u32()?; if slot_count != 0 && (slot_count & (slot_count - 1) != 0 || slot_count <= unit_count) { return Err(Error::InvalidIndexSlotCount); } let hash_ids = input.split(R::Offset::from_u64(u64::from(slot_count) * 8)?)?; let hash_rows = input.split(R::Offset::from_u64(u64::from(slot_count) * 4)?)?; let mut sections = [IndexSectionId::DebugAbbrev; SECTION_COUNT_MAX as usize]; if section_count > SECTION_COUNT_MAX.into() { return Err(Error::InvalidIndexSectionCount); } for i in 0..section_count { let section = input.read_u32()?; sections[i as usize] = if version == 2 { match constants::DwSectV2(section) { constants::DW_SECT_V2_INFO => IndexSectionId::DebugInfo, constants::DW_SECT_V2_TYPES => IndexSectionId::DebugTypes, constants::DW_SECT_V2_ABBREV => IndexSectionId::DebugAbbrev, constants::DW_SECT_V2_LINE => IndexSectionId::DebugLine, constants::DW_SECT_V2_LOC => IndexSectionId::DebugLoc, constants::DW_SECT_V2_STR_OFFSETS => IndexSectionId::DebugStrOffsets, constants::DW_SECT_V2_MACINFO => IndexSectionId::DebugMacinfo, constants::DW_SECT_V2_MACRO => IndexSectionId::DebugMacro, section => return Err(Error::UnknownIndexSectionV2(section)), } } else { match constants::DwSect(section) { constants::DW_SECT_INFO => IndexSectionId::DebugInfo, constants::DW_SECT_ABBREV => IndexSectionId::DebugAbbrev, constants::DW_SECT_LINE => IndexSectionId::DebugLine, constants::DW_SECT_LOCLISTS => IndexSectionId::DebugLocLists, constants::DW_SECT_STR_OFFSETS => IndexSectionId::DebugStrOffsets, constants::DW_SECT_MACRO => IndexSectionId::DebugMacro, constants::DW_SECT_RNGLISTS => IndexSectionId::DebugRngLists, section => return Err(Error::UnknownIndexSection(section)), } }; } let offsets = input.split(R::Offset::from_u64( u64::from(unit_count) * u64::from(section_count) * 4, )?)?; let sizes = input.split(R::Offset::from_u64( u64::from(unit_count) * u64::from(section_count) * 4, )?)?; Ok(UnitIndex { version, section_count, unit_count, slot_count, hash_ids, hash_rows, sections, offsets, sizes, }) } /// Find `id` in the index hash table, and return the row index. /// /// `id` may be a compilation unit ID if this index is from `.debug_cu_index`, /// or a type signature if this index is from `.debug_tu_index`. pub fn find(&self, id: u64) -> Option { if self.slot_count == 0 { return None; } let mask = u64::from(self.slot_count - 1); let mut hash1 = id & mask; let hash2 = ((id >> 32) & mask) | 1; for _ in 0..self.slot_count { // The length of these arrays was validated in `UnitIndex::parse`. let mut hash_ids = self.hash_ids.clone(); hash_ids.skip(R::Offset::from_u64(hash1 * 8).ok()?).ok()?; let hash_id = hash_ids.read_u64().ok()?; if hash_id == id { let mut hash_rows = self.hash_rows.clone(); hash_rows.skip(R::Offset::from_u64(hash1 * 4).ok()?).ok()?; let hash_row = hash_rows.read_u32().ok()?; return Some(hash_row); } if hash_id == 0 { return None; } hash1 = (hash1 + hash2) & mask; } None } /// Return the section offsets and sizes for the given row index. pub fn sections(&self, mut row: u32) -> Result> { if row == 0 { return Err(Error::InvalidIndexRow); } row -= 1; if row >= self.unit_count { return Err(Error::InvalidIndexRow); } let mut offsets = self.offsets.clone(); offsets.skip(R::Offset::from_u64( u64::from(row) * u64::from(self.section_count) * 4, )?)?; let mut sizes = self.sizes.clone(); sizes.skip(R::Offset::from_u64( u64::from(row) * u64::from(self.section_count) * 4, )?)?; Ok(UnitIndexSectionIterator { sections: self.sections[..self.section_count as usize].iter(), offsets, sizes, }) } /// Return the version. /// /// Defaults to 0 for empty sections. pub fn version(&self) -> u16 { self.version } /// Return the number of sections. pub fn section_count(&self) -> u32 { self.section_count } /// Return the number of units. pub fn unit_count(&self) -> u32 { self.unit_count } /// Return the number of slots. pub fn slot_count(&self) -> u32 { self.slot_count } } /// An iterator over the section offsets and sizes for a row in a `UnitIndex`. #[derive(Debug, Clone)] pub struct UnitIndexSectionIterator<'index, R: Reader> { sections: slice::Iter<'index, IndexSectionId>, offsets: R, sizes: R, } impl<'index, R: Reader> Iterator for UnitIndexSectionIterator<'index, R> { type Item = UnitIndexSection; fn next(&mut self) -> Option { let section = *self.sections.next()?; // The length of these arrays was validated in `UnitIndex::parse`. let offset = self.offsets.read_u32().ok()?; let size = self.sizes.read_u32().ok()?; Some(UnitIndexSection { section, offset, size, }) } } /// Information about a unit's contribution to a section in a `.dwp` file. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct UnitIndexSection { /// The section kind. pub section: IndexSectionId, /// The base offset of the unit's contribution to the section. pub offset: u32, /// The size of the unit's contribution to the section. pub size: u32, } /// Section kinds which are permitted in a `.dwp` index. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IndexSectionId { /// The `.debug_abbrev.dwo` section. DebugAbbrev, /// The `.debug_info.dwo` section. DebugInfo, /// The `.debug_line.dwo` section. DebugLine, /// The `.debug_loc.dwo` section. DebugLoc, /// The `.debug_loclists.dwo` section. DebugLocLists, /// The `.debug_macinfo.dwo` section. DebugMacinfo, /// The `.debug_macro.dwo` section. DebugMacro, /// The `.debug_rnglists.dwo` section. DebugRngLists, /// The `.debug_str_offsets.dwo` section. DebugStrOffsets, /// The `.debug_types.dwo` section. DebugTypes, } impl IndexSectionId { /// Returns the corresponding `SectionId`. pub fn section_id(self) -> SectionId { match self { IndexSectionId::DebugAbbrev => SectionId::DebugAbbrev, IndexSectionId::DebugInfo => SectionId::DebugInfo, IndexSectionId::DebugLine => SectionId::DebugLine, IndexSectionId::DebugLoc => SectionId::DebugLoc, IndexSectionId::DebugLocLists => SectionId::DebugLocLists, IndexSectionId::DebugMacro => SectionId::DebugMacro, IndexSectionId::DebugMacinfo => SectionId::DebugMacinfo, IndexSectionId::DebugRngLists => SectionId::DebugRngLists, IndexSectionId::DebugStrOffsets => SectionId::DebugStrOffsets, IndexSectionId::DebugTypes => SectionId::DebugTypes, } } /// Returns the ELF section name for this kind, when found in a .dwo or .dwp file. pub fn dwo_name(self) -> &'static str { self.section_id().dwo_name().unwrap() } } #[cfg(test)] mod tests { use super::*; use crate::endianity::BigEndian; use test_assembler::{Endian, Section}; #[test] fn test_empty() { let buf = EndianSlice::new(&[], BigEndian); let index = UnitIndex::parse(buf).unwrap(); assert_eq!(index.version(), 0); assert_eq!(index.unit_count(), 0); assert_eq!(index.slot_count(), 0); assert!(index.find(0).is_none()); } #[test] fn test_zero_slots() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D32(2).D32(0).D32(0).D32(0); let buf = section.get_contents().unwrap(); let buf = EndianSlice::new(&buf, BigEndian); let index = UnitIndex::parse(buf).unwrap(); assert_eq!(index.version(), 2); assert_eq!(index.unit_count(), 0); assert_eq!(index.slot_count(), 0); assert!(index.find(0).is_none()); } #[test] fn test_version_2() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D32(2).D32(0).D32(0).D32(1) // Slots. .D64(0).D32(0); let buf = section.get_contents().unwrap(); let buf = EndianSlice::new(&buf, BigEndian); let index = UnitIndex::parse(buf).unwrap(); assert_eq!(index.version, 2); } #[test] fn test_version_5() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D16(5).D16(0).D32(0).D32(0).D32(1) // Slots. .D64(0).D32(0); let buf = section.get_contents().unwrap(); let buf = EndianSlice::new(&buf, BigEndian); let index = UnitIndex::parse(buf).unwrap(); assert_eq!(index.version, 5); } #[test] fn test_version_5_invalid() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D32(5).D32(0).D32(0).D32(1) // Slots. .D64(0).D32(0); let buf = section.get_contents().unwrap(); let buf = EndianSlice::new(&buf, BigEndian); assert!(UnitIndex::parse(buf).is_err()); } #[test] fn test_version_2_sections() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D32(2).D32(8).D32(1).D32(2) // Slots. .D64(0).D64(0).D32(0).D32(0) // Sections. .D32(constants::DW_SECT_V2_INFO.0) .D32(constants::DW_SECT_V2_TYPES.0) .D32(constants::DW_SECT_V2_ABBREV.0) .D32(constants::DW_SECT_V2_LINE.0) .D32(constants::DW_SECT_V2_LOC.0) .D32(constants::DW_SECT_V2_STR_OFFSETS.0) .D32(constants::DW_SECT_V2_MACINFO.0) .D32(constants::DW_SECT_V2_MACRO.0) // Offsets. .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17).D32(18) // Sizes. .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27).D32(28); let buf = section.get_contents().unwrap(); let buf = EndianSlice::new(&buf, BigEndian); let index = UnitIndex::parse(buf).unwrap(); assert_eq!(index.section_count, 8); assert_eq!( index.sections, [ IndexSectionId::DebugInfo, IndexSectionId::DebugTypes, IndexSectionId::DebugAbbrev, IndexSectionId::DebugLine, IndexSectionId::DebugLoc, IndexSectionId::DebugStrOffsets, IndexSectionId::DebugMacinfo, IndexSectionId::DebugMacro, ] ); #[rustfmt::skip] let expect = [ UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 }, UnitIndexSection { section: IndexSectionId::DebugTypes, offset: 12, size: 22 }, UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 13, size: 23 }, UnitIndexSection { section: IndexSectionId::DebugLine, offset: 14, size: 24 }, UnitIndexSection { section: IndexSectionId::DebugLoc, offset: 15, size: 25 }, UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 16, size: 26 }, UnitIndexSection { section: IndexSectionId::DebugMacinfo, offset: 17, size: 27 }, UnitIndexSection { section: IndexSectionId::DebugMacro, offset: 18, size: 28 }, ]; let mut sections = index.sections(1).unwrap(); for section in &expect { assert_eq!(*section, sections.next().unwrap()); } assert!(sections.next().is_none()); } #[test] fn test_version_5_sections() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D16(5).D16(0).D32(7).D32(1).D32(2) // Slots. .D64(0).D64(0).D32(0).D32(0) // Sections. .D32(constants::DW_SECT_INFO.0) .D32(constants::DW_SECT_ABBREV.0) .D32(constants::DW_SECT_LINE.0) .D32(constants::DW_SECT_LOCLISTS.0) .D32(constants::DW_SECT_STR_OFFSETS.0) .D32(constants::DW_SECT_MACRO.0) .D32(constants::DW_SECT_RNGLISTS.0) // Offsets. .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17) // Sizes. .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27); let buf = section.get_contents().unwrap(); let buf = EndianSlice::new(&buf, BigEndian); let index = UnitIndex::parse(buf).unwrap(); assert_eq!(index.section_count, 7); assert_eq!( index.sections[..7], [ IndexSectionId::DebugInfo, IndexSectionId::DebugAbbrev, IndexSectionId::DebugLine, IndexSectionId::DebugLocLists, IndexSectionId::DebugStrOffsets, IndexSectionId::DebugMacro, IndexSectionId::DebugRngLists, ] ); #[rustfmt::skip] let expect = [ UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 }, UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 12, size: 22 }, UnitIndexSection { section: IndexSectionId::DebugLine, offset: 13, size: 23 }, UnitIndexSection { section: IndexSectionId::DebugLocLists, offset: 14, size: 24 }, UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 15, size: 25 }, UnitIndexSection { section: IndexSectionId::DebugMacro, offset: 16, size: 26 }, UnitIndexSection { section: IndexSectionId::DebugRngLists, offset: 17, size: 27 }, ]; let mut sections = index.sections(1).unwrap(); for section in &expect { assert_eq!(*section, sections.next().unwrap()); } assert!(sections.next().is_none()); assert!(index.sections(0).is_err()); assert!(index.sections(2).is_err()); } #[test] fn test_hash() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D16(5).D16(0).D32(2).D32(3).D32(4) // Slots. .D64(0xffff_fff2_ffff_fff1) .D64(0xffff_fff0_ffff_fff1) .D64(0xffff_fff1_ffff_fff1) .D64(0) .D32(3).D32(1).D32(2).D32(0) // Sections. .D32(constants::DW_SECT_INFO.0) .D32(constants::DW_SECT_ABBREV.0) // Offsets. .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0) // Sizes. .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0); let buf = section.get_contents().unwrap(); let buf = EndianSlice::new(&buf, BigEndian); let index = UnitIndex::parse(buf).unwrap(); assert_eq!(index.version(), 5); assert_eq!(index.slot_count(), 4); assert_eq!(index.unit_count(), 3); assert_eq!(index.section_count(), 2); assert_eq!(index.find(0xffff_fff0_ffff_fff1), Some(1)); assert_eq!(index.find(0xffff_fff1_ffff_fff1), Some(2)); assert_eq!(index.find(0xffff_fff2_ffff_fff1), Some(3)); assert_eq!(index.find(0xffff_fff3_ffff_fff1), None); } #[test] fn test_cu_index() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D16(5).D16(0).D32(0).D32(0).D32(1) // Slots. .D64(0).D32(0); let buf = section.get_contents().unwrap(); let cu_index = DebugCuIndex::new(&buf, BigEndian); let index = cu_index.index().unwrap(); assert_eq!(index.version, 5); } #[test] fn test_tu_index() { #[rustfmt::skip] let section = Section::with_endian(Endian::Big) // Header. .D16(5).D16(0).D32(0).D32(0).D32(1) // Slots. .D64(0).D32(0); let buf = section.get_contents().unwrap(); let tu_index = DebugTuIndex::new(&buf, BigEndian); let index = tu_index.index().unwrap(); assert_eq!(index.version, 5); } } gimli-0.31.1/src/read/line.rs000064400000000000000000003350001046102023000140070ustar 00000000000000use alloc::vec::Vec; use core::num::{NonZeroU64, Wrapping}; use crate::common::{ DebugLineOffset, DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsIndex, Encoding, Format, LineEncoding, SectionId, }; use crate::constants; use crate::endianity::Endianity; use crate::read::{ AttributeValue, EndianSlice, Error, Reader, ReaderAddress, ReaderOffset, Result, Section, }; /// The `DebugLine` struct contains the source location to instruction mapping /// found in the `.debug_line` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugLine { debug_line_section: R, } impl<'input, Endian> DebugLine> where Endian: Endianity, { /// Construct a new `DebugLine` instance from the data in the `.debug_line` /// section. /// /// It is the caller's responsibility to read the `.debug_line` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugLine, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_line_section_somehow = || &buf; /// let debug_line = DebugLine::new(read_debug_line_section_somehow(), LittleEndian); /// ``` pub fn new(debug_line_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_line_section, endian)) } } impl DebugLine { /// Parse the line number program whose header is at the given `offset` in the /// `.debug_line` section. /// /// The `address_size` must match the compilation unit that the lines apply to. /// The `comp_dir` should be from the `DW_AT_comp_dir` attribute of the compilation /// unit. The `comp_name` should be from the `DW_AT_name` attribute of the /// compilation unit. /// /// ```rust,no_run /// use gimli::{DebugLine, DebugLineOffset, IncompleteLineProgram, EndianSlice, LittleEndian}; /// /// # let buf = []; /// # let read_debug_line_section_somehow = || &buf; /// let debug_line = DebugLine::new(read_debug_line_section_somehow(), LittleEndian); /// /// // In a real example, we'd grab the offset via a compilation unit /// // entry's `DW_AT_stmt_list` attribute, and the address size from that /// // unit directly. /// let offset = DebugLineOffset(0); /// let address_size = 8; /// /// let program = debug_line.program(offset, address_size, None, None) /// .expect("should have found a header at that offset, and parsed it OK"); /// ``` pub fn program( &self, offset: DebugLineOffset, address_size: u8, comp_dir: Option, comp_name: Option, ) -> Result> { let input = &mut self.debug_line_section.clone(); input.skip(offset.0)?; let header = LineProgramHeader::parse(input, offset, address_size, comp_dir, comp_name)?; let program = IncompleteLineProgram { header }; Ok(program) } } impl DebugLine { /// Create a `DebugLine` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLine where F: FnMut(&'a T) -> R, { borrow(&self.debug_line_section).into() } } impl Section for DebugLine { fn id() -> SectionId { SectionId::DebugLine } fn reader(&self) -> &R { &self.debug_line_section } } impl From for DebugLine { fn from(debug_line_section: R) -> Self { DebugLine { debug_line_section } } } /// Deprecated. `LineNumberProgram` has been renamed to `LineProgram`. #[deprecated(note = "LineNumberProgram has been renamed to LineProgram, use that instead.")] pub type LineNumberProgram = dyn LineProgram; /// A `LineProgram` provides access to a `LineProgramHeader` and /// a way to add files to the files table if necessary. Gimli consumers should /// never need to use or see this trait. pub trait LineProgram::Offset> where R: Reader, Offset: ReaderOffset, { /// Get a reference to the held `LineProgramHeader`. fn header(&self) -> &LineProgramHeader; /// Add a file to the file table if necessary. fn add_file(&mut self, file: FileEntry); } impl LineProgram for IncompleteLineProgram where R: Reader, Offset: ReaderOffset, { fn header(&self) -> &LineProgramHeader { &self.header } fn add_file(&mut self, file: FileEntry) { self.header.file_names.push(file); } } impl<'program, R, Offset> LineProgram for &'program CompleteLineProgram where R: Reader, Offset: ReaderOffset, { fn header(&self) -> &LineProgramHeader { &self.header } fn add_file(&mut self, _: FileEntry) { // Nop. Our file table is already complete. } } /// Deprecated. `StateMachine` has been renamed to `LineRows`. #[deprecated(note = "StateMachine has been renamed to LineRows, use that instead.")] pub type StateMachine = LineRows; /// Executes a `LineProgram` to iterate over the rows in the matrix of line number information. /// /// "The hypothetical machine used by a consumer of the line number information /// to expand the byte-coded instruction stream into a matrix of line number /// information." -- Section 6.2.1 #[derive(Debug, Clone)] pub struct LineRows::Offset> where Program: LineProgram, R: Reader, Offset: ReaderOffset, { program: Program, row: LineRow, instructions: LineInstructions, } type OneShotLineRows::Offset> = LineRows, Offset>; type ResumedLineRows<'program, R, Offset = ::Offset> = LineRows, Offset>; impl LineRows where Program: LineProgram, R: Reader, Offset: ReaderOffset, { fn new(program: IncompleteLineProgram) -> OneShotLineRows { let row = LineRow::new(program.header()); let instructions = LineInstructions { input: program.header().program_buf.clone(), }; LineRows { program, row, instructions, } } fn resume<'program>( program: &'program CompleteLineProgram, sequence: &LineSequence, ) -> ResumedLineRows<'program, R, Offset> { let row = LineRow::new(program.header()); let instructions = sequence.instructions.clone(); LineRows { program, row, instructions, } } /// Get a reference to the header for this state machine's line number /// program. #[inline] pub fn header(&self) -> &LineProgramHeader { self.program.header() } /// Parse and execute the next instructions in the line number program until /// another row in the line number matrix is computed. /// /// The freshly computed row is returned as `Ok(Some((header, row)))`. /// If the matrix is complete, and there are no more new rows in the line /// number matrix, then `Ok(None)` is returned. If there was an error parsing /// an instruction, then `Err(e)` is returned. /// /// Unfortunately, the references mean that this cannot be a /// `FallibleIterator`. pub fn next_row(&mut self) -> Result, &LineRow)>> { // Perform any reset that was required after copying the previous row. self.row.reset(self.program.header()); loop { // Split the borrow here, rather than calling `self.header()`. match self.instructions.next_instruction(self.program.header()) { Err(err) => return Err(err), Ok(None) => return Ok(None), Ok(Some(instruction)) => { if self.row.execute(instruction, &mut self.program)? { if self.row.tombstone { // Perform any reset that was required for the tombstone row. // Normally this is done when `next_row` is called again, but for // tombstones we loop immediately. self.row.reset(self.program.header()); } else { return Ok(Some((self.header(), &self.row))); } } // Fall through, parse the next instruction, and see if that // yields a row. } } } } } /// Deprecated. `Opcode` has been renamed to `LineInstruction`. #[deprecated(note = "Opcode has been renamed to LineInstruction, use that instead.")] pub type Opcode = LineInstruction::Offset>; /// A parsed line number program instruction. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum LineInstruction::Offset> where R: Reader, Offset: ReaderOffset, { /// > ### 6.2.5.1 Special Opcodes /// > /// > Each ubyte special opcode has the following effect on the state machine: /// > /// > 1. Add a signed integer to the line register. /// > /// > 2. Modify the operation pointer by incrementing the address and /// > op_index registers as described below. /// > /// > 3. Append a row to the matrix using the current values of the state /// > machine registers. /// > /// > 4. Set the basic_block register to “false.” /// > /// > 5. Set the prologue_end register to “false.” /// > /// > 6. Set the epilogue_begin register to “false.” /// > /// > 7. Set the discriminator register to 0. /// > /// > All of the special opcodes do those same seven things; they differ from /// > one another only in what values they add to the line, address and /// > op_index registers. Special(u8), /// "[`LineInstruction::Copy`] appends a row to the matrix using the current /// values of the state machine registers. Then it sets the discriminator /// register to 0, and sets the basic_block, prologue_end and epilogue_begin /// registers to “false.”" Copy, /// "The DW_LNS_advance_pc opcode takes a single unsigned LEB128 operand as /// the operation advance and modifies the address and op_index registers /// [the same as `LineInstruction::Special`]" AdvancePc(u64), /// "The DW_LNS_advance_line opcode takes a single signed LEB128 operand and /// adds that value to the line register of the state machine." AdvanceLine(i64), /// "The DW_LNS_set_file opcode takes a single unsigned LEB128 operand and /// stores it in the file register of the state machine." SetFile(u64), /// "The DW_LNS_set_column opcode takes a single unsigned LEB128 operand and /// stores it in the column register of the state machine." SetColumn(u64), /// "The DW_LNS_negate_stmt opcode takes no operands. It sets the is_stmt /// register of the state machine to the logical negation of its current /// value." NegateStatement, /// "The DW_LNS_set_basic_block opcode takes no operands. It sets the /// basic_block register of the state machine to “true.”" SetBasicBlock, /// > The DW_LNS_const_add_pc opcode takes no operands. It advances the /// > address and op_index registers by the increments corresponding to /// > special opcode 255. /// > /// > When the line number program needs to advance the address by a small /// > amount, it can use a single special opcode, which occupies a single /// > byte. When it needs to advance the address by up to twice the range of /// > the last special opcode, it can use DW_LNS_const_add_pc followed by a /// > special opcode, for a total of two bytes. Only if it needs to advance /// > the address by more than twice that range will it need to use both /// > DW_LNS_advance_pc and a special opcode, requiring three or more bytes. ConstAddPc, /// > The DW_LNS_fixed_advance_pc opcode takes a single uhalf (unencoded) /// > operand and adds it to the address register of the state machine and /// > sets the op_index register to 0. This is the only standard opcode whose /// > operand is not a variable length number. It also does not multiply the /// > operand by the minimum_instruction_length field of the header. FixedAddPc(u16), /// "[`LineInstruction::SetPrologueEnd`] sets the prologue_end register to “true”." SetPrologueEnd, /// "[`LineInstruction::SetEpilogueBegin`] sets the epilogue_begin register to /// “true”." SetEpilogueBegin, /// "The DW_LNS_set_isa opcode takes a single unsigned LEB128 operand and /// stores that value in the isa register of the state machine." SetIsa(u64), /// An unknown standard opcode with zero operands. UnknownStandard0(constants::DwLns), /// An unknown standard opcode with one operand. UnknownStandard1(constants::DwLns, u64), /// An unknown standard opcode with multiple operands. UnknownStandardN(constants::DwLns, R), /// > [`LineInstruction::EndSequence`] sets the end_sequence register of the state /// > machine to “true” and appends a row to the matrix using the current /// > values of the state-machine registers. Then it resets the registers to /// > the initial values specified above (see Section 6.2.2). Every line /// > number program sequence must end with a DW_LNE_end_sequence instruction /// > which creates a row whose address is that of the byte after the last /// > target machine instruction of the sequence. EndSequence, /// > The DW_LNE_set_address opcode takes a single relocatable address as an /// > operand. The size of the operand is the size of an address on the target /// > machine. It sets the address register to the value given by the /// > relocatable address and sets the op_index register to 0. /// > /// > All of the other line number program opcodes that affect the address /// > register add a delta to it. This instruction stores a relocatable value /// > into it instead. SetAddress(u64), /// Defines a new source file in the line number program and appends it to /// the line number program header's list of source files. DefineFile(FileEntry), /// "The DW_LNE_set_discriminator opcode takes a single parameter, an /// unsigned LEB128 integer. It sets the discriminator register to the new /// value." SetDiscriminator(u64), /// An unknown extended opcode and the slice of its unparsed operands. UnknownExtended(constants::DwLne, R), } impl LineInstruction where R: Reader, Offset: ReaderOffset, { fn parse<'header>( header: &'header LineProgramHeader, input: &mut R, ) -> Result> where R: 'header, { let opcode = input.read_u8()?; if opcode == 0 { let length = input.read_uleb128().and_then(R::Offset::from_u64)?; let mut instr_rest = input.split(length)?; let opcode = instr_rest.read_u8()?; match constants::DwLne(opcode) { constants::DW_LNE_end_sequence => Ok(LineInstruction::EndSequence), constants::DW_LNE_set_address => { let address = instr_rest.read_address(header.address_size())?; Ok(LineInstruction::SetAddress(address)) } constants::DW_LNE_define_file => { if header.version() <= 4 { let path_name = instr_rest.read_null_terminated_slice()?; let entry = FileEntry::parse(&mut instr_rest, path_name)?; Ok(LineInstruction::DefineFile(entry)) } else { Ok(LineInstruction::UnknownExtended( constants::DW_LNE_define_file, instr_rest, )) } } constants::DW_LNE_set_discriminator => { let discriminator = instr_rest.read_uleb128()?; Ok(LineInstruction::SetDiscriminator(discriminator)) } otherwise => Ok(LineInstruction::UnknownExtended(otherwise, instr_rest)), } } else if opcode >= header.opcode_base { Ok(LineInstruction::Special(opcode)) } else { match constants::DwLns(opcode) { constants::DW_LNS_copy => Ok(LineInstruction::Copy), constants::DW_LNS_advance_pc => { let advance = input.read_uleb128()?; Ok(LineInstruction::AdvancePc(advance)) } constants::DW_LNS_advance_line => { let increment = input.read_sleb128()?; Ok(LineInstruction::AdvanceLine(increment)) } constants::DW_LNS_set_file => { let file = input.read_uleb128()?; Ok(LineInstruction::SetFile(file)) } constants::DW_LNS_set_column => { let column = input.read_uleb128()?; Ok(LineInstruction::SetColumn(column)) } constants::DW_LNS_negate_stmt => Ok(LineInstruction::NegateStatement), constants::DW_LNS_set_basic_block => Ok(LineInstruction::SetBasicBlock), constants::DW_LNS_const_add_pc => Ok(LineInstruction::ConstAddPc), constants::DW_LNS_fixed_advance_pc => { let advance = input.read_u16()?; Ok(LineInstruction::FixedAddPc(advance)) } constants::DW_LNS_set_prologue_end => Ok(LineInstruction::SetPrologueEnd), constants::DW_LNS_set_epilogue_begin => Ok(LineInstruction::SetEpilogueBegin), constants::DW_LNS_set_isa => { let isa = input.read_uleb128()?; Ok(LineInstruction::SetIsa(isa)) } otherwise => { let mut opcode_lengths = header.standard_opcode_lengths().clone(); opcode_lengths.skip(R::Offset::from_u8(opcode - 1))?; let num_args = opcode_lengths.read_u8()? as usize; match num_args { 0 => Ok(LineInstruction::UnknownStandard0(otherwise)), 1 => { let arg = input.read_uleb128()?; Ok(LineInstruction::UnknownStandard1(otherwise, arg)) } _ => { let mut args = input.clone(); for _ in 0..num_args { input.read_uleb128()?; } let len = input.offset_from(&args); args.truncate(len)?; Ok(LineInstruction::UnknownStandardN(otherwise, args)) } } } } } } } /// Deprecated. `OpcodesIter` has been renamed to `LineInstructions`. #[deprecated(note = "OpcodesIter has been renamed to LineInstructions, use that instead.")] pub type OpcodesIter = LineInstructions; /// An iterator yielding parsed instructions. /// /// See /// [`LineProgramHeader::instructions`](./struct.LineProgramHeader.html#method.instructions) /// for more details. #[derive(Clone, Debug)] pub struct LineInstructions { input: R, } impl LineInstructions { fn remove_trailing(&self, other: &LineInstructions) -> Result> { let offset = other.input.offset_from(&self.input); let mut input = self.input.clone(); input.truncate(offset)?; Ok(LineInstructions { input }) } } impl LineInstructions { /// Advance the iterator and return the next instruction. /// /// Returns the newly parsed instruction as `Ok(Some(instruction))`. Returns /// `Ok(None)` when iteration is complete and all instructions have already been /// parsed and yielded. If an error occurs while parsing the next attribute, /// then this error is returned as `Err(e)`, and all subsequent calls return /// `Ok(None)`. /// /// Unfortunately, the `header` parameter means that this cannot be a /// `FallibleIterator`. #[inline(always)] pub fn next_instruction( &mut self, header: &LineProgramHeader, ) -> Result>> { if self.input.is_empty() { return Ok(None); } match LineInstruction::parse(header, &mut self.input) { Ok(instruction) => Ok(Some(instruction)), Err(e) => { self.input.empty(); Err(e) } } } } /// Deprecated. `LineNumberRow` has been renamed to `LineRow`. #[deprecated(note = "LineNumberRow has been renamed to LineRow, use that instead.")] pub type LineNumberRow = LineRow; /// A row in the line number program's resulting matrix. /// /// Each row is a copy of the registers of the state machine, as defined in section 6.2.2. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct LineRow { tombstone: bool, address: u64, op_index: Wrapping, file: u64, line: Wrapping, column: u64, is_stmt: bool, basic_block: bool, end_sequence: bool, prologue_end: bool, epilogue_begin: bool, isa: u64, discriminator: u64, } impl LineRow { /// Create a line number row in the initial state for the given program. pub fn new(header: &LineProgramHeader) -> Self { LineRow { // "At the beginning of each sequence within a line number program, the // state of the registers is:" -- Section 6.2.2 tombstone: false, address: 0, op_index: Wrapping(0), file: 1, line: Wrapping(1), column: 0, // "determined by default_is_stmt in the line number program header" is_stmt: header.line_encoding.default_is_stmt, basic_block: false, end_sequence: false, prologue_end: false, epilogue_begin: false, // "The isa value 0 specifies that the instruction set is the // architecturally determined default instruction set. This may be fixed // by the ABI, or it may be specified by other means, for example, by // the object file description." isa: 0, discriminator: 0, } } /// "The program-counter value corresponding to a machine instruction /// generated by the compiler." #[inline] pub fn address(&self) -> u64 { self.address } /// > An unsigned integer representing the index of an operation within a VLIW /// > instruction. The index of the first operation is 0. For non-VLIW /// > architectures, this register will always be 0. /// > /// > The address and op_index registers, taken together, form an operation /// > pointer that can reference any individual operation with the /// > instruction stream. #[inline] pub fn op_index(&self) -> u64 { self.op_index.0 } /// "An unsigned integer indicating the identity of the source file /// corresponding to a machine instruction." #[inline] pub fn file_index(&self) -> u64 { self.file } /// The source file corresponding to the current machine instruction. #[inline] pub fn file<'header, R: Reader>( &self, header: &'header LineProgramHeader, ) -> Option<&'header FileEntry> { header.file(self.file) } /// "An unsigned integer indicating a source line number. Lines are numbered /// beginning at 1. The compiler may emit the value 0 in cases where an /// instruction cannot be attributed to any source line." /// Line number values of 0 are represented as `None`. #[inline] pub fn line(&self) -> Option { NonZeroU64::new(self.line.0) } /// "An unsigned integer indicating a column number within a source /// line. Columns are numbered beginning at 1. The value 0 is reserved to /// indicate that a statement begins at the “left edge” of the line." #[inline] pub fn column(&self) -> ColumnType { NonZeroU64::new(self.column) .map(ColumnType::Column) .unwrap_or(ColumnType::LeftEdge) } /// "A boolean indicating that the current instruction is a recommended /// breakpoint location. A recommended breakpoint location is intended to /// “represent” a line, a statement and/or a semantically distinct subpart /// of a statement." #[inline] pub fn is_stmt(&self) -> bool { self.is_stmt } /// "A boolean indicating that the current instruction is the beginning of a /// basic block." #[inline] pub fn basic_block(&self) -> bool { self.basic_block } /// "A boolean indicating that the current address is that of the first byte /// after the end of a sequence of target machine instructions. end_sequence /// terminates a sequence of lines; therefore other information in the same /// row is not meaningful." #[inline] pub fn end_sequence(&self) -> bool { self.end_sequence } /// "A boolean indicating that the current address is one (of possibly many) /// where execution should be suspended for an entry breakpoint of a /// function." #[inline] pub fn prologue_end(&self) -> bool { self.prologue_end } /// "A boolean indicating that the current address is one (of possibly many) /// where execution should be suspended for an exit breakpoint of a /// function." #[inline] pub fn epilogue_begin(&self) -> bool { self.epilogue_begin } /// Tag for the current instruction set architecture. /// /// > An unsigned integer whose value encodes the applicable instruction set /// > architecture for the current instruction. /// > /// > The encoding of instruction sets should be shared by all users of a /// > given architecture. It is recommended that this encoding be defined by /// > the ABI authoring committee for each architecture. #[inline] pub fn isa(&self) -> u64 { self.isa } /// "An unsigned integer identifying the block to which the current /// instruction belongs. Discriminator values are assigned arbitrarily by /// the DWARF producer and serve to distinguish among multiple blocks that /// may all be associated with the same source file, line, and column. Where /// only one block exists for a given source position, the discriminator /// value should be zero." #[inline] pub fn discriminator(&self) -> u64 { self.discriminator } /// Execute the given instruction, and return true if a new row in the /// line number matrix needs to be generated. /// /// Unknown opcodes are treated as no-ops. #[inline] pub fn execute( &mut self, instruction: LineInstruction, program: &mut Program, ) -> Result where Program: LineProgram, R: Reader, { Ok(match instruction { LineInstruction::Special(opcode) => { self.exec_special_opcode(opcode, program.header())?; true } LineInstruction::Copy => true, LineInstruction::AdvancePc(operation_advance) => { self.apply_operation_advance(operation_advance, program.header())?; false } LineInstruction::AdvanceLine(line_increment) => { self.apply_line_advance(line_increment); false } LineInstruction::SetFile(file) => { self.file = file; false } LineInstruction::SetColumn(column) => { self.column = column; false } LineInstruction::NegateStatement => { self.is_stmt = !self.is_stmt; false } LineInstruction::SetBasicBlock => { self.basic_block = true; false } LineInstruction::ConstAddPc => { let adjusted = self.adjust_opcode(255, program.header()); let operation_advance = adjusted / program.header().line_encoding.line_range; self.apply_operation_advance(u64::from(operation_advance), program.header())?; false } LineInstruction::FixedAddPc(operand) => { if !self.tombstone { let address_size = program.header().address_size(); self.address = self.address.add_sized(u64::from(operand), address_size)?; self.op_index.0 = 0; } false } LineInstruction::SetPrologueEnd => { self.prologue_end = true; false } LineInstruction::SetEpilogueBegin => { self.epilogue_begin = true; false } LineInstruction::SetIsa(isa) => { self.isa = isa; false } LineInstruction::EndSequence => { self.end_sequence = true; true } LineInstruction::SetAddress(address) => { // If the address is a tombstone, then skip instructions until the next address. // DWARF specifies a tombstone value of -1, but many linkers use 0. // However, 0 may be a valid address, so we only skip that if we have previously // seen a higher address. Additionally, gold may keep the relocation addend, // so we treat all lower addresses as tombstones instead of just 0. // This works because DWARF specifies that addresses are monotonically increasing // within a sequence; the alternative is to return an error. let tombstone_address = !0 >> (64 - program.header().encoding.address_size * 8); self.tombstone = address < self.address || address == tombstone_address; if !self.tombstone { self.address = address; self.op_index.0 = 0; } false } LineInstruction::DefineFile(entry) => { program.add_file(entry); false } LineInstruction::SetDiscriminator(discriminator) => { self.discriminator = discriminator; false } // Compatibility with future opcodes. LineInstruction::UnknownStandard0(_) | LineInstruction::UnknownStandard1(_, _) | LineInstruction::UnknownStandardN(_, _) | LineInstruction::UnknownExtended(_, _) => false, }) } /// Perform any reset that was required after copying the previous row. #[inline] pub fn reset(&mut self, header: &LineProgramHeader) { if self.end_sequence { // Previous instruction was EndSequence, so reset everything // as specified in Section 6.2.5.3. *self = Self::new(header); } else { // Previous instruction was one of: // - Special - specified in Section 6.2.5.1, steps 4-7 // - Copy - specified in Section 6.2.5.2 // The reset behaviour is the same in both cases. self.discriminator = 0; self.basic_block = false; self.prologue_end = false; self.epilogue_begin = false; } } /// Step 1 of section 6.2.5.1 fn apply_line_advance(&mut self, line_increment: i64) { if line_increment < 0 { let decrement = -line_increment as u64; if decrement <= self.line.0 { self.line.0 -= decrement; } else { self.line.0 = 0; } } else { self.line += Wrapping(line_increment as u64); } } /// Step 2 of section 6.2.5.1 fn apply_operation_advance( &mut self, operation_advance: u64, header: &LineProgramHeader, ) -> Result<()> { if self.tombstone { return Ok(()); } let operation_advance = Wrapping(operation_advance); let minimum_instruction_length = u64::from(header.line_encoding.minimum_instruction_length); let minimum_instruction_length = Wrapping(minimum_instruction_length); let maximum_operations_per_instruction = u64::from(header.line_encoding.maximum_operations_per_instruction); let maximum_operations_per_instruction = Wrapping(maximum_operations_per_instruction); let address_advance = if maximum_operations_per_instruction.0 == 1 { self.op_index.0 = 0; minimum_instruction_length * operation_advance } else { let op_index_with_advance = self.op_index + operation_advance; self.op_index = op_index_with_advance % maximum_operations_per_instruction; minimum_instruction_length * (op_index_with_advance / maximum_operations_per_instruction) }; self.address = self .address .add_sized(address_advance.0, header.address_size())?; Ok(()) } #[inline] fn adjust_opcode(&self, opcode: u8, header: &LineProgramHeader) -> u8 { opcode - header.opcode_base } /// Section 6.2.5.1 fn exec_special_opcode( &mut self, opcode: u8, header: &LineProgramHeader, ) -> Result<()> { let adjusted_opcode = self.adjust_opcode(opcode, header); let line_range = header.line_encoding.line_range; let line_advance = adjusted_opcode % line_range; let operation_advance = adjusted_opcode / line_range; // Step 1 let line_base = i64::from(header.line_encoding.line_base); self.apply_line_advance(line_base + i64::from(line_advance)); // Step 2 self.apply_operation_advance(u64::from(operation_advance), header)?; Ok(()) } } /// The type of column that a row is referring to. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum ColumnType { /// The `LeftEdge` means that the statement begins at the start of the new /// line. LeftEdge, /// A column number, whose range begins at 1. Column(NonZeroU64), } /// Deprecated. `LineNumberSequence` has been renamed to `LineSequence`. #[deprecated(note = "LineNumberSequence has been renamed to LineSequence, use that instead.")] pub type LineNumberSequence = LineSequence; /// A sequence within a line number program. A sequence, as defined in section /// 6.2.5 of the standard, is a linear subset of a line number program within /// which addresses are monotonically increasing. #[derive(Clone, Debug)] pub struct LineSequence { /// The first address that is covered by this sequence within the line number /// program. pub start: u64, /// The first address that is *not* covered by this sequence within the line /// number program. pub end: u64, instructions: LineInstructions, } /// Deprecated. `LineNumberProgramHeader` has been renamed to `LineProgramHeader`. #[deprecated( note = "LineNumberProgramHeader has been renamed to LineProgramHeader, use that instead." )] pub type LineNumberProgramHeader = LineProgramHeader; /// A header for a line number program in the `.debug_line` section, as defined /// in section 6.2.4 of the standard. #[derive(Clone, Debug, Eq, PartialEq)] pub struct LineProgramHeader::Offset> where R: Reader, Offset: ReaderOffset, { encoding: Encoding, offset: DebugLineOffset, unit_length: Offset, header_length: Offset, line_encoding: LineEncoding, /// "The number assigned to the first special opcode." opcode_base: u8, /// "This array specifies the number of LEB128 operands for each of the /// standard opcodes. The first element of the array corresponds to the /// opcode whose value is 1, and the last element corresponds to the opcode /// whose value is `opcode_base - 1`." standard_opcode_lengths: R, /// "A sequence of directory entry format descriptions." directory_entry_format: Vec, /// > Entries in this sequence describe each path that was searched for /// > included source files in this compilation. (The paths include those /// > directories specified explicitly by the user for the compiler to search /// > and those the compiler searches without explicit direction.) Each path /// > entry is either a full path name or is relative to the current directory /// > of the compilation. /// > /// > The last entry is followed by a single null byte. include_directories: Vec>, /// "A sequence of file entry format descriptions." file_name_entry_format: Vec, /// "Entries in this sequence describe source files that contribute to the /// line number information for this compilation unit or is used in other /// contexts." file_names: Vec>, /// The encoded line program instructions. program_buf: R, /// The current directory of the compilation. comp_dir: Option, /// The primary source file. comp_file: Option>, } impl LineProgramHeader where R: Reader, Offset: ReaderOffset, { /// Return the offset of the line number program header in the `.debug_line` section. pub fn offset(&self) -> DebugLineOffset { self.offset } /// Return the length of the line number program and header, not including /// the length of the encoded length itself. pub fn unit_length(&self) -> R::Offset { self.unit_length } /// Return the encoding parameters for this header's line program. pub fn encoding(&self) -> Encoding { self.encoding } /// Get the version of this header's line program. pub fn version(&self) -> u16 { self.encoding.version } /// Get the length of the encoded line number program header, not including /// the length of the encoded length itself. pub fn header_length(&self) -> R::Offset { self.header_length } /// Get the size in bytes of a target machine address. pub fn address_size(&self) -> u8 { self.encoding.address_size } /// Whether this line program is encoded in 64- or 32-bit DWARF. pub fn format(&self) -> Format { self.encoding.format } /// Get the line encoding parameters for this header's line program. pub fn line_encoding(&self) -> LineEncoding { self.line_encoding } /// Get the minimum instruction length any instruction in this header's line /// program may have. pub fn minimum_instruction_length(&self) -> u8 { self.line_encoding.minimum_instruction_length } /// Get the maximum number of operations each instruction in this header's /// line program may have. pub fn maximum_operations_per_instruction(&self) -> u8 { self.line_encoding.maximum_operations_per_instruction } /// Get the default value of the `is_stmt` register for this header's line /// program. pub fn default_is_stmt(&self) -> bool { self.line_encoding.default_is_stmt } /// Get the line base for this header's line program. pub fn line_base(&self) -> i8 { self.line_encoding.line_base } /// Get the line range for this header's line program. pub fn line_range(&self) -> u8 { self.line_encoding.line_range } /// Get opcode base for this header's line program. pub fn opcode_base(&self) -> u8 { self.opcode_base } /// An array of `u8` that specifies the number of LEB128 operands for /// each of the standard opcodes. pub fn standard_opcode_lengths(&self) -> &R { &self.standard_opcode_lengths } /// Get the format of a directory entry. pub fn directory_entry_format(&self) -> &[FileEntryFormat] { &self.directory_entry_format[..] } /// Get the set of include directories for this header's line program. /// /// For DWARF version <= 4, the compilation's current directory is not included /// in the return value, but is implicitly considered to be in the set per spec. pub fn include_directories(&self) -> &[AttributeValue] { &self.include_directories[..] } /// The include directory with the given directory index. /// /// A directory index of 0 corresponds to the compilation unit directory. pub fn directory(&self, directory: u64) -> Option> { if self.encoding.version <= 4 { if directory == 0 { self.comp_dir.clone().map(AttributeValue::String) } else { let directory = directory as usize - 1; self.include_directories.get(directory).cloned() } } else { self.include_directories.get(directory as usize).cloned() } } /// Get the format of a file name entry. pub fn file_name_entry_format(&self) -> &[FileEntryFormat] { &self.file_name_entry_format[..] } /// Return true if the file entries may have valid timestamps. /// /// Only returns false if we definitely know that all timestamp fields /// are invalid. pub fn file_has_timestamp(&self) -> bool { self.encoding.version <= 4 || self .file_name_entry_format .iter() .any(|x| x.content_type == constants::DW_LNCT_timestamp) } /// Return true if the file entries may have valid sizes. /// /// Only returns false if we definitely know that all size fields /// are invalid. pub fn file_has_size(&self) -> bool { self.encoding.version <= 4 || self .file_name_entry_format .iter() .any(|x| x.content_type == constants::DW_LNCT_size) } /// Return true if the file name entry format contains an MD5 field. pub fn file_has_md5(&self) -> bool { self.file_name_entry_format .iter() .any(|x| x.content_type == constants::DW_LNCT_MD5) } /// Return true if the file name entry format contains a source field. pub fn file_has_source(&self) -> bool { self.file_name_entry_format .iter() .any(|x| x.content_type == constants::DW_LNCT_LLVM_source) } /// Get the list of source files that appear in this header's line program. pub fn file_names(&self) -> &[FileEntry] { &self.file_names[..] } /// The source file with the given file index. /// /// A file index of 0 corresponds to the compilation unit file. /// Note that a file index of 0 is invalid for DWARF version <= 4, /// but we support it anyway. pub fn file(&self, file: u64) -> Option<&FileEntry> { if self.encoding.version <= 4 { if file == 0 { self.comp_file.as_ref() } else { let file = file as usize - 1; self.file_names.get(file) } } else { self.file_names.get(file as usize) } } /// Get the raw, un-parsed `EndianSlice` containing this header's line number /// program. /// /// ``` /// # fn foo() { /// use gimli::{LineProgramHeader, EndianSlice, NativeEndian}; /// /// fn get_line_number_program_header<'a>() -> LineProgramHeader> { /// // Get a line number program header from some offset in a /// // `.debug_line` section... /// # unimplemented!() /// } /// /// let header = get_line_number_program_header(); /// let raw_program = header.raw_program_buf(); /// println!("The length of the raw program in bytes is {}", raw_program.len()); /// # } /// ``` pub fn raw_program_buf(&self) -> R { self.program_buf.clone() } /// Iterate over the instructions in this header's line number program, parsing /// them as we go. pub fn instructions(&self) -> LineInstructions { LineInstructions { input: self.program_buf.clone(), } } fn parse( input: &mut R, offset: DebugLineOffset, mut address_size: u8, mut comp_dir: Option, comp_name: Option, ) -> Result> { let (unit_length, format) = input.read_initial_length()?; let rest = &mut input.split(unit_length)?; let version = rest.read_u16()?; if version < 2 || version > 5 { return Err(Error::UnknownVersion(u64::from(version))); } if version >= 5 { address_size = rest.read_address_size()?; let segment_selector_size = rest.read_u8()?; if segment_selector_size != 0 { return Err(Error::UnsupportedSegmentSize); } } let encoding = Encoding { format, version, address_size, }; let header_length = rest.read_length(format)?; let mut program_buf = rest.clone(); program_buf.skip(header_length)?; rest.truncate(header_length)?; let minimum_instruction_length = rest.read_u8()?; if minimum_instruction_length == 0 { return Err(Error::MinimumInstructionLengthZero); } // This field did not exist before DWARF 4, but is specified to be 1 for // non-VLIW architectures, which makes it a no-op. let maximum_operations_per_instruction = if version >= 4 { rest.read_u8()? } else { 1 }; if maximum_operations_per_instruction == 0 { return Err(Error::MaximumOperationsPerInstructionZero); } let default_is_stmt = rest.read_u8()? != 0; let line_base = rest.read_i8()?; let line_range = rest.read_u8()?; if line_range == 0 { return Err(Error::LineRangeZero); } let line_encoding = LineEncoding { minimum_instruction_length, maximum_operations_per_instruction, default_is_stmt, line_base, line_range, }; let opcode_base = rest.read_u8()?; if opcode_base == 0 { return Err(Error::OpcodeBaseZero); } let standard_opcode_count = R::Offset::from_u8(opcode_base - 1); let standard_opcode_lengths = rest.split(standard_opcode_count)?; let directory_entry_format; let mut include_directories = Vec::new(); if version <= 4 { directory_entry_format = Vec::new(); loop { let directory = rest.read_null_terminated_slice()?; if directory.is_empty() { break; } include_directories.push(AttributeValue::String(directory)); } } else { comp_dir = None; directory_entry_format = FileEntryFormat::parse(rest)?; let count = rest.read_uleb128()?; for _ in 0..count { include_directories.push(parse_directory_v5( rest, encoding, &directory_entry_format, )?); } } let comp_file; let file_name_entry_format; let mut file_names = Vec::new(); if version <= 4 { comp_file = comp_name.map(|name| FileEntry { path_name: AttributeValue::String(name), directory_index: 0, timestamp: 0, size: 0, md5: [0; 16], source: None, }); file_name_entry_format = Vec::new(); loop { let path_name = rest.read_null_terminated_slice()?; if path_name.is_empty() { break; } file_names.push(FileEntry::parse(rest, path_name)?); } } else { comp_file = None; file_name_entry_format = FileEntryFormat::parse(rest)?; let count = rest.read_uleb128()?; for _ in 0..count { file_names.push(parse_file_v5(rest, encoding, &file_name_entry_format)?); } } let header = LineProgramHeader { encoding, offset, unit_length, header_length, line_encoding, opcode_base, standard_opcode_lengths, directory_entry_format, include_directories, file_name_entry_format, file_names, program_buf, comp_dir, comp_file, }; Ok(header) } } /// Deprecated. `IncompleteLineNumberProgram` has been renamed to `IncompleteLineProgram`. #[deprecated( note = "IncompleteLineNumberProgram has been renamed to IncompleteLineProgram, use that instead." )] pub type IncompleteLineNumberProgram = IncompleteLineProgram; /// A line number program that has not been run to completion. #[derive(Clone, Debug, Eq, PartialEq)] pub struct IncompleteLineProgram::Offset> where R: Reader, Offset: ReaderOffset, { header: LineProgramHeader, } impl IncompleteLineProgram where R: Reader, Offset: ReaderOffset, { /// Retrieve the `LineProgramHeader` for this program. pub fn header(&self) -> &LineProgramHeader { &self.header } /// Construct a new `LineRows` for executing this program to iterate /// over rows in the line information matrix. pub fn rows(self) -> OneShotLineRows { OneShotLineRows::new(self) } /// Execute the line number program, completing the `IncompleteLineProgram` /// into a `CompleteLineProgram` and producing an array of sequences within /// the line number program that can later be used with /// `CompleteLineProgram::resume_from`. /// /// ``` /// # fn foo() { /// use gimli::{IncompleteLineProgram, EndianSlice, NativeEndian}; /// /// fn get_line_number_program<'a>() -> IncompleteLineProgram> { /// // Get a line number program from some offset in a /// // `.debug_line` section... /// # unimplemented!() /// } /// /// let program = get_line_number_program(); /// let (program, sequences) = program.sequences().unwrap(); /// println!("There are {} sequences in this line number program", sequences.len()); /// # } /// ``` #[allow(clippy::type_complexity)] pub fn sequences(self) -> Result<(CompleteLineProgram, Vec>)> { let mut sequences = Vec::new(); let mut rows = self.rows(); let mut instructions = rows.instructions.clone(); let mut sequence_start_addr = None; loop { let sequence_end_addr; if rows.next_row()?.is_none() { break; } let row = &rows.row; if row.end_sequence() { sequence_end_addr = row.address(); } else if sequence_start_addr.is_none() { sequence_start_addr = Some(row.address()); continue; } else { continue; } // We just finished a sequence. sequences.push(LineSequence { // In theory one could have multiple DW_LNE_end_sequence instructions // in a row. start: sequence_start_addr.unwrap_or(0), end: sequence_end_addr, instructions: instructions.remove_trailing(&rows.instructions)?, }); sequence_start_addr = None; instructions = rows.instructions.clone(); } let program = CompleteLineProgram { header: rows.program.header, }; Ok((program, sequences)) } } /// Deprecated. `CompleteLineNumberProgram` has been renamed to `CompleteLineProgram`. #[deprecated( note = "CompleteLineNumberProgram has been renamed to CompleteLineProgram, use that instead." )] pub type CompleteLineNumberProgram = CompleteLineProgram; /// A line number program that has previously been run to completion. #[derive(Clone, Debug, Eq, PartialEq)] pub struct CompleteLineProgram::Offset> where R: Reader, Offset: ReaderOffset, { header: LineProgramHeader, } impl CompleteLineProgram where R: Reader, Offset: ReaderOffset, { /// Retrieve the `LineProgramHeader` for this program. pub fn header(&self) -> &LineProgramHeader { &self.header } /// Construct a new `LineRows` for executing the subset of the line /// number program identified by 'sequence' and generating the line information /// matrix. /// /// ``` /// # fn foo() { /// use gimli::{IncompleteLineProgram, EndianSlice, NativeEndian}; /// /// fn get_line_number_program<'a>() -> IncompleteLineProgram> { /// // Get a line number program from some offset in a /// // `.debug_line` section... /// # unimplemented!() /// } /// /// let program = get_line_number_program(); /// let (program, sequences) = program.sequences().unwrap(); /// for sequence in &sequences { /// let mut sm = program.resume_from(sequence); /// } /// # } /// ``` pub fn resume_from<'program>( &'program self, sequence: &LineSequence, ) -> ResumedLineRows<'program, R, Offset> { ResumedLineRows::resume(self, sequence) } } /// An entry in the `LineProgramHeader`'s `file_names` set. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct FileEntry::Offset> where R: Reader, Offset: ReaderOffset, { path_name: AttributeValue, directory_index: u64, timestamp: u64, size: u64, md5: [u8; 16], source: Option>, } impl FileEntry where R: Reader, Offset: ReaderOffset, { // version 2-4 fn parse(input: &mut R, path_name: R) -> Result> { let directory_index = input.read_uleb128()?; let timestamp = input.read_uleb128()?; let size = input.read_uleb128()?; let entry = FileEntry { path_name: AttributeValue::String(path_name), directory_index, timestamp, size, md5: [0; 16], source: None, }; Ok(entry) } /// > A slice containing the full or relative path name of /// > a source file. If the entry contains a file name or a relative path /// > name, the file is located relative to either the compilation directory /// > (as specified by the DW_AT_comp_dir attribute given in the compilation /// > unit) or one of the directories in the include_directories section. pub fn path_name(&self) -> AttributeValue { self.path_name.clone() } /// > An unsigned LEB128 number representing the directory index of the /// > directory in which the file was found. /// > /// > ... /// > /// > The directory index represents an entry in the include_directories /// > section of the line number program header. The index is 0 if the file /// > was found in the current directory of the compilation, 1 if it was found /// > in the first directory in the include_directories section, and so /// > on. The directory index is ignored for file names that represent full /// > path names. pub fn directory_index(&self) -> u64 { self.directory_index } /// Get this file's directory. /// /// A directory index of 0 corresponds to the compilation unit directory. pub fn directory(&self, header: &LineProgramHeader) -> Option> { header.directory(self.directory_index) } /// The implementation-defined time of last modification of the file, /// or 0 if not available. pub fn timestamp(&self) -> u64 { self.timestamp } /// "An unsigned LEB128 number representing the time of last modification of /// the file, or 0 if not available." // Terminology changed in DWARF version 5. #[doc(hidden)] pub fn last_modification(&self) -> u64 { self.timestamp } /// The size of the file in bytes, or 0 if not available. pub fn size(&self) -> u64 { self.size } /// "An unsigned LEB128 number representing the length in bytes of the file, /// or 0 if not available." // Terminology changed in DWARF version 5. #[doc(hidden)] pub fn length(&self) -> u64 { self.size } /// A 16-byte MD5 digest of the file contents. /// /// Only valid if `LineProgramHeader::file_has_md5` returns `true`. pub fn md5(&self) -> &[u8; 16] { &self.md5 } /// The source code of this file. (UTF-8 source text string with "\n" line /// endings). /// /// Note: For DWARF v5 files this may return an empty attribute that /// indicates that no source code is available, which this function /// represents as `Some()`. pub fn source(&self) -> Option> { self.source.clone() } } /// The format of a component of an include directory or file name entry. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct FileEntryFormat { /// The type of information that is represented by the component. pub content_type: constants::DwLnct, /// The encoding form of the component value. pub form: constants::DwForm, } impl FileEntryFormat { fn parse(input: &mut R) -> Result> { let format_count = input.read_u8()? as usize; let mut format = Vec::with_capacity(format_count); let mut path_count = 0; for _ in 0..format_count { let content_type = input.read_uleb128()?; let content_type = if content_type > u64::from(u16::MAX) { constants::DwLnct(u16::MAX) } else { constants::DwLnct(content_type as u16) }; if content_type == constants::DW_LNCT_path { path_count += 1; } let form = constants::DwForm(input.read_uleb128_u16()?); format.push(FileEntryFormat { content_type, form }); } if path_count != 1 { return Err(Error::MissingFileEntryFormatPath); } Ok(format) } } fn parse_directory_v5( input: &mut R, encoding: Encoding, formats: &[FileEntryFormat], ) -> Result> { let mut path_name = None; for format in formats { let value = parse_attribute(input, encoding, format.form)?; if format.content_type == constants::DW_LNCT_path { path_name = Some(value); } } Ok(path_name.unwrap()) } fn parse_file_v5( input: &mut R, encoding: Encoding, formats: &[FileEntryFormat], ) -> Result> { let mut path_name = None; let mut directory_index = 0; let mut timestamp = 0; let mut size = 0; let mut md5 = [0; 16]; let mut source = None; for format in formats { let value = parse_attribute(input, encoding, format.form)?; match format.content_type { constants::DW_LNCT_path => path_name = Some(value), constants::DW_LNCT_directory_index => { if let Some(value) = value.udata_value() { directory_index = value; } } constants::DW_LNCT_timestamp => { if let Some(value) = value.udata_value() { timestamp = value; } } constants::DW_LNCT_size => { if let Some(value) = value.udata_value() { size = value; } } constants::DW_LNCT_MD5 => { if let AttributeValue::Block(mut value) = value { if value.len().into_u64() == 16 { md5 = value.read_u8_array()?; } } } constants::DW_LNCT_LLVM_source => { source = Some(value); } // Ignore unknown content types. _ => {} } } Ok(FileEntry { path_name: path_name.unwrap(), directory_index, timestamp, size, md5, source, }) } // TODO: this should be shared with unit::parse_attribute(), but that is hard to do. fn parse_attribute( input: &mut R, encoding: Encoding, form: constants::DwForm, ) -> Result> { Ok(match form { constants::DW_FORM_block1 => { let len = input.read_u8().map(R::Offset::from_u8)?; let block = input.split(len)?; AttributeValue::Block(block) } constants::DW_FORM_block2 => { let len = input.read_u16().map(R::Offset::from_u16)?; let block = input.split(len)?; AttributeValue::Block(block) } constants::DW_FORM_block4 => { let len = input.read_u32().map(R::Offset::from_u32)?; let block = input.split(len)?; AttributeValue::Block(block) } constants::DW_FORM_block => { let len = input.read_uleb128().and_then(R::Offset::from_u64)?; let block = input.split(len)?; AttributeValue::Block(block) } constants::DW_FORM_data1 => { let data = input.read_u8()?; AttributeValue::Data1(data) } constants::DW_FORM_data2 => { let data = input.read_u16()?; AttributeValue::Data2(data) } constants::DW_FORM_data4 => { let data = input.read_u32()?; AttributeValue::Data4(data) } constants::DW_FORM_data8 => { let data = input.read_u64()?; AttributeValue::Data8(data) } constants::DW_FORM_data16 => { let block = input.split(R::Offset::from_u8(16))?; AttributeValue::Block(block) } constants::DW_FORM_udata => { let data = input.read_uleb128()?; AttributeValue::Udata(data) } constants::DW_FORM_sdata => { let data = input.read_sleb128()?; AttributeValue::Sdata(data) } constants::DW_FORM_flag => { let present = input.read_u8()?; AttributeValue::Flag(present != 0) } constants::DW_FORM_sec_offset => { let offset = input.read_offset(encoding.format)?; AttributeValue::SecOffset(offset) } constants::DW_FORM_string => { let string = input.read_null_terminated_slice()?; AttributeValue::String(string) } constants::DW_FORM_strp => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugStrRef(DebugStrOffset(offset)) } constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugStrRefSup(DebugStrOffset(offset)) } constants::DW_FORM_line_strp => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset)) } constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx1 => { let index = input.read_u8().map(R::Offset::from_u8)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx2 => { let index = input.read_u16().map(R::Offset::from_u16)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx3 => { let index = input.read_uint(3).and_then(R::Offset::from_u64)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx4 => { let index = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } _ => { return Err(Error::UnknownForm(form)); } }) } #[cfg(test)] mod tests { use super::*; use crate::constants; use crate::endianity::LittleEndian; use crate::read::{EndianSlice, Error}; use crate::test_util::GimliSectionMethods; use test_assembler::{Endian, Label, LabelMaker, Section}; #[test] fn test_parse_debug_line_32_ok() { #[rustfmt::skip] let buf = [ // 32-bit length = 62. 0x3e, 0x00, 0x00, 0x00, // Version. 0x04, 0x00, // Header length = 40. 0x28, 0x00, 0x00, 0x00, // Minimum instruction length. 0x01, // Maximum operations per byte. 0x01, // Default is_stmt. 0x01, // Line base. 0x00, // Line range. 0x01, // Opcode base. 0x03, // Standard opcode lengths for opcodes 1 .. opcode base - 1. 0x01, 0x02, // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, // File names // foo.rs 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, // bar.h 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, 0x01, 0x00, 0x00, // End file names. 0x00, // Dummy line program data. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy next line program. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let rest = &mut EndianSlice::new(&buf, LittleEndian); let comp_dir = EndianSlice::new(b"/comp_dir", LittleEndian); let comp_name = EndianSlice::new(b"/comp_name", LittleEndian); let header = LineProgramHeader::parse(rest, DebugLineOffset(0), 4, Some(comp_dir), Some(comp_name)) .expect("should parse header ok"); assert_eq!( *rest, EndianSlice::new(&buf[buf.len() - 16..], LittleEndian) ); assert_eq!(header.offset, DebugLineOffset(0)); assert_eq!(header.version(), 4); assert_eq!(header.minimum_instruction_length(), 1); assert_eq!(header.maximum_operations_per_instruction(), 1); assert!(header.default_is_stmt()); assert_eq!(header.line_base(), 0); assert_eq!(header.line_range(), 1); assert_eq!(header.opcode_base(), 3); assert_eq!(header.directory(0), Some(AttributeValue::String(comp_dir))); assert_eq!( header.file(0).unwrap().path_name, AttributeValue::String(comp_name) ); let expected_lengths = [1, 2]; assert_eq!(header.standard_opcode_lengths().slice(), &expected_lengths); let expected_include_directories = [ AttributeValue::String(EndianSlice::new(b"/inc", LittleEndian)), AttributeValue::String(EndianSlice::new(b"/inc2", LittleEndian)), ]; assert_eq!(header.include_directories(), &expected_include_directories); let expected_file_names = [ FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"foo.rs", LittleEndian)), directory_index: 0, timestamp: 0, size: 0, md5: [0; 16], source: None, }, FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)), directory_index: 1, timestamp: 0, size: 0, md5: [0; 16], source: None, }, ]; assert_eq!(header.file_names(), &expected_file_names); } #[test] fn test_parse_debug_line_header_length_too_short() { #[rustfmt::skip] let buf = [ // 32-bit length = 62. 0x3e, 0x00, 0x00, 0x00, // Version. 0x04, 0x00, // Header length = 20. TOO SHORT!!! 0x15, 0x00, 0x00, 0x00, // Minimum instruction length. 0x01, // Maximum operations per byte. 0x01, // Default is_stmt. 0x01, // Line base. 0x00, // Line range. 0x01, // Opcode base. 0x03, // Standard opcode lengths for opcodes 1 .. opcode base - 1. 0x01, 0x02, // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, // File names // foo.rs 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, // bar.h 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, 0x01, 0x00, 0x00, // End file names. 0x00, // Dummy line program data. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy next line program. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let input = &mut EndianSlice::new(&buf, LittleEndian); match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] fn test_parse_debug_line_unit_length_too_short() { #[rustfmt::skip] let buf = [ // 32-bit length = 40. TOO SHORT!!! 0x28, 0x00, 0x00, 0x00, // Version. 0x04, 0x00, // Header length = 40. 0x28, 0x00, 0x00, 0x00, // Minimum instruction length. 0x01, // Maximum operations per byte. 0x01, // Default is_stmt. 0x01, // Line base. 0x00, // Line range. 0x01, // Opcode base. 0x03, // Standard opcode lengths for opcodes 1 .. opcode base - 1. 0x01, 0x02, // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, // File names // foo.rs 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, // bar.h 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, 0x01, 0x00, 0x00, // End file names. 0x00, // Dummy line program data. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dummy next line program. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let input = &mut EndianSlice::new(&buf, LittleEndian); match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } const OPCODE_BASE: u8 = 13; const STANDARD_OPCODE_LENGTHS: &[u8] = &[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1]; fn make_test_header( buf: EndianSlice<'_, LittleEndian>, ) -> LineProgramHeader> { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 8, }; let line_encoding = LineEncoding { line_base: -3, line_range: 12, ..Default::default() }; LineProgramHeader { encoding, offset: DebugLineOffset(0), unit_length: 1, header_length: 1, line_encoding, opcode_base: OPCODE_BASE, standard_opcode_lengths: EndianSlice::new(STANDARD_OPCODE_LENGTHS, LittleEndian), file_names: vec![ FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)), directory_index: 0, timestamp: 0, size: 0, md5: [0; 16], source: None, }, FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)), directory_index: 0, timestamp: 0, size: 0, md5: [0; 16], source: None, }, ], include_directories: vec![], directory_entry_format: vec![], file_name_entry_format: vec![], program_buf: buf, comp_dir: None, comp_file: None, } } fn make_test_program( buf: EndianSlice<'_, LittleEndian>, ) -> IncompleteLineProgram> { IncompleteLineProgram { header: make_test_header(buf), } } #[test] fn test_parse_special_opcodes() { for i in OPCODE_BASE..u8::MAX { let input = [i, 0, 0, 0]; let input = EndianSlice::new(&input, LittleEndian); let header = make_test_header(input); let mut rest = input; let opcode = LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); assert_eq!(*rest, *input.range_from(1..)); assert_eq!(opcode, LineInstruction::Special(i)); } } #[test] fn test_parse_standard_opcodes() { fn test( raw: constants::DwLns, operands: Operands, expected: LineInstruction>, ) where Operands: AsRef<[u8]>, { let mut input = Vec::new(); input.push(raw.0); input.extend_from_slice(operands.as_ref()); let expected_rest = [0, 1, 2, 3, 4]; input.extend_from_slice(&expected_rest); let input = EndianSlice::new(&input, LittleEndian); let header = make_test_header(input); let mut rest = input; let opcode = LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); assert_eq!(opcode, expected); assert_eq!(*rest, expected_rest); } test(constants::DW_LNS_copy, [], LineInstruction::Copy); test( constants::DW_LNS_advance_pc, [42], LineInstruction::AdvancePc(42), ); test( constants::DW_LNS_advance_line, [9], LineInstruction::AdvanceLine(9), ); test(constants::DW_LNS_set_file, [7], LineInstruction::SetFile(7)); test( constants::DW_LNS_set_column, [1], LineInstruction::SetColumn(1), ); test( constants::DW_LNS_negate_stmt, [], LineInstruction::NegateStatement, ); test( constants::DW_LNS_set_basic_block, [], LineInstruction::SetBasicBlock, ); test( constants::DW_LNS_const_add_pc, [], LineInstruction::ConstAddPc, ); test( constants::DW_LNS_fixed_advance_pc, [42, 0], LineInstruction::FixedAddPc(42), ); test( constants::DW_LNS_set_prologue_end, [], LineInstruction::SetPrologueEnd, ); test( constants::DW_LNS_set_isa, [57 + 0x80, 100], LineInstruction::SetIsa(12857), ); } #[test] fn test_parse_unknown_standard_opcode_no_args() { let input = [OPCODE_BASE, 1, 2, 3]; let input = EndianSlice::new(&input, LittleEndian); let mut standard_opcode_lengths = Vec::new(); let mut header = make_test_header(input); standard_opcode_lengths.extend(header.standard_opcode_lengths.slice()); standard_opcode_lengths.push(0); header.opcode_base += 1; header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian); let mut rest = input; let opcode = LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); assert_eq!( opcode, LineInstruction::UnknownStandard0(constants::DwLns(OPCODE_BASE)) ); assert_eq!(*rest, *input.range_from(1..)); } #[test] fn test_parse_unknown_standard_opcode_one_arg() { let input = [OPCODE_BASE, 1, 2, 3]; let input = EndianSlice::new(&input, LittleEndian); let mut standard_opcode_lengths = Vec::new(); let mut header = make_test_header(input); standard_opcode_lengths.extend(header.standard_opcode_lengths.slice()); standard_opcode_lengths.push(1); header.opcode_base += 1; header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian); let mut rest = input; let opcode = LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); assert_eq!( opcode, LineInstruction::UnknownStandard1(constants::DwLns(OPCODE_BASE), 1) ); assert_eq!(*rest, *input.range_from(2..)); } #[test] fn test_parse_unknown_standard_opcode_many_args() { let input = [OPCODE_BASE, 1, 2, 3]; let input = EndianSlice::new(&input, LittleEndian); let args = input.range_from(1..); let mut standard_opcode_lengths = Vec::new(); let mut header = make_test_header(input); standard_opcode_lengths.extend(header.standard_opcode_lengths.slice()); standard_opcode_lengths.push(3); header.opcode_base += 1; header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian); let mut rest = input; let opcode = LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); assert_eq!( opcode, LineInstruction::UnknownStandardN(constants::DwLns(OPCODE_BASE), args) ); assert_eq!(*rest, []); } #[test] fn test_parse_extended_opcodes() { fn test( raw: constants::DwLne, operands: Operands, expected: LineInstruction>, ) where Operands: AsRef<[u8]>, { let mut input = Vec::new(); input.push(0); let operands = operands.as_ref(); input.push(1 + operands.len() as u8); input.push(raw.0); input.extend_from_slice(operands); let expected_rest = [0, 1, 2, 3, 4]; input.extend_from_slice(&expected_rest); let input = EndianSlice::new(&input, LittleEndian); let header = make_test_header(input); let mut rest = input; let opcode = LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); assert_eq!(opcode, expected); assert_eq!(*rest, expected_rest); } test( constants::DW_LNE_end_sequence, [], LineInstruction::EndSequence, ); test( constants::DW_LNE_set_address, [1, 2, 3, 4, 5, 6, 7, 8], LineInstruction::SetAddress(578_437_695_752_307_201), ); test( constants::DW_LNE_set_discriminator, [42], LineInstruction::SetDiscriminator(42), ); let mut file = Vec::new(); // "foo.c" let path_name = [b'f', b'o', b'o', b'.', b'c', 0]; file.extend_from_slice(&path_name); // Directory index. file.push(0); // Last modification of file. file.push(1); // Size of file. file.push(2); test( constants::DW_LNE_define_file, file, LineInstruction::DefineFile(FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)), directory_index: 0, timestamp: 1, size: 2, md5: [0; 16], source: None, }), ); // Unknown extended opcode. let operands = [1, 2, 3, 4, 5, 6]; let opcode = constants::DwLne(99); test( opcode, operands, LineInstruction::UnknownExtended(opcode, EndianSlice::new(&operands, LittleEndian)), ); } #[test] fn test_file_entry_directory() { let path_name = [b'f', b'o', b'o', b'.', b'r', b's', 0]; let mut file = FileEntry { path_name: AttributeValue::String(EndianSlice::new(&path_name, LittleEndian)), directory_index: 1, timestamp: 0, size: 0, md5: [0; 16], source: None, }; let mut header = make_test_header(EndianSlice::new(&[], LittleEndian)); let dir = AttributeValue::String(EndianSlice::new(b"dir", LittleEndian)); header.include_directories.push(dir); assert_eq!(file.directory(&header), Some(dir)); // Now test the compilation's current directory. file.directory_index = 0; assert_eq!(file.directory(&header), None); } fn assert_exec_opcode<'input>( header: LineProgramHeader>, mut registers: LineRow, opcode: LineInstruction>, expected_registers: LineRow, expect_new_row: bool, ) { let mut program = IncompleteLineProgram { header }; let is_new_row = registers.execute(opcode, &mut program); assert_eq!(is_new_row, Ok(expect_new_row)); assert_eq!(registers, expected_registers); } #[test] fn test_exec_special_noop() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::Special(16); let expected_registers = initial_registers; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_special_negative_line_advance() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.line.0 = 10; let opcode = LineInstruction::Special(13); let mut expected_registers = initial_registers; expected_registers.line.0 -= 3; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_special_positive_line_advance() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::Special(19); let mut expected_registers = initial_registers; expected_registers.line.0 += 3; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_special_positive_address_advance() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::Special(52); let mut expected_registers = initial_registers; expected_registers.address += 3; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_special_positive_address_and_line_advance() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::Special(55); let mut expected_registers = initial_registers; expected_registers.address += 3; expected_registers.line.0 += 3; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_special_positive_address_and_negative_line_advance() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.line.0 = 10; let opcode = LineInstruction::Special(49); let mut expected_registers = initial_registers; expected_registers.address += 3; expected_registers.line.0 -= 3; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_special_line_underflow() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.line.0 = 2; // -3 line advance. let opcode = LineInstruction::Special(13); let mut expected_registers = initial_registers; // Clamp at 0. No idea if this is the best way to handle this situation // or not... expected_registers.line.0 = 0; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_copy() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.address = 1337; initial_registers.line.0 = 42; let opcode = LineInstruction::Copy; let expected_registers = initial_registers; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_advance_pc() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::AdvancePc(42); let mut expected_registers = initial_registers; expected_registers.address += 42; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_advance_pc_overflow_32() { let mut header = make_test_header(EndianSlice::new(&[], LittleEndian)); header.encoding.address_size = 4; let mut registers = LineRow::new(&header); registers.address = u32::MAX.into(); let opcode = LineInstruction::AdvancePc(42); let mut program = IncompleteLineProgram { header }; let result = registers.execute(opcode, &mut program); assert_eq!(result, Err(Error::AddressOverflow)); } #[test] fn test_exec_advance_pc_overflow_64() { let mut header = make_test_header(EndianSlice::new(&[], LittleEndian)); header.encoding.address_size = 8; let mut registers = LineRow::new(&header); registers.address = u64::MAX; let opcode = LineInstruction::AdvancePc(42); let mut program = IncompleteLineProgram { header }; let result = registers.execute(opcode, &mut program); assert_eq!(result, Err(Error::AddressOverflow)); } #[test] fn test_exec_advance_line() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::AdvanceLine(42); let mut expected_registers = initial_registers; expected_registers.line.0 += 42; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_advance_line_overflow() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let opcode = LineInstruction::AdvanceLine(42); let mut initial_registers = LineRow::new(&header); initial_registers.line.0 = u64::MAX; let mut expected_registers = initial_registers; expected_registers.line.0 = 41; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_set_file_in_bounds() { for file_idx in 1..3 { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::SetFile(file_idx); let mut expected_registers = initial_registers; expected_registers.file = file_idx; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } } #[test] fn test_exec_set_file_out_of_bounds() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::SetFile(100); // The spec doesn't say anything about rejecting input programs // that set the file register out of bounds of the actual number // of files that have been defined. Instead, we cross our // fingers and hope that one gets defined before // `LineRow::file` gets called and handle the error at // that time if need be. let mut expected_registers = initial_registers; expected_registers.file = 100; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_file_entry_file_index_out_of_bounds() { // These indices are 1-based, so 0 is invalid. 100 is way more than the // number of files defined in the header. let out_of_bounds_indices = [0, 100]; for file_idx in &out_of_bounds_indices[..] { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut row = LineRow::new(&header); row.file = *file_idx; assert_eq!(row.file(&header), None); } } #[test] fn test_file_entry_file_index_in_bounds() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut row = LineRow::new(&header); row.file = 2; assert_eq!(row.file(&header), Some(&header.file_names()[1])); } #[test] fn test_exec_set_column() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::SetColumn(42); let mut expected_registers = initial_registers; expected_registers.column = 42; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_negate_statement() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::NegateStatement; let mut expected_registers = initial_registers; expected_registers.is_stmt = !initial_registers.is_stmt; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_set_basic_block() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.basic_block = false; let opcode = LineInstruction::SetBasicBlock; let mut expected_registers = initial_registers; expected_registers.basic_block = true; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_const_add_pc() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::ConstAddPc; let mut expected_registers = initial_registers; expected_registers.address += 20; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_const_add_pc_overflow() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut registers = LineRow::new(&header); registers.address = u64::MAX; let opcode = LineInstruction::ConstAddPc; let mut program = IncompleteLineProgram { header }; let result = registers.execute(opcode, &mut program); assert_eq!(result, Err(Error::AddressOverflow)); } #[test] fn test_exec_fixed_add_pc() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.op_index.0 = 1; let opcode = LineInstruction::FixedAddPc(10); let mut expected_registers = initial_registers; expected_registers.address += 10; expected_registers.op_index.0 = 0; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_fixed_add_pc_overflow() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut registers = LineRow::new(&header); registers.address = u64::MAX; registers.op_index.0 = 1; let opcode = LineInstruction::FixedAddPc(10); let mut program = IncompleteLineProgram { header }; let result = registers.execute(opcode, &mut program); assert_eq!(result, Err(Error::AddressOverflow)); } #[test] fn test_exec_set_prologue_end() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.prologue_end = false; let opcode = LineInstruction::SetPrologueEnd; let mut expected_registers = initial_registers; expected_registers.prologue_end = true; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_set_isa() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::SetIsa(1993); let mut expected_registers = initial_registers; expected_registers.isa = 1993; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_unknown_standard_0() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::UnknownStandard0(constants::DwLns(111)); let expected_registers = initial_registers; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_unknown_standard_1() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::UnknownStandard1(constants::DwLns(111), 2); let expected_registers = initial_registers; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_unknown_standard_n() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::UnknownStandardN( constants::DwLns(111), EndianSlice::new(&[2, 2, 2], LittleEndian), ); let expected_registers = initial_registers; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_end_sequence() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::EndSequence; let mut expected_registers = initial_registers; expected_registers.end_sequence = true; assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); } #[test] fn test_exec_set_address() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::SetAddress(3030); let mut expected_registers = initial_registers; expected_registers.address = 3030; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_set_address_tombstone() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::SetAddress(!0); let mut expected_registers = initial_registers; expected_registers.tombstone = true; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_set_address_backwards() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let mut initial_registers = LineRow::new(&header); initial_registers.address = 1; let opcode = LineInstruction::SetAddress(0); let mut expected_registers = initial_registers; expected_registers.tombstone = true; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_define_file() { let mut program = make_test_program(EndianSlice::new(&[], LittleEndian)); let mut row = LineRow::new(program.header()); let file = FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"test.cpp", LittleEndian)), directory_index: 0, timestamp: 0, size: 0, md5: [0; 16], source: None, }; let opcode = LineInstruction::DefineFile(file); let is_new_row = row.execute(opcode, &mut program).unwrap(); assert!(!is_new_row); assert_eq!(Some(&file), program.header().file_names.last()); } #[test] fn test_exec_set_discriminator() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::SetDiscriminator(9); let mut expected_registers = initial_registers; expected_registers.discriminator = 9; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] fn test_exec_unknown_extended() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); let initial_registers = LineRow::new(&header); let opcode = LineInstruction::UnknownExtended( constants::DwLne(74), EndianSlice::new(&[], LittleEndian), ); let expected_registers = initial_registers; assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } /// Ensure that `LineRows` is covariant wrt R. /// This only needs to compile. #[allow(dead_code, unreachable_code, unused_variables)] #[allow(clippy::diverging_sub_expression)] fn test_line_rows_variance<'a, 'b>(_: &'a [u8], _: &'b [u8]) where 'a: 'b, { let a: &OneShotLineRows> = unimplemented!(); let _: &OneShotLineRows> = a; } #[test] fn test_parse_debug_line_v5_ok() { let expected_lengths = &[1, 2]; let expected_program = &[0, 1, 2, 3, 4]; let expected_rest = &[5, 6, 7, 8, 9]; let expected_include_directories = [ AttributeValue::String(EndianSlice::new(b"dir1", LittleEndian)), AttributeValue::String(EndianSlice::new(b"dir2", LittleEndian)), ]; let expected_file_names = [ FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"file1", LittleEndian)), directory_index: 0, timestamp: 0, size: 0, md5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], source: Some(AttributeValue::String(EndianSlice::new( b"foobar", LittleEndian, ))), }, FileEntry { path_name: AttributeValue::String(EndianSlice::new(b"file2", LittleEndian)), directory_index: 1, timestamp: 0, size: 0, md5: [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, ], source: Some(AttributeValue::String(EndianSlice::new( b"quux", LittleEndian, ))), }, ]; for format in [Format::Dwarf32, Format::Dwarf64] { let length = Label::new(); let header_length = Label::new(); let start = Label::new(); let header_start = Label::new(); let end = Label::new(); let header_end = Label::new(); let section = Section::with_endian(Endian::Little) .initial_length(format, &length, &start) .D16(5) // Address size. .D8(4) // Segment selector size. .D8(0) .word_label(format.word_size(), &header_length) .mark(&header_start) // Minimum instruction length. .D8(1) // Maximum operations per byte. .D8(1) // Default is_stmt. .D8(1) // Line base. .D8(0) // Line range. .D8(1) // Opcode base. .D8(expected_lengths.len() as u8 + 1) // Standard opcode lengths for opcodes 1 .. opcode base - 1. .append_bytes(expected_lengths) // Directory entry format count. .D8(1) .uleb(constants::DW_LNCT_path.0 as u64) .uleb(constants::DW_FORM_string.0 as u64) // Directory count. .D8(2) .append_bytes(b"dir1\0") .append_bytes(b"dir2\0") // File entry format count. .D8(4) .uleb(constants::DW_LNCT_path.0 as u64) .uleb(constants::DW_FORM_string.0 as u64) .uleb(constants::DW_LNCT_directory_index.0 as u64) .uleb(constants::DW_FORM_data1.0 as u64) .uleb(constants::DW_LNCT_MD5.0 as u64) .uleb(constants::DW_FORM_data16.0 as u64) .uleb(constants::DW_LNCT_LLVM_source.0 as u64) .uleb(constants::DW_FORM_string.0 as u64) // File count. .D8(2) .append_bytes(b"file1\0") .D8(0) .append_bytes(&expected_file_names[0].md5) .append_bytes(b"foobar\0") .append_bytes(b"file2\0") .D8(1) .append_bytes(&expected_file_names[1].md5) .append_bytes(b"quux\0") .mark(&header_end) // Dummy line program data. .append_bytes(expected_program) .mark(&end) // Dummy trailing data. .append_bytes(expected_rest); length.set_const((&end - &start) as u64); header_length.set_const((&header_end - &header_start) as u64); let section = section.get_contents().unwrap(); let input = &mut EndianSlice::new(§ion, LittleEndian); let header = LineProgramHeader::parse(input, DebugLineOffset(0), 0, None, None) .expect("should parse header ok"); assert_eq!(header.raw_program_buf().slice(), expected_program); assert_eq!(input.slice(), expected_rest); assert_eq!(header.offset, DebugLineOffset(0)); assert_eq!(header.version(), 5); assert_eq!(header.address_size(), 4); assert_eq!(header.minimum_instruction_length(), 1); assert_eq!(header.maximum_operations_per_instruction(), 1); assert!(header.default_is_stmt()); assert_eq!(header.line_base(), 0); assert_eq!(header.line_range(), 1); assert_eq!(header.opcode_base(), expected_lengths.len() as u8 + 1); assert_eq!(header.standard_opcode_lengths().slice(), expected_lengths); assert_eq!( header.directory_entry_format(), &[FileEntryFormat { content_type: constants::DW_LNCT_path, form: constants::DW_FORM_string, }] ); assert_eq!(header.include_directories(), expected_include_directories); assert_eq!(header.directory(0), Some(expected_include_directories[0])); assert_eq!( header.file_name_entry_format(), &[ FileEntryFormat { content_type: constants::DW_LNCT_path, form: constants::DW_FORM_string, }, FileEntryFormat { content_type: constants::DW_LNCT_directory_index, form: constants::DW_FORM_data1, }, FileEntryFormat { content_type: constants::DW_LNCT_MD5, form: constants::DW_FORM_data16, }, FileEntryFormat { content_type: constants::DW_LNCT_LLVM_source, form: constants::DW_FORM_string, } ] ); assert_eq!(header.file_names(), expected_file_names); assert_eq!(header.file(0), Some(&expected_file_names[0])); } } #[test] fn test_sequences() { #[rustfmt::skip] let buf = [ // 32-bit length 94, 0x00, 0x00, 0x00, // Version. 0x04, 0x00, // Header length = 40. 0x28, 0x00, 0x00, 0x00, // Minimum instruction length. 0x01, // Maximum operations per byte. 0x01, // Default is_stmt. 0x01, // Line base. 0x00, // Line range. 0x01, // Opcode base. 0x03, // Standard opcode lengths for opcodes 1 .. opcode base - 1. 0x01, 0x02, // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, // File names // foo.rs 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, // bar.h 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, 0x01, 0x00, 0x00, // End file names. 0x00, 0, 5, constants::DW_LNE_set_address.0, 1, 0, 0, 0, constants::DW_LNS_copy.0, constants::DW_LNS_advance_pc.0, 1, constants::DW_LNS_copy.0, constants::DW_LNS_advance_pc.0, 2, 0, 1, constants::DW_LNE_end_sequence.0, // Tombstone 0, 5, constants::DW_LNE_set_address.0, 0xff, 0xff, 0xff, 0xff, constants::DW_LNS_copy.0, constants::DW_LNS_advance_pc.0, 1, constants::DW_LNS_copy.0, constants::DW_LNS_advance_pc.0, 2, 0, 1, constants::DW_LNE_end_sequence.0, 0, 5, constants::DW_LNE_set_address.0, 11, 0, 0, 0, constants::DW_LNS_copy.0, constants::DW_LNS_advance_pc.0, 1, constants::DW_LNS_copy.0, constants::DW_LNS_advance_pc.0, 2, 0, 1, constants::DW_LNE_end_sequence.0, ]; assert_eq!(buf[0] as usize, buf.len() - 4); let rest = &mut EndianSlice::new(&buf, LittleEndian); let header = LineProgramHeader::parse(rest, DebugLineOffset(0), 4, None, None) .expect("should parse header ok"); let program = IncompleteLineProgram { header }; let sequences = program.sequences().unwrap().1; assert_eq!(sequences.len(), 2); assert_eq!(sequences[0].start, 1); assert_eq!(sequences[0].end, 4); assert_eq!(sequences[1].start, 11); assert_eq!(sequences[1].end, 14); } } gimli-0.31.1/src/read/lists.rs000064400000000000000000000035451046102023000142240ustar 00000000000000use crate::common::{Encoding, Format}; use crate::read::{Error, Reader, Result}; #[derive(Debug, Clone, Copy)] pub(crate) struct ListsHeader { encoding: Encoding, #[allow(dead_code)] offset_entry_count: u32, } impl Default for ListsHeader { fn default() -> Self { ListsHeader { encoding: Encoding { format: Format::Dwarf32, version: 5, address_size: 0, }, offset_entry_count: 0, } } } impl ListsHeader { /// Return the serialized size of the table header. #[allow(dead_code)] #[inline] fn size(self) -> u8 { // initial_length + version + address_size + segment_selector_size + offset_entry_count ListsHeader::size_for_encoding(self.encoding) } /// Return the serialized size of the table header. #[inline] pub(crate) fn size_for_encoding(encoding: Encoding) -> u8 { // initial_length + version + address_size + segment_selector_size + offset_entry_count encoding.format.initial_length_size() + 2 + 1 + 1 + 4 } } // TODO: add an iterator over headers in the appropriate sections section #[allow(dead_code)] fn parse_header(input: &mut R) -> Result { let (length, format) = input.read_initial_length()?; input.truncate(length)?; let version = input.read_u16()?; if version != 5 { return Err(Error::UnknownVersion(u64::from(version))); } let address_size = input.read_address_size()?; let segment_selector_size = input.read_u8()?; if segment_selector_size != 0 { return Err(Error::UnsupportedSegmentSize); } let offset_entry_count = input.read_u32()?; let encoding = Encoding { format, version, address_size, }; Ok(ListsHeader { encoding, offset_entry_count, }) } gimli-0.31.1/src/read/loclists.rs000064400000000000000000001177611046102023000147300ustar 00000000000000use crate::common::{ DebugAddrBase, DebugAddrIndex, DebugLocListsBase, DebugLocListsIndex, DwarfFileType, Encoding, LocationListsOffset, SectionId, }; use crate::constants; use crate::endianity::Endianity; use crate::read::{ lists::ListsHeader, DebugAddr, EndianSlice, Error, Expression, Range, RawRange, Reader, ReaderAddress, ReaderOffset, ReaderOffsetId, Result, Section, }; /// The raw contents of the `.debug_loc` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugLoc { pub(crate) section: R, } impl<'input, Endian> DebugLoc> where Endian: Endianity, { /// Construct a new `DebugLoc` instance from the data in the `.debug_loc` /// section. /// /// It is the caller's responsibility to read the `.debug_loc` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugLoc, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_loc_section_somehow = || &buf; /// let debug_loc = DebugLoc::new(read_debug_loc_section_somehow(), LittleEndian); /// ``` pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl DebugLoc { /// Create a `DebugLoc` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLoc where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugLoc { fn id() -> SectionId { SectionId::DebugLoc } fn reader(&self) -> &R { &self.section } } impl From for DebugLoc { fn from(section: R) -> Self { DebugLoc { section } } } /// The `DebugLocLists` struct represents the DWARF data /// found in the `.debug_loclists` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugLocLists { section: R, } impl<'input, Endian> DebugLocLists> where Endian: Endianity, { /// Construct a new `DebugLocLists` instance from the data in the `.debug_loclists` /// section. /// /// It is the caller's responsibility to read the `.debug_loclists` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugLocLists, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_loclists_section_somehow = || &buf; /// let debug_loclists = DebugLocLists::new(read_debug_loclists_section_somehow(), LittleEndian); /// ``` pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl DebugLocLists { /// Create a `DebugLocLists` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLocLists where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugLocLists { fn id() -> SectionId { SectionId::DebugLocLists } fn reader(&self) -> &R { &self.section } } impl From for DebugLocLists { fn from(section: R) -> Self { DebugLocLists { section } } } pub(crate) type LocListsHeader = ListsHeader; impl DebugLocListsBase where Offset: ReaderOffset, { /// Returns a `DebugLocListsBase` with the default value of DW_AT_loclists_base /// for the given `Encoding` and `DwarfFileType`. pub fn default_for_encoding_and_file( encoding: Encoding, file_type: DwarfFileType, ) -> DebugLocListsBase { if encoding.version >= 5 && file_type == DwarfFileType::Dwo { // In .dwo files, the compiler omits the DW_AT_loclists_base attribute (because there is // only a single unit in the file) but we must skip past the header, which the attribute // would normally do for us. DebugLocListsBase(Offset::from_u8(LocListsHeader::size_for_encoding(encoding))) } else { DebugLocListsBase(Offset::from_u8(0)) } } } /// The DWARF data found in `.debug_loc` and `.debug_loclists` sections. #[derive(Debug, Default, Clone, Copy)] pub struct LocationLists { debug_loc: DebugLoc, debug_loclists: DebugLocLists, } impl LocationLists { /// Construct a new `LocationLists` instance from the data in the `.debug_loc` and /// `.debug_loclists` sections. pub fn new(debug_loc: DebugLoc, debug_loclists: DebugLocLists) -> LocationLists { LocationLists { debug_loc, debug_loclists, } } } impl LocationLists { /// Create a `LocationLists` that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `Dwarf::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> LocationLists where F: FnMut(&'a T) -> R, { LocationLists { debug_loc: borrow(&self.debug_loc.section).into(), debug_loclists: borrow(&self.debug_loclists.section).into(), } } } impl LocationLists { /// Iterate over the `LocationListEntry`s starting at the given offset. /// /// The `unit_encoding` must match the compilation unit that the /// offset was contained in. /// /// The `base_address` should be obtained from the `DW_AT_low_pc` attribute in the /// `DW_TAG_compile_unit` entry for the compilation unit that contains this location /// list. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn locations( &self, offset: LocationListsOffset, unit_encoding: Encoding, base_address: u64, debug_addr: &DebugAddr, debug_addr_base: DebugAddrBase, ) -> Result> { Ok(LocListIter::new( self.raw_locations(offset, unit_encoding)?, base_address, debug_addr.clone(), debug_addr_base, )) } /// Similar to `locations`, but with special handling for .dwo files. /// This should only been used when this `LocationLists` was loaded from a /// .dwo file. pub fn locations_dwo( &self, offset: LocationListsOffset, unit_encoding: Encoding, base_address: u64, debug_addr: &DebugAddr, debug_addr_base: DebugAddrBase, ) -> Result> { Ok(LocListIter::new( self.raw_locations_dwo(offset, unit_encoding)?, base_address, debug_addr.clone(), debug_addr_base, )) } /// Iterate over the raw `LocationListEntry`s starting at the given offset. /// /// The `unit_encoding` must match the compilation unit that the /// offset was contained in. /// /// This iterator does not perform any processing of the location entries, /// such as handling base addresses. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn raw_locations( &self, offset: LocationListsOffset, unit_encoding: Encoding, ) -> Result> { let (mut input, format) = if unit_encoding.version <= 4 { (self.debug_loc.section.clone(), LocListsFormat::Bare) } else { (self.debug_loclists.section.clone(), LocListsFormat::Lle) }; input.skip(offset.0)?; Ok(RawLocListIter::new(input, unit_encoding, format)) } /// Similar to `raw_locations`, but with special handling for .dwo files. /// This should only been used when this `LocationLists` was loaded from a /// .dwo file. pub fn raw_locations_dwo( &self, offset: LocationListsOffset, unit_encoding: Encoding, ) -> Result> { let mut input = if unit_encoding.version <= 4 { // In the GNU split dwarf extension the locations are present in the // .debug_loc section but are encoded with the DW_LLE values used // for the DWARF 5 .debug_loclists section. self.debug_loc.section.clone() } else { self.debug_loclists.section.clone() }; input.skip(offset.0)?; Ok(RawLocListIter::new( input, unit_encoding, LocListsFormat::Lle, )) } /// Returns the `.debug_loclists` offset at the given `base` and `index`. /// /// The `base` must be the `DW_AT_loclists_base` value from the compilation unit DIE. /// This is an offset that points to the first entry following the header. /// /// The `index` is the value of a `DW_FORM_loclistx` attribute. pub fn get_offset( &self, unit_encoding: Encoding, base: DebugLocListsBase, index: DebugLocListsIndex, ) -> Result> { let format = unit_encoding.format; let input = &mut self.debug_loclists.section.clone(); input.skip(base.0)?; input.skip(R::Offset::from_u64( index.0.into_u64() * u64::from(format.word_size()), )?)?; input .read_offset(format) .map(|x| LocationListsOffset(base.0 + x)) } /// Call `Reader::lookup_offset_id` for each section, and return the first match. pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> { self.debug_loc .lookup_offset_id(id) .or_else(|| self.debug_loclists.lookup_offset_id(id)) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum LocListsFormat { /// The bare location list format used before DWARF 5. Bare, /// The DW_LLE encoded range list format used in DWARF 5 and the non-standard GNU /// split dwarf extension. Lle, } /// A raw iterator over a location list. /// /// This iterator does not perform any processing of the location entries, /// such as handling base addresses. #[derive(Debug)] pub struct RawLocListIter { input: R, encoding: Encoding, format: LocListsFormat, } /// A raw entry in .debug_loclists. #[derive(Clone, Debug)] pub enum RawLocListEntry { /// A location from DWARF version <= 4. AddressOrOffsetPair { /// Start of range. May be an address or an offset. begin: u64, /// End of range. May be an address or an offset. end: u64, /// expression data: Expression, }, /// DW_LLE_base_address BaseAddress { /// base address addr: u64, }, /// DW_LLE_base_addressx BaseAddressx { /// base address addr: DebugAddrIndex, }, /// DW_LLE_startx_endx StartxEndx { /// start of range begin: DebugAddrIndex, /// end of range end: DebugAddrIndex, /// expression data: Expression, }, /// DW_LLE_startx_length StartxLength { /// start of range begin: DebugAddrIndex, /// length of range length: u64, /// expression data: Expression, }, /// DW_LLE_offset_pair OffsetPair { /// start of range begin: u64, /// end of range end: u64, /// expression data: Expression, }, /// DW_LLE_default_location DefaultLocation { /// expression data: Expression, }, /// DW_LLE_start_end StartEnd { /// start of range begin: u64, /// end of range end: u64, /// expression data: Expression, }, /// DW_LLE_start_length StartLength { /// start of range begin: u64, /// length of range length: u64, /// expression data: Expression, }, } fn parse_data(input: &mut R, encoding: Encoding) -> Result> { if encoding.version >= 5 { let len = R::Offset::from_u64(input.read_uleb128()?)?; Ok(Expression(input.split(len)?)) } else { // In the GNU split-dwarf extension this is a fixed 2 byte value. let len = R::Offset::from_u16(input.read_u16()?); Ok(Expression(input.split(len)?)) } } impl RawLocListEntry { /// Parse a location list entry from `.debug_loclists` fn parse(input: &mut R, encoding: Encoding, format: LocListsFormat) -> Result> { Ok(match format { LocListsFormat::Bare => { let range = RawRange::parse(input, encoding.address_size)?; if range.is_end() { None } else if range.is_base_address(encoding.address_size) { Some(RawLocListEntry::BaseAddress { addr: range.end }) } else { let len = R::Offset::from_u16(input.read_u16()?); let data = Expression(input.split(len)?); Some(RawLocListEntry::AddressOrOffsetPair { begin: range.begin, end: range.end, data, }) } } LocListsFormat::Lle => match constants::DwLle(input.read_u8()?) { constants::DW_LLE_end_of_list => None, constants::DW_LLE_base_addressx => Some(RawLocListEntry::BaseAddressx { addr: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), }), constants::DW_LLE_startx_endx => Some(RawLocListEntry::StartxEndx { begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), end: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), data: parse_data(input, encoding)?, }), constants::DW_LLE_startx_length => Some(RawLocListEntry::StartxLength { begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), length: if encoding.version >= 5 { input.read_uleb128()? } else { // In the GNU split-dwarf extension this is a fixed 4 byte value. input.read_u32()? as u64 }, data: parse_data(input, encoding)?, }), constants::DW_LLE_offset_pair => Some(RawLocListEntry::OffsetPair { begin: input.read_uleb128()?, end: input.read_uleb128()?, data: parse_data(input, encoding)?, }), constants::DW_LLE_default_location => Some(RawLocListEntry::DefaultLocation { data: parse_data(input, encoding)?, }), constants::DW_LLE_base_address => Some(RawLocListEntry::BaseAddress { addr: input.read_address(encoding.address_size)?, }), constants::DW_LLE_start_end => Some(RawLocListEntry::StartEnd { begin: input.read_address(encoding.address_size)?, end: input.read_address(encoding.address_size)?, data: parse_data(input, encoding)?, }), constants::DW_LLE_start_length => Some(RawLocListEntry::StartLength { begin: input.read_address(encoding.address_size)?, length: input.read_uleb128()?, data: parse_data(input, encoding)?, }), entry => { return Err(Error::UnknownLocListsEntry(entry)); } }, }) } } impl RawLocListIter { /// Construct a `RawLocListIter`. fn new(input: R, encoding: Encoding, format: LocListsFormat) -> RawLocListIter { RawLocListIter { input, encoding, format, } } /// Advance the iterator to the next location. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { return Ok(None); } match RawLocListEntry::parse(&mut self.input, self.encoding, self.format) { Ok(entry) => { if entry.is_none() { self.input.empty(); } Ok(entry) } Err(e) => { self.input.empty(); Err(e) } } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for RawLocListIter { type Item = RawLocListEntry; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { RawLocListIter::next(self) } } /// An iterator over a location list. /// /// This iterator internally handles processing of base address selection entries /// and list end entries. Thus, it only returns location entries that are valid /// and already adjusted for the base address. #[derive(Debug)] pub struct LocListIter { raw: RawLocListIter, base_address: u64, debug_addr: DebugAddr, debug_addr_base: DebugAddrBase, } impl LocListIter { /// Construct a `LocListIter`. fn new( raw: RawLocListIter, base_address: u64, debug_addr: DebugAddr, debug_addr_base: DebugAddrBase, ) -> LocListIter { LocListIter { raw, base_address, debug_addr, debug_addr_base, } } #[inline] fn get_address(&self, index: DebugAddrIndex) -> Result { self.debug_addr .get_address(self.raw.encoding.address_size, self.debug_addr_base, index) } /// Advance the iterator to the next location. pub fn next(&mut self) -> Result>> { loop { let raw_loc = match self.raw.next()? { Some(loc) => loc, None => return Ok(None), }; let loc = self.convert_raw(raw_loc)?; if loc.is_some() { return Ok(loc); } } } /// Return the next raw location. /// /// The raw location should be passed to `convert_raw`. #[doc(hidden)] pub fn next_raw(&mut self) -> Result>> { self.raw.next() } /// Convert a raw location into a location, and update the state of the iterator. /// /// The raw location should have been obtained from `next_raw`. #[doc(hidden)] pub fn convert_raw( &mut self, raw_loc: RawLocListEntry, ) -> Result>> { let address_size = self.raw.encoding.address_size; let mask = u64::ones_sized(address_size); let tombstone = if self.raw.encoding.version <= 4 { mask - 1 } else { mask }; let (range, data) = match raw_loc { RawLocListEntry::BaseAddress { addr } => { self.base_address = addr; return Ok(None); } RawLocListEntry::BaseAddressx { addr } => { self.base_address = self.get_address(addr)?; return Ok(None); } RawLocListEntry::StartxEndx { begin, end, data } => { let begin = self.get_address(begin)?; let end = self.get_address(end)?; (Range { begin, end }, data) } RawLocListEntry::StartxLength { begin, length, data, } => { let begin = self.get_address(begin)?; let end = begin.wrapping_add_sized(length, address_size); (Range { begin, end }, data) } RawLocListEntry::DefaultLocation { data } => ( Range { begin: 0, end: u64::MAX, }, data, ), RawLocListEntry::AddressOrOffsetPair { begin, end, data } | RawLocListEntry::OffsetPair { begin, end, data } => { // Skip tombstone entries (see below). if self.base_address == tombstone { return Ok(None); } let mut range = Range { begin, end }; range.add_base_address(self.base_address, self.raw.encoding.address_size); (range, data) } RawLocListEntry::StartEnd { begin, end, data } => (Range { begin, end }, data), RawLocListEntry::StartLength { begin, length, data, } => { let end = begin.wrapping_add_sized(length, address_size); (Range { begin, end }, data) } }; // Skip tombstone entries. // // DWARF specifies a tombstone value of -1 or -2, but many linkers use 0 or 1. // However, 0/1 may be a valid address, so we can't always reliably skip them. // One case where we can skip them is for address pairs, where both values are // replaced by tombstones and thus `begin` equals `end`. Since these entries // are empty, it's safe to skip them even if they aren't tombstones. // // In addition to skipping tombstone entries, we also skip invalid entries // where `begin` is greater than `end`. This can occur due to compiler bugs. if range.begin == tombstone || range.begin >= range.end { return Ok(None); } Ok(Some(LocationListEntry { range, data })) } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for LocListIter { type Item = LocationListEntry; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { LocListIter::next(self) } } /// A location list entry from the `.debug_loc` or `.debug_loclists` sections. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LocationListEntry { /// The address range that this location is valid for. pub range: Range, /// The data containing a single location description. pub data: Expression, } #[cfg(test)] mod tests { use super::*; use crate::common::Format; use crate::constants::*; use crate::endianity::LittleEndian; use crate::read::{EndianSlice, Range}; use crate::test_util::GimliSectionMethods; use alloc::vec::Vec; use test_assembler::{Endian, Label, LabelMaker, Section}; #[test] fn test_loclists() { let format = Format::Dwarf32; for size in [4, 8] { let tombstone = u64::ones_sized(size); let tombstone_0 = 0; let encoding = Encoding { format, version: 5, address_size: size, }; let section = Section::with_endian(Endian::Little) .word(size, 0x0300_0000) .word(size, 0x0301_0300) .word(size, 0x0301_0400) .word(size, 0x0301_0500) .word(size, tombstone) .word(size, 0x0301_0600) .word(size, tombstone_0); let buf = section.get_contents().unwrap(); let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); let debug_addr_base = DebugAddrBase(0); let length = Label::new(); let start = Label::new(); let first = Label::new(); let end = Label::new(); let mut section = Section::with_endian(Endian::Little) .initial_length(format, &length, &start) .L16(encoding.version) .L8(encoding.address_size) .L8(0) .L32(0) .mark(&first); let mut expected_locations = Vec::new(); let mut expect_location = |begin, end, data| { expected_locations.push(LocationListEntry { range: Range { begin, end }, data: Expression(EndianSlice::new(data, LittleEndian)), }); }; // An offset pair using the unit base address. section = section.L8(DW_LLE_offset_pair.0).uleb(0x10200).uleb(0x10300); section = section.uleb(4).L32(2); expect_location(0x0101_0200, 0x0101_0300, &[2, 0, 0, 0]); section = section.L8(DW_LLE_base_address.0).word(size, 0x0200_0000); section = section.L8(DW_LLE_offset_pair.0).uleb(0x10400).uleb(0x10500); section = section.uleb(4).L32(3); expect_location(0x0201_0400, 0x0201_0500, &[3, 0, 0, 0]); section = section .L8(DW_LLE_start_end.0) .word(size, 0x201_0a00) .word(size, 0x201_0b00); section = section.uleb(4).L32(6); expect_location(0x0201_0a00, 0x0201_0b00, &[6, 0, 0, 0]); section = section .L8(DW_LLE_start_length.0) .word(size, 0x201_0c00) .uleb(0x100); section = section.uleb(4).L32(7); expect_location(0x0201_0c00, 0x0201_0d00, &[7, 0, 0, 0]); // An offset pair that starts at 0. section = section.L8(DW_LLE_base_address.0).word(size, 0); section = section.L8(DW_LLE_offset_pair.0).uleb(0).uleb(1); section = section.uleb(4).L32(8); expect_location(0, 1, &[8, 0, 0, 0]); // An offset pair that ends at -1. section = section.L8(DW_LLE_base_address.0).word(size, 0); section = section.L8(DW_LLE_offset_pair.0).uleb(0).uleb(tombstone); section = section.uleb(4).L32(9); expect_location(0, tombstone, &[9, 0, 0, 0]); section = section.L8(DW_LLE_default_location.0).uleb(4).L32(10); expect_location(0, u64::MAX, &[10, 0, 0, 0]); section = section.L8(DW_LLE_base_addressx.0).uleb(0); section = section.L8(DW_LLE_offset_pair.0).uleb(0x10100).uleb(0x10200); section = section.uleb(4).L32(11); expect_location(0x0301_0100, 0x0301_0200, &[11, 0, 0, 0]); section = section.L8(DW_LLE_startx_endx.0).uleb(1).uleb(2); section = section.uleb(4).L32(12); expect_location(0x0301_0300, 0x0301_0400, &[12, 0, 0, 0]); section = section.L8(DW_LLE_startx_length.0).uleb(3).uleb(0x100); section = section.uleb(4).L32(13); expect_location(0x0301_0500, 0x0301_0600, &[13, 0, 0, 0]); // Tombstone entries, all of which should be ignored. section = section.L8(DW_LLE_base_addressx.0).uleb(4); section = section.L8(DW_LLE_offset_pair.0).uleb(0x11100).uleb(0x11200); section = section.uleb(4).L32(20); section = section.L8(DW_LLE_base_address.0).word(size, tombstone); section = section.L8(DW_LLE_offset_pair.0).uleb(0x11300).uleb(0x11400); section = section.uleb(4).L32(21); section = section.L8(DW_LLE_startx_endx.0).uleb(4).uleb(5); section = section.uleb(4).L32(22); section = section.L8(DW_LLE_startx_length.0).uleb(4).uleb(0x100); section = section.uleb(4).L32(23); section = section .L8(DW_LLE_start_end.0) .word(size, tombstone) .word(size, 0x201_1500); section = section.uleb(4).L32(24); section = section .L8(DW_LLE_start_length.0) .word(size, tombstone) .uleb(0x100); section = section.uleb(4).L32(25); // Ignore some instances of 0 for tombstone. section = section.L8(DW_LLE_startx_endx.0).uleb(6).uleb(6); section = section.uleb(4).L32(30); section = section .L8(DW_LLE_start_end.0) .word(size, tombstone_0) .word(size, tombstone_0); section = section.uleb(4).L32(31); // Ignore empty ranges. section = section.L8(DW_LLE_base_address.0).word(size, 0); section = section.L8(DW_LLE_offset_pair.0).uleb(0).uleb(0); section = section.uleb(4).L32(41); section = section.L8(DW_LLE_base_address.0).word(size, 0x10000); section = section.L8(DW_LLE_offset_pair.0).uleb(0x1234).uleb(0x1234); section = section.uleb(4).L32(42); // A valid range after the tombstones. section = section .L8(DW_LLE_start_end.0) .word(size, 0x201_1600) .word(size, 0x201_1700); section = section.uleb(4).L32(100); expect_location(0x0201_1600, 0x0201_1700, &[100, 0, 0, 0]); section = section.L8(DW_LLE_end_of_list.0); section = section.mark(&end); // Some extra data. section = section.word(size, 0x1234_5678); length.set_const((&end - &start) as u64); let offset = LocationListsOffset((&first - §ion.start()) as usize); let buf = section.get_contents().unwrap(); let debug_loc = DebugLoc::new(&[], LittleEndian); let debug_loclists = DebugLocLists::new(&buf, LittleEndian); let loclists = LocationLists::new(debug_loc, debug_loclists); let mut locations = loclists .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) .unwrap(); for expected_location in expected_locations { let location = locations.next(); assert_eq!( location, Ok(Some(expected_location)), "read {:x?}, expect {:x?}", location, expected_location ); } assert_eq!(locations.next(), Ok(None)); } } #[test] fn test_location_list() { for size in [4, 8] { let base = u64::ones_sized(size); let tombstone = u64::ones_sized(size) - 1; let start = Label::new(); let first = Label::new(); let mut section = Section::with_endian(Endian::Little) // A location before the offset. .mark(&start) .word(size, 0x10000) .word(size, 0x10100) .L16(4) .L32(1) .mark(&first); let mut expected_locations = Vec::new(); let mut expect_location = |begin, end, data| { expected_locations.push(LocationListEntry { range: Range { begin, end }, data: Expression(EndianSlice::new(data, LittleEndian)), }); }; // A normal location. section = section.word(size, 0x10200).word(size, 0x10300); section = section.L16(4).L32(2); expect_location(0x0101_0200, 0x0101_0300, &[2, 0, 0, 0]); // A base address selection followed by a normal location. section = section.word(size, base).word(size, 0x0200_0000); section = section.word(size, 0x10400).word(size, 0x10500); section = section.L16(4).L32(3); expect_location(0x0201_0400, 0x0201_0500, &[3, 0, 0, 0]); // An empty location range followed by a normal location. section = section.word(size, 0x10600).word(size, 0x10600); section = section.L16(4).L32(4); section = section.word(size, 0x10800).word(size, 0x10900); section = section.L16(4).L32(5); expect_location(0x0201_0800, 0x0201_0900, &[5, 0, 0, 0]); // A location range that starts at 0. section = section.word(size, base).word(size, 0); section = section.word(size, 0).word(size, 1); section = section.L16(4).L32(6); expect_location(0, 1, &[6, 0, 0, 0]); // A location range that ends at -1. section = section.word(size, base).word(size, 0); section = section.word(size, 0).word(size, base); section = section.L16(4).L32(7); expect_location(0, base, &[7, 0, 0, 0]); // A normal location with tombstone. section = section.word(size, tombstone).word(size, tombstone); section = section.L16(4).L32(8); // A base address selection with tombstone followed by a normal location. section = section.word(size, base).word(size, tombstone); section = section.word(size, 0x10a00).word(size, 0x10b00); section = section.L16(4).L32(9); // A location list end. section = section.word(size, 0).word(size, 0); // Some extra data. section = section.word(size, 0x1234_5678); let buf = section.get_contents().unwrap(); let debug_loc = DebugLoc::new(&buf, LittleEndian); let debug_loclists = DebugLocLists::new(&[], LittleEndian); let loclists = LocationLists::new(debug_loc, debug_loclists); let offset = LocationListsOffset((&first - &start) as usize); let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); let debug_addr_base = DebugAddrBase(0); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: size, }; let mut locations = loclists .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) .unwrap(); for expected_location in expected_locations { let location = locations.next(); assert_eq!( location, Ok(Some(expected_location)), "read {:x?}, expect {:x?}", location, expected_location ); } assert_eq!(locations.next(), Ok(None)); // An offset at the end of buf. let mut locations = loclists .locations( LocationListsOffset(buf.len()), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!(locations.next(), Ok(None)); } } #[test] fn test_locations_invalid() { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) // An invalid location range. .L32(0x20000).L32(0x10000).L16(4).L32(1) // An invalid range after wrapping. .L32(0x20000).L32(0xff01_0000).L16(4).L32(2); let buf = section.get_contents().unwrap(); let debug_loc = DebugLoc::new(&buf, LittleEndian); let debug_loclists = DebugLocLists::new(&[], LittleEndian); let loclists = LocationLists::new(debug_loc, debug_loclists); let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); let debug_addr_base = DebugAddrBase(0); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; // An invalid location range. let mut locations = loclists .locations( LocationListsOffset(0x0), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!(locations.next(), Ok(None)); // An invalid location range after wrapping. let mut locations = loclists .locations( LocationListsOffset(14), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!(locations.next(), Ok(None)); // An invalid offset. match loclists.locations( LocationListsOffset(buf.len() + 1), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] fn test_get_offset() { for format in [Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version: 5, address_size: 4, }; let zero = Label::new(); let length = Label::new(); let start = Label::new(); let first = Label::new(); let end = Label::new(); let mut section = Section::with_endian(Endian::Little) .mark(&zero) .initial_length(format, &length, &start) .D16(encoding.version) .D8(encoding.address_size) .D8(0) .D32(20) .mark(&first); for i in 0..20 { section = section.word(format.word_size(), 1000 + i); } section = section.mark(&end); length.set_const((&end - &start) as u64); let section = section.get_contents().unwrap(); let debug_loc = DebugLoc::from(EndianSlice::new(&[], LittleEndian)); let debug_loclists = DebugLocLists::from(EndianSlice::new(§ion, LittleEndian)); let locations = LocationLists::new(debug_loc, debug_loclists); let base = DebugLocListsBase((&first - &zero) as usize); assert_eq!( locations.get_offset(encoding, base, DebugLocListsIndex(0)), Ok(LocationListsOffset(base.0 + 1000)) ); assert_eq!( locations.get_offset(encoding, base, DebugLocListsIndex(19)), Ok(LocationListsOffset(base.0 + 1019)) ); } } #[test] fn test_loclists_gnu_v4_split_dwarf() { #[rustfmt::skip] let buf = [ 0x03, // DW_LLE_startx_length 0x00, // ULEB encoded b7 0x08, 0x00, 0x00, 0x00, // Fixed 4 byte length of 8 0x03, 0x00, // Fixed two byte length of the location 0x11, 0x00, // DW_OP_constu 0 0x9f, // DW_OP_stack_value // Padding data //0x99, 0x99, 0x99, 0x99 ]; let data_buf = [0x11, 0x00, 0x9f]; let expected_data = EndianSlice::new(&data_buf, LittleEndian); let debug_loc = DebugLoc::new(&buf, LittleEndian); let debug_loclists = DebugLocLists::new(&[], LittleEndian); let loclists = LocationLists::new(debug_loc, debug_loclists); let debug_addr = &DebugAddr::from(EndianSlice::new(&[0x01, 0x02, 0x03, 0x04], LittleEndian)); let debug_addr_base = DebugAddrBase(0); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; // An invalid location range. let mut locations = loclists .locations_dwo( LocationListsOffset(0x0), encoding, 0, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!( locations.next(), Ok(Some(LocationListEntry { range: Range { begin: 0x0403_0201, end: 0x0403_0209 }, data: Expression(expected_data), })) ); } } gimli-0.31.1/src/read/lookup.rs000064400000000000000000000140121046102023000143660ustar 00000000000000use core::marker::PhantomData; use crate::common::{DebugInfoOffset, Format}; use crate::read::{parse_debug_info_offset, Error, Reader, ReaderOffset, Result, UnitOffset}; // The various "Accelerated Access" sections (DWARF standard v4 Section 6.1) all have // similar structures. They consist of a header with metadata and an offset into the // .debug_info section for the entire compilation unit, and a series // of following entries that list addresses (for .debug_aranges) or names // (for .debug_pubnames and .debug_pubtypes) that are covered. // // Because these three tables all have similar structures, we abstract out some of // the parsing mechanics. pub trait LookupParser { /// The type of the produced header. type Header; /// The type of the produced entry. type Entry; /// Parse a header from `input`. Returns a tuple of `input` sliced to contain just the entries /// corresponding to this header (without the header itself), and the parsed representation of /// the header itself. fn parse_header(input: &mut R) -> Result<(R, Self::Header)>; /// Parse a single entry from `input`. Returns either a parsed representation of the entry /// or None if `input` is exhausted. fn parse_entry(input: &mut R, header: &Self::Header) -> Result>; } #[derive(Clone, Debug)] pub struct DebugLookup where R: Reader, Parser: LookupParser, { input_buffer: R, phantom: PhantomData, } impl From for DebugLookup where R: Reader, Parser: LookupParser, { fn from(input_buffer: R) -> Self { DebugLookup { input_buffer, phantom: PhantomData, } } } impl DebugLookup where R: Reader, Parser: LookupParser, { pub fn items(&self) -> LookupEntryIter { LookupEntryIter { current_set: None, remaining_input: self.input_buffer.clone(), } } pub fn reader(&self) -> &R { &self.input_buffer } } #[derive(Clone, Debug)] pub struct LookupEntryIter where R: Reader, Parser: LookupParser, { current_set: Option<(R, Parser::Header)>, // Only none at the very beginning and end. remaining_input: R, } impl LookupEntryIter where R: Reader, Parser: LookupParser, { /// Advance the iterator and return the next entry. /// /// Returns the newly parsed entry as `Ok(Some(Parser::Entry))`. Returns /// `Ok(None)` when iteration is complete and all entries have already been /// parsed and yielded. If an error occurs while parsing the next entry, /// then this error is returned as `Err(e)`, and all subsequent calls return /// `Ok(None)`. /// /// Can be [used with `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn next(&mut self) -> Result> { loop { if let Some((ref mut input, ref header)) = self.current_set { if !input.is_empty() { match Parser::parse_entry(input, header) { Ok(Some(entry)) => return Ok(Some(entry)), Ok(None) => {} Err(e) => { input.empty(); self.remaining_input.empty(); return Err(e); } } } } if self.remaining_input.is_empty() { self.current_set = None; return Ok(None); } match Parser::parse_header(&mut self.remaining_input) { Ok(set) => { self.current_set = Some(set); } Err(e) => { self.current_set = None; self.remaining_input.empty(); return Err(e); } } } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct PubStuffHeader { format: Format, length: T, version: u16, unit_offset: DebugInfoOffset, unit_length: T, } pub trait PubStuffEntry { fn new( die_offset: UnitOffset, name: R, unit_header_offset: DebugInfoOffset, ) -> Self; } #[derive(Clone, Debug)] pub struct PubStuffParser where R: Reader, Entry: PubStuffEntry, { // This struct is never instantiated. phantom: PhantomData<(R, Entry)>, } impl LookupParser for PubStuffParser where R: Reader, Entry: PubStuffEntry, { type Header = PubStuffHeader; type Entry = Entry; /// Parse an pubthings set header. Returns a tuple of the /// pubthings to be parsed for this set, and the newly created PubThingHeader struct. fn parse_header(input: &mut R) -> Result<(R, Self::Header)> { let (length, format) = input.read_initial_length()?; let mut rest = input.split(length)?; let version = rest.read_u16()?; if version != 2 { return Err(Error::UnknownVersion(u64::from(version))); } let unit_offset = parse_debug_info_offset(&mut rest, format)?; let unit_length = rest.read_length(format)?; let header = PubStuffHeader { format, length, version, unit_offset, unit_length, }; Ok((rest, header)) } /// Parse a single pubthing. Return `None` for the null pubthing, `Some` for an actual pubthing. fn parse_entry(input: &mut R, header: &Self::Header) -> Result> { let offset = input.read_offset(header.format)?; if offset.into_u64() == 0 { input.empty(); Ok(None) } else { let name = input.read_null_terminated_slice()?; Ok(Some(Self::Entry::new( UnitOffset(offset), name, header.unit_offset, ))) } } } gimli-0.31.1/src/read/mod.rs000064400000000000000000001017271046102023000136460ustar 00000000000000//! Read DWARF debugging information. //! //! * [Example Usage](#example-usage) //! * [API Structure](#api-structure) //! * [Using with `FallibleIterator`](#using-with-fallibleiterator) //! //! ## Example Usage //! //! Print out all of the functions in the debuggee program: //! //! ```rust,no_run //! # fn example() -> Result<(), gimli::Error> { //! # type R = gimli::EndianSlice<'static, gimli::LittleEndian>; //! # let get_file_section_reader = |name| -> Result { unimplemented!() }; //! # let get_sup_file_section_reader = |name| -> Result { unimplemented!() }; //! // Read the DWARF sections with whatever object loader you're using. //! // These closures should return a `Reader` instance (e.g. `EndianSlice`). //! let loader = |section: gimli::SectionId| { get_file_section_reader(section.name()) }; //! let sup_loader = |section: gimli::SectionId| { get_sup_file_section_reader(section.name()) }; //! let mut dwarf = gimli::Dwarf::load(loader)?; //! dwarf.load_sup(sup_loader)?; //! //! // Iterate over all compilation units. //! let mut iter = dwarf.units(); //! while let Some(header) = iter.next()? { //! // Parse the abbreviations and other information for this compilation unit. //! let unit = dwarf.unit(header)?; //! //! // Iterate over all of this compilation unit's entries. //! let mut entries = unit.entries(); //! while let Some((_, entry)) = entries.next_dfs()? { //! // If we find an entry for a function, print it. //! if entry.tag() == gimli::DW_TAG_subprogram { //! println!("Found a function: {:?}", entry); //! } //! } //! } //! # unreachable!() //! # } //! ``` //! //! Full example programs: //! //! * [A simple parser](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/simple.rs) //! //! * [A `dwarfdump` //! clone](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/dwarfdump.rs) //! //! * [An `addr2line` clone](https://github.com/gimli-rs/addr2line) //! //! * [`ddbug`](https://github.com/gimli-rs/ddbug), a utility giving insight into //! code generation by making debugging information readable //! //! * [`dwprod`](https://github.com/fitzgen/dwprod), a tiny utility to list the //! compilers used to create each compilation unit within a shared library or //! executable (via `DW_AT_producer`) //! //! * [`dwarf-validate`](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/dwarf-validate.rs), //! a program to validate the integrity of some DWARF and its references //! between sections and compilation units. //! //! ## API Structure //! //! * Basic familiarity with DWARF is assumed. //! //! * The [`Dwarf`](./struct.Dwarf.html) type contains the commonly used DWARF //! sections. It has methods that simplify access to debugging data that spans //! multiple sections. Use of this type is optional, but recommended. //! //! * The [`DwarfPackage`](./struct.Dwarf.html) type contains the DWARF //! package (DWP) sections. It has methods to find a DWARF object (DWO) //! within the package. //! //! * Each section gets its own type. Consider these types the entry points to //! the library: //! //! * [`DebugAbbrev`](./struct.DebugAbbrev.html): The `.debug_abbrev` section. //! //! * [`DebugAddr`](./struct.DebugAddr.html): The `.debug_addr` section. //! //! * [`DebugAranges`](./struct.DebugAranges.html): The `.debug_aranges` //! section. //! //! * [`DebugFrame`](./struct.DebugFrame.html): The `.debug_frame` section. //! //! * [`DebugInfo`](./struct.DebugInfo.html): The `.debug_info` section. //! //! * [`DebugLine`](./struct.DebugLine.html): The `.debug_line` section. //! //! * [`DebugLineStr`](./struct.DebugLineStr.html): The `.debug_line_str` section. //! //! * [`DebugLoc`](./struct.DebugLoc.html): The `.debug_loc` section. //! //! * [`DebugLocLists`](./struct.DebugLocLists.html): The `.debug_loclists` section. //! //! * [`DebugPubNames`](./struct.DebugPubNames.html): The `.debug_pubnames` //! section. //! //! * [`DebugPubTypes`](./struct.DebugPubTypes.html): The `.debug_pubtypes` //! section. //! //! * [`DebugRanges`](./struct.DebugRanges.html): The `.debug_ranges` section. //! //! * [`DebugRngLists`](./struct.DebugRngLists.html): The `.debug_rnglists` section. //! //! * [`DebugStr`](./struct.DebugStr.html): The `.debug_str` section. //! //! * [`DebugStrOffsets`](./struct.DebugStrOffsets.html): The `.debug_str_offsets` section. //! //! * [`DebugTypes`](./struct.DebugTypes.html): The `.debug_types` section. //! //! * [`DebugCuIndex`](./struct.DebugCuIndex.html): The `.debug_cu_index` section. //! //! * [`DebugTuIndex`](./struct.DebugTuIndex.html): The `.debug_tu_index` section. //! //! * [`EhFrame`](./struct.EhFrame.html): The `.eh_frame` section. //! //! * [`EhFrameHdr`](./struct.EhFrameHdr.html): The `.eh_frame_hdr` section. //! //! * Each section type exposes methods for accessing the debugging data encoded //! in that section. For example, the [`DebugInfo`](./struct.DebugInfo.html) //! struct has the [`units`](./struct.DebugInfo.html#method.units) method for //! iterating over the compilation units defined within it. //! //! * Offsets into a section are strongly typed: an offset into `.debug_info` is //! the [`DebugInfoOffset`](./struct.DebugInfoOffset.html) type. It cannot be //! used to index into the [`DebugLine`](./struct.DebugLine.html) type because //! `DebugLine` represents the `.debug_line` section. There are similar types //! for offsets relative to a compilation unit rather than a section. //! //! ## Using with `FallibleIterator` //! //! The standard library's `Iterator` trait and related APIs do not play well //! with iterators where the `next` operation is fallible. One can make the //! `Iterator`'s associated `Item` type be a `Result`, however the //! provided methods cannot gracefully handle the case when an `Err` is //! returned. //! //! This situation led to the //! [`fallible-iterator`](https://crates.io/crates/fallible-iterator) crate's //! existence. You can read more of the rationale for its existence in its //! docs. The crate provides the helpers you have come to expect (eg `map`, //! `filter`, etc) for iterators that can fail. //! //! `gimli`'s many lazy parsing iterators are a perfect match for the //! `fallible-iterator` crate's `FallibleIterator` trait because parsing is not //! done eagerly. Parse errors later in the input might only be discovered after //! having iterated through many items. //! //! To use `gimli` iterators with `FallibleIterator`, import the crate and trait //! into your code: //! //! ``` //! # #[cfg(feature = "fallible-iterator")] //! # fn foo() { //! // Use the `FallibleIterator` trait so its methods are in scope! //! use fallible_iterator::FallibleIterator; //! use gimli::{DebugAranges, EndianSlice, LittleEndian}; //! //! fn find_sum_of_address_range_lengths(aranges: DebugAranges>) //! -> gimli::Result //! { //! // `DebugAranges::headers` returns a `FallibleIterator`! //! aranges.headers() //! // `flat_map` is provided by `FallibleIterator`! //! .flat_map(|header| Ok(header.entries())) //! // `map` is provided by `FallibleIterator`! //! .map(|arange| Ok(arange.length())) //! // `fold` is provided by `FallibleIterator`! //! .fold(0, |sum, len| Ok(sum + len)) //! } //! # } //! # fn main() {} //! ``` use core::fmt::{self, Debug}; use core::result; #[cfg(feature = "std")] use std::{error, io}; use crate::common::{Register, SectionId}; use crate::constants; mod util; pub use util::*; mod addr; pub use self::addr::*; mod cfi; pub use self::cfi::*; #[cfg(feature = "read")] mod dwarf; #[cfg(feature = "read")] pub use self::dwarf::*; mod endian_slice; pub use self::endian_slice::*; #[cfg(feature = "endian-reader")] mod endian_reader; #[cfg(feature = "endian-reader")] pub use self::endian_reader::*; mod reader; pub use self::reader::*; mod relocate; pub use self::relocate::*; #[cfg(feature = "read")] mod abbrev; #[cfg(feature = "read")] pub use self::abbrev::*; mod aranges; pub use self::aranges::*; mod index; pub use self::index::*; #[cfg(feature = "read")] mod line; #[cfg(feature = "read")] pub use self::line::*; mod lists; mod loclists; pub use self::loclists::*; #[cfg(feature = "read")] mod lookup; mod op; pub use self::op::*; #[cfg(feature = "read")] mod pubnames; #[cfg(feature = "read")] pub use self::pubnames::*; #[cfg(feature = "read")] mod pubtypes; #[cfg(feature = "read")] pub use self::pubtypes::*; mod rnglists; pub use self::rnglists::*; mod str; pub use self::str::*; /// An offset into the current compilation or type unit. #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct UnitOffset(pub T); #[cfg(feature = "read")] mod unit; #[cfg(feature = "read")] pub use self::unit::*; mod value; pub use self::value::*; /// Indicates that storage should be allocated on heap. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct StoreOnHeap; /// `EndianBuf` has been renamed to `EndianSlice`. For ease of upgrading across /// `gimli` versions, we export this type alias. #[deprecated(note = "EndianBuf has been renamed to EndianSlice, use that instead.")] pub type EndianBuf<'input, Endian> = EndianSlice<'input, Endian>; /// An error that occurred when parsing. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] pub enum Error { /// An I/O error occurred while reading. Io, /// Found a PC relative pointer, but the section base is undefined. PcRelativePointerButSectionBaseIsUndefined, /// Found a `.text` relative pointer, but the `.text` base is undefined. TextRelativePointerButTextBaseIsUndefined, /// Found a data relative pointer, but the data base is undefined. DataRelativePointerButDataBaseIsUndefined, /// Found a function relative pointer in a context that does not have a /// function base. FuncRelativePointerInBadContext, /// Cannot parse a pointer with a `DW_EH_PE_omit` encoding. CannotParseOmitPointerEncoding, /// An error parsing an unsigned LEB128 value. BadUnsignedLeb128, /// An error parsing a signed LEB128 value. BadSignedLeb128, /// An abbreviation declared that its tag is zero, but zero is reserved for /// null records. AbbreviationTagZero, /// An attribute specification declared that its form is zero, but zero is /// reserved for null records. AttributeFormZero, /// The abbreviation's has-children byte was not one of /// `DW_CHILDREN_{yes,no}`. BadHasChildren, /// The specified length is impossible. BadLength, /// Found an unknown `DW_FORM_*` type. UnknownForm(constants::DwForm), /// Expected a zero, found something else. ExpectedZero, /// Found an abbreviation code that has already been used. DuplicateAbbreviationCode, /// Found a duplicate arange. DuplicateArange, /// Found an unknown reserved length value. UnknownReservedLength, /// Found an unknown DWARF version. UnknownVersion(u64), /// Found a record with an unknown abbreviation code. UnknownAbbreviation(u64), /// Hit the end of input before it was expected. UnexpectedEof(ReaderOffsetId), /// Read a null entry before it was expected. UnexpectedNull, /// Found an unknown standard opcode. UnknownStandardOpcode(constants::DwLns), /// Found an unknown extended opcode. UnknownExtendedOpcode(constants::DwLne), /// Found an unknown location-lists format. UnknownLocListsEntry(constants::DwLle), /// Found an unknown range-lists format. UnknownRangeListsEntry(constants::DwRle), /// The specified address size is not supported. UnsupportedAddressSize(u8), /// The specified offset size is not supported. UnsupportedOffsetSize(u8), /// The specified field size is not supported. UnsupportedFieldSize(u8), /// The minimum instruction length must not be zero. MinimumInstructionLengthZero, /// The maximum operations per instruction must not be zero. MaximumOperationsPerInstructionZero, /// The line range must not be zero. LineRangeZero, /// The opcode base must not be zero. OpcodeBaseZero, /// Found an invalid UTF-8 string. BadUtf8, /// Expected to find the CIE ID, but found something else. NotCieId, /// Expected to find a pointer to a CIE, but found the CIE ID instead. NotCiePointer, /// Expected to find a pointer to an FDE, but found a CIE instead. NotFdePointer, /// Invalid branch target for a DW_OP_bra or DW_OP_skip. BadBranchTarget(u64), /// DW_OP_push_object_address used but no address passed in. InvalidPushObjectAddress, /// Not enough items on the stack when evaluating an expression. NotEnoughStackItems, /// Too many iterations to compute the expression. TooManyIterations, /// An unrecognized operation was found while parsing a DWARF /// expression. InvalidExpression(constants::DwOp), /// An unsupported operation was found while evaluating a DWARF expression. UnsupportedEvaluation, /// The expression had a piece followed by an expression /// terminator without a piece. InvalidPiece, /// An expression-terminating operation was followed by something /// other than the end of the expression or a piece operation. InvalidExpressionTerminator(u64), /// Division or modulus by zero when evaluating an expression. DivisionByZero, /// An expression operation used mismatching types. TypeMismatch, /// An expression operation required an integral type but saw a /// floating point type. IntegralTypeRequired, /// An expression operation used types that are not supported. UnsupportedTypeOperation, /// The shift value in an expression must be a non-negative integer. InvalidShiftExpression, /// The size of a deref expression must not be larger than the size of an address. InvalidDerefSize(u8), /// An unknown DW_CFA_* instruction. UnknownCallFrameInstruction(constants::DwCfa), /// The end of an address range was before the beginning. InvalidAddressRange, /// An address calculation overflowed. /// /// This is returned in cases where the address is expected to be /// larger than a previous address, but the calculation overflowed. AddressOverflow, /// Encountered a call frame instruction in a context in which it is not /// valid. CfiInstructionInInvalidContext, /// When evaluating call frame instructions, found a `DW_CFA_restore_state` /// stack pop instruction, but the stack was empty, and had nothing to pop. PopWithEmptyStack, /// Do not have unwind info for the given address. NoUnwindInfoForAddress, /// An offset value was larger than the maximum supported value. UnsupportedOffset, /// The given pointer encoding is either unknown or invalid. UnknownPointerEncoding(constants::DwEhPe), /// Did not find an entry at the given offset. NoEntryAtGivenOffset, /// The given offset is out of bounds. OffsetOutOfBounds, /// Found an unknown CFI augmentation. UnknownAugmentation, /// We do not support the given pointer encoding yet. UnsupportedPointerEncoding, /// Registers larger than `u16` are not supported. UnsupportedRegister(u64), /// The CFI program defined more register rules than we have storage for. TooManyRegisterRules, /// Attempted to push onto the CFI or evaluation stack, but it was already /// at full capacity. StackFull, /// The `.eh_frame_hdr` binary search table claims to be variable-length encoded, /// which makes binary search impossible. VariableLengthSearchTable, /// The `DW_UT_*` value for this unit is not supported yet. UnsupportedUnitType, /// Ranges using AddressIndex are not supported yet. UnsupportedAddressIndex, /// Nonzero segment selector sizes aren't supported yet. UnsupportedSegmentSize, /// A compilation unit or type unit is missing its top level DIE. MissingUnitDie, /// A DIE attribute used an unsupported form. UnsupportedAttributeForm, /// Missing DW_LNCT_path in file entry format. MissingFileEntryFormatPath, /// Expected an attribute value to be a string form. ExpectedStringAttributeValue, /// `DW_FORM_implicit_const` used in an invalid context. InvalidImplicitConst, /// Invalid section count in `.dwp` index. InvalidIndexSectionCount, /// Invalid slot count in `.dwp` index. InvalidIndexSlotCount, /// Invalid hash row in `.dwp` index. InvalidIndexRow, /// Unknown section type in `.dwp` index. UnknownIndexSection(constants::DwSect), /// Unknown section type in version 2 `.dwp` index. UnknownIndexSectionV2(constants::DwSectV2), } impl fmt::Display for Error { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> ::core::result::Result<(), fmt::Error> { write!(f, "{}", self.description()) } } impl Error { /// A short description of the error. pub fn description(&self) -> &str { match *self { Error::Io => "An I/O error occurred while reading.", Error::PcRelativePointerButSectionBaseIsUndefined => { "Found a PC relative pointer, but the section base is undefined." } Error::TextRelativePointerButTextBaseIsUndefined => { "Found a `.text` relative pointer, but the `.text` base is undefined." } Error::DataRelativePointerButDataBaseIsUndefined => { "Found a data relative pointer, but the data base is undefined." } Error::FuncRelativePointerInBadContext => { "Found a function relative pointer in a context that does not have a function base." } Error::CannotParseOmitPointerEncoding => { "Cannot parse a pointer with a `DW_EH_PE_omit` encoding." } Error::BadUnsignedLeb128 => "An error parsing an unsigned LEB128 value", Error::BadSignedLeb128 => "An error parsing a signed LEB128 value", Error::AbbreviationTagZero => { "An abbreviation declared that its tag is zero, but zero is reserved for null records" } Error::AttributeFormZero => { "An attribute specification declared that its form is zero, but zero is reserved for null records" } Error::BadHasChildren => { "The abbreviation's has-children byte was not one of `DW_CHILDREN_{yes,no}`" } Error::BadLength => "The specified length is impossible", Error::UnknownForm(_) => "Found an unknown `DW_FORM_*` type", Error::ExpectedZero => "Expected a zero, found something else", Error::DuplicateAbbreviationCode => { "Found an abbreviation code that has already been used" } Error::DuplicateArange => "Found a duplicate arange", Error::UnknownReservedLength => "Found an unknown reserved length value", Error::UnknownVersion(_) => "Found an unknown DWARF version", Error::UnknownAbbreviation(_) => "Found a record with an unknown abbreviation code", Error::UnexpectedEof(_) => "Hit the end of input before it was expected", Error::UnexpectedNull => "Read a null entry before it was expected.", Error::UnknownStandardOpcode(_) => "Found an unknown standard opcode", Error::UnknownExtendedOpcode(_) => "Found an unknown extended opcode", Error::UnknownLocListsEntry(_) => "Found an unknown location lists entry", Error::UnknownRangeListsEntry(_) => "Found an unknown range lists entry", Error::UnsupportedAddressSize(_) => "The specified address size is not supported", Error::UnsupportedOffsetSize(_) => "The specified offset size is not supported", Error::UnsupportedFieldSize(_) => "The specified field size is not supported", Error::MinimumInstructionLengthZero => { "The minimum instruction length must not be zero." } Error::MaximumOperationsPerInstructionZero => { "The maximum operations per instruction must not be zero." } Error::LineRangeZero => "The line range must not be zero.", Error::OpcodeBaseZero => "The opcode base must not be zero.", Error::BadUtf8 => "Found an invalid UTF-8 string.", Error::NotCieId => "Expected to find the CIE ID, but found something else.", Error::NotCiePointer => "Expected to find a CIE pointer, but found the CIE ID instead.", Error::NotFdePointer => { "Expected to find an FDE pointer, but found a CIE pointer instead." } Error::BadBranchTarget(_) => "Invalid branch target in DWARF expression", Error::InvalidPushObjectAddress => { "DW_OP_push_object_address used but no object address given" } Error::NotEnoughStackItems => "Not enough items on stack when evaluating expression", Error::TooManyIterations => "Too many iterations to evaluate DWARF expression", Error::InvalidExpression(_) => "Invalid opcode in DWARF expression", Error::UnsupportedEvaluation => "Unsupported operation when evaluating expression", Error::InvalidPiece => { "DWARF expression has piece followed by non-piece expression at end" } Error::InvalidExpressionTerminator(_) => "Expected DW_OP_piece or DW_OP_bit_piece", Error::DivisionByZero => "Division or modulus by zero when evaluating expression", Error::TypeMismatch => "Type mismatch when evaluating expression", Error::IntegralTypeRequired => "Integral type expected when evaluating expression", Error::UnsupportedTypeOperation => { "An expression operation used types that are not supported" } Error::InvalidShiftExpression => { "The shift value in an expression must be a non-negative integer." } Error::InvalidDerefSize(_) => { "The size of a deref expression must not be larger than the size of an address." } Error::UnknownCallFrameInstruction(_) => "An unknown DW_CFA_* instructiion", Error::InvalidAddressRange => { "The end of an address range must not be before the beginning." } Error::AddressOverflow => "An address calculation overflowed.", Error::CfiInstructionInInvalidContext => { "Encountered a call frame instruction in a context in which it is not valid." } Error::PopWithEmptyStack => { "When evaluating call frame instructions, found a `DW_CFA_restore_state` stack pop \ instruction, but the stack was empty, and had nothing to pop." } Error::NoUnwindInfoForAddress => "Do not have unwind info for the given address.", Error::UnsupportedOffset => { "An offset value was larger than the maximum supported value." } Error::UnknownPointerEncoding(_) => { "The given pointer encoding is either unknown or invalid." } Error::NoEntryAtGivenOffset => "Did not find an entry at the given offset.", Error::OffsetOutOfBounds => "The given offset is out of bounds.", Error::UnknownAugmentation => "Found an unknown CFI augmentation.", Error::UnsupportedPointerEncoding => { "We do not support the given pointer encoding yet." } Error::UnsupportedRegister(_) => "Registers larger than `u16` are not supported.", Error::TooManyRegisterRules => { "The CFI program defined more register rules than we have storage for." } Error::StackFull => { "Attempted to push onto the CFI stack, but it was already at full capacity." } Error::VariableLengthSearchTable => { "The `.eh_frame_hdr` binary search table claims to be variable-length encoded, \ which makes binary search impossible." } Error::UnsupportedUnitType => "The `DW_UT_*` value for this unit is not supported yet", Error::UnsupportedAddressIndex => "Ranges involving AddressIndex are not supported yet", Error::UnsupportedSegmentSize => "Nonzero segment size not supported yet", Error::MissingUnitDie => { "A compilation unit or type unit is missing its top level DIE." } Error::UnsupportedAttributeForm => "A DIE attribute used an unsupported form.", Error::MissingFileEntryFormatPath => "Missing DW_LNCT_path in file entry format.", Error::ExpectedStringAttributeValue => { "Expected an attribute value to be a string form." } Error::InvalidImplicitConst => "DW_FORM_implicit_const used in an invalid context.", Error::InvalidIndexSectionCount => "Invalid section count in `.dwp` index.", Error::InvalidIndexSlotCount => "Invalid slot count in `.dwp` index.", Error::InvalidIndexRow => "Invalid hash row in `.dwp` index.", Error::UnknownIndexSection(_) => "Unknown section type in `.dwp` index.", Error::UnknownIndexSectionV2(_) => "Unknown section type in version 2 `.dwp` index.", } } } #[cfg(feature = "std")] impl error::Error for Error {} #[cfg(feature = "std")] impl From for Error { fn from(_: io::Error) -> Self { Error::Io } } /// The result of a parse. pub type Result = result::Result; /// A convenience trait for loading DWARF sections from object files. To be /// used like: /// /// ``` /// use gimli::{DebugInfo, EndianSlice, LittleEndian, Reader, Section}; /// /// let buf = [0x00, 0x01, 0x02, 0x03]; /// let reader = EndianSlice::new(&buf, LittleEndian); /// let loader = |name| -> Result<_, ()> { Ok(reader) }; /// /// let debug_info: DebugInfo<_> = Section::load(loader).unwrap(); /// ``` pub trait Section: From { /// Returns the section id for this type. fn id() -> SectionId; /// Returns the ELF section name for this type. fn section_name() -> &'static str { Self::id().name() } /// Returns the ELF section name (if any) for this type when used in a dwo /// file. fn dwo_section_name() -> Option<&'static str> { Self::id().dwo_name() } /// Returns the XCOFF section name (if any) for this type when used in a XCOFF /// file. fn xcoff_section_name() -> Option<&'static str> { Self::id().xcoff_name() } /// Try to load the section using the given loader function. fn load(f: F) -> core::result::Result where F: FnOnce(SectionId) -> core::result::Result, { f(Self::id()).map(From::from) } /// Returns the `Reader` for this section. fn reader(&self) -> &R where R: Reader; /// Returns the subrange of the section that is the contribution of /// a unit in a `.dwp` file. fn dwp_range(&self, offset: u32, size: u32) -> Result where R: Reader, { let mut data = self.reader().clone(); data.skip(R::Offset::from_u32(offset))?; data.truncate(R::Offset::from_u32(size))?; Ok(data.into()) } /// Returns the `Reader` for this section. fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> where R: Reader, { self.reader() .lookup_offset_id(id) .map(|offset| (Self::id(), offset)) } } impl Register { pub(crate) fn from_u64(x: u64) -> Result { let y = x as u16; if u64::from(y) == x { Ok(Register(y)) } else { Err(Error::UnsupportedRegister(x)) } } } #[cfg(test)] mod tests { use super::*; use crate::common::Format; use crate::endianity::LittleEndian; use test_assembler::{Endian, Section}; #[test] fn test_parse_initial_length_32_ok() { let section = Section::with_endian(Endian::Little).L32(0x7856_3412); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_initial_length() { Ok((length, format)) => { assert_eq!(input.len(), 0); assert_eq!(format, Format::Dwarf32); assert_eq!(0x7856_3412, length); } otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] fn test_parse_initial_length_64_ok() { let section = Section::with_endian(Endian::Little) // Dwarf_64_INITIAL_UNIT_LENGTH .L32(0xffff_ffff) // Actual length .L64(0xffde_bc9a_7856_3412); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); #[cfg(target_pointer_width = "64")] match input.read_initial_length() { Ok((length, format)) => { assert_eq!(input.len(), 0); assert_eq!(format, Format::Dwarf64); assert_eq!(0xffde_bc9a_7856_3412, length); } otherwise => panic!("Unexpected result: {:?}", otherwise), } #[cfg(target_pointer_width = "32")] match input.read_initial_length() { Err(Error::UnsupportedOffset) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_initial_length_unknown_reserved_value() { let section = Section::with_endian(Endian::Little).L32(0xffff_fffe); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_initial_length() { Err(Error::UnknownReservedLength) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_initial_length_incomplete() { let buf = [0xff, 0xff, 0xff]; // Need at least 4 bytes. let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_initial_length() { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_initial_length_64_incomplete() { let section = Section::with_endian(Endian::Little) // Dwarf_64_INITIAL_UNIT_LENGTH .L32(0xffff_ffff) // Actual length is not long enough. .L32(0x7856_3412); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_initial_length() { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_offset_32() { let section = Section::with_endian(Endian::Little).L32(0x0123_4567); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_offset(Format::Dwarf32) { Ok(val) => { assert_eq!(input.len(), 0); assert_eq!(val, 0x0123_4567); } otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_offset_64_small() { let section = Section::with_endian(Endian::Little).L64(0x0123_4567); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_offset(Format::Dwarf64) { Ok(val) => { assert_eq!(input.len(), 0); assert_eq!(val, 0x0123_4567); } otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_offset_64_large() { let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_offset(Format::Dwarf64) { Ok(val) => { assert_eq!(input.len(), 0); assert_eq!(val, 0x0123_4567_89ab_cdef); } otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "32")] fn test_parse_offset_64_large() { let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef); let buf = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_offset(Format::Dwarf64) { Err(Error::UnsupportedOffset) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } } gimli-0.31.1/src/read/op.rs000064400000000000000000004350221046102023000135030ustar 00000000000000//! Functions for parsing and evaluating DWARF expressions. #[cfg(feature = "read")] use alloc::vec::Vec; use core::mem; use super::util::{ArrayLike, ArrayVec}; use crate::common::{DebugAddrIndex, DebugInfoOffset, Encoding, Register}; use crate::constants; use crate::read::{Error, Reader, ReaderOffset, Result, StoreOnHeap, UnitOffset, Value, ValueType}; /// A reference to a DIE, either relative to the current CU or /// relative to the section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DieReference { /// A CU-relative reference. UnitRef(UnitOffset), /// A section-relative reference. DebugInfoRef(DebugInfoOffset), } /// A single decoded DWARF expression operation. /// /// DWARF expression evaluation is done in two parts: first the raw /// bytes of the next part of the expression are decoded; and then the /// decoded operation is evaluated. This approach lets other /// consumers inspect the DWARF expression without reimplementing the /// decoding operation. /// /// Multiple DWARF opcodes may decode into a single `Operation`. For /// example, both `DW_OP_deref` and `DW_OP_xderef` are represented /// using `Operation::Deref`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Operation::Offset> where R: Reader, Offset: ReaderOffset, { /// Dereference the topmost value of the stack. Deref { /// The DIE of the base type or 0 to indicate the generic type base_type: UnitOffset, /// The size of the data to dereference. size: u8, /// True if the dereference operation takes an address space /// argument from the stack; false otherwise. space: bool, }, /// Drop an item from the stack. Drop, /// Pick an item from the stack and push it on top of the stack. /// This operation handles `DW_OP_pick`, `DW_OP_dup`, and /// `DW_OP_over`. Pick { /// The index, from the top of the stack, of the item to copy. index: u8, }, /// Swap the top two stack items. Swap, /// Rotate the top three stack items. Rot, /// Take the absolute value of the top of the stack. Abs, /// Bitwise `and` of the top two values on the stack. And, /// Divide the top two values on the stack. Div, /// Subtract the top two values on the stack. Minus, /// Modulus of the top two values on the stack. Mod, /// Multiply the top two values on the stack. Mul, /// Negate the top of the stack. Neg, /// Bitwise `not` of the top of the stack. Not, /// Bitwise `or` of the top two values on the stack. Or, /// Add the top two values on the stack. Plus, /// Add a constant to the topmost value on the stack. PlusConstant { /// The value to add. value: u64, }, /// Logical left shift of the 2nd value on the stack by the number /// of bits given by the topmost value on the stack. Shl, /// Right shift of the 2nd value on the stack by the number of /// bits given by the topmost value on the stack. Shr, /// Arithmetic left shift of the 2nd value on the stack by the /// number of bits given by the topmost value on the stack. Shra, /// Bitwise `xor` of the top two values on the stack. Xor, /// Branch to the target location if the top of stack is nonzero. Bra { /// The relative offset to the target bytecode. target: i16, }, /// Compare the top two stack values for equality. Eq, /// Compare the top two stack values using `>=`. Ge, /// Compare the top two stack values using `>`. Gt, /// Compare the top two stack values using `<=`. Le, /// Compare the top two stack values using `<`. Lt, /// Compare the top two stack values using `!=`. Ne, /// Unconditional branch to the target location. Skip { /// The relative offset to the target bytecode. target: i16, }, /// Push an unsigned constant value on the stack. This handles multiple /// DWARF opcodes. UnsignedConstant { /// The value to push. value: u64, }, /// Push a signed constant value on the stack. This handles multiple /// DWARF opcodes. SignedConstant { /// The value to push. value: i64, }, /// Indicate that this piece's location is in the given register. /// /// Completes the piece or expression. Register { /// The register number. register: Register, }, /// Find the value of the given register, add the offset, and then /// push the resulting sum on the stack. RegisterOffset { /// The register number. register: Register, /// The offset to add. offset: i64, /// The DIE of the base type or 0 to indicate the generic type base_type: UnitOffset, }, /// Compute the frame base (using `DW_AT_frame_base`), add the /// given offset, and then push the resulting sum on the stack. FrameOffset { /// The offset to add. offset: i64, }, /// No operation. Nop, /// Push the object address on the stack. PushObjectAddress, /// Evaluate a DWARF expression as a subroutine. The expression /// comes from the `DW_AT_location` attribute of the indicated /// DIE. Call { /// The DIE to use. offset: DieReference, }, /// Compute the address of a thread-local variable and push it on /// the stack. TLS, /// Compute the call frame CFA and push it on the stack. CallFrameCFA, /// Terminate a piece. Piece { /// The size of this piece in bits. size_in_bits: u64, /// The bit offset of this piece. If `None`, then this piece /// was specified using `DW_OP_piece` and should start at the /// next byte boundary. bit_offset: Option, }, /// The object has no location, but has a known constant value. /// /// Represents `DW_OP_implicit_value`. /// Completes the piece or expression. ImplicitValue { /// The implicit value to use. data: R, }, /// The object has no location, but its value is at the top of the stack. /// /// Represents `DW_OP_stack_value`. /// Completes the piece or expression. StackValue, /// The object is a pointer to a value which has no actual location, /// such as an implicit value or a stack value. /// /// Represents `DW_OP_implicit_pointer`. /// Completes the piece or expression. ImplicitPointer { /// The `.debug_info` offset of the value that this is an implicit pointer into. value: DebugInfoOffset, /// The byte offset into the value that the implicit pointer points to. byte_offset: i64, }, /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. /// /// Represents `DW_OP_entry_value`. EntryValue { /// The expression to be evaluated. expression: R, }, /// This represents a parameter that was optimized out. /// /// The offset points to the definition of the parameter, and is /// matched to the `DW_TAG_GNU_call_site_parameter` in the caller that also /// points to the same definition of the parameter. /// /// Represents `DW_OP_GNU_parameter_ref`. ParameterRef { /// The DIE to use. offset: UnitOffset, }, /// Relocate the address if needed, and push it on the stack. /// /// Represents `DW_OP_addr`. Address { /// The offset to add. address: u64, }, /// Read the address at the given index in `.debug_addr, relocate the address if needed, /// and push it on the stack. /// /// Represents `DW_OP_addrx`. AddressIndex { /// The index of the address in `.debug_addr`. index: DebugAddrIndex, }, /// Read the address at the given index in `.debug_addr, and push it on the stack. /// Do not relocate the address. /// /// Represents `DW_OP_constx`. ConstantIndex { /// The index of the address in `.debug_addr`. index: DebugAddrIndex, }, /// Interpret the value bytes as a constant of a given type, and push it on the stack. /// /// Represents `DW_OP_const_type`. TypedLiteral { /// The DIE of the base type. base_type: UnitOffset, /// The value bytes. value: R, }, /// Pop the top stack entry, convert it to a different type, and push it on the stack. /// /// Represents `DW_OP_convert`. Convert { /// The DIE of the base type. base_type: UnitOffset, }, /// Pop the top stack entry, reinterpret the bits in its value as a different type, /// and push it on the stack. /// /// Represents `DW_OP_reinterpret`. Reinterpret { /// The DIE of the base type. base_type: UnitOffset, }, /// The index of a local in the currently executing function. /// /// Represents `DW_OP_WASM_location 0x00`. /// Completes the piece or expression. WasmLocal { /// The index of the local. index: u32, }, /// The index of a global. /// /// Represents `DW_OP_WASM_location 0x01` or `DW_OP_WASM_location 0x03`. /// Completes the piece or expression. WasmGlobal { /// The index of the global. index: u32, }, /// The index of an item on the operand stack. /// /// Represents `DW_OP_WASM_location 0x02`. /// Completes the piece or expression. WasmStack { /// The index of the stack item. 0 is the bottom of the operand stack. index: u32, }, } #[derive(Debug)] enum OperationEvaluationResult { Piece, Incomplete, Complete { location: Location }, Waiting(EvaluationWaiting, EvaluationResult), } /// A single location of a piece of the result of a DWARF expression. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Location::Offset> where R: Reader, Offset: ReaderOffset, { /// The piece is empty. Ordinarily this means the piece has been /// optimized away. Empty, /// The piece is found in a register. Register { /// The register number. register: Register, }, /// The piece is found in memory. Address { /// The address. address: u64, }, /// The piece has no location but its value is known. Value { /// The value. value: Value, }, /// The piece is represented by some constant bytes. Bytes { /// The value. value: R, }, /// The piece is a pointer to a value which has no actual location. ImplicitPointer { /// The `.debug_info` offset of the value that this is an implicit pointer into. value: DebugInfoOffset, /// The byte offset into the value that the implicit pointer points to. byte_offset: i64, }, } impl Location where R: Reader, Offset: ReaderOffset, { /// Return true if the piece is empty. pub fn is_empty(&self) -> bool { matches!(*self, Location::Empty) } } /// The description of a single piece of the result of a DWARF /// expression. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Piece::Offset> where R: Reader, Offset: ReaderOffset, { /// If given, the size of the piece in bits. If `None`, there /// must be only one piece whose size is all of the object. pub size_in_bits: Option, /// If given, the bit offset of the piece within the location. /// If the location is a `Location::Register` or `Location::Value`, /// then this offset is from the least significant bit end of /// the register or value. /// If the location is a `Location::Address` then the offset uses /// the bit numbering and direction conventions of the language /// and target system. /// /// If `None`, the piece starts at the location. If the /// location is a register whose size is larger than the piece, /// then placement within the register is defined by the ABI. pub bit_offset: Option, /// Where this piece is to be found. pub location: Location, } // A helper function to handle branch offsets. fn compute_pc(pc: &R, bytecode: &R, offset: i16) -> Result { let pc_offset = pc.offset_from(bytecode); let new_pc_offset = pc_offset.wrapping_add(R::Offset::from_i16(offset)); if new_pc_offset > bytecode.len() { Err(Error::BadBranchTarget(new_pc_offset.into_u64())) } else { let mut new_pc = bytecode.clone(); new_pc.skip(new_pc_offset)?; Ok(new_pc) } } fn generic_type() -> UnitOffset { UnitOffset(O::from_u64(0).unwrap()) } impl Operation where R: Reader, Offset: ReaderOffset, { /// Parse a single DWARF expression operation. /// /// This is useful when examining a DWARF expression for reasons other /// than direct evaluation. /// /// `bytes` points to a the operation to decode. It should point into /// the same array as `bytecode`, which should be the entire /// expression. pub fn parse(bytes: &mut R, encoding: Encoding) -> Result> { let opcode = bytes.read_u8()?; let name = constants::DwOp(opcode); match name { constants::DW_OP_addr => { let address = bytes.read_address(encoding.address_size)?; Ok(Operation::Address { address }) } constants::DW_OP_deref => Ok(Operation::Deref { base_type: generic_type(), size: encoding.address_size, space: false, }), constants::DW_OP_const1u => { let value = bytes.read_u8()?; Ok(Operation::UnsignedConstant { value: u64::from(value), }) } constants::DW_OP_const1s => { let value = bytes.read_i8()?; Ok(Operation::SignedConstant { value: i64::from(value), }) } constants::DW_OP_const2u => { let value = bytes.read_u16()?; Ok(Operation::UnsignedConstant { value: u64::from(value), }) } constants::DW_OP_const2s => { let value = bytes.read_i16()?; Ok(Operation::SignedConstant { value: i64::from(value), }) } constants::DW_OP_const4u => { let value = bytes.read_u32()?; Ok(Operation::UnsignedConstant { value: u64::from(value), }) } constants::DW_OP_const4s => { let value = bytes.read_i32()?; Ok(Operation::SignedConstant { value: i64::from(value), }) } constants::DW_OP_const8u => { let value = bytes.read_u64()?; Ok(Operation::UnsignedConstant { value }) } constants::DW_OP_const8s => { let value = bytes.read_i64()?; Ok(Operation::SignedConstant { value }) } constants::DW_OP_constu => { let value = bytes.read_uleb128()?; Ok(Operation::UnsignedConstant { value }) } constants::DW_OP_consts => { let value = bytes.read_sleb128()?; Ok(Operation::SignedConstant { value }) } constants::DW_OP_dup => Ok(Operation::Pick { index: 0 }), constants::DW_OP_drop => Ok(Operation::Drop), constants::DW_OP_over => Ok(Operation::Pick { index: 1 }), constants::DW_OP_pick => { let value = bytes.read_u8()?; Ok(Operation::Pick { index: value }) } constants::DW_OP_swap => Ok(Operation::Swap), constants::DW_OP_rot => Ok(Operation::Rot), constants::DW_OP_xderef => Ok(Operation::Deref { base_type: generic_type(), size: encoding.address_size, space: true, }), constants::DW_OP_abs => Ok(Operation::Abs), constants::DW_OP_and => Ok(Operation::And), constants::DW_OP_div => Ok(Operation::Div), constants::DW_OP_minus => Ok(Operation::Minus), constants::DW_OP_mod => Ok(Operation::Mod), constants::DW_OP_mul => Ok(Operation::Mul), constants::DW_OP_neg => Ok(Operation::Neg), constants::DW_OP_not => Ok(Operation::Not), constants::DW_OP_or => Ok(Operation::Or), constants::DW_OP_plus => Ok(Operation::Plus), constants::DW_OP_plus_uconst => { let value = bytes.read_uleb128()?; Ok(Operation::PlusConstant { value }) } constants::DW_OP_shl => Ok(Operation::Shl), constants::DW_OP_shr => Ok(Operation::Shr), constants::DW_OP_shra => Ok(Operation::Shra), constants::DW_OP_xor => Ok(Operation::Xor), constants::DW_OP_bra => { let target = bytes.read_i16()?; Ok(Operation::Bra { target }) } constants::DW_OP_eq => Ok(Operation::Eq), constants::DW_OP_ge => Ok(Operation::Ge), constants::DW_OP_gt => Ok(Operation::Gt), constants::DW_OP_le => Ok(Operation::Le), constants::DW_OP_lt => Ok(Operation::Lt), constants::DW_OP_ne => Ok(Operation::Ne), constants::DW_OP_skip => { let target = bytes.read_i16()?; Ok(Operation::Skip { target }) } constants::DW_OP_lit0 | constants::DW_OP_lit1 | constants::DW_OP_lit2 | constants::DW_OP_lit3 | constants::DW_OP_lit4 | constants::DW_OP_lit5 | constants::DW_OP_lit6 | constants::DW_OP_lit7 | constants::DW_OP_lit8 | constants::DW_OP_lit9 | constants::DW_OP_lit10 | constants::DW_OP_lit11 | constants::DW_OP_lit12 | constants::DW_OP_lit13 | constants::DW_OP_lit14 | constants::DW_OP_lit15 | constants::DW_OP_lit16 | constants::DW_OP_lit17 | constants::DW_OP_lit18 | constants::DW_OP_lit19 | constants::DW_OP_lit20 | constants::DW_OP_lit21 | constants::DW_OP_lit22 | constants::DW_OP_lit23 | constants::DW_OP_lit24 | constants::DW_OP_lit25 | constants::DW_OP_lit26 | constants::DW_OP_lit27 | constants::DW_OP_lit28 | constants::DW_OP_lit29 | constants::DW_OP_lit30 | constants::DW_OP_lit31 => Ok(Operation::UnsignedConstant { value: (opcode - constants::DW_OP_lit0.0).into(), }), constants::DW_OP_reg0 | constants::DW_OP_reg1 | constants::DW_OP_reg2 | constants::DW_OP_reg3 | constants::DW_OP_reg4 | constants::DW_OP_reg5 | constants::DW_OP_reg6 | constants::DW_OP_reg7 | constants::DW_OP_reg8 | constants::DW_OP_reg9 | constants::DW_OP_reg10 | constants::DW_OP_reg11 | constants::DW_OP_reg12 | constants::DW_OP_reg13 | constants::DW_OP_reg14 | constants::DW_OP_reg15 | constants::DW_OP_reg16 | constants::DW_OP_reg17 | constants::DW_OP_reg18 | constants::DW_OP_reg19 | constants::DW_OP_reg20 | constants::DW_OP_reg21 | constants::DW_OP_reg22 | constants::DW_OP_reg23 | constants::DW_OP_reg24 | constants::DW_OP_reg25 | constants::DW_OP_reg26 | constants::DW_OP_reg27 | constants::DW_OP_reg28 | constants::DW_OP_reg29 | constants::DW_OP_reg30 | constants::DW_OP_reg31 => Ok(Operation::Register { register: Register((opcode - constants::DW_OP_reg0.0).into()), }), constants::DW_OP_breg0 | constants::DW_OP_breg1 | constants::DW_OP_breg2 | constants::DW_OP_breg3 | constants::DW_OP_breg4 | constants::DW_OP_breg5 | constants::DW_OP_breg6 | constants::DW_OP_breg7 | constants::DW_OP_breg8 | constants::DW_OP_breg9 | constants::DW_OP_breg10 | constants::DW_OP_breg11 | constants::DW_OP_breg12 | constants::DW_OP_breg13 | constants::DW_OP_breg14 | constants::DW_OP_breg15 | constants::DW_OP_breg16 | constants::DW_OP_breg17 | constants::DW_OP_breg18 | constants::DW_OP_breg19 | constants::DW_OP_breg20 | constants::DW_OP_breg21 | constants::DW_OP_breg22 | constants::DW_OP_breg23 | constants::DW_OP_breg24 | constants::DW_OP_breg25 | constants::DW_OP_breg26 | constants::DW_OP_breg27 | constants::DW_OP_breg28 | constants::DW_OP_breg29 | constants::DW_OP_breg30 | constants::DW_OP_breg31 => { let value = bytes.read_sleb128()?; Ok(Operation::RegisterOffset { register: Register((opcode - constants::DW_OP_breg0.0).into()), offset: value, base_type: generic_type(), }) } constants::DW_OP_regx => { let register = bytes.read_uleb128().and_then(Register::from_u64)?; Ok(Operation::Register { register }) } constants::DW_OP_fbreg => { let value = bytes.read_sleb128()?; Ok(Operation::FrameOffset { offset: value }) } constants::DW_OP_bregx => { let register = bytes.read_uleb128().and_then(Register::from_u64)?; let offset = bytes.read_sleb128()?; Ok(Operation::RegisterOffset { register, offset, base_type: generic_type(), }) } constants::DW_OP_piece => { let size = bytes.read_uleb128()?; Ok(Operation::Piece { size_in_bits: 8 * size, bit_offset: None, }) } constants::DW_OP_deref_size => { let size = bytes.read_u8()?; Ok(Operation::Deref { base_type: generic_type(), size, space: false, }) } constants::DW_OP_xderef_size => { let size = bytes.read_u8()?; Ok(Operation::Deref { base_type: generic_type(), size, space: true, }) } constants::DW_OP_nop => Ok(Operation::Nop), constants::DW_OP_push_object_address => Ok(Operation::PushObjectAddress), constants::DW_OP_call2 => { let value = bytes.read_u16().map(R::Offset::from_u16)?; Ok(Operation::Call { offset: DieReference::UnitRef(UnitOffset(value)), }) } constants::DW_OP_call4 => { let value = bytes.read_u32().map(R::Offset::from_u32)?; Ok(Operation::Call { offset: DieReference::UnitRef(UnitOffset(value)), }) } constants::DW_OP_call_ref => { let value = bytes.read_offset(encoding.format)?; Ok(Operation::Call { offset: DieReference::DebugInfoRef(DebugInfoOffset(value)), }) } constants::DW_OP_form_tls_address | constants::DW_OP_GNU_push_tls_address => { Ok(Operation::TLS) } constants::DW_OP_call_frame_cfa => Ok(Operation::CallFrameCFA), constants::DW_OP_bit_piece => { let size = bytes.read_uleb128()?; let offset = bytes.read_uleb128()?; Ok(Operation::Piece { size_in_bits: size, bit_offset: Some(offset), }) } constants::DW_OP_implicit_value => { let len = bytes.read_uleb128().and_then(R::Offset::from_u64)?; let data = bytes.split(len)?; Ok(Operation::ImplicitValue { data }) } constants::DW_OP_stack_value => Ok(Operation::StackValue), constants::DW_OP_implicit_pointer | constants::DW_OP_GNU_implicit_pointer => { let value = if encoding.version == 2 { bytes .read_address(encoding.address_size) .and_then(Offset::from_u64)? } else { bytes.read_offset(encoding.format)? }; let byte_offset = bytes.read_sleb128()?; Ok(Operation::ImplicitPointer { value: DebugInfoOffset(value), byte_offset, }) } constants::DW_OP_addrx | constants::DW_OP_GNU_addr_index => { let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; Ok(Operation::AddressIndex { index: DebugAddrIndex(index), }) } constants::DW_OP_constx | constants::DW_OP_GNU_const_index => { let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; Ok(Operation::ConstantIndex { index: DebugAddrIndex(index), }) } constants::DW_OP_entry_value | constants::DW_OP_GNU_entry_value => { let len = bytes.read_uleb128().and_then(R::Offset::from_u64)?; let expression = bytes.split(len)?; Ok(Operation::EntryValue { expression }) } constants::DW_OP_GNU_parameter_ref => { let value = bytes.read_u32().map(R::Offset::from_u32)?; Ok(Operation::ParameterRef { offset: UnitOffset(value), }) } constants::DW_OP_const_type | constants::DW_OP_GNU_const_type => { let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; let len = bytes.read_u8()?; let value = bytes.split(R::Offset::from_u8(len))?; Ok(Operation::TypedLiteral { base_type: UnitOffset(base_type), value, }) } constants::DW_OP_regval_type | constants::DW_OP_GNU_regval_type => { let register = bytes.read_uleb128().and_then(Register::from_u64)?; let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; Ok(Operation::RegisterOffset { register, offset: 0, base_type: UnitOffset(base_type), }) } constants::DW_OP_deref_type | constants::DW_OP_GNU_deref_type => { let size = bytes.read_u8()?; let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; Ok(Operation::Deref { base_type: UnitOffset(base_type), size, space: false, }) } constants::DW_OP_xderef_type => { let size = bytes.read_u8()?; let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; Ok(Operation::Deref { base_type: UnitOffset(base_type), size, space: true, }) } constants::DW_OP_convert | constants::DW_OP_GNU_convert => { let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; Ok(Operation::Convert { base_type: UnitOffset(base_type), }) } constants::DW_OP_reinterpret | constants::DW_OP_GNU_reinterpret => { let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; Ok(Operation::Reinterpret { base_type: UnitOffset(base_type), }) } constants::DW_OP_WASM_location => match bytes.read_u8()? { 0x0 => { let index = bytes.read_uleb128_u32()?; Ok(Operation::WasmLocal { index }) } 0x1 => { let index = bytes.read_uleb128_u32()?; Ok(Operation::WasmGlobal { index }) } 0x2 => { let index = bytes.read_uleb128_u32()?; Ok(Operation::WasmStack { index }) } 0x3 => { let index = bytes.read_u32()?; Ok(Operation::WasmGlobal { index }) } _ => Err(Error::InvalidExpression(name)), }, _ => Err(Error::InvalidExpression(name)), } } } #[derive(Debug)] enum EvaluationState { Start(Option), Ready, Error(Error), Complete, Waiting(EvaluationWaiting), } #[derive(Debug)] enum EvaluationWaiting { Memory, Register { offset: i64 }, FrameBase { offset: i64 }, Tls, Cfa, AtLocation, EntryValue, ParameterRef, RelocatedAddress, IndexedAddress, TypedLiteral { value: R }, Convert, Reinterpret, } /// The state of an `Evaluation` after evaluating a DWARF expression. /// The evaluation is either `Complete`, or it requires more data /// to continue, as described by the variant. #[derive(Debug, PartialEq)] pub enum EvaluationResult { /// The `Evaluation` is complete, and `Evaluation::result()` can be called. Complete, /// The `Evaluation` needs a value from memory to proceed further. Once the /// caller determines what value to provide it should resume the `Evaluation` /// by calling `Evaluation::resume_with_memory`. RequiresMemory { /// The address of the value required. address: u64, /// The size of the value required. This is guaranteed to be at most the /// word size of the target architecture. size: u8, /// If not `None`, a target-specific address space value. space: Option, /// The DIE of the base type or 0 to indicate the generic type base_type: UnitOffset, }, /// The `Evaluation` needs a value from a register to proceed further. Once /// the caller determines what value to provide it should resume the /// `Evaluation` by calling `Evaluation::resume_with_register`. RequiresRegister { /// The register number. register: Register, /// The DIE of the base type or 0 to indicate the generic type base_type: UnitOffset, }, /// The `Evaluation` needs the frame base address to proceed further. Once /// the caller determines what value to provide it should resume the /// `Evaluation` by calling `Evaluation::resume_with_frame_base`. The frame /// base address is the address produced by the location description in the /// `DW_AT_frame_base` attribute of the current function. RequiresFrameBase, /// The `Evaluation` needs a value from TLS to proceed further. Once the /// caller determines what value to provide it should resume the /// `Evaluation` by calling `Evaluation::resume_with_tls`. RequiresTls(u64), /// The `Evaluation` needs the CFA to proceed further. Once the caller /// determines what value to provide it should resume the `Evaluation` by /// calling `Evaluation::resume_with_call_frame_cfa`. RequiresCallFrameCfa, /// The `Evaluation` needs the DWARF expression at the given location to /// proceed further. Once the caller determines what value to provide it /// should resume the `Evaluation` by calling /// `Evaluation::resume_with_at_location`. RequiresAtLocation(DieReference), /// The `Evaluation` needs the value produced by evaluating a DWARF /// expression at the entry point of the current subprogram. Once the /// caller determines what value to provide it should resume the /// `Evaluation` by calling `Evaluation::resume_with_entry_value`. RequiresEntryValue(Expression), /// The `Evaluation` needs the value of the parameter at the given location /// in the current function's caller. Once the caller determines what value /// to provide it should resume the `Evaluation` by calling /// `Evaluation::resume_with_parameter_ref`. RequiresParameterRef(UnitOffset), /// The `Evaluation` needs an address to be relocated to proceed further. /// Once the caller determines what value to provide it should resume the /// `Evaluation` by calling `Evaluation::resume_with_relocated_address`. RequiresRelocatedAddress(u64), /// The `Evaluation` needs an address from the `.debug_addr` section. /// This address may also need to be relocated. /// Once the caller determines what value to provide it should resume the /// `Evaluation` by calling `Evaluation::resume_with_indexed_address`. RequiresIndexedAddress { /// The index of the address in the `.debug_addr` section, /// relative to the `DW_AT_addr_base` of the compilation unit. index: DebugAddrIndex, /// Whether the address also needs to be relocated. relocate: bool, }, /// The `Evaluation` needs the `ValueType` for the base type DIE at /// the give unit offset. Once the caller determines what value to provide it /// should resume the `Evaluation` by calling /// `Evaluation::resume_with_base_type`. RequiresBaseType(UnitOffset), } /// The bytecode for a DWARF expression or location description. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Expression(pub R); impl Expression { /// Create an evaluation for this expression. /// /// The `encoding` is determined by the /// [`CompilationUnitHeader`](struct.CompilationUnitHeader.html) or /// [`TypeUnitHeader`](struct.TypeUnitHeader.html) that this expression /// relates to. /// /// # Examples /// ```rust,no_run /// use gimli::Expression; /// # let endian = gimli::LittleEndian; /// # let debug_info = gimli::DebugInfo::from(gimli::EndianSlice::new(&[], endian)); /// # let unit = debug_info.units().next().unwrap().unwrap(); /// # let bytecode = gimli::EndianSlice::new(&[], endian); /// let expression = gimli::Expression(bytecode); /// let mut eval = expression.evaluation(unit.encoding()); /// let mut result = eval.evaluate().unwrap(); /// ``` #[cfg(feature = "read")] #[inline] pub fn evaluation(self, encoding: Encoding) -> Evaluation { Evaluation::new(self.0, encoding) } /// Return an iterator for the operations in the expression. pub fn operations(self, encoding: Encoding) -> OperationIter { OperationIter { input: self.0, encoding, } } } /// An iterator for the operations in an expression. #[derive(Debug, Clone, Copy)] pub struct OperationIter { input: R, encoding: Encoding, } impl OperationIter { /// Read the next operation in an expression. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { return Ok(None); } match Operation::parse(&mut self.input, self.encoding) { Ok(op) => Ok(Some(op)), Err(e) => { self.input.empty(); Err(e) } } } /// Return the current byte offset of the iterator. pub fn offset_from(&self, expression: &Expression) -> R::Offset { self.input.offset_from(&expression.0) } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for OperationIter { type Item = Operation; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { OperationIter::next(self) } } /// Specification of what storage should be used for [`Evaluation`]. /// #[cfg_attr( feature = "read", doc = " Normally you would only need to use [`StoreOnHeap`], which places the stacks and the results on the heap using [`Vec`]. This is the default storage type parameter for [`Evaluation`]. " )] /// /// If you need to avoid [`Evaluation`] from allocating memory, e.g. for signal safety, /// you can provide you own storage specification: /// ```rust,no_run /// # use gimli::*; /// # let bytecode = EndianSlice::new(&[], LittleEndian); /// # let encoding = unimplemented!(); /// # let get_register_value = |_, _| Value::Generic(42); /// # let get_frame_base = || 0xdeadbeef; /// # /// struct StoreOnStack; /// /// impl EvaluationStorage for StoreOnStack { /// type Stack = [Value; 64]; /// type ExpressionStack = [(R, R); 4]; /// type Result = [Piece; 1]; /// } /// /// let mut eval = Evaluation::<_, StoreOnStack>::new_in(bytecode, encoding); /// let mut result = eval.evaluate().unwrap(); /// while result != EvaluationResult::Complete { /// match result { /// EvaluationResult::RequiresRegister { register, base_type } => { /// let value = get_register_value(register, base_type); /// result = eval.resume_with_register(value).unwrap(); /// }, /// EvaluationResult::RequiresFrameBase => { /// let frame_base = get_frame_base(); /// result = eval.resume_with_frame_base(frame_base).unwrap(); /// }, /// _ => unimplemented!(), /// }; /// } /// /// let result = eval.as_result(); /// println!("{:?}", result); /// ``` pub trait EvaluationStorage { /// The storage used for the evaluation stack. type Stack: ArrayLike; /// The storage used for the expression stack. type ExpressionStack: ArrayLike; /// The storage used for the results. type Result: ArrayLike>; } #[cfg(feature = "read")] impl EvaluationStorage for StoreOnHeap { type Stack = Vec; type ExpressionStack = Vec<(R, R)>; type Result = Vec>; } /// A DWARF expression evaluator. /// /// # Usage /// A DWARF expression may require additional data to produce a final result, /// such as the value of a register or a memory location. Once initial setup /// is complete (i.e. `set_initial_value()`, `set_object_address()`) the /// consumer calls the `evaluate()` method. That returns an `EvaluationResult`, /// which is either `EvaluationResult::Complete` or a value indicating what /// data is needed to resume the `Evaluation`. The consumer is responsible for /// producing that data and resuming the computation with the correct method, /// as documented for `EvaluationResult`. Only once an `EvaluationResult::Complete` /// is returned can the consumer call `result()`. /// /// This design allows the consumer of `Evaluation` to decide how and when to /// produce the required data and resume the computation. The `Evaluation` can /// be driven synchronously (as shown below) or by some asynchronous mechanism /// such as futures. /// /// # Examples /// ```rust,no_run /// use gimli::{Evaluation, EvaluationResult, Expression}; /// # let bytecode = gimli::EndianSlice::new(&[], gimli::LittleEndian); /// # let encoding = unimplemented!(); /// # let get_register_value = |_, _| gimli::Value::Generic(42); /// # let get_frame_base = || 0xdeadbeef; /// /// let mut eval = Evaluation::new(bytecode, encoding); /// let mut result = eval.evaluate().unwrap(); /// while result != EvaluationResult::Complete { /// match result { /// EvaluationResult::RequiresRegister { register, base_type } => { /// let value = get_register_value(register, base_type); /// result = eval.resume_with_register(value).unwrap(); /// }, /// EvaluationResult::RequiresFrameBase => { /// let frame_base = get_frame_base(); /// result = eval.resume_with_frame_base(frame_base).unwrap(); /// }, /// _ => unimplemented!(), /// }; /// } /// /// let result = eval.result(); /// println!("{:?}", result); /// ``` #[derive(Debug)] pub struct Evaluation = StoreOnHeap> { bytecode: R, encoding: Encoding, object_address: Option, max_iterations: Option, iteration: u32, state: EvaluationState, // Stack operations are done on word-sized values. We do all // operations on 64-bit values, and then mask the results // appropriately when popping. addr_mask: u64, // The stack. stack: ArrayVec, // The next operation to decode and evaluate. pc: R, // If we see a DW_OP_call* operation, the previous PC and bytecode // is stored here while evaluating the subroutine. expression_stack: ArrayVec, value_result: Option, result: ArrayVec, } #[cfg(feature = "read")] impl Evaluation { /// Create a new DWARF expression evaluator. /// /// The new evaluator is created without an initial value, without /// an object address, and without a maximum number of iterations. pub fn new(bytecode: R, encoding: Encoding) -> Self { Self::new_in(bytecode, encoding) } /// Get the result of this `Evaluation`. /// /// # Panics /// Panics if this `Evaluation` has not been driven to completion. pub fn result(self) -> Vec> { match self.state { EvaluationState::Complete => self.result.into_vec(), _ => { panic!("Called `Evaluation::result` on an `Evaluation` that has not been completed") } } } } impl> Evaluation { /// Create a new DWARF expression evaluator. /// /// The new evaluator is created without an initial value, without /// an object address, and without a maximum number of iterations. pub fn new_in(bytecode: R, encoding: Encoding) -> Self { let pc = bytecode.clone(); Evaluation { bytecode, encoding, object_address: None, max_iterations: None, iteration: 0, state: EvaluationState::Start(None), addr_mask: if encoding.address_size == 8 { !0u64 } else { (1 << (8 * u64::from(encoding.address_size))) - 1 }, stack: Default::default(), expression_stack: Default::default(), pc, value_result: None, result: Default::default(), } } /// Set an initial value to be pushed on the DWARF expression /// evaluator's stack. This can be used in cases like /// `DW_AT_vtable_elem_location`, which require a value on the /// stack before evaluation commences. If no initial value is /// set, and the expression uses an opcode requiring the initial /// value, then evaluation will fail with an error. /// /// # Panics /// Panics if `set_initial_value()` has already been called, or if /// `evaluate()` has already been called. pub fn set_initial_value(&mut self, value: u64) { match self.state { EvaluationState::Start(None) => { self.state = EvaluationState::Start(Some(value)); } _ => panic!( "`Evaluation::set_initial_value` was called twice, or after evaluation began." ), }; } /// Set the enclosing object's address, as used by /// `DW_OP_push_object_address`. If no object address is set, and /// the expression uses an opcode requiring the object address, /// then evaluation will fail with an error. pub fn set_object_address(&mut self, value: u64) { self.object_address = Some(value); } /// Set the maximum number of iterations to be allowed by the /// expression evaluator. /// /// An iteration corresponds approximately to the evaluation of a /// single operation in an expression ("approximately" because the /// implementation may allow two such operations in some cases). /// The default is not to have a maximum; once set, it's not /// possible to go back to this default state. This value can be /// set to avoid denial of service attacks by bad DWARF bytecode. pub fn set_max_iterations(&mut self, value: u32) { self.max_iterations = Some(value); } fn pop(&mut self) -> Result { match self.stack.pop() { Some(value) => Ok(value), None => Err(Error::NotEnoughStackItems), } } fn push(&mut self, value: Value) -> Result<()> { self.stack.try_push(value).map_err(|_| Error::StackFull) } fn evaluate_one_operation(&mut self) -> Result> { let operation = Operation::parse(&mut self.pc, self.encoding)?; match operation { Operation::Deref { base_type, size, space, } => { if size > self.encoding.address_size { return Err(Error::InvalidDerefSize(size)); } let entry = self.pop()?; let addr = entry.to_u64(self.addr_mask)?; let addr_space = if space { let entry = self.pop()?; let value = entry.to_u64(self.addr_mask)?; Some(value) } else { None }; return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::Memory, EvaluationResult::RequiresMemory { address: addr, size, space: addr_space, base_type, }, )); } Operation::Drop => { self.pop()?; } Operation::Pick { index } => { let len = self.stack.len(); let index = index as usize; if index >= len { return Err(Error::NotEnoughStackItems); } let value = self.stack[len - index - 1]; self.push(value)?; } Operation::Swap => { let top = self.pop()?; let next = self.pop()?; self.push(top)?; self.push(next)?; } Operation::Rot => { let one = self.pop()?; let two = self.pop()?; let three = self.pop()?; self.push(one)?; self.push(three)?; self.push(two)?; } Operation::Abs => { let value = self.pop()?; let result = value.abs(self.addr_mask)?; self.push(result)?; } Operation::And => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.and(rhs, self.addr_mask)?; self.push(result)?; } Operation::Div => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.div(rhs, self.addr_mask)?; self.push(result)?; } Operation::Minus => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.sub(rhs, self.addr_mask)?; self.push(result)?; } Operation::Mod => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.rem(rhs, self.addr_mask)?; self.push(result)?; } Operation::Mul => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.mul(rhs, self.addr_mask)?; self.push(result)?; } Operation::Neg => { let v = self.pop()?; let result = v.neg(self.addr_mask)?; self.push(result)?; } Operation::Not => { let value = self.pop()?; let result = value.not(self.addr_mask)?; self.push(result)?; } Operation::Or => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.or(rhs, self.addr_mask)?; self.push(result)?; } Operation::Plus => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.add(rhs, self.addr_mask)?; self.push(result)?; } Operation::PlusConstant { value } => { let lhs = self.pop()?; let rhs = Value::from_u64(lhs.value_type(), value)?; let result = lhs.add(rhs, self.addr_mask)?; self.push(result)?; } Operation::Shl => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.shl(rhs, self.addr_mask)?; self.push(result)?; } Operation::Shr => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.shr(rhs, self.addr_mask)?; self.push(result)?; } Operation::Shra => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.shra(rhs, self.addr_mask)?; self.push(result)?; } Operation::Xor => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.xor(rhs, self.addr_mask)?; self.push(result)?; } Operation::Bra { target } => { let entry = self.pop()?; let v = entry.to_u64(self.addr_mask)?; if v != 0 { self.pc = compute_pc(&self.pc, &self.bytecode, target)?; } } Operation::Eq => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.eq(rhs, self.addr_mask)?; self.push(result)?; } Operation::Ge => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.ge(rhs, self.addr_mask)?; self.push(result)?; } Operation::Gt => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.gt(rhs, self.addr_mask)?; self.push(result)?; } Operation::Le => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.le(rhs, self.addr_mask)?; self.push(result)?; } Operation::Lt => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.lt(rhs, self.addr_mask)?; self.push(result)?; } Operation::Ne => { let rhs = self.pop()?; let lhs = self.pop()?; let result = lhs.ne(rhs, self.addr_mask)?; self.push(result)?; } Operation::Skip { target } => { self.pc = compute_pc(&self.pc, &self.bytecode, target)?; } Operation::UnsignedConstant { value } => { self.push(Value::Generic(value))?; } Operation::SignedConstant { value } => { self.push(Value::Generic(value as u64))?; } Operation::RegisterOffset { register, offset, base_type, } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::Register { offset }, EvaluationResult::RequiresRegister { register, base_type, }, )); } Operation::FrameOffset { offset } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::FrameBase { offset }, EvaluationResult::RequiresFrameBase, )); } Operation::Nop => {} Operation::PushObjectAddress => { if let Some(value) = self.object_address { self.push(Value::Generic(value))?; } else { return Err(Error::InvalidPushObjectAddress); } } Operation::Call { offset } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::AtLocation, EvaluationResult::RequiresAtLocation(offset), )); } Operation::TLS => { let entry = self.pop()?; let index = entry.to_u64(self.addr_mask)?; return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::Tls, EvaluationResult::RequiresTls(index), )); } Operation::CallFrameCFA => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::Cfa, EvaluationResult::RequiresCallFrameCfa, )); } Operation::Register { register } => { let location = Location::Register { register }; return Ok(OperationEvaluationResult::Complete { location }); } Operation::ImplicitValue { ref data } => { let location = Location::Bytes { value: data.clone(), }; return Ok(OperationEvaluationResult::Complete { location }); } Operation::StackValue => { let value = self.pop()?; let location = Location::Value { value }; return Ok(OperationEvaluationResult::Complete { location }); } Operation::ImplicitPointer { value, byte_offset } => { let location = Location::ImplicitPointer { value, byte_offset }; return Ok(OperationEvaluationResult::Complete { location }); } Operation::EntryValue { ref expression } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::EntryValue, EvaluationResult::RequiresEntryValue(Expression(expression.clone())), )); } Operation::ParameterRef { offset } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::ParameterRef, EvaluationResult::RequiresParameterRef(offset), )); } Operation::Address { address } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::RelocatedAddress, EvaluationResult::RequiresRelocatedAddress(address), )); } Operation::AddressIndex { index } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::IndexedAddress, EvaluationResult::RequiresIndexedAddress { index, relocate: true, }, )); } Operation::ConstantIndex { index } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::IndexedAddress, EvaluationResult::RequiresIndexedAddress { index, relocate: false, }, )); } Operation::Piece { size_in_bits, bit_offset, } => { let location = if self.stack.is_empty() { Location::Empty } else { let entry = self.pop()?; let address = entry.to_u64(self.addr_mask)?; Location::Address { address } }; self.result .try_push(Piece { size_in_bits: Some(size_in_bits), bit_offset, location, }) .map_err(|_| Error::StackFull)?; return Ok(OperationEvaluationResult::Piece); } Operation::TypedLiteral { base_type, value } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::TypedLiteral { value }, EvaluationResult::RequiresBaseType(base_type), )); } Operation::Convert { base_type } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::Convert, EvaluationResult::RequiresBaseType(base_type), )); } Operation::Reinterpret { base_type } => { return Ok(OperationEvaluationResult::Waiting( EvaluationWaiting::Reinterpret, EvaluationResult::RequiresBaseType(base_type), )); } Operation::WasmLocal { .. } | Operation::WasmGlobal { .. } | Operation::WasmStack { .. } => { return Err(Error::UnsupportedEvaluation); } } Ok(OperationEvaluationResult::Incomplete) } /// Get the result if this is an evaluation for a value. /// /// Returns `None` if the evaluation contained operations that are only /// valid for location descriptions. /// /// # Panics /// Panics if this `Evaluation` has not been driven to completion. pub fn value_result(&self) -> Option { match self.state { EvaluationState::Complete => self.value_result, _ => { panic!("Called `Evaluation::value_result` on an `Evaluation` that has not been completed") } } } /// Get the result of this `Evaluation`. /// /// # Panics /// Panics if this `Evaluation` has not been driven to completion. pub fn as_result(&self) -> &[Piece] { match self.state { EvaluationState::Complete => &self.result, _ => { panic!( "Called `Evaluation::as_result` on an `Evaluation` that has not been completed" ) } } } /// Evaluate a DWARF expression. This method should only ever be called /// once. If the returned `EvaluationResult` is not /// `EvaluationResult::Complete`, the caller should provide the required /// value and resume the evaluation by calling the appropriate resume_with /// method on `Evaluation`. pub fn evaluate(&mut self) -> Result> { match self.state { EvaluationState::Start(initial_value) => { if let Some(value) = initial_value { self.push(Value::Generic(value))?; } self.state = EvaluationState::Ready; } EvaluationState::Ready => {} EvaluationState::Error(err) => return Err(err), EvaluationState::Complete => return Ok(EvaluationResult::Complete), EvaluationState::Waiting(_) => panic!(), }; match self.evaluate_internal() { Ok(r) => Ok(r), Err(e) => { self.state = EvaluationState::Error(e); Err(e) } } } /// Resume the `Evaluation` with the provided memory `value`. This will apply /// the provided memory value to the evaluation and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresMemory`. pub fn resume_with_memory(&mut self, value: Value) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::Memory) => { self.push(value)?; } _ => panic!( "Called `Evaluation::resume_with_memory` without a preceding `EvaluationResult::RequiresMemory`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `register` value. This will apply /// the provided register value to the evaluation and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresRegister`. pub fn resume_with_register(&mut self, value: Value) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::Register { offset }) => { let offset = Value::from_u64(value.value_type(), offset as u64)?; let value = value.add(offset, self.addr_mask)?; self.push(value)?; } _ => panic!( "Called `Evaluation::resume_with_register` without a preceding `EvaluationResult::RequiresRegister`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `frame_base`. This will /// apply the provided frame base value to the evaluation and continue /// evaluating opcodes until the evaluation is completed, reaches an error, /// or needs more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresFrameBase`. pub fn resume_with_frame_base(&mut self, frame_base: u64) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::FrameBase { offset }) => { self.push(Value::Generic(frame_base.wrapping_add(offset as u64)))?; } _ => panic!( "Called `Evaluation::resume_with_frame_base` without a preceding `EvaluationResult::RequiresFrameBase`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `value`. This will apply /// the provided TLS value to the evaluation and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresTls`. pub fn resume_with_tls(&mut self, value: u64) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::Tls) => { self.push(Value::Generic(value))?; } _ => panic!( "Called `Evaluation::resume_with_tls` without a preceding `EvaluationResult::RequiresTls`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `cfa`. This will /// apply the provided CFA value to the evaluation and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresCallFrameCfa`. pub fn resume_with_call_frame_cfa(&mut self, cfa: u64) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::Cfa) => { self.push(Value::Generic(cfa))?; } _ => panic!( "Called `Evaluation::resume_with_call_frame_cfa` without a preceding `EvaluationResult::RequiresCallFrameCfa`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `bytes`. This will /// continue processing the evaluation with the new expression provided /// until the evaluation is completed, reaches an error, or needs more /// information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresAtLocation`. pub fn resume_with_at_location(&mut self, mut bytes: R) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::AtLocation) => { if !bytes.is_empty() { let mut pc = bytes.clone(); mem::swap(&mut pc, &mut self.pc); mem::swap(&mut bytes, &mut self.bytecode); self.expression_stack.try_push((pc, bytes)).map_err(|_| Error::StackFull)?; } } _ => panic!( "Called `Evaluation::resume_with_at_location` without a precedeing `EvaluationResult::RequiresAtLocation`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `entry_value`. This will /// apply the provided entry value to the evaluation and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresEntryValue`. pub fn resume_with_entry_value(&mut self, entry_value: Value) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::EntryValue) => { self.push(entry_value)?; } _ => panic!( "Called `Evaluation::resume_with_entry_value` without a preceding `EvaluationResult::RequiresEntryValue`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `parameter_value`. This will /// apply the provided parameter value to the evaluation and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresParameterRef`. pub fn resume_with_parameter_ref( &mut self, parameter_value: u64, ) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::ParameterRef) => { self.push(Value::Generic(parameter_value))?; } _ => panic!( "Called `Evaluation::resume_with_parameter_ref` without a preceding `EvaluationResult::RequiresParameterRef`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided relocated `address`. This will use the /// provided relocated address for the operation that required it, and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with /// `EvaluationResult::RequiresRelocatedAddress`. pub fn resume_with_relocated_address(&mut self, address: u64) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::RelocatedAddress) => { self.push(Value::Generic(address))?; } _ => panic!( "Called `Evaluation::resume_with_relocated_address` without a preceding `EvaluationResult::RequiresRelocatedAddress`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided indexed `address`. This will use the /// provided indexed address for the operation that required it, and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with /// `EvaluationResult::RequiresIndexedAddress`. pub fn resume_with_indexed_address(&mut self, address: u64) -> Result> { match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::IndexedAddress) => { self.push(Value::Generic(address))?; } _ => panic!( "Called `Evaluation::resume_with_indexed_address` without a preceding `EvaluationResult::RequiresIndexedAddress`" ), }; self.evaluate_internal() } /// Resume the `Evaluation` with the provided `base_type`. This will use the /// provided base type for the operation that required it, and continue evaluating /// opcodes until the evaluation is completed, reaches an error, or needs /// more information again. /// /// # Panics /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresBaseType`. pub fn resume_with_base_type(&mut self, base_type: ValueType) -> Result> { let value = match self.state { EvaluationState::Error(err) => return Err(err), EvaluationState::Waiting(EvaluationWaiting::TypedLiteral { ref value }) => { Value::parse(base_type, value.clone())? } EvaluationState::Waiting(EvaluationWaiting::Convert) => { let entry = self.pop()?; entry.convert(base_type, self.addr_mask)? } EvaluationState::Waiting(EvaluationWaiting::Reinterpret) => { let entry = self.pop()?; entry.reinterpret(base_type, self.addr_mask)? } _ => panic!( "Called `Evaluation::resume_with_base_type` without a preceding `EvaluationResult::RequiresBaseType`" ), }; self.push(value)?; self.evaluate_internal() } fn end_of_expression(&mut self) -> bool { while self.pc.is_empty() { match self.expression_stack.pop() { Some((newpc, newbytes)) => { self.pc = newpc; self.bytecode = newbytes; } None => return true, } } false } fn evaluate_internal(&mut self) -> Result> { while !self.end_of_expression() { self.iteration += 1; if let Some(max_iterations) = self.max_iterations { if self.iteration > max_iterations { return Err(Error::TooManyIterations); } } let op_result = self.evaluate_one_operation()?; match op_result { OperationEvaluationResult::Piece => {} OperationEvaluationResult::Incomplete => { if self.end_of_expression() && !self.result.is_empty() { // We saw a piece earlier and then some // unterminated piece. It's not clear this is // well-defined. return Err(Error::InvalidPiece); } } OperationEvaluationResult::Complete { location } => { if self.end_of_expression() { if !self.result.is_empty() { // We saw a piece earlier and then some // unterminated piece. It's not clear this is // well-defined. return Err(Error::InvalidPiece); } self.result .try_push(Piece { size_in_bits: None, bit_offset: None, location, }) .map_err(|_| Error::StackFull)?; } else { // If there are more operations, then the next operation must // be a Piece. match Operation::parse(&mut self.pc, self.encoding)? { Operation::Piece { size_in_bits, bit_offset, } => { self.result .try_push(Piece { size_in_bits: Some(size_in_bits), bit_offset, location, }) .map_err(|_| Error::StackFull)?; } _ => { let value = self.bytecode.len().into_u64() - self.pc.len().into_u64() - 1; return Err(Error::InvalidExpressionTerminator(value)); } } } } OperationEvaluationResult::Waiting(waiting, result) => { self.state = EvaluationState::Waiting(waiting); return Ok(result); } } } // If no pieces have been seen, use the stack top as the // result. if self.result.is_empty() { let entry = self.pop()?; self.value_result = Some(entry); let addr = entry.to_u64(self.addr_mask)?; self.result .try_push(Piece { size_in_bits: None, bit_offset: None, location: Location::Address { address: addr }, }) .map_err(|_| Error::StackFull)?; } self.state = EvaluationState::Complete; Ok(EvaluationResult::Complete) } } #[cfg(test)] // Tests require leb128::write. #[cfg(feature = "write")] mod tests { use super::*; use crate::common::Format; use crate::constants; use crate::endianity::LittleEndian; use crate::leb128; use crate::read::{EndianSlice, Error, Result, UnitOffset}; use crate::test_util::GimliSectionMethods; use test_assembler::{Endian, Section}; fn encoding4() -> Encoding { Encoding { format: Format::Dwarf32, version: 4, address_size: 4, } } fn encoding8() -> Encoding { Encoding { format: Format::Dwarf64, version: 4, address_size: 8, } } #[test] fn test_compute_pc() { // Contents don't matter for this test, just length. let bytes = [0, 1, 2, 3, 4]; let bytecode = &bytes[..]; let ebuf = &EndianSlice::new(bytecode, LittleEndian); assert_eq!(compute_pc(ebuf, ebuf, 0), Ok(*ebuf)); assert_eq!( compute_pc(ebuf, ebuf, -1), Err(Error::BadBranchTarget(usize::MAX as u64)) ); assert_eq!(compute_pc(ebuf, ebuf, 5), Ok(ebuf.range_from(5..))); assert_eq!( compute_pc(&ebuf.range_from(3..), ebuf, -2), Ok(ebuf.range_from(1..)) ); assert_eq!( compute_pc(&ebuf.range_from(2..), ebuf, 2), Ok(ebuf.range_from(4..)) ); } fn check_op_parse_simple<'input>( input: &'input [u8], expect: &Operation>, encoding: Encoding, ) { let buf = EndianSlice::new(input, LittleEndian); let mut pc = buf; let value = Operation::parse(&mut pc, encoding); match value { Ok(val) => { assert_eq!(val, *expect); assert_eq!(pc.len(), 0); } _ => panic!("Unexpected result"), } } fn check_op_parse_eof(input: &[u8], encoding: Encoding) { let buf = EndianSlice::new(input, LittleEndian); let mut pc = buf; match Operation::parse(&mut pc, encoding) { Err(Error::UnexpectedEof(id)) => { assert!(buf.lookup_offset_id(id).is_some()); } _ => panic!("Unexpected result"), } } fn check_op_parse( input: F, expect: &Operation>, encoding: Encoding, ) where F: Fn(Section) -> Section, { let input = input(Section::with_endian(Endian::Little)) .get_contents() .unwrap(); for i in 1..input.len() { check_op_parse_eof(&input[..i], encoding); } check_op_parse_simple(&input, expect, encoding); } #[test] fn test_op_parse_onebyte() { // Doesn't matter for this test. let encoding = encoding4(); // Test all single-byte opcodes. #[rustfmt::skip] let inputs = [ ( constants::DW_OP_deref, Operation::Deref { base_type: generic_type(), size: encoding.address_size, space: false, }, ), (constants::DW_OP_dup, Operation::Pick { index: 0 }), (constants::DW_OP_drop, Operation::Drop), (constants::DW_OP_over, Operation::Pick { index: 1 }), (constants::DW_OP_swap, Operation::Swap), (constants::DW_OP_rot, Operation::Rot), ( constants::DW_OP_xderef, Operation::Deref { base_type: generic_type(), size: encoding.address_size, space: true, }, ), (constants::DW_OP_abs, Operation::Abs), (constants::DW_OP_and, Operation::And), (constants::DW_OP_div, Operation::Div), (constants::DW_OP_minus, Operation::Minus), (constants::DW_OP_mod, Operation::Mod), (constants::DW_OP_mul, Operation::Mul), (constants::DW_OP_neg, Operation::Neg), (constants::DW_OP_not, Operation::Not), (constants::DW_OP_or, Operation::Or), (constants::DW_OP_plus, Operation::Plus), (constants::DW_OP_shl, Operation::Shl), (constants::DW_OP_shr, Operation::Shr), (constants::DW_OP_shra, Operation::Shra), (constants::DW_OP_xor, Operation::Xor), (constants::DW_OP_eq, Operation::Eq), (constants::DW_OP_ge, Operation::Ge), (constants::DW_OP_gt, Operation::Gt), (constants::DW_OP_le, Operation::Le), (constants::DW_OP_lt, Operation::Lt), (constants::DW_OP_ne, Operation::Ne), (constants::DW_OP_lit0, Operation::UnsignedConstant { value: 0 }), (constants::DW_OP_lit1, Operation::UnsignedConstant { value: 1 }), (constants::DW_OP_lit2, Operation::UnsignedConstant { value: 2 }), (constants::DW_OP_lit3, Operation::UnsignedConstant { value: 3 }), (constants::DW_OP_lit4, Operation::UnsignedConstant { value: 4 }), (constants::DW_OP_lit5, Operation::UnsignedConstant { value: 5 }), (constants::DW_OP_lit6, Operation::UnsignedConstant { value: 6 }), (constants::DW_OP_lit7, Operation::UnsignedConstant { value: 7 }), (constants::DW_OP_lit8, Operation::UnsignedConstant { value: 8 }), (constants::DW_OP_lit9, Operation::UnsignedConstant { value: 9 }), (constants::DW_OP_lit10, Operation::UnsignedConstant { value: 10 }), (constants::DW_OP_lit11, Operation::UnsignedConstant { value: 11 }), (constants::DW_OP_lit12, Operation::UnsignedConstant { value: 12 }), (constants::DW_OP_lit13, Operation::UnsignedConstant { value: 13 }), (constants::DW_OP_lit14, Operation::UnsignedConstant { value: 14 }), (constants::DW_OP_lit15, Operation::UnsignedConstant { value: 15 }), (constants::DW_OP_lit16, Operation::UnsignedConstant { value: 16 }), (constants::DW_OP_lit17, Operation::UnsignedConstant { value: 17 }), (constants::DW_OP_lit18, Operation::UnsignedConstant { value: 18 }), (constants::DW_OP_lit19, Operation::UnsignedConstant { value: 19 }), (constants::DW_OP_lit20, Operation::UnsignedConstant { value: 20 }), (constants::DW_OP_lit21, Operation::UnsignedConstant { value: 21 }), (constants::DW_OP_lit22, Operation::UnsignedConstant { value: 22 }), (constants::DW_OP_lit23, Operation::UnsignedConstant { value: 23 }), (constants::DW_OP_lit24, Operation::UnsignedConstant { value: 24 }), (constants::DW_OP_lit25, Operation::UnsignedConstant { value: 25 }), (constants::DW_OP_lit26, Operation::UnsignedConstant { value: 26 }), (constants::DW_OP_lit27, Operation::UnsignedConstant { value: 27 }), (constants::DW_OP_lit28, Operation::UnsignedConstant { value: 28 }), (constants::DW_OP_lit29, Operation::UnsignedConstant { value: 29 }), (constants::DW_OP_lit30, Operation::UnsignedConstant { value: 30 }), (constants::DW_OP_lit31, Operation::UnsignedConstant { value: 31 }), (constants::DW_OP_reg0, Operation::Register { register: Register(0) }), (constants::DW_OP_reg1, Operation::Register { register: Register(1) }), (constants::DW_OP_reg2, Operation::Register { register: Register(2) }), (constants::DW_OP_reg3, Operation::Register { register: Register(3) }), (constants::DW_OP_reg4, Operation::Register { register: Register(4) }), (constants::DW_OP_reg5, Operation::Register { register: Register(5) }), (constants::DW_OP_reg6, Operation::Register { register: Register(6) }), (constants::DW_OP_reg7, Operation::Register { register: Register(7) }), (constants::DW_OP_reg8, Operation::Register { register: Register(8) }), (constants::DW_OP_reg9, Operation::Register { register: Register(9) }), (constants::DW_OP_reg10, Operation::Register { register: Register(10) }), (constants::DW_OP_reg11, Operation::Register { register: Register(11) }), (constants::DW_OP_reg12, Operation::Register { register: Register(12) }), (constants::DW_OP_reg13, Operation::Register { register: Register(13) }), (constants::DW_OP_reg14, Operation::Register { register: Register(14) }), (constants::DW_OP_reg15, Operation::Register { register: Register(15) }), (constants::DW_OP_reg16, Operation::Register { register: Register(16) }), (constants::DW_OP_reg17, Operation::Register { register: Register(17) }), (constants::DW_OP_reg18, Operation::Register { register: Register(18) }), (constants::DW_OP_reg19, Operation::Register { register: Register(19) }), (constants::DW_OP_reg20, Operation::Register { register: Register(20) }), (constants::DW_OP_reg21, Operation::Register { register: Register(21) }), (constants::DW_OP_reg22, Operation::Register { register: Register(22) }), (constants::DW_OP_reg23, Operation::Register { register: Register(23) }), (constants::DW_OP_reg24, Operation::Register { register: Register(24) }), (constants::DW_OP_reg25, Operation::Register { register: Register(25) }), (constants::DW_OP_reg26, Operation::Register { register: Register(26) }), (constants::DW_OP_reg27, Operation::Register { register: Register(27) }), (constants::DW_OP_reg28, Operation::Register { register: Register(28) }), (constants::DW_OP_reg29, Operation::Register { register: Register(29) }), (constants::DW_OP_reg30, Operation::Register { register: Register(30) }), (constants::DW_OP_reg31, Operation::Register { register: Register(31) }), (constants::DW_OP_nop, Operation::Nop), (constants::DW_OP_push_object_address, Operation::PushObjectAddress), (constants::DW_OP_form_tls_address, Operation::TLS), (constants::DW_OP_GNU_push_tls_address, Operation::TLS), (constants::DW_OP_call_frame_cfa, Operation::CallFrameCFA), (constants::DW_OP_stack_value, Operation::StackValue), ]; let input = []; check_op_parse_eof(&input[..], encoding); for item in inputs.iter() { let (opcode, ref result) = *item; check_op_parse(|s| s.D8(opcode.0), result, encoding); } } #[test] fn test_op_parse_twobyte() { // Doesn't matter for this test. let encoding = encoding4(); let inputs = [ ( constants::DW_OP_const1u, 23, Operation::UnsignedConstant { value: 23 }, ), ( constants::DW_OP_const1s, (-23i8) as u8, Operation::SignedConstant { value: -23 }, ), (constants::DW_OP_pick, 7, Operation::Pick { index: 7 }), ( constants::DW_OP_deref_size, 19, Operation::Deref { base_type: generic_type(), size: 19, space: false, }, ), ( constants::DW_OP_xderef_size, 19, Operation::Deref { base_type: generic_type(), size: 19, space: true, }, ), ]; for item in inputs.iter() { let (opcode, arg, ref result) = *item; check_op_parse(|s| s.D8(opcode.0).D8(arg), result, encoding); } } #[test] fn test_op_parse_threebyte() { // Doesn't matter for this test. let encoding = encoding4(); // While bra and skip are 3-byte opcodes, they aren't tested here, // but rather specially in their own function. let inputs = [ ( constants::DW_OP_const2u, 23, Operation::UnsignedConstant { value: 23 }, ), ( constants::DW_OP_const2s, (-23i16) as u16, Operation::SignedConstant { value: -23 }, ), ( constants::DW_OP_call2, 1138, Operation::Call { offset: DieReference::UnitRef(UnitOffset(1138)), }, ), ( constants::DW_OP_bra, (-23i16) as u16, Operation::Bra { target: -23 }, ), ( constants::DW_OP_skip, (-23i16) as u16, Operation::Skip { target: -23 }, ), ]; for item in inputs.iter() { let (opcode, arg, ref result) = *item; check_op_parse(|s| s.D8(opcode.0).L16(arg), result, encoding); } } #[test] fn test_op_parse_fivebyte() { // There are some tests here that depend on address size. let encoding = encoding4(); let inputs = [ ( constants::DW_OP_addr, 0x1234_5678, Operation::Address { address: 0x1234_5678, }, ), ( constants::DW_OP_const4u, 0x1234_5678, Operation::UnsignedConstant { value: 0x1234_5678 }, ), ( constants::DW_OP_const4s, (-23i32) as u32, Operation::SignedConstant { value: -23 }, ), ( constants::DW_OP_call4, 0x1234_5678, Operation::Call { offset: DieReference::UnitRef(UnitOffset(0x1234_5678)), }, ), ( constants::DW_OP_call_ref, 0x1234_5678, Operation::Call { offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678)), }, ), ]; for item in inputs.iter() { let (op, arg, ref expect) = *item; check_op_parse(|s| s.D8(op.0).L32(arg), expect, encoding); } } #[test] #[cfg(target_pointer_width = "64")] fn test_op_parse_ninebyte() { // There are some tests here that depend on address size. let encoding = encoding8(); let inputs = [ ( constants::DW_OP_addr, 0x1234_5678_1234_5678, Operation::Address { address: 0x1234_5678_1234_5678, }, ), ( constants::DW_OP_const8u, 0x1234_5678_1234_5678, Operation::UnsignedConstant { value: 0x1234_5678_1234_5678, }, ), ( constants::DW_OP_const8s, (-23i64) as u64, Operation::SignedConstant { value: -23 }, ), ( constants::DW_OP_call_ref, 0x1234_5678_1234_5678, Operation::Call { offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678_1234_5678)), }, ), ]; for item in inputs.iter() { let (op, arg, ref expect) = *item; check_op_parse(|s| s.D8(op.0).L64(arg), expect, encoding); } } #[test] fn test_op_parse_sleb() { // Doesn't matter for this test. let encoding = encoding4(); let values = [ -1i64, 0, 1, 0x100, 0x1eee_eeee, 0x7fff_ffff_ffff_ffff, -0x100, -0x1eee_eeee, -0x7fff_ffff_ffff_ffff, ]; for value in values.iter() { let mut inputs = vec![ ( constants::DW_OP_consts.0, Operation::SignedConstant { value: *value }, ), ( constants::DW_OP_fbreg.0, Operation::FrameOffset { offset: *value }, ), ]; for i in 0..32 { inputs.push(( constants::DW_OP_breg0.0 + i, Operation::RegisterOffset { register: Register(i.into()), offset: *value, base_type: UnitOffset(0), }, )); } for item in inputs.iter() { let (op, ref expect) = *item; check_op_parse(|s| s.D8(op).sleb(*value), expect, encoding); } } } #[test] fn test_op_parse_uleb() { // Doesn't matter for this test. let encoding = encoding4(); let values = [ 0, 1, 0x100, (!0u16).into(), 0x1eee_eeee, 0x7fff_ffff_ffff_ffff, !0u64, ]; for value in values.iter() { let mut inputs = vec![ ( constants::DW_OP_constu, Operation::UnsignedConstant { value: *value }, ), ( constants::DW_OP_plus_uconst, Operation::PlusConstant { value: *value }, ), ]; if *value <= (!0u16).into() { inputs.push(( constants::DW_OP_regx, Operation::Register { register: Register::from_u64(*value).unwrap(), }, )); } if *value <= (!0u32).into() { inputs.extend(&[ ( constants::DW_OP_addrx, Operation::AddressIndex { index: DebugAddrIndex(*value as usize), }, ), ( constants::DW_OP_constx, Operation::ConstantIndex { index: DebugAddrIndex(*value as usize), }, ), ]); } // FIXME if *value < !0u64 / 8 { inputs.push(( constants::DW_OP_piece, Operation::Piece { size_in_bits: 8 * value, bit_offset: None, }, )); } for item in inputs.iter() { let (op, ref expect) = *item; let input = Section::with_endian(Endian::Little) .D8(op.0) .uleb(*value) .get_contents() .unwrap(); check_op_parse_simple(&input, expect, encoding); } } } #[test] fn test_op_parse_bregx() { // Doesn't matter for this test. let encoding = encoding4(); let uvalues = [0, 1, 0x100, !0u16]; let svalues = [ -1i64, 0, 1, 0x100, 0x1eee_eeee, 0x7fff_ffff_ffff_ffff, -0x100, -0x1eee_eeee, -0x7fff_ffff_ffff_ffff, ]; for v1 in uvalues.iter() { for v2 in svalues.iter() { check_op_parse( |s| s.D8(constants::DW_OP_bregx.0).uleb((*v1).into()).sleb(*v2), &Operation::RegisterOffset { register: Register(*v1), offset: *v2, base_type: UnitOffset(0), }, encoding, ); } } } #[test] fn test_op_parse_bit_piece() { // Doesn't matter for this test. let encoding = encoding4(); let values = [0, 1, 0x100, 0x1eee_eeee, 0x7fff_ffff_ffff_ffff, !0u64]; for v1 in values.iter() { for v2 in values.iter() { let input = Section::with_endian(Endian::Little) .D8(constants::DW_OP_bit_piece.0) .uleb(*v1) .uleb(*v2) .get_contents() .unwrap(); check_op_parse_simple( &input, &Operation::Piece { size_in_bits: *v1, bit_offset: Some(*v2), }, encoding, ); } } } #[test] fn test_op_parse_implicit_value() { // Doesn't matter for this test. let encoding = encoding4(); let data = b"hello"; check_op_parse( |s| { s.D8(constants::DW_OP_implicit_value.0) .uleb(data.len() as u64) .append_bytes(&data[..]) }, &Operation::ImplicitValue { data: EndianSlice::new(&data[..], LittleEndian), }, encoding, ); } #[test] fn test_op_parse_const_type() { // Doesn't matter for this test. let encoding = encoding4(); let data = b"hello"; check_op_parse( |s| { s.D8(constants::DW_OP_const_type.0) .uleb(100) .D8(data.len() as u8) .append_bytes(&data[..]) }, &Operation::TypedLiteral { base_type: UnitOffset(100), value: EndianSlice::new(&data[..], LittleEndian), }, encoding, ); check_op_parse( |s| { s.D8(constants::DW_OP_GNU_const_type.0) .uleb(100) .D8(data.len() as u8) .append_bytes(&data[..]) }, &Operation::TypedLiteral { base_type: UnitOffset(100), value: EndianSlice::new(&data[..], LittleEndian), }, encoding, ); } #[test] fn test_op_parse_regval_type() { // Doesn't matter for this test. let encoding = encoding4(); check_op_parse( |s| s.D8(constants::DW_OP_regval_type.0).uleb(1).uleb(100), &Operation::RegisterOffset { register: Register(1), offset: 0, base_type: UnitOffset(100), }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_GNU_regval_type.0).uleb(1).uleb(100), &Operation::RegisterOffset { register: Register(1), offset: 0, base_type: UnitOffset(100), }, encoding, ); } #[test] fn test_op_parse_deref_type() { // Doesn't matter for this test. let encoding = encoding4(); check_op_parse( |s| s.D8(constants::DW_OP_deref_type.0).D8(8).uleb(100), &Operation::Deref { base_type: UnitOffset(100), size: 8, space: false, }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_GNU_deref_type.0).D8(8).uleb(100), &Operation::Deref { base_type: UnitOffset(100), size: 8, space: false, }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_xderef_type.0).D8(8).uleb(100), &Operation::Deref { base_type: UnitOffset(100), size: 8, space: true, }, encoding, ); } #[test] fn test_op_convert() { // Doesn't matter for this test. let encoding = encoding4(); check_op_parse( |s| s.D8(constants::DW_OP_convert.0).uleb(100), &Operation::Convert { base_type: UnitOffset(100), }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_GNU_convert.0).uleb(100), &Operation::Convert { base_type: UnitOffset(100), }, encoding, ); } #[test] fn test_op_reinterpret() { // Doesn't matter for this test. let encoding = encoding4(); check_op_parse( |s| s.D8(constants::DW_OP_reinterpret.0).uleb(100), &Operation::Reinterpret { base_type: UnitOffset(100), }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_GNU_reinterpret.0).uleb(100), &Operation::Reinterpret { base_type: UnitOffset(100), }, encoding, ); } #[test] fn test_op_parse_implicit_pointer() { for op in &[ constants::DW_OP_implicit_pointer, constants::DW_OP_GNU_implicit_pointer, ] { check_op_parse( |s| s.D8(op.0).D32(0x1234_5678).sleb(0x123), &Operation::ImplicitPointer { value: DebugInfoOffset(0x1234_5678), byte_offset: 0x123, }, encoding4(), ); check_op_parse( |s| s.D8(op.0).D64(0x1234_5678).sleb(0x123), &Operation::ImplicitPointer { value: DebugInfoOffset(0x1234_5678), byte_offset: 0x123, }, encoding8(), ); check_op_parse( |s| s.D8(op.0).D64(0x1234_5678).sleb(0x123), &Operation::ImplicitPointer { value: DebugInfoOffset(0x1234_5678), byte_offset: 0x123, }, Encoding { format: Format::Dwarf32, version: 2, address_size: 8, }, ) } } #[test] fn test_op_parse_entry_value() { for op in &[ constants::DW_OP_entry_value, constants::DW_OP_GNU_entry_value, ] { let data = b"hello"; check_op_parse( |s| s.D8(op.0).uleb(data.len() as u64).append_bytes(&data[..]), &Operation::EntryValue { expression: EndianSlice::new(&data[..], LittleEndian), }, encoding4(), ); } } #[test] fn test_op_parse_gnu_parameter_ref() { check_op_parse( |s| s.D8(constants::DW_OP_GNU_parameter_ref.0).D32(0x1234_5678), &Operation::ParameterRef { offset: UnitOffset(0x1234_5678), }, encoding4(), ) } #[test] fn test_op_wasm() { // Doesn't matter for this test. let encoding = encoding4(); check_op_parse( |s| s.D8(constants::DW_OP_WASM_location.0).D8(0).uleb(1000), &Operation::WasmLocal { index: 1000 }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_WASM_location.0).D8(1).uleb(1000), &Operation::WasmGlobal { index: 1000 }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_WASM_location.0).D8(2).uleb(1000), &Operation::WasmStack { index: 1000 }, encoding, ); check_op_parse( |s| s.D8(constants::DW_OP_WASM_location.0).D8(3).D32(1000), &Operation::WasmGlobal { index: 1000 }, encoding, ); } enum AssemblerEntry { Op(constants::DwOp), Mark(u8), Branch(u8), U8(u8), U16(u16), U32(u32), U64(u64), Uleb(u64), Sleb(u64), } fn assemble(entries: &[AssemblerEntry]) -> Vec { let mut result = Vec::new(); struct Marker(Option, Vec); let mut markers = Vec::new(); for _ in 0..256 { markers.push(Marker(None, Vec::new())); } fn write(stack: &mut [u8], index: usize, mut num: u64, nbytes: u8) { for i in 0..nbytes as usize { stack[index + i] = (num & 0xff) as u8; num >>= 8; } } fn push(stack: &mut Vec, num: u64, nbytes: u8) { let index = stack.len(); for _ in 0..nbytes { stack.push(0); } write(stack, index, num, nbytes); } for item in entries { match *item { AssemblerEntry::Op(op) => result.push(op.0), AssemblerEntry::Mark(num) => { assert!(markers[num as usize].0.is_none()); markers[num as usize].0 = Some(result.len()); } AssemblerEntry::Branch(num) => { markers[num as usize].1.push(result.len()); push(&mut result, 0, 2); } AssemblerEntry::U8(num) => result.push(num), AssemblerEntry::U16(num) => push(&mut result, u64::from(num), 2), AssemblerEntry::U32(num) => push(&mut result, u64::from(num), 4), AssemblerEntry::U64(num) => push(&mut result, num, 8), AssemblerEntry::Uleb(num) => { leb128::write::unsigned(&mut result, num).unwrap(); } AssemblerEntry::Sleb(num) => { leb128::write::signed(&mut result, num as i64).unwrap(); } } } // Update all the branches. for marker in markers { if let Some(offset) = marker.0 { for branch_offset in marker.1 { let delta = offset.wrapping_sub(branch_offset + 2) as u64; write(&mut result, branch_offset, delta, 2); } } } result } fn check_eval_with_args( program: &[AssemblerEntry], expect: Result<&[Piece>]>, encoding: Encoding, object_address: Option, initial_value: Option, max_iterations: Option, f: F, ) where for<'a> F: Fn( &mut Evaluation>, EvaluationResult>, ) -> Result>>, { let bytes = assemble(program); let bytes = EndianSlice::new(&bytes, LittleEndian); let mut eval = Evaluation::new(bytes, encoding); if let Some(val) = object_address { eval.set_object_address(val); } if let Some(val) = initial_value { eval.set_initial_value(val); } if let Some(val) = max_iterations { eval.set_max_iterations(val); } let result = match eval.evaluate() { Err(e) => Err(e), Ok(r) => f(&mut eval, r), }; match (result, expect) { (Ok(EvaluationResult::Complete), Ok(pieces)) => { let vec = eval.result(); assert_eq!(vec.len(), pieces.len()); for i in 0..pieces.len() { assert_eq!(vec[i], pieces[i]); } } (Err(f1), Err(f2)) => { assert_eq!(f1, f2); } otherwise => panic!("Unexpected result: {:?}", otherwise), } } fn check_eval( program: &[AssemblerEntry], expect: Result<&[Piece>]>, encoding: Encoding, ) { check_eval_with_args(program, expect, encoding, None, None, None, |_, result| { Ok(result) }); } #[test] fn test_eval_arith() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; // Indices of marks in the assembly. let done = 0; let fail = 1; #[rustfmt::skip] let program = [ Op(DW_OP_const1u), U8(23), Op(DW_OP_const1s), U8((-23i8) as u8), Op(DW_OP_plus), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const2u), U16(23), Op(DW_OP_const2s), U16((-23i16) as u16), Op(DW_OP_plus), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const4u), U32(0x1111_2222), Op(DW_OP_const4s), U32((-0x1111_2222i32) as u32), Op(DW_OP_plus), Op(DW_OP_bra), Branch(fail), // Plus should overflow. Op(DW_OP_const1s), U8(0xff), Op(DW_OP_const1u), U8(1), Op(DW_OP_plus), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_plus_uconst), Uleb(1), Op(DW_OP_bra), Branch(fail), // Minus should underflow. Op(DW_OP_const1s), U8(0), Op(DW_OP_const1u), U8(1), Op(DW_OP_minus), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_abs), Op(DW_OP_const1u), U8(1), Op(DW_OP_minus), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const4u), U32(0xf078_fffe), Op(DW_OP_const4u), U32(0x0f87_0001), Op(DW_OP_and), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const4u), U32(0xf078_fffe), Op(DW_OP_const4u), U32(0xf000_00fe), Op(DW_OP_and), Op(DW_OP_const4u), U32(0xf000_00fe), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), // Division is signed. Op(DW_OP_const1s), U8(0xfe), Op(DW_OP_const1s), U8(2), Op(DW_OP_div), Op(DW_OP_plus_uconst), Uleb(1), Op(DW_OP_bra), Branch(fail), // Mod is unsigned. Op(DW_OP_const1s), U8(0xfd), Op(DW_OP_const1s), U8(2), Op(DW_OP_mod), Op(DW_OP_neg), Op(DW_OP_plus_uconst), Uleb(1), Op(DW_OP_bra), Branch(fail), // Overflow is defined for multiplication. Op(DW_OP_const4u), U32(0x8000_0001), Op(DW_OP_lit2), Op(DW_OP_mul), Op(DW_OP_lit2), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const4u), U32(0xf0f0_f0f0), Op(DW_OP_const4u), U32(0xf0f0_f0f0), Op(DW_OP_xor), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const4u), U32(0xf0f0_f0f0), Op(DW_OP_const4u), U32(0x0f0f_0f0f), Op(DW_OP_or), Op(DW_OP_not), Op(DW_OP_bra), Branch(fail), // In 32 bit mode, values are truncated. Op(DW_OP_const8u), U64(0xffff_ffff_0000_0000), Op(DW_OP_lit2), Op(DW_OP_div), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1u), U8(0xff), Op(DW_OP_lit1), Op(DW_OP_shl), Op(DW_OP_const2u), U16(0x1fe), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1u), U8(0xff), Op(DW_OP_const1u), U8(50), Op(DW_OP_shl), Op(DW_OP_bra), Branch(fail), // Absurd shift. Op(DW_OP_const1u), U8(0xff), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_shl), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_lit1), Op(DW_OP_shr), Op(DW_OP_const4u), U32(0x7fff_ffff), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_const1u), U8(0xff), Op(DW_OP_shr), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_lit1), Op(DW_OP_shra), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_const1u), U8(0xff), Op(DW_OP_shra), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), // Success. Op(DW_OP_lit0), Op(DW_OP_nop), Op(DW_OP_skip), Branch(done), Mark(fail), Op(DW_OP_lit1), Mark(done), Op(DW_OP_stack_value), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(0), }, }]; check_eval(&program, Ok(&result), encoding4()); } #[test] fn test_eval_arith64() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; // Indices of marks in the assembly. let done = 0; let fail = 1; #[rustfmt::skip] let program = [ Op(DW_OP_const8u), U64(0x1111_2222_3333_4444), Op(DW_OP_const8s), U64((-0x1111_2222_3333_4444i64) as u64), Op(DW_OP_plus), Op(DW_OP_bra), Branch(fail), Op(DW_OP_constu), Uleb(0x1111_2222_3333_4444), Op(DW_OP_consts), Sleb((-0x1111_2222_3333_4444i64) as u64), Op(DW_OP_plus), Op(DW_OP_bra), Branch(fail), Op(DW_OP_lit1), Op(DW_OP_plus_uconst), Uleb(!0u64), Op(DW_OP_bra), Branch(fail), Op(DW_OP_lit1), Op(DW_OP_neg), Op(DW_OP_not), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), Op(DW_OP_const1u), U8(63), Op(DW_OP_shr), Op(DW_OP_lit1), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), Op(DW_OP_const1u), U8(62), Op(DW_OP_shra), Op(DW_OP_plus_uconst), Uleb(2), Op(DW_OP_bra), Branch(fail), Op(DW_OP_lit1), Op(DW_OP_const1u), U8(63), Op(DW_OP_shl), Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), // Success. Op(DW_OP_lit0), Op(DW_OP_nop), Op(DW_OP_skip), Branch(done), Mark(fail), Op(DW_OP_lit1), Mark(done), Op(DW_OP_stack_value), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(0), }, }]; check_eval(&program, Ok(&result), encoding8()); } #[test] fn test_eval_compare() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; // Indices of marks in the assembly. let done = 0; let fail = 1; #[rustfmt::skip] let program = [ // Comparisons are signed. Op(DW_OP_const1s), U8(1), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_lt), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_const1s), U8(1), Op(DW_OP_gt), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(1), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_le), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_const1s), U8(1), Op(DW_OP_ge), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const1s), U8(0xff), Op(DW_OP_const1s), U8(1), Op(DW_OP_eq), Op(DW_OP_bra), Branch(fail), Op(DW_OP_const4s), U32(1), Op(DW_OP_const1s), U8(1), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), // Success. Op(DW_OP_lit0), Op(DW_OP_nop), Op(DW_OP_skip), Branch(done), Mark(fail), Op(DW_OP_lit1), Mark(done), Op(DW_OP_stack_value), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(0), }, }]; check_eval(&program, Ok(&result), encoding4()); } #[test] fn test_eval_stack() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; #[rustfmt::skip] let program = [ Op(DW_OP_lit17), // -- 17 Op(DW_OP_dup), // -- 17 17 Op(DW_OP_over), // -- 17 17 17 Op(DW_OP_minus), // -- 17 0 Op(DW_OP_swap), // -- 0 17 Op(DW_OP_dup), // -- 0 17 17 Op(DW_OP_plus_uconst), Uleb(1), // -- 0 17 18 Op(DW_OP_rot), // -- 18 0 17 Op(DW_OP_pick), U8(2), // -- 18 0 17 18 Op(DW_OP_pick), U8(3), // -- 18 0 17 18 18 Op(DW_OP_minus), // -- 18 0 17 0 Op(DW_OP_drop), // -- 18 0 17 Op(DW_OP_swap), // -- 18 17 0 Op(DW_OP_drop), // -- 18 17 Op(DW_OP_minus), // -- 1 Op(DW_OP_stack_value), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(1), }, }]; check_eval(&program, Ok(&result), encoding4()); } #[test] fn test_eval_lit_and_reg() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; let mut program = Vec::new(); program.push(Op(DW_OP_lit0)); for i in 0..32 { program.push(Op(DwOp(DW_OP_lit0.0 + i))); program.push(Op(DwOp(DW_OP_breg0.0 + i))); program.push(Sleb(u64::from(i))); program.push(Op(DW_OP_plus)); program.push(Op(DW_OP_plus)); } program.push(Op(DW_OP_bregx)); program.push(Uleb(0x1234)); program.push(Sleb(0x1234)); program.push(Op(DW_OP_plus)); program.push(Op(DW_OP_stack_value)); let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(496), }, }]; check_eval_with_args( &program, Ok(&result), encoding4(), None, None, None, |eval, mut result| { while result != EvaluationResult::Complete { result = eval.resume_with_register(match result { EvaluationResult::RequiresRegister { register, base_type, } => { assert_eq!(base_type, UnitOffset(0)); Value::Generic(u64::from(register.0).wrapping_neg()) } _ => panic!(), })?; } Ok(result) }, ); } #[test] fn test_eval_memory() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; // Indices of marks in the assembly. let done = 0; let fail = 1; #[rustfmt::skip] let program = [ Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_deref), Op(DW_OP_const4u), U32(0xffff_fffc), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_deref_size), U8(2), Op(DW_OP_const4u), U32(0xfffc), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_lit1), Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_xderef), Op(DW_OP_const4u), U32(0xffff_fffd), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_lit1), Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_xderef_size), U8(2), Op(DW_OP_const4u), U32(0xfffd), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_lit17), Op(DW_OP_form_tls_address), Op(DW_OP_constu), Uleb(!17), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_lit17), Op(DW_OP_GNU_push_tls_address), Op(DW_OP_constu), Uleb(!17), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_addrx), Uleb(0x10), Op(DW_OP_deref), Op(DW_OP_const4u), U32(0x4040), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), Op(DW_OP_constx), Uleb(17), Op(DW_OP_form_tls_address), Op(DW_OP_constu), Uleb(!27), Op(DW_OP_ne), Op(DW_OP_bra), Branch(fail), // Success. Op(DW_OP_lit0), Op(DW_OP_nop), Op(DW_OP_skip), Branch(done), Mark(fail), Op(DW_OP_lit1), Mark(done), Op(DW_OP_stack_value), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(0), }, }]; check_eval_with_args( &program, Ok(&result), encoding4(), None, None, None, |eval, mut result| { while result != EvaluationResult::Complete { result = match result { EvaluationResult::RequiresMemory { address, size, space, base_type, } => { assert_eq!(base_type, UnitOffset(0)); let mut v = address << 2; if let Some(value) = space { v += value; } v &= (1u64 << (8 * size)) - 1; eval.resume_with_memory(Value::Generic(v))? } EvaluationResult::RequiresTls(slot) => eval.resume_with_tls(!slot)?, EvaluationResult::RequiresRelocatedAddress(address) => { eval.resume_with_relocated_address(address)? } EvaluationResult::RequiresIndexedAddress { index, relocate } => { if relocate { eval.resume_with_indexed_address(0x1000 + index.0 as u64)? } else { eval.resume_with_indexed_address(10 + index.0 as u64)? } } _ => panic!(), }; } Ok(result) }, ); #[rustfmt::skip] let program = [ Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_deref_size), U8(8), ]; check_eval_with_args( &program, Err(Error::InvalidDerefSize(8)), encoding4(), None, None, None, |eval, mut result| { while result != EvaluationResult::Complete { result = match result { EvaluationResult::RequiresMemory { address, size, space, base_type, } => { assert_eq!(base_type, UnitOffset(0)); let mut v = address << 2; if let Some(value) = space { v += value; } v &= (1u64 << (8 * size)) - 1; eval.resume_with_memory(Value::Generic(v))? } EvaluationResult::RequiresRelocatedAddress(address) => { eval.resume_with_relocated_address(address)? } _ => panic!("Unexpected result: {:?}", result), }; } Ok(result) }, ); } #[test] fn test_eval_register() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; for i in 0..32 { #[rustfmt::skip] let program = [ Op(DwOp(DW_OP_reg0.0 + i)), // Included only in the "bad" run. Op(DW_OP_lit23), ]; let ok_result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Register { register: Register(i.into()), }, }]; check_eval(&program[..1], Ok(&ok_result), encoding4()); check_eval( &program, Err(Error::InvalidExpressionTerminator(1)), encoding4(), ); } #[rustfmt::skip] let program = [ Op(DW_OP_regx), Uleb(0x1234) ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Register { register: Register(0x1234), }, }]; check_eval(&program, Ok(&result), encoding4()); } #[test] fn test_eval_context() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; // Test `frame_base` and `call_frame_cfa` callbacks. #[rustfmt::skip] let program = [ Op(DW_OP_fbreg), Sleb((-8i8) as u64), Op(DW_OP_call_frame_cfa), Op(DW_OP_plus), Op(DW_OP_neg), Op(DW_OP_stack_value) ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(9), }, }]; check_eval_with_args( &program, Ok(&result), encoding8(), None, None, None, |eval, result| { match result { EvaluationResult::RequiresFrameBase => {} _ => panic!(), }; match eval.resume_with_frame_base(0x0123_4567_89ab_cdef)? { EvaluationResult::RequiresCallFrameCfa => {} _ => panic!(), }; eval.resume_with_call_frame_cfa(0xfedc_ba98_7654_3210) }, ); // Test `evaluate_entry_value` callback. #[rustfmt::skip] let program = [ Op(DW_OP_entry_value), Uleb(8), U64(0x1234_5678), Op(DW_OP_stack_value) ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(0x1234_5678), }, }]; check_eval_with_args( &program, Ok(&result), encoding8(), None, None, None, |eval, result| { let entry_value = match result { EvaluationResult::RequiresEntryValue(mut expression) => { expression.0.read_u64()? } _ => panic!(), }; eval.resume_with_entry_value(Value::Generic(entry_value)) }, ); // Test missing `object_address` field. #[rustfmt::skip] let program = [ Op(DW_OP_push_object_address), ]; check_eval_with_args( &program, Err(Error::InvalidPushObjectAddress), encoding4(), None, None, None, |_, _| panic!(), ); // Test `object_address` field. #[rustfmt::skip] let program = [ Op(DW_OP_push_object_address), Op(DW_OP_stack_value), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(0xff), }, }]; check_eval_with_args( &program, Ok(&result), encoding8(), Some(0xff), None, None, |_, result| Ok(result), ); // Test `initial_value` field. #[rustfmt::skip] let program = [ ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Address { address: 0x1234_5678, }, }]; check_eval_with_args( &program, Ok(&result), encoding8(), None, Some(0x1234_5678), None, |_, result| Ok(result), ); } #[test] fn test_eval_empty_stack() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; #[rustfmt::skip] let program = [ Op(DW_OP_stack_value) ]; check_eval(&program, Err(Error::NotEnoughStackItems), encoding4()); } #[test] fn test_eval_call() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; #[rustfmt::skip] let program = [ Op(DW_OP_lit23), Op(DW_OP_call2), U16(0x7755), Op(DW_OP_call4), U32(0x7755_aaee), Op(DW_OP_call_ref), U32(0x7755_aaee), Op(DW_OP_stack_value) ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(23), }, }]; check_eval_with_args( &program, Ok(&result), encoding4(), None, None, None, |eval, result| { let buf = EndianSlice::new(&[], LittleEndian); match result { EvaluationResult::RequiresAtLocation(_) => {} _ => panic!(), }; eval.resume_with_at_location(buf)?; match result { EvaluationResult::RequiresAtLocation(_) => {} _ => panic!(), }; eval.resume_with_at_location(buf)?; match result { EvaluationResult::RequiresAtLocation(_) => {} _ => panic!(), }; eval.resume_with_at_location(buf) }, ); // DW_OP_lit2 DW_OP_mul const SUBR: &[u8] = &[0x32, 0x1e]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value: Value::Generic(184), }, }]; check_eval_with_args( &program, Ok(&result), encoding4(), None, None, None, |eval, result| { let buf = EndianSlice::new(SUBR, LittleEndian); match result { EvaluationResult::RequiresAtLocation(_) => {} _ => panic!(), }; eval.resume_with_at_location(buf)?; match result { EvaluationResult::RequiresAtLocation(_) => {} _ => panic!(), }; eval.resume_with_at_location(buf)?; match result { EvaluationResult::RequiresAtLocation(_) => {} _ => panic!(), }; eval.resume_with_at_location(buf) }, ); } #[test] fn test_eval_pieces() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; // Example from DWARF 2.6.1.3. #[rustfmt::skip] let program = [ Op(DW_OP_reg3), Op(DW_OP_piece), Uleb(4), Op(DW_OP_reg4), Op(DW_OP_piece), Uleb(2), ]; let result = [ Piece { size_in_bits: Some(32), bit_offset: None, location: Location::Register { register: Register(3), }, }, Piece { size_in_bits: Some(16), bit_offset: None, location: Location::Register { register: Register(4), }, }, ]; check_eval(&program, Ok(&result), encoding4()); // Example from DWARF 2.6.1.3 (but hacked since dealing with fbreg // in the tests is a pain). #[rustfmt::skip] let program = [ Op(DW_OP_reg0), Op(DW_OP_piece), Uleb(4), Op(DW_OP_piece), Uleb(4), Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_piece), Uleb(4), ]; let result = [ Piece { size_in_bits: Some(32), bit_offset: None, location: Location::Register { register: Register(0), }, }, Piece { size_in_bits: Some(32), bit_offset: None, location: Location::Empty, }, Piece { size_in_bits: Some(32), bit_offset: None, location: Location::Address { address: 0x7fff_ffff, }, }, ]; check_eval_with_args( &program, Ok(&result), encoding4(), None, None, None, |eval, mut result| { while result != EvaluationResult::Complete { result = match result { EvaluationResult::RequiresRelocatedAddress(address) => { eval.resume_with_relocated_address(address)? } _ => panic!(), }; } Ok(result) }, ); #[rustfmt::skip] let program = [ Op(DW_OP_implicit_value), Uleb(5), U8(23), U8(24), U8(25), U8(26), U8(0), ]; const BYTES: &[u8] = &[23, 24, 25, 26, 0]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Bytes { value: EndianSlice::new(BYTES, LittleEndian), }, }]; check_eval(&program, Ok(&result), encoding4()); #[rustfmt::skip] let program = [ Op(DW_OP_lit7), Op(DW_OP_stack_value), Op(DW_OP_bit_piece), Uleb(5), Uleb(0), Op(DW_OP_bit_piece), Uleb(3), Uleb(0), ]; let result = [ Piece { size_in_bits: Some(5), bit_offset: Some(0), location: Location::Value { value: Value::Generic(7), }, }, Piece { size_in_bits: Some(3), bit_offset: Some(0), location: Location::Empty, }, ]; check_eval(&program, Ok(&result), encoding4()); #[rustfmt::skip] let program = [ Op(DW_OP_lit7), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Address { address: 7 }, }]; check_eval(&program, Ok(&result), encoding4()); #[rustfmt::skip] let program = [ Op(DW_OP_implicit_pointer), U32(0x1234_5678), Sleb(0x123), ]; let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::ImplicitPointer { value: DebugInfoOffset(0x1234_5678), byte_offset: 0x123, }, }]; check_eval(&program, Ok(&result), encoding4()); #[rustfmt::skip] let program = [ Op(DW_OP_reg3), Op(DW_OP_piece), Uleb(4), Op(DW_OP_reg4), ]; check_eval(&program, Err(Error::InvalidPiece), encoding4()); #[rustfmt::skip] let program = [ Op(DW_OP_reg3), Op(DW_OP_piece), Uleb(4), Op(DW_OP_lit0), ]; check_eval(&program, Err(Error::InvalidPiece), encoding4()); } #[test] fn test_eval_max_iterations() { // It's nice if an operation and its arguments can fit on a single // line in the test program. use self::AssemblerEntry::*; use crate::constants::*; #[rustfmt::skip] let program = [ Mark(1), Op(DW_OP_skip), Branch(1), ]; check_eval_with_args( &program, Err(Error::TooManyIterations), encoding4(), None, None, Some(150), |_, _| panic!(), ); } #[test] fn test_eval_typed_stack() { use self::AssemblerEntry::*; use crate::constants::*; let base_types = [ ValueType::Generic, ValueType::U16, ValueType::U32, ValueType::F32, ]; // TODO: convert, reinterpret #[rustfmt::skip] let tests = [ ( &[ Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), Op(DW_OP_stack_value), ][..], Value::U16(0x1234), ), ( &[ Op(DW_OP_regval_type), Uleb(0x1234), Uleb(1), Op(DW_OP_stack_value), ][..], Value::U16(0x2340), ), ( &[ Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_deref_type), U8(2), Uleb(1), Op(DW_OP_stack_value), ][..], Value::U16(0xfff0), ), ( &[ Op(DW_OP_lit1), Op(DW_OP_addr), U32(0x7fff_ffff), Op(DW_OP_xderef_type), U8(2), Uleb(1), Op(DW_OP_stack_value), ][..], Value::U16(0xfff1), ), ( &[ Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), Op(DW_OP_convert), Uleb(2), Op(DW_OP_stack_value), ][..], Value::U32(0x1234), ), ( &[ Op(DW_OP_const_type), Uleb(2), U8(4), U32(0x3f80_0000), Op(DW_OP_reinterpret), Uleb(3), Op(DW_OP_stack_value), ][..], Value::F32(1.0), ), ]; for &(program, value) in &tests { let result = [Piece { size_in_bits: None, bit_offset: None, location: Location::Value { value }, }]; check_eval_with_args( program, Ok(&result), encoding4(), None, None, None, |eval, mut result| { while result != EvaluationResult::Complete { result = match result { EvaluationResult::RequiresMemory { address, size, space, base_type, } => { let mut v = address << 4; if let Some(value) = space { v += value; } v &= (1u64 << (8 * size)) - 1; let v = Value::from_u64(base_types[base_type.0], v)?; eval.resume_with_memory(v)? } EvaluationResult::RequiresRegister { register, base_type, } => { let v = Value::from_u64( base_types[base_type.0], u64::from(register.0) << 4, )?; eval.resume_with_register(v)? } EvaluationResult::RequiresBaseType(offset) => { eval.resume_with_base_type(base_types[offset.0])? } EvaluationResult::RequiresRelocatedAddress(address) => { eval.resume_with_relocated_address(address)? } _ => panic!("Unexpected result {:?}", result), } } Ok(result) }, ); } } } gimli-0.31.1/src/read/pubnames.rs000064400000000000000000000107721046102023000147000ustar 00000000000000use crate::common::{DebugInfoOffset, SectionId}; use crate::endianity::Endianity; use crate::read::lookup::{DebugLookup, LookupEntryIter, PubStuffEntry, PubStuffParser}; use crate::read::{EndianSlice, Reader, Result, Section, UnitOffset}; /// A single parsed pubname. #[derive(Debug, Clone)] pub struct PubNamesEntry { unit_header_offset: DebugInfoOffset, die_offset: UnitOffset, name: R, } impl PubNamesEntry { /// Returns the name this entry refers to. pub fn name(&self) -> &R { &self.name } /// Returns the offset into the .debug_info section for the header of the compilation unit /// which contains this name. pub fn unit_header_offset(&self) -> DebugInfoOffset { self.unit_header_offset } /// Returns the offset into the compilation unit for the debugging information entry which /// has this name. pub fn die_offset(&self) -> UnitOffset { self.die_offset } } impl PubStuffEntry for PubNamesEntry { fn new( die_offset: UnitOffset, name: R, unit_header_offset: DebugInfoOffset, ) -> Self { PubNamesEntry { unit_header_offset, die_offset, name, } } } /// The `DebugPubNames` struct represents the DWARF public names information /// found in the `.debug_pubnames` section. #[derive(Debug, Clone)] pub struct DebugPubNames(DebugLookup>>); impl<'input, Endian> DebugPubNames> where Endian: Endianity, { /// Construct a new `DebugPubNames` instance from the data in the `.debug_pubnames` /// section. /// /// It is the caller's responsibility to read the `.debug_pubnames` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugPubNames, LittleEndian}; /// /// # let buf = []; /// # let read_debug_pubnames_section_somehow = || &buf; /// let debug_pubnames = /// DebugPubNames::new(read_debug_pubnames_section_somehow(), LittleEndian); /// ``` pub fn new(debug_pubnames_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_pubnames_section, endian)) } } impl DebugPubNames { /// Iterate the pubnames in the `.debug_pubnames` section. /// /// ``` /// use gimli::{DebugPubNames, EndianSlice, LittleEndian}; /// /// # let buf = []; /// # let read_debug_pubnames_section_somehow = || &buf; /// let debug_pubnames = /// DebugPubNames::new(read_debug_pubnames_section_somehow(), LittleEndian); /// /// let mut iter = debug_pubnames.items(); /// while let Some(pubname) = iter.next().unwrap() { /// println!("pubname {} found!", pubname.name().to_string_lossy()); /// } /// ``` pub fn items(&self) -> PubNamesEntryIter { PubNamesEntryIter(self.0.items()) } } impl Section for DebugPubNames { fn id() -> SectionId { SectionId::DebugPubNames } fn reader(&self) -> &R { self.0.reader() } } impl From for DebugPubNames { fn from(debug_pubnames_section: R) -> Self { DebugPubNames(DebugLookup::from(debug_pubnames_section)) } } /// An iterator over the pubnames from a `.debug_pubnames` section. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[derive(Debug, Clone)] pub struct PubNamesEntryIter(LookupEntryIter>>); impl PubNamesEntryIter { /// Advance the iterator and return the next pubname. /// /// Returns the newly parsed pubname as `Ok(Some(pubname))`. Returns /// `Ok(None)` when iteration is complete and all pubnames have already been /// parsed and yielded. If an error occurs while parsing the next pubname, /// then this error is returned as `Err(e)`, and all subsequent calls return /// `Ok(None)`. pub fn next(&mut self) -> Result>> { self.0.next() } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for PubNamesEntryIter { type Item = PubNamesEntry; type Error = crate::read::Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { self.0.next() } } gimli-0.31.1/src/read/pubtypes.rs000064400000000000000000000110121046102023000147250ustar 00000000000000use crate::common::{DebugInfoOffset, SectionId}; use crate::endianity::Endianity; use crate::read::lookup::{DebugLookup, LookupEntryIter, PubStuffEntry, PubStuffParser}; use crate::read::{EndianSlice, Reader, Result, Section, UnitOffset}; /// A single parsed pubtype. #[derive(Debug, Clone)] pub struct PubTypesEntry { unit_header_offset: DebugInfoOffset, die_offset: UnitOffset, name: R, } impl PubTypesEntry { /// Returns the name of the type this entry refers to. pub fn name(&self) -> &R { &self.name } /// Returns the offset into the .debug_info section for the header of the compilation unit /// which contains the type with this name. pub fn unit_header_offset(&self) -> DebugInfoOffset { self.unit_header_offset } /// Returns the offset into the compilation unit for the debugging information entry which /// the type with this name. pub fn die_offset(&self) -> UnitOffset { self.die_offset } } impl PubStuffEntry for PubTypesEntry { fn new( die_offset: UnitOffset, name: R, unit_header_offset: DebugInfoOffset, ) -> Self { PubTypesEntry { unit_header_offset, die_offset, name, } } } /// The `DebugPubTypes` struct represents the DWARF public types information /// found in the `.debug_info` section. #[derive(Debug, Clone)] pub struct DebugPubTypes(DebugLookup>>); impl<'input, Endian> DebugPubTypes> where Endian: Endianity, { /// Construct a new `DebugPubTypes` instance from the data in the `.debug_pubtypes` /// section. /// /// It is the caller's responsibility to read the `.debug_pubtypes` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugPubTypes, LittleEndian}; /// /// # let buf = []; /// # let read_debug_pubtypes_somehow = || &buf; /// let debug_pubtypes = /// DebugPubTypes::new(read_debug_pubtypes_somehow(), LittleEndian); /// ``` pub fn new(debug_pubtypes_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_pubtypes_section, endian)) } } impl DebugPubTypes { /// Iterate the pubtypes in the `.debug_pubtypes` section. /// /// ``` /// use gimli::{DebugPubTypes, EndianSlice, LittleEndian}; /// /// # let buf = []; /// # let read_debug_pubtypes_section_somehow = || &buf; /// let debug_pubtypes = /// DebugPubTypes::new(read_debug_pubtypes_section_somehow(), LittleEndian); /// /// let mut iter = debug_pubtypes.items(); /// while let Some(pubtype) = iter.next().unwrap() { /// println!("pubtype {} found!", pubtype.name().to_string_lossy()); /// } /// ``` pub fn items(&self) -> PubTypesEntryIter { PubTypesEntryIter(self.0.items()) } } impl Section for DebugPubTypes { fn id() -> SectionId { SectionId::DebugPubTypes } fn reader(&self) -> &R { self.0.reader() } } impl From for DebugPubTypes { fn from(debug_pubtypes_section: R) -> Self { DebugPubTypes(DebugLookup::from(debug_pubtypes_section)) } } /// An iterator over the pubtypes from a `.debug_pubtypes` section. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[derive(Debug, Clone)] pub struct PubTypesEntryIter(LookupEntryIter>>); impl PubTypesEntryIter { /// Advance the iterator and return the next pubtype. /// /// Returns the newly parsed pubtype as `Ok(Some(pubtype))`. Returns /// `Ok(None)` when iteration is complete and all pubtypes have already been /// parsed and yielded. If an error occurs while parsing the next pubtype, /// then this error is returned as `Err(e)`, and all subsequent calls return /// `Ok(None)`. pub fn next(&mut self) -> Result>> { self.0.next() } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for PubTypesEntryIter { type Item = PubTypesEntry; type Error = crate::read::Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { self.0.next() } } gimli-0.31.1/src/read/reader.rs000064400000000000000000000406071046102023000143300ustar 00000000000000#[cfg(feature = "read")] use alloc::borrow::Cow; use core::convert::TryInto; use core::fmt::Debug; use core::hash::Hash; use core::ops::{Add, AddAssign, Sub}; use crate::common::Format; use crate::endianity::Endianity; use crate::leb128; use crate::read::{Error, Result}; /// An identifier for an offset within a section reader. /// /// This is used for error reporting. The meaning of this value is specific to /// each reader implementation. The values should be chosen to be unique amongst /// all readers. If values are not unique then errors may point to the wrong reader. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ReaderOffsetId(pub u64); /// A trait for offsets with a DWARF section. /// /// This allows consumers to choose a size that is appropriate for their address space. pub trait ReaderOffset: Debug + Copy + Eq + Ord + Hash + Add + AddAssign + Sub { /// Convert a u8 to an offset. fn from_u8(offset: u8) -> Self; /// Convert a u16 to an offset. fn from_u16(offset: u16) -> Self; /// Convert an i16 to an offset. fn from_i16(offset: i16) -> Self; /// Convert a u32 to an offset. fn from_u32(offset: u32) -> Self; /// Convert a u64 to an offset. /// /// Returns `Error::UnsupportedOffset` if the value is too large. fn from_u64(offset: u64) -> Result; /// Convert an offset to a u64. fn into_u64(self) -> u64; /// Wrapping (modular) addition. Computes `self + other`. fn wrapping_add(self, other: Self) -> Self; /// Checked subtraction. Computes `self - other`. fn checked_sub(self, other: Self) -> Option; } impl ReaderOffset for u64 { #[inline] fn from_u8(offset: u8) -> Self { u64::from(offset) } #[inline] fn from_u16(offset: u16) -> Self { u64::from(offset) } #[inline] fn from_i16(offset: i16) -> Self { offset as u64 } #[inline] fn from_u32(offset: u32) -> Self { u64::from(offset) } #[inline] fn from_u64(offset: u64) -> Result { Ok(offset) } #[inline] fn into_u64(self) -> u64 { self } #[inline] fn wrapping_add(self, other: Self) -> Self { self.wrapping_add(other) } #[inline] fn checked_sub(self, other: Self) -> Option { self.checked_sub(other) } } impl ReaderOffset for u32 { #[inline] fn from_u8(offset: u8) -> Self { u32::from(offset) } #[inline] fn from_u16(offset: u16) -> Self { u32::from(offset) } #[inline] fn from_i16(offset: i16) -> Self { offset as u32 } #[inline] fn from_u32(offset: u32) -> Self { offset } #[inline] fn from_u64(offset64: u64) -> Result { let offset = offset64 as u32; if u64::from(offset) == offset64 { Ok(offset) } else { Err(Error::UnsupportedOffset) } } #[inline] fn into_u64(self) -> u64 { u64::from(self) } #[inline] fn wrapping_add(self, other: Self) -> Self { self.wrapping_add(other) } #[inline] fn checked_sub(self, other: Self) -> Option { self.checked_sub(other) } } impl ReaderOffset for usize { #[inline] fn from_u8(offset: u8) -> Self { offset as usize } #[inline] fn from_u16(offset: u16) -> Self { offset as usize } #[inline] fn from_i16(offset: i16) -> Self { offset as usize } #[inline] fn from_u32(offset: u32) -> Self { offset as usize } #[inline] fn from_u64(offset64: u64) -> Result { let offset = offset64 as usize; if offset as u64 == offset64 { Ok(offset) } else { Err(Error::UnsupportedOffset) } } #[inline] fn into_u64(self) -> u64 { self as u64 } #[inline] fn wrapping_add(self, other: Self) -> Self { self.wrapping_add(other) } #[inline] fn checked_sub(self, other: Self) -> Option { self.checked_sub(other) } } /// A trait for addresses within a DWARF section. /// /// Currently this is a simple extension trait for `u64`, but it may be expanded /// in the future to support user-defined address types. pub(crate) trait ReaderAddress: Sized { /// Add a length to an address of the given size. /// /// Returns an error for overflow. fn add_sized(self, length: u64, size: u8) -> Result; /// Add a length to an address of the given size. /// /// Wraps the result to the size of the address to allow for the possibility /// that the length is a negative value. fn wrapping_add_sized(self, length: u64, size: u8) -> Self; /// The all-ones value of an address of the given size. fn ones_sized(size: u8) -> Self; } impl ReaderAddress for u64 { #[inline] fn add_sized(self, length: u64, size: u8) -> Result { let address = self.checked_add(length).ok_or(Error::AddressOverflow)?; let mask = Self::ones_sized(size); if address & !mask != 0 { return Err(Error::AddressOverflow); } Ok(address) } #[inline] fn wrapping_add_sized(self, length: u64, size: u8) -> Self { let mask = Self::ones_sized(size); self.wrapping_add(length) & mask } #[inline] fn ones_sized(size: u8) -> Self { !0 >> (64 - size * 8) } } #[cfg(not(feature = "read"))] pub(crate) mod seal_if_no_alloc { #[derive(Debug)] pub struct Sealed; } /// A trait for reading the data from a DWARF section. /// /// All read operations advance the section offset of the reader /// unless specified otherwise. /// /// ## Choosing a `Reader` Implementation /// /// `gimli` comes with a few different `Reader` implementations and lets you /// choose the one that is right for your use case. A `Reader` is essentially a /// view into the raw bytes that make up some DWARF, but this view might borrow /// the underlying data or use reference counting ownership, and it might be /// thread safe or not. /// /// | Implementation | Ownership | Thread Safe | Notes | /// |:------------------|:------------------|:------------|:------| /// | [`EndianSlice`](./struct.EndianSlice.html) | Borrowed | Yes | Fastest, but requires that all of your code work with borrows. | /// | [`EndianRcSlice`](./struct.EndianRcSlice.html) | Reference counted | No | Shared ownership via reference counting, which alleviates the borrow restrictions of `EndianSlice` but imposes reference counting increments and decrements. Cannot be sent across threads, because the reference count is not atomic. | /// | [`EndianArcSlice`](./struct.EndianArcSlice.html) | Reference counted | Yes | The same as `EndianRcSlice`, but uses atomic reference counting, and therefore reference counting operations are slower but `EndianArcSlice`s may be sent across threads. | /// | [`EndianReader`](./struct.EndianReader.html) | Same as `T` | Same as `T` | Escape hatch for easily defining your own type of `Reader`. | pub trait Reader: Debug + Clone { /// The endianity of bytes that are read. type Endian: Endianity; /// The type used for offsets and lengths. type Offset: ReaderOffset; /// Return the endianity of bytes that are read. fn endian(&self) -> Self::Endian; /// Return the number of bytes remaining. fn len(&self) -> Self::Offset; /// Set the number of bytes remaining to zero. fn empty(&mut self); /// Set the number of bytes remaining to the specified length. fn truncate(&mut self, len: Self::Offset) -> Result<()>; /// Return the offset of this reader's data relative to the start of /// the given base reader's data. /// /// May panic if this reader's data is not contained within the given /// base reader's data. fn offset_from(&self, base: &Self) -> Self::Offset; /// Return an identifier for the current reader offset. fn offset_id(&self) -> ReaderOffsetId; /// Return the offset corresponding to the given `id` if /// it is associated with this reader. fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option; /// Find the index of the first occurrence of the given byte. /// The offset of the reader is not changed. fn find(&self, byte: u8) -> Result; /// Discard the specified number of bytes. fn skip(&mut self, len: Self::Offset) -> Result<()>; /// Split a reader in two. /// /// A new reader is returned that can be used to read the next /// `len` bytes, and `self` is advanced so that it reads the remainder. fn split(&mut self, len: Self::Offset) -> Result; /// This trait cannot be implemented if "read" feature is not enabled. /// /// `Reader` trait has a few methods that depend on `alloc` crate. /// Disallowing `Reader` trait implementation prevents a crate that only depends on /// "read-core" from being broken if another crate depending on `gimli` enables /// "read" feature. #[cfg(not(feature = "read"))] fn cannot_implement() -> seal_if_no_alloc::Sealed; /// Return all remaining data as a clone-on-write slice. /// /// The slice will be borrowed where possible, but some readers may /// always return an owned vector. /// /// Does not advance the reader. #[cfg(feature = "read")] fn to_slice(&self) -> Result>; /// Convert all remaining data to a clone-on-write string. /// /// The string will be borrowed where possible, but some readers may /// always return an owned string. /// /// Does not advance the reader. /// /// Returns an error if the data contains invalid characters. #[cfg(feature = "read")] fn to_string(&self) -> Result>; /// Convert all remaining data to a clone-on-write string, including invalid characters. /// /// The string will be borrowed where possible, but some readers may /// always return an owned string. /// /// Does not advance the reader. #[cfg(feature = "read")] fn to_string_lossy(&self) -> Result>; /// Read exactly `buf.len()` bytes into `buf`. fn read_slice(&mut self, buf: &mut [u8]) -> Result<()>; /// Read a u8 array. #[inline] fn read_u8_array(&mut self) -> Result where A: Sized + Default + AsMut<[u8]>, { let mut val = Default::default(); self.read_slice(>::as_mut(&mut val))?; Ok(val) } /// Return true if the number of bytes remaining is zero. #[inline] fn is_empty(&self) -> bool { self.len() == Self::Offset::from_u8(0) } /// Read a u8. #[inline] fn read_u8(&mut self) -> Result { let a: [u8; 1] = self.read_u8_array()?; Ok(a[0]) } /// Read an i8. #[inline] fn read_i8(&mut self) -> Result { let a: [u8; 1] = self.read_u8_array()?; Ok(a[0] as i8) } /// Read a u16. #[inline] fn read_u16(&mut self) -> Result { let a: [u8; 2] = self.read_u8_array()?; Ok(self.endian().read_u16(&a)) } /// Read an i16. #[inline] fn read_i16(&mut self) -> Result { let a: [u8; 2] = self.read_u8_array()?; Ok(self.endian().read_i16(&a)) } /// Read a u32. #[inline] fn read_u32(&mut self) -> Result { let a: [u8; 4] = self.read_u8_array()?; Ok(self.endian().read_u32(&a)) } /// Read an i32. #[inline] fn read_i32(&mut self) -> Result { let a: [u8; 4] = self.read_u8_array()?; Ok(self.endian().read_i32(&a)) } /// Read a u64. #[inline] fn read_u64(&mut self) -> Result { let a: [u8; 8] = self.read_u8_array()?; Ok(self.endian().read_u64(&a)) } /// Read an i64. #[inline] fn read_i64(&mut self) -> Result { let a: [u8; 8] = self.read_u8_array()?; Ok(self.endian().read_i64(&a)) } /// Read a f32. #[inline] fn read_f32(&mut self) -> Result { let a: [u8; 4] = self.read_u8_array()?; Ok(self.endian().read_f32(&a)) } /// Read a f64. #[inline] fn read_f64(&mut self) -> Result { let a: [u8; 8] = self.read_u8_array()?; Ok(self.endian().read_f64(&a)) } /// Read an unsigned n-bytes integer u64. /// /// # Panics /// /// Panics when nbytes < 1 or nbytes > 8 #[inline] fn read_uint(&mut self, n: usize) -> Result { let mut buf = [0; 8]; self.read_slice(&mut buf[..n])?; Ok(self.endian().read_uint(&buf[..n])) } /// Read a null-terminated slice, and return it (excluding the null). fn read_null_terminated_slice(&mut self) -> Result { let idx = self.find(0)?; let val = self.split(idx)?; self.skip(Self::Offset::from_u8(1))?; Ok(val) } /// Skip a LEB128 encoded integer. fn skip_leb128(&mut self) -> Result<()> { leb128::read::skip(self) } /// Read an unsigned LEB128 encoded integer. fn read_uleb128(&mut self) -> Result { leb128::read::unsigned(self) } /// Read an unsigned LEB128 encoded u32. fn read_uleb128_u32(&mut self) -> Result { leb128::read::unsigned(self)? .try_into() .map_err(|_| Error::BadUnsignedLeb128) } /// Read an unsigned LEB128 encoded u16. fn read_uleb128_u16(&mut self) -> Result { leb128::read::u16(self) } /// Read a signed LEB128 encoded integer. fn read_sleb128(&mut self) -> Result { leb128::read::signed(self) } /// Read an initial length field. /// /// This field is encoded as either a 32-bit length or /// a 64-bit length, and the returned `Format` indicates which. fn read_initial_length(&mut self) -> Result<(Self::Offset, Format)> { const MAX_DWARF_32_UNIT_LENGTH: u32 = 0xffff_fff0; const DWARF_64_INITIAL_UNIT_LENGTH: u32 = 0xffff_ffff; let val = self.read_u32()?; if val < MAX_DWARF_32_UNIT_LENGTH { Ok((Self::Offset::from_u32(val), Format::Dwarf32)) } else if val == DWARF_64_INITIAL_UNIT_LENGTH { let val = self.read_u64().and_then(Self::Offset::from_u64)?; Ok((val, Format::Dwarf64)) } else { Err(Error::UnknownReservedLength) } } /// Read a byte and validate it as an address size. fn read_address_size(&mut self) -> Result { let size = self.read_u8()?; match size { 1 | 2 | 4 | 8 => Ok(size), _ => Err(Error::UnsupportedAddressSize(size)), } } /// Read an address-sized integer, and return it as a `u64`. fn read_address(&mut self, address_size: u8) -> Result { match address_size { 1 => self.read_u8().map(u64::from), 2 => self.read_u16().map(u64::from), 4 => self.read_u32().map(u64::from), 8 => self.read_u64(), otherwise => Err(Error::UnsupportedAddressSize(otherwise)), } } /// Parse a word-sized integer according to the DWARF format. /// /// These are always used to encode section offsets or lengths, /// and so have a type of `Self::Offset`. fn read_word(&mut self, format: Format) -> Result { match format { Format::Dwarf32 => self.read_u32().map(Self::Offset::from_u32), Format::Dwarf64 => self.read_u64().and_then(Self::Offset::from_u64), } } /// Parse a word-sized section length according to the DWARF format. #[inline] fn read_length(&mut self, format: Format) -> Result { self.read_word(format) } /// Parse a word-sized section offset according to the DWARF format. #[inline] fn read_offset(&mut self, format: Format) -> Result { self.read_word(format) } /// Parse a section offset of the given size. /// /// This is used for `DW_FORM_ref_addr` values in DWARF version 2. fn read_sized_offset(&mut self, size: u8) -> Result { match size { 1 => self.read_u8().map(u64::from), 2 => self.read_u16().map(u64::from), 4 => self.read_u32().map(u64::from), 8 => self.read_u64(), otherwise => Err(Error::UnsupportedOffsetSize(otherwise)), } .and_then(Self::Offset::from_u64) } } gimli-0.31.1/src/read/relocate.rs000064400000000000000000000101241046102023000146530ustar 00000000000000#[cfg(feature = "read")] use alloc::borrow::Cow; use core::fmt::Debug; use crate::common::Format; use crate::read::{Reader, ReaderOffset, ReaderOffsetId, Result}; /// Trait for relocating addresses and offsets while reading a section. pub trait Relocate { /// Relocate an address which was read from the given section offset. fn relocate_address(&self, offset: T, value: u64) -> Result; /// Relocate a value which was read from the given section offset. fn relocate_offset(&self, offset: T, value: T) -> Result; } /// A `Reader` which applies relocations to addresses and offsets. /// /// This is useful for reading sections which contain relocations, /// such as those in a relocatable object file. /// It is generally not used for reading sections in an executable file. #[derive(Debug, Clone)] pub struct RelocateReader, T: Relocate> { section: R, reader: R, relocate: T, } impl RelocateReader where R: Reader, T: Relocate, { /// Create a new `RelocateReader` which applies relocations to the given section reader. pub fn new(section: R, relocate: T) -> Self { let reader = section.clone(); Self { section, reader, relocate, } } } impl Reader for RelocateReader where R: Reader, T: Relocate + Debug + Clone, { type Endian = R::Endian; type Offset = R::Offset; fn read_address(&mut self, address_size: u8) -> Result { let offset = self.reader.offset_from(&self.section); let value = self.reader.read_address(address_size)?; self.relocate.relocate_address(offset, value) } fn read_offset(&mut self, format: Format) -> Result { let offset = self.reader.offset_from(&self.section); let value = self.reader.read_offset(format)?; self.relocate.relocate_offset(offset, value) } fn read_sized_offset(&mut self, size: u8) -> Result { let offset = self.reader.offset_from(&self.section); let value = self.reader.read_sized_offset(size)?; self.relocate.relocate_offset(offset, value) } #[inline] fn split(&mut self, len: Self::Offset) -> Result { let mut other = self.clone(); other.reader.truncate(len)?; self.reader.skip(len)?; Ok(other) } // All remaining methods simply delegate to `self.reader`. #[inline] fn endian(&self) -> Self::Endian { self.reader.endian() } #[inline] fn len(&self) -> Self::Offset { self.reader.len() } #[inline] fn empty(&mut self) { self.reader.empty() } #[inline] fn truncate(&mut self, len: Self::Offset) -> Result<()> { self.reader.truncate(len) } #[inline] fn offset_from(&self, base: &Self) -> Self::Offset { self.reader.offset_from(&base.reader) } #[inline] fn offset_id(&self) -> ReaderOffsetId { self.reader.offset_id() } #[inline] fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option { self.reader.lookup_offset_id(id) } #[inline] fn find(&self, byte: u8) -> Result { self.reader.find(byte) } #[inline] fn skip(&mut self, len: Self::Offset) -> Result<()> { self.reader.skip(len) } #[cfg(not(feature = "read"))] fn cannot_implement() -> super::reader::seal_if_no_alloc::Sealed { super::reader::seal_if_no_alloc::Sealed } #[cfg(feature = "read")] #[inline] fn to_slice(&self) -> Result> { self.reader.to_slice() } #[cfg(feature = "read")] #[inline] fn to_string(&self) -> Result> { self.reader.to_string() } #[cfg(feature = "read")] #[inline] fn to_string_lossy(&self) -> Result> { self.reader.to_string_lossy() } #[inline] fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { self.reader.read_slice(buf) } } gimli-0.31.1/src/read/rnglists.rs000064400000000000000000001072771046102023000147420ustar 00000000000000use crate::common::{ DebugAddrBase, DebugAddrIndex, DebugRngListsBase, DebugRngListsIndex, DwarfFileType, Encoding, RangeListsOffset, SectionId, }; use crate::constants; use crate::endianity::Endianity; use crate::read::{ lists::ListsHeader, DebugAddr, EndianSlice, Error, Reader, ReaderAddress, ReaderOffset, ReaderOffsetId, Result, Section, }; /// The raw contents of the `.debug_ranges` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugRanges { pub(crate) section: R, } impl<'input, Endian> DebugRanges> where Endian: Endianity, { /// Construct a new `DebugRanges` instance from the data in the `.debug_ranges` /// section. /// /// It is the caller's responsibility to read the `.debug_ranges` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugRanges, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_ranges_section_somehow = || &buf; /// let debug_ranges = DebugRanges::new(read_debug_ranges_section_somehow(), LittleEndian); /// ``` pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl DebugRanges { /// Create a `DebugRanges` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugRanges where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugRanges { fn id() -> SectionId { SectionId::DebugRanges } fn reader(&self) -> &R { &self.section } } impl From for DebugRanges { fn from(section: R) -> Self { DebugRanges { section } } } /// The `DebugRngLists` struct represents the contents of the /// `.debug_rnglists` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugRngLists { section: R, } impl<'input, Endian> DebugRngLists> where Endian: Endianity, { /// Construct a new `DebugRngLists` instance from the data in the /// `.debug_rnglists` section. /// /// It is the caller's responsibility to read the `.debug_rnglists` /// section and present it as a `&[u8]` slice. That means using some ELF /// loader on Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugRngLists, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_rnglists_section_somehow = || &buf; /// let debug_rnglists = /// DebugRngLists::new(read_debug_rnglists_section_somehow(), LittleEndian); /// ``` pub fn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian)) } } impl DebugRngLists { /// Create a `DebugRngLists` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugRngLists where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugRngLists { fn id() -> SectionId { SectionId::DebugRngLists } fn reader(&self) -> &R { &self.section } } impl From for DebugRngLists { fn from(section: R) -> Self { DebugRngLists { section } } } #[allow(unused)] pub(crate) type RngListsHeader = ListsHeader; impl DebugRngListsBase where Offset: ReaderOffset, { /// Returns a `DebugRngListsBase` with the default value of DW_AT_rnglists_base /// for the given `Encoding` and `DwarfFileType`. pub fn default_for_encoding_and_file( encoding: Encoding, file_type: DwarfFileType, ) -> DebugRngListsBase { if encoding.version >= 5 && file_type == DwarfFileType::Dwo { // In .dwo files, the compiler omits the DW_AT_rnglists_base attribute (because there is // only a single unit in the file) but we must skip past the header, which the attribute // would normally do for us. DebugRngListsBase(Offset::from_u8(RngListsHeader::size_for_encoding(encoding))) } else { DebugRngListsBase(Offset::from_u8(0)) } } } /// The DWARF data found in `.debug_ranges` and `.debug_rnglists` sections. #[derive(Debug, Default, Clone, Copy)] pub struct RangeLists { debug_ranges: DebugRanges, debug_rnglists: DebugRngLists, } impl RangeLists { /// Construct a new `RangeLists` instance from the data in the `.debug_ranges` and /// `.debug_rnglists` sections. pub fn new(debug_ranges: DebugRanges, debug_rnglists: DebugRngLists) -> RangeLists { RangeLists { debug_ranges, debug_rnglists, } } /// Return the `.debug_ranges` section. pub fn debug_ranges(&self) -> &DebugRanges { &self.debug_ranges } /// Replace the `.debug_ranges` section. /// /// This is useful for `.dwo` files when using the GNU split-dwarf extension to DWARF 4. pub fn set_debug_ranges(&mut self, debug_ranges: DebugRanges) { self.debug_ranges = debug_ranges; } /// Return the `.debug_rnglists` section. pub fn debug_rnglists(&self) -> &DebugRngLists { &self.debug_rnglists } } impl RangeLists { /// Create a `RangeLists` that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `Dwarf::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> RangeLists where F: FnMut(&'a T) -> R, { RangeLists { debug_ranges: borrow(&self.debug_ranges.section).into(), debug_rnglists: borrow(&self.debug_rnglists.section).into(), } } } impl RangeLists { /// Iterate over the `Range` list entries starting at the given offset. /// /// The `unit_version` and `address_size` must match the compilation unit that the /// offset was contained in. /// /// The `base_address` should be obtained from the `DW_AT_low_pc` attribute in the /// `DW_TAG_compile_unit` entry for the compilation unit that contains this range list. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn ranges( &self, offset: RangeListsOffset, unit_encoding: Encoding, base_address: u64, debug_addr: &DebugAddr, debug_addr_base: DebugAddrBase, ) -> Result> { Ok(RngListIter::new( self.raw_ranges(offset, unit_encoding)?, base_address, debug_addr.clone(), debug_addr_base, )) } /// Iterate over the `RawRngListEntry`ies starting at the given offset. /// /// The `unit_encoding` must match the compilation unit that the /// offset was contained in. /// /// This iterator does not perform any processing of the range entries, /// such as handling base addresses. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn raw_ranges( &self, offset: RangeListsOffset, unit_encoding: Encoding, ) -> Result> { let (mut input, format) = if unit_encoding.version <= 4 { (self.debug_ranges.section.clone(), RangeListsFormat::Bare) } else { (self.debug_rnglists.section.clone(), RangeListsFormat::Rle) }; input.skip(offset.0)?; Ok(RawRngListIter::new(input, unit_encoding, format)) } /// Returns the `.debug_rnglists` offset at the given `base` and `index`. /// /// The `base` must be the `DW_AT_rnglists_base` value from the compilation unit DIE. /// This is an offset that points to the first entry following the header. /// /// The `index` is the value of a `DW_FORM_rnglistx` attribute. /// /// The `unit_encoding` must match the compilation unit that the /// index was contained in. pub fn get_offset( &self, unit_encoding: Encoding, base: DebugRngListsBase, index: DebugRngListsIndex, ) -> Result> { let format = unit_encoding.format; let input = &mut self.debug_rnglists.section.clone(); input.skip(base.0)?; input.skip(R::Offset::from_u64( index.0.into_u64() * u64::from(format.word_size()), )?)?; input .read_offset(format) .map(|x| RangeListsOffset(base.0 + x)) } /// Call `Reader::lookup_offset_id` for each section, and return the first match. pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> { self.debug_ranges .lookup_offset_id(id) .or_else(|| self.debug_rnglists.lookup_offset_id(id)) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum RangeListsFormat { /// The bare range list format used before DWARF 5. Bare, /// The DW_RLE encoded range list format used in DWARF 5. Rle, } /// A raw iterator over an address range list. /// /// This iterator does not perform any processing of the range entries, /// such as handling base addresses. #[derive(Debug)] pub struct RawRngListIter { input: R, encoding: Encoding, format: RangeListsFormat, } /// A raw entry in .debug_rnglists #[derive(Clone, Debug)] pub enum RawRngListEntry { /// A range from DWARF version <= 4. AddressOrOffsetPair { /// Start of range. May be an address or an offset. begin: u64, /// End of range. May be an address or an offset. end: u64, }, /// DW_RLE_base_address BaseAddress { /// base address addr: u64, }, /// DW_RLE_base_addressx BaseAddressx { /// base address addr: DebugAddrIndex, }, /// DW_RLE_startx_endx StartxEndx { /// start of range begin: DebugAddrIndex, /// end of range end: DebugAddrIndex, }, /// DW_RLE_startx_length StartxLength { /// start of range begin: DebugAddrIndex, /// length of range length: u64, }, /// DW_RLE_offset_pair OffsetPair { /// start of range begin: u64, /// end of range end: u64, }, /// DW_RLE_start_end StartEnd { /// start of range begin: u64, /// end of range end: u64, }, /// DW_RLE_start_length StartLength { /// start of range begin: u64, /// length of range length: u64, }, } impl RawRngListEntry { /// Parse a range entry from `.debug_rnglists` fn parse>( input: &mut R, encoding: Encoding, format: RangeListsFormat, ) -> Result> { Ok(match format { RangeListsFormat::Bare => { let range = RawRange::parse(input, encoding.address_size)?; if range.is_end() { None } else if range.is_base_address(encoding.address_size) { Some(RawRngListEntry::BaseAddress { addr: range.end }) } else { Some(RawRngListEntry::AddressOrOffsetPair { begin: range.begin, end: range.end, }) } } RangeListsFormat::Rle => match constants::DwRle(input.read_u8()?) { constants::DW_RLE_end_of_list => None, constants::DW_RLE_base_addressx => Some(RawRngListEntry::BaseAddressx { addr: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), }), constants::DW_RLE_startx_endx => Some(RawRngListEntry::StartxEndx { begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), end: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), }), constants::DW_RLE_startx_length => Some(RawRngListEntry::StartxLength { begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), length: input.read_uleb128()?, }), constants::DW_RLE_offset_pair => Some(RawRngListEntry::OffsetPair { begin: input.read_uleb128()?, end: input.read_uleb128()?, }), constants::DW_RLE_base_address => Some(RawRngListEntry::BaseAddress { addr: input.read_address(encoding.address_size)?, }), constants::DW_RLE_start_end => Some(RawRngListEntry::StartEnd { begin: input.read_address(encoding.address_size)?, end: input.read_address(encoding.address_size)?, }), constants::DW_RLE_start_length => Some(RawRngListEntry::StartLength { begin: input.read_address(encoding.address_size)?, length: input.read_uleb128()?, }), entry => { return Err(Error::UnknownRangeListsEntry(entry)); } }, }) } } impl RawRngListIter { /// Construct a `RawRngListIter`. fn new(input: R, encoding: Encoding, format: RangeListsFormat) -> RawRngListIter { RawRngListIter { input, encoding, format, } } /// Advance the iterator to the next range. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { return Ok(None); } match RawRngListEntry::parse(&mut self.input, self.encoding, self.format) { Ok(range) => { if range.is_none() { self.input.empty(); } Ok(range) } Err(e) => { self.input.empty(); Err(e) } } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for RawRngListIter { type Item = RawRngListEntry; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { RawRngListIter::next(self) } } /// An iterator over an address range list. /// /// This iterator internally handles processing of base addresses and different /// entry types. Thus, it only returns range entries that are valid /// and already adjusted for the base address. #[derive(Debug)] pub struct RngListIter { raw: RawRngListIter, base_address: u64, debug_addr: DebugAddr, debug_addr_base: DebugAddrBase, } impl RngListIter { /// Construct a `RngListIter`. fn new( raw: RawRngListIter, base_address: u64, debug_addr: DebugAddr, debug_addr_base: DebugAddrBase, ) -> RngListIter { RngListIter { raw, base_address, debug_addr, debug_addr_base, } } #[inline] fn get_address(&self, index: DebugAddrIndex) -> Result { self.debug_addr .get_address(self.raw.encoding.address_size, self.debug_addr_base, index) } /// Advance the iterator to the next range. pub fn next(&mut self) -> Result> { loop { let raw_range = match self.raw.next()? { Some(range) => range, None => return Ok(None), }; let range = self.convert_raw(raw_range)?; if range.is_some() { return Ok(range); } } } /// Return the next raw range. /// /// The raw range should be passed to `convert_range`. #[doc(hidden)] pub fn next_raw(&mut self) -> Result>> { self.raw.next() } /// Convert a raw range into a range, and update the state of the iterator. /// /// The raw range should have been obtained from `next_raw`. #[doc(hidden)] pub fn convert_raw(&mut self, raw_range: RawRngListEntry) -> Result> { let address_size = self.raw.encoding.address_size; let mask = u64::ones_sized(address_size); let tombstone = if self.raw.encoding.version <= 4 { mask - 1 } else { mask }; let range = match raw_range { RawRngListEntry::BaseAddress { addr } => { self.base_address = addr; return Ok(None); } RawRngListEntry::BaseAddressx { addr } => { self.base_address = self.get_address(addr)?; return Ok(None); } RawRngListEntry::StartxEndx { begin, end } => { let begin = self.get_address(begin)?; let end = self.get_address(end)?; Range { begin, end } } RawRngListEntry::StartxLength { begin, length } => { let begin = self.get_address(begin)?; let end = begin.wrapping_add_sized(length, address_size); Range { begin, end } } RawRngListEntry::AddressOrOffsetPair { begin, end } | RawRngListEntry::OffsetPair { begin, end } => { // Skip tombstone entries (see below). if self.base_address == tombstone { return Ok(None); } let mut range = Range { begin, end }; range.add_base_address(self.base_address, self.raw.encoding.address_size); range } RawRngListEntry::StartEnd { begin, end } => Range { begin, end }, RawRngListEntry::StartLength { begin, length } => { let end = begin.wrapping_add_sized(length, address_size); Range { begin, end } } }; // Skip tombstone entries. // // DWARF specifies a tombstone value of -1 or -2, but many linkers use 0 or 1. // However, 0/1 may be a valid address, so we can't always reliably skip them. // One case where we can skip them is for address pairs, where both values are // replaced by tombstones and thus `begin` equals `end`. Since these entries // are empty, it's safe to skip them even if they aren't tombstones. // // In addition to skipping tombstone entries, we also skip invalid entries // where `begin` is greater than `end`. This can occur due to compiler bugs. if range.begin == tombstone || range.begin >= range.end { return Ok(None); } Ok(Some(range)) } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for RngListIter { type Item = Range; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { RngListIter::next(self) } } /// A raw address range from the `.debug_ranges` section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct RawRange { /// The beginning address of the range. pub begin: u64, /// The first address past the end of the range. pub end: u64, } impl RawRange { /// Check if this is a range end entry. #[inline] pub fn is_end(&self) -> bool { self.begin == 0 && self.end == 0 } /// Check if this is a base address selection entry. /// /// A base address selection entry changes the base address that subsequent /// range entries are relative to. #[inline] pub fn is_base_address(&self, address_size: u8) -> bool { self.begin == !0 >> (64 - address_size * 8) } /// Parse an address range entry from `.debug_ranges` or `.debug_loc`. #[inline] pub fn parse(input: &mut R, address_size: u8) -> Result { let begin = input.read_address(address_size)?; let end = input.read_address(address_size)?; let range = RawRange { begin, end }; Ok(range) } } /// An address range from the `.debug_ranges`, `.debug_rnglists`, or `.debug_aranges` sections. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Range { /// The beginning address of the range. pub begin: u64, /// The first address past the end of the range. pub end: u64, } impl Range { /// Add a base address to this range. #[inline] pub(crate) fn add_base_address(&mut self, base_address: u64, address_size: u8) { self.begin = base_address.wrapping_add_sized(self.begin, address_size); self.end = base_address.wrapping_add_sized(self.end, address_size); } } #[cfg(test)] mod tests { use super::*; use crate::common::Format; use crate::constants::*; use crate::endianity::LittleEndian; use crate::test_util::GimliSectionMethods; use alloc::vec::Vec; use test_assembler::{Endian, Label, LabelMaker, Section}; #[test] fn test_rnglists() { let format = Format::Dwarf32; for size in [4, 8] { let tombstone = u64::ones_sized(size); let tombstone_0 = 0; let encoding = Encoding { format, version: 5, address_size: size, }; let section = Section::with_endian(Endian::Little) .word(size, 0x0300_0000) .word(size, 0x0301_0300) .word(size, 0x0301_0400) .word(size, 0x0301_0500) .word(size, tombstone) .word(size, 0x0301_0600) .word(size, tombstone_0); let buf = section.get_contents().unwrap(); let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); let debug_addr_base = DebugAddrBase(0); let length = Label::new(); let start = Label::new(); let first = Label::new(); let end = Label::new(); let mut section = Section::with_endian(Endian::Little) .initial_length(format, &length, &start) .L16(encoding.version) .L8(encoding.address_size) .L8(0) .L32(0) .mark(&first); let mut expected_ranges = Vec::new(); let mut expect_range = |begin, end| { expected_ranges.push(Range { begin, end }); }; // An offset pair using the unit base address. section = section.L8(DW_RLE_offset_pair.0).uleb(0x10200).uleb(0x10300); expect_range(0x0101_0200, 0x0101_0300); section = section.L8(DW_RLE_base_address.0).word(size, 0x0200_0000); section = section.L8(DW_RLE_offset_pair.0).uleb(0x10400).uleb(0x10500); expect_range(0x0201_0400, 0x0201_0500); section = section .L8(DW_RLE_start_end.0) .word(size, 0x201_0a00) .word(size, 0x201_0b00); expect_range(0x0201_0a00, 0x0201_0b00); section = section .L8(DW_RLE_start_length.0) .word(size, 0x201_0c00) .uleb(0x100); expect_range(0x0201_0c00, 0x0201_0d00); // An offset pair that starts at 0. section = section.L8(DW_RLE_base_address.0).word(size, 0); section = section.L8(DW_RLE_offset_pair.0).uleb(0).uleb(1); expect_range(0, 1); // An offset pair that ends at -1. section = section.L8(DW_RLE_base_address.0).word(size, 0); section = section.L8(DW_RLE_offset_pair.0).uleb(0).uleb(tombstone); expect_range(0, tombstone); section = section.L8(DW_RLE_base_addressx.0).uleb(0); section = section.L8(DW_RLE_offset_pair.0).uleb(0x10100).uleb(0x10200); expect_range(0x0301_0100, 0x0301_0200); section = section.L8(DW_RLE_startx_endx.0).uleb(1).uleb(2); expect_range(0x0301_0300, 0x0301_0400); section = section.L8(DW_RLE_startx_length.0).uleb(3).uleb(0x100); expect_range(0x0301_0500, 0x0301_0600); // Tombstone entries, all of which should be ignored. section = section.L8(DW_RLE_base_addressx.0).uleb(4); section = section.L8(DW_RLE_offset_pair.0).uleb(0x11100).uleb(0x11200); section = section.L8(DW_RLE_base_address.0).word(size, tombstone); section = section.L8(DW_RLE_offset_pair.0).uleb(0x11300).uleb(0x11400); section = section.L8(DW_RLE_startx_endx.0).uleb(4).uleb(5); section = section.L8(DW_RLE_startx_length.0).uleb(4).uleb(0x100); section = section .L8(DW_RLE_start_end.0) .word(size, tombstone) .word(size, 0x201_1500); section = section .L8(DW_RLE_start_length.0) .word(size, tombstone) .uleb(0x100); // Ignore some instances of 0 for tombstone. section = section.L8(DW_RLE_startx_endx.0).uleb(6).uleb(6); section = section .L8(DW_RLE_start_end.0) .word(size, tombstone_0) .word(size, tombstone_0); // Ignore empty ranges. section = section.L8(DW_RLE_base_address.0).word(size, 0); section = section.L8(DW_RLE_offset_pair.0).uleb(0).uleb(0); section = section.L8(DW_RLE_base_address.0).word(size, 0x10000); section = section.L8(DW_RLE_offset_pair.0).uleb(0x1234).uleb(0x1234); // A valid range after the tombstones. section = section .L8(DW_RLE_start_end.0) .word(size, 0x201_1600) .word(size, 0x201_1700); expect_range(0x0201_1600, 0x0201_1700); section = section.L8(DW_RLE_end_of_list.0); section = section.mark(&end); // Some extra data. section = section.word(size, 0x1234_5678); length.set_const((&end - &start) as u64); let offset = RangeListsOffset((&first - §ion.start()) as usize); let buf = section.get_contents().unwrap(); let debug_ranges = DebugRanges::new(&[], LittleEndian); let debug_rnglists = DebugRngLists::new(&buf, LittleEndian); let rnglists = RangeLists::new(debug_ranges, debug_rnglists); let mut ranges = rnglists .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) .unwrap(); for expected_range in expected_ranges { let range = ranges.next(); assert_eq!( range, Ok(Some(expected_range)), "read {:x?}, expect {:x?}", range, expected_range ); } assert_eq!(ranges.next(), Ok(None)); // An offset at the end of buf. let mut ranges = rnglists .ranges( RangeListsOffset(buf.len()), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!(ranges.next(), Ok(None)); } } #[test] fn test_raw_range() { let range = RawRange { begin: 0, end: 0xffff_ffff, }; assert!(!range.is_end()); assert!(!range.is_base_address(4)); assert!(!range.is_base_address(8)); let range = RawRange { begin: 0, end: 0 }; assert!(range.is_end()); assert!(!range.is_base_address(4)); assert!(!range.is_base_address(8)); let range = RawRange { begin: 0xffff_ffff, end: 0, }; assert!(!range.is_end()); assert!(range.is_base_address(4)); assert!(!range.is_base_address(8)); let range = RawRange { begin: 0xffff_ffff_ffff_ffff, end: 0, }; assert!(!range.is_end()); assert!(!range.is_base_address(4)); assert!(range.is_base_address(8)); } #[test] fn test_ranges() { for size in [4, 8] { let base = u64::ones_sized(size); let tombstone = u64::ones_sized(size) - 1; let start = Label::new(); let first = Label::new(); let mut section = Section::with_endian(Endian::Little) // A range before the offset. .mark(&start) .word(size, 0x10000) .word(size, 0x10100) .mark(&first); let mut expected_ranges = Vec::new(); let mut expect_range = |begin, end| { expected_ranges.push(Range { begin, end }); }; // A normal range. section = section.word(size, 0x10200).word(size, 0x10300); expect_range(0x0101_0200, 0x0101_0300); // A base address selection followed by a normal range. section = section.word(size, base).word(size, 0x0200_0000); section = section.word(size, 0x10400).word(size, 0x10500); expect_range(0x0201_0400, 0x0201_0500); // An empty range followed by a normal range. section = section.word(size, 0x10600).word(size, 0x10600); section = section.word(size, 0x10800).word(size, 0x10900); expect_range(0x0201_0800, 0x0201_0900); // A range that starts at 0. section = section.word(size, base).word(size, 0); section = section.word(size, 0).word(size, 1); expect_range(0, 1); // A range that ends at -1. section = section.word(size, base).word(size, 0); section = section.word(size, 0).word(size, base); expect_range(0, base); // A normal range with tombstone. section = section.word(size, tombstone).word(size, tombstone); // A base address selection with tombstone followed by a normal range. section = section.word(size, base).word(size, tombstone); section = section.word(size, 0x10a00).word(size, 0x10b00); // A range end. section = section.word(size, 0).word(size, 0); // Some extra data. section = section.word(size, 0x1234_5678); let buf = section.get_contents().unwrap(); let debug_ranges = DebugRanges::new(&buf, LittleEndian); let debug_rnglists = DebugRngLists::new(&[], LittleEndian); let rnglists = RangeLists::new(debug_ranges, debug_rnglists); let offset = RangeListsOffset((&first - &start) as usize); let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); let debug_addr_base = DebugAddrBase(0); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: size, }; let mut ranges = rnglists .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) .unwrap(); for expected_range in expected_ranges { let range = ranges.next(); assert_eq!( range, Ok(Some(expected_range)), "read {:x?}, expect {:x?}", range, expected_range ); } assert_eq!(ranges.next(), Ok(None)); // An offset at the end of buf. let mut ranges = rnglists .ranges( RangeListsOffset(buf.len()), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!(ranges.next(), Ok(None)); } } #[test] fn test_ranges_invalid() { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) // An invalid range. .L32(0x20000).L32(0x10000) // An invalid range after wrapping. .L32(0x20000).L32(0xff01_0000); let buf = section.get_contents().unwrap(); let debug_ranges = DebugRanges::new(&buf, LittleEndian); let debug_rnglists = DebugRngLists::new(&[], LittleEndian); let rnglists = RangeLists::new(debug_ranges, debug_rnglists); let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); let debug_addr_base = DebugAddrBase(0); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; // An invalid range. let mut ranges = rnglists .ranges( RangeListsOffset(0x0), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!(ranges.next(), Ok(None)); // An invalid range after wrapping. let mut ranges = rnglists .ranges( RangeListsOffset(0x8), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) .unwrap(); assert_eq!(ranges.next(), Ok(None)); // An invalid offset. match rnglists.ranges( RangeListsOffset(buf.len() + 1), encoding, 0x0100_0000, debug_addr, debug_addr_base, ) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] fn test_get_offset() { for format in [Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version: 5, address_size: 4, }; let zero = Label::new(); let length = Label::new(); let start = Label::new(); let first = Label::new(); let end = Label::new(); let mut section = Section::with_endian(Endian::Little) .mark(&zero) .initial_length(format, &length, &start) .D16(encoding.version) .D8(encoding.address_size) .D8(0) .D32(20) .mark(&first); for i in 0..20 { section = section.word(format.word_size(), 1000 + i); } section = section.mark(&end); length.set_const((&end - &start) as u64); let section = section.get_contents().unwrap(); let debug_ranges = DebugRanges::from(EndianSlice::new(&[], LittleEndian)); let debug_rnglists = DebugRngLists::from(EndianSlice::new(§ion, LittleEndian)); let ranges = RangeLists::new(debug_ranges, debug_rnglists); let base = DebugRngListsBase((&first - &zero) as usize); assert_eq!( ranges.get_offset(encoding, base, DebugRngListsIndex(0)), Ok(RangeListsOffset(base.0 + 1000)) ); assert_eq!( ranges.get_offset(encoding, base, DebugRngListsIndex(19)), Ok(RangeListsOffset(base.0 + 1019)) ); } } } gimli-0.31.1/src/read/str.rs000064400000000000000000000224031046102023000136700ustar 00000000000000use crate::common::{ DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, Encoding, SectionId, }; use crate::endianity::Endianity; use crate::read::{EndianSlice, Reader, ReaderOffset, Result, Section}; use crate::Format; /// The `DebugStr` struct represents the DWARF strings /// found in the `.debug_str` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugStr { debug_str_section: R, } impl<'input, Endian> DebugStr> where Endian: Endianity, { /// Construct a new `DebugStr` instance from the data in the `.debug_str` /// section. /// /// It is the caller's responsibility to read the `.debug_str` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugStr, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_str_section_somehow = || &buf; /// let debug_str = DebugStr::new(read_debug_str_section_somehow(), LittleEndian); /// ``` pub fn new(debug_str_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_str_section, endian)) } } impl DebugStr { /// Lookup a string from the `.debug_str` section by DebugStrOffset. /// /// ``` /// use gimli::{DebugStr, DebugStrOffset, LittleEndian}; /// /// # let buf = [0x01, 0x02, 0x00]; /// # let offset = DebugStrOffset(0); /// # let read_debug_str_section_somehow = || &buf; /// # let debug_str_offset_somehow = || offset; /// let debug_str = DebugStr::new(read_debug_str_section_somehow(), LittleEndian); /// println!("Found string {:?}", debug_str.get_str(debug_str_offset_somehow())); /// ``` pub fn get_str(&self, offset: DebugStrOffset) -> Result { let input = &mut self.debug_str_section.clone(); input.skip(offset.0)?; input.read_null_terminated_slice() } } impl DebugStr { /// Create a `DebugStr` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStr where F: FnMut(&'a T) -> R, { borrow(&self.debug_str_section).into() } } impl Section for DebugStr { fn id() -> SectionId { SectionId::DebugStr } fn reader(&self) -> &R { &self.debug_str_section } } impl From for DebugStr { fn from(debug_str_section: R) -> Self { DebugStr { debug_str_section } } } /// The raw contents of the `.debug_str_offsets` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugStrOffsets { section: R, } impl DebugStrOffsets { // TODO: add an iterator over the sets of entries in the section. // This is not needed for common usage of the section though. /// Returns the `.debug_str` offset at the given `base` and `index`. /// /// A set of entries in the `.debug_str_offsets` section consists of a header /// followed by a series of string table offsets. /// /// The `base` must be the `DW_AT_str_offsets_base` value from the compilation unit DIE. /// This is an offset that points to the first entry following the header. /// /// The `index` is the value of a `DW_FORM_strx` attribute. /// /// The `format` must be the DWARF format of the compilation unit. This format must /// match the header. However, note that we do not parse the header to validate this, /// since locating the header is unreliable, and the GNU extensions do not emit it. pub fn get_str_offset( &self, format: Format, base: DebugStrOffsetsBase, index: DebugStrOffsetsIndex, ) -> Result> { let input = &mut self.section.clone(); input.skip(base.0)?; input.skip(R::Offset::from_u64( index.0.into_u64() * u64::from(format.word_size()), )?)?; input.read_offset(format).map(DebugStrOffset) } } impl DebugStrOffsets { /// Create a `DebugStrOffsets` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStrOffsets where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugStrOffsets { fn id() -> SectionId { SectionId::DebugStrOffsets } fn reader(&self) -> &R { &self.section } } impl From for DebugStrOffsets { fn from(section: R) -> Self { DebugStrOffsets { section } } } impl DebugStrOffsetsBase where Offset: ReaderOffset, { /// Returns a `DebugStrOffsetsBase` with the default value of DW_AT_str_offsets_base /// for the given `Encoding` and `DwarfFileType`. pub fn default_for_encoding_and_file( encoding: Encoding, file_type: DwarfFileType, ) -> DebugStrOffsetsBase { if encoding.version >= 5 && file_type == DwarfFileType::Dwo { // In .dwo files, the compiler omits the DW_AT_str_offsets_base attribute (because there is // only a single unit in the file) but we must skip past the header, which the attribute // would normally do for us. // initial_length_size + version + 2 bytes of padding. DebugStrOffsetsBase(Offset::from_u8( encoding.format.initial_length_size() + 2 + 2, )) } else { DebugStrOffsetsBase(Offset::from_u8(0)) } } } /// The `DebugLineStr` struct represents the DWARF strings /// found in the `.debug_line_str` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugLineStr { section: R, } impl<'input, Endian> DebugLineStr> where Endian: Endianity, { /// Construct a new `DebugLineStr` instance from the data in the `.debug_line_str` /// section. /// /// It is the caller's responsibility to read the `.debug_line_str` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugLineStr, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_line_str_section_somehow = || &buf; /// let debug_str = DebugLineStr::new(read_debug_line_str_section_somehow(), LittleEndian); /// ``` pub fn new(debug_line_str_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_line_str_section, endian)) } } impl DebugLineStr { /// Lookup a string from the `.debug_line_str` section by DebugLineStrOffset. pub fn get_str(&self, offset: DebugLineStrOffset) -> Result { let input = &mut self.section.clone(); input.skip(offset.0)?; input.read_null_terminated_slice() } } impl DebugLineStr { /// Create a `DebugLineStr` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLineStr where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugLineStr { fn id() -> SectionId { SectionId::DebugLineStr } fn reader(&self) -> &R { &self.section } } impl From for DebugLineStr { fn from(section: R) -> Self { DebugLineStr { section } } } #[cfg(test)] mod tests { use super::*; use crate::test_util::GimliSectionMethods; use crate::LittleEndian; use test_assembler::{Endian, Label, LabelMaker, Section}; #[test] fn test_get_str_offset() { for format in [Format::Dwarf32, Format::Dwarf64] { let zero = Label::new(); let length = Label::new(); let start = Label::new(); let first = Label::new(); let end = Label::new(); let mut section = Section::with_endian(Endian::Little) .mark(&zero) .initial_length(format, &length, &start) .D16(5) .D16(0) .mark(&first); for i in 0..20 { section = section.word(format.word_size(), 1000 + i); } section = section.mark(&end); length.set_const((&end - &start) as u64); let section = section.get_contents().unwrap(); let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(§ion, LittleEndian)); let base = DebugStrOffsetsBase((&first - &zero) as usize); assert_eq!( debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(0)), Ok(DebugStrOffset(1000)) ); assert_eq!( debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(19)), Ok(DebugStrOffset(1019)) ); } } } gimli-0.31.1/src/read/unit.rs000064400000000000000000006512451046102023000140530ustar 00000000000000//! Functions for parsing DWARF `.debug_info` and `.debug_types` sections. use core::cell::Cell; use core::ops::{Range, RangeFrom, RangeTo}; use crate::common::{ DebugAbbrevOffset, DebugAddrBase, DebugAddrIndex, DebugInfoOffset, DebugLineOffset, DebugLineStrOffset, DebugLocListsBase, DebugLocListsIndex, DebugMacinfoOffset, DebugMacroOffset, DebugRngListsBase, DebugRngListsIndex, DebugStrOffset, DebugStrOffsetsBase, DebugStrOffsetsIndex, DebugTypeSignature, DebugTypesOffset, DwoId, Encoding, Format, LocationListsOffset, RawRangeListsOffset, SectionId, UnitSectionOffset, }; use crate::constants; use crate::endianity::Endianity; use crate::read::abbrev::get_attribute_size; use crate::read::{ Abbreviation, Abbreviations, AttributeSpecification, DebugAbbrev, DebugStr, EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section, UnitOffset, }; impl DebugTypesOffset { /// Convert an offset to be relative to the start of the given unit, /// instead of relative to the start of the .debug_types section. /// Returns `None` if the offset is not within the unit entries. pub fn to_unit_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_types_offset()?; let offset = UnitOffset(self.0.checked_sub(unit_offset.0)?); if !unit.is_valid_offset(offset) { return None; } Some(offset) } } impl DebugInfoOffset { /// Convert an offset to be relative to the start of the given unit, /// instead of relative to the start of the .debug_info section. /// Returns `None` if the offset is not within this unit entries. pub fn to_unit_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_info_offset()?; let offset = UnitOffset(self.0.checked_sub(unit_offset.0)?); if !unit.is_valid_offset(offset) { return None; } Some(offset) } } impl UnitOffset { /// Convert an offset to be relative to the start of the .debug_info section, /// instead of relative to the start of the given unit. Returns None if the /// provided unit lives in the .debug_types section. pub fn to_debug_info_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_info_offset()?; Some(DebugInfoOffset(unit_offset.0 + self.0)) } /// Convert an offset to be relative to the start of the .debug_types section, /// instead of relative to the start of the given unit. Returns None if the /// provided unit lives in the .debug_info section. pub fn to_debug_types_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_types_offset()?; Some(DebugTypesOffset(unit_offset.0 + self.0)) } } /// The `DebugInfo` struct represents the DWARF debugging information found in /// the `.debug_info` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugInfo { debug_info_section: R, } impl<'input, Endian> DebugInfo> where Endian: Endianity, { /// Construct a new `DebugInfo` instance from the data in the `.debug_info` /// section. /// /// It is the caller's responsibility to read the `.debug_info` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugInfo, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_info_section_somehow = || &buf; /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); /// ``` pub fn new(debug_info_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_info_section, endian)) } } impl DebugInfo { /// Iterate the units in this `.debug_info` section. /// /// ``` /// use gimli::{DebugInfo, LittleEndian}; /// /// # let buf = []; /// # let read_debug_info_section_somehow = || &buf; /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); /// /// let mut iter = debug_info.units(); /// while let Some(unit) = iter.next().unwrap() { /// println!("unit's length is {}", unit.unit_length()); /// } /// ``` /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn units(&self) -> DebugInfoUnitHeadersIter { DebugInfoUnitHeadersIter { input: self.debug_info_section.clone(), offset: DebugInfoOffset(R::Offset::from_u8(0)), } } /// Get the UnitHeader located at offset from this .debug_info section. /// /// pub fn header_from_offset(&self, offset: DebugInfoOffset) -> Result> { let input = &mut self.debug_info_section.clone(); input.skip(offset.0)?; parse_unit_header(input, offset.into()) } } impl DebugInfo { /// Create a `DebugInfo` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugInfo where F: FnMut(&'a T) -> R, { borrow(&self.debug_info_section).into() } } impl Section for DebugInfo { fn id() -> SectionId { SectionId::DebugInfo } fn reader(&self) -> &R { &self.debug_info_section } } impl From for DebugInfo { fn from(debug_info_section: R) -> Self { DebugInfo { debug_info_section } } } /// An iterator over the units of a .debug_info section. /// /// See the [documentation on /// `DebugInfo::units`](./struct.DebugInfo.html#method.units) for more detail. #[derive(Clone, Debug)] pub struct DebugInfoUnitHeadersIter { input: R, offset: DebugInfoOffset, } impl DebugInfoUnitHeadersIter { /// Advance the iterator to the next unit header. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { Ok(None) } else { let len = self.input.len(); match parse_unit_header(&mut self.input, self.offset.into()) { Ok(header) => { self.offset.0 += len - self.input.len(); Ok(Some(header)) } Err(e) => { self.input.empty(); Err(e) } } } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for DebugInfoUnitHeadersIter { type Item = UnitHeader; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { DebugInfoUnitHeadersIter::next(self) } } /// Parse the unit type from the unit header. fn parse_unit_type(input: &mut R) -> Result { let val = input.read_u8()?; Ok(constants::DwUt(val)) } /// Parse the `debug_abbrev_offset` in the compilation unit header. fn parse_debug_abbrev_offset( input: &mut R, format: Format, ) -> Result> { input.read_offset(format).map(DebugAbbrevOffset) } /// Parse the `debug_info_offset` in the arange header. pub(crate) fn parse_debug_info_offset( input: &mut R, format: Format, ) -> Result> { input.read_offset(format).map(DebugInfoOffset) } /// This enum specifies the type of the unit and any type /// specific data carried in the header (e.g. the type /// signature/type offset of a type unit). #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UnitType where Offset: ReaderOffset, { /// In DWARF5, a unit with type `DW_UT_compile`. In previous DWARF versions, /// any unit appearing in the .debug_info section. Compilation, /// In DWARF5, a unit with type `DW_UT_type`. In DWARF4, any unit appearing /// in the .debug_types section. Type { /// The unique type signature for this type unit. type_signature: DebugTypeSignature, /// The offset within this type unit where the type is defined. type_offset: UnitOffset, }, /// A unit with type `DW_UT_partial`. The root DIE of this unit should be a /// `DW_TAG_partial_unit`. Partial, /// A unit with type `DW_UT_skeleton`. The enclosed dwo_id can be used to /// link this with the corresponding `SplitCompilation` unit in a dwo file. /// NB: The non-standard GNU split DWARF extension to DWARF 4 will instead /// be a `Compilation` unit with the dwo_id present as an attribute on the /// root DIE. Skeleton(DwoId), /// A unit with type `DW_UT_split_compile`. The enclosed dwo_id can be used to /// link this with the corresponding `Skeleton` unit in the original binary. /// NB: The non-standard GNU split DWARF extension to DWARF 4 will instead /// be a `Compilation` unit with the dwo_id present as an attribute on the /// root DIE. SplitCompilation(DwoId), /// A unit with type `DW_UT_split_type`. A split type unit is identical to a /// conventional type unit except for the section in which it appears. SplitType { /// The unique type signature for this type unit. type_signature: DebugTypeSignature, /// The offset within this type unit where the type is defined. type_offset: UnitOffset, }, } impl UnitType where Offset: ReaderOffset, { // TODO: This will be used by the DWARF writing code once it // supports unit types other than simple compilation units. #[allow(unused)] pub(crate) fn dw_ut(&self) -> constants::DwUt { match self { UnitType::Compilation => constants::DW_UT_compile, UnitType::Type { .. } => constants::DW_UT_type, UnitType::Partial => constants::DW_UT_partial, UnitType::Skeleton(_) => constants::DW_UT_skeleton, UnitType::SplitCompilation(_) => constants::DW_UT_split_compile, UnitType::SplitType { .. } => constants::DW_UT_split_type, } } } /// The common fields for the headers of compilation units and /// type units. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct UnitHeader::Offset> where R: Reader, Offset: ReaderOffset, { encoding: Encoding, unit_length: Offset, unit_type: UnitType, debug_abbrev_offset: DebugAbbrevOffset, unit_offset: UnitSectionOffset, entries_buf: R, } /// Static methods. impl UnitHeader where R: Reader, Offset: ReaderOffset, { /// Construct a new `UnitHeader`. pub fn new( encoding: Encoding, unit_length: Offset, unit_type: UnitType, debug_abbrev_offset: DebugAbbrevOffset, unit_offset: UnitSectionOffset, entries_buf: R, ) -> Self { UnitHeader { encoding, unit_length, unit_type, debug_abbrev_offset, unit_offset, entries_buf, } } } /// Instance methods. impl UnitHeader where R: Reader, Offset: ReaderOffset, { /// Get the offset of this unit within its section. pub fn offset(&self) -> UnitSectionOffset { self.unit_offset } /// Return the serialized size of the common unit header for the given /// DWARF format. pub fn size_of_header(&self) -> usize { let unit_length_size = self.encoding.format.initial_length_size() as usize; let version_size = 2; let debug_abbrev_offset_size = self.encoding.format.word_size() as usize; let address_size_size = 1; let unit_type_size = if self.encoding.version == 5 { 1 } else { 0 }; let type_specific_size = match self.unit_type { UnitType::Compilation | UnitType::Partial => 0, UnitType::Type { .. } | UnitType::SplitType { .. } => { let type_signature_size = 8; let type_offset_size = self.encoding.format.word_size() as usize; type_signature_size + type_offset_size } UnitType::Skeleton(_) | UnitType::SplitCompilation(_) => 8, }; unit_length_size + version_size + debug_abbrev_offset_size + address_size_size + unit_type_size + type_specific_size } /// Get the length of the debugging info for this compilation unit, not /// including the byte length of the encoded length itself. pub fn unit_length(&self) -> Offset { self.unit_length } /// Get the length of the debugging info for this compilation unit, /// including the byte length of the encoded length itself. pub fn length_including_self(&self) -> Offset { Offset::from_u8(self.format().initial_length_size()) + self.unit_length } /// Return the encoding parameters for this unit. pub fn encoding(&self) -> Encoding { self.encoding } /// Get the DWARF version of the debugging info for this compilation unit. pub fn version(&self) -> u16 { self.encoding.version } /// Get the UnitType of this unit. pub fn type_(&self) -> UnitType { self.unit_type } /// The offset into the `.debug_abbrev` section for this compilation unit's /// debugging information entries' abbreviations. pub fn debug_abbrev_offset(&self) -> DebugAbbrevOffset { self.debug_abbrev_offset } /// The size of addresses (in bytes) in this compilation unit. pub fn address_size(&self) -> u8 { self.encoding.address_size } /// Whether this compilation unit is encoded in 64- or 32-bit DWARF. pub fn format(&self) -> Format { self.encoding.format } /// The serialized size of the header for this compilation unit. pub fn header_size(&self) -> Offset { self.length_including_self() - self.entries_buf.len() } pub(crate) fn is_valid_offset(&self, offset: UnitOffset) -> bool { let size_of_header = self.header_size(); if offset.0 < size_of_header { return false; } let relative_to_entries_buf = offset.0 - size_of_header; relative_to_entries_buf < self.entries_buf.len() } /// Get the underlying bytes for the supplied range. pub fn range(&self, idx: Range>) -> Result { if !self.is_valid_offset(idx.start) { return Err(Error::OffsetOutOfBounds); } if !self.is_valid_offset(idx.end) { return Err(Error::OffsetOutOfBounds); } assert!(idx.start <= idx.end); let size_of_header = self.header_size(); let start = idx.start.0 - size_of_header; let end = idx.end.0 - size_of_header; let mut input = self.entries_buf.clone(); input.skip(start)?; input.truncate(end - start)?; Ok(input) } /// Get the underlying bytes for the supplied range. pub fn range_from(&self, idx: RangeFrom>) -> Result { if !self.is_valid_offset(idx.start) { return Err(Error::OffsetOutOfBounds); } let start = idx.start.0 - self.header_size(); let mut input = self.entries_buf.clone(); input.skip(start)?; Ok(input) } /// Get the underlying bytes for the supplied range. pub fn range_to(&self, idx: RangeTo>) -> Result { if !self.is_valid_offset(idx.end) { return Err(Error::OffsetOutOfBounds); } let end = idx.end.0 - self.header_size(); let mut input = self.entries_buf.clone(); input.truncate(end)?; Ok(input) } /// Read the `DebuggingInformationEntry` at the given offset. pub fn entry<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: UnitOffset, ) -> Result> { let mut input = self.range_from(offset..)?; let entry = DebuggingInformationEntry::parse(&mut input, self, abbreviations)?; entry.ok_or(Error::NoEntryAtGivenOffset) } /// Navigate this unit's `DebuggingInformationEntry`s. pub fn entries<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, ) -> EntriesCursor<'abbrev, 'me, R> { EntriesCursor { unit: self, input: self.entries_buf.clone(), abbreviations, cached_current: None, delta_depth: 0, } } /// Navigate this compilation unit's `DebuggingInformationEntry`s /// starting at the given offset. pub fn entries_at_offset<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: UnitOffset, ) -> Result> { let input = self.range_from(offset..)?; Ok(EntriesCursor { unit: self, input, abbreviations, cached_current: None, delta_depth: 0, }) } /// Navigate this unit's `DebuggingInformationEntry`s as a tree /// starting at the given offset. pub fn entries_tree<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: Option>, ) -> Result> { let input = match offset { Some(offset) => self.range_from(offset..)?, None => self.entries_buf.clone(), }; Ok(EntriesTree::new(input, self, abbreviations)) } /// Read the raw data that defines the Debugging Information Entries. pub fn entries_raw<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: Option>, ) -> Result> { let input = match offset { Some(offset) => self.range_from(offset..)?, None => self.entries_buf.clone(), }; Ok(EntriesRaw { input, unit: self, abbreviations, depth: 0, }) } /// Parse this unit's abbreviations. pub fn abbreviations(&self, debug_abbrev: &DebugAbbrev) -> Result { debug_abbrev.abbreviations(self.debug_abbrev_offset()) } } /// Parse a unit header. fn parse_unit_header( input: &mut R, unit_offset: UnitSectionOffset, ) -> Result> where R: Reader, Offset: ReaderOffset, { let (unit_length, format) = input.read_initial_length()?; let mut rest = input.split(unit_length)?; let version = rest.read_u16()?; let abbrev_offset; let address_size; let unit_type; // DWARF 1 was very different, and is obsolete, so isn't supported by this // reader. if 2 <= version && version <= 4 { abbrev_offset = parse_debug_abbrev_offset(&mut rest, format)?; address_size = rest.read_address_size()?; // Before DWARF5, all units in the .debug_info section are compilation // units, and all units in the .debug_types section are type units. unit_type = match unit_offset { UnitSectionOffset::DebugInfoOffset(_) => constants::DW_UT_compile, UnitSectionOffset::DebugTypesOffset(_) => constants::DW_UT_type, }; } else if version == 5 { unit_type = parse_unit_type(&mut rest)?; address_size = rest.read_address_size()?; abbrev_offset = parse_debug_abbrev_offset(&mut rest, format)?; } else { return Err(Error::UnknownVersion(u64::from(version))); } let encoding = Encoding { format, version, address_size, }; // Parse any data specific to this type of unit. let unit_type = match unit_type { constants::DW_UT_compile => UnitType::Compilation, constants::DW_UT_type => { let type_signature = parse_type_signature(&mut rest)?; let type_offset = parse_type_offset(&mut rest, format)?; UnitType::Type { type_signature, type_offset, } } constants::DW_UT_partial => UnitType::Partial, constants::DW_UT_skeleton => { let dwo_id = parse_dwo_id(&mut rest)?; UnitType::Skeleton(dwo_id) } constants::DW_UT_split_compile => { let dwo_id = parse_dwo_id(&mut rest)?; UnitType::SplitCompilation(dwo_id) } constants::DW_UT_split_type => { let type_signature = parse_type_signature(&mut rest)?; let type_offset = parse_type_offset(&mut rest, format)?; UnitType::SplitType { type_signature, type_offset, } } _ => return Err(Error::UnsupportedUnitType), }; Ok(UnitHeader::new( encoding, unit_length, unit_type, abbrev_offset, unit_offset, rest, )) } /// Parse a dwo_id from a header fn parse_dwo_id(input: &mut R) -> Result { Ok(DwoId(input.read_u64()?)) } /// A Debugging Information Entry (DIE). /// /// DIEs have a set of attributes and optionally have children DIEs as well. #[derive(Clone, Debug)] pub struct DebuggingInformationEntry<'abbrev, 'unit, R, Offset = ::Offset> where R: Reader, Offset: ReaderOffset, { offset: UnitOffset, attrs_slice: R, attrs_len: Cell>, abbrev: &'abbrev Abbreviation, unit: &'unit UnitHeader, } impl<'abbrev, 'unit, R, Offset> DebuggingInformationEntry<'abbrev, 'unit, R, Offset> where R: Reader, Offset: ReaderOffset, { /// Construct a new `DebuggingInformationEntry`. pub fn new( offset: UnitOffset, attrs_slice: R, abbrev: &'abbrev Abbreviation, unit: &'unit UnitHeader, ) -> Self { DebuggingInformationEntry { offset, attrs_slice, attrs_len: Cell::new(None), abbrev, unit, } } /// Get this entry's code. pub fn code(&self) -> u64 { self.abbrev.code() } /// Get this entry's offset. pub fn offset(&self) -> UnitOffset { self.offset } /// Get this entry's `DW_TAG_whatever` tag. /// /// ``` /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 12 /// # 0x0c, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # ]; /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_no /// # 0x00, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); /// # let unit = debug_info.units().next().unwrap().unwrap(); /// # let abbrevs = unit.abbreviations(&debug_abbrev).unwrap(); /// # let mut cursor = unit.entries(&abbrevs); /// # let (_, entry) = cursor.next_dfs().unwrap().unwrap(); /// # let mut get_some_entry = || entry; /// let entry = get_some_entry(); /// /// match entry.tag() { /// gimli::DW_TAG_subprogram => /// println!("this entry contains debug info about a function"), /// gimli::DW_TAG_inlined_subroutine => /// println!("this entry contains debug info about a particular instance of inlining"), /// gimli::DW_TAG_variable => /// println!("this entry contains debug info about a local variable"), /// gimli::DW_TAG_formal_parameter => /// println!("this entry contains debug info about a function parameter"), /// otherwise => /// println!("this entry is some other kind of data: {:?}", otherwise), /// }; /// ``` pub fn tag(&self) -> constants::DwTag { self.abbrev.tag() } /// Return true if this entry's type can have children, false otherwise. pub fn has_children(&self) -> bool { self.abbrev.has_children() } /// Iterate over this entry's set of attributes. /// /// ``` /// use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// /// // Read the `.debug_info` section. /// /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 12 /// # 0x0c, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # ]; /// # let read_debug_info_section_somehow = || &info_buf; /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); /// /// // Get the data about the first compilation unit out of the `.debug_info`. /// /// let unit = debug_info.units().next() /// .expect("Should have at least one compilation unit") /// .expect("and it should parse ok"); /// /// // Read the `.debug_abbrev` section and parse the /// // abbreviations for our compilation unit. /// /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_no /// # 0x00, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let read_debug_abbrev_section_somehow = || &abbrev_buf; /// let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); /// let abbrevs = unit.abbreviations(&debug_abbrev).unwrap(); /// /// // Get the first entry from that compilation unit. /// /// let mut cursor = unit.entries(&abbrevs); /// let (_, entry) = cursor.next_dfs() /// .expect("Should parse next entry") /// .expect("Should have at least one entry"); /// /// // Finally, print the first entry's attributes. /// /// let mut attrs = entry.attrs(); /// while let Some(attr) = attrs.next().unwrap() { /// println!("Attribute name = {:?}", attr.name()); /// println!("Attribute value = {:?}", attr.value()); /// } /// ``` /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn attrs<'me>(&'me self) -> AttrsIter<'abbrev, 'me, 'unit, R> { AttrsIter { input: self.attrs_slice.clone(), attributes: self.abbrev.attributes(), entry: self, } } /// Find the first attribute in this entry which has the given name, /// and return it. Returns `Ok(None)` if no attribute is found. pub fn attr(&self, name: constants::DwAt) -> Result>> { let mut attrs = self.attrs(); while let Some(attr) = attrs.next()? { if attr.name() == name { return Ok(Some(attr)); } } Ok(None) } /// Find the first attribute in this entry which has the given name, /// and return its raw value. Returns `Ok(None)` if no attribute is found. pub fn attr_value_raw(&self, name: constants::DwAt) -> Result>> { self.attr(name) .map(|attr| attr.map(|attr| attr.raw_value())) } /// Find the first attribute in this entry which has the given name, /// and return its normalized value. Returns `Ok(None)` if no /// attribute is found. pub fn attr_value(&self, name: constants::DwAt) -> Result>> { self.attr(name).map(|attr| attr.map(|attr| attr.value())) } /// Return the input buffer after the last attribute. #[inline(always)] fn after_attrs(&self) -> Result { if let Some(attrs_len) = self.attrs_len.get() { let mut input = self.attrs_slice.clone(); input.skip(attrs_len)?; Ok(input) } else { let mut attrs = self.attrs(); while attrs.next()?.is_some() {} Ok(attrs.input) } } /// Use the `DW_AT_sibling` attribute to find the input buffer for the /// next sibling. Returns `None` if the attribute is missing or invalid. fn sibling(&self) -> Option { let attr = self.attr_value(constants::DW_AT_sibling); if let Ok(Some(AttributeValue::UnitRef(offset))) = attr { if offset.0 > self.offset.0 { if let Ok(input) = self.unit.range_from(offset..) { return Some(input); } } } None } /// Parse an entry. Returns `Ok(None)` for null entries. #[inline(always)] fn parse( input: &mut R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, ) -> Result> { let offset = unit.header_size() + input.offset_from(&unit.entries_buf); let code = input.read_uleb128()?; if code == 0 { return Ok(None); }; let abbrev = abbreviations .get(code) .ok_or(Error::UnknownAbbreviation(code))?; Ok(Some(DebuggingInformationEntry { offset: UnitOffset(offset), attrs_slice: input.clone(), attrs_len: Cell::new(None), abbrev, unit, })) } } /// The value of an attribute in a `DebuggingInformationEntry`. // // Set the discriminant size so that all variants use the same alignment // for their data. This gives better code generation in `parse_attribute`. #[repr(u64)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum AttributeValue::Offset> where R: Reader, Offset: ReaderOffset, { /// "Refers to some location in the address space of the described program." Addr(u64), /// A slice of an arbitrary number of bytes. Block(R), /// A one byte constant data value. How to interpret the byte depends on context. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data1(u8), /// A two byte constant data value. How to interpret the bytes depends on context. /// /// These bytes have been converted from `R::Endian`. This may need to be reversed /// if this was not required. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data2(u16), /// A four byte constant data value. How to interpret the bytes depends on context. /// /// These bytes have been converted from `R::Endian`. This may need to be reversed /// if this was not required. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data4(u32), /// An eight byte constant data value. How to interpret the bytes depends on context. /// /// These bytes have been converted from `R::Endian`. This may need to be reversed /// if this was not required. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data8(u64), /// A signed integer constant. Sdata(i64), /// An unsigned integer constant. Udata(u64), /// "The information bytes contain a DWARF expression (see Section 2.5) or /// location description (see Section 2.6)." Exprloc(Expression), /// A boolean that indicates presence or absence of the attribute. Flag(bool), /// An offset into another section. Which section this is an offset into /// depends on context. SecOffset(Offset), /// An offset to a set of addresses in the `.debug_addr` section. DebugAddrBase(DebugAddrBase), /// An index into a set of addresses in the `.debug_addr` section. DebugAddrIndex(DebugAddrIndex), /// An offset into the current compilation unit. UnitRef(UnitOffset), /// An offset into the current `.debug_info` section, but possibly a /// different compilation unit from the current one. DebugInfoRef(DebugInfoOffset), /// An offset into the `.debug_info` section of the supplementary object file. DebugInfoRefSup(DebugInfoOffset), /// An offset into the `.debug_line` section. DebugLineRef(DebugLineOffset), /// An offset into either the `.debug_loc` section or the `.debug_loclists` section. LocationListsRef(LocationListsOffset), /// An offset to a set of offsets in the `.debug_loclists` section. DebugLocListsBase(DebugLocListsBase), /// An index into a set of offsets in the `.debug_loclists` section. DebugLocListsIndex(DebugLocListsIndex), /// An offset into the `.debug_macinfo` section. DebugMacinfoRef(DebugMacinfoOffset), /// An offset into the `.debug_macro` section. DebugMacroRef(DebugMacroOffset), /// An offset into the `.debug_ranges` section. RangeListsRef(RawRangeListsOffset), /// An offset to a set of offsets in the `.debug_rnglists` section. DebugRngListsBase(DebugRngListsBase), /// An index into a set of offsets in the `.debug_rnglists` section. DebugRngListsIndex(DebugRngListsIndex), /// A type signature. DebugTypesRef(DebugTypeSignature), /// An offset into the `.debug_str` section. DebugStrRef(DebugStrOffset), /// An offset into the `.debug_str` section of the supplementary object file. DebugStrRefSup(DebugStrOffset), /// An offset to a set of entries in the `.debug_str_offsets` section. DebugStrOffsetsBase(DebugStrOffsetsBase), /// An index into a set of entries in the `.debug_str_offsets` section. DebugStrOffsetsIndex(DebugStrOffsetsIndex), /// An offset into the `.debug_line_str` section. DebugLineStrRef(DebugLineStrOffset), /// A slice of bytes representing a string. Does not include a final null byte. /// Not guaranteed to be UTF-8 or anything like that. String(R), /// The value of a `DW_AT_encoding` attribute. Encoding(constants::DwAte), /// The value of a `DW_AT_decimal_sign` attribute. DecimalSign(constants::DwDs), /// The value of a `DW_AT_endianity` attribute. Endianity(constants::DwEnd), /// The value of a `DW_AT_accessibility` attribute. Accessibility(constants::DwAccess), /// The value of a `DW_AT_visibility` attribute. Visibility(constants::DwVis), /// The value of a `DW_AT_virtuality` attribute. Virtuality(constants::DwVirtuality), /// The value of a `DW_AT_language` attribute. Language(constants::DwLang), /// The value of a `DW_AT_address_class` attribute. AddressClass(constants::DwAddr), /// The value of a `DW_AT_identifier_case` attribute. IdentifierCase(constants::DwId), /// The value of a `DW_AT_calling_convention` attribute. CallingConvention(constants::DwCc), /// The value of a `DW_AT_inline` attribute. Inline(constants::DwInl), /// The value of a `DW_AT_ordering` attribute. Ordering(constants::DwOrd), /// An index into the filename entries from the line number information /// table for the compilation unit containing this value. FileIndex(u64), /// An implementation-defined identifier uniquely identifying a compilation /// unit. DwoId(DwoId), } /// An attribute in a `DebuggingInformationEntry`, consisting of a name and /// associated value. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Attribute { name: constants::DwAt, value: AttributeValue, } impl Attribute { /// Get this attribute's name. pub fn name(&self) -> constants::DwAt { self.name } /// Get this attribute's raw value. pub fn raw_value(&self) -> AttributeValue { self.value.clone() } /// Get this attribute's normalized value. /// /// Attribute values can potentially be encoded in multiple equivalent forms, /// and may have special meaning depending on the attribute name. This method /// converts the attribute value to a normalized form based on the attribute /// name. /// /// See "Table 7.5: Attribute encodings" and "Table 7.6: Attribute form encodings". pub fn value(&self) -> AttributeValue { // Table 7.5 shows the possible attribute classes for each name. // Table 7.6 shows the possible attribute classes for each form. // For each attribute name, we need to match on the form, and // convert it to one of the classes that is allowed for both // the name and the form. // // The individual class conversions rarely vary for each name, // so for each class conversion we define a macro that matches // on the allowed forms for that class. // // For some classes, we don't need to do any conversion, so their // macro is empty. In the future we may want to fill them in to // provide strict checking of the forms for each class. For now, // they simply provide a way to document the allowed classes for // each name. // DW_FORM_addr // DW_FORM_addrx // DW_FORM_addrx1 // DW_FORM_addrx2 // DW_FORM_addrx3 // DW_FORM_addrx4 macro_rules! address { () => {}; } // DW_FORM_sec_offset macro_rules! addrptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugAddrBase(DebugAddrBase(offset)); } }; } // DW_FORM_block // DW_FORM_block1 // DW_FORM_block2 // DW_FORM_block4 macro_rules! block { () => {}; } // DW_FORM_sdata // DW_FORM_udata // DW_FORM_data1 // DW_FORM_data2 // DW_FORM_data4 // DW_FORM_data8 // DW_FORM_data16 // DW_FORM_implicit_const macro_rules! constant { ($value:ident, $variant:ident) => { if let Some(value) = self.$value() { return AttributeValue::$variant(value); } }; ($value:ident, $variant:ident, $constant:ident) => { if let Some(value) = self.$value() { return AttributeValue::$variant(constants::$constant(value)); } }; } // DW_FORM_exprloc macro_rules! exprloc { () => { if let Some(value) = self.exprloc_value() { return AttributeValue::Exprloc(value); } }; } // DW_FORM_flag // DW_FORM_flag_present macro_rules! flag { () => {}; } // DW_FORM_sec_offset macro_rules! lineptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugLineRef(DebugLineOffset(offset)); } }; } // This also covers `loclist` in DWARF version 5. // DW_FORM_sec_offset // DW_FORM_loclistx macro_rules! loclistptr { () => { // DebugLocListsIndex is also an allowed form in DWARF version 5. if let Some(offset) = self.offset_value() { return AttributeValue::LocationListsRef(LocationListsOffset(offset)); } }; } // DW_FORM_sec_offset macro_rules! loclistsptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugLocListsBase(DebugLocListsBase(offset)); } }; } // DWARF version <= 4. // DW_FORM_sec_offset macro_rules! macinfoptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugMacinfoRef(DebugMacinfoOffset(offset)); } }; } // DWARF version >= 5. // DW_FORM_sec_offset macro_rules! macroptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugMacroRef(DebugMacroOffset(offset)); } }; } // DW_FORM_ref_addr // DW_FORM_ref1 // DW_FORM_ref2 // DW_FORM_ref4 // DW_FORM_ref8 // DW_FORM_ref_udata // DW_FORM_ref_sig8 // DW_FORM_ref_sup4 // DW_FORM_ref_sup8 macro_rules! reference { () => {}; } // This also covers `rnglist` in DWARF version 5. // DW_FORM_sec_offset // DW_FORM_rnglistx macro_rules! rangelistptr { () => { // DebugRngListsIndex is also an allowed form in DWARF version 5. if let Some(offset) = self.offset_value() { return AttributeValue::RangeListsRef(RawRangeListsOffset(offset)); } }; } // DW_FORM_sec_offset macro_rules! rnglistsptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugRngListsBase(DebugRngListsBase(offset)); } }; } // DW_FORM_string // DW_FORM_strp // DW_FORM_strx // DW_FORM_strx1 // DW_FORM_strx2 // DW_FORM_strx3 // DW_FORM_strx4 // DW_FORM_strp_sup // DW_FORM_line_strp macro_rules! string { () => {}; } // DW_FORM_sec_offset macro_rules! stroffsetsptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugStrOffsetsBase(DebugStrOffsetsBase(offset)); } }; } // This isn't a separate form but it's useful to distinguish it from a generic udata. macro_rules! dwoid { () => { if let Some(value) = self.udata_value() { return AttributeValue::DwoId(DwoId(value)); } }; } // Perform the allowed class conversions for each attribute name. match self.name { constants::DW_AT_sibling => { reference!(); } constants::DW_AT_location => { exprloc!(); loclistptr!(); } constants::DW_AT_name => { string!(); } constants::DW_AT_ordering => { constant!(u8_value, Ordering, DwOrd); } constants::DW_AT_byte_size | constants::DW_AT_bit_offset | constants::DW_AT_bit_size => { constant!(udata_value, Udata); exprloc!(); reference!(); } constants::DW_AT_stmt_list => { lineptr!(); } constants::DW_AT_low_pc => { address!(); } constants::DW_AT_high_pc => { address!(); constant!(udata_value, Udata); } constants::DW_AT_language => { constant!(u16_value, Language, DwLang); } constants::DW_AT_discr => { reference!(); } constants::DW_AT_discr_value => { // constant: depends on type of DW_TAG_variant_part, // so caller must normalize. } constants::DW_AT_visibility => { constant!(u8_value, Visibility, DwVis); } constants::DW_AT_import => { reference!(); } constants::DW_AT_string_length => { exprloc!(); loclistptr!(); reference!(); } constants::DW_AT_common_reference => { reference!(); } constants::DW_AT_comp_dir => { string!(); } constants::DW_AT_const_value => { // TODO: constant: sign depends on DW_AT_type. block!(); string!(); } constants::DW_AT_containing_type => { reference!(); } constants::DW_AT_default_value => { // TODO: constant: sign depends on DW_AT_type. reference!(); flag!(); } constants::DW_AT_inline => { constant!(u8_value, Inline, DwInl); } constants::DW_AT_is_optional => { flag!(); } constants::DW_AT_lower_bound => { // TODO: constant: sign depends on DW_AT_type. exprloc!(); reference!(); } constants::DW_AT_producer => { string!(); } constants::DW_AT_prototyped => { flag!(); } constants::DW_AT_return_addr => { exprloc!(); loclistptr!(); } constants::DW_AT_start_scope => { // TODO: constant rangelistptr!(); } constants::DW_AT_bit_stride => { constant!(udata_value, Udata); exprloc!(); reference!(); } constants::DW_AT_upper_bound => { // TODO: constant: sign depends on DW_AT_type. exprloc!(); reference!(); } constants::DW_AT_abstract_origin => { reference!(); } constants::DW_AT_accessibility => { constant!(u8_value, Accessibility, DwAccess); } constants::DW_AT_address_class => { constant!(udata_value, AddressClass, DwAddr); } constants::DW_AT_artificial => { flag!(); } constants::DW_AT_base_types => { reference!(); } constants::DW_AT_calling_convention => { constant!(u8_value, CallingConvention, DwCc); } constants::DW_AT_count => { // TODO: constant exprloc!(); reference!(); } constants::DW_AT_data_member_location => { // Constants must be handled before loclistptr so that DW_FORM_data4/8 // are correctly interpreted for DWARF version 4+. constant!(udata_value, Udata); exprloc!(); loclistptr!(); } constants::DW_AT_decl_column => { constant!(udata_value, Udata); } constants::DW_AT_decl_file => { constant!(udata_value, FileIndex); } constants::DW_AT_decl_line => { constant!(udata_value, Udata); } constants::DW_AT_declaration => { flag!(); } constants::DW_AT_discr_list => { block!(); } constants::DW_AT_encoding => { constant!(u8_value, Encoding, DwAte); } constants::DW_AT_external => { flag!(); } constants::DW_AT_frame_base => { exprloc!(); loclistptr!(); } constants::DW_AT_friend => { reference!(); } constants::DW_AT_identifier_case => { constant!(u8_value, IdentifierCase, DwId); } constants::DW_AT_macro_info => { macinfoptr!(); } constants::DW_AT_namelist_item => { reference!(); } constants::DW_AT_priority => { reference!(); } constants::DW_AT_segment => { exprloc!(); loclistptr!(); } constants::DW_AT_specification => { reference!(); } constants::DW_AT_static_link => { exprloc!(); loclistptr!(); } constants::DW_AT_type => { reference!(); } constants::DW_AT_use_location => { exprloc!(); loclistptr!(); } constants::DW_AT_variable_parameter => { flag!(); } constants::DW_AT_virtuality => { constant!(u8_value, Virtuality, DwVirtuality); } constants::DW_AT_vtable_elem_location => { exprloc!(); loclistptr!(); } constants::DW_AT_allocated => { // TODO: constant exprloc!(); reference!(); } constants::DW_AT_associated => { // TODO: constant exprloc!(); reference!(); } constants::DW_AT_data_location => { exprloc!(); } constants::DW_AT_byte_stride => { constant!(udata_value, Udata); exprloc!(); reference!(); } constants::DW_AT_entry_pc => { // TODO: constant address!(); } constants::DW_AT_use_UTF8 => { flag!(); } constants::DW_AT_extension => { reference!(); } constants::DW_AT_ranges => { rangelistptr!(); } constants::DW_AT_trampoline => { address!(); flag!(); reference!(); string!(); } constants::DW_AT_call_column => { constant!(udata_value, Udata); } constants::DW_AT_call_file => { constant!(udata_value, FileIndex); } constants::DW_AT_call_line => { constant!(udata_value, Udata); } constants::DW_AT_description => { string!(); } constants::DW_AT_binary_scale => { // TODO: constant } constants::DW_AT_decimal_scale => { // TODO: constant } constants::DW_AT_small => { reference!(); } constants::DW_AT_decimal_sign => { constant!(u8_value, DecimalSign, DwDs); } constants::DW_AT_digit_count => { // TODO: constant } constants::DW_AT_picture_string => { string!(); } constants::DW_AT_mutable => { flag!(); } constants::DW_AT_threads_scaled => { flag!(); } constants::DW_AT_explicit => { flag!(); } constants::DW_AT_object_pointer => { reference!(); } constants::DW_AT_endianity => { constant!(u8_value, Endianity, DwEnd); } constants::DW_AT_elemental => { flag!(); } constants::DW_AT_pure => { flag!(); } constants::DW_AT_recursive => { flag!(); } constants::DW_AT_signature => { reference!(); } constants::DW_AT_main_subprogram => { flag!(); } constants::DW_AT_data_bit_offset => { // TODO: constant } constants::DW_AT_const_expr => { flag!(); } constants::DW_AT_enum_class => { flag!(); } constants::DW_AT_linkage_name => { string!(); } constants::DW_AT_string_length_bit_size => { // TODO: constant } constants::DW_AT_string_length_byte_size => { // TODO: constant } constants::DW_AT_rank => { // TODO: constant exprloc!(); } constants::DW_AT_str_offsets_base => { stroffsetsptr!(); } constants::DW_AT_addr_base | constants::DW_AT_GNU_addr_base => { addrptr!(); } constants::DW_AT_rnglists_base | constants::DW_AT_GNU_ranges_base => { rnglistsptr!(); } constants::DW_AT_dwo_name => { string!(); } constants::DW_AT_reference => { flag!(); } constants::DW_AT_rvalue_reference => { flag!(); } constants::DW_AT_macros => { macroptr!(); } constants::DW_AT_call_all_calls => { flag!(); } constants::DW_AT_call_all_source_calls => { flag!(); } constants::DW_AT_call_all_tail_calls => { flag!(); } constants::DW_AT_call_return_pc => { address!(); } constants::DW_AT_call_value => { exprloc!(); } constants::DW_AT_call_origin => { exprloc!(); } constants::DW_AT_call_parameter => { reference!(); } constants::DW_AT_call_pc => { address!(); } constants::DW_AT_call_tail_call => { flag!(); } constants::DW_AT_call_target => { exprloc!(); } constants::DW_AT_call_target_clobbered => { exprloc!(); } constants::DW_AT_call_data_location => { exprloc!(); } constants::DW_AT_call_data_value => { exprloc!(); } constants::DW_AT_noreturn => { flag!(); } constants::DW_AT_alignment => { // TODO: constant } constants::DW_AT_export_symbols => { flag!(); } constants::DW_AT_deleted => { flag!(); } constants::DW_AT_defaulted => { // TODO: constant } constants::DW_AT_loclists_base => { loclistsptr!(); } constants::DW_AT_GNU_dwo_id => { dwoid!(); } _ => {} } self.value.clone() } /// Try to convert this attribute's value to a u8. #[inline] pub fn u8_value(&self) -> Option { self.value.u8_value() } /// Try to convert this attribute's value to a u16. #[inline] pub fn u16_value(&self) -> Option { self.value.u16_value() } /// Try to convert this attribute's value to an unsigned integer. #[inline] pub fn udata_value(&self) -> Option { self.value.udata_value() } /// Try to convert this attribute's value to a signed integer. #[inline] pub fn sdata_value(&self) -> Option { self.value.sdata_value() } /// Try to convert this attribute's value to an offset. #[inline] pub fn offset_value(&self) -> Option { self.value.offset_value() } /// Try to convert this attribute's value to an expression or location buffer. /// /// Expressions and locations may be `DW_FORM_block*` or `DW_FORM_exprloc`. /// The standard doesn't mention `DW_FORM_block*` as a possible form, but /// it is encountered in practice. #[inline] pub fn exprloc_value(&self) -> Option> { self.value.exprloc_value() } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. #[inline] pub fn string_value(&self, debug_str: &DebugStr) -> Option { self.value.string_value(debug_str) } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, or a `DW_FORM_strp_sup` reference to an offset into a supplementary /// object file, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. #[inline] pub fn string_value_sup( &self, debug_str: &DebugStr, debug_str_sup: Option<&DebugStr>, ) -> Option { self.value.string_value_sup(debug_str, debug_str_sup) } } impl AttributeValue where R: Reader, Offset: ReaderOffset, { /// Try to convert this attribute's value to a u8. pub fn u8_value(&self) -> Option { if let Some(value) = self.udata_value() { if value <= u64::from(u8::MAX) { return Some(value as u8); } } None } /// Try to convert this attribute's value to a u16. pub fn u16_value(&self) -> Option { if let Some(value) = self.udata_value() { if value <= u64::from(u16::MAX) { return Some(value as u16); } } None } /// Try to convert this attribute's value to an unsigned integer. pub fn udata_value(&self) -> Option { Some(match *self { AttributeValue::Data1(data) => u64::from(data), AttributeValue::Data2(data) => u64::from(data), AttributeValue::Data4(data) => u64::from(data), AttributeValue::Data8(data) => data, AttributeValue::Udata(data) => data, AttributeValue::Sdata(data) => { if data < 0 { // Maybe we should emit a warning here return None; } data as u64 } _ => return None, }) } /// Try to convert this attribute's value to a signed integer. pub fn sdata_value(&self) -> Option { Some(match *self { AttributeValue::Data1(data) => i64::from(data as i8), AttributeValue::Data2(data) => i64::from(data as i16), AttributeValue::Data4(data) => i64::from(data as i32), AttributeValue::Data8(data) => data as i64, AttributeValue::Sdata(data) => data, AttributeValue::Udata(data) => { if data > i64::MAX as u64 { // Maybe we should emit a warning here return None; } data as i64 } _ => return None, }) } /// Try to convert this attribute's value to an offset. pub fn offset_value(&self) -> Option { // While offsets will be DW_FORM_data4/8 in DWARF version 2/3, // these have already been converted to `SecOffset. if let AttributeValue::SecOffset(offset) = *self { Some(offset) } else { None } } /// Try to convert this attribute's value to an expression or location buffer. /// /// Expressions and locations may be `DW_FORM_block*` or `DW_FORM_exprloc`. /// The standard doesn't mention `DW_FORM_block*` as a possible form, but /// it is encountered in practice. pub fn exprloc_value(&self) -> Option> { Some(match *self { AttributeValue::Block(ref data) => Expression(data.clone()), AttributeValue::Exprloc(ref data) => data.clone(), _ => return None, }) } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. pub fn string_value(&self, debug_str: &DebugStr) -> Option { match *self { AttributeValue::String(ref string) => Some(string.clone()), AttributeValue::DebugStrRef(offset) => debug_str.get_str(offset).ok(), _ => None, } } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, or a `DW_FORM_strp_sup` reference to an offset into a supplementary /// object file, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. pub fn string_value_sup( &self, debug_str: &DebugStr, debug_str_sup: Option<&DebugStr>, ) -> Option { match *self { AttributeValue::String(ref string) => Some(string.clone()), AttributeValue::DebugStrRef(offset) => debug_str.get_str(offset).ok(), AttributeValue::DebugStrRefSup(offset) => { debug_str_sup.and_then(|s| s.get_str(offset).ok()) } _ => None, } } } fn length_u8_value(input: &mut R) -> Result { let len = input.read_u8().map(R::Offset::from_u8)?; input.split(len) } fn length_u16_value(input: &mut R) -> Result { let len = input.read_u16().map(R::Offset::from_u16)?; input.split(len) } fn length_u32_value(input: &mut R) -> Result { let len = input.read_u32().map(R::Offset::from_u32)?; input.split(len) } fn length_uleb128_value(input: &mut R) -> Result { let len = input.read_uleb128().and_then(R::Offset::from_u64)?; input.split(len) } // Return true if the given `name` can be a section offset in DWARF version 2/3. // This is required to correctly handle relocations. fn allow_section_offset(name: constants::DwAt, version: u16) -> bool { match name { constants::DW_AT_location | constants::DW_AT_stmt_list | constants::DW_AT_string_length | constants::DW_AT_return_addr | constants::DW_AT_start_scope | constants::DW_AT_frame_base | constants::DW_AT_macro_info | constants::DW_AT_macros | constants::DW_AT_segment | constants::DW_AT_static_link | constants::DW_AT_use_location | constants::DW_AT_vtable_elem_location | constants::DW_AT_ranges => true, constants::DW_AT_data_member_location => version == 2 || version == 3, _ => false, } } pub(crate) fn parse_attribute( input: &mut R, encoding: Encoding, spec: AttributeSpecification, ) -> Result> { let mut form = spec.form(); loop { let value = match form { constants::DW_FORM_indirect => { let dynamic_form = input.read_uleb128_u16()?; form = constants::DwForm(dynamic_form); continue; } constants::DW_FORM_addr => { let addr = input.read_address(encoding.address_size)?; AttributeValue::Addr(addr) } constants::DW_FORM_block1 => { let block = length_u8_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_block2 => { let block = length_u16_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_block4 => { let block = length_u32_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_block => { let block = length_uleb128_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_data1 => { let data = input.read_u8()?; AttributeValue::Data1(data) } constants::DW_FORM_data2 => { let data = input.read_u16()?; AttributeValue::Data2(data) } constants::DW_FORM_data4 => { // DWARF version 2/3 may use DW_FORM_data4/8 for section offsets. // Ensure we handle relocations here. if encoding.format == Format::Dwarf32 && allow_section_offset(spec.name(), encoding.version) { let offset = input.read_offset(Format::Dwarf32)?; AttributeValue::SecOffset(offset) } else { let data = input.read_u32()?; AttributeValue::Data4(data) } } constants::DW_FORM_data8 => { // DWARF version 2/3 may use DW_FORM_data4/8 for section offsets. // Ensure we handle relocations here. if encoding.format == Format::Dwarf64 && allow_section_offset(spec.name(), encoding.version) { let offset = input.read_offset(Format::Dwarf64)?; AttributeValue::SecOffset(offset) } else { let data = input.read_u64()?; AttributeValue::Data8(data) } } constants::DW_FORM_data16 => { let block = input.split(R::Offset::from_u8(16))?; AttributeValue::Block(block) } constants::DW_FORM_udata => { let data = input.read_uleb128()?; AttributeValue::Udata(data) } constants::DW_FORM_sdata => { let data = input.read_sleb128()?; AttributeValue::Sdata(data) } constants::DW_FORM_exprloc => { let block = length_uleb128_value(input)?; AttributeValue::Exprloc(Expression(block)) } constants::DW_FORM_flag => { let present = input.read_u8()?; AttributeValue::Flag(present != 0) } constants::DW_FORM_flag_present => { // FlagPresent is this weird compile time always true thing that // isn't actually present in the serialized DIEs, only in the abbreviation. AttributeValue::Flag(true) } constants::DW_FORM_sec_offset => { let offset = input.read_offset(encoding.format)?; AttributeValue::SecOffset(offset) } constants::DW_FORM_ref1 => { let reference = input.read_u8().map(R::Offset::from_u8)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref2 => { let reference = input.read_u16().map(R::Offset::from_u16)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref4 => { let reference = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref8 => { let reference = input.read_u64().and_then(R::Offset::from_u64)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref_udata => { let reference = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref_addr => { // This is an offset, but DWARF version 2 specifies that DW_FORM_ref_addr // has the same size as an address on the target system. This was changed // in DWARF version 3. let offset = if encoding.version == 2 { input.read_sized_offset(encoding.address_size)? } else { input.read_offset(encoding.format)? }; AttributeValue::DebugInfoRef(DebugInfoOffset(offset)) } constants::DW_FORM_ref_sig8 => { let signature = input.read_u64()?; AttributeValue::DebugTypesRef(DebugTypeSignature(signature)) } constants::DW_FORM_ref_sup4 => { let offset = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) } constants::DW_FORM_ref_sup8 => { let offset = input.read_u64().and_then(R::Offset::from_u64)?; AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) } constants::DW_FORM_GNU_ref_alt => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) } constants::DW_FORM_string => { let string = input.read_null_terminated_slice()?; AttributeValue::String(string) } constants::DW_FORM_strp => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugStrRef(DebugStrOffset(offset)) } constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugStrRefSup(DebugStrOffset(offset)) } constants::DW_FORM_line_strp => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset)) } constants::DW_FORM_implicit_const => { let data = spec .implicit_const_value() .ok_or(Error::InvalidImplicitConst)?; AttributeValue::Sdata(data) } constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx1 => { let index = input.read_u8().map(R::Offset::from_u8)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx2 => { let index = input.read_u16().map(R::Offset::from_u16)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx3 => { let index = input.read_uint(3).and_then(R::Offset::from_u64)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx4 => { let index = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_addrx | constants::DW_FORM_GNU_addr_index => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx1 => { let index = input.read_u8().map(R::Offset::from_u8)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx2 => { let index = input.read_u16().map(R::Offset::from_u16)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx3 => { let index = input.read_uint(3).and_then(R::Offset::from_u64)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx4 => { let index = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_loclistx => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugLocListsIndex(DebugLocListsIndex(index)) } constants::DW_FORM_rnglistx => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugRngListsIndex(DebugRngListsIndex(index)) } _ => { return Err(Error::UnknownForm(form)); } }; let attr = Attribute { name: spec.name(), value, }; return Ok(attr); } } pub(crate) fn skip_attributes( input: &mut R, encoding: Encoding, specs: &[AttributeSpecification], ) -> Result<()> { let mut skip_bytes = R::Offset::from_u8(0); for spec in specs { let mut form = spec.form(); loop { if let Some(len) = get_attribute_size(form, encoding) { // We know the length of this attribute. Accumulate that length. skip_bytes += R::Offset::from_u8(len); break; } // We have encountered a variable-length attribute. if skip_bytes != R::Offset::from_u8(0) { // Skip the accumulated skip bytes and then read the attribute normally. input.skip(skip_bytes)?; skip_bytes = R::Offset::from_u8(0); } match form { constants::DW_FORM_indirect => { let dynamic_form = input.read_uleb128_u16()?; form = constants::DwForm(dynamic_form); continue; } constants::DW_FORM_block1 => { skip_bytes = input.read_u8().map(R::Offset::from_u8)?; } constants::DW_FORM_block2 => { skip_bytes = input.read_u16().map(R::Offset::from_u16)?; } constants::DW_FORM_block4 => { skip_bytes = input.read_u32().map(R::Offset::from_u32)?; } constants::DW_FORM_block | constants::DW_FORM_exprloc => { skip_bytes = input.read_uleb128().and_then(R::Offset::from_u64)?; } constants::DW_FORM_string => { let _ = input.read_null_terminated_slice()?; } constants::DW_FORM_udata | constants::DW_FORM_sdata | constants::DW_FORM_ref_udata | constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index | constants::DW_FORM_addrx | constants::DW_FORM_GNU_addr_index | constants::DW_FORM_loclistx | constants::DW_FORM_rnglistx => { input.skip_leb128()?; } _ => { return Err(Error::UnknownForm(form)); } }; break; } } if skip_bytes != R::Offset::from_u8(0) { // Skip the remaining accumulated skip bytes. input.skip(skip_bytes)?; } Ok(()) } /// An iterator over a particular entry's attributes. /// /// See [the documentation for /// `DebuggingInformationEntry::attrs()`](./struct.DebuggingInformationEntry.html#method.attrs) /// for details. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[derive(Clone, Copy, Debug)] pub struct AttrsIter<'abbrev, 'entry, 'unit, R: Reader> { input: R, attributes: &'abbrev [AttributeSpecification], entry: &'entry DebuggingInformationEntry<'abbrev, 'unit, R>, } impl<'abbrev, 'entry, 'unit, R: Reader> AttrsIter<'abbrev, 'entry, 'unit, R> { /// Advance the iterator and return the next attribute. /// /// Returns `None` when iteration is finished. If an error /// occurs while parsing the next attribute, then this error /// is returned, and all subsequent calls return `None`. #[inline(always)] pub fn next(&mut self) -> Result>> { if self.attributes.is_empty() { // Now that we have parsed all of the attributes, we know where // either (1) this entry's children start, if the abbreviation says // this entry has children; or (2) where this entry's siblings // begin. if let Some(end) = self.entry.attrs_len.get() { debug_assert_eq!(end, self.input.offset_from(&self.entry.attrs_slice)); } else { self.entry .attrs_len .set(Some(self.input.offset_from(&self.entry.attrs_slice))); } return Ok(None); } let spec = self.attributes[0]; let rest_spec = &self.attributes[1..]; match parse_attribute(&mut self.input, self.entry.unit.encoding(), spec) { Ok(attr) => { self.attributes = rest_spec; Ok(Some(attr)) } Err(e) => { self.input.empty(); Err(e) } } } } #[cfg(feature = "fallible-iterator")] impl<'abbrev, 'entry, 'unit, R: Reader> fallible_iterator::FallibleIterator for AttrsIter<'abbrev, 'entry, 'unit, R> { type Item = Attribute; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { AttrsIter::next(self) } } /// A raw reader of the data that defines the Debugging Information Entries. /// /// `EntriesRaw` provides primitives to read the components of Debugging Information /// Entries (DIEs). A DIE consists of an abbreviation code (read with `read_abbreviation`) /// followed by a number of attributes (read with `read_attribute`). /// The user must provide the control flow to read these correctly. /// In particular, all attributes must always be read before reading another /// abbreviation code. /// /// `EntriesRaw` lacks some features of `EntriesCursor`, such as the ability to skip /// to the next sibling DIE. However, this also allows it to optimize better, since it /// does not need to perform the extra bookkeeping required to support these features, /// and thus it is suitable for cases where performance is important. /// /// ## Example Usage /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let debug_info = gimli::DebugInfo::new(&[], gimli::LittleEndian); /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// let unit = get_some_unit(); /// # let debug_abbrev = gimli::DebugAbbrev::new(&[], gimli::LittleEndian); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut entries = unit.entries_raw(&abbrevs, None)?; /// while !entries.is_empty() { /// let abbrev = if let Some(abbrev) = entries.read_abbreviation()? { /// abbrev /// } else { /// // Null entry with no attributes. /// continue /// }; /// match abbrev.tag() { /// gimli::DW_TAG_subprogram => { /// // Loop over attributes for DIEs we care about. /// for spec in abbrev.attributes() { /// let attr = entries.read_attribute(*spec)?; /// match attr.name() { /// // Handle attributes. /// _ => {} /// } /// } /// } /// _ => { /// // Skip attributes for DIEs we don't care about. /// entries.skip_attributes(abbrev.attributes()); /// } /// } /// } /// # unreachable!() /// # } /// ``` #[derive(Clone, Debug)] pub struct EntriesRaw<'abbrev, 'unit, R> where R: Reader, { input: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, depth: isize, } impl<'abbrev, 'unit, R: Reader> EntriesRaw<'abbrev, 'unit, R> { /// Return true if there is no more input. #[inline] pub fn is_empty(&self) -> bool { self.input.is_empty() } /// Return the unit offset at which the reader will read next. /// /// If you want the offset of the next entry, then this must be called prior to reading /// the next entry. pub fn next_offset(&self) -> UnitOffset { UnitOffset(self.unit.header_size() + self.input.offset_from(&self.unit.entries_buf)) } /// Return the depth of the next entry. /// /// This depth is updated when `read_abbreviation` is called, and is updated /// based on null entries and the `has_children` field in the abbreviation. #[inline] pub fn next_depth(&self) -> isize { self.depth } /// Read an abbreviation code and lookup the corresponding `Abbreviation`. /// /// Returns `Ok(None)` for null entries. #[inline] pub fn read_abbreviation(&mut self) -> Result> { let code = self.input.read_uleb128()?; if code == 0 { self.depth -= 1; return Ok(None); }; let abbrev = self .abbreviations .get(code) .ok_or(Error::UnknownAbbreviation(code))?; if abbrev.has_children() { self.depth += 1; } Ok(Some(abbrev)) } /// Read an attribute. #[inline] pub fn read_attribute(&mut self, spec: AttributeSpecification) -> Result> { parse_attribute(&mut self.input, self.unit.encoding(), spec) } /// Skip all the attributes of an abbreviation. #[inline] pub fn skip_attributes(&mut self, specs: &[AttributeSpecification]) -> Result<()> { skip_attributes(&mut self.input, self.unit.encoding(), specs) } } /// A cursor into the Debugging Information Entries tree for a compilation unit. /// /// The `EntriesCursor` can traverse the DIE tree in DFS order using `next_dfs()`, /// or skip to the next sibling of the entry the cursor is currently pointing to /// using `next_sibling()`. /// /// It is also possible to traverse the DIE tree at a lower abstraction level /// using `next_entry()`. This method does not skip over null entries, or provide /// any indication of the current tree depth. In this case, you must use `current()` /// to obtain the current entry, and `current().has_children()` to determine if /// the entry following the current entry will be a sibling or child. `current()` /// will return `None` if the current entry is a null entry, which signifies the /// end of the current tree depth. #[derive(Clone, Debug)] pub struct EntriesCursor<'abbrev, 'unit, R> where R: Reader, { input: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, cached_current: Option>, delta_depth: isize, } impl<'abbrev, 'unit, R: Reader> EntriesCursor<'abbrev, 'unit, R> { /// Get a reference to the entry that the cursor is currently pointing to. /// /// If the cursor is not pointing at an entry, or if the current entry is a /// null entry, then `None` is returned. #[inline] pub fn current(&self) -> Option<&DebuggingInformationEntry<'abbrev, 'unit, R>> { self.cached_current.as_ref() } /// Move the cursor to the next DIE in the tree. /// /// Returns `Some` if there is a next entry, even if this entry is null. /// If there is no next entry, then `None` is returned. pub fn next_entry(&mut self) -> Result> { if let Some(ref current) = self.cached_current { self.input = current.after_attrs()?; } if self.input.is_empty() { self.cached_current = None; self.delta_depth = 0; return Ok(None); } match DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations) { Ok(Some(entry)) => { self.delta_depth = entry.has_children() as isize; self.cached_current = Some(entry); Ok(Some(())) } Ok(None) => { self.delta_depth = -1; self.cached_current = None; Ok(Some(())) } Err(e) => { self.input.empty(); self.delta_depth = 0; self.cached_current = None; Err(e) } } } /// Move the cursor to the next DIE in the tree in DFS order. /// /// Upon successful movement of the cursor, return the delta traversal /// depth and the entry: /// /// * If we moved down into the previous current entry's children, we get /// `Some((1, entry))`. /// /// * If we moved to the previous current entry's sibling, we get /// `Some((0, entry))`. /// /// * If the previous entry does not have any siblings and we move up to /// its parent's next sibling, then we get `Some((-1, entry))`. Note that /// if the parent doesn't have a next sibling, then it could go up to the /// parent's parent's next sibling and return `Some((-2, entry))`, etc. /// /// If there is no next entry, then `None` is returned. /// /// Here is an example that finds the first entry in a compilation unit that /// does not have any children. /// /// ``` /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 25 /// # 0x19, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # ]; /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); /// # /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_yes /// # 0x01, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); /// # /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// /// let unit = get_some_unit(); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut first_entry_with_no_children = None; /// let mut cursor = unit.entries(&abbrevs); /// /// // Move the cursor to the root. /// assert!(cursor.next_dfs().unwrap().is_some()); /// /// // Traverse the DIE tree in depth-first search order. /// let mut depth = 0; /// while let Some((delta_depth, current)) = cursor.next_dfs().expect("Should parse next dfs") { /// // Update depth value, and break out of the loop when we /// // return to the original starting position. /// depth += delta_depth; /// if depth <= 0 { /// break; /// } /// /// first_entry_with_no_children = Some(current.clone()); /// } /// /// println!("The first entry with no children is {:?}", /// first_entry_with_no_children.unwrap()); /// ``` pub fn next_dfs( &mut self, ) -> Result)>> { let mut delta_depth = self.delta_depth; loop { // The next entry should be the one we want. if self.next_entry()?.is_some() { if let Some(ref entry) = self.cached_current { return Ok(Some((delta_depth, entry))); } // next_entry() read a null entry. delta_depth += self.delta_depth; } else { return Ok(None); } } } /// Move the cursor to the next sibling DIE of the current one. /// /// Returns `Ok(Some(entry))` when the cursor has been moved to /// the next sibling, `Ok(None)` when there is no next sibling. /// /// The depth of the cursor is never changed if this method returns `Ok`. /// Once `Ok(None)` is returned, this method will continue to return /// `Ok(None)` until either `next_entry` or `next_dfs` is called. /// /// Here is an example that iterates over all of the direct children of the /// root entry: /// /// ``` /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 25 /// # 0x19, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # ]; /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); /// # /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_yes /// # 0x01, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); /// # /// let unit = get_some_unit(); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut cursor = unit.entries(&abbrevs); /// /// // Move the cursor to the root. /// assert!(cursor.next_dfs().unwrap().is_some()); /// /// // Move the cursor to the root's first child. /// assert!(cursor.next_dfs().unwrap().is_some()); /// /// // Iterate the root's children. /// loop { /// { /// let current = cursor.current().expect("Should be at an entry"); /// println!("{:?} is a child of the root", current); /// } /// /// if cursor.next_sibling().expect("Should parse next sibling").is_none() { /// break; /// } /// } /// ``` pub fn next_sibling( &mut self, ) -> Result>> { if self.current().is_none() { // We're already at the null for the end of the sibling list. return Ok(None); } // Loop until we find an entry at the current level. let mut depth = 0; loop { // Use is_some() and unwrap() to keep borrow checker happy. if self.current().is_some() && self.current().unwrap().has_children() { if let Some(sibling_input) = self.current().unwrap().sibling() { // Fast path: this entry has a DW_AT_sibling // attribute pointing to its sibling, so jump // to it (which keeps us at the same depth). self.input = sibling_input; self.cached_current = None; } else { // This entry has children, so the next entry is // down one level. depth += 1; } } if self.next_entry()?.is_none() { // End of input. return Ok(None); } if depth == 0 { // Found an entry at the current level. return Ok(self.current()); } if self.current().is_none() { // A null entry means the end of a child list, so we're // back up a level. depth -= 1; } } } } /// The state information for a tree view of the Debugging Information Entries. /// /// The `EntriesTree` can be used to recursively iterate through the DIE /// tree, following the parent/child relationships. The `EntriesTree` contains /// shared state for all nodes in the tree, avoiding any duplicate parsing of /// entries during the traversal. /// /// ## Example Usage /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let debug_info = gimli::DebugInfo::new(&[], gimli::LittleEndian); /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// let unit = get_some_unit(); /// # let debug_abbrev = gimli::DebugAbbrev::new(&[], gimli::LittleEndian); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut tree = unit.entries_tree(&abbrevs, None)?; /// let root = tree.root()?; /// process_tree(root)?; /// # unreachable!() /// # } /// /// fn process_tree(mut node: gimli::EntriesTreeNode) -> gimli::Result<()> /// where R: gimli::Reader /// { /// { /// // Examine the entry attributes. /// let mut attrs = node.entry().attrs(); /// while let Some(attr) = attrs.next()? { /// } /// } /// let mut children = node.children(); /// while let Some(child) = children.next()? { /// // Recursively process a child. /// process_tree(child); /// } /// Ok(()) /// } /// ``` #[derive(Clone, Debug)] pub struct EntriesTree<'abbrev, 'unit, R> where R: Reader, { root: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, input: R, entry: Option>, depth: isize, } impl<'abbrev, 'unit, R: Reader> EntriesTree<'abbrev, 'unit, R> { fn new(root: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations) -> Self { let input = root.clone(); EntriesTree { root, unit, abbreviations, input, entry: None, depth: 0, } } /// Returns the root node of the tree. pub fn root<'me>(&'me mut self) -> Result> { self.input = self.root.clone(); self.entry = DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations)?; if self.entry.is_none() { return Err(Error::UnexpectedNull); } self.depth = 0; Ok(EntriesTreeNode::new(self, 1)) } /// Move the cursor to the next entry at the specified depth. /// /// Requires `depth <= self.depth + 1`. /// /// Returns `true` if successful. fn next(&mut self, depth: isize) -> Result { if self.depth < depth { debug_assert_eq!(self.depth + 1, depth); match self.entry { Some(ref entry) => { if !entry.has_children() { return Ok(false); } self.depth += 1; self.input = entry.after_attrs()?; } None => return Ok(false), } if self.input.is_empty() { self.entry = None; return Ok(false); } return match DebuggingInformationEntry::parse( &mut self.input, self.unit, self.abbreviations, ) { Ok(entry) => { self.entry = entry; Ok(self.entry.is_some()) } Err(e) => { self.input.empty(); self.entry = None; Err(e) } }; } loop { match self.entry { Some(ref entry) => { if entry.has_children() { if let Some(sibling_input) = entry.sibling() { // Fast path: this entry has a DW_AT_sibling // attribute pointing to its sibling, so jump // to it (which keeps us at the same depth). self.input = sibling_input; } else { // This entry has children, so the next entry is // down one level. self.depth += 1; self.input = entry.after_attrs()?; } } else { // This entry has no children, so next entry is at same depth. self.input = entry.after_attrs()?; } } None => { // This entry is a null, so next entry is up one level. self.depth -= 1; } } if self.input.is_empty() { self.entry = None; return Ok(false); } match DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations) { Ok(entry) => { self.entry = entry; if self.depth == depth { return Ok(self.entry.is_some()); } } Err(e) => { self.input.empty(); self.entry = None; return Err(e); } } } } } /// A node in the Debugging Information Entry tree. /// /// The root node of a tree can be obtained /// via [`EntriesTree::root`](./struct.EntriesTree.html#method.root). #[derive(Debug)] pub struct EntriesTreeNode<'abbrev, 'unit, 'tree, R: Reader> { tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, } impl<'abbrev, 'unit, 'tree, R: Reader> EntriesTreeNode<'abbrev, 'unit, 'tree, R> { fn new( tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, ) -> EntriesTreeNode<'abbrev, 'unit, 'tree, R> { debug_assert!(tree.entry.is_some()); EntriesTreeNode { tree, depth } } /// Returns the current entry in the tree. pub fn entry(&self) -> &DebuggingInformationEntry<'abbrev, 'unit, R> { // We never create a node without an entry. self.tree.entry.as_ref().unwrap() } /// Create an iterator for the children of the current entry. /// /// The current entry can no longer be accessed after creating the /// iterator. pub fn children(self) -> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { EntriesTreeIter::new(self.tree, self.depth) } } /// An iterator that allows traversal of the children of an /// `EntriesTreeNode`. /// /// The items returned by this iterator are also `EntriesTreeNode`s, /// which allow recursive traversal of grandchildren, etc. #[derive(Debug)] pub struct EntriesTreeIter<'abbrev, 'unit, 'tree, R: Reader> { tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, empty: bool, } impl<'abbrev, 'unit, 'tree, R: Reader> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { fn new( tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, ) -> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { EntriesTreeIter { tree, depth, empty: false, } } /// Returns an `EntriesTreeNode` for the next child entry. /// /// Returns `None` if there are no more children. pub fn next<'me>(&'me mut self) -> Result>> { if self.empty { Ok(None) } else if self.tree.next(self.depth)? { Ok(Some(EntriesTreeNode::new(self.tree, self.depth + 1))) } else { self.empty = true; Ok(None) } } } /// Parse a type unit header's unique type signature. Callers should handle /// unique-ness checking. fn parse_type_signature(input: &mut R) -> Result { input.read_u64().map(DebugTypeSignature) } /// Parse a type unit header's type offset. fn parse_type_offset(input: &mut R, format: Format) -> Result> { input.read_offset(format).map(UnitOffset) } /// The `DebugTypes` struct represents the DWARF type information /// found in the `.debug_types` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugTypes { debug_types_section: R, } impl<'input, Endian> DebugTypes> where Endian: Endianity, { /// Construct a new `DebugTypes` instance from the data in the `.debug_types` /// section. /// /// It is the caller's responsibility to read the `.debug_types` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugTypes, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_types_section_somehow = || &buf; /// let debug_types = DebugTypes::new(read_debug_types_section_somehow(), LittleEndian); /// ``` pub fn new(debug_types_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_types_section, endian)) } } impl DebugTypes { /// Create a `DebugTypes` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// Used by `DwarfSections::borrow`. pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTypes where F: FnMut(&'a T) -> R, { borrow(&self.debug_types_section).into() } } impl Section for DebugTypes { fn id() -> SectionId { SectionId::DebugTypes } fn reader(&self) -> &R { &self.debug_types_section } } impl From for DebugTypes { fn from(debug_types_section: R) -> Self { DebugTypes { debug_types_section, } } } impl DebugTypes { /// Iterate the type-units in this `.debug_types` section. /// /// ``` /// use gimli::{DebugTypes, LittleEndian}; /// /// # let buf = []; /// # let read_debug_types_section_somehow = || &buf; /// let debug_types = DebugTypes::new(read_debug_types_section_somehow(), LittleEndian); /// /// let mut iter = debug_types.units(); /// while let Some(unit) = iter.next().unwrap() { /// println!("unit's length is {}", unit.unit_length()); /// } /// ``` /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn units(&self) -> DebugTypesUnitHeadersIter { DebugTypesUnitHeadersIter { input: self.debug_types_section.clone(), offset: DebugTypesOffset(R::Offset::from_u8(0)), } } } /// An iterator over the type-units of this `.debug_types` section. /// /// See the [documentation on /// `DebugTypes::units`](./struct.DebugTypes.html#method.units) for /// more detail. #[derive(Clone, Debug)] pub struct DebugTypesUnitHeadersIter { input: R, offset: DebugTypesOffset, } impl DebugTypesUnitHeadersIter { /// Advance the iterator to the next type unit header. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { Ok(None) } else { let len = self.input.len(); match parse_unit_header(&mut self.input, self.offset.into()) { Ok(header) => { self.offset.0 += len - self.input.len(); Ok(Some(header)) } Err(e) => { self.input.empty(); Err(e) } } } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for DebugTypesUnitHeadersIter { type Item = UnitHeader; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { DebugTypesUnitHeadersIter::next(self) } } #[cfg(test)] // Tests require leb128::write. #[cfg(feature = "write")] mod tests { use super::*; use crate::constants; use crate::constants::*; use crate::endianity::{Endianity, LittleEndian}; use crate::leb128; use crate::read::abbrev::tests::AbbrevSectionMethods; use crate::read::{ Abbreviation, AttributeSpecification, DebugAbbrev, EndianSlice, Error, Result, }; use crate::test_util::GimliSectionMethods; use alloc::vec::Vec; use core::cell::Cell; use test_assembler::{Endian, Label, LabelMaker, Section}; // Mixin methods for `Section` to help define binary test data. trait UnitSectionMethods { fn unit(self, unit: &mut UnitHeader>) -> Self where E: Endianity; fn die(self, code: u64, attr: F) -> Self where F: Fn(Section) -> Section; fn die_null(self) -> Self; fn attr_string(self, s: &str) -> Self; fn attr_ref1(self, o: u8) -> Self; fn offset(self, offset: usize, format: Format) -> Self; } impl UnitSectionMethods for Section { fn unit(self, unit: &mut UnitHeader>) -> Self where E: Endianity, { let size = self.size(); let length = Label::new(); let start = Label::new(); let end = Label::new(); let section = match unit.format() { Format::Dwarf32 => self.L32(&length), Format::Dwarf64 => self.L32(0xffff_ffff).L64(&length), }; let section = match unit.version() { 2..=4 => section .mark(&start) .L16(unit.version()) .offset(unit.debug_abbrev_offset.0, unit.format()) .D8(unit.address_size()), 5 => section .mark(&start) .L16(unit.version()) .D8(unit.type_().dw_ut().0) .D8(unit.address_size()) .offset(unit.debug_abbrev_offset.0, unit.format()), _ => unreachable!(), }; let section = match unit.type_() { UnitType::Compilation | UnitType::Partial => { unit.unit_offset = DebugInfoOffset(size as usize).into(); section } UnitType::Type { type_signature, type_offset, } | UnitType::SplitType { type_signature, type_offset, } => { if unit.version() == 5 { unit.unit_offset = DebugInfoOffset(size as usize).into(); } else { unit.unit_offset = DebugTypesOffset(size as usize).into(); } section .L64(type_signature.0) .offset(type_offset.0, unit.format()) } UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => { unit.unit_offset = DebugInfoOffset(size as usize).into(); section.L64(dwo_id.0) } }; let section = section.append_bytes(unit.entries_buf.slice()).mark(&end); unit.unit_length = (&end - &start) as usize; length.set_const(unit.unit_length as u64); section } fn die(self, code: u64, attr: F) -> Self where F: Fn(Section) -> Section, { let section = self.uleb(code); attr(section) } fn die_null(self) -> Self { self.D8(0) } fn attr_string(self, attr: &str) -> Self { self.append_bytes(attr.as_bytes()).D8(0) } fn attr_ref1(self, attr: u8) -> Self { self.D8(attr) } fn offset(self, offset: usize, format: Format) -> Self { match format { Format::Dwarf32 => self.L32(offset as u32), Format::Dwarf64 => self.L64(offset as u64), } } } /// Ensure that `UnitHeader` is covariant wrt R. #[test] fn test_unit_header_variance() { /// This only needs to compile. fn _f<'a: 'b, 'b, E: Endianity>( x: UnitHeader>, ) -> UnitHeader> { x } } #[test] fn test_parse_debug_abbrev_offset_32() { let section = Section::with_endian(Endian::Little).L32(0x0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf32) { Ok(val) => assert_eq!(val, DebugAbbrevOffset(0x0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_abbrev_offset_32_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf32) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_debug_abbrev_offset_64() { let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf64) { Ok(val) => assert_eq!(val, DebugAbbrevOffset(0x0807_0605_0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_abbrev_offset_64_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf64) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_info_offset_32() { let section = Section::with_endian(Endian::Little).L32(0x0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf32) { Ok(val) => assert_eq!(val, DebugInfoOffset(0x0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_info_offset_32_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf32) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_debug_info_offset_64() { let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf64) { Ok(val) => assert_eq!(val, DebugInfoOffset(0x0807_0605_0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_info_offset_64_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf64) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "64")] fn test_units() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let mut unit64 = UnitHeader { encoding: Encoding { format: Format::Dwarf64, version: 4, address_size: 8, }, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let mut unit32 = UnitHeader { encoding: Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut unit64) .unit(&mut unit32); let buf = section.get_contents().unwrap(); let debug_info = DebugInfo::new(&buf, LittleEndian); let mut units = debug_info.units(); assert_eq!(units.next(), Ok(Some(unit64))); assert_eq!(units.next(), Ok(Some(unit32))); assert_eq!(units.next(), Ok(None)); } #[test] fn test_unit_version_unknown_version() { let buf = [0x02, 0x00, 0x00, 0x00, 0xab, 0xcd]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_unit_header(rest, DebugInfoOffset(0).into()) { Err(Error::UnknownVersion(0xcdab)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; let buf = [0x02, 0x00, 0x00, 0x00, 0x1, 0x0]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_unit_header(rest, DebugInfoOffset(0).into()) { Err(Error::UnknownVersion(1)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_unit_version_incomplete() { let buf = [0x01, 0x00, 0x00, 0x00, 0x04]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_unit_header(rest, DebugInfoOffset(0).into()) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 4, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_partial_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Partial, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_partial_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Partial, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_skeleton_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_skeleton_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_split_compilation_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_split_compilation_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_type_offset_32_ok() { let buf = [0x12, 0x34, 0x56, 0x78, 0x00]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_type_offset(rest, Format::Dwarf32) { Ok(offset) => { assert_eq!(rest.len(), 1); assert_eq!(UnitOffset(0x7856_3412), offset); } otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_type_offset_64_ok() { let buf = [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0x00]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_type_offset(rest, Format::Dwarf64) { Ok(offset) => { assert_eq!(rest.len(), 1); assert_eq!(UnitOffset(0xffde_bc9a_7856_3412), offset); } otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] fn test_parse_type_offset_incomplete() { // Need at least 4 bytes. let buf = [0xff, 0xff, 0xff]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_type_offset(rest, Format::Dwarf32) { Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_type_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugTypesOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_type_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 4, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412_7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugTypesOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_type_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_type_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412_7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_split_type_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitType { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_split_type_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitType { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412_7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } fn section_contents(f: F) -> Vec where F: Fn(Section) -> Section, { f(Section::with_endian(Endian::Little)) .get_contents() .unwrap() } #[test] fn test_attribute_value() { let mut unit = test_parse_attribute_unit_default(); let endian = unit.entries_buf.endian(); let block_data = &[1, 2, 3, 4]; let buf = section_contents(|s| s.uleb(block_data.len() as u64).append_bytes(block_data)); let block = EndianSlice::new(&buf, endian); let buf = section_contents(|s| s.L32(0x0102_0304)); let data4 = EndianSlice::new(&buf, endian); let buf = section_contents(|s| s.L64(0x0102_0304_0506_0708)); let data8 = EndianSlice::new(&buf, endian); let tests = [ ( Format::Dwarf32, 2, constants::DW_AT_data_member_location, constants::DW_FORM_block, block, AttributeValue::Block(EndianSlice::new(block_data, endian)), AttributeValue::Exprloc(Expression(EndianSlice::new(block_data, endian))), ), ( Format::Dwarf32, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data4, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304)), ), ( Format::Dwarf64, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data4, data4, AttributeValue::Data4(0x0102_0304), AttributeValue::Udata(0x0102_0304), ), ( Format::Dwarf32, 4, constants::DW_AT_data_member_location, constants::DW_FORM_data4, data4, AttributeValue::Data4(0x0102_0304), AttributeValue::Udata(0x0102_0304), ), ( Format::Dwarf32, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data8, data8, AttributeValue::Data8(0x0102_0304_0506_0708), AttributeValue::Udata(0x0102_0304_0506_0708), ), #[cfg(target_pointer_width = "64")] ( Format::Dwarf64, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data8, data8, AttributeValue::SecOffset(0x0102_0304_0506_0708), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304_0506_0708)), ), ( Format::Dwarf64, 4, constants::DW_AT_data_member_location, constants::DW_FORM_data8, data8, AttributeValue::Data8(0x0102_0304_0506_0708), AttributeValue::Udata(0x0102_0304_0506_0708), ), ( Format::Dwarf32, 4, constants::DW_AT_location, constants::DW_FORM_data4, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304)), ), #[cfg(target_pointer_width = "64")] ( Format::Dwarf64, 4, constants::DW_AT_location, constants::DW_FORM_data8, data8, AttributeValue::SecOffset(0x0102_0304_0506_0708), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304_0506_0708)), ), ( Format::Dwarf32, 4, constants::DW_AT_str_offsets_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugStrOffsetsBase(DebugStrOffsetsBase(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_stmt_list, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugLineRef(DebugLineOffset(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_addr_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugAddrBase(DebugAddrBase(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_rnglists_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugRngListsBase(DebugRngListsBase(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_loclists_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugLocListsBase(DebugLocListsBase(0x0102_0304)), ), ]; for test in tests.iter() { let (format, version, name, form, mut input, expect_raw, expect_value) = *test; unit.encoding.format = format; unit.encoding.version = version; let spec = AttributeSpecification::new(name, form, None); let attribute = parse_attribute(&mut input, unit.encoding(), spec).expect("Should parse attribute"); assert_eq!(attribute.raw_value(), expect_raw); assert_eq!(attribute.value(), expect_value); } } #[test] fn test_attribute_udata_sdata_value() { #[allow(clippy::type_complexity)] let tests: &[( AttributeValue>, Option, Option, )] = &[ (AttributeValue::Data1(1), Some(1), Some(1)), ( AttributeValue::Data1(u8::MAX), Some(u64::from(u8::MAX)), Some(-1), ), (AttributeValue::Data2(1), Some(1), Some(1)), ( AttributeValue::Data2(u16::MAX), Some(u64::from(u16::MAX)), Some(-1), ), (AttributeValue::Data4(1), Some(1), Some(1)), ( AttributeValue::Data4(u32::MAX), Some(u64::from(u32::MAX)), Some(-1), ), (AttributeValue::Data8(1), Some(1), Some(1)), (AttributeValue::Data8(u64::MAX), Some(u64::MAX), Some(-1)), (AttributeValue::Sdata(1), Some(1), Some(1)), (AttributeValue::Sdata(-1), None, Some(-1)), (AttributeValue::Udata(1), Some(1), Some(1)), (AttributeValue::Udata(1u64 << 63), Some(1u64 << 63), None), ]; for test in tests.iter() { let (value, expect_udata, expect_sdata) = *test; let attribute = Attribute { name: DW_AT_data_member_location, value, }; assert_eq!(attribute.udata_value(), expect_udata); assert_eq!(attribute.sdata_value(), expect_sdata); } } fn test_parse_attribute_unit( address_size: u8, format: Format, endian: Endian, ) -> UnitHeader> where Endian: Endianity, { let encoding = Encoding { format, version: 4, address_size, }; UnitHeader::new( encoding, 7, UnitType::Compilation, DebugAbbrevOffset(0x0807_0605), DebugInfoOffset(0).into(), EndianSlice::new(&[], endian), ) } fn test_parse_attribute_unit_default() -> UnitHeader> { test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian) } fn test_parse_attribute<'input, Endian>( buf: &'input [u8], len: usize, unit: &UnitHeader>, form: constants::DwForm, value: AttributeValue>, ) where Endian: Endianity, { let spec = AttributeSpecification::new(constants::DW_AT_low_pc, form, None); let expect = Attribute { name: constants::DW_AT_low_pc, value, }; let rest = &mut EndianSlice::new(buf, Endian::default()); match parse_attribute(rest, unit.encoding(), spec) { Ok(attr) => { assert_eq!(attr, expect); assert_eq!(*rest, EndianSlice::new(&buf[len..], Endian::default())); if let Some(size) = spec.size(unit) { assert_eq!(rest.len() + size, buf.len()); } } otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } }; } #[test] fn test_parse_attribute_addr() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_addr; let value = AttributeValue::Addr(0x0403_0201); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_addr8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; let unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_addr; let value = AttributeValue::Addr(0x0807_0605_0403_0201); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_block1() { // Length of data (3), three bytes of data, two bytes of left over input. let buf = [0x03, 0x09, 0x09, 0x09, 0x00, 0x00]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block1; let value = AttributeValue::Block(EndianSlice::new(&buf[1..4], LittleEndian)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_block2() { // Two byte length of data (2), two bytes of data, two bytes of left over input. let buf = [0x02, 0x00, 0x09, 0x09, 0x00, 0x00]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block2; let value = AttributeValue::Block(EndianSlice::new(&buf[2..4], LittleEndian)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_block4() { // Four byte length of data (2), two bytes of data, no left over input. let buf = [0x02, 0x00, 0x00, 0x00, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block4; let value = AttributeValue::Block(EndianSlice::new(&buf[4..], LittleEndian)); test_parse_attribute(&buf, 6, &unit, form, value); } #[test] fn test_parse_attribute_block() { // LEB length of data (2, one byte), two bytes of data, no left over input. let buf = [0x02, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block; let value = AttributeValue::Block(EndianSlice::new(&buf[1..], LittleEndian)); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_data1() { let buf = [0x03]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data1; let value = AttributeValue::Data1(0x03); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_data2() { let buf = [0x02, 0x01, 0x0]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data2; let value = AttributeValue::Data2(0x0102); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_data4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data4; let value = AttributeValue::Data4(0x0403_0201); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_data8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data8; let value = AttributeValue::Data8(0x0807_0605_0403_0201); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_udata() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_udata; let value = AttributeValue::Udata(4097); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_sdata() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::signed(&mut writable, -4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_sdata; let value = AttributeValue::Sdata(-4097); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_exprloc() { // LEB length of data (2, one byte), two bytes of data, one byte left over input. let buf = [0x02, 0x99, 0x99, 0x11]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_exprloc; let value = AttributeValue::Exprloc(Expression(EndianSlice::new(&buf[1..3], LittleEndian))); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_flag_true() { let buf = [0x42]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_flag; let value = AttributeValue::Flag(true); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_flag_false() { let buf = [0x00]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_flag; let value = AttributeValue::Flag(false); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_flag_present() { let buf = [0x01, 0x02, 0x03, 0x04]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_flag_present; let value = AttributeValue::Flag(true); // DW_FORM_flag_present does not consume any bytes of the input stream. test_parse_attribute(&buf, 0, &unit, form, value); } #[test] fn test_parse_attribute_sec_offset_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_sec_offset; let value = AttributeValue::SecOffset(0x0403_0201); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_sec_offset_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_sec_offset; let value = AttributeValue::SecOffset(0x0807_0605_0403_0201); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_ref1() { let buf = [0x03]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref1; let value = AttributeValue::UnitRef(UnitOffset(3)); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_ref2() { let buf = [0x02, 0x01, 0x0]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref2; let value = AttributeValue::UnitRef(UnitOffset(258)); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_ref4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref4; let value = AttributeValue::UnitRef(UnitOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_ref8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref8; let value = AttributeValue::UnitRef(UnitOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_ref_sup4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_sup4; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_ref_sup8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_sup8; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_refudata() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_udata; let value = AttributeValue::UnitRef(UnitOffset(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_refaddr_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_refaddr_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_refaddr_version2() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let mut unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); unit.encoding.version = 2; let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_refaddr8_version2() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let mut unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); unit.encoding.version = 2; let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_gnu_ref_alt_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_GNU_ref_alt; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_gnu_ref_alt_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_GNU_ref_alt; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_refsig8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_sig8; let value = AttributeValue::DebugTypesRef(DebugTypeSignature(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_string() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_string; let value = AttributeValue::String(EndianSlice::new(&buf[..5], LittleEndian)); test_parse_attribute(&buf, 6, &unit, form, value); } #[test] fn test_parse_attribute_strp_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_strp; let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_strp_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strp; let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_strp_sup_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_strp_sup; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_strp_sup_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strp_sup; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_gnu_strp_alt_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_GNU_strp_alt; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_gnu_strp_alt_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_GNU_strp_alt; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_strx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_strx; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_strx1() { let buf = [0x01, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx1; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x01)); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_strx2() { let buf = [0x01, 0x02, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx2; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0201)); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_strx3() { let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx3; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x03_0201)); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_strx4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx4; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_addrx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_addrx; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_addrx1() { let buf = [0x01, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx1; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x01)); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_addrx2() { let buf = [0x01, 0x02, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx2; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0201)); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_addrx3() { let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx3; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x03_0201)); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_addrx4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx4; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_loclistx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_loclistx; let value = AttributeValue::DebugLocListsIndex(DebugLocListsIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_rnglistx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_rnglistx; let value = AttributeValue::DebugRngListsIndex(DebugRngListsIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_indirect() { let mut buf = [0; 100]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, constants::DW_FORM_udata.0.into()) .expect("should write udata") + leb128::write::unsigned(&mut writable, 9_999_999).expect("should write value") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_indirect; let value = AttributeValue::Udata(9_999_999); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_indirect_implicit_const() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut buf = [0; 100]; let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, constants::DW_FORM_implicit_const.0.into()) .expect("should write implicit_const"); let input = &mut EndianSlice::new(&buf, LittleEndian); let spec = AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_indirect, None); assert_eq!( parse_attribute(input, encoding, spec), Err(Error::InvalidImplicitConst) ); } #[test] fn test_attrs_iter() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let unit = UnitHeader::new( encoding, 7, UnitType::Compilation, DebugAbbrevOffset(0x0807_0605), DebugInfoOffset(0).into(), EndianSlice::new(&[], LittleEndian), ); let abbrev = Abbreviation::new( 42, constants::DW_TAG_subprogram, constants::DW_CHILDREN_yes, vec![ AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), AttributeSpecification::new( constants::DW_AT_high_pc, constants::DW_FORM_addr, None, ), ] .into(), ); // "foo", 42, 1337, 4 dangling bytes of 0xaa where children would be let buf = [ 0x66, 0x6f, 0x6f, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x39, 0x05, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, ]; let entry = DebuggingInformationEntry { offset: UnitOffset(0), attrs_slice: EndianSlice::new(&buf, LittleEndian), attrs_len: Cell::new(None), abbrev: &abbrev, unit: &unit, }; let mut attrs = AttrsIter { input: EndianSlice::new(&buf, LittleEndian), attributes: abbrev.attributes(), entry: &entry, }; match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_name, value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), } ); } otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_low_pc, value: AttributeValue::Addr(0x2a), } ); } otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_high_pc, value: AttributeValue::Addr(0x539), } ); } otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); assert!(attrs.next().expect("should parse next").is_none()); assert!(entry.attrs_len.get().is_some()); assert_eq!( entry.attrs_len.get().expect("should have entry.attrs_len"), buf.len() - 4 ) } #[test] fn test_attrs_iter_incomplete() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let unit = UnitHeader::new( encoding, 7, UnitType::Compilation, DebugAbbrevOffset(0x0807_0605), DebugInfoOffset(0).into(), EndianSlice::new(&[], LittleEndian), ); let abbrev = Abbreviation::new( 42, constants::DW_TAG_subprogram, constants::DW_CHILDREN_yes, vec![ AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), AttributeSpecification::new( constants::DW_AT_high_pc, constants::DW_FORM_addr, None, ), ] .into(), ); // "foo" let buf = [0x66, 0x6f, 0x6f, 0x00]; let entry = DebuggingInformationEntry { offset: UnitOffset(0), attrs_slice: EndianSlice::new(&buf, LittleEndian), attrs_len: Cell::new(None), abbrev: &abbrev, unit: &unit, }; let mut attrs = AttrsIter { input: EndianSlice::new(&buf, LittleEndian), attributes: abbrev.attributes(), entry: &entry, }; match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_name, value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), } ); } otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); // Return error for incomplete attribute. assert!(attrs.next().is_err()); assert!(entry.attrs_len.get().is_none()); // Return error for all subsequent calls. assert!(attrs.next().is_err()); assert!(attrs.next().is_err()); assert!(attrs.next().is_err()); assert!(attrs.next().is_err()); assert!(entry.attrs_len.get().is_none()); } fn assert_entry_name( entry: &DebuggingInformationEntry<'_, '_, EndianSlice<'_, Endian>>, name: &str, ) where Endian: Endianity, { let value = entry .attr_value(constants::DW_AT_name) .expect("Should have parsed the name attribute") .expect("Should have found the name attribute"); assert_eq!( value, AttributeValue::String(EndianSlice::new(name.as_bytes(), Endian::default())) ); } fn assert_current_name( cursor: &EntriesCursor<'_, '_, EndianSlice<'_, Endian>>, name: &str, ) where Endian: Endianity, { let entry = cursor.current().expect("Should have an entry result"); assert_entry_name(entry, name); } fn assert_next_entry( cursor: &mut EntriesCursor<'_, '_, EndianSlice<'_, Endian>>, name: &str, ) where Endian: Endianity, { cursor .next_entry() .expect("Should parse next entry") .expect("Should have an entry"); assert_current_name(cursor, name); } fn assert_next_entry_null(cursor: &mut EntriesCursor<'_, '_, EndianSlice<'_, Endian>>) where Endian: Endianity, { cursor .next_entry() .expect("Should parse next entry") .expect("Should have an entry"); assert!(cursor.current().is_none()); } fn assert_next_dfs( cursor: &mut EntriesCursor<'_, '_, EndianSlice<'_, Endian>>, name: &str, depth: isize, ) where Endian: Endianity, { { let (val, entry) = cursor .next_dfs() .expect("Should parse next dfs") .expect("Should not be done with traversal"); assert_eq!(val, depth); assert_entry_name(entry, name); } assert_current_name(cursor, name); } fn assert_next_sibling( cursor: &mut EntriesCursor<'_, '_, EndianSlice<'_, Endian>>, name: &str, ) where Endian: Endianity, { { let entry = cursor .next_sibling() .expect("Should parse next sibling") .expect("Should not be done with traversal"); assert_entry_name(entry, name); } assert_current_name(cursor, name); } fn assert_valid_sibling_ptr(cursor: &EntriesCursor<'_, '_, EndianSlice<'_, Endian>>) where Endian: Endianity, { let sibling_ptr = cursor .current() .expect("Should have current entry") .attr_value(constants::DW_AT_sibling); match sibling_ptr { Ok(Some(AttributeValue::UnitRef(offset))) => { cursor .unit .range_from(offset..) .expect("Sibling offset should be valid"); } _ => panic!("Invalid sibling pointer {:?}", sibling_ptr), } } fn entries_cursor_tests_abbrev_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null(); section.get_contents().unwrap() } fn entries_cursor_tests_debug_info_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .die(1, |s| s.attr_string("001")) .die(1, |s| s.attr_string("002")) .die(1, |s| s.attr_string("003")) .die_null() .die_null() .die(1, |s| s.attr_string("004")) .die(1, |s| s.attr_string("005")) .die_null() .die(1, |s| s.attr_string("006")) .die_null() .die_null() .die(1, |s| s.attr_string("007")) .die(1, |s| s.attr_string("008")) .die(1, |s| s.attr_string("009")) .die_null() .die_null() .die_null() .die(1, |s| s.attr_string("010")) .die_null() .die_null(); let entries_buf = section.get_contents().unwrap(); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&entries_buf, LittleEndian), }; let section = Section::with_endian(Endian::Little).unit(&mut unit); section.get_contents().unwrap() } #[test] fn test_cursor_next_entry_incomplete() { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .die(1, |s| s.attr_string("001")) .die(1, |s| s.attr_string("002")) .die(1, |s| s); let entries_buf = section.get_contents().unwrap(); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&entries_buf, LittleEndian), }; let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = §ion.get_contents().unwrap(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_entry(&mut cursor, "001"); assert_next_entry(&mut cursor, "002"); { // Entry code is present, but none of the attributes. cursor .next_entry() .expect("Should parse next entry") .expect("Should have an entry"); let entry = cursor.current().expect("Should have an entry result"); assert!(entry.attrs().next().is_err()); } assert!(cursor.next_entry().is_err()); assert!(cursor.next_entry().is_err()); } #[test] fn test_cursor_next_entry() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_entry(&mut cursor, "001"); assert_next_entry(&mut cursor, "002"); assert_next_entry(&mut cursor, "003"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "004"); assert_next_entry(&mut cursor, "005"); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "006"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "007"); assert_next_entry(&mut cursor, "008"); assert_next_entry(&mut cursor, "009"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "010"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert!(cursor .next_entry() .expect("Should parse next entry") .is_none()); assert!(cursor.current().is_none()); } #[test] fn test_cursor_next_dfs() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_dfs(&mut cursor, "001", 0); assert_next_dfs(&mut cursor, "002", 1); assert_next_dfs(&mut cursor, "003", 1); assert_next_dfs(&mut cursor, "004", -1); assert_next_dfs(&mut cursor, "005", 1); assert_next_dfs(&mut cursor, "006", 0); assert_next_dfs(&mut cursor, "007", -1); assert_next_dfs(&mut cursor, "008", 1); assert_next_dfs(&mut cursor, "009", 1); assert_next_dfs(&mut cursor, "010", -2); assert!(cursor.next_dfs().expect("Should parse next dfs").is_none()); assert!(cursor.current().is_none()); } #[test] fn test_cursor_next_sibling_no_sibling_ptr() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_dfs(&mut cursor, "001", 0); // Down to the first child of the root entry. assert_next_dfs(&mut cursor, "002", 1); // Now iterate all children of the root via `next_sibling`. assert_next_sibling(&mut cursor, "004"); assert_next_sibling(&mut cursor, "007"); assert_next_sibling(&mut cursor, "010"); // There should be no more siblings. assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor.current().is_none()); } #[test] fn test_cursor_next_sibling_continuation() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_dfs(&mut cursor, "001", 0); // Down to the first child of the root entry. assert_next_dfs(&mut cursor, "002", 1); // Get the next sibling, then iterate its children assert_next_sibling(&mut cursor, "004"); assert_next_dfs(&mut cursor, "005", 1); assert_next_sibling(&mut cursor, "006"); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); // And we should be able to continue with the children of the root entry. assert_next_dfs(&mut cursor, "007", -1); assert_next_sibling(&mut cursor, "010"); // There should be no more siblings. assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor.current().is_none()); } fn entries_cursor_sibling_abbrev_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr(DW_AT_sibling, DW_FORM_ref1) .abbrev_attr_null() .abbrev(2, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null(); section.get_contents().unwrap() } fn entries_cursor_sibling_entries_buf(header_size: usize) -> Vec { let start = Label::new(); let sibling004_ref = Label::new(); let sibling004 = Label::new(); let sibling009_ref = Label::new(); let sibling009 = Label::new(); #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .mark(&start) .die(2, |s| s.attr_string("001")) // Valid sibling attribute. .die(1, |s| s.attr_string("002").D8(&sibling004_ref)) // Invalid code to ensure the sibling attribute was used. .die(10, |s| s.attr_string("003")) .die_null() .die_null() .mark(&sibling004) // Invalid sibling attribute. .die(1, |s| s.attr_string("004").attr_ref1(255)) .die(2, |s| s.attr_string("005")) .die_null() .die_null() // Sibling attribute in child only. .die(2, |s| s.attr_string("006")) // Valid sibling attribute. .die(1, |s| s.attr_string("007").D8(&sibling009_ref)) // Invalid code to ensure the sibling attribute was used. .die(10, |s| s.attr_string("008")) .die_null() .die_null() .mark(&sibling009) .die(2, |s| s.attr_string("009")) .die_null() .die_null() // No sibling attribute. .die(2, |s| s.attr_string("010")) .die(2, |s| s.attr_string("011")) .die_null() .die_null() .die_null(); let offset = header_size as u64 + (&sibling004 - &start) as u64; sibling004_ref.set_const(offset); let offset = header_size as u64 + (&sibling009 - &start) as u64; sibling009_ref.set_const(offset); section.get_contents().unwrap() } fn test_cursor_next_sibling_with_ptr( cursor: &mut EntriesCursor<'_, '_, EndianSlice<'_, LittleEndian>>, ) { assert_next_dfs(cursor, "001", 0); // Down to the first child of the root. assert_next_dfs(cursor, "002", 1); // Now iterate all children of the root via `next_sibling`. assert_valid_sibling_ptr(cursor); assert_next_sibling(cursor, "004"); assert_next_sibling(cursor, "006"); assert_next_sibling(cursor, "010"); // There should be no more siblings. assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor.current().is_none()); } #[test] fn test_debug_info_next_sibling_with_ptr() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; let header_size = unit.size_of_header(); let entries_buf = entries_cursor_sibling_entries_buf(header_size); unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = section.get_contents().unwrap(); let debug_info = DebugInfo::new(&info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrev_buf = entries_cursor_sibling_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); test_cursor_next_sibling_with_ptr(&mut cursor); } #[test] fn test_debug_types_next_sibling_with_ptr() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0), type_offset: UnitOffset(0), }, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; let header_size = unit.size_of_header(); let entries_buf = entries_cursor_sibling_entries_buf(header_size); unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = section.get_contents().unwrap(); let debug_types = DebugTypes::new(&info_buf, LittleEndian); let unit = debug_types .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrev_buf = entries_cursor_sibling_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); test_cursor_next_sibling_with_ptr(&mut cursor); } #[test] fn test_entries_at_offset() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit .entries_at_offset(&abbrevs, UnitOffset(unit.header_size())) .unwrap(); assert_next_entry(&mut cursor, "001"); let cursor = unit.entries_at_offset(&abbrevs, UnitOffset(0)); match cursor { Err(Error::OffsetOutOfBounds) => {} otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } } } fn entries_tree_tests_debug_abbrevs_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev(2, DW_TAG_subprogram, DW_CHILDREN_no) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null() .get_contents() .unwrap(); section } fn entries_tree_tests_debug_info_buf(header_size: usize) -> (Vec, UnitOffset) { let start = Label::new(); let entry2 = Label::new(); #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .mark(&start) .die(1, |s| s.attr_string("root")) .die(1, |s| s.attr_string("1")) .die(1, |s| s.attr_string("1a")) .die_null() .die(2, |s| s.attr_string("1b")) .die_null() .mark(&entry2) .die(1, |s| s.attr_string("2")) .die(1, |s| s.attr_string("2a")) .die(1, |s| s.attr_string("2a1")) .die_null() .die_null() .die(1, |s| s.attr_string("2b")) .die(2, |s| s.attr_string("2b1")) .die_null() .die_null() .die(1, |s| s.attr_string("3")) .die(1, |s| s.attr_string("3a")) .die(2, |s| s.attr_string("3a1")) .die(2, |s| s.attr_string("3a2")) .die_null() .die(2, |s| s.attr_string("3b")) .die_null() .die(2, |s| s.attr_string("final")) .die_null() .get_contents() .unwrap(); let entry2 = UnitOffset(header_size + (&entry2 - &start) as usize); (section, entry2) } #[test] fn test_entries_tree() { fn assert_entry<'input, 'abbrev, 'unit, 'tree, Endian>( node: Result< Option>>, >, name: &str, ) -> EntriesTreeIter<'abbrev, 'unit, 'tree, EndianSlice<'input, Endian>> where Endian: Endianity, { let node = node .expect("Should parse entry") .expect("Should have entry"); assert_entry_name(node.entry(), name); node.children() } fn assert_null( node: Result>>>, ) { match node { Ok(None) => {} otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } } } let abbrevs_buf = entries_tree_tests_debug_abbrevs_buf(); let debug_abbrev = DebugAbbrev::new(&abbrevs_buf, LittleEndian); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; let header_size = unit.size_of_header(); let (entries_buf, entry2) = entries_tree_tests_debug_info_buf(header_size); unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); let info_buf = Section::with_endian(Endian::Little) .unit(&mut unit) .get_contents() .unwrap(); let debug_info = DebugInfo::new(&info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("Should parse unit") .expect("and it should be some"); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut tree = unit .entries_tree(&abbrevs, None) .expect("Should have entries tree"); // Test we can restart iteration of the tree. { let mut iter = assert_entry(tree.root().map(Some), "root"); assert_entry(iter.next(), "1"); } { let mut iter = assert_entry(tree.root().map(Some), "root"); assert_entry(iter.next(), "1"); } let mut iter = assert_entry(tree.root().map(Some), "root"); { // Test iteration with children. let mut iter = assert_entry(iter.next(), "1"); { // Test iteration with children flag, but no children. let mut iter = assert_entry(iter.next(), "1a"); assert_null(iter.next()); assert_null(iter.next()); } { // Test iteration without children flag. let mut iter = assert_entry(iter.next(), "1b"); assert_null(iter.next()); assert_null(iter.next()); } assert_null(iter.next()); assert_null(iter.next()); } { // Test skipping over children. let mut iter = assert_entry(iter.next(), "2"); assert_entry(iter.next(), "2a"); assert_entry(iter.next(), "2b"); assert_null(iter.next()); } { // Test skipping after partial iteration. let mut iter = assert_entry(iter.next(), "3"); { let mut iter = assert_entry(iter.next(), "3a"); assert_entry(iter.next(), "3a1"); // Parent iter should be able to skip over "3a2". } assert_entry(iter.next(), "3b"); assert_null(iter.next()); } assert_entry(iter.next(), "final"); assert_null(iter.next()); // Test starting at an offset. let mut tree = unit .entries_tree(&abbrevs, Some(entry2)) .expect("Should have entries tree"); let mut iter = assert_entry(tree.root().map(Some), "2"); assert_entry(iter.next(), "2a"); assert_entry(iter.next(), "2b"); assert_null(iter.next()); } #[test] fn test_entries_raw() { fn assert_abbrev<'abbrev, Endian>( entries: &mut EntriesRaw<'abbrev, '_, EndianSlice<'_, Endian>>, tag: DwTag, ) -> &'abbrev Abbreviation where Endian: Endianity, { let abbrev = entries .read_abbreviation() .expect("Should parse abbrev") .expect("Should have abbrev"); assert_eq!(abbrev.tag(), tag); abbrev } fn assert_null(entries: &mut EntriesRaw<'_, '_, EndianSlice<'_, Endian>>) where Endian: Endianity, { match entries.read_abbreviation() { Ok(None) => {} otherwise => { panic!("Unexpected parse result = {:#?}", otherwise); } } } fn assert_attr( entries: &mut EntriesRaw<'_, '_, EndianSlice<'_, Endian>>, spec: Option, name: DwAt, value: &str, ) where Endian: Endianity, { let spec = spec.expect("Should have attribute specification"); let attr = entries .read_attribute(spec) .expect("Should parse attribute"); assert_eq!(attr.name(), name); assert_eq!( attr.value(), AttributeValue::String(EndianSlice::new(value.as_bytes(), Endian::default())) ); } #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr(DW_AT_linkage_name, DW_FORM_string) .abbrev_attr_null() .abbrev(2, DW_TAG_variable, DW_CHILDREN_no) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null(); let abbrevs_buf = section.get_contents().unwrap(); let debug_abbrev = DebugAbbrev::new(&abbrevs_buf, LittleEndian); #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .die(1, |s| s.attr_string("f1").attr_string("l1")) .die(2, |s| s.attr_string("v1")) .die(2, |s| s.attr_string("v2")) .die(1, |s| s.attr_string("f2").attr_string("l2")) .die_null() .die_null(); let entries_buf = section.get_contents().unwrap(); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&entries_buf, LittleEndian), }; let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = section.get_contents().unwrap(); let debug_info = DebugInfo::new(&info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut entries = unit .entries_raw(&abbrevs, None) .expect("Should have entries"); assert_eq!(entries.next_depth(), 0); let abbrev = assert_abbrev(&mut entries, DW_TAG_subprogram); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "f1"); assert_attr(&mut entries, attrs.next(), DW_AT_linkage_name, "l1"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 1); let abbrev = assert_abbrev(&mut entries, DW_TAG_variable); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "v1"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 1); let abbrev = assert_abbrev(&mut entries, DW_TAG_variable); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "v2"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 1); let abbrev = assert_abbrev(&mut entries, DW_TAG_subprogram); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "f2"); assert_attr(&mut entries, attrs.next(), DW_AT_linkage_name, "l2"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 2); assert_null(&mut entries); assert_eq!(entries.next_depth(), 1); assert_null(&mut entries); assert_eq!(entries.next_depth(), 0); assert!(entries.is_empty()); } #[test] fn test_debug_info_offset() { let padding = &[0; 10]; let entries = &[0; 20]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(entries, LittleEndian), }; Section::with_endian(Endian::Little) .append_bytes(padding) .unit(&mut unit); let offset = padding.len(); let header_length = unit.size_of_header(); let length = unit.length_including_self(); assert_eq!(DebugInfoOffset(0).to_unit_offset(&unit), None); assert_eq!(DebugInfoOffset(offset - 1).to_unit_offset(&unit), None); assert_eq!(DebugInfoOffset(offset).to_unit_offset(&unit), None); assert_eq!( DebugInfoOffset(offset + header_length - 1).to_unit_offset(&unit), None ); assert_eq!( DebugInfoOffset(offset + header_length).to_unit_offset(&unit), Some(UnitOffset(header_length)) ); assert_eq!( DebugInfoOffset(offset + length - 1).to_unit_offset(&unit), Some(UnitOffset(length - 1)) ); assert_eq!(DebugInfoOffset(offset + length).to_unit_offset(&unit), None); assert_eq!( UnitOffset(header_length).to_debug_info_offset(&unit), Some(DebugInfoOffset(offset + header_length)) ); assert_eq!( UnitOffset(length - 1).to_debug_info_offset(&unit), Some(DebugInfoOffset(offset + length - 1)) ); } #[test] fn test_debug_types_offset() { let padding = &[0; 10]; let entries = &[0; 20]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0), type_offset: UnitOffset(0), }, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(entries, LittleEndian), }; Section::with_endian(Endian::Little) .append_bytes(padding) .unit(&mut unit); let offset = padding.len(); let header_length = unit.size_of_header(); let length = unit.length_including_self(); assert_eq!(DebugTypesOffset(0).to_unit_offset(&unit), None); assert_eq!(DebugTypesOffset(offset - 1).to_unit_offset(&unit), None); assert_eq!(DebugTypesOffset(offset).to_unit_offset(&unit), None); assert_eq!( DebugTypesOffset(offset + header_length - 1).to_unit_offset(&unit), None ); assert_eq!( DebugTypesOffset(offset + header_length).to_unit_offset(&unit), Some(UnitOffset(header_length)) ); assert_eq!( DebugTypesOffset(offset + length - 1).to_unit_offset(&unit), Some(UnitOffset(length - 1)) ); assert_eq!( DebugTypesOffset(offset + length).to_unit_offset(&unit), None ); assert_eq!( UnitOffset(header_length).to_debug_types_offset(&unit), Some(DebugTypesOffset(offset + header_length)) ); assert_eq!( UnitOffset(length - 1).to_debug_types_offset(&unit), Some(DebugTypesOffset(offset + length - 1)) ); } #[test] fn test_length_including_self() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; unit.encoding.format = Format::Dwarf32; assert_eq!(unit.length_including_self(), 4); unit.encoding.format = Format::Dwarf64; assert_eq!(unit.length_including_self(), 12); unit.unit_length = 10; assert_eq!(unit.length_including_self(), 22); } #[test] fn test_parse_type_unit_abbrevs() { let types_buf = [ // Type unit header 0x25, 0x00, 0x00, 0x00, // 32-bit unit length = 37 0x04, 0x00, // Version 4 0x00, 0x00, 0x00, 0x00, // debug_abbrev_offset 0x04, // Address size 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Type signature 0x01, 0x02, 0x03, 0x04, // Type offset // DIEs // Abbreviation code 0x01, // Attribute of form DW_FORM_string = "foo\0" 0x66, 0x6f, 0x6f, 0x00, // Children // Abbreviation code 0x01, // Attribute of form DW_FORM_string = "foo\0" 0x66, 0x6f, 0x6f, 0x00, // Children // Abbreviation code 0x01, // Attribute of form DW_FORM_string = "foo\0" 0x66, 0x6f, 0x6f, 0x00, // Children 0x00, // End of children 0x00, // End of children 0x00, // End of children ]; let debug_types = DebugTypes::new(&types_buf, LittleEndian); let abbrev_buf = [ // Code 0x01, // DW_TAG_subprogram 0x2e, // DW_CHILDREN_yes 0x01, // Begin attributes 0x03, // Attribute name = DW_AT_name 0x08, // Attribute form = DW_FORM_string 0x00, 0x00, // End attributes 0x00, // Null terminator ]; let get_some_type_unit = || debug_types.units().next().unwrap().unwrap(); let unit = get_some_type_unit(); let read_debug_abbrev_section_somehow = || &abbrev_buf; let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); let _abbrevs_for_unit = unit.abbreviations(&debug_abbrev).unwrap(); } } gimli-0.31.1/src/read/util.rs000064400000000000000000000160211046102023000140340ustar 00000000000000#[cfg(feature = "read")] use alloc::boxed::Box; #[cfg(feature = "read")] use alloc::vec::Vec; use core::fmt; use core::mem::MaybeUninit; use core::ops; use core::ptr; use core::slice; mod sealed { /// # Safety /// Implementer must not modify the content in storage. pub unsafe trait Sealed { type Storage; fn new_storage() -> Self::Storage; fn grow(_storage: &mut Self::Storage, _additional: usize) -> Result<(), CapacityFull> { Err(CapacityFull) } } #[derive(Clone, Copy, Debug)] pub struct CapacityFull; } use sealed::*; /// Marker trait for types that can be used as backing storage when a growable array type is needed. /// /// This trait is sealed and cannot be implemented for types outside this crate. pub trait ArrayLike: Sealed { /// Type of the elements being stored. type Item; #[doc(hidden)] fn as_slice(storage: &Self::Storage) -> &[MaybeUninit]; #[doc(hidden)] fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit]; } // SAFETY: does not modify the content in storage. unsafe impl Sealed for [T; N] { type Storage = [MaybeUninit; N]; fn new_storage() -> Self::Storage { // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid. unsafe { MaybeUninit::uninit().assume_init() } } } impl ArrayLike for [T; N] { type Item = T; fn as_slice(storage: &Self::Storage) -> &[MaybeUninit] { storage } fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit] { storage } } // SAFETY: does not modify the content in storage. #[cfg(feature = "read")] unsafe impl Sealed for Box<[T; N]> { type Storage = Box<[MaybeUninit; N]>; fn new_storage() -> Self::Storage { // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid. Box::new(unsafe { MaybeUninit::uninit().assume_init() }) } } #[cfg(feature = "read")] impl ArrayLike for Box<[T; N]> { type Item = T; fn as_slice(storage: &Self::Storage) -> &[MaybeUninit] { &storage[..] } fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit] { &mut storage[..] } } #[cfg(feature = "read")] unsafe impl Sealed for Vec { type Storage = Box<[MaybeUninit]>; fn new_storage() -> Self::Storage { Box::new([]) } fn grow(storage: &mut Self::Storage, additional: usize) -> Result<(), CapacityFull> { let mut vec: Vec<_> = core::mem::replace(storage, Box::new([])).into(); vec.reserve(additional); // SAFETY: This is a `Vec` of `MaybeUninit`. unsafe { vec.set_len(vec.capacity()) }; *storage = vec.into_boxed_slice(); Ok(()) } } #[cfg(feature = "read")] impl ArrayLike for Vec { type Item = T; fn as_slice(storage: &Self::Storage) -> &[MaybeUninit] { storage } fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit] { storage } } pub(crate) struct ArrayVec { storage: A::Storage, len: usize, } impl ArrayVec { pub fn new() -> Self { Self { storage: A::new_storage(), len: 0, } } pub fn clear(&mut self) { let ptr: *mut [A::Item] = &mut **self; // Set length first so the type invariant is upheld even if `drop_in_place` panicks. self.len = 0; // SAFETY: `ptr` contains valid elements only and we "forget" them by setting the length. unsafe { ptr::drop_in_place(ptr) }; } pub fn try_push(&mut self, value: A::Item) -> Result<(), CapacityFull> { let mut storage = A::as_mut_slice(&mut self.storage); if self.len >= storage.len() { A::grow(&mut self.storage, 1)?; storage = A::as_mut_slice(&mut self.storage); } storage[self.len] = MaybeUninit::new(value); self.len += 1; Ok(()) } pub fn try_insert(&mut self, index: usize, element: A::Item) -> Result<(), CapacityFull> { assert!(index <= self.len); let mut storage = A::as_mut_slice(&mut self.storage); if self.len >= storage.len() { A::grow(&mut self.storage, 1)?; storage = A::as_mut_slice(&mut self.storage); } // SAFETY: storage[index] is filled later. unsafe { let p = storage.as_mut_ptr().add(index); core::ptr::copy(p as *const _, p.add(1), self.len - index); } storage[index] = MaybeUninit::new(element); self.len += 1; Ok(()) } pub fn pop(&mut self) -> Option { if self.len == 0 { None } else { self.len -= 1; // SAFETY: this element is valid and we "forget" it by setting the length. Some(unsafe { A::as_slice(&self.storage)[self.len].as_ptr().read() }) } } pub fn swap_remove(&mut self, index: usize) -> A::Item { assert!(self.len > 0); A::as_mut_slice(&mut self.storage).swap(index, self.len - 1); self.pop().unwrap() } } #[cfg(feature = "read")] impl ArrayVec> { pub fn into_vec(mut self) -> Vec { let len = core::mem::replace(&mut self.len, 0); let storage = core::mem::replace(&mut self.storage, Box::new([])); let slice = Box::leak(storage); debug_assert!(len <= slice.len()); // SAFETY: valid elements. unsafe { Vec::from_raw_parts(slice.as_mut_ptr() as *mut T, len, slice.len()) } } } impl Drop for ArrayVec { fn drop(&mut self) { self.clear(); } } impl Default for ArrayVec { fn default() -> Self { Self::new() } } impl ops::Deref for ArrayVec { type Target = [A::Item]; fn deref(&self) -> &[A::Item] { let slice = &A::as_slice(&self.storage); debug_assert!(self.len <= slice.len()); // SAFETY: valid elements. unsafe { slice::from_raw_parts(slice.as_ptr() as _, self.len) } } } impl ops::DerefMut for ArrayVec { fn deref_mut(&mut self) -> &mut [A::Item] { let slice = &mut A::as_mut_slice(&mut self.storage); debug_assert!(self.len <= slice.len()); // SAFETY: valid elements. unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as _, self.len) } } } impl Clone for ArrayVec where A::Item: Clone, { fn clone(&self) -> Self { let mut new = Self::default(); for value in &**self { new.try_push(value.clone()).unwrap(); } new } } impl PartialEq for ArrayVec where A::Item: PartialEq, { fn eq(&self, other: &Self) -> bool { **self == **other } } impl Eq for ArrayVec where A::Item: Eq {} impl fmt::Debug for ArrayVec where A::Item: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } gimli-0.31.1/src/read/value.rs000064400000000000000000002165201046102023000142010ustar 00000000000000//! Definitions for values used in DWARF expressions. use crate::constants; #[cfg(feature = "read")] use crate::read::{AttributeValue, DebuggingInformationEntry}; use crate::read::{Error, Reader, Result}; /// Convert a u64 to an i64, with sign extension if required. /// /// This is primarily used when needing to treat `Value::Generic` /// as a signed value. #[inline] fn sign_extend(value: u64, mask: u64) -> i64 { let value = (value & mask) as i64; let sign = ((mask >> 1) + 1) as i64; (value ^ sign).wrapping_sub(sign) } #[inline] fn mask_bit_size(addr_mask: u64) -> u32 { 64 - addr_mask.leading_zeros() } /// The type of an entry on the DWARF stack. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ValueType { /// The generic type, which is address-sized and of unspecified sign, /// as specified in the DWARF 5 standard, section 2.5.1. /// This type is also used to represent address base types. Generic, /// Signed 8-bit integer type. I8, /// Unsigned 8-bit integer type. U8, /// Signed 16-bit integer type. I16, /// Unsigned 16-bit integer type. U16, /// Signed 32-bit integer type. I32, /// Unsigned 32-bit integer type. U32, /// Signed 64-bit integer type. I64, /// Unsigned 64-bit integer type. U64, /// 32-bit floating point type. F32, /// 64-bit floating point type. F64, } /// The value of an entry on the DWARF stack. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Value { /// A generic value, which is address-sized and of unspecified sign. Generic(u64), /// A signed 8-bit integer value. I8(i8), /// An unsigned 8-bit integer value. U8(u8), /// A signed 16-bit integer value. I16(i16), /// An unsigned 16-bit integer value. U16(u16), /// A signed 32-bit integer value. I32(i32), /// An unsigned 32-bit integer value. U32(u32), /// A signed 64-bit integer value. I64(i64), /// An unsigned 64-bit integer value. U64(u64), /// A 32-bit floating point value. F32(f32), /// A 64-bit floating point value. F64(f64), } impl ValueType { /// The size in bits of a value for this type. pub fn bit_size(self, addr_mask: u64) -> u32 { match self { ValueType::Generic => mask_bit_size(addr_mask), ValueType::I8 | ValueType::U8 => 8, ValueType::I16 | ValueType::U16 => 16, ValueType::I32 | ValueType::U32 | ValueType::F32 => 32, ValueType::I64 | ValueType::U64 | ValueType::F64 => 64, } } /// Construct a `ValueType` from the attributes of a base type DIE. pub fn from_encoding(encoding: constants::DwAte, byte_size: u64) -> Option { Some(match (encoding, byte_size) { (constants::DW_ATE_signed, 1) => ValueType::I8, (constants::DW_ATE_signed, 2) => ValueType::I16, (constants::DW_ATE_signed, 4) => ValueType::I32, (constants::DW_ATE_signed, 8) => ValueType::I64, (constants::DW_ATE_unsigned, 1) => ValueType::U8, (constants::DW_ATE_unsigned, 2) => ValueType::U16, (constants::DW_ATE_unsigned, 4) => ValueType::U32, (constants::DW_ATE_unsigned, 8) => ValueType::U64, (constants::DW_ATE_float, 4) => ValueType::F32, (constants::DW_ATE_float, 8) => ValueType::F64, _ => return None, }) } /// Construct a `ValueType` from a base type DIE. #[cfg(feature = "read")] pub fn from_entry( entry: &DebuggingInformationEntry<'_, '_, R>, ) -> Result> { if entry.tag() != constants::DW_TAG_base_type { return Ok(None); } let mut encoding = None; let mut byte_size = None; let mut endianity = constants::DW_END_default; let mut attrs = entry.attrs(); while let Some(attr) = attrs.next()? { match attr.name() { constants::DW_AT_byte_size => byte_size = attr.udata_value(), constants::DW_AT_encoding => { if let AttributeValue::Encoding(x) = attr.value() { encoding = Some(x); } } constants::DW_AT_endianity => { if let AttributeValue::Endianity(x) = attr.value() { endianity = x; } } _ => {} } } if endianity != constants::DW_END_default { // TODO: we could check if it matches the reader endianity, // but normally it would use DW_END_default in that case. return Ok(None); } if let (Some(encoding), Some(byte_size)) = (encoding, byte_size) { Ok(ValueType::from_encoding(encoding, byte_size)) } else { Ok(None) } } } impl Value { /// Return the `ValueType` corresponding to this `Value`. pub fn value_type(&self) -> ValueType { match *self { Value::Generic(_) => ValueType::Generic, Value::I8(_) => ValueType::I8, Value::U8(_) => ValueType::U8, Value::I16(_) => ValueType::I16, Value::U16(_) => ValueType::U16, Value::I32(_) => ValueType::I32, Value::U32(_) => ValueType::U32, Value::I64(_) => ValueType::I64, Value::U64(_) => ValueType::U64, Value::F32(_) => ValueType::F32, Value::F64(_) => ValueType::F64, } } /// Read a `Value` with the given `value_type` from a `Reader`. pub fn parse(value_type: ValueType, mut bytes: R) -> Result { let value = match value_type { ValueType::I8 => Value::I8(bytes.read_i8()?), ValueType::U8 => Value::U8(bytes.read_u8()?), ValueType::I16 => Value::I16(bytes.read_i16()?), ValueType::U16 => Value::U16(bytes.read_u16()?), ValueType::I32 => Value::I32(bytes.read_i32()?), ValueType::U32 => Value::U32(bytes.read_u32()?), ValueType::I64 => Value::I64(bytes.read_i64()?), ValueType::U64 => Value::U64(bytes.read_u64()?), ValueType::F32 => Value::F32(bytes.read_f32()?), ValueType::F64 => Value::F64(bytes.read_f64()?), _ => return Err(Error::UnsupportedTypeOperation), }; Ok(value) } /// Convert a `Value` to a `u64`. /// /// The `ValueType` of `self` must be integral. /// Values are sign extended if the source value is signed. pub fn to_u64(self, addr_mask: u64) -> Result { let value = match self { Value::Generic(value) => value & addr_mask, Value::I8(value) => value as u64, Value::U8(value) => u64::from(value), Value::I16(value) => value as u64, Value::U16(value) => u64::from(value), Value::I32(value) => value as u64, Value::U32(value) => u64::from(value), Value::I64(value) => value as u64, Value::U64(value) => value, _ => return Err(Error::IntegralTypeRequired), }; Ok(value) } /// Create a `Value` with the given `value_type` from a `u64` value. /// /// The `value_type` may be integral or floating point. /// The result is truncated if the `u64` value does /// not fit the bounds of the `value_type`. pub fn from_u64(value_type: ValueType, value: u64) -> Result { let value = match value_type { ValueType::Generic => Value::Generic(value), ValueType::I8 => Value::I8(value as i8), ValueType::U8 => Value::U8(value as u8), ValueType::I16 => Value::I16(value as i16), ValueType::U16 => Value::U16(value as u16), ValueType::I32 => Value::I32(value as i32), ValueType::U32 => Value::U32(value as u32), ValueType::I64 => Value::I64(value as i64), ValueType::U64 => Value::U64(value), ValueType::F32 => Value::F32(value as f32), ValueType::F64 => Value::F64(value as f64), }; Ok(value) } /// Create a `Value` with the given `value_type` from a `f32` value. /// /// The `value_type` may be integral or floating point. /// The result is not defined if the `f32` value does /// not fit the bounds of the `value_type`. fn from_f32(value_type: ValueType, value: f32) -> Result { let value = match value_type { ValueType::Generic => Value::Generic(value as u64), ValueType::I8 => Value::I8(value as i8), ValueType::U8 => Value::U8(value as u8), ValueType::I16 => Value::I16(value as i16), ValueType::U16 => Value::U16(value as u16), ValueType::I32 => Value::I32(value as i32), ValueType::U32 => Value::U32(value as u32), ValueType::I64 => Value::I64(value as i64), ValueType::U64 => Value::U64(value as u64), ValueType::F32 => Value::F32(value), ValueType::F64 => Value::F64(f64::from(value)), }; Ok(value) } /// Create a `Value` with the given `value_type` from a `f64` value. /// /// The `value_type` may be integral or floating point. /// The result is not defined if the `f64` value does /// not fit the bounds of the `value_type`. fn from_f64(value_type: ValueType, value: f64) -> Result { let value = match value_type { ValueType::Generic => Value::Generic(value as u64), ValueType::I8 => Value::I8(value as i8), ValueType::U8 => Value::U8(value as u8), ValueType::I16 => Value::I16(value as i16), ValueType::U16 => Value::U16(value as u16), ValueType::I32 => Value::I32(value as i32), ValueType::U32 => Value::U32(value as u32), ValueType::I64 => Value::I64(value as i64), ValueType::U64 => Value::U64(value as u64), ValueType::F32 => Value::F32(value as f32), ValueType::F64 => Value::F64(value), }; Ok(value) } /// Convert a `Value` to the given `value_type`. /// /// When converting between integral types, the result is truncated /// if the source value does not fit the bounds of the `value_type`. /// When converting from floating point types, the result is not defined /// if the source value does not fit the bounds of the `value_type`. /// /// This corresponds to the DWARF `DW_OP_convert` operation. pub fn convert(self, value_type: ValueType, addr_mask: u64) -> Result { match self { Value::F32(value) => Value::from_f32(value_type, value), Value::F64(value) => Value::from_f64(value_type, value), _ => Value::from_u64(value_type, self.to_u64(addr_mask)?), } } /// Reinterpret the bits in a `Value` as the given `value_type`. /// /// The source and result value types must have equal sizes. /// /// This corresponds to the DWARF `DW_OP_reinterpret` operation. pub fn reinterpret(self, value_type: ValueType, addr_mask: u64) -> Result { if self.value_type().bit_size(addr_mask) != value_type.bit_size(addr_mask) { return Err(Error::TypeMismatch); } let bits = match self { Value::Generic(value) => value, Value::I8(value) => value as u64, Value::U8(value) => u64::from(value), Value::I16(value) => value as u64, Value::U16(value) => u64::from(value), Value::I32(value) => value as u64, Value::U32(value) => u64::from(value), Value::I64(value) => value as u64, Value::U64(value) => value, Value::F32(value) => u64::from(f32::to_bits(value)), Value::F64(value) => f64::to_bits(value), }; let value = match value_type { ValueType::Generic => Value::Generic(bits), ValueType::I8 => Value::I8(bits as i8), ValueType::U8 => Value::U8(bits as u8), ValueType::I16 => Value::I16(bits as i16), ValueType::U16 => Value::U16(bits as u16), ValueType::I32 => Value::I32(bits as i32), ValueType::U32 => Value::U32(bits as u32), ValueType::I64 => Value::I64(bits as i64), ValueType::U64 => Value::U64(bits), ValueType::F32 => Value::F32(f32::from_bits(bits as u32)), ValueType::F64 => Value::F64(f64::from_bits(bits)), }; Ok(value) } /// Perform an absolute value operation. /// /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_abs` operation. pub fn abs(self, addr_mask: u64) -> Result { // wrapping_abs() can be used because DWARF specifies that the result is undefined // for negative minimal values. let value = match self { Value::Generic(value) => { Value::Generic(sign_extend(value, addr_mask).wrapping_abs() as u64) } Value::I8(value) => Value::I8(value.wrapping_abs()), Value::I16(value) => Value::I16(value.wrapping_abs()), Value::I32(value) => Value::I32(value.wrapping_abs()), Value::I64(value) => Value::I64(value.wrapping_abs()), // f32/f64::abs() is not available in libcore Value::F32(value) => Value::F32(if value < 0. { -value } else { value }), Value::F64(value) => Value::F64(if value < 0. { -value } else { value }), Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => self, }; Ok(value) } /// Perform a negation operation. /// /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_neg` operation. pub fn neg(self, addr_mask: u64) -> Result { // wrapping_neg() can be used because DWARF specifies that the result is undefined // for negative minimal values. let value = match self { Value::Generic(value) => { Value::Generic(sign_extend(value, addr_mask).wrapping_neg() as u64) } Value::I8(value) => Value::I8(value.wrapping_neg()), Value::I16(value) => Value::I16(value.wrapping_neg()), Value::I32(value) => Value::I32(value.wrapping_neg()), Value::I64(value) => Value::I64(value.wrapping_neg()), Value::F32(value) => Value::F32(-value), Value::F64(value) => Value::F64(-value), // It's unclear if these should implicitly convert to a signed value. // For now, we don't support them. Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => { return Err(Error::UnsupportedTypeOperation); } }; Ok(value) } /// Perform an addition operation. /// /// This operation requires matching types. /// /// This corresponds to the DWARF `DW_OP_plus` operation. pub fn add(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { Value::Generic(v1.wrapping_add(v2) & addr_mask) } (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_add(v2)), (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_add(v2)), (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_add(v2)), (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_add(v2)), (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_add(v2)), (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_add(v2)), (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_add(v2)), (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_add(v2)), (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 + v2), (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 + v2), _ => return Err(Error::TypeMismatch), }; Ok(value) } /// Perform a subtraction operation. /// /// This operation requires matching types. /// /// This corresponds to the DWARF `DW_OP_minus` operation. pub fn sub(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { Value::Generic(v1.wrapping_sub(v2) & addr_mask) } (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_sub(v2)), (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_sub(v2)), (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_sub(v2)), (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_sub(v2)), (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_sub(v2)), (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_sub(v2)), (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_sub(v2)), (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_sub(v2)), (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 - v2), (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 - v2), _ => return Err(Error::TypeMismatch), }; Ok(value) } /// Perform a multiplication operation. /// /// This operation requires matching types. /// /// This corresponds to the DWARF `DW_OP_mul` operation. pub fn mul(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { Value::Generic(v1.wrapping_mul(v2) & addr_mask) } (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_mul(v2)), (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_mul(v2)), (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_mul(v2)), (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_mul(v2)), (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_mul(v2)), (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_mul(v2)), (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_mul(v2)), (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_mul(v2)), (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 * v2), (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 * v2), _ => return Err(Error::TypeMismatch), }; Ok(value) } /// Perform a division operation. /// /// This operation requires matching types. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_div` operation. pub fn div(self, rhs: Value, addr_mask: u64) -> Result { match rhs { Value::Generic(v2) if sign_extend(v2, addr_mask) == 0 => { return Err(Error::DivisionByZero); } Value::I8(0) | Value::U8(0) | Value::I16(0) | Value::U16(0) | Value::I32(0) | Value::U32(0) | Value::I64(0) | Value::U64(0) => { return Err(Error::DivisionByZero); } _ => {} } let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { // Signed division Value::Generic( sign_extend(v1, addr_mask).wrapping_div(sign_extend(v2, addr_mask)) as u64, ) } (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_div(v2)), (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_div(v2)), (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_div(v2)), (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_div(v2)), (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_div(v2)), (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_div(v2)), (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_div(v2)), (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_div(v2)), (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 / v2), (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 / v2), _ => return Err(Error::TypeMismatch), }; Ok(value) } /// Perform a remainder operation. /// /// This operation requires matching integral types. /// If the value type is `Generic`, then it is interpreted as an unsigned value. /// /// This corresponds to the DWARF `DW_OP_mod` operation. pub fn rem(self, rhs: Value, addr_mask: u64) -> Result { match rhs { Value::Generic(rhs) if (rhs & addr_mask) == 0 => { return Err(Error::DivisionByZero); } Value::I8(0) | Value::U8(0) | Value::I16(0) | Value::U16(0) | Value::I32(0) | Value::U32(0) | Value::I64(0) | Value::U64(0) => { return Err(Error::DivisionByZero); } _ => {} } let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { // Unsigned modulus Value::Generic((v1 & addr_mask).wrapping_rem(v2 & addr_mask)) } (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_rem(v2)), (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_rem(v2)), (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_rem(v2)), (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_rem(v2)), (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_rem(v2)), (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_rem(v2)), (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_rem(v2)), (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_rem(v2)), (Value::F32(_), Value::F32(_)) => return Err(Error::IntegralTypeRequired), (Value::F64(_), Value::F64(_)) => return Err(Error::IntegralTypeRequired), _ => return Err(Error::TypeMismatch), }; Ok(value) } /// Perform a bitwise not operation. /// /// This operation requires matching integral types. /// /// This corresponds to the DWARF `DW_OP_not` operation. pub fn not(self, addr_mask: u64) -> Result { let value_type = self.value_type(); let v = self.to_u64(addr_mask)?; Value::from_u64(value_type, !v) } /// Perform a bitwise and operation. /// /// This operation requires matching integral types. /// /// This corresponds to the DWARF `DW_OP_and` operation. pub fn and(self, rhs: Value, addr_mask: u64) -> Result { let value_type = self.value_type(); if value_type != rhs.value_type() { return Err(Error::TypeMismatch); } let v1 = self.to_u64(addr_mask)?; let v2 = rhs.to_u64(addr_mask)?; Value::from_u64(value_type, v1 & v2) } /// Perform a bitwise or operation. /// /// This operation requires matching integral types. /// /// This corresponds to the DWARF `DW_OP_or` operation. pub fn or(self, rhs: Value, addr_mask: u64) -> Result { let value_type = self.value_type(); if value_type != rhs.value_type() { return Err(Error::TypeMismatch); } let v1 = self.to_u64(addr_mask)?; let v2 = rhs.to_u64(addr_mask)?; Value::from_u64(value_type, v1 | v2) } /// Perform a bitwise exclusive-or operation. /// /// This operation requires matching integral types. /// /// This corresponds to the DWARF `DW_OP_xor` operation. pub fn xor(self, rhs: Value, addr_mask: u64) -> Result { let value_type = self.value_type(); if value_type != rhs.value_type() { return Err(Error::TypeMismatch); } let v1 = self.to_u64(addr_mask)?; let v2 = rhs.to_u64(addr_mask)?; Value::from_u64(value_type, v1 ^ v2) } /// Convert value to bit length suitable for a shift operation. /// /// If the value is negative then an error is returned. fn shift_length(self) -> Result { let value = match self { Value::Generic(value) => value, Value::I8(value) if value >= 0 => value as u64, Value::U8(value) => u64::from(value), Value::I16(value) if value >= 0 => value as u64, Value::U16(value) => u64::from(value), Value::I32(value) if value >= 0 => value as u64, Value::U32(value) => u64::from(value), Value::I64(value) if value >= 0 => value as u64, Value::U64(value) => value, _ => return Err(Error::InvalidShiftExpression), }; Ok(value) } /// Perform a shift left operation. /// /// This operation requires integral types. /// If the shift length exceeds the type size, then 0 is returned. /// If the shift length is negative then an error is returned. /// /// This corresponds to the DWARF `DW_OP_shl` operation. pub fn shl(self, rhs: Value, addr_mask: u64) -> Result { let v2 = rhs.shift_length()?; let value = match self { Value::Generic(v1) => Value::Generic(if v2 >= u64::from(mask_bit_size(addr_mask)) { 0 } else { (v1 & addr_mask) << v2 }), Value::I8(v1) => Value::I8(if v2 >= 8 { 0 } else { v1 << v2 }), Value::U8(v1) => Value::U8(if v2 >= 8 { 0 } else { v1 << v2 }), Value::I16(v1) => Value::I16(if v2 >= 16 { 0 } else { v1 << v2 }), Value::U16(v1) => Value::U16(if v2 >= 16 { 0 } else { v1 << v2 }), Value::I32(v1) => Value::I32(if v2 >= 32 { 0 } else { v1 << v2 }), Value::U32(v1) => Value::U32(if v2 >= 32 { 0 } else { v1 << v2 }), Value::I64(v1) => Value::I64(if v2 >= 64 { 0 } else { v1 << v2 }), Value::U64(v1) => Value::U64(if v2 >= 64 { 0 } else { v1 << v2 }), _ => return Err(Error::IntegralTypeRequired), }; Ok(value) } /// Perform a logical shift right operation. /// /// This operation requires an unsigned integral type for the value. /// If the value type is `Generic`, then it is interpreted as an unsigned value. /// /// This operation requires an integral type for the shift length. /// If the shift length exceeds the type size, then 0 is returned. /// If the shift length is negative then an error is returned. /// /// This corresponds to the DWARF `DW_OP_shr` operation. pub fn shr(self, rhs: Value, addr_mask: u64) -> Result { let v2 = rhs.shift_length()?; let value = match self { Value::Generic(v1) => Value::Generic(if v2 >= u64::from(mask_bit_size(addr_mask)) { 0 } else { (v1 & addr_mask) >> v2 }), Value::U8(v1) => Value::U8(if v2 >= 8 { 0 } else { v1 >> v2 }), Value::U16(v1) => Value::U16(if v2 >= 16 { 0 } else { v1 >> v2 }), Value::U32(v1) => Value::U32(if v2 >= 32 { 0 } else { v1 >> v2 }), Value::U64(v1) => Value::U64(if v2 >= 64 { 0 } else { v1 >> v2 }), // It's unclear if signed values should implicitly convert to an unsigned value. // For now, we don't support them. Value::I8(_) | Value::I16(_) | Value::I32(_) | Value::I64(_) => { return Err(Error::UnsupportedTypeOperation); } _ => return Err(Error::IntegralTypeRequired), }; Ok(value) } /// Perform an arithmetic shift right operation. /// /// This operation requires a signed integral type for the value. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This operation requires an integral type for the shift length. /// If the shift length exceeds the type size, then 0 is returned for positive values, /// and -1 is returned for negative values. /// If the shift length is negative then an error is returned. /// /// This corresponds to the DWARF `DW_OP_shra` operation. pub fn shra(self, rhs: Value, addr_mask: u64) -> Result { let v2 = rhs.shift_length()?; let value = match self { Value::Generic(v1) => { let v1 = sign_extend(v1, addr_mask); let value = if v2 >= u64::from(mask_bit_size(addr_mask)) { if v1 < 0 { !0 } else { 0 } } else { (v1 >> v2) as u64 }; Value::Generic(value) } Value::I8(v1) => Value::I8(if v2 >= 8 { if v1 < 0 { !0 } else { 0 } } else { v1 >> v2 }), Value::I16(v1) => Value::I16(if v2 >= 16 { if v1 < 0 { !0 } else { 0 } } else { v1 >> v2 }), Value::I32(v1) => Value::I32(if v2 >= 32 { if v1 < 0 { !0 } else { 0 } } else { v1 >> v2 }), Value::I64(v1) => Value::I64(if v2 >= 64 { if v1 < 0 { !0 } else { 0 } } else { v1 >> v2 }), // It's unclear if unsigned values should implicitly convert to a signed value. // For now, we don't support them. Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => { return Err(Error::UnsupportedTypeOperation); } _ => return Err(Error::IntegralTypeRequired), }; Ok(value) } /// Perform the `==` relational operation. /// /// This operation requires matching integral types. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_eq` operation. pub fn eq(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { sign_extend(v1, addr_mask) == sign_extend(v2, addr_mask) } (Value::I8(v1), Value::I8(v2)) => v1 == v2, (Value::U8(v1), Value::U8(v2)) => v1 == v2, (Value::I16(v1), Value::I16(v2)) => v1 == v2, (Value::U16(v1), Value::U16(v2)) => v1 == v2, (Value::I32(v1), Value::I32(v2)) => v1 == v2, (Value::U32(v1), Value::U32(v2)) => v1 == v2, (Value::I64(v1), Value::I64(v2)) => v1 == v2, (Value::U64(v1), Value::U64(v2)) => v1 == v2, (Value::F32(v1), Value::F32(v2)) => v1 == v2, (Value::F64(v1), Value::F64(v2)) => v1 == v2, _ => return Err(Error::TypeMismatch), }; Ok(Value::Generic(value as u64)) } /// Perform the `>=` relational operation. /// /// This operation requires matching integral types. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_ge` operation. pub fn ge(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { sign_extend(v1, addr_mask) >= sign_extend(v2, addr_mask) } (Value::I8(v1), Value::I8(v2)) => v1 >= v2, (Value::U8(v1), Value::U8(v2)) => v1 >= v2, (Value::I16(v1), Value::I16(v2)) => v1 >= v2, (Value::U16(v1), Value::U16(v2)) => v1 >= v2, (Value::I32(v1), Value::I32(v2)) => v1 >= v2, (Value::U32(v1), Value::U32(v2)) => v1 >= v2, (Value::I64(v1), Value::I64(v2)) => v1 >= v2, (Value::U64(v1), Value::U64(v2)) => v1 >= v2, (Value::F32(v1), Value::F32(v2)) => v1 >= v2, (Value::F64(v1), Value::F64(v2)) => v1 >= v2, _ => return Err(Error::TypeMismatch), }; Ok(Value::Generic(value as u64)) } /// Perform the `>` relational operation. /// /// This operation requires matching integral types. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_gt` operation. pub fn gt(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { sign_extend(v1, addr_mask) > sign_extend(v2, addr_mask) } (Value::I8(v1), Value::I8(v2)) => v1 > v2, (Value::U8(v1), Value::U8(v2)) => v1 > v2, (Value::I16(v1), Value::I16(v2)) => v1 > v2, (Value::U16(v1), Value::U16(v2)) => v1 > v2, (Value::I32(v1), Value::I32(v2)) => v1 > v2, (Value::U32(v1), Value::U32(v2)) => v1 > v2, (Value::I64(v1), Value::I64(v2)) => v1 > v2, (Value::U64(v1), Value::U64(v2)) => v1 > v2, (Value::F32(v1), Value::F32(v2)) => v1 > v2, (Value::F64(v1), Value::F64(v2)) => v1 > v2, _ => return Err(Error::TypeMismatch), }; Ok(Value::Generic(value as u64)) } /// Perform the `<= relational operation. /// /// This operation requires matching integral types. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_le` operation. pub fn le(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { sign_extend(v1, addr_mask) <= sign_extend(v2, addr_mask) } (Value::I8(v1), Value::I8(v2)) => v1 <= v2, (Value::U8(v1), Value::U8(v2)) => v1 <= v2, (Value::I16(v1), Value::I16(v2)) => v1 <= v2, (Value::U16(v1), Value::U16(v2)) => v1 <= v2, (Value::I32(v1), Value::I32(v2)) => v1 <= v2, (Value::U32(v1), Value::U32(v2)) => v1 <= v2, (Value::I64(v1), Value::I64(v2)) => v1 <= v2, (Value::U64(v1), Value::U64(v2)) => v1 <= v2, (Value::F32(v1), Value::F32(v2)) => v1 <= v2, (Value::F64(v1), Value::F64(v2)) => v1 <= v2, _ => return Err(Error::TypeMismatch), }; Ok(Value::Generic(value as u64)) } /// Perform the `< relational operation. /// /// This operation requires matching integral types. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_lt` operation. pub fn lt(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { sign_extend(v1, addr_mask) < sign_extend(v2, addr_mask) } (Value::I8(v1), Value::I8(v2)) => v1 < v2, (Value::U8(v1), Value::U8(v2)) => v1 < v2, (Value::I16(v1), Value::I16(v2)) => v1 < v2, (Value::U16(v1), Value::U16(v2)) => v1 < v2, (Value::I32(v1), Value::I32(v2)) => v1 < v2, (Value::U32(v1), Value::U32(v2)) => v1 < v2, (Value::I64(v1), Value::I64(v2)) => v1 < v2, (Value::U64(v1), Value::U64(v2)) => v1 < v2, (Value::F32(v1), Value::F32(v2)) => v1 < v2, (Value::F64(v1), Value::F64(v2)) => v1 < v2, _ => return Err(Error::TypeMismatch), }; Ok(Value::Generic(value as u64)) } /// Perform the `!= relational operation. /// /// This operation requires matching integral types. /// If the value type is `Generic`, then it is interpreted as a signed value. /// /// This corresponds to the DWARF `DW_OP_ne` operation. pub fn ne(self, rhs: Value, addr_mask: u64) -> Result { let value = match (self, rhs) { (Value::Generic(v1), Value::Generic(v2)) => { sign_extend(v1, addr_mask) != sign_extend(v2, addr_mask) } (Value::I8(v1), Value::I8(v2)) => v1 != v2, (Value::U8(v1), Value::U8(v2)) => v1 != v2, (Value::I16(v1), Value::I16(v2)) => v1 != v2, (Value::U16(v1), Value::U16(v2)) => v1 != v2, (Value::I32(v1), Value::I32(v2)) => v1 != v2, (Value::U32(v1), Value::U32(v2)) => v1 != v2, (Value::I64(v1), Value::I64(v2)) => v1 != v2, (Value::U64(v1), Value::U64(v2)) => v1 != v2, (Value::F32(v1), Value::F32(v2)) => v1 != v2, (Value::F64(v1), Value::F64(v2)) => v1 != v2, _ => return Err(Error::TypeMismatch), }; Ok(Value::Generic(value as u64)) } } #[cfg(test)] mod tests { use super::*; use crate::common::{DebugAbbrevOffset, DebugInfoOffset, Encoding, Format}; use crate::endianity::LittleEndian; use crate::read::{ Abbreviation, AttributeSpecification, DebuggingInformationEntry, EndianSlice, UnitHeader, UnitOffset, UnitType, }; #[test] #[rustfmt::skip] fn valuetype_from_encoding() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let unit = UnitHeader::new( encoding, 7, UnitType::Compilation, DebugAbbrevOffset(0), DebugInfoOffset(0).into(), EndianSlice::new(&[], LittleEndian), ); let abbrev = Abbreviation::new( 42, constants::DW_TAG_base_type, constants::DW_CHILDREN_no, vec![ AttributeSpecification::new( constants::DW_AT_byte_size, constants::DW_FORM_udata, None, ), AttributeSpecification::new( constants::DW_AT_encoding, constants::DW_FORM_udata, None, ), AttributeSpecification::new( constants::DW_AT_endianity, constants::DW_FORM_udata, None, ), ].into(), ); for &(attrs, result) in &[ ([0x01, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I8), ([0x02, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I16), ([0x04, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I32), ([0x08, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I64), ([0x01, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U8), ([0x02, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U16), ([0x04, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U32), ([0x08, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U64), ([0x04, constants::DW_ATE_float.0, constants::DW_END_default.0], ValueType::F32), ([0x08, constants::DW_ATE_float.0, constants::DW_END_default.0], ValueType::F64), ] { let entry = DebuggingInformationEntry::new( UnitOffset(0), EndianSlice::new(&attrs, LittleEndian), &abbrev, &unit, ); assert_eq!(ValueType::from_entry(&entry), Ok(Some(result))); } for attrs in &[ [0x03, constants::DW_ATE_signed.0, constants::DW_END_default.0], [0x02, constants::DW_ATE_signed.0, constants::DW_END_big.0], ] { let entry = DebuggingInformationEntry::new( UnitOffset(0), EndianSlice::new(attrs, LittleEndian), &abbrev, &unit, ); assert_eq!(ValueType::from_entry(&entry), Ok(None)); } } #[test] fn value_convert() { let addr_mask = !0 >> 32; for &(v, t, result) in &[ (Value::Generic(1), ValueType::I8, Ok(Value::I8(1))), (Value::I8(1), ValueType::U8, Ok(Value::U8(1))), (Value::U8(1), ValueType::I16, Ok(Value::I16(1))), (Value::I16(1), ValueType::U16, Ok(Value::U16(1))), (Value::U16(1), ValueType::I32, Ok(Value::I32(1))), (Value::I32(1), ValueType::U32, Ok(Value::U32(1))), (Value::U32(1), ValueType::F32, Ok(Value::F32(1.))), (Value::F32(1.), ValueType::I64, Ok(Value::I64(1))), (Value::I64(1), ValueType::U64, Ok(Value::U64(1))), (Value::U64(1), ValueType::F64, Ok(Value::F64(1.))), (Value::F64(1.), ValueType::Generic, Ok(Value::Generic(1))), ] { assert_eq!(v.convert(t, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_reinterpret() { let addr_mask = !0 >> 32; for &(v, t, result) in &[ // 8-bit (Value::I8(-1), ValueType::U8, Ok(Value::U8(0xff))), (Value::U8(0xff), ValueType::I8, Ok(Value::I8(-1))), // 16-bit (Value::I16(1), ValueType::U16, Ok(Value::U16(1))), (Value::U16(1), ValueType::I16, Ok(Value::I16(1))), // 32-bit (Value::Generic(1), ValueType::I32, Ok(Value::I32(1))), (Value::I32(1), ValueType::U32, Ok(Value::U32(1))), (Value::U32(0x3f80_0000), ValueType::F32, Ok(Value::F32(1.0))), (Value::F32(1.0), ValueType::Generic, Ok(Value::Generic(0x3f80_0000))), // Type mismatches (Value::Generic(1), ValueType::U8, Err(Error::TypeMismatch)), (Value::U8(1), ValueType::U16, Err(Error::TypeMismatch)), (Value::U16(1), ValueType::U32, Err(Error::TypeMismatch)), (Value::U32(1), ValueType::U64, Err(Error::TypeMismatch)), (Value::U64(1), ValueType::Generic, Err(Error::TypeMismatch)), ] { assert_eq!(v.reinterpret(t, addr_mask), result); } let addr_mask = !0; for &(v, t, result) in &[ // 64-bit (Value::Generic(1), ValueType::I64, Ok(Value::I64(1))), (Value::I64(1), ValueType::U64, Ok(Value::U64(1))), (Value::U64(0x3ff0_0000_0000_0000), ValueType::F64, Ok(Value::F64(1.0))), (Value::F64(1.0), ValueType::Generic, Ok(Value::Generic(0x3ff0_0000_0000_0000))), ] { assert_eq!(v.reinterpret(t, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_abs() { let addr_mask = 0xffff_ffff; for &(v, result) in &[ (Value::Generic(0xffff_ffff), Ok(Value::Generic(1))), (Value::I8(-1), Ok(Value::I8(1))), (Value::U8(1), Ok(Value::U8(1))), (Value::I16(-1), Ok(Value::I16(1))), (Value::U16(1), Ok(Value::U16(1))), (Value::I32(-1), Ok(Value::I32(1))), (Value::U32(1), Ok(Value::U32(1))), (Value::I64(-1), Ok(Value::I64(1))), (Value::U64(1), Ok(Value::U64(1))), (Value::F32(-1.), Ok(Value::F32(1.))), (Value::F64(-1.), Ok(Value::F64(1.))), ] { assert_eq!(v.abs(addr_mask), result); } } #[test] #[rustfmt::skip] fn value_neg() { let addr_mask = 0xffff_ffff; for &(v, result) in &[ (Value::Generic(0xffff_ffff), Ok(Value::Generic(1))), (Value::I8(1), Ok(Value::I8(-1))), (Value::U8(1), Err(Error::UnsupportedTypeOperation)), (Value::I16(1), Ok(Value::I16(-1))), (Value::U16(1), Err(Error::UnsupportedTypeOperation)), (Value::I32(1), Ok(Value::I32(-1))), (Value::U32(1), Err(Error::UnsupportedTypeOperation)), (Value::I64(1), Ok(Value::I64(-1))), (Value::U64(1), Err(Error::UnsupportedTypeOperation)), (Value::F32(1.), Ok(Value::F32(-1.))), (Value::F64(1.), Ok(Value::F64(-1.))), ] { assert_eq!(v.neg(addr_mask), result); } } #[test] #[rustfmt::skip] fn value_add() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(1), Value::Generic(2), Ok(Value::Generic(3))), (Value::I8(-1), Value::I8(2), Ok(Value::I8(1))), (Value::U8(1), Value::U8(2), Ok(Value::U8(3))), (Value::I16(-1), Value::I16(2), Ok(Value::I16(1))), (Value::U16(1), Value::U16(2), Ok(Value::U16(3))), (Value::I32(-1), Value::I32(2), Ok(Value::I32(1))), (Value::U32(1), Value::U32(2), Ok(Value::U32(3))), (Value::I64(-1), Value::I64(2), Ok(Value::I64(1))), (Value::U64(1), Value::U64(2), Ok(Value::U64(3))), (Value::F32(-1.), Value::F32(2.), Ok(Value::F32(1.))), (Value::F64(-1.), Value::F64(2.), Ok(Value::F64(1.))), (Value::Generic(1), Value::U32(2), Err(Error::TypeMismatch)), ] { assert_eq!(v1.add(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_sub() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(2), Ok(Value::Generic(1))), (Value::I8(-1), Value::I8(2), Ok(Value::I8(-3))), (Value::U8(3), Value::U8(2), Ok(Value::U8(1))), (Value::I16(-1), Value::I16(2), Ok(Value::I16(-3))), (Value::U16(3), Value::U16(2), Ok(Value::U16(1))), (Value::I32(-1), Value::I32(2), Ok(Value::I32(-3))), (Value::U32(3), Value::U32(2), Ok(Value::U32(1))), (Value::I64(-1), Value::I64(2), Ok(Value::I64(-3))), (Value::U64(3), Value::U64(2), Ok(Value::U64(1))), (Value::F32(-1.), Value::F32(2.), Ok(Value::F32(-3.))), (Value::F64(-1.), Value::F64(2.), Ok(Value::F64(-3.))), (Value::Generic(3), Value::U32(2), Err(Error::TypeMismatch)), ] { assert_eq!(v1.sub(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_mul() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(2), Value::Generic(3), Ok(Value::Generic(6))), (Value::I8(-2), Value::I8(3), Ok(Value::I8(-6))), (Value::U8(2), Value::U8(3), Ok(Value::U8(6))), (Value::I16(-2), Value::I16(3), Ok(Value::I16(-6))), (Value::U16(2), Value::U16(3), Ok(Value::U16(6))), (Value::I32(-2), Value::I32(3), Ok(Value::I32(-6))), (Value::U32(2), Value::U32(3), Ok(Value::U32(6))), (Value::I64(-2), Value::I64(3), Ok(Value::I64(-6))), (Value::U64(2), Value::U64(3), Ok(Value::U64(6))), (Value::F32(-2.), Value::F32(3.), Ok(Value::F32(-6.))), (Value::F64(-2.), Value::F64(3.), Ok(Value::F64(-6.))), (Value::Generic(2), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.mul(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_div() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(6), Value::Generic(3), Ok(Value::Generic(2))), (Value::I8(-6), Value::I8(3), Ok(Value::I8(-2))), (Value::U8(6), Value::U8(3), Ok(Value::U8(2))), (Value::I16(-6), Value::I16(3), Ok(Value::I16(-2))), (Value::U16(6), Value::U16(3), Ok(Value::U16(2))), (Value::I32(-6), Value::I32(3), Ok(Value::I32(-2))), (Value::U32(6), Value::U32(3), Ok(Value::U32(2))), (Value::I64(-6), Value::I64(3), Ok(Value::I64(-2))), (Value::U64(6), Value::U64(3), Ok(Value::U64(2))), (Value::F32(-6.), Value::F32(3.), Ok(Value::F32(-2.))), (Value::F64(-6.), Value::F64(3.), Ok(Value::F64(-2.))), (Value::Generic(6), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.div(v2, addr_mask), result); } for &(v1, v2, result) in &[ (Value::Generic(6), Value::Generic(0), Err(Error::DivisionByZero)), (Value::I8(-6), Value::I8(0), Err(Error::DivisionByZero)), (Value::U8(6), Value::U8(0), Err(Error::DivisionByZero)), (Value::I16(-6), Value::I16(0), Err(Error::DivisionByZero)), (Value::U16(6), Value::U16(0), Err(Error::DivisionByZero)), (Value::I32(-6), Value::I32(0), Err(Error::DivisionByZero)), (Value::U32(6), Value::U32(0), Err(Error::DivisionByZero)), (Value::I64(-6), Value::I64(0), Err(Error::DivisionByZero)), (Value::U64(6), Value::U64(0), Err(Error::DivisionByZero)), (Value::F32(-6.), Value::F32(0.), Ok(Value::F32(-6. / 0.))), (Value::F64(-6.), Value::F64(0.), Ok(Value::F64(-6. / 0.))), ] { assert_eq!(v1.div(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_rem() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(2), Ok(Value::Generic(1))), (Value::I8(-3), Value::I8(2), Ok(Value::I8(-1))), (Value::U8(3), Value::U8(2), Ok(Value::U8(1))), (Value::I16(-3), Value::I16(2), Ok(Value::I16(-1))), (Value::U16(3), Value::U16(2), Ok(Value::U16(1))), (Value::I32(-3), Value::I32(2), Ok(Value::I32(-1))), (Value::U32(3), Value::U32(2), Ok(Value::U32(1))), (Value::I64(-3), Value::I64(2), Ok(Value::I64(-1))), (Value::U64(3), Value::U64(2), Ok(Value::U64(1))), (Value::F32(-3.), Value::F32(2.), Err(Error::IntegralTypeRequired)), (Value::F64(-3.), Value::F64(2.), Err(Error::IntegralTypeRequired)), (Value::Generic(3), Value::U32(2), Err(Error::TypeMismatch)), ] { assert_eq!(v1.rem(v2, addr_mask), result); } for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(0), Err(Error::DivisionByZero)), (Value::I8(-3), Value::I8(0), Err(Error::DivisionByZero)), (Value::U8(3), Value::U8(0), Err(Error::DivisionByZero)), (Value::I16(-3), Value::I16(0), Err(Error::DivisionByZero)), (Value::U16(3), Value::U16(0), Err(Error::DivisionByZero)), (Value::I32(-3), Value::I32(0), Err(Error::DivisionByZero)), (Value::U32(3), Value::U32(0), Err(Error::DivisionByZero)), (Value::I64(-3), Value::I64(0), Err(Error::DivisionByZero)), (Value::U64(3), Value::U64(0), Err(Error::DivisionByZero)), ] { assert_eq!(v1.rem(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_not() { let addr_mask = 0xffff_ffff; for &(v, result) in &[ (Value::Generic(1), Ok(Value::Generic(!1))), (Value::I8(1), Ok(Value::I8(!1))), (Value::U8(1), Ok(Value::U8(!1))), (Value::I16(1), Ok(Value::I16(!1))), (Value::U16(1), Ok(Value::U16(!1))), (Value::I32(1), Ok(Value::I32(!1))), (Value::U32(1), Ok(Value::U32(!1))), (Value::I64(1), Ok(Value::I64(!1))), (Value::U64(1), Ok(Value::U64(!1))), (Value::F32(1.), Err(Error::IntegralTypeRequired)), (Value::F64(1.), Err(Error::IntegralTypeRequired)), ] { assert_eq!(v.not(addr_mask), result); } } #[test] #[rustfmt::skip] fn value_and() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(1))), (Value::I8(3), Value::I8(5), Ok(Value::I8(1))), (Value::U8(3), Value::U8(5), Ok(Value::U8(1))), (Value::I16(3), Value::I16(5), Ok(Value::I16(1))), (Value::U16(3), Value::U16(5), Ok(Value::U16(1))), (Value::I32(3), Value::I32(5), Ok(Value::I32(1))), (Value::U32(3), Value::U32(5), Ok(Value::U32(1))), (Value::I64(3), Value::I64(5), Ok(Value::I64(1))), (Value::U64(3), Value::U64(5), Ok(Value::U64(1))), (Value::F32(3.), Value::F32(5.), Err(Error::IntegralTypeRequired)), (Value::F64(3.), Value::F64(5.), Err(Error::IntegralTypeRequired)), (Value::Generic(3), Value::U32(5), Err(Error::TypeMismatch)), ] { assert_eq!(v1.and(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_or() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(7))), (Value::I8(3), Value::I8(5), Ok(Value::I8(7))), (Value::U8(3), Value::U8(5), Ok(Value::U8(7))), (Value::I16(3), Value::I16(5), Ok(Value::I16(7))), (Value::U16(3), Value::U16(5), Ok(Value::U16(7))), (Value::I32(3), Value::I32(5), Ok(Value::I32(7))), (Value::U32(3), Value::U32(5), Ok(Value::U32(7))), (Value::I64(3), Value::I64(5), Ok(Value::I64(7))), (Value::U64(3), Value::U64(5), Ok(Value::U64(7))), (Value::F32(3.), Value::F32(5.), Err(Error::IntegralTypeRequired)), (Value::F64(3.), Value::F64(5.), Err(Error::IntegralTypeRequired)), (Value::Generic(3), Value::U32(5), Err(Error::TypeMismatch)), ] { assert_eq!(v1.or(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_xor() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(6))), (Value::I8(3), Value::I8(5), Ok(Value::I8(6))), (Value::U8(3), Value::U8(5), Ok(Value::U8(6))), (Value::I16(3), Value::I16(5), Ok(Value::I16(6))), (Value::U16(3), Value::U16(5), Ok(Value::U16(6))), (Value::I32(3), Value::I32(5), Ok(Value::I32(6))), (Value::U32(3), Value::U32(5), Ok(Value::U32(6))), (Value::I64(3), Value::I64(5), Ok(Value::I64(6))), (Value::U64(3), Value::U64(5), Ok(Value::U64(6))), (Value::F32(3.), Value::F32(5.), Err(Error::IntegralTypeRequired)), (Value::F64(3.), Value::F64(5.), Err(Error::IntegralTypeRequired)), (Value::Generic(3), Value::U32(5), Err(Error::TypeMismatch)), ] { assert_eq!(v1.xor(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_shl() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ // One of each type (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(96))), (Value::I8(3), Value::U8(5), Ok(Value::I8(96))), (Value::U8(3), Value::I8(5), Ok(Value::U8(96))), (Value::I16(3), Value::U16(5), Ok(Value::I16(96))), (Value::U16(3), Value::I16(5), Ok(Value::U16(96))), (Value::I32(3), Value::U32(5), Ok(Value::I32(96))), (Value::U32(3), Value::I32(5), Ok(Value::U32(96))), (Value::I64(3), Value::U64(5), Ok(Value::I64(96))), (Value::U64(3), Value::I64(5), Ok(Value::U64(96))), (Value::F32(3.), Value::U8(5), Err(Error::IntegralTypeRequired)), (Value::F64(3.), Value::U8(5), Err(Error::IntegralTypeRequired)), // Invalid shifts (Value::U8(3), Value::I8(-5), Err(Error::InvalidShiftExpression)), (Value::U8(3), Value::I16(-5), Err(Error::InvalidShiftExpression)), (Value::U8(3), Value::I32(-5), Err(Error::InvalidShiftExpression)), (Value::U8(3), Value::I64(-5), Err(Error::InvalidShiftExpression)), (Value::U8(3), Value::F32(5.), Err(Error::InvalidShiftExpression)), (Value::U8(3), Value::F64(5.), Err(Error::InvalidShiftExpression)), // Large shifts (Value::Generic(3), Value::Generic(32), Ok(Value::Generic(0))), (Value::I8(3), Value::U8(8), Ok(Value::I8(0))), (Value::U8(3), Value::I8(9), Ok(Value::U8(0))), (Value::I16(3), Value::U16(17), Ok(Value::I16(0))), (Value::U16(3), Value::I16(16), Ok(Value::U16(0))), (Value::I32(3), Value::U32(32), Ok(Value::I32(0))), (Value::U32(3), Value::I32(33), Ok(Value::U32(0))), (Value::I64(3), Value::U64(65), Ok(Value::I64(0))), (Value::U64(3), Value::I64(64), Ok(Value::U64(0))), ] { assert_eq!(v1.shl(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_shr() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ // One of each type (Value::Generic(96), Value::Generic(5), Ok(Value::Generic(3))), (Value::I8(96), Value::U8(5), Err(Error::UnsupportedTypeOperation)), (Value::U8(96), Value::I8(5), Ok(Value::U8(3))), (Value::I16(96), Value::U16(5), Err(Error::UnsupportedTypeOperation)), (Value::U16(96), Value::I16(5), Ok(Value::U16(3))), (Value::I32(96), Value::U32(5), Err(Error::UnsupportedTypeOperation)), (Value::U32(96), Value::I32(5), Ok(Value::U32(3))), (Value::I64(96), Value::U64(5), Err(Error::UnsupportedTypeOperation)), (Value::U64(96), Value::I64(5), Ok(Value::U64(3))), (Value::F32(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), (Value::F64(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), // Invalid shifts (Value::U8(96), Value::I8(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::I16(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::I32(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::I64(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::F32(5.), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::F64(5.), Err(Error::InvalidShiftExpression)), // Large shifts (Value::Generic(96), Value::Generic(32), Ok(Value::Generic(0))), (Value::U8(96), Value::I8(9), Ok(Value::U8(0))), (Value::U16(96), Value::I16(16), Ok(Value::U16(0))), (Value::U32(96), Value::I32(33), Ok(Value::U32(0))), (Value::U64(96), Value::I64(64), Ok(Value::U64(0))), ] { assert_eq!(v1.shr(v2, addr_mask), result); } } #[test] #[rustfmt::skip] fn value_shra() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ // One of each type (Value::Generic(u64::from(-96i32 as u32)), Value::Generic(5), Ok(Value::Generic(-3i64 as u64))), (Value::I8(-96), Value::U8(5), Ok(Value::I8(-3))), (Value::U8(96), Value::I8(5), Err(Error::UnsupportedTypeOperation)), (Value::I16(-96), Value::U16(5), Ok(Value::I16(-3))), (Value::U16(96), Value::I16(5), Err(Error::UnsupportedTypeOperation)), (Value::I32(-96), Value::U32(5), Ok(Value::I32(-3))), (Value::U32(96), Value::I32(5), Err(Error::UnsupportedTypeOperation)), (Value::I64(-96), Value::U64(5), Ok(Value::I64(-3))), (Value::U64(96), Value::I64(5), Err(Error::UnsupportedTypeOperation)), (Value::F32(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), (Value::F64(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), // Invalid shifts (Value::U8(96), Value::I8(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::I16(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::I32(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::I64(-5), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::F32(5.), Err(Error::InvalidShiftExpression)), (Value::U8(96), Value::F64(5.), Err(Error::InvalidShiftExpression)), // Large shifts (Value::Generic(96), Value::Generic(32), Ok(Value::Generic(0))), (Value::I8(96), Value::U8(8), Ok(Value::I8(0))), (Value::I8(-96), Value::U8(8), Ok(Value::I8(-1))), (Value::I16(96), Value::U16(17), Ok(Value::I16(0))), (Value::I16(-96), Value::U16(17), Ok(Value::I16(-1))), (Value::I32(96), Value::U32(32), Ok(Value::I32(0))), (Value::I32(-96), Value::U32(32), Ok(Value::I32(-1))), (Value::I64(96), Value::U64(65), Ok(Value::I64(0))), (Value::I64(-96), Value::U64(65), Ok(Value::I64(-1))), ] { assert_eq!(v1.shra(v2, addr_mask), result); } } #[test] fn value_eq() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(3), Ok(Value::Generic(1))), (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(0))), (Value::I8(3), Value::I8(3), Ok(Value::Generic(1))), (Value::I8(!3), Value::I8(3), Ok(Value::Generic(0))), (Value::U8(3), Value::U8(3), Ok(Value::Generic(1))), (Value::U8(!3), Value::U8(3), Ok(Value::Generic(0))), (Value::I16(3), Value::I16(3), Ok(Value::Generic(1))), (Value::I16(!3), Value::I16(3), Ok(Value::Generic(0))), (Value::U16(3), Value::U16(3), Ok(Value::Generic(1))), (Value::U16(!3), Value::U16(3), Ok(Value::Generic(0))), (Value::I32(3), Value::I32(3), Ok(Value::Generic(1))), (Value::I32(!3), Value::I32(3), Ok(Value::Generic(0))), (Value::U32(3), Value::U32(3), Ok(Value::Generic(1))), (Value::U32(!3), Value::U32(3), Ok(Value::Generic(0))), (Value::I64(3), Value::I64(3), Ok(Value::Generic(1))), (Value::I64(!3), Value::I64(3), Ok(Value::Generic(0))), (Value::U64(3), Value::U64(3), Ok(Value::Generic(1))), (Value::U64(!3), Value::U64(3), Ok(Value::Generic(0))), (Value::F32(3.), Value::F32(3.), Ok(Value::Generic(1))), (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(0))), (Value::F64(3.), Value::F64(3.), Ok(Value::Generic(1))), (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(0))), (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.eq(v2, addr_mask), result); } } #[test] fn value_ne() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(3), Ok(Value::Generic(0))), (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(1))), (Value::I8(3), Value::I8(3), Ok(Value::Generic(0))), (Value::I8(!3), Value::I8(3), Ok(Value::Generic(1))), (Value::U8(3), Value::U8(3), Ok(Value::Generic(0))), (Value::U8(!3), Value::U8(3), Ok(Value::Generic(1))), (Value::I16(3), Value::I16(3), Ok(Value::Generic(0))), (Value::I16(!3), Value::I16(3), Ok(Value::Generic(1))), (Value::U16(3), Value::U16(3), Ok(Value::Generic(0))), (Value::U16(!3), Value::U16(3), Ok(Value::Generic(1))), (Value::I32(3), Value::I32(3), Ok(Value::Generic(0))), (Value::I32(!3), Value::I32(3), Ok(Value::Generic(1))), (Value::U32(3), Value::U32(3), Ok(Value::Generic(0))), (Value::U32(!3), Value::U32(3), Ok(Value::Generic(1))), (Value::I64(3), Value::I64(3), Ok(Value::Generic(0))), (Value::I64(!3), Value::I64(3), Ok(Value::Generic(1))), (Value::U64(3), Value::U64(3), Ok(Value::Generic(0))), (Value::U64(!3), Value::U64(3), Ok(Value::Generic(1))), (Value::F32(3.), Value::F32(3.), Ok(Value::Generic(0))), (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(1))), (Value::F64(3.), Value::F64(3.), Ok(Value::Generic(0))), (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(1))), (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.ne(v2, addr_mask), result); } } #[test] fn value_ge() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(1))), (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(0))), (Value::I8(3), Value::I8(!3), Ok(Value::Generic(1))), (Value::I8(!3), Value::I8(3), Ok(Value::Generic(0))), (Value::U8(3), Value::U8(!3), Ok(Value::Generic(0))), (Value::U8(!3), Value::U8(3), Ok(Value::Generic(1))), (Value::I16(3), Value::I16(!3), Ok(Value::Generic(1))), (Value::I16(!3), Value::I16(3), Ok(Value::Generic(0))), (Value::U16(3), Value::U16(!3), Ok(Value::Generic(0))), (Value::U16(!3), Value::U16(3), Ok(Value::Generic(1))), (Value::I32(3), Value::I32(!3), Ok(Value::Generic(1))), (Value::I32(!3), Value::I32(3), Ok(Value::Generic(0))), (Value::U32(3), Value::U32(!3), Ok(Value::Generic(0))), (Value::U32(!3), Value::U32(3), Ok(Value::Generic(1))), (Value::I64(3), Value::I64(!3), Ok(Value::Generic(1))), (Value::I64(!3), Value::I64(3), Ok(Value::Generic(0))), (Value::U64(3), Value::U64(!3), Ok(Value::Generic(0))), (Value::U64(!3), Value::U64(3), Ok(Value::Generic(1))), (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(1))), (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(0))), (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(1))), (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(0))), (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.ge(v2, addr_mask), result); } } #[test] fn value_gt() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(1))), (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(0))), (Value::I8(3), Value::I8(!3), Ok(Value::Generic(1))), (Value::I8(!3), Value::I8(3), Ok(Value::Generic(0))), (Value::U8(3), Value::U8(!3), Ok(Value::Generic(0))), (Value::U8(!3), Value::U8(3), Ok(Value::Generic(1))), (Value::I16(3), Value::I16(!3), Ok(Value::Generic(1))), (Value::I16(!3), Value::I16(3), Ok(Value::Generic(0))), (Value::U16(3), Value::U16(!3), Ok(Value::Generic(0))), (Value::U16(!3), Value::U16(3), Ok(Value::Generic(1))), (Value::I32(3), Value::I32(!3), Ok(Value::Generic(1))), (Value::I32(!3), Value::I32(3), Ok(Value::Generic(0))), (Value::U32(3), Value::U32(!3), Ok(Value::Generic(0))), (Value::U32(!3), Value::U32(3), Ok(Value::Generic(1))), (Value::I64(3), Value::I64(!3), Ok(Value::Generic(1))), (Value::I64(!3), Value::I64(3), Ok(Value::Generic(0))), (Value::U64(3), Value::U64(!3), Ok(Value::Generic(0))), (Value::U64(!3), Value::U64(3), Ok(Value::Generic(1))), (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(1))), (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(0))), (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(1))), (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(0))), (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.gt(v2, addr_mask), result); } } #[test] fn value_le() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(0))), (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(1))), (Value::I8(3), Value::I8(!3), Ok(Value::Generic(0))), (Value::I8(!3), Value::I8(3), Ok(Value::Generic(1))), (Value::U8(3), Value::U8(!3), Ok(Value::Generic(1))), (Value::U8(!3), Value::U8(3), Ok(Value::Generic(0))), (Value::I16(3), Value::I16(!3), Ok(Value::Generic(0))), (Value::I16(!3), Value::I16(3), Ok(Value::Generic(1))), (Value::U16(3), Value::U16(!3), Ok(Value::Generic(1))), (Value::U16(!3), Value::U16(3), Ok(Value::Generic(0))), (Value::I32(3), Value::I32(!3), Ok(Value::Generic(0))), (Value::I32(!3), Value::I32(3), Ok(Value::Generic(1))), (Value::U32(3), Value::U32(!3), Ok(Value::Generic(1))), (Value::U32(!3), Value::U32(3), Ok(Value::Generic(0))), (Value::I64(3), Value::I64(!3), Ok(Value::Generic(0))), (Value::I64(!3), Value::I64(3), Ok(Value::Generic(1))), (Value::U64(3), Value::U64(!3), Ok(Value::Generic(1))), (Value::U64(!3), Value::U64(3), Ok(Value::Generic(0))), (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(0))), (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(1))), (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(0))), (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(1))), (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.le(v2, addr_mask), result); } } #[test] fn value_lt() { let addr_mask = 0xffff_ffff; for &(v1, v2, result) in &[ (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(0))), (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(1))), (Value::I8(3), Value::I8(!3), Ok(Value::Generic(0))), (Value::I8(!3), Value::I8(3), Ok(Value::Generic(1))), (Value::U8(3), Value::U8(!3), Ok(Value::Generic(1))), (Value::U8(!3), Value::U8(3), Ok(Value::Generic(0))), (Value::I16(3), Value::I16(!3), Ok(Value::Generic(0))), (Value::I16(!3), Value::I16(3), Ok(Value::Generic(1))), (Value::U16(3), Value::U16(!3), Ok(Value::Generic(1))), (Value::U16(!3), Value::U16(3), Ok(Value::Generic(0))), (Value::I32(3), Value::I32(!3), Ok(Value::Generic(0))), (Value::I32(!3), Value::I32(3), Ok(Value::Generic(1))), (Value::U32(3), Value::U32(!3), Ok(Value::Generic(1))), (Value::U32(!3), Value::U32(3), Ok(Value::Generic(0))), (Value::I64(3), Value::I64(!3), Ok(Value::Generic(0))), (Value::I64(!3), Value::I64(3), Ok(Value::Generic(1))), (Value::U64(3), Value::U64(!3), Ok(Value::Generic(1))), (Value::U64(!3), Value::U64(3), Ok(Value::Generic(0))), (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(0))), (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(1))), (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(0))), (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(1))), (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), ] { assert_eq!(v1.lt(v2, addr_mask), result); } } } gimli-0.31.1/src/test_util.rs000064400000000000000000000027461046102023000141710ustar 00000000000000#![allow(missing_docs)] use crate::Format; use test_assembler::{Label, Section}; pub trait GimliSectionMethods { fn sleb(self, val: i64) -> Self; fn uleb(self, val: u64) -> Self; fn initial_length(self, format: Format, length: &Label, start: &Label) -> Self; fn word(self, size: u8, val: u64) -> Self; fn word_label(self, size: u8, val: &Label) -> Self; } impl GimliSectionMethods for Section { fn sleb(mut self, mut val: i64) -> Self { while val & !0x3f != 0 && val | 0x3f != -1 { self = self.D8(val as u8 | 0x80); val >>= 7; } self.D8(val as u8 & 0x7f) } fn uleb(mut self, mut val: u64) -> Self { while val & !0x7f != 0 { self = self.D8(val as u8 | 0x80); val >>= 7; } self.D8(val as u8) } fn initial_length(self, format: Format, length: &Label, start: &Label) -> Self { match format { Format::Dwarf32 => self.D32(length).mark(start), Format::Dwarf64 => self.D32(0xffff_ffff).D64(length).mark(start), } } fn word(self, size: u8, val: u64) -> Self { match size { 4 => self.D32(val as u32), 8 => self.D64(val), _ => panic!("unsupported word size"), } } fn word_label(self, size: u8, val: &Label) -> Self { match size { 4 => self.D32(val), 8 => self.D64(val), _ => panic!("unsupported word size"), } } } gimli-0.31.1/src/write/abbrev.rs000064400000000000000000000137621046102023000145500ustar 00000000000000use alloc::vec::Vec; use indexmap::IndexSet; use std::ops::{Deref, DerefMut}; use crate::common::{DebugAbbrevOffset, SectionId}; use crate::constants; use crate::write::{Result, Section, Writer}; /// A table of abbreviations that will be stored in a `.debug_abbrev` section. // Requirements: // - values are `Abbreviation` // - insertion returns an abbreviation code for use in writing a DIE // - inserting a duplicate returns the code of the existing value #[derive(Debug, Default)] pub(crate) struct AbbreviationTable { abbrevs: IndexSet, } impl AbbreviationTable { /// Add an abbreviation to the table and return its code. pub fn add(&mut self, abbrev: Abbreviation) -> u64 { let (code, _) = self.abbrevs.insert_full(abbrev); // Code must be non-zero (code + 1) as u64 } /// Write the abbreviation table to the `.debug_abbrev` section. pub fn write(&self, w: &mut DebugAbbrev) -> Result<()> { for (code, abbrev) in self.abbrevs.iter().enumerate() { w.write_uleb128((code + 1) as u64)?; abbrev.write(w)?; } // Null abbreviation code w.write_u8(0) } } /// An abbreviation describes the shape of a `DebuggingInformationEntry`'s type: /// its tag type, whether it has children, and its set of attributes. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct Abbreviation { tag: constants::DwTag, has_children: bool, attributes: Vec, } impl Abbreviation { /// Construct a new `Abbreviation`. #[inline] pub fn new( tag: constants::DwTag, has_children: bool, attributes: Vec, ) -> Abbreviation { Abbreviation { tag, has_children, attributes, } } /// Write the abbreviation to the `.debug_abbrev` section. pub fn write(&self, w: &mut DebugAbbrev) -> Result<()> { w.write_uleb128(self.tag.0.into())?; w.write_u8(if self.has_children { constants::DW_CHILDREN_yes.0 } else { constants::DW_CHILDREN_no.0 })?; for attr in &self.attributes { attr.write(w)?; } // Null name and form w.write_u8(0)?; w.write_u8(0) } } /// The description of an attribute in an abbreviated type. // TODO: support implicit const #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct AttributeSpecification { name: constants::DwAt, form: constants::DwForm, } impl AttributeSpecification { /// Construct a new `AttributeSpecification`. #[inline] pub fn new(name: constants::DwAt, form: constants::DwForm) -> AttributeSpecification { AttributeSpecification { name, form } } /// Write the attribute specification to the `.debug_abbrev` section. #[inline] pub fn write(&self, w: &mut DebugAbbrev) -> Result<()> { w.write_uleb128(self.name.0.into())?; w.write_uleb128(self.form.0.into()) } } define_section!( DebugAbbrev, DebugAbbrevOffset, "A writable `.debug_abbrev` section." ); #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::constants; use crate::read; use crate::write::EndianVec; use crate::LittleEndian; #[test] fn test_abbreviation_table() { let mut abbrevs = AbbreviationTable::default(); let abbrev1 = Abbreviation::new( constants::DW_TAG_subprogram, false, vec![AttributeSpecification::new( constants::DW_AT_name, constants::DW_FORM_string, )], ); let abbrev2 = Abbreviation::new( constants::DW_TAG_compile_unit, true, vec![ AttributeSpecification::new(constants::DW_AT_producer, constants::DW_FORM_strp), AttributeSpecification::new(constants::DW_AT_language, constants::DW_FORM_data2), ], ); let code1 = abbrevs.add(abbrev1.clone()); assert_eq!(code1, 1); let code2 = abbrevs.add(abbrev2.clone()); assert_eq!(code2, 2); assert_eq!(abbrevs.add(abbrev1.clone()), code1); assert_eq!(abbrevs.add(abbrev2.clone()), code2); let mut debug_abbrev = DebugAbbrev::from(EndianVec::new(LittleEndian)); let debug_abbrev_offset = debug_abbrev.offset(); assert_eq!(debug_abbrev_offset, DebugAbbrevOffset(0)); abbrevs.write(&mut debug_abbrev).unwrap(); assert_eq!(debug_abbrev.offset(), DebugAbbrevOffset(17)); let read_debug_abbrev = read::DebugAbbrev::new(debug_abbrev.slice(), LittleEndian); let read_abbrevs = read_debug_abbrev .abbreviations(debug_abbrev_offset) .unwrap(); let read_abbrev1 = read_abbrevs.get(code1).unwrap(); assert_eq!(abbrev1.tag, read_abbrev1.tag()); assert_eq!(abbrev1.has_children, read_abbrev1.has_children()); assert_eq!(abbrev1.attributes.len(), read_abbrev1.attributes().len()); assert_eq!( abbrev1.attributes[0].name, read_abbrev1.attributes()[0].name() ); assert_eq!( abbrev1.attributes[0].form, read_abbrev1.attributes()[0].form() ); let read_abbrev2 = read_abbrevs.get(code2).unwrap(); assert_eq!(abbrev2.tag, read_abbrev2.tag()); assert_eq!(abbrev2.has_children, read_abbrev2.has_children()); assert_eq!(abbrev2.attributes.len(), read_abbrev2.attributes().len()); assert_eq!( abbrev2.attributes[0].name, read_abbrev2.attributes()[0].name() ); assert_eq!( abbrev2.attributes[0].form, read_abbrev2.attributes()[0].form() ); assert_eq!( abbrev2.attributes[1].name, read_abbrev2.attributes()[1].name() ); assert_eq!( abbrev2.attributes[1].form, read_abbrev2.attributes()[1].form() ); } } gimli-0.31.1/src/write/cfi.rs000064400000000000000000001234501046102023000140440ustar 00000000000000use alloc::vec::Vec; use indexmap::IndexSet; use std::ops::{Deref, DerefMut}; use crate::common::{DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId}; use crate::constants; use crate::write::{Address, BaseId, Error, Expression, Result, Section, Writer}; define_section!( DebugFrame, DebugFrameOffset, "A writable `.debug_frame` section." ); define_section!(EhFrame, EhFrameOffset, "A writable `.eh_frame` section."); define_id!(CieId, "An identifier for a CIE in a `FrameTable`."); /// A table of frame description entries. #[derive(Debug, Default)] pub struct FrameTable { /// Base id for CIEs. base_id: BaseId, /// The common information entries. cies: IndexSet, /// The frame description entries. fdes: Vec<(CieId, FrameDescriptionEntry)>, } impl FrameTable { /// Add a CIE and return its id. /// /// If the CIE already exists, then return the id of the existing CIE. pub fn add_cie(&mut self, cie: CommonInformationEntry) -> CieId { let (index, _) = self.cies.insert_full(cie); CieId::new(self.base_id, index) } /// The number of CIEs. pub fn cie_count(&self) -> usize { self.cies.len() } /// Add a FDE. /// /// Does not check for duplicates. /// /// # Panics /// /// Panics if the CIE id is invalid. pub fn add_fde(&mut self, cie: CieId, fde: FrameDescriptionEntry) { debug_assert_eq!(self.base_id, cie.base_id); self.fdes.push((cie, fde)); } /// The number of FDEs. pub fn fde_count(&self) -> usize { self.fdes.len() } /// Write the frame table entries to the given `.debug_frame` section. pub fn write_debug_frame(&self, w: &mut DebugFrame) -> Result<()> { self.write(&mut w.0, false) } /// Write the frame table entries to the given `.eh_frame` section. pub fn write_eh_frame(&self, w: &mut EhFrame) -> Result<()> { self.write(&mut w.0, true) } fn write(&self, w: &mut W, eh_frame: bool) -> Result<()> { let mut cie_offsets = vec![None; self.cies.len()]; for (cie_id, fde) in &self.fdes { let cie_index = cie_id.index; let cie = self.cies.get_index(cie_index).unwrap(); let cie_offset = match cie_offsets[cie_index] { Some(offset) => offset, None => { // Only write CIEs as they are referenced. let offset = cie.write(w, eh_frame)?; cie_offsets[cie_index] = Some(offset); offset } }; fde.write(w, eh_frame, cie_offset, cie)?; } // TODO: write length 0 terminator for eh_frame? Ok(()) } } /// A common information entry. This contains information that is shared between FDEs. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CommonInformationEntry { encoding: Encoding, /// A constant that is factored out of code offsets. /// /// This should be set to the minimum instruction length. /// Writing a code offset that is not a multiple of this factor will generate an error. code_alignment_factor: u8, /// A constant that is factored out of data offsets. /// /// This should be set to the minimum data alignment for the frame. /// Writing a data offset that is not a multiple of this factor will generate an error. data_alignment_factor: i8, /// The return address register. This might not correspond to an actual machine register. return_address_register: Register, /// The address of the personality function and its encoding. pub personality: Option<(constants::DwEhPe, Address)>, /// The encoding to use for the LSDA address in FDEs. /// /// If set then all FDEs which use this CIE must have a LSDA address. pub lsda_encoding: Option, /// The encoding to use for addresses in FDEs. pub fde_address_encoding: constants::DwEhPe, /// True for signal trampolines. pub signal_trampoline: bool, /// The initial instructions upon entry to this function. instructions: Vec, } impl CommonInformationEntry { /// Create a new common information entry. /// /// The encoding version must be a CFI version, not a DWARF version. pub fn new( encoding: Encoding, code_alignment_factor: u8, data_alignment_factor: i8, return_address_register: Register, ) -> Self { CommonInformationEntry { encoding, code_alignment_factor, data_alignment_factor, return_address_register, personality: None, lsda_encoding: None, fde_address_encoding: constants::DW_EH_PE_absptr, signal_trampoline: false, instructions: Vec::new(), } } /// Add an initial instruction. pub fn add_instruction(&mut self, instruction: CallFrameInstruction) { self.instructions.push(instruction); } fn has_augmentation(&self) -> bool { self.personality.is_some() || self.lsda_encoding.is_some() || self.signal_trampoline || self.fde_address_encoding != constants::DW_EH_PE_absptr } /// Returns the section offset of the CIE. fn write(&self, w: &mut W, eh_frame: bool) -> Result { let encoding = self.encoding; let offset = w.len(); let length_offset = w.write_initial_length(encoding.format)?; let length_base = w.len(); if eh_frame { w.write_u32(0)?; } else { match encoding.format { Format::Dwarf32 => w.write_u32(0xffff_ffff)?, Format::Dwarf64 => w.write_u64(0xffff_ffff_ffff_ffff)?, } } if eh_frame { if encoding.version != 1 { return Err(Error::UnsupportedVersion(encoding.version)); }; } else { match encoding.version { 1 | 3 | 4 => {} _ => return Err(Error::UnsupportedVersion(encoding.version)), }; } w.write_u8(encoding.version as u8)?; let augmentation = self.has_augmentation(); if augmentation { w.write_u8(b'z')?; if self.lsda_encoding.is_some() { w.write_u8(b'L')?; } if self.personality.is_some() { w.write_u8(b'P')?; } if self.fde_address_encoding != constants::DW_EH_PE_absptr { w.write_u8(b'R')?; } if self.signal_trampoline { w.write_u8(b'S')?; } } w.write_u8(0)?; if encoding.version >= 4 { w.write_u8(encoding.address_size)?; w.write_u8(0)?; // segment_selector_size } w.write_uleb128(self.code_alignment_factor.into())?; w.write_sleb128(self.data_alignment_factor.into())?; if !eh_frame && encoding.version == 1 { let register = self.return_address_register.0 as u8; if u16::from(register) != self.return_address_register.0 { return Err(Error::ValueTooLarge); } w.write_u8(register)?; } else { w.write_uleb128(self.return_address_register.0.into())?; } if augmentation { let augmentation_length_offset = w.len(); w.write_u8(0)?; let augmentation_length_base = w.len(); if let Some(eh_pe) = self.lsda_encoding { w.write_u8(eh_pe.0)?; } if let Some((eh_pe, address)) = self.personality { w.write_u8(eh_pe.0)?; w.write_eh_pointer(address, eh_pe, encoding.address_size)?; } if self.fde_address_encoding != constants::DW_EH_PE_absptr { w.write_u8(self.fde_address_encoding.0)?; } let augmentation_length = (w.len() - augmentation_length_base) as u64; debug_assert!(augmentation_length < 0x80); w.write_udata_at(augmentation_length_offset, augmentation_length, 1)?; } for instruction in &self.instructions { instruction.write(w, encoding, self)?; } write_nop( w, encoding.format.word_size() as usize + w.len() - length_base, encoding.address_size, )?; let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, encoding.format)?; Ok(offset) } } /// A frame description entry. There should be one FDE per function. #[derive(Debug, Clone, PartialEq, Eq)] pub struct FrameDescriptionEntry { /// The initial address of the function. address: Address, /// The length in bytes of the function. length: u32, /// The address of the LSDA. pub lsda: Option
, /// The instructions for this function, ordered by offset. instructions: Vec<(u32, CallFrameInstruction)>, } impl FrameDescriptionEntry { /// Create a new frame description entry for a function. pub fn new(address: Address, length: u32) -> Self { FrameDescriptionEntry { address, length, lsda: None, instructions: Vec::new(), } } /// Add an instruction. /// /// Instructions must be added in increasing order of offset, or writing will fail. pub fn add_instruction(&mut self, offset: u32, instruction: CallFrameInstruction) { debug_assert!(self.instructions.last().map(|x| x.0).unwrap_or(0) <= offset); self.instructions.push((offset, instruction)); } fn write( &self, w: &mut W, eh_frame: bool, cie_offset: usize, cie: &CommonInformationEntry, ) -> Result<()> { let encoding = cie.encoding; let length_offset = w.write_initial_length(encoding.format)?; let length_base = w.len(); if eh_frame { // .eh_frame uses a relative offset which doesn't need relocation. w.write_udata((w.len() - cie_offset) as u64, 4)?; } else { w.write_offset( cie_offset, SectionId::DebugFrame, encoding.format.word_size(), )?; } if cie.fde_address_encoding != constants::DW_EH_PE_absptr { w.write_eh_pointer( self.address, cie.fde_address_encoding, encoding.address_size, )?; w.write_eh_pointer_data( self.length.into(), cie.fde_address_encoding.format(), encoding.address_size, )?; } else { w.write_address(self.address, encoding.address_size)?; w.write_udata(self.length.into(), encoding.address_size)?; } if cie.has_augmentation() { let augmentation_length_offset = w.len(); w.write_u8(0)?; let augmentation_length_base = w.len(); debug_assert_eq!(self.lsda.is_some(), cie.lsda_encoding.is_some()); if let (Some(lsda), Some(lsda_encoding)) = (self.lsda, cie.lsda_encoding) { w.write_eh_pointer(lsda, lsda_encoding, encoding.address_size)?; } let augmentation_length = (w.len() - augmentation_length_base) as u64; debug_assert!(augmentation_length < 0x80); w.write_udata_at(augmentation_length_offset, augmentation_length, 1)?; } let mut prev_offset = 0; for (offset, instruction) in &self.instructions { write_advance_loc(w, cie.code_alignment_factor, prev_offset, *offset)?; prev_offset = *offset; instruction.write(w, encoding, cie)?; } write_nop( w, encoding.format.word_size() as usize + w.len() - length_base, encoding.address_size, )?; let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, encoding.format)?; Ok(()) } } /// An instruction in a frame description entry. /// /// This may be a CFA definition, a register rule, or some other directive. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum CallFrameInstruction { /// Define the CFA rule to use the provided register and offset. Cfa(Register, i32), /// Update the CFA rule to use the provided register. The offset is unchanged. CfaRegister(Register), /// Update the CFA rule to use the provided offset. The register is unchanged. CfaOffset(i32), /// Define the CFA rule to use the provided expression. CfaExpression(Expression), /// Restore the initial rule for the register. Restore(Register), /// The previous value of the register is not recoverable. Undefined(Register), /// The register has not been modified. SameValue(Register), /// The previous value of the register is saved at address CFA + offset. Offset(Register, i32), /// The previous value of the register is CFA + offset. ValOffset(Register, i32), /// The previous value of the register is stored in another register. Register(Register, Register), /// The previous value of the register is saved at address given by the expression. Expression(Register, Expression), /// The previous value of the register is given by the expression. ValExpression(Register, Expression), /// Push all register rules onto a stack. RememberState, /// Pop all register rules off the stack. RestoreState, /// The size of the arguments that have been pushed onto the stack. ArgsSize(u32), /// AAarch64 extension: negate the `RA_SIGN_STATE` pseudo-register. NegateRaState, } impl CallFrameInstruction { fn write( &self, w: &mut W, encoding: Encoding, cie: &CommonInformationEntry, ) -> Result<()> { match *self { CallFrameInstruction::Cfa(register, offset) => { if offset < 0 { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; w.write_u8(constants::DW_CFA_def_cfa_sf.0)?; w.write_uleb128(register.0.into())?; w.write_sleb128(offset.into())?; } else { // Unfactored offset. w.write_u8(constants::DW_CFA_def_cfa.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::CfaRegister(register) => { w.write_u8(constants::DW_CFA_def_cfa_register.0)?; w.write_uleb128(register.0.into())?; } CallFrameInstruction::CfaOffset(offset) => { if offset < 0 { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; w.write_u8(constants::DW_CFA_def_cfa_offset_sf.0)?; w.write_sleb128(offset.into())?; } else { // Unfactored offset. w.write_u8(constants::DW_CFA_def_cfa_offset.0)?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::CfaExpression(ref expression) => { w.write_u8(constants::DW_CFA_def_cfa_expression.0)?; w.write_uleb128(expression.size(encoding, None) as u64)?; expression.write(w, None, encoding, None)?; } CallFrameInstruction::Restore(register) => { if register.0 < 0x40 { w.write_u8(constants::DW_CFA_restore.0 | register.0 as u8)?; } else { w.write_u8(constants::DW_CFA_restore_extended.0)?; w.write_uleb128(register.0.into())?; } } CallFrameInstruction::Undefined(register) => { w.write_u8(constants::DW_CFA_undefined.0)?; w.write_uleb128(register.0.into())?; } CallFrameInstruction::SameValue(register) => { w.write_u8(constants::DW_CFA_same_value.0)?; w.write_uleb128(register.0.into())?; } CallFrameInstruction::Offset(register, offset) => { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; if offset < 0 { w.write_u8(constants::DW_CFA_offset_extended_sf.0)?; w.write_uleb128(register.0.into())?; w.write_sleb128(offset.into())?; } else if register.0 < 0x40 { w.write_u8(constants::DW_CFA_offset.0 | register.0 as u8)?; w.write_uleb128(offset as u64)?; } else { w.write_u8(constants::DW_CFA_offset_extended.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::ValOffset(register, offset) => { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; if offset < 0 { w.write_u8(constants::DW_CFA_val_offset_sf.0)?; w.write_uleb128(register.0.into())?; w.write_sleb128(offset.into())?; } else { w.write_u8(constants::DW_CFA_val_offset.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::Register(register1, register2) => { w.write_u8(constants::DW_CFA_register.0)?; w.write_uleb128(register1.0.into())?; w.write_uleb128(register2.0.into())?; } CallFrameInstruction::Expression(register, ref expression) => { w.write_u8(constants::DW_CFA_expression.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(expression.size(encoding, None) as u64)?; expression.write(w, None, encoding, None)?; } CallFrameInstruction::ValExpression(register, ref expression) => { w.write_u8(constants::DW_CFA_val_expression.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(expression.size(encoding, None) as u64)?; expression.write(w, None, encoding, None)?; } CallFrameInstruction::RememberState => { w.write_u8(constants::DW_CFA_remember_state.0)?; } CallFrameInstruction::RestoreState => { w.write_u8(constants::DW_CFA_restore_state.0)?; } CallFrameInstruction::ArgsSize(size) => { w.write_u8(constants::DW_CFA_GNU_args_size.0)?; w.write_uleb128(size.into())?; } CallFrameInstruction::NegateRaState => { w.write_u8(constants::DW_CFA_AARCH64_negate_ra_state.0)?; } } Ok(()) } } fn write_advance_loc( w: &mut W, code_alignment_factor: u8, prev_offset: u32, offset: u32, ) -> Result<()> { if offset == prev_offset { return Ok(()); } let delta = factored_code_delta(prev_offset, offset, code_alignment_factor)?; if delta < 0x40 { w.write_u8(constants::DW_CFA_advance_loc.0 | delta as u8)?; } else if delta < 0x100 { w.write_u8(constants::DW_CFA_advance_loc1.0)?; w.write_u8(delta as u8)?; } else if delta < 0x10000 { w.write_u8(constants::DW_CFA_advance_loc2.0)?; w.write_u16(delta as u16)?; } else { w.write_u8(constants::DW_CFA_advance_loc4.0)?; w.write_u32(delta)?; } Ok(()) } fn write_nop(w: &mut W, len: usize, align: u8) -> Result<()> { debug_assert_eq!(align & (align - 1), 0); let tail_len = (!len + 1) & (align as usize - 1); for _ in 0..tail_len { w.write_u8(constants::DW_CFA_nop.0)?; } Ok(()) } fn factored_code_delta(prev_offset: u32, offset: u32, factor: u8) -> Result { if offset < prev_offset { return Err(Error::InvalidFrameCodeOffset(offset)); } let delta = offset - prev_offset; let factor = u32::from(factor); let factored_delta = delta / factor; if delta != factored_delta * factor { return Err(Error::InvalidFrameCodeOffset(offset)); } Ok(factored_delta) } fn factored_data_offset(offset: i32, factor: i8) -> Result { let factor = i32::from(factor); let factored_offset = offset / factor; if offset != factored_offset * factor { return Err(Error::InvalidFrameDataOffset(offset)); } Ok(factored_offset) } #[cfg(feature = "read")] pub(crate) mod convert { use super::*; use crate::read::{self, Reader}; use crate::write::{ConvertError, ConvertResult}; use std::collections::{hash_map, HashMap}; impl FrameTable { /// Create a frame table by reading the data in the given section. /// /// `convert_address` is a function to convert read addresses into the `Address` /// type. For non-relocatable addresses, this function may simply return /// `Address::Constant(address)`. For relocatable addresses, it is the caller's /// responsibility to determine the symbol and addend corresponding to the address /// and return `Address::Symbol { symbol, addend }`. pub fn from( frame: &Section, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult where R: Reader, Section: read::UnwindSection, Section::Offset: read::UnwindOffset, { let bases = read::BaseAddresses::default().set_eh_frame(0); let mut frame_table = FrameTable::default(); let mut cie_ids = HashMap::new(); let mut entries = frame.entries(&bases); while let Some(entry) = entries.next()? { let partial = match entry { read::CieOrFde::Cie(_) => continue, read::CieOrFde::Fde(partial) => partial, }; // TODO: is it worth caching the parsed CIEs? It would be better if FDEs only // stored a reference. let from_fde = partial.parse(Section::cie_from_offset)?; let from_cie = from_fde.cie(); let cie_id = match cie_ids.entry(from_cie.offset()) { hash_map::Entry::Occupied(o) => *o.get(), hash_map::Entry::Vacant(e) => { let cie = CommonInformationEntry::from(from_cie, frame, &bases, convert_address)?; let cie_id = frame_table.add_cie(cie); e.insert(cie_id); cie_id } }; let fde = FrameDescriptionEntry::from(&from_fde, frame, &bases, convert_address)?; frame_table.add_fde(cie_id, fde); } Ok(frame_table) } } impl CommonInformationEntry { fn from( from_cie: &read::CommonInformationEntry, frame: &Section, bases: &read::BaseAddresses, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult where R: Reader, Section: read::UnwindSection, Section::Offset: read::UnwindOffset, { let mut cie = CommonInformationEntry::new( from_cie.encoding(), from_cie.code_alignment_factor() as u8, from_cie.data_alignment_factor() as i8, from_cie.return_address_register(), ); cie.personality = match from_cie.personality_with_encoding() { // We treat these the same because the encoding already determines // whether it is indirect. Some((eh_pe, read::Pointer::Direct(p))) | Some((eh_pe, read::Pointer::Indirect(p))) => { let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?; Some((eh_pe, address)) } _ => None, }; cie.lsda_encoding = from_cie.lsda_encoding(); cie.fde_address_encoding = from_cie .fde_address_encoding() .unwrap_or(constants::DW_EH_PE_absptr); cie.signal_trampoline = from_cie.is_signal_trampoline(); let mut offset = 0; let mut from_instructions = from_cie.instructions(frame, bases); while let Some(from_instruction) = from_instructions.next()? { if let Some(instruction) = CallFrameInstruction::from( from_instruction, from_cie, frame, convert_address, &mut offset, )? { cie.instructions.push(instruction); } } Ok(cie) } } impl FrameDescriptionEntry { fn from( from_fde: &read::FrameDescriptionEntry, frame: &Section, bases: &read::BaseAddresses, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult where R: Reader, Section: read::UnwindSection, Section::Offset: read::UnwindOffset, { let address = convert_address(from_fde.initial_address()).ok_or(ConvertError::InvalidAddress)?; let length = from_fde.len() as u32; let mut fde = FrameDescriptionEntry::new(address, length); match from_fde.lsda() { // We treat these the same because the encoding already determines // whether it is indirect. Some(read::Pointer::Direct(p)) | Some(read::Pointer::Indirect(p)) => { let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?; fde.lsda = Some(address); } None => {} } let from_cie = from_fde.cie(); let mut offset = 0; let mut from_instructions = from_fde.instructions(frame, bases); while let Some(from_instruction) = from_instructions.next()? { if let Some(instruction) = CallFrameInstruction::from( from_instruction, from_cie, frame, convert_address, &mut offset, )? { fde.instructions.push((offset, instruction)); } } Ok(fde) } } impl CallFrameInstruction { fn from( from_instruction: read::CallFrameInstruction, from_cie: &read::CommonInformationEntry, frame: &Section, convert_address: &dyn Fn(u64) -> Option
, offset: &mut u32, ) -> ConvertResult> where R: Reader, Section: read::UnwindSection, { let convert_expression = |x| Expression::from(x, from_cie.encoding(), None, None, None, convert_address); // TODO: validate integer type conversions Ok(Some(match from_instruction { read::CallFrameInstruction::SetLoc { .. } => { return Err(ConvertError::UnsupportedCfiInstruction); } read::CallFrameInstruction::AdvanceLoc { delta } => { *offset += delta * from_cie.code_alignment_factor() as u32; return Ok(None); } read::CallFrameInstruction::DefCfa { register, offset } => { CallFrameInstruction::Cfa(register, offset as i32) } read::CallFrameInstruction::DefCfaSf { register, factored_offset, } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::Cfa(register, offset as i32) } read::CallFrameInstruction::DefCfaRegister { register } => { CallFrameInstruction::CfaRegister(register) } read::CallFrameInstruction::DefCfaOffset { offset } => { CallFrameInstruction::CfaOffset(offset as i32) } read::CallFrameInstruction::DefCfaOffsetSf { factored_offset } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::CfaOffset(offset as i32) } read::CallFrameInstruction::DefCfaExpression { expression } => { let expression = expression.get(frame)?; CallFrameInstruction::CfaExpression(convert_expression(expression)?) } read::CallFrameInstruction::Undefined { register } => { CallFrameInstruction::Undefined(register) } read::CallFrameInstruction::SameValue { register } => { CallFrameInstruction::SameValue(register) } read::CallFrameInstruction::Offset { register, factored_offset, } => { let offset = factored_offset as i64 * from_cie.data_alignment_factor(); CallFrameInstruction::Offset(register, offset as i32) } read::CallFrameInstruction::OffsetExtendedSf { register, factored_offset, } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::Offset(register, offset as i32) } read::CallFrameInstruction::ValOffset { register, factored_offset, } => { let offset = factored_offset as i64 * from_cie.data_alignment_factor(); CallFrameInstruction::ValOffset(register, offset as i32) } read::CallFrameInstruction::ValOffsetSf { register, factored_offset, } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::ValOffset(register, offset as i32) } read::CallFrameInstruction::Register { dest_register, src_register, } => CallFrameInstruction::Register(dest_register, src_register), read::CallFrameInstruction::Expression { register, expression, } => { let expression = expression.get(frame)?; CallFrameInstruction::Expression(register, convert_expression(expression)?) } read::CallFrameInstruction::ValExpression { register, expression, } => { let expression = expression.get(frame)?; CallFrameInstruction::ValExpression(register, convert_expression(expression)?) } read::CallFrameInstruction::Restore { register } => { CallFrameInstruction::Restore(register) } read::CallFrameInstruction::RememberState => CallFrameInstruction::RememberState, read::CallFrameInstruction::RestoreState => CallFrameInstruction::RestoreState, read::CallFrameInstruction::ArgsSize { size } => { CallFrameInstruction::ArgsSize(size as u32) } read::CallFrameInstruction::NegateRaState => CallFrameInstruction::NegateRaState, read::CallFrameInstruction::Nop => return Ok(None), })) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::arch::X86_64; use crate::read; use crate::write::EndianVec; use crate::{LittleEndian, Vendor}; #[test] fn test_frame_table() { for &version in &[1, 3, 4] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut frames = FrameTable::default(); let cie1 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); let cie1_id = frames.add_cie(cie1.clone()); assert_eq!(cie1_id, frames.add_cie(cie1.clone())); let mut cie2 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); cie2.lsda_encoding = Some(constants::DW_EH_PE_absptr); cie2.personality = Some((constants::DW_EH_PE_absptr, Address::Constant(0x1234))); cie2.signal_trampoline = true; let cie2_id = frames.add_cie(cie2.clone()); assert_ne!(cie1_id, cie2_id); assert_eq!(cie2_id, frames.add_cie(cie2.clone())); let fde1 = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); frames.add_fde(cie1_id, fde1.clone()); let fde2 = FrameDescriptionEntry::new(Address::Constant(0x2000), 0x20); frames.add_fde(cie1_id, fde2.clone()); let mut fde3 = FrameDescriptionEntry::new(Address::Constant(0x3000), 0x30); fde3.lsda = Some(Address::Constant(0x3300)); frames.add_fde(cie2_id, fde3.clone()); let mut fde4 = FrameDescriptionEntry::new(Address::Constant(0x4000), 0x40); fde4.lsda = Some(Address::Constant(0x4400)); frames.add_fde(cie2_id, fde4.clone()); let mut cie3 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); cie3.fde_address_encoding = constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4; cie3.lsda_encoding = Some(constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4); cie3.personality = Some(( constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, Address::Constant(0x1235), )); cie3.signal_trampoline = true; let cie3_id = frames.add_cie(cie3.clone()); assert_ne!(cie2_id, cie3_id); assert_eq!(cie3_id, frames.add_cie(cie3.clone())); let mut fde5 = FrameDescriptionEntry::new(Address::Constant(0x5000), 0x50); fde5.lsda = Some(Address::Constant(0x5500)); frames.add_fde(cie3_id, fde5.clone()); // Test writing `.debug_frame`. let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); frames.write_debug_frame(&mut debug_frame).unwrap(); let mut read_debug_frame = read::DebugFrame::new(debug_frame.slice(), LittleEndian); read_debug_frame.set_address_size(address_size); let convert_frames = FrameTable::from(&read_debug_frame, &|address| { Some(Address::Constant(address)) }) .unwrap(); assert_eq!(frames.cies, convert_frames.cies); assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { assert_eq!(a.1, b.1); } if version == 1 { // Test writing `.eh_frame`. let mut eh_frame = EhFrame::from(EndianVec::new(LittleEndian)); frames.write_eh_frame(&mut eh_frame).unwrap(); let mut read_eh_frame = read::EhFrame::new(eh_frame.slice(), LittleEndian); read_eh_frame.set_address_size(address_size); let convert_frames = FrameTable::from(&read_eh_frame, &|address| { Some(Address::Constant(address)) }) .unwrap(); assert_eq!(frames.cies, convert_frames.cies); assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { assert_eq!(a.1, b.1); } } } } } } #[test] fn test_frame_instruction() { let mut expression = Expression::new(); expression.op_constu(0); let cie_instructions = [ CallFrameInstruction::Cfa(X86_64::RSP, 8), CallFrameInstruction::Offset(X86_64::RA, -8), ]; let fde_instructions = [ (0, CallFrameInstruction::Cfa(X86_64::RSP, 0)), (0, CallFrameInstruction::Cfa(X86_64::RSP, -8)), (2, CallFrameInstruction::CfaRegister(X86_64::RBP)), (4, CallFrameInstruction::CfaOffset(8)), (4, CallFrameInstruction::CfaOffset(0)), (4, CallFrameInstruction::CfaOffset(-8)), (6, CallFrameInstruction::CfaExpression(expression.clone())), (8, CallFrameInstruction::Restore(Register(1))), (8, CallFrameInstruction::Restore(Register(101))), (10, CallFrameInstruction::Undefined(Register(2))), (12, CallFrameInstruction::SameValue(Register(3))), (14, CallFrameInstruction::Offset(Register(4), 16)), (14, CallFrameInstruction::Offset(Register(104), 16)), (16, CallFrameInstruction::ValOffset(Register(5), -24)), (16, CallFrameInstruction::ValOffset(Register(5), 24)), (18, CallFrameInstruction::Register(Register(6), Register(7))), ( 20, CallFrameInstruction::Expression(Register(8), expression.clone()), ), ( 22, CallFrameInstruction::ValExpression(Register(9), expression.clone()), ), (24 + 0x80, CallFrameInstruction::RememberState), (26 + 0x280, CallFrameInstruction::RestoreState), (28 + 0x20280, CallFrameInstruction::ArgsSize(23)), ]; let fde_instructions_aarch64 = [(0, CallFrameInstruction::NegateRaState)]; for &version in &[1, 3, 4] { for &address_size in &[4, 8] { for &vendor in &[Vendor::Default, Vendor::AArch64] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut frames = FrameTable::default(); let mut cie = CommonInformationEntry::new(encoding, 2, 8, X86_64::RA); for i in &cie_instructions { cie.add_instruction(i.clone()); } let cie_id = frames.add_cie(cie); let mut fde = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); for (o, i) in &fde_instructions { fde.add_instruction(*o, i.clone()); } frames.add_fde(cie_id, fde); if vendor == Vendor::AArch64 { let mut fde = FrameDescriptionEntry::new(Address::Constant(0x2000), 0x10); for (o, i) in &fde_instructions_aarch64 { fde.add_instruction(*o, i.clone()); } frames.add_fde(cie_id, fde); } let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); frames.write_debug_frame(&mut debug_frame).unwrap(); let mut read_debug_frame = read::DebugFrame::new(debug_frame.slice(), LittleEndian); read_debug_frame.set_address_size(address_size); read_debug_frame.set_vendor(vendor); let frames = FrameTable::from(&read_debug_frame, &|address| { Some(Address::Constant(address)) }) .unwrap(); assert_eq!( &frames.cies.get_index(0).unwrap().instructions, &cie_instructions ); assert_eq!(&frames.fdes[0].1.instructions, &fde_instructions); if vendor == Vendor::AArch64 { assert_eq!(&frames.fdes[1].1.instructions, &fde_instructions_aarch64); } } } } } } } gimli-0.31.1/src/write/dwarf.rs000064400000000000000000000114121046102023000144000ustar 00000000000000use alloc::vec::Vec; use crate::common::Encoding; use crate::write::{ AbbreviationTable, LineProgram, LineStringTable, Result, Sections, StringTable, Unit, UnitTable, Writer, }; /// Writable DWARF information for more than one unit. #[derive(Debug, Default)] pub struct Dwarf { /// A table of units. These are primarily stored in the `.debug_info` section, /// but they also contain information that is stored in other sections. pub units: UnitTable, /// Extra line number programs that are not associated with a unit. /// /// These should only be used when generating DWARF5 line-only debug /// information. pub line_programs: Vec, /// A table of strings that will be stored in the `.debug_line_str` section. pub line_strings: LineStringTable, /// A table of strings that will be stored in the `.debug_str` section. pub strings: StringTable, } impl Dwarf { /// Create a new `Dwarf` instance. #[inline] pub fn new() -> Self { Self::default() } /// Write the DWARF information to the given sections. pub fn write(&mut self, sections: &mut Sections) -> Result<()> { let line_strings = self.line_strings.write(&mut sections.debug_line_str)?; let strings = self.strings.write(&mut sections.debug_str)?; self.units.write(sections, &line_strings, &strings)?; for line_program in &self.line_programs { line_program.write( &mut sections.debug_line, line_program.encoding(), &line_strings, &strings, )?; } Ok(()) } } /// Writable DWARF information for a single unit. #[derive(Debug)] pub struct DwarfUnit { /// A unit. This is primarily stored in the `.debug_info` section, /// but also contains information that is stored in other sections. pub unit: Unit, /// A table of strings that will be stored in the `.debug_line_str` section. pub line_strings: LineStringTable, /// A table of strings that will be stored in the `.debug_str` section. pub strings: StringTable, } impl DwarfUnit { /// Create a new `DwarfUnit`. /// /// Note: you should set `self.unit.line_program` after creation. /// This cannot be done earlier because it may need to reference /// `self.line_strings`. pub fn new(encoding: Encoding) -> Self { let unit = Unit::new(encoding, LineProgram::none()); DwarfUnit { unit, line_strings: LineStringTable::default(), strings: StringTable::default(), } } /// Write the DWARf information to the given sections. pub fn write(&mut self, sections: &mut Sections) -> Result<()> { let line_strings = self.line_strings.write(&mut sections.debug_line_str)?; let strings = self.strings.write(&mut sections.debug_str)?; let abbrev_offset = sections.debug_abbrev.offset(); let mut abbrevs = AbbreviationTable::default(); self.unit.write( sections, abbrev_offset, &mut abbrevs, &line_strings, &strings, )?; // None should exist because we didn't give out any UnitId. assert!(sections.debug_info_refs.is_empty()); assert!(sections.debug_loc_refs.is_empty()); assert!(sections.debug_loclists_refs.is_empty()); abbrevs.write(&mut sections.debug_abbrev)?; Ok(()) } } #[cfg(feature = "read")] pub(crate) mod convert { use super::*; use crate::read::{self, Reader}; use crate::write::{Address, ConvertResult}; impl Dwarf { /// Create a `write::Dwarf` by converting a `read::Dwarf`. /// /// `convert_address` is a function to convert read addresses into the `Address` /// type. For non-relocatable addresses, this function may simply return /// `Address::Constant(address)`. For relocatable addresses, it is the caller's /// responsibility to determine the symbol and addend corresponding to the address /// and return `Address::Symbol { symbol, addend }`. pub fn from>( dwarf: &read::Dwarf, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult { let mut line_strings = LineStringTable::default(); let mut strings = StringTable::default(); let units = UnitTable::from(dwarf, &mut line_strings, &mut strings, convert_address)?; // TODO: convert the line programs that were not referenced by a unit. let line_programs = Vec::new(); Ok(Dwarf { units, line_programs, line_strings, strings, }) } } } gimli-0.31.1/src/write/endian_vec.rs000064400000000000000000000054331046102023000153760ustar 00000000000000use alloc::vec::Vec; use std::mem; use crate::endianity::Endianity; use crate::write::{Error, Result, Writer}; /// A `Vec` with endianity metadata. /// /// This implements the `Writer` trait, which is used for all writing of DWARF sections. #[derive(Debug, Clone)] pub struct EndianVec where Endian: Endianity, { vec: Vec, endian: Endian, } impl EndianVec where Endian: Endianity, { /// Construct an empty `EndianVec` with the given endianity. pub fn new(endian: Endian) -> EndianVec { EndianVec { vec: Vec::new(), endian, } } /// Return a reference to the raw slice. pub fn slice(&self) -> &[u8] { &self.vec } /// Convert into a `Vec`. pub fn into_vec(self) -> Vec { self.vec } /// Take any written data out of the `EndianVec`, leaving an empty `Vec` in its place. pub fn take(&mut self) -> Vec { let mut vec = Vec::new(); mem::swap(&mut self.vec, &mut vec); vec } } impl Writer for EndianVec where Endian: Endianity, { type Endian = Endian; #[inline] fn endian(&self) -> Self::Endian { self.endian } #[inline] fn len(&self) -> usize { self.vec.len() } fn write(&mut self, bytes: &[u8]) -> Result<()> { self.vec.extend(bytes); Ok(()) } fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { if offset > self.vec.len() { return Err(Error::OffsetOutOfBounds); } let to = &mut self.vec[offset..]; if bytes.len() > to.len() { return Err(Error::LengthOutOfBounds); } let to = &mut to[..bytes.len()]; to.copy_from_slice(bytes); Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::LittleEndian; #[test] fn test_endian_vec() { let mut w = EndianVec::new(LittleEndian); assert_eq!(w.endian(), LittleEndian); assert_eq!(w.len(), 0); w.write(&[1, 2]).unwrap(); assert_eq!(w.slice(), &[1, 2]); assert_eq!(w.len(), 2); w.write(&[3, 4, 5]).unwrap(); assert_eq!(w.slice(), &[1, 2, 3, 4, 5]); assert_eq!(w.len(), 5); w.write_at(0, &[6, 7]).unwrap(); assert_eq!(w.slice(), &[6, 7, 3, 4, 5]); assert_eq!(w.len(), 5); w.write_at(3, &[8, 9]).unwrap(); assert_eq!(w.slice(), &[6, 7, 3, 8, 9]); assert_eq!(w.len(), 5); assert_eq!(w.write_at(4, &[6, 7]), Err(Error::LengthOutOfBounds)); assert_eq!(w.write_at(5, &[6, 7]), Err(Error::LengthOutOfBounds)); assert_eq!(w.write_at(6, &[6, 7]), Err(Error::OffsetOutOfBounds)); assert_eq!(w.into_vec(), vec![6, 7, 3, 8, 9]); } } gimli-0.31.1/src/write/line.rs000064400000000000000000002260651046102023000142400ustar 00000000000000use alloc::vec::Vec; use indexmap::{IndexMap, IndexSet}; use std::ops::{Deref, DerefMut}; use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId}; use crate::constants; use crate::leb128; use crate::write::{ Address, DebugLineStrOffsets, DebugStrOffsets, Error, LineStringId, LineStringTable, Result, Section, StringId, Writer, }; /// The number assigned to the first special opcode. // // We output all instructions for all DWARF versions, since readers // should be able to ignore instructions they don't support. const OPCODE_BASE: u8 = 13; /// A line number program. #[derive(Debug, Clone)] pub struct LineProgram { /// True if this line program was created with `LineProgram::none()`. none: bool, encoding: Encoding, line_encoding: LineEncoding, /// A list of source directory path names. /// /// If a path is relative, then the directory is located relative to the working /// directory of the compilation unit. /// /// The first entry is for the working directory of the compilation unit. directories: IndexSet, /// A list of source file entries. /// /// Each entry has a path name and a directory. /// /// If a path is a relative, then the file is located relative to the /// directory. Otherwise the directory is meaningless. /// /// Does not include comp_file, even for version >= 5. files: IndexMap<(LineString, DirectoryId), FileInfo>, /// True if the file entries may have valid timestamps. /// /// Entries may still have a timestamp of 0 even if this is set. /// For version <= 4, this is ignored. /// For version 5, this controls whether to emit `DW_LNCT_timestamp`. pub file_has_timestamp: bool, /// True if the file entries may have valid sizes. /// /// Entries may still have a size of 0 even if this is set. /// For version <= 4, this is ignored. /// For version 5, this controls whether to emit `DW_LNCT_size`. pub file_has_size: bool, /// True if the file entries have valid MD5 checksums. /// /// For version <= 4, this is ignored. /// For version 5, this controls whether to emit `DW_LNCT_MD5`. pub file_has_md5: bool, /// True if the file entries have embedded source code. /// /// For version <= 4, this is ignored. /// For version 5, this controls whether to emit `DW_LNCT_LLVM_source`. pub file_has_source: bool, prev_row: LineRow, row: LineRow, // TODO: this probably should be either rows or sequences instead instructions: Vec, in_sequence: bool, } impl LineProgram { /// Create a new `LineProgram`. /// /// `comp_dir` defines the working directory of the compilation unit, /// and must be the same as the `DW_AT_comp_dir` attribute /// of the compilation unit DIE. /// /// `comp_file` and `comp_file_info` define the primary source file /// of the compilation unit and must be the same as the `DW_AT_name` /// attribute of the compilation unit DIE. /// /// # Panics /// /// Panics if `line_encoding.line_base` > 0. /// /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0. /// /// Panics if `comp_dir` is empty or contains a null byte. /// /// Panics if `comp_file` is empty or contains a null byte. pub fn new( encoding: Encoding, line_encoding: LineEncoding, comp_dir: LineString, comp_file: LineString, comp_file_info: Option, ) -> LineProgram { // We require a special opcode for a line advance of 0. // See the debug_asserts in generate_row(). assert!(line_encoding.line_base <= 0); assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0); let mut program = LineProgram { none: false, encoding, line_encoding, directories: IndexSet::new(), files: IndexMap::new(), prev_row: LineRow::initial_state(encoding, line_encoding), row: LineRow::initial_state(encoding, line_encoding), instructions: Vec::new(), in_sequence: false, file_has_timestamp: false, file_has_size: false, file_has_md5: false, file_has_source: false, }; // For all DWARF versions, directory index 0 is comp_dir. // For version <= 4, the entry is implicit. We still add // it here so that we use it, but we don't emit it. let comp_dir_id = program.add_directory(comp_dir); // For DWARF version >= 5, file index 0 is comp_file and must exist. if encoding.version >= 5 { program.add_file(comp_file, comp_dir_id, comp_file_info); } program } /// Create a new `LineProgram` with no fields set. /// /// This can be used when the `LineProgram` will not be used. /// /// You should not attempt to add files or line instructions to /// this line program, or write it to the `.debug_line` section. pub fn none() -> Self { let encoding = Encoding { format: Format::Dwarf32, version: 2, address_size: 0, }; let line_encoding = LineEncoding::default(); LineProgram { none: true, encoding, line_encoding, directories: IndexSet::new(), files: IndexMap::new(), prev_row: LineRow::initial_state(encoding, line_encoding), row: LineRow::initial_state(encoding, line_encoding), instructions: Vec::new(), in_sequence: false, file_has_timestamp: false, file_has_size: false, file_has_md5: false, file_has_source: false, } } /// Return true if this line program was created with `LineProgram::none()`. #[inline] pub fn is_none(&self) -> bool { self.none } /// Return the encoding parameters for this line program. #[inline] pub fn encoding(&self) -> Encoding { self.encoding } /// Return the DWARF version for this line program. #[inline] pub fn version(&self) -> u16 { self.encoding.version } /// Return the address size in bytes for this line program. #[inline] pub fn address_size(&self) -> u8 { self.encoding.address_size } /// Return the DWARF format for this line program. #[inline] pub fn format(&self) -> Format { self.encoding.format } /// Return the id for the working directory of the compilation unit. #[inline] pub fn default_directory(&self) -> DirectoryId { DirectoryId(0) } /// Add a directory entry and return its id. /// /// If the directory already exists, then return the id of the existing entry. /// /// If the path is relative, then the directory is located relative to the working /// directory of the compilation unit. /// /// # Panics /// /// Panics if `directory` is empty or contains a null byte. pub fn add_directory(&mut self, directory: LineString) -> DirectoryId { if let LineString::String(ref val) = directory { // For DWARF version <= 4, directories must not be empty. // The first directory isn't emitted so skip the check for it. if self.encoding.version <= 4 && !self.directories.is_empty() { assert!(!val.is_empty()); } assert!(!val.contains(&0)); } let (index, _) = self.directories.insert_full(directory); DirectoryId(index) } /// Get a reference to a directory entry. /// /// # Panics /// /// Panics if `id` is invalid. pub fn get_directory(&self, id: DirectoryId) -> &LineString { self.directories.get_index(id.0).unwrap() } /// Add a file entry and return its id. /// /// If the file already exists, then return the id of the existing entry. /// /// If the file path is relative, then the file is located relative /// to the directory. Otherwise the directory is meaningless, but it /// is still used as a key for file entries. /// /// If `info` is `None`, then new entries are assigned /// default information, and existing entries are unmodified. /// /// If `info` is not `None`, then it is always assigned to the /// entry, even if the entry already exists. /// /// # Panics /// /// Panics if 'file' is empty or contains a null byte. pub fn add_file( &mut self, file: LineString, directory: DirectoryId, info: Option, ) -> FileId { if let LineString::String(ref val) = file { if self.encoding.version <= 4 { assert!(!val.is_empty()); } assert!(!val.contains(&0)); } let key = (file, directory); let index = if let Some(info) = info { let (index, _) = self.files.insert_full(key, info); index } else { let entry = self.files.entry(key); let index = entry.index(); entry.or_default(); index }; FileId::new(index) } /// Get a reference to a file entry. /// /// # Panics /// /// Panics if `id` is invalid. pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) { self.files .get_index(id.index()) .map(|entry| (&(entry.0).0, (entry.0).1)) .unwrap() } /// Get a reference to the info for a file entry. /// /// # Panics /// /// Panics if `id` is invalid. pub fn get_file_info(&self, id: FileId) -> &FileInfo { self.files .get_index(id.index()) .map(|entry| entry.1) .unwrap() } /// Get a mutable reference to the info for a file entry. /// /// # Panics /// /// Panics if `id` is invalid. pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo { self.files .get_index_mut(id.index()) .map(|entry| entry.1) .unwrap() } /// Begin a new sequence and set its base address. /// /// # Panics /// /// Panics if a sequence has already begun. pub fn begin_sequence(&mut self, address: Option
) { assert!(!self.in_sequence); self.in_sequence = true; if let Some(address) = address { self.instructions.push(LineInstruction::SetAddress(address)); } } /// End the sequence, and reset the row to its default values. /// /// Only the `address_offset` and op_index` fields of the current row are used. /// /// # Panics /// /// Panics if a sequence has not begun. pub fn end_sequence(&mut self, address_offset: u64) { assert!(self.in_sequence); self.in_sequence = false; self.row.address_offset = address_offset; let op_advance = self.op_advance(); if op_advance != 0 { self.instructions .push(LineInstruction::AdvancePc(op_advance)); } self.instructions.push(LineInstruction::EndSequence); self.prev_row = LineRow::initial_state(self.encoding, self.line_encoding); self.row = LineRow::initial_state(self.encoding, self.line_encoding); } /// Return true if a sequence has begun. #[inline] pub fn in_sequence(&self) -> bool { self.in_sequence } /// Returns a reference to the data for the current row. #[inline] pub fn row(&mut self) -> &mut LineRow { &mut self.row } /// Generates the line number information instructions for the current row. /// /// After the instructions are generated, it sets `discriminator` to 0, and sets /// `basic_block`, `prologue_end`, and `epilogue_begin` to false. /// /// # Panics /// /// Panics if a sequence has not begun. /// Panics if the address_offset decreases. pub fn generate_row(&mut self) { assert!(self.in_sequence); // Output fields that are reset on every row. if self.row.discriminator != 0 { self.instructions .push(LineInstruction::SetDiscriminator(self.row.discriminator)); self.row.discriminator = 0; } if self.row.basic_block { self.instructions.push(LineInstruction::SetBasicBlock); self.row.basic_block = false; } if self.row.prologue_end { self.instructions.push(LineInstruction::SetPrologueEnd); self.row.prologue_end = false; } if self.row.epilogue_begin { self.instructions.push(LineInstruction::SetEpilogueBegin); self.row.epilogue_begin = false; } // Output fields that are not reset on every row. if self.row.is_statement != self.prev_row.is_statement { self.instructions.push(LineInstruction::NegateStatement); } if self.row.file != self.prev_row.file { self.instructions .push(LineInstruction::SetFile(self.row.file)); } if self.row.column != self.prev_row.column { self.instructions .push(LineInstruction::SetColumn(self.row.column)); } if self.row.isa != self.prev_row.isa { self.instructions .push(LineInstruction::SetIsa(self.row.isa)); } // Advance the line, address, and operation index. let line_base = i64::from(self.line_encoding.line_base) as u64; let line_range = u64::from(self.line_encoding.line_range); let line_advance = self.row.line as i64 - self.prev_row.line as i64; let op_advance = self.op_advance(); // Default to special advances of 0. let special_base = u64::from(OPCODE_BASE); // TODO: handle lack of special opcodes for 0 line advance debug_assert!(self.line_encoding.line_base <= 0); debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0); let special_default = special_base.wrapping_sub(line_base); let mut special = special_default; let mut use_special = false; if line_advance != 0 { let special_line = (line_advance as u64).wrapping_sub(line_base); if special_line < line_range { special = special_base + special_line; use_special = true; } else { self.instructions .push(LineInstruction::AdvanceLine(line_advance)); } } if op_advance != 0 { // Using ConstAddPc can save a byte. let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 { (op_advance, false) } else { let op_range = (255 - special_base) / line_range; (op_advance - op_range, true) }; let special_op = special_op_advance * line_range; if special + special_op <= 255 { special += special_op; use_special = true; if const_add_pc { self.instructions.push(LineInstruction::ConstAddPc); } } else { self.instructions .push(LineInstruction::AdvancePc(op_advance)); } } if use_special && special != special_default { debug_assert!(special >= special_base); debug_assert!(special <= 255); self.instructions .push(LineInstruction::Special(special as u8)); } else { self.instructions.push(LineInstruction::Copy); } self.prev_row = self.row; } fn op_advance(&self) -> u64 { debug_assert!(self.row.address_offset >= self.prev_row.address_offset); let mut address_advance = self.row.address_offset - self.prev_row.address_offset; if self.line_encoding.minimum_instruction_length != 1 { debug_assert_eq!( self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length), 0 ); address_advance /= u64::from(self.line_encoding.minimum_instruction_length); } address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction) + self.row.op_index - self.prev_row.op_index } /// Returns true if the line number program has no instructions. /// /// Does not check the file or directory entries. #[inline] pub fn is_empty(&self) -> bool { self.instructions.is_empty() } /// Write the line number program to the given section. /// /// # Panics /// /// Panics if `self.is_none()`. pub fn write( &self, w: &mut DebugLine, encoding: Encoding, debug_line_str_offsets: &DebugLineStrOffsets, debug_str_offsets: &DebugStrOffsets, ) -> Result { assert!(!self.is_none()); if encoding.version < self.version() || encoding.format != self.format() || encoding.address_size != self.address_size() { return Err(Error::IncompatibleLineProgramEncoding); } let offset = w.offset(); let length_offset = w.write_initial_length(self.format())?; let length_base = w.len(); if self.version() < 2 || self.version() > 5 { return Err(Error::UnsupportedVersion(self.version())); } w.write_u16(self.version())?; if self.version() >= 5 { w.write_u8(self.address_size())?; // Segment selector size. w.write_u8(0)?; } let header_length_offset = w.len(); w.write_udata(0, self.format().word_size())?; let header_length_base = w.len(); w.write_u8(self.line_encoding.minimum_instruction_length)?; if self.version() >= 4 { w.write_u8(self.line_encoding.maximum_operations_per_instruction)?; } else if self.line_encoding.maximum_operations_per_instruction != 1 { return Err(Error::NeedVersion(4)); }; w.write_u8(if self.line_encoding.default_is_stmt { 1 } else { 0 })?; w.write_u8(self.line_encoding.line_base as u8)?; w.write_u8(self.line_encoding.line_range)?; w.write_u8(OPCODE_BASE)?; w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?; if self.version() <= 4 { // The first directory is stored as DW_AT_comp_dir. for dir in self.directories.iter().skip(1) { dir.write( w, constants::DW_FORM_string, self.encoding, debug_line_str_offsets, debug_str_offsets, )?; } w.write_u8(0)?; for ((file, dir), info) in self.files.iter() { file.write( w, constants::DW_FORM_string, self.encoding, debug_line_str_offsets, debug_str_offsets, )?; w.write_uleb128(dir.0 as u64)?; w.write_uleb128(info.timestamp)?; w.write_uleb128(info.size)?; } w.write_u8(0)?; } else { // Directory entry formats (only ever 1). w.write_u8(1)?; w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; let dir_form = self.directories.get_index(0).unwrap().form(); w.write_uleb128(dir_form.0.into())?; // Directory entries. w.write_uleb128(self.directories.len() as u64)?; for dir in self.directories.iter() { dir.write( w, dir_form, self.encoding, debug_line_str_offsets, debug_str_offsets, )?; } // File name entry formats. let count = 2 + if self.file_has_timestamp { 1 } else { 0 } + if self.file_has_size { 1 } else { 0 } + if self.file_has_md5 { 1 } else { 0 } + if self.file_has_source { 1 } else { 0 }; w.write_u8(count)?; w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; let file_form = (self.files.get_index(0).unwrap().0).0.form(); w.write_uleb128(file_form.0.into())?; w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?; w.write_uleb128(constants::DW_FORM_udata.0.into())?; if self.file_has_timestamp { w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?; w.write_uleb128(constants::DW_FORM_udata.0.into())?; } if self.file_has_size { w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?; w.write_uleb128(constants::DW_FORM_udata.0.into())?; } if self.file_has_md5 { w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?; w.write_uleb128(constants::DW_FORM_data16.0.into())?; } if self.file_has_source { w.write_uleb128(u64::from(constants::DW_LNCT_LLVM_source.0))?; w.write_uleb128(constants::DW_FORM_string.0.into())?; } // File name entries. w.write_uleb128(self.files.len() as u64)?; let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| { file.write( w, file_form, self.encoding, debug_line_str_offsets, debug_str_offsets, )?; w.write_uleb128(dir.0 as u64)?; if self.file_has_timestamp { w.write_uleb128(info.timestamp)?; } if self.file_has_size { w.write_uleb128(info.size)?; } if self.file_has_md5 { w.write(&info.md5)?; } if self.file_has_source { // Note: An empty DW_LNCT_LLVM_source is interpreted as missing // source code. Included source code should always be // terminated by a "\n" line ending. let empty_str = LineString::String(Vec::new()); let source = info.source.as_ref().unwrap_or(&empty_str); source.write( w, constants::DW_FORM_string, self.encoding, debug_line_str_offsets, debug_str_offsets, )?; } Ok(()) }; for ((file, dir), info) in self.files.iter() { write_file(file, *dir, info)?; } } let header_length = (w.len() - header_length_base) as u64; w.write_udata_at( header_length_offset, header_length, self.format().word_size(), )?; for instruction in &self.instructions { instruction.write(w, self.encoding)?; } let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, self.format())?; Ok(offset) } } /// A row in the line number table that corresponds to a machine instruction. #[derive(Debug, Clone, Copy)] pub struct LineRow { /// The offset of the instruction from the start address of the sequence. pub address_offset: u64, /// The index of an operation within a VLIW instruction. /// /// The index of the first operation is 0. /// Set to 0 for non-VLIW instructions. pub op_index: u64, /// The source file corresponding to the instruction. pub file: FileId, /// The line number within the source file. /// /// Lines are numbered beginning at 1. Set to 0 if there is no source line. pub line: u64, /// The column number within the source line. /// /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line. pub column: u64, /// An additional discriminator used to distinguish between source locations. /// This value is assigned arbitrarily by the DWARF producer. pub discriminator: u64, /// Set to true if the instruction is a recommended breakpoint for a statement. pub is_statement: bool, /// Set to true if the instruction is the beginning of a basic block. pub basic_block: bool, /// Set to true if the instruction is a recommended breakpoint at the entry of a /// function. pub prologue_end: bool, /// Set to true if the instruction is a recommended breakpoint prior to the exit of /// a function. pub epilogue_begin: bool, /// The instruction set architecture of the instruction. /// /// Set to 0 for the default ISA. Other values are defined by the architecture ABI. pub isa: u64, } impl LineRow { /// Return the initial state as specified in the DWARF standard. fn initial_state(encoding: Encoding, line_encoding: LineEncoding) -> Self { LineRow { address_offset: 0, op_index: 0, file: FileId::initial_state(encoding.version), line: 1, column: 0, discriminator: 0, is_statement: line_encoding.default_is_stmt, basic_block: false, prologue_end: false, epilogue_begin: false, isa: 0, } } } /// An instruction in a line number program. #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum LineInstruction { // Special opcodes Special(u8), // Standard opcodes Copy, AdvancePc(u64), AdvanceLine(i64), SetFile(FileId), SetColumn(u64), NegateStatement, SetBasicBlock, ConstAddPc, // DW_LNS_fixed_advance_pc is not supported. SetPrologueEnd, SetEpilogueBegin, SetIsa(u64), // Extended opcodes EndSequence, // TODO: this doubles the size of this enum. SetAddress(Address), // DW_LNE_define_file is not supported. SetDiscriminator(u64), } impl LineInstruction { /// Write the line number instruction to the given section. fn write(self, w: &mut DebugLine, encoding: Encoding) -> Result<()> { use self::LineInstruction::*; match self { Special(val) => w.write_u8(val)?, Copy => w.write_u8(constants::DW_LNS_copy.0)?, AdvancePc(val) => { w.write_u8(constants::DW_LNS_advance_pc.0)?; w.write_uleb128(val)?; } AdvanceLine(val) => { w.write_u8(constants::DW_LNS_advance_line.0)?; w.write_sleb128(val)?; } SetFile(val) => { w.write_u8(constants::DW_LNS_set_file.0)?; w.write_uleb128(val.raw(encoding.version))?; } SetColumn(val) => { w.write_u8(constants::DW_LNS_set_column.0)?; w.write_uleb128(val)?; } NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?, SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?, ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?, SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?, SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?, SetIsa(val) => { w.write_u8(constants::DW_LNS_set_isa.0)?; w.write_uleb128(val)?; } EndSequence => { w.write_u8(0)?; w.write_uleb128(1)?; w.write_u8(constants::DW_LNE_end_sequence.0)?; } SetAddress(address) => { w.write_u8(0)?; w.write_uleb128(1 + u64::from(encoding.address_size))?; w.write_u8(constants::DW_LNE_set_address.0)?; w.write_address(address, encoding.address_size)?; } SetDiscriminator(val) => { let mut bytes = [0u8; 10]; // bytes is long enough so this will never fail. let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); w.write_u8(0)?; w.write_uleb128(1 + len as u64)?; w.write_u8(constants::DW_LNE_set_discriminator.0)?; w.write(&bytes[..len])?; } } Ok(()) } } /// A string value for use in defining paths in line number programs. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum LineString { /// A slice of bytes representing a string. Must not include null bytes. /// Not guaranteed to be UTF-8 or anything like that. String(Vec), /// A reference to a string in the `.debug_str` section. StringRef(StringId), /// A reference to a string in the `.debug_line_str` section. LineStringRef(LineStringId), } impl LineString { /// Create a `LineString` using the normal form for the given encoding. pub fn new(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self where T: Into>, { let val = val.into(); if encoding.version <= 4 { LineString::String(val) } else { LineString::LineStringRef(line_strings.add(val)) } } fn form(&self) -> constants::DwForm { match *self { LineString::String(..) => constants::DW_FORM_string, LineString::StringRef(..) => constants::DW_FORM_strp, LineString::LineStringRef(..) => constants::DW_FORM_line_strp, } } fn write( &self, w: &mut DebugLine, form: constants::DwForm, encoding: Encoding, debug_line_str_offsets: &DebugLineStrOffsets, debug_str_offsets: &DebugStrOffsets, ) -> Result<()> { if form != self.form() { return Err(Error::LineStringFormMismatch); } match *self { LineString::String(ref val) => { if encoding.version <= 4 { debug_assert!(!val.is_empty()); } w.write(val)?; w.write_u8(0)?; } LineString::StringRef(val) => { if encoding.version < 5 { return Err(Error::NeedVersion(5)); } w.write_offset( debug_str_offsets.get(val).0, SectionId::DebugStr, encoding.format.word_size(), )?; } LineString::LineStringRef(val) => { if encoding.version < 5 { return Err(Error::NeedVersion(5)); } w.write_offset( debug_line_str_offsets.get(val).0, SectionId::DebugLineStr, encoding.format.word_size(), )?; } } Ok(()) } } /// An identifier for a directory in a `LineProgram`. /// /// Defaults to the working directory of the compilation unit. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct DirectoryId(usize); // Force FileId access via the methods. mod id { /// An identifier for a file in a `LineProgram`. // // We internally use a 0-based index for all versions, but // emit a 1-based index for DWARF version <= 4. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct FileId(usize); impl FileId { /// Create a `FileId` given a 0-based index into `LineProgram::files`. pub(crate) fn new(index: usize) -> Self { FileId(index) } /// The 0-based index of the file in `LineProgram::files`. pub(super) fn index(self) -> usize { self.0 } /// The initial state of the file register. pub(super) fn initial_state(version: u16) -> Self { if version == 5 { // For version 5, the files are 0-based and the default file is 1, // which is a 0-based index of 1. FileId(1) } else { // For version <= 4, the files are 1-based and the default file is 1, // which is a 0-based index of 0. // For version >= 6, the files are 0-based and the default file is 0, // which is a 0-based index of 0. FileId(0) } } /// Convert to a raw value used for writing. /// /// This converts to a 1-based index for DWARF version <= 4. pub(crate) fn raw(self, version: u16) -> u64 { if version <= 4 { self.0 as u64 + 1 } else { self.0 as u64 } } } } pub use self::id::*; /// Extra information for file in a `LineProgram`. #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct FileInfo { /// The implementation defined timestamp of the last modification of the file, /// or 0 if not available. pub timestamp: u64, /// The size of the file in bytes, or 0 if not available. pub size: u64, /// A 16-byte MD5 digest of the file contents. /// /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`. pub md5: [u8; 16], /// Optionally some embedded sourcecode. /// /// Only used if version >= 5 and `LineProgram::file_has_source` is `true`. /// /// NOTE: This currently only supports the `LineString::String` variant, /// since we're encoding the string with `DW_FORM_string`. /// Other variants will result in an `LineStringFormMismatch` error. pub source: Option, } define_section!( DebugLine, DebugLineOffset, "A writable `.debug_line` section." ); #[cfg(feature = "read")] mod convert { use super::*; use crate::read::{self, Reader}; use crate::write::{self, ConvertError, ConvertResult}; impl LineProgram { /// Create a line number program by reading the data from the given program. /// /// Return the program and a mapping from file index to `FileId`. pub fn from>( mut from_program: read::IncompleteLineProgram, dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult<(LineProgram, Vec)> { // Create mappings in case the source has duplicate files or directories. let mut dirs = Vec::new(); let mut files = Vec::new(); let mut program = { let from_header = from_program.header(); let encoding = from_header.encoding(); let comp_dir = match from_header.directory(0) { Some(comp_dir) => LineString::from(comp_dir, dwarf, line_strings, strings)?, None => LineString::new(&[][..], encoding, line_strings), }; let comp_name = match from_header.file(0) { Some(comp_file) => { if comp_file.directory_index() != 0 { return Err(ConvertError::InvalidDirectoryIndex); } LineString::from(comp_file.path_name(), dwarf, line_strings, strings)? } None => LineString::new(&[][..], encoding, line_strings), }; if from_header.line_base() > 0 { return Err(ConvertError::InvalidLineBase); } let mut program = LineProgram::new( encoding, from_header.line_encoding(), comp_dir, comp_name, None, // We'll set this later if needed when we add the file again. ); if from_header.version() <= 4 { // The first directory is implicit. dirs.push(DirectoryId(0)); // A file index of 0 is invalid for version <= 4, but putting // something there makes the indexing easier. files.push(FileId::new(0)); } for from_dir in from_header.include_directories() { let from_dir = LineString::from(from_dir.clone(), dwarf, line_strings, strings)?; dirs.push(program.add_directory(from_dir)); } program.file_has_timestamp = from_header.file_has_timestamp(); program.file_has_size = from_header.file_has_size(); program.file_has_md5 = from_header.file_has_md5(); program.file_has_source = from_header.file_has_source(); for from_file in from_header.file_names().iter() { let from_name = LineString::from(from_file.path_name(), dwarf, line_strings, strings)?; let from_dir = from_file.directory_index(); if from_dir >= dirs.len() as u64 { return Err(ConvertError::InvalidDirectoryIndex); } let from_dir = dirs[from_dir as usize]; let from_info = Some(FileInfo { timestamp: from_file.timestamp(), size: from_file.size(), md5: *from_file.md5(), source: match from_file.source() { Some(source) => { Some(LineString::from(source, dwarf, line_strings, strings)?) } None => None, }, }); files.push(program.add_file(from_name, from_dir, from_info)); } program }; // We can't use the `from_program.rows()` because that wouldn't let // us preserve address relocations. let mut from_row = read::LineRow::new(from_program.header()); let mut instructions = from_program.header().instructions(); let mut address = None; while let Some(instruction) = instructions.next_instruction(from_program.header())? { match instruction { read::LineInstruction::SetAddress(val) => { if program.in_sequence() { return Err(ConvertError::UnsupportedLineInstruction); } match convert_address(val) { Some(val) => address = Some(val), None => return Err(ConvertError::InvalidAddress), } from_row .execute(read::LineInstruction::SetAddress(0), &mut from_program)?; } read::LineInstruction::DefineFile(_) => { return Err(ConvertError::UnsupportedLineInstruction); } _ => { if from_row.execute(instruction, &mut from_program)? { if !program.in_sequence() { program.begin_sequence(address); address = None; } if from_row.end_sequence() { program.end_sequence(from_row.address()); } else { program.row().address_offset = from_row.address(); program.row().op_index = from_row.op_index(); program.row().file = { let file = from_row.file_index(); if file >= files.len() as u64 { return Err(ConvertError::InvalidFileIndex); } if file == 0 && program.version() <= 4 { return Err(ConvertError::InvalidFileIndex); } files[file as usize] }; program.row().line = match from_row.line() { Some(line) => line.get(), None => 0, }; program.row().column = match from_row.column() { read::ColumnType::LeftEdge => 0, read::ColumnType::Column(val) => val.get(), }; program.row().discriminator = from_row.discriminator(); program.row().is_statement = from_row.is_stmt(); program.row().basic_block = from_row.basic_block(); program.row().prologue_end = from_row.prologue_end(); program.row().epilogue_begin = from_row.epilogue_begin(); program.row().isa = from_row.isa(); program.generate_row(); } from_row.reset(from_program.header()); } } }; } Ok((program, files)) } } impl LineString { fn from>( from_attr: read::AttributeValue, dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, ) -> ConvertResult { Ok(match from_attr { read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()), read::AttributeValue::DebugStrRef(offset) => { let r = dwarf.debug_str.get_str(offset)?; let id = strings.add(r.to_slice()?); LineString::StringRef(id) } read::AttributeValue::DebugLineStrRef(offset) => { let r = dwarf.debug_line_str.get_str(offset)?; let id = line_strings.add(r.to_slice()?); LineString::LineStringRef(id) } _ => return Err(ConvertError::UnsupportedLineStringForm), }) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::read; use crate::write::{AttributeValue, Dwarf, EndianVec, Sections, Unit}; use crate::LittleEndian; #[test] fn test_line_program() { let dir1 = LineString::String(b"dir1".to_vec()); let file1 = LineString::String(b"file1".to_vec()); let dir2 = LineString::String(b"dir2".to_vec()); let file2 = LineString::String(b"file2".to_vec()); let mut dwarf = Dwarf::new(); for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut program = LineProgram::new( encoding, LineEncoding::default(), dir1.clone(), file1.clone(), None, ); assert_eq!(&dir1, program.get_directory(program.default_directory())); program.file_has_timestamp = true; program.file_has_size = true; if encoding.version >= 5 { program.file_has_md5 = true; } // Note: Embedded source code is an accepted extension // that will become part of DWARF v6. We're using the LLVM extension // here for v5. if encoding.version >= 5 { program.file_has_source = true; } let dir_id = program.add_directory(dir2.clone()); assert_eq!(&dir2, program.get_directory(dir_id)); assert_eq!(dir_id, program.add_directory(dir2.clone())); let file_info = FileInfo { timestamp: 1, size: 2, md5: if encoding.version >= 5 { [3; 16] } else { [0; 16] }, source: (encoding.version >= 5) .then(|| LineString::String(b"the source code\n".to_vec())), }; let file_id = program.add_file(file2.clone(), dir_id, Some(file_info.clone())); assert_eq!((&file2, dir_id), program.get_file(file_id)); assert_eq!(file_info, *program.get_file_info(file_id)); program.get_file_info_mut(file_id).size = 3; assert_ne!(file_info, *program.get_file_info(file_id)); assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None)); assert_ne!(file_info, *program.get_file_info(file_id)); assert_eq!( file_id, program.add_file(file2.clone(), dir_id, Some(file_info.clone())) ); assert_eq!(file_info, *program.get_file_info(file_id)); let mut unit = Unit::new(encoding, program); let root = unit.get_mut(unit.root()); root.set( constants::DW_AT_comp_dir, AttributeValue::String(b"dir1".to_vec()), ); root.set( constants::DW_AT_name, AttributeValue::String(b"file1".to_vec()), ); root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); root.set( constants::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id)), ); let mut dwarf = Dwarf::new(); dwarf.units.add(unit); } } } let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let convert_dwarf = Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap(); let mut convert_units = convert_dwarf.units.iter(); for (_, unit) in dwarf.units.iter() { let program = &unit.line_program; let root = unit.get(unit.root()); let Some(AttributeValue::FileIndex(Some(file_id))) = root.get(constants::DW_AT_decl_file) else { panic!("missing DW_AT_decl_file"); }; let (_, convert_unit) = convert_units.next().unwrap(); let convert_program = &convert_unit.line_program; let convert_root = convert_unit.get(convert_unit.root()); let Some(AttributeValue::FileIndex(Some(convert_file_id))) = convert_root.get(constants::DW_AT_decl_file) else { panic!("missing DW_AT_decl_file"); }; assert_eq!(convert_program.version(), program.version()); assert_eq!(convert_program.address_size(), program.address_size()); assert_eq!(convert_program.format(), program.format()); let (file, dir) = program.get_file(*file_id); let (convert_file, convert_dir) = convert_program.get_file(*convert_file_id); assert_eq!(file, convert_file); assert_eq!( program.get_directory(dir), convert_program.get_directory(convert_dir) ); assert_eq!( program.get_file_info(*file_id), convert_program.get_file_info(*convert_file_id) ); } } #[test] fn test_line_row() { let dir1 = &b"dir1"[..]; let file1 = &b"file1"[..]; let file2 = &b"file2"[..]; for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let line_base = -5; let line_range = 14; let neg_line_base = (-line_base) as u8; let mut program = LineProgram::new( encoding, LineEncoding { line_base, line_range, ..Default::default() }, LineString::String(dir1.to_vec()), LineString::String(file1.to_vec()), None, ); let dir_id = program.default_directory(); let file1_id = program.add_file(LineString::String(file1.to_vec()), dir_id, None); let file2_id = program.add_file(LineString::String(file2.to_vec()), dir_id, None); // Test sequences. { let mut program = program.clone(); let address = Address::Constant(0x12); program.begin_sequence(Some(address)); assert_eq!( program.instructions, vec![LineInstruction::SetAddress(address)] ); } { let mut program = program.clone(); program.begin_sequence(None); assert_eq!(program.instructions, Vec::new()); } { let mut program = program.clone(); program.begin_sequence(None); program.end_sequence(0x1234); assert_eq!( program.instructions, vec![ LineInstruction::AdvancePc(0x1234), LineInstruction::EndSequence ] ); } // Create a base program. program.begin_sequence(None); program.row.line = 0x1000; program.generate_row(); let base_row = program.row; let base_instructions = program.instructions.clone(); // Create test cases. let mut tests = Vec::new(); let row = base_row; tests.push((row, vec![LineInstruction::Copy])); let mut row = base_row; row.line -= u64::from(neg_line_base); tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)])); let mut row = base_row; row.line += u64::from(line_range) - 1; row.line -= u64::from(neg_line_base); tests.push(( row, vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)], )); let mut row = base_row; row.line += u64::from(line_range); row.line -= u64::from(neg_line_base); tests.push(( row, vec![ LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)), LineInstruction::Copy, ], )); let mut row = base_row; row.address_offset = 1; row.line -= u64::from(neg_line_base); tests.push(( row, vec![LineInstruction::Special(OPCODE_BASE + line_range)], )); let op_range = (255 - OPCODE_BASE) / line_range; let mut row = base_row; row.address_offset = u64::from(op_range); row.line -= u64::from(neg_line_base); tests.push(( row, vec![LineInstruction::Special( OPCODE_BASE + op_range * line_range, )], )); let mut row = base_row; row.address_offset = u64::from(op_range); row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); row.line -= u64::from(neg_line_base); tests.push((row, vec![LineInstruction::Special(255)])); let mut row = base_row; row.address_offset = u64::from(op_range); row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; row.line -= u64::from(neg_line_base); tests.push(( row, vec![LineInstruction::ConstAddPc, LineInstruction::Copy], )); let mut row = base_row; row.address_offset = u64::from(op_range); row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; row.line -= u64::from(neg_line_base); tests.push(( row, vec![ LineInstruction::ConstAddPc, LineInstruction::Special(OPCODE_BASE + 6), ], )); let mut row = base_row; row.address_offset = u64::from(op_range) * 2; row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); row.line -= u64::from(neg_line_base); tests.push(( row, vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)], )); let mut row = base_row; row.address_offset = u64::from(op_range) * 2; row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; row.line -= u64::from(neg_line_base); tests.push(( row, vec![ LineInstruction::AdvancePc(row.address_offset), LineInstruction::Copy, ], )); let mut row = base_row; row.address_offset = u64::from(op_range) * 2; row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; row.line -= u64::from(neg_line_base); tests.push(( row, vec![ LineInstruction::AdvancePc(row.address_offset), LineInstruction::Special(OPCODE_BASE + 6), ], )); let mut row = base_row; row.address_offset = 0x1234; tests.push(( row, vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy], )); let mut row = base_row; row.line += 0x1234; tests.push(( row, vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy], )); let mut row = base_row; row.file = file1_id; if version == 5 { // Version 5 is 0-based, but the default file is 1, so this row // will need to set the file. tests.push(( row, vec![LineInstruction::SetFile(file1_id), LineInstruction::Copy], )); } else { // This is the first file, so normally this is already the default. tests.push((row, vec![LineInstruction::Copy])); } let mut row = base_row; row.file = file2_id; if version == 5 { tests.push((row, vec![LineInstruction::Copy])); } else { tests.push(( row, vec![LineInstruction::SetFile(file2_id), LineInstruction::Copy], )); } let mut row = base_row; row.column = 0x1234; tests.push(( row, vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy], )); let mut row = base_row; row.discriminator = 0x1234; tests.push(( row, vec![ LineInstruction::SetDiscriminator(0x1234), LineInstruction::Copy, ], )); let mut row = base_row; row.is_statement = !row.is_statement; tests.push(( row, vec![LineInstruction::NegateStatement, LineInstruction::Copy], )); let mut row = base_row; row.basic_block = true; tests.push(( row, vec![LineInstruction::SetBasicBlock, LineInstruction::Copy], )); let mut row = base_row; row.prologue_end = true; tests.push(( row, vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy], )); let mut row = base_row; row.epilogue_begin = true; tests.push(( row, vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy], )); let mut row = base_row; row.isa = 0x1234; tests.push(( row, vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy], )); for test in tests { // Test generate_row(). let mut program = program.clone(); program.row = test.0; program.generate_row(); assert_eq!( &program.instructions[base_instructions.len()..], &test.1[..] ); // Test LineProgram::from(). let mut unit = Unit::new(encoding, program); let root = unit.get_mut(unit.root()); root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); let mut dwarf = Dwarf::new(); dwarf.units.add(unit); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let convert_dwarf = Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) .unwrap(); let convert_unit = convert_dwarf.units.iter().next().unwrap().1; let convert_program = &convert_unit.line_program; assert_eq!( &convert_program.instructions[base_instructions.len()..], &test.1[..] ); } } } } } #[test] fn test_line_instruction() { let dir1 = &b"dir1"[..]; let file1 = &b"file1"[..]; for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut program = LineProgram::new( encoding, LineEncoding::default(), LineString::String(dir1.to_vec()), LineString::String(file1.to_vec()), None, ); let dir_id = program.default_directory(); let file_id = program.add_file(LineString::String(file1.to_vec()), dir_id, None); for (inst, expect_inst) in &[ ( LineInstruction::Special(OPCODE_BASE), read::LineInstruction::Special(OPCODE_BASE), ), ( LineInstruction::Special(255), read::LineInstruction::Special(255), ), (LineInstruction::Copy, read::LineInstruction::Copy), ( LineInstruction::AdvancePc(0x12), read::LineInstruction::AdvancePc(0x12), ), ( LineInstruction::AdvanceLine(0x12), read::LineInstruction::AdvanceLine(0x12), ), ( LineInstruction::SetFile(file_id), read::LineInstruction::SetFile(file_id.raw(encoding.version)), ), ( LineInstruction::SetColumn(0x12), read::LineInstruction::SetColumn(0x12), ), ( LineInstruction::NegateStatement, read::LineInstruction::NegateStatement, ), ( LineInstruction::SetBasicBlock, read::LineInstruction::SetBasicBlock, ), ( LineInstruction::ConstAddPc, read::LineInstruction::ConstAddPc, ), ( LineInstruction::SetPrologueEnd, read::LineInstruction::SetPrologueEnd, ), ( LineInstruction::SetEpilogueBegin, read::LineInstruction::SetEpilogueBegin, ), ( LineInstruction::SetIsa(0x12), read::LineInstruction::SetIsa(0x12), ), ( LineInstruction::EndSequence, read::LineInstruction::EndSequence, ), ( LineInstruction::SetAddress(Address::Constant(0x12)), read::LineInstruction::SetAddress(0x12), ), ( LineInstruction::SetDiscriminator(0x12), read::LineInstruction::SetDiscriminator(0x12), ), ][..] { let mut program = program.clone(); program.instructions.push(*inst); let mut unit = Unit::new(encoding, program); let root = unit.get_mut(unit.root()); root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); let mut dwarf = Dwarf::new(); dwarf.units.add(unit); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let read_unit_header = read_dwarf.units().next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit_header).unwrap(); let read_unit = read_unit.unit_ref(&read_dwarf); let read_header = read_unit.line_program.as_ref().unwrap().header(); let mut read_insts = read_header.instructions(); assert_eq!( *expect_inst, read_insts.next_instruction(read_header).unwrap().unwrap() ); assert_eq!(None, read_insts.next_instruction(read_header).unwrap()); } } } } } // Test that the address/line advance is correct. We don't test for optimality. #[test] fn test_advance() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 8, }; let dir1 = &b"dir1"[..]; let file1 = &b"file1"[..]; let addresses = 0..50; let lines = -10..25i64; for minimum_instruction_length in [1, 4] { for maximum_operations_per_instruction in [1, 3] { for line_base in [-5, 0] { for line_range in [10, 20] { let line_encoding = LineEncoding { minimum_instruction_length, maximum_operations_per_instruction, line_base, line_range, default_is_stmt: true, }; let mut program = LineProgram::new( encoding, line_encoding, LineString::String(dir1.to_vec()), LineString::String(file1.to_vec()), None, ); for address_advance in addresses.clone() { program.begin_sequence(Some(Address::Constant(0x1000))); program.row().line = 0x10000; program.generate_row(); for line_advance in lines.clone() { { let row = program.row(); row.address_offset += address_advance * u64::from(minimum_instruction_length); row.line = row.line.wrapping_add(line_advance as u64); } program.generate_row(); } let address_offset = program.row().address_offset + u64::from(minimum_instruction_length); program.end_sequence(address_offset); } let mut unit = Unit::new(encoding, program); let root = unit.get_mut(unit.root()); root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); let mut dwarf = Dwarf::new(); dwarf.units.add(unit); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let read_unit_header = read_dwarf.units().next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit_header).unwrap(); let read_unit = read_unit.unit_ref(&read_dwarf); let read_program = read_unit.line_program.clone().unwrap(); let mut rows = read_program.rows(); for address_advance in addresses.clone() { let mut address; let mut line; { let row = rows.next_row().unwrap().unwrap().1; address = row.address(); line = row.line().unwrap().get(); } assert_eq!(address, 0x1000); assert_eq!(line, 0x10000); for line_advance in lines.clone() { let row = rows.next_row().unwrap().unwrap().1; assert_eq!( row.address() - address, address_advance * u64::from(minimum_instruction_length) ); assert_eq!( (row.line().unwrap().get() as i64) - (line as i64), line_advance ); address = row.address(); line = row.line().unwrap().get(); } let row = rows.next_row().unwrap().unwrap().1; assert!(row.end_sequence()); } } } } } } #[test] fn test_line_string() { let version = 5; let file1 = "file1"; for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let files: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [ &mut |_dwarf| LineString::String(file1.as_bytes().to_vec()), &mut |dwarf| LineString::StringRef(dwarf.strings.add(file1)), &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(file1)), ]; for file in files { let mut dwarf = Dwarf::new(); let file = file(&mut dwarf); let mut program = LineProgram::new( encoding, LineEncoding::default(), LineString::String(b"dir".to_vec()), file.clone(), None, ); program.begin_sequence(Some(Address::Constant(0x1000))); program.row().line = 0x10000; program.generate_row(); let mut unit = Unit::new(encoding, program); let root = unit.get_mut(unit.root()); root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); dwarf.units.add(unit); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let read_unit_header = read_dwarf.units().next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit_header).unwrap(); let read_unit = read_unit.unit_ref(&read_dwarf); let read_program = read_unit.line_program.clone().unwrap(); let read_header = read_program.header(); let read_file = read_header.file(0).unwrap(); let read_path = read_unit.attr_string(read_file.path_name()).unwrap(); assert_eq!(read_path.slice(), file1.as_bytes()); } } } } #[test] fn test_missing_comp_dir() { for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut program = LineProgram::new( encoding, LineEncoding::default(), LineString::String(Vec::new()), LineString::String(Vec::new()), None, ); // Ensure the program is not empty. let dir_id = program.default_directory(); let file_id = program.add_file(LineString::String(b"file1".to_vec()), dir_id, None); program.begin_sequence(Some(Address::Constant(0x1000))); program.row().file = file_id; program.row().line = 0x10000; program.generate_row(); let mut unit = Unit::new(encoding, program); let root = unit.get_mut(unit.root()); // Testing missing DW_AT_comp_dir/DW_AT_name. root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); let mut dwarf = Dwarf::new(); dwarf.units.add(unit); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let _convert_dwarf = Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) .unwrap(); } } } } } gimli-0.31.1/src/write/loc.rs000064400000000000000000000534251046102023000140640ustar 00000000000000use alloc::vec::Vec; use indexmap::IndexSet; use std::ops::{Deref, DerefMut}; use crate::common::{Encoding, LocationListsOffset, SectionId}; use crate::write::{ Address, BaseId, DebugInfoReference, Error, Expression, Result, Section, Sections, UnitOffsets, Writer, }; define_section!( DebugLoc, LocationListsOffset, "A writable `.debug_loc` section." ); define_section!( DebugLocLists, LocationListsOffset, "A writable `.debug_loclists` section." ); define_offsets!( LocationListOffsets: LocationListId => LocationListsOffset, "The section offsets of a series of location lists within the `.debug_loc` or `.debug_loclists` sections." ); define_id!( LocationListId, "An identifier for a location list in a `LocationListTable`." ); /// A table of location lists that will be stored in a `.debug_loc` or `.debug_loclists` section. #[derive(Debug, Default)] pub struct LocationListTable { base_id: BaseId, locations: IndexSet, } impl LocationListTable { /// Add a location list to the table. pub fn add(&mut self, loc_list: LocationList) -> LocationListId { let (index, _) = self.locations.insert_full(loc_list); LocationListId::new(self.base_id, index) } /// Get a reference to a location list. /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pub fn get(&self, id: LocationListId) -> &LocationList { debug_assert_eq!(self.base_id, id.base_id); &self.locations[id.index] } /// Write the location list table to the appropriate section for the given DWARF version. pub(crate) fn write( &self, sections: &mut Sections, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result { if self.locations.is_empty() { return Ok(LocationListOffsets::none()); } match encoding.version { 2..=4 => self.write_loc( &mut sections.debug_loc, &mut sections.debug_loc_refs, encoding, unit_offsets, ), 5 => self.write_loclists( &mut sections.debug_loclists, &mut sections.debug_loclists_refs, encoding, unit_offsets, ), _ => Err(Error::UnsupportedVersion(encoding.version)), } } /// Write the location list table to the `.debug_loc` section. fn write_loc( &self, w: &mut DebugLoc, refs: &mut Vec, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result { let address_size = encoding.address_size; let mut offsets = Vec::new(); for loc_list in self.locations.iter() { offsets.push(w.offset()); for loc in &loc_list.0 { // Note that we must ensure none of the ranges have both begin == 0 and end == 0. // We do this by ensuring that begin != end, which is a bit more restrictive // than required, but still seems reasonable. match *loc { Location::BaseAddress { address } => { let marker = !0 >> (64 - address_size * 8); w.write_udata(marker, address_size)?; w.write_address(address, address_size)?; } Location::OffsetPair { begin, end, ref data, } => { if begin == end { return Err(Error::InvalidRange); } w.write_udata(begin, address_size)?; w.write_udata(end, address_size)?; write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; } Location::StartEnd { begin, end, ref data, } => { if begin == end { return Err(Error::InvalidRange); } w.write_address(begin, address_size)?; w.write_address(end, address_size)?; write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; } Location::StartLength { begin, length, ref data, } => { let end = match begin { Address::Constant(begin) => Address::Constant(begin + length), Address::Symbol { symbol, addend } => Address::Symbol { symbol, addend: addend + length as i64, }, }; if begin == end { return Err(Error::InvalidRange); } w.write_address(begin, address_size)?; w.write_address(end, address_size)?; write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; } Location::DefaultLocation { .. } => { return Err(Error::InvalidRange); } } } w.write_udata(0, address_size)?; w.write_udata(0, address_size)?; } Ok(LocationListOffsets { base_id: self.base_id, offsets, }) } /// Write the location list table to the `.debug_loclists` section. fn write_loclists( &self, w: &mut DebugLocLists, refs: &mut Vec, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result { let mut offsets = Vec::new(); if encoding.version != 5 { return Err(Error::NeedVersion(5)); } let length_offset = w.write_initial_length(encoding.format)?; let length_base = w.len(); w.write_u16(encoding.version)?; w.write_u8(encoding.address_size)?; w.write_u8(0)?; // segment_selector_size w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list for loc_list in self.locations.iter() { offsets.push(w.offset()); for loc in &loc_list.0 { match *loc { Location::BaseAddress { address } => { w.write_u8(crate::constants::DW_LLE_base_address.0)?; w.write_address(address, encoding.address_size)?; } Location::OffsetPair { begin, end, ref data, } => { w.write_u8(crate::constants::DW_LLE_offset_pair.0)?; w.write_uleb128(begin)?; w.write_uleb128(end)?; write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; } Location::StartEnd { begin, end, ref data, } => { w.write_u8(crate::constants::DW_LLE_start_end.0)?; w.write_address(begin, encoding.address_size)?; w.write_address(end, encoding.address_size)?; write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; } Location::StartLength { begin, length, ref data, } => { w.write_u8(crate::constants::DW_LLE_start_length.0)?; w.write_address(begin, encoding.address_size)?; w.write_uleb128(length)?; write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; } Location::DefaultLocation { ref data } => { w.write_u8(crate::constants::DW_LLE_default_location.0)?; write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; } } } w.write_u8(crate::constants::DW_LLE_end_of_list.0)?; } let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, encoding.format)?; Ok(LocationListOffsets { base_id: self.base_id, offsets, }) } } /// A locations list that will be stored in a `.debug_loc` or `.debug_loclists` section. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct LocationList(pub Vec); /// A single location. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum Location { /// DW_LLE_base_address BaseAddress { /// Base address. address: Address, }, /// DW_LLE_offset_pair OffsetPair { /// Start of range relative to base address. begin: u64, /// End of range relative to base address. end: u64, /// Location description. data: Expression, }, /// DW_LLE_start_end StartEnd { /// Start of range. begin: Address, /// End of range. end: Address, /// Location description. data: Expression, }, /// DW_LLE_start_length StartLength { /// Start of range. begin: Address, /// Length of range. length: u64, /// Location description. data: Expression, }, /// DW_LLE_default_location DefaultLocation { /// Location description. data: Expression, }, } fn write_expression( w: &mut W, refs: &mut Vec, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, val: &Expression, ) -> Result<()> { let size = val.size(encoding, unit_offsets) as u64; if encoding.version <= 4 { w.write_udata(size, 2)?; } else { w.write_uleb128(size)?; } val.write(w, Some(refs), encoding, unit_offsets)?; Ok(()) } #[cfg(feature = "read")] mod convert { use super::*; use crate::read::{self, Reader}; use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; impl LocationList { /// Create a location list by reading the data from the give location list iter. pub(crate) fn from>( mut from: read::RawLocListIter, context: &ConvertUnitContext<'_, R>, ) -> ConvertResult { let mut have_base_address = context.base_address != Address::Constant(0); let convert_address = |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); let convert_expression = |x| { Expression::from( x, context.unit.encoding(), Some(context.dwarf), Some(context.unit), Some(context.entry_ids), context.convert_address, ) }; let mut loc_list = Vec::new(); while let Some(from_loc) = from.next()? { let loc = match from_loc { read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => { // These were parsed as addresses, even if they are offsets. let begin = convert_address(begin)?; let end = convert_address(end)?; let data = convert_expression(data)?; match (begin, end) { (Address::Constant(begin_offset), Address::Constant(end_offset)) => { if have_base_address { Location::OffsetPair { begin: begin_offset, end: end_offset, data, } } else { Location::StartEnd { begin, end, data } } } _ => { if have_base_address { // At least one of begin/end is an address, but we also have // a base address. Adding addresses is undefined. return Err(ConvertError::InvalidRangeRelativeAddress); } Location::StartEnd { begin, end, data } } } } read::RawLocListEntry::BaseAddress { addr } => { have_base_address = true; let address = convert_address(addr)?; Location::BaseAddress { address } } read::RawLocListEntry::BaseAddressx { addr } => { have_base_address = true; let address = convert_address(context.dwarf.address(context.unit, addr)?)?; Location::BaseAddress { address } } read::RawLocListEntry::StartxEndx { begin, end, data } => { let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; let end = convert_address(context.dwarf.address(context.unit, end)?)?; let data = convert_expression(data)?; Location::StartEnd { begin, end, data } } read::RawLocListEntry::StartxLength { begin, length, data, } => { let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; let data = convert_expression(data)?; Location::StartLength { begin, length, data, } } read::RawLocListEntry::OffsetPair { begin, end, data } => { let data = convert_expression(data)?; Location::OffsetPair { begin, end, data } } read::RawLocListEntry::StartEnd { begin, end, data } => { let begin = convert_address(begin)?; let end = convert_address(end)?; let data = convert_expression(data)?; Location::StartEnd { begin, end, data } } read::RawLocListEntry::StartLength { begin, length, data, } => { let begin = convert_address(begin)?; let data = convert_expression(data)?; Location::StartLength { begin, length, data, } } read::RawLocListEntry::DefaultLocation { data } => { let data = convert_expression(data)?; Location::DefaultLocation { data } } }; // In some cases, existing data may contain begin == end, filtering // these out. match loc { Location::StartLength { length: 0, .. } => continue, Location::StartEnd { begin, end, .. } if begin == end => continue, Location::OffsetPair { begin, end, .. } if begin == end => continue, _ => (), } loc_list.push(loc); } Ok(LocationList(loc_list)) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::common::{ DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, DebugStrOffsetsBase, Format, }; use crate::read; use crate::write::{ ConvertUnitContext, EndianVec, LineStringTable, RangeListTable, StringTable, }; use crate::LittleEndian; use std::collections::HashMap; use std::sync::Arc; #[test] fn test_loc_list() { let mut line_strings = LineStringTable::default(); let mut strings = StringTable::default(); let mut expression = Expression::new(); expression.op_constu(0); for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut loc_list = LocationList(vec![ Location::StartLength { begin: Address::Constant(6666), length: 7777, data: expression.clone(), }, Location::StartEnd { begin: Address::Constant(4444), end: Address::Constant(5555), data: expression.clone(), }, Location::BaseAddress { address: Address::Constant(1111), }, Location::OffsetPair { begin: 2222, end: 3333, data: expression.clone(), }, ]); if version >= 5 { loc_list.0.push(Location::DefaultLocation { data: expression.clone(), }); } let mut locations = LocationListTable::default(); let loc_list_id = locations.add(loc_list.clone()); let mut sections = Sections::new(EndianVec::new(LittleEndian)); let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); assert!(sections.debug_loc_refs.is_empty()); assert!(sections.debug_loclists_refs.is_empty()); let read_debug_loc = read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); let read_debug_loclists = read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); let read_loc = read::LocationLists::new(read_debug_loc, read_debug_loclists); let offset = loc_list_offsets.get(loc_list_id); let read_loc_list = read_loc.raw_locations(offset, encoding).unwrap(); let dwarf = read::Dwarf { locations: read_loc, ..Default::default() }; let unit = read::Unit { header: read::UnitHeader::new( encoding, 0, read::UnitType::Compilation, DebugAbbrevOffset(0), DebugInfoOffset(0).into(), read::EndianSlice::default(), ), abbreviations: Arc::new(read::Abbreviations::default()), name: None, comp_dir: None, low_pc: 0, str_offsets_base: DebugStrOffsetsBase(0), addr_base: DebugAddrBase(0), loclists_base: DebugLocListsBase(0), rnglists_base: DebugRngListsBase(0), line_program: None, dwo_id: None, }; let context = ConvertUnitContext { dwarf: &dwarf, unit: &unit, line_strings: &mut line_strings, strings: &mut strings, ranges: &mut RangeListTable::default(), locations: &mut locations, convert_address: &|address| Some(Address::Constant(address)), base_address: Address::Constant(0), line_program_offset: None, line_program_files: Vec::new(), entry_ids: &HashMap::new(), }; let convert_loc_list = LocationList::from(read_loc_list, &context).unwrap(); if version <= 4 { loc_list.0[0] = Location::StartEnd { begin: Address::Constant(6666), end: Address::Constant(6666 + 7777), data: expression.clone(), }; } assert_eq!(loc_list, convert_loc_list); } } } } } gimli-0.31.1/src/write/mod.rs000064400000000000000000000347671046102023000140760ustar 00000000000000//! Write DWARF debugging information. //! //! ## API Structure //! //! This module works by building up a representation of the debugging information //! in memory, and then writing it all at once. It supports two major use cases: //! //! * Use the [`DwarfUnit`](./struct.DwarfUnit.html) type when writing DWARF //! for a single compilation unit. //! //! * Use the [`Dwarf`](./struct.Dwarf.html) type when writing DWARF for multiple //! compilation units. //! //! The module also supports reading in DWARF debugging information and writing it out //! again, possibly after modifying it. Create a [`read::Dwarf`](../read/struct.Dwarf.html) //! instance, and then use [`Dwarf::from`](./struct.Dwarf.html#method.from) to convert //! it to a writable instance. //! //! ## Example Usage //! //! Write a compilation unit containing only the top level DIE. //! //! ```rust //! use gimli::write::{ //! Address, AttributeValue, DwarfUnit, EndianVec, Error, Range, RangeList, Sections, //! }; //! //! fn example() -> Result<(), Error> { //! // Choose the encoding parameters. //! let encoding = gimli::Encoding { //! format: gimli::Format::Dwarf32, //! version: 5, //! address_size: 8, //! }; //! // Create a container for a single compilation unit. //! let mut dwarf = DwarfUnit::new(encoding); //! // Set a range attribute on the root DIE. //! let range_list = RangeList(vec![Range::StartLength { //! begin: Address::Constant(0x100), //! length: 42, //! }]); //! let range_list_id = dwarf.unit.ranges.add(range_list); //! let root = dwarf.unit.root(); //! dwarf.unit.get_mut(root).set( //! gimli::DW_AT_ranges, //! AttributeValue::RangeListRef(range_list_id), //! ); //! // Create a `Vec` for each DWARF section. //! let mut sections = Sections::new(EndianVec::new(gimli::LittleEndian)); //! // Finally, write the DWARF data to the sections. //! dwarf.write(&mut sections)?; //! sections.for_each(|id, data| { //! // Here you can add the data to the output object file. //! Ok(()) //! }) //! } //! # fn main() { //! # example().unwrap(); //! # } use std::error; use std::fmt; use std::result; use crate::constants; mod endian_vec; pub use self::endian_vec::*; mod writer; pub use self::writer::*; mod relocate; pub use self::relocate::*; #[macro_use] mod section; pub use self::section::*; macro_rules! define_id { ($name:ident, $docs:expr) => { #[doc=$docs] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct $name { base_id: BaseId, index: usize, } impl $name { #[inline] fn new(base_id: BaseId, index: usize) -> Self { $name { base_id, index } } } }; } macro_rules! define_offsets { ($offsets:ident: $id:ident => $offset:ident, $off_doc:expr) => { #[doc=$off_doc] #[derive(Debug)] pub struct $offsets { base_id: BaseId, // We know ids start at 0. offsets: Vec<$offset>, } impl $offsets { /// Return an empty list of offsets. #[inline] pub fn none() -> Self { $offsets { base_id: BaseId::default(), offsets: Vec::new(), } } /// Get the offset /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pub fn get(&self, id: $id) -> $offset { debug_assert_eq!(self.base_id, id.base_id); self.offsets[id.index] } /// Return the number of offsets. #[inline] pub fn count(&self) -> usize { self.offsets.len() } } }; } mod abbrev; pub use self::abbrev::*; mod cfi; pub use self::cfi::*; mod dwarf; pub use self::dwarf::*; mod line; pub use self::line::*; mod loc; pub use self::loc::*; mod op; pub use self::op::*; mod range; pub use self::range::*; mod str; pub use self::str::*; mod unit; pub use self::unit::*; /// An error that occurred when writing. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Error { /// The given offset is out of bounds. OffsetOutOfBounds, /// The given length is out of bounds. LengthOutOfBounds, /// The attribute value is an invalid for writing. InvalidAttributeValue, /// The value is too large for the encoding form. ValueTooLarge, /// Unsupported word size. UnsupportedWordSize(u8), /// Unsupported DWARF version. UnsupportedVersion(u16), /// The unit length is too large for the requested DWARF format. InitialLengthOverflow, /// The address is invalid. InvalidAddress, /// The reference is invalid. InvalidReference, /// A requested feature requires a different DWARF version. NeedVersion(u16), /// Strings in line number program have mismatched forms. LineStringFormMismatch, /// The range is empty or otherwise invalid. InvalidRange, /// The line number program encoding is incompatible with the unit encoding. IncompatibleLineProgramEncoding, /// Could not encode code offset for a frame instruction. InvalidFrameCodeOffset(u32), /// Could not encode data offset for a frame instruction. InvalidFrameDataOffset(i32), /// Unsupported eh_frame pointer encoding. UnsupportedPointerEncoding(constants::DwEhPe), /// Unsupported reference in CFI expression. UnsupportedCfiExpressionReference, /// Unsupported forward reference in expression. UnsupportedExpressionForwardReference, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> { match *self { Error::OffsetOutOfBounds => write!(f, "The given offset is out of bounds."), Error::LengthOutOfBounds => write!(f, "The given length is out of bounds."), Error::InvalidAttributeValue => { write!(f, "The attribute value is an invalid for writing.") } Error::ValueTooLarge => write!(f, "The value is too large for the encoding form."), Error::UnsupportedWordSize(size) => write!(f, "Unsupported word size: {}", size), Error::UnsupportedVersion(version) => { write!(f, "Unsupported DWARF version: {}", version) } Error::InitialLengthOverflow => write!( f, "The unit length is too large for the requested DWARF format." ), Error::InvalidAddress => write!(f, "The address is invalid."), Error::InvalidReference => write!(f, "The reference is invalid."), Error::NeedVersion(version) => write!( f, "A requested feature requires a DWARF version {}.", version ), Error::LineStringFormMismatch => { write!(f, "Strings in line number program have mismatched forms.") } Error::InvalidRange => write!(f, "The range is empty or otherwise invalid."), Error::IncompatibleLineProgramEncoding => write!( f, "The line number program encoding is incompatible with the unit encoding." ), Error::InvalidFrameCodeOffset(offset) => write!( f, "Could not encode code offset ({}) for a frame instruction.", offset, ), Error::InvalidFrameDataOffset(offset) => write!( f, "Could not encode data offset ({}) for a frame instruction.", offset, ), Error::UnsupportedPointerEncoding(eh_pe) => { write!(f, "Unsupported eh_frame pointer encoding ({}).", eh_pe) } Error::UnsupportedCfiExpressionReference => { write!(f, "Unsupported reference in CFI expression.") } Error::UnsupportedExpressionForwardReference => { write!(f, "Unsupported forward reference in expression.") } } } } impl error::Error for Error {} /// The result of a write. pub type Result = result::Result; /// An address. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Address { /// A fixed address that does not require relocation. Constant(u64), /// An address that is relative to a symbol which may be relocated. Symbol { /// The symbol that the address is relative to. /// /// The meaning of this value is decided by the writer, but /// will typically be an index into a symbol table. symbol: usize, /// The offset of the address relative to the symbol. /// /// This will typically be used as the addend in a relocation. addend: i64, }, } /// A reference to a `.debug_info` entry. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Reference { /// An external symbol. /// /// The meaning of this value is decided by the writer, but /// will typically be an index into a symbol table. Symbol(usize), /// An entry in the same section. /// /// This only supports references in units that are emitted together. Entry(UnitId, UnitEntryId), } // This type is only used in debug assertions. #[cfg(not(debug_assertions))] type BaseId = (); #[cfg(debug_assertions)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct BaseId(usize); #[cfg(debug_assertions)] impl Default for BaseId { fn default() -> Self { use std::sync::atomic; static BASE_ID: atomic::AtomicUsize = atomic::AtomicUsize::new(0); BaseId(BASE_ID.fetch_add(1, atomic::Ordering::Relaxed)) } } #[cfg(feature = "read")] mod convert { use super::*; use crate::read; pub(crate) use super::unit::convert::*; /// An error that occurred when converting a read value into a write value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConvertError { /// An error occurred when reading. Read(read::Error), /// Writing of this attribute value is not implemented yet. UnsupportedAttributeValue, /// This attribute value is an invalid name/form combination. InvalidAttributeValue, /// A `.debug_info` reference does not refer to a valid entry. InvalidDebugInfoOffset, /// An address could not be converted. InvalidAddress, /// Writing this line number instruction is not implemented yet. UnsupportedLineInstruction, /// Writing this form of line string is not implemented yet. UnsupportedLineStringForm, /// A `.debug_line` file index is invalid. InvalidFileIndex, /// A `.debug_line` directory index is invalid. InvalidDirectoryIndex, /// A `.debug_line` line base is invalid. InvalidLineBase, /// A `.debug_line` reference is invalid. InvalidLineRef, /// A `.debug_info` unit entry reference is invalid. InvalidUnitRef, /// A `.debug_info` reference is invalid. InvalidDebugInfoRef, /// Invalid relative address in a range list. InvalidRangeRelativeAddress, /// Writing this CFI instruction is not implemented yet. UnsupportedCfiInstruction, /// Writing indirect pointers is not implemented yet. UnsupportedIndirectAddress, /// Writing this expression operation is not implemented yet. UnsupportedOperation, /// Operation branch target is invalid. InvalidBranchTarget, /// Writing this unit type is not supported yet. UnsupportedUnitType, } impl fmt::Display for ConvertError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> { use self::ConvertError::*; match *self { Read(ref e) => e.fmt(f), UnsupportedAttributeValue => { write!(f, "Writing of this attribute value is not implemented yet.") } InvalidAttributeValue => write!( f, "This attribute value is an invalid name/form combination." ), InvalidDebugInfoOffset => write!( f, "A `.debug_info` reference does not refer to a valid entry." ), InvalidAddress => write!(f, "An address could not be converted."), UnsupportedLineInstruction => write!( f, "Writing this line number instruction is not implemented yet." ), UnsupportedLineStringForm => write!( f, "Writing this form of line string is not implemented yet." ), InvalidFileIndex => write!(f, "A `.debug_line` file index is invalid."), InvalidDirectoryIndex => write!(f, "A `.debug_line` directory index is invalid."), InvalidLineBase => write!(f, "A `.debug_line` line base is invalid."), InvalidLineRef => write!(f, "A `.debug_line` reference is invalid."), InvalidUnitRef => write!(f, "A `.debug_info` unit entry reference is invalid."), InvalidDebugInfoRef => write!(f, "A `.debug_info` reference is invalid."), InvalidRangeRelativeAddress => { write!(f, "Invalid relative address in a range list.") } UnsupportedCfiInstruction => { write!(f, "Writing this CFI instruction is not implemented yet.") } UnsupportedIndirectAddress => { write!(f, "Writing indirect pointers is not implemented yet.") } UnsupportedOperation => write!( f, "Writing this expression operation is not implemented yet." ), InvalidBranchTarget => write!(f, "Operation branch target is invalid."), UnsupportedUnitType => write!(f, "Writing this unit type is not supported yet."), } } } impl error::Error for ConvertError {} impl From for ConvertError { fn from(e: read::Error) -> Self { ConvertError::Read(e) } } /// The result of a conversion. pub type ConvertResult = result::Result; } #[cfg(feature = "read")] pub use self::convert::*; gimli-0.31.1/src/write/op.rs000064400000000000000000002102621046102023000137170ustar 00000000000000use alloc::boxed::Box; use alloc::vec::Vec; use crate::common::{Encoding, Register}; use crate::constants::{self, DwOp}; use crate::leb128::write::{sleb128_size, uleb128_size}; use crate::write::{ Address, DebugInfoReference, Error, Reference, Result, UnitEntryId, UnitOffsets, Writer, }; /// The bytecode for a DWARF expression or location description. #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct Expression { operations: Vec, } impl Expression { /// Create an empty expression. #[inline] pub fn new() -> Self { Self::default() } /// Create an expression from raw bytecode. /// /// This does not support operations that require references, such as `DW_OP_addr`. #[inline] pub fn raw(bytecode: Vec) -> Self { Expression { operations: vec![Operation::Raw(bytecode)], } } /// Add an operation to the expression. /// /// This should only be used for operations that have no explicit operands. pub fn op(&mut self, opcode: DwOp) { self.operations.push(Operation::Simple(opcode)); } /// Add a `DW_OP_addr` operation to the expression. pub fn op_addr(&mut self, address: Address) { self.operations.push(Operation::Address(address)); } /// Add a `DW_OP_constu` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_constu(&mut self, value: u64) { self.operations.push(Operation::UnsignedConstant(value)); } /// Add a `DW_OP_consts` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_consts(&mut self, value: i64) { self.operations.push(Operation::SignedConstant(value)); } /// Add a `DW_OP_const_type` or `DW_OP_GNU_const_type` operation to the expression. pub fn op_const_type(&mut self, base: UnitEntryId, value: Box<[u8]>) { self.operations.push(Operation::ConstantType(base, value)); } /// Add a `DW_OP_fbreg` operation to the expression. pub fn op_fbreg(&mut self, offset: i64) { self.operations.push(Operation::FrameOffset(offset)); } /// Add a `DW_OP_bregx` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_breg(&mut self, register: Register, offset: i64) { self.operations .push(Operation::RegisterOffset(register, offset)); } /// Add a `DW_OP_regval_type` or `DW_OP_GNU_regval_type` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_regval_type(&mut self, register: Register, base: UnitEntryId) { self.operations .push(Operation::RegisterType(register, base)); } /// Add a `DW_OP_pick` operation to the expression. /// /// This may be emitted as a `DW_OP_dup` or `DW_OP_over` operation. pub fn op_pick(&mut self, index: u8) { self.operations.push(Operation::Pick(index)); } /// Add a `DW_OP_deref` operation to the expression. pub fn op_deref(&mut self) { self.operations.push(Operation::Deref { space: false }); } /// Add a `DW_OP_xderef` operation to the expression. pub fn op_xderef(&mut self) { self.operations.push(Operation::Deref { space: true }); } /// Add a `DW_OP_deref_size` operation to the expression. pub fn op_deref_size(&mut self, size: u8) { self.operations .push(Operation::DerefSize { size, space: false }); } /// Add a `DW_OP_xderef_size` operation to the expression. pub fn op_xderef_size(&mut self, size: u8) { self.operations .push(Operation::DerefSize { size, space: true }); } /// Add a `DW_OP_deref_type` or `DW_OP_GNU_deref_type` operation to the expression. pub fn op_deref_type(&mut self, size: u8, base: UnitEntryId) { self.operations.push(Operation::DerefType { size, base, space: false, }); } /// Add a `DW_OP_xderef_type` operation to the expression. pub fn op_xderef_type(&mut self, size: u8, base: UnitEntryId) { self.operations.push(Operation::DerefType { size, base, space: true, }); } /// Add a `DW_OP_plus_uconst` operation to the expression. pub fn op_plus_uconst(&mut self, value: u64) { self.operations.push(Operation::PlusConstant(value)); } /// Add a `DW_OP_skip` operation to the expression. /// /// Returns the index of the operation. The caller must call `set_target` with /// this index to set the target of the branch. pub fn op_skip(&mut self) -> usize { let index = self.next_index(); self.operations.push(Operation::Skip(!0)); index } /// Add a `DW_OP_bra` operation to the expression. /// /// Returns the index of the operation. The caller must call `set_target` with /// this index to set the target of the branch. pub fn op_bra(&mut self) -> usize { let index = self.next_index(); self.operations.push(Operation::Branch(!0)); index } /// Return the index that will be assigned to the next operation. /// /// This can be passed to `set_target`. #[inline] pub fn next_index(&self) -> usize { self.operations.len() } /// Set the target of a `DW_OP_skip` or `DW_OP_bra` operation . pub fn set_target(&mut self, operation: usize, new_target: usize) { debug_assert!(new_target <= self.next_index()); debug_assert_ne!(operation, new_target); match self.operations[operation] { Operation::Skip(ref mut target) | Operation::Branch(ref mut target) => { *target = new_target; } _ => unimplemented!(), } } /// Add a `DW_OP_call4` operation to the expression. pub fn op_call(&mut self, entry: UnitEntryId) { self.operations.push(Operation::Call(entry)); } /// Add a `DW_OP_call_ref` operation to the expression. pub fn op_call_ref(&mut self, entry: Reference) { self.operations.push(Operation::CallRef(entry)); } /// Add a `DW_OP_convert` or `DW_OP_GNU_convert` operation to the expression. /// /// `base` is the DIE of the base type, or `None` for the generic type. pub fn op_convert(&mut self, base: Option) { self.operations.push(Operation::Convert(base)); } /// Add a `DW_OP_reinterpret` or `DW_OP_GNU_reinterpret` operation to the expression. /// /// `base` is the DIE of the base type, or `None` for the generic type. pub fn op_reinterpret(&mut self, base: Option) { self.operations.push(Operation::Reinterpret(base)); } /// Add a `DW_OP_entry_value` or `DW_OP_GNU_entry_value` operation to the expression. pub fn op_entry_value(&mut self, expression: Expression) { self.operations.push(Operation::EntryValue(expression)); } /// Add a `DW_OP_regx` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_reg(&mut self, register: Register) { self.operations.push(Operation::Register(register)); } /// Add a `DW_OP_implicit_value` operation to the expression. pub fn op_implicit_value(&mut self, data: Box<[u8]>) { self.operations.push(Operation::ImplicitValue(data)); } /// Add a `DW_OP_implicit_pointer` or `DW_OP_GNU_implicit_pointer` operation to the expression. pub fn op_implicit_pointer(&mut self, entry: Reference, byte_offset: i64) { self.operations .push(Operation::ImplicitPointer { entry, byte_offset }); } /// Add a `DW_OP_piece` operation to the expression. pub fn op_piece(&mut self, size_in_bytes: u64) { self.operations.push(Operation::Piece { size_in_bytes }); } /// Add a `DW_OP_bit_piece` operation to the expression. pub fn op_bit_piece(&mut self, size_in_bits: u64, bit_offset: u64) { self.operations.push(Operation::BitPiece { size_in_bits, bit_offset, }); } /// Add a `DW_OP_GNU_parameter_ref` operation to the expression. pub fn op_gnu_parameter_ref(&mut self, entry: UnitEntryId) { self.operations.push(Operation::ParameterRef(entry)); } /// Add a `DW_OP_WASM_location 0x0` operation to the expression. pub fn op_wasm_local(&mut self, index: u32) { self.operations.push(Operation::WasmLocal(index)); } /// Add a `DW_OP_WASM_location 0x1` operation to the expression. pub fn op_wasm_global(&mut self, index: u32) { self.operations.push(Operation::WasmGlobal(index)); } /// Add a `DW_OP_WASM_location 0x2` operation to the expression. pub fn op_wasm_stack(&mut self, index: u32) { self.operations.push(Operation::WasmStack(index)); } pub(crate) fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { let mut size = 0; for operation in &self.operations { size += operation.size(encoding, unit_offsets); } size } pub(crate) fn write( &self, w: &mut W, mut refs: Option<&mut Vec>, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result<()> { // TODO: only calculate offsets if needed? let mut offsets = Vec::with_capacity(self.operations.len()); let mut offset = w.len(); for operation in &self.operations { offsets.push(offset); offset += operation.size(encoding, unit_offsets); } offsets.push(offset); for (operation, offset) in self.operations.iter().zip(offsets.iter().copied()) { debug_assert_eq!(w.len(), offset); operation.write(w, refs.as_deref_mut(), encoding, unit_offsets, &offsets)?; } Ok(()) } } /// A single DWARF operation. // // This type is intentionally not public so that we can change the // representation of expressions as needed. // // Variants are listed in the order they appear in Section 2.5. #[derive(Debug, Clone, PartialEq, Eq, Hash)] enum Operation { /// Raw bytecode. /// /// Does not support references. Raw(Vec), /// An operation that has no explicit operands. /// /// Represents: /// - `DW_OP_drop`, `DW_OP_swap`, `DW_OP_rot` /// - `DW_OP_push_object_address`, `DW_OP_form_tls_address`, `DW_OP_call_frame_cfa` /// - `DW_OP_abs`, `DW_OP_and`, `DW_OP_div`, `DW_OP_minus`, `DW_OP_mod`, `DW_OP_mul`, /// `DW_OP_neg`, `DW_OP_not`, `DW_OP_or`, `DW_OP_plus`, `DW_OP_shl`, `DW_OP_shr`, /// `DW_OP_shra`, `DW_OP_xor` /// - `DW_OP_le`, `DW_OP_ge`, `DW_OP_eq`, `DW_OP_lt`, `DW_OP_gt`, `DW_OP_ne` /// - `DW_OP_nop` /// - `DW_OP_stack_value` Simple(DwOp), /// Relocate the address if needed, and push it on the stack. /// /// Represents `DW_OP_addr`. Address(Address), /// Push an unsigned constant value on the stack. /// /// Represents `DW_OP_constu`. UnsignedConstant(u64), /// Push a signed constant value on the stack. /// /// Represents `DW_OP_consts`. SignedConstant(i64), /* TODO: requires .debug_addr write support /// Read the address at the given index in `.debug_addr, relocate the address if needed, /// and push it on the stack. /// /// Represents `DW_OP_addrx`. AddressIndex(DebugAddrIndex), /// Read the address at the given index in `.debug_addr, and push it on the stack. /// Do not relocate the address. /// /// Represents `DW_OP_constx`. ConstantIndex(DebugAddrIndex), */ /// Interpret the value bytes as a constant of a given type, and push it on the stack. /// /// Represents `DW_OP_const_type`. ConstantType(UnitEntryId, Box<[u8]>), /// Compute the frame base (using `DW_AT_frame_base`), add the /// given offset, and then push the resulting sum on the stack. /// /// Represents `DW_OP_fbreg`. FrameOffset(i64), /// Find the contents of the given register, add the offset, and then /// push the resulting sum on the stack. /// /// Represents `DW_OP_bregx`. RegisterOffset(Register, i64), /// Interpret the contents of the given register as a value of the given type, /// and push it on the stack. /// /// Represents `DW_OP_regval_type`. RegisterType(Register, UnitEntryId), /// Copy the item at a stack index and push it on top of the stack. /// /// Represents `DW_OP_pick`, `DW_OP_dup`, and `DW_OP_over`. Pick(u8), /// Pop the topmost value of the stack, dereference it, and push the /// resulting value. /// /// Represents `DW_OP_deref` and `DW_OP_xderef`. Deref { /// True if the dereference operation takes an address space /// argument from the stack; false otherwise. space: bool, }, /// Pop the topmost value of the stack, dereference it to obtain a value /// of the given size, and push the resulting value. /// /// Represents `DW_OP_deref_size` and `DW_OP_xderef_size`. DerefSize { /// True if the dereference operation takes an address space /// argument from the stack; false otherwise. space: bool, /// The size of the data to dereference. size: u8, }, /// Pop the topmost value of the stack, dereference it to obtain a value /// of the given type, and push the resulting value. /// /// Represents `DW_OP_deref_type` and `DW_OP_xderef_type`. DerefType { /// True if the dereference operation takes an address space /// argument from the stack; false otherwise. space: bool, /// The size of the data to dereference. size: u8, /// The DIE of the base type, or `None` for the generic type. base: UnitEntryId, }, /// Add an unsigned constant to the topmost value on the stack. /// /// Represents `DW_OP_plus_uconst`. PlusConstant(u64), /// Unconditional branch to the target location. /// /// The value is the index within the expression of the operation to branch to. /// This will be converted to a relative offset when writing. /// /// Represents `DW_OP_skip`. Skip(usize), /// Branch to the target location if the top of stack is nonzero. /// /// The value is the index within the expression of the operation to branch to. /// This will be converted to a relative offset when writing. /// /// Represents `DW_OP_bra`. Branch(usize), /// Evaluate a DWARF expression as a subroutine. /// /// The expression comes from the `DW_AT_location` attribute of the indicated DIE. /// /// Represents `DW_OP_call4`. Call(UnitEntryId), /// Evaluate an external DWARF expression as a subroutine. /// /// The expression comes from the `DW_AT_location` attribute of the indicated DIE, /// which may be in another compilation unit or shared object. /// /// Represents `DW_OP_call_ref`. CallRef(Reference), /// Pop the top stack entry, convert it to a different type, and push it on the stack. /// /// Represents `DW_OP_convert`. Convert(Option), /// Pop the top stack entry, reinterpret the bits in its value as a different type, /// and push it on the stack. /// /// Represents `DW_OP_reinterpret`. Reinterpret(Option), /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. /// /// Represents `DW_OP_entry_value`. EntryValue(Expression), // FIXME: EntryRegister /// Indicate that this piece's location is in the given register. /// /// Completes the piece or expression. /// /// Represents `DW_OP_regx`. Register(Register), /// The object has no location, but has a known constant value. /// /// Completes the piece or expression. /// /// Represents `DW_OP_implicit_value`. ImplicitValue(Box<[u8]>), /// The object is a pointer to a value which has no actual location, such as /// an implicit value or a stack value. /// /// Completes the piece or expression. /// /// Represents `DW_OP_implicit_pointer`. ImplicitPointer { /// The DIE of the value that this is an implicit pointer into. entry: Reference, /// The byte offset into the value that the implicit pointer points to. byte_offset: i64, }, /// Terminate a piece. /// /// Represents `DW_OP_piece`. Piece { /// The size of this piece in bytes. size_in_bytes: u64, }, /// Terminate a piece with a size in bits. /// /// Represents `DW_OP_bit_piece`. BitPiece { /// The size of this piece in bits. size_in_bits: u64, /// The bit offset of this piece. bit_offset: u64, }, /// This represents a parameter that was optimized out. /// /// The entry is the definition of the parameter, and is matched to /// the `DW_TAG_GNU_call_site_parameter` in the caller that also /// points to the same definition of the parameter. /// /// Represents `DW_OP_GNU_parameter_ref`. ParameterRef(UnitEntryId), /// The index of a local in the currently executing function. /// /// Represents `DW_OP_WASM_location 0x00`. WasmLocal(u32), /// The index of a global. /// /// Represents `DW_OP_WASM_location 0x01`. WasmGlobal(u32), /// The index of an item on the operand stack. /// /// Represents `DW_OP_WASM_location 0x02`. WasmStack(u32), } impl Operation { fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { let base_size = |base| { // Errors are handled during writes. match unit_offsets { Some(offsets) => uleb128_size(offsets.unit_offset(base)), None => 0, } }; 1 + match *self { Operation::Raw(ref bytecode) => return bytecode.len(), Operation::Simple(_) => 0, Operation::Address(_) => encoding.address_size as usize, Operation::UnsignedConstant(value) => { if value < 32 { 0 } else { uleb128_size(value) } } Operation::SignedConstant(value) => sleb128_size(value), Operation::ConstantType(base, ref value) => base_size(base) + 1 + value.len(), Operation::FrameOffset(offset) => sleb128_size(offset), Operation::RegisterOffset(register, offset) => { if register.0 < 32 { sleb128_size(offset) } else { uleb128_size(register.0.into()) + sleb128_size(offset) } } Operation::RegisterType(register, base) => { uleb128_size(register.0.into()) + base_size(base) } Operation::Pick(index) => { if index > 1 { 1 } else { 0 } } Operation::Deref { .. } => 0, Operation::DerefSize { .. } => 1, Operation::DerefType { base, .. } => 1 + base_size(base), Operation::PlusConstant(value) => uleb128_size(value), Operation::Skip(_) => 2, Operation::Branch(_) => 2, Operation::Call(_) => 4, Operation::CallRef(_) => encoding.format.word_size() as usize, Operation::Convert(base) => match base { Some(base) => base_size(base), None => 1, }, Operation::Reinterpret(base) => match base { Some(base) => base_size(base), None => 1, }, Operation::EntryValue(ref expression) => { let length = expression.size(encoding, unit_offsets); uleb128_size(length as u64) + length } Operation::Register(register) => { if register.0 < 32 { 0 } else { uleb128_size(register.0.into()) } } Operation::ImplicitValue(ref data) => uleb128_size(data.len() as u64) + data.len(), Operation::ImplicitPointer { byte_offset, .. } => { let size = if encoding.version == 2 { encoding.address_size } else { encoding.format.word_size() }; size as usize + sleb128_size(byte_offset) } Operation::Piece { size_in_bytes } => uleb128_size(size_in_bytes), Operation::BitPiece { size_in_bits, bit_offset, } => uleb128_size(size_in_bits) + uleb128_size(bit_offset), Operation::ParameterRef(_) => 4, Operation::WasmLocal(index) | Operation::WasmGlobal(index) | Operation::WasmStack(index) => 1 + uleb128_size(index.into()), } } pub(crate) fn write( &self, w: &mut W, refs: Option<&mut Vec>, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, offsets: &[usize], ) -> Result<()> { let entry_offset = |entry| match unit_offsets { Some(offsets) => { let offset = offsets.unit_offset(entry); if offset == 0 { Err(Error::UnsupportedExpressionForwardReference) } else { Ok(offset) } } None => Err(Error::UnsupportedCfiExpressionReference), }; match *self { Operation::Raw(ref bytecode) => w.write(bytecode)?, Operation::Simple(opcode) => w.write_u8(opcode.0)?, Operation::Address(address) => { w.write_u8(constants::DW_OP_addr.0)?; w.write_address(address, encoding.address_size)?; } Operation::UnsignedConstant(value) => { if value < 32 { w.write_u8(constants::DW_OP_lit0.0 + value as u8)?; } else { w.write_u8(constants::DW_OP_constu.0)?; w.write_uleb128(value)?; } } Operation::SignedConstant(value) => { w.write_u8(constants::DW_OP_consts.0)?; w.write_sleb128(value)?; } Operation::ConstantType(base, ref value) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_const_type.0)?; } else { w.write_u8(constants::DW_OP_GNU_const_type.0)?; } w.write_uleb128(entry_offset(base)?)?; w.write_udata(value.len() as u64, 1)?; w.write(value)?; } Operation::FrameOffset(offset) => { w.write_u8(constants::DW_OP_fbreg.0)?; w.write_sleb128(offset)?; } Operation::RegisterOffset(register, offset) => { if register.0 < 32 { w.write_u8(constants::DW_OP_breg0.0 + register.0 as u8)?; } else { w.write_u8(constants::DW_OP_bregx.0)?; w.write_uleb128(register.0.into())?; } w.write_sleb128(offset)?; } Operation::RegisterType(register, base) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_regval_type.0)?; } else { w.write_u8(constants::DW_OP_GNU_regval_type.0)?; } w.write_uleb128(register.0.into())?; w.write_uleb128(entry_offset(base)?)?; } Operation::Pick(index) => match index { 0 => w.write_u8(constants::DW_OP_dup.0)?, 1 => w.write_u8(constants::DW_OP_over.0)?, _ => { w.write_u8(constants::DW_OP_pick.0)?; w.write_u8(index)?; } }, Operation::Deref { space } => { if space { w.write_u8(constants::DW_OP_xderef.0)?; } else { w.write_u8(constants::DW_OP_deref.0)?; } } Operation::DerefSize { space, size } => { if space { w.write_u8(constants::DW_OP_xderef_size.0)?; } else { w.write_u8(constants::DW_OP_deref_size.0)?; } w.write_u8(size)?; } Operation::DerefType { space, size, base } => { if space { w.write_u8(constants::DW_OP_xderef_type.0)?; } else { if encoding.version >= 5 { w.write_u8(constants::DW_OP_deref_type.0)?; } else { w.write_u8(constants::DW_OP_GNU_deref_type.0)?; } } w.write_u8(size)?; w.write_uleb128(entry_offset(base)?)?; } Operation::PlusConstant(value) => { w.write_u8(constants::DW_OP_plus_uconst.0)?; w.write_uleb128(value)?; } Operation::Skip(target) => { w.write_u8(constants::DW_OP_skip.0)?; let offset = offsets[target] as i64 - (w.len() as i64 + 2); w.write_sdata(offset, 2)?; } Operation::Branch(target) => { w.write_u8(constants::DW_OP_bra.0)?; let offset = offsets[target] as i64 - (w.len() as i64 + 2); w.write_sdata(offset, 2)?; } Operation::Call(entry) => { w.write_u8(constants::DW_OP_call4.0)?; // TODO: this probably won't work in practice, because we may // only know the offsets of base type DIEs at this point. w.write_udata(entry_offset(entry)?, 4)?; } Operation::CallRef(entry) => { w.write_u8(constants::DW_OP_call_ref.0)?; let size = encoding.format.word_size(); match entry { Reference::Symbol(symbol) => w.write_reference(symbol, size)?, Reference::Entry(unit, entry) => { let refs = refs.ok_or(Error::InvalidReference)?; refs.push(DebugInfoReference { offset: w.len(), unit, entry, size, }); w.write_udata(0, size)?; } } } Operation::Convert(base) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_convert.0)?; } else { w.write_u8(constants::DW_OP_GNU_convert.0)?; } match base { Some(base) => w.write_uleb128(entry_offset(base)?)?, None => w.write_u8(0)?, } } Operation::Reinterpret(base) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_reinterpret.0)?; } else { w.write_u8(constants::DW_OP_GNU_reinterpret.0)?; } match base { Some(base) => w.write_uleb128(entry_offset(base)?)?, None => w.write_u8(0)?, } } Operation::EntryValue(ref expression) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_entry_value.0)?; } else { w.write_u8(constants::DW_OP_GNU_entry_value.0)?; } let length = expression.size(encoding, unit_offsets); w.write_uleb128(length as u64)?; expression.write(w, refs, encoding, unit_offsets)?; } Operation::Register(register) => { if register.0 < 32 { w.write_u8(constants::DW_OP_reg0.0 + register.0 as u8)?; } else { w.write_u8(constants::DW_OP_regx.0)?; w.write_uleb128(register.0.into())?; } } Operation::ImplicitValue(ref data) => { w.write_u8(constants::DW_OP_implicit_value.0)?; w.write_uleb128(data.len() as u64)?; w.write(data)?; } Operation::ImplicitPointer { entry, byte_offset } => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_implicit_pointer.0)?; } else { w.write_u8(constants::DW_OP_GNU_implicit_pointer.0)?; } let size = if encoding.version == 2 { encoding.address_size } else { encoding.format.word_size() }; match entry { Reference::Symbol(symbol) => { w.write_reference(symbol, size)?; } Reference::Entry(unit, entry) => { let refs = refs.ok_or(Error::InvalidReference)?; refs.push(DebugInfoReference { offset: w.len(), unit, entry, size, }); w.write_udata(0, size)?; } } w.write_sleb128(byte_offset)?; } Operation::Piece { size_in_bytes } => { w.write_u8(constants::DW_OP_piece.0)?; w.write_uleb128(size_in_bytes)?; } Operation::BitPiece { size_in_bits, bit_offset, } => { w.write_u8(constants::DW_OP_bit_piece.0)?; w.write_uleb128(size_in_bits)?; w.write_uleb128(bit_offset)?; } Operation::ParameterRef(entry) => { w.write_u8(constants::DW_OP_GNU_parameter_ref.0)?; w.write_udata(entry_offset(entry)?, 4)?; } Operation::WasmLocal(index) => { w.write(&[constants::DW_OP_WASM_location.0, 0])?; w.write_uleb128(index.into())?; } Operation::WasmGlobal(index) => { w.write(&[constants::DW_OP_WASM_location.0, 1])?; w.write_uleb128(index.into())?; } Operation::WasmStack(index) => { w.write(&[constants::DW_OP_WASM_location.0, 2])?; w.write_uleb128(index.into())?; } } Ok(()) } } #[cfg(feature = "read")] pub(crate) mod convert { use super::*; use crate::common::UnitSectionOffset; use crate::read::{self, Reader}; use crate::write::{ConvertError, ConvertResult, UnitId}; use std::collections::HashMap; impl Expression { /// Create an expression from the input expression. pub fn from>( from_expression: read::Expression, encoding: Encoding, dwarf: Option<&read::Dwarf>, unit: Option<&read::Unit>, entry_ids: Option<&HashMap>, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult { let convert_unit_offset = |offset: read::UnitOffset| -> ConvertResult<_> { let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; let id = entry_ids .get(&offset.to_unit_section_offset(unit)) .ok_or(ConvertError::InvalidUnitRef)?; Ok(id.1) }; let convert_debug_info_offset = |offset| -> ConvertResult<_> { // TODO: support relocations let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; let id = entry_ids .get(&UnitSectionOffset::DebugInfoOffset(offset)) .ok_or(ConvertError::InvalidDebugInfoRef)?; Ok(Reference::Entry(id.0, id.1)) }; // Calculate offsets for use in branch/skip operations. let mut offsets = Vec::new(); let mut offset = 0; let mut from_operations = from_expression.clone().operations(encoding); while from_operations.next()?.is_some() { offsets.push(offset); offset = from_operations.offset_from(&from_expression); } offsets.push(from_expression.0.len()); let mut from_operations = from_expression.clone().operations(encoding); let mut operations = Vec::new(); while let Some(from_operation) = from_operations.next()? { let operation = match from_operation { read::Operation::Deref { base_type, size, space, } => { if base_type.0 != 0 { let base = convert_unit_offset(base_type)?; Operation::DerefType { space, size, base } } else if size != encoding.address_size { Operation::DerefSize { space, size } } else { Operation::Deref { space } } } read::Operation::Drop => Operation::Simple(constants::DW_OP_drop), read::Operation::Pick { index } => Operation::Pick(index), read::Operation::Swap => Operation::Simple(constants::DW_OP_swap), read::Operation::Rot => Operation::Simple(constants::DW_OP_rot), read::Operation::Abs => Operation::Simple(constants::DW_OP_abs), read::Operation::And => Operation::Simple(constants::DW_OP_and), read::Operation::Div => Operation::Simple(constants::DW_OP_div), read::Operation::Minus => Operation::Simple(constants::DW_OP_minus), read::Operation::Mod => Operation::Simple(constants::DW_OP_mod), read::Operation::Mul => Operation::Simple(constants::DW_OP_mul), read::Operation::Neg => Operation::Simple(constants::DW_OP_neg), read::Operation::Not => Operation::Simple(constants::DW_OP_not), read::Operation::Or => Operation::Simple(constants::DW_OP_or), read::Operation::Plus => Operation::Simple(constants::DW_OP_plus), read::Operation::PlusConstant { value } => Operation::PlusConstant(value), read::Operation::Shl => Operation::Simple(constants::DW_OP_shl), read::Operation::Shr => Operation::Simple(constants::DW_OP_shr), read::Operation::Shra => Operation::Simple(constants::DW_OP_shra), read::Operation::Xor => Operation::Simple(constants::DW_OP_xor), read::Operation::Eq => Operation::Simple(constants::DW_OP_eq), read::Operation::Ge => Operation::Simple(constants::DW_OP_ge), read::Operation::Gt => Operation::Simple(constants::DW_OP_gt), read::Operation::Le => Operation::Simple(constants::DW_OP_le), read::Operation::Lt => Operation::Simple(constants::DW_OP_lt), read::Operation::Ne => Operation::Simple(constants::DW_OP_ne), read::Operation::Bra { target } => { let offset = from_operations .offset_from(&from_expression) .wrapping_add(i64::from(target) as usize); let index = offsets .binary_search(&offset) .map_err(|_| ConvertError::InvalidBranchTarget)?; Operation::Branch(index) } read::Operation::Skip { target } => { let offset = from_operations .offset_from(&from_expression) .wrapping_add(i64::from(target) as usize); let index = offsets .binary_search(&offset) .map_err(|_| ConvertError::InvalidBranchTarget)?; Operation::Skip(index) } read::Operation::UnsignedConstant { value } => { Operation::UnsignedConstant(value) } read::Operation::SignedConstant { value } => Operation::SignedConstant(value), read::Operation::Register { register } => Operation::Register(register), read::Operation::RegisterOffset { register, offset, base_type, } => { if base_type.0 != 0 { Operation::RegisterType(register, convert_unit_offset(base_type)?) } else { Operation::RegisterOffset(register, offset) } } read::Operation::FrameOffset { offset } => Operation::FrameOffset(offset), read::Operation::Nop => Operation::Simple(constants::DW_OP_nop), read::Operation::PushObjectAddress => { Operation::Simple(constants::DW_OP_push_object_address) } read::Operation::Call { offset } => match offset { read::DieReference::UnitRef(offset) => { Operation::Call(convert_unit_offset(offset)?) } read::DieReference::DebugInfoRef(offset) => { Operation::CallRef(convert_debug_info_offset(offset)?) } }, read::Operation::TLS => Operation::Simple(constants::DW_OP_form_tls_address), read::Operation::CallFrameCFA => { Operation::Simple(constants::DW_OP_call_frame_cfa) } read::Operation::Piece { size_in_bits, bit_offset: None, } => Operation::Piece { size_in_bytes: size_in_bits / 8, }, read::Operation::Piece { size_in_bits, bit_offset: Some(bit_offset), } => Operation::BitPiece { size_in_bits, bit_offset, }, read::Operation::ImplicitValue { data } => { Operation::ImplicitValue(data.to_slice()?.into_owned().into()) } read::Operation::StackValue => Operation::Simple(constants::DW_OP_stack_value), read::Operation::ImplicitPointer { value, byte_offset } => { let entry = convert_debug_info_offset(value)?; Operation::ImplicitPointer { entry, byte_offset } } read::Operation::EntryValue { expression } => { let expression = Expression::from( read::Expression(expression), encoding, dwarf, unit, entry_ids, convert_address, )?; Operation::EntryValue(expression) } read::Operation::ParameterRef { offset } => { let entry = convert_unit_offset(offset)?; Operation::ParameterRef(entry) } read::Operation::Address { address } => { let address = convert_address(address).ok_or(ConvertError::InvalidAddress)?; Operation::Address(address) } read::Operation::AddressIndex { index } => { let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; let val = dwarf.address(unit, index)?; let address = convert_address(val).ok_or(ConvertError::InvalidAddress)?; Operation::Address(address) } read::Operation::ConstantIndex { index } => { let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; let val = dwarf.address(unit, index)?; Operation::UnsignedConstant(val) } read::Operation::TypedLiteral { base_type, value } => { let entry = convert_unit_offset(base_type)?; Operation::ConstantType(entry, value.to_slice()?.into_owned().into()) } read::Operation::Convert { base_type } => { if base_type.0 == 0 { Operation::Convert(None) } else { let entry = convert_unit_offset(base_type)?; Operation::Convert(Some(entry)) } } read::Operation::Reinterpret { base_type } => { if base_type.0 == 0 { Operation::Reinterpret(None) } else { let entry = convert_unit_offset(base_type)?; Operation::Reinterpret(Some(entry)) } } read::Operation::WasmLocal { index } => Operation::WasmLocal(index), read::Operation::WasmGlobal { index } => Operation::WasmGlobal(index), read::Operation::WasmStack { index } => Operation::WasmStack(index), }; operations.push(operation); } Ok(Expression { operations }) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::common::{DebugInfoOffset, Format}; use crate::read; use crate::write::{AttributeValue, Dwarf, EndianVec, LineProgram, Sections, Unit}; use crate::LittleEndian; use std::collections::HashMap; #[test] #[allow(clippy::type_complexity)] fn test_operation() { for version in [2, 3, 4, 5] { for address_size in [4, 8] { for format in [Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut dwarf = Dwarf::new(); let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); let unit = dwarf.units.get_mut(unit_id); // Create an entry that can be referenced by the expression. let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type); let reference = Reference::Entry(unit_id, entry_id); // The offsets for the above entry when reading back the expression. struct ReadState { debug_info_offset: DebugInfoOffset, entry_offset: read::UnitOffset, } let mut reg_expression = Expression::new(); reg_expression.op_reg(Register(23)); let operations: &[( &dyn Fn(&mut Expression), Operation, &dyn Fn(&ReadState) -> read::Operation<_>, )] = &[ ( &|x| x.op_deref(), Operation::Deref { space: false }, &|_| read::Operation::Deref { base_type: read::UnitOffset(0), size: address_size, space: false, }, ), ( &|x| x.op_xderef(), Operation::Deref { space: true }, &|_| read::Operation::Deref { base_type: read::UnitOffset(0), size: address_size, space: true, }, ), ( &|x| x.op_deref_size(2), Operation::DerefSize { space: false, size: 2, }, &|_| read::Operation::Deref { base_type: read::UnitOffset(0), size: 2, space: false, }, ), ( &|x| x.op_xderef_size(2), Operation::DerefSize { space: true, size: 2, }, &|_| read::Operation::Deref { base_type: read::UnitOffset(0), size: 2, space: true, }, ), ( &|x| x.op_deref_type(2, entry_id), Operation::DerefType { space: false, size: 2, base: entry_id, }, &|x| read::Operation::Deref { base_type: x.entry_offset, size: 2, space: false, }, ), ( &|x| x.op_xderef_type(2, entry_id), Operation::DerefType { space: true, size: 2, base: entry_id, }, &|x| read::Operation::Deref { base_type: x.entry_offset, size: 2, space: true, }, ), ( &|x| x.op(constants::DW_OP_drop), Operation::Simple(constants::DW_OP_drop), &|_| read::Operation::Drop, ), (&|x| x.op_pick(0), Operation::Pick(0), &|_| { read::Operation::Pick { index: 0 } }), (&|x| x.op_pick(1), Operation::Pick(1), &|_| { read::Operation::Pick { index: 1 } }), (&|x| x.op_pick(2), Operation::Pick(2), &|_| { read::Operation::Pick { index: 2 } }), ( &|x| x.op(constants::DW_OP_swap), Operation::Simple(constants::DW_OP_swap), &|_| read::Operation::Swap, ), ( &|x| x.op(constants::DW_OP_rot), Operation::Simple(constants::DW_OP_rot), &|_| read::Operation::Rot, ), ( &|x| x.op(constants::DW_OP_abs), Operation::Simple(constants::DW_OP_abs), &|_| read::Operation::Abs, ), ( &|x| x.op(constants::DW_OP_and), Operation::Simple(constants::DW_OP_and), &|_| read::Operation::And, ), ( &|x| x.op(constants::DW_OP_div), Operation::Simple(constants::DW_OP_div), &|_| read::Operation::Div, ), ( &|x| x.op(constants::DW_OP_minus), Operation::Simple(constants::DW_OP_minus), &|_| read::Operation::Minus, ), ( &|x| x.op(constants::DW_OP_mod), Operation::Simple(constants::DW_OP_mod), &|_| read::Operation::Mod, ), ( &|x| x.op(constants::DW_OP_mul), Operation::Simple(constants::DW_OP_mul), &|_| read::Operation::Mul, ), ( &|x| x.op(constants::DW_OP_neg), Operation::Simple(constants::DW_OP_neg), &|_| read::Operation::Neg, ), ( &|x| x.op(constants::DW_OP_not), Operation::Simple(constants::DW_OP_not), &|_| read::Operation::Not, ), ( &|x| x.op(constants::DW_OP_or), Operation::Simple(constants::DW_OP_or), &|_| read::Operation::Or, ), ( &|x| x.op(constants::DW_OP_plus), Operation::Simple(constants::DW_OP_plus), &|_| read::Operation::Plus, ), ( &|x| x.op_plus_uconst(23), Operation::PlusConstant(23), &|_| read::Operation::PlusConstant { value: 23 }, ), ( &|x| x.op(constants::DW_OP_shl), Operation::Simple(constants::DW_OP_shl), &|_| read::Operation::Shl, ), ( &|x| x.op(constants::DW_OP_shr), Operation::Simple(constants::DW_OP_shr), &|_| read::Operation::Shr, ), ( &|x| x.op(constants::DW_OP_shra), Operation::Simple(constants::DW_OP_shra), &|_| read::Operation::Shra, ), ( &|x| x.op(constants::DW_OP_xor), Operation::Simple(constants::DW_OP_xor), &|_| read::Operation::Xor, ), ( &|x| x.op(constants::DW_OP_eq), Operation::Simple(constants::DW_OP_eq), &|_| read::Operation::Eq, ), ( &|x| x.op(constants::DW_OP_ge), Operation::Simple(constants::DW_OP_ge), &|_| read::Operation::Ge, ), ( &|x| x.op(constants::DW_OP_gt), Operation::Simple(constants::DW_OP_gt), &|_| read::Operation::Gt, ), ( &|x| x.op(constants::DW_OP_le), Operation::Simple(constants::DW_OP_le), &|_| read::Operation::Le, ), ( &|x| x.op(constants::DW_OP_lt), Operation::Simple(constants::DW_OP_lt), &|_| read::Operation::Lt, ), ( &|x| x.op(constants::DW_OP_ne), Operation::Simple(constants::DW_OP_ne), &|_| read::Operation::Ne, ), ( &|x| x.op_constu(23), Operation::UnsignedConstant(23), &|_| read::Operation::UnsignedConstant { value: 23 }, ), ( &|x| x.op_consts(-23), Operation::SignedConstant(-23), &|_| read::Operation::SignedConstant { value: -23 }, ), ( &|x| x.op_reg(Register(23)), Operation::Register(Register(23)), &|_| read::Operation::Register { register: Register(23), }, ), ( &|x| x.op_reg(Register(123)), Operation::Register(Register(123)), &|_| read::Operation::Register { register: Register(123), }, ), ( &|x| x.op_breg(Register(23), 34), Operation::RegisterOffset(Register(23), 34), &|_| read::Operation::RegisterOffset { register: Register(23), offset: 34, base_type: read::UnitOffset(0), }, ), ( &|x| x.op_breg(Register(123), 34), Operation::RegisterOffset(Register(123), 34), &|_| read::Operation::RegisterOffset { register: Register(123), offset: 34, base_type: read::UnitOffset(0), }, ), ( &|x| x.op_regval_type(Register(23), entry_id), Operation::RegisterType(Register(23), entry_id), &|x| read::Operation::RegisterOffset { register: Register(23), offset: 0, base_type: x.entry_offset, }, ), (&|x| x.op_fbreg(34), Operation::FrameOffset(34), &|_| { read::Operation::FrameOffset { offset: 34 } }), ( &|x| x.op(constants::DW_OP_nop), Operation::Simple(constants::DW_OP_nop), &|_| read::Operation::Nop, ), ( &|x| x.op(constants::DW_OP_push_object_address), Operation::Simple(constants::DW_OP_push_object_address), &|_| read::Operation::PushObjectAddress, ), (&|x| x.op_call(entry_id), Operation::Call(entry_id), &|x| { read::Operation::Call { offset: read::DieReference::UnitRef(x.entry_offset), } }), ( &|x| x.op_call_ref(reference), Operation::CallRef(reference), &|x| read::Operation::Call { offset: read::DieReference::DebugInfoRef(x.debug_info_offset), }, ), ( &|x| x.op(constants::DW_OP_form_tls_address), Operation::Simple(constants::DW_OP_form_tls_address), &|_| read::Operation::TLS, ), ( &|x| x.op(constants::DW_OP_call_frame_cfa), Operation::Simple(constants::DW_OP_call_frame_cfa), &|_| read::Operation::CallFrameCFA, ), ( &|x| x.op_piece(23), Operation::Piece { size_in_bytes: 23 }, &|_| read::Operation::Piece { size_in_bits: 23 * 8, bit_offset: None, }, ), ( &|x| x.op_bit_piece(23, 34), Operation::BitPiece { size_in_bits: 23, bit_offset: 34, }, &|_| read::Operation::Piece { size_in_bits: 23, bit_offset: Some(34), }, ), ( &|x| x.op_implicit_value(vec![23].into()), Operation::ImplicitValue(vec![23].into()), &|_| read::Operation::ImplicitValue { data: read::EndianSlice::new(&[23], LittleEndian), }, ), ( &|x| x.op(constants::DW_OP_stack_value), Operation::Simple(constants::DW_OP_stack_value), &|_| read::Operation::StackValue, ), ( &|x| x.op_implicit_pointer(reference, 23), Operation::ImplicitPointer { entry: reference, byte_offset: 23, }, &|x| read::Operation::ImplicitPointer { value: x.debug_info_offset, byte_offset: 23, }, ), ( &|x| x.op_entry_value(reg_expression.clone()), Operation::EntryValue(reg_expression.clone()), &|_| read::Operation::EntryValue { expression: read::EndianSlice::new( &[constants::DW_OP_reg23.0], LittleEndian, ), }, ), ( &|x| x.op_gnu_parameter_ref(entry_id), Operation::ParameterRef(entry_id), &|x| read::Operation::ParameterRef { offset: x.entry_offset, }, ), ( &|x| x.op_addr(Address::Constant(23)), Operation::Address(Address::Constant(23)), &|_| read::Operation::Address { address: 23 }, ), ( &|x| x.op_const_type(entry_id, vec![23].into()), Operation::ConstantType(entry_id, vec![23].into()), &|x| read::Operation::TypedLiteral { base_type: x.entry_offset, value: read::EndianSlice::new(&[23], LittleEndian), }, ), (&|x| x.op_convert(None), Operation::Convert(None), &|_| { read::Operation::Convert { base_type: read::UnitOffset(0), } }), ( &|x| x.op_convert(Some(entry_id)), Operation::Convert(Some(entry_id)), &|x| read::Operation::Convert { base_type: x.entry_offset, }, ), ( &|x| x.op_reinterpret(None), Operation::Reinterpret(None), &|_| read::Operation::Reinterpret { base_type: read::UnitOffset(0), }, ), ( &|x| x.op_reinterpret(Some(entry_id)), Operation::Reinterpret(Some(entry_id)), &|x| read::Operation::Reinterpret { base_type: x.entry_offset, }, ), ( &|x| x.op_wasm_local(1000), Operation::WasmLocal(1000), &|_| read::Operation::WasmLocal { index: 1000 }, ), ( &|x| x.op_wasm_global(1000), Operation::WasmGlobal(1000), &|_| read::Operation::WasmGlobal { index: 1000 }, ), ( &|x| x.op_wasm_stack(1000), Operation::WasmStack(1000), &|_| read::Operation::WasmStack { index: 1000 }, ), ]; // Create a single expression containing all operations. let mut expression = Expression::new(); let start_index = expression.next_index(); for (f, o, _) in operations { f(&mut expression); assert_eq!(expression.operations.last(), Some(o)); } let bra_index = expression.op_bra(); let skip_index = expression.op_skip(); expression.op(constants::DW_OP_nop); let end_index = expression.next_index(); expression.set_target(bra_index, start_index); expression.set_target(skip_index, end_index); // Create an entry containing the expression. let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); let subprogram = unit.get_mut(subprogram_id); subprogram.set( constants::DW_AT_location, AttributeValue::Exprloc(expression), ); // Write the DWARF, then parse it. let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let mut read_units = read_dwarf.units(); let read_unit_header = read_units.next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit_header).unwrap(); let mut read_entries = read_unit.entries(); let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(read_entry.tag(), constants::DW_TAG_compile_unit); // Determine the offset of the entry that can be referenced by the expression. let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(read_entry.tag(), constants::DW_TAG_base_type); let read_state = ReadState { debug_info_offset: read_entry .offset() .to_debug_info_offset(&read_unit.header) .unwrap(), entry_offset: read_entry.offset(), }; // Get the expression. let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(read_entry.tag(), constants::DW_TAG_subprogram); let read_attr = read_entry .attr_value(constants::DW_AT_location) .unwrap() .unwrap(); let read_expression = read_attr.exprloc_value().unwrap(); let mut read_operations = read_expression.operations(encoding); for (_, _, operation) in operations { assert_eq!(read_operations.next(), Ok(Some(operation(&read_state)))); } // 4 = DW_OP_skip + i16 + DW_OP_nop assert_eq!( read_operations.next(), Ok(Some(read::Operation::Bra { target: -(read_expression.0.len() as i16) + 4 })) ); // 1 = DW_OP_nop assert_eq!( read_operations.next(), Ok(Some(read::Operation::Skip { target: 1 })) ); assert_eq!(read_operations.next(), Ok(Some(read::Operation::Nop))); assert_eq!(read_operations.next(), Ok(None)); let mut entry_ids = HashMap::new(); entry_ids.insert(read_state.debug_info_offset.into(), (unit_id, entry_id)); let convert_expression = Expression::from( read_expression, encoding, Some(&read_dwarf), Some(&read_unit), Some(&entry_ids), &|address| Some(Address::Constant(address)), ) .unwrap(); let mut convert_operations = convert_expression.operations.iter(); for (_, operation, _) in operations { assert_eq!(convert_operations.next(), Some(operation)); } assert_eq!( convert_operations.next(), Some(&Operation::Branch(start_index)) ); assert_eq!(convert_operations.next(), Some(&Operation::Skip(end_index))); assert_eq!( convert_operations.next(), Some(&Operation::Simple(constants::DW_OP_nop)) ); } } } } } gimli-0.31.1/src/write/range.rs000064400000000000000000000414111046102023000143730ustar 00000000000000use alloc::vec::Vec; use indexmap::IndexSet; use std::ops::{Deref, DerefMut}; use crate::common::{Encoding, RangeListsOffset, SectionId}; use crate::write::{Address, BaseId, Error, Result, Section, Sections, Writer}; define_section!( DebugRanges, RangeListsOffset, "A writable `.debug_ranges` section." ); define_section!( DebugRngLists, RangeListsOffset, "A writable `.debug_rnglists` section." ); define_offsets!( RangeListOffsets: RangeListId => RangeListsOffset, "The section offsets of a series of range lists within the `.debug_ranges` or `.debug_rnglists` sections." ); define_id!( RangeListId, "An identifier for a range list in a `RangeListTable`." ); /// A table of range lists that will be stored in a `.debug_ranges` or `.debug_rnglists` section. #[derive(Debug, Default)] pub struct RangeListTable { base_id: BaseId, ranges: IndexSet, } impl RangeListTable { /// Add a range list to the table. pub fn add(&mut self, range_list: RangeList) -> RangeListId { let (index, _) = self.ranges.insert_full(range_list); RangeListId::new(self.base_id, index) } /// Get a reference to a location list. /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pub fn get(&self, id: RangeListId) -> &RangeList { debug_assert_eq!(self.base_id, id.base_id); &self.ranges[id.index] } /// Write the range list table to the appropriate section for the given DWARF version. pub(crate) fn write( &self, sections: &mut Sections, encoding: Encoding, ) -> Result { if self.ranges.is_empty() { return Ok(RangeListOffsets::none()); } match encoding.version { 2..=4 => self.write_ranges(&mut sections.debug_ranges, encoding.address_size), 5 => self.write_rnglists(&mut sections.debug_rnglists, encoding), _ => Err(Error::UnsupportedVersion(encoding.version)), } } /// Write the range list table to the `.debug_ranges` section. fn write_ranges( &self, w: &mut DebugRanges, address_size: u8, ) -> Result { let mut offsets = Vec::new(); for range_list in self.ranges.iter() { offsets.push(w.offset()); for range in &range_list.0 { // Note that we must ensure none of the ranges have both begin == 0 and end == 0. // We do this by ensuring that begin != end, which is a bit more restrictive // than required, but still seems reasonable. match *range { Range::BaseAddress { address } => { let marker = !0 >> (64 - address_size * 8); w.write_udata(marker, address_size)?; w.write_address(address, address_size)?; } Range::OffsetPair { begin, end } => { if begin == end { return Err(Error::InvalidRange); } w.write_udata(begin, address_size)?; w.write_udata(end, address_size)?; } Range::StartEnd { begin, end } => { if begin == end { return Err(Error::InvalidRange); } w.write_address(begin, address_size)?; w.write_address(end, address_size)?; } Range::StartLength { begin, length } => { let end = match begin { Address::Constant(begin) => Address::Constant(begin + length), Address::Symbol { symbol, addend } => Address::Symbol { symbol, addend: addend + length as i64, }, }; if begin == end { return Err(Error::InvalidRange); } w.write_address(begin, address_size)?; w.write_address(end, address_size)?; } } } w.write_udata(0, address_size)?; w.write_udata(0, address_size)?; } Ok(RangeListOffsets { base_id: self.base_id, offsets, }) } /// Write the range list table to the `.debug_rnglists` section. fn write_rnglists( &self, w: &mut DebugRngLists, encoding: Encoding, ) -> Result { let mut offsets = Vec::new(); if encoding.version != 5 { return Err(Error::NeedVersion(5)); } let length_offset = w.write_initial_length(encoding.format)?; let length_base = w.len(); w.write_u16(encoding.version)?; w.write_u8(encoding.address_size)?; w.write_u8(0)?; // segment_selector_size w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list for range_list in self.ranges.iter() { offsets.push(w.offset()); for range in &range_list.0 { match *range { Range::BaseAddress { address } => { w.write_u8(crate::constants::DW_RLE_base_address.0)?; w.write_address(address, encoding.address_size)?; } Range::OffsetPair { begin, end } => { w.write_u8(crate::constants::DW_RLE_offset_pair.0)?; w.write_uleb128(begin)?; w.write_uleb128(end)?; } Range::StartEnd { begin, end } => { w.write_u8(crate::constants::DW_RLE_start_end.0)?; w.write_address(begin, encoding.address_size)?; w.write_address(end, encoding.address_size)?; } Range::StartLength { begin, length } => { w.write_u8(crate::constants::DW_RLE_start_length.0)?; w.write_address(begin, encoding.address_size)?; w.write_uleb128(length)?; } } } w.write_u8(crate::constants::DW_RLE_end_of_list.0)?; } let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, encoding.format)?; Ok(RangeListOffsets { base_id: self.base_id, offsets, }) } } /// A range list that will be stored in a `.debug_ranges` or `.debug_rnglists` section. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct RangeList(pub Vec); /// A single range. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum Range { /// DW_RLE_base_address BaseAddress { /// Base address. address: Address, }, /// DW_RLE_offset_pair OffsetPair { /// Start of range relative to base address. begin: u64, /// End of range relative to base address. end: u64, }, /// DW_RLE_start_end StartEnd { /// Start of range. begin: Address, /// End of range. end: Address, }, /// DW_RLE_start_length StartLength { /// Start of range. begin: Address, /// Length of range. length: u64, }, } #[cfg(feature = "read")] mod convert { use super::*; use crate::read::{self, Reader}; use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; impl RangeList { /// Create a range list by reading the data from the give range list iter. pub(crate) fn from>( mut from: read::RawRngListIter, context: &ConvertUnitContext<'_, R>, ) -> ConvertResult { let mut have_base_address = context.base_address != Address::Constant(0); let convert_address = |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); let mut ranges = Vec::new(); while let Some(from_range) = from.next()? { let range = match from_range { read::RawRngListEntry::AddressOrOffsetPair { begin, end } => { // These were parsed as addresses, even if they are offsets. let begin = convert_address(begin)?; let end = convert_address(end)?; match (begin, end) { (Address::Constant(begin_offset), Address::Constant(end_offset)) => { if have_base_address { Range::OffsetPair { begin: begin_offset, end: end_offset, } } else { Range::StartEnd { begin, end } } } _ => { if have_base_address { // At least one of begin/end is an address, but we also have // a base address. Adding addresses is undefined. return Err(ConvertError::InvalidRangeRelativeAddress); } Range::StartEnd { begin, end } } } } read::RawRngListEntry::BaseAddress { addr } => { have_base_address = true; let address = convert_address(addr)?; Range::BaseAddress { address } } read::RawRngListEntry::BaseAddressx { addr } => { have_base_address = true; let address = convert_address(context.dwarf.address(context.unit, addr)?)?; Range::BaseAddress { address } } read::RawRngListEntry::StartxEndx { begin, end } => { let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; let end = convert_address(context.dwarf.address(context.unit, end)?)?; Range::StartEnd { begin, end } } read::RawRngListEntry::StartxLength { begin, length } => { let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; Range::StartLength { begin, length } } read::RawRngListEntry::OffsetPair { begin, end } => { Range::OffsetPair { begin, end } } read::RawRngListEntry::StartEnd { begin, end } => { let begin = convert_address(begin)?; let end = convert_address(end)?; Range::StartEnd { begin, end } } read::RawRngListEntry::StartLength { begin, length } => { let begin = convert_address(begin)?; Range::StartLength { begin, length } } }; // Filtering empty ranges out. match range { Range::StartLength { length: 0, .. } => continue, Range::StartEnd { begin, end, .. } if begin == end => continue, Range::OffsetPair { begin, end, .. } if begin == end => continue, _ => (), } ranges.push(range); } Ok(RangeList(ranges)) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::common::{ DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, DebugStrOffsetsBase, Format, }; use crate::read; use crate::write::{ ConvertUnitContext, EndianVec, LineStringTable, LocationListTable, Range, RangeListTable, StringTable, }; use crate::LittleEndian; use std::collections::HashMap; use std::sync::Arc; #[test] fn test_range() { let mut line_strings = LineStringTable::default(); let mut strings = StringTable::default(); for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut range_list = RangeList(vec![ Range::StartLength { begin: Address::Constant(6666), length: 7777, }, Range::StartEnd { begin: Address::Constant(4444), end: Address::Constant(5555), }, Range::BaseAddress { address: Address::Constant(1111), }, Range::OffsetPair { begin: 2222, end: 3333, }, ]); let mut ranges = RangeListTable::default(); let range_list_id = ranges.add(range_list.clone()); let mut sections = Sections::new(EndianVec::new(LittleEndian)); let range_list_offsets = ranges.write(&mut sections, encoding).unwrap(); let read_debug_ranges = read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian); let read_debug_rnglists = read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian); let read_ranges = read::RangeLists::new(read_debug_ranges, read_debug_rnglists); let offset = range_list_offsets.get(range_list_id); let read_range_list = read_ranges.raw_ranges(offset, encoding).unwrap(); let dwarf = read::Dwarf { ranges: read_ranges, ..Default::default() }; let unit = read::Unit { header: read::UnitHeader::new( encoding, 0, read::UnitType::Compilation, DebugAbbrevOffset(0), DebugInfoOffset(0).into(), read::EndianSlice::default(), ), abbreviations: Arc::new(read::Abbreviations::default()), name: None, comp_dir: None, low_pc: 0, str_offsets_base: DebugStrOffsetsBase(0), addr_base: DebugAddrBase(0), loclists_base: DebugLocListsBase(0), rnglists_base: DebugRngListsBase(0), line_program: None, dwo_id: None, }; let context = ConvertUnitContext { dwarf: &dwarf, unit: &unit, line_strings: &mut line_strings, strings: &mut strings, ranges: &mut ranges, locations: &mut LocationListTable::default(), convert_address: &|address| Some(Address::Constant(address)), base_address: Address::Constant(0), line_program_offset: None, line_program_files: Vec::new(), entry_ids: &HashMap::new(), }; let convert_range_list = RangeList::from(read_range_list, &context).unwrap(); if version <= 4 { range_list.0[0] = Range::StartEnd { begin: Address::Constant(6666), end: Address::Constant(6666 + 7777), }; } assert_eq!(range_list, convert_range_list); } } } } } gimli-0.31.1/src/write/relocate.rs000064400000000000000000000212151046102023000150750ustar 00000000000000use crate::constants; use crate::write::{Address, Error, Result, Writer}; use crate::SectionId; /// A relocation to be applied to a section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Relocation { /// The offset within the section where the relocation should be applied. pub offset: usize, /// The size of the value to be relocated. pub size: u8, /// The target of the relocation. pub target: RelocationTarget, /// The addend to be applied to the relocated value. pub addend: i64, /// The pointer encoding for relocations in unwind information. pub eh_pe: Option, } /// The target of a relocation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RelocationTarget { /// The relocation target is a symbol. /// /// The meaning of this value is decided by the writer, but /// will typically be an index into a symbol table. Symbol(usize), /// The relocation target is a section. Section(SectionId), } /// A `Writer` which also records relocations. pub trait RelocateWriter { /// The type of the writer being used to write the section data. type Writer: Writer; /// Get the writer being used to write the section data. fn writer(&self) -> &Self::Writer; /// Get the writer being used to write the section data. fn writer_mut(&mut self) -> &mut Self::Writer; /// Record a relocation. fn relocate(&mut self, relocation: Relocation); } impl Writer for T { type Endian = <::Writer as Writer>::Endian; fn endian(&self) -> Self::Endian { self.writer().endian() } fn len(&self) -> usize { self.writer().len() } fn write(&mut self, bytes: &[u8]) -> Result<()> { self.writer_mut().write(bytes) } fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { self.writer_mut().write_at(offset, bytes) } fn write_address(&mut self, address: Address, size: u8) -> Result<()> { match address { Address::Constant(val) => self.writer_mut().write_udata(val, size), Address::Symbol { symbol, addend } => { self.relocate(Relocation { offset: self.len(), size, target: RelocationTarget::Symbol(symbol), addend, eh_pe: None, }); self.writer_mut().write_udata(0, size) } } } fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { self.relocate(Relocation { offset: self.len(), size, target: RelocationTarget::Section(section), addend: val as i64, eh_pe: None, }); self.writer_mut().write_udata(0, size) } fn write_offset_at( &mut self, offset: usize, val: usize, section: SectionId, size: u8, ) -> Result<()> { self.relocate(Relocation { offset, size, target: RelocationTarget::Section(section), addend: val as i64, eh_pe: None, }); self.writer_mut().write_udata_at(offset, 0, size) } fn write_eh_pointer( &mut self, address: Address, eh_pe: constants::DwEhPe, size: u8, ) -> Result<()> { match address { Address::Constant(_) => self.writer_mut().write_eh_pointer(address, eh_pe, size), Address::Symbol { symbol, addend } => { let size = match eh_pe.format() { constants::DW_EH_PE_absptr => size, constants::DW_EH_PE_udata2 => 2, constants::DW_EH_PE_udata4 => 4, constants::DW_EH_PE_udata8 => 8, constants::DW_EH_PE_sdata2 => 2, constants::DW_EH_PE_sdata4 => 4, constants::DW_EH_PE_sdata8 => 8, _ => return Err(Error::UnsupportedPointerEncoding(eh_pe)), }; self.relocate(Relocation { offset: self.len(), size, target: RelocationTarget::Symbol(symbol), addend, eh_pe: Some(eh_pe), }); self.writer_mut().write_udata(0, size) } } } } #[cfg(test)] mod tests { use super::*; use crate::write::EndianVec; use crate::{LittleEndian, SectionId}; use alloc::vec::Vec; struct Section { writer: EndianVec, relocations: Vec, } impl RelocateWriter for Section { type Writer = EndianVec; fn writer(&self) -> &Self::Writer { &self.writer } fn writer_mut(&mut self) -> &mut Self::Writer { &mut self.writer } fn relocate(&mut self, relocation: Relocation) { self.relocations.push(relocation); } } #[test] fn test_relocate_writer() { let mut expected_data = Vec::new(); let mut expected_relocations = Vec::new(); let mut section = Section { writer: EndianVec::new(LittleEndian), relocations: Vec::new(), }; // No relocation for plain data. section.write_udata(0x12345678, 4).unwrap(); expected_data.extend_from_slice(&0x12345678u32.to_le_bytes()); // No relocation for a constant address. section .write_address(Address::Constant(0x87654321), 4) .unwrap(); expected_data.extend_from_slice(&0x87654321u32.to_le_bytes()); // Relocation for a symbol address. let offset = section.len(); section .write_address( Address::Symbol { symbol: 1, addend: 0x12345678, }, 4, ) .unwrap(); expected_data.extend_from_slice(&[0; 4]); expected_relocations.push(Relocation { offset, size: 4, target: RelocationTarget::Symbol(1), addend: 0x12345678, eh_pe: None, }); // Relocation for a section offset. let offset = section.len(); section .write_offset(0x12345678, SectionId::DebugAbbrev, 4) .unwrap(); expected_data.extend_from_slice(&[0; 4]); expected_relocations.push(Relocation { offset, size: 4, target: RelocationTarget::Section(SectionId::DebugAbbrev), addend: 0x12345678, eh_pe: None, }); // Relocation for a section offset at a specific offset. let offset = section.len(); section.write_udata(0x12345678, 4).unwrap(); section .write_offset_at(offset, 0x12345678, SectionId::DebugStr, 4) .unwrap(); expected_data.extend_from_slice(&[0; 4]); expected_relocations.push(Relocation { offset, size: 4, target: RelocationTarget::Section(SectionId::DebugStr), addend: 0x12345678, eh_pe: None, }); // No relocation for a constant in unwind information. section .write_eh_pointer(Address::Constant(0x87654321), constants::DW_EH_PE_absptr, 8) .unwrap(); expected_data.extend_from_slice(&0x87654321u64.to_le_bytes()); // No relocation for a relative constant in unwind information. let offset = section.len(); section .write_eh_pointer( Address::Constant(offset as u64 - 8), constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, 8, ) .unwrap(); expected_data.extend_from_slice(&(-8i32).to_le_bytes()); // Relocation for a symbol in unwind information. let offset = section.len(); section .write_eh_pointer( Address::Symbol { symbol: 2, addend: 0x12345678, }, constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, 8, ) .unwrap(); expected_data.extend_from_slice(&[0; 4]); expected_relocations.push(Relocation { offset, size: 4, target: RelocationTarget::Symbol(2), addend: 0x12345678, eh_pe: Some(constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4), }); assert_eq!(section.writer.into_vec(), expected_data); assert_eq!(section.relocations, expected_relocations); } } gimli-0.31.1/src/write/section.rs000064400000000000000000000170661046102023000147540ustar 00000000000000use std::ops::DerefMut; use std::result; use std::vec::Vec; use crate::common::SectionId; use crate::write::{ DebugAbbrev, DebugFrame, DebugInfo, DebugInfoReference, DebugLine, DebugLineStr, DebugLoc, DebugLocLists, DebugRanges, DebugRngLists, DebugStr, EhFrame, Writer, }; macro_rules! define_section { ($name:ident, $offset:ident, $docs:expr) => { #[doc=$docs] #[derive(Debug, Default)] pub struct $name(pub W); impl $name { /// Return the offset of the next write. pub fn offset(&self) -> $offset { $offset(self.len()) } } impl From for $name { #[inline] fn from(w: W) -> Self { $name(w) } } impl Deref for $name { type Target = W; #[inline] fn deref(&self) -> &W { &self.0 } } impl DerefMut for $name { #[inline] fn deref_mut(&mut self) -> &mut W { &mut self.0 } } impl Section for $name { #[inline] fn id(&self) -> SectionId { SectionId::$name } } }; } /// Functionality common to all writable DWARF sections. pub trait Section: DerefMut { /// Returns the DWARF section kind for this type. fn id(&self) -> SectionId; /// Returns the ELF section name for this type. fn name(&self) -> &'static str { self.id().name() } } /// All of the writable DWARF sections. #[derive(Debug, Default)] pub struct Sections { /// The `.debug_abbrev` section. pub debug_abbrev: DebugAbbrev, /// The `.debug_info` section. pub debug_info: DebugInfo, /// The `.debug_line` section. pub debug_line: DebugLine, /// The `.debug_line_str` section. pub debug_line_str: DebugLineStr, /// The `.debug_ranges` section. pub debug_ranges: DebugRanges, /// The `.debug_rnglists` section. pub debug_rnglists: DebugRngLists, /// The `.debug_loc` section. pub debug_loc: DebugLoc, /// The `.debug_loclists` section. pub debug_loclists: DebugLocLists, /// The `.debug_str` section. pub debug_str: DebugStr, /// The `.debug_frame` section. pub debug_frame: DebugFrame, /// The `.eh_frame` section. pub eh_frame: EhFrame, /// Unresolved references in the `.debug_info` section. pub(crate) debug_info_refs: Vec, /// Unresolved references in the `.debug_loc` section. pub(crate) debug_loc_refs: Vec, /// Unresolved references in the `.debug_loclists` section. pub(crate) debug_loclists_refs: Vec, } impl Sections { /// Create a new `Sections` using clones of the given `section`. pub fn new(section: W) -> Self { Sections { debug_abbrev: DebugAbbrev(section.clone()), debug_info: DebugInfo(section.clone()), debug_line: DebugLine(section.clone()), debug_line_str: DebugLineStr(section.clone()), debug_ranges: DebugRanges(section.clone()), debug_rnglists: DebugRngLists(section.clone()), debug_loc: DebugLoc(section.clone()), debug_loclists: DebugLocLists(section.clone()), debug_str: DebugStr(section.clone()), debug_frame: DebugFrame(section.clone()), eh_frame: EhFrame(section), debug_info_refs: Vec::new(), debug_loc_refs: Vec::new(), debug_loclists_refs: Vec::new(), } } } impl Sections { /// Get the section with the given `id`. pub fn get(&self, id: SectionId) -> Option<&W> { match id { SectionId::DebugAbbrev => Some(&self.debug_abbrev.0), SectionId::DebugInfo => Some(&self.debug_info.0), SectionId::DebugLine => Some(&self.debug_line.0), SectionId::DebugLineStr => Some(&self.debug_line_str.0), SectionId::DebugRanges => Some(&self.debug_ranges.0), SectionId::DebugRngLists => Some(&self.debug_rnglists.0), SectionId::DebugLoc => Some(&self.debug_loc.0), SectionId::DebugLocLists => Some(&self.debug_loclists.0), SectionId::DebugStr => Some(&self.debug_str.0), SectionId::DebugFrame => Some(&self.debug_frame.0), SectionId::EhFrame => Some(&self.eh_frame.0), _ => None, } } /// Get the section with the given `id`. pub fn get_mut(&mut self, id: SectionId) -> Option<&mut W> { match id { SectionId::DebugAbbrev => Some(&mut self.debug_abbrev.0), SectionId::DebugInfo => Some(&mut self.debug_info.0), SectionId::DebugLine => Some(&mut self.debug_line.0), SectionId::DebugLineStr => Some(&mut self.debug_line_str.0), SectionId::DebugRanges => Some(&mut self.debug_ranges.0), SectionId::DebugRngLists => Some(&mut self.debug_rnglists.0), SectionId::DebugLoc => Some(&mut self.debug_loc.0), SectionId::DebugLocLists => Some(&mut self.debug_loclists.0), SectionId::DebugStr => Some(&mut self.debug_str.0), SectionId::DebugFrame => Some(&mut self.debug_frame.0), SectionId::EhFrame => Some(&mut self.eh_frame.0), _ => None, } } /// For each section, call `f` once with a shared reference. pub fn for_each<'a, F, E>(&'a self, mut f: F) -> result::Result<(), E> where F: FnMut(SectionId, &'a W) -> result::Result<(), E>, { macro_rules! f { ($s:expr) => { f($s.id(), &$s) }; } // Ordered so that earlier sections do not reference later sections. f!(self.debug_abbrev)?; f!(self.debug_str)?; f!(self.debug_line_str)?; f!(self.debug_line)?; f!(self.debug_ranges)?; f!(self.debug_rnglists)?; f!(self.debug_loc)?; f!(self.debug_loclists)?; f!(self.debug_info)?; f!(self.debug_frame)?; f!(self.eh_frame)?; Ok(()) } /// For each section, call `f` once with a mutable reference. pub fn for_each_mut<'a, F, E>(&'a mut self, mut f: F) -> result::Result<(), E> where F: FnMut(SectionId, &'a mut W) -> result::Result<(), E>, { macro_rules! f { ($s:expr) => { f($s.id(), &mut $s) }; } // Ordered so that earlier sections do not reference later sections. f!(self.debug_abbrev)?; f!(self.debug_str)?; f!(self.debug_line_str)?; f!(self.debug_line)?; f!(self.debug_ranges)?; f!(self.debug_rnglists)?; f!(self.debug_loc)?; f!(self.debug_loclists)?; f!(self.debug_info)?; f!(self.debug_frame)?; f!(self.eh_frame)?; Ok(()) } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::{read, write::EndianVec, Endianity}; impl Sections> { pub(crate) fn read(&self, endian: E) -> read::Dwarf> { read::Dwarf::load(|section_id| -> read::Result<_> { Ok(read::EndianSlice::new( self.get(section_id).map(|w| w.slice()).unwrap_or_default(), endian, )) }) .unwrap() } } } gimli-0.31.1/src/write/str.rs000064400000000000000000000127411046102023000141130ustar 00000000000000use alloc::vec::Vec; use indexmap::IndexSet; use std::ops::{Deref, DerefMut}; use crate::common::{DebugLineStrOffset, DebugStrOffset, SectionId}; use crate::write::{BaseId, Result, Section, Writer}; // Requirements: // - values are `[u8]`, null bytes are not allowed // - insertion returns a fixed id // - inserting a duplicate returns the id of the existing value // - able to convert an id to a section offset // Optional? // - able to get an existing value given an id // // Limitations of current implementation (using IndexSet): // - inserting requires either an allocation for duplicates, // or a double lookup for non-duplicates // - doesn't preserve offsets when updating an existing `.debug_str` section // // Possible changes: // - calculate offsets as we add values, and use that as the id. // This would avoid the need for DebugStrOffsets but would make it // hard to implement `get`. macro_rules! define_string_table { ($name:ident, $id:ident, $section:ident, $offsets:ident, $docs:expr) => { #[doc=$docs] #[derive(Debug, Default)] pub struct $name { base_id: BaseId, strings: IndexSet>, } impl $name { /// Add a string to the string table and return its id. /// /// If the string already exists, then return the id of the existing string. /// /// # Panics /// /// Panics if `bytes` contains a null byte. pub fn add(&mut self, bytes: T) -> $id where T: Into>, { let bytes = bytes.into(); assert!(!bytes.contains(&0)); let (index, _) = self.strings.insert_full(bytes); $id::new(self.base_id, index) } /// Return the number of strings in the table. #[inline] pub fn count(&self) -> usize { self.strings.len() } /// Get a reference to a string in the table. /// /// # Panics /// /// Panics if `id` is invalid. pub fn get(&self, id: $id) -> &[u8] { debug_assert_eq!(self.base_id, id.base_id); self.strings.get_index(id.index).map(Vec::as_slice).unwrap() } /// Write the string table to the `.debug_str` section. /// /// Returns the offsets at which the strings are written. pub fn write(&self, w: &mut $section) -> Result<$offsets> { let mut offsets = Vec::new(); for bytes in self.strings.iter() { offsets.push(w.offset()); w.write(bytes)?; w.write_u8(0)?; } Ok($offsets { base_id: self.base_id, offsets, }) } } }; } define_id!(StringId, "An identifier for a string in a `StringTable`."); define_string_table!( StringTable, StringId, DebugStr, DebugStrOffsets, "A table of strings that will be stored in a `.debug_str` section." ); define_section!(DebugStr, DebugStrOffset, "A writable `.debug_str` section."); define_offsets!( DebugStrOffsets: StringId => DebugStrOffset, "The section offsets of all strings within a `.debug_str` section." ); define_id!( LineStringId, "An identifier for a string in a `LineStringTable`." ); define_string_table!( LineStringTable, LineStringId, DebugLineStr, DebugLineStrOffsets, "A table of strings that will be stored in a `.debug_line_str` section." ); define_section!( DebugLineStr, DebugLineStrOffset, "A writable `.debug_line_str` section." ); define_offsets!( DebugLineStrOffsets: LineStringId => DebugLineStrOffset, "The section offsets of all strings within a `.debug_line_str` section." ); #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::read; use crate::write::EndianVec; use crate::LittleEndian; #[test] fn test_string_table() { let mut strings = StringTable::default(); assert_eq!(strings.count(), 0); let id1 = strings.add(&b"one"[..]); let id2 = strings.add(&b"two"[..]); assert_eq!(strings.add(&b"one"[..]), id1); assert_eq!(strings.add(&b"two"[..]), id2); assert_eq!(strings.get(id1), &b"one"[..]); assert_eq!(strings.get(id2), &b"two"[..]); assert_eq!(strings.count(), 2); let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); let offsets = strings.write(&mut debug_str).unwrap(); assert_eq!(debug_str.slice(), b"one\0two\0"); assert_eq!(offsets.get(id1), DebugStrOffset(0)); assert_eq!(offsets.get(id2), DebugStrOffset(4)); assert_eq!(offsets.count(), 2); } #[test] fn test_string_table_read() { let mut strings = StringTable::default(); let id1 = strings.add(&b"one"[..]); let id2 = strings.add(&b"two"[..]); let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); let offsets = strings.write(&mut debug_str).unwrap(); let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian); let str1 = read_debug_str.get_str(offsets.get(id1)).unwrap(); let str2 = read_debug_str.get_str(offsets.get(id2)).unwrap(); assert_eq!(str1.slice(), &b"one"[..]); assert_eq!(str2.slice(), &b"two"[..]); } } gimli-0.31.1/src/write/unit.rs000064400000000000000000003544361046102023000142740ustar 00000000000000use alloc::vec::Vec; use std::ops::{Deref, DerefMut}; use std::slice; use crate::common::{ DebugAbbrevOffset, DebugInfoOffset, DebugLineOffset, DebugMacinfoOffset, DebugMacroOffset, DebugStrOffset, DebugTypeSignature, Encoding, Format, SectionId, }; use crate::constants; use crate::leb128::write::{sleb128_size, uleb128_size}; use crate::write::{ Abbreviation, AbbreviationTable, Address, AttributeSpecification, BaseId, DebugLineStrOffsets, DebugStrOffsets, Error, Expression, FileId, LineProgram, LineStringId, LocationListId, LocationListOffsets, LocationListTable, RangeListId, RangeListOffsets, RangeListTable, Reference, Result, Section, Sections, StringId, Writer, }; define_id!(UnitId, "An identifier for a unit in a `UnitTable`."); define_id!(UnitEntryId, "An identifier for an entry in a `Unit`."); /// A table of units that will be stored in the `.debug_info` section. #[derive(Debug, Default)] pub struct UnitTable { base_id: BaseId, units: Vec, } impl UnitTable { /// Create a new unit and add it to the table. /// /// `address_size` must be in bytes. /// /// Returns the `UnitId` of the new unit. #[inline] pub fn add(&mut self, unit: Unit) -> UnitId { let id = UnitId::new(self.base_id, self.units.len()); self.units.push(unit); id } /// Return the number of units. #[inline] pub fn count(&self) -> usize { self.units.len() } /// Return the id of a unit. /// /// # Panics /// /// Panics if `index >= self.count()`. #[inline] pub fn id(&self, index: usize) -> UnitId { assert!(index < self.count()); UnitId::new(self.base_id, index) } /// Get a reference to a unit. /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pub fn get(&self, id: UnitId) -> &Unit { debug_assert_eq!(self.base_id, id.base_id); &self.units[id.index] } /// Get a mutable reference to a unit. /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pub fn get_mut(&mut self, id: UnitId) -> &mut Unit { debug_assert_eq!(self.base_id, id.base_id); &mut self.units[id.index] } /// Get an iterator for the units. pub fn iter(&self) -> impl Iterator { self.units .iter() .enumerate() .map(move |(index, unit)| (UnitId::new(self.base_id, index), unit)) } /// Get a mutable iterator for the units. pub fn iter_mut(&mut self) -> impl Iterator { let base_id = self.base_id; self.units .iter_mut() .enumerate() .map(move |(index, unit)| (UnitId::new(base_id, index), unit)) } /// Write the units to the given sections. /// /// `strings` must contain the `.debug_str` offsets of the corresponding /// `StringTable`. pub fn write( &mut self, sections: &mut Sections, line_strings: &DebugLineStrOffsets, strings: &DebugStrOffsets, ) -> Result { let mut offsets = DebugInfoOffsets { base_id: self.base_id, units: Vec::new(), }; for unit in &mut self.units { // TODO: maybe share abbreviation tables let abbrev_offset = sections.debug_abbrev.offset(); let mut abbrevs = AbbreviationTable::default(); offsets.units.push(unit.write( sections, abbrev_offset, &mut abbrevs, line_strings, strings, )?); abbrevs.write(&mut sections.debug_abbrev)?; } write_section_refs( &mut sections.debug_info_refs, &mut sections.debug_info.0, &offsets, )?; write_section_refs( &mut sections.debug_loc_refs, &mut sections.debug_loc.0, &offsets, )?; write_section_refs( &mut sections.debug_loclists_refs, &mut sections.debug_loclists.0, &offsets, )?; Ok(offsets) } } fn write_section_refs( references: &mut Vec, w: &mut W, offsets: &DebugInfoOffsets, ) -> Result<()> { for r in references.drain(..) { let entry_offset = offsets.entry(r.unit, r.entry).0; debug_assert_ne!(entry_offset, 0); w.write_offset_at(r.offset, entry_offset, SectionId::DebugInfo, r.size)?; } Ok(()) } /// A unit's debugging information. #[derive(Debug)] pub struct Unit { base_id: BaseId, /// The encoding parameters for this unit. encoding: Encoding, /// The line number program for this unit. pub line_program: LineProgram, /// A table of range lists used by this unit. pub ranges: RangeListTable, /// A table of location lists used by this unit. pub locations: LocationListTable, /// All entries in this unit. The order is unrelated to the tree order. // Requirements: // - entries form a tree // - entries can be added in any order // - entries have a fixed id // - able to quickly lookup an entry from its id // Limitations of current implementation: // - mutable iteration of children is messy due to borrow checker entries: Vec, /// The index of the root entry in entries. root: UnitEntryId, } impl Unit { /// Create a new `Unit`. pub fn new(encoding: Encoding, line_program: LineProgram) -> Self { let base_id = BaseId::default(); let ranges = RangeListTable::default(); let locations = LocationListTable::default(); let mut entries = Vec::new(); let root = DebuggingInformationEntry::new( base_id, &mut entries, None, constants::DW_TAG_compile_unit, ); Unit { base_id, encoding, line_program, ranges, locations, entries, root, } } /// Return the encoding parameters for this unit. #[inline] pub fn encoding(&self) -> Encoding { self.encoding } /// Return the DWARF version for this unit. #[inline] pub fn version(&self) -> u16 { self.encoding.version } /// Return the address size in bytes for this unit. #[inline] pub fn address_size(&self) -> u8 { self.encoding.address_size } /// Return the DWARF format for this unit. #[inline] pub fn format(&self) -> Format { self.encoding.format } /// Return the number of `DebuggingInformationEntry`s created for this unit. /// /// This includes entries that no longer have a parent. #[inline] pub fn count(&self) -> usize { self.entries.len() } /// Return the id of the root entry. #[inline] pub fn root(&self) -> UnitEntryId { self.root } /// Add a new `DebuggingInformationEntry` to this unit and return its id. /// /// The `parent` must be within the same unit. /// /// # Panics /// /// Panics if `parent` is invalid. #[inline] pub fn add(&mut self, parent: UnitEntryId, tag: constants::DwTag) -> UnitEntryId { debug_assert_eq!(self.base_id, parent.base_id); DebuggingInformationEntry::new(self.base_id, &mut self.entries, Some(parent), tag) } /// Get a reference to an entry. /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pub fn get(&self, id: UnitEntryId) -> &DebuggingInformationEntry { debug_assert_eq!(self.base_id, id.base_id); &self.entries[id.index] } /// Get a mutable reference to an entry. /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pub fn get_mut(&mut self, id: UnitEntryId) -> &mut DebuggingInformationEntry { debug_assert_eq!(self.base_id, id.base_id); &mut self.entries[id.index] } /// Return true if `self.line_program` is used by a DIE. fn line_program_in_use(&self) -> bool { if self.line_program.is_none() { return false; } if !self.line_program.is_empty() { return true; } for entry in &self.entries { for attr in &entry.attrs { if let AttributeValue::FileIndex(Some(_)) = attr.value { return true; } } } false } /// Write the unit to the given sections. pub(crate) fn write( &mut self, sections: &mut Sections, abbrev_offset: DebugAbbrevOffset, abbrevs: &mut AbbreviationTable, line_strings: &DebugLineStrOffsets, strings: &DebugStrOffsets, ) -> Result { let line_program = if self.line_program_in_use() { self.entries[self.root.index] .set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); Some(self.line_program.write( &mut sections.debug_line, self.encoding, line_strings, strings, )?) } else { self.entries[self.root.index].delete(constants::DW_AT_stmt_list); None }; // TODO: use .debug_types for type units in DWARF v4. let w = &mut sections.debug_info; let mut offsets = UnitOffsets { base_id: self.base_id, unit: w.offset(), // Entries can be written in any order, so create the complete vec now. entries: vec![EntryOffset::none(); self.entries.len()], }; let length_offset = w.write_initial_length(self.format())?; let length_base = w.len(); w.write_u16(self.version())?; if 2 <= self.version() && self.version() <= 4 { w.write_offset( abbrev_offset.0, SectionId::DebugAbbrev, self.format().word_size(), )?; w.write_u8(self.address_size())?; } else if self.version() == 5 { w.write_u8(constants::DW_UT_compile.0)?; w.write_u8(self.address_size())?; w.write_offset( abbrev_offset.0, SectionId::DebugAbbrev, self.format().word_size(), )?; } else { return Err(Error::UnsupportedVersion(self.version())); } // Calculate all DIE offsets, so that we are able to output references to them. // However, references to base types in expressions use ULEB128, so base types // must be moved to the front before we can calculate offsets. self.reorder_base_types(); let mut offset = w.len(); self.entries[self.root.index].calculate_offsets( self, &mut offset, &mut offsets, abbrevs, )?; let range_lists = self.ranges.write(sections, self.encoding)?; // Location lists can't be written until we have DIE offsets. let loc_lists = self .locations .write(sections, self.encoding, Some(&offsets))?; let w = &mut sections.debug_info; let mut unit_refs = Vec::new(); self.entries[self.root.index].write( w, &mut sections.debug_info_refs, &mut unit_refs, self, &mut offsets, line_program, line_strings, strings, &range_lists, &loc_lists, )?; let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, self.format())?; for (offset, entry) in unit_refs { // This does not need relocation. w.write_udata_at( offset.0, offsets.unit_offset(entry), self.format().word_size(), )?; } Ok(offsets) } /// Reorder base types to come first so that typed stack operations /// can get their offset. fn reorder_base_types(&mut self) { let root = &self.entries[self.root.index]; let mut root_children = Vec::with_capacity(root.children.len()); for entry in &root.children { if self.entries[entry.index].tag == constants::DW_TAG_base_type { root_children.push(*entry); } } for entry in &root.children { if self.entries[entry.index].tag != constants::DW_TAG_base_type { root_children.push(*entry); } } self.entries[self.root.index].children = root_children; } } /// A Debugging Information Entry (DIE). /// /// DIEs have a set of attributes and optionally have children DIEs as well. /// /// DIEs form a tree without any cycles. This is enforced by specifying the /// parent when creating a DIE, and disallowing changes of parent. #[derive(Debug)] pub struct DebuggingInformationEntry { id: UnitEntryId, parent: Option, tag: constants::DwTag, /// Whether to emit `DW_AT_sibling`. sibling: bool, attrs: Vec, children: Vec, } impl DebuggingInformationEntry { /// Create a new `DebuggingInformationEntry`. /// /// # Panics /// /// Panics if `parent` is invalid. #[allow(clippy::new_ret_no_self)] fn new( base_id: BaseId, entries: &mut Vec, parent: Option, tag: constants::DwTag, ) -> UnitEntryId { let id = UnitEntryId::new(base_id, entries.len()); entries.push(DebuggingInformationEntry { id, parent, tag, sibling: false, attrs: Vec::new(), children: Vec::new(), }); if let Some(parent) = parent { debug_assert_eq!(base_id, parent.base_id); assert_ne!(parent, id); entries[parent.index].children.push(id); } id } /// Return the id of this entry. #[inline] pub fn id(&self) -> UnitEntryId { self.id } /// Return the parent of this entry. #[inline] pub fn parent(&self) -> Option { self.parent } /// Return the tag of this entry. #[inline] pub fn tag(&self) -> constants::DwTag { self.tag } /// Return `true` if a `DW_AT_sibling` attribute will be emitted. #[inline] pub fn sibling(&self) -> bool { self.sibling } /// Set whether a `DW_AT_sibling` attribute will be emitted. /// /// The attribute will only be emitted if the DIE has children. #[inline] pub fn set_sibling(&mut self, sibling: bool) { self.sibling = sibling; } /// Iterate over the attributes of this entry. #[inline] pub fn attrs(&self) -> slice::Iter<'_, Attribute> { self.attrs.iter() } /// Iterate over the attributes of this entry for modification. #[inline] pub fn attrs_mut(&mut self) -> slice::IterMut<'_, Attribute> { self.attrs.iter_mut() } /// Get an attribute. pub fn get(&self, name: constants::DwAt) -> Option<&AttributeValue> { self.attrs .iter() .find(|attr| attr.name == name) .map(|attr| &attr.value) } /// Get an attribute for modification. pub fn get_mut(&mut self, name: constants::DwAt) -> Option<&mut AttributeValue> { self.attrs .iter_mut() .find(|attr| attr.name == name) .map(|attr| &mut attr.value) } /// Set an attribute. /// /// Replaces any existing attribute with the same name. /// /// # Panics /// /// Panics if `name` is `DW_AT_sibling`. Use `set_sibling` instead. pub fn set(&mut self, name: constants::DwAt, value: AttributeValue) { assert_ne!(name, constants::DW_AT_sibling); if let Some(attr) = self.attrs.iter_mut().find(|attr| attr.name == name) { attr.value = value; return; } self.attrs.push(Attribute { name, value }); } /// Delete an attribute. /// /// Replaces any existing attribute with the same name. pub fn delete(&mut self, name: constants::DwAt) { self.attrs.retain(|x| x.name != name); } /// Iterate over the children of this entry. /// /// Note: use `Unit::add` to add a new child to this entry. #[inline] pub fn children(&self) -> slice::Iter<'_, UnitEntryId> { self.children.iter() } /// Delete a child entry and all of its children. pub fn delete_child(&mut self, id: UnitEntryId) { self.children.retain(|&child| child != id); } /// Return the type abbreviation for this DIE. fn abbreviation(&self, encoding: Encoding) -> Result { let mut attrs = Vec::new(); if self.sibling && !self.children.is_empty() { let form = match encoding.format { Format::Dwarf32 => constants::DW_FORM_ref4, Format::Dwarf64 => constants::DW_FORM_ref8, }; attrs.push(AttributeSpecification::new(constants::DW_AT_sibling, form)); } for attr in &self.attrs { attrs.push(attr.specification(encoding)?); } Ok(Abbreviation::new( self.tag, !self.children.is_empty(), attrs, )) } fn calculate_offsets( &self, unit: &Unit, offset: &mut usize, offsets: &mut UnitOffsets, abbrevs: &mut AbbreviationTable, ) -> Result<()> { offsets.entries[self.id.index].offset = DebugInfoOffset(*offset); offsets.entries[self.id.index].abbrev = abbrevs.add(self.abbreviation(unit.encoding())?); *offset += self.size(unit, offsets); if !self.children.is_empty() { for child in &self.children { unit.entries[child.index].calculate_offsets(unit, offset, offsets, abbrevs)?; } // Null child *offset += 1; } Ok(()) } fn size(&self, unit: &Unit, offsets: &UnitOffsets) -> usize { let mut size = uleb128_size(offsets.abbrev(self.id)); if self.sibling && !self.children.is_empty() { size += unit.format().word_size() as usize; } for attr in &self.attrs { size += attr.value.size(unit, offsets); } size } /// Write the entry to the given sections. fn write( &self, w: &mut DebugInfo, debug_info_refs: &mut Vec, unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, unit: &Unit, offsets: &mut UnitOffsets, line_program: Option, line_strings: &DebugLineStrOffsets, strings: &DebugStrOffsets, range_lists: &RangeListOffsets, loc_lists: &LocationListOffsets, ) -> Result<()> { debug_assert_eq!(offsets.debug_info_offset(self.id), w.offset()); w.write_uleb128(offsets.abbrev(self.id))?; let sibling_offset = if self.sibling && !self.children.is_empty() { let offset = w.offset(); w.write_udata(0, unit.format().word_size())?; Some(offset) } else { None }; for attr in &self.attrs { attr.value.write( w, debug_info_refs, unit_refs, unit, offsets, line_program, line_strings, strings, range_lists, loc_lists, )?; } if !self.children.is_empty() { for child in &self.children { unit.entries[child.index].write( w, debug_info_refs, unit_refs, unit, offsets, line_program, line_strings, strings, range_lists, loc_lists, )?; } // Null child w.write_u8(0)?; } if let Some(offset) = sibling_offset { let next_offset = (w.offset().0 - offsets.unit.0) as u64; // This does not need relocation. w.write_udata_at(offset.0, next_offset, unit.format().word_size())?; } Ok(()) } } /// An attribute in a `DebuggingInformationEntry`, consisting of a name and /// associated value. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Attribute { name: constants::DwAt, value: AttributeValue, } impl Attribute { /// Get the name of this attribute. #[inline] pub fn name(&self) -> constants::DwAt { self.name } /// Get the value of this attribute. #[inline] pub fn get(&self) -> &AttributeValue { &self.value } /// Set the value of this attribute. #[inline] pub fn set(&mut self, value: AttributeValue) { self.value = value; } /// Return the type specification for this attribute. fn specification(&self, encoding: Encoding) -> Result { Ok(AttributeSpecification::new( self.name, self.value.form(encoding)?, )) } } /// The value of an attribute in a `DebuggingInformationEntry`. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AttributeValue { /// "Refers to some location in the address space of the described program." Address(Address), /// A slice of an arbitrary number of bytes. Block(Vec), /// A one byte constant data value. How to interpret the byte depends on context. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data1(u8), /// A two byte constant data value. How to interpret the bytes depends on context. /// /// This value will be converted to the target endian before writing. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data2(u16), /// A four byte constant data value. How to interpret the bytes depends on context. /// /// This value will be converted to the target endian before writing. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data4(u32), /// An eight byte constant data value. How to interpret the bytes depends on context. /// /// This value will be converted to the target endian before writing. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data8(u64), /// A signed integer constant. Sdata(i64), /// An unsigned integer constant. Udata(u64), /// "The information bytes contain a DWARF expression (see Section 2.5) or /// location description (see Section 2.6)." Exprloc(Expression), /// A boolean that indicates presence or absence of the attribute. Flag(bool), /// An attribute that is always present. FlagPresent, /// A reference to a `DebuggingInformationEntry` in this unit. UnitRef(UnitEntryId), /// A reference to a `DebuggingInformationEntry` in a potentially different unit. DebugInfoRef(Reference), /// An offset into the `.debug_info` section of the supplementary object file. /// /// The API does not currently assist with generating this offset. /// This variant will be removed from the API once support for writing /// supplementary object files is implemented. DebugInfoRefSup(DebugInfoOffset), /// A reference to a line number program. LineProgramRef, /// A reference to a location list. LocationListRef(LocationListId), /// An offset into the `.debug_macinfo` section. /// /// The API does not currently assist with generating this offset. /// This variant will be removed from the API once support for writing /// `.debug_macinfo` sections is implemented. DebugMacinfoRef(DebugMacinfoOffset), /// An offset into the `.debug_macro` section. /// /// The API does not currently assist with generating this offset. /// This variant will be removed from the API once support for writing /// `.debug_macro` sections is implemented. DebugMacroRef(DebugMacroOffset), /// A reference to a range list. RangeListRef(RangeListId), /// A type signature. /// /// The API does not currently assist with generating this signature. /// This variant will be removed from the API once support for writing /// `.debug_types` sections is implemented. DebugTypesRef(DebugTypeSignature), /// A reference to a string in the `.debug_str` section. StringRef(StringId), /// An offset into the `.debug_str` section of the supplementary object file. /// /// The API does not currently assist with generating this offset. /// This variant will be removed from the API once support for writing /// supplementary object files is implemented. DebugStrRefSup(DebugStrOffset), /// A reference to a string in the `.debug_line_str` section. LineStringRef(LineStringId), /// A slice of bytes representing a string. Must not include null bytes. /// Not guaranteed to be UTF-8 or anything like that. String(Vec), /// The value of a `DW_AT_encoding` attribute. Encoding(constants::DwAte), /// The value of a `DW_AT_decimal_sign` attribute. DecimalSign(constants::DwDs), /// The value of a `DW_AT_endianity` attribute. Endianity(constants::DwEnd), /// The value of a `DW_AT_accessibility` attribute. Accessibility(constants::DwAccess), /// The value of a `DW_AT_visibility` attribute. Visibility(constants::DwVis), /// The value of a `DW_AT_virtuality` attribute. Virtuality(constants::DwVirtuality), /// The value of a `DW_AT_language` attribute. Language(constants::DwLang), /// The value of a `DW_AT_address_class` attribute. AddressClass(constants::DwAddr), /// The value of a `DW_AT_identifier_case` attribute. IdentifierCase(constants::DwId), /// The value of a `DW_AT_calling_convention` attribute. CallingConvention(constants::DwCc), /// The value of a `DW_AT_inline` attribute. Inline(constants::DwInl), /// The value of a `DW_AT_ordering` attribute. Ordering(constants::DwOrd), /// An index into the filename entries from the line number information /// table for the unit containing this value. FileIndex(Option), } impl AttributeValue { /// Return the form that will be used to encode this value. pub fn form(&self, encoding: Encoding) -> Result { // TODO: missing forms: // - DW_FORM_indirect // - DW_FORM_implicit_const // - FW_FORM_block1/block2/block4 // - DW_FORM_str/strx1/strx2/strx3/strx4 // - DW_FORM_addrx/addrx1/addrx2/addrx3/addrx4 // - DW_FORM_data16 // - DW_FORM_line_strp // - DW_FORM_loclistx // - DW_FORM_rnglistx let form = match *self { AttributeValue::Address(_) => constants::DW_FORM_addr, AttributeValue::Block(_) => constants::DW_FORM_block, AttributeValue::Data1(_) => constants::DW_FORM_data1, AttributeValue::Data2(_) => constants::DW_FORM_data2, AttributeValue::Data4(_) => constants::DW_FORM_data4, AttributeValue::Data8(_) => constants::DW_FORM_data8, AttributeValue::Exprloc(_) => constants::DW_FORM_exprloc, AttributeValue::Flag(_) => constants::DW_FORM_flag, AttributeValue::FlagPresent => constants::DW_FORM_flag_present, AttributeValue::UnitRef(_) => { // Using a fixed size format lets us write a placeholder before we know // the value. match encoding.format { Format::Dwarf32 => constants::DW_FORM_ref4, Format::Dwarf64 => constants::DW_FORM_ref8, } } AttributeValue::DebugInfoRef(_) => constants::DW_FORM_ref_addr, AttributeValue::DebugInfoRefSup(_) => { // TODO: should this depend on the size of supplementary section? match encoding.format { Format::Dwarf32 => constants::DW_FORM_ref_sup4, Format::Dwarf64 => constants::DW_FORM_ref_sup8, } } AttributeValue::LineProgramRef | AttributeValue::LocationListRef(_) | AttributeValue::DebugMacinfoRef(_) | AttributeValue::DebugMacroRef(_) | AttributeValue::RangeListRef(_) => { if encoding.version == 2 || encoding.version == 3 { match encoding.format { Format::Dwarf32 => constants::DW_FORM_data4, Format::Dwarf64 => constants::DW_FORM_data8, } } else { constants::DW_FORM_sec_offset } } AttributeValue::DebugTypesRef(_) => constants::DW_FORM_ref_sig8, AttributeValue::StringRef(_) => constants::DW_FORM_strp, AttributeValue::DebugStrRefSup(_) => constants::DW_FORM_strp_sup, AttributeValue::LineStringRef(_) => constants::DW_FORM_line_strp, AttributeValue::String(_) => constants::DW_FORM_string, AttributeValue::Encoding(_) | AttributeValue::DecimalSign(_) | AttributeValue::Endianity(_) | AttributeValue::Accessibility(_) | AttributeValue::Visibility(_) | AttributeValue::Virtuality(_) | AttributeValue::Language(_) | AttributeValue::AddressClass(_) | AttributeValue::IdentifierCase(_) | AttributeValue::CallingConvention(_) | AttributeValue::Inline(_) | AttributeValue::Ordering(_) | AttributeValue::FileIndex(_) | AttributeValue::Udata(_) => constants::DW_FORM_udata, AttributeValue::Sdata(_) => constants::DW_FORM_sdata, }; Ok(form) } fn size(&self, unit: &Unit, offsets: &UnitOffsets) -> usize { macro_rules! debug_assert_form { ($form:expr) => { debug_assert_eq!(self.form(unit.encoding()).unwrap(), $form) }; } match *self { AttributeValue::Address(_) => { debug_assert_form!(constants::DW_FORM_addr); unit.address_size() as usize } AttributeValue::Block(ref val) => { debug_assert_form!(constants::DW_FORM_block); uleb128_size(val.len() as u64) + val.len() } AttributeValue::Data1(_) => { debug_assert_form!(constants::DW_FORM_data1); 1 } AttributeValue::Data2(_) => { debug_assert_form!(constants::DW_FORM_data2); 2 } AttributeValue::Data4(_) => { debug_assert_form!(constants::DW_FORM_data4); 4 } AttributeValue::Data8(_) => { debug_assert_form!(constants::DW_FORM_data8); 8 } AttributeValue::Sdata(val) => { debug_assert_form!(constants::DW_FORM_sdata); sleb128_size(val) } AttributeValue::Udata(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val) } AttributeValue::Exprloc(ref val) => { debug_assert_form!(constants::DW_FORM_exprloc); let size = val.size(unit.encoding(), Some(offsets)); uleb128_size(size as u64) + size } AttributeValue::Flag(_) => { debug_assert_form!(constants::DW_FORM_flag); 1 } AttributeValue::FlagPresent => { debug_assert_form!(constants::DW_FORM_flag_present); 0 } AttributeValue::UnitRef(_) => { match unit.format() { Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref4), Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref8), } unit.format().word_size() as usize } AttributeValue::DebugInfoRef(_) => { debug_assert_form!(constants::DW_FORM_ref_addr); if unit.version() == 2 { unit.address_size() as usize } else { unit.format().word_size() as usize } } AttributeValue::DebugInfoRefSup(_) => { match unit.format() { Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref_sup4), Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref_sup8), } unit.format().word_size() as usize } AttributeValue::LineProgramRef => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } unit.format().word_size() as usize } AttributeValue::LocationListRef(_) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } unit.format().word_size() as usize } AttributeValue::DebugMacinfoRef(_) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } unit.format().word_size() as usize } AttributeValue::DebugMacroRef(_) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } unit.format().word_size() as usize } AttributeValue::RangeListRef(_) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } unit.format().word_size() as usize } AttributeValue::DebugTypesRef(_) => { debug_assert_form!(constants::DW_FORM_ref_sig8); 8 } AttributeValue::StringRef(_) => { debug_assert_form!(constants::DW_FORM_strp); unit.format().word_size() as usize } AttributeValue::DebugStrRefSup(_) => { debug_assert_form!(constants::DW_FORM_strp_sup); unit.format().word_size() as usize } AttributeValue::LineStringRef(_) => { debug_assert_form!(constants::DW_FORM_line_strp); unit.format().word_size() as usize } AttributeValue::String(ref val) => { debug_assert_form!(constants::DW_FORM_string); val.len() + 1 } AttributeValue::Encoding(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::DecimalSign(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::Endianity(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::Accessibility(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::Visibility(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::Virtuality(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::Language(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::AddressClass(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0) } AttributeValue::IdentifierCase(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::CallingConvention(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::Inline(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::Ordering(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.0 as u64) } AttributeValue::FileIndex(val) => { debug_assert_form!(constants::DW_FORM_udata); uleb128_size(val.map(|id| id.raw(unit.version())).unwrap_or(0)) } } } /// Write the attribute value to the given sections. fn write( &self, w: &mut DebugInfo, debug_info_refs: &mut Vec, unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, unit: &Unit, offsets: &UnitOffsets, line_program: Option, line_strings: &DebugLineStrOffsets, strings: &DebugStrOffsets, range_lists: &RangeListOffsets, loc_lists: &LocationListOffsets, ) -> Result<()> { macro_rules! debug_assert_form { ($form:expr) => { debug_assert_eq!(self.form(unit.encoding()).unwrap(), $form) }; } match *self { AttributeValue::Address(val) => { debug_assert_form!(constants::DW_FORM_addr); w.write_address(val, unit.address_size())?; } AttributeValue::Block(ref val) => { debug_assert_form!(constants::DW_FORM_block); w.write_uleb128(val.len() as u64)?; w.write(val)?; } AttributeValue::Data1(val) => { debug_assert_form!(constants::DW_FORM_data1); w.write_u8(val)?; } AttributeValue::Data2(val) => { debug_assert_form!(constants::DW_FORM_data2); w.write_u16(val)?; } AttributeValue::Data4(val) => { debug_assert_form!(constants::DW_FORM_data4); w.write_u32(val)?; } AttributeValue::Data8(val) => { debug_assert_form!(constants::DW_FORM_data8); w.write_u64(val)?; } AttributeValue::Sdata(val) => { debug_assert_form!(constants::DW_FORM_sdata); w.write_sleb128(val)?; } AttributeValue::Udata(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(val)?; } AttributeValue::Exprloc(ref val) => { debug_assert_form!(constants::DW_FORM_exprloc); w.write_uleb128(val.size(unit.encoding(), Some(offsets)) as u64)?; val.write( &mut w.0, Some(debug_info_refs), unit.encoding(), Some(offsets), )?; } AttributeValue::Flag(val) => { debug_assert_form!(constants::DW_FORM_flag); w.write_u8(val as u8)?; } AttributeValue::FlagPresent => { debug_assert_form!(constants::DW_FORM_flag_present); } AttributeValue::UnitRef(id) => { match unit.format() { Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref4), Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref8), } unit_refs.push((w.offset(), id)); w.write_udata(0, unit.format().word_size())?; } AttributeValue::DebugInfoRef(reference) => { debug_assert_form!(constants::DW_FORM_ref_addr); let size = if unit.version() == 2 { unit.address_size() } else { unit.format().word_size() }; match reference { Reference::Symbol(symbol) => w.write_reference(symbol, size)?, Reference::Entry(unit, entry) => { debug_info_refs.push(DebugInfoReference { offset: w.len(), unit, entry, size, }); w.write_udata(0, size)?; } } } AttributeValue::DebugInfoRefSup(val) => { match unit.format() { Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref_sup4), Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref_sup8), } w.write_udata(val.0 as u64, unit.format().word_size())?; } AttributeValue::LineProgramRef => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } match line_program { Some(line_program) => { w.write_offset( line_program.0, SectionId::DebugLine, unit.format().word_size(), )?; } None => return Err(Error::InvalidAttributeValue), } } AttributeValue::LocationListRef(val) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } let section = if unit.version() <= 4 { SectionId::DebugLoc } else { SectionId::DebugLocLists }; w.write_offset(loc_lists.get(val).0, section, unit.format().word_size())?; } AttributeValue::DebugMacinfoRef(val) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } w.write_offset(val.0, SectionId::DebugMacinfo, unit.format().word_size())?; } AttributeValue::DebugMacroRef(val) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } w.write_offset(val.0, SectionId::DebugMacro, unit.format().word_size())?; } AttributeValue::RangeListRef(val) => { if unit.version() >= 4 { debug_assert_form!(constants::DW_FORM_sec_offset); } let section = if unit.version() <= 4 { SectionId::DebugRanges } else { SectionId::DebugRngLists }; w.write_offset(range_lists.get(val).0, section, unit.format().word_size())?; } AttributeValue::DebugTypesRef(val) => { debug_assert_form!(constants::DW_FORM_ref_sig8); w.write_u64(val.0)?; } AttributeValue::StringRef(val) => { debug_assert_form!(constants::DW_FORM_strp); w.write_offset( strings.get(val).0, SectionId::DebugStr, unit.format().word_size(), )?; } AttributeValue::DebugStrRefSup(val) => { debug_assert_form!(constants::DW_FORM_strp_sup); w.write_udata(val.0 as u64, unit.format().word_size())?; } AttributeValue::LineStringRef(val) => { debug_assert_form!(constants::DW_FORM_line_strp); w.write_offset( line_strings.get(val).0, SectionId::DebugLineStr, unit.format().word_size(), )?; } AttributeValue::String(ref val) => { debug_assert_form!(constants::DW_FORM_string); w.write(val)?; w.write_u8(0)?; } AttributeValue::Encoding(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::DecimalSign(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::Endianity(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::Accessibility(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::Visibility(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::Virtuality(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::Language(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::AddressClass(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(val.0)?; } AttributeValue::IdentifierCase(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::CallingConvention(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::Inline(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::Ordering(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(u64::from(val.0))?; } AttributeValue::FileIndex(val) => { debug_assert_form!(constants::DW_FORM_udata); w.write_uleb128(val.map(|id| id.raw(unit.version())).unwrap_or(0))?; } } Ok(()) } } define_section!( DebugInfo, DebugInfoOffset, "A writable `.debug_info` section." ); /// The section offsets of all elements within a `.debug_info` section. #[derive(Debug, Default)] pub struct DebugInfoOffsets { base_id: BaseId, units: Vec, } impl DebugInfoOffsets { /// Get the `.debug_info` section offset for the given unit. #[inline] pub fn unit(&self, unit: UnitId) -> DebugInfoOffset { debug_assert_eq!(self.base_id, unit.base_id); self.units[unit.index].unit } /// Get the `.debug_info` section offset for the given entry. #[inline] pub fn entry(&self, unit: UnitId, entry: UnitEntryId) -> DebugInfoOffset { debug_assert_eq!(self.base_id, unit.base_id); self.units[unit.index].debug_info_offset(entry) } } /// The section offsets of all elements of a unit within a `.debug_info` section. #[derive(Debug)] pub(crate) struct UnitOffsets { base_id: BaseId, unit: DebugInfoOffset, entries: Vec, } impl UnitOffsets { /// Get the .debug_info offset for the given entry. #[inline] pub(crate) fn debug_info_offset(&self, entry: UnitEntryId) -> DebugInfoOffset { debug_assert_eq!(self.base_id, entry.base_id); let offset = self.entries[entry.index].offset; debug_assert_ne!(offset.0, 0); offset } /// Get the unit offset for the given entry. #[inline] pub(crate) fn unit_offset(&self, entry: UnitEntryId) -> u64 { let offset = self.debug_info_offset(entry); (offset.0 - self.unit.0) as u64 } /// Get the abbreviation code for the given entry. #[inline] pub(crate) fn abbrev(&self, entry: UnitEntryId) -> u64 { debug_assert_eq!(self.base_id, entry.base_id); self.entries[entry.index].abbrev } } #[derive(Debug, Clone, Copy)] pub(crate) struct EntryOffset { offset: DebugInfoOffset, abbrev: u64, } impl EntryOffset { fn none() -> Self { EntryOffset { offset: DebugInfoOffset(0), abbrev: 0, } } } /// A reference to a `.debug_info` entry that has yet to be resolved. #[derive(Debug, Clone, Copy)] pub(crate) struct DebugInfoReference { /// The offset within the section of the reference. pub offset: usize, /// The size of the reference. pub size: u8, /// The unit containing the entry. pub unit: UnitId, /// The entry being referenced. pub entry: UnitEntryId, } #[cfg(feature = "read")] pub(crate) mod convert { use super::*; use crate::common::{DwoId, UnitSectionOffset}; use crate::read::{self, Reader}; use crate::write::{self, ConvertError, ConvertResult, LocationList, RangeList}; use std::collections::HashMap; pub(crate) struct ConvertUnit> { from_unit: read::Unit, base_id: BaseId, encoding: Encoding, entries: Vec, entry_offsets: Vec, root: UnitEntryId, } pub(crate) struct ConvertUnitContext<'a, R: Reader> { pub dwarf: &'a read::Dwarf, pub unit: &'a read::Unit, pub line_strings: &'a mut write::LineStringTable, pub strings: &'a mut write::StringTable, pub ranges: &'a mut write::RangeListTable, pub locations: &'a mut write::LocationListTable, pub convert_address: &'a dyn Fn(u64) -> Option
, pub base_address: Address, pub line_program_offset: Option, pub line_program_files: Vec, pub entry_ids: &'a HashMap, } impl UnitTable { /// Create a unit table by reading the data in the given sections. /// /// This also updates the given tables with the values that are referenced from /// attributes in this section. /// /// `convert_address` is a function to convert read addresses into the `Address` /// type. For non-relocatable addresses, this function may simply return /// `Address::Constant(address)`. For relocatable addresses, it is the caller's /// responsibility to determine the symbol and addend corresponding to the address /// and return `Address::Symbol { symbol, addend }`. pub fn from>( dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult { let base_id = BaseId::default(); let mut unit_entries = Vec::new(); let mut entry_ids = HashMap::new(); let mut from_units = dwarf.units(); while let Some(from_unit) = from_units.next()? { let unit_id = UnitId::new(base_id, unit_entries.len()); unit_entries.push(Unit::convert_entries( from_unit, unit_id, &mut entry_ids, dwarf, )?); } // Attributes must be converted in a separate pass so that we can handle // references to other compilation units. let mut units = Vec::new(); for unit_entries in unit_entries.drain(..) { units.push(Unit::convert_attributes( unit_entries, &entry_ids, dwarf, line_strings, strings, convert_address, )?); } Ok(UnitTable { base_id, units }) } } impl Unit { /// Create a unit by reading the data in the input sections. /// /// Does not add entry attributes. pub(crate) fn convert_entries>( from_header: read::UnitHeader, unit_id: UnitId, entry_ids: &mut HashMap, dwarf: &read::Dwarf, ) -> ConvertResult> { match from_header.type_() { read::UnitType::Compilation => (), _ => return Err(ConvertError::UnsupportedUnitType), } let base_id = BaseId::default(); let from_unit = dwarf.unit(from_header)?; let encoding = from_unit.encoding(); let mut entries = Vec::new(); let mut entry_offsets = Vec::new(); let mut from_tree = from_unit.entries_tree(None)?; let from_root = from_tree.root()?; let root = DebuggingInformationEntry::convert_entry( from_root, &from_unit, base_id, &mut entries, &mut entry_offsets, entry_ids, None, unit_id, )?; Ok(ConvertUnit { from_unit, base_id, encoding, entries, entry_offsets, root, }) } /// Create entry attributes by reading the data in the input sections. fn convert_attributes>( unit: ConvertUnit, entry_ids: &HashMap, dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult { let from_unit = unit.from_unit; let base_address = convert_address(from_unit.low_pc).ok_or(ConvertError::InvalidAddress)?; let (line_program_offset, line_program, line_program_files) = match from_unit.line_program { Some(ref from_program) => { let from_program = from_program.clone(); let line_program_offset = from_program.header().offset(); let (line_program, line_program_files) = LineProgram::from( from_program, dwarf, line_strings, strings, convert_address, )?; (Some(line_program_offset), line_program, line_program_files) } None => (None, LineProgram::none(), Vec::new()), }; let mut ranges = RangeListTable::default(); let mut locations = LocationListTable::default(); let mut context = ConvertUnitContext { entry_ids, dwarf, unit: &from_unit, line_strings, strings, ranges: &mut ranges, locations: &mut locations, convert_address, base_address, line_program_offset, line_program_files, }; let mut entries = unit.entries; for entry in &mut entries { entry.convert_attributes(&mut context, &unit.entry_offsets)?; } Ok(Unit { base_id: unit.base_id, encoding: unit.encoding, line_program, ranges, locations, entries, root: unit.root, }) } } impl DebuggingInformationEntry { /// Create an entry by reading the data in the input sections. /// /// Does not add the entry attributes. fn convert_entry>( from: read::EntriesTreeNode<'_, '_, '_, R>, from_unit: &read::Unit, base_id: BaseId, entries: &mut Vec, entry_offsets: &mut Vec, entry_ids: &mut HashMap, parent: Option, unit_id: UnitId, ) -> ConvertResult { let from_entry = from.entry(); let id = DebuggingInformationEntry::new(base_id, entries, parent, from_entry.tag()); let offset = from_entry.offset(); entry_offsets.push(offset); entry_ids.insert(offset.to_unit_section_offset(from_unit), (unit_id, id)); let mut from_children = from.children(); while let Some(from_child) = from_children.next()? { DebuggingInformationEntry::convert_entry( from_child, from_unit, base_id, entries, entry_offsets, entry_ids, Some(id), unit_id, )?; } Ok(id) } /// Create an entry's attributes by reading the data in the input sections. fn convert_attributes>( &mut self, context: &mut ConvertUnitContext<'_, R>, entry_offsets: &[read::UnitOffset], ) -> ConvertResult<()> { let offset = entry_offsets[self.id.index]; let from = context.unit.entry(offset)?; let mut from_attrs = from.attrs(); while let Some(from_attr) = from_attrs.next()? { if from_attr.name() == constants::DW_AT_sibling { // This may point to a null entry, so we have to treat it differently. self.set_sibling(true); } else if let Some(attr) = Attribute::from(context, &from_attr)? { self.set(attr.name, attr.value); } } Ok(()) } } impl Attribute { /// Create an attribute by reading the data in the given sections. pub(crate) fn from>( context: &mut ConvertUnitContext<'_, R>, from: &read::Attribute, ) -> ConvertResult> { let value = AttributeValue::from(context, from.value())?; Ok(value.map(|value| Attribute { name: from.name(), value, })) } } impl AttributeValue { /// Create an attribute value by reading the data in the given sections. pub(crate) fn from>( context: &mut ConvertUnitContext<'_, R>, from: read::AttributeValue, ) -> ConvertResult> { let to = match from { read::AttributeValue::Addr(val) => match (context.convert_address)(val) { Some(val) => AttributeValue::Address(val), None => return Err(ConvertError::InvalidAddress), }, read::AttributeValue::Block(r) => AttributeValue::Block(r.to_slice()?.into()), read::AttributeValue::Data1(val) => AttributeValue::Data1(val), read::AttributeValue::Data2(val) => AttributeValue::Data2(val), read::AttributeValue::Data4(val) => AttributeValue::Data4(val), read::AttributeValue::Data8(val) => AttributeValue::Data8(val), read::AttributeValue::Sdata(val) => AttributeValue::Sdata(val), read::AttributeValue::Udata(val) => AttributeValue::Udata(val), read::AttributeValue::Exprloc(expression) => { let expression = Expression::from( expression, context.unit.encoding(), Some(context.dwarf), Some(context.unit), Some(context.entry_ids), context.convert_address, )?; AttributeValue::Exprloc(expression) } // TODO: it would be nice to preserve the flag form. read::AttributeValue::Flag(val) => AttributeValue::Flag(val), read::AttributeValue::DebugAddrBase(_base) => { // We convert all address indices to addresses, // so this is unneeded. return Ok(None); } read::AttributeValue::DebugAddrIndex(index) => { let val = context.dwarf.address(context.unit, index)?; match (context.convert_address)(val) { Some(val) => AttributeValue::Address(val), None => return Err(ConvertError::InvalidAddress), } } read::AttributeValue::UnitRef(val) => { if !context.unit.header.is_valid_offset(val) { return Err(ConvertError::InvalidUnitRef); } let id = context .entry_ids .get(&val.to_unit_section_offset(context.unit)) .ok_or(ConvertError::InvalidUnitRef)?; AttributeValue::UnitRef(id.1) } read::AttributeValue::DebugInfoRef(val) => { // TODO: support relocation of this value let id = context .entry_ids .get(&UnitSectionOffset::DebugInfoOffset(val)) .ok_or(ConvertError::InvalidDebugInfoRef)?; AttributeValue::DebugInfoRef(Reference::Entry(id.0, id.1)) } read::AttributeValue::DebugInfoRefSup(val) => AttributeValue::DebugInfoRefSup(val), read::AttributeValue::DebugLineRef(val) => { // There should only be the line program in the CU DIE which we've already // converted, so check if it matches that. if Some(val) == context.line_program_offset { AttributeValue::LineProgramRef } else { return Err(ConvertError::InvalidLineRef); } } read::AttributeValue::DebugMacinfoRef(val) => AttributeValue::DebugMacinfoRef(val), read::AttributeValue::DebugMacroRef(val) => AttributeValue::DebugMacroRef(val), read::AttributeValue::LocationListsRef(val) => { let iter = context .dwarf .locations .raw_locations(val, context.unit.encoding())?; let loc_list = LocationList::from(iter, context)?; let loc_id = context.locations.add(loc_list); AttributeValue::LocationListRef(loc_id) } read::AttributeValue::DebugLocListsBase(_base) => { // We convert all location list indices to offsets, // so this is unneeded. return Ok(None); } read::AttributeValue::DebugLocListsIndex(index) => { let offset = context.dwarf.locations_offset(context.unit, index)?; let iter = context .dwarf .locations .raw_locations(offset, context.unit.encoding())?; let loc_list = LocationList::from(iter, context)?; let loc_id = context.locations.add(loc_list); AttributeValue::LocationListRef(loc_id) } read::AttributeValue::RangeListsRef(offset) => { let offset = context.dwarf.ranges_offset_from_raw(context.unit, offset); let iter = context.dwarf.raw_ranges(context.unit, offset)?; let range_list = RangeList::from(iter, context)?; let range_id = context.ranges.add(range_list); AttributeValue::RangeListRef(range_id) } read::AttributeValue::DebugRngListsBase(_base) => { // We convert all range list indices to offsets, // so this is unneeded. return Ok(None); } read::AttributeValue::DebugRngListsIndex(index) => { let offset = context.dwarf.ranges_offset(context.unit, index)?; let iter = context .dwarf .ranges .raw_ranges(offset, context.unit.encoding())?; let range_list = RangeList::from(iter, context)?; let range_id = context.ranges.add(range_list); AttributeValue::RangeListRef(range_id) } read::AttributeValue::DebugTypesRef(val) => AttributeValue::DebugTypesRef(val), read::AttributeValue::DebugStrRef(offset) => { let r = context.dwarf.string(offset)?; let id = context.strings.add(r.to_slice()?); AttributeValue::StringRef(id) } read::AttributeValue::DebugStrRefSup(val) => AttributeValue::DebugStrRefSup(val), read::AttributeValue::DebugStrOffsetsBase(_base) => { // We convert all string offsets to `.debug_str` references, // so this is unneeded. return Ok(None); } read::AttributeValue::DebugStrOffsetsIndex(index) => { let offset = context.dwarf.string_offset(context.unit, index)?; let r = context.dwarf.string(offset)?; let id = context.strings.add(r.to_slice()?); AttributeValue::StringRef(id) } read::AttributeValue::DebugLineStrRef(offset) => { let r = context.dwarf.line_string(offset)?; let id = context.line_strings.add(r.to_slice()?); AttributeValue::LineStringRef(id) } read::AttributeValue::String(r) => AttributeValue::String(r.to_slice()?.into()), read::AttributeValue::Encoding(val) => AttributeValue::Encoding(val), read::AttributeValue::DecimalSign(val) => AttributeValue::DecimalSign(val), read::AttributeValue::Endianity(val) => AttributeValue::Endianity(val), read::AttributeValue::Accessibility(val) => AttributeValue::Accessibility(val), read::AttributeValue::Visibility(val) => AttributeValue::Visibility(val), read::AttributeValue::Virtuality(val) => AttributeValue::Virtuality(val), read::AttributeValue::Language(val) => AttributeValue::Language(val), read::AttributeValue::AddressClass(val) => AttributeValue::AddressClass(val), read::AttributeValue::IdentifierCase(val) => AttributeValue::IdentifierCase(val), read::AttributeValue::CallingConvention(val) => { AttributeValue::CallingConvention(val) } read::AttributeValue::Inline(val) => AttributeValue::Inline(val), read::AttributeValue::Ordering(val) => AttributeValue::Ordering(val), read::AttributeValue::FileIndex(val) => { if val == 0 && context.unit.encoding().version <= 4 { AttributeValue::FileIndex(None) } else { match context.line_program_files.get(val as usize) { Some(id) => AttributeValue::FileIndex(Some(*id)), None => return Err(ConvertError::InvalidFileIndex), } } } // Should always be a more specific section reference. read::AttributeValue::SecOffset(_) => { return Err(ConvertError::InvalidAttributeValue); } read::AttributeValue::DwoId(DwoId(val)) => AttributeValue::Udata(val), }; Ok(Some(to)) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::common::LineEncoding; use crate::constants; use crate::read; use crate::write::{ Dwarf, DwarfUnit, EndianVec, LineString, Location, LocationList, Range, RangeList, }; use crate::LittleEndian; use std::mem; #[test] fn test_unit_table() { let mut dwarf = Dwarf::new(); let unit_id1 = dwarf.units.add(Unit::new( Encoding { version: 4, address_size: 8, format: Format::Dwarf32, }, LineProgram::none(), )); let unit2 = dwarf.units.add(Unit::new( Encoding { version: 2, address_size: 4, format: Format::Dwarf64, }, LineProgram::none(), )); let unit3 = dwarf.units.add(Unit::new( Encoding { version: 5, address_size: 4, format: Format::Dwarf32, }, LineProgram::none(), )); assert_eq!(dwarf.units.count(), 3); { let unit1 = dwarf.units.get_mut(unit_id1); assert_eq!(unit1.version(), 4); assert_eq!(unit1.address_size(), 8); assert_eq!(unit1.format(), Format::Dwarf32); assert_eq!(unit1.count(), 1); let root_id = unit1.root(); assert_eq!(root_id, UnitEntryId::new(unit1.base_id, 0)); { let root = unit1.get_mut(root_id); assert_eq!(root.id(), root_id); assert!(root.parent().is_none()); assert_eq!(root.tag(), constants::DW_TAG_compile_unit); // Test get/get_mut assert!(root.get(constants::DW_AT_producer).is_none()); assert!(root.get_mut(constants::DW_AT_producer).is_none()); let mut producer = AttributeValue::String(b"root"[..].into()); root.set(constants::DW_AT_producer, producer.clone()); assert_eq!(root.get(constants::DW_AT_producer), Some(&producer)); assert_eq!(root.get_mut(constants::DW_AT_producer), Some(&mut producer)); // Test attrs let mut attrs = root.attrs(); let attr = attrs.next().unwrap(); assert_eq!(attr.name(), constants::DW_AT_producer); assert_eq!(attr.get(), &producer); assert!(attrs.next().is_none()); } let child1 = unit1.add(root_id, constants::DW_TAG_subprogram); assert_eq!(child1, UnitEntryId::new(unit1.base_id, 1)); { let child1 = unit1.get_mut(child1); assert_eq!(child1.parent(), Some(root_id)); let tmp = AttributeValue::String(b"tmp"[..].into()); child1.set(constants::DW_AT_name, tmp.clone()); assert_eq!(child1.get(constants::DW_AT_name), Some(&tmp)); // Test attrs_mut let name = AttributeValue::StringRef(dwarf.strings.add(&b"child1"[..])); { let attr = child1.attrs_mut().next().unwrap(); assert_eq!(attr.name(), constants::DW_AT_name); attr.set(name.clone()); } assert_eq!(child1.get(constants::DW_AT_name), Some(&name)); } let child2 = unit1.add(root_id, constants::DW_TAG_subprogram); assert_eq!(child2, UnitEntryId::new(unit1.base_id, 2)); { let child2 = unit1.get_mut(child2); assert_eq!(child2.parent(), Some(root_id)); let tmp = AttributeValue::String(b"tmp"[..].into()); child2.set(constants::DW_AT_name, tmp.clone()); assert_eq!(child2.get(constants::DW_AT_name), Some(&tmp)); // Test replace let name = AttributeValue::StringRef(dwarf.strings.add(&b"child2"[..])); child2.set(constants::DW_AT_name, name.clone()); assert_eq!(child2.get(constants::DW_AT_name), Some(&name)); } { let root = unit1.get(root_id); assert_eq!( root.children().cloned().collect::>(), vec![child1, child2] ); } } { let unit2 = dwarf.units.get(unit2); assert_eq!(unit2.version(), 2); assert_eq!(unit2.address_size(), 4); assert_eq!(unit2.format(), Format::Dwarf64); assert_eq!(unit2.count(), 1); let root = unit2.root(); assert_eq!(root, UnitEntryId::new(unit2.base_id, 0)); let root = unit2.get(root); assert_eq!(root.id(), UnitEntryId::new(unit2.base_id, 0)); assert!(root.parent().is_none()); assert_eq!(root.tag(), constants::DW_TAG_compile_unit); } let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); println!("{:?}", sections.debug_str); println!("{:?}", sections.debug_info); println!("{:?}", sections.debug_abbrev); let read_dwarf = sections.read(LittleEndian); let mut read_units = read_dwarf.units(); { let read_unit1 = read_units.next().unwrap().unwrap(); let unit1 = dwarf.units.get(unit_id1); assert_eq!(unit1.version(), read_unit1.version()); assert_eq!(unit1.address_size(), read_unit1.address_size()); assert_eq!(unit1.format(), read_unit1.format()); let read_unit1 = read_dwarf.unit(read_unit1).unwrap(); let mut read_entries = read_unit1.entries(); let root = unit1.get(unit1.root()); { let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(depth, 0); assert_eq!(root.tag(), read_root.tag()); assert!(read_root.has_children()); let producer = match root.get(constants::DW_AT_producer).unwrap() { AttributeValue::String(ref producer) => &**producer, otherwise => panic!("unexpected {:?}", otherwise), }; assert_eq!(producer, b"root"); let read_producer = read_root .attr_value(constants::DW_AT_producer) .unwrap() .unwrap(); assert_eq!( read_dwarf .attr_string(&read_unit1, read_producer) .unwrap() .slice(), producer ); } let mut children = root.children().cloned(); { let child = children.next().unwrap(); assert_eq!(child, UnitEntryId::new(unit1.base_id, 1)); let child = unit1.get(child); let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(depth, 1); assert_eq!(child.tag(), read_child.tag()); assert!(!read_child.has_children()); let name = match child.get(constants::DW_AT_name).unwrap() { AttributeValue::StringRef(name) => *name, otherwise => panic!("unexpected {:?}", otherwise), }; let name = dwarf.strings.get(name); assert_eq!(name, b"child1"); let read_name = read_child .attr_value(constants::DW_AT_name) .unwrap() .unwrap(); assert_eq!( read_dwarf .attr_string(&read_unit1, read_name) .unwrap() .slice(), name ); } { let child = children.next().unwrap(); assert_eq!(child, UnitEntryId::new(unit1.base_id, 2)); let child = unit1.get(child); let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(depth, 0); assert_eq!(child.tag(), read_child.tag()); assert!(!read_child.has_children()); let name = match child.get(constants::DW_AT_name).unwrap() { AttributeValue::StringRef(name) => *name, otherwise => panic!("unexpected {:?}", otherwise), }; let name = dwarf.strings.get(name); assert_eq!(name, b"child2"); let read_name = read_child .attr_value(constants::DW_AT_name) .unwrap() .unwrap(); assert_eq!( read_dwarf .attr_string(&read_unit1, read_name) .unwrap() .slice(), name ); } assert!(read_entries.next_dfs().unwrap().is_none()); } { let read_unit2 = read_units.next().unwrap().unwrap(); let unit2 = dwarf.units.get(unit2); assert_eq!(unit2.version(), read_unit2.version()); assert_eq!(unit2.address_size(), read_unit2.address_size()); assert_eq!(unit2.format(), read_unit2.format()); let abbrevs = read_dwarf.abbreviations(&read_unit2).unwrap(); let mut read_entries = read_unit2.entries(&abbrevs); { let root = unit2.get(unit2.root()); let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(depth, 0); assert_eq!(root.tag(), read_root.tag()); assert!(!read_root.has_children()); } assert!(read_entries.next_dfs().unwrap().is_none()); } { let read_unit3 = read_units.next().unwrap().unwrap(); let unit3 = dwarf.units.get(unit3); assert_eq!(unit3.version(), read_unit3.version()); assert_eq!(unit3.address_size(), read_unit3.address_size()); assert_eq!(unit3.format(), read_unit3.format()); let abbrevs = read_dwarf.abbreviations(&read_unit3).unwrap(); let mut read_entries = read_unit3.entries(&abbrevs); { let root = unit3.get(unit3.root()); let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); assert_eq!(depth, 0); assert_eq!(root.tag(), read_root.tag()); assert!(!read_root.has_children()); } assert!(read_entries.next_dfs().unwrap().is_none()); } assert!(read_units.next().unwrap().is_none()); let convert_dwarf = Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap(); assert_eq!(convert_dwarf.units.count(), dwarf.units.count()); for i in 0..convert_dwarf.units.count() { let unit_id = dwarf.units.id(i); let unit = dwarf.units.get(unit_id); let convert_unit_id = convert_dwarf.units.id(i); let convert_unit = convert_dwarf.units.get(convert_unit_id); assert_eq!(convert_unit.version(), unit.version()); assert_eq!(convert_unit.address_size(), unit.address_size()); assert_eq!(convert_unit.format(), unit.format()); assert_eq!(convert_unit.count(), unit.count()); let root = unit.get(unit.root()); let convert_root = convert_unit.get(convert_unit.root()); assert_eq!(convert_root.tag(), root.tag()); for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { assert_eq!(convert_attr, attr); } } } #[test] fn test_attribute_value() { let string_data = "string data"; let line_string_data = "line string data"; let data = vec![1, 2, 3, 4]; let read_data = read::EndianSlice::new(&[1, 2, 3, 4], LittleEndian); let mut expression = Expression::new(); expression.op_constu(57); let read_expression = read::Expression(read::EndianSlice::new( &[constants::DW_OP_constu.0, 57], LittleEndian, )); let range = RangeList(vec![Range::StartEnd { begin: Address::Constant(0x1234), end: Address::Constant(0x2345), }]); let location = LocationList(vec![Location::StartEnd { begin: Address::Constant(0x1234), end: Address::Constant(0x2345), data: expression.clone(), }]); for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut dwarf = Dwarf::new(); let unit = dwarf.units.add(Unit::new(encoding, LineProgram::none())); let unit = dwarf.units.get_mut(unit); let loc_id = unit.locations.add(location.clone()); let range_id = unit.ranges.add(range.clone()); // Create a string with a non-zero id/offset. dwarf.strings.add("dummy string"); let string_id = dwarf.strings.add(string_data); dwarf.line_strings.add("dummy line string"); let line_string_id = dwarf.line_strings.add(line_string_data); let attributes = &[ ( constants::DW_AT_name, AttributeValue::Address(Address::Constant(0x1234)), read::AttributeValue::Addr(0x1234), ), ( constants::DW_AT_name, AttributeValue::Block(data.clone()), read::AttributeValue::Block(read_data), ), ( constants::DW_AT_name, AttributeValue::Data1(0x12), read::AttributeValue::Data1(0x12), ), ( constants::DW_AT_name, AttributeValue::Data2(0x1234), read::AttributeValue::Data2(0x1234), ), ( constants::DW_AT_name, AttributeValue::Data4(0x1234), read::AttributeValue::Data4(0x1234), ), ( constants::DW_AT_name, AttributeValue::Data8(0x1234), read::AttributeValue::Data8(0x1234), ), ( constants::DW_AT_name, AttributeValue::Sdata(0x1234), read::AttributeValue::Sdata(0x1234), ), ( constants::DW_AT_name, AttributeValue::Udata(0x1234), read::AttributeValue::Udata(0x1234), ), ( constants::DW_AT_name, AttributeValue::Exprloc(expression.clone()), read::AttributeValue::Exprloc(read_expression), ), ( constants::DW_AT_name, AttributeValue::Flag(false), read::AttributeValue::Flag(false), ), /* ( constants::DW_AT_name, AttributeValue::FlagPresent, read::AttributeValue::Flag(true), ), */ ( constants::DW_AT_name, AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), read::AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), ), ( constants::DW_AT_macro_info, AttributeValue::DebugMacinfoRef(DebugMacinfoOffset(0x1234)), read::AttributeValue::SecOffset(0x1234), ), ( constants::DW_AT_macros, AttributeValue::DebugMacroRef(DebugMacroOffset(0x1234)), read::AttributeValue::SecOffset(0x1234), ), ( constants::DW_AT_name, AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), read::AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), ), ( constants::DW_AT_name, AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), read::AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), ), ( constants::DW_AT_name, AttributeValue::String(data.clone()), read::AttributeValue::String(read_data), ), ( constants::DW_AT_encoding, AttributeValue::Encoding(constants::DwAte(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_decimal_sign, AttributeValue::DecimalSign(constants::DwDs(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_endianity, AttributeValue::Endianity(constants::DwEnd(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_accessibility, AttributeValue::Accessibility(constants::DwAccess(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_visibility, AttributeValue::Visibility(constants::DwVis(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_virtuality, AttributeValue::Virtuality(constants::DwVirtuality(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_language, AttributeValue::Language(constants::DwLang(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_address_class, AttributeValue::AddressClass(constants::DwAddr(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_identifier_case, AttributeValue::IdentifierCase(constants::DwId(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_calling_convention, AttributeValue::CallingConvention(constants::DwCc(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_ordering, AttributeValue::Ordering(constants::DwOrd(0x12)), read::AttributeValue::Udata(0x12), ), ( constants::DW_AT_inline, AttributeValue::Inline(constants::DwInl(0x12)), read::AttributeValue::Udata(0x12), ), ]; let mut add_attribute = |name, value| { let entry_id = unit.add(unit.root(), constants::DW_TAG_subprogram); let entry = unit.get_mut(entry_id); entry.set(name, value); }; for (name, value, _) in attributes { add_attribute(*name, value.clone()); } add_attribute( constants::DW_AT_location, AttributeValue::LocationListRef(loc_id), ); add_attribute( constants::DW_AT_ranges, AttributeValue::RangeListRef(range_id), ); add_attribute(constants::DW_AT_name, AttributeValue::StringRef(string_id)); add_attribute( constants::DW_AT_name, AttributeValue::LineStringRef(line_string_id), ); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let mut read_units = read_dwarf.units(); let read_unit = read_units.next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit).unwrap(); let read_unit = read_unit.unit_ref(&read_dwarf); let mut read_entries = read_unit.entries(); let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); let mut get_attribute = |name| { let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); entry.attr(name).unwrap().unwrap() }; for (name, _, expect_value) in attributes { let read_value = &get_attribute(*name).raw_value(); // read::AttributeValue is invariant in the lifetime of R. // The lifetimes here are all okay, so transmute it. let read_value = unsafe { mem::transmute::< &read::AttributeValue>, &read::AttributeValue>, >(read_value) }; assert_eq!(read_value, expect_value); } let read_attr = get_attribute(constants::DW_AT_location).value(); let read::AttributeValue::LocationListsRef(read_loc_offset) = read_attr else { panic!("unexpected {:?}", read_attr); }; let mut read_locations = read_unit.locations(read_loc_offset).unwrap(); let read_location = read_locations.next().unwrap().unwrap(); assert_eq!(read_location.range.begin, 0x1234); assert_eq!(read_location.range.end, 0x2345); assert_eq!(read_location.data, read_expression); let read_attr = get_attribute(constants::DW_AT_ranges).value(); let read::AttributeValue::RangeListsRef(read_range_offset) = read_attr else { panic!("unexpected {:?}", read_attr); }; let read_range_offset = read_unit.ranges_offset_from_raw(read_range_offset); let mut read_ranges = read_unit.ranges(read_range_offset).unwrap(); let read_range = read_ranges.next().unwrap().unwrap(); assert_eq!(read_range.begin, 0x1234); assert_eq!(read_range.end, 0x2345); let read_string = get_attribute(constants::DW_AT_name).raw_value(); let read::AttributeValue::DebugStrRef(read_string_offset) = read_string else { panic!("unexpected {:?}", read_string); }; assert_eq!( read_dwarf.string(read_string_offset).unwrap().slice(), string_data.as_bytes() ); let read_line_string = get_attribute(constants::DW_AT_name).raw_value(); let read::AttributeValue::DebugLineStrRef(read_line_string_offset) = read_line_string else { panic!("unexpected {:?}", read_line_string); }; assert_eq!( read_dwarf .line_string(read_line_string_offset) .unwrap() .slice(), line_string_data.as_bytes() ); let convert_dwarf = Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) .unwrap(); let convert_unit = convert_dwarf.units.get(convert_dwarf.units.id(0)); let convert_root = convert_unit.get(convert_unit.root()); let mut convert_entries = convert_root.children(); let mut get_convert_attr = |name| { let convert_entry = convert_unit.get(*convert_entries.next().unwrap()); convert_entry.get(name).unwrap() }; for (name, attr, _) in attributes { let convert_attr = get_convert_attr(*name); assert_eq!(convert_attr, attr); } let convert_attr = get_convert_attr(constants::DW_AT_location); let AttributeValue::LocationListRef(convert_loc_id) = convert_attr else { panic!("unexpected {:?}", convert_attr); }; let convert_location = convert_unit.locations.get(*convert_loc_id); assert_eq!(*convert_location, location); let convert_attr = get_convert_attr(constants::DW_AT_ranges); let AttributeValue::RangeListRef(convert_range_id) = convert_attr else { panic!("unexpected {:?}", convert_attr); }; let convert_range = convert_unit.ranges.get(*convert_range_id); assert_eq!(*convert_range, range); let convert_attr = get_convert_attr(constants::DW_AT_name); let AttributeValue::StringRef(convert_string_id) = convert_attr else { panic!("unexpected {:?}", convert_attr); }; let convert_string = convert_dwarf.strings.get(*convert_string_id); assert_eq!(convert_string, string_data.as_bytes()); let convert_attr = get_convert_attr(constants::DW_AT_name); let AttributeValue::LineStringRef(convert_line_string_id) = convert_attr else { panic!("unexpected {:?}", convert_attr); }; let convert_line_string = convert_dwarf.line_strings.get(*convert_line_string_id); assert_eq!(convert_line_string, line_string_data.as_bytes()); } } } } #[test] fn test_unit_ref() { let mut dwarf = Dwarf::new(); let unit_id1 = dwarf.units.add(Unit::new( Encoding { version: 4, address_size: 8, format: Format::Dwarf32, }, LineProgram::none(), )); assert_eq!(unit_id1, dwarf.units.id(0)); let unit_id2 = dwarf.units.add(Unit::new( Encoding { version: 2, address_size: 4, format: Format::Dwarf64, }, LineProgram::none(), )); assert_eq!(unit_id2, dwarf.units.id(1)); let unit1_child1 = UnitEntryId::new(dwarf.units.get(unit_id1).base_id, 1); let unit1_child2 = UnitEntryId::new(dwarf.units.get(unit_id1).base_id, 2); let unit2_child1 = UnitEntryId::new(dwarf.units.get(unit_id2).base_id, 1); let unit2_child2 = UnitEntryId::new(dwarf.units.get(unit_id2).base_id, 2); { let unit1 = dwarf.units.get_mut(unit_id1); let root = unit1.root(); let child_id1 = unit1.add(root, constants::DW_TAG_subprogram); assert_eq!(child_id1, unit1_child1); let child_id2 = unit1.add(root, constants::DW_TAG_subprogram); assert_eq!(child_id2, unit1_child2); { let child1 = unit1.get_mut(child_id1); child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); } { let child2 = unit1.get_mut(child_id2); child2.set( constants::DW_AT_type, AttributeValue::DebugInfoRef(Reference::Entry(unit_id2, unit2_child1)), ); } } { let unit2 = dwarf.units.get_mut(unit_id2); let root = unit2.root(); let child_id1 = unit2.add(root, constants::DW_TAG_subprogram); assert_eq!(child_id1, unit2_child1); let child_id2 = unit2.add(root, constants::DW_TAG_subprogram); assert_eq!(child_id2, unit2_child2); { let child1 = unit2.get_mut(child_id1); child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); } { let child2 = unit2.get_mut(child_id2); child2.set( constants::DW_AT_type, AttributeValue::DebugInfoRef(Reference::Entry(unit_id1, unit1_child1)), ); } } let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); println!("{:?}", sections.debug_info); println!("{:?}", sections.debug_abbrev); let read_dwarf = sections.read(LittleEndian); let mut read_units = read_dwarf.units(); let read_unit = read_units.next().unwrap().unwrap(); let abbrevs = read_dwarf.abbreviations(&read_unit).unwrap(); let mut read_entries = read_unit.entries(&abbrevs); let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); let read_unit1_child1_attr = entry.attr_value(constants::DW_AT_type).unwrap(); let read_unit1_child1_section_offset = entry.offset().to_debug_info_offset(&read_unit).unwrap(); let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); let read_unit1_child2_attr = entry.attr_value(constants::DW_AT_type).unwrap(); let read_unit1_child2_offset = entry.offset(); let read_unit = read_units.next().unwrap().unwrap(); let abbrevs = read_dwarf.abbreviations(&read_unit).unwrap(); let mut read_entries = read_unit.entries(&abbrevs); let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); let read_unit2_child1_attr = entry.attr_value(constants::DW_AT_type).unwrap(); let read_unit2_child1_section_offset = entry.offset().to_debug_info_offset(&read_unit).unwrap(); let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); let read_unit2_child2_attr = entry.attr_value(constants::DW_AT_type).unwrap(); let read_unit2_child2_offset = entry.offset(); assert_eq!( read_unit1_child1_attr, Some(read::AttributeValue::UnitRef(read_unit1_child2_offset)) ); assert_eq!( read_unit1_child2_attr, Some(read::AttributeValue::DebugInfoRef( read_unit2_child1_section_offset )) ); assert_eq!( read_unit2_child1_attr, Some(read::AttributeValue::UnitRef(read_unit2_child2_offset)) ); assert_eq!( read_unit2_child2_attr, Some(read::AttributeValue::DebugInfoRef( read_unit1_child1_section_offset )) ); let convert_dwarf = Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap(); let convert_units = &convert_dwarf.units; assert_eq!(convert_units.count(), dwarf.units.count()); for i in 0..convert_units.count() { let unit = dwarf.units.get(dwarf.units.id(i)); let convert_unit = convert_units.get(convert_units.id(i)); assert_eq!(convert_unit.version(), unit.version()); assert_eq!(convert_unit.address_size(), unit.address_size()); assert_eq!(convert_unit.format(), unit.format()); assert_eq!(convert_unit.count(), unit.count()); let root = unit.get(unit.root()); let convert_root = convert_unit.get(convert_unit.root()); assert_eq!(convert_root.tag(), root.tag()); for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { assert_eq!(convert_attr, attr); } let child1 = unit.get(UnitEntryId::new(unit.base_id, 1)); let convert_child1 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 1)); assert_eq!(convert_child1.tag(), child1.tag()); for (convert_attr, attr) in convert_child1.attrs().zip(child1.attrs()) { assert_eq!(convert_attr.name, attr.name); match (convert_attr.value.clone(), attr.value.clone()) { ( AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), ) => { assert_eq!(convert_unit.index, unit.index); assert_eq!(convert_entry.index, entry.index); } (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { assert_eq!(convert_id.index, id.index); } (convert_value, value) => assert_eq!(convert_value, value), } } let child2 = unit.get(UnitEntryId::new(unit.base_id, 2)); let convert_child2 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 2)); assert_eq!(convert_child2.tag(), child2.tag()); for (convert_attr, attr) in convert_child2.attrs().zip(child2.attrs()) { assert_eq!(convert_attr.name, attr.name); match (convert_attr.value.clone(), attr.value.clone()) { ( AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), ) => { assert_eq!(convert_unit.index, unit.index); assert_eq!(convert_entry.index, entry.index); } (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { assert_eq!(convert_id.index, id.index); } (convert_value, value) => assert_eq!(convert_value, value), } } } } #[test] fn test_sibling() { fn add_child( unit: &mut Unit, parent: UnitEntryId, tag: constants::DwTag, name: &str, ) -> UnitEntryId { let id = unit.add(parent, tag); let child = unit.get_mut(id); child.set(constants::DW_AT_name, AttributeValue::String(name.into())); child.set_sibling(true); id } fn add_children(unit: &mut Unit) { let root = unit.root(); let child1 = add_child(unit, root, constants::DW_TAG_subprogram, "child1"); add_child(unit, child1, constants::DW_TAG_variable, "grandchild1"); add_child(unit, root, constants::DW_TAG_subprogram, "child2"); add_child(unit, root, constants::DW_TAG_subprogram, "child3"); } fn next_child>( entries: &mut read::EntriesCursor<'_, '_, R>, ) -> (read::UnitOffset, Option) { let (_, entry) = entries.next_dfs().unwrap().unwrap(); let offset = entry.offset(); let sibling = entry .attr_value(constants::DW_AT_sibling) .unwrap() .map(|attr| match attr { read::AttributeValue::UnitRef(offset) => offset, _ => panic!("bad sibling value"), }); (offset, sibling) } fn check_sibling>( unit: read::UnitHeader, dwarf: &read::Dwarf, ) { let unit = dwarf.unit(unit).unwrap(); let mut entries = unit.entries(); // root entries.next_dfs().unwrap().unwrap(); // child1 let (_, sibling1) = next_child(&mut entries); // grandchild1 entries.next_dfs().unwrap().unwrap(); // child2 let (offset2, sibling2) = next_child(&mut entries); // child3 let (_, _) = next_child(&mut entries); assert_eq!(sibling1, Some(offset2)); assert_eq!(sibling2, None); } let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 8, }; let mut dwarf = Dwarf::new(); let unit_id1 = dwarf.units.add(Unit::new(encoding, LineProgram::none())); add_children(dwarf.units.get_mut(unit_id1)); let unit_id2 = dwarf.units.add(Unit::new(encoding, LineProgram::none())); add_children(dwarf.units.get_mut(unit_id2)); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); println!("{:?}", sections.debug_info); println!("{:?}", sections.debug_abbrev); let read_dwarf = sections.read(LittleEndian); let mut read_units = read_dwarf.units(); check_sibling(read_units.next().unwrap().unwrap(), &read_dwarf); check_sibling(read_units.next().unwrap().unwrap(), &read_dwarf); } #[test] fn test_line_ref() { let dir_bytes = b"dir"; let file_bytes1 = b"file1"; let file_bytes2 = b"file2"; let file_string1 = LineString::String(file_bytes1.to_vec()); let file_string2 = LineString::String(file_bytes2.to_vec()); for &version in &[2, 3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; // The line program we'll be referencing. let mut line_program = LineProgram::new( encoding, LineEncoding::default(), LineString::String(dir_bytes.to_vec()), file_string1.clone(), None, ); let dir = line_program.default_directory(); // For version >= 5, this will reuse the existing file at index 0. let file1 = line_program.add_file(file_string1.clone(), dir, None); let file2 = line_program.add_file(file_string2.clone(), dir, None); let mut unit = Unit::new(encoding, line_program); let root = unit.get_mut(unit.root()); root.set( constants::DW_AT_name, AttributeValue::String(file_bytes1.to_vec()), ); root.set( constants::DW_AT_comp_dir, AttributeValue::String(dir_bytes.to_vec()), ); root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); let child = unit.add(unit.root(), constants::DW_TAG_subprogram); unit.get_mut(child).set( constants::DW_AT_decl_file, AttributeValue::FileIndex(Some(file1)), ); let child = unit.add(unit.root(), constants::DW_TAG_subprogram); unit.get_mut(child).set( constants::DW_AT_call_file, AttributeValue::FileIndex(Some(file2)), ); let mut dwarf = Dwarf::new(); dwarf.units.add(unit); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let mut read_units = read_dwarf.units(); let read_unit = read_units.next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit).unwrap(); let read_unit = read_unit.unit_ref(&read_dwarf); let read_line_program = read_unit.line_program.as_ref().unwrap().header(); let mut read_entries = read_unit.entries(); let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); let mut get_path = |name| { let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); let read_attr = entry.attr(name).unwrap().unwrap(); let read::AttributeValue::FileIndex(read_file_index) = read_attr.value() else { panic!("unexpected {:?}", read_attr); }; let read_file = read_line_program.file(read_file_index).unwrap(); let read_path = read_unit .attr_string(read_file.path_name()) .unwrap() .slice(); (read_file_index, read_path) }; let (read_index, read_path) = get_path(constants::DW_AT_decl_file); assert_eq!(read_index, if version >= 5 { 0 } else { 1 }); assert_eq!(read_path, file_bytes1); let (read_index, read_path) = get_path(constants::DW_AT_call_file); assert_eq!(read_index, if version >= 5 { 1 } else { 2 }); assert_eq!(read_path, file_bytes2); let convert_dwarf = Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) .unwrap(); let convert_unit = convert_dwarf.units.get(convert_dwarf.units.id(0)); let convert_root = convert_unit.get(convert_unit.root()); let mut convert_entries = convert_root.children(); let mut get_convert_path = |name| { let convert_entry = convert_unit.get(*convert_entries.next().unwrap()); let convert_attr = convert_entry.get(name).unwrap(); let AttributeValue::FileIndex(Some(convert_file_index)) = convert_attr else { panic!("unexpected {:?}", convert_attr); }; convert_unit.line_program.get_file(*convert_file_index).0 }; let convert_path = get_convert_path(constants::DW_AT_decl_file); assert_eq!(convert_path, &file_string1); let convert_path = get_convert_path(constants::DW_AT_call_file); assert_eq!(convert_path, &file_string2); } } } } #[test] fn test_line_program_used() { for used in [false, true] { let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 8, }; let line_program = LineProgram::new( encoding, LineEncoding::default(), LineString::String(b"comp_dir".to_vec()), LineString::String(b"comp_name".to_vec()), None, ); let mut unit = Unit::new(encoding, line_program); let file_id = if used { Some(FileId::new(0)) } else { None }; let root = unit.root(); unit.get_mut(root).set( constants::DW_AT_decl_file, AttributeValue::FileIndex(file_id), ); let mut dwarf = Dwarf::new(); dwarf.units.add(unit); let mut sections = Sections::new(EndianVec::new(LittleEndian)); dwarf.write(&mut sections).unwrap(); assert_eq!(!used, sections.debug_line.slice().is_empty()); } } #[test] fn test_delete_child() { fn set_name(unit: &mut Unit, id: UnitEntryId, name: &str) { let entry = unit.get_mut(id); entry.set(constants::DW_AT_name, AttributeValue::String(name.into())); } fn check_name( entry: &read::DebuggingInformationEntry<'_, '_, R>, unit: read::UnitRef<'_, R>, name: &str, ) { let name_attr = entry.attr(constants::DW_AT_name).unwrap().unwrap(); let entry_name = unit.attr_string(name_attr.value()).unwrap(); let entry_name_str = entry_name.to_string().unwrap(); assert_eq!(entry_name_str, name); } let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 8, }; let mut dwarf = DwarfUnit::new(encoding); let root = dwarf.unit.root(); // Add and delete entries in the root unit let child1 = dwarf.unit.add(root, constants::DW_TAG_subprogram); set_name(&mut dwarf.unit, child1, "child1"); let grandchild1 = dwarf.unit.add(child1, constants::DW_TAG_variable); set_name(&mut dwarf.unit, grandchild1, "grandchild1"); let child2 = dwarf.unit.add(root, constants::DW_TAG_subprogram); set_name(&mut dwarf.unit, child2, "child2"); // This deletes both `child1` and its child `grandchild1` dwarf.unit.get_mut(root).delete_child(child1); let child3 = dwarf.unit.add(root, constants::DW_TAG_subprogram); set_name(&mut dwarf.unit, child3, "child3"); let child4 = dwarf.unit.add(root, constants::DW_TAG_subprogram); set_name(&mut dwarf.unit, child4, "child4"); let grandchild4 = dwarf.unit.add(child4, constants::DW_TAG_variable); set_name(&mut dwarf.unit, grandchild4, "grandchild4"); dwarf.unit.get_mut(child4).delete_child(grandchild4); let mut sections = Sections::new(EndianVec::new(LittleEndian)); // Write DWARF data which should only include `child2`, `child3` and `child4` dwarf.write(&mut sections).unwrap(); let read_dwarf = sections.read(LittleEndian); let read_unit = read_dwarf.units().next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit).unwrap(); let read_unit = read_unit.unit_ref(&read_dwarf); let mut entries = read_unit.entries(); // root entries.next_dfs().unwrap().unwrap(); // child2 let (_, read_child2) = entries.next_dfs().unwrap().unwrap(); check_name(read_child2, read_unit, "child2"); // child3 let (_, read_child3) = entries.next_dfs().unwrap().unwrap(); check_name(read_child3, read_unit, "child3"); // child4 let (_, read_child4) = entries.next_dfs().unwrap().unwrap(); check_name(read_child4, read_unit, "child4"); // There should be no more entries assert!(entries.next_dfs().unwrap().is_none()); } } gimli-0.31.1/src/write/writer.rs000064400000000000000000000422411046102023000146150ustar 00000000000000use crate::common::{Format, SectionId}; use crate::constants; use crate::endianity::Endianity; use crate::leb128; use crate::write::{Address, Error, Result}; /// A trait for writing the data to a DWARF section. /// /// All write operations append to the section unless otherwise specified. #[allow(clippy::len_without_is_empty)] pub trait Writer { /// The endianity of bytes that are written. type Endian: Endianity; /// Return the endianity of bytes that are written. fn endian(&self) -> Self::Endian; /// Return the current section length. /// /// This may be used as an offset for future `write_at` calls. fn len(&self) -> usize; /// Write a slice. fn write(&mut self, bytes: &[u8]) -> Result<()>; /// Write a slice at a given offset. /// /// The write must not extend past the current section length. fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()>; /// Write an address. /// /// If the writer supports relocations, then it must provide its own implementation /// of this method. // TODO: use write_reference instead? fn write_address(&mut self, address: Address, size: u8) -> Result<()> { match address { Address::Constant(val) => self.write_udata(val, size), Address::Symbol { .. } => Err(Error::InvalidAddress), } } /// Write an address with a `.eh_frame` pointer encoding. /// /// The given size is only used for `DW_EH_PE_absptr` formats. /// /// If the writer supports relocations, then it must provide its own implementation /// of this method. fn write_eh_pointer( &mut self, address: Address, eh_pe: constants::DwEhPe, size: u8, ) -> Result<()> { match address { Address::Constant(val) => { // Indirect doesn't matter here. let val = match eh_pe.application() { constants::DW_EH_PE_absptr => val, constants::DW_EH_PE_pcrel => { // TODO: better handling of sign let offset = self.len() as u64; val.wrapping_sub(offset) } _ => { return Err(Error::UnsupportedPointerEncoding(eh_pe)); } }; self.write_eh_pointer_data(val, eh_pe.format(), size) } Address::Symbol { .. } => Err(Error::InvalidAddress), } } /// Write a value with a `.eh_frame` pointer format. /// /// The given size is only used for `DW_EH_PE_absptr` formats. /// /// This must not be used directly for values that may require relocation. fn write_eh_pointer_data( &mut self, val: u64, format: constants::DwEhPe, size: u8, ) -> Result<()> { match format { constants::DW_EH_PE_absptr => self.write_udata(val, size), constants::DW_EH_PE_uleb128 => self.write_uleb128(val), constants::DW_EH_PE_udata2 => self.write_udata(val, 2), constants::DW_EH_PE_udata4 => self.write_udata(val, 4), constants::DW_EH_PE_udata8 => self.write_udata(val, 8), constants::DW_EH_PE_sleb128 => self.write_sleb128(val as i64), constants::DW_EH_PE_sdata2 => self.write_sdata(val as i64, 2), constants::DW_EH_PE_sdata4 => self.write_sdata(val as i64, 4), constants::DW_EH_PE_sdata8 => self.write_sdata(val as i64, 8), _ => Err(Error::UnsupportedPointerEncoding(format)), } } /// Write an offset that is relative to the start of the given section. /// /// If the writer supports relocations, then it must provide its own implementation /// of this method. fn write_offset(&mut self, val: usize, _section: SectionId, size: u8) -> Result<()> { self.write_udata(val as u64, size) } /// Write an offset that is relative to the start of the given section. /// /// If the writer supports relocations, then it must provide its own implementation /// of this method. fn write_offset_at( &mut self, offset: usize, val: usize, _section: SectionId, size: u8, ) -> Result<()> { self.write_udata_at(offset, val as u64, size) } /// Write a reference to a symbol. /// /// If the writer supports symbols, then it must provide its own implementation /// of this method. fn write_reference(&mut self, _symbol: usize, _size: u8) -> Result<()> { Err(Error::InvalidReference) } /// Write a u8. fn write_u8(&mut self, val: u8) -> Result<()> { let bytes = [val]; self.write(&bytes) } /// Write a u16. fn write_u16(&mut self, val: u16) -> Result<()> { let mut bytes = [0; 2]; self.endian().write_u16(&mut bytes, val); self.write(&bytes) } /// Write a u32. fn write_u32(&mut self, val: u32) -> Result<()> { let mut bytes = [0; 4]; self.endian().write_u32(&mut bytes, val); self.write(&bytes) } /// Write a u64. fn write_u64(&mut self, val: u64) -> Result<()> { let mut bytes = [0; 8]; self.endian().write_u64(&mut bytes, val); self.write(&bytes) } /// Write a u8 at the given offset. fn write_u8_at(&mut self, offset: usize, val: u8) -> Result<()> { let bytes = [val]; self.write_at(offset, &bytes) } /// Write a u16 at the given offset. fn write_u16_at(&mut self, offset: usize, val: u16) -> Result<()> { let mut bytes = [0; 2]; self.endian().write_u16(&mut bytes, val); self.write_at(offset, &bytes) } /// Write a u32 at the given offset. fn write_u32_at(&mut self, offset: usize, val: u32) -> Result<()> { let mut bytes = [0; 4]; self.endian().write_u32(&mut bytes, val); self.write_at(offset, &bytes) } /// Write a u64 at the given offset. fn write_u64_at(&mut self, offset: usize, val: u64) -> Result<()> { let mut bytes = [0; 8]; self.endian().write_u64(&mut bytes, val); self.write_at(offset, &bytes) } /// Write unsigned data of the given size. /// /// Returns an error if the value is too large for the size. /// This must not be used directly for values that may require relocation. fn write_udata(&mut self, val: u64, size: u8) -> Result<()> { match size { 1 => { let write_val = val as u8; if val != u64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u8(write_val) } 2 => { let write_val = val as u16; if val != u64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u16(write_val) } 4 => { let write_val = val as u32; if val != u64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u32(write_val) } 8 => self.write_u64(val), otherwise => Err(Error::UnsupportedWordSize(otherwise)), } } /// Write signed data of the given size. /// /// Returns an error if the value is too large for the size. /// This must not be used directly for values that may require relocation. fn write_sdata(&mut self, val: i64, size: u8) -> Result<()> { match size { 1 => { let write_val = val as i8; if val != i64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u8(write_val as u8) } 2 => { let write_val = val as i16; if val != i64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u16(write_val as u16) } 4 => { let write_val = val as i32; if val != i64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u32(write_val as u32) } 8 => self.write_u64(val as u64), otherwise => Err(Error::UnsupportedWordSize(otherwise)), } } /// Write a word of the given size at the given offset. /// /// Returns an error if the value is too large for the size. /// This must not be used directly for values that may require relocation. fn write_udata_at(&mut self, offset: usize, val: u64, size: u8) -> Result<()> { match size { 1 => { let write_val = val as u8; if val != u64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u8_at(offset, write_val) } 2 => { let write_val = val as u16; if val != u64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u16_at(offset, write_val) } 4 => { let write_val = val as u32; if val != u64::from(write_val) { return Err(Error::ValueTooLarge); } self.write_u32_at(offset, write_val) } 8 => self.write_u64_at(offset, val), otherwise => Err(Error::UnsupportedWordSize(otherwise)), } } /// Write an unsigned LEB128 encoded integer. fn write_uleb128(&mut self, val: u64) -> Result<()> { let mut bytes = [0u8; 10]; // bytes is long enough so this will never fail. let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); self.write(&bytes[..len]) } /// Read an unsigned LEB128 encoded integer. fn write_sleb128(&mut self, val: i64) -> Result<()> { let mut bytes = [0u8; 10]; // bytes is long enough so this will never fail. let len = leb128::write::signed(&mut { &mut bytes[..] }, val).unwrap(); self.write(&bytes[..len]) } /// Write an initial length according to the given DWARF format. /// /// This will only write a length of zero, since the length isn't /// known yet, and a subsequent call to `write_initial_length_at` /// will write the actual length. fn write_initial_length(&mut self, format: Format) -> Result { if format == Format::Dwarf64 { self.write_u32(0xffff_ffff)?; } let offset = InitialLengthOffset(self.len()); self.write_udata(0, format.word_size())?; Ok(offset) } /// Write an initial length at the given offset according to the given DWARF format. /// /// `write_initial_length` must have previously returned the offset. fn write_initial_length_at( &mut self, offset: InitialLengthOffset, length: u64, format: Format, ) -> Result<()> { self.write_udata_at(offset.0, length, format.word_size()) } } /// The offset at which an initial length should be written. #[derive(Debug, Clone, Copy)] pub struct InitialLengthOffset(usize); #[cfg(test)] mod tests { use super::*; use crate::write; use crate::{BigEndian, LittleEndian}; #[test] fn test_writer() { let mut w = write::EndianVec::new(LittleEndian); w.write_address(Address::Constant(0x1122_3344), 4).unwrap(); assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); assert_eq!( w.write_address( Address::Symbol { symbol: 0, addend: 0 }, 4 ), Err(Error::InvalidAddress) ); let mut w = write::EndianVec::new(LittleEndian); w.write_offset(0x1122_3344, SectionId::DebugInfo, 4) .unwrap(); assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); w.write_offset_at(1, 0x5566, SectionId::DebugInfo, 2) .unwrap(); assert_eq!(w.slice(), &[0x44, 0x66, 0x55, 0x11]); let mut w = write::EndianVec::new(LittleEndian); w.write_u8(0x11).unwrap(); w.write_u16(0x2233).unwrap(); w.write_u32(0x4455_6677).unwrap(); w.write_u64(0x8081_8283_8485_8687).unwrap(); #[rustfmt::skip] assert_eq!(w.slice(), &[ 0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, ]); w.write_u8_at(14, 0x11).unwrap(); w.write_u16_at(12, 0x2233).unwrap(); w.write_u32_at(8, 0x4455_6677).unwrap(); w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); #[rustfmt::skip] assert_eq!(w.slice(), &[ 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, ]); let mut w = write::EndianVec::new(BigEndian); w.write_u8(0x11).unwrap(); w.write_u16(0x2233).unwrap(); w.write_u32(0x4455_6677).unwrap(); w.write_u64(0x8081_8283_8485_8687).unwrap(); #[rustfmt::skip] assert_eq!(w.slice(), &[ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, ]); w.write_u8_at(14, 0x11).unwrap(); w.write_u16_at(12, 0x2233).unwrap(); w.write_u32_at(8, 0x4455_6677).unwrap(); w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); #[rustfmt::skip] assert_eq!(w.slice(), &[ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x44, 0x55, 0x66, 0x77, 0x22, 0x33, 0x11, ]); let mut w = write::EndianVec::new(LittleEndian); w.write_udata(0x11, 1).unwrap(); w.write_udata(0x2233, 2).unwrap(); w.write_udata(0x4455_6677, 4).unwrap(); w.write_udata(0x8081_8283_8485_8687, 8).unwrap(); #[rustfmt::skip] assert_eq!(w.slice(), &[ 0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, ]); assert_eq!(w.write_udata(0x100, 1), Err(Error::ValueTooLarge)); assert_eq!(w.write_udata(0x1_0000, 2), Err(Error::ValueTooLarge)); assert_eq!(w.write_udata(0x1_0000_0000, 4), Err(Error::ValueTooLarge)); assert_eq!(w.write_udata(0x00, 3), Err(Error::UnsupportedWordSize(3))); w.write_udata_at(14, 0x11, 1).unwrap(); w.write_udata_at(12, 0x2233, 2).unwrap(); w.write_udata_at(8, 0x4455_6677, 4).unwrap(); w.write_udata_at(0, 0x8081_8283_8485_8687, 8).unwrap(); #[rustfmt::skip] assert_eq!(w.slice(), &[ 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, ]); assert_eq!(w.write_udata_at(0, 0x100, 1), Err(Error::ValueTooLarge)); assert_eq!(w.write_udata_at(0, 0x1_0000, 2), Err(Error::ValueTooLarge)); assert_eq!( w.write_udata_at(0, 0x1_0000_0000, 4), Err(Error::ValueTooLarge) ); assert_eq!( w.write_udata_at(0, 0x00, 3), Err(Error::UnsupportedWordSize(3)) ); let mut w = write::EndianVec::new(LittleEndian); w.write_uleb128(0).unwrap(); assert_eq!(w.slice(), &[0]); let mut w = write::EndianVec::new(LittleEndian); w.write_uleb128(u64::MAX).unwrap(); assert_eq!( w.slice(), &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1] ); let mut w = write::EndianVec::new(LittleEndian); w.write_sleb128(0).unwrap(); assert_eq!(w.slice(), &[0]); let mut w = write::EndianVec::new(LittleEndian); w.write_sleb128(i64::MAX).unwrap(); assert_eq!( w.slice(), &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0] ); let mut w = write::EndianVec::new(LittleEndian); w.write_sleb128(i64::MIN).unwrap(); assert_eq!( w.slice(), &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f] ); let mut w = write::EndianVec::new(LittleEndian); let offset = w.write_initial_length(Format::Dwarf32).unwrap(); assert_eq!(w.slice(), &[0, 0, 0, 0]); w.write_initial_length_at(offset, 0x1122_3344, Format::Dwarf32) .unwrap(); assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); assert_eq!( w.write_initial_length_at(offset, 0x1_0000_0000, Format::Dwarf32), Err(Error::ValueTooLarge) ); let mut w = write::EndianVec::new(LittleEndian); let offset = w.write_initial_length(Format::Dwarf64).unwrap(); assert_eq!(w.slice(), &[0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0]); w.write_initial_length_at(offset, 0x1122_3344_5566_7788, Format::Dwarf64) .unwrap(); assert_eq!( w.slice(), &[0xff, 0xff, 0xff, 0xff, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11] ); } }