summaryrefslogtreecommitdiffstats
path: root/vendor/gimli
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/gimli
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gimli')
-rw-r--r--vendor/gimli/.cargo-checksum.json1
-rw-r--r--vendor/gimli/CHANGELOG.md851
-rw-r--r--vendor/gimli/CONTRIBUTING.md137
-rw-r--r--vendor/gimli/Cargo.lock381
-rw-r--r--vendor/gimli/Cargo.toml115
-rw-r--r--vendor/gimli/LICENSE-APACHE201
-rw-r--r--vendor/gimli/LICENSE-MIT25
-rw-r--r--vendor/gimli/README.md78
-rw-r--r--vendor/gimli/benches/bench.rs807
-rw-r--r--vendor/gimli/examples/dwarf-validate.rs267
-rw-r--r--vendor/gimli/examples/dwarfdump.rs2398
-rw-r--r--vendor/gimli/examples/simple.rs67
-rw-r--r--vendor/gimli/examples/simple_line.rs99
-rw-r--r--vendor/gimli/fixtures/self/README.md147
-rw-r--r--vendor/gimli/fixtures/self/debug_abbrevbin0 -> 1865 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_arangesbin0 -> 16304 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_infobin0 -> 392832 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_inlinedbin0 -> 25062 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_linebin0 -> 109251 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_locbin0 -> 283588 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_pubnamesbin0 -> 138556 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_pubtypesbin0 -> 52984 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_rangesbin0 -> 186016 bytes
-rw-r--r--vendor/gimli/fixtures/self/debug_strbin0 -> 145794 bytes
-rw-r--r--vendor/gimli/fixtures/self/eh_framebin0 -> 147656 bytes
-rw-r--r--vendor/gimli/fixtures/self/eh_frame_hdrbin0 -> 108732 bytes
-rw-r--r--vendor/gimli/rustfmt.toml0
-rw-r--r--vendor/gimli/src/arch.rs603
-rw-r--r--vendor/gimli/src/common.rs363
-rw-r--r--vendor/gimli/src/constants.rs1425
-rw-r--r--vendor/gimli/src/endianity.rs256
-rw-r--r--vendor/gimli/src/leb128.rs612
-rw-r--r--vendor/gimli/src/lib.rs76
-rw-r--r--vendor/gimli/src/read/abbrev.rs996
-rw-r--r--vendor/gimli/src/read/addr.rs128
-rw-r--r--vendor/gimli/src/read/aranges.rs660
-rw-r--r--vendor/gimli/src/read/cfi.rs7463
-rw-r--r--vendor/gimli/src/read/dwarf.rs1143
-rw-r--r--vendor/gimli/src/read/endian_reader.rs639
-rw-r--r--vendor/gimli/src/read/endian_slice.rs350
-rw-r--r--vendor/gimli/src/read/index.rs535
-rw-r--r--vendor/gimli/src/read/line.rs3030
-rw-r--r--vendor/gimli/src/read/lists.rs67
-rw-r--r--vendor/gimli/src/read/loclists.rs1514
-rw-r--r--vendor/gimli/src/read/lookup.rs202
-rw-r--r--vendor/gimli/src/read/mod.rs821
-rw-r--r--vendor/gimli/src/read/op.rs4095
-rw-r--r--vendor/gimli/src/read/pubnames.rs141
-rw-r--r--vendor/gimli/src/read/pubtypes.rs141
-rw-r--r--vendor/gimli/src/read/reader.rs502
-rw-r--r--vendor/gimli/src/read/rnglists.rs1354
-rw-r--r--vendor/gimli/src/read/str.rs321
-rw-r--r--vendor/gimli/src/read/unit.rs6146
-rw-r--r--vendor/gimli/src/read/util.rs250
-rw-r--r--vendor/gimli/src/read/value.rs1623
-rw-r--r--vendor/gimli/src/test_util.rs53
-rw-r--r--vendor/gimli/src/write/abbrev.rs188
-rw-r--r--vendor/gimli/src/write/cfi.rs1012
-rw-r--r--vendor/gimli/src/write/dwarf.rs138
-rw-r--r--vendor/gimli/src/write/endian_vec.rs117
-rw-r--r--vendor/gimli/src/write/line.rs1960
-rw-r--r--vendor/gimli/src/write/loc.rs549
-rw-r--r--vendor/gimli/src/write/mod.rs425
-rw-r--r--vendor/gimli/src/write/op.rs1617
-rw-r--r--vendor/gimli/src/write/range.rs415
-rw-r--r--vendor/gimli/src/write/section.rs172
-rw-r--r--vendor/gimli/src/write/str.rs172
-rw-r--r--vendor/gimli/src/write/unit.rs3157
-rw-r--r--vendor/gimli/src/write/writer.rs497
-rw-r--r--vendor/gimli/tests/convert_self.rs158
-rwxr-xr-xvendor/gimli/tests/parse_self.rs431
71 files changed, 52091 insertions, 0 deletions
diff --git a/vendor/gimli/.cargo-checksum.json b/vendor/gimli/.cargo-checksum.json
new file mode 100644
index 000000000..5b855e30c
--- /dev/null
+++ b/vendor/gimli/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"13bb20c4d7f171cdaf835093237311af52dd979091cbe2c46a6f978c9d783721","CONTRIBUTING.md":"5f513ec06013e4f6f097e9c9492da5a47b9f25c94c6ecadfb655a77405fe912c","Cargo.lock":"9961bcba2db19ce16584ca5cb089568de7ffcbc9668cba37fc3dd851b2e58e56","Cargo.toml":"d607f683bfe0d69d932c331701d54bdf429dbe5280b7cd3b1a5478d9dd9cdc79","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.md":"03ac1a78a14682d70da4d485d3fa01d0ad819b65b6aed50359ee72af43158ca9","benches/bench.rs":"e0045b989683794951563aa91b37069b2f6ae55f95e288d23f5c984b46e3a7eb","examples/dwarf-validate.rs":"8a322dc48a04bff33a759030f399ca9972ddb103e63851cbb9b8c9672095f645","examples/dwarfdump.rs":"3ef93684d0b417da7dc89751e2f5d1c297b3574b478848cb79b550d4ecd83222","examples/simple.rs":"684dc9785f4aa0714fcb91a3ae1bb7c8612c9b12969be47fe1327447a2101d1c","examples/simple_line.rs":"ec5fa47c1ab019c6cd9d525223f947533f6d65d6add3131a0c40ac18fc1297db","fixtures/self/README.md":"557cd710240a14fdaa5842b216de57f2ed481151b640af09d6877984b3b2389f","fixtures/self/debug_abbrev":"7c0faa940d9c68d196d03ad55a20e5c746040fa428ff323277fa381deff82bba","fixtures/self/debug_aranges":"8c2aeb2335f61d04ecb7b747070d24f83a6517cbee79dc5c96d97fb6c53d6b6d","fixtures/self/debug_info":"42028a5983006e0703f9ca9515cd27d891ae4af70279fae5011d547f581e2661","fixtures/self/debug_inlined":"89d9516f06ff835621936037f5884fc56712bf304c1dcde52251ddd510fe8710","fixtures/self/debug_line":"b29aebcca3b38bb2bb8aa708cbe74a0dce5a3b0c18916b63d6d17282c017bec7","fixtures/self/debug_loc":"8906ccb9c204f233eb74c1d069dee97a19d18c2051f9147795d7b5364a9266aa","fixtures/self/debug_pubnames":"cf58e237f89c68afba724597fa7e260448636b45f2e69dc6f1bfe34006e27c48","fixtures/self/debug_pubtypes":"d43c1bed71c9d14d1683294cdc1833f069cf131d6e95ee808547919b4f352d81","fixtures/self/debug_ranges":"6d765ac18d33accd89186d077eeb505cbdf97d990c9201d63d9463cd7787ce7a","fixtures/self/debug_str":"9ed904b68eee77b8558b80b3b7ca03e8527f6c64483e9d6d845f40270eb21183","fixtures/self/eh_frame":"6dc3d84351cac42cf73d03452fbb532470dd94d08715154c48417e3f62095f17","fixtures/self/eh_frame_hdr":"afba7a0aa233c9a8c81c986495bd2505164844adb93272d6bc0c9e592e684716","rustfmt.toml":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/arch.rs":"1c4cb3e2a322f3f42fe0b82875c9d0ce060d9af2388990139bdce9a4487c32da","src/common.rs":"392f52a58db6101187ca5525bbeafca9bda2342debd058cabca37350cd9db619","src/constants.rs":"ca3169f7a45ff1b6a504966c5627ad5c99193e2d0ec06dc9edfdf37617198093","src/endianity.rs":"1f7e62ae34f540c06bedf1e7948739211556eea7dd83731a5ca52c7d687ed0fc","src/leb128.rs":"996d5c79d027f97c010ca487bc4ff5f8265f4b9e63d62b4e4fa291383c259ee9","src/lib.rs":"427d747b0af62894e3c5ea20aecad33e5f458bec0b50fbe584e1e6aa1e5eb4f8","src/read/abbrev.rs":"c49c47ff607435c9d0b702faf366068cd1e95d175be79358b1e21244151eeab6","src/read/addr.rs":"f63f289edf889e87107bb2090fb1c50b48af7015f31b7c39c3d6ea09630a38e9","src/read/aranges.rs":"dee9500e0428bc34fca58b2dda85aef6bf0293785c4077c1a4c144109e9d87c9","src/read/cfi.rs":"e4dacfb576ecbe9248cbea3c3c682b6fd835d3843bca68bb3e18dbda55728643","src/read/dwarf.rs":"a39c24429b437ae3a1cd17bae2f01c973c9ce39f7b5f2b3435982d6860944e0e","src/read/endian_reader.rs":"320983a859c2bb0dd44a3e6fae55ff0a84dba3fa80c2edbc64aa8135c44eddf0","src/read/endian_slice.rs":"ae1c52499728f6a85648f1bf87c02dcf43bebecb5ad4e835a1246938ba4338bf","src/read/index.rs":"e79b8d591b8e2007a37f5ea85a6d71b69d56ca3739a85cf7bf361724c5b829fa","src/read/line.rs":"47ca5ebb8bd19000045954686d3320b838e9404cba917ec60525f11ea0d87095","src/read/lists.rs":"e473ff419feed9756289e245b7879bd89e7f19098a53162fe6773fac496ae5bc","src/read/loclists.rs":"2a5655c53fb2bf5cfe2df373210217edaa06e4d3addf27df0f724100cbfbe43b","src/read/lookup.rs":"0cf89ba12b9d48b1fe035dd3a497730323acb9427a9457abbc2f7c58c4c71165","src/read/mod.rs":"7ea1d01906db92a31a0915b8d2a84776b2a1b2a6587aac8a4acf5ecc48c019b6","src/read/op.rs":"2de049cdcff6c0a324c5737d3fc93431c729554b3bf38e09777b855d7058b29f","src/read/pubnames.rs":"701c1279aef596ed8eff13f19a5803f9e1070afa20c9bafbf29659d4c294edd4","src/read/pubtypes.rs":"6250112d63120ed283698cb42189b127f624fb453abb1222dfa75fe103ad077e","src/read/reader.rs":"b10ff3e77b54347e96b1f3cff30da104dfdd0c4d7a55b672950788f1f1ae3478","src/read/rnglists.rs":"e7426fa1564cbd7e84871ddd741d6a7f016596633f1ffa097885b6e685fc8da2","src/read/str.rs":"932971a6f6f3453685dbd33ff3c2d31a10820b989a209bdfeca3e8c5012cc4b8","src/read/unit.rs":"305e834d7c14e6855beab411076cb7db2615373fd761396ec253250bb0381a59","src/read/util.rs":"480acb9a1fbae7ce935dd1d1307e6a0ab222e009b63ae7817b4bcdcccb9a9ec4","src/read/value.rs":"9a961a49c43bd05061fce1765bda0da049f26420d1be2ed0584de7d1597ab836","src/test_util.rs":"291eefa6b51c6d934ba2f4a4c9bc7c403046fc1cccf4d43487820f0154bb89e2","src/write/abbrev.rs":"fa02163389e92e804d139cf84f833ab6af932083f0eb2d74464b4a70bd3237ff","src/write/cfi.rs":"5d36c6978d2bda09921f5b71b764a75a58e819551471fb44db23ce5db7c4a8e3","src/write/dwarf.rs":"8a1a0893e31134ad68993994594f3024ad0c8af7c1188b29e0ffc26b42edef21","src/write/endian_vec.rs":"1d5811986648816a677580b22630f5059757a381487d73e9adbb3008c9ae0c58","src/write/line.rs":"df7d2082c71b5e523cd52745700aae3dcfa5800f0b280e831ef5d8eb8035d6a7","src/write/loc.rs":"bb5b750c04f6603e18225db72652ea00239234ba674a8a8627c99d4ab07b47a9","src/write/mod.rs":"d8aa1da854cdee629d470d00d87e00dc6998e4bec1ca951f8d2f277730ab9d69","src/write/op.rs":"fb99e95631e24e46eaddef393281ed4f4c56ebc0713a8cbe1683893f7bdde8c6","src/write/range.rs":"5bac01e372c08e3cc19e1e07e40492d8214cdfa8881737920cb792f4aa2ba80b","src/write/section.rs":"3ce781d5e82ba365ff54fdd36e0ef58c58a2215b09a8861eb0b038efac82b77f","src/write/str.rs":"4850cc2fee55980f9cbb6b4169f9861ab9d05c2b28a85c2b790480b83a66f514","src/write/unit.rs":"213c881736f8c87fcb2f921e379791eaba2915e8d077139965a9c6211001fe44","src/write/writer.rs":"304181287f90445bbfb33349c26b34bd87002d6844fc5686bfc0756fd0a1ecd8","tests/convert_self.rs":"8fba3599ac892a704cbcd5aed53eaef51b040043da04f85f002c597ee7549046","tests/parse_self.rs":"f2da1c7daef7139545c9367c2f26199e8b4623b31d4ec6480ddd851e6980f2dc"},"package":"78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"} \ No newline at end of file
diff --git a/vendor/gimli/CHANGELOG.md b/vendor/gimli/CHANGELOG.md
new file mode 100644
index 000000000..da2ab0fe7
--- /dev/null
+++ b/vendor/gimli/CHANGELOG.md
@@ -0,0 +1,851 @@
+# `gimli` Change Log
+
+--------------------------------------------------------------------------------
+
+## 0.26.1
+
+Released 2021/11/02.
+
+### Changed
+
+* Fixed segmentation fault in `ArrayVec<Vec<T>>::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<UnwindContext>` 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<NonZeroU64>`.
+ 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 `<read::Error as Display>`
+ 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 evaluting 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<T>` 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
diff --git a/vendor/gimli/CONTRIBUTING.md b/vendor/gimli/CONTRIBUTING.md
new file mode 100644
index 000000000..4f9e574ce
--- /dev/null
+++ b/vendor/gimli/CONTRIBUTING.md
@@ -0,0 +1,137 @@
+# Contributing to `gimli`
+
+Hi! We'd love to have your contributions! If you want help or mentorship, reach
+out to us in a GitHub issue, or ping `fitzgen` in `#rust` on `irc.mozilla.org`.
+
+* [Code of Conduct](#coc)
+* [Filing an Issue](#issues)
+* [Building `gimli`](#building)
+* [Testing `gimli`](#testing)
+ * [Test Coverage](#coverage)
+ * [Using `test-assembler`](#test-assembler)
+ * [Fuzzing](#fuzzing)
+* [Benchmarking](#benchmarking)
+* [Style](#style)
+
+## <a id="coc"></a> Code of Conduct
+
+We abide by the
+[Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html) and ask
+that you do as well.
+
+## <a id="issues"></a> Filing an Issue
+
+Think you've found a bug? File an issue! To help us understand and reproduce the
+issue, provide us with:
+
+* The (preferably minimal) test case
+* Steps to reproduce the issue using the test case
+* The expected result of following those steps
+* The actual result of following those steps
+
+Definitely file an issue if you see an unexpected panic originating from within
+`gimli`! `gimli` should never panic unless it is explicitly documented to panic
+in the specific circumstances provided.
+
+## <a id="building"></a> Building `gimli`
+
+`gimli` should always build on stable `rustc`, but we recommend using
+[`rustup`](https://www.rustup.rs/) so you can switch to nightly `rustc` and run
+benchmarks.
+
+To build `gimli`:
+
+```
+$ cargo build
+```
+
+## <a id="testing"></a> Testing `gimli`
+
+Run the tests with `cargo`:
+
+```
+$ cargo test
+```
+
+### <a id="coverage"></a> Test Coverage
+
+If you have `kcov` installed under linux, then you can generate code coverage
+results using the `coverage` script in the root of the repository, and view them
+at `target/kcov/index.html`. Otherwise you can create a pull request and view
+the coverage results on coveralls.io.
+
+```
+$ ./coverage
+```
+
+The ideal we aim to reach is having our unit tests exercise every branch in
+`gimli`. We allow an exception for branches which propagate errors inside a
+`try!(..)` invocation, but we *do* want to exercise the original error paths.
+
+Pull requests adding new code should ensure that this ideal is met.
+
+At the time of writing we have 94% test coverage according to our coveralls.io
+continuous integration. That number should generally stay the same or go up ;)
+This is a bit subjective, because -.001% is just noise and doesn't matter.
+
+### <a id="test-assembler"></a> Using `test-assembler`
+
+We use the awesome
+[`test-assembler`](https://github.com/luser/rust-test-assembler) crate to
+construct binary test data. It makes building complex test cases readable.
+
+[Here is an example usage in `gimli`](https://github.com/gimli-rs/gimli/blob/156451f3fe6eeb2fa62b84b362c33fcb176e1171/src/loc.rs#L263)
+
+### <a id="fuzzing"></a> Fuzzing
+
+First, install `cargo fuzz`:
+
+```
+$ cargo install cargo-fuzz
+```
+
+Optionally, [set up the corpora for our fuzz targets by following these
+instructions](https://github.com/gimli-rs/gimli-libfuzzer-corpora/blob/master/README.md#using-these-corpora).
+
+Finally, run a fuzz target! In this case, we are running the `eh_frame` fuzz
+target:
+
+```
+$ cargo fuzz run eh_frame
+```
+
+The fuzz target definitions live in `fuzz/fuzz_targets/*`. You can add new ones
+via `cargo fuzz add <my_new_target>`.
+
+## <a id="benchmarking"></a> Benchmarking
+
+The benchmarks require nightly `rustc`, so use `rustup`:
+
+```
+$ rustup run nightly cargo bench
+```
+
+We aim to be the fastest DWARF library. Period.
+
+Please provide before and after benchmark results with your pull requests. You
+may also find [`cargo benchcmp`](https://github.com/BurntSushi/cargo-benchcmp)
+handy for comparing results.
+
+Pull requests adding `#[bench]` micro-benchmarks that exercise a new edge case
+are very welcome!
+
+## <a id="style"></a> Style
+
+We use `rustfmt` to automatically format and style all of our code.
+
+To install `rustfmt`:
+
+```
+$ rustup component add rustfmt-preview
+```
+
+To run `rustfmt` on `gimli`:
+
+```
+$ cargo fmt
+```
diff --git a/vendor/gimli/Cargo.lock b/vendor/gimli/Cargo.lock
new file mode 100644
index 000000000..d55f428d0
--- /dev/null
+++ b/vendor/gimli/Cargo.lock
@@ -0,0 +1,381 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "compiler_builtins"
+version = "0.1.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3587b3669d6f2c1cfd34c475272dabcfef29d52703933f6f72ebb36d6bd81a97"
+
+[[package]]
+name = "crc32fast"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845"
+dependencies = [
+ "cfg-if",
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+ "lazy_static",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
+dependencies = [
+ "cfg-if",
+ "lazy_static",
+]
+
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
+[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
+[[package]]
+name = "flate2"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
+dependencies = [
+ "cfg-if",
+ "crc32fast",
+ "libc",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "gimli"
+version = "0.26.1"
+dependencies = [
+ "compiler_builtins",
+ "crossbeam",
+ "fallible-iterator",
+ "getopts",
+ "indexmap",
+ "memmap",
+ "num_cpus",
+ "object",
+ "rayon",
+ "regex",
+ "rustc-std-workspace-alloc",
+ "rustc-std-workspace-core",
+ "stable_deref_trait",
+ "test-assembler",
+ "typed-arena",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "memmap"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
+dependencies = [
+ "flate2",
+ "memchr",
+ "wasmparser",
+]
+
+[[package]]
+name = "rayon"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
+dependencies = [
+ "autocfg",
+ "crossbeam-deque",
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "lazy_static",
+ "num_cpus",
+]
+
+[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "rustc-std-workspace-alloc"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff66d57013a5686e1917ed6a025d54dd591fcda71a41fe07edf4d16726aefa86"
+
+[[package]]
+name = "rustc-std-workspace-core"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "test-assembler"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a6da51de149453f5c43fa67d5e73cccd75b3c5727a38a2f18c5f3c47f2db582"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "typed-arena"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "wasmparser"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/vendor/gimli/Cargo.toml b/vendor/gimli/Cargo.toml
new file mode 100644
index 000000000..d5f283c80
--- /dev/null
+++ b/vendor/gimli/Cargo.toml
@@ -0,0 +1,115 @@
+# 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 believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "gimli"
+version = "0.26.1"
+exclude = ["/ci/*", "/releases/*", "/.github"]
+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 = "Apache-2.0/MIT"
+repository = "https://github.com/gimli-rs/gimli"
+[profile.bench]
+codegen-units = 1
+debug = true
+split-debuginfo = "packed"
+
+[profile.test]
+split-debuginfo = "packed"
+
+[[example]]
+name = "simple"
+required-features = ["read"]
+
+[[example]]
+name = "simple_line"
+required-features = ["read"]
+
+[[example]]
+name = "dwarfdump"
+required-features = ["read", "std"]
+
+[[example]]
+name = "dwarf-validate"
+required-features = ["read", "std"]
+[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.2.0"
+optional = true
+default-features = false
+
+[dependencies.indexmap]
+version = "1.0.2"
+optional = true
+
+[dependencies.stable_deref_trait]
+version = "1.1.0"
+optional = true
+default-features = false
+[dev-dependencies.crossbeam]
+version = "0.8"
+
+[dev-dependencies.getopts]
+version = "0.2"
+
+[dev-dependencies.memmap]
+version = "0.7"
+
+[dev-dependencies.num_cpus]
+version = "1"
+
+[dev-dependencies.object]
+version = "0.27.1"
+features = ["wasm"]
+
+[dev-dependencies.rayon]
+version = "1.0"
+
+[dev-dependencies.regex]
+version = "1"
+
+[dev-dependencies.test-assembler]
+version = "0.1.3"
+
+[dev-dependencies.typed-arena]
+version = "2"
+
+[features]
+default = ["read", "write", "std", "fallible-iterator", "endian-reader"]
+endian-reader = ["read", "stable_deref_trait"]
+read = ["read-core"]
+read-core = []
+rustc-dep-of-std = ["core", "alloc", "compiler_builtins"]
+std = ["fallible-iterator/std", "stable_deref_trait/std"]
+write = ["indexmap"]
+[badges.coveralls]
+repository = "gimli-rs/gimli"
+
+[badges.travis-ci]
+repository = "gimli-rs/gimli"
diff --git a/vendor/gimli/LICENSE-APACHE b/vendor/gimli/LICENSE-APACHE
new file mode 100644
index 000000000..16fe87b06
--- /dev/null
+++ b/vendor/gimli/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/gimli/LICENSE-MIT b/vendor/gimli/LICENSE-MIT
new file mode 100644
index 000000000..e69282e38
--- /dev/null
+++ b/vendor/gimli/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (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.
diff --git a/vendor/gimli/README.md b/vendor/gimli/README.md
new file mode 100644
index 000000000..56cee2531
--- /dev/null
+++ b/vendor/gimli/README.md
@@ -0,0 +1,78 @@
+# `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 blazing fast library for consuming the
+[DWARF debugging format](http://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
+ [`examples/`](./examples) directory for usage with `gimli`.
+
+## Install
+
+Add this to your `Cargo.toml`:
+
+```toml
+[dependencies]
+gimli = "0.26.1"
+```
+
+The minimum supported Rust version is 1.42.0.
+
+## Documentation
+
+* [Documentation on docs.rs](https://docs.rs/gimli/)
+
+* Example programs:
+
+ * [A simple `.debug_info` parser](./examples/simple.rs)
+
+ * [A simple `.debug_line` parser](./examples/simple_line.rs)
+
+ * [A `dwarfdump` clone](./examples/dwarfdump.rs)
+
+ * [An `addr2line` clone](https://github.com/gimli-rs/addr2line)
+
+ * [`ddbug`](https://github.com/philipc/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`](./examples/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.
diff --git a/vendor/gimli/benches/bench.rs b/vendor/gimli/benches/bench.rs
new file mode 100644
index 000000000..fb29df77c
--- /dev/null
+++ b/vendor/gimli/benches/bench.rs
@@ -0,0 +1,807 @@
+#![feature(test)]
+
+extern crate test;
+
+use gimli::{
+ AttributeValue, DebugAbbrev, DebugAddr, DebugAddrBase, DebugAranges, DebugInfo, DebugLine,
+ DebugLineOffset, DebugLoc, DebugLocLists, DebugPubNames, DebugPubTypes, DebugRanges,
+ DebugRngLists, Encoding, EndianSlice, EntriesTreeNode, Expression, LittleEndian, LocationLists,
+ Operation, RangeLists, RangeListsOffset, Reader, ReaderOffset,
+};
+use std::env;
+use std::fs::File;
+use std::io::Read;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+pub fn read_section(section: &str) -> Vec<u8> {
+ let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()));
+ path.push("./fixtures/self/");
+ path.push(section);
+
+ assert!(path.is_file());
+ let mut file = File::open(path).unwrap();
+
+ let mut buf = Vec::new();
+ file.read_to_end(&mut buf).unwrap();
+ buf
+}
+
+#[bench]
+fn bench_parsing_debug_abbrev(b: &mut test::Bencher) {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+ let unit = debug_info
+ .units()
+ .next()
+ .expect("Should have at least one compilation unit")
+ .expect("And it should parse OK");
+
+ let debug_abbrev = read_section("debug_abbrev");
+
+ b.iter(|| {
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+ test::black_box(
+ unit.abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations"),
+ );
+ });
+}
+
+#[inline]
+fn impl_bench_parsing_debug_info<R: Reader>(
+ debug_info: DebugInfo<R>,
+ debug_abbrev: DebugAbbrev<R>,
+) {
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") {
+ let mut attrs = entry.attrs();
+ loop {
+ match attrs.next() {
+ Ok(Some(ref attr)) => {
+ test::black_box(attr);
+ }
+ Ok(None) => break,
+ e @ Err(_) => {
+ e.expect("Should parse entry's attribute");
+ }
+ }
+ }
+ }
+ }
+}
+
+#[bench]
+fn bench_parsing_debug_info(b: &mut test::Bencher) {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ b.iter(|| impl_bench_parsing_debug_info(debug_info, debug_abbrev));
+}
+
+#[bench]
+fn bench_parsing_debug_info_with_endian_rc_slice(b: &mut test::Bencher) {
+ let debug_info = read_section("debug_info");
+ let debug_info = Rc::from(&debug_info[..]);
+ let debug_info = gimli::EndianRcSlice::new(debug_info, LittleEndian);
+ let debug_info = DebugInfo::from(debug_info);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = Rc::from(&debug_abbrev[..]);
+ let debug_abbrev = gimli::EndianRcSlice::new(debug_abbrev, LittleEndian);
+ let debug_abbrev = DebugAbbrev::from(debug_abbrev);
+
+ b.iter(|| impl_bench_parsing_debug_info(debug_info.clone(), debug_abbrev.clone()));
+}
+
+#[bench]
+fn bench_parsing_debug_info_tree(b: &mut test::Bencher) {
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_info = read_section("debug_info");
+
+ b.iter(|| {
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut tree = unit
+ .entries_tree(&abbrevs, None)
+ .expect("Should have entries tree");
+ let root = tree.root().expect("Should parse root entry");
+ parse_debug_info_tree(root);
+ }
+ });
+}
+
+fn parse_debug_info_tree<R: Reader>(node: EntriesTreeNode<R>) {
+ {
+ let mut attrs = node.entry().attrs();
+ loop {
+ match attrs.next() {
+ Ok(Some(ref attr)) => {
+ test::black_box(attr);
+ }
+ Ok(None) => break,
+ e @ Err(_) => {
+ e.expect("Should parse entry's attribute");
+ }
+ }
+ }
+ }
+ let mut children = node.children();
+ while let Some(child) = children.next().expect("Should parse child entry") {
+ parse_debug_info_tree(child);
+ }
+}
+
+#[bench]
+fn bench_parsing_debug_info_raw(b: &mut test::Bencher) {
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_info = read_section("debug_info");
+
+ b.iter(|| {
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut raw = unit
+ .entries_raw(&abbrevs, None)
+ .expect("Should have entries");
+ while !raw.is_empty() {
+ if let Some(abbrev) = raw
+ .read_abbreviation()
+ .expect("Should parse abbreviation code")
+ {
+ for spec in abbrev.attributes().iter().cloned() {
+ match raw.read_attribute(spec) {
+ Ok(ref attr) => {
+ test::black_box(attr);
+ }
+ e @ Err(_) => {
+ e.expect("Should parse attribute");
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+}
+
+#[bench]
+fn bench_parsing_debug_aranges(b: &mut test::Bencher) {
+ let debug_aranges = read_section("debug_aranges");
+ let debug_aranges = DebugAranges::new(&debug_aranges, LittleEndian);
+
+ b.iter(|| {
+ let mut headers = debug_aranges.headers();
+ while let Some(header) = headers.next().expect("Should parse arange header OK") {
+ let mut entries = header.entries();
+ while let Some(arange) = entries.next().expect("Should parse arange entry OK") {
+ test::black_box(arange);
+ }
+ }
+ });
+}
+
+#[bench]
+fn bench_parsing_debug_pubnames(b: &mut test::Bencher) {
+ let debug_pubnames = read_section("debug_pubnames");
+ let debug_pubnames = DebugPubNames::new(&debug_pubnames, LittleEndian);
+
+ b.iter(|| {
+ let mut pubnames = debug_pubnames.items();
+ while let Some(pubname) = pubnames.next().expect("Should parse pubname OK") {
+ test::black_box(pubname);
+ }
+ });
+}
+
+#[bench]
+fn bench_parsing_debug_pubtypes(b: &mut test::Bencher) {
+ let debug_pubtypes = read_section("debug_pubtypes");
+ let debug_pubtypes = DebugPubTypes::new(&debug_pubtypes, LittleEndian);
+
+ b.iter(|| {
+ let mut pubtypes = debug_pubtypes.items();
+ while let Some(pubtype) = pubtypes.next().expect("Should parse pubtype OK") {
+ test::black_box(pubtype);
+ }
+ });
+}
+
+// We happen to know that there is a line number program and header at
+// offset 0 and that address size is 8 bytes. No need to parse DIEs to grab
+// this info off of the compilation units.
+const OFFSET: DebugLineOffset = DebugLineOffset(0);
+const ADDRESS_SIZE: u8 = 8;
+
+#[bench]
+fn bench_parsing_line_number_program_opcodes(b: &mut test::Bencher) {
+ let debug_line = read_section("debug_line");
+ let debug_line = DebugLine::new(&debug_line, LittleEndian);
+
+ b.iter(|| {
+ let program = debug_line
+ .program(OFFSET, ADDRESS_SIZE, None, None)
+ .expect("Should parse line number program header");
+ let header = program.header();
+
+ let mut instructions = header.instructions();
+ while let Some(instruction) = instructions
+ .next_instruction(header)
+ .expect("Should parse instruction")
+ {
+ test::black_box(instruction);
+ }
+ });
+}
+
+#[bench]
+fn bench_executing_line_number_programs(b: &mut test::Bencher) {
+ let debug_line = read_section("debug_line");
+ let debug_line = DebugLine::new(&debug_line, LittleEndian);
+
+ b.iter(|| {
+ let program = debug_line
+ .program(OFFSET, ADDRESS_SIZE, None, None)
+ .expect("Should parse line number program header");
+
+ let mut rows = program.rows();
+ while let Some(row) = rows
+ .next_row()
+ .expect("Should parse and execute all rows in the line number program")
+ {
+ test::black_box(row);
+ }
+ });
+}
+
+#[bench]
+fn bench_parsing_debug_loc(b: &mut test::Bencher) {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let debug_loc = read_section("debug_loc");
+ let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
+ let debug_loclists = DebugLocLists::new(&[], LittleEndian);
+ let loclists = LocationLists::new(debug_loc, debug_loclists);
+
+ let mut offsets = Vec::new();
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ cursor.next_dfs().expect("Should parse next dfs");
+
+ let mut low_pc = 0;
+
+ {
+ let unit_entry = cursor.current().expect("Should have a root entry");
+ let low_pc_attr = unit_entry
+ .attr_value(gimli::DW_AT_low_pc)
+ .expect("Should parse low_pc");
+ if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
+ low_pc = address;
+ }
+ }
+
+ while cursor.next_dfs().expect("Should parse next dfs").is_some() {
+ let entry = cursor.current().expect("Should have a current entry");
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
+ if let gimli::AttributeValue::LocationListsRef(offset) = attr.value() {
+ offsets.push((offset, unit.encoding(), low_pc));
+ }
+ }
+ }
+ }
+
+ b.iter(|| {
+ for &(offset, encoding, base_address) in &*offsets {
+ let mut locs = loclists
+ .locations(offset, encoding, base_address, &debug_addr, debug_addr_base)
+ .expect("Should parse locations OK");
+ while let Some(loc) = locs.next().expect("Should parse next location") {
+ test::black_box(loc);
+ }
+ }
+ });
+}
+
+#[bench]
+fn bench_parsing_debug_ranges(b: &mut test::Bencher) {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let debug_ranges = read_section("debug_ranges");
+ let debug_ranges = DebugRanges::new(&debug_ranges, LittleEndian);
+ let debug_rnglists = DebugRngLists::new(&[], LittleEndian);
+ let rnglists = RangeLists::new(debug_ranges, debug_rnglists);
+
+ let mut offsets = Vec::new();
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ cursor.next_dfs().expect("Should parse next dfs");
+
+ let mut low_pc = 0;
+
+ {
+ let unit_entry = cursor.current().expect("Should have a root entry");
+ let low_pc_attr = unit_entry
+ .attr_value(gimli::DW_AT_low_pc)
+ .expect("Should parse low_pc");
+ if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
+ low_pc = address;
+ }
+ }
+
+ while cursor.next_dfs().expect("Should parse next dfs").is_some() {
+ let entry = cursor.current().expect("Should have a current entry");
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
+ if let gimli::AttributeValue::RangeListsRef(offset) = attr.value() {
+ offsets.push((RangeListsOffset(offset.0), unit.encoding(), low_pc));
+ }
+ }
+ }
+ }
+
+ b.iter(|| {
+ for &(offset, encoding, base_address) in &*offsets {
+ let mut ranges = rnglists
+ .ranges(offset, encoding, base_address, &debug_addr, debug_addr_base)
+ .expect("Should parse ranges OK");
+ while let Some(range) = ranges.next().expect("Should parse next range") {
+ test::black_box(range);
+ }
+ }
+ });
+}
+
+fn debug_info_expressions<R: Reader>(
+ debug_info: &DebugInfo<R>,
+ debug_abbrev: &DebugAbbrev<R>,
+) -> Vec<(Expression<R>, Encoding)> {
+ let mut expressions = Vec::new();
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") {
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
+ if let AttributeValue::Exprloc(expression) = attr.value() {
+ expressions.push((expression, unit.encoding()));
+ }
+ }
+ }
+ }
+
+ expressions
+}
+
+#[bench]
+fn bench_parsing_debug_info_expressions(b: &mut test::Bencher) {
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let expressions = debug_info_expressions(&debug_info, &debug_abbrev);
+
+ b.iter(|| {
+ for &(expression, encoding) in &*expressions {
+ let mut pc = expression.0;
+ while !pc.is_empty() {
+ Operation::parse(&mut pc, encoding).expect("Should parse operation");
+ }
+ }
+ });
+}
+
+#[bench]
+fn bench_evaluating_debug_info_expressions(b: &mut test::Bencher) {
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let expressions = debug_info_expressions(&debug_info, &debug_abbrev);
+
+ b.iter(|| {
+ for &(expression, encoding) in &*expressions {
+ let mut eval = expression.evaluation(encoding);
+ eval.set_initial_value(0);
+ let result = eval.evaluate().expect("Should evaluate expression");
+ test::black_box(result);
+ }
+ });
+}
+
+fn debug_loc_expressions<R: Reader>(
+ debug_info: &DebugInfo<R>,
+ debug_abbrev: &DebugAbbrev<R>,
+ debug_addr: &DebugAddr<R>,
+ loclists: &LocationLists<R>,
+) -> Vec<(Expression<R>, Encoding)> {
+ let debug_addr_base = DebugAddrBase(R::Offset::from_u8(0));
+
+ let mut expressions = Vec::new();
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ cursor.next_dfs().expect("Should parse next dfs");
+
+ let mut low_pc = 0;
+
+ {
+ let unit_entry = cursor.current().expect("Should have a root entry");
+ let low_pc_attr = unit_entry
+ .attr_value(gimli::DW_AT_low_pc)
+ .expect("Should parse low_pc");
+ if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
+ low_pc = address;
+ }
+ }
+
+ while cursor.next_dfs().expect("Should parse next dfs").is_some() {
+ let entry = cursor.current().expect("Should have a current entry");
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
+ if let gimli::AttributeValue::LocationListsRef(offset) = attr.value() {
+ let mut locs = loclists
+ .locations(offset, unit.encoding(), low_pc, debug_addr, debug_addr_base)
+ .expect("Should parse locations OK");
+ while let Some(loc) = locs.next().expect("Should parse next location") {
+ expressions.push((loc.data, unit.encoding()));
+ }
+ }
+ }
+ }
+ }
+
+ expressions
+}
+
+#[bench]
+fn bench_parsing_debug_loc_expressions(b: &mut test::Bencher) {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
+
+ let debug_loc = read_section("debug_loc");
+ let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
+ let debug_loclists = DebugLocLists::new(&[], LittleEndian);
+ let loclists = LocationLists::new(debug_loc, debug_loclists);
+
+ let expressions = debug_loc_expressions(&debug_info, &debug_abbrev, &debug_addr, &loclists);
+
+ b.iter(|| {
+ for &(expression, encoding) in &*expressions {
+ let mut pc = expression.0;
+ while !pc.is_empty() {
+ Operation::parse(&mut pc, encoding).expect("Should parse operation");
+ }
+ }
+ });
+}
+
+#[bench]
+fn bench_evaluating_debug_loc_expressions(b: &mut test::Bencher) {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
+
+ let debug_loc = read_section("debug_loc");
+ let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
+ let debug_loclists = DebugLocLists::new(&[], LittleEndian);
+ let loclists = LocationLists::new(debug_loc, debug_loclists);
+
+ let expressions = debug_loc_expressions(&debug_info, &debug_abbrev, &debug_addr, &loclists);
+
+ b.iter(|| {
+ for &(expression, encoding) in &*expressions {
+ let mut eval = expression.evaluation(encoding);
+ eval.set_initial_value(0);
+ let result = eval.evaluate().expect("Should evaluate expression");
+ test::black_box(result);
+ }
+ });
+}
+
+// See comment above `test_parse_self_eh_frame`.
+#[cfg(target_pointer_width = "64")]
+mod cfi {
+ use super::*;
+ use fallible_iterator::FallibleIterator;
+
+ use gimli::{
+ BaseAddresses, CieOrFde, EhFrame, FrameDescriptionEntry, LittleEndian, UnwindContext,
+ UnwindSection,
+ };
+
+ #[bench]
+ fn iterate_entries_and_do_not_parse_any_fde(b: &mut test::Bencher) {
+ let eh_frame = read_section("eh_frame");
+ let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_got(0)
+ .set_text(0);
+
+ b.iter(|| {
+ let mut entries = eh_frame.entries(&bases);
+ while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
+ test::black_box(entry);
+ }
+ });
+ }
+
+ #[bench]
+ fn iterate_entries_and_parse_every_fde(b: &mut test::Bencher) {
+ let eh_frame = read_section("eh_frame");
+ let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_got(0)
+ .set_text(0);
+
+ b.iter(|| {
+ let mut entries = eh_frame.entries(&bases);
+ while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
+ match entry {
+ CieOrFde::Cie(cie) => {
+ test::black_box(cie);
+ }
+ CieOrFde::Fde(partial) => {
+ let fde = partial
+ .parse(EhFrame::cie_from_offset)
+ .expect("Should be able to get CIE for FED");
+ test::black_box(fde);
+ }
+ };
+ }
+ });
+ }
+
+ #[bench]
+ fn iterate_entries_and_parse_every_fde_and_instructions(b: &mut test::Bencher) {
+ let eh_frame = read_section("eh_frame");
+ let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_got(0)
+ .set_text(0);
+
+ b.iter(|| {
+ let mut entries = eh_frame.entries(&bases);
+ while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
+ match entry {
+ CieOrFde::Cie(cie) => {
+ let mut instrs = cie.instructions(&eh_frame, &bases);
+ while let Some(i) =
+ instrs.next().expect("Can parse next CFI instruction OK")
+ {
+ test::black_box(i);
+ }
+ }
+ CieOrFde::Fde(partial) => {
+ let fde = partial
+ .parse(EhFrame::cie_from_offset)
+ .expect("Should be able to get CIE for FED");
+ let mut instrs = fde.instructions(&eh_frame, &bases);
+ while let Some(i) =
+ instrs.next().expect("Can parse next CFI instruction OK")
+ {
+ test::black_box(i);
+ }
+ }
+ };
+ }
+ });
+ }
+
+ #[bench]
+ fn iterate_entries_evaluate_every_fde(b: &mut test::Bencher) {
+ let eh_frame = read_section("eh_frame");
+ let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_got(0)
+ .set_text(0);
+
+ let mut ctx = Box::new(UnwindContext::new());
+
+ b.iter(|| {
+ let mut entries = eh_frame.entries(&bases);
+ while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
+ match entry {
+ CieOrFde::Cie(_) => {}
+ CieOrFde::Fde(partial) => {
+ let fde = partial
+ .parse(EhFrame::cie_from_offset)
+ .expect("Should be able to get CIE for FED");
+ let mut table = fde
+ .rows(&eh_frame, &bases, &mut ctx)
+ .expect("Should be able to initialize ctx");
+ while let Some(row) =
+ table.next_row().expect("Should get next unwind table row")
+ {
+ test::black_box(row);
+ }
+ }
+ };
+ }
+ });
+ }
+
+ fn instrs_len<R: Reader>(
+ eh_frame: &EhFrame<R>,
+ bases: &BaseAddresses,
+ fde: &FrameDescriptionEntry<R>,
+ ) -> usize {
+ fde.instructions(eh_frame, bases)
+ .fold(0, |count, _| Ok(count + 1))
+ .expect("fold over instructions OK")
+ }
+
+ fn get_fde_with_longest_cfi_instructions<R: Reader>(
+ eh_frame: &EhFrame<R>,
+ bases: &BaseAddresses,
+ ) -> FrameDescriptionEntry<R> {
+ let mut longest: Option<(usize, FrameDescriptionEntry<_>)> = None;
+
+ let mut entries = eh_frame.entries(bases);
+ while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
+ match entry {
+ CieOrFde::Cie(_) => {}
+ CieOrFde::Fde(partial) => {
+ let fde = partial
+ .parse(EhFrame::cie_from_offset)
+ .expect("Should be able to get CIE for FED");
+
+ let this_len = instrs_len(eh_frame, bases, &fde);
+
+ let found_new_longest = match longest {
+ None => true,
+ Some((longest_len, ref _fde)) => this_len > longest_len,
+ };
+
+ if found_new_longest {
+ longest = Some((this_len, fde));
+ }
+ }
+ };
+ }
+
+ longest.expect("At least one FDE in .eh_frame").1
+ }
+
+ #[bench]
+ fn parse_longest_fde_instructions(b: &mut test::Bencher) {
+ let eh_frame = read_section("eh_frame");
+ let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_got(0)
+ .set_text(0);
+ let fde = get_fde_with_longest_cfi_instructions(&eh_frame, &bases);
+
+ b.iter(|| {
+ let mut instrs = fde.instructions(&eh_frame, &bases);
+ while let Some(i) = instrs.next().expect("Should parse instruction OK") {
+ test::black_box(i);
+ }
+ });
+ }
+
+ #[bench]
+ fn eval_longest_fde_instructions_new_ctx_everytime(b: &mut test::Bencher) {
+ let eh_frame = read_section("eh_frame");
+ let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_got(0)
+ .set_text(0);
+ let fde = get_fde_with_longest_cfi_instructions(&eh_frame, &bases);
+
+ b.iter(|| {
+ let mut ctx = Box::new(UnwindContext::new());
+ let mut table = fde
+ .rows(&eh_frame, &bases, &mut ctx)
+ .expect("Should initialize the ctx OK");
+ while let Some(row) = table.next_row().expect("Should get next unwind table row") {
+ test::black_box(row);
+ }
+ });
+ }
+
+ #[bench]
+ fn eval_longest_fde_instructions_same_ctx(b: &mut test::Bencher) {
+ let eh_frame = read_section("eh_frame");
+ let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_got(0)
+ .set_text(0);
+ let fde = get_fde_with_longest_cfi_instructions(&eh_frame, &bases);
+
+ let mut ctx = Box::new(UnwindContext::new());
+
+ b.iter(|| {
+ let mut table = fde
+ .rows(&eh_frame, &bases, &mut ctx)
+ .expect("Should initialize the ctx OK");
+ while let Some(row) = table.next_row().expect("Should get next unwind table row") {
+ test::black_box(row);
+ }
+ });
+ }
+}
diff --git a/vendor/gimli/examples/dwarf-validate.rs b/vendor/gimli/examples/dwarf-validate.rs
new file mode 100644
index 000000000..1eabccc11
--- /dev/null
+++ b/vendor/gimli/examples/dwarf-validate.rs
@@ -0,0 +1,267 @@
+// Allow clippy lints when building without clippy.
+#![allow(unknown_lints)]
+
+use gimli::{AttributeValue, UnitHeader};
+use object::{Object, ObjectSection};
+use rayon::prelude::*;
+use std::borrow::{Borrow, Cow};
+use std::env;
+use std::fs;
+use std::io::{self, BufWriter, Write};
+use std::iter::Iterator;
+use std::path::{Path, PathBuf};
+use std::process;
+use std::sync::Mutex;
+use typed_arena::Arena;
+
+trait Reader: gimli::Reader<Offset = usize> + Send + Sync {
+ type SyncSendEndian: gimli::Endianity + Send + Sync;
+}
+
+impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian>
+where
+ Endian: gimli::Endianity + Send + Sync,
+{
+ type SyncSendEndian = Endian;
+}
+
+struct ErrorWriter<W: Write + Send> {
+ inner: Mutex<(W, usize)>,
+ path: PathBuf,
+}
+
+impl<W: Write + Send> ErrorWriter<W> {
+ #[allow(clippy::needless_pass_by_value)]
+ fn error(&self, s: String) {
+ let mut lock = self.inner.lock().unwrap();
+ writeln!(&mut lock.0, "DWARF error in {}: {}", self.path.display(), s).unwrap();
+ lock.1 += 1;
+ }
+}
+
+fn main() {
+ let mut w = BufWriter::new(io::stdout());
+ let mut errors = 0;
+ for arg in env::args_os().skip(1) {
+ let path = Path::new(&arg);
+ let file = match fs::File::open(&path) {
+ Ok(file) => file,
+ Err(err) => {
+ eprintln!("Failed to open file '{}': {}", path.display(), err);
+ errors += 1;
+ continue;
+ }
+ };
+ let file = match unsafe { memmap::Mmap::map(&file) } {
+ Ok(mmap) => mmap,
+ Err(err) => {
+ eprintln!("Failed to map file '{}': {}", path.display(), &err);
+ errors += 1;
+ continue;
+ }
+ };
+ let file = match object::File::parse(&*file) {
+ Ok(file) => file,
+ Err(err) => {
+ eprintln!("Failed to parse file '{}': {}", path.display(), err);
+ errors += 1;
+ continue;
+ }
+ };
+
+ let endian = if file.is_little_endian() {
+ gimli::RunTimeEndian::Little
+ } else {
+ gimli::RunTimeEndian::Big
+ };
+ let mut error_writer = ErrorWriter {
+ inner: Mutex::new((&mut w, 0)),
+ path: path.to_owned(),
+ };
+ validate_file(&mut error_writer, &file, endian);
+ errors += error_writer.inner.into_inner().unwrap().1;
+ }
+ // Flush any errors.
+ drop(w);
+ if errors > 0 {
+ process::exit(1);
+ }
+}
+
+fn validate_file<W, Endian>(w: &mut ErrorWriter<W>, file: &object::File, endian: Endian)
+where
+ W: Write + Send,
+ Endian: gimli::Endianity + Send + Sync,
+{
+ let arena = Arena::new();
+
+ fn load_section<'a, 'file, 'input, S, Endian>(
+ arena: &'a Arena<Cow<'file, [u8]>>,
+ file: &'file object::File<'input>,
+ endian: Endian,
+ ) -> S
+ where
+ S: gimli::Section<gimli::EndianSlice<'a, Endian>>,
+ Endian: gimli::Endianity + Send + Sync,
+ 'file: 'input,
+ 'a: 'file,
+ {
+ let data = match file.section_by_name(S::section_name()) {
+ Some(ref section) => section
+ .uncompressed_data()
+ .unwrap_or(Cow::Borrowed(&[][..])),
+ None => Cow::Borrowed(&[][..]),
+ };
+ let data_ref = (*arena.alloc(data)).borrow();
+ S::from(gimli::EndianSlice::new(data_ref, endian))
+ }
+
+ // Variables representing sections of the file. The type of each is inferred from its use in the
+ // validate_info function below.
+ let debug_abbrev = &load_section(&arena, file, endian);
+ let debug_info = &load_section(&arena, file, endian);
+
+ validate_info(w, debug_info, debug_abbrev);
+}
+
+struct UnitSummary {
+ // True if we successfully parsed all the DIEs and attributes in the compilation unit
+ internally_valid: bool,
+ offset: gimli::DebugInfoOffset,
+ die_offsets: Vec<gimli::UnitOffset>,
+ global_die_references: Vec<(gimli::UnitOffset, gimli::DebugInfoOffset)>,
+}
+
+fn validate_info<W, R>(
+ w: &mut ErrorWriter<W>,
+ debug_info: &gimli::DebugInfo<R>,
+ debug_abbrev: &gimli::DebugAbbrev<R>,
+) where
+ W: Write + Send,
+ R: Reader,
+{
+ let mut units = Vec::new();
+ let mut units_iter = debug_info.units();
+ let mut last_offset = 0;
+ loop {
+ let u = match units_iter.next() {
+ Err(err) => {
+ w.error(format!(
+ "Can't read unit header at offset {:#x}, stopping reading units: {}",
+ last_offset, err
+ ));
+ break;
+ }
+ Ok(None) => break,
+ Ok(Some(u)) => u,
+ };
+ last_offset = u.offset().as_debug_info_offset().unwrap().0 + u.length_including_self();
+ units.push(u);
+ }
+ let process_unit = |unit: UnitHeader<R>| -> UnitSummary {
+ let unit_offset = unit.offset().as_debug_info_offset().unwrap();
+ let mut ret = UnitSummary {
+ internally_valid: false,
+ offset: unit_offset,
+ die_offsets: Vec::new(),
+ global_die_references: Vec::new(),
+ };
+ let abbrevs = match unit.abbreviations(debug_abbrev) {
+ Ok(abbrevs) => abbrevs,
+ Err(err) => {
+ w.error(format!(
+ "Invalid abbrevs for unit {:#x}: {}",
+ unit_offset.0, &err
+ ));
+ return ret;
+ }
+ };
+ let mut entries = unit.entries(&abbrevs);
+ let mut unit_refs = Vec::new();
+ loop {
+ let (_, entry) = match entries.next_dfs() {
+ Err(err) => {
+ w.error(format!(
+ "Invalid DIE for unit {:#x}: {}",
+ unit_offset.0, &err
+ ));
+ return ret;
+ }
+ Ok(None) => break,
+ Ok(Some(entry)) => entry,
+ };
+ ret.die_offsets.push(entry.offset());
+
+ let mut attrs = entry.attrs();
+ loop {
+ let attr = match attrs.next() {
+ Err(err) => {
+ w.error(format!(
+ "Invalid attribute for unit {:#x} at DIE {:#x}: {}",
+ unit_offset.0,
+ entry.offset().0,
+ &err
+ ));
+ return ret;
+ }
+ Ok(None) => break,
+ Ok(Some(attr)) => attr,
+ };
+ match attr.value() {
+ AttributeValue::UnitRef(offset) => {
+ unit_refs.push((entry.offset(), offset));
+ }
+ AttributeValue::DebugInfoRef(offset) => {
+ ret.global_die_references.push((entry.offset(), offset));
+ }
+ _ => (),
+ }
+ }
+ }
+ ret.internally_valid = true;
+ ret.die_offsets.shrink_to_fit();
+ ret.global_die_references.shrink_to_fit();
+
+ // Check intra-unit references
+ for (from, to) in unit_refs {
+ if ret.die_offsets.binary_search(&to).is_err() {
+ w.error(format!(
+ "Invalid intra-unit reference in unit {:#x} from DIE {:#x} to {:#x}",
+ unit_offset.0, from.0, to.0
+ ));
+ }
+ }
+
+ ret
+ };
+ let processed_units = units.into_par_iter().map(process_unit).collect::<Vec<_>>();
+
+ let check_unit = |summary: &UnitSummary| {
+ if !summary.internally_valid {
+ return;
+ }
+ for &(from, to) in summary.global_die_references.iter() {
+ let u = match processed_units.binary_search_by_key(&to, |v| v.offset) {
+ Ok(i) => &processed_units[i],
+ Err(i) => {
+ if i > 0 {
+ &processed_units[i - 1]
+ } else {
+ w.error(format!("Invalid cross-unit reference in unit {:#x} from DIE {:#x} to global DIE {:#x}: no unit found",
+ summary.offset.0, from.0, to.0));
+ continue;
+ }
+ }
+ };
+ if !u.internally_valid {
+ continue;
+ }
+ let to_offset = gimli::UnitOffset(to.0 - u.offset.0);
+ if u.die_offsets.binary_search(&to_offset).is_err() {
+ w.error(format!("Invalid cross-unit reference in unit {:#x} from DIE {:#x} to global DIE {:#x}: unit at {:#x} contains no DIE {:#x}",
+ summary.offset.0, from.0, to.0, u.offset.0, to_offset.0));
+ }
+ }
+ };
+ processed_units.par_iter().for_each(check_unit);
+}
diff --git a/vendor/gimli/examples/dwarfdump.rs b/vendor/gimli/examples/dwarfdump.rs
new file mode 100644
index 000000000..64b233305
--- /dev/null
+++ b/vendor/gimli/examples/dwarfdump.rs
@@ -0,0 +1,2398 @@
+// Allow clippy lints when building without clippy.
+#![allow(unknown_lints)]
+
+use fallible_iterator::FallibleIterator;
+use gimli::{Section, UnitHeader, UnitOffset, UnitSectionOffset, UnitType, UnwindSection};
+use object::{Object, ObjectSection, ObjectSymbol};
+use regex::bytes::Regex;
+use std::borrow::{Borrow, Cow};
+use std::cmp::min;
+use std::collections::HashMap;
+use std::env;
+use std::fmt::{self, Debug};
+use std::fs;
+use std::io;
+use std::io::{BufWriter, Write};
+use std::iter::Iterator;
+use std::mem;
+use std::process;
+use std::result;
+use std::sync::{Condvar, Mutex};
+use typed_arena::Arena;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Error {
+ GimliError(gimli::Error),
+ ObjectError(object::read::Error),
+ IoError,
+}
+
+impl fmt::Display for Error {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
+ Debug::fmt(self, f)
+ }
+}
+
+fn writeln_error<W: Write, R: Reader>(
+ w: &mut W,
+ dwarf: &gimli::Dwarf<R>,
+ err: Error,
+ msg: &str,
+) -> io::Result<()> {
+ writeln!(
+ w,
+ "{}: {}",
+ msg,
+ match err {
+ Error::GimliError(err) => dwarf.format_error(err),
+ Error::ObjectError(err) =>
+ format!("{}:{:?}", "An object error occurred while reading", err),
+ Error::IoError => "An I/O error occurred while writing.".to_string(),
+ }
+ )
+}
+
+impl From<gimli::Error> for Error {
+ fn from(err: gimli::Error) -> Self {
+ Error::GimliError(err)
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(_: io::Error) -> Self {
+ Error::IoError
+ }
+}
+
+impl From<object::read::Error> for Error {
+ fn from(err: object::read::Error) -> Self {
+ Error::ObjectError(err)
+ }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+fn parallel_output<W, II, F>(w: &mut W, max_workers: usize, iter: II, f: F) -> Result<()>
+where
+ W: Write + Send,
+ F: Sync + Fn(II::Item, &mut Vec<u8>) -> Result<()>,
+ II: IntoIterator,
+ II::IntoIter: Send,
+{
+ struct ParallelOutputState<I, W> {
+ iterator: I,
+ current_worker: usize,
+ result: Result<()>,
+ w: W,
+ }
+
+ let state = Mutex::new(ParallelOutputState {
+ iterator: iter.into_iter().fuse(),
+ current_worker: 0,
+ result: Ok(()),
+ w,
+ });
+ let workers = min(max_workers, num_cpus::get());
+ let mut condvars = Vec::new();
+ for _ in 0..workers {
+ condvars.push(Condvar::new());
+ }
+ {
+ let state_ref = &state;
+ let f_ref = &f;
+ let condvars_ref = &condvars;
+ crossbeam::scope(|scope| {
+ for i in 0..workers {
+ scope.spawn(move |_| {
+ let mut v = Vec::new();
+ let mut lock = state_ref.lock().unwrap();
+ while lock.current_worker != i {
+ lock = condvars_ref[i].wait(lock).unwrap();
+ }
+ loop {
+ let item = if lock.result.is_ok() {
+ lock.iterator.next()
+ } else {
+ None
+ };
+ lock.current_worker = (i + 1) % workers;
+ condvars_ref[lock.current_worker].notify_one();
+ mem::drop(lock);
+
+ let ret = if let Some(item) = item {
+ v.clear();
+ f_ref(item, &mut v)
+ } else {
+ return;
+ };
+
+ lock = state_ref.lock().unwrap();
+ while lock.current_worker != i {
+ lock = condvars_ref[i].wait(lock).unwrap();
+ }
+ if lock.result.is_ok() {
+ let ret2 = lock.w.write_all(&v);
+ if ret.is_err() {
+ lock.result = ret;
+ } else {
+ lock.result = ret2.map_err(Error::from);
+ }
+ }
+ }
+ });
+ }
+ })
+ .unwrap();
+ }
+ state.into_inner().unwrap().result
+}
+
+trait Reader: gimli::Reader<Offset = usize> + Send + Sync {}
+
+impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where
+ Endian: gimli::Endianity + Send + Sync
+{
+}
+
+type RelocationMap = HashMap<usize, object::Relocation>;
+
+fn add_relocations(
+ relocations: &mut RelocationMap,
+ file: &object::File,
+ section: &object::Section,
+) {
+ for (offset64, mut relocation) in section.relocations() {
+ let offset = offset64 as usize;
+ if offset as u64 != offset64 {
+ continue;
+ }
+ let offset = offset as usize;
+ match relocation.kind() {
+ object::RelocationKind::Absolute => {
+ match relocation.target() {
+ object::RelocationTarget::Symbol(symbol_idx) => {
+ match file.symbol_by_index(symbol_idx) {
+ Ok(symbol) => {
+ let addend =
+ symbol.address().wrapping_add(relocation.addend() as u64);
+ relocation.set_addend(addend as i64);
+ }
+ Err(_) => {
+ eprintln!(
+ "Relocation with invalid symbol for section {} at offset 0x{:08x}",
+ section.name().unwrap(),
+ offset
+ );
+ }
+ }
+ }
+ _ => {}
+ }
+ if relocations.insert(offset, relocation).is_some() {
+ eprintln!(
+ "Multiple relocations for section {} at offset 0x{:08x}",
+ section.name().unwrap(),
+ offset
+ );
+ }
+ }
+ _ => {
+ eprintln!(
+ "Unsupported relocation for section {} at offset 0x{:08x}",
+ section.name().unwrap(),
+ offset
+ );
+ }
+ }
+ }
+}
+
+/// Apply relocations to addresses and offsets during parsing,
+/// instead of requiring the data to be fully relocated prior
+/// to parsing.
+///
+/// Pros
+/// - allows readonly buffers, we don't need to implement writing of values back to buffers
+/// - potentially allows us to handle addresses and offsets differently
+/// - potentially allows us to add metadata from the relocation (eg symbol names)
+/// Cons
+/// - maybe incomplete
+#[derive(Debug, Clone)]
+struct Relocate<'a, R: gimli::Reader<Offset = usize>> {
+ relocations: &'a RelocationMap,
+ section: R,
+ reader: R,
+}
+
+impl<'a, R: gimli::Reader<Offset = usize>> Relocate<'a, R> {
+ fn relocate(&self, offset: usize, value: u64) -> u64 {
+ if let Some(relocation) = self.relocations.get(&offset) {
+ match relocation.kind() {
+ object::RelocationKind::Absolute => {
+ if relocation.has_implicit_addend() {
+ // Use the explicit addend too, because it may have the symbol value.
+ return value.wrapping_add(relocation.addend() as u64);
+ } else {
+ return relocation.addend() as u64;
+ }
+ }
+ _ => {}
+ }
+ };
+ value
+ }
+}
+
+impl<'a, R: gimli::Reader<Offset = usize>> gimli::Reader for Relocate<'a, R> {
+ type Endian = R::Endian;
+ type Offset = R::Offset;
+
+ fn read_address(&mut self, address_size: u8) -> gimli::Result<u64> {
+ let offset = self.reader.offset_from(&self.section);
+ let value = self.reader.read_address(address_size)?;
+ Ok(self.relocate(offset, value))
+ }
+
+ fn read_length(&mut self, format: gimli::Format) -> gimli::Result<usize> {
+ let offset = self.reader.offset_from(&self.section);
+ let value = self.reader.read_length(format)?;
+ <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
+ }
+
+ fn read_offset(&mut self, format: gimli::Format) -> gimli::Result<usize> {
+ let offset = self.reader.offset_from(&self.section);
+ let value = self.reader.read_offset(format)?;
+ <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
+ }
+
+ fn read_sized_offset(&mut self, size: u8) -> gimli::Result<usize> {
+ let offset = self.reader.offset_from(&self.section);
+ let value = self.reader.read_sized_offset(size)?;
+ <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
+ }
+
+ #[inline]
+ fn split(&mut self, len: Self::Offset) -> gimli::Result<Self> {
+ 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) -> gimli::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) -> gimli::ReaderOffsetId {
+ self.reader.offset_id()
+ }
+
+ #[inline]
+ fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option<Self::Offset> {
+ self.reader.lookup_offset_id(id)
+ }
+
+ #[inline]
+ fn find(&self, byte: u8) -> gimli::Result<Self::Offset> {
+ self.reader.find(byte)
+ }
+
+ #[inline]
+ fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> {
+ self.reader.skip(len)
+ }
+
+ #[inline]
+ fn to_slice(&self) -> gimli::Result<Cow<[u8]>> {
+ self.reader.to_slice()
+ }
+
+ #[inline]
+ fn to_string(&self) -> gimli::Result<Cow<str>> {
+ self.reader.to_string()
+ }
+
+ #[inline]
+ fn to_string_lossy(&self) -> gimli::Result<Cow<str>> {
+ self.reader.to_string_lossy()
+ }
+
+ #[inline]
+ fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> {
+ self.reader.read_slice(buf)
+ }
+}
+
+impl<'a, R: Reader> Reader for Relocate<'a, R> {}
+
+#[derive(Default)]
+struct Flags<'a> {
+ eh_frame: bool,
+ goff: bool,
+ info: bool,
+ line: bool,
+ pubnames: bool,
+ pubtypes: bool,
+ aranges: bool,
+ dwo: bool,
+ dwp: bool,
+ dwo_parent: Option<object::File<'a>>,
+ sup: Option<object::File<'a>>,
+ raw: bool,
+ match_units: Option<Regex>,
+}
+
+fn print_usage(opts: &getopts::Options) -> ! {
+ let brief = format!("Usage: {} <options> <file>", env::args().next().unwrap());
+ write!(&mut io::stderr(), "{}", opts.usage(&brief)).ok();
+ process::exit(1);
+}
+
+fn main() {
+ let mut opts = getopts::Options::new();
+ opts.optflag(
+ "",
+ "eh-frame",
+ "print .eh-frame exception handling frame information",
+ );
+ opts.optflag("G", "", "show global die offsets");
+ opts.optflag("i", "", "print .debug_info and .debug_types sections");
+ opts.optflag("l", "", "print .debug_line section");
+ opts.optflag("p", "", "print .debug_pubnames section");
+ opts.optflag("r", "", "print .debug_aranges section");
+ opts.optflag("y", "", "print .debug_pubtypes section");
+ opts.optflag(
+ "",
+ "dwo",
+ "print the .dwo versions of the selected sections",
+ );
+ opts.optflag(
+ "",
+ "dwp",
+ "print the .dwp versions of the selected sections",
+ );
+ opts.optopt(
+ "",
+ "dwo-parent",
+ "use the specified file as the parent of the dwo or dwp (e.g. for .debug_addr)",
+ "library path",
+ );
+ opts.optflag("", "raw", "print raw data values");
+ opts.optopt(
+ "u",
+ "match-units",
+ "print compilation units whose output matches a regex",
+ "REGEX",
+ );
+ opts.optopt("", "sup", "path to supplementary object file", "PATH");
+
+ let matches = match opts.parse(env::args().skip(1)) {
+ Ok(m) => m,
+ Err(e) => {
+ writeln!(&mut io::stderr(), "{:?}\n", e).ok();
+ print_usage(&opts);
+ }
+ };
+ if matches.free.is_empty() {
+ print_usage(&opts);
+ }
+
+ let mut all = true;
+ let mut flags = Flags::default();
+ if matches.opt_present("eh-frame") {
+ flags.eh_frame = true;
+ all = false;
+ }
+ if matches.opt_present("G") {
+ flags.goff = true;
+ }
+ if matches.opt_present("i") {
+ flags.info = true;
+ all = false;
+ }
+ if matches.opt_present("l") {
+ flags.line = true;
+ all = false;
+ }
+ if matches.opt_present("p") {
+ flags.pubnames = true;
+ all = false;
+ }
+ if matches.opt_present("y") {
+ flags.pubtypes = true;
+ all = false;
+ }
+ if matches.opt_present("r") {
+ flags.aranges = true;
+ all = false;
+ }
+ if matches.opt_present("dwo") {
+ flags.dwo = true;
+ }
+ if matches.opt_present("dwp") {
+ flags.dwp = true;
+ }
+ if matches.opt_present("raw") {
+ flags.raw = true;
+ }
+ if all {
+ // .eh_frame is excluded even when printing all information.
+ // cosmetic flags like -G must be set explicitly too.
+ flags.info = true;
+ flags.line = true;
+ flags.pubnames = true;
+ flags.pubtypes = true;
+ flags.aranges = true;
+ }
+ flags.match_units = if let Some(r) = matches.opt_str("u") {
+ match Regex::new(&r) {
+ Ok(r) => Some(r),
+ Err(e) => {
+ eprintln!("Invalid regular expression {}: {}", r, e);
+ process::exit(1);
+ }
+ }
+ } else {
+ None
+ };
+
+ let arena_mmap = Arena::new();
+ let load_file = |path| {
+ let file = match fs::File::open(&path) {
+ Ok(file) => file,
+ Err(err) => {
+ eprintln!("Failed to open file '{}': {}", path, err);
+ process::exit(1);
+ }
+ };
+ let mmap = match unsafe { memmap::Mmap::map(&file) } {
+ Ok(mmap) => mmap,
+ Err(err) => {
+ eprintln!("Failed to map file '{}': {}", path, err);
+ process::exit(1);
+ }
+ };
+ let mmap_ref = (*arena_mmap.alloc(mmap)).borrow();
+ match object::File::parse(&**mmap_ref) {
+ Ok(file) => Some(file),
+ Err(err) => {
+ eprintln!("Failed to parse file '{}': {}", path, err);
+ process::exit(1);
+ }
+ }
+ };
+
+ flags.sup = matches.opt_str("sup").and_then(load_file);
+ flags.dwo_parent = matches.opt_str("dwo-parent").and_then(load_file);
+ if flags.dwo_parent.is_some() && !flags.dwo && !flags.dwp {
+ eprintln!("--dwo-parent also requires --dwo or --dwp");
+ process::exit(1);
+ }
+ if flags.dwo_parent.is_none() && flags.dwp {
+ eprintln!("--dwp also requires --dwo-parent");
+ process::exit(1);
+ }
+
+ for file_path in &matches.free {
+ if matches.free.len() != 1 {
+ println!("{}", file_path);
+ println!();
+ }
+
+ let file = match fs::File::open(&file_path) {
+ Ok(file) => file,
+ Err(err) => {
+ eprintln!("Failed to open file '{}': {}", file_path, err);
+ continue;
+ }
+ };
+ let file = match unsafe { memmap::Mmap::map(&file) } {
+ Ok(mmap) => mmap,
+ Err(err) => {
+ eprintln!("Failed to map file '{}': {}", file_path, err);
+ continue;
+ }
+ };
+ let file = match object::File::parse(&*file) {
+ Ok(file) => file,
+ Err(err) => {
+ eprintln!("Failed to parse file '{}': {}", file_path, err);
+ continue;
+ }
+ };
+
+ let endian = if file.is_little_endian() {
+ gimli::RunTimeEndian::Little
+ } else {
+ gimli::RunTimeEndian::Big
+ };
+ let ret = dump_file(&file, endian, &flags);
+ match ret {
+ Ok(_) => (),
+ Err(err) => eprintln!("Failed to dump '{}': {}", file_path, err,),
+ }
+ }
+}
+
+fn empty_file_section<'input, 'arena, Endian: gimli::Endianity>(
+ endian: Endian,
+ arena_relocations: &'arena Arena<RelocationMap>,
+) -> Relocate<'arena, gimli::EndianSlice<'arena, Endian>> {
+ let reader = gimli::EndianSlice::new(&[], endian);
+ let section = reader;
+ let relocations = RelocationMap::default();
+ let relocations = (*arena_relocations.alloc(relocations)).borrow();
+ Relocate {
+ relocations,
+ section,
+ reader,
+ }
+}
+
+fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
+ id: gimli::SectionId,
+ file: &object::File<'input>,
+ endian: Endian,
+ is_dwo: bool,
+ arena_data: &'arena Arena<Cow<'input, [u8]>>,
+ arena_relocations: &'arena Arena<RelocationMap>,
+) -> Result<Relocate<'arena, gimli::EndianSlice<'arena, Endian>>> {
+ let mut relocations = RelocationMap::default();
+ let name = if is_dwo {
+ id.dwo_name()
+ } else {
+ Some(id.name())
+ };
+
+ let data = match name.and_then(|name| file.section_by_name(&name)) {
+ Some(ref section) => {
+ // DWO sections never have relocations, so don't bother.
+ if !is_dwo {
+ add_relocations(&mut relocations, file, section);
+ }
+ section.uncompressed_data()?
+ }
+ // Use a non-zero capacity so that `ReaderOffsetId`s are unique.
+ None => Cow::Owned(Vec::with_capacity(1)),
+ };
+ let data_ref = (*arena_data.alloc(data)).borrow();
+ let reader = gimli::EndianSlice::new(data_ref, endian);
+ let section = reader;
+ let relocations = (*arena_relocations.alloc(relocations)).borrow();
+ Ok(Relocate {
+ relocations,
+ section,
+ reader,
+ })
+}
+
+fn dump_file<Endian>(file: &object::File, endian: Endian, flags: &Flags) -> Result<()>
+where
+ Endian: gimli::Endianity + Send + Sync,
+{
+ let arena_data = Arena::new();
+ let arena_relocations = Arena::new();
+
+ let dwo_parent = if let Some(dwo_parent_file) = flags.dwo_parent.as_ref() {
+ let mut load_dwo_parent_section = |id: gimli::SectionId| -> Result<_> {
+ load_file_section(
+ id,
+ dwo_parent_file,
+ endian,
+ false,
+ &arena_data,
+ &arena_relocations,
+ )
+ };
+ Some(gimli::Dwarf::load(&mut load_dwo_parent_section)?)
+ } else {
+ None
+ };
+ let dwo_parent = dwo_parent.as_ref();
+
+ let dwo_parent_units = if let Some(dwo_parent) = dwo_parent {
+ Some(
+ match dwo_parent
+ .units()
+ .map(|unit_header| dwo_parent.unit(unit_header))
+ .filter_map(|unit| Ok(unit.dwo_id.map(|dwo_id| (dwo_id, unit))))
+ .collect()
+ {
+ Ok(units) => units,
+ Err(err) => {
+ eprintln!("Failed to process --dwo-parent units: {}", err);
+ return Ok(());
+ }
+ },
+ )
+ } else {
+ None
+ };
+ let dwo_parent_units = dwo_parent_units.as_ref();
+
+ let mut load_section = |id: gimli::SectionId| -> Result<_> {
+ load_file_section(
+ id,
+ file,
+ endian,
+ flags.dwo || flags.dwp,
+ &arena_data,
+ &arena_relocations,
+ )
+ };
+
+ let w = &mut BufWriter::new(io::stdout());
+ if flags.dwp {
+ let empty = empty_file_section(endian, &arena_relocations);
+ let dwp = gimli::DwarfPackage::load(&mut load_section, empty)?;
+ dump_dwp(w, &dwp, dwo_parent.unwrap(), dwo_parent_units, flags)?;
+ w.flush()?;
+ return Ok(());
+ }
+
+ let mut dwarf = gimli::Dwarf::load(&mut load_section)?;
+ if flags.dwo {
+ dwarf.file_type = gimli::DwarfFileType::Dwo;
+ if let Some(dwo_parent) = dwo_parent {
+ dwarf.debug_addr = dwo_parent.debug_addr.clone();
+ dwarf
+ .ranges
+ .set_debug_ranges(dwo_parent.ranges.debug_ranges().clone());
+ }
+ }
+
+ if let Some(sup_file) = flags.sup.as_ref() {
+ let mut load_sup_section = |id: gimli::SectionId| -> Result<_> {
+ // Note: we really only need the `.debug_str` section,
+ // but for now we load them all.
+ load_file_section(id, sup_file, endian, false, &arena_data, &arena_relocations)
+ };
+ dwarf.load_sup(&mut load_sup_section)?;
+ }
+
+ if flags.eh_frame {
+ let eh_frame = gimli::EhFrame::load(&mut load_section).unwrap();
+ dump_eh_frame(w, file, eh_frame)?;
+ }
+ if flags.info {
+ dump_info(w, &dwarf, dwo_parent_units, flags)?;
+ dump_types(w, &dwarf, dwo_parent_units, flags)?;
+ }
+ if flags.line {
+ dump_line(w, &dwarf)?;
+ }
+ if flags.pubnames {
+ let debug_pubnames = &gimli::Section::load(&mut load_section).unwrap();
+ dump_pubnames(w, debug_pubnames, &dwarf.debug_info)?;
+ }
+ if flags.aranges {
+ let debug_aranges = &gimli::Section::load(&mut load_section).unwrap();
+ dump_aranges(w, debug_aranges)?;
+ }
+ if flags.pubtypes {
+ let debug_pubtypes = &gimli::Section::load(&mut load_section).unwrap();
+ dump_pubtypes(w, debug_pubtypes, &dwarf.debug_info)?;
+ }
+ w.flush()?;
+ Ok(())
+}
+
+fn dump_eh_frame<R: Reader, W: Write>(
+ w: &mut W,
+ file: &object::File,
+ mut eh_frame: gimli::EhFrame<R>,
+) -> Result<()> {
+ // TODO: this might be better based on the file format.
+ let address_size = file
+ .architecture()
+ .address_size()
+ .map(|w| w.bytes())
+ .unwrap_or(mem::size_of::<usize>() as u8);
+ eh_frame.set_address_size(address_size);
+
+ fn register_name_none(_: gimli::Register) -> Option<&'static str> {
+ None
+ }
+ let arch_register_name = match file.architecture() {
+ object::Architecture::Arm | object::Architecture::Aarch64 => gimli::Arm::register_name,
+ object::Architecture::I386 => gimli::X86::register_name,
+ object::Architecture::X86_64 => gimli::X86_64::register_name,
+ _ => register_name_none,
+ };
+ let register_name = &|register| match arch_register_name(register) {
+ Some(name) => Cow::Borrowed(name),
+ None => Cow::Owned(format!("{}", register.0)),
+ };
+
+ let mut bases = gimli::BaseAddresses::default();
+ if let Some(section) = file.section_by_name(".eh_frame_hdr") {
+ bases = bases.set_eh_frame_hdr(section.address());
+ }
+ if let Some(section) = file.section_by_name(".eh_frame") {
+ bases = bases.set_eh_frame(section.address());
+ }
+ if let Some(section) = file.section_by_name(".text") {
+ bases = bases.set_text(section.address());
+ }
+ if let Some(section) = file.section_by_name(".got") {
+ bases = bases.set_got(section.address());
+ }
+
+ // TODO: Print "__eh_frame" here on macOS, and more generally use the
+ // section that we're actually looking at, which is what the canonical
+ // dwarfdump does.
+ writeln!(
+ w,
+ "Exception handling frame information for section .eh_frame"
+ )?;
+
+ let mut cies = HashMap::new();
+
+ let mut entries = eh_frame.entries(&bases);
+ loop {
+ match entries.next()? {
+ None => return Ok(()),
+ Some(gimli::CieOrFde::Cie(cie)) => {
+ writeln!(w)?;
+ writeln!(w, "{:#010x}: CIE", cie.offset())?;
+ writeln!(w, " length: {:#010x}", cie.entry_len())?;
+ // TODO: CIE_id
+ writeln!(w, " version: {:#04x}", cie.version())?;
+ // TODO: augmentation
+ writeln!(w, " code_align: {}", cie.code_alignment_factor())?;
+ writeln!(w, " data_align: {}", cie.data_alignment_factor())?;
+ writeln!(w, " ra_register: {:#x}", cie.return_address_register().0)?;
+ if let Some(encoding) = cie.lsda_encoding() {
+ writeln!(w, " lsda_encoding: {:#02x}", encoding.0)?;
+ }
+ if let Some((encoding, personality)) = cie.personality_with_encoding() {
+ write!(w, " personality: {:#02x} ", encoding.0)?;
+ dump_pointer(w, personality)?;
+ writeln!(w)?;
+ }
+ if let Some(encoding) = cie.fde_address_encoding() {
+ writeln!(w, " fde_encoding: {:#02x}", encoding.0)?;
+ }
+ let instructions = cie.instructions(&eh_frame, &bases);
+ dump_cfi_instructions(w, instructions, true, register_name)?;
+ writeln!(w)?;
+ }
+ Some(gimli::CieOrFde::Fde(partial)) => {
+ let mut offset = None;
+ let fde = partial.parse(|_, bases, o| {
+ offset = Some(o);
+ cies.entry(o)
+ .or_insert_with(|| eh_frame.cie_from_offset(bases, o))
+ .clone()
+ })?;
+
+ writeln!(w)?;
+ writeln!(w, "{:#010x}: FDE", fde.offset())?;
+ writeln!(w, " length: {:#010x}", fde.entry_len())?;
+ writeln!(w, " CIE_pointer: {:#010x}", offset.unwrap().0)?;
+ // TODO: symbolicate the start address like the canonical dwarfdump does.
+ writeln!(w, " start_addr: {:#018x}", fde.initial_address())?;
+ writeln!(
+ w,
+ " range_size: {:#018x} (end_addr = {:#018x})",
+ fde.len(),
+ fde.initial_address() + fde.len()
+ )?;
+ if let Some(lsda) = fde.lsda() {
+ write!(w, " lsda: ")?;
+ dump_pointer(w, lsda)?;
+ writeln!(w)?;
+ }
+ let instructions = fde.instructions(&eh_frame, &bases);
+ dump_cfi_instructions(w, instructions, false, register_name)?;
+ writeln!(w)?;
+ }
+ }
+ }
+}
+
+fn dump_pointer<W: Write>(w: &mut W, p: gimli::Pointer) -> Result<()> {
+ match p {
+ gimli::Pointer::Direct(p) => {
+ write!(w, "{:#018x}", p)?;
+ }
+ gimli::Pointer::Indirect(p) => {
+ write!(w, "({:#018x})", p)?;
+ }
+ }
+ Ok(())
+}
+
+#[allow(clippy::unneeded_field_pattern)]
+fn dump_cfi_instructions<R: Reader, W: Write>(
+ w: &mut W,
+ mut insns: gimli::CallFrameInstructionIter<R>,
+ is_initial: bool,
+ register_name: &dyn Fn(gimli::Register) -> Cow<'static, str>,
+) -> Result<()> {
+ use gimli::CallFrameInstruction::*;
+
+ // TODO: we need to actually evaluate these instructions as we iterate them
+ // so we can print the initialized state for CIEs, and each unwind row's
+ // registers for FDEs.
+ //
+ // TODO: We should print DWARF expressions for the CFI instructions that
+ // embed DWARF expressions within themselves.
+
+ if !is_initial {
+ writeln!(w, " Instructions:")?;
+ }
+
+ loop {
+ match insns.next() {
+ Err(e) => {
+ writeln!(w, "Failed to decode CFI instruction: {}", e)?;
+ return Ok(());
+ }
+ Ok(None) => {
+ if is_initial {
+ writeln!(w, " Instructions: Init State:")?;
+ }
+ return Ok(());
+ }
+ Ok(Some(op)) => match op {
+ SetLoc { address } => {
+ writeln!(w, " DW_CFA_set_loc ({:#x})", address)?;
+ }
+ AdvanceLoc { delta } => {
+ writeln!(w, " DW_CFA_advance_loc ({})", delta)?;
+ }
+ DefCfa { register, offset } => {
+ writeln!(
+ w,
+ " DW_CFA_def_cfa ({}, {})",
+ register_name(register),
+ offset
+ )?;
+ }
+ DefCfaSf {
+ register,
+ factored_offset,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_def_cfa_sf ({}, {})",
+ register_name(register),
+ factored_offset
+ )?;
+ }
+ DefCfaRegister { register } => {
+ writeln!(
+ w,
+ " DW_CFA_def_cfa_register ({})",
+ register_name(register)
+ )?;
+ }
+ DefCfaOffset { offset } => {
+ writeln!(w, " DW_CFA_def_cfa_offset ({})", offset)?;
+ }
+ DefCfaOffsetSf { factored_offset } => {
+ writeln!(
+ w,
+ " DW_CFA_def_cfa_offset_sf ({})",
+ factored_offset
+ )?;
+ }
+ DefCfaExpression { expression: _ } => {
+ writeln!(w, " DW_CFA_def_cfa_expression (...)")?;
+ }
+ Undefined { register } => {
+ writeln!(
+ w,
+ " DW_CFA_undefined ({})",
+ register_name(register)
+ )?;
+ }
+ SameValue { register } => {
+ writeln!(
+ w,
+ " DW_CFA_same_value ({})",
+ register_name(register)
+ )?;
+ }
+ Offset {
+ register,
+ factored_offset,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_offset ({}, {})",
+ register_name(register),
+ factored_offset
+ )?;
+ }
+ OffsetExtendedSf {
+ register,
+ factored_offset,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_offset_extended_sf ({}, {})",
+ register_name(register),
+ factored_offset
+ )?;
+ }
+ ValOffset {
+ register,
+ factored_offset,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_val_offset ({}, {})",
+ register_name(register),
+ factored_offset
+ )?;
+ }
+ ValOffsetSf {
+ register,
+ factored_offset,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_val_offset_sf ({}, {})",
+ register_name(register),
+ factored_offset
+ )?;
+ }
+ Register {
+ dest_register,
+ src_register,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_register ({}, {})",
+ register_name(dest_register),
+ register_name(src_register)
+ )?;
+ }
+ Expression {
+ register,
+ expression: _,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_expression ({}, ...)",
+ register_name(register)
+ )?;
+ }
+ ValExpression {
+ register,
+ expression: _,
+ } => {
+ writeln!(
+ w,
+ " DW_CFA_val_expression ({}, ...)",
+ register_name(register)
+ )?;
+ }
+ Restore { register } => {
+ writeln!(
+ w,
+ " DW_CFA_restore ({})",
+ register_name(register)
+ )?;
+ }
+ RememberState => {
+ writeln!(w, " DW_CFA_remember_state")?;
+ }
+ RestoreState => {
+ writeln!(w, " DW_CFA_restore_state")?;
+ }
+ ArgsSize { size } => {
+ writeln!(w, " DW_CFA_GNU_args_size ({})", size)?;
+ }
+ Nop => {
+ writeln!(w, " DW_CFA_nop")?;
+ }
+ },
+ }
+ }
+}
+
+fn dump_dwp<R: Reader, W: Write + Send>(
+ w: &mut W,
+ dwp: &gimli::DwarfPackage<R>,
+ dwo_parent: &gimli::Dwarf<R>,
+ dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
+ flags: &Flags,
+) -> Result<()>
+where
+ R::Endian: Send + Sync,
+{
+ if dwp.cu_index.unit_count() != 0 {
+ writeln!(
+ w,
+ "\n.debug_cu_index: version = {}, sections = {}, units = {}, slots = {}",
+ dwp.cu_index.version(),
+ dwp.cu_index.section_count(),
+ dwp.cu_index.unit_count(),
+ dwp.cu_index.slot_count(),
+ )?;
+ for i in 1..=dwp.cu_index.unit_count() {
+ writeln!(w, "\nCU index {}", i)?;
+ dump_dwp_sections(
+ w,
+ &dwp,
+ dwo_parent,
+ dwo_parent_units,
+ flags,
+ dwp.cu_index.sections(i)?,
+ )?;
+ }
+ }
+
+ if dwp.tu_index.unit_count() != 0 {
+ writeln!(
+ w,
+ "\n.debug_tu_index: version = {}, sections = {}, units = {}, slots = {}",
+ dwp.tu_index.version(),
+ dwp.tu_index.section_count(),
+ dwp.tu_index.unit_count(),
+ dwp.tu_index.slot_count(),
+ )?;
+ for i in 1..=dwp.tu_index.unit_count() {
+ writeln!(w, "\nTU index {}", i)?;
+ dump_dwp_sections(
+ w,
+ &dwp,
+ dwo_parent,
+ dwo_parent_units,
+ flags,
+ dwp.tu_index.sections(i)?,
+ )?;
+ }
+ }
+
+ Ok(())
+}
+
+fn dump_dwp_sections<R: Reader, W: Write + Send>(
+ w: &mut W,
+ dwp: &gimli::DwarfPackage<R>,
+ dwo_parent: &gimli::Dwarf<R>,
+ dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
+ flags: &Flags,
+ sections: gimli::UnitIndexSectionIterator<R>,
+) -> Result<()>
+where
+ R::Endian: Send + Sync,
+{
+ for section in sections.clone() {
+ writeln!(
+ w,
+ " {}: offset = 0x{:x}, size = 0x{:x}",
+ section.section.dwo_name().unwrap(),
+ section.offset,
+ section.size
+ )?;
+ }
+ let dwarf = dwp.sections(sections, dwo_parent)?;
+ if flags.info {
+ dump_info(w, &dwarf, dwo_parent_units, flags)?;
+ dump_types(w, &dwarf, dwo_parent_units, flags)?;
+ }
+ if flags.line {
+ dump_line(w, &dwarf)?;
+ }
+ Ok(())
+}
+
+fn dump_info<R: Reader, W: Write + Send>(
+ w: &mut W,
+ dwarf: &gimli::Dwarf<R>,
+ dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
+ flags: &Flags,
+) -> Result<()>
+where
+ R::Endian: Send + Sync,
+{
+ writeln!(w, "\n.debug_info")?;
+
+ let units = match dwarf.units().collect::<Vec<_>>() {
+ Ok(units) => units,
+ Err(err) => {
+ writeln_error(
+ w,
+ dwarf,
+ Error::GimliError(err),
+ "Failed to read unit headers",
+ )?;
+ return Ok(());
+ }
+ };
+ let process_unit = |header: UnitHeader<R>, buf: &mut Vec<u8>| -> Result<()> {
+ dump_unit(buf, header, dwarf, dwo_parent_units, flags)?;
+ if !flags
+ .match_units
+ .as_ref()
+ .map(|r| r.is_match(&buf))
+ .unwrap_or(true)
+ {
+ buf.clear();
+ }
+ Ok(())
+ };
+ // Don't use more than 16 cores even if available. No point in soaking hundreds
+ // of cores if you happen to have them.
+ parallel_output(w, 16, units, process_unit)
+}
+
+fn dump_types<R: Reader, W: Write>(
+ w: &mut W,
+ dwarf: &gimli::Dwarf<R>,
+ dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
+ flags: &Flags,
+) -> Result<()> {
+ writeln!(w, "\n.debug_types")?;
+
+ let mut iter = dwarf.type_units();
+ while let Some(header) = iter.next()? {
+ dump_unit(w, header, dwarf, dwo_parent_units, flags)?;
+ }
+ Ok(())
+}
+
+fn dump_unit<R: Reader, W: Write>(
+ w: &mut W,
+ header: UnitHeader<R>,
+ dwarf: &gimli::Dwarf<R>,
+ dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
+ flags: &Flags,
+) -> Result<()> {
+ write!(w, "\nUNIT<")?;
+ match header.offset() {
+ UnitSectionOffset::DebugInfoOffset(o) => {
+ write!(w, ".debug_info+0x{:08x}", o.0)?;
+ }
+ UnitSectionOffset::DebugTypesOffset(o) => {
+ write!(w, ".debug_types+0x{:08x}", o.0)?;
+ }
+ }
+ writeln!(w, ">: length = 0x{:x}, format = {:?}, version = {}, address_size = {}, abbrev_offset = 0x{:x}",
+ header.unit_length(),
+ header.format(),
+ header.version(),
+ header.address_size(),
+ header.debug_abbrev_offset().0,
+ )?;
+
+ match header.type_() {
+ UnitType::Compilation | UnitType::Partial => (),
+ UnitType::Type {
+ type_signature,
+ type_offset,
+ }
+ | UnitType::SplitType {
+ type_signature,
+ type_offset,
+ } => {
+ write!(w, " signature = ")?;
+ dump_type_signature(w, type_signature)?;
+ writeln!(w)?;
+ writeln!(w, " type_offset = 0x{:x}", type_offset.0,)?;
+ }
+ UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => {
+ write!(w, " dwo_id = ")?;
+ writeln!(w, "0x{:016x}", dwo_id.0)?;
+ }
+ }
+
+ let mut unit = match dwarf.unit(header) {
+ Ok(unit) => unit,
+ Err(err) => {
+ writeln_error(w, dwarf, err.into(), "Failed to parse unit root entry")?;
+ return Ok(());
+ }
+ };
+
+ if let Some(dwo_parent_units) = dwo_parent_units {
+ if let Some(dwo_id) = unit.dwo_id {
+ if let Some(parent_unit) = dwo_parent_units.get(&dwo_id) {
+ unit.copy_relocated_attributes(parent_unit);
+ }
+ }
+ }
+
+ let entries_result = dump_entries(w, unit, dwarf, flags);
+ if let Err(err) = entries_result {
+ writeln_error(w, dwarf, err, "Failed to dump entries")?;
+ }
+ Ok(())
+}
+
+fn spaces(buf: &mut String, len: usize) -> &str {
+ while buf.len() < len {
+ buf.push(' ');
+ }
+ &buf[..len]
+}
+
+// " GOFF=0x{:08x}" adds exactly 16 spaces.
+const GOFF_SPACES: usize = 16;
+
+fn write_offset<R: Reader, W: Write>(
+ w: &mut W,
+ unit: &gimli::Unit<R>,
+ offset: gimli::UnitOffset<R::Offset>,
+ flags: &Flags,
+) -> Result<()> {
+ write!(w, "<0x{:08x}", offset.0)?;
+ if flags.goff {
+ let goff = match offset.to_unit_section_offset(unit) {
+ UnitSectionOffset::DebugInfoOffset(o) => o.0,
+ UnitSectionOffset::DebugTypesOffset(o) => o.0,
+ };
+ write!(w, " GOFF=0x{:08x}", goff)?;
+ }
+ write!(w, ">")?;
+ Ok(())
+}
+
+fn dump_entries<R: Reader, W: Write>(
+ w: &mut W,
+ unit: gimli::Unit<R>,
+ dwarf: &gimli::Dwarf<R>,
+ flags: &Flags,
+) -> Result<()> {
+ let mut spaces_buf = String::new();
+
+ let mut entries = unit.entries_raw(None)?;
+ while !entries.is_empty() {
+ let offset = entries.next_offset();
+ let depth = entries.next_depth();
+ let abbrev = entries.read_abbreviation()?;
+
+ let mut indent = if depth >= 0 {
+ depth as usize * 2 + 2
+ } else {
+ 2
+ };
+ write!(w, "<{}{}>", if depth < 10 { " " } else { "" }, depth)?;
+ write_offset(w, &unit, offset, flags)?;
+ writeln!(
+ w,
+ "{}{}",
+ spaces(&mut spaces_buf, indent),
+ abbrev.map(|x| x.tag()).unwrap_or(gimli::DW_TAG_null)
+ )?;
+
+ indent += 18;
+ if flags.goff {
+ indent += GOFF_SPACES;
+ }
+
+ for spec in abbrev.map(|x| x.attributes()).unwrap_or(&[]) {
+ let attr = entries.read_attribute(*spec)?;
+ w.write_all(spaces(&mut spaces_buf, indent).as_bytes())?;
+ if let Some(n) = attr.name().static_string() {
+ let right_padding = 27 - std::cmp::min(27, n.len());
+ write!(w, "{}{} ", n, spaces(&mut spaces_buf, right_padding))?;
+ } else {
+ write!(w, "{:27} ", attr.name())?;
+ }
+ if flags.raw {
+ writeln!(w, "{:?}", attr.raw_value())?;
+ } else {
+ match dump_attr_value(w, &attr, &unit, dwarf) {
+ Ok(_) => (),
+ Err(err) => writeln_error(w, dwarf, err, "Failed to dump attribute value")?,
+ };
+ }
+ }
+ }
+ Ok(())
+}
+
+fn dump_attr_value<R: Reader, W: Write>(
+ w: &mut W,
+ attr: &gimli::Attribute<R>,
+ unit: &gimli::Unit<R>,
+ dwarf: &gimli::Dwarf<R>,
+) -> Result<()> {
+ let value = attr.value();
+ match value {
+ gimli::AttributeValue::Addr(address) => {
+ writeln!(w, "0x{:08x}", address)?;
+ }
+ gimli::AttributeValue::Block(data) => {
+ for byte in data.to_slice()?.iter() {
+ write!(w, "{:02x}", byte)?;
+ }
+ writeln!(w)?;
+ }
+ gimli::AttributeValue::Data1(_)
+ | gimli::AttributeValue::Data2(_)
+ | gimli::AttributeValue::Data4(_)
+ | gimli::AttributeValue::Data8(_) => {
+ if let (Some(udata), Some(sdata)) = (attr.udata_value(), attr.sdata_value()) {
+ if sdata >= 0 {
+ writeln!(w, "{}", udata)?;
+ } else {
+ writeln!(w, "{} ({})", udata, sdata)?;
+ }
+ } else {
+ writeln!(w, "{:?}", value)?;
+ }
+ }
+ gimli::AttributeValue::Sdata(data) => {
+ match attr.name() {
+ gimli::DW_AT_data_member_location => {
+ writeln!(w, "{}", data)?;
+ }
+ _ => {
+ if data >= 0 {
+ writeln!(w, "0x{:08x}", data)?;
+ } else {
+ writeln!(w, "0x{:08x} ({})", data, data)?;
+ }
+ }
+ };
+ }
+ gimli::AttributeValue::Udata(data) => {
+ match attr.name() {
+ gimli::DW_AT_high_pc => {
+ writeln!(w, "<offset-from-lowpc>{}", data)?;
+ }
+ gimli::DW_AT_data_member_location => {
+ if let Some(sdata) = attr.sdata_value() {
+ // This is a DW_FORM_data* value.
+ // libdwarf-dwarfdump displays this as signed too.
+ if sdata >= 0 {
+ writeln!(w, "{}", data)?;
+ } else {
+ writeln!(w, "{} ({})", data, sdata)?;
+ }
+ } else {
+ writeln!(w, "{}", data)?;
+ }
+ }
+ gimli::DW_AT_lower_bound | gimli::DW_AT_upper_bound => {
+ writeln!(w, "{}", data)?;
+ }
+ _ => {
+ writeln!(w, "0x{:08x}", data)?;
+ }
+ };
+ }
+ gimli::AttributeValue::Exprloc(ref data) => {
+ if let gimli::AttributeValue::Exprloc(_) = attr.raw_value() {
+ write!(w, "len 0x{:04x}: ", data.0.len())?;
+ for byte in data.0.to_slice()?.iter() {
+ write!(w, "{:02x}", byte)?;
+ }
+ write!(w, ": ")?;
+ }
+ dump_exprloc(w, unit.encoding(), data)?;
+ writeln!(w)?;
+ }
+ gimli::AttributeValue::Flag(true) => {
+ writeln!(w, "yes")?;
+ }
+ gimli::AttributeValue::Flag(false) => {
+ writeln!(w, "no")?;
+ }
+ gimli::AttributeValue::SecOffset(offset) => {
+ writeln!(w, "0x{:08x}", offset)?;
+ }
+ gimli::AttributeValue::DebugAddrBase(base) => {
+ writeln!(w, "<.debug_addr+0x{:08x}>", base.0)?;
+ }
+ gimli::AttributeValue::DebugAddrIndex(index) => {
+ write!(w, "(indirect address, index {:#x}): ", index.0)?;
+ let address = dwarf.address(unit, index)?;
+ writeln!(w, "0x{:08x}", address)?;
+ }
+ gimli::AttributeValue::UnitRef(offset) => {
+ write!(w, "0x{:08x}", offset.0)?;
+ match offset.to_unit_section_offset(unit) {
+ UnitSectionOffset::DebugInfoOffset(goff) => {
+ write!(w, "<.debug_info+0x{:08x}>", goff.0)?;
+ }
+ UnitSectionOffset::DebugTypesOffset(goff) => {
+ write!(w, "<.debug_types+0x{:08x}>", goff.0)?;
+ }
+ }
+ writeln!(w)?;
+ }
+ gimli::AttributeValue::DebugInfoRef(offset) => {
+ writeln!(w, "<.debug_info+0x{:08x}>", offset.0)?;
+ }
+ gimli::AttributeValue::DebugInfoRefSup(offset) => {
+ writeln!(w, "<.debug_info(sup)+0x{:08x}>", offset.0)?;
+ }
+ gimli::AttributeValue::DebugLineRef(offset) => {
+ writeln!(w, "<.debug_line+0x{:08x}>", offset.0)?;
+ }
+ gimli::AttributeValue::LocationListsRef(offset) => {
+ dump_loc_list(w, offset, unit, dwarf)?;
+ }
+ gimli::AttributeValue::DebugLocListsBase(base) => {
+ writeln!(w, "<.debug_loclists+0x{:08x}>", base.0)?;
+ }
+ gimli::AttributeValue::DebugLocListsIndex(index) => {
+ write!(w, "(indirect location list, index {:#x}): ", index.0)?;
+ let offset = dwarf.locations_offset(unit, index)?;
+ dump_loc_list(w, offset, unit, dwarf)?;
+ }
+ gimli::AttributeValue::DebugMacinfoRef(offset) => {
+ writeln!(w, "<.debug_macinfo+0x{:08x}>", offset.0)?;
+ }
+ gimli::AttributeValue::DebugMacroRef(offset) => {
+ writeln!(w, "<.debug_macro+0x{:08x}>", offset.0)?;
+ }
+ gimli::AttributeValue::RangeListsRef(offset) => {
+ let offset = dwarf.ranges_offset_from_raw(unit, offset);
+ dump_range_list(w, offset, unit, dwarf)?;
+ }
+ gimli::AttributeValue::DebugRngListsBase(base) => {
+ writeln!(w, "<.debug_rnglists+0x{:08x}>", base.0)?;
+ }
+ gimli::AttributeValue::DebugRngListsIndex(index) => {
+ write!(w, "(indirect range list, index {:#x}): ", index.0)?;
+ let offset = dwarf.ranges_offset(unit, index)?;
+ dump_range_list(w, offset, unit, dwarf)?;
+ }
+ gimli::AttributeValue::DebugTypesRef(signature) => {
+ dump_type_signature(w, signature)?;
+ writeln!(w, " <type signature>")?;
+ }
+ gimli::AttributeValue::DebugStrRef(offset) => {
+ if let Ok(s) = dwarf.debug_str.get_str(offset) {
+ writeln!(w, "{}", s.to_string_lossy()?)?;
+ } else {
+ writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?;
+ }
+ }
+ gimli::AttributeValue::DebugStrRefSup(offset) => {
+ if let Some(s) = dwarf
+ .sup()
+ .and_then(|sup| sup.debug_str.get_str(offset).ok())
+ {
+ writeln!(w, "{}", s.to_string_lossy()?)?;
+ } else {
+ writeln!(w, "<.debug_str(sup)+0x{:08x}>", offset.0)?;
+ }
+ }
+ gimli::AttributeValue::DebugStrOffsetsBase(base) => {
+ writeln!(w, "<.debug_str_offsets+0x{:08x}>", base.0)?;
+ }
+ gimli::AttributeValue::DebugStrOffsetsIndex(index) => {
+ write!(w, "(indirect string, index {:#x}): ", index.0)?;
+ let offset = dwarf.debug_str_offsets.get_str_offset(
+ unit.encoding().format,
+ unit.str_offsets_base,
+ index,
+ )?;
+ if let Ok(s) = dwarf.debug_str.get_str(offset) {
+ writeln!(w, "{}", s.to_string_lossy()?)?;
+ } else {
+ writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?;
+ }
+ }
+ gimli::AttributeValue::DebugLineStrRef(offset) => {
+ if let Ok(s) = dwarf.debug_line_str.get_str(offset) {
+ writeln!(w, "{}", s.to_string_lossy()?)?;
+ } else {
+ writeln!(w, "<.debug_line_str=0x{:08x}>", offset.0)?;
+ }
+ }
+ gimli::AttributeValue::String(s) => {
+ writeln!(w, "{}", s.to_string_lossy()?)?;
+ }
+ gimli::AttributeValue::Encoding(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::DecimalSign(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::Endianity(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::Accessibility(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::Visibility(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::Virtuality(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::Language(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::AddressClass(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::IdentifierCase(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::CallingConvention(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::Inline(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::Ordering(value) => {
+ writeln!(w, "{}", value)?;
+ }
+ gimli::AttributeValue::FileIndex(value) => {
+ write!(w, "0x{:08x}", value)?;
+ dump_file_index(w, value, unit, dwarf)?;
+ writeln!(w)?;
+ }
+ gimli::AttributeValue::DwoId(value) => {
+ writeln!(w, "0x{:016x}", value.0)?;
+ }
+ }
+
+ Ok(())
+}
+
+fn dump_type_signature<W: Write>(w: &mut W, signature: gimli::DebugTypeSignature) -> Result<()> {
+ write!(w, "0x{:016x}", signature.0)?;
+ Ok(())
+}
+
+fn dump_file_index<R: Reader, W: Write>(
+ w: &mut W,
+ file: u64,
+ unit: &gimli::Unit<R>,
+ dwarf: &gimli::Dwarf<R>,
+) -> Result<()> {
+ if file == 0 {
+ return Ok(());
+ }
+ let header = match unit.line_program {
+ Some(ref program) => program.header(),
+ None => return Ok(()),
+ };
+ let file = match header.file(file) {
+ Some(header) => header,
+ None => {
+ writeln!(w, "Unable to get header for file {}", file)?;
+ return Ok(());
+ }
+ };
+ write!(w, " ")?;
+ if let Some(directory) = file.directory(header) {
+ let directory = dwarf.attr_string(unit, directory)?;
+ let directory = directory.to_string_lossy()?;
+ if !directory.starts_with('/') {
+ if let Some(ref comp_dir) = unit.comp_dir {
+ write!(w, "{}/", comp_dir.to_string_lossy()?,)?;
+ }
+ }
+ write!(w, "{}/", directory)?;
+ }
+ write!(
+ w,
+ "{}",
+ dwarf
+ .attr_string(unit, file.path_name())?
+ .to_string_lossy()?
+ )?;
+ Ok(())
+}
+
+fn dump_exprloc<R: Reader, W: Write>(
+ w: &mut W,
+ encoding: gimli::Encoding,
+ data: &gimli::Expression<R>,
+) -> Result<()> {
+ let mut pc = data.0.clone();
+ let mut space = false;
+ while pc.len() != 0 {
+ let pc_clone = pc.clone();
+ match gimli::Operation::parse(&mut pc, encoding) {
+ Ok(op) => {
+ if space {
+ write!(w, " ")?;
+ } else {
+ space = true;
+ }
+ dump_op(w, encoding, pc_clone, op)?;
+ }
+ Err(gimli::Error::InvalidExpression(op)) => {
+ writeln!(w, "WARNING: unsupported operation 0x{:02x}", op.0)?;
+ return Ok(());
+ }
+ Err(gimli::Error::UnsupportedRegister(register)) => {
+ writeln!(w, "WARNING: unsupported register {}", register)?;
+ return Ok(());
+ }
+ Err(gimli::Error::UnexpectedEof(_)) => {
+ writeln!(w, "WARNING: truncated or malformed expression")?;
+ return Ok(());
+ }
+ Err(e) => {
+ writeln!(w, "WARNING: unexpected operation parse error: {}", e)?;
+ return Ok(());
+ }
+ }
+ }
+ Ok(())
+}
+
+fn dump_op<R: Reader, W: Write>(
+ w: &mut W,
+ encoding: gimli::Encoding,
+ mut pc: R,
+ op: gimli::Operation<R>,
+) -> Result<()> {
+ let dwop = gimli::DwOp(pc.read_u8()?);
+ write!(w, "{}", dwop)?;
+ match op {
+ gimli::Operation::Deref {
+ base_type, size, ..
+ } => {
+ if dwop == gimli::DW_OP_deref_size || dwop == gimli::DW_OP_xderef_size {
+ write!(w, " {}", size)?;
+ }
+ if base_type != UnitOffset(0) {
+ write!(w, " type 0x{:08x}", base_type.0)?;
+ }
+ }
+ gimli::Operation::Pick { index } => {
+ if dwop == gimli::DW_OP_pick {
+ write!(w, " {}", index)?;
+ }
+ }
+ gimli::Operation::PlusConstant { value } => {
+ write!(w, " {}", value as i64)?;
+ }
+ gimli::Operation::Bra { target } => {
+ write!(w, " {}", target)?;
+ }
+ gimli::Operation::Skip { target } => {
+ write!(w, " {}", target)?;
+ }
+ gimli::Operation::SignedConstant { value } => match dwop {
+ gimli::DW_OP_const1s
+ | gimli::DW_OP_const2s
+ | gimli::DW_OP_const4s
+ | gimli::DW_OP_const8s
+ | gimli::DW_OP_consts => {
+ write!(w, " {}", value)?;
+ }
+ _ => {}
+ },
+ gimli::Operation::UnsignedConstant { value } => match dwop {
+ gimli::DW_OP_const1u
+ | gimli::DW_OP_const2u
+ | gimli::DW_OP_const4u
+ | gimli::DW_OP_const8u
+ | gimli::DW_OP_constu => {
+ write!(w, " {}", value)?;
+ }
+ _ => {
+ // These have the value encoded in the operation, eg DW_OP_lit0.
+ }
+ },
+ gimli::Operation::Register { register } => {
+ if dwop == gimli::DW_OP_regx {
+ write!(w, " {}", register.0)?;
+ }
+ }
+ gimli::Operation::RegisterOffset {
+ register,
+ offset,
+ base_type,
+ } => {
+ if dwop >= gimli::DW_OP_breg0 && dwop <= gimli::DW_OP_breg31 {
+ write!(w, "{:+}", offset)?;
+ } else {
+ write!(w, " {}", register.0)?;
+ if offset != 0 {
+ write!(w, "{:+}", offset)?;
+ }
+ if base_type != UnitOffset(0) {
+ write!(w, " type 0x{:08x}", base_type.0)?;
+ }
+ }
+ }
+ gimli::Operation::FrameOffset { offset } => {
+ write!(w, " {}", offset)?;
+ }
+ gimli::Operation::Call { offset } => match offset {
+ gimli::DieReference::UnitRef(gimli::UnitOffset(offset)) => {
+ write!(w, " 0x{:08x}", offset)?;
+ }
+ gimli::DieReference::DebugInfoRef(gimli::DebugInfoOffset(offset)) => {
+ write!(w, " 0x{:08x}", offset)?;
+ }
+ },
+ gimli::Operation::Piece {
+ size_in_bits,
+ bit_offset: None,
+ } => {
+ write!(w, " {}", size_in_bits / 8)?;
+ }
+ gimli::Operation::Piece {
+ size_in_bits,
+ bit_offset: Some(bit_offset),
+ } => {
+ write!(w, " 0x{:08x} offset 0x{:08x}", size_in_bits, bit_offset)?;
+ }
+ gimli::Operation::ImplicitValue { data } => {
+ let data = data.to_slice()?;
+ write!(w, " 0x{:08x} contents 0x", data.len())?;
+ for byte in data.iter() {
+ write!(w, "{:02x}", byte)?;
+ }
+ }
+ gimli::Operation::ImplicitPointer { value, byte_offset } => {
+ write!(w, " 0x{:08x} {}", value.0, byte_offset)?;
+ }
+ gimli::Operation::EntryValue { expression } => {
+ write!(w, "(")?;
+ dump_exprloc(w, encoding, &gimli::Expression(expression))?;
+ write!(w, ")")?;
+ }
+ gimli::Operation::ParameterRef { offset } => {
+ write!(w, " 0x{:08x}", offset.0)?;
+ }
+ gimli::Operation::Address { address } => {
+ write!(w, " 0x{:08x}", address)?;
+ }
+ gimli::Operation::AddressIndex { index } => {
+ write!(w, " 0x{:08x}", index.0)?;
+ }
+ gimli::Operation::ConstantIndex { index } => {
+ write!(w, " 0x{:08x}", index.0)?;
+ }
+ gimli::Operation::TypedLiteral { base_type, value } => {
+ write!(w, " type 0x{:08x} contents 0x", base_type.0)?;
+ for byte in value.to_slice()?.iter() {
+ write!(w, "{:02x}", byte)?;
+ }
+ }
+ gimli::Operation::Convert { base_type } => {
+ write!(w, " type 0x{:08x}", base_type.0)?;
+ }
+ gimli::Operation::Reinterpret { base_type } => {
+ write!(w, " type 0x{:08x}", base_type.0)?;
+ }
+ gimli::Operation::WasmLocal { index }
+ | gimli::Operation::WasmGlobal { index }
+ | gimli::Operation::WasmStack { index } => {
+ let wasmop = pc.read_u8()?;
+ write!(w, " 0x{:x} 0x{:x}", wasmop, index)?;
+ }
+ gimli::Operation::Drop
+ | gimli::Operation::Swap
+ | gimli::Operation::Rot
+ | gimli::Operation::Abs
+ | gimli::Operation::And
+ | gimli::Operation::Div
+ | gimli::Operation::Minus
+ | gimli::Operation::Mod
+ | gimli::Operation::Mul
+ | gimli::Operation::Neg
+ | gimli::Operation::Not
+ | gimli::Operation::Or
+ | gimli::Operation::Plus
+ | gimli::Operation::Shl
+ | gimli::Operation::Shr
+ | gimli::Operation::Shra
+ | gimli::Operation::Xor
+ | gimli::Operation::Eq
+ | gimli::Operation::Ge
+ | gimli::Operation::Gt
+ | gimli::Operation::Le
+ | gimli::Operation::Lt
+ | gimli::Operation::Ne
+ | gimli::Operation::Nop
+ | gimli::Operation::PushObjectAddress
+ | gimli::Operation::TLS
+ | gimli::Operation::CallFrameCFA
+ | gimli::Operation::StackValue => {}
+ };
+ Ok(())
+}
+
+fn dump_loc_list<R: Reader, W: Write>(
+ w: &mut W,
+ offset: gimli::LocationListsOffset<R::Offset>,
+ unit: &gimli::Unit<R>,
+ dwarf: &gimli::Dwarf<R>,
+) -> Result<()> {
+ let raw_locations = dwarf.raw_locations(unit, offset)?;
+ let raw_locations: Vec<_> = raw_locations.collect()?;
+ let mut locations = dwarf.locations(unit, offset)?;
+ writeln!(
+ w,
+ "<loclist at {}+0x{:08x} with {} entries>",
+ if unit.encoding().version < 5 {
+ ".debug_loc"
+ } else {
+ ".debug_loclists"
+ },
+ offset.0,
+ raw_locations.len()
+ )?;
+ for (i, raw) in raw_locations.iter().enumerate() {
+ write!(w, "\t\t\t[{:2}]", i)?;
+ match *raw {
+ gimli::RawLocListEntry::BaseAddress { addr } => {
+ writeln!(w, "<new base address 0x{:08x}>", addr)?;
+ }
+ gimli::RawLocListEntry::BaseAddressx { addr } => {
+ let addr_val = dwarf.address(unit, addr)?;
+ writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?;
+ }
+ gimli::RawLocListEntry::StartxEndx {
+ begin,
+ end,
+ ref data,
+ } => {
+ let begin_val = dwarf.address(unit, begin)?;
+ let end_val = dwarf.address(unit, end)?;
+ let location = locations.next()?.unwrap();
+ write!(
+ w,
+ "<startx-endx \
+ low-off: [{}]0x{:08x} addr 0x{:08x} \
+ high-off: [{}]0x{:08x} addr 0x{:08x}>",
+ begin.0, begin_val, location.range.begin, end.0, end_val, location.range.end
+ )?;
+ dump_exprloc(w, unit.encoding(), data)?;
+ writeln!(w)?;
+ }
+ gimli::RawLocListEntry::StartxLength {
+ begin,
+ length,
+ ref data,
+ } => {
+ let begin_val = dwarf.address(unit, begin)?;
+ let location = locations.next()?.unwrap();
+ write!(
+ w,
+ "<start-length \
+ low-off: [{}]0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin.0, begin_val, location.range.begin, length, location.range.end
+ )?;
+ dump_exprloc(w, unit.encoding(), data)?;
+ writeln!(w)?;
+ }
+ gimli::RawLocListEntry::AddressOrOffsetPair {
+ begin,
+ end,
+ ref data,
+ }
+ | gimli::RawLocListEntry::OffsetPair {
+ begin,
+ end,
+ ref data,
+ } => {
+ let location = locations.next()?.unwrap();
+ write!(
+ w,
+ "<offset pair \
+ low-off: 0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin, location.range.begin, end, location.range.end
+ )?;
+ dump_exprloc(w, unit.encoding(), data)?;
+ writeln!(w)?;
+ }
+ gimli::RawLocListEntry::DefaultLocation { ref data } => {
+ write!(w, "<default location>")?;
+ dump_exprloc(w, unit.encoding(), data)?;
+ writeln!(w)?;
+ }
+ gimli::RawLocListEntry::StartEnd {
+ begin,
+ end,
+ ref data,
+ } => {
+ let location = locations.next()?.unwrap();
+ write!(
+ w,
+ "<start-end \
+ low-off: 0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin, location.range.begin, end, location.range.end
+ )?;
+ dump_exprloc(w, unit.encoding(), data)?;
+ writeln!(w)?;
+ }
+ gimli::RawLocListEntry::StartLength {
+ begin,
+ length,
+ ref data,
+ } => {
+ let location = locations.next()?.unwrap();
+ write!(
+ w,
+ "<start-length \
+ low-off: 0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin, location.range.begin, length, location.range.end
+ )?;
+ dump_exprloc(w, unit.encoding(), data)?;
+ writeln!(w)?;
+ }
+ };
+ }
+ Ok(())
+}
+
+fn dump_range_list<R: Reader, W: Write>(
+ w: &mut W,
+ offset: gimli::RangeListsOffset<R::Offset>,
+ unit: &gimli::Unit<R>,
+ dwarf: &gimli::Dwarf<R>,
+) -> Result<()> {
+ let raw_ranges = dwarf.raw_ranges(unit, offset)?;
+ let raw_ranges: Vec<_> = raw_ranges.collect()?;
+ let mut ranges = dwarf.ranges(unit, offset)?;
+ writeln!(
+ w,
+ "<rnglist at {}+0x{:08x} with {} entries>",
+ if unit.encoding().version < 5 {
+ ".debug_ranges"
+ } else {
+ ".debug_rnglists"
+ },
+ offset.0,
+ raw_ranges.len()
+ )?;
+ for (i, raw) in raw_ranges.iter().enumerate() {
+ write!(w, "\t\t\t[{:2}] ", i)?;
+ match *raw {
+ gimli::RawRngListEntry::AddressOrOffsetPair { begin, end } => {
+ let range = ranges.next()?.unwrap();
+ writeln!(
+ w,
+ "<address pair \
+ low-off: 0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin, range.begin, end, range.end
+ )?;
+ }
+ gimli::RawRngListEntry::BaseAddress { addr } => {
+ writeln!(w, "<new base address 0x{:08x}>", addr)?;
+ }
+ gimli::RawRngListEntry::BaseAddressx { addr } => {
+ let addr_val = dwarf.address(unit, addr)?;
+ writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?;
+ }
+ gimli::RawRngListEntry::StartxEndx { begin, end } => {
+ let begin_val = dwarf.address(unit, begin)?;
+ let end_val = dwarf.address(unit, end)?;
+ let range = if begin_val == end_val {
+ gimli::Range {
+ begin: begin_val,
+ end: end_val,
+ }
+ } else {
+ ranges.next()?.unwrap()
+ };
+ writeln!(
+ w,
+ "<startx-endx \
+ low-off: [{}]0x{:08x} addr 0x{:08x} \
+ high-off: [{}]0x{:08x} addr 0x{:08x}>",
+ begin.0, begin_val, range.begin, end.0, end_val, range.end
+ )?;
+ }
+ gimli::RawRngListEntry::StartxLength { begin, length } => {
+ let begin_val = dwarf.address(unit, begin)?;
+ let range = ranges.next()?.unwrap();
+ writeln!(
+ w,
+ "<startx-length \
+ low-off: [{}]0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin.0, begin_val, range.begin, length, range.end
+ )?;
+ }
+ gimli::RawRngListEntry::OffsetPair { begin, end } => {
+ let range = ranges.next()?.unwrap();
+ writeln!(
+ w,
+ "<offset pair \
+ low-off: 0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin, range.begin, end, range.end
+ )?;
+ }
+ gimli::RawRngListEntry::StartEnd { begin, end } => {
+ let range = if begin == end {
+ gimli::Range { begin, end }
+ } else {
+ ranges.next()?.unwrap()
+ };
+ writeln!(
+ w,
+ "<start-end \
+ low-off: 0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin, range.begin, end, range.end
+ )?;
+ }
+ gimli::RawRngListEntry::StartLength { begin, length } => {
+ let range = ranges.next()?.unwrap();
+ writeln!(
+ w,
+ "<start-length \
+ low-off: 0x{:08x} addr 0x{:08x} \
+ high-off: 0x{:08x} addr 0x{:08x}>",
+ begin, range.begin, length, range.end
+ )?;
+ }
+ };
+ }
+ Ok(())
+}
+
+fn dump_line<R: Reader, W: Write>(w: &mut W, dwarf: &gimli::Dwarf<R>) -> Result<()> {
+ let mut iter = dwarf.units();
+ while let Some(header) = iter.next()? {
+ writeln!(
+ w,
+ "\n.debug_line: line number info for unit at .debug_info offset 0x{:08x}",
+ header.offset().as_debug_info_offset().unwrap().0
+ )?;
+ let unit = match dwarf.unit(header) {
+ Ok(unit) => unit,
+ Err(err) => {
+ writeln_error(
+ w,
+ dwarf,
+ err.into(),
+ "Failed to parse unit root entry for dump_line",
+ )?;
+ continue;
+ }
+ };
+ match dump_line_program(w, &unit, dwarf) {
+ Ok(_) => (),
+ Err(Error::IoError) => return Err(Error::IoError),
+ Err(err) => writeln_error(w, dwarf, err, "Failed to dump line program")?,
+ }
+ }
+ Ok(())
+}
+
+fn dump_line_program<R: Reader, W: Write>(
+ w: &mut W,
+ unit: &gimli::Unit<R>,
+ dwarf: &gimli::Dwarf<R>,
+) -> Result<()> {
+ if let Some(program) = unit.line_program.clone() {
+ {
+ let header = program.header();
+ writeln!(w)?;
+ writeln!(
+ w,
+ "Offset: 0x{:x}",
+ header.offset().0
+ )?;
+ writeln!(
+ w,
+ "Length: {}",
+ header.unit_length()
+ )?;
+ writeln!(
+ w,
+ "DWARF version: {}",
+ header.version()
+ )?;
+ writeln!(
+ w,
+ "Address size: {}",
+ header.address_size()
+ )?;
+ writeln!(
+ w,
+ "Prologue length: {}",
+ header.header_length()
+ )?;
+ writeln!(
+ w,
+ "Minimum instruction length: {}",
+ header.minimum_instruction_length()
+ )?;
+ writeln!(
+ w,
+ "Maximum operations per instruction: {}",
+ header.maximum_operations_per_instruction()
+ )?;
+ writeln!(
+ w,
+ "Default is_stmt: {}",
+ header.default_is_stmt()
+ )?;
+ writeln!(
+ w,
+ "Line base: {}",
+ header.line_base()
+ )?;
+ writeln!(
+ w,
+ "Line range: {}",
+ header.line_range()
+ )?;
+ writeln!(
+ w,
+ "Opcode base: {}",
+ header.opcode_base()
+ )?;
+
+ writeln!(w)?;
+ writeln!(w, "Opcodes:")?;
+ for (i, length) in header
+ .standard_opcode_lengths()
+ .to_slice()?
+ .iter()
+ .enumerate()
+ {
+ writeln!(w, " Opcode {} has {} args", i + 1, length)?;
+ }
+
+ let base = if header.version() >= 5 { 0 } else { 1 };
+ writeln!(w)?;
+ writeln!(w, "The Directory Table:")?;
+ for (i, dir) in header.include_directories().iter().enumerate() {
+ writeln!(
+ w,
+ " {} {}",
+ base + i,
+ dwarf.attr_string(unit, dir.clone())?.to_string_lossy()?
+ )?;
+ }
+
+ writeln!(w)?;
+ writeln!(w, "The File Name Table")?;
+ write!(w, " Entry\tDir\tTime\tSize")?;
+ if header.file_has_md5() {
+ write!(w, "\tMD5\t\t\t\t")?;
+ }
+ writeln!(w, "\tName")?;
+ for (i, file) in header.file_names().iter().enumerate() {
+ write!(
+ w,
+ " {}\t{}\t{}\t{}",
+ base + i,
+ file.directory_index(),
+ file.timestamp(),
+ file.size(),
+ )?;
+ if header.file_has_md5() {
+ let md5 = file.md5();
+ write!(w, "\t")?;
+ for i in 0..16 {
+ write!(w, "{:02X}", md5[i])?;
+ }
+ }
+ writeln!(
+ w,
+ "\t{}",
+ dwarf
+ .attr_string(unit, file.path_name())?
+ .to_string_lossy()?
+ )?;
+ }
+
+ writeln!(w)?;
+ writeln!(w, "Line Number Instructions:")?;
+ let mut instructions = header.instructions();
+ while let Some(instruction) = instructions.next_instruction(header)? {
+ writeln!(w, " {}", instruction)?;
+ }
+
+ writeln!(w)?;
+ writeln!(w, "Line Number Rows:")?;
+ writeln!(w, "<pc> [lno,col]")?;
+ }
+ let mut rows = program.rows();
+ let mut file_index = 0;
+ while let Some((header, row)) = rows.next_row()? {
+ let line = match row.line() {
+ Some(line) => line.get(),
+ None => 0,
+ };
+ let column = match row.column() {
+ gimli::ColumnType::Column(column) => column.get(),
+ gimli::ColumnType::LeftEdge => 0,
+ };
+ write!(w, "0x{:08x} [{:4},{:2}]", row.address(), line, column)?;
+ if row.is_stmt() {
+ write!(w, " NS")?;
+ }
+ if row.basic_block() {
+ write!(w, " BB")?;
+ }
+ if row.end_sequence() {
+ write!(w, " ET")?;
+ }
+ if row.prologue_end() {
+ write!(w, " PE")?;
+ }
+ if row.epilogue_begin() {
+ write!(w, " EB")?;
+ }
+ if row.isa() != 0 {
+ write!(w, " IS={}", row.isa())?;
+ }
+ if row.discriminator() != 0 {
+ write!(w, " DI={}", row.discriminator())?;
+ }
+ if file_index != row.file_index() {
+ file_index = row.file_index();
+ if let Some(file) = row.file(header) {
+ if let Some(directory) = file.directory(header) {
+ write!(
+ w,
+ " uri: \"{}/{}\"",
+ dwarf.attr_string(unit, directory)?.to_string_lossy()?,
+ dwarf
+ .attr_string(unit, file.path_name())?
+ .to_string_lossy()?
+ )?;
+ } else {
+ write!(
+ w,
+ " uri: \"{}\"",
+ dwarf
+ .attr_string(unit, file.path_name())?
+ .to_string_lossy()?
+ )?;
+ }
+ }
+ }
+ writeln!(w)?;
+ }
+ }
+ Ok(())
+}
+
+fn dump_pubnames<R: Reader, W: Write>(
+ w: &mut W,
+ debug_pubnames: &gimli::DebugPubNames<R>,
+ debug_info: &gimli::DebugInfo<R>,
+) -> Result<()> {
+ writeln!(w, "\n.debug_pubnames")?;
+
+ let mut cu_offset;
+ let mut cu_die_offset = gimli::DebugInfoOffset(0);
+ let mut prev_cu_offset = None;
+ let mut pubnames = debug_pubnames.items();
+ while let Some(pubname) = pubnames.next()? {
+ cu_offset = pubname.unit_header_offset();
+ if Some(cu_offset) != prev_cu_offset {
+ let cu = debug_info.header_from_offset(cu_offset)?;
+ cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size());
+ prev_cu_offset = Some(cu_offset);
+ }
+ let die_in_cu = pubname.die_offset();
+ let die_in_sect = cu_offset.0 + die_in_cu.0;
+ writeln!(w,
+ "global die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'",
+ die_in_sect,
+ cu_die_offset.0,
+ die_in_cu.0,
+ cu_offset.0,
+ pubname.name().to_string_lossy()?
+ )?;
+ }
+ Ok(())
+}
+
+fn dump_pubtypes<R: Reader, W: Write>(
+ w: &mut W,
+ debug_pubtypes: &gimli::DebugPubTypes<R>,
+ debug_info: &gimli::DebugInfo<R>,
+) -> Result<()> {
+ writeln!(w, "\n.debug_pubtypes")?;
+
+ let mut cu_offset;
+ let mut cu_die_offset = gimli::DebugInfoOffset(0);
+ let mut prev_cu_offset = None;
+ let mut pubtypes = debug_pubtypes.items();
+ while let Some(pubtype) = pubtypes.next()? {
+ cu_offset = pubtype.unit_header_offset();
+ if Some(cu_offset) != prev_cu_offset {
+ let cu = debug_info.header_from_offset(cu_offset)?;
+ cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size());
+ prev_cu_offset = Some(cu_offset);
+ }
+ let die_in_cu = pubtype.die_offset();
+ let die_in_sect = cu_offset.0 + die_in_cu.0;
+ writeln!(w,
+ "pubtype die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'",
+ die_in_sect,
+ cu_die_offset.0,
+ die_in_cu.0,
+ cu_offset.0,
+ pubtype.name().to_string_lossy()?
+ )?;
+ }
+ Ok(())
+}
+
+fn dump_aranges<R: Reader, W: Write>(
+ w: &mut W,
+ debug_aranges: &gimli::DebugAranges<R>,
+) -> Result<()> {
+ writeln!(w, "\n.debug_aranges")?;
+
+ let mut headers = debug_aranges.headers();
+ while let Some(header) = headers.next()? {
+ writeln!(
+ w,
+ "Address Range Header: length = 0x{:08x}, version = 0x{:04x}, cu_offset = 0x{:08x}, addr_size = 0x{:02x}, seg_size = 0x{:02x}",
+ header.length(),
+ header.encoding().version,
+ header.debug_info_offset().0,
+ header.encoding().address_size,
+ header.segment_size(),
+ )?;
+ let mut aranges = header.entries();
+ while let Some(arange) = aranges.next()? {
+ let range = arange.range();
+ if let Some(segment) = arange.segment() {
+ writeln!(
+ w,
+ "[0x{:016x}, 0x{:016x}) segment 0x{:x}",
+ range.begin, range.end, segment
+ )?;
+ } else {
+ writeln!(w, "[0x{:016x}, 0x{:016x})", range.begin, range.end)?;
+ }
+ }
+ }
+ Ok(())
+}
diff --git a/vendor/gimli/examples/simple.rs b/vendor/gimli/examples/simple.rs
new file mode 100644
index 000000000..b73ab7552
--- /dev/null
+++ b/vendor/gimli/examples/simple.rs
@@ -0,0 +1,67 @@
+//! A simple example of parsing `.debug_info`.
+
+use object::{Object, ObjectSection};
+use std::{borrow, env, fs};
+
+fn main() {
+ for path in env::args().skip(1) {
+ let file = fs::File::open(&path).unwrap();
+ let mmap = unsafe { memmap::Mmap::map(&file).unwrap() };
+ let object = object::File::parse(&*mmap).unwrap();
+ let endian = if object.is_little_endian() {
+ gimli::RunTimeEndian::Little
+ } else {
+ gimli::RunTimeEndian::Big
+ };
+ dump_file(&object, endian).unwrap();
+ }
+}
+
+fn dump_file(object: &object::File, endian: gimli::RunTimeEndian) -> Result<(), gimli::Error> {
+ // Load a section and return as `Cow<[u8]>`.
+ let load_section = |id: gimli::SectionId| -> Result<borrow::Cow<[u8]>, gimli::Error> {
+ match object.section_by_name(id.name()) {
+ Some(ref section) => Ok(section
+ .uncompressed_data()
+ .unwrap_or(borrow::Cow::Borrowed(&[][..]))),
+ None => Ok(borrow::Cow::Borrowed(&[][..])),
+ }
+ };
+
+ // Load all of the sections.
+ let dwarf_cow = gimli::Dwarf::load(&load_section)?;
+
+ // Borrow a `Cow<[u8]>` to create an `EndianSlice`.
+ let borrow_section: &dyn for<'a> Fn(
+ &'a borrow::Cow<[u8]>,
+ ) -> gimli::EndianSlice<'a, gimli::RunTimeEndian> =
+ &|section| gimli::EndianSlice::new(&*section, endian);
+
+ // Create `EndianSlice`s for all of the sections.
+ let dwarf = dwarf_cow.borrow(&borrow_section);
+
+ // Iterate over the compilation units.
+ let mut iter = dwarf.units();
+ while let Some(header) = iter.next()? {
+ println!(
+ "Unit at <.debug_info+0x{:x}>",
+ header.offset().as_debug_info_offset().unwrap().0
+ );
+ let unit = dwarf.unit(header)?;
+
+ // Iterate over the Debugging Information Entries (DIEs) in the unit.
+ let mut depth = 0;
+ let mut entries = unit.entries();
+ while let Some((delta_depth, entry)) = entries.next_dfs()? {
+ depth += delta_depth;
+ println!("<{}><{:x}> {}", depth, entry.offset().0, entry.tag());
+
+ // Iterate over the attributes in the DIE.
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next()? {
+ println!(" {}: {:?}", attr.name(), attr.value());
+ }
+ }
+ }
+ Ok(())
+}
diff --git a/vendor/gimli/examples/simple_line.rs b/vendor/gimli/examples/simple_line.rs
new file mode 100644
index 000000000..bf1114f90
--- /dev/null
+++ b/vendor/gimli/examples/simple_line.rs
@@ -0,0 +1,99 @@
+//! A simple example of parsing `.debug_line`.
+
+use object::{Object, ObjectSection};
+use std::{borrow, env, fs, path};
+
+fn main() {
+ for path in env::args().skip(1) {
+ let file = fs::File::open(&path).unwrap();
+ let mmap = unsafe { memmap::Mmap::map(&file).unwrap() };
+ let object = object::File::parse(&*mmap).unwrap();
+ let endian = if object.is_little_endian() {
+ gimli::RunTimeEndian::Little
+ } else {
+ gimli::RunTimeEndian::Big
+ };
+ dump_file(&object, endian).unwrap();
+ }
+}
+
+fn dump_file(object: &object::File, endian: gimli::RunTimeEndian) -> Result<(), gimli::Error> {
+ // Load a section and return as `Cow<[u8]>`.
+ let load_section = |id: gimli::SectionId| -> Result<borrow::Cow<[u8]>, gimli::Error> {
+ match object.section_by_name(id.name()) {
+ Some(ref section) => Ok(section
+ .uncompressed_data()
+ .unwrap_or(borrow::Cow::Borrowed(&[][..]))),
+ None => Ok(borrow::Cow::Borrowed(&[][..])),
+ }
+ };
+
+ // Load all of the sections.
+ let dwarf_cow = gimli::Dwarf::load(&load_section)?;
+
+ // Borrow a `Cow<[u8]>` to create an `EndianSlice`.
+ let borrow_section: &dyn for<'a> Fn(
+ &'a borrow::Cow<[u8]>,
+ ) -> gimli::EndianSlice<'a, gimli::RunTimeEndian> =
+ &|section| gimli::EndianSlice::new(&*section, endian);
+
+ // Create `EndianSlice`s for all of the sections.
+ let dwarf = dwarf_cow.borrow(&borrow_section);
+
+ // Iterate over the compilation units.
+ let mut iter = dwarf.units();
+ while let Some(header) = iter.next()? {
+ println!(
+ "Line number info for unit at <.debug_info+0x{:x}>",
+ header.offset().as_debug_info_offset().unwrap().0
+ );
+ let unit = dwarf.unit(header)?;
+
+ // Get the line program for the compilation unit.
+ if let Some(program) = unit.line_program.clone() {
+ let comp_dir = if let Some(ref dir) = unit.comp_dir {
+ path::PathBuf::from(dir.to_string_lossy().into_owned())
+ } else {
+ path::PathBuf::new()
+ };
+
+ // Iterate over the line program rows.
+ let mut rows = program.rows();
+ while let Some((header, row)) = rows.next_row()? {
+ if row.end_sequence() {
+ // End of sequence indicates a possible gap in addresses.
+ println!("{:x} end-sequence", row.address());
+ } else {
+ // Determine the path. Real applications should cache this for performance.
+ let mut path = path::PathBuf::new();
+ if let Some(file) = row.file(header) {
+ path = comp_dir.clone();
+ if let Some(dir) = file.directory(header) {
+ path.push(dwarf.attr_string(&unit, dir)?.to_string_lossy().as_ref());
+ }
+ path.push(
+ dwarf
+ .attr_string(&unit, file.path_name())?
+ .to_string_lossy()
+ .as_ref(),
+ );
+ }
+
+ // Determine line/column. DWARF line/column is never 0, so we use that
+ // but other applications may want to display this differently.
+ let line = match row.line() {
+ Some(line) => line.get(),
+ None => 0,
+ };
+ let column = match row.column() {
+ gimli::ColumnType::LeftEdge => 0,
+ gimli::ColumnType::Column(column) => column.get(),
+ };
+
+ println!("{:x} {}:{}:{}", row.address(), path.display(), line, column);
+ }
+ }
+ }
+ }
+ Ok(())
+}
diff --git a/vendor/gimli/fixtures/self/README.md b/vendor/gimli/fixtures/self/README.md
new file mode 100644
index 000000000..3847c3852
--- /dev/null
+++ b/vendor/gimli/fixtures/self/README.md
@@ -0,0 +1,147 @@
+# What are these files?
+
+These files are the DWARF data generated for (an early version of) this
+library. Each file corresponds is a section from the built library's object
+file. By splitting the sections out to their own files, we don't need to worry
+about cross platform and cross object file format issues when running examples.
+
+# Updating and adding new sections
+
+## OSX
+
+Use `otool` to list the sections of a binary:
+
+```
+$ otool -l path/to/binary
+```
+
+You should see output similar to this:
+
+```
+Load command 0
+ cmd LC_SEGMENT_64
+ cmdsize 72
+ segname __PAGEZERO
+ vmaddr 0x0000000000000000
+ vmsize 0x0000000100000000
+ fileoff 0
+ filesize 0
+ maxprot 0x00000000
+ initprot 0x00000000
+ nsects 0
+ flags 0x0
+Load command 1
+ cmd LC_SEGMENT_64
+ cmdsize 712
+ segname __TEXT
+ vmaddr 0x0000000100000000
+ vmsize 0x00000000001b7000
+ fileoff 0
+ filesize 1798144
+ maxprot 0x00000007
+ initprot 0x00000005
+ nsects 8
+ flags 0x0
+Section
+ sectname __text
+ segname __TEXT
+ addr 0x0000000100000a50
+ size 0x0000000000170716
+ offset 2640
+ align 2^4 (16)
+ reloff 0
+ nreloc 0
+ flags 0x80000400
+ reserved1 0
+ reserved2 0
+```
+
+Etc.
+
+Find the `Section` entry of the section you'd like to isolate. For example, if
+you're looking for `eh_frame`, find an entry like this:
+
+```
+Section
+ sectname __eh_frame
+ segname __TEXT
+ addr 0x0000000100192f38
+ size 0x00000000000240c8
+ offset 1650488
+ align 2^3 (8)
+ reloff 0
+ nreloc 0
+ flags 0x00000000
+ reserved1 0
+ reserved2 0
+```
+
+Then use `dd` to copy `size` bytes starting from `offset`:
+
+```
+$ dd bs=1 skip=1650488 count=$(printf "%d" 0x00000000000240c8) if=path/to/binary of=fixtures/self/eh_frame
+```
+
+Finally, use `otool` and `hexdump` to verify that the isolated section has the
+same data as the section within the binary:
+
+```
+$ otool -s __TEXT __eh_frame path/to/binary | head
+path/to/binary:
+Contents of (__TEXT,__eh_frame) section
+0000000100192f38 14 00 00 00 00 00 00 00 01 7a 52 00 01 78 10 01
+0000000100192f48 10 0c 07 08 90 01 00 00 24 00 00 00 1c 00 00 00
+0000000100192f58 f8 da e6 ff ff ff ff ff 66 00 00 00 00 00 00 00
+0000000100192f68 00 41 0e 10 86 02 43 0d 06 00 00 00 00 00 00 00
+0000000100192f78 1c 00 00 00 00 00 00 00 01 7a 50 4c 52 00 01 78
+0000000100192f88 10 07 9b 9d 40 02 00 10 10 0c 07 08 90 01 00 00
+0000000100192f98 2c 00 00 00 24 00 00 00 20 db e6 ff ff ff ff ff
+0000000100192fa8 8d 00 00 00 00 00 00 00 08 37 e7 fd ff ff ff ff
+
+$ otool -s __TEXT __eh_frame path/to/binary | tail
+00000001001b6f68 9a 0a 00 00 00 00 00 00 00 41 0e 10 86 02 43 0d
+00000001001b6f78 06 50 83 07 8c 06 8d 05 8e 04 8f 03 00 00 00 00
+00000001001b6f88 24 00 00 00 7c 0e 00 00 30 a0 fb ff ff ff ff ff
+00000001001b6f98 15 00 00 00 00 00 00 00 00 41 0e 10 86 02 43 0d
+00000001001b6fa8 06 00 00 00 00 00 00 00 24 00 00 00 a4 0e 00 00
+00000001001b6fb8 28 a0 fb ff ff ff ff ff 1c 00 00 00 00 00 00 00
+00000001001b6fc8 00 41 0e 10 86 02 43 0d 06 00 00 00 00 00 00 00
+00000001001b6fd8 24 00 00 00 cc 0e 00 00 20 a0 fb ff ff ff ff ff
+00000001001b6fe8 66 01 00 00 00 00 00 00 00 41 0e 10 86 02 43 0d
+00000001001b6ff8 06 00 00 00 00 00 00 00
+```
+
+This should be the same, ignoring the leading offsets:
+
+```
+$ hexdump fixtures/self/eh_frame | head
+0000000 14 00 00 00 00 00 00 00 01 7a 52 00 01 78 10 01
+0000010 10 0c 07 08 90 01 00 00 24 00 00 00 1c 00 00 00
+0000020 f8 da e6 ff ff ff ff ff 66 00 00 00 00 00 00 00
+0000030 00 41 0e 10 86 02 43 0d 06 00 00 00 00 00 00 00
+0000040 1c 00 00 00 00 00 00 00 01 7a 50 4c 52 00 01 78
+0000050 10 07 9b 9d 40 02 00 10 10 0c 07 08 90 01 00 00
+0000060 2c 00 00 00 24 00 00 00 20 db e6 ff ff ff ff ff
+0000070 8d 00 00 00 00 00 00 00 08 37 e7 fd ff ff ff ff
+0000080 ff 41 0e 10 86 02 43 0d 06 00 00 00 00 00 00 00
+0000090 24 00 00 00 94 00 00 00 80 db e6 ff ff ff ff ff
+
+$ hexdump fixtures/self/eh_frame | tail
+0024040 06 50 83 07 8c 06 8d 05 8e 04 8f 03 00 00 00 00
+0024050 24 00 00 00 7c 0e 00 00 30 a0 fb ff ff ff ff ff
+0024060 15 00 00 00 00 00 00 00 00 41 0e 10 86 02 43 0d
+0024070 06 00 00 00 00 00 00 00 24 00 00 00 a4 0e 00 00
+0024080 28 a0 fb ff ff ff ff ff 1c 00 00 00 00 00 00 00
+0024090 00 41 0e 10 86 02 43 0d 06 00 00 00 00 00 00 00
+00240a0 24 00 00 00 cc 0e 00 00 20 a0 fb ff ff ff ff ff
+00240b0 66 01 00 00 00 00 00 00 00 41 0e 10 86 02 43 0d
+00240c0 06 00 00 00 00 00 00 00
+```
+
+## Linux
+
+Something like this:
+
+```
+objcopy --dump-section .eh_frame=fixtures/self/eh_frame path/to/binary
+```
diff --git a/vendor/gimli/fixtures/self/debug_abbrev b/vendor/gimli/fixtures/self/debug_abbrev
new file mode 100644
index 000000000..809e61152
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_abbrev
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_aranges b/vendor/gimli/fixtures/self/debug_aranges
new file mode 100644
index 000000000..b2d983d78
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_aranges
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_info b/vendor/gimli/fixtures/self/debug_info
new file mode 100644
index 000000000..aa430a5ce
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_info
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_inlined b/vendor/gimli/fixtures/self/debug_inlined
new file mode 100644
index 000000000..949d18c93
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_inlined
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_line b/vendor/gimli/fixtures/self/debug_line
new file mode 100644
index 000000000..896a07364
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_line
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_loc b/vendor/gimli/fixtures/self/debug_loc
new file mode 100644
index 000000000..3fcdb32ba
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_loc
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_pubnames b/vendor/gimli/fixtures/self/debug_pubnames
new file mode 100644
index 000000000..bbcd62e24
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_pubnames
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_pubtypes b/vendor/gimli/fixtures/self/debug_pubtypes
new file mode 100644
index 000000000..68b4e0405
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_pubtypes
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_ranges b/vendor/gimli/fixtures/self/debug_ranges
new file mode 100644
index 000000000..a5f52ed4a
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_ranges
Binary files differ
diff --git a/vendor/gimli/fixtures/self/debug_str b/vendor/gimli/fixtures/self/debug_str
new file mode 100644
index 000000000..da35ee574
--- /dev/null
+++ b/vendor/gimli/fixtures/self/debug_str
Binary files differ
diff --git a/vendor/gimli/fixtures/self/eh_frame b/vendor/gimli/fixtures/self/eh_frame
new file mode 100644
index 000000000..1d4df1a61
--- /dev/null
+++ b/vendor/gimli/fixtures/self/eh_frame
Binary files differ
diff --git a/vendor/gimli/fixtures/self/eh_frame_hdr b/vendor/gimli/fixtures/self/eh_frame_hdr
new file mode 100644
index 000000000..a590ba213
--- /dev/null
+++ b/vendor/gimli/fixtures/self/eh_frame_hdr
Binary files differ
diff --git a/vendor/gimli/rustfmt.toml b/vendor/gimli/rustfmt.toml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/gimli/rustfmt.toml
diff --git a/vendor/gimli/src/arch.rs b/vendor/gimli/src/arch.rs
new file mode 100644
index 000000000..f5b2e5ed8
--- /dev/null
+++ b/vendor/gimli/src/arch.rs
@@ -0,0 +1,603 @@
+use 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<Register> {
+ 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://developer.arm.com/documentation/ihi0040/c/).
+#[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"),
+
+ 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"),
+},
+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://developer.arm.com/documentation/ihi0057/b/).
+#[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"),
+
+ 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"),
+});
+
+/// 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"),
+});
diff --git a/vendor/gimli/src/common.rs b/vendor/gimli/src/common.rs
new file mode 100644
index 000000000..79cf76616
--- /dev/null
+++ b/vendor/gimli/src/common.rs
@@ -0,0 +1,363 @@
+/// 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,
+ }
+ }
+}
+
+/// 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,
+
+ // The size of a segment selector.
+ // TODO: pub segment_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<T = usize>(pub T);
+
+/// An offset to a set of entries in the `.debug_addr` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugAddrBase<T = usize>(pub T);
+
+/// An index into a set of addresses in the `.debug_addr` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugAddrIndex<T = usize>(pub T);
+
+/// An offset into the `.debug_aranges` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugArangesOffset<T = usize>(pub T);
+
+/// An offset into the `.debug_info` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
+pub struct DebugInfoOffset<T = usize>(pub T);
+
+/// An offset into the `.debug_line` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugLineOffset<T = usize>(pub T);
+
+/// An offset into the `.debug_line_str` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugLineStrOffset<T = usize>(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<T = usize>(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<T = usize>(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<T = usize>(pub T);
+
+/// An offset into the `.debug_macinfo` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct DebugMacinfoOffset<T = usize>(pub T);
+
+/// An offset into the `.debug_macro` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct DebugMacroOffset<T = usize>(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<T = usize>(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<T = usize>(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<T = usize>(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<T = usize>(pub T);
+
+/// An offset into the `.debug_str` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugStrOffset<T = usize>(pub T);
+
+/// An offset to a set of entries in the `.debug_str_offsets` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugStrOffsetsBase<T = usize>(pub T);
+
+/// An index into a set of entries in the `.debug_str_offsets` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct DebugStrOffsetsIndex<T = usize>(pub T);
+
+/// An offset into the `.debug_types` section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
+pub struct DebugTypesOffset<T = usize>(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<T = usize>(pub T);
+
+impl<T> From<T> for DebugFrameOffset<T> {
+ #[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<T = usize>(pub T);
+
+impl<T> From<T> for EhFrameOffset<T> {
+ #[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<T = usize> {
+ /// An offset into the `.debug_info` section.
+ DebugInfoOffset(DebugInfoOffset<T>),
+ /// An offset into the `.debug_types` section.
+ DebugTypesOffset(DebugTypesOffset<T>),
+}
+
+impl<T> From<DebugInfoOffset<T>> for UnitSectionOffset<T> {
+ fn from(offset: DebugInfoOffset<T>) -> Self {
+ UnitSectionOffset::DebugInfoOffset(offset)
+ }
+}
+
+impl<T> From<DebugTypesOffset<T>> for UnitSectionOffset<T> {
+ fn from(offset: DebugTypesOffset<T>) -> Self {
+ UnitSectionOffset::DebugTypesOffset(offset)
+ }
+}
+
+impl<T> UnitSectionOffset<T>
+where
+ T: Clone,
+{
+ /// Returns the `DebugInfoOffset` inside, or `None` otherwise.
+ pub fn as_debug_info_offset(&self) -> Option<DebugInfoOffset<T>> {
+ 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<DebugTypesOffset<T>> {
+ 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::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,
+ })
+ }
+}
+
+/// 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
+ }
+}
diff --git a/vendor/gimli/src/constants.rs b/vendor/gimli/src/constants.rs
new file mode 100644
index 000000000..c8d6a1662
--- /dev/null
+++ b/vendor/gimli/src/constants.rs
@@ -0,0 +1,1425 @@
+// 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;
+
+// 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),+ $(,)? }) => {
+ $(#[$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);
+ )+
+
+ 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,
+});
+
+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,
+
+// 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<usize> {
+ 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_lo_user = 0x2000,
+ 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 https://refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
+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 OSX 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 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 = DwEhPe(DW_EH_PE_pcrel.0 | DW_EH_PE_uleb128.0);
+ assert_eq!(encoding.format(), DW_EH_PE_uleb128);
+ }
+
+ #[test]
+ fn test_dw_eh_pe_application() {
+ let encoding = DwEhPe(DW_EH_PE_pcrel.0 | DW_EH_PE_uleb128.0);
+ assert_eq!(encoding.application(), DW_EH_PE_pcrel);
+ }
+
+ #[test]
+ fn test_dw_eh_pe_is_absent() {
+ assert_eq!(DW_EH_PE_absptr.is_absent(), false);
+ assert_eq!(DW_EH_PE_omit.is_absent(), true);
+ }
+
+ #[test]
+ fn test_dw_eh_pe_is_valid_encoding_ok() {
+ let encoding = DwEhPe(DW_EH_PE_uleb128.0 | DW_EH_PE_pcrel.0);
+ 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_eq!(encoding.is_valid_encoding(), false);
+ }
+
+ #[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_eq!(encoding.is_valid_encoding(), false);
+ }
+}
diff --git a/vendor/gimli/src/endianity.rs b/vendor/gimli/src/endianity.rs
new file mode 100644
index 000000000..3201551f1
--- /dev/null
+++ b/vendor/gimli/src/endianity.rs
@@ -0,0 +1,256 @@
+//! 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;
diff --git a/vendor/gimli/src/leb128.rs b/vendor/gimli/src/leb128.rs
new file mode 100644
index 000000000..de81cfdcf
--- /dev/null
+++ b/vendor/gimli/src/leb128.rs
@@ -0,0 +1,612 @@
+//! 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(core::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: Reader>(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: Reader>(r: &mut R) -> Result<u64> {
+ 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: Reader>(r: &mut R) -> Result<u16> {
+ 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: Reader>(r: &mut R) -> Result<i64> {
+ 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>(w: &mut W, mut val: u64) -> Result<usize, io::Error>
+ 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>(w: &mut W, mut val: i64) -> Result<usize, io::Error>
+ 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<T> ResultExt for Result<T, Error> {
+ 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(core::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);
+ }
+ }
+}
diff --git a/vendor/gimli/src/lib.rs b/vendor/gimli/src/lib.rs
new file mode 100644
index 000000000..7ac1820e3
--- /dev/null
+++ b/vendor/gimli/src/lib.rs
@@ -0,0 +1,76 @@
+//! `gimli` is a library for reading and writing the
+//! [DWARF debugging format](http://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)]
+// Allow clippy warnings when we aren't building with clippy.
+#![allow(unknown_lints)]
+// False positives with `fallible_iterator`.
+#![allow(clippy::should_implement_trait)]
+// Many false positives involving `continue`.
+#![allow(clippy::never_loop)]
+// False positives when block expressions are used inside an assertion.
+#![allow(clippy::panic_params)]
+#![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 = "stable_deref_trait")]
+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::{BigEndian, Endianity, LittleEndian, NativeEndian, RunTimeEndian};
+
+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;
diff --git a/vendor/gimli/src/read/abbrev.rs b/vendor/gimli/src/read/abbrev.rs
new file mode 100644
index 000000000..5f0e7bed7
--- /dev/null
+++ b/vendor/gimli/src/read/abbrev.rs
@@ -0,0 +1,996 @@
+//! Functions for parsing DWARF debugging abbreviations.
+
+use alloc::collections::btree_map;
+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::{EndianSlice, Error, Reader, 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<R> {
+ debug_abbrev_section: R,
+}
+
+impl<'input, Endian> DebugAbbrev<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugAbbrev<R> {
+ /// 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<R::Offset>,
+ ) -> Result<Abbreviations> {
+ let input = &mut self.debug_abbrev_section.clone();
+ input.skip(debug_abbrev_offset.0)?;
+ Abbreviations::parse(input)
+ }
+}
+
+impl<T> DebugAbbrev<T> {
+ /// Create a `DebugAbbrev` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugAbbrev<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAbbrev<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.debug_abbrev_section).into()
+ }
+}
+
+impl<R> Section<R> for DebugAbbrev<R> {
+ fn id() -> SectionId {
+ SectionId::DebugAbbrev
+ }
+
+ fn reader(&self) -> &R {
+ &self.debug_abbrev_section
+ }
+}
+
+impl<R> From<R> for DebugAbbrev<R> {
+ fn from(debug_abbrev_section: R) -> Self {
+ DebugAbbrev {
+ debug_abbrev_section,
+ }
+ }
+}
+
+/// 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<Abbreviation>,
+ map: btree_map::BTreeMap<u64, Abbreviation>,
+}
+
+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<R: Reader>(input: &mut R) -> Result<Abbreviations> {
+ 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<R: Reader>(input: &mut R) -> Result<constants::DwTag> {
+ 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<R: Reader>(input: &mut R) -> Result<constants::DwChildren> {
+ 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<R: Reader>(input: &mut R) -> Result<Attributes> {
+ 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<R: Reader>(input: &mut R) -> Result<Option<Abbreviation>> {
+ 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<AttributeSpecification>),
+}
+
+// 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) => return 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<AttributeSpecification> for Attributes {
+ fn from_iter<I>(iter: I) -> Attributes
+ where
+ I: IntoIterator<Item = AttributeSpecification>,
+ {
+ let mut list = Attributes::new();
+ for item in iter {
+ list.push(item);
+ }
+ return list;
+ }
+}
+
+impl From<Vec<AttributeSpecification>> for Attributes {
+ fn from(list: Vec<AttributeSpecification>) -> 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<i64>,
+ ) -> 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<i64> {
+ 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<R: Reader>(&self, header: &UnitHeader<R>) -> Option<usize> {
+ get_attribute_size(self.form, header.encoding()).map(usize::from)
+ }
+
+ /// Parse an attribute's form.
+ fn parse_form<R: Reader>(input: &mut R) -> Result<constants::DwForm> {
+ 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<R: Reader>(input: &mut R) -> Result<Option<AttributeSpecification>> {
+ 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<u8> {
+ 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 |
+
+ // We don't know the size of unknown forms.
+ _ => None,
+ }
+}
+
+#[cfg(test)]
+pub 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());
+ }
+}
diff --git a/vendor/gimli/src/read/addr.rs b/vendor/gimli/src/read/addr.rs
new file mode 100644
index 000000000..593f9fe3c
--- /dev/null
+++ b/vendor/gimli/src/read/addr.rs
@@ -0,0 +1,128 @@
+use 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<R> {
+ section: R,
+}
+
+impl<R: Reader> DebugAddr<R> {
+ // 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<R::Offset>,
+ index: DebugAddrIndex<R::Offset>,
+ ) -> Result<u64> {
+ 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<T> DebugAddr<T> {
+ /// Create a `DebugAddr` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugAddr<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAddr<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.section).into()
+ }
+}
+
+impl<R> Section<R> for DebugAddr<R> {
+ fn id() -> SectionId {
+ SectionId::DebugAddr
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugAddr<R> {
+ 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 vec![Format::Dwarf32, Format::Dwarf64] {
+ for address_size in vec![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(&section, 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)
+ );
+ }
+ }
+ }
+}
diff --git a/vendor/gimli/src/read/aranges.rs b/vendor/gimli/src/read/aranges.rs
new file mode 100644
index 000000000..c2d22850c
--- /dev/null
+++ b/vendor/gimli/src/read/aranges.rs
@@ -0,0 +1,660 @@
+use crate::common::{DebugArangesOffset, DebugInfoOffset, Encoding, SectionId};
+use crate::endianity::Endianity;
+use crate::read::{EndianSlice, Error, Range, Reader, 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<R> {
+ section: R,
+}
+
+impl<'input, Endian> DebugAranges<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugAranges<R> {
+ /// Iterate the sets of entries in the `.debug_aranges` section.
+ ///
+ /// Each set of entries belongs to a single unit.
+ pub fn headers(&self) -> ArangeHeaderIter<R> {
+ 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<R::Offset>) -> Result<ArangeHeader<R>> {
+ let mut input = self.section.clone();
+ input.skip(offset.0)?;
+ ArangeHeader::parse(&mut input, offset)
+ }
+}
+
+impl<T> DebugAranges<T> {
+ /// Create a `DebugAranges` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugAranges<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAranges<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.section).into()
+ }
+}
+
+impl<R> Section<R> for DebugAranges<R> {
+ fn id() -> SectionId {
+ SectionId::DebugAranges
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugAranges<R> {
+ fn from(section: R) -> Self {
+ DebugAranges { section }
+ }
+}
+
+/// An iterator over the headers of a `.debug_aranges` section.
+#[derive(Clone, Debug)]
+pub struct ArangeHeaderIter<R: Reader> {
+ input: R,
+ offset: DebugArangesOffset<R::Offset>,
+}
+
+impl<R: Reader> ArangeHeaderIter<R> {
+ /// Advance the iterator to the next header.
+ pub fn next(&mut self) -> Result<Option<ArangeHeader<R>>> {
+ 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<R: Reader> fallible_iterator::FallibleIterator for ArangeHeaderIter<R> {
+ type Item = ArangeHeader<R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ offset: DebugArangesOffset<Offset>,
+ encoding: Encoding,
+ length: Offset,
+ debug_info_offset: DebugInfoOffset<Offset>,
+ segment_size: u8,
+ entries: R,
+}
+
+impl<R, Offset> ArangeHeader<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ fn parse(input: &mut R, offset: DebugArangesOffset<Offset>) -> Result<Self> {
+ 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_u8()?;
+ let segment_size = rest.read_u8()?;
+
+ // 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, the size of a segment selector
+ // plus twice the size of an address).
+ let tuple_length = address_size
+ .checked_mul(2)
+ .and_then(|x| x.checked_add(segment_size))
+ .ok_or(Error::InvalidAddressRange)?;
+ if tuple_length == 0 {
+ return Err(Error::InvalidAddressRange)?;
+ }
+ 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,
+ // TODO: segment_size
+ };
+ Ok(ArangeHeader {
+ offset,
+ encoding,
+ length,
+ debug_info_offset,
+ segment_size,
+ entries: rest,
+ })
+ }
+
+ /// Return the offset of this header within the `.debug_aranges` section.
+ #[inline]
+ pub fn offset(&self) -> DebugArangesOffset<Offset> {
+ 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 segment size for this set of entries.
+ #[inline]
+ pub fn segment_size(&self) -> u8 {
+ self.segment_size
+ }
+
+ /// Return the offset into the .debug_info section for this set of arange entries.
+ #[inline]
+ pub fn debug_info_offset(&self) -> DebugInfoOffset<Offset> {
+ self.debug_info_offset
+ }
+
+ /// Return the arange entries in this set.
+ #[inline]
+ pub fn entries(&self) -> ArangeEntryIter<R> {
+ ArangeEntryIter {
+ input: self.entries.clone(),
+ encoding: self.encoding,
+ segment_size: self.segment_size,
+ }
+ }
+}
+
+/// 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<R: Reader> {
+ input: R,
+ encoding: Encoding,
+ segment_size: u8,
+}
+
+impl<R: Reader> ArangeEntryIter<R> {
+ /// 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<Option<ArangeEntry>> {
+ if self.input.is_empty() {
+ return Ok(None);
+ }
+
+ match ArangeEntry::parse(&mut self.input, self.encoding, self.segment_size) {
+ Ok(Some(entry)) => Ok(Some(entry)),
+ Ok(None) => {
+ self.input.empty();
+ Ok(None)
+ }
+ Err(e) => {
+ self.input.empty();
+ Err(e)
+ }
+ }
+ }
+}
+
+#[cfg(feature = "fallible-iterator")]
+impl<R: Reader> fallible_iterator::FallibleIterator for ArangeEntryIter<R> {
+ type Item = ArangeEntry;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
+ ArangeEntryIter::next(self)
+ }
+}
+
+/// A single parsed arange.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ArangeEntry {
+ segment: Option<u64>,
+ address: u64,
+ length: u64,
+}
+
+impl ArangeEntry {
+ /// Parse a single arange. Return `None` for the null arange, `Some` for an actual arange.
+ fn parse<R: Reader>(
+ input: &mut R,
+ encoding: Encoding,
+ segment_size: u8,
+ ) -> Result<Option<Self>> {
+ let address_size = encoding.address_size;
+
+ let tuple_length = R::Offset::from_u8(2 * address_size + segment_size);
+ if tuple_length > input.len() {
+ input.empty();
+ return Ok(None);
+ }
+
+ let segment = if segment_size != 0 {
+ input.read_address(segment_size)?
+ } else {
+ 0
+ };
+ let address = input.read_address(address_size)?;
+ let length = input.read_address(address_size)?;
+
+ match (segment, address, 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, 0) => Self::parse(input, encoding, segment_size),
+ _ => Ok(Some(ArangeEntry {
+ segment: if segment_size != 0 {
+ Some(segment)
+ } else {
+ None
+ },
+ address,
+ length,
+ })),
+ }
+ }
+
+ /// Return the segment selector of this arange.
+ #[inline]
+ pub fn segment(&self) -> Option<u64> {
+ self.segment
+ }
+
+ /// Return the beginning address of this arange.
+ #[inline]
+ pub fn address(&self) -> u64 {
+ self.address
+ }
+
+ /// Return the length of this arange.
+ #[inline]
+ pub fn length(&self) -> u64 {
+ self.length
+ }
+
+ /// Return the range.
+ #[inline]
+ pub fn range(&self) -> Range {
+ Range {
+ begin: self.address,
+ end: self.address.wrapping_add(self.length),
+ }
+ }
+}
+
+#[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 = 32.
+ 0x20, 0x00, 0x00, 0x00,
+ // Version.
+ 0x02, 0x00,
+ // Offset.
+ 0x01, 0x02, 0x03, 0x04,
+ // Address size.
+ 0x08,
+ // Segment size.
+ 0x04,
+ // 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 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: 0x20,
+ debug_info_offset: DebugInfoOffset(0x0403_0201),
+ segment_size: 4,
+ 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.
+ 0xff,
+ // 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::InvalidAddressRange);
+ }
+
+ #[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::InvalidAddressRange);
+ }
+
+ #[test]
+ fn test_parse_entry_ok() {
+ let encoding = Encoding {
+ format: Format::Dwarf32,
+ version: 2,
+ address_size: 4,
+ };
+ let segment_size = 0;
+ let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
+ let rest = &mut EndianSlice::new(&buf, LittleEndian);
+ let entry =
+ ArangeEntry::parse(rest, encoding, segment_size).expect("should parse entry ok");
+ assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
+ assert_eq!(
+ entry,
+ Some(ArangeEntry {
+ segment: None,
+ address: 0x0403_0201,
+ length: 0x0807_0605,
+ })
+ );
+ }
+
+ #[test]
+ fn test_parse_entry_segment() {
+ let encoding = Encoding {
+ format: Format::Dwarf32,
+ version: 2,
+ address_size: 4,
+ };
+ let segment_size = 8;
+ #[rustfmt::skip]
+ let buf = [
+ // Segment.
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ // Address.
+ 0x01, 0x02, 0x03, 0x04,
+ // Length.
+ 0x05, 0x06, 0x07, 0x08,
+ // Next tuple.
+ 0x09
+ ];
+ let rest = &mut EndianSlice::new(&buf, LittleEndian);
+ let entry =
+ ArangeEntry::parse(rest, encoding, segment_size).expect("should parse entry ok");
+ assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
+ assert_eq!(
+ entry,
+ Some(ArangeEntry {
+ segment: Some(0x1817_1615_1413_1211),
+ address: 0x0403_0201,
+ length: 0x0807_0605,
+ })
+ );
+ }
+
+ #[test]
+ fn test_parse_entry_zero() {
+ let encoding = Encoding {
+ format: Format::Dwarf32,
+ version: 2,
+ address_size: 4,
+ };
+ let segment_size = 0;
+ #[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 rest = &mut EndianSlice::new(&buf, LittleEndian);
+ let entry =
+ ArangeEntry::parse(rest, encoding, segment_size).expect("should parse entry ok");
+ assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
+ assert_eq!(
+ entry,
+ Some(ArangeEntry {
+ segment: None,
+ address: 0x0403_0201,
+ length: 0x0807_0605,
+ })
+ );
+ }
+}
diff --git a/vendor/gimli/src/read/cfi.rs b/vendor/gimli/src/read/cfi.rs
new file mode 100644
index 000000000..b6a81f3f4
--- /dev/null
+++ b/vendor/gimli/src/read/cfi.rs
@@ -0,0 +1,7463 @@
+#[cfg(feature = "read")]
+use alloc::vec::Vec;
+
+use core::cmp::{Ord, 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};
+use crate::constants::{self, DwEhPe};
+use crate::endianity::Endianity;
+use crate::read::{
+ EndianSlice, Error, Expression, Reader, 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<R: Reader> {
+ section: R,
+ address_size: u8,
+ segment_size: u8,
+}
+
+impl<R: Reader> DebugFrame<R> {
+ /// 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 size of a segment selector in bytes.
+ ///
+ /// This defaults to 0.
+ /// This is only used if the CIE version is less than 4.
+ pub fn set_segment_size(&mut self, segment_size: u8) {
+ self.segment_size = segment_size
+ }
+}
+
+impl<'input, Endian> DebugFrame<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> Section<R> for DebugFrame<R> {
+ fn id() -> SectionId {
+ SectionId::DebugFrame
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R: Reader> From<R> for DebugFrame<R> {
+ fn from(section: R) -> Self {
+ // Default to no segments and native word size.
+ DebugFrame {
+ section,
+ address_size: mem::size_of::<usize>() as u8,
+ segment_size: 0,
+ }
+ }
+}
+
+/// `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: Reader>(R);
+
+/// `ParsedEhFrameHdr` contains the parsed information from the `.eh_frame_hdr` section.
+#[derive(Clone, Debug)]
+pub struct ParsedEhFrameHdr<R: Reader> {
+ address_size: u8,
+ section: R,
+
+ eh_frame_ptr: Pointer,
+ fde_count: u64,
+ table_enc: DwEhPe,
+ table: R,
+}
+
+impl<'input, Endian> EhFrameHdr<EndianSlice<'input, Endian>>
+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<R: Reader> EhFrameHdr<R> {
+ /// Parses this `EhFrameHdr` to a `ParsedEhFrameHdr`.
+ pub fn parse(&self, bases: &BaseAddresses, address_size: u8) -> Result<ParsedEhFrameHdr<R>> {
+ 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, &parameters, &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 {
+ let ptr = parse_encoded_pointer(fde_count_enc, &parameters, &mut reader)?;
+ fde_count = match ptr {
+ Pointer::Direct(c) => c,
+ Pointer::Indirect(_) => return Err(Error::UnsupportedPointerEncoding),
+ }
+ }
+
+ Ok(ParsedEhFrameHdr {
+ address_size,
+ section: self.0.clone(),
+
+ eh_frame_ptr,
+ fde_count,
+ table_enc,
+ table: reader,
+ })
+ }
+}
+
+impl<R: Reader> Section<R> for EhFrameHdr<R> {
+ fn id() -> SectionId {
+ SectionId::EhFrameHdr
+ }
+
+ fn reader(&self) -> &R {
+ &self.0
+ }
+}
+
+impl<R: Reader> From<R> for EhFrameHdr<R> {
+ fn from(section: R) -> Self {
+ EhFrameHdr(section)
+ }
+}
+
+impl<R: Reader> ParsedEhFrameHdr<R> {
+ /// 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<EhHdrTable<R>> {
+ // 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 })
+ }
+ }
+}
+
+/// 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<R>,
+}
+
+impl<'a, R: Reader + 'a> EhHdrTable<'a, R> {
+ /// *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<Pointer> {
+ 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),
+ };
+
+ 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, &parameters, &mut reader)?;
+ let pivot = match pivot {
+ Pointer::Direct(x) => x,
+ Pointer::Indirect(_) => return Err(Error::UnsupportedPointerEncoding),
+ };
+
+ 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, &parameters, &mut reader)
+ }
+
+ /// Convert a `Pointer` to a section offset.
+ ///
+ /// This does not support indirect pointers.
+ pub fn pointer_to_offset(&self, ptr: Pointer) -> Result<EhFrameOffset<R::Offset>> {
+ let ptr = match ptr {
+ Pointer::Direct(x) => x,
+ _ => return Err(Error::UnsupportedPointerEncoding),
+ };
+
+ let eh_frame_ptr = match self.hdr.eh_frame_ptr() {
+ Pointer::Direct(x) => x,
+ _ => return Err(Error::UnsupportedPointerEncoding),
+ };
+
+ // 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<EndianSlice<NativeEndian>> = unreachable!();
+ /// # let eh_frame_hdr: ParsedEhFrameHdr<EndianSlice<NativeEndian>> = 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<F>(
+ &self,
+ frame: &EhFrame<R>,
+ bases: &BaseAddresses,
+ address: u64,
+ get_cie: F,
+ ) -> Result<FrameDescriptionEntry<R>>
+ where
+ F: FnMut(
+ &EhFrame<R>,
+ &BaseAddresses,
+ EhFrameOffset<R::Offset>,
+ ) -> Result<CommonInformationEntry<R>>,
+ {
+ 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<F>(
+ &self,
+ address: u64,
+ bases: &BaseAddresses,
+ frame: EhFrame<R>,
+ get_cie: F,
+ ) -> Result<FrameDescriptionEntry<R>>
+ where
+ F: FnMut(
+ &EhFrame<R>,
+ &BaseAddresses,
+ EhFrameOffset<R::Offset>,
+ ) -> Result<CommonInformationEntry<R>>,
+ {
+ 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, A: UnwindContextStorage<R>>(
+ &self,
+ frame: &EhFrame<R>,
+ bases: &BaseAddresses,
+ ctx: &'ctx mut UnwindContext<R, A>,
+ address: u64,
+ get_cie: F,
+ ) -> Result<&'ctx UnwindTableRow<R, A>>
+ where
+ F: FnMut(
+ &EhFrame<R>,
+ &BaseAddresses,
+ EhFrameOffset<R::Offset>,
+ ) -> Result<CommonInformationEntry<R>>,
+ {
+ 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<R: Reader> {
+ section: R,
+ address_size: u8,
+}
+
+impl<R: Reader> EhFrame<R> {
+ /// 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
+ }
+}
+
+impl<'input, Endian> EhFrame<EndianSlice<'input, Endian>>
+where
+ Endian: Endianity,
+{
+ /// Construct a new `EhFrame` 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 OSX, etc.
+ ///
+ /// ```
+ /// use gimli::{EhFrame, EndianSlice, NativeEndian};
+ ///
+ /// // Use with `.debug_frame`
+ /// # let buf = [0x00, 0x01, 0x02, 0x03];
+ /// # let read_debug_frame_section_somehow = || &buf;
+ /// let debug_frame = EhFrame::new(read_debug_frame_section_somehow(), NativeEndian);
+ /// ```
+ pub fn new(section: &'input [u8], endian: Endian) -> Self {
+ Self::from(EndianSlice::new(section, endian))
+ }
+}
+
+impl<R: Reader> Section<R> for EhFrame<R> {
+ fn id() -> SectionId {
+ SectionId::EhFrame
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R: Reader> From<R> for EhFrame<R> {
+ fn from(section: R) -> Self {
+ // Default to native word size.
+ EhFrame {
+ section,
+ address_size: mem::size_of::<usize>() as u8,
+ }
+ }
+}
+
+// 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<T>`.
+pub trait UnwindOffset<T = usize>: Copy + Debug + Eq + From<T>
+where
+ T: ReaderOffset,
+{
+ /// Convert an `UnwindOffset<T>` into a `T`.
+ fn into(self) -> T;
+}
+
+impl<T> UnwindOffset<T> for DebugFrameOffset<T>
+where
+ T: ReaderOffset,
+{
+ #[inline]
+ fn into(self) -> T {
+ self.0
+ }
+}
+
+impl<T> UnwindOffset<T> for EhFrameOffset<T>
+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<R: Reader> {
+ /// Get the underlying section data.
+ fn section(&self) -> &R;
+
+ /// Returns true if the given length value should be considered an
+ /// end-of-entries sentinel.
+ fn length_value_is_end_of_entries(length: R::Offset) -> 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<R::Offset>;
+
+ /// 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 segment size to use if `has_address_and_segment_sizes` returns false.
+ fn segment_size(&self) -> u8;
+}
+
+/// 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<R: Reader>: Clone + Debug + _UnwindSectionPrivate<R> {
+ /// The offset type associated with this CFI section. Either
+ /// `DebugFrameOffset` or `EhFrameOffset`.
+ type Offset: UnwindOffset<R::Offset>;
+
+ /// 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<CommonInformationEntry<R>> {
+ 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<PartialFrameDescriptionEntry<'bases, Self, R>> {
+ 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<F>(
+ &self,
+ bases: &BaseAddresses,
+ offset: Self::Offset,
+ get_cie: F,
+ ) -> Result<FrameDescriptionEntry<R>>
+ where
+ F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>,
+ {
+ 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<F>(
+ &self,
+ bases: &BaseAddresses,
+ address: u64,
+ mut get_cie: F,
+ ) -> Result<FrameDescriptionEntry<R>>
+ where
+ F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>,
+ {
+ 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, A: UnwindContextStorage<R>>(
+ &self,
+ bases: &BaseAddresses,
+ ctx: &'ctx mut UnwindContext<R, A>,
+ address: u64,
+ get_cie: F,
+ ) -> Result<&'ctx UnwindTableRow<R, A>>
+ where
+ F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>,
+ {
+ let fde = self.fde_for_address(bases, address, get_cie)?;
+ fde.unwind_info_for_address(self, bases, ctx, address)
+ }
+}
+
+impl<R: Reader> _UnwindSectionPrivate<R> for DebugFrame<R> {
+ fn section(&self) -> &R {
+ &self.section
+ }
+
+ fn length_value_is_end_of_entries(_: R::Offset) -> 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<R::Offset> {
+ Some(offset)
+ }
+
+ fn has_address_and_segment_sizes(version: u8) -> bool {
+ version == 4
+ }
+
+ fn address_size(&self) -> u8 {
+ self.address_size
+ }
+
+ fn segment_size(&self) -> u8 {
+ self.segment_size
+ }
+}
+
+impl<R: Reader> UnwindSection<R> for DebugFrame<R> {
+ type Offset = DebugFrameOffset<R::Offset>;
+}
+
+impl<R: Reader> _UnwindSectionPrivate<R> for EhFrame<R> {
+ fn section(&self) -> &R {
+ &self.section
+ }
+
+ fn length_value_is_end_of_entries(length: R::Offset) -> bool {
+ length.into_u64() == 0
+ }
+
+ 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<R::Offset> {
+ base.checked_sub(offset)
+ }
+
+ fn has_address_and_segment_sizes(_version: u8) -> bool {
+ false
+ }
+
+ fn address_size(&self) -> u8 {
+ self.address_size
+ }
+
+ fn segment_size(&self) -> u8 {
+ 0
+ }
+}
+
+impl<R: Reader> UnwindSection<R> for EhFrame<R> {
+ type Offset = EhFrameOffset<R::Offset>;
+}
+
+/// 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<u64>,
+
+ /// The base address for text relative pointers.
+ /// This is generally the address of the `.text` section.
+ pub text: Option<u64>,
+
+ /// 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<u64>,
+}
+
+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<R>,
+{
+ section: Section,
+ bases: &'bases BaseAddresses,
+ input: R,
+}
+
+impl<'bases, Section, R> CfiEntriesIter<'bases, Section, R>
+where
+ R: Reader,
+ Section: UnwindSection<R>,
+{
+ /// Advance the iterator to the next entry.
+ pub fn next(&mut self) -> Result<Option<CieOrFde<'bases, Section, R>>> {
+ if self.input.is_empty() {
+ return Ok(None);
+ }
+
+ match parse_cfi_entry(self.bases, &self.section, &mut self.input) {
+ Err(e) => {
+ self.input.empty();
+ Err(e)
+ }
+ Ok(None) => {
+ self.input.empty();
+ Ok(None)
+ }
+ Ok(Some(entry)) => Ok(Some(entry)),
+ }
+ }
+}
+
+#[cfg(feature = "fallible-iterator")]
+impl<'bases, Section, R> fallible_iterator::FallibleIterator for CfiEntriesIter<'bases, Section, R>
+where
+ R: Reader,
+ Section: UnwindSection<R>,
+{
+ type Item = CieOrFde<'bases, Section, R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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<R>,
+{
+ /// This CFI entry is a `CommonInformationEntry`.
+ Cie(CommonInformationEntry<R>),
+ /// 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>),
+}
+
+#[allow(clippy::type_complexity)]
+fn parse_cfi_entry<'bases, Section, R>(
+ bases: &'bases BaseAddresses,
+ section: &Section,
+ input: &mut R,
+) -> Result<Option<CieOrFde<'bases, Section, R>>>
+where
+ R: Reader,
+ Section: UnwindSection<R>,
+{
+ let (offset, length, format) = loop {
+ let offset = input.offset_from(section.section());
+ let (length, format) = input.read_initial_length()?;
+
+ if Section::length_value_is_end_of_entries(length) {
+ return Ok(None);
+ }
+
+ // Hack: skip zero padding inserted by buggy compilers/linkers.
+ // We require that the padding is a multiple of 32-bits, otherwise
+ // there is no reliable way to determine when the padding ends. This
+ // should be okay since CFI entries must be aligned to the address size.
+
+ if length.into_u64() != 0 || format != Format::Dwarf32 {
+ break (offset, length, format);
+ }
+ };
+
+ 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<constants::DwEhPe>,
+
+ /// > 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<constants::DwEhPe>,
+
+ /// True if this CIE's FDEs are trampolines for signal handlers.
+ is_signal_trampoline: bool,
+}
+
+impl Augmentation {
+ fn parse<Section, R>(
+ augmentation_str: &mut R,
+ bases: &BaseAddresses,
+ address_size: u8,
+ section: &Section,
+ input: &mut R,
+ ) -> Result<Augmentation>
+ where
+ R: Reader,
+ Section: UnwindSection<R>,
+ {
+ 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, &parameters, 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<Pointer>,
+}
+
+impl AugmentationData {
+ fn parse<R: Reader>(
+ augmentation: &Augmentation,
+ encoding_parameters: &PointerEncodingParameters<R>,
+ input: &mut R,
+ ) -> Result<AugmentationData> {
+ // 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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<Augmentation>,
+
+ /// > 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,
+
+ /// "The size of a segment selector in this CIE and any FDEs that use it, in
+ /// bytes."
+ segment_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<R: Reader> CommonInformationEntry<R> {
+ fn parse<Section: UnwindSection<R>>(
+ bases: &BaseAddresses,
+ section: &Section,
+ input: &mut R,
+ ) -> Result<CommonInformationEntry<R>> {
+ 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<Section: UnwindSection<R>>(
+ offset: R::Offset,
+ length: R::Offset,
+ format: Format,
+ bases: &BaseAddresses,
+ section: &Section,
+ mut rest: R,
+ ) -> Result<CommonInformationEntry<R>> {
+ 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, segment_size) = if Section::has_address_and_segment_sizes(version) {
+ let address_size = rest.read_u8()?;
+ let segment_size = rest.read_u8()?;
+ (address_size, segment_size)
+ } else {
+ (section.address_size(), section.segment_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,
+ segment_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<R: Reader> CommonInformationEntry<R> {
+ /// 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<R>,
+ {
+ 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(),
+ },
+ }
+ }
+
+ /// > 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<constants::DwEhPe> {
+ 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<Pointer> {
+ 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<constants::DwEhPe> {
+ 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<R>,
+{
+ 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<R>,
+{
+ fn parse_partial(
+ section: &Section,
+ bases: &'bases BaseAddresses,
+ input: &mut R,
+ ) -> Result<PartialFrameDescriptionEntry<'bases, Section, R>> {
+ 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<F>(&self, get_cie: F) -> Result<FrameDescriptionEntry<R>>
+ where
+ F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>,
+ {
+ FrameDescriptionEntry::parse_rest(
+ self.offset,
+ self.length,
+ self.format,
+ self.cie_offset,
+ self.rest.clone(),
+ &self.section,
+ self.bases,
+ get_cie,
+ )
+ }
+}
+
+/// A `FrameDescriptionEntry` is a set of CFA instructions for an address range.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct FrameDescriptionEntry<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<R, Offset>,
+
+ /// > 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_segment: u64,
+ 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<AugmentationData>,
+
+ /// "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<R: Reader> FrameDescriptionEntry<R> {
+ #[allow(clippy::too_many_arguments)]
+ fn parse_rest<Section, F>(
+ offset: R::Offset,
+ length: R::Offset,
+ format: Format,
+ cie_pointer: Section::Offset,
+ mut rest: R,
+ section: &Section,
+ bases: &BaseAddresses,
+ mut get_cie: F,
+ ) -> Result<FrameDescriptionEntry<R>>
+ where
+ Section: UnwindSection<R>,
+ F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>,
+ {
+ let cie = get_cie(section, bases, cie_pointer)?;
+
+ let initial_segment = if cie.segment_size > 0 {
+ rest.read_address(cie.segment_size)?
+ } else {
+ 0
+ };
+
+ 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, &parameters)?;
+ parameters.func_base = Some(initial_address);
+
+ let aug_data = if let Some(ref augmentation) = cie.augmentation {
+ Some(AugmentationData::parse(
+ augmentation,
+ &parameters,
+ &mut rest,
+ )?)
+ } else {
+ None
+ };
+
+ let entry = FrameDescriptionEntry {
+ offset,
+ length,
+ format,
+ cie,
+ initial_segment,
+ initial_address,
+ address_range,
+ augmentation: aug_data,
+ instructions: rest,
+ };
+
+ Ok(entry)
+ }
+
+ fn parse_addresses(
+ input: &mut R,
+ cie: &CommonInformationEntry<R>,
+ parameters: &PointerEncodingParameters<R>,
+ ) -> Result<(u64, u64)> {
+ let encoding = cie.augmentation().and_then(|a| a.fde_address_encoding);
+ if let Some(encoding) = encoding {
+ let initial_address = parse_encoded_pointer(encoding, parameters, input)?;
+
+ // Ignore indirection.
+ let initial_address = initial_address.into();
+
+ // Address ranges cannot be relative to anything, so just grab the
+ // data format bits from the encoding.
+ let address_range = parse_encoded_pointer(encoding.format(), parameters, input)?;
+ Ok((initial_address, address_range.into()))
+ } 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: UnwindSection<R>, A: UnwindContextStorage<R>>(
+ &self,
+ section: &'a Section,
+ bases: &'a BaseAddresses,
+ ctx: &'ctx mut UnwindContext<R, A>,
+ ) -> Result<UnwindTable<'a, 'ctx, R, A>> {
+ 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: UnwindSection<R>, A: UnwindContextStorage<R>>(
+ &self,
+ section: &Section,
+ bases: &BaseAddresses,
+ ctx: &'ctx mut UnwindContext<R, A>,
+ address: u64,
+ ) -> Result<&'ctx UnwindTableRow<R, A>> {
+ 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<R: Reader> FrameDescriptionEntry<R> {
+ /// 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<R> {
+ &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<R>,
+ {
+ 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(),
+ },
+ }
+ }
+
+ /// The first address for which this entry has unwind information for.
+ pub fn initial_address(&self) -> u64 {
+ self.initial_address
+ }
+
+ /// 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 {
+ let start = self.initial_address();
+ let end = start + self.len();
+ start <= address && address < end
+ }
+
+ /// The address of this FDE's language-specific data area (LSDA), if it has
+ /// any.
+ pub fn lsda(&self) -> Option<Pointer> {
+ 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<Pointer> {
+ 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 [`Vec`]. This is the default storage type parameter for [`UnwindContext`].
+"
+)]
+///
+/// If you need to avoid [`UnwindContext`] from allocating memory, e.g. for signal safety,
+/// you can provide you own storage specification:
+/// ```rust,no_run
+/// # use gimli::*;
+/// #
+/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>)
+/// # -> gimli::Result<()> {
+/// # let eh_frame: gimli::EhFrame<_> = unreachable!();
+/// # let bases = unimplemented!();
+/// #
+/// struct StoreOnStack;
+///
+/// impl<R: Reader> UnwindContextStorage<R> for StoreOnStack {
+/// type Rules = [(Register, RegisterRule<R>); 192];
+/// type Stack = [UnwindTableRow<R, Self>; 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<R: Reader>: Sized {
+ /// The storage used for register rules in a unwind table row.
+ ///
+ /// Note that this is nested within the stack.
+ type Rules: ArrayLike<Item = (Register, RegisterRule<R>)>;
+
+ /// The storage used for unwind table row stack.
+ type Stack: ArrayLike<Item = UnwindTableRow<R, Self>>;
+}
+
+#[cfg(feature = "read")]
+const MAX_RULES: usize = 192;
+
+#[cfg(feature = "read")]
+impl<R: Reader> UnwindContextStorage<R> for StoreOnHeap {
+ type Rules = [(Register, RegisterRule<R>); MAX_RULES];
+ type Stack = Vec<UnwindTableRow<R, Self>>;
+}
+
+/// Common context needed when evaluating the call frame unwinding information.
+///
+/// This structure can be large so it is advisable to place it on the heap.
+/// To avoid re-allocating the context multiple times when evaluating multiple
+/// CFI programs, it can be reused.
+///
+/// ```
+/// use gimli::{UnwindContext, UnwindTable};
+///
+/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>)
+/// # -> gimli::Result<()> {
+/// # let eh_frame: gimli::EhFrame<_> = unreachable!();
+/// # let bases = unimplemented!();
+/// // An uninitialized context.
+/// let mut ctx = Box::new(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<R: Reader, A: UnwindContextStorage<R> = StoreOnHeap> {
+ // 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<A::Stack>,
+
+ // 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<R>)>,
+
+ is_initialized: bool,
+}
+
+impl<R: Reader, S: UnwindContextStorage<R>> Debug for UnwindContext<R, S> {
+ 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<R: Reader, A: UnwindContextStorage<R>> Default for UnwindContext<R, A> {
+ fn default() -> Self {
+ Self::new_in()
+ }
+}
+
+#[cfg(feature = "read")]
+impl<R: Reader> UnwindContext<R> {
+ /// 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<R: Reader, A: UnwindContextStorage<R>> UnwindContext<R, A> {
+ /// 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<Section: UnwindSection<R>>(
+ &mut self,
+ section: &Section,
+ bases: &BaseAddresses,
+ cie: &CommonInformationEntry<R>,
+ ) -> Result<()> {
+ if self.is_initialized {
+ self.reset();
+ }
+
+ let mut table = UnwindTable::new_for_cie(section, bases, self, cie);
+ while let Some(_) = table.next_row()? {}
+
+ 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<R, A> {
+ self.stack.last().unwrap()
+ }
+
+ fn row_mut(&mut self) -> &mut UnwindTableRow<R, A> {
+ self.stack.last_mut().unwrap()
+ }
+
+ fn save_initial_rules(&mut self) -> Result<()> {
+ assert_eq!(self.is_initialized, false);
+ 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<R>) -> 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<RegisterRule<R>> {
+ 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<R>) {
+ self.row_mut().cfa = cfa;
+ }
+
+ fn cfa_mut(&mut self) -> &mut CfaRule<R> {
+ &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:
+/// >
+/// > <table>
+/// > <tr>
+/// > <th>LOC</th><th>CFA</th><th>R0</th><th>R1</th><td>...</td><th>RN</th>
+/// > </tr>
+/// > <tr>
+/// > <th>L0</th> <td></td> <td></td> <td></td> <td></td> <td></td>
+/// > </tr>
+/// > <tr>
+/// > <th>L1</th> <td></td> <td></td> <td></td> <td></td> <td></td>
+/// > </tr>
+/// > <tr>
+/// > <td>...</td><td></td> <td></td> <td></td> <td></td> <td></td>
+/// > </tr>
+/// > <tr>
+/// > <th>LN</th> <td></td> <td></td> <td></td> <td></td> <td></td>
+/// > </tr>
+/// > </table>
+/// >
+/// > 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: Reader, A: UnwindContextStorage<R> = StoreOnHeap> {
+ code_alignment_factor: Wrapping<u64>,
+ data_alignment_factor: Wrapping<i64>,
+ next_start_address: u64,
+ last_end_address: u64,
+ returned_last_row: bool,
+ current_row_valid: bool,
+ instructions: CallFrameInstructionIter<'a, R>,
+ ctx: &'ctx mut UnwindContext<R, A>,
+}
+
+/// # Signal Safe Methods
+///
+/// These methods are guaranteed not to allocate, acquire locks, or perform any
+/// other signal-unsafe operations.
+impl<'a, 'ctx, R: Reader, A: UnwindContextStorage<R>> UnwindTable<'a, 'ctx, R, A> {
+ /// Construct a new `UnwindTable` for the given
+ /// `FrameDescriptionEntry`'s CFI unwinding program.
+ pub fn new<Section: UnwindSection<R>>(
+ section: &'a Section,
+ bases: &'a BaseAddresses,
+ ctx: &'ctx mut UnwindContext<R, A>,
+ fde: &FrameDescriptionEntry<R>,
+ ) -> Result<Self> {
+ ctx.initialize(section, bases, fde.cie())?;
+ Ok(Self::new_for_fde(section, bases, ctx, fde))
+ }
+
+ fn new_for_fde<Section: UnwindSection<R>>(
+ section: &'a Section,
+ bases: &'a BaseAddresses,
+ ctx: &'ctx mut UnwindContext<R, A>,
+ fde: &FrameDescriptionEntry<R>,
+ ) -> 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()),
+ next_start_address: fde.initial_address(),
+ last_end_address: fde.initial_address().wrapping_add(fde.len()),
+ returned_last_row: false,
+ current_row_valid: false,
+ instructions: fde.instructions(section, bases),
+ ctx,
+ }
+ }
+
+ fn new_for_cie<Section: UnwindSection<R>>(
+ section: &'a Section,
+ bases: &'a BaseAddresses,
+ ctx: &'ctx mut UnwindContext<R, A>,
+ cie: &CommonInformationEntry<R>,
+ ) -> Self {
+ assert!(ctx.stack.len() >= 1);
+ UnwindTable {
+ code_alignment_factor: Wrapping(cie.code_alignment_factor()),
+ data_alignment_factor: Wrapping(cie.data_alignment_factor()),
+ 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<Option<&UnwindTableRow<R, A>>> {
+ 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<R, A>> {
+ 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<R>) -> Result<bool> {
+ 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 = (Wrapping(self.ctx.start_address()) + delta).0;
+ 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;
+ }
+
+ // 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<R: Reader, S: UnwindContextStorage<R> = StoreOnHeap> {
+ rules: ArrayVec<S::Rules>,
+}
+
+impl<R: Reader, S: UnwindContextStorage<R>> Debug for RegisterRuleMap<R, S> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("RegisterRuleMap")
+ .field("rules", &self.rules)
+ .finish()
+ }
+}
+
+impl<R: Reader, S: UnwindContextStorage<R>> Clone for RegisterRuleMap<R, S> {
+ fn clone(&self) -> Self {
+ Self {
+ rules: self.rules.clone(),
+ }
+ }
+}
+
+impl<R: Reader, S: UnwindContextStorage<R>> Default for RegisterRuleMap<R, S> {
+ 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<R: Reader, S: UnwindContextStorage<R>> RegisterRuleMap<R, S> {
+ fn is_default(&self) -> bool {
+ self.rules.is_empty()
+ }
+
+ fn get(&self, register: Register) -> RegisterRule<R> {
+ 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<R>) -> 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<R> {
+ RegisterRuleIter(self.rules.iter())
+ }
+}
+
+impl<'a, R, S: UnwindContextStorage<R>> FromIterator<&'a (Register, RegisterRule<R>)>
+ for RegisterRuleMap<R, S>
+where
+ R: 'a + Reader,
+{
+ fn from_iter<T>(iter: T) -> Self
+ where
+ T: IntoIterator<Item = &'a (Register, RegisterRule<R>)>,
+ {
+ 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<R, S: UnwindContextStorage<R>> PartialEq for RegisterRuleMap<R, S>
+where
+ R: Reader + PartialEq,
+{
+ 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<R, S: UnwindContextStorage<R>> Eq for RegisterRuleMap<R, S> where R: Reader + Eq {}
+
+/// An unordered iterator for register rules.
+#[derive(Debug, Clone)]
+pub struct RegisterRuleIter<'iter, R>(::core::slice::Iter<'iter, (Register, RegisterRule<R>)>)
+where
+ R: Reader;
+
+impl<'iter, R: Reader> Iterator for RegisterRuleIter<'iter, R> {
+ type Item = &'iter (Register, RegisterRule<R>);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ 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<R: Reader, S: UnwindContextStorage<R> = StoreOnHeap> {
+ start_address: u64,
+ end_address: u64,
+ saved_args_size: u64,
+ cfa: CfaRule<R>,
+ registers: RegisterRuleMap<R, S>,
+}
+
+impl<R: Reader, S: UnwindContextStorage<R>> Debug for UnwindTableRow<R, S> {
+ 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<R: Reader, S: UnwindContextStorage<R>> Clone for UnwindTableRow<R, S> {
+ 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<R: Reader, S: UnwindContextStorage<R>> Default for UnwindTableRow<R, S> {
+ fn default() -> Self {
+ UnwindTableRow {
+ start_address: 0,
+ end_address: 0,
+ saved_args_size: 0,
+ cfa: Default::default(),
+ registers: Default::default(),
+ }
+ }
+}
+
+impl<R: Reader, S: UnwindContextStorage<R>> UnwindTableRow<R, S> {
+ 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<R> {
+ &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
+ /// >
+ /// > <table>
+ /// > <tr><th>Register Name</th> <th>Number</th> <th>Abbreviation</th></tr>
+ /// > <tr><td>General Purpose Register RAX</td> <td>0</td> <td>%rax</td></tr>
+ /// > <tr><td>General Purpose Register RDX</td> <td>1</td> <td>%rdx</td></tr>
+ /// > <tr><td>General Purpose Register RCX</td> <td>2</td> <td>%rcx</td></tr>
+ /// > <tr><td>General Purpose Register RBX</td> <td>3</td> <td>%rbx</td></tr>
+ /// > <tr><td>General Purpose Register RSI</td> <td>4</td> <td>%rsi</td></tr>
+ /// > <tr><td>General Purpose Register RDI</td> <td>5</td> <td>%rdi</td></tr>
+ /// > <tr><td>General Purpose Register RBP</td> <td>6</td> <td>%rbp</td></tr>
+ /// > <tr><td>Stack Pointer Register RSP</td> <td>7</td> <td>%rsp</td></tr>
+ /// > <tr><td>Extended Integer Registers 8-15</td> <td>8-15</td> <td>%r8-%r15</td></tr>
+ /// > <tr><td>Return Address RA</td> <td>16</td> <td></td></tr>
+ /// > <tr><td>Vector Registers 0–7</td> <td>17-24</td> <td>%xmm0–%xmm7</td></tr>
+ /// > <tr><td>Extended Vector Registers 8–15</td> <td>25-32</td> <td>%xmm8–%xmm15</td></tr>
+ /// > <tr><td>Floating Point Registers 0–7</td> <td>33-40</td> <td>%st0–%st7</td></tr>
+ /// > <tr><td>MMX Registers 0–7</td> <td>41-48</td> <td>%mm0–%mm7</td></tr>
+ /// > <tr><td>Flag Register</td> <td>49</td> <td>%rFLAGS</td></tr>
+ /// > <tr><td>Segment Register ES</td> <td>50</td> <td>%es</td></tr>
+ /// > <tr><td>Segment Register CS</td> <td>51</td> <td>%cs</td></tr>
+ /// > <tr><td>Segment Register SS</td> <td>52</td> <td>%ss</td></tr>
+ /// > <tr><td>Segment Register DS</td> <td>53</td> <td>%ds</td></tr>
+ /// > <tr><td>Segment Register FS</td> <td>54</td> <td>%fs</td></tr>
+ /// > <tr><td>Segment Register GS</td> <td>55</td> <td>%gs</td></tr>
+ /// > <tr><td>Reserved</td> <td>56-57</td> <td></td></tr>
+ /// > <tr><td>FS Base address</td> <td>58</td> <td>%fs.base</td></tr>
+ /// > <tr><td>GS Base address</td> <td>59</td> <td>%gs.base</td></tr>
+ /// > <tr><td>Reserved</td> <td>60-61</td> <td></td></tr>
+ /// > <tr><td>Task Register</td> <td>62</td> <td>%tr</td></tr>
+ /// > <tr><td>LDT Register</td> <td>63</td> <td>%ldtr</td></tr>
+ /// > <tr><td>128-bit Media Control and Status</td> <td>64</td> <td>%mxcsr</td></tr>
+ /// > <tr><td>x87 Control Word</td> <td>65</td> <td>%fcw</td></tr>
+ /// > <tr><td>x87 Status Word</td> <td>66</td> <td>%fsw</td></tr>
+ /// > <tr><td>Upper Vector Registers 16–31</td> <td>67-82</td> <td>%xmm16–%xmm31</td></tr>
+ /// > <tr><td>Reserved</td> <td>83-117</td> <td></td></tr>
+ /// > <tr><td>Vector Mask Registers 0–7</td> <td>118-125</td> <td>%k0–%k7</td></tr>
+ /// > <tr><td>Reserved</td> <td>126-129</td> <td></td></tr>
+ /// > </table>
+ pub fn register(&self, register: Register) -> RegisterRule<R> {
+ 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<EndianSlice<'input, LittleEndian>>) {
+ /// for &(register, ref rule) in unwind_table_row.registers() {
+ /// // ...
+ /// # drop(register); drop(rule);
+ /// }
+ /// # }
+ /// ```
+ pub fn registers(&self) -> RegisterRuleIter<R> {
+ self.registers.iter()
+ }
+}
+
+/// The canonical frame address (CFA) recovery rules.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum CfaRule<R: Reader> {
+ /// 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 this `Reader` as a DWARF expression
+ /// program.
+ Expression(Expression<R>),
+}
+
+impl<R: Reader> Default for CfaRule<R> {
+ fn default() -> Self {
+ CfaRule::RegisterAndOffset {
+ register: Register(0),
+ offset: 0,
+ }
+ }
+}
+
+impl<R: Reader> CfaRule<R> {
+ 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)]
+pub enum RegisterRule<R: Reader> {
+ /// > 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(Expression<R>),
+
+ /// "The previous value of this register is the value produced by executing
+ /// the DWARF expression."
+ ValExpression(Expression<R>),
+
+ /// "The rule is defined externally to this specification by the augmenter."
+ Architectural,
+}
+
+impl<R: Reader> RegisterRule<R> {
+ fn is_defined(&self) -> bool {
+ match *self {
+ RegisterRule::Undefined => false,
+ _ => true,
+ }
+ }
+}
+
+/// A parsed call frame instruction.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum CallFrameInstruction<R: Reader> {
+ // 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 DWARF expression.
+ expression: Expression<R>,
+ },
+
+ // 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 DWARF expression.
+ expression: Expression<R>,
+ },
+
+ /// > 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 DWARF expression.
+ expression: Expression<R>,
+ },
+
+ /// 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,
+ },
+
+ // 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<R: Reader> CallFrameInstruction<R> {
+ fn parse(
+ input: &mut R,
+ address_encoding: Option<DwEhPe>,
+ parameters: &PointerEncodingParameters<R>,
+ ) -> Result<CallFrameInstruction<R>> {
+ 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 {
+ match parse_encoded_pointer(encoding, parameters, input)? {
+ Pointer::Direct(x) => x,
+ _ => return Err(Error::UnsupportedPointerEncoding),
+ }
+ } 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 len = input.read_uleb128().and_then(R::Offset::from_u64)?;
+ let expression = input.split(len)?;
+ Ok(CallFrameInstruction::DefCfaExpression {
+ expression: Expression(expression),
+ })
+ }
+
+ constants::DW_CFA_expression => {
+ let register = input.read_uleb128().and_then(Register::from_u64)?;
+ let len = input.read_uleb128().and_then(R::Offset::from_u64)?;
+ let expression = input.split(len)?;
+ Ok(CallFrameInstruction::Expression {
+ register,
+ expression: Expression(expression),
+ })
+ }
+
+ 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 len = input.read_uleb128().and_then(R::Offset::from_u64)?;
+ let expression = input.split(len)?;
+ Ok(CallFrameInstruction::ValExpression {
+ register,
+ expression: Expression(expression),
+ })
+ }
+
+ constants::DW_CFA_GNU_args_size => {
+ let size = input.read_uleb128()?;
+ Ok(CallFrameInstruction::ArgsSize { size })
+ }
+
+ 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<constants::DwEhPe>,
+ parameters: PointerEncodingParameters<'a, R>,
+}
+
+impl<'a, R: Reader> CallFrameInstructionIter<'a, R> {
+ /// Parse the next call frame instruction.
+ pub fn next(&mut self) -> Result<Option<CallFrameInstruction<R>>> {
+ if self.input.is_empty() {
+ return Ok(None);
+ }
+
+ match CallFrameInstruction::parse(&mut self.input, self.address_encoding, &self.parameters)
+ {
+ 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<R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
+ CallFrameInstructionIter::next(self)
+ }
+}
+
+/// Parse a `DW_EH_PE_*` pointer encoding.
+#[doc(hidden)]
+#[inline]
+fn parse_pointer_encoding<R: Reader>(input: &mut R) -> Result<constants::DwEhPe> {
+ 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)
+ }
+}
+
+/// 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 Into<u64> for Pointer {
+ #[inline]
+ fn into(self) -> u64 {
+ match self {
+ Pointer::Direct(p) | Pointer::Indirect(p) => p,
+ }
+ }
+}
+
+impl Pointer {
+ #[inline]
+ fn new(encoding: constants::DwEhPe, address: u64) -> Pointer {
+ if encoding.is_indirect() {
+ Pointer::Indirect(address)
+ } else {
+ Pointer::Direct(address)
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+struct PointerEncodingParameters<'a, R: Reader> {
+ bases: &'a SectionBaseAddresses,
+ func_base: Option<u64>,
+ address_size: u8,
+ section: &'a R,
+}
+
+fn parse_encoded_pointer<R: Reader>(
+ encoding: constants::DwEhPe,
+ parameters: &PointerEncodingParameters<R>,
+ input: &mut R,
+) -> Result<Pointer> {
+ // TODO: check this once only in parse_pointer_encoding
+ if !encoding.is_valid_encoding() {
+ return Err(Error::UnknownPointerEncoding);
+ }
+
+ 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(offset_from_section.into_u64())
+ } 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 = 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!(),
+ }?;
+
+ Ok(Pointer::new(encoding, base.wrapping_add(offset)))
+}
+
+#[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, Expression, 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 core::u64;
+ 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<Section>(PhantomData<Section>);
+
+ impl<T> SectionKind<T> {
+ fn endian<'input, E>(self) -> Endian
+ where
+ E: Endianity,
+ T: UnwindSection<EndianSlice<'input, E>>,
+ T::Offset: UnwindOffset<usize>,
+ {
+ 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<EndianSlice<'input, E>> + ReadSection<EndianSlice<'input, E>>,
+ T::Offset: UnwindOffset<usize>,
+ {
+ EndianSlice::new(contents, E::default()).into()
+ }
+ }
+
+ fn debug_frame_le<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, LittleEndian>>> {
+ SectionKind(PhantomData)
+ }
+
+ fn debug_frame_be<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, BigEndian>>> {
+ SectionKind(PhantomData)
+ }
+
+ fn eh_frame_le<'a>() -> SectionKind<EhFrame<EndianSlice<'a, LittleEndian>>> {
+ SectionKind(PhantomData)
+ }
+
+ fn parse_fde<Section, O, F, R>(
+ section: Section,
+ input: &mut R,
+ get_cie: F,
+ ) -> Result<FrameDescriptionEntry<R>>
+ where
+ R: Reader,
+ Section: UnwindSection<R, Offset = O>,
+ O: UnwindOffset<R::Offset>,
+ F: FnMut(&Section, &BaseAddresses, O) -> Result<CommonInformationEntry<R>>,
+ {
+ let bases = Default::default();
+ match parse_cfi_entry(&bases, &section, 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<T>,
+ augmentation: Option<&'aug str>,
+ cie: &mut CommonInformationEntry<EndianSlice<'input, E>>,
+ ) -> Self
+ where
+ E: Endianity,
+ T: UnwindSection<EndianSlice<'input, E>>,
+ T::Offset: UnwindOffset;
+ fn fde<'a, 'input, E, T, L>(
+ self,
+ _kind: SectionKind<T>,
+ cie_offset: L,
+ fde: &mut FrameDescriptionEntry<EndianSlice<'input, E>>,
+ ) -> Self
+ where
+ E: Endianity,
+ T: UnwindSection<EndianSlice<'input, E>>,
+ T::Offset: UnwindOffset,
+ L: ToLabelOrNum<'a, u64>;
+ }
+
+ impl CfiSectionMethods for Section {
+ fn cie<'aug, 'input, E, T>(
+ self,
+ _kind: SectionKind<T>,
+ augmentation: Option<&'aug str>,
+ cie: &mut CommonInformationEntry<EndianSlice<'input, E>>,
+ ) -> Self
+ where
+ E: Endianity,
+ T: UnwindSection<EndianSlice<'input, E>>,
+ 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(cie.segment_size)
+ } 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.into())
+ .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<T>,
+ cie_offset: L,
+ fde: &mut FrameDescriptionEntry<EndianSlice<'input, E>>,
+ ) -> Self
+ where
+ E: Endianity,
+ T: UnwindSection<EndianSlice<'input, E>>,
+ 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.segment_size {
+ 0 => section,
+ 4 => section.D32(fde.initial_segment as u32),
+ 8 => section.D64(fde.initial_segment),
+ x => panic!("Unsupported test segment size: {}", x),
+ };
+
+ 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.into();
+ x as u32
+ }),
+ 8 => section.D64({
+ let x: u64 = lsda.into();
+ 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.into()).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<T> ResultExt for Result<T> {
+ 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,
+ }
+ }
+ }
+
+ #[allow(clippy::type_complexity)]
+ #[allow(clippy::needless_pass_by_value)]
+ fn assert_parse_cie<'input, E>(
+ kind: SectionKind<DebugFrame<EndianSlice<'input, E>>>,
+ section: Section,
+ address_size: u8,
+ expected: Result<(
+ EndianSlice<'input, E>,
+ CommonInformationEntry<EndianSlice<'input, E>>,
+ )>,
+ ) where
+ E: Endianity,
+ {
+ let section = section.get_contents().unwrap();
+ let mut debug_frame = kind.section(&section);
+ debug_frame.set_address_size(address_size);
+ let input = &mut EndianSlice::new(&section, E::default());
+ let bases = Default::default();
+ let result = CommonInformationEntry::parse(&bases, &debug_frame, input);
+ let result = result.map(|cie| (*input, cie)).map_eof(&section);
+ 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,
+ segment_size: 0,
+ 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 = Some("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.unwrap().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,
+ segment_size: 0,
+ 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,
+ segment_size: 0,
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, LittleEndian);
+ assert_eq!(
+ parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(&section),
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, LittleEndian);
+ assert_eq!(
+ parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(&section),
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, BigEndian);
+ assert_eq!(
+ parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(&section),
+ 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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, 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_32_with_segment_ok() {
+ let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+ let cie_offset = 0xbad0_bad1;
+ let expected_instrs: Vec<_> = (0..92).map(|_| constants::DW_CFA_nop.0).collect();
+
+ let cie = CommonInformationEntry {
+ offset: 0,
+ length: 100,
+ format: Format::Dwarf32,
+ version: 4,
+ augmentation: None,
+ address_size: 4,
+ segment_size: 4,
+ 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_segment: 0xbadb_ad11,
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, 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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, 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,
+ segment_size: 0,
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, 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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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(&section);
+ let rest = &mut EndianSlice::new(&section, 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,
+ segment_size: 0,
+ 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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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_segment: 0,
+ 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,
+ segment_size: 0,
+ 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<R: Reader + Default>(
+ input: &mut R,
+ address_size: u8,
+ ) -> Result<CallFrameInstruction<R>> {
+ let parameters = &PointerEncodingParameters {
+ bases: &SectionBaseAddresses::default(),
+ func_base: None,
+ address_size,
+ section: &R::default(),
+ };
+ CallFrameInstruction::parse(input, None, parameters)
+ }
+
+ #[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),
+ 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 contents = section.get_contents().unwrap();
+ let input = &mut EndianSlice::new(&contents, LittleEndian);
+
+ assert_eq!(
+ parse_cfi_instruction(input, 8),
+ Ok(CallFrameInstruction::DefCfaExpression {
+ expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)),
+ })
+ );
+ 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 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: Expression(EndianSlice::new(&expected_expr, LittleEndian)),
+ })
+ );
+ 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 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: Expression(EndianSlice::new(&expected_expr, LittleEndian)),
+ })
+ );
+ 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 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,
+ };
+
+ assert_eq!(
+ iter.next(),
+ Ok(Some(CallFrameInstruction::ValExpression {
+ register: Register(expected_reg),
+ expression: Expression(EndianSlice::new(&expected_expr, BigEndian)),
+ }))
+ );
+
+ 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,
+ };
+
+ assert_eq!(
+ iter.next().map_eof(&contents),
+ Err(Error::UnexpectedEof(ReaderOffsetId(1)))
+ );
+ assert_eq!(iter.next(), Ok(None));
+ }
+
+ #[allow(clippy::needless_pass_by_value)]
+ fn assert_eval<'a, I>(
+ mut initial_ctx: UnwindContext<EndianSlice<'a, LittleEndian>>,
+ expected_ctx: UnwindContext<EndianSlice<'a, LittleEndian>>,
+ cie: CommonInformationEntry<EndianSlice<'a, LittleEndian>>,
+ fde: Option<FrameDescriptionEntry<EndianSlice<'a, LittleEndian>>>,
+ instructions: I,
+ ) where
+ I: AsRef<
+ [(
+ Result<bool>,
+ CallFrameInstruction<EndianSlice<'a, LittleEndian>>,
+ )],
+ >,
+ {
+ {
+ 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 &(ref expected_result, ref 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<EndianSlice<'a, LittleEndian>> {
+ CommonInformationEntry {
+ offset: 0,
+ format: Format::Dwarf64,
+ length: 0,
+ return_address_register: Register(0),
+ version: 4,
+ address_size: mem::size_of::<usize>() as u8,
+ initial_instructions: EndianSlice::new(&[], LittleEndian),
+ augmentation: None,
+ segment_size: 0,
+ 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() {
+ let cie = make_test_cie();
+ let mut ctx = UnwindContext::new();
+ ctx.row_mut().start_address = u64::MAX;
+ let mut expected = ctx.clone();
+ expected.row_mut().end_address = 42 * cie.code_alignment_factor - 1;
+ let instructions = [(Ok(true), 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(Expression(EndianSlice::new(
+ &[],
+ LittleEndian,
+ ))));
+ 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(Expression(EndianSlice::new(
+ &[],
+ LittleEndian,
+ ))));
+ 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 = [1, 2, 3, 4];
+ let cie = make_test_cie();
+ let ctx = UnwindContext::new();
+ let mut expected = ctx.clone();
+ expected.set_cfa(CfaRule::Expression(Expression(EndianSlice::new(
+ &expr,
+ LittleEndian,
+ ))));
+ let instructions = [(
+ Ok(false),
+ CallFrameInstruction::DefCfaExpression {
+ expression: Expression(EndianSlice::new(&expr, LittleEndian)),
+ },
+ )];
+ 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 = [1, 2, 3, 4];
+ let cie = make_test_cie();
+ let ctx = UnwindContext::new();
+ let mut expected = ctx.clone();
+ expected
+ .set_register_rule(
+ Register(9),
+ RegisterRule::Expression(Expression(EndianSlice::new(&expr, LittleEndian))),
+ )
+ .unwrap();
+ let instructions = [(
+ Ok(false),
+ CallFrameInstruction::Expression {
+ register: Register(9),
+ expression: Expression(EndianSlice::new(&expr, LittleEndian)),
+ },
+ )];
+ assert_eval(ctx, expected, cie, None, instructions);
+ }
+
+ #[test]
+ fn test_eval_val_expression() {
+ let expr = [1, 2, 3, 4];
+ let cie = make_test_cie();
+ let ctx = UnwindContext::new();
+ let mut expected = ctx.clone();
+ expected
+ .set_register_rule(
+ Register(9),
+ RegisterRule::ValExpression(Expression(EndianSlice::new(&expr, LittleEndian))),
+ )
+ .unwrap();
+ let instructions = [(
+ Ok(false),
+ CallFrameInstruction::ValExpression {
+ register: Register(9),
+ expression: Expression(EndianSlice::new(&expr, LittleEndian)),
+ },
+ )];
+ 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,
+ initial_segment: 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_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() {
+ #[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)
+ .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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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() {
+ #[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 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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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_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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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,
+ segment_size: 0,
+ 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,
+ segment_size: 0,
+ 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_segment: 0,
+ 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_segment: 0,
+ 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.initial_address() + fde1.len(),
+ 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(&section, 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(&section, 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(&section, 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(&section, 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(&section, 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(&section, 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(&section, 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_segment: 0,
+ 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_segment: 0,
+ 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 section = EndianSlice::new(&section, LittleEndian);
+ let eh_frame = kind.section(&section);
+
+ // 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(&section, 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 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 section = Section::with_endian(Endian::Little).L32(0);
+ let section = section.get_contents().unwrap();
+ let rest = &mut EndianSlice::new(&section, LittleEndian);
+ let bases = Default::default();
+
+ assert_eq!(
+ parse_cfi_entry(&bases, &EhFrame::new(&*section, LittleEndian), rest),
+ Ok(None)
+ );
+
+ assert_eq!(
+ EhFrame::new(&section, LittleEndian).cie_from_offset(&bases, EhFrameOffset(0)),
+ Err(Error::NoEntryAtGivenOffset)
+ );
+ }
+
+ fn resolve_cie_offset(buf: &[u8], cie_offset: usize) -> Result<usize> {
+ let mut fde = FrameDescriptionEntry {
+ offset: 0,
+ length: 0,
+ format: Format::Dwarf64,
+ cie: make_test_cie(),
+ initial_segment: 0,
+ 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(&section);
+ let input = &mut EndianSlice::new(&section[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, ::core::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_segment: 0,
+ 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(&section);
+ let section = EndianSlice::new(&section, LittleEndian);
+
+ let mut offset = None;
+ match 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())
+ },
+ ) {
+ 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_segment: 0,
+ 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(&section);
+ let section = EndianSlice::new(&section, 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, &section, 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 mut augmentation = Augmentation::default();
+ augmentation.is_signal_trampoline = true;
+
+ assert_eq!(
+ Augmentation::parse(aug_str, &bases, address_size, &section, 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(&section, LittleEndian);
+ let input = &mut section.section().clone();
+ let augmentation = &mut EndianSlice::new(b"zZ", LittleEndian);
+ assert_eq!(
+ Augmentation::parse(augmentation, &bases, address_size, &section, 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(&section, LittleEndian);
+ let input = &mut section.section().clone();
+ let aug_str = &mut EndianSlice::new(b"zL", LittleEndian);
+
+ let mut augmentation = Augmentation::default();
+ augmentation.lsda = Some(constants::DW_EH_PE_uleb128);
+
+ assert_eq!(
+ Augmentation::parse(aug_str, &bases, address_size, &section, 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(&section, LittleEndian);
+ let input = &mut section.section().clone();
+ let aug_str = &mut EndianSlice::new(b"zP", LittleEndian);
+
+ let mut augmentation = Augmentation::default();
+ augmentation.personality = Some((constants::DW_EH_PE_udata8, Pointer::Direct(0xf00d_f00d)));
+
+ assert_eq!(
+ Augmentation::parse(aug_str, &bases, address_size, &section, 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(&section, LittleEndian);
+ let input = &mut section.section().clone();
+ let aug_str = &mut EndianSlice::new(b"zR", LittleEndian);
+
+ let mut augmentation = Augmentation::default();
+ augmentation.fde_address_encoding = Some(constants::DW_EH_PE_udata4);
+
+ assert_eq!(
+ Augmentation::parse(aug_str, &bases, address_size, &section, 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(&section, LittleEndian);
+ let input = &mut section.section().clone();
+ let aug_str = &mut EndianSlice::new(b"zS", LittleEndian);
+
+ let mut augmentation = Augmentation::default();
+ augmentation.is_signal_trampoline = true;
+
+ assert_eq!(
+ Augmentation::parse(aug_str, &bases, address_size, &section, 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(&section, 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, &section, 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_segment: 0,
+ 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(&section);
+ 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_segment: 0,
+ 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(&section);
+ 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_segment: 0,
+ 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(&section);
+ 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::DwEhPe(
+ constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_absptr.0,
+ ));
+
+ let mut fde = FrameDescriptionEntry {
+ offset: 0,
+ length: 0,
+ format: Format::Dwarf32,
+ cie: cie.clone(),
+ initial_segment: 0,
+ 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(&section);
+ 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(&section, 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<EndianSlice<LittleEndian>> = [
+ (Register(0), RegisterRule::SameValue),
+ (Register(3), RegisterRule::Offset(1)),
+ ]
+ .iter()
+ .collect();
+ let map2: RegisterRuleMap<EndianSlice<LittleEndian>> = [
+ (Register(3), RegisterRule::Offset(1)),
+ (Register(0), RegisterRule::SameValue),
+ ]
+ .iter()
+ .collect();
+ assert_eq!(map1, map2);
+ assert_eq!(map2, map1);
+
+ // Not equal.
+ let map3: RegisterRuleMap<EndianSlice<LittleEndian>> = [
+ (Register(0), RegisterRule::SameValue),
+ (Register(2), RegisterRule::Offset(1)),
+ ]
+ .iter()
+ .collect();
+ let map4: RegisterRuleMap<EndianSlice<LittleEndian>> = [
+ (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::<EndianSlice<LittleEndian>>::default();
+ map5.set(Register(0), RegisterRule::SameValue).unwrap();
+ map5.set(Register(0), RegisterRule::Undefined).unwrap();
+ let map6 = RegisterRuleMap::<EndianSlice<LittleEndian>>::default();
+ assert_eq!(map5, map6);
+ assert_eq!(map6, map5);
+ }
+
+ #[test]
+ fn iter_register_rules() {
+ let mut row = UnwindTableRow::<EndianSlice<LittleEndian>>::default();
+ row.registers = [
+ (Register(0), RegisterRule::SameValue),
+ (Register(1), RegisterRule::Offset(1)),
+ (Register(2), RegisterRule::ValOffset(2)),
+ ]
+ .iter()
+ .collect();
+
+ let mut found0 = false;
+ let mut found1 = false;
+ let mut found2 = false;
+
+ for &(register, ref rule) in row.registers() {
+ match register.0 {
+ 0 => {
+ assert_eq!(found0, false);
+ found0 = true;
+ assert_eq!(*rule, RegisterRule::SameValue);
+ }
+ 1 => {
+ assert_eq!(found1, false);
+ found1 = true;
+ assert_eq!(*rule, RegisterRule::Offset(1));
+ }
+ 2 => {
+ assert_eq!(found2, false);
+ found2 = true;
+ assert_eq!(*rule, RegisterRule::ValOffset(2));
+ }
+ x => panic!("Unexpected register rule: ({}, {:?})", x, rule),
+ }
+ }
+
+ assert_eq!(found0, true);
+ assert_eq!(found1, true);
+ assert_eq!(found2, true);
+ }
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn size_of_unwind_ctx() {
+ use core::mem;
+ let size = mem::size_of::<UnwindContext<EndianSlice<NativeEndian>>>();
+ 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::<RegisterRuleMap<EndianSlice<NativeEndian>>>();
+ 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::DwEhPe(constants::DW_EH_PE_uleb128.0 | constants::DW_EH_PE_pcrel.0);
+ 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),
+ 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, &parameters, &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, &parameters, &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, &parameters, &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, &parameters, &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, &parameters, &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, &parameters, &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, &parameters, &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, &parameters, &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, &parameters, &mut rest),
+ Err(Error::FuncRelativePointerInBadContext)
+ );
+ }
+
+ #[test]
+ fn test_parse_encoded_pointer_uleb128() {
+ let encoding =
+ constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_uleb128.0);
+ 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, &parameters, &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::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata2.0);
+ 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, &parameters, &mut rest),
+ Ok(Pointer::Direct(0x1234))
+ );
+ assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
+ }
+
+ #[test]
+ fn test_parse_encoded_pointer_udata4() {
+ let encoding =
+ constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata4.0);
+ 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, &parameters, &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::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata8.0);
+ 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: 4,
+ section: &input,
+ };
+ assert_eq!(
+ parse_encoded_pointer(encoding, &parameters, &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::DwEhPe(constants::DW_EH_PE_textrel.0 | constants::DW_EH_PE_sleb128.0);
+ 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, &parameters, &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::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata2.0);
+ let expected_rest = [1, 2, 3, 4];
+ let expected = 0x111 as 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, &parameters, &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::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata4.0);
+ let expected_rest = [1, 2, 3, 4];
+ let expected = 0x111_1111 as 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, &parameters, &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::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata8.0);
+ let expected_rest = [1, 2, 3, 4];
+ let expected = -0x11_1111_1222_2222 as 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: 4,
+ section: &input,
+ };
+ assert_eq!(
+ parse_encoded_pointer(encoding, &parameters, &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, &parameters, &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, &parameters, &mut rest),
+ Err(Error::UnknownPointerEncoding)
+ );
+ }
+
+ #[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, &parameters, &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, &parameters, &mut rest),
+ Ok(Pointer::Indirect(0x1234_5678))
+ );
+ assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
+ }
+}
diff --git a/vendor/gimli/src/read/dwarf.rs b/vendor/gimli/src/read/dwarf.rs
new file mode 100644
index 000000000..b63526941
--- /dev/null
+++ b/vendor/gimli/src/read/dwarf.rs
@@ -0,0 +1,1143 @@
+use 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, AttributeValue, DebugAbbrev, DebugAddr, DebugAranges, DebugCuIndex, DebugInfo,
+ DebugInfoUnitHeadersIter, DebugLine, DebugLineStr, DebugLoc, DebugLocLists, DebugRngLists,
+ DebugStr, DebugStrOffsets, DebugTuIndex, DebugTypes, DebugTypesUnitHeadersIter,
+ DebuggingInformationEntry, EntriesCursor, EntriesRaw, EntriesTree, Error,
+ IncompleteLineProgram, LocListIter, LocationLists, Range, RangeLists, RawLocListIter,
+ RawRngListIter, Reader, ReaderOffset, ReaderOffsetId, Result, RngListIter, Section, UnitHeader,
+ UnitIndex, UnitIndexSectionIterator, UnitOffset, UnitType,
+};
+
+/// All of the commonly used DWARF sections, and other common information.
+#[derive(Debug, Default)]
+pub struct Dwarf<R> {
+ /// The `.debug_abbrev` section.
+ pub debug_abbrev: DebugAbbrev<R>,
+
+ /// The `.debug_addr` section.
+ pub debug_addr: DebugAddr<R>,
+
+ /// The `.debug_aranges` section.
+ pub debug_aranges: DebugAranges<R>,
+
+ /// The `.debug_info` section.
+ pub debug_info: DebugInfo<R>,
+
+ /// The `.debug_line` section.
+ pub debug_line: DebugLine<R>,
+
+ /// The `.debug_line_str` section.
+ pub debug_line_str: DebugLineStr<R>,
+
+ /// The `.debug_str` section.
+ pub debug_str: DebugStr<R>,
+
+ /// The `.debug_str_offsets` section.
+ pub debug_str_offsets: DebugStrOffsets<R>,
+
+ /// The `.debug_types` section.
+ pub debug_types: DebugTypes<R>,
+
+ /// The location lists in the `.debug_loc` and `.debug_loclists` sections.
+ pub locations: LocationLists<R>,
+
+ /// The range lists in the `.debug_ranges` and `.debug_rnglists` sections.
+ pub ranges: RangeLists<R>,
+
+ /// The type of this file.
+ pub file_type: DwarfFileType,
+
+ /// The DWARF sections for a supplementary object file.
+ pub sup: Option<Arc<Dwarf<R>>>,
+}
+
+impl<T> Dwarf<T> {
+ /// 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.
+ ///
+ /// `section` may either directly return a `Reader` instance (such as
+ /// `EndianSlice`), or it may return some other type and then convert
+ /// that type into a `Reader` using `Dwarf::borrow`.
+ ///
+ /// After loading, the user should set the `file_type` field and
+ /// call `load_sup` if required.
+ pub fn load<F, E>(mut section: F) -> core::result::Result<Self, E>
+ where
+ F: FnMut(SectionId) -> core::result::Result<T, E>,
+ {
+ // Section types are inferred.
+ let debug_loc = Section::load(&mut section)?;
+ let debug_loclists = Section::load(&mut section)?;
+ let debug_ranges = Section::load(&mut section)?;
+ let debug_rnglists = Section::load(&mut section)?;
+ Ok(Dwarf {
+ 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)?,
+ locations: LocationLists::new(debug_loc, debug_loclists),
+ ranges: RangeLists::new(debug_ranges, debug_rnglists),
+ file_type: DwarfFileType::Main,
+ sup: None,
+ })
+ }
+
+ /// 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<F, E>(&mut self, section: F) -> core::result::Result<(), E>
+ where
+ F: FnMut(SectionId) -> core::result::Result<T, E>,
+ {
+ self.sup = Some(Arc::new(Self::load(section)?));
+ Ok(())
+ }
+
+ /// 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<Vec<u8>> = 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(&section, gimli::LittleEndian)
+ /// });
+ /// # unreachable!()
+ /// # }
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> Dwarf<R>
+ 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))),
+ }
+ }
+
+ /// Return a reference to the DWARF sections for supplementary object file.
+ pub fn sup(&self) -> Option<&Dwarf<T>> {
+ self.sup.as_ref().map(Arc::as_ref)
+ }
+}
+
+impl<R: Reader> Dwarf<R> {
+ /// 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<R> {
+ self.debug_info.units()
+ }
+
+ /// Construct a new `Unit` from the given unit header.
+ #[inline]
+ pub fn unit(&self, header: UnitHeader<R>) -> Result<Unit<R>> {
+ 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<R> {
+ self.debug_types.units()
+ }
+
+ /// Parse the abbreviations for a compilation unit.
+ // TODO: provide caching of abbreviations
+ #[inline]
+ pub fn abbreviations(&self, unit: &UnitHeader<R>) -> Result<Abbreviations> {
+ unit.abbreviations(&self.debug_abbrev)
+ }
+
+ /// Return the string offset at the given index.
+ #[inline]
+ pub fn string_offset(
+ &self,
+ unit: &Unit<R>,
+ index: DebugStrOffsetsIndex<R::Offset>,
+ ) -> Result<DebugStrOffset<R::Offset>> {
+ 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<R::Offset>) -> Result<R> {
+ 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<R::Offset>) -> Result<R> {
+ self.debug_line_str.get_str(offset)
+ }
+
+ /// 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<R>, attr: AttributeValue<R>) -> Result<R> {
+ match attr {
+ AttributeValue::String(string) => Ok(string),
+ AttributeValue::DebugStrRef(offset) => self.debug_str.get_str(offset),
+ AttributeValue::DebugStrRefSup(offset) => {
+ if let Some(sup) = self.sup() {
+ sup.debug_str.get_str(offset)
+ } else {
+ Err(Error::ExpectedStringAttributeValue)
+ }
+ }
+ AttributeValue::DebugLineStrRef(offset) => self.debug_line_str.get_str(offset),
+ AttributeValue::DebugStrOffsetsIndex(index) => {
+ let offset = self.debug_str_offsets.get_str_offset(
+ unit.header.format(),
+ unit.str_offsets_base,
+ index,
+ )?;
+ self.debug_str.get_str(offset)
+ }
+ _ => Err(Error::ExpectedStringAttributeValue),
+ }
+ }
+
+ /// Return the address at the given index.
+ pub fn address(&self, unit: &Unit<R>, index: DebugAddrIndex<R::Offset>) -> Result<u64> {
+ 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<R>, attr: AttributeValue<R>) -> Result<Option<u64>> {
+ 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<R>,
+ offset: RawRangeListsOffset<R::Offset>,
+ ) -> RangeListsOffset<R::Offset> {
+ 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<R>,
+ index: DebugRngListsIndex<R::Offset>,
+ ) -> Result<RangeListsOffset<R::Offset>> {
+ 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<R>,
+ offset: RangeListsOffset<R::Offset>,
+ ) -> Result<RngListIter<R>> {
+ 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<R>,
+ offset: RangeListsOffset<R::Offset>,
+ ) -> Result<RawRngListIter<R>> {
+ 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<R>,
+ attr: AttributeValue<R>,
+ ) -> Result<Option<RangeListsOffset<R::Offset>>> {
+ 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<R>,
+ attr: AttributeValue<R>,
+ ) -> Result<Option<RngListIter<R>>> {
+ 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<R>,
+ entry: &DebuggingInformationEntry<R>,
+ ) -> Result<RangeIter<R>> {
+ 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<R>) -> Result<RangeIter<R>> {
+ 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<R>,
+ index: DebugLocListsIndex<R::Offset>,
+ ) -> Result<LocationListsOffset<R::Offset>> {
+ 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<R>,
+ offset: LocationListsOffset<R::Offset>,
+ ) -> Result<LocListIter<R>> {
+ 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<R>,
+ offset: LocationListsOffset<R::Offset>,
+ ) -> Result<RawLocListIter<R>> {
+ 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<R>,
+ attr: AttributeValue<R>,
+ ) -> Result<Option<LocationListsOffset<R::Offset>>> {
+ 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<R>,
+ attr: AttributeValue<R>,
+ ) -> Result<Option<LocListIter<R>>> {
+ 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()
+ }
+}
+
+/// The sections from a `.dwp` file.
+#[derive(Debug)]
+pub struct DwarfPackage<R: Reader> {
+ /// The compilation unit index in the `.debug_cu_index` section.
+ pub cu_index: UnitIndex<R>,
+
+ /// The type unit index in the `.debug_tu_index` section.
+ pub tu_index: UnitIndex<R>,
+
+ /// The `.debug_abbrev.dwo` section.
+ pub debug_abbrev: DebugAbbrev<R>,
+
+ /// The `.debug_info.dwo` section.
+ pub debug_info: DebugInfo<R>,
+
+ /// The `.debug_line.dwo` section.
+ pub debug_line: DebugLine<R>,
+
+ /// The `.debug_str.dwo` section.
+ pub debug_str: DebugStr<R>,
+
+ /// The `.debug_str_offsets.dwo` section.
+ pub debug_str_offsets: DebugStrOffsets<R>,
+
+ /// The `.debug_loc.dwo` section.
+ ///
+ /// Only present when using GNU split-dwarf extension to DWARF 4.
+ pub debug_loc: DebugLoc<R>,
+
+ /// The `.debug_loclists.dwo` section.
+ pub debug_loclists: DebugLocLists<R>,
+
+ /// The `.debug_rnglists.dwo` section.
+ pub debug_rnglists: DebugRngLists<R>,
+
+ /// The `.debug_types.dwo` section.
+ ///
+ /// Only present when using GNU split-dwarf extension to DWARF 4.
+ pub debug_types: DebugTypes<R>,
+
+ /// An empty section.
+ ///
+ /// Used when creating `Dwarf<R>`.
+ pub empty: R,
+}
+
+impl<R: Reader> DwarfPackage<R> {
+ /// 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<F, E>(mut section: F, empty: R) -> core::result::Result<Self, E>
+ where
+ F: FnMut(SectionId) -> core::result::Result<R, E>,
+ E: From<Error>,
+ {
+ Ok(DwarfPackage {
+ cu_index: DebugCuIndex::load(&mut section)?.index()?,
+ tu_index: DebugTuIndex::load(&mut section)?.index()?,
+ // Section types are inferred.
+ 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)?,
+ empty,
+ })
+ }
+
+ /// Find the compilation unit with the given DWO identifier and return its section
+ /// contributions.
+ pub fn find_cu(&self, id: DwoId, parent: &Dwarf<R>) -> Result<Option<Dwarf<R>>> {
+ 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<R>,
+ ) -> Result<Option<Dwarf<R>>> {
+ 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<R>) -> Result<Dwarf<R>> {
+ 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<R>) -> Result<Dwarf<R>> {
+ 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<R>,
+ ) -> Result<Dwarf<R>> {
+ 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 {
+ SectionId::DebugAbbrev => {
+ abbrev_offset = section.offset;
+ abbrev_size = section.size;
+ }
+ SectionId::DebugInfo => {
+ info_offset = section.offset;
+ info_size = section.size;
+ }
+ SectionId::DebugLine => {
+ line_offset = section.offset;
+ line_size = section.size;
+ }
+ SectionId::DebugLoc => {
+ loc_offset = section.offset;
+ loc_size = section.size;
+ }
+ SectionId::DebugLocLists => {
+ loclists_offset = section.offset;
+ loclists_size = section.size;
+ }
+ SectionId::DebugStrOffsets => {
+ str_offsets_offset = section.offset;
+ str_offsets_size = section.size;
+ }
+ SectionId::DebugRngLists => {
+ rnglists_offset = section.offset;
+ rnglists_size = section.size;
+ }
+ SectionId::DebugTypes => {
+ types_offset = section.offset;
+ types_size = section.size;
+ }
+ SectionId::DebugMacro | SectionId::DebugMacinfo => {
+ // These are valid but we can't parse these yet.
+ }
+ _ => return Err(Error::UnknownIndexSection),
+ }
+ }
+
+ 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: None,
+ })
+ }
+}
+
+/// All of the commonly used information for a unit in the `.debug_info` or `.debug_types`
+/// sections.
+#[derive(Debug)]
+pub struct Unit<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// The header of the unit.
+ pub header: UnitHeader<R, Offset>,
+
+ /// The parsed abbreviations for the unit.
+ pub abbreviations: Abbreviations,
+
+ /// The `DW_AT_name` attribute of the unit.
+ pub name: Option<R>,
+
+ /// The `DW_AT_comp_dir` attribute of the unit.
+ pub comp_dir: Option<R>,
+
+ /// 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<Offset>,
+
+ /// The `DW_AT_addr_base` attribute of the unit. Defaults to 0.
+ pub addr_base: DebugAddrBase<Offset>,
+
+ /// The `DW_AT_loclists_base` attribute of the unit. Defaults to 0.
+ pub loclists_base: DebugLocListsBase<Offset>,
+
+ /// The `DW_AT_rnglists_base` attribute of the unit. Defaults to 0.
+ pub rnglists_base: DebugRngListsBase<Offset>,
+
+ /// The line number program of the unit.
+ pub line_program: Option<IncompleteLineProgram<R, Offset>>,
+
+ /// The DWO ID of a skeleton unit or split compilation unit.
+ pub dwo_id: Option<DwoId>,
+}
+
+impl<R: Reader> Unit<R> {
+ /// Construct a new `Unit` from the given unit header.
+ #[inline]
+ pub fn new(dwarf: &Dwarf<R>, header: UnitHeader<R>) -> Result<Self> {
+ let abbreviations = header.abbreviations(&dwarf.debug_abbrev)?;
+ 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 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<R::Offset>) -> Result<DebuggingInformationEntry<R>> {
+ 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<R::Offset>) -> Result<EntriesCursor<R>> {
+ 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<UnitOffset<R::Offset>>) -> Result<EntriesTree<R>> {
+ 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<UnitOffset<R::Offset>>) -> Result<EntriesRaw<R>> {
+ 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<R>) {
+ self.low_pc = other.low_pc;
+ self.addr_base = other.addr_base;
+ if self.header.version() < 5 {
+ self.rnglists_base = other.rnglists_base;
+ }
+ }
+}
+
+impl<T: ReaderOffset> UnitSectionOffset<T> {
+ /// 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<R>(&self, unit: &Unit<R>) -> Option<UnitOffset<T>>
+ where
+ R: Reader<Offset = T>,
+ {
+ 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<T: ReaderOffset> UnitOffset<T> {
+ /// 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<R>(&self, unit: &Unit<R>) -> UnitSectionOffset<T>
+ where
+ R: Reader<Offset = T>,
+ {
+ 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<R: Reader>(RangeIterInner<R>);
+
+#[derive(Debug)]
+enum RangeIterInner<R: Reader> {
+ Single(Option<Range>),
+ List(RngListIter<R>),
+}
+
+impl<R: Reader> Default for RangeIter<R> {
+ fn default() -> Self {
+ RangeIter(RangeIterInner::Single(None))
+ }
+}
+
+impl<R: Reader> RangeIter<R> {
+ /// Advance the iterator to the next range.
+ pub fn next(&mut self) -> Result<Option<Range>> {
+ match self.0 {
+ RangeIterInner::Single(ref mut range) => Ok(range.take()),
+ RangeIterInner::List(ref mut list) => list.next(),
+ }
+ }
+}
+
+#[cfg(feature = "fallible-iterator")]
+impl<R: Reader> fallible_iterator::FallibleIterator for RangeIter<R> {
+ type Item = Range;
+ type Error = Error;
+
+ #[inline]
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
+ RangeIter::next(self)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::read::EndianSlice;
+ use crate::{Endianity, LittleEndian};
+
+ /// Ensure that `Dwarf<R>` is covariant wrt R.
+ #[test]
+ fn test_dwarf_variance() {
+ /// This only needs to compile.
+ fn _f<'a: 'b, 'b, E: Endianity>(x: Dwarf<EndianSlice<'a, E>>) -> Dwarf<EndianSlice<'b, E>> {
+ x
+ }
+ }
+
+ /// Ensure that `Unit<R>` is covariant wrt R.
+ #[test]
+ fn test_dwarf_unit_variance() {
+ /// This only needs to compile.
+ fn _f<'a: 'b, 'b, E: Endianity>(x: Unit<EndianSlice<'a, E>>) -> Unit<EndianSlice<'b, E>> {
+ x
+ }
+ }
+
+ #[test]
+ fn test_send() {
+ fn assert_is_send<T: Send>() {}
+ assert_is_send::<Dwarf<EndianSlice<LittleEndian>>>();
+ assert_is_send::<Unit<EndianSlice<LittleEndian>>>();
+ }
+
+ #[test]
+ fn test_format_error() {
+ let mut owned_dwarf = Dwarf::load(|_| -> Result<_> { Ok(vec![1, 2]) }).unwrap();
+ owned_dwarf
+ .load_sup(|_| -> Result<_> { Ok(vec![1, 2]) })
+ .unwrap();
+ let dwarf = owned_dwarf.borrow(|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());
+ }
+}
diff --git a/vendor/gimli/src/read/endian_reader.rs b/vendor/gimli/src/read/endian_reader.rs
new file mode 100644
index 000000000..8852b3804
--- /dev/null
+++ b/vendor/gimli/src/read/endian_reader.rs
@@ -0,0 +1,639 @@
+//! 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::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<Endian> = EndianReader<Endian, Rc<[u8]>>;
+
+/// 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<Endian> = EndianReader<Endian, Arc<[u8]>>;
+
+/// 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<Rc<MmapFile>>` or `EndianReader<Arc<MmapFile>>` 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<MmapFile> {
+/// // 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<MmapFile>);
+///
+/// // 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<Endian> = gimli::EndianReader<Endian, ArcMmapFile>;
+/// # fn test(_: &MmapFileReader<gimli::NativeEndian>) { }
+/// ```
+#[derive(Debug, Clone, Copy, Hash)]
+pub struct EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + Debug,
+{
+ range: SubRange<T>,
+ endian: Endian,
+}
+
+impl<Endian, T1, T2> PartialEq<EndianReader<Endian, T2>> for EndianReader<Endian, T1>
+where
+ Endian: Endianity,
+ T1: CloneStableDeref<Target = [u8]> + Debug,
+ T2: CloneStableDeref<Target = [u8]> + Debug,
+{
+ fn eq(&self, rhs: &EndianReader<Endian, T2>) -> bool {
+ self.bytes() == rhs.bytes()
+ }
+}
+
+impl<Endian, T> Eq for EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + Debug,
+{
+}
+
+// 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<T>
+where
+ T: CloneStableDeref<Target = [u8]> + Debug,
+{
+ bytes: T,
+ ptr: *const u8,
+ len: usize,
+}
+
+unsafe impl<T> Send for SubRange<T> where T: CloneStableDeref<Target = [u8]> + Debug + Send {}
+
+unsafe impl<T> Sync for SubRange<T> where T: CloneStableDeref<Target = [u8]> + Debug + Sync {}
+
+impl<T> SubRange<T>
+where
+ T: CloneStableDeref<Target = [u8]> + 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<Endian, T> EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + Debug,
+{
+ /// Construct a new `EndianReader` with the given bytes.
+ #[inline]
+ pub fn new(bytes: T, endian: Endian) -> EndianReader<Endian, T> {
+ 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<Range<usize>>` to return a new `EndianReader` the way we
+/// would like to. Instead, we abandon fancy indexing operators and have these
+/// plain old methods.
+impl<Endian, T> EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + 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<usize>) -> EndianReader<Endian, T> {
+ 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<usize>) -> EndianReader<Endian, T> {
+ 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<usize>) -> EndianReader<Endian, T> {
+ let mut r = self.clone();
+ r.range.truncate(idx.end);
+ r
+ }
+}
+
+impl<Endian, T> Index<usize> for EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + Debug,
+{
+ type Output = u8;
+ fn index(&self, idx: usize) -> &Self::Output {
+ &self.bytes()[idx]
+ }
+}
+
+impl<Endian, T> Index<RangeFrom<usize>> for EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + Debug,
+{
+ type Output = [u8];
+ fn index(&self, idx: RangeFrom<usize>) -> &Self::Output {
+ &self.bytes()[idx]
+ }
+}
+
+impl<Endian, T> Deref for EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + Debug,
+{
+ type Target = [u8];
+ fn deref(&self) -> &Self::Target {
+ self.bytes()
+ }
+}
+
+impl<Endian, T> Reader for EndianReader<Endian, T>
+where
+ Endian: Endianity,
+ T: CloneStableDeref<Target = [u8]> + 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<Endian, T>) -> usize {
+ let base_ptr = base.bytes().as_ptr() as *const u8 as usize;
+ let ptr = self.bytes().as_ptr() as *const u8 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<Self::Offset> {
+ 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<usize> {
+ 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<Self> {
+ 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<Cow<[u8]>> {
+ Ok(self.bytes().into())
+ }
+
+ #[inline]
+ fn to_string(&self) -> Result<Cow<str>> {
+ match str::from_utf8(self.bytes()) {
+ Ok(s) => Ok(s.into()),
+ _ => Err(Error::BadUtf8),
+ }
+ }
+
+ #[inline]
+ fn to_string_lossy(&self) -> Result<Cow<str>> {
+ 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<T: CloneStableDeref<Target = [u8]> + Debug>(
+ bytes: T,
+ ) -> EndianReader<NativeEndian, T> {
+ 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..]);
+ }
+}
diff --git a/vendor/gimli/src/read/endian_slice.rs b/vendor/gimli/src/read/endian_slice.rs
new file mode 100644
index 000000000..05262cdec
--- /dev/null
+++ b/vendor/gimli/src/read/endian_slice.rs
@@ -0,0 +1,350 @@
+//! 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::ops::{Deref, Index, 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(Debug, 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 occurence of a byte in the slice, and return its index.
+ #[inline]
+ pub fn find(&self, byte: u8) -> Option<usize> {
+ 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 *const u8 as usize;
+ let ptr = self.slice.as_ptr() as *const u8 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<Range<usize>>` 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<usize>) -> 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<usize>) -> 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<usize>) -> EndianSlice<'input, Endian> {
+ EndianSlice {
+ slice: &self.slice[idx],
+ endian: self.endian,
+ }
+ }
+}
+
+impl<'input, Endian> Index<usize> for EndianSlice<'input, Endian>
+where
+ Endian: Endianity,
+{
+ type Output = u8;
+ fn index(&self, idx: usize) -> &Self::Output {
+ &self.slice[idx]
+ }
+}
+
+impl<'input, Endian> Index<RangeFrom<usize>> for EndianSlice<'input, Endian>
+where
+ Endian: Endianity,
+{
+ type Output = [u8];
+ fn index(&self, idx: RangeFrom<usize>) -> &Self::Output {
+ &self.slice[idx]
+ }
+}
+
+impl<'input, Endian> Deref for EndianSlice<'input, Endian>
+where
+ Endian: Endianity,
+{
+ type Target = [u8];
+ fn deref(&self) -> &Self::Target {
+ self.slice
+ }
+}
+
+impl<'input, Endian> Into<&'input [u8]> for EndianSlice<'input, Endian>
+where
+ Endian: Endianity,
+{
+ fn into(self) -> &'input [u8] {
+ self.slice
+ }
+}
+
+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<Self::Offset> {
+ 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<usize> {
+ 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<Self> {
+ 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<Cow<[u8]>> {
+ Ok(self.slice.into())
+ }
+
+ #[cfg(feature = "read")]
+ #[inline]
+ fn to_string(&self) -> Result<Cow<str>> {
+ match str::from_utf8(self.slice) {
+ Ok(s) => Ok(s.into()),
+ _ => Err(Error::BadUtf8),
+ }
+ }
+
+ #[cfg(feature = "read")]
+ #[inline]
+ fn to_string_lossy(&self) -> Result<Cow<str>> {
+ 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);
+ }
+}
diff --git a/vendor/gimli/src/read/index.rs b/vendor/gimli/src/read/index.rs
new file mode 100644
index 000000000..129eb2fb1
--- /dev/null
+++ b/vendor/gimli/src/read/index.rs
@@ -0,0 +1,535 @@
+use 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<R> {
+ section: R,
+}
+
+impl<'input, Endian> DebugCuIndex<EndianSlice<'input, Endian>>
+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<R> Section<R> for DebugCuIndex<R> {
+ fn id() -> SectionId {
+ SectionId::DebugCuIndex
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugCuIndex<R> {
+ fn from(section: R) -> Self {
+ DebugCuIndex { section }
+ }
+}
+
+impl<R: Reader> DebugCuIndex<R> {
+ /// Parse the index header.
+ pub fn index(self) -> Result<UnitIndex<R>> {
+ 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<R> {
+ section: R,
+}
+
+impl<'input, Endian> DebugTuIndex<EndianSlice<'input, Endian>>
+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<R> Section<R> for DebugTuIndex<R> {
+ fn id() -> SectionId {
+ SectionId::DebugTuIndex
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugTuIndex<R> {
+ fn from(section: R) -> Self {
+ DebugTuIndex { section }
+ }
+}
+
+impl<R: Reader> DebugTuIndex<R> {
+ /// Parse the index header.
+ pub fn index(self) -> Result<UnitIndex<R>> {
+ 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<R: Reader> {
+ version: u16,
+ section_count: u32,
+ unit_count: u32,
+ slot_count: u32,
+ hash_ids: R,
+ hash_rows: R,
+ // Only `section_count` values are valid.
+ sections: [SectionId; SECTION_COUNT_MAX as usize],
+ offsets: R,
+ sizes: R,
+}
+
+impl<R: Reader> UnitIndex<R> {
+ fn parse(mut input: R) -> Result<UnitIndex<R>> {
+ if input.is_empty() {
+ return Ok(UnitIndex {
+ version: 5,
+ section_count: 0,
+ unit_count: 0,
+ slot_count: 0,
+ hash_ids: input.clone(),
+ hash_rows: input.clone(),
+ sections: [SectionId::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 = [SectionId::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 => SectionId::DebugInfo,
+ constants::DW_SECT_V2_TYPES => SectionId::DebugTypes,
+ constants::DW_SECT_V2_ABBREV => SectionId::DebugAbbrev,
+ constants::DW_SECT_V2_LINE => SectionId::DebugLine,
+ constants::DW_SECT_V2_LOC => SectionId::DebugLoc,
+ constants::DW_SECT_V2_STR_OFFSETS => SectionId::DebugStrOffsets,
+ constants::DW_SECT_V2_MACINFO => SectionId::DebugMacinfo,
+ constants::DW_SECT_V2_MACRO => SectionId::DebugMacro,
+ _ => return Err(Error::UnknownIndexSection),
+ }
+ } else {
+ match constants::DwSect(section) {
+ constants::DW_SECT_INFO => SectionId::DebugInfo,
+ constants::DW_SECT_ABBREV => SectionId::DebugAbbrev,
+ constants::DW_SECT_LINE => SectionId::DebugLine,
+ constants::DW_SECT_LOCLISTS => SectionId::DebugLocLists,
+ constants::DW_SECT_STR_OFFSETS => SectionId::DebugStrOffsets,
+ constants::DW_SECT_MACRO => SectionId::DebugMacro,
+ constants::DW_SECT_RNGLISTS => SectionId::DebugRngLists,
+ _ => return Err(Error::UnknownIndexSection),
+ }
+ };
+ }
+
+ 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<u32> {
+ 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<UnitIndexSectionIterator<R>> {
+ 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.
+ 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, SectionId>,
+ offsets: R,
+ sizes: R,
+}
+
+impl<'index, R: Reader> Iterator for UnitIndexSectionIterator<'index, R> {
+ type Item = UnitIndexSection;
+
+ fn next(&mut self) -> Option<UnitIndexSection> {
+ 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: SectionId,
+ /// 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,
+}
+
+#[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!(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,
+ [
+ SectionId::DebugInfo,
+ SectionId::DebugTypes,
+ SectionId::DebugAbbrev,
+ SectionId::DebugLine,
+ SectionId::DebugLoc,
+ SectionId::DebugStrOffsets,
+ SectionId::DebugMacinfo,
+ SectionId::DebugMacro,
+ ]
+ );
+ #[rustfmt::skip]
+ let expect = [
+ UnitIndexSection { section: SectionId::DebugInfo, offset: 11, size: 21 },
+ UnitIndexSection { section: SectionId::DebugTypes, offset: 12, size: 22 },
+ UnitIndexSection { section: SectionId::DebugAbbrev, offset: 13, size: 23 },
+ UnitIndexSection { section: SectionId::DebugLine, offset: 14, size: 24 },
+ UnitIndexSection { section: SectionId::DebugLoc, offset: 15, size: 25 },
+ UnitIndexSection { section: SectionId::DebugStrOffsets, offset: 16, size: 26 },
+ UnitIndexSection { section: SectionId::DebugMacinfo, offset: 17, size: 27 },
+ UnitIndexSection { section: SectionId::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],
+ [
+ SectionId::DebugInfo,
+ SectionId::DebugAbbrev,
+ SectionId::DebugLine,
+ SectionId::DebugLocLists,
+ SectionId::DebugStrOffsets,
+ SectionId::DebugMacro,
+ SectionId::DebugRngLists,
+ ]
+ );
+ #[rustfmt::skip]
+ let expect = [
+ UnitIndexSection { section: SectionId::DebugInfo, offset: 11, size: 21 },
+ UnitIndexSection { section: SectionId::DebugAbbrev, offset: 12, size: 22 },
+ UnitIndexSection { section: SectionId::DebugLine, offset: 13, size: 23 },
+ UnitIndexSection { section: SectionId::DebugLocLists, offset: 14, size: 24 },
+ UnitIndexSection { section: SectionId::DebugStrOffsets, offset: 15, size: 25 },
+ UnitIndexSection { section: SectionId::DebugMacro, offset: 16, size: 26 },
+ UnitIndexSection { section: SectionId::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);
+ }
+}
diff --git a/vendor/gimli/src/read/line.rs b/vendor/gimli/src/read/line.rs
new file mode 100644
index 000000000..096ddf07b
--- /dev/null
+++ b/vendor/gimli/src/read/line.rs
@@ -0,0 +1,3030 @@
+use alloc::vec::Vec;
+use core::fmt;
+use core::num::{NonZeroU64, Wrapping};
+use core::result;
+
+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, 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<R> {
+ debug_line_section: R,
+}
+
+impl<'input, Endian> DebugLine<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugLine<R> {
+ /// 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<R::Offset>,
+ address_size: u8,
+ comp_dir: Option<R>,
+ comp_name: Option<R>,
+ ) -> Result<IncompleteLineProgram<R>> {
+ 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<T> DebugLine<T> {
+ /// Create a `DebugLine` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugLine<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLine<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.debug_line_section).into()
+ }
+}
+
+impl<R> Section<R> for DebugLine<R> {
+ fn id() -> SectionId {
+ SectionId::DebugLine
+ }
+
+ fn reader(&self) -> &R {
+ &self.debug_line_section
+ }
+}
+
+impl<R> From<R> for DebugLine<R> {
+ 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<R, Offset> = dyn LineProgram<R, Offset>;
+
+/// 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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Get a reference to the held `LineProgramHeader`.
+ fn header(&self) -> &LineProgramHeader<R, Offset>;
+ /// Add a file to the file table if necessary.
+ fn add_file(&mut self, file: FileEntry<R, Offset>);
+}
+
+impl<R, Offset> LineProgram<R, Offset> for IncompleteLineProgram<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ fn header(&self) -> &LineProgramHeader<R, Offset> {
+ &self.header
+ }
+ fn add_file(&mut self, file: FileEntry<R, Offset>) {
+ self.header.file_names.push(file);
+ }
+}
+
+impl<'program, R, Offset> LineProgram<R, Offset> for &'program CompleteLineProgram<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ fn header(&self) -> &LineProgramHeader<R, Offset> {
+ &self.header
+ }
+ fn add_file(&mut self, _: FileEntry<R, Offset>) {
+ // 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<R, Program, Offset> = LineRows<R, Program, Offset>;
+
+/// 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<R, Program, Offset = <R as Reader>::Offset>
+where
+ Program: LineProgram<R, Offset>,
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ program: Program,
+ row: LineRow,
+ instructions: LineInstructions<R>,
+}
+
+type OneShotLineRows<R, Offset = <R as Reader>::Offset> =
+ LineRows<R, IncompleteLineProgram<R, Offset>, Offset>;
+
+type ResumedLineRows<'program, R, Offset = <R as Reader>::Offset> =
+ LineRows<R, &'program CompleteLineProgram<R, Offset>, Offset>;
+
+impl<R, Program, Offset> LineRows<R, Program, Offset>
+where
+ Program: LineProgram<R, Offset>,
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ #[allow(clippy::new_ret_no_self)]
+ fn new(program: IncompleteLineProgram<R, Offset>) -> OneShotLineRows<R, Offset> {
+ 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<R, Offset>,
+ sequence: &LineSequence<R>,
+ ) -> 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<R, Offset> {
+ 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<Option<(&LineProgramHeader<R, Offset>, &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) {
+ 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<R> = LineInstruction<R, <R as Reader>::Offset>;
+
+/// A parsed line number program instruction.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum LineInstruction<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<R, Offset>),
+
+ /// "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<R, Offset> LineInstruction<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ fn parse<'header>(
+ header: &'header LineProgramHeader<R>,
+ input: &mut R,
+ ) -> Result<LineInstruction<R>>
+ 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))
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+impl<R, Offset> fmt::Display for LineInstruction<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
+ match *self {
+ LineInstruction::Special(opcode) => write!(f, "Special opcode {}", opcode),
+ LineInstruction::Copy => write!(f, "{}", constants::DW_LNS_copy),
+ LineInstruction::AdvancePc(advance) => {
+ write!(f, "{} by {}", constants::DW_LNS_advance_pc, advance)
+ }
+ LineInstruction::AdvanceLine(increment) => {
+ write!(f, "{} by {}", constants::DW_LNS_advance_line, increment)
+ }
+ LineInstruction::SetFile(file) => {
+ write!(f, "{} to {}", constants::DW_LNS_set_file, file)
+ }
+ LineInstruction::SetColumn(column) => {
+ write!(f, "{} to {}", constants::DW_LNS_set_column, column)
+ }
+ LineInstruction::NegateStatement => write!(f, "{}", constants::DW_LNS_negate_stmt),
+ LineInstruction::SetBasicBlock => write!(f, "{}", constants::DW_LNS_set_basic_block),
+ LineInstruction::ConstAddPc => write!(f, "{}", constants::DW_LNS_const_add_pc),
+ LineInstruction::FixedAddPc(advance) => {
+ write!(f, "{} by {}", constants::DW_LNS_fixed_advance_pc, advance)
+ }
+ LineInstruction::SetPrologueEnd => write!(f, "{}", constants::DW_LNS_set_prologue_end),
+ LineInstruction::SetEpilogueBegin => {
+ write!(f, "{}", constants::DW_LNS_set_epilogue_begin)
+ }
+ LineInstruction::SetIsa(isa) => write!(f, "{} to {}", constants::DW_LNS_set_isa, isa),
+ LineInstruction::UnknownStandard0(opcode) => write!(f, "Unknown {}", opcode),
+ LineInstruction::UnknownStandard1(opcode, arg) => {
+ write!(f, "Unknown {} with operand {}", opcode, arg)
+ }
+ LineInstruction::UnknownStandardN(opcode, ref args) => {
+ write!(f, "Unknown {} with operands {:?}", opcode, args)
+ }
+ LineInstruction::EndSequence => write!(f, "{}", constants::DW_LNE_end_sequence),
+ LineInstruction::SetAddress(address) => {
+ write!(f, "{} to {}", constants::DW_LNE_set_address, address)
+ }
+ LineInstruction::DefineFile(_) => write!(f, "{}", constants::DW_LNE_define_file),
+ LineInstruction::SetDiscriminator(discr) => {
+ write!(f, "{} to {}", constants::DW_LNE_set_discriminator, discr)
+ }
+ LineInstruction::UnknownExtended(opcode, _) => write!(f, "Unknown {}", opcode),
+ }
+ }
+}
+
+/// Deprecated. `OpcodesIter` has been renamed to `LineInstructions`.
+#[deprecated(note = "OpcodesIter has been renamed to LineInstructions, use that instead.")]
+pub type OpcodesIter<R> = LineInstructions<R>;
+
+/// An iterator yielding parsed instructions.
+///
+/// See
+/// [`LineProgramHeader::instructions`](./struct.LineProgramHeader.html#method.instructions)
+/// for more details.
+#[derive(Clone, Debug)]
+pub struct LineInstructions<R: Reader> {
+ input: R,
+}
+
+impl<R: Reader> LineInstructions<R> {
+ fn remove_trailing(&self, other: &LineInstructions<R>) -> Result<LineInstructions<R>> {
+ let offset = other.input.offset_from(&self.input);
+ let mut input = self.input.clone();
+ input.truncate(offset)?;
+ Ok(LineInstructions { input })
+ }
+}
+
+impl<R: Reader> LineInstructions<R> {
+ /// 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`.
+ #[allow(clippy::inline_always)]
+ #[inline(always)]
+ pub fn next_instruction(
+ &mut self,
+ header: &LineProgramHeader<R>,
+ ) -> Result<Option<LineInstruction<R>>> {
+ 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 {
+ address: Wrapping<u64>,
+ op_index: Wrapping<u64>,
+ file: u64,
+ line: Wrapping<u64>,
+ 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<R: Reader>(header: &LineProgramHeader<R>) -> Self {
+ LineRow {
+ // "At the beginning of each sequence within a line number program, the
+ // state of the registers is:" -- Section 6.2.2
+ address: Wrapping(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.0
+ }
+
+ /// > 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<R>,
+ ) -> Option<&'header FileEntry<R>> {
+ 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> {
+ 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<R, Program>(
+ &mut self,
+ instruction: LineInstruction<R>,
+ program: &mut Program,
+ ) -> bool
+ where
+ Program: LineProgram<R>,
+ R: Reader,
+ {
+ 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) => {
+ self.address += Wrapping(u64::from(operand));
+ 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) => {
+ self.address.0 = 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<R: Reader>(&mut self, header: &LineProgramHeader<R>) {
+ 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<R: Reader>(
+ &mut self,
+ operation_advance: u64,
+ header: &LineProgramHeader<R>,
+ ) {
+ 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);
+
+ if maximum_operations_per_instruction.0 == 1 {
+ self.address += minimum_instruction_length * operation_advance;
+ self.op_index.0 = 0;
+ } else {
+ let op_index_with_advance = self.op_index + operation_advance;
+ self.address += minimum_instruction_length
+ * (op_index_with_advance / maximum_operations_per_instruction);
+ self.op_index = op_index_with_advance % maximum_operations_per_instruction;
+ }
+ }
+
+ #[inline]
+ fn adjust_opcode<R: Reader>(&self, opcode: u8, header: &LineProgramHeader<R>) -> u8 {
+ opcode - header.opcode_base
+ }
+
+ /// Section 6.2.5.1
+ fn exec_special_opcode<R: Reader>(&mut self, opcode: u8, header: &LineProgramHeader<R>) {
+ 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);
+ }
+}
+
+/// 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<R> = LineSequence<R>;
+
+/// 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<R: Reader> {
+ /// 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<R>,
+}
+
+/// Deprecated. `LineNumberProgramHeader` has been renamed to `LineProgramHeader`.
+#[deprecated(
+ note = "LineNumberProgramHeader has been renamed to LineProgramHeader, use that instead."
+)]
+pub type LineNumberProgramHeader<R, Offset> = LineProgramHeader<R, Offset>;
+
+/// 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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ encoding: Encoding,
+ offset: DebugLineOffset<Offset>,
+ 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<FileEntryFormat>,
+
+ /// > 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<AttributeValue<R, Offset>>,
+
+ /// "A sequence of file entry format descriptions."
+ file_name_entry_format: Vec<FileEntryFormat>,
+
+ /// "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<FileEntry<R, Offset>>,
+
+ /// The encoded line program instructions.
+ program_buf: R,
+
+ /// The current directory of the compilation.
+ comp_dir: Option<R>,
+
+ /// The primary source file.
+ comp_file: Option<FileEntry<R, Offset>>,
+}
+
+impl<R, Offset> LineProgramHeader<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Return the offset of the line number program header in the `.debug_line` section.
+ pub fn offset(&self) -> DebugLineOffset<R::Offset> {
+ 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<R, Offset>] {
+ &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<AttributeValue<R, Offset>> {
+ 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)
+ }
+
+ /// Get the list of source files that appear in this header's line program.
+ pub fn file_names(&self) -> &[FileEntry<R, Offset>] {
+ &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<R, Offset>> {
+ 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<EndianSlice<'a, NativeEndian>> {
+ /// // 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<R> {
+ LineInstructions {
+ input: self.program_buf.clone(),
+ }
+ }
+
+ fn parse(
+ input: &mut R,
+ offset: DebugLineOffset<Offset>,
+ mut address_size: u8,
+ mut comp_dir: Option<R>,
+ comp_name: Option<R>,
+ ) -> Result<LineProgramHeader<R, Offset>> {
+ 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_u8()?;
+ 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],
+ });
+
+ 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<R, Offset> = IncompleteLineProgram<R, Offset>;
+
+/// A line number program that has not been run to completion.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct IncompleteLineProgram<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ header: LineProgramHeader<R, Offset>,
+}
+
+impl<R, Offset> IncompleteLineProgram<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Retrieve the `LineProgramHeader` for this program.
+ pub fn header(&self) -> &LineProgramHeader<R, Offset> {
+ &self.header
+ }
+
+ /// Construct a new `LineRows` for executing this program to iterate
+ /// over rows in the line information matrix.
+ pub fn rows(self) -> OneShotLineRows<R, Offset> {
+ 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<EndianSlice<'a, NativeEndian>> {
+ /// // 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<R, Offset>, Vec<LineSequence<R>>)> {
+ 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<R, Offset> = CompleteLineProgram<R, Offset>;
+
+/// A line number program that has previously been run to completion.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct CompleteLineProgram<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ header: LineProgramHeader<R, Offset>,
+}
+
+impl<R, Offset> CompleteLineProgram<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Retrieve the `LineProgramHeader` for this program.
+ pub fn header(&self) -> &LineProgramHeader<R, Offset> {
+ &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<EndianSlice<'a, NativeEndian>> {
+ /// // 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<R>,
+ ) -> 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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ path_name: AttributeValue<R, Offset>,
+ directory_index: u64,
+ timestamp: u64,
+ size: u64,
+ md5: [u8; 16],
+}
+
+impl<R, Offset> FileEntry<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ // version 2-4
+ fn parse(input: &mut R, path_name: R) -> Result<FileEntry<R, Offset>> {
+ 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],
+ };
+
+ 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<R, Offset> {
+ 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<R>) -> Option<AttributeValue<R, Offset>> {
+ 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 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<R: Reader>(input: &mut R) -> Result<Vec<FileEntryFormat>> {
+ 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_value()) {
+ constants::DwLnct(u16::max_value())
+ } 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<R: Reader>(
+ input: &mut R,
+ encoding: Encoding,
+ formats: &[FileEntryFormat],
+) -> Result<AttributeValue<R>> {
+ 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<R: Reader>(
+ input: &mut R,
+ encoding: Encoding,
+ formats: &[FileEntryFormat],
+) -> Result<FileEntry<R>> {
+ let mut path_name = None;
+ let mut directory_index = 0;
+ let mut timestamp = 0;
+ let mut size = 0;
+ let mut md5 = [0; 16];
+
+ 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()?;
+ }
+ }
+ }
+ // Ignore unknown content types.
+ _ => {}
+ }
+ }
+
+ Ok(FileEntry {
+ path_name: path_name.unwrap(),
+ directory_index,
+ timestamp,
+ size,
+ md5,
+ })
+}
+
+// TODO: this should be shared with unit::parse_attribute(), but that is hard to do.
+fn parse_attribute<R: Reader>(
+ input: &mut R,
+ encoding: Encoding,
+ form: constants::DwForm,
+) -> Result<AttributeValue<R>> {
+ 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);
+ }
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::constants;
+ use crate::endianity::LittleEndian;
+ use crate::read::{EndianSlice, Error};
+ use crate::test_util::GimliSectionMethods;
+ use core::u64;
+ use core::u8;
+ 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_eq!(header.default_is_stmt(), true);
+ 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],
+ },
+ FileEntry {
+ path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)),
+ directory_index: 1,
+ timestamp: 0,
+ size: 0,
+ md5: [0; 16],
+ },
+ ];
+ 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(_)) => return,
+ 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(_)) => return,
+ 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<EndianSlice<LittleEndian>> {
+ 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],
+ },
+ FileEntry {
+ path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)),
+ directory_index: 0,
+ timestamp: 0,
+ size: 0,
+ md5: [0; 16],
+ },
+ ],
+ 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<EndianSlice<LittleEndian>> {
+ 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<Operands>(
+ raw: constants::DwLns,
+ operands: Operands,
+ expected: LineInstruction<EndianSlice<LittleEndian>>,
+ ) 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 = EndianSlice::new(&input[1..], 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(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<Operands>(
+ raw: constants::DwLne,
+ operands: Operands,
+ expected: LineInstruction<EndianSlice<LittleEndian>>,
+ ) 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],
+ }),
+ );
+
+ // 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],
+ };
+
+ 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<EndianSlice<'input, LittleEndian>>,
+ mut registers: LineRow,
+ opcode: LineInstruction<EndianSlice<'input, LittleEndian>>,
+ 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, 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.0 += 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.0 += 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.0 += 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.0 = 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.0 += 42;
+
+ assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
+ }
+
+ #[test]
+ fn test_exec_advance_pc_overflow() {
+ let header = make_test_header(EndianSlice::new(&[], LittleEndian));
+ let opcode = LineInstruction::AdvancePc(42);
+
+ let mut initial_registers = LineRow::new(&header);
+ initial_registers.address.0 = u64::MAX;
+
+ let mut expected_registers = initial_registers;
+ expected_registers.address.0 = 41;
+
+ assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
+ }
+
+ #[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.0 += 20;
+
+ assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
+ }
+
+ #[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.0 += 10;
+ expected_registers.op_index.0 = 0;
+
+ assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
+ }
+
+ #[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.0 = 3030;
+
+ 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],
+ };
+
+ let opcode = LineInstruction::DefineFile(file);
+ let is_new_row = row.execute(opcode, &mut program);
+
+ assert_eq!(is_new_row, false);
+ 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<R,P>` is covariant wrt R.
+ /// This only needs to compile.
+ #[allow(dead_code, unreachable_code, unused_variables)]
+ fn test_line_rows_variance<'a, 'b>(_: &'a [u8], _: &'b [u8])
+ where
+ 'a: 'b,
+ {
+ let a: &OneShotLineRows<EndianSlice<'a, LittleEndian>> = unimplemented!();
+ let _: &OneShotLineRows<EndianSlice<'b, LittleEndian>> = 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],
+ },
+ 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,
+ ],
+ },
+ ];
+
+ for format in vec![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(3)
+ .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)
+ // File count.
+ .D8(2)
+ .append_bytes(b"file1\0")
+ .D8(0)
+ .append_bytes(&expected_file_names[0].md5)
+ .append_bytes(b"file2\0")
+ .D8(1)
+ .append_bytes(&expected_file_names[1].md5)
+ .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(&section, 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_eq!(header.default_is_stmt(), true);
+ 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,
+ }
+ ]
+ );
+ assert_eq!(header.file_names(), expected_file_names);
+ assert_eq!(header.file(0), Some(&expected_file_names[0]));
+ }
+ }
+}
diff --git a/vendor/gimli/src/read/lists.rs b/vendor/gimli/src/read/lists.rs
new file mode 100644
index 000000000..b63c5c126
--- /dev/null
+++ b/vendor/gimli/src/read/lists.rs
@@ -0,0 +1,67 @@
+use crate::common::{Encoding, Format};
+use crate::read::{Error, Reader, Result};
+
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct ListsHeader {
+ encoding: Encoding,
+ 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<R: Reader>(input: &mut R) -> Result<ListsHeader> {
+ 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_u8()?;
+ 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,
+ })
+}
diff --git a/vendor/gimli/src/read/loclists.rs b/vendor/gimli/src/read/loclists.rs
new file mode 100644
index 000000000..3c4da127d
--- /dev/null
+++ b/vendor/gimli/src/read/loclists.rs
@@ -0,0 +1,1514 @@
+use 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,
+ ReaderOffset, ReaderOffsetId, Result, Section,
+};
+
+/// The raw contents of the `.debug_loc` section.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct DebugLoc<R> {
+ pub(crate) section: R,
+}
+
+impl<'input, Endian> DebugLoc<EndianSlice<'input, Endian>>
+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 OSX, 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<R> Section<R> for DebugLoc<R> {
+ fn id() -> SectionId {
+ SectionId::DebugLoc
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugLoc<R> {
+ 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<R> {
+ section: R,
+}
+
+impl<'input, Endian> DebugLocLists<EndianSlice<'input, Endian>>
+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 OSX, 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<R> Section<R> for DebugLocLists<R> {
+ fn id() -> SectionId {
+ SectionId::DebugLocLists
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugLocLists<R> {
+ fn from(section: R) -> Self {
+ DebugLocLists { section }
+ }
+}
+
+pub(crate) type LocListsHeader = ListsHeader;
+
+impl<Offset> DebugLocListsBase<Offset>
+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<Offset> {
+ 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<R> {
+ debug_loc: DebugLoc<R>,
+ debug_loclists: DebugLocLists<R>,
+}
+
+impl<R> LocationLists<R> {
+ /// Construct a new `LocationLists` instance from the data in the `.debug_loc` and
+ /// `.debug_loclists` sections.
+ pub fn new(debug_loc: DebugLoc<R>, debug_loclists: DebugLocLists<R>) -> LocationLists<R> {
+ LocationLists {
+ debug_loc,
+ debug_loclists,
+ }
+ }
+}
+
+impl<T> LocationLists<T> {
+ /// Create a `LocationLists` that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::LocationLists<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> LocationLists<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ LocationLists {
+ debug_loc: borrow(&self.debug_loc.section).into(),
+ debug_loclists: borrow(&self.debug_loclists.section).into(),
+ }
+ }
+}
+
+impl<R: Reader> LocationLists<R> {
+ /// 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<R::Offset>,
+ unit_encoding: Encoding,
+ base_address: u64,
+ debug_addr: &DebugAddr<R>,
+ debug_addr_base: DebugAddrBase<R::Offset>,
+ ) -> Result<LocListIter<R>> {
+ 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<R::Offset>,
+ unit_encoding: Encoding,
+ base_address: u64,
+ debug_addr: &DebugAddr<R>,
+ debug_addr_base: DebugAddrBase<R::Offset>,
+ ) -> Result<LocListIter<R>> {
+ 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<R::Offset>,
+ unit_encoding: Encoding,
+ ) -> Result<RawLocListIter<R>> {
+ 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<R::Offset>,
+ unit_encoding: Encoding,
+ ) -> Result<RawLocListIter<R>> {
+ 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<R::Offset>,
+ index: DebugLocListsIndex<R::Offset>,
+ ) -> Result<LocationListsOffset<R::Offset>> {
+ 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<R: Reader> {
+ input: R,
+ encoding: Encoding,
+ format: LocListsFormat,
+}
+
+/// A raw entry in .debug_loclists.
+#[derive(Clone, Debug)]
+pub enum RawLocListEntry<R: Reader> {
+ /// 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<R>,
+ },
+ /// DW_LLE_base_address
+ BaseAddress {
+ /// base address
+ addr: u64,
+ },
+ /// DW_LLE_base_addressx
+ BaseAddressx {
+ /// base address
+ addr: DebugAddrIndex<R::Offset>,
+ },
+ /// DW_LLE_startx_endx
+ StartxEndx {
+ /// start of range
+ begin: DebugAddrIndex<R::Offset>,
+ /// end of range
+ end: DebugAddrIndex<R::Offset>,
+ /// expression
+ data: Expression<R>,
+ },
+ /// DW_LLE_startx_length
+ StartxLength {
+ /// start of range
+ begin: DebugAddrIndex<R::Offset>,
+ /// length of range
+ length: u64,
+ /// expression
+ data: Expression<R>,
+ },
+ /// DW_LLE_offset_pair
+ OffsetPair {
+ /// start of range
+ begin: u64,
+ /// end of range
+ end: u64,
+ /// expression
+ data: Expression<R>,
+ },
+ /// DW_LLE_default_location
+ DefaultLocation {
+ /// expression
+ data: Expression<R>,
+ },
+ /// DW_LLE_start_end
+ StartEnd {
+ /// start of range
+ begin: u64,
+ /// end of range
+ end: u64,
+ /// expression
+ data: Expression<R>,
+ },
+ /// DW_LLE_start_length
+ StartLength {
+ /// start of range
+ begin: u64,
+ /// length of range
+ length: u64,
+ /// expression
+ data: Expression<R>,
+ },
+}
+
+fn parse_data<R: Reader>(input: &mut R, encoding: Encoding) -> Result<Expression<R>> {
+ 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<R: Reader> RawLocListEntry<R> {
+ /// Parse a location list entry from `.debug_loclists`
+ fn parse(input: &mut R, encoding: Encoding, format: LocListsFormat) -> Result<Option<Self>> {
+ match format {
+ LocListsFormat::Bare => {
+ let range = RawRange::parse(input, encoding.address_size)?;
+ return Ok(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 => Ok(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)?,
+ }),
+ _ => {
+ return Err(Error::InvalidAddressRange);
+ }
+ }),
+ }
+ }
+}
+
+impl<R: Reader> RawLocListIter<R> {
+ /// Construct a `RawLocListIter`.
+ fn new(input: R, encoding: Encoding, format: LocListsFormat) -> RawLocListIter<R> {
+ RawLocListIter {
+ input,
+ encoding,
+ format,
+ }
+ }
+
+ /// Advance the iterator to the next location.
+ pub fn next(&mut self) -> Result<Option<RawLocListEntry<R>>> {
+ 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<R: Reader> fallible_iterator::FallibleIterator for RawLocListIter<R> {
+ type Item = RawLocListEntry<R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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<R: Reader> {
+ raw: RawLocListIter<R>,
+ base_address: u64,
+ debug_addr: DebugAddr<R>,
+ debug_addr_base: DebugAddrBase<R::Offset>,
+}
+
+impl<R: Reader> LocListIter<R> {
+ /// Construct a `LocListIter`.
+ fn new(
+ raw: RawLocListIter<R>,
+ base_address: u64,
+ debug_addr: DebugAddr<R>,
+ debug_addr_base: DebugAddrBase<R::Offset>,
+ ) -> LocListIter<R> {
+ LocListIter {
+ raw,
+ base_address,
+ debug_addr,
+ debug_addr_base,
+ }
+ }
+
+ #[inline]
+ fn get_address(&self, index: DebugAddrIndex<R::Offset>) -> Result<u64> {
+ 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<Option<LocationListEntry<R>>> {
+ loop {
+ let raw_loc = match self.raw.next()? {
+ Some(loc) => loc,
+ None => return Ok(None),
+ };
+
+ let (range, data) = match raw_loc {
+ RawLocListEntry::BaseAddress { addr } => {
+ self.base_address = addr;
+ continue;
+ }
+ RawLocListEntry::BaseAddressx { addr } => {
+ self.base_address = self.get_address(addr)?;
+ continue;
+ }
+ 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 + length;
+ (Range { begin, end }, data)
+ }
+ RawLocListEntry::DefaultLocation { data } => (
+ Range {
+ begin: 0,
+ end: u64::max_value(),
+ },
+ data,
+ ),
+ RawLocListEntry::AddressOrOffsetPair { begin, end, data }
+ | RawLocListEntry::OffsetPair { begin, end, data } => {
+ 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,
+ } => (
+ Range {
+ begin,
+ end: begin + length,
+ },
+ data,
+ ),
+ };
+
+ if range.begin > range.end {
+ self.raw.input.empty();
+ return Err(Error::InvalidLocationAddressRange);
+ }
+
+ return Ok(Some(LocationListEntry { range, data }));
+ }
+ }
+}
+
+#[cfg(feature = "fallible-iterator")]
+impl<R: Reader> fallible_iterator::FallibleIterator for LocListIter<R> {
+ type Item = LocationListEntry<R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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<R: Reader> {
+ /// The address range that this location is valid for.
+ pub range: Range,
+
+ /// The data containing a single location description.
+ pub data: Expression<R>,
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::common::Format;
+ use crate::endianity::LittleEndian;
+ use crate::read::{EndianSlice, Range};
+ use crate::test_util::GimliSectionMethods;
+ use test_assembler::{Endian, Label, LabelMaker, Section};
+
+ #[test]
+ fn test_loclists_32() {
+ let encoding = Encoding {
+ format: Format::Dwarf32,
+ version: 5,
+ address_size: 4,
+ };
+
+ let section = Section::with_endian(Endian::Little)
+ .L32(0x0300_0000)
+ .L32(0x0301_0300)
+ .L32(0x0301_0400)
+ .L32(0x0301_0500);
+ let buf = section.get_contents().unwrap();
+ let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let start = Label::new();
+ let first = Label::new();
+ let size = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // Header
+ .mark(&start)
+ .L32(&size)
+ .L16(encoding.version)
+ .L8(encoding.address_size)
+ .L8(0)
+ .L32(0)
+ .mark(&first)
+ // OffsetPair
+ .L8(4).uleb(0x10200).uleb(0x10300).uleb(4).L32(2)
+ // A base address selection followed by an OffsetPair.
+ .L8(6).L32(0x0200_0000)
+ .L8(4).uleb(0x10400).uleb(0x10500).uleb(4).L32(3)
+ // An empty OffsetPair followed by a normal OffsetPair.
+ .L8(4).uleb(0x10600).uleb(0x10600).uleb(4).L32(4)
+ .L8(4).uleb(0x10800).uleb(0x10900).uleb(4).L32(5)
+ // A StartEnd
+ .L8(7).L32(0x201_0a00).L32(0x201_0b00).uleb(4).L32(6)
+ // A StartLength
+ .L8(8).L32(0x201_0c00).uleb(0x100).uleb(4).L32(7)
+ // An OffsetPair that starts at 0.
+ .L8(4).uleb(0).uleb(1).uleb(4).L32(8)
+ // An OffsetPair that ends at -1.
+ .L8(6).L32(0)
+ .L8(4).uleb(0).uleb(0xffff_ffff).uleb(4).L32(9)
+ // A DefaultLocation
+ .L8(5).uleb(4).L32(10)
+ // A BaseAddressx + OffsetPair
+ .L8(1).uleb(0)
+ .L8(4).uleb(0x10100).uleb(0x10200).uleb(4).L32(11)
+ // A StartxEndx
+ .L8(2).uleb(1).uleb(2).uleb(4).L32(12)
+ // A StartxLength
+ .L8(3).uleb(3).uleb(0x100).uleb(4).L32(13)
+ // A range end.
+ .L8(0)
+ // Some extra data.
+ .L32(0xffff_ffff);
+ size.set_const((&section.here() - &start - 4) as u64);
+
+ 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 offset = LocationListsOffset((&first - &start) as usize);
+ let mut locations = loclists
+ .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ },
+ data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A base address selection followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ },
+ data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // An empty location range followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ },
+ data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)),
+ }))
+ );
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ },
+ data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0a00,
+ end: 0x0201_0b00,
+ },
+ data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0c00,
+ end: 0x0201_0d00,
+ },
+ data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that starts at 0.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ },
+ data: Expression(EndianSlice::new(&[8, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that ends at -1.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0000_0000,
+ end: 0xffff_ffff,
+ },
+ data: Expression(EndianSlice::new(&[9, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A DefaultLocation.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0,
+ end: u64::max_value(),
+ },
+ data: Expression(EndianSlice::new(&[10, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A BaseAddressx + OffsetPair
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0301_0100,
+ end: 0x0301_0200,
+ },
+ data: Expression(EndianSlice::new(&[11, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A StartxEndx
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0301_0300,
+ end: 0x0301_0400,
+ },
+ data: Expression(EndianSlice::new(&[12, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A StartxLength
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0301_0500,
+ end: 0x0301_0600,
+ },
+ data: Expression(EndianSlice::new(&[13, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location list end.
+ 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_loclists_64() {
+ let encoding = Encoding {
+ format: Format::Dwarf64,
+ version: 5,
+ address_size: 8,
+ };
+
+ let section = Section::with_endian(Endian::Little)
+ .L64(0x0300_0000)
+ .L64(0x0301_0300)
+ .L64(0x0301_0400)
+ .L64(0x0301_0500);
+ let buf = section.get_contents().unwrap();
+ let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let start = Label::new();
+ let first = Label::new();
+ let size = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // Header
+ .mark(&start)
+ .L32(0xffff_ffff)
+ .L64(&size)
+ .L16(encoding.version)
+ .L8(encoding.address_size)
+ .L8(0)
+ .L32(0)
+ .mark(&first)
+ // OffsetPair
+ .L8(4).uleb(0x10200).uleb(0x10300).uleb(4).L32(2)
+ // A base address selection followed by an OffsetPair.
+ .L8(6).L64(0x0200_0000)
+ .L8(4).uleb(0x10400).uleb(0x10500).uleb(4).L32(3)
+ // An empty OffsetPair followed by a normal OffsetPair.
+ .L8(4).uleb(0x10600).uleb(0x10600).uleb(4).L32(4)
+ .L8(4).uleb(0x10800).uleb(0x10900).uleb(4).L32(5)
+ // A StartEnd
+ .L8(7).L64(0x201_0a00).L64(0x201_0b00).uleb(4).L32(6)
+ // A StartLength
+ .L8(8).L64(0x201_0c00).uleb(0x100).uleb(4).L32(7)
+ // An OffsetPair that starts at 0.
+ .L8(4).uleb(0).uleb(1).uleb(4).L32(8)
+ // An OffsetPair that ends at -1.
+ .L8(6).L64(0)
+ .L8(4).uleb(0).uleb(0xffff_ffff).uleb(4).L32(9)
+ // A DefaultLocation
+ .L8(5).uleb(4).L32(10)
+ // A BaseAddressx + OffsetPair
+ .L8(1).uleb(0)
+ .L8(4).uleb(0x10100).uleb(0x10200).uleb(4).L32(11)
+ // A StartxEndx
+ .L8(2).uleb(1).uleb(2).uleb(4).L32(12)
+ // A StartxLength
+ .L8(3).uleb(3).uleb(0x100).uleb(4).L32(13)
+ // A range end.
+ .L8(0)
+ // Some extra data.
+ .L32(0xffff_ffff);
+ size.set_const((&section.here() - &start - 12) as u64);
+
+ 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 offset = LocationListsOffset((&first - &start) as usize);
+ let mut locations = loclists
+ .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ },
+ data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A base address selection followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ },
+ data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // An empty location range followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ },
+ data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)),
+ }))
+ );
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ },
+ data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0a00,
+ end: 0x0201_0b00,
+ },
+ data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0c00,
+ end: 0x0201_0d00,
+ },
+ data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that starts at 0.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ },
+ data: Expression(EndianSlice::new(&[8, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that ends at -1.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0000_0000,
+ end: 0xffff_ffff,
+ },
+ data: Expression(EndianSlice::new(&[9, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A DefaultLocation.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0,
+ end: u64::max_value(),
+ },
+ data: Expression(EndianSlice::new(&[10, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A BaseAddressx + OffsetPair
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0301_0100,
+ end: 0x0301_0200,
+ },
+ data: Expression(EndianSlice::new(&[11, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A StartxEndx
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0301_0300,
+ end: 0x0301_0400,
+ },
+ data: Expression(EndianSlice::new(&[12, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A StartxLength
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0301_0500,
+ end: 0x0301_0600,
+ },
+ data: Expression(EndianSlice::new(&[13, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location list end.
+ 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_location_list_32() {
+ let start = Label::new();
+ let first = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // A location before the offset.
+ .mark(&start)
+ .L32(0x10000).L32(0x10100).L16(4).L32(1)
+ .mark(&first)
+ // A normal location.
+ .L32(0x10200).L32(0x10300).L16(4).L32(2)
+ // A base address selection followed by a normal location.
+ .L32(0xffff_ffff).L32(0x0200_0000)
+ .L32(0x10400).L32(0x10500).L16(4).L32(3)
+ // An empty location range followed by a normal location.
+ .L32(0x10600).L32(0x10600).L16(4).L32(4)
+ .L32(0x10800).L32(0x10900).L16(4).L32(5)
+ // A location range that starts at 0.
+ .L32(0).L32(1).L16(4).L32(6)
+ // A location range that ends at -1.
+ .L32(0xffff_ffff).L32(0x0000_0000)
+ .L32(0).L32(0xffff_ffff).L16(4).L32(7)
+ // A location list end.
+ .L32(0).L32(0)
+ // Some extra data.
+ .L32(0);
+
+ 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: 4,
+ };
+ let mut locations = loclists
+ .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ },
+ data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A base address selection followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ },
+ data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // An empty location range followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ },
+ data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)),
+ }))
+ );
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ },
+ data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that starts at 0.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ },
+ data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that ends at -1.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0000_0000,
+ end: 0xffff_ffff,
+ },
+ data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location list end.
+ 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_location_list_64() {
+ let start = Label::new();
+ let first = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // A location before the offset.
+ .mark(&start)
+ .L64(0x10000).L64(0x10100).L16(4).L32(1)
+ .mark(&first)
+ // A normal location.
+ .L64(0x10200).L64(0x10300).L16(4).L32(2)
+ // A base address selection followed by a normal location.
+ .L64(0xffff_ffff_ffff_ffff).L64(0x0200_0000)
+ .L64(0x10400).L64(0x10500).L16(4).L32(3)
+ // An empty location range followed by a normal location.
+ .L64(0x10600).L64(0x10600).L16(4).L32(4)
+ .L64(0x10800).L64(0x10900).L16(4).L32(5)
+ // A location range that starts at 0.
+ .L64(0).L64(1).L16(4).L32(6)
+ // A location range that ends at -1.
+ .L64(0xffff_ffff_ffff_ffff).L64(0x0000_0000)
+ .L64(0).L64(0xffff_ffff_ffff_ffff).L16(4).L32(7)
+ // A location list end.
+ .L64(0).L64(0)
+ // Some extra data.
+ .L64(0);
+
+ 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::Dwarf64,
+ version: 4,
+ address_size: 8,
+ };
+ let mut locations = loclists
+ .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ },
+ data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A base address selection followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ },
+ data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // An empty location range followed by a normal location.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ },
+ data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)),
+ }))
+ );
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ },
+ data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that starts at 0.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ },
+ data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location range that ends at -1.
+ assert_eq!(
+ locations.next(),
+ Ok(Some(LocationListEntry {
+ range: Range {
+ begin: 0x0,
+ end: 0xffff_ffff_ffff_ffff,
+ },
+ data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)),
+ }))
+ );
+
+ // A location list end.
+ 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(), Err(Error::InvalidLocationAddressRange));
+
+ // 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(), Err(Error::InvalidLocationAddressRange));
+
+ // 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 vec![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(&section, 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),
+ }))
+ );
+ }
+}
diff --git a/vendor/gimli/src/read/lookup.rs b/vendor/gimli/src/read/lookup.rs
new file mode 100644
index 000000000..1d082f24f
--- /dev/null
+++ b/vendor/gimli/src/read/lookup.rs
@@ -0,0 +1,202 @@
+use 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<R: Reader> {
+ /// 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<Option<Self::Entry>>;
+}
+
+#[derive(Clone, Debug)]
+pub struct DebugLookup<R, Parser>
+where
+ R: Reader,
+ Parser: LookupParser<R>,
+{
+ input_buffer: R,
+ phantom: PhantomData<Parser>,
+}
+
+impl<R, Parser> From<R> for DebugLookup<R, Parser>
+where
+ R: Reader,
+ Parser: LookupParser<R>,
+{
+ fn from(input_buffer: R) -> Self {
+ DebugLookup {
+ input_buffer,
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl<R, Parser> DebugLookup<R, Parser>
+where
+ R: Reader,
+ Parser: LookupParser<R>,
+{
+ pub fn items(&self) -> LookupEntryIter<R, Parser> {
+ LookupEntryIter {
+ current_set: None,
+ remaining_input: self.input_buffer.clone(),
+ }
+ }
+
+ pub fn reader(&self) -> &R {
+ &self.input_buffer
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct LookupEntryIter<R, Parser>
+where
+ R: Reader,
+ Parser: LookupParser<R>,
+{
+ current_set: Option<(R, Parser::Header)>, // Only none at the very beginning and end.
+ remaining_input: R,
+}
+
+impl<R, Parser> LookupEntryIter<R, Parser>
+where
+ R: Reader,
+ Parser: LookupParser<R>,
+{
+ /// 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<Option<Parser::Entry>> {
+ 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<T = usize> {
+ format: Format,
+ length: T,
+ version: u16,
+ unit_offset: DebugInfoOffset<T>,
+ unit_length: T,
+}
+
+pub trait PubStuffEntry<R: Reader> {
+ fn new(
+ die_offset: UnitOffset<R::Offset>,
+ name: R,
+ unit_header_offset: DebugInfoOffset<R::Offset>,
+ ) -> Self;
+}
+
+#[derive(Clone, Debug)]
+pub struct PubStuffParser<R, Entry>
+where
+ R: Reader,
+ Entry: PubStuffEntry<R>,
+{
+ // This struct is never instantiated.
+ phantom: PhantomData<(R, Entry)>,
+}
+
+impl<R, Entry> LookupParser<R> for PubStuffParser<R, Entry>
+where
+ R: Reader,
+ Entry: PubStuffEntry<R>,
+{
+ type Header = PubStuffHeader<R::Offset>;
+ 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<Option<Self::Entry>> {
+ 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,
+ )))
+ }
+ }
+}
diff --git a/vendor/gimli/src/read/mod.rs b/vendor/gimli/src/read/mod.rs
new file mode 100644
index 000000000..7291f3b96
--- /dev/null
+++ b/vendor/gimli/src/read/mod.rs
@@ -0,0 +1,821 @@
+//! 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<R, gimli::Error> { unimplemented!() };
+//! # let get_sup_file_section_reader = |name| -> Result<R, gimli::Error> { 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/examples/simple.rs)
+//!
+//! * [A `dwarfdump`
+//! clone](https://github.com/gimli-rs/gimli/blob/master/examples/dwarfdump.rs)
+//!
+//! * [An `addr2line` clone](https://github.com/gimli-rs/addr2line)
+//!
+//! * [`ddbug`](https://github.com/philipc/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/examples/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.
+//!
+//! * 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<T, E>`, 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<EndianSlice<LittleEndian>>)
+//! -> gimli::Result<u64>
+//! {
+//! // `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::*;
+
+#[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<T = usize>(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)]
+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,
+ /// 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,
+ /// 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),
+ /// 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,
+ /// An unknown DW_CFA_* instruction.
+ UnknownCallFrameInstruction(constants::DwCfa),
+ /// The end of an address range was before the beginning.
+ InvalidAddressRange,
+ /// The end offset of a loc list entry was before the beginning.
+ InvalidLocationAddressRange,
+ /// 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,
+ /// 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,
+}
+
+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::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::UnknownCallFrameInstruction(_) => "An unknown DW_CFA_* instructiion",
+ Error::InvalidAddressRange => {
+ "The end of an address range must not be before the beginning."
+ }
+ Error::InvalidLocationAddressRange => {
+ "The end offset of a location list entry must not be before the beginning."
+ }
+ 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.",
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl error::Error for Error {}
+
+#[cfg(feature = "std")]
+impl From<io::Error> for Error {
+ fn from(_: io::Error) -> Self {
+ Error::Io
+ }
+}
+
+/// The result of a parse.
+pub type Result<T> = result::Result<T, Error>;
+
+/// 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<R>: From<R> {
+ /// 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()
+ }
+
+ /// Try to load the section using the given loader function.
+ fn load<F, E>(f: F) -> core::result::Result<Self, E>
+ where
+ F: FnOnce(SectionId) -> core::result::Result<R, E>,
+ {
+ 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<Self>
+ 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<Register> {
+ 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) => assert!(true),
+ 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(_)) => assert!(true),
+ 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(_)) => assert!(true),
+ 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) => assert!(true),
+ otherwise => panic!("Unexpected result: {:?}", otherwise),
+ };
+ }
+}
diff --git a/vendor/gimli/src/read/op.rs b/vendor/gimli/src/read/op.rs
new file mode 100644
index 000000000..2ca6247bc
--- /dev/null
+++ b/vendor/gimli/src/read/op.rs
@@ -0,0 +1,4095 @@
+//! 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<T = usize> {
+ /// A CU-relative reference.
+ UnitRef(UnitOffset<T>),
+ /// A section-relative reference.
+ DebugInfoRef(DebugInfoOffset<T>),
+}
+
+/// 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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<Offset>,
+ /// 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<Offset>,
+ },
+ /// 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<Offset>,
+ },
+ /// 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<u64>,
+ },
+ /// 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<Offset>,
+ /// 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<Offset>,
+ },
+ /// 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<Offset>,
+ },
+ /// 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<Offset>,
+ },
+ /// 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<Offset>,
+ /// 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<Offset>,
+ },
+ /// 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<Offset>,
+ },
+ /// 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<R: Reader> {
+ Piece,
+ Incomplete,
+ Complete { location: Location<R> },
+ Waiting(EvaluationWaiting<R>, EvaluationResult<R>),
+}
+
+/// A single location of a piece of the result of a DWARF expression.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum Location<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<Offset>,
+ /// The byte offset into the value that the implicit pointer points to.
+ byte_offset: i64,
+ },
+}
+
+impl<R, Offset> Location<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Return true if the piece is empty.
+ pub fn is_empty(&self) -> bool {
+ match *self {
+ Location::Empty => true,
+ _ => false,
+ }
+ }
+}
+
+/// The description of a single piece of the result of a DWARF
+/// expression.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Piece<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<u64>,
+ /// 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<u64>,
+ /// Where this piece is to be found.
+ pub location: Location<R, Offset>,
+}
+
+// A helper function to handle branch offsets.
+fn compute_pc<R: Reader>(pc: &R, bytecode: &R, offset: i16) -> Result<R> {
+ 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<O: ReaderOffset>() -> UnitOffset<O> {
+ UnitOffset(O::from_u64(0).unwrap())
+}
+
+impl<R, Offset> Operation<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<Operation<R, Offset>> {
+ 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 = 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<R: Reader> {
+ Start(Option<u64>),
+ Ready,
+ Error(Error),
+ Complete,
+ Waiting(EvaluationWaiting<R>),
+}
+
+#[derive(Debug)]
+enum EvaluationWaiting<R: Reader> {
+ 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<R: Reader> {
+ /// 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<u64>,
+ /// The DIE of the base type or 0 to indicate the generic type
+ base_type: UnitOffset<R::Offset>,
+ },
+ /// 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<R::Offset>,
+ },
+ /// 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<R::Offset>),
+ /// 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<R>),
+ /// 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<R::Offset>),
+ /// 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<R::Offset>,
+ /// 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<R::Offset>),
+}
+
+/// The bytecode for a DWARF expression or location description.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Expression<R: Reader>(pub R);
+
+impl<R: Reader> Expression<R> {
+ /// 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<R> {
+ Evaluation::new(self.0, encoding)
+ }
+
+ /// Return an iterator for the operations in the expression.
+ pub fn operations(self, encoding: Encoding) -> OperationIter<R> {
+ OperationIter {
+ input: self.0,
+ encoding,
+ }
+ }
+}
+
+/// An iterator for the operations in an expression.
+#[derive(Debug, Clone, Copy)]
+pub struct OperationIter<R: Reader> {
+ input: R,
+ encoding: Encoding,
+}
+
+impl<R: Reader> OperationIter<R> {
+ /// Read the next operation in an expression.
+ pub fn next(&mut self) -> Result<Option<Operation<R>>> {
+ 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>) -> R::Offset {
+ self.input.offset_from(&expression.0)
+ }
+}
+
+/// 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<R: Reader> EvaluationStorage<R> for StoreOnStack {
+/// type Stack = [Value; 64];
+/// type ExpressionStack = [(R, R); 4];
+/// type Result = [Piece<R>; 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<R: Reader> {
+ /// The storage used for the evaluation stack.
+ type Stack: ArrayLike<Item = Value>;
+ /// The storage used for the expression stack.
+ type ExpressionStack: ArrayLike<Item = (R, R)>;
+ /// The storage used for the results.
+ type Result: ArrayLike<Item = Piece<R>>;
+}
+
+#[cfg(feature = "read")]
+impl<R: Reader> EvaluationStorage<R> for StoreOnHeap {
+ type Stack = Vec<Value>;
+ type ExpressionStack = Vec<(R, R)>;
+ type Result = Vec<Piece<R>>;
+}
+
+/// 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::{EndianSlice, Evaluation, EvaluationResult, Format, LittleEndian, Value};
+/// # let bytecode = EndianSlice::new(&[], LittleEndian);
+/// # let encoding = unimplemented!();
+/// # let get_register_value = |_, _| 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<R: Reader, S: EvaluationStorage<R> = StoreOnHeap> {
+ bytecode: R,
+ encoding: Encoding,
+ object_address: Option<u64>,
+ max_iterations: Option<u32>,
+ iteration: u32,
+ state: EvaluationState<R>,
+
+ // 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<S::Stack>,
+
+ // 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<S::ExpressionStack>,
+
+ result: ArrayVec<S::Result>,
+}
+
+#[cfg(feature = "read")]
+impl<R: Reader> Evaluation<R> {
+ /// 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<Piece<R>> {
+ match self.state {
+ EvaluationState::Complete => self.result.into_vec(),
+ _ => {
+ panic!("Called `Evaluation::result` on an `Evaluation` that has not been completed")
+ }
+ }
+ }
+}
+
+impl<R: Reader, S: EvaluationStorage<R>> Evaluation<R, S> {
+ /// 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,
+ 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<Value> {
+ 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)
+ }
+
+ #[allow(clippy::cyclomatic_complexity)]
+ fn evaluate_one_operation(&mut self) -> Result<OperationEvaluationResult<R>> {
+ let operation = Operation::parse(&mut self.pc, self.encoding)?;
+
+ match operation {
+ Operation::Deref {
+ base_type,
+ size,
+ space,
+ } => {
+ 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 of this `Evaluation`.
+ ///
+ /// # Panics
+ /// Panics if this `Evaluation` has not been driven to completion.
+ pub fn as_result(&self) -> &[Piece<R>] {
+ match self.state {
+ EvaluationState::Complete => &self.result,
+ _ => {
+ panic!("Called `Evaluation::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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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<EvaluationResult<R>> {
+ 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()?;
+ 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 core::usize;
+ 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<EndianSlice<'input, LittleEndian>>,
+ 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<F>(
+ input: F,
+ expect: &Operation<EndianSlice<LittleEndian>>,
+ 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(),
+ );
+ }
+ }
+
+ #[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<u8> {
+ let mut result = Vec::new();
+
+ struct Marker(Option<usize>, Vec<usize>);
+
+ let mut markers = Vec::new();
+ for _ in 0..256 {
+ markers.push(Marker(None, Vec::new()));
+ }
+
+ fn write(stack: &mut Vec<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<u8>, 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
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ fn check_eval_with_args<F>(
+ program: &[AssemblerEntry],
+ expect: Result<&[Piece<EndianSlice<LittleEndian>>]>,
+ encoding: Encoding,
+ object_address: Option<u64>,
+ initial_value: Option<u64>,
+ max_iterations: Option<u32>,
+ f: F,
+ ) where
+ for<'a> F: Fn(
+ &mut Evaluation<EndianSlice<'a, LittleEndian>>,
+ EvaluationResult<EndianSlice<'a, LittleEndian>>,
+ ) -> Result<EvaluationResult<EndianSlice<'a, LittleEndian>>>,
+ {
+ 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<EndianSlice<LittleEndian>>]>,
+ 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)
+ },
+ );
+ }
+
+ #[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)
+ },
+ );
+ }
+ }
+}
diff --git a/vendor/gimli/src/read/pubnames.rs b/vendor/gimli/src/read/pubnames.rs
new file mode 100644
index 000000000..f05861f70
--- /dev/null
+++ b/vendor/gimli/src/read/pubnames.rs
@@ -0,0 +1,141 @@
+use 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<R: Reader> {
+ unit_header_offset: DebugInfoOffset<R::Offset>,
+ die_offset: UnitOffset<R::Offset>,
+ name: R,
+}
+
+impl<R: Reader> PubNamesEntry<R> {
+ /// 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<R::Offset> {
+ 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<R::Offset> {
+ self.die_offset
+ }
+}
+
+impl<R: Reader> PubStuffEntry<R> for PubNamesEntry<R> {
+ fn new(
+ die_offset: UnitOffset<R::Offset>,
+ name: R,
+ unit_header_offset: DebugInfoOffset<R::Offset>,
+ ) -> 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<R: Reader>(DebugLookup<R, PubStuffParser<R, PubNamesEntry<R>>>);
+
+impl<'input, Endian> DebugPubNames<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugPubNames<R> {
+ /// 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<R> {
+ PubNamesEntryIter(self.0.items())
+ }
+}
+
+impl<R: Reader> Section<R> for DebugPubNames<R> {
+ fn id() -> SectionId {
+ SectionId::DebugPubNames
+ }
+
+ fn reader(&self) -> &R {
+ self.0.reader()
+ }
+}
+
+impl<R: Reader> From<R> for DebugPubNames<R> {
+ 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<R: Reader>(LookupEntryIter<R, PubStuffParser<R, PubNamesEntry<R>>>);
+
+impl<R: Reader> PubNamesEntryIter<R> {
+ /// 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<Option<PubNamesEntry<R>>> {
+ self.0.next()
+ }
+}
+
+#[cfg(feature = "fallible-iterator")]
+impl<R: Reader> fallible_iterator::FallibleIterator for PubNamesEntryIter<R> {
+ type Item = PubNamesEntry<R>;
+ type Error = crate::read::Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
+ self.0.next()
+ }
+}
diff --git a/vendor/gimli/src/read/pubtypes.rs b/vendor/gimli/src/read/pubtypes.rs
new file mode 100644
index 000000000..0226e84d4
--- /dev/null
+++ b/vendor/gimli/src/read/pubtypes.rs
@@ -0,0 +1,141 @@
+use 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<R: Reader> {
+ unit_header_offset: DebugInfoOffset<R::Offset>,
+ die_offset: UnitOffset<R::Offset>,
+ name: R,
+}
+
+impl<R: Reader> PubTypesEntry<R> {
+ /// 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<R::Offset> {
+ 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<R::Offset> {
+ self.die_offset
+ }
+}
+
+impl<R: Reader> PubStuffEntry<R> for PubTypesEntry<R> {
+ fn new(
+ die_offset: UnitOffset<R::Offset>,
+ name: R,
+ unit_header_offset: DebugInfoOffset<R::Offset>,
+ ) -> 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<R: Reader>(DebugLookup<R, PubStuffParser<R, PubTypesEntry<R>>>);
+
+impl<'input, Endian> DebugPubTypes<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugPubTypes<R> {
+ /// 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<R> {
+ PubTypesEntryIter(self.0.items())
+ }
+}
+
+impl<R: Reader> Section<R> for DebugPubTypes<R> {
+ fn id() -> SectionId {
+ SectionId::DebugPubTypes
+ }
+
+ fn reader(&self) -> &R {
+ self.0.reader()
+ }
+}
+
+impl<R: Reader> From<R> for DebugPubTypes<R> {
+ 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<R: Reader>(LookupEntryIter<R, PubStuffParser<R, PubTypesEntry<R>>>);
+
+impl<R: Reader> PubTypesEntryIter<R> {
+ /// 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<Option<PubTypesEntry<R>>> {
+ self.0.next()
+ }
+}
+
+#[cfg(feature = "fallible-iterator")]
+impl<R: Reader> fallible_iterator::FallibleIterator for PubTypesEntryIter<R> {
+ type Item = PubTypesEntry<R>;
+ type Error = crate::read::Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
+ self.0.next()
+ }
+}
diff --git a/vendor/gimli/src/read/reader.rs b/vendor/gimli/src/read/reader.rs
new file mode 100644
index 000000000..1bb748bb8
--- /dev/null
+++ b/vendor/gimli/src/read/reader.rs
@@ -0,0 +1,502 @@
+#[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<Output = Self> + AddAssign + Sub<Output = Self>
+{
+ /// 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<Self>;
+
+ /// 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<Self>;
+}
+
+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<Self> {
+ 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> {
+ 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<Self> {
+ 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> {
+ 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<Self> {
+ 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> {
+ self.checked_sub(other)
+ }
+}
+
+#[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<T>`](./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<Self::Offset>;
+
+ /// Find the index of the first occurence of the given byte.
+ /// The offset of the reader is not changed.
+ fn find(&self, byte: u8) -> Result<Self::Offset>;
+
+ /// 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<Self>;
+
+ /// 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<Cow<[u8]>>;
+
+ /// 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<Cow<str>>;
+
+ /// 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<Cow<str>>;
+
+ /// 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<A>(&mut self) -> Result<A>
+ where
+ A: Sized + Default + AsMut<[u8]>,
+ {
+ let mut val = Default::default();
+ self.read_slice(<A as AsMut<[u8]>>::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<u8> {
+ let a: [u8; 1] = self.read_u8_array()?;
+ Ok(a[0])
+ }
+
+ /// Read an i8.
+ #[inline]
+ fn read_i8(&mut self) -> Result<i8> {
+ let a: [u8; 1] = self.read_u8_array()?;
+ Ok(a[0] as i8)
+ }
+
+ /// Read a u16.
+ #[inline]
+ fn read_u16(&mut self) -> Result<u16> {
+ let a: [u8; 2] = self.read_u8_array()?;
+ Ok(self.endian().read_u16(&a))
+ }
+
+ /// Read an i16.
+ #[inline]
+ fn read_i16(&mut self) -> Result<i16> {
+ let a: [u8; 2] = self.read_u8_array()?;
+ Ok(self.endian().read_i16(&a))
+ }
+
+ /// Read a u32.
+ #[inline]
+ fn read_u32(&mut self) -> Result<u32> {
+ let a: [u8; 4] = self.read_u8_array()?;
+ Ok(self.endian().read_u32(&a))
+ }
+
+ /// Read an i32.
+ #[inline]
+ fn read_i32(&mut self) -> Result<i32> {
+ let a: [u8; 4] = self.read_u8_array()?;
+ Ok(self.endian().read_i32(&a))
+ }
+
+ /// Read a u64.
+ #[inline]
+ fn read_u64(&mut self) -> Result<u64> {
+ let a: [u8; 8] = self.read_u8_array()?;
+ Ok(self.endian().read_u64(&a))
+ }
+
+ /// Read an i64.
+ #[inline]
+ fn read_i64(&mut self) -> Result<i64> {
+ let a: [u8; 8] = self.read_u8_array()?;
+ Ok(self.endian().read_i64(&a))
+ }
+
+ /// Read a f32.
+ #[inline]
+ fn read_f32(&mut self) -> Result<f32> {
+ let a: [u8; 4] = self.read_u8_array()?;
+ Ok(self.endian().read_f32(&a))
+ }
+
+ /// Read a f64.
+ #[inline]
+ fn read_f64(&mut self) -> Result<f64> {
+ 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<u64> {
+ 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<Self> {
+ 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<u64> {
+ leb128::read::unsigned(self)
+ }
+
+ /// Read an unsigned LEB128 encoded u32.
+ fn read_uleb128_u32(&mut self) -> Result<u32> {
+ leb128::read::unsigned(self)?
+ .try_into()
+ .map_err(|_| Error::BadUnsignedLeb128)
+ }
+
+ /// Read an unsigned LEB128 encoded u16.
+ fn read_uleb128_u16(&mut self) -> Result<u16> {
+ leb128::read::u16(self)
+ }
+
+ /// Read a signed LEB128 encoded integer.
+ fn read_sleb128(&mut self) -> Result<i64> {
+ 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 an address-sized integer, and return it as a `u64`.
+ fn read_address(&mut self, address_size: u8) -> Result<u64> {
+ 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<Self::Offset> {
+ 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::Offset> {
+ 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::Offset> {
+ 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<Self::Offset> {
+ 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)
+ }
+}
diff --git a/vendor/gimli/src/read/rnglists.rs b/vendor/gimli/src/read/rnglists.rs
new file mode 100644
index 000000000..dd68083ac
--- /dev/null
+++ b/vendor/gimli/src/read/rnglists.rs
@@ -0,0 +1,1354 @@
+use 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, ReaderOffset, ReaderOffsetId,
+ Result, Section,
+};
+
+/// The raw contents of the `.debug_ranges` section.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct DebugRanges<R> {
+ pub(crate) section: R,
+}
+
+impl<'input, Endian> DebugRanges<EndianSlice<'input, Endian>>
+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 OSX, 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<R> Section<R> for DebugRanges<R> {
+ fn id() -> SectionId {
+ SectionId::DebugRanges
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugRanges<R> {
+ 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<R> {
+ section: R,
+}
+
+impl<'input, Endian> DebugRngLists<EndianSlice<'input, Endian>>
+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 OSX, 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<R> Section<R> for DebugRngLists<R> {
+ fn id() -> SectionId {
+ SectionId::DebugRngLists
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugRngLists<R> {
+ fn from(section: R) -> Self {
+ DebugRngLists { section }
+ }
+}
+
+#[allow(unused)]
+pub(crate) type RngListsHeader = ListsHeader;
+
+impl<Offset> DebugRngListsBase<Offset>
+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<Offset> {
+ 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<R> {
+ debug_ranges: DebugRanges<R>,
+ debug_rnglists: DebugRngLists<R>,
+}
+
+impl<R> RangeLists<R> {
+ /// Construct a new `RangeLists` instance from the data in the `.debug_ranges` and
+ /// `.debug_rnglists` sections.
+ pub fn new(debug_ranges: DebugRanges<R>, debug_rnglists: DebugRngLists<R>) -> RangeLists<R> {
+ RangeLists {
+ debug_ranges,
+ debug_rnglists,
+ }
+ }
+
+ /// Return the `.debug_ranges` section.
+ pub fn debug_ranges(&self) -> &DebugRanges<R> {
+ &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<R>) {
+ self.debug_ranges = debug_ranges;
+ }
+
+ /// Return the `.debug_rnglists` section.
+ pub fn debug_rnglists(&self) -> &DebugRngLists<R> {
+ &self.debug_rnglists
+ }
+}
+
+impl<T> RangeLists<T> {
+ /// Create a `RangeLists` that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::RangeLists<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> RangeLists<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ RangeLists {
+ debug_ranges: borrow(&self.debug_ranges.section).into(),
+ debug_rnglists: borrow(&self.debug_rnglists.section).into(),
+ }
+ }
+}
+
+impl<R: Reader> RangeLists<R> {
+ /// 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<R::Offset>,
+ unit_encoding: Encoding,
+ base_address: u64,
+ debug_addr: &DebugAddr<R>,
+ debug_addr_base: DebugAddrBase<R::Offset>,
+ ) -> Result<RngListIter<R>> {
+ 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<R::Offset>,
+ unit_encoding: Encoding,
+ ) -> Result<RawRngListIter<R>> {
+ 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<R::Offset>,
+ index: DebugRngListsIndex<R::Offset>,
+ ) -> Result<RangeListsOffset<R::Offset>> {
+ 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<R: Reader> {
+ input: R,
+ encoding: Encoding,
+ format: RangeListsFormat,
+}
+
+/// A raw entry in .debug_rnglists
+#[derive(Clone, Debug)]
+pub enum RawRngListEntry<T> {
+ /// 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<T>,
+ },
+ /// DW_RLE_startx_endx
+ StartxEndx {
+ /// start of range
+ begin: DebugAddrIndex<T>,
+ /// end of range
+ end: DebugAddrIndex<T>,
+ },
+ /// DW_RLE_startx_length
+ StartxLength {
+ /// start of range
+ begin: DebugAddrIndex<T>,
+ /// 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<T: ReaderOffset> RawRngListEntry<T> {
+ /// Parse a range entry from `.debug_rnglists`
+ fn parse<R: Reader<Offset = T>>(
+ input: &mut R,
+ encoding: Encoding,
+ format: RangeListsFormat,
+ ) -> Result<Option<Self>> {
+ match format {
+ RangeListsFormat::Bare => {
+ let range = RawRange::parse(input, encoding.address_size)?;
+ return Ok(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 => Ok(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()?,
+ }),
+ _ => {
+ return Err(Error::InvalidAddressRange);
+ }
+ }),
+ }
+ }
+}
+
+impl<R: Reader> RawRngListIter<R> {
+ /// Construct a `RawRngListIter`.
+ fn new(input: R, encoding: Encoding, format: RangeListsFormat) -> RawRngListIter<R> {
+ RawRngListIter {
+ input,
+ encoding,
+ format,
+ }
+ }
+
+ /// Advance the iterator to the next range.
+ pub fn next(&mut self) -> Result<Option<RawRngListEntry<R::Offset>>> {
+ 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<R: Reader> fallible_iterator::FallibleIterator for RawRngListIter<R> {
+ type Item = RawRngListEntry<R::Offset>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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<R: Reader> {
+ raw: RawRngListIter<R>,
+ base_address: u64,
+ debug_addr: DebugAddr<R>,
+ debug_addr_base: DebugAddrBase<R::Offset>,
+}
+
+impl<R: Reader> RngListIter<R> {
+ /// Construct a `RngListIter`.
+ fn new(
+ raw: RawRngListIter<R>,
+ base_address: u64,
+ debug_addr: DebugAddr<R>,
+ debug_addr_base: DebugAddrBase<R::Offset>,
+ ) -> RngListIter<R> {
+ RngListIter {
+ raw,
+ base_address,
+ debug_addr,
+ debug_addr_base,
+ }
+ }
+
+ #[inline]
+ fn get_address(&self, index: DebugAddrIndex<R::Offset>) -> Result<u64> {
+ 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<Option<Range>> {
+ loop {
+ let raw_range = match self.raw.next()? {
+ Some(range) => range,
+ None => return Ok(None),
+ };
+
+ let range = match raw_range {
+ RawRngListEntry::BaseAddress { addr } => {
+ self.base_address = addr;
+ continue;
+ }
+ RawRngListEntry::BaseAddressx { addr } => {
+ self.base_address = self.get_address(addr)?;
+ continue;
+ }
+ 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 + length;
+ Range { begin, end }
+ }
+ RawRngListEntry::AddressOrOffsetPair { begin, end }
+ | RawRngListEntry::OffsetPair { begin, end } => {
+ 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 } => Range {
+ begin,
+ end: begin + length,
+ },
+ };
+
+ if range.begin > range.end {
+ self.raw.input.empty();
+ return Err(Error::InvalidAddressRange);
+ }
+
+ return Ok(Some(range));
+ }
+ }
+}
+
+#[cfg(feature = "fallible-iterator")]
+impl<R: Reader> fallible_iterator::FallibleIterator for RngListIter<R> {
+ type Item = Range;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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.
+ ///
+ /// This will only occur for raw ranges.
+ #[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. This will only occur for raw ranges.
+ #[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`.
+ #[doc(hidden)]
+ #[inline]
+ pub fn parse<R: Reader>(input: &mut R, address_size: u8) -> Result<RawRange> {
+ 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, 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) {
+ let mask = !0 >> (64 - address_size * 8);
+ self.begin = base_address.wrapping_add(self.begin) & mask;
+ self.end = base_address.wrapping_add(self.end) & mask;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::common::Format;
+ use crate::endianity::LittleEndian;
+ use crate::test_util::GimliSectionMethods;
+ use test_assembler::{Endian, Label, LabelMaker, Section};
+
+ #[test]
+ fn test_rnglists_32() {
+ let encoding = Encoding {
+ format: Format::Dwarf32,
+ version: 5,
+ address_size: 4,
+ };
+ let section = Section::with_endian(Endian::Little)
+ .L32(0x0300_0000)
+ .L32(0x0301_0300)
+ .L32(0x0301_0400)
+ .L32(0x0301_0500);
+ let buf = section.get_contents().unwrap();
+ let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let start = Label::new();
+ let first = Label::new();
+ let size = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // Header
+ .mark(&start)
+ .L32(&size)
+ .L16(encoding.version)
+ .L8(encoding.address_size)
+ .L8(0)
+ .L32(0)
+ .mark(&first)
+ // OffsetPair
+ .L8(4).uleb(0x10200).uleb(0x10300)
+ // A base address selection followed by an OffsetPair.
+ .L8(5).L32(0x0200_0000)
+ .L8(4).uleb(0x10400).uleb(0x10500)
+ // An empty OffsetPair followed by a normal OffsetPair.
+ .L8(4).uleb(0x10600).uleb(0x10600)
+ .L8(4).uleb(0x10800).uleb(0x10900)
+ // A StartEnd
+ .L8(6).L32(0x201_0a00).L32(0x201_0b00)
+ // A StartLength
+ .L8(7).L32(0x201_0c00).uleb(0x100)
+ // An OffsetPair that starts at 0.
+ .L8(4).uleb(0).uleb(1)
+ // An OffsetPair that starts and ends at 0.
+ .L8(4).uleb(0).uleb(0)
+ // An OffsetPair that ends at -1.
+ .L8(5).L32(0)
+ .L8(4).uleb(0).uleb(0xffff_ffff)
+ // A BaseAddressx + OffsetPair
+ .L8(1).uleb(0)
+ .L8(4).uleb(0x10100).uleb(0x10200)
+ // A StartxEndx
+ .L8(2).uleb(1).uleb(2)
+ // A StartxLength
+ .L8(3).uleb(3).uleb(0x100)
+ // A range end.
+ .L8(0)
+ // Some extra data.
+ .L32(0xffff_ffff);
+ size.set_const((&section.here() - &start - 4) as u64);
+
+ 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 offset = RangeListsOffset((&first - &start) as usize);
+ let mut ranges = rnglists
+ .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ }))
+ );
+
+ // A base address selection followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ }))
+ );
+
+ // An empty range followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ }))
+ );
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ }))
+ );
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0a00,
+ end: 0x0201_0b00,
+ }))
+ );
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0c00,
+ end: 0x0201_0d00,
+ }))
+ );
+
+ // A range that starts at 0.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ }))
+ );
+
+ // A range that starts and ends at 0.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0000,
+ }))
+ );
+
+ // A range that ends at -1.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0000_0000,
+ end: 0xffff_ffff,
+ }))
+ );
+
+ // A BaseAddressx + OffsetPair
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0301_0100,
+ end: 0x0301_0200,
+ }))
+ );
+
+ // A StartxEndx
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0301_0300,
+ end: 0x0301_0400,
+ }))
+ );
+
+ // A StartxLength
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0301_0500,
+ end: 0x0301_0600,
+ }))
+ );
+
+ // A range end.
+ 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_rnglists_64() {
+ let encoding = Encoding {
+ format: Format::Dwarf64,
+ version: 5,
+ address_size: 8,
+ };
+ let section = Section::with_endian(Endian::Little)
+ .L64(0x0300_0000)
+ .L64(0x0301_0300)
+ .L64(0x0301_0400)
+ .L64(0x0301_0500);
+ let buf = section.get_contents().unwrap();
+ let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let start = Label::new();
+ let first = Label::new();
+ let size = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // Header
+ .mark(&start)
+ .L32(0xffff_ffff)
+ .L64(&size)
+ .L16(encoding.version)
+ .L8(encoding.address_size)
+ .L8(0)
+ .L32(0)
+ .mark(&first)
+ // OffsetPair
+ .L8(4).uleb(0x10200).uleb(0x10300)
+ // A base address selection followed by an OffsetPair.
+ .L8(5).L64(0x0200_0000)
+ .L8(4).uleb(0x10400).uleb(0x10500)
+ // An empty OffsetPair followed by a normal OffsetPair.
+ .L8(4).uleb(0x10600).uleb(0x10600)
+ .L8(4).uleb(0x10800).uleb(0x10900)
+ // A StartEnd
+ .L8(6).L64(0x201_0a00).L64(0x201_0b00)
+ // A StartLength
+ .L8(7).L64(0x201_0c00).uleb(0x100)
+ // An OffsetPair that starts at 0.
+ .L8(4).uleb(0).uleb(1)
+ // An OffsetPair that starts and ends at 0.
+ .L8(4).uleb(0).uleb(0)
+ // An OffsetPair that ends at -1.
+ .L8(5).L64(0)
+ .L8(4).uleb(0).uleb(0xffff_ffff)
+ // A BaseAddressx + OffsetPair
+ .L8(1).uleb(0)
+ .L8(4).uleb(0x10100).uleb(0x10200)
+ // A StartxEndx
+ .L8(2).uleb(1).uleb(2)
+ // A StartxLength
+ .L8(3).uleb(3).uleb(0x100)
+ // A range end.
+ .L8(0)
+ // Some extra data.
+ .L32(0xffff_ffff);
+ size.set_const((&section.here() - &start - 12) as u64);
+
+ 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 offset = RangeListsOffset((&first - &start) as usize);
+ let mut ranges = rnglists
+ .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ }))
+ );
+
+ // A base address selection followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ }))
+ );
+
+ // An empty range followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ }))
+ );
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ }))
+ );
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0a00,
+ end: 0x0201_0b00,
+ }))
+ );
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0c00,
+ end: 0x0201_0d00,
+ }))
+ );
+
+ // A range that starts at 0.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ }))
+ );
+
+ // A range that starts and ends at 0.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0000,
+ }))
+ );
+
+ // A range that ends at -1.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0000_0000,
+ end: 0xffff_ffff,
+ }))
+ );
+
+ // A BaseAddressx + OffsetPair
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0301_0100,
+ end: 0x0301_0200,
+ }))
+ );
+
+ // A StartxEndx
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0301_0300,
+ end: 0x0301_0400,
+ }))
+ );
+
+ // A StartxLength
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0301_0500,
+ end: 0x0301_0600,
+ }))
+ );
+
+ // A range end.
+ 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_32() {
+ let start = Label::new();
+ let first = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // A range before the offset.
+ .mark(&start)
+ .L32(0x10000).L32(0x10100)
+ .mark(&first)
+ // A normal range.
+ .L32(0x10200).L32(0x10300)
+ // A base address selection followed by a normal range.
+ .L32(0xffff_ffff).L32(0x0200_0000)
+ .L32(0x10400).L32(0x10500)
+ // An empty range followed by a normal range.
+ .L32(0x10600).L32(0x10600)
+ .L32(0x10800).L32(0x10900)
+ // A range that starts at 0.
+ .L32(0).L32(1)
+ // A range that ends at -1.
+ .L32(0xffff_ffff).L32(0x0000_0000)
+ .L32(0).L32(0xffff_ffff)
+ // A range end.
+ .L32(0).L32(0)
+ // Some extra data.
+ .L32(0);
+
+ 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: 4,
+ };
+ let mut ranges = rnglists
+ .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ }))
+ );
+
+ // A base address selection followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ }))
+ );
+
+ // An empty range followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ }))
+ );
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ }))
+ );
+
+ // A range that starts at 0.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ }))
+ );
+
+ // A range that ends at -1.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0000_0000,
+ end: 0xffff_ffff,
+ }))
+ );
+
+ // A range end.
+ 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_64() {
+ let start = Label::new();
+ let first = Label::new();
+ #[rustfmt::skip]
+ let section = Section::with_endian(Endian::Little)
+ // A range before the offset.
+ .mark(&start)
+ .L64(0x10000).L64(0x10100)
+ .mark(&first)
+ // A normal range.
+ .L64(0x10200).L64(0x10300)
+ // A base address selection followed by a normal range.
+ .L64(0xffff_ffff_ffff_ffff).L64(0x0200_0000)
+ .L64(0x10400).L64(0x10500)
+ // An empty range followed by a normal range.
+ .L64(0x10600).L64(0x10600)
+ .L64(0x10800).L64(0x10900)
+ // A range that starts at 0.
+ .L64(0).L64(1)
+ // A range that ends at -1.
+ .L64(0xffff_ffff_ffff_ffff).L64(0x0000_0000)
+ .L64(0).L64(0xffff_ffff_ffff_ffff)
+ // A range end.
+ .L64(0).L64(0)
+ // Some extra data.
+ .L64(0);
+
+ 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::Dwarf64,
+ version: 4,
+ address_size: 8,
+ };
+ let mut ranges = rnglists
+ .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base)
+ .unwrap();
+
+ // A normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0101_0200,
+ end: 0x0101_0300,
+ }))
+ );
+
+ // A base address selection followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0400,
+ end: 0x0201_0500,
+ }))
+ );
+
+ // An empty range followed by a normal range.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0600,
+ end: 0x0201_0600,
+ }))
+ );
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0201_0800,
+ end: 0x0201_0900,
+ }))
+ );
+
+ // A range that starts at 0.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0200_0000,
+ end: 0x0200_0001,
+ }))
+ );
+
+ // A range that ends at -1.
+ assert_eq!(
+ ranges.next(),
+ Ok(Some(Range {
+ begin: 0x0,
+ end: 0xffff_ffff_ffff_ffff,
+ }))
+ );
+
+ // A range end.
+ 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(), Err(Error::InvalidAddressRange));
+
+ // 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(), Err(Error::InvalidAddressRange));
+
+ // 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 vec![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(&section, 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))
+ );
+ }
+ }
+}
diff --git a/vendor/gimli/src/read/str.rs b/vendor/gimli/src/read/str.rs
new file mode 100644
index 000000000..dce8016af
--- /dev/null
+++ b/vendor/gimli/src/read/str.rs
@@ -0,0 +1,321 @@
+use 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<R> {
+ debug_str_section: R,
+}
+
+impl<'input, Endian> DebugStr<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugStr<R> {
+ /// 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<R::Offset>) -> Result<R> {
+ let input = &mut self.debug_str_section.clone();
+ input.skip(offset.0)?;
+ input.read_null_terminated_slice()
+ }
+}
+
+impl<T> DebugStr<T> {
+ /// Create a `DebugStr` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugStr<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStr<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.debug_str_section).into()
+ }
+}
+
+impl<R> Section<R> for DebugStr<R> {
+ fn id() -> SectionId {
+ SectionId::DebugStr
+ }
+
+ fn reader(&self) -> &R {
+ &self.debug_str_section
+ }
+}
+
+impl<R> From<R> for DebugStr<R> {
+ 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<R> {
+ section: R,
+}
+
+impl<R: Reader> DebugStrOffsets<R> {
+ // 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<R::Offset>,
+ index: DebugStrOffsetsIndex<R::Offset>,
+ ) -> Result<DebugStrOffset<R::Offset>> {
+ 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<T> DebugStrOffsets<T> {
+ /// Create a `DebugStrOffsets` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugStrOffsets<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStrOffsets<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.section).into()
+ }
+}
+
+impl<R> Section<R> for DebugStrOffsets<R> {
+ fn id() -> SectionId {
+ SectionId::DebugStrOffsets
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugStrOffsets<R> {
+ fn from(section: R) -> Self {
+ DebugStrOffsets { section }
+ }
+}
+
+impl<Offset> DebugStrOffsetsBase<Offset>
+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<Offset> {
+ 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<R> {
+ section: R,
+}
+
+impl<'input, Endian> DebugLineStr<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugLineStr<R> {
+ /// Lookup a string from the `.debug_line_str` section by DebugLineStrOffset.
+ pub fn get_str(&self, offset: DebugLineStrOffset<R::Offset>) -> Result<R> {
+ let input = &mut self.section.clone();
+ input.skip(offset.0)?;
+ input.read_null_terminated_slice()
+ }
+}
+
+impl<T> DebugLineStr<T> {
+ /// Create a `DebugLineStr` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugLineStr<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLineStr<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.section).into()
+ }
+}
+
+impl<R> Section<R> for DebugLineStr<R> {
+ fn id() -> SectionId {
+ SectionId::DebugLineStr
+ }
+
+ fn reader(&self) -> &R {
+ &self.section
+ }
+}
+
+impl<R> From<R> for DebugLineStr<R> {
+ 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 vec![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(&section, 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))
+ );
+ }
+ }
+}
diff --git a/vendor/gimli/src/read/unit.rs b/vendor/gimli/src/read/unit.rs
new file mode 100644
index 000000000..4766b2e84
--- /dev/null
+++ b/vendor/gimli/src/read/unit.rs
@@ -0,0 +1,6146 @@
+//! Functions for parsing DWARF `.debug_info` and `.debug_types` sections.
+
+use core::cell::Cell;
+use core::ops::{Range, RangeFrom, RangeTo};
+use core::{u16, u8};
+
+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<T: ReaderOffset> DebugTypesOffset<T> {
+ /// 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<R>(&self, unit: &UnitHeader<R>) -> Option<UnitOffset<T>>
+ where
+ R: Reader<Offset = T>,
+ {
+ 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<T: ReaderOffset> DebugInfoOffset<T> {
+ /// 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<R>(&self, unit: &UnitHeader<R>) -> Option<UnitOffset<T>>
+ where
+ R: Reader<Offset = T>,
+ {
+ 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<T: ReaderOffset> UnitOffset<T> {
+ /// 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<R>(&self, unit: &UnitHeader<R>) -> Option<DebugInfoOffset<T>>
+ where
+ R: Reader<Offset = T>,
+ {
+ 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<R>(&self, unit: &UnitHeader<R>) -> Option<DebugTypesOffset<T>>
+ where
+ R: Reader<Offset = T>,
+ {
+ 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<R> {
+ debug_info_section: R,
+}
+
+impl<'input, Endian> DebugInfo<EndianSlice<'input, Endian>>
+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 OSX, 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<R: Reader> DebugInfo<R> {
+ /// 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<R> {
+ 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<R::Offset>) -> Result<UnitHeader<R>> {
+ let input = &mut self.debug_info_section.clone();
+ input.skip(offset.0)?;
+ parse_unit_header(input, offset.into())
+ }
+}
+
+impl<T> DebugInfo<T> {
+ /// Create a `DebugInfo` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugInfo<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugInfo<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.debug_info_section).into()
+ }
+}
+
+impl<R> Section<R> for DebugInfo<R> {
+ fn id() -> SectionId {
+ SectionId::DebugInfo
+ }
+
+ fn reader(&self) -> &R {
+ &self.debug_info_section
+ }
+}
+
+impl<R> From<R> for DebugInfo<R> {
+ 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<R: Reader> {
+ input: R,
+ offset: DebugInfoOffset<R::Offset>,
+}
+
+impl<R: Reader> DebugInfoUnitHeadersIter<R> {
+ /// Advance the iterator to the next unit header.
+ pub fn next(&mut self) -> Result<Option<UnitHeader<R>>> {
+ 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<R: Reader> fallible_iterator::FallibleIterator for DebugInfoUnitHeadersIter<R> {
+ type Item = UnitHeader<R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
+ DebugInfoUnitHeadersIter::next(self)
+ }
+}
+
+/// Parse the unit type from the unit header.
+fn parse_unit_type<R: Reader>(input: &mut R) -> Result<constants::DwUt> {
+ let val = input.read_u8()?;
+ Ok(constants::DwUt(val))
+}
+
+/// Parse the `debug_abbrev_offset` in the compilation unit header.
+fn parse_debug_abbrev_offset<R: Reader>(
+ input: &mut R,
+ format: Format,
+) -> Result<DebugAbbrevOffset<R::Offset>> {
+ input.read_offset(format).map(DebugAbbrevOffset)
+}
+
+/// Parse the `debug_info_offset` in the arange header.
+pub(crate) fn parse_debug_info_offset<R: Reader>(
+ input: &mut R,
+ format: Format,
+) -> Result<DebugInfoOffset<R::Offset>> {
+ 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<Offset>
+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<Offset>,
+ },
+ /// 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<Offset>,
+ },
+}
+
+impl<Offset> UnitType<Offset>
+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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ encoding: Encoding,
+ unit_length: Offset,
+ unit_type: UnitType<Offset>,
+ debug_abbrev_offset: DebugAbbrevOffset<Offset>,
+ unit_offset: UnitSectionOffset<Offset>,
+ entries_buf: R,
+}
+
+/// Static methods.
+impl<R, Offset> UnitHeader<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Construct a new `UnitHeader`.
+ pub fn new(
+ encoding: Encoding,
+ unit_length: Offset,
+ unit_type: UnitType<Offset>,
+ debug_abbrev_offset: DebugAbbrevOffset<Offset>,
+ unit_offset: UnitSectionOffset<Offset>,
+ entries_buf: R,
+ ) -> Self {
+ UnitHeader {
+ encoding,
+ unit_length,
+ unit_type,
+ debug_abbrev_offset,
+ unit_offset,
+ entries_buf,
+ }
+ }
+}
+
+/// Instance methods.
+impl<R, Offset> UnitHeader<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Get the offset of this unit within its section.
+ pub fn offset(&self) -> UnitSectionOffset<Offset> {
+ 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<Offset> {
+ 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<Offset> {
+ 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<Offset>) -> 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<UnitOffset<Offset>>) -> Result<R> {
+ 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<UnitOffset<Offset>>) -> Result<R> {
+ 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<UnitOffset<Offset>>) -> Result<R> {
+ 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<Offset>,
+ ) -> Result<DebuggingInformationEntry<'abbrev, 'me, R>> {
+ 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<Offset>,
+ ) -> Result<EntriesCursor<'abbrev, 'me, R>> {
+ 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<UnitOffset<Offset>>,
+ ) -> Result<EntriesTree<'abbrev, 'me, R>> {
+ 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<UnitOffset<Offset>>,
+ ) -> Result<EntriesRaw<'abbrev, 'me, R>> {
+ 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<R>) -> Result<Abbreviations> {
+ debug_abbrev.abbreviations(self.debug_abbrev_offset())
+ }
+}
+
+/// Parse a unit header.
+fn parse_unit_header<R, Offset>(
+ input: &mut R,
+ unit_offset: UnitSectionOffset<Offset>,
+) -> Result<UnitHeader<R>>
+where
+ R: Reader<Offset = Offset>,
+ 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_u8()?;
+ // 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_u8()?;
+ 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<R: Reader>(input: &mut R) -> Result<DwoId> {
+ 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 = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ offset: UnitOffset<Offset>,
+ attrs_slice: R,
+ attrs_len: Cell<Option<Offset>>,
+ abbrev: &'abbrev Abbreviation,
+ unit: &'unit UnitHeader<R, Offset>,
+}
+
+impl<'abbrev, 'unit, R, Offset> DebuggingInformationEntry<'abbrev, 'unit, R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Construct a new `DebuggingInformationEntry`.
+ pub fn new(
+ offset: UnitOffset<Offset>,
+ attrs_slice: R,
+ abbrev: &'abbrev Abbreviation,
+ unit: &'unit UnitHeader<R, Offset>,
+ ) -> 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<Offset> {
+ 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<Option<Attribute<R>>> {
+ 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<Option<AttributeValue<R>>> {
+ 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<Option<AttributeValue<R>>> {
+ self.attr(name).map(|attr| attr.map(|attr| attr.value()))
+ }
+
+ /// Return the input buffer after the last attribute.
+ #[allow(clippy::inline_always)]
+ #[inline(always)]
+ fn after_attrs(&self) -> Result<R> {
+ 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 let Some(_) = attrs.next()? {}
+ 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<R> {
+ 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.
+ #[allow(clippy::inline_always)]
+ #[inline(always)]
+ fn parse(
+ input: &mut R,
+ unit: &'unit UnitHeader<R>,
+ abbreviations: &'abbrev Abbreviations,
+ ) -> Result<Option<Self>> {
+ 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)?;
+ 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<R, Offset = <R as Reader>::Offset>
+where
+ R: Reader<Offset = Offset>,
+ 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<R>),
+
+ /// 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<Offset>),
+
+ /// An index into a set of addresses in the `.debug_addr` section.
+ DebugAddrIndex(DebugAddrIndex<Offset>),
+
+ /// An offset into the current compilation unit.
+ UnitRef(UnitOffset<Offset>),
+
+ /// An offset into the current `.debug_info` section, but possibly a
+ /// different compilation unit from the current one.
+ DebugInfoRef(DebugInfoOffset<Offset>),
+
+ /// An offset into the `.debug_info` section of the supplementary object file.
+ DebugInfoRefSup(DebugInfoOffset<Offset>),
+
+ /// An offset into the `.debug_line` section.
+ DebugLineRef(DebugLineOffset<Offset>),
+
+ /// An offset into either the `.debug_loc` section or the `.debug_loclists` section.
+ LocationListsRef(LocationListsOffset<Offset>),
+
+ /// An offset to a set of offsets in the `.debug_loclists` section.
+ DebugLocListsBase(DebugLocListsBase<Offset>),
+
+ /// An index into a set of offsets in the `.debug_loclists` section.
+ DebugLocListsIndex(DebugLocListsIndex<Offset>),
+
+ /// An offset into the `.debug_macinfo` section.
+ DebugMacinfoRef(DebugMacinfoOffset<Offset>),
+
+ /// An offset into the `.debug_macro` section.
+ DebugMacroRef(DebugMacroOffset<Offset>),
+
+ /// An offset into the `.debug_ranges` section.
+ RangeListsRef(RawRangeListsOffset<Offset>),
+
+ /// An offset to a set of offsets in the `.debug_rnglists` section.
+ DebugRngListsBase(DebugRngListsBase<Offset>),
+
+ /// An index into a set of offsets in the `.debug_rnglists` section.
+ DebugRngListsIndex(DebugRngListsIndex<Offset>),
+
+ /// A type signature.
+ DebugTypesRef(DebugTypeSignature),
+
+ /// An offset into the `.debug_str` section.
+ DebugStrRef(DebugStrOffset<Offset>),
+
+ /// An offset into the `.debug_str` section of the supplementary object file.
+ DebugStrRefSup(DebugStrOffset<Offset>),
+
+ /// An offset to a set of entries in the `.debug_str_offsets` section.
+ DebugStrOffsetsBase(DebugStrOffsetsBase<Offset>),
+
+ /// An index into a set of entries in the `.debug_str_offsets` section.
+ DebugStrOffsetsIndex(DebugStrOffsetsIndex<Offset>),
+
+ /// An offset into the `.debug_line_str` section.
+ DebugLineStrRef(DebugLineStrOffset<Offset>),
+
+ /// 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<R: Reader> {
+ name: constants::DwAt,
+ value: AttributeValue<R>,
+}
+
+impl<R: Reader> Attribute<R> {
+ /// 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<R> {
+ 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".
+ #[allow(clippy::cyclomatic_complexity)]
+ #[allow(clippy::match_same_arms)]
+ pub fn value(&self) -> AttributeValue<R> {
+ // 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<u8> {
+ self.value.u8_value()
+ }
+
+ /// Try to convert this attribute's value to a u16.
+ #[inline]
+ pub fn u16_value(&self) -> Option<u16> {
+ self.value.u16_value()
+ }
+
+ /// Try to convert this attribute's value to an unsigned integer.
+ #[inline]
+ pub fn udata_value(&self) -> Option<u64> {
+ self.value.udata_value()
+ }
+
+ /// Try to convert this attribute's value to a signed integer.
+ #[inline]
+ pub fn sdata_value(&self) -> Option<i64> {
+ self.value.sdata_value()
+ }
+
+ /// Try to convert this attribute's value to an offset.
+ #[inline]
+ pub fn offset_value(&self) -> Option<R::Offset> {
+ 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<Expression<R>> {
+ 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<R>) -> Option<R> {
+ 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<R>,
+ debug_str_sup: Option<&DebugStr<R>>,
+ ) -> Option<R> {
+ self.value.string_value_sup(debug_str, debug_str_sup)
+ }
+}
+
+impl<R, Offset> AttributeValue<R, Offset>
+where
+ R: Reader<Offset = Offset>,
+ Offset: ReaderOffset,
+{
+ /// Try to convert this attribute's value to a u8.
+ pub fn u8_value(&self) -> Option<u8> {
+ 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<u16> {
+ 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<u64> {
+ 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<i64> {
+ 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_value() 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<R::Offset> {
+ // 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<Expression<R>> {
+ 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<R>) -> Option<R> {
+ 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<R>,
+ debug_str_sup: Option<&DebugStr<R>>,
+ ) -> Option<R> {
+ 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<R: Reader>(input: &mut R) -> Result<R> {
+ let len = input.read_u8().map(R::Offset::from_u8)?;
+ input.split(len)
+}
+
+fn length_u16_value<R: Reader>(input: &mut R) -> Result<R> {
+ let len = input.read_u16().map(R::Offset::from_u16)?;
+ input.split(len)
+}
+
+fn length_u32_value<R: Reader>(input: &mut R) -> Result<R> {
+ let len = input.read_u32().map(R::Offset::from_u32)?;
+ input.split(len)
+}
+
+fn length_uleb128_value<R: Reader>(input: &mut R) -> Result<R> {
+ 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<'unit, R: Reader>(
+ input: &mut R,
+ encoding: Encoding,
+ spec: AttributeSpecification,
+) -> Result<Attribute<R>> {
+ 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);
+ }
+ };
+ let attr = Attribute {
+ name: spec.name(),
+ value,
+ };
+ return Ok(attr);
+ }
+}
+
+pub(crate) fn skip_attributes<'unit, R: Reader>(
+ 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);
+ }
+ };
+ 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`.
+ #[allow(clippy::inline_always)]
+ #[inline(always)]
+ pub fn next(&mut self) -> Result<Option<Attribute<R>>> {
+ 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<R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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<R>,
+ 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<R::Offset> {
+ 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<Option<&'abbrev Abbreviation>> {
+ 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)?;
+ if abbrev.has_children() {
+ self.depth += 1;
+ }
+ Ok(Some(abbrev))
+ }
+
+ /// Read an attribute.
+ #[inline]
+ pub fn read_attribute(&mut self, spec: AttributeSpecification) -> Result<Attribute<R>> {
+ 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<R>,
+ abbreviations: &'abbrev Abbreviations,
+ cached_current: Option<DebuggingInformationEntry<'abbrev, 'unit, R>>,
+ 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<Option<()>> {
+ 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());
+ /// ```
+ #[allow(clippy::type_complexity)]
+ pub fn next_dfs(
+ &mut self,
+ ) -> Result<Option<(isize, &DebuggingInformationEntry<'abbrev, 'unit, R>)>> {
+ 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<Option<&DebuggingInformationEntry<'abbrev, 'unit, R>>> {
+ 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<R>(mut node: gimli::EntriesTreeNode<R>) -> 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<R>,
+ abbreviations: &'abbrev Abbreviations,
+ input: R,
+ entry: Option<DebuggingInformationEntry<'abbrev, 'unit, R>>,
+ depth: isize,
+}
+
+impl<'abbrev, 'unit, R: Reader> EntriesTree<'abbrev, 'unit, R> {
+ fn new(root: R, unit: &'unit UnitHeader<R>, 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<EntriesTreeNode<'abbrev, 'unit, 'me, R>> {
+ 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<bool> {
+ 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<Option<EntriesTreeNode<'abbrev, 'unit, 'me, R>>> {
+ 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<R: Reader>(input: &mut R) -> Result<DebugTypeSignature> {
+ input.read_u64().map(DebugTypeSignature)
+}
+
+/// Parse a type unit header's type offset.
+fn parse_type_offset<R: Reader>(input: &mut R, format: Format) -> Result<UnitOffset<R::Offset>> {
+ 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<R> {
+ debug_types_section: R,
+}
+
+impl<'input, Endian> DebugTypes<EndianSlice<'input, Endian>>
+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 OSX, 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<T> DebugTypes<T> {
+ /// Create a `DebugTypes` section that references the data in `self`.
+ ///
+ /// This is useful when `R` implements `Reader` but `T` does not.
+ ///
+ /// ## Example Usage
+ ///
+ /// ```rust,no_run
+ /// # let load_section = || unimplemented!();
+ /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
+ /// let owned_section: gimli::DebugTypes<Vec<u8>> = load_section();
+ /// // Create a reference to the DWARF section.
+ /// let section = owned_section.borrow(|section| {
+ /// gimli::EndianSlice::new(&section, gimli::LittleEndian)
+ /// });
+ /// ```
+ pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTypes<R>
+ where
+ F: FnMut(&'a T) -> R,
+ {
+ borrow(&self.debug_types_section).into()
+ }
+}
+
+impl<R> Section<R> for DebugTypes<R> {
+ fn id() -> SectionId {
+ SectionId::DebugTypes
+ }
+
+ fn reader(&self) -> &R {
+ &self.debug_types_section
+ }
+}
+
+impl<R> From<R> for DebugTypes<R> {
+ fn from(debug_types_section: R) -> Self {
+ DebugTypes {
+ debug_types_section,
+ }
+ }
+}
+
+impl<R: Reader> DebugTypes<R> {
+ /// 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<R> {
+ 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<R: Reader> {
+ input: R,
+ offset: DebugTypesOffset<R::Offset>,
+}
+
+impl<R: Reader> DebugTypesUnitHeadersIter<R> {
+ /// Advance the iterator to the next type unit header.
+ pub fn next(&mut self) -> Result<Option<UnitHeader<R>>> {
+ 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<R: Reader> fallible_iterator::FallibleIterator for DebugTypesUnitHeadersIter<R> {
+ type Item = UnitHeader<R>;
+ type Error = Error;
+
+ fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, 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<'input, E>(self, unit: &mut UnitHeader<EndianSlice<'input, E>>) -> Self
+ where
+ E: Endianity;
+ fn die<F>(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<'input, E>(self, unit: &mut UnitHeader<EndianSlice<'input, E>>) -> 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 | 3 | 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.into()).mark(&end);
+
+ unit.unit_length = (&end - &start) as usize;
+ length.set_const(unit.unit_length as u64);
+
+ section
+ }
+
+ fn die<F>(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<R>` is covariant wrt R.
+ #[test]
+ fn test_unit_header_variance() {
+ /// This only needs to compile.
+ fn _f<'a: 'b, 'b, E: Endianity>(
+ x: UnitHeader<EndianSlice<'a, E>>,
+ ) -> UnitHeader<EndianSlice<'b, E>> {
+ 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(_)) => assert!(true),
+ 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(_)) => assert!(true),
+ 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(_)) => assert!(true),
+ 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(_)) => assert!(true),
+ 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)) => assert!(true),
+ 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)) => assert!(true),
+ 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(_)) => assert!(true),
+ 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(_)) => assert!(true),
+ 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: F) -> Vec<u8>
+ 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<EndianSlice<LittleEndian>>,
+ Option<u64>,
+ Option<i64>,
+ )] = &[
+ (AttributeValue::Data1(1), Some(1), Some(1)),
+ (
+ AttributeValue::Data1(core::u8::MAX),
+ Some(u64::from(std::u8::MAX)),
+ Some(-1),
+ ),
+ (AttributeValue::Data2(1), Some(1), Some(1)),
+ (
+ AttributeValue::Data2(core::u16::MAX),
+ Some(u64::from(std::u16::MAX)),
+ Some(-1),
+ ),
+ (AttributeValue::Data4(1), Some(1), Some(1)),
+ (
+ AttributeValue::Data4(core::u32::MAX),
+ Some(u64::from(std::u32::MAX)),
+ Some(-1),
+ ),
+ (AttributeValue::Data8(1), Some(1), Some(1)),
+ (
+ AttributeValue::Data8(core::u64::MAX),
+ Some(core::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<Endian>(
+ address_size: u8,
+ format: Format,
+ endian: Endian,
+ ) -> UnitHeader<EndianSlice<'static, Endian>>
+ 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<EndianSlice<'static, LittleEndian>> {
+ test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian)
+ }
+
+ fn test_parse_attribute<'input, Endian>(
+ buf: &'input [u8],
+ len: usize,
+ unit: &UnitHeader<EndianSlice<'input, Endian>>,
+ form: constants::DwForm,
+ value: AttributeValue<EndianSlice<'input, Endian>>,
+ ) 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 => {
+ assert!(false, "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 => {
+ assert!(false, "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 => {
+ assert!(false, "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 => {
+ assert!(false, "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 => {
+ assert!(false, "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<Endian>(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<Endian>(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<Endian>(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<Endian>(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<Endian>(
+ 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<Endian>(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<Endian>(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<u8> {
+ #[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<u8> {
+ #[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 = &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_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<u8> {
+ #[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<u8> {
+ 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 => {
+ assert!(false, "Unexpected parse result = {:#?}", otherwise);
+ }
+ }
+ }
+
+ fn entries_tree_tests_debug_abbrevs_buf() -> Vec<u8> {
+ #[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<u8>, 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<EntriesTreeNode<'abbrev, 'unit, 'tree, EndianSlice<'input, Endian>>>,
+ >,
+ 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<E: Endianity>(node: Result<Option<EntriesTreeNode<EndianSlice<E>>>>) {
+ match node {
+ Ok(None) => {}
+ otherwise => {
+ assert!(false, "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<'input, 'abbrev, 'unit, Endian>(
+ entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, 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<'input, 'abbrev, 'unit, Endian>(
+ entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>,
+ ) where
+ Endian: Endianity,
+ {
+ match entries.read_abbreviation() {
+ Ok(None) => {}
+ otherwise => {
+ assert!(false, "Unexpected parse result = {:#?}", otherwise);
+ }
+ }
+ }
+
+ fn assert_attr<'input, 'abbrev, 'unit, Endian>(
+ entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>,
+ spec: Option<AttributeSpecification>,
+ 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();
+ }
+}
diff --git a/vendor/gimli/src/read/util.rs b/vendor/gimli/src/read/util.rs
new file mode 100644
index 000000000..dd2af8181
--- /dev/null
+++ b/vendor/gimli/src/read/util.rs
@@ -0,0 +1,250 @@
+#[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<Self::Item>];
+
+ #[doc(hidden)]
+ fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<Self::Item>];
+}
+
+// Use macro since const generics can't be used due to MSRV.
+macro_rules! impl_array {
+ () => {};
+ ($n:literal $($rest:tt)*) => {
+ // SAFETY: does not modify the content in storage.
+ unsafe impl<T> Sealed for [T; $n] {
+ type Storage = [MaybeUninit<T>; $n];
+
+ fn new_storage() -> Self::Storage {
+ // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid.
+ unsafe { MaybeUninit::uninit().assume_init() }
+ }
+ }
+
+ impl<T> ArrayLike for [T; $n] {
+ type Item = T;
+
+ fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] {
+ storage
+ }
+
+ fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] {
+ storage
+ }
+ }
+
+ impl_array!($($rest)*);
+ }
+}
+
+impl_array!(0 1 2 3 4 8 16 32 64 128 192);
+
+#[cfg(feature = "read")]
+unsafe impl<T> Sealed for Vec<T> {
+ type Storage = Box<[MaybeUninit<T>]>;
+
+ 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<T> ArrayLike for Vec<T> {
+ type Item = T;
+
+ fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] {
+ storage
+ }
+
+ fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] {
+ storage
+ }
+}
+
+pub(crate) struct ArrayVec<A: ArrayLike> {
+ storage: A::Storage,
+ len: usize,
+}
+
+impl<A: ArrayLike> ArrayVec<A> {
+ 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<A::Item> {
+ 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(&mut 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<T> ArrayVec<Vec<T>> {
+ pub fn into_vec(mut self) -> Vec<T> {
+ 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_ptr() as _, len, slice.len()) }
+ }
+}
+
+impl<A: ArrayLike> Drop for ArrayVec<A> {
+ fn drop(&mut self) {
+ self.clear();
+ }
+}
+
+impl<A: ArrayLike> Default for ArrayVec<A> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<A: ArrayLike> ops::Deref for ArrayVec<A> {
+ 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<A: ArrayLike> ops::DerefMut for ArrayVec<A> {
+ 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<A: ArrayLike> Clone for ArrayVec<A>
+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<A: ArrayLike> PartialEq for ArrayVec<A>
+where
+ A::Item: PartialEq,
+{
+ fn eq(&self, other: &Self) -> bool {
+ **self == **other
+ }
+}
+
+impl<A: ArrayLike> Eq for ArrayVec<A> where A::Item: Eq {}
+
+impl<A: ArrayLike> fmt::Debug for ArrayVec<A>
+where
+ A::Item: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
diff --git a/vendor/gimli/src/read/value.rs b/vendor/gimli/src/read/value.rs
new file mode 100644
index 000000000..fc8c355a9
--- /dev/null
+++ b/vendor/gimli/src/read/value.rs
@@ -0,0 +1,1623 @@
+//! Definitions for values used in DWARF expressions.
+
+use core::mem;
+
+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<ValueType> {
+ 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<R: Reader>(
+ entry: &DebuggingInformationEntry<R>,
+ ) -> Result<Option<ValueType>> {
+ 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<R: Reader>(value_type: ValueType, mut bytes: R) -> Result<Value> {
+ 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<u64> {
+ 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 as u64,
+ _ => 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ // 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<Value> {
+ // 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 implicity 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<u64> {
+ 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<Value> {
+ 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<Value> {
+ 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 implicity 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<Value> {
+ 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 implicity 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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);
+ }
+ }
+}
diff --git a/vendor/gimli/src/test_util.rs b/vendor/gimli/src/test_util.rs
new file mode 100644
index 000000000..706aaf934
--- /dev/null
+++ b/vendor/gimli/src/test_util.rs
@@ -0,0 +1,53 @@
+#![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"),
+ }
+ }
+}
diff --git a/vendor/gimli/src/write/abbrev.rs b/vendor/gimli/src/write/abbrev.rs
new file mode 100644
index 000000000..7cdfa969c
--- /dev/null
+++ b/vendor/gimli/src/write/abbrev.rs
@@ -0,0 +1,188 @@
+use 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<Abbreviation>,
+}
+
+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<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> 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<AttributeSpecification>,
+}
+
+impl Abbreviation {
+ /// Construct a new `Abbreviation`.
+ #[inline]
+ pub fn new(
+ tag: constants::DwTag,
+ has_children: bool,
+ attributes: Vec<AttributeSpecification>,
+ ) -> Abbreviation {
+ Abbreviation {
+ tag,
+ has_children,
+ attributes,
+ }
+ }
+
+ /// Write the abbreviation to the `.debug_abbrev` section.
+ pub fn write<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> 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<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> 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()
+ );
+ }
+}
diff --git a/vendor/gimli/src/write/cfi.rs b/vendor/gimli/src/write/cfi.rs
new file mode 100644
index 000000000..c58eb1b1d
--- /dev/null
+++ b/vendor/gimli/src/write/cfi.rs
@@ -0,0 +1,1012 @@
+use 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<CommonInformationEntry>,
+ /// 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<W: Writer>(&self, w: &mut DebugFrame<W>) -> Result<()> {
+ self.write(&mut w.0, false)
+ }
+
+ /// Write the frame table entries to the given `.eh_frame` section.
+ pub fn write_eh_frame<W: Writer>(&self, w: &mut EhFrame<W>) -> Result<()> {
+ self.write(&mut w.0, true)
+ }
+
+ fn write<W: Writer>(&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<constants::DwEhPe>,
+
+ /// 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<CallFrameInstruction>,
+}
+
+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<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<usize> {
+ 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)?;
+ // TODO: segment_selector_size
+ w.write_u8(0)?;
+ }
+
+ 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, constants::DW_EH_PE_absptr, 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<Address>,
+
+ /// 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<W: Writer>(
+ &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 mut augmentation_length = 0u64;
+ if self.lsda.is_some() {
+ augmentation_length += u64::from(encoding.address_size);
+ }
+ w.write_uleb128(augmentation_length)?;
+
+ 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 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)]
+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),
+}
+
+impl CallFrameInstruction {
+ fn write<W: Writer>(
+ &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())?;
+ }
+ }
+ Ok(())
+ }
+}
+
+fn write_advance_loc<W: Writer>(
+ 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: Writer>(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<u32> {
+ 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<i32> {
+ 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<R, Section>(
+ frame: &Section,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<FrameTable>
+ where
+ R: Reader<Offset = usize>,
+ Section: read::UnwindSection<R>,
+ Section::Offset: read::UnwindOffset<usize>,
+ {
+ 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<R, Section>(
+ from_cie: &read::CommonInformationEntry<R>,
+ frame: &Section,
+ bases: &read::BaseAddresses,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<CommonInformationEntry>
+ where
+ R: Reader<Offset = usize>,
+ Section: read::UnwindSection<R>,
+ Section::Offset: read::UnwindOffset<usize>,
+ {
+ 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,
+ convert_address,
+ &mut offset,
+ )? {
+ cie.instructions.push(instruction);
+ }
+ }
+ Ok(cie)
+ }
+ }
+
+ impl FrameDescriptionEntry {
+ fn from<R, Section>(
+ from_fde: &read::FrameDescriptionEntry<R>,
+ frame: &Section,
+ bases: &read::BaseAddresses,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<FrameDescriptionEntry>
+ where
+ R: Reader<Offset = usize>,
+ Section: read::UnwindSection<R>,
+ Section::Offset: read::UnwindOffset<usize>,
+ {
+ 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,
+ convert_address,
+ &mut offset,
+ )? {
+ fde.instructions.push((offset, instruction));
+ }
+ }
+
+ Ok(fde)
+ }
+ }
+
+ impl CallFrameInstruction {
+ fn from<R: Reader<Offset = usize>>(
+ from_instruction: read::CallFrameInstruction<R>,
+ from_cie: &read::CommonInformationEntry<R>,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ offset: &mut u32,
+ ) -> ConvertResult<Option<CallFrameInstruction>> {
+ 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 } => {
+ 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,
+ } => CallFrameInstruction::Expression(register, convert_expression(expression)?),
+ read::CallFrameInstruction::ValExpression {
+ register,
+ expression,
+ } => 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::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;
+
+ #[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());
+
+ // 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)),
+ ];
+
+ 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 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);
+
+ 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 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);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gimli/src/write/dwarf.rs b/vendor/gimli/src/write/dwarf.rs
new file mode 100644
index 000000000..ea507126a
--- /dev/null
+++ b/vendor/gimli/src/write/dwarf.rs
@@ -0,0 +1,138 @@
+use 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<LineProgram>,
+
+ /// 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<W: Writer>(&mut self, sections: &mut Sections<W>) -> 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<W: Writer>(&mut self, sections: &mut Sections<W>) -> 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<R: Reader<Offset = usize>>(
+ dwarf: &read::Dwarf<R>,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<Dwarf> {
+ 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,
+ })
+ }
+ }
+}
diff --git a/vendor/gimli/src/write/endian_vec.rs b/vendor/gimli/src/write/endian_vec.rs
new file mode 100644
index 000000000..7b040606a
--- /dev/null
+++ b/vendor/gimli/src/write/endian_vec.rs
@@ -0,0 +1,117 @@
+use alloc::vec::Vec;
+use std::mem;
+
+use crate::endianity::Endianity;
+use crate::write::{Error, Result, Writer};
+
+/// A `Vec<u8>` with endianity metadata.
+///
+/// This implements the `Writer` trait, which is used for all writing of DWARF sections.
+#[derive(Debug, Clone)]
+pub struct EndianVec<Endian>
+where
+ Endian: Endianity,
+{
+ vec: Vec<u8>,
+ endian: Endian,
+}
+
+impl<Endian> EndianVec<Endian>
+where
+ Endian: Endianity,
+{
+ /// Construct an empty `EndianVec` with the given endianity.
+ pub fn new(endian: Endian) -> EndianVec<Endian> {
+ EndianVec {
+ vec: Vec::new(),
+ endian,
+ }
+ }
+
+ /// Return a reference to the raw slice.
+ pub fn slice(&self) -> &[u8] {
+ &self.vec
+ }
+
+ /// Convert into a `Vec<u8>`.
+ pub fn into_vec(self) -> Vec<u8> {
+ self.vec
+ }
+
+ /// Take any written data out of the `EndianVec`, leaving an empty `Vec` in its place.
+ pub fn take(&mut self) -> Vec<u8> {
+ let mut vec = Vec::new();
+ mem::swap(&mut self.vec, &mut vec);
+ vec
+ }
+}
+
+impl<Endian> Writer for EndianVec<Endian>
+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]);
+ }
+}
diff --git a/vendor/gimli/src/write/line.rs b/vendor/gimli/src/write/line.rs
new file mode 100644
index 000000000..310170d9a
--- /dev/null
+++ b/vendor/gimli/src/write/line.rs
@@ -0,0 +1,1960 @@
+use 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<LineString>,
+
+ /// 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>,
+
+ /// The primary source file of the compilation unit.
+ /// This is required for version >= 5, but we never reference it elsewhere
+ /// because DWARF defines DW_AT_decl_file=0 to mean not specified.
+ comp_file: (LineString, 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,
+
+ prev_row: LineRow,
+ row: LineRow,
+ // TODO: this probably should be either rows or sequences instead
+ instructions: Vec<LineInstruction>,
+ 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.
+ #[allow(clippy::too_many_arguments)]
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new(
+ encoding: Encoding,
+ line_encoding: LineEncoding,
+ comp_dir: LineString,
+ comp_file: LineString,
+ comp_file_info: Option<FileInfo>,
+ ) -> 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(),
+ comp_file: (comp_file, comp_file_info.unwrap_or_default()),
+ prev_row: LineRow::initial_state(line_encoding),
+ row: LineRow::initial_state(line_encoding),
+ instructions: Vec::new(),
+ in_sequence: false,
+ file_has_timestamp: false,
+ file_has_size: false,
+ file_has_md5: 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.
+ program.add_directory(comp_dir);
+ 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 line_encoding = LineEncoding::default();
+ LineProgram {
+ none: true,
+ encoding: Encoding {
+ format: Format::Dwarf32,
+ version: 2,
+ address_size: 0,
+ },
+ line_encoding,
+ directories: IndexSet::new(),
+ files: IndexMap::new(),
+ comp_file: (LineString::String(Vec::new()), FileInfo::default()),
+ prev_row: LineRow::initial_state(line_encoding),
+ row: LineRow::initial_state(line_encoding),
+ instructions: Vec::new(),
+ in_sequence: false,
+ file_has_timestamp: false,
+ file_has_size: false,
+ file_has_md5: 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<FileInfo>,
+ ) -> FileId {
+ if let LineString::String(ref val) = file {
+ 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_insert(FileInfo::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) {
+ match id.index() {
+ None => (&self.comp_file.0, DirectoryId(0)),
+ Some(index) => self
+ .files
+ .get_index(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 {
+ match id.index() {
+ None => &self.comp_file.1,
+ Some(index) => self.files.get_index(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 {
+ match id.index() {
+ None => &mut self.comp_file.1,
+ Some(index) => self
+ .files
+ .get_index_mut(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<Address>) {
+ 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.line_encoding);
+ self.row = LineRow::initial_state(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<W: Writer>(
+ &self,
+ w: &mut DebugLine<W>,
+ encoding: Encoding,
+ debug_line_str_offsets: &DebugLineStrOffsets,
+ debug_str_offsets: &DebugStrOffsets,
+ ) -> Result<DebugLineOffset> {
+ 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 };
+ w.write_u8(count)?;
+ w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
+ let file_form = self.comp_file.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())?;
+ }
+
+ // File name entries.
+ w.write_uleb128(self.files.len() as u64 + 1)?;
+ 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)?;
+ }
+ Ok(())
+ };
+ write_file(&self.comp_file.0, DirectoryId(0), &self.comp_file.1)?;
+ 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.address_size())?;
+ }
+
+ 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(line_encoding: LineEncoding) -> Self {
+ LineRow {
+ address_offset: 0,
+ op_index: 0,
+
+ file: FileId::initial_state(),
+ 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<W: Writer>(self, w: &mut DebugLine<W>, address_size: u8) -> 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())?;
+ }
+ 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(address_size))?;
+ w.write_u8(constants::DW_LNE_set_address.0)?;
+ w.write_address(address, 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<u8>),
+
+ /// 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<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self
+ where
+ T: Into<Vec<u8>>,
+ {
+ 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<W: Writer>(
+ &self,
+ w: &mut DebugLine<W>,
+ 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`.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ pub struct FileId(usize);
+
+ impl FileId {
+ /// Create a FileId given an index into `LineProgram::files`.
+ pub(crate) fn new(index: usize) -> Self {
+ FileId(index + 1)
+ }
+
+ /// The index of the file in `LineProgram::files`.
+ pub(super) fn index(self) -> Option<usize> {
+ if self.0 == 0 {
+ None
+ } else {
+ Some(self.0 - 1)
+ }
+ }
+
+ /// The initial state of the file register.
+ pub(super) fn initial_state() -> Self {
+ FileId(1)
+ }
+
+ /// The raw value used when writing.
+ pub(crate) fn raw(self) -> u64 {
+ self.0 as u64
+ }
+
+ /// The id for file index 0 in DWARF version 5.
+ /// Only used when converting.
+ // Used for tests only.
+ #[allow(unused)]
+ pub(super) fn zero() -> Self {
+ FileId(0)
+ }
+ }
+}
+pub use self::id::*;
+
+/// Extra information for file in a `LineProgram`.
+#[derive(Debug, Default, Clone, Copy, 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],
+}
+
+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<R: Reader<Offset = usize>>(
+ mut from_program: read::IncompleteLineProgram<R>,
+ dwarf: &read::Dwarf<R>,
+ line_strings: &mut write::LineStringTable,
+ strings: &mut write::StringTable,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<(LineProgram, Vec<FileId>)> {
+ // 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, comp_file_info) = 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)?,
+ Some(FileInfo {
+ timestamp: comp_file.timestamp(),
+ size: comp_file.size(),
+ md5: *comp_file.md5(),
+ }),
+ )
+ }
+ None => (LineString::new(&[][..], encoding, line_strings), None),
+ };
+
+ if from_header.line_base() > 0 {
+ return Err(ConvertError::InvalidLineBase);
+ }
+ let mut program = LineProgram::new(
+ encoding,
+ from_header.line_encoding(),
+ comp_dir,
+ comp_name,
+ comp_file_info,
+ );
+
+ let file_skip;
+ 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.
+ file_skip = 0;
+ files.push(FileId::zero());
+ } else {
+ // We don't add the first file to `files`, but still allow
+ // it to be referenced from converted instructions.
+ file_skip = 1;
+ files.push(FileId::zero());
+ }
+
+ 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();
+ for from_file in from_header.file_names().iter().skip(file_skip) {
+ 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(),
+ });
+ 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<R: Reader<Offset = usize>>(
+ from_attr: read::AttributeValue<R>,
+ dwarf: &read::Dwarf<R>,
+ line_strings: &mut write::LineStringTable,
+ strings: &mut write::StringTable,
+ ) -> ConvertResult<LineString> {
+ 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::{DebugLineStr, DebugStr, EndianVec, StringTable};
+ use crate::LittleEndian;
+
+ #[test]
+ fn test_line_program_table() {
+ 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 programs = Vec::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;
+ }
+
+ 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]
+ },
+ };
+ let file_id = program.add_file(file2.clone(), dir_id, Some(file_info));
+ 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))
+ );
+ assert_eq!(file_info, *program.get_file_info(file_id));
+
+ programs.push((program, file_id, encoding));
+ }
+ }
+ }
+ }
+
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+ let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
+ let mut debug_line_offsets = Vec::new();
+ for (program, _, encoding) in &programs {
+ debug_line_offsets.push(
+ program
+ .write(
+ &mut debug_line,
+ *encoding,
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ )
+ .unwrap(),
+ );
+ }
+
+ let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
+
+ let convert_address = &|address| Some(Address::Constant(address));
+ for ((program, file_id, encoding), offset) in programs.iter().zip(debug_line_offsets.iter())
+ {
+ let read_program = read_debug_line
+ .program(
+ *offset,
+ encoding.address_size,
+ Some(read::EndianSlice::new(b"dir1", LittleEndian)),
+ Some(read::EndianSlice::new(b"file1", LittleEndian)),
+ )
+ .unwrap();
+
+ let dwarf = read::Dwarf::default();
+ let mut convert_line_strings = LineStringTable::default();
+ let mut convert_strings = StringTable::default();
+ let (convert_program, convert_files) = LineProgram::from(
+ read_program,
+ &dwarf,
+ &mut convert_line_strings,
+ &mut convert_strings,
+ convert_address,
+ )
+ .unwrap();
+ assert_eq!(convert_program.version(), program.version());
+ assert_eq!(convert_program.address_size(), program.address_size());
+ assert_eq!(convert_program.format(), program.format());
+
+ let convert_file_id = convert_files[file_id.raw() as usize];
+ 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"[..];
+ let convert_address = &|address| Some(Address::Constant(address));
+
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+
+ 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();
+ program.add_file(LineString::String(file1.to_vec()), dir_id, None);
+ let file_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 = file_id;
+ tests.push((
+ row,
+ vec![LineInstruction::SetFile(file_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 debug_line = DebugLine::from(EndianVec::new(LittleEndian));
+ let debug_line_offset = program
+ .write(
+ &mut debug_line,
+ encoding,
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ )
+ .unwrap();
+
+ let read_debug_line =
+ read::DebugLine::new(debug_line.slice(), LittleEndian);
+ let read_program = read_debug_line
+ .program(
+ debug_line_offset,
+ address_size,
+ Some(read::EndianSlice::new(dir1, LittleEndian)),
+ Some(read::EndianSlice::new(file1, LittleEndian)),
+ )
+ .unwrap();
+
+ let dwarf = read::Dwarf::default();
+ let mut convert_line_strings = LineStringTable::default();
+ let mut convert_strings = StringTable::default();
+ let (convert_program, _convert_files) = LineProgram::from(
+ read_program,
+ &dwarf,
+ &mut convert_line_strings,
+ &mut convert_strings,
+ convert_address,
+ )
+ .unwrap();
+ assert_eq!(
+ &convert_program.instructions[base_instructions.len()..],
+ &test.1[..]
+ );
+ }
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn test_line_instruction() {
+ let dir1 = &b"dir1"[..];
+ let file1 = &b"file1"[..];
+
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+
+ 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 &(ref inst, ref 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()),
+ ),
+ (
+ 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 debug_line = DebugLine::from(EndianVec::new(LittleEndian));
+ let debug_line_offset = program
+ .write(
+ &mut debug_line,
+ encoding,
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ )
+ .unwrap();
+
+ let read_debug_line =
+ read::DebugLine::new(debug_line.slice(), LittleEndian);
+ let read_program = read_debug_line
+ .program(
+ debug_line_offset,
+ address_size,
+ Some(read::EndianSlice::new(dir1, LittleEndian)),
+ Some(read::EndianSlice::new(file1, LittleEndian)),
+ )
+ .unwrap();
+ let read_header = read_program.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]
+ #[allow(clippy::useless_vec)]
+ 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;
+
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+
+ for minimum_instruction_length in vec![1, 4] {
+ for maximum_operations_per_instruction in vec![1, 3] {
+ for line_base in vec![-5, 0] {
+ for line_range in vec![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 debug_line = DebugLine::from(EndianVec::new(LittleEndian));
+ let debug_line_offset = program
+ .write(
+ &mut debug_line,
+ encoding,
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ )
+ .unwrap();
+
+ let read_debug_line =
+ read::DebugLine::new(debug_line.slice(), LittleEndian);
+ let read_program = read_debug_line
+ .program(
+ debug_line_offset,
+ 8,
+ Some(read::EndianSlice::new(dir1, LittleEndian)),
+ Some(read::EndianSlice::new(file1, LittleEndian)),
+ )
+ .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 file = b"file1";
+
+ let mut strings = StringTable::default();
+ let string_id = strings.add("file2");
+ let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
+ let debug_str_offsets = strings.write(&mut debug_str).unwrap();
+
+ let mut line_strings = LineStringTable::default();
+ let line_string_id = line_strings.add("file3");
+ let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian));
+ let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap();
+
+ for &address_size in &[4, 8] {
+ for &format in &[Format::Dwarf32, Format::Dwarf64] {
+ let encoding = Encoding {
+ format,
+ version,
+ address_size,
+ };
+
+ for (file, expect_file) in vec![
+ (
+ LineString::String(file.to_vec()),
+ read::AttributeValue::String(read::EndianSlice::new(file, LittleEndian)),
+ ),
+ (
+ LineString::StringRef(string_id),
+ read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)),
+ ),
+ (
+ LineString::LineStringRef(line_string_id),
+ read::AttributeValue::DebugLineStrRef(
+ debug_line_str_offsets.get(line_string_id),
+ ),
+ ),
+ ] {
+ let program = LineProgram::new(
+ encoding,
+ LineEncoding::default(),
+ LineString::String(b"dir".to_vec()),
+ file,
+ None,
+ );
+
+ let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
+ let debug_line_offset = program
+ .write(
+ &mut debug_line,
+ encoding,
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ )
+ .unwrap();
+
+ let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
+ let read_program = read_debug_line
+ .program(debug_line_offset, address_size, None, None)
+ .unwrap();
+ let read_header = read_program.header();
+ assert_eq!(read_header.file(0).unwrap().path_name(), expect_file);
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn test_missing_comp_dir() {
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+
+ 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 program = LineProgram::new(
+ encoding,
+ LineEncoding::default(),
+ LineString::String(Vec::new()),
+ LineString::String(Vec::new()),
+ None,
+ );
+
+ let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
+ let debug_line_offset = program
+ .write(
+ &mut debug_line,
+ encoding,
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ )
+ .unwrap();
+
+ let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
+ let read_program = read_debug_line
+ .program(
+ debug_line_offset,
+ address_size,
+ // Testing missing comp_dir/comp_name.
+ None,
+ None,
+ )
+ .unwrap();
+
+ let dwarf = read::Dwarf::default();
+ let mut convert_line_strings = LineStringTable::default();
+ let mut convert_strings = StringTable::default();
+ let convert_address = &|address| Some(Address::Constant(address));
+ LineProgram::from(
+ read_program,
+ &dwarf,
+ &mut convert_line_strings,
+ &mut convert_strings,
+ convert_address,
+ )
+ .unwrap();
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gimli/src/write/loc.rs b/vendor/gimli/src/write/loc.rs
new file mode 100644
index 000000000..ea0ecb1cf
--- /dev/null
+++ b/vendor/gimli/src/write/loc.rs
@@ -0,0 +1,549 @@
+use 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<LocationList>,
+}
+
+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)
+ }
+
+ /// Write the location list table to the appropriate section for the given DWARF version.
+ pub(crate) fn write<W: Writer>(
+ &self,
+ sections: &mut Sections<W>,
+ encoding: Encoding,
+ unit_offsets: Option<&UnitOffsets>,
+ ) -> Result<LocationListOffsets> {
+ 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<W: Writer>(
+ &self,
+ w: &mut DebugLoc<W>,
+ refs: &mut Vec<DebugInfoReference>,
+ encoding: Encoding,
+ unit_offsets: Option<&UnitOffsets>,
+ ) -> Result<LocationListOffsets> {
+ 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<W: Writer>(
+ &self,
+ w: &mut DebugLocLists<W>,
+ refs: &mut Vec<DebugInfoReference>,
+ encoding: Encoding,
+ unit_offsets: Option<&UnitOffsets>,
+ ) -> Result<LocationListOffsets> {
+ 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<Location>);
+
+/// 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: Writer>(
+ w: &mut W,
+ refs: &mut Vec<DebugInfoReference>,
+ 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<R: Reader<Offset = usize>>(
+ mut from: read::RawLocListIter<R>,
+ context: &ConvertUnitContext<R>,
+ ) -> ConvertResult<Self> {
+ 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, .. } if 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;
+
+ #[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: 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);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gimli/src/write/mod.rs b/vendor/gimli/src/write/mod.rs
new file mode 100644
index 000000000..47ba6319d
--- /dev/null
+++ b/vendor/gimli/src/write/mod.rs
@@ -0,0 +1,425 @@
+//! 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::*;
+
+#[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<T> = result::Result<T, Error>;
+
+/// 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<read::Error> for ConvertError {
+ fn from(e: read::Error) -> Self {
+ ConvertError::Read(e)
+ }
+ }
+
+ /// The result of a conversion.
+ pub type ConvertResult<T> = result::Result<T, ConvertError>;
+}
+#[cfg(feature = "read")]
+pub use self::convert::*;
diff --git a/vendor/gimli/src/write/op.rs b/vendor/gimli/src/write/op.rs
new file mode 100644
index 000000000..d1cacb356
--- /dev/null
+++ b/vendor/gimli/src/write/op.rs
@@ -0,0 +1,1617 @@
+use 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<Operation>,
+}
+
+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<u8>) -> 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<UnitEntryId>) {
+ 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<UnitEntryId>) {
+ 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<W: Writer>(
+ &self,
+ w: &mut W,
+ mut refs: Option<&mut Vec<DebugInfoReference>>,
+ 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()) {
+ let refs = match refs {
+ Some(ref mut refs) => Some(&mut **refs),
+ None => None,
+ };
+ debug_assert_eq!(w.len(), offset);
+ operation.write(w, refs, 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<u8>),
+ /// 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<Offset>),
+ /// 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<Offset>),
+ */
+ /// 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<UnitEntryId>),
+ /// 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<UnitEntryId>),
+ /// 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, .. } => {
+ encoding.format.word_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<W: Writer>(
+ &self,
+ w: &mut W,
+ refs: Option<&mut Vec<DebugInfoReference>>,
+ 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 = 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, UnitEntryId, UnitId};
+ use std::collections::HashMap;
+
+ impl Expression {
+ /// Create an expression from the input expression.
+ pub fn from<R: Reader<Offset = usize>>(
+ from_expression: read::Expression<R>,
+ encoding: Encoding,
+ dwarf: Option<&read::Dwarf<R>>,
+ unit: Option<&read::Unit<R>>,
+ entry_ids: Option<&HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>>,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<Expression> {
+ 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 let Some(_) = from_operations.next()? {
+ 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::{
+ DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase,
+ DebugStrOffsetsBase, Format, SectionId,
+ };
+ use crate::read;
+ use crate::write::{
+ DebugLineStrOffsets, DebugStrOffsets, EndianVec, LineProgram, Sections, Unit, UnitTable,
+ };
+ use crate::LittleEndian;
+ use std::collections::HashMap;
+
+ #[test]
+ fn test_operation() {
+ for &version in &[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 units = UnitTable::default();
+ let unit_id = units.add(Unit::new(encoding, LineProgram::none()));
+ let unit = units.get_mut(unit_id);
+ let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type);
+ let reference = Reference::Entry(unit_id, entry_id);
+
+ let mut sections = Sections::new(EndianVec::new(LittleEndian));
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+ let debug_info_offsets = units
+ .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets)
+ .unwrap();
+ let unit_offsets = debug_info_offsets.unit_offsets(unit_id);
+ let debug_info_offset = unit_offsets.debug_info_offset(entry_id);
+ let entry_offset =
+ read::UnitOffset(unit_offsets.unit_offset(entry_id) as usize);
+
+ let mut reg_expression = Expression::new();
+ reg_expression.op_reg(Register(23));
+
+ let operations: &[(&dyn Fn(&mut Expression), Operation, 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,
+ },
+ read::Operation::Deref {
+ base_type: entry_offset,
+ size: 2,
+ space: false,
+ },
+ ),
+ (
+ &|x| x.op_xderef_type(2, entry_id),
+ Operation::DerefType {
+ space: true,
+ size: 2,
+ base: entry_id,
+ },
+ read::Operation::Deref {
+ base_type: 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),
+ read::Operation::RegisterOffset {
+ register: Register(23),
+ offset: 0,
+ base_type: 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),
+ read::Operation::Call {
+ offset: read::DieReference::UnitRef(entry_offset),
+ },
+ ),
+ (
+ &|x| x.op_call_ref(reference),
+ Operation::CallRef(reference),
+ read::Operation::Call {
+ offset: read::DieReference::DebugInfoRef(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,
+ },
+ read::Operation::ImplicitPointer {
+ value: 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),
+ read::Operation::ParameterRef {
+ offset: 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()),
+ read::Operation::TypedLiteral {
+ base_type: 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)),
+ read::Operation::Convert {
+ base_type: 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)),
+ read::Operation::Reinterpret {
+ base_type: 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 },
+ ),
+ ];
+
+ 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);
+
+ let mut w = EndianVec::new(LittleEndian);
+ let mut refs = Vec::new();
+ expression
+ .write(&mut w, Some(&mut refs), encoding, Some(&unit_offsets))
+ .unwrap();
+ for r in &refs {
+ assert_eq!(r.unit, unit_id);
+ assert_eq!(r.entry, entry_id);
+ w.write_offset_at(
+ r.offset,
+ debug_info_offset.0,
+ SectionId::DebugInfo,
+ r.size,
+ )
+ .unwrap();
+ }
+
+ let read_expression =
+ read::Expression(read::EndianSlice::new(w.slice(), LittleEndian));
+ let mut read_operations = read_expression.operations(encoding);
+ for (_, _, operation) in operations {
+ assert_eq!(read_operations.next(), Ok(Some(*operation)));
+ }
+
+ // 4 = DW_OP_skip + i16 + DW_OP_nop
+ assert_eq!(
+ read_operations.next(),
+ Ok(Some(read::Operation::Bra {
+ target: -(w.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));
+
+ // Fake the unit.
+ let unit = read::Unit {
+ header: read::UnitHeader::new(
+ encoding,
+ 0,
+ read::UnitType::Compilation,
+ DebugAbbrevOffset(0),
+ DebugInfoOffset(0).into(),
+ read::EndianSlice::new(&[], LittleEndian),
+ ),
+ abbreviations: 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 mut entry_ids = HashMap::new();
+ entry_ids.insert(debug_info_offset.into(), (unit_id, entry_id));
+ let convert_expression = Expression::from(
+ read_expression,
+ encoding,
+ None, /* dwarf */
+ Some(&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))
+ );
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gimli/src/write/range.rs b/vendor/gimli/src/write/range.rs
new file mode 100644
index 000000000..b44ce1b7b
--- /dev/null
+++ b/vendor/gimli/src/write/range.rs
@@ -0,0 +1,415 @@
+use 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<RangeList>,
+}
+
+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)
+ }
+
+ /// Write the range list table to the appropriate section for the given DWARF version.
+ pub(crate) fn write<W: Writer>(
+ &self,
+ sections: &mut Sections<W>,
+ encoding: Encoding,
+ ) -> Result<RangeListOffsets> {
+ 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<W: Writer>(
+ &self,
+ w: &mut DebugRanges<W>,
+ address_size: u8,
+ ) -> Result<RangeListOffsets> {
+ 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<W: Writer>(
+ &self,
+ w: &mut DebugRngLists<W>,
+ encoding: Encoding,
+ ) -> Result<RangeListOffsets> {
+ 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<Range>);
+
+/// 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<R: Reader<Offset = usize>>(
+ mut from: read::RawRngListIter<R>,
+ context: &ConvertUnitContext<R>,
+ ) -> ConvertResult<Self> {
+ 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, .. } if 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;
+
+ #[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: 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);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gimli/src/write/section.rs b/vendor/gimli/src/write/section.rs
new file mode 100644
index 000000000..e8f3378cd
--- /dev/null
+++ b/vendor/gimli/src/write/section.rs
@@ -0,0 +1,172 @@
+use 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<W: Writer>(pub W);
+
+ impl<W: Writer> $name<W> {
+ /// Return the offset of the next write.
+ pub fn offset(&self) -> $offset {
+ $offset(self.len())
+ }
+ }
+
+ impl<W: Writer> From<W> for $name<W> {
+ #[inline]
+ fn from(w: W) -> Self {
+ $name(w)
+ }
+ }
+
+ impl<W: Writer> Deref for $name<W> {
+ type Target = W;
+
+ #[inline]
+ fn deref(&self) -> &W {
+ &self.0
+ }
+ }
+
+ impl<W: Writer> DerefMut for $name<W> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut W {
+ &mut self.0
+ }
+ }
+
+ impl<W: Writer> Section<W> for $name<W> {
+ #[inline]
+ fn id(&self) -> SectionId {
+ SectionId::$name
+ }
+ }
+ };
+}
+
+/// Functionality common to all writable DWARF sections.
+pub trait Section<W: Writer>: DerefMut<Target = W> {
+ /// 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<W: Writer> {
+ /// The `.debug_abbrev` section.
+ pub debug_abbrev: DebugAbbrev<W>,
+ /// The `.debug_info` section.
+ pub debug_info: DebugInfo<W>,
+ /// The `.debug_line` section.
+ pub debug_line: DebugLine<W>,
+ /// The `.debug_line_str` section.
+ pub debug_line_str: DebugLineStr<W>,
+ /// The `.debug_ranges` section.
+ pub debug_ranges: DebugRanges<W>,
+ /// The `.debug_rnglists` section.
+ pub debug_rnglists: DebugRngLists<W>,
+ /// The `.debug_loc` section.
+ pub debug_loc: DebugLoc<W>,
+ /// The `.debug_loclists` section.
+ pub debug_loclists: DebugLocLists<W>,
+ /// The `.debug_str` section.
+ pub debug_str: DebugStr<W>,
+ /// The `.debug_frame` section.
+ pub debug_frame: DebugFrame<W>,
+ /// The `.eh_frame` section.
+ pub eh_frame: EhFrame<W>,
+ /// Unresolved references in the `.debug_info` section.
+ pub(crate) debug_info_refs: Vec<DebugInfoReference>,
+ /// Unresolved references in the `.debug_loc` section.
+ pub(crate) debug_loc_refs: Vec<DebugInfoReference>,
+ /// Unresolved references in the `.debug_loclists` section.
+ pub(crate) debug_loclists_refs: Vec<DebugInfoReference>,
+}
+
+impl<W: Writer + Clone> Sections<W> {
+ /// 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.clone()),
+ debug_info_refs: Vec::new(),
+ debug_loc_refs: Vec::new(),
+ debug_loclists_refs: Vec::new(),
+ }
+ }
+}
+
+impl<W: Writer> Sections<W> {
+ /// For each section, call `f` once with a shared reference.
+ pub fn for_each<F, E>(&self, mut f: F) -> result::Result<(), E>
+ where
+ F: FnMut(SectionId, &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<F, E>(&mut self, mut f: F) -> result::Result<(), E>
+ where
+ F: FnMut(SectionId, &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(())
+ }
+}
diff --git a/vendor/gimli/src/write/str.rs b/vendor/gimli/src/write/str.rs
new file mode 100644
index 000000000..83285c035
--- /dev/null
+++ b/vendor/gimli/src/write/str.rs
@@ -0,0 +1,172 @@
+use 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<Vec<u8>>,
+ }
+
+ 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<T>(&mut self, bytes: T) -> $id
+ where
+ T: Into<Vec<u8>>,
+ {
+ 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<W: Writer>(&self, w: &mut $section<W>) -> 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"[..]);
+ }
+}
diff --git a/vendor/gimli/src/write/unit.rs b/vendor/gimli/src/write/unit.rs
new file mode 100644
index 000000000..bf85ff421
--- /dev/null
+++ b/vendor/gimli/src/write/unit.rs
@@ -0,0 +1,3157 @@
+use alloc::vec::Vec;
+use std::ops::{Deref, DerefMut};
+use std::{slice, usize};
+
+use crate::common::{
+ DebugAbbrevOffset, DebugInfoOffset, DebugLineOffset, DebugMacinfoOffset, DebugMacroOffset,
+ DebugStrOffset, DebugTypeSignature, DwoId, 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<Unit>,
+}
+
+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]
+ }
+
+ /// Write the units to the given sections.
+ ///
+ /// `strings` must contain the `.debug_str` offsets of the corresponding
+ /// `StringTable`.
+ pub fn write<W: Writer>(
+ &mut self,
+ sections: &mut Sections<W>,
+ line_strings: &DebugLineStrOffsets,
+ strings: &DebugStrOffsets,
+ ) -> Result<DebugInfoOffsets> {
+ 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<W: Writer>(
+ references: &mut Vec<DebugInfoReference>,
+ 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 implemention:
+ // - mutable iteration of children is messy due to borrow checker
+ entries: Vec<DebuggingInformationEntry>,
+ /// 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<W: Writer>(
+ &mut self,
+ sections: &mut Sections<W>,
+ abbrev_offset: DebugAbbrevOffset,
+ abbrevs: &mut AbbreviationTable,
+ line_strings: &DebugLineStrOffsets,
+ strings: &DebugStrOffsets,
+ ) -> Result<UnitOffsets> {
+ 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,
+ abbrevs,
+ 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<UnitEntryId>,
+ tag: constants::DwTag,
+ /// Whether to emit `DW_AT_sibling`.
+ sibling: bool,
+ attrs: Vec<Attribute>,
+ children: Vec<UnitEntryId>,
+}
+
+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<DebuggingInformationEntry>,
+ parent: Option<UnitEntryId>,
+ 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<UnitEntryId> {
+ 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<Abbreviation> {
+ 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.
+ #[allow(clippy::too_many_arguments)]
+ fn write<W: Writer>(
+ &self,
+ w: &mut DebugInfo<W>,
+ debug_info_refs: &mut Vec<DebugInfoReference>,
+ unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>,
+ unit: &Unit,
+ offsets: &mut UnitOffsets,
+ abbrevs: &mut AbbreviationTable,
+ line_program: Option<DebugLineOffset>,
+ 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,
+ abbrevs,
+ 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<AttributeSpecification> {
+ 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<u8>),
+
+ /// 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<u8>),
+
+ /// 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<FileId>),
+}
+
+impl AttributeValue {
+ /// Return the form that will be used to encode this value.
+ pub fn form(&self, encoding: Encoding) -> Result<constants::DwForm> {
+ // 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 as u64)
+ }
+ 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(FileId::raw).unwrap_or(0))
+ }
+ }
+ }
+
+ /// Write the attribute value to the given sections.
+ #[allow(clippy::cyclomatic_complexity, clippy::too_many_arguments)]
+ fn write<W: Writer>(
+ &self,
+ w: &mut DebugInfo<W>,
+ debug_info_refs: &mut Vec<DebugInfoReference>,
+ unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>,
+ unit: &Unit,
+ offsets: &UnitOffsets,
+ line_program: Option<DebugLineOffset>,
+ 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(FileId::raw).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<UnitOffsets>,
+}
+
+impl DebugInfoOffsets {
+ #[cfg(test)]
+ pub(crate) fn unit_offsets(&self, unit: UnitId) -> &UnitOffsets {
+ debug_assert_eq!(self.base_id, unit.base_id);
+ &self.units[unit.index]
+ }
+
+ /// 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<EntryOffset>,
+}
+
+impl UnitOffsets {
+ #[cfg(test)]
+ fn none() -> Self {
+ UnitOffsets {
+ base_id: BaseId::default(),
+ unit: DebugInfoOffset(0),
+ entries: Vec::new(),
+ }
+ }
+
+ /// 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::UnitSectionOffset;
+ use crate::read::{self, Reader};
+ use crate::write::{self, ConvertError, ConvertResult, LocationList, RangeList};
+ use std::collections::HashMap;
+
+ pub(crate) struct ConvertUnit<R: Reader<Offset = usize>> {
+ from_unit: read::Unit<R>,
+ base_id: BaseId,
+ encoding: Encoding,
+ entries: Vec<DebuggingInformationEntry>,
+ entry_offsets: Vec<read::UnitOffset>,
+ root: UnitEntryId,
+ }
+
+ pub(crate) struct ConvertUnitContext<'a, R: Reader<Offset = usize>> {
+ pub dwarf: &'a read::Dwarf<R>,
+ pub unit: &'a read::Unit<R>,
+ 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<Address>,
+ pub base_address: Address,
+ pub line_program_offset: Option<DebugLineOffset>,
+ pub line_program_files: Vec<FileId>,
+ pub entry_ids: &'a HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>,
+ }
+
+ 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<R: Reader<Offset = usize>>(
+ dwarf: &read::Dwarf<R>,
+ line_strings: &mut write::LineStringTable,
+ strings: &mut write::StringTable,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<UnitTable> {
+ 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.
+ #[allow(clippy::too_many_arguments)]
+ pub(crate) fn convert_entries<R: Reader<Offset = usize>>(
+ from_header: read::UnitHeader<R>,
+ unit_id: UnitId,
+ entry_ids: &mut HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>,
+ dwarf: &read::Dwarf<R>,
+ ) -> ConvertResult<ConvertUnit<R>> {
+ 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<R: Reader<Offset = usize>>(
+ unit: ConvertUnit<R>,
+ entry_ids: &HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>,
+ dwarf: &read::Dwarf<R>,
+ line_strings: &mut write::LineStringTable,
+ strings: &mut write::StringTable,
+ convert_address: &dyn Fn(u64) -> Option<Address>,
+ ) -> ConvertResult<Unit> {
+ 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<R: Reader<Offset = usize>>(
+ from: read::EntriesTreeNode<R>,
+ from_unit: &read::Unit<R>,
+ base_id: BaseId,
+ entries: &mut Vec<DebuggingInformationEntry>,
+ entry_offsets: &mut Vec<read::UnitOffset>,
+ entry_ids: &mut HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>,
+ parent: Option<UnitEntryId>,
+ unit_id: UnitId,
+ ) -> ConvertResult<UnitEntryId> {
+ 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<R: Reader<Offset = usize>>(
+ &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<R: Reader<Offset = usize>>(
+ context: &mut ConvertUnitContext<R>,
+ from: &read::Attribute<R>,
+ ) -> ConvertResult<Option<Attribute>> {
+ 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<R: Reader<Offset = usize>>(
+ context: &mut ConvertUnitContext<R>,
+ from: read::AttributeValue<R>,
+ ) -> ConvertResult<Option<AttributeValue>> {
+ 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 {
+ // 0 means not specified, even for version 5.
+ 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::{
+ DebugAddrBase, DebugLocListsBase, DebugRngListsBase, DebugStrOffsetsBase, LineEncoding,
+ };
+ use crate::constants;
+ use crate::read;
+ use crate::write::{
+ DebugLine, DebugLineStr, DebugStr, DwarfUnit, EndianVec, LineString, LineStringTable,
+ Location, LocationList, LocationListTable, Range, RangeList, RangeListOffsets,
+ RangeListTable, StringTable,
+ };
+ use crate::LittleEndian;
+ use std::collections::HashMap;
+ use std::mem;
+
+ #[test]
+ #[allow(clippy::cyclomatic_complexity)]
+ fn test_unit_table() {
+ let mut strings = StringTable::default();
+
+ let mut units = UnitTable::default();
+ let unit_id1 = units.add(Unit::new(
+ Encoding {
+ version: 4,
+ address_size: 8,
+ format: Format::Dwarf32,
+ },
+ LineProgram::none(),
+ ));
+ let unit2 = units.add(Unit::new(
+ Encoding {
+ version: 2,
+ address_size: 4,
+ format: Format::Dwarf64,
+ },
+ LineProgram::none(),
+ ));
+ let unit3 = units.add(Unit::new(
+ Encoding {
+ version: 5,
+ address_size: 4,
+ format: Format::Dwarf32,
+ },
+ LineProgram::none(),
+ ));
+ assert_eq!(units.count(), 3);
+ {
+ let unit1 = 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(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(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<_>>(),
+ vec![child1, child2]
+ );
+ }
+ }
+ {
+ let unit2 = 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));
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = strings.write(&mut sections.debug_str).unwrap();
+ units
+ .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets)
+ .unwrap();
+
+ println!("{:?}", sections.debug_str);
+ println!("{:?}", sections.debug_info);
+ println!("{:?}", sections.debug_abbrev);
+
+ let dwarf = read::Dwarf {
+ debug_abbrev: read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian),
+ debug_info: read::DebugInfo::new(sections.debug_info.slice(), LittleEndian),
+ debug_str: read::DebugStr::new(sections.debug_str.slice(), LittleEndian),
+ ..Default::default()
+ };
+ let mut read_units = dwarf.units();
+
+ {
+ let read_unit1 = read_units.next().unwrap().unwrap();
+ let unit1 = 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 = 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!(
+ 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 = strings.get(name);
+ assert_eq!(name, b"child1");
+ let read_name = read_child
+ .attr_value(constants::DW_AT_name)
+ .unwrap()
+ .unwrap();
+ assert_eq!(
+ 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 = strings.get(name);
+ assert_eq!(name, b"child2");
+ let read_name = read_child
+ .attr_value(constants::DW_AT_name)
+ .unwrap()
+ .unwrap();
+ assert_eq!(
+ 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 = 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 = 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 = 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 = 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 mut convert_line_strings = LineStringTable::default();
+ let mut convert_strings = StringTable::default();
+ let convert_units = UnitTable::from(
+ &dwarf,
+ &mut convert_line_strings,
+ &mut convert_strings,
+ &|address| Some(Address::Constant(address)),
+ )
+ .unwrap();
+ assert_eq!(convert_units.count(), units.count());
+
+ for i in 0..convert_units.count() {
+ let unit_id = units.id(i);
+ let unit = units.get(unit_id);
+ let convert_unit_id = convert_units.id(i);
+ let convert_unit = convert_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() {
+ // Create a string table and a string with a non-zero id/offset.
+ let mut strings = StringTable::default();
+ strings.add("string one");
+ let string_id = strings.add("string two");
+ let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
+ let debug_str_offsets = strings.write(&mut debug_str).unwrap();
+ let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian);
+
+ let mut line_strings = LineStringTable::default();
+ line_strings.add("line string one");
+ let line_string_id = line_strings.add("line string two");
+ let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian));
+ let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap();
+ let read_debug_line_str =
+ read::DebugLineStr::from(read::EndianSlice::new(debug_line_str.slice(), LittleEndian));
+
+ 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 mut ranges = RangeListTable::default();
+ let range_id = ranges.add(RangeList(vec![Range::StartEnd {
+ begin: Address::Constant(0x1234),
+ end: Address::Constant(0x2345),
+ }]));
+
+ let mut locations = LocationListTable::default();
+ let loc_id = locations.add(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 sections = Sections::new(EndianVec::new(LittleEndian));
+ let range_list_offsets = ranges.write(&mut sections, encoding).unwrap();
+ let loc_list_offsets = locations.write(&mut sections, encoding, None).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_debug_loc =
+ read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian);
+ let read_debug_loclists =
+ read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian);
+
+ let mut units = UnitTable::default();
+ let unit = units.add(Unit::new(encoding, LineProgram::none()));
+ let unit = units.get(unit);
+ let encoding = Encoding {
+ format,
+ version,
+ address_size,
+ };
+ let from_unit = read::UnitHeader::new(
+ encoding,
+ 0,
+ read::UnitType::Compilation,
+ DebugAbbrevOffset(0),
+ DebugInfoOffset(0).into(),
+ read::EndianSlice::new(&[], LittleEndian),
+ );
+
+ for &(ref name, ref value, ref expect_value) in &[
+ (
+ 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_location,
+ AttributeValue::LocationListRef(loc_id),
+ read::AttributeValue::SecOffset(loc_list_offsets.get(loc_id).0),
+ ),
+ (
+ 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_ranges,
+ AttributeValue::RangeListRef(range_id),
+ read::AttributeValue::SecOffset(range_list_offsets.get(range_id).0),
+ ),
+ (
+ constants::DW_AT_name,
+ AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)),
+ read::AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)),
+ ),
+ (
+ constants::DW_AT_name,
+ AttributeValue::StringRef(string_id),
+ read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)),
+ ),
+ (
+ constants::DW_AT_name,
+ AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)),
+ read::AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)),
+ ),
+ (
+ constants::DW_AT_name,
+ AttributeValue::LineStringRef(line_string_id),
+ read::AttributeValue::DebugLineStrRef(
+ debug_line_str_offsets.get(line_string_id),
+ ),
+ ),
+ (
+ 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 form = value.form(encoding).unwrap();
+ let attr = Attribute {
+ name: *name,
+ value: value.clone(),
+ };
+
+ let offsets = UnitOffsets::none();
+ let line_program_offset = None;
+ let mut debug_info_refs = Vec::new();
+ let mut unit_refs = Vec::new();
+ let mut debug_info = DebugInfo::from(EndianVec::new(LittleEndian));
+ attr.value
+ .write(
+ &mut debug_info,
+ &mut debug_info_refs,
+ &mut unit_refs,
+ &unit,
+ &offsets,
+ line_program_offset,
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ &range_list_offsets,
+ &loc_list_offsets,
+ )
+ .unwrap();
+
+ let spec = read::AttributeSpecification::new(*name, form, None);
+ let mut r = read::EndianSlice::new(debug_info.slice(), LittleEndian);
+ let read_attr = read::parse_attribute(&mut r, encoding, spec).unwrap();
+ let read_value = &read_attr.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::EndianSlice<LittleEndian>>,
+ &read::AttributeValue<read::EndianSlice<LittleEndian>>,
+ >(read_value)
+ };
+ assert_eq!(read_value, expect_value);
+
+ let dwarf = read::Dwarf {
+ debug_str: read_debug_str.clone(),
+ debug_line_str: read_debug_line_str.clone(),
+ ranges: read::RangeLists::new(read_debug_ranges, read_debug_rnglists),
+ locations: read::LocationLists::new(
+ read_debug_loc,
+ read_debug_loclists,
+ ),
+ ..Default::default()
+ };
+
+ let unit = read::Unit {
+ header: from_unit,
+ abbreviations: 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 mut context = convert::ConvertUnitContext {
+ dwarf: &dwarf,
+ unit: &unit,
+ line_strings: &mut line_strings,
+ strings: &mut strings,
+ ranges: &mut ranges,
+ 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_attr =
+ Attribute::from(&mut context, &read_attr).unwrap().unwrap();
+ assert_eq!(convert_attr, attr);
+ }
+ }
+ }
+ }
+ }
+
+ #[test]
+ #[allow(clippy::cyclomatic_complexity)]
+ fn test_unit_ref() {
+ let mut units = UnitTable::default();
+ let unit_id1 = units.add(Unit::new(
+ Encoding {
+ version: 4,
+ address_size: 8,
+ format: Format::Dwarf32,
+ },
+ LineProgram::none(),
+ ));
+ assert_eq!(unit_id1, units.id(0));
+ let unit_id2 = units.add(Unit::new(
+ Encoding {
+ version: 2,
+ address_size: 4,
+ format: Format::Dwarf64,
+ },
+ LineProgram::none(),
+ ));
+ assert_eq!(unit_id2, units.id(1));
+ let unit1_child1 = UnitEntryId::new(units.get(unit_id1).base_id, 1);
+ let unit1_child2 = UnitEntryId::new(units.get(unit_id1).base_id, 2);
+ let unit2_child1 = UnitEntryId::new(units.get(unit_id2).base_id, 1);
+ let unit2_child2 = UnitEntryId::new(units.get(unit_id2).base_id, 2);
+ {
+ let unit1 = 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 = 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 debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+ let mut sections = Sections::new(EndianVec::new(LittleEndian));
+ let debug_info_offsets = units
+ .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets)
+ .unwrap();
+
+ println!("{:?}", sections.debug_info);
+ println!("{:?}", sections.debug_abbrev);
+
+ let dwarf = read::Dwarf {
+ debug_abbrev: read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian),
+ debug_info: read::DebugInfo::new(sections.debug_info.slice(), LittleEndian),
+ ..Default::default()
+ };
+
+ let mut read_units = dwarf.units();
+ {
+ let read_unit1 = read_units.next().unwrap().unwrap();
+ assert_eq!(
+ read_unit1.offset(),
+ debug_info_offsets.unit(unit_id1).into()
+ );
+
+ let abbrevs = dwarf.abbreviations(&read_unit1).unwrap();
+ let mut read_entries = read_unit1.entries(&abbrevs);
+ {
+ let (_, _read_root) = read_entries.next_dfs().unwrap().unwrap();
+ }
+ {
+ let (_, read_child1) = read_entries.next_dfs().unwrap().unwrap();
+ let offset = debug_info_offsets
+ .entry(unit_id1, unit1_child2)
+ .to_unit_offset(&read_unit1)
+ .unwrap();
+ assert_eq!(
+ read_child1.attr_value(constants::DW_AT_type).unwrap(),
+ Some(read::AttributeValue::UnitRef(offset))
+ );
+ }
+ {
+ let (_, read_child2) = read_entries.next_dfs().unwrap().unwrap();
+ let offset = debug_info_offsets.entry(unit_id2, unit2_child1);
+ assert_eq!(
+ read_child2.attr_value(constants::DW_AT_type).unwrap(),
+ Some(read::AttributeValue::DebugInfoRef(offset))
+ );
+ }
+ }
+ {
+ let read_unit2 = read_units.next().unwrap().unwrap();
+ assert_eq!(
+ read_unit2.offset(),
+ debug_info_offsets.unit(unit_id2).into()
+ );
+
+ let abbrevs = dwarf.abbreviations(&read_unit2).unwrap();
+ let mut read_entries = read_unit2.entries(&abbrevs);
+ {
+ let (_, _read_root) = read_entries.next_dfs().unwrap().unwrap();
+ }
+ {
+ let (_, read_child1) = read_entries.next_dfs().unwrap().unwrap();
+ let offset = debug_info_offsets
+ .entry(unit_id2, unit2_child2)
+ .to_unit_offset(&read_unit2)
+ .unwrap();
+ assert_eq!(
+ read_child1.attr_value(constants::DW_AT_type).unwrap(),
+ Some(read::AttributeValue::UnitRef(offset))
+ );
+ }
+ {
+ let (_, read_child2) = read_entries.next_dfs().unwrap().unwrap();
+ let offset = debug_info_offsets.entry(unit_id1, unit1_child1);
+ assert_eq!(
+ read_child2.attr_value(constants::DW_AT_type).unwrap(),
+ Some(read::AttributeValue::DebugInfoRef(offset))
+ );
+ }
+ }
+
+ let mut convert_line_strings = LineStringTable::default();
+ let mut convert_strings = StringTable::default();
+ let convert_units = UnitTable::from(
+ &dwarf,
+ &mut convert_line_strings,
+ &mut convert_strings,
+ &|address| Some(Address::Constant(address)),
+ )
+ .unwrap();
+ assert_eq!(convert_units.count(), units.count());
+
+ for i in 0..convert_units.count() {
+ let unit = units.get(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(units: &mut UnitTable, unit_id: UnitId) {
+ let unit = units.get_mut(unit_id);
+ 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<R: read::Reader<Offset = usize>>(
+ entries: &mut read::EntriesCursor<R>,
+ ) -> (read::UnitOffset, Option<read::UnitOffset>) {
+ 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<R: read::Reader<Offset = usize>>(
+ unit: &read::UnitHeader<R>,
+ debug_abbrev: &read::DebugAbbrev<R>,
+ ) {
+ let abbrevs = unit.abbreviations(debug_abbrev).unwrap();
+ let mut entries = unit.entries(&abbrevs);
+ // 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 units = UnitTable::default();
+ let unit_id1 = units.add(Unit::new(encoding, LineProgram::none()));
+ add_children(&mut units, unit_id1);
+ let unit_id2 = units.add(Unit::new(encoding, LineProgram::none()));
+ add_children(&mut units, unit_id2);
+
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+ let mut sections = Sections::new(EndianVec::new(LittleEndian));
+ units
+ .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets)
+ .unwrap();
+
+ println!("{:?}", sections.debug_info);
+ println!("{:?}", sections.debug_abbrev);
+
+ let read_debug_info = read::DebugInfo::new(sections.debug_info.slice(), LittleEndian);
+ let read_debug_abbrev = read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian);
+ let mut read_units = read_debug_info.units();
+ check_sibling(&read_units.next().unwrap().unwrap(), &read_debug_abbrev);
+ check_sibling(&read_units.next().unwrap().unwrap(), &read_debug_abbrev);
+ }
+
+ #[test]
+ fn test_line_ref() {
+ 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(b"comp_dir".to_vec()),
+ LineString::String(b"comp_name".to_vec()),
+ None,
+ );
+ let dir = line_program.default_directory();
+ let file1 =
+ line_program.add_file(LineString::String(b"file1".to_vec()), dir, None);
+ let file2 =
+ line_program.add_file(LineString::String(b"file2".to_vec()), dir, None);
+
+ // Write, read, and convert the line program, so that we have the info
+ // required to convert the attributes.
+ let line_strings = DebugLineStrOffsets::none();
+ let strings = DebugStrOffsets::none();
+ let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
+ let line_program_offset = line_program
+ .write(&mut debug_line, encoding, &line_strings, &strings)
+ .unwrap();
+ let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
+ let read_line_program = read_debug_line
+ .program(
+ line_program_offset,
+ address_size,
+ Some(read::EndianSlice::new(b"comp_dir", LittleEndian)),
+ Some(read::EndianSlice::new(b"comp_name", LittleEndian)),
+ )
+ .unwrap();
+ let dwarf = read::Dwarf::default();
+ let mut convert_line_strings = LineStringTable::default();
+ let mut convert_strings = StringTable::default();
+ let (_, line_program_files) = LineProgram::from(
+ read_line_program,
+ &dwarf,
+ &mut convert_line_strings,
+ &mut convert_strings,
+ &|address| Some(Address::Constant(address)),
+ )
+ .unwrap();
+
+ // Fake the unit.
+ let mut units = UnitTable::default();
+ let unit = units.add(Unit::new(encoding, LineProgram::none()));
+ let unit = units.get(unit);
+ let from_unit = read::UnitHeader::new(
+ encoding,
+ 0,
+ read::UnitType::Compilation,
+ DebugAbbrevOffset(0),
+ DebugInfoOffset(0).into(),
+ read::EndianSlice::new(&[], LittleEndian),
+ );
+
+ for &(ref name, ref value, ref expect_value) in &[
+ (
+ constants::DW_AT_stmt_list,
+ AttributeValue::LineProgramRef,
+ read::AttributeValue::SecOffset(line_program_offset.0),
+ ),
+ (
+ constants::DW_AT_decl_file,
+ AttributeValue::FileIndex(Some(file1)),
+ read::AttributeValue::Udata(file1.raw()),
+ ),
+ (
+ constants::DW_AT_decl_file,
+ AttributeValue::FileIndex(Some(file2)),
+ read::AttributeValue::Udata(file2.raw()),
+ ),
+ ][..]
+ {
+ let mut ranges = RangeListTable::default();
+ let mut locations = LocationListTable::default();
+ let mut strings = StringTable::default();
+ let mut line_strings = LineStringTable::default();
+
+ let form = value.form(encoding).unwrap();
+ let attr = Attribute {
+ name: *name,
+ value: value.clone(),
+ };
+
+ let mut debug_info_refs = Vec::new();
+ let mut unit_refs = Vec::new();
+ let mut debug_info = DebugInfo::from(EndianVec::new(LittleEndian));
+ let offsets = UnitOffsets::none();
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+ let range_list_offsets = RangeListOffsets::none();
+ let loc_list_offsets = LocationListOffsets::none();
+ attr.value
+ .write(
+ &mut debug_info,
+ &mut debug_info_refs,
+ &mut unit_refs,
+ &unit,
+ &offsets,
+ Some(line_program_offset),
+ &debug_line_str_offsets,
+ &debug_str_offsets,
+ &range_list_offsets,
+ &loc_list_offsets,
+ )
+ .unwrap();
+
+ let spec = read::AttributeSpecification::new(*name, form, None);
+ let mut r = read::EndianSlice::new(debug_info.slice(), LittleEndian);
+ let read_attr = read::parse_attribute(&mut r, encoding, spec).unwrap();
+ let read_value = &read_attr.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::EndianSlice<LittleEndian>>,
+ &read::AttributeValue<read::EndianSlice<LittleEndian>>,
+ >(read_value)
+ };
+ assert_eq!(read_value, expect_value);
+
+ let unit = read::Unit {
+ header: from_unit,
+ abbreviations: 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 mut context = convert::ConvertUnitContext {
+ dwarf: &dwarf,
+ unit: &unit,
+ line_strings: &mut line_strings,
+ strings: &mut strings,
+ ranges: &mut ranges,
+ locations: &mut locations,
+ convert_address: &|address| Some(Address::Constant(address)),
+ base_address: Address::Constant(0),
+ line_program_offset: Some(line_program_offset),
+ line_program_files: line_program_files.clone(),
+ entry_ids: &HashMap::new(),
+ };
+
+ let convert_attr =
+ Attribute::from(&mut context, &read_attr).unwrap().unwrap();
+ assert_eq!(convert_attr, attr);
+ }
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn test_line_program_used() {
+ for used in vec![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 units = UnitTable::default();
+ units.add(unit);
+
+ let debug_line_str_offsets = DebugLineStrOffsets::none();
+ let debug_str_offsets = DebugStrOffsets::none();
+ let mut sections = Sections::new(EndianVec::new(LittleEndian));
+ units
+ .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets)
+ .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<R: read::Reader>(
+ entry: &read::DebuggingInformationEntry<R>,
+ debug_str: &read::DebugStr<R>,
+ name: &str,
+ ) {
+ let name_attr = entry.attr(constants::DW_AT_name).unwrap().unwrap();
+ let entry_name = name_attr.string_value(debug_str).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_debug_info = read::DebugInfo::new(sections.debug_info.slice(), LittleEndian);
+ let read_debug_abbrev = read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian);
+ let read_debug_str = read::DebugStr::new(sections.debug_str.slice(), LittleEndian);
+ let read_unit = read_debug_info.units().next().unwrap().unwrap();
+ let abbrevs = read_unit.abbreviations(&read_debug_abbrev).unwrap();
+ let mut entries = read_unit.entries(&abbrevs);
+ // root
+ entries.next_dfs().unwrap().unwrap();
+ // child2
+ let (_, read_child2) = entries.next_dfs().unwrap().unwrap();
+ check_name(read_child2, &read_debug_str, "child2");
+ // child3
+ let (_, read_child3) = entries.next_dfs().unwrap().unwrap();
+ check_name(read_child3, &read_debug_str, "child3");
+ // child4
+ let (_, read_child4) = entries.next_dfs().unwrap().unwrap();
+ check_name(read_child4, &read_debug_str, "child4");
+ // There should be no more entries
+ assert!(entries.next_dfs().unwrap().is_none());
+ }
+}
diff --git a/vendor/gimli/src/write/writer.rs b/vendor/gimli/src/write/writer.rs
new file mode 100644
index 000000000..0785d1686
--- /dev/null
+++ b/vendor/gimli/src/write/writer.rs
@@ -0,0 +1,497 @@
+use 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),
+ _ => {
+ return 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<InitialLengthOffset> {
+ 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};
+ use std::{i64, u64};
+
+ #[test]
+ #[allow(clippy::cyclomatic_complexity)]
+ 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]
+ );
+ }
+}
diff --git a/vendor/gimli/tests/convert_self.rs b/vendor/gimli/tests/convert_self.rs
new file mode 100644
index 000000000..ec8b592d3
--- /dev/null
+++ b/vendor/gimli/tests/convert_self.rs
@@ -0,0 +1,158 @@
+#![cfg(all(feature = "read", feature = "write"))]
+
+use std::env;
+use std::fs::File;
+use std::io::Read;
+use std::path::PathBuf;
+
+use gimli::read;
+use gimli::write::{self, Address, EndianVec};
+use gimli::LittleEndian;
+
+fn read_section(section: &str) -> Vec<u8> {
+ let mut path = PathBuf::new();
+ if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") {
+ path.push(dir);
+ }
+ path.push("fixtures/self");
+ path.push(section);
+
+ println!("Reading section \"{}\" at path {:?}", section, path);
+ assert!(path.is_file());
+ let mut file = File::open(path).unwrap();
+
+ let mut buf = Vec::new();
+ file.read_to_end(&mut buf).unwrap();
+ buf
+}
+
+#[test]
+fn test_convert_debug_info() {
+ // Convert existing sections
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = read::DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_info = read_section("debug_info");
+ let debug_info = read::DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_line = read_section("debug_line");
+ let debug_line = read::DebugLine::new(&debug_line, LittleEndian);
+
+ let debug_str = read_section("debug_str");
+ let debug_str = read::DebugStr::new(&debug_str, LittleEndian);
+
+ let debug_ranges = read_section("debug_ranges");
+ let debug_ranges = read::DebugRanges::new(&debug_ranges, LittleEndian);
+
+ let debug_rnglists = read::DebugRngLists::new(&[], LittleEndian);
+
+ let ranges = gimli::RangeLists::new(debug_ranges, debug_rnglists);
+
+ let debug_loc = read_section("debug_loc");
+ let debug_loc = read::DebugLoc::new(&debug_loc, LittleEndian);
+
+ let debug_loclists = read::DebugLocLists::new(&[], LittleEndian);
+
+ let locations = gimli::LocationLists::new(debug_loc, debug_loclists);
+
+ let dwarf = read::Dwarf {
+ debug_abbrev,
+ debug_info,
+ debug_line,
+ debug_str,
+ ranges,
+ locations,
+ ..Default::default()
+ };
+
+ let mut dwarf = write::Dwarf::from(&dwarf, &|address| Some(Address::Constant(address)))
+ .expect("Should convert DWARF information");
+
+ assert_eq!(dwarf.units.count(), 23);
+ let entries: usize = (0..dwarf.units.count())
+ .map(|i| dwarf.units.get(dwarf.units.id(i)).count())
+ .sum();
+ assert_eq!(entries, 29_560);
+ assert_eq!(dwarf.line_strings.count(), 0);
+ assert_eq!(dwarf.strings.count(), 3921);
+
+ // Write to new sections
+ let mut write_sections = write::Sections::new(EndianVec::new(LittleEndian));
+ dwarf
+ .write(&mut write_sections)
+ .expect("Should write DWARF information");
+ let debug_info_data = write_sections.debug_info.slice();
+ let debug_abbrev_data = write_sections.debug_abbrev.slice();
+ let debug_line_data = write_sections.debug_line.slice();
+ let debug_ranges_data = write_sections.debug_ranges.slice();
+ let debug_loc_data = write_sections.debug_loc.slice();
+ let debug_str_data = write_sections.debug_str.slice();
+ assert_eq!(debug_info_data.len(), 394_930);
+ assert_eq!(debug_abbrev_data.len(), 9701);
+ assert_eq!(debug_line_data.len(), 105_797);
+ assert_eq!(debug_ranges_data.len(), 155_712);
+ assert_eq!(debug_loc_data.len(), 245_168);
+ assert_eq!(debug_str_data.len(), 144_731);
+
+ // Convert new sections
+ let debug_abbrev = read::DebugAbbrev::new(debug_abbrev_data, LittleEndian);
+ let debug_info = read::DebugInfo::new(debug_info_data, LittleEndian);
+ let debug_line = read::DebugLine::new(debug_line_data, LittleEndian);
+ let debug_str = read::DebugStr::new(debug_str_data, LittleEndian);
+ let debug_ranges = read::DebugRanges::new(debug_ranges_data, LittleEndian);
+ let debug_rnglists = read::DebugRngLists::new(&[], LittleEndian);
+ let debug_loc = read::DebugLoc::new(debug_loc_data, LittleEndian);
+ let debug_loclists = read::DebugLocLists::new(&[], LittleEndian);
+
+ let ranges = gimli::RangeLists::new(debug_ranges, debug_rnglists);
+ let locations = gimli::LocationLists::new(debug_loc, debug_loclists);
+
+ let dwarf = read::Dwarf {
+ debug_abbrev,
+ debug_info,
+ debug_line,
+ debug_str,
+ ranges,
+ locations,
+ ..Default::default()
+ };
+
+ let dwarf = write::Dwarf::from(&dwarf, &|address| Some(Address::Constant(address)))
+ .expect("Should convert DWARF information");
+
+ assert_eq!(dwarf.units.count(), 23);
+ let entries: usize = (0..dwarf.units.count())
+ .map(|i| dwarf.units.get(dwarf.units.id(i)).count())
+ .sum();
+ assert_eq!(entries, 29_560);
+ assert_eq!(dwarf.strings.count(), 3921);
+}
+
+#[test]
+fn test_convert_eh_frame() {
+ // Convert existing section
+ let eh_frame = read_section("eh_frame");
+ let mut eh_frame = read::EhFrame::new(&eh_frame, LittleEndian);
+ // The `.eh_frame` fixture data was created on a 64-bit machine.
+ eh_frame.set_address_size(8);
+ let frames = write::FrameTable::from(&eh_frame, &|address| Some(Address::Constant(address)))
+ .expect("Should convert eh_frame information");
+ assert_eq!(frames.cie_count(), 2);
+ assert_eq!(frames.fde_count(), 3482);
+
+ // Write to new section
+ let mut write_eh_frame = write::EhFrame(EndianVec::new(LittleEndian));
+ frames
+ .write_eh_frame(&mut write_eh_frame)
+ .expect("Should write eh_frame information");
+ let eh_frame = write_eh_frame.slice();
+ assert_eq!(eh_frame.len(), 147152);
+
+ // Convert new section
+ let mut eh_frame = read::EhFrame::new(&eh_frame, LittleEndian);
+ eh_frame.set_address_size(8);
+ let frames = write::FrameTable::from(&eh_frame, &|address| Some(Address::Constant(address)))
+ .expect("Should convert eh_frame information");
+ assert_eq!(frames.cie_count(), 2);
+ assert_eq!(frames.fde_count(), 3482);
+}
diff --git a/vendor/gimli/tests/parse_self.rs b/vendor/gimli/tests/parse_self.rs
new file mode 100755
index 000000000..fb316314e
--- /dev/null
+++ b/vendor/gimli/tests/parse_self.rs
@@ -0,0 +1,431 @@
+#![cfg(all(feature = "read", feature = "std", feature = "endian-reader"))]
+
+use gimli::{
+ AttributeValue, DebugAbbrev, DebugAddr, DebugAddrBase, DebugAranges, DebugInfo, DebugLine,
+ DebugLoc, DebugLocLists, DebugPubNames, DebugPubTypes, DebugRanges, DebugRngLists, DebugStr,
+ Encoding, EndianSlice, Expression, LittleEndian, LocationLists, Operation, RangeLists,
+ RangeListsOffset, Reader,
+};
+use std::collections::hash_map::HashMap;
+use std::env;
+use std::fs::File;
+use std::io::Read;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+fn read_section(section: &str) -> Vec<u8> {
+ let mut path = PathBuf::new();
+ if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") {
+ path.push(dir);
+ }
+ path.push("fixtures/self");
+ path.push(section);
+
+ println!("Reading section \"{}\" at path {:?}", section, path);
+ assert!(path.is_file());
+ let mut file = File::open(path).unwrap();
+
+ let mut buf = Vec::new();
+ file.read_to_end(&mut buf).unwrap();
+ buf
+}
+
+fn parse_expression<R: Reader>(expr: Expression<R>, encoding: Encoding) {
+ let mut pc = expr.0.clone();
+ while !pc.is_empty() {
+ Operation::parse(&mut pc, encoding).expect("Should parse operation");
+ }
+
+ // Also attempt to evaluate some of it.
+ let mut eval = expr.evaluation(encoding);
+ eval.set_initial_value(0);
+ eval.evaluate().expect("Should evaluate expression");
+}
+
+fn impl_parse_self_debug_info<R: gimli::Reader>(
+ debug_info: &DebugInfo<R>,
+ debug_abbrev: &DebugAbbrev<R>,
+) {
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+
+ while cursor.next_dfs().expect("Should parse next dfs").is_some() {
+ let entry = cursor.current().expect("Should have a current entry");
+
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
+ if let AttributeValue::Exprloc(expression) = attr.value() {
+ parse_expression(expression, unit.encoding());
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn test_parse_self_debug_info() {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ impl_parse_self_debug_info(&debug_info, &debug_abbrev);
+}
+
+#[test]
+fn test_parse_self_debug_info_with_endian_rc_slice() {
+ let debug_info = read_section("debug_info");
+ let debug_info = Rc::from(&debug_info[..]);
+ let debug_info = gimli::EndianRcSlice::new(debug_info, LittleEndian);
+ let debug_info = DebugInfo::from(debug_info);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = Rc::from(&debug_abbrev[..]);
+ let debug_abbrev = gimli::EndianRcSlice::new(debug_abbrev, LittleEndian);
+ let debug_abbrev = DebugAbbrev::from(debug_abbrev);
+
+ impl_parse_self_debug_info(&debug_info, &debug_abbrev);
+}
+
+#[test]
+fn test_parse_self_debug_line() {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_line = read_section("debug_line");
+ let debug_line = DebugLine::new(&debug_line, LittleEndian);
+
+ let debug_str = read_section("debug_str");
+ let debug_str = DebugStr::new(&debug_str, LittleEndian);
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ cursor.next_dfs().expect("Should parse next dfs");
+
+ let unit_entry = cursor.current().expect("Should have a root entry");
+
+ let comp_dir = unit_entry
+ .attr_value(gimli::DW_AT_comp_dir)
+ .expect("Should parse comp_dir attribute")
+ .and_then(|val| val.string_value(&debug_str));
+ let comp_name = unit_entry
+ .attr_value(gimli::DW_AT_name)
+ .expect("Should parse name attribute")
+ .and_then(|val| val.string_value(&debug_str));
+
+ if let Some(AttributeValue::DebugLineRef(offset)) = unit_entry
+ .attr_value(gimli::DW_AT_stmt_list)
+ .expect("Should parse stmt_list")
+ {
+ let program = debug_line
+ .program(offset, unit.address_size(), comp_dir, comp_name)
+ .expect("should parse line number program header");
+
+ let mut results = Vec::new();
+ let mut rows = program.rows();
+ while let Some((_, row)) = rows
+ .next_row()
+ .expect("Should parse and execute all rows in the line number program")
+ {
+ results.push(*row);
+ }
+ results.reverse();
+
+ let program = debug_line
+ .program(offset, unit.address_size(), comp_dir, comp_name)
+ .expect("should parse line number program header");
+ let (program, sequences) = program
+ .sequences()
+ .expect("should parse and execute the entire line number program");
+ assert!(!sequences.is_empty()); // Should be at least one sequence.
+ for sequence in sequences {
+ let mut rows = program.resume_from(&sequence);
+ while let Some((_, row)) = rows
+ .next_row()
+ .expect("Should parse and execute all rows after resuming")
+ {
+ let other_row = results.pop().unwrap();
+ assert!(row.address() >= sequence.start);
+ assert!(row.address() <= sequence.end);
+ assert_eq!(row.address(), other_row.address());
+ assert_eq!(row.line(), other_row.line());
+ }
+ }
+ assert!(results.is_empty());
+ }
+ }
+}
+
+#[test]
+fn test_parse_self_debug_loc() {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let debug_loc = read_section("debug_loc");
+ let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
+ let debug_loclists = DebugLocLists::new(&[], LittleEndian);
+ let loclists = LocationLists::new(debug_loc, debug_loclists);
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ cursor.next_dfs().expect("Should parse next dfs");
+
+ let mut low_pc = 0;
+
+ {
+ let unit_entry = cursor.current().expect("Should have a root entry");
+ let low_pc_attr = unit_entry
+ .attr_value(gimli::DW_AT_low_pc)
+ .expect("Should parse low_pc");
+ if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
+ low_pc = address;
+ }
+ }
+
+ while cursor.next_dfs().expect("Should parse next dfs").is_some() {
+ let entry = cursor.current().expect("Should have a current entry");
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
+ if let AttributeValue::LocationListsRef(offset) = attr.value() {
+ let mut locs = loclists
+ .locations(
+ offset,
+ unit.encoding(),
+ low_pc,
+ &debug_addr,
+ debug_addr_base,
+ )
+ .expect("Should parse locations OK");
+ while let Some(loc) = locs.next().expect("Should parse next location") {
+ assert!(loc.range.begin <= loc.range.end);
+ parse_expression(loc.data, unit.encoding());
+ }
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn test_parse_self_debug_ranges() {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
+ let debug_addr_base = DebugAddrBase(0);
+
+ let debug_ranges = read_section("debug_ranges");
+ let debug_ranges = DebugRanges::new(&debug_ranges, LittleEndian);
+ let debug_rnglists = DebugRngLists::new(&[], LittleEndian);
+ let rnglists = RangeLists::new(debug_ranges, debug_rnglists);
+
+ let mut iter = debug_info.units();
+ while let Some(unit) = iter.next().expect("Should parse compilation unit") {
+ let abbrevs = unit
+ .abbreviations(&debug_abbrev)
+ .expect("Should parse abbreviations");
+
+ let mut cursor = unit.entries(&abbrevs);
+ cursor.next_dfs().expect("Should parse next dfs");
+
+ let mut low_pc = 0;
+
+ {
+ let unit_entry = cursor.current().expect("Should have a root entry");
+ let low_pc_attr = unit_entry
+ .attr_value(gimli::DW_AT_low_pc)
+ .expect("Should parse low_pc");
+ if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
+ low_pc = address;
+ }
+ }
+
+ while cursor.next_dfs().expect("Should parse next dfs").is_some() {
+ let entry = cursor.current().expect("Should have a current entry");
+ let mut attrs = entry.attrs();
+ while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
+ if let AttributeValue::RangeListsRef(offset) = attr.value() {
+ let mut ranges = rnglists
+ .ranges(
+ RangeListsOffset(offset.0),
+ unit.encoding(),
+ low_pc,
+ &debug_addr,
+ debug_addr_base,
+ )
+ .expect("Should parse ranges OK");
+ while let Some(range) = ranges.next().expect("Should parse next range") {
+ assert!(range.begin <= range.end);
+ }
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn test_parse_self_debug_aranges() {
+ let debug_aranges = read_section("debug_aranges");
+ let debug_aranges = DebugAranges::new(&debug_aranges, LittleEndian);
+
+ let mut headers = debug_aranges.headers();
+ while let Some(header) = headers.next().expect("Should parse arange header OK") {
+ let mut entries = header.entries();
+ while let Some(_) = entries.next().expect("Should parse arange entry OK") {
+ // Not really anything else we can check right now.
+ }
+ }
+}
+
+#[test]
+fn test_parse_self_debug_pubnames() {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_pubnames = read_section("debug_pubnames");
+ let debug_pubnames = DebugPubNames::new(&debug_pubnames, LittleEndian);
+
+ let mut units = HashMap::new();
+ let mut abbrevs = HashMap::new();
+ let mut pubnames = debug_pubnames.items();
+ while let Some(entry) = pubnames.next().expect("Should parse pubname OK") {
+ let unit_offset = entry.unit_header_offset();
+ let unit = units.entry(unit_offset).or_insert_with(|| {
+ debug_info
+ .header_from_offset(unit_offset)
+ .expect("Should parse unit header OK")
+ });
+ let abbrev_offset = unit.debug_abbrev_offset();
+ let abbrevs = abbrevs.entry(abbrev_offset).or_insert_with(|| {
+ debug_abbrev
+ .abbreviations(abbrev_offset)
+ .expect("Should parse abbreviations OK")
+ });
+ let mut cursor = unit
+ .entries_at_offset(abbrevs, entry.die_offset())
+ .expect("DIE offset should be valid");
+ assert!(cursor.next_dfs().expect("Should parse DIE").is_some());
+ }
+}
+
+#[test]
+fn test_parse_self_debug_pubtypes() {
+ let debug_info = read_section("debug_info");
+ let debug_info = DebugInfo::new(&debug_info, LittleEndian);
+
+ let debug_abbrev = read_section("debug_abbrev");
+ let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
+
+ let debug_pubtypes = read_section("debug_pubtypes");
+ let debug_pubtypes = DebugPubTypes::new(&debug_pubtypes, LittleEndian);
+
+ let mut units = HashMap::new();
+ let mut abbrevs = HashMap::new();
+ let mut pubtypes = debug_pubtypes.items();
+ while let Some(entry) = pubtypes.next().expect("Should parse pubtype OK") {
+ let unit_offset = entry.unit_header_offset();
+ let unit = units.entry(unit_offset).or_insert_with(|| {
+ debug_info
+ .header_from_offset(unit_offset)
+ .expect("Should parse unit header OK")
+ });
+ let abbrev_offset = unit.debug_abbrev_offset();
+ let abbrevs = abbrevs.entry(abbrev_offset).or_insert_with(|| {
+ debug_abbrev
+ .abbreviations(abbrev_offset)
+ .expect("Should parse abbreviations OK")
+ });
+ let mut cursor = unit
+ .entries_at_offset(abbrevs, entry.die_offset())
+ .expect("DIE offset should be valid");
+ assert!(cursor.next_dfs().expect("Should parse DIE").is_some());
+ }
+}
+
+#[test]
+fn test_parse_self_eh_frame() {
+ use gimli::{BaseAddresses, CieOrFde, EhFrame, UnwindSection};
+
+ let eh_frame = read_section("eh_frame");
+ let mut eh_frame = EhFrame::new(&eh_frame, LittleEndian);
+ // The `.eh_frame` fixture data was created on a 64-bit machine.
+ eh_frame.set_address_size(8);
+
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_text(0)
+ .set_got(0);
+ let mut entries = eh_frame.entries(&bases);
+ while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
+ match entry {
+ CieOrFde::Cie(cie) => {
+ let mut instrs = cie.instructions(&eh_frame, &bases);
+ while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
+ // TODO FITZGEN
+ }
+ }
+ CieOrFde::Fde(partial) => {
+ let fde = partial
+ .parse(UnwindSection::cie_from_offset)
+ .expect("Should be able to get CIE for FDE");
+
+ let mut instrs = fde.instructions(&eh_frame, &bases);
+ while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
+ // TODO FITZGEN
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn test_parse_self_eh_frame_hdr() {
+ use gimli::{BaseAddresses, EhFrameHdr};
+
+ let eh_frame_hdr = read_section("eh_frame_hdr");
+ let eh_frame_hdr = EhFrameHdr::new(&eh_frame_hdr, LittleEndian);
+
+ let bases = BaseAddresses::default()
+ .set_eh_frame(0)
+ .set_eh_frame_hdr(0)
+ .set_text(0)
+ .set_got(0);
+
+ // `.eh_frame_hdr` was generated on a 64 bit machine.
+ let address_size = 8;
+
+ let _parsed_header = eh_frame_hdr
+ .parse(&bases, address_size)
+ .expect("we can parse the `.eh_frame_hdr` section OK");
+}