Add backtrace dependency for libafl crate usage Due to the size of crrev.com/c/7564051, this CL works to split all of the unsafe dependencies into separate CLs for individual review. The end goal is to have all unsafe transitive dependencies for libafl imported and reviewed prior to importing the remaining dependencies (including libafl). We have approval for importing libafl by Chrome TSCs/ATLs (see https://groups.google.com/a/google.com/g/chrome-tsc-discuss/c/jvyNkbHOLhM). The backtrace crate has also been marked as `group = test` and `allow_first_party_usage = false`. Bug: 495772162 Change-Id: I973207bf387bcdd1ba497226da7a76d7fc683bcf Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7697596 Reviewed-by: Ćukasz Anforowicz <lukasza@chromium.org> Commit-Queue: Antonio Alphonse <alphonsea@google.com> Cr-Commit-Position: refs/heads/main@{#1609463} NOKEYCHECK=True GitOrigin-RevId: 3b101ccecc71a7743783e7a68b8fc7113b1accc1
diff --git a/addr2line/v0_25/BUILD.gn b/addr2line/v0_25/BUILD.gn new file mode 100644 index 0000000..e20495f --- /dev/null +++ b/addr2line/v0_25/BUILD.gn
@@ -0,0 +1,80 @@ +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# @generated from third_party/rust/chromium_crates_io/BUILD.gn.hbs by +# tools/crates/gnrt. +# Do not edit! + +import("//build/rust/cargo_crate.gni") + +cargo_crate("lib") { + crate_name = "addr2line" + epoch = "0.25" + enabled = !is_win + crate_type = "rlib" + crate_root = + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/lib.rs" + sources = [ + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/bin/addr2line.rs", + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/frame.rs", + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/function.rs", + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/lib.rs", + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/line.rs", + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/loader.rs", + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/lookup.rs", + "//third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/src/unit.rs", + ] + inputs = [] + + build_native_rust_unit_tests = false + edition = "2018" + cargo_pkg_name = "addr2line" + cargo_pkg_description = + "A cross-platform symbolication library written in Rust, using `gimli`" + cargo_pkg_repository = "https://github.com/gimli-rs/addr2line" + cargo_pkg_version = "0.25.1" + + allow_unsafe = true + + deps = [] + if (!is_win) { + deps += [ "//third_party/rust/gimli/v0_32:lib" ] + } + + # Only for usage from third-party crates. Add the crate to + # //third_party/rust/chromium_crates_io/Cargo.toml to use + # it from first-party code. + visibility = [ "//third_party/rust/*" ] + testonly = true + + ##################################################################### + # Tweaking which GN `config`s apply to this target. + + # Config changes that apply to all `//third_party/rust` crates. + _configs_to_remove = [ + # We don't need code coverage data for any `chromium_crates_io` crates. + "//build/config/coverage:default_coverage", + + # This is third-party code, so remove `chromium_code` config. We are not + # at the same time adding `//build/config/compiler:no_chromium_code`, + # because 1) we don't want to pull how warnings are handled by that config + # and 2) that config doesn't have any non-warnings-related stuff. + "//build/config/compiler:chromium_code", + ] + _configs_to_add = [] + + # Changing (if needed) which configs apply to this specific crate (based on + # `extra_kv.configs_to_remove` and `extra_kv.configs_to_add` from + # `gnrt_config.toml`). + _configs_to_remove += [] + _configs_to_add += [] + + # Applying config changes. + library_configs -= _configs_to_remove + library_configs += _configs_to_add + executable_configs -= _configs_to_remove + executable_configs += _configs_to_add + proc_macro_configs -= _configs_to_remove + proc_macro_configs += _configs_to_add +}
diff --git a/addr2line/v0_25/README.chromium b/addr2line/v0_25/README.chromium new file mode 100644 index 0000000..9f12707 --- /dev/null +++ b/addr2line/v0_25/README.chromium
@@ -0,0 +1,11 @@ +Name: addr2line +URL: https://crates.io/crates/addr2line +Version: 0.25.1 +Revision: f02db009deb9b441818afa49cb1b17453c1e4243 +Update Mechanism: Manual (https://crbug.com/449898466) +License: Apache-2.0 +License File: //third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-APACHE +Shipped: no +Security Critical: no + +Description: A cross-platform symbolication library written in Rust, using `gimli`
diff --git a/arbitrary_int/v2/README.chromium b/arbitrary_int/v2/README.chromium index b68d988..cc539d0 100644 --- a/arbitrary_int/v2/README.chromium +++ b/arbitrary_int/v2/README.chromium
@@ -5,7 +5,7 @@ Update Mechanism: Manual (https://crbug.com/449898466) License: MIT License File: //third_party/rust/chromium_crates_io/vendor/arbitrary-int-v2/LICENSE.txt -Shipped: yes -Security Critical: yes +Shipped: no +Security Critical: no Description: Modern and lightweight implementation of u2, u3, u4, ..., u127.
diff --git a/backtrace/v0_3/BUILD.gn b/backtrace/v0_3/BUILD.gn new file mode 100644 index 0000000..07f9f96 --- /dev/null +++ b/backtrace/v0_3/BUILD.gn
@@ -0,0 +1,118 @@ +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# @generated from third_party/rust/chromium_crates_io/BUILD.gn.hbs by +# tools/crates/gnrt. +# Do not edit! + +import("//build/rust/cargo_crate.gni") + +cargo_crate("lib") { + crate_name = "backtrace" + epoch = "0.3" + crate_type = "rlib" + crate_root = + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/lib.rs" + sources = [ + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/libunwind.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/miri.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/noop.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win32.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win64.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/capture.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/dbghelp.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/lib.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/print.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/print/fuchsia.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/dbghelp.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/coff.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/elf.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_aix.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_dl_iterate_phdr.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_haiku.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_illumos.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_libnx.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_macos.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_windows.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/lru.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/macho.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_fake.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_unix.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_windows.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/parse_running_mmaps_unix.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/stash.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/xcoff.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/miri.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/noop.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/types.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys.rs", + "//third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys_arm32_shim.rs", + ] + inputs = [] + + build_native_rust_unit_tests = false + edition = "2021" + cargo_pkg_authors = "The Rust Project Developers" + cargo_pkg_name = "backtrace" + cargo_pkg_description = "A library to acquire a stack trace (backtrace) at runtime in a Rust program." + cargo_pkg_repository = "https://github.com/rust-lang/backtrace-rs" + cargo_pkg_version = "0.3.76" + + allow_unsafe = true + + deps = [ + "//third_party/rust/cfg_if/v1:lib", + "//third_party/rust/rustc_demangle/v0_1:lib", + ] + if (!is_win) { + deps += [ + "//third_party/rust/addr2line/v0_25:lib", + "//third_party/rust/libc/v0_2:lib", + "//third_party/rust/miniz_oxide/v0_8:lib", + "//third_party/rust/object/v0_37:lib", + ] + } + if (is_win) { + deps += [ "//third_party/rust/windows_link/v0_2:lib" ] + } + features = [ + "default", + "std", + ] + + testonly = true + + ##################################################################### + # Tweaking which GN `config`s apply to this target. + + # Config changes that apply to all `//third_party/rust` crates. + _configs_to_remove = [ + # We don't need code coverage data for any `chromium_crates_io` crates. + "//build/config/coverage:default_coverage", + + # This is third-party code, so remove `chromium_code` config. We are not + # at the same time adding `//build/config/compiler:no_chromium_code`, + # because 1) we don't want to pull how warnings are handled by that config + # and 2) that config doesn't have any non-warnings-related stuff. + "//build/config/compiler:chromium_code", + ] + _configs_to_add = [] + + # Changing (if needed) which configs apply to this specific crate (based on + # `extra_kv.configs_to_remove` and `extra_kv.configs_to_add` from + # `gnrt_config.toml`). + _configs_to_remove += [] + _configs_to_add += [] + + # Applying config changes. + library_configs -= _configs_to_remove + library_configs += _configs_to_add + executable_configs -= _configs_to_remove + executable_configs += _configs_to_add + proc_macro_configs -= _configs_to_remove + proc_macro_configs += _configs_to_add +}
diff --git a/backtrace/v0_3/README.chromium b/backtrace/v0_3/README.chromium new file mode 100644 index 0000000..3c1adca --- /dev/null +++ b/backtrace/v0_3/README.chromium
@@ -0,0 +1,12 @@ +Name: backtrace +URL: https://crates.io/crates/backtrace +Version: 0.3.76 +Revision: 775f6a1ba62e7d35a1fac76e64c61d9d4687b5f2 +Update Mechanism: Manual (https://crbug.com/449898466) +License: Apache-2.0 +License File: //third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-APACHE +Shipped: no +Security Critical: no + +Description: A library to acquire a stack trace (backtrace) at runtime in a Rust program. +
diff --git a/chromium_crates_io/Cargo.lock b/chromium_crates_io/Cargo.lock index cd11c45..7b080b3 100644 --- a/chromium_crates_io/Cargo.lock +++ b/chromium_crates_io/Cargo.lock
@@ -3,6 +3,14 @@ version = 4 [[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gimli", +] + +[[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -82,6 +90,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -185,6 +207,7 @@ "antlr4rust", "anyhow", "arbitrary-int", + "backtrace", "base64", "bitflags 2.11.0", "bytemuck", @@ -494,6 +517,11 @@ ] [[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1018,6 +1046,14 @@ ] [[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr", +] + +[[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/chromium_crates_io/Cargo.toml b/chromium_crates_io/Cargo.toml index 3ad566c..d1cc86a 100644 --- a/chromium_crates_io/Cargo.toml +++ b/chromium_crates_io/Cargo.toml
@@ -21,6 +21,7 @@ antlr4rust = "0.5.2" anyhow = "1.0.98" arbitrary-int = "2.1.1" #TODO(crbug.com/496920769): Remove once libafl is added as a dependency. +backtrace = "0.3.76" #TODO(crbug.com/496920769): Remove once libafl is added as a dependency. base64 = "0.22" bitflags = "2" bytes = "1"
diff --git a/chromium_crates_io/gnrt_config.toml b/chromium_crates_io/gnrt_config.toml index ea593e0..c499b1a 100644 --- a/chromium_crates_io/gnrt_config.toml +++ b/chromium_crates_io/gnrt_config.toml
@@ -111,6 +111,10 @@ remove_crates = [] root = "chromium" +[crate.addr2line] +allow_first_party_usage = false +extra_kv = { allow_unsafe = true } + [crate.adler2.extra_kv] allow_unsafe = false @@ -150,6 +154,11 @@ [crate.autocfg.extra_kv] allow_unsafe = false +[crate.backtrace] +group = 'test' +allow_first_party_usage = false +extra_kv = { allow_unsafe = true } + [crate.base64.extra_kv] allow_unsafe = false @@ -299,6 +308,10 @@ allow_first_party_usage = false extra_kv = { allow_unsafe = true } +[crate.gimli] +allow_first_party_usage = false +extra_kv = { allow_unsafe = true } + [crate.hashbrown.extra_kv] # `hashbrown` crate is implicitly trusted because it comes from # https://github.com/rust-lang/ (and we already implicitly trust maintainers of @@ -524,6 +537,10 @@ remove_build_rs = true extra_kv = { allow_unsafe = true } +[crate.object] +allow_first_party_usage = false +extra_kv = { allow_unsafe = true } + [crate.once_cell] extra_kv = { allow_unsafe = true }
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/.cargo-checksum.json b/chromium_crates_io/vendor/addr2line-v0_25/.cargo-checksum.json new file mode 100644 index 0000000..697c9ce --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/.cargo-checksum.json
@@ -0,0 +1 @@ +{"files":{}}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/.cargo_vcs_info.json b/chromium_crates_io/vendor/addr2line-v0_25/.cargo_vcs_info.json new file mode 100644 index 0000000..1c33eaf --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "f02db009deb9b441818afa49cb1b17453c1e4243" + }, + "path_in_vcs": "" +} \ No newline at end of file
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/CHANGELOG.md b/chromium_crates_io/vendor/addr2line-v0_25/CHANGELOG.md new file mode 100644 index 0000000..5c2cc45 --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/CHANGELOG.md
@@ -0,0 +1,485 @@ +# `addr2line` Change Log + +-------------------------------------------------------------------------------- + +## 0.25.1 (2025/09/13) + +### Changed + +* Fixed line parsing for split DWARF. + [#353](https://github.com/gimli-rs/addr2line/pull/353) + +* Changed `.debug_aranges` parsing to skip invalid entries instead of failing. + [#355](https://github.com/gimli-rs/addr2line/pull/355) + +-------------------------------------------------------------------------------- + +## 0.25.0 (2025/06/11) + +### Breaking changes + +* Updated `gimli` dependency. + +### Added + +* Added `Loader::find_symbol`. + [#341](https://github.com/gimli-rs/addr2line/pull/341) + [#349](https://github.com/gimli-rs/addr2line/pull/349) + +* Added `Loader::get_section_range`. + Added `--section` option to `addr2line` binary. + [#343](https://github.com/gimli-rs/addr2line/pull/343) + +* Added `wasm` feature. + [#348](https://github.com/gimli-rs/addr2line/pull/348) + +### Changed + +* Fixed handling of Windows paths that use forward slashes. + [#342](https://github.com/gimli-rs/addr2line/pull/342) + +* Removed `compiler-builtins` from `rustc-dep-of-std` dependencies. + [#345](https://github.com/gimli-rs/addr2line/pull/345) + +-------------------------------------------------------------------------------- + +## 0.24.2 (2024/10/04) + +### Changed + +* Enabled caching of DWARF abbreviations. + [#318](https://github.com/gimli-rs/addr2line/pull/318) + +* Changed the `addr2line` binary to prefer symbol names over DWARF names. + [#332](https://github.com/gimli-rs/addr2line/pull/332) + +* Updated `gimli` dependency. + +### Added + +* Added `Context::from_arc_dwarf`. + [#327](https://github.com/gimli-rs/addr2line/pull/327) + +* Added benchmark comparison. + [#315](https://github.com/gimli-rs/addr2line/pull/315) + [#321](https://github.com/gimli-rs/addr2line/pull/321) + [#322](https://github.com/gimli-rs/addr2line/pull/322) + [#325](https://github.com/gimli-rs/addr2line/pull/325) + +* Added more tests. + [#328](https://github.com/gimli-rs/addr2line/pull/328) + [#330](https://github.com/gimli-rs/addr2line/pull/330) + [#331](https://github.com/gimli-rs/addr2line/pull/331) + [#333](https://github.com/gimli-rs/addr2line/pull/333) + +-------------------------------------------------------------------------------- + +## 0.24.1 (2024/07/26) + +### Changed + +* Fixed parsing of partial units, which are found in supplementary object files. + [#313](https://github.com/gimli-rs/addr2line/pull/313) + +-------------------------------------------------------------------------------- + +## 0.24.0 (2024/07/16) + +### Breaking changes + +* Updated `gimli` dependency. + +### Changed + +* Changed the order of ranges returned by `Context::find_location_range`, and + fixed handling in rare situations. + [#303](https://github.com/gimli-rs/addr2line/pull/303) + [#304](https://github.com/gimli-rs/addr2line/pull/304) + [#306](https://github.com/gimli-rs/addr2line/pull/306) + +* Improved the performance of `Context::find_location`. + [#305](https://github.com/gimli-rs/addr2line/pull/305) + +### Added + +* Added `LoaderReader`. + [#307](https://github.com/gimli-rs/addr2line/pull/307) + +* Added `--all` option to `addr2line`. + [#307](https://github.com/gimli-rs/addr2line/pull/307) + +-------------------------------------------------------------------------------- + +## 0.23.0 (2024/05/26) + +### Breaking changes + +* Updated `gimli` dependency. + +* Deleted `Context::new`, `Context::new_with_sup`, and `builtin_split_dwarf_loader`. + Use `Context::from_dwarf` or `Loader::new` instead. + This removes `object` from the public API. + [#296](https://github.com/gimli-rs/addr2line/pull/296) + +### Changed + +* Fixed handling of column 0 in the line table. + [#290](https://github.com/gimli-rs/addr2line/pull/290) + +* Moved `addr2line` from `examples` to `bin`. Requires the `bin` feature. + [#291](https://github.com/gimli-rs/addr2line/pull/291) + +* Split up `lib.rs` into smaller modules. + [#292](https://github.com/gimli-rs/addr2line/pull/292) + +### Added + +* Added `Loader`. Requires the `loader` feature. + [#296](https://github.com/gimli-rs/addr2line/pull/296) + [#297](https://github.com/gimli-rs/addr2line/pull/297) + +* Added unpacked Mach-O support to `Loader`. + [#298](https://github.com/gimli-rs/addr2line/pull/298) + +-------------------------------------------------------------------------------- + +## 0.22.0 (2024/04/11) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +-------------------------------------------------------------------------------- + +## 0.21.0 (2023/08/12) + +### Breaking changes + +* Updated `gimli`, `object`, and `fallible-iterator` dependencies. + +### Changed + +* The minimum supported rust version is 1.65.0. + +* Store boxed slices instead of `Vec` objects in `Context`. + [#278](https://github.com/gimli-rs/addr2line/pull/278) + +-------------------------------------------------------------------------------- + +## 0.20.0 (2023/04/15) + +### Breaking changes + +* The minimum supported rust version is 1.58.0. + +* Changed `Context::find_frames` to return `LookupResult`. + Use `LookupResult::skip_all_loads` to obtain the result without loading split DWARF. + [#260](https://github.com/gimli-rs/addr2line/pull/260) + +* Replaced `Context::find_dwarf_unit` with `Context::find_dwarf_and_unit`. + [#260](https://github.com/gimli-rs/addr2line/pull/260) + +* Updated `object` dependency. + +### Changed + +* Fix handling of file index 0 for DWARF 5. + [#264](https://github.com/gimli-rs/addr2line/pull/264) + +### Added + +* Added types and methods to support loading split DWARF: + `LookupResult`, `SplitDwarfLoad`, `SplitDwarfLoader`, `Context::preload_units`. + [#260](https://github.com/gimli-rs/addr2line/pull/260) + [#262](https://github.com/gimli-rs/addr2line/pull/262) + [#263](https://github.com/gimli-rs/addr2line/pull/263) + +-------------------------------------------------------------------------------- + +## 0.19.0 (2022/11/24) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +-------------------------------------------------------------------------------- + +## 0.18.0 (2022/07/16) + +### Breaking changes + +* Updated `object` dependency. + +### Changed + +* Fixed handling of relative path for `DW_AT_comp_dir`. + [#239](https://github.com/gimli-rs/addr2line/pull/239) + +* Fixed handling of `DW_FORM_addrx` for DWARF 5 support. + [#243](https://github.com/gimli-rs/addr2line/pull/243) + +* Fixed handling of units that are missing range information. + [#249](https://github.com/gimli-rs/addr2line/pull/249) + +-------------------------------------------------------------------------------- + +## 0.17.0 (2021/10/24) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +### Changed + +* Use `skip_attributes` to improve performance. + [#236](https://github.com/gimli-rs/addr2line/pull/236) + +-------------------------------------------------------------------------------- + +## 0.16.0 (2021/07/26) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +-------------------------------------------------------------------------------- + +## 0.15.2 (2021/06/04) + +### Fixed + +* Allow `Context` to be `Send`. + [#219](https://github.com/gimli-rs/addr2line/pull/219) + +-------------------------------------------------------------------------------- + +## 0.15.1 (2021/05/02) + +### Fixed + +* Don't ignore aranges with address 0. + [#217](https://github.com/gimli-rs/addr2line/pull/217) + +-------------------------------------------------------------------------------- + +## 0.15.0 (2021/05/02) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + [#215](https://github.com/gimli-rs/addr2line/pull/215) + +* Added `debug_aranges` parameter to `Context::from_sections`. + [#200](https://github.com/gimli-rs/addr2line/pull/200) + +### Added + +* Added `.debug_aranges` support. + [#200](https://github.com/gimli-rs/addr2line/pull/200) + +* Added supplementary object file support. + [#208](https://github.com/gimli-rs/addr2line/pull/208) + +### Fixed + +* Fixed handling of Windows paths in locations. + [#209](https://github.com/gimli-rs/addr2line/pull/209) + +* examples/addr2line: Flush stdout after each response. + [#210](https://github.com/gimli-rs/addr2line/pull/210) + +* examples/addr2line: Avoid copying every section. + [#213](https://github.com/gimli-rs/addr2line/pull/213) + +-------------------------------------------------------------------------------- + +## 0.14.1 (2020/12/31) + +### Fixed + +* Fix location lookup for skeleton units. + [#201](https://github.com/gimli-rs/addr2line/pull/201) + +### Added + +* Added `Context::find_location_range`. + [#196](https://github.com/gimli-rs/addr2line/pull/196) + [#199](https://github.com/gimli-rs/addr2line/pull/199) + +-------------------------------------------------------------------------------- + +## 0.14.0 (2020/10/27) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +### Fixed + +* Handle units that only have line information. + [#188](https://github.com/gimli-rs/addr2line/pull/188) + +* Handle DWARF units with version <= 4 and no `DW_AT_name`. + [#191](https://github.com/gimli-rs/addr2line/pull/191) + +* Fix handling of `DW_FORM_ref_addr`. + [#193](https://github.com/gimli-rs/addr2line/pull/193) + +-------------------------------------------------------------------------------- + +## 0.13.0 (2020/07/07) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +* Added `rustc-dep-of-std` feature. + [#166](https://github.com/gimli-rs/addr2line/pull/166) + +### Changed + +* Improve performance by parsing function contents lazily. + [#178](https://github.com/gimli-rs/addr2line/pull/178) + +* Don't skip `.debug_info` and `.debug_line` entries with a zero address. + [#182](https://github.com/gimli-rs/addr2line/pull/182) + +-------------------------------------------------------------------------------- + +## 0.12.2 (2020/06/21) + +### Fixed + +* Avoid linear search for `DW_FORM_ref_addr`. + [#175](https://github.com/gimli-rs/addr2line/pull/175) + +-------------------------------------------------------------------------------- + +## 0.12.1 (2020/05/19) + +### Fixed + +* Handle units with overlapping address ranges. + [#163](https://github.com/gimli-rs/addr2line/pull/163) + +* Don't assert for functions with overlapping address ranges. + [#168](https://github.com/gimli-rs/addr2line/pull/168) + +-------------------------------------------------------------------------------- + +## 0.12.0 (2020/05/12) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +* Added more optional features: `smallvec` and `fallible-iterator`. + [#160](https://github.com/gimli-rs/addr2line/pull/160) + +### Added + +* Added `Context::dwarf` and `Context::find_dwarf_unit`. + [#159](https://github.com/gimli-rs/addr2line/pull/159) + +### Changed + +* Removed `lazycell` dependency. + [#160](https://github.com/gimli-rs/addr2line/pull/160) + +-------------------------------------------------------------------------------- + +## 0.11.0 (2020/01/11) + +### Breaking changes + +* Updated `gimli` and `object` dependencies. + +* [#130](https://github.com/gimli-rs/addr2line/pull/130) + Changed `Location::file` from `Option<String>` to `Option<&str>`. + This required adding lifetime parameters to `Location` and other structs that + contain it. + +* [#152](https://github.com/gimli-rs/addr2line/pull/152) + Changed `Location::line` and `Location::column` from `Option<u64>`to `Option<u32>`. + +* [#156](https://github.com/gimli-rs/addr2line/pull/156) + Deleted `alloc` feature, and fixed `no-std` builds with stable rust. + Removed default `Reader` parameter for `Context`, and added `ObjectContext` instead. + +### Added + +* [#134](https://github.com/gimli-rs/addr2line/pull/134) + Added `Context::from_dwarf`. + +### Changed + +* [#133](https://github.com/gimli-rs/addr2line/pull/133) + Fixed handling of units that can't be parsed. + +* [#155](https://github.com/gimli-rs/addr2line/pull/155) + Fixed `addr2line` output to match binutils. + +* [#130](https://github.com/gimli-rs/addr2line/pull/130) + Improved `.debug_line` parsing performance. + +* [#148](https://github.com/gimli-rs/addr2line/pull/148) + [#150](https://github.com/gimli-rs/addr2line/pull/150) + [#151](https://github.com/gimli-rs/addr2line/pull/151) + [#152](https://github.com/gimli-rs/addr2line/pull/152) + Improved `.debug_info` parsing performance. + +* [#137](https://github.com/gimli-rs/addr2line/pull/137) + [#138](https://github.com/gimli-rs/addr2line/pull/138) + [#139](https://github.com/gimli-rs/addr2line/pull/139) + [#140](https://github.com/gimli-rs/addr2line/pull/140) + [#146](https://github.com/gimli-rs/addr2line/pull/146) + Improved benchmarks. + +-------------------------------------------------------------------------------- + +## 0.10.0 (2019/07/07) + +### Breaking changes + +* [#127](https://github.com/gimli-rs/addr2line/pull/127) + Update `gimli`. + +-------------------------------------------------------------------------------- + +## 0.9.0 (2019/05/02) + +### Breaking changes + +* [#121](https://github.com/gimli-rs/addr2line/pull/121) + Update `gimli`, `object`, and `fallible-iterator` dependencies. + +### Added + +* [#121](https://github.com/gimli-rs/addr2line/pull/121) + Reexport `gimli`, `object`, and `fallible-iterator`. + +-------------------------------------------------------------------------------- + +## 0.8.0 (2019/02/06) + +### Breaking changes + +* [#107](https://github.com/gimli-rs/addr2line/pull/107) + Update `object` dependency to 0.11. This is part of the public API. + +### Added + +* [#101](https://github.com/gimli-rs/addr2line/pull/101) + Add `object` feature (enabled by default). Disable this feature to remove + the `object` dependency and `Context::new` API. + +* [#102](https://github.com/gimli-rs/addr2line/pull/102) + Add `std` (enabled by default) and `alloc` features. + +### Changed + +* [#108](https://github.com/gimli-rs/addr2line/issues/108) + `demangle` no longer outputs the hash for rust symbols. + +* [#109](https://github.com/gimli-rs/addr2line/issues/109) + Set default `R` for `Context<R>`.
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/Cargo.lock b/chromium_crates_io/vendor/addr2line-v0_25/Cargo.lock new file mode 100644 index 0000000..47feaae --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/Cargo.lock
@@ -0,0 +1,1089 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli 0.31.1", +] + +[[package]] +name = "addr2line" +version = "0.25.1" +dependencies = [ + "backtrace", + "clap", + "cpp_demangle", + "criterion", + "fallible-iterator", + "findshlibs", + "gimli 0.32.3", + "libtest-mimic", + "memmap2", + "object 0.37.3", + "rustc-demangle", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "smallvec", + "typed-arena", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line 0.24.2", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.36.7", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "cpp_demangle" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.0", +] + +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +dependencies = [ + "fallible-iterator", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "stable_deref_trait", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libtest-mimic" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" +dependencies = [ + "anstream", + "anstyle", + "clap", + "escape8259", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memmap2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +dependencies = [ + "libc", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "flate2", + "memchr", + "ruzstd", + "wasmparser", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3" + +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9c45b374136f52f2d6311062c7146bff20fec063c3f5d46a410bd937746955" + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.0", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ruzstd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "terminal_size" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +dependencies = [ + "rustix", + "windows-sys 0.60.2", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "twox-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmparser" +version = "0.236.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b1e81f3eb254cf7404a82cee6926a4a3ccc5aad80cc3d43608a070c67aa1d7" +dependencies = [ + "bitflags", +] + +[[package]] +name = "web-sys" +version = "0.3.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/Cargo.toml b/chromium_crates_io/vendor/addr2line-v0_25/Cargo.toml new file mode 100644 index 0000000..c734b4c --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/Cargo.toml
@@ -0,0 +1,164 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.81" +name = "addr2line" +version = "0.25.1" +build = false +include = [ + "/CHANGELOG.md", + "/Cargo.lock", + "/Cargo.toml", + "/LICENSE-APACHE", + "/LICENSE-MIT", + "/README.md", + "/src", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A cross-platform symbolication library written in Rust, using `gimli`" +documentation = "https://docs.rs/addr2line" +readme = "README.md" +keywords = [ + "DWARF", + "debug", + "elf", + "symbolicate", + "atos", +] +categories = ["development-tools::debugging"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/gimli-rs/addr2line" + +[features] +all = [ + "bin", + "wasm", +] +bin = [ + "loader", + "rustc-demangle", + "cpp_demangle", + "fallible-iterator", + "smallvec", + "dep:clap", +] +cargo-all = [] +default = [ + "rustc-demangle", + "cpp_demangle", + "loader", + "fallible-iterator", + "smallvec", +] +loader = [ + "std", + "dep:object", + "dep:memmap2", + "dep:typed-arena", +] +rustc-dep-of-std = [ + "core", + "alloc", + "gimli/rustc-dep-of-std", +] +std = ["gimli/std"] +wasm = ["object/wasm"] + +[lib] +name = "addr2line" +path = "src/lib.rs" + +[[bin]] +name = "addr2line" +path = "src/bin/addr2line.rs" +required-features = ["bin"] + +[dependencies.alloc] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-alloc" + +[dependencies.clap] +version = "4.3.21" +features = ["wrap_help"] +optional = true + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" + +[dependencies.cpp_demangle] +version = "0.4" +features = ["alloc"] +optional = true +default-features = false + +[dependencies.fallible-iterator] +version = "0.3.0" +optional = true +default-features = false + +[dependencies.gimli] +version = "0.32.0" +features = ["read"] +default-features = false + +[dependencies.memmap2] +version = "0.9.4" +optional = true + +[dependencies.object] +version = "0.37.0" +features = [ + "read", + "compression", +] +optional = true +default-features = false + +[dependencies.rustc-demangle] +version = "0.1" +optional = true + +[dependencies.smallvec] +version = "1" +optional = true +default-features = false + +[dependencies.typed-arena] +version = "2" +optional = true + +[dev-dependencies.backtrace] +version = "0.3.13" + +[dev-dependencies.criterion] +version = "0.7.0" + +[dev-dependencies.findshlibs] +version = "0.10" + +[dev-dependencies.libtest-mimic] +version = "0.8.1" + +[profile.bench] +codegen-units = 1 +debug = 2 + +[profile.release] +debug = 2
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/Cargo.toml.orig b/chromium_crates_io/vendor/addr2line-v0_25/Cargo.toml.orig new file mode 100644 index 0000000..d155a15 --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/Cargo.toml.orig
@@ -0,0 +1,92 @@ +[package] +name = "addr2line" +version = "0.25.1" +description = "A cross-platform symbolication library written in Rust, using `gimli`" +documentation = "https://docs.rs/addr2line" +keywords = ["DWARF", "debug", "elf", "symbolicate", "atos"] +categories = ["development-tools::debugging"] +license = "Apache-2.0 OR MIT" +readme = "./README.md" +repository = "https://github.com/gimli-rs/addr2line" +edition = "2018" +rust-version = "1.81" +include = [ + "/CHANGELOG.md", + "/Cargo.lock", + "/Cargo.toml", + "/LICENSE-APACHE", + "/LICENSE-MIT", + "/README.md", + "/src", +] + +[dependencies] +gimli = { version = "0.32.0", default-features = false, features = ["read"] } +fallible-iterator = { version = "0.3.0", default-features = false, optional = true } +smallvec = { version = "1", default-features = false, optional = true } +rustc-demangle = { version = "0.1", optional = true } +cpp_demangle = { version = "0.4", default-features = false, features = ["alloc"], optional = true } + +# loader dependencies +object = { version = "0.37.0", default-features = false, features = ["read", "compression"], optional = true } +memmap2 = { version = "0.9.4", optional = true } +typed-arena = { version = "2", optional = true } + +# bin dependencies +clap = { version = "4.3.21", features = ["wrap_help"], optional = true } + +# Internal feature, only used when building as part of libstd, not part of the +# stable interface of this crate. +core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } +alloc = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-alloc' } + +[dev-dependencies] +backtrace = "0.3.13" +criterion = "0.7.0" +findshlibs = "0.10" +libtest-mimic = "0.8.1" +auxiliary = { path = "tests/auxiliary" } + +[profile.release] +debug = true + +[profile.bench] +debug = true +codegen-units = 1 + +[features] +default = ["rustc-demangle", "cpp_demangle", "loader", "fallible-iterator", "smallvec"] +std = ["gimli/std"] +wasm = ["object/wasm"] +loader = ["std", "dep:object", "dep:memmap2", "dep:typed-arena"] +bin = ["loader", "rustc-demangle", "cpp_demangle", "fallible-iterator", "smallvec", "dep:clap"] +all = ["bin", "wasm"] + +# Use of --all-features is not supported. +# This is a dummy feature to detect when --all-features is used. +cargo-all = [] + +# Internal feature, only used when building as part of libstd, not part of the +# stable interface of this crate. +rustc-dep-of-std = ['core', 'alloc', 'gimli/rustc-dep-of-std'] + +[[test]] +name = "testinput" +harness = false +required-features = ["bin"] + +[[test]] +name = "correctness" +required-features = ["loader", "fallible-iterator"] + +[[test]] +name = "parse" +required-features = ["loader"] + +[[bin]] +name = "addr2line" +required-features = ["bin"] + +[[bench]] +name = "bench" +harness = false
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-APACHE b/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/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/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-MIT b/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-MIT new file mode 100644 index 0000000..3a03f1f --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-MIT
@@ -0,0 +1,25 @@ +Copyright (c) 2016-2018 The gimli Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/README.md b/chromium_crates_io/vendor/addr2line-v0_25/README.md new file mode 100644 index 0000000..74cf333 --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/README.md
@@ -0,0 +1,50 @@ +# addr2line + +[](https://crates.io/crates/addr2line) +[](https://docs.rs/addr2line) +[](https://coveralls.io/github/gimli-rs/addr2line?branch=master) + +`addr2line` provides a cross-platform library for retrieving per-address debug information +from files with DWARF debug information. Given an address, it can return the file name, +line number, and function name associated with that address, as well as the inline call +stack leading to that address. + +The crate has a CLI wrapper around the library which provides some of +the functionality of the `addr2line` command line tool distributed with +[GNU binutils](https://sourceware.org/binutils/docs/binutils/addr2line.html). + +# Quickstart + - Add the [`addr2line` crate](https://crates.io/crates/addr2line) to your `Cargo.toml`. + - Call [`addr2line::Loader::new`](https://docs.rs/addr2line/*/addr2line/struct.Loader.html#method.new) with the file path. + - Use [`addr2line::Loader::find_location`](https://docs.rs/addr2line/*/addr2line/struct.Loader.html#method.find_location) + or [`addr2line::Loader::find_frames`](https://docs.rs/addr2line/*/addr2line/struct.Loader.html#method.find_frames) + to look up debug information for an address. + +If you want to provide your own file loading and memory management, use +[`addr2line::Context`](https://docs.rs/addr2line/*/addr2line/struct.Context.html) +instead of `addr2line::Loader`. + +# Performance + +`addr2line` optimizes for speed over memory by caching parsed information. +The DWARF information is parsed lazily where possible. + +The library aims to perform similarly to equivalent existing tools such +as `addr2line` from binutils, `eu-addr2line` from elfutils, and +`llvm-addr2line` from the llvm project. Current benchmarks show a performance +improvement in all cases: + + + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([`LICENSE-MIT`](./LICENSE-MIT) or https://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions.
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/bin/addr2line.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/bin/addr2line.rs new file mode 100644 index 0000000..150ee9a --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/bin/addr2line.rs
@@ -0,0 +1,313 @@ +use fallible_iterator::FallibleIterator; +use std::borrow::Cow; +use std::io::{BufRead, Lines, StdinLock, Write}; +use std::path::{Path, PathBuf}; + +use clap::{Arg, ArgAction, Command}; + +use addr2line::{Loader, LoaderReader, Location}; + +fn parse_uint_from_hex_string(string: &str) -> Option<u64> { + if string.len() > 2 && string.starts_with("0x") { + u64::from_str_radix(&string[2..], 16).ok() + } else { + u64::from_str_radix(string, 16).ok() + } +} + +enum Addrs<'a> { + Args(clap::parser::ValuesRef<'a, String>), + Stdin(Lines<StdinLock<'a>>), + All { + iter: addr2line::LocationRangeIter<'a, LoaderReader<'a>>, + max: u64, + }, +} + +impl<'a> Iterator for Addrs<'a> { + type Item = Option<u64>; + + fn next(&mut self) -> Option<Option<u64>> { + let text = match self { + Addrs::Args(vals) => vals.next().map(Cow::from), + Addrs::Stdin(lines) => lines.next().map(Result::unwrap).map(Cow::from), + Addrs::All { iter, max } => { + for (addr, _len, _loc) in iter { + if addr >= *max { + *max = addr + 1; + return Some(Some(addr)); + } + } + return None; + } + }; + text.as_ref() + .map(Cow::as_ref) + .map(parse_uint_from_hex_string) + } +} + +fn print_loc(loc: Option<&Location<'_>>, basenames: bool, llvm: bool) { + if let Some(loc) = loc { + if let Some(ref file) = loc.file.as_ref() { + let path = if basenames { + Path::new(Path::new(file).file_name().unwrap()) + } else { + Path::new(file) + }; + print!("{}:", path.display()); + } else { + print!("??:"); + } + if llvm { + print!("{}:{}", loc.line.unwrap_or(0), loc.column.unwrap_or(0)); + } else if let Some(line) = loc.line { + print!("{}", line); + } else { + print!("?"); + } + println!(); + } else if llvm { + println!("??:0:0"); + } else { + println!("??:0"); + } +} + +fn print_function(name: Option<&str>, language: Option<gimli::DwLang>, demangle: bool) { + if let Some(name) = name { + if demangle { + print!("{}", addr2line::demangle_auto(Cow::from(name), language)); + } else { + print!("{}", name); + } + } else { + print!("??"); + } +} + +struct Options<'a> { + do_functions: bool, + do_inlines: bool, + pretty: bool, + print_addrs: bool, + basenames: bool, + demangle: bool, + llvm: bool, + exe: &'a PathBuf, + sup: Option<&'a PathBuf>, + section: Option<&'a String>, +} + +fn main() { + let matches = Command::new("addr2line") + .version(env!("CARGO_PKG_VERSION")) + .about("A fast addr2line Rust port") + .max_term_width(100) + .args(&[ + Arg::new("exe") + .short('e') + .long("exe") + .value_name("filename") + .value_parser(clap::value_parser!(PathBuf)) + .help( + "Specify the name of the executable for which addresses should be translated.", + ) + .required(true), + Arg::new("sup") + .long("sup") + .value_name("filename") + .value_parser(clap::value_parser!(PathBuf)) + .help("Path to supplementary object file."), + Arg::new("section") + .short('j') + .long("section") + .value_name("name") + .help( + "Read offsets relative to the specified section instead of absolute addresses.", + ), + Arg::new("all") + .long("all") + .action(ArgAction::SetTrue) + .conflicts_with("addrs") + .help("Display all addresses that have line number information."), + Arg::new("functions") + .short('f') + .long("functions") + .action(ArgAction::SetTrue) + .help("Display function names as well as file and line number information."), + Arg::new("pretty").short('p').long("pretty-print") + .action(ArgAction::SetTrue) + .help( + "Make the output more human friendly: each location are printed on one line.", + ), + Arg::new("inlines").short('i').long("inlines") + .action(ArgAction::SetTrue) + .help( + "If the address belongs to a function that was inlined, the source information for \ + all enclosing scopes back to the first non-inlined function will also be printed.", + ), + Arg::new("addresses").short('a').long("addresses") + .action(ArgAction::SetTrue) + .help( + "Display the address before the function name, file and line number information.", + ), + Arg::new("basenames") + .short('s') + .long("basenames") + .action(ArgAction::SetTrue) + .help("Display only the base of each file name."), + Arg::new("demangle").short('C').long("demangle") + .action(ArgAction::SetTrue) + .help( + "Demangle function names. \ + Specifying a specific demangling style (like GNU addr2line) is not supported. \ + (TODO)" + ), + Arg::new("llvm") + .long("llvm") + .action(ArgAction::SetTrue) + .help("Display output in the same format as llvm-symbolizer."), + Arg::new("addrs") + .action(ArgAction::Append) + .help("Addresses to use instead of reading from stdin."), + ]) + .get_matches(); + + let opts = Options { + do_functions: matches.get_flag("functions"), + do_inlines: matches.get_flag("inlines"), + pretty: matches.get_flag("pretty"), + print_addrs: matches.get_flag("addresses"), + basenames: matches.get_flag("basenames"), + demangle: matches.get_flag("demangle"), + llvm: matches.get_flag("llvm"), + exe: matches.get_one::<PathBuf>("exe").unwrap(), + sup: matches.get_one::<PathBuf>("sup"), + section: matches.get_one::<String>("section"), + }; + + let ctx = Loader::new_with_sup(opts.exe, opts.sup).unwrap(); + + let section_range = opts.section.map(|section_name| { + ctx.get_section_range(section_name.as_bytes()) + .unwrap_or_else(|| panic!("cannot find section {}", section_name)) + }); + + let stdin = std::io::stdin(); + let addrs = if matches.get_flag("all") { + Addrs::All { + iter: ctx.find_location_range(0, !0).unwrap(), + max: 0, + } + } else { + matches + .get_many::<String>("addrs") + .map(Addrs::Args) + .unwrap_or_else(|| Addrs::Stdin(stdin.lock().lines())) + }; + + for probe in addrs { + if opts.print_addrs { + let addr = probe.unwrap_or(0); + if opts.llvm { + print!("0x{:x}", addr); + } else { + print!("0x{:016x}", addr); + } + if opts.pretty { + print!(": "); + } else { + println!(); + } + } + + // If --section is given, add the section address to probe. + let probe = probe.and_then(|probe| { + if let Some(section_range) = section_range { + if probe < (section_range.end - section_range.begin) { + Some(probe + section_range.begin) + } else { + // If addr >= section size, treat it as if no line number information was found. + None + } + } else { + Some(probe) + } + }); + + if opts.do_functions || opts.do_inlines { + let mut printed_anything = false; + if let Some(probe) = probe { + let mut frames = ctx.find_frames(probe).unwrap().peekable(); + let mut first = true; + while let Some(frame) = frames.next().unwrap() { + if opts.pretty && !first { + print!(" (inlined by) "); + } + first = false; + + if opts.do_functions { + // Only use the symbol table if this isn't an inlined function. + let symbol = if matches!(frames.peek(), Ok(None)) { + ctx.find_symbol(probe) + } else { + None + }; + if symbol.is_some() { + // Prefer the symbol table over the DWARF name because: + // - the symbol can include a clone suffix + // - llvm may omit the linkage name in the DWARF with -g1 + print_function(symbol, None, opts.demangle); + } else if let Some(func) = frame.function { + print_function( + func.raw_name().ok().as_deref(), + func.language, + opts.demangle, + ); + } else { + print_function(None, None, opts.demangle); + } + + if opts.pretty { + print!(" at "); + } else { + println!(); + } + } + + print_loc(frame.location.as_ref(), opts.basenames, opts.llvm); + + printed_anything = true; + + if !opts.do_inlines { + break; + } + } + } + + if !printed_anything { + if opts.do_functions { + let name = probe.and_then(|probe| ctx.find_symbol(probe)); + print_function(name, None, opts.demangle); + + if opts.pretty { + print!(" at "); + } else { + println!(); + } + } + + print_loc(None, opts.basenames, opts.llvm); + } + } else { + let loc = probe.and_then(|probe| ctx.find_location(probe).unwrap()); + print_loc(loc.as_ref(), opts.basenames, opts.llvm); + } + + if opts.llvm { + println!(); + } + std::io::stdout().flush().unwrap(); + } +}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/frame.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/frame.rs new file mode 100644 index 0000000..f665eeb --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/frame.rs
@@ -0,0 +1,221 @@ +use alloc::borrow::Cow; +use alloc::string::String; +use core::iter; + +use crate::{maybe_small, Error, Function, InlinedFunction, ResUnit}; + +/// A source location. +pub struct Location<'a> { + /// The file name. + pub file: Option<&'a str>, + /// The line number. + pub line: Option<u32>, + /// The column number. + /// + /// A value of `Some(0)` indicates the left edge. + pub column: Option<u32>, +} + +/// A function frame. +pub struct Frame<'ctx, R: gimli::Reader> { + /// The DWARF unit offset corresponding to the DIE of the function. + pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>, + /// The name of the function. + pub function: Option<FunctionName<R>>, + /// The source location corresponding to this frame. + pub location: Option<Location<'ctx>>, +} + +/// An iterator over function frames. +pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>) +where + R: gimli::Reader; + +enum FrameIterState<'ctx, R> +where + R: gimli::Reader, +{ + Empty, + Location(Option<Location<'ctx>>), + Frames(FrameIterFrames<'ctx, R>), +} + +struct FrameIterFrames<'ctx, R> +where + R: gimli::Reader, +{ + unit: &'ctx ResUnit<R>, + sections: &'ctx gimli::Dwarf<R>, + function: &'ctx Function<R>, + inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>, + next: Option<Location<'ctx>>, +} + +impl<'ctx, R> FrameIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + pub(crate) fn new_empty() -> Self { + FrameIter(FrameIterState::Empty) + } + + pub(crate) fn new_location(location: Location<'ctx>) -> Self { + FrameIter(FrameIterState::Location(Some(location))) + } + + pub(crate) fn new_frames( + unit: &'ctx ResUnit<R>, + sections: &'ctx gimli::Dwarf<R>, + function: &'ctx Function<R>, + inlined_functions: maybe_small::Vec<&'ctx InlinedFunction<R>>, + location: Option<Location<'ctx>>, + ) -> Self { + FrameIter(FrameIterState::Frames(FrameIterFrames { + unit, + sections, + function, + inlined_functions: inlined_functions.into_iter().rev(), + next: location, + })) + } + + /// Advances the iterator and returns the next frame. + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> { + let frames = match &mut self.0 { + FrameIterState::Empty => return Ok(None), + FrameIterState::Location(location) => { + // We can't move out of a mutable reference, so use `take` instead. + let location = location.take(); + self.0 = FrameIterState::Empty; + return Ok(Some(Frame { + dw_die_offset: None, + function: None, + location, + })); + } + FrameIterState::Frames(frames) => frames, + }; + + let loc = frames.next.take(); + let func = match frames.inlined_functions.next() { + Some(func) => func, + None => { + let frame = Frame { + dw_die_offset: Some(frames.function.dw_die_offset), + function: frames.function.name.clone().map(|name| FunctionName { + name, + language: frames.unit.lang, + }), + location: loc, + }; + self.0 = FrameIterState::Empty; + return Ok(Some(frame)); + } + }; + + let mut next = Location { + file: None, + line: if func.call_line != 0 { + Some(func.call_line) + } else { + None + }, + column: if func.call_column != 0 { + Some(func.call_column) + } else { + None + }, + }; + if let Some(call_file) = func.call_file { + if let Some(lines) = frames.unit.parse_lines(frames.sections)? { + next.file = lines.file(call_file); + } + } + frames.next = Some(next); + + Ok(Some(Frame { + dw_die_offset: Some(func.dw_die_offset), + function: func.name.clone().map(|name| FunctionName { + name, + language: frames.unit.lang, + }), + location: loc, + })) + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + type Item = Frame<'ctx, R>; + type Error = Error; + + #[inline] + fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> { + self.next() + } +} + +/// A function name. +pub struct FunctionName<R: gimli::Reader> { + /// The name of the function. + pub name: R, + /// The language of the compilation unit containing this function. + pub language: Option<gimli::DwLang>, +} + +impl<R: gimli::Reader> FunctionName<R> { + /// The raw name of this function before demangling. + pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> { + self.name.to_string_lossy() + } + + /// The name of this function after demangling (if applicable). + pub fn demangle(&self) -> Result<Cow<'_, str>, Error> { + self.raw_name().map(|x| demangle_auto(x, self.language)) + } +} + +/// Demangle a symbol name using the demangling scheme for the given language. +/// +/// Returns `None` if demangling failed or is not required. +#[allow(unused_variables)] +pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> { + match language { + #[cfg(feature = "rustc-demangle")] + gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name) + .ok() + .as_ref() + .map(|x| format!("{:#}", x)), + #[cfg(feature = "cpp_demangle")] + gimli::DW_LANG_C_plus_plus + | gimli::DW_LANG_C_plus_plus_03 + | gimli::DW_LANG_C_plus_plus_11 + | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name) + .ok() + .and_then(|x| x.demangle(&Default::default()).ok()), + _ => None, + } +} + +/// Apply 'best effort' demangling of a symbol name. +/// +/// If `language` is given, then only the demangling scheme for that language +/// is used. +/// +/// If `language` is `None`, then heuristics are used to determine how to +/// demangle the name. Currently, these heuristics are very basic. +/// +/// If demangling fails or is not required, then `name` is returned unchanged. +pub fn demangle_auto(name: Cow<'_, str>, language: Option<gimli::DwLang>) -> Cow<'_, str> { + match language { + Some(language) => demangle(name.as_ref(), language), + None => demangle(name.as_ref(), gimli::DW_LANG_Rust) + .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)), + } + .map(Cow::from) + .unwrap_or(name) +}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/function.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/function.rs new file mode 100644 index 0000000..e84c80f --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/function.rs
@@ -0,0 +1,563 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cmp::Ordering; + +use crate::maybe_small; +use crate::{Context, DebugFile, Error, LazyResult, RangeAttributes}; + +pub(crate) struct LazyFunctions<R: gimli::Reader>(LazyResult<Functions<R>>); + +impl<R: gimli::Reader> LazyFunctions<R> { + pub(crate) fn new() -> Self { + LazyFunctions(LazyResult::new()) + } + + pub(crate) fn borrow(&self, unit: gimli::UnitRef<R>) -> Result<&Functions<R>, Error> { + self.0 + .get_or_init(|| Functions::parse(unit)) + .as_ref() + .map_err(Error::clone) + } +} + +pub(crate) struct Functions<R: gimli::Reader> { + /// List of all `DW_TAG_subprogram` details in the unit. + pub(crate) functions: Box<[LazyFunction<R>]>, + /// List of `DW_TAG_subprogram` address ranges in the unit. + pub(crate) addresses: Box<[FunctionAddress]>, +} + +/// A single address range for a function. +/// +/// It is possible for a function to have multiple address ranges; this +/// is handled by having multiple `FunctionAddress` entries with the same +/// `function` field. +pub(crate) struct FunctionAddress { + range: gimli::Range, + /// An index into `Functions::functions`. + pub(crate) function: usize, +} + +pub(crate) struct LazyFunction<R: gimli::Reader> { + dw_die_offset: gimli::UnitOffset<R::Offset>, + lazy: LazyResult<Function<R>>, +} + +impl<R: gimli::Reader> LazyFunction<R> { + fn new(dw_die_offset: gimli::UnitOffset<R::Offset>) -> Self { + LazyFunction { + dw_die_offset, + lazy: LazyResult::new(), + } + } + + pub(crate) fn borrow( + &self, + file: DebugFile, + unit: gimli::UnitRef<R>, + ctx: &Context<R>, + ) -> Result<&Function<R>, Error> { + self.lazy + .get_or_init(|| Function::parse(self.dw_die_offset, file, unit, ctx)) + .as_ref() + .map_err(Error::clone) + } +} + +pub(crate) struct Function<R: gimli::Reader> { + pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>, + pub(crate) name: Option<R>, + /// List of all `DW_TAG_inlined_subroutine` details in this function. + inlined_functions: Box<[InlinedFunction<R>]>, + /// List of `DW_TAG_inlined_subroutine` address ranges in this function. + inlined_addresses: Box<[InlinedFunctionAddress]>, +} + +pub(crate) struct InlinedFunctionAddress { + range: gimli::Range, + call_depth: usize, + /// An index into `Function::inlined_functions`. + function: usize, +} + +pub(crate) struct InlinedFunction<R: gimli::Reader> { + pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>, + pub(crate) name: Option<R>, + pub(crate) call_file: Option<u64>, + pub(crate) call_line: u32, + pub(crate) call_column: u32, +} + +impl<R: gimli::Reader> Functions<R> { + fn parse(unit: gimli::UnitRef<R>) -> Result<Functions<R>, Error> { + let mut functions = Vec::new(); + let mut addresses = Vec::new(); + let mut entries = unit.entries_raw(None)?; + while !entries.is_empty() { + let dw_die_offset = entries.next_offset(); + if let Some(abbrev) = entries.read_abbreviation()? { + if abbrev.tag() == gimli::DW_TAG_subprogram { + let mut ranges = RangeAttributes::default(); + for spec in abbrev.attributes() { + match entries.read_attribute(*spec) { + Ok(ref attr) => { + match attr.name() { + gimli::DW_AT_low_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => { + ranges.low_pc = Some(val) + } + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.low_pc = Some(unit.address(index)?); + } + _ => {} + }, + gimli::DW_AT_high_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => { + ranges.high_pc = Some(val) + } + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.high_pc = Some(unit.address(index)?); + } + gimli::AttributeValue::Udata(val) => { + ranges.size = Some(val) + } + _ => {} + }, + gimli::DW_AT_ranges => { + ranges.ranges_offset = + unit.attr_ranges_offset(attr.value())?; + } + _ => {} + }; + } + Err(e) => return Err(e), + } + } + + let function_index = functions.len(); + let has_address = ranges.for_each_range(unit, |range| { + addresses.push(FunctionAddress { + range, + function: function_index, + }); + })?; + if has_address { + functions.push(LazyFunction::new(dw_die_offset)); + } + } else { + entries.skip_attributes(abbrev.attributes())?; + } + } + } + + // The binary search requires the addresses to be sorted. + // + // It also requires them to be non-overlapping. In practice, overlapping + // function ranges are unlikely, so we don't try to handle that yet. + // + // It's possible for multiple functions to have the same address range if the + // compiler can detect and remove functions with identical code. In that case + // we'll nondeterministically return one of them. + addresses.sort_by_key(|x| x.range.begin); + + Ok(Functions { + functions: functions.into_boxed_slice(), + addresses: addresses.into_boxed_slice(), + }) + } + + pub(crate) fn find_address(&self, probe: u64) -> Option<usize> { + self.addresses + .binary_search_by(|address| { + if probe < address.range.begin { + Ordering::Greater + } else if probe >= address.range.end { + Ordering::Less + } else { + Ordering::Equal + } + }) + .ok() + } + + pub(crate) fn parse_inlined_functions( + &self, + file: DebugFile, + unit: gimli::UnitRef<R>, + ctx: &Context<R>, + ) -> Result<(), Error> { + for function in &*self.functions { + function.borrow(file, unit, ctx)?; + } + Ok(()) + } +} + +impl<R: gimli::Reader> Function<R> { + fn parse( + dw_die_offset: gimli::UnitOffset<R::Offset>, + file: DebugFile, + unit: gimli::UnitRef<R>, + ctx: &Context<R>, + ) -> Result<Self, Error> { + let mut entries = unit.entries_raw(Some(dw_die_offset))?; + let depth = entries.next_depth(); + let abbrev = entries.read_abbreviation()?.unwrap(); + debug_assert_eq!(abbrev.tag(), gimli::DW_TAG_subprogram); + + let mut name = None; + for spec in abbrev.attributes() { + match entries.read_attribute(*spec) { + Ok(ref attr) => { + match attr.name() { + gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { + if let Ok(val) = unit.attr_string(attr.value()) { + name = Some(val); + } + } + gimli::DW_AT_name => { + if name.is_none() { + name = unit.attr_string(attr.value()).ok(); + } + } + gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { + if name.is_none() { + name = name_attr(attr.value(), file, unit, ctx, 16)?; + } + } + _ => {} + }; + } + Err(e) => return Err(e), + } + } + + let mut state = InlinedState { + entries, + functions: Vec::new(), + addresses: Vec::new(), + file, + unit, + ctx, + }; + Function::parse_children(&mut state, depth, 0)?; + + // Sort ranges in "breadth-first traversal order", i.e. first by call_depth + // and then by range.begin. This allows finding the range containing an + // address at a certain depth using binary search. + // Note: Using DFS order, i.e. ordering by range.begin first and then by + // call_depth, would not work! Consider the two examples + // "[0..10 at depth 0], [0..2 at depth 1], [6..8 at depth 1]" and + // "[0..5 at depth 0], [0..2 at depth 1], [5..10 at depth 0], [6..8 at depth 1]". + // In this example, if you want to look up address 7 at depth 0, and you + // encounter [0..2 at depth 1], are you before or after the target range? + // You don't know. + state.addresses.sort_by(|r1, r2| { + if r1.call_depth < r2.call_depth { + Ordering::Less + } else if r1.call_depth > r2.call_depth { + Ordering::Greater + } else if r1.range.begin < r2.range.begin { + Ordering::Less + } else if r1.range.begin > r2.range.begin { + Ordering::Greater + } else { + Ordering::Equal + } + }); + + Ok(Function { + dw_die_offset, + name, + inlined_functions: state.functions.into_boxed_slice(), + inlined_addresses: state.addresses.into_boxed_slice(), + }) + } + + fn parse_children( + state: &mut InlinedState<R>, + depth: isize, + inlined_depth: usize, + ) -> Result<(), Error> { + loop { + let dw_die_offset = state.entries.next_offset(); + let next_depth = state.entries.next_depth(); + if next_depth <= depth { + return Ok(()); + } + if let Some(abbrev) = state.entries.read_abbreviation()? { + match abbrev.tag() { + gimli::DW_TAG_subprogram => { + Function::skip(&mut state.entries, abbrev, next_depth)?; + } + gimli::DW_TAG_inlined_subroutine => { + InlinedFunction::parse( + state, + dw_die_offset, + abbrev, + next_depth, + inlined_depth, + )?; + } + _ => { + state.entries.skip_attributes(abbrev.attributes())?; + } + } + } + } + } + + fn skip( + entries: &mut gimli::EntriesRaw<'_, '_, R>, + abbrev: &gimli::Abbreviation, + depth: isize, + ) -> Result<(), Error> { + // TODO: use DW_AT_sibling + entries.skip_attributes(abbrev.attributes())?; + while entries.next_depth() > depth { + if let Some(abbrev) = entries.read_abbreviation()? { + entries.skip_attributes(abbrev.attributes())?; + } + } + Ok(()) + } + + /// Build the list of inlined functions that contain `probe`. + pub(crate) fn find_inlined_functions( + &self, + probe: u64, + ) -> maybe_small::Vec<&InlinedFunction<R>> { + // `inlined_functions` is ordered from outside to inside. + let mut inlined_functions = maybe_small::Vec::new(); + let mut inlined_addresses = &self.inlined_addresses[..]; + loop { + let current_depth = inlined_functions.len(); + // Look up (probe, current_depth) in inline_ranges. + // `inlined_addresses` is sorted in "breadth-first traversal order", i.e. + // by `call_depth` first, and then by `range.begin`. See the comment at + // the sort call for more information about why. + let search = inlined_addresses.binary_search_by(|range| { + if range.call_depth > current_depth { + Ordering::Greater + } else if range.call_depth < current_depth { + Ordering::Less + } else if range.range.begin > probe { + Ordering::Greater + } else if range.range.end <= probe { + Ordering::Less + } else { + Ordering::Equal + } + }); + if let Ok(index) = search { + let function_index = inlined_addresses[index].function; + inlined_functions.push(&self.inlined_functions[function_index]); + inlined_addresses = &inlined_addresses[index + 1..]; + } else { + break; + } + } + inlined_functions + } +} + +impl<R: gimli::Reader> InlinedFunction<R> { + fn parse( + state: &mut InlinedState<R>, + dw_die_offset: gimli::UnitOffset<R::Offset>, + abbrev: &gimli::Abbreviation, + depth: isize, + inlined_depth: usize, + ) -> Result<(), Error> { + let unit = state.unit; + let mut ranges = RangeAttributes::default(); + let mut name = None; + let mut call_file = None; + let mut call_line = 0; + let mut call_column = 0; + for spec in abbrev.attributes() { + match state.entries.read_attribute(*spec) { + Ok(ref attr) => match attr.name() { + gimli::DW_AT_low_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.low_pc = Some(unit.address(index)?); + } + _ => {} + }, + gimli::DW_AT_high_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.high_pc = Some(unit.address(index)?); + } + gimli::AttributeValue::Udata(val) => ranges.size = Some(val), + _ => {} + }, + gimli::DW_AT_ranges => { + ranges.ranges_offset = unit.attr_ranges_offset(attr.value())?; + } + gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { + if let Ok(val) = unit.attr_string(attr.value()) { + name = Some(val); + } + } + gimli::DW_AT_name => { + if name.is_none() { + name = unit.attr_string(attr.value()).ok(); + } + } + gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { + if name.is_none() { + name = name_attr(attr.value(), state.file, unit, state.ctx, 16)?; + } + } + gimli::DW_AT_call_file => { + // There is a spec issue [1] with how DW_AT_call_file is specified in DWARF 5. + // Before, a file index of 0 would indicate no source file, however in + // DWARF 5 this could be a valid index into the file table. + // + // Implementations such as LLVM generates a file index of 0 when DWARF 5 is + // used. + // + // Thus, if we see a version of 5 or later, treat a file index of 0 as such. + // [1]: http://wiki.dwarfstd.org/index.php?title=DWARF5_Line_Table_File_Numbers + if let gimli::AttributeValue::FileIndex(fi) = attr.value() { + if fi > 0 || unit.header.version() >= 5 { + call_file = Some(fi); + } + } + } + gimli::DW_AT_call_line => { + call_line = attr.udata_value().unwrap_or(0) as u32; + } + gimli::DW_AT_call_column => { + call_column = attr.udata_value().unwrap_or(0) as u32; + } + _ => {} + }, + Err(e) => return Err(e), + } + } + + let function_index = state.functions.len(); + state.functions.push(InlinedFunction { + dw_die_offset, + name, + call_file, + call_line, + call_column, + }); + + ranges.for_each_range(unit, |range| { + state.addresses.push(InlinedFunctionAddress { + range, + call_depth: inlined_depth, + function: function_index, + }); + })?; + + Function::parse_children(state, depth, inlined_depth + 1) + } +} + +struct InlinedState<'a, R: gimli::Reader> { + // Mutable fields. + entries: gimli::EntriesRaw<'a, 'a, R>, + functions: Vec<InlinedFunction<R>>, + addresses: Vec<InlinedFunctionAddress>, + + // Constant fields. + file: DebugFile, + unit: gimli::UnitRef<'a, R>, + ctx: &'a Context<R>, +} + +fn name_attr<R>( + attr: gimli::AttributeValue<R>, + mut file: DebugFile, + unit: gimli::UnitRef<R>, + ctx: &Context<R>, + recursion_limit: usize, +) -> Result<Option<R>, Error> +where + R: gimli::Reader, +{ + if recursion_limit == 0 { + return Ok(None); + } + + match attr { + gimli::AttributeValue::UnitRef(offset) => { + name_entry(file, unit, offset, ctx, recursion_limit) + } + gimli::AttributeValue::DebugInfoRef(dr) => { + let sections = unit.dwarf; + let (unit, offset) = ctx.find_unit(dr, file)?; + let unit = gimli::UnitRef::new(sections, unit); + name_entry(file, unit, offset, ctx, recursion_limit) + } + gimli::AttributeValue::DebugInfoRefSup(dr) => { + if let Some(sup_sections) = unit.dwarf.sup.as_ref() { + file = DebugFile::Supplementary; + let (unit, offset) = ctx.find_unit(dr, file)?; + let unit = gimli::UnitRef::new(sup_sections, unit); + name_entry(file, unit, offset, ctx, recursion_limit) + } else { + Ok(None) + } + } + _ => Ok(None), + } +} + +fn name_entry<R>( + file: DebugFile, + unit: gimli::UnitRef<R>, + offset: gimli::UnitOffset<R::Offset>, + ctx: &Context<R>, + recursion_limit: usize, +) -> Result<Option<R>, Error> +where + R: gimli::Reader, +{ + let mut entries = unit.entries_raw(Some(offset))?; + let abbrev = if let Some(abbrev) = entries.read_abbreviation()? { + abbrev + } else { + return Err(gimli::Error::NoEntryAtGivenOffset); + }; + + let mut name = None; + let mut next = None; + for spec in abbrev.attributes() { + match entries.read_attribute(*spec) { + Ok(ref attr) => match attr.name() { + gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { + if let Ok(val) = unit.attr_string(attr.value()) { + return Ok(Some(val)); + } + } + gimli::DW_AT_name => { + if let Ok(val) = unit.attr_string(attr.value()) { + name = Some(val); + } + } + gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { + next = Some(attr.value()); + } + _ => {} + }, + Err(e) => return Err(e), + } + } + + if name.is_some() { + return Ok(name); + } + + if let Some(next) = next { + return name_attr(next, file, unit, ctx, recursion_limit - 1); + } + + Ok(None) +}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/lib.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/lib.rs new file mode 100644 index 0000000..ac458de --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/lib.rs
@@ -0,0 +1,418 @@ +//! `addr2line` provides a cross-platform library for retrieving per-address debug information +//! from files with DWARF debug information. Given an address, it can return the file name, +//! line number, and function name associated with that address, as well as the inline call +//! stack leading to that address. +//! +//! At the lowest level, the library uses a [`Context`] to cache parsed information so that +//! multiple lookups are efficient. To create a `Context`, you first need to open and parse the +//! file using an object file parser such as [`object`](https://github.com/gimli-rs/object), +//! create a [`gimli::Dwarf`], and finally call [`Context::from_dwarf`]. +//! +//! Location information is obtained with [`Context::find_location`] or +//! [`Context::find_location_range`]. Function information is obtained with +//! [`Context::find_frames`], which returns a frame for each inline function. Each frame +//! contains both name and location. +//! +//! The library also provides a [`Loader`] which internally memory maps the files, +//! uses the `object` crate to do the parsing, and creates a `Context`. +//! The `Context` is not exposed, but the `Loader` provides the same functionality +//! via [`Loader::find_location`], [`Loader::find_location_range`], and +//! [`Loader::find_frames`]. The `Loader` also provides [`Loader::find_symbol`] +//! to use the symbol table instead of DWARF debugging information. +//! The `Loader` will load Mach-O dSYM files and split DWARF files as needed. +//! +//! The crate has a CLI wrapper around the library which provides some of +//! the functionality of the `addr2line` command line tool distributed with +//! [GNU binutils](https://sourceware.org/binutils/docs/binutils/addr2line.html). +#![deny(missing_docs)] +#![no_std] + +#[cfg(feature = "cargo-all")] +compile_error!("'--all-features' is not supported; use '--features all' instead"); + +#[cfg(feature = "std")] +extern crate std; + +#[allow(unused_imports)] +#[macro_use] +extern crate alloc; + +#[cfg(feature = "fallible-iterator")] +pub extern crate fallible_iterator; +pub extern crate gimli; + +use alloc::sync::Arc; +use core::cell::OnceCell; +use core::ops::ControlFlow; + +use crate::function::{Function, Functions, InlinedFunction, LazyFunctions}; +use crate::line::{LazyLines, LineLocationRangeIter, Lines}; +use crate::lookup::{LoopingLookup, SimpleLookup}; +use crate::unit::{ResUnit, ResUnits, SupUnits}; + +#[cfg(feature = "smallvec")] +mod maybe_small { + pub type Vec<T> = smallvec::SmallVec<[T; 16]>; + pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>; +} +#[cfg(not(feature = "smallvec"))] +mod maybe_small { + pub type Vec<T> = alloc::vec::Vec<T>; + pub type IntoIter<T> = alloc::vec::IntoIter<T>; +} + +mod frame; +pub use frame::{demangle, demangle_auto, Frame, FrameIter, FunctionName, Location}; + +mod function; +mod line; + +#[cfg(feature = "loader")] +mod loader; +#[cfg(feature = "loader")] +pub use loader::{Loader, LoaderReader, Symbol}; + +mod lookup; +pub use lookup::{LookupContinuation, LookupResult, SplitDwarfLoad}; + +mod unit; +pub use unit::LocationRangeIter; + +type Error = gimli::Error; +type LazyResult<T> = OnceCell<Result<T, Error>>; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum DebugFile { + Primary, + Supplementary, + Dwo, +} + +/// The state necessary to perform address to line translation. +/// +/// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s +/// when performing lookups for many addresses in the same executable. +pub struct Context<R: gimli::Reader> { + sections: Arc<gimli::Dwarf<R>>, + units: ResUnits<R>, + sup_units: SupUnits<R>, +} + +impl<R: gimli::Reader> Context<R> { + /// Construct a new `Context` from DWARF sections. + /// + /// This method does not support using a supplementary object file. + #[allow(clippy::too_many_arguments)] + pub fn from_sections( + debug_abbrev: gimli::DebugAbbrev<R>, + debug_addr: gimli::DebugAddr<R>, + debug_aranges: gimli::DebugAranges<R>, + debug_info: gimli::DebugInfo<R>, + debug_line: gimli::DebugLine<R>, + debug_line_str: gimli::DebugLineStr<R>, + debug_ranges: gimli::DebugRanges<R>, + debug_rnglists: gimli::DebugRngLists<R>, + debug_str: gimli::DebugStr<R>, + debug_str_offsets: gimli::DebugStrOffsets<R>, + default_section: R, + ) -> Result<Self, Error> { + Self::from_dwarf(gimli::Dwarf { + debug_abbrev, + debug_addr, + debug_aranges, + debug_info, + debug_line, + debug_line_str, + debug_macinfo: default_section.clone().into(), + debug_macro: default_section.clone().into(), + debug_str, + debug_str_offsets, + debug_types: default_section.clone().into(), + locations: gimli::LocationLists::new( + default_section.clone().into(), + default_section.into(), + ), + ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists), + file_type: gimli::DwarfFileType::Main, + sup: None, + abbreviations_cache: gimli::AbbreviationsCache::new(), + }) + } + + /// Construct a new `Context` from an existing [`gimli::Dwarf`] object. + #[inline] + pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> { + Self::from_arc_dwarf(Arc::new(sections)) + } + + /// Construct a new `Context` from an existing [`gimli::Dwarf`] object. + #[inline] + pub fn from_arc_dwarf(sections: Arc<gimli::Dwarf<R>>) -> Result<Context<R>, Error> { + let units = ResUnits::parse(§ions)?; + let sup_units = if let Some(sup) = sections.sup.as_ref() { + SupUnits::parse(sup)? + } else { + SupUnits::default() + }; + Ok(Context { + sections, + units, + sup_units, + }) + } +} + +impl<R: gimli::Reader> Context<R> { + /// Find the DWARF unit corresponding to the given virtual memory address. + pub fn find_dwarf_and_unit( + &self, + probe: u64, + ) -> LookupResult<impl LookupContinuation<Output = Option<gimli::UnitRef<'_, R>>, Buf = R>> + { + let mut units_iter = self.units.find(probe); + if let Some(unit) = units_iter.next() { + return LoopingLookup::new_lookup( + unit.find_function_or_location(probe, self), + move |r| { + ControlFlow::Break(match r { + Ok((Some(_), _)) | Ok((_, Some(_))) => { + let (_file, unit) = unit + .dwarf_and_unit(self) + // We've already been through both error cases here to get to this point. + .unwrap() + .unwrap(); + Some(unit) + } + _ => match units_iter.next() { + Some(next_unit) => { + return ControlFlow::Continue( + next_unit.find_function_or_location(probe, self), + ); + } + None => None, + }, + }) + }, + ); + } + + LoopingLookup::new_complete(None) + } + + /// Find the source file and line corresponding to the given virtual memory address. + pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> { + for unit in self.units.find(probe) { + if let Some(location) = unit.find_location(probe, &self.sections)? { + return Ok(Some(location)); + } + } + Ok(None) + } + + /// Return source file and lines for a range of addresses. For each location it also + /// returns the address and size of the range of the underlying instructions. + pub fn find_location_range( + &self, + probe_low: u64, + probe_high: u64, + ) -> Result<LocationRangeIter<'_, R>, Error> { + self.units + .find_location_range(probe_low, probe_high, &self.sections) + } + + /// Return an iterator for the function frames corresponding to the given virtual + /// memory address. + /// + /// If the probe address is not for an inline function then only one frame is + /// returned. + /// + /// If the probe address is for an inline function then the first frame corresponds + /// to the innermost inline function. Subsequent frames contain the caller and call + /// location, until an non-inline caller is reached. + pub fn find_frames( + &self, + probe: u64, + ) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>> + { + let mut units_iter = self.units.find(probe); + if let Some(unit) = units_iter.next() { + LoopingLookup::new_lookup(unit.find_function_or_location(probe, self), move |r| { + ControlFlow::Break(match r { + Err(e) => Err(e), + Ok((Some(function), location)) => { + let inlined_functions = function.find_inlined_functions(probe); + Ok(FrameIter::new_frames( + unit, + &self.sections, + function, + inlined_functions, + location, + )) + } + Ok((None, Some(location))) => Ok(FrameIter::new_location(location)), + Ok((None, None)) => match units_iter.next() { + Some(next_unit) => { + return ControlFlow::Continue( + next_unit.find_function_or_location(probe, self), + ); + } + None => Ok(FrameIter::new_empty()), + }, + }) + }) + } else { + LoopingLookup::new_complete(Ok(FrameIter::new_empty())) + } + } + + /// Preload units for `probe`. + /// + /// The iterator returns pairs of `SplitDwarfLoad`s containing the + /// information needed to locate and load split DWARF for `probe` and + /// a matching callback to invoke once that data is available. + /// + /// If this method is called, and all of the returned closures are invoked, + /// addr2line guarantees that any future API call for the address `probe` + /// will not require the loading of any split DWARF. + /// + /// ```no_run + /// # use addr2line::*; + /// # use std::sync::Arc; + /// # let ctx: Context<gimli::EndianSlice<gimli::RunTimeEndian>> = todo!(); + /// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianSlice<gimli::RunTimeEndian>>>> { None }; + /// const ADDRESS: u64 = 0xdeadbeef; + /// ctx.preload_units(ADDRESS).for_each(|(load, callback)| { + /// let dwo = do_split_dwarf_load(load); + /// callback(dwo); + /// }); + /// + /// let frames_iter = match ctx.find_frames(ADDRESS) { + /// LookupResult::Output(result) => result, + /// LookupResult::Load { .. } => unreachable!("addr2line promised we wouldn't get here"), + /// }; + /// + /// // ... + /// ``` + pub fn preload_units( + &'_ self, + probe: u64, + ) -> impl Iterator< + Item = ( + SplitDwarfLoad<R>, + impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_, + ), + > { + self.units + .find(probe) + .filter_map(move |unit| match unit.dwarf_and_unit(self) { + LookupResult::Output(_) => None, + LookupResult::Load { load, continuation } => Some((load, |result| { + continuation.resume(result).unwrap().map(|_| ()) + })), + }) + } + + /// Initialize all line data structures. This is used for benchmarks. + #[doc(hidden)] + pub fn parse_lines(&self) -> Result<(), Error> { + for unit in self.units.iter() { + unit.parse_lines(&self.sections)?; + } + Ok(()) + } + + /// Initialize all function data structures. This is used for benchmarks. + #[doc(hidden)] + pub fn parse_functions(&self) -> Result<(), Error> { + for unit in self.units.iter() { + unit.parse_functions(self).skip_all_loads()?; + } + Ok(()) + } + + /// Initialize all inlined function data structures. This is used for benchmarks. + #[doc(hidden)] + pub fn parse_inlined_functions(&self) -> Result<(), Error> { + for unit in self.units.iter() { + unit.parse_inlined_functions(self).skip_all_loads()?; + } + Ok(()) + } +} + +impl<R: gimli::Reader> Context<R> { + // Find the unit containing the given offset, and convert the offset into a unit offset. + fn find_unit( + &self, + offset: gimli::DebugInfoOffset<R::Offset>, + file: DebugFile, + ) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> { + let unit = match file { + DebugFile::Primary => self.units.find_offset(offset)?, + DebugFile::Supplementary => self.sup_units.find_offset(offset)?, + DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset), + }; + + let unit_offset = offset + .to_unit_offset(&unit.header) + .ok_or(gimli::Error::NoEntryAtGivenOffset)?; + Ok((unit, unit_offset)) + } +} + +struct RangeAttributes<R: gimli::Reader> { + low_pc: Option<u64>, + high_pc: Option<u64>, + size: Option<u64>, + ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>, +} + +impl<R: gimli::Reader> Default for RangeAttributes<R> { + fn default() -> Self { + RangeAttributes { + low_pc: None, + high_pc: None, + size: None, + ranges_offset: None, + } + } +} + +impl<R: gimli::Reader> RangeAttributes<R> { + fn for_each_range<F: FnMut(gimli::Range)>( + &self, + unit: gimli::UnitRef<R>, + mut f: F, + ) -> Result<bool, Error> { + let mut added_any = false; + let mut add_range = |range: gimli::Range| { + if range.begin < range.end { + f(range); + added_any = true + } + }; + if let Some(ranges_offset) = self.ranges_offset { + let mut range_list = unit.ranges(ranges_offset)?; + while let Some(range) = range_list.next()? { + add_range(range); + } + } else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) { + add_range(gimli::Range { begin, end }); + } else if let (Some(begin), Some(size)) = (self.low_pc, self.size) { + // If `begin` is a -1 tombstone, this will overflow and the check in + // `add_range` will ignore it. + let end = begin.wrapping_add(size); + add_range(gimli::Range { begin, end }); + } + Ok(added_any) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn context_is_send() { + fn assert_is_send<T: Send>() {} + assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>(); + } +}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/line.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/line.rs new file mode 100644 index 0000000..4b1c95d --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/line.rs
@@ -0,0 +1,313 @@ +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; +use core::num::NonZeroU64; + +use crate::{Error, LazyResult, Location}; + +pub(crate) struct LazyLines(LazyResult<Lines>); + +impl LazyLines { + pub(crate) fn new() -> Self { + LazyLines(LazyResult::new()) + } + + pub(crate) fn borrow<R: gimli::Reader>( + &self, + dw_unit: gimli::UnitRef<R>, + ilnp: &gimli::IncompleteLineProgram<R, R::Offset>, + ) -> Result<&Lines, Error> { + self.0 + .get_or_init(|| Lines::parse(dw_unit, ilnp.clone())) + .as_ref() + .map_err(Error::clone) + } +} + +struct LineSequence { + start: u64, + end: u64, + rows: Box<[LineRow]>, +} + +struct LineRow { + address: u64, + file_index: u64, + line: u32, + column: u32, +} + +pub(crate) struct Lines { + files: Box<[String]>, + sequences: Box<[LineSequence]>, +} + +impl Lines { + fn parse<R: gimli::Reader>( + dw_unit: gimli::UnitRef<R>, + ilnp: gimli::IncompleteLineProgram<R, R::Offset>, + ) -> Result<Self, Error> { + let mut sequences = Vec::new(); + let mut sequence_rows = Vec::<LineRow>::new(); + let mut rows = ilnp.rows(); + while let Some((_, row)) = rows.next_row()? { + if row.end_sequence() { + if let Some(start) = sequence_rows.first().map(|x| x.address) { + let end = row.address(); + let mut rows = Vec::new(); + mem::swap(&mut rows, &mut sequence_rows); + sequences.push(LineSequence { + start, + end, + rows: rows.into_boxed_slice(), + }); + } + continue; + } + + let address = row.address(); + let file_index = row.file_index(); + // Convert line and column to u32 to save a little memory. + // We'll handle the special case of line 0 later, + // and return left edge as column 0 in the public API. + let line = row.line().map(NonZeroU64::get).unwrap_or(0) as u32; + let column = match row.column() { + gimli::ColumnType::LeftEdge => 0, + gimli::ColumnType::Column(x) => x.get() as u32, + }; + + if let Some(last_row) = sequence_rows.last_mut() { + if last_row.address == address { + last_row.file_index = file_index; + last_row.line = line; + last_row.column = column; + continue; + } + } + + sequence_rows.push(LineRow { + address, + file_index, + line, + column, + }); + } + sequences.sort_by_key(|x| x.start); + + let mut files = Vec::new(); + let header = rows.header(); + match header.file(0) { + Some(file) => files.push(render_file(dw_unit, file, header)?), + None => files.push(String::from("")), // DWARF version <= 4 may not have 0th index + } + let mut index = 1; + while let Some(file) = header.file(index) { + files.push(render_file(dw_unit, file, header)?); + index += 1; + } + + Ok(Self { + files: files.into_boxed_slice(), + sequences: sequences.into_boxed_slice(), + }) + } + + pub(crate) fn file(&self, index: u64) -> Option<&str> { + self.files.get(index as usize).map(String::as_str) + } + + pub(crate) fn ranges(&self) -> impl Iterator<Item = gimli::Range> + '_ { + self.sequences.iter().map(|sequence| gimli::Range { + begin: sequence.start, + end: sequence.end, + }) + } + + fn row_location(&self, row: &LineRow) -> Location<'_> { + let file = self.files.get(row.file_index as usize).map(String::as_str); + Location { + file, + line: if row.line != 0 { Some(row.line) } else { None }, + // If row.line is specified then row.column always has meaning. + column: if row.line != 0 { + Some(row.column) + } else { + None + }, + } + } + + pub(crate) fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> { + let seq_idx = self.sequences.binary_search_by(|sequence| { + if probe < sequence.start { + Ordering::Greater + } else if probe >= sequence.end { + Ordering::Less + } else { + Ordering::Equal + } + }); + let seq_idx = match seq_idx { + Ok(x) => x, + Err(_) => return Ok(None), + }; + let sequence = &self.sequences[seq_idx]; + + let idx = sequence + .rows + .binary_search_by(|row| row.address.cmp(&probe)); + let idx = match idx { + Ok(x) => x, + Err(0) => return Ok(None), + Err(x) => x - 1, + }; + Ok(Some(self.row_location(&sequence.rows[idx]))) + } + + pub(crate) fn find_location_range( + &self, + probe_low: u64, + probe_high: u64, + ) -> Result<LineLocationRangeIter<'_>, Error> { + // Find index for probe_low. + let seq_idx = self.sequences.binary_search_by(|sequence| { + if probe_low < sequence.start { + Ordering::Greater + } else if probe_low >= sequence.end { + Ordering::Less + } else { + Ordering::Equal + } + }); + let seq_idx = match seq_idx { + Ok(x) => x, + Err(x) => x, // probe below sequence, but range could overlap + }; + + let row_idx = if let Some(seq) = self.sequences.get(seq_idx) { + let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low)); + match idx { + Ok(x) => x, + Err(0) => 0, // probe below sequence, but range could overlap + Err(x) => x - 1, + } + } else { + 0 + }; + + Ok(LineLocationRangeIter { + lines: self, + seq_idx, + row_idx, + probe_high, + }) + } +} + +pub(crate) struct LineLocationRangeIter<'ctx> { + lines: &'ctx Lines, + seq_idx: usize, + row_idx: usize, + probe_high: u64, +} + +impl<'ctx> Iterator for LineLocationRangeIter<'ctx> { + type Item = (u64, u64, Location<'ctx>); + + fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> { + while let Some(seq) = self.lines.sequences.get(self.seq_idx) { + if seq.start >= self.probe_high { + break; + } + + match seq.rows.get(self.row_idx) { + Some(row) => { + if row.address >= self.probe_high { + break; + } + + let nextaddr = seq + .rows + .get(self.row_idx + 1) + .map(|row| row.address) + .unwrap_or(seq.end); + + let item = ( + row.address, + nextaddr - row.address, + self.lines.row_location(row), + ); + self.row_idx += 1; + + return Some(item); + } + None => { + self.seq_idx += 1; + self.row_idx = 0; + } + } + } + None + } +} + +fn render_file<R: gimli::Reader>( + dw_unit: gimli::UnitRef<R>, + file: &gimli::FileEntry<R, R::Offset>, + header: &gimli::LineProgramHeader<R, R::Offset>, +) -> Result<String, gimli::Error> { + let mut path = if let Some(ref comp_dir) = dw_unit.comp_dir { + comp_dir.to_string_lossy()?.into_owned() + } else { + String::new() + }; + + // The directory index 0 is defined to correspond to the compilation unit directory. + if file.directory_index() != 0 { + if let Some(directory) = file.directory(header) { + path_push( + &mut path, + dw_unit.attr_string(directory)?.to_string_lossy()?.as_ref(), + ); + } + } + + path_push( + &mut path, + dw_unit + .attr_string(file.path_name())? + .to_string_lossy()? + .as_ref(), + ); + + Ok(path) +} + +fn path_push(path: &mut String, p: &str) { + if has_forward_slash_root(p) || has_backward_slash_root(p) { + *path = p.to_string(); + } else { + let dir_separator = if has_backward_slash_root(path.as_str()) { + '\\' + } else { + '/' + }; + + if !path.is_empty() && !path.ends_with(dir_separator) { + path.push(dir_separator); + } + *path += p; + } +} + +/// Check if the path in the given string has a unix style root +fn has_forward_slash_root(p: &str) -> bool { + p.starts_with('/') || p.get(1..3) == Some(":/") +} + +/// Check if the path in the given string has a windows style root +fn has_backward_slash_root(p: &str) -> bool { + p.starts_with('\\') || p.get(1..3) == Some(":\\") +}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/loader.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/loader.rs new file mode 100644 index 0000000..a006ee5 --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/loader.rs
@@ -0,0 +1,499 @@ +use alloc::borrow::Cow; +use alloc::boxed::Box; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::cell::OnceCell; +use std::ffi::OsStr; +use std::fs::File; +use std::path::{Path, PathBuf}; + +use memmap2::Mmap; +use object::{Object, ObjectMapFile, ObjectSection, SymbolMap, SymbolMapName}; +use typed_arena::Arena; + +use crate::{ + Context, FrameIter, Location, LocationRangeIter, LookupContinuation, LookupResult, + SplitDwarfLoad, +}; + +/// The type used by [`Loader`] for reading DWARF data. +/// +/// This is used in the return types of the methods of [`Loader`]. +// TODO: use impl Trait when stable +pub type LoaderReader<'a> = gimli::EndianSlice<'a, gimli::RunTimeEndian>; + +type Error = Box<dyn std::error::Error>; +type Result<T> = std::result::Result<T, Error>; + +/// A loader for the DWARF data required for a `Context`. +/// +/// For performance reasons, a [`Context`] normally borrows the input data. +/// However, that means the input data must outlive the `Context`, which +/// is inconvenient for long-lived `Context`s. +/// This loader uses an arena to store the input data, together with the +/// `Context` itself. This ensures that the input data lives as long as +/// the `Context`. +/// +/// The loader performs some additional tasks: +/// - Loads the symbol table from the executable file (see [`Self::find_symbol`]). +/// - Loads Mach-O dSYM files that are located next to the executable file. +/// - Locates and loads split DWARF files (DWO and DWP). +pub struct Loader { + internal: LoaderInternal<'static>, + arena_data: Arena<Vec<u8>>, + arena_mmap: Arena<Mmap>, +} + +impl Loader { + /// Load the DWARF data for an executable file and create a `Context`. + #[inline] + pub fn new(path: impl AsRef<Path>) -> Result<Self> { + Self::new_with_sup(path, None::<&Path>) + } + + /// Load the DWARF data for an executable file and create a `Context`. + /// + /// Optionally also use a supplementary object file. + pub fn new_with_sup( + path: impl AsRef<Path>, + sup_path: Option<impl AsRef<Path>>, + ) -> Result<Self> { + let arena_data = Arena::new(); + let arena_mmap = Arena::new(); + + let internal = LoaderInternal::new( + path.as_ref(), + sup_path.as_ref().map(AsRef::as_ref), + &arena_data, + &arena_mmap, + )?; + Ok(Loader { + // Convert to static lifetime to allow self-reference by `internal`. + // `internal` is only accessed through `borrow_internal`, which ensures + // that the static lifetime does not leak. + internal: unsafe { + core::mem::transmute::<LoaderInternal<'_>, LoaderInternal<'static>>(internal) + }, + arena_data, + arena_mmap, + }) + } + + fn borrow_internal<'a, F, T>(&'a self, f: F) -> T + where + F: FnOnce(&'a LoaderInternal<'a>, &'a Arena<Vec<u8>>, &'a Arena<Mmap>) -> T, + { + // Do not leak the static lifetime. + let internal = unsafe { + core::mem::transmute::<&LoaderInternal<'static>, &'a LoaderInternal<'a>>(&self.internal) + }; + f(internal, &self.arena_data, &self.arena_mmap) + } + + /// Get the base address used for relative virtual addresses. + /// + /// Currently this is only non-zero for PE. + pub fn relative_address_base(&self) -> u64 { + self.borrow_internal(|i, _data, _mmap| i.relative_address_base) + } + + /// Find the source file and line corresponding to the given virtual memory address. + /// + /// This calls [`Context::find_location`] with the given address. + pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>> { + self.borrow_internal(|i, data, mmap| i.find_location(probe, data, mmap)) + } + + /// Return source file and lines for a range of addresses. + /// + /// This calls [`Context::find_location_range`] with the given range. + pub fn find_location_range( + &self, + probe_low: u64, + probe_high: u64, + ) -> Result<LocationRangeIter<'_, LoaderReader<'_>>> { + self.borrow_internal(|i, data, mmap| { + i.find_location_range(probe_low, probe_high, data, mmap) + }) + } + + /// Return an iterator for the function frames corresponding to the given virtual + /// memory address. + /// + /// This calls [`Context::find_frames`] with the given address. + pub fn find_frames(&self, probe: u64) -> Result<FrameIter<'_, LoaderReader<'_>>> { + self.borrow_internal(|i, data, mmap| i.find_frames(probe, data, mmap)) + } + + /// Find the symbol table entry corresponding to the given virtual memory address. + /// Return the symbol name. + pub fn find_symbol(&self, probe: u64) -> Option<&str> { + self.find_symbol_info(probe).map(|symbol| symbol.name) + } + + /// Find the symbol table entry corresponding to the given virtual memory address. + pub fn find_symbol_info(&self, probe: u64) -> Option<Symbol<'_>> { + self.borrow_internal(|i, _data, _mmap| i.find_symbol_info(probe)) + } + + /// Get the address of a section + pub fn get_section_range(&self, section_name: &[u8]) -> Option<gimli::Range> { + self.borrow_internal(|i, _data, _mmap| i.get_section_range(section_name)) + } +} + +struct LoaderInternal<'a> { + ctx: Context<LoaderReader<'a>>, + object: object::File<'a>, + relative_address_base: u64, + symbols: SymbolMap<SymbolMapName<'a>>, + dwarf_package: Option<gimli::DwarfPackage<LoaderReader<'a>>>, + // Map from address to Mach-O object file path. + object_map: object::ObjectMap<'a>, + // A context for each Mach-O object file. + objects: Vec<OnceCell<Option<ObjectContext<'a>>>>, +} + +impl<'a> LoaderInternal<'a> { + fn new( + path: &Path, + sup_path: Option<&Path>, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> Result<Self> { + let file = File::open(path)?; + let map = arena_mmap.alloc(unsafe { Mmap::map(&file)? }); + let object = object::File::parse(&**map)?; + + let relative_address_base = object.relative_address_base(); + let symbols = object.symbol_map(); + let object_map = object.object_map(); + let mut objects = Vec::new(); + objects.resize_with(object_map.objects().len(), OnceCell::new); + + // Load supplementary object file. + // TODO: use debuglink and debugaltlink + let sup_map; + let sup_object = if let Some(sup_path) = sup_path { + let sup_file = File::open(sup_path)?; + sup_map = arena_mmap.alloc(unsafe { Mmap::map(&sup_file)? }); + Some(object::File::parse(&**sup_map)?) + } else { + None + }; + + // Load Mach-O dSYM file, ignoring errors. + let dsym = if let Some(map) = (|| { + let uuid = object.mach_uuid().ok()??; + path.parent()?.read_dir().ok()?.find_map(|candidate| { + let candidate = candidate.ok()?; + let path = candidate.path(); + if path.extension().and_then(OsStr::to_str) != Some("dSYM") { + return None; + } + let path = path.join("Contents/Resources/DWARF"); + path.read_dir().ok()?.find_map(|candidate| { + let candidate = candidate.ok()?; + let path = candidate.path(); + let file = File::open(path).ok()?; + let map = unsafe { Mmap::map(&file) }.ok()?; + let object = object::File::parse(&*map).ok()?; + if object.mach_uuid() == Ok(Some(uuid)) { + Some(map) + } else { + None + } + }) + }) + })() { + let map = arena_mmap.alloc(map); + Some(object::File::parse(&**map)?) + } else { + None + }; + let dwarf_object = dsym.as_ref().unwrap_or(&object); + + // Load the DWARF sections. + let endian = if dwarf_object.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + let mut dwarf = gimli::Dwarf::load(|id| { + load_section(Some(id.name()), dwarf_object, endian, arena_data) + })?; + if let Some(sup_object) = &sup_object { + dwarf.load_sup(|id| load_section(Some(id.name()), sup_object, endian, arena_data))?; + } + dwarf.populate_abbreviations_cache(gimli::AbbreviationsCacheStrategy::Duplicates); + + let ctx = Context::from_dwarf(dwarf)?; + + // Load the DWP file, ignoring errors. + let dwarf_package = (|| { + let mut dwp_path = path.to_path_buf(); + let dwp_extension = path + .extension() + .map(|previous_extension| { + let mut previous_extension = previous_extension.to_os_string(); + previous_extension.push(".dwp"); + previous_extension + }) + .unwrap_or_else(|| "dwp".into()); + dwp_path.set_extension(dwp_extension); + let dwp_file = File::open(&dwp_path).ok()?; + let map = arena_mmap.alloc(unsafe { Mmap::map(&dwp_file) }.ok()?); + let dwp_object = object::File::parse(&**map).ok()?; + + let endian = if dwp_object.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + let empty = gimli::EndianSlice::new(&[][..], endian); + gimli::DwarfPackage::load( + |id| load_section(id.dwo_name(), &dwp_object, endian, arena_data), + empty, + ) + .ok() + })(); + + Ok(LoaderInternal { + ctx, + object, + relative_address_base, + symbols, + dwarf_package, + object_map, + objects, + }) + } + + fn ctx( + &self, + probe: u64, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> (&Context<LoaderReader<'a>>, u64) { + self.object_ctx(probe, arena_data, arena_mmap) + .unwrap_or((&self.ctx, probe)) + } + + fn object_ctx( + &self, + probe: u64, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> Option<(&Context<LoaderReader<'a>>, u64)> { + let symbol = self.object_map.get(probe)?; + let object_context = self.objects[symbol.object_index()] + .get_or_init(|| { + ObjectContext::new(symbol.object(&self.object_map), arena_data, arena_mmap) + }) + .as_ref()?; + object_context.ctx(symbol.name(), probe - symbol.address()) + } + + fn find_symbol_info(&self, probe: u64) -> Option<Symbol<'a>> { + self.symbols.get(probe).map(|x| Symbol { + name: x.name(), + address: x.address(), + }) + } + + fn get_section_range(&self, section_name: &[u8]) -> Option<gimli::Range> { + self.object + .section_by_name_bytes(section_name) + .map(|section| { + let begin = section.address(); + let end = begin + section.size(); + gimli::Range { begin, end } + }) + } + + fn find_location( + &'a self, + probe: u64, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> Result<Option<Location<'a>>> { + let (ctx, probe) = self.ctx(probe, arena_data, arena_mmap); + Ok(ctx.find_location(probe)?) + } + + fn find_location_range( + &self, + probe_low: u64, + probe_high: u64, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> Result<LocationRangeIter<'_, LoaderReader<'a>>> { + let (ctx, probe) = self.ctx(probe_low, arena_data, arena_mmap); + // TODO: handle ranges that cover multiple objects + let probe_high = probe + (probe_high - probe_low); + Ok(ctx.find_location_range(probe, probe_high)?) + } + + fn find_frames( + &self, + probe: u64, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> Result<FrameIter<'_, LoaderReader<'a>>> { + let (ctx, probe) = self.ctx(probe, arena_data, arena_mmap); + let mut frames = ctx.find_frames(probe); + loop { + let (load, continuation) = match frames { + LookupResult::Output(output) => return Ok(output?), + LookupResult::Load { load, continuation } => (load, continuation), + }; + + let r = self.load_dwo(load, arena_data, arena_mmap)?; + frames = continuation.resume(r); + } + } + + fn load_dwo( + &self, + load: SplitDwarfLoad<LoaderReader<'a>>, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> Result<Option<Arc<gimli::Dwarf<LoaderReader<'a>>>>> { + // Load the DWO file from the DWARF package, if available. + if let Some(dwp) = self.dwarf_package.as_ref() { + if let Some(cu) = dwp.find_cu(load.dwo_id, &load.parent)? { + return Ok(Some(Arc::new(cu))); + } + } + + // Determine the path to the DWO file. + let mut path = PathBuf::new(); + if let Some(p) = load.comp_dir.as_ref() { + path.push(convert_path(p.slice())?); + } + let Some(p) = load.path.as_ref() else { + return Ok(None); + }; + path.push(convert_path(p.slice())?); + + // Load the DWO file, ignoring errors. + let dwo = (|| { + let file = File::open(&path).ok()?; + let map = arena_mmap.alloc(unsafe { Mmap::map(&file) }.ok()?); + let object = object::File::parse(&**map).ok()?; + let endian = if object.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + let mut dwo_dwarf = + gimli::Dwarf::load(|id| load_section(id.dwo_name(), &object, endian, arena_data)) + .ok()?; + let dwo_unit_header = dwo_dwarf.units().next().ok()??; + let dwo_unit = dwo_dwarf.unit(dwo_unit_header).ok()?; + if dwo_unit.dwo_id != Some(load.dwo_id) { + return None; + } + dwo_dwarf.make_dwo(&load.parent); + Some(Arc::new(dwo_dwarf)) + })(); + Ok(dwo) + } +} + +struct ObjectContext<'a> { + ctx: Context<LoaderReader<'a>>, + symbols: SymbolMap<SymbolMapName<'a>>, +} + +impl<'a> ObjectContext<'a> { + fn new( + object: &ObjectMapFile<'a>, + arena_data: &'a Arena<Vec<u8>>, + arena_mmap: &'a Arena<Mmap>, + ) -> Option<Self> { + let file = File::open(convert_path(object.path()).ok()?).ok()?; + let map = &**arena_mmap.alloc(unsafe { Mmap::map(&file) }.ok()?); + let data = if let Some(member_name) = object.member() { + let archive = object::read::archive::ArchiveFile::parse(map).ok()?; + let member = archive.members().find_map(|member| { + let member = member.ok()?; + if member.name() == member_name { + Some(member) + } else { + None + } + })?; + member.data(map).ok()? + } else { + map + }; + let object = object::File::parse(data).ok()?; + let endian = if object.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + let dwarf = + gimli::Dwarf::load(|id| load_section(Some(id.name()), &object, endian, arena_data)) + .ok()?; + let ctx = Context::from_dwarf(dwarf).ok()?; + let symbols = object.symbol_map(); + Some(ObjectContext { ctx, symbols }) + } + + fn ctx(&self, symbol_name: &[u8], probe: u64) -> Option<(&Context<LoaderReader<'a>>, u64)> { + self.symbols + .symbols() + .iter() + .find(|symbol| symbol.name().as_bytes() == symbol_name) + .map(|symbol| (&self.ctx, probe + symbol.address())) + } +} + +fn load_section<'input, Endian: gimli::Endianity>( + name: Option<&'static str>, + file: &object::File<'input>, + endian: Endian, + arena_data: &'input Arena<Vec<u8>>, +) -> Result<gimli::EndianSlice<'input, Endian>> { + let data = match name.and_then(|name| file.section_by_name(name)) { + Some(section) => match section.uncompressed_data()? { + Cow::Borrowed(b) => b, + Cow::Owned(b) => arena_data.alloc(b), + }, + None => &[], + }; + Ok(gimli::EndianSlice::new(data, endian)) +} + +#[cfg(unix)] +fn convert_path(bytes: &[u8]) -> Result<PathBuf> { + use std::os::unix::ffi::OsStrExt; + let s = OsStr::from_bytes(bytes); + Ok(PathBuf::from(s)) +} + +#[cfg(not(unix))] +fn convert_path(bytes: &[u8]) -> Result<PathBuf> { + let s = std::str::from_utf8(bytes)?; + Ok(PathBuf::from(s)) +} + +/// Information from a symbol table entry. +pub struct Symbol<'a> { + name: &'a str, + address: u64, +} + +impl<'a> Symbol<'a> { + /// Get the symbol name. + pub fn name(&self) -> &'a str { + self.name + } + + /// Get the symbol address. + pub fn address(&self) -> u64 { + self.address + } +}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/lookup.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/lookup.rs new file mode 100644 index 0000000..cc33dd5 --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/lookup.rs
@@ -0,0 +1,261 @@ +use alloc::sync::Arc; +use core::marker::PhantomData; +use core::ops::ControlFlow; + +/// This struct contains the information needed to find split DWARF data +/// and to produce a `gimli::Dwarf<R>` for it. +pub struct SplitDwarfLoad<R> { + /// The dwo id, for looking up in a DWARF package, or for + /// verifying an unpacked dwo found on the file system + pub dwo_id: gimli::DwoId, + /// The compilation directory `path` is relative to. + pub comp_dir: Option<R>, + /// A path on the filesystem, relative to `comp_dir` to find this dwo. + pub path: Option<R>, + /// Once the split DWARF data is loaded, the loader is expected + /// to call [make_dwo(parent)](gimli::read::Dwarf::make_dwo) before + /// returning the data. + pub parent: Arc<gimli::Dwarf<R>>, +} + +/// Operations that consult debug information may require additional files +/// to be loaded if split DWARF is being used. This enum returns the result +/// of the operation in the `Output` variant, or information about the split +/// DWARF that is required and a continuation to invoke once it is available +/// in the `Load` variant. +/// +/// This enum is intended to be used in a loop like so: +/// ```no_run +/// # use addr2line::*; +/// # use std::sync::Arc; +/// # let ctx: Context<gimli::EndianSlice<gimli::RunTimeEndian>> = todo!(); +/// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianSlice<gimli::RunTimeEndian>>>> { None }; +/// const ADDRESS: u64 = 0xdeadbeef; +/// let mut r = ctx.find_frames(ADDRESS); +/// let result = loop { +/// match r { +/// LookupResult::Output(result) => break result, +/// LookupResult::Load { load, continuation } => { +/// let dwo = do_split_dwarf_load(load); +/// r = continuation.resume(dwo); +/// } +/// } +/// }; +/// ``` +pub enum LookupResult<L: LookupContinuation> { + /// The lookup requires split DWARF data to be loaded. + Load { + /// The information needed to find the split DWARF data. + load: SplitDwarfLoad<<L as LookupContinuation>::Buf>, + /// The continuation to resume with the loaded split DWARF data. + continuation: L, + }, + /// The lookup has completed and produced an output. + Output(<L as LookupContinuation>::Output), +} + +/// This trait represents a partially complete operation that can be resumed +/// once a load of needed split DWARF data is completed or abandoned by the +/// API consumer. +pub trait LookupContinuation: Sized { + /// The final output of this operation. + type Output; + /// The type of reader used. + type Buf: gimli::Reader; + + /// Resumes the operation with the provided data. + /// + /// After the caller loads the split DWARF data required, call this + /// method to resume the operation. The return value of this method + /// indicates if the computation has completed or if further data is + /// required. + /// + /// If the additional data cannot be located, or the caller does not + /// support split DWARF, `resume(None)` can be used to continue the + /// operation with the data that is available. + fn resume(self, input: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self>; +} + +impl<L: LookupContinuation> LookupResult<L> { + /// Callers that do not handle split DWARF can call `skip_all_loads` + /// to fast-forward to the end result. This result is produced with + /// the data that is available and may be less accurate than the + /// the results that would be produced if the caller did properly + /// support split DWARF. + pub fn skip_all_loads(mut self) -> L::Output { + loop { + self = match self { + LookupResult::Output(t) => return t, + LookupResult::Load { continuation, .. } => continuation.resume(None), + }; + } + } + + pub(crate) fn map<T, F: FnOnce(L::Output) -> T>( + self, + f: F, + ) -> LookupResult<MappedLookup<T, L, F>> { + match self { + LookupResult::Output(t) => LookupResult::Output(f(t)), + LookupResult::Load { load, continuation } => LookupResult::Load { + load, + continuation: MappedLookup { + original: continuation, + mutator: f, + }, + }, + } + } + + pub(crate) fn unwrap(self) -> L::Output { + match self { + LookupResult::Output(t) => t, + LookupResult::Load { .. } => unreachable!("Internal API misuse"), + } + } +} + +pub(crate) struct SimpleLookup<T, R, F> +where + F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, + R: gimli::Reader, +{ + f: F, + phantom: PhantomData<(T, R)>, +} + +impl<T, R, F> SimpleLookup<T, R, F> +where + F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, + R: gimli::Reader, +{ + pub(crate) fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> { + LookupResult::Output(t) + } + + pub(crate) fn new_needs_load( + load: SplitDwarfLoad<R>, + f: F, + ) -> LookupResult<SimpleLookup<T, R, F>> { + LookupResult::Load { + load, + continuation: SimpleLookup { + f, + phantom: PhantomData, + }, + } + } +} + +impl<T, R, F> LookupContinuation for SimpleLookup<T, R, F> +where + F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, + R: gimli::Reader, +{ + type Output = T; + type Buf = R; + + fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { + LookupResult::Output((self.f)(v)) + } +} + +pub(crate) struct MappedLookup<T, L, F> +where + L: LookupContinuation, + F: FnOnce(L::Output) -> T, +{ + original: L, + mutator: F, +} + +impl<T, L, F> LookupContinuation for MappedLookup<T, L, F> +where + L: LookupContinuation, + F: FnOnce(L::Output) -> T, +{ + type Output = T; + type Buf = L::Buf; + + fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { + match self.original.resume(v) { + LookupResult::Output(t) => LookupResult::Output((self.mutator)(t)), + LookupResult::Load { load, continuation } => LookupResult::Load { + load, + continuation: MappedLookup { + original: continuation, + mutator: self.mutator, + }, + }, + } + } +} + +/// Some functions (e.g. `find_frames`) require considering multiple +/// compilation units, each of which might require their own split DWARF +/// lookup (and thus produce a continuation). +/// +/// We store the underlying continuation here as well as a mutator function +/// that will either a) decide that the result of this continuation is +/// what is needed and mutate it to the final result or b) produce another +/// `LookupResult`. `new_lookup` will in turn eagerly drive any non-continuation +/// `LookupResult` with successive invocations of the mutator, until a new +/// continuation or a final result is produced. And finally, the impl of +/// `LookupContinuation::resume` will call `new_lookup` each time the +/// computation is resumed. +pub(crate) struct LoopingLookup<T, L, F> +where + L: LookupContinuation, + F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, +{ + continuation: L, + mutator: F, +} + +impl<T, L, F> LoopingLookup<T, L, F> +where + L: LookupContinuation, + F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, +{ + pub(crate) fn new_complete(t: T) -> LookupResult<Self> { + LookupResult::Output(t) + } + + pub(crate) fn new_lookup(mut r: LookupResult<L>, mut mutator: F) -> LookupResult<Self> { + // Drive the loop eagerly so that we only ever have to represent one state + // (the r == ControlFlow::Continue state) in LoopingLookup. + loop { + match r { + LookupResult::Output(l) => match mutator(l) { + ControlFlow::Break(t) => return LookupResult::Output(t), + ControlFlow::Continue(r2) => { + r = r2; + } + }, + LookupResult::Load { load, continuation } => { + return LookupResult::Load { + load, + continuation: LoopingLookup { + continuation, + mutator, + }, + }; + } + } + } + } +} + +impl<T, L, F> LookupContinuation for LoopingLookup<T, L, F> +where + L: LookupContinuation, + F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, +{ + type Output = T; + type Buf = L::Buf; + + fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { + let r = self.continuation.resume(v); + LoopingLookup::new_lookup(r, self.mutator) + } +}
diff --git a/chromium_crates_io/vendor/addr2line-v0_25/src/unit.rs b/chromium_crates_io/vendor/addr2line-v0_25/src/unit.rs new file mode 100644 index 0000000..ef0bca2 --- /dev/null +++ b/chromium_crates_io/vendor/addr2line-v0_25/src/unit.rs
@@ -0,0 +1,598 @@ +use alloc::boxed::Box; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::cmp; + +use crate::{ + Context, DebugFile, Error, Function, Functions, LazyFunctions, LazyLines, LazyResult, + LineLocationRangeIter, Lines, Location, LookupContinuation, LookupResult, RangeAttributes, + SimpleLookup, SplitDwarfLoad, +}; + +pub(crate) struct UnitRange { + unit_id: usize, + min_begin: u64, + range: gimli::Range, +} + +pub(crate) struct ResUnit<R: gimli::Reader> { + offset: gimli::DebugInfoOffset<R::Offset>, + dw_unit: gimli::Unit<R>, + pub(crate) lang: Option<gimli::DwLang>, + lines: LazyLines, + functions: LazyFunctions<R>, + dwo: LazyResult<Option<Box<DwoUnit<R>>>>, +} + +type UnitRef<'unit, R> = (DebugFile, gimli::UnitRef<'unit, R>); + +impl<R: gimli::Reader> ResUnit<R> { + pub(crate) fn unit_ref<'a>(&'a self, sections: &'a gimli::Dwarf<R>) -> gimli::UnitRef<'a, R> { + gimli::UnitRef::new(sections, &self.dw_unit) + } + + /// Returns the DWARF sections and the unit. + /// + /// Loads the DWO unit if necessary. + pub(crate) fn dwarf_and_unit<'unit, 'ctx: 'unit>( + &'unit self, + ctx: &'ctx Context<R>, + ) -> LookupResult< + SimpleLookup< + Result<UnitRef<'unit, R>, Error>, + R, + impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<UnitRef<'unit, R>, Error>, + >, + > { + let map_dwo = move |dwo: &'unit Result<Option<Box<DwoUnit<R>>>, Error>| match dwo { + Ok(Some(dwo)) => Ok((DebugFile::Dwo, dwo.unit_ref())), + Ok(None) => Ok((DebugFile::Primary, self.unit_ref(&*ctx.sections))), + Err(e) => Err(*e), + }; + let complete = |dwo| SimpleLookup::new_complete(map_dwo(dwo)); + + if let Some(dwo) = self.dwo.get() { + return complete(dwo); + } + + let dwo_id = match self.dw_unit.dwo_id { + None => { + return complete(self.dwo.get_or_init(|| Ok(None))); + } + Some(dwo_id) => dwo_id, + }; + + let comp_dir = self.dw_unit.comp_dir.clone(); + + let dwo_name = self.dw_unit.dwo_name().and_then(|s| { + if let Some(s) = s { + Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?)) + } else { + Ok(None) + } + }); + + let path = match dwo_name { + Ok(v) => v, + Err(e) => { + return complete(self.dwo.get_or_init(|| Err(e))); + } + }; + + let process_dwo = move |dwo_dwarf: Option<Arc<gimli::Dwarf<R>>>| { + let dwo_dwarf = match dwo_dwarf { + None => return Ok(None), + Some(dwo_dwarf) => dwo_dwarf, + }; + let mut dwo_units = dwo_dwarf.units(); + let dwo_header = match dwo_units.next()? { + Some(dwo_header) => dwo_header, + None => return Ok(None), + }; + + let mut dwo_unit = dwo_dwarf.unit(dwo_header)?; + dwo_unit.copy_relocated_attributes(&self.dw_unit); + Ok(Some(Box::new(DwoUnit { + sections: dwo_dwarf, + dw_unit: dwo_unit, + }))) + }; + + SimpleLookup::new_needs_load( + SplitDwarfLoad { + dwo_id, + comp_dir, + path, + parent: ctx.sections.clone(), + }, + move |dwo_dwarf| map_dwo(self.dwo.get_or_init(|| process_dwo(dwo_dwarf))), + ) + } + + pub(crate) fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> { + // NB: line information is always stored in the main debug file so this does not need + // to handle DWOs. + let ilnp = match self.dw_unit.line_program { + Some(ref ilnp) => ilnp, + None => return Ok(None), + }; + self.lines.borrow(self.unit_ref(sections), ilnp).map(Some) + } + + pub(crate) fn parse_functions<'unit, 'ctx: 'unit>( + &'unit self, + ctx: &'ctx Context<R>, + ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>> + { + self.dwarf_and_unit(ctx).map(move |r| { + let (_file, unit) = r?; + self.functions.borrow(unit) + }) + } + + pub(crate) fn parse_inlined_functions<'unit, 'ctx: 'unit>( + &'unit self, + ctx: &'ctx Context<R>, + ) -> LookupResult<impl LookupContinuation<Output = Result<(), Error>, Buf = R> + 'unit> { + self.dwarf_and_unit(ctx).map(move |r| { + let (file, unit) = r?; + self.functions + .borrow(unit)? + .parse_inlined_functions(file, unit, ctx) + }) + } + + pub(crate) fn find_location( + &self, + probe: u64, + sections: &gimli::Dwarf<R>, + ) -> Result<Option<Location<'_>>, Error> { + let Some(lines) = self.parse_lines(sections)? else { + return Ok(None); + }; + lines.find_location(probe) + } + + #[inline] + pub(crate) fn find_location_range( + &self, + probe_low: u64, + probe_high: u64, + sections: &gimli::Dwarf<R>, + ) -> Result<Option<LineLocationRangeIter<'_>>, Error> { + let Some(lines) = self.parse_lines(sections)? else { + return Ok(None); + }; + lines.find_location_range(probe_low, probe_high).map(Some) + } + + pub(crate) fn find_function_or_location<'unit, 'ctx: 'unit>( + &'unit self, + probe: u64, + ctx: &'ctx Context<R>, + ) -> LookupResult< + impl LookupContinuation< + Output = Result<(Option<&'unit Function<R>>, Option<Location<'unit>>), Error>, + Buf = R, + >, + > { + self.dwarf_and_unit(ctx).map(move |r| { + let (file, unit) = r?; + let functions = self.functions.borrow(unit)?; + let function = match functions.find_address(probe) { + Some(address) => { + let function_index = functions.addresses[address].function; + let function = &functions.functions[function_index]; + Some(function.borrow(file, unit, ctx)?) + } + None => None, + }; + let location = self.find_location(probe, &ctx.sections)?; + Ok((function, location)) + }) + } +} + +pub(crate) struct ResUnits<R: gimli::Reader> { + ranges: Box<[UnitRange]>, + units: Box<[ResUnit<R>]>, +} + +impl<R: gimli::Reader> ResUnits<R> { + pub(crate) fn parse(sections: &gimli::Dwarf<R>) -> Result<Self, Error> { + // Find all the references to compilation units in .debug_aranges. + // Note that we always also iterate through all of .debug_info to + // find compilation units, because .debug_aranges may be missing some. + let mut aranges = Vec::new(); + let mut headers = sections.debug_aranges.headers(); + while let Some(header) = headers.next()? { + aranges.push((header.debug_info_offset(), header.offset())); + } + aranges.sort_by_key(|i| i.0); + + let mut unit_ranges = Vec::new(); + let mut res_units = Vec::new(); + let mut units = sections.units(); + while let Some(header) = units.next()? { + let unit_id = res_units.len(); + let offset = match header.offset().as_debug_info_offset() { + Some(offset) => offset, + None => continue, + }; + // We mainly want compile units, but we may need to follow references to entries + // within other units for function names. We don't need anything from type units. + let mut need_unit_range = match header.type_() { + gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue, + gimli::UnitType::Partial => { + // Partial units are only needed for references from other units. + // They shouldn't have any address ranges. + false + } + _ => true, + }; + let dw_unit = match sections.unit(header) { + Ok(dw_unit) => dw_unit, + Err(_) => continue, + }; + let dw_unit_ref = gimli::UnitRef::new(sections, &dw_unit); + + let mut lang = None; + if need_unit_range { + let mut entries = dw_unit_ref.entries_raw(None)?; + + let abbrev = match entries.read_abbreviation()? { + Some(abbrev) => abbrev, + None => continue, + }; + + let mut ranges = RangeAttributes::default(); + for spec in abbrev.attributes() { + let attr = entries.read_attribute(*spec)?; + match attr.name() { + gimli::DW_AT_low_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.low_pc = Some(dw_unit_ref.address(index)?); + } + _ => {} + }, + gimli::DW_AT_high_pc => match attr.value() { + gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val), + gimli::AttributeValue::DebugAddrIndex(index) => { + ranges.high_pc = Some(dw_unit_ref.address(index)?); + } + gimli::AttributeValue::Udata(val) => ranges.size = Some(val), + _ => {} + }, + gimli::DW_AT_ranges => { + ranges.ranges_offset = dw_unit_ref.attr_ranges_offset(attr.value())?; + } + gimli::DW_AT_language => { + if let gimli::AttributeValue::Language(val) = attr.value() { + lang = Some(val); + } + } + _ => {} + } + } + + // Find the address ranges for the CU, using in order of preference: + // - DW_AT_ranges + // - .debug_aranges + // - DW_AT_low_pc/DW_AT_high_pc + // + // Using DW_AT_ranges before .debug_aranges is possibly an arbitrary choice, + // but the feeling is that DW_AT_ranges is more likely to be reliable or complete + // if it is present. + // + // .debug_aranges must be used before DW_AT_low_pc/DW_AT_high_pc because + // it has been observed on macOS that DW_AT_ranges was not emitted even for + // discontiguous CUs. + let i = match ranges.ranges_offset { + Some(_) => None, + None => aranges.binary_search_by_key(&offset, |x| x.0).ok(), + }; + if let Some(mut i) = i { + // There should be only one set per CU, but in practice multiple + // sets have been observed. This is probably a compiler bug, but + // either way we need to handle it. + while i > 0 && aranges[i - 1].0 == offset { + i -= 1; + } + for (_, aranges_offset) in aranges[i..].iter().take_while(|x| x.0 == offset) { + let aranges_header = sections.debug_aranges.header(*aranges_offset)?; + let mut aranges = aranges_header.entries(); + while let Some(arange) = aranges.next().transpose() { + let Ok(arange) = arange else { + // Ignore errors. In particular, this will ignore address overflow. + // This has been seen for a unit that had a single variable + // with rustc 1.89.0. + // + // This relies on `ArangeEntryIter::next` fusing for errors that + // can't be ignored. + continue; + }; + if arange.length() != 0 { + unit_ranges.push(UnitRange { + range: arange.range(), + unit_id, + min_begin: 0, + }); + need_unit_range = false; + } + } + } + } + if need_unit_range { + need_unit_range = !ranges.for_each_range(dw_unit_ref, |range| { + unit_ranges.push(UnitRange { + range, + unit_id, + min_begin: 0, + }); + })?; + } + } + + let lines = LazyLines::new(); + if need_unit_range { + // The unit did not declare any ranges. + // Try to get some ranges from the line program sequences. + if let Some(ref ilnp) = dw_unit_ref.line_program { + if let Ok(lines) = lines.borrow(dw_unit_ref, ilnp) { + for range in lines.ranges() { + unit_ranges.push(UnitRange { + range, + unit_id, + min_begin: 0, + }) + } + } + } + } + + res_units.push(ResUnit { + offset, + dw_unit, + lang, + lines, + functions: LazyFunctions::new(), + dwo: LazyResult::new(), + }); + } + + // Sort this for faster lookup in `Self::find_range`. + unit_ranges.sort_by_key(|i| i.range.end); + + // Calculate the `min_begin` field now that we've determined the order of + // CUs. + let mut min = !0; + for i in unit_ranges.iter_mut().rev() { + min = min.min(i.range.begin); + i.min_begin = min; + } + + Ok(ResUnits { + ranges: unit_ranges.into_boxed_slice(), + units: res_units.into_boxed_slice(), + }) + } + + pub(crate) fn iter(&self) -> impl Iterator<Item = &ResUnit<R>> { + self.units.iter() + } + + pub(crate) fn find_offset( + &self, + offset: gimli::DebugInfoOffset<R::Offset>, + ) -> Result<&gimli::Unit<R>, Error> { + match self + .units + .binary_search_by_key(&offset.0, |unit| unit.offset.0) + { + // There is never a DIE at the unit offset or before the first unit. + Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset), + Err(i) => Ok(&self.units[i - 1].dw_unit), + } + } + + /// Finds the CUs for the function address given. + /// + /// There might be multiple CUs whose range contains this address. + /// Weak symbols have shown up in the wild which cause this to happen + /// but otherwise this can happen if the CU has non-contiguous functions + /// but only reports a single range. + /// + /// Consequently we return an iterator for all CUs which may contain the + /// address, and the caller must check if there is actually a function or + /// location in the CU for that address. + pub(crate) fn find(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> { + self.find_range(probe, probe + 1).map(|(unit, _range)| unit) + } + + /// Finds the CUs covering the range of addresses given. + /// + /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple + /// ranges for the same unit. + #[inline] + pub(crate) fn find_range( + &self, + probe_low: u64, + probe_high: u64, + ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> { + // Find the position of the next range after a range which + // ends at `probe_low` or lower. + let pos = match self + .ranges + .binary_search_by_key(&probe_low, |i| i.range.end) + { + Ok(i) => i + 1, // Range `i` ends at exactly `probe_low`. + Err(i) => i, // Range `i - 1` ends at a lower address. + }; + + // Iterate from that position to find matching CUs. + self.ranges[pos..] + .iter() + .take_while(move |i| { + // We know that this CU's end is at least `probe_low` because + // of our sorted array. + debug_assert!(i.range.end >= probe_low); + + // Each entry keeps track of the minimum begin address for the + // remainder of the array of unit ranges. If our probe is before + // the minimum range begin of this entry, then it's guaranteed + // to not fit in any subsequent entries, so we break out. + probe_high > i.min_begin + }) + .filter_map(move |i| { + // If this CU doesn't actually contain this address, move to the + // next CU. + if probe_low >= i.range.end || probe_high <= i.range.begin { + return None; + } + Some((&self.units[i.unit_id], &i.range)) + }) + } + + pub(crate) fn find_location_range<'a>( + &'a self, + probe_low: u64, + probe_high: u64, + sections: &'a gimli::Dwarf<R>, + ) -> Result<LocationRangeIter<'a, R>, Error> { + let unit_iter = Box::new(self.find_range(probe_low, probe_high)); + Ok(LocationRangeIter { + unit_iter, + iter: None, + probe_low, + probe_high, + sections, + }) + } +} + +/// A DWO unit has its own DWARF sections. +struct DwoUnit<R: gimli::Reader> { + sections: Arc<gimli::Dwarf<R>>, + dw_unit: gimli::Unit<R>, +} + +impl<R: gimli::Reader> DwoUnit<R> { + fn unit_ref(&self) -> gimli::UnitRef<'_, R> { + gimli::UnitRef::new(&self.sections, &self.dw_unit) + } +} + +pub(crate) struct SupUnit<R: gimli::Reader> { + offset: gimli::DebugInfoOffset<R::Offset>, + dw_unit: gimli::Unit<R>, +} + +pub(crate) struct SupUnits<R: gimli::Reader> { + units: Box<[SupUnit<R>]>, +} + +impl<R: gimli::Reader> Default for SupUnits<R> { + fn default() -> Self { + SupUnits { + units: Box::default(), + } + } +} + +impl<R: gimli::Reader> SupUnits<R> { + pub(crate) fn parse(sections: &gimli::Dwarf<R>) -> Result<Self, Error> { + let mut sup_units = Vec::new(); + let mut units = sections.units(); + while let Some(header) = units.next()? { + let offset = match header.offset().as_debug_info_offset() { + Some(offset) => offset, + None => continue, + }; + let dw_unit = match sections.unit(header) { + Ok(dw_unit) => dw_unit, + Err(_) => continue, + }; + sup_units.push(SupUnit { dw_unit, offset }); + } + Ok(SupUnits { + units: sup_units.into_boxed_slice(), + }) + } + + pub(crate) fn find_offset( + &self, + offset: gimli::DebugInfoOffset<R::Offset>, + ) -> Result<&gimli::Unit<R>, Error> { + match self + .units + .binary_search_by_key(&offset.0, |unit| unit.offset.0) + { + // There is never a DIE at the unit offset or before the first unit. + Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset), + Err(i) => Ok(&self.units[i - 1].dw_unit), + } + } +} + +/// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`. +pub struct LocationRangeIter<'ctx, R: gimli::Reader> { + unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>, + iter: Option<LineLocationRangeIter<'ctx>>, + + probe_low: u64, + probe_high: u64, + sections: &'ctx gimli::Dwarf<R>, +} + +impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> { + fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> { + loop { + let iter = self.iter.take(); + match iter { + None => match self.unit_iter.next() { + Some((unit, range)) => { + self.iter = unit.find_location_range( + cmp::max(self.probe_low, range.begin), + cmp::min(self.probe_high, range.end), + self.sections, + )?; + } + None => return Ok(None), + }, + Some(mut iter) => { + if let item @ Some(_) = iter.next() { + self.iter = Some(iter); + return Ok(item); + } + } + } + } + } +} + +impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + type Item = (u64, u64, Location<'ctx>); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.next_loc().unwrap_or_default() + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R> +where + R: gimli::Reader + 'ctx, +{ + type Item = (u64, u64, Location<'ctx>); + type Error = Error; + + #[inline] + fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> { + self.next_loc() + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/.cargo-checksum.json b/chromium_crates_io/vendor/backtrace-v0_3/.cargo-checksum.json new file mode 100644 index 0000000..697c9ce --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/.cargo-checksum.json
@@ -0,0 +1 @@ +{"files":{}}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/.cargo_vcs_info.json b/chromium_crates_io/vendor/backtrace-v0_3/.cargo_vcs_info.json new file mode 100644 index 0000000..ad76f0d --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "775f6a1ba62e7d35a1fac76e64c61d9d4687b5f2" + }, + "path_in_vcs": "" +} \ No newline at end of file
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/.github/workflows/main.yml b/chromium_crates_io/vendor/backtrace-v0_3/.github/workflows/main.yml new file mode 100644 index 0000000..9b70e41 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/.github/workflows/main.yml
@@ -0,0 +1,283 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test: + name: Test + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-24.04 + rust: stable + - os: ubuntu-24.04 + rust: beta + - os: ubuntu-24.04 + rust: nightly + - os: macos-14 + rust: stable + - os: macos-14 + rust: nightly + - os: macos-latest # macOS 15 + rust: stable + - os: windows-latest + rust: stable-x86_64-msvc + - os: windows-latest + rust: stable-i686-msvc + - os: windows-latest + rust: stable-x86_64-gnu + - os: windows-latest + rust: nightly-x86_64-msvc + - os: windows-latest + rust: nightly-i686-msvc + - os: windows-latest + rust: nightly-x86_64-gnu + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Rust (rustup) + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + shell: bash + - run: echo RUSTFLAGS=-Dwarnings >> $GITHUB_ENV + shell: bash + + # full fidelity of backtraces on 32-bit msvc requires frame pointers, so + # enable that for our tests + - name: Force frame pointers + run: echo RUSTFLAGS="-Cforce-frame-pointers $RUSTFLAGS" >> $GITHUB_ENV + shell: bash + if: contains(matrix.rust, 'i686') + + # Starting with Ubuntu 22.04 libc6-dbg is needed. + - name: Install libc debug info + run: sudo apt-get install -y libc6-dbg + shell: bash + if: contains(matrix.os, 'ubuntu-24.04') + + - name: Enable collapse_debuginfo based on version + run: echo RUSTFLAGS="--cfg dbginfo=\"collapsible\" $RUSTFLAGS" >> $GITHUB_ENV + shell: bash + if: contains(matrix.rust, 'nightly') || contains(matrix.rust, 'beta') + + - run: cargo build + - run: cargo test + - run: cargo test --features "serialize-serde" + - run: cargo test --features "cpp_demangle" + - run: cargo test --no-default-features + - run: cargo test --no-default-features --features "std" + - run: cargo test --manifest-path crates/cpp_smoke_test/Cargo.toml + # This test is specifically about packed debuginfo with `*.dSYM` files + - run: cargo test --manifest-path crates/macos_frames_test/Cargo.toml + env: + CARGO_PROFILE_DEV_SPLIT_DEBUGINFO: packed + CARGO_PROFILE_TEST_SPLIT_DEBUGINFO: packed + - run: cargo test --manifest-path crates/without_debuginfo/Cargo.toml + - if: matrix.os != 'macos-latest' + run: cargo test --manifest-path crates/line-tables-only/Cargo.toml + # FIXME: This currently fails on macOS 15. + - if: matrix.os == 'macos-latest' + run: '! cargo test --manifest-path crates/line-tables-only/Cargo.toml' + + # Test debuginfo compression still works + - run: cargo test + if: contains(matrix.os, 'ubuntu') + env: + RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zlib" + - run: cargo test --features "ruzstd" + if: contains(matrix.os, 'ubuntu-24.04') || + (contains(matrix.os, 'ubuntu') && contains(matrix.rust, 'nightly')) + env: + RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zstd" + + # Test that, on macOS, packed/unpacked debuginfo both work + - run: cargo clean && cargo test + # Test that, on macOS, packed/unpacked debuginfo both work + if: contains(matrix.os, 'macos') + env: + CARGO_PROFILE_DEV_SPLIT_DEBUGINFO: unpacked + CARGO_PROFILE_TEST_SPLIT_DEBUGINFO: unpacked + - run: cargo clean && cargo test + if: contains(matrix.os, 'macos') + env: + CARGO_PROFILE_DEV_SPLIT_DEBUGINFO: packed + CARGO_PROFILE_TEST_SPLIT_DEBUGINFO: packed + # Test that, on macOS, binaries with no UUID work + - run: cargo clean && cargo test + if: contains(matrix.os, 'macos') + env: + RUSTFLAGS: "-C link-arg=-Wl,-no_uuid" + + # Test that, on Linux, packed/unpacked debuginfo both work + - run: cargo clean && cargo test + if: matrix.rust == 'nightly' + env: + RUSTFLAGS: "-C split-debuginfo=unpacked -Zunstable-options" + - run: cargo clean && cargo test + if: matrix.rust == 'nightly' + env: + RUSTFLAGS: "-C split-debuginfo=packed -Zunstable-options" + + # Test that separate debug info works + - run: ./ci/debuglink-docker.sh + if: contains(matrix.os, 'ubuntu') + + # Test that backtraces are still symbolicated if we don't embed an absolute + # path to the PDB file in the binary. + # Add -Cforce-frame-pointers for stability. The test otherwise fails + # non-deterministically on i686-pc-windows-msvc because the stack cannot be + # unwound reliably. This failure is not related to the feature being tested. + - run: cargo clean && cargo test + if: contains(matrix.rust, 'msvc') + name: "Test that backtraces are symbolicated without absolute PDB path" + env: + RUSTFLAGS: "-Clink-arg=/PDBALTPATH:%_PDB% -Cforce-frame-pointers" + + # Test that including as a submodule will still work, both with and without + # the `backtrace` feature enabled. + # Building as if part of std requires nightly features to be available + - run: cargo build --manifest-path crates/as-if-std/Cargo.toml + if: matrix.rust == 'nightly' + - run: cargo build --manifest-path crates/as-if-std/Cargo.toml --no-default-features + if: matrix.rust == 'nightly' + + windows_arm64: + name: Windows AArch64 + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Rust + run: rustup update stable --no-self-update && rustup default stable + shell: bash + - run: echo RUSTFLAGS=-Dwarnings >> $GITHUB_ENV + shell: bash + - run: rustup target add aarch64-pc-windows-msvc + - run: cargo test --no-run --target aarch64-pc-windows-msvc + + ios: + name: iOS + runs-on: macos-latest + strategy: + matrix: + include: + - target: aarch64-apple-ios + - target: x86_64-apple-ios + - target: aarch64-apple-ios-macabi + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: rustup target add ${{ matrix.target }} + - name: Run tests + run: cargo test ${{ contains(matrix.target, 'macabi') && '' || '--no-run' }} --target ${{ matrix.target }} + env: + RUSTFLAGS: -Dwarnings + + docker: + name: Docker + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + target: + - aarch64-unknown-linux-gnu + - arm-unknown-linux-gnueabihf + - armv7-unknown-linux-gnueabihf + - i586-unknown-linux-gnu + - i686-unknown-linux-gnu + - powerpc64-unknown-linux-gnu + - s390x-unknown-linux-gnu + - x86_64-pc-windows-gnu + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + - arm-linux-androideabi + - armv7-linux-androideabi + - aarch64-linux-android + - i686-linux-android + - x86_64-linux-android + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Rust + run: rustup update stable --no-self-update && rustup default stable + - run: rustup target add ${{ matrix.target }} + - run: cargo generate-lockfile + - run: echo RUSTFLAGS=-Dwarnings >> $GITHUB_ENV + shell: bash + - run: ./ci/run-docker.sh ${{ matrix.target }} + + rustfmt: + name: Rustfmt + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Rust + run: rustup update stable --no-self-update && rustup default stable && rustup component add rustfmt + - run: cargo fmt --all -- --check + + build: + name: Build Targets + runs-on: ubuntu-24.04 + strategy: + matrix: + target: + - wasm32-unknown-unknown + - wasm32-wasip1 + - x86_64-unknown-fuchsia + - x86_64-fortanix-unknown-sgx + - x86_64-unknown-illumos + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Rust + run: rustup update nightly --no-self-update && rustup default nightly + - run: rustup target add ${{ matrix.target }} + - run: echo RUSTFLAGS=-Dwarnings >> $GITHUB_ENV + shell: bash + - run: cargo build --target ${{ matrix.target }} + - run: cargo build --manifest-path crates/as-if-std/Cargo.toml --target ${{ matrix.target }} + + msrv: + name: MSRV + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-24.04 + - os: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Rust + run: rustup update 1.82.0 --no-self-update && rustup default 1.82.0 + - run: cargo build + + miri: + name: Miri + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Miri + run: | + rustup toolchain install nightly --component miri + rustup override set nightly + cargo miri setup + - run: MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/.github/workflows/publish.yml b/chromium_crates_io/vendor/backtrace-v0_3/.github/workflows/publish.yml new file mode 100644 index 0000000..9d74db3 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/.github/workflows/publish.yml
@@ -0,0 +1,52 @@ +name: Release-plz + +permissions: + pull-requests: write + contents: write + +on: + push: { branches: [master] } + +jobs: + # Release unpublished packages. + release-plz-release: + name: Release-plz release + runs-on: ubuntu-24.04 + if: ${{ github.repository_owner == 'rust-lang' }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Rust toolchain + run: rustup update stable --no-self-update && rustup default stable + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + # Create a PR with the new versions and changelog, preparing the next release. + release-plz-pr: + name: Release-plz PR + runs-on: ubuntu-24.04 + if: ${{ github.repository_owner == 'rust-lang' }} + concurrency: + group: release-plz-${{ github.ref }} + cancel-in-progress: false + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Rust toolchain + run: rustup update stable --no-self-update && rustup default stable + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/.gitignore b/chromium_crates_io/vendor/backtrace-v0_3/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/.gitignore
@@ -0,0 +1 @@ +target
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/CHANGELOG.md b/chromium_crates_io/vendor/backtrace-v0_3/CHANGELOG.md new file mode 100644 index 0000000..9a2cc4f --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/CHANGELOG.md
@@ -0,0 +1,33 @@ +# Changelog + +Notable changes to this project should be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +The backtrace crate attempts to adhere to the modified [Cargo interpretation of SemVer](https://doc.rust-lang.org/cargo/reference/resolver.html#semver-compatibility). +As a unique component of `std` it may make exceptional changes in order to support `std`. + +## [Unreleased] + +## [0.3.76](https://github.com/rust-lang/backtrace-rs/compare/backtrace-v0.3.75...backtrace-v0.3.76) - 2025-09-26 + +### Behavior +- Fix inverted polarity of "full printing" logic in rust-lang/backtrace-rs#726: + Previously we used to do the opposite of what you would expect. + +### Platform Support + +- Windows: Removed hypothetical soundness risk from padding bytes in rust-lang/backtrace-rs#737 +- Fuchsia: Added appropriate alignment checks during `Elf_Nhdr` parsing in rust-lang/backtrace-rs#725 +- Cygwin: Added support in rust-lang/backtrace-rs#704 +- Windows (32-bit Arm): Restore support in rust-lang/backtrace-rs#685 +- NuttX (32-bit Arm): Use builtin `_Unwind_GetIP` in rust-lang/backtrace-rs#692 +- RTEMS: Enable libunwind in rust-lang/backtrace-rs#682 + +### Dependencies + +- Update cpp_demangle to 0.5 in rust-lang/backtrace-rs#732 +- Update memchr to 2.7.6 in rust-lang/backtrace-rs#734 +- Switch from windows-targets to windows-link in rust-lang/backtrace-rs#727 +- Update ruzstd to 0.8.1 in rust-lang/backtrace-rs#718 +- Update object to 0.37 in rust-lang/backtrace-rs#718 +- Update addr2line to 0.25 in rust-lang/backtrace-rs#718
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/Cargo.lock b/chromium_crates_io/vendor/backtrace-v0_3/Cargo.lock new file mode 100644 index 0000000..9f9086a --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/Cargo.lock
@@ -0,0 +1,169 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "backtrace" +version = "0.3.76" +dependencies = [ + "addr2line", + "cfg-if", + "cpp_demangle", + "libc", + "libloading", + "miniz_oxide", + "object", + "rustc-demangle", + "ruzstd", + "serde", + "windows-link", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpp_demangle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcc405d55da54ad965aff198909afdcc8aeefc8ac6ba26d6abd19aa8aeacb2a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "gimli" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "object" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a" +dependencies = [ + "memchr", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "ruzstd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/Cargo.toml b/chromium_crates_io/vendor/backtrace-v0_3/Cargo.toml new file mode 100644 index 0000000..466be34 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/Cargo.toml
@@ -0,0 +1,157 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.82.0" +name = "backtrace" +version = "0.3.76" +authors = ["The Rust Project Developers"] +build = false +exclude = ["/ci/"] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = """ +A library to acquire a stack trace (backtrace) at runtime in a Rust program. +""" +homepage = "https://github.com/rust-lang/backtrace-rs" +documentation = "https://docs.rs/backtrace" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/backtrace-rs" + +[features] +coresymbolication = [] +dbghelp = [] +default = ["std"] +dl_iterate_phdr = [] +dladdr = [] +kernel32 = [] +libunwind = [] +ruzstd = ["dep:ruzstd"] +serialize-serde = ["serde"] +std = [] +unix-backtrace = [] + +[lib] +name = "backtrace" +path = "src/lib.rs" + +[[example]] +name = "backtrace" +path = "examples/backtrace.rs" +required-features = ["std"] + +[[example]] +name = "raw" +path = "examples/raw.rs" +required-features = ["std"] + +[[test]] +name = "accuracy" +path = "tests/accuracy/main.rs" +required-features = ["std"] +edition = "2021" + +[[test]] +name = "concurrent-panics" +path = "tests/concurrent-panics.rs" +harness = false +required-features = ["std"] + +[[test]] +name = "current-exe-mismatch" +path = "tests/current-exe-mismatch.rs" +harness = false +required-features = ["std"] + +[[test]] +name = "long_fn_name" +path = "tests/long_fn_name.rs" +required-features = ["std"] + +[[test]] +name = "sgx-image-base" +path = "tests/sgx-image-base.rs" + +[[test]] +name = "skip_inner_frames" +path = "tests/skip_inner_frames.rs" +required-features = ["std"] + +[[test]] +name = "smoke" +path = "tests/smoke.rs" +required-features = ["std"] +edition = "2021" + +[[bench]] +name = "benchmarks" +path = "benches/benchmarks.rs" + +[dependencies.cfg-if] +version = "1.0" + +[dependencies.cpp_demangle] +version = "0.5.0" +features = ["alloc"] +optional = true +default-features = false + +[dependencies.rustc-demangle] +version = "0.1.24" + +[dependencies.serde] +version = "1.0" +features = ["derive"] +optional = true + +[dev-dependencies.libloading] +version = "0.8" + +[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-link] +version = "0.2" + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.addr2line] +version = "0.25.0" +default-features = false + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.libc] +version = "0.2.156" +default-features = false + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.miniz_oxide] +version = "0.8" +default-features = false + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.object] +version = "0.37.0" +features = [ + "read_core", + "elf", + "macho", + "pe", + "xcoff", + "unaligned", + "archive", +] +default-features = false + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.ruzstd] +version = "0.8.1" +optional = true +default-features = false + +[lints.rust] +unexpected_cfgs = "allow"
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/Cargo.toml.orig b/chromium_crates_io/vendor/backtrace-v0_3/Cargo.toml.orig new file mode 100644 index 0000000..78f5276 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/Cargo.toml.orig
@@ -0,0 +1,121 @@ +[package] +name = "backtrace" +version = "0.3.76" +authors = ["The Rust Project Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-lang/backtrace-rs" +homepage = "https://github.com/rust-lang/backtrace-rs" +documentation = "https://docs.rs/backtrace" +description = """ +A library to acquire a stack trace (backtrace) at runtime in a Rust program. +""" +autoexamples = true +autotests = true +edition = "2021" +exclude = ["/ci/"] +rust-version = "1.82.0" + +[workspace] +members = ['crates/cpp_smoke_test', 'crates/as-if-std'] +exclude = [ + 'crates/without_debuginfo', + 'crates/macos_frames_test', + 'crates/line-tables-only', + 'crates/debuglink', +] + +[dependencies] +cfg-if = "1.0" +rustc-demangle = "0.1.24" + +# Optionally enable the ability to serialize a `Backtrace`, controlled through +# the `serialize-serde` feature below. +serde = { version = "1.0", optional = true, features = ['derive'] } + +# Optionally demangle C++ frames' symbols in backtraces. +cpp_demangle = { default-features = false, version = "0.5.0", optional = true, features = [ + "alloc", +] } + +[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies] +windows-link = "0.2" + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] +miniz_oxide = { version = "0.8", default-features = false } +ruzstd = { version = "0.8.1", default-features = false, optional = true } +addr2line = { version = "0.25.0", default-features = false } +libc = { version = "0.2.156", default-features = false } + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.object] +version = "0.37.0" +default-features = false +features = ['read_core', 'elf', 'macho', 'pe', 'xcoff', 'unaligned', 'archive'] + +[dev-dependencies] +dylib-dep = { path = "crates/dylib-dep" } +libloading = "0.8" + +[features] +# By default libstd support is enabled. +default = ["std"] + +# Include std support. This enables types like `Backtrace`. +std = [] + +serialize-serde = ["serde"] + +ruzstd = ["dep:ruzstd"] + +#======================================= +# Deprecated/internal features +# +# Only here for backwards compatibility purposes or for internal testing +# purposes. New code should use none of these features. +coresymbolication = [] +dbghelp = [] +dl_iterate_phdr = [] +dladdr = [] +kernel32 = [] +libunwind = [] +unix-backtrace = [] + +[[example]] +name = "backtrace" +required-features = ["std"] + +[[example]] +name = "raw" +required-features = ["std"] + +[[test]] +name = "skip_inner_frames" +required-features = ["std"] + +[[test]] +name = "long_fn_name" +required-features = ["std"] + +[[test]] +name = "smoke" +required-features = ["std"] +edition = '2021' + +[[test]] +name = "accuracy" +required-features = ["std"] +edition = '2021' + +[[test]] +name = "concurrent-panics" +required-features = ["std"] +harness = false + +[[test]] +name = "current-exe-mismatch" +required-features = ["std"] +harness = false + +[lints.rust] +# This crate uses them pervasively +unexpected_cfgs = "allow"
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-APACHE b/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/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/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-MIT b/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-MIT new file mode 100644 index 0000000..39e0ed6 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-MIT
@@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/README.md b/chromium_crates_io/vendor/backtrace-v0_3/README.md new file mode 100644 index 0000000..cebbad6 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/README.md
@@ -0,0 +1,85 @@ +# backtrace-rs + +[Documentation](https://docs.rs/backtrace) + +A library for acquiring backtraces at runtime for Rust. This library aims to +enhance the support of the standard library by providing a programmatic +interface to work with, but it also supports simply easily printing the current +backtrace like libstd's panics. + +## Install + +```toml +[dependencies] +backtrace = "0.3" +``` + +## Usage + +To simply capture a backtrace and defer dealing with it until a later time, +you can use the top-level `Backtrace` type. + +```rust +use backtrace::Backtrace; + +fn main() { + let bt = Backtrace::new(); + + // do_some_work(); + + println!("{bt:?}"); +} +``` + +If, however, you'd like more raw access to the actual tracing functionality, you +can use the `trace` and `resolve` functions directly. + +```rust +fn main() { + backtrace::trace(|frame| { + let ip = frame.ip(); + let symbol_address = frame.symbol_address(); + + // Resolve this instruction pointer to a symbol name + backtrace::resolve_frame(frame, |symbol| { + if let Some(name) = symbol.name() { + // ... + } + if let Some(filename) = symbol.filename() { + // ... + } + }); + + true // keep going to the next frame + }); +} +``` + +# Supported Rust Versions + +The `backtrace` crate is a core component of the standard library, and must +at times keep up with the evolution of various platforms in order to serve +the standard library's needs. This often means using recent libraries +that provide unwinding and symbolication for various platforms. +Thus `backtrace` is likely to use recent Rust features or depend on a library +which itself uses them. Its minimum supported Rust version, by policy, is +within a few versions of current stable, approximately "stable - 2". + +This policy takes precedence over versions written anywhere else in this repo. + +# License + +This project is 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 + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in backtrace-rs 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/chromium_crates_io/vendor/backtrace-v0_3/benches/benchmarks.rs b/chromium_crates_io/vendor/backtrace-v0_3/benches/benchmarks.rs new file mode 100644 index 0000000..e14e733 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/benches/benchmarks.rs
@@ -0,0 +1,92 @@ +#![feature(test)] + +extern crate test; + +#[cfg(feature = "std")] +use backtrace::Backtrace; + +#[bench] +#[cfg(feature = "std")] +fn trace(b: &mut test::Bencher) { + #[inline(never)] + fn the_function() { + backtrace::trace(|frame| { + let ip = frame.ip(); + test::black_box(ip); + true + }); + } + b.iter(the_function); +} + +#[bench] +#[cfg(feature = "std")] +fn trace_and_resolve_callback(b: &mut test::Bencher) { + #[inline(never)] + fn the_function() { + backtrace::trace(|frame| { + backtrace::resolve(frame.ip(), |symbol| { + let addr = symbol.addr(); + test::black_box(addr); + }); + true + }); + } + b.iter(the_function); +} + +#[bench] +#[cfg(feature = "std")] +fn trace_and_resolve_separate(b: &mut test::Bencher) { + #[inline(never)] + fn the_function(frames: &mut Vec<*mut std::ffi::c_void>) { + backtrace::trace(|frame| { + frames.push(frame.ip()); + true + }); + frames.iter().for_each(|frame_ip| { + backtrace::resolve(*frame_ip, |symbol| { + test::black_box(symbol); + }); + }); + } + let mut frames = Vec::with_capacity(1024); + b.iter(|| { + the_function(&mut frames); + frames.clear(); + }); +} + +#[bench] +#[cfg(feature = "std")] +fn new_unresolved(b: &mut test::Bencher) { + #[inline(never)] + fn the_function() { + let bt = Backtrace::new_unresolved(); + test::black_box(bt); + } + b.iter(the_function); +} + +#[bench] +#[cfg(feature = "std")] +fn new(b: &mut test::Bencher) { + #[inline(never)] + fn the_function() { + let bt = Backtrace::new(); + test::black_box(bt); + } + b.iter(the_function); +} + +#[bench] +#[cfg(feature = "std")] +fn new_unresolved_and_resolve_separate(b: &mut test::Bencher) { + #[inline(never)] + fn the_function() { + let mut bt = Backtrace::new_unresolved(); + bt.resolve(); + test::black_box(bt); + } + b.iter(the_function); +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/bindings.txt b/chromium_crates_io/vendor/backtrace-v0_3/bindings.txt new file mode 100644 index 0000000..166224b --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/bindings.txt
@@ -0,0 +1,63 @@ +--out src/windows_sys.rs +--config sys flatten +--filter +Windows.Win32.Foundation.CloseHandle +Windows.Win32.Foundation.FALSE +Windows.Win32.Foundation.HINSTANCE +Windows.Win32.Foundation.INVALID_HANDLE_VALUE +Windows.Win32.Foundation.TRUE +Windows.Win32.Globalization.CP_UTF8 +Windows.Win32.Globalization.lstrlenW +Windows.Win32.Globalization.WideCharToMultiByte +Windows.Win32.System.Diagnostics.Debug.AddrModeFlat +Windows.Win32.System.Diagnostics.Debug.CONTEXT +Windows.Win32.System.Diagnostics.Debug.EnumerateLoadedModulesW64 +Windows.Win32.System.Diagnostics.Debug.IMAGEHLP_LINEW64 +Windows.Win32.System.Diagnostics.Debug.MAX_SYM_NAME +Windows.Win32.System.Diagnostics.Debug.PENUMLOADED_MODULES_CALLBACKW64 +Windows.Win32.System.Diagnostics.Debug.PFUNCTION_TABLE_ACCESS_ROUTINE64 +Windows.Win32.System.Diagnostics.Debug.PGET_MODULE_BASE_ROUTINE64 +Windows.Win32.System.Diagnostics.Debug.PREAD_PROCESS_MEMORY_ROUTINE64 +Windows.Win32.System.Diagnostics.Debug.PTRANSLATE_ADDRESS_ROUTINE64 +Windows.Win32.System.Diagnostics.Debug.RtlCaptureContext +Windows.Win32.System.Diagnostics.Debug.RtlLookupFunctionEntry +Windows.Win32.System.Diagnostics.Debug.RtlVirtualUnwind +Windows.Win32.System.Diagnostics.Debug.STACKFRAME64 +Windows.Win32.System.Diagnostics.Debug.STACKFRAME_EX +Windows.Win32.System.Diagnostics.Debug.StackWalk64 +Windows.Win32.System.Diagnostics.Debug.StackWalkEx +Windows.Win32.System.Diagnostics.Debug.SymAddrIncludeInlineTrace +Windows.Win32.System.Diagnostics.Debug.SYMBOL_INFOW +Windows.Win32.System.Diagnostics.Debug.SymFromAddrW +Windows.Win32.System.Diagnostics.Debug.SymFromInlineContextW +Windows.Win32.System.Diagnostics.Debug.SymFunctionTableAccess64 +Windows.Win32.System.Diagnostics.Debug.SymGetLineFromAddrW64 +Windows.Win32.System.Diagnostics.Debug.SymGetLineFromInlineContextW +Windows.Win32.System.Diagnostics.Debug.SymGetModuleBase64 +Windows.Win32.System.Diagnostics.Debug.SymGetOptions +Windows.Win32.System.Diagnostics.Debug.SymGetSearchPathW +Windows.Win32.System.Diagnostics.Debug.SymInitializeW +Windows.Win32.System.Diagnostics.Debug.SYMOPT_DEFERRED_LOADS +Windows.Win32.System.Diagnostics.Debug.SymQueryInlineTrace +Windows.Win32.System.Diagnostics.Debug.SymSetOptions +Windows.Win32.System.Diagnostics.Debug.SymSetSearchPathW +Windows.Win32.System.Diagnostics.ToolHelp.CreateToolhelp32Snapshot +Windows.Win32.System.Diagnostics.ToolHelp.Module32FirstW +Windows.Win32.System.Diagnostics.ToolHelp.Module32NextW +Windows.Win32.System.Diagnostics.ToolHelp.MODULEENTRY32W +Windows.Win32.System.Diagnostics.ToolHelp.TH32CS_SNAPMODULE +Windows.Win32.System.LibraryLoader.GetProcAddress +Windows.Win32.System.LibraryLoader.LoadLibraryA +Windows.Win32.System.Memory.CreateFileMappingA +Windows.Win32.System.Memory.FILE_MAP_READ +Windows.Win32.System.Memory.MapViewOfFile +Windows.Win32.System.Memory.PAGE_READONLY +Windows.Win32.System.Memory.UnmapViewOfFile +Windows.Win32.System.SystemInformation.IMAGE_FILE_MACHINE_I386 +Windows.Win32.System.Threading.CreateMutexA +Windows.Win32.System.Threading.GetCurrentProcess +Windows.Win32.System.Threading.GetCurrentProcessId +Windows.Win32.System.Threading.GetCurrentThread +Windows.Win32.System.Threading.INFINITE +Windows.Win32.System.Threading.ReleaseMutex +Windows.Win32.System.Threading.WaitForSingleObjectEx \ No newline at end of file
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/examples/backtrace.rs b/chromium_crates_io/vendor/backtrace-v0_3/examples/backtrace.rs new file mode 100644 index 0000000..7ff6cd3 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/examples/backtrace.rs
@@ -0,0 +1,5 @@ +use backtrace::Backtrace; + +fn main() { + println!("{:?}", Backtrace::new()); +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/examples/raw.rs b/chromium_crates_io/vendor/backtrace-v0_3/examples/raw.rs new file mode 100644 index 0000000..95e17db --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/examples/raw.rs
@@ -0,0 +1,52 @@ +fn main() { + foo(); +} + +fn foo() { + bar() +} +fn bar() { + baz() +} +fn baz() { + print() +} + +#[cfg(target_pointer_width = "32")] +const HEX_WIDTH: usize = 10; +#[cfg(target_pointer_width = "64")] +const HEX_WIDTH: usize = 20; + +fn print() { + let mut cnt = 0; + backtrace::trace(|frame| { + let ip = frame.ip(); + print!("frame #{:<2} - {:#02$x}", cnt, ip as usize, HEX_WIDTH); + cnt += 1; + + let mut resolved = false; + backtrace::resolve(frame.ip(), |symbol| { + if !resolved { + resolved = true; + } else { + print!("{}", vec![" "; 7 + 2 + 3 + HEX_WIDTH].join("")); + } + + if let Some(name) = symbol.name() { + print!(" - {name}"); + } else { + print!(" - <unknown>"); + } + if let Some(file) = symbol.filename() { + if let Some(l) = symbol.lineno() { + print!("\n{:13}{:4$}@ {}:{}", "", "", file.display(), l, HEX_WIDTH); + } + } + println!(""); + }); + if !resolved { + println!(" - <no info>"); + } + true // keep going + }); +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/libunwind.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/libunwind.rs new file mode 100644 index 0000000..0564f2e --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/libunwind.rs
@@ -0,0 +1,301 @@ +//! Backtrace support using libunwind/gcc_s/etc APIs. +//! +//! This module contains the ability to unwind the stack using libunwind-style +//! APIs. Note that there's a whole bunch of implementations of the +//! libunwind-like API, and this is just trying to be compatible with most of +//! them all at once instead of being picky. +//! +//! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very +//! reliable at generating a backtrace. It's not entirely clear how it does it +//! (frame pointers? eh_frame info? both?) but it seems to work! +//! +//! Most of the complexity of this module is handling the various platform +//! differences across libunwind implementations. Otherwise this is a pretty +//! straightforward Rust binding to the libunwind APIs. +//! +//! This is the default unwinding API for all non-Windows platforms currently. + +use core::ffi::c_void; +use core::ptr::addr_of_mut; + +pub enum Frame { + Raw(*mut uw::_Unwind_Context), + Cloned { + ip: *mut c_void, + sp: *mut c_void, + symbol_address: *mut c_void, + }, +} + +// With a raw libunwind pointer it should only ever be access in a readonly +// threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone` +// we always switch to a version which doesn't retain interior pointers, so we +// should be `Send` as well. +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} + +impl Frame { + pub fn ip(&self) -> *mut c_void { + let ctx = match *self { + Frame::Raw(ctx) => ctx, + Frame::Cloned { ip, .. } => return ip, + }; + #[allow(unused_mut)] + let mut ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }; + + // To reduce TCB size in SGX enclaves, we do not want to implement + // symbol resolution functionality. Rather, we can print the offset of + // the address here, which could be later mapped to correct function. + #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] + { + let image_base = super::sgx_image_base::get_image_base(); + ip = usize::wrapping_sub(ip as usize, image_base as _) as _; + } + ip + } + + pub fn sp(&self) -> *mut c_void { + match *self { + Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void }, + Frame::Cloned { sp, .. } => sp, + } + } + + pub fn symbol_address(&self) -> *mut c_void { + if let Frame::Cloned { symbol_address, .. } = *self { + return symbol_address; + } + + // The macOS linker emits a "compact" unwind table that only includes an + // entry for a function if that function either has an LSDA or its + // encoding differs from that of the previous entry. Consequently, on + // macOS, `_Unwind_FindEnclosingFunction` is unreliable (it can return a + // pointer to some totally unrelated function). Instead, we just always + // return the ip. + // + // https://github.com/rust-lang/rust/issues/74771#issuecomment-664056788 + // + // Note the `skip_inner_frames.rs` test is skipped on macOS due to this + // clause, and if this is fixed that test in theory can be run on macOS! + if cfg!(target_vendor = "apple") { + self.ip() + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } + } + } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + None + } +} + +impl Clone for Frame { + fn clone(&self) -> Frame { + Frame::Cloned { + ip: self.ip(), + sp: self.sp(), + symbol_address: self.symbol_address(), + } + } +} + +struct Bomb { + enabled: bool, +} + +impl Drop for Bomb { + fn drop(&mut self) { + if self.enabled { + panic!("cannot panic during the backtrace function"); + } + } +} + +#[inline(always)] +pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) { + unsafe { + uw::_Unwind_Backtrace(trace_fn, addr_of_mut!(cb).cast()); + } + + extern "C" fn trace_fn( + ctx: *mut uw::_Unwind_Context, + arg: *mut c_void, + ) -> uw::_Unwind_Reason_Code { + let cb = unsafe { &mut *arg.cast::<&mut dyn FnMut(&super::Frame) -> bool>() }; + let cx = super::Frame { + inner: Frame::Raw(ctx), + }; + + let mut bomb = Bomb { enabled: true }; + let keep_going = cb(&cx); + bomb.enabled = false; + + if keep_going { + uw::_URC_NO_REASON + } else { + uw::_URC_FAILURE + } + } +} + +/// Unwind library interface used for backtraces +/// +/// Note that dead code is allowed as here are just bindings +/// iOS doesn't use all of them it but adding more +/// platform-specific configs pollutes the code too much +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(dead_code)] +mod uw { + pub use self::_Unwind_Reason_Code::*; + + use core::ffi::c_void; + + #[repr(C)] + pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EABI + } + + pub enum _Unwind_Context {} + + pub type _Unwind_Trace_Fn = + extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code; + + unsafe extern "C" { + pub fn _Unwind_Backtrace( + trace: _Unwind_Trace_Fn, + trace_argument: *mut c_void, + ) -> _Unwind_Reason_Code; + } + + cfg_if::cfg_if! { + // available since GCC 4.2.0, should be fine for our purpose + if #[cfg(all( + not(all(target_os = "android", target_arch = "arm")), + not(all(target_os = "freebsd", target_arch = "arm")), + not(all(target_os = "linux", target_arch = "arm")), + not(all(target_os = "horizon", target_arch = "arm")), + not(all(target_os = "rtems", target_arch = "arm")), + not(all(target_os = "vita", target_arch = "arm")), + not(all(target_os = "nuttx", target_arch = "arm")), + ))] { + unsafe extern "C" { + pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; + pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void; + + #[cfg(not(all(target_os = "linux", target_arch = "s390x")))] + // This function is a misnomer: rather than getting this frame's + // Canonical Frame Address (aka the caller frame's SP) it + // returns this frame's SP. + // + // https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35 + #[link_name = "_Unwind_GetCFA"] + pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t; + + } + + // s390x uses a biased CFA value, therefore we need to use + // _Unwind_GetGR to get the stack pointer register (%r15) + // instead of relying on _Unwind_GetCFA. + #[cfg(all(target_os = "linux", target_arch = "s390x"))] + pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + unsafe extern "C" { + pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t; + } + unsafe { _Unwind_GetGR(ctx, 15) } + } + } else { + use core::ptr::addr_of_mut; + + // On android and arm, the function `_Unwind_GetIP` and a bunch of + // others are macros, so we define functions containing the + // expansion of the macros. + // + // TODO: link to the header file that defines these macros, if you + // can find it. (I, fitzgen, cannot find the header file that some + // of these macro expansions were originally borrowed from.) + #[repr(C)] + enum _Unwind_VRS_Result { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2, + } + #[repr(C)] + enum _Unwind_VRS_RegClass { + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_FPA = 2, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4, + } + #[repr(C)] + enum _Unwind_VRS_DataRepresentation { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_FPAX = 2, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5, + } + + type _Unwind_Word = libc::c_uint; + unsafe extern "C" { + fn _Unwind_VRS_Get( + ctx: *mut _Unwind_Context, + klass: _Unwind_VRS_RegClass, + word: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut c_void, + ) -> _Unwind_VRS_Result; + } + + pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + let mut val: _Unwind_Word = 0; + let ptr = addr_of_mut!(val); + unsafe { + let _ = _Unwind_VRS_Get( + ctx, + _Unwind_VRS_RegClass::_UVRSC_CORE, + 15, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + ptr.cast::<c_void>(), + ); + } + (val & !1) as libc::uintptr_t + } + + // R13 is the stack pointer on arm. + const SP: _Unwind_Word = 13; + + pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + let mut val: _Unwind_Word = 0; + let ptr = addr_of_mut!(val); + unsafe { + let _ = _Unwind_VRS_Get( + ctx, + _Unwind_VRS_RegClass::_UVRSC_CORE, + SP, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + ptr.cast::<c_void>(), + ); + } + val as libc::uintptr_t + } + + // This function also doesn't exist on Android or ARM/Linux, so make it + // a no-op. + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void { + pc + } + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/miri.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/miri.rs new file mode 100644 index 0000000..2c36be4 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/miri.rs
@@ -0,0 +1,119 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::ffi::c_void; + +unsafe extern "Rust" { + fn miri_backtrace_size(flags: u64) -> usize; + fn miri_get_backtrace(flags: u64, buf: *mut *mut ()); + fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame; + fn miri_resolve_frame_names(ptr: *mut (), flags: u64, name_buf: *mut u8, filename_buf: *mut u8); +} + +#[repr(C)] +pub struct MiriFrame { + pub name_len: usize, + pub filename_len: usize, + pub lineno: u32, + pub colno: u32, + pub fn_ptr: *mut c_void, +} + +#[derive(Clone, Debug)] +pub struct FullMiriFrame { + pub name: Box<[u8]>, + pub filename: Box<[u8]>, + pub lineno: u32, + pub colno: u32, + pub fn_ptr: *mut c_void, +} + +#[derive(Debug, Clone)] +pub struct Frame { + pub addr: *mut c_void, + pub inner: FullMiriFrame, +} + +// SAFETY: Miri guarantees that the returned pointer +// can be used from any thread. +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} + +impl Frame { + pub fn ip(&self) -> *mut c_void { + self.addr + } + + pub fn sp(&self) -> *mut c_void { + core::ptr::null_mut() + } + + pub fn symbol_address(&self) -> *mut c_void { + self.inner.fn_ptr + } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + None + } +} + +// SAFETY: This function is safe to call. It is only marked as `unsafe` to +// avoid having to allow `unused_unsafe` since other implementations are +// unsafe. +pub unsafe fn trace<F: FnMut(&super::Frame) -> bool>(cb: F) { + // SAFETY: Miri guarantees that the backtrace API functions + // can be called from any thread. + unsafe { trace_unsynchronized(cb) }; +} + +pub fn resolve_addr(ptr: *mut c_void) -> Frame { + // SAFETY: Miri will stop execution with an error if this pointer + // is invalid. + let frame = unsafe { miri_resolve_frame(ptr.cast::<()>(), 1) }; + + let mut name = Vec::with_capacity(frame.name_len); + let mut filename = Vec::with_capacity(frame.filename_len); + + // SAFETY: name and filename have been allocated with the amount + // of memory miri has asked for, and miri guarantees it will initialize it + unsafe { + miri_resolve_frame_names( + ptr.cast::<()>(), + 0, + name.as_mut_ptr(), + filename.as_mut_ptr(), + ); + + name.set_len(frame.name_len); + filename.set_len(frame.filename_len); + } + + Frame { + addr: ptr, + inner: FullMiriFrame { + name: name.into(), + filename: filename.into(), + lineno: frame.lineno, + colno: frame.colno, + fn_ptr: frame.fn_ptr, + }, + } +} + +unsafe fn trace_unsynchronized<F: FnMut(&super::Frame) -> bool>(mut cb: F) { + let len = unsafe { miri_backtrace_size(0) }; + + let mut frames = Vec::with_capacity(len); + + unsafe { + miri_get_backtrace(1, frames.as_mut_ptr()); + + frames.set_len(len); + } + + for ptr in frames.iter() { + let frame = resolve_addr((*ptr).cast::<c_void>()); + if !cb(&super::Frame { inner: frame }) { + return; + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/mod.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/mod.rs new file mode 100644 index 0000000..2a36214 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/mod.rs
@@ -0,0 +1,206 @@ +use core::ffi::c_void; +use core::fmt; + +/// Inspects the current call-stack, passing all active frames into the closure +/// provided to calculate a stack trace. +/// +/// This function is the workhorse of this library in calculating the stack +/// traces for a program. The given closure `cb` is yielded instances of a +/// `Frame` which represent information about that call frame on the stack. The +/// closure is yielded frames in a top-down fashion (most recently called +/// functions first). +/// +/// The closure's return value is an indication of whether the backtrace should +/// continue. A return value of `false` will terminate the backtrace and return +/// immediately. +/// +/// Once a `Frame` is acquired you will likely want to call `backtrace::resolve` +/// to convert the `ip` (instruction pointer) or symbol address to a `Symbol` +/// through which the name and/or filename/line number can be learned. +/// +/// Note that this is a relatively low-level function and if you'd like to, for +/// example, capture a backtrace to be inspected later, then the `Backtrace` +/// type may be more appropriate. +/// +/// # Required features +/// +/// This function requires the `std` feature of the `backtrace` crate to be +/// enabled, and the `std` feature is enabled by default. +/// +/// # Panics +/// +/// This function strives to never panic, but if the `cb` provided panics then +/// some platforms will force a double panic to abort the process. Some +/// platforms use a C library which internally uses callbacks which cannot be +/// unwound through, so panicking from `cb` may trigger a process abort. +/// +/// # Example +/// +/// ``` +/// extern crate backtrace; +/// +/// fn main() { +/// backtrace::trace(|frame| { +/// // ... +/// +/// true // continue the backtrace +/// }); +/// } +/// ``` +#[cfg(feature = "std")] +pub fn trace<F: FnMut(&Frame) -> bool>(cb: F) { + let _guard = crate::lock::lock(); + unsafe { trace_unsynchronized(cb) } +} + +/// Same as `trace`, only unsafe as it's unsynchronized. +/// +/// This function does not have synchronization guarantees but is available +/// when the `std` feature of this crate isn't compiled in. See the `trace` +/// function for more documentation and examples. +/// +/// # Panics +/// +/// See information on `trace` for caveats on `cb` panicking. +pub unsafe fn trace_unsynchronized<F: FnMut(&Frame) -> bool>(mut cb: F) { + unsafe { trace_imp(&mut cb) } +} + +/// A trait representing one frame of a backtrace, yielded to the `trace` +/// function of this crate. +/// +/// The tracing function's closure will be yielded frames, and the frame is +/// virtually dispatched as the underlying implementation is not always known +/// until runtime. +#[derive(Clone)] +pub struct Frame { + pub(crate) inner: FrameImp, +} + +impl Frame { + /// Returns the current instruction pointer of this frame. + /// + /// This is normally the next instruction to execute in the frame, but not + /// all implementations list this with 100% accuracy (but it's generally + /// pretty close). + /// + /// It is recommended to pass this value to `backtrace::resolve` to turn it + /// into a symbol name. + pub fn ip(&self) -> *mut c_void { + self.inner.ip() + } + + /// Returns the current stack pointer of this frame. + /// + /// In the case that a backend cannot recover the stack pointer for this + /// frame, a null pointer is returned. + pub fn sp(&self) -> *mut c_void { + self.inner.sp() + } + + /// Returns the starting symbol address of the frame of this function. + /// + /// This will attempt to rewind the instruction pointer returned by `ip` to + /// the start of the function, returning that value. In some cases, however, + /// backends will just return `ip` from this function. + /// + /// The returned value can sometimes be used if `backtrace::resolve` failed + /// on the `ip` given above. + pub fn symbol_address(&self) -> *mut c_void { + self.inner.symbol_address() + } + + /// Returns the base address of the module to which the frame belongs. + pub fn module_base_address(&self) -> Option<*mut c_void> { + self.inner.module_base_address() + } +} + +impl fmt::Debug for Frame { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Frame") + .field("ip", &self.ip()) + .field("symbol_address", &self.symbol_address()) + .finish() + } +} + +#[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] +mod sgx_image_base { + + #[cfg(not(feature = "std"))] + pub(crate) mod imp { + use core::ffi::c_void; + use core::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + + static IMAGE_BASE: AtomicUsize = AtomicUsize::new(0); + + /// Set the image base address. This is only available for Fortanix SGX + /// target when the `std` feature is not enabled. This can be used in the + /// standard library to set the correct base address. + #[doc(hidden)] + pub fn set_image_base(base_addr: *mut c_void) { + IMAGE_BASE.store(base_addr as _, SeqCst); + } + + pub(crate) fn get_image_base() -> *mut c_void { + IMAGE_BASE.load(SeqCst) as _ + } + } + + #[cfg(feature = "std")] + mod imp { + use core::ffi::c_void; + + pub(crate) fn get_image_base() -> *mut c_void { + std::os::fortanix_sgx::mem::image_base() as _ + } + } + + pub(crate) use imp::get_image_base; +} + +#[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))] +pub use sgx_image_base::imp::set_image_base; + +cfg_if::cfg_if! { + // This needs to come first, to ensure that + // Miri takes priority over the host platform + if #[cfg(miri)] { + pub(crate) mod miri; + use self::miri::trace as trace_imp; + pub(crate) use self::miri::Frame as FrameImp; + } else if #[cfg( + any( + all( + unix, + not(target_os = "emscripten"), + not(all(target_os = "ios", target_arch = "arm")), + ), + all( + target_env = "sgx", + target_vendor = "fortanix", + ), + ) + )] { + mod libunwind; + use self::libunwind::trace as trace_imp; + pub(crate) use self::libunwind::Frame as FrameImp; + } else if #[cfg(all(windows, not(target_vendor = "uwp")))] { + cfg_if::cfg_if! { + if #[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "arm64ec"))] { + mod win64; + use self::win64::trace as trace_imp; + pub(crate) use self::win64::Frame as FrameImp; + } else if #[cfg(any(target_arch = "x86", target_arch = "arm"))] { + mod win32; + use self::win32::trace as trace_imp; + pub(crate) use self::win32::Frame as FrameImp; + } + } + } else { + mod noop; + use self::noop::trace as trace_imp; + pub(crate) use self::noop::Frame as FrameImp; + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/noop.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/noop.rs new file mode 100644 index 0000000..98dbcfa --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/noop.rs
@@ -0,0 +1,32 @@ +//! Empty implementation of unwinding used when no other implementation is +//! appropriate. + +use core::ffi::c_void; +use core::ptr::null_mut; + +// SAFETY: This function is safe to call. It is only marked as `unsafe` to +// avoid having to allow `unused_unsafe` since other implementations are +// unsafe. +#[inline(always)] +pub unsafe fn trace(_cb: &mut dyn FnMut(&super::Frame) -> bool) {} + +#[derive(Clone)] +pub struct Frame; + +impl Frame { + pub fn ip(&self) -> *mut c_void { + null_mut() + } + + pub fn sp(&self) -> *mut c_void { + null_mut() + } + + pub fn symbol_address(&self) -> *mut c_void { + null_mut() + } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + None + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win32.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win32.rs new file mode 100644 index 0000000..9c45930 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win32.rs
@@ -0,0 +1,214 @@ +//! Backtrace strategy for Windows platforms. +//! +//! This module contains the ability to generate a backtrace on Windows using one +//! of two possible methods. The `StackWalkEx` function is primarily used if +//! possible, but not all systems have that. Failing that the `StackWalk64` +//! function is used instead. Note that `StackWalkEx` is favored because it +//! handles debuginfo internally and returns inline frame information. +//! +//! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs` +//! for more information about that. + +use super::super::{dbghelp, windows_sys::*}; +use core::ffi::c_void; +use core::mem; + +#[derive(Clone, Copy)] +pub enum StackFrame { + New(STACKFRAME_EX), + Old(STACKFRAME64), +} + +#[derive(Clone, Copy)] +pub struct Frame { + pub(crate) stack_frame: StackFrame, + base_address: *mut c_void, +} + +// we're just sending around raw pointers and reading them, never interpreting +// them so this should be safe to both send and share across threads. +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} + +impl Frame { + pub fn ip(&self) -> *mut c_void { + self.addr_pc().Offset as *mut _ + } + + pub fn sp(&self) -> *mut c_void { + self.addr_stack().Offset as *mut _ + } + + pub fn symbol_address(&self) -> *mut c_void { + self.ip() + } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + Some(self.base_address) + } + + #[cfg(not(target_env = "gnu"))] + pub fn inline_context(&self) -> Option<u32> { + match self.stack_frame { + StackFrame::New(ref new) => Some(new.InlineFrameContext), + StackFrame::Old(_) => None, + } + } + + fn addr_pc(&self) -> &ADDRESS64 { + match self.stack_frame { + StackFrame::New(ref new) => &new.AddrPC, + StackFrame::Old(ref old) => &old.AddrPC, + } + } + + fn addr_pc_mut(&mut self) -> &mut ADDRESS64 { + match self.stack_frame { + StackFrame::New(ref mut new) => &mut new.AddrPC, + StackFrame::Old(ref mut old) => &mut old.AddrPC, + } + } + + fn addr_frame_mut(&mut self) -> &mut ADDRESS64 { + match self.stack_frame { + StackFrame::New(ref mut new) => &mut new.AddrFrame, + StackFrame::Old(ref mut old) => &mut old.AddrFrame, + } + } + + fn addr_stack(&self) -> &ADDRESS64 { + match self.stack_frame { + StackFrame::New(ref new) => &new.AddrStack, + StackFrame::Old(ref old) => &old.AddrStack, + } + } + + fn addr_stack_mut(&mut self) -> &mut ADDRESS64 { + match self.stack_frame { + StackFrame::New(ref mut new) => &mut new.AddrStack, + StackFrame::Old(ref mut old) => &mut old.AddrStack, + } + } +} + +#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in windows metadata right now +struct MyContext(CONTEXT); + +#[inline(always)] +pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) { + // Allocate necessary structures for doing the stack walk + let process = GetCurrentProcess(); + let thread = GetCurrentThread(); + + let mut context = mem::zeroed::<MyContext>(); + RtlCaptureContext(&mut context.0); + + // Ensure this process's symbols are initialized + let dbghelp = match dbghelp::init() { + Ok(dbghelp) => dbghelp, + Err(()) => return, // oh well... + }; + + let function_table_access = dbghelp.SymFunctionTableAccess64(); + let get_module_base = dbghelp.SymGetModuleBase64(); + + let process_handle = GetCurrentProcess(); + + // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64` + // since it's in theory supported on more systems. + match (*dbghelp.dbghelp()).StackWalkEx() { + #[allow(non_snake_case)] + Some(StackWalkEx) => { + let mut inner: STACKFRAME_EX = mem::zeroed(); + inner.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as u32; + let mut frame = super::Frame { + inner: Frame { + stack_frame: StackFrame::New(inner), + base_address: 0 as _, + }, + }; + let image = init_frame(&mut frame.inner, &context.0); + let frame_ptr = match &mut frame.inner.stack_frame { + StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX, + _ => unreachable!(), + }; + + while StackWalkEx( + image as u32, + process, + thread, + frame_ptr, + &mut context.0 as *mut CONTEXT as *mut _, + None, + Some(function_table_access), + Some(get_module_base), + None, + 0, + ) == TRUE + { + frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _; + + if !cb(&frame) { + break; + } + } + } + None => { + let mut frame = super::Frame { + inner: Frame { + stack_frame: StackFrame::Old(mem::zeroed()), + base_address: 0 as _, + }, + }; + let image = init_frame(&mut frame.inner, &context.0); + let frame_ptr = match &mut frame.inner.stack_frame { + StackFrame::Old(ptr) => ptr as *mut STACKFRAME64, + _ => unreachable!(), + }; + + while dbghelp.StackWalk64()( + image as u32, + process, + thread, + frame_ptr, + &mut context.0 as *mut CONTEXT as *mut _, + None, + Some(function_table_access), + Some(get_module_base), + None, + ) == TRUE + { + frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _; + + if !cb(&frame) { + break; + } + } + } + } +} + +#[cfg(target_arch = "x86")] +fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> u16 { + frame.addr_pc_mut().Offset = ctx.Eip as u64; + frame.addr_pc_mut().Mode = AddrModeFlat; + frame.addr_stack_mut().Offset = ctx.Esp as u64; + frame.addr_stack_mut().Mode = AddrModeFlat; + frame.addr_frame_mut().Offset = ctx.Ebp as u64; + frame.addr_frame_mut().Mode = AddrModeFlat; + + IMAGE_FILE_MACHINE_I386 +} + +#[cfg(target_arch = "arm")] +fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> u16 { + frame.addr_pc_mut().Offset = ctx.Pc as u64; + frame.addr_pc_mut().Mode = AddrModeFlat; + frame.addr_stack_mut().Offset = ctx.Sp as u64; + frame.addr_stack_mut().Mode = AddrModeFlat; + unsafe { + frame.addr_frame_mut().Offset = ctx.R11 as u64; + } + frame.addr_frame_mut().Mode = AddrModeFlat; + IMAGE_FILE_MACHINE_ARMNT +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win64.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win64.rs new file mode 100644 index 0000000..81f6358 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/backtrace/win64.rs
@@ -0,0 +1,151 @@ +//! Backtrace strategy for Windows `x86_64` and `aarch64` platforms. +//! +//! This module contains the ability to capture a backtrace on Windows using +//! `RtlVirtualUnwind` to walk the stack one frame at a time. This function is much faster than using +//! `dbghelp!StackWalk*` because it does not load debug info to report inlined frames. +//! We still report inlined frames during symbolization by consulting the appropriate +//! `dbghelp` functions. + +use super::super::windows_sys::*; +use core::ffi::c_void; + +#[derive(Clone, Copy)] +pub struct Frame { + base_address: *mut c_void, + ip: *mut c_void, + sp: *mut c_void, + #[cfg(not(target_env = "gnu"))] + inline_context: Option<u32>, +} + +// we're just sending around raw pointers and reading them, never interpreting +// them so this should be safe to both send and share across threads. +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} + +impl Frame { + pub fn ip(&self) -> *mut c_void { + self.ip + } + + pub fn sp(&self) -> *mut c_void { + self.sp + } + + pub fn symbol_address(&self) -> *mut c_void { + self.ip + } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + Some(self.base_address) + } + + #[cfg(not(target_env = "gnu"))] + pub fn inline_context(&self) -> Option<u32> { + self.inline_context + } +} + +#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in windows metadata right now +struct MyContext(CONTEXT); + +#[cfg(any(target_arch = "x86_64", target_arch = "arm64ec"))] +impl MyContext { + #[inline(always)] + fn ip(&self) -> u64 { + self.0.Rip + } + + #[inline(always)] + fn sp(&self) -> u64 { + self.0.Rsp + } +} + +#[cfg(target_arch = "aarch64")] +impl MyContext { + #[inline(always)] + fn ip(&self) -> usize { + self.0.Pc as usize + } + + #[inline(always)] + fn sp(&self) -> usize { + self.0.Sp as usize + } +} + +#[inline(always)] +pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) { + use core::ptr; + + // Capture the initial context to start walking from. + // FIXME: shouldn't this have a Default impl? + let mut context = unsafe { core::mem::zeroed::<MyContext>() }; + unsafe { RtlCaptureContext(&mut context.0) }; + + loop { + let ip = context.ip(); + + // The base address of the module containing the function will be stored here + // when RtlLookupFunctionEntry returns successfully. + let mut base = 0; + // We use the `RtlLookupFunctionEntry` function in kernel32 which allows + // us to backtrace through JIT frames. + // Note that `RtlLookupFunctionEntry` only works for in-process backtraces, + // but that's all we support anyway, so it all lines up well. + let fn_entry = unsafe { RtlLookupFunctionEntry(ip, &mut base, ptr::null_mut()) }; + if fn_entry.is_null() { + // No function entry could be found - this may indicate a corrupt + // stack or that a binary was unloaded (amongst other issues). Stop + // walking and don't call the callback as we can't be confident in + // this frame or the rest of the stack. + break; + } + + let frame = super::Frame { + inner: Frame { + base_address: base as *mut c_void, + ip: ip as *mut c_void, + sp: context.sp() as *mut c_void, + #[cfg(not(target_env = "gnu"))] + inline_context: None, + }, + }; + + // We've loaded all the info about the current frame, so now call the + // callback. + if !cb(&frame) { + // Callback told us to stop, so we're done. + break; + } + + // Unwind to the next frame. + let previous_ip = ip; + let previous_sp = context.sp(); + let mut handler_data = 0usize; + let mut establisher_frame = 0; + unsafe { + RtlVirtualUnwind( + 0, + base, + ip, + fn_entry, + &mut context.0, + ptr::addr_of_mut!(handler_data).cast::<*mut c_void>(), + &mut establisher_frame, + ptr::null_mut(), + ); + } + + // RtlVirtualUnwind indicates the end of the stack in two different ways: + // * On x64, it sets the instruction pointer to 0. + // * On ARM64, it leaves the context unchanged (easiest way to check is + // to see if the instruction and stack pointers are the same). + // If we detect either of these, then unwinding is completed. + let ip = context.ip(); + if ip == 0 || (ip == previous_ip && context.sp() == previous_sp) { + break; + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/capture.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/capture.rs new file mode 100644 index 0000000..e7de879 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/capture.rs
@@ -0,0 +1,621 @@ +#![allow(clippy::from_over_into)] + +#[cfg(feature = "serde")] +use crate::resolve; +use crate::PrintFmt; +use crate::{resolve_frame, trace, BacktraceFmt, Symbol, SymbolName}; +use core::ffi::c_void; +use std::fmt; +use std::path::{Path, PathBuf}; +use std::prelude::v1::*; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Representation of an owned and self-contained backtrace. +/// +/// This structure can be used to capture a backtrace at various points in a +/// program and later used to inspect what the backtrace was at that time. +/// +/// `Backtrace` supports pretty-printing of backtraces through its `Debug` +/// implementation. +/// +/// # Required features +/// +/// This function requires the `std` feature of the `backtrace` crate to be +/// enabled, and the `std` feature is enabled by default. +#[derive(Clone)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +pub struct Backtrace { + // Frames here are listed from top-to-bottom of the stack + frames: Box<[BacktraceFrame]>, +} + +#[derive(Clone, Copy)] +struct TracePtr(*mut c_void); +/// SAFETY: These pointers are always valid within a process and are not used for mutation. +unsafe impl Send for TracePtr {} +/// SAFETY: These pointers are always valid within a process and are not used for mutation. +unsafe impl Sync for TracePtr {} + +impl TracePtr { + fn into_void(self) -> *mut c_void { + self.0 + } + #[cfg(feature = "serde")] + fn from_addr(addr: usize) -> Self { + TracePtr(addr as *mut c_void) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for TracePtr { + #[inline] + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + struct PrimitiveVisitor; + + impl<'de> serde::de::Visitor<'de> for PrimitiveVisitor { + type Value = TracePtr; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("usize") + } + + #[inline] + fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + Ok(TracePtr(v as usize as *mut c_void)) + } + + #[inline] + fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + Ok(TracePtr(v as usize as *mut c_void)) + } + + #[inline] + fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + if usize::BITS >= 32 { + Ok(TracePtr(v as usize as *mut c_void)) + } else { + Err(E::invalid_type( + serde::de::Unexpected::Unsigned(v as _), + &self, + )) + } + } + + #[inline] + fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + if usize::BITS >= 64 { + Ok(TracePtr(v as usize as *mut c_void)) + } else { + Err(E::invalid_type( + serde::de::Unexpected::Unsigned(v as _), + &self, + )) + } + } + } + + deserializer.deserialize_u64(PrimitiveVisitor) + } +} + +#[cfg(feature = "serde")] +impl Serialize for TracePtr { + #[inline] + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::ser::Serializer, + { + serializer.serialize_u64(self.0 as usize as u64) + } +} + +fn _assert_send_sync() { + fn _assert<T: Send + Sync>() {} + _assert::<Backtrace>(); +} + +/// Captured version of a frame in a backtrace. +/// +/// This type is returned as a list from `Backtrace::frames` and represents one +/// stack frame in a captured backtrace. +/// +/// # Required features +/// +/// This function requires the `std` feature of the `backtrace` crate to be +/// enabled, and the `std` feature is enabled by default. +#[derive(Clone)] +pub struct BacktraceFrame { + frame: Frame, + symbols: Option<Box<[BacktraceSymbol]>>, +} + +#[derive(Clone)] +enum Frame { + Raw(crate::Frame), + #[cfg(feature = "serde")] + Deserialized { + ip: TracePtr, + symbol_address: TracePtr, + module_base_address: Option<TracePtr>, + }, +} + +impl Frame { + fn ip(&self) -> *mut c_void { + match *self { + Frame::Raw(ref f) => f.ip(), + #[cfg(feature = "serde")] + Frame::Deserialized { ip, .. } => ip.into_void(), + } + } + + fn symbol_address(&self) -> *mut c_void { + match *self { + Frame::Raw(ref f) => f.symbol_address(), + #[cfg(feature = "serde")] + Frame::Deserialized { symbol_address, .. } => symbol_address.into_void(), + } + } + + fn module_base_address(&self) -> Option<*mut c_void> { + match *self { + Frame::Raw(ref f) => f.module_base_address(), + #[cfg(feature = "serde")] + Frame::Deserialized { + module_base_address, + .. + } => module_base_address.map(|addr| addr.into_void()), + } + } + + /// Resolve all addresses in the frame to their symbolic names. + fn resolve_symbols(&self) -> Box<[BacktraceSymbol]> { + let mut symbols = Vec::new(); + let sym = |symbol: &Symbol| { + symbols.push(BacktraceSymbol { + name: symbol.name().map(|m| m.as_bytes().into()), + addr: symbol.addr().map(TracePtr), + filename: symbol.filename().map(|m| m.to_owned()), + lineno: symbol.lineno(), + colno: symbol.colno(), + }); + }; + match *self { + Frame::Raw(ref f) => resolve_frame(f, sym), + #[cfg(feature = "serde")] + Frame::Deserialized { ip, .. } => { + resolve(ip.into_void(), sym); + } + } + symbols.into_boxed_slice() + } +} + +/// Captured version of a symbol in a backtrace. +/// +/// This type is returned as a list from `BacktraceFrame::symbols` and +/// represents the metadata for a symbol in a backtrace. +/// +/// # Required features +/// +/// This function requires the `std` feature of the `backtrace` crate to be +/// enabled, and the `std` feature is enabled by default. +#[derive(Clone)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +pub struct BacktraceSymbol { + name: Option<Box<[u8]>>, + addr: Option<TracePtr>, + filename: Option<PathBuf>, + lineno: Option<u32>, + colno: Option<u32>, +} + +impl Backtrace { + /// Captures a backtrace at the callsite of this function, returning an + /// owned representation. + /// + /// This function is useful for representing a backtrace as an object in + /// Rust. This returned value can be sent across threads and printed + /// elsewhere, and the purpose of this value is to be entirely self + /// contained. + /// + /// Note that on some platforms acquiring a full backtrace and resolving it + /// can be extremely expensive. If the cost is too much for your application + /// it's recommended to instead use `Backtrace::new_unresolved()` which + /// avoids the symbol resolution step (which typically takes the longest) + /// and allows deferring that to a later date. + /// + /// # Examples + /// + /// ``` + /// use backtrace::Backtrace; + /// + /// let current_backtrace = Backtrace::new(); + /// ``` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + #[inline(never)] // want to make sure there's a frame here to remove + pub fn new() -> Backtrace { + let mut bt = Self::create(Self::new as usize); + bt.resolve(); + bt + } + + /// Similar to `new` except that this does not resolve any symbols, this + /// simply captures the backtrace as a list of addresses. + /// + /// At a later time the `resolve` function can be called to resolve this + /// backtrace's symbols into readable names. This function exists because + /// the resolution process can sometimes take a significant amount of time + /// whereas any one backtrace may only be rarely printed. + /// + /// # Examples + /// + /// ``` + /// use backtrace::Backtrace; + /// + /// let mut current_backtrace = Backtrace::new_unresolved(); + /// println!("{current_backtrace:?}"); // no symbol names + /// current_backtrace.resolve(); + /// println!("{current_backtrace:?}"); // symbol names now present + /// ``` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + #[inline(never)] // want to make sure there's a frame here to remove + pub fn new_unresolved() -> Backtrace { + Self::create(Self::new_unresolved as usize) + } + + fn create(ip: usize) -> Backtrace { + let mut frames = Vec::new(); + trace(|frame| { + frames.push(BacktraceFrame { + frame: Frame::Raw(frame.clone()), + symbols: None, + }); + + // clear inner frames, and start with call site. + if frame.symbol_address() as usize == ip { + frames.clear(); + } + + true + }); + frames.shrink_to_fit(); + + Backtrace { + frames: frames.into_boxed_slice(), + } + } + + /// Returns the frames from when this backtrace was captured. + /// + /// The first entry of this slice is likely the function `Backtrace::new`, + /// and the last frame is likely something about how this thread or the main + /// function started. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn frames(&self) -> &[BacktraceFrame] { + self.frames.as_ref() + } + + /// If this backtrace was created from `new_unresolved` then this function + /// will resolve all addresses in the backtrace to their symbolic names. + /// + /// If this backtrace has been previously resolved or was created through + /// `new`, this function does nothing. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn resolve(&mut self) { + self.frames.iter_mut().for_each(BacktraceFrame::resolve); + } +} + +impl From<Vec<BacktraceFrame>> for Backtrace { + fn from(frames: Vec<BacktraceFrame>) -> Self { + Backtrace { + frames: frames.into_boxed_slice(), + } + } +} + +impl From<crate::Frame> for BacktraceFrame { + fn from(frame: crate::Frame) -> Self { + BacktraceFrame { + frame: Frame::Raw(frame), + symbols: None, + } + } +} + +// we don't want to implement `impl From<Backtrace> for Vec<BacktraceFrame>` on purpose, +// because "... additional directions for Vec<T> can weaken type inference ..." +// more information on https://github.com/rust-lang/backtrace-rs/pull/526 +impl Into<Vec<BacktraceFrame>> for Backtrace { + fn into(self) -> Vec<BacktraceFrame> { + self.frames.into_vec() + } +} + +impl BacktraceFrame { + /// Same as `Frame::ip` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn ip(&self) -> *mut c_void { + self.frame.ip() + } + + /// Same as `Frame::symbol_address` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn symbol_address(&self) -> *mut c_void { + self.frame.symbol_address() + } + + /// Same as `Frame::module_base_address` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn module_base_address(&self) -> Option<*mut c_void> { + self.frame.module_base_address() + } + + /// Returns the list of symbols that this frame corresponds to. + /// + /// Normally there is only one symbol per frame, but sometimes if a number + /// of functions are inlined into one frame then multiple symbols will be + /// returned. The first symbol listed is the "innermost function", whereas + /// the last symbol is the outermost (last caller). + /// + /// Note that if this frame came from an unresolved backtrace then this will + /// return an empty list. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn symbols(&self) -> &[BacktraceSymbol] { + self.symbols.as_ref().map(|s| &s[..]).unwrap_or(&[]) + } + + /// Resolve all addresses in this frame to their symbolic names. + /// + /// If this frame has been previously resolved, this function does nothing. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn resolve(&mut self) { + if self.symbols.is_none() { + self.symbols = Some(self.frame.resolve_symbols()); + } + } +} + +impl BacktraceSymbol { + /// Same as `Symbol::name` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn name(&self) -> Option<SymbolName<'_>> { + self.name.as_ref().map(|s| SymbolName::new(s)) + } + + /// Same as `Symbol::addr` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn addr(&self) -> Option<*mut c_void> { + self.addr.map(|s| s.into_void()) + } + + /// Same as `Symbol::filename` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn filename(&self) -> Option<&Path> { + self.filename.as_deref() + } + + /// Same as `Symbol::lineno` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn lineno(&self) -> Option<u32> { + self.lineno + } + + /// Same as `Symbol::colno` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn colno(&self) -> Option<u32> { + self.colno + } +} + +impl fmt::Debug for Backtrace { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let style = if fmt.alternate() { + PrintFmt::Full + } else { + PrintFmt::Short + }; + + // When printing paths we try to strip the cwd if it exists, otherwise + // we just print the path as-is. Note that we also only do this for the + // short format, because if it's full we presumably want to print + // everything. + let cwd = std::env::current_dir(); + let mut print_path = + move |fmt: &mut fmt::Formatter<'_>, path: crate::BytesOrWideString<'_>| { + let path = path.into_path_buf(); + if style != PrintFmt::Full { + if let Ok(cwd) = &cwd { + if let Ok(suffix) = path.strip_prefix(cwd) { + return fmt::Display::fmt(&suffix.display(), fmt); + } + } + } + fmt::Display::fmt(&path.display(), fmt) + }; + + let mut f = BacktraceFmt::new(fmt, style, &mut print_path); + f.add_context()?; + for frame in &self.frames { + f.frame().backtrace_frame(frame)?; + } + f.finish()?; + Ok(()) + } +} + +impl Default for Backtrace { + fn default() -> Backtrace { + Backtrace::new() + } +} + +impl fmt::Debug for BacktraceFrame { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BacktraceFrame") + .field("ip", &self.ip()) + .field("symbol_address", &self.symbol_address()) + .finish() + } +} + +impl fmt::Debug for BacktraceSymbol { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BacktraceSymbol") + .field("name", &self.name()) + .field("addr", &self.addr()) + .field("filename", &self.filename()) + .field("lineno", &self.lineno()) + .field("colno", &self.colno()) + .finish() + } +} + +#[cfg(feature = "serde")] +mod serde_impls { + use super::*; + use serde::de::Deserializer; + use serde::ser::Serializer; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize)] + struct SerializedFrame { + ip: usize, + symbol_address: usize, + module_base_address: Option<usize>, + symbols: Option<Box<[BacktraceSymbol]>>, + } + + impl Serialize for BacktraceFrame { + fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let BacktraceFrame { frame, symbols } = self; + SerializedFrame { + ip: frame.ip() as usize, + symbol_address: frame.symbol_address() as usize, + module_base_address: frame.module_base_address().map(|sym_a| sym_a as usize), + symbols: symbols.clone(), + } + .serialize(s) + } + } + + impl<'a> Deserialize<'a> for BacktraceFrame { + fn deserialize<D>(d: D) -> Result<Self, D::Error> + where + D: Deserializer<'a>, + { + let frame: SerializedFrame = SerializedFrame::deserialize(d)?; + Ok(BacktraceFrame { + frame: Frame::Deserialized { + ip: TracePtr::from_addr(frame.ip), + symbol_address: TracePtr::from_addr(frame.symbol_address), + module_base_address: frame.module_base_address.map(TracePtr::from_addr), + }, + symbols: frame.symbols, + }) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_frame_conversion() { + let mut frames = vec![]; + crate::trace(|frame| { + let converted = BacktraceFrame::from(frame.clone()); + frames.push(converted); + true + }); + + let mut manual = Backtrace::from(frames); + manual.resolve(); + let frames = manual.frames(); + + for frame in frames { + println!("{:?}", frame.ip()); + println!("{:?}", frame.symbol_address()); + println!("{:?}", frame.module_base_address()); + println!("{:?}", frame.symbols()); + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/dbghelp.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/dbghelp.rs new file mode 100644 index 0000000..df31fef --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/dbghelp.rs
@@ -0,0 +1,487 @@ +//! A module to assist in managing dbghelp bindings on Windows +//! +//! Backtraces on Windows (at least for MSVC) are largely powered through +//! `dbghelp.dll` and the various functions that it contains. These functions +//! are currently loaded *dynamically* rather than linking to `dbghelp.dll` +//! statically. This is currently done by the standard library (and is in theory +//! required there), but is an effort to help reduce the static dll dependencies +//! of a library since backtraces are typically pretty optional. That being +//! said, `dbghelp.dll` almost always successfully loads on Windows. +//! +//! Note though that since we're loading all this support dynamically we can't +//! actually use the raw definitions in `windows_sys`, but rather we need to define +//! the function pointer types ourselves and use that. We don't really want to +//! be in the business of duplicating auto-generated bindings, so we assert that all bindings match +//! those in `windows_sys.rs`. +//! +//! Finally, you'll note here that the dll for `dbghelp.dll` is never unloaded, +//! and that's currently intentional. The thinking is that we can globally cache +//! it and use it between calls to the API, avoiding expensive loads/unloads. If +//! this is a problem for leak detectors or something like that we can cross the +//! bridge when we get there. + +#![allow(non_snake_case)] + +use alloc::vec::Vec; + +use super::windows_sys::*; +use core::ffi::c_void; +use core::mem; +use core::ptr; +use core::slice; + +// This macro is used to define a `Dbghelp` structure which internally contains +// all the function pointers that we might load. +macro_rules! dbghelp { + (extern "system" { + $(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)* + }) => ( + pub struct Dbghelp { + /// The loaded DLL for `dbghelp.dll` + dll: HINSTANCE, + + // Each function pointer for each function we might use + $($name: usize,)* + } + + static mut DBGHELP: Dbghelp = Dbghelp { + // Initially we haven't loaded the DLL + dll: ptr::null_mut(), + // Initially all functions are set to zero to say they need to be + // dynamically loaded. + $($name: 0,)* + }; + + // Convenience typedef for each function type. + $(pub type $name = unsafe extern "system" fn($($argty),*) -> $ret;)* + + impl Dbghelp { + /// Attempts to open `dbghelp.dll`. Returns success if it works or + /// error if `LoadLibraryW` fails. + fn ensure_open(&mut self) -> Result<(), ()> { + if !self.dll.is_null() { + return Ok(()) + } + let lib = b"dbghelp.dll\0"; + unsafe { + self.dll = LoadLibraryA(lib.as_ptr()); + if self.dll.is_null() { + Err(()) + } else { + Ok(()) + } + } + } + + // Function for each method we'd like to use. When called it will + // either read the cached function pointer or load it and return the + // loaded value. Loads are asserted to succeed. + $(pub fn $name(&mut self) -> Option<$name> { + // Assert that windows_sys::$name is declared to have the same + // argument types and return type as our declaration, although + // it might be either extern "C" or extern "system". + cfg_if::cfg_if! { + if #[cfg(any(target_arch = "x86", not(windows_raw_dylib)))] { + let _: unsafe extern "system" fn($($argty),*) -> $ret = super::windows_sys::$name; + } else { + let _: unsafe extern "C" fn($($argty),*) -> $ret = super::windows_sys::$name; + } + } + + unsafe { + if self.$name == 0 { + let name = concat!(stringify!($name), "\0"); + self.$name = self.symbol(name.as_bytes())?; + } + Some(mem::transmute::<usize, $name>(self.$name)) + } + })* + + fn symbol(&self, symbol: &[u8]) -> Option<usize> { + unsafe { + GetProcAddress(self.dll, symbol.as_ptr()).map(|address|address as usize) + } + } + } + + // Convenience proxy to use the cleanup locks to reference dbghelp + // functions. + #[allow(dead_code)] + impl Init { + $(pub fn $name(&self) -> $name { + // FIXME: https://github.com/rust-lang/backtrace-rs/issues/678 + #[allow(static_mut_refs)] + unsafe { + DBGHELP.$name().unwrap() + } + })* + + pub fn dbghelp(&self) -> *mut Dbghelp { + #[allow(unused_unsafe)] + unsafe { ptr::addr_of_mut!(DBGHELP) } + } + } + ) + +} + +dbghelp! { + extern "system" { + fn SymGetOptions() -> u32; + fn SymSetOptions(options: u32) -> u32; + fn SymInitializeW( + handle: HANDLE, + path: PCWSTR, + invade: BOOL + ) -> BOOL; + fn SymGetSearchPathW( + hprocess: HANDLE, + searchpatha: PWSTR, + searchpathlength: u32 + ) -> BOOL; + fn SymSetSearchPathW( + hprocess: HANDLE, + searchpatha: PCWSTR + ) -> BOOL; + fn EnumerateLoadedModulesW64( + hprocess: HANDLE, + enumloadedmodulescallback: PENUMLOADED_MODULES_CALLBACKW64, + usercontext: *const c_void + ) -> BOOL; + fn StackWalk64( + MachineType: u32, + hProcess: HANDLE, + hThread: HANDLE, + StackFrame: *mut STACKFRAME64, + ContextRecord: *mut c_void, + ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64, + FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64, + GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64, + TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64 + ) -> BOOL; + fn SymFunctionTableAccess64( + hProcess: HANDLE, + AddrBase: u64 + ) -> *mut c_void; + fn SymGetModuleBase64( + hProcess: HANDLE, + AddrBase: u64 + ) -> u64; + fn SymFromAddrW( + hProcess: HANDLE, + Address: u64, + Displacement: *mut u64, + Symbol: *mut SYMBOL_INFOW + ) -> BOOL; + fn SymGetLineFromAddrW64( + hProcess: HANDLE, + dwAddr: u64, + pdwDisplacement: *mut u32, + Line: *mut IMAGEHLP_LINEW64 + ) -> BOOL; + fn StackWalkEx( + MachineType: u32, + hProcess: HANDLE, + hThread: HANDLE, + StackFrame: *mut STACKFRAME_EX, + ContextRecord: *mut c_void, + ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64, + FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64, + GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64, + TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64, + Flags: u32 + ) -> BOOL; + fn SymFromInlineContextW( + hProcess: HANDLE, + Address: u64, + InlineContext: u32, + Displacement: *mut u64, + Symbol: *mut SYMBOL_INFOW + ) -> BOOL; + fn SymGetLineFromInlineContextW( + hProcess: HANDLE, + dwAddr: u64, + InlineContext: u32, + qwModuleBaseAddress: u64, + pdwDisplacement: *mut u32, + Line: *mut IMAGEHLP_LINEW64 + ) -> BOOL; + fn SymAddrIncludeInlineTrace( + hProcess: HANDLE, + Address: u64 + ) -> u32; + fn SymQueryInlineTrace( + hProcess: HANDLE, + StartAddress: u64, + StartContext: u32, + StartRetAddress: u64, + CurAddress: u64, + CurContext: *mut u32, + CurFrameIndex: *mut u32 + ) -> BOOL; + } +} + +pub struct Init { + lock: HANDLE, +} + +/// Initialize all support necessary to access `dbghelp` API functions from this +/// crate. +/// +/// Note that this function is **safe**, it internally has its own +/// synchronization. Also note that it is safe to call this function multiple +/// times recursively. +pub fn init() -> Result<Init, ()> { + use core::sync::atomic::{AtomicPtr, Ordering::SeqCst}; + + // Helper function for generating a name that's unique to the process. + fn mutex_name() -> [u8; 33] { + let mut name: [u8; 33] = *b"Local\\RustBacktraceMutex00000000\0"; + let mut id = unsafe { GetCurrentProcessId() }; + // Quick and dirty no alloc u32 to hex. + let mut index = name.len() - 1; + while id > 0 { + name[index - 1] = match (id & 0xF) as u8 { + h @ 0..=9 => b'0' + h, + h => b'A' + (h - 10), + }; + id >>= 4; + index -= 1; + } + name + } + + unsafe { + // First thing we need to do is to synchronize this function. This can + // be called concurrently from other threads or recursively within one + // thread. Note that it's trickier than that though because what we're + // using here, `dbghelp`, *also* needs to be synchronized with all other + // callers to `dbghelp` in this process. + // + // Typically there aren't really that many calls to `dbghelp` within the + // same process and we can probably safely assume that we're the only + // ones accessing it. There is, however, one primary other user we have + // to worry about which is ironically ourselves, but in the standard + // library. The Rust standard library depends on this crate for + // backtrace support, and this crate also exists on crates.io. This + // means that if the standard library is printing a panic backtrace it + // may race with this crate coming from crates.io, causing segfaults. + // + // To help solve this synchronization problem we employ a + // Windows-specific trick here (it is, after all, a Windows-specific + // restriction about synchronization). We create a *session-local* named + // mutex to protect this call. The intention here is that the standard + // library and this crate don't have to share Rust-level APIs to + // synchronize here but can instead work behind the scenes to make sure + // they're synchronizing with one another. That way when this function + // is called through the standard library or through crates.io we can be + // sure that the same mutex is being acquired. + // + // So all of that is to say that the first thing we do here is we + // atomically create a `HANDLE` which is a named mutex on Windows. We + // synchronize a bit with other threads sharing this function + // specifically and ensure that only one handle is created per instance + // of this function. Note that the handle is never closed once it's + // stored in the global. + // + // After we've actually go the lock we simply acquire it, and our `Init` + // handle we hand out will be responsible for dropping it eventually. + static LOCK: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); + let mut lock = LOCK.load(SeqCst); + if lock.is_null() { + let name = mutex_name(); + lock = CreateMutexA(ptr::null_mut(), FALSE, name.as_ptr()); + if lock.is_null() { + return Err(()); + } + if let Err(other) = LOCK.compare_exchange(ptr::null_mut(), lock, SeqCst, SeqCst) { + debug_assert!(!other.is_null()); + CloseHandle(lock); + lock = other; + } + } + debug_assert!(!lock.is_null()); + let r = WaitForSingleObjectEx(lock, INFINITE, FALSE); + debug_assert_eq!(r, 0); + let ret = Init { lock }; + + // Ok, phew! Now that we're all safely synchronized, let's actually + // start processing everything. First up we need to ensure that + // `dbghelp.dll` is actually loaded in this process. We do this + // dynamically to avoid a static dependency. This has historically been + // done to work around weird linking issues and is intended at making + // binaries a bit more portable since this is largely just a debugging + // utility. + // + // Once we've opened `dbghelp.dll` we need to call some initialization + // functions in it, and that's detailed more below. We only do this + // once, though, so we've got a global boolean indicating whether we're + // done yet or not. + // FIXME: https://github.com/rust-lang/backtrace-rs/issues/678 + #[allow(static_mut_refs)] + DBGHELP.ensure_open()?; + + static mut INITIALIZED: bool = false; + if !INITIALIZED { + set_optional_options(ret.dbghelp()); + INITIALIZED = true; + } + Ok(ret) + } +} +unsafe fn set_optional_options(dbghelp: *mut Dbghelp) -> Option<()> { + unsafe { + let orig = (*dbghelp).SymGetOptions()?(); + + // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because + // according to MSVC's own docs about this: "This is the fastest, most + // efficient way to use the symbol handler.", so let's do that! + (*dbghelp).SymSetOptions()?(orig | SYMOPT_DEFERRED_LOADS); + + // Actually initialize symbols with MSVC. Note that this can fail, but we + // ignore it. There's not a ton of prior art for this per se, but LLVM + // internally seems to ignore the return value here and one of the + // sanitizer libraries in LLVM prints a scary warning if this fails but + // basically ignores it in the long run. + // + // One case this comes up a lot for Rust is that the standard library and + // this crate on crates.io both want to compete for `SymInitializeW`. The + // standard library historically wanted to initialize then cleanup most of + // the time, but now that it's using this crate it means that someone will + // get to initialization first and the other will pick up that + // initialization. + (*dbghelp).SymInitializeW()?(GetCurrentProcess(), ptr::null_mut(), TRUE); + + // The default search path for dbghelp will only look in the current working + // directory and (possibly) `_NT_SYMBOL_PATH` and `_NT_ALT_SYMBOL_PATH`. + // However, we also want to look in the directory of the executable + // and each DLL that is loaded. To do this, we need to update the search path + // to include these directories. + // + // See https://learn.microsoft.com/cpp/build/reference/pdbpath for an + // example of where symbols are usually searched for. + let mut search_path_buf = Vec::new(); + search_path_buf.resize(1024, 0); + + // Prefill the buffer with the current search path. + if (*dbghelp).SymGetSearchPathW()?( + GetCurrentProcess(), + search_path_buf.as_mut_ptr(), + search_path_buf.len() as _, + ) == TRUE + { + // Trim the buffer to the actual length of the string. + let len = lstrlenW(search_path_buf.as_mut_ptr()); + assert!(len >= 0); + search_path_buf.truncate(len as usize); + } else { + // If getting the search path fails, at least include the current directory. + search_path_buf.clear(); + search_path_buf.push(utf16_char('.')); + search_path_buf.push(utf16_char(';')); + } + + let mut search_path = SearchPath::new(search_path_buf); + + // Update the search path to include the directory of the executable and each DLL. + (*dbghelp).EnumerateLoadedModulesW64()?( + GetCurrentProcess(), + Some(enum_loaded_modules_callback), + ((&mut search_path) as *mut SearchPath) as *mut c_void, + ); + + let new_search_path = search_path.finalize(); + + // Set the new search path. + (*dbghelp).SymSetSearchPathW()?(GetCurrentProcess(), new_search_path.as_ptr()); + } + Some(()) +} + +struct SearchPath { + search_path_utf16: Vec<u16>, +} + +fn utf16_char(c: char) -> u16 { + let buf = &mut [0u16; 2]; + let buf = c.encode_utf16(buf); + assert!(buf.len() == 1); + buf[0] +} + +impl SearchPath { + fn new(initial_search_path: Vec<u16>) -> Self { + Self { + search_path_utf16: initial_search_path, + } + } + + /// Add a path to the search path if it is not already present. + fn add(&mut self, path: &[u16]) { + let sep = utf16_char(';'); + + // We could deduplicate in a case-insensitive way, but case-sensitivity + // can be configured by directory on Windows, so let's not do that. + // https://learn.microsoft.com/windows/wsl/case-sensitivity + if !self + .search_path_utf16 + .split(|&c| c == sep) + .any(|p| p == path) + { + if self.search_path_utf16.last() != Some(&sep) { + self.search_path_utf16.push(sep); + } + self.search_path_utf16.extend_from_slice(path); + } + } + + fn finalize(mut self) -> Vec<u16> { + // Add a null terminator. + self.search_path_utf16.push(0); + self.search_path_utf16 + } +} + +extern "system" fn enum_loaded_modules_callback( + module_name: PCWSTR, + _: u64, + _: u32, + user_context: *const c_void, +) -> BOOL { + // `module_name` is an absolute path like `C:\path\to\module.dll` + // or `C:\path\to\module.exe` + let len: usize = unsafe { lstrlenW(module_name).try_into().unwrap() }; + + if len == 0 { + // This should not happen, but if it does, we can just ignore it. + return TRUE; + } + + let module_name = unsafe { slice::from_raw_parts(module_name, len) }; + let path_sep = utf16_char('\\'); + let alt_path_sep = utf16_char('/'); + + let Some(end_of_directory) = module_name + .iter() + .rposition(|&c| c == path_sep || c == alt_path_sep) + else { + // `module_name` being an absolute path, it should always contain at least one + // path separator. If not, there is nothing we can do. + return TRUE; + }; + + let search_path = unsafe { &mut *(user_context as *mut SearchPath) }; + search_path.add(&module_name[..end_of_directory]); + + TRUE +} + +impl Drop for Init { + fn drop(&mut self) { + unsafe { + let r = ReleaseMutex(self.lock); + debug_assert!(r != 0); + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/lib.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/lib.rs new file mode 100644 index 0000000..8e18835 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/lib.rs
@@ -0,0 +1,242 @@ +//! A library for acquiring a backtrace at runtime +//! +//! This library is meant to supplement the `RUST_BACKTRACE=1` support of the +//! standard library by allowing an acquisition of a backtrace at runtime +//! programmatically. The backtraces generated by this library do not need to be +//! parsed, for example, and expose the functionality of multiple backend +//! implementations. +//! +//! # Usage +//! +//! First, add this to your Cargo.toml +//! +//! ```toml +//! [dependencies] +//! backtrace = "0.3" +//! ``` +//! +//! Next: +//! +//! ``` +//! # // Unsafe here so test passes on no_std. +//! # #[cfg(feature = "std")] { +//! backtrace::trace(|frame| { +//! let ip = frame.ip(); +//! let symbol_address = frame.symbol_address(); +//! +//! // Resolve this instruction pointer to a symbol name +//! backtrace::resolve_frame(frame, |symbol| { +//! if let Some(name) = symbol.name() { +//! // ... +//! } +//! if let Some(filename) = symbol.filename() { +//! // ... +//! } +//! }); +//! +//! true // keep going to the next frame +//! }); +//! # } +//! ``` +//! +//! # Backtrace accuracy +//! +//! This crate implements best-effort attempts to get the native backtrace. This +//! is not always guaranteed to work, and some platforms don't return any +//! backtrace at all. If your application requires accurate backtraces then it's +//! recommended to closely evaluate this crate to see whether it's suitable +//! for your use case on your target platforms. +//! +//! Even on supported platforms, there's a number of reasons that backtraces may +//! be less-than-accurate, including but not limited to: +//! +//! * Unwind information may not be available. This crate primarily implements +//! backtraces by unwinding the stack, but not all functions may have +//! unwinding information (e.g. DWARF unwinding information). +//! +//! * Rust code may be compiled without unwinding information for some +//! functions. This can also happen for Rust code compiled with +//! `-Cpanic=abort`. You can remedy this, however, with +//! `-Cforce-unwind-tables` as a compiler option. +//! +//! * Unwind information may be inaccurate or corrupt. In the worst case +//! inaccurate unwind information can lead this library to segfault. In the +//! best case inaccurate information will result in a truncated stack trace. +//! +//! * Backtraces may not report filenames/line numbers correctly due to missing +//! or corrupt debug information. This won't lead to segfaults unlike corrupt +//! unwinding information, but missing or malformed debug information will +//! mean that filenames and line numbers will not be available. This may be +//! because debug information wasn't generated by the compiler, or it's just +//! missing on the filesystem. +//! +//! * Not all platforms are supported. For example there's no way to get a +//! backtrace on WebAssembly at the moment. +//! +//! * Crate features may be disabled. Currently this crate supports using Gimli +//! libbacktrace on non-Windows platforms for reading debuginfo for +//! backtraces. If both crate features are disabled, however, then these +//! platforms will generate a backtrace but be unable to generate symbols for +//! it. +//! +//! In most standard workflows for most standard platforms you generally don't +//! need to worry about these caveats. We'll try to fix ones where we can over +//! time, but otherwise it's important to be aware of the limitations of +//! unwinding-based backtraces! + +#![deny(missing_docs)] +#![no_std] +#![cfg_attr( + all(feature = "std", target_env = "sgx", target_vendor = "fortanix"), + feature(sgx_platform) +)] +#![warn(rust_2018_idioms)] +// When we're building as part of libstd, silence all warnings since they're +// irrelevant as this crate is developed out-of-tree. +#![cfg_attr(backtrace_in_libstd, allow(warnings))] +#![cfg_attr(not(feature = "std"), allow(dead_code))] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; + +// This is only used for gimli right now, which is only used on some platforms, and miri +// so don't worry if it's unused in other configurations. +#[allow(unused_extern_crates)] +extern crate alloc; + +pub use self::backtrace::{trace_unsynchronized, Frame}; +mod backtrace; + +pub use self::symbolize::resolve_frame_unsynchronized; +pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; +mod symbolize; + +pub use self::types::BytesOrWideString; +mod types; + +#[cfg(feature = "std")] +pub use self::symbolize::clear_symbol_cache; + +mod print; +pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt}; + +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + pub use self::backtrace::trace; + pub use self::symbolize::{resolve, resolve_frame}; + pub use self::capture::{Backtrace, BacktraceFrame, BacktraceSymbol}; + mod capture; + } +} + +cfg_if::cfg_if! { + if #[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))] { + pub use self::backtrace::set_image_base; + } +} + +#[cfg(feature = "std")] +mod lock { + use std::cell::Cell; + use std::sync::{Mutex, MutexGuard}; + + /// A "Maybe" LockGuard + pub struct LockGuard(Option<MutexGuard<'static, ()>>); + + /// The global lock + static LOCK: Mutex<()> = Mutex::new(()); + // Whether this thread is the one that holds the lock + thread_local!(static LOCK_HELD: Cell<bool> = const { Cell::new(false) }); + + impl Drop for LockGuard { + fn drop(&mut self) { + // Don't do anything if we're a LockGuard(None) + if self.0.is_some() { + LOCK_HELD.with(|slot| { + // Immediately crash if we somehow aren't the thread holding this lock + assert!(slot.get()); + // We are no longer the thread holding this lock + slot.set(false); + }); + } + // lock implicitly released here, if we're a LockGuard(Some(..)) + } + } + + /// Acquire a partially unsound(!!!) global re-entrant lock over + /// backtrace's internals. + /// + /// That is, this lock can be acquired as many times as you want + /// on a single thread without deadlocking, allowing one thread + /// to acquire exclusive access to the ability to make backtraces. + /// Calls to this locking function are freely sprinkled in every place + /// where that needs to be enforced. + /// + /// + /// # Why + /// + /// This was first introduced to guard uses of Windows' dbghelp API, + /// which isn't threadsafe. It's unclear if other things now rely on + /// this locking. + /// + /// + /// # How + /// + /// The basic idea is to have a single global mutex, and a thread_local + /// boolean saying "yep this is the thread that acquired the mutex". + /// + /// The first time a thread acquires the lock, it is handed a + /// `LockGuard(Some(..))` that will actually release the lock on Drop. + /// All subsequence attempts to lock on the same thread will see + /// that their thread acquired the lock, and get `LockGuard(None)` + /// which will do nothing when dropped. + /// + /// + /// # Safety + /// + /// As long as you only ever assign the returned LockGuard to a freshly + /// declared local variable, it will do its job correctly, as the "first" + /// LockGuard will strictly outlive all subsequent LockGuards and + /// properly release the lock when the thread is done with backtracing. + /// + /// However if you ever attempt to store a LockGuard beyond the scope + /// it was acquired in, it might actually be a `LockGuard(None)` that + /// doesn't actually hold the lock! In this case another thread might + /// acquire the lock and you'll get races this system was intended to + /// avoid! + /// + /// This is why this is "partially unsound". As a public API this would + /// be unacceptable, but this is crate-private, and if you use this in + /// the most obvious and simplistic way it Just Works™. + /// + /// Note however that std specifically bypasses this lock, and uses + /// the `*_unsynchronized` backtrace APIs. This is "fine" because + /// it wraps its own calls to backtrace in a non-reentrant Mutex + /// that prevents two backtraces from getting interleaved during printing. + pub fn lock() -> LockGuard { + // If we're the thread holding this lock, pretend to acquire the lock + // again by returning a LockGuard(None) + if LOCK_HELD.with(|l| l.get()) { + return LockGuard(None); + } + // Insist that we totally are the thread holding the lock + // (our thread will block until we are) + LOCK_HELD.with(|s| s.set(true)); + // ok *actually* try to acquire the lock, blocking as necessary + LockGuard(Some(LOCK.lock().unwrap())) + } +} + +#[cfg(all( + windows, + any( + target_env = "msvc", + all(target_env = "gnu", any(target_arch = "x86", target_arch = "arm")) + ), + not(target_vendor = "uwp") +))] +mod dbghelp; +// Auto-generated by windows-bindgen/riddle +#[cfg(any(windows, target_os = "cygwin"))] +mod windows_sys;
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/print.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/print.rs new file mode 100644 index 0000000..888840e --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/print.rs
@@ -0,0 +1,310 @@ +#[cfg(feature = "std")] +use super::{BacktraceFrame, BacktraceSymbol}; +use super::{BytesOrWideString, Frame, SymbolName}; +use core::ffi::c_void; +use core::fmt; + +const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>(); + +#[cfg(target_os = "fuchsia")] +mod fuchsia; + +/// A formatter for backtraces. +/// +/// This type can be used to print a backtrace regardless of where the backtrace +/// itself comes from. If you have a `Backtrace` type then its `Debug` +/// implementation already uses this printing format. +pub struct BacktraceFmt<'a, 'b> { + fmt: &'a mut fmt::Formatter<'b>, + frame_index: usize, + format: PrintFmt, + print_path: + &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result + 'b), +} + +/// The styles of printing that we can print +#[derive(Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum PrintFmt { + /// Prints a terser backtrace which ideally only contains relevant information + Short, + /// Prints a backtrace that contains all possible information + Full, +} + +impl<'a, 'b> BacktraceFmt<'a, 'b> { + /// Create a new `BacktraceFmt` which will write output to the provided + /// `fmt`. + /// + /// The `format` argument will control the style in which the backtrace is + /// printed, and the `print_path` argument will be used to print the + /// `BytesOrWideString` instances of filenames. This type itself doesn't do + /// any printing of filenames, but this callback is required to do so. + pub fn new( + fmt: &'a mut fmt::Formatter<'b>, + format: PrintFmt, + print_path: &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result + + 'b), + ) -> Self { + BacktraceFmt { + fmt, + frame_index: 0, + format, + print_path, + } + } + + /// Prints a preamble for the backtrace about to be printed. + /// + /// This is required on some platforms for backtraces to be fully + /// symbolicated later, and otherwise this should just be the first method + /// you call after creating a `BacktraceFmt`. + pub fn add_context(&mut self) -> fmt::Result { + #[cfg(target_os = "fuchsia")] + fuchsia::print_dso_context(self.fmt)?; + Ok(()) + } + + /// Adds a frame to the backtrace output. + /// + /// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used + /// to actually print a frame, and on destruction it will increment the + /// frame counter. + pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> { + BacktraceFrameFmt { + fmt: self, + symbol_index: 0, + } + } + + /// Completes the backtrace output. + /// + /// This is currently a no-op but is added for future compatibility with + /// backtrace formats. + pub fn finish(&mut self) -> fmt::Result { + #[cfg(target_os = "fuchsia")] + fuchsia::finish_context(self.fmt)?; + Ok(()) + } + + /// Inserts a message in the backtrace output. + /// + /// This allows information to be inserted between frames, + /// and won't increment the `frame_index` unlike the `frame` + /// method. + pub fn message(&mut self, msg: &str) -> fmt::Result { + self.fmt.write_str(msg) + } + + /// Return the inner formatter. + /// + /// This is used for writing custom information between frames with `write!` and `writeln!`, + /// and won't increment the `frame_index` unlike the `frame` method. + pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> { + self.fmt + } +} + +/// A formatter for just one frame of a backtrace. +/// +/// This type is created by the `BacktraceFmt::frame` function. +pub struct BacktraceFrameFmt<'fmt, 'a, 'b> { + fmt: &'fmt mut BacktraceFmt<'a, 'b>, + symbol_index: usize, +} + +impl BacktraceFrameFmt<'_, '_, '_> { + /// Prints a `BacktraceFrame` with this frame formatter. + /// + /// This will recursively print all `BacktraceSymbol` instances within the + /// `BacktraceFrame`. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + #[cfg(feature = "std")] + pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result { + let symbols = frame.symbols(); + for symbol in symbols { + self.backtrace_symbol(frame, symbol)?; + } + if symbols.is_empty() { + self.print_raw(frame.ip(), None, None, None)?; + } + Ok(()) + } + + /// Prints a `BacktraceSymbol` within a `BacktraceFrame`. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + #[cfg(feature = "std")] + pub fn backtrace_symbol( + &mut self, + frame: &BacktraceFrame, + symbol: &BacktraceSymbol, + ) -> fmt::Result { + self.print_raw_with_column( + frame.ip(), + symbol.name(), + // TODO: this isn't great that we don't end up printing anything + // with non-utf8 filenames. Thankfully almost everything is utf8 so + // this shouldn't be too bad. + symbol + .filename() + .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))), + symbol.lineno(), + symbol.colno(), + )?; + Ok(()) + } + + /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw + /// callbacks of this crate. + pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result { + self.print_raw_with_column( + frame.ip(), + symbol.name(), + symbol.filename_raw(), + symbol.lineno(), + symbol.colno(), + )?; + Ok(()) + } + + /// Adds a raw frame to the backtrace output. + /// + /// This method, unlike the previous, takes the raw arguments in case + /// they're being source from different locations. Note that this may be + /// called multiple times for one frame. + pub fn print_raw( + &mut self, + frame_ip: *mut c_void, + symbol_name: Option<SymbolName<'_>>, + filename: Option<BytesOrWideString<'_>>, + lineno: Option<u32>, + ) -> fmt::Result { + self.print_raw_with_column(frame_ip, symbol_name, filename, lineno, None) + } + + /// Adds a raw frame to the backtrace output, including column information. + /// + /// This method, like the previous, takes the raw arguments in case + /// they're being source from different locations. Note that this may be + /// called multiple times for one frame. + pub fn print_raw_with_column( + &mut self, + frame_ip: *mut c_void, + symbol_name: Option<SymbolName<'_>>, + filename: Option<BytesOrWideString<'_>>, + lineno: Option<u32>, + colno: Option<u32>, + ) -> fmt::Result { + // Fuchsia is unable to symbolize within a process so it has a special + // format which can be used to symbolize later. Print that instead of + // printing addresses in our own format here. + if cfg!(target_os = "fuchsia") { + self.print_raw_fuchsia(frame_ip)?; + } else { + self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?; + } + self.symbol_index += 1; + Ok(()) + } + + #[allow(unused_mut)] + fn print_raw_generic( + &mut self, + frame_ip: *mut c_void, + symbol_name: Option<SymbolName<'_>>, + filename: Option<BytesOrWideString<'_>>, + lineno: Option<u32>, + colno: Option<u32>, + ) -> fmt::Result { + // No need to print "null" frames, it basically just means that the + // system backtrace was a bit eager to trace back super far. + if let PrintFmt::Short = self.fmt.format { + if frame_ip.is_null() { + return Ok(()); + } + } + + // Print the index of the frame as well as the optional instruction + // pointer of the frame. If we're beyond the first symbol of this frame + // though we just print appropriate whitespace. + if self.symbol_index == 0 { + write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?; + if let PrintFmt::Full = self.fmt.format { + write!(self.fmt.fmt, "{frame_ip:HEX_WIDTH$?} - ")?; + } + } else { + write!(self.fmt.fmt, " ")?; + if let PrintFmt::Full = self.fmt.format { + write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?; + } + } + + // Next up write out the symbol name, using the alternate formatting for + // more information if we're a full backtrace. Here we also handle + // symbols which don't have a name, + match (symbol_name, &self.fmt.format) { + (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{name:#}")?, + (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{name}")?, + (None, _) => write!(self.fmt.fmt, "<unknown>")?, + } + self.fmt.fmt.write_str("\n")?; + + // And last up, print out the filename/line number if they're available. + if let (Some(file), Some(line)) = (filename, lineno) { + self.print_fileline(file, line, colno)?; + } + + Ok(()) + } + + fn print_fileline( + &mut self, + file: BytesOrWideString<'_>, + line: u32, + colno: Option<u32>, + ) -> fmt::Result { + // Filename/line are printed on lines under the symbol name, so print + // some appropriate whitespace to sort of right-align ourselves. + if let PrintFmt::Full = self.fmt.format { + write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?; + } + write!(self.fmt.fmt, " at ")?; + + // Delegate to our internal callback to print the filename and then + // print out the line number. + (self.fmt.print_path)(self.fmt.fmt, file)?; + write!(self.fmt.fmt, ":{line}")?; + + // Add column number, if available. + if let Some(colno) = colno { + write!(self.fmt.fmt, ":{colno}")?; + } + + writeln!(self.fmt.fmt)?; + Ok(()) + } + + fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result { + // We only care about the first symbol of a frame + if self.symbol_index == 0 { + self.fmt.fmt.write_str("{{{bt:")?; + write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?; + self.fmt.fmt.write_str("}}}\n")?; + } + Ok(()) + } +} + +impl Drop for BacktraceFrameFmt<'_, '_, '_> { + fn drop(&mut self) { + self.fmt.frame_index += 1; + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/print/fuchsia.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/print/fuchsia.rs new file mode 100644 index 0000000..15e3626 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/print/fuchsia.rs
@@ -0,0 +1,432 @@ +use core::fmt::{self, Write}; +use core::slice::from_raw_parts; +use libc::c_char; +use object::NativeEndian as NE; + +unsafe extern "C" { + // dl_iterate_phdr takes a callback that will receive a dl_phdr_info pointer + // for every DSO that has been linked into the process. dl_iterate_phdr also + // ensures that the dynamic linker is locked from start to finish of the + // iteration. If the callback returns a non-zero value the iteration is + // terminated early. 'data' will be passed as the third argument to the + // callback on each call. 'size' gives the size of the dl_phdr_info. + #[allow(improper_ctypes)] + fn dl_iterate_phdr( + f: extern "C" fn(info: &dl_phdr_info, size: usize, data: &mut DsoPrinter<'_, '_>) -> i32, + data: &mut DsoPrinter<'_, '_>, + ) -> i32; +} + +// We need to parse out the build ID and some basic program header data +// which means that we need a bit of stuff from the ELF spec as well. + +const PT_LOAD: u32 = 1; +const PT_NOTE: u32 = 4; + +// Now we have to replicate, bit for bit, the structure of the dl_phdr_info +// type used by fuchsia's current dynamic linker. Chromium also has this ABI +// boundary as well as crashpad. Eventually we'd like to move these cases to +// use elf-search but we'd need to provide that in the SDK and that has not +// yet been done. Thus we (and they) are stuck having to use this method +// which incurs a tight coupling with the fuchsia libc. + +#[allow(non_camel_case_types)] +#[repr(C)] +struct dl_phdr_info { + addr: *const u8, + name: *const c_char, + phdr: *const Elf_Phdr, + phnum: u16, + adds: u64, + subs: u64, + tls_modid: usize, + tls_data: *const u8, +} + +impl dl_phdr_info { + fn program_headers(&self) -> PhdrIter<'_> { + PhdrIter { + phdrs: self.phdr_slice(), + base: self.addr, + } + } + // We have no way of knowing of checking if e_phoff and e_phnum are valid. + // libc should ensure this for us however so it's safe to form a slice here. + fn phdr_slice(&self) -> &[Elf_Phdr] { + unsafe { from_raw_parts(self.phdr, self.phnum as usize) } + } +} + +struct PhdrIter<'a> { + phdrs: &'a [Elf_Phdr], + base: *const u8, +} + +impl<'a> Iterator for PhdrIter<'a> { + type Item = Phdr<'a>; + fn next(&mut self) -> Option<Self::Item> { + self.phdrs.split_first().map(|(phdr, new_phdrs)| { + self.phdrs = new_phdrs; + Phdr { + phdr, + base: self.base, + } + }) + } +} + +// Elf_Phdr represents a 64-bit ELF program header in the endianness of the target +// architecture. +#[allow(non_camel_case_types)] +#[derive(Clone, Debug)] +#[repr(C)] +struct Elf_Phdr { + p_type: u32, + p_flags: u32, + p_offset: u64, + p_vaddr: u64, + p_paddr: u64, + p_filesz: u64, + p_memsz: u64, + p_align: u64, +} + +// Phdr represents a valid ELF program header and its contents. +struct Phdr<'a> { + phdr: &'a Elf_Phdr, + base: *const u8, +} + +impl<'a> Phdr<'a> { + // We have no way of checking if p_addr or p_memsz are valid. Fuchsia's libc + // parses the notes first however so by virtue of being here these headers + // must be valid. NoteIter does not require the underlying data to be valid + // but it does require the bounds to be valid. We trust that libc has ensured + // that this is the case for us here. + fn notes(&self) -> NoteIter<'a> { + unsafe { + NoteIter::new( + self.base.add(self.phdr.p_offset as usize), + self.phdr.p_memsz as usize, + ) + } + } +} + +// The note type for build IDs. +const NT_GNU_BUILD_ID: u32 = 3; + +// Elf_Nhdr represents an ELF note header in the endianness of the target. +#[allow(non_camel_case_types)] +type Elf_Nhdr = object::elf::NoteHeader32<NE>; + +// Note represents an ELF note (header + contents). The name is left as a u8 +// slice because it is not always null terminated and rust makes it easy enough +// to check that the bytes match eitherway. +struct Note<'a> { + name: &'a [u8], + desc: &'a [u8], + tipe: u32, +} + +// NoteIter lets you safely iterate over a note segment. It terminates as soon +// as an error occurs or there are no more notes. If you iterate over invalid +// data it will function as though no notes were found. +struct NoteIter<'a> { + base: &'a [u8], + error: bool, +} + +impl<'a> NoteIter<'a> { + // It is an invariant of function that the pointer and size given denote a + // valid range of bytes that can all be read. The contents of these bytes + // can be anything but the range must be valid for this to be safe. + unsafe fn new(base: *const u8, size: usize) -> Self { + NoteIter { + base: unsafe { from_raw_parts(base, size) }, + error: false, + } + } +} + +// align_to aligns 'x' to 'to'-byte alignment assuming 'to' is a power of 2. +// This follows a standard pattern in C/C++ ELF parsing code where +// (x + to - 1) & -to is used. Rust does not let you negate usize so I use +// 2's-complement conversion to recreate that. +fn align_to(x: usize, to: usize) -> usize { + (x + to - 1) & (!to + 1) +} + +// take_bytes_align4 consumes num bytes from the slice (if present) and +// additionally ensures that the final slice is properlly aligned. If an +// either the number of bytes requested is too large or the slice can't be +// realigned afterwards due to not enough remaining bytes existing, None is +// returned and the slice is not modified. +fn take_bytes_align4<'a>(num: usize, bytes: &mut &'a [u8]) -> Option<&'a [u8]> { + if bytes.len() < align_to(num, 4) { + return None; + } + let (out, bytes_new) = bytes.split_at(num); + *bytes = &bytes_new[align_to(num, 4) - num..]; + Some(out) +} + +/// This function has no invariants the caller must uphold, but +/// it will return `None`, without mutating, if `bytes` has insufficient size or alignment. +/// If this returns `Some(nhdr)`, then `bytes` was and remains 4-byte-aligned. +/// The values in the Elf_Nhdr fields might be nonsense. +fn take_nhdr<'a>(bytes: &mut &'a [u8]) -> Option<&'a Elf_Nhdr> { + let (out, rest) = object::pod::from_bytes::<Elf_Nhdr>(bytes).ok()?; + // Note that size_of::<Elf_Nhdr>() is always a multiple of 4-bytes, so the + // 4-byte alignment is maintained. + *bytes = rest; + Some(out) +} + +impl<'a> Iterator for NoteIter<'a> { + type Item = Note<'a>; + fn next(&mut self) -> Option<Self::Item> { + // Check if we've reached the end. + if self.base.is_empty() || self.error { + return None; + } + // We transmute out an nhdr but we carefully consider the resulting + // struct. We don't trust the namesz or descsz and we make no unsafe + // decisions based on the type. So even if we get out complete garbage + // we should still be safe. + let nhdr = take_nhdr(&mut self.base)?; + let name = take_bytes_align4(nhdr.n_namesz.get(NE) as usize, &mut self.base)?; + let desc = take_bytes_align4(nhdr.n_descsz.get(NE) as usize, &mut self.base)?; + Some(Note { + name: name, + desc: desc, + tipe: nhdr.n_type.get(NE), + }) + } +} + +struct Perm(u32); + +/// Indicates that a segment is executable. +const PERM_X: u32 = 0b00000001; +/// Indicates that a segment is writable. +const PERM_W: u32 = 0b00000010; +/// Indicates that a segment is readable. +const PERM_R: u32 = 0b00000100; + +impl core::fmt::Display for Perm { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let v = self.0; + if v & PERM_R != 0 { + f.write_char('r')? + } + if v & PERM_W != 0 { + f.write_char('w')? + } + if v & PERM_X != 0 { + f.write_char('x')? + } + Ok(()) + } +} + +/// Represents an ELF segment at runtime. +struct Segment { + /// Gives the runtime virtual address of this segment's contents. + addr: usize, + /// Gives the memory size of this segment's contents. + size: usize, + /// Gives the module virtual address of this segment with the ELF file. + mod_rel_addr: usize, + /// Gives the permissions found in the ELF file. These permissions are not + /// necessarily the permissions present at runtime however. + flags: Perm, +} + +/// Lets one iterate over Segments from a DSO. +struct SegmentIter<'a> { + phdrs: &'a [Elf_Phdr], + base: usize, +} + +impl Iterator for SegmentIter<'_> { + type Item = Segment; + + fn next(&mut self) -> Option<Self::Item> { + self.phdrs.split_first().and_then(|(phdr, new_phdrs)| { + self.phdrs = new_phdrs; + if phdr.p_type != PT_LOAD { + self.next() + } else { + Some(Segment { + addr: phdr.p_vaddr as usize + self.base, + size: phdr.p_memsz as usize, + mod_rel_addr: phdr.p_vaddr as usize, + flags: Perm(phdr.p_flags), + }) + } + }) + } +} + +/// Represents an ELF DSO (Dynamic Shared Object). This type references +/// the data stored in the actual DSO rather than making its own copy. +struct Dso<'a> { + /// The dynamic linker always gives us a name, even if the name is empty. + /// In the case of the main executable this name will be empty. In the case + /// of a shared object it will be the soname (see DT_SONAME). + name: &'a str, + /// On Fuchsia virtually all binaries have build IDs but this is not a strict + /// requirement. There's no way to match up DSO information with a real ELF + /// file afterwards if there is no build_id so we require that every DSO + /// have one here. DSO's without a build_id are ignored. + build_id: &'a [u8], + + base: usize, + phdrs: &'a [Elf_Phdr], +} + +impl Dso<'_> { + /// Returns an iterator over Segments in this DSO. + fn segments(&self) -> SegmentIter<'_> { + SegmentIter { + phdrs: self.phdrs.as_ref(), + base: self.base, + } + } +} + +struct HexSlice<'a> { + bytes: &'a [u8], +} + +impl fmt::Display for HexSlice<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in self.bytes { + write!(f, "{byte:02x}")?; + } + Ok(()) + } +} + +fn get_build_id<'a>(info: &'a dl_phdr_info) -> Option<&'a [u8]> { + for phdr in info.program_headers() { + if phdr.phdr.p_type == PT_NOTE { + for note in phdr.notes() { + if note.tipe == NT_GNU_BUILD_ID && (note.name == b"GNU\0" || note.name == b"GNU") { + return Some(note.desc); + } + } + } + } + None +} + +/// These errors encode issues that arise while parsing information about +/// each DSO. +enum Error { + /// NameError means that an error occurred while converting a C style string + /// into a rust string. + NameError, + /// BuildIDError means that we didn't find a build ID. This could either be + /// because the DSO had no build ID or because the segment containing the + /// build ID was malformed. + BuildIDError, +} + +/// Calls either 'dso' or 'error' for each DSO linked into the process by the +/// dynamic linker. +/// +/// # Arguments +/// +/// * `visitor` - A DsoPrinter that will have one of eats methods called foreach DSO. +fn for_each_dso(mut visitor: &mut DsoPrinter<'_, '_>) { + extern "C" fn callback( + info: &dl_phdr_info, + _size: usize, + visitor: &mut DsoPrinter<'_, '_>, + ) -> i32 { + // dl_iterate_phdr ensures that info.name will point to a valid + // location. + let name_len = unsafe { libc::strlen(info.name) }; + let name_slice: &[u8] = + unsafe { core::slice::from_raw_parts(info.name.cast::<u8>(), name_len) }; + let name = match core::str::from_utf8(name_slice) { + Ok(name) => name, + Err(_) => { + return visitor.error(Error::NameError) as i32; + } + }; + let build_id = match get_build_id(info) { + Some(build_id) => build_id, + None => { + return visitor.error(Error::BuildIDError) as i32; + } + }; + visitor.dso(Dso { + name: name, + build_id: build_id, + phdrs: info.phdr_slice(), + base: info.addr as usize, + }) as i32 + } + unsafe { dl_iterate_phdr(callback, &mut visitor) }; +} + +struct DsoPrinter<'a, 'b> { + writer: &'a mut core::fmt::Formatter<'b>, + module_count: usize, + error: core::fmt::Result, +} + +impl DsoPrinter<'_, '_> { + fn dso(&mut self, dso: Dso<'_>) -> bool { + let mut write = || { + write!( + self.writer, + "{{{{{{module:{:#x}:{}:elf:{}}}}}}}\n", + self.module_count, + dso.name, + HexSlice { + bytes: dso.build_id.as_ref() + } + )?; + for seg in dso.segments() { + write!( + self.writer, + "{{{{{{mmap:{:#x}:{:#x}:load:{:#x}:{}:{:#x}}}}}}}\n", + seg.addr, seg.size, self.module_count, seg.flags, seg.mod_rel_addr + )?; + } + self.module_count += 1; + Ok(()) + }; + match write() { + Ok(()) => false, + Err(err) => { + self.error = Err(err); + true + } + } + } + fn error(&mut self, _error: Error) -> bool { + false + } +} + +/// This function prints the Fuchsia symbolizer markup for all information contained in a DSO. +pub fn print_dso_context(out: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + out.write_str("{{{reset:begin}}}\n")?; + let mut visitor = DsoPrinter { + writer: out, + module_count: 0, + error: Ok(()), + }; + for_each_dso(&mut visitor); + visitor.error +} + +/// This function prints the Fuchsia symbolizer markup to end the backtrace. +pub fn finish_context(out: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + out.write_str("{{{reset:end}}}\n") +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/dbghelp.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/dbghelp.rs new file mode 100644 index 0000000..243eb27 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/dbghelp.rs
@@ -0,0 +1,325 @@ +//! Symbolication strategy using `dbghelp.dll` on Windows, only used for MSVC +//! +//! This symbolication strategy, like with backtraces, uses dynamically loaded +//! information from `dbghelp.dll`. (see `src/dbghelp.rs` for info about why +//! it's dynamically loaded). +//! +//! This API selects its resolution strategy based on the frame provided or the +//! information we have at hand. If a frame from `StackWalkEx` is given to us +//! then we use similar APIs to generate correct information about inlined +//! functions. Otherwise if all we have is an address or an older stack frame +//! from `StackWalk64` we use the older APIs for symbolication. +//! +//! There's a good deal of support in this module, but a good chunk of it is +//! converting back and forth between Windows types and Rust types. For example +//! symbols come to us as wide strings which we then convert to utf-8 strings if +//! we can. + +#![allow(bad_style)] + +use super::super::{dbghelp, windows_sys::*}; +use super::{BytesOrWideString, ResolveWhat, SymbolName}; +use core::cmp; +use core::ffi::c_void; +use core::marker; +use core::mem::{self, MaybeUninit}; +use core::ptr; +use core::slice; + +// FIXME: replace with ptr::from_ref once MSRV is high enough +#[inline(always)] +#[must_use] +const fn ptr_from_ref<T: ?Sized>(r: &T) -> *const T { + r +} + +// Store an OsString on std so we can provide the symbol name and filename. +pub struct Symbol<'a> { + name: *const [u8], + addr: *mut c_void, + line: Option<u32>, + filename: Option<*const [u16]>, + #[cfg(feature = "std")] + _filename_cache: Option<::std::ffi::OsString>, + #[cfg(not(feature = "std"))] + _filename_cache: (), + _marker: marker::PhantomData<&'a i32>, +} + +impl Symbol<'_> { + pub fn name(&self) -> Option<SymbolName<'_>> { + Some(SymbolName::new(unsafe { &*self.name })) + } + + pub fn addr(&self) -> Option<*mut c_void> { + Some(self.addr) + } + + pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> { + self.filename + .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) }) + } + + pub fn colno(&self) -> Option<u32> { + None + } + + pub fn lineno(&self) -> Option<u32> { + self.line + } + + #[cfg(feature = "std")] + pub fn filename(&self) -> Option<&::std::path::Path> { + use std::path::Path; + + self._filename_cache.as_ref().map(Path::new) + } +} + +#[repr(C, align(8))] +struct Aligned8<T>(T); + +#[cfg(not(target_vendor = "win7"))] +pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { + // Ensure this process's symbols are initialized + let dbghelp = match dbghelp::init() { + Ok(dbghelp) => dbghelp, + Err(()) => return, // oh well... + }; + unsafe { + match what { + ResolveWhat::Address(_) => { + resolve_with_inline(&dbghelp, what.address_or_ip(), None, cb) + } + ResolveWhat::Frame(frame) => { + resolve_with_inline(&dbghelp, frame.ip(), frame.inner.inline_context(), cb) + } + }; + } +} + +#[cfg(target_vendor = "win7")] +pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { + // Ensure this process's symbols are initialized + let dbghelp = match dbghelp::init() { + Ok(dbghelp) => dbghelp, + Err(()) => return, // oh well... + }; + + unsafe { + let resolve_inner = if (*dbghelp.dbghelp()).SymAddrIncludeInlineTrace().is_some() { + // We are on a version of dbghelp 6.2+, which contains the more modern + // Inline APIs. + resolve_with_inline + } else { + // We are on an older version of dbghelp which doesn't contain the Inline + // APIs. + resolve_legacy + }; + match what { + ResolveWhat::Address(_) => resolve_inner(&dbghelp, what.address_or_ip(), None, cb), + ResolveWhat::Frame(frame) => { + resolve_inner(&dbghelp, frame.ip(), frame.inner.inline_context(), cb) + } + }; + } +} + +/// Resolve the address using the legacy dbghelp API. +/// +/// This should work all the way down to Windows XP. The inline context is +/// ignored, since this concept was only introduced in dbghelp 6.2+. +#[cfg(target_vendor = "win7")] +unsafe fn resolve_legacy( + dbghelp: &dbghelp::Init, + addr: *mut c_void, + _inline_context: Option<u32>, + cb: &mut dyn FnMut(&super::Symbol), +) -> Option<()> { + let addr = super::adjust_ip(addr) as u64; + unsafe { + do_resolve( + |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr, &mut 0, info), + |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr, &mut 0, line), + cb, + ); + } + Some(()) +} + +/// Resolve the address using the modern dbghelp APIs. +/// +/// Note that calling this function requires having dbghelp 6.2+ loaded - and +/// will panic otherwise. +unsafe fn resolve_with_inline( + dbghelp: &dbghelp::Init, + addr: *mut c_void, + inline_context: Option<u32>, + cb: &mut dyn FnMut(&super::Symbol), +) -> Option<()> { + unsafe { + let current_process = GetCurrentProcess(); + // Ensure we have the functions we need. Return if any aren't found. + let SymFromInlineContextW = (*dbghelp.dbghelp()).SymFromInlineContextW()?; + let SymGetLineFromInlineContextW = (*dbghelp.dbghelp()).SymGetLineFromInlineContextW()?; + + let addr = super::adjust_ip(addr) as u64; + + let (inlined_frame_count, inline_context) = if let Some(ic) = inline_context { + (0, ic) + } else { + let SymAddrIncludeInlineTrace = (*dbghelp.dbghelp()).SymAddrIncludeInlineTrace()?; + let SymQueryInlineTrace = (*dbghelp.dbghelp()).SymQueryInlineTrace()?; + + let mut inlined_frame_count = SymAddrIncludeInlineTrace(current_process, addr); + + let mut inline_context = 0; + + // If there is are inlined frames but we can't load them for some reason OR if there are no + // inlined frames, then we disregard inlined_frame_count and inline_context. + if (inlined_frame_count > 0 + && SymQueryInlineTrace( + current_process, + addr, + 0, + addr, + addr, + &mut inline_context, + &mut 0, + ) != TRUE) + || inlined_frame_count == 0 + { + inlined_frame_count = 0; + inline_context = 0; + } + + (inlined_frame_count, inline_context) + }; + + let last_inline_context = inline_context + 1 + inlined_frame_count; + + for inline_context in inline_context..last_inline_context { + do_resolve( + |info| SymFromInlineContextW(current_process, addr, inline_context, &mut 0, info), + |line| { + SymGetLineFromInlineContextW( + current_process, + addr, + inline_context, + 0, + &mut 0, + line, + ) + }, + cb, + ); + } + } + Some(()) +} + +/// This function is only meant to be called with certain Windows API functions as its arguments, +/// using closures to simplify away here-unspecified arguments: +/// - `sym_from_addr`: either `SymFromAddrW` or `SymFromInlineContextW` +/// - `get_line_from_addr`: `SymGetLineFromAddrW64` or `SymGetLineFromInlineContextW` +unsafe fn do_resolve( + sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL, + get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL, + cb: &mut dyn FnMut(&super::Symbol), +) { + const SIZE: usize = 2 * MAX_SYM_NAME as usize + mem::size_of::<SYMBOL_INFOW>(); + let mut data = MaybeUninit::<Aligned8<[u8; SIZE]>>::zeroed(); + let info = data.as_mut_ptr().cast::<SYMBOL_INFOW>(); + unsafe { (*info).MaxNameLen = MAX_SYM_NAME as u32 }; + // the struct size in C. the value is different to + // `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + unsafe { (*info).SizeOfStruct = 88 }; + + if sym_from_addr(info) != TRUE { + return; + } + + // If the symbol name is greater than MaxNameLen, SymFromAddrW will + // give a buffer of (MaxNameLen - 1) characters and set NameLen to + // the real value. + // SAFETY: We assume NameLen has been initialized by SymFromAddrW, and we initialized MaxNameLen + let name_len = unsafe { cmp::min((*info).NameLen as usize, (*info).MaxNameLen as usize - 1) }; + // Name must be initialized by SymFromAddrW, but we only interact with it as a pointer anyways. + let name_ptr = (&raw const (*info).Name).cast::<u16>(); + + // Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like + // all other platforms + let mut name_buffer = [0_u8; 256]; + let mut name_len = unsafe { + WideCharToMultiByte( + CP_UTF8, + 0, + name_ptr, + name_len as i32, + name_buffer.as_mut_ptr(), + name_buffer.len() as i32, + core::ptr::null_mut(), + core::ptr::null_mut(), + ) as usize + }; + if name_len == 0 { + // If the returned length is zero that means the buffer wasn't big enough. + // However, the buffer will be filled with as much as will fit. + name_len = name_buffer.len(); + } else if name_len > name_buffer.len() { + // This can't happen. + return; + } + let name = ptr::addr_of!(name_buffer[..name_len]); + + let mut line = IMAGEHLP_LINEW64 { + SizeOfStruct: 0, + Key: core::ptr::null_mut(), + LineNumber: 0, + FileName: core::ptr::null_mut(), + Address: 0, + }; + line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as u32; + + let mut filename = None; + let mut lineno = None; + if get_line_from_addr(&mut line) == TRUE { + lineno = Some(line.LineNumber); + + let base = line.FileName; + let mut len = 0; + while unsafe { *base.offset(len) != 0 } { + len += 1; + } + + let len = len as usize; + + unsafe { + filename = Some(ptr_from_ref(slice::from_raw_parts(base, len))); + } + } + + cb(&super::Symbol { + inner: Symbol { + name, + // SAFETY: we assume Address has been initialized by SymFromAddrW + addr: unsafe { (*info).Address } as *mut _, + line: lineno, + filename, + _filename_cache: unsafe { cache(filename) }, + _marker: marker::PhantomData, + }, + }) +} + +#[cfg(feature = "std")] +unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> { + use std::os::windows::ffi::OsStringExt; + unsafe { filename.map(|f| ::std::ffi::OsString::from_wide(&*f)) } +} + +#[cfg(not(feature = "std"))] +unsafe fn cache(_filename: Option<*const [u16]>) {} + +pub unsafe fn clear_symbol_cache() {}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli.rs new file mode 100644 index 0000000..d7ff410 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli.rs
@@ -0,0 +1,567 @@ +//! Support for symbolication using the `gimli` crate on crates.io +//! +//! This is the default symbolication implementation for Rust. + +use self::gimli::read::EndianSlice; +use self::gimli::NativeEndian as Endian; +use self::mmap::Mmap; +use self::stash::Stash; +use super::BytesOrWideString; +use super::ResolveWhat; +use super::SymbolName; +use addr2line::gimli; +use core::convert::TryInto; +use core::mem; +use libc::c_void; +use mystd::ffi::OsString; +use mystd::fs::File; +use mystd::path::Path; +use mystd::prelude::v1::*; + +#[cfg(backtrace_in_libstd)] +mod mystd { + pub use crate::*; +} +#[cfg(not(backtrace_in_libstd))] +extern crate std as mystd; + +cfg_if::cfg_if! { + if #[cfg(windows)] { + #[path = "gimli/mmap_windows.rs"] + mod mmap; + } else if #[cfg(target_vendor = "apple")] { + #[path = "gimli/mmap_unix.rs"] + mod mmap; + } else if #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", + target_os = "hurd", + target_os = "linux", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "aix", + target_os = "cygwin", + ))] { + #[path = "gimli/mmap_unix.rs"] + mod mmap; + } else { + #[path = "gimli/mmap_fake.rs"] + mod mmap; + } +} + +mod lru; +mod stash; + +use lru::Lru; + +const MAPPINGS_CACHE_SIZE: usize = 4; + +struct Mapping { + // 'static lifetime is a lie to hack around lack of support for self-referential structs. + cx: Context<'static>, + _map: Mmap, + stash: Stash, +} + +enum Either<A, B> { + #[allow(dead_code)] + A(A), + B(B), +} + +impl Mapping { + /// Creates a `Mapping` by ensuring that the `data` specified is used to + /// create a `Context` and it can only borrow from that or the `Stash` of + /// decompressed sections or auxiliary data. + fn mk<F>(data: Mmap, mk: F) -> Option<Mapping> + where + F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Context<'a>>, + { + Mapping::mk_or_other(data, move |data, stash| { + let cx = mk(data, stash)?; + Some(Either::B(cx)) + }) + } + + /// Creates a `Mapping` from `data`, or if the closure decides to, returns a + /// different mapping. + fn mk_or_other<F>(data: Mmap, mk: F) -> Option<Mapping> + where + F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Either<Mapping, Context<'a>>>, + { + let stash = Stash::new(); + let cx = match mk(&data, &stash)? { + Either::A(mapping) => return Some(mapping), + Either::B(cx) => cx, + }; + Some(Mapping { + // Convert to 'static lifetimes since the symbols should + // only borrow `map` and `stash` and we're preserving them below. + cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) }, + _map: data, + stash, + }) + } +} + +struct Context<'a> { + dwarf: addr2line::Context<EndianSlice<'a, Endian>>, + object: Object<'a>, + package: Option<gimli::DwarfPackage<EndianSlice<'a, Endian>>>, +} + +impl<'data> Context<'data> { + // #[feature(optimize_attr)] is enabled when we're built inside libstd + #[cfg_attr(backtrace_in_libstd, optimize(size))] + fn new( + stash: &'data Stash, + object: Object<'data>, + sup: Option<Object<'data>>, + dwp: Option<Object<'data>>, + ) -> Option<Context<'data>> { + let mut sections = gimli::Dwarf::load(|id| -> Result<_, ()> { + if cfg!(not(target_os = "aix")) { + let data = object.section(stash, id.name()).unwrap_or(&[]); + Ok(EndianSlice::new(data, Endian)) + } else if let Some(name) = id.xcoff_name() { + let data = object.section(stash, name).unwrap_or(&[]); + Ok(EndianSlice::new(data, Endian)) + } else { + Ok(EndianSlice::new(&[], Endian)) + } + }) + .ok()?; + + if let Some(sup) = sup { + sections + .load_sup(|id| -> Result<_, ()> { + let data = sup.section(stash, id.name()).unwrap_or(&[]); + Ok(EndianSlice::new(data, Endian)) + }) + .ok()?; + } + let dwarf = addr2line::Context::from_dwarf(sections).ok()?; + + let mut package = None; + if let Some(dwp) = dwp { + package = Some( + gimli::DwarfPackage::load( + |id| -> Result<_, gimli::Error> { + let data = id + .dwo_name() + .and_then(|name| dwp.section(stash, name)) + .unwrap_or(&[]); + Ok(EndianSlice::new(data, Endian)) + }, + EndianSlice::new(&[], Endian), + ) + .ok()?, + ); + } + + Some(Context { + dwarf, + object, + package, + }) + } + + fn find_frames( + &'_ self, + stash: &'data Stash, + probe: u64, + ) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> { + use addr2line::{LookupContinuation, LookupResult}; + + let mut l = self.dwarf.find_frames(probe); + loop { + let (load, continuation) = match l { + LookupResult::Output(output) => break output, + LookupResult::Load { load, continuation } => (load, continuation), + }; + + l = continuation.resume(handle_split_dwarf(self.package.as_ref(), stash, load)); + } + } +} + +fn mmap(path: &Path) -> Option<Mmap> { + let file = File::open(path).ok()?; + let len = file.metadata().ok()?.len().try_into().ok()?; + unsafe { Mmap::map(&file, len, 0) } +} + +cfg_if::cfg_if! { + if #[cfg(any(windows, target_os = "cygwin"))] { + mod coff; + use self::coff::{handle_split_dwarf, Object}; + } else if #[cfg(any(target_vendor = "apple"))] { + mod macho; + use self::macho::{handle_split_dwarf, Object}; + } else if #[cfg(target_os = "aix")] { + mod xcoff; + use self::xcoff::{handle_split_dwarf, Object}; + } else { + mod elf; + use self::elf::{handle_split_dwarf, Object}; + } +} + +cfg_if::cfg_if! { + if #[cfg(any(windows, target_os = "cygwin"))] { + mod libs_windows; + use libs_windows::native_libraries; + } else if #[cfg(target_vendor = "apple")] { + mod libs_macos; + use libs_macos::native_libraries; + } else if #[cfg(target_os = "illumos")] { + mod libs_illumos; + use libs_illumos::native_libraries; + } else if #[cfg(all( + any( + target_os = "linux", + target_os = "fuchsia", + target_os = "freebsd", + target_os = "hurd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "nto", + target_os = "android", + ), + not(target_env = "uclibc"), + ))] { + mod libs_dl_iterate_phdr; + use libs_dl_iterate_phdr::native_libraries; + #[path = "gimli/parse_running_mmaps_unix.rs"] + mod parse_running_mmaps; + } else if #[cfg(target_env = "libnx")] { + mod libs_libnx; + use libs_libnx::native_libraries; + } else if #[cfg(target_os = "haiku")] { + mod libs_haiku; + use libs_haiku::native_libraries; + } else if #[cfg(target_os = "aix")] { + mod libs_aix; + use libs_aix::native_libraries; + } else { + // Everything else should doesn't know how to load native libraries. + fn native_libraries() -> Vec<Library> { + Vec::new() + } + } +} + +#[derive(Default)] +struct Cache { + /// All known shared libraries that have been loaded. + libraries: Vec<Library>, + + /// Mappings cache where we retain parsed dwarf information. + /// + /// This list has a fixed capacity for its entire lifetime which never + /// increases. The `usize` element of each pair is an index into `libraries` + /// above where `usize::max_value()` represents the current executable. The + /// `Mapping` is corresponding parsed dwarf information. + /// + /// Note that this is basically an LRU cache and we'll be shifting things + /// around in here as we symbolize addresses. + mappings: Lru<(usize, Mapping), MAPPINGS_CACHE_SIZE>, +} + +struct Library { + name: OsString, + #[cfg(target_os = "android")] + /// On Android, the dynamic linker [can map libraries directly from a + /// ZIP archive][ndk-linker-changes] (typically an `.apk`). + /// + /// The linker requires that these libraries are stored uncompressed + /// and page-aligned. + /// + /// These "embedded" libraries have filepaths of the form + /// `/path/to/my.apk!/lib/mylib.so` (where `/path/to/my.apk` is the archive + /// and `lib/mylib.so` is the name of the library within the archive). + /// + /// This mechanism is present on Android since API level 23. + /// + /// [ndk-linker-changes]: https://android.googlesource.com/platform/bionic/+/main/android-changes-for-ndk-developers.md#opening-shared-libraries-directly-from-an-apk + zip_offset: Option<u64>, + #[cfg(target_os = "aix")] + /// On AIX, the library mmapped can be a member of a big-archive file. + /// For example, with a big-archive named libfoo.a containing libbar.so, + /// one can use `dlopen("libfoo.a(libbar.so)", RTLD_MEMBER | RTLD_LAZY)` + /// to use the `libbar.so` library. In this case, only `libbar.so` is + /// mmapped, not the whole `libfoo.a`. + member_name: OsString, + /// Segments of this library loaded into memory, and where they're loaded. + segments: Vec<LibrarySegment>, + /// The "bias" of this library, typically where it's loaded into memory. + /// This value is added to each segment's stated address to get the actual + /// virtual memory address that the segment is loaded into. Additionally + /// this bias is subtracted from real virtual memory addresses to index into + /// debuginfo and the symbol table. + bias: usize, +} + +struct LibrarySegment { + /// The stated address of this segment in the object file. This is not + /// actually where the segment is loaded, but rather this address plus the + /// containing library's `bias` is where to find it. + stated_virtual_memory_address: usize, + /// The size of this segment in memory. + len: usize, +} + +fn create_mapping(lib: &Library) -> Option<Mapping> { + cfg_if::cfg_if! { + if #[cfg(target_os = "aix")] { + Mapping::new(lib.name.as_ref(), &lib.member_name) + } else if #[cfg(target_os = "android")] { + Mapping::new_android(lib.name.as_ref(), lib.zip_offset) + } else { + Mapping::new(lib.name.as_ref()) + } + } +} + +/// Try to extract the archive path from an "embedded" library path +/// (e.g. `/path/to/my.apk` from `/path/to/my.apk!/mylib.so`). +/// +/// Returns `None` if the path does not contain a `!/` separator. +#[cfg(target_os = "android")] +fn extract_zip_path_android(path: &mystd::ffi::OsStr) -> Option<&mystd::ffi::OsStr> { + use mystd::os::unix::ffi::OsStrExt; + + path.as_bytes() + .windows(2) + .enumerate() + .find(|(_, chunk)| chunk == b"!/") + .map(|(index, _)| mystd::ffi::OsStr::from_bytes(path.as_bytes().split_at(index).0)) +} + +// unsafe because this is required to be externally synchronized +pub unsafe fn clear_symbol_cache() { + unsafe { + Cache::with_global(|cache| cache.mappings.clear()); + } +} + +impl Cache { + fn new() -> Cache { + Cache { + mappings: Lru::default(), + libraries: native_libraries(), + } + } + + // unsafe because this is required to be externally synchronized + // #[feature(optimize_attr)] is enabled when we're built inside libstd + #[cfg_attr(backtrace_in_libstd, optimize(size))] + unsafe fn with_global(f: impl FnOnce(&mut Self)) { + // A very small, very simple LRU cache for debug info mappings. + // + // The hit rate should be very high, since the typical stack doesn't cross + // between many shared libraries. + // + // The `addr2line::Context` structures are pretty expensive to create. Its + // cost is expected to be amortized by subsequent `locate` queries, which + // leverage the structures built when constructing `addr2line::Context`s to + // get nice speedups. If we didn't have this cache, that amortization would + // never happen, and symbolicating backtraces would be ssssllllooooowwww. + static mut MAPPINGS_CACHE: Option<Cache> = None; + + unsafe { + // FIXME: https://github.com/rust-lang/backtrace-rs/issues/678 + #[allow(static_mut_refs)] + f(MAPPINGS_CACHE.get_or_insert_with(Cache::new)) + } + } + + fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, *const u8)> { + self.libraries + .iter() + .enumerate() + .filter_map(|(i, lib)| { + // First up, test if this `lib` has any segment containing the + // `addr` (handling relocation). If this check passes then we + // can continue below and actually translate the address. + // + // Note that we're using `wrapping_add` here to avoid overflow + // checks. It's been seen in the wild that the SVMA + bias + // computation overflows. It seems a bit odd that would happen + // but there's not a huge amount we can do about it other than + // probably just ignore those segments since they're likely + // pointing off into space. This originally came up in + // rust-lang/backtrace-rs#329. + if !lib.segments.iter().any(|s| { + let svma = s.stated_virtual_memory_address; + let start = svma.wrapping_add(lib.bias); + let end = start.wrapping_add(s.len); + let address = addr as usize; + start <= address && address < end + }) { + return None; + } + + // Now that we know `lib` contains `addr`, we can offset with + // the bias to find the stated virtual memory address. + let svma = (addr as usize).wrapping_sub(lib.bias); + Some((i, svma as *const u8)) + }) + .next() + } + + fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<(&'a mut Context<'a>, &'a Stash)> { + let cache_idx = self.mappings.iter().position(|(lib_id, _)| *lib_id == lib); + + let cache_entry = if let Some(idx) = cache_idx { + self.mappings.move_to_front(idx) + } else { + // When the mapping is not in the cache, create a new mapping and insert it, + // which will also evict the oldest entry. + create_mapping(&self.libraries[lib]) + .and_then(|mapping| self.mappings.push_front((lib, mapping))) + }; + + let (_, mapping) = cache_entry?; + let cx: &'a mut Context<'static> = &mut mapping.cx; + let stash: &'a Stash = &mapping.stash; + // don't leak the `'static` lifetime, make sure it's scoped to just + // ourselves + Some(( + unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) }, + stash, + )) + } +} + +pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { + let addr = what.address_or_ip(); + let mut call = |sym: Symbol<'_>| { + // Extend the lifetime of `sym` to `'static` since we are unfortunately + // required to here, but it's only ever going out as a reference so no + // reference to it should be persisted beyond this frame anyway. + // SAFETY: praying the above is correct + let sym = unsafe { mem::transmute::<Symbol<'_>, Symbol<'static>>(sym) }; + (cb)(&super::Symbol { inner: sym }); + }; + + unsafe { + Cache::with_global(|cache| { + let (lib, addr) = match cache.avma_to_svma(addr.cast_const().cast::<u8>()) { + Some(pair) => pair, + None => return, + }; + + // Finally, get a cached mapping or create a new mapping for this file, and + // evaluate the DWARF info to find the file/line/name for this address. + let (cx, stash) = match cache.mapping_for_lib(lib) { + Some((cx, stash)) => (cx, stash), + None => return, + }; + let mut any_frames = false; + if let Ok(mut frames) = cx.find_frames(stash, addr as u64) { + while let Ok(Some(frame)) = frames.next() { + any_frames = true; + let name = match frame.function { + Some(f) => Some(f.name.slice()), + None => cx.object.search_symtab(addr as u64), + }; + call(Symbol::Frame { + addr: addr as *mut c_void, + location: frame.location, + name, + }); + } + } + if !any_frames { + if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) { + if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) { + while let Ok(Some(frame)) = frames.next() { + any_frames = true; + call(Symbol::Frame { + addr: addr as *mut c_void, + location: frame.location, + name: frame.function.map(|f| f.name.slice()), + }); + } + } + } + } + if !any_frames { + if let Some(name) = cx.object.search_symtab(addr as u64) { + call(Symbol::Symtab { name }); + } + } + }); + } +} + +pub enum Symbol<'a> { + /// We were able to locate frame information for this symbol, and + /// `addr2line`'s frame internally has all the nitty gritty details. + Frame { + addr: *mut c_void, + location: Option<addr2line::Location<'a>>, + name: Option<&'a [u8]>, + }, + /// Couldn't find debug information, but we found it in the symbol table of + /// the elf executable. + Symtab { name: &'a [u8] }, +} + +impl Symbol<'_> { + pub fn name(&self) -> Option<SymbolName<'_>> { + match self { + Symbol::Frame { name, .. } => { + let name = name.as_ref()?; + Some(SymbolName::new(name)) + } + Symbol::Symtab { name, .. } => Some(SymbolName::new(name)), + } + } + + pub fn addr(&self) -> Option<*mut c_void> { + match self { + Symbol::Frame { addr, .. } => Some(*addr), + Symbol::Symtab { .. } => None, + } + } + + pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> { + match self { + Symbol::Frame { location, .. } => { + let file = location.as_ref()?.file?; + Some(BytesOrWideString::Bytes(file.as_bytes())) + } + Symbol::Symtab { .. } => None, + } + } + + pub fn filename(&self) -> Option<&Path> { + match self { + Symbol::Frame { location, .. } => { + let file = location.as_ref()?.file?; + Some(Path::new(file)) + } + Symbol::Symtab { .. } => None, + } + } + + pub fn lineno(&self) -> Option<u32> { + match self { + Symbol::Frame { location, .. } => location.as_ref()?.line, + Symbol::Symtab { .. } => None, + } + } + + pub fn colno(&self) -> Option<u32> { + match self { + Symbol::Frame { location, .. } => location.as_ref()?.column, + Symbol::Symtab { .. } => None, + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/coff.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/coff.rs new file mode 100644 index 0000000..031afea --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/coff.rs
@@ -0,0 +1,116 @@ +use super::mystd::path::Path; +use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash}; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::convert::TryFrom; +use object::pe::{ImageDosHeader, ImageSymbol}; +use object::read::coff::ImageSymbol as _; +use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable}; +use object::read::StringTable; +use object::LittleEndian as LE; + +#[cfg(target_pointer_width = "32")] +type Pe = object::pe::ImageNtHeaders32; +#[cfg(target_pointer_width = "64")] +type Pe = object::pe::ImageNtHeaders64; + +impl Mapping { + pub fn new(path: &Path) -> Option<Mapping> { + let map = super::mmap(path)?; + Mapping::mk(map, |data, stash| { + Context::new(stash, Object::parse(data)?, None, None) + }) + } +} + +pub struct Object<'a> { + data: &'a [u8], + sections: SectionTable<'a>, + symbols: Vec<(usize, &'a ImageSymbol)>, + strings: StringTable<'a>, +} + +pub fn get_image_base(data: &[u8]) -> Option<usize> { + let dos_header = ImageDosHeader::parse(data).ok()?; + let mut offset = dos_header.nt_headers_offset().into(); + let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?; + usize::try_from(nt_headers.optional_header().image_base()).ok() +} + +impl<'a> Object<'a> { + fn parse(data: &'a [u8]) -> Option<Object<'a>> { + let dos_header = ImageDosHeader::parse(data).ok()?; + let mut offset = dos_header.nt_headers_offset().into(); + let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?; + let sections = nt_headers.sections(data, offset).ok()?; + let symtab = nt_headers.symbols(data).ok()?; + let strings = symtab.strings(); + let image_base = usize::try_from(nt_headers.optional_header().image_base()).ok()?; + + // Collect all the symbols into a local vector which is sorted + // by address and contains enough data to learn about the symbol + // name. Note that we only look at function symbols and also + // note that the sections are 1-indexed because the zero section + // is special (apparently). + let mut symbols = Vec::new(); + for (_, sym) in symtab.iter() { + if sym.derived_type() != object::pe::IMAGE_SYM_DTYPE_FUNCTION { + continue; + } + let Some(section_index) = sym.section() else { + continue; + }; + let addr = usize::try_from(sym.value.get(LE)).ok()?; + let section = sections.section(section_index).ok()?; + let va = usize::try_from(section.virtual_address.get(LE)).ok()?; + symbols.push((addr + va + image_base, sym)); + } + symbols.sort_unstable_by_key(|x| x.0); + Some(Object { + data, + sections, + strings, + symbols, + }) + } + + pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> { + Some( + self.sections + .section_by_name(self.strings, name.as_bytes())? + .1 + .pe_data(self.data) + .ok()?, + ) + } + + pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> { + // Note that unlike other formats COFF doesn't embed the size of + // each symbol. As a last ditch effort search for the *closest* + // symbol to a particular address and return that one. This gets + // really wonky once symbols start getting removed because the + // symbols returned here can be totally incorrect, but we have + // no idea of knowing how to detect that. + let addr = usize::try_from(addr).ok()?; + let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) { + Ok(i) => i, + // typically `addr` isn't in the array, but `i` is where + // we'd insert it, so the previous position must be the + // greatest less than `addr` + Err(i) => i.checked_sub(1)?, + }; + self.symbols[i].1.name(self.strings).ok() + } + + pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> { + None + } +} + +pub(super) fn handle_split_dwarf<'data>( + _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, + _stash: &'data Stash, + _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { + None +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/elf.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/elf.rs new file mode 100644 index 0000000..e32fbad --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/elf.rs
@@ -0,0 +1,567 @@ +#![allow(clippy::useless_conversion)] + +use super::mystd::ffi::OsStr; +use super::mystd::fs; +use super::mystd::os::unix::ffi::OsStrExt; +use super::mystd::path::{Path, PathBuf}; +use super::Either; +use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash}; +use alloc::string::String; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::convert::{TryFrom, TryInto}; +use core::str; +#[cfg(feature = "ruzstd")] +use object::elf::ELFCOMPRESS_ZSTD; +use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED}; +use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym}; +use object::read::StringTable; +use object::{BigEndian, Bytes, NativeEndian}; + +#[cfg(target_pointer_width = "32")] +type Elf = object::elf::FileHeader32<NativeEndian>; +#[cfg(target_pointer_width = "64")] +type Elf = object::elf::FileHeader64<NativeEndian>; + +impl Mapping { + pub fn new(path: &Path) -> Option<Mapping> { + let map = super::mmap(path)?; + Mapping::mk_or_other(map, |map, stash| { + let object = Object::parse(map)?; + + // Try to locate an external debug file using the build ID. + if let Some(path_debug) = object.build_id().and_then(locate_build_id) { + if let Some(mapping) = Mapping::new_debug(path, path_debug, None) { + return Some(Either::A(mapping)); + } + } + + // Try to locate an external debug file using the GNU debug link section. + if let Some((path_debug, crc)) = object.gnu_debuglink_path(path) { + if let Some(mapping) = Mapping::new_debug(path, path_debug, Some(crc)) { + return Some(Either::A(mapping)); + } + } + + let dwp = Mapping::load_dwarf_package(path, stash); + + Context::new(stash, object, None, dwp).map(Either::B) + }) + } + + /// On Android, shared objects can be loaded directly from a ZIP archive + /// (see: [`super::Library::zip_offset`]). + /// + /// If `zip_offset` is not None, we interpret the `path` as an + /// "embedded" library path, and the value of `zip_offset` tells us where + /// in the ZIP archive the library data starts. + /// + /// We expect `zip_offset` to be page-aligned because the dynamic linker + /// requires this. Otherwise, loading the embedded library will fail. + /// + /// If we fail to load an embedded library for any reason, we fallback to + /// interpreting the path as a literal file on disk (same as calling [`Self::new`]). + #[cfg(target_os = "android")] + pub fn new_android(path: &Path, zip_offset: Option<u64>) -> Option<Mapping> { + fn map_embedded_library(path: &Path, zip_offset: u64) -> Option<Mapping> { + // get path of ZIP archive (delimited by `!/`) + let zip_path = Path::new(super::extract_zip_path_android(path.as_os_str())?); + + let file = fs::File::open(zip_path).ok()?; + let len = file.metadata().ok()?.len(); + + // NOTE: we map the remainder of the entire archive instead of just the library so we don't have to determine its length + // NOTE: mmap will fail if `zip_offset` is not page-aligned + let map = unsafe { + super::mmap::Mmap::map(&file, usize::try_from(len - zip_offset).ok()?, zip_offset) + }?; + + Mapping::mk(map, |map, stash| { + Context::new(stash, Object::parse(&map)?, None, None) + }) + } + + // if ZIP offset is given, try mapping as a ZIP-embedded library + // otherwise, fallback to mapping as a literal filepath + if let Some(zip_offset) = zip_offset { + map_embedded_library(path, zip_offset).or_else(|| Self::new(path)) + } else { + Self::new(path) + } + } + + /// Load debuginfo from an external debug file. + fn new_debug(original_path: &Path, path: PathBuf, crc: Option<u32>) -> Option<Mapping> { + let map = super::mmap(&path)?; + Mapping::mk(map, |map, stash| { + let object = Object::parse(map)?; + + if let Some(_crc) = crc { + // TODO: check crc + } + + // Try to locate a supplementary object file. + let mut sup = None; + if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) { + if let Some(map_sup) = super::mmap(&path_sup) { + let map_sup = stash.cache_mmap(map_sup); + if let Some(sup_) = Object::parse(map_sup) { + if sup_.build_id() == Some(build_id_sup) { + sup = Some(sup_); + } + } + } + } + + let dwp = Mapping::load_dwarf_package(original_path, stash); + + Context::new(stash, object, sup, dwp) + }) + } + + /// Try to locate a DWARF package file. + fn load_dwarf_package<'data>(path: &Path, stash: &'data Stash) -> Option<Object<'data>> { + let mut path_dwp = path.to_path_buf(); + let dwp_extension = path + .extension() + .map(|previous_extension| { + let mut previous_extension = previous_extension.to_os_string(); + previous_extension.push(".dwp"); + previous_extension + }) + .unwrap_or_else(|| "dwp".into()); + path_dwp.set_extension(dwp_extension); + if let Some(map_dwp) = super::mmap(&path_dwp) { + let map_dwp = stash.cache_mmap(map_dwp); + if let Some(dwp_) = Object::parse(map_dwp) { + return Some(dwp_); + } + } + + None + } +} + +struct ParsedSym { + address: u64, + size: u64, + name: u32, +} + +pub struct Object<'a> { + /// Zero-sized type representing the native endianness. + /// + /// We could use a literal instead, but this helps ensure correctness. + endian: NativeEndian, + /// The entire file data. + data: &'a [u8], + sections: SectionTable<'a, Elf>, + strings: StringTable<'a>, + /// List of pre-parsed and sorted symbols by base address. + syms: Vec<ParsedSym>, +} + +impl<'a> Object<'a> { + fn parse(data: &'a [u8]) -> Option<Object<'a>> { + let elf = Elf::parse(data).ok()?; + let endian = elf.endian().ok()?; + let sections = elf.sections(endian, data).ok()?; + let mut syms = sections + .symbols(endian, data, object::elf::SHT_SYMTAB) + .ok()?; + if syms.is_empty() { + syms = sections + .symbols(endian, data, object::elf::SHT_DYNSYM) + .ok()?; + } + let strings = syms.strings(); + + let mut syms = syms + .iter() + // Only look at function/object symbols. This mirrors what + // libbacktrace does and in general we're only symbolicating + // function addresses in theory. Object symbols correspond + // to data, and maybe someone's crazy enough to have a + // function go into static data? + .filter(|sym| { + let st_type = sym.st_type(); + st_type == object::elf::STT_FUNC || st_type == object::elf::STT_OBJECT + }) + // skip anything that's in an undefined section header, + // since it means it's an imported function and we're only + // symbolicating with locally defined functions. + .filter(|sym| sym.st_shndx(endian) != object::elf::SHN_UNDEF) + .map(|sym| { + let address = sym.st_value(endian).into(); + let size = sym.st_size(endian).into(); + let name = sym.st_name(endian); + ParsedSym { + address, + size, + name, + } + }) + .collect::<Vec<_>>(); + syms.sort_unstable_by_key(|s| s.address); + Some(Object { + endian, + data, + sections, + strings, + syms, + }) + } + + pub fn section(&self, stash: &'a Stash, name: &str) -> Option<&'a [u8]> { + if let Some(section) = self.section_header(name) { + let mut data = Bytes(section.data(self.endian, self.data).ok()?); + + // Check for DWARF-standard (gABI) compression, i.e., as generated + // by ld's `--compress-debug-sections=zlib-gabi` and + // `--compress-debug-sections=zstd` flags. + let flags: u64 = section.sh_flags(self.endian).into(); + if (flags & u64::from(SHF_COMPRESSED)) == 0 { + // Not compressed. + return Some(data.0); + } + + let header = data.read::<<Elf as FileHeader>::CompressionHeader>().ok()?; + match header.ch_type(self.endian) { + ELFCOMPRESS_ZLIB => { + let size = usize::try_from(header.ch_size(self.endian)).ok()?; + let buf = stash.allocate(size); + decompress_zlib(data.0, buf)?; + return Some(buf); + } + #[cfg(feature = "ruzstd")] + ELFCOMPRESS_ZSTD => { + let size = usize::try_from(header.ch_size(self.endian)).ok()?; + let buf = stash.allocate(size); + decompress_zstd(data.0, buf)?; + return Some(buf); + } + _ => return None, // Unknown compression type. + } + } + + // Check for the nonstandard GNU compression format, i.e., as generated + // by ld's `--compress-debug-sections=zlib-gnu` flag. This means that if + // we're actually asking for `.debug_info` then we need to look up a + // section named `.zdebug_info`. + if !name.starts_with(".debug_") { + return None; + } + let debug_name = name[7..].as_bytes(); + let compressed_section = self + .sections + .iter() + .filter_map(|header| { + let name = self.sections.section_name(self.endian, header).ok()?; + if name.starts_with(b".zdebug_") && &name[8..] == debug_name { + Some(header) + } else { + None + } + }) + .next()?; + let mut data = Bytes(compressed_section.data(self.endian, self.data).ok()?); + if data.read_bytes(8).ok()?.0 != b"ZLIB\0\0\0\0" { + return None; + } + let size = usize::try_from(data.read::<object::U32Bytes<_>>().ok()?.get(BigEndian)).ok()?; + let buf = stash.allocate(size); + decompress_zlib(data.0, buf)?; + Some(buf) + } + + fn section_header(&self, name: &str) -> Option<&<Elf as FileHeader>::SectionHeader> { + self.sections + .section_by_name(self.endian, name.as_bytes()) + .map(|(_index, section)| section) + } + + pub fn search_symtab(&self, addr: u64) -> Option<&[u8]> { + // Same sort of binary search as Windows above + let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) { + Ok(i) => i, + Err(i) => i.checked_sub(1)?, + }; + let sym = self.syms.get(i)?; + if sym.address <= addr && addr <= sym.address + sym.size { + self.strings.get(sym.name).ok() + } else { + None + } + } + + pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> { + None + } + + fn build_id(&self) -> Option<&'a [u8]> { + for section in self.sections.iter() { + if let Ok(Some(mut notes)) = section.notes(self.endian, self.data) { + while let Ok(Some(note)) = notes.next() { + if note.name() == ELF_NOTE_GNU && note.n_type(self.endian) == NT_GNU_BUILD_ID { + return Some(note.desc()); + } + } + } + } + None + } + + // The contents of the ".gnu_debuglink" section is documented at: + // https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html + fn gnu_debuglink_path(&self, path: &Path) -> Option<(PathBuf, u32)> { + let section = self.section_header(".gnu_debuglink")?; + let data = section.data(self.endian, self.data).ok()?; + let len = data.iter().position(|x| *x == 0)?; + let filename = OsStr::from_bytes(&data[..len]); + let offset = (len + 1 + 3) & !3; + let crc_bytes = data + .get(offset..offset + 4) + .and_then(|bytes| bytes.try_into().ok())?; + let crc = u32::from_ne_bytes(crc_bytes); + let path_debug = locate_debuglink(path, filename)?; + Some((path_debug, crc)) + } + + // The format of the ".gnu_debugaltlink" section is based on gdb. + fn gnu_debugaltlink_path(&self, path: &Path) -> Option<(PathBuf, &'a [u8])> { + let section = self.section_header(".gnu_debugaltlink")?; + let data = section.data(self.endian, self.data).ok()?; + let len = data.iter().position(|x| *x == 0)?; + let filename = OsStr::from_bytes(&data[..len]); + let build_id = &data[len + 1..]; + let path_sup = locate_debugaltlink(path, filename, build_id)?; + Some((path_sup, build_id)) + } +} + +fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> { + use miniz_oxide::inflate::core::inflate_flags::{ + TINFL_FLAG_PARSE_ZLIB_HEADER, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF, + }; + use miniz_oxide::inflate::core::{decompress, DecompressorOxide}; + use miniz_oxide::inflate::TINFLStatus; + + let (status, in_read, out_read) = decompress( + &mut DecompressorOxide::new(), + input, + output, + 0, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER, + ); + if status == TINFLStatus::Done && in_read == input.len() && out_read == output.len() { + Some(()) + } else { + None + } +} + +#[cfg(feature = "ruzstd")] +fn decompress_zstd(mut input: &[u8], mut output: &mut [u8]) -> Option<()> { + use ruzstd::decoding::errors::{FrameDecoderError, ReadFrameHeaderError}; + use ruzstd::io::Read; + + while !input.is_empty() { + let mut decoder = match ruzstd::decoding::StreamingDecoder::new(&mut input) { + Ok(decoder) => decoder, + Err(FrameDecoderError::ReadFrameHeaderError(ReadFrameHeaderError::SkipFrame { + length, + .. + })) => { + input = &input.get(length as usize..)?; + continue; + } + Err(_) => return None, + }; + loop { + let bytes_written = decoder.read(output).ok()?; + if bytes_written == 0 { + break; + } + output = &mut output[bytes_written..]; + } + } + + if !output.is_empty() { + // Lengths didn't match, something is wrong. + return None; + } + + Some(()) +} + +const DEBUG_PATH: &str = "/usr/lib/debug"; + +fn debug_path_exists() -> bool { + cfg_if::cfg_if! { + if #[cfg(any(target_os = "freebsd", target_os = "hurd", target_os = "linux"))] { + use core::sync::atomic::{AtomicU8, Ordering}; + static DEBUG_PATH_EXISTS: AtomicU8 = AtomicU8::new(0); + + let mut exists = DEBUG_PATH_EXISTS.load(Ordering::Relaxed); + if exists == 0 { + exists = if Path::new(DEBUG_PATH).is_dir() { + 1 + } else { + 2 + }; + DEBUG_PATH_EXISTS.store(exists, Ordering::Relaxed); + } + exists == 1 + } else { + false + } + } +} + +/// Locate a debug file based on its build ID. +/// +/// The format of build id paths is documented at: +/// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html +fn locate_build_id(build_id: &[u8]) -> Option<PathBuf> { + const BUILD_ID_PATH: &str = "/usr/lib/debug/.build-id/"; + const BUILD_ID_SUFFIX: &str = ".debug"; + + if build_id.len() < 2 { + return None; + } + + if !debug_path_exists() { + return None; + } + + let mut path = + String::with_capacity(BUILD_ID_PATH.len() + BUILD_ID_SUFFIX.len() + build_id.len() * 2 + 1); + path.push_str(BUILD_ID_PATH); + path.push(char::from_digit((build_id[0] >> 4) as u32, 16)?); + path.push(char::from_digit((build_id[0] & 0xf) as u32, 16)?); + path.push('/'); + for byte in &build_id[1..] { + path.push(char::from_digit((byte >> 4) as u32, 16)?); + path.push(char::from_digit((byte & 0xf) as u32, 16)?); + } + path.push_str(BUILD_ID_SUFFIX); + Some(PathBuf::from(path)) +} + +/// Locate a file specified in a `.gnu_debuglink` section. +/// +/// `path` is the file containing the section. +/// `filename` is from the contents of the section. +/// +/// Search order is based on gdb, documented at: +/// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html +/// +/// gdb also allows the user to customize the debug search path, but we don't. +/// +/// gdb also supports debuginfod, but we don't yet. +fn locate_debuglink(path: &Path, filename: &OsStr) -> Option<PathBuf> { + let path = fs::canonicalize(path).ok()?; + let parent = path.parent()?; + let mut f = + PathBuf::with_capacity(DEBUG_PATH.len() + parent.as_os_str().len() + filename.len() + 2); + let filename = Path::new(filename); + + // Try "/parent/filename" if it differs from "path" + f.push(parent); + f.push(filename); + if f != path && f.is_file() { + return Some(f); + } + + // Try "/parent/.debug/filename" + f.clear(); + f.push(parent); + f.push(".debug"); + f.push(filename); + if f.is_file() { + return Some(f); + } + + if debug_path_exists() { + // Try "/usr/lib/debug/parent/filename" + f.clear(); + f.push(DEBUG_PATH); + f.push(parent.strip_prefix("/").unwrap()); + f.push(filename); + if f.is_file() { + return Some(f); + } + } + + None +} + +/// Locate a file specified in a `.gnu_debugaltlink` section. +/// +/// `path` is the file containing the section. +/// `filename` and `build_id` are the contents of the section. +/// +/// Search order is based on gdb: +/// - filename, which is either absolute or relative to `path` +/// - the build ID path under `BUILD_ID_PATH` +/// +/// gdb also allows the user to customize the debug search path, but we don't. +/// +/// gdb also supports debuginfod, but we don't yet. +fn locate_debugaltlink(path: &Path, filename: &OsStr, build_id: &[u8]) -> Option<PathBuf> { + let filename = Path::new(filename); + if filename.is_absolute() { + if filename.is_file() { + return Some(filename.into()); + } + } else { + let path = fs::canonicalize(path).ok()?; + let parent = path.parent()?; + let mut f = PathBuf::from(parent); + f.push(filename); + if f.is_file() { + return Some(f); + } + } + + locate_build_id(build_id) +} + +pub(super) fn handle_split_dwarf<'data>( + package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, + stash: &'data Stash, + load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { + if let Some(dwp) = package.as_ref() { + if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) { + return Some(Arc::new(cu)); + } + } + + let mut path = PathBuf::new(); + if let Some(p) = load.comp_dir.as_ref() { + path.push(OsStr::from_bytes(&p)); + } + + path.push(OsStr::from_bytes(&load.path.as_ref()?)); + + if let Some(map_dwo) = super::mmap(&path) { + let map_dwo = stash.cache_mmap(map_dwo); + if let Some(dwo) = Object::parse(map_dwo) { + return gimli::Dwarf::load(|id| -> Result<_, ()> { + let data = id + .dwo_name() + .and_then(|name| dwo.section(stash, name)) + .unwrap_or(&[]); + Ok(EndianSlice::new(data, Endian)) + }) + .ok() + .map(|mut dwo_dwarf| { + dwo_dwarf.make_dwo(&load.parent); + Arc::new(dwo_dwarf) + }); + } + } + + None +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_aix.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_aix.rs new file mode 100644 index 0000000..01270a7 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_aix.rs
@@ -0,0 +1,85 @@ +use super::mystd::env; +use super::mystd::ffi::OsStr; +use super::mystd::os::unix::prelude::*; +use super::xcoff; +use super::{Library, LibrarySegment}; +use alloc::borrow::ToOwned; +use alloc::vec; +use alloc::vec::Vec; +use core::ffi::{c_int, CStr}; +use core::mem; + +const EXE_IMAGE_BASE: u64 = 0x100000000; + +unsafe extern "C" { + #[link_name = "_Errno"] + fn errno_location() -> *mut c_int; +} + +fn errno() -> i32 { + unsafe { (*errno_location()) as i32 } +} + +/// On AIX, we use `loadquery` with `L_GETINFO` flag to query libraries mmapped. +/// See https://www.ibm.com/docs/en/aix/7.2?topic=l-loadquery-subroutine for +/// detailed information of `loadquery`. +pub(super) fn native_libraries() -> Vec<Library> { + let mut ret = Vec::new(); + unsafe { + let mut buffer = vec![mem::zeroed::<libc::ld_info>(); 64]; + loop { + if libc::loadquery( + libc::L_GETINFO, + buffer.as_mut_ptr().cast::<libc::c_char>(), + (mem::size_of::<libc::ld_info>() * buffer.len()) as u32, + ) != -1 + { + break; + } else { + match errno() { + libc::ENOMEM => { + buffer.resize(buffer.len() * 2, mem::zeroed::<libc::ld_info>()); + } + _ => { + // If other error occurs, return empty libraries. + return Vec::new(); + } + } + } + } + let mut current = buffer.as_mut_ptr(); + loop { + let text_base = (*current).ldinfo_textorg as usize; + let filename_ptr: *const libc::c_char = &(*current).ldinfo_filename[0]; + let bytes = CStr::from_ptr(filename_ptr).to_bytes(); + let member_name_ptr = filename_ptr.offset((bytes.len() + 1) as isize); + let mut filename = OsStr::from_bytes(bytes).to_owned(); + if text_base == EXE_IMAGE_BASE as usize { + if let Ok(exe) = env::current_exe() { + filename = exe.into_os_string(); + } + } + let bytes = CStr::from_ptr(member_name_ptr).to_bytes(); + let member_name = OsStr::from_bytes(bytes).to_owned(); + if let Some(image) = xcoff::parse_image(filename.as_ref(), &member_name) { + ret.push(Library { + name: filename, + member_name, + segments: vec![LibrarySegment { + stated_virtual_memory_address: image.base as usize, + len: image.size, + }], + bias: (text_base + image.offset).wrapping_sub(image.base as usize), + }); + } + if (*current).ldinfo_next == 0 { + break; + } + current = current + .cast::<libc::c_char>() + .offset((*current).ldinfo_next as isize) + .cast::<libc::ld_info>(); + } + } + return ret; +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_dl_iterate_phdr.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_dl_iterate_phdr.rs new file mode 100644 index 0000000..d52819f --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_dl_iterate_phdr.rs
@@ -0,0 +1,122 @@ +// Other Unix (e.g. Linux) platforms use ELF as an object file format +// and typically implement an API called `dl_iterate_phdr` to load +// native libraries. + +use super::mystd::env; +use super::mystd::ffi::{OsStr, OsString}; +use super::mystd::os::unix::prelude::*; +use super::{parse_running_mmaps, Library, LibrarySegment}; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; +use core::ffi::CStr; +use core::slice; + +struct CallbackData { + libs: Vec<Library>, + maps: Option<Vec<parse_running_mmaps::MapsEntry>>, +} +pub(super) fn native_libraries() -> Vec<Library> { + let mut cb_data = CallbackData { + libs: Vec::new(), + #[cfg(not(target_os = "hurd"))] + maps: parse_running_mmaps::parse_maps().ok(), + #[cfg(target_os = "hurd")] + maps: None, + }; + unsafe { + libc::dl_iterate_phdr(Some(callback), core::ptr::addr_of_mut!(cb_data).cast()); + } + cb_data.libs +} + +fn infer_current_exe( + maps: &Option<Vec<parse_running_mmaps::MapsEntry>>, + base_addr: usize, +) -> OsString { + #[cfg(not(target_os = "hurd"))] + if let Some(entries) = maps { + let opt_path = entries + .iter() + .find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0) + .map(|e| e.pathname()) + .cloned(); + if let Some(path) = opt_path { + return path; + } + } + + env::current_exe().map(|e| e.into()).unwrap_or_default() +} + +/// # Safety +/// `info` must be a valid pointer. +/// `data` must be a valid pointer to `CallbackData`. +#[forbid(unsafe_op_in_unsafe_fn)] +unsafe extern "C" fn callback( + info: *mut libc::dl_phdr_info, + _size: libc::size_t, + data: *mut libc::c_void, +) -> libc::c_int { + // SAFETY: We are guaranteed these fields: + let dlpi_addr = unsafe { (*info).dlpi_addr }; + let dlpi_name = unsafe { (*info).dlpi_name }; + let dlpi_phdr = unsafe { (*info).dlpi_phdr }; + let dlpi_phnum = unsafe { (*info).dlpi_phnum }; + // SAFETY: We assured this. + let CallbackData { libs, maps } = unsafe { &mut *data.cast::<CallbackData>() }; + // most implementations give us the main program first + let is_main = libs.is_empty(); + // we may be statically linked, which means we are main and mostly one big blob of code + let is_static = dlpi_addr == 0; + // sometimes we get a null or 0-len CStr, based on libc's whims, but these mean the same thing + let no_given_name = dlpi_name.is_null() + // SAFETY: we just checked for null + || unsafe { *dlpi_name == 0 }; + let name = if is_static { + // don't try to look up our name from /proc/self/maps, it'll get silly + env::current_exe().unwrap_or_default().into_os_string() + } else if is_main && no_given_name { + infer_current_exe(&maps, dlpi_addr as usize) + } else { + // this fallback works even if we are main, because some platforms give the name anyways + if dlpi_name.is_null() { + OsString::new() + } else { + // SAFETY: we just checked for nullness + OsStr::from_bytes(unsafe { CStr::from_ptr(dlpi_name) }.to_bytes()).to_owned() + } + }; + #[cfg(target_os = "android")] + let zip_offset: Option<u64> = { + // only check for ZIP-embedded file if we have data from /proc/self/maps + maps.as_ref().and_then(|maps| { + // check if file is embedded within a ZIP archive by searching for `!/` + super::extract_zip_path_android(&name).and_then(|_| { + // find MapsEntry matching library's base address and get its file offset + maps.iter() + .find(|m| m.ip_matches(dlpi_addr as usize)) + .map(|m| m.offset()) + }) + }) + }; + let headers = if dlpi_phdr.is_null() || dlpi_phnum == 0 { + &[] + } else { + // SAFETY: We just checked for nullness or 0-len slices + unsafe { slice::from_raw_parts(dlpi_phdr, dlpi_phnum as usize) } + }; + libs.push(Library { + name, + #[cfg(target_os = "android")] + zip_offset, + segments: headers + .iter() + .map(|header| LibrarySegment { + len: header.p_memsz as usize, + stated_virtual_memory_address: header.p_vaddr as usize, + }) + .collect(), + bias: dlpi_addr as usize, + }); + 0 +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_haiku.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_haiku.rs new file mode 100644 index 0000000..ddfd6b4 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_haiku.rs
@@ -0,0 +1,50 @@ +// Haiku implements the image_info struct and the get_next_image_info() +// functions to iterate through the loaded executable images. The +// image_info struct contains a pointer to the start of the .text +// section within the virtual address space, as well as the size of +// that section. All the read-only segments of the ELF-binary are in +// that part of the address space. + +use super::mystd::ffi::OsStr; +use super::mystd::os::unix::prelude::*; +use super::{Library, LibrarySegment}; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; +use core::ffi::CStr; +use core::mem::MaybeUninit; + +pub(super) fn native_libraries() -> Vec<Library> { + let mut libraries: Vec<Library> = Vec::new(); + + unsafe { + let mut info = MaybeUninit::<libc::image_info>::zeroed(); + let mut cookie: i32 = 0; + // Load the first image to get a valid info struct + let mut status = + libc::get_next_image_info(libc::B_CURRENT_TEAM, &mut cookie, info.as_mut_ptr()); + if status != libc::B_OK { + return libraries; + } + let mut info = info.assume_init(); + + while status == libc::B_OK { + let mut segments = Vec::new(); + segments.push(LibrarySegment { + stated_virtual_memory_address: 0, + len: info.text_size as usize, + }); + + let bytes = CStr::from_ptr(info.name.as_ptr()).to_bytes(); + let name = OsStr::from_bytes(bytes).to_owned(); + libraries.push(Library { + name: name, + segments: segments, + bias: info.text as usize, + }); + + status = libc::get_next_image_info(libc::B_CURRENT_TEAM, &mut cookie, &mut info); + } + } + + libraries +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_illumos.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_illumos.rs new file mode 100644 index 0000000..025eb25 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_illumos.rs
@@ -0,0 +1,101 @@ +use super::mystd::ffi::OsStr; +use super::mystd::os::unix::prelude::*; +use super::{Library, LibrarySegment}; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; +use core::ffi::CStr; +use core::mem; +use object::NativeEndian; + +#[cfg(target_pointer_width = "64")] +use object::elf::{FileHeader64 as FileHeader, ProgramHeader64 as ProgramHeader}; + +type EHdr = FileHeader<NativeEndian>; +type PHdr = ProgramHeader<NativeEndian>; + +#[repr(C)] +struct LinkMap { + l_addr: libc::c_ulong, + l_name: *const libc::c_char, + l_ld: *const libc::c_void, + l_next: *const LinkMap, + l_prev: *const LinkMap, + l_refname: *const libc::c_char, +} + +const RTLD_SELF: *const libc::c_void = -3isize as *const libc::c_void; +const RTLD_DI_LINKMAP: libc::c_int = 2; + +unsafe extern "C" { + fn dlinfo( + handle: *const libc::c_void, + request: libc::c_int, + p: *mut libc::c_void, + ) -> libc::c_int; +} + +pub(super) fn native_libraries() -> Vec<Library> { + let mut libs = Vec::new(); + + // Request the current link map from the runtime linker: + let map = unsafe { + let mut map: *const LinkMap = mem::zeroed(); + if dlinfo( + RTLD_SELF, + RTLD_DI_LINKMAP, + core::ptr::addr_of_mut!(map).cast::<libc::c_void>(), + ) != 0 + { + return libs; + } + map + }; + + // Each entry in the link map represents a loaded object: + let mut l = map; + while !l.is_null() { + // Fetch the fully qualified path of the loaded object: + let bytes = unsafe { CStr::from_ptr((*l).l_name) }.to_bytes(); + let name = OsStr::from_bytes(bytes).to_owned(); + + // The base address of the object loaded into memory: + let addr = unsafe { (*l).l_addr }; + + // Use the ELF header for this object to locate the program + // header: + let e: *const EHdr = unsafe { (*l).l_addr as *const EHdr }; + let phoff = unsafe { (*e).e_phoff }.get(NativeEndian); + let phnum = unsafe { (*e).e_phnum }.get(NativeEndian); + let etype = unsafe { (*e).e_type }.get(NativeEndian); + + let phdr: *const PHdr = (addr + phoff) as *const PHdr; + let phdr = unsafe { core::slice::from_raw_parts(phdr, phnum as usize) }; + + libs.push(Library { + name, + segments: phdr + .iter() + .map(|p| { + let memsz = p.p_memsz.get(NativeEndian); + let vaddr = p.p_vaddr.get(NativeEndian); + LibrarySegment { + len: memsz as usize, + stated_virtual_memory_address: vaddr as usize, + } + }) + .collect(), + bias: if etype == object::elf::ET_EXEC { + // Program header addresses for the base executable are + // already absolute. + 0 + } else { + // Other addresses are relative to the object base. + addr as usize + }, + }); + + l = unsafe { (*l).l_next }; + } + + libs +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_libnx.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_libnx.rs new file mode 100644 index 0000000..7f28078 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_libnx.rs
@@ -0,0 +1,28 @@ +use super::{Library, LibrarySegment}; +use alloc::vec::Vec; + +// DevkitA64 doesn't natively support debug info, but the build system will +// place debug info at the path `romfs:/debug_info.elf`. +pub(super) fn native_libraries() -> Vec<Library> { + unsafe extern "C" { + static __start__: u8; + } + + let bias = core::ptr::addr_of!(__start__) as usize; + + let mut ret = Vec::new(); + let mut segments = Vec::new(); + segments.push(LibrarySegment { + stated_virtual_memory_address: 0, + len: usize::max_value() - bias, + }); + + let path = "romfs:/debug_info.elf"; + ret.push(Library { + name: path.into(), + segments, + bias, + }); + + ret +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_macos.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_macos.rs new file mode 100644 index 0000000..f65811b --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_macos.rs
@@ -0,0 +1,154 @@ +#![allow(deprecated)] + +use super::mystd::ffi::OsStr; +use super::mystd::os::unix::prelude::*; +use super::mystd::prelude::v1::*; +use super::{Library, LibrarySegment}; +use core::convert::TryInto; +use core::ffi::CStr; +use core::mem; + +// FIXME: replace with ptr::from_ref once MSRV is high enough +#[inline(always)] +#[must_use] +const fn ptr_from_ref<T: ?Sized>(r: &T) -> *const T { + r +} + +pub(super) fn native_libraries() -> Vec<Library> { + let mut ret = Vec::new(); + let images = unsafe { libc::_dyld_image_count() }; + for i in 0..images { + ret.extend(native_library(i)); + } + return ret; +} + +fn native_library(i: u32) -> Option<Library> { + use object::macho; + use object::read::macho::{MachHeader, Segment}; + use object::NativeEndian; + + // Fetch the name of this library which corresponds to the path of + // where to load it as well. + let name = unsafe { + let name = libc::_dyld_get_image_name(i); + if name.is_null() { + return None; + } + CStr::from_ptr(name) + }; + + // Load the image header of this library and delegate to `object` to + // parse all the load commands so we can figure out all the segments + // involved here. + let (mut load_commands, endian) = unsafe { + let header = libc::_dyld_get_image_header(i); + if header.is_null() { + return None; + } + match (*header).magic { + macho::MH_MAGIC => { + let endian = NativeEndian; + let header = &*header.cast::<macho::MachHeader32<NativeEndian>>(); + let data = core::slice::from_raw_parts( + ptr_from_ref(header).cast::<u8>(), + mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize, + ); + (header.load_commands(endian, data, 0).ok()?, endian) + } + macho::MH_MAGIC_64 => { + let endian = NativeEndian; + let header = &*header.cast::<macho::MachHeader64<NativeEndian>>(); + let data = core::slice::from_raw_parts( + ptr_from_ref(header).cast::<u8>(), + mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize, + ); + (header.load_commands(endian, data, 0).ok()?, endian) + } + _ => return None, + } + }; + + // Iterate over the segments and register known regions for segments + // that we find. Additionally record information bout text segments + // for processing later, see comments below. + let mut segments = Vec::new(); + let mut first_text = 0; + let mut text_fileoff_zero = false; + while let Some(cmd) = load_commands.next().ok()? { + if let Some((seg, _)) = cmd.segment_32().ok()? { + if seg.name() == b"__TEXT" { + first_text = segments.len(); + if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 { + text_fileoff_zero = true; + } + } + segments.push(LibrarySegment { + len: seg.vmsize(endian).try_into().ok()?, + stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?, + }); + } + if let Some((seg, _)) = cmd.segment_64().ok()? { + if seg.name() == b"__TEXT" { + first_text = segments.len(); + if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 { + text_fileoff_zero = true; + } + } + segments.push(LibrarySegment { + len: seg.vmsize(endian).try_into().ok()?, + stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?, + }); + } + } + + // Determine the "slide" for this library which ends up being the + // bias we use to figure out where in memory objects are loaded. + // This is a bit of a weird computation though and is the result of + // trying a few things in the wild and seeing what sticks. + // + // The general idea is that the `bias` plus a segment's + // `stated_virtual_memory_address` is going to be where in the + // actual address space the segment resides. The other thing we rely + // on though is that a real address minus the `bias` is the index to + // look up in the symbol table and debuginfo. + // + // It turns out, though, that for system loaded libraries these + // calculations are incorrect. For native executables, however, it + // appears correct. Lifting some logic from LLDB's source it has + // some special-casing for the first `__TEXT` section loaded from + // file offset 0 with a nonzero size. For whatever reason when this + // is present it appears to mean that the symbol table is relative + // to just the vmaddr slide for the library. If it's *not* present + // then the symbol table is relative to the vmaddr slide plus the + // segment's stated address. + // + // To handle this situation if we *don't* find a text section at + // file offset zero then we increase the bias by the first text + // sections's stated address and decrease all stated addresses by + // that amount as well. That way the symbol table is always appears + // relative to the library's bias amount. This appears to have the + // right results for symbolizing via the symbol table. + // + // Honestly I'm not entirely sure whether this is right or if + // there's something else that should indicate how to do this. For + // now though this seems to work well enough (?) and we should + // always be able to tweak this over time if necessary. + // + // For some more information see #318 + let mut slide = unsafe { libc::_dyld_get_image_vmaddr_slide(i) as usize }; + if !text_fileoff_zero { + let adjust = segments[first_text].stated_virtual_memory_address; + for segment in segments.iter_mut() { + segment.stated_virtual_memory_address -= adjust; + } + slide += adjust; + } + + Some(Library { + name: OsStr::from_bytes(name.to_bytes()).to_owned(), + segments, + bias: slide, + }) +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_windows.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_windows.rs new file mode 100644 index 0000000..1d9a74c --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/libs_windows.rs
@@ -0,0 +1,158 @@ +use super::super::super::windows_sys::*; +use super::mystd::ffi::OsString; +use super::{coff, mmap, Library, LibrarySegment}; +use alloc::vec; +use alloc::vec::Vec; +use core::mem; +use core::mem::MaybeUninit; + +// For loading native libraries on Windows, see some discussion on +// rust-lang/rust#71060 for the various strategies here. +pub(super) fn native_libraries() -> Vec<Library> { + let mut ret = Vec::new(); + unsafe { + add_loaded_images(&mut ret); + } + return ret; +} + +unsafe fn add_loaded_images(ret: &mut Vec<Library>) { + unsafe { + let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); + if snap == INVALID_HANDLE_VALUE { + return; + } + + // huge struct, probably should avoid manually initializing it even if we can + let mut me = MaybeUninit::<MODULEENTRY32W>::zeroed().assume_init(); + me.dwSize = mem::size_of_val(&me) as u32; + if Module32FirstW(snap, &mut me) == TRUE { + loop { + if let Some(lib) = load_library(&me) { + ret.push(lib); + } + + if Module32NextW(snap, &mut me) != TRUE { + break; + } + } + } + + CloseHandle(snap); + } +} + +// Safety: long_path should be null-terminated +#[cfg(target_os = "cygwin")] +unsafe fn get_posix_path(long_path: &[u16]) -> Option<OsString> { + use super::mystd::os::unix::ffi::OsStringExt; + + unsafe extern "C" { + // Doc: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html + // Src: https://github.com/cygwin/cygwin/blob/718a15ba50e0d01c79800bd658c2477f9a603540/winsup/cygwin/path.cc#L3902 + // Safety: + // * `what` should be `CCP_WIN_W_TO_POSIX` here + // * `from` is null-terminated UTF-16 path + // * `to` is buffer, the buffer size is `size`. + fn cygwin_conv_path( + what: libc::c_uint, + from: *const u16, + to: *mut u8, + size: libc::size_t, + ) -> libc::ssize_t; + } + const CCP_WIN_W_TO_POSIX: libc::c_uint = 3; + + // If `size` is 0, returns needed buffer size, including null terminator; + // or -1 if error. + // Safety: if `size` is 0, `to` is not used. + let name_len = unsafe { + cygwin_conv_path( + CCP_WIN_W_TO_POSIX, + long_path.as_ptr(), + core::ptr::null_mut(), + 0, + ) + }; + // Expect at least 1 for null terminator. + // It's not likely to return error here. + if name_len < 1 { + return None; + } + let name_len = name_len as usize; + let mut name_buffer = Vec::with_capacity(name_len); + // Safety: `name_buffer` is large enough. + let res = unsafe { + cygwin_conv_path( + CCP_WIN_W_TO_POSIX, + long_path.as_ptr(), + name_buffer.as_mut_ptr(), + name_len, + ) + }; + // It's not likely to return error here. + if res != 0 { + return None; + } + // Remove the null terminator. + unsafe { name_buffer.set_len(name_len - 1) }; + let name = OsString::from_vec(name_buffer); + Some(name) +} + +unsafe fn load_library(me: &MODULEENTRY32W) -> Option<Library> { + #[cfg(windows)] + let name = { + use super::mystd::os::windows::prelude::*; + let pos = me + .szExePath + .iter() + .position(|i| *i == 0) + .unwrap_or(me.szExePath.len()); + OsString::from_wide(&me.szExePath[..pos]) + }; + #[cfg(target_os = "cygwin")] + // Safety: the path with max length MAX_PATH always contains a null + // terminator. Don't slice it. + let name = unsafe { get_posix_path(&me.szExePath[..])? }; + + // MinGW libraries currently don't support ASLR + // (rust-lang/rust#16514), but DLLs can still be relocated around in + // the address space. It appears that addresses in debug info are + // all as-if this library was loaded at its "image base", which is a + // field in its COFF file headers. Since this is what debuginfo + // seems to list we parse the symbol table and store addresses as if + // the library was loaded at "image base" as well. + // + // The library may not be loaded at "image base", however. + // (presumably something else may be loaded there?) This is where + // the `bias` field comes into play, and we need to figure out the + // value of `bias` here. Unfortunately though it's not clear how to + // acquire this from a loaded module. What we do have, however, is + // the actual load address (`modBaseAddr`). + // + // As a bit of a cop-out for now we mmap the file, read the file + // header information, then drop the mmap. This is wasteful because + // we'll probably reopen the mmap later, but this should work well + // enough for now. + // + // Once we have the `image_base` (desired load location) and the + // `base_addr` (actual load location) we can fill in the `bias` + // (difference between the actual and desired) and then the stated + // address of each segment is the `image_base` since that's what the + // file says. + // + // For now it appears that unlike ELF/MachO we can make do with one + // segment per library, using `modBaseSize` as the whole size. + let mmap = mmap(name.as_ref())?; + let image_base = coff::get_image_base(&mmap)?; + let base_addr = me.modBaseAddr as usize; + Some(Library { + name, + bias: base_addr.wrapping_sub(image_base), + segments: vec![LibrarySegment { + stated_virtual_memory_address: image_base, + len: me.modBaseSize as usize, + }], + }) +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/lru.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/lru.rs new file mode 100644 index 0000000..b7cf5a5 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/lru.rs
@@ -0,0 +1,75 @@ +use core::mem::{self, MaybeUninit}; +use core::ptr; + +/// least-recently-used cache with static size +pub(crate) struct Lru<T, const N: usize> { + // SAFETY: len <= initialized values + len: usize, + arr: [MaybeUninit<T>; N], +} + +impl<T, const N: usize> Default for Lru<T, N> { + fn default() -> Self { + Lru { + len: 0, + arr: [const { MaybeUninit::uninit() }; N], + } + } +} + +impl<T, const N: usize> Lru<T, N> { + #[inline] + pub fn clear(&mut self) { + let len = self.len; + self.len = 0; + // SAFETY: we can't touch these values again due to setting self.len = 0 + unsafe { ptr::drop_in_place(ptr::addr_of_mut!(self.arr[0..len]) as *mut [T]) } + } + + #[inline] + pub fn iter(&self) -> impl Iterator<Item = &T> { + self.arr[0..self.len] + .iter() + // SAFETY: we only iterate initialized values due to our len invariant + .map(|init| unsafe { init.assume_init_ref() }) + } + + #[inline] + pub fn push_front(&mut self, value: T) -> Option<&mut T> { + if N == 0 { + return None; + } else if self.len == N { + self.len = N - 1; + // SAFETY: we maintain len invariant and bail on N == 0 + unsafe { ptr::drop_in_place(self.arr.as_mut_ptr().cast::<T>().add(N - 1)) }; + }; + let len_to_init = self.len + 1; + let mut last = MaybeUninit::new(value); + for elem in self.arr[0..len_to_init].iter_mut() { + // OPT(size): using `mem::swap` allows surprising size regressions + last = mem::replace(elem, last); + } + self.len = len_to_init; + + self.arr + .first_mut() + // SAFETY: we just pushed it + .map(|elem| unsafe { elem.assume_init_mut() }) + } + + #[inline] + pub fn move_to_front(&mut self, idx: usize) -> Option<&mut T> { + let elem = self.arr[0..self.len].get_mut(idx)?; + // SAFETY: we already bailed if the index is bad, so our slicing will be infallible, + // so it is permissible to allow the len invariant to decay, as we always restore it + let mut last = mem::replace(elem, MaybeUninit::uninit()); + for elem in self.arr[0..=idx].iter_mut() { + // OPT(size): using `mem::swap` allows surprising size regressions + last = mem::replace(elem, last); + } + self.arr + .first_mut() + // SAFETY: we have restored the len invariant + .map(|elem| unsafe { elem.assume_init_mut() }) + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/macho.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/macho.rs new file mode 100644 index 0000000..fcbe609 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/macho.rs
@@ -0,0 +1,318 @@ +use super::mystd::path::Path; +use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash}; +use alloc::boxed::Box; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::convert::TryInto; +use object::macho; +use object::read::macho::{MachHeader, Nlist, Section, Segment as _}; +use object::{Bytes, NativeEndian}; + +#[cfg(target_pointer_width = "32")] +type Mach = object::macho::MachHeader32<NativeEndian>; +#[cfg(target_pointer_width = "64")] +type Mach = object::macho::MachHeader64<NativeEndian>; +type MachSegment = <Mach as MachHeader>::Segment; +type MachSection = <Mach as MachHeader>::Section; +type MachNlist = <Mach as MachHeader>::Nlist; + +impl Mapping { + // The loading path for macOS is so different we just have a completely + // different implementation of the function here. On macOS we need to go + // probing the filesystem for a bunch of files. + pub fn new(path: &Path) -> Option<Mapping> { + // First up we need to load the unique UUID which is stored in the macho + // header of the file we're reading, specified at `path`. + let map = super::mmap(path)?; + let (macho, data) = find_header(&map)?; + let endian = macho.endian().ok()?; + let uuid = macho.uuid(endian, data, 0).ok()?; + + // Next we need to look for a `*.dSYM` file. For now we just probe the + // containing directory and look around for something that matches + // `*.dSYM`. Once it's found we root through the dwarf resources that it + // contains and try to find a macho file which has a matching UUID as + // the one of our own file. If we find a match that's the dwarf file we + // want to return. + if let Some(uuid) = uuid { + if let Some(parent) = path.parent() { + if let Some(mapping) = Mapping::load_dsym(parent, uuid) { + return Some(mapping); + } + } + } + + // Looks like nothing matched our UUID, so let's at least return our own + // file. This should have the symbol table for at least some + // symbolication purposes. + Mapping::mk(map, |data, stash| { + let (macho, data) = find_header(data)?; + let endian = macho.endian().ok()?; + let obj = Object::parse(macho, endian, data)?; + Context::new(stash, obj, None, None) + }) + } + + fn load_dsym(dir: &Path, uuid: [u8; 16]) -> Option<Mapping> { + for entry in dir.read_dir().ok()? { + let entry = entry.ok()?; + let filename = match entry.file_name().into_string() { + Ok(name) => name, + Err(_) => continue, + }; + if !filename.ends_with(".dSYM") { + continue; + } + let candidates = entry.path().join("Contents/Resources/DWARF"); + if let Some(mapping) = Mapping::try_dsym_candidate(&candidates, uuid) { + return Some(mapping); + } + } + None + } + + fn try_dsym_candidate(dir: &Path, uuid: [u8; 16]) -> Option<Mapping> { + // Look for files in the `DWARF` directory which have a matching uuid to + // the original object file. If we find one then we found the debug + // information. + for entry in dir.read_dir().ok()? { + let entry = entry.ok()?; + let map = super::mmap(&entry.path())?; + let candidate = Mapping::mk(map, |data, stash| { + let (macho, data) = find_header(data)?; + let endian = macho.endian().ok()?; + let entry_uuid = macho.uuid(endian, data, 0).ok()??; + if entry_uuid != uuid { + return None; + } + let obj = Object::parse(macho, endian, data)?; + Context::new(stash, obj, None, None) + }); + if let Some(candidate) = candidate { + return Some(candidate); + } + } + + None + } +} + +fn find_header(data: &'_ [u8]) -> Option<(&'_ Mach, &'_ [u8])> { + use object::endian::BigEndian; + + let desired_cpu = || { + if cfg!(target_arch = "x86") { + Some(macho::CPU_TYPE_X86) + } else if cfg!(target_arch = "x86_64") { + Some(macho::CPU_TYPE_X86_64) + } else if cfg!(target_arch = "arm") { + Some(macho::CPU_TYPE_ARM) + } else if cfg!(target_arch = "aarch64") { + Some(macho::CPU_TYPE_ARM64) + } else { + None + } + }; + + let mut data = Bytes(data); + match data + .clone() + .read::<object::endian::U32<NativeEndian>>() + .ok()? + .get(NativeEndian) + { + macho::MH_MAGIC_64 | macho::MH_CIGAM_64 | macho::MH_MAGIC | macho::MH_CIGAM => {} + + macho::FAT_MAGIC | macho::FAT_CIGAM => { + let mut header_data = data; + let endian = BigEndian; + let header = header_data.read::<macho::FatHeader>().ok()?; + let nfat = header.nfat_arch.get(endian); + let arch = (0..nfat) + .filter_map(|_| header_data.read::<macho::FatArch32>().ok()) + .find(|arch| desired_cpu() == Some(arch.cputype.get(endian)))?; + let offset = arch.offset.get(endian); + let size = arch.size.get(endian); + data = data + .read_bytes_at(offset.try_into().ok()?, size.try_into().ok()?) + .ok()?; + } + + macho::FAT_MAGIC_64 | macho::FAT_CIGAM_64 => { + let mut header_data = data; + let endian = BigEndian; + let header = header_data.read::<macho::FatHeader>().ok()?; + let nfat = header.nfat_arch.get(endian); + let arch = (0..nfat) + .filter_map(|_| header_data.read::<macho::FatArch64>().ok()) + .find(|arch| desired_cpu() == Some(arch.cputype.get(endian)))?; + let offset = arch.offset.get(endian); + let size = arch.size.get(endian); + data = data + .read_bytes_at(offset.try_into().ok()?, size.try_into().ok()?) + .ok()?; + } + + _ => return None, + } + + Mach::parse(data.0, 0).ok().map(|h| (h, data.0)) +} + +// This is used both for executables/libraries and source object files. +pub struct Object<'a> { + endian: NativeEndian, + data: &'a [u8], + dwarf: Option<&'a [MachSection]>, + syms: Vec<(&'a [u8], u64)>, + syms_sort_by_name: bool, + // Only set for executables/libraries, and not the source object files. + object_map: Option<object::ObjectMap<'a>>, + // The outer Option is for lazy loading, and the inner Option allows load errors to be cached. + object_mappings: Box<[Option<Option<Mapping>>]>, +} + +impl<'a> Object<'a> { + fn parse(mach: &'a Mach, endian: NativeEndian, data: &'a [u8]) -> Option<Object<'a>> { + let is_object = mach.filetype(endian) == object::macho::MH_OBJECT; + let mut dwarf = None; + let mut syms = Vec::new(); + let mut syms_sort_by_name = false; + let mut commands = mach.load_commands(endian, data, 0).ok()?; + let mut object_map = None; + let mut object_mappings = Vec::new(); + while let Ok(Some(command)) = commands.next() { + if let Some((segment, section_data)) = MachSegment::from_command(command).ok()? { + // Object files should have all sections in a single unnamed segment load command. + if segment.name() == b"__DWARF" || (is_object && segment.name() == b"") { + dwarf = segment.sections(endian, section_data).ok(); + } + } else if let Some(symtab) = command.symtab().ok()? { + let symbols = symtab.symbols::<Mach, _>(endian, data).ok()?; + syms = symbols + .iter() + .filter_map(|nlist: &MachNlist| { + let name = nlist.name(endian, symbols.strings()).ok()?; + if name.len() > 0 && nlist.is_definition() { + Some((name, u64::from(nlist.n_value(endian)))) + } else { + None + } + }) + .collect(); + if is_object { + // We never search object file symbols by address. + // Instead, we already know the symbol name from the executable, and we + // need to search by name to find the matching symbol in the object file. + syms.sort_unstable_by_key(|(name, _)| *name); + syms_sort_by_name = true; + } else { + syms.sort_unstable_by_key(|(_, addr)| *addr); + let map = symbols.object_map(endian); + object_mappings.resize_with(map.objects().len(), || None); + object_map = Some(map); + } + } + } + + Some(Object { + endian, + data, + dwarf, + syms, + syms_sort_by_name, + object_map, + object_mappings: object_mappings.into_boxed_slice(), + }) + } + + pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> { + let name = name.as_bytes(); + let dwarf = self.dwarf?; + let section = dwarf.into_iter().find(|section| { + let section_name = section.name(); + section_name == name || { + section_name.starts_with(b"__") + && name.starts_with(b".") + && §ion_name[2..] == &name[1..] + } + })?; + Some(section.data(self.endian, self.data).ok()?) + } + + pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> { + debug_assert!(!self.syms_sort_by_name); + let i = match self.syms.binary_search_by_key(&addr, |(_, addr)| *addr) { + Ok(i) => i, + Err(i) => i.checked_sub(1)?, + }; + let (sym, _addr) = self.syms.get(i)?; + Some(sym) + } + + /// Try to load a context for an object file. + /// + /// If dsymutil was not run, then the DWARF may be found in the source object files. + pub(super) fn search_object_map<'b>(&'b mut self, addr: u64) -> Option<(&'b Context<'b>, u64)> { + // `object_map` contains a map from addresses to symbols and object paths. + // Look up the address and get a mapping for the object. + let object_map = self.object_map.as_ref()?; + let symbol = object_map.get(addr)?; + let object_index = symbol.object_index(); + let mapping = self.object_mappings.get_mut(object_index)?; + if mapping.is_none() { + // No cached mapping, so create it. + *mapping = Some(object_mapping(object_map.objects().get(object_index)?)); + } + let cx: &'b Context<'static> = &mapping.as_ref()?.as_ref()?.cx; + // Don't leak the `'static` lifetime, make sure it's scoped to just ourselves. + let cx = unsafe { core::mem::transmute::<&'b Context<'static>, &'b Context<'b>>(cx) }; + + // We must translate the address in order to be able to look it up + // in the DWARF in the object file. + debug_assert!(cx.object.syms.is_empty() || cx.object.syms_sort_by_name); + let i = cx + .object + .syms + .binary_search_by_key(&symbol.name(), |(name, _)| *name) + .ok()?; + let object_symbol = cx.object.syms.get(i)?; + let object_addr = addr + .wrapping_sub(symbol.address()) + .wrapping_add(object_symbol.1); + Some((cx, object_addr)) + } +} + +fn object_mapping(file: &object::read::ObjectMapFile<'_>) -> Option<Mapping> { + use super::mystd::ffi::OsStr; + use super::mystd::os::unix::prelude::*; + + let map = super::mmap(Path::new(OsStr::from_bytes(file.path())))?; + let member_name = file.member(); + Mapping::mk(map, |data, stash| { + let data = match member_name { + Some(member_name) => { + let archive = object::read::archive::ArchiveFile::parse(data).ok()?; + let member = archive + .members() + .filter_map(Result::ok) + .find(|m| m.name() == member_name)?; + member.data(data).ok()? + } + None => data, + }; + let (macho, data) = find_header(data)?; + let endian = macho.endian().ok()?; + let obj = Object::parse(macho, endian, data)?; + Context::new(stash, obj, None, None) + }) +} + +pub(super) fn handle_split_dwarf<'data>( + _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, + _stash: &'data Stash, + _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { + None +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_fake.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_fake.rs new file mode 100644 index 0000000..71697fc --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_fake.rs
@@ -0,0 +1,27 @@ +use super::mystd::io::{Read, Seek, SeekFrom}; +use super::File; +use alloc::vec::Vec; +use core::ops::Deref; + +pub struct Mmap { + vec: Vec<u8>, +} + +impl Mmap { + pub unsafe fn map(mut file: &File, len: usize, offset: u64) -> Option<Mmap> { + let mut mmap = Mmap { + vec: Vec::with_capacity(len), + }; + file.seek(SeekFrom::Start(offset)); + file.read_to_end(&mut mmap.vec).ok()?; + Some(mmap) + } +} + +impl Deref for Mmap { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + &self.vec[..] + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_unix.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_unix.rs new file mode 100644 index 0000000..24ebeb3 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_unix.rs
@@ -0,0 +1,51 @@ +use super::mystd::fs::File; +use super::mystd::os::unix::prelude::*; +use core::ops::Deref; +use core::ptr; +use core::slice; + +#[cfg(not(all(target_os = "linux", target_env = "gnu")))] +use libc::mmap as mmap64; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use libc::mmap64; + +pub struct Mmap { + ptr: *mut libc::c_void, + len: usize, +} + +impl Mmap { + pub unsafe fn map(file: &File, len: usize, offset: u64) -> Option<Mmap> { + let ptr = unsafe { + mmap64( + ptr::null_mut(), + len, + libc::PROT_READ, + libc::MAP_PRIVATE, + file.as_raw_fd(), + offset.try_into().ok()?, + ) + }; + if ptr == libc::MAP_FAILED { + return None; + } + Some(Mmap { ptr, len }) + } +} + +impl Deref for Mmap { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) } + } +} + +impl Drop for Mmap { + fn drop(&mut self) { + unsafe { + let r = libc::munmap(self.ptr, self.len); + debug_assert_eq!(r, 0); + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_windows.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_windows.rs new file mode 100644 index 0000000..d3c0272 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/mmap_windows.rs
@@ -0,0 +1,67 @@ +use super::super::super::windows_sys::*; + +use super::mystd::fs::File; +use super::mystd::os::windows::prelude::*; +use core::ffi::c_void; +use core::ops::Deref; +use core::ptr; +use core::slice; + +pub struct Mmap { + // keep the file alive to prevent it from being deleted which would cause + // us to read bad data. + _file: File, + ptr: *mut c_void, + len: usize, +} + +impl Mmap { + pub unsafe fn map(file: &File, len: usize, offset: u64) -> Option<Mmap> { + unsafe { + let file = file.try_clone().ok()?; + let mapping = CreateFileMappingA( + file.as_raw_handle(), + ptr::null_mut(), + PAGE_READONLY, + 0, + 0, + ptr::null(), + ); + if mapping.is_null() { + return None; + } + let ptr = MapViewOfFile( + mapping, + FILE_MAP_READ, + (offset >> 32) as u32, + offset as u32, + len, + ); + CloseHandle(mapping); + if ptr.Value.is_null() { + return None; + } + Some(Mmap { + _file: file, + ptr: ptr.Value, + len, + }) + } + } +} +impl Deref for Mmap { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.ptr.cast_const().cast::<u8>(), self.len) } + } +} + +impl Drop for Mmap { + fn drop(&mut self) { + unsafe { + let r = UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { Value: self.ptr }); + debug_assert!(r != 0); + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/parse_running_mmaps_unix.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/parse_running_mmaps_unix.rs new file mode 100644 index 0000000..5803d5d --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/parse_running_mmaps_unix.rs
@@ -0,0 +1,304 @@ +// Note: This file is only currently used on targets that call out to the code +// in `mod libs_dl_iterate_phdr` (e.g. linux, freebsd, ...); it may be more +// general purpose, but it hasn't been tested elsewhere. + +use super::mystd::ffi::OsString; +use super::mystd::fs::File; +use super::mystd::io::Read; +use alloc::string::String; +use alloc::vec::Vec; +use core::str::FromStr; + +#[derive(PartialEq, Eq, Debug)] +pub(super) struct MapsEntry { + /// start (inclusive) and limit (exclusive) of address range. + address: (usize, usize), + /// The perms field are the permissions for the entry + /// + /// r = read + /// w = write + /// x = execute + /// s = shared + /// p = private (copy on write) + perms: [char; 4], + /// Offset into the file (or "whatever"). + offset: u64, + /// device (major, minor) + dev: (usize, usize), + /// inode on the device. 0 indicates that no inode is associated with the memory region (e.g. uninitalized data aka BSS). + inode: usize, + /// Usually the file backing the mapping. + /// + /// Note: The man page for proc includes a note about "coordination" by + /// using readelf to see the Offset field in ELF program headers. pnkfelix + /// is not yet sure if that is intended to be a comment on pathname, or what + /// form/purpose such coordination is meant to have. + /// + /// There are also some pseudo-paths: + /// "[stack]": The initial process's (aka main thread's) stack. + /// "[stack:<tid>]": a specific thread's stack. (This was only present for a limited range of Linux verisons; it was determined to be too expensive to provide.) + /// "[vdso]": Virtual dynamically linked shared object + /// "[heap]": The process's heap + /// + /// The pathname can be blank, which means it is an anonymous mapping + /// obtained via mmap. + /// + /// Newlines in pathname are replaced with an octal escape sequence. + /// + /// The pathname may have "(deleted)" appended onto it if the file-backed + /// path has been deleted. + /// + /// Note that modifications like the latter two indicated above imply that + /// in general the pathname may be ambiguous. (I.e. you cannot tell if the + /// denoted filename actually ended with the text "(deleted)", or if that + /// was added by the maps rendering. + pathname: OsString, +} + +pub(super) fn parse_maps() -> Result<Vec<MapsEntry>, &'static str> { + let mut v = Vec::new(); + let mut proc_self_maps = + File::open("/proc/self/maps").map_err(|_| "Couldn't open /proc/self/maps")?; + let mut buf = String::new(); + let _bytes_read = proc_self_maps + .read_to_string(&mut buf) + .map_err(|_| "Couldn't read /proc/self/maps")?; + for line in buf.lines() { + v.push(line.parse()?); + } + + Ok(v) +} + +impl MapsEntry { + pub(super) fn pathname(&self) -> &OsString { + &self.pathname + } + + pub(super) fn ip_matches(&self, ip: usize) -> bool { + self.address.0 <= ip && ip < self.address.1 + } + + #[cfg(target_os = "android")] + pub(super) fn offset(&self) -> u64 { + self.offset + } +} + +impl FromStr for MapsEntry { + type Err = &'static str; + + // Format: address perms offset dev inode pathname + // e.g.: "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]" + // e.g.: "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2" + // e.g.: "35b1a21000-35b1a22000 rw-p 00000000 00:00 0" + // + // Note that paths may contain spaces, so we can't use `str::split` for parsing (until + // Split::remainder is stabilized #77998). + fn from_str(s: &str) -> Result<Self, Self::Err> { + let (range_str, s) = s.trim_start().split_once(' ').unwrap_or((s, "")); + if range_str.is_empty() { + return Err("Couldn't find address"); + } + + let (perms_str, s) = s.trim_start().split_once(' ').unwrap_or((s, "")); + if perms_str.is_empty() { + return Err("Couldn't find permissions"); + } + + let (offset_str, s) = s.trim_start().split_once(' ').unwrap_or((s, "")); + if offset_str.is_empty() { + return Err("Couldn't find offset"); + } + + let (dev_str, s) = s.trim_start().split_once(' ').unwrap_or((s, "")); + if dev_str.is_empty() { + return Err("Couldn't find dev"); + } + + let (inode_str, s) = s.trim_start().split_once(' ').unwrap_or((s, "")); + if inode_str.is_empty() { + return Err("Couldn't find inode"); + } + + // Pathname may be omitted in which case it will be empty + let pathname_str = s.trim_start(); + + let hex = |s| usize::from_str_radix(s, 16).map_err(|_| "Couldn't parse hex number"); + let hex64 = |s| u64::from_str_radix(s, 16).map_err(|_| "Couldn't parse hex number"); + + let address = if let Some((start, limit)) = range_str.split_once('-') { + (hex(start)?, hex(limit)?) + } else { + return Err("Couldn't parse address range"); + }; + let perms: [char; 4] = { + let mut chars = perms_str.chars(); + let mut c = || chars.next().ok_or("insufficient perms"); + let perms = [c()?, c()?, c()?, c()?]; + if chars.next().is_some() { + return Err("too many perms"); + } + perms + }; + let offset = hex64(offset_str)?; + let dev = if let Some((major, minor)) = dev_str.split_once(':') { + (hex(major)?, hex(minor)?) + } else { + return Err("Couldn't parse dev"); + }; + let inode = hex(inode_str)?; + let pathname = pathname_str.into(); + + Ok(MapsEntry { + address, + perms, + offset, + dev, + inode, + pathname, + }) + } +} + +// Make sure we can parse 64-bit sample output if we're on a 64-bit target. +#[cfg(target_pointer_width = "64")] +#[test] +fn check_maps_entry_parsing_64bit() { + assert_eq!( + "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 \ + [vsyscall]" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0xffffffffff600000, 0xffffffffff601000), + perms: ['-', '-', 'x', 'p'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: "[vsyscall]".into(), + } + ); + + assert_eq!( + "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 \ + /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0x7f5985f46000, 0x7f5985f48000), + perms: ['r', 'w', '-', 'p'], + offset: 0x00039000, + dev: (0x103, 0x06), + inode: 0x76021795, + pathname: "/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2".into(), + } + ); + assert_eq!( + "35b1a21000-35b1a22000 rw-p 00000000 00:00 0" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0x35b1a21000, 0x35b1a22000), + perms: ['r', 'w', '-', 'p'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: Default::default(), + } + ); +} + +// (This output was taken from a 32-bit machine, but will work on any target) +#[test] +fn check_maps_entry_parsing_32bit() { + /* Example snippet of output: + 08056000-08077000 rw-p 00000000 00:00 0 [heap] + b7c79000-b7e02000 r--p 00000000 08:01 60662705 /usr/lib/locale/locale-archive + b7e02000-b7e03000 rw-p 00000000 00:00 0 + */ + assert_eq!( + "08056000-08077000 rw-p 00000000 00:00 0 \ + [heap]" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0x08056000, 0x08077000), + perms: ['r', 'w', '-', 'p'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: "[heap]".into(), + } + ); + + assert_eq!( + "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \ + /usr/lib/locale/locale-archive" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0xb7c79000, 0xb7e02000), + perms: ['r', '-', '-', 'p'], + offset: 0x00000000, + dev: (0x08, 0x01), + inode: 0x60662705, + pathname: "/usr/lib/locale/locale-archive".into(), + } + ); + assert_eq!( + "b7e02000-b7e03000 rw-p 00000000 00:00 0" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0xb7e02000, 0xb7e03000), + perms: ['r', 'w', '-', 'p'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: Default::default(), + } + ); + assert_eq!( + "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \ + /executable/path/with some spaces" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0xb7c79000, 0xb7e02000), + perms: ['r', '-', '-', 'p'], + offset: 0x00000000, + dev: (0x08, 0x01), + inode: 0x60662705, + pathname: "/executable/path/with some spaces".into(), + } + ); + assert_eq!( + "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \ + /executable/path/with multiple-continuous spaces " + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0xb7c79000, 0xb7e02000), + perms: ['r', '-', '-', 'p'], + offset: 0x00000000, + dev: (0x08, 0x01), + inode: 0x60662705, + pathname: "/executable/path/with multiple-continuous spaces ".into(), + } + ); + assert_eq!( + " b7c79000-b7e02000 r--p 00000000 08:01 60662705 \ + /executable/path/starts-with-spaces" + .parse::<MapsEntry>() + .unwrap(), + MapsEntry { + address: (0xb7c79000, 0xb7e02000), + perms: ['r', '-', '-', 'p'], + offset: 0x00000000, + dev: (0x08, 0x01), + inode: 0x60662705, + pathname: "/executable/path/starts-with-spaces".into(), + } + ); +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/stash.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/stash.rs new file mode 100644 index 0000000..5ec06e2 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/stash.rs
@@ -0,0 +1,51 @@ +#![allow(clippy::all)] +// only used on Linux right now, so allow dead code elsewhere +#![cfg_attr(not(target_os = "linux"), allow(dead_code))] + +use super::Mmap; +use alloc::vec; +use alloc::vec::Vec; +use core::cell::UnsafeCell; + +/// A simple arena allocator for byte buffers. +pub struct Stash { + buffers: UnsafeCell<Vec<Vec<u8>>>, + mmaps: UnsafeCell<Vec<Mmap>>, +} + +impl Stash { + pub fn new() -> Stash { + Stash { + buffers: UnsafeCell::new(Vec::new()), + mmaps: UnsafeCell::new(Vec::new()), + } + } + + /// Allocates a buffer of the specified size and returns a mutable reference + /// to it. + pub fn allocate(&self, size: usize) -> &mut [u8] { + // SAFETY: this is the only function that ever constructs a mutable + // reference to `self.buffers`. + let buffers = unsafe { &mut *self.buffers.get() }; + let i = buffers.len(); + buffers.push(vec![0; size]); + // SAFETY: we never remove elements from `self.buffers`, so a reference + // to the data inside any buffer will live as long as `self` does. + &mut buffers[i] + } + + /// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer + /// which is scoped to just this lifetime. + pub fn cache_mmap(&self, map: Mmap) -> &[u8] { + // SAFETY: this is the only location for a mutable pointer to + // `mmaps`, and this structure isn't threadsafe to shared across + // threads either. We also never remove elements from `self.mmaps`, + // so a reference to the data inside the map will live as long as + // `self` does. + unsafe { + let mmaps = &mut *self.mmaps.get(); + mmaps.push(map); + mmaps.last().unwrap() + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/xcoff.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/xcoff.rs new file mode 100644 index 0000000..18c61cb --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/gimli/xcoff.rs
@@ -0,0 +1,188 @@ +use super::mystd::ffi::OsStr; +use super::mystd::os::unix::ffi::OsStrExt; +use super::mystd::path::Path; +use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash}; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::ops::Deref; +use core::str; +use object::read::archive::ArchiveFile; +use object::read::xcoff::{FileHeader, SectionHeader, XcoffFile, XcoffSymbol}; +use object::Object as _; +use object::ObjectSection as _; +use object::ObjectSymbol as _; +use object::SymbolFlags; + +#[cfg(target_pointer_width = "32")] +type Xcoff = object::xcoff::FileHeader32; +#[cfg(target_pointer_width = "64")] +type Xcoff = object::xcoff::FileHeader64; + +impl Mapping { + pub fn new(path: &Path, member_name: &OsStr) -> Option<Mapping> { + let map = super::mmap(path)?; + Mapping::mk(map, |data, stash| { + if member_name.is_empty() { + Context::new(stash, Object::parse(data)?, None, None) + } else { + let archive = ArchiveFile::parse(data).ok()?; + for member in archive + .members() + .filter_map(|m| m.ok()) + .filter(|m| OsStr::from_bytes(m.name()) == member_name) + { + let member_data = member.data(data).ok()?; + if let Some(obj) = Object::parse(member_data) { + return Context::new(stash, obj, None, None); + } + } + None + } + }) + } +} + +struct ParsedSym<'a> { + address: u64, + size: u64, + name: &'a str, +} + +pub struct Object<'a> { + syms: Vec<ParsedSym<'a>>, + file: XcoffFile<'a, Xcoff>, +} + +pub struct Image { + pub offset: usize, + pub base: u64, + pub size: usize, +} + +pub fn parse_xcoff(data: &[u8]) -> Option<Image> { + let mut offset = 0; + let header = Xcoff::parse(data, &mut offset).ok()?; + let _ = header.aux_header(data, &mut offset).ok()?; + let sections = header.sections(data, &mut offset).ok()?; + if let Some(section) = sections.iter().find(|s| { + if let Ok(name) = str::from_utf8(&s.s_name()[0..5]) { + name == ".text" + } else { + false + } + }) { + Some(Image { + offset: section.s_scnptr() as usize, + base: section.s_paddr() as u64, + size: section.s_size() as usize, + }) + } else { + None + } +} + +pub fn parse_image(path: &Path, member_name: &OsStr) -> Option<Image> { + let map = super::mmap(path)?; + let data = map.deref(); + if member_name.is_empty() { + return parse_xcoff(data); + } else { + let archive = ArchiveFile::parse(data).ok()?; + for member in archive + .members() + .filter_map(|m| m.ok()) + .filter(|m| OsStr::from_bytes(m.name()) == member_name) + { + let member_data = member.data(data).ok()?; + if let Some(image) = parse_xcoff(member_data) { + return Some(image); + } + } + None + } +} + +impl<'a> Object<'a> { + fn get_concrete_size(file: &XcoffFile<'a, Xcoff>, sym: &XcoffSymbol<'a, '_, Xcoff>) -> u64 { + match sym.flags() { + SymbolFlags::Xcoff { + n_sclass: _, + x_smtyp: _, + x_smclas: _, + containing_csect: Some(index), + } => { + if let Ok(tgt_sym) = file.symbol_by_index(index) { + Self::get_concrete_size(file, &tgt_sym) + } else { + 0 + } + } + _ => sym.size(), + } + } + + fn parse(data: &'a [u8]) -> Option<Object<'a>> { + let file = XcoffFile::parse(data).ok()?; + let mut syms = file + .symbols() + .filter_map(|sym| { + let name = sym.name().map_or("", |v| v); + let address = sym.address(); + let size = Self::get_concrete_size(&file, &sym); + if name == ".text" || name == ".data" { + // We don't want to include ".text" and ".data" symbols. + // If they are included, since their ranges cover other + // symbols, when searching a symbol for a given address, + // ".text" or ".data" is returned. That's not what we expect. + None + } else { + Some(ParsedSym { + address, + size, + name, + }) + } + }) + .collect::<Vec<_>>(); + syms.sort_by_key(|s| s.address); + Some(Object { syms, file }) + } + + pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> { + Some(self.file.section_by_name(name)?.data().ok()?) + } + + pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> { + // Symbols, except ".text" and ".data", are sorted and are not overlapped each other, + // so we can just perform a binary search here. + let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) { + Ok(i) => i, + Err(i) => i.checked_sub(1)?, + }; + let sym = self.syms.get(i)?; + if (sym.address..sym.address + sym.size).contains(&addr) { + // On AIX, for a function call, for example, `foo()`, we have + // two symbols `foo` and `.foo`. `foo` references the function + // descriptor and `.foo` references the function entry. + // See https://www.ibm.com/docs/en/xl-fortran-aix/16.1.0?topic=calls-linkage-convention-function + // for more information. + // We trim the prefix `.` here, so that the rust demangler can work + // properly. + Some(sym.name.trim_start_matches(".").as_bytes()) + } else { + None + } + } + + pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> { + None + } +} + +pub(super) fn handle_split_dwarf<'data>( + _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, + _stash: &'data Stash, + _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { + None +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/miri.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/miri.rs new file mode 100644 index 0000000..5b0dc30 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/miri.rs
@@ -0,0 +1,56 @@ +use core::ffi::c_void; +use core::marker::PhantomData; + +use super::super::backtrace::miri::{resolve_addr, Frame}; +use super::BytesOrWideString; +use super::{ResolveWhat, SymbolName}; + +pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { + let sym = match what { + ResolveWhat::Address(addr) => Symbol { + inner: resolve_addr(addr), + _unused: PhantomData, + }, + ResolveWhat::Frame(frame) => Symbol { + inner: frame.inner.clone(), + _unused: PhantomData, + }, + }; + cb(&super::Symbol { inner: sym }) +} + +pub struct Symbol<'a> { + inner: Frame, + _unused: PhantomData<&'a ()>, +} + +impl<'a> Symbol<'a> { + pub fn name(&self) -> Option<SymbolName<'_>> { + Some(SymbolName::new(&self.inner.inner.name)) + } + + pub fn addr(&self) -> Option<*mut c_void> { + Some(self.inner.addr) + } + + pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> { + Some(BytesOrWideString::Bytes(&self.inner.inner.filename)) + } + + pub fn lineno(&self) -> Option<u32> { + Some(self.inner.inner.lineno) + } + + pub fn colno(&self) -> Option<u32> { + Some(self.inner.inner.colno) + } + + #[cfg(feature = "std")] + pub fn filename(&self) -> Option<&std::path::Path> { + Some(std::path::Path::new( + core::str::from_utf8(&self.inner.inner.filename).unwrap(), + )) + } +} + +pub unsafe fn clear_symbol_cache() {}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/mod.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/mod.rs new file mode 100644 index 0000000..62714f0 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/mod.rs
@@ -0,0 +1,452 @@ +use core::{fmt, str}; + +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + use std::path::Path; + use std::prelude::v1::*; + } +} + +use super::backtrace::Frame; +use super::types::BytesOrWideString; +use core::ffi::c_void; +use rustc_demangle::{try_demangle, Demangle}; + +/// Resolve an address to a symbol, passing the symbol to the specified +/// closure. +/// +/// This function will look up the given address in areas such as the local +/// symbol table, dynamic symbol table, or DWARF debug info (depending on the +/// activated implementation) to find symbols to yield. +/// +/// The closure may not be called if resolution could not be performed, and it +/// also may be called more than once in the case of inlined functions. +/// +/// Symbols yielded represent the execution at the specified `addr`, returning +/// file/line pairs for that address (if available). +/// +/// Note that if you have a `Frame` then it's recommended to use the +/// `resolve_frame` function instead of this one. +/// +/// # Required features +/// +/// This function requires the `std` feature of the `backtrace` crate to be +/// enabled, and the `std` feature is enabled by default. +/// +/// # Panics +/// +/// This function strives to never panic, but if the `cb` provided panics then +/// some platforms will force a double panic to abort the process. Some +/// platforms use a C library which internally uses callbacks which cannot be +/// unwound through, so panicking from `cb` may trigger a process abort. +/// +/// # Example +/// +/// ``` +/// extern crate backtrace; +/// +/// fn main() { +/// backtrace::trace(|frame| { +/// let ip = frame.ip(); +/// +/// backtrace::resolve(ip, |symbol| { +/// // ... +/// }); +/// +/// false // only look at the top frame +/// }); +/// } +/// ``` +#[cfg(feature = "std")] +pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) { + let _guard = crate::lock::lock(); + unsafe { resolve_unsynchronized(addr, cb) } +} + +/// Resolve a previously captured frame to a symbol, passing the symbol to the +/// specified closure. +/// +/// This function performs the same function as `resolve` except that it takes a +/// `Frame` as an argument instead of an address. This can allow some platform +/// implementations of backtracing to provide more accurate symbol information +/// or information about inline frames for example. It's recommended to use this +/// if you can. +/// +/// # Required features +/// +/// This function requires the `std` feature of the `backtrace` crate to be +/// enabled, and the `std` feature is enabled by default. +/// +/// # Panics +/// +/// This function strives to never panic, but if the `cb` provided panics then +/// some platforms will force a double panic to abort the process. Some +/// platforms use a C library which internally uses callbacks which cannot be +/// unwound through, so panicking from `cb` may trigger a process abort. +/// +/// # Example +/// +/// ``` +/// extern crate backtrace; +/// +/// fn main() { +/// backtrace::trace(|frame| { +/// backtrace::resolve_frame(frame, |symbol| { +/// // ... +/// }); +/// +/// false // only look at the top frame +/// }); +/// } +/// ``` +#[cfg(feature = "std")] +pub fn resolve_frame<F: FnMut(&Symbol)>(frame: &Frame, cb: F) { + let _guard = crate::lock::lock(); + unsafe { resolve_frame_unsynchronized(frame, cb) } +} + +pub enum ResolveWhat<'a> { + Address(*mut c_void), + Frame(&'a Frame), +} + +impl<'a> ResolveWhat<'a> { + #[allow(dead_code)] + fn address_or_ip(&self) -> *mut c_void { + match self { + ResolveWhat::Address(a) => adjust_ip(*a), + ResolveWhat::Frame(f) => adjust_ip(f.ip()), + } + } +} + +// IP values from stack frames are typically (always?) the instruction +// *after* the call that's the actual stack trace. Symbolizing this on +// causes the filename/line number to be one ahead and perhaps into +// the void if it's near the end of the function. +// +// This appears to basically always be the case on all platforms, so we always +// subtract one from a resolved ip to resolve it to the previous call +// instruction instead of the instruction being returned to. +// +// Ideally we would not do this. Ideally we would require callers of the +// `resolve` APIs here to manually do the -1 and account that they want location +// information for the *previous* instruction, not the current. Ideally we'd +// also expose on `Frame` if we are indeed the address of the next instruction +// or the current. +// +// For now though this is a pretty niche concern so we just internally always +// subtract one. Consumers should keep working and getting pretty good results, +// so we should be good enough. +fn adjust_ip(a: *mut c_void) -> *mut c_void { + if a.is_null() { + a + } else { + (a as usize - 1) as *mut c_void + } +} + +/// Same as `resolve`, only unsafe as it's unsynchronized. +/// +/// This function does not have synchronization guarantees but is available when +/// the `std` feature of this crate isn't compiled in. See the `resolve` +/// function for more documentation and examples. +/// +/// # Panics +/// +/// See information on `resolve` for caveats on `cb` panicking. +pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F) +where + F: FnMut(&Symbol), +{ + unsafe { imp::resolve(ResolveWhat::Address(addr), &mut cb) } +} + +/// Same as `resolve_frame`, only unsafe as it's unsynchronized. +/// +/// This function does not have synchronization guarantees but is available +/// when the `std` feature of this crate isn't compiled in. See the +/// `resolve_frame` function for more documentation and examples. +/// +/// # Panics +/// +/// See information on `resolve_frame` for caveats on `cb` panicking. +pub unsafe fn resolve_frame_unsynchronized<F>(frame: &Frame, mut cb: F) +where + F: FnMut(&Symbol), +{ + unsafe { imp::resolve(ResolveWhat::Frame(frame), &mut cb) } +} + +/// A trait representing the resolution of a symbol in a file. +/// +/// This trait is yielded as a trait object to the closure given to the +/// `backtrace::resolve` function, and it is virtually dispatched as it's +/// unknown which implementation is behind it. +/// +/// A symbol can give contextual information about a function, for example the +/// name, filename, line number, precise address, etc. Not all information is +/// always available in a symbol, however, so all methods return an `Option`. +pub struct Symbol { + // TODO: this lifetime bound needs to be persisted eventually to `Symbol`, + // but that's currently a breaking change. For now this is safe since + // `Symbol` is only ever handed out by reference and can't be cloned. + inner: imp::Symbol<'static>, +} + +impl Symbol { + /// Returns the name of this function. + /// + /// The returned structure can be used to query various properties about the + /// symbol name: + /// + /// * The `Display` implementation will print out the demangled symbol. + /// * The raw `str` value of the symbol can be accessed (if it's valid + /// utf-8). + /// * The raw bytes for the symbol name can be accessed. + pub fn name(&self) -> Option<SymbolName<'_>> { + self.inner.name() + } + + /// Returns the starting address of this function. + pub fn addr(&self) -> Option<*mut c_void> { + self.inner.addr() + } + + /// Returns the raw filename as a slice. This is mainly useful for `no_std` + /// environments. + pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> { + self.inner.filename_raw() + } + + /// Returns the column number for where this symbol is currently executing. + /// + /// Only gimli currently provides a value here and even then only if `filename` + /// returns `Some`, and so it is then consequently subject to similar caveats. + pub fn colno(&self) -> Option<u32> { + self.inner.colno() + } + + /// Returns the line number for where this symbol is currently executing. + /// + /// This return value is typically `Some` if `filename` returns `Some`, and + /// is consequently subject to similar caveats. + pub fn lineno(&self) -> Option<u32> { + self.inner.lineno() + } + + /// Returns the file name where this function was defined. + /// + /// This is currently only available when libbacktrace or gimli is being + /// used (e.g. unix platforms other) and when a binary is compiled with + /// debuginfo. If neither of these conditions is met then this will likely + /// return `None`. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + #[cfg(feature = "std")] + #[allow(unreachable_code)] + pub fn filename(&self) -> Option<&Path> { + self.inner.filename() + } +} + +impl fmt::Debug for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("Symbol"); + if let Some(name) = self.name() { + d.field("name", &name); + } + if let Some(addr) = self.addr() { + d.field("addr", &addr); + } + + #[cfg(feature = "std")] + { + if let Some(filename) = self.filename() { + d.field("filename", &filename); + } + } + + if let Some(lineno) = self.lineno() { + d.field("lineno", &lineno); + } + d.finish() + } +} + +cfg_if::cfg_if! { + if #[cfg(feature = "cpp_demangle")] { + // Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust + // failed. + struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>); + + impl<'a> OptionCppSymbol<'a> { + fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> { + OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok()) + } + + fn none() -> OptionCppSymbol<'a> { + OptionCppSymbol(None) + } + } + } +} + +/// A wrapper around a symbol name to provide ergonomic accessors to the +/// demangled name, the raw bytes, the raw string, etc. +pub struct SymbolName<'a> { + bytes: &'a [u8], + demangled: Option<Demangle<'a>>, + #[cfg(feature = "cpp_demangle")] + cpp_demangled: OptionCppSymbol<'a>, +} + +impl<'a> SymbolName<'a> { + /// Creates a new symbol name from the raw underlying bytes. + pub fn new(bytes: &'a [u8]) -> SymbolName<'a> { + let str_bytes = str::from_utf8(bytes).ok(); + let demangled = str_bytes.and_then(|s| try_demangle(s).ok()); + + #[cfg(feature = "cpp_demangle")] + let cpp = if demangled.is_none() { + OptionCppSymbol::parse(bytes) + } else { + OptionCppSymbol::none() + }; + + SymbolName { + bytes, + demangled, + #[cfg(feature = "cpp_demangle")] + cpp_demangled: cpp, + } + } + + /// Returns the raw (mangled) symbol name as a `str` if the symbol is valid utf-8. + /// + /// Use the `Display` implementation if you want the demangled version. + pub fn as_str(&self) -> Option<&'a str> { + self.demangled + .as_ref() + .map(|s| s.as_str()) + .or_else(|| str::from_utf8(self.bytes).ok()) + } + + /// Returns the raw symbol name as a list of bytes + pub fn as_bytes(&self) -> &'a [u8] { + self.bytes + } +} + +fn format_symbol_name( + fmt: fn(&str, &mut fmt::Formatter<'_>) -> fmt::Result, + mut bytes: &[u8], + f: &mut fmt::Formatter<'_>, +) -> fmt::Result { + while bytes.len() > 0 { + match str::from_utf8(bytes) { + Ok(name) => { + fmt(name, f)?; + break; + } + Err(err) => { + fmt("\u{FFFD}", f)?; + + match err.error_len() { + Some(len) => bytes = &bytes[err.valid_up_to() + len..], + None => break, + } + } + } + } + Ok(()) +} + +impl<'a> fmt::Display for SymbolName<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(ref s) = self.demangled { + return s.fmt(f); + } + + #[cfg(feature = "cpp_demangle")] + { + // This may fail to print if the demangled symbol isn't actually + // valid, so handle the error here gracefully by not propagating + // it outwards. + if let Some(ref cpp) = self.cpp_demangled.0 { + if let Ok(s) = cpp.demangle() { + return s.fmt(f); + } + } + } + + format_symbol_name(fmt::Display::fmt, self.bytes, f) + } +} + +impl<'a> fmt::Debug for SymbolName<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(ref s) = self.demangled { + return s.fmt(f); + } + + #[cfg(all(feature = "std", feature = "cpp_demangle"))] + { + // This may fail to print if the demangled symbol isn't actually + // valid, so handle the error here gracefully by not propagating + // it outwards. + if let Some(ref cpp) = self.cpp_demangled.0 { + if let Ok(s) = cpp.demangle() { + return s.fmt(f); + } + } + } + + format_symbol_name(fmt::Debug::fmt, self.bytes, f) + } +} + +/// Attempt to reclaim that cached memory used to symbolicate addresses. +/// +/// This method will attempt to release any global data structures that have +/// otherwise been cached globally or in the thread which typically represent +/// parsed DWARF information or similar. +/// +/// # Caveats +/// +/// While this function is always available it doesn't actually do anything on +/// most implementations. Libraries like dbghelp or libbacktrace do not provide +/// facilities to deallocate state and manage the allocated memory. For now the +/// `std` feature of this crate is the only feature where this +/// function has any effect. +#[cfg(feature = "std")] +pub fn clear_symbol_cache() { + let _guard = crate::lock::lock(); + unsafe { + imp::clear_symbol_cache(); + } +} + +cfg_if::cfg_if! { + if #[cfg(miri)] { + mod miri; + use miri as imp; + } else if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] { + mod dbghelp; + use dbghelp as imp; + } else if #[cfg(all( + any(unix, all(windows, target_env = "gnu")), + not(target_vendor = "uwp"), + not(target_os = "emscripten"), + any(not(backtrace_in_libstd), feature = "backtrace"), + ))] { + mod gimli; + use gimli as imp; + } else { + mod noop; + use noop as imp; + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/noop.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/noop.rs new file mode 100644 index 0000000..c533365 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/symbolize/noop.rs
@@ -0,0 +1,41 @@ +//! Empty symbolication strategy used to compile for platforms that have no +//! support. + +use super::{BytesOrWideString, ResolveWhat, SymbolName}; +use core::ffi::c_void; +use core::marker; + +pub unsafe fn resolve(_addr: ResolveWhat<'_>, _cb: &mut dyn FnMut(&super::Symbol)) {} + +pub struct Symbol<'a> { + _marker: marker::PhantomData<&'a i32>, +} + +impl Symbol<'_> { + pub fn name(&self) -> Option<SymbolName<'_>> { + None + } + + pub fn addr(&self) -> Option<*mut c_void> { + None + } + + pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> { + None + } + + #[cfg(feature = "std")] + pub fn filename(&self) -> Option<&::std::path::Path> { + None + } + + pub fn lineno(&self) -> Option<u32> { + None + } + + pub fn colno(&self) -> Option<u32> { + None + } +} + +pub unsafe fn clear_symbol_cache() {}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/types.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/types.rs new file mode 100644 index 0000000..c419247 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/types.rs
@@ -0,0 +1,83 @@ +//! Platform dependent types. + +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + use std::borrow::Cow; + use std::fmt; + use std::path::PathBuf; + use std::prelude::v1::*; + use std::str; + } +} + +/// A platform independent representation of a string. When working with `std` +/// enabled it is recommended to the convenience methods for providing +/// conversions to `std` types. +#[derive(Debug)] +pub enum BytesOrWideString<'a> { + /// A slice, typically provided on Unix platforms. + Bytes(&'a [u8]), + /// Wide strings typically from Windows. + Wide(&'a [u16]), +} + +#[cfg(feature = "std")] +impl<'a> BytesOrWideString<'a> { + /// Lossy converts to a `Cow<str>`, will allocate if `Bytes` is not valid + /// UTF-8 or if `BytesOrWideString` is `Wide`. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn to_str_lossy(&self) -> Cow<'a, str> { + use self::BytesOrWideString::*; + + match *self { + Bytes(slice) => String::from_utf8_lossy(slice), + Wide(wide) => Cow::Owned(String::from_utf16_lossy(wide)), + } + } + + /// Provides a `Path` representation of `BytesOrWideString`. + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn into_path_buf(self) -> PathBuf { + #[cfg(unix)] + { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + if let BytesOrWideString::Bytes(slice) = self { + return PathBuf::from(OsStr::from_bytes(slice)); + } + } + + #[cfg(windows)] + { + use std::ffi::OsString; + use std::os::windows::ffi::OsStringExt; + + if let BytesOrWideString::Wide(slice) = self { + return PathBuf::from(OsString::from_wide(slice)); + } + } + + if let BytesOrWideString::Bytes(b) = self { + if let Ok(s) = str::from_utf8(b) { + return PathBuf::from(s); + } + } + unreachable!() + } +} + +#[cfg(feature = "std")] +impl<'a> fmt::Display for BytesOrWideString<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.to_str_lossy().fmt(f) + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys.rs new file mode 100644 index 0000000..ddd5393 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys.rs
@@ -0,0 +1,668 @@ +// Bindings generated by `windows-bindgen` 0.58.0 + +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +windows_link::link!("dbghelp.dll" "system" fn EnumerateLoadedModulesW64(hprocess : HANDLE, enumloadedmodulescallback : PENUMLOADED_MODULES_CALLBACKW64, usercontext : *const core::ffi::c_void) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn StackWalk64(machinetype : u32, hprocess : HANDLE, hthread : HANDLE, stackframe : *mut STACKFRAME64, contextrecord : *mut core::ffi::c_void, readmemoryroutine : PREAD_PROCESS_MEMORY_ROUTINE64, functiontableaccessroutine : PFUNCTION_TABLE_ACCESS_ROUTINE64, getmodulebaseroutine : PGET_MODULE_BASE_ROUTINE64, translateaddress : PTRANSLATE_ADDRESS_ROUTINE64) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn StackWalkEx(machinetype : u32, hprocess : HANDLE, hthread : HANDLE, stackframe : *mut STACKFRAME_EX, contextrecord : *mut core::ffi::c_void, readmemoryroutine : PREAD_PROCESS_MEMORY_ROUTINE64, functiontableaccessroutine : PFUNCTION_TABLE_ACCESS_ROUTINE64, getmodulebaseroutine : PGET_MODULE_BASE_ROUTINE64, translateaddress : PTRANSLATE_ADDRESS_ROUTINE64, flags : u32) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymAddrIncludeInlineTrace(hprocess : HANDLE, address : u64) -> u32); +windows_link::link!("dbghelp.dll" "system" fn SymFromAddrW(hprocess : HANDLE, address : u64, displacement : *mut u64, symbol : *mut SYMBOL_INFOW) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymFromInlineContextW(hprocess : HANDLE, address : u64, inlinecontext : u32, displacement : *mut u64, symbol : *mut SYMBOL_INFOW) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymFunctionTableAccess64(hprocess : HANDLE, addrbase : u64) -> *mut core::ffi::c_void); +windows_link::link!("dbghelp.dll" "system" fn SymGetLineFromAddrW64(hprocess : HANDLE, dwaddr : u64, pdwdisplacement : *mut u32, line : *mut IMAGEHLP_LINEW64) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymGetLineFromInlineContextW(hprocess : HANDLE, dwaddr : u64, inlinecontext : u32, qwmodulebaseaddress : u64, pdwdisplacement : *mut u32, line : *mut IMAGEHLP_LINEW64) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymGetModuleBase64(hprocess : HANDLE, qwaddr : u64) -> u64); +windows_link::link!("dbghelp.dll" "system" fn SymGetOptions() -> u32); +windows_link::link!("dbghelp.dll" "system" fn SymGetSearchPathW(hprocess : HANDLE, searchpatha : PWSTR, searchpathlength : u32) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymInitializeW(hprocess : HANDLE, usersearchpath : PCWSTR, finvadeprocess : BOOL) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymQueryInlineTrace(hprocess : HANDLE, startaddress : u64, startcontext : u32, startretaddress : u64, curaddress : u64, curcontext : *mut u32, curframeindex : *mut u32) -> BOOL); +windows_link::link!("dbghelp.dll" "system" fn SymSetOptions(symoptions : u32) -> u32); +windows_link::link!("dbghelp.dll" "system" fn SymSetSearchPathW(hprocess : HANDLE, searchpatha : PCWSTR) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn CreateFileMappingA(hfile : HANDLE, lpfilemappingattributes : *const SECURITY_ATTRIBUTES, flprotect : PAGE_PROTECTION_FLAGS, dwmaximumsizehigh : u32, dwmaximumsizelow : u32, lpname : PCSTR) -> HANDLE); +windows_link::link!("kernel32.dll" "system" fn CreateMutexA(lpmutexattributes : *const SECURITY_ATTRIBUTES, binitialowner : BOOL, lpname : PCSTR) -> HANDLE); +windows_link::link!("kernel32.dll" "system" fn CreateToolhelp32Snapshot(dwflags : CREATE_TOOLHELP_SNAPSHOT_FLAGS, th32processid : u32) -> HANDLE); +windows_link::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE); +windows_link::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32); +windows_link::link!("kernel32.dll" "system" fn GetCurrentThread() -> HANDLE); +windows_link::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC); +windows_link::link!("kernel32.dll" "system" fn LoadLibraryA(lplibfilename : PCSTR) -> HMODULE); +windows_link::link!("kernel32.dll" "system" fn MapViewOfFile(hfilemappingobject : HANDLE, dwdesiredaccess : FILE_MAP, dwfileoffsethigh : u32, dwfileoffsetlow : u32, dwnumberofbytestomap : usize) -> MEMORY_MAPPED_VIEW_ADDRESS); +windows_link::link!("kernel32.dll" "system" fn Module32FirstW(hsnapshot : HANDLE, lpme : *mut MODULEENTRY32W) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn Module32NextW(hsnapshot : HANDLE, lpme : *mut MODULEENTRY32W) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn ReleaseMutex(hmutex : HANDLE) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn RtlCaptureContext(contextrecord : *mut CONTEXT)); +#[cfg(target_arch = "aarch64")] +windows_link::link!("kernel32.dll" "system" fn RtlLookupFunctionEntry(controlpc : usize, imagebase : *mut usize, historytable : *mut UNWIND_HISTORY_TABLE) -> *mut IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY); +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +windows_link::link!("kernel32.dll" "system" fn RtlLookupFunctionEntry(controlpc : u64, imagebase : *mut u64, historytable : *mut UNWIND_HISTORY_TABLE) -> *mut IMAGE_RUNTIME_FUNCTION_ENTRY); +#[cfg(target_arch = "aarch64")] +windows_link::link!("kernel32.dll" "system" fn RtlVirtualUnwind(handlertype : RTL_VIRTUAL_UNWIND_HANDLER_TYPE, imagebase : usize, controlpc : usize, functionentry : *const IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY, contextrecord : *mut CONTEXT, handlerdata : *mut *mut core::ffi::c_void, establisherframe : *mut usize, contextpointers : *mut KNONVOLATILE_CONTEXT_POINTERS) -> EXCEPTION_ROUTINE); +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +windows_link::link!("kernel32.dll" "system" fn RtlVirtualUnwind(handlertype : RTL_VIRTUAL_UNWIND_HANDLER_TYPE, imagebase : u64, controlpc : u64, functionentry : *const IMAGE_RUNTIME_FUNCTION_ENTRY, contextrecord : *mut CONTEXT, handlerdata : *mut *mut core::ffi::c_void, establisherframe : *mut u64, contextpointers : *mut KNONVOLATILE_CONTEXT_POINTERS) -> EXCEPTION_ROUTINE); +windows_link::link!("kernel32.dll" "system" fn UnmapViewOfFile(lpbaseaddress : MEMORY_MAPPED_VIEW_ADDRESS) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn WaitForSingleObjectEx(hhandle : HANDLE, dwmilliseconds : u32, balertable : BOOL) -> WAIT_EVENT); +windows_link::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : u32, dwflags : u32, lpwidecharstr : PCWSTR, cchwidechar : i32, lpmultibytestr : PSTR, cbmultibyte : i32, lpdefaultchar : PCSTR, lpuseddefaultchar : *mut BOOL) -> i32); +windows_link::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32); +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ADDRESS64 { + pub Offset: u64, + pub Segment: u16, + pub Mode: ADDRESS_MODE, +} +pub type ADDRESS_MODE = i32; +#[repr(C)] +#[derive(Clone, Copy)] +pub union ARM64_NT_NEON128 { + pub Anonymous: ARM64_NT_NEON128_0, + pub D: [f64; 2], + pub S: [f32; 4], + pub H: [u16; 8], + pub B: [u8; 16], +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ARM64_NT_NEON128_0 { + pub Low: u64, + pub High: i64, +} +pub const AddrModeFlat: ADDRESS_MODE = 3i32; +pub type BOOL = i32; +#[repr(C)] +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] +pub struct CONTEXT { + pub ContextFlags: CONTEXT_FLAGS, + pub Cpsr: u32, + pub Anonymous: CONTEXT_0, + pub Sp: u64, + pub Pc: u64, + pub V: [ARM64_NT_NEON128; 32], + pub Fpcr: u32, + pub Fpsr: u32, + pub Bcr: [u32; 8], + pub Bvr: [u64; 8], + pub Wcr: [u32; 2], + pub Wvr: [u64; 2], +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] +pub union CONTEXT_0 { + pub Anonymous: CONTEXT_0_0, + pub X: [u64; 31], +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] +pub struct CONTEXT_0_0 { + pub X0: u64, + pub X1: u64, + pub X2: u64, + pub X3: u64, + pub X4: u64, + pub X5: u64, + pub X6: u64, + pub X7: u64, + pub X8: u64, + pub X9: u64, + pub X10: u64, + pub X11: u64, + pub X12: u64, + pub X13: u64, + pub X14: u64, + pub X15: u64, + pub X16: u64, + pub X17: u64, + pub X18: u64, + pub X19: u64, + pub X20: u64, + pub X21: u64, + pub X22: u64, + pub X23: u64, + pub X24: u64, + pub X25: u64, + pub X26: u64, + pub X27: u64, + pub X28: u64, + pub Fp: u64, + pub Lr: u64, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub struct CONTEXT { + pub P1Home: u64, + pub P2Home: u64, + pub P3Home: u64, + pub P4Home: u64, + pub P5Home: u64, + pub P6Home: u64, + pub ContextFlags: CONTEXT_FLAGS, + pub MxCsr: u32, + pub SegCs: u16, + pub SegDs: u16, + pub SegEs: u16, + pub SegFs: u16, + pub SegGs: u16, + pub SegSs: u16, + pub EFlags: u32, + pub Dr0: u64, + pub Dr1: u64, + pub Dr2: u64, + pub Dr3: u64, + pub Dr6: u64, + pub Dr7: u64, + pub Rax: u64, + pub Rcx: u64, + pub Rdx: u64, + pub Rbx: u64, + pub Rsp: u64, + pub Rbp: u64, + pub Rsi: u64, + pub Rdi: u64, + pub R8: u64, + pub R9: u64, + pub R10: u64, + pub R11: u64, + pub R12: u64, + pub R13: u64, + pub R14: u64, + pub R15: u64, + pub Rip: u64, + pub Anonymous: CONTEXT_0, + pub VectorRegister: [M128A; 26], + pub VectorControl: u64, + pub DebugControl: u64, + pub LastBranchToRip: u64, + pub LastBranchFromRip: u64, + pub LastExceptionToRip: u64, + pub LastExceptionFromRip: u64, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub union CONTEXT_0 { + pub FltSave: XSAVE_FORMAT, + pub Anonymous: CONTEXT_0_0, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub struct CONTEXT_0_0 { + pub Header: [M128A; 2], + pub Legacy: [M128A; 8], + pub Xmm0: M128A, + pub Xmm1: M128A, + pub Xmm2: M128A, + pub Xmm3: M128A, + pub Xmm4: M128A, + pub Xmm5: M128A, + pub Xmm6: M128A, + pub Xmm7: M128A, + pub Xmm8: M128A, + pub Xmm9: M128A, + pub Xmm10: M128A, + pub Xmm11: M128A, + pub Xmm12: M128A, + pub Xmm13: M128A, + pub Xmm14: M128A, + pub Xmm15: M128A, +} +#[repr(C)] +#[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] +pub struct CONTEXT { + pub ContextFlags: CONTEXT_FLAGS, + pub Dr0: u32, + pub Dr1: u32, + pub Dr2: u32, + pub Dr3: u32, + pub Dr6: u32, + pub Dr7: u32, + pub FloatSave: FLOATING_SAVE_AREA, + pub SegGs: u32, + pub SegFs: u32, + pub SegEs: u32, + pub SegDs: u32, + pub Edi: u32, + pub Esi: u32, + pub Ebx: u32, + pub Edx: u32, + pub Ecx: u32, + pub Eax: u32, + pub Ebp: u32, + pub Eip: u32, + pub SegCs: u32, + pub EFlags: u32, + pub Esp: u32, + pub SegSs: u32, + pub ExtendedRegisters: [u8; 512], +} + +pub type CONTEXT_FLAGS = u32; +pub const CP_UTF8: u32 = 65001u32; +pub type CREATE_TOOLHELP_SNAPSHOT_FLAGS = u32; +pub type EXCEPTION_DISPOSITION = i32; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: NTSTATUS, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut core::ffi::c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], +} +pub type EXCEPTION_ROUTINE = Option< + unsafe extern "system" fn( + exceptionrecord: *mut EXCEPTION_RECORD, + establisherframe: *const core::ffi::c_void, + contextrecord: *mut CONTEXT, + dispatchercontext: *const core::ffi::c_void, + ) -> EXCEPTION_DISPOSITION, +>; +pub const FALSE: BOOL = 0i32; +pub type FARPROC = Option<unsafe extern "system" fn() -> isize>; +pub type FILE_MAP = u32; +pub const FILE_MAP_READ: FILE_MAP = 4u32; +#[repr(C)] +#[cfg(any( + target_arch = "aarch64", + target_arch = "arm64ec", + target_arch = "x86_64" +))] +#[derive(Clone, Copy)] +pub struct FLOATING_SAVE_AREA { + pub ControlWord: u32, + pub StatusWord: u32, + pub TagWord: u32, + pub ErrorOffset: u32, + pub ErrorSelector: u32, + pub DataOffset: u32, + pub DataSelector: u32, + pub RegisterArea: [u8; 80], + pub Cr0NpxState: u32, +} +#[repr(C)] +#[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] +pub struct FLOATING_SAVE_AREA { + pub ControlWord: u32, + pub StatusWord: u32, + pub TagWord: u32, + pub ErrorOffset: u32, + pub ErrorSelector: u32, + pub DataOffset: u32, + pub DataSelector: u32, + pub RegisterArea: [u8; 80], + pub Spare0: u32, +} +pub type HANDLE = *mut core::ffi::c_void; +pub type HINSTANCE = *mut core::ffi::c_void; +pub type HMODULE = *mut core::ffi::c_void; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IMAGEHLP_LINEW64 { + pub SizeOfStruct: u32, + pub Key: *mut core::ffi::c_void, + pub LineNumber: u32, + pub FileName: PWSTR, + pub Address: u64, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY { + pub BeginAddress: u32, + pub Anonymous: IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_0, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub union IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_0 { + pub UnwindData: u32, + pub Anonymous: IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_0_0, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_0_0 { + pub _bitfield: u32, +} +pub type IMAGE_FILE_MACHINE = u16; +pub const IMAGE_FILE_MACHINE_I386: IMAGE_FILE_MACHINE = 332u16; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IMAGE_RUNTIME_FUNCTION_ENTRY { + pub BeginAddress: u32, + pub EndAddress: u32, + pub Anonymous: IMAGE_RUNTIME_FUNCTION_ENTRY_0, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub union IMAGE_RUNTIME_FUNCTION_ENTRY_0 { + pub UnwindInfoAddress: u32, + pub UnwindData: u32, +} +pub const INFINITE: u32 = 4294967295u32; +pub const INVALID_HANDLE_VALUE: HANDLE = -1i32 as _; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct KDHELP64 { + pub Thread: u64, + pub ThCallbackStack: u32, + pub ThCallbackBStore: u32, + pub NextCallback: u32, + pub FramePointer: u32, + pub KiCallUserMode: u64, + pub KeUserCallbackDispatcher: u64, + pub SystemRangeStart: u64, + pub KiUserExceptionDispatcher: u64, + pub StackBase: u64, + pub StackLimit: u64, + pub BuildVersion: u32, + pub RetpolineStubFunctionTableSize: u32, + pub RetpolineStubFunctionTable: u64, + pub RetpolineStubOffset: u32, + pub RetpolineStubSize: u32, + pub Reserved0: [u64; 2], +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] +pub struct KNONVOLATILE_CONTEXT_POINTERS { + pub X19: *mut u64, + pub X20: *mut u64, + pub X21: *mut u64, + pub X22: *mut u64, + pub X23: *mut u64, + pub X24: *mut u64, + pub X25: *mut u64, + pub X26: *mut u64, + pub X27: *mut u64, + pub X28: *mut u64, + pub Fp: *mut u64, + pub Lr: *mut u64, + pub D8: *mut u64, + pub D9: *mut u64, + pub D10: *mut u64, + pub D11: *mut u64, + pub D12: *mut u64, + pub D13: *mut u64, + pub D14: *mut u64, + pub D15: *mut u64, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub struct KNONVOLATILE_CONTEXT_POINTERS { + pub Anonymous1: KNONVOLATILE_CONTEXT_POINTERS_0, + pub Anonymous2: KNONVOLATILE_CONTEXT_POINTERS_1, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub union KNONVOLATILE_CONTEXT_POINTERS_0 { + pub FloatingContext: [*mut M128A; 16], + pub Anonymous: KNONVOLATILE_CONTEXT_POINTERS_0_0, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub struct KNONVOLATILE_CONTEXT_POINTERS_0_0 { + pub Xmm0: *mut M128A, + pub Xmm1: *mut M128A, + pub Xmm2: *mut M128A, + pub Xmm3: *mut M128A, + pub Xmm4: *mut M128A, + pub Xmm5: *mut M128A, + pub Xmm6: *mut M128A, + pub Xmm7: *mut M128A, + pub Xmm8: *mut M128A, + pub Xmm9: *mut M128A, + pub Xmm10: *mut M128A, + pub Xmm11: *mut M128A, + pub Xmm12: *mut M128A, + pub Xmm13: *mut M128A, + pub Xmm14: *mut M128A, + pub Xmm15: *mut M128A, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub union KNONVOLATILE_CONTEXT_POINTERS_1 { + pub IntegerContext: [*mut u64; 16], + pub Anonymous: KNONVOLATILE_CONTEXT_POINTERS_1_0, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub struct KNONVOLATILE_CONTEXT_POINTERS_1_0 { + pub Rax: *mut u64, + pub Rcx: *mut u64, + pub Rdx: *mut u64, + pub Rbx: *mut u64, + pub Rsp: *mut u64, + pub Rbp: *mut u64, + pub Rsi: *mut u64, + pub Rdi: *mut u64, + pub R8: *mut u64, + pub R9: *mut u64, + pub R10: *mut u64, + pub R11: *mut u64, + pub R12: *mut u64, + pub R13: *mut u64, + pub R14: *mut u64, + pub R15: *mut u64, +} +#[repr(C)] +#[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] +pub struct KNONVOLATILE_CONTEXT_POINTERS { + pub Dummy: u32, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct M128A { + pub Low: u64, + pub High: i64, +} +pub const MAX_SYM_NAME: u32 = 2000u32; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct MEMORY_MAPPED_VIEW_ADDRESS { + pub Value: *mut core::ffi::c_void, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct MODULEENTRY32W { + pub dwSize: u32, + pub th32ModuleID: u32, + pub th32ProcessID: u32, + pub GlblcntUsage: u32, + pub ProccntUsage: u32, + pub modBaseAddr: *mut u8, + pub modBaseSize: u32, + pub hModule: HMODULE, + pub szModule: [u16; 256], + pub szExePath: [u16; 260], +} +pub type NTSTATUS = i32; +pub type PAGE_PROTECTION_FLAGS = u32; +pub const PAGE_READONLY: PAGE_PROTECTION_FLAGS = 2u32; +pub type PCSTR = *const u8; +pub type PCWSTR = *const u16; +pub type PENUMLOADED_MODULES_CALLBACKW64 = Option< + unsafe extern "system" fn( + modulename: PCWSTR, + modulebase: u64, + modulesize: u32, + usercontext: *const core::ffi::c_void, + ) -> BOOL, +>; +pub type PFUNCTION_TABLE_ACCESS_ROUTINE64 = + Option<unsafe extern "system" fn(ahprocess: HANDLE, addrbase: u64) -> *mut core::ffi::c_void>; +pub type PGET_MODULE_BASE_ROUTINE64 = + Option<unsafe extern "system" fn(hprocess: HANDLE, address: u64) -> u64>; +pub type PREAD_PROCESS_MEMORY_ROUTINE64 = Option< + unsafe extern "system" fn( + hprocess: HANDLE, + qwbaseaddress: u64, + lpbuffer: *mut core::ffi::c_void, + nsize: u32, + lpnumberofbytesread: *mut u32, + ) -> BOOL, +>; +pub type PSTR = *mut u8; +pub type PTRANSLATE_ADDRESS_ROUTINE64 = Option< + unsafe extern "system" fn(hprocess: HANDLE, hthread: HANDLE, lpaddr: *const ADDRESS64) -> u64, +>; +pub type PWSTR = *mut u16; +pub type RTL_VIRTUAL_UNWIND_HANDLER_TYPE = u32; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut core::ffi::c_void, + pub bInheritHandle: BOOL, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct STACKFRAME64 { + pub AddrPC: ADDRESS64, + pub AddrReturn: ADDRESS64, + pub AddrFrame: ADDRESS64, + pub AddrStack: ADDRESS64, + pub AddrBStore: ADDRESS64, + pub FuncTableEntry: *mut core::ffi::c_void, + pub Params: [u64; 4], + pub Far: BOOL, + pub Virtual: BOOL, + pub Reserved: [u64; 3], + pub KdHelp: KDHELP64, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct STACKFRAME_EX { + pub AddrPC: ADDRESS64, + pub AddrReturn: ADDRESS64, + pub AddrFrame: ADDRESS64, + pub AddrStack: ADDRESS64, + pub AddrBStore: ADDRESS64, + pub FuncTableEntry: *mut core::ffi::c_void, + pub Params: [u64; 4], + pub Far: BOOL, + pub Virtual: BOOL, + pub Reserved: [u64; 3], + pub KdHelp: KDHELP64, + pub StackFrameSize: u32, + pub InlineFrameContext: u32, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SYMBOL_INFOW { + pub SizeOfStruct: u32, + pub TypeIndex: u32, + pub Reserved: [u64; 2], + pub Index: u32, + pub Size: u32, + pub ModBase: u64, + pub Flags: SYMBOL_INFO_FLAGS, + pub Value: u64, + pub Address: u64, + pub Register: u32, + pub Scope: u32, + pub Tag: u32, + pub NameLen: u32, + pub MaxNameLen: u32, + pub Name: [u16; 1], +} +pub type SYMBOL_INFO_FLAGS = u32; +pub const SYMOPT_DEFERRED_LOADS: u32 = 4u32; +pub const TH32CS_SNAPMODULE: CREATE_TOOLHELP_SNAPSHOT_FLAGS = 8u32; +pub const TRUE: BOOL = 1i32; +#[repr(C)] +#[cfg(any( + target_arch = "aarch64", + target_arch = "arm64ec", + target_arch = "x86_64" +))] +#[derive(Clone, Copy)] +pub struct UNWIND_HISTORY_TABLE { + pub Count: u32, + pub LocalHint: u8, + pub GlobalHint: u8, + pub Search: u8, + pub Once: u8, + pub LowAddress: usize, + pub HighAddress: usize, + pub Entry: [UNWIND_HISTORY_TABLE_ENTRY; 12], +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] +pub struct UNWIND_HISTORY_TABLE_ENTRY { + pub ImageBase: usize, + pub FunctionEntry: *mut IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY, +} +#[repr(C)] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] +pub struct UNWIND_HISTORY_TABLE_ENTRY { + pub ImageBase: usize, + pub FunctionEntry: *mut IMAGE_RUNTIME_FUNCTION_ENTRY, +} +pub type WAIT_EVENT = u32; +#[repr(C)] +#[cfg(any( + target_arch = "aarch64", + target_arch = "arm64ec", + target_arch = "x86_64" +))] +#[derive(Clone, Copy)] +pub struct XSAVE_FORMAT { + pub ControlWord: u16, + pub StatusWord: u16, + pub TagWord: u8, + pub Reserved1: u8, + pub ErrorOpcode: u16, + pub ErrorOffset: u32, + pub ErrorSelector: u16, + pub Reserved2: u16, + pub DataOffset: u32, + pub DataSelector: u16, + pub Reserved3: u16, + pub MxCsr: u32, + pub MxCsr_Mask: u32, + pub FloatRegisters: [M128A; 8], + pub XmmRegisters: [M128A; 16], + pub Reserved4: [u8; 96], +} +#[repr(C)] +#[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] +pub struct XSAVE_FORMAT { + pub ControlWord: u16, + pub StatusWord: u16, + pub TagWord: u8, + pub Reserved1: u8, + pub ErrorOpcode: u16, + pub ErrorOffset: u32, + pub ErrorSelector: u16, + pub Reserved2: u16, + pub DataOffset: u32, + pub DataSelector: u16, + pub Reserved3: u16, + pub MxCsr: u32, + pub MxCsr_Mask: u32, + pub FloatRegisters: [M128A; 8], + pub XmmRegisters: [M128A; 8], + pub Reserved4: [u8; 224], +} + +#[cfg(target_arch = "arm")] +include!("./windows_sys_arm32_shim.rs");
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys_arm32_shim.rs b/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys_arm32_shim.rs new file mode 100644 index 0000000..4df9064 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/src/windows_sys_arm32_shim.rs
@@ -0,0 +1,53 @@ +pub const ARM_MAX_BREAKPOINTS: usize = 8; +pub const ARM_MAX_WATCHPOINTS: usize = 1; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct NEON128 { + pub Low: u64, + pub High: i64, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union CONTEXT_FloatRegs { + pub Q: [NEON128; 16], + pub D: [u64; 32], + pub S: [u32; 32], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CONTEXT { + pub ContextFlags: u32, + pub R0: u32, + pub R1: u32, + pub R2: u32, + pub R3: u32, + pub R4: u32, + pub R5: u32, + pub R6: u32, + pub R7: u32, + pub R8: u32, + pub R9: u32, + pub R10: u32, + pub R11: u32, + pub R12: u32, + // Control registers + pub Sp: u32, + pub Lr: u32, + pub Pc: u32, + pub Cpsr: u32, + // Floating-point registers + pub Fpsrc: u32, + pub Padding: u32, + pub u: CONTEXT_FloatRegs, + // Debug registers + pub Bvr: [u32; ARM_MAX_BREAKPOINTS], + pub Bcr: [u32; ARM_MAX_BREAKPOINTS], + pub Wvr: [u32; ARM_MAX_WATCHPOINTS], + pub Wcr: [u32; ARM_MAX_WATCHPOINTS], + pub Padding2: [u32; 2], +} + +pub const IMAGE_FILE_MACHINE_ARMNT: IMAGE_FILE_MACHINE = 0x01c4;
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/accuracy/auxiliary.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/accuracy/auxiliary.rs new file mode 100644 index 0000000..9c8015d --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/accuracy/auxiliary.rs
@@ -0,0 +1,15 @@ +#[inline(never)] +pub fn callback<F>(f: F) +where + F: FnOnce((&'static str, u32)), +{ + f((file!(), line!())) +} + +#[inline(always)] +pub fn callback_inlined<F>(f: F) +where + F: FnOnce((&'static str, u32)), +{ + f((file!(), line!())) +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/accuracy/main.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/accuracy/main.rs new file mode 100644 index 0000000..b50e445 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/accuracy/main.rs
@@ -0,0 +1,121 @@ +#![cfg(dbginfo = "collapsible")] +mod auxiliary; + +macro_rules! pos { + () => { + (file!(), line!()) + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! check { + ($($pos:expr),*) => ({ + verify(&[$($pos,)* pos!()]); + }) +} + +type Pos = (&'static str, u32); + +#[test] +fn doit() { + if + // Skip musl which is by default statically linked and doesn't support + // dynamic libraries. + !cfg!(target_env = "musl") + // Skip Miri, since it doesn't support dynamic libraries. + && !cfg!(miri) + { + // TODO(#238) this shouldn't have to happen first in this function, but + // currently it does. + let mut dir = std::env::current_exe().unwrap(); + dir.pop(); + if cfg!(windows) { + dir.push("dylib_dep.dll"); + } else if cfg!(target_vendor = "apple") { + dir.push("libdylib_dep.dylib"); + } else if cfg!(target_os = "aix") { + dir.push("libdylib_dep.a"); + } else { + dir.push("libdylib_dep.so"); + } + unsafe { + let lib = libloading::Library::new(&dir).unwrap(); + let api = lib.get::<extern "C" fn(Pos, fn(Pos, Pos))>(b"foo").unwrap(); + api(pos!(), |a, b| { + check!(a, b); + }); + } + } + + outer(pos!()); +} + +#[inline(never)] +fn outer(main_pos: Pos) { + inner(main_pos, pos!()); + inner_inlined(main_pos, pos!()); +} + +#[inline(never)] +#[rustfmt::skip] +fn inner(main_pos: Pos, outer_pos: Pos) { + check!(main_pos, outer_pos); + check!(main_pos, outer_pos); + let inner_pos = pos!(); auxiliary::callback(|aux_pos| { + check!(main_pos, outer_pos, inner_pos, aux_pos); + }); + let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| { + check!(main_pos, outer_pos, inner_pos, aux_pos); + }); +} + +#[inline(always)] +#[rustfmt::skip] +fn inner_inlined(main_pos: Pos, outer_pos: Pos) { + check!(main_pos, outer_pos); + check!(main_pos, outer_pos); + + #[inline(always)] + fn inner_further_inlined(main_pos: Pos, outer_pos: Pos, inner_pos: Pos) { + check!(main_pos, outer_pos, inner_pos); + } + inner_further_inlined(main_pos, outer_pos, pos!()); + + let inner_pos = pos!(); auxiliary::callback(|aux_pos| { + check!(main_pos, outer_pos, inner_pos, aux_pos); + }); + let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| { + check!(main_pos, outer_pos, inner_pos, aux_pos); + }); + + // this tests a distinction between two independent calls to the inlined function. + // (un)fortunately, LLVM somehow merges two consecutive such calls into one node. + inner_further_inlined(main_pos, outer_pos, pos!()); +} + +fn verify(filelines: &[Pos]) { + let trace = backtrace::Backtrace::new(); + println!("-----------------------------------"); + println!("looking for:"); + for (file, line) in filelines.iter().rev() { + println!("\t{file}:{line}"); + } + println!("found:\n{trace:?}"); + let mut symbols = trace.frames().iter().flat_map(|frame| frame.symbols()); + let mut iter = filelines.iter().rev(); + while let Some((file, line)) = iter.next() { + loop { + let sym = match symbols.next() { + Some(sym) => sym, + None => panic!("failed to find {file}:{line}"), + }; + if let Some(filename) = sym.filename() { + if let Some(lineno) = sym.lineno() { + if filename.ends_with(file) && lineno == *line { + break; + } + } + } + } + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/common/mod.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/common/mod.rs new file mode 100644 index 0000000..3c07934 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/common/mod.rs
@@ -0,0 +1,14 @@ +/// Some tests only make sense in contexts where they can re-exec the test +/// itself. Not all contexts support this, so you can call this method to find +/// out which case you are in. +pub fn cannot_reexec_the_test() -> bool { + // These run in docker containers on CI where they can't re-exec the test, + // so just skip these for CI. No other reason this can't run on those + // platforms though. + // Miri does not have support for re-execing a file + cfg!(unix) + && (cfg!(target_arch = "arm") + || cfg!(target_arch = "aarch64") + || cfg!(target_arch = "s390x")) + || cfg!(miri) +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/concurrent-panics.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/concurrent-panics.rs new file mode 100644 index 0000000..350c247 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/concurrent-panics.rs
@@ -0,0 +1,72 @@ +use std::env; +use std::panic; +use std::process::Command; +use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; +use std::sync::Arc; +use std::thread; + +const PANICS: usize = 100; +const THREADS: usize = 8; +const VAR: &str = "__THE_TEST_YOU_ARE_LUKE"; + +mod common; + +fn main() { + // If we cannot re-exec this test, there's no point in trying to do it. + if common::cannot_reexec_the_test() { + println!("test result: ok"); + return; + } + + if env::var(VAR).is_err() { + parent(); + } else { + child(); + } +} + +fn parent() { + let me = env::current_exe().unwrap(); + let result = Command::new(&me) + .env("RUST_BACKTRACE", "1") + .env(VAR, "1") + .output() + .unwrap(); + if result.status.success() { + println!("test result: ok"); + return; + } + println!("stdout:\n{}", String::from_utf8_lossy(&result.stdout)); + println!("stderr:\n{}", String::from_utf8_lossy(&result.stderr)); + println!("code: {}", result.status); + panic!(); +} + +fn child() { + let done = Arc::new(AtomicBool::new(false)); + let done2 = done.clone(); + let a = thread::spawn(move || loop { + if done2.load(SeqCst) { + break format!("{:?}", backtrace::Backtrace::new()); + } + }); + + let threads = (0..THREADS) + .map(|_| { + thread::spawn(|| { + for _ in 0..PANICS { + assert!(panic::catch_unwind(|| { + panic!(); + }) + .is_err()); + } + }) + }) + .collect::<Vec<_>>(); + for thread in threads { + thread.join().unwrap(); + } + + done.store(true, SeqCst); + a.join().unwrap(); +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/current-exe-mismatch.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/current-exe-mismatch.rs new file mode 100644 index 0000000..d99c574 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/current-exe-mismatch.rs
@@ -0,0 +1,139 @@ +// rust-lang/rust#101913: when you run your program explicitly via `ld.so`, +// `std::env::current_exe` will return the path of *that* program, and not +// the Rust program itself. + +// This behavior is only known to be supported on Linux and FreeBSD, see +// https://mail-index.netbsd.org/tech-toolchain/2024/07/27/msg004469.html + +use std::io::{BufRead, BufReader}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +mod common; + +fn main() { + if cfg!(target_os = "netbsd") { + // NetBSD doesn't support this silliness, so because this is an fn main test, + // just pass it on there. If we used ui-test or something we'd use + //@ ignore-netbsd + return; + } + + if std::env::var(VAR).is_err() { + // the parent waits for the child; then we then handle either printing + // "test result: ok", "test result: ignored", or panicking. + match parent() { + Ok(()) => { + println!("test result: ok"); + } + Err(EarlyExit::IgnoreTest) => { + println!("test result: ignored"); + } + Err(EarlyExit::IoError(e)) => { + println!("{} parent encountered IoError: {:?}", file!(), e); + panic!(); + } + } + } else { + // println!("{} running child", file!()); + child().unwrap(); + } +} + +const VAR: &str = "__THE_TEST_YOU_ARE_LUKE"; + +#[derive(Debug)] +enum EarlyExit { + IgnoreTest, + IoError(std::io::Error), +} + +impl From<std::io::Error> for EarlyExit { + fn from(e: std::io::Error) -> Self { + EarlyExit::IoError(e) + } +} + +fn parent() -> Result<(), EarlyExit> { + // If we cannot re-exec this test, there's no point in trying to do it. + if common::cannot_reexec_the_test() { + return Err(EarlyExit::IgnoreTest); + } + + let me = std::env::current_exe().unwrap(); + let ld_so = find_interpreter(&me)?; + + // use interp to invoke current exe, yielding child test. + // + // (if you're curious what you might compare this against, you can try + // swapping in the below definition for `result`, which is the easy case of + // not using the ld.so interpreter directly that Rust handled fine even + // prior to resolution of rust-lang/rust#101913.) + // + // let result = Command::new(me).env(VAR, "1").output()?; + let result = Command::new(ld_so).env(VAR, "1").arg(&me).output().unwrap(); + + if result.status.success() { + return Ok(()); + } + println!("stdout:\n{}", String::from_utf8_lossy(&result.stdout)); + println!("stderr:\n{}", String::from_utf8_lossy(&result.stderr)); + println!("code: {}", result.status); + panic!(); +} + +fn child() -> Result<(), EarlyExit> { + let bt = backtrace::Backtrace::new(); + println!("{bt:?}"); + + let mut found_my_name = false; + + let my_filename = file!(); + 'frames: for frame in bt.frames() { + let symbols = frame.symbols(); + if symbols.is_empty() { + continue; + } + + for sym in symbols { + if let Some(filename) = sym.filename() { + if filename.ends_with(my_filename) { + // huzzah! + found_my_name = true; + break 'frames; + } + } + } + } + + assert!(found_my_name); + + Ok(()) +} + +// we use the `readelf` command to extract the path to the interpreter requested +// by our binary. +// +// if we cannot `readelf` for some reason, or if we fail to parse its output, +// then we will just give up on this test (and not treat it as a test failure). +fn find_interpreter(me: &Path) -> Result<PathBuf, EarlyExit> { + let result = Command::new("readelf") + .arg("-l") + .arg(me) + .output() + .map_err(|_| EarlyExit::IgnoreTest)?; + if result.status.success() { + let r = BufReader::new(&result.stdout[..]); + for line in r.lines() { + let line = line?; + let line = line.trim(); + let prefix = "[Requesting program interpreter: "; + if let Some((_, suffix)) = line.split_once(prefix) { + if let Some((found_path, _)) = suffix.rsplit_once("]") { + return Ok(found_path.into()); + } + } + } + } + Err(EarlyExit::IgnoreTest) +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/long_fn_name.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/long_fn_name.rs new file mode 100644 index 0000000..4a03825 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/long_fn_name.rs
@@ -0,0 +1,48 @@ +use backtrace::Backtrace; + +// 50-character module name +mod _234567890_234567890_234567890_234567890_234567890 { + // 50-character struct name + #[allow(non_camel_case_types)] + pub struct _234567890_234567890_234567890_234567890_234567890<T>(T); + impl<T> _234567890_234567890_234567890_234567890_234567890<T> { + #[allow(dead_code)] + pub fn new() -> crate::Backtrace { + crate::Backtrace::new() + } + } +} + +// Long function names must be truncated to (MAX_SYM_NAME - 1) characters. +// Only run this test for msvc, since gnu prints "<no info>" for all frames. +#[test] +#[cfg(all(windows, target_env = "msvc"))] +fn test_long_fn_name() { + use _234567890_234567890_234567890_234567890_234567890::_234567890_234567890_234567890_234567890_234567890 as S; + + // 10 repetitions of struct name, so fully qualified function name is + // atleast 10 * (50 + 50) * 2 = 2000 characters long. + // It's actually longer since it also includes `::`, `<>` and the + // name of the current module + let bt = S::<S<S<S<S<S<S<S<S<S<i32>>>>>>>>>>::new(); + println!("{bt:?}"); + + let mut found_long_name_frame = false; + + for frame in bt.frames() { + let symbols = frame.symbols(); + if symbols.is_empty() { + continue; + } + + if let Some(function_name) = symbols[0].name() { + let function_name = function_name.as_str().unwrap(); + if function_name.contains("::_234567890_234567890_234567890_234567890_234567890") { + found_long_name_frame = true; + assert!(function_name.len() > 200); + } + } + } + + assert!(found_long_name_frame); +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/sgx-image-base.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/sgx-image-base.rs new file mode 100644 index 0000000..c29a8b6 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/sgx-image-base.rs
@@ -0,0 +1,56 @@ +#![cfg(all(target_env = "sgx", target_vendor = "fortanix"))] +#![feature(sgx_platform)] + +#[cfg(feature = "std")] +#[test] +fn sgx_image_base_with_std() { + use backtrace::trace; + + let image_base = std::os::fortanix_sgx::mem::image_base(); + + let mut frame_ips = Vec::new(); + trace(|frame| { + frame_ips.push(frame.ip()); + true + }); + + assert!(frame_ips.len() > 0); + for ip in frame_ips { + let ip: u64 = ip as _; + assert!(ip < image_base); + } +} + +#[cfg(not(feature = "std"))] +#[test] +fn sgx_image_base_no_std() { + use backtrace::trace_unsynchronized; + + fn guess_image_base() -> u64 { + let mut top_frame_ip = None; + unsafe { + trace_unsynchronized(|frame| { + top_frame_ip = Some(frame.ip()); + false + }); + } + top_frame_ip.unwrap() as u64 & 0xFFFFFF000000 + } + + let image_base = guess_image_base(); + backtrace::set_image_base(image_base as _); + + let mut frame_ips = Vec::new(); + unsafe { + trace_unsynchronized(|frame| { + frame_ips.push(frame.ip()); + true + }); + } + + assert!(frame_ips.len() > 0); + for ip in frame_ips { + let ip: u64 = ip as _; + assert!(ip < image_base); + } +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/skip_inner_frames.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/skip_inner_frames.rs new file mode 100644 index 0000000..e62a160 --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/skip_inner_frames.rs
@@ -0,0 +1,51 @@ +use backtrace::Backtrace; +use core::ffi::c_void; + +// This test only works on platforms which have a working `symbol_address` +// function for frames which reports the starting address of a symbol. As a +// result it's only enabled on a few platforms. +const ENABLED: bool = cfg!(all( + // Windows hasn't really been tested, and macOS doesn't support actually + // finding an enclosing frame, so disable this + target_os = "linux", + // On ARM finding the enclosing function is simply returning the ip itself. + not(target_arch = "arm"), +)); + +#[test] +#[inline(never)] +fn backtrace_new_unresolved_should_start_with_call_site_trace() { + if !ENABLED { + return; + } + let mut b = Backtrace::new_unresolved(); + b.resolve(); + println!("{b:?}"); + + assert!(!b.frames().is_empty()); + + let this_ip = backtrace_new_unresolved_should_start_with_call_site_trace as *mut c_void; + println!("this_ip: {:p}", this_ip); + let frame_ip = b.frames().first().unwrap().symbol_address(); + assert_eq!(this_ip, frame_ip); +} + +#[test] +#[inline(never)] +fn backtrace_new_should_start_with_call_site_trace() { + if !ENABLED { + return; + } + let b = Backtrace::new(); + println!("{b:?}"); + + assert!(!b.frames().is_empty()); + + let this_ip = backtrace_new_should_start_with_call_site_trace as *mut c_void; + let frame_ip = b.frames().first().unwrap().symbol_address(); + assert_eq!(this_ip, frame_ip); + + let trace = format!("{b:?}"); + // FIXME: need more stacktrace content tests + assert!(trace.ends_with("\n")); +}
diff --git a/chromium_crates_io/vendor/backtrace-v0_3/tests/smoke.rs b/chromium_crates_io/vendor/backtrace-v0_3/tests/smoke.rs new file mode 100644 index 0000000..fd5684f --- /dev/null +++ b/chromium_crates_io/vendor/backtrace-v0_3/tests/smoke.rs
@@ -0,0 +1,325 @@ +use backtrace::Frame; +use core::ffi::c_void; +use std::ptr; +use std::thread; + +fn get_actual_fn_pointer(fp: *mut c_void) -> *mut c_void { + // On AIX, the function name references a function descriptor. + // A function descriptor consists of (See https://reviews.llvm.org/D62532) + // * The address of the entry point of the function. + // * The TOC base address for the function. + // * The environment pointer. + // Deref `fp` directly so that we can get the address of `fp`'s + // entry point in text section. + // + // For TOC, one can find more information in + // https://www.ibm.com/docs/en/aix/7.2?topic=program-understanding-programming-toc + if cfg!(target_os = "aix") { + unsafe { + let actual_fn_entry = *(fp as *const *mut c_void); + actual_fn_entry + } + } else { + fp as *mut c_void + } +} + +#[test] +// FIXME: shouldn't ignore this test on i686-msvc, unsure why it's failing +#[cfg_attr(all(target_arch = "x86", target_env = "msvc"), ignore)] +#[inline(never)] +#[rustfmt::skip] // we care about line numbers here +fn smoke_test_frames() { + frame_1(line!()); + #[inline(never)] fn frame_1(start_line: u32) { frame_2(start_line) } + #[inline(never)] fn frame_2(start_line: u32) { frame_3(start_line) } + #[inline(never)] fn frame_3(start_line: u32) { frame_4(start_line) } + #[inline(never)] fn frame_4(start_line: u32) { + let mut v = Vec::new(); + backtrace::trace(|cx| { + v.push(cx.clone()); + true + }); + + // Various platforms have various bits of weirdness about their + // backtraces. To find a good starting spot let's search through the + // frames + let target = get_actual_fn_pointer(frame_4 as *mut c_void); + let offset = v + .iter() + .map(|frame| frame.symbol_address()) + .enumerate() + .filter_map(|(i, sym)| { + if sym >= target { + Some((sym, i)) + } else { + None + } + }) + .min() + .unwrap() + .1; + let mut frames = v[offset..].iter(); + + assert_frame( + frames.next().unwrap(), + get_actual_fn_pointer(frame_4 as *mut c_void) as usize, + "frame_4", + "tests/smoke.rs", + start_line + 6, + 9, + ); + assert_frame( + frames.next().unwrap(), + get_actual_fn_pointer(frame_3 as *mut c_void) as usize, + "frame_3", + "tests/smoke.rs", + start_line + 3, + 52, + ); + assert_frame( + frames.next().unwrap(), + get_actual_fn_pointer(frame_2 as *mut c_void) as usize, + "frame_2", + "tests/smoke.rs", + start_line + 2, + 52, + ); + assert_frame( + frames.next().unwrap(), + get_actual_fn_pointer(frame_1 as *mut c_void) as usize, + "frame_1", + "tests/smoke.rs", + start_line + 1, + 52, + ); + assert_frame( + frames.next().unwrap(), + get_actual_fn_pointer(smoke_test_frames as *mut c_void) as usize, + "smoke_test_frames", + "", + 0, + 0, + ); + } + + fn assert_frame( + frame: &Frame, + actual_fn_pointer: usize, + expected_name: &str, + expected_file: &str, + expected_line: u32, + expected_col: u32, + ) { + backtrace::resolve_frame(frame, |sym| { + print!("symbol ip:{:?} address:{:?} ", frame.ip(), frame.symbol_address()); + if let Some(name) = sym.name() { + print!("name:{name} "); + } + if let Some(file) = sym.filename() { + print!("file:{} ", file.display()); + } + if let Some(lineno) = sym.lineno() { + print!("lineno:{lineno} "); + } + if let Some(colno) = sym.colno() { + print!("colno:{colno} "); + } + println!(); + }); + + let ip = frame.ip() as usize; + let sym = frame.symbol_address() as usize; + assert!(ip >= sym); + assert!( + sym >= actual_fn_pointer, + "{:?} < {:?} ({} {}:{}:{})", + sym as *const usize, + actual_fn_pointer as *const usize, + expected_name, + expected_file, + expected_line, + expected_col, + ); + + // windows dbghelp is *quite* liberal (and wrong) in many of its reports + // right now... + // + // This assertion can also fail for release builds, so skip it there + if cfg!(debug_assertions) { + assert!(sym - actual_fn_pointer < 1024); + } + + let mut resolved = 0; + + let mut name = None; + let mut addr = None; + let mut col = None; + let mut line = None; + let mut file = None; + backtrace::resolve_frame(frame, |sym| { + resolved += 1; + name = sym.name().map(|v| v.to_string()); + addr = sym.addr(); + col = sym.colno(); + line = sym.lineno(); + file = sym.filename().map(|v| v.to_path_buf()); + }); + assert!(resolved > 0); + + let name = name.expect("didn't find a name"); + + // in release mode names get weird as functions can get merged + // together with `mergefunc`, so only assert this in debug mode + if cfg!(debug_assertions) { + assert!( + name.contains(expected_name), + "didn't find `{expected_name}` in `{name}`" + ); + } + + addr.expect("didn't find a symbol"); + + if cfg!(debug_assertions) { + let line = line.expect("didn't find a line number"); + let file = file.expect("didn't find a line number"); + if !expected_file.is_empty() { + assert!( + file.ends_with(expected_file), + "{file:?} didn't end with {expected_file:?}" + ); + } + if expected_line != 0 { + assert_eq!( + line, + expected_line, + "bad line number on frame for `{expected_name}`: {line} != {expected_line}"); + } + + // dbghelp on MSVC doesn't support column numbers + if !cfg!(target_env = "msvc") { + let col = col.expect("didn't find a column number"); + if expected_col != 0 { + assert_eq!( + col, + expected_col, + "bad column number on frame for `{expected_name}`: {col} != {expected_col}"); + } + } + } + } +} + +#[test] +fn many_threads() { + let threads = (0..16) + .map(|_| { + thread::spawn(|| { + for _ in 0..16 { + backtrace::trace(|frame| { + backtrace::resolve(frame.ip(), |symbol| { + let _s = symbol.name().map(|s| s.to_string()); + }); + true + }); + } + }) + }) + .collect::<Vec<_>>(); + + for t in threads { + t.join().unwrap() + } +} + +#[test] +#[cfg(feature = "serde")] +fn is_serde() { + extern crate serde; + + fn is_serialize<T: serde::ser::Serialize>() {} + fn is_deserialize<T: serde::de::DeserializeOwned>() {} + + is_serialize::<backtrace::Backtrace>(); + is_deserialize::<backtrace::Backtrace>(); +} + +#[test] +fn sp_smoke_test() { + let mut refs = vec![]; + recursive_stack_references(&mut refs); + return; + + #[inline(never)] + fn recursive_stack_references(refs: &mut Vec<*mut c_void>) { + assert!(refs.len() < 5); + + let mut x = refs.len(); + refs.push(ptr::addr_of_mut!(x).cast()); + + if refs.len() < 5 { + recursive_stack_references(refs); + eprintln!("exiting: {x}"); + return; + } + + backtrace::trace(make_trace_closure(refs)); + eprintln!("exiting: {x}"); + } + + // NB: the following `make_*` functions are pulled out of line, rather than + // defining their results as inline closures at their call sites, so that + // the resulting closures don't have "recursive_stack_references" in their + // mangled names. + + fn make_trace_closure<'a>( + refs: &'a mut Vec<*mut c_void>, + ) -> impl FnMut(&backtrace::Frame) -> bool + 'a { + let mut child_sp = None; + let mut child_ref = None; + move |frame| { + eprintln!("\n=== frame ==================================="); + + let mut is_recursive_stack_references = false; + backtrace::resolve(frame.ip(), |sym| { + is_recursive_stack_references |= + sym.name() + .and_then(|name| name.as_str()) + .map_or(false, |name| { + eprintln!("name = {name}"); + name.contains("recursive_stack_references") + }) + }); + + let sp = frame.sp(); + eprintln!("sp = {sp:p}"); + if sp as usize == 0 { + // If the SP is null, then we don't have an implementation for + // getting the SP on this target. Just keep walking the stack, + // but don't make our assertions about the on-stack pointers and + // SP values. + return true; + } + + // The stack grows down. + if let Some(child_sp) = child_sp { + assert!(child_sp <= sp); + } + + if is_recursive_stack_references { + let r = refs.pop().unwrap(); + eprintln!("ref = {:p}", r); + if sp as usize != 0 { + assert!(r > sp); + if let Some(child_ref) = child_ref { + assert!(sp >= child_ref); + } + } + child_ref = Some(r); + } + + child_sp = Some(sp); + true + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/.cargo-checksum.json b/chromium_crates_io/vendor/gimli-v0_32/.cargo-checksum.json new file mode 100644 index 0000000..697c9ce --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/.cargo-checksum.json
@@ -0,0 +1 @@ +{"files":{}}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/.cargo_vcs_info.json b/chromium_crates_io/vendor/gimli-v0_32/.cargo_vcs_info.json new file mode 100644 index 0000000..1f7dc46 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "8bc8e622fcb9be20fc9f03c96bc6335d936b869d" + }, + "path_in_vcs": "" +} \ No newline at end of file
diff --git a/chromium_crates_io/vendor/gimli-v0_32/CHANGELOG.md b/chromium_crates_io/vendor/gimli-v0_32/CHANGELOG.md new file mode 100644 index 0000000..a7a2b7e --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/CHANGELOG.md
@@ -0,0 +1,1280 @@ +# `gimli` Change Log + +-------------------------------------------------------------------------------- + +## 0.32.3 + +Released 2025/09/13. + +### Changed + +* Changed parsing to accept -2 for tombstone values in `.debug_line`, + `.debug_aranges`, `.debug_loclists`, and `.debug_rnglists`. + [#791](https://github.com/gimli-rs/gimli/pull/791) + +### Added + +* Added more x86-64 register definitions. + [#794](https://github.com/gimli-rs/gimli/pull/794) + +-------------------------------------------------------------------------------- + +## 0.32.2 + +Released 2025/08/26. + +### Changed + +* Removed `PartialEq<Debug*Offset>` implementations for `UnitSectionOffset`. + These were an unintended breaking change. + [#789](https://github.com/gimli-rs/gimli/pull/789) + +-------------------------------------------------------------------------------- + +## 0.32.1 + +Released 2025/08/22. + +### Changed + +* Improved handling of invalid DIE references during writing. + [#777](https://github.com/gimli-rs/gimli/pull/777) + [#779](https://github.com/gimli-rs/gimli/pull/779) + +* Changed abbreviation parsing to allow a missing null terminator. + [#781](https://github.com/gimli-rs/gimli/pull/781) + +* Changed `write::LineProgram` to support any form for file source code. + [#784](https://github.com/gimli-rs/gimli/pull/784) + [#786](https://github.com/gimli-rs/gimli/pull/786) + +### Added + +* Added DWARF version 1.1 constant definitions. + [#775](https://github.com/gimli-rs/gimli/pull/775) + +-------------------------------------------------------------------------------- + +## 0.32.0 + +Released 2025/06/11. + +### Breaking changes + +* Added `read::Dwarf::debug_macinfo`, `read::Dwarf::debug_macro`, and associated + support. + [#759](https://github.com/gimli-rs/gimli/pull/759) + [#763](https://github.com/gimli-rs/gimli/pull/763) + [#772](https://github.com/gimli-rs/gimli/pull/772) + +* Removed `#[non_exhaustive]` from `read::CallFrameInstruction`. + [#764](https://github.com/gimli-rs/gimli/pull/764) + +* Added `source_dir` parameter to `write::LineProgram::new`. + [#768](https://github.com/gimli-rs/gimli/pull/768) + +### Changed + +* Fixed spelling in `Error::UnknownCallFrameInstruction` description. + [#758](https://github.com/gimli-rs/gimli/pull/758) + +* Removed `compiler-builtins` from `rustc-dep-of-std` dependencies. + [#769](https://github.com/gimli-rs/gimli/pull/769) + +* Changed `write::UnitTable::from` to ignore `DW_AT_GNU_locviews` attributes. + [#771](https://github.com/gimli-rs/gimli/pull/771) + +### Added + +* Added `DebugAddr::headers`. + [#760](https://github.com/gimli-rs/gimli/pull/760) + +-------------------------------------------------------------------------------- + +## 0.31.1 + +Released 2024/10/04. + +### Changed + +* Changed `read::Evaluation::evaluate` to validate `DW_OP_deref_size`. + [#739](https://github.com/gimli-rs/gimli/pull/739) + +* Changed `write::LineProgram` to allow use of file index 0 for DWARF version 5. + [#740](https://github.com/gimli-rs/gimli/pull/740) + +* Improved the workaround for reading zero length entries in `.debug_frame`. + [#741](https://github.com/gimli-rs/gimli/pull/741) + +* Implemented `Default` for `read::DwarfSections` and `read::DwarfPackageSections`. + [#742](https://github.com/gimli-rs/gimli/pull/742) + +* Changed `read::ArangeEntryIter` to handle tombstones in `.debug_aranges`. + [#743](https://github.com/gimli-rs/gimli/pull/743) + +* Improved handling handling of 0 for tombstones in `DW_LNE_set_address` + and address pairs in ranges and locations. + [#750](https://github.com/gimli-rs/gimli/pull/750) + +* Changed the `read::ArrayLike` trait implementation to use const generics. + [#752](https://github.com/gimli-rs/gimli/pull/752) + +### Added + +* Added `MIPS::HI` and `MIPS::LO`. + [#749](https://github.com/gimli-rs/gimli/pull/749) + +-------------------------------------------------------------------------------- + +## 0.31.0 + +Released 2024/07/16. + +### Breaking changes + +* Deleted support for segment selectors. + [#720](https://github.com/gimli-rs/gimli/pull/720) + +* Added `read::FileEntry::source` and deleted `Copy` implementation. + [#728](https://github.com/gimli-rs/gimli/pull/728) + +* Changed `read::LineRow::execute` to return a `Result`. + [#731](https://github.com/gimli-rs/gimli/pull/731) + +* Deleted `Display` implementation for `read::LineInstruction`. + [#734](https://github.com/gimli-rs/gimli/pull/734) + +* Changed `read::Error` to be non-exhaustive. + +### Changed + +* Fixed `Hash` implementation for `read::EndianReader`. + [#723](https://github.com/gimli-rs/gimli/pull/723) + +* Changed `read::EhFrameHdr::parse` to validate the FDE count encoding. + [#725](https://github.com/gimli-rs/gimli/pull/725) + +* Changed address overflow to be an error for `read::UnwindTableRow`, + `read::LineRow`, and `read::ArangeEntry`. + [#730](https://github.com/gimli-rs/gimli/pull/730) + [#731](https://github.com/gimli-rs/gimli/pull/731) + [#732](https://github.com/gimli-rs/gimli/pull/732) + +* Changed wrapping addition for 32-bit addresses to wrap at 32 bits instead of + at 64 bits. + [#733](https://github.com/gimli-rs/gimli/pull/733) + +* Added earlier validation of address sizes. + [#733](https://github.com/gimli-rs/gimli/pull/733) + +### Added + +* Added `read::IndexSectionId::section_id`. + [#719](https://github.com/gimli-rs/gimli/pull/719) + +* Added `read::FrameDescriptionEntry::end_address`. + [#727](https://github.com/gimli-rs/gimli/pull/727) + +* Added support for `DW_LNCT_LLVM_source`. + [#728](https://github.com/gimli-rs/gimli/pull/728) + +-------------------------------------------------------------------------------- + +## 0.30.0 + +Released 2024/05/26. + +### Breaking changes + +* Added context to some `read::Error` variants. + [#703](https://github.com/gimli-rs/gimli/pull/703) + +* Changed type of `read::UnitIndexSection::section` to `IndexSectionId`. + [#716](https://github.com/gimli-rs/gimli/pull/716) + +### Changed + +* Fixed `write::Operation::ImplicitPointer::size`. + [#712](https://github.com/gimli-rs/gimli/pull/712) + +* Changed `read::RngListIter` and `read::LocListIter` to skip ranges where + the end is before the beginning, instead of returning an error. + [#715](https://github.com/gimli-rs/gimli/pull/715) + +* Fixed clippy warnings. + [#713](https://github.com/gimli-rs/gimli/pull/713) + +### Added + +* Added `read::UnitRef`. + [#711](https://github.com/gimli-rs/gimli/pull/711) + +-------------------------------------------------------------------------------- + +## 0.29.0 + +Released 2024/04/11. + +### Breaking changes + +* Changed `Reader` type parameter to `ReaderOffset` for `read::UnwindContext` and related types. + Replaced `Expression` with `UnwindExpression` in unwind information types. + [#703](https://github.com/gimli-rs/gimli/pull/703) + +### Changed + +* Changed `write::Sections::for_each` and `for_each_mut` to specify section lifetime. + [#699](https://github.com/gimli-rs/gimli/pull/699) + +* Fixed writing unwind information with an LSDA encoding that is not `DW_EH_PE_absptr`. + [#704](https://github.com/gimli-rs/gimli/pull/704) + +* Fixed parsing for an empty DWP index. + [#706](https://github.com/gimli-rs/gimli/pull/706) + +* Improved error handling in `read::Unit::dwo_name`. + [#693](https://github.com/gimli-rs/gimli/pull/693) + +* Fixed warnings. + [#692](https://github.com/gimli-rs/gimli/pull/692) + [#694](https://github.com/gimli-rs/gimli/pull/694) + [#695](https://github.com/gimli-rs/gimli/pull/695) + [#696](https://github.com/gimli-rs/gimli/pull/696) + +### Added + +* Added MIPS register definitions. + [#690](https://github.com/gimli-rs/gimli/pull/690) + +* Added PowerPC register definitions. + [#691](https://github.com/gimli-rs/gimli/pull/691) + +* Added `read::DwarfSections` and `read::DwarfPackageSections`. + [#698](https://github.com/gimli-rs/gimli/pull/698) + +* Implemented `BitOr` for `DwEhPe`. + [#709](https://github.com/gimli-rs/gimli/pull/709) + +* Added `read::Relocate`, `read::RelocateReader`, and `write::RelocateWriter`. + [#709](https://github.com/gimli-rs/gimli/pull/709) + +-------------------------------------------------------------------------------- + +## 0.28.1 + +Released 2023/11/24. + +### Changed + +* Changed `read::AbbreviationsCache` to require manual population using + `Dwarf::populate_abbreviations_cache`. + [#679](https://github.com/gimli-rs/gimli/pull/679) + +* Changed the default `read::UnwindContextStorage` to use `Box` instead of `Vec` + so that its memory usage is limited. + [#687](https://github.com/gimli-rs/gimli/pull/687) + +* Changed `read::UnwindTable::new` to always reset the context, because + previous errors may have left the context in an invalid state. + [#684](https://github.com/gimli-rs/gimli/pull/684) + +* Changed the `Debug` implementation for `read::EndianSlice` to limit the number + of bytes it displays. + [#686](https://github.com/gimli-rs/gimli/pull/686) + +### Added + +* Added more AArch64 register definitions. + [#680](https://github.com/gimli-rs/gimli/pull/680) + +* Added `read::Unit::new_with_abbreviations`. + [#677](https://github.com/gimli-rs/gimli/pull/677) + +* Added `read::Evaluation::value_result`. + [#676](https://github.com/gimli-rs/gimli/pull/676) + +-------------------------------------------------------------------------------- + +## 0.28.0 + +Released 2023/08/12. + +### Breaking changes + +* Deleted `impl From<EndianSlice> for &[u8]`. Use `EndianSlice::slice` instead. + [#669](https://github.com/gimli-rs/gimli/pull/669) + +* Deleted `impl Index<usize> for EndianSlice` and + `impl Index<RangeFrom<usize>> for EndianSlice`. + [#669](https://github.com/gimli-rs/gimli/pull/669) + +* Replaced `impl From<Pointer> for u64` with `Pointer::pointer`. + [#670](https://github.com/gimli-rs/gimli/pull/670) + +* Updated `fallible-iterator` to 0.3.0. + [#672](https://github.com/gimli-rs/gimli/pull/672) + +* Changed some optional dependencies to use the `dep:` feature syntax. + [#672](https://github.com/gimli-rs/gimli/pull/672) + +* Added `non_exhaustive` attribute to `read::RegisterRule`, + `read::CallFrameInstruction`, and `write::CallFrameInstruction`. + [#673](https://github.com/gimli-rs/gimli/pull/673) + +### Changed + +* The minimum supported rust version for the `read` feature and its dependencies + increased to 1.60.0. + +* The minimum supported rust version for other features increased to 1.65.0. + +### Added + +* Added `Vendor`, `read::DebugFrame::set_vendor`, and `read::EhFrame::set_vendor`. + [#673](https://github.com/gimli-rs/gimli/pull/673) + +* Added more ARM and AArch64 register definitions, and + `DW_CFA_AARCH64_negate_ra_state` support. + [#673](https://github.com/gimli-rs/gimli/pull/673) + +-------------------------------------------------------------------------------- + +## 0.27.3 + +Released 2023/06/14. + +### Changed + +* Excluded test fixtures from published package. + [#661](https://github.com/gimli-rs/gimli/pull/661) + +### Added + +* Added `FallibleIterator` implementation for `read::OperationIter`. + [#649](https://github.com/gimli-rs/gimli/pull/649) + +* Added `DW_AT_GNU_deleted` constant. + [#658](https://github.com/gimli-rs/gimli/pull/658) + +-------------------------------------------------------------------------------- + +## 0.27.2 + +Released 2023/02/15. + +### Added + +* Added support for tombstones in `read::LineRows`. + [#642](https://github.com/gimli-rs/gimli/pull/642) + +-------------------------------------------------------------------------------- + +## 0.27.1 + +Released 2023/01/23. + +### Added + +* Added `SectionId::xcoff_name` and `read::Section::xcoff_section_name`. + [#635](https://github.com/gimli-rs/gimli/pull/635) + +* Added `read::Dwarf::make_dwo` and `read::Unit::dwo_name`. + [#637](https://github.com/gimli-rs/gimli/pull/637) + +### Changed + +* Changed `read::DwarfPackage::sections` to handle supplementary files. + [#638](https://github.com/gimli-rs/gimli/pull/638) + +-------------------------------------------------------------------------------- + +## 0.27.0 + +Released 2022/11/23. + +### Breaking changes + +* Added `read::Dwarf::abbreviations_cache` to cache abbreviations at offset 0. + Changed `read::Dwarf::abbreviations` to return `Result<Arc<Abbreviations>>`, + and changed `read::Unit::abbreviations` to `Arc<Abbreviations>`. + [#628](https://github.com/gimli-rs/gimli/pull/628) + +### Added + +* Added LoongArch register definitions. + [#624](https://github.com/gimli-rs/gimli/pull/624) + +* Added support for tombstones in `read::LocListIter` and `read::RngListIter`. + [#631](https://github.com/gimli-rs/gimli/pull/631) + +-------------------------------------------------------------------------------- + +## 0.26.2 + +Released 2022/07/16. + +### Changed + +* Fixed CFI personality encoding when writing. + [#609](https://github.com/gimli-rs/gimli/pull/609) + +* Fixed use of raw pointer for mutation, detected by Miri. + [#614](https://github.com/gimli-rs/gimli/pull/614) + +* Fixed `DW_OP_GNU_implicit_pointer` handling for DWARF version 2. + [#618](https://github.com/gimli-rs/gimli/pull/618) + +### Added + +* Added `read::EhHdrTable::iter`. + [#619](https://github.com/gimli-rs/gimli/pull/619) + +-------------------------------------------------------------------------------- + +## 0.26.1 + +Released 2021/11/02. + +### Changed + +* Fixed segmentation fault in `ArrayVec<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 evaluating CFI instructions + that advance the location. + [#401](https://github.com/gimli-rs/gimli/pull/401) + +* Fixed parsing of pointers encoded with `DW_EH_PE_funcrel`. + [#402](https://github.com/gimli-rs/gimli/pull/402) + +* Use the FDE address encoding from the augmentation when parsing `DW_CFA_set_loc`. + [#403](https://github.com/gimli-rs/gimli/pull/403) + +* Fixed setting of `.eh_frame` base addresses in dwarfdump. + [#410](https://github.com/gimli-rs/gimli/pull/410) + +## 0.17.0 + +Released 2019/02/21. + +The focus of this release has been on improving DWARF 5 support, and +adding support for writing DWARF. + +### Breaking changes + +* Changed register values to a `Register` type instead of `u8`/`u64`. + [#328](https://github.com/gimli-rs/gimli/pull/328) + +* Replaced `BaseAddresses::set_cfi` with `set_eh_frame_hdr` and `set_eh_frame`. + Replaced `BaseAddresses::set_data` with `set_got`. + You should now use the same `BaseAddresses` value for parsing both + `.eh_frame` and `.eh_frame_hdr`. + [#351](https://github.com/gimli-rs/gimli/pull/351) + +* Renamed many types and functions related to `.debug_line`. + Renamed `LineNumberProgram` to `LineProgram`. + Renamed `IncompleteLineNumberProgram` to `IncompleteLineProgram`. + Renamed `CompleteLineNumberProgram` to `CompleteLineProgram`. + Renamed `LineNumberProgramHeader` to `LineProgramHeader`. + Renamed `LineNumberRow` to `LineRow`. + Renamed `StateMachine` to `LineRows`. + Renamed `Opcode` to `LineInstruction`. + Renamed `OpcodesIter` to `LineInstructions`. + Renamed `LineNumberSequence` to `LineSequence`. + [#359](https://github.com/gimli-rs/gimli/pull/359) + +* Added `Offset` type parameter to `AttributeValue`, `LineProgram`, + `IncompleteLineProgram`, `CompleteLineProgram`, `LineRows`, `LineInstruction`, + and `FileEntry`. + [#324](https://github.com/gimli-rs/gimli/pull/324) + +* Changed `FileEntry::path_name`, `FileEntry::directory`, and + `LineProgramHeader::directory` to return an `AttributeValue` instead + of a `Reader`. + [#366](https://github.com/gimli-rs/gimli/pull/366) + +* Renamed `FileEntry::last_modification` to `FileEntry::timestamp` + and renamed `FileEntry::length` to `FileEntry::size`. + [#366](https://github.com/gimli-rs/gimli/pull/366) + +* Added an `Encoding` type. Changed many functions that previously accepted + `Format`, version or address size parameters to accept an `Encoding` + parameter instead. + Notable changes are `LocationLists::locations`, `RangeLists::ranges`, + and `Expression::evaluation`. + [#364](https://github.com/gimli-rs/gimli/pull/364) + +* Changed return type of `LocationLists::new` and `RangeLists::new`. + [#370](https://github.com/gimli-rs/gimli/pull/370) + +* Added parameters to `LocationsLists::locations` and `RangeLists::ranges` + to support `.debug_addr`. + [#358](https://github.com/gimli-rs/gimli/pull/358) + +* Added more `AttributeValue` variants: `DebugAddrBase`, `DebugAddrIndex`, + `DebugLocListsBase`, `DebugLocListsIndex`, `DebugRngListsBase`, `DebugRngListsIndex`, + `DebugStrOffsetsBase`, `DebugStrOffsetsIndex`, `DebugLineStrRef`. + [#358](https://github.com/gimli-rs/gimli/pull/358) + +* Changed `AttributeValue::Data*` attributes to native endian integers instead + of byte arrays. + [#365](https://github.com/gimli-rs/gimli/pull/365) + +* Replaced `EvaluationResult::TextBase` with + `EvaluationResult::RequiresRelocatedAddress`. The handling of `TextBase` + was incorrect. + [#335](https://github.com/gimli-rs/gimli/pull/335) + +* Added `EvaluationResult::IndexedAddress` for operations that require an + address from `.debug_addr`. + [#358](https://github.com/gimli-rs/gimli/pull/358) + +* Added `Reader::read_slice`. Added a default implementation of + `Reader::read_u8_array` which uses this. + [#358](https://github.com/gimli-rs/gimli/pull/358) + +### Added + +* Added initial support for writing DWARF. This is targeted at supporting + line number information only. + [#340](https://github.com/gimli-rs/gimli/pull/340) + [#344](https://github.com/gimli-rs/gimli/pull/344) + [#346](https://github.com/gimli-rs/gimli/pull/346) + [#361](https://github.com/gimli-rs/gimli/pull/361) + [#362](https://github.com/gimli-rs/gimli/pull/362) + [#365](https://github.com/gimli-rs/gimli/pull/365) + [#368](https://github.com/gimli-rs/gimli/pull/368) + [#382](https://github.com/gimli-rs/gimli/pull/382) + +* Added `read` and `write` Cargo features. Both are enabled by default. + [#343](https://github.com/gimli-rs/gimli/pull/343) + +* Added support for reading DWARF 5 `.debug_line` and `.debug_line_str` sections. + [#366](https://github.com/gimli-rs/gimli/pull/366) + +* Added support for reading DWARF 5 `.debug_str_offsets` sections, including + parsing `DW_FORM_strx*` attributes. + [#358](https://github.com/gimli-rs/gimli/pull/358) + +* Added support for reading DWARF 5 `.debug_addr` sections, including parsing + `DW_FORM_addrx*` attributes and evaluating `DW_OP_addrx` and `DW_OP_constx` + operations. + [#358](https://github.com/gimli-rs/gimli/pull/358) + +* Added support for reading DWARF 5 indexed addresses and offsets in + `.debug_loclists` and `.debug_rnglists`, including parsing `DW_FORM_rnglistx` + and `DW_FORM_loclistx` attributes. + [#358](https://github.com/gimli-rs/gimli/pull/358) + +* Added high level `Dwarf` and `Unit` types. Existing code does not need to + switch to using these types, but doing so will make DWARF 5 support simpler. + [#352](https://github.com/gimli-rs/gimli/pull/352) + [#380](https://github.com/gimli-rs/gimli/pull/380) + [#381](https://github.com/gimli-rs/gimli/pull/381) + +* Added `EhFrame::set_address_size` and `DebugFrame::set_address_size` methods + to allow parsing non-native CFI sections. The default address size is still + the native size. + [#325](https://github.com/gimli-rs/gimli/pull/325) + +* Added architecture specific definitions for `Register` values and names. + Changed dwarfdump to print them. + [#328](https://github.com/gimli-rs/gimli/pull/328) + +* Added support for reading relocatable DWARF sections. + [#337](https://github.com/gimli-rs/gimli/pull/337) + +* Added parsing of `DW_FORM_data16`. + [#366](https://github.com/gimli-rs/gimli/pull/366) + +### Fixed + +* Fixed parsing DWARF 5 ranges with `start == end == 0`. + [#323](https://github.com/gimli-rs/gimli/pull/323) + +* Changed `LineRows` to be covariant in its `Reader` type parameter. + [#324](https://github.com/gimli-rs/gimli/pull/324) + +* Fixed handling of empty units in dwarfdump. + [#330](https://github.com/gimli-rs/gimli/pull/330) + +* Fixed `UnitHeader::length_including_self` for `Dwarf64`. + [#342](https://github.com/gimli-rs/gimli/pull/342) + +* Fixed parsing of `DW_CFA_set_loc`. + [#355](https://github.com/gimli-rs/gimli/pull/355) + +* Fixed handling of multiple headers in `.debug_loclists` and `.debug_rnglists`. + [#370](https://github.com/gimli-rs/gimli/pull/370) + +-------------------------------------------------------------------------------- + +## 0.16.1 + +Released 2018/08/28. + +### Added + +* Added `EhFrameHdr::lookup_and_parse`. [#316][] +* Added support for `DW_CFA_GNU_args_size`. [#319][] + +### Fixed + +* Implement `Send`/`Sync` for `SubRange`. [#305][] +* Fixed `alloc` support on nightly. [#306][] [#310][] + +[#305]: https://github.com/gimli-rs/gimli/pull/305 +[#306]: https://github.com/gimli-rs/gimli/pull/306 +[#310]: https://github.com/gimli-rs/gimli/pull/310 +[#316]: https://github.com/gimli-rs/gimli/pull/316 +[#319]: https://github.com/gimli-rs/gimli/pull/319 + +-------------------------------------------------------------------------------- + +## 0.16.0 + +Released 2018/06/01. + +### Added + +* Added support for building in `#![no_std]` environments, when the `alloc` + crate is available. Disable the "std" feature and enable the "alloc" + feature. [#138][] [#271][] + +* Added support for DWARF 5 `.debug_rnglists` and `.debug_loclists` + sections. [#272][] + +* Added support for DWARF 5 `DW_FORM_ref_sup` and `DW_FORM_strp_sup` attribute + forms. [#288][] + +* Added support for DWARF 5 operations on typed values. [#293][] + +* A `dwarf-validate` example program that checks the integrity of the given + DWARF and its references between sections. [#290][] + +* Added the `EndianReader<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/chromium_crates_io/vendor/gimli-v0_32/Cargo.lock b/chromium_crates_io/vendor/gimli-v0_32/Cargo.lock new file mode 100644 index 0000000..d81e77c --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/Cargo.lock
@@ -0,0 +1,76 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "gimli" +version = "0.32.3" +dependencies = [ + "fallible-iterator", + "indexmap", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "stable_deref_trait", + "test-assembler", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[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 = "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", +]
diff --git a/chromium_crates_io/vendor/gimli-v0_32/Cargo.toml b/chromium_crates_io/vendor/gimli-v0_32/Cargo.toml new file mode 100644 index 0000000..2041bdb --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/Cargo.toml
@@ -0,0 +1,114 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.60" +name = "gimli" +version = "0.32.3" +build = false +include = [ + "/CHANGELOG.md", + "/Cargo.toml", + "/LICENSE-APACHE", + "/LICENSE-MIT", + "/README.md", + "/src", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A library for reading and writing the DWARF debugging format." +documentation = "https://docs.rs/gimli" +readme = "README.md" +keywords = [ + "DWARF", + "debug", + "ELF", + "eh_frame", +] +categories = [ + "development-tools::debugging", + "development-tools::profiling", + "parser-implementations", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/gimli-rs/gimli" +resolver = "2" + +[features] +default = [ + "read-all", + "write", +] +endian-reader = [ + "read", + "dep:stable_deref_trait", +] +fallible-iterator = ["dep:fallible-iterator"] +read = ["read-core"] +read-all = [ + "read", + "std", + "fallible-iterator", + "endian-reader", +] +read-core = [] +rustc-dep-of-std = [ + "dep:core", + "dep:alloc", +] +std = [ + "fallible-iterator?/std", + "stable_deref_trait?/std", +] +write = ["dep:indexmap"] + +[lib] +name = "gimli" +path = "src/lib.rs" + +[dependencies.alloc] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-alloc" + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" + +[dependencies.fallible-iterator] +version = "0.3.0" +optional = true +default-features = false + +[dependencies.indexmap] +version = "2.0.0" +optional = true + +[dependencies.stable_deref_trait] +version = "1.1.0" +optional = true +default-features = false + +[dev-dependencies.test-assembler] +version = "0.1.3" + +[profile.bench] +codegen-units = 1 +debug = 2 +split-debuginfo = "packed" + +[profile.test] +split-debuginfo = "packed"
diff --git a/chromium_crates_io/vendor/gimli-v0_32/Cargo.toml.orig b/chromium_crates_io/vendor/gimli-v0_32/Cargo.toml.orig new file mode 100644 index 0000000..6baee9c --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/Cargo.toml.orig
@@ -0,0 +1,60 @@ +[package] +name = "gimli" +version = "0.32.3" +categories = ["development-tools::debugging", "development-tools::profiling", "parser-implementations"] +description = "A library for reading and writing the DWARF debugging format." +documentation = "https://docs.rs/gimli" +edition = "2018" +include = [ + "/CHANGELOG.md", + "/Cargo.toml", + "/LICENSE-APACHE", + "/LICENSE-MIT", + "/README.md", + "/src", +] +keywords = ["DWARF", "debug", "ELF", "eh_frame"] +license = "MIT OR Apache-2.0" +readme = "./README.md" +repository = "https://github.com/gimli-rs/gimli" +rust-version = "1.60" + +[dependencies] +fallible-iterator = { version = "0.3.0", default-features = false, optional = true } +indexmap = { version = "2.0.0", optional = true } +stable_deref_trait = { version = "1.1.0", default-features = false, optional = true } + +# Internal feature, only used when building as part of libstd, not part of the +# stable interface of this crate. +core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } +alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" } + +[dev-dependencies] +test-assembler = "0.1.3" + +[features] +read-core = [] +read = ["read-core"] +read-all = ["read", "std", "fallible-iterator", "endian-reader"] +endian-reader = ["read", "dep:stable_deref_trait"] +fallible-iterator = ["dep:fallible-iterator"] +write = ["dep:indexmap"] +std = ["fallible-iterator?/std", "stable_deref_trait?/std"] +default = ["read-all", "write"] + +# Internal feature, only used when building as part of libstd, not part of the +# stable interface of this crate. +rustc-dep-of-std = ["dep:core", "dep:alloc"] + +[profile.test] +split-debuginfo = "packed" + +[profile.bench] +debug = true +codegen-units = 1 +split-debuginfo = "packed" + +[workspace] +members = ["crates/examples"] +default-members = [".", "crates/examples"] +resolver = "2"
diff --git a/chromium_crates_io/vendor/gimli-v0_32/LICENSE-APACHE b/chromium_crates_io/vendor/gimli-v0_32/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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/chromium_crates_io/vendor/gimli-v0_32/LICENSE-MIT b/chromium_crates_io/vendor/gimli-v0_32/LICENSE-MIT new file mode 100644 index 0000000..e69282e --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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/chromium_crates_io/vendor/gimli-v0_32/README.md b/chromium_crates_io/vendor/gimli-v0_32/README.md new file mode 100644 index 0000000..03bf6b8 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/README.md
@@ -0,0 +1,84 @@ +# `gimli` + +[ ](https://crates.io/crates/gimli) +[](https://docs.rs/gimli/) +[](https://github.com/gimli-rs/gimli/actions) +[](https://coveralls.io/github/gimli-rs/gimli?branch=master) + +`gimli` is a library for reading and writing the +[DWARF debugging format](https://dwarfstd.org/). + +* **Zero copy:** everything is just a reference to the original input buffer. No + copies of the input data get made. + +* **Lazy:** you can iterate compilation units without parsing their + contents. Parse only as many debugging information entry (DIE) trees as you + iterate over. `gimli` also uses `DW_AT_sibling` references to avoid parsing a + DIE's children to find its next sibling, when possible. + +* **Cross-platform:** `gimli` makes no assumptions about what kind of object + file you're working with. The flipside to that is that it's up to you to + provide an ELF loader on Linux or Mach-O loader on macOS. + + * Unsure which object file parser to use? Try the cross-platform + [`object`](https://github.com/gimli-rs/object) crate. See the + [`gimli-examples`](./crates/examples/src/bin) crate for usage with `gimli`. + +## Install + +To add a `gimli` dependency to your `Cargo.toml`, run: +```console +$ cargo add gimli +``` + +The minimum supported Rust version is: + +* 1.60.0 for the `read` feature and its dependencies. +* 1.65.0 for other features. + +## Documentation + +* [Documentation on docs.rs](https://docs.rs/gimli/) + +* Example programs: + + * [A simple `.debug_info` parser](./crates/examples/src/bin/simple.rs) + + * [A simple `.debug_line` parser](./crates/examples/src/bin/simple_line.rs) + + * [A simple DWARF writer](./crates/examples/src/bin/simple_write.rs) + + * [A simple DWARF converter](./crates/examples/src/bin/simple_convert.rs) + to read DWARF sections then write them back out again. + + * [A `dwarfdump` clone](./crates/examples/src/bin/dwarfdump.rs) + + * [An `addr2line` clone](https://github.com/gimli-rs/addr2line) + + * [`ddbug`](https://github.com/gimli-rs/ddbug), a utility giving insight into + code generation by making debugging information readable. + + * [`dwprod`](https://github.com/fitzgen/dwprod), a tiny utility to list the + compilers used to create each compilation unit within a shared library or + executable (via `DW_AT_producer`). + + * [`dwarf-validate`](./crates/examples/src/bin/dwarf-validate.rs), a program to validate the + integrity of some DWARF and its references between sections and compilation + units. + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([`LICENSE-MIT`](./LICENSE-MIT) or https://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +See [CONTRIBUTING.md](./CONTRIBUTING.md) for hacking. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions.
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/arch.rs b/chromium_crates_io/vendor/gimli-v0_32/src/arch.rs new file mode 100644 index 0000000..a005a16 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/arch.rs
@@ -0,0 +1,1118 @@ +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://github.com/ARM-software/abi-aa/blob/main/aadwarf32/aadwarf32.rst). +#[derive(Debug, Clone, Copy)] +pub struct Arm; + +registers!(Arm, { + R0 = (0, "R0"), + R1 = (1, "R1"), + R2 = (2, "R2"), + R3 = (3, "R3"), + R4 = (4, "R4"), + R5 = (5, "R5"), + R6 = (6, "R6"), + R7 = (7, "R7"), + R8 = (8, "R8"), + R9 = (9, "R9"), + R10 = (10, "R10"), + R11 = (11, "R11"), + R12 = (12, "R12"), + R13 = (13, "R13"), + R14 = (14, "R14"), + R15 = (15, "R15"), + + WCGR0 = (104, "wCGR0"), + WCGR1 = (105, "wCGR1"), + WCGR2 = (106, "wCGR2"), + WCGR3 = (107, "wCGR3"), + WCGR4 = (108, "wCGR4"), + WCGR5 = (109, "wCGR5"), + WCGR6 = (110, "wCGR6"), + WCGR7 = (111, "wCGR7"), + + WR0 = (112, "wR0"), + WR1 = (113, "wR1"), + WR2 = (114, "wR2"), + WR3 = (115, "wR3"), + WR4 = (116, "wR4"), + WR5 = (117, "wR5"), + WR6 = (118, "wR6"), + WR7 = (119, "wR7"), + WR8 = (120, "wR8"), + WR9 = (121, "wR9"), + WR10 = (122, "wR10"), + WR11 = (123, "wR11"), + WR12 = (124, "wR12"), + WR13 = (125, "wR13"), + WR14 = (126, "wR14"), + WR15 = (127, "wR15"), + + SPSR = (128, "SPSR"), + SPSR_FIQ = (129, "SPSR_FIQ"), + SPSR_IRQ = (130, "SPSR_IRQ"), + SPSR_ABT = (131, "SPSR_ABT"), + SPSR_UND = (132, "SPSR_UND"), + SPSR_SVC = (133, "SPSR_SVC"), + + RA_AUTH_CODE = (143, "RA_AUTH_CODE"), + + R8_USR = (144, "R8_USR"), + R9_USR = (145, "R9_USR"), + R10_USR = (146, "R10_USR"), + R11_USR = (147, "R11_USR"), + R12_USR = (148, "R12_USR"), + R13_USR = (149, "R13_USR"), + R14_USR = (150, "R14_USR"), + + R8_FIQ = (151, "R8_FIQ"), + R9_FIQ = (152, "R9_FIQ"), + R10_FIQ = (153, "R10_FIQ"), + R11_FIQ = (154, "R11_FIQ"), + R12_FIQ = (155, "R12_FIQ"), + R13_FIQ = (156, "R13_FIQ"), + R14_FIQ = (157, "R14_FIQ"), + + R13_IRQ = (158, "R13_IRQ"), + R14_IRQ = (159, "R14_IRQ"), + + R13_ABT = (160, "R13_ABT"), + R14_ABT = (161, "R14_ABT"), + + R13_UND = (162, "R13_UND"), + R14_UND = (163, "R14_UND"), + + R13_SVC = (164, "R13_SVC"), + R14_SVC = (165, "R14_SVC"), + + WC0 = (192, "wC0"), + WC1 = (193, "wC1"), + WC2 = (194, "wC2"), + WC3 = (195, "wC3"), + WC4 = (196, "wC4"), + WC5 = (197, "wC5"), + WC6 = (198, "wC6"), + WC7 = (199, "wC7"), + + D0 = (256, "D0"), + D1 = (257, "D1"), + D2 = (258, "D2"), + D3 = (259, "D3"), + D4 = (260, "D4"), + D5 = (261, "D5"), + D6 = (262, "D6"), + D7 = (263, "D7"), + D8 = (264, "D8"), + D9 = (265, "D9"), + D10 = (266, "D10"), + D11 = (267, "D11"), + D12 = (268, "D12"), + D13 = (269, "D13"), + D14 = (270, "D14"), + D15 = (271, "D15"), + D16 = (272, "D16"), + D17 = (273, "D17"), + D18 = (274, "D18"), + D19 = (275, "D19"), + D20 = (276, "D20"), + D21 = (277, "D21"), + D22 = (278, "D22"), + D23 = (279, "D23"), + D24 = (280, "D24"), + D25 = (281, "D25"), + D26 = (282, "D26"), + D27 = (283, "D27"), + D28 = (284, "D28"), + D29 = (285, "D29"), + D30 = (286, "D30"), + D31 = (287, "D31"), + + TPIDRURO = (320, "TPIDRURO"), + TPIDRURW = (321, "TPIDRURW"), + TPIDPR = (322, "TPIDPR"), + HTPIDPR = (323, "HTPIDPR"), +}, +aliases { + SP = (13, "SP"), + LR = (14, "LR"), + PC = (15, "PC"), + + ACC0 = (104, "ACC0"), + ACC1 = (105, "ACC1"), + ACC2 = (106, "ACC2"), + ACC3 = (107, "ACC3"), + ACC4 = (108, "ACC4"), + ACC5 = (109, "ACC5"), + ACC6 = (110, "ACC6"), + ACC7 = (111, "ACC7"), + + S0 = (256, "S0"), + S1 = (256, "S1"), + S2 = (257, "S2"), + S3 = (257, "S3"), + S4 = (258, "S4"), + S5 = (258, "S5"), + S6 = (259, "S6"), + S7 = (259, "S7"), + S8 = (260, "S8"), + S9 = (260, "S9"), + S10 = (261, "S10"), + S11 = (261, "S11"), + S12 = (262, "S12"), + S13 = (262, "S13"), + S14 = (263, "S14"), + S15 = (263, "S15"), + S16 = (264, "S16"), + S17 = (264, "S17"), + S18 = (265, "S18"), + S19 = (265, "S19"), + S20 = (266, "S20"), + S21 = (266, "S21"), + S22 = (267, "S22"), + S23 = (267, "S23"), + S24 = (268, "S24"), + S25 = (268, "S25"), + S26 = (269, "S26"), + S27 = (269, "S27"), + S28 = (270, "S28"), + S29 = (270, "S29"), + S30 = (271, "S30"), + S31 = (271, "S31"), +}); + +/// ARM 64-bit (AArch64) architecture specific definitions. +/// +/// See [DWARF for the ARM 64-bit Architecture]( +/// https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst). +#[derive(Debug, Clone, Copy)] +pub struct AArch64; + +registers!(AArch64, { + X0 = (0, "X0"), + X1 = (1, "X1"), + X2 = (2, "X2"), + X3 = (3, "X3"), + X4 = (4, "X4"), + X5 = (5, "X5"), + X6 = (6, "X6"), + X7 = (7, "X7"), + X8 = (8, "X8"), + X9 = (9, "X9"), + X10 = (10, "X10"), + X11 = (11, "X11"), + X12 = (12, "X12"), + X13 = (13, "X13"), + X14 = (14, "X14"), + X15 = (15, "X15"), + X16 = (16, "X16"), + X17 = (17, "X17"), + X18 = (18, "X18"), + X19 = (19, "X19"), + X20 = (20, "X20"), + X21 = (21, "X21"), + X22 = (22, "X22"), + X23 = (23, "X23"), + X24 = (24, "X24"), + X25 = (25, "X25"), + X26 = (26, "X26"), + X27 = (27, "X27"), + X28 = (28, "X28"), + X29 = (29, "X29"), + X30 = (30, "X30"), + SP = (31, "SP"), + PC = (32, "PC"), + ELR_MODE = (33, "ELR_mode"), + RA_SIGN_STATE = (34, "RA_SIGN_STATE"), + TPIDRRO_EL0 = (35, "TPIDRRO_EL0"), + TPIDR_EL0 = (36, "TPIDR_EL0"), + TPIDR_EL1 = (37, "TPIDR_EL1"), + TPIDR_EL2 = (38, "TPIDR_EL2"), + TPIDR_EL3 = (39, "TPIDR_EL3"), + + VG = (46, "VG"), + FFR = (47, "FFR"), + + P0 = (48, "P0"), + P1 = (49, "P1"), + P2 = (50, "P2"), + P3 = (51, "P3"), + P4 = (52, "P4"), + P5 = (53, "P5"), + P6 = (54, "P6"), + P7 = (55, "P7"), + P8 = (56, "P8"), + P9 = (57, "P9"), + P10 = (58, "P10"), + P11 = (59, "P11"), + P12 = (60, "P12"), + P13 = (61, "P13"), + P14 = (62, "P14"), + P15 = (63, "P15"), + + V0 = (64, "V0"), + V1 = (65, "V1"), + V2 = (66, "V2"), + V3 = (67, "V3"), + V4 = (68, "V4"), + V5 = (69, "V5"), + V6 = (70, "V6"), + V7 = (71, "V7"), + V8 = (72, "V8"), + V9 = (73, "V9"), + V10 = (74, "V10"), + V11 = (75, "V11"), + V12 = (76, "V12"), + V13 = (77, "V13"), + V14 = (78, "V14"), + V15 = (79, "V15"), + V16 = (80, "V16"), + V17 = (81, "V17"), + V18 = (82, "V18"), + V19 = (83, "V19"), + V20 = (84, "V20"), + V21 = (85, "V21"), + V22 = (86, "V22"), + V23 = (87, "V23"), + V24 = (88, "V24"), + V25 = (89, "V25"), + V26 = (90, "V26"), + V27 = (91, "V27"), + V28 = (92, "V28"), + V29 = (93, "V29"), + V30 = (94, "V30"), + V31 = (95, "V31"), + + Z0 = (96, "Z0"), + Z1 = (97, "Z1"), + Z2 = (98, "Z2"), + Z3 = (99, "Z3"), + Z4 = (100, "Z4"), + Z5 = (101, "Z5"), + Z6 = (102, "Z6"), + Z7 = (103, "Z7"), + Z8 = (104, "Z8"), + Z9 = (105, "Z9"), + Z10 = (106, "Z10"), + Z11 = (107, "Z11"), + Z12 = (108, "Z12"), + Z13 = (109, "Z13"), + Z14 = (110, "Z14"), + Z15 = (111, "Z15"), + Z16 = (112, "Z16"), + Z17 = (113, "Z17"), + Z18 = (114, "Z18"), + Z19 = (115, "Z19"), + Z20 = (116, "Z20"), + Z21 = (117, "Z21"), + Z22 = (118, "Z22"), + Z23 = (119, "Z23"), + Z24 = (120, "Z24"), + Z25 = (121, "Z25"), + Z26 = (122, "Z26"), + Z27 = (123, "Z27"), + Z28 = (124, "Z28"), + Z29 = (125, "Z29"), + Z30 = (126, "Z30"), + Z31 = (127, "Z31"), +}); + +/// LoongArch architecture specific definitions. +/// +/// See [LoongArch ELF psABI specification](https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html). +#[derive(Debug, Clone, Copy)] +pub struct LoongArch; + +registers!(LoongArch, { + R0 = (0, "$r0"), + R1 = (1, "$r1"), + R2 = (2, "$r2"), + R3 = (3, "$r3"), + R4 = (4, "$r4"), + R5 = (5, "$r5"), + R6 = (6, "$r6"), + R7 = (7, "$r7"), + R8 = (8, "$r8"), + R9 = (9, "$r9"), + R10 = (10, "$r10"), + R11 = (11, "$r11"), + R12 = (12, "$r12"), + R13 = (13, "$r13"), + R14 = (14, "$r14"), + R15 = (15, "$r15"), + R16 = (16, "$r16"), + R17 = (17, "$r17"), + R18 = (18, "$r18"), + R19 = (19, "$r19"), + R20 = (20, "$r20"), + R21 = (21, "$r21"), + R22 = (22, "$r22"), + R23 = (23, "$r23"), + R24 = (24, "$r24"), + R25 = (25, "$r25"), + R26 = (26, "$r26"), + R27 = (27, "$r27"), + R28 = (28, "$r28"), + R29 = (29, "$r29"), + R30 = (30, "$r30"), + R31 = (31, "$r31"), + + F0 = (32, "$f0"), + F1 = (33, "$f1"), + F2 = (34, "$f2"), + F3 = (35, "$f3"), + F4 = (36, "$f4"), + F5 = (37, "$f5"), + F6 = (38, "$f6"), + F7 = (39, "$f7"), + F8 = (40, "$f8"), + F9 = (41, "$f9"), + F10 = (42, "$f10"), + F11 = (43, "$f11"), + F12 = (44, "$f12"), + F13 = (45, "$f13"), + F14 = (46, "$f14"), + F15 = (47, "$f15"), + F16 = (48, "$f16"), + F17 = (49, "$f17"), + F18 = (50, "$f18"), + F19 = (51, "$f19"), + F20 = (52, "$f20"), + F21 = (53, "$f21"), + F22 = (54, "$f22"), + F23 = (55, "$f23"), + F24 = (56, "$f24"), + F25 = (57, "$f25"), + F26 = (58, "$f26"), + F27 = (59, "$f27"), + F28 = (60, "$f28"), + F29 = (61, "$f29"), + F30 = (62, "$f30"), + F31 = (63, "$f31"), + FCC0 = (64, "$fcc0"), + FCC1 = (65, "$fcc1"), + FCC2 = (66, "$fcc2"), + FCC3 = (67, "$fcc3"), + FCC4 = (68, "$fcc4"), + FCC5 = (69, "$fcc5"), + FCC6 = (70, "$fcc6"), + FCC7 = (71, "$fcc7"), +}, +aliases { + ZERO = (0, "$zero"), + RA = (1, "$ra"), + TP = (2, "$tp"), + SP = (3, "$sp"), + A0 = (4, "$a0"), + A1 = (5, "$a1"), + A2 = (6, "$a2"), + A3 = (7, "$a3"), + A4 = (8, "$a4"), + A5 = (9, "$a5"), + A6 = (10, "$a6"), + A7 = (11, "$a7"), + T0 = (12, "$t0"), + T1 = (13, "$t1"), + T2 = (14, "$t2"), + T3 = (15, "$t3"), + T4 = (16, "$t4"), + T5 = (17, "$t5"), + T6 = (18, "$t6"), + T7 = (19, "$t7"), + T8 = (20, "$t8"), + FP = (22, "$fp"), + S0 = (23, "$s0"), + S1 = (24, "$s1"), + S2 = (25, "$s2"), + S3 = (26, "$s3"), + S4 = (27, "$s4"), + S5 = (28, "$s5"), + S6 = (29, "$s6"), + S7 = (30, "$s7"), + S8 = (31, "$s8"), + + FA0 = (32, "$fa0"), + FA1 = (33, "$fa1"), + FA2 = (34, "$fa2"), + FA3 = (35, "$fa3"), + FA4 = (36, "$fa4"), + FA5 = (37, "$fa5"), + FA6 = (38, "$fa6"), + FA7 = (39, "$fa7"), + FT0 = (40, "$ft0"), + FT1 = (41, "$ft1"), + FT2 = (42, "$ft2"), + FT3 = (43, "$ft3"), + FT4 = (44, "$ft4"), + FT5 = (45, "$ft5"), + FT6 = (46, "$ft6"), + FT7 = (47, "$ft7"), + FT8 = (48, "$ft8"), + FT9 = (49, "$ft9"), + FT10 = (50, "$ft10"), + FT11 = (51, "$ft11"), + FT12 = (52, "$ft12"), + FT13 = (53, "$ft13"), + FT14 = (54, "$ft14"), + FT15 = (55, "$ft15"), + FS0 = (56, "$fs0"), + FS1 = (57, "$fs1"), + FS2 = (58, "$fs2"), + FS3 = (59, "$fs3"), + FS4 = (60, "$fs4"), + FS5 = (61, "$fs5"), + FS6 = (62, "$fs6"), + FS7 = (63, "$fs7"), +}); + +/// MIPS architecture specific definitions. +/// +/// See [MIPS Details](https://en.wikibooks.org/wiki/MIPS_Assembly/MIPS_Details). +#[derive(Debug, Clone, Copy)] +pub struct MIPS; + +registers!(MIPS, { + R0 = (0, "$0"), + R1 = (1, "$1"), + R2 = (2, "$2"), + R3 = (3, "$3"), + R4 = (4, "$4"), + R5 = (5, "$5"), + R6 = (6, "$6"), + R7 = (7, "$7"), + R8 = (8, "$8"), + R9 = (9, "$9"), + R10 = (10, "$10"), + R11 = (11, "$11"), + R12 = (12, "$12"), + R13 = (13, "$13"), + R14 = (14, "$14"), + R15 = (15, "$15"), + R16 = (16, "$16"), + R17 = (17, "$17"), + R18 = (18, "$18"), + R19 = (19, "$19"), + R20 = (20, "$20"), + R21 = (21, "$21"), + R22 = (22, "$22"), + R23 = (23, "$23"), + R24 = (24, "$24"), + R25 = (25, "$25"), + R26 = (26, "$26"), + R27 = (27, "$27"), + R28 = (28, "$28"), + R29 = (29, "$29"), + R30 = (30, "$30"), + R31 = (31, "$31"), + + F0 = (32, "$f0"), + F1 = (33, "$f1"), + F2 = (34, "$f2"), + F3 = (35, "$f3"), + F4 = (36, "$f4"), + F5 = (37, "$f5"), + F6 = (38, "$f6"), + F7 = (39, "$f7"), + F8 = (40, "$f8"), + F9 = (41, "$f9"), + F10 = (42, "$f10"), + F11 = (43, "$f11"), + F12 = (44, "$f12"), + F13 = (45, "$f13"), + F14 = (46, "$f14"), + F15 = (47, "$f15"), + F16 = (48, "$f16"), + F17 = (49, "$f17"), + F18 = (50, "$f18"), + F19 = (51, "$f19"), + F20 = (52, "$f20"), + F21 = (53, "$f21"), + F22 = (54, "$f22"), + F23 = (55, "$f23"), + F24 = (56, "$f24"), + F25 = (57, "$f25"), + F26 = (58, "$f26"), + F27 = (59, "$f27"), + F28 = (60, "$f28"), + F29 = (61, "$f29"), + F30 = (62, "$f30"), + F31 = (63, "$f31"), + + HI = (64, "$hi"), + LO = (65, "$lo"), +}, +aliases { + ZERO = (0, "$zero"), + AT = (1, "$at"), + V0 = (2, "$v0"), + V1 = (3, "$v1"), + A0 = (4, "$a0"), + A1 = (5, "$a1"), + A2 = (6, "$a2"), + A3 = (7, "$a3"), + T0 = (8, "$t0"), + T1 = (9, "$t1"), + T2 = (10, "$t2"), + T3 = (11, "$t3"), + T4 = (12, "$t4"), + T5 = (13, "$t5"), + T6 = (14, "$t6"), + T7 = (15, "$t7"), + S0 = (16, "$s0"), + S1 = (17, "$s1"), + S2 = (18, "$s2"), + S3 = (19, "$s3"), + S4 = (20, "$s4"), + S5 = (21, "$s5"), + S6 = (22, "$s6"), + S7 = (23, "$s7"), + T8 = (24, "$t8"), + T9 = (25, "$t9"), + K0 = (26, "$k0"), + K1 = (27, "$k1"), + GP = (28, "$gp"), + SP = (29, "$sp"), + FP = (30, "$fp"), + RA = (31, "$ra"), + + S8 = (30, "$s8") +}); + +/// RISC-V architecture specific definitions. +/// +/// See [RISC-V ELF psABI specification](https://github.com/riscv/riscv-elf-psabi-doc). +#[derive(Debug, Clone, Copy)] +pub struct RiscV; + +registers!(RiscV, { + X0 = (0, "x0"), + X1 = (1, "x1"), + X2 = (2, "x2"), + X3 = (3, "x3"), + X4 = (4, "x4"), + X5 = (5, "x5"), + X6 = (6, "x6"), + X7 = (7, "x7"), + X8 = (8, "x8"), + X9 = (9, "x9"), + X10 = (10, "x10"), + X11 = (11, "x11"), + X12 = (12, "x12"), + X13 = (13, "x13"), + X14 = (14, "x14"), + X15 = (15, "x15"), + X16 = (16, "x16"), + X17 = (17, "x17"), + X18 = (18, "x18"), + X19 = (19, "x19"), + X20 = (20, "x20"), + X21 = (21, "x21"), + X22 = (22, "x22"), + X23 = (23, "x23"), + X24 = (24, "x24"), + X25 = (25, "x25"), + X26 = (26, "x26"), + X27 = (27, "x27"), + X28 = (28, "x28"), + X29 = (29, "x29"), + X30 = (30, "x30"), + X31 = (31, "x31"), + + F0 = (32, "f0"), + F1 = (33, "f1"), + F2 = (34, "f2"), + F3 = (35, "f3"), + F4 = (36, "f4"), + F5 = (37, "f5"), + F6 = (38, "f6"), + F7 = (39, "f7"), + F8 = (40, "f8"), + F9 = (41, "f9"), + F10 = (42, "f10"), + F11 = (43, "f11"), + F12 = (44, "f12"), + F13 = (45, "f13"), + F14 = (46, "f14"), + F15 = (47, "f15"), + F16 = (48, "f16"), + F17 = (49, "f17"), + F18 = (50, "f18"), + F19 = (51, "f19"), + F20 = (52, "f20"), + F21 = (53, "f21"), + F22 = (54, "f22"), + F23 = (55, "f23"), + F24 = (56, "f24"), + F25 = (57, "f25"), + F26 = (58, "f26"), + F27 = (59, "f27"), + F28 = (60, "f28"), + F29 = (61, "f29"), + F30 = (62, "f30"), + F31 = (63, "f31"), +}, +aliases { + ZERO = (0, "zero"), + RA = (1, "ra"), + SP = (2, "sp"), + GP = (3, "gp"), + TP = (4, "tp"), + T0 = (5, "t0"), + T1 = (6, "t1"), + T2 = (7, "t2"), + S0 = (8, "s0"), + S1 = (9, "s1"), + A0 = (10, "a0"), + A1 = (11, "a1"), + A2 = (12, "a2"), + A3 = (13, "a3"), + A4 = (14, "a4"), + A5 = (15, "a5"), + A6 = (16, "a6"), + A7 = (17, "a7"), + S2 = (18, "s2"), + S3 = (19, "s3"), + S4 = (20, "s4"), + S5 = (21, "s5"), + S6 = (22, "s6"), + S7 = (23, "s7"), + S8 = (24, "s8"), + S9 = (25, "s9"), + S10 = (26, "s10"), + S11 = (27, "s11"), + T3 = (28, "t3"), + T4 = (29, "t4"), + T5 = (30, "t5"), + T6 = (31, "t6"), + + FT0 = (32, "ft0"), + FT1 = (33, "ft1"), + FT2 = (34, "ft2"), + FT3 = (35, "ft3"), + FT4 = (36, "ft4"), + FT5 = (37, "ft5"), + FT6 = (38, "ft6"), + FT7 = (39, "ft7"), + FS0 = (40, "fs0"), + FS1 = (41, "fs1"), + FA0 = (42, "fa0"), + FA1 = (43, "fa1"), + FA2 = (44, "fa2"), + FA3 = (45, "fa3"), + FA4 = (46, "fa4"), + FA5 = (47, "fa5"), + FA6 = (48, "fa6"), + FA7 = (49, "fa7"), + FS2 = (50, "fs2"), + FS3 = (51, "fs3"), + FS4 = (52, "fs4"), + FS5 = (53, "fs5"), + FS6 = (54, "fs6"), + FS7 = (55, "fs7"), + FS8 = (56, "fs8"), + FS9 = (57, "fs9"), + FS10 = (58, "fs10"), + FS11 = (59, "fs11"), + FT8 = (60, "ft8"), + FT9 = (61, "ft9"), + FT10 = (62, "ft10"), + FT11 = (63, "ft11"), +}); + +/// Intel i386 architecture specific definitions. +/// +/// See section 2.4.2 of the [i386 psABI](https://gitlab.com/x86-psABIs/i386-ABI). +#[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 section 3.6.2 of the [x86-64 psABI](https://gitlab.com/x86-psABIs/x86-64-ABI). +#[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"), + + R16 = (130, "r16"), + R17 = (131, "r17"), + R18 = (132, "r18"), + R19 = (133, "r19"), + R20 = (134, "r20"), + R21 = (135, "r21"), + R22 = (136, "r22"), + R23 = (137, "r23"), + R24 = (138, "r24"), + R25 = (139, "r25"), + R26 = (140, "r26"), + R27 = (141, "r27"), + R28 = (142, "r28"), + R29 = (143, "r29"), + R30 = (144, "r30"), + R31 = (145, "r31"), + + TMM0 = (146, "tmm0"), + TMM1 = (147, "tmm1"), + TMM2 = (148, "tmm2"), + TMM3 = (149, "tmm3"), + TMM4 = (150, "tmm4"), + TMM5 = (151, "tmm5"), + TMM6 = (152, "tmm6"), + TMM7 = (153, "tmm7"), + TILECFG = (154, "tilecfg"), +}); + +/// PowerPC 64bit +/// +/// See [64-bit ELF ABI Specification for OpenPOWER Architecture](https://openpowerfoundation.org/specifications/64bitelfabi/). +#[derive(Debug, Clone, Copy)] +pub struct PowerPc64; + +registers!(PowerPc64, { + R0 = (0, "r0"), + R1 = (1, "r1"), + R2 = (2, "r2"), + R3 = (3, "r3"), + R4 = (4, "r4"), + R5 = (5, "r5"), + R6 = (6, "r6"), + R7 = (7, "r7"), + R8 = (8, "r8"), + R9 = (9, "r9"), + R10 = (10, "r10"), + R11 = (11, "r11"), + R12 = (12, "r12"), + R13 = (13, "r13"), + R14 = (14, "r14"), + R15 = (15, "r15"), + R16 = (16, "r16"), + R17 = (17, "r17"), + R18 = (18, "r18"), + R19 = (19, "r19"), + R20 = (20, "r20"), + R21 = (21, "r21"), + R22 = (22, "r22"), + R23 = (23, "r23"), + R24 = (24, "r24"), + R25 = (25, "r25"), + R26 = (26, "r26"), + R27 = (27, "r27"), + R28 = (28, "r28"), + R29 = (29, "r29"), + R30 = (30, "r30"), + R31 = (31, "r31"), + + F0 = (32, "f0"), + F1 = (33, "f1"), + F2 = (34, "f2"), + F3 = (35, "f3"), + F4 = (36, "f4"), + F5 = (37, "f5"), + F6 = (38, "f6"), + F7 = (39, "f7"), + F8 = (40, "f8"), + F9 = (41, "f9"), + F10 = (42, "f10"), + F11 = (43, "f11"), + F12 = (44, "f12"), + F13 = (45, "f13"), + F14 = (46, "f14"), + F15 = (47, "f15"), + F16 = (48, "f16"), + F17 = (49, "f17"), + F18 = (50, "f18"), + F19 = (51, "f19"), + F20 = (52, "f20"), + F21 = (53, "f21"), + F22 = (54, "f22"), + F23 = (55, "f23"), + F24 = (56, "f24"), + F25 = (57, "f25"), + F26 = (58, "f26"), + F27 = (59, "f27"), + F28 = (60, "f28"), + F29 = (61, "f29"), + F30 = (62, "f30"), + F31 = (63, "f31"), + + LR = (65, "lr"), + CTR = (66, "ctr"), + + CR0 = (68, "cr0"), + CR1 = (69, "cr1"), + CR2 = (70, "cr2"), + CR3 = (71, "cr3"), + CR4 = (72, "cr4"), + CR5 = (73, "cr5"), + CR6 = (74, "cr6"), + CR7 = (75, "cr7"), + XER = (76, "xer"), + + VR0 = (77, "vr0"), + VR1 = (78, "vr1"), + VR2 = (79, "vr2"), + VR3 = (80, "vr3"), + VR4 = (81, "vr4"), + VR5 = (82, "vr5"), + VR6 = (83, "vr6"), + VR7 = (84, "vr7"), + VR8 = (85, "vr8"), + VR9 = (86, "vr9"), + VR10 = (87, "vr10"), + VR11 = (88, "vr11"), + VR12 = (89, "vr12"), + VR13 = (90, "vr13"), + VR14 = (91, "vr14"), + VR15 = (92, "vr15"), + VR16 = (93, "vr16"), + VR17 = (94, "vr17"), + VR18 = (95, "vr18"), + VR19 = (96, "vr19"), + VR20 = (97, "vr20"), + VR21 = (98, "vr21"), + VR22 = (99, "vr22"), + VR23 = (100, "vr23"), + VR24 = (101, "vr24"), + VR25 = (102, "vr25"), + VR26 = (103, "vr26"), + VR27 = (104, "vr27"), + VR28 = (105, "vr28"), + VR29 = (106, "vr29"), + VR30 = (107, "vr30"), + VR31 = (108, "vr31"), + + VSCR = (110, "vscr"), + TFHAR = (114, "tfhar"), + TFIAR = (115, "tfiar"), + TEXASR = (116, "texasr"), +}); + +#[cfg(test)] +mod tests { + + #[test] + #[cfg(feature = "std")] + fn test_aarch64_registers() { + use super::*; + use std::collections::HashSet; + + let mut names = HashSet::new(); + for n in (0..=39).chain(46..=127) { + let name = AArch64::register_name(Register(n)) + .unwrap_or_else(|| panic!("Register {} should have a name.", n)); + assert!(names.insert(name)); + } + } + + #[test] + #[cfg(feature = "std")] + fn test_power64_registers() { + use super::*; + use std::collections::HashSet; + + let mut names = HashSet::new(); + for n in (0..=63).chain(68..=75).chain(77..=108) { + let name = PowerPc64::register_name(Register(n)) + .unwrap_or_else(|| panic!("Register {} should have a name.", n)); + assert!(names.insert(name)); + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/common.rs b/chromium_crates_io/vendor/gimli-v0_32/src/common.rs new file mode 100644 index 0000000..d7cc7f7 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/common.rs
@@ -0,0 +1,401 @@ +/// Whether the format of a compilation unit is 32- or 64-bit. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Format { + /// 64-bit DWARF + Dwarf64 = 8, + /// 32-bit DWARF + Dwarf32 = 4, +} + +impl Format { + /// Return the serialized size of an initial length field for the format. + #[inline] + pub fn initial_length_size(self) -> u8 { + match self { + Format::Dwarf32 => 4, + Format::Dwarf64 => 12, + } + } + + /// Return the natural word size for the format + #[inline] + pub fn word_size(self) -> u8 { + match self { + Format::Dwarf32 => 4, + Format::Dwarf64 => 8, + } + } +} + +/// Which vendor extensions to support. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Vendor { + /// A default set of extensions, including some common GNU extensions. + Default, + /// AAarch64 extensions. + AArch64, +} + +/// Encoding parameters that are commonly used for multiple DWARF sections. +/// +/// This is intended to be small enough to pass by value. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +// `address_size` and `format` are used more often than `version`, so keep +// them first. +#[repr(C)] +pub struct Encoding { + /// The size of an address. + pub address_size: u8, + + /// Whether the DWARF format is 32- or 64-bit. + pub format: Format, + + /// The DWARF version of the header. + pub version: u16, +} + +/// Encoding parameters for a line number program. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LineEncoding { + /// The size in bytes of the smallest target machine instruction. + pub minimum_instruction_length: u8, + + /// The maximum number of individual operations that may be encoded in an + /// instruction. + pub maximum_operations_per_instruction: u8, + + /// The initial value of the `is_stmt` register. + pub default_is_stmt: bool, + + /// The minimum value which a special opcode can add to the line register. + pub line_base: i8, + + /// The range of values which a special opcode can add to the line register. + pub line_range: u8, +} + +impl Default for LineEncoding { + fn default() -> Self { + // Values from LLVM. + LineEncoding { + minimum_instruction_length: 1, + maximum_operations_per_instruction: 1, + default_is_stmt: true, + line_base: -5, + line_range: 14, + } + } +} + +/// A DWARF register number. +/// +/// The meaning of this value is ABI dependent. This is generally encoded as +/// a ULEB128, but supported architectures need 16 bits at most. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Register(pub u16); + +/// An offset into the `.debug_abbrev` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DebugAbbrevOffset<T = usize>(pub T); + +/// An offset into the `.debug_addr` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugAddrOffset<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::DebugMacinfo => ".debug_macinfo.dwo", + SectionId::DebugMacro => ".debug_macro.dwo", + SectionId::DebugRngLists => ".debug_rnglists.dwo", + SectionId::DebugStr => ".debug_str.dwo", + SectionId::DebugStrOffsets => ".debug_str_offsets.dwo", + SectionId::DebugTuIndex => ".debug_tu_index", + SectionId::DebugTypes => ".debug_types.dwo", + _ => return None, + }) + } + + /// Returns the XCOFF section name for this kind. + pub fn xcoff_name(self) -> Option<&'static str> { + Some(match self { + SectionId::DebugAbbrev => ".dwabrev", + SectionId::DebugAranges => ".dwarnge", + SectionId::DebugFrame => ".dwframe", + SectionId::DebugInfo => ".dwinfo", + SectionId::DebugLine => ".dwline", + SectionId::DebugLoc => ".dwloc", + SectionId::DebugMacinfo => ".dwmac", + SectionId::DebugPubNames => ".dwpbnms", + SectionId::DebugPubTypes => ".dwpbtyp", + SectionId::DebugRanges => ".dwrnges", + SectionId::DebugStr => ".dwstr", + _ => return None, + }) + } + + /// Returns true if this is a mergeable string section. + /// + /// This is useful for determining the correct section flags. + pub fn is_string(self) -> bool { + matches!(self, SectionId::DebugStr | SectionId::DebugLineStr) + } +} + +/// An optionally-provided implementation-defined compilation unit ID to enable +/// split DWARF and linking a split compilation unit back together. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DwoId(pub u64); + +/// The "type" of file with DWARF debugging information. This determines, among other things, +/// which files DWARF sections should be loaded from. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DwarfFileType { + /// A normal executable or object file. + Main, + /// A .dwo split DWARF file. + Dwo, + // TODO: Supplementary files, .dwps? +} + +impl Default for DwarfFileType { + fn default() -> Self { + DwarfFileType::Main + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/constants.rs b/chromium_crates_io/vendor/gimli-v0_32/src/constants.rs new file mode 100644 index 0000000..4121033 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/constants.rs
@@ -0,0 +1,1495 @@ +// This file originally from https://github.com/philipc/rust-dwarf/ and +// distributed under either MIT or Apache 2.0 licenses. +// +// Copyright 2016 The rust-dwarf Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Constant definitions. +//! +//! The DWARF spec's `DW_AT_*` type is represented as `struct DwAt(u16)`, +//! `DW_FORM_*` as `DwForm(u16)`, etc. +//! +//! There are also exported const definitions for each constant. + +#![allow(non_upper_case_globals)] +#![allow(missing_docs)] + +use core::{fmt, ops}; + +// The `dw!` macro turns this: +// +// dw!(DwFoo(u32) { +// DW_FOO_bar = 0, +// DW_FOO_baz = 1, +// DW_FOO_bang = 2, +// }); +// +// into this: +// +// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +// pub struct DwFoo(pub u32); +// +// pub const DW_FOO_bar: DwFoo = DwFoo(0); +// pub const DW_FOO_baz: DwFoo = DwFoo(1); +// pub const DW_FOO_bang: DwFoo = DwFoo(2); +// +// impl DwFoo { +// pub fn static_string(&self) -> Option<&'static str> { +// ... +// } +// } +// +// impl fmt::Display for DwFoo { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { +// ... +// } +// } +macro_rules! dw { + ($(#[$meta:meta])* $struct_name:ident($struct_type:ty) + { $($name:ident = $val:expr),+ $(,)? } + $(, aliases { $($alias_name:ident = $alias_val:expr),+ $(,)? })? + ) => { + $(#[$meta])* + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct $struct_name(pub $struct_type); + + $( + pub const $name: $struct_name = $struct_name($val); + )+ + $($( + pub const $alias_name: $struct_name = $struct_name($alias_val); + )+)* + + impl $struct_name { + pub fn static_string(&self) -> Option<&'static str> { + Some(match *self { + $( + $name => stringify!($name), + )+ + _ => return None, + }) + } + } + + impl fmt::Display for $struct_name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + if let Some(s) = self.static_string() { + f.pad(s) + } else { + #[cfg(feature = "read")] + { + f.pad(&format!("Unknown {}: {}", stringify!($struct_name), self.0)) + } + #[cfg(not(feature = "read"))] + { + write!(f, "Unknown {}: {}", stringify!($struct_name), self.0) + } + } + } + } + }; +} + +dw!( +/// The section type field in a `.dwp` unit index. +/// +/// This is used for version 5 and later. +/// +/// See Section 7.3.5. +DwSect(u32) { + DW_SECT_INFO = 1, + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOCLISTS = 5, + DW_SECT_STR_OFFSETS = 6, + DW_SECT_MACRO = 7, + DW_SECT_RNGLISTS = 8, +}); + +dw!( +/// The section type field in a `.dwp` unit index with version 2. +DwSectV2(u32) { + DW_SECT_V2_INFO = 1, + DW_SECT_V2_TYPES = 2, + DW_SECT_V2_ABBREV = 3, + DW_SECT_V2_LINE = 4, + DW_SECT_V2_LOC = 5, + DW_SECT_V2_STR_OFFSETS = 6, + DW_SECT_V2_MACINFO = 7, + DW_SECT_V2_MACRO = 8, +}); + +dw!( +/// The unit type field in a unit header. +/// +/// See Section 7.5.1, Table 7.2. +DwUt(u8) { + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff, +}); + +dw!( +/// The opcode for a call frame instruction. +/// +/// Section 7.24: +/// > Call frame instructions are encoded in one or more bytes. The primary +/// > opcode is encoded in the high order two bits of the first byte (that is, +/// > opcode = byte >> 6). An operand or extended opcode may be encoded in the +/// > low order 6 bits. Additional operands are encoded in subsequent bytes. +DwCfa(u8) { + DW_CFA_advance_loc = 0x01 << 6, + DW_CFA_offset = 0x02 << 6, + DW_CFA_restore = 0x03 << 6, + DW_CFA_nop = 0, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + DW_CFA_MIPS_advance_loc8 = 0x1d, + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f, +}, +aliases { + DW_CFA_AARCH64_negate_ra_state = 0x2d, +}); + +dw!( +/// The child determination encodings for DIE attributes. +/// +/// See Section 7.5.3, Table 7.4. +DwChildren(u8) { + DW_CHILDREN_no = 0, + DW_CHILDREN_yes = 1, +}); + +dw!( +/// The tag encodings for DIE attributes. +/// +/// See Section 7.5.3, Table 7.3. +DwTag(u16) { + DW_TAG_null = 0x00, + +// DWARF 1 only, obsolete in DWARF 2+. + DW_TAG_global_subroutine = 0x06, + DW_TAG_global_variable = 0x07, + DW_TAG_local_variable = 0x0c, + DW_TAG_subroutine = 0x14, + +// DWARF 1. + 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, + +// DWARF 2. + 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, + +// DWARF 1 only, obsolete in DWARF 2+. + DW_AT_fund_type = 0x05, + DW_AT_mod_fund_type = 0x06, + DW_AT_user_def_type = 0x07, + DW_AT_mod_u_d_type = 0x08, + DW_AT_subscr_data = 0x0a, + DW_AT_element_list = 0x0f, + DW_AT_member = 0x14, + DW_AT_friends = 0x1f, + DW_AT_program = 0x23, + DW_AT_private = 0x24, + DW_AT_protected = 0x26, + DW_AT_public = 0x28, + DW_AT_pure_virtual = 0x29, + DW_AT_virtual = 0x30, + +// Moved to 0x47 in DWARF 2+. + DW_AT_specification_v1 = 0x2b, + +// DWARF 1. + 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, + +// DWARF 2. + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + +// DWARF 3. + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5a, + DW_AT_binary_scale = 0x5b, + DW_AT_decimal_scale = 0x5c, + DW_AT_small = 0x5d, + DW_AT_decimal_sign = 0x5e, + DW_AT_digit_count = 0x5f, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, + +// DWARF 4. + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + +// DWARF 5. + DW_AT_string_length_bit_size = 0x6f, + DW_AT_string_length_byte_size = 0x70, + DW_AT_rank = 0x71, + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + DW_AT_dwo_name = 0x76, + DW_AT_reference = 0x77, + DW_AT_rvalue_reference = 0x78, + DW_AT_macros = 0x79, + DW_AT_call_all_calls = 0x7a, + DW_AT_call_all_source_calls = 0x7b, + DW_AT_call_all_tail_calls = 0x7c, + DW_AT_call_return_pc = 0x7d, + DW_AT_call_value = 0x7e, + DW_AT_call_origin = 0x7f, + DW_AT_call_parameter = 0x80, + DW_AT_call_pc = 0x81, + DW_AT_call_tail_call = 0x82, + DW_AT_call_target = 0x83, + DW_AT_call_target_clobbered = 0x84, + DW_AT_call_data_location = 0x85, + DW_AT_call_data_value = 0x86, + DW_AT_noreturn = 0x87, + DW_AT_alignment = 0x88, + DW_AT_export_symbols = 0x89, + DW_AT_deleted = 0x8a, + DW_AT_defaulted = 0x8b, + DW_AT_loclists_base = 0x8c, + + DW_AT_lo_user = 0x2000, + DW_AT_hi_user = 0x3fff, + +// SGI/MIPS extensions. + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + DW_AT_MIPS_stride_byte = 0x200c, + DW_AT_MIPS_stride_elem = 0x200d, + DW_AT_MIPS_ptr_dopetype = 0x200e, + DW_AT_MIPS_allocatable_dopetype = 0x200f, + DW_AT_MIPS_assumed_shape_dopetype = 0x2010, + +// This one appears to have only been implemented by Open64 for +// fortran and may conflict with other extensions. + DW_AT_MIPS_assumed_size = 0x2011, + +// TODO: HP/CPQ extensions. +// These conflict with the MIPS extensions. + + DW_AT_INTEL_other_endian = 0x2026, + +// GNU extensions + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + DW_AT_GNU_guarded_by = 0x2108, + DW_AT_GNU_pt_guarded_by = 0x2109, + DW_AT_GNU_guarded = 0x210a, + DW_AT_GNU_pt_guarded = 0x210b, + DW_AT_GNU_locks_excluded = 0x210c, + DW_AT_GNU_exclusive_locks_required = 0x210d, + DW_AT_GNU_shared_locks_required = 0x210e, + DW_AT_GNU_odr_signature = 0x210f, + DW_AT_GNU_template_name = 0x2110, + DW_AT_GNU_call_site_value = 0x2111, + DW_AT_GNU_call_site_data_value = 0x2112, + DW_AT_GNU_call_site_target = 0x2113, + DW_AT_GNU_call_site_target_clobbered = 0x2114, + DW_AT_GNU_tail_call = 0x2115, + DW_AT_GNU_all_tail_call_sites = 0x2116, + DW_AT_GNU_all_call_sites = 0x2117, + DW_AT_GNU_all_source_call_sites = 0x2118, + DW_AT_GNU_macros = 0x2119, + DW_AT_GNU_deleted = 0x211a, + +// Extensions for Fission proposal. + DW_AT_GNU_dwo_name = 0x2130, + DW_AT_GNU_dwo_id = 0x2131, + DW_AT_GNU_ranges_base = 0x2132, + DW_AT_GNU_addr_base = 0x2133, + DW_AT_GNU_pubnames = 0x2134, + DW_AT_GNU_pubtypes = 0x2135, + DW_AT_GNU_discriminator = 0x2136, + DW_AT_GNU_locviews = 0x2137, + DW_AT_GNU_entry_view = 0x2138, + +// Conflict with Sun. +// DW_AT_VMS_rtnbeg_pd_address = 0x2201, + +// Sun extensions. + DW_AT_SUN_template = 0x2201, + DW_AT_SUN_alignment = 0x2202, + DW_AT_SUN_vtable = 0x2203, + DW_AT_SUN_count_guarantee = 0x2204, + DW_AT_SUN_command_line = 0x2205, + DW_AT_SUN_vbase = 0x2206, + DW_AT_SUN_compile_options = 0x2207, + DW_AT_SUN_language = 0x2208, + DW_AT_SUN_browser_file = 0x2209, + DW_AT_SUN_vtable_abi = 0x2210, + DW_AT_SUN_func_offsets = 0x2211, + DW_AT_SUN_cf_kind = 0x2212, + DW_AT_SUN_vtable_index = 0x2213, + DW_AT_SUN_omp_tpriv_addr = 0x2214, + DW_AT_SUN_omp_child_func = 0x2215, + DW_AT_SUN_func_offset = 0x2216, + DW_AT_SUN_memop_type_ref = 0x2217, + DW_AT_SUN_profile_id = 0x2218, + DW_AT_SUN_memop_signature = 0x2219, + DW_AT_SUN_obj_dir = 0x2220, + DW_AT_SUN_obj_file = 0x2221, + DW_AT_SUN_original_name = 0x2222, + DW_AT_SUN_hwcprof_signature = 0x2223, + DW_AT_SUN_amd64_parmdump = 0x2224, + DW_AT_SUN_part_link_name = 0x2225, + DW_AT_SUN_link_name = 0x2226, + DW_AT_SUN_pass_with_const = 0x2227, + DW_AT_SUN_return_with_const = 0x2228, + DW_AT_SUN_import_by_name = 0x2229, + DW_AT_SUN_f90_pointer = 0x222a, + DW_AT_SUN_pass_by_ref = 0x222b, + DW_AT_SUN_f90_allocatable = 0x222c, + DW_AT_SUN_f90_assumed_shape_array = 0x222d, + DW_AT_SUN_c_vla = 0x222e, + DW_AT_SUN_return_value_ptr = 0x2230, + DW_AT_SUN_dtor_start = 0x2231, + DW_AT_SUN_dtor_length = 0x2232, + DW_AT_SUN_dtor_state_initial = 0x2233, + DW_AT_SUN_dtor_state_final = 0x2234, + DW_AT_SUN_dtor_state_deltas = 0x2235, + DW_AT_SUN_import_by_lname = 0x2236, + DW_AT_SUN_f90_use_only = 0x2237, + DW_AT_SUN_namelist_spec = 0x2238, + DW_AT_SUN_is_omp_child_func = 0x2239, + DW_AT_SUN_fortran_main_alias = 0x223a, + DW_AT_SUN_fortran_based = 0x223b, + + DW_AT_ALTIUM_loclist = 0x2300, + + DW_AT_use_GNAT_descriptive_type = 0x2301, + DW_AT_GNAT_descriptive_type = 0x2302, + DW_AT_GNU_numerator = 0x2303, + DW_AT_GNU_denominator = 0x2304, + DW_AT_GNU_bias = 0x2305, + + DW_AT_upc_threads_scaled = 0x3210, + +// PGI (STMicroelectronics) extensions. + DW_AT_PGI_lbase = 0x3a00, + DW_AT_PGI_soffset = 0x3a01, + DW_AT_PGI_lstride = 0x3a02, + +// Borland extensions. + DW_AT_BORLAND_property_read = 0x3b11, + DW_AT_BORLAND_property_write = 0x3b12, + DW_AT_BORLAND_property_implements = 0x3b13, + DW_AT_BORLAND_property_index = 0x3b14, + DW_AT_BORLAND_property_default = 0x3b15, + DW_AT_BORLAND_Delphi_unit = 0x3b20, + DW_AT_BORLAND_Delphi_class = 0x3b21, + DW_AT_BORLAND_Delphi_record = 0x3b22, + DW_AT_BORLAND_Delphi_metaclass = 0x3b23, + DW_AT_BORLAND_Delphi_constructor = 0x3b24, + DW_AT_BORLAND_Delphi_destructor = 0x3b25, + DW_AT_BORLAND_Delphi_anonymous_method = 0x3b26, + DW_AT_BORLAND_Delphi_interface = 0x3b27, + DW_AT_BORLAND_Delphi_ABI = 0x3b28, + DW_AT_BORLAND_Delphi_return = 0x3b29, + DW_AT_BORLAND_Delphi_frameptr = 0x3b30, + DW_AT_BORLAND_closure = 0x3b31, + +// LLVM project extensions. + DW_AT_LLVM_include_path = 0x3e00, + DW_AT_LLVM_config_macros = 0x3e01, + DW_AT_LLVM_isysroot = 0x3e02, + +// Apple extensions. + DW_AT_APPLE_optimized = 0x3fe1, + DW_AT_APPLE_flags = 0x3fe2, + DW_AT_APPLE_isa = 0x3fe3, + DW_AT_APPLE_block = 0x3fe4, + DW_AT_APPLE_major_runtime_vers = 0x3fe5, + DW_AT_APPLE_runtime_class = 0x3fe6, + DW_AT_APPLE_omit_frame_ptr = 0x3fe7, + DW_AT_APPLE_property_name = 0x3fe8, + DW_AT_APPLE_property_getter = 0x3fe9, + DW_AT_APPLE_property_setter = 0x3fea, + DW_AT_APPLE_property_attribute = 0x3feb, + DW_AT_APPLE_objc_complete_type = 0x3fec, + DW_AT_APPLE_property = 0x3fed +}); + +dw!( +/// The attribute form encodings for DIE attributes. +/// +/// See Section 7.5.6, Table 7.6. +DwForm(u16) { + DW_FORM_null = 0x00, + +// DWARF 1 only, obsolete in DWARF 2+. + DW_FORM_ref = 0x02, + +// DWARF 1. + 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, + +// DWARF 2. + 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_source = 0x6, + DW_LNCT_lo_user = 0x2000, + // We currently only implement the LLVM embedded source code extension for DWARF v5. + DW_LNCT_LLVM_source = 0x2001, + DW_LNCT_hi_user = 0x3fff, +}); + +dw!( +/// Type codes for macro definitions in the `.debug_macinfo` section. +/// +/// See Section 7.22, Figure 39 for DWARF 4. +DwMacinfo(u8) { + DW_MACINFO_define = 0x01, + DW_MACINFO_undef = 0x02, + DW_MACINFO_start_file = 0x03, + DW_MACINFO_end_file = 0x04, + DW_MACINFO_vendor_ext = 0xff, +}); + +dw!( +/// The encodings for macro information entry types. +/// +/// See Section 7.23, Table 7.28 for DWARF 5. +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 macOS tooling. + DW_EH_PE_indirect = 0x80, + +// These constants apply to both the lower and upper bits. + +// "The Value is a literal pointer whose size is determined by the +// architecture." + DW_EH_PE_absptr = 0x0, +// The absence of a pointer and encoding. + DW_EH_PE_omit = 0xff, +}); + +const DW_EH_PE_FORMAT_MASK: u8 = 0b0000_1111; + +// Ignores indirection bit. +const DW_EH_PE_APPLICATION_MASK: u8 = 0b0111_0000; + +impl ops::BitOr for DwEhPe { + type Output = DwEhPe; + + fn bitor(self, rhs: DwEhPe) -> DwEhPe { + DwEhPe(self.0 | rhs.0) + } +} + +impl DwEhPe { + /// Get the pointer encoding's format. + #[inline] + pub fn format(self) -> DwEhPe { + DwEhPe(self.0 & DW_EH_PE_FORMAT_MASK) + } + + /// Get the pointer encoding's application. + #[inline] + pub fn application(self) -> DwEhPe { + DwEhPe(self.0 & DW_EH_PE_APPLICATION_MASK) + } + + /// Is this encoding the absent pointer encoding? + #[inline] + pub fn is_absent(self) -> bool { + self == DW_EH_PE_omit + } + + /// Is this coding indirect? If so, its encoded value is the address of the + /// real pointer result, not the pointer result itself. + #[inline] + pub fn is_indirect(self) -> bool { + self.0 & DW_EH_PE_indirect.0 != 0 + } + + /// Is this a known, valid pointer encoding? + pub fn is_valid_encoding(self) -> bool { + if self.is_absent() { + return true; + } + + match self.format() { + DW_EH_PE_absptr | DW_EH_PE_uleb128 | DW_EH_PE_udata2 | DW_EH_PE_udata4 + | DW_EH_PE_udata8 | DW_EH_PE_sleb128 | DW_EH_PE_sdata2 | DW_EH_PE_sdata4 + | DW_EH_PE_sdata8 => {} + _ => return false, + } + + match self.application() { + DW_EH_PE_absptr | DW_EH_PE_pcrel | DW_EH_PE_textrel | DW_EH_PE_datarel + | DW_EH_PE_funcrel | DW_EH_PE_aligned => {} + _ => return false, + } + + true + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_dw_eh_pe_format() { + let encoding = DW_EH_PE_pcrel | DW_EH_PE_uleb128; + assert_eq!(encoding.format(), DW_EH_PE_uleb128); + } + + #[test] + fn test_dw_eh_pe_application() { + let encoding = DW_EH_PE_pcrel | DW_EH_PE_uleb128; + assert_eq!(encoding.application(), DW_EH_PE_pcrel); + } + + #[test] + fn test_dw_eh_pe_is_absent() { + assert!(!DW_EH_PE_absptr.is_absent()); + assert!(DW_EH_PE_omit.is_absent()); + } + + #[test] + fn test_dw_eh_pe_is_valid_encoding_ok() { + let encoding = DW_EH_PE_uleb128 | DW_EH_PE_pcrel; + assert!(encoding.is_valid_encoding()); + assert!(DW_EH_PE_absptr.is_valid_encoding()); + assert!(DW_EH_PE_omit.is_valid_encoding()); + } + + #[test] + fn test_dw_eh_pe_is_valid_encoding_bad_format() { + let encoding = DwEhPe((DW_EH_PE_sdata8.0 + 1) | DW_EH_PE_pcrel.0); + assert!(!encoding.is_valid_encoding()); + } + + #[test] + fn test_dw_eh_pe_is_valid_encoding_bad_application() { + let encoding = DwEhPe(DW_EH_PE_sdata8.0 | (DW_EH_PE_aligned.0 + 1)); + assert!(!encoding.is_valid_encoding()); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/endianity.rs b/chromium_crates_io/vendor/gimli-v0_32/src/endianity.rs new file mode 100644 index 0000000..3201551 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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/chromium_crates_io/vendor/gimli-v0_32/src/leb128.rs b/chromium_crates_io/vendor/gimli-v0_32/src/leb128.rs new file mode 100644 index 0000000..c0ee303 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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(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(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/chromium_crates_io/vendor/gimli-v0_32/src/lib.rs b/chromium_crates_io/vendor/gimli-v0_32/src/lib.rs new file mode 100644 index 0000000..cce4113 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/lib.rs
@@ -0,0 +1,80 @@ +//! `gimli` is a library for reading and writing the +//! [DWARF debugging format](https://dwarfstd.org/). +//! +//! See the [read](./read/index.html) and [write](./write/index.html) modules +//! for examples and API documentation. +//! +//! ## Cargo Features +//! +//! Cargo features that can be enabled with `gimli`: +//! +//! * `std`: Enabled by default. Use the `std` library. Disabling this feature +//! allows using `gimli` in embedded environments that do not have access to +//! `std`. Note that even when `std` is disabled, `gimli` still requires an +//! implementation of the `alloc` crate. +//! +//! * `read`: Enabled by default. Enables the `read` module. Use of `std` is +//! optional. +//! +//! * `write`: Enabled by default. Enables the `write` module. Always uses +//! the `std` library. +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +// Selectively enable rust 2018 warnings +#![warn(bare_trait_objects)] +#![warn(unused_extern_crates)] +#![warn(ellipsis_inclusive_range_patterns)] +#![warn(elided_lifetimes_in_paths)] +#![warn(explicit_outlives_requirements)] +// Style. +#![allow(clippy::bool_to_int_with_if)] +#![allow(clippy::collapsible_else_if)] +#![allow(clippy::comparison_chain)] +#![allow(clippy::manual_range_contains)] +#![allow(clippy::needless_late_init)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::needless_lifetimes)] +// False positives with `fallible_iterator`. +#![allow(clippy::should_implement_trait)] +// False positives. +#![allow(clippy::derive_partial_eq_without_eq)] +#![no_std] + +#[allow(unused_imports)] +#[cfg(any(feature = "read", feature = "write"))] +#[macro_use] +extern crate alloc; + +#[cfg(any(feature = "std", feature = "write"))] +#[macro_use] +extern crate std; + +#[cfg(feature = "endian-reader")] +pub use stable_deref_trait::{CloneStableDeref, StableDeref}; + +mod common; +pub use crate::common::*; + +mod arch; +pub use crate::arch::*; + +pub mod constants; +// For backwards compat. +pub use crate::constants::*; + +mod endianity; +pub use crate::endianity::*; + +pub mod leb128; + +#[cfg(feature = "read-core")] +pub mod read; +// For backwards compat. +#[cfg(feature = "read-core")] +pub use crate::read::*; + +#[cfg(feature = "write")] +pub mod write; + +#[cfg(test)] +mod test_util;
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/abbrev.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/abbrev.rs new file mode 100644 index 0000000..002cc91 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/abbrev.rs
@@ -0,0 +1,1098 @@ +//! Functions for parsing DWARF debugging abbreviations. + +use alloc::collections::btree_map; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::fmt::{self, Debug}; +use core::iter::FromIterator; +use core::ops::Deref; + +use crate::common::{DebugAbbrevOffset, Encoding, SectionId}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::{ + DebugInfoUnitHeadersIter, EndianSlice, Error, Reader, ReaderOffset, Result, Section, UnitHeader, +}; + +/// The `DebugAbbrev` struct represents the abbreviations describing +/// `DebuggingInformationEntry`s' attribute names and forms found in the +/// `.debug_abbrev` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugAbbrev<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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugAbbrev, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_abbrev_section_somehow = || &buf; + /// let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_abbrev_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_abbrev_section, endian)) + } +} + +impl<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. + /// + /// Used by `DwarfSections::borrow`. + 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, + } + } +} + +/// The strategy to use for caching abbreviations. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum AbbreviationsCacheStrategy { + /// Cache abbreviations that are used more than once. + /// + /// This is useful if the units in the `.debug_info` section will be parsed only once. + Duplicates, + /// Cache all abbreviations. + /// + /// This is useful if the units in the `.debug_info` section will be parsed more than once. + All, +} + +/// A cache of previously parsed `Abbreviations`. +#[derive(Debug, Default)] +pub struct AbbreviationsCache { + abbreviations: btree_map::BTreeMap<u64, Result<Arc<Abbreviations>>>, +} + +impl AbbreviationsCache { + /// Create an empty abbreviations cache. + pub fn new() -> Self { + Self::default() + } + + /// Parse abbreviations and store them in the cache. + /// + /// This will iterate over the given units to determine the abbreviations + /// offsets. Any existing cache entries are discarded. + /// + /// Errors during parsing abbreviations are also stored in the cache. + /// Errors during iterating over the units are ignored. + pub fn populate<R: Reader>( + &mut self, + strategy: AbbreviationsCacheStrategy, + debug_abbrev: &DebugAbbrev<R>, + mut units: DebugInfoUnitHeadersIter<R>, + ) { + let mut offsets = Vec::new(); + match strategy { + AbbreviationsCacheStrategy::Duplicates => { + while let Ok(Some(unit)) = units.next() { + offsets.push(unit.debug_abbrev_offset()); + } + offsets.sort_unstable_by_key(|offset| offset.0); + let mut prev_offset = R::Offset::from_u8(0); + let mut count = 0; + offsets.retain(|offset| { + if count == 0 || prev_offset != offset.0 { + prev_offset = offset.0; + count = 1; + } else { + count += 1; + } + count == 2 + }); + } + AbbreviationsCacheStrategy::All => { + while let Ok(Some(unit)) = units.next() { + offsets.push(unit.debug_abbrev_offset()); + } + offsets.sort_unstable_by_key(|offset| offset.0); + offsets.dedup(); + } + } + self.abbreviations = offsets + .into_iter() + .map(|offset| { + ( + offset.0.into_u64(), + debug_abbrev.abbreviations(offset).map(Arc::new), + ) + }) + .collect(); + } + + /// Set an entry in the abbreviations cache. + /// + /// This is only required if you want to manually populate the cache. + pub fn set<R: Reader>( + &mut self, + offset: DebugAbbrevOffset<R::Offset>, + abbreviations: Arc<Abbreviations>, + ) { + self.abbreviations + .insert(offset.0.into_u64(), Ok(abbreviations)); + } + + /// Parse the abbreviations at the given offset. + /// + /// This uses the cache if possible, but does not update it. + pub fn get<R: Reader>( + &self, + debug_abbrev: &DebugAbbrev<R>, + offset: DebugAbbrevOffset<R::Offset>, + ) -> Result<Arc<Abbreviations>> { + match self.abbreviations.get(&offset.0.into_u64()) { + Some(entry) => entry.clone(), + None => debug_abbrev.abbreviations(offset).map(Arc::new), + } + } +} + +/// A set of type abbreviations. +/// +/// Construct an `Abbreviations` instance with the +/// [`abbreviations()`](struct.UnitHeader.html#method.abbreviations) +/// method. +#[derive(Debug, Default, Clone)] +pub struct Abbreviations { + vec: Vec<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>> { + if input.is_empty() { + // Try to recover from missing null terminator. + // If the input was actually truncated, then we'll return an error later + // when trying to find the missing abbreviation. + return Ok(None); + } + 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) => 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); + } + 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 => None, + + // We don't know the size of unknown forms. + _ => None, + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::constants; + use crate::endianity::LittleEndian; + use crate::read::{EndianSlice, Error}; + use crate::test_util::GimliSectionMethods; + #[cfg(target_pointer_width = "32")] + use core::u32; + use test_assembler::Section; + + pub trait AbbrevSectionMethods { + fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self; + fn abbrev_null(self) -> Self; + fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self; + fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self; + fn abbrev_attr_null(self) -> Self; + } + + impl AbbrevSectionMethods for Section { + fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self { + self.uleb(code).uleb(tag.0.into()).D8(children.0) + } + + fn abbrev_null(self) -> Self { + self.D8(0) + } + + fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self { + self.uleb(name.0.into()).uleb(form.0.into()) + } + + fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self { + self.uleb(name.0.into()) + .uleb(constants::DW_FORM_implicit_const.0.into()) + .sleb(value) + } + + fn abbrev_attr_null(self) -> Self { + self.D8(0).D8(0) + } + } + + #[test] + fn test_debug_abbrev_ok() { + let extra_start = [1, 2, 3, 4]; + let expected_rest = [5, 6, 7, 8]; + #[rustfmt::skip] + let buf = Section::new() + .append_bytes(&extra_start) + .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) + .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) + .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) + .abbrev_attr_null() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + + let abbrev1 = Abbreviation::new( + 1, + constants::DW_TAG_compile_unit, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new( + constants::DW_AT_producer, + constants::DW_FORM_strp, + None, + ), + AttributeSpecification::new( + constants::DW_AT_language, + constants::DW_FORM_data2, + None, + ), + ] + .into(), + ); + + let abbrev2 = Abbreviation::new( + 2, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + None, + )] + .into(), + ); + + let debug_abbrev = DebugAbbrev::new(&buf, LittleEndian); + let debug_abbrev_offset = DebugAbbrevOffset(extra_start.len()); + let abbrevs = debug_abbrev + .abbreviations(debug_abbrev_offset) + .expect("Should parse abbreviations"); + assert_eq!(abbrevs.get(1), Some(&abbrev1)); + assert_eq!(abbrevs.get(2), Some(&abbrev2)); + } + + #[test] + fn test_abbreviations_insert() { + fn abbrev(code: u16) -> Abbreviation { + Abbreviation::new( + code.into(), + constants::DwTag(code), + constants::DW_CHILDREN_no, + vec![].into(), + ) + } + + fn assert_abbrev(abbrevs: &Abbreviations, code: u16) { + let abbrev = abbrevs.get(code.into()).unwrap(); + assert_eq!(abbrev.tag(), constants::DwTag(code)); + } + + // Sequential insert. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.vec.len(), 2); + assert!(abbrevs.map.is_empty()); + assert_abbrev(&abbrevs, 1); + assert_abbrev(&abbrevs, 2); + + // Out of order insert. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + abbrevs.insert(abbrev(3)).unwrap(); + assert!(abbrevs.vec.is_empty()); + assert_abbrev(&abbrevs, 2); + assert_abbrev(&abbrevs, 3); + + // Mixed order insert. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + abbrevs.insert(abbrev(3)).unwrap(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.vec.len(), 2); + assert_abbrev(&abbrevs, 1); + assert_abbrev(&abbrevs, 2); + assert_abbrev(&abbrevs, 3); + + // Duplicate code in vec. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.insert(abbrev(1)), Err(())); + assert_eq!(abbrevs.insert(abbrev(2)), Err(())); + + // Duplicate code in map when adding to map. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.insert(abbrev(2)), Err(())); + + // Duplicate code in map when adding to vec. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + abbrevs.insert(abbrev(1)).unwrap(); + assert_eq!(abbrevs.insert(abbrev(2)), Err(())); + + // 32-bit usize conversions. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + } + + #[test] + #[cfg(target_pointer_width = "32")] + fn test_abbreviations_insert_32() { + fn abbrev(code: u64) -> Abbreviation { + Abbreviation::new( + code, + constants::DwTag(code as u16), + constants::DW_CHILDREN_no, + vec![].into(), + ) + } + + fn assert_abbrev(abbrevs: &Abbreviations, code: u64) { + let abbrev = abbrevs.get(code).unwrap(); + assert_eq!(abbrev.tag(), constants::DwTag(code as u16)); + } + + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + + let wrap_code = (u32::MAX as u64 + 1) + 1; + // `get` should not treat the wrapped code as `1`. + assert_eq!(abbrevs.get(wrap_code), None); + // `insert` should not treat the wrapped code as `1`. + abbrevs.insert(abbrev(wrap_code)).unwrap(); + assert_abbrev(&abbrevs, 1); + assert_abbrev(&abbrevs, wrap_code); + } + + #[test] + fn test_parse_abbreviations_ok() { + let expected_rest = [1, 2, 3, 4]; + #[rustfmt::skip] + let buf = Section::new() + .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) + .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) + .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) + .abbrev_attr_null() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let abbrev1 = Abbreviation::new( + 1, + constants::DW_TAG_compile_unit, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new( + constants::DW_AT_producer, + constants::DW_FORM_strp, + None, + ), + AttributeSpecification::new( + constants::DW_AT_language, + constants::DW_FORM_data2, + None, + ), + ] + .into(), + ); + + let abbrev2 = Abbreviation::new( + 2, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + None, + )] + .into(), + ); + + let abbrevs = Abbreviations::parse(rest).expect("Should parse abbreviations"); + assert_eq!(abbrevs.get(1), Some(&abbrev1)); + assert_eq!(abbrevs.get(2), Some(&abbrev2)); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_abbreviations_duplicate() { + let expected_rest = [1, 2, 3, 4]; + #[rustfmt::skip] + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) + .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) + .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) + .abbrev_attr_null() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match Abbreviations::parse(buf) { + Err(Error::DuplicateAbbreviationCode) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_abbreviation_tag_ok() { + let buf = [0x01, 0x02]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let tag = Abbreviation::parse_tag(rest).expect("Should parse tag"); + assert_eq!(tag, constants::DW_TAG_array_type); + assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian)); + } + + #[test] + fn test_parse_abbreviation_tag_zero() { + let buf = [0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match Abbreviation::parse_tag(buf) { + Err(Error::AbbreviationTagZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_abbreviation_has_children() { + let buf = [0x00, 0x01, 0x02]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let val = Abbreviation::parse_has_children(rest).expect("Should parse children"); + assert_eq!(val, constants::DW_CHILDREN_no); + let val = Abbreviation::parse_has_children(rest).expect("Should parse children"); + assert_eq!(val, constants::DW_CHILDREN_yes); + match Abbreviation::parse_has_children(rest) { + Err(Error::BadHasChildren) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_abbreviation_ok() { + let expected_rest = [0x01, 0x02, 0x03, 0x04]; + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let expect = Some(Abbreviation::new( + 1, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + None, + )] + .into(), + )); + + let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation"); + assert_eq!(abbrev, expect); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_abbreviation_implicit_const_ok() { + let expected_rest = [0x01, 0x02, 0x03, 0x04]; + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr_implicit_const(constants::DW_AT_name, -42) + .abbrev_attr_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let expect = Some(Abbreviation::new( + 1, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_implicit_const, + Some(-42), + )] + .into(), + )); + + let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation"); + assert_eq!(abbrev, expect); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_abbreviation_implicit_const_no_const() { + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_implicit_const) + .get_contents() + .unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match Abbreviation::parse(buf) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_parse_null_abbreviation_ok() { + let expected_rest = [0x01, 0x02, 0x03, 0x04]; + let buf = Section::new() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let abbrev = Abbreviation::parse(rest).expect("Should parse null abbreviation"); + assert!(abbrev.is_none()); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_attribute_form_ok() { + let buf = [0x01, 0x02]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let tag = AttributeSpecification::parse_form(rest).expect("Should parse form"); + assert_eq!(tag, constants::DW_FORM_addr); + assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian)); + } + + #[test] + fn test_parse_attribute_form_zero() { + let buf = [0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match AttributeSpecification::parse_form(buf) { + Err(Error::AttributeFormZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_null_attribute_specification_ok() { + let buf = [0x00, 0x00, 0x01]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let attr = + AttributeSpecification::parse(rest).expect("Should parse null attribute specification"); + assert!(attr.is_none()); + assert_eq!(*rest, EndianSlice::new(&buf[2..], LittleEndian)); + } + + #[test] + fn test_parse_attribute_specifications_name_zero() { + let buf = [0x00, 0x01, 0x00, 0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match AttributeSpecification::parse(buf) { + Err(Error::ExpectedZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_attribute_specifications_form_zero() { + let buf = [0x01, 0x00, 0x00, 0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match AttributeSpecification::parse(buf) { + Err(Error::AttributeFormZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_get_abbrev_zero() { + let mut abbrevs = Abbreviations::empty(); + abbrevs + .insert(Abbreviation::new( + 1, + constants::DwTag(1), + constants::DW_CHILDREN_no, + vec![].into(), + )) + .unwrap(); + assert!(abbrevs.get(0).is_none()); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/addr.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/addr.rs new file mode 100644 index 0000000..5c715db --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/addr.rs
@@ -0,0 +1,353 @@ +use crate::common::{DebugAddrBase, DebugAddrIndex, DebugAddrOffset, Encoding, SectionId}; +use crate::read::{Error, 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> { + /// 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) + } + + /// Iterate the sets of entries in the `.debug_addr` section. + /// + /// Each set of entries belongs to a single unit. + pub fn headers(&self) -> AddrHeaderIter<R> { + AddrHeaderIter { + input: self.section.clone(), + offset: DebugAddrOffset(R::Offset::from_u8(0)), + } + } +} + +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. + /// + /// Used by `DwarfSections::borrow`. + 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 } + } +} + +/// An iterator over the headers of a `.debug_addr` section. +#[derive(Clone, Debug)] +pub struct AddrHeaderIter<R: Reader> { + input: R, + offset: DebugAddrOffset<R::Offset>, +} + +impl<R: Reader> AddrHeaderIter<R> { + /// Advance the iterator to the next header. + pub fn next(&mut self) -> Result<Option<AddrHeader<R>>> { + if self.input.is_empty() { + return Ok(None); + } + + let len = self.input.len(); + match AddrHeader::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 AddrHeaderIter<R> { + type Item = AddrHeader<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + AddrHeaderIter::next(self) + } +} + +/// A header for a set of entries in the `.debug_addr` section. +/// +/// These entries all belong to a single unit. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AddrHeader<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + offset: DebugAddrOffset<Offset>, + encoding: Encoding, + length: Offset, + entries: R, +} + +impl<R, Offset> AddrHeader<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + fn parse(input: &mut R, offset: DebugAddrOffset<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 5. + let version = rest.read_u16()?; + if version != 5 { + return Err(Error::UnknownVersion(u64::from(version))); + } + + let address_size = rest.read_address_size()?; + let segment_size = rest.read_u8()?; + if segment_size != 0 { + return Err(Error::UnsupportedSegmentSize); + } + + // unit_length + version + address_size + segment_size + let header_length = format.initial_length_size() + 2 + 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, + // which must be zero, and an address). + let tuple_length = address_size; + if tuple_length == 0 { + return Err(Error::UnsupportedAddressSize(address_size)); + } + let padding = if header_length % tuple_length == 0 { + 0 + } else { + tuple_length - header_length % tuple_length + }; + rest.skip(R::Offset::from_u8(padding))?; + + let encoding = Encoding { + format, + version, + address_size, + }; + Ok(AddrHeader { + offset, + encoding, + length, + entries: rest, + }) + } + + /// Return the offset of this header within the `.debug_addr` section. + #[inline] + pub fn offset(&self) -> DebugAddrOffset<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 address entries in this set. + #[inline] + pub fn entries(&self) -> AddrEntryIter<R> { + AddrEntryIter { + input: self.entries.clone(), + encoding: self.encoding, + } + } +} + +/// An iterator over the addresses from a `.debug_addr` section. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Debug, Clone)] +pub struct AddrEntryIter<R: Reader> { + input: R, + encoding: Encoding, +} + +impl<R: Reader> AddrEntryIter<R> { + /// Advance the iterator and return the next address. + /// + /// Returns the newly parsed address as `Ok(Some(addr))`. Returns `Ok(None)` + /// when iteration is complete and all addresses have already been parsed and + /// yielded. If an error occurs while parsing the next address, then this error + /// is returned as `Err(e)`, and all subsequent calls return `Ok(None)`. + pub fn next(&mut self) -> Result<Option<u64>> { + if self.input.is_empty() { + return Ok(None); + } + + match self.input.read_address(self.encoding.address_size) { + Ok(entry) => Ok(Some(entry)), + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for AddrEntryIter<R> { + type Item = u64; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + AddrEntryIter::next(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::read::EndianSlice; + use crate::test_util::GimliSectionMethods; + use crate::{Format, LittleEndian}; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_get_address() { + for format in [Format::Dwarf32, Format::Dwarf64] { + for address_size in [4, 8] { + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(5) + .D8(address_size) + .D8(0) + .mark(&first); + for i in 0..20 { + section = section.word(address_size, 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + + let section = section.get_contents().unwrap(); + let debug_addr = DebugAddr::from(EndianSlice::new(§ion, LittleEndian)); + let base = DebugAddrBase((&first - &zero) as usize); + + assert_eq!( + debug_addr.get_address(address_size, base, DebugAddrIndex(0)), + Ok(1000) + ); + assert_eq!( + debug_addr.get_address(address_size, base, DebugAddrIndex(19)), + Ok(1019) + ); + } + } + } + + #[test] + fn test_iterator() { + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + // First CU. + let mut section = Section::with_endian(Endian::Little) + .initial_length(Format::Dwarf32, &length, &start) + .D16(5) // Version + .D8(4) // Address size + .D8(0) // Segment size + .word(4, 0x12345678) + .word(4, 0xdeadbeef) + .mark(&end); + length.set_const((&end - &start) as u64); + // Second CU. + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + section = section + .initial_length(Format::Dwarf64, &length, &start) + .D16(5) // Version + .D8(8) // Address size + .D8(0) // Segment size + .word(8, 0x123456789abcdef0) + .word(8, 0xdeadbeefdeadbeef) + .mark(&end); + length.set_const((&end - &start) as u64); + let section = section.get_contents().unwrap(); + let debug_addr = DebugAddr::from(EndianSlice::new(§ion, LittleEndian)); + let mut iter = debug_addr.headers(); + let first_header = iter.next().unwrap().unwrap(); + let first_encoding = first_header.encoding(); + assert_eq!(first_encoding.address_size, 4); + assert_eq!(first_encoding.format, Format::Dwarf32); + assert_eq!(first_encoding.version, 5); + assert_eq!(first_header.length(), 12); + let mut first_entries = first_header.entries(); + assert_eq!(first_entries.next(), Ok(Some(0x12345678))); + assert_eq!(first_entries.next(), Ok(Some(0xdeadbeef))); + assert_eq!(first_entries.next(), Ok(None)); + let second_header = iter.next().unwrap().unwrap(); + let second_encoding = second_header.encoding(); + assert_eq!(second_encoding.address_size, 8); + assert_eq!(second_encoding.format, Format::Dwarf64); + assert_eq!(second_encoding.version, 5); + assert_eq!(second_header.length(), 20); + let mut second_entries = second_header.entries(); + assert_eq!(second_entries.next(), Ok(Some(0x123456789abcdef0))); + assert_eq!(second_entries.next(), Ok(Some(0xdeadbeefdeadbeef))); + assert_eq!(second_entries.next(), Ok(None)); + assert_eq!(iter.next(), Ok(None)); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/aranges.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/aranges.rs new file mode 100644 index 0000000..e8cfbf3 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/aranges.rs
@@ -0,0 +1,809 @@ +use crate::common::{DebugArangesOffset, DebugInfoOffset, Encoding, SectionId}; +use crate::endianity::Endianity; +use crate::read::{ + EndianSlice, Error, Range, Reader, ReaderAddress, ReaderOffset, Result, Section, +}; + +/// The `DebugAranges` struct represents the DWARF address range information +/// found in the `.debug_aranges` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugAranges<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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugAranges, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_aranges_section = || &buf; + /// let debug_aranges = + /// DebugAranges::new(read_debug_aranges_section(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + DebugAranges { + section: EndianSlice::new(section, endian), + } + } +} + +impl<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. + /// + /// Used by `DwarfSections::borrow`. + 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>, + 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_address_size()?; + let segment_size = rest.read_u8()?; + if segment_size != 0 { + return Err(Error::UnsupportedSegmentSize); + } + + // unit_length + version + offset + address_size + segment_size + let header_length = format.initial_length_size() + 2 + format.word_size() + 1 + 1; + + // The first tuple following the header in each set begins at an offset that is + // a multiple of the size of a single tuple (that is, twice the size of an address). + let tuple_length = address_size + .checked_mul(2) + .ok_or(Error::UnsupportedAddressSize(address_size))?; + if tuple_length == 0 { + return Err(Error::UnsupportedAddressSize(address_size)); + } + let padding = if header_length % tuple_length == 0 { + 0 + } else { + tuple_length - header_length % tuple_length + }; + rest.skip(R::Offset::from_u8(padding))?; + + let encoding = Encoding { + format, + version, + address_size, + }; + Ok(ArangeHeader { + offset, + encoding, + length, + debug_info_offset, + entries: rest, + }) + } + + /// Return the offset of this header within the `.debug_aranges` section. + #[inline] + pub fn offset(&self) -> DebugArangesOffset<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 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, + } + } +} + +/// 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, +} + +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>> { + loop { + let raw_entry = match self.next_raw()? { + Some(entry) => entry, + None => return Ok(None), + }; + + let entry = self.convert_raw(raw_entry)?; + if entry.is_some() { + return Ok(entry); + } + } + } + + /// Advance the iterator and return the next arange without validating it. + /// + /// The returned entry will have `range.end` set to 0. + /// This will return tombstone entries as well. + pub fn next_raw(&mut self) -> Result<Option<ArangeEntry>> { + if self.input.is_empty() { + return Ok(None); + } + + match ArangeEntry::parse(&mut self.input, self.encoding) { + Ok(Some(entry)) => Ok(Some(entry)), + Ok(None) => { + self.input.empty(); + Ok(None) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } + + /// Convert a raw range into a range. + /// + /// The raw range should have been obtained from `next_raw`. + #[doc(hidden)] + pub fn convert_raw(&self, mut entry: ArangeEntry) -> Result<Option<ArangeEntry>> { + // Skip negative tombstone entries. + // Callers must handle tombstones of 0 or greater themselves + // because we have no way of knowing if they are valid or not. + let address_size = self.encoding.address_size; + if entry.range.begin >= u64::min_tombstone(address_size) { + return Ok(None); + } + + // Calculate end now so that we can handle overflow. + entry.range.end = entry.range.begin.add_sized(entry.length, address_size)?; + Ok(Some(entry)) + } +} + +#[cfg(feature = "fallible-iterator")] +impl<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 { + range: Range, + 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) -> Result<Option<Self>> { + let address_size = encoding.address_size; + + let tuple_length = R::Offset::from_u8(2 * address_size); + if tuple_length > input.len() { + input.empty(); + return Ok(None); + } + + let begin = input.read_address(address_size)?; + let length = input.read_address(address_size)?; + let range = Range { begin, end: 0 }; + + match (begin, length) { + // This is meant to be a null terminator, but in practice it can occur + // before the end, possibly due to a linker omitting a function and + // leaving an unrelocated entry. + (0, 0) => Self::parse(input, encoding), + _ => Ok(Some(ArangeEntry { range, length })), + } + } + + /// Return the beginning address of this arange. + #[inline] + pub fn address(&self) -> u64 { + self.range.begin + } + + /// Return the length of this arange. + #[inline] + pub fn length(&self) -> u64 { + self.length + } + + /// Return the range. + #[inline] + pub fn range(&self) -> Range { + self.range + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::{DebugInfoOffset, Format}; + use crate::endianity::LittleEndian; + use crate::read::EndianSlice; + + #[test] + fn test_iterate_headers() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 28. + 0x1c, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size. + 0x04, + // Segment size. + 0x00, + // Dummy padding and arange tuples. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 32-bit length = 36. + 0x24, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x11, 0x12, 0x13, 0x14, + // Address size. + 0x04, + // Segment size. + 0x00, + // Dummy padding and arange tuples. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let debug_aranges = DebugAranges::new(&buf, LittleEndian); + let mut headers = debug_aranges.headers(); + + let header = headers + .next() + .expect("should parse header ok") + .expect("should have a header"); + assert_eq!(header.offset(), DebugArangesOffset(0)); + assert_eq!(header.debug_info_offset(), DebugInfoOffset(0x0403_0201)); + + let header = headers + .next() + .expect("should parse header ok") + .expect("should have a header"); + assert_eq!(header.offset(), DebugArangesOffset(0x20)); + assert_eq!(header.debug_info_offset(), DebugInfoOffset(0x1413_1211)); + } + + #[test] + fn test_parse_header_ok() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 28 (8 bytes header, 4 bytes padding, 16 bytes tuple data). + 0x1c, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size. + 0x08, + // Segment size. + 0x00, + // Length to here = 12, tuple length = 16. + // Padding to tuple length multiple = 4. + 0x10, 0x00, 0x00, 0x00, + + // Dummy arange tuple data. + 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next arange. + 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let header = + ArangeHeader::parse(rest, DebugArangesOffset(0x10)).expect("should parse header ok"); + + assert_eq!( + *rest, + EndianSlice::new(&buf[buf.len() - 16..], LittleEndian) + ); + assert_eq!( + header, + ArangeHeader { + offset: DebugArangesOffset(0x10), + encoding: Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 8, + }, + length: 0x1c, + debug_info_offset: DebugInfoOffset(0x0403_0201), + entries: EndianSlice::new(&buf[buf.len() - 32..buf.len() - 16], LittleEndian), + } + ); + } + + #[test] + fn test_parse_header_overflow_error() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 32. + 0x20, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size. + 0xff, + // Segment size. + 0x00, + // Length to here = 12, tuple length = 20. + // Padding to tuple length multiple = 4. + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy arange tuple data. + 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next arange. + 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10)) + .expect_err("should fail to parse header"); + assert_eq!(error, Error::UnsupportedAddressSize(0xff)); + } + + #[test] + fn test_parse_header_div_by_zero_error() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 32. + 0x20, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size = 0. Could cause a division by zero if we aren't + // careful. + 0x00, + // Segment size. + 0x00, + // Length to here = 12, tuple length = 20. + // Padding to tuple length multiple = 4. + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy arange tuple data. + 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next arange. + 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10)) + .expect_err("should fail to parse header"); + assert_eq!(error, Error::UnsupportedAddressSize(0)); + } + + #[test] + fn test_parse_entry_ok() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 4, + }; + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next().expect("should parse entry ok"); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) + ); + assert_eq!( + entry, + Some(ArangeEntry { + range: Range { + begin: 0x0403_0201, + end: 0x0403_0201 + 0x0807_0605, + }, + length: 0x0807_0605, + }) + ); + } + + #[test] + fn test_parse_entry_zero() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 4, + }; + #[rustfmt::skip] + let buf = [ + // Zero tuple. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Address. + 0x01, 0x02, 0x03, 0x04, + // Length. + 0x05, 0x06, 0x07, 0x08, + // Next tuple. + 0x09 + ]; + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next().expect("should parse entry ok"); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) + ); + assert_eq!( + entry, + Some(ArangeEntry { + range: Range { + begin: 0x0403_0201, + end: 0x0403_0201 + 0x0807_0605, + }, + length: 0x0807_0605, + }) + ); + } + + #[test] + fn test_parse_entry_overflow_32() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 4, + }; + #[rustfmt::skip] + let buf = [ + // Address. + 0x01, 0x02, 0x03, 0x84, + // Length. + 0x05, 0x06, 0x07, 0x88, + // Next tuple. + 0x09 + ]; + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next(); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) + ); + assert_eq!(entry, Err(Error::AddressOverflow)); + } + + #[test] + fn test_parse_entry_overflow_64() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 8, + }; + #[rustfmt::skip] + let buf = [ + // Address. + 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x80, + // Length. + 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x80, + // Next tuple. + 0x09 + ]; + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next(); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) + ); + assert_eq!(entry, Err(Error::AddressOverflow)); + } + + #[test] + fn test_parse_entry_tombstone_32() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 4, + }; + #[rustfmt::skip] + let buf = [ + // Address. + 0xff, 0xff, 0xff, 0xff, + // Length. + 0x05, 0x06, 0x07, 0x08, + // Address. + 0x01, 0x02, 0x03, 0x04, + // Length. + 0x05, 0x06, 0x07, 0x08, + // Next tuple. + 0x09 + ]; + + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next_raw().unwrap(); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 9..], LittleEndian) + ); + assert_eq!( + entry, + Some(ArangeEntry { + range: Range { + begin: 0xffff_ffff, + end: 0, + }, + length: 0x0807_0605, + }) + ); + + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next().unwrap(); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) + ); + assert_eq!( + entry, + Some(ArangeEntry { + range: Range { + begin: 0x0403_0201, + end: 0x0403_0201 + 0x0807_0605, + }, + length: 0x0807_0605, + }) + ); + } + + #[test] + fn test_parse_entry_tombstone_64() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 8, + }; + #[rustfmt::skip] + let buf = [ + // Address. + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + // Length. + 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, + // Address. + 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, + // Length. + 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, + // Next tuple. + 0x09 + ]; + + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next_raw().unwrap(); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 17..], LittleEndian) + ); + assert_eq!( + entry, + Some(ArangeEntry { + range: Range { + begin: 0xffff_ffff_ffff_ffff, + end: 0, + }, + length: 0x0807_0605, + }) + ); + + let mut iter = ArangeEntryIter { + input: EndianSlice::new(&buf, LittleEndian), + encoding, + }; + let entry = iter.next().unwrap(); + assert_eq!( + iter.input, + EndianSlice::new(&buf[buf.len() - 1..], LittleEndian) + ); + assert_eq!( + entry, + Some(ArangeEntry { + range: Range { + begin: 0x0403_0201, + end: 0x0403_0201 + 0x0807_0605, + }, + length: 0x0807_0605, + }) + ); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/cfi.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/cfi.rs new file mode 100644 index 0000000..2c865c4 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/cfi.rs
@@ -0,0 +1,7951 @@ +#[cfg(feature = "read")] +use alloc::boxed::Box; + +use core::cmp::Ordering; +use core::fmt::{self, Debug}; +use core::iter::FromIterator; +use core::mem; +use core::num::Wrapping; + +use super::util::{ArrayLike, ArrayVec}; +use crate::common::{ + DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId, Vendor, +}; +use crate::constants::{self, DwEhPe}; +use crate::endianity::Endianity; +use crate::read::{ + EndianSlice, Error, Expression, Reader, ReaderAddress, ReaderOffset, Result, Section, + StoreOnHeap, +}; + +/// `DebugFrame` contains the `.debug_frame` section's frame unwinding +/// information required to unwind to and recover registers from older frames on +/// the stack. For example, this is useful for a debugger that wants to print +/// locals in a backtrace. +/// +/// Most interesting methods are defined in the +/// [`UnwindSection`](trait.UnwindSection.html) trait. +/// +/// ### Differences between `.debug_frame` and `.eh_frame` +/// +/// While the `.debug_frame` section's information has a lot of overlap with the +/// `.eh_frame` section's information, the `.eh_frame` information tends to only +/// encode the subset of information needed for exception handling. Often, only +/// one of `.eh_frame` or `.debug_frame` will be present in an object file. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct DebugFrame<R: Reader> { + section: R, + address_size: u8, + vendor: Vendor, +} + +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 vendor extensions to use. + /// + /// This defaults to `Vendor::Default`. + pub fn set_vendor(&mut self, vendor: Vendor) { + self.vendor = vendor; + } +} + +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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugFrame, NativeEndian}; + /// + /// // Use with `.debug_frame` + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_frame_section_somehow = || &buf; + /// let debug_frame = DebugFrame::new(read_debug_frame_section_somehow(), NativeEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<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 native word size. + DebugFrame { + section, + address_size: mem::size_of::<usize>() as u8, + vendor: Vendor::Default, + } + } +} + +/// `EhFrameHdr` contains the information about the `.eh_frame_hdr` section. +/// +/// A pointer to the start of the `.eh_frame` data, and optionally, a binary +/// search table of pointers to the `.eh_frame` records that are found in this section. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EhFrameHdr<R: 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, ¶meters, &mut reader)?; + + let fde_count; + if fde_count_enc == constants::DW_EH_PE_omit || table_enc == constants::DW_EH_PE_omit { + fde_count = 0 + } else { + if fde_count_enc != fde_count_enc.format() { + return Err(Error::UnsupportedPointerEncoding); + } + fde_count = parse_encoded_value(fde_count_enc, ¶meters, &mut reader)?; + } + + Ok(ParsedEhFrameHdr { + address_size, + section: self.0.clone(), + + eh_frame_ptr, + fde_count, + table_enc, + table: reader, + }) + } +} + +impl<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 }) + } + } +} + +/// An iterator for `.eh_frame_hdr` section's binary search table. +/// +/// Each table entry consists of a tuple containing an `initial_location` and `address`. +/// The `initial location` represents the first address that the targeted FDE +/// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. +/// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. +#[derive(Debug)] +pub struct EhHdrTableIter<'a, 'bases, R: Reader> { + hdr: &'a ParsedEhFrameHdr<R>, + table: R, + bases: &'bases BaseAddresses, + remain: u64, +} + +impl<'a, 'bases, R: Reader> EhHdrTableIter<'a, 'bases, R> { + /// Yield the next entry in the `EhHdrTableIter`. + pub fn next(&mut self) -> Result<Option<(Pointer, Pointer)>> { + if self.remain == 0 { + return Ok(None); + } + + let parameters = PointerEncodingParameters { + bases: &self.bases.eh_frame_hdr, + func_base: None, + address_size: self.hdr.address_size, + section: &self.hdr.section, + }; + + self.remain -= 1; + let from = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; + let to = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; + Ok(Some((from, to))) + } + /// Yield the nth entry in the `EhHdrTableIter` + pub fn nth(&mut self, n: usize) -> Result<Option<(Pointer, Pointer)>> { + use core::convert::TryFrom; + let size = match self.hdr.table_enc.format() { + constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => { + return Err(Error::VariableLengthSearchTable); + } + constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2, + constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4, + constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8, + _ => return Err(Error::UnknownPointerEncoding(self.hdr.table_enc)), + }; + + let row_size = size * 2; + let n = u64::try_from(n).map_err(|_| Error::UnsupportedOffset)?; + self.remain = self.remain.saturating_sub(n); + self.table.skip(R::Offset::from_u64(n * row_size)?)?; + self.next() + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'a, 'bases, R: Reader> fallible_iterator::FallibleIterator for EhHdrTableIter<'a, 'bases, R> { + type Item = (Pointer, Pointer); + type Error = Error; + fn next(&mut self) -> Result<Option<Self::Item>> { + EhHdrTableIter::next(self) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + use core::convert::TryInto; + ( + self.remain.try_into().unwrap_or(0), + self.remain.try_into().ok(), + ) + } + + fn nth(&mut self, n: usize) -> Result<Option<Self::Item>> { + EhHdrTableIter::nth(self, n) + } +} + +/// The CFI binary search table that is an optional part of the `.eh_frame_hdr` section. +#[derive(Debug, Clone)] +pub struct EhHdrTable<'a, R: Reader> { + hdr: &'a ParsedEhFrameHdr<R>, +} + +impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { + /// Return an iterator that can walk the `.eh_frame_hdr` table. + /// + /// Each table entry consists of a tuple containing an `initial_location` and `address`. + /// The `initial location` represents the first address that the targeted FDE + /// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. + /// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. + pub fn iter<'bases>(&self, bases: &'bases BaseAddresses) -> EhHdrTableIter<'_, 'bases, R> { + EhHdrTableIter { + hdr: self.hdr, + bases, + remain: self.hdr.fde_count, + table: self.hdr.table.clone(), + } + } + /// *Probably* returns a pointer to the FDE for the given address. + /// + /// This performs a binary search, so if there is no FDE for the given address, + /// this function **will** return a pointer to any other FDE that's close by. + /// + /// To be sure, you **must** call `contains` on the FDE. + pub fn lookup(&self, address: u64, bases: &BaseAddresses) -> Result<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(self.hdr.table_enc)), + }; + + let row_size = size * 2; + + let mut len = self.hdr.fde_count; + + let mut reader = self.hdr.table.clone(); + + let parameters = PointerEncodingParameters { + bases: &bases.eh_frame_hdr, + func_base: None, + address_size: self.hdr.address_size, + section: &self.hdr.section, + }; + + while len > 1 { + let head = reader.split(R::Offset::from_u64((len / 2) * row_size)?)?; + let tail = reader.clone(); + + let pivot = + parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader)?.direct()?; + + match pivot.cmp(&address) { + Ordering::Equal => { + reader = tail; + break; + } + Ordering::Less => { + reader = tail; + len = len - (len / 2); + } + Ordering::Greater => { + reader = head; + len /= 2; + } + } + } + + reader.skip(R::Offset::from_u64(size)?)?; + + parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader) + } + + /// Convert a `Pointer` to a section offset. + /// + /// This does not support indirect pointers. + pub fn pointer_to_offset(&self, ptr: Pointer) -> Result<EhFrameOffset<R::Offset>> { + let ptr = ptr.direct()?; + let eh_frame_ptr = self.hdr.eh_frame_ptr().direct()?; + + // Calculate the offset in the EhFrame section + R::Offset::from_u64(ptr - eh_frame_ptr).map(EhFrameOffset) + } + + /// Returns a parsed FDE for the given address, or `NoUnwindInfoForAddress` + /// if there are none. + /// + /// You must provide a function to get its associated CIE. See + /// `PartialFrameDescriptionEntry::parse` for more information. + /// + /// # Example + /// + /// ``` + /// # use gimli::{BaseAddresses, EhFrame, ParsedEhFrameHdr, EndianSlice, NativeEndian, Error, UnwindSection}; + /// # fn foo() -> Result<(), Error> { + /// # let eh_frame: EhFrame<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, S>( + &self, + frame: &EhFrame<R>, + bases: &BaseAddresses, + ctx: &'ctx mut UnwindContext<R::Offset, S>, + address: u64, + get_cie: F, + ) -> Result<&'ctx UnwindTableRow<R::Offset, S>> + where + F: FnMut( + &EhFrame<R>, + &BaseAddresses, + EhFrameOffset<R::Offset>, + ) -> Result<CommonInformationEntry<R>>, + S: UnwindContextStorage<R::Offset>, + { + 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, + vendor: Vendor, +} + +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 + } + + /// Set the vendor extensions to use. + /// + /// This defaults to `Vendor::Default`. + pub fn set_vendor(&mut self, vendor: Vendor) { + self.vendor = vendor; + } +} + +impl<'input, Endian> EhFrame<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `EhFrame` instance from the data in the + /// `.eh_frame` section. + /// + /// It is the caller's responsibility to read the section and present it as + /// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O + /// loader on macOS, etc. + /// + /// ``` + /// use gimli::{EhFrame, EndianSlice, NativeEndian}; + /// + /// // Use with `.eh_frame` + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_eh_frame_section_somehow = || &buf; + /// let eh_frame = EhFrame::new(read_eh_frame_section_somehow(), NativeEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<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, + vendor: Vendor::Default, + } + } +} + +// This has to be `pub` to silence a warning (that is deny(..)'d by default) in +// rustc. Eventually, not having this `pub` will become a hard error. +#[doc(hidden)] +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CieOffsetEncoding { + U32, + U64, +} + +/// An offset into an `UnwindSection`. +// +// Needed to avoid conflicting implementations of `Into<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 section allows a zero terminator. + fn has_zero_terminator() -> bool; + + /// Return true if the given offset if the CIE sentinel, false otherwise. + fn is_cie(format: Format, id: u64) -> bool; + + /// Return the CIE offset/ID encoding used by this unwind section with the + /// given DWARF format. + fn cie_offset_encoding(format: Format) -> CieOffsetEncoding; + + /// For `.eh_frame`, CIE offsets are relative to the current position. For + /// `.debug_frame`, they are relative to the start of the section. We always + /// internally store them relative to the section, so we handle translating + /// `.eh_frame`'s relative offsets in this method. If the offset calculation + /// underflows, return `None`. + fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<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 vendor extensions to use. + fn vendor(&self) -> Vendor; +} + +/// A section holding unwind information: either `.debug_frame` or +/// `.eh_frame`. See [`DebugFrame`](./struct.DebugFrame.html) and +/// [`EhFrame`](./struct.EhFrame.html) respectively. +pub trait UnwindSection<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, S>( + &self, + bases: &BaseAddresses, + ctx: &'ctx mut UnwindContext<R::Offset, S>, + address: u64, + get_cie: F, + ) -> Result<&'ctx UnwindTableRow<R::Offset, S>> + where + F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>, + S: UnwindContextStorage<R::Offset>, + { + 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 has_zero_terminator() -> bool { + false + } + + fn is_cie(format: Format, id: u64) -> bool { + match format { + Format::Dwarf32 => id == 0xffff_ffff, + Format::Dwarf64 => id == 0xffff_ffff_ffff_ffff, + } + } + + fn cie_offset_encoding(format: Format) -> CieOffsetEncoding { + match format { + Format::Dwarf32 => CieOffsetEncoding::U32, + Format::Dwarf64 => CieOffsetEncoding::U64, + } + } + + fn resolve_cie_offset(&self, _: R::Offset, offset: R::Offset) -> Option<R::Offset> { + Some(offset) + } + + fn has_address_and_segment_sizes(version: u8) -> bool { + version == 4 + } + + fn address_size(&self) -> u8 { + self.address_size + } + + fn vendor(&self) -> Vendor { + self.vendor + } +} + +impl<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 has_zero_terminator() -> bool { + true + } + + fn is_cie(_: Format, id: u64) -> bool { + id == 0 + } + + fn cie_offset_encoding(_format: Format) -> CieOffsetEncoding { + // `.eh_frame` offsets are always 4 bytes, regardless of the DWARF + // format. + CieOffsetEncoding::U32 + } + + fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<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 vendor(&self) -> Vendor { + self.vendor + } +} + +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>>> { + loop { + if self.input.is_empty() { + return Ok(None); + } + + match parse_cfi_entry(self.bases, &self.section, &mut self.input) { + Ok(Some(entry)) => return Ok(Some(entry)), + Err(e) => { + self.input.empty(); + return Err(e); + } + Ok(None) => { + if Section::has_zero_terminator() { + self.input.empty(); + return Ok(None); + } + + // Hack: If we get to here, then we're reading `.debug_frame` and + // encountered a length of 0. This is a compiler or linker bug + // (originally seen for NASM, fixed in 2.15rc9). + // Skip this value and try again. + continue; + } + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'bases, Section, R> fallible_iterator::FallibleIterator for CfiEntriesIter<'bases, Section, R> +where + R: Reader, + Section: UnwindSection<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>), +} + +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 = input.offset_from(section.section()); + let (length, format) = input.read_initial_length()?; + if length.into_u64() == 0 { + return Ok(None); + } + + let mut rest = input.split(length)?; + let cie_offset_base = rest.offset_from(section.section()); + let cie_id_or_offset = match Section::cie_offset_encoding(format) { + CieOffsetEncoding::U32 => rest.read_u32().map(u64::from)?, + CieOffsetEncoding::U64 => rest.read_u64()?, + }; + + if Section::is_cie(format, cie_id_or_offset) { + let cie = CommonInformationEntry::parse_rest(offset, length, format, bases, section, rest)?; + Ok(Some(CieOrFde::Cie(cie))) + } else { + let cie_offset = R::Offset::from_u64(cie_id_or_offset)?; + let cie_offset = match section.resolve_cie_offset(cie_offset_base, cie_offset) { + None => return Err(Error::OffsetOutOfBounds), + Some(cie_offset) => cie_offset, + }; + + let fde = PartialFrameDescriptionEntry { + offset, + length, + format, + cie_offset: cie_offset.into(), + rest, + section: section.clone(), + bases, + }; + + Ok(Some(CieOrFde::Fde(fde))) + } +} + +/// We support the z-style augmentation [defined by `.eh_frame`][ehframe]. +/// +/// [ehframe]: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Augmentation { + /// > A 'L' may be present at any position after the first character of the + /// > string. This character may only be present if 'z' is the first character + /// > of the string. If present, it indicates the presence of one argument in + /// > the Augmentation Data of the CIE, and a corresponding argument in the + /// > Augmentation Data of the FDE. The argument in the Augmentation Data of + /// > the CIE is 1-byte and represents the pointer encoding used for the + /// > argument in the Augmentation Data of the FDE, which is the address of a + /// > language-specific data area (LSDA). The size of the LSDA pointer is + /// > specified by the pointer encoding used. + lsda: Option<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, ¶meters, rest)?; + augmentation.personality = Some((encoding, personality)); + } + b'R' => { + let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; + let encoding = parse_pointer_encoding(rest)?; + augmentation.fde_address_encoding = Some(encoding); + } + b'S' => augmentation.is_signal_trampoline = true, + _ => return Err(Error::UnknownAugmentation), + } + + parsed_first = true; + } + + Ok(augmentation) + } +} + +/// Parsed augmentation data for a `FrameDescriptEntry`. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +struct AugmentationData { + lsda: Option<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, + + /// "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 = if Section::has_address_and_segment_sizes(version) { + let address_size = rest.read_address_size()?; + let segment_size = rest.read_u8()?; + if segment_size != 0 { + return Err(Error::UnsupportedSegmentSize); + } + address_size + } else { + section.address_size() + }; + + let code_alignment_factor = rest.read_uleb128()?; + let data_alignment_factor = rest.read_sleb128()?; + + let return_address_register = if version == 1 { + Register(rest.read_u8()?.into()) + } else { + rest.read_uleb128().and_then(Register::from_u64)? + }; + + let augmentation = if augmentation_string.is_empty() { + None + } else { + Some(Augmentation::parse( + &mut augmentation_string, + bases, + address_size, + section, + &mut rest, + )?) + }; + + let entry = CommonInformationEntry { + offset, + length, + format, + version, + augmentation, + address_size, + code_alignment_factor, + data_alignment_factor, + return_address_register, + initial_instructions: rest, + }; + + Ok(entry) + } +} + +/// # Signal Safe Methods +/// +/// These methods are guaranteed not to allocate, acquire locks, or perform any +/// other signal-unsafe operations. +impl<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(), + }, + vendor: section.vendor(), + } + } + + /// > A constant that gives the number of bytes of the CIE structure, not + /// > including the length field itself (see Section 7.2.2). The size of the + /// > length field plus the value of length must be an integral multiple of + /// > the address size. + pub fn entry_len(&self) -> R::Offset { + self.length + } + + /// > A version number (see Section 7.23). This number is specific to the + /// > call frame information and is independent of the DWARF version number. + pub fn version(&self) -> u8 { + self.version + } + + /// Get the augmentation data, if any exists. + /// + /// The only augmentation understood by `gimli` is that which is defined by + /// `.eh_frame`. + pub fn augmentation(&self) -> Option<&Augmentation> { + self.augmentation.as_ref() + } + + /// True if this CIE's FDEs have a LSDA. + pub fn has_lsda(&self) -> bool { + self.augmentation.map_or(false, |a| a.lsda.is_some()) + } + + /// Return the encoding of the LSDA address for this CIE's FDEs. + pub fn lsda_encoding(&self) -> Option<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, + ) + } + + /// Get the offset of this entry from the start of its containing section. + pub fn offset(&self) -> R::Offset { + self.offset + } + + /// Get the offset of this FDE's CIE. + pub fn cie_offset(&self) -> Section::Offset { + self.cie_offset + } + + /// > A constant that gives the number of bytes of the header and + /// > instruction stream for this function, not including the length field + /// > itself (see Section 7.2.2). The size of the length field plus the value + /// > of length must be an integral multiple of the address size. + pub fn entry_len(&self) -> R::Offset { + self.length + } +} + +/// A `FrameDescriptionEntry` is a set of CFA instructions for an address range. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FrameDescriptionEntry<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_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> { + 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 mut parameters = PointerEncodingParameters { + bases: &bases.eh_frame, + func_base: None, + address_size: cie.address_size, + section: section.section(), + }; + + let (initial_address, address_range) = Self::parse_addresses(&mut rest, &cie, ¶meters)?; + parameters.func_base = Some(initial_address); + + let aug_data = if let Some(ref augmentation) = cie.augmentation { + Some(AugmentationData::parse( + augmentation, + ¶meters, + &mut rest, + )?) + } else { + None + }; + + let entry = FrameDescriptionEntry { + offset, + length, + format, + cie, + initial_address, + address_range, + augmentation: aug_data, + instructions: rest, + }; + + Ok(entry) + } + + fn parse_addresses( + input: &mut R, + cie: &CommonInformationEntry<R>, + parameters: &PointerEncodingParameters<'_, R>, + ) -> Result<(u64, u64)> { + let encoding = cie.augmentation().and_then(|a| a.fde_address_encoding); + if let Some(encoding) = encoding { + // Ignore indirection. + let initial_address = parse_encoded_pointer(encoding, parameters, input)?.pointer(); + let address_range = parse_encoded_value(encoding, parameters, input)?; + Ok((initial_address, address_range)) + } else { + let initial_address = input.read_address(cie.address_size)?; + let address_range = input.read_address(cie.address_size)?; + Ok((initial_address, address_range)) + } + } + + /// Return the table of unwind information for this FDE. + #[inline] + pub fn rows<'a, 'ctx, Section, S>( + &self, + section: &'a Section, + bases: &'a BaseAddresses, + ctx: &'ctx mut UnwindContext<R::Offset, S>, + ) -> Result<UnwindTable<'a, 'ctx, R, S>> + where + Section: UnwindSection<R>, + S: UnwindContextStorage<R::Offset>, + { + UnwindTable::new(section, bases, ctx, self) + } + + /// Find the frame unwind information for the given address. + /// + /// If found, the unwind information is returned along with the reset + /// context in the form `Ok((unwind_info, context))`. If not found, + /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or + /// CFI evaluation fails, the error is returned. + pub fn unwind_info_for_address<'ctx, Section, S>( + &self, + section: &Section, + bases: &BaseAddresses, + ctx: &'ctx mut UnwindContext<R::Offset, S>, + address: u64, + ) -> Result<&'ctx UnwindTableRow<R::Offset, S>> + where + Section: UnwindSection<R>, + S: UnwindContextStorage<R::Offset>, + { + 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(), + }, + vendor: section.vendor(), + } + } + + /// The first address for which this entry has unwind information for. + pub fn initial_address(&self) -> u64 { + self.initial_address + } + + /// One more than the last address that this entry has unwind information for. + /// + /// This uses wrapping arithmetic, so the result may be less than + /// `initial_address`. + pub fn end_address(&self) -> u64 { + self.initial_address + .wrapping_add_sized(self.address_range, self.cie.address_size) + } + + /// The number of bytes of instructions that this entry has unwind + /// information for. + pub fn len(&self) -> u64 { + self.address_range + } + + /// Return `true` if the given address is within this FDE, `false` + /// otherwise. + /// + /// This is equivalent to `entry.initial_address() <= address < + /// entry.initial_address() + entry.len()`. + pub fn contains(&self, address: u64) -> bool { + self.initial_address() <= address && address < self.end_address() + } + + /// The address of this FDE's language-specific data area (LSDA), if it has + /// any. + pub fn lsda(&self) -> Option<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 [`Box`]. This is the default storage type parameter for [`UnwindContext`]. + +You may want to supply your own storage type for one of the following reasons: + + 1. In rare cases you may run into failed unwinds due to the fixed stack size + used by [`StoreOnHeap`], so you may want to try a larger `Box`. If denial + of service is not a concern, then you could also try a `Vec`-based stack which + can grow as needed. + 2. You may want to avoid heap allocations entirely. You can use a fixed-size + stack with in-line arrays, which will place the entire storage in-line into + [`UnwindContext`]. +" +)] +/// +/// Here's an implementation which uses a fixed-size stack and allocates everything in-line, +/// which will cause `UnwindContext` to be large: +/// +/// ```rust,no_run +/// # use gimli::*; +/// # +/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>) +/// # -> gimli::Result<()> { +/// # let eh_frame: gimli::EhFrame<_> = unreachable!(); +/// # let bases = unimplemented!(); +/// # +/// struct StoreOnStack; +/// +/// impl<T: ReaderOffset> UnwindContextStorage<T> for StoreOnStack { +/// type Rules = [(Register, RegisterRule<T>); 192]; +/// type Stack = [UnwindTableRow<T, 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<T: ReaderOffset>: 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<T>)>; + + /// The storage used for unwind table row stack. + type Stack: ArrayLike<Item = UnwindTableRow<T, Self>>; +} + +#[cfg(feature = "read")] +const MAX_RULES: usize = 192; +#[cfg(feature = "read")] +const MAX_UNWIND_STACK_DEPTH: usize = 4; + +#[cfg(feature = "read")] +impl<T: ReaderOffset> UnwindContextStorage<T> for StoreOnHeap { + type Rules = [(Register, RegisterRule<T>); MAX_RULES]; + type Stack = Box<[UnwindTableRow<T, Self>; MAX_UNWIND_STACK_DEPTH]>; +} + +/// Common context needed when evaluating the call frame unwinding information. +/// +/// By default, this structure is small and allocates its internal storage +/// on the heap using [`Box`] during [`UnwindContext::new`]. +/// +/// This can be overridden by providing a custom [`UnwindContextStorage`] type parameter. +/// When using a custom storage with in-line arrays, the [`UnwindContext`] type itself +/// will be big, so in that case it's recommended to place [`UnwindContext`] on the +/// heap, e.g. using `Box::new(UnwindContext::<R, MyCustomStorage>::new_in())`. +/// +/// To avoid re-allocating the context multiple times when evaluating multiple +/// CFI programs, the same [`UnwindContext`] can be reused for multiple unwinds. +/// +/// ``` +/// use gimli::{UnwindContext, UnwindTable}; +/// +/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>) +/// # -> gimli::Result<()> { +/// # let eh_frame: gimli::EhFrame<_> = unreachable!(); +/// # let bases = unimplemented!(); +/// // An uninitialized context. +/// let mut ctx = UnwindContext::new(); +/// +/// // Initialize the context by evaluating the CIE's initial instruction program, +/// // and generate the unwind table. +/// let mut table = some_fde.rows(&eh_frame, &bases, &mut ctx)?; +/// while let Some(row) = table.next_row()? { +/// // Do stuff with each row... +/// # let _ = row; +/// } +/// # unreachable!() +/// # } +/// ``` +#[derive(Clone, PartialEq, Eq)] +pub struct UnwindContext<T, S = StoreOnHeap> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + // 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<S::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<T>)>, + + is_initialized: bool, +} + +impl<T, S> Debug for UnwindContext<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + 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<T, S> Default for UnwindContext<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + fn default() -> Self { + Self::new_in() + } +} + +#[cfg(feature = "read")] +impl<T: ReaderOffset> UnwindContext<T> { + /// 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<T, S> UnwindContext<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + /// 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, R>( + &mut self, + section: &Section, + bases: &BaseAddresses, + cie: &CommonInformationEntry<R>, + ) -> Result<()> + where + R: Reader<Offset = T>, + Section: UnwindSection<R>, + { + // Always reset because previous initialization failure may leave dirty state. + self.reset(); + + let mut table = UnwindTable::new_for_cie(section, bases, self, cie); + while table.next_row()?.is_some() {} + + self.save_initial_rules()?; + Ok(()) + } + + fn reset(&mut self) { + self.stack.clear(); + self.stack.try_push(UnwindTableRow::default()).unwrap(); + debug_assert!(self.stack[0].is_default()); + self.initial_rule = None; + self.is_initialized = false; + } + + fn row(&self) -> &UnwindTableRow<T, S> { + self.stack.last().unwrap() + } + + fn row_mut(&mut self) -> &mut UnwindTableRow<T, S> { + self.stack.last_mut().unwrap() + } + + fn save_initial_rules(&mut self) -> Result<()> { + debug_assert!(!self.is_initialized); + self.initial_rule = match *self.stack.last().unwrap().registers.rules { + // All rules are default (undefined). In this case just synthesize + // an undefined rule. + [] => Some((Register(0), RegisterRule::Undefined)), + [ref rule] => Some(rule.clone()), + _ => { + let rules = self.stack.last().unwrap().clone(); + self.stack + .try_insert(0, rules) + .map_err(|_| Error::StackFull)?; + None + } + }; + self.is_initialized = true; + Ok(()) + } + + fn start_address(&self) -> u64 { + self.row().start_address + } + + fn set_start_address(&mut self, start_address: u64) { + let row = self.row_mut(); + row.start_address = start_address; + } + + fn set_register_rule(&mut self, register: Register, rule: RegisterRule<T>) -> 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<T>> { + 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<T>) { + self.row_mut().cfa = cfa; + } + + fn cfa_mut(&mut self) -> &mut CfaRule<T> { + &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, S = StoreOnHeap> +where + R: Reader, + S: UnwindContextStorage<R::Offset>, +{ + code_alignment_factor: Wrapping<u64>, + data_alignment_factor: Wrapping<i64>, + address_size: u8, + next_start_address: u64, + last_end_address: u64, + returned_last_row: bool, + current_row_valid: bool, + instructions: CallFrameInstructionIter<'a, R>, + ctx: &'ctx mut UnwindContext<R::Offset, S>, +} + +/// # Signal Safe Methods +/// +/// These methods are guaranteed not to allocate, acquire locks, or perform any +/// other signal-unsafe operations. +impl<'a, 'ctx, R, S> UnwindTable<'a, 'ctx, R, S> +where + R: Reader, + S: UnwindContextStorage<R::Offset>, +{ + /// 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::Offset, S>, + 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::Offset, S>, + fde: &FrameDescriptionEntry<R>, + ) -> Self { + assert!(!ctx.stack.is_empty()); + UnwindTable { + code_alignment_factor: Wrapping(fde.cie().code_alignment_factor()), + data_alignment_factor: Wrapping(fde.cie().data_alignment_factor()), + address_size: fde.cie().address_size, + next_start_address: fde.initial_address(), + last_end_address: fde.end_address(), + returned_last_row: false, + current_row_valid: false, + instructions: fde.instructions(section, bases), + ctx, + } + } + + fn new_for_cie<Section: UnwindSection<R>>( + section: &'a Section, + bases: &'a BaseAddresses, + ctx: &'ctx mut UnwindContext<R::Offset, S>, + cie: &CommonInformationEntry<R>, + ) -> Self { + assert!(!ctx.stack.is_empty()); + UnwindTable { + code_alignment_factor: Wrapping(cie.code_alignment_factor()), + data_alignment_factor: Wrapping(cie.data_alignment_factor()), + address_size: cie.address_size, + next_start_address: 0, + last_end_address: 0, + returned_last_row: false, + current_row_valid: false, + instructions: cie.instructions(section, bases), + ctx, + } + } + + /// Evaluate call frame instructions until the next row of the table is + /// completed, and return it. + /// + /// Unfortunately, this cannot be used with `FallibleIterator` because of + /// the restricted lifetime of the yielded item. + pub fn next_row(&mut self) -> Result<Option<&UnwindTableRow<R::Offset, S>>> { + assert!(!self.ctx.stack.is_empty()); + 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::Offset, S>> { + 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::Offset>) -> 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 = self + .ctx + .start_address() + .add_sized(delta.0, self.address_size)?; + self.ctx.row_mut().end_address = self.next_start_address; + return Ok(true); + } + + // Instructions that modify the CFA. + DefCfa { register, offset } => { + self.ctx.set_cfa(CfaRule::RegisterAndOffset { + register, + offset: offset as i64, + }); + } + DefCfaSf { + register, + factored_offset, + } => { + let data_align = self.data_alignment_factor; + self.ctx.set_cfa(CfaRule::RegisterAndOffset { + register, + offset: (Wrapping(factored_offset) * data_align).0, + }); + } + DefCfaRegister { register } => { + if let CfaRule::RegisterAndOffset { + register: ref mut reg, + .. + } = *self.ctx.cfa_mut() + { + *reg = register; + } else { + return Err(Error::CfiInstructionInInvalidContext); + } + } + DefCfaOffset { offset } => { + if let CfaRule::RegisterAndOffset { + offset: ref mut off, + .. + } = *self.ctx.cfa_mut() + { + *off = offset as i64; + } else { + return Err(Error::CfiInstructionInInvalidContext); + } + } + DefCfaOffsetSf { factored_offset } => { + if let CfaRule::RegisterAndOffset { + offset: ref mut off, + .. + } = *self.ctx.cfa_mut() + { + let data_align = self.data_alignment_factor; + *off = (Wrapping(factored_offset) * data_align).0; + } else { + return Err(Error::CfiInstructionInInvalidContext); + } + } + DefCfaExpression { expression } => { + self.ctx.set_cfa(CfaRule::Expression(expression)); + } + + // Instructions that define register rules. + Undefined { register } => { + self.ctx + .set_register_rule(register, RegisterRule::Undefined)?; + } + SameValue { register } => { + self.ctx + .set_register_rule(register, RegisterRule::SameValue)?; + } + Offset { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::Offset(offset.0))?; + } + OffsetExtendedSf { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::Offset(offset.0))?; + } + ValOffset { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; + } + ValOffsetSf { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; + } + Register { + dest_register, + src_register, + } => { + self.ctx + .set_register_rule(dest_register, RegisterRule::Register(src_register))?; + } + Expression { + register, + expression, + } => { + let expression = RegisterRule::Expression(expression); + self.ctx.set_register_rule(register, expression)?; + } + ValExpression { + register, + expression, + } => { + let expression = RegisterRule::ValExpression(expression); + self.ctx.set_register_rule(register, expression)?; + } + Restore { register } => { + let initial_rule = if let Some(rule) = self.ctx.get_initial_rule(register) { + rule + } else { + // Can't restore the initial rule when we are + // evaluating the initial rules! + return Err(Error::CfiInstructionInInvalidContext); + }; + + self.ctx.set_register_rule(register, initial_rule)?; + } + + // Row push and pop instructions. + RememberState => { + self.ctx.push_row()?; + } + RestoreState => { + // Pop state while preserving current location. + let start_address = self.ctx.start_address(); + self.ctx.pop_row()?; + self.ctx.set_start_address(start_address); + } + + // GNU Extension. Save the size somewhere so the unwinder can use + // it when restoring IP + ArgsSize { size } => { + self.ctx.row_mut().saved_args_size = size; + } + + // AArch64 extension. + NegateRaState => { + let register = crate::AArch64::RA_SIGN_STATE; + let value = match self.ctx.row().register(register) { + RegisterRule::Undefined => 0, + RegisterRule::Constant(value) => value, + _ => return Err(Error::CfiInstructionInInvalidContext), + }; + self.ctx + .set_register_rule(register, RegisterRule::Constant(value ^ 1))?; + } + + // No operation. + Nop => {} + }; + + Ok(false) + } +} + +// We tend to have very few register rules: usually only a couple. Even if we +// have a rule for every register, on x86-64 with SSE and everything we're +// talking about ~100 rules. So rather than keeping the rules in a hash map, or +// a vector indexed by register number (which would lead to filling lots of +// empty entries), we store them as a vec of (register number, register rule) +// pairs. +// +// Additionally, because every register's default rule is implicitly +// `RegisterRule::Undefined`, we never store a register's rule in this vec if it +// is undefined and save a little bit more space and do a little fewer +// comparisons that way. +// +// The maximum number of rules preallocated by libunwind is 97 for AArch64, 128 +// for ARM, and even 188 for MIPS. It is extremely unlikely to encounter this +// many register rules in practice. +// +// See: +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-x86_64/dwarf-config.h#L36 +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-aarch64/dwarf-config.h#L32 +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-arm/dwarf-config.h#L31 +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-mips/dwarf-config.h#L31 +struct RegisterRuleMap<T, S = StoreOnHeap> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + rules: ArrayVec<S::Rules>, +} + +impl<T, S> Debug for RegisterRuleMap<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RegisterRuleMap") + .field("rules", &self.rules) + .finish() + } +} + +impl<T, S> Clone for RegisterRuleMap<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + fn clone(&self) -> Self { + Self { + rules: self.rules.clone(), + } + } +} + +impl<T, S> Default for RegisterRuleMap<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + 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<T, S> RegisterRuleMap<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + fn is_default(&self) -> bool { + self.rules.is_empty() + } + + fn get(&self, register: Register) -> RegisterRule<T> { + 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<T>) -> Result<()> { + if !rule.is_defined() { + let idx = self + .rules + .iter() + .enumerate() + .find(|&(_, r)| r.0 == register) + .map(|(i, _)| i); + if let Some(idx) = idx { + self.rules.swap_remove(idx); + } + return Ok(()); + } + + for &mut (reg, ref mut old_rule) in &mut *self.rules { + debug_assert!(old_rule.is_defined()); + if reg == register { + *old_rule = rule; + return Ok(()); + } + } + + self.rules + .try_push((register, rule)) + .map_err(|_| Error::TooManyRegisterRules) + } + + fn iter(&self) -> RegisterRuleIter<'_, T> { + RegisterRuleIter(self.rules.iter()) + } +} + +impl<'a, R, S> FromIterator<&'a (Register, RegisterRule<R>)> for RegisterRuleMap<R, S> +where + R: 'a + ReaderOffset, + S: UnwindContextStorage<R>, +{ + 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<T, S> PartialEq for RegisterRuleMap<T, S> +where + T: ReaderOffset + PartialEq, + S: UnwindContextStorage<T>, +{ + 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<T, S> Eq for RegisterRuleMap<T, S> +where + T: ReaderOffset + Eq, + S: UnwindContextStorage<T>, +{ +} + +/// An unordered iterator for register rules. +#[derive(Debug, Clone)] +pub struct RegisterRuleIter<'iter, T>(::core::slice::Iter<'iter, (Register, RegisterRule<T>)>) +where + T: ReaderOffset; + +impl<'iter, T: ReaderOffset> Iterator for RegisterRuleIter<'iter, T> { + type Item = &'iter (Register, RegisterRule<T>); + + 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<T, S = StoreOnHeap> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + start_address: u64, + end_address: u64, + saved_args_size: u64, + cfa: CfaRule<T>, + registers: RegisterRuleMap<T, S>, +} + +impl<T, S> Debug for UnwindTableRow<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + 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<T, S> Clone for UnwindTableRow<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + 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<T, S> Default for UnwindTableRow<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + fn default() -> Self { + UnwindTableRow { + start_address: 0, + end_address: 0, + saved_args_size: 0, + cfa: Default::default(), + registers: Default::default(), + } + } +} + +impl<T, S> UnwindTableRow<T, S> +where + T: ReaderOffset, + S: UnwindContextStorage<T>, +{ + 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<T> { + &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<T> { + 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<usize>) { + /// for &(register, ref rule) in unwind_table_row.registers() { + /// // ... + /// # drop(register); drop(rule); + /// } + /// # } + /// ``` + pub fn registers(&self) -> RegisterRuleIter<'_, T> { + self.registers.iter() + } +} + +/// The canonical frame address (CFA) recovery rules. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum CfaRule<T: ReaderOffset> { + /// The CFA is given offset from the given register's value. + RegisterAndOffset { + /// The register containing the base value. + register: Register, + /// The offset from the register's base value. + offset: i64, + }, + /// The CFA is obtained by evaluating a DWARF expression program. + Expression(UnwindExpression<T>), +} + +impl<T: ReaderOffset> Default for CfaRule<T> { + fn default() -> Self { + CfaRule::RegisterAndOffset { + register: Register(0), + offset: 0, + } + } +} + +impl<T: ReaderOffset> CfaRule<T> { + fn is_default(&self) -> bool { + match *self { + CfaRule::RegisterAndOffset { register, offset } => { + register == Register(0) && offset == 0 + } + _ => false, + } + } +} + +/// An entry in the abstract CFI table that describes how to find the value of a +/// register. +/// +/// "The register columns contain rules that describe whether a given register +/// has been saved and the rule to find the value for the register in the +/// previous frame." +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum RegisterRule<T: ReaderOffset> { + /// > A register that has this rule has no recoverable value in the previous + /// > frame. (By convention, it is not preserved by a callee.) + Undefined, + + /// > This register has not been modified from the previous frame. (By + /// > convention, it is preserved by the callee, but the callee has not + /// > modified it.) + SameValue, + + /// "The previous value of this register is saved at the address CFA+N where + /// CFA is the current CFA value and N is a signed offset." + Offset(i64), + + /// "The previous value of this register is the value CFA+N where CFA is the + /// current CFA value and N is a signed offset." + ValOffset(i64), + + /// "The previous value of this register is stored in another register + /// numbered R." + Register(Register), + + /// "The previous value of this register is located at the address produced + /// by executing the DWARF expression." + Expression(UnwindExpression<T>), + + /// "The previous value of this register is the value produced by executing + /// the DWARF expression." + ValExpression(UnwindExpression<T>), + + /// "The rule is defined externally to this specification by the augmenter." + Architectural, + + /// This is a pseudo-register with a constant value. + Constant(u64), +} + +impl<T: ReaderOffset> RegisterRule<T> { + fn is_defined(&self) -> bool { + !matches!(*self, RegisterRule::Undefined) + } +} + +/// A parsed call frame instruction. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum CallFrameInstruction<T: ReaderOffset> { + // 6.4.2.1 Row Creation Methods + /// > 1. DW_CFA_set_loc + /// > + /// > The DW_CFA_set_loc instruction takes a single operand that represents + /// > a target address. The required action is to create a new table row + /// > using the specified address as the location. All other values in the + /// > new row are initially identical to the current row. The new location + /// > value is always greater than the current one. If the segment_size + /// > field of this FDE's CIE is non- zero, the initial location is preceded + /// > by a segment selector of the given length. + SetLoc { + /// The target address. + address: u64, + }, + + /// The `AdvanceLoc` instruction is used for all of `DW_CFA_advance_loc` and + /// `DW_CFA_advance_loc{1,2,4}`. + /// + /// > 2. DW_CFA_advance_loc + /// > + /// > The DW_CFA_advance instruction takes a single operand (encoded with + /// > the opcode) that represents a constant delta. The required action is + /// > to create a new table row with a location value that is computed by + /// > taking the current entry’s location value and adding the value of + /// > delta * code_alignment_factor. All other values in the new row are + /// > initially identical to the current row. + AdvanceLoc { + /// The delta to be added to the current address. + delta: u32, + }, + + // 6.4.2.2 CFA Definition Methods + /// > 1. DW_CFA_def_cfa + /// > + /// > The DW_CFA_def_cfa instruction takes two unsigned LEB128 operands + /// > representing a register number and a (non-factored) offset. The + /// > required action is to define the current CFA rule to use the provided + /// > register and offset. + DefCfa { + /// The target register's number. + register: Register, + /// The non-factored offset. + offset: u64, + }, + + /// > 2. DW_CFA_def_cfa_sf + /// > + /// > The DW_CFA_def_cfa_sf instruction takes two operands: an unsigned + /// > LEB128 value representing a register number and a signed LEB128 + /// > factored offset. This instruction is identical to DW_CFA_def_cfa + /// > except that the second operand is signed and factored. The resulting + /// > offset is factored_offset * data_alignment_factor. + DefCfaSf { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: i64, + }, + + /// > 3. DW_CFA_def_cfa_register + /// > + /// > The DW_CFA_def_cfa_register instruction takes a single unsigned LEB128 + /// > operand representing a register number. The required action is to + /// > define the current CFA rule to use the provided register (but to keep + /// > the old offset). This operation is valid only if the current CFA rule + /// > is defined to use a register and offset. + DefCfaRegister { + /// The target register's number. + register: Register, + }, + + /// > 4. DW_CFA_def_cfa_offset + /// > + /// > The DW_CFA_def_cfa_offset instruction takes a single unsigned LEB128 + /// > operand representing a (non-factored) offset. The required action is + /// > to define the current CFA rule to use the provided offset (but to keep + /// > the old register). This operation is valid only if the current CFA + /// > rule is defined to use a register and offset. + DefCfaOffset { + /// The non-factored offset. + offset: u64, + }, + + /// > 5. DW_CFA_def_cfa_offset_sf + /// > + /// > The DW_CFA_def_cfa_offset_sf instruction takes a signed LEB128 operand + /// > representing a factored offset. This instruction is identical to + /// > DW_CFA_def_cfa_offset except that the operand is signed and + /// > factored. The resulting offset is factored_offset * + /// > data_alignment_factor. This operation is valid only if the current CFA + /// > rule is defined to use a register and offset. + DefCfaOffsetSf { + /// The factored offset. + factored_offset: i64, + }, + + /// > 6. DW_CFA_def_cfa_expression + /// > + /// > The DW_CFA_def_cfa_expression instruction takes a single operand + /// > encoded as a DW_FORM_exprloc value representing a DWARF + /// > expression. The required action is to establish that expression as the + /// > means by which the current CFA is computed. + DefCfaExpression { + /// The location of the DWARF expression. + expression: UnwindExpression<T>, + }, + + // 6.4.2.3 Register Rule Instructions + /// > 1. DW_CFA_undefined + /// > + /// > The DW_CFA_undefined instruction takes a single unsigned LEB128 + /// > operand that represents a register number. The required action is to + /// > set the rule for the specified register to “undefined.” + Undefined { + /// The target register's number. + register: Register, + }, + + /// > 2. DW_CFA_same_value + /// > + /// > The DW_CFA_same_value instruction takes a single unsigned LEB128 + /// > operand that represents a register number. The required action is to + /// > set the rule for the specified register to “same value.” + SameValue { + /// The target register's number. + register: Register, + }, + + /// The `Offset` instruction represents both `DW_CFA_offset` and + /// `DW_CFA_offset_extended`. + /// + /// > 3. DW_CFA_offset + /// > + /// > The DW_CFA_offset instruction takes two operands: a register number + /// > (encoded with the opcode) and an unsigned LEB128 constant representing + /// > a factored offset. The required action is to change the rule for the + /// > register indicated by the register number to be an offset(N) rule + /// > where the value of N is factored offset * data_alignment_factor. + Offset { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: u64, + }, + + /// > 5. DW_CFA_offset_extended_sf + /// > + /// > The DW_CFA_offset_extended_sf instruction takes two operands: an + /// > unsigned LEB128 value representing a register number and a signed + /// > LEB128 factored offset. This instruction is identical to + /// > DW_CFA_offset_extended except that the second operand is signed and + /// > factored. The resulting offset is factored_offset * + /// > data_alignment_factor. + OffsetExtendedSf { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: i64, + }, + + /// > 6. DW_CFA_val_offset + /// > + /// > The DW_CFA_val_offset instruction takes two unsigned LEB128 operands + /// > representing a register number and a factored offset. The required + /// > action is to change the rule for the register indicated by the + /// > register number to be a val_offset(N) rule where the value of N is + /// > factored_offset * data_alignment_factor. + ValOffset { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: u64, + }, + + /// > 7. DW_CFA_val_offset_sf + /// > + /// > The DW_CFA_val_offset_sf instruction takes two operands: an unsigned + /// > LEB128 value representing a register number and a signed LEB128 + /// > factored offset. This instruction is identical to DW_CFA_val_offset + /// > except that the second operand is signed and factored. The resulting + /// > offset is factored_offset * data_alignment_factor. + ValOffsetSf { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: i64, + }, + + /// > 8. DW_CFA_register + /// > + /// > The DW_CFA_register instruction takes two unsigned LEB128 operands + /// > representing register numbers. The required action is to set the rule + /// > for the first register to be register(R) where R is the second + /// > register. + Register { + /// The number of the register whose rule is being changed. + dest_register: Register, + /// The number of the register where the other register's value can be + /// found. + src_register: Register, + }, + + /// > 9. DW_CFA_expression + /// > + /// > The DW_CFA_expression instruction takes two operands: an unsigned + /// > LEB128 value representing a register number, and a DW_FORM_block value + /// > representing a DWARF expression. The required action is to change the + /// > rule for the register indicated by the register number to be an + /// > expression(E) rule where E is the DWARF expression. That is, the DWARF + /// > expression computes the address. The value of the CFA is pushed on the + /// > DWARF evaluation stack prior to execution of the DWARF expression. + Expression { + /// The target register's number. + register: Register, + /// The location of the DWARF expression. + expression: UnwindExpression<T>, + }, + + /// > 10. DW_CFA_val_expression + /// > + /// > The DW_CFA_val_expression instruction takes two operands: an unsigned + /// > LEB128 value representing a register number, and a DW_FORM_block value + /// > representing a DWARF expression. The required action is to change the + /// > rule for the register indicated by the register number to be a + /// > val_expression(E) rule where E is the DWARF expression. That is, the + /// > DWARF expression computes the value of the given register. The value + /// > of the CFA is pushed on the DWARF evaluation stack prior to execution + /// > of the DWARF expression. + ValExpression { + /// The target register's number. + register: Register, + /// The location of the DWARF expression. + expression: UnwindExpression<T>, + }, + + /// The `Restore` instruction represents both `DW_CFA_restore` and + /// `DW_CFA_restore_extended`. + /// + /// > 11. DW_CFA_restore + /// > + /// > The DW_CFA_restore instruction takes a single operand (encoded with + /// > the opcode) that represents a register number. The required action is + /// > to change the rule for the indicated register to the rule assigned it + /// > by the initial_instructions in the CIE. + Restore { + /// The register to be reset. + register: Register, + }, + + // 6.4.2.4 Row State Instructions + /// > 1. DW_CFA_remember_state + /// > + /// > The DW_CFA_remember_state instruction takes no operands. The required + /// > action is to push the set of rules for every register onto an implicit + /// > stack. + RememberState, + + /// > 2. DW_CFA_restore_state + /// > + /// > The DW_CFA_restore_state instruction takes no operands. The required + /// > action is to pop the set of rules off the implicit stack and place + /// > them in the current row. + RestoreState, + + /// > DW_CFA_GNU_args_size + /// > + /// > GNU Extension + /// > + /// > The DW_CFA_GNU_args_size instruction takes an unsigned LEB128 operand + /// > representing an argument size. This instruction specifies the total of + /// > the size of the arguments which have been pushed onto the stack. + ArgsSize { + /// The size of the arguments which have been pushed onto the stack + size: u64, + }, + + /// > DW_CFA_AARCH64_negate_ra_state + /// > + /// > AArch64 Extension + /// > + /// > The DW_CFA_AARCH64_negate_ra_state operation negates bit 0 of the + /// > RA_SIGN_STATE pseudo-register. It does not take any operands. The + /// > DW_CFA_AARCH64_negate_ra_state must not be mixed with other DWARF Register + /// > Rule Instructions on the RA_SIGN_STATE pseudo-register in one Common + /// > Information Entry (CIE) and Frame Descriptor Entry (FDE) program sequence. + NegateRaState, + + // 6.4.2.5 Padding Instruction + /// > 1. DW_CFA_nop + /// > + /// > The DW_CFA_nop instruction has no operands and no required actions. It + /// > is used as padding to make a CIE or FDE an appropriate size. + Nop, +} + +const CFI_INSTRUCTION_HIGH_BITS_MASK: u8 = 0b1100_0000; +const CFI_INSTRUCTION_LOW_BITS_MASK: u8 = !CFI_INSTRUCTION_HIGH_BITS_MASK; + +impl<T: ReaderOffset> CallFrameInstruction<T> { + fn parse<R: Reader<Offset = T>>( + input: &mut R, + address_encoding: Option<DwEhPe>, + parameters: &PointerEncodingParameters<'_, R>, + vendor: Vendor, + ) -> Result<CallFrameInstruction<T>> { + let instruction = input.read_u8()?; + let high_bits = instruction & CFI_INSTRUCTION_HIGH_BITS_MASK; + + if high_bits == constants::DW_CFA_advance_loc.0 { + let delta = instruction & CFI_INSTRUCTION_LOW_BITS_MASK; + return Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(delta), + }); + } + + if high_bits == constants::DW_CFA_offset.0 { + let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); + let offset = input.read_uleb128()?; + return Ok(CallFrameInstruction::Offset { + register, + factored_offset: offset, + }); + } + + if high_bits == constants::DW_CFA_restore.0 { + let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); + return Ok(CallFrameInstruction::Restore { register }); + } + + debug_assert_eq!(high_bits, 0); + let instruction = constants::DwCfa(instruction); + + match instruction { + constants::DW_CFA_nop => Ok(CallFrameInstruction::Nop), + + constants::DW_CFA_set_loc => { + let address = if let Some(encoding) = address_encoding { + parse_encoded_pointer(encoding, parameters, input)?.direct()? + } else { + input.read_address(parameters.address_size)? + }; + Ok(CallFrameInstruction::SetLoc { address }) + } + + constants::DW_CFA_advance_loc1 => { + let delta = input.read_u8()?; + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(delta), + }) + } + + constants::DW_CFA_advance_loc2 => { + let delta = input.read_u16()?; + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(delta), + }) + } + + constants::DW_CFA_advance_loc4 => { + let delta = input.read_u32()?; + Ok(CallFrameInstruction::AdvanceLoc { delta }) + } + + constants::DW_CFA_offset_extended => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::Offset { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_restore_extended => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::Restore { register }) + } + + constants::DW_CFA_undefined => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::Undefined { register }) + } + + constants::DW_CFA_same_value => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::SameValue { register }) + } + + constants::DW_CFA_register => { + let dest = input.read_uleb128().and_then(Register::from_u64)?; + let src = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::Register { + dest_register: dest, + src_register: src, + }) + } + + constants::DW_CFA_remember_state => Ok(CallFrameInstruction::RememberState), + + constants::DW_CFA_restore_state => Ok(CallFrameInstruction::RestoreState), + + constants::DW_CFA_def_cfa => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::DefCfa { register, offset }) + } + + constants::DW_CFA_def_cfa_register => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::DefCfaRegister { register }) + } + + constants::DW_CFA_def_cfa_offset => { + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::DefCfaOffset { offset }) + } + + constants::DW_CFA_def_cfa_expression => { + let length = input.read_uleb128().and_then(R::Offset::from_u64)?; + let offset = input.offset_from(parameters.section); + input.skip(length)?; + Ok(CallFrameInstruction::DefCfaExpression { + expression: UnwindExpression { offset, length }, + }) + } + + constants::DW_CFA_expression => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let length = input.read_uleb128().and_then(R::Offset::from_u64)?; + let offset = input.offset_from(parameters.section); + input.skip(length)?; + Ok(CallFrameInstruction::Expression { + register, + expression: UnwindExpression { offset, length }, + }) + } + + constants::DW_CFA_offset_extended_sf => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::OffsetExtendedSf { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_def_cfa_sf => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::DefCfaSf { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_def_cfa_offset_sf => { + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::DefCfaOffsetSf { + factored_offset: offset, + }) + } + + constants::DW_CFA_val_offset => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::ValOffset { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_val_offset_sf => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::ValOffsetSf { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_val_expression => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let length = input.read_uleb128().and_then(R::Offset::from_u64)?; + let offset = input.offset_from(parameters.section); + input.skip(length)?; + Ok(CallFrameInstruction::ValExpression { + register, + expression: UnwindExpression { offset, length }, + }) + } + + constants::DW_CFA_GNU_args_size => { + let size = input.read_uleb128()?; + Ok(CallFrameInstruction::ArgsSize { size }) + } + + constants::DW_CFA_AARCH64_negate_ra_state if vendor == Vendor::AArch64 => { + Ok(CallFrameInstruction::NegateRaState) + } + + otherwise => Err(Error::UnknownCallFrameInstruction(otherwise)), + } + } +} + +/// A lazy iterator parsing call frame instructions. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Clone, Debug)] +pub struct CallFrameInstructionIter<'a, R: Reader> { + input: R, + address_encoding: Option<constants::DwEhPe>, + parameters: PointerEncodingParameters<'a, R>, + vendor: Vendor, +} + +impl<'a, R: Reader> CallFrameInstructionIter<'a, R> { + /// Parse the next call frame instruction. + pub fn next(&mut self) -> Result<Option<CallFrameInstruction<R::Offset>>> { + if self.input.is_empty() { + return Ok(None); + } + + match CallFrameInstruction::parse( + &mut self.input, + self.address_encoding, + &self.parameters, + self.vendor, + ) { + Ok(instruction) => Ok(Some(instruction)), + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'a, R: Reader> fallible_iterator::FallibleIterator for CallFrameInstructionIter<'a, R> { + type Item = CallFrameInstruction<R::Offset>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + CallFrameInstructionIter::next(self) + } +} + +/// The location of a DWARF expression within an unwind section. +/// +/// This is stored as an offset and length within the section instead of as a +/// `Reader` to avoid lifetime issues when reusing [`UnwindContext`]. +/// +/// # Example +/// ``` +/// # use gimli::{EhFrame, EndianSlice, NativeEndian, Error, FrameDescriptionEntry, UnwindExpression, EvaluationResult}; +/// # fn foo() -> Result<(), Error> { +/// # let eh_frame: EhFrame<EndianSlice<NativeEndian>> = unreachable!(); +/// # let fde: FrameDescriptionEntry<EndianSlice<NativeEndian>> = unimplemented!(); +/// # let unwind_expression: UnwindExpression<_> = unimplemented!(); +/// let expression = unwind_expression.get(&eh_frame)?; +/// let mut evaluation = expression.evaluation(fde.cie().encoding()); +/// let mut result = evaluation.evaluate()?; +/// loop { +/// match result { +/// EvaluationResult::Complete => break, +/// // Provide information to the evaluation. +/// _ => { unimplemented!()} +/// } +/// } +/// let value = evaluation.value_result(); +/// # Ok(()) +/// # } +/// ``` +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct UnwindExpression<T: ReaderOffset> { + /// The offset of the expression within the section. + pub offset: T, + /// The length of the expression. + pub length: T, +} + +impl<T: ReaderOffset> UnwindExpression<T> { + /// Get the expression from the section. + /// + /// The offset and length were previously validated when the + /// `UnwindExpression` was created, so this should not fail. + pub fn get<R, S>(&self, section: &S) -> Result<Expression<R>> + where + R: Reader<Offset = T>, + S: UnwindSection<R>, + { + let input = &mut section.section().clone(); + input.skip(self.offset)?; + let data = input.split(self.length)?; + Ok(Expression(data)) + } +} + +/// Parse a `DW_EH_PE_*` pointer encoding. +#[doc(hidden)] +#[inline] +fn parse_pointer_encoding<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(eh_pe)) + } +} + +/// A decoded pointer. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Pointer { + /// This value is the decoded pointer value. + Direct(u64), + + /// This value is *not* the pointer value, but points to the address of + /// where the real pointer value lives. In other words, deref this pointer + /// to get the real pointer value. + /// + /// Chase this pointer at your own risk: do you trust the DWARF data it came + /// from? + Indirect(u64), +} + +impl Default for Pointer { + #[inline] + fn default() -> Self { + Pointer::Direct(0) + } +} + +impl Pointer { + #[inline] + fn new(encoding: constants::DwEhPe, address: u64) -> Pointer { + if encoding.is_indirect() { + Pointer::Indirect(address) + } else { + Pointer::Direct(address) + } + } + + /// Return the direct pointer value. + #[inline] + pub fn direct(self) -> Result<u64> { + match self { + Pointer::Direct(p) => Ok(p), + Pointer::Indirect(_) => Err(Error::UnsupportedPointerEncoding), + } + } + + /// Return the pointer value, discarding indirectness information. + #[inline] + pub fn pointer(self) -> u64 { + match self { + Pointer::Direct(p) | Pointer::Indirect(p) => p, + } + } +} + +#[derive(Clone, Debug)] +struct PointerEncodingParameters<'a, R: Reader> { + bases: &'a SectionBaseAddresses, + func_base: Option<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(encoding)); + } + + if encoding == constants::DW_EH_PE_omit { + return Err(Error::CannotParseOmitPointerEncoding); + } + + let base = match encoding.application() { + constants::DW_EH_PE_absptr => 0, + constants::DW_EH_PE_pcrel => { + if let Some(section_base) = parameters.bases.section { + let offset_from_section = input.offset_from(parameters.section); + section_base + .wrapping_add_sized(offset_from_section.into_u64(), parameters.address_size) + } else { + return Err(Error::PcRelativePointerButSectionBaseIsUndefined); + } + } + constants::DW_EH_PE_textrel => { + if let Some(text) = parameters.bases.text { + text + } else { + return Err(Error::TextRelativePointerButTextBaseIsUndefined); + } + } + constants::DW_EH_PE_datarel => { + if let Some(data) = parameters.bases.data { + data + } else { + return Err(Error::DataRelativePointerButDataBaseIsUndefined); + } + } + constants::DW_EH_PE_funcrel => { + if let Some(func) = parameters.func_base { + func + } else { + return Err(Error::FuncRelativePointerInBadContext); + } + } + constants::DW_EH_PE_aligned => return Err(Error::UnsupportedPointerEncoding), + _ => unreachable!(), + }; + + let offset = parse_encoded_value(encoding, parameters, input)?; + Ok(Pointer::new( + encoding, + base.wrapping_add_sized(offset, parameters.address_size), + )) +} + +fn parse_encoded_value<R: Reader>( + encoding: constants::DwEhPe, + parameters: &PointerEncodingParameters<'_, R>, + input: &mut R, +) -> Result<u64> { + match encoding.format() { + // Unsigned variants. + constants::DW_EH_PE_absptr => input.read_address(parameters.address_size), + constants::DW_EH_PE_uleb128 => input.read_uleb128(), + constants::DW_EH_PE_udata2 => input.read_u16().map(u64::from), + constants::DW_EH_PE_udata4 => input.read_u32().map(u64::from), + constants::DW_EH_PE_udata8 => input.read_u64(), + + // Signed variants. Here we sign extend the values (happens by + // default when casting a signed integer to a larger range integer + // in Rust), return them as u64, and rely on wrapping addition to do + // the right thing when adding these offsets to their bases. + constants::DW_EH_PE_sleb128 => input.read_sleb128().map(|a| a as u64), + constants::DW_EH_PE_sdata2 => input.read_i16().map(|a| a as u64), + constants::DW_EH_PE_sdata4 => input.read_i32().map(|a| a as u64), + constants::DW_EH_PE_sdata8 => input.read_i64().map(|a| a as u64), + + // That was all of the valid encoding formats. + _ => unreachable!(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::{parse_cfi_entry, AugmentationData, RegisterRuleMap, UnwindContext}; + use crate::common::Format; + use crate::constants; + use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian}; + use crate::read::{ + EndianSlice, Error, Pointer, ReaderOffsetId, Result, Section as ReadSection, + }; + use crate::test_util::GimliSectionMethods; + use alloc::boxed::Box; + use alloc::vec::Vec; + use core::marker::PhantomData; + use core::mem; + use test_assembler::{Endian, Label, LabelMaker, LabelOrNum, Section, ToLabelOrNum}; + + // Ensure each test tries to read the same section kind that it wrote. + #[derive(Clone, Copy)] + struct SectionKind<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, §ion, input) { + Ok(Some(CieOrFde::Fde(partial))) => partial.parse(get_cie), + Ok(_) => Err(Error::NoEntryAtGivenOffset), + Err(e) => Err(e), + } + } + + // Mixin methods for `Section` to help define binary test data. + + trait CfiSectionMethods: GimliSectionMethods { + fn cie<'aug, 'input, E, T>( + self, + _kind: SectionKind<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(0) + } else { + section + }; + + let section = section + .uleb(cie.code_alignment_factor) + .sleb(cie.data_alignment_factor) + .uleb(cie.return_address_register.0.into()) + .append_bytes(cie.initial_instructions.slice()) + .mark(&end); + + cie.length = (&end - &start) as usize; + length.set_const(cie.length as u64); + + section + } + + fn fde<'a, 'input, E, T, L>( + self, + _kind: SectionKind<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.address_size { + 4 => section + .D32(fde.initial_address() as u32) + .D32(fde.len() as u32), + 8 => section.D64(fde.initial_address()).D64(fde.len()), + x => panic!("Unsupported address size: {}", x), + }; + + let section = if let Some(ref augmentation) = fde.augmentation { + let cie_aug = fde + .cie + .augmentation + .expect("FDE has augmentation, but CIE doesn't"); + + if let Some(lsda) = augmentation.lsda { + // We only support writing `DW_EH_PE_absptr` here. + assert_eq!( + cie_aug + .lsda + .expect("FDE has lsda, but CIE doesn't") + .format(), + constants::DW_EH_PE_absptr + ); + + // Augmentation data length + let section = section.uleb(u64::from(fde.cie.address_size)); + match fde.cie.address_size { + 4 => section.D32({ + let x: u64 = lsda.pointer(); + x as u32 + }), + 8 => section.D64({ + let x: u64 = lsda.pointer(); + x + }), + x => panic!("Unsupported address size: {}", x), + } + } else { + // Even if we don't have any augmentation data, if there is + // an augmentation defined, we need to put the length in. + section.uleb(0) + } + } else { + section + }; + + let section = section.append_bytes(fde.instructions.slice()).mark(&end); + + fde.length = (&end - &start) as usize; + length.set_const(fde.length as u64); + + section + } + } + + trait ResultExt { + fn map_eof(self, input: &[u8]) -> Self; + } + + impl<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, + } + } + } + + 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(§ion); + debug_frame.set_address_size(address_size); + let input = &mut EndianSlice::new(§ion, E::default()); + let bases = Default::default(); + let result = CommonInformationEntry::parse(&bases, &debug_frame, input); + let result = result.map(|cie| (*input, cie)).map_eof(§ion); + assert_eq!(result, expected); + } + + #[test] + fn test_parse_cie_incomplete_length_32() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).L16(5); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(0))), + ); + } + + #[test] + fn test_parse_cie_incomplete_length_64() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .L32(0xffff_ffff) + .L32(12345); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(4))), + ); + } + + #[test] + fn test_parse_cie_incomplete_id_32() { + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + // The length is not large enough to contain the ID. + .B32(3) + .B32(0xffff_ffff); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(4))), + ); + } + + #[test] + fn test_parse_cie_bad_id_32() { + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + // Initial length + .B32(4) + // Not the CIE Id. + .B32(0xbad1_bad2); + assert_parse_cie(kind, section, 8, Err(Error::NotCieId)); + } + + #[test] + fn test_parse_cie_32_bad_version() { + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 99, + augmentation: None, + address_size: 4, + code_alignment_factor: 1, + data_alignment_factor: 2, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&[], LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); + assert_parse_cie(kind, section, 4, Err(Error::UnknownVersion(99))); + } + + #[test] + fn test_parse_cie_unknown_augmentation() { + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let augmentation = "replicant"; + let expected_rest = [1, 2, 3]; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + // Initial length + .L32(&length) + .mark(&start) + // CIE Id + .L32(0xffff_ffff) + // Version + .D8(4) + // Augmentation + .append_bytes(augmentation.as_bytes()) + // Null terminator + .D8(0) + // Extra augmented data that we can't understand. + .L32(1) + .L32(2) + .L32(3) + .L32(4) + .L32(5) + .L32(6) + .mark(&end) + .append_bytes(&expected_rest); + + let expected_length = (&end - &start) as u64; + length.set_const(expected_length); + + assert_parse_cie(kind, section, 8, Err(Error::UnknownAugmentation)); + } + + fn test_parse_cie(format: Format, version: u8, address_size: u8) { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format, + version, + augmentation: None, + address_size, + code_alignment_factor: 16, + data_alignment_factor: 32, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .cie(kind, None, &mut cie) + .append_bytes(&expected_rest); + + assert_parse_cie( + kind, + section, + address_size, + Ok((EndianSlice::new(&expected_rest, LittleEndian), cie)), + ); + } + + #[test] + fn test_parse_cie_32_ok() { + test_parse_cie(Format::Dwarf32, 1, 4); + test_parse_cie(Format::Dwarf32, 1, 8); + test_parse_cie(Format::Dwarf32, 4, 4); + test_parse_cie(Format::Dwarf32, 4, 8); + } + + #[test] + fn test_parse_cie_64_ok() { + test_parse_cie(Format::Dwarf64, 1, 4); + test_parse_cie(Format::Dwarf64, 1, 8); + test_parse_cie(Format::Dwarf64, 4, 4); + test_parse_cie(Format::Dwarf64, 4, 8); + } + + #[test] + fn test_parse_cie_length_too_big() { + let expected_instrs: Vec<_> = (0..13).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + code_alignment_factor: 0, + data_alignment_factor: 0, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); + + let mut contents = section.get_contents().unwrap(); + + // Overwrite the length to be too big. + contents[0] = 0; + contents[1] = 0; + contents[2] = 0; + contents[3] = 255; + + let debug_frame = DebugFrame::new(&contents, LittleEndian); + let bases = Default::default(); + assert_eq!( + CommonInformationEntry::parse( + &bases, + &debug_frame, + &mut EndianSlice::new(&contents, LittleEndian) + ) + .map_eof(&contents), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) + ); + } + + #[test] + fn test_parse_fde_incomplete_length_32() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).L16(5); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + assert_eq!( + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(0))) + ); + } + + #[test] + fn test_parse_fde_incomplete_length_64() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .L32(0xffff_ffff) + .L32(12345); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + assert_eq!( + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) + ); + } + + #[test] + fn test_parse_fde_incomplete_cie_pointer_32() { + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + // The length is not large enough to contain the CIE pointer. + .B32(3) + .B32(1994); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, BigEndian); + assert_eq!( + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) + ); + } + + #[test] + fn test_parse_fde_32_ok() { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let cie_offset = 0xbad0_bad1; + let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); + + let cie = CommonInformationEntry { + offset: 0, + length: 100, + format: Format::Dwarf32, + version: 4, + augmentation: None, + // DWARF32 with a 64 bit address size! Holy moly! + address_size: 8, + code_alignment_factor: 3, + data_alignment_factor: 2, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&[], LittleEndian), + }; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&expected_rest); + + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); + Ok(cie.clone()) + }; + + assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_fde_64_ok() { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let cie_offset = 0xbad0_bad1; + let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); + + let cie = CommonInformationEntry { + offset: 0, + length: 100, + format: Format::Dwarf64, + version: 4, + augmentation: None, + address_size: 8, + code_alignment_factor: 3, + data_alignment_factor: 2, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&[], LittleEndian), + }; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + cie: cie.clone(), + initial_address: 0xfeed_beef, + address_range: 999, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&expected_rest); + + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); + Ok(cie.clone()) + }; + + assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_entry_on_cie_32_ok() { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + code_alignment_factor: 16, + data_alignment_factor: 32, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&expected_instrs, BigEndian), + }; + + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .cie(kind, None, &mut cie) + .append_bytes(&expected_rest); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, BigEndian); + + let bases = Default::default(); + assert_eq!( + parse_cfi_entry(&bases, &debug_frame, rest), + Ok(Some(CieOrFde::Cie(cie))) + ); + assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); + } + + #[test] + fn test_parse_cfi_entry_on_fde_32_ok() { + let cie_offset = 0x1234_5678; + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + code_alignment_factor: 16, + data_alignment_factor: 32, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&[], BigEndian), + }; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs, BigEndian), + }; + + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&expected_rest); + + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, BigEndian); + + let bases = Default::default(); + match parse_cfi_entry(&bases, &debug_frame, rest) { + Ok(Some(CieOrFde::Fde(partial))) => { + assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); + + assert_eq!(partial.length, fde.length); + assert_eq!(partial.format, fde.format); + assert_eq!(partial.cie_offset, DebugFrameOffset(cie_offset as usize)); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); + Ok(cie.clone()) + }; + + assert_eq!(partial.parse(get_cie), Ok(fde)); + } + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + } + + #[test] + fn test_cfi_entries_iter() { + let expected_instrs1: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let expected_instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); + + let expected_instrs3: Vec<_> = (0..12).map(|_| constants::DW_CFA_nop.0).collect(); + + let expected_instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie1 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + code_alignment_factor: 1, + data_alignment_factor: 2, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&expected_instrs1, BigEndian), + }; + + let mut cie2 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + code_alignment_factor: 3, + data_alignment_factor: 2, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&expected_instrs2, BigEndian), + }; + + let cie1_location = Label::new(); + let cie2_location = Label::new(); + + // Write the CIEs first so that their length gets set before we clone + // them into the FDEs and our equality assertions down the line end up + // with all the CIEs always having he correct length. + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .mark(&cie1_location) + .cie(kind, None, &mut cie1) + .mark(&cie2_location) + .cie(kind, None, &mut cie2); + + let mut fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie1.clone(), + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs3, BigEndian), + }; + + let mut fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie2.clone(), + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs4, BigEndian), + }; + + let section = + section + .fde(kind, &cie1_location, &mut fde1) + .fde(kind, &cie2_location, &mut fde2); + + section.start().set_const(0); + + let cie1_offset = cie1_location.value().unwrap() as usize; + let cie2_offset = cie2_location.value().unwrap() as usize; + + let contents = section.get_contents().unwrap(); + let debug_frame = kind.section(&contents); + + let bases = Default::default(); + let mut entries = debug_frame.entries(&bases); + + assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie1.clone())))); + assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie2.clone())))); + + match entries.next() { + Ok(Some(CieOrFde::Fde(partial))) => { + assert_eq!(partial.length, fde1.length); + assert_eq!(partial.format, fde1.format); + assert_eq!(partial.cie_offset, DebugFrameOffset(cie1_offset)); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie1_offset)); + Ok(cie1.clone()) + }; + assert_eq!(partial.parse(get_cie), Ok(fde1)); + } + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + + match entries.next() { + Ok(Some(CieOrFde::Fde(partial))) => { + assert_eq!(partial.length, fde2.length); + assert_eq!(partial.format, fde2.format); + assert_eq!(partial.cie_offset, DebugFrameOffset(cie2_offset)); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie2_offset)); + Ok(cie2.clone()) + }; + assert_eq!(partial.parse(get_cie), Ok(fde2)); + } + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + + assert_eq!(entries.next(), Ok(None)); + } + + #[test] + fn test_parse_cie_from_offset() { + let filler = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let instrs: Vec<_> = (0..5).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + version: 4, + augmentation: None, + address_size: 4, + code_alignment_factor: 4, + data_alignment_factor: 8, + return_address_register: Register(12), + initial_instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let cie_location = Label::new(); + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_bytes(&filler) + .mark(&cie_location) + .cie(kind, None, &mut cie) + .append_bytes(&filler); + + section.start().set_const(0); + + let cie_offset = DebugFrameOffset(cie_location.value().unwrap() as usize); + + let contents = section.get_contents().unwrap(); + let debug_frame = kind.section(&contents); + let bases = Default::default(); + + assert_eq!(debug_frame.cie_from_offset(&bases, cie_offset), Ok(cie)); + } + + fn parse_cfi_instruction<R: Reader + Default>( + input: &mut R, + address_size: u8, + ) -> Result<CallFrameInstruction<R::Offset>> { + let section = input.clone(); + let parameters = &PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size, + section: §ion, + }; + CallFrameInstruction::parse(input, None, parameters, Vendor::Default) + } + + #[test] + fn test_parse_cfi_instruction_advance_loc() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 42; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc.0 | expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_offset() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 3; + let expected_offset = 1997; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_offset.0 | expected_reg) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Offset { + register: Register(expected_reg.into()), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_restore() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 3; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_restore.0 | expected_reg) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Restore { + register: Register(expected_reg.into()), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_nop() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_nop.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Nop) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_set_loc() { + let expected_rest = [1, 2, 3, 4]; + let expected_addr = 0xdead_beef; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_set_loc.0) + .L64(expected_addr) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::SetLoc { + address: expected_addr, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_set_loc_encoding() { + let text_base = 0xfeed_face; + let addr_offset = 0xbeef; + let expected_addr = text_base + addr_offset; + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_set_loc.0) + .L64(addr_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + let parameters = &PointerEncodingParameters { + bases: &BaseAddresses::default().set_text(text_base).eh_frame, + func_base: None, + address_size: 8, + section: &EndianSlice::new(&[], LittleEndian), + }; + assert_eq!( + CallFrameInstruction::parse( + input, + Some(constants::DW_EH_PE_textrel), + parameters, + Vendor::Default + ), + Ok(CallFrameInstruction::SetLoc { + address: expected_addr, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_advance_loc1() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 8; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc1.0) + .D8(expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_advance_loc2() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 500; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc2.0) + .L16(expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_advance_loc4() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 1 << 20; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc4.0) + .L32(expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: expected_delta, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_offset_extended() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let expected_offset = 33; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_offset_extended.0) + .uleb(expected_reg.into()) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Offset { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_restore_extended() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_restore_extended.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Restore { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_undefined() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_undefined.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Undefined { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_same_value() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_same_value.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::SameValue { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_register() { + let expected_rest = [1, 2, 3, 4]; + let expected_dest_reg = 7; + let expected_src_reg = 8; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_register.0) + .uleb(expected_dest_reg.into()) + .uleb(expected_src_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Register { + dest_register: Register(expected_dest_reg), + src_register: Register(expected_src_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_remember_state() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_remember_state.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::RememberState) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_restore_state() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_restore_state.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::RestoreState) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 2; + let expected_offset = 0; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa.0) + .uleb(expected_reg.into()) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfa { + register: Register(expected_reg), + offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_register() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 2; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_register.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaRegister { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_offset() { + let expected_rest = [1, 2, 3, 4]; + let expected_offset = 23; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_offset.0) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaOffset { + offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_expression() { + let expected_rest = [1, 2, 3, 4]; + let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_expression.0) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .append_bytes(&expected_rest); + + length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaExpression { + expression: expected_expression, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_expression() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 99; + let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_expression.0) + .uleb(expected_reg.into()) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .append_bytes(&expected_rest); + + length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Expression { + register: Register(expected_reg), + expression: expected_expression, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_offset_extended_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let expected_offset = -33; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(expected_reg.into()) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::OffsetExtendedSf { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 2; + let expected_offset = -9999; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(expected_reg.into()) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaSf { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_offset_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_offset = -123; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_offset_sf.0) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaOffsetSf { + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_val_offset() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 50; + let expected_offset = 23; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_val_offset.0) + .uleb(expected_reg.into()) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::ValOffset { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_val_offset_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 50; + let expected_offset = -23; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_val_offset_sf.0) + .uleb(expected_reg.into()) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::ValOffsetSf { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_val_expression() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 50; + let expected_expr = [2, 2, 1, 1, 5, 5]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_val_expression.0) + .uleb(expected_reg.into()) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .append_bytes(&expected_rest); + + length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::ValExpression { + register: Register(expected_reg), + expression: expected_expression, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_negate_ra_state() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_AARCH64_negate_ra_state.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + let parameters = &PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &EndianSlice::default(), + }; + assert_eq!( + CallFrameInstruction::parse(input, None, parameters, Vendor::AArch64), + Ok(CallFrameInstruction::NegateRaState) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_unknown_instruction() { + let expected_rest = [1, 2, 3, 4]; + let unknown_instr = constants::DwCfa(0b0011_1111); + let section = Section::with_endian(Endian::Little) + .D8(unknown_instr.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Err(Error::UnknownCallFrameInstruction(unknown_instr)) + ); + } + + #[test] + fn test_call_frame_instruction_iter_ok() { + let expected_reg = 50; + let expected_expr = [2, 2, 1, 1, 5, 5]; + let expected_delta = 230; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Big) + .D8(constants::DW_CFA_val_expression.0) + .uleb(expected_reg.into()) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .D8(constants::DW_CFA_advance_loc1.0) + .D8(expected_delta); + + length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; + let contents = section.get_contents().unwrap(); + let input = EndianSlice::new(&contents, BigEndian); + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &input, + }; + let mut iter = CallFrameInstructionIter { + input, + address_encoding: None, + parameters, + vendor: Vendor::Default, + }; + + assert_eq!( + iter.next(), + Ok(Some(CallFrameInstruction::ValExpression { + register: Register(expected_reg), + expression: expected_expression, + })) + ); + + assert_eq!( + iter.next(), + Ok(Some(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + })) + ); + + assert_eq!(iter.next(), Ok(None)); + } + + #[test] + fn test_call_frame_instruction_iter_err() { + // DW_CFA_advance_loc1 without an operand. + let section = Section::with_endian(Endian::Big).D8(constants::DW_CFA_advance_loc1.0); + + let contents = section.get_contents().unwrap(); + let input = EndianSlice::new(&contents, BigEndian); + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &EndianSlice::default(), + }; + let mut iter = CallFrameInstructionIter { + input, + address_encoding: None, + parameters, + vendor: Vendor::Default, + }; + + assert_eq!( + iter.next().map_eof(&contents), + Err(Error::UnexpectedEof(ReaderOffsetId(1))) + ); + assert_eq!(iter.next(), Ok(None)); + } + + fn assert_eval<'a, I>( + mut initial_ctx: UnwindContext<usize>, + expected_ctx: UnwindContext<usize>, + cie: CommonInformationEntry<EndianSlice<'a, LittleEndian>>, + fde: Option<FrameDescriptionEntry<EndianSlice<'a, LittleEndian>>>, + instructions: I, + ) where + I: AsRef<[(Result<bool>, CallFrameInstruction<usize>)]>, + { + { + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut table = match fde { + Some(fde) => UnwindTable::new_for_fde(section, bases, &mut initial_ctx, &fde), + None => UnwindTable::new_for_cie(section, bases, &mut initial_ctx, &cie), + }; + for (expected_result, instruction) in instructions.as_ref() { + assert_eq!(*expected_result, table.evaluate(instruction.clone())); + } + } + + assert_eq!(expected_ctx, initial_ctx); + } + + fn make_test_cie<'a>() -> CommonInformationEntry<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, + data_alignment_factor: 2, + code_alignment_factor: 3, + } + } + + #[test] + fn test_eval_set_loc() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.row_mut().end_address = 42; + let instructions = [(Ok(true), CallFrameInstruction::SetLoc { address: 42 })]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_set_loc_backwards() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.row_mut().start_address = 999; + let expected = ctx.clone(); + let instructions = [( + Err(Error::InvalidAddressRange), + CallFrameInstruction::SetLoc { address: 42 }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_advance_loc() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.row_mut().start_address = 3; + let mut expected = ctx.clone(); + expected.row_mut().end_address = 3 + 2 * cie.code_alignment_factor; + let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 2 })]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_advance_loc_overflow_32() { + let mut cie = make_test_cie(); + cie.address_size = 4; + let mut ctx = UnwindContext::new(); + ctx.row_mut().start_address = u32::MAX.into(); + let expected = ctx.clone(); + let instructions = [( + Err(Error::AddressOverflow), + CallFrameInstruction::AdvanceLoc { delta: 42 }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_advance_loc_overflow_64() { + let mut cie = make_test_cie(); + cie.address_size = 8; + let mut ctx = UnwindContext::new(); + ctx.row_mut().start_address = u64::MAX; + let expected = ctx.clone(); + let instructions = [( + Err(Error::AddressOverflow), + CallFrameInstruction::AdvanceLoc { delta: 42 }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(42), + offset: 36, + }); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfa { + register: Register(42), + offset: 36, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_sf() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(42), + offset: 36 * cie.data_alignment_factor as i64, + }); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfaSf { + register: Register(42), + factored_offset: 36, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_register() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::RegisterAndOffset { + register: Register(3), + offset: 8, + }); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(42), + offset: 8, + }); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfaRegister { + register: Register(42), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_register_invalid_context() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::Expression(UnwindExpression { + offset: 0, + length: 0, + })); + let expected = ctx.clone(); + let instructions = [( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::DefCfaRegister { + register: Register(42), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_offset() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::RegisterAndOffset { + register: Register(3), + offset: 8, + }); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(3), + offset: 42, + }); + let instructions = [(Ok(false), CallFrameInstruction::DefCfaOffset { offset: 42 })]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_offset_invalid_context() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::Expression(UnwindExpression { + offset: 10, + length: 11, + })); + let expected = ctx.clone(); + let instructions = [( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::DefCfaOffset { offset: 1993 }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_expression() { + let expr = UnwindExpression { + offset: 10, + length: 11, + }; + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::Expression(expr)); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfaExpression { expression: expr }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_undefined() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(Register(5), RegisterRule::Undefined) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::Undefined { + register: Register(5), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_same_value() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(Register(0), RegisterRule::SameValue) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::SameValue { + register: Register(0), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_offset() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(2), + RegisterRule::Offset(3 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::Offset { + register: Register(2), + factored_offset: 3, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_offset_extended_sf() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(4), + RegisterRule::Offset(-3 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::OffsetExtendedSf { + register: Register(4), + factored_offset: -3, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_val_offset() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(5), + RegisterRule::ValOffset(7 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::ValOffset { + register: Register(5), + factored_offset: 7, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_val_offset_sf() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(5), + RegisterRule::ValOffset(-7 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::ValOffsetSf { + register: Register(5), + factored_offset: -7, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_expression() { + let expr = UnwindExpression { + offset: 10, + length: 11, + }; + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(Register(9), RegisterRule::Expression(expr)) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::Expression { + register: Register(9), + expression: expr, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_val_expression() { + let expr = UnwindExpression { + offset: 10, + length: 11, + }; + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(Register(9), RegisterRule::ValExpression(expr)) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::ValExpression { + register: Register(9), + expression: expr, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_restore() { + let cie = make_test_cie(); + let fde = FrameDescriptionEntry { + offset: 0, + format: Format::Dwarf64, + length: 0, + address_range: 0, + augmentation: None, + initial_address: 0, + cie: cie.clone(), + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let mut ctx = UnwindContext::new(); + ctx.set_register_rule(Register(0), RegisterRule::Offset(1)) + .unwrap(); + ctx.save_initial_rules().unwrap(); + let expected = ctx.clone(); + ctx.set_register_rule(Register(0), RegisterRule::Offset(2)) + .unwrap(); + + let instructions = [( + Ok(false), + CallFrameInstruction::Restore { + register: Register(0), + }, + )]; + assert_eval(ctx, expected, cie, Some(fde), instructions); + } + + #[test] + fn test_eval_restore_havent_saved_initial_context() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let expected = ctx.clone(); + let instructions = [( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::Restore { + register: Register(0), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_remember_state() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.push_row().unwrap(); + let instructions = [(Ok(false), CallFrameInstruction::RememberState)]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_restore_state() { + let cie = make_test_cie(); + + let mut ctx = UnwindContext::new(); + ctx.set_start_address(1); + ctx.set_register_rule(Register(0), RegisterRule::SameValue) + .unwrap(); + let mut expected = ctx.clone(); + ctx.push_row().unwrap(); + ctx.set_start_address(2); + ctx.set_register_rule(Register(0), RegisterRule::Offset(16)) + .unwrap(); + + // Restore state should preserve current location. + expected.set_start_address(2); + + let instructions = [ + // First one pops just fine. + (Ok(false), CallFrameInstruction::RestoreState), + // Second pop would try to pop out of bounds. + ( + Err(Error::PopWithEmptyStack), + CallFrameInstruction::RestoreState, + ), + ]; + + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_negate_ra_state() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(1)) + .unwrap(); + let instructions = [(Ok(false), CallFrameInstruction::NegateRaState)]; + assert_eval(ctx, expected, cie, None, instructions); + + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(0)) + .unwrap(); + let instructions = [ + (Ok(false), CallFrameInstruction::NegateRaState), + (Ok(false), CallFrameInstruction::NegateRaState), + ]; + assert_eval(ctx, expected, cie, None, instructions); + + // NegateRaState can't be used with other instructions. + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + crate::AArch64::RA_SIGN_STATE, + RegisterRule::Offset(cie.data_alignment_factor as i64), + ) + .unwrap(); + let instructions = [ + ( + Ok(false), + CallFrameInstruction::Offset { + register: crate::AArch64::RA_SIGN_STATE, + factored_offset: 1, + }, + ), + ( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::NegateRaState, + ), + ]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_nop() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let expected = ctx.clone(); + let instructions = [(Ok(false), CallFrameInstruction::Nop)]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_unwind_table_cie_no_rule() { + let initial_instructions = Section::with_endian(Endian::Little) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions = initial_instructions.get_contents().unwrap(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), + }; + + let instructions = Section::with_endian(Endian::Little) + // A bunch of nop padding. + .append_repeated(constants::DW_CFA_nop.0, 8); + let instructions = instructions.get_contents().unwrap(); + + let fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&instructions, LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let mut table = fde + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(table.ctx.is_initialized); + let expected_initial_rule = (Register(0), RegisterRule::Undefined); + assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); + + { + let row = table.next_row().expect("Should evaluate first row OK"); + let expected = UnwindTableRow { + start_address: 0, + end_address: 100, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [].iter().collect(), + }; + assert_eq!(Some(&expected), row); + } + + // All done! + assert_eq!(Ok(None), table.next_row()); + assert_eq!(Ok(None), table.next_row()); + } + + #[test] + fn test_unwind_table_cie_single_rule() { + let initial_instructions = Section::with_endian(Endian::Little) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12) + // Register 3 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 3) + .uleb(4) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions = initial_instructions.get_contents().unwrap(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), + }; + + let instructions = Section::with_endian(Endian::Little) + // A bunch of nop padding. + .append_repeated(constants::DW_CFA_nop.0, 8); + let instructions = instructions.get_contents().unwrap(); + + let fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&instructions, LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let mut table = fde + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(table.ctx.is_initialized); + let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); + assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); + + { + let row = table.next_row().expect("Should evaluate first row OK"); + let expected = UnwindTableRow { + start_address: 0, + end_address: 100, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [(Register(3), RegisterRule::Offset(4))].iter().collect(), + }; + assert_eq!(Some(&expected), row); + } + + // All done! + assert_eq!(Ok(None), table.next_row()); + assert_eq!(Ok(None), table.next_row()); + } + + #[test] + fn test_unwind_table_cie_invalid_rule() { + let initial_instructions1 = Section::with_endian(Endian::Little) + // Test that stack length is reset. + .D8(constants::DW_CFA_remember_state.0) + // Test that stack value is reset (different register from that used later). + .D8(constants::DW_CFA_offset.0 | 4) + .uleb(8) + // Invalid due to missing operands. + .D8(constants::DW_CFA_offset.0); + let initial_instructions1 = initial_instructions1.get_contents().unwrap(); + + let cie1 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions1, LittleEndian), + }; + + let initial_instructions2 = Section::with_endian(Endian::Little) + // Register 3 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 3) + .uleb(4) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions2 = initial_instructions2.get_contents().unwrap(); + + let cie2 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions2, LittleEndian), + }; + + let fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie1.clone(), + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie2.clone(), + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let table = fde1 + .rows(section, bases, &mut ctx) + .map_eof(&initial_instructions1); + assert_eq!(table.err(), Some(Error::UnexpectedEof(ReaderOffsetId(4)))); + assert!(!ctx.is_initialized); + assert_eq!(ctx.stack.len(), 2); + assert_eq!(ctx.initial_rule, None); + + let _table = fde2 + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(ctx.is_initialized); + assert_eq!(ctx.stack.len(), 1); + let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); + assert_eq!(ctx.initial_rule, Some(expected_initial_rule)); + } + + #[test] + fn test_unwind_table_next_row() { + #[allow(clippy::identity_op)] + let initial_instructions = Section::with_endian(Endian::Little) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12) + // Register 0 is 8 from the CFA. + .D8(constants::DW_CFA_offset.0 | 0) + .uleb(8) + // Register 3 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 3) + .uleb(4) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions = initial_instructions.get_contents().unwrap(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), + }; + + let instructions = Section::with_endian(Endian::Little) + // Initial instructions form a row, advance the address by 1. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(1) + // Register 0 is -16 from the CFA. + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(0) + .sleb(-16) + // Finish this row, advance the address by 32. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(32) + // Register 3 is -4 from the CFA. + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(3) + .sleb(-4) + // Finish this row, advance the address by 64. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(64) + // Register 5 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 5) + .uleb(4) + // A bunch of nop padding. + .append_repeated(constants::DW_CFA_nop.0, 8); + let instructions = instructions.get_contents().unwrap(); + + let fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&instructions, LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let mut table = fde + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(table.ctx.is_initialized); + assert!(table.ctx.initial_rule.is_none()); + let expected_initial_rules: RegisterRuleMap<_> = [ + (Register(0), RegisterRule::Offset(8)), + (Register(3), RegisterRule::Offset(4)), + ] + .iter() + .collect(); + assert_eq!(table.ctx.stack[0].registers, expected_initial_rules); + + { + let row = table.next_row().expect("Should evaluate first row OK"); + let expected = UnwindTableRow { + start_address: 0, + end_address: 1, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(8)), + (Register(3), RegisterRule::Offset(4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + { + let row = table.next_row().expect("Should evaluate second row OK"); + let expected = UnwindTableRow { + start_address: 1, + end_address: 33, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(-16)), + (Register(3), RegisterRule::Offset(4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + { + let row = table.next_row().expect("Should evaluate third row OK"); + let expected = UnwindTableRow { + start_address: 33, + end_address: 97, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(-16)), + (Register(3), RegisterRule::Offset(-4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + { + let row = table.next_row().expect("Should evaluate fourth row OK"); + let expected = UnwindTableRow { + start_address: 97, + end_address: 100, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(-16)), + (Register(3), RegisterRule::Offset(-4)), + (Register(5), RegisterRule::Offset(4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + // All done! + assert_eq!(Ok(None), table.next_row()); + assert_eq!(Ok(None), table.next_row()); + } + + #[test] + fn test_unwind_info_for_address_ok() { + let instrs1 = Section::with_endian(Endian::Big) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12); + let instrs1 = instrs1.get_contents().unwrap(); + + let instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); + + let instrs3 = Section::with_endian(Endian::Big) + // Initial instructions form a row, advance the address by 100. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(100) + // Register 0 is -16 from the CFA. + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(0) + .sleb(-16); + let instrs3 = instrs3.get_contents().unwrap(); + + let instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie1 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&instrs1, BigEndian), + }; + + let mut cie2 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&instrs2, BigEndian), + }; + + let cie1_location = Label::new(); + let cie2_location = Label::new(); + + // Write the CIEs first so that their length gets set before we clone + // them into the FDEs and our equality assertions down the line end up + // with all the CIEs always having he correct length. + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .mark(&cie1_location) + .cie(kind, None, &mut cie1) + .mark(&cie2_location) + .cie(kind, None, &mut cie2); + + let mut fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie1.clone(), + initial_address: 0xfeed_beef, + address_range: 200, + augmentation: None, + instructions: EndianSlice::new(&instrs3, BigEndian), + }; + + let mut fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie2.clone(), + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: None, + instructions: EndianSlice::new(&instrs4, BigEndian), + }; + + let section = + section + .fde(kind, &cie1_location, &mut fde1) + .fde(kind, &cie2_location, &mut fde2); + section.start().set_const(0); + + let contents = section.get_contents().unwrap(); + let debug_frame = kind.section(&contents); + + // Get the second row of the unwind table in `instrs3`. + let bases = Default::default(); + let mut ctx = Box::new(UnwindContext::new()); + let result = debug_frame.unwind_info_for_address( + &bases, + &mut ctx, + 0xfeed_beef + 150, + DebugFrame::cie_from_offset, + ); + assert!(result.is_ok()); + let unwind_info = result.unwrap(); + + assert_eq!( + *unwind_info, + UnwindTableRow { + start_address: fde1.initial_address() + 100, + end_address: fde1.end_address(), + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [(Register(0), RegisterRule::Offset(-16))].iter().collect(), + } + ); + } + + #[test] + fn test_unwind_info_for_address_not_found() { + let debug_frame = DebugFrame::new(&[], NativeEndian); + let bases = Default::default(); + let mut ctx = Box::new(UnwindContext::new()); + let result = debug_frame.unwind_info_for_address( + &bases, + &mut ctx, + 0xbadb_ad99, + DebugFrame::cie_from_offset, + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress); + } + + #[test] + fn test_eh_frame_hdr_unknown_version() { + let bases = BaseAddresses::default(); + let buf = &[42]; + let result = EhFrameHdr::new(buf, NativeEndian).parse(&bases, 8); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::UnknownVersion(42)); + } + + #[test] + fn test_eh_frame_hdr_omit_ehptr() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0xff) + .L8(0x03) + .L8(0x0b) + .L32(2) + .L32(10) + .L32(1) + .L32(20) + .L32(2) + .L32(0); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::CannotParseOmitPointerEncoding); + } + + #[test] + fn test_eh_frame_hdr_omit_count() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0xff) + .L8(0x0b) + .L32(0x12345); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + assert!(result.table().is_none()); + } + + #[test] + fn test_eh_frame_hdr_omit_table() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0xff) + .L32(0x12345) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + assert!(result.table().is_none()); + } + + #[test] + fn test_eh_frame_hdr_varlen_table() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0x01) + .L32(0x12345) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + let table = result.table(); + assert!(table.is_some()); + let table = table.unwrap(); + assert_eq!( + table.lookup(0, &bases), + Err(Error::VariableLengthSearchTable) + ); + } + + #[test] + fn test_eh_frame_hdr_indirect_length() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x83) + .L8(0x0b) + .L32(0x12345) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::UnsupportedPointerEncoding); + } + + #[test] + fn test_eh_frame_hdr_indirect_ptrs() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x8b) + .L8(0x03) + .L8(0x8b) + .L32(0x12345) + .L32(2) + .L32(10) + .L32(1) + .L32(20) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Indirect(0x12345)); + let table = result.table(); + assert!(table.is_some()); + let table = table.unwrap(); + assert_eq!( + table.lookup(0, &bases), + Err(Error::UnsupportedPointerEncoding) + ); + } + + #[test] + fn test_eh_frame_hdr_good() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0x0b) + .L32(0x12345) + .L32(2) + .L32(10) + .L32(1) + .L32(20) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + let table = result.table(); + assert!(table.is_some()); + let table = table.unwrap(); + assert_eq!(table.lookup(0, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(9, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(10, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(11, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(19, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(20, &bases), Ok(Pointer::Direct(2))); + assert_eq!(table.lookup(21, &bases), Ok(Pointer::Direct(2))); + assert_eq!(table.lookup(100_000, &bases), Ok(Pointer::Direct(2))); + } + + #[test] + fn test_eh_frame_fde_for_address_good() { + // First, setup eh_frame + // Write the CIE first so that its length gets set before we clone it + // into the FDE. + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + + let start_of_cie = Label::new(); + let end_of_cie = Label::new(); + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_repeated(0, 16) + .mark(&start_of_cie) + .cie(kind, None, &mut cie) + .mark(&end_of_cie); + + let mut fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 9, + address_range: 4, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + let mut fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 20, + address_range: 8, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let start_of_fde1 = Label::new(); + let start_of_fde2 = Label::new(); + + let section = section + // +4 for the FDE length before the CIE offset. + .mark(&start_of_fde1) + .fde(kind, (&start_of_fde1 - &start_of_cie + 4) as u64, &mut fde1) + .mark(&start_of_fde2) + .fde(kind, (&start_of_fde2 - &start_of_cie + 4) as u64, &mut fde2); + + section.start().set_const(0); + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + + // Setup eh_frame_hdr + let section = Section::with_endian(kind.endian()) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0x0b) + .L32(0x12345) + .L32(2) + .L32(10) + .L32(0x12345 + start_of_fde1.value().unwrap() as u32) + .L32(20) + .L32(0x12345 + start_of_fde2.value().unwrap() as u32); + + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let eh_frame_hdr = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(eh_frame_hdr.is_ok()); + let eh_frame_hdr = eh_frame_hdr.unwrap(); + + let table = eh_frame_hdr.table(); + assert!(table.is_some()); + let table = table.unwrap(); + + let bases = Default::default(); + let mut iter = table.iter(&bases); + assert_eq!( + iter.next(), + Ok(Some(( + Pointer::Direct(10), + Pointer::Direct(0x12345 + start_of_fde1.value().unwrap()) + ))) + ); + assert_eq!( + iter.next(), + Ok(Some(( + Pointer::Direct(20), + Pointer::Direct(0x12345 + start_of_fde2.value().unwrap()) + ))) + ); + assert_eq!(iter.next(), Ok(None)); + + assert_eq!( + table.iter(&bases).nth(0), + Ok(Some(( + Pointer::Direct(10), + Pointer::Direct(0x12345 + start_of_fde1.value().unwrap()) + ))) + ); + + assert_eq!( + table.iter(&bases).nth(1), + Ok(Some(( + Pointer::Direct(20), + Pointer::Direct(0x12345 + start_of_fde2.value().unwrap()) + ))) + ); + assert_eq!(table.iter(&bases).nth(2), Ok(None)); + + let f = |_: &_, _: &_, o: EhFrameOffset| { + assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); + Ok(cie.clone()) + }; + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 9, f), + Ok(fde1.clone()) + ); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 10, f), + Ok(fde1.clone()) + ); + assert_eq!(table.fde_for_address(&eh_frame, &bases, 11, f), Ok(fde1)); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 19, f), + Err(Error::NoUnwindInfoForAddress) + ); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 20, f), + Ok(fde2.clone()) + ); + assert_eq!(table.fde_for_address(&eh_frame, &bases, 21, f), Ok(fde2)); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 100_000, f), + Err(Error::NoUnwindInfoForAddress) + ); + } + + #[test] + fn test_eh_frame_stops_at_zero_length() { + let mut cie = make_test_cie(); + let kind = eh_frame_le(); + let section = Section::with_endian(Endian::Little) + .L32(0) + .cie(kind, None, &mut cie) + .L32(0); + let contents = section.get_contents().unwrap(); + let eh_frame = kind.section(&contents); + let bases = Default::default(); + + let mut entries = eh_frame.entries(&bases); + assert_eq!(entries.next(), Ok(None)); + + assert_eq!( + eh_frame.cie_from_offset(&bases, EhFrameOffset(0)), + Err(Error::NoEntryAtGivenOffset) + ); + } + + #[test] + fn test_debug_frame_skips_zero_length() { + let mut cie = make_test_cie(); + let kind = debug_frame_le(); + let section = Section::with_endian(Endian::Little) + .L32(0) + .cie(kind, None, &mut cie) + .L32(0); + let contents = section.get_contents().unwrap(); + let debug_frame = kind.section(&contents); + let bases = Default::default(); + + let mut entries = debug_frame.entries(&bases); + assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie)))); + assert_eq!(entries.next(), Ok(None)); + + assert_eq!( + debug_frame.cie_from_offset(&bases, DebugFrameOffset(0)), + Err(Error::NoEntryAtGivenOffset) + ); + } + + fn resolve_cie_offset(buf: &[u8], cie_offset: usize) -> Result<usize> { + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + cie: make_test_cie(), + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_bytes(buf) + .fde(kind, cie_offset as u64, &mut fde) + .append_bytes(buf); + + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + let input = &mut EndianSlice::new(§ion[buf.len()..], LittleEndian); + + let bases = Default::default(); + match parse_cfi_entry(&bases, &eh_frame, input) { + Ok(Some(CieOrFde::Fde(partial))) => Ok(partial.cie_offset.0), + Err(e) => Err(e), + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + } + + #[test] + fn test_eh_frame_resolve_cie_offset_ok() { + let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let cie_offset = 2; + // + 4 for size of length field + assert_eq!( + resolve_cie_offset(&buf, buf.len() + 4 - cie_offset), + Ok(cie_offset) + ); + } + + #[test] + fn test_eh_frame_resolve_cie_offset_out_of_bounds() { + let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + assert_eq!( + resolve_cie_offset(&buf, buf.len() + 4 + 2), + Err(Error::OffsetOutOfBounds) + ); + } + + #[test] + fn test_eh_frame_resolve_cie_offset_underflow() { + let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + assert_eq!( + resolve_cie_offset(&buf, usize::MAX), + Err(Error::OffsetOutOfBounds) + ); + } + + #[test] + fn test_eh_frame_fde_ok() { + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + + let start_of_cie = Label::new(); + let end_of_cie = Label::new(); + + // Write the CIE first so that its length gets set before we clone it + // into the FDE. + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_repeated(0, 16) + .mark(&start_of_cie) + .cie(kind, None, &mut cie) + .mark(&end_of_cie); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0xfeed_beef, + address_range: 999, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let section = section + // +4 for the FDE length before the CIE offset. + .fde(kind, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde); + + section.start().set_const(0); + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + let section = EndianSlice::new(§ion, LittleEndian); + + let mut offset = None; + let result = parse_fde( + eh_frame, + &mut section.range_from(end_of_cie.value().unwrap() as usize..), + |_, _, o| { + offset = Some(o); + assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); + Ok(cie.clone()) + }, + ); + match result { + Ok(actual) => assert_eq!(actual, fde), + otherwise => panic!("Unexpected result {:?}", otherwise), + } + assert!(offset.is_some()); + } + + #[test] + fn test_eh_frame_fde_out_of_bounds() { + let mut cie = make_test_cie(); + cie.version = 1; + + let end_of_cie = Label::new(); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + cie: cie.clone(), + initial_address: 0xfeed_beef, + address_range: 999, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .cie(kind, None, &mut cie) + .mark(&end_of_cie) + .fde(kind, 99_999_999_999_999, &mut fde); + + section.start().set_const(0); + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + let section = EndianSlice::new(§ion, LittleEndian); + + let result = parse_fde( + eh_frame, + &mut section.range_from(end_of_cie.value().unwrap() as usize..), + UnwindSection::cie_from_offset, + ); + assert_eq!(result, Err(Error::OffsetOutOfBounds)); + } + + #[test] + fn test_augmentation_parse_not_z_augmentation() { + let augmentation = &mut EndianSlice::new(b"wtf", NativeEndian); + let bases = Default::default(); + let address_size = 8; + let section = EhFrame::new(&[], NativeEndian); + let input = &mut EndianSlice::new(&[], NativeEndian); + assert_eq!( + Augmentation::parse(augmentation, &bases, address_size, §ion, input), + Err(Error::UnknownAugmentation) + ); + } + + #[test] + fn test_augmentation_parse_just_signal_trampoline() { + let aug_str = &mut EndianSlice::new(b"S", LittleEndian); + let bases = Default::default(); + let address_size = 8; + let section = EhFrame::new(&[], LittleEndian); + let input = &mut EndianSlice::new(&[], LittleEndian); + + let augmentation = Augmentation { + is_signal_trampoline: true, + ..Default::default() + }; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + } + + #[test] + fn test_augmentation_parse_unknown_part_of_z_augmentation() { + // The 'Z' character is not defined by the z-style augmentation. + let bases = Default::default(); + let address_size = 8; + let section = Section::with_endian(Endian::Little) + .uleb(4) + .append_repeated(4, 4) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let augmentation = &mut EndianSlice::new(b"zZ", LittleEndian); + assert_eq!( + Augmentation::parse(augmentation, &bases, address_size, §ion, input), + Err(Error::UnknownAugmentation) + ); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_L() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(1) + .D8(constants::DW_EH_PE_uleb128.0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zL", LittleEndian); + + let augmentation = Augmentation { + lsda: Some(constants::DW_EH_PE_uleb128), + ..Default::default() + }; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_P() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(9) + .D8(constants::DW_EH_PE_udata8.0) + .L64(0xf00d_f00d) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zP", LittleEndian); + + let augmentation = Augmentation { + personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0xf00d_f00d))), + ..Default::default() + }; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_R() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(1) + .D8(constants::DW_EH_PE_udata4.0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zR", LittleEndian); + + let augmentation = Augmentation { + fde_address_encoding: Some(constants::DW_EH_PE_udata4), + ..Default::default() + }; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_S() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zS", LittleEndian); + + let augmentation = Augmentation { + is_signal_trampoline: true, + ..Default::default() + }; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_augmentation_parse_all() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(1 + 9 + 1) + // L + .D8(constants::DW_EH_PE_uleb128.0) + // P + .D8(constants::DW_EH_PE_udata8.0) + .L64(0x1bad_f00d) + // R + .D8(constants::DW_EH_PE_uleb128.0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zLPRS", LittleEndian); + + let augmentation = Augmentation { + lsda: Some(constants::DW_EH_PE_uleb128), + personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0x1bad_f00d))), + fde_address_encoding: Some(constants::DW_EH_PE_uleb128), + is_signal_trampoline: true, + }; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_no_augmentation() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: None, + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().clone(); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_empty_augmentation() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + cie.augmentation = Some(Augmentation::default()); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: Some(AugmentationData::default()), + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().clone(); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_lsda_augmentation() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + cie.augmentation = Some(Augmentation::default()); + cie.augmentation.as_mut().unwrap().lsda = Some(constants::DW_EH_PE_absptr); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: Some(AugmentationData { + lsda: Some(Pointer::Direct(0x1122_3344)), + }), + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().clone(); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_lsda_function_relative() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + cie.augmentation = Some(Augmentation::default()); + cie.augmentation.as_mut().unwrap().lsda = + Some(constants::DW_EH_PE_funcrel | constants::DW_EH_PE_absptr); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: Some(AugmentationData { + lsda: Some(Pointer::Direct(0xbeef)), + }), + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_repeated(10, 10) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().range_from(10..); + + // Adjust the FDE's augmentation to be relative to the function. + fde.augmentation.as_mut().unwrap().lsda = Some(Pointer::Direct(0xfeed_face + 0xbeef)); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_cie_personality_function_relative_bad_context() { + let instrs = [1, 2, 3, 4]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let aug_len = Label::new(); + let aug_start = Label::new(); + let aug_end = Label::new(); + + let section = Section::with_endian(Endian::Little) + // Length + .L32(&length) + .mark(&start) + // CIE ID + .L32(0) + // Version + .D8(1) + // Augmentation + .append_bytes(b"zP\0") + // Code alignment factor + .uleb(1) + // Data alignment factor + .sleb(1) + // Return address register + .uleb(1) + // Augmentation data length. This is a uleb, be we rely on the value + // being less than 2^7 and therefore a valid uleb (can't use Label + // with uleb). + .D8(&aug_len) + .mark(&aug_start) + // Augmentation data. Personality encoding and then encoded pointer. + .D8(constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_uleb128.0) + .uleb(1) + .mark(&aug_end) + // Initial instructions + .append_bytes(&instrs) + .mark(&end); + + length.set_const((&end - &start) as u64); + aug_len.set_const((&aug_end - &aug_start) as u64); + + let section = section.get_contents().unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + + let bases = BaseAddresses::default(); + let mut iter = section.entries(&bases); + assert_eq!(iter.next(), Err(Error::FuncRelativePointerInBadContext)); + } + + #[test] + fn register_rule_map_eq() { + // Different order, but still equal. + let map1: RegisterRuleMap<usize> = [ + (Register(0), RegisterRule::SameValue), + (Register(3), RegisterRule::Offset(1)), + ] + .iter() + .collect(); + let map2: RegisterRuleMap<usize> = [ + (Register(3), RegisterRule::Offset(1)), + (Register(0), RegisterRule::SameValue), + ] + .iter() + .collect(); + assert_eq!(map1, map2); + assert_eq!(map2, map1); + + // Not equal. + let map3: RegisterRuleMap<usize> = [ + (Register(0), RegisterRule::SameValue), + (Register(2), RegisterRule::Offset(1)), + ] + .iter() + .collect(); + let map4: RegisterRuleMap<usize> = [ + (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::<usize>::default(); + map5.set(Register(0), RegisterRule::SameValue).unwrap(); + map5.set(Register(0), RegisterRule::Undefined).unwrap(); + let map6 = RegisterRuleMap::<usize>::default(); + assert_eq!(map5, map6); + assert_eq!(map6, map5); + } + + #[test] + fn iter_register_rules() { + let row = UnwindTableRow::<usize> { + registers: [ + (Register(0), RegisterRule::SameValue), + (Register(1), RegisterRule::Offset(1)), + (Register(2), RegisterRule::ValOffset(2)), + ] + .iter() + .collect(), + ..Default::default() + }; + + let mut found0 = false; + let mut found1 = false; + let mut found2 = false; + + for &(register, ref rule) in row.registers() { + match register.0 { + 0 => { + assert!(!found0); + found0 = true; + assert_eq!(*rule, RegisterRule::SameValue); + } + 1 => { + assert!(!found1); + found1 = true; + assert_eq!(*rule, RegisterRule::Offset(1)); + } + 2 => { + assert!(!found2); + found2 = true; + assert_eq!(*rule, RegisterRule::ValOffset(2)); + } + x => panic!("Unexpected register rule: ({}, {:?})", x, rule), + } + } + + assert!(found0); + assert!(found1); + assert!(found2); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn size_of_unwind_ctx() { + use core::mem; + let size = mem::size_of::<UnwindContext<usize>>(); + 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<usize>>(); + let max_size = 6152; + if size > max_size { + assert_eq!(size, max_size); + } + } + + #[test] + fn test_parse_pointer_encoding_ok() { + use crate::endianity::NativeEndian; + let expected = constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_pcrel; + let input = [expected.0, 1, 2, 3, 4]; + let input = &mut EndianSlice::new(&input, NativeEndian); + assert_eq!(parse_pointer_encoding(input), Ok(expected)); + assert_eq!(*input, EndianSlice::new(&[1, 2, 3, 4], NativeEndian)); + } + + #[test] + fn test_parse_pointer_encoding_bad_encoding() { + use crate::endianity::NativeEndian; + let expected = + constants::DwEhPe((constants::DW_EH_PE_sdata8.0 + 1) | constants::DW_EH_PE_pcrel.0); + let input = [expected.0, 1, 2, 3, 4]; + let input = &mut EndianSlice::new(&input, NativeEndian); + assert_eq!( + Err(Error::UnknownPointerEncoding(expected)), + parse_pointer_encoding(input) + ); + } + + #[test] + fn test_parse_encoded_pointer_absptr() { + let encoding = constants::DW_EH_PE_absptr; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0xf00d_f00d) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0xf00d_f00d)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_pcrel() { + let encoding = constants::DW_EH_PE_pcrel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .append_repeated(0, 0x10) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input.range_from(0x10..); + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_eh_frame(0x100).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x111)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_pcrel_undefined() { + let encoding = constants::DW_EH_PE_pcrel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::PcRelativePointerButSectionBaseIsUndefined) + ); + } + + #[test] + fn test_parse_encoded_pointer_textrel() { + let encoding = constants::DW_EH_PE_textrel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_text(0x10).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x11)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_textrel_undefined() { + let encoding = constants::DW_EH_PE_textrel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::TextRelativePointerButTextBaseIsUndefined) + ); + } + + #[test] + fn test_parse_encoded_pointer_datarel() { + let encoding = constants::DW_EH_PE_datarel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_got(0x10).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x11)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_datarel_undefined() { + let encoding = constants::DW_EH_PE_datarel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::DataRelativePointerButDataBaseIsUndefined) + ); + } + + #[test] + fn test_parse_encoded_pointer_funcrel() { + let encoding = constants::DW_EH_PE_funcrel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: Some(0x10), + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x11)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_funcrel_undefined() { + let encoding = constants::DW_EH_PE_funcrel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::FuncRelativePointerInBadContext) + ); + } + + #[test] + fn test_parse_encoded_pointer_uleb128() { + let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_uleb128; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .uleb(0x12_3456) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x12_3456)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_udata2() { + let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata2; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L16(0x1234) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1234)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_udata4() { + let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata4; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1234_5678) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1234_5678)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_udata8() { + let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata8; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L64(0x1234_5678_1234_5678) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1234_5678_1234_5678)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sleb128() { + let encoding = constants::DW_EH_PE_textrel | constants::DW_EH_PE_sleb128; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .sleb(-0x1111) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_text(0x1111_1111).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1111_0000)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sdata2() { + let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata2; + let expected_rest = [1, 2, 3, 4]; + let expected = 0x111_i16; + + let input = Section::with_endian(Endian::Little) + .L16(expected as u16) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(expected as u64)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sdata4() { + let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata4; + let expected_rest = [1, 2, 3, 4]; + let expected = 0x111_1111_i32; + + let input = Section::with_endian(Endian::Little) + .L32(expected as u32) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(expected as u64)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sdata8() { + let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata8; + let expected_rest = [1, 2, 3, 4]; + let expected = -0x11_1111_1222_2222_i64; + + let input = Section::with_endian(Endian::Little) + .L64(expected as u64) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(expected as u64)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_omit() { + let encoding = constants::DW_EH_PE_omit; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::CannotParseOmitPointerEncoding) + ); + assert_eq!(rest, input); + } + + #[test] + fn test_parse_encoded_pointer_bad_encoding() { + let encoding = constants::DwEhPe(constants::DW_EH_PE_sdata8.0 + 1); + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::UnknownPointerEncoding(encoding)) + ); + } + + #[test] + fn test_parse_encoded_pointer_aligned() { + // FIXME: support this encoding! + + let encoding = constants::DW_EH_PE_aligned; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::UnsupportedPointerEncoding) + ); + } + + #[test] + fn test_parse_encoded_pointer_indirect() { + let expected_rest = [1, 2, 3, 4]; + let encoding = constants::DW_EH_PE_indirect; + + let input = Section::with_endian(Endian::Little) + .L32(0x1234_5678) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Indirect(0x1234_5678)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_unwind_context_reuse() { + fn unwind_one(ctx: &mut UnwindContext<usize>, data: &[u8]) { + let debug_frame = DebugFrame::new(data, NativeEndian); + let bases = Default::default(); + let result = debug_frame.unwind_info_for_address( + &bases, + ctx, + 0xbadb_ad99, + DebugFrame::cie_from_offset, + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress); + } + + // Use the same context for two different data lifetimes. + let mut ctx: UnwindContext<usize> = UnwindContext::new(); + { + let data1 = vec![]; + unwind_one(&mut ctx, &data1); + } + { + let data2 = vec![]; + unwind_one(&mut ctx, &data2); + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/dwarf.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/dwarf.rs new file mode 100644 index 0000000..efb37b6 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/dwarf.rs
@@ -0,0 +1,1724 @@ +use alloc::string::String; +use alloc::sync::Arc; + +use crate::common::{ + DebugAddrBase, DebugAddrIndex, DebugInfoOffset, DebugLineStrOffset, DebugLocListsBase, + DebugLocListsIndex, DebugMacinfoOffset, DebugRngListsBase, DebugRngListsIndex, DebugStrOffset, + DebugStrOffsetsBase, DebugStrOffsetsIndex, DebugTypeSignature, DebugTypesOffset, DwarfFileType, + DwoId, Encoding, LocationListsOffset, RangeListsOffset, RawRangeListsOffset, SectionId, + UnitSectionOffset, +}; +use crate::read::{ + Abbreviations, AbbreviationsCache, AbbreviationsCacheStrategy, AttributeValue, DebugAbbrev, + DebugAddr, DebugAranges, DebugCuIndex, DebugInfo, DebugInfoUnitHeadersIter, DebugLine, + DebugLineStr, DebugLoc, DebugLocLists, DebugMacinfo, DebugMacro, DebugRanges, DebugRngLists, + DebugStr, DebugStrOffsets, DebugTuIndex, DebugTypes, DebugTypesUnitHeadersIter, + DebuggingInformationEntry, EntriesCursor, EntriesRaw, EntriesTree, Error, + IncompleteLineProgram, IndexSectionId, LocListIter, LocationLists, MacroIter, Range, + RangeLists, RawLocListIter, RawRngListIter, Reader, ReaderOffset, ReaderOffsetId, Result, + RngListIter, Section, UnitHeader, UnitIndex, UnitIndexSectionIterator, UnitOffset, UnitType, +}; +use crate::{constants, DebugMacroOffset}; + +/// All of the commonly used DWARF sections. +/// +/// This is useful for storing sections when `T` does not implement `Reader`. +/// It can be used to create a `Dwarf` that references the data in `self`. +/// If `T` does implement `Reader`, then use `Dwarf` directly. +/// +/// ## Example Usage +/// +/// It can be useful to load DWARF sections into owned data structures, +/// such as `Vec`. However, we do not implement the `Reader` trait +/// for `Vec`, because it would be very inefficient, but this trait +/// is required for all of the methods that parse the DWARF data. +/// So we first load the DWARF sections into `Vec`s, and then use +/// `borrow` to create `Reader`s that reference the data. +/// +/// ```rust,no_run +/// # fn example() -> Result<(), gimli::Error> { +/// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; +/// // Read the DWARF sections into `Vec`s with whatever object loader you're using. +/// let dwarf_sections: gimli::DwarfSections<Vec<u8>> = gimli::DwarfSections::load(loader)?; +/// // Create references to the DWARF sections. +/// let dwarf: gimli::Dwarf<_> = dwarf_sections.borrow(|section| { +/// gimli::EndianSlice::new(§ion, gimli::LittleEndian) +/// }); +/// # unreachable!() +/// # } +/// ``` +#[derive(Debug, Default)] +pub struct DwarfSections<T> { + /// The `.debug_abbrev` section. + pub debug_abbrev: DebugAbbrev<T>, + /// The `.debug_addr` section. + pub debug_addr: DebugAddr<T>, + /// The `.debug_aranges` section. + pub debug_aranges: DebugAranges<T>, + /// The `.debug_info` section. + pub debug_info: DebugInfo<T>, + /// The `.debug_line` section. + pub debug_line: DebugLine<T>, + /// The `.debug_line_str` section. + pub debug_line_str: DebugLineStr<T>, + /// The `.debug_macinfo` section. + pub debug_macinfo: DebugMacinfo<T>, + /// The `.debug_macro` section. + pub debug_macro: DebugMacro<T>, + /// The `.debug_str` section. + pub debug_str: DebugStr<T>, + /// The `.debug_str_offsets` section. + pub debug_str_offsets: DebugStrOffsets<T>, + /// The `.debug_types` section. + pub debug_types: DebugTypes<T>, + /// The `.debug_loc` section. + pub debug_loc: DebugLoc<T>, + /// The `.debug_loclists` section. + pub debug_loclists: DebugLocLists<T>, + /// The `.debug_ranges` section. + pub debug_ranges: DebugRanges<T>, + /// The `.debug_rnglists` section. + pub debug_rnglists: DebugRngLists<T>, +} + +impl<T> DwarfSections<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. + pub fn load<F, E>(mut section: F) -> core::result::Result<Self, E> + where + F: FnMut(SectionId) -> core::result::Result<T, E>, + { + Ok(DwarfSections { + // Section types are inferred. + debug_abbrev: Section::load(&mut section)?, + debug_addr: Section::load(&mut section)?, + debug_aranges: Section::load(&mut section)?, + debug_info: Section::load(&mut section)?, + debug_line: Section::load(&mut section)?, + debug_line_str: Section::load(&mut section)?, + debug_macinfo: Section::load(&mut section)?, + debug_macro: Section::load(&mut section)?, + debug_str: Section::load(&mut section)?, + debug_str_offsets: Section::load(&mut section)?, + debug_types: Section::load(&mut section)?, + debug_loc: Section::load(&mut section)?, + debug_loclists: Section::load(&mut section)?, + debug_ranges: Section::load(&mut section)?, + debug_rnglists: Section::load(&mut section)?, + }) + } + + /// Create a `Dwarf` structure that references the data in `self`. + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> Dwarf<R> + where + F: FnMut(&'a T) -> R, + { + Dwarf::from_sections(DwarfSections { + debug_abbrev: self.debug_abbrev.borrow(&mut borrow), + debug_addr: self.debug_addr.borrow(&mut borrow), + debug_aranges: self.debug_aranges.borrow(&mut borrow), + debug_info: self.debug_info.borrow(&mut borrow), + debug_line: self.debug_line.borrow(&mut borrow), + debug_line_str: self.debug_line_str.borrow(&mut borrow), + debug_macinfo: self.debug_macinfo.borrow(&mut borrow), + debug_macro: self.debug_macro.borrow(&mut borrow), + debug_str: self.debug_str.borrow(&mut borrow), + debug_str_offsets: self.debug_str_offsets.borrow(&mut borrow), + debug_types: self.debug_types.borrow(&mut borrow), + debug_loc: self.debug_loc.borrow(&mut borrow), + debug_loclists: self.debug_loclists.borrow(&mut borrow), + debug_ranges: self.debug_ranges.borrow(&mut borrow), + debug_rnglists: self.debug_rnglists.borrow(&mut borrow), + }) + } + + /// Create a `Dwarf` structure that references the data in `self` and `sup`. + /// + /// This is like `borrow`, but also includes the supplementary object file. + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # fn example() -> Result<(), gimli::Error> { + /// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; + /// # let sup_loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; + /// // Read the DWARF sections into `Vec`s with whatever object loader you're using. + /// let dwarf_sections: gimli::DwarfSections<Vec<u8>> = gimli::DwarfSections::load(loader)?; + /// let dwarf_sup_sections: gimli::DwarfSections<Vec<u8>> = gimli::DwarfSections::load(sup_loader)?; + /// // Create references to the DWARF sections. + /// let dwarf = dwarf_sections.borrow_with_sup(&dwarf_sup_sections, |section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// # unreachable!() + /// # } + /// ``` + pub fn borrow_with_sup<'a, F, R>(&'a self, sup: &'a Self, mut borrow: F) -> Dwarf<R> + where + F: FnMut(&'a T) -> R, + { + let mut dwarf = self.borrow(&mut borrow); + dwarf.set_sup(sup.borrow(&mut borrow)); + dwarf + } +} + +/// All of the commonly used DWARF sections, and other common information. +#[derive(Debug, Default)] +pub struct Dwarf<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_macinfo` section. + pub debug_macinfo: DebugMacinfo<R>, + + /// The `.debug_macro` section. + pub debug_macro: DebugMacro<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>>>, + + /// A cache of previously parsed abbreviations for units in this file. + pub abbreviations_cache: AbbreviationsCache, +} + +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. + /// + /// After loading, the user should set the `file_type` field and + /// call `load_sup` if required. + pub fn load<F, E>(section: F) -> core::result::Result<Self, E> + where + F: FnMut(SectionId) -> core::result::Result<T, E>, + { + let sections = DwarfSections::load(section)?; + Ok(Self::from_sections(sections)) + } + + /// Load the DWARF sections from the supplementary object file. + /// + /// `section` operates the same as for `load`. + /// + /// Sets `self.sup`, replacing any previous value. + pub fn load_sup<F, E>(&mut self, section: F) -> core::result::Result<(), E> + where + F: FnMut(SectionId) -> core::result::Result<T, E>, + { + self.set_sup(Self::load(section)?); + Ok(()) + } + + /// Create a `Dwarf` structure from the given sections. + /// + /// The caller should set the `file_type` and `sup` fields if required. + fn from_sections(sections: DwarfSections<T>) -> Self { + Dwarf { + debug_abbrev: sections.debug_abbrev, + debug_addr: sections.debug_addr, + debug_aranges: sections.debug_aranges, + debug_info: sections.debug_info, + debug_line: sections.debug_line, + debug_line_str: sections.debug_line_str, + debug_macinfo: sections.debug_macinfo, + debug_macro: sections.debug_macro, + debug_str: sections.debug_str, + debug_str_offsets: sections.debug_str_offsets, + debug_types: sections.debug_types, + locations: LocationLists::new(sections.debug_loc, sections.debug_loclists), + ranges: RangeLists::new(sections.debug_ranges, sections.debug_rnglists), + file_type: DwarfFileType::Main, + sup: None, + abbreviations_cache: AbbreviationsCache::new(), + } + } + + /// Create a `Dwarf` structure that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// It can be useful to load DWARF sections into owned data structures, + /// such as `Vec`. However, we do not implement the `Reader` trait + /// for `Vec`, because it would be very inefficient, but this trait + /// is required for all of the methods that parse the DWARF data. + /// So we first load the DWARF sections into `Vec`s, and then use + /// `borrow` to create `Reader`s that reference the data. + /// + /// ```rust,no_run + /// # fn example() -> Result<(), gimli::Error> { + /// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; + /// # let sup_loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; + /// // Read the DWARF sections into `Vec`s with whatever object loader you're using. + /// let mut owned_dwarf: gimli::Dwarf<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(§ion, gimli::LittleEndian) + /// }); + /// # unreachable!() + /// # } + /// ``` + #[deprecated(note = "use `DwarfSections::borrow` instead")] + 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_macinfo: self.debug_macinfo.borrow(&mut borrow), + debug_macro: self.debug_macro.borrow(&mut borrow), + debug_str: self.debug_str.borrow(&mut borrow), + debug_str_offsets: self.debug_str_offsets.borrow(&mut borrow), + debug_types: self.debug_types.borrow(&mut borrow), + locations: self.locations.borrow(&mut borrow), + ranges: self.ranges.borrow(&mut borrow), + file_type: self.file_type, + sup: self.sup().map(|sup| Arc::new(sup.borrow(borrow))), + abbreviations_cache: AbbreviationsCache::new(), + } + } + + /// Store the DWARF sections for the supplementary object file. + pub fn set_sup(&mut self, sup: Dwarf<T>) { + self.sup = Some(Arc::new(sup)); + } + + /// Return a reference to the DWARF sections for the supplementary object file. + pub fn sup(&self) -> Option<&Dwarf<T>> { + self.sup.as_ref().map(Arc::as_ref) + } +} + +impl<R: Reader> Dwarf<R> { + /// Parse abbreviations and store them in the cache. + /// + /// This will iterate over the units in `self.debug_info` to determine the + /// abbreviations offsets. + /// + /// Errors during parsing abbreviations are also stored in the cache. + /// Errors during iterating over the units are ignored. + pub fn populate_abbreviations_cache(&mut self, strategy: AbbreviationsCacheStrategy) { + self.abbreviations_cache + .populate(strategy, &self.debug_abbrev, self.debug_info.units()); + } + + /// Iterate the unit headers in the `.debug_info` section. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + #[inline] + pub fn units(&self) -> DebugInfoUnitHeadersIter<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. + #[inline] + pub fn abbreviations(&self, unit: &UnitHeader<R>) -> Result<Arc<Abbreviations>> { + self.abbreviations_cache + .get(&self.debug_abbrev, unit.debug_abbrev_offset()) + } + + /// Return the string offset at the given index. + #[inline] + pub fn string_offset( + &self, + unit: &Unit<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 the string at the given offset in the `.debug_str` + /// in the supplementary object file. + #[inline] + pub fn sup_string(&self, offset: DebugStrOffset<R::Offset>) -> Result<R> { + if let Some(sup) = self.sup() { + sup.debug_str.get_str(offset) + } else { + Err(Error::ExpectedStringAttributeValue) + } + } + + /// Return an attribute value as a string slice. + /// + /// If the attribute value is one of: + /// + /// - an inline `DW_FORM_string` string + /// - a `DW_FORM_strp` reference to an offset into the `.debug_str` section + /// - a `DW_FORM_strp_sup` reference to an offset into a supplementary + /// object file + /// - a `DW_FORM_line_strp` reference to an offset into the `.debug_line_str` + /// section + /// - a `DW_FORM_strx` index into the `.debug_str_offsets` entries for the unit + /// + /// then return the attribute's string value. Returns an error if the attribute + /// value does not have a string form, or if a string form has an invalid value. + pub fn attr_string(&self, unit: &Unit<R>, attr: AttributeValue<R>) -> Result<R> { + match attr { + AttributeValue::String(string) => Ok(string), + AttributeValue::DebugStrRef(offset) => self.string(offset), + AttributeValue::DebugStrRefSup(offset) => self.sup_string(offset), + AttributeValue::DebugLineStrRef(offset) => self.line_string(offset), + AttributeValue::DebugStrOffsetsIndex(index) => { + let offset = self.string_offset(unit, index)?; + self.string(offset) + } + _ => Err(Error::ExpectedStringAttributeValue), + } + } + + /// Return the address at the given index. + pub fn address(&self, unit: &Unit<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() + } + + /// Return a fallible iterator over the macro information from `.debug_macinfo` for the given offset. + pub fn macinfo(&self, offset: DebugMacinfoOffset<R::Offset>) -> Result<MacroIter<R>> { + self.debug_macinfo.get_macinfo(offset) + } + + /// Return a fallible iterator over the macro information from `.debug_macro` for the given offset. + pub fn macros(&self, offset: DebugMacroOffset<R::Offset>) -> Result<MacroIter<R>> { + self.debug_macro.get_macros(offset) + } +} + +impl<R: Clone> Dwarf<R> { + /// Assuming `self` was loaded from a .dwo, take the appropriate + /// sections from `parent` (which contains the skeleton unit for this + /// dwo) such as `.debug_addr` and merge them into this `Dwarf`. + pub fn make_dwo(&mut self, parent: &Dwarf<R>) { + self.file_type = DwarfFileType::Dwo; + // These sections are always taken from the parent file and not the dwo. + self.debug_addr = parent.debug_addr.clone(); + // .debug_rnglists comes from the DWO, .debug_ranges comes from the + // parent file. + self.ranges + .set_debug_ranges(parent.ranges.debug_ranges().clone()); + self.sup.clone_from(&parent.sup); + } +} + +/// The sections from a `.dwp` file. +/// +/// This is useful for storing sections when `T` does not implement `Reader`. +/// It can be used to create a `DwarfPackage` that references the data in `self`. +/// If `T` does implement `Reader`, then use `DwarfPackage` directly. +/// +/// ## Example Usage +/// +/// It can be useful to load DWARF sections into owned data structures, +/// such as `Vec`. However, we do not implement the `Reader` trait +/// for `Vec`, because it would be very inefficient, but this trait +/// is required for all of the methods that parse the DWARF data. +/// So we first load the DWARF sections into `Vec`s, and then use +/// `borrow` to create `Reader`s that reference the data. +/// +/// ```rust,no_run +/// # fn example() -> Result<(), gimli::Error> { +/// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; +/// // Read the DWARF sections into `Vec`s with whatever object loader you're using. +/// let dwp_sections: gimli::DwarfPackageSections<Vec<u8>> = gimli::DwarfPackageSections::load(loader)?; +/// // Create references to the DWARF sections. +/// let dwp: gimli::DwarfPackage<_> = dwp_sections.borrow( +/// |section| gimli::EndianSlice::new(§ion, gimli::LittleEndian), +/// gimli::EndianSlice::new(&[], gimli::LittleEndian), +/// )?; +/// # unreachable!() +/// # } +/// ``` +#[derive(Debug, Default)] +pub struct DwarfPackageSections<T> { + /// The `.debug_cu_index` section. + pub cu_index: DebugCuIndex<T>, + /// The `.debug_tu_index` section. + pub tu_index: DebugTuIndex<T>, + /// The `.debug_abbrev.dwo` section. + pub debug_abbrev: DebugAbbrev<T>, + /// The `.debug_info.dwo` section. + pub debug_info: DebugInfo<T>, + /// The `.debug_line.dwo` section. + pub debug_line: DebugLine<T>, + /// The `.debug_str.dwo` section. + pub debug_str: DebugStr<T>, + /// The `.debug_str_offsets.dwo` section. + pub debug_str_offsets: DebugStrOffsets<T>, + /// The `.debug_loc.dwo` section. + /// + /// Only present when using GNU split-dwarf extension to DWARF 4. + pub debug_loc: DebugLoc<T>, + /// The `.debug_loclists.dwo` section. + pub debug_loclists: DebugLocLists<T>, + /// The `.debug_rnglists.dwo` section. + pub debug_rnglists: DebugRngLists<T>, + /// The `.debug_types.dwo` section. + /// + /// Only present when using GNU split-dwarf extension to DWARF 4. + pub debug_types: DebugTypes<T>, +} + +impl<T> DwarfPackageSections<T> { + /// 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) -> core::result::Result<Self, E> + where + F: FnMut(SectionId) -> core::result::Result<T, E>, + E: From<Error>, + { + Ok(DwarfPackageSections { + // Section types are inferred. + cu_index: Section::load(&mut section)?, + tu_index: Section::load(&mut section)?, + debug_abbrev: Section::load(&mut section)?, + debug_info: Section::load(&mut section)?, + debug_line: Section::load(&mut section)?, + debug_str: Section::load(&mut section)?, + debug_str_offsets: Section::load(&mut section)?, + debug_loc: Section::load(&mut section)?, + debug_loclists: Section::load(&mut section)?, + debug_rnglists: Section::load(&mut section)?, + debug_types: Section::load(&mut section)?, + }) + } + + /// Create a `DwarfPackage` structure that references the data in `self`. + pub fn borrow<'a, F, R>(&'a self, mut borrow: F, empty: R) -> Result<DwarfPackage<R>> + where + F: FnMut(&'a T) -> R, + R: Reader, + { + DwarfPackage::from_sections( + DwarfPackageSections { + cu_index: self.cu_index.borrow(&mut borrow), + tu_index: self.tu_index.borrow(&mut borrow), + debug_abbrev: self.debug_abbrev.borrow(&mut borrow), + debug_info: self.debug_info.borrow(&mut borrow), + debug_line: self.debug_line.borrow(&mut borrow), + debug_str: self.debug_str.borrow(&mut borrow), + debug_str_offsets: self.debug_str_offsets.borrow(&mut borrow), + debug_loc: self.debug_loc.borrow(&mut borrow), + debug_loclists: self.debug_loclists.borrow(&mut borrow), + debug_rnglists: self.debug_rnglists.borrow(&mut borrow), + debug_types: self.debug_types.borrow(&mut borrow), + }, + empty, + ) + } +} + +/// The sections from a `.dwp` file, with parsed indices. +#[derive(Debug)] +pub struct DwarfPackage<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>(section: F, empty: R) -> core::result::Result<Self, E> + where + F: FnMut(SectionId) -> core::result::Result<R, E>, + E: From<Error>, + { + let sections = DwarfPackageSections::load(section)?; + Ok(Self::from_sections(sections, empty)?) + } + + /// Create a `DwarfPackage` structure from the given sections. + fn from_sections(sections: DwarfPackageSections<R>, empty: R) -> Result<Self> { + Ok(DwarfPackage { + cu_index: sections.cu_index.index()?, + tu_index: sections.tu_index.index()?, + debug_abbrev: sections.debug_abbrev, + debug_info: sections.debug_info, + debug_line: sections.debug_line, + debug_str: sections.debug_str, + debug_str_offsets: sections.debug_str_offsets, + debug_loc: sections.debug_loc, + debug_loclists: sections.debug_loclists, + debug_rnglists: sections.debug_rnglists, + debug_types: sections.debug_types, + empty, + }) + } + + /// Find the compilation unit with the given DWO identifier and return its section + /// contributions. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # fn example<R: gimli::Reader>( + /// # dwarf: &gimli::Dwarf<R>, + /// # dwp: &gimli::DwarfPackage<R>, + /// # dwo_id: gimli::DwoId, + /// # ) -> Result<(), gimli::Error> { + /// if let Some(dwo) = dwp.find_cu(dwo_id, dwarf)? { + /// let dwo_header = dwo.units().next()?.expect("DWO should have one unit"); + /// let dwo_unit = dwo.unit(dwo_header)?; + /// // Do something with `dwo_unit`. + /// } + /// # unreachable!() + /// # } + pub fn find_cu(&self, id: DwoId, parent: &Dwarf<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 { + IndexSectionId::DebugAbbrev => { + abbrev_offset = section.offset; + abbrev_size = section.size; + } + IndexSectionId::DebugInfo => { + info_offset = section.offset; + info_size = section.size; + } + IndexSectionId::DebugLine => { + line_offset = section.offset; + line_size = section.size; + } + IndexSectionId::DebugLoc => { + loc_offset = section.offset; + loc_size = section.size; + } + IndexSectionId::DebugLocLists => { + loclists_offset = section.offset; + loclists_size = section.size; + } + IndexSectionId::DebugStrOffsets => { + str_offsets_offset = section.offset; + str_offsets_size = section.size; + } + IndexSectionId::DebugRngLists => { + rnglists_offset = section.offset; + rnglists_size = section.size; + } + IndexSectionId::DebugTypes => { + types_offset = section.offset; + types_size = section.size; + } + IndexSectionId::DebugMacro | IndexSectionId::DebugMacinfo => { + // These are valid but we can't parse these yet. + } + } + } + + let debug_abbrev = self.debug_abbrev.dwp_range(abbrev_offset, abbrev_size)?; + let debug_info = self.debug_info.dwp_range(info_offset, info_size)?; + let debug_line = self.debug_line.dwp_range(line_offset, line_size)?; + let debug_loc = self.debug_loc.dwp_range(loc_offset, loc_size)?; + let debug_loclists = self + .debug_loclists + .dwp_range(loclists_offset, loclists_size)?; + let debug_str_offsets = self + .debug_str_offsets + .dwp_range(str_offsets_offset, str_offsets_size)?; + let debug_rnglists = self + .debug_rnglists + .dwp_range(rnglists_offset, rnglists_size)?; + let debug_types = self.debug_types.dwp_range(types_offset, types_size)?; + + let debug_str = self.debug_str.clone(); + + let debug_addr = parent.debug_addr.clone(); + let debug_ranges = parent.ranges.debug_ranges().clone(); + + let debug_aranges = self.empty.clone().into(); + let debug_line_str = self.empty.clone().into(); + let debug_macinfo = self.empty.clone().into(); + let debug_macro = self.empty.clone().into(); + + Ok(Dwarf { + debug_abbrev, + debug_addr, + debug_aranges, + debug_info, + debug_line, + debug_line_str, + debug_macinfo, + debug_macro, + debug_str, + debug_str_offsets, + debug_types, + locations: LocationLists::new(debug_loc, debug_loclists), + ranges: RangeLists::new(debug_ranges, debug_rnglists), + file_type: DwarfFileType::Dwo, + sup: parent.sup.clone(), + abbreviations_cache: AbbreviationsCache::new(), + }) + } +} + +/// All of the commonly used information for a unit in the `.debug_info` or `.debug_types` +/// sections. +#[derive(Debug)] +pub struct Unit<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: Arc<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 = dwarf.abbreviations(&header)?; + Self::new_with_abbreviations(dwarf, header, abbreviations) + } + + /// Construct a new `Unit` from the given unit header and abbreviations. + /// + /// The abbreviations for this call can be obtained using `dwarf.abbreviations(&header)`. + /// The caller may implement caching to reuse the `Abbreviations` across units with the + /// same `header.debug_abbrev_offset()` value. + #[inline] + pub fn new_with_abbreviations( + dwarf: &Dwarf<R>, + header: UnitHeader<R>, + abbreviations: Arc<Abbreviations>, + ) -> Result<Self> { + let mut unit = Unit { + abbreviations, + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase::default_for_encoding_and_file( + header.encoding(), + dwarf.file_type, + ), + // NB: Because the .debug_addr section never lives in a .dwo, we can assume its base is always 0 or provided. + addr_base: DebugAddrBase(R::Offset::from_u8(0)), + loclists_base: DebugLocListsBase::default_for_encoding_and_file( + header.encoding(), + dwarf.file_type, + ), + rnglists_base: DebugRngListsBase::default_for_encoding_and_file( + header.encoding(), + dwarf.file_type, + ), + line_program: None, + dwo_id: match header.type_() { + UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => Some(dwo_id), + _ => None, + }, + header, + }; + let mut name = None; + let mut comp_dir = None; + let mut line_program_offset = None; + let mut low_pc_attr = None; + + { + let mut cursor = unit.header.entries(&unit.abbreviations); + cursor.next_dfs()?; + let root = cursor.current().ok_or(Error::MissingUnitDie)?; + let mut attrs = root.attrs(); + while let Some(attr) = attrs.next()? { + match attr.name() { + constants::DW_AT_name => { + name = Some(attr.value()); + } + constants::DW_AT_comp_dir => { + comp_dir = Some(attr.value()); + } + constants::DW_AT_low_pc => { + low_pc_attr = Some(attr.value()); + } + constants::DW_AT_stmt_list => { + if let AttributeValue::DebugLineRef(offset) = attr.value() { + line_program_offset = Some(offset); + } + } + constants::DW_AT_str_offsets_base => { + if let AttributeValue::DebugStrOffsetsBase(base) = attr.value() { + unit.str_offsets_base = base; + } + } + constants::DW_AT_addr_base | constants::DW_AT_GNU_addr_base => { + if let AttributeValue::DebugAddrBase(base) = attr.value() { + unit.addr_base = base; + } + } + constants::DW_AT_loclists_base => { + if let AttributeValue::DebugLocListsBase(base) = attr.value() { + unit.loclists_base = base; + } + } + constants::DW_AT_rnglists_base | constants::DW_AT_GNU_ranges_base => { + if let AttributeValue::DebugRngListsBase(base) = attr.value() { + unit.rnglists_base = base; + } + } + constants::DW_AT_GNU_dwo_id => { + if unit.dwo_id.is_none() { + if let AttributeValue::DwoId(dwo_id) = attr.value() { + unit.dwo_id = Some(dwo_id); + } + } + } + _ => {} + } + } + } + + unit.name = match name { + Some(val) => dwarf.attr_string(&unit, val).ok(), + None => None, + }; + unit.comp_dir = match comp_dir { + Some(val) => dwarf.attr_string(&unit, val).ok(), + None => None, + }; + unit.line_program = match line_program_offset { + Some(offset) => Some(dwarf.debug_line.program( + offset, + unit.header.address_size(), + unit.comp_dir.clone(), + unit.name.clone(), + )?), + None => None, + }; + if let Some(low_pc_attr) = low_pc_attr { + if let Some(addr) = dwarf.attr_address(&unit, low_pc_attr)? { + unit.low_pc = addr; + } + } + Ok(unit) + } + + /// Return a reference to this unit and its associated `Dwarf`. + pub fn unit_ref<'a>(&'a self, dwarf: &'a Dwarf<R>) -> UnitRef<'a, R> { + UnitRef::new(dwarf, self) + } + + /// Return the encoding parameters for this unit. + #[inline] + pub fn encoding(&self) -> Encoding { + self.header.encoding() + } + + /// Read the `DebuggingInformationEntry` at the given offset. + pub fn entry( + &self, + offset: UnitOffset<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; + } + } + + /// Find the dwo name (if any) for this unit, automatically handling the differences + /// between the standardized DWARF 5 split DWARF format and the pre-DWARF 5 GNU + /// extension. + /// + /// The returned value is relative to this unit's `comp_dir`. + pub fn dwo_name(&self) -> Result<Option<AttributeValue<R>>> { + let mut entries = self.entries(); + entries.next_entry()?; + let entry = entries.current().ok_or(Error::MissingUnitDie)?; + if self.header.version() < 5 { + entry.attr_value(constants::DW_AT_GNU_dwo_name) + } else { + entry.attr_value(constants::DW_AT_dwo_name) + } + } +} + +/// A reference to a `Unit` and its associated `Dwarf`. +/// +/// These often need to be passed around together, so this struct makes that easier. +/// +/// It implements `Deref` to `Unit`, so you can use it as if it were a `Unit`. +/// It also implements methods that correspond to methods on `Dwarf` that take a `Unit`. +#[derive(Debug)] +pub struct UnitRef<'a, R: Reader> { + /// The `Dwarf` that contains the unit. + pub dwarf: &'a Dwarf<R>, + + /// The `Unit` being referenced. + pub unit: &'a Unit<R>, +} + +impl<'a, R: Reader> Clone for UnitRef<'a, R> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, R: Reader> Copy for UnitRef<'a, R> {} + +impl<'a, R: Reader> core::ops::Deref for UnitRef<'a, R> { + type Target = Unit<R>; + + fn deref(&self) -> &Self::Target { + self.unit + } +} + +impl<'a, R: Reader> UnitRef<'a, R> { + /// Construct a new `UnitRef` from a `Dwarf` and a `Unit`. + pub fn new(dwarf: &'a Dwarf<R>, unit: &'a Unit<R>) -> Self { + UnitRef { dwarf, unit } + } + + /// Return the string offset at the given index. + #[inline] + pub fn string_offset( + &self, + index: DebugStrOffsetsIndex<R::Offset>, + ) -> Result<DebugStrOffset<R::Offset>> { + self.dwarf.string_offset(self.unit, index) + } + + /// Return the string at the given offset in `.debug_str`. + #[inline] + pub fn string(&self, offset: DebugStrOffset<R::Offset>) -> Result<R> { + self.dwarf.string(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.dwarf.line_string(offset) + } + + /// Return the string at the given offset in the `.debug_str` + /// in the supplementary object file. + #[inline] + pub fn sup_string(&self, offset: DebugStrOffset<R::Offset>) -> Result<R> { + self.dwarf.sup_string(offset) + } + + /// Return an attribute value as a string slice. + /// + /// See [`Dwarf::attr_string`] for more information. + pub fn attr_string(&self, attr: AttributeValue<R>) -> Result<R> { + self.dwarf.attr_string(self.unit, attr) + } + + /// Return the address at the given index. + pub fn address(&self, index: DebugAddrIndex<R::Offset>) -> Result<u64> { + self.dwarf.address(self.unit, index) + } + + /// Try to return an attribute value as an address. + /// + /// See [`Dwarf::attr_address`] for more information. + pub fn attr_address(&self, attr: AttributeValue<R>) -> Result<Option<u64>> { + self.dwarf.attr_address(self.unit, attr) + } + + /// Return the range list offset for the given raw offset. + /// + /// This handles adding `DW_AT_GNU_ranges_base` if required. + pub fn ranges_offset_from_raw( + &self, + offset: RawRangeListsOffset<R::Offset>, + ) -> RangeListsOffset<R::Offset> { + self.dwarf.ranges_offset_from_raw(self.unit, offset) + } + + /// Return the range list offset at the given index. + pub fn ranges_offset( + &self, + index: DebugRngListsIndex<R::Offset>, + ) -> Result<RangeListsOffset<R::Offset>> { + self.dwarf.ranges_offset(self.unit, index) + } + + /// Iterate over the `RangeListEntry`s starting at the given offset. + pub fn ranges(&self, offset: RangeListsOffset<R::Offset>) -> Result<RngListIter<R>> { + self.dwarf.ranges(self.unit, offset) + } + + /// Iterate over the `RawRngListEntry`ies starting at the given offset. + pub fn raw_ranges(&self, offset: RangeListsOffset<R::Offset>) -> Result<RawRngListIter<R>> { + self.dwarf.raw_ranges(self.unit, offset) + } + + /// Try to return an attribute value as a range list offset. + /// + /// See [`Dwarf::attr_ranges_offset`] for more information. + pub fn attr_ranges_offset( + &self, + attr: AttributeValue<R>, + ) -> Result<Option<RangeListsOffset<R::Offset>>> { + self.dwarf.attr_ranges_offset(self.unit, attr) + } + + /// Try to return an attribute value as a range list entry iterator. + /// + /// See [`Dwarf::attr_ranges`] for more information. + pub fn attr_ranges(&self, attr: AttributeValue<R>) -> Result<Option<RngListIter<R>>> { + self.dwarf.attr_ranges(self.unit, attr) + } + + /// Return an iterator for the address ranges of a `DebuggingInformationEntry`. + /// + /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges`. + pub fn die_ranges(&self, entry: &DebuggingInformationEntry<'_, '_, R>) -> Result<RangeIter<R>> { + self.dwarf.die_ranges(self.unit, entry) + } + + /// Return an iterator for the address ranges of the `Unit`. + /// + /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges` of the + /// root `DebuggingInformationEntry`. + pub fn unit_ranges(&self) -> Result<RangeIter<R>> { + self.dwarf.unit_ranges(self.unit) + } + + /// Return the location list offset at the given index. + pub fn locations_offset( + &self, + index: DebugLocListsIndex<R::Offset>, + ) -> Result<LocationListsOffset<R::Offset>> { + self.dwarf.locations_offset(self.unit, index) + } + + /// Iterate over the `LocationListEntry`s starting at the given offset. + pub fn locations(&self, offset: LocationListsOffset<R::Offset>) -> Result<LocListIter<R>> { + self.dwarf.locations(self.unit, offset) + } + + /// Iterate over the raw `LocationListEntry`s starting at the given offset. + pub fn raw_locations( + &self, + offset: LocationListsOffset<R::Offset>, + ) -> Result<RawLocListIter<R>> { + self.dwarf.raw_locations(self.unit, offset) + } + + /// Try to return an attribute value as a location list offset. + /// + /// See [`Dwarf::attr_locations_offset`] for more information. + pub fn attr_locations_offset( + &self, + attr: AttributeValue<R>, + ) -> Result<Option<LocationListsOffset<R::Offset>>> { + self.dwarf.attr_locations_offset(self.unit, attr) + } + + /// Try to return an attribute value as a location list entry iterator. + /// + /// See [`Dwarf::attr_locations`] for more information. + pub fn attr_locations(&self, attr: AttributeValue<R>) -> Result<Option<LocListIter<R>>> { + self.dwarf.attr_locations(self.unit, attr) + } + + /// Try to return an iterator for the list of macros at the given `.debug_macinfo` offset. + pub fn macinfo(&self, offset: DebugMacinfoOffset<R::Offset>) -> Result<MacroIter<R>> { + self.dwarf.macinfo(offset) + } + + /// Try to return an iterator for the list of macros at the given `.debug_macro` offset. + pub fn macros(&self, offset: DebugMacroOffset<R::Offset>) -> Result<MacroIter<R>> { + self.dwarf.macros(offset) + } +} + +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 dwarf_sections = DwarfSections::load(|_| -> Result<_> { Ok(vec![1, 2]) }).unwrap(); + let sup_sections = DwarfSections::load(|_| -> Result<_> { Ok(vec![1, 2]) }).unwrap(); + let dwarf = dwarf_sections.borrow_with_sup(&sup_sections, |section| { + EndianSlice::new(section, LittleEndian) + }); + + match dwarf.debug_str.get_str(DebugStrOffset(1)) { + Ok(r) => panic!("Unexpected str {:?}", r), + Err(e) => { + assert_eq!( + dwarf.format_error(e), + "Hit the end of input before it was expected at .debug_str+0x1" + ); + } + } + match dwarf.sup().unwrap().debug_str.get_str(DebugStrOffset(1)) { + Ok(r) => panic!("Unexpected str {:?}", r), + Err(e) => { + assert_eq!( + dwarf.format_error(e), + "Hit the end of input before it was expected at .debug_str(sup)+0x1" + ); + } + } + assert_eq!(dwarf.format_error(Error::Io), Error::Io.description()); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_reader.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_reader.rs new file mode 100644 index 0000000..5e65b4d --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_reader.rs
@@ -0,0 +1,651 @@ +//! Defining custom `Reader`s quickly. + +use alloc::borrow::Cow; +use alloc::rc::Rc; +use alloc::string::String; +use alloc::sync::Arc; +use core::fmt::Debug; +use core::hash::{Hash, Hasher}; +use core::ops::{Deref, Index, Range, RangeFrom, RangeTo}; +use core::slice; +use core::str; +use stable_deref_trait::CloneStableDeref; + +use crate::endianity::Endianity; +use crate::read::{Error, Reader, ReaderOffsetId, Result}; + +/// A reference counted, non-thread-safe slice of bytes and associated +/// endianity. +/// +/// ``` +/// # #[cfg(feature = "std")] { +/// use std::rc::Rc; +/// +/// let buf = Rc::from(&[1, 2, 3, 4][..]); +/// let reader = gimli::EndianRcSlice::new(buf, gimli::NativeEndian); +/// # let _ = reader; +/// # } +/// ``` +pub type EndianRcSlice<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)] +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, +{ +} + +impl<Endian, T> Hash for EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + fn hash<H: Hasher>(&self, state: &mut H) { + // This must match the `PartialEq` implementation. + self.bytes().hash(state); + } +} + +// This is separated out from `EndianReader` so that we can avoid running afoul +// of borrowck. We need to `read_slice(&mut self, ...) -> &[u8]` and then call +// `self.endian.read_whatever` on the result. The problem is that the returned +// slice keeps the `&mut self` borrow active, so we wouldn't be able to access +// `self.endian`. Splitting the sub-range out from the endian lets us work +// around this, making it so that only the `self.range` borrow is held active, +// not all of `self`. +// +// This also serves to encapsulate the unsafe code concerning `CloneStableDeref`. +// The `bytes` member is held so that the bytes live long enough, and the +// `CloneStableDeref` ensures these bytes never move. The `ptr` and `len` +// members point inside `bytes`, and are updated during read operations. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct SubRange<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 usize; + let ptr = self.bytes().as_ptr() as usize; + debug_assert!(base_ptr <= ptr); + debug_assert!(ptr + self.bytes().len() <= base_ptr + base.bytes().len()); + ptr - base_ptr + } + + #[inline] + fn offset_id(&self) -> ReaderOffsetId { + ReaderOffsetId(self.bytes().as_ptr() as u64) + } + + #[inline] + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<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/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_slice.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_slice.rs new file mode 100644 index 0000000..0335142 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_slice.rs
@@ -0,0 +1,360 @@ +//! Working with byte slices that have an associated endianity. + +#[cfg(feature = "read")] +use alloc::borrow::Cow; +#[cfg(feature = "read")] +use alloc::string::String; +use core::fmt; +use core::ops::{Deref, Range, RangeFrom, RangeTo}; +use core::str; + +use crate::endianity::Endianity; +use crate::read::{Error, Reader, ReaderOffsetId, Result}; + +/// A `&[u8]` slice with endianity metadata. +/// +/// This implements the `Reader` trait, which is used for all reading of DWARF sections. +#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + slice: &'input [u8], + endian: Endian, +} + +impl<'input, Endian> EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + /// Construct a new `EndianSlice` with the given slice and endianity. + #[inline] + pub fn new(slice: &'input [u8], endian: Endian) -> EndianSlice<'input, Endian> { + EndianSlice { slice, endian } + } + + /// Return a reference to the raw slice. + #[inline] + #[doc(hidden)] + #[deprecated(note = "Method renamed to EndianSlice::slice; use that instead.")] + pub fn buf(&self) -> &'input [u8] { + self.slice + } + + /// Return a reference to the raw slice. + #[inline] + pub fn slice(&self) -> &'input [u8] { + self.slice + } + + /// Split the slice in two at the given index, resulting in the tuple where + /// the first item has range [0, idx), and the second has range [idx, + /// len). Panics if the index is out of bounds. + #[inline] + pub fn split_at( + &self, + idx: usize, + ) -> (EndianSlice<'input, Endian>, EndianSlice<'input, Endian>) { + (self.range_to(..idx), self.range_from(idx..)) + } + + /// Find the first occurrence of a byte in the slice, and return its index. + #[inline] + pub fn find(&self, byte: u8) -> Option<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 usize; + let ptr = self.slice.as_ptr() as usize; + debug_assert!(base_ptr <= ptr); + debug_assert!(ptr + self.slice.len() <= base_ptr + base.slice.len()); + ptr - base_ptr + } + + /// Converts the slice to a string using `str::from_utf8`. + /// + /// Returns an error if the slice contains invalid characters. + #[inline] + pub fn to_string(&self) -> Result<&'input str> { + str::from_utf8(self.slice).map_err(|_| Error::BadUtf8) + } + + /// Converts the slice to a string, including invalid characters, + /// using `String::from_utf8_lossy`. + #[cfg(feature = "read")] + #[inline] + pub fn to_string_lossy(&self) -> Cow<'input, str> { + String::from_utf8_lossy(self.slice) + } + + #[inline] + fn read_slice(&mut self, len: usize) -> Result<&'input [u8]> { + if self.slice.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + let val = &self.slice[..len]; + self.slice = &self.slice[len..]; + Ok(val) + } + } +} + +/// # Range Methods +/// +/// Unfortunately, `std::ops::Index` *must* return a reference, so we can't +/// implement `Index<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> Deref for EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.slice + } +} + +impl<'input, Endian: Endianity> fmt::Debug for EndianSlice<'input, Endian> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { + fmt.debug_tuple("EndianSlice") + .field(&self.endian) + .field(&DebugBytes(self.slice)) + .finish() + } +} + +struct DebugBytes<'input>(&'input [u8]); + +impl<'input> core::fmt::Debug for DebugBytes<'input> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { + let mut list = fmt.debug_list(); + list.entries(self.0.iter().take(8).copied().map(DebugByte)); + if self.0.len() > 8 { + list.entry(&DebugLen(self.0.len())); + } + list.finish() + } +} + +struct DebugByte(u8); + +impl fmt::Debug for DebugByte { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "0x{:02x}", self.0) + } +} + +struct DebugLen(usize); + +impl fmt::Debug for DebugLen { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "...; {}", self.0) + } +} + +impl<'input, Endian> Reader for EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + type Endian = Endian; + type Offset = usize; + + #[inline] + fn endian(&self) -> Endian { + self.endian + } + + #[inline] + fn len(&self) -> usize { + self.slice.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.slice.is_empty() + } + + #[inline] + fn empty(&mut self) { + self.slice = &[]; + } + + #[inline] + fn truncate(&mut self, len: usize) -> Result<()> { + if self.slice.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + self.slice = &self.slice[..len]; + Ok(()) + } + } + + #[inline] + fn offset_from(&self, base: &Self) -> usize { + self.offset_from(*base) + } + + #[inline] + fn offset_id(&self) -> ReaderOffsetId { + ReaderOffsetId(self.slice.as_ptr() as u64) + } + + #[inline] + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<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/chromium_crates_io/vendor/gimli-v0_32/src/read/index.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/index.rs new file mode 100644 index 0000000..bd8e74b --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/index.rs
@@ -0,0 +1,631 @@ +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<T> DebugCuIndex<T> { + /// Create a `DebugCuIndex` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfPackageSections::borrow`. + pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugCuIndex<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +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<T> DebugTuIndex<T> { + /// Create a `DebugTuIndex` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfPackageSections::borrow`. + pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTuIndex<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +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: [IndexSectionId; 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: 0, + section_count: 0, + unit_count: 0, + slot_count: 0, + hash_ids: input.clone(), + hash_rows: input.clone(), + sections: [IndexSectionId::DebugAbbrev; SECTION_COUNT_MAX as usize], + offsets: input.clone(), + sizes: input.clone(), + }); + } + + // GNU split-dwarf extension to DWARF 4 uses a 32-bit version, + // but DWARF 5 uses a 16-bit version followed by 16-bit padding. + let mut original_input = input.clone(); + let version; + if input.read_u32()? == 2 { + version = 2 + } else { + version = original_input.read_u16()?; + if version != 5 { + return Err(Error::UnknownVersion(version.into())); + } + } + + let section_count = input.read_u32()?; + let unit_count = input.read_u32()?; + let slot_count = input.read_u32()?; + if slot_count != 0 && (slot_count & (slot_count - 1) != 0 || slot_count <= unit_count) { + return Err(Error::InvalidIndexSlotCount); + } + + let hash_ids = input.split(R::Offset::from_u64(u64::from(slot_count) * 8)?)?; + let hash_rows = input.split(R::Offset::from_u64(u64::from(slot_count) * 4)?)?; + + let mut sections = [IndexSectionId::DebugAbbrev; SECTION_COUNT_MAX as usize]; + if section_count > SECTION_COUNT_MAX.into() { + return Err(Error::InvalidIndexSectionCount); + } + for i in 0..section_count { + let section = input.read_u32()?; + sections[i as usize] = if version == 2 { + match constants::DwSectV2(section) { + constants::DW_SECT_V2_INFO => IndexSectionId::DebugInfo, + constants::DW_SECT_V2_TYPES => IndexSectionId::DebugTypes, + constants::DW_SECT_V2_ABBREV => IndexSectionId::DebugAbbrev, + constants::DW_SECT_V2_LINE => IndexSectionId::DebugLine, + constants::DW_SECT_V2_LOC => IndexSectionId::DebugLoc, + constants::DW_SECT_V2_STR_OFFSETS => IndexSectionId::DebugStrOffsets, + constants::DW_SECT_V2_MACINFO => IndexSectionId::DebugMacinfo, + constants::DW_SECT_V2_MACRO => IndexSectionId::DebugMacro, + section => return Err(Error::UnknownIndexSectionV2(section)), + } + } else { + match constants::DwSect(section) { + constants::DW_SECT_INFO => IndexSectionId::DebugInfo, + constants::DW_SECT_ABBREV => IndexSectionId::DebugAbbrev, + constants::DW_SECT_LINE => IndexSectionId::DebugLine, + constants::DW_SECT_LOCLISTS => IndexSectionId::DebugLocLists, + constants::DW_SECT_STR_OFFSETS => IndexSectionId::DebugStrOffsets, + constants::DW_SECT_MACRO => IndexSectionId::DebugMacro, + constants::DW_SECT_RNGLISTS => IndexSectionId::DebugRngLists, + section => return Err(Error::UnknownIndexSection(section)), + } + }; + } + + let offsets = input.split(R::Offset::from_u64( + u64::from(unit_count) * u64::from(section_count) * 4, + )?)?; + let sizes = input.split(R::Offset::from_u64( + u64::from(unit_count) * u64::from(section_count) * 4, + )?)?; + + Ok(UnitIndex { + version, + section_count, + unit_count, + slot_count, + hash_ids, + hash_rows, + sections, + offsets, + sizes, + }) + } + + /// Find `id` in the index hash table, and return the row index. + /// + /// `id` may be a compilation unit ID if this index is from `.debug_cu_index`, + /// or a type signature if this index is from `.debug_tu_index`. + pub fn find(&self, id: u64) -> Option<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. + /// + /// Defaults to 0 for empty sections. + pub fn version(&self) -> u16 { + self.version + } + + /// Return the number of sections. + pub fn section_count(&self) -> u32 { + self.section_count + } + + /// Return the number of units. + pub fn unit_count(&self) -> u32 { + self.unit_count + } + + /// Return the number of slots. + pub fn slot_count(&self) -> u32 { + self.slot_count + } +} + +/// An iterator over the section offsets and sizes for a row in a `UnitIndex`. +#[derive(Debug, Clone)] +pub struct UnitIndexSectionIterator<'index, R: Reader> { + sections: slice::Iter<'index, IndexSectionId>, + offsets: R, + sizes: R, +} + +impl<'index, R: Reader> Iterator for UnitIndexSectionIterator<'index, R> { + type Item = UnitIndexSection; + + fn next(&mut self) -> Option<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: IndexSectionId, + /// The base offset of the unit's contribution to the section. + pub offset: u32, + /// The size of the unit's contribution to the section. + pub size: u32, +} + +/// Section kinds which are permitted in a `.dwp` index. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IndexSectionId { + /// The `.debug_abbrev.dwo` section. + DebugAbbrev, + /// The `.debug_info.dwo` section. + DebugInfo, + /// The `.debug_line.dwo` section. + DebugLine, + /// The `.debug_loc.dwo` section. + DebugLoc, + /// The `.debug_loclists.dwo` section. + DebugLocLists, + /// The `.debug_macinfo.dwo` section. + DebugMacinfo, + /// The `.debug_macro.dwo` section. + DebugMacro, + /// The `.debug_rnglists.dwo` section. + DebugRngLists, + /// The `.debug_str_offsets.dwo` section. + DebugStrOffsets, + /// The `.debug_types.dwo` section. + DebugTypes, +} + +impl IndexSectionId { + /// Returns the corresponding `SectionId`. + pub fn section_id(self) -> SectionId { + match self { + IndexSectionId::DebugAbbrev => SectionId::DebugAbbrev, + IndexSectionId::DebugInfo => SectionId::DebugInfo, + IndexSectionId::DebugLine => SectionId::DebugLine, + IndexSectionId::DebugLoc => SectionId::DebugLoc, + IndexSectionId::DebugLocLists => SectionId::DebugLocLists, + IndexSectionId::DebugMacro => SectionId::DebugMacro, + IndexSectionId::DebugMacinfo => SectionId::DebugMacinfo, + IndexSectionId::DebugRngLists => SectionId::DebugRngLists, + IndexSectionId::DebugStrOffsets => SectionId::DebugStrOffsets, + IndexSectionId::DebugTypes => SectionId::DebugTypes, + } + } + + /// Returns the ELF section name for this kind, when found in a .dwo or .dwp file. + pub fn dwo_name(self) -> &'static str { + self.section_id().dwo_name().unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::endianity::BigEndian; + use test_assembler::{Endian, Section}; + + #[test] + fn test_empty() { + let buf = EndianSlice::new(&[], BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version(), 0); + assert_eq!(index.unit_count(), 0); + assert_eq!(index.slot_count(), 0); + assert!(index.find(0).is_none()); + } + + #[test] + fn test_zero_slots() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D32(2).D32(0).D32(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version(), 2); + assert_eq!(index.unit_count(), 0); + assert_eq!(index.slot_count(), 0); + assert!(index.find(0).is_none()); + } + + #[test] + fn test_version_2() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D32(2).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version, 2); + } + + #[test] + fn test_version_5() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version, 5); + } + + #[test] + fn test_version_5_invalid() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D32(5).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + assert!(UnitIndex::parse(buf).is_err()); + } + + #[test] + fn test_version_2_sections() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D32(2).D32(8).D32(1).D32(2) + // Slots. + .D64(0).D64(0).D32(0).D32(0) + // Sections. + .D32(constants::DW_SECT_V2_INFO.0) + .D32(constants::DW_SECT_V2_TYPES.0) + .D32(constants::DW_SECT_V2_ABBREV.0) + .D32(constants::DW_SECT_V2_LINE.0) + .D32(constants::DW_SECT_V2_LOC.0) + .D32(constants::DW_SECT_V2_STR_OFFSETS.0) + .D32(constants::DW_SECT_V2_MACINFO.0) + .D32(constants::DW_SECT_V2_MACRO.0) + // Offsets. + .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17).D32(18) + // Sizes. + .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27).D32(28); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.section_count, 8); + assert_eq!( + index.sections, + [ + IndexSectionId::DebugInfo, + IndexSectionId::DebugTypes, + IndexSectionId::DebugAbbrev, + IndexSectionId::DebugLine, + IndexSectionId::DebugLoc, + IndexSectionId::DebugStrOffsets, + IndexSectionId::DebugMacinfo, + IndexSectionId::DebugMacro, + ] + ); + #[rustfmt::skip] + let expect = [ + UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 }, + UnitIndexSection { section: IndexSectionId::DebugTypes, offset: 12, size: 22 }, + UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 13, size: 23 }, + UnitIndexSection { section: IndexSectionId::DebugLine, offset: 14, size: 24 }, + UnitIndexSection { section: IndexSectionId::DebugLoc, offset: 15, size: 25 }, + UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 16, size: 26 }, + UnitIndexSection { section: IndexSectionId::DebugMacinfo, offset: 17, size: 27 }, + UnitIndexSection { section: IndexSectionId::DebugMacro, offset: 18, size: 28 }, + ]; + let mut sections = index.sections(1).unwrap(); + for section in &expect { + assert_eq!(*section, sections.next().unwrap()); + } + assert!(sections.next().is_none()); + } + + #[test] + fn test_version_5_sections() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(7).D32(1).D32(2) + // Slots. + .D64(0).D64(0).D32(0).D32(0) + // Sections. + .D32(constants::DW_SECT_INFO.0) + .D32(constants::DW_SECT_ABBREV.0) + .D32(constants::DW_SECT_LINE.0) + .D32(constants::DW_SECT_LOCLISTS.0) + .D32(constants::DW_SECT_STR_OFFSETS.0) + .D32(constants::DW_SECT_MACRO.0) + .D32(constants::DW_SECT_RNGLISTS.0) + // Offsets. + .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17) + // Sizes. + .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.section_count, 7); + assert_eq!( + index.sections[..7], + [ + IndexSectionId::DebugInfo, + IndexSectionId::DebugAbbrev, + IndexSectionId::DebugLine, + IndexSectionId::DebugLocLists, + IndexSectionId::DebugStrOffsets, + IndexSectionId::DebugMacro, + IndexSectionId::DebugRngLists, + ] + ); + #[rustfmt::skip] + let expect = [ + UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 }, + UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 12, size: 22 }, + UnitIndexSection { section: IndexSectionId::DebugLine, offset: 13, size: 23 }, + UnitIndexSection { section: IndexSectionId::DebugLocLists, offset: 14, size: 24 }, + UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 15, size: 25 }, + UnitIndexSection { section: IndexSectionId::DebugMacro, offset: 16, size: 26 }, + UnitIndexSection { section: IndexSectionId::DebugRngLists, offset: 17, size: 27 }, + ]; + let mut sections = index.sections(1).unwrap(); + for section in &expect { + assert_eq!(*section, sections.next().unwrap()); + } + assert!(sections.next().is_none()); + + assert!(index.sections(0).is_err()); + assert!(index.sections(2).is_err()); + } + + #[test] + fn test_hash() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(2).D32(3).D32(4) + // Slots. + .D64(0xffff_fff2_ffff_fff1) + .D64(0xffff_fff0_ffff_fff1) + .D64(0xffff_fff1_ffff_fff1) + .D64(0) + .D32(3).D32(1).D32(2).D32(0) + // Sections. + .D32(constants::DW_SECT_INFO.0) + .D32(constants::DW_SECT_ABBREV.0) + // Offsets. + .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0) + // Sizes. + .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version(), 5); + assert_eq!(index.slot_count(), 4); + assert_eq!(index.unit_count(), 3); + assert_eq!(index.section_count(), 2); + assert_eq!(index.find(0xffff_fff0_ffff_fff1), Some(1)); + assert_eq!(index.find(0xffff_fff1_ffff_fff1), Some(2)); + assert_eq!(index.find(0xffff_fff2_ffff_fff1), Some(3)); + assert_eq!(index.find(0xffff_fff3_ffff_fff1), None); + } + + #[test] + fn test_cu_index() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let cu_index = DebugCuIndex::new(&buf, BigEndian); + let index = cu_index.index().unwrap(); + assert_eq!(index.version, 5); + } + + #[test] + fn test_tu_index() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let tu_index = DebugTuIndex::new(&buf, BigEndian); + let index = tu_index.index().unwrap(); + assert_eq!(index.version, 5); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/line.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/line.rs new file mode 100644 index 0000000..d2b6c66 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/line.rs
@@ -0,0 +1,3185 @@ +use alloc::vec::Vec; +use core::num::{NonZeroU64, Wrapping}; + +use crate::common::{ + DebugLineOffset, DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsIndex, Encoding, Format, + LineEncoding, SectionId, +}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::{ + AttributeValue, EndianSlice, Error, Reader, ReaderAddress, ReaderOffset, Result, Section, +}; + +/// The `DebugLine` struct contains the source location to instruction mapping +/// found in the `.debug_line` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugLine<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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugLine, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_line_section_somehow = || &buf; + /// let debug_line = DebugLine::new(read_debug_line_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_line_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_line_section, endian)) + } +} + +impl<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. + /// + /// Used by `DwarfSections::borrow`. + 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, +{ + 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)? { + if self.row.tombstone { + // Perform any reset that was required for the tombstone row. + // Normally this is done when `next_row` is called again, but for + // tombstones we loop immediately. + self.row.reset(self.program.header()); + } else { + return Ok(Some((self.header(), &self.row))); + } + } + // Fall through, parse the next instruction, and see if that + // yields a row. + } + } + } + } +} + +/// Deprecated. `Opcode` has been renamed to `LineInstruction`. +#[deprecated(note = "Opcode has been renamed to LineInstruction, use that instead.")] +pub type Opcode<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)) + } + } + } + } + } + } +} + +/// 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`. + #[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 { + tombstone: bool, + address: 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 + tombstone: false, + address: 0, + op_index: Wrapping(0), + file: 1, + line: Wrapping(1), + column: 0, + // "determined by default_is_stmt in the line number program header" + is_stmt: header.line_encoding.default_is_stmt, + basic_block: false, + end_sequence: false, + prologue_end: false, + epilogue_begin: false, + // "The isa value 0 specifies that the instruction set is the + // architecturally determined default instruction set. This may be fixed + // by the ABI, or it may be specified by other means, for example, by + // the object file description." + isa: 0, + discriminator: 0, + } + } + + /// "The program-counter value corresponding to a machine instruction + /// generated by the compiler." + #[inline] + pub fn address(&self) -> u64 { + self.address + } + + /// > An unsigned integer representing the index of an operation within a VLIW + /// > instruction. The index of the first operation is 0. For non-VLIW + /// > architectures, this register will always be 0. + /// > + /// > The address and op_index registers, taken together, form an operation + /// > pointer that can reference any individual operation with the + /// > instruction stream. + #[inline] + pub fn op_index(&self) -> u64 { + self.op_index.0 + } + + /// "An unsigned integer indicating the identity of the source file + /// corresponding to a machine instruction." + #[inline] + pub fn file_index(&self) -> u64 { + self.file + } + + /// The source file corresponding to the current machine instruction. + #[inline] + pub fn file<'header, R: Reader>( + &self, + header: &'header LineProgramHeader<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, + ) -> Result<bool> + where + Program: LineProgram<R>, + R: Reader, + { + Ok(match instruction { + LineInstruction::Special(opcode) => { + self.exec_special_opcode(opcode, program.header())?; + true + } + + LineInstruction::Copy => true, + + LineInstruction::AdvancePc(operation_advance) => { + self.apply_operation_advance(operation_advance, program.header())?; + false + } + + LineInstruction::AdvanceLine(line_increment) => { + self.apply_line_advance(line_increment); + false + } + + LineInstruction::SetFile(file) => { + self.file = file; + false + } + + LineInstruction::SetColumn(column) => { + self.column = column; + false + } + + LineInstruction::NegateStatement => { + self.is_stmt = !self.is_stmt; + false + } + + LineInstruction::SetBasicBlock => { + self.basic_block = true; + false + } + + LineInstruction::ConstAddPc => { + let adjusted = self.adjust_opcode(255, program.header()); + let operation_advance = adjusted / program.header().line_encoding.line_range; + self.apply_operation_advance(u64::from(operation_advance), program.header())?; + false + } + + LineInstruction::FixedAddPc(operand) => { + if !self.tombstone { + let address_size = program.header().address_size(); + self.address = self.address.add_sized(u64::from(operand), address_size)?; + self.op_index.0 = 0; + } + false + } + + LineInstruction::SetPrologueEnd => { + self.prologue_end = true; + false + } + + LineInstruction::SetEpilogueBegin => { + self.epilogue_begin = true; + false + } + + LineInstruction::SetIsa(isa) => { + self.isa = isa; + false + } + + LineInstruction::EndSequence => { + self.end_sequence = true; + true + } + + LineInstruction::SetAddress(address) => { + // If the address is a tombstone, then skip instructions until the next address. + // DWARF specifies a tombstone value of -1, but many linkers use 0. + // However, 0 may be a valid address, so we only skip that if we have previously + // seen a higher address. Additionally, gold may keep the relocation addend, + // so we treat all lower addresses as tombstones instead of just 0. + // This works because DWARF specifies that addresses are monotonically increasing + // within a sequence; the alternative is to return an error. + self.tombstone = address < self.address + || address >= u64::min_tombstone(program.header().encoding.address_size); + if !self.tombstone { + self.address = address; + self.op_index.0 = 0; + } + false + } + + LineInstruction::DefineFile(entry) => { + program.add_file(entry); + false + } + + LineInstruction::SetDiscriminator(discriminator) => { + self.discriminator = discriminator; + false + } + + // Compatibility with future opcodes. + LineInstruction::UnknownStandard0(_) + | LineInstruction::UnknownStandard1(_, _) + | LineInstruction::UnknownStandardN(_, _) + | LineInstruction::UnknownExtended(_, _) => false, + }) + } + + /// Perform any reset that was required after copying the previous row. + #[inline] + pub fn reset<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>, + ) -> Result<()> { + if self.tombstone { + return Ok(()); + } + + let operation_advance = Wrapping(operation_advance); + + let minimum_instruction_length = u64::from(header.line_encoding.minimum_instruction_length); + let minimum_instruction_length = Wrapping(minimum_instruction_length); + + let maximum_operations_per_instruction = + u64::from(header.line_encoding.maximum_operations_per_instruction); + let maximum_operations_per_instruction = Wrapping(maximum_operations_per_instruction); + + let address_advance = if maximum_operations_per_instruction.0 == 1 { + self.op_index.0 = 0; + minimum_instruction_length * operation_advance + } else { + let op_index_with_advance = self.op_index + operation_advance; + self.op_index = op_index_with_advance % maximum_operations_per_instruction; + minimum_instruction_length + * (op_index_with_advance / maximum_operations_per_instruction) + }; + self.address = self + .address + .add_sized(address_advance.0, header.address_size())?; + Ok(()) + } + + #[inline] + fn adjust_opcode<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>, + ) -> Result<()> { + let adjusted_opcode = self.adjust_opcode(opcode, header); + + let line_range = header.line_encoding.line_range; + let line_advance = adjusted_opcode % line_range; + let operation_advance = adjusted_opcode / line_range; + + // Step 1 + let line_base = i64::from(header.line_encoding.line_base); + self.apply_line_advance(line_base + i64::from(line_advance)); + + // Step 2 + self.apply_operation_advance(u64::from(operation_advance), header)?; + Ok(()) + } +} + +/// The type of column that a row is referring to. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum ColumnType { + /// The `LeftEdge` means that the statement begins at the start of the new + /// line. + LeftEdge, + /// A column number, whose range begins at 1. + Column(NonZeroU64), +} + +/// Deprecated. `LineNumberSequence` has been renamed to `LineSequence`. +#[deprecated(note = "LineNumberSequence has been renamed to LineSequence, use that instead.")] +pub type LineNumberSequence<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) + } + + /// Return true if the file name entry format contains a source field. + pub fn file_has_source(&self) -> bool { + self.file_name_entry_format + .iter() + .any(|x| x.content_type == constants::DW_LNCT_LLVM_source) + } + + /// Get the list of source files that appear in this header's line program. + pub fn file_names(&self) -> &[FileEntry<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_address_size()?; + let segment_selector_size = rest.read_u8()?; + if segment_selector_size != 0 { + return Err(Error::UnsupportedSegmentSize); + } + } + + let encoding = Encoding { + format, + version, + address_size, + }; + + let header_length = rest.read_length(format)?; + + let mut program_buf = rest.clone(); + program_buf.skip(header_length)?; + rest.truncate(header_length)?; + + let minimum_instruction_length = rest.read_u8()?; + if minimum_instruction_length == 0 { + return Err(Error::MinimumInstructionLengthZero); + } + + // This field did not exist before DWARF 4, but is specified to be 1 for + // non-VLIW architectures, which makes it a no-op. + let maximum_operations_per_instruction = if version >= 4 { rest.read_u8()? } else { 1 }; + if maximum_operations_per_instruction == 0 { + return Err(Error::MaximumOperationsPerInstructionZero); + } + + let default_is_stmt = rest.read_u8()? != 0; + let line_base = rest.read_i8()?; + let line_range = rest.read_u8()?; + if line_range == 0 { + return Err(Error::LineRangeZero); + } + let line_encoding = LineEncoding { + minimum_instruction_length, + maximum_operations_per_instruction, + default_is_stmt, + line_base, + line_range, + }; + + let opcode_base = rest.read_u8()?; + if opcode_base == 0 { + return Err(Error::OpcodeBaseZero); + } + + let standard_opcode_count = R::Offset::from_u8(opcode_base - 1); + let standard_opcode_lengths = rest.split(standard_opcode_count)?; + + let directory_entry_format; + let mut include_directories = Vec::new(); + if version <= 4 { + directory_entry_format = Vec::new(); + loop { + let directory = rest.read_null_terminated_slice()?; + if directory.is_empty() { + break; + } + include_directories.push(AttributeValue::String(directory)); + } + } else { + comp_dir = None; + directory_entry_format = FileEntryFormat::parse(rest)?; + let count = rest.read_uleb128()?; + for _ in 0..count { + include_directories.push(parse_directory_v5( + rest, + encoding, + &directory_entry_format, + )?); + } + } + + let comp_file; + let file_name_entry_format; + let mut file_names = Vec::new(); + if version <= 4 { + comp_file = comp_name.map(|name| FileEntry { + path_name: AttributeValue::String(name), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + source: None, + }); + + file_name_entry_format = Vec::new(); + loop { + let path_name = rest.read_null_terminated_slice()?; + if path_name.is_empty() { + break; + } + file_names.push(FileEntry::parse(rest, path_name)?); + } + } else { + comp_file = None; + file_name_entry_format = FileEntryFormat::parse(rest)?; + let count = rest.read_uleb128()?; + for _ in 0..count { + file_names.push(parse_file_v5(rest, encoding, &file_name_entry_format)?); + } + } + + let header = LineProgramHeader { + encoding, + offset, + unit_length, + header_length, + line_encoding, + opcode_base, + standard_opcode_lengths, + directory_entry_format, + include_directories, + file_name_entry_format, + file_names, + program_buf, + comp_dir, + comp_file, + }; + Ok(header) + } +} + +/// Deprecated. `IncompleteLineNumberProgram` has been renamed to `IncompleteLineProgram`. +#[deprecated( + note = "IncompleteLineNumberProgram has been renamed to IncompleteLineProgram, use that instead." +)] +pub type IncompleteLineNumberProgram<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], + source: Option<AttributeValue<R, Offset>>, +} + +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], + source: None, + }; + + Ok(entry) + } + + /// > A slice containing the full or relative path name of + /// > a source file. If the entry contains a file name or a relative path + /// > name, the file is located relative to either the compilation directory + /// > (as specified by the DW_AT_comp_dir attribute given in the compilation + /// > unit) or one of the directories in the include_directories section. + pub fn path_name(&self) -> AttributeValue<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 source code of this file. (UTF-8 source text string with "\n" line + /// endings). + /// + /// Note: For DWARF v5 files this may return an empty attribute that + /// indicates that no source code is available, which this function + /// represents as `Some(<zero-length attr>)`. + pub fn source(&self) -> Option<AttributeValue<R, Offset>> { + self.source.clone() + } +} + +/// The format of a component of an include directory or file name entry. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct FileEntryFormat { + /// The type of information that is represented by the component. + pub content_type: constants::DwLnct, + + /// The encoding form of the component value. + pub form: constants::DwForm, +} + +impl FileEntryFormat { + fn parse<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) { + constants::DwLnct(u16::MAX) + } else { + constants::DwLnct(content_type as u16) + }; + if content_type == constants::DW_LNCT_path { + path_count += 1; + } + + let form = constants::DwForm(input.read_uleb128_u16()?); + + format.push(FileEntryFormat { content_type, form }); + } + if path_count != 1 { + return Err(Error::MissingFileEntryFormatPath); + } + Ok(format) + } +} + +fn parse_directory_v5<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]; + let mut source = None; + + for format in formats { + let value = parse_attribute(input, encoding, format.form)?; + match format.content_type { + constants::DW_LNCT_path => path_name = Some(value), + constants::DW_LNCT_directory_index => { + if let Some(value) = value.udata_value() { + directory_index = value; + } + } + constants::DW_LNCT_timestamp => { + if let Some(value) = value.udata_value() { + timestamp = value; + } + } + constants::DW_LNCT_size => { + if let Some(value) = value.udata_value() { + size = value; + } + } + constants::DW_LNCT_MD5 => { + if let AttributeValue::Block(mut value) = value { + if value.len().into_u64() == 16 { + md5 = value.read_u8_array()?; + } + } + } + constants::DW_LNCT_LLVM_source => { + source = Some(value); + } + // Ignore unknown content types. + _ => {} + } + } + + Ok(FileEntry { + path_name: path_name.unwrap(), + directory_index, + timestamp, + size, + md5, + source, + }) +} + +// TODO: this should be shared with unit::parse_attribute(), but that is hard to do. +fn parse_attribute<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(form)); + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::constants; + use crate::endianity::LittleEndian; + use crate::read::{EndianSlice, Error}; + use crate::test_util::GimliSectionMethods; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_parse_debug_line_32_ok() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 62. + 0x3e, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 40. + 0x28, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + // Dummy line program data. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next line program. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let comp_dir = EndianSlice::new(b"/comp_dir", LittleEndian); + let comp_name = EndianSlice::new(b"/comp_name", LittleEndian); + + let header = + LineProgramHeader::parse(rest, DebugLineOffset(0), 4, Some(comp_dir), Some(comp_name)) + .expect("should parse header ok"); + + assert_eq!( + *rest, + EndianSlice::new(&buf[buf.len() - 16..], LittleEndian) + ); + + assert_eq!(header.offset, DebugLineOffset(0)); + assert_eq!(header.version(), 4); + assert_eq!(header.minimum_instruction_length(), 1); + assert_eq!(header.maximum_operations_per_instruction(), 1); + assert!(header.default_is_stmt()); + assert_eq!(header.line_base(), 0); + assert_eq!(header.line_range(), 1); + assert_eq!(header.opcode_base(), 3); + assert_eq!(header.directory(0), Some(AttributeValue::String(comp_dir))); + assert_eq!( + header.file(0).unwrap().path_name, + AttributeValue::String(comp_name) + ); + + let expected_lengths = [1, 2]; + assert_eq!(header.standard_opcode_lengths().slice(), &expected_lengths); + + let expected_include_directories = [ + AttributeValue::String(EndianSlice::new(b"/inc", LittleEndian)), + AttributeValue::String(EndianSlice::new(b"/inc2", LittleEndian)), + ]; + assert_eq!(header.include_directories(), &expected_include_directories); + + let expected_file_names = [ + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"foo.rs", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + source: None, + }, + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)), + directory_index: 1, + timestamp: 0, + size: 0, + md5: [0; 16], + source: None, + }, + ]; + assert_eq!(header.file_names(), &expected_file_names); + } + + #[test] + fn test_parse_debug_line_header_length_too_short() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 62. + 0x3e, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 20. TOO SHORT!!! + 0x15, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + // Dummy line program data. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next line program. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let input = &mut EndianSlice::new(&buf, LittleEndian); + + match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_parse_debug_line_unit_length_too_short() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 40. TOO SHORT!!! + 0x28, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 40. + 0x28, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + // Dummy line program data. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next line program. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let input = &mut EndianSlice::new(&buf, LittleEndian); + + match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + const OPCODE_BASE: u8 = 13; + const STANDARD_OPCODE_LENGTHS: &[u8] = &[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1]; + + fn make_test_header( + buf: EndianSlice<'_, LittleEndian>, + ) -> LineProgramHeader<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], + source: None, + }, + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + source: None, + }, + ], + include_directories: vec![], + directory_entry_format: vec![], + file_name_entry_format: vec![], + program_buf: buf, + comp_dir: None, + comp_file: None, + } + } + + fn make_test_program( + buf: EndianSlice<'_, LittleEndian>, + ) -> IncompleteLineProgram<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 = input.range_from(1..); + let mut standard_opcode_lengths = Vec::new(); + let mut header = make_test_header(input); + standard_opcode_lengths.extend(header.standard_opcode_lengths.slice()); + standard_opcode_lengths.push(3); + header.opcode_base += 1; + header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian); + + let mut rest = input; + let opcode = + LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); + + assert_eq!( + opcode, + LineInstruction::UnknownStandardN(constants::DwLns(OPCODE_BASE), args) + ); + assert_eq!(*rest, []); + } + + #[test] + fn test_parse_extended_opcodes() { + fn test<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], + source: None, + }), + ); + + // Unknown extended opcode. + let operands = [1, 2, 3, 4, 5, 6]; + let opcode = constants::DwLne(99); + test( + opcode, + operands, + LineInstruction::UnknownExtended(opcode, EndianSlice::new(&operands, LittleEndian)), + ); + } + + #[test] + fn test_file_entry_directory() { + let path_name = [b'f', b'o', b'o', b'.', b'r', b's', 0]; + + let mut file = FileEntry { + path_name: AttributeValue::String(EndianSlice::new(&path_name, LittleEndian)), + directory_index: 1, + timestamp: 0, + size: 0, + md5: [0; 16], + source: None, + }; + + let mut header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let dir = AttributeValue::String(EndianSlice::new(b"dir", LittleEndian)); + header.include_directories.push(dir); + + assert_eq!(file.directory(&header), Some(dir)); + + // Now test the compilation's current directory. + file.directory_index = 0; + assert_eq!(file.directory(&header), None); + } + + fn assert_exec_opcode<'input>( + header: LineProgramHeader<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, Ok(expect_new_row)); + assert_eq!(registers, expected_registers); + } + + #[test] + fn test_exec_special_noop() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::Special(16); + let expected_registers = initial_registers; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_negative_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = 10; + + let opcode = LineInstruction::Special(13); + + let mut expected_registers = initial_registers; + expected_registers.line.0 -= 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + + let opcode = LineInstruction::Special(19); + + let mut expected_registers = initial_registers; + expected_registers.line.0 += 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_address_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + + let opcode = LineInstruction::Special(52); + + let mut expected_registers = initial_registers; + expected_registers.address += 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_address_and_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + + let opcode = LineInstruction::Special(55); + + let mut expected_registers = initial_registers; + expected_registers.address += 3; + expected_registers.line.0 += 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_address_and_negative_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = 10; + + let opcode = LineInstruction::Special(49); + + let mut expected_registers = initial_registers; + expected_registers.address += 3; + expected_registers.line.0 -= 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_line_underflow() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = 2; + + // -3 line advance. + let opcode = LineInstruction::Special(13); + + let mut expected_registers = initial_registers; + // Clamp at 0. No idea if this is the best way to handle this situation + // or not... + expected_registers.line.0 = 0; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_copy() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.address = 1337; + initial_registers.line.0 = 42; + + let opcode = LineInstruction::Copy; + + let expected_registers = initial_registers; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_advance_pc() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::AdvancePc(42); + + let mut expected_registers = initial_registers; + expected_registers.address += 42; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_advance_pc_overflow_32() { + let mut header = make_test_header(EndianSlice::new(&[], LittleEndian)); + header.encoding.address_size = 4; + let mut registers = LineRow::new(&header); + registers.address = u32::MAX.into(); + let opcode = LineInstruction::AdvancePc(42); + let mut program = IncompleteLineProgram { header }; + let result = registers.execute(opcode, &mut program); + assert_eq!(result, Err(Error::AddressOverflow)); + } + + #[test] + fn test_exec_advance_pc_overflow_64() { + let mut header = make_test_header(EndianSlice::new(&[], LittleEndian)); + header.encoding.address_size = 8; + let mut registers = LineRow::new(&header); + registers.address = u64::MAX; + let opcode = LineInstruction::AdvancePc(42); + let mut program = IncompleteLineProgram { header }; + let result = registers.execute(opcode, &mut program); + assert_eq!(result, Err(Error::AddressOverflow)); + } + + #[test] + fn test_exec_advance_line() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::AdvanceLine(42); + + let mut expected_registers = initial_registers; + expected_registers.line.0 += 42; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_advance_line_overflow() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let opcode = LineInstruction::AdvanceLine(42); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = u64::MAX; + + let mut expected_registers = initial_registers; + expected_registers.line.0 = 41; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_file_in_bounds() { + for file_idx in 1..3 { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetFile(file_idx); + + let mut expected_registers = initial_registers; + expected_registers.file = file_idx; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + } + + #[test] + fn test_exec_set_file_out_of_bounds() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetFile(100); + + // The spec doesn't say anything about rejecting input programs + // that set the file register out of bounds of the actual number + // of files that have been defined. Instead, we cross our + // fingers and hope that one gets defined before + // `LineRow::file` gets called and handle the error at + // that time if need be. + let mut expected_registers = initial_registers; + expected_registers.file = 100; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_file_entry_file_index_out_of_bounds() { + // These indices are 1-based, so 0 is invalid. 100 is way more than the + // number of files defined in the header. + let out_of_bounds_indices = [0, 100]; + + for file_idx in &out_of_bounds_indices[..] { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let mut row = LineRow::new(&header); + + row.file = *file_idx; + + assert_eq!(row.file(&header), None); + } + } + + #[test] + fn test_file_entry_file_index_in_bounds() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let mut row = LineRow::new(&header); + + row.file = 2; + + assert_eq!(row.file(&header), Some(&header.file_names()[1])); + } + + #[test] + fn test_exec_set_column() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetColumn(42); + + let mut expected_registers = initial_registers; + expected_registers.column = 42; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_negate_statement() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::NegateStatement; + + let mut expected_registers = initial_registers; + expected_registers.is_stmt = !initial_registers.is_stmt; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_basic_block() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.basic_block = false; + + let opcode = LineInstruction::SetBasicBlock; + + let mut expected_registers = initial_registers; + expected_registers.basic_block = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_const_add_pc() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::ConstAddPc; + + let mut expected_registers = initial_registers; + expected_registers.address += 20; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_const_add_pc_overflow() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let mut registers = LineRow::new(&header); + registers.address = u64::MAX; + let opcode = LineInstruction::ConstAddPc; + let mut program = IncompleteLineProgram { header }; + let result = registers.execute(opcode, &mut program); + assert_eq!(result, Err(Error::AddressOverflow)); + } + + #[test] + fn test_exec_fixed_add_pc() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.op_index.0 = 1; + + let opcode = LineInstruction::FixedAddPc(10); + + let mut expected_registers = initial_registers; + expected_registers.address += 10; + expected_registers.op_index.0 = 0; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_fixed_add_pc_overflow() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let mut registers = LineRow::new(&header); + registers.address = u64::MAX; + registers.op_index.0 = 1; + let opcode = LineInstruction::FixedAddPc(10); + let mut program = IncompleteLineProgram { header }; + let result = registers.execute(opcode, &mut program); + assert_eq!(result, Err(Error::AddressOverflow)); + } + + #[test] + fn test_exec_set_prologue_end() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.prologue_end = false; + + let opcode = LineInstruction::SetPrologueEnd; + + let mut expected_registers = initial_registers; + expected_registers.prologue_end = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_isa() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetIsa(1993); + + let mut expected_registers = initial_registers; + expected_registers.isa = 1993; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_standard_0() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownStandard0(constants::DwLns(111)); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_standard_1() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownStandard1(constants::DwLns(111), 2); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_standard_n() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownStandardN( + constants::DwLns(111), + EndianSlice::new(&[2, 2, 2], LittleEndian), + ); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_end_sequence() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::EndSequence; + + let mut expected_registers = initial_registers; + expected_registers.end_sequence = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_set_address() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetAddress(3030); + + let mut expected_registers = initial_registers; + expected_registers.address = 3030; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_address_tombstone() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetAddress(!0); + + let mut expected_registers = initial_registers; + expected_registers.tombstone = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_address_backwards() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let mut initial_registers = LineRow::new(&header); + initial_registers.address = 1; + let opcode = LineInstruction::SetAddress(0); + + let mut expected_registers = initial_registers; + expected_registers.tombstone = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_define_file() { + let mut program = make_test_program(EndianSlice::new(&[], LittleEndian)); + let mut row = LineRow::new(program.header()); + + let file = FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"test.cpp", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + source: None, + }; + + let opcode = LineInstruction::DefineFile(file); + let is_new_row = row.execute(opcode, &mut program).unwrap(); + + assert!(!is_new_row); + assert_eq!(Some(&file), program.header().file_names.last()); + } + + #[test] + fn test_exec_set_discriminator() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetDiscriminator(9); + + let mut expected_registers = initial_registers; + expected_registers.discriminator = 9; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_extended() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownExtended( + constants::DwLne(74), + EndianSlice::new(&[], LittleEndian), + ); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + /// Ensure that `LineRows<R,P>` is covariant wrt R. + /// This only needs to compile. + #[allow(dead_code, unreachable_code, unused_variables)] + #[allow(clippy::diverging_sub_expression)] + fn test_line_rows_variance<'a, 'b>(_: &'a [u8], _: &'b [u8]) + where + 'a: 'b, + { + let a: &OneShotLineRows<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], + source: Some(AttributeValue::String(EndianSlice::new( + b"foobar", + LittleEndian, + ))), + }, + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"file2", LittleEndian)), + directory_index: 1, + timestamp: 0, + size: 0, + md5: [ + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + ], + source: Some(AttributeValue::String(EndianSlice::new( + b"quux", + LittleEndian, + ))), + }, + ]; + + for format in [Format::Dwarf32, Format::Dwarf64] { + let length = Label::new(); + let header_length = Label::new(); + let start = Label::new(); + let header_start = Label::new(); + let end = Label::new(); + let header_end = Label::new(); + let section = Section::with_endian(Endian::Little) + .initial_length(format, &length, &start) + .D16(5) + // Address size. + .D8(4) + // Segment selector size. + .D8(0) + .word_label(format.word_size(), &header_length) + .mark(&header_start) + // Minimum instruction length. + .D8(1) + // Maximum operations per byte. + .D8(1) + // Default is_stmt. + .D8(1) + // Line base. + .D8(0) + // Line range. + .D8(1) + // Opcode base. + .D8(expected_lengths.len() as u8 + 1) + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + .append_bytes(expected_lengths) + // Directory entry format count. + .D8(1) + .uleb(constants::DW_LNCT_path.0 as u64) + .uleb(constants::DW_FORM_string.0 as u64) + // Directory count. + .D8(2) + .append_bytes(b"dir1\0") + .append_bytes(b"dir2\0") + // File entry format count. + .D8(4) + .uleb(constants::DW_LNCT_path.0 as u64) + .uleb(constants::DW_FORM_string.0 as u64) + .uleb(constants::DW_LNCT_directory_index.0 as u64) + .uleb(constants::DW_FORM_data1.0 as u64) + .uleb(constants::DW_LNCT_MD5.0 as u64) + .uleb(constants::DW_FORM_data16.0 as u64) + .uleb(constants::DW_LNCT_LLVM_source.0 as u64) + .uleb(constants::DW_FORM_string.0 as u64) + // File count. + .D8(2) + .append_bytes(b"file1\0") + .D8(0) + .append_bytes(&expected_file_names[0].md5) + .append_bytes(b"foobar\0") + .append_bytes(b"file2\0") + .D8(1) + .append_bytes(&expected_file_names[1].md5) + .append_bytes(b"quux\0") + .mark(&header_end) + // Dummy line program data. + .append_bytes(expected_program) + .mark(&end) + // Dummy trailing data. + .append_bytes(expected_rest); + length.set_const((&end - &start) as u64); + header_length.set_const((&header_end - &header_start) as u64); + let section = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(§ion, LittleEndian); + + let header = LineProgramHeader::parse(input, DebugLineOffset(0), 0, None, None) + .expect("should parse header ok"); + + assert_eq!(header.raw_program_buf().slice(), expected_program); + assert_eq!(input.slice(), expected_rest); + + assert_eq!(header.offset, DebugLineOffset(0)); + assert_eq!(header.version(), 5); + assert_eq!(header.address_size(), 4); + assert_eq!(header.minimum_instruction_length(), 1); + assert_eq!(header.maximum_operations_per_instruction(), 1); + assert!(header.default_is_stmt()); + assert_eq!(header.line_base(), 0); + assert_eq!(header.line_range(), 1); + assert_eq!(header.opcode_base(), expected_lengths.len() as u8 + 1); + assert_eq!(header.standard_opcode_lengths().slice(), expected_lengths); + assert_eq!( + header.directory_entry_format(), + &[FileEntryFormat { + content_type: constants::DW_LNCT_path, + form: constants::DW_FORM_string, + }] + ); + assert_eq!(header.include_directories(), expected_include_directories); + assert_eq!(header.directory(0), Some(expected_include_directories[0])); + assert_eq!( + header.file_name_entry_format(), + &[ + FileEntryFormat { + content_type: constants::DW_LNCT_path, + form: constants::DW_FORM_string, + }, + FileEntryFormat { + content_type: constants::DW_LNCT_directory_index, + form: constants::DW_FORM_data1, + }, + FileEntryFormat { + content_type: constants::DW_LNCT_MD5, + form: constants::DW_FORM_data16, + }, + FileEntryFormat { + content_type: constants::DW_LNCT_LLVM_source, + form: constants::DW_FORM_string, + } + ] + ); + assert_eq!(header.file_names(), expected_file_names); + assert_eq!(header.file(0), Some(&expected_file_names[0])); + } + } + + #[test] + fn test_sequences() { + #[rustfmt::skip] + let buf = [ + // 32-bit length + 94, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 40. + 0x28, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + 0, 5, constants::DW_LNE_set_address.0, 1, 0, 0, 0, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 1, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 2, + 0, 1, constants::DW_LNE_end_sequence.0, + + // Tombstone + 0, 5, constants::DW_LNE_set_address.0, 0xff, 0xff, 0xff, 0xff, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 1, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 2, + 0, 1, constants::DW_LNE_end_sequence.0, + + 0, 5, constants::DW_LNE_set_address.0, 11, 0, 0, 0, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 1, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 2, + 0, 1, constants::DW_LNE_end_sequence.0, + ]; + assert_eq!(buf[0] as usize, buf.len() - 4); + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let header = LineProgramHeader::parse(rest, DebugLineOffset(0), 4, None, None) + .expect("should parse header ok"); + let program = IncompleteLineProgram { header }; + + let sequences = program.sequences().unwrap().1; + assert_eq!(sequences.len(), 2); + assert_eq!(sequences[0].start, 1); + assert_eq!(sequences[0].end, 4); + assert_eq!(sequences[1].start, 11); + assert_eq!(sequences[1].end, 14); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/lists.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/lists.rs new file mode 100644 index 0000000..bb41901 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/lists.rs
@@ -0,0 +1,68 @@ +use crate::common::{Encoding, Format}; +use crate::read::{Error, Reader, Result}; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct ListsHeader { + encoding: Encoding, + #[allow(dead_code)] + offset_entry_count: u32, +} + +impl Default for ListsHeader { + fn default() -> Self { + ListsHeader { + encoding: Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 0, + }, + offset_entry_count: 0, + } + } +} + +impl ListsHeader { + /// Return the serialized size of the table header. + #[allow(dead_code)] + #[inline] + fn size(self) -> u8 { + // initial_length + version + address_size + segment_selector_size + offset_entry_count + ListsHeader::size_for_encoding(self.encoding) + } + + /// Return the serialized size of the table header. + #[inline] + pub(crate) fn size_for_encoding(encoding: Encoding) -> u8 { + // initial_length + version + address_size + segment_selector_size + offset_entry_count + encoding.format.initial_length_size() + 2 + 1 + 1 + 4 + } +} + +// TODO: add an iterator over headers in the appropriate sections section +#[allow(dead_code)] +fn parse_header<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_address_size()?; + let segment_selector_size = input.read_u8()?; + if segment_selector_size != 0 { + return Err(Error::UnsupportedSegmentSize); + } + let offset_entry_count = input.read_u32()?; + + let encoding = Encoding { + format, + version, + address_size, + }; + Ok(ListsHeader { + encoding, + offset_entry_count, + }) +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/loclists.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/loclists.rs new file mode 100644 index 0000000..0efca02 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/loclists.rs
@@ -0,0 +1,1126 @@ +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, + ReaderAddress, 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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugLoc, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_loc_section_somehow = || &buf; + /// let debug_loc = DebugLoc::new(read_debug_loc_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<T> DebugLoc<T> { + /// Create a `DebugLoc` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfSections::borrow`. + pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLoc<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugLocLists, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_loclists_section_somehow = || &buf; + /// let debug_loclists = DebugLocLists::new(read_debug_loclists_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<T> DebugLocLists<T> { + /// Create a `DebugLocLists` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfSections::borrow`. + pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLocLists<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +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. + /// + /// Used by `Dwarf::borrow`. + 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>> { + Ok(match format { + LocListsFormat::Bare => { + let range = RawRange::parse(input, encoding.address_size)?; + if range.is_end() { + None + } else if range.is_base_address(encoding.address_size) { + Some(RawLocListEntry::BaseAddress { addr: range.end }) + } else { + let len = R::Offset::from_u16(input.read_u16()?); + let data = Expression(input.split(len)?); + Some(RawLocListEntry::AddressOrOffsetPair { + begin: range.begin, + end: range.end, + data, + }) + } + } + LocListsFormat::Lle => match constants::DwLle(input.read_u8()?) { + constants::DW_LLE_end_of_list => None, + constants::DW_LLE_base_addressx => Some(RawLocListEntry::BaseAddressx { + addr: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + }), + constants::DW_LLE_startx_endx => Some(RawLocListEntry::StartxEndx { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + end: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_startx_length => Some(RawLocListEntry::StartxLength { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + length: if encoding.version >= 5 { + input.read_uleb128()? + } else { + // In the GNU split-dwarf extension this is a fixed 4 byte value. + input.read_u32()? as u64 + }, + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_offset_pair => Some(RawLocListEntry::OffsetPair { + begin: input.read_uleb128()?, + end: input.read_uleb128()?, + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_default_location => Some(RawLocListEntry::DefaultLocation { + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_base_address => Some(RawLocListEntry::BaseAddress { + addr: input.read_address(encoding.address_size)?, + }), + constants::DW_LLE_start_end => Some(RawLocListEntry::StartEnd { + begin: input.read_address(encoding.address_size)?, + end: input.read_address(encoding.address_size)?, + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_start_length => Some(RawLocListEntry::StartLength { + begin: input.read_address(encoding.address_size)?, + length: input.read_uleb128()?, + data: parse_data(input, encoding)?, + }), + entry => { + return Err(Error::UnknownLocListsEntry(entry)); + } + }, + }) + } +} + +impl<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 loc = self.convert_raw(raw_loc)?; + if loc.is_some() { + return Ok(loc); + } + } + } + + /// Return the next raw location. + /// + /// The raw location should be passed to `convert_raw`. + #[doc(hidden)] + pub fn next_raw(&mut self) -> Result<Option<RawLocListEntry<R>>> { + self.raw.next() + } + + /// Convert a raw location into a location, and update the state of the iterator. + /// + /// The raw location should have been obtained from `next_raw`. + #[doc(hidden)] + pub fn convert_raw( + &mut self, + raw_loc: RawLocListEntry<R>, + ) -> Result<Option<LocationListEntry<R>>> { + let address_size = self.raw.encoding.address_size; + + let (range, data) = match raw_loc { + RawLocListEntry::BaseAddress { addr } => { + self.base_address = addr; + return Ok(None); + } + RawLocListEntry::BaseAddressx { addr } => { + self.base_address = self.get_address(addr)?; + return Ok(None); + } + RawLocListEntry::StartxEndx { begin, end, data } => { + let begin = self.get_address(begin)?; + let end = self.get_address(end)?; + (Range { begin, end }, data) + } + RawLocListEntry::StartxLength { + begin, + length, + data, + } => { + let begin = self.get_address(begin)?; + let end = begin.wrapping_add_sized(length, address_size); + (Range { begin, end }, data) + } + RawLocListEntry::DefaultLocation { data } => ( + Range { + begin: 0, + end: u64::MAX, + }, + data, + ), + RawLocListEntry::AddressOrOffsetPair { begin, end, data } + | RawLocListEntry::OffsetPair { begin, end, data } => { + // Skip tombstone entries (see below). + if self.base_address >= u64::min_tombstone(address_size) { + return Ok(None); + } + let mut range = Range { begin, end }; + range.add_base_address(self.base_address, address_size); + (range, data) + } + RawLocListEntry::StartEnd { begin, end, data } => (Range { begin, end }, data), + RawLocListEntry::StartLength { + begin, + length, + data, + } => { + let end = begin.wrapping_add_sized(length, address_size); + (Range { begin, end }, data) + } + }; + + // Skip tombstone entries. + // + // DWARF specifies a tombstone value of -1 or -2, but many linkers use 0 or 1. + // However, 0/1 may be a valid address, so we can't always reliably skip them. + // One case where we can skip them is for address pairs, where both values are + // replaced by tombstones and thus `begin` equals `end`. Since these entries + // are empty, it's safe to skip them even if they aren't tombstones. + // + // In addition to skipping tombstone entries, we also skip invalid entries + // where `begin` is greater than `end`. This can occur due to compiler bugs. + if range.begin >= u64::min_tombstone(address_size) || range.begin >= range.end { + return Ok(None); + } + + 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::constants::*; + use crate::endianity::LittleEndian; + use crate::read::{EndianSlice, Range}; + use crate::test_util::GimliSectionMethods; + use alloc::vec::Vec; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_loclists() { + let format = Format::Dwarf32; + for size in [4, 8] { + let tombstone = u64::ones_sized(size); + let tombstone_0 = 0; + let encoding = Encoding { + format, + version: 5, + address_size: size, + }; + + let section = Section::with_endian(Endian::Little) + .word(size, 0x0300_0000) + .word(size, 0x0301_0300) + .word(size, 0x0301_0400) + .word(size, 0x0301_0500) + .word(size, tombstone) + .word(size, 0x0301_0600) + .word(size, tombstone_0); + let buf = section.get_contents().unwrap(); + let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .initial_length(format, &length, &start) + .L16(encoding.version) + .L8(encoding.address_size) + .L8(0) + .L32(0) + .mark(&first); + + let mut expected_locations = Vec::new(); + let mut expect_location = |begin, end, data| { + expected_locations.push(LocationListEntry { + range: Range { begin, end }, + data: Expression(EndianSlice::new(data, LittleEndian)), + }); + }; + + // An offset pair using the unit base address. + section = section.L8(DW_LLE_offset_pair.0).uleb(0x10200).uleb(0x10300); + section = section.uleb(4).L32(2); + expect_location(0x0101_0200, 0x0101_0300, &[2, 0, 0, 0]); + + section = section.L8(DW_LLE_base_address.0).word(size, 0x0200_0000); + section = section.L8(DW_LLE_offset_pair.0).uleb(0x10400).uleb(0x10500); + section = section.uleb(4).L32(3); + expect_location(0x0201_0400, 0x0201_0500, &[3, 0, 0, 0]); + + section = section + .L8(DW_LLE_start_end.0) + .word(size, 0x201_0a00) + .word(size, 0x201_0b00); + section = section.uleb(4).L32(6); + expect_location(0x0201_0a00, 0x0201_0b00, &[6, 0, 0, 0]); + + section = section + .L8(DW_LLE_start_length.0) + .word(size, 0x201_0c00) + .uleb(0x100); + section = section.uleb(4).L32(7); + expect_location(0x0201_0c00, 0x0201_0d00, &[7, 0, 0, 0]); + + // An offset pair that starts at 0. + section = section.L8(DW_LLE_base_address.0).word(size, 0); + section = section.L8(DW_LLE_offset_pair.0).uleb(0).uleb(1); + section = section.uleb(4).L32(8); + expect_location(0, 1, &[8, 0, 0, 0]); + + // An offset pair that ends at -1. + section = section.L8(DW_LLE_base_address.0).word(size, 0); + section = section.L8(DW_LLE_offset_pair.0).uleb(0).uleb(tombstone); + section = section.uleb(4).L32(9); + expect_location(0, tombstone, &[9, 0, 0, 0]); + + section = section.L8(DW_LLE_default_location.0).uleb(4).L32(10); + expect_location(0, u64::MAX, &[10, 0, 0, 0]); + + section = section.L8(DW_LLE_base_addressx.0).uleb(0); + section = section.L8(DW_LLE_offset_pair.0).uleb(0x10100).uleb(0x10200); + section = section.uleb(4).L32(11); + expect_location(0x0301_0100, 0x0301_0200, &[11, 0, 0, 0]); + + section = section.L8(DW_LLE_startx_endx.0).uleb(1).uleb(2); + section = section.uleb(4).L32(12); + expect_location(0x0301_0300, 0x0301_0400, &[12, 0, 0, 0]); + + section = section.L8(DW_LLE_startx_length.0).uleb(3).uleb(0x100); + section = section.uleb(4).L32(13); + expect_location(0x0301_0500, 0x0301_0600, &[13, 0, 0, 0]); + + // Tombstone entries, all of which should be ignored. + section = section.L8(DW_LLE_base_addressx.0).uleb(4); + section = section.L8(DW_LLE_offset_pair.0).uleb(0x11100).uleb(0x11200); + section = section.uleb(4).L32(20); + + section = section.L8(DW_LLE_base_address.0).word(size, tombstone); + section = section.L8(DW_LLE_offset_pair.0).uleb(0x11300).uleb(0x11400); + section = section.uleb(4).L32(21); + + section = section.L8(DW_LLE_startx_endx.0).uleb(4).uleb(5); + section = section.uleb(4).L32(22); + section = section.L8(DW_LLE_startx_length.0).uleb(4).uleb(0x100); + section = section.uleb(4).L32(23); + section = section + .L8(DW_LLE_start_end.0) + .word(size, tombstone) + .word(size, 0x201_1500); + section = section.uleb(4).L32(24); + section = section + .L8(DW_LLE_start_length.0) + .word(size, tombstone) + .uleb(0x100); + section = section.uleb(4).L32(25); + + // Ignore some instances of 0 for tombstone. + section = section.L8(DW_LLE_startx_endx.0).uleb(6).uleb(6); + section = section.uleb(4).L32(30); + section = section + .L8(DW_LLE_start_end.0) + .word(size, tombstone_0) + .word(size, tombstone_0); + section = section.uleb(4).L32(31); + + // Ignore empty ranges. + section = section.L8(DW_LLE_base_address.0).word(size, 0); + section = section.L8(DW_LLE_offset_pair.0).uleb(0).uleb(0); + section = section.uleb(4).L32(41); + section = section.L8(DW_LLE_base_address.0).word(size, 0x10000); + section = section.L8(DW_LLE_offset_pair.0).uleb(0x1234).uleb(0x1234); + section = section.uleb(4).L32(42); + + // A valid range after the tombstones. + section = section + .L8(DW_LLE_start_end.0) + .word(size, 0x201_1600) + .word(size, 0x201_1700); + section = section.uleb(4).L32(100); + expect_location(0x0201_1600, 0x0201_1700, &[100, 0, 0, 0]); + + section = section.L8(DW_LLE_end_of_list.0); + section = section.mark(&end); + // Some extra data. + section = section.word(size, 0x1234_5678); + length.set_const((&end - &start) as u64); + + let offset = LocationListsOffset((&first - §ion.start()) as usize); + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&[], LittleEndian); + let debug_loclists = DebugLocLists::new(&buf, LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let mut locations = loclists + .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + for expected_location in expected_locations { + let location = locations.next(); + assert_eq!( + location, + Ok(Some(expected_location)), + "read {:x?}, expect {:x?}", + location, + expected_location + ); + } + assert_eq!(locations.next(), Ok(None)); + } + } + + #[test] + fn test_location_list() { + for size in [4, 8] { + let base = u64::ones_sized(size); + let tombstone = u64::ones_sized(size) - 1; + let start = Label::new(); + let first = Label::new(); + let mut section = Section::with_endian(Endian::Little) + // A location before the offset. + .mark(&start) + .word(size, 0x10000) + .word(size, 0x10100) + .L16(4) + .L32(1) + .mark(&first); + + let mut expected_locations = Vec::new(); + let mut expect_location = |begin, end, data| { + expected_locations.push(LocationListEntry { + range: Range { begin, end }, + data: Expression(EndianSlice::new(data, LittleEndian)), + }); + }; + + // A normal location. + section = section.word(size, 0x10200).word(size, 0x10300); + section = section.L16(4).L32(2); + expect_location(0x0101_0200, 0x0101_0300, &[2, 0, 0, 0]); + // A base address selection followed by a normal location. + section = section.word(size, base).word(size, 0x0200_0000); + section = section.word(size, 0x10400).word(size, 0x10500); + section = section.L16(4).L32(3); + expect_location(0x0201_0400, 0x0201_0500, &[3, 0, 0, 0]); + // An empty location range followed by a normal location. + section = section.word(size, 0x10600).word(size, 0x10600); + section = section.L16(4).L32(4); + section = section.word(size, 0x10800).word(size, 0x10900); + section = section.L16(4).L32(5); + expect_location(0x0201_0800, 0x0201_0900, &[5, 0, 0, 0]); + // A location range that starts at 0. + section = section.word(size, base).word(size, 0); + section = section.word(size, 0).word(size, 1); + section = section.L16(4).L32(6); + expect_location(0, 1, &[6, 0, 0, 0]); + // A location range that ends at -1. + section = section.word(size, base).word(size, 0); + section = section.word(size, 0).word(size, base); + section = section.L16(4).L32(7); + expect_location(0, base, &[7, 0, 0, 0]); + // A normal location with tombstone. + section = section.word(size, tombstone).word(size, tombstone); + section = section.L16(4).L32(8); + // A base address selection with tombstone followed by a normal location. + section = section.word(size, base).word(size, tombstone); + section = section.word(size, 0x10a00).word(size, 0x10b00); + section = section.L16(4).L32(9); + // A location list end. + section = section.word(size, 0).word(size, 0); + // Some extra data. + section = section.word(size, 0x1234_5678); + + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&buf, LittleEndian); + let debug_loclists = DebugLocLists::new(&[], LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let offset = LocationListsOffset((&first - &start) as usize); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: size, + }; + let mut locations = loclists + .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + for expected_location in expected_locations { + let location = locations.next(); + assert_eq!( + location, + Ok(Some(expected_location)), + "read {:x?}, expect {:x?}", + location, + expected_location + ); + } + assert_eq!(locations.next(), Ok(None)); + + // An offset at the end of buf. + let mut locations = loclists + .locations( + LocationListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Ok(None)); + } + } + + #[test] + fn test_locations_invalid() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // An invalid location range. + .L32(0x20000).L32(0x10000).L16(4).L32(1) + // An invalid range after wrapping. + .L32(0x20000).L32(0xff01_0000).L16(4).L32(2); + + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&buf, LittleEndian); + let debug_loclists = DebugLocLists::new(&[], LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + + // An invalid location range. + let mut locations = loclists + .locations( + LocationListsOffset(0x0), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Ok(None)); + + // An invalid location range after wrapping. + let mut locations = loclists + .locations( + LocationListsOffset(14), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Ok(None)); + + // An invalid offset. + match loclists.locations( + LocationListsOffset(buf.len() + 1), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_get_offset() { + for format in [Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version: 5, + address_size: 4, + }; + + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(encoding.version) + .D8(encoding.address_size) + .D8(0) + .D32(20) + .mark(&first); + for i in 0..20 { + section = section.word(format.word_size(), 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + let section = section.get_contents().unwrap(); + + let debug_loc = DebugLoc::from(EndianSlice::new(&[], LittleEndian)); + let debug_loclists = DebugLocLists::from(EndianSlice::new(§ion, LittleEndian)); + let locations = LocationLists::new(debug_loc, debug_loclists); + + let base = DebugLocListsBase((&first - &zero) as usize); + assert_eq!( + locations.get_offset(encoding, base, DebugLocListsIndex(0)), + Ok(LocationListsOffset(base.0 + 1000)) + ); + assert_eq!( + locations.get_offset(encoding, base, DebugLocListsIndex(19)), + Ok(LocationListsOffset(base.0 + 1019)) + ); + } + } + + #[test] + fn test_loclists_gnu_v4_split_dwarf() { + #[rustfmt::skip] + let buf = [ + 0x03, // DW_LLE_startx_length + 0x00, // ULEB encoded b7 + 0x08, 0x00, 0x00, 0x00, // Fixed 4 byte length of 8 + 0x03, 0x00, // Fixed two byte length of the location + 0x11, 0x00, // DW_OP_constu 0 + 0x9f, // DW_OP_stack_value + // Padding data + //0x99, 0x99, 0x99, 0x99 + ]; + let data_buf = [0x11, 0x00, 0x9f]; + let expected_data = EndianSlice::new(&data_buf, LittleEndian); + let debug_loc = DebugLoc::new(&buf, LittleEndian); + let debug_loclists = DebugLocLists::new(&[], LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let debug_addr = + &DebugAddr::from(EndianSlice::new(&[0x01, 0x02, 0x03, 0x04], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + + // An invalid location range. + let mut locations = loclists + .locations_dwo( + LocationListsOffset(0x0), + encoding, + 0, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0403_0201, + end: 0x0403_0209 + }, + data: Expression(expected_data), + })) + ); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/lookup.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/lookup.rs new file mode 100644 index 0000000..1d082f2 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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/chromium_crates_io/vendor/gimli-v0_32/src/read/macros.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/macros.rs new file mode 100644 index 0000000..b723abc --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/macros.rs
@@ -0,0 +1,872 @@ +use crate::common::{DebugMacinfoOffset, SectionId}; +use crate::endianity::Endianity; +use crate::read::{EndianSlice, Reader, ReaderOffset, Section, UnitRef}; +use crate::{ + constants, DebugLineOffset, DebugMacroOffset, DebugStrOffset, DebugStrOffsetsIndex, DwMacinfo, + DwMacro, Error, Format, Result, +}; + +/// The raw contents of the `.debug_macinfo` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugMacinfo<R> { + pub(crate) section: R, +} + +impl<'input, Endian> DebugMacinfo<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugMacinfo` instance from the data in the `.debug_macinfo` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_macinfo` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugMacinfo, LittleEndian}; + /// + /// # let buf = [1, 0, 95, 95, 83, 84, 68, 67, 95, 95, 32, 49, 0]; + /// # let read_section_somehow = || &buf; + /// let debug_str = DebugMacinfo::new(read_section_somehow(), LittleEndian); + /// ``` + pub fn new(macinfo_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(macinfo_section, endian)) + } +} + +impl<R: Reader> DebugMacinfo<R> { + /// Look up a macro reference the `.debug_macinfo` section by DebugMacinfoOffset. + /// + /// A macinfo offset points to a list of macro information entries in the `.debug_macinfo` section. + /// To handle this, the function returns an iterator. + /// + /// ``` + /// use gimli::{DebugMacinfo, DebugMacinfoOffset, LittleEndian}; + /// + /// # fn main() -> Result<(), gimli::Error> { + /// # let buf = [1, 0, 95, 95, 83, 84, 68, 67, 95, 95, 32, 49, 0, 0]; + /// # let offset = DebugMacinfoOffset(0); + /// # let read_section_somehow = || &buf; + /// # let debug_macinfo_offset_somehow = || offset; + /// let debug_macinfo = DebugMacinfo::new(read_section_somehow(), LittleEndian); + /// let mut iter = debug_macinfo.get_macinfo(debug_macinfo_offset_somehow())?; + /// while let Some(macinfo) = iter.next()? { + /// println!("Found macro info {:?}", macinfo); + /// } + /// # Ok(()) } + /// ``` + pub fn get_macinfo(&self, offset: DebugMacinfoOffset<R::Offset>) -> Result<MacroIter<R>> { + let mut input = self.section.clone(); + input.skip(offset.0)?; + Ok(MacroIter { + input, + format: Format::Dwarf32, + is_macro: false, + }) + } +} + +impl<T> DebugMacinfo<T> { + /// Create a `DebugMacinfo` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfSections::borrow`. + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugMacinfo<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +impl<R> Section<R> for DebugMacinfo<R> { + fn id() -> SectionId { + SectionId::DebugMacinfo + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugMacinfo<R> { + fn from(macinfo_section: R) -> Self { + DebugMacinfo { + section: macinfo_section, + } + } +} + +/// The raw contents of the `.debug_macro` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugMacro<R> { + pub(crate) section: R, +} + +impl<'input, Endian> DebugMacro<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugMacro` instance from the data in the `.debug_macro` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_macro` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugMacro, LittleEndian}; + /// + /// # let buf = [1, 0, 95, 95, 83, 84, 68, 67, 95, 95, 32, 49, 0]; + /// # let read_section_somehow = || &buf; + /// let debug_str = DebugMacro::new(read_section_somehow(), LittleEndian); + /// ``` + pub fn new(macro_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(macro_section, endian)) + } +} + +impl<R: Reader> DebugMacro<R> { + /// Look up a macro reference the `.debug_macinfo` section by DebugMacroOffset. + /// + /// A macinfo offset points to a list of macro information entries in the `.debug_macinfo` section. + /// To handle this, the function returns an iterator. + /// + /// ``` + /// use gimli::{DebugMacro, DebugMacroOffset, LittleEndian}; + /// + /// # fn main() -> Result<(), gimli::Error> { + /// # let buf = [0x05, 0x00, 0x00, 0x01, 0x00, 0x5f, 0x5f, 0x53, 0x54, 0x44, 0x43, 0x5f, 0x5f, 0x20, 0x31, 0x00, 0x00]; + /// # let offset = DebugMacroOffset(0); + /// # let read_section_somehow = || &buf; + /// # let debug_macro_offset_somehow = || offset; + /// let debug_macro = DebugMacro::new(read_section_somehow(), LittleEndian); + /// let mut iter = debug_macro.get_macros(debug_macro_offset_somehow())?; + /// while let Some(cur_macro) = iter.next()? { + /// println!("Found macro info {:?}", cur_macro); + /// } + /// # Ok(()) } + /// ``` + pub fn get_macros(&self, offset: DebugMacroOffset<R::Offset>) -> Result<MacroIter<R>> { + let mut input = self.section.clone(); + input.skip(offset.0)?; + let header = MacroUnitHeader::parse(&mut input)?; + Ok(MacroIter { + input, + format: header.format(), + is_macro: true, + }) + } +} + +impl<T> DebugMacro<T> { + /// Create a `DebugMacro` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfSections::borrow`. + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugMacro<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +impl<R> Section<R> for DebugMacro<R> { + fn id() -> SectionId { + SectionId::DebugMacro + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugMacro<R> { + fn from(macro_section: R) -> Self { + DebugMacro { + section: macro_section, + } + } +} + +#[derive(Debug, Clone)] +struct MacroUnitHeader<R: Reader> { + /// The version of the macro unit header. At the moment only version 5 is defined. + _version: u16, + flags: u8, + _debug_line_offset: DebugLineOffset<R::Offset>, +} + +impl<R: Reader> MacroUnitHeader<R> { + const OFFSET_SIZE_FLAG: u8 = 0b0000_0001; + const DEBUG_LINE_OFFSET_FLAG: u8 = 0b0000_0010; + const OPCODE_OPERANDS_TABLE_FLAG: u8 = 0b0000_0100; + + fn parse(input: &mut R) -> Result<Self> { + let version = input.read_u16()?; + let flags = input.read_u8()?; + let format = if flags & Self::OFFSET_SIZE_FLAG == 0 { + Format::Dwarf32 + } else { + Format::Dwarf64 + }; + let _debug_line_offset = if flags & Self::DEBUG_LINE_OFFSET_FLAG != 0 { + DebugLineOffset(input.read_offset(format)?) + } else { + DebugLineOffset(R::Offset::from_u64(0)?) + }; + // if the opcode operands table flag is set, there is a table in the header which currently isn't parsed + if flags & Self::OPCODE_OPERANDS_TABLE_FLAG != 0 { + return Err(Error::UnsupportedOpcodeOperandsTable); + } + Ok(MacroUnitHeader { + _version: version, + flags, + _debug_line_offset, + }) + } + + fn format(&self) -> Format { + if self.flags & Self::OFFSET_SIZE_FLAG == 0 { + Format::Dwarf32 + } else { + Format::Dwarf64 + } + } +} + +/// A string in a macro entry. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MacroString<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// The string is directly embedded in the macro entry + Direct(R), + /// The macro refers to a string in the `.debug_str` section using a `DebugStrOffset`. + StringPointer(DebugStrOffset<Offset>), + /// The macro contains an index into an array in the `.debug_str_offsets` + /// section, which refers to a string in the `.debug_str` section. + IndirectStringPointer(DebugStrOffsetsIndex<Offset>), + /// The macro refers to a string in the `.debug_str` section in the supplementary object file + Supplementary(DebugStrOffset<Offset>), +} + +impl<R: Reader> MacroString<R> { + /// Get the string slice from the macro entry. + pub fn string(&self, unit: UnitRef<'_, R>) -> Result<R> { + match self { + MacroString::Direct(s) => Ok(s.clone()), + MacroString::StringPointer(offset) => unit.string(*offset), + MacroString::IndirectStringPointer(index) => { + let str_offset = unit.string_offset(*index)?; + unit.string(str_offset) + } + MacroString::Supplementary(offset) => unit.sup_string(*offset), + } + } +} + +/// an Entry in the `.debug_macro` section. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MacroEntry<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// A macro definition. + Define { + /// The line number where the macro is defined. + line: u64, + /// The text of the macro: The name of the macro followed immediately by any formal + /// parameters including the surrounding parentheses, followed by the macro definition. + text: MacroString<R>, + }, + /// A macro undefinition. + Undef { + /// The line number where the macro is undefined. + line: u64, + /// The name of the macro without the definition. + name: MacroString<R>, + }, + /// The start of a file. + StartFile { + /// Line number of the source file on which the inclusion macro directive occurred. + line: u64, + /// An index into the line number table of the compilation unit. + file: u64, + }, + /// The end of the current included file. + EndFile, + /// import a macro unit + Import { + /// offset of the macro unit in the `.debug_macro` section + offset: DebugMacroOffset<Offset>, + }, + /// import a macro unit from the supplementary object file + ImportSup { + /// offset of the macro unit in the `.debug_macro` section of the supplementary object file + offset: DebugMacroOffset<Offset>, + }, + /// A vendor-specific extension. + VendorExt { + /// A numeric constant, whose meaning is vendor specific. + numeric: u64, + /// A string whose meaning is vendor specific. + string: R, + }, +} + +/// Iterator over the entries in the `.debug_macro` section. +#[derive(Clone, Debug)] +pub struct MacroIter<R: Reader> { + input: R, + format: Format, + is_macro: bool, +} + +impl<R: Reader> MacroIter<R> { + /// Advance the iterator to the next entry in the `.debug_macro` section. + pub fn next(&mut self) -> Result<Option<MacroEntry<R>>> { + // DW_MACINFO_* and DW_MACRO_* have the same values, so we can use the same parsing logic. + let macro_type = DwMacro(self.input.read_u8()?); + match macro_type { + DwMacro(0) => { + self.input.empty(); + Ok(None) + } + constants::DW_MACRO_define => { + let line = self.input.read_uleb128()?; + let text = self.input.read_null_terminated_slice()?; + Ok(Some(MacroEntry::Define { + line, + text: MacroString::Direct(text), + })) + } + constants::DW_MACRO_undef => { + let line = self.input.read_uleb128()?; + let name = self.input.read_null_terminated_slice()?; + Ok(Some(MacroEntry::Undef { + line, + name: MacroString::Direct(name), + })) + } + constants::DW_MACRO_start_file => { + let line = self.input.read_uleb128()?; + let file = self.input.read_uleb128()?; + Ok(Some(MacroEntry::StartFile { line, file })) + } + constants::DW_MACRO_end_file => Ok(Some(MacroEntry::EndFile)), + constants::DW_MACRO_define_strp if self.is_macro => { + let line = self.input.read_uleb128()?; + let text_offset = DebugStrOffset(self.input.read_offset(self.format)?); + Ok(Some(MacroEntry::Define { + line, + text: MacroString::StringPointer(text_offset), + })) + } + constants::DW_MACRO_undef_strp if self.is_macro => { + let line = self.input.read_uleb128()?; + let name_offset = DebugStrOffset(self.input.read_offset(self.format)?); + Ok(Some(MacroEntry::Undef { + line, + name: MacroString::StringPointer(name_offset), + })) + } + constants::DW_MACRO_import if self.is_macro => { + let offset = DebugMacroOffset(self.input.read_offset(self.format)?); + Ok(Some(MacroEntry::Import { offset })) + } + constants::DW_MACRO_define_sup if self.is_macro => { + let line = self.input.read_uleb128()?; + let text_offset = DebugStrOffset(self.input.read_offset(self.format)?); + Ok(Some(MacroEntry::Define { + line, + text: MacroString::Supplementary(text_offset), + })) + } + constants::DW_MACRO_undef_sup if self.is_macro => { + let line = self.input.read_uleb128()?; + let name_offset = DebugStrOffset(self.input.read_offset(self.format)?); + Ok(Some(MacroEntry::Undef { + line, + name: MacroString::Supplementary(name_offset), + })) + } + constants::DW_MACRO_import_sup if self.is_macro => { + let offset = DebugMacroOffset(self.input.read_offset(self.format)?); + Ok(Some(MacroEntry::ImportSup { offset })) + } + constants::DW_MACRO_define_strx if self.is_macro => { + let line = self.input.read_uleb128()?; + let index = self.input.read_uleb128().and_then(R::Offset::from_u64)?; + let text_index = DebugStrOffsetsIndex(index); + Ok(Some(MacroEntry::Define { + line, + text: MacroString::IndirectStringPointer(text_index), + })) + } + constants::DW_MACRO_undef_strx if self.is_macro => { + let line = self.input.read_uleb128()?; + let index = self.input.read_uleb128().and_then(R::Offset::from_u64)?; + let name_index = DebugStrOffsetsIndex(index); + Ok(Some(MacroEntry::Undef { + line, + name: MacroString::IndirectStringPointer(name_index), + })) + } + _ => { + if self.is_macro { + self.input.empty(); + Err(Error::InvalidMacroType(macro_type)) + } else if macro_type.0 == constants::DW_MACINFO_vendor_ext.0 { + let numeric = self.input.read_uleb128()?; + let string = self.input.read_null_terminated_slice()?; + Ok(Some(MacroEntry::VendorExt { numeric, string })) + } else { + self.input.empty(); + Err(Error::InvalidMacinfoType(DwMacinfo(macro_type.0))) + } + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for MacroIter<R> { + type Item = MacroEntry<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Error> { + MacroIter::next(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{test_util::GimliSectionMethods, DebugStr, LittleEndian}; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_get_macinfo() { + let position = Label::new(); + + // Create a test section with some macinfo entries + let section = Section::with_endian(Endian::Little) + .set_start_const(0) + .mark(&position) + .D8(crate::DW_MACINFO_define.0) + .uleb(0) // line number: 0 - defined on the compiler command line + .append_bytes(b"__STDC__ 1\0") + .D8(crate::DW_MACINFO_define.0) + .uleb(1) // line number: 1 - defined in the source file + .append_bytes(b"__GNUC__ 1\0") + .D8(crate::DW_MACINFO_undef.0) + .uleb(2) // line number: 2 - undefined in the source file + .append_bytes(b"__GNUC__\0") + .D8(crate::DW_MACINFO_start_file.0) + .uleb(3) // line number: 3 - start of file + .uleb(4) // file number index: 4 - index into the line number table + .D8(crate::DW_MACINFO_end_file.0) // end of file + .D8(crate::DW_MACINFO_vendor_ext.0) + .uleb(5) // numeric constant: 5 - vendor specific + .append_bytes(b"foo\0") + .D8(0); // end of unit + + // Create a DebugMacinfo instance from the section + let section = section.get_contents().unwrap(); + let debug_macinfo = DebugMacinfo::from(EndianSlice::new(§ion, LittleEndian)); + + let offset = position.value().unwrap() as usize; + + let mut iter = debug_macinfo + .get_macinfo(DebugMacinfoOffset(offset)) + .unwrap(); + + // Test getting macinfo entries + let entry = iter.next().unwrap().unwrap(); + assert!( + matches!(entry, MacroEntry::Define { line: 0, text: MacroString::Direct(text) } if text.slice() == b"__STDC__ 1") + ); + + let entry = iter.next().unwrap().unwrap(); + assert!( + matches!(entry, MacroEntry::Define { line: 1, text: MacroString::Direct(text) } if text.slice() == b"__GNUC__ 1") + ); + + let entry = iter.next().unwrap().unwrap(); + assert!( + matches!(entry, MacroEntry::Undef { line: 2, name: MacroString::Direct(name) } if name.slice() == b"__GNUC__") + ); + + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 3, file: 4 })); + + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + + let entry = iter.next().unwrap().unwrap(); + assert!( + matches!(entry, MacroEntry::VendorExt { numeric: 5, string } if string.slice() == b"foo") + ); + + assert_eq!(iter.next(), Ok(None)); + } + + #[test] + fn get_macros_1() { + let position = Label::new(); + + // The test data is originally from the DWARF v5 standard, appendix D.16 + // 1) Figure D.71, simple DWARF encoding + let section = Section::with_endian(Endian::Little) + .set_start_const(0) + .mark(&position) + .D16(5) // Dwarf version + .D8(0b0000_0010) // Flags: offset_size = 0 (32-bit), debug_line_offset = 1, opcode_operands_table = 0 + .D32(0) // debug line offset + .D8(crate::DW_MACRO_start_file.0) // start file: "a.c" + .uleb(0) // line number + .uleb(0) // file number + .D8(crate::DW_MACRO_start_file.0) // start file: "a.h" + .uleb(1) // line number + .uleb(1) // file number + .D8(crate::DW_MACRO_define.0) // define + .uleb(1) // line number + .append_bytes(b"LONGER_MACRO 1\0") // macro name + .D8(crate::DW_MACRO_define.0) // define + .uleb(2) // line number + .append_bytes(b"B 2\0") // macro name + .D8(crate::DW_MACRO_start_file.0) // start file: "b.h" + .uleb(3) // line number + .uleb(2) // file number + .D8(crate::DW_MACRO_undef.0) // undef + .uleb(1) // line number + .append_bytes(b"B\0") // macro name + .D8(crate::DW_MACRO_define.0) // define + .uleb(2) // line number + .append_bytes(b"D 3\0") // macro name + .D8(crate::DW_MACRO_define.0) // define + .uleb(3) // line number + .append_bytes(b"FUNCTION_LIKE_MACRO(x) 4+x\0") // macro name + .D8(crate::DW_MACRO_end_file.0) // end file: "b.h" -> "a.h" + .D8(crate::DW_MACRO_define.0) // define + .uleb(4) // line number + .append_bytes(b"B 3\0") // macro name + .D8(crate::DW_MACRO_end_file.0) // end file: "a.h" -> "a.c" + .D8(crate::DW_MACRO_define.0) // define + .uleb(2) // line number + .append_bytes(b"FUNCTION_LIKE_MACRO(x) 4+x\0") // macro name + .D8(crate::DW_MACRO_start_file.0) // start file: "b.h" + .uleb(3) // line number + .uleb(2) // file number + .D8(crate::DW_MACRO_undef.0) // undef + .uleb(1) // line number + .append_bytes(b"B\0") // macro name + .D8(crate::DW_MACRO_define.0) // define + .uleb(2) // line number + .append_bytes(b"D 3\0") // macro name + .D8(crate::DW_MACRO_define.0) // define + .uleb(3) // line number + .append_bytes(b"FUNCTION_LIKE_MACRO(x) 4+x\0") // macro name + .D8(crate::DW_MACRO_end_file.0) // end file: "b.h" -> "a.c" + .D8(crate::DW_MACRO_end_file.0) // end file: "a.c" -> "" + .D8(0); // end of unit + + // Create a DebugMacro instance from the section + let section = section.get_contents().unwrap(); + let debug_macro = DebugMacro::from(EndianSlice::new(§ion, LittleEndian)); + + let offset = position.value().unwrap() as usize; + + let mut iter = debug_macro.get_macros(DebugMacroOffset(offset)).unwrap(); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 0, file: 0 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 1, file: 1 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 1, text: MacroString::Direct(text) + } if text.slice() == b"LONGER_MACRO 1" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::Direct(text) + } if text.slice() == b"B 2" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 3, file: 2 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Undef { + line: 1, name: MacroString::Direct(name) + } if name.slice() == b"B" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::Direct(text) + } if text.slice() == b"D 3" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 3, text: MacroString::Direct(text) + } if text.slice() == b"FUNCTION_LIKE_MACRO(x) 4+x" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 4, text: MacroString::Direct(text) + } if text.slice() == b"B 3" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::Direct(text) + } if text.slice() == b"FUNCTION_LIKE_MACRO(x) 4+x" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 3, file: 2 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Undef { + line: 1, name: MacroString::Direct(name) + } if name.slice() == b"B" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::Direct(text) + } if text.slice() == b"D 3" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 3, text: MacroString::Direct(text) + } if text.slice() == b"FUNCTION_LIKE_MACRO(x) 4+x" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + assert_eq!(iter.next(), Ok(None)); + } + + #[test] + fn get_macros_2() { + let str_0 = Label::new(); + let str_1 = Label::new(); + let macro_unit_0 = Label::new(); + let macro_unit_1 = Label::new(); + let macro_unit_2 = Label::new(); + + // The test data is originally from the DWARF v5 standard, appendix D.16 + // 2) Figure D.72, shareable DWARF encoding + let str_section = Section::with_endian(Endian::Little) + .set_start_const(0) + .mark(&str_0) + .append_bytes(b"FUNCTION_LIKE_MACRO(x) 4+x\0") // macro name + .mark(&str_1) + .append_bytes(b"LONGER_MACRO 1\0"); // macro name + + let macro_section = Section::with_endian(Endian::Little) + .set_start_const(0) + //--------------------unit 0---------------------- + .mark(¯o_unit_0) // start of unit 0 + .D16(5) // Dwarf version + .D8(0b0000_0010) // Flags: offset_size = 0 (32-bit), debug_line_offset = 1, opcode_operands_table = 0 + .D32(0) // debug line offset + .D8(crate::DW_MACRO_start_file.0) // start file: "a.c" + .uleb(0) // line number + .uleb(0) // file number + .D8(crate::DW_MACRO_start_file.0) // start file: "a.h" + .uleb(1) // line number + .uleb(1) // file number + .D8(crate::DW_MACRO_import.0) // import unit 1 + .L32(macro_unit_1.clone()) // debug line offset to unit 1 + .D8(crate::DW_MACRO_start_file.0) // start file: "b.h" + .uleb(3) // line number + .uleb(2) // file number + .D8(crate::DW_MACRO_import.0) // import unit 2 + .L32(macro_unit_2.clone()) // debug line offset to unit 2 + .D8(crate::DW_MACRO_end_file.0) // end file: "b.h" -> "a.h" + .D8(crate::DW_MACRO_define.0) // define + .uleb(4) // line number + .append_bytes(b"B 3\0") // macro name + .D8(crate::DW_MACRO_end_file.0) // end file: "a.h" -> "a.c" + .D8(crate::DW_MACRO_define_strp.0) // define: "FUNCTION_LIKE_MACRO(x) 4+x" + .uleb(2) // line number + .D32(0) // macro name offset in the string table + .D8(crate::DW_MACRO_start_file.0) // start file: "b.h" + .uleb(3) // line number + .uleb(2) // file number + .D8(crate::DW_MACRO_import.0) // import unit 2 + .L32(¯o_unit_2) // debug line offset to unit 2 + .D8(crate::DW_MACRO_end_file.0) // end file: "b.h" -> "a.c" + .D8(crate::DW_MACRO_end_file.0) // end file: "a.c" -> "" + .D8(0) + //--------------------unit 1---------------------- + .mark(¯o_unit_1) // start of unit 1 + .D16(5) // Dwarf version + .D8(0b0000_0000) // Flags: offset_size = 0 (32-bit), debug_line_offset = 0, opcode_operands_table = 0 + .D8(crate::DW_MACRO_define_strp.0) // define strp: "LONGER_MACRO 1" + .uleb(1) // line number + .L32(str_0.clone()) // macro name offset in the string table + .D8(crate::DW_MACRO_define.0) // define: "B 2" + .uleb(2) // line number + .append_bytes(b"B 2\0") // macro name + .D8(0) // end of unit + //---------------------unit 2--------------------- + .mark(¯o_unit_2) // start of unit 2 + .D16(5) // Dwarf version + .D8(0b0000_0000) // Flags: offset_size = 0 (32-bit), debug_line_offset = 0, opcode_operands_table = 0 + .D8(crate::DW_MACRO_undef.0) // undef: "B" + .uleb(1) // line number + .append_bytes(b"B\0") // macro name + .D8(crate::DW_MACRO_define.0) // define: "D 3" + .uleb(2) // line number + .append_bytes(b"D 3\0") // macro name + .D8(crate::DW_MACRO_define_strp.0) // define strp: "FUNCTION_LIKE_MACRO(x) 4+x" + .uleb(2) // line number + .L32(str_1.clone()) // macro name offset in the string table + .D8(0); // end of unit + + // Create a DebugMacro instance from the section + let str_section = str_section.get_contents().unwrap(); + let debug_str = DebugStr::from(EndianSlice::new(&str_section, LittleEndian)); + + // Create a DebugMacro instance from the section + let macro_section = macro_section.get_contents().unwrap(); + let debug_macro = DebugMacro::from(EndianSlice::new(¯o_section, LittleEndian)); + + // check the content of macro unit 0 + let offset = macro_unit_0.value().unwrap() as usize; + let mut iter = debug_macro.get_macros(DebugMacroOffset(offset)).unwrap(); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 0, file: 0 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 1, file: 1 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Import { offset } if offset.0 == macro_unit_1.value().unwrap() as usize + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 3, file: 2 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Import { offset } if offset.0 == macro_unit_2.value().unwrap() as usize + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 4, text: MacroString::Direct(text) + } if text.slice() == b"B 3" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::StringPointer(text_offset) + } if text_offset.0 == str_0.value().unwrap() as usize + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::StartFile { line: 3, file: 2 })); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Import { offset } if offset.0 == macro_unit_2.value().unwrap() as usize + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!(entry, MacroEntry::EndFile)); + assert_eq!(iter.next(), Ok(None)); + + // check the content of macro unit 1 + let offset = macro_unit_1.value().unwrap() as usize; + let mut iter = debug_macro.get_macros(DebugMacroOffset(offset)).unwrap(); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 1, text: MacroString::StringPointer(text_offset) + } if text_offset.0 == str_0.value().unwrap() as usize + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::Direct(text) + } if text.slice() == b"B 2" + )); + assert_eq!(iter.next(), Ok(None)); + + // check the content of macro unit 2 + let offset = macro_unit_2.value().unwrap() as usize; + let mut iter = debug_macro.get_macros(DebugMacroOffset(offset)).unwrap(); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Undef { + line: 1, name: MacroString::Direct(name) + } if name.slice() == b"B" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::Direct(text) + } if text.slice() == b"D 3" + )); + let entry = iter.next().unwrap().unwrap(); + assert!(matches!( + entry, + MacroEntry::Define { + line: 2, text: MacroString::StringPointer(text_offset) + } if text_offset.0 == str_1.value().unwrap() as usize + )); + assert_eq!(iter.next(), Ok(None)); + + // check the content of the string table + let text_offset = DebugStrOffset(str_0.value().unwrap() as usize); + assert_eq!( + debug_str.get_str(text_offset).unwrap().slice(), + b"FUNCTION_LIKE_MACRO(x) 4+x" + ); + let text_offset = DebugStrOffset(str_1.value().unwrap() as usize); + assert_eq!( + debug_str.get_str(text_offset).unwrap().slice(), + b"LONGER_MACRO 1" + ); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/mod.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/mod.rs new file mode 100644 index 0000000..0aaa766 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/mod.rs
@@ -0,0 +1,868 @@ +//! 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 `.debug_info` parser](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/simple.rs) +//! +//! * [A simple `.debug_line` parser](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/simple_line.rs) +//! +//! * [A `dwarfdump` +//! clone](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/dwarfdump.rs) +//! +//! * [An `addr2line` clone](https://github.com/gimli-rs/addr2line) +//! +//! * [`ddbug`](https://github.com/gimli-rs/ddbug), a utility giving insight into +//! code generation by making debugging information readable +//! +//! * [`dwprod`](https://github.com/fitzgen/dwprod), a tiny utility to list the +//! compilers used to create each compilation unit within a shared library or +//! executable (via `DW_AT_producer`) +//! +//! * [`dwarf-validate`](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/dwarf-validate.rs), +//! a program to validate the integrity of some DWARF and its references +//! between sections and compilation units. +//! +//! ## API Structure +//! +//! * Basic familiarity with DWARF is assumed. +//! +//! * The [`Dwarf`](./struct.Dwarf.html) type contains the commonly used DWARF +//! sections. It has methods that simplify access to debugging data that spans +//! multiple sections. Use of this type is optional, but recommended. +//! +//! * The [`DwarfPackage`](./struct.Dwarf.html) type contains the DWARF +//! package (DWP) sections. It has methods to find a DWARF object (DWO) +//! within the package. +//! +//! * Each section gets its own type. Consider these types the entry points to +//! the library: +//! +//! * [`DebugAbbrev`](./struct.DebugAbbrev.html): The `.debug_abbrev` section. +//! +//! * [`DebugAddr`](./struct.DebugAddr.html): The `.debug_addr` section. +//! +//! * [`DebugAranges`](./struct.DebugAranges.html): The `.debug_aranges` +//! section. +//! +//! * [`DebugFrame`](./struct.DebugFrame.html): The `.debug_frame` section. +//! +//! * [`DebugInfo`](./struct.DebugInfo.html): The `.debug_info` section. +//! +//! * [`DebugLine`](./struct.DebugLine.html): The `.debug_line` section. +//! +//! * [`DebugLineStr`](./struct.DebugLineStr.html): The `.debug_line_str` section. +//! +//! * [`DebugLoc`](./struct.DebugLoc.html): The `.debug_loc` section. +//! +//! * [`DebugLocLists`](./struct.DebugLocLists.html): The `.debug_loclists` section. +//! +//! * [`DebugPubNames`](./struct.DebugPubNames.html): The `.debug_pubnames` +//! section. +//! +//! * [`DebugPubTypes`](./struct.DebugPubTypes.html): The `.debug_pubtypes` +//! section. +//! +//! * [`DebugRanges`](./struct.DebugRanges.html): The `.debug_ranges` section. +//! +//! * [`DebugRngLists`](./struct.DebugRngLists.html): The `.debug_rnglists` section. +//! +//! * [`DebugStr`](./struct.DebugStr.html): The `.debug_str` section. +//! +//! * [`DebugStrOffsets`](./struct.DebugStrOffsets.html): The `.debug_str_offsets` section. +//! +//! * [`DebugTypes`](./struct.DebugTypes.html): The `.debug_types` section. +//! +//! * [`DebugCuIndex`](./struct.DebugCuIndex.html): The `.debug_cu_index` section. +//! +//! * [`DebugTuIndex`](./struct.DebugTuIndex.html): The `.debug_tu_index` section. +//! +//! * [`EhFrame`](./struct.EhFrame.html): The `.eh_frame` section. +//! +//! * [`EhFrameHdr`](./struct.EhFrameHdr.html): The `.eh_frame_hdr` section. +//! +//! * Each section type exposes methods for accessing the debugging data encoded +//! in that section. For example, the [`DebugInfo`](./struct.DebugInfo.html) +//! struct has the [`units`](./struct.DebugInfo.html#method.units) method for +//! iterating over the compilation units defined within it. +//! +//! * Offsets into a section are strongly typed: an offset into `.debug_info` is +//! the [`DebugInfoOffset`](./struct.DebugInfoOffset.html) type. It cannot be +//! used to index into the [`DebugLine`](./struct.DebugLine.html) type because +//! `DebugLine` represents the `.debug_line` section. There are similar types +//! for offsets relative to a compilation unit rather than a section. +//! +//! ## Using with `FallibleIterator` +//! +//! The standard library's `Iterator` trait and related APIs do not play well +//! with iterators where the `next` operation is fallible. One can make the +//! `Iterator`'s associated `Item` type be a `Result<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::*; + +mod relocate; +pub use self::relocate::*; + +#[cfg(feature = "read")] +mod abbrev; +#[cfg(feature = "read")] +pub use self::abbrev::*; + +mod aranges; +pub use self::aranges::*; + +mod index; +pub use self::index::*; + +#[cfg(feature = "read")] +mod line; +#[cfg(feature = "read")] +pub use self::line::*; + +mod lists; + +mod loclists; +pub use self::loclists::*; + +#[cfg(feature = "read")] +mod lookup; + +#[cfg(feature = "read")] +mod macros; +#[cfg(feature = "read")] +pub use self::macros::*; + +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)] +#[non_exhaustive] +pub enum Error { + /// An I/O error occurred while reading. + Io, + /// Found a PC relative pointer, but the section base is undefined. + PcRelativePointerButSectionBaseIsUndefined, + /// Found a `.text` relative pointer, but the `.text` base is undefined. + TextRelativePointerButTextBaseIsUndefined, + /// Found a data relative pointer, but the data base is undefined. + DataRelativePointerButDataBaseIsUndefined, + /// Found a function relative pointer in a context that does not have a + /// function base. + FuncRelativePointerInBadContext, + /// Cannot parse a pointer with a `DW_EH_PE_omit` encoding. + CannotParseOmitPointerEncoding, + /// An error parsing an unsigned LEB128 value. + BadUnsignedLeb128, + /// An error parsing a signed LEB128 value. + BadSignedLeb128, + /// An abbreviation declared that its tag is zero, but zero is reserved for + /// null records. + AbbreviationTagZero, + /// An attribute specification declared that its form is zero, but zero is + /// reserved for null records. + AttributeFormZero, + /// The abbreviation's has-children byte was not one of + /// `DW_CHILDREN_{yes,no}`. + BadHasChildren, + /// The specified length is impossible. + BadLength, + /// Found an unknown `DW_FORM_*` type. + UnknownForm(constants::DwForm), + /// Expected a zero, found something else. + ExpectedZero, + /// Found an abbreviation code that has already been used. + DuplicateAbbreviationCode, + /// Found a duplicate arange. + DuplicateArange, + /// Found an unknown reserved length value. + UnknownReservedLength, + /// Found an unknown DWARF version. + UnknownVersion(u64), + /// Found a record with an unknown abbreviation code. + UnknownAbbreviation(u64), + /// Hit the end of input before it was expected. + UnexpectedEof(ReaderOffsetId), + /// Read a null entry before it was expected. + UnexpectedNull, + /// Found an unknown standard opcode. + UnknownStandardOpcode(constants::DwLns), + /// Found an unknown extended opcode. + UnknownExtendedOpcode(constants::DwLne), + /// Found an unknown location-lists format. + UnknownLocListsEntry(constants::DwLle), + /// Found an unknown range-lists format. + UnknownRangeListsEntry(constants::DwRle), + /// The specified address size is not supported. + UnsupportedAddressSize(u8), + /// The specified offset size is not supported. + UnsupportedOffsetSize(u8), + /// The specified field size is not supported. + UnsupportedFieldSize(u8), + /// The minimum instruction length must not be zero. + MinimumInstructionLengthZero, + /// The maximum operations per instruction must not be zero. + MaximumOperationsPerInstructionZero, + /// The line range must not be zero. + LineRangeZero, + /// The opcode base must not be zero. + OpcodeBaseZero, + /// Found an invalid UTF-8 string. + BadUtf8, + /// Expected to find the CIE ID, but found something else. + NotCieId, + /// Expected to find a pointer to a CIE, but found the CIE ID instead. + NotCiePointer, + /// Expected to find a pointer to an FDE, but found a CIE instead. + NotFdePointer, + /// Invalid branch target for a DW_OP_bra or DW_OP_skip. + BadBranchTarget(u64), + /// DW_OP_push_object_address used but no address passed in. + InvalidPushObjectAddress, + /// Not enough items on the stack when evaluating an expression. + NotEnoughStackItems, + /// Too many iterations to compute the expression. + TooManyIterations, + /// An unrecognized operation was found while parsing a DWARF + /// expression. + InvalidExpression(constants::DwOp), + /// An unsupported operation was found while evaluating a DWARF expression. + UnsupportedEvaluation, + /// The expression had a piece followed by an expression + /// terminator without a piece. + InvalidPiece, + /// An expression-terminating operation was followed by something + /// other than the end of the expression or a piece operation. + InvalidExpressionTerminator(u64), + /// Division or modulus by zero when evaluating an expression. + DivisionByZero, + /// An expression operation used mismatching types. + TypeMismatch, + /// An expression operation required an integral type but saw a + /// floating point type. + IntegralTypeRequired, + /// An expression operation used types that are not supported. + UnsupportedTypeOperation, + /// The shift value in an expression must be a non-negative integer. + InvalidShiftExpression, + /// The size of a deref expression must not be larger than the size of an address. + InvalidDerefSize(u8), + /// An unknown DW_CFA_* instruction. + UnknownCallFrameInstruction(constants::DwCfa), + /// The end of an address range was before the beginning. + InvalidAddressRange, + /// An address calculation overflowed. + /// + /// This is returned in cases where the address is expected to be + /// larger than a previous address, but the calculation overflowed. + AddressOverflow, + /// Encountered a call frame instruction in a context in which it is not + /// valid. + CfiInstructionInInvalidContext, + /// When evaluating call frame instructions, found a `DW_CFA_restore_state` + /// stack pop instruction, but the stack was empty, and had nothing to pop. + PopWithEmptyStack, + /// Do not have unwind info for the given address. + NoUnwindInfoForAddress, + /// An offset value was larger than the maximum supported value. + UnsupportedOffset, + /// The given pointer encoding is either unknown or invalid. + UnknownPointerEncoding(constants::DwEhPe), + /// Did not find an entry at the given offset. + NoEntryAtGivenOffset, + /// The given offset is out of bounds. + OffsetOutOfBounds, + /// Found an unknown CFI augmentation. + UnknownAugmentation, + /// We do not support the given pointer encoding yet. + UnsupportedPointerEncoding, + /// Registers larger than `u16` are not supported. + UnsupportedRegister(u64), + /// The CFI program defined more register rules than we have storage for. + TooManyRegisterRules, + /// Attempted to push onto the CFI or evaluation stack, but it was already + /// at full capacity. + StackFull, + /// The `.eh_frame_hdr` binary search table claims to be variable-length encoded, + /// which makes binary search impossible. + VariableLengthSearchTable, + /// The `DW_UT_*` value for this unit is not supported yet. + UnsupportedUnitType, + /// Ranges using AddressIndex are not supported yet. + UnsupportedAddressIndex, + /// Nonzero segment selector sizes aren't supported yet. + UnsupportedSegmentSize, + /// A compilation unit or type unit is missing its top level DIE. + MissingUnitDie, + /// A DIE attribute used an unsupported form. + UnsupportedAttributeForm, + /// Missing DW_LNCT_path in file entry format. + MissingFileEntryFormatPath, + /// Expected an attribute value to be a string form. + ExpectedStringAttributeValue, + /// `DW_FORM_implicit_const` used in an invalid context. + InvalidImplicitConst, + /// Invalid section count in `.dwp` index. + InvalidIndexSectionCount, + /// Invalid slot count in `.dwp` index. + InvalidIndexSlotCount, + /// Invalid hash row in `.dwp` index. + InvalidIndexRow, + /// Unknown section type in `.dwp` index. + UnknownIndexSection(constants::DwSect), + /// Unknown section type in version 2 `.dwp` index. + UnknownIndexSectionV2(constants::DwSectV2), + /// Invalid macinfo type in `.debug_macinfo`. + InvalidMacinfoType(constants::DwMacinfo), + /// Invalid macro type in `.debug_macro`. + InvalidMacroType(constants::DwMacro), + /// The optional `opcode_operands_table` in `.debug_macro` is currently not supported. + UnsupportedOpcodeOperandsTable, +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> ::core::result::Result<(), fmt::Error> { + write!(f, "{}", self.description()) + } +} + +impl Error { + /// A short description of the error. + pub fn description(&self) -> &str { + match *self { + Error::Io => "An I/O error occurred while reading.", + Error::PcRelativePointerButSectionBaseIsUndefined => { + "Found a PC relative pointer, but the section base is undefined." + } + Error::TextRelativePointerButTextBaseIsUndefined => { + "Found a `.text` relative pointer, but the `.text` base is undefined." + } + Error::DataRelativePointerButDataBaseIsUndefined => { + "Found a data relative pointer, but the data base is undefined." + } + Error::FuncRelativePointerInBadContext => { + "Found a function relative pointer in a context that does not have a function base." + } + Error::CannotParseOmitPointerEncoding => { + "Cannot parse a pointer with a `DW_EH_PE_omit` encoding." + } + Error::BadUnsignedLeb128 => "An error parsing an unsigned LEB128 value", + Error::BadSignedLeb128 => "An error parsing a signed LEB128 value", + Error::AbbreviationTagZero => { + "An abbreviation declared that its tag is zero, + but zero is reserved for null records" + } + Error::AttributeFormZero => { + "An attribute specification declared that its form is zero, + but zero is reserved for null records" + } + Error::BadHasChildren => { + "The abbreviation's has-children byte was not one of + `DW_CHILDREN_{yes,no}`" + } + Error::BadLength => "The specified length is impossible", + Error::UnknownForm(_) => "Found an unknown `DW_FORM_*` type", + Error::ExpectedZero => "Expected a zero, found something else", + Error::DuplicateAbbreviationCode => { + "Found an abbreviation code that has already been used" + } + Error::DuplicateArange => "Found a duplicate arange", + Error::UnknownReservedLength => "Found an unknown reserved length value", + Error::UnknownVersion(_) => "Found an unknown DWARF version", + Error::UnknownAbbreviation(_) => "Found a record with an unknown abbreviation code", + Error::UnexpectedEof(_) => "Hit the end of input before it was expected", + Error::UnexpectedNull => "Read a null entry before it was expected.", + Error::UnknownStandardOpcode(_) => "Found an unknown standard opcode", + Error::UnknownExtendedOpcode(_) => "Found an unknown extended opcode", + Error::UnknownLocListsEntry(_) => "Found an unknown location lists entry", + Error::UnknownRangeListsEntry(_) => "Found an unknown range lists entry", + Error::UnsupportedAddressSize(_) => "The specified address size is not supported", + Error::UnsupportedOffsetSize(_) => "The specified offset size is not supported", + Error::UnsupportedFieldSize(_) => "The specified field size is not supported", + Error::MinimumInstructionLengthZero => { + "The minimum instruction length must not be zero." + } + Error::MaximumOperationsPerInstructionZero => { + "The maximum operations per instruction must not be zero." + } + Error::LineRangeZero => "The line range must not be zero.", + Error::OpcodeBaseZero => "The opcode base must not be zero.", + Error::BadUtf8 => "Found an invalid UTF-8 string.", + Error::NotCieId => "Expected to find the CIE ID, but found something else.", + Error::NotCiePointer => "Expected to find a CIE pointer, but found the CIE ID instead.", + Error::NotFdePointer => { + "Expected to find an FDE pointer, but found a CIE pointer instead." + } + Error::BadBranchTarget(_) => "Invalid branch target in DWARF expression", + Error::InvalidPushObjectAddress => { + "DW_OP_push_object_address used but no object address given" + } + Error::NotEnoughStackItems => "Not enough items on stack when evaluating expression", + Error::TooManyIterations => "Too many iterations to evaluate DWARF expression", + Error::InvalidExpression(_) => "Invalid opcode in DWARF expression", + Error::UnsupportedEvaluation => "Unsupported operation when evaluating expression", + Error::InvalidPiece => { + "DWARF expression has piece followed by non-piece expression at end" + } + Error::InvalidExpressionTerminator(_) => "Expected DW_OP_piece or DW_OP_bit_piece", + Error::DivisionByZero => "Division or modulus by zero when evaluating expression", + Error::TypeMismatch => "Type mismatch when evaluating expression", + Error::IntegralTypeRequired => "Integral type expected when evaluating expression", + Error::UnsupportedTypeOperation => { + "An expression operation used types that are not supported" + } + Error::InvalidShiftExpression => { + "The shift value in an expression must be a non-negative integer." + } + Error::InvalidDerefSize(_) => { + "The size of a deref expression must not be larger than the size of an address." + } + Error::UnknownCallFrameInstruction(_) => "An unknown DW_CFA_* instruction", + Error::InvalidAddressRange => { + "The end of an address range must not be before the beginning." + } + Error::AddressOverflow => "An address calculation overflowed.", + Error::CfiInstructionInInvalidContext => { + "Encountered a call frame instruction in a context in which it is not valid." + } + Error::PopWithEmptyStack => { + "When evaluating call frame instructions, found a `DW_CFA_restore_state` stack pop \ + instruction, but the stack was empty, and had nothing to pop." + } + Error::NoUnwindInfoForAddress => "Do not have unwind info for the given address.", + Error::UnsupportedOffset => { + "An offset value was larger than the maximum supported value." + } + Error::UnknownPointerEncoding(_) => { + "The given pointer encoding is either unknown or invalid." + } + Error::NoEntryAtGivenOffset => "Did not find an entry at the given offset.", + Error::OffsetOutOfBounds => "The given offset is out of bounds.", + Error::UnknownAugmentation => "Found an unknown CFI augmentation.", + Error::UnsupportedPointerEncoding => { + "We do not support the given pointer encoding yet." + } + Error::UnsupportedRegister(_) => "Registers larger than `u16` are not supported.", + Error::TooManyRegisterRules => { + "The CFI program defined more register rules than we have storage for." + } + Error::StackFull => { + "Attempted to push onto the CFI stack, but it was already at full capacity." + } + Error::VariableLengthSearchTable => { + "The `.eh_frame_hdr` binary search table claims to be variable-length encoded, \ + which makes binary search impossible." + } + Error::UnsupportedUnitType => "The `DW_UT_*` value for this unit is not supported yet", + Error::UnsupportedAddressIndex => "Ranges involving AddressIndex are not supported yet", + Error::UnsupportedSegmentSize => "Nonzero segment size not supported yet", + Error::MissingUnitDie => { + "A compilation unit or type unit is missing its top level DIE." + } + Error::UnsupportedAttributeForm => "A DIE attribute used an unsupported form.", + Error::MissingFileEntryFormatPath => "Missing DW_LNCT_path in file entry format.", + Error::ExpectedStringAttributeValue => { + "Expected an attribute value to be a string form." + } + Error::InvalidImplicitConst => "DW_FORM_implicit_const used in an invalid context.", + Error::InvalidIndexSectionCount => "Invalid section count in `.dwp` index.", + Error::InvalidIndexSlotCount => "Invalid slot count in `.dwp` index.", + Error::InvalidIndexRow => "Invalid hash row in `.dwp` index.", + Error::UnknownIndexSection(_) => "Unknown section type in `.dwp` index.", + Error::UnknownIndexSectionV2(_) => "Unknown section type in version 2 `.dwp` index.", + Error::InvalidMacinfoType(_) => "Invalid macinfo type in `.debug_macinfo`.", + Error::InvalidMacroType(_) => "Invalid macro type in `.debug_macro`.", + Error::UnsupportedOpcodeOperandsTable => { + "The optional `opcode_operands_table` in `.debug_macro` is currently not supported." + } + } + } +} + +#[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() + } + + /// Returns the XCOFF section name (if any) for this type when used in a XCOFF + /// file. + fn xcoff_section_name() -> Option<&'static str> { + Self::id().xcoff_name() + } + + /// Try to load the section using the given loader function. + fn load<F, 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) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_initial_length_incomplete() { + let buf = [0xff, 0xff, 0xff]; // Need at least 4 bytes. + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_initial_length() { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_initial_length_64_incomplete() { + let section = Section::with_endian(Endian::Little) + // Dwarf_64_INITIAL_UNIT_LENGTH + .L32(0xffff_ffff) + // Actual length is not long enough. + .L32(0x7856_3412); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_initial_length() { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_offset_32() { + let section = Section::with_endian(Endian::Little).L32(0x0123_4567); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf32) { + Ok(val) => { + assert_eq!(input.len(), 0); + assert_eq!(val, 0x0123_4567); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_offset_64_small() { + let section = Section::with_endian(Endian::Little).L64(0x0123_4567); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf64) { + Ok(val) => { + assert_eq!(input.len(), 0); + assert_eq!(val, 0x0123_4567); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_offset_64_large() { + let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf64) { + Ok(val) => { + assert_eq!(input.len(), 0); + assert_eq!(val, 0x0123_4567_89ab_cdef); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "32")] + fn test_parse_offset_64_large() { + let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf64) { + Err(Error::UnsupportedOffset) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/op.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/op.rs new file mode 100644 index 0000000..0f9261a --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/op.rs
@@ -0,0 +1,4182 @@ +//! 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 { + matches!(*self, Location::Empty) + } +} + +/// The description of a single piece of the result of a DWARF +/// expression. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Piece<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 = if encoding.version == 2 { + bytes + .read_address(encoding.address_size) + .and_then(Offset::from_u64)? + } else { + bytes.read_offset(encoding.format)? + }; + let byte_offset = bytes.read_sleb128()?; + Ok(Operation::ImplicitPointer { + value: DebugInfoOffset(value), + byte_offset, + }) + } + constants::DW_OP_addrx | constants::DW_OP_GNU_addr_index => { + let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::AddressIndex { + index: DebugAddrIndex(index), + }) + } + constants::DW_OP_constx | constants::DW_OP_GNU_const_index => { + let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::ConstantIndex { + index: DebugAddrIndex(index), + }) + } + constants::DW_OP_entry_value | constants::DW_OP_GNU_entry_value => { + let len = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + let expression = bytes.split(len)?; + Ok(Operation::EntryValue { expression }) + } + constants::DW_OP_GNU_parameter_ref => { + let value = bytes.read_u32().map(R::Offset::from_u32)?; + Ok(Operation::ParameterRef { + offset: UnitOffset(value), + }) + } + constants::DW_OP_const_type | constants::DW_OP_GNU_const_type => { + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + let len = bytes.read_u8()?; + let value = bytes.split(R::Offset::from_u8(len))?; + Ok(Operation::TypedLiteral { + base_type: UnitOffset(base_type), + value, + }) + } + constants::DW_OP_regval_type | constants::DW_OP_GNU_regval_type => { + let register = bytes.read_uleb128().and_then(Register::from_u64)?; + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::RegisterOffset { + register, + offset: 0, + base_type: UnitOffset(base_type), + }) + } + constants::DW_OP_deref_type | constants::DW_OP_GNU_deref_type => { + let size = bytes.read_u8()?; + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Deref { + base_type: UnitOffset(base_type), + size, + space: false, + }) + } + constants::DW_OP_xderef_type => { + let size = bytes.read_u8()?; + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Deref { + base_type: UnitOffset(base_type), + size, + space: true, + }) + } + constants::DW_OP_convert | constants::DW_OP_GNU_convert => { + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Convert { + base_type: UnitOffset(base_type), + }) + } + constants::DW_OP_reinterpret | constants::DW_OP_GNU_reinterpret => { + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Reinterpret { + base_type: UnitOffset(base_type), + }) + } + constants::DW_OP_WASM_location => match bytes.read_u8()? { + 0x0 => { + let index = bytes.read_uleb128_u32()?; + Ok(Operation::WasmLocal { index }) + } + 0x1 => { + let index = bytes.read_uleb128_u32()?; + Ok(Operation::WasmGlobal { index }) + } + 0x2 => { + let index = bytes.read_uleb128_u32()?; + Ok(Operation::WasmStack { index }) + } + 0x3 => { + let index = bytes.read_u32()?; + Ok(Operation::WasmGlobal { index }) + } + _ => Err(Error::InvalidExpression(name)), + }, + _ => Err(Error::InvalidExpression(name)), + } + } +} + +#[derive(Debug)] +enum EvaluationState<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) + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for OperationIter<R> { + type Item = Operation<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + OperationIter::next(self) + } +} + +/// Specification of what storage should be used for [`Evaluation`]. +/// +#[cfg_attr( + feature = "read", + doc = " +Normally you would only need to use [`StoreOnHeap`], which places the stacks and the results +on the heap using [`Vec`]. This is the default storage type parameter for [`Evaluation`]. +" +)] +/// +/// If you need to avoid [`Evaluation`] from allocating memory, e.g. for signal safety, +/// you can provide you own storage specification: +/// ```rust,no_run +/// # use gimli::*; +/// # let bytecode = EndianSlice::new(&[], LittleEndian); +/// # let encoding = unimplemented!(); +/// # let get_register_value = |_, _| Value::Generic(42); +/// # let get_frame_base = || 0xdeadbeef; +/// # +/// struct StoreOnStack; +/// +/// impl<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::{Evaluation, EvaluationResult, Expression}; +/// # let bytecode = gimli::EndianSlice::new(&[], gimli::LittleEndian); +/// # let encoding = unimplemented!(); +/// # let get_register_value = |_, _| gimli::Value::Generic(42); +/// # let get_frame_base = || 0xdeadbeef; +/// +/// let mut eval = Evaluation::new(bytecode, encoding); +/// let mut result = eval.evaluate().unwrap(); +/// while result != EvaluationResult::Complete { +/// match result { +/// EvaluationResult::RequiresRegister { register, base_type } => { +/// let value = get_register_value(register, base_type); +/// result = eval.resume_with_register(value).unwrap(); +/// }, +/// EvaluationResult::RequiresFrameBase => { +/// let frame_base = get_frame_base(); +/// result = eval.resume_with_frame_base(frame_base).unwrap(); +/// }, +/// _ => unimplemented!(), +/// }; +/// } +/// +/// let result = eval.result(); +/// println!("{:?}", result); +/// ``` +#[derive(Debug)] +pub struct Evaluation<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>, + + value_result: Option<Value>, + 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, + value_result: None, + result: Default::default(), + } + } + + /// Set an initial value to be pushed on the DWARF expression + /// evaluator's stack. This can be used in cases like + /// `DW_AT_vtable_elem_location`, which require a value on the + /// stack before evaluation commences. If no initial value is + /// set, and the expression uses an opcode requiring the initial + /// value, then evaluation will fail with an error. + /// + /// # Panics + /// Panics if `set_initial_value()` has already been called, or if + /// `evaluate()` has already been called. + pub fn set_initial_value(&mut self, value: u64) { + match self.state { + EvaluationState::Start(None) => { + self.state = EvaluationState::Start(Some(value)); + } + _ => panic!( + "`Evaluation::set_initial_value` was called twice, or after evaluation began." + ), + }; + } + + /// Set the enclosing object's address, as used by + /// `DW_OP_push_object_address`. If no object address is set, and + /// the expression uses an opcode requiring the object address, + /// then evaluation will fail with an error. + pub fn set_object_address(&mut self, value: u64) { + self.object_address = Some(value); + } + + /// Set the maximum number of iterations to be allowed by the + /// expression evaluator. + /// + /// An iteration corresponds approximately to the evaluation of a + /// single operation in an expression ("approximately" because the + /// implementation may allow two such operations in some cases). + /// The default is not to have a maximum; once set, it's not + /// possible to go back to this default state. This value can be + /// set to avoid denial of service attacks by bad DWARF bytecode. + pub fn set_max_iterations(&mut self, value: u32) { + self.max_iterations = Some(value); + } + + fn pop(&mut self) -> Result<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) + } + + 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, + } => { + if size > self.encoding.address_size { + return Err(Error::InvalidDerefSize(size)); + } + let entry = self.pop()?; + let addr = entry.to_u64(self.addr_mask)?; + let addr_space = if space { + let entry = self.pop()?; + let value = entry.to_u64(self.addr_mask)?; + Some(value) + } else { + None + }; + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Memory, + EvaluationResult::RequiresMemory { + address: addr, + size, + space: addr_space, + base_type, + }, + )); + } + + Operation::Drop => { + self.pop()?; + } + Operation::Pick { index } => { + let len = self.stack.len(); + let index = index as usize; + if index >= len { + return Err(Error::NotEnoughStackItems); + } + let value = self.stack[len - index - 1]; + self.push(value)?; + } + Operation::Swap => { + let top = self.pop()?; + let next = self.pop()?; + self.push(top)?; + self.push(next)?; + } + Operation::Rot => { + let one = self.pop()?; + let two = self.pop()?; + let three = self.pop()?; + self.push(one)?; + self.push(three)?; + self.push(two)?; + } + + Operation::Abs => { + let value = self.pop()?; + let result = value.abs(self.addr_mask)?; + self.push(result)?; + } + Operation::And => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.and(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Div => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.div(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Minus => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.sub(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Mod => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.rem(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Mul => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.mul(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Neg => { + let v = self.pop()?; + let result = v.neg(self.addr_mask)?; + self.push(result)?; + } + Operation::Not => { + let value = self.pop()?; + let result = value.not(self.addr_mask)?; + self.push(result)?; + } + Operation::Or => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.or(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Plus => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.add(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::PlusConstant { value } => { + let lhs = self.pop()?; + let rhs = Value::from_u64(lhs.value_type(), value)?; + let result = lhs.add(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Shl => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.shl(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Shr => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.shr(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Shra => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.shra(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Xor => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.xor(rhs, self.addr_mask)?; + self.push(result)?; + } + + Operation::Bra { target } => { + let entry = self.pop()?; + let v = entry.to_u64(self.addr_mask)?; + if v != 0 { + self.pc = compute_pc(&self.pc, &self.bytecode, target)?; + } + } + + Operation::Eq => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.eq(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Ge => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.ge(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Gt => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.gt(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Le => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.le(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Lt => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.lt(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Ne => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.ne(rhs, self.addr_mask)?; + self.push(result)?; + } + + Operation::Skip { target } => { + self.pc = compute_pc(&self.pc, &self.bytecode, target)?; + } + + Operation::UnsignedConstant { value } => { + self.push(Value::Generic(value))?; + } + + Operation::SignedConstant { value } => { + self.push(Value::Generic(value as u64))?; + } + + Operation::RegisterOffset { + register, + offset, + base_type, + } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Register { offset }, + EvaluationResult::RequiresRegister { + register, + base_type, + }, + )); + } + + Operation::FrameOffset { offset } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::FrameBase { offset }, + EvaluationResult::RequiresFrameBase, + )); + } + + Operation::Nop => {} + + Operation::PushObjectAddress => { + if let Some(value) = self.object_address { + self.push(Value::Generic(value))?; + } else { + return Err(Error::InvalidPushObjectAddress); + } + } + + Operation::Call { offset } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::AtLocation, + EvaluationResult::RequiresAtLocation(offset), + )); + } + + Operation::TLS => { + let entry = self.pop()?; + let index = entry.to_u64(self.addr_mask)?; + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Tls, + EvaluationResult::RequiresTls(index), + )); + } + + Operation::CallFrameCFA => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Cfa, + EvaluationResult::RequiresCallFrameCfa, + )); + } + + Operation::Register { register } => { + let location = Location::Register { register }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::ImplicitValue { ref data } => { + let location = Location::Bytes { + value: data.clone(), + }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::StackValue => { + let value = self.pop()?; + let location = Location::Value { value }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::ImplicitPointer { value, byte_offset } => { + let location = Location::ImplicitPointer { value, byte_offset }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::EntryValue { ref expression } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::EntryValue, + EvaluationResult::RequiresEntryValue(Expression(expression.clone())), + )); + } + + Operation::ParameterRef { offset } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::ParameterRef, + EvaluationResult::RequiresParameterRef(offset), + )); + } + + Operation::Address { address } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::RelocatedAddress, + EvaluationResult::RequiresRelocatedAddress(address), + )); + } + + Operation::AddressIndex { index } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::IndexedAddress, + EvaluationResult::RequiresIndexedAddress { + index, + relocate: true, + }, + )); + } + + Operation::ConstantIndex { index } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::IndexedAddress, + EvaluationResult::RequiresIndexedAddress { + index, + relocate: false, + }, + )); + } + + Operation::Piece { + size_in_bits, + bit_offset, + } => { + let location = if self.stack.is_empty() { + Location::Empty + } else { + let entry = self.pop()?; + let address = entry.to_u64(self.addr_mask)?; + Location::Address { address } + }; + self.result + .try_push(Piece { + size_in_bits: Some(size_in_bits), + bit_offset, + location, + }) + .map_err(|_| Error::StackFull)?; + return Ok(OperationEvaluationResult::Piece); + } + + Operation::TypedLiteral { base_type, value } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::TypedLiteral { value }, + EvaluationResult::RequiresBaseType(base_type), + )); + } + Operation::Convert { base_type } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Convert, + EvaluationResult::RequiresBaseType(base_type), + )); + } + Operation::Reinterpret { base_type } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Reinterpret, + EvaluationResult::RequiresBaseType(base_type), + )); + } + Operation::WasmLocal { .. } + | Operation::WasmGlobal { .. } + | Operation::WasmStack { .. } => { + return Err(Error::UnsupportedEvaluation); + } + } + + Ok(OperationEvaluationResult::Incomplete) + } + + /// Get the result if this is an evaluation for a value. + /// + /// Returns `None` if the evaluation contained operations that are only + /// valid for location descriptions. + /// + /// # Panics + /// Panics if this `Evaluation` has not been driven to completion. + pub fn value_result(&self) -> Option<Value> { + match self.state { + EvaluationState::Complete => self.value_result, + _ => { + panic!("Called `Evaluation::value_result` on an `Evaluation` that has not been completed") + } + } + } + + /// Get the result of this `Evaluation`. + /// + /// # Panics + /// Panics if this `Evaluation` has not been driven to completion. + pub fn as_result(&self) -> &[Piece<R>] { + match self.state { + EvaluationState::Complete => &self.result, + _ => { + panic!( + "Called `Evaluation::as_result` on an `Evaluation` that has not been completed" + ) + } + } + } + + /// Evaluate a DWARF expression. This method should only ever be called + /// once. If the returned `EvaluationResult` is not + /// `EvaluationResult::Complete`, the caller should provide the required + /// value and resume the evaluation by calling the appropriate resume_with + /// method on `Evaluation`. + pub fn evaluate(&mut self) -> Result<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()?; + self.value_result = Some(entry); + let addr = entry.to_u64(self.addr_mask)?; + self.result + .try_push(Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Address { address: addr }, + }) + .map_err(|_| Error::StackFull)?; + } + + self.state = EvaluationState::Complete; + Ok(EvaluationResult::Complete) + } +} + +#[cfg(test)] +// Tests require leb128::write. +#[cfg(feature = "write")] +mod tests { + use super::*; + use crate::common::Format; + use crate::constants; + use crate::endianity::LittleEndian; + use crate::leb128; + use crate::read::{EndianSlice, Error, Result, UnitOffset}; + use crate::test_util::GimliSectionMethods; + use test_assembler::{Endian, Section}; + + fn encoding4() -> Encoding { + Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + } + } + + fn encoding8() -> Encoding { + Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + } + } + + #[test] + fn test_compute_pc() { + // Contents don't matter for this test, just length. + let bytes = [0, 1, 2, 3, 4]; + let bytecode = &bytes[..]; + let ebuf = &EndianSlice::new(bytecode, LittleEndian); + + assert_eq!(compute_pc(ebuf, ebuf, 0), Ok(*ebuf)); + assert_eq!( + compute_pc(ebuf, ebuf, -1), + Err(Error::BadBranchTarget(usize::MAX as u64)) + ); + assert_eq!(compute_pc(ebuf, ebuf, 5), Ok(ebuf.range_from(5..))); + assert_eq!( + compute_pc(&ebuf.range_from(3..), ebuf, -2), + Ok(ebuf.range_from(1..)) + ); + assert_eq!( + compute_pc(&ebuf.range_from(2..), ebuf, 2), + Ok(ebuf.range_from(4..)) + ); + } + + fn check_op_parse_simple<'input>( + input: &'input [u8], + expect: &Operation<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(), + ); + + check_op_parse( + |s| s.D8(op.0).D64(0x1234_5678).sleb(0x123), + &Operation::ImplicitPointer { + value: DebugInfoOffset(0x1234_5678), + byte_offset: 0x123, + }, + Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 8, + }, + ) + } + } + + #[test] + fn test_op_parse_entry_value() { + for op in &[ + constants::DW_OP_entry_value, + constants::DW_OP_GNU_entry_value, + ] { + let data = b"hello"; + check_op_parse( + |s| s.D8(op.0).uleb(data.len() as u64).append_bytes(&data[..]), + &Operation::EntryValue { + expression: EndianSlice::new(&data[..], LittleEndian), + }, + encoding4(), + ); + } + } + + #[test] + fn test_op_parse_gnu_parameter_ref() { + check_op_parse( + |s| s.D8(constants::DW_OP_GNU_parameter_ref.0).D32(0x1234_5678), + &Operation::ParameterRef { + offset: UnitOffset(0x1234_5678), + }, + encoding4(), + ) + } + + #[test] + fn test_op_wasm() { + // Doesn't matter for this test. + let encoding = encoding4(); + + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(0).uleb(1000), + &Operation::WasmLocal { index: 1000 }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(1).uleb(1000), + &Operation::WasmGlobal { index: 1000 }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(2).uleb(1000), + &Operation::WasmStack { index: 1000 }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(3).D32(1000), + &Operation::WasmGlobal { index: 1000 }, + encoding, + ); + } + + enum AssemblerEntry { + Op(constants::DwOp), + Mark(u8), + Branch(u8), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + Uleb(u64), + Sleb(u64), + } + + fn assemble(entries: &[AssemblerEntry]) -> Vec<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 [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 + } + + 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) + }, + ); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_deref_size), U8(8), + ]; + check_eval_with_args( + &program, + Err(Error::InvalidDerefSize(8)), + encoding4(), + None, + None, + None, + |eval, mut result| { + while result != EvaluationResult::Complete { + result = match result { + EvaluationResult::RequiresMemory { + address, + size, + space, + base_type, + } => { + assert_eq!(base_type, UnitOffset(0)); + let mut v = address << 2; + if let Some(value) = space { + v += value; + } + v &= (1u64 << (8 * size)) - 1; + eval.resume_with_memory(Value::Generic(v))? + } + EvaluationResult::RequiresRelocatedAddress(address) => { + eval.resume_with_relocated_address(address)? + } + _ => panic!("Unexpected result: {:?}", result), + }; + } + + Ok(result) + }, + ); + } + + #[test] + fn test_eval_register() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + for i in 0..32 { + #[rustfmt::skip] + let program = [ + Op(DwOp(DW_OP_reg0.0 + i)), + // Included only in the "bad" run. + Op(DW_OP_lit23), + ]; + let ok_result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Register { + register: Register(i.into()), + }, + }]; + + check_eval(&program[..1], Ok(&ok_result), encoding4()); + + check_eval( + &program, + Err(Error::InvalidExpressionTerminator(1)), + encoding4(), + ); + } + + #[rustfmt::skip] + let program = [ + Op(DW_OP_regx), Uleb(0x1234) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Register { + register: Register(0x1234), + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + } + + #[test] + fn test_eval_context() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Test `frame_base` and `call_frame_cfa` callbacks. + #[rustfmt::skip] + let program = [ + Op(DW_OP_fbreg), Sleb((-8i8) as u64), + Op(DW_OP_call_frame_cfa), + Op(DW_OP_plus), + Op(DW_OP_neg), + Op(DW_OP_stack_value) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(9), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + None, + None, + None, + |eval, result| { + match result { + EvaluationResult::RequiresFrameBase => {} + _ => panic!(), + }; + match eval.resume_with_frame_base(0x0123_4567_89ab_cdef)? { + EvaluationResult::RequiresCallFrameCfa => {} + _ => panic!(), + }; + eval.resume_with_call_frame_cfa(0xfedc_ba98_7654_3210) + }, + ); + + // Test `evaluate_entry_value` callback. + #[rustfmt::skip] + let program = [ + Op(DW_OP_entry_value), Uleb(8), U64(0x1234_5678), + Op(DW_OP_stack_value) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0x1234_5678), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + None, + None, + None, + |eval, result| { + let entry_value = match result { + EvaluationResult::RequiresEntryValue(mut expression) => { + expression.0.read_u64()? + } + _ => panic!(), + }; + eval.resume_with_entry_value(Value::Generic(entry_value)) + }, + ); + + // Test missing `object_address` field. + #[rustfmt::skip] + let program = [ + Op(DW_OP_push_object_address), + ]; + + check_eval_with_args( + &program, + Err(Error::InvalidPushObjectAddress), + encoding4(), + None, + None, + None, + |_, _| panic!(), + ); + + // Test `object_address` field. + #[rustfmt::skip] + let program = [ + Op(DW_OP_push_object_address), + Op(DW_OP_stack_value), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0xff), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + Some(0xff), + None, + None, + |_, result| Ok(result), + ); + + // Test `initial_value` field. + #[rustfmt::skip] + let program = [ + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Address { + address: 0x1234_5678, + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + None, + Some(0x1234_5678), + None, + |_, result| Ok(result), + ); + } + + #[test] + fn test_eval_empty_stack() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_stack_value) + ]; + + check_eval(&program, Err(Error::NotEnoughStackItems), encoding4()); + } + + #[test] + fn test_eval_call() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_lit23), + Op(DW_OP_call2), U16(0x7755), + Op(DW_OP_call4), U32(0x7755_aaee), + Op(DW_OP_call_ref), U32(0x7755_aaee), + Op(DW_OP_stack_value) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(23), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, result| { + let buf = EndianSlice::new(&[], LittleEndian); + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf) + }, + ); + + // DW_OP_lit2 DW_OP_mul + const SUBR: &[u8] = &[0x32, 0x1e]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(184), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, result| { + let buf = EndianSlice::new(SUBR, LittleEndian); + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf) + }, + ); + } + + #[test] + fn test_eval_pieces() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Example from DWARF 2.6.1.3. + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg3), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_reg4), + Op(DW_OP_piece), Uleb(2), + ]; + + let result = [ + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Register { + register: Register(3), + }, + }, + Piece { + size_in_bits: Some(16), + bit_offset: None, + location: Location::Register { + register: Register(4), + }, + }, + ]; + + check_eval(&program, Ok(&result), encoding4()); + + // Example from DWARF 2.6.1.3 (but hacked since dealing with fbreg + // in the tests is a pain). + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg0), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_piece), Uleb(4), + ]; + + let result = [ + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Register { + register: Register(0), + }, + }, + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Empty, + }, + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Address { + address: 0x7fff_ffff, + }, + }, + ]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, mut result| { + while result != EvaluationResult::Complete { + result = match result { + EvaluationResult::RequiresRelocatedAddress(address) => { + eval.resume_with_relocated_address(address)? + } + _ => panic!(), + }; + } + + Ok(result) + }, + ); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_implicit_value), Uleb(5), + U8(23), U8(24), U8(25), U8(26), U8(0), + ]; + + const BYTES: &[u8] = &[23, 24, 25, 26, 0]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Bytes { + value: EndianSlice::new(BYTES, LittleEndian), + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_lit7), + Op(DW_OP_stack_value), + Op(DW_OP_bit_piece), Uleb(5), Uleb(0), + Op(DW_OP_bit_piece), Uleb(3), Uleb(0), + ]; + + let result = [ + Piece { + size_in_bits: Some(5), + bit_offset: Some(0), + location: Location::Value { + value: Value::Generic(7), + }, + }, + Piece { + size_in_bits: Some(3), + bit_offset: Some(0), + location: Location::Empty, + }, + ]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_lit7), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Address { address: 7 }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_implicit_pointer), U32(0x1234_5678), Sleb(0x123), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::ImplicitPointer { + value: DebugInfoOffset(0x1234_5678), + byte_offset: 0x123, + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg3), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_reg4), + ]; + + check_eval(&program, Err(Error::InvalidPiece), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg3), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_lit0), + ]; + + check_eval(&program, Err(Error::InvalidPiece), encoding4()); + } + + #[test] + fn test_eval_max_iterations() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + #[rustfmt::skip] + let program = [ + Mark(1), + Op(DW_OP_skip), Branch(1), + ]; + + check_eval_with_args( + &program, + Err(Error::TooManyIterations), + encoding4(), + None, + None, + Some(150), + |_, _| panic!(), + ); + } + + #[test] + fn test_eval_typed_stack() { + use self::AssemblerEntry::*; + use crate::constants::*; + + let base_types = [ + ValueType::Generic, + ValueType::U16, + ValueType::U32, + ValueType::F32, + ]; + + // TODO: convert, reinterpret + #[rustfmt::skip] + let tests = [ + ( + &[ + Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), + Op(DW_OP_stack_value), + ][..], + Value::U16(0x1234), + ), + ( + &[ + Op(DW_OP_regval_type), Uleb(0x1234), Uleb(1), + Op(DW_OP_stack_value), + ][..], + Value::U16(0x2340), + ), + ( + &[ + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_deref_type), U8(2), Uleb(1), + Op(DW_OP_stack_value), + ][..], + Value::U16(0xfff0), + ), + ( + &[ + Op(DW_OP_lit1), + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_xderef_type), U8(2), Uleb(1), + Op(DW_OP_stack_value), + ][..], + Value::U16(0xfff1), + ), + ( + &[ + Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), + Op(DW_OP_convert), Uleb(2), + Op(DW_OP_stack_value), + ][..], + Value::U32(0x1234), + ), + ( + &[ + Op(DW_OP_const_type), Uleb(2), U8(4), U32(0x3f80_0000), + Op(DW_OP_reinterpret), Uleb(3), + Op(DW_OP_stack_value), + ][..], + Value::F32(1.0), + ), + ]; + for &(program, value) in &tests { + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { value }, + }]; + + check_eval_with_args( + program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, mut result| { + while result != EvaluationResult::Complete { + result = match result { + EvaluationResult::RequiresMemory { + address, + size, + space, + base_type, + } => { + let mut v = address << 4; + if let Some(value) = space { + v += value; + } + v &= (1u64 << (8 * size)) - 1; + let v = Value::from_u64(base_types[base_type.0], v)?; + eval.resume_with_memory(v)? + } + EvaluationResult::RequiresRegister { + register, + base_type, + } => { + let v = Value::from_u64( + base_types[base_type.0], + u64::from(register.0) << 4, + )?; + eval.resume_with_register(v)? + } + EvaluationResult::RequiresBaseType(offset) => { + eval.resume_with_base_type(base_types[offset.0])? + } + EvaluationResult::RequiresRelocatedAddress(address) => { + eval.resume_with_relocated_address(address)? + } + _ => panic!("Unexpected result {:?}", result), + } + } + Ok(result) + }, + ); + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/pubnames.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/pubnames.rs new file mode 100644 index 0000000..e8b7e55 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugPubNames, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_pubnames_section_somehow = || &buf; + /// let debug_pubnames = + /// DebugPubNames::new(read_debug_pubnames_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_pubnames_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_pubnames_section, endian)) + } +} + +impl<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/chromium_crates_io/vendor/gimli-v0_32/src/read/pubtypes.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/pubtypes.rs new file mode 100644 index 0000000..6723b42 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugPubTypes, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_pubtypes_somehow = || &buf; + /// let debug_pubtypes = + /// DebugPubTypes::new(read_debug_pubtypes_somehow(), LittleEndian); + /// ``` + pub fn new(debug_pubtypes_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_pubtypes_section, endian)) + } +} + +impl<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/chromium_crates_io/vendor/gimli-v0_32/src/read/reader.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/reader.rs new file mode 100644 index 0000000..1e3148f --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/reader.rs
@@ -0,0 +1,594 @@ +#[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) + } +} + +/// A trait for addresses within a DWARF section. +/// +/// Currently this is a simple extension trait for `u64`, but it may be expanded +/// in the future to support user-defined address types. +pub(crate) trait ReaderAddress: Sized { + /// Add a length to an address of the given size. + /// + /// Returns an error for overflow. + fn add_sized(self, length: u64, size: u8) -> Result<Self>; + + /// Add a length to an address of the given size. + /// + /// Wraps the result to the size of the address to allow for the possibility + /// that the length is a negative value. + fn wrapping_add_sized(self, length: u64, size: u8) -> Self; + + /// The all-zeros value of an address. + fn zeros() -> Self; + + /// The all-ones value of an address of the given size. + fn ones_sized(size: u8) -> Self; + + /// Return the minimum value for a tombstone address. + /// + /// A variety of values may be used as tombstones in DWARF data. DWARF 6 specifies a + /// tombstone value of -1, and this is compatible with most sections in earlier DWARF + /// versions. However, for .debug_loc and .debug_ranges in DWARF 4 and earlier, the + /// tombstone value is -2, because -1 already has a special meaning. -2 has also been + /// seen in .debug_line, possibly from a proprietary fork of lld. + /// + /// So this function returns -2 (cast to an unsigned value), and callers can consider + /// addresses greater than or equal to this value to be tombstones. + /// + /// Prior to the use of -1 or -2 for tombstones, it was common to use 0 or 1. + /// Additionally, gold may leave the relocation addend in place. These values are not + /// handled by this function, so callers will need to handle them separately if they + /// want to. + fn min_tombstone(size: u8) -> Self { + Self::zeros().wrapping_add_sized(-2i64 as u64, size) + } +} + +impl ReaderAddress for u64 { + #[inline] + fn add_sized(self, length: u64, size: u8) -> Result<Self> { + let address = self.checked_add(length).ok_or(Error::AddressOverflow)?; + let mask = Self::ones_sized(size); + if address & !mask != 0 { + return Err(Error::AddressOverflow); + } + Ok(address) + } + + #[inline] + fn wrapping_add_sized(self, length: u64, size: u8) -> Self { + let mask = Self::ones_sized(size); + self.wrapping_add(length) & mask + } + + #[inline] + fn zeros() -> Self { + 0 + } + + #[inline] + fn ones_sized(size: u8) -> Self { + !0 >> (64 - size * 8) + } +} + +#[cfg(not(feature = "read"))] +pub(crate) mod seal_if_no_alloc { + #[derive(Debug)] + pub struct Sealed; +} + +/// A trait for reading the data from a DWARF section. +/// +/// All read operations advance the section offset of the reader +/// unless specified otherwise. +/// +/// ## Choosing a `Reader` Implementation +/// +/// `gimli` comes with a few different `Reader` implementations and lets you +/// choose the one that is right for your use case. A `Reader` is essentially a +/// view into the raw bytes that make up some DWARF, but this view might borrow +/// the underlying data or use reference counting ownership, and it might be +/// thread safe or not. +/// +/// | Implementation | Ownership | Thread Safe | Notes | +/// |:------------------|:------------------|:------------|:------| +/// | [`EndianSlice`](./struct.EndianSlice.html) | Borrowed | Yes | Fastest, but requires that all of your code work with borrows. | +/// | [`EndianRcSlice`](./struct.EndianRcSlice.html) | Reference counted | No | Shared ownership via reference counting, which alleviates the borrow restrictions of `EndianSlice` but imposes reference counting increments and decrements. Cannot be sent across threads, because the reference count is not atomic. | +/// | [`EndianArcSlice`](./struct.EndianArcSlice.html) | Reference counted | Yes | The same as `EndianRcSlice`, but uses atomic reference counting, and therefore reference counting operations are slower but `EndianArcSlice`s may be sent across threads. | +/// | [`EndianReader<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 occurrence 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 a byte and validate it as an address size. + fn read_address_size(&mut self) -> Result<u8> { + let size = self.read_u8()?; + match size { + 1 | 2 | 4 | 8 => Ok(size), + _ => Err(Error::UnsupportedAddressSize(size)), + } + } + + /// Read an address-sized integer, and return it as a `u64`. + fn read_address(&mut self, address_size: u8) -> Result<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) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_min_tombstone() { + assert_eq!(u64::min_tombstone(1), 0xfe); + assert_eq!(u64::min_tombstone(2), 0xfffe); + assert_eq!(u64::min_tombstone(4), 0xffff_fffe); + assert_eq!(u64::min_tombstone(8), 0xffff_ffff_ffff_fffe); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/relocate.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/relocate.rs new file mode 100644 index 0000000..d26c2bf --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/relocate.rs
@@ -0,0 +1,153 @@ +#[cfg(feature = "read")] +use alloc::borrow::Cow; +use core::fmt::Debug; + +use crate::common::Format; +use crate::read::{Reader, ReaderOffset, ReaderOffsetId, Result}; + +/// Trait for relocating addresses and offsets while reading a section. +pub trait Relocate<T: ReaderOffset = usize> { + /// Relocate an address which was read from the given section offset. + fn relocate_address(&self, offset: T, value: u64) -> Result<u64>; + + /// Relocate a value which was read from the given section offset. + fn relocate_offset(&self, offset: T, value: T) -> Result<T>; +} + +/// A `Reader` which applies relocations to addresses and offsets. +/// +/// This is useful for reading sections which contain relocations, +/// such as those in a relocatable object file. +/// It is generally not used for reading sections in an executable file. +#[derive(Debug, Clone)] +pub struct RelocateReader<R: Reader<Offset = usize>, T: Relocate<R::Offset>> { + section: R, + reader: R, + relocate: T, +} + +impl<R, T> RelocateReader<R, T> +where + R: Reader<Offset = usize>, + T: Relocate<R::Offset>, +{ + /// Create a new `RelocateReader` which applies relocations to the given section reader. + pub fn new(section: R, relocate: T) -> Self { + let reader = section.clone(); + Self { + section, + reader, + relocate, + } + } +} + +impl<R, T> Reader for RelocateReader<R, T> +where + R: Reader<Offset = usize>, + T: Relocate<R::Offset> + Debug + Clone, +{ + type Endian = R::Endian; + type Offset = R::Offset; + + fn read_address(&mut self, address_size: u8) -> Result<u64> { + let offset = self.reader.offset_from(&self.section); + let value = self.reader.read_address(address_size)?; + self.relocate.relocate_address(offset, value) + } + + fn read_offset(&mut self, format: Format) -> Result<R::Offset> { + let offset = self.reader.offset_from(&self.section); + let value = self.reader.read_offset(format)?; + self.relocate.relocate_offset(offset, value) + } + + fn read_sized_offset(&mut self, size: u8) -> Result<R::Offset> { + let offset = self.reader.offset_from(&self.section); + let value = self.reader.read_sized_offset(size)?; + self.relocate.relocate_offset(offset, value) + } + + #[inline] + fn split(&mut self, len: Self::Offset) -> Result<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) -> Result<()> { + self.reader.truncate(len) + } + + #[inline] + fn offset_from(&self, base: &Self) -> Self::Offset { + self.reader.offset_from(&base.reader) + } + + #[inline] + fn offset_id(&self) -> ReaderOffsetId { + self.reader.offset_id() + } + + #[inline] + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<Self::Offset> { + self.reader.lookup_offset_id(id) + } + + #[inline] + fn find(&self, byte: u8) -> Result<Self::Offset> { + self.reader.find(byte) + } + + #[inline] + fn skip(&mut self, len: Self::Offset) -> Result<()> { + self.reader.skip(len) + } + + #[cfg(not(feature = "read"))] + fn cannot_implement() -> super::reader::seal_if_no_alloc::Sealed { + super::reader::seal_if_no_alloc::Sealed + } + + #[cfg(feature = "read")] + #[inline] + fn to_slice(&self) -> Result<Cow<'_, [u8]>> { + self.reader.to_slice() + } + + #[cfg(feature = "read")] + #[inline] + fn to_string(&self) -> Result<Cow<'_, str>> { + self.reader.to_string() + } + + #[cfg(feature = "read")] + #[inline] + fn to_string_lossy(&self) -> Result<Cow<'_, str>> { + self.reader.to_string_lossy() + } + + #[inline] + fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { + self.reader.read_slice(buf) + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/rnglists.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/rnglists.rs new file mode 100644 index 0000000..dba8019 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/rnglists.rs
@@ -0,0 +1,1044 @@ +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, ReaderAddress, 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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugRanges, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_ranges_section_somehow = || &buf; + /// let debug_ranges = DebugRanges::new(read_debug_ranges_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<T> DebugRanges<T> { + /// Create a `DebugRanges` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfSections::borrow`. + pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugRanges<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugRngLists, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_rnglists_section_somehow = || &buf; + /// let debug_rnglists = + /// DebugRngLists::new(read_debug_rnglists_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<T> DebugRngLists<T> { + /// Create a `DebugRngLists` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfSections::borrow`. + pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugRngLists<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +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. + /// + /// Used by `Dwarf::borrow`. + 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>> { + Ok(match format { + RangeListsFormat::Bare => { + let range = RawRange::parse(input, encoding.address_size)?; + if range.is_end() { + None + } else if range.is_base_address(encoding.address_size) { + Some(RawRngListEntry::BaseAddress { addr: range.end }) + } else { + Some(RawRngListEntry::AddressOrOffsetPair { + begin: range.begin, + end: range.end, + }) + } + } + RangeListsFormat::Rle => match constants::DwRle(input.read_u8()?) { + constants::DW_RLE_end_of_list => None, + constants::DW_RLE_base_addressx => Some(RawRngListEntry::BaseAddressx { + addr: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + }), + constants::DW_RLE_startx_endx => Some(RawRngListEntry::StartxEndx { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + end: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + }), + constants::DW_RLE_startx_length => Some(RawRngListEntry::StartxLength { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + length: input.read_uleb128()?, + }), + constants::DW_RLE_offset_pair => Some(RawRngListEntry::OffsetPair { + begin: input.read_uleb128()?, + end: input.read_uleb128()?, + }), + constants::DW_RLE_base_address => Some(RawRngListEntry::BaseAddress { + addr: input.read_address(encoding.address_size)?, + }), + constants::DW_RLE_start_end => Some(RawRngListEntry::StartEnd { + begin: input.read_address(encoding.address_size)?, + end: input.read_address(encoding.address_size)?, + }), + constants::DW_RLE_start_length => Some(RawRngListEntry::StartLength { + begin: input.read_address(encoding.address_size)?, + length: input.read_uleb128()?, + }), + entry => { + return Err(Error::UnknownRangeListsEntry(entry)); + } + }, + }) + } +} + +impl<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 = self.convert_raw(raw_range)?; + if range.is_some() { + return Ok(range); + } + } + } + + /// Return the next raw range. + /// + /// The raw range should be passed to `convert_range`. + #[doc(hidden)] + pub fn next_raw(&mut self) -> Result<Option<RawRngListEntry<R::Offset>>> { + self.raw.next() + } + + /// Convert a raw range into a range, and update the state of the iterator. + /// + /// The raw range should have been obtained from `next_raw`. + #[doc(hidden)] + pub fn convert_raw(&mut self, raw_range: RawRngListEntry<R::Offset>) -> Result<Option<Range>> { + let address_size = self.raw.encoding.address_size; + + let range = match raw_range { + RawRngListEntry::BaseAddress { addr } => { + self.base_address = addr; + return Ok(None); + } + RawRngListEntry::BaseAddressx { addr } => { + self.base_address = self.get_address(addr)?; + return Ok(None); + } + RawRngListEntry::StartxEndx { begin, end } => { + let begin = self.get_address(begin)?; + let end = self.get_address(end)?; + Range { begin, end } + } + RawRngListEntry::StartxLength { begin, length } => { + let begin = self.get_address(begin)?; + let end = begin.wrapping_add_sized(length, address_size); + Range { begin, end } + } + RawRngListEntry::AddressOrOffsetPair { begin, end } + | RawRngListEntry::OffsetPair { begin, end } => { + // Skip tombstone entries (see below). + if self.base_address >= u64::min_tombstone(address_size) { + return Ok(None); + } + let mut range = Range { begin, end }; + range.add_base_address(self.base_address, address_size); + range + } + RawRngListEntry::StartEnd { begin, end } => Range { begin, end }, + RawRngListEntry::StartLength { begin, length } => { + let end = begin.wrapping_add_sized(length, address_size); + Range { begin, end } + } + }; + + // Skip tombstone entries. + // + // DWARF specifies a tombstone value of -1 or -2, but many linkers use 0 or 1. + // However, 0/1 may be a valid address, so we can't always reliably skip them. + // One case where we can skip them is for address pairs, where both values are + // replaced by tombstones and thus `begin` equals `end`. Since these entries + // are empty, it's safe to skip them even if they aren't tombstones. + // + // In addition to skipping tombstone entries, we also skip invalid entries + // where `begin` is greater than `end`. This can occur due to compiler bugs. + if range.begin >= u64::min_tombstone(address_size) || range.begin >= range.end { + return Ok(None); + } + + 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. + #[inline] + pub fn is_end(&self) -> bool { + self.begin == 0 && self.end == 0 + } + + /// Check if this is a base address selection entry. + /// + /// A base address selection entry changes the base address that subsequent + /// range entries are relative to. + #[inline] + pub fn is_base_address(&self, address_size: u8) -> bool { + self.begin == !0 >> (64 - address_size * 8) + } + + /// Parse an address range entry from `.debug_ranges` or `.debug_loc`. + #[inline] + pub fn parse<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, PartialOrd, Ord, Hash)] +pub struct Range { + /// The beginning address of the range. + pub begin: u64, + + /// The first address past the end of the range. + pub end: u64, +} + +impl Range { + /// Add a base address to this range. + #[inline] + pub(crate) fn add_base_address(&mut self, base_address: u64, address_size: u8) { + self.begin = base_address.wrapping_add_sized(self.begin, address_size); + self.end = base_address.wrapping_add_sized(self.end, address_size); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::Format; + use crate::constants::*; + use crate::endianity::LittleEndian; + use crate::test_util::GimliSectionMethods; + use alloc::vec::Vec; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_rnglists() { + let format = Format::Dwarf32; + for size in [4, 8] { + let tombstone = u64::ones_sized(size); + let tombstone_0 = 0; + let encoding = Encoding { + format, + version: 5, + address_size: size, + }; + let section = Section::with_endian(Endian::Little) + .word(size, 0x0300_0000) + .word(size, 0x0301_0300) + .word(size, 0x0301_0400) + .word(size, 0x0301_0500) + .word(size, tombstone) + .word(size, 0x0301_0600) + .word(size, tombstone_0); + let buf = section.get_contents().unwrap(); + let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .initial_length(format, &length, &start) + .L16(encoding.version) + .L8(encoding.address_size) + .L8(0) + .L32(0) + .mark(&first); + + let mut expected_ranges = Vec::new(); + let mut expect_range = |begin, end| { + expected_ranges.push(Range { begin, end }); + }; + + // An offset pair using the unit base address. + section = section.L8(DW_RLE_offset_pair.0).uleb(0x10200).uleb(0x10300); + expect_range(0x0101_0200, 0x0101_0300); + + section = section.L8(DW_RLE_base_address.0).word(size, 0x0200_0000); + section = section.L8(DW_RLE_offset_pair.0).uleb(0x10400).uleb(0x10500); + expect_range(0x0201_0400, 0x0201_0500); + + section = section + .L8(DW_RLE_start_end.0) + .word(size, 0x201_0a00) + .word(size, 0x201_0b00); + expect_range(0x0201_0a00, 0x0201_0b00); + + section = section + .L8(DW_RLE_start_length.0) + .word(size, 0x201_0c00) + .uleb(0x100); + expect_range(0x0201_0c00, 0x0201_0d00); + + // An offset pair that starts at 0. + section = section.L8(DW_RLE_base_address.0).word(size, 0); + section = section.L8(DW_RLE_offset_pair.0).uleb(0).uleb(1); + expect_range(0, 1); + + // An offset pair that ends at -1. + section = section.L8(DW_RLE_base_address.0).word(size, 0); + section = section.L8(DW_RLE_offset_pair.0).uleb(0).uleb(tombstone); + expect_range(0, tombstone); + + section = section.L8(DW_RLE_base_addressx.0).uleb(0); + section = section.L8(DW_RLE_offset_pair.0).uleb(0x10100).uleb(0x10200); + expect_range(0x0301_0100, 0x0301_0200); + + section = section.L8(DW_RLE_startx_endx.0).uleb(1).uleb(2); + expect_range(0x0301_0300, 0x0301_0400); + + section = section.L8(DW_RLE_startx_length.0).uleb(3).uleb(0x100); + expect_range(0x0301_0500, 0x0301_0600); + + // Tombstone entries, all of which should be ignored. + section = section.L8(DW_RLE_base_addressx.0).uleb(4); + section = section.L8(DW_RLE_offset_pair.0).uleb(0x11100).uleb(0x11200); + + section = section.L8(DW_RLE_base_address.0).word(size, tombstone); + section = section.L8(DW_RLE_offset_pair.0).uleb(0x11300).uleb(0x11400); + + section = section.L8(DW_RLE_startx_endx.0).uleb(4).uleb(5); + section = section.L8(DW_RLE_startx_length.0).uleb(4).uleb(0x100); + section = section + .L8(DW_RLE_start_end.0) + .word(size, tombstone) + .word(size, 0x201_1500); + section = section + .L8(DW_RLE_start_length.0) + .word(size, tombstone) + .uleb(0x100); + + // Ignore some instances of 0 for tombstone. + section = section.L8(DW_RLE_startx_endx.0).uleb(6).uleb(6); + section = section + .L8(DW_RLE_start_end.0) + .word(size, tombstone_0) + .word(size, tombstone_0); + + // Ignore empty ranges. + section = section.L8(DW_RLE_base_address.0).word(size, 0); + section = section.L8(DW_RLE_offset_pair.0).uleb(0).uleb(0); + section = section.L8(DW_RLE_base_address.0).word(size, 0x10000); + section = section.L8(DW_RLE_offset_pair.0).uleb(0x1234).uleb(0x1234); + + // A valid range after the tombstones. + section = section + .L8(DW_RLE_start_end.0) + .word(size, 0x201_1600) + .word(size, 0x201_1700); + expect_range(0x0201_1600, 0x0201_1700); + + section = section.L8(DW_RLE_end_of_list.0); + section = section.mark(&end); + // Some extra data. + section = section.word(size, 0x1234_5678); + length.set_const((&end - &start) as u64); + + let offset = RangeListsOffset((&first - §ion.start()) as usize); + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&[], LittleEndian); + let debug_rnglists = DebugRngLists::new(&buf, LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let mut ranges = rnglists + .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + for expected_range in expected_ranges { + let range = ranges.next(); + assert_eq!( + range, + Ok(Some(expected_range)), + "read {:x?}, expect {:x?}", + range, + expected_range + ); + } + assert_eq!(ranges.next(), Ok(None)); + + // An offset at the end of buf. + let mut ranges = rnglists + .ranges( + RangeListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + } + } + + #[test] + fn test_raw_range() { + let range = RawRange { + begin: 0, + end: 0xffff_ffff, + }; + assert!(!range.is_end()); + assert!(!range.is_base_address(4)); + assert!(!range.is_base_address(8)); + + let range = RawRange { begin: 0, end: 0 }; + assert!(range.is_end()); + assert!(!range.is_base_address(4)); + assert!(!range.is_base_address(8)); + + let range = RawRange { + begin: 0xffff_ffff, + end: 0, + }; + assert!(!range.is_end()); + assert!(range.is_base_address(4)); + assert!(!range.is_base_address(8)); + + let range = RawRange { + begin: 0xffff_ffff_ffff_ffff, + end: 0, + }; + assert!(!range.is_end()); + assert!(!range.is_base_address(4)); + assert!(range.is_base_address(8)); + } + + #[test] + fn test_ranges() { + for size in [4, 8] { + let base = u64::ones_sized(size); + let tombstone = u64::ones_sized(size) - 1; + let start = Label::new(); + let first = Label::new(); + let mut section = Section::with_endian(Endian::Little) + // A range before the offset. + .mark(&start) + .word(size, 0x10000) + .word(size, 0x10100) + .mark(&first); + + let mut expected_ranges = Vec::new(); + let mut expect_range = |begin, end| { + expected_ranges.push(Range { begin, end }); + }; + + // A normal range. + section = section.word(size, 0x10200).word(size, 0x10300); + expect_range(0x0101_0200, 0x0101_0300); + // A base address selection followed by a normal range. + section = section.word(size, base).word(size, 0x0200_0000); + section = section.word(size, 0x10400).word(size, 0x10500); + expect_range(0x0201_0400, 0x0201_0500); + // An empty range followed by a normal range. + section = section.word(size, 0x10600).word(size, 0x10600); + section = section.word(size, 0x10800).word(size, 0x10900); + expect_range(0x0201_0800, 0x0201_0900); + // A range that starts at 0. + section = section.word(size, base).word(size, 0); + section = section.word(size, 0).word(size, 1); + expect_range(0, 1); + // A range that ends at -1. + section = section.word(size, base).word(size, 0); + section = section.word(size, 0).word(size, base); + expect_range(0, base); + // A normal range with tombstone. + section = section.word(size, tombstone).word(size, tombstone); + // A base address selection with tombstone followed by a normal range. + section = section.word(size, base).word(size, tombstone); + section = section.word(size, 0x10a00).word(size, 0x10b00); + // A range end. + section = section.word(size, 0).word(size, 0); + // Some extra data. + section = section.word(size, 0x1234_5678); + + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&buf, LittleEndian); + let debug_rnglists = DebugRngLists::new(&[], LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let offset = RangeListsOffset((&first - &start) as usize); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: size, + }; + let mut ranges = rnglists + .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + for expected_range in expected_ranges { + let range = ranges.next(); + assert_eq!( + range, + Ok(Some(expected_range)), + "read {:x?}, expect {:x?}", + range, + expected_range + ); + } + assert_eq!(ranges.next(), Ok(None)); + + // An offset at the end of buf. + let mut ranges = rnglists + .ranges( + RangeListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + } + } + + #[test] + fn test_ranges_invalid() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // An invalid range. + .L32(0x20000).L32(0x10000) + // An invalid range after wrapping. + .L32(0x20000).L32(0xff01_0000); + + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&buf, LittleEndian); + let debug_rnglists = DebugRngLists::new(&[], LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + + // An invalid range. + let mut ranges = rnglists + .ranges( + RangeListsOffset(0x0), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + + // An invalid range after wrapping. + let mut ranges = rnglists + .ranges( + RangeListsOffset(0x8), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + + // An invalid offset. + match rnglists.ranges( + RangeListsOffset(buf.len() + 1), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_get_offset() { + for format in [Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version: 5, + address_size: 4, + }; + + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(encoding.version) + .D8(encoding.address_size) + .D8(0) + .D32(20) + .mark(&first); + for i in 0..20 { + section = section.word(format.word_size(), 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + let section = section.get_contents().unwrap(); + + let debug_ranges = DebugRanges::from(EndianSlice::new(&[], LittleEndian)); + let debug_rnglists = DebugRngLists::from(EndianSlice::new(§ion, LittleEndian)); + let ranges = RangeLists::new(debug_ranges, debug_rnglists); + + let base = DebugRngListsBase((&first - &zero) as usize); + assert_eq!( + ranges.get_offset(encoding, base, DebugRngListsIndex(0)), + Ok(RangeListsOffset(base.0 + 1000)) + ); + assert_eq!( + ranges.get_offset(encoding, base, DebugRngListsIndex(19)), + Ok(RangeListsOffset(base.0 + 1019)) + ); + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/str.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/str.rs new file mode 100644 index 0000000..df7ab1c --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/str.rs
@@ -0,0 +1,291 @@ +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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugStr, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_str_section_somehow = || &buf; + /// let debug_str = DebugStr::new(read_debug_str_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_str_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_str_section, endian)) + } +} + +impl<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. + /// + /// Used by `DwarfSections::borrow`. + 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. + /// + /// Used by `DwarfSections::borrow`. + 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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugLineStr, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_line_str_section_somehow = || &buf; + /// let debug_str = DebugLineStr::new(read_debug_line_str_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_line_str_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_line_str_section, endian)) + } +} + +impl<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. + /// + /// Used by `DwarfSections::borrow`. + 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 [Format::Dwarf32, Format::Dwarf64] { + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(5) + .D16(0) + .mark(&first); + for i in 0..20 { + section = section.word(format.word_size(), 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + + let section = section.get_contents().unwrap(); + let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(§ion, LittleEndian)); + let base = DebugStrOffsetsBase((&first - &zero) as usize); + + assert_eq!( + debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(0)), + Ok(DebugStrOffset(1000)) + ); + assert_eq!( + debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(19)), + Ok(DebugStrOffset(1019)) + ); + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/read/unit.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/unit.rs new file mode 100644 index 0000000..e8e3862 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/unit.rs
@@ -0,0 +1,6128 @@ +//! Functions for parsing DWARF `.debug_info` and `.debug_types` sections. + +use core::cell::Cell; +use core::ops::{Range, RangeFrom, RangeTo}; + +use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugAddrIndex, DebugInfoOffset, DebugLineOffset, + DebugLineStrOffset, DebugLocListsBase, DebugLocListsIndex, DebugMacinfoOffset, + DebugMacroOffset, DebugRngListsBase, DebugRngListsIndex, DebugStrOffset, DebugStrOffsetsBase, + DebugStrOffsetsIndex, DebugTypeSignature, DebugTypesOffset, DwoId, Encoding, Format, + LocationListsOffset, RawRangeListsOffset, SectionId, UnitSectionOffset, +}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::abbrev::get_attribute_size; +use crate::read::{ + Abbreviation, Abbreviations, AttributeSpecification, DebugAbbrev, DebugStr, EndianSlice, Error, + Expression, Reader, ReaderOffset, Result, Section, UnitOffset, +}; + +impl<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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugInfo, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_info_section_somehow = || &buf; + /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_info_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_info_section, endian)) + } +} + +impl<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. + /// + /// Used by `DwarfSections::borrow`. + 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_address_size()?; + // Before DWARF5, all units in the .debug_info section are compilation + // units, and all units in the .debug_types section are type units. + unit_type = match unit_offset { + UnitSectionOffset::DebugInfoOffset(_) => constants::DW_UT_compile, + UnitSectionOffset::DebugTypesOffset(_) => constants::DW_UT_type, + }; + } else if version == 5 { + unit_type = parse_unit_type(&mut rest)?; + address_size = rest.read_address_size()?; + abbrev_offset = parse_debug_abbrev_offset(&mut rest, format)?; + } else { + return Err(Error::UnknownVersion(u64::from(version))); + } + let encoding = Encoding { + format, + version, + address_size, + }; + + // Parse any data specific to this type of unit. + let unit_type = match unit_type { + constants::DW_UT_compile => UnitType::Compilation, + constants::DW_UT_type => { + let type_signature = parse_type_signature(&mut rest)?; + let type_offset = parse_type_offset(&mut rest, format)?; + UnitType::Type { + type_signature, + type_offset, + } + } + constants::DW_UT_partial => UnitType::Partial, + constants::DW_UT_skeleton => { + let dwo_id = parse_dwo_id(&mut rest)?; + UnitType::Skeleton(dwo_id) + } + constants::DW_UT_split_compile => { + let dwo_id = parse_dwo_id(&mut rest)?; + UnitType::SplitCompilation(dwo_id) + } + constants::DW_UT_split_type => { + let type_signature = parse_type_signature(&mut rest)?; + let type_offset = parse_type_offset(&mut rest, format)?; + UnitType::SplitType { + type_signature, + type_offset, + } + } + _ => return Err(Error::UnsupportedUnitType), + }; + + Ok(UnitHeader::new( + encoding, + unit_length, + unit_type, + abbrev_offset, + unit_offset, + rest, + )) +} + +/// Parse a dwo_id from a header +fn parse_dwo_id<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. + #[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 attrs.next()?.is_some() {} + Ok(attrs.input) + } + } + + /// Use the `DW_AT_sibling` attribute to find the input buffer for the + /// next sibling. Returns `None` if the attribute is missing or invalid. + fn sibling(&self) -> Option<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. + #[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(code))?; + Ok(Some(DebuggingInformationEntry { + offset: UnitOffset(offset), + attrs_slice: input.clone(), + attrs_len: Cell::new(None), + abbrev, + unit, + })) + } +} + +/// The value of an attribute in a `DebuggingInformationEntry`. +// +// Set the discriminant size so that all variants use the same alignment +// for their data. This gives better code generation in `parse_attribute`. +#[repr(u64)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AttributeValue<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". + 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 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<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(form)); + } + }; + let attr = Attribute { + name: spec.name(), + value, + }; + return Ok(attr); + } +} + +pub(crate) fn skip_attributes<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(form)); + } + }; + break; + } + } + if skip_bytes != R::Offset::from_u8(0) { + // Skip the remaining accumulated skip bytes. + input.skip(skip_bytes)?; + } + Ok(()) +} + +/// An iterator over a particular entry's attributes. +/// +/// See [the documentation for +/// `DebuggingInformationEntry::attrs()`](./struct.DebuggingInformationEntry.html#method.attrs) +/// for details. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Clone, Copy, Debug)] +pub struct AttrsIter<'abbrev, 'entry, 'unit, R: Reader> { + input: R, + attributes: &'abbrev [AttributeSpecification], + entry: &'entry DebuggingInformationEntry<'abbrev, 'unit, R>, +} + +impl<'abbrev, 'entry, 'unit, R: Reader> AttrsIter<'abbrev, 'entry, 'unit, R> { + /// Advance the iterator and return the next attribute. + /// + /// Returns `None` when iteration is finished. If an error + /// occurs while parsing the next attribute, then this error + /// is returned, and all subsequent calls return `None`. + #[inline(always)] + pub fn next(&mut self) -> Result<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(code))?; + 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()); + /// ``` + 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 macOS, etc. + /// + /// ``` + /// use gimli::{DebugTypes, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_types_section_somehow = || &buf; + /// let debug_types = DebugTypes::new(read_debug_types_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_types_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_types_section, endian)) + } +} + +impl<T> DebugTypes<T> { + /// Create a `DebugTypes` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// Used by `DwarfSections::borrow`. + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTypes<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<E>(self, unit: &mut UnitHeader<EndianSlice<'_, 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<E>(self, unit: &mut UnitHeader<EndianSlice<'_, 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..=4 => section + .mark(&start) + .L16(unit.version()) + .offset(unit.debug_abbrev_offset.0, unit.format()) + .D8(unit.address_size()), + 5 => section + .mark(&start) + .L16(unit.version()) + .D8(unit.type_().dw_ut().0) + .D8(unit.address_size()) + .offset(unit.debug_abbrev_offset.0, unit.format()), + _ => unreachable!(), + }; + + let section = match unit.type_() { + UnitType::Compilation | UnitType::Partial => { + unit.unit_offset = DebugInfoOffset(size as usize).into(); + section + } + UnitType::Type { + type_signature, + type_offset, + } + | UnitType::SplitType { + type_signature, + type_offset, + } => { + if unit.version() == 5 { + unit.unit_offset = DebugInfoOffset(size as usize).into(); + } else { + unit.unit_offset = DebugTypesOffset(size as usize).into(); + } + section + .L64(type_signature.0) + .offset(type_offset.0, unit.format()) + } + UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => { + unit.unit_offset = DebugInfoOffset(size as usize).into(); + section.L64(dwo_id.0) + } + }; + + let section = section.append_bytes(unit.entries_buf.slice()).mark(&end); + + unit.unit_length = (&end - &start) as usize; + length.set_const(unit.unit_length as u64); + + section + } + + fn die<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(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_debug_abbrev_offset_64() { + let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); + let buf = section.get_contents().unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_abbrev_offset(buf, Format::Dwarf64) { + Ok(val) => assert_eq!(val, DebugAbbrevOffset(0x0807_0605_0403_0201)), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_abbrev_offset_64_incomplete() { + let buf = [0x01, 0x02]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_abbrev_offset(buf, Format::Dwarf64) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_info_offset_32() { + let section = Section::with_endian(Endian::Little).L32(0x0403_0201); + let buf = section.get_contents().unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf32) { + Ok(val) => assert_eq!(val, DebugInfoOffset(0x0403_0201)), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_info_offset_32_incomplete() { + let buf = [0x01, 0x02]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf32) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_debug_info_offset_64() { + let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); + let buf = section.get_contents().unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf64) { + Ok(val) => assert_eq!(val, DebugInfoOffset(0x0807_0605_0403_0201)), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_info_offset_64_incomplete() { + let buf = [0x01, 0x02]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf64) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_units() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut unit64 = UnitHeader { + encoding: Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let mut unit32 = UnitHeader { + encoding: Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut unit64) + .unit(&mut unit32); + let buf = section.get_contents().unwrap(); + + let debug_info = DebugInfo::new(&buf, LittleEndian); + let mut units = debug_info.units(); + + assert_eq!(units.next(), Ok(Some(unit64))); + assert_eq!(units.next(), Ok(Some(unit32))); + assert_eq!(units.next(), Ok(None)); + } + + #[test] + fn test_unit_version_unknown_version() { + let buf = [0x02, 0x00, 0x00, 0x00, 0xab, 0xcd]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_unit_header(rest, DebugInfoOffset(0).into()) { + Err(Error::UnknownVersion(0xcdab)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + + let buf = [0x02, 0x00, 0x00, 0x00, 0x1, 0x0]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_unit_header(rest, DebugInfoOffset(0).into()) { + Err(Error::UnknownVersion(1)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_unit_version_incomplete() { + let buf = [0x01, 0x00, 0x00, 0x00, 0x04]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_unit_header(rest, DebugInfoOffset(0).into()) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_partial_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Partial, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_partial_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Partial, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_skeleton_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_skeleton_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_split_compilation_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_split_compilation_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_type_offset_32_ok() { + let buf = [0x12, 0x34, 0x56, 0x78, 0x00]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_type_offset(rest, Format::Dwarf32) { + Ok(offset) => { + assert_eq!(rest.len(), 1); + assert_eq!(UnitOffset(0x7856_3412), offset); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_type_offset_64_ok() { + let buf = [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0x00]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_type_offset(rest, Format::Dwarf64) { + Ok(offset) => { + assert_eq!(rest.len(), 1); + assert_eq!(UnitOffset(0xffde_bc9a_7856_3412), offset); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_parse_type_offset_incomplete() { + // Need at least 4 bytes. + let buf = [0xff, 0xff, 0xff]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_type_offset(rest, Format::Dwarf32) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_type_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugTypesOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugTypesOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_type_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412_7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugTypesOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugTypesOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_type_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_type_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412_7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_split_type_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitType { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_split_type_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitType { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412_7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + fn section_contents<F>(f: 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(u8::MAX), + Some(u64::from(u8::MAX)), + Some(-1), + ), + (AttributeValue::Data2(1), Some(1), Some(1)), + ( + AttributeValue::Data2(u16::MAX), + Some(u64::from(u16::MAX)), + Some(-1), + ), + (AttributeValue::Data4(1), Some(1), Some(1)), + ( + AttributeValue::Data4(u32::MAX), + Some(u64::from(u32::MAX)), + Some(-1), + ), + (AttributeValue::Data8(1), Some(1), Some(1)), + (AttributeValue::Data8(u64::MAX), Some(u64::MAX), Some(-1)), + (AttributeValue::Sdata(1), Some(1), Some(1)), + (AttributeValue::Sdata(-1), None, Some(-1)), + (AttributeValue::Udata(1), Some(1), Some(1)), + (AttributeValue::Udata(1u64 << 63), Some(1u64 << 63), None), + ]; + for test in tests.iter() { + let (value, expect_udata, expect_sdata) = *test; + let attribute = Attribute { + name: DW_AT_data_member_location, + value, + }; + assert_eq!(attribute.udata_value(), expect_udata); + assert_eq!(attribute.sdata_value(), expect_sdata); + } + } + + fn test_parse_attribute_unit<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 => { + panic!("Unexpected parse result = {:#?}", otherwise); + } + }; + } + + #[test] + fn test_parse_attribute_addr() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_addr; + let value = AttributeValue::Addr(0x0403_0201); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addr8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_addr; + let value = AttributeValue::Addr(0x0807_0605_0403_0201); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block1() { + // Length of data (3), three bytes of data, two bytes of left over input. + let buf = [0x03, 0x09, 0x09, 0x09, 0x00, 0x00]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block1; + let value = AttributeValue::Block(EndianSlice::new(&buf[1..4], LittleEndian)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block2() { + // Two byte length of data (2), two bytes of data, two bytes of left over input. + let buf = [0x02, 0x00, 0x09, 0x09, 0x00, 0x00]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block2; + let value = AttributeValue::Block(EndianSlice::new(&buf[2..4], LittleEndian)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block4() { + // Four byte length of data (2), two bytes of data, no left over input. + let buf = [0x02, 0x00, 0x00, 0x00, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block4; + let value = AttributeValue::Block(EndianSlice::new(&buf[4..], LittleEndian)); + test_parse_attribute(&buf, 6, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block() { + // LEB length of data (2, one byte), two bytes of data, no left over input. + let buf = [0x02, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block; + let value = AttributeValue::Block(EndianSlice::new(&buf[1..], LittleEndian)); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data1() { + let buf = [0x03]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data1; + let value = AttributeValue::Data1(0x03); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data2() { + let buf = [0x02, 0x01, 0x0]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data2; + let value = AttributeValue::Data2(0x0102); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data4; + let value = AttributeValue::Data4(0x0403_0201); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data8; + let value = AttributeValue::Data8(0x0807_0605_0403_0201); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_udata() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_udata; + let value = AttributeValue::Udata(4097); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_sdata() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::signed(&mut writable, -4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_sdata; + let value = AttributeValue::Sdata(-4097); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_exprloc() { + // LEB length of data (2, one byte), two bytes of data, one byte left over input. + let buf = [0x02, 0x99, 0x99, 0x11]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_exprloc; + let value = AttributeValue::Exprloc(Expression(EndianSlice::new(&buf[1..3], LittleEndian))); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_flag_true() { + let buf = [0x42]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_flag; + let value = AttributeValue::Flag(true); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_flag_false() { + let buf = [0x00]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_flag; + let value = AttributeValue::Flag(false); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_flag_present() { + let buf = [0x01, 0x02, 0x03, 0x04]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_flag_present; + let value = AttributeValue::Flag(true); + // DW_FORM_flag_present does not consume any bytes of the input stream. + test_parse_attribute(&buf, 0, &unit, form, value); + } + + #[test] + fn test_parse_attribute_sec_offset_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_sec_offset; + let value = AttributeValue::SecOffset(0x0403_0201); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_sec_offset_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_sec_offset; + let value = AttributeValue::SecOffset(0x0807_0605_0403_0201); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref1() { + let buf = [0x03]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref1; + let value = AttributeValue::UnitRef(UnitOffset(3)); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref2() { + let buf = [0x02, 0x01, 0x0]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref2; + let value = AttributeValue::UnitRef(UnitOffset(258)); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref4; + let value = AttributeValue::UnitRef(UnitOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_ref8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref8; + let value = AttributeValue::UnitRef(UnitOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref_sup4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_sup4; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_ref_sup8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_sup8; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refudata() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_udata; + let value = AttributeValue::UnitRef(UnitOffset(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refaddr_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_refaddr_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refaddr_version2() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let mut unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + unit.encoding.version = 2; + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_refaddr8_version2() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let mut unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); + unit.encoding.version = 2; + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_gnu_ref_alt_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_GNU_ref_alt; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_gnu_ref_alt_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_GNU_ref_alt; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refsig8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_sig8; + let value = AttributeValue::DebugTypesRef(DebugTypeSignature(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_string() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_string; + let value = AttributeValue::String(EndianSlice::new(&buf[..5], LittleEndian)); + test_parse_attribute(&buf, 6, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strp_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_strp; + let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_strp_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strp; + let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strp_sup_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_strp_sup; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_strp_sup_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strp_sup; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_gnu_strp_alt_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_GNU_strp_alt; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_gnu_strp_alt_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_GNU_strp_alt; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_strx; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx1() { + let buf = [0x01, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx1; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x01)); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx2() { + let buf = [0x01, 0x02, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx2; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0201)); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx3() { + let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx3; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x03_0201)); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx4; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_addrx; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx1() { + let buf = [0x01, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx1; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x01)); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx2() { + let buf = [0x01, 0x02, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx2; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0201)); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx3() { + let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx3; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x03_0201)); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx4; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_loclistx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_loclistx; + let value = AttributeValue::DebugLocListsIndex(DebugLocListsIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_rnglistx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_rnglistx; + let value = AttributeValue::DebugRngListsIndex(DebugRngListsIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_indirect() { + let mut buf = [0; 100]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, constants::DW_FORM_udata.0.into()) + .expect("should write udata") + + leb128::write::unsigned(&mut writable, 9_999_999).expect("should write value") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_indirect; + let value = AttributeValue::Udata(9_999_999); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_indirect_implicit_const() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut buf = [0; 100]; + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, constants::DW_FORM_implicit_const.0.into()) + .expect("should write implicit_const"); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + let spec = + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_indirect, None); + assert_eq!( + parse_attribute(input, encoding, spec), + Err(Error::InvalidImplicitConst) + ); + } + + #[test] + fn test_attrs_iter() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let unit = UnitHeader::new( + encoding, + 7, + UnitType::Compilation, + DebugAbbrevOffset(0x0807_0605), + DebugInfoOffset(0).into(), + EndianSlice::new(&[], LittleEndian), + ); + + let abbrev = Abbreviation::new( + 42, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), + AttributeSpecification::new( + constants::DW_AT_high_pc, + constants::DW_FORM_addr, + None, + ), + ] + .into(), + ); + + // "foo", 42, 1337, 4 dangling bytes of 0xaa where children would be + let buf = [ + 0x66, 0x6f, 0x6f, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x39, 0x05, 0x00, 0x00, 0xaa, 0xaa, + 0xaa, 0xaa, + ]; + + let entry = DebuggingInformationEntry { + offset: UnitOffset(0), + attrs_slice: EndianSlice::new(&buf, LittleEndian), + attrs_len: Cell::new(None), + abbrev: &abbrev, + unit: &unit, + }; + + let mut attrs = AttrsIter { + input: EndianSlice::new(&buf, LittleEndian), + attributes: abbrev.attributes(), + entry: &entry, + }; + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_name, + value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), + } + ); + } + otherwise => { + panic!("Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_low_pc, + value: AttributeValue::Addr(0x2a), + } + ); + } + otherwise => { + panic!("Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_high_pc, + value: AttributeValue::Addr(0x539), + } + ); + } + otherwise => { + panic!("Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + assert!(attrs.next().expect("should parse next").is_none()); + assert!(entry.attrs_len.get().is_some()); + assert_eq!( + entry.attrs_len.get().expect("should have entry.attrs_len"), + buf.len() - 4 + ) + } + + #[test] + fn test_attrs_iter_incomplete() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let unit = UnitHeader::new( + encoding, + 7, + UnitType::Compilation, + DebugAbbrevOffset(0x0807_0605), + DebugInfoOffset(0).into(), + EndianSlice::new(&[], LittleEndian), + ); + + let abbrev = Abbreviation::new( + 42, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), + AttributeSpecification::new( + constants::DW_AT_high_pc, + constants::DW_FORM_addr, + None, + ), + ] + .into(), + ); + + // "foo" + let buf = [0x66, 0x6f, 0x6f, 0x00]; + + let entry = DebuggingInformationEntry { + offset: UnitOffset(0), + attrs_slice: EndianSlice::new(&buf, LittleEndian), + attrs_len: Cell::new(None), + abbrev: &abbrev, + unit: &unit, + }; + + let mut attrs = AttrsIter { + input: EndianSlice::new(&buf, LittleEndian), + attributes: abbrev.attributes(), + entry: &entry, + }; + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_name, + value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), + } + ); + } + otherwise => { + panic!("Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + // Return error for incomplete attribute. + assert!(attrs.next().is_err()); + assert!(entry.attrs_len.get().is_none()); + + // Return error for all subsequent calls. + assert!(attrs.next().is_err()); + assert!(attrs.next().is_err()); + assert!(attrs.next().is_err()); + assert!(attrs.next().is_err()); + assert!(entry.attrs_len.get().is_none()); + } + + fn assert_entry_name<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 = §ion.get_contents().unwrap(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_entry(&mut cursor, "001"); + assert_next_entry(&mut cursor, "002"); + + { + // Entry code is present, but none of the attributes. + cursor + .next_entry() + .expect("Should parse next entry") + .expect("Should have an entry"); + let entry = cursor.current().expect("Should have an entry result"); + assert!(entry.attrs().next().is_err()); + } + + assert!(cursor.next_entry().is_err()); + assert!(cursor.next_entry().is_err()); + } + + #[test] + fn test_cursor_next_entry() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_entry(&mut cursor, "001"); + assert_next_entry(&mut cursor, "002"); + assert_next_entry(&mut cursor, "003"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "004"); + assert_next_entry(&mut cursor, "005"); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "006"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "007"); + assert_next_entry(&mut cursor, "008"); + assert_next_entry(&mut cursor, "009"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "010"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + + assert!(cursor + .next_entry() + .expect("Should parse next entry") + .is_none()); + assert!(cursor.current().is_none()); + } + + #[test] + fn test_cursor_next_dfs() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_dfs(&mut cursor, "001", 0); + assert_next_dfs(&mut cursor, "002", 1); + assert_next_dfs(&mut cursor, "003", 1); + assert_next_dfs(&mut cursor, "004", -1); + assert_next_dfs(&mut cursor, "005", 1); + assert_next_dfs(&mut cursor, "006", 0); + assert_next_dfs(&mut cursor, "007", -1); + assert_next_dfs(&mut cursor, "008", 1); + assert_next_dfs(&mut cursor, "009", 1); + assert_next_dfs(&mut cursor, "010", -2); + + assert!(cursor.next_dfs().expect("Should parse next dfs").is_none()); + assert!(cursor.current().is_none()); + } + + #[test] + fn test_cursor_next_sibling_no_sibling_ptr() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_dfs(&mut cursor, "001", 0); + + // Down to the first child of the root entry. + + assert_next_dfs(&mut cursor, "002", 1); + + // Now iterate all children of the root via `next_sibling`. + + assert_next_sibling(&mut cursor, "004"); + assert_next_sibling(&mut cursor, "007"); + assert_next_sibling(&mut cursor, "010"); + + // There should be no more siblings. + + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor.current().is_none()); + } + + #[test] + fn test_cursor_next_sibling_continuation() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_dfs(&mut cursor, "001", 0); + + // Down to the first child of the root entry. + + assert_next_dfs(&mut cursor, "002", 1); + + // Get the next sibling, then iterate its children + + assert_next_sibling(&mut cursor, "004"); + assert_next_dfs(&mut cursor, "005", 1); + assert_next_sibling(&mut cursor, "006"); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + + // And we should be able to continue with the children of the root entry. + + assert_next_dfs(&mut cursor, "007", -1); + assert_next_sibling(&mut cursor, "010"); + + // There should be no more siblings. + + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor.current().is_none()); + } + + fn entries_cursor_sibling_abbrev_buf() -> Vec<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 => { + panic!("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 => { + panic!("Unexpected parse result = {:#?}", otherwise); + } + } + } + + let abbrevs_buf = entries_tree_tests_debug_abbrevs_buf(); + let debug_abbrev = DebugAbbrev::new(&abbrevs_buf, LittleEndian); + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(&[], LittleEndian), + }; + let header_size = unit.size_of_header(); + let (entries_buf, entry2) = entries_tree_tests_debug_info_buf(header_size); + unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); + let info_buf = Section::with_endian(Endian::Little) + .unit(&mut unit) + .get_contents() + .unwrap(); + let debug_info = DebugInfo::new(&info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("Should parse unit") + .expect("and it should be some"); + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + let mut tree = unit + .entries_tree(&abbrevs, None) + .expect("Should have entries tree"); + + // Test we can restart iteration of the tree. + { + let mut iter = assert_entry(tree.root().map(Some), "root"); + assert_entry(iter.next(), "1"); + } + { + let mut iter = assert_entry(tree.root().map(Some), "root"); + assert_entry(iter.next(), "1"); + } + + let mut iter = assert_entry(tree.root().map(Some), "root"); + { + // Test iteration with children. + let mut iter = assert_entry(iter.next(), "1"); + { + // Test iteration with children flag, but no children. + let mut iter = assert_entry(iter.next(), "1a"); + assert_null(iter.next()); + assert_null(iter.next()); + } + { + // Test iteration without children flag. + let mut iter = assert_entry(iter.next(), "1b"); + assert_null(iter.next()); + assert_null(iter.next()); + } + assert_null(iter.next()); + assert_null(iter.next()); + } + { + // Test skipping over children. + let mut iter = assert_entry(iter.next(), "2"); + assert_entry(iter.next(), "2a"); + assert_entry(iter.next(), "2b"); + assert_null(iter.next()); + } + { + // Test skipping after partial iteration. + let mut iter = assert_entry(iter.next(), "3"); + { + let mut iter = assert_entry(iter.next(), "3a"); + assert_entry(iter.next(), "3a1"); + // Parent iter should be able to skip over "3a2". + } + assert_entry(iter.next(), "3b"); + assert_null(iter.next()); + } + assert_entry(iter.next(), "final"); + assert_null(iter.next()); + + // Test starting at an offset. + let mut tree = unit + .entries_tree(&abbrevs, Some(entry2)) + .expect("Should have entries tree"); + let mut iter = assert_entry(tree.root().map(Some), "2"); + assert_entry(iter.next(), "2a"); + assert_entry(iter.next(), "2b"); + assert_null(iter.next()); + } + + #[test] + fn test_entries_raw() { + fn assert_abbrev<'abbrev, Endian>( + entries: &mut EntriesRaw<'abbrev, '_, EndianSlice<'_, Endian>>, + tag: DwTag, + ) -> &'abbrev Abbreviation + where + Endian: Endianity, + { + let abbrev = entries + .read_abbreviation() + .expect("Should parse abbrev") + .expect("Should have abbrev"); + assert_eq!(abbrev.tag(), tag); + abbrev + } + + fn assert_null<Endian>(entries: &mut EntriesRaw<'_, '_, EndianSlice<'_, Endian>>) + where + Endian: Endianity, + { + match entries.read_abbreviation() { + Ok(None) => {} + otherwise => { + panic!("Unexpected parse result = {:#?}", otherwise); + } + } + } + + fn assert_attr<Endian>( + entries: &mut EntriesRaw<'_, '_, EndianSlice<'_, 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/chromium_crates_io/vendor/gimli-v0_32/src/read/util.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/util.rs new file mode 100644 index 0000000..39adab3 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/util.rs
@@ -0,0 +1,265 @@ +#[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>]; +} + +// SAFETY: does not modify the content in storage. +unsafe impl<T, const N: usize> 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, const N: usize> 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 + } +} + +// SAFETY: does not modify the content in storage. +#[cfg(feature = "read")] +unsafe impl<T, const N: usize> Sealed for Box<[T; N]> { + type Storage = Box<[MaybeUninit<T>; N]>; + + fn new_storage() -> Self::Storage { + // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid. + Box::new(unsafe { MaybeUninit::uninit().assume_init() }) + } +} + +#[cfg(feature = "read")] +impl<T, const N: usize> ArrayLike for Box<[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>] { + &mut storage[..] + } +} + +#[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(&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_mut_ptr() as *mut T, 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/chromium_crates_io/vendor/gimli-v0_32/src/read/value.rs b/chromium_crates_io/vendor/gimli-v0_32/src/read/value.rs new file mode 100644 index 0000000..114736d --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/read/value.rs
@@ -0,0 +1,1621 @@ +//! Definitions for values used in DWARF expressions. + +use crate::constants; +#[cfg(feature = "read")] +use crate::read::{AttributeValue, DebuggingInformationEntry}; +use crate::read::{Error, Reader, Result}; + +/// Convert a u64 to an i64, with sign extension if required. +/// +/// This is primarily used when needing to treat `Value::Generic` +/// as a signed value. +#[inline] +fn sign_extend(value: u64, mask: u64) -> i64 { + let value = (value & mask) as i64; + let sign = ((mask >> 1) + 1) as i64; + (value ^ sign).wrapping_sub(sign) +} + +#[inline] +fn mask_bit_size(addr_mask: u64) -> u32 { + 64 - addr_mask.leading_zeros() +} + +/// The type of an entry on the DWARF stack. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ValueType { + /// The generic type, which is address-sized and of unspecified sign, + /// as specified in the DWARF 5 standard, section 2.5.1. + /// This type is also used to represent address base types. + Generic, + /// Signed 8-bit integer type. + I8, + /// Unsigned 8-bit integer type. + U8, + /// Signed 16-bit integer type. + I16, + /// Unsigned 16-bit integer type. + U16, + /// Signed 32-bit integer type. + I32, + /// Unsigned 32-bit integer type. + U32, + /// Signed 64-bit integer type. + I64, + /// Unsigned 64-bit integer type. + U64, + /// 32-bit floating point type. + F32, + /// 64-bit floating point type. + F64, +} + +/// The value of an entry on the DWARF stack. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Value { + /// A generic value, which is address-sized and of unspecified sign. + Generic(u64), + /// A signed 8-bit integer value. + I8(i8), + /// An unsigned 8-bit integer value. + U8(u8), + /// A signed 16-bit integer value. + I16(i16), + /// An unsigned 16-bit integer value. + U16(u16), + /// A signed 32-bit integer value. + I32(i32), + /// An unsigned 32-bit integer value. + U32(u32), + /// A signed 64-bit integer value. + I64(i64), + /// An unsigned 64-bit integer value. + U64(u64), + /// A 32-bit floating point value. + F32(f32), + /// A 64-bit floating point value. + F64(f64), +} + +impl ValueType { + /// The size in bits of a value for this type. + pub fn bit_size(self, addr_mask: u64) -> u32 { + match self { + ValueType::Generic => mask_bit_size(addr_mask), + ValueType::I8 | ValueType::U8 => 8, + ValueType::I16 | ValueType::U16 => 16, + ValueType::I32 | ValueType::U32 | ValueType::F32 => 32, + ValueType::I64 | ValueType::U64 | ValueType::F64 => 64, + } + } + + /// Construct a `ValueType` from the attributes of a base type DIE. + pub fn from_encoding(encoding: constants::DwAte, byte_size: u64) -> Option<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, + _ => 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 implicitly convert to a signed value. + // For now, we don't support them. + Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => { + return Err(Error::UnsupportedTypeOperation); + } + }; + Ok(value) + } + + /// Perform an addition operation. + /// + /// This operation requires matching types. + /// + /// This corresponds to the DWARF `DW_OP_plus` operation. + pub fn add(self, rhs: Value, addr_mask: u64) -> Result<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 implicitly convert to an unsigned value. + // For now, we don't support them. + Value::I8(_) | Value::I16(_) | Value::I32(_) | Value::I64(_) => { + return Err(Error::UnsupportedTypeOperation); + } + _ => return Err(Error::IntegralTypeRequired), + }; + Ok(value) + } + + /// Perform an arithmetic shift right operation. + /// + /// This operation requires a signed integral type for the value. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This operation requires an integral type for the shift length. + /// If the shift length exceeds the type size, then 0 is returned for positive values, + /// and -1 is returned for negative values. + /// If the shift length is negative then an error is returned. + /// + /// This corresponds to the DWARF `DW_OP_shra` operation. + pub fn shra(self, rhs: Value, addr_mask: u64) -> Result<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 implicitly convert to a signed value. + // For now, we don't support them. + Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => { + return Err(Error::UnsupportedTypeOperation); + } + _ => return Err(Error::IntegralTypeRequired), + }; + Ok(value) + } + + /// Perform the `==` relational operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_eq` operation. + pub fn eq(self, rhs: Value, addr_mask: u64) -> Result<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/chromium_crates_io/vendor/gimli-v0_32/src/test_util.rs b/chromium_crates_io/vendor/gimli-v0_32/src/test_util.rs new file mode 100644 index 0000000..706aaf9 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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/chromium_crates_io/vendor/gimli-v0_32/src/write/abbrev.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/abbrev.rs new file mode 100644 index 0000000..7cdfa96 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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/chromium_crates_io/vendor/gimli-v0_32/src/write/cfi.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/cfi.rs new file mode 100644 index 0000000..8ef683f --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/cfi.rs
@@ -0,0 +1,1070 @@ +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)?; + w.write_u8(0)?; // segment_selector_size + } + + w.write_uleb128(self.code_alignment_factor.into())?; + w.write_sleb128(self.data_alignment_factor.into())?; + + if !eh_frame && encoding.version == 1 { + let register = self.return_address_register.0 as u8; + if u16::from(register) != self.return_address_register.0 { + return Err(Error::ValueTooLarge); + } + w.write_u8(register)?; + } else { + w.write_uleb128(self.return_address_register.0.into())?; + } + + if augmentation { + let augmentation_length_offset = w.len(); + w.write_u8(0)?; + let augmentation_length_base = w.len(); + + if let Some(eh_pe) = self.lsda_encoding { + w.write_u8(eh_pe.0)?; + } + if let Some((eh_pe, address)) = self.personality { + w.write_u8(eh_pe.0)?; + w.write_eh_pointer(address, eh_pe, encoding.address_size)?; + } + if self.fde_address_encoding != constants::DW_EH_PE_absptr { + w.write_u8(self.fde_address_encoding.0)?; + } + + let augmentation_length = (w.len() - augmentation_length_base) as u64; + debug_assert!(augmentation_length < 0x80); + w.write_udata_at(augmentation_length_offset, augmentation_length, 1)?; + } + + for instruction in &self.instructions { + instruction.write(w, encoding, self)?; + } + + write_nop( + w, + encoding.format.word_size() as usize + w.len() - length_base, + encoding.address_size, + )?; + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(offset) + } +} + +/// A frame description entry. There should be one FDE per function. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FrameDescriptionEntry { + /// The initial address of the function. + address: Address, + + /// The length in bytes of the function. + length: u32, + + /// The address of the LSDA. + pub lsda: Option<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 augmentation_length_offset = w.len(); + w.write_u8(0)?; + let augmentation_length_base = w.len(); + + debug_assert_eq!(self.lsda.is_some(), cie.lsda_encoding.is_some()); + if let (Some(lsda), Some(lsda_encoding)) = (self.lsda, cie.lsda_encoding) { + w.write_eh_pointer(lsda, lsda_encoding, encoding.address_size)?; + } + + let augmentation_length = (w.len() - augmentation_length_base) as u64; + debug_assert!(augmentation_length < 0x80); + w.write_udata_at(augmentation_length_offset, augmentation_length, 1)?; + } + + let mut prev_offset = 0; + for (offset, instruction) in &self.instructions { + write_advance_loc(w, cie.code_alignment_factor, prev_offset, *offset)?; + prev_offset = *offset; + instruction.write(w, encoding, cie)?; + } + + write_nop( + w, + encoding.format.word_size() as usize + w.len() - length_base, + encoding.address_size, + )?; + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(()) + } +} + +/// An instruction in a frame description entry. +/// +/// This may be a CFA definition, a register rule, or some other directive. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum CallFrameInstruction { + /// Define the CFA rule to use the provided register and offset. + Cfa(Register, i32), + /// Update the CFA rule to use the provided register. The offset is unchanged. + CfaRegister(Register), + /// Update the CFA rule to use the provided offset. The register is unchanged. + CfaOffset(i32), + /// Define the CFA rule to use the provided expression. + CfaExpression(Expression), + + /// Restore the initial rule for the register. + Restore(Register), + /// The previous value of the register is not recoverable. + Undefined(Register), + /// The register has not been modified. + SameValue(Register), + /// The previous value of the register is saved at address CFA + offset. + Offset(Register, i32), + /// The previous value of the register is CFA + offset. + ValOffset(Register, i32), + /// The previous value of the register is stored in another register. + Register(Register, Register), + /// The previous value of the register is saved at address given by the expression. + Expression(Register, Expression), + /// The previous value of the register is given by the expression. + ValExpression(Register, Expression), + + /// Push all register rules onto a stack. + RememberState, + /// Pop all register rules off the stack. + RestoreState, + /// The size of the arguments that have been pushed onto the stack. + ArgsSize(u32), + + /// AAarch64 extension: negate the `RA_SIGN_STATE` pseudo-register. + NegateRaState, +} + +impl CallFrameInstruction { + fn write<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())?; + } + CallFrameInstruction::NegateRaState => { + w.write_u8(constants::DW_CFA_AARCH64_negate_ra_state.0)?; + } + } + 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, + frame, + 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, + frame, + convert_address, + &mut offset, + )? { + fde.instructions.push((offset, instruction)); + } + } + + Ok(fde) + } + } + + impl CallFrameInstruction { + fn from<R, Section>( + from_instruction: read::CallFrameInstruction<R::Offset>, + from_cie: &read::CommonInformationEntry<R>, + frame: &Section, + convert_address: &dyn Fn(u64) -> Option<Address>, + offset: &mut u32, + ) -> ConvertResult<Option<CallFrameInstruction>> + where + R: Reader<Offset = usize>, + Section: read::UnwindSection<R>, + { + let convert_expression = + |x| Expression::from(x, from_cie.encoding(), None, None, None, convert_address); + // TODO: validate integer type conversions + Ok(Some(match from_instruction { + read::CallFrameInstruction::SetLoc { .. } => { + return Err(ConvertError::UnsupportedCfiInstruction); + } + read::CallFrameInstruction::AdvanceLoc { delta } => { + *offset += delta * from_cie.code_alignment_factor() as u32; + return Ok(None); + } + read::CallFrameInstruction::DefCfa { register, offset } => { + CallFrameInstruction::Cfa(register, offset as i32) + } + read::CallFrameInstruction::DefCfaSf { + register, + factored_offset, + } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::Cfa(register, offset as i32) + } + read::CallFrameInstruction::DefCfaRegister { register } => { + CallFrameInstruction::CfaRegister(register) + } + + read::CallFrameInstruction::DefCfaOffset { offset } => { + CallFrameInstruction::CfaOffset(offset as i32) + } + read::CallFrameInstruction::DefCfaOffsetSf { factored_offset } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::CfaOffset(offset as i32) + } + read::CallFrameInstruction::DefCfaExpression { expression } => { + let expression = expression.get(frame)?; + CallFrameInstruction::CfaExpression(convert_expression(expression)?) + } + read::CallFrameInstruction::Undefined { register } => { + CallFrameInstruction::Undefined(register) + } + read::CallFrameInstruction::SameValue { register } => { + CallFrameInstruction::SameValue(register) + } + read::CallFrameInstruction::Offset { + register, + factored_offset, + } => { + let offset = factored_offset as i64 * from_cie.data_alignment_factor(); + CallFrameInstruction::Offset(register, offset as i32) + } + read::CallFrameInstruction::OffsetExtendedSf { + register, + factored_offset, + } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::Offset(register, offset as i32) + } + read::CallFrameInstruction::ValOffset { + register, + factored_offset, + } => { + let offset = factored_offset as i64 * from_cie.data_alignment_factor(); + CallFrameInstruction::ValOffset(register, offset as i32) + } + read::CallFrameInstruction::ValOffsetSf { + register, + factored_offset, + } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::ValOffset(register, offset as i32) + } + read::CallFrameInstruction::Register { + dest_register, + src_register, + } => CallFrameInstruction::Register(dest_register, src_register), + read::CallFrameInstruction::Expression { + register, + expression, + } => { + let expression = expression.get(frame)?; + CallFrameInstruction::Expression(register, convert_expression(expression)?) + } + read::CallFrameInstruction::ValExpression { + register, + expression, + } => { + let expression = expression.get(frame)?; + CallFrameInstruction::ValExpression(register, convert_expression(expression)?) + } + read::CallFrameInstruction::Restore { register } => { + CallFrameInstruction::Restore(register) + } + read::CallFrameInstruction::RememberState => CallFrameInstruction::RememberState, + read::CallFrameInstruction::RestoreState => CallFrameInstruction::RestoreState, + read::CallFrameInstruction::ArgsSize { size } => { + CallFrameInstruction::ArgsSize(size as u32) + } + read::CallFrameInstruction::NegateRaState => CallFrameInstruction::NegateRaState, + read::CallFrameInstruction::Nop => return Ok(None), + })) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::arch::X86_64; + use crate::read; + use crate::write::EndianVec; + use crate::{LittleEndian, Vendor}; + + #[test] + fn test_frame_table() { + for &version in &[1, 3, 4] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut frames = FrameTable::default(); + + let cie1 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); + let cie1_id = frames.add_cie(cie1.clone()); + assert_eq!(cie1_id, frames.add_cie(cie1.clone())); + + let mut cie2 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); + cie2.lsda_encoding = Some(constants::DW_EH_PE_absptr); + cie2.personality = + Some((constants::DW_EH_PE_absptr, Address::Constant(0x1234))); + cie2.signal_trampoline = true; + let cie2_id = frames.add_cie(cie2.clone()); + assert_ne!(cie1_id, cie2_id); + assert_eq!(cie2_id, frames.add_cie(cie2.clone())); + + let fde1 = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); + frames.add_fde(cie1_id, fde1.clone()); + + let fde2 = FrameDescriptionEntry::new(Address::Constant(0x2000), 0x20); + frames.add_fde(cie1_id, fde2.clone()); + + let mut fde3 = FrameDescriptionEntry::new(Address::Constant(0x3000), 0x30); + fde3.lsda = Some(Address::Constant(0x3300)); + frames.add_fde(cie2_id, fde3.clone()); + + let mut fde4 = FrameDescriptionEntry::new(Address::Constant(0x4000), 0x40); + fde4.lsda = Some(Address::Constant(0x4400)); + frames.add_fde(cie2_id, fde4.clone()); + + let mut cie3 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); + cie3.fde_address_encoding = + constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4; + cie3.lsda_encoding = + Some(constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4); + cie3.personality = Some(( + constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, + Address::Constant(0x1235), + )); + cie3.signal_trampoline = true; + let cie3_id = frames.add_cie(cie3.clone()); + assert_ne!(cie2_id, cie3_id); + assert_eq!(cie3_id, frames.add_cie(cie3.clone())); + + let mut fde5 = FrameDescriptionEntry::new(Address::Constant(0x5000), 0x50); + fde5.lsda = Some(Address::Constant(0x5500)); + frames.add_fde(cie3_id, fde5.clone()); + + // Test writing `.debug_frame`. + let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); + frames.write_debug_frame(&mut debug_frame).unwrap(); + + let mut read_debug_frame = + read::DebugFrame::new(debug_frame.slice(), LittleEndian); + read_debug_frame.set_address_size(address_size); + let convert_frames = FrameTable::from(&read_debug_frame, &|address| { + Some(Address::Constant(address)) + }) + .unwrap(); + assert_eq!(frames.cies, convert_frames.cies); + assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); + for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { + assert_eq!(a.1, b.1); + } + + if version == 1 { + // Test writing `.eh_frame`. + let mut eh_frame = EhFrame::from(EndianVec::new(LittleEndian)); + frames.write_eh_frame(&mut eh_frame).unwrap(); + + let mut read_eh_frame = read::EhFrame::new(eh_frame.slice(), LittleEndian); + read_eh_frame.set_address_size(address_size); + let convert_frames = FrameTable::from(&read_eh_frame, &|address| { + Some(Address::Constant(address)) + }) + .unwrap(); + assert_eq!(frames.cies, convert_frames.cies); + assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); + for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { + assert_eq!(a.1, b.1); + } + } + } + } + } + } + + #[test] + fn test_frame_instruction() { + let mut expression = Expression::new(); + expression.op_constu(0); + + let cie_instructions = [ + CallFrameInstruction::Cfa(X86_64::RSP, 8), + CallFrameInstruction::Offset(X86_64::RA, -8), + ]; + + let fde_instructions = [ + (0, CallFrameInstruction::Cfa(X86_64::RSP, 0)), + (0, CallFrameInstruction::Cfa(X86_64::RSP, -8)), + (2, CallFrameInstruction::CfaRegister(X86_64::RBP)), + (4, CallFrameInstruction::CfaOffset(8)), + (4, CallFrameInstruction::CfaOffset(0)), + (4, CallFrameInstruction::CfaOffset(-8)), + (6, CallFrameInstruction::CfaExpression(expression.clone())), + (8, CallFrameInstruction::Restore(Register(1))), + (8, CallFrameInstruction::Restore(Register(101))), + (10, CallFrameInstruction::Undefined(Register(2))), + (12, CallFrameInstruction::SameValue(Register(3))), + (14, CallFrameInstruction::Offset(Register(4), 16)), + (14, CallFrameInstruction::Offset(Register(104), 16)), + (16, CallFrameInstruction::ValOffset(Register(5), -24)), + (16, CallFrameInstruction::ValOffset(Register(5), 24)), + (18, CallFrameInstruction::Register(Register(6), Register(7))), + ( + 20, + CallFrameInstruction::Expression(Register(8), expression.clone()), + ), + ( + 22, + CallFrameInstruction::ValExpression(Register(9), expression.clone()), + ), + (24 + 0x80, CallFrameInstruction::RememberState), + (26 + 0x280, CallFrameInstruction::RestoreState), + (28 + 0x20280, CallFrameInstruction::ArgsSize(23)), + ]; + + let fde_instructions_aarch64 = [(0, CallFrameInstruction::NegateRaState)]; + + for &version in &[1, 3, 4] { + for &address_size in &[4, 8] { + for &vendor in &[Vendor::Default, Vendor::AArch64] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut frames = FrameTable::default(); + + let mut cie = CommonInformationEntry::new(encoding, 2, 8, X86_64::RA); + for i in &cie_instructions { + cie.add_instruction(i.clone()); + } + let cie_id = frames.add_cie(cie); + + let mut fde = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); + for (o, i) in &fde_instructions { + fde.add_instruction(*o, i.clone()); + } + frames.add_fde(cie_id, fde); + + if vendor == Vendor::AArch64 { + let mut fde = + FrameDescriptionEntry::new(Address::Constant(0x2000), 0x10); + for (o, i) in &fde_instructions_aarch64 { + fde.add_instruction(*o, i.clone()); + } + frames.add_fde(cie_id, fde); + } + + let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); + frames.write_debug_frame(&mut debug_frame).unwrap(); + + let mut read_debug_frame = + read::DebugFrame::new(debug_frame.slice(), LittleEndian); + read_debug_frame.set_address_size(address_size); + read_debug_frame.set_vendor(vendor); + let frames = FrameTable::from(&read_debug_frame, &|address| { + Some(Address::Constant(address)) + }) + .unwrap(); + + assert_eq!( + &frames.cies.get_index(0).unwrap().instructions, + &cie_instructions + ); + assert_eq!(&frames.fdes[0].1.instructions, &fde_instructions); + if vendor == Vendor::AArch64 { + assert_eq!(&frames.fdes[1].1.instructions, &fde_instructions_aarch64); + } + } + } + } + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/dwarf.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/dwarf.rs new file mode 100644 index 0000000..59c2a6d --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/dwarf.rs
@@ -0,0 +1,148 @@ +use alloc::vec::Vec; + +use crate::common::Encoding; +use crate::write::{ + AbbreviationTable, LineProgram, LineString, 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(()) + } + + /// Get a reference to the data for a line string. + pub fn get_line_string<'a>(&'a self, string: &'a LineString) -> &'a [u8] { + string.get(&self.strings, &self.line_strings) + } +} + +/// 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(()) + } + + /// Get a reference to the data for a line string. + pub fn get_line_string<'a>(&'a self, string: &'a LineString) -> &'a [u8] { + string.get(&self.strings, &self.line_strings) + } +} + +#[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/chromium_crates_io/vendor/gimli-v0_32/src/write/endian_vec.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/endian_vec.rs new file mode 100644 index 0000000..7b04060 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/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/chromium_crates_io/vendor/gimli-v0_32/src/write/line.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/line.rs new file mode 100644 index 0000000..bc18023 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/line.rs
@@ -0,0 +1,2165 @@ +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, StringTable, 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>, + + /// True if the file entries may have valid timestamps. + /// + /// Entries may still have a timestamp of 0 even if this is set. + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_timestamp`. + pub file_has_timestamp: bool, + + /// True if the file entries may have valid sizes. + /// + /// Entries may still have a size of 0 even if this is set. + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_size`. + pub file_has_size: bool, + + /// True if the file entries have valid MD5 checksums. + /// + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_MD5`. + pub file_has_md5: bool, + + /// True if the file entries have embedded source code. + /// + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_LLVM_source`. + pub file_has_source: bool, + + prev_row: LineRow, + row: LineRow, + // TODO: this probably should be either rows or sequences instead + instructions: Vec<LineInstruction>, + in_sequence: bool, +} + +impl LineProgram { + /// Create a new `LineProgram`. + /// + /// `working_dir` defines the working directory of the compilation unit. + /// + /// `source_dir`, `source_file` and `source_file_info` define the first + /// file entry. `source_dir` may be relative to `working_dir`, and may be + /// `None` if `source_file` is in `working_dir`. The first file entry + /// is usually the primary source file. + /// + /// The standard specifies that `working_dir` should be the same as the + /// `DW_AT_comp_dir` attribute of the compilation unit DIE, and the + /// combination of `source_dir` and `source_file` should be the same + /// as the `DW_AT_name` attribute of the compilation unit DIE. + /// However, neither of these are enforced by this library. + /// + /// # Panics + /// + /// Panics if `line_encoding.line_base` > 0. + /// + /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0. + /// + /// Panics if `working_dir`, `source_dir`, or `source_file` are empty or + /// contain a null byte. + pub fn new( + encoding: Encoding, + line_encoding: LineEncoding, + working_dir: LineString, + source_dir: Option<LineString>, + source_file: LineString, + source_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(), + prev_row: LineRow::initial_state(encoding, line_encoding), + row: LineRow::initial_state(encoding, line_encoding), + instructions: Vec::new(), + in_sequence: false, + file_has_timestamp: false, + file_has_size: false, + file_has_md5: false, + file_has_source: false, + }; + // For all DWARF versions, directory index 0 is working_dir. + // For version <= 4, the entry is implicit. We still add + // it here so that we use it, but we don't emit it. + let working_dir_id = program.add_directory(working_dir); + // For DWARF version >= 5, file index 0 is source_file and must exist. + if encoding.version >= 5 { + let source_dir_id = match source_dir { + Some(source_dir) => program.add_directory(source_dir), + None => working_dir_id, + }; + program.add_file(source_file, source_dir_id, source_file_info); + } + program + } + + /// Create a new `LineProgram` with no fields set. + /// + /// This can be used when the `LineProgram` will not be used. + /// + /// You should not attempt to add files or line instructions to + /// this line program, or write it to the `.debug_line` section. + pub fn none() -> Self { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 0, + }; + let line_encoding = LineEncoding::default(); + LineProgram { + none: true, + encoding, + line_encoding, + directories: IndexSet::new(), + files: IndexMap::new(), + prev_row: LineRow::initial_state(encoding, line_encoding), + row: LineRow::initial_state(encoding, line_encoding), + instructions: Vec::new(), + in_sequence: false, + file_has_timestamp: false, + file_has_size: false, + file_has_md5: false, + file_has_source: false, + } + } + + /// Return true if this line program was created with `LineProgram::none()`. + #[inline] + pub fn is_none(&self) -> bool { + self.none + } + + /// Return the encoding parameters for this line program. + #[inline] + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Return the DWARF version for this line program. + #[inline] + pub fn version(&self) -> u16 { + self.encoding.version + } + + /// Return the address size in bytes for this line program. + #[inline] + pub fn address_size(&self) -> u8 { + self.encoding.address_size + } + + /// Return the DWARF format for this line program. + #[inline] + pub fn format(&self) -> Format { + self.encoding.format + } + + /// Return the id for the working directory of the compilation unit. + #[inline] + pub fn default_directory(&self) -> DirectoryId { + DirectoryId(0) + } + + /// Add a directory entry and return its id. + /// + /// If the directory already exists, then return the id of the existing entry. + /// + /// If the path is relative, then the directory is located relative to the working + /// directory of the compilation unit. + /// + /// # Panics + /// + /// Panics if `directory` is empty or contains a null byte. + pub fn add_directory(&mut self, directory: LineString) -> DirectoryId { + if let LineString::String(ref val) = directory { + // For DWARF version <= 4, directories must not be empty. + // The first directory isn't emitted so skip the check for it. + if self.encoding.version <= 4 && !self.directories.is_empty() { + assert!(!val.is_empty()); + } + assert!(!val.contains(&0)); + } + let (index, _) = self.directories.insert_full(directory); + DirectoryId(index) + } + + /// Get a reference to a directory entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_directory(&self, id: DirectoryId) -> &LineString { + self.directories.get_index(id.0).unwrap() + } + + /// Add a file entry and return its id. + /// + /// If the file already exists, then return the id of the existing entry. + /// + /// If the file path is relative, then the file is located relative + /// to the directory. Otherwise the directory is meaningless, but it + /// is still used as a key for file entries. + /// + /// If `info` is `None`, then new entries are assigned + /// default information, and existing entries are unmodified. + /// + /// If `info` is not `None`, then it is always assigned to the + /// entry, even if the entry already exists. + /// + /// # Panics + /// + /// Panics if 'file' is empty or contains a null byte. + pub fn add_file( + &mut self, + file: LineString, + directory: DirectoryId, + info: Option<FileInfo>, + ) -> FileId { + if let LineString::String(ref val) = file { + if self.encoding.version <= 4 { + assert!(!val.is_empty()); + } + assert!(!val.contains(&0)); + } + + let key = (file, directory); + let index = if let Some(info) = info { + let (index, _) = self.files.insert_full(key, info); + index + } else { + let entry = self.files.entry(key); + let index = entry.index(); + entry.or_default(); + index + }; + FileId::new(index) + } + + /// Get an iterator for the files. + pub fn files(&self) -> impl Iterator<Item = (FileId, &LineString, DirectoryId)> { + self.files + .iter() + .enumerate() + .map(move |(index, entry)| (FileId::new(index), &(entry.0).0, (entry.0).1)) + } + + /// Get a reference to a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) { + self.files + .get_index(id.index()) + .map(|entry| (&(entry.0).0, (entry.0).1)) + .unwrap() + } + + /// Get a reference to the info for a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file_info(&self, id: FileId) -> &FileInfo { + self.files + .get_index(id.index()) + .map(|entry| entry.1) + .unwrap() + } + + /// Get a mutable reference to the info for a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo { + self.files + .get_index_mut(id.index()) + .map(|entry| entry.1) + .unwrap() + } + + /// Begin a new sequence and set its base address. + /// + /// # Panics + /// + /// Panics if a sequence has already begun. + pub fn begin_sequence(&mut self, address: Option<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.encoding, self.line_encoding); + self.row = LineRow::initial_state(self.encoding, self.line_encoding); + } + + /// Return true if a sequence has begun. + #[inline] + pub fn in_sequence(&self) -> bool { + self.in_sequence + } + + /// Returns a reference to the data for the current row. + #[inline] + pub fn row(&mut self) -> &mut LineRow { + &mut self.row + } + + /// Generates the line number information instructions for the current row. + /// + /// After the instructions are generated, it sets `discriminator` to 0, and sets + /// `basic_block`, `prologue_end`, and `epilogue_begin` to false. + /// + /// # Panics + /// + /// Panics if a sequence has not begun. + /// Panics if the address_offset decreases. + pub fn generate_row(&mut self) { + assert!(self.in_sequence); + + // Output fields that are reset on every row. + if self.row.discriminator != 0 { + self.instructions + .push(LineInstruction::SetDiscriminator(self.row.discriminator)); + self.row.discriminator = 0; + } + if self.row.basic_block { + self.instructions.push(LineInstruction::SetBasicBlock); + self.row.basic_block = false; + } + if self.row.prologue_end { + self.instructions.push(LineInstruction::SetPrologueEnd); + self.row.prologue_end = false; + } + if self.row.epilogue_begin { + self.instructions.push(LineInstruction::SetEpilogueBegin); + self.row.epilogue_begin = false; + } + + // Output fields that are not reset on every row. + if self.row.is_statement != self.prev_row.is_statement { + self.instructions.push(LineInstruction::NegateStatement); + } + if self.row.file != self.prev_row.file { + self.instructions + .push(LineInstruction::SetFile(self.row.file)); + } + if self.row.column != self.prev_row.column { + self.instructions + .push(LineInstruction::SetColumn(self.row.column)); + } + if self.row.isa != self.prev_row.isa { + self.instructions + .push(LineInstruction::SetIsa(self.row.isa)); + } + + // Advance the line, address, and operation index. + let line_base = i64::from(self.line_encoding.line_base) as u64; + let line_range = u64::from(self.line_encoding.line_range); + let line_advance = self.row.line as i64 - self.prev_row.line as i64; + let op_advance = self.op_advance(); + + // Default to special advances of 0. + let special_base = u64::from(OPCODE_BASE); + // TODO: handle lack of special opcodes for 0 line advance + debug_assert!(self.line_encoding.line_base <= 0); + debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0); + let special_default = special_base.wrapping_sub(line_base); + let mut special = special_default; + let mut use_special = false; + + if line_advance != 0 { + let special_line = (line_advance as u64).wrapping_sub(line_base); + if special_line < line_range { + special = special_base + special_line; + use_special = true; + } else { + self.instructions + .push(LineInstruction::AdvanceLine(line_advance)); + } + } + + if op_advance != 0 { + // Using ConstAddPc can save a byte. + let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 { + (op_advance, false) + } else { + let op_range = (255 - special_base) / line_range; + (op_advance - op_range, true) + }; + + let special_op = special_op_advance * line_range; + if special + special_op <= 255 { + special += special_op; + use_special = true; + if const_add_pc { + self.instructions.push(LineInstruction::ConstAddPc); + } + } else { + self.instructions + .push(LineInstruction::AdvancePc(op_advance)); + } + } + + if use_special && special != special_default { + debug_assert!(special >= special_base); + debug_assert!(special <= 255); + self.instructions + .push(LineInstruction::Special(special as u8)); + } else { + self.instructions.push(LineInstruction::Copy); + } + + self.prev_row = self.row; + } + + fn op_advance(&self) -> u64 { + debug_assert!(self.row.address_offset >= self.prev_row.address_offset); + let mut address_advance = self.row.address_offset - self.prev_row.address_offset; + if self.line_encoding.minimum_instruction_length != 1 { + debug_assert_eq!( + self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length), + 0 + ); + address_advance /= u64::from(self.line_encoding.minimum_instruction_length); + } + address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction) + + self.row.op_index + - self.prev_row.op_index + } + + /// Returns true if the line number program has no instructions. + /// + /// Does not check the file or directory entries. + #[inline] + pub fn is_empty(&self) -> bool { + self.instructions.is_empty() + } + + /// Write the line number program to the given section. + /// + /// # Panics + /// + /// Panics if `self.is_none()`. + pub fn write<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 } + + if self.file_has_source { 1 } else { 0 }; + w.write_u8(count)?; + w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; + let file_form = (self.files.get_index(0).unwrap().0).0.form(); + w.write_uleb128(file_form.0.into())?; + w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + if self.file_has_timestamp { + w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + } + if self.file_has_size { + w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + } + if self.file_has_md5 { + w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?; + w.write_uleb128(constants::DW_FORM_data16.0.into())?; + } + let file_source_form = self + .files + .iter() + .find_map(|file| file.1.source.as_ref().map(LineString::form)) + .unwrap_or(constants::DW_FORM_string); + // Create a string to use for files with no source. + // Note: An empty DW_LNCT_LLVM_source is interpreted as missing + // source code. Included source code should always be + // terminated by a "\n" line ending. + let file_source_empty = match file_source_form { + // If any file source is set, then `get_empty` will succeed. + // If all are missing then `file_source_form` will be `DW_FORM_string`. + constants::DW_FORM_line_strp => debug_line_str_offsets + .get_empty() + .map(LineString::LineStringRef), + constants::DW_FORM_strp => debug_str_offsets.get_empty().map(LineString::StringRef), + _ => None, + } + .unwrap_or(LineString::String(Vec::new())); + if self.file_has_source { + w.write_uleb128(u64::from(constants::DW_LNCT_LLVM_source.0))?; + w.write_uleb128(file_source_form.0.into())?; + } + + // File name entries. + w.write_uleb128(self.files.len() as u64)?; + let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| { + file.write( + w, + file_form, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + w.write_uleb128(dir.0 as u64)?; + if self.file_has_timestamp { + w.write_uleb128(info.timestamp)?; + } + if self.file_has_size { + w.write_uleb128(info.size)?; + } + if self.file_has_md5 { + w.write(&info.md5)?; + } + if self.file_has_source { + let source = info.source.as_ref().unwrap_or(&file_source_empty); + source.write( + w, + file_source_form, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + } + Ok(()) + }; + for ((file, dir), info) in self.files.iter() { + write_file(file, *dir, info)?; + } + } + + let header_length = (w.len() - header_length_base) as u64; + w.write_udata_at( + header_length_offset, + header_length, + self.format().word_size(), + )?; + + for instruction in &self.instructions { + instruction.write(w, self.encoding)?; + } + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, self.format())?; + + Ok(offset) + } +} + +/// A row in the line number table that corresponds to a machine instruction. +#[derive(Debug, Clone, Copy)] +pub struct LineRow { + /// The offset of the instruction from the start address of the sequence. + pub address_offset: u64, + /// The index of an operation within a VLIW instruction. + /// + /// The index of the first operation is 0. + /// Set to 0 for non-VLIW instructions. + pub op_index: u64, + + /// The source file corresponding to the instruction. + pub file: FileId, + /// The line number within the source file. + /// + /// Lines are numbered beginning at 1. Set to 0 if there is no source line. + pub line: u64, + /// The column number within the source line. + /// + /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line. + pub column: u64, + /// An additional discriminator used to distinguish between source locations. + /// This value is assigned arbitrarily by the DWARF producer. + pub discriminator: u64, + + /// Set to true if the instruction is a recommended breakpoint for a statement. + pub is_statement: bool, + /// Set to true if the instruction is the beginning of a basic block. + pub basic_block: bool, + /// Set to true if the instruction is a recommended breakpoint at the entry of a + /// function. + pub prologue_end: bool, + /// Set to true if the instruction is a recommended breakpoint prior to the exit of + /// a function. + pub epilogue_begin: bool, + + /// The instruction set architecture of the instruction. + /// + /// Set to 0 for the default ISA. Other values are defined by the architecture ABI. + pub isa: u64, +} + +impl LineRow { + /// Return the initial state as specified in the DWARF standard. + fn initial_state(encoding: Encoding, line_encoding: LineEncoding) -> Self { + LineRow { + address_offset: 0, + op_index: 0, + + file: FileId::initial_state(encoding.version), + line: 1, + column: 0, + discriminator: 0, + + is_statement: line_encoding.default_is_stmt, + basic_block: false, + prologue_end: false, + epilogue_begin: false, + + isa: 0, + } + } +} + +/// An instruction in a line number program. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum LineInstruction { + // Special opcodes + Special(u8), + + // Standard opcodes + Copy, + AdvancePc(u64), + AdvanceLine(i64), + SetFile(FileId), + SetColumn(u64), + NegateStatement, + SetBasicBlock, + ConstAddPc, + // DW_LNS_fixed_advance_pc is not supported. + SetPrologueEnd, + SetEpilogueBegin, + SetIsa(u64), + + // Extended opcodes + EndSequence, + // TODO: this doubles the size of this enum. + SetAddress(Address), + // DW_LNE_define_file is not supported. + SetDiscriminator(u64), +} + +impl LineInstruction { + /// Write the line number instruction to the given section. + fn write<W: Writer>(self, w: &mut DebugLine<W>, encoding: Encoding) -> Result<()> { + use self::LineInstruction::*; + match self { + Special(val) => w.write_u8(val)?, + Copy => w.write_u8(constants::DW_LNS_copy.0)?, + AdvancePc(val) => { + w.write_u8(constants::DW_LNS_advance_pc.0)?; + w.write_uleb128(val)?; + } + AdvanceLine(val) => { + w.write_u8(constants::DW_LNS_advance_line.0)?; + w.write_sleb128(val)?; + } + SetFile(val) => { + w.write_u8(constants::DW_LNS_set_file.0)?; + w.write_uleb128(val.raw(encoding.version))?; + } + SetColumn(val) => { + w.write_u8(constants::DW_LNS_set_column.0)?; + w.write_uleb128(val)?; + } + NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?, + SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?, + ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?, + SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?, + SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?, + SetIsa(val) => { + w.write_u8(constants::DW_LNS_set_isa.0)?; + w.write_uleb128(val)?; + } + EndSequence => { + w.write_u8(0)?; + w.write_uleb128(1)?; + w.write_u8(constants::DW_LNE_end_sequence.0)?; + } + SetAddress(address) => { + w.write_u8(0)?; + w.write_uleb128(1 + u64::from(encoding.address_size))?; + w.write_u8(constants::DW_LNE_set_address.0)?; + w.write_address(address, encoding.address_size)?; + } + SetDiscriminator(val) => { + let mut bytes = [0u8; 10]; + // bytes is long enough so this will never fail. + let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); + w.write_u8(0)?; + w.write_uleb128(1 + len as u64)?; + w.write_u8(constants::DW_LNE_set_discriminator.0)?; + w.write(&bytes[..len])?; + } + } + Ok(()) + } +} + +/// A string value for use in defining paths in line number programs. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum LineString { + /// A slice of bytes representing a string. Must not include null bytes. + /// Not guaranteed to be UTF-8 or anything like that. + String(Vec<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)) + } + } + + /// Get a reference to the string data. + pub fn get<'a>( + &'a self, + strings: &'a StringTable, + line_strings: &'a LineStringTable, + ) -> &'a [u8] { + match self { + LineString::String(val) => val, + LineString::StringRef(val) => strings.get(*val), + LineString::LineStringRef(val) => line_strings.get(*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`. + // + // We internally use a 0-based index for all versions, but + // emit a 1-based index for DWARF version <= 4. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct FileId(usize); + + impl FileId { + /// Create a `FileId` given a 0-based index into `LineProgram::files`. + pub(crate) fn new(index: usize) -> Self { + FileId(index) + } + + /// The 0-based index of the file in `LineProgram::files`. + pub(super) fn index(self) -> usize { + self.0 + } + + /// The initial state of the file register. + pub(super) fn initial_state(version: u16) -> Self { + if version == 5 { + // For version 5, the files are 0-based and the default file is 1, + // which is a 0-based index of 1. + FileId(1) + } else { + // For version <= 4, the files are 1-based and the default file is 1, + // which is a 0-based index of 0. + // For version >= 6, the files are 0-based and the default file is 0, + // which is a 0-based index of 0. + FileId(0) + } + } + + /// Convert to a raw value used for writing. + /// + /// This converts to a 1-based index for DWARF version <= 4. + pub(crate) fn raw(self, version: u16) -> u64 { + if version <= 4 { + self.0 as u64 + 1 + } else { + self.0 as u64 + } + } + } +} +pub use self::id::*; + +/// Extra information for file in a `LineProgram`. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct FileInfo { + /// The implementation defined timestamp of the last modification of the file, + /// or 0 if not available. + pub timestamp: u64, + + /// The size of the file in bytes, or 0 if not available. + pub size: u64, + + /// A 16-byte MD5 digest of the file contents. + /// + /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`. + pub md5: [u8; 16], + + /// Optionally some embedded sourcecode. + /// + /// Only used if version >= 5 and `LineProgram::file_has_source` is `true`. + pub source: Option<LineString>, +} + +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 working_dir = match from_header.directory(0) { + Some(working_dir) => { + LineString::from(working_dir, dwarf, line_strings, strings)? + } + None => LineString::new(&[][..], encoding, line_strings), + }; + + let (source_dir, source_file) = match from_header.file(0) { + Some(source_file) => { + let source_dir_index = source_file.directory_index(); + let source_dir = if source_dir_index != 0 { + match from_header.directory(source_dir_index) { + Some(source_dir) => Some(LineString::from( + source_dir, + dwarf, + line_strings, + strings, + )?), + None => return Err(ConvertError::InvalidDirectoryIndex), + } + } else { + None + }; + let source_file = LineString::from( + source_file.path_name(), + dwarf, + line_strings, + strings, + )?; + (source_dir, source_file) + } + None => (None, LineString::new(&[][..], encoding, line_strings)), + }; + + if from_header.line_base() > 0 { + return Err(ConvertError::InvalidLineBase); + } + let mut program = LineProgram::new( + encoding, + from_header.line_encoding(), + working_dir, + source_dir, + source_file, + None, // We'll set this later if needed when we add the file again. + ); + + if from_header.version() <= 4 { + // The first directory is implicit. + dirs.push(DirectoryId(0)); + // A file index of 0 is invalid for version <= 4, but putting + // something there makes the indexing easier. + files.push(FileId::new(0)); + } + + for from_dir in from_header.include_directories() { + let from_dir = + LineString::from(from_dir.clone(), dwarf, line_strings, strings)?; + dirs.push(program.add_directory(from_dir)); + } + + program.file_has_timestamp = from_header.file_has_timestamp(); + program.file_has_size = from_header.file_has_size(); + program.file_has_md5 = from_header.file_has_md5(); + program.file_has_source = from_header.file_has_source(); + for from_file in from_header.file_names().iter() { + let from_name = + LineString::from(from_file.path_name(), dwarf, line_strings, strings)?; + let from_dir = from_file.directory_index(); + if from_dir >= dirs.len() as u64 { + return Err(ConvertError::InvalidDirectoryIndex); + } + let from_dir = dirs[from_dir as usize]; + let from_info = Some(FileInfo { + timestamp: from_file.timestamp(), + size: from_file.size(), + md5: *from_file.md5(), + source: match from_file.source() { + Some(source) => { + Some(LineString::from(source, dwarf, line_strings, strings)?) + } + None => None, + }, + }); + files.push(program.add_file(from_name, from_dir, from_info)); + } + + program + }; + + // We can't use the `from_program.rows()` because that wouldn't let + // us preserve address relocations. + let mut from_row = read::LineRow::new(from_program.header()); + let mut instructions = from_program.header().instructions(); + let mut address = None; + while let Some(instruction) = instructions.next_instruction(from_program.header())? { + match instruction { + read::LineInstruction::SetAddress(val) => { + if program.in_sequence() { + return Err(ConvertError::UnsupportedLineInstruction); + } + match convert_address(val) { + Some(val) => address = Some(val), + None => return Err(ConvertError::InvalidAddress), + } + from_row + .execute(read::LineInstruction::SetAddress(0), &mut from_program)?; + } + read::LineInstruction::DefineFile(_) => { + return Err(ConvertError::UnsupportedLineInstruction); + } + _ => { + if from_row.execute(instruction, &mut from_program)? { + if !program.in_sequence() { + program.begin_sequence(address); + address = None; + } + if from_row.end_sequence() { + program.end_sequence(from_row.address()); + } else { + program.row().address_offset = from_row.address(); + program.row().op_index = from_row.op_index(); + program.row().file = { + let file = from_row.file_index(); + if file >= files.len() as u64 { + return Err(ConvertError::InvalidFileIndex); + } + if file == 0 && program.version() <= 4 { + return Err(ConvertError::InvalidFileIndex); + } + files[file as usize] + }; + program.row().line = match from_row.line() { + Some(line) => line.get(), + None => 0, + }; + program.row().column = match from_row.column() { + read::ColumnType::LeftEdge => 0, + read::ColumnType::Column(val) => val.get(), + }; + program.row().discriminator = from_row.discriminator(); + program.row().is_statement = from_row.is_stmt(); + program.row().basic_block = from_row.basic_block(); + program.row().prologue_end = from_row.prologue_end(); + program.row().epilogue_begin = from_row.epilogue_begin(); + program.row().isa = from_row.isa(); + program.generate_row(); + } + from_row.reset(from_program.header()); + } + } + }; + } + Ok((program, files)) + } + } + + impl LineString { + fn from<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::{AttributeValue, Dwarf, EndianVec, Sections, Unit}; + use crate::LittleEndian; + + #[test] + fn test_line_program() { + let dir1 = LineString::String(b"dir1".to_vec()); + let file1 = LineString::String(b"file1".to_vec()); + let dir2 = LineString::String(b"dir2".to_vec()); + let file2 = LineString::String(b"file2".to_vec()); + + let mut dwarf = Dwarf::new(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + dir1.clone(), + None, + file1.clone(), + None, + ); + + assert_eq!(&dir1, program.get_directory(program.default_directory())); + program.file_has_timestamp = true; + program.file_has_size = true; + if encoding.version >= 5 { + program.file_has_md5 = true; + } + + // Note: Embedded source code is an accepted extension + // that will become part of DWARF v6. We're using the LLVM extension + // here for v5. + if encoding.version >= 5 { + program.file_has_source = true; + } + + let dir_id = program.add_directory(dir2.clone()); + assert_eq!(&dir2, program.get_directory(dir_id)); + assert_eq!(dir_id, program.add_directory(dir2.clone())); + + let file_info = FileInfo { + timestamp: 1, + size: 2, + md5: if encoding.version >= 5 { + [3; 16] + } else { + [0; 16] + }, + source: (encoding.version >= 5) + .then(|| LineString::String(b"the source code\n".to_vec())), + }; + let file_id = program.add_file(file2.clone(), dir_id, Some(file_info.clone())); + assert_eq!((&file2, dir_id), program.get_file(file_id)); + assert_eq!(file_info, *program.get_file_info(file_id)); + + program.get_file_info_mut(file_id).size = 3; + assert_ne!(file_info, *program.get_file_info(file_id)); + assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None)); + assert_ne!(file_info, *program.get_file_info(file_id)); + assert_eq!( + file_id, + program.add_file(file2.clone(), dir_id, Some(file_info.clone())) + ); + assert_eq!(file_info, *program.get_file_info(file_id)); + + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + root.set( + constants::DW_AT_comp_dir, + AttributeValue::String(b"dir1".to_vec()), + ); + root.set( + constants::DW_AT_name, + AttributeValue::String(b"file1".to_vec()), + ); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + root.set( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file_id)), + ); + + dwarf.units.add(unit); + } + } + } + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + let read_dwarf = sections.read(LittleEndian); + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap(); + + let mut convert_units = convert_dwarf.units.iter(); + for (_, unit) in dwarf.units.iter() { + let program = &unit.line_program; + let root = unit.get(unit.root()); + let Some(AttributeValue::FileIndex(Some(file_id))) = + root.get(constants::DW_AT_decl_file) + else { + panic!("missing DW_AT_decl_file"); + }; + + let (_, convert_unit) = convert_units.next().unwrap(); + let convert_program = &convert_unit.line_program; + let convert_root = convert_unit.get(convert_unit.root()); + let Some(AttributeValue::FileIndex(Some(convert_file_id))) = + convert_root.get(constants::DW_AT_decl_file) + else { + panic!("missing DW_AT_decl_file"); + }; + + assert_eq!(convert_program.version(), program.version()); + assert_eq!(convert_program.address_size(), program.address_size()); + assert_eq!(convert_program.format(), program.format()); + + let (file, dir) = program.get_file(*file_id); + let (convert_file, convert_dir) = convert_program.get_file(*convert_file_id); + assert_eq!(file, convert_file); + assert_eq!( + program.get_directory(dir), + convert_program.get_directory(convert_dir) + ); + assert_eq!( + program.get_file_info(*file_id), + convert_program.get_file_info(*convert_file_id) + ); + } + } + + #[test] + fn test_line_row() { + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + let file2 = &b"file2"[..]; + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let line_base = -5; + let line_range = 14; + let neg_line_base = (-line_base) as u8; + let mut program = LineProgram::new( + encoding, + LineEncoding { + line_base, + line_range, + ..Default::default() + }, + LineString::String(dir1.to_vec()), + None, + LineString::String(file1.to_vec()), + None, + ); + let dir_id = program.default_directory(); + let file1_id = + program.add_file(LineString::String(file1.to_vec()), dir_id, None); + let file2_id = + program.add_file(LineString::String(file2.to_vec()), dir_id, None); + + // Test sequences. + { + let mut program = program.clone(); + let address = Address::Constant(0x12); + program.begin_sequence(Some(address)); + assert_eq!( + program.instructions, + vec![LineInstruction::SetAddress(address)] + ); + } + + { + let mut program = program.clone(); + program.begin_sequence(None); + assert_eq!(program.instructions, Vec::new()); + } + + { + let mut program = program.clone(); + program.begin_sequence(None); + program.end_sequence(0x1234); + assert_eq!( + program.instructions, + vec![ + LineInstruction::AdvancePc(0x1234), + LineInstruction::EndSequence + ] + ); + } + + // Create a base program. + program.begin_sequence(None); + program.row.line = 0x1000; + program.generate_row(); + let base_row = program.row; + let base_instructions = program.instructions.clone(); + + // Create test cases. + let mut tests = Vec::new(); + + let row = base_row; + tests.push((row, vec![LineInstruction::Copy])); + + let mut row = base_row; + row.line -= u64::from(neg_line_base); + tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)])); + + let mut row = base_row; + row.line += u64::from(line_range) - 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)], + )); + + let mut row = base_row; + row.line += u64::from(line_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.address_offset = 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special(OPCODE_BASE + line_range)], + )); + + let op_range = (255 - OPCODE_BASE) / line_range; + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special( + OPCODE_BASE + op_range * line_range, + )], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); + row.line -= u64::from(neg_line_base); + tests.push((row, vec![LineInstruction::Special(255)])); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::ConstAddPc, LineInstruction::Copy], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::ConstAddPc, + LineInstruction::Special(OPCODE_BASE + 6), + ], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvancePc(row.address_offset), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvancePc(row.address_offset), + LineInstruction::Special(OPCODE_BASE + 6), + ], + )); + + let mut row = base_row; + row.address_offset = 0x1234; + tests.push(( + row, + vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.line += 0x1234; + tests.push(( + row, + vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.file = file1_id; + if version == 5 { + // Version 5 is 0-based, but the default file is 1, so this row + // will need to set the file. + tests.push(( + row, + vec![LineInstruction::SetFile(file1_id), LineInstruction::Copy], + )); + } else { + // This is the first file, so normally this is already the default. + tests.push((row, vec![LineInstruction::Copy])); + } + + let mut row = base_row; + row.file = file2_id; + if version == 5 { + tests.push((row, vec![LineInstruction::Copy])); + } else { + tests.push(( + row, + vec![LineInstruction::SetFile(file2_id), LineInstruction::Copy], + )); + } + + let mut row = base_row; + row.column = 0x1234; + tests.push(( + row, + vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.discriminator = 0x1234; + tests.push(( + row, + vec![ + LineInstruction::SetDiscriminator(0x1234), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.is_statement = !row.is_statement; + tests.push(( + row, + vec![LineInstruction::NegateStatement, LineInstruction::Copy], + )); + + let mut row = base_row; + row.basic_block = true; + tests.push(( + row, + vec![LineInstruction::SetBasicBlock, LineInstruction::Copy], + )); + + let mut row = base_row; + row.prologue_end = true; + tests.push(( + row, + vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy], + )); + + let mut row = base_row; + row.epilogue_begin = true; + tests.push(( + row, + vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy], + )); + + let mut row = base_row; + row.isa = 0x1234; + tests.push(( + row, + vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy], + )); + + for test in tests { + // Test generate_row(). + let mut program = program.clone(); + program.row = test.0; + program.generate_row(); + assert_eq!( + &program.instructions[base_instructions.len()..], + &test.1[..] + ); + + // Test LineProgram::from(). + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + + let mut dwarf = Dwarf::new(); + dwarf.units.add(unit); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + let read_dwarf = sections.read(LittleEndian); + + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) + .unwrap(); + let convert_unit = convert_dwarf.units.iter().next().unwrap().1; + let convert_program = &convert_unit.line_program; + + assert_eq!( + &convert_program.instructions[base_instructions.len()..], + &test.1[..] + ); + } + } + } + } + } + + #[test] + fn test_line_instruction() { + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(dir1.to_vec()), + None, + LineString::String(file1.to_vec()), + None, + ); + let dir_id = program.default_directory(); + let file_id = + program.add_file(LineString::String(file1.to_vec()), dir_id, None); + + for (inst, expect_inst) in &[ + ( + LineInstruction::Special(OPCODE_BASE), + read::LineInstruction::Special(OPCODE_BASE), + ), + ( + LineInstruction::Special(255), + read::LineInstruction::Special(255), + ), + (LineInstruction::Copy, read::LineInstruction::Copy), + ( + LineInstruction::AdvancePc(0x12), + read::LineInstruction::AdvancePc(0x12), + ), + ( + LineInstruction::AdvanceLine(0x12), + read::LineInstruction::AdvanceLine(0x12), + ), + ( + LineInstruction::SetFile(file_id), + read::LineInstruction::SetFile(file_id.raw(encoding.version)), + ), + ( + LineInstruction::SetColumn(0x12), + read::LineInstruction::SetColumn(0x12), + ), + ( + LineInstruction::NegateStatement, + read::LineInstruction::NegateStatement, + ), + ( + LineInstruction::SetBasicBlock, + read::LineInstruction::SetBasicBlock, + ), + ( + LineInstruction::ConstAddPc, + read::LineInstruction::ConstAddPc, + ), + ( + LineInstruction::SetPrologueEnd, + read::LineInstruction::SetPrologueEnd, + ), + ( + LineInstruction::SetEpilogueBegin, + read::LineInstruction::SetEpilogueBegin, + ), + ( + LineInstruction::SetIsa(0x12), + read::LineInstruction::SetIsa(0x12), + ), + ( + LineInstruction::EndSequence, + read::LineInstruction::EndSequence, + ), + ( + LineInstruction::SetAddress(Address::Constant(0x12)), + read::LineInstruction::SetAddress(0x12), + ), + ( + LineInstruction::SetDiscriminator(0x12), + read::LineInstruction::SetDiscriminator(0x12), + ), + ][..] + { + let mut program = program.clone(); + program.instructions.push(*inst); + + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + + let mut dwarf = Dwarf::new(); + dwarf.units.add(unit); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let read_unit_header = read_dwarf.units().next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit_header).unwrap(); + let read_unit = read_unit.unit_ref(&read_dwarf); + let read_header = read_unit.line_program.as_ref().unwrap().header(); + let mut read_insts = read_header.instructions(); + assert_eq!( + *expect_inst, + read_insts.next_instruction(read_header).unwrap().unwrap() + ); + assert_eq!(None, read_insts.next_instruction(read_header).unwrap()); + } + } + } + } + } + + // Test that the address/line advance is correct. We don't test for optimality. + #[test] + fn test_advance() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + + let addresses = 0..50; + let lines = -10..25i64; + + for minimum_instruction_length in [1, 4] { + for maximum_operations_per_instruction in [1, 3] { + for line_base in [-5, 0] { + for line_range in [10, 20] { + let line_encoding = LineEncoding { + minimum_instruction_length, + maximum_operations_per_instruction, + line_base, + line_range, + default_is_stmt: true, + }; + let mut program = LineProgram::new( + encoding, + line_encoding, + LineString::String(dir1.to_vec()), + None, + LineString::String(file1.to_vec()), + None, + ); + for address_advance in addresses.clone() { + program.begin_sequence(Some(Address::Constant(0x1000))); + program.row().line = 0x10000; + program.generate_row(); + for line_advance in lines.clone() { + { + let row = program.row(); + row.address_offset += + address_advance * u64::from(minimum_instruction_length); + row.line = row.line.wrapping_add(line_advance as u64); + } + program.generate_row(); + } + let address_offset = program.row().address_offset + + u64::from(minimum_instruction_length); + program.end_sequence(address_offset); + } + + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + + let mut dwarf = Dwarf::new(); + dwarf.units.add(unit); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let read_unit_header = read_dwarf.units().next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit_header).unwrap(); + let read_unit = read_unit.unit_ref(&read_dwarf); + let read_program = read_unit.line_program.clone().unwrap(); + + let mut rows = read_program.rows(); + for address_advance in addresses.clone() { + let mut address; + let mut line; + { + let row = rows.next_row().unwrap().unwrap().1; + address = row.address(); + line = row.line().unwrap().get(); + } + assert_eq!(address, 0x1000); + assert_eq!(line, 0x10000); + for line_advance in lines.clone() { + let row = rows.next_row().unwrap().unwrap().1; + assert_eq!( + row.address() - address, + address_advance * u64::from(minimum_instruction_length) + ); + assert_eq!( + (row.line().unwrap().get() as i64) - (line as i64), + line_advance + ); + address = row.address(); + line = row.line().unwrap().get(); + } + let row = rows.next_row().unwrap().unwrap().1; + assert!(row.end_sequence()); + } + } + } + } + } + } + + #[test] + fn test_line_string() { + let version = 5; + + let file1 = "file1"; + + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let files: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [ + &mut |_dwarf| LineString::String(file1.as_bytes().to_vec()), + &mut |dwarf| LineString::StringRef(dwarf.strings.add(file1)), + &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(file1)), + ]; + + for file in files { + let mut dwarf = Dwarf::new(); + let file = file(&mut dwarf); + + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"dir".to_vec()), + None, + file.clone(), + None, + ); + program.begin_sequence(Some(Address::Constant(0x1000))); + program.row().line = 0x10000; + program.generate_row(); + + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + dwarf.units.add(unit); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let read_unit_header = read_dwarf.units().next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit_header).unwrap(); + let read_unit = read_unit.unit_ref(&read_dwarf); + let read_program = read_unit.line_program.clone().unwrap(); + let read_header = read_program.header(); + let read_file = read_header.file(0).unwrap(); + let read_path = read_unit.attr_string(read_file.path_name()).unwrap(); + assert_eq!(read_path.slice(), file1.as_bytes()); + } + } + } + } + + #[test] + fn test_missing_comp_dir() { + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(Vec::new()), + None, + LineString::String(Vec::new()), + None, + ); + // Ensure the program is not empty. + let dir_id = program.default_directory(); + let file_id = + program.add_file(LineString::String(b"file1".to_vec()), dir_id, None); + program.begin_sequence(Some(Address::Constant(0x1000))); + program.row().file = file_id; + program.row().line = 0x10000; + program.generate_row(); + + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + // Testing missing DW_AT_comp_dir/DW_AT_name. + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + + let mut dwarf = Dwarf::new(); + dwarf.units.add(unit); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + let read_dwarf = sections.read(LittleEndian); + let _convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) + .unwrap(); + } + } + } + } + + #[test] + fn test_separate_working_dir() { + let working_dir = LineString::String(b"working".to_vec()); + let source_dir = LineString::String(b"source".to_vec()); + let source_file = LineString::String(b"file".to_vec()); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + working_dir.clone(), + Some(source_dir.clone()), + source_file.clone(), + None, + ); + + assert_eq!( + &working_dir, + program.get_directory(program.default_directory()) + ); + + // Ensure the program is not empty. + let dir_id = program.add_directory(source_dir.clone()); + let file_id = program.add_file(source_file.clone(), dir_id, None); + program.begin_sequence(Some(Address::Constant(0x1000))); + program.row().file = file_id; + program.row().line = 0x10000; + program.generate_row(); + + // Test LineProgram::from(). + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + root.set( + constants::DW_AT_comp_dir, + AttributeValue::String(b"working".to_vec()), + ); + root.set( + constants::DW_AT_name, + AttributeValue::String(b"source/file".to_vec()), + ); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + + let mut dwarf = Dwarf::new(); + dwarf.units.add(unit); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + let read_dwarf = sections.read(LittleEndian); + + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) + .unwrap(); + let convert_unit = convert_dwarf.units.iter().next().unwrap().1; + let convert_program = &convert_unit.line_program; + + assert_eq!( + &working_dir, + convert_program.get_directory(convert_program.default_directory()) + ); + let (_file_id, file, dir_id) = convert_program.files().next().unwrap(); + assert_eq!(&source_file, file); + assert_eq!(&source_dir, convert_program.get_directory(dir_id)); + } + } + } + } + + #[test] + fn test_file_source() { + let version = 5; + + let source1 = "source1"; + + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let sources: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [ + &mut |_dwarf| LineString::String(source1.as_bytes().to_vec()), + &mut |dwarf| LineString::StringRef(dwarf.strings.add(source1)), + &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(source1)), + ]; + + for source in sources { + let mut dwarf = Dwarf::new(); + let source = Some(source(&mut dwarf)); + + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"dir".to_vec()), + None, + LineString::String(b"file".to_vec()), + Some(FileInfo { + timestamp: 0, + size: 0, + md5: [0; 16], + source, + }), + ); + program.file_has_source = true; + + let file_id = program.files().next().unwrap().0; + program.begin_sequence(Some(Address::Constant(0x1000))); + program.row().file = file_id; + program.row().line = 0x10000; + program.generate_row(); + + let mut unit = Unit::new(encoding, program); + let root = unit.get_mut(unit.root()); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + dwarf.units.add(unit); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let read_unit_header = read_dwarf.units().next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit_header).unwrap(); + let read_unit = read_unit.unit_ref(&read_dwarf); + let read_program = read_unit.line_program.clone().unwrap(); + let read_header = read_program.header(); + let read_file = read_header.file(0).unwrap(); + let read_source = read_unit.attr_string(read_file.source().unwrap()).unwrap(); + assert_eq!(read_source.slice(), source1.as_bytes()); + + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) + .unwrap(); + let (_, convert_unit) = convert_dwarf.units.iter().next().unwrap(); + let convert_program = &convert_unit.line_program; + let convert_file_id = convert_program.files().next().unwrap().0; + let convert_file_info = convert_program.get_file_info(convert_file_id); + assert_eq!( + convert_dwarf.get_line_string(convert_file_info.source.as_ref().unwrap()), + source1.as_bytes(), + ); + } + } + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/loc.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/loc.rs new file mode 100644 index 0000000..7f67d19 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/loc.rs
@@ -0,0 +1,561 @@ +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) + } + + /// Get a reference to a location list. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: LocationListId) -> &LocationList { + debug_assert_eq!(self.base_id, id.base_id); + &self.locations[id.index] + } + + /// Write the location list table to the appropriate section for the given DWARF version. + pub(crate) fn write<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: 0, .. } => continue, + Location::StartEnd { begin, end, .. } if begin == end => continue, + Location::OffsetPair { begin, end, .. } if begin == end => continue, + _ => (), + } + loc_list.push(loc); + } + Ok(LocationList(loc_list)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, + }; + use crate::read; + use crate::write::{ + ConvertUnitContext, EndianVec, LineStringTable, RangeListTable, StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + use std::sync::Arc; + + #[test] + fn test_loc_list() { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + let mut expression = Expression::new(); + expression.op_constu(0); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut loc_list = LocationList(vec![ + Location::StartLength { + begin: Address::Constant(6666), + length: 7777, + data: expression.clone(), + }, + Location::StartEnd { + begin: Address::Constant(4444), + end: Address::Constant(5555), + data: expression.clone(), + }, + Location::BaseAddress { + address: Address::Constant(1111), + }, + Location::OffsetPair { + begin: 2222, + end: 3333, + data: expression.clone(), + }, + ]); + if version >= 5 { + loc_list.0.push(Location::DefaultLocation { + data: expression.clone(), + }); + } + + let mut locations = LocationListTable::default(); + let loc_list_id = locations.add(loc_list.clone()); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); + assert!(sections.debug_loc_refs.is_empty()); + assert!(sections.debug_loclists_refs.is_empty()); + + let read_debug_loc = + read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); + let read_debug_loclists = + read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); + let read_loc = read::LocationLists::new(read_debug_loc, read_debug_loclists); + let offset = loc_list_offsets.get(loc_list_id); + let read_loc_list = read_loc.raw_locations(offset, encoding).unwrap(); + + let dwarf = read::Dwarf { + locations: read_loc, + ..Default::default() + }; + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::default(), + ), + abbreviations: Arc::new(read::Abbreviations::default()), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + let context = ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut RangeListTable::default(), + locations: &mut locations, + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + let convert_loc_list = LocationList::from(read_loc_list, &context).unwrap(); + + if version <= 4 { + loc_list.0[0] = Location::StartEnd { + begin: Address::Constant(6666), + end: Address::Constant(6666 + 7777), + data: expression.clone(), + }; + } + assert_eq!(loc_list, convert_loc_list); + } + } + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/mod.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/mod.rs new file mode 100644 index 0000000..b7adbbe --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/mod.rs
@@ -0,0 +1,428 @@ +//! Write DWARF debugging information. +//! +//! ## API Structure +//! +//! This module works by building up a representation of the debugging information +//! in memory, and then writing it all at once. It supports two major use cases: +//! +//! * Use the [`DwarfUnit`](./struct.DwarfUnit.html) type when writing DWARF +//! for a single compilation unit. +//! +//! * Use the [`Dwarf`](./struct.Dwarf.html) type when writing DWARF for multiple +//! compilation units. +//! +//! The module also supports reading in DWARF debugging information and writing it out +//! again, possibly after modifying it. Create a [`read::Dwarf`](../read/struct.Dwarf.html) +//! instance, and then use [`Dwarf::from`](./struct.Dwarf.html#method.from) to convert +//! it to a writable instance. +//! +//! ## Example Usage +//! +//! Write a compilation unit containing only the top level DIE. +//! +//! ```rust +//! use gimli::write::{ +//! Address, AttributeValue, DwarfUnit, EndianVec, Error, Range, RangeList, Sections, +//! }; +//! +//! fn example() -> Result<(), Error> { +//! // Choose the encoding parameters. +//! let encoding = gimli::Encoding { +//! format: gimli::Format::Dwarf32, +//! version: 5, +//! address_size: 8, +//! }; +//! // Create a container for a single compilation unit. +//! let mut dwarf = DwarfUnit::new(encoding); +//! // Set a range attribute on the root DIE. +//! let range_list = RangeList(vec![Range::StartLength { +//! begin: Address::Constant(0x100), +//! length: 42, +//! }]); +//! let range_list_id = dwarf.unit.ranges.add(range_list); +//! let root = dwarf.unit.root(); +//! dwarf.unit.get_mut(root).set( +//! gimli::DW_AT_ranges, +//! AttributeValue::RangeListRef(range_list_id), +//! ); +//! // Create a `Vec` for each DWARF section. +//! let mut sections = Sections::new(EndianVec::new(gimli::LittleEndian)); +//! // Finally, write the DWARF data to the sections. +//! dwarf.write(&mut sections)?; +//! sections.for_each(|id, data| { +//! // Here you can add the data to the output object file. +//! Ok(()) +//! }) +//! } +//! # fn main() { +//! # example().unwrap(); +//! # } + +use std::error; +use std::fmt; +use std::result; + +use crate::constants; + +mod endian_vec; +pub use self::endian_vec::*; + +mod writer; +pub use self::writer::*; + +mod relocate; +pub use self::relocate::*; + +#[macro_use] +mod section; +pub use self::section::*; + +macro_rules! define_id { + ($name:ident, $docs:expr) => { + #[doc=$docs] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name { + base_id: BaseId, + index: usize, + } + + impl $name { + #[inline] + fn new(base_id: BaseId, index: usize) -> Self { + $name { base_id, index } + } + } + }; +} + +macro_rules! define_offsets { + ($offsets:ident: $id:ident => $offset:ident, $off_doc:expr) => { + #[doc=$off_doc] + #[derive(Debug)] + pub struct $offsets { + base_id: BaseId, + // We know ids start at 0. + offsets: Vec<$offset>, + } + + impl $offsets { + /// Return an empty list of offsets. + #[inline] + pub fn none() -> Self { + $offsets { + base_id: BaseId::default(), + offsets: Vec::new(), + } + } + + /// Get the offset + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: $id) -> $offset { + debug_assert_eq!(self.base_id, id.base_id); + self.offsets[id.index] + } + + /// Return the number of offsets. + #[inline] + pub fn count(&self) -> usize { + self.offsets.len() + } + } + }; +} + +mod abbrev; +pub use self::abbrev::*; + +mod cfi; +pub use self::cfi::*; + +mod dwarf; +pub use self::dwarf::*; + +mod line; +pub use self::line::*; + +mod loc; +pub use self::loc::*; + +mod op; +pub use self::op::*; + +mod range; +pub use self::range::*; + +mod str; +pub use self::str::*; + +mod unit; +pub use self::unit::*; + +/// An error that occurred when writing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + /// The given offset is out of bounds. + OffsetOutOfBounds, + /// The given length is out of bounds. + LengthOutOfBounds, + /// The attribute value is an invalid for writing. + InvalidAttributeValue, + /// The value is too large for the encoding form. + ValueTooLarge, + /// Unsupported word size. + UnsupportedWordSize(u8), + /// Unsupported DWARF version. + UnsupportedVersion(u16), + /// The unit length is too large for the requested DWARF format. + InitialLengthOverflow, + /// The address is invalid. + InvalidAddress, + /// The reference is invalid. + InvalidReference, + /// A requested feature requires a different DWARF version. + NeedVersion(u16), + /// Strings in line number program have mismatched forms. + LineStringFormMismatch, + /// The range is empty or otherwise invalid. + InvalidRange, + /// The line number program encoding is incompatible with the unit encoding. + IncompatibleLineProgramEncoding, + /// Could not encode code offset for a frame instruction. + InvalidFrameCodeOffset(u32), + /// Could not encode data offset for a frame instruction. + InvalidFrameDataOffset(i32), + /// Unsupported eh_frame pointer encoding. + UnsupportedPointerEncoding(constants::DwEhPe), + /// Unsupported reference in CFI expression. + UnsupportedCfiExpressionReference, + /// Unsupported forward reference in expression. + UnsupportedExpressionForwardReference, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> { + match *self { + Error::OffsetOutOfBounds => write!(f, "The given offset is out of bounds."), + Error::LengthOutOfBounds => write!(f, "The given length is out of bounds."), + Error::InvalidAttributeValue => { + write!(f, "The attribute value is an invalid for writing.") + } + Error::ValueTooLarge => write!(f, "The value is too large for the encoding form."), + Error::UnsupportedWordSize(size) => write!(f, "Unsupported word size: {}", size), + Error::UnsupportedVersion(version) => { + write!(f, "Unsupported DWARF version: {}", version) + } + Error::InitialLengthOverflow => write!( + f, + "The unit length is too large for the requested DWARF format." + ), + Error::InvalidAddress => write!(f, "The address is invalid."), + Error::InvalidReference => write!(f, "The reference is invalid."), + Error::NeedVersion(version) => write!( + f, + "A requested feature requires a DWARF version {}.", + version + ), + Error::LineStringFormMismatch => { + write!(f, "Strings in line number program have mismatched forms.") + } + Error::InvalidRange => write!(f, "The range is empty or otherwise invalid."), + Error::IncompatibleLineProgramEncoding => write!( + f, + "The line number program encoding is incompatible with the unit encoding." + ), + Error::InvalidFrameCodeOffset(offset) => write!( + f, + "Could not encode code offset ({}) for a frame instruction.", + offset, + ), + Error::InvalidFrameDataOffset(offset) => write!( + f, + "Could not encode data offset ({}) for a frame instruction.", + offset, + ), + Error::UnsupportedPointerEncoding(eh_pe) => { + write!(f, "Unsupported eh_frame pointer encoding ({}).", eh_pe) + } + Error::UnsupportedCfiExpressionReference => { + write!(f, "Unsupported reference in CFI expression.") + } + Error::UnsupportedExpressionForwardReference => { + write!(f, "Unsupported forward reference in expression.") + } + } + } +} + +impl error::Error for Error {} + +/// The result of a write. +pub type Result<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/chromium_crates_io/vendor/gimli-v0_32/src/write/op.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/op.rs new file mode 100644 index 0000000..f4c632b --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/op.rs
@@ -0,0 +1,1762 @@ +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>, + ) -> Result<usize> { + let mut size = 0; + for operation in &self.operations { + size += operation.size(encoding, unit_offsets)?; + } + Ok(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()) { + debug_assert_eq!(w.len(), offset); + operation.write(w, refs.as_deref_mut(), encoding, unit_offsets, &offsets)?; + } + debug_assert_eq!(w.len(), offset); + 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>) -> Result<usize> { + let base_size = |entry| match unit_offsets { + Some(offsets) => offsets + .unit_offset(entry) + .map(uleb128_size) + .ok_or(Error::UnsupportedExpressionForwardReference), + None => Err(Error::UnsupportedCfiExpressionReference), + }; + Ok(1 + match *self { + Operation::Raw(ref bytecode) => return Ok(bytecode.len()), + Operation::Simple(_) => 0, + Operation::Address(_) => encoding.address_size as usize, + Operation::UnsignedConstant(value) => { + if value < 32 { + 0 + } else { + uleb128_size(value) + } + } + Operation::SignedConstant(value) => sleb128_size(value), + Operation::ConstantType(base, ref value) => base_size(base)? + 1 + value.len(), + Operation::FrameOffset(offset) => sleb128_size(offset), + Operation::RegisterOffset(register, offset) => { + if register.0 < 32 { + sleb128_size(offset) + } else { + uleb128_size(register.0.into()) + sleb128_size(offset) + } + } + Operation::RegisterType(register, base) => { + uleb128_size(register.0.into()) + base_size(base)? + } + Operation::Pick(index) => { + if index > 1 { + 1 + } else { + 0 + } + } + Operation::Deref { .. } => 0, + Operation::DerefSize { .. } => 1, + Operation::DerefType { base, .. } => 1 + base_size(base)?, + Operation::PlusConstant(value) => uleb128_size(value), + Operation::Skip(_) => 2, + Operation::Branch(_) => 2, + Operation::Call(_) => 4, + Operation::CallRef(_) => encoding.format.word_size() as usize, + Operation::Convert(base) => match base { + Some(base) => base_size(base)?, + None => 1, + }, + Operation::Reinterpret(base) => match base { + Some(base) => base_size(base)?, + None => 1, + }, + Operation::EntryValue(ref expression) => { + let length = expression.size(encoding, unit_offsets)?; + uleb128_size(length as u64) + length + } + Operation::Register(register) => { + if register.0 < 32 { + 0 + } else { + uleb128_size(register.0.into()) + } + } + Operation::ImplicitValue(ref data) => uleb128_size(data.len() as u64) + data.len(), + Operation::ImplicitPointer { byte_offset, .. } => { + let size = if encoding.version == 2 { + encoding.address_size + } else { + encoding.format.word_size() + }; + size as usize + sleb128_size(byte_offset) + } + Operation::Piece { size_in_bytes } => uleb128_size(size_in_bytes), + Operation::BitPiece { + size_in_bits, + bit_offset, + } => uleb128_size(size_in_bits) + uleb128_size(bit_offset), + Operation::ParameterRef(_) => 4, + Operation::WasmLocal(index) + | Operation::WasmGlobal(index) + | Operation::WasmStack(index) => 1 + uleb128_size(index.into()), + }) + } + + pub(crate) fn write<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) => offsets + .unit_offset(entry) + .ok_or(Error::UnsupportedExpressionForwardReference), + None => Err(Error::UnsupportedCfiExpressionReference), + }; + match *self { + Operation::Raw(ref bytecode) => w.write(bytecode)?, + Operation::Simple(opcode) => w.write_u8(opcode.0)?, + Operation::Address(address) => { + w.write_u8(constants::DW_OP_addr.0)?; + w.write_address(address, encoding.address_size)?; + } + Operation::UnsignedConstant(value) => { + if value < 32 { + w.write_u8(constants::DW_OP_lit0.0 + value as u8)?; + } else { + w.write_u8(constants::DW_OP_constu.0)?; + w.write_uleb128(value)?; + } + } + Operation::SignedConstant(value) => { + w.write_u8(constants::DW_OP_consts.0)?; + w.write_sleb128(value)?; + } + Operation::ConstantType(base, ref value) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_const_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_const_type.0)?; + } + w.write_uleb128(entry_offset(base)?)?; + w.write_udata(value.len() as u64, 1)?; + w.write(value)?; + } + Operation::FrameOffset(offset) => { + w.write_u8(constants::DW_OP_fbreg.0)?; + w.write_sleb128(offset)?; + } + Operation::RegisterOffset(register, offset) => { + if register.0 < 32 { + w.write_u8(constants::DW_OP_breg0.0 + register.0 as u8)?; + } else { + w.write_u8(constants::DW_OP_bregx.0)?; + w.write_uleb128(register.0.into())?; + } + w.write_sleb128(offset)?; + } + Operation::RegisterType(register, base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_regval_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_regval_type.0)?; + } + w.write_uleb128(register.0.into())?; + w.write_uleb128(entry_offset(base)?)?; + } + Operation::Pick(index) => match index { + 0 => w.write_u8(constants::DW_OP_dup.0)?, + 1 => w.write_u8(constants::DW_OP_over.0)?, + _ => { + w.write_u8(constants::DW_OP_pick.0)?; + w.write_u8(index)?; + } + }, + Operation::Deref { space } => { + if space { + w.write_u8(constants::DW_OP_xderef.0)?; + } else { + w.write_u8(constants::DW_OP_deref.0)?; + } + } + Operation::DerefSize { space, size } => { + if space { + w.write_u8(constants::DW_OP_xderef_size.0)?; + } else { + w.write_u8(constants::DW_OP_deref_size.0)?; + } + w.write_u8(size)?; + } + Operation::DerefType { space, size, base } => { + if space { + w.write_u8(constants::DW_OP_xderef_type.0)?; + } else { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_deref_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_deref_type.0)?; + } + } + w.write_u8(size)?; + w.write_uleb128(entry_offset(base)?)?; + } + Operation::PlusConstant(value) => { + w.write_u8(constants::DW_OP_plus_uconst.0)?; + w.write_uleb128(value)?; + } + Operation::Skip(target) => { + w.write_u8(constants::DW_OP_skip.0)?; + let offset = offsets[target] as i64 - (w.len() as i64 + 2); + w.write_sdata(offset, 2)?; + } + Operation::Branch(target) => { + w.write_u8(constants::DW_OP_bra.0)?; + let offset = offsets[target] as i64 - (w.len() as i64 + 2); + w.write_sdata(offset, 2)?; + } + Operation::Call(entry) => { + w.write_u8(constants::DW_OP_call4.0)?; + // TODO: this probably won't work in practice, because we may + // only know the offsets of base type DIEs at this point. + w.write_udata(entry_offset(entry)?, 4)?; + } + Operation::CallRef(entry) => { + w.write_u8(constants::DW_OP_call_ref.0)?; + let size = encoding.format.word_size(); + match entry { + Reference::Symbol(symbol) => w.write_reference(symbol, size)?, + Reference::Entry(unit, entry) => { + let refs = refs.ok_or(Error::InvalidReference)?; + refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + } + Operation::Convert(base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_convert.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_convert.0)?; + } + match base { + Some(base) => w.write_uleb128(entry_offset(base)?)?, + None => w.write_u8(0)?, + } + } + Operation::Reinterpret(base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_reinterpret.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_reinterpret.0)?; + } + match base { + Some(base) => w.write_uleb128(entry_offset(base)?)?, + None => w.write_u8(0)?, + } + } + Operation::EntryValue(ref expression) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_entry_value.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_entry_value.0)?; + } + let length = expression.size(encoding, unit_offsets)?; + w.write_uleb128(length as u64)?; + expression.write(w, refs, encoding, unit_offsets)?; + } + Operation::Register(register) => { + if register.0 < 32 { + w.write_u8(constants::DW_OP_reg0.0 + register.0 as u8)?; + } else { + w.write_u8(constants::DW_OP_regx.0)?; + w.write_uleb128(register.0.into())?; + } + } + Operation::ImplicitValue(ref data) => { + w.write_u8(constants::DW_OP_implicit_value.0)?; + w.write_uleb128(data.len() as u64)?; + w.write(data)?; + } + Operation::ImplicitPointer { entry, byte_offset } => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_implicit_pointer.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_implicit_pointer.0)?; + } + let size = if encoding.version == 2 { + encoding.address_size + } else { + encoding.format.word_size() + }; + match entry { + Reference::Symbol(symbol) => { + w.write_reference(symbol, size)?; + } + Reference::Entry(unit, entry) => { + let refs = refs.ok_or(Error::InvalidReference)?; + refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + w.write_sleb128(byte_offset)?; + } + Operation::Piece { size_in_bytes } => { + w.write_u8(constants::DW_OP_piece.0)?; + w.write_uleb128(size_in_bytes)?; + } + Operation::BitPiece { + size_in_bits, + bit_offset, + } => { + w.write_u8(constants::DW_OP_bit_piece.0)?; + w.write_uleb128(size_in_bits)?; + w.write_uleb128(bit_offset)?; + } + Operation::ParameterRef(entry) => { + w.write_u8(constants::DW_OP_GNU_parameter_ref.0)?; + w.write_udata(entry_offset(entry)?, 4)?; + } + Operation::WasmLocal(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 0])?; + w.write_uleb128(index.into())?; + } + Operation::WasmGlobal(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 1])?; + w.write_uleb128(index.into())?; + } + Operation::WasmStack(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 2])?; + w.write_uleb128(index.into())?; + } + } + Ok(()) + } +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::common::UnitSectionOffset; + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult, UnitId}; + use std::collections::HashMap; + + impl Expression { + /// Create an expression from the input expression. + pub fn from<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 from_operations.next()?.is_some() { + offsets.push(offset); + offset = from_operations.offset_from(&from_expression); + } + offsets.push(from_expression.0.len()); + + let mut from_operations = from_expression.clone().operations(encoding); + let mut operations = Vec::new(); + while let Some(from_operation) = from_operations.next()? { + let operation = match from_operation { + read::Operation::Deref { + base_type, + size, + space, + } => { + if base_type.0 != 0 { + let base = convert_unit_offset(base_type)?; + Operation::DerefType { space, size, base } + } else if size != encoding.address_size { + Operation::DerefSize { space, size } + } else { + Operation::Deref { space } + } + } + read::Operation::Drop => Operation::Simple(constants::DW_OP_drop), + read::Operation::Pick { index } => Operation::Pick(index), + read::Operation::Swap => Operation::Simple(constants::DW_OP_swap), + read::Operation::Rot => Operation::Simple(constants::DW_OP_rot), + read::Operation::Abs => Operation::Simple(constants::DW_OP_abs), + read::Operation::And => Operation::Simple(constants::DW_OP_and), + read::Operation::Div => Operation::Simple(constants::DW_OP_div), + read::Operation::Minus => Operation::Simple(constants::DW_OP_minus), + read::Operation::Mod => Operation::Simple(constants::DW_OP_mod), + read::Operation::Mul => Operation::Simple(constants::DW_OP_mul), + read::Operation::Neg => Operation::Simple(constants::DW_OP_neg), + read::Operation::Not => Operation::Simple(constants::DW_OP_not), + read::Operation::Or => Operation::Simple(constants::DW_OP_or), + read::Operation::Plus => Operation::Simple(constants::DW_OP_plus), + read::Operation::PlusConstant { value } => Operation::PlusConstant(value), + read::Operation::Shl => Operation::Simple(constants::DW_OP_shl), + read::Operation::Shr => Operation::Simple(constants::DW_OP_shr), + read::Operation::Shra => Operation::Simple(constants::DW_OP_shra), + read::Operation::Xor => Operation::Simple(constants::DW_OP_xor), + read::Operation::Eq => Operation::Simple(constants::DW_OP_eq), + read::Operation::Ge => Operation::Simple(constants::DW_OP_ge), + read::Operation::Gt => Operation::Simple(constants::DW_OP_gt), + read::Operation::Le => Operation::Simple(constants::DW_OP_le), + read::Operation::Lt => Operation::Simple(constants::DW_OP_lt), + read::Operation::Ne => Operation::Simple(constants::DW_OP_ne), + read::Operation::Bra { target } => { + let offset = from_operations + .offset_from(&from_expression) + .wrapping_add(i64::from(target) as usize); + let index = offsets + .binary_search(&offset) + .map_err(|_| ConvertError::InvalidBranchTarget)?; + Operation::Branch(index) + } + read::Operation::Skip { target } => { + let offset = from_operations + .offset_from(&from_expression) + .wrapping_add(i64::from(target) as usize); + let index = offsets + .binary_search(&offset) + .map_err(|_| ConvertError::InvalidBranchTarget)?; + Operation::Skip(index) + } + read::Operation::UnsignedConstant { value } => { + Operation::UnsignedConstant(value) + } + read::Operation::SignedConstant { value } => Operation::SignedConstant(value), + read::Operation::Register { register } => Operation::Register(register), + read::Operation::RegisterOffset { + register, + offset, + base_type, + } => { + if base_type.0 != 0 { + Operation::RegisterType(register, convert_unit_offset(base_type)?) + } else { + Operation::RegisterOffset(register, offset) + } + } + read::Operation::FrameOffset { offset } => Operation::FrameOffset(offset), + read::Operation::Nop => Operation::Simple(constants::DW_OP_nop), + read::Operation::PushObjectAddress => { + Operation::Simple(constants::DW_OP_push_object_address) + } + read::Operation::Call { offset } => match offset { + read::DieReference::UnitRef(offset) => { + Operation::Call(convert_unit_offset(offset)?) + } + read::DieReference::DebugInfoRef(offset) => { + Operation::CallRef(convert_debug_info_offset(offset)?) + } + }, + read::Operation::TLS => Operation::Simple(constants::DW_OP_form_tls_address), + read::Operation::CallFrameCFA => { + Operation::Simple(constants::DW_OP_call_frame_cfa) + } + read::Operation::Piece { + size_in_bits, + bit_offset: None, + } => Operation::Piece { + size_in_bytes: size_in_bits / 8, + }, + read::Operation::Piece { + size_in_bits, + bit_offset: Some(bit_offset), + } => Operation::BitPiece { + size_in_bits, + bit_offset, + }, + read::Operation::ImplicitValue { data } => { + Operation::ImplicitValue(data.to_slice()?.into_owned().into()) + } + read::Operation::StackValue => Operation::Simple(constants::DW_OP_stack_value), + read::Operation::ImplicitPointer { value, byte_offset } => { + let entry = convert_debug_info_offset(value)?; + Operation::ImplicitPointer { entry, byte_offset } + } + read::Operation::EntryValue { expression } => { + let expression = Expression::from( + read::Expression(expression), + encoding, + dwarf, + unit, + entry_ids, + convert_address, + )?; + Operation::EntryValue(expression) + } + read::Operation::ParameterRef { offset } => { + let entry = convert_unit_offset(offset)?; + Operation::ParameterRef(entry) + } + read::Operation::Address { address } => { + let address = + convert_address(address).ok_or(ConvertError::InvalidAddress)?; + Operation::Address(address) + } + read::Operation::AddressIndex { index } => { + let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let val = dwarf.address(unit, index)?; + let address = convert_address(val).ok_or(ConvertError::InvalidAddress)?; + Operation::Address(address) + } + read::Operation::ConstantIndex { index } => { + let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let val = dwarf.address(unit, index)?; + Operation::UnsignedConstant(val) + } + read::Operation::TypedLiteral { base_type, value } => { + let entry = convert_unit_offset(base_type)?; + Operation::ConstantType(entry, value.to_slice()?.into_owned().into()) + } + read::Operation::Convert { base_type } => { + if base_type.0 == 0 { + Operation::Convert(None) + } else { + let entry = convert_unit_offset(base_type)?; + Operation::Convert(Some(entry)) + } + } + read::Operation::Reinterpret { base_type } => { + if base_type.0 == 0 { + Operation::Reinterpret(None) + } else { + let entry = convert_unit_offset(base_type)?; + Operation::Reinterpret(Some(entry)) + } + } + read::Operation::WasmLocal { index } => Operation::WasmLocal(index), + read::Operation::WasmGlobal { index } => Operation::WasmGlobal(index), + read::Operation::WasmStack { index } => Operation::WasmStack(index), + }; + operations.push(operation); + } + Ok(Expression { operations }) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{DebugInfoOffset, Format}; + use crate::read; + use crate::write::{AttributeValue, Dwarf, EndianVec, LineProgram, Sections, Unit}; + use crate::LittleEndian; + use std::collections::HashMap; + + #[test] + #[allow(clippy::type_complexity)] + fn test_operation() { + for version in [2, 3, 4, 5] { + for address_size in [4, 8] { + for format in [Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut dwarf = Dwarf::new(); + let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit_id); + + // Create an entry that can be referenced by the expression. + let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type); + let reference = Reference::Entry(unit_id, entry_id); + + // The offsets for the above entry when reading back the expression. + struct ReadState { + debug_info_offset: DebugInfoOffset, + entry_offset: read::UnitOffset, + } + + let mut reg_expression = Expression::new(); + reg_expression.op_reg(Register(23)); + + let operations: &[( + &dyn Fn(&mut Expression), + Operation, + &dyn Fn(&ReadState) -> read::Operation<_>, + )] = &[ + ( + &|x| x.op_deref(), + Operation::Deref { space: false }, + &|_| read::Operation::Deref { + base_type: read::UnitOffset(0), + size: address_size, + space: false, + }, + ), + ( + &|x| x.op_xderef(), + Operation::Deref { space: true }, + &|_| read::Operation::Deref { + base_type: read::UnitOffset(0), + size: address_size, + space: true, + }, + ), + ( + &|x| x.op_deref_size(2), + Operation::DerefSize { + space: false, + size: 2, + }, + &|_| read::Operation::Deref { + base_type: read::UnitOffset(0), + size: 2, + space: false, + }, + ), + ( + &|x| x.op_xderef_size(2), + Operation::DerefSize { + space: true, + size: 2, + }, + &|_| read::Operation::Deref { + base_type: read::UnitOffset(0), + size: 2, + space: true, + }, + ), + ( + &|x| x.op_deref_type(2, entry_id), + Operation::DerefType { + space: false, + size: 2, + base: entry_id, + }, + &|x| read::Operation::Deref { + base_type: x.entry_offset, + size: 2, + space: false, + }, + ), + ( + &|x| x.op_xderef_type(2, entry_id), + Operation::DerefType { + space: true, + size: 2, + base: entry_id, + }, + &|x| read::Operation::Deref { + base_type: x.entry_offset, + size: 2, + space: true, + }, + ), + ( + &|x| x.op(constants::DW_OP_drop), + Operation::Simple(constants::DW_OP_drop), + &|_| read::Operation::Drop, + ), + (&|x| x.op_pick(0), Operation::Pick(0), &|_| { + read::Operation::Pick { index: 0 } + }), + (&|x| x.op_pick(1), Operation::Pick(1), &|_| { + read::Operation::Pick { index: 1 } + }), + (&|x| x.op_pick(2), Operation::Pick(2), &|_| { + read::Operation::Pick { index: 2 } + }), + ( + &|x| x.op(constants::DW_OP_swap), + Operation::Simple(constants::DW_OP_swap), + &|_| read::Operation::Swap, + ), + ( + &|x| x.op(constants::DW_OP_rot), + Operation::Simple(constants::DW_OP_rot), + &|_| read::Operation::Rot, + ), + ( + &|x| x.op(constants::DW_OP_abs), + Operation::Simple(constants::DW_OP_abs), + &|_| read::Operation::Abs, + ), + ( + &|x| x.op(constants::DW_OP_and), + Operation::Simple(constants::DW_OP_and), + &|_| read::Operation::And, + ), + ( + &|x| x.op(constants::DW_OP_div), + Operation::Simple(constants::DW_OP_div), + &|_| read::Operation::Div, + ), + ( + &|x| x.op(constants::DW_OP_minus), + Operation::Simple(constants::DW_OP_minus), + &|_| read::Operation::Minus, + ), + ( + &|x| x.op(constants::DW_OP_mod), + Operation::Simple(constants::DW_OP_mod), + &|_| read::Operation::Mod, + ), + ( + &|x| x.op(constants::DW_OP_mul), + Operation::Simple(constants::DW_OP_mul), + &|_| read::Operation::Mul, + ), + ( + &|x| x.op(constants::DW_OP_neg), + Operation::Simple(constants::DW_OP_neg), + &|_| read::Operation::Neg, + ), + ( + &|x| x.op(constants::DW_OP_not), + Operation::Simple(constants::DW_OP_not), + &|_| read::Operation::Not, + ), + ( + &|x| x.op(constants::DW_OP_or), + Operation::Simple(constants::DW_OP_or), + &|_| read::Operation::Or, + ), + ( + &|x| x.op(constants::DW_OP_plus), + Operation::Simple(constants::DW_OP_plus), + &|_| read::Operation::Plus, + ), + ( + &|x| x.op_plus_uconst(23), + Operation::PlusConstant(23), + &|_| read::Operation::PlusConstant { value: 23 }, + ), + ( + &|x| x.op(constants::DW_OP_shl), + Operation::Simple(constants::DW_OP_shl), + &|_| read::Operation::Shl, + ), + ( + &|x| x.op(constants::DW_OP_shr), + Operation::Simple(constants::DW_OP_shr), + &|_| read::Operation::Shr, + ), + ( + &|x| x.op(constants::DW_OP_shra), + Operation::Simple(constants::DW_OP_shra), + &|_| read::Operation::Shra, + ), + ( + &|x| x.op(constants::DW_OP_xor), + Operation::Simple(constants::DW_OP_xor), + &|_| read::Operation::Xor, + ), + ( + &|x| x.op(constants::DW_OP_eq), + Operation::Simple(constants::DW_OP_eq), + &|_| read::Operation::Eq, + ), + ( + &|x| x.op(constants::DW_OP_ge), + Operation::Simple(constants::DW_OP_ge), + &|_| read::Operation::Ge, + ), + ( + &|x| x.op(constants::DW_OP_gt), + Operation::Simple(constants::DW_OP_gt), + &|_| read::Operation::Gt, + ), + ( + &|x| x.op(constants::DW_OP_le), + Operation::Simple(constants::DW_OP_le), + &|_| read::Operation::Le, + ), + ( + &|x| x.op(constants::DW_OP_lt), + Operation::Simple(constants::DW_OP_lt), + &|_| read::Operation::Lt, + ), + ( + &|x| x.op(constants::DW_OP_ne), + Operation::Simple(constants::DW_OP_ne), + &|_| read::Operation::Ne, + ), + ( + &|x| x.op_constu(23), + Operation::UnsignedConstant(23), + &|_| read::Operation::UnsignedConstant { value: 23 }, + ), + ( + &|x| x.op_consts(-23), + Operation::SignedConstant(-23), + &|_| read::Operation::SignedConstant { value: -23 }, + ), + ( + &|x| x.op_reg(Register(23)), + Operation::Register(Register(23)), + &|_| read::Operation::Register { + register: Register(23), + }, + ), + ( + &|x| x.op_reg(Register(123)), + Operation::Register(Register(123)), + &|_| read::Operation::Register { + register: Register(123), + }, + ), + ( + &|x| x.op_breg(Register(23), 34), + Operation::RegisterOffset(Register(23), 34), + &|_| read::Operation::RegisterOffset { + register: Register(23), + offset: 34, + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_breg(Register(123), 34), + Operation::RegisterOffset(Register(123), 34), + &|_| read::Operation::RegisterOffset { + register: Register(123), + offset: 34, + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_regval_type(Register(23), entry_id), + Operation::RegisterType(Register(23), entry_id), + &|x| read::Operation::RegisterOffset { + register: Register(23), + offset: 0, + base_type: x.entry_offset, + }, + ), + (&|x| x.op_fbreg(34), Operation::FrameOffset(34), &|_| { + read::Operation::FrameOffset { offset: 34 } + }), + ( + &|x| x.op(constants::DW_OP_nop), + Operation::Simple(constants::DW_OP_nop), + &|_| read::Operation::Nop, + ), + ( + &|x| x.op(constants::DW_OP_push_object_address), + Operation::Simple(constants::DW_OP_push_object_address), + &|_| read::Operation::PushObjectAddress, + ), + (&|x| x.op_call(entry_id), Operation::Call(entry_id), &|x| { + read::Operation::Call { + offset: read::DieReference::UnitRef(x.entry_offset), + } + }), + ( + &|x| x.op_call_ref(reference), + Operation::CallRef(reference), + &|x| read::Operation::Call { + offset: read::DieReference::DebugInfoRef(x.debug_info_offset), + }, + ), + ( + &|x| x.op(constants::DW_OP_form_tls_address), + Operation::Simple(constants::DW_OP_form_tls_address), + &|_| read::Operation::TLS, + ), + ( + &|x| x.op(constants::DW_OP_call_frame_cfa), + Operation::Simple(constants::DW_OP_call_frame_cfa), + &|_| read::Operation::CallFrameCFA, + ), + ( + &|x| x.op_piece(23), + Operation::Piece { size_in_bytes: 23 }, + &|_| read::Operation::Piece { + size_in_bits: 23 * 8, + bit_offset: None, + }, + ), + ( + &|x| x.op_bit_piece(23, 34), + Operation::BitPiece { + size_in_bits: 23, + bit_offset: 34, + }, + &|_| read::Operation::Piece { + size_in_bits: 23, + bit_offset: Some(34), + }, + ), + ( + &|x| x.op_implicit_value(vec![23].into()), + Operation::ImplicitValue(vec![23].into()), + &|_| read::Operation::ImplicitValue { + data: read::EndianSlice::new(&[23], LittleEndian), + }, + ), + ( + &|x| x.op(constants::DW_OP_stack_value), + Operation::Simple(constants::DW_OP_stack_value), + &|_| read::Operation::StackValue, + ), + ( + &|x| x.op_implicit_pointer(reference, 23), + Operation::ImplicitPointer { + entry: reference, + byte_offset: 23, + }, + &|x| read::Operation::ImplicitPointer { + value: x.debug_info_offset, + byte_offset: 23, + }, + ), + ( + &|x| x.op_entry_value(reg_expression.clone()), + Operation::EntryValue(reg_expression.clone()), + &|_| read::Operation::EntryValue { + expression: read::EndianSlice::new( + &[constants::DW_OP_reg23.0], + LittleEndian, + ), + }, + ), + ( + &|x| x.op_gnu_parameter_ref(entry_id), + Operation::ParameterRef(entry_id), + &|x| read::Operation::ParameterRef { + offset: x.entry_offset, + }, + ), + ( + &|x| x.op_addr(Address::Constant(23)), + Operation::Address(Address::Constant(23)), + &|_| read::Operation::Address { address: 23 }, + ), + ( + &|x| x.op_const_type(entry_id, vec![23].into()), + Operation::ConstantType(entry_id, vec![23].into()), + &|x| read::Operation::TypedLiteral { + base_type: x.entry_offset, + value: read::EndianSlice::new(&[23], LittleEndian), + }, + ), + (&|x| x.op_convert(None), Operation::Convert(None), &|_| { + read::Operation::Convert { + base_type: read::UnitOffset(0), + } + }), + ( + &|x| x.op_convert(Some(entry_id)), + Operation::Convert(Some(entry_id)), + &|x| read::Operation::Convert { + base_type: x.entry_offset, + }, + ), + ( + &|x| x.op_reinterpret(None), + Operation::Reinterpret(None), + &|_| read::Operation::Reinterpret { + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_reinterpret(Some(entry_id)), + Operation::Reinterpret(Some(entry_id)), + &|x| read::Operation::Reinterpret { + base_type: x.entry_offset, + }, + ), + ( + &|x| x.op_wasm_local(1000), + Operation::WasmLocal(1000), + &|_| read::Operation::WasmLocal { index: 1000 }, + ), + ( + &|x| x.op_wasm_global(1000), + Operation::WasmGlobal(1000), + &|_| read::Operation::WasmGlobal { index: 1000 }, + ), + ( + &|x| x.op_wasm_stack(1000), + Operation::WasmStack(1000), + &|_| read::Operation::WasmStack { index: 1000 }, + ), + ]; + + // Create a single expression containing all operations. + let mut expression = Expression::new(); + let start_index = expression.next_index(); + for (f, o, _) in operations { + f(&mut expression); + assert_eq!(expression.operations.last(), Some(o)); + } + + let bra_index = expression.op_bra(); + let skip_index = expression.op_skip(); + expression.op(constants::DW_OP_nop); + let end_index = expression.next_index(); + expression.set_target(bra_index, start_index); + expression.set_target(skip_index, end_index); + + // Create an entry containing the expression. + let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + let subprogram = unit.get_mut(subprogram_id); + subprogram.set( + constants::DW_AT_location, + AttributeValue::Exprloc(expression), + ); + + // Write the DWARF, then parse it. + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let mut read_units = read_dwarf.units(); + let read_unit_header = read_units.next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit_header).unwrap(); + let mut read_entries = read_unit.entries(); + let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(read_entry.tag(), constants::DW_TAG_compile_unit); + + // Determine the offset of the entry that can be referenced by the expression. + let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(read_entry.tag(), constants::DW_TAG_base_type); + let read_state = ReadState { + debug_info_offset: read_entry + .offset() + .to_debug_info_offset(&read_unit.header) + .unwrap(), + entry_offset: read_entry.offset(), + }; + + // Get the expression. + let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(read_entry.tag(), constants::DW_TAG_subprogram); + let read_attr = read_entry + .attr_value(constants::DW_AT_location) + .unwrap() + .unwrap(); + let read_expression = read_attr.exprloc_value().unwrap(); + let mut read_operations = read_expression.operations(encoding); + for (_, _, operation) in operations { + assert_eq!(read_operations.next(), Ok(Some(operation(&read_state)))); + } + + // 4 = DW_OP_skip + i16 + DW_OP_nop + assert_eq!( + read_operations.next(), + Ok(Some(read::Operation::Bra { + target: -(read_expression.0.len() as i16) + 4 + })) + ); + // 1 = DW_OP_nop + assert_eq!( + read_operations.next(), + Ok(Some(read::Operation::Skip { target: 1 })) + ); + assert_eq!(read_operations.next(), Ok(Some(read::Operation::Nop))); + assert_eq!(read_operations.next(), Ok(None)); + + let mut entry_ids = HashMap::new(); + entry_ids.insert(read_state.debug_info_offset.into(), (unit_id, entry_id)); + let convert_expression = Expression::from( + read_expression, + encoding, + Some(&read_dwarf), + Some(&read_unit), + Some(&entry_ids), + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + let mut convert_operations = convert_expression.operations.iter(); + for (_, operation, _) in operations { + assert_eq!(convert_operations.next(), Some(operation)); + } + assert_eq!( + convert_operations.next(), + Some(&Operation::Branch(start_index)) + ); + assert_eq!(convert_operations.next(), Some(&Operation::Skip(end_index))); + assert_eq!( + convert_operations.next(), + Some(&Operation::Simple(constants::DW_OP_nop)) + ); + } + } + } + } + + #[test] + #[allow(clippy::type_complexity)] + fn test_expression_raw() { + for version in [2, 3, 4, 5] { + for address_size in [4, 8] { + for format in [Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut dwarf = Dwarf::new(); + let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit_id); + let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + let subprogram = unit.get_mut(subprogram_id); + let expression = Expression::raw(vec![constants::DW_OP_constu.0, 23]); + subprogram.set( + constants::DW_AT_location, + AttributeValue::Exprloc(expression), + ); + + // Write the DWARF, then parse it. + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let mut read_units = read_dwarf.units(); + let read_unit_header = read_units.next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit_header).unwrap(); + let mut read_entries = read_unit.entries(); + let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(read_entry.tag(), constants::DW_TAG_compile_unit); + let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(read_entry.tag(), constants::DW_TAG_subprogram); + let read_attr = read_entry + .attr_value(constants::DW_AT_location) + .unwrap() + .unwrap(); + let read_expression = read_attr.exprloc_value().unwrap(); + let mut read_operations = read_expression.operations(encoding); + assert_eq!( + read_operations.next(), + Ok(Some(read::Operation::UnsignedConstant { value: 23 })) + ); + assert_eq!(read_operations.next(), Ok(None)); + } + } + } + } + + #[test] + fn test_forward_ref() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let mut dwarf = Dwarf::new(); + let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit_id); + + // First, create an entry that will contain the expression. + let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + + // Now create the entry to be referenced by the expression. + let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); + + // Create an expression containing the reference. + let mut expression = Expression::new(); + expression.op_deref_type(2, entry_id); + + // Add the expression to the subprogram. + unit.get_mut(subprogram_id).set( + constants::DW_AT_location, + AttributeValue::Exprloc(expression), + ); + + // Writing the DWARF should fail. + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + assert_eq!( + dwarf.write(&mut sections), + Err(Error::UnsupportedExpressionForwardReference) + ); + } + + #[test] + fn test_missing_unit_ref() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let mut dwarf = Dwarf::new(); + let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit_id); + + // Create the entry to be referenced by the expression. + let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); + // And delete it so that it is not available when writing the expression. + unit.get_mut(unit.root()).delete_child(entry_id); + + // Create an expression containing the reference. + let mut expression = Expression::new(); + expression.op_deref_type(2, entry_id); + + // Create an entry containing the expression. + let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + unit.get_mut(subprogram_id).set( + constants::DW_AT_location, + AttributeValue::Exprloc(expression), + ); + + // Writing the DWARF should fail. + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + assert_eq!( + dwarf.write(&mut sections), + Err(Error::UnsupportedExpressionForwardReference) + ); + } + + #[test] + fn test_missing_debuginfo_ref() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let mut dwarf = Dwarf::new(); + let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit_id); + + // Create the entry to be referenced by the expression. + let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); + // And delete it so that it is not available when writing the expression. + unit.get_mut(unit.root()).delete_child(entry_id); + + // Create an expression containing the reference. + let mut expression = Expression::new(); + expression.op_call_ref(Reference::Entry(unit_id, entry_id)); + + // Create an entry containing the expression. + let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + unit.get_mut(subprogram_id).set( + constants::DW_AT_location, + AttributeValue::Exprloc(expression), + ); + + // Writing the DWARF should fail. + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + assert_eq!(dwarf.write(&mut sections), Err(Error::InvalidReference)); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/range.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/range.rs new file mode 100644 index 0000000..10a55fb --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/range.rs
@@ -0,0 +1,427 @@ +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) + } + + /// Get a reference to a location list. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: RangeListId) -> &RangeList { + debug_assert_eq!(self.base_id, id.base_id); + &self.ranges[id.index] + } + + /// Write the range list table to the appropriate section for the given DWARF version. + pub(crate) fn write<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: 0, .. } => continue, + Range::StartEnd { begin, end, .. } if begin == end => continue, + Range::OffsetPair { begin, end, .. } if begin == end => continue, + _ => (), + } + ranges.push(range); + } + Ok(RangeList(ranges)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, + }; + use crate::read; + use crate::write::{ + ConvertUnitContext, EndianVec, LineStringTable, LocationListTable, Range, RangeListTable, + StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + use std::sync::Arc; + + #[test] + fn test_range() { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut range_list = RangeList(vec![ + Range::StartLength { + begin: Address::Constant(6666), + length: 7777, + }, + Range::StartEnd { + begin: Address::Constant(4444), + end: Address::Constant(5555), + }, + Range::BaseAddress { + address: Address::Constant(1111), + }, + Range::OffsetPair { + begin: 2222, + end: 3333, + }, + ]); + + let mut ranges = RangeListTable::default(); + let range_list_id = ranges.add(range_list.clone()); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let range_list_offsets = ranges.write(&mut sections, encoding).unwrap(); + + let read_debug_ranges = + read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian); + let read_debug_rnglists = + read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian); + let read_ranges = read::RangeLists::new(read_debug_ranges, read_debug_rnglists); + let offset = range_list_offsets.get(range_list_id); + let read_range_list = read_ranges.raw_ranges(offset, encoding).unwrap(); + + let dwarf = read::Dwarf { + ranges: read_ranges, + ..Default::default() + }; + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::default(), + ), + abbreviations: Arc::new(read::Abbreviations::default()), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + let context = ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut ranges, + locations: &mut LocationListTable::default(), + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + let convert_range_list = RangeList::from(read_range_list, &context).unwrap(); + + if version <= 4 { + range_list.0[0] = Range::StartEnd { + begin: Address::Constant(6666), + end: Address::Constant(6666 + 7777), + }; + } + assert_eq!(range_list, convert_range_list); + } + } + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/relocate.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/relocate.rs new file mode 100644 index 0000000..ff8dde1 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/relocate.rs
@@ -0,0 +1,280 @@ +use crate::constants; +use crate::write::{Address, Error, Result, Writer}; +use crate::SectionId; + +/// A relocation to be applied to a section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Relocation { + /// The offset within the section where the relocation should be applied. + pub offset: usize, + /// The size of the value to be relocated. + pub size: u8, + /// The target of the relocation. + pub target: RelocationTarget, + /// The addend to be applied to the relocated value. + pub addend: i64, + /// The pointer encoding for relocations in unwind information. + pub eh_pe: Option<constants::DwEhPe>, +} + +/// The target of a relocation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RelocationTarget { + /// The relocation target is a symbol. + /// + /// The meaning of this value is decided by the writer, but + /// will typically be an index into a symbol table. + Symbol(usize), + /// The relocation target is a section. + Section(SectionId), +} + +/// A `Writer` which also records relocations. +pub trait RelocateWriter { + /// The type of the writer being used to write the section data. + type Writer: Writer; + + /// Get the writer being used to write the section data. + fn writer(&self) -> &Self::Writer; + + /// Get the writer being used to write the section data. + fn writer_mut(&mut self) -> &mut Self::Writer; + + /// Record a relocation. + fn relocate(&mut self, relocation: Relocation); +} + +impl<T: RelocateWriter> Writer for T { + type Endian = <<T as RelocateWriter>::Writer as Writer>::Endian; + + fn endian(&self) -> Self::Endian { + self.writer().endian() + } + + fn len(&self) -> usize { + self.writer().len() + } + + fn write(&mut self, bytes: &[u8]) -> Result<()> { + self.writer_mut().write(bytes) + } + + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { + self.writer_mut().write_at(offset, bytes) + } + + fn write_address(&mut self, address: Address, size: u8) -> Result<()> { + match address { + Address::Constant(val) => self.writer_mut().write_udata(val, size), + Address::Symbol { symbol, addend } => { + self.relocate(Relocation { + offset: self.len(), + size, + target: RelocationTarget::Symbol(symbol), + addend, + eh_pe: None, + }); + self.writer_mut().write_udata(0, size) + } + } + } + + fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { + self.relocate(Relocation { + offset: self.len(), + size, + target: RelocationTarget::Section(section), + addend: val as i64, + eh_pe: None, + }); + self.writer_mut().write_udata(0, size) + } + + fn write_offset_at( + &mut self, + offset: usize, + val: usize, + section: SectionId, + size: u8, + ) -> Result<()> { + self.relocate(Relocation { + offset, + size, + target: RelocationTarget::Section(section), + addend: val as i64, + eh_pe: None, + }); + self.writer_mut().write_udata_at(offset, 0, size) + } + + fn write_eh_pointer( + &mut self, + address: Address, + eh_pe: constants::DwEhPe, + size: u8, + ) -> Result<()> { + match address { + Address::Constant(_) => self.writer_mut().write_eh_pointer(address, eh_pe, size), + Address::Symbol { symbol, addend } => { + let size = match eh_pe.format() { + constants::DW_EH_PE_absptr => size, + constants::DW_EH_PE_udata2 => 2, + constants::DW_EH_PE_udata4 => 4, + constants::DW_EH_PE_udata8 => 8, + constants::DW_EH_PE_sdata2 => 2, + constants::DW_EH_PE_sdata4 => 4, + constants::DW_EH_PE_sdata8 => 8, + _ => return Err(Error::UnsupportedPointerEncoding(eh_pe)), + }; + self.relocate(Relocation { + offset: self.len(), + size, + target: RelocationTarget::Symbol(symbol), + addend, + eh_pe: Some(eh_pe), + }); + self.writer_mut().write_udata(0, size) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::write::EndianVec; + use crate::{LittleEndian, SectionId}; + use alloc::vec::Vec; + + struct Section { + writer: EndianVec<LittleEndian>, + relocations: Vec<Relocation>, + } + + impl RelocateWriter for Section { + type Writer = EndianVec<LittleEndian>; + + fn writer(&self) -> &Self::Writer { + &self.writer + } + + fn writer_mut(&mut self) -> &mut Self::Writer { + &mut self.writer + } + + fn relocate(&mut self, relocation: Relocation) { + self.relocations.push(relocation); + } + } + + #[test] + fn test_relocate_writer() { + let mut expected_data = Vec::new(); + let mut expected_relocations = Vec::new(); + + let mut section = Section { + writer: EndianVec::new(LittleEndian), + relocations: Vec::new(), + }; + + // No relocation for plain data. + section.write_udata(0x12345678, 4).unwrap(); + expected_data.extend_from_slice(&0x12345678u32.to_le_bytes()); + + // No relocation for a constant address. + section + .write_address(Address::Constant(0x87654321), 4) + .unwrap(); + expected_data.extend_from_slice(&0x87654321u32.to_le_bytes()); + + // Relocation for a symbol address. + let offset = section.len(); + section + .write_address( + Address::Symbol { + symbol: 1, + addend: 0x12345678, + }, + 4, + ) + .unwrap(); + expected_data.extend_from_slice(&[0; 4]); + expected_relocations.push(Relocation { + offset, + size: 4, + target: RelocationTarget::Symbol(1), + addend: 0x12345678, + eh_pe: None, + }); + + // Relocation for a section offset. + let offset = section.len(); + section + .write_offset(0x12345678, SectionId::DebugAbbrev, 4) + .unwrap(); + expected_data.extend_from_slice(&[0; 4]); + expected_relocations.push(Relocation { + offset, + size: 4, + target: RelocationTarget::Section(SectionId::DebugAbbrev), + addend: 0x12345678, + eh_pe: None, + }); + + // Relocation for a section offset at a specific offset. + let offset = section.len(); + section.write_udata(0x12345678, 4).unwrap(); + section + .write_offset_at(offset, 0x12345678, SectionId::DebugStr, 4) + .unwrap(); + expected_data.extend_from_slice(&[0; 4]); + expected_relocations.push(Relocation { + offset, + size: 4, + target: RelocationTarget::Section(SectionId::DebugStr), + addend: 0x12345678, + eh_pe: None, + }); + + // No relocation for a constant in unwind information. + section + .write_eh_pointer(Address::Constant(0x87654321), constants::DW_EH_PE_absptr, 8) + .unwrap(); + expected_data.extend_from_slice(&0x87654321u64.to_le_bytes()); + + // No relocation for a relative constant in unwind information. + let offset = section.len(); + section + .write_eh_pointer( + Address::Constant(offset as u64 - 8), + constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, + 8, + ) + .unwrap(); + expected_data.extend_from_slice(&(-8i32).to_le_bytes()); + + // Relocation for a symbol in unwind information. + let offset = section.len(); + section + .write_eh_pointer( + Address::Symbol { + symbol: 2, + addend: 0x12345678, + }, + constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, + 8, + ) + .unwrap(); + expected_data.extend_from_slice(&[0; 4]); + expected_relocations.push(Relocation { + offset, + size: 4, + target: RelocationTarget::Symbol(2), + addend: 0x12345678, + eh_pe: Some(constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4), + }); + + assert_eq!(section.writer.into_vec(), expected_data); + assert_eq!(section.relocations, expected_relocations); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/section.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/section.rs new file mode 100644 index 0000000..6439680 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/section.rs
@@ -0,0 +1,227 @@ +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), + debug_info_refs: Vec::new(), + debug_loc_refs: Vec::new(), + debug_loclists_refs: Vec::new(), + } + } +} + +impl<W: Writer> Sections<W> { + /// Get the section with the given `id`. + pub fn get(&self, id: SectionId) -> Option<&W> { + match id { + SectionId::DebugAbbrev => Some(&self.debug_abbrev.0), + SectionId::DebugInfo => Some(&self.debug_info.0), + SectionId::DebugLine => Some(&self.debug_line.0), + SectionId::DebugLineStr => Some(&self.debug_line_str.0), + SectionId::DebugRanges => Some(&self.debug_ranges.0), + SectionId::DebugRngLists => Some(&self.debug_rnglists.0), + SectionId::DebugLoc => Some(&self.debug_loc.0), + SectionId::DebugLocLists => Some(&self.debug_loclists.0), + SectionId::DebugStr => Some(&self.debug_str.0), + SectionId::DebugFrame => Some(&self.debug_frame.0), + SectionId::EhFrame => Some(&self.eh_frame.0), + _ => None, + } + } + + /// Get the section with the given `id`. + pub fn get_mut(&mut self, id: SectionId) -> Option<&mut W> { + match id { + SectionId::DebugAbbrev => Some(&mut self.debug_abbrev.0), + SectionId::DebugInfo => Some(&mut self.debug_info.0), + SectionId::DebugLine => Some(&mut self.debug_line.0), + SectionId::DebugLineStr => Some(&mut self.debug_line_str.0), + SectionId::DebugRanges => Some(&mut self.debug_ranges.0), + SectionId::DebugRngLists => Some(&mut self.debug_rnglists.0), + SectionId::DebugLoc => Some(&mut self.debug_loc.0), + SectionId::DebugLocLists => Some(&mut self.debug_loclists.0), + SectionId::DebugStr => Some(&mut self.debug_str.0), + SectionId::DebugFrame => Some(&mut self.debug_frame.0), + SectionId::EhFrame => Some(&mut self.eh_frame.0), + _ => None, + } + } + + /// For each section, call `f` once with a shared reference. + pub fn for_each<'a, F, E>(&'a self, mut f: F) -> result::Result<(), E> + where + F: FnMut(SectionId, &'a W) -> result::Result<(), E>, + { + macro_rules! f { + ($s:expr) => { + f($s.id(), &$s) + }; + } + // Ordered so that earlier sections do not reference later sections. + f!(self.debug_abbrev)?; + f!(self.debug_str)?; + f!(self.debug_line_str)?; + f!(self.debug_line)?; + f!(self.debug_ranges)?; + f!(self.debug_rnglists)?; + f!(self.debug_loc)?; + f!(self.debug_loclists)?; + f!(self.debug_info)?; + f!(self.debug_frame)?; + f!(self.eh_frame)?; + Ok(()) + } + + /// For each section, call `f` once with a mutable reference. + pub fn for_each_mut<'a, F, E>(&'a mut self, mut f: F) -> result::Result<(), E> + where + F: FnMut(SectionId, &'a mut W) -> result::Result<(), E>, + { + macro_rules! f { + ($s:expr) => { + f($s.id(), &mut $s) + }; + } + // Ordered so that earlier sections do not reference later sections. + f!(self.debug_abbrev)?; + f!(self.debug_str)?; + f!(self.debug_line_str)?; + f!(self.debug_line)?; + f!(self.debug_ranges)?; + f!(self.debug_rnglists)?; + f!(self.debug_loc)?; + f!(self.debug_loclists)?; + f!(self.debug_info)?; + f!(self.debug_frame)?; + f!(self.eh_frame)?; + Ok(()) + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::{read, write::EndianVec, Endianity}; + + impl<E: Endianity> Sections<EndianVec<E>> { + pub(crate) fn read(&self, endian: E) -> read::Dwarf<read::EndianSlice<'_, E>> { + read::Dwarf::load(|section_id| -> read::Result<_> { + Ok(read::EndianSlice::new( + self.get(section_id).map(|w| w.slice()).unwrap_or_default(), + endian, + )) + }) + .unwrap() + } + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/str.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/str.rs new file mode 100644 index 0000000..11ea403 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/str.rs
@@ -0,0 +1,198 @@ +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, $offset: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(); + let mut empty = None; + for bytes in self.strings.iter() { + offsets.push(w.offset()); + w.write(bytes)?; + if empty.is_none() { + empty = Some(w.offset()); + } + w.write_u8(0)?; + } + // Record the offset of the first null, for use as an empty string. + if let Some(empty) = empty { + offsets.push(empty); + } + + Ok($offsets { + base_id: self.base_id, + offsets, + }) + } + } + + impl $offsets { + pub(crate) fn get_empty(&self) -> Option<$id> { + if self.offsets.is_empty() { + None + } else { + // The last offset is always the empty string. + Some($id::new(self.base_id, self.offsets.len() - 1)) + } + } + } + }; +} + +define_id!(StringId, "An identifier for a string in a `StringTable`."); + +define_string_table!( + StringTable, + StringId, + DebugStr, + DebugStrOffset, + 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, + DebugLineStrOffset, + 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.get(offsets.get_empty().unwrap()), DebugStrOffset(3)); + assert_eq!(offsets.count(), 3); + } + + #[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(); + let str3 = read_debug_str + .get_str(offsets.get(offsets.get_empty().unwrap())) + .unwrap(); + assert_eq!(str1.slice(), &b"one"[..]); + assert_eq!(str2.slice(), &b"two"[..]); + assert_eq!(str3.slice(), b""); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/unit.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/unit.rs new file mode 100644 index 0000000..fa70595 --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/unit.rs
@@ -0,0 +1,3107 @@ +use alloc::vec::Vec; +use std::ops::{Deref, DerefMut}; +use std::slice; + +use crate::common::{ + DebugAbbrevOffset, DebugInfoOffset, DebugLineOffset, DebugMacinfoOffset, DebugMacroOffset, + DebugStrOffset, DebugTypeSignature, Encoding, Format, SectionId, +}; +use crate::constants; +use crate::leb128::write::{sleb128_size, uleb128_size}; +use crate::write::{ + Abbreviation, AbbreviationTable, Address, AttributeSpecification, BaseId, DebugLineStrOffsets, + DebugStrOffsets, Error, Expression, FileId, LineProgram, LineStringId, LocationListId, + LocationListOffsets, LocationListTable, RangeListId, RangeListOffsets, RangeListTable, + Reference, Result, Section, Sections, StringId, Writer, +}; + +define_id!(UnitId, "An identifier for a unit in a `UnitTable`."); + +define_id!(UnitEntryId, "An identifier for an entry in a `Unit`."); + +/// A table of units that will be stored in the `.debug_info` section. +#[derive(Debug, Default)] +pub struct UnitTable { + base_id: BaseId, + units: Vec<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] + } + + /// Get an iterator for the units. + pub fn iter(&self) -> impl Iterator<Item = (UnitId, &Unit)> { + self.units + .iter() + .enumerate() + .map(move |(index, unit)| (UnitId::new(self.base_id, index), unit)) + } + + /// Get a mutable iterator for the units. + pub fn iter_mut(&mut self) -> impl Iterator<Item = (UnitId, &mut Unit)> { + let base_id = self.base_id; + self.units + .iter_mut() + .enumerate() + .map(move |(index, unit)| (UnitId::new(base_id, index), unit)) + } + + /// Write the units to the given sections. + /// + /// `strings` must contain the `.debug_str` offsets of the corresponding + /// `StringTable`. + pub fn write<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) + .ok_or(Error::InvalidReference)? + .0; + debug_assert_ne!(entry_offset, 0); + w.write_offset_at(r.offset, entry_offset, SectionId::DebugInfo, r.size)?; + } + Ok(()) +} + +/// A unit's debugging information. +#[derive(Debug)] +pub struct Unit { + base_id: BaseId, + /// The encoding parameters for this unit. + encoding: Encoding, + /// The line number program for this unit. + pub line_program: LineProgram, + /// A table of range lists used by this unit. + pub ranges: RangeListTable, + /// A table of location lists used by this unit. + pub locations: LocationListTable, + /// All entries in this unit. The order is unrelated to the tree order. + // Requirements: + // - entries form a tree + // - entries can be added in any order + // - entries have a fixed id + // - able to quickly lookup an entry from its id + // Limitations of current implementation: + // - mutable iteration of children is messy due to borrow checker + entries: Vec<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, + 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).ok_or(Error::InvalidReference)?, + 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) -> Result<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)?; + } + Ok(size) + } + + /// Write the entry to the given sections. + 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, + 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), Some(w.offset())); + w.write_uleb128(offsets.abbrev(self.id))?; + + let sibling_offset = if self.sibling && !self.children.is_empty() { + let offset = w.offset(); + w.write_udata(0, unit.format().word_size())?; + Some(offset) + } else { + None + }; + + for attr in &self.attrs { + attr.value.write( + w, + debug_info_refs, + unit_refs, + unit, + offsets, + line_program, + line_strings, + strings, + range_lists, + loc_lists, + )?; + } + + if !self.children.is_empty() { + for child in &self.children { + unit.entries[child.index].write( + w, + debug_info_refs, + unit_refs, + unit, + offsets, + line_program, + line_strings, + strings, + range_lists, + loc_lists, + )?; + } + // Null child + w.write_u8(0)?; + } + + if let Some(offset) = sibling_offset { + let next_offset = (w.offset().0 - offsets.unit.0) as u64; + // This does not need relocation. + w.write_udata_at(offset.0, next_offset, unit.format().word_size())?; + } + Ok(()) + } +} + +/// An attribute in a `DebuggingInformationEntry`, consisting of a name and +/// associated value. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Attribute { + name: constants::DwAt, + value: AttributeValue, +} + +impl Attribute { + /// Get the name of this attribute. + #[inline] + pub fn name(&self) -> constants::DwAt { + self.name + } + + /// Get the value of this attribute. + #[inline] + pub fn get(&self) -> &AttributeValue { + &self.value + } + + /// Set the value of this attribute. + #[inline] + pub fn set(&mut self, value: AttributeValue) { + self.value = value; + } + + /// Return the type specification for this attribute. + fn specification(&self, encoding: Encoding) -> Result<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) -> Result<usize> { + macro_rules! debug_assert_form { + ($form:expr) => { + debug_assert_eq!(self.form(unit.encoding()).unwrap(), $form) + }; + } + Ok(match *self { + AttributeValue::Address(_) => { + debug_assert_form!(constants::DW_FORM_addr); + unit.address_size() as usize + } + AttributeValue::Block(ref val) => { + debug_assert_form!(constants::DW_FORM_block); + uleb128_size(val.len() as u64) + val.len() + } + AttributeValue::Data1(_) => { + debug_assert_form!(constants::DW_FORM_data1); + 1 + } + AttributeValue::Data2(_) => { + debug_assert_form!(constants::DW_FORM_data2); + 2 + } + AttributeValue::Data4(_) => { + debug_assert_form!(constants::DW_FORM_data4); + 4 + } + AttributeValue::Data8(_) => { + debug_assert_form!(constants::DW_FORM_data8); + 8 + } + AttributeValue::Sdata(val) => { + debug_assert_form!(constants::DW_FORM_sdata); + sleb128_size(val) + } + AttributeValue::Udata(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val) + } + AttributeValue::Exprloc(ref val) => { + debug_assert_form!(constants::DW_FORM_exprloc); + let size = val.size(unit.encoding(), Some(offsets))?; + uleb128_size(size as u64) + size + } + AttributeValue::Flag(_) => { + debug_assert_form!(constants::DW_FORM_flag); + 1 + } + AttributeValue::FlagPresent => { + debug_assert_form!(constants::DW_FORM_flag_present); + 0 + } + AttributeValue::UnitRef(_) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref8), + } + unit.format().word_size() as usize + } + AttributeValue::DebugInfoRef(_) => { + debug_assert_form!(constants::DW_FORM_ref_addr); + if unit.version() == 2 { + unit.address_size() as usize + } else { + unit.format().word_size() as usize + } + } + AttributeValue::DebugInfoRefSup(_) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref_sup4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref_sup8), + } + unit.format().word_size() as usize + } + AttributeValue::LineProgramRef => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::LocationListRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugMacinfoRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugMacroRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::RangeListRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugTypesRef(_) => { + debug_assert_form!(constants::DW_FORM_ref_sig8); + 8 + } + AttributeValue::StringRef(_) => { + debug_assert_form!(constants::DW_FORM_strp); + unit.format().word_size() as usize + } + AttributeValue::DebugStrRefSup(_) => { + debug_assert_form!(constants::DW_FORM_strp_sup); + unit.format().word_size() as usize + } + AttributeValue::LineStringRef(_) => { + debug_assert_form!(constants::DW_FORM_line_strp); + unit.format().word_size() as usize + } + AttributeValue::String(ref val) => { + debug_assert_form!(constants::DW_FORM_string); + val.len() + 1 + } + AttributeValue::Encoding(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::DecimalSign(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Endianity(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Accessibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Visibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Virtuality(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Language(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::AddressClass(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0) + } + AttributeValue::IdentifierCase(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::CallingConvention(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Inline(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Ordering(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::FileIndex(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.map(|id| id.raw(unit.version())).unwrap_or(0)) + } + }) + } + + /// Write the attribute value to the given sections. + fn write<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(|id| id.raw(unit.version())).unwrap_or(0))?; + } + } + Ok(()) + } +} + +define_section!( + DebugInfo, + DebugInfoOffset, + "A writable `.debug_info` section." +); + +/// The section offsets of all elements within a `.debug_info` section. +#[derive(Debug, Default)] +pub struct DebugInfoOffsets { + base_id: BaseId, + units: Vec<UnitOffsets>, +} + +impl DebugInfoOffsets { + /// Get the `.debug_info` section offset for the given unit. + #[inline] + pub fn unit(&self, unit: UnitId) -> DebugInfoOffset { + debug_assert_eq!(self.base_id, unit.base_id); + self.units[unit.index].unit + } + + /// Get the `.debug_info` section offset for the given entry. + #[inline] + pub fn entry(&self, unit: UnitId, entry: UnitEntryId) -> Option<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 { + /// Get the `.debug_info` offset for the given entry. + /// + /// Returns `None` if the offset has not been calculated yet. + #[inline] + fn debug_info_offset(&self, entry: UnitEntryId) -> Option<DebugInfoOffset> { + debug_assert_eq!(self.base_id, entry.base_id); + let offset = self.entries[entry.index].offset; + if offset.0 == 0 { + None + } else { + Some(offset) + } + } + + /// Get the unit offset for the given entry. + /// + /// Returns `None` if the offset has not been calculated yet. + /// This may occur if the entry is orphaned or if a reference + /// to the entry occurs before the entry itself is written. + #[inline] + pub(crate) fn unit_offset(&self, entry: UnitEntryId) -> Option<u64> { + self.debug_info_offset(entry) + .map(|offset| (offset.0 - self.unit.0) as u64) + } + + /// Get the abbreviation code for the given entry. + #[inline] + pub(crate) fn abbrev(&self, entry: UnitEntryId) -> u64 { + debug_assert_eq!(self.base_id, entry.base_id); + self.entries[entry.index].abbrev + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct EntryOffset { + offset: DebugInfoOffset, + abbrev: u64, +} + +impl EntryOffset { + fn none() -> Self { + EntryOffset { + offset: DebugInfoOffset(0), + abbrev: 0, + } + } +} + +/// A reference to a `.debug_info` entry that has yet to be resolved. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DebugInfoReference { + /// The offset within the section of the reference. + pub offset: usize, + /// The size of the reference. + pub size: u8, + /// The unit containing the entry. + pub unit: UnitId, + /// The entry being referenced. + pub entry: UnitEntryId, +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::common::{DwoId, UnitSectionOffset}; + use crate::read::{self, Reader}; + use crate::write::{self, ConvertError, ConvertResult, LocationList, RangeList}; + use std::collections::HashMap; + + pub(crate) struct ConvertUnit<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. + 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 from_attr.name() == constants::DW_AT_GNU_locviews { + // This is a GNU extension that is not supported, and is safe to ignore. + // TODO: remove this when we support it. + } 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 && context.unit.encoding().version <= 4 { + AttributeValue::FileIndex(None) + } else { + match context.line_program_files.get(val as usize) { + Some(id) => AttributeValue::FileIndex(Some(*id)), + None => return Err(ConvertError::InvalidFileIndex), + } + } + } + // Should always be a more specific section reference. + read::AttributeValue::SecOffset(_) => { + return Err(ConvertError::InvalidAttributeValue); + } + read::AttributeValue::DwoId(DwoId(val)) => AttributeValue::Udata(val), + }; + Ok(Some(to)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::LineEncoding; + use crate::constants; + use crate::read; + use crate::write::{ + Dwarf, DwarfUnit, EndianVec, LineString, Location, LocationList, Range, RangeList, + }; + use crate::LittleEndian; + use std::mem; + + #[test] + fn test_unit_table() { + let mut dwarf = Dwarf::new(); + let unit_id1 = dwarf.units.add(Unit::new( + Encoding { + version: 4, + address_size: 8, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + let unit2 = dwarf.units.add(Unit::new( + Encoding { + version: 2, + address_size: 4, + format: Format::Dwarf64, + }, + LineProgram::none(), + )); + let unit3 = dwarf.units.add(Unit::new( + Encoding { + version: 5, + address_size: 4, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + assert_eq!(dwarf.units.count(), 3); + { + let unit1 = dwarf.units.get_mut(unit_id1); + assert_eq!(unit1.version(), 4); + assert_eq!(unit1.address_size(), 8); + assert_eq!(unit1.format(), Format::Dwarf32); + assert_eq!(unit1.count(), 1); + + let root_id = unit1.root(); + assert_eq!(root_id, UnitEntryId::new(unit1.base_id, 0)); + { + let root = unit1.get_mut(root_id); + assert_eq!(root.id(), root_id); + assert!(root.parent().is_none()); + assert_eq!(root.tag(), constants::DW_TAG_compile_unit); + + // Test get/get_mut + assert!(root.get(constants::DW_AT_producer).is_none()); + assert!(root.get_mut(constants::DW_AT_producer).is_none()); + let mut producer = AttributeValue::String(b"root"[..].into()); + root.set(constants::DW_AT_producer, producer.clone()); + assert_eq!(root.get(constants::DW_AT_producer), Some(&producer)); + assert_eq!(root.get_mut(constants::DW_AT_producer), Some(&mut producer)); + + // Test attrs + let mut attrs = root.attrs(); + let attr = attrs.next().unwrap(); + assert_eq!(attr.name(), constants::DW_AT_producer); + assert_eq!(attr.get(), &producer); + assert!(attrs.next().is_none()); + } + + let child1 = unit1.add(root_id, constants::DW_TAG_subprogram); + assert_eq!(child1, UnitEntryId::new(unit1.base_id, 1)); + { + let child1 = unit1.get_mut(child1); + assert_eq!(child1.parent(), Some(root_id)); + + let tmp = AttributeValue::String(b"tmp"[..].into()); + child1.set(constants::DW_AT_name, tmp.clone()); + assert_eq!(child1.get(constants::DW_AT_name), Some(&tmp)); + + // Test attrs_mut + let name = AttributeValue::StringRef(dwarf.strings.add(&b"child1"[..])); + { + let attr = child1.attrs_mut().next().unwrap(); + assert_eq!(attr.name(), constants::DW_AT_name); + attr.set(name.clone()); + } + assert_eq!(child1.get(constants::DW_AT_name), Some(&name)); + } + + let child2 = unit1.add(root_id, constants::DW_TAG_subprogram); + assert_eq!(child2, UnitEntryId::new(unit1.base_id, 2)); + { + let child2 = unit1.get_mut(child2); + assert_eq!(child2.parent(), Some(root_id)); + + let tmp = AttributeValue::String(b"tmp"[..].into()); + child2.set(constants::DW_AT_name, tmp.clone()); + assert_eq!(child2.get(constants::DW_AT_name), Some(&tmp)); + + // Test replace + let name = AttributeValue::StringRef(dwarf.strings.add(&b"child2"[..])); + child2.set(constants::DW_AT_name, name.clone()); + assert_eq!(child2.get(constants::DW_AT_name), Some(&name)); + } + + { + let root = unit1.get(root_id); + assert_eq!( + root.children().cloned().collect::<Vec<_>>(), + vec![child1, child2] + ); + } + } + { + let unit2 = dwarf.units.get(unit2); + assert_eq!(unit2.version(), 2); + assert_eq!(unit2.address_size(), 4); + assert_eq!(unit2.format(), Format::Dwarf64); + assert_eq!(unit2.count(), 1); + + let root = unit2.root(); + assert_eq!(root, UnitEntryId::new(unit2.base_id, 0)); + let root = unit2.get(root); + assert_eq!(root.id(), UnitEntryId::new(unit2.base_id, 0)); + assert!(root.parent().is_none()); + assert_eq!(root.tag(), constants::DW_TAG_compile_unit); + } + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + println!("{:?}", sections.debug_str); + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let read_dwarf = sections.read(LittleEndian); + let mut read_units = read_dwarf.units(); + + { + let read_unit1 = read_units.next().unwrap().unwrap(); + let unit1 = dwarf.units.get(unit_id1); + assert_eq!(unit1.version(), read_unit1.version()); + assert_eq!(unit1.address_size(), read_unit1.address_size()); + assert_eq!(unit1.format(), read_unit1.format()); + + let read_unit1 = read_dwarf.unit(read_unit1).unwrap(); + let mut read_entries = read_unit1.entries(); + + let root = unit1.get(unit1.root()); + { + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(read_root.has_children()); + + let producer = match root.get(constants::DW_AT_producer).unwrap() { + AttributeValue::String(ref producer) => &**producer, + otherwise => panic!("unexpected {:?}", otherwise), + }; + assert_eq!(producer, b"root"); + let read_producer = read_root + .attr_value(constants::DW_AT_producer) + .unwrap() + .unwrap(); + assert_eq!( + read_dwarf + .attr_string(&read_unit1, read_producer) + .unwrap() + .slice(), + producer + ); + } + + let mut children = root.children().cloned(); + + { + let child = children.next().unwrap(); + assert_eq!(child, UnitEntryId::new(unit1.base_id, 1)); + let child = unit1.get(child); + let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 1); + assert_eq!(child.tag(), read_child.tag()); + assert!(!read_child.has_children()); + + let name = match child.get(constants::DW_AT_name).unwrap() { + AttributeValue::StringRef(name) => *name, + otherwise => panic!("unexpected {:?}", otherwise), + }; + let name = dwarf.strings.get(name); + assert_eq!(name, b"child1"); + let read_name = read_child + .attr_value(constants::DW_AT_name) + .unwrap() + .unwrap(); + assert_eq!( + read_dwarf + .attr_string(&read_unit1, read_name) + .unwrap() + .slice(), + name + ); + } + + { + let child = children.next().unwrap(); + assert_eq!(child, UnitEntryId::new(unit1.base_id, 2)); + let child = unit1.get(child); + let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(child.tag(), read_child.tag()); + assert!(!read_child.has_children()); + + let name = match child.get(constants::DW_AT_name).unwrap() { + AttributeValue::StringRef(name) => *name, + otherwise => panic!("unexpected {:?}", otherwise), + }; + let name = dwarf.strings.get(name); + assert_eq!(name, b"child2"); + let read_name = read_child + .attr_value(constants::DW_AT_name) + .unwrap() + .unwrap(); + assert_eq!( + read_dwarf + .attr_string(&read_unit1, read_name) + .unwrap() + .slice(), + name + ); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + { + let read_unit2 = read_units.next().unwrap().unwrap(); + let unit2 = dwarf.units.get(unit2); + assert_eq!(unit2.version(), read_unit2.version()); + assert_eq!(unit2.address_size(), read_unit2.address_size()); + assert_eq!(unit2.format(), read_unit2.format()); + + let abbrevs = read_dwarf.abbreviations(&read_unit2).unwrap(); + let mut read_entries = read_unit2.entries(&abbrevs); + + { + let root = unit2.get(unit2.root()); + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(!read_root.has_children()); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + { + let read_unit3 = read_units.next().unwrap().unwrap(); + let unit3 = dwarf.units.get(unit3); + assert_eq!(unit3.version(), read_unit3.version()); + assert_eq!(unit3.address_size(), read_unit3.address_size()); + assert_eq!(unit3.format(), read_unit3.format()); + + let abbrevs = read_dwarf.abbreviations(&read_unit3).unwrap(); + let mut read_entries = read_unit3.entries(&abbrevs); + + { + let root = unit3.get(unit3.root()); + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(!read_root.has_children()); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + assert!(read_units.next().unwrap().is_none()); + + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap(); + assert_eq!(convert_dwarf.units.count(), dwarf.units.count()); + + for i in 0..convert_dwarf.units.count() { + let unit_id = dwarf.units.id(i); + let unit = dwarf.units.get(unit_id); + let convert_unit_id = convert_dwarf.units.id(i); + let convert_unit = convert_dwarf.units.get(convert_unit_id); + assert_eq!(convert_unit.version(), unit.version()); + assert_eq!(convert_unit.address_size(), unit.address_size()); + assert_eq!(convert_unit.format(), unit.format()); + assert_eq!(convert_unit.count(), unit.count()); + + let root = unit.get(unit.root()); + let convert_root = convert_unit.get(convert_unit.root()); + assert_eq!(convert_root.tag(), root.tag()); + for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { + assert_eq!(convert_attr, attr); + } + } + } + + #[test] + fn test_attribute_value() { + let string_data = "string data"; + let line_string_data = "line string data"; + + let data = vec![1, 2, 3, 4]; + let read_data = read::EndianSlice::new(&[1, 2, 3, 4], LittleEndian); + + let mut expression = Expression::new(); + expression.op_constu(57); + let read_expression = read::Expression(read::EndianSlice::new( + &[constants::DW_OP_constu.0, 57], + LittleEndian, + )); + + let range = RangeList(vec![Range::StartEnd { + begin: Address::Constant(0x1234), + end: Address::Constant(0x2345), + }]); + + let location = LocationList(vec![Location::StartEnd { + begin: Address::Constant(0x1234), + end: Address::Constant(0x2345), + data: expression.clone(), + }]); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut dwarf = Dwarf::new(); + let unit = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit); + let loc_id = unit.locations.add(location.clone()); + let range_id = unit.ranges.add(range.clone()); + // Create a string with a non-zero id/offset. + dwarf.strings.add("dummy string"); + let string_id = dwarf.strings.add(string_data); + dwarf.line_strings.add("dummy line string"); + let line_string_id = dwarf.line_strings.add(line_string_data); + + let attributes = &[ + ( + constants::DW_AT_name, + AttributeValue::Address(Address::Constant(0x1234)), + read::AttributeValue::Addr(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Block(data.clone()), + read::AttributeValue::Block(read_data), + ), + ( + constants::DW_AT_name, + AttributeValue::Data1(0x12), + read::AttributeValue::Data1(0x12), + ), + ( + constants::DW_AT_name, + AttributeValue::Data2(0x1234), + read::AttributeValue::Data2(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Data4(0x1234), + read::AttributeValue::Data4(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Data8(0x1234), + read::AttributeValue::Data8(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Sdata(0x1234), + read::AttributeValue::Sdata(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Udata(0x1234), + read::AttributeValue::Udata(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Exprloc(expression.clone()), + read::AttributeValue::Exprloc(read_expression), + ), + ( + constants::DW_AT_name, + AttributeValue::Flag(false), + read::AttributeValue::Flag(false), + ), + /* + ( + constants::DW_AT_name, + AttributeValue::FlagPresent, + read::AttributeValue::Flag(true), + ), + */ + ( + constants::DW_AT_name, + AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), + read::AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), + ), + ( + constants::DW_AT_macro_info, + AttributeValue::DebugMacinfoRef(DebugMacinfoOffset(0x1234)), + read::AttributeValue::SecOffset(0x1234), + ), + ( + constants::DW_AT_macros, + AttributeValue::DebugMacroRef(DebugMacroOffset(0x1234)), + read::AttributeValue::SecOffset(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), + read::AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), + ), + ( + constants::DW_AT_name, + AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), + read::AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), + ), + ( + constants::DW_AT_name, + AttributeValue::String(data.clone()), + read::AttributeValue::String(read_data), + ), + ( + constants::DW_AT_encoding, + AttributeValue::Encoding(constants::DwAte(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_decimal_sign, + AttributeValue::DecimalSign(constants::DwDs(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_endianity, + AttributeValue::Endianity(constants::DwEnd(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_accessibility, + AttributeValue::Accessibility(constants::DwAccess(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_visibility, + AttributeValue::Visibility(constants::DwVis(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_virtuality, + AttributeValue::Virtuality(constants::DwVirtuality(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_language, + AttributeValue::Language(constants::DwLang(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_address_class, + AttributeValue::AddressClass(constants::DwAddr(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_identifier_case, + AttributeValue::IdentifierCase(constants::DwId(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_calling_convention, + AttributeValue::CallingConvention(constants::DwCc(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_ordering, + AttributeValue::Ordering(constants::DwOrd(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_inline, + AttributeValue::Inline(constants::DwInl(0x12)), + read::AttributeValue::Udata(0x12), + ), + ]; + + let mut add_attribute = |name, value| { + let entry_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + let entry = unit.get_mut(entry_id); + entry.set(name, value); + }; + for (name, value, _) in attributes { + add_attribute(*name, value.clone()); + } + add_attribute( + constants::DW_AT_location, + AttributeValue::LocationListRef(loc_id), + ); + add_attribute( + constants::DW_AT_ranges, + AttributeValue::RangeListRef(range_id), + ); + add_attribute(constants::DW_AT_name, AttributeValue::StringRef(string_id)); + add_attribute( + constants::DW_AT_name, + AttributeValue::LineStringRef(line_string_id), + ); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let mut read_units = read_dwarf.units(); + let read_unit = read_units.next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit).unwrap(); + let read_unit = read_unit.unit_ref(&read_dwarf); + let mut read_entries = read_unit.entries(); + let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); + + let mut get_attribute = |name| { + let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); + entry.attr(name).unwrap().unwrap() + }; + for (name, _, expect_value) in attributes { + let read_value = &get_attribute(*name).raw_value(); + // read::AttributeValue is invariant in the lifetime of R. + // The lifetimes here are all okay, so transmute it. + let read_value = unsafe { + mem::transmute::< + &read::AttributeValue<read::EndianSlice<'_, LittleEndian>>, + &read::AttributeValue<read::EndianSlice<'_, LittleEndian>>, + >(read_value) + }; + assert_eq!(read_value, expect_value); + } + + let read_attr = get_attribute(constants::DW_AT_location).value(); + let read::AttributeValue::LocationListsRef(read_loc_offset) = read_attr else { + panic!("unexpected {:?}", read_attr); + }; + let mut read_locations = read_unit.locations(read_loc_offset).unwrap(); + let read_location = read_locations.next().unwrap().unwrap(); + assert_eq!(read_location.range.begin, 0x1234); + assert_eq!(read_location.range.end, 0x2345); + assert_eq!(read_location.data, read_expression); + + let read_attr = get_attribute(constants::DW_AT_ranges).value(); + let read::AttributeValue::RangeListsRef(read_range_offset) = read_attr else { + panic!("unexpected {:?}", read_attr); + }; + let read_range_offset = read_unit.ranges_offset_from_raw(read_range_offset); + let mut read_ranges = read_unit.ranges(read_range_offset).unwrap(); + let read_range = read_ranges.next().unwrap().unwrap(); + assert_eq!(read_range.begin, 0x1234); + assert_eq!(read_range.end, 0x2345); + + let read_string = get_attribute(constants::DW_AT_name).raw_value(); + let read::AttributeValue::DebugStrRef(read_string_offset) = read_string else { + panic!("unexpected {:?}", read_string); + }; + assert_eq!( + read_dwarf.string(read_string_offset).unwrap().slice(), + string_data.as_bytes() + ); + + let read_line_string = get_attribute(constants::DW_AT_name).raw_value(); + let read::AttributeValue::DebugLineStrRef(read_line_string_offset) = + read_line_string + else { + panic!("unexpected {:?}", read_line_string); + }; + assert_eq!( + read_dwarf + .line_string(read_line_string_offset) + .unwrap() + .slice(), + line_string_data.as_bytes() + ); + + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) + .unwrap(); + let convert_unit = convert_dwarf.units.get(convert_dwarf.units.id(0)); + let convert_root = convert_unit.get(convert_unit.root()); + let mut convert_entries = convert_root.children(); + + let mut get_convert_attr = |name| { + let convert_entry = convert_unit.get(*convert_entries.next().unwrap()); + convert_entry.get(name).unwrap() + }; + for (name, attr, _) in attributes { + let convert_attr = get_convert_attr(*name); + assert_eq!(convert_attr, attr); + } + + let convert_attr = get_convert_attr(constants::DW_AT_location); + let AttributeValue::LocationListRef(convert_loc_id) = convert_attr else { + panic!("unexpected {:?}", convert_attr); + }; + let convert_location = convert_unit.locations.get(*convert_loc_id); + assert_eq!(*convert_location, location); + + let convert_attr = get_convert_attr(constants::DW_AT_ranges); + let AttributeValue::RangeListRef(convert_range_id) = convert_attr else { + panic!("unexpected {:?}", convert_attr); + }; + let convert_range = convert_unit.ranges.get(*convert_range_id); + assert_eq!(*convert_range, range); + + let convert_attr = get_convert_attr(constants::DW_AT_name); + let AttributeValue::StringRef(convert_string_id) = convert_attr else { + panic!("unexpected {:?}", convert_attr); + }; + let convert_string = convert_dwarf.strings.get(*convert_string_id); + assert_eq!(convert_string, string_data.as_bytes()); + + let convert_attr = get_convert_attr(constants::DW_AT_name); + let AttributeValue::LineStringRef(convert_line_string_id) = convert_attr else { + panic!("unexpected {:?}", convert_attr); + }; + let convert_line_string = + convert_dwarf.line_strings.get(*convert_line_string_id); + assert_eq!(convert_line_string, line_string_data.as_bytes()); + } + } + } + } + + #[test] + fn test_unit_ref() { + let mut dwarf = Dwarf::new(); + let unit_id1 = dwarf.units.add(Unit::new( + Encoding { + version: 4, + address_size: 8, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + assert_eq!(unit_id1, dwarf.units.id(0)); + let unit_id2 = dwarf.units.add(Unit::new( + Encoding { + version: 2, + address_size: 4, + format: Format::Dwarf64, + }, + LineProgram::none(), + )); + assert_eq!(unit_id2, dwarf.units.id(1)); + let unit1_child1 = UnitEntryId::new(dwarf.units.get(unit_id1).base_id, 1); + let unit1_child2 = UnitEntryId::new(dwarf.units.get(unit_id1).base_id, 2); + let unit2_child1 = UnitEntryId::new(dwarf.units.get(unit_id2).base_id, 1); + let unit2_child2 = UnitEntryId::new(dwarf.units.get(unit_id2).base_id, 2); + { + let unit1 = dwarf.units.get_mut(unit_id1); + let root = unit1.root(); + let child_id1 = unit1.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id1, unit1_child1); + let child_id2 = unit1.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id2, unit1_child2); + { + let child1 = unit1.get_mut(child_id1); + child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); + } + { + let child2 = unit1.get_mut(child_id2); + child2.set( + constants::DW_AT_type, + AttributeValue::DebugInfoRef(Reference::Entry(unit_id2, unit2_child1)), + ); + } + } + { + let unit2 = dwarf.units.get_mut(unit_id2); + let root = unit2.root(); + let child_id1 = unit2.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id1, unit2_child1); + let child_id2 = unit2.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id2, unit2_child2); + { + let child1 = unit2.get_mut(child_id1); + child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); + } + { + let child2 = unit2.get_mut(child_id2); + child2.set( + constants::DW_AT_type, + AttributeValue::DebugInfoRef(Reference::Entry(unit_id1, unit1_child1)), + ); + } + } + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let read_dwarf = sections.read(LittleEndian); + let mut read_units = read_dwarf.units(); + + let read_unit = read_units.next().unwrap().unwrap(); + let abbrevs = read_dwarf.abbreviations(&read_unit).unwrap(); + let mut read_entries = read_unit.entries(&abbrevs); + let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); + let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); + let read_unit1_child1_attr = entry.attr_value(constants::DW_AT_type).unwrap(); + let read_unit1_child1_section_offset = + entry.offset().to_debug_info_offset(&read_unit).unwrap(); + let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); + let read_unit1_child2_attr = entry.attr_value(constants::DW_AT_type).unwrap(); + let read_unit1_child2_offset = entry.offset(); + + let read_unit = read_units.next().unwrap().unwrap(); + let abbrevs = read_dwarf.abbreviations(&read_unit).unwrap(); + let mut read_entries = read_unit.entries(&abbrevs); + let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); + let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); + let read_unit2_child1_attr = entry.attr_value(constants::DW_AT_type).unwrap(); + let read_unit2_child1_section_offset = + entry.offset().to_debug_info_offset(&read_unit).unwrap(); + let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); + let read_unit2_child2_attr = entry.attr_value(constants::DW_AT_type).unwrap(); + let read_unit2_child2_offset = entry.offset(); + + assert_eq!( + read_unit1_child1_attr, + Some(read::AttributeValue::UnitRef(read_unit1_child2_offset)) + ); + assert_eq!( + read_unit1_child2_attr, + Some(read::AttributeValue::DebugInfoRef( + read_unit2_child1_section_offset + )) + ); + assert_eq!( + read_unit2_child1_attr, + Some(read::AttributeValue::UnitRef(read_unit2_child2_offset)) + ); + assert_eq!( + read_unit2_child2_attr, + Some(read::AttributeValue::DebugInfoRef( + read_unit1_child1_section_offset + )) + ); + + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap(); + let convert_units = &convert_dwarf.units; + assert_eq!(convert_units.count(), dwarf.units.count()); + + for i in 0..convert_units.count() { + let unit = dwarf.units.get(dwarf.units.id(i)); + let convert_unit = convert_units.get(convert_units.id(i)); + assert_eq!(convert_unit.version(), unit.version()); + assert_eq!(convert_unit.address_size(), unit.address_size()); + assert_eq!(convert_unit.format(), unit.format()); + assert_eq!(convert_unit.count(), unit.count()); + + let root = unit.get(unit.root()); + let convert_root = convert_unit.get(convert_unit.root()); + assert_eq!(convert_root.tag(), root.tag()); + for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { + assert_eq!(convert_attr, attr); + } + + let child1 = unit.get(UnitEntryId::new(unit.base_id, 1)); + let convert_child1 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 1)); + assert_eq!(convert_child1.tag(), child1.tag()); + for (convert_attr, attr) in convert_child1.attrs().zip(child1.attrs()) { + assert_eq!(convert_attr.name, attr.name); + match (convert_attr.value.clone(), attr.value.clone()) { + ( + AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), + AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), + ) => { + assert_eq!(convert_unit.index, unit.index); + assert_eq!(convert_entry.index, entry.index); + } + (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { + assert_eq!(convert_id.index, id.index); + } + (convert_value, value) => assert_eq!(convert_value, value), + } + } + + let child2 = unit.get(UnitEntryId::new(unit.base_id, 2)); + let convert_child2 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 2)); + assert_eq!(convert_child2.tag(), child2.tag()); + for (convert_attr, attr) in convert_child2.attrs().zip(child2.attrs()) { + assert_eq!(convert_attr.name, attr.name); + match (convert_attr.value.clone(), attr.value.clone()) { + ( + AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), + AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), + ) => { + assert_eq!(convert_unit.index, unit.index); + assert_eq!(convert_entry.index, entry.index); + } + (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { + assert_eq!(convert_id.index, id.index); + } + (convert_value, value) => assert_eq!(convert_value, value), + } + } + } + } + + #[test] + fn test_sibling() { + fn add_child( + unit: &mut Unit, + parent: UnitEntryId, + tag: constants::DwTag, + name: &str, + ) -> UnitEntryId { + let id = unit.add(parent, tag); + let child = unit.get_mut(id); + child.set(constants::DW_AT_name, AttributeValue::String(name.into())); + child.set_sibling(true); + id + } + + fn add_children(unit: &mut Unit) { + let root = unit.root(); + let child1 = add_child(unit, root, constants::DW_TAG_subprogram, "child1"); + add_child(unit, child1, constants::DW_TAG_variable, "grandchild1"); + add_child(unit, root, constants::DW_TAG_subprogram, "child2"); + add_child(unit, root, constants::DW_TAG_subprogram, "child3"); + } + + fn next_child<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>, + dwarf: &read::Dwarf<R>, + ) { + let unit = dwarf.unit(unit).unwrap(); + let mut entries = unit.entries(); + // root + entries.next_dfs().unwrap().unwrap(); + // child1 + let (_, sibling1) = next_child(&mut entries); + // grandchild1 + entries.next_dfs().unwrap().unwrap(); + // child2 + let (offset2, sibling2) = next_child(&mut entries); + // child3 + let (_, _) = next_child(&mut entries); + assert_eq!(sibling1, Some(offset2)); + assert_eq!(sibling2, None); + } + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut dwarf = Dwarf::new(); + let unit_id1 = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + add_children(dwarf.units.get_mut(unit_id1)); + let unit_id2 = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + add_children(dwarf.units.get_mut(unit_id2)); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let read_dwarf = sections.read(LittleEndian); + let mut read_units = read_dwarf.units(); + check_sibling(read_units.next().unwrap().unwrap(), &read_dwarf); + check_sibling(read_units.next().unwrap().unwrap(), &read_dwarf); + } + + #[test] + fn test_line_ref() { + let dir_bytes = b"dir"; + let file_bytes1 = b"file1"; + let file_bytes2 = b"file2"; + let file_string1 = LineString::String(file_bytes1.to_vec()); + let file_string2 = LineString::String(file_bytes2.to_vec()); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + // The line program we'll be referencing. + let mut line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(dir_bytes.to_vec()), + None, + file_string1.clone(), + None, + ); + let dir = line_program.default_directory(); + // For version >= 5, this will reuse the existing file at index 0. + let file1 = line_program.add_file(file_string1.clone(), dir, None); + let file2 = line_program.add_file(file_string2.clone(), dir, None); + + let mut unit = Unit::new(encoding, line_program); + let root = unit.get_mut(unit.root()); + root.set( + constants::DW_AT_name, + AttributeValue::String(file_bytes1.to_vec()), + ); + root.set( + constants::DW_AT_comp_dir, + AttributeValue::String(dir_bytes.to_vec()), + ); + root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + + let child = unit.add(unit.root(), constants::DW_TAG_subprogram); + unit.get_mut(child).set( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file1)), + ); + + let child = unit.add(unit.root(), constants::DW_TAG_subprogram); + unit.get_mut(child).set( + constants::DW_AT_call_file, + AttributeValue::FileIndex(Some(file2)), + ); + + let mut dwarf = Dwarf::new(); + dwarf.units.add(unit); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let mut read_units = read_dwarf.units(); + let read_unit = read_units.next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit).unwrap(); + let read_unit = read_unit.unit_ref(&read_dwarf); + let read_line_program = read_unit.line_program.as_ref().unwrap().header(); + let mut read_entries = read_unit.entries(); + let (_, _root) = read_entries.next_dfs().unwrap().unwrap(); + + let mut get_path = |name| { + let (_, entry) = read_entries.next_dfs().unwrap().unwrap(); + let read_attr = entry.attr(name).unwrap().unwrap(); + let read::AttributeValue::FileIndex(read_file_index) = read_attr.value() + else { + panic!("unexpected {:?}", read_attr); + }; + let read_file = read_line_program.file(read_file_index).unwrap(); + let read_path = read_unit + .attr_string(read_file.path_name()) + .unwrap() + .slice(); + (read_file_index, read_path) + }; + + let (read_index, read_path) = get_path(constants::DW_AT_decl_file); + assert_eq!(read_index, if version >= 5 { 0 } else { 1 }); + assert_eq!(read_path, file_bytes1); + + let (read_index, read_path) = get_path(constants::DW_AT_call_file); + assert_eq!(read_index, if version >= 5 { 1 } else { 2 }); + assert_eq!(read_path, file_bytes2); + + let convert_dwarf = + Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) + .unwrap(); + let convert_unit = convert_dwarf.units.get(convert_dwarf.units.id(0)); + let convert_root = convert_unit.get(convert_unit.root()); + let mut convert_entries = convert_root.children(); + + let mut get_convert_path = |name| { + let convert_entry = convert_unit.get(*convert_entries.next().unwrap()); + let convert_attr = convert_entry.get(name).unwrap(); + let AttributeValue::FileIndex(Some(convert_file_index)) = convert_attr + else { + panic!("unexpected {:?}", convert_attr); + }; + convert_unit.line_program.get_file(*convert_file_index).0 + }; + + let convert_path = get_convert_path(constants::DW_AT_decl_file); + assert_eq!(convert_path, &file_string1); + + let convert_path = get_convert_path(constants::DW_AT_call_file); + assert_eq!(convert_path, &file_string2); + } + } + } + } + + #[test] + fn test_line_program_used() { + for used in [false, true] { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"comp_dir".to_vec()), + None, + LineString::String(b"comp_name".to_vec()), + None, + ); + + let mut unit = Unit::new(encoding, line_program); + let file_id = if used { Some(FileId::new(0)) } else { None }; + let root = unit.root(); + unit.get_mut(root).set( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(file_id), + ); + + let mut dwarf = Dwarf::new(); + dwarf.units.add(unit); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + dwarf.write(&mut sections).unwrap(); + assert_eq!(!used, sections.debug_line.slice().is_empty()); + } + } + + #[test] + fn test_delete_child() { + fn set_name(unit: &mut Unit, id: UnitEntryId, name: &str) { + let entry = unit.get_mut(id); + entry.set(constants::DW_AT_name, AttributeValue::String(name.into())); + } + fn check_name<R: read::Reader>( + entry: &read::DebuggingInformationEntry<'_, '_, R>, + unit: read::UnitRef<'_, R>, + name: &str, + ) { + let name_attr = entry.attr(constants::DW_AT_name).unwrap().unwrap(); + let entry_name = unit.attr_string(name_attr.value()).unwrap(); + let entry_name_str = entry_name.to_string().unwrap(); + assert_eq!(entry_name_str, name); + } + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut dwarf = DwarfUnit::new(encoding); + let root = dwarf.unit.root(); + + // Add and delete entries in the root unit + let child1 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child1, "child1"); + let grandchild1 = dwarf.unit.add(child1, constants::DW_TAG_variable); + set_name(&mut dwarf.unit, grandchild1, "grandchild1"); + let child2 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child2, "child2"); + // This deletes both `child1` and its child `grandchild1` + dwarf.unit.get_mut(root).delete_child(child1); + let child3 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child3, "child3"); + let child4 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child4, "child4"); + let grandchild4 = dwarf.unit.add(child4, constants::DW_TAG_variable); + set_name(&mut dwarf.unit, grandchild4, "grandchild4"); + dwarf.unit.get_mut(child4).delete_child(grandchild4); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + + // Write DWARF data which should only include `child2`, `child3` and `child4` + dwarf.write(&mut sections).unwrap(); + + let read_dwarf = sections.read(LittleEndian); + let read_unit = read_dwarf.units().next().unwrap().unwrap(); + let read_unit = read_dwarf.unit(read_unit).unwrap(); + let read_unit = read_unit.unit_ref(&read_dwarf); + let mut entries = read_unit.entries(); + // root + entries.next_dfs().unwrap().unwrap(); + // child2 + let (_, read_child2) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child2, read_unit, "child2"); + // child3 + let (_, read_child3) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child3, read_unit, "child3"); + // child4 + let (_, read_child4) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child4, read_unit, "child4"); + // There should be no more entries + assert!(entries.next_dfs().unwrap().is_none()); + } + + #[test] + fn test_missing_unit_ref() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let mut dwarf = Dwarf::new(); + let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit_id); + + // Create the entry to be referenced. + let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); + // And delete it so that it is not available when writing. + unit.get_mut(unit.root()).delete_child(entry_id); + + // Create a reference to the deleted entry. + let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + unit.get_mut(subprogram_id) + .set(constants::DW_AT_type, AttributeValue::UnitRef(entry_id)); + + // Writing the DWARF should fail. + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + assert_eq!(dwarf.write(&mut sections), Err(Error::InvalidReference)); + } + + #[test] + fn test_missing_debuginfo_ref() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let mut dwarf = Dwarf::new(); + let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); + let unit = dwarf.units.get_mut(unit_id); + + // Create the entry to be referenced. + let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); + // And delete it so that it is not available when writing. + unit.get_mut(unit.root()).delete_child(entry_id); + + // Create a reference to the deleted entry. + let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); + unit.get_mut(subprogram_id).set( + constants::DW_AT_type, + AttributeValue::DebugInfoRef(Reference::Entry(unit_id, entry_id)), + ); + + // Writing the DWARF should fail. + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + assert_eq!(dwarf.write(&mut sections), Err(Error::InvalidReference)); + } +}
diff --git a/chromium_crates_io/vendor/gimli-v0_32/src/write/writer.rs b/chromium_crates_io/vendor/gimli-v0_32/src/write/writer.rs new file mode 100644 index 0000000..9ee071b --- /dev/null +++ b/chromium_crates_io/vendor/gimli-v0_32/src/write/writer.rs
@@ -0,0 +1,493 @@ +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), + _ => 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}; + + #[test] + fn test_writer() { + let mut w = write::EndianVec::new(LittleEndian); + w.write_address(Address::Constant(0x1122_3344), 4).unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + assert_eq!( + w.write_address( + Address::Symbol { + symbol: 0, + addend: 0 + }, + 4 + ), + Err(Error::InvalidAddress) + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_offset(0x1122_3344, SectionId::DebugInfo, 4) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + w.write_offset_at(1, 0x5566, SectionId::DebugInfo, 2) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x66, 0x55, 0x11]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_u8(0x11).unwrap(); + w.write_u16(0x2233).unwrap(); + w.write_u32(0x4455_6677).unwrap(); + w.write_u64(0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x33, 0x22, + 0x77, 0x66, 0x55, 0x44, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + ]); + w.write_u8_at(14, 0x11).unwrap(); + w.write_u16_at(12, 0x2233).unwrap(); + w.write_u32_at(8, 0x4455_6677).unwrap(); + w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, + 0x11, + ]); + + let mut w = write::EndianVec::new(BigEndian); + w.write_u8(0x11).unwrap(); + w.write_u16(0x2233).unwrap(); + w.write_u32(0x4455_6677).unwrap(); + w.write_u64(0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x22, 0x33, + 0x44, 0x55, 0x66, 0x77, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + ]); + w.write_u8_at(14, 0x11).unwrap(); + w.write_u16_at(12, 0x2233).unwrap(); + w.write_u32_at(8, 0x4455_6677).unwrap(); + w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x44, 0x55, 0x66, 0x77, + 0x22, 0x33, + 0x11, + ]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_udata(0x11, 1).unwrap(); + w.write_udata(0x2233, 2).unwrap(); + w.write_udata(0x4455_6677, 4).unwrap(); + w.write_udata(0x8081_8283_8485_8687, 8).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x33, 0x22, + 0x77, 0x66, 0x55, 0x44, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + ]); + assert_eq!(w.write_udata(0x100, 1), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x1_0000, 2), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x1_0000_0000, 4), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x00, 3), Err(Error::UnsupportedWordSize(3))); + w.write_udata_at(14, 0x11, 1).unwrap(); + w.write_udata_at(12, 0x2233, 2).unwrap(); + w.write_udata_at(8, 0x4455_6677, 4).unwrap(); + w.write_udata_at(0, 0x8081_8283_8485_8687, 8).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, + 0x11, + ]); + assert_eq!(w.write_udata_at(0, 0x100, 1), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata_at(0, 0x1_0000, 2), Err(Error::ValueTooLarge)); + assert_eq!( + w.write_udata_at(0, 0x1_0000_0000, 4), + Err(Error::ValueTooLarge) + ); + assert_eq!( + w.write_udata_at(0, 0x00, 3), + Err(Error::UnsupportedWordSize(3)) + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_uleb128(0).unwrap(); + assert_eq!(w.slice(), &[0]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_uleb128(u64::MAX).unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1] + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(0).unwrap(); + assert_eq!(w.slice(), &[0]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(i64::MAX).unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0] + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(i64::MIN).unwrap(); + assert_eq!( + w.slice(), + &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f] + ); + + let mut w = write::EndianVec::new(LittleEndian); + let offset = w.write_initial_length(Format::Dwarf32).unwrap(); + assert_eq!(w.slice(), &[0, 0, 0, 0]); + w.write_initial_length_at(offset, 0x1122_3344, Format::Dwarf32) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + assert_eq!( + w.write_initial_length_at(offset, 0x1_0000_0000, Format::Dwarf32), + Err(Error::ValueTooLarge) + ); + + let mut w = write::EndianVec::new(LittleEndian); + let offset = w.write_initial_length(Format::Dwarf64).unwrap(); + assert_eq!(w.slice(), &[0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0]); + w.write_initial_length_at(offset, 0x1122_3344_5566_7788, Format::Dwarf64) + .unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11] + ); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/.cargo-checksum.json b/chromium_crates_io/vendor/object-v0_37/.cargo-checksum.json new file mode 100644 index 0000000..697c9ce --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/.cargo-checksum.json
@@ -0,0 +1 @@ +{"files":{}}
diff --git a/chromium_crates_io/vendor/object-v0_37/.cargo_vcs_info.json b/chromium_crates_io/vendor/object-v0_37/.cargo_vcs_info.json new file mode 100644 index 0000000..bad791e --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "916c47b90e5c0bea139ca4bdfc53811f7d2c3383" + }, + "path_in_vcs": "" +} \ No newline at end of file
diff --git a/chromium_crates_io/vendor/object-v0_37/CHANGELOG.md b/chromium_crates_io/vendor/object-v0_37/CHANGELOG.md new file mode 100644 index 0000000..a45ea9b --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/CHANGELOG.md
@@ -0,0 +1,1252 @@ +# `object` Change Log + +-------------------------------------------------------------------------------- + +## 0.37.3 + +Released 2025/08/13. + +### Changed + +* Fixed MSVC weak extern symbol support in `write::Object` by using + `IMAGE_WEAK_EXTERN_SEARCH_ALIAS`. + [#803](https://github.com/gimli-rs/object/pull/803) + +### Added + +* Added `elf::SHT_GNU_SFRAME` and `elf::PT_GNU_SFRAME`. + [#799](https://github.com/gimli-rs/object/pull/799) + +* Added `section_flags_mut` and `symbol_flags_mut` to `write::Object`. + [#801](https://github.com/gimli-rs/object/pull/801) + +-------------------------------------------------------------------------------- + +## 0.37.2 + +Released 2025/08/01. + +### Added + +* Added `elf::EF_RISCV_RV64ILP32`. + [#779](https://github.com/gimli-rs/object/pull/779) + +* Added `pe::IMAGE_FILE_MACHINE_POWERPCBE` and associated read support. + [#783](https://github.com/gimli-rs/object/pull/783) + +* Added PowerPC support to `write::coff`. + [#795](https://github.com/gimli-rs/object/pull/795) + +* Added support for COFF auxiliary weak external symbols to `write::Object` and + `write::coff::Writer`. + [#791](https://github.com/gimli-rs/object/pull/791) + +* Added methods to `write::Object` to obtain default section and symbol flags. + [#789](https://github.com/gimli-rs/object/pull/789) + +* Added compact relocation support to `read::elf`. + [#782](https://github.com/gimli-rs/object/pull/782) + [#784](https://github.com/gimli-rs/object/pull/784) + [#785](https://github.com/gimli-rs/object/pull/785) + [#788](https://github.com/gimli-rs/object/pull/788) + +* Added `Architecture::Alpha`. + [#790](https://github.com/gimli-rs/object/pull/790) + +* Added `Architecture::Hppa`. + [#793](https://github.com/gimli-rs/object/pull/793) + +### Changed + +* Updated `wasmparser` dependency. + +* Changed `write::Object` to accept undefined symbols of unknown kind for COFF. + [#795](https://github.com/gimli-rs/object/pull/795) + +-------------------------------------------------------------------------------- + +## 0.37.1 + +Released 2025/06/11. + +### Changed + +* Removed `compiler-builtins` from `rustc-dep-of-std` dependencies. + [#777](https://github.com/gimli-rs/object/pull/777) + +* Updated `wasmparser` dependency. + +-------------------------------------------------------------------------------- + +## 0.37.0 + +Released 2025/06/02. + +### Breaking changes + +* Changed dyld cache definitions and API to support iterating mapping and slide information. + [#738](https://github.com/gimli-rs/object/pull/738) + [#753](https://github.com/gimli-rs/object/pull/753) + [#754](https://github.com/gimli-rs/object/pull/754) + [#775](https://github.com/gimli-rs/object/pull/775) + +* Removed `elf::R_RISCV_GNU_VTINHERIT` and `elf::R_RISCV_GNU_VTENTRY`. + [#767](https://github.com/gimli-rs/object/pull/767) + +* Changed the type of `pe::IMAGE_WEAK_EXTERN_*` constants. + [#770](https://github.com/gimli-rs/object/pull/770) + +### Added + +* Added support for generating `ARM_RELOC_VANILLA` in `write::Object`. + [#757](https://github.com/gimli-rs/object/pull/757) + +* Added `size_hint` for `read::archive::ArchiveSymbolIterator`. + [#759](https://github.com/gimli-rs/object/pull/759) + +* Added `Architecture::SuperH`. + [#762](https://github.com/gimli-rs/object/pull/762) + +* Added `Architecture::LoongArch32`. + [#765](https://github.com/gimli-rs/object/pull/765) + +* Added support for Wasm object files to `read::WasmFile`. + [#766](https://github.com/gimli-rs/object/pull/766) + +* Added `elf::R_RISCV_TLSDESC` and `elf::R_RISCV_GOT32_PCREL`. + [#767](https://github.com/gimli-rs/object/pull/767) + [#768](https://github.com/gimli-rs/object/pull/768) + +* Added `read::pe::SymbolTable::aux_weak_external` and `read::pe::SymbolTable::has_aux_weak_external`. + [#770](https://github.com/gimli-rs/object/pull/770) + +* Added ELF relocations for LoongArch ABI v2.30. + [#773](https://github.com/gimli-rs/object/pull/773) + +### Changed + +* Changed `ReadRef::read_bytes_at` to allow zero size reads at any offset. + This allows reading of empty sections in stripped ELF files. + [#758](https://github.com/gimli-rs/object/pull/758) + +* Changed `read::MachOFile::object_map` to include static symbols. + [#764](https://github.com/gimli-rs/object/pull/764) + +* Fixed `read::pe::SymbolTable::has_aux_function` to exclude weak externals. + [#772](https://github.com/gimli-rs/object/pull/772) + +* Updated `wasmparser` and `ruzstd` dependencies. + +-------------------------------------------------------------------------------- + +## 0.36.7 + +Released 2024/12/21. + +### Changed + +* Included `build.rs` in package. + +-------------------------------------------------------------------------------- + +## 0.36.6 + +Released 2024/12/21. + +### Added + +* Added `Architecuture::M68k`. + [#742](https://github.com/gimli-rs/object/pull/742) + [#749](https://github.com/gimli-rs/object/pull/749) + +* Added `Architecuture::Mips64_N32`. + [#743](https://github.com/gimli-rs/object/pull/743) + +* Added `elf::SHT_RELR`, `read::elf::SectionHeader::relr`, and + `write::elf::Writer::write_relative_relocation_section_header`. + [#746](https://github.com/gimli-rs/object/pull/746) + +* Added `core::error::Error` implementation for Rust 1.81 onwards. + [#747](https://github.com/gimli-rs/object/pull/747) + +### Changed + +* Changed `build::elf::Builder` to support `.annobin.notes`, + `SHT_LLVM_DEPENDENT_LIBRARIES`, and `SHT_RELR` sections. + [#735](https://github.com/gimli-rs/object/pull/735) + [#737](https://github.com/gimli-rs/object/pull/737) + [#746](https://github.com/gimli-rs/object/pull/746) + +* Changed `write::Object::add_subsection` to omit the subsection name suffix + if the subsection name is empty. + [#748](https://github.com/gimli-rs/object/pull/748) + +-------------------------------------------------------------------------------- + +## 0.36.5 + +Released 2024/10/04. + +### Added + +* Added `Architecture::E2K32` and `Architecture::E2K64`. + [#727](https://github.com/gimli-rs/object/pull/727) + +* Added read and write support for `pe::IMAGE_REL_ARM64_BRANCH26`. + [#731](https://github.com/gimli-rs/object/pull/731) + +### Changed + +* Fixed decompression of multi-frame Zstandard data in `read::CompressedData::decompress`. + [#730](https://github.com/gimli-rs/object/pull/730) + +-------------------------------------------------------------------------------- + +## 0.36.4 + +Released 2024/08/30. + +### Added + +* Added `pe::IMAGE_FILE_MACHINE_ARM64X` and `pe::IMAGE_FILE_MACHINE_CHPE_X86`. + [#717](https://github.com/gimli-rs/object/pull/717) + +* Added `elf::SHF_GNU_RETAIN` and `elf::SHF_GNU_MBIND`. + [#720](https://github.com/gimli-rs/object/pull/720) + +### Changed + +* Fixed the checksum for COFF BSS section symbols in `write::Object`. + [#718](https://github.com/gimli-rs/object/pull/718) + +* Changed `read::CompressedData::decompress` to validate the decompressed size. + [#723](https://github.com/gimli-rs/object/pull/723) + +* Updated `wasmparser` dependency. + +-------------------------------------------------------------------------------- + +## 0.36.3 + +Released 2024/08/07. + +### Added + +* Added `Iterator` implementations for various types in the low level read API. + [#713](https://github.com/gimli-rs/object/pull/713) + [#714](https://github.com/gimli-rs/object/pull/714) + +### Changed + +* Changed `from_bytes` constructors for integer endian types to `const`. + [#712](https://github.com/gimli-rs/object/pull/712) + +* Changed `next` methods in the low level read API to fuse after returning an + error. + [#714](https://github.com/gimli-rs/object/pull/714) + +* Updated `wasmparser` dependency. + [#715](https://github.com/gimli-rs/object/pull/715) + +-------------------------------------------------------------------------------- + +## 0.36.2 + +Released 2024/07/24. + +### Changed + +* Improved writing of GNU symbol versioning in `build::elf::Builder`. + [#705](https://github.com/gimli-rs/object/pull/705) + +* Fixed alignment of `SHT_HASH`/`SHT_GNU_verdef`/`SHT_GNU_verneed` sections in + `write::elf::Writer`. + [#706](https://github.com/gimli-rs/object/pull/706) + +* Fixed writing of GNU hash for absolute symbols in `build::elf::Builder`. + [#707](https://github.com/gimli-rs/object/pull/707) + +* Fixed writing of empty ELF string table in `write::Object`. + [#710](https://github.com/gimli-rs/object/pull/710) + +-------------------------------------------------------------------------------- + +## 0.36.1 + +Released 2024/06/29. + +### Added + +* Added `SectionKind::DebugString`. + [#694](https://github.com/gimli-rs/object/pull/694) + +* Added `Architecture::Sparc` and `Architecture::Sparc32Plus`. + [#699](https://github.com/gimli-rs/object/pull/699) + [#700](https://github.com/gimli-rs/object/pull/700) + +* Added more RISC-V ELF relocation constants. + [#701](https://github.com/gimli-rs/object/pull/701) + +### Changed + +* Changed `read::ElfFile::imports` to return the library for versioned symbols. + [#693](https://github.com/gimli-rs/object/pull/693) + +* Changed `read::MachOFile` to support Go's debug section compression. + [#697](https://github.com/gimli-rs/object/pull/697) + +* Reversed the order of Mach-O relocations emitted by `write::Object`. + [#702](https://github.com/gimli-rs/object/pull/702) + +-------------------------------------------------------------------------------- + +## 0.36.0 + +Released 2024/05/26. + +### Breaking changes + +* Deleted `data` and `align` parameters for `write::Object::add_subsection`. + Use `add_symbol_data` or `add_symbol_bss` instead. + [#676](https://github.com/gimli-rs/object/pull/676) + +* Changed methods in the lower level read API to accept or return `SectionIndex` + or `SymbolIndex` instead of `usize`. + [#677](https://github.com/gimli-rs/object/pull/677) + [#684](https://github.com/gimli-rs/object/pull/684) + [#685](https://github.com/gimli-rs/object/pull/685) + +* Deleted `SymbolKind::Null`. Changed `read::Object::sections` and `read::Object::symbols` + to no longer return null entries. This affects ELF and XCOFF. + [#679](https://github.com/gimli-rs/object/pull/679) + +* Changed `read::ObjectMap::object` to return `ObjectMapFile`. This handles + splitting the object file name into path and member. + [#686](https://github.com/gimli-rs/object/pull/686) + +* Changed `read::coff::ImageSymbol::address` to only return an address for + symbols that have an address. + [#689](https://github.com/gimli-rs/object/pull/689) + +### Added + +* Added `pod::slice_from_all_bytes` and `pod::slice_from_all_bytes_mut`. + [#672](https://github.com/gimli-rs/object/pull/672) + +* Added `write::Object::set_subsections_via_symbols`. + Changed `write::Object::add_symbol_data` and `write::Object::add_symbol_bss` + to correctly handle zero size symbols when subsections are enabled. + [#676](https://github.com/gimli-rs/object/pull/676) + +* Added methods in the unified read API to return the lower level API structures. + Some existing methods were deprecated so that naming of these methods is more consistent. + [#678](https://github.com/gimli-rs/object/pull/678) + +* Added methods in the lower level read API to return a `SectionIndex` or `SymbolIndex`. + [#684](https://github.com/gimli-rs/object/pull/684) + [#689](https://github.com/gimli-rs/object/pull/689) + +* Implemented `Display` for `read::SymbolIndex` and `read::SectionIndex`. + [#684](https://github.com/gimli-rs/object/pull/684) + +* Added `is_common`, `is_absolute`, `is_local`, and `is_weak` to `read::elf::Sym`. + [#685](https://github.com/gimli-rs/object/pull/685) + +### Changed + +* Changed `read::ArchiveFile` to skip the `<ECSYMBOLS>` member. + [#669](https://github.com/gimli-rs/object/pull/669) + +* Fixed handling of segment data in the dyld shared cache. + [#673](https://github.com/gimli-rs/object/pull/673) + +* Changed `read::RelocationMap` to handle Mach-O section relocations. + [#675](https://github.com/gimli-rs/object/pull/675) + +* Changed `read::elf::RelocationSections` to ignore relocations that apply to relocations. + [#680](https://github.com/gimli-rs/object/pull/680) + +* Removed a lifetime bound from an argument in `read::elf::SectionTable::section_name`, + `read::elf::SymbolTable::symbol_name`, and `read::elf::SymbolTable::symbol_section`. + [#681](https://github.com/gimli-rs/object/pull/681) + +-------------------------------------------------------------------------------- + +## 0.35.0 + +Released 2024/04/10. + +### Breaking changes + +* Moved the `'file` lifetime parameter from `read::Object` to its associated types. + [#655](https://github.com/gimli-rs/object/pull/655) + +### Added + +* Added support more section kinds in `build::elf`. + [#650](https://github.com/gimli-rs/object/pull/650) + +* Added thin archive support to `read::ArchiveFile`. + [#651](https://github.com/gimli-rs/object/pull/651) + +* Added `read::ReadCacheOps` and changed `read::ReadCache` bound from `Read + Seek` to `ReadCacheOps`. + [#652](https://github.com/gimli-rs/object/pull/652) + +* Added `read::ObjectSection::relocation_map` + [#654](https://github.com/gimli-rs/object/pull/654) + +* Added `read::ArchiveFile::symbols`. + [#658](https://github.com/gimli-rs/object/pull/658) + +* Added `BinaryFormat::native_object`. + [#661](https://github.com/gimli-rs/object/pull/661) + +### Changed + +* The minimum supported rust version for the `read` feature and its dependencies + has changed to 1.65.0. + [#655](https://github.com/gimli-rs/object/pull/655) + +* Fixed `sh_offset` handling for `SHT_NOBITS` sections in `build::elf`. + [#645](https://github.com/gimli-rs/object/pull/645) + +* Fixed handling of ELF files with dynamic symbols but no dynamic strings. + [#646](https://github.com/gimli-rs/object/pull/646) + +* Fixed potential panics in `read::WasmFile` due to invalid function indices. + [#649](https://github.com/gimli-rs/object/pull/649) + +* Fixed handling of Wasm components in `read::WasmFile`. + [#649](https://github.com/gimli-rs/object/pull/649) + +* Fixed `sh_entsize` for 32-bit hash sections in `write::elf`. + [#650](https://github.com/gimli-rs/object/pull/650) + +* Fixed `sh_size` for attribute sections in `build::elf`. + [#650](https://github.com/gimli-rs/object/pull/650) + +* Fixed `sh_info` for `SHT_DYNSYM` sections in `build::elf`. + [#650](https://github.com/gimli-rs/object/pull/650) + +* Fixed handling of dynamic relocations with invalid `sh_link` in `build::elf`. + [#650](https://github.com/gimli-rs/object/pull/650) + +* Fixed parsing of member names containing '/' in `read::ArchiveFile`. + [#657](https://github.com/gimli-rs/object/pull/657) + +* Fixed handling of load segment alignments in `build::elf::Builder::read`. + [#659](https://github.com/gimli-rs/object/pull/659) + +-------------------------------------------------------------------------------- + +## 0.34.0 + +Released 2024/03/11. + +### Breaking changes + +* Replaced `macho::DyldSubCacheInfo` with `macho::DyldSubCacheEntryV1`. + Changed the return type of `macho::DyldCacheHeader::subcaches`. + [#642](https://github.com/gimli-rs/object/pull/642) + +### Changed + +* Added `macho::DyldSubCacheEntryV2` and changed `read::macho::DyldCache` + to handle both versions. This is needed for macOS 13 and above. + [#642](https://github.com/gimli-rs/object/pull/642) + +-------------------------------------------------------------------------------- + +## 0.33.0 + +Released 2024/03/05. + +### Breaking changes + +* Deleted file format variants in `RelocationKind`. Replaced their usage + with `read::Relocation::flags` and `write::Relocation::flags`. + [#585](https://github.com/gimli-rs/object/pull/585) + +* Replaced `kind`, `encoding` and `size` fields in `write::Relocation` + with `RelocationFlags::Generic` in the `flags` field. + [#585](https://github.com/gimli-rs/object/pull/585) + +* Replaced `macho::FatHeader::parse`, `macho::FatHeader::parse_arch32`, + and `macho::FatHeader::parse_arch64` with `read::macho::MachOFatFile`, + `read::macho::MachOFatFile32` and `read::macho::MachOFatFile64`. + [#623](https://github.com/gimli-rs/object/pull/623) + +### Added + +* Added `macho::PLATFORM_XROS` and `macho::PLATFORM_XROSSIMULATOR`. + [#626](https://github.com/gimli-rs/object/pull/626) + +* Added `build::elf::Builder` and associated types. + Extended `write::elf::Writer` to support this. + [#618](https://github.com/gimli-rs/object/pull/618) + +### Changed + +* Changed the lifetime to `'data` for the return value of `ObjectSection::name`, + `ObjectSection::name_bytes`, `ObjectComdat::name`, `ObjectComdat::name_bytes`. + [#620](https://github.com/gimli-rs/object/pull/620) + [#622](https://github.com/gimli-rs/object/pull/622) + +* Checked that sizes are smaller than the file length in `read::ReadCache`. + [#630](https://github.com/gimli-rs/object/pull/630) + +* Used `Vec::try_reserve_exact` for large allocations. + [#632](https://github.com/gimli-rs/object/pull/632) + +-------------------------------------------------------------------------------- + +## 0.32.2 + +Released 2023/12/24. + +### Added + +* Added ELF relocations for LoongArch ABI v2.20. + [#578](https://github.com/gimli-rs/object/pull/578) + [#589](https://github.com/gimli-rs/object/pull/589) + +* Added ELF support for SHARC. + [#593](https://github.com/gimli-rs/object/pull/593) + +* Added `write::coff::Writer`. + [#595](https://github.com/gimli-rs/object/pull/595) + +* Added `SubArchitecture::Arm64EC` support for PE/COFF. + [#607](https://github.com/gimli-rs/object/pull/607) + +* Added `SubArchitecture::Arm64E` support for Mach-O. + [#614](https://github.com/gimli-rs/object/pull/614) + +* Added `read::Object::symbol_by_name` and `read::Object::symbol_by_name_bytes`. + [#602](https://github.com/gimli-rs/object/pull/602) + +* Added more functions to the low level API in `read::xcoff`. + [#608](https://github.com/gimli-rs/object/pull/608) + +* Added more functions to the low level API in `read::macho`. + [#584](https://github.com/gimli-rs/object/pull/584) + +### Changed + +* Fixes for AArch64 relocation addends for Mach-O. + [#581](https://github.com/gimli-rs/object/pull/581) + +* Changes to `write::Object` output for Mach-O, including the addition of a `LC_DYSYMTAB` load command. + [#584](https://github.com/gimli-rs/object/pull/584) + +* Changed `write::Object` to always use `R_X86_64_PLT32` for x86-64 branches for ELF. + [#590](https://github.com/gimli-rs/object/pull/590) + +* Fixed `read::ObjectSymbol::kind` for undefined section symbols for COFF. + [#592](https://github.com/gimli-rs/object/pull/592) + +* Fixed `write::Object` to accept undefined section symbols for COFF. + [#594](https://github.com/gimli-rs/object/pull/594) + +* Improved parsing of auxiliary section symbols for COFF. + [#603](https://github.com/gimli-rs/object/pull/603) + +* Improved the selection of symbols for `read::Object::symbol_map`. + This includes changes to `read::Symbol::is_definition`. + [#601](https://github.com/gimli-rs/object/pull/601) + [#606](https://github.com/gimli-rs/object/pull/606) + +* Changed `read::ObjectSymbol::kind` for ELF `STT_NOTYPE` symbols to `SymbolKind::Unknown`. + [#604](https://github.com/gimli-rs/object/pull/604) + +* Changed `read::ObjectSymbol::scope` for XCOFF `C_HIDEXT` symbols to `SymbolScope::Compilation`. + [#605](https://github.com/gimli-rs/object/pull/605) + +-------------------------------------------------------------------------------- + +## 0.32.1 + +Released 2023/09/03. + +### Added + +* Added `write::Object::set_macho_cpu_subtype`. + [#574](https://github.com/gimli-rs/object/pull/574) + +-------------------------------------------------------------------------------- + +## 0.32.0 + +Released 2023/08/12. + +### Breaking changes + +* Changed `read::elf::Note::name` to exclude all trailing null bytes. + [#549](https://github.com/gimli-rs/object/pull/549) + +* Updated dependencies, and changed some optional dependencies to use the `dep:` + feature syntax. + [#558](https://github.com/gimli-rs/object/pull/558) + [#569](https://github.com/gimli-rs/object/pull/569) + +### Changed + +* The minimum supported rust version for the `read` feature and its dependencies + has changed to 1.60.0. + +* The minimum supported rust version for other features has changed to 1.65.0. + +* Changed many definitions from `static` to `const`. + [#549](https://github.com/gimli-rs/object/pull/549) + +* Fixed Mach-O section alignment padding in `write::Object`. + [#553](https://github.com/gimli-rs/object/pull/553) + +* Changed `read::File` to an enum. + [#564](https://github.com/gimli-rs/object/pull/564) + +### Added + +* Added `elf::ELF_NOTE_GO`, `elf::NT_GO_BUILD_ID`, and `read::elf::Note::name_bytes`. + [#549](https://github.com/gimli-rs/object/pull/549) + +* Added `read::FileKind::CoffImport` and `read::coff::ImportFile`. + [#555](https://github.com/gimli-rs/object/pull/555) + [#556](https://github.com/gimli-rs/object/pull/556) + +* Added `Architecture::Csky` and basic ELF support for C-SKY. + [#561](https://github.com/gimli-rs/object/pull/561) + +* Added `read::elf::ElfSymbol::raw_symbol`. + [#562](https://github.com/gimli-rs/object/pull/562) + +-------------------------------------------------------------------------------- + +## 0.30.4 + +Released 2023/06/05. + +### Changed + +* Fixed Mach-O section alignment padding in `write::Object`. + [#553](https://github.com/gimli-rs/object/pull/553) + +-------------------------------------------------------------------------------- + +## 0.31.1 + +Released 2023/05/09. + +### Changed + +* Fixed address for global symbols in `read::wasm`. + [#539](https://github.com/gimli-rs/object/pull/539) + +* Fixed writing of alignment for empty ELF sections. + [#540](https://github.com/gimli-rs/object/pull/540) + +### Added + +* Added more `elf::GNU_PROPERTY_*` definitions. + Added `read::elf::note::gnu_properties`, `write::StandardSection::GnuProperty`, + and `write::Object::add_elf_gnu_property_u32`. + [#537](https://github.com/gimli-rs/object/pull/537) + [#541](https://github.com/gimli-rs/object/pull/541) + +* Added Mach-O support for `Architecture::Aarch64_Ilp32`. + [#542](https://github.com/gimli-rs/object/pull/542) + [#545](https://github.com/gimli-rs/object/pull/545) + +* Added `Architecture::Wasm64`. + [#543](https://github.com/gimli-rs/object/pull/543) + +-------------------------------------------------------------------------------- + +## 0.31.0 + +Released 2023/04/14. + +### Breaking changes + +* Added a type parameter on existing COFF types to support reading COFF `/bigobj` files. + [#502](https://github.com/gimli-rs/object/pull/502) + +* Changed PE symbols to support COFF `/bigobj`. + Changed `pe::IMAGE_SYM_*` to `i32`. + Changed `pe::ImageSymbolEx::section_number` to `I32Bytes`. + Deleted a number of methods from `pe::ImageSymbol`. + Use the `read::pe::ImageSymbol` trait instead. + [#502](https://github.com/gimli-rs/object/pull/502) + +* Changed `pe::Guid` to a single array, and added methods to read the individual fields. + [#502](https://github.com/gimli-rs/object/pull/502) + +* Added `Symbol` type parameter to `SymbolFlags` to support `SymbolFlags::Xcoff`. + [#527](https://github.com/gimli-rs/object/pull/527) + +### Changed + +* Fix alignment when reserving zero length sections in `write::elf::Write::reserve`. + [#514](https://github.com/gimli-rs/object/pull/514) + +* Validate command size in `read::macho::LoadCommandIterator`. + [#516](https://github.com/gimli-rs/object/pull/516) + +* Handle invalid alignment in `read::macho::MachoSection::align`. + [#516](https://github.com/gimli-rs/object/pull/516) + +* Accept `SymbolKind::Unknown` in `write::Object::macho_write`. + [#519](https://github.com/gimli-rs/object/pull/519) + +* Updated `wasmparser` dependency. + [#528](https://github.com/gimli-rs/object/pull/528) + +### Added + +* Added more `elf::EF_RISCV_*` definitions. + [#507](https://github.com/gimli-rs/object/pull/507) + +* Added `read::elf::SectionHeader::gnu_attributes` and associated types. + Added `.gnu.attributes` support to `write::elf::Writer`. + [#509](https://github.com/gimli-rs/object/pull/509) + [#525](https://github.com/gimli-rs/object/pull/525) + +* Added `write::Object::set_macho_build_version`. + [#524](https://github.com/gimli-rs/object/pull/524) + +* Added `read::FileKind::Xcoff32`, `read::FileKind::Xcoff64`, `read::XcoffFile`, + and associated types. + Added XCOFF support to `write::Object`. + [#469](https://github.com/gimli-rs/object/pull/469) + [#476](https://github.com/gimli-rs/object/pull/476) + [#477](https://github.com/gimli-rs/object/pull/477) + [#482](https://github.com/gimli-rs/object/pull/482) + [#484](https://github.com/gimli-rs/object/pull/484) + [#486](https://github.com/gimli-rs/object/pull/486) + [#527](https://github.com/gimli-rs/object/pull/527) + +* Added `read::FileKind::CoffBig`, `read::pe::CoffHeader` and `read::pe::ImageSymbol`. + [#502](https://github.com/gimli-rs/object/pull/502) + +* Added `elf::PT_GNU_PROPERTY`. + [#530](https://github.com/gimli-rs/object/pull/530) + +* Added `elf::ELFCOMPRESS_ZSTD`, `read::CompressionFormat::Zstandard`, + and Zstandard decompression in `read::CompressedData::decompress` using + the `ruzstd` crate. + [#532](https://github.com/gimli-rs/object/pull/532) + +* Added `read::elf::NoteIterator::new`. + [#533](https://github.com/gimli-rs/object/pull/533) + +-------------------------------------------------------------------------------- + +## 0.30.3 + +Released 2023/01/23. + +### Added + +* Added `SectionKind::ReadOnlyDataWithRel` for writing. + [#504](https://github.com/gimli-rs/object/pull/504) + +-------------------------------------------------------------------------------- + +## 0.30.2 + +Released 2023/01/11. + +### Added + +* Added more ELF constants for AVR flags and relocations. + [#500](https://github.com/gimli-rs/object/pull/500) + +-------------------------------------------------------------------------------- + +## 0.30.1 + +Released 2023/01/04. + +### Changed + +* Changed `read::ElfSymbol::kind` to handle `STT_NOTYPE` and `STT_GNU_IFUNC`. + [#498](https://github.com/gimli-rs/object/pull/498) + +### Added + +* Added `read::CoffSymbol::raw_symbol`. + [#494](https://github.com/gimli-rs/object/pull/494) + +* Added ELF support for Solana Binary Format. + [#491](https://github.com/gimli-rs/object/pull/491) + +* Added ELF support for AArch64 ILP32. + [#497](https://github.com/gimli-rs/object/pull/497) + +-------------------------------------------------------------------------------- + +## 0.30.0 + +Released 2022/11/22. + +### Breaking changes + +* The minimum supported rust version for the `read` feature has changed to 1.52.0. + [#458](https://github.com/gimli-rs/object/pull/458) + +* The minimum supported rust version for the `write` feature has changed to 1.61.0. + +* Fixed endian handling in `read::elf::SymbolTable::shndx`. + [#458](https://github.com/gimli-rs/object/pull/458) + +* Fixed endian handling in `read::pe::ResourceName`. + [#458](https://github.com/gimli-rs/object/pull/458) + +* Changed definitions for LoongArch ELF header flags. + [#483](https://github.com/gimli-rs/object/pull/483) + +### Changed + +* Fixed parsing of multiple debug directory entries in `read::pe::PeFile::pdb_info`. + [#451](https://github.com/gimli-rs/object/pull/451) + +* Changed the section name used when writing COFF stub symbols. + [#475](https://github.com/gimli-rs/object/pull/475) + +### Added + +* Added `read::pe::DataDirectories::delay_load_import_table`. + [#448](https://github.com/gimli-rs/object/pull/448) + +* Added `read::macho::LoadCommandData::raw_data`. + [#449](https://github.com/gimli-rs/object/pull/449) + +* Added ELF relocations for LoongArch ps ABI v2. + [#450](https://github.com/gimli-rs/object/pull/450) + +* Added PowerPC support for Mach-O. + [#460](https://github.com/gimli-rs/object/pull/460) + +* Added support for reading the AIX big archive format. + [#462](https://github.com/gimli-rs/object/pull/462) + [#467](https://github.com/gimli-rs/object/pull/467) + [#473](https://github.com/gimli-rs/object/pull/473) + +* Added support for `RelocationEncoding::AArch64Call` when writing Mach-O files. + [#465](https://github.com/gimli-rs/object/pull/465) + +* Added support for `RelocationKind::Relative` when writing RISC-V ELF files. + [#470](https://github.com/gimli-rs/object/pull/470) + +* Added Xtensa architecture support for ELF. + [#481](https://github.com/gimli-rs/object/pull/481) + +* Added `read::pe::ResourceName::raw_data`. + [#487](https://github.com/gimli-rs/object/pull/487) + +-------------------------------------------------------------------------------- + +## 0.29.0 + +Released 2022/06/22. + +### Breaking changes + +* The `write` feature now has a minimum supported rust version of 1.56.1. + [#444](https://github.com/gimli-rs/object/pull/444) + +* Added `os_abi` and `abi_version` fields to `FileFlags::Elf`. + [#438](https://github.com/gimli-rs/object/pull/438) + [#441](https://github.com/gimli-rs/object/pull/441) + +### Changed + +* Fixed handling of empty symbol tables in `read::elf::ElfFile::symbol_table` and + `read::elf::ElfFile::dynamic_symbol_table`. + [#443](https://github.com/gimli-rs/object/pull/443) + +### Added + +* Added more `ELF_OSABI_*` constants. + [#439](https://github.com/gimli-rs/object/pull/439) + +-------------------------------------------------------------------------------- + +## 0.28.4 + +Released 2022/05/09. + +### Added + +* Added `read::pe::DataDirectories::resource_directory`. + [#425](https://github.com/gimli-rs/object/pull/425) + [#427](https://github.com/gimli-rs/object/pull/427) + +* Added PE support for more ARM relocations. + [#428](https://github.com/gimli-rs/object/pull/428) + +* Added support for `Architecture::LoongArch64`. + [#430](https://github.com/gimli-rs/object/pull/430) + [#432](https://github.com/gimli-rs/object/pull/432) + +* Added `elf::EF_MIPS_ABI` and associated constants. + [#433](https://github.com/gimli-rs/object/pull/433) + +-------------------------------------------------------------------------------- + +## 0.28.3 + +Released 2022/01/19. + +### Changed + +* For the Mach-O support in `write::Object`, accept `RelocationKind::MachO` for all + architectures, and accept `RelocationKind::Absolute` for ARM64. + [#422](https://github.com/gimli-rs/object/pull/422) + +### Added + +* Added `pe::ImageDataDirectory::file_range`, `read::pe::SectionTable::pe_file_range_at` + and `pe::ImageSectionHeader::pe_file_range_at`. + [#421](https://github.com/gimli-rs/object/pull/421) + +* Added `write::Object::add_coff_exports`. + [#423](https://github.com/gimli-rs/object/pull/423) + +-------------------------------------------------------------------------------- + +## 0.28.2 + +Released 2022/01/09. + +### Changed + +* Ignored errors for the Wasm extended name section in `read::WasmFile::parse`. + [#408](https://github.com/gimli-rs/object/pull/408) + +* Ignored errors for the COFF symbol table in `read::PeFile::parse`. + [#410](https://github.com/gimli-rs/object/pull/410) + +* Fixed handling of `SectionFlags::Coff` in `write::Object::coff_write`. + [#412](https://github.com/gimli-rs/object/pull/412) + +### Added + +* Added `read::ObjectSegment::flags`. + [#416](https://github.com/gimli-rs/object/pull/416) + [#418](https://github.com/gimli-rs/object/pull/418) + +-------------------------------------------------------------------------------- + +## 0.28.1 + +Released 2021/12/12. + +### Changed + +* Fixed `read::elf::SymbolTable::shndx_section`. + [#405](https://github.com/gimli-rs/object/pull/405) + +* Fixed build warnings. + [#405](https://github.com/gimli-rs/object/pull/405) + [#406](https://github.com/gimli-rs/object/pull/406) + +-------------------------------------------------------------------------------- + +## 0.28.0 + +Released 2021/12/12. + +### Breaking changes + +* `write_core` feature no longer enables `std` support. Use `write_std` instead. + [#400](https://github.com/gimli-rs/object/pull/400) + +* Multiple changes related to Mach-O split dyld cache support. + [#398](https://github.com/gimli-rs/object/pull/398) + +### Added + +* Added `write::pe::Writer::write_file_align`. + [#397](https://github.com/gimli-rs/object/pull/397) + +* Added support for Mach-O split dyld cache. + [#398](https://github.com/gimli-rs/object/pull/398) + +* Added support for `IMAGE_SCN_LNK_NRELOC_OVFL` when reading and writing COFF. + [#399](https://github.com/gimli-rs/object/pull/399) + +* Added `write::elf::Writer::reserve_null_symbol_index`. + [#402](https://github.com/gimli-rs/object/pull/402) + +-------------------------------------------------------------------------------- + +## 0.27.1 + +Released 2021/10/22. + +### Changed + +* Fixed build error with older Rust versions due to cargo resolver version. + +-------------------------------------------------------------------------------- + +## 0.27.0 + +Released 2021/10/17. + +### Breaking changes + +* Changed `read::elf` to use `SectionIndex` instead of `usize` in more places. + [#341](https://github.com/gimli-rs/object/pull/341) + +* Changed some `read::elf` section methods to additionally return the linked section index. + [#341](https://github.com/gimli-rs/object/pull/341) + +* Changed `read::pe::ImageNtHeaders::parse` to return `DataDirectories` instead of a slice. + [#357](https://github.com/gimli-rs/object/pull/357) + +* Deleted `value` parameter for `write:WritableBuffer::resize`. + [#369](https://github.com/gimli-rs/object/pull/369) + +* Changed `write::Object` and `write::Section` to use `Cow` for section data. + This added a lifetime parameter, which existing users can set to `'static`. + [#370](https://github.com/gimli-rs/object/pull/370) + +### Changed + +* Fixed parsing when PE import directory has zero size. + [#341](https://github.com/gimli-rs/object/pull/341) + +* Fixed parsing when PE import directory has zero for original first thunk. + [#385](https://github.com/gimli-rs/object/pull/385) + [#387](https://github.com/gimli-rs/object/pull/387) + +* Fixed parsing when PE export directory has zero number of names. + [#353](https://github.com/gimli-rs/object/pull/353) + +* Fixed parsing when PE export directory has zero number of names and addresses. + [#362](https://github.com/gimli-rs/object/pull/362) + +* Fixed parsing when PE sections are contiguous. + [#354](https://github.com/gimli-rs/object/pull/354) + +* Fixed `std` feature for `indexmap` dependency. + [#374](https://github.com/gimli-rs/object/pull/374) + +* Fixed overflow in COFF section name offset parsing. + [#390](https://github.com/gimli-rs/object/pull/390) + +### Added + +* Added `name_bytes` methods to unified `read` traits. + [#351](https://github.com/gimli-rs/object/pull/351) + +* Added `read::Object::kind`. + [#352](https://github.com/gimli-rs/object/pull/352) + +* Added `read::elf::VersionTable` and related helpers. + [#341](https://github.com/gimli-rs/object/pull/341) + +* Added `read::elf::SectionTable::dynamic` and related helpers. + [#345](https://github.com/gimli-rs/object/pull/345) + +* Added `read::coff::SectionTable::max_section_file_offset`. + [#344](https://github.com/gimli-rs/object/pull/344) + +* Added `read::pe::ExportTable` and related helpers. + [#349](https://github.com/gimli-rs/object/pull/349) + [#353](https://github.com/gimli-rs/object/pull/353) + +* Added `read::pe::ImportTable` and related helpers. + [#357](https://github.com/gimli-rs/object/pull/357) + +* Added `read::pe::DataDirectories` and related helpers. + [#357](https://github.com/gimli-rs/object/pull/357) + [#384](https://github.com/gimli-rs/object/pull/384) + +* Added `read::pe::RichHeaderInfo` and related helpers. + [#375](https://github.com/gimli-rs/object/pull/375) + [#379](https://github.com/gimli-rs/object/pull/379) + +* Added `read::pe::RelocationBlocks` and related helpers. + [#378](https://github.com/gimli-rs/object/pull/378) + +* Added `write::elf::Writer`. + [#350](https://github.com/gimli-rs/object/pull/350) + +* Added `write::pe::Writer`. + [#382](https://github.com/gimli-rs/object/pull/382) + [#388](https://github.com/gimli-rs/object/pull/388) + +* Added `write::Section::data/data_mut`. + [#367](https://github.com/gimli-rs/object/pull/367) + +* Added `write::Object::write_stream`. + [#369](https://github.com/gimli-rs/object/pull/369) + +* Added MIPSr6 ELF header flag definitions. + [#372](https://github.com/gimli-rs/object/pull/372) + +-------------------------------------------------------------------------------- + +## 0.26.2 + +Released 2021/08/28. + +### Added + +* Added support for 64-bit symbol table names to `read::archive`. + [#366](https://github.com/gimli-rs/object/pull/366) + +-------------------------------------------------------------------------------- + +## 0.26.1 + +Released 2021/08/19. + +### Changed + +* Activate `memchr`'s `rustc-dep-of-std` feature + [#356](https://github.com/gimli-rs/object/pull/356) + +-------------------------------------------------------------------------------- + +## 0.26.0 + +Released 2021/07/26. + +### Breaking changes + +* Changed `ReadRef::read_bytes_at_until` to accept a range parameter. + [#326](https://github.com/gimli-rs/object/pull/326) + +* Added `ReadRef` type parameter to `read::StringTable` and types that + contain it. String table entries are now only read as required. + [#326](https://github.com/gimli-rs/object/pull/326) + +* Changed result type of `read::elf::SectionHeader::data` and `data_as_array`. + [#332](https://github.com/gimli-rs/object/pull/332) + +* Moved `pod::WritableBuffer` to `write::WritableBuffer`. + Renamed `WritableBuffer::extend` to `write_bytes`. + Added more provided methods to `WritableBuffer`. + [#335](https://github.com/gimli-rs/object/pull/335) + +* Moved `pod::Bytes` to `read::Bytes`. + [#336](https://github.com/gimli-rs/object/pull/336) + +* Added `is_mips64el` parameter to `elf::Rela64::r_info/set_r_info`. + [#337](https://github.com/gimli-rs/object/pull/337) + +### Changed + +* Removed `alloc` dependency when no features are enabled. + [#336](https://github.com/gimli-rs/object/pull/336) + +### Added + +* Added `read::pe::PeFile` methods: `section_table`, `data_directory`, and `data`. + [#324](https://github.com/gimli-rs/object/pull/324) + +* Added more ELF definitions. + [#332](https://github.com/gimli-rs/object/pull/332) + +* Added `read::elf::SectionTable` methods for hash tables and symbol version + information. + [#332](https://github.com/gimli-rs/object/pull/332) + +* Added PE RISC-V definitions. + [#333](https://github.com/gimli-rs/object/pull/333) + +* Added `WritableBuffer` implementation for `Vec`. + [#335](https://github.com/gimli-rs/object/pull/335) + +-------------------------------------------------------------------------------- + +## 0.25.3 + +Released 2021/06/12. + +### Added + +* Added `RelocationEncoding::AArch64Call`. + [#322](https://github.com/gimli-rs/object/pull/322) + +-------------------------------------------------------------------------------- + +## 0.25.2 + +Released 2021/06/04. + +### Added + +* Added `Architecture::X86_64_X32`. + [#320](https://github.com/gimli-rs/object/pull/320) + +-------------------------------------------------------------------------------- + +## 0.25.1 + +Released 2021/06/03. + +### Changed + +* write: Fix choice of `SHT_REL` or `SHT_RELA` for most architectures. + [#318](https://github.com/gimli-rs/object/pull/318) + +* write: Fix relocation encoding for MIPS64EL. + [#318](https://github.com/gimli-rs/object/pull/318) + +-------------------------------------------------------------------------------- + +## 0.25.0 + +Released 2021/06/02. + +### Breaking changes + +* Added `non_exhaustive` to most public enums. + [#306](https://github.com/gimli-rs/object/pull/306) + +* `MachHeader::parse` and `MachHeader::load_commands` now require a header offset. + [#304](https://github.com/gimli-rs/object/pull/304) + +* Added `ReadRef::read_bytes_at_until`. + [#308](https://github.com/gimli-rs/object/pull/308) + +* `PeFile::entry`, `PeSection::address` and `PeSegment::address` now return a + virtual address instead of a RVA. + [#315](https://github.com/gimli-rs/object/pull/315) + +### Added + +* Added `pod::from_bytes_mut`, `pod::slice_from_bytes_mut`, `pod::bytes_of_mut`, + and `pod::bytes_of_slice_mut`. + [#296](https://github.com/gimli-rs/object/pull/296) + [#297](https://github.com/gimli-rs/object/pull/297) + +* Added `Object::pdb_info`. + [#298](https://github.com/gimli-rs/object/pull/298) + +* Added `read::macho::DyldCache`, other associated definitions, + and support for these in the examples. + [#308](https://github.com/gimli-rs/object/pull/308) + +* Added more architecture support. + [#303](https://github.com/gimli-rs/object/pull/303) + [#309](https://github.com/gimli-rs/object/pull/309) + +* Derive more traits for enums. + [#311](https://github.com/gimli-rs/object/pull/311) + +* Added `Object::relative_address_base`. + [#315](https://github.com/gimli-rs/object/pull/315) + +### Changed + +* Improved performance for string parsing. + [#302](https://github.com/gimli-rs/object/pull/302) + +* `objdump` example allows selecting container members. + [#308](https://github.com/gimli-rs/object/pull/308)
diff --git a/chromium_crates_io/vendor/object-v0_37/Cargo.lock b/chromium_crates_io/vendor/object-v0_37/Cargo.lock new file mode 100644 index 0000000..5a99cb2 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/Cargo.lock
@@ -0,0 +1,140 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "foldhash", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "object" +version = "0.37.3" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown", + "indexmap", + "memchr", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "ruzstd", + "wasmparser", +] + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3" + +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9c45b374136f52f2d6311062c7146bff20fec063c3f5d46a410bd937746955" + +[[package]] +name = "ruzstd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "twox-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56" + +[[package]] +name = "wasmparser" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d1eee846a705f6f3cb9d7b9f79b54583810f1fb57a1e3aea76d1742db2e3d2" +dependencies = [ + "bitflags", +]
diff --git a/chromium_crates_io/vendor/object-v0_37/Cargo.toml b/chromium_crates_io/vendor/object-v0_37/Cargo.toml new file mode 100644 index 0000000..a478df3 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/Cargo.toml
@@ -0,0 +1,209 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.65" +name = "object" +version = "0.37.3" +build = "build.rs" +include = [ + "/build.rs", + "/Cargo.toml", + "/CHANGELOG.md", + "/README.md", + "/LICENSE-APACHE", + "/LICENSE-MIT", + "/src", + "/tests", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A unified interface for reading and writing object file formats." +readme = "README.md" +keywords = [ + "object", + "elf", + "mach-o", + "pe", + "coff", +] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/gimli-rs/object" +resolver = "2" + +[package.metadata.docs.rs] +features = ["doc"] + +[features] +all = [ + "read", + "write", + "build", + "std", + "compression", + "wasm", +] +archive = [] +build = [ + "build_core", + "write_std", + "elf", +] +build_core = [ + "read_core", + "write_core", +] +cargo-all = [] +coff = [] +compression = [ + "dep:flate2", + "dep:ruzstd", + "std", +] +default = [ + "read", + "compression", +] +doc = [ + "read_core", + "write_std", + "build_core", + "std", + "compression", + "archive", + "coff", + "elf", + "macho", + "pe", + "wasm", + "xcoff", +] +elf = [] +macho = [] +pe = ["coff"] +read = [ + "read_core", + "archive", + "coff", + "elf", + "macho", + "pe", + "xcoff", + "unaligned", +] +read_core = [] +rustc-dep-of-std = [ + "core", + "alloc", + "memchr/rustc-dep-of-std", +] +std = ["memchr/std"] +unaligned = [] +unstable = [] +unstable-all = [ + "all", + "unstable", +] +wasm = ["dep:wasmparser"] +write = [ + "write_std", + "coff", + "elf", + "macho", + "pe", + "xcoff", +] +write_core = [ + "dep:crc32fast", + "dep:indexmap", + "dep:hashbrown", +] +write_std = [ + "write_core", + "std", + "indexmap?/std", + "crc32fast?/std", +] +xcoff = [] + +[lib] +name = "object" +path = "src/lib.rs" + +[[test]] +name = "integration" +path = "tests/integration.rs" + +[[test]] +name = "parse_self" +path = "tests/parse_self.rs" + +[dependencies.alloc] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-alloc" + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" + +[dependencies.crc32fast] +version = "1.2" +optional = true +default-features = false + +[dependencies.flate2] +version = "1" +optional = true + +[dependencies.hashbrown] +version = "0.15.0" +features = ["default-hasher"] +optional = true +default-features = false + +[dependencies.indexmap] +version = "2.0" +optional = true +default-features = false + +[dependencies.memchr] +version = "2.4.1" +default-features = false + +[dependencies.ruzstd] +version = "0.8.1" +optional = true + +[dependencies.wasmparser] +version = "0.236.0" +optional = true +default-features = false + +[lints.clippy] +collapsible_else_if = "allow" +collapsible_if = "allow" +collapsible_match = "allow" +comparison_chain = "allow" +field_reassign_with_default = "allow" +manual_flatten = "allow" +match_like_matches_macro = "allow" +needless_lifetimes = "allow" +result_unit_err = "allow" +should_implement_trait = "allow" +single_match = "allow" +type_complexity = "allow" +uninlined_format_args = "allow"
diff --git a/chromium_crates_io/vendor/object-v0_37/Cargo.toml.orig b/chromium_crates_io/vendor/object-v0_37/Cargo.toml.orig new file mode 100644 index 0000000..142e09e --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/Cargo.toml.orig
@@ -0,0 +1,137 @@ +[package] +name = "object" +version = "0.37.3" +edition = "2018" +keywords = ["object", "elf", "mach-o", "pe", "coff"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/gimli-rs/object" +# NB: if this number increases to 1.81-or-later delete the `build.rs` script +# as it's no longer necessary. +rust-version = "1.65" +description = "A unified interface for reading and writing object file formats." +include = [ + "/build.rs", + "/Cargo.toml", + "/CHANGELOG.md", + "/README.md", + "/LICENSE-APACHE", + "/LICENSE-MIT", + "/src", + "/tests", +] + +[package.metadata.docs.rs] +features = ['doc'] + +[dependencies] +crc32fast = { version = "1.2", default-features = false, optional = true } +flate2 = { version = "1", optional = true } +indexmap = { version = "2.0", default-features = false, optional = true } +wasmparser = { version = "0.236.0", default-features = false, optional = true } +memchr = { version = "2.4.1", default-features = false } +hashbrown = { version = "0.15.0", features = ["default-hasher"], default-features = false, optional = true } +ruzstd = { version = "0.8.1", optional = true } + +# Internal feature, only used when building as part of libstd, not part of the +# stable interface of this crate. +core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } +alloc = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-alloc' } + +[features] +#======================================= +# Read/write features. + +# Core read support. You will need to enable some file formats too. +read_core = [] +# Read support for most file formats (including unaligned files). +read = ["read_core", "archive", "coff", "elf", "macho", "pe", "xcoff", "unaligned"] +# Core write support. You will need to enable some file formats too. +write_core = ["dep:crc32fast", "dep:indexmap", "dep:hashbrown"] +# Core write support with libstd features. You will need to enable some file formats too. +write_std = ["write_core", "std", "indexmap?/std", "crc32fast?/std"] +# Write support for all file formats, including libstd features. +write = ["write_std", "coff", "elf", "macho", "pe", "xcoff"] +# Core builder support. You will need to enable some file formats too. +build_core = ["read_core", "write_core"] +# Builder support for all file formats. +build = ["build_core", "write_std", "elf"] + +#======================================= +# Misc features. + +# Enable things that require libstd. +# Currently, this provides an `Error` implementation. +std = ["memchr/std"] +# Enable decompression of compressed sections. +# This feature is not required if you want to do your own decompression. +compression = ["dep:flate2", "dep:ruzstd", "std"] +# Treat all types as unaligned. +# Normally types use the alignment required by the specifications, but +# sometimes files do not strictly follow the specifications. +# This may be useful to enable when processing files for architectures +# that have no alignment constraints. +unaligned = [] + +#======================================= +# File format features. +archive = [] +coff = [] +elf = [] +macho = [] +pe = ["coff"] +wasm = ["dep:wasmparser"] +xcoff = [] + +#======================================= +# By default, support all read features. +default = ["read", "compression"] + +#======================================= +# Umbrella feature for enabling all user-facing features of this crate. Does not +# enable internal features like `rustc-dep-of-std`. +all = ["read", "write", "build", "std", "compression", "wasm"] + +# Use of --all-features is not supported. +# This is a dummy feature to detect when --all-features is used. +cargo-all = [] + +#======================================= +# Documentation should be generated with everything in "all" except for "unaligned". +doc = [ + "read_core", "write_std", "build_core", + "std", "compression", + "archive", "coff", "elf", "macho", "pe", "wasm", "xcoff", +] + +#======================================= +# Unstable features. Breaking changes in these features will not affect versioning. +unstable = [] +unstable-all = ["all", "unstable"] + +#======================================= +# Internal feature, only used when building as part of libstd, not part of the +# stable interface of this crate. +rustc-dep-of-std = ['core', 'alloc', 'memchr/rustc-dep-of-std'] + +[lints.clippy] +# Style. +collapsible_else_if = "allow" +collapsible_if = "allow" +collapsible_match = "allow" +comparison_chain = "allow" +field_reassign_with_default = "allow" +manual_flatten = "allow" +match_like_matches_macro = "allow" +needless_lifetimes = "allow" +single_match = "allow" +type_complexity = "allow" +uninlined_format_args = "allow" +# Occurs due to fallible iteration. +should_implement_trait = "allow" +# Unit errors are converted to other types by callers. +result_unit_err = "allow" + +[workspace] +members = ["crates/*"] +default-members = [".", "crates/examples"] +resolver = "2"
diff --git a/chromium_crates_io/vendor/object-v0_37/LICENSE-APACHE b/chromium_crates_io/vendor/object-v0_37/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/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/chromium_crates_io/vendor/object-v0_37/LICENSE-MIT b/chromium_crates_io/vendor/object-v0_37/LICENSE-MIT new file mode 100644 index 0000000..8440c7e --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/LICENSE-MIT
@@ -0,0 +1,25 @@ +Copyright (c) 2015 The Gimli Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
diff --git a/chromium_crates_io/vendor/object-v0_37/README.md b/chromium_crates_io/vendor/object-v0_37/README.md new file mode 100644 index 0000000..beef0ac --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/README.md
@@ -0,0 +1,61 @@ +# `object` + +The `object` crate provides a unified interface to working with object files +across platforms. It supports reading relocatable object files and executable files, +and writing COFF/ELF/Mach-O/XCOFF relocatable object files and ELF/PE executable files. + +For reading files, it provides multiple levels of support: + +* raw struct definitions suitable for zero copy access +* low level APIs for accessing the raw structs ([example](crates/examples/src/readobj/)) +* a higher level unified API for accessing common features of object files, such + as sections and symbols ([example](crates/examples/src/objdump.rs)) + +Supported file formats for reading: ELF, Mach-O, Windows PE/COFF, Wasm, XCOFF, and Unix archive. + +For writing files, it provides: + +* low level writers for ELF, PE, and COFF +* higher level builder for ELF ([example](crates/rewrite/src)) +* a unified API for writing relocatable object files (ELF, Mach-O, COFF, XCOFF) + ([example](crates/examples/src/bin/simple_write.rs)) + +## Example for unified read API +```rust +use object::{Object, ObjectSection}; +use std::error::Error; +use std::fs; + +/// Reads a file and displays the name of each section. +fn main() -> Result<(), Box<dyn Error>> { + let binary_data = fs::read("path/to/binary")?; + let file = object::File::parse(&*binary_data)?; + for section in file.sections() { + println!("{}", section.name()?); + } + Ok(()) +} +``` + +See [`crates/examples`](crates/examples) for more examples. + +## Minimum Supported Rust Version (MSRV) + +Changes to MSRV are considered breaking changes. We are conservative about changing the MSRV, +but sometimes are required to due to dependencies. The MSRV with all features enabled is 1.81.0. +The MSRV with some features disabled is 1.65.0. + +## 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 + +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/chromium_crates_io/vendor/object-v0_37/build.rs b/chromium_crates_io/vendor/object-v0_37/build.rs new file mode 100644 index 0000000..305853b --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/build.rs
@@ -0,0 +1,27 @@ +use std::process::Command; +use std::str; + +fn main() { + // Temporary check to see if the rustc version >= 1.81 in which case the + // `Error` trait is always available. This is temporary because in the + // future the MSRV of this crate will be beyond 1.81 in which case this + // build script can be deleted. + let minor = rustc_minor_version().unwrap_or(0); + if minor >= 81 { + println!("cargo:rustc-cfg=core_error"); + } + if minor >= 80 { + println!("cargo:rustc-check-cfg=cfg(core_error)"); + } +} + +fn rustc_minor_version() -> Option<u32> { + let rustc = std::env::var("RUSTC").unwrap(); + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + pieces.next()?.parse().ok() +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/archive.rs b/chromium_crates_io/vendor/object-v0_37/src/archive.rs new file mode 100644 index 0000000..6271d07 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/archive.rs
@@ -0,0 +1,91 @@ +//! Archive definitions. +//! +//! These definitions are independent of read/write support, although we do implement +//! some traits useful for those. + +use crate::pod::Pod; + +/// File identification bytes stored at the beginning of the file. +pub const MAGIC: [u8; 8] = *b"!<arch>\n"; + +/// File identification bytes at the beginning of AIX big archive. +pub const AIX_BIG_MAGIC: [u8; 8] = *b"<bigaf>\n"; + +/// File identification bytes stored at the beginning of a thin archive. +/// +/// A thin archive only contains a symbol table and file names. +pub const THIN_MAGIC: [u8; 8] = *b"!<thin>\n"; + +/// The terminator for each archive member header. +pub const TERMINATOR: [u8; 2] = *b"`\n"; + +/// The header at the start of an archive member. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Header { + /// The file name. + pub name: [u8; 16], + /// File modification timestamp in decimal. + pub date: [u8; 12], + /// User ID in decimal. + pub uid: [u8; 6], + /// Group ID in decimal. + pub gid: [u8; 6], + /// File mode in octal. + pub mode: [u8; 8], + /// File size in decimal. + pub size: [u8; 10], + /// Must be equal to `TERMINATOR`. + pub terminator: [u8; 2], +} + +/// The header at the start of an AIX big archive member, without name. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AixHeader { + /// File member size in decimal. + pub size: [u8; 20], + /// Next member offset in decimal. + pub nxtmem: [u8; 20], + /// Previous member offset in decimal. + pub prvmem: [u8; 20], + /// File member date in decimal. + pub date: [u8; 12], + /// File member user id in decimal. + pub uid: [u8; 12], + /// File member group id in decimal. + pub gid: [u8; 12], + /// File member mode in octal. + pub mode: [u8; 12], + /// File member name length in decimal. + pub namlen: [u8; 4], +} + +/// The AIX big archive's fixed length header at file beginning. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AixFileHeader { + /// Archive magic string. + pub magic: [u8; 8], + /// Offset of member table. + pub memoff: [u8; 20], + /// Offset of global symbol table. + pub gstoff: [u8; 20], + /// Offset of global symbol table for 64-bit objects. + pub gst64off: [u8; 20], + /// Offset of first member. + pub fstmoff: [u8; 20], + /// Offset of last member. + pub lstmoff: [u8; 20], + /// Offset of first member on free list. + pub freeoff: [u8; 20], +} + +/// Offset of a member in an AIX big archive. +/// +/// This is used in the member index. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AixMemberOffset(pub [u8; 20]); + +unsafe_impl_pod!(Header, AixHeader, AixFileHeader, AixMemberOffset,);
diff --git a/chromium_crates_io/vendor/object-v0_37/src/build/bytes.rs b/chromium_crates_io/vendor/object-v0_37/src/build/bytes.rs new file mode 100644 index 0000000..2363558 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/build/bytes.rs
@@ -0,0 +1,146 @@ +use alloc::borrow::Cow; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + +/// A byte slice. +/// +/// Uses copy-on-write to avoid unnecessary allocations. The bytes can be +/// accessed as a slice using the `Deref` trait, or as a mutable `Vec` using the +/// `to_mut` method. +/// +/// Provides a `Debug` implementation that shows the first 8 bytes and the length. +#[derive(Default, Clone, PartialEq, Eq)] +pub struct Bytes<'a>(Cow<'a, [u8]>); + +impl<'a> Bytes<'a> { + /// Acquire a mutable reference to the bytes. + /// + /// Clones the bytes if they are shared. + pub fn to_mut(&mut self) -> &mut Vec<u8> { + self.0.to_mut() + } + + /// Get the bytes as a slice. + pub fn as_slice(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl<'a> core::ops::Deref for Bytes<'a> { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.deref() + } +} + +impl<'a> From<&'a [u8]> for Bytes<'a> { + fn from(bytes: &'a [u8]) -> Self { + Bytes(Cow::Borrowed(bytes)) + } +} + +impl<'a> From<Vec<u8>> for Bytes<'a> { + fn from(bytes: Vec<u8>) -> Self { + Bytes(Cow::Owned(bytes)) + } +} + +impl<'a> fmt::Debug for Bytes<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_list_bytes(&self.0, f) + } +} + +// Only for Debug impl of `Bytes`. +fn debug_list_bytes(bytes: &[u8], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = fmt.debug_list(); + list.entries(bytes.iter().take(8).copied().map(DebugByte)); + if bytes.len() > 8 { + list.entry(&DebugLen(bytes.len())); + } + list.finish() +} + +struct DebugByte(u8); + +impl fmt::Debug for DebugByte { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "0x{:02x}", self.0) + } +} + +struct DebugLen(usize); + +impl fmt::Debug for DebugLen { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "...; {}", self.0) + } +} + +/// A byte slice that is a string of an unknown encoding. +/// +/// Uses copy-on-write to avoid unnecessary allocations. The bytes can be +/// accessed as a slice using the `Deref` trait, or as a mutable `Vec` using the +/// `to_mut` method. +/// +/// Provides a `Debug` implementation that interprets the bytes as UTF-8. +#[derive(Default, Clone, PartialEq, Eq, Hash)] +pub struct ByteString<'a>(Cow<'a, [u8]>); + +impl<'a> ByteString<'a> { + /// Acquire a mutable reference to the bytes. + /// + /// Clones the bytes if they are shared. + pub fn to_mut(&mut self) -> &mut Vec<u8> { + self.0.to_mut() + } + + /// Get the bytes as a slice. + pub fn as_slice(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl<'a> core::borrow::Borrow<[u8]> for ByteString<'a> { + fn borrow(&self) -> &[u8] { + self.0.borrow() + } +} + +impl<'a> core::ops::Deref for ByteString<'a> { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.deref() + } +} + +impl<'a> From<&'a [u8]> for ByteString<'a> { + fn from(bytes: &'a [u8]) -> Self { + ByteString(Cow::Borrowed(bytes)) + } +} + +impl<'a> From<Vec<u8>> for ByteString<'a> { + fn from(bytes: Vec<u8>) -> Self { + ByteString(Cow::Owned(bytes)) + } +} + +impl<'a> From<&'a str> for ByteString<'a> { + fn from(s: &'a str) -> Self { + ByteString(Cow::Borrowed(s.as_bytes())) + } +} + +impl<'a> fmt::Debug for ByteString<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"{}\"", String::from_utf8_lossy(&self.0)) + } +} + +impl<'a> fmt::Display for ByteString<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{}", String::from_utf8_lossy(&self.0)) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/build/elf.rs b/chromium_crates_io/vendor/object-v0_37/src/build/elf.rs new file mode 100644 index 0000000..9ade35c --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/build/elf.rs
@@ -0,0 +1,3131 @@ +//! This module provides a [`Builder`] for reading, modifying, and then writing ELF files. +use alloc::vec::Vec; +use core::convert::TryInto; +use core::fmt; +use core::marker::PhantomData; +#[cfg(not(feature = "std"))] +use hashbrown::HashMap; +#[cfg(feature = "std")] +use std::collections::HashMap; + +use crate::build::{ByteString, Bytes, Error, Id, IdPrivate, Item, Result, Table}; +use crate::elf; +use crate::read::elf::{Dyn, FileHeader, ProgramHeader, Rela, SectionHeader, Sym}; +use crate::read::{self, FileKind, ReadRef}; +use crate::write; +use crate::Endianness; + +/// A builder for reading, modifying, and then writing ELF files. +/// +/// Public fields are available for modifying the values that will be written. +/// Methods are available to add elements to tables, and elements can be deleted +/// from tables by setting the `delete` field in the element. +#[derive(Debug)] +pub struct Builder<'data> { + /// The endianness. + /// + /// Used to set the data encoding when writing the ELF file. + pub endian: Endianness, + /// Whether file is 64-bit. + /// + /// Use to set the file class when writing the ELF file. + pub is_64: bool, + /// The alignment of [`elf::PT_LOAD`] segments. + /// + /// This is an informational field and is not used when writing the ELF file. + /// It can optionally be used when calling [`Segments::add_load_segment`]. + /// + /// It is determined heuristically when reading the ELF file. Currently, + /// if all load segments have the same alignment, that alignment is used, + /// otherwise it is set to 1. + pub load_align: u64, + /// The file header. + pub header: Header, + /// The segment table. + pub segments: Segments<'data>, + /// The section table. + pub sections: Sections<'data>, + /// The symbol table. + pub symbols: Symbols<'data>, + /// The dynamic symbol table. + pub dynamic_symbols: DynamicSymbols<'data>, + /// The base version for the GNU version definitions. + /// + /// This will be written as a version definition with index 1. + pub version_base: Option<ByteString<'data>>, + /// The GNU version definitions and dependencies. + pub versions: Versions<'data>, + /// The filenames used in the GNU version definitions. + pub version_files: VersionFiles<'data>, + /// The bucket count parameter for the hash table. + pub hash_bucket_count: u32, + /// The bloom shift parameter for the GNU hash table. + pub gnu_hash_bloom_shift: u32, + /// The bloom count parameter for the GNU hash table. + pub gnu_hash_bloom_count: u32, + /// The bucket count parameter for the GNU hash table. + pub gnu_hash_bucket_count: u32, + marker: PhantomData<()>, +} + +impl<'data> Builder<'data> { + /// Create a new ELF builder. + pub fn new(endian: Endianness, is_64: bool) -> Self { + Self { + endian, + is_64, + load_align: 1, + header: Header::default(), + segments: Segments::new(), + sections: Sections::new(), + symbols: Symbols::new(), + dynamic_symbols: Symbols::new(), + version_base: None, + versions: Versions::new(), + version_files: VersionFiles::new(), + hash_bucket_count: 0, + gnu_hash_bloom_shift: 0, + gnu_hash_bloom_count: 0, + gnu_hash_bucket_count: 0, + marker: PhantomData, + } + } + + /// Read the ELF file from file data. + pub fn read<R: ReadRef<'data>>(data: R) -> Result<Self> { + match FileKind::parse(data)? { + FileKind::Elf32 => Self::read32(data), + FileKind::Elf64 => Self::read64(data), + #[allow(unreachable_patterns)] + _ => Err(Error::new("Not an ELF file")), + } + } + + /// Read a 32-bit ELF file from file data. + pub fn read32<R: ReadRef<'data>>(data: R) -> Result<Self> { + Self::read_file::<elf::FileHeader32<Endianness>, R>(data) + } + + /// Read a 64-bit ELF file from file data. + pub fn read64<R: ReadRef<'data>>(data: R) -> Result<Self> { + Self::read_file::<elf::FileHeader64<Endianness>, R>(data) + } + + fn read_file<Elf, R>(data: R) -> Result<Self> + where + Elf: FileHeader<Endian = Endianness>, + R: ReadRef<'data>, + { + let header = Elf::parse(data)?; + let endian = header.endian()?; + let is_mips64el = header.is_mips64el(endian); + let section_strings_index = header.section_strings_index(endian, data)?; + let segments = header.program_headers(endian, data)?; + let sections = header.sections(endian, data)?; + let symbols = sections.symbols(endian, data, elf::SHT_SYMTAB)?; + let dynamic_symbols = sections.symbols(endian, data, elf::SHT_DYNSYM)?; + + let mut builder = Builder { + endian, + is_64: header.is_type_64(), + load_align: 0, + header: Header { + os_abi: header.e_ident().os_abi, + abi_version: header.e_ident().abi_version, + e_type: header.e_type(endian), + e_machine: header.e_machine(endian), + e_entry: header.e_entry(endian).into(), + e_flags: header.e_flags(endian), + e_phoff: header.e_phoff(endian).into(), + }, + segments: Segments::new(), + sections: Sections::new(), + symbols: Symbols::new(), + dynamic_symbols: Symbols::new(), + version_base: None, + versions: Versions::new(), + version_files: VersionFiles::new(), + hash_bucket_count: 0, + gnu_hash_bloom_shift: 0, + gnu_hash_bloom_count: 0, + gnu_hash_bucket_count: 0, + marker: PhantomData, + }; + + for segment in segments { + if segment.p_type(endian) == elf::PT_LOAD { + let p_align = segment.p_align(endian).into(); + if builder.load_align == 0 { + builder.load_align = p_align; + } else if builder.load_align != p_align { + builder.load_align = 1; + } + } + + let id = builder.segments.next_id(); + builder.segments.push(Segment { + id, + delete: false, + p_type: segment.p_type(endian), + p_flags: segment.p_flags(endian), + p_offset: segment.p_offset(endian).into(), + p_vaddr: segment.p_vaddr(endian).into(), + p_paddr: segment.p_paddr(endian).into(), + p_filesz: segment.p_filesz(endian).into(), + p_memsz: segment.p_memsz(endian).into(), + p_align: segment.p_align(endian).into(), + sections: Vec::new(), + marker: PhantomData, + }); + } + if builder.load_align == 0 { + builder.load_align = 1; + } + + for (index, section) in sections.enumerate().skip(1) { + let id = SectionId(index.0 - 1); + let relocations = if let Some((rels, link)) = section.rel(endian, data)? { + Self::read_relocations( + index, + endian, + is_mips64el, + section, + rels, + link, + &symbols, + &dynamic_symbols, + )? + } else if let Some((rels, link)) = section.rela(endian, data)? { + Self::read_relocations( + index, + endian, + is_mips64el, + section, + rels, + link, + &symbols, + &dynamic_symbols, + )? + } else { + SectionData::Data(Bytes::default()) + }; + if let Some(hash) = section.hash_header(endian, data)? { + builder.hash_bucket_count = hash.bucket_count.get(endian); + } + if let Some(hash) = section.gnu_hash_header(endian, data)? { + builder.gnu_hash_bloom_shift = hash.bloom_shift.get(endian); + builder.gnu_hash_bloom_count = hash.bloom_count.get(endian); + builder.gnu_hash_bucket_count = hash.bucket_count.get(endian); + } + let name = sections.section_name(endian, section)?; + let data = match section.sh_type(endian) { + elf::SHT_NOBITS => SectionData::UninitializedData(section.sh_size(endian).into()), + // Section types that we treat as opaque data. In future, some of these could be + // changed to a parsed variant if we need to modify their contents. + elf::SHT_PROGBITS + | elf::SHT_INIT_ARRAY + | elf::SHT_FINI_ARRAY + | elf::SHT_PREINIT_ARRAY + | elf::SHT_RELR + | elf::SHT_CREL + | elf::SHT_LLVM_DEPENDENT_LIBRARIES => { + SectionData::Data(section.data(endian, data)?.into()) + } + elf::SHT_REL | elf::SHT_RELA => relocations, + elf::SHT_SYMTAB => { + if index == symbols.section() { + SectionData::Symbol + } else { + return Err(Error(format!( + "Unsupported SHT_SYMTAB section at index {}", + index + ))); + } + } + elf::SHT_SYMTAB_SHNDX => { + if index == symbols.shndx_section() { + SectionData::SymbolSectionIndex + } else { + return Err(Error(format!( + "Unsupported SHT_SYMTAB_SHNDX section at index {}", + index + ))); + } + } + elf::SHT_DYNSYM => { + if index == dynamic_symbols.section() { + SectionData::DynamicSymbol + } else { + return Err(Error(format!( + "Unsupported SHT_DYNSYM section at index {}", + index + ))); + } + } + elf::SHT_STRTAB => { + if index == symbols.string_section() { + SectionData::String + } else if index == dynamic_symbols.string_section() { + SectionData::DynamicString + } else if index == section_strings_index { + SectionData::SectionString + } else if name == b".annobin.notes" { + // Not actually a string table because nothing references the strings. + // We simply need to preserve the data (similar to a .comment section). + SectionData::Data(section.data(endian, data)?.into()) + } else { + return Err(Error(format!( + "Unsupported SHT_STRTAB section at index {}", + index + ))); + } + } + elf::SHT_NOTE => SectionData::Note(section.data(endian, data)?.into()), + elf::SHT_DYNAMIC => { + let (dyns, link) = section.dynamic(endian, data)?.unwrap(); + let dynamic_strings = sections.strings(endian, data, link)?; + Self::read_dynamics::<Elf, _>(endian, dyns, dynamic_strings)? + } + elf::SHT_GNU_ATTRIBUTES => { + let attributes = section.attributes(endian, data)?; + Self::read_attributes(index, attributes, sections.len(), symbols.len())? + } + elf::SHT_HASH => SectionData::Hash, + elf::SHT_GNU_HASH => SectionData::GnuHash, + elf::SHT_GNU_VERSYM => SectionData::GnuVersym, + elf::SHT_GNU_VERDEF => SectionData::GnuVerdef, + elf::SHT_GNU_VERNEED => SectionData::GnuVerneed, + other => match (builder.header.e_machine, other) { + (elf::EM_ARM, elf::SHT_ARM_ATTRIBUTES) + | (elf::EM_AARCH64, elf::SHT_AARCH64_ATTRIBUTES) + | (elf::EM_CSKY, elf::SHT_CSKY_ATTRIBUTES) + | (elf::EM_RISCV, elf::SHT_RISCV_ATTRIBUTES) => { + let attributes = section.attributes(endian, data)?; + Self::read_attributes(index, attributes, sections.len(), symbols.len())? + } + // Some section types that we can't parse but that are safe to copy. + // Lots of types missing, add as needed. We can't default to copying + // everything because some types are not safe to copy. + (elf::EM_ARM, elf::SHT_ARM_EXIDX) + | (elf::EM_IA_64, elf::SHT_IA_64_UNWIND) + | (elf::EM_MIPS, elf::SHT_MIPS_REGINFO) + | (elf::EM_MIPS, elf::SHT_MIPS_DWARF) + | (elf::EM_X86_64, elf::SHT_X86_64_UNWIND) => { + SectionData::Data(section.data(endian, data)?.into()) + } + _ => return Err(Error(format!("Unsupported section type {:x}", other))), + }, + }; + let sh_flags = section.sh_flags(endian).into(); + let sh_link = section.sh_link(endian); + let sh_link_section = if sh_link == 0 { + None + } else { + if sh_link as usize >= sections.len() { + return Err(Error(format!( + "Invalid sh_link {} in section at index {}", + sh_link, index + ))); + } + Some(SectionId(sh_link as usize - 1)) + }; + let sh_info = section.sh_info(endian); + let sh_info_section = if sh_info == 0 || sh_flags & u64::from(elf::SHF_INFO_LINK) == 0 { + None + } else { + if sh_info as usize >= sections.len() { + return Err(Error(format!( + "Invalid sh_info link {} in section at index {}", + sh_info, index + ))); + } + Some(SectionId(sh_info as usize - 1)) + }; + let sh_flags = section.sh_flags(endian).into(); + let sh_addr = section.sh_addr(endian).into(); + if sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + for segment in &mut builder.segments { + if segment.contains_address(sh_addr) { + segment.sections.push(id); + } + } + } + builder.sections.push(Section { + id, + delete: false, + name: name.into(), + sh_type: section.sh_type(endian), + sh_flags, + sh_addr, + sh_offset: section.sh_offset(endian).into(), + sh_size: section.sh_size(endian).into(), + sh_link_section, + sh_info, + sh_info_section, + sh_addralign: section.sh_addralign(endian).into(), + sh_entsize: section.sh_entsize(endian).into(), + data, + }); + } + + Self::read_symbols( + endian, + &symbols, + &mut builder.symbols, + builder.sections.len(), + )?; + Self::read_symbols( + endian, + &dynamic_symbols, + &mut builder.dynamic_symbols, + builder.sections.len(), + )?; + builder.read_gnu_versions(endian, data, §ions, &dynamic_symbols)?; + + Ok(builder) + } + + #[allow(clippy::too_many_arguments)] + fn read_relocations<Elf, Rel, R>( + index: read::SectionIndex, + endian: Elf::Endian, + is_mips64el: bool, + section: &'data Elf::SectionHeader, + rels: &'data [Rel], + link: read::SectionIndex, + symbols: &read::elf::SymbolTable<'data, Elf, R>, + dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>, + ) -> Result<SectionData<'data>> + where + Elf: FileHeader<Endian = Endianness>, + Rel: Copy + Into<Elf::Rela>, + R: ReadRef<'data>, + { + if link == dynamic_symbols.section() { + Self::read_relocations_impl::<Elf, Rel, true>( + index, + endian, + is_mips64el, + rels, + dynamic_symbols.len(), + ) + .map(SectionData::DynamicRelocation) + } else if link.0 == 0 || section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 { + // If there's no link, then none of the relocations may reference symbols. + // Assume that these are dynamic relocations, but don't use the dynamic + // symbol table when parsing. + // + // Additionally, sometimes there is an allocated section that links to + // the static symbol table. We don't currently support this case in general, + // but if none of the relocation entries reference a symbol then it is + // safe to treat it as a dynamic relocation section. + // + // For both of these cases, if there is a reference to a symbol then + // an error will be returned when parsing the relocations. + Self::read_relocations_impl::<Elf, Rel, true>(index, endian, is_mips64el, rels, 0) + .map(SectionData::DynamicRelocation) + } else if link == symbols.section() { + Self::read_relocations_impl::<Elf, Rel, false>( + index, + endian, + is_mips64el, + rels, + symbols.len(), + ) + .map(SectionData::Relocation) + } else { + return Err(Error(format!( + "Invalid sh_link {} in relocation section at index {}", + link.0, index, + ))); + } + } + + fn read_relocations_impl<Elf, Rel, const DYNAMIC: bool>( + index: read::SectionIndex, + endian: Elf::Endian, + is_mips64el: bool, + rels: &'data [Rel], + symbols_len: usize, + ) -> Result<Vec<Relocation<DYNAMIC>>> + where + Elf: FileHeader<Endian = Endianness>, + Rel: Copy + Into<Elf::Rela>, + { + let mut relocations = Vec::new(); + for rel in rels { + let rel = (*rel).into(); + let symbol = if let Some(symbol) = rel.symbol(endian, is_mips64el) { + if symbol.0 >= symbols_len { + return Err(Error(format!( + "Invalid symbol index {} in relocation section at index {}", + symbol, index, + ))); + } + Some(SymbolId(symbol.0 - 1)) + } else { + None + }; + relocations.push(Relocation { + r_offset: rel.r_offset(endian).into(), + symbol, + r_type: rel.r_type(endian, is_mips64el), + r_addend: rel.r_addend(endian).into(), + }); + } + Ok(relocations) + } + + fn read_dynamics<Elf, R>( + endian: Elf::Endian, + dyns: &'data [Elf::Dyn], + strings: read::StringTable<'data, R>, + ) -> Result<SectionData<'data>> + where + Elf: FileHeader<Endian = Endianness>, + R: ReadRef<'data>, + { + let mut dynamics = Vec::with_capacity(dyns.len()); + for d in dyns { + let tag = d.d_tag(endian).into().try_into().map_err(|_| { + Error(format!( + "Unsupported dynamic tag 0x{:x}", + d.d_tag(endian).into() + )) + })?; + if tag == elf::DT_NULL { + break; + } + let val = d.d_val(endian).into(); + dynamics.push(if d.is_string(endian) { + let val = + strings + .get(val.try_into().map_err(|_| { + Error(format!("Unsupported dynamic string 0x{:x}", val)) + })?) + .map_err(|_| Error(format!("Invalid dynamic string 0x{:x}", val)))?; + Dynamic::String { + tag, + val: val.into(), + } + } else { + match tag { + elf::DT_SYMTAB + | elf::DT_STRTAB + | elf::DT_STRSZ + | elf::DT_HASH + | elf::DT_GNU_HASH + | elf::DT_VERSYM + | elf::DT_VERDEF + | elf::DT_VERDEFNUM + | elf::DT_VERNEED + | elf::DT_VERNEEDNUM => Dynamic::Auto { tag }, + _ => Dynamic::Integer { tag, val }, + } + }); + } + Ok(SectionData::Dynamic(dynamics)) + } + + fn read_symbols<Elf, R, const DYNAMIC: bool>( + endian: Elf::Endian, + symbols: &read::elf::SymbolTable<'data, Elf, R>, + builder_symbols: &mut Symbols<'data, DYNAMIC>, + sections_len: usize, + ) -> Result<()> + where + Elf: FileHeader<Endian = Endianness>, + R: ReadRef<'data>, + { + for (index, symbol) in symbols.enumerate().skip(1) { + let id = SymbolId(index.0 - 1); + let section = + if let Some(section_index) = symbols.symbol_section(endian, symbol, index)? { + let section_id = section_index.0.wrapping_sub(1); + if section_id >= sections_len { + return Err(Error::new("Invalid symbol section index")); + } + Some(SectionId(section_id)) + } else { + None + }; + builder_symbols.push(Symbol { + id, + delete: false, + name: symbols.symbol_name(endian, symbol)?.into(), + section, + st_info: symbol.st_info(), + st_other: symbol.st_other(), + st_shndx: symbol.st_shndx(endian), + st_value: symbol.st_value(endian).into(), + st_size: symbol.st_size(endian).into(), + version: VersionId::local(), + version_hidden: false, + }); + } + Ok(()) + } + + fn read_attributes<Elf>( + index: read::SectionIndex, + attributes: read::elf::AttributesSection<'data, Elf>, + sections_len: usize, + symbols_len: usize, + ) -> Result<SectionData<'data>> + where + Elf: FileHeader<Endian = Endianness>, + { + let mut builder_attributes = AttributesSection::new(); + let mut subsections = attributes.subsections()?; + while let Some(subsection) = subsections.next()? { + let mut builder_subsection = AttributesSubsection::new(subsection.vendor().into()); + let mut subsubsections = subsection.subsubsections(); + while let Some(subsubsection) = subsubsections.next()? { + let tag = match subsubsection.tag() { + elf::Tag_File => AttributeTag::File, + elf::Tag_Section => { + let mut tag_sections = Vec::new(); + let mut indices = subsubsection.indices(); + while let Some(index) = indices.next()? { + let index = index as usize; + if index >= sections_len { + return Err(Error(format!( + "Invalid section index {} in attribute", + index + ))); + } + tag_sections.push(SectionId(index - 1)); + } + AttributeTag::Section(tag_sections) + } + elf::Tag_Symbol => { + let mut tag_symbols = Vec::new(); + let mut indices = subsubsection.indices(); + while let Some(index) = indices.next()? { + let index = index as usize; + if index >= symbols_len { + return Err(Error(format!( + "Invalid symbol index {} in attribute", + index + ))); + } + tag_symbols.push(SymbolId(index - 1)); + } + AttributeTag::Symbol(tag_symbols) + } + tag => { + return Err(Error(format!( + "Unsupported attribute tag 0x{:x} in section at index {}", + tag, index, + ))) + } + }; + let data = subsubsection.attributes_data().into(); + builder_subsection + .subsubsections + .push(AttributesSubsubsection { tag, data }); + } + builder_attributes.subsections.push(builder_subsection); + } + Ok(SectionData::Attributes(builder_attributes)) + } + + fn read_gnu_versions<Elf, R>( + &mut self, + endian: Elf::Endian, + data: R, + sections: &read::elf::SectionTable<'data, Elf, R>, + dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>, + ) -> Result<()> + where + Elf: FileHeader<Endian = Endianness>, + R: ReadRef<'data>, + { + let strings = dynamic_symbols.strings(); + let mut ids = HashMap::new(); + ids.insert(0, VersionId::local()); + ids.insert(1, VersionId::global()); + + if let Some((mut verdefs, link)) = sections.gnu_verdef(endian, data)? { + if link != dynamic_symbols.string_section() { + return Err(Error::new("Invalid SHT_GNU_VERDEF section")); + } + while let Some((verdef, mut verdauxs)) = verdefs.next()? { + let flags = verdef.vd_flags.get(endian); + if flags & elf::VER_FLG_BASE != 0 { + if flags != elf::VER_FLG_BASE + || verdef.vd_ndx.get(endian) != 1 + || verdef.vd_cnt.get(endian) != 1 + { + return Err(Error::new("Unsupported VER_FLG_BASE in SHT_GNU_VERDEF")); + } + if self.version_base.is_some() { + return Err(Error::new("Duplicate VER_FLG_BASE in SHT_GNU_VERDEF")); + } + let verdaux = verdauxs.next()?.ok_or_else(|| { + Error::new("Missing name for VER_FLG_BASE in SHT_GNU_VERDEF") + })?; + self.version_base = Some(verdaux.name(endian, strings)?.into()); + continue; + } + + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + let id = self.versions.next_id(); + if ids.insert(index, id).is_some() { + return Err(Error(format!("Duplicate SHT_GNU_VERDEF index {}", index))); + } + + let mut names = Vec::new(); + while let Some(verdaux) = verdauxs.next()? { + names.push(verdaux.name(endian, strings)?.into()); + } + + let data = VersionData::Def(VersionDef { flags, names }); + self.versions.push(Version { + id, + delete: false, + data, + }); + } + } + + if let Some((mut verneeds, link)) = sections.gnu_verneed(endian, data)? { + if link != dynamic_symbols.string_section() { + return Err(Error::new("Invalid SHT_GNU_VERNEED section")); + } + while let Some((verneed, mut vernauxs)) = verneeds.next()? { + let file = VersionFileId(self.version_files.len()); + self.version_files.push(VersionFile { + id: file, + delete: false, + name: verneed.file(endian, strings)?.into(), + }); + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + let id = self.versions.next_id(); + if ids.insert(index, id).is_some() { + return Err(Error(format!("Duplicate SHT_GNU_VERNEED index {}", index))); + } + + let data = VersionData::Need(VersionNeed { + flags: vernaux.vna_flags.get(endian), + name: vernaux.name(endian, strings)?.into(), + file, + }); + self.versions.push(Version { + id, + delete: false, + data, + }); + } + } + } + + if let Some((versyms, link)) = sections.gnu_versym(endian, data)? { + if versyms.len() != dynamic_symbols.len() || link != dynamic_symbols.section() { + return Err(Error::new("Invalid SHT_GNU_VERSYM section")); + } + for (id, versym) in versyms.iter().skip(1).enumerate() { + let index = versym.0.get(endian); + let symbol = self.dynamic_symbols.get_mut(SymbolId(id)); + symbol.version = *ids + .get(&(index & elf::VERSYM_VERSION)) + .ok_or_else(|| Error(format!("Invalid SHT_GNU_VERSYM index {:x}", index)))?; + symbol.version_hidden = index & elf::VERSYM_HIDDEN != 0; + } + } + Ok(()) + } + + /// Write the ELF file to the buffer. + pub fn write(mut self, buffer: &mut dyn write::WritableBuffer) -> Result<()> { + struct SectionOut { + id: SectionId, + name: Option<write::StringId>, + offset: usize, + attributes: Vec<u8>, + } + + struct SymbolOut { + id: SymbolId, + name: Option<write::StringId>, + } + + struct DynamicSymbolOut { + id: DynamicSymbolId, + name: Option<write::StringId>, + hash: Option<u32>, + gnu_hash: Option<u32>, + } + + #[derive(Default, Clone)] + struct VersionFileOut { + versions: Vec<VersionId>, + } + + // TODO: require the caller to do this? + self.delete_orphans(); + self.delete_unused_versions(); + + let mut writer = write::elf::Writer::new(self.endian, self.is_64, buffer); + + // Find metadata sections, and assign section indices. + let mut shstrtab_id = None; + let mut symtab_id = None; + let mut symtab_shndx_id = None; + let mut strtab_id = None; + let mut dynsym_id = None; + let mut dynstr_id = None; + let mut hash_id = None; + let mut gnu_hash_id = None; + let mut gnu_versym_id = None; + let mut gnu_verdef_id = None; + let mut gnu_verneed_id = None; + let mut out_sections = Vec::with_capacity(self.sections.len()); + let mut out_sections_index = vec![None; self.sections.len()]; + if !self.sections.is_empty() { + writer.reserve_null_section_index(); + } + for section in &self.sections { + let index = match §ion.data { + SectionData::Data(_) + | SectionData::UninitializedData(_) + | SectionData::Relocation(_) + | SectionData::DynamicRelocation(_) + | SectionData::Note(_) + | SectionData::Dynamic(_) + | SectionData::Attributes(_) => writer.reserve_section_index(), + SectionData::SectionString => { + if shstrtab_id.is_some() { + return Err(Error::new("Multiple .shstrtab sections")); + } + shstrtab_id = Some(section.id); + writer.reserve_shstrtab_section_index_with_name(§ion.name) + } + SectionData::Symbol => { + if symtab_id.is_some() { + return Err(Error::new("Multiple .symtab sections")); + } + symtab_id = Some(section.id); + writer.reserve_symtab_section_index_with_name(§ion.name) + } + SectionData::SymbolSectionIndex => { + if symtab_shndx_id.is_some() { + return Err(Error::new("Multiple .symtab_shndx sections")); + } + symtab_shndx_id = Some(section.id); + writer.reserve_symtab_shndx_section_index_with_name(§ion.name) + } + SectionData::String => { + if strtab_id.is_some() { + return Err(Error::new("Multiple .strtab sections")); + } + strtab_id = Some(section.id); + writer.reserve_strtab_section_index_with_name(§ion.name) + } + SectionData::DynamicSymbol => { + if dynsym_id.is_some() { + return Err(Error::new("Multiple .dynsym sections")); + } + dynsym_id = Some(section.id); + writer.reserve_dynsym_section_index_with_name(§ion.name) + } + SectionData::DynamicString => { + if dynstr_id.is_some() { + return Err(Error::new("Multiple .dynstr sections")); + } + dynstr_id = Some(section.id); + writer.reserve_dynstr_section_index_with_name(§ion.name) + } + SectionData::Hash => { + if hash_id.is_some() { + return Err(Error::new("Multiple .hash sections")); + } + hash_id = Some(section.id); + writer.reserve_hash_section_index_with_name(§ion.name) + } + SectionData::GnuHash => { + if gnu_hash_id.is_some() { + return Err(Error::new("Multiple .gnu.hash sections")); + } + gnu_hash_id = Some(section.id); + writer.reserve_gnu_hash_section_index_with_name(§ion.name) + } + SectionData::GnuVersym => { + if gnu_versym_id.is_some() { + return Err(Error::new("Multiple .gnu.version sections")); + } + gnu_versym_id = Some(section.id); + writer.reserve_gnu_versym_section_index_with_name(§ion.name) + } + SectionData::GnuVerdef => { + if gnu_verdef_id.is_some() { + return Err(Error::new("Multiple .gnu.version_d sections")); + } + gnu_verdef_id = Some(section.id); + writer.reserve_gnu_verdef_section_index_with_name(§ion.name) + } + SectionData::GnuVerneed => { + if gnu_verneed_id.is_some() { + return Err(Error::new("Multiple .gnu.version_r sections")); + } + gnu_verneed_id = Some(section.id); + writer.reserve_gnu_verneed_section_index_with_name(§ion.name) + } + }; + out_sections_index[section.id.0] = Some(index); + + let name = if section.name.is_empty() { + None + } else { + Some(writer.add_section_name(§ion.name)) + }; + out_sections.push(SectionOut { + id: section.id, + name, + offset: 0, + attributes: Vec::new(), + }); + } + + // Assign dynamic strings. + for section in &self.sections { + if let SectionData::Dynamic(dynamics) = §ion.data { + for dynamic in dynamics { + if let Dynamic::String { val, .. } = dynamic { + writer.add_dynamic_string(val); + } + } + } + } + + // Assign dynamic symbol indices. + let mut out_dynsyms = Vec::with_capacity(self.dynamic_symbols.len()); + // Local symbols must come before global. + let local_symbols = self + .dynamic_symbols + .into_iter() + .filter(|symbol| symbol.st_bind() == elf::STB_LOCAL); + let global_symbols = self + .dynamic_symbols + .into_iter() + .filter(|symbol| symbol.st_bind() != elf::STB_LOCAL); + for symbol in local_symbols.chain(global_symbols) { + let mut name = None; + let mut hash = None; + let mut gnu_hash = None; + if !symbol.name.is_empty() { + name = Some(writer.add_dynamic_string(&symbol.name)); + if hash_id.is_some() { + hash = Some(elf::hash(&symbol.name)); + } + if gnu_hash_id.is_some() + && (symbol.section.is_some() || symbol.st_shndx != elf::SHN_UNDEF) + { + gnu_hash = Some(elf::gnu_hash(&symbol.name)); + } + } + out_dynsyms.push(DynamicSymbolOut { + id: symbol.id, + name, + hash, + gnu_hash, + }); + } + let num_local_dynamic = out_dynsyms + .iter() + .take_while(|sym| self.dynamic_symbols.get(sym.id).st_bind() == elf::STB_LOCAL) + .count(); + // We must sort for GNU hash before allocating symbol indices. + let mut gnu_hash_symbol_count = 0; + if gnu_hash_id.is_some() { + if self.gnu_hash_bucket_count == 0 { + return Err(Error::new(".gnu.hash bucket count is zero")); + } + // TODO: recalculate bucket_count? + out_dynsyms[num_local_dynamic..].sort_by_key(|sym| match sym.gnu_hash { + None => (0, 0), + Some(hash) => (1, hash % self.gnu_hash_bucket_count), + }); + gnu_hash_symbol_count = out_dynsyms + .iter() + .skip(num_local_dynamic) + .skip_while(|sym| sym.gnu_hash.is_none()) + .count() as u32; + } + let mut out_dynsyms_index = vec![None; self.dynamic_symbols.len()]; + if dynsym_id.is_some() { + writer.reserve_null_dynamic_symbol_index(); + } + for out_dynsym in &mut out_dynsyms { + out_dynsyms_index[out_dynsym.id.0] = Some(writer.reserve_dynamic_symbol_index()); + } + + // Hash parameters. + let hash_index_base = 1; // Null symbol. + let hash_chain_count = hash_index_base + out_dynsyms.len() as u32; + + // GNU hash parameters. + let gnu_hash_index_base = if gnu_hash_symbol_count == 0 { + 0 + } else { + out_dynsyms.len() as u32 - gnu_hash_symbol_count + }; + let gnu_hash_symbol_base = gnu_hash_index_base + 1; // Null symbol. + + // Assign symbol indices. + let mut out_syms = Vec::with_capacity(self.symbols.len()); + // Local symbols must come before global. + let local_symbols = self + .symbols + .into_iter() + .filter(|symbol| symbol.st_bind() == elf::STB_LOCAL); + let global_symbols = self + .symbols + .into_iter() + .filter(|symbol| symbol.st_bind() != elf::STB_LOCAL); + for symbol in local_symbols.chain(global_symbols) { + let name = if symbol.name.is_empty() { + None + } else { + Some(writer.add_string(&symbol.name)) + }; + + out_syms.push(SymbolOut { + id: symbol.id, + name, + }); + } + let num_local = out_syms + .iter() + .take_while(|sym| self.symbols.get(sym.id).st_bind() == elf::STB_LOCAL) + .count(); + let mut out_syms_index = vec![None; self.symbols.len()]; + if symtab_id.is_some() { + writer.reserve_null_symbol_index(); + } + for out_sym in out_syms.iter_mut() { + out_syms_index[out_sym.id.0] = Some(writer.reserve_symbol_index(None)); + } + + // Count the versions and add version strings. + let mut verdef_count = 0; + let mut verdaux_count = 0; + let mut verdef_shared_base = false; + let mut verneed_count = 0; + let mut vernaux_count = 0; + let mut out_version_files = vec![VersionFileOut::default(); self.version_files.len()]; + if let Some(version_base) = &self.version_base { + verdef_count += 1; + verdaux_count += 1; + writer.add_dynamic_string(version_base); + } + for version in &self.versions { + match &version.data { + VersionData::Def(def) => { + if def.is_shared(verdef_count, self.version_base.as_ref()) { + verdef_shared_base = true; + } else { + verdaux_count += def.names.len(); + for name in &def.names { + writer.add_dynamic_string(name); + } + } + verdef_count += 1; + } + VersionData::Need(need) => { + vernaux_count += 1; + writer.add_dynamic_string(&need.name); + out_version_files[need.file.0].versions.push(version.id); + } + } + } + for file in &self.version_files { + verneed_count += 1; + writer.add_dynamic_string(&file.name); + } + + // Build the attributes sections. + for out_section in &mut out_sections { + let SectionData::Attributes(attributes) = &self.sections.get(out_section.id).data + else { + continue; + }; + if attributes.subsections.is_empty() { + continue; + } + let mut writer = writer.attributes_writer(); + for subsection in &attributes.subsections { + writer.start_subsection(&subsection.vendor); + for subsubsection in &subsection.subsubsections { + writer.start_subsubsection(subsubsection.tag.tag()); + match &subsubsection.tag { + AttributeTag::File => {} + AttributeTag::Section(sections) => { + for id in sections { + if let Some(index) = out_sections_index[id.0] { + writer.write_subsubsection_index(index.0); + } + } + writer.write_subsubsection_index(0); + } + AttributeTag::Symbol(symbols) => { + for id in symbols { + if let Some(index) = out_syms_index[id.0] { + writer.write_subsubsection_index(index.0); + } + } + writer.write_subsubsection_index(0); + } + } + writer.write_subsubsection_attributes(&subsubsection.data); + writer.end_subsubsection(); + } + writer.end_subsection(); + } + out_section.attributes = writer.data(); + } + + // TODO: support section headers in strtab + if shstrtab_id.is_none() && !out_sections.is_empty() { + return Err(Error::new(".shstrtab section is needed but not present")); + } + if symtab_id.is_none() && !out_syms.is_empty() { + return Err(Error::new(".symtab section is needed but not present")); + } + if symtab_shndx_id.is_none() && writer.symtab_shndx_needed() { + return Err(Error::new( + ".symtab.shndx section is needed but not present", + )); + } else if symtab_shndx_id.is_some() { + writer.require_symtab_shndx(); + } + if strtab_id.is_none() && writer.strtab_needed() { + return Err(Error::new(".strtab section is needed but not present")); + } else if strtab_id.is_some() { + writer.require_strtab(); + } + if dynsym_id.is_none() && !out_dynsyms.is_empty() { + return Err(Error::new(".dynsym section is needed but not present")); + } + if dynstr_id.is_none() && writer.dynstr_needed() { + return Err(Error::new(".dynstr section is needed but not present")); + } else if dynstr_id.is_some() { + writer.require_dynstr(); + } + if gnu_verdef_id.is_none() && verdef_count > 0 { + return Err(Error::new( + ".gnu.version_d section is needed but not present", + )); + } + if gnu_verneed_id.is_none() && verneed_count > 0 { + return Err(Error::new( + ".gnu.version_r section is needed but not present", + )); + } + + // Start reserving file ranges. + writer.reserve_file_header(); + + let mut dynsym_addr = None; + let mut dynstr_addr = None; + let mut hash_addr = None; + let mut gnu_hash_addr = None; + let mut versym_addr = None; + let mut verdef_addr = None; + let mut verneed_addr = None; + + if !self.segments.is_empty() { + // TODO: support program headers in other locations. + if self.header.e_phoff != writer.reserved_len() as u64 { + return Err(Error(format!( + "Unsupported e_phoff value 0x{:x}", + self.header.e_phoff + ))); + } + writer.reserve_program_headers(self.segments.count() as u32); + } + + let mut alloc_sections = Vec::new(); + if !self.segments.is_empty() { + // Reserve alloc sections at original offsets. + alloc_sections = out_sections + .iter() + .enumerate() + .filter_map(|(index, out_section)| { + let section = self.sections.get(out_section.id); + if section.is_alloc() { + Some(index) + } else { + None + } + }) + .collect(); + // The data for alloc sections may need to be written in a different order + // from their section headers. + alloc_sections.sort_by_key(|index| { + let section = &self.sections.get(out_sections[*index].id); + // Empty sections need to come before other sections at the same offset. + (section.sh_offset, section.sh_size) + }); + for index in &alloc_sections { + let out_section = &mut out_sections[*index]; + let section = &self.sections.get(out_section.id); + + if section.sh_type == elf::SHT_NOBITS { + // sh_offset is meaningless for SHT_NOBITS, so preserve the input + // value without checking it. + out_section.offset = section.sh_offset as usize; + continue; + } + + if section.sh_offset < writer.reserved_len() as u64 { + return Err(Error(format!( + "Unsupported sh_offset value 0x{:x} for section '{}', expected at least 0x{:x}", + section.sh_offset, + section.name, + writer.reserved_len(), + ))); + } + // The input sh_offset needs to be preserved so that offsets in program + // headers are correct. + writer.reserve_until(section.sh_offset as usize); + out_section.offset = match §ion.data { + SectionData::Data(data) => { + writer.reserve(data.len(), section.sh_addralign as usize) + } + SectionData::DynamicRelocation(relocations) => writer + .reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA), + SectionData::Note(data) => { + writer.reserve(data.len(), section.sh_addralign as usize) + } + SectionData::Dynamic(dynamics) => writer.reserve_dynamics(1 + dynamics.len()), + SectionData::DynamicSymbol => { + dynsym_addr = Some(section.sh_addr); + writer.reserve_dynsym() + } + SectionData::DynamicString => { + dynstr_addr = Some(section.sh_addr); + writer.reserve_dynstr() + } + SectionData::Hash => { + hash_addr = Some(section.sh_addr); + writer.reserve_hash(self.hash_bucket_count, hash_chain_count) + } + SectionData::GnuHash => { + gnu_hash_addr = Some(section.sh_addr); + writer.reserve_gnu_hash( + self.gnu_hash_bloom_count, + self.gnu_hash_bucket_count, + gnu_hash_symbol_count, + ) + } + SectionData::GnuVersym => { + versym_addr = Some(section.sh_addr); + writer.reserve_gnu_versym() + } + SectionData::GnuVerdef => { + verdef_addr = Some(section.sh_addr); + writer.reserve_gnu_verdef(verdef_count, verdaux_count) + } + SectionData::GnuVerneed => { + verneed_addr = Some(section.sh_addr); + writer.reserve_gnu_verneed(verneed_count, vernaux_count) + } + _ => { + return Err(Error(format!( + "Unsupported alloc section type {:x} for section '{}'", + section.sh_type, section.name, + ))); + } + }; + if out_section.offset as u64 != section.sh_offset { + return Err(Error(format!( + "Unaligned sh_offset value 0x{:x} for section '{}', expected 0x{:x}", + section.sh_offset, section.name, out_section.offset, + ))); + } + } + } + + // Reserve non-alloc sections at any offset. + for out_section in &mut out_sections { + let section = self.sections.get(out_section.id); + if !self.segments.is_empty() && section.is_alloc() { + continue; + } + out_section.offset = match §ion.data { + SectionData::Data(data) => { + writer.reserve(data.len(), section.sh_addralign as usize) + } + SectionData::UninitializedData(_) => writer.reserved_len(), + SectionData::Note(data) => { + writer.reserve(data.len(), section.sh_addralign as usize) + } + SectionData::Attributes(_) => { + writer.reserve(out_section.attributes.len(), section.sh_addralign as usize) + } + // These are handled elsewhere. + SectionData::Relocation(_) + | SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String => { + continue; + } + _ => { + return Err(Error(format!( + "Unsupported non-alloc section type {:x}", + section.sh_type + ))); + } + }; + } + + writer.reserve_symtab(); + writer.reserve_symtab_shndx(); + writer.reserve_strtab(); + + // Reserve non-alloc relocations. + for out_section in &mut out_sections { + let section = self.sections.get(out_section.id); + if !self.segments.is_empty() && section.is_alloc() { + continue; + } + let SectionData::Relocation(relocations) = §ion.data else { + continue; + }; + out_section.offset = + writer.reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA); + } + + writer.reserve_shstrtab(); + writer.reserve_section_headers(); + + // Start writing. + writer.write_file_header(&write::elf::FileHeader { + os_abi: self.header.os_abi, + abi_version: self.header.abi_version, + e_type: self.header.e_type, + e_machine: self.header.e_machine, + e_entry: self.header.e_entry, + e_flags: self.header.e_flags, + })?; + + if !self.segments.is_empty() { + writer.write_align_program_headers(); + for segment in &self.segments { + writer.write_program_header(&write::elf::ProgramHeader { + p_type: segment.p_type, + p_flags: segment.p_flags, + p_offset: segment.p_offset, + p_vaddr: segment.p_vaddr, + p_paddr: segment.p_paddr, + p_filesz: segment.p_filesz, + p_memsz: segment.p_memsz, + p_align: segment.p_align, + }); + } + } + + // Write alloc sections. + if !self.segments.is_empty() { + for index in &alloc_sections { + let out_section = &mut out_sections[*index]; + let section = self.sections.get(out_section.id); + + if section.sh_type == elf::SHT_NOBITS { + continue; + } + + writer.pad_until(out_section.offset); + match §ion.data { + SectionData::Data(data) => { + writer.write(data); + } + SectionData::DynamicRelocation(relocations) => { + for rel in relocations { + let r_sym = if let Some(symbol) = rel.symbol { + out_dynsyms_index[symbol.0].unwrap().0 + } else { + 0 + }; + writer.write_relocation( + section.sh_type == elf::SHT_RELA, + &write::elf::Rel { + r_offset: rel.r_offset, + r_sym, + r_type: rel.r_type, + r_addend: rel.r_addend, + }, + ); + } + } + SectionData::Note(data) => { + writer.write(data); + } + SectionData::Dynamic(dynamics) => { + for d in dynamics { + match *d { + Dynamic::Auto { tag } => { + // TODO: support more values + let val = match tag { + elf::DT_SYMTAB => dynsym_addr.ok_or(Error::new( + "Missing .dynsym section for DT_SYMTAB", + ))?, + elf::DT_STRTAB => dynstr_addr.ok_or(Error::new( + "Missing .dynstr section for DT_STRTAB", + ))?, + elf::DT_STRSZ => writer.dynstr_len() as u64, + elf::DT_HASH => hash_addr.ok_or(Error::new( + "Missing .hash section for DT_HASH", + ))?, + elf::DT_GNU_HASH => gnu_hash_addr.ok_or(Error::new( + "Missing .gnu.hash section for DT_GNU_HASH", + ))?, + elf::DT_VERSYM => versym_addr.ok_or(Error::new( + "Missing .gnu.version section for DT_VERSYM", + ))?, + elf::DT_VERDEF => verdef_addr.ok_or(Error::new( + "Missing .gnu.version_d section for DT_VERDEF", + ))?, + elf::DT_VERDEFNUM => verdef_count as u64, + elf::DT_VERNEED => verneed_addr.ok_or(Error::new( + "Missing .gnu.version_r section for DT_VERNEED", + ))?, + elf::DT_VERNEEDNUM => verneed_count as u64, + _ => { + return Err(Error(format!( + "Cannot generate value for dynamic tag 0x{:x}", + tag + ))) + } + }; + writer.write_dynamic(tag, val); + } + Dynamic::Integer { tag, val } => { + writer.write_dynamic(tag, val); + } + Dynamic::String { tag, ref val } => { + let val = writer.get_dynamic_string(val); + writer.write_dynamic_string(tag, val); + } + } + } + writer.write_dynamic(elf::DT_NULL, 0); + } + SectionData::DynamicSymbol => { + writer.write_null_dynamic_symbol(); + for out_dynsym in &out_dynsyms { + let symbol = self.dynamic_symbols.get(out_dynsym.id); + let section = + symbol.section.map(|id| out_sections_index[id.0].unwrap()); + writer.write_dynamic_symbol(&write::elf::Sym { + name: out_dynsym.name, + section, + st_info: symbol.st_info, + st_other: symbol.st_other, + st_shndx: symbol.st_shndx, + st_value: symbol.st_value, + st_size: symbol.st_size, + }); + } + } + SectionData::DynamicString => { + writer.write_dynstr(); + } + SectionData::Hash => { + if self.hash_bucket_count == 0 { + return Err(Error::new(".hash bucket count is zero")); + } + writer.write_hash(self.hash_bucket_count, hash_chain_count, |index| { + out_dynsyms + .get(index.checked_sub(hash_index_base)? as usize)? + .hash + }); + } + SectionData::GnuHash => { + if self.gnu_hash_bucket_count == 0 { + return Err(Error::new(".gnu.hash bucket count is zero")); + } + writer.write_gnu_hash( + gnu_hash_symbol_base, + self.gnu_hash_bloom_shift, + self.gnu_hash_bloom_count, + self.gnu_hash_bucket_count, + gnu_hash_symbol_count, + |index| { + out_dynsyms[(gnu_hash_index_base + index) as usize] + .gnu_hash + .unwrap() + }, + ); + } + SectionData::GnuVersym => { + writer.write_null_gnu_versym(); + for out_dynsym in &out_dynsyms { + let symbol = self.dynamic_symbols.get(out_dynsym.id); + let mut index = symbol.version.0 as u16; + if symbol.version_hidden { + index |= elf::VERSYM_HIDDEN; + } + writer.write_gnu_versym(index); + } + } + SectionData::GnuVerdef => { + writer.write_align_gnu_verdef(); + if let Some(version_base) = &self.version_base { + let verdef = write::elf::Verdef { + version: elf::VER_DEF_CURRENT, + flags: elf::VER_FLG_BASE, + index: 1, + aux_count: 1, + name: writer.get_dynamic_string(version_base), + }; + if verdef_shared_base { + writer.write_gnu_verdef_shared(&verdef); + } else { + writer.write_gnu_verdef(&verdef); + } + } + for version in &self.versions { + if let VersionData::Def(def) = &version.data { + let mut names = def.names.iter(); + let name = names.next().ok_or_else(|| { + Error(format!("Missing SHT_GNU_VERDEF name {}", version.id.0)) + })?; + writer.write_gnu_verdef(&write::elf::Verdef { + version: elf::VER_DEF_CURRENT, + flags: def.flags, + index: version.id.0 as u16, + aux_count: def.names.len() as u16, + name: writer.get_dynamic_string(name), + }); + for name in names { + writer.write_gnu_verdaux(writer.get_dynamic_string(name)); + } + } + } + } + SectionData::GnuVerneed => { + writer.write_align_gnu_verneed(); + for file in &self.version_files { + let out_file = &out_version_files[file.id.0]; + if out_file.versions.is_empty() { + continue; + } + writer.write_gnu_verneed(&write::elf::Verneed { + version: elf::VER_NEED_CURRENT, + aux_count: out_file.versions.len() as u16, + file: writer.get_dynamic_string(&file.name), + }); + for id in &out_file.versions { + let version = self.versions.get(*id); + // This will always match. + if let VersionData::Need(need) = &version.data { + debug_assert_eq!(*id, version.id); + writer.write_gnu_vernaux(&write::elf::Vernaux { + flags: need.flags, + index: version.id.0 as u16, + name: writer.get_dynamic_string(&need.name), + }); + } + } + } + } + _ => { + return Err(Error(format!( + "Unsupported alloc section type {:x}", + section.sh_type + ))); + } + } + } + } + + // Write non-alloc sections. + for out_section in &mut out_sections { + let section = self.sections.get(out_section.id); + if !self.segments.is_empty() && section.is_alloc() { + continue; + } + match §ion.data { + SectionData::Data(data) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(out_section.offset, writer.len()); + writer.write(data); + } + SectionData::UninitializedData(_) => { + // Nothing to do. + } + SectionData::Note(data) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(out_section.offset, writer.len()); + writer.write(data); + } + SectionData::Attributes(_) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(out_section.offset, writer.len()); + writer.write(&out_section.attributes); + } + // These are handled elsewhere. + SectionData::Relocation(_) + | SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String => {} + _ => { + return Err(Error(format!( + "Unsupported non-alloc section type {:x}", + section.sh_type + ))); + } + } + } + + writer.write_null_symbol(); + for out_sym in &out_syms { + let symbol = self.symbols.get(out_sym.id); + let section = symbol.section.map(|id| out_sections_index[id.0].unwrap()); + writer.write_symbol(&write::elf::Sym { + name: out_sym.name, + section, + st_info: symbol.st_info, + st_other: symbol.st_other, + st_shndx: symbol.st_shndx, + st_value: symbol.st_value, + st_size: symbol.st_size, + }); + } + writer.write_symtab_shndx(); + writer.write_strtab(); + + // Write non-alloc relocations. + for section in &self.sections { + if !self.segments.is_empty() && section.is_alloc() { + continue; + } + let SectionData::Relocation(relocations) = §ion.data else { + continue; + }; + writer.write_align_relocation(); + for rel in relocations { + let r_sym = if let Some(id) = rel.symbol { + out_syms_index[id.0].unwrap().0 + } else { + 0 + }; + writer.write_relocation( + section.sh_type == elf::SHT_RELA, + &write::elf::Rel { + r_offset: rel.r_offset, + r_sym, + r_type: rel.r_type, + r_addend: rel.r_addend, + }, + ); + } + } + + writer.write_shstrtab(); + + writer.write_null_section_header(); + for out_section in &out_sections { + let section = self.sections.get(out_section.id); + match §ion.data { + SectionData::Data(_) + | SectionData::UninitializedData(_) + | SectionData::Relocation(_) + | SectionData::DynamicRelocation(_) + | SectionData::Note(_) + | SectionData::Dynamic(_) + | SectionData::Attributes(_) => { + let sh_size = match §ion.data { + SectionData::Data(data) => data.len() as u64, + SectionData::UninitializedData(len) => *len, + SectionData::Relocation(relocations) => { + (relocations.len() + * self.class().rel_size(section.sh_type == elf::SHT_RELA)) + as u64 + } + SectionData::DynamicRelocation(relocations) => { + (relocations.len() + * self.class().rel_size(section.sh_type == elf::SHT_RELA)) + as u64 + } + SectionData::Note(data) => data.len() as u64, + SectionData::Dynamic(dynamics) => { + ((1 + dynamics.len()) * self.class().dyn_size()) as u64 + } + SectionData::Attributes(_) => out_section.attributes.len() as u64, + _ => { + return Err(Error(format!( + "Unimplemented size for section type {:x}", + section.sh_type + ))) + } + }; + let sh_link = if let Some(id) = section.sh_link_section { + if let Some(index) = out_sections_index[id.0] { + index.0 + } else { + return Err(Error(format!( + "Invalid sh_link from section '{}' to deleted section '{}'", + section.name, + self.sections.get(id).name, + ))); + } + } else { + 0 + }; + let sh_info = if let Some(id) = section.sh_info_section { + if let Some(index) = out_sections_index[id.0] { + index.0 + } else { + return Err(Error(format!( + "Invalid sh_info link from section '{}' to deleted section '{}'", + section.name, + self.sections.get(id).name, + ))); + } + } else { + section.sh_info + }; + writer.write_section_header(&write::elf::SectionHeader { + name: out_section.name, + sh_type: section.sh_type, + sh_flags: section.sh_flags, + sh_addr: section.sh_addr, + sh_offset: out_section.offset as u64, + sh_size, + sh_link, + sh_info, + sh_addralign: section.sh_addralign, + sh_entsize: section.sh_entsize, + }); + } + SectionData::SectionString => { + writer.write_shstrtab_section_header(); + } + SectionData::Symbol => { + writer.write_symtab_section_header(1 + num_local as u32); + } + SectionData::SymbolSectionIndex => { + writer.write_symtab_shndx_section_header(); + } + SectionData::String => { + writer.write_strtab_section_header(); + } + SectionData::DynamicString => { + writer.write_dynstr_section_header(section.sh_addr); + } + SectionData::DynamicSymbol => { + writer + .write_dynsym_section_header(section.sh_addr, 1 + num_local_dynamic as u32); + } + SectionData::Hash => { + writer.write_hash_section_header(section.sh_addr); + } + SectionData::GnuHash => { + writer.write_gnu_hash_section_header(section.sh_addr); + } + SectionData::GnuVersym => { + writer.write_gnu_versym_section_header(section.sh_addr); + } + SectionData::GnuVerdef => { + writer.write_gnu_verdef_section_header(section.sh_addr); + } + SectionData::GnuVerneed => { + writer.write_gnu_verneed_section_header(section.sh_addr); + } + } + } + debug_assert_eq!(writer.reserved_len(), writer.len()); + Ok(()) + } + + /// Delete segments, symbols, relocations, and dynamics that refer + /// to deleted items. + /// + /// This calls `delete_orphan_segments`, `delete_orphan_symbols`, + /// `delete_orphan_relocations`, and `delete_orphan_dynamics`. + pub fn delete_orphans(&mut self) { + self.delete_orphan_segments(); + self.delete_orphan_symbols(); + self.delete_orphan_relocations(); + self.delete_orphan_dynamics(); + } + + /// Set the delete flag for segments that only refer to deleted sections. + pub fn delete_orphan_segments(&mut self) { + let sections = &self.sections; + for segment in &mut self.segments { + // We only delete segments that have become empty due to section deletions. + if segment.sections.is_empty() { + continue; + } + segment.sections.retain(|id| !sections.get(*id).delete); + segment.delete = segment.sections.is_empty(); + } + } + + /// Set the delete flag for symbols that refer to deleted sections. + pub fn delete_orphan_symbols(&mut self) { + for symbol in &mut self.symbols { + if let Some(section) = symbol.section { + if self.sections.get_mut(section).delete { + symbol.delete = true; + } + } + } + for symbol in &mut self.dynamic_symbols { + if let Some(section) = symbol.section { + if self.sections.get_mut(section).delete { + symbol.delete = true; + } + } + } + } + + /// Delete relocations that refer to deleted symbols. + pub fn delete_orphan_relocations(&mut self) { + let symbols = &self.symbols; + let dynamic_symbols = &self.dynamic_symbols; + for section in &mut self.sections { + match &mut section.data { + SectionData::Relocation(relocations) => { + relocations.retain(|relocation| match relocation.symbol { + None => true, + Some(id) => !symbols.get(id).delete, + }); + } + SectionData::DynamicRelocation(relocations) => { + relocations.retain(|relocation| match relocation.symbol { + None => true, + Some(id) => !dynamic_symbols.get(id).delete, + }); + } + _ => {} + } + } + } + + /// Delete dynamic entries that refer to deleted sections. + pub fn delete_orphan_dynamics(&mut self) { + let mut have_dynsym = false; + let mut have_dynstr = false; + let mut have_hash = false; + let mut have_gnu_hash = false; + let mut have_versym = false; + let mut have_verdef = false; + let mut have_verneed = false; + for section in &self.sections { + match §ion.data { + SectionData::DynamicSymbol => have_dynsym = true, + SectionData::DynamicString => have_dynstr = true, + SectionData::Hash => have_hash = true, + SectionData::GnuHash => have_gnu_hash = true, + SectionData::GnuVersym => have_versym = true, + SectionData::GnuVerdef => have_verdef = true, + SectionData::GnuVerneed => have_verneed = true, + _ => {} + } + } + for section in &mut self.sections { + if let SectionData::Dynamic(dynamics) = &mut section.data { + dynamics.retain(|dynamic| match dynamic { + Dynamic::Auto { + tag: elf::DT_SYMTAB, + } => have_dynsym, + Dynamic::Auto { + tag: elf::DT_STRTAB, + } + | Dynamic::Auto { tag: elf::DT_STRSZ } => have_dynstr, + Dynamic::Auto { tag: elf::DT_HASH } => have_hash, + Dynamic::Auto { + tag: elf::DT_GNU_HASH, + } => have_gnu_hash, + Dynamic::Auto { + tag: elf::DT_VERSYM, + } => have_versym, + Dynamic::Auto { + tag: elf::DT_VERNEED, + } + | Dynamic::Auto { + tag: elf::DT_VERNEEDNUM, + } => have_verneed, + Dynamic::Auto { + tag: elf::DT_VERDEF, + } + | Dynamic::Auto { + tag: elf::DT_VERDEFNUM, + } => have_verdef, + _ => true, + }); + } + } + } + + /// Delete unused GNU version entries. + pub fn delete_unused_versions(&mut self) { + let mut version_used = vec![false; self.versions.len() + VERSION_ID_BASE]; + for symbol in &self.dynamic_symbols { + version_used[symbol.version.0] = true; + } + let mut version_file_used = vec![false; self.version_files.len()]; + for version in &mut self.versions { + if let VersionData::Need(need) = &version.data { + // This is a dummy version that is required if DT_RELR is used. + if need.name.as_slice() == b"GLIBC_ABI_DT_RELR" { + version_used[version.id.0] = true; + } + } + if !version_used[version.id.0] { + version.delete = true; + continue; + } + if let VersionData::Need(need) = &version.data { + version_file_used[need.file.0] = true; + } + } + for file in &mut self.version_files { + if !version_file_used[file.id.0] { + file.delete = true; + } + } + } + + /// Return the ELF file class that will be written. + /// + /// This can be useful for calculating sizes. + pub fn class(&self) -> write::elf::Class { + write::elf::Class { is_64: self.is_64 } + } + + /// Calculate the size of the file header. + pub fn file_header_size(&self) -> usize { + self.class().file_header_size() + } + + /// Calculate the size of the program headers. + pub fn program_headers_size(&self) -> usize { + self.segments.count() * self.class().program_header_size() + } + + /// Calculate the size of the dynamic symbol table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn dynamic_symbol_size(&self) -> usize { + (1 + self.dynamic_symbols.count()) * self.class().sym_size() + } + + /// Calculate the size of the dynamic string table. + /// + /// This adds all of the currently used dynamic strings to a string table, + /// calculates the size of the string table, and discards the string table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn dynamic_string_size(&self) -> usize { + let mut dynstr = write::string::StringTable::default(); + for section in &self.sections { + if let SectionData::Dynamic(dynamics) = §ion.data { + for dynamic in dynamics { + if let Dynamic::String { val, .. } = dynamic { + dynstr.add(val); + } + } + } + } + for symbol in &self.dynamic_symbols { + dynstr.add(&symbol.name); + } + if let Some(version_base) = &self.version_base { + dynstr.add(version_base); + } + for version in &self.versions { + match &version.data { + VersionData::Def(def) => { + for name in &def.names { + dynstr.add(name); + } + } + VersionData::Need(need) => { + dynstr.add(&need.name); + } + } + } + for file in &self.version_files { + dynstr.add(&file.name); + } + dynstr.size(1) + } + + /// Calculate the size of the hash table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn hash_size(&self) -> usize { + let chain_count = 1 + self.dynamic_symbols.count(); + self.class() + .hash_size(self.hash_bucket_count, chain_count as u32) + } + + /// Calculate the size of the GNU hash table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn gnu_hash_size(&self) -> usize { + let symbol_count = self.dynamic_symbols.count_defined(); + self.class().gnu_hash_size( + self.gnu_hash_bloom_count, + self.gnu_hash_bucket_count, + symbol_count as u32, + ) + } + + /// Calculate the size of the GNU symbol version section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_versym_size(&self) -> usize { + let symbol_count = 1 + self.dynamic_symbols.count(); + self.class().gnu_versym_size(symbol_count) + } + + /// Calculate the size of the GNU version definition section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_verdef_size(&self) -> usize { + let mut verdef_count = 0; + let mut verdaux_count = 0; + if self.version_base.is_some() { + verdef_count += 1; + verdaux_count += 1; + } + for version in &self.versions { + if let VersionData::Def(def) = &version.data { + if !def.is_shared(verdef_count, self.version_base.as_ref()) { + verdaux_count += def.names.len(); + } + verdef_count += 1; + } + } + self.class().gnu_verdef_size(verdef_count, verdaux_count) + } + + /// Calculate the size of the GNU version dependency section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_verneed_size(&self) -> usize { + let verneed_count = self.version_files.count(); + let mut vernaux_count = 0; + for version in &self.versions { + if let VersionData::Need(_) = &version.data { + vernaux_count += 1; + } + } + self.class().gnu_verneed_size(verneed_count, vernaux_count) + } + + /// Calculate the memory size of a section. + /// + /// Returns 0 for sections that are deleted or aren't allocated. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn section_size(&self, section: &Section<'_>) -> usize { + if section.delete || !section.is_alloc() { + return 0; + } + match §ion.data { + SectionData::Data(data) => data.len(), + SectionData::UninitializedData(len) => *len as usize, + SectionData::Relocation(relocations) => { + relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA) + } + SectionData::DynamicRelocation(relocations) => { + relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA) + } + SectionData::Note(data) => data.len(), + SectionData::Dynamic(dynamics) => (1 + dynamics.len()) * self.class().dyn_size(), + SectionData::DynamicString => self.dynamic_string_size(), + SectionData::DynamicSymbol => self.dynamic_symbol_size(), + SectionData::Hash => self.hash_size(), + SectionData::GnuHash => self.gnu_hash_size(), + SectionData::GnuVersym => self.gnu_versym_size(), + SectionData::GnuVerdef => self.gnu_verdef_size(), + SectionData::GnuVerneed => self.gnu_verneed_size(), + // None of these should be allocated. + SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String + | SectionData::Attributes(_) => 0, + } + } + + /// Set the `sh_size` field for every allocated section. + /// + /// This is useful to call prior to doing memory layout. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn set_section_sizes(&mut self) { + for id in (0..self.sections.len()).map(SectionId) { + let section = self.sections.get(id); + if section.delete || !section.is_alloc() { + continue; + } + self.sections.get_mut(id).sh_size = self.section_size(section) as u64; + } + } + + /// Find the section containing the dynamic table. + /// + /// This uses the `PT_DYNAMIC` program header to find the dynamic section. + pub fn dynamic_section(&self) -> Option<SectionId> { + let segment = self + .segments + .iter() + .find(|segment| segment.p_type == elf::PT_DYNAMIC)?; + // TODO: handle multiple sections in the segment? + segment.sections.iter().copied().next() + } + + /// Find the dynamic table entries. + /// + /// This uses the `PT_DYNAMIC` program header to find the dynamic section, + pub fn dynamic_data(&self) -> Option<&[Dynamic<'data>]> { + let section = self.dynamic_section()?; + match &self.sections.get(section).data { + SectionData::Dynamic(dynamics) => Some(dynamics), + _ => None, + } + } + + /// Find the dynamic table entries. + /// + /// This uses the `PT_DYNAMIC` program header to find the dynamic section, + pub fn dynamic_data_mut(&mut self) -> Option<&mut Vec<Dynamic<'data>>> { + let section = self.dynamic_section()?; + match &mut self.sections.get_mut(section).data { + SectionData::Dynamic(dynamics) => Some(dynamics), + _ => None, + } + } + + /// Find the section containing the interpreter path. + /// + /// This uses the `PT_INTERP` program header to find the interp section. + pub fn interp_section(&self) -> Option<SectionId> { + let segment = self + .segments + .iter() + .find(|segment| segment.p_type == elf::PT_INTERP)?; + // TODO: handle multiple sections in the segment? + segment.sections.iter().copied().next() + } + + /// Find the interpreter path. + /// + /// This uses the `PT_INTERP` program header to find the interp section. + pub fn interp_data(&self) -> Option<&[u8]> { + let section = self.interp_section()?; + match &self.sections.get(section).data { + SectionData::Data(data) => Some(data), + _ => None, + } + } + + /// Find the interpreter path. + /// + /// This uses the `PT_INTERP` program header to find the interp section. + pub fn interp_data_mut(&mut self) -> Option<&mut Bytes<'data>> { + let section = self.interp_section()?; + match &mut self.sections.get_mut(section).data { + SectionData::Data(data) => Some(data), + _ => None, + } + } +} + +/// ELF file header. +/// +/// This corresponds to fields in [`elf::FileHeader32`] or [`elf::FileHeader64`]. +/// This only contains the ELF file header fields that can be modified. +/// The other fields are automatically calculated. +#[derive(Debug, Default)] +pub struct Header { + /// The OS ABI field in the file header. + /// + /// One of the `ELFOSABI*` constants. + pub os_abi: u8, + /// The ABI version field in the file header. + /// + /// The meaning of this field depends on the `os_abi` value. + pub abi_version: u8, + /// The object file type in the file header. + /// + /// One of the `ET_*` constants. + pub e_type: u16, + /// The architecture in the file header. + /// + /// One of the `EM_*` constants. + pub e_machine: u16, + /// Entry point virtual address in the file header. + pub e_entry: u64, + /// The processor-specific flags in the file header. + /// + /// A combination of the `EF_*` constants. + pub e_flags: u32, + /// The file offset of the program header table. + /// + /// Writing will fail if the program header table cannot be placed at this offset. + pub e_phoff: u64, +} + +/// An ID for referring to a segment in [`Segments`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct SegmentId(usize); + +impl fmt::Debug for SegmentId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SegmentId({})", self.0) + } +} + +impl Id for SegmentId { + fn index(&self) -> usize { + self.0 + } +} + +impl IdPrivate for SegmentId { + fn new(id: usize) -> Self { + SegmentId(id) + } +} + +/// A segment in [`Segments`]. +/// +/// This corresponds to [`elf::ProgramHeader32`] or [`elf::ProgramHeader64`]. +#[derive(Debug)] +pub struct Segment<'data> { + id: SegmentId, + /// Ignore this segment when writing the ELF file. + pub delete: bool, + /// The `p_type` field in the ELF program header. + /// + /// One of the `PT_*` constants. + pub p_type: u32, + /// The `p_flags` field in the ELF program header. + /// + /// A combination of the `PF_*` constants. + pub p_flags: u32, + /// The `p_offset` field in the ELF program header. + /// + /// This is the file offset of the data in the segment. This should + /// correspond to the file offset of the sections that are placed in + /// this segment. Currently there is no support for section data + /// that is not contained in sections. + pub p_offset: u64, + /// The `p_vaddr` field in the ELF program header. + pub p_vaddr: u64, + /// The `p_paddr` field in the ELF program header. + pub p_paddr: u64, + /// The `p_filesz` field in the ELF program header. + pub p_filesz: u64, + /// The `p_memsz` field in the ELF program header. + pub p_memsz: u64, + /// The `p_align` field in the ELF program header. + pub p_align: u64, + /// The sections contained in this segment. + pub sections: Vec<SectionId>, + // Might need to add reference to data if no sections. + marker: PhantomData<&'data ()>, +} + +impl<'data> Item for Segment<'data> { + type Id = SegmentId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> Segment<'data> { + /// The ID used for referring to this segment. + pub fn id(&self) -> SegmentId { + self.id + } + + /// Returns true if the segment type is `PT_LOAD`. + pub fn is_load(&self) -> bool { + self.p_type == elf::PT_LOAD + } + + /// Returns true if the segment contains the given file offset. + pub fn contains_offset(&self, offset: u64) -> bool { + offset >= self.p_offset && offset - self.p_offset < self.p_filesz + } + + /// Return the address corresponding to the given file offset. + /// + /// This will return a meaningless value if `contains_offset` is false. + pub fn address_from_offset(&self, offset: u64) -> u64 { + self.p_vaddr + .wrapping_add(offset.wrapping_sub(self.p_offset)) + } + + /// Returns true if the segment contains the given address. + pub fn contains_address(&self, address: u64) -> bool { + address >= self.p_vaddr && address - self.p_vaddr < self.p_memsz + } + + /// Remove all sections from the segment, and set its size to zero. + pub fn remove_sections(&mut self) { + self.p_filesz = 0; + self.p_memsz = 0; + self.sections.clear(); + } + + /// Add a section to the segment. + /// + /// If this is a [`elf::PT_LOAD`] segment, then the file offset and address of the + /// section is changed to be at the end of the segment. + /// + /// The segment's file and address ranges are extended to include the section. + /// This uses the `sh_size` field of the section, not the size of the section data. + /// + /// The section's id is added to the segment's list of sections. + pub fn append_section(&mut self, section: &mut Section<'_>) { + debug_assert_eq!(self.p_filesz, self.p_memsz); + if self.p_type == elf::PT_LOAD { + let align = section.sh_addralign; + let offset = (self.p_offset + self.p_filesz + (align - 1)) & !(align - 1); + let addr = (self.p_paddr + self.p_memsz + (align - 1)) & !(align - 1); + section.sh_offset = offset; + section.sh_addr = addr; + } + self.append_section_range(section); + self.sections.push(section.id); + } + + /// Extend this segment's file and address ranges to include the given section. + /// + /// If the segment's `p_memsz` is zero, then this signifies that the segment + /// has no file or address range yet. In this case, the segment's file and address + /// ranges are set equal to the section. Otherwise, the segment's file and address + /// ranges are extended to include the section. + /// + /// This uses the `sh_size` field of the section, not the size of the section data. + pub fn append_section_range(&mut self, section: &Section<'_>) { + let section_filesize = if section.sh_type == elf::SHT_NOBITS { + 0 + } else { + section.sh_size + }; + if self.p_memsz == 0 { + self.p_offset = section.sh_offset; + self.p_filesz = section_filesize; + self.p_vaddr = section.sh_addr; + self.p_paddr = section.sh_addr; + self.p_memsz = section.sh_size; + } else { + if self.p_offset > section.sh_offset { + self.p_offset = section.sh_offset; + } + let filesz = section.sh_offset + section_filesize - self.p_offset; + if self.p_filesz < filesz { + self.p_filesz = filesz; + } + if self.p_vaddr > section.sh_addr { + self.p_vaddr = section.sh_addr; + self.p_paddr = section.sh_addr; + } + let memsz = section.sh_addr + section.sh_size - self.p_vaddr; + if self.p_memsz < memsz { + self.p_memsz = memsz; + } + } + } + + /// Recalculate the file and address ranges of the segment. + /// + /// Resets the segment's file and address ranges to zero, and then + /// calls `append_section_range` for each section in the segment. + pub fn recalculate_ranges(&mut self, sections: &Sections<'data>) { + self.p_offset = 0; + self.p_filesz = 0; + self.p_vaddr = 0; + self.p_paddr = 0; + self.p_memsz = 0; + let ids = core::mem::take(&mut self.sections); + for id in &ids { + let section = sections.get(*id); + self.append_section_range(section); + } + self.sections = ids; + } +} + +/// A segment table. +pub type Segments<'data> = Table<Segment<'data>>; + +impl<'data> Segments<'data> { + /// Add a new segment to the table. + pub fn add(&mut self) -> &mut Segment<'data> { + let id = self.next_id(); + self.push(Segment { + id, + delete: false, + p_type: 0, + p_flags: 0, + p_offset: 0, + p_vaddr: 0, + p_paddr: 0, + p_filesz: 0, + p_memsz: 0, + p_align: 0, + sections: Vec::new(), + marker: PhantomData, + }); + self.get_mut(id) + } + + /// Find a `PT_LOAD` segment containing the given offset. + pub fn find_load_segment_from_offset(&self, offset: u64) -> Option<&Segment<'data>> { + self.iter() + .find(|segment| segment.is_load() && segment.contains_offset(offset)) + } + + /// Add a new `PT_LOAD` segment to the table. + /// + /// The file offset and address will be derived from the current maximum for any segment. + /// The address will be chosen so that `p_paddr % align == p_offset % align`. + /// You may wish to use [`Builder::load_align`] for the alignment. + pub fn add_load_segment(&mut self, flags: u32, align: u64) -> &mut Segment<'data> { + let mut max_offset = 0; + let mut max_addr = 0; + for segment in &*self { + let offset = segment.p_offset + segment.p_filesz; + if max_offset < offset { + max_offset = offset; + } + let addr = segment.p_vaddr + segment.p_memsz; + if max_addr < addr { + max_addr = addr; + } + } + // No alignment is required for the segment file offset because sections + // will add their alignment to the file offset when they are added. + let offset = max_offset; + // The address must be chosen so that addr % align == offset % align. + let addr = ((max_addr + (align - 1)) & !(align - 1)) + (offset & (align - 1)); + + let segment = self.add(); + segment.p_type = elf::PT_LOAD; + segment.p_flags = flags; + segment.p_offset = offset; + segment.p_vaddr = addr; + segment.p_paddr = addr; + segment.p_align = align; + segment + } + + /// Add a copy of a segment to the table. + /// + /// This will copy the segment type, flags and alignment. + /// + /// Additionally, if the segment type is `PT_LOAD`, then the file offset and address + /// will be set as in `add_load_segment`. + pub fn copy(&mut self, id: SegmentId) -> &mut Segment<'data> { + let segment = self.get(id); + let p_type = segment.p_type; + let p_flags = segment.p_flags; + let p_align = segment.p_align; + if p_type == elf::PT_LOAD { + self.add_load_segment(p_flags, p_align) + } else { + let segment = self.add(); + segment.p_type = p_type; + segment.p_flags = p_flags; + segment.p_align = p_align; + segment + } + } +} + +/// An ID for referring to a section in [`Sections`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct SectionId(usize); + +impl fmt::Debug for SectionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SectionId({})", self.0) + } +} + +impl Id for SectionId { + fn index(&self) -> usize { + self.0 + } +} + +impl IdPrivate for SectionId { + fn new(id: usize) -> Self { + SectionId(id) + } +} + +/// A section in [`Sections`]. +/// +/// This corresponds to [`elf::SectionHeader32`] or [`elf::SectionHeader64`]. +#[derive(Debug)] +pub struct Section<'data> { + id: SectionId, + /// Ignore this section when writing the ELF file. + pub delete: bool, + /// The name of the section. + /// + /// This is automatically added to the section header string table, + /// and the resulting string table offset is used to set the `sh_name` + /// field in the ELF section header. + pub name: ByteString<'data>, + /// The `sh_type` field in the ELF section header. + /// + /// One of the `SHT_*` constants. + pub sh_type: u32, + /// The `sh_flags` field in the ELF section header. + /// + /// A combination of the `SHF_*` constants. + pub sh_flags: u64, + /// The `sh_addr` field in the ELF section header. + pub sh_addr: u64, + /// The `sh_offset` field in the ELF section header. + /// + /// This is the file offset of the data in the section. + /// Writing will fail if the data cannot be placed at this offset. + /// + /// This is only used for sections that have `SHF_ALLOC` set. + /// For other sections, the section data is written at the next available + /// offset. + pub sh_offset: u64, + /// The `sh_size` field in the ELF section header. + /// + /// This size is not used when writing. The size of the `data` field is + /// used instead. + pub sh_size: u64, + /// The ID of the section linked to by the `sh_link` field in the ELF section header. + pub sh_link_section: Option<SectionId>, + /// The `sh_info` field in the ELF section header. + /// + /// Only used if `sh_info_section` is `None`. + pub sh_info: u32, + /// The ID of the section linked to by the `sh_info` field in the ELF section header. + pub sh_info_section: Option<SectionId>, + /// The `sh_addralign` field in the ELF section header. + pub sh_addralign: u64, + /// The `sh_entsize` field in the ELF section header. + pub sh_entsize: u64, + /// The section data. + pub data: SectionData<'data>, +} + +impl<'data> Item for Section<'data> { + type Id = SectionId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> Section<'data> { + /// The ID used for referring to this section. + pub fn id(&self) -> SectionId { + self.id + } + + /// Returns true if the section flags include `SHF_ALLOC`. + pub fn is_alloc(&self) -> bool { + self.sh_flags & u64::from(elf::SHF_ALLOC) != 0 + } + + /// Return the segment permission flags that are equivalent to the section flags. + pub fn p_flags(&self) -> u32 { + let mut p_flags = elf::PF_R; + if self.sh_flags & u64::from(elf::SHF_WRITE) != 0 { + p_flags |= elf::PF_W; + } + if self.sh_flags & u64::from(elf::SHF_EXECINSTR) != 0 { + p_flags |= elf::PF_X; + } + p_flags + } +} + +/// The data for a [`Section`]. +#[derive(Debug, Clone)] +pub enum SectionData<'data> { + /// The section contains the given raw data bytes. + Data(Bytes<'data>), + /// The section contains uninitialised data bytes of the given length. + UninitializedData(u64), + /// The section contains relocations. + Relocation(Vec<Relocation>), + /// The section contains dynamic relocations. + DynamicRelocation(Vec<DynamicRelocation>), + /// The section contains notes. + // TODO: parse notes + Note(Bytes<'data>), + /// The section contains dynamic entries. + Dynamic(Vec<Dynamic<'data>>), + /// The section contains attributes. + /// + /// This may be GNU attributes or other vendor-specific attributes. + Attributes(AttributesSection<'data>), + /// The section contains the strings for the section headers. + SectionString, + /// The section contains the symbol table. + Symbol, + /// The section contains the extended section index for the symbol table. + SymbolSectionIndex, + /// The section contains the strings for symbol table. + String, + /// The section contains the dynamic symbol table. + DynamicSymbol, + /// The section contains the dynamic string table. + DynamicString, + /// The section contains the hash table. + Hash, + /// The section contains the GNU hash table. + GnuHash, + /// The section contains the GNU symbol versions. + GnuVersym, + /// The section contains the GNU version definitions. + GnuVerdef, + /// The section contains the GNU version dependencies. + GnuVerneed, +} + +/// A section table. +pub type Sections<'data> = Table<Section<'data>>; + +impl<'data> Sections<'data> { + /// Add a new section to the table. + pub fn add(&mut self) -> &mut Section<'data> { + let id = self.next_id(); + self.push(Section { + id, + delete: false, + name: ByteString::default(), + sh_type: 0, + sh_flags: 0, + sh_addr: 0, + sh_offset: 0, + sh_size: 0, + sh_link_section: None, + sh_info: 0, + sh_info_section: None, + sh_addralign: 0, + sh_entsize: 0, + data: SectionData::Data(Bytes::default()), + }) + } + + /// Add a copy of a section to the table. + /// + /// This will set the file offset of the copy to zero. + /// [`Segment::append_section`] can be used to assign a valid file offset and a new address. + pub fn copy(&mut self, id: SectionId) -> &mut Section<'data> { + let section = self.get(id); + let id = self.next_id(); + let name = section.name.clone(); + let sh_type = section.sh_type; + let sh_flags = section.sh_flags; + let sh_addr = section.sh_addr; + let sh_size = section.sh_size; + let sh_link_section = section.sh_link_section; + let sh_info = section.sh_info; + let sh_info_section = section.sh_info_section; + let sh_addralign = section.sh_addralign; + let sh_entsize = section.sh_entsize; + let data = section.data.clone(); + self.push(Section { + id, + delete: false, + name, + sh_type, + sh_flags, + sh_addr, + sh_offset: 0, + sh_size, + sh_link_section, + sh_info, + sh_info_section, + sh_addralign, + sh_entsize, + data, + }) + } +} + +/// An ID for referring to a symbol in [`Symbols`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct SymbolId<const DYNAMIC: bool = false>(usize); + +impl<const DYNAMIC: bool> fmt::Debug for SymbolId<DYNAMIC> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl<const DYNAMIC: bool> Id for SymbolId<DYNAMIC> { + fn index(&self) -> usize { + self.0 + } +} + +impl<const DYNAMIC: bool> IdPrivate for SymbolId<DYNAMIC> { + fn new(id: usize) -> Self { + SymbolId(id) + } +} + +/// A symbol in [`Symbols`]. +/// +/// This corresponds to [`elf::Sym32`] or [`elf::Sym64`]. +#[derive(Debug)] +pub struct Symbol<'data, const DYNAMIC: bool = false> { + id: SymbolId<DYNAMIC>, + /// Ignore this symbol when writing the ELF file. + pub delete: bool, + /// The name of the symbol. + pub name: ByteString<'data>, + /// The section referenced by the symbol. + /// + /// Used to set the `st_shndx` field in the ELF symbol. + pub section: Option<SectionId>, + /// The `st_info` field in the ELF symbol. + pub st_info: u8, + /// The `st_other` field in the ELF symbol. + pub st_other: u8, + /// The `st_shndx` field in the ELF symbol. + /// + /// Only used if `Self::section` is `None`. + pub st_shndx: u16, + /// The `st_value` field in the ELF symbol. + pub st_value: u64, + /// The `st_size` field in the ELF symbol. + pub st_size: u64, + /// GNU version for dynamic symbols. + pub version: VersionId, + /// Set the [`elf::VERSYM_HIDDEN`] flag for this symbol. + pub version_hidden: bool, +} + +impl<'data, const DYNAMIC: bool> Item for Symbol<'data, DYNAMIC> { + type Id = SymbolId<DYNAMIC>; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data, const DYNAMIC: bool> Symbol<'data, DYNAMIC> { + /// The ID used for referring to this symbol. + pub fn id(&self) -> SymbolId<DYNAMIC> { + self.id + } + + /// Get the `st_bind` component of the `st_info` field. + #[inline] + pub fn st_bind(&self) -> u8 { + self.st_info >> 4 + } + + /// Get the `st_type` component of the `st_info` field. + #[inline] + pub fn st_type(&self) -> u8 { + self.st_info & 0xf + } + + /// Set the `st_info` field given the `st_bind` and `st_type` components. + #[inline] + pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { + self.st_info = (st_bind << 4) + (st_type & 0xf); + } +} + +/// A symbol table. +pub type Symbols<'data, const DYNAMIC: bool = false> = Table<Symbol<'data, DYNAMIC>>; + +impl<'data, const DYNAMIC: bool> Symbols<'data, DYNAMIC> { + /// Number of defined symbols. + pub fn count_defined(&self) -> usize { + self.into_iter() + .filter(|symbol| symbol.st_shndx != elf::SHN_UNDEF) + .count() + } + + /// Add a new symbol to the table. + pub fn add(&mut self) -> &mut Symbol<'data, DYNAMIC> { + let id = self.next_id(); + self.push(Symbol { + id, + delete: false, + name: ByteString::default(), + section: None, + st_info: 0, + st_other: 0, + st_shndx: 0, + st_value: 0, + st_size: 0, + version: VersionId::local(), + version_hidden: false, + }) + } +} + +/// A relocation stored in a [`Section`]. +/// +/// This corresponds to [`elf::Rel32`], [`elf::Rela32`], [`elf::Rel64`] or [`elf::Rela64`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Relocation<const DYNAMIC: bool = false> { + /// The `r_offset` field in the ELF relocation. + pub r_offset: u64, + /// The symbol referenced by the ELF relocation. + pub symbol: Option<SymbolId<DYNAMIC>>, + /// The `r_type` field in the ELF relocation. + pub r_type: u32, + /// The `r_addend` field in the ELF relocation. + /// + /// Only used if the section type is `SHT_RELA`. + pub r_addend: i64, +} + +/// A dynamic symbol ID. +pub type DynamicSymbolId = SymbolId<true>; + +/// A dynamic symbol. +pub type DynamicSymbol<'data> = Symbol<'data, true>; + +/// A dynamic symbol table. +pub type DynamicSymbols<'data> = Symbols<'data, true>; + +/// A dynamic relocation. +pub type DynamicRelocation = Relocation<true>; + +/// An entry in the dynamic section. +/// +/// This corresponds to [`elf::Dyn32`] or [`elf::Dyn64`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Dynamic<'data> { + /// The value is an automatically generated integer. + /// + /// Writing will fail if the value cannot be automatically generated. + Auto { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + }, + /// The value is an integer. + Integer { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + /// The `d_val` field in the dynamic entry. + val: u64, + }, + /// The value is a string. + String { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + /// The string value. + /// + /// This will be stored in the dynamic string section. + val: ByteString<'data>, + }, +} + +impl<'data> Dynamic<'data> { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + pub fn tag(&self) -> u32 { + match self { + Dynamic::Auto { tag } => *tag, + Dynamic::Integer { tag, .. } => *tag, + Dynamic::String { tag, .. } => *tag, + } + } +} + +/// An ID for referring to a filename in [`VersionFiles`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct VersionFileId(usize); + +impl fmt::Debug for VersionFileId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "VersionFileId({})", self.0) + } +} + +impl Id for VersionFileId { + fn index(&self) -> usize { + self.0 + } +} + +impl IdPrivate for VersionFileId { + fn new(id: usize) -> Self { + VersionFileId(id) + } +} + +/// A filename used for GNU versioning. +/// +/// Stored in [`VersionFiles`]. +#[derive(Debug)] +pub struct VersionFile<'data> { + id: VersionFileId, + /// Ignore this file when writing the ELF file. + pub delete: bool, + /// The filename. + pub name: ByteString<'data>, +} + +impl<'data> Item for VersionFile<'data> { + type Id = VersionFileId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> VersionFile<'data> { + /// The ID used for referring to this filename. + pub fn id(&self) -> VersionFileId { + self.id + } +} + +/// A table of filenames used for GNU versioning. +pub type VersionFiles<'data> = Table<VersionFile<'data>>; + +impl<'data> VersionFiles<'data> { + /// Add a new filename to the table. + pub fn add(&mut self, name: ByteString<'data>) -> VersionFileId { + let id = self.next_id(); + self.push(VersionFile { + id, + name, + delete: false, + }); + id + } +} + +const VERSION_ID_BASE: usize = 2; + +/// An ID for referring to a version in [`Versions`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct VersionId(usize); + +impl fmt::Debug for VersionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "VersionId({})", self.0) + } +} + +impl Id for VersionId { + fn index(&self) -> usize { + self.0 - VERSION_ID_BASE + } +} + +impl IdPrivate for VersionId { + fn new(id: usize) -> Self { + VersionId(VERSION_ID_BASE + id) + } +} + +impl VersionId { + /// Return `True` if this is a special version that does not exist in the version table. + pub fn is_special(&self) -> bool { + self.0 < VERSION_ID_BASE + } + + /// Return the ID for a version index of [`elf::VER_NDX_LOCAL`]. + pub fn local() -> Self { + VersionId(elf::VER_NDX_LOCAL as usize) + } + + /// Return the ID for a version index of [`elf::VER_NDX_GLOBAL`]. + pub fn global() -> Self { + VersionId(elf::VER_NDX_GLOBAL as usize) + } +} + +/// A version for a symbol. +#[derive(Debug)] +pub struct Version<'data> { + id: VersionId, + /// The data for this version. + pub data: VersionData<'data>, + /// Ignore this version when writing the ELF file. + pub delete: bool, +} + +impl<'data> Item for Version<'data> { + type Id = VersionId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> Version<'data> { + /// The ID used for referring to this version. + pub fn id(&self) -> VersionId { + self.id + } +} + +/// The data for a version for a symbol. +#[derive(Debug)] +pub enum VersionData<'data> { + /// The version for a defined symbol. + Def(VersionDef<'data>), + /// The version for an undefined symbol. + Need(VersionNeed<'data>), +} + +/// A GNU version definition. +#[derive(Debug)] +pub struct VersionDef<'data> { + /// The names for the version. + /// + /// This usually has two elements. The first element is the name of this + /// version, and the second element is the name of the previous version + /// in the tree of versions. + pub names: Vec<ByteString<'data>>, + /// The version flags. + /// + /// A combination of the `VER_FLG_*` constants. + pub flags: u16, +} + +impl<'data> VersionDef<'data> { + /// Optimise for the common case where the first version is the same as the base version. + fn is_shared(&self, index: usize, base: Option<&ByteString<'_>>) -> bool { + index == 1 && self.names.len() == 1 && self.names.first() == base + } +} + +/// A GNU version dependency. +#[derive(Debug)] +pub struct VersionNeed<'data> { + /// The filename of the library providing this version. + pub file: VersionFileId, + /// The name of the version. + pub name: ByteString<'data>, + /// The version flags. + /// + /// A combination of the `VER_FLG_*` constants. + pub flags: u16, +} + +/// A table of versions that are referenced by symbols. +pub type Versions<'data> = Table<Version<'data>>; + +impl<'data> Versions<'data> { + /// Add a version. + pub fn add(&mut self, data: VersionData<'data>) -> VersionId { + let id = self.next_id(); + self.push(Version { + id, + data, + delete: false, + }); + id + } +} + +/// The contents of an attributes section. +#[derive(Debug, Default, Clone)] +pub struct AttributesSection<'data> { + /// The subsections. + pub subsections: Vec<AttributesSubsection<'data>>, +} + +impl<'data> AttributesSection<'data> { + /// Create a new attributes section. + pub fn new() -> Self { + Self::default() + } +} + +/// A subsection of an attributes section. +#[derive(Debug, Clone)] +pub struct AttributesSubsection<'data> { + /// The vendor namespace for these attributes. + pub vendor: ByteString<'data>, + /// The sub-subsections. + pub subsubsections: Vec<AttributesSubsubsection<'data>>, +} + +impl<'data> AttributesSubsection<'data> { + /// Create a new subsection. + pub fn new(vendor: ByteString<'data>) -> Self { + AttributesSubsection { + vendor, + subsubsections: Vec::new(), + } + } +} + +/// A sub-subsection in an attributes section. +#[derive(Debug, Clone)] +pub struct AttributesSubsubsection<'data> { + /// The sub-subsection tag. + pub tag: AttributeTag, + /// The data containing the attributes. + pub data: Bytes<'data>, +} + +/// The tag for a sub-subsection in an attributes section. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AttributeTag { + /// The attributes apply to the whole file. + /// + /// Correspeonds to [`elf::Tag_File`]. + File, + /// The attributes apply to the given sections. + /// + /// Correspeonds to [`elf::Tag_Section`]. + Section(Vec<SectionId>), + /// The attributes apply to the given symbols. + /// + /// Correspeonds to [`elf::Tag_Symbol`]. + Symbol(Vec<SymbolId>), +} + +impl AttributeTag { + /// Return the corresponding `elf::Tag_*` value for this tag. + pub fn tag(&self) -> u8 { + match self { + AttributeTag::File => elf::Tag_File, + AttributeTag::Section(_) => elf::Tag_Section, + AttributeTag::Symbol(_) => elf::Tag_Symbol, + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/build/error.rs b/chromium_crates_io/vendor/object-v0_37/src/build/error.rs new file mode 100644 index 0000000..1e56780 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/build/error.rs
@@ -0,0 +1,43 @@ +use alloc::string::String; +use core::{fmt, result}; +#[cfg(feature = "std")] +use std::error; + +use crate::{read, write}; + +/// The error type used within the build module. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Error(pub(super) String); + +impl Error { + pub(super) fn new(message: impl Into<String>) -> Self { + Error(message.into()) + } +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +#[cfg(feature = "std")] +impl error::Error for Error {} +#[cfg(all(not(feature = "std"), core_error))] +impl core::error::Error for Error {} + +impl From<read::Error> for Error { + fn from(error: read::Error) -> Error { + Error(format!("{}", error)) + } +} + +impl From<write::Error> for Error { + fn from(error: write::Error) -> Error { + Error(error.0) + } +} + +/// The result type used within the build module. +pub type Result<T> = result::Result<T, Error>;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/build/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/build/mod.rs new file mode 100644 index 0000000..5945f86 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/build/mod.rs
@@ -0,0 +1,18 @@ +//! Interface for building object files. +//! +//! This module provides common types and traits used in the builders. +//! +//! The submodules define the builders for each file format. + +mod error; +pub use error::{Error, Result}; + +mod bytes; +pub use bytes::{ByteString, Bytes}; + +mod table; +use table::IdPrivate; +pub use table::{Id, Item, Table}; + +#[cfg(feature = "elf")] +pub mod elf;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/build/table.rs b/chromium_crates_io/vendor/object-v0_37/src/build/table.rs new file mode 100644 index 0000000..63f276a --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/build/table.rs
@@ -0,0 +1,128 @@ +use alloc::vec::Vec; + +/// An item in a [`Table`]. +pub trait Item { + /// The type of identifier for the item. + type Id: Id; + + /// Return `True` if the item is deleted. + fn is_deleted(&self) -> bool; +} + +/// An identifier for referring to an item in a [`Table`]. +pub trait Id: IdPrivate { + /// Return the index of the item in the table. + fn index(&self) -> usize; +} + +mod id_private { + pub trait IdPrivate { + fn new(id: usize) -> Self; + } +} +pub(super) use id_private::IdPrivate; + +/// A table of items. +/// +/// Each item has a unique identifier. +/// Items can be deleted without changing the identifiers of other items. +#[derive(Debug)] +pub struct Table<T>(Vec<T>); + +impl<T> Table<T> { + pub(super) fn new() -> Self { + Table(Vec::new()) + } +} + +impl<T: Item> Table<T> { + pub(super) fn next_id(&self) -> T::Id { + T::Id::new(self.0.len()) + } + + pub(super) fn push(&mut self, item: T) -> &mut T { + self.0.push(item); + self.0.last_mut().unwrap() + } + + /// Number of items, including deleted items. + pub(super) fn len(&self) -> usize { + self.0.len() + } + + /// Return `True` if there are no non-deleted items. + pub fn is_empty(&self) -> bool { + self.into_iter().next().is_none() + } + + /// Number of non-deleted items. + pub fn count(&self) -> usize { + self.into_iter().count() + } + + /// Return a reference to an item. + pub fn get(&self, id: T::Id) -> &T { + self.0.get(id.index()).unwrap() + } + + /// Return a mutable reference to a segment. + pub fn get_mut(&mut self, id: T::Id) -> &mut T { + self.0.get_mut(id.index()).unwrap() + } + + /// Return an iterator for the segments. + pub fn iter(&self) -> TableIter<'_, T> { + self.into_iter() + } + + /// Return a mutable iterator for the segments. + pub fn iter_mut(&mut self) -> TableIterMut<'_, T> { + self.into_iter() + } +} + +impl<'a, T: Item> IntoIterator for &'a Table<T> { + type Item = &'a T; + type IntoIter = TableIter<'a, T>; + fn into_iter(self) -> TableIter<'a, T> { + TableIter { + iter: self.0.iter(), + } + } +} + +impl<'a, T: Item> IntoIterator for &'a mut Table<T> { + type Item = &'a mut T; + type IntoIter = TableIterMut<'a, T>; + fn into_iter(self) -> TableIterMut<'a, T> { + TableIterMut { + iter: self.0.iter_mut(), + } + } +} + +/// An iterator for non-deleted items in a [`Table`]. +#[derive(Debug)] +pub struct TableIter<'a, T> { + iter: core::slice::Iter<'a, T>, +} + +impl<'a, T: Item> Iterator for TableIter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option<&'a T> { + self.iter.find(|item| !item.is_deleted()) + } +} + +/// An iterator for non-deleted items in a [`Table`]. +#[derive(Debug)] +pub struct TableIterMut<'a, T> { + iter: core::slice::IterMut<'a, T>, +} + +impl<'a, T: Item> Iterator for TableIterMut<'a, T> { + type Item = &'a mut T; + fn next(&mut self) -> Option<&'a mut T> { + self.iter.find(|item| !item.is_deleted()) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/common.rs b/chromium_crates_io/vendor/object-v0_37/src/common.rs new file mode 100644 index 0000000..d0a4cdb --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/common.rs
@@ -0,0 +1,619 @@ +/// A CPU architecture. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum Architecture { + Unknown, + Aarch64, + #[allow(non_camel_case_types)] + Aarch64_Ilp32, + Alpha, + Arm, + Avr, + Bpf, + Csky, + E2K32, + E2K64, + I386, + X86_64, + #[allow(non_camel_case_types)] + X86_64_X32, + Hexagon, + Hppa, + LoongArch32, + LoongArch64, + M68k, + Mips, + Mips64, + #[allow(non_camel_case_types)] + Mips64_N32, + Msp430, + PowerPc, + PowerPc64, + Riscv32, + Riscv64, + S390x, + Sbf, + Sharc, + Sparc, + Sparc32Plus, + Sparc64, + SuperH, + Wasm32, + Wasm64, + Xtensa, +} + +/// A CPU sub-architecture. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SubArchitecture { + Arm64E, + Arm64EC, +} + +impl Architecture { + /// The size of an address value for this architecture. + /// + /// Returns `None` for unknown architectures. + pub fn address_size(self) -> Option<AddressSize> { + match self { + Architecture::Unknown => None, + Architecture::Aarch64 => Some(AddressSize::U64), + Architecture::Aarch64_Ilp32 => Some(AddressSize::U32), + Architecture::Alpha => Some(AddressSize::U64), + Architecture::Arm => Some(AddressSize::U32), + Architecture::Avr => Some(AddressSize::U8), + Architecture::Bpf => Some(AddressSize::U64), + Architecture::Csky => Some(AddressSize::U32), + Architecture::E2K32 => Some(AddressSize::U32), + Architecture::E2K64 => Some(AddressSize::U64), + Architecture::I386 => Some(AddressSize::U32), + Architecture::X86_64 => Some(AddressSize::U64), + Architecture::X86_64_X32 => Some(AddressSize::U32), + Architecture::Hexagon => Some(AddressSize::U32), + Architecture::Hppa => Some(AddressSize::U32), + Architecture::LoongArch32 => Some(AddressSize::U32), + Architecture::LoongArch64 => Some(AddressSize::U64), + Architecture::M68k => Some(AddressSize::U32), + Architecture::Mips => Some(AddressSize::U32), + Architecture::Mips64 => Some(AddressSize::U64), + Architecture::Mips64_N32 => Some(AddressSize::U32), + Architecture::Msp430 => Some(AddressSize::U16), + Architecture::PowerPc => Some(AddressSize::U32), + Architecture::PowerPc64 => Some(AddressSize::U64), + Architecture::Riscv32 => Some(AddressSize::U32), + Architecture::Riscv64 => Some(AddressSize::U64), + Architecture::S390x => Some(AddressSize::U64), + Architecture::Sbf => Some(AddressSize::U64), + Architecture::Sharc => Some(AddressSize::U32), + Architecture::Sparc => Some(AddressSize::U32), + Architecture::Sparc32Plus => Some(AddressSize::U32), + Architecture::Sparc64 => Some(AddressSize::U64), + Architecture::Wasm32 => Some(AddressSize::U32), + Architecture::Wasm64 => Some(AddressSize::U64), + Architecture::Xtensa => Some(AddressSize::U32), + Architecture::SuperH => Some(AddressSize::U32), + } + } +} + +/// The size of an address value for an architecture. +/// +/// This may differ from the address size supported by the file format (such as for COFF). +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +#[repr(u8)] +pub enum AddressSize { + U8 = 1, + U16 = 2, + U32 = 4, + U64 = 8, +} + +impl AddressSize { + /// The size in bytes of an address value. + #[inline] + pub fn bytes(self) -> u8 { + self as u8 + } +} + +/// A binary file format. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum BinaryFormat { + Coff, + Elf, + MachO, + Pe, + Wasm, + Xcoff, +} + +impl BinaryFormat { + /// The target's native binary format for relocatable object files. + /// + /// Defaults to `Elf` for unknown platforms. + pub fn native_object() -> BinaryFormat { + if cfg!(target_os = "windows") { + BinaryFormat::Coff + } else if cfg!(target_os = "macos") { + BinaryFormat::MachO + } else { + BinaryFormat::Elf + } + } +} + +/// The kind of a section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SectionKind { + /// The section kind is unknown. + Unknown, + /// An executable code section. + /// + /// Example ELF sections: `.text` + /// + /// Example Mach-O sections: `__TEXT/__text` + Text, + /// A data section. + /// + /// Example ELF sections: `.data` + /// + /// Example Mach-O sections: `__DATA/__data` + Data, + /// A read only data section. + /// + /// Example ELF sections: `.rodata` + /// + /// Example Mach-O sections: `__TEXT/__const`, `__DATA/__const`, `__TEXT/__literal4` + ReadOnlyData, + /// A read only data section with relocations. + /// + /// This is the same as either `Data` or `ReadOnlyData`, depending on the file format. + /// This value is only used in the API for writing files. It is never returned when reading files. + ReadOnlyDataWithRel, + /// A loadable string section. + /// + /// Example ELF sections: `.rodata.str` + /// + /// Example Mach-O sections: `__TEXT/__cstring` + ReadOnlyString, + /// An uninitialized data section. + /// + /// Example ELF sections: `.bss` + /// + /// Example Mach-O sections: `__DATA/__bss` + UninitializedData, + /// An uninitialized common data section. + /// + /// Example Mach-O sections: `__DATA/__common` + Common, + /// A TLS data section. + /// + /// Example ELF sections: `.tdata` + /// + /// Example Mach-O sections: `__DATA/__thread_data` + Tls, + /// An uninitialized TLS data section. + /// + /// Example ELF sections: `.tbss` + /// + /// Example Mach-O sections: `__DATA/__thread_bss` + UninitializedTls, + /// A TLS variables section. + /// + /// This contains TLS variable structures, rather than the variable initializers. + /// + /// Example Mach-O sections: `__DATA/__thread_vars` + TlsVariables, + /// A non-loadable string section. + /// + /// Example ELF sections: `.comment`, `.debug_str` + OtherString, + /// Some other non-loadable section. + /// + /// Example ELF sections: `.debug_info` + Other, + /// Debug information. + /// + /// Example Mach-O sections: `__DWARF/__debug_info` + Debug, + /// Debug strings. + /// + /// This is the same as either `Debug` or `OtherString`, depending on the file format. + /// This value is only used in the API for writing files. It is never returned when reading files. + DebugString, + /// Information for the linker. + /// + /// Example COFF sections: `.drectve` + Linker, + /// ELF note section. + Note, + /// Metadata such as symbols or relocations. + /// + /// Example ELF sections: `.symtab`, `.strtab`, `.group` + Metadata, + /// Some other ELF section type. + /// + /// This is the `sh_type` field in the section header. + /// The meaning may be dependent on the architecture. + Elf(u32), +} + +impl SectionKind { + /// Return true if this section contains zerofill data. + pub fn is_bss(self) -> bool { + self == SectionKind::UninitializedData + || self == SectionKind::UninitializedTls + || self == SectionKind::Common + } +} + +/// The selection kind for a COMDAT section group. +/// +/// This determines the way in which the linker resolves multiple definitions of the COMDAT +/// sections. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum ComdatKind { + /// The selection kind is unknown. + Unknown, + /// Multiple definitions are allowed. + /// + /// An arbitrary definition is selected, and the rest are removed. + /// + /// This is the only supported selection kind for ELF. + Any, + /// Multiple definitions are not allowed. + /// + /// This is used to group sections without allowing duplicates. + NoDuplicates, + /// Multiple definitions must have the same size. + /// + /// An arbitrary definition is selected, and the rest are removed. + SameSize, + /// Multiple definitions must match exactly. + /// + /// An arbitrary definition is selected, and the rest are removed. + ExactMatch, + /// Multiple definitions are allowed, and the largest is selected. + /// + /// An arbitrary definition with the largest size is selected, and the rest are removed. + Largest, + /// Multiple definitions are allowed, and the newest is selected. + Newest, +} + +/// The kind of a symbol. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SymbolKind { + /// The symbol kind is unknown. + Unknown, + /// The symbol is for executable code. + Text, + /// The symbol is for a data object. + Data, + /// The symbol is for a section. + Section, + /// The symbol is the name of a file. It precedes symbols within that file. + File, + /// The symbol is for a code label. + Label, + /// The symbol is for a thread local storage entity. + Tls, +} + +/// A symbol scope. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SymbolScope { + /// Unknown scope. + Unknown, + /// Symbol is visible to the compilation unit. + Compilation, + /// Symbol is visible to the static linkage unit. + Linkage, + /// Symbol is visible to dynamically linked objects. + Dynamic, +} + +/// The operation used to calculate the result of the relocation. +/// +/// The relocation descriptions use the following definitions. Note that +/// these definitions probably don't match any ELF ABI. +/// +/// * A - The value of the addend. +/// * G - The address of the symbol's entry within the global offset table. +/// * L - The address of the symbol's entry within the procedure linkage table. +/// * P - The address of the place of the relocation. +/// * S - The address of the symbol. +/// * GotBase - The address of the global offset table. +/// * Image - The base address of the image. +/// * Section - The address of the section containing the symbol. +/// +/// 'XxxRelative' means 'Xxx + A - P'. 'XxxOffset' means 'S + A - Xxx'. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum RelocationKind { + /// The operation is unknown. + Unknown, + /// S + A + Absolute, + /// S + A - P + Relative, + /// G + A - GotBase + Got, + /// G + A - P + GotRelative, + /// GotBase + A - P + GotBaseRelative, + /// S + A - GotBase + GotBaseOffset, + /// L + A - P + PltRelative, + /// S + A - Image + ImageOffset, + /// S + A - Section + SectionOffset, + /// The index of the section containing the symbol. + SectionIndex, +} + +/// Information about how the result of the relocation operation is encoded in the place. +/// +/// This is usually architecture specific, such as specifying an addressing mode or +/// a specific instruction. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum RelocationEncoding { + /// The relocation encoding is unknown. + Unknown, + /// Generic encoding. + Generic, + + /// x86 sign extension at runtime. + /// + /// Used with `RelocationKind::Absolute`. + X86Signed, + /// x86 rip-relative addressing. + /// + /// The `RelocationKind` must be PC relative. + X86RipRelative, + /// x86 rip-relative addressing in movq instruction. + /// + /// The `RelocationKind` must be PC relative. + X86RipRelativeMovq, + /// x86 branch instruction. + /// + /// The `RelocationKind` must be PC relative. + X86Branch, + + /// s390x PC-relative offset shifted right by one bit. + /// + /// The `RelocationKind` must be PC relative. + S390xDbl, + + /// AArch64 call target. + /// + /// The `RelocationKind` must be PC relative. + AArch64Call, + + /// LoongArch branch offset with two trailing zeros. + /// + /// The `RelocationKind` must be PC relative. + LoongArchBranch, + + /// SHARC+ 48-bit Type A instruction + /// + /// Represents these possible variants, each with a corresponding + /// `R_SHARC_*` constant: + /// + /// * 24-bit absolute address + /// * 32-bit absolute address + /// * 6-bit relative address + /// * 24-bit relative address + /// * 6-bit absolute address in the immediate value field + /// * 16-bit absolute address in the immediate value field + SharcTypeA, + + /// SHARC+ 32-bit Type B instruction + /// + /// Represents these possible variants, each with a corresponding + /// `R_SHARC_*` constant: + /// + /// * 6-bit absolute address in the immediate value field + /// * 7-bit absolute address in the immediate value field + /// * 16-bit absolute address + /// * 6-bit relative address + SharcTypeB, + + /// E2K 64-bit value stored in two LTS + /// + /// Memory representation: + /// ```text + /// 0: LTS1 = value[63:32] + /// 4: LTS0 = value[31:0] + /// ``` + E2KLit, + + /// E2K 28-bit value stored in CS0 + E2KDisp, +} + +/// File flags that are specific to each file format. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum FileFlags { + /// No file flags. + None, + /// ELF file flags. + Elf { + /// `os_abi` field in the ELF file header. + os_abi: u8, + /// `abi_version` field in the ELF file header. + abi_version: u8, + /// `e_flags` field in the ELF file header. + e_flags: u32, + }, + /// Mach-O file flags. + MachO { + /// `flags` field in the Mach-O file header. + flags: u32, + }, + /// COFF file flags. + Coff { + /// `Characteristics` field in the COFF file header. + characteristics: u16, + }, + /// XCOFF file flags. + Xcoff { + /// `f_flags` field in the XCOFF file header. + f_flags: u16, + }, +} + +/// Segment flags that are specific to each file format. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SegmentFlags { + /// No segment flags. + None, + /// ELF segment flags. + Elf { + /// `p_flags` field in the segment header. + p_flags: u32, + }, + /// Mach-O segment flags. + MachO { + /// `flags` field in the segment header. + flags: u32, + /// `maxprot` field in the segment header. + maxprot: u32, + /// `initprot` field in the segment header. + initprot: u32, + }, + /// COFF segment flags. + Coff { + /// `Characteristics` field in the segment header. + characteristics: u32, + }, +} + +/// Section flags that are specific to each file format. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SectionFlags { + /// No section flags. + None, + /// ELF section flags. + Elf { + /// `sh_flags` field in the section header. + sh_flags: u64, + }, + /// Mach-O section flags. + MachO { + /// `flags` field in the section header. + flags: u32, + }, + /// COFF section flags. + Coff { + /// `Characteristics` field in the section header. + characteristics: u32, + }, + /// XCOFF section flags. + Xcoff { + /// `s_flags` field in the section header. + s_flags: u32, + }, +} + +/// Symbol flags that are specific to each file format. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SymbolFlags<Section, Symbol> { + /// No symbol flags. + None, + /// ELF symbol flags. + Elf { + /// `st_info` field in the ELF symbol. + st_info: u8, + /// `st_other` field in the ELF symbol. + st_other: u8, + }, + /// Mach-O symbol flags. + MachO { + /// `n_desc` field in the Mach-O symbol. + n_desc: u16, + }, + /// COFF flags for a section symbol. + CoffSection { + /// `Selection` field in the auxiliary symbol for the section. + selection: u8, + /// `Number` field in the auxiliary symbol for the section. + associative_section: Option<Section>, + }, + /// XCOFF symbol flags. + Xcoff { + /// `n_sclass` field in the XCOFF symbol. + n_sclass: u8, + /// `x_smtyp` field in the CSECT auxiliary symbol. + /// + /// Only valid if `n_sclass` is `C_EXT`, `C_WEAKEXT`, or `C_HIDEXT`. + x_smtyp: u8, + /// `x_smclas` field in the CSECT auxiliary symbol. + /// + /// Only valid if `n_sclass` is `C_EXT`, `C_WEAKEXT`, or `C_HIDEXT`. + x_smclas: u8, + /// The containing csect for the symbol. + /// + /// Only valid if `x_smtyp` is `XTY_LD`. + containing_csect: Option<Symbol>, + }, +} + +/// Relocation fields that are specific to each file format and architecture. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum RelocationFlags { + /// Format independent representation. + Generic { + /// The operation used to calculate the result of the relocation. + kind: RelocationKind, + /// Information about how the result of the relocation operation is encoded in the place. + encoding: RelocationEncoding, + /// The size in bits of the place of relocation. + size: u8, + }, + /// ELF relocation fields. + Elf { + /// `r_type` field in the ELF relocation. + r_type: u32, + }, + /// Mach-O relocation fields. + MachO { + /// `r_type` field in the Mach-O relocation. + r_type: u8, + /// `r_pcrel` field in the Mach-O relocation. + r_pcrel: bool, + /// `r_length` field in the Mach-O relocation. + r_length: u8, + }, + /// COFF relocation fields. + Coff { + /// `typ` field in the COFF relocation. + typ: u16, + }, + /// XCOFF relocation fields. + Xcoff { + /// `r_rtype` field in the XCOFF relocation. + r_rtype: u8, + /// `r_rsize` field in the XCOFF relocation. + r_rsize: u8, + }, +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/elf.rs b/chromium_crates_io/vendor/object-v0_37/src/elf.rs new file mode 100644 index 0000000..68fb683 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/elf.rs
@@ -0,0 +1,6533 @@ +//! ELF definitions. +//! +//! These definitions are independent of read/write support, although we do implement +//! some traits useful for those. +//! +//! This module is the equivalent of /usr/include/elf.h, and is based heavily on it. + +#![allow(missing_docs)] +#![allow(clippy::identity_op)] + +use crate::endian::{Endian, U32Bytes, U64Bytes, I32, I64, U16, U32, U64}; +use crate::pod::Pod; + +/// The header at the start of every 32-bit ELF file. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FileHeader32<E: Endian> { + /// Magic number and other information. + pub e_ident: Ident, + /// Object file type. One of the `ET_*` constants. + pub e_type: U16<E>, + /// Architecture. One of the `EM_*` constants. + pub e_machine: U16<E>, + /// Object file version. Must be `EV_CURRENT`. + pub e_version: U32<E>, + /// Entry point virtual address. + pub e_entry: U32<E>, + /// Program header table file offset. + pub e_phoff: U32<E>, + /// Section header table file offset. + pub e_shoff: U32<E>, + /// Processor-specific flags. + /// + /// A combination of the `EF_*` constants. + pub e_flags: U32<E>, + /// Size in bytes of this header. + pub e_ehsize: U16<E>, + /// Program header table entry size. + pub e_phentsize: U16<E>, + /// Program header table entry count. + /// + /// If the count is greater than or equal to `PN_XNUM` then this field is set to + /// `PN_XNUM` and the count is stored in the `sh_info` field of section 0. + pub e_phnum: U16<E>, + /// Section header table entry size. + pub e_shentsize: U16<E>, + /// Section header table entry count. + /// + /// If the count is greater than or equal to `SHN_LORESERVE` then this field is set to + /// `0` and the count is stored in the `sh_size` field of section 0. + /// first section header. + pub e_shnum: U16<E>, + /// Section header string table index. + /// + /// If the index is greater than or equal to `SHN_LORESERVE` then this field is set to + /// `SHN_XINDEX` and the index is stored in the `sh_link` field of section 0. + pub e_shstrndx: U16<E>, +} + +/// The header at the start of every 64-bit ELF file. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FileHeader64<E: Endian> { + /// Magic number and other information. + pub e_ident: Ident, + /// Object file type. One of the `ET_*` constants. + pub e_type: U16<E>, + /// Architecture. One of the `EM_*` constants. + pub e_machine: U16<E>, + /// Object file version. Must be `EV_CURRENT`. + pub e_version: U32<E>, + /// Entry point virtual address. + pub e_entry: U64<E>, + /// Program header table file offset. + pub e_phoff: U64<E>, + /// Section header table file offset. + pub e_shoff: U64<E>, + /// Processor-specific flags. + /// + /// A combination of the `EF_*` constants. + pub e_flags: U32<E>, + /// Size in bytes of this header. + pub e_ehsize: U16<E>, + /// Program header table entry size. + pub e_phentsize: U16<E>, + /// Program header table entry count. + /// + /// If the count is greater than or equal to `PN_XNUM` then this field is set to + /// `PN_XNUM` and the count is stored in the `sh_info` field of section 0. + pub e_phnum: U16<E>, + /// Section header table entry size. + pub e_shentsize: U16<E>, + /// Section header table entry count. + /// + /// If the count is greater than or equal to `SHN_LORESERVE` then this field is set to + /// `0` and the count is stored in the `sh_size` field of section 0. + /// first section header. + pub e_shnum: U16<E>, + /// Section header string table index. + /// + /// If the index is greater than or equal to `SHN_LORESERVE` then this field is set to + /// `SHN_XINDEX` and the index is stored in the `sh_link` field of section 0. + pub e_shstrndx: U16<E>, +} + +/// Magic number and other information. +/// +/// Contained in the file header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Ident { + /// Magic number. Must be `ELFMAG`. + pub magic: [u8; 4], + /// File class. One of the `ELFCLASS*` constants. + pub class: u8, + /// Data encoding. One of the `ELFDATA*` constants. + pub data: u8, + /// ELF version. Must be `EV_CURRENT`. + pub version: u8, + /// OS ABI identification. One of the `ELFOSABI*` constants. + pub os_abi: u8, + /// ABI version. + /// + /// The meaning of this field depends on the `os_abi` value. + pub abi_version: u8, + /// Padding bytes. + pub padding: [u8; 7], +} + +/// File identification bytes stored in `Ident::magic`. +pub const ELFMAG: [u8; 4] = [0x7f, b'E', b'L', b'F']; + +// Values for `Ident::class`. +/// Invalid class. +pub const ELFCLASSNONE: u8 = 0; +/// 32-bit object. +pub const ELFCLASS32: u8 = 1; +/// 64-bit object. +pub const ELFCLASS64: u8 = 2; + +// Values for `Ident::data`. +/// Invalid data encoding. +pub const ELFDATANONE: u8 = 0; +/// 2's complement, little endian. +pub const ELFDATA2LSB: u8 = 1; +/// 2's complement, big endian. +pub const ELFDATA2MSB: u8 = 2; + +// Values for `Ident::os_abi`. +/// UNIX System V ABI. +pub const ELFOSABI_NONE: u8 = 0; +/// UNIX System V ABI. +/// +/// Alias. +pub const ELFOSABI_SYSV: u8 = 0; +/// HP-UX. +pub const ELFOSABI_HPUX: u8 = 1; +/// NetBSD. +pub const ELFOSABI_NETBSD: u8 = 2; +/// Object uses GNU ELF extensions. +pub const ELFOSABI_GNU: u8 = 3; +/// Object uses GNU ELF extensions. +/// +/// Compatibility alias. +pub const ELFOSABI_LINUX: u8 = ELFOSABI_GNU; +/// GNU/Hurd. +pub const ELFOSABI_HURD: u8 = 4; +/// Sun Solaris. +pub const ELFOSABI_SOLARIS: u8 = 6; +/// IBM AIX. +pub const ELFOSABI_AIX: u8 = 7; +/// SGI Irix. +pub const ELFOSABI_IRIX: u8 = 8; +/// FreeBSD. +pub const ELFOSABI_FREEBSD: u8 = 9; +/// Compaq TRU64 UNIX. +pub const ELFOSABI_TRU64: u8 = 10; +/// Novell Modesto. +pub const ELFOSABI_MODESTO: u8 = 11; +/// OpenBSD. +pub const ELFOSABI_OPENBSD: u8 = 12; +/// OpenVMS. +pub const ELFOSABI_OPENVMS: u8 = 13; +/// Hewlett-Packard Non-Stop Kernel. +pub const ELFOSABI_NSK: u8 = 14; +/// AROS +pub const ELFOSABI_AROS: u8 = 15; +/// FenixOS +pub const ELFOSABI_FENIXOS: u8 = 16; +/// Nuxi CloudABI +pub const ELFOSABI_CLOUDABI: u8 = 17; +/// ARM EABI. +pub const ELFOSABI_ARM_AEABI: u8 = 64; +/// ARM. +pub const ELFOSABI_ARM: u8 = 97; +/// Standalone (embedded) application. +pub const ELFOSABI_STANDALONE: u8 = 255; + +// Values for `FileHeader*::e_type`. +/// No file type. +pub const ET_NONE: u16 = 0; +/// Relocatable file. +pub const ET_REL: u16 = 1; +/// Executable file. +pub const ET_EXEC: u16 = 2; +/// Shared object file. +pub const ET_DYN: u16 = 3; +/// Core file. +pub const ET_CORE: u16 = 4; +/// OS-specific range start. +pub const ET_LOOS: u16 = 0xfe00; +/// OS-specific range end. +pub const ET_HIOS: u16 = 0xfeff; +/// Processor-specific range start. +pub const ET_LOPROC: u16 = 0xff00; +/// Processor-specific range end. +pub const ET_HIPROC: u16 = 0xffff; + +// Values for `FileHeader*::e_machine`. +/// No machine +pub const EM_NONE: u16 = 0; +/// AT&T WE 32100 +pub const EM_M32: u16 = 1; +/// SUN SPARC +pub const EM_SPARC: u16 = 2; +/// Intel 80386 +pub const EM_386: u16 = 3; +/// Motorola m68k family +pub const EM_68K: u16 = 4; +/// Motorola m88k family +pub const EM_88K: u16 = 5; +/// Intel MCU +pub const EM_IAMCU: u16 = 6; +/// Intel 80860 +pub const EM_860: u16 = 7; +/// MIPS R3000 big-endian +pub const EM_MIPS: u16 = 8; +/// IBM System/370 +pub const EM_S370: u16 = 9; +/// MIPS R3000 little-endian +pub const EM_MIPS_RS3_LE: u16 = 10; +/// HPPA +pub const EM_PARISC: u16 = 15; +/// Fujitsu VPP500 +pub const EM_VPP500: u16 = 17; +/// Sun's "v8plus" +pub const EM_SPARC32PLUS: u16 = 18; +/// Intel 80960 +pub const EM_960: u16 = 19; +/// PowerPC +pub const EM_PPC: u16 = 20; +/// PowerPC 64-bit +pub const EM_PPC64: u16 = 21; +/// IBM S390 +pub const EM_S390: u16 = 22; +/// IBM SPU/SPC +pub const EM_SPU: u16 = 23; +/// NEC V800 series +pub const EM_V800: u16 = 36; +/// Fujitsu FR20 +pub const EM_FR20: u16 = 37; +/// TRW RH-32 +pub const EM_RH32: u16 = 38; +/// Motorola RCE +pub const EM_RCE: u16 = 39; +/// ARM +pub const EM_ARM: u16 = 40; +/// Digital Alpha +pub const EM_FAKE_ALPHA: u16 = 41; +/// Hitachi SH +pub const EM_SH: u16 = 42; +/// SPARC v9 64-bit +pub const EM_SPARCV9: u16 = 43; +/// Siemens Tricore +pub const EM_TRICORE: u16 = 44; +/// Argonaut RISC Core +pub const EM_ARC: u16 = 45; +/// Hitachi H8/300 +pub const EM_H8_300: u16 = 46; +/// Hitachi H8/300H +pub const EM_H8_300H: u16 = 47; +/// Hitachi H8S +pub const EM_H8S: u16 = 48; +/// Hitachi H8/500 +pub const EM_H8_500: u16 = 49; +/// Intel Merced +pub const EM_IA_64: u16 = 50; +/// Stanford MIPS-X +pub const EM_MIPS_X: u16 = 51; +/// Motorola Coldfire +pub const EM_COLDFIRE: u16 = 52; +/// Motorola M68HC12 +pub const EM_68HC12: u16 = 53; +/// Fujitsu MMA Multimedia Accelerator +pub const EM_MMA: u16 = 54; +/// Siemens PCP +pub const EM_PCP: u16 = 55; +/// Sony nCPU embeeded RISC +pub const EM_NCPU: u16 = 56; +/// Denso NDR1 microprocessor +pub const EM_NDR1: u16 = 57; +/// Motorola Start*Core processor +pub const EM_STARCORE: u16 = 58; +/// Toyota ME16 processor +pub const EM_ME16: u16 = 59; +/// STMicroelectronic ST100 processor +pub const EM_ST100: u16 = 60; +/// Advanced Logic Corp. Tinyj emb.fam +pub const EM_TINYJ: u16 = 61; +/// AMD x86-64 architecture +pub const EM_X86_64: u16 = 62; +/// Sony DSP Processor +pub const EM_PDSP: u16 = 63; +/// Digital PDP-10 +pub const EM_PDP10: u16 = 64; +/// Digital PDP-11 +pub const EM_PDP11: u16 = 65; +/// Siemens FX66 microcontroller +pub const EM_FX66: u16 = 66; +/// STMicroelectronics ST9+ 8/16 mc +pub const EM_ST9PLUS: u16 = 67; +/// STmicroelectronics ST7 8 bit mc +pub const EM_ST7: u16 = 68; +/// Motorola MC68HC16 microcontroller +pub const EM_68HC16: u16 = 69; +/// Motorola MC68HC11 microcontroller +pub const EM_68HC11: u16 = 70; +/// Motorola MC68HC08 microcontroller +pub const EM_68HC08: u16 = 71; +/// Motorola MC68HC05 microcontroller +pub const EM_68HC05: u16 = 72; +/// Silicon Graphics SVx +pub const EM_SVX: u16 = 73; +/// STMicroelectronics ST19 8 bit mc +pub const EM_ST19: u16 = 74; +/// Digital VAX +pub const EM_VAX: u16 = 75; +/// Axis Communications 32-bit emb.proc +pub const EM_CRIS: u16 = 76; +/// Infineon Technologies 32-bit emb.proc +pub const EM_JAVELIN: u16 = 77; +/// Element 14 64-bit DSP Processor +pub const EM_FIREPATH: u16 = 78; +/// LSI Logic 16-bit DSP Processor +pub const EM_ZSP: u16 = 79; +/// Donald Knuth's educational 64-bit proc +pub const EM_MMIX: u16 = 80; +/// Harvard University machine-independent object files +pub const EM_HUANY: u16 = 81; +/// SiTera Prism +pub const EM_PRISM: u16 = 82; +/// Atmel AVR 8-bit microcontroller +pub const EM_AVR: u16 = 83; +/// Fujitsu FR30 +pub const EM_FR30: u16 = 84; +/// Mitsubishi D10V +pub const EM_D10V: u16 = 85; +/// Mitsubishi D30V +pub const EM_D30V: u16 = 86; +/// NEC v850 +pub const EM_V850: u16 = 87; +/// Mitsubishi M32R +pub const EM_M32R: u16 = 88; +/// Matsushita MN10300 +pub const EM_MN10300: u16 = 89; +/// Matsushita MN10200 +pub const EM_MN10200: u16 = 90; +/// picoJava +pub const EM_PJ: u16 = 91; +/// OpenRISC 32-bit embedded processor +pub const EM_OPENRISC: u16 = 92; +/// ARC International ARCompact +pub const EM_ARC_COMPACT: u16 = 93; +/// Tensilica Xtensa Architecture +pub const EM_XTENSA: u16 = 94; +/// Alphamosaic VideoCore +pub const EM_VIDEOCORE: u16 = 95; +/// Thompson Multimedia General Purpose Proc +pub const EM_TMM_GPP: u16 = 96; +/// National Semi. 32000 +pub const EM_NS32K: u16 = 97; +/// Tenor Network TPC +pub const EM_TPC: u16 = 98; +/// Trebia SNP 1000 +pub const EM_SNP1K: u16 = 99; +/// STMicroelectronics ST200 +pub const EM_ST200: u16 = 100; +/// Ubicom IP2xxx +pub const EM_IP2K: u16 = 101; +/// MAX processor +pub const EM_MAX: u16 = 102; +/// National Semi. CompactRISC +pub const EM_CR: u16 = 103; +/// Fujitsu F2MC16 +pub const EM_F2MC16: u16 = 104; +/// Texas Instruments msp430 +pub const EM_MSP430: u16 = 105; +/// Analog Devices Blackfin DSP +pub const EM_BLACKFIN: u16 = 106; +/// Seiko Epson S1C33 family +pub const EM_SE_C33: u16 = 107; +/// Sharp embedded microprocessor +pub const EM_SEP: u16 = 108; +/// Arca RISC +pub const EM_ARCA: u16 = 109; +/// PKU-Unity & MPRC Peking Uni. mc series +pub const EM_UNICORE: u16 = 110; +/// eXcess configurable cpu +pub const EM_EXCESS: u16 = 111; +/// Icera Semi. Deep Execution Processor +pub const EM_DXP: u16 = 112; +/// Altera Nios II +pub const EM_ALTERA_NIOS2: u16 = 113; +/// National Semi. CompactRISC CRX +pub const EM_CRX: u16 = 114; +/// Motorola XGATE +pub const EM_XGATE: u16 = 115; +/// Infineon C16x/XC16x +pub const EM_C166: u16 = 116; +/// Renesas M16C +pub const EM_M16C: u16 = 117; +/// Microchip Technology dsPIC30F +pub const EM_DSPIC30F: u16 = 118; +/// Freescale Communication Engine RISC +pub const EM_CE: u16 = 119; +/// Renesas M32C +pub const EM_M32C: u16 = 120; +/// Altium TSK3000 +pub const EM_TSK3000: u16 = 131; +/// Freescale RS08 +pub const EM_RS08: u16 = 132; +/// Analog Devices SHARC family +pub const EM_SHARC: u16 = 133; +/// Cyan Technology eCOG2 +pub const EM_ECOG2: u16 = 134; +/// Sunplus S+core7 RISC +pub const EM_SCORE7: u16 = 135; +/// New Japan Radio (NJR) 24-bit DSP +pub const EM_DSP24: u16 = 136; +/// Broadcom VideoCore III +pub const EM_VIDEOCORE3: u16 = 137; +/// RISC for Lattice FPGA +pub const EM_LATTICEMICO32: u16 = 138; +/// Seiko Epson C17 +pub const EM_SE_C17: u16 = 139; +/// Texas Instruments TMS320C6000 DSP +pub const EM_TI_C6000: u16 = 140; +/// Texas Instruments TMS320C2000 DSP +pub const EM_TI_C2000: u16 = 141; +/// Texas Instruments TMS320C55x DSP +pub const EM_TI_C5500: u16 = 142; +/// Texas Instruments App. Specific RISC +pub const EM_TI_ARP32: u16 = 143; +/// Texas Instruments Prog. Realtime Unit +pub const EM_TI_PRU: u16 = 144; +/// STMicroelectronics 64bit VLIW DSP +pub const EM_MMDSP_PLUS: u16 = 160; +/// Cypress M8C +pub const EM_CYPRESS_M8C: u16 = 161; +/// Renesas R32C +pub const EM_R32C: u16 = 162; +/// NXP Semi. TriMedia +pub const EM_TRIMEDIA: u16 = 163; +/// QUALCOMM Hexagon +pub const EM_HEXAGON: u16 = 164; +/// Intel 8051 and variants +pub const EM_8051: u16 = 165; +/// STMicroelectronics STxP7x +pub const EM_STXP7X: u16 = 166; +/// Andes Tech. compact code emb. RISC +pub const EM_NDS32: u16 = 167; +/// Cyan Technology eCOG1X +pub const EM_ECOG1X: u16 = 168; +/// Dallas Semi. MAXQ30 mc +pub const EM_MAXQ30: u16 = 169; +/// New Japan Radio (NJR) 16-bit DSP +pub const EM_XIMO16: u16 = 170; +/// M2000 Reconfigurable RISC +pub const EM_MANIK: u16 = 171; +/// Cray NV2 vector architecture +pub const EM_CRAYNV2: u16 = 172; +/// Renesas RX +pub const EM_RX: u16 = 173; +/// Imagination Tech. META +pub const EM_METAG: u16 = 174; +/// MCST Elbrus +pub const EM_MCST_ELBRUS: u16 = 175; +/// Cyan Technology eCOG16 +pub const EM_ECOG16: u16 = 176; +/// National Semi. CompactRISC CR16 +pub const EM_CR16: u16 = 177; +/// Freescale Extended Time Processing Unit +pub const EM_ETPU: u16 = 178; +/// Infineon Tech. SLE9X +pub const EM_SLE9X: u16 = 179; +/// Intel L10M +pub const EM_L10M: u16 = 180; +/// Intel K10M +pub const EM_K10M: u16 = 181; +/// ARM AARCH64 +pub const EM_AARCH64: u16 = 183; +/// Amtel 32-bit microprocessor +pub const EM_AVR32: u16 = 185; +/// STMicroelectronics STM8 +pub const EM_STM8: u16 = 186; +/// Tileta TILE64 +pub const EM_TILE64: u16 = 187; +/// Tilera TILEPro +pub const EM_TILEPRO: u16 = 188; +/// Xilinx MicroBlaze +pub const EM_MICROBLAZE: u16 = 189; +/// NVIDIA CUDA +pub const EM_CUDA: u16 = 190; +/// Tilera TILE-Gx +pub const EM_TILEGX: u16 = 191; +/// CloudShield +pub const EM_CLOUDSHIELD: u16 = 192; +/// KIPO-KAIST Core-A 1st gen. +pub const EM_COREA_1ST: u16 = 193; +/// KIPO-KAIST Core-A 2nd gen. +pub const EM_COREA_2ND: u16 = 194; +/// Synopsys ARCompact V2 +pub const EM_ARC_COMPACT2: u16 = 195; +/// Open8 RISC +pub const EM_OPEN8: u16 = 196; +/// Renesas RL78 +pub const EM_RL78: u16 = 197; +/// Broadcom VideoCore V +pub const EM_VIDEOCORE5: u16 = 198; +/// Renesas 78KOR +pub const EM_78KOR: u16 = 199; +/// Freescale 56800EX DSC +pub const EM_56800EX: u16 = 200; +/// Beyond BA1 +pub const EM_BA1: u16 = 201; +/// Beyond BA2 +pub const EM_BA2: u16 = 202; +/// XMOS xCORE +pub const EM_XCORE: u16 = 203; +/// Microchip 8-bit PIC(r) +pub const EM_MCHP_PIC: u16 = 204; +/// KM211 KM32 +pub const EM_KM32: u16 = 210; +/// KM211 KMX32 +pub const EM_KMX32: u16 = 211; +/// KM211 KMX16 +pub const EM_EMX16: u16 = 212; +/// KM211 KMX8 +pub const EM_EMX8: u16 = 213; +/// KM211 KVARC +pub const EM_KVARC: u16 = 214; +/// Paneve CDP +pub const EM_CDP: u16 = 215; +/// Cognitive Smart Memory Processor +pub const EM_COGE: u16 = 216; +/// Bluechip CoolEngine +pub const EM_COOL: u16 = 217; +/// Nanoradio Optimized RISC +pub const EM_NORC: u16 = 218; +/// CSR Kalimba +pub const EM_CSR_KALIMBA: u16 = 219; +/// Zilog Z80 +pub const EM_Z80: u16 = 220; +/// Controls and Data Services VISIUMcore +pub const EM_VISIUM: u16 = 221; +/// FTDI Chip FT32 +pub const EM_FT32: u16 = 222; +/// Moxie processor +pub const EM_MOXIE: u16 = 223; +/// AMD GPU +pub const EM_AMDGPU: u16 = 224; +/// RISC-V +pub const EM_RISCV: u16 = 243; +/// Linux BPF -- in-kernel virtual machine +pub const EM_BPF: u16 = 247; +/// C-SKY +pub const EM_CSKY: u16 = 252; +/// Loongson LoongArch +pub const EM_LOONGARCH: u16 = 258; +/// Solana Binary Format +pub const EM_SBF: u16 = 263; +/// Digital Alpha +pub const EM_ALPHA: u16 = 0x9026; + +// Values for `FileHeader*::e_version` and `Ident::version`. +/// Invalid ELF version. +pub const EV_NONE: u8 = 0; +/// Current ELF version. +pub const EV_CURRENT: u8 = 1; + +/// Section header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SectionHeader32<E: Endian> { + /// Section name. + /// + /// This is an offset into the section header string table. + pub sh_name: U32<E>, + /// Section type. One of the `SHT_*` constants. + pub sh_type: U32<E>, + /// Section flags. A combination of the `SHF_*` constants. + pub sh_flags: U32<E>, + /// Section virtual address at execution. + pub sh_addr: U32<E>, + /// Section file offset. + pub sh_offset: U32<E>, + /// Section size in bytes. + pub sh_size: U32<E>, + /// Link to another section. + /// + /// The section relationship depends on the `sh_type` value. + pub sh_link: U32<E>, + /// Additional section information. + /// + /// The meaning of this field depends on the `sh_type` value. + pub sh_info: U32<E>, + /// Section alignment. + pub sh_addralign: U32<E>, + /// Entry size if the section holds a table. + pub sh_entsize: U32<E>, +} + +/// Section header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SectionHeader64<E: Endian> { + /// Section name. + /// + /// This is an offset into the section header string table. + pub sh_name: U32<E>, + /// Section type. One of the `SHT_*` constants. + pub sh_type: U32<E>, + /// Section flags. A combination of the `SHF_*` constants. + pub sh_flags: U64<E>, + /// Section virtual address at execution. + pub sh_addr: U64<E>, + /// Section file offset. + pub sh_offset: U64<E>, + /// Section size in bytes. + pub sh_size: U64<E>, + /// Link to another section. + /// + /// The section relationship depends on the `sh_type` value. + pub sh_link: U32<E>, + /// Additional section information. + /// + /// The meaning of this field depends on the `sh_type` value. + pub sh_info: U32<E>, + /// Section alignment. + pub sh_addralign: U64<E>, + /// Entry size if the section holds a table. + pub sh_entsize: U64<E>, +} + +// Special values for section indices. +/// Undefined section. +pub const SHN_UNDEF: u16 = 0; +/// OS-specific range start. +/// Start of reserved section indices. +pub const SHN_LORESERVE: u16 = 0xff00; +/// Start of processor-specific section indices. +pub const SHN_LOPROC: u16 = 0xff00; +/// End of processor-specific section indices. +pub const SHN_HIPROC: u16 = 0xff1f; +/// Start of OS-specific section indices. +pub const SHN_LOOS: u16 = 0xff20; +/// End of OS-specific section indices. +pub const SHN_HIOS: u16 = 0xff3f; +/// Associated symbol is absolute. +pub const SHN_ABS: u16 = 0xfff1; +/// Associated symbol is common. +pub const SHN_COMMON: u16 = 0xfff2; +/// Section index is in the `SHT_SYMTAB_SHNDX` section. +pub const SHN_XINDEX: u16 = 0xffff; +/// End of reserved section indices. +pub const SHN_HIRESERVE: u16 = 0xffff; + +// Values for `SectionHeader*::sh_type`. +/// Section header table entry is unused. +pub const SHT_NULL: u32 = 0; +/// Program data. +pub const SHT_PROGBITS: u32 = 1; +/// Symbol table. +pub const SHT_SYMTAB: u32 = 2; +/// String table. +pub const SHT_STRTAB: u32 = 3; +/// Relocation entries with explicit addends. +pub const SHT_RELA: u32 = 4; +/// Symbol hash table. +pub const SHT_HASH: u32 = 5; +/// Dynamic linking information. +pub const SHT_DYNAMIC: u32 = 6; +/// Notes. +pub const SHT_NOTE: u32 = 7; +/// Program space with no data (bss). +pub const SHT_NOBITS: u32 = 8; +/// Relocation entries without explicit addends. +pub const SHT_REL: u32 = 9; +/// Reserved section type. +pub const SHT_SHLIB: u32 = 10; +/// Dynamic linker symbol table. +pub const SHT_DYNSYM: u32 = 11; +/// Array of constructors. +pub const SHT_INIT_ARRAY: u32 = 14; +/// Array of destructors. +pub const SHT_FINI_ARRAY: u32 = 15; +/// Array of pre-constructors. +pub const SHT_PREINIT_ARRAY: u32 = 16; +/// Section group. +pub const SHT_GROUP: u32 = 17; +/// Extended section indices for a symbol table. +pub const SHT_SYMTAB_SHNDX: u32 = 18; +/// Relocation entries; only offsets. +pub const SHT_RELR: u32 = 19; +/// Experimental CREL relocations. LLVM will change the value and +/// break compatibility in the future. +pub const SHT_CREL: u32 = 0x40000014; +/// Start of OS-specific section types. +pub const SHT_LOOS: u32 = 0x6000_0000; +/// LLVM-style dependent libraries. +pub const SHT_LLVM_DEPENDENT_LIBRARIES: u32 = 0x6fff4c04; +/// GNU SFrame stack trace format. +pub const SHT_GNU_SFRAME: u32 = 0x6fff_fff4; +/// Object attributes. +pub const SHT_GNU_ATTRIBUTES: u32 = 0x6fff_fff5; +/// GNU-style hash table. +pub const SHT_GNU_HASH: u32 = 0x6fff_fff6; +/// Prelink library list +pub const SHT_GNU_LIBLIST: u32 = 0x6fff_fff7; +/// Checksum for DSO content. +pub const SHT_CHECKSUM: u32 = 0x6fff_fff8; +/// Sun-specific low bound. +pub const SHT_LOSUNW: u32 = 0x6fff_fffa; +#[allow(non_upper_case_globals)] +pub const SHT_SUNW_move: u32 = 0x6fff_fffa; +pub const SHT_SUNW_COMDAT: u32 = 0x6fff_fffb; +#[allow(non_upper_case_globals)] +pub const SHT_SUNW_syminfo: u32 = 0x6fff_fffc; +/// Version definition section. +#[allow(non_upper_case_globals)] +pub const SHT_GNU_VERDEF: u32 = 0x6fff_fffd; +/// Version needs section. +#[allow(non_upper_case_globals)] +pub const SHT_GNU_VERNEED: u32 = 0x6fff_fffe; +/// Version symbol table. +#[allow(non_upper_case_globals)] +pub const SHT_GNU_VERSYM: u32 = 0x6fff_ffff; +/// Sun-specific high bound. +pub const SHT_HISUNW: u32 = 0x6fff_ffff; +/// End of OS-specific section types. +pub const SHT_HIOS: u32 = 0x6fff_ffff; +/// Start of processor-specific section types. +pub const SHT_LOPROC: u32 = 0x7000_0000; +/// End of processor-specific section types. +pub const SHT_HIPROC: u32 = 0x7fff_ffff; +/// Start of application-specific section types. +pub const SHT_LOUSER: u32 = 0x8000_0000; +/// End of application-specific section types. +pub const SHT_HIUSER: u32 = 0x8fff_ffff; + +// Values for `SectionHeader*::sh_flags`. +/// Section is writable. +pub const SHF_WRITE: u32 = 1 << 0; +/// Section occupies memory during execution. +pub const SHF_ALLOC: u32 = 1 << 1; +/// Section is executable. +pub const SHF_EXECINSTR: u32 = 1 << 2; +/// Section may be be merged to eliminate duplication. +pub const SHF_MERGE: u32 = 1 << 4; +/// Section contains nul-terminated strings. +pub const SHF_STRINGS: u32 = 1 << 5; +/// The `sh_info` field contains a section header table index. +pub const SHF_INFO_LINK: u32 = 1 << 6; +/// Section has special ordering requirements when combining sections. +pub const SHF_LINK_ORDER: u32 = 1 << 7; +/// Section requires special OS-specific handling. +pub const SHF_OS_NONCONFORMING: u32 = 1 << 8; +/// Section is a member of a group. +pub const SHF_GROUP: u32 = 1 << 9; +/// Section holds thread-local storage. +pub const SHF_TLS: u32 = 1 << 10; +/// Section is compressed. +/// +/// Compressed sections begin with one of the `CompressionHeader*` headers. +pub const SHF_COMPRESSED: u32 = 1 << 11; +/// OS-specific section flags. +pub const SHF_MASKOS: u32 = 0x0ff0_0000; +/// Section should not be garbage collected by the linker. +pub const SHF_GNU_RETAIN: u32 = 1 << 21; +/// Mbind section. +pub const SHF_GNU_MBIND: u32 = 1 << 24; +/// Processor-specific section flags. +pub const SHF_MASKPROC: u32 = 0xf000_0000; +/// This section is excluded from the final executable or shared library. +pub const SHF_EXCLUDE: u32 = 0x8000_0000; + +/// Section compression header. +/// +/// Used when `SHF_COMPRESSED` is set. +/// +/// Note: this type currently allows for misaligned headers, but that may be +/// changed in a future version. +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct CompressionHeader32<E: Endian> { + /// Compression format. One of the `ELFCOMPRESS_*` values. + pub ch_type: U32Bytes<E>, + /// Uncompressed data size. + pub ch_size: U32Bytes<E>, + /// Uncompressed data alignment. + pub ch_addralign: U32Bytes<E>, +} + +/// Section compression header. +/// +/// Used when `SHF_COMPRESSED` is set. +/// +/// Note: this type currently allows for misaligned headers, but that may be +/// changed in a future version. +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct CompressionHeader64<E: Endian> { + /// Compression format. One of the `ELFCOMPRESS_*` values. + pub ch_type: U32Bytes<E>, + /// Reserved. + pub ch_reserved: U32Bytes<E>, + /// Uncompressed data size. + pub ch_size: U64Bytes<E>, + /// Uncompressed data alignment. + pub ch_addralign: U64Bytes<E>, +} + +/// ZLIB/DEFLATE algorithm. +pub const ELFCOMPRESS_ZLIB: u32 = 1; +/// Zstandard algorithm. +pub const ELFCOMPRESS_ZSTD: u32 = 2; +/// Start of OS-specific compression types. +pub const ELFCOMPRESS_LOOS: u32 = 0x6000_0000; +/// End of OS-specific compression types. +pub const ELFCOMPRESS_HIOS: u32 = 0x6fff_ffff; +/// Start of processor-specific compression types. +pub const ELFCOMPRESS_LOPROC: u32 = 0x7000_0000; +/// End of processor-specific compression types. +pub const ELFCOMPRESS_HIPROC: u32 = 0x7fff_ffff; + +// Values for the flag entry for section groups. +/// Mark group as COMDAT. +pub const GRP_COMDAT: u32 = 1; + +/// Symbol table entry. +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct Sym32<E: Endian> { + /// Symbol name. + /// + /// This is an offset into the symbol string table. + pub st_name: U32<E>, + /// Symbol value. + pub st_value: U32<E>, + /// Symbol size. + pub st_size: U32<E>, + /// Symbol type and binding. + /// + /// Use the `st_type` and `st_bind` methods to access this value. + pub st_info: u8, + /// Symbol visibility. + /// + /// Use the `st_visibility` method to access this value. + pub st_other: u8, + /// Section index or one of the `SHN_*` values. + pub st_shndx: U16<E>, +} + +impl<E: Endian> Sym32<E> { + /// Get the `st_bind` component of the `st_info` field. + #[inline] + pub fn st_bind(&self) -> u8 { + self.st_info >> 4 + } + + /// Get the `st_type` component of the `st_info` field. + #[inline] + pub fn st_type(&self) -> u8 { + self.st_info & 0xf + } + + /// Set the `st_info` field given the `st_bind` and `st_type` components. + #[inline] + pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { + self.st_info = (st_bind << 4) + (st_type & 0xf); + } + + /// Get the `st_visibility` component of the `st_info` field. + #[inline] + pub fn st_visibility(&self) -> u8 { + self.st_other & 0x3 + } +} + +/// Symbol table entry. +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct Sym64<E: Endian> { + /// Symbol name. + /// + /// This is an offset into the symbol string table. + pub st_name: U32<E>, + /// Symbol type and binding. + /// + /// Use the `st_bind` and `st_type` methods to access this value. + pub st_info: u8, + /// Symbol visibility. + /// + /// Use the `st_visibility` method to access this value. + pub st_other: u8, + /// Section index or one of the `SHN_*` values. + pub st_shndx: U16<E>, + /// Symbol value. + pub st_value: U64<E>, + /// Symbol size. + pub st_size: U64<E>, +} + +impl<E: Endian> Sym64<E> { + /// Get the `st_bind` component of the `st_info` field. + #[inline] + pub fn st_bind(&self) -> u8 { + self.st_info >> 4 + } + + /// Get the `st_type` component of the `st_info` field. + #[inline] + pub fn st_type(&self) -> u8 { + self.st_info & 0xf + } + + /// Set the `st_info` field given the `st_bind` and `st_type` components. + #[inline] + pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { + self.st_info = (st_bind << 4) + (st_type & 0xf); + } + + /// Get the `st_visibility` component of the `st_info` field. + #[inline] + pub fn st_visibility(&self) -> u8 { + self.st_other & 0x3 + } +} + +/// Additional information about a `Sym32`. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Syminfo32<E: Endian> { + /// Direct bindings, symbol bound to. + pub si_boundto: U16<E>, + /// Per symbol flags. + pub si_flags: U16<E>, +} + +/// Additional information about a `Sym64`. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Syminfo64<E: Endian> { + /// Direct bindings, symbol bound to. + pub si_boundto: U16<E>, + /// Per symbol flags. + pub si_flags: U16<E>, +} + +// Values for `Syminfo*::si_boundto`. +/// Symbol bound to self +pub const SYMINFO_BT_SELF: u16 = 0xffff; +/// Symbol bound to parent +pub const SYMINFO_BT_PARENT: u16 = 0xfffe; +/// Beginning of reserved entries +pub const SYMINFO_BT_LOWRESERVE: u16 = 0xff00; + +// Values for `Syminfo*::si_flags`. +/// Direct bound symbol +pub const SYMINFO_FLG_DIRECT: u16 = 0x0001; +/// Pass-thru symbol for translator +pub const SYMINFO_FLG_PASSTHRU: u16 = 0x0002; +/// Symbol is a copy-reloc +pub const SYMINFO_FLG_COPY: u16 = 0x0004; +/// Symbol bound to object to be lazy loaded +pub const SYMINFO_FLG_LAZYLOAD: u16 = 0x0008; + +// Syminfo version values. +pub const SYMINFO_NONE: u16 = 0; +pub const SYMINFO_CURRENT: u16 = 1; +pub const SYMINFO_NUM: u16 = 2; + +// Values for bind component of `Sym*::st_info`. +/// Local symbol. +pub const STB_LOCAL: u8 = 0; +/// Global symbol. +pub const STB_GLOBAL: u8 = 1; +/// Weak symbol. +pub const STB_WEAK: u8 = 2; +/// Start of OS-specific symbol binding. +pub const STB_LOOS: u8 = 10; +/// Unique symbol. +pub const STB_GNU_UNIQUE: u8 = 10; +/// End of OS-specific symbol binding. +pub const STB_HIOS: u8 = 12; +/// Start of processor-specific symbol binding. +pub const STB_LOPROC: u8 = 13; +/// End of processor-specific symbol binding. +pub const STB_HIPROC: u8 = 15; + +// Values for type component of `Sym*::st_info`. +/// Symbol type is unspecified. +pub const STT_NOTYPE: u8 = 0; +/// Symbol is a data object. +pub const STT_OBJECT: u8 = 1; +/// Symbol is a code object. +pub const STT_FUNC: u8 = 2; +/// Symbol is associated with a section. +pub const STT_SECTION: u8 = 3; +/// Symbol's name is a file name. +pub const STT_FILE: u8 = 4; +/// Symbol is a common data object. +pub const STT_COMMON: u8 = 5; +/// Symbol is a thread-local storage object. +pub const STT_TLS: u8 = 6; +/// Start of OS-specific symbol types. +pub const STT_LOOS: u8 = 10; +/// Symbol is an indirect code object. +pub const STT_GNU_IFUNC: u8 = 10; +/// End of OS-specific symbol types. +pub const STT_HIOS: u8 = 12; +/// Start of processor-specific symbol types. +pub const STT_LOPROC: u8 = 13; +/// End of processor-specific symbol types. +pub const STT_HIPROC: u8 = 15; + +// Values for visibility component of `Symbol*::st_other`. +/// Default symbol visibility rules. +pub const STV_DEFAULT: u8 = 0; +/// Processor specific hidden class. +pub const STV_INTERNAL: u8 = 1; +/// Symbol is not visible to other components. +pub const STV_HIDDEN: u8 = 2; +/// Symbol is visible to other components, but is not preemptible. +pub const STV_PROTECTED: u8 = 3; + +/// Relocation table entry without explicit addend. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Rel32<E: Endian> { + /// Relocation address. + pub r_offset: U32<E>, + /// Relocation type and symbol index. + pub r_info: U32<E>, +} + +impl<E: Endian> Rel32<E> { + /// Get the `r_sym` component of the `r_info` field. + #[inline] + pub fn r_sym(&self, endian: E) -> u32 { + self.r_info.get(endian) >> 8 + } + + /// Get the `r_type` component of the `r_info` field. + #[inline] + pub fn r_type(&self, endian: E) -> u32 { + self.r_info.get(endian) & 0xff + } + + /// Calculate the `r_info` field given the `r_sym` and `r_type` components. + pub fn r_info(endian: E, r_sym: u32, r_type: u8) -> U32<E> { + U32::new(endian, (r_sym << 8) | u32::from(r_type)) + } + + /// Set the `r_info` field given the `r_sym` and `r_type` components. + pub fn set_r_info(&mut self, endian: E, r_sym: u32, r_type: u8) { + self.r_info = Self::r_info(endian, r_sym, r_type) + } +} + +/// Relocation table entry with explicit addend. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Rela32<E: Endian> { + /// Relocation address. + pub r_offset: U32<E>, + /// Relocation type and symbol index. + pub r_info: U32<E>, + /// Explicit addend. + pub r_addend: I32<E>, +} + +impl<E: Endian> Rela32<E> { + /// Get the `r_sym` component of the `r_info` field. + #[inline] + pub fn r_sym(&self, endian: E) -> u32 { + self.r_info.get(endian) >> 8 + } + + /// Get the `r_type` component of the `r_info` field. + #[inline] + pub fn r_type(&self, endian: E) -> u32 { + self.r_info.get(endian) & 0xff + } + + /// Calculate the `r_info` field given the `r_sym` and `r_type` components. + pub fn r_info(endian: E, r_sym: u32, r_type: u8) -> U32<E> { + U32::new(endian, (r_sym << 8) | u32::from(r_type)) + } + + /// Set the `r_info` field given the `r_sym` and `r_type` components. + pub fn set_r_info(&mut self, endian: E, r_sym: u32, r_type: u8) { + self.r_info = Self::r_info(endian, r_sym, r_type) + } +} + +impl<E: Endian> From<Rel32<E>> for Rela32<E> { + fn from(rel: Rel32<E>) -> Self { + Rela32 { + r_offset: rel.r_offset, + r_info: rel.r_info, + r_addend: I32::default(), + } + } +} + +/// Relocation table entry without explicit addend. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Rel64<E: Endian> { + /// Relocation address. + pub r_offset: U64<E>, + /// Relocation type and symbol index. + pub r_info: U64<E>, +} + +impl<E: Endian> Rel64<E> { + /// Get the `r_sym` component of the `r_info` field. + #[inline] + pub fn r_sym(&self, endian: E) -> u32 { + (self.r_info.get(endian) >> 32) as u32 + } + + /// Get the `r_type` component of the `r_info` field. + #[inline] + pub fn r_type(&self, endian: E) -> u32 { + (self.r_info.get(endian) & 0xffff_ffff) as u32 + } + + /// Calculate the `r_info` field given the `r_sym` and `r_type` components. + pub fn r_info(endian: E, r_sym: u32, r_type: u32) -> U64<E> { + U64::new(endian, (u64::from(r_sym) << 32) | u64::from(r_type)) + } + + /// Set the `r_info` field given the `r_sym` and `r_type` components. + pub fn set_r_info(&mut self, endian: E, r_sym: u32, r_type: u32) { + self.r_info = Self::r_info(endian, r_sym, r_type) + } +} + +impl<E: Endian> From<Rel64<E>> for Rela64<E> { + fn from(rel: Rel64<E>) -> Self { + Rela64 { + r_offset: rel.r_offset, + r_info: rel.r_info, + r_addend: I64::default(), + } + } +} + +/// Relocation table entry with explicit addend. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Rela64<E: Endian> { + /// Relocation address. + pub r_offset: U64<E>, + /// Relocation type and symbol index. + pub r_info: U64<E>, + /// Explicit addend. + pub r_addend: I64<E>, +} + +impl<E: Endian> Rela64<E> { + pub(crate) fn get_r_info(&self, endian: E, is_mips64el: bool) -> u64 { + let mut t = self.r_info.get(endian); + if is_mips64el { + t = (t << 32) + | ((t >> 8) & 0xff000000) + | ((t >> 24) & 0x00ff0000) + | ((t >> 40) & 0x0000ff00) + | ((t >> 56) & 0x000000ff); + } + t + } + + /// Get the `r_sym` component of the `r_info` field. + #[inline] + pub fn r_sym(&self, endian: E, is_mips64el: bool) -> u32 { + (self.get_r_info(endian, is_mips64el) >> 32) as u32 + } + + /// Get the `r_type` component of the `r_info` field. + #[inline] + pub fn r_type(&self, endian: E, is_mips64el: bool) -> u32 { + (self.get_r_info(endian, is_mips64el) & 0xffff_ffff) as u32 + } + + /// Calculate the `r_info` field given the `r_sym` and `r_type` components. + pub fn r_info(endian: E, is_mips64el: bool, r_sym: u32, r_type: u32) -> U64<E> { + let mut t = (u64::from(r_sym) << 32) | u64::from(r_type); + if is_mips64el { + t = (t >> 32) + | ((t & 0xff000000) << 8) + | ((t & 0x00ff0000) << 24) + | ((t & 0x0000ff00) << 40) + | ((t & 0x000000ff) << 56); + } + U64::new(endian, t) + } + + /// Set the `r_info` field given the `r_sym` and `r_type` components. + pub fn set_r_info(&mut self, endian: E, is_mips64el: bool, r_sym: u32, r_type: u32) { + self.r_info = Self::r_info(endian, is_mips64el, r_sym, r_type); + } +} + +/// 32-bit relative relocation table entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Relr32<E: Endian>(pub U32<E>); + +/// 64-bit relative relocation table entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Relr64<E: Endian>(pub U64<E>); + +/// Program segment header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ProgramHeader32<E: Endian> { + /// Segment type. One of the `PT_*` constants. + pub p_type: U32<E>, + /// Segment file offset. + pub p_offset: U32<E>, + /// Segment virtual address. + pub p_vaddr: U32<E>, + /// Segment physical address. + pub p_paddr: U32<E>, + /// Segment size in the file. + pub p_filesz: U32<E>, + /// Segment size in memory. + pub p_memsz: U32<E>, + /// Segment flags. A combination of the `PF_*` constants. + pub p_flags: U32<E>, + /// Segment alignment. + pub p_align: U32<E>, +} + +/// Program segment header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ProgramHeader64<E: Endian> { + /// Segment type. One of the `PT_*` constants. + pub p_type: U32<E>, + /// Segment flags. A combination of the `PF_*` constants. + pub p_flags: U32<E>, + /// Segment file offset. + pub p_offset: U64<E>, + /// Segment virtual address. + pub p_vaddr: U64<E>, + /// Segment physical address. + pub p_paddr: U64<E>, + /// Segment size in the file. + pub p_filesz: U64<E>, + /// Segment size in memory. + pub p_memsz: U64<E>, + /// Segment alignment. + pub p_align: U64<E>, +} + +/// Special value for `FileHeader*::e_phnum`. +/// +/// This indicates that the real number of program headers is too large to fit into e_phnum. +/// Instead the real value is in the field `sh_info` of section 0. +pub const PN_XNUM: u16 = 0xffff; + +// Values for `ProgramHeader*::p_type`. +/// Program header table entry is unused. +pub const PT_NULL: u32 = 0; +/// Loadable program segment. +pub const PT_LOAD: u32 = 1; +/// Dynamic linking information. +pub const PT_DYNAMIC: u32 = 2; +/// Program interpreter. +pub const PT_INTERP: u32 = 3; +/// Auxiliary information. +pub const PT_NOTE: u32 = 4; +/// Reserved. +pub const PT_SHLIB: u32 = 5; +/// Segment contains the program header table. +pub const PT_PHDR: u32 = 6; +/// Thread-local storage segment. +pub const PT_TLS: u32 = 7; +/// Start of OS-specific segment types. +pub const PT_LOOS: u32 = 0x6000_0000; +/// GCC `.eh_frame_hdr` segment. +pub const PT_GNU_EH_FRAME: u32 = 0x6474_e550; +/// Indicates stack executability. +pub const PT_GNU_STACK: u32 = 0x6474_e551; +/// Read-only after relocation. +pub const PT_GNU_RELRO: u32 = 0x6474_e552; +/// Segment containing `.note.gnu.property` section. +pub const PT_GNU_PROPERTY: u32 = 0x6474_e553; +/// GNU SFrame stack trace format. +pub const PT_GNU_SFRAME: u32 = 0x6474_e554; +/// End of OS-specific segment types. +pub const PT_HIOS: u32 = 0x6fff_ffff; +/// Start of processor-specific segment types. +pub const PT_LOPROC: u32 = 0x7000_0000; +/// End of processor-specific segment types. +pub const PT_HIPROC: u32 = 0x7fff_ffff; + +// Values for `ProgramHeader*::p_flags`. +/// Segment is executable. +pub const PF_X: u32 = 1 << 0; +/// Segment is writable. +pub const PF_W: u32 = 1 << 1; +/// Segment is readable. +pub const PF_R: u32 = 1 << 2; +/// OS-specific segment flags. +pub const PF_MASKOS: u32 = 0x0ff0_0000; +/// Processor-specific segment flags. +pub const PF_MASKPROC: u32 = 0xf000_0000; + +/// Note name for core files. +pub const ELF_NOTE_CORE: &[u8] = b"CORE"; +/// Note name for linux core files. +/// +/// Notes in linux core files may also use `ELF_NOTE_CORE`. +pub const ELF_NOTE_LINUX: &[u8] = b"LINUX"; + +// Values for `NoteHeader*::n_type` in core files. +// +/// Contains copy of prstatus struct. +pub const NT_PRSTATUS: u32 = 1; +/// Contains copy of fpregset struct. +pub const NT_PRFPREG: u32 = 2; +/// Contains copy of fpregset struct. +pub const NT_FPREGSET: u32 = 2; +/// Contains copy of prpsinfo struct. +pub const NT_PRPSINFO: u32 = 3; +/// Contains copy of prxregset struct. +pub const NT_PRXREG: u32 = 4; +/// Contains copy of task structure. +pub const NT_TASKSTRUCT: u32 = 4; +/// String from sysinfo(SI_PLATFORM). +pub const NT_PLATFORM: u32 = 5; +/// Contains copy of auxv array. +pub const NT_AUXV: u32 = 6; +/// Contains copy of gwindows struct. +pub const NT_GWINDOWS: u32 = 7; +/// Contains copy of asrset struct. +pub const NT_ASRS: u32 = 8; +/// Contains copy of pstatus struct. +pub const NT_PSTATUS: u32 = 10; +/// Contains copy of psinfo struct. +pub const NT_PSINFO: u32 = 13; +/// Contains copy of prcred struct. +pub const NT_PRCRED: u32 = 14; +/// Contains copy of utsname struct. +pub const NT_UTSNAME: u32 = 15; +/// Contains copy of lwpstatus struct. +pub const NT_LWPSTATUS: u32 = 16; +/// Contains copy of lwpinfo struct. +pub const NT_LWPSINFO: u32 = 17; +/// Contains copy of fprxregset struct. +pub const NT_PRFPXREG: u32 = 20; +/// Contains copy of siginfo_t, size might increase. +pub const NT_SIGINFO: u32 = 0x5349_4749; +/// Contains information about mapped files. +pub const NT_FILE: u32 = 0x4649_4c45; +/// Contains copy of user_fxsr_struct. +pub const NT_PRXFPREG: u32 = 0x46e6_2b7f; +/// PowerPC Altivec/VMX registers. +pub const NT_PPC_VMX: u32 = 0x100; +/// PowerPC SPE/EVR registers. +pub const NT_PPC_SPE: u32 = 0x101; +/// PowerPC VSX registers. +pub const NT_PPC_VSX: u32 = 0x102; +/// Target Address Register. +pub const NT_PPC_TAR: u32 = 0x103; +/// Program Priority Register. +pub const NT_PPC_PPR: u32 = 0x104; +/// Data Stream Control Register. +pub const NT_PPC_DSCR: u32 = 0x105; +/// Event Based Branch Registers. +pub const NT_PPC_EBB: u32 = 0x106; +/// Performance Monitor Registers. +pub const NT_PPC_PMU: u32 = 0x107; +/// TM checkpointed GPR Registers. +pub const NT_PPC_TM_CGPR: u32 = 0x108; +/// TM checkpointed FPR Registers. +pub const NT_PPC_TM_CFPR: u32 = 0x109; +/// TM checkpointed VMX Registers. +pub const NT_PPC_TM_CVMX: u32 = 0x10a; +/// TM checkpointed VSX Registers. +pub const NT_PPC_TM_CVSX: u32 = 0x10b; +/// TM Special Purpose Registers. +pub const NT_PPC_TM_SPR: u32 = 0x10c; +/// TM checkpointed Target Address Register. +pub const NT_PPC_TM_CTAR: u32 = 0x10d; +/// TM checkpointed Program Priority Register. +pub const NT_PPC_TM_CPPR: u32 = 0x10e; +/// TM checkpointed Data Stream Control Register. +pub const NT_PPC_TM_CDSCR: u32 = 0x10f; +/// Memory Protection Keys registers. +pub const NT_PPC_PKEY: u32 = 0x110; +/// i386 TLS slots (struct user_desc). +pub const NT_386_TLS: u32 = 0x200; +/// x86 io permission bitmap (1=deny). +pub const NT_386_IOPERM: u32 = 0x201; +/// x86 extended state using xsave. +pub const NT_X86_XSTATE: u32 = 0x202; +/// s390 upper register halves. +pub const NT_S390_HIGH_GPRS: u32 = 0x300; +/// s390 timer register. +pub const NT_S390_TIMER: u32 = 0x301; +/// s390 TOD clock comparator register. +pub const NT_S390_TODCMP: u32 = 0x302; +/// s390 TOD programmable register. +pub const NT_S390_TODPREG: u32 = 0x303; +/// s390 control registers. +pub const NT_S390_CTRS: u32 = 0x304; +/// s390 prefix register. +pub const NT_S390_PREFIX: u32 = 0x305; +/// s390 breaking event address. +pub const NT_S390_LAST_BREAK: u32 = 0x306; +/// s390 system call restart data. +pub const NT_S390_SYSTEM_CALL: u32 = 0x307; +/// s390 transaction diagnostic block. +pub const NT_S390_TDB: u32 = 0x308; +/// s390 vector registers 0-15 upper half. +pub const NT_S390_VXRS_LOW: u32 = 0x309; +/// s390 vector registers 16-31. +pub const NT_S390_VXRS_HIGH: u32 = 0x30a; +/// s390 guarded storage registers. +pub const NT_S390_GS_CB: u32 = 0x30b; +/// s390 guarded storage broadcast control block. +pub const NT_S390_GS_BC: u32 = 0x30c; +/// s390 runtime instrumentation. +pub const NT_S390_RI_CB: u32 = 0x30d; +/// ARM VFP/NEON registers. +pub const NT_ARM_VFP: u32 = 0x400; +/// ARM TLS register. +pub const NT_ARM_TLS: u32 = 0x401; +/// ARM hardware breakpoint registers. +pub const NT_ARM_HW_BREAK: u32 = 0x402; +/// ARM hardware watchpoint registers. +pub const NT_ARM_HW_WATCH: u32 = 0x403; +/// ARM system call number. +pub const NT_ARM_SYSTEM_CALL: u32 = 0x404; +/// ARM Scalable Vector Extension registers. +pub const NT_ARM_SVE: u32 = 0x405; +/// Vmcore Device Dump Note. +pub const NT_VMCOREDD: u32 = 0x700; +/// MIPS DSP ASE registers. +pub const NT_MIPS_DSP: u32 = 0x800; +/// MIPS floating-point mode. +pub const NT_MIPS_FP_MODE: u32 = 0x801; + +/// Note type for version string. +/// +/// This note may appear in object files. +/// +/// It must be handled as a special case because it has no descriptor, and instead +/// uses the note name as the version string. +pub const NT_VERSION: u32 = 1; + +/// Dynamic section entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Dyn32<E: Endian> { + /// Dynamic entry type. + pub d_tag: U32<E>, + /// Value (integer or address). + pub d_val: U32<E>, +} + +/// Dynamic section entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Dyn64<E: Endian> { + /// Dynamic entry type. + pub d_tag: U64<E>, + /// Value (integer or address). + pub d_val: U64<E>, +} + +// Values for `Dyn*::d_tag`. + +/// Marks end of dynamic section +pub const DT_NULL: u32 = 0; +/// Name of needed library +pub const DT_NEEDED: u32 = 1; +/// Size in bytes of PLT relocs +pub const DT_PLTRELSZ: u32 = 2; +/// Processor defined value +pub const DT_PLTGOT: u32 = 3; +/// Address of symbol hash table +pub const DT_HASH: u32 = 4; +/// Address of string table +pub const DT_STRTAB: u32 = 5; +/// Address of symbol table +pub const DT_SYMTAB: u32 = 6; +/// Address of Rela relocs +pub const DT_RELA: u32 = 7; +/// Total size of Rela relocs +pub const DT_RELASZ: u32 = 8; +/// Size of one Rela reloc +pub const DT_RELAENT: u32 = 9; +/// Size of string table +pub const DT_STRSZ: u32 = 10; +/// Size of one symbol table entry +pub const DT_SYMENT: u32 = 11; +/// Address of init function +pub const DT_INIT: u32 = 12; +/// Address of termination function +pub const DT_FINI: u32 = 13; +/// Name of shared object +pub const DT_SONAME: u32 = 14; +/// Library search path (deprecated) +pub const DT_RPATH: u32 = 15; +/// Start symbol search here +pub const DT_SYMBOLIC: u32 = 16; +/// Address of Rel relocs +pub const DT_REL: u32 = 17; +/// Total size of Rel relocs +pub const DT_RELSZ: u32 = 18; +/// Size of one Rel reloc +pub const DT_RELENT: u32 = 19; +/// Type of reloc in PLT +pub const DT_PLTREL: u32 = 20; +/// For debugging; unspecified +pub const DT_DEBUG: u32 = 21; +/// Reloc might modify .text +pub const DT_TEXTREL: u32 = 22; +/// Address of PLT relocs +pub const DT_JMPREL: u32 = 23; +/// Process relocations of object +pub const DT_BIND_NOW: u32 = 24; +/// Array with addresses of init fct +pub const DT_INIT_ARRAY: u32 = 25; +/// Array with addresses of fini fct +pub const DT_FINI_ARRAY: u32 = 26; +/// Size in bytes of DT_INIT_ARRAY +pub const DT_INIT_ARRAYSZ: u32 = 27; +/// Size in bytes of DT_FINI_ARRAY +pub const DT_FINI_ARRAYSZ: u32 = 28; +/// Library search path +pub const DT_RUNPATH: u32 = 29; +/// Flags for the object being loaded +pub const DT_FLAGS: u32 = 30; +/// Start of encoded range +pub const DT_ENCODING: u32 = 32; +/// Array with addresses of preinit fct +pub const DT_PREINIT_ARRAY: u32 = 32; +/// size in bytes of DT_PREINIT_ARRAY +pub const DT_PREINIT_ARRAYSZ: u32 = 33; +/// Address of SYMTAB_SHNDX section +pub const DT_SYMTAB_SHNDX: u32 = 34; +/// Start of OS-specific +pub const DT_LOOS: u32 = 0x6000_000d; +/// End of OS-specific +pub const DT_HIOS: u32 = 0x6fff_f000; +/// Start of processor-specific +pub const DT_LOPROC: u32 = 0x7000_0000; +/// End of processor-specific +pub const DT_HIPROC: u32 = 0x7fff_ffff; + +// `DT_*` entries between `DT_VALRNGHI` & `DT_VALRNGLO` use `d_val` as a value. +pub const DT_VALRNGLO: u32 = 0x6fff_fd00; +/// Prelinking timestamp +pub const DT_GNU_PRELINKED: u32 = 0x6fff_fdf5; +/// Size of conflict section +pub const DT_GNU_CONFLICTSZ: u32 = 0x6fff_fdf6; +/// Size of library list +pub const DT_GNU_LIBLISTSZ: u32 = 0x6fff_fdf7; +pub const DT_CHECKSUM: u32 = 0x6fff_fdf8; +pub const DT_PLTPADSZ: u32 = 0x6fff_fdf9; +pub const DT_MOVEENT: u32 = 0x6fff_fdfa; +pub const DT_MOVESZ: u32 = 0x6fff_fdfb; +/// Feature selection (DTF_*). +pub const DT_FEATURE_1: u32 = 0x6fff_fdfc; +/// Flags for DT_* entries, affecting the following DT_* entry. +pub const DT_POSFLAG_1: u32 = 0x6fff_fdfd; +/// Size of syminfo table (in bytes) +pub const DT_SYMINSZ: u32 = 0x6fff_fdfe; +/// Entry size of syminfo +pub const DT_SYMINENT: u32 = 0x6fff_fdff; +pub const DT_VALRNGHI: u32 = 0x6fff_fdff; + +// `DT_*` entries between `DT_ADDRRNGHI` & `DT_ADDRRNGLO` use `d_val` as an address. +// +// If any adjustment is made to the ELF object after it has been +// built these entries will need to be adjusted. +pub const DT_ADDRRNGLO: u32 = 0x6fff_fe00; +/// GNU-style hash table. +pub const DT_GNU_HASH: u32 = 0x6fff_fef5; +pub const DT_TLSDESC_PLT: u32 = 0x6fff_fef6; +pub const DT_TLSDESC_GOT: u32 = 0x6fff_fef7; +/// Start of conflict section +pub const DT_GNU_CONFLICT: u32 = 0x6fff_fef8; +/// Library list +pub const DT_GNU_LIBLIST: u32 = 0x6fff_fef9; +/// Configuration information. +pub const DT_CONFIG: u32 = 0x6fff_fefa; +/// Dependency auditing. +pub const DT_DEPAUDIT: u32 = 0x6fff_fefb; +/// Object auditing. +pub const DT_AUDIT: u32 = 0x6fff_fefc; +/// PLT padding. +pub const DT_PLTPAD: u32 = 0x6fff_fefd; +/// Move table. +pub const DT_MOVETAB: u32 = 0x6fff_fefe; +/// Syminfo table. +pub const DT_SYMINFO: u32 = 0x6fff_feff; +pub const DT_ADDRRNGHI: u32 = 0x6fff_feff; + +// The versioning entry types. The next are defined as part of the +// GNU extension. +pub const DT_VERSYM: u32 = 0x6fff_fff0; +pub const DT_RELACOUNT: u32 = 0x6fff_fff9; +pub const DT_RELCOUNT: u32 = 0x6fff_fffa; +/// State flags, see DF_1_* below. +pub const DT_FLAGS_1: u32 = 0x6fff_fffb; +/// Address of version definition table +pub const DT_VERDEF: u32 = 0x6fff_fffc; +/// Number of version definitions +pub const DT_VERDEFNUM: u32 = 0x6fff_fffd; +/// Address of table with needed versions +pub const DT_VERNEED: u32 = 0x6fff_fffe; +/// Number of needed versions +pub const DT_VERNEEDNUM: u32 = 0x6fff_ffff; + +// Machine-independent extensions in the "processor-specific" range. +/// Shared object to load before self +pub const DT_AUXILIARY: u32 = 0x7fff_fffd; +/// Shared object to get values from +pub const DT_FILTER: u32 = 0x7fff_ffff; + +// Values of `Dyn*::d_val` in the `DT_FLAGS` entry. +/// Object may use DF_ORIGIN +pub const DF_ORIGIN: u32 = 0x0000_0001; +/// Symbol resolutions starts here +pub const DF_SYMBOLIC: u32 = 0x0000_0002; +/// Object contains text relocations +pub const DF_TEXTREL: u32 = 0x0000_0004; +/// No lazy binding for this object +pub const DF_BIND_NOW: u32 = 0x0000_0008; +/// Module uses the static TLS model +pub const DF_STATIC_TLS: u32 = 0x0000_0010; + +// Values of `Dyn*::d_val` in the `DT_FLAGS_1` entry. +/// Set RTLD_NOW for this object. +pub const DF_1_NOW: u32 = 0x0000_0001; +/// Set RTLD_GLOBAL for this object. +pub const DF_1_GLOBAL: u32 = 0x0000_0002; +/// Set RTLD_GROUP for this object. +pub const DF_1_GROUP: u32 = 0x0000_0004; +/// Set RTLD_NODELETE for this object. +pub const DF_1_NODELETE: u32 = 0x0000_0008; +/// Trigger filtee loading at runtime. +pub const DF_1_LOADFLTR: u32 = 0x0000_0010; +/// Set RTLD_INITFIRST for this object. +pub const DF_1_INITFIRST: u32 = 0x0000_0020; +/// Set RTLD_NOOPEN for this object. +pub const DF_1_NOOPEN: u32 = 0x0000_0040; +/// $ORIGIN must be handled. +pub const DF_1_ORIGIN: u32 = 0x0000_0080; +/// Direct binding enabled. +pub const DF_1_DIRECT: u32 = 0x0000_0100; +pub const DF_1_TRANS: u32 = 0x0000_0200; +/// Object is used to interpose. +pub const DF_1_INTERPOSE: u32 = 0x0000_0400; +/// Ignore default lib search path. +pub const DF_1_NODEFLIB: u32 = 0x0000_0800; +/// Object can't be dldump'ed. +pub const DF_1_NODUMP: u32 = 0x0000_1000; +/// Configuration alternative created. +pub const DF_1_CONFALT: u32 = 0x0000_2000; +/// Filtee terminates filters search. +pub const DF_1_ENDFILTEE: u32 = 0x0000_4000; +/// Disp reloc applied at build time. +pub const DF_1_DISPRELDNE: u32 = 0x0000_8000; +/// Disp reloc applied at run-time. +pub const DF_1_DISPRELPND: u32 = 0x0001_0000; +/// Object has no-direct binding. +pub const DF_1_NODIRECT: u32 = 0x0002_0000; +pub const DF_1_IGNMULDEF: u32 = 0x0004_0000; +pub const DF_1_NOKSYMS: u32 = 0x0008_0000; +pub const DF_1_NOHDR: u32 = 0x0010_0000; +/// Object is modified after built. +pub const DF_1_EDITED: u32 = 0x0020_0000; +pub const DF_1_NORELOC: u32 = 0x0040_0000; +/// Object has individual interposers. +pub const DF_1_SYMINTPOSE: u32 = 0x0080_0000; +/// Global auditing required. +pub const DF_1_GLOBAUDIT: u32 = 0x0100_0000; +/// Singleton symbols are used. +pub const DF_1_SINGLETON: u32 = 0x0200_0000; +pub const DF_1_STUB: u32 = 0x0400_0000; +pub const DF_1_PIE: u32 = 0x0800_0000; + +/// Version symbol information +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Versym<E: Endian>(pub U16<E>); + +/// Symbol is hidden. +pub const VERSYM_HIDDEN: u16 = 0x8000; +/// Symbol version index. +pub const VERSYM_VERSION: u16 = 0x7fff; + +/// Version definition sections +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Verdef<E: Endian> { + /// Version revision + pub vd_version: U16<E>, + /// Version information + pub vd_flags: U16<E>, + /// Version Index + pub vd_ndx: U16<E>, + /// Number of associated aux entries + pub vd_cnt: U16<E>, + /// Version name hash value + pub vd_hash: U32<E>, + /// Offset in bytes to verdaux array + pub vd_aux: U32<E>, + /// Offset in bytes to next verdef entry + pub vd_next: U32<E>, +} + +// Legal values for vd_version (version revision). +/// No version +pub const VER_DEF_NONE: u16 = 0; +/// Current version +pub const VER_DEF_CURRENT: u16 = 1; + +// Legal values for vd_flags (version information flags). +/// Version definition of file itself +pub const VER_FLG_BASE: u16 = 0x1; +// Legal values for vd_flags and vna_flags (version information flags). +/// Weak version identifier +pub const VER_FLG_WEAK: u16 = 0x2; + +// Versym symbol index values. +/// Symbol is local. +pub const VER_NDX_LOCAL: u16 = 0; +/// Symbol is global. +pub const VER_NDX_GLOBAL: u16 = 1; + +/// Auxiliary version information. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Verdaux<E: Endian> { + /// Version or dependency names + pub vda_name: U32<E>, + /// Offset in bytes to next verdaux + pub vda_next: U32<E>, +} + +/// Version dependency. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Verneed<E: Endian> { + /// Version of structure + pub vn_version: U16<E>, + /// Number of associated aux entries + pub vn_cnt: U16<E>, + /// Offset of filename for this dependency + pub vn_file: U32<E>, + /// Offset in bytes to vernaux array + pub vn_aux: U32<E>, + /// Offset in bytes to next verneed entry + pub vn_next: U32<E>, +} + +// Legal values for vn_version (version revision). +/// No version +pub const VER_NEED_NONE: u16 = 0; +/// Current version +pub const VER_NEED_CURRENT: u16 = 1; + +/// Auxiliary needed version information. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Vernaux<E: Endian> { + /// Hash value of dependency name + pub vna_hash: U32<E>, + /// Dependency specific information + pub vna_flags: U16<E>, + /// Version Index + pub vna_other: U16<E>, + /// Dependency name string offset + pub vna_name: U32<E>, + /// Offset in bytes to next vernaux entry + pub vna_next: U32<E>, +} + +// TODO: Elf*_auxv_t, AT_* + +/// Note section entry header. +/// +/// A note consists of a header followed by a variable length name and descriptor. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct NoteHeader32<E: Endian> { + /// Length of the note's name. + /// + /// Some known names are defined by the `ELF_NOTE_*` constants. + pub n_namesz: U32<E>, + /// Length of the note's descriptor. + /// + /// The content of the descriptor depends on the note name and type. + pub n_descsz: U32<E>, + /// Type of the note. + /// + /// One of the `NT_*` constants. The note name determines which + /// `NT_*` constants are valid. + pub n_type: U32<E>, +} + +/// Note section entry header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct NoteHeader64<E: Endian> { + /// Length of the note's name. + /// + /// Some known names are defined by the `ELF_NOTE_*` constants. + pub n_namesz: U32<E>, + /// Length of the note's descriptor. + /// + /// The content of the descriptor depends on the note name and type. + pub n_descsz: U32<E>, + /// Type of the note. + /// + /// One of the `NT_*` constants. The note name determines which + /// `NT_*` constants are valid. + pub n_type: U32<E>, +} + +/// Solaris entries in the note section have this name. +pub const ELF_NOTE_SOLARIS: &[u8] = b"SUNW Solaris"; + +// Values for `n_type` when the name is `ELF_NOTE_SOLARIS`. +/// Desired pagesize for the binary. +pub const NT_SOLARIS_PAGESIZE_HINT: u32 = 1; + +/// GNU entries in the note section have this name. +pub const ELF_NOTE_GNU: &[u8] = b"GNU"; + +/// Go entries in the note section have this name. +// See https://go-review.googlesource.com/9520 and https://go-review.googlesource.com/10704. +pub const ELF_NOTE_GO: &[u8] = b"Go"; + +// Note types for `ELF_NOTE_GNU`. + +/// ABI information. +/// +/// The descriptor consists of words: +/// - word 0: OS descriptor +/// - word 1: major version of the ABI +/// - word 2: minor version of the ABI +/// - word 3: subminor version of the ABI +pub const NT_GNU_ABI_TAG: u32 = 1; + +/// OS descriptor for `NT_GNU_ABI_TAG`. +pub const ELF_NOTE_OS_LINUX: u32 = 0; +/// OS descriptor for `NT_GNU_ABI_TAG`. +pub const ELF_NOTE_OS_GNU: u32 = 1; +/// OS descriptor for `NT_GNU_ABI_TAG`. +pub const ELF_NOTE_OS_SOLARIS2: u32 = 2; +/// OS descriptor for `NT_GNU_ABI_TAG`. +pub const ELF_NOTE_OS_FREEBSD: u32 = 3; + +/// Synthetic hwcap information. +/// +/// The descriptor begins with two words: +/// - word 0: number of entries +/// - word 1: bitmask of enabled entries +/// +/// Then follow variable-length entries, one byte followed by a +/// '\0'-terminated hwcap name string. The byte gives the bit +/// number to test if enabled, (1U << bit) & bitmask. */ +pub const NT_GNU_HWCAP: u32 = 2; + +/// Build ID bits as generated by `ld --build-id`. +/// +/// The descriptor consists of any nonzero number of bytes. +pub const NT_GNU_BUILD_ID: u32 = 3; + +/// Build ID bits as generated by Go's gc compiler. +/// +/// The descriptor consists of any nonzero number of bytes. +// See https://go-review.googlesource.com/10707. +pub const NT_GO_BUILD_ID: u32 = 4; + +/// Version note generated by GNU gold containing a version string. +pub const NT_GNU_GOLD_VERSION: u32 = 4; + +/// Program property. +pub const NT_GNU_PROPERTY_TYPE_0: u32 = 5; + +// Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). + +/// Stack size. +pub const GNU_PROPERTY_STACK_SIZE: u32 = 1; +/// No copy relocation on protected data symbol. +pub const GNU_PROPERTY_NO_COPY_ON_PROTECTED: u32 = 2; + +// A 4-byte unsigned integer property: A bit is set if it is set in all +// relocatable inputs. +pub const GNU_PROPERTY_UINT32_AND_LO: u32 = 0xb0000000; +pub const GNU_PROPERTY_UINT32_AND_HI: u32 = 0xb0007fff; + +// A 4-byte unsigned integer property: A bit is set if it is set in any +// relocatable inputs. +pub const GNU_PROPERTY_UINT32_OR_LO: u32 = 0xb0008000; +pub const GNU_PROPERTY_UINT32_OR_HI: u32 = 0xb000ffff; + +/// The needed properties by the object file. */ +pub const GNU_PROPERTY_1_NEEDED: u32 = GNU_PROPERTY_UINT32_OR_LO; + +/// Set if the object file requires canonical function pointers and +/// cannot be used with copy relocation. +pub const GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS: u32 = 1 << 0; + +/// Processor-specific semantics, lo +pub const GNU_PROPERTY_LOPROC: u32 = 0xc0000000; +/// Processor-specific semantics, hi +pub const GNU_PROPERTY_HIPROC: u32 = 0xdfffffff; +/// Application-specific semantics, lo +pub const GNU_PROPERTY_LOUSER: u32 = 0xe0000000; +/// Application-specific semantics, hi +pub const GNU_PROPERTY_HIUSER: u32 = 0xffffffff; + +/// AArch64 specific GNU properties. +pub const GNU_PROPERTY_AARCH64_FEATURE_1_AND: u32 = 0xc0000000; +pub const GNU_PROPERTY_AARCH64_FEATURE_PAUTH: u32 = 0xc0000001; + +pub const GNU_PROPERTY_AARCH64_FEATURE_1_BTI: u32 = 1 << 0; +pub const GNU_PROPERTY_AARCH64_FEATURE_1_PAC: u32 = 1 << 1; + +// A 4-byte unsigned integer property: A bit is set if it is set in all +// relocatable inputs. +pub const GNU_PROPERTY_X86_UINT32_AND_LO: u32 = 0xc0000002; +pub const GNU_PROPERTY_X86_UINT32_AND_HI: u32 = 0xc0007fff; + +// A 4-byte unsigned integer property: A bit is set if it is set in any +// relocatable inputs. +pub const GNU_PROPERTY_X86_UINT32_OR_LO: u32 = 0xc0008000; +pub const GNU_PROPERTY_X86_UINT32_OR_HI: u32 = 0xc000ffff; + +// A 4-byte unsigned integer property: A bit is set if it is set in any +// relocatable inputs and the property is present in all relocatable +// inputs. +pub const GNU_PROPERTY_X86_UINT32_OR_AND_LO: u32 = 0xc0010000; +pub const GNU_PROPERTY_X86_UINT32_OR_AND_HI: u32 = 0xc0017fff; + +/// The x86 instruction sets indicated by the corresponding bits are +/// used in program. Their support in the hardware is optional. +pub const GNU_PROPERTY_X86_ISA_1_USED: u32 = 0xc0010002; +/// The x86 instruction sets indicated by the corresponding bits are +/// used in program and they must be supported by the hardware. +pub const GNU_PROPERTY_X86_ISA_1_NEEDED: u32 = 0xc0008002; +/// X86 processor-specific features used in program. +pub const GNU_PROPERTY_X86_FEATURE_1_AND: u32 = 0xc0000002; + +/// GNU_PROPERTY_X86_ISA_1_BASELINE: CMOV, CX8 (cmpxchg8b), FPU (fld), +/// MMX, OSFXSR (fxsave), SCE (syscall), SSE and SSE2. +pub const GNU_PROPERTY_X86_ISA_1_BASELINE: u32 = 1 << 0; +/// GNU_PROPERTY_X86_ISA_1_V2: GNU_PROPERTY_X86_ISA_1_BASELINE, +/// CMPXCHG16B (cmpxchg16b), LAHF-SAHF (lahf), POPCNT (popcnt), SSE3, +/// SSSE3, SSE4.1 and SSE4.2. +pub const GNU_PROPERTY_X86_ISA_1_V2: u32 = 1 << 1; +/// GNU_PROPERTY_X86_ISA_1_V3: GNU_PROPERTY_X86_ISA_1_V2, AVX, AVX2, BMI1, +/// BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE. +pub const GNU_PROPERTY_X86_ISA_1_V3: u32 = 1 << 2; +/// GNU_PROPERTY_X86_ISA_1_V4: GNU_PROPERTY_X86_ISA_1_V3, AVX512F, +/// AVX512BW, AVX512CD, AVX512DQ and AVX512VL. +pub const GNU_PROPERTY_X86_ISA_1_V4: u32 = 1 << 3; + +/// This indicates that all executable sections are compatible with IBT. +pub const GNU_PROPERTY_X86_FEATURE_1_IBT: u32 = 1 << 0; +/// This indicates that all executable sections are compatible with SHSTK. +pub const GNU_PROPERTY_X86_FEATURE_1_SHSTK: u32 = 1 << 1; + +// TODO: Elf*_Move + +/// Header of `SHT_HASH` section. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct HashHeader<E: Endian> { + /// The number of hash buckets. + pub bucket_count: U32<E>, + /// The number of chain values. + pub chain_count: U32<E>, + // Array of hash bucket start indices. + // buckets: U32<E>[bucket_count] + // Array of hash chain links. An index of 0 terminates the chain. + // chains: U32<E>[chain_count] +} + +/// Calculate the SysV hash for a symbol name. +/// +/// Used for `SHT_HASH`. +pub fn hash(name: &[u8]) -> u32 { + let mut hash = 0u32; + for byte in name { + hash = hash.wrapping_mul(16).wrapping_add(u32::from(*byte)); + hash ^= (hash >> 24) & 0xf0; + } + hash & 0xfff_ffff +} + +/// Header of `SHT_GNU_HASH` section. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct GnuHashHeader<E: Endian> { + /// The number of hash buckets. + pub bucket_count: U32<E>, + /// The symbol table index of the first symbol in the hash. + pub symbol_base: U32<E>, + /// The number of words in the bloom filter. + /// + /// Must be a non-zero power of 2. + pub bloom_count: U32<E>, + /// The bit shift count for the bloom filter. + pub bloom_shift: U32<E>, + // Array of bloom filter words. + // bloom_filters: U32<E>[bloom_count] or U64<E>[bloom_count] + // Array of hash bucket start indices. + // buckets: U32<E>[bucket_count] + // Array of hash values, one for each symbol starting at symbol_base. + // values: U32<E>[symbol_count] +} + +/// Calculate the GNU hash for a symbol name. +/// +/// Used for `SHT_GNU_HASH`. +pub fn gnu_hash(name: &[u8]) -> u32 { + let mut hash = 5381u32; + for byte in name { + hash = hash.wrapping_mul(33).wrapping_add(u32::from(*byte)); + } + hash +} + +// Motorola 68k specific definitions. + +// m68k values for `Rel*::r_type`. + +/// No reloc +pub const R_68K_NONE: u32 = 0; +/// Direct 32 bit +pub const R_68K_32: u32 = 1; +/// Direct 16 bit +pub const R_68K_16: u32 = 2; +/// Direct 8 bit +pub const R_68K_8: u32 = 3; +/// PC relative 32 bit +pub const R_68K_PC32: u32 = 4; +/// PC relative 16 bit +pub const R_68K_PC16: u32 = 5; +/// PC relative 8 bit +pub const R_68K_PC8: u32 = 6; +/// 32 bit PC relative GOT entry +pub const R_68K_GOT32: u32 = 7; +/// 16 bit PC relative GOT entry +pub const R_68K_GOT16: u32 = 8; +/// 8 bit PC relative GOT entry +pub const R_68K_GOT8: u32 = 9; +/// 32 bit GOT offset +pub const R_68K_GOT32O: u32 = 10; +/// 16 bit GOT offset +pub const R_68K_GOT16O: u32 = 11; +/// 8 bit GOT offset +pub const R_68K_GOT8O: u32 = 12; +/// 32 bit PC relative PLT address +pub const R_68K_PLT32: u32 = 13; +/// 16 bit PC relative PLT address +pub const R_68K_PLT16: u32 = 14; +/// 8 bit PC relative PLT address +pub const R_68K_PLT8: u32 = 15; +/// 32 bit PLT offset +pub const R_68K_PLT32O: u32 = 16; +/// 16 bit PLT offset +pub const R_68K_PLT16O: u32 = 17; +/// 8 bit PLT offset +pub const R_68K_PLT8O: u32 = 18; +/// Copy symbol at runtime +pub const R_68K_COPY: u32 = 19; +/// Create GOT entry +pub const R_68K_GLOB_DAT: u32 = 20; +/// Create PLT entry +pub const R_68K_JMP_SLOT: u32 = 21; +/// Adjust by program base +pub const R_68K_RELATIVE: u32 = 22; +/// 32 bit GOT offset for GD +pub const R_68K_TLS_GD32: u32 = 25; +/// 16 bit GOT offset for GD +pub const R_68K_TLS_GD16: u32 = 26; +/// 8 bit GOT offset for GD +pub const R_68K_TLS_GD8: u32 = 27; +/// 32 bit GOT offset for LDM +pub const R_68K_TLS_LDM32: u32 = 28; +/// 16 bit GOT offset for LDM +pub const R_68K_TLS_LDM16: u32 = 29; +/// 8 bit GOT offset for LDM +pub const R_68K_TLS_LDM8: u32 = 30; +/// 32 bit module-relative offset +pub const R_68K_TLS_LDO32: u32 = 31; +/// 16 bit module-relative offset +pub const R_68K_TLS_LDO16: u32 = 32; +/// 8 bit module-relative offset +pub const R_68K_TLS_LDO8: u32 = 33; +/// 32 bit GOT offset for IE +pub const R_68K_TLS_IE32: u32 = 34; +/// 16 bit GOT offset for IE +pub const R_68K_TLS_IE16: u32 = 35; +/// 8 bit GOT offset for IE +pub const R_68K_TLS_IE8: u32 = 36; +/// 32 bit offset relative to static TLS block +pub const R_68K_TLS_LE32: u32 = 37; +/// 16 bit offset relative to static TLS block +pub const R_68K_TLS_LE16: u32 = 38; +/// 8 bit offset relative to static TLS block +pub const R_68K_TLS_LE8: u32 = 39; +/// 32 bit module number +pub const R_68K_TLS_DTPMOD32: u32 = 40; +/// 32 bit module-relative offset +pub const R_68K_TLS_DTPREL32: u32 = 41; +/// 32 bit TP-relative offset +pub const R_68K_TLS_TPREL32: u32 = 42; + +// Intel 80386 specific definitions. + +// i386 values for `Rel*::r_type`. + +/// No reloc +pub const R_386_NONE: u32 = 0; +/// Direct 32 bit +pub const R_386_32: u32 = 1; +/// PC relative 32 bit +pub const R_386_PC32: u32 = 2; +/// 32 bit GOT entry +pub const R_386_GOT32: u32 = 3; +/// 32 bit PLT address +pub const R_386_PLT32: u32 = 4; +/// Copy symbol at runtime +pub const R_386_COPY: u32 = 5; +/// Create GOT entry +pub const R_386_GLOB_DAT: u32 = 6; +/// Create PLT entry +pub const R_386_JMP_SLOT: u32 = 7; +/// Adjust by program base +pub const R_386_RELATIVE: u32 = 8; +/// 32 bit offset to GOT +pub const R_386_GOTOFF: u32 = 9; +/// 32 bit PC relative offset to GOT +pub const R_386_GOTPC: u32 = 10; +/// Direct 32 bit PLT address +pub const R_386_32PLT: u32 = 11; +/// Offset in static TLS block +pub const R_386_TLS_TPOFF: u32 = 14; +/// Address of GOT entry for static TLS block offset +pub const R_386_TLS_IE: u32 = 15; +/// GOT entry for static TLS block offset +pub const R_386_TLS_GOTIE: u32 = 16; +/// Offset relative to static TLS block +pub const R_386_TLS_LE: u32 = 17; +/// Direct 32 bit for GNU version of general dynamic thread local data +pub const R_386_TLS_GD: u32 = 18; +/// Direct 32 bit for GNU version of local dynamic thread local data in LE code +pub const R_386_TLS_LDM: u32 = 19; +/// Direct 16 bit +pub const R_386_16: u32 = 20; +/// PC relative 16 bit +pub const R_386_PC16: u32 = 21; +/// Direct 8 bit +pub const R_386_8: u32 = 22; +/// PC relative 8 bit +pub const R_386_PC8: u32 = 23; +/// Direct 32 bit for general dynamic thread local data +pub const R_386_TLS_GD_32: u32 = 24; +/// Tag for pushl in GD TLS code +pub const R_386_TLS_GD_PUSH: u32 = 25; +/// Relocation for call to __tls_get_addr() +pub const R_386_TLS_GD_CALL: u32 = 26; +/// Tag for popl in GD TLS code +pub const R_386_TLS_GD_POP: u32 = 27; +/// Direct 32 bit for local dynamic thread local data in LE code +pub const R_386_TLS_LDM_32: u32 = 28; +/// Tag for pushl in LDM TLS code +pub const R_386_TLS_LDM_PUSH: u32 = 29; +/// Relocation for call to __tls_get_addr() in LDM code +pub const R_386_TLS_LDM_CALL: u32 = 30; +/// Tag for popl in LDM TLS code +pub const R_386_TLS_LDM_POP: u32 = 31; +/// Offset relative to TLS block +pub const R_386_TLS_LDO_32: u32 = 32; +/// GOT entry for negated static TLS block offset +pub const R_386_TLS_IE_32: u32 = 33; +/// Negated offset relative to static TLS block +pub const R_386_TLS_LE_32: u32 = 34; +/// ID of module containing symbol +pub const R_386_TLS_DTPMOD32: u32 = 35; +/// Offset in TLS block +pub const R_386_TLS_DTPOFF32: u32 = 36; +/// Negated offset in static TLS block +pub const R_386_TLS_TPOFF32: u32 = 37; +/// 32-bit symbol size +pub const R_386_SIZE32: u32 = 38; +/// GOT offset for TLS descriptor. +pub const R_386_TLS_GOTDESC: u32 = 39; +/// Marker of call through TLS descriptor for relaxation. +pub const R_386_TLS_DESC_CALL: u32 = 40; +/// TLS descriptor containing pointer to code and to argument, returning the TLS offset for the symbol. +pub const R_386_TLS_DESC: u32 = 41; +/// Adjust indirectly by program base +pub const R_386_IRELATIVE: u32 = 42; +/// Load from 32 bit GOT entry, relaxable. +pub const R_386_GOT32X: u32 = 43; + +// ADI SHARC specific definitions + +// SHARC values for `Rel*::r_type` + +/// 24-bit absolute address in bits 23:0 of a 48-bit instr +/// +/// Targets: +/// +/// * Type 25a (PC_DIRECT) +pub const R_SHARC_ADDR24_V3: u32 = 0x0b; + +/// 32-bit absolute address in bits 31:0 of a 48-bit instr +/// +/// Targets: +/// +/// * Type 14a +/// * Type 14d +/// * Type 15a +/// * Type 16a +/// * Type 17a +/// * Type 18a +/// * Type 19a +pub const R_SHARC_ADDR32_V3: u32 = 0x0c; + +/// 32-bit absolute address in bits 31:0 of a 32-bit data location +/// +/// Represented with `RelocationEncoding::Generic` +pub const R_SHARC_ADDR_VAR_V3: u32 = 0x0d; + +/// 6-bit PC-relative address in bits 32:27 of a 48-bit instr +/// +/// Targets: +/// +/// * Type 9a +/// * Type 10a +pub const R_SHARC_PCRSHORT_V3: u32 = 0x0e; + +/// 24-bit PC-relative address in bits 23:0 of a 48-bit instr +/// +/// Targets: +/// +/// * Type 8a +/// * Type 12a (truncated to 23 bits after relocation) +/// * Type 13a (truncated to 23 bits after relocation) +/// * Type 25a (PC Relative) +pub const R_SHARC_PCRLONG_V3: u32 = 0x0f; + +/// 6-bit absolute address in bits 32:27 of a 48-bit instr +/// +/// Targets: +/// +/// * Type 4a +/// * Type 4b +/// * Type 4d +pub const R_SHARC_DATA6_V3: u32 = 0x10; + +/// 16-bit absolute address in bits 39:24 of a 48-bit instr +/// +/// Targets: +/// +/// * Type 12a +pub const R_SHARC_DATA16_V3: u32 = 0x11; + +/// 6-bit absolute address into bits 16:11 of a 32-bit instr +/// +/// Targets: +/// +/// * Type 4b +pub const R_SHARC_DATA6_VISA_V3: u32 = 0x12; + +/// 7-bit absolute address into bits 6:0 of a 32-bit instr +pub const R_SHARC_DATA7_VISA_V3: u32 = 0x13; + +/// 16-bit absolute address into bits 15:0 of a 32-bit instr +pub const R_SHARC_DATA16_VISA_V3: u32 = 0x14; + +/// 6-bit PC-relative address into bits 16:11 of a Type B +/// +/// Targets: +/// +/// * Type 9b +pub const R_SHARC_PCR6_VISA_V3: u32 = 0x17; + +/// 16-bit absolute address into bits 15:0 of a 16-bit location. +/// +/// Represented with `RelocationEncoding::Generic` +pub const R_SHARC_ADDR_VAR16_V3: u32 = 0x19; + +pub const R_SHARC_CALC_PUSH_ADDR: u32 = 0xe0; +pub const R_SHARC_CALC_PUSH_ADDEND: u32 = 0xe1; +pub const R_SHARC_CALC_ADD: u32 = 0xe2; +pub const R_SHARC_CALC_SUB: u32 = 0xe3; +pub const R_SHARC_CALC_MUL: u32 = 0xe4; +pub const R_SHARC_CALC_DIV: u32 = 0xe5; +pub const R_SHARC_CALC_MOD: u32 = 0xe6; +pub const R_SHARC_CALC_LSHIFT: u32 = 0xe7; +pub const R_SHARC_CALC_RSHIFT: u32 = 0xe8; +pub const R_SHARC_CALC_AND: u32 = 0xe9; +pub const R_SHARC_CALC_OR: u32 = 0xea; +pub const R_SHARC_CALC_XOR: u32 = 0xeb; +pub const R_SHARC_CALC_PUSH_LEN: u32 = 0xec; +pub const R_SHARC_CALC_NOT: u32 = 0xf6; + +// SHARC values for `SectionHeader*::sh_type`. + +/// .adi.attributes +pub const SHT_SHARC_ADI_ATTRIBUTES: u32 = SHT_LOPROC + 0x2; + +// SUN SPARC specific definitions. + +// SPARC values for `st_type` component of `Sym*::st_info`. + +/// Global register reserved to app. +pub const STT_SPARC_REGISTER: u8 = 13; + +// SPARC values for `FileHeader64::e_flags`. + +pub const EF_SPARCV9_MM: u32 = 3; +pub const EF_SPARCV9_TSO: u32 = 0; +pub const EF_SPARCV9_PSO: u32 = 1; +pub const EF_SPARCV9_RMO: u32 = 2; +/// little endian data +pub const EF_SPARC_LEDATA: u32 = 0x80_0000; +pub const EF_SPARC_EXT_MASK: u32 = 0xFF_FF00; +/// generic V8+ features +pub const EF_SPARC_32PLUS: u32 = 0x00_0100; +/// Sun UltraSPARC1 extensions +pub const EF_SPARC_SUN_US1: u32 = 0x00_0200; +/// HAL R1 extensions +pub const EF_SPARC_HAL_R1: u32 = 0x00_0400; +/// Sun UltraSPARCIII extensions +pub const EF_SPARC_SUN_US3: u32 = 0x00_0800; + +// SPARC values for `Rel*::r_type`. + +/// No reloc +pub const R_SPARC_NONE: u32 = 0; +/// Direct 8 bit +pub const R_SPARC_8: u32 = 1; +/// Direct 16 bit +pub const R_SPARC_16: u32 = 2; +/// Direct 32 bit +pub const R_SPARC_32: u32 = 3; +/// PC relative 8 bit +pub const R_SPARC_DISP8: u32 = 4; +/// PC relative 16 bit +pub const R_SPARC_DISP16: u32 = 5; +/// PC relative 32 bit +pub const R_SPARC_DISP32: u32 = 6; +/// PC relative 30 bit shifted +pub const R_SPARC_WDISP30: u32 = 7; +/// PC relative 22 bit shifted +pub const R_SPARC_WDISP22: u32 = 8; +/// High 22 bit +pub const R_SPARC_HI22: u32 = 9; +/// Direct 22 bit +pub const R_SPARC_22: u32 = 10; +/// Direct 13 bit +pub const R_SPARC_13: u32 = 11; +/// Truncated 10 bit +pub const R_SPARC_LO10: u32 = 12; +/// Truncated 10 bit GOT entry +pub const R_SPARC_GOT10: u32 = 13; +/// 13 bit GOT entry +pub const R_SPARC_GOT13: u32 = 14; +/// 22 bit GOT entry shifted +pub const R_SPARC_GOT22: u32 = 15; +/// PC relative 10 bit truncated +pub const R_SPARC_PC10: u32 = 16; +/// PC relative 22 bit shifted +pub const R_SPARC_PC22: u32 = 17; +/// 30 bit PC relative PLT address +pub const R_SPARC_WPLT30: u32 = 18; +/// Copy symbol at runtime +pub const R_SPARC_COPY: u32 = 19; +/// Create GOT entry +pub const R_SPARC_GLOB_DAT: u32 = 20; +/// Create PLT entry +pub const R_SPARC_JMP_SLOT: u32 = 21; +/// Adjust by program base +pub const R_SPARC_RELATIVE: u32 = 22; +/// Direct 32 bit unaligned +pub const R_SPARC_UA32: u32 = 23; + +// Sparc64 values for `Rel*::r_type`. + +/// Direct 32 bit ref to PLT entry +pub const R_SPARC_PLT32: u32 = 24; +/// High 22 bit PLT entry +pub const R_SPARC_HIPLT22: u32 = 25; +/// Truncated 10 bit PLT entry +pub const R_SPARC_LOPLT10: u32 = 26; +/// PC rel 32 bit ref to PLT entry +pub const R_SPARC_PCPLT32: u32 = 27; +/// PC rel high 22 bit PLT entry +pub const R_SPARC_PCPLT22: u32 = 28; +/// PC rel trunc 10 bit PLT entry +pub const R_SPARC_PCPLT10: u32 = 29; +/// Direct 10 bit +pub const R_SPARC_10: u32 = 30; +/// Direct 11 bit +pub const R_SPARC_11: u32 = 31; +/// Direct 64 bit +pub const R_SPARC_64: u32 = 32; +/// 10bit with secondary 13bit addend +pub const R_SPARC_OLO10: u32 = 33; +/// Top 22 bits of direct 64 bit +pub const R_SPARC_HH22: u32 = 34; +/// High middle 10 bits of ... +pub const R_SPARC_HM10: u32 = 35; +/// Low middle 22 bits of ... +pub const R_SPARC_LM22: u32 = 36; +/// Top 22 bits of pc rel 64 bit +pub const R_SPARC_PC_HH22: u32 = 37; +/// High middle 10 bit of ... +pub const R_SPARC_PC_HM10: u32 = 38; +/// Low miggle 22 bits of ... +pub const R_SPARC_PC_LM22: u32 = 39; +/// PC relative 16 bit shifted +pub const R_SPARC_WDISP16: u32 = 40; +/// PC relative 19 bit shifted +pub const R_SPARC_WDISP19: u32 = 41; +/// was part of v9 ABI but was removed +pub const R_SPARC_GLOB_JMP: u32 = 42; +/// Direct 7 bit +pub const R_SPARC_7: u32 = 43; +/// Direct 5 bit +pub const R_SPARC_5: u32 = 44; +/// Direct 6 bit +pub const R_SPARC_6: u32 = 45; +/// PC relative 64 bit +pub const R_SPARC_DISP64: u32 = 46; +/// Direct 64 bit ref to PLT entry +pub const R_SPARC_PLT64: u32 = 47; +/// High 22 bit complemented +pub const R_SPARC_HIX22: u32 = 48; +/// Truncated 11 bit complemented +pub const R_SPARC_LOX10: u32 = 49; +/// Direct high 12 of 44 bit +pub const R_SPARC_H44: u32 = 50; +/// Direct mid 22 of 44 bit +pub const R_SPARC_M44: u32 = 51; +/// Direct low 10 of 44 bit +pub const R_SPARC_L44: u32 = 52; +/// Global register usage +pub const R_SPARC_REGISTER: u32 = 53; +/// Direct 64 bit unaligned +pub const R_SPARC_UA64: u32 = 54; +/// Direct 16 bit unaligned +pub const R_SPARC_UA16: u32 = 55; +pub const R_SPARC_TLS_GD_HI22: u32 = 56; +pub const R_SPARC_TLS_GD_LO10: u32 = 57; +pub const R_SPARC_TLS_GD_ADD: u32 = 58; +pub const R_SPARC_TLS_GD_CALL: u32 = 59; +pub const R_SPARC_TLS_LDM_HI22: u32 = 60; +pub const R_SPARC_TLS_LDM_LO10: u32 = 61; +pub const R_SPARC_TLS_LDM_ADD: u32 = 62; +pub const R_SPARC_TLS_LDM_CALL: u32 = 63; +pub const R_SPARC_TLS_LDO_HIX22: u32 = 64; +pub const R_SPARC_TLS_LDO_LOX10: u32 = 65; +pub const R_SPARC_TLS_LDO_ADD: u32 = 66; +pub const R_SPARC_TLS_IE_HI22: u32 = 67; +pub const R_SPARC_TLS_IE_LO10: u32 = 68; +pub const R_SPARC_TLS_IE_LD: u32 = 69; +pub const R_SPARC_TLS_IE_LDX: u32 = 70; +pub const R_SPARC_TLS_IE_ADD: u32 = 71; +pub const R_SPARC_TLS_LE_HIX22: u32 = 72; +pub const R_SPARC_TLS_LE_LOX10: u32 = 73; +pub const R_SPARC_TLS_DTPMOD32: u32 = 74; +pub const R_SPARC_TLS_DTPMOD64: u32 = 75; +pub const R_SPARC_TLS_DTPOFF32: u32 = 76; +pub const R_SPARC_TLS_DTPOFF64: u32 = 77; +pub const R_SPARC_TLS_TPOFF32: u32 = 78; +pub const R_SPARC_TLS_TPOFF64: u32 = 79; +pub const R_SPARC_GOTDATA_HIX22: u32 = 80; +pub const R_SPARC_GOTDATA_LOX10: u32 = 81; +pub const R_SPARC_GOTDATA_OP_HIX22: u32 = 82; +pub const R_SPARC_GOTDATA_OP_LOX10: u32 = 83; +pub const R_SPARC_GOTDATA_OP: u32 = 84; +pub const R_SPARC_H34: u32 = 85; +pub const R_SPARC_SIZE32: u32 = 86; +pub const R_SPARC_SIZE64: u32 = 87; +pub const R_SPARC_WDISP10: u32 = 88; +pub const R_SPARC_JMP_IREL: u32 = 248; +pub const R_SPARC_IRELATIVE: u32 = 249; +pub const R_SPARC_GNU_VTINHERIT: u32 = 250; +pub const R_SPARC_GNU_VTENTRY: u32 = 251; +pub const R_SPARC_REV32: u32 = 252; + +// Sparc64 values for `Dyn32::d_tag`. + +pub const DT_SPARC_REGISTER: u32 = 0x7000_0001; + +// MIPS R3000 specific definitions. + +// MIPS values for `FileHeader32::e_flags`. + +/// A .noreorder directive was used. +pub const EF_MIPS_NOREORDER: u32 = 1; +/// Contains PIC code. +pub const EF_MIPS_PIC: u32 = 2; +/// Uses PIC calling sequence. +pub const EF_MIPS_CPIC: u32 = 4; +pub const EF_MIPS_XGOT: u32 = 8; +pub const EF_MIPS_64BIT_WHIRL: u32 = 16; +pub const EF_MIPS_ABI2: u32 = 32; +pub const EF_MIPS_ABI_ON32: u32 = 64; +/// Uses FP64 (12 callee-saved). +pub const EF_MIPS_FP64: u32 = 512; +/// Uses IEEE 754-2008 NaN encoding. +pub const EF_MIPS_NAN2008: u32 = 1024; +/// MIPS architecture level. +pub const EF_MIPS_ARCH: u32 = 0xf000_0000; + +/// The first MIPS 32 bit ABI +pub const EF_MIPS_ABI_O32: u32 = 0x0000_1000; +/// O32 ABI extended for 64-bit architectures +pub const EF_MIPS_ABI_O64: u32 = 0x0000_2000; +/// EABI in 32-bit mode +pub const EF_MIPS_ABI_EABI32: u32 = 0x0000_3000; +/// EABI in 64-bit mode +pub const EF_MIPS_ABI_EABI64: u32 = 0x0000_4000; +/// Mask for selecting EF_MIPS_ABI_ variant +pub const EF_MIPS_ABI: u32 = 0x0000_f000; + +// Legal values for MIPS architecture level. + +/// -mips1 code. +pub const EF_MIPS_ARCH_1: u32 = 0x0000_0000; +/// -mips2 code. +pub const EF_MIPS_ARCH_2: u32 = 0x1000_0000; +/// -mips3 code. +pub const EF_MIPS_ARCH_3: u32 = 0x2000_0000; +/// -mips4 code. +pub const EF_MIPS_ARCH_4: u32 = 0x3000_0000; +/// -mips5 code. +pub const EF_MIPS_ARCH_5: u32 = 0x4000_0000; +/// MIPS32 code. +pub const EF_MIPS_ARCH_32: u32 = 0x5000_0000; +/// MIPS64 code. +pub const EF_MIPS_ARCH_64: u32 = 0x6000_0000; +/// MIPS32r2 code. +pub const EF_MIPS_ARCH_32R2: u32 = 0x7000_0000; +/// MIPS64r2 code. +pub const EF_MIPS_ARCH_64R2: u32 = 0x8000_0000; +/// MIPS32r6 code +pub const EF_MIPS_ARCH_32R6: u32 = 0x9000_0000; +/// MIPS64r6 code +pub const EF_MIPS_ARCH_64R6: u32 = 0xa000_0000; + +// MIPS values for `Sym32::st_shndx`. + +/// Allocated common symbols. +pub const SHN_MIPS_ACOMMON: u16 = 0xff00; +/// Allocated test symbols. +pub const SHN_MIPS_TEXT: u16 = 0xff01; +/// Allocated data symbols. +pub const SHN_MIPS_DATA: u16 = 0xff02; +/// Small common symbols. +pub const SHN_MIPS_SCOMMON: u16 = 0xff03; +/// Small undefined symbols. +pub const SHN_MIPS_SUNDEFINED: u16 = 0xff04; + +// MIPS values for `SectionHeader32::sh_type`. + +/// Shared objects used in link. +pub const SHT_MIPS_LIBLIST: u32 = 0x7000_0000; +pub const SHT_MIPS_MSYM: u32 = 0x7000_0001; +/// Conflicting symbols. +pub const SHT_MIPS_CONFLICT: u32 = 0x7000_0002; +/// Global data area sizes. +pub const SHT_MIPS_GPTAB: u32 = 0x7000_0003; +/// Reserved for SGI/MIPS compilers +pub const SHT_MIPS_UCODE: u32 = 0x7000_0004; +/// MIPS ECOFF debugging info. +pub const SHT_MIPS_DEBUG: u32 = 0x7000_0005; +/// Register usage information. +pub const SHT_MIPS_REGINFO: u32 = 0x7000_0006; +pub const SHT_MIPS_PACKAGE: u32 = 0x7000_0007; +pub const SHT_MIPS_PACKSYM: u32 = 0x7000_0008; +pub const SHT_MIPS_RELD: u32 = 0x7000_0009; +pub const SHT_MIPS_IFACE: u32 = 0x7000_000b; +pub const SHT_MIPS_CONTENT: u32 = 0x7000_000c; +/// Miscellaneous options. +pub const SHT_MIPS_OPTIONS: u32 = 0x7000_000d; +pub const SHT_MIPS_SHDR: u32 = 0x7000_0010; +pub const SHT_MIPS_FDESC: u32 = 0x7000_0011; +pub const SHT_MIPS_EXTSYM: u32 = 0x7000_0012; +pub const SHT_MIPS_DENSE: u32 = 0x7000_0013; +pub const SHT_MIPS_PDESC: u32 = 0x7000_0014; +pub const SHT_MIPS_LOCSYM: u32 = 0x7000_0015; +pub const SHT_MIPS_AUXSYM: u32 = 0x7000_0016; +pub const SHT_MIPS_OPTSYM: u32 = 0x7000_0017; +pub const SHT_MIPS_LOCSTR: u32 = 0x7000_0018; +pub const SHT_MIPS_LINE: u32 = 0x7000_0019; +pub const SHT_MIPS_RFDESC: u32 = 0x7000_001a; +pub const SHT_MIPS_DELTASYM: u32 = 0x7000_001b; +pub const SHT_MIPS_DELTAINST: u32 = 0x7000_001c; +pub const SHT_MIPS_DELTACLASS: u32 = 0x7000_001d; +/// DWARF debugging information. +pub const SHT_MIPS_DWARF: u32 = 0x7000_001e; +pub const SHT_MIPS_DELTADECL: u32 = 0x7000_001f; +pub const SHT_MIPS_SYMBOL_LIB: u32 = 0x7000_0020; +/// Event section. +pub const SHT_MIPS_EVENTS: u32 = 0x7000_0021; +pub const SHT_MIPS_TRANSLATE: u32 = 0x7000_0022; +pub const SHT_MIPS_PIXIE: u32 = 0x7000_0023; +pub const SHT_MIPS_XLATE: u32 = 0x7000_0024; +pub const SHT_MIPS_XLATE_DEBUG: u32 = 0x7000_0025; +pub const SHT_MIPS_WHIRL: u32 = 0x7000_0026; +pub const SHT_MIPS_EH_REGION: u32 = 0x7000_0027; +pub const SHT_MIPS_XLATE_OLD: u32 = 0x7000_0028; +pub const SHT_MIPS_PDR_EXCEPTION: u32 = 0x7000_0029; + +// MIPS values for `SectionHeader32::sh_flags`. + +/// Must be in global data area. +pub const SHF_MIPS_GPREL: u32 = 0x1000_0000; +pub const SHF_MIPS_MERGE: u32 = 0x2000_0000; +pub const SHF_MIPS_ADDR: u32 = 0x4000_0000; +pub const SHF_MIPS_STRINGS: u32 = 0x8000_0000; +pub const SHF_MIPS_NOSTRIP: u32 = 0x0800_0000; +pub const SHF_MIPS_LOCAL: u32 = 0x0400_0000; +pub const SHF_MIPS_NAMES: u32 = 0x0200_0000; +pub const SHF_MIPS_NODUPE: u32 = 0x0100_0000; + +// MIPS values for `Sym32::st_other`. + +pub const STO_MIPS_PLT: u8 = 0x8; +/// Only valid for `STB_MIPS_SPLIT_COMMON`. +pub const STO_MIPS_SC_ALIGN_UNUSED: u8 = 0xff; + +// MIPS values for `Sym32::st_info'. +pub const STB_MIPS_SPLIT_COMMON: u8 = 13; + +// Entries found in sections of type `SHT_MIPS_GPTAB`. + +// TODO: Elf32_gptab, Elf32_RegInfo, Elf_Options + +// Values for `Elf_Options::kind`. + +/// Undefined. +pub const ODK_NULL: u32 = 0; +/// Register usage information. +pub const ODK_REGINFO: u32 = 1; +/// Exception processing options. +pub const ODK_EXCEPTIONS: u32 = 2; +/// Section padding options. +pub const ODK_PAD: u32 = 3; +/// Hardware workarounds performed +pub const ODK_HWPATCH: u32 = 4; +/// record the fill value used by the linker. +pub const ODK_FILL: u32 = 5; +/// reserve space for desktop tools to write. +pub const ODK_TAGS: u32 = 6; +/// HW workarounds. 'AND' bits when merging. +pub const ODK_HWAND: u32 = 7; +/// HW workarounds. 'OR' bits when merging. +pub const ODK_HWOR: u32 = 8; + +// Values for `Elf_Options::info` for `ODK_EXCEPTIONS` entries. + +/// FPE's which MUST be enabled. +pub const OEX_FPU_MIN: u32 = 0x1f; +/// FPE's which MAY be enabled. +pub const OEX_FPU_MAX: u32 = 0x1f00; +/// page zero must be mapped. +pub const OEX_PAGE0: u32 = 0x10000; +/// Force sequential memory mode? +pub const OEX_SMM: u32 = 0x20000; +/// Force floating point debug mode? +pub const OEX_FPDBUG: u32 = 0x40000; +pub const OEX_PRECISEFP: u32 = OEX_FPDBUG; +/// Dismiss invalid address faults? +pub const OEX_DISMISS: u32 = 0x80000; + +pub const OEX_FPU_INVAL: u32 = 0x10; +pub const OEX_FPU_DIV0: u32 = 0x08; +pub const OEX_FPU_OFLO: u32 = 0x04; +pub const OEX_FPU_UFLO: u32 = 0x02; +pub const OEX_FPU_INEX: u32 = 0x01; + +// Masks for `Elf_Options::info` for an `ODK_HWPATCH` entry. */ +/// R4000 end-of-page patch. +pub const OHW_R4KEOP: u32 = 0x1; +/// may need R8000 prefetch patch. +pub const OHW_R8KPFETCH: u32 = 0x2; +/// R5000 end-of-page patch. +pub const OHW_R5KEOP: u32 = 0x4; +/// R5000 cvt.\[ds\].l bug. clean=1. +pub const OHW_R5KCVTL: u32 = 0x8; + +pub const OPAD_PREFIX: u32 = 0x1; +pub const OPAD_POSTFIX: u32 = 0x2; +pub const OPAD_SYMBOL: u32 = 0x4; + +// Entries found in sections of type `SHT_MIPS_OPTIONS`. + +// TODO: Elf_Options_Hw + +// Masks for `ElfOptions::info` for `ODK_HWAND` and `ODK_HWOR` entries. + +pub const OHWA0_R4KEOP_CHECKED: u32 = 0x0000_0001; +pub const OHWA1_R4KEOP_CLEAN: u32 = 0x0000_0002; + +// MIPS values for `Rel*::r_type`. + +/// No reloc +pub const R_MIPS_NONE: u32 = 0; +/// Direct 16 bit +pub const R_MIPS_16: u32 = 1; +/// Direct 32 bit +pub const R_MIPS_32: u32 = 2; +/// PC relative 32 bit +pub const R_MIPS_REL32: u32 = 3; +/// Direct 26 bit shifted +pub const R_MIPS_26: u32 = 4; +/// High 16 bit +pub const R_MIPS_HI16: u32 = 5; +/// Low 16 bit +pub const R_MIPS_LO16: u32 = 6; +/// GP relative 16 bit +pub const R_MIPS_GPREL16: u32 = 7; +/// 16 bit literal entry +pub const R_MIPS_LITERAL: u32 = 8; +/// 16 bit GOT entry +pub const R_MIPS_GOT16: u32 = 9; +/// PC relative 16 bit +pub const R_MIPS_PC16: u32 = 10; +/// 16 bit GOT entry for function +pub const R_MIPS_CALL16: u32 = 11; +/// GP relative 32 bit +pub const R_MIPS_GPREL32: u32 = 12; + +pub const R_MIPS_SHIFT5: u32 = 16; +pub const R_MIPS_SHIFT6: u32 = 17; +pub const R_MIPS_64: u32 = 18; +pub const R_MIPS_GOT_DISP: u32 = 19; +pub const R_MIPS_GOT_PAGE: u32 = 20; +pub const R_MIPS_GOT_OFST: u32 = 21; +pub const R_MIPS_GOT_HI16: u32 = 22; +pub const R_MIPS_GOT_LO16: u32 = 23; +pub const R_MIPS_SUB: u32 = 24; +pub const R_MIPS_INSERT_A: u32 = 25; +pub const R_MIPS_INSERT_B: u32 = 26; +pub const R_MIPS_DELETE: u32 = 27; +pub const R_MIPS_HIGHER: u32 = 28; +pub const R_MIPS_HIGHEST: u32 = 29; +pub const R_MIPS_CALL_HI16: u32 = 30; +pub const R_MIPS_CALL_LO16: u32 = 31; +pub const R_MIPS_SCN_DISP: u32 = 32; +pub const R_MIPS_REL16: u32 = 33; +pub const R_MIPS_ADD_IMMEDIATE: u32 = 34; +pub const R_MIPS_PJUMP: u32 = 35; +pub const R_MIPS_RELGOT: u32 = 36; +pub const R_MIPS_JALR: u32 = 37; +/// Module number 32 bit +pub const R_MIPS_TLS_DTPMOD32: u32 = 38; +/// Module-relative offset 32 bit +pub const R_MIPS_TLS_DTPREL32: u32 = 39; +/// Module number 64 bit +pub const R_MIPS_TLS_DTPMOD64: u32 = 40; +/// Module-relative offset 64 bit +pub const R_MIPS_TLS_DTPREL64: u32 = 41; +/// 16 bit GOT offset for GD +pub const R_MIPS_TLS_GD: u32 = 42; +/// 16 bit GOT offset for LDM +pub const R_MIPS_TLS_LDM: u32 = 43; +/// Module-relative offset, high 16 bits +pub const R_MIPS_TLS_DTPREL_HI16: u32 = 44; +/// Module-relative offset, low 16 bits +pub const R_MIPS_TLS_DTPREL_LO16: u32 = 45; +/// 16 bit GOT offset for IE +pub const R_MIPS_TLS_GOTTPREL: u32 = 46; +/// TP-relative offset, 32 bit +pub const R_MIPS_TLS_TPREL32: u32 = 47; +/// TP-relative offset, 64 bit +pub const R_MIPS_TLS_TPREL64: u32 = 48; +/// TP-relative offset, high 16 bits +pub const R_MIPS_TLS_TPREL_HI16: u32 = 49; +/// TP-relative offset, low 16 bits +pub const R_MIPS_TLS_TPREL_LO16: u32 = 50; +pub const R_MIPS_GLOB_DAT: u32 = 51; +pub const R_MIPS_COPY: u32 = 126; +pub const R_MIPS_JUMP_SLOT: u32 = 127; + +// MIPS values for `ProgramHeader32::p_type`. + +/// Register usage information. +pub const PT_MIPS_REGINFO: u32 = 0x7000_0000; +/// Runtime procedure table. +pub const PT_MIPS_RTPROC: u32 = 0x7000_0001; +pub const PT_MIPS_OPTIONS: u32 = 0x7000_0002; +/// FP mode requirement. +pub const PT_MIPS_ABIFLAGS: u32 = 0x7000_0003; + +// MIPS values for `ProgramHeader32::p_flags`. + +pub const PF_MIPS_LOCAL: u32 = 0x1000_0000; + +// MIPS values for `Dyn32::d_tag`. + +/// Runtime linker interface version +pub const DT_MIPS_RLD_VERSION: u32 = 0x7000_0001; +/// Timestamp +pub const DT_MIPS_TIME_STAMP: u32 = 0x7000_0002; +/// Checksum +pub const DT_MIPS_ICHECKSUM: u32 = 0x7000_0003; +/// Version string (string tbl index) +pub const DT_MIPS_IVERSION: u32 = 0x7000_0004; +/// Flags +pub const DT_MIPS_FLAGS: u32 = 0x7000_0005; +/// Base address +pub const DT_MIPS_BASE_ADDRESS: u32 = 0x7000_0006; +pub const DT_MIPS_MSYM: u32 = 0x7000_0007; +/// Address of CONFLICT section +pub const DT_MIPS_CONFLICT: u32 = 0x7000_0008; +/// Address of LIBLIST section +pub const DT_MIPS_LIBLIST: u32 = 0x7000_0009; +/// Number of local GOT entries +pub const DT_MIPS_LOCAL_GOTNO: u32 = 0x7000_000a; +/// Number of CONFLICT entries +pub const DT_MIPS_CONFLICTNO: u32 = 0x7000_000b; +/// Number of LIBLIST entries +pub const DT_MIPS_LIBLISTNO: u32 = 0x7000_0010; +/// Number of DYNSYM entries +pub const DT_MIPS_SYMTABNO: u32 = 0x7000_0011; +/// First external DYNSYM +pub const DT_MIPS_UNREFEXTNO: u32 = 0x7000_0012; +/// First GOT entry in DYNSYM +pub const DT_MIPS_GOTSYM: u32 = 0x7000_0013; +/// Number of GOT page table entries +pub const DT_MIPS_HIPAGENO: u32 = 0x7000_0014; +/// Address of run time loader map. +pub const DT_MIPS_RLD_MAP: u32 = 0x7000_0016; +/// Delta C++ class definition. +pub const DT_MIPS_DELTA_CLASS: u32 = 0x7000_0017; +/// Number of entries in DT_MIPS_DELTA_CLASS. +pub const DT_MIPS_DELTA_CLASS_NO: u32 = 0x7000_0018; +/// Delta C++ class instances. +pub const DT_MIPS_DELTA_INSTANCE: u32 = 0x7000_0019; +/// Number of entries in DT_MIPS_DELTA_INSTANCE. +pub const DT_MIPS_DELTA_INSTANCE_NO: u32 = 0x7000_001a; +/// Delta relocations. +pub const DT_MIPS_DELTA_RELOC: u32 = 0x7000_001b; +/// Number of entries in DT_MIPS_DELTA_RELOC. +pub const DT_MIPS_DELTA_RELOC_NO: u32 = 0x7000_001c; +/// Delta symbols that Delta relocations refer to. +pub const DT_MIPS_DELTA_SYM: u32 = 0x7000_001d; +/// Number of entries in DT_MIPS_DELTA_SYM. +pub const DT_MIPS_DELTA_SYM_NO: u32 = 0x7000_001e; +/// Delta symbols that hold the class declaration. +pub const DT_MIPS_DELTA_CLASSSYM: u32 = 0x7000_0020; +/// Number of entries in DT_MIPS_DELTA_CLASSSYM. +pub const DT_MIPS_DELTA_CLASSSYM_NO: u32 = 0x7000_0021; +/// Flags indicating for C++ flavor. +pub const DT_MIPS_CXX_FLAGS: u32 = 0x7000_0022; +pub const DT_MIPS_PIXIE_INIT: u32 = 0x7000_0023; +pub const DT_MIPS_SYMBOL_LIB: u32 = 0x7000_0024; +pub const DT_MIPS_LOCALPAGE_GOTIDX: u32 = 0x7000_0025; +pub const DT_MIPS_LOCAL_GOTIDX: u32 = 0x7000_0026; +pub const DT_MIPS_HIDDEN_GOTIDX: u32 = 0x7000_0027; +pub const DT_MIPS_PROTECTED_GOTIDX: u32 = 0x7000_0028; +/// Address of .options. +pub const DT_MIPS_OPTIONS: u32 = 0x7000_0029; +/// Address of .interface. +pub const DT_MIPS_INTERFACE: u32 = 0x7000_002a; +pub const DT_MIPS_DYNSTR_ALIGN: u32 = 0x7000_002b; +/// Size of the .interface section. +pub const DT_MIPS_INTERFACE_SIZE: u32 = 0x7000_002c; +/// Address of rld_text_rsolve function stored in GOT. +pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR: u32 = 0x7000_002d; +/// Default suffix of dso to be added by rld on dlopen() calls. +pub const DT_MIPS_PERF_SUFFIX: u32 = 0x7000_002e; +/// (O32)Size of compact rel section. +pub const DT_MIPS_COMPACT_SIZE: u32 = 0x7000_002f; +/// GP value for aux GOTs. +pub const DT_MIPS_GP_VALUE: u32 = 0x7000_0030; +/// Address of aux .dynamic. +pub const DT_MIPS_AUX_DYNAMIC: u32 = 0x7000_0031; +/// The address of .got.plt in an executable using the new non-PIC ABI. +pub const DT_MIPS_PLTGOT: u32 = 0x7000_0032; +/// The base of the PLT in an executable using the new non-PIC ABI if that PLT is writable. For a non-writable PLT, this is omitted or has a zero value. +pub const DT_MIPS_RWPLT: u32 = 0x7000_0034; +/// An alternative description of the classic MIPS RLD_MAP that is usable in a PIE as it stores a relative offset from the address of the tag rather than an absolute address. +pub const DT_MIPS_RLD_MAP_REL: u32 = 0x7000_0035; + +// Values for `DT_MIPS_FLAGS` `Dyn32` entry. + +/// No flags +pub const RHF_NONE: u32 = 0; +/// Use quickstart +pub const RHF_QUICKSTART: u32 = 1 << 0; +/// Hash size not power of 2 +pub const RHF_NOTPOT: u32 = 1 << 1; +/// Ignore LD_LIBRARY_PATH +pub const RHF_NO_LIBRARY_REPLACEMENT: u32 = 1 << 2; +pub const RHF_NO_MOVE: u32 = 1 << 3; +pub const RHF_SGI_ONLY: u32 = 1 << 4; +pub const RHF_GUARANTEE_INIT: u32 = 1 << 5; +pub const RHF_DELTA_C_PLUS_PLUS: u32 = 1 << 6; +pub const RHF_GUARANTEE_START_INIT: u32 = 1 << 7; +pub const RHF_PIXIE: u32 = 1 << 8; +pub const RHF_DEFAULT_DELAY_LOAD: u32 = 1 << 9; +pub const RHF_REQUICKSTART: u32 = 1 << 10; +pub const RHF_REQUICKSTARTED: u32 = 1 << 11; +pub const RHF_CORD: u32 = 1 << 12; +pub const RHF_NO_UNRES_UNDEF: u32 = 1 << 13; +pub const RHF_RLD_ORDER_SAFE: u32 = 1 << 14; + +// Entries found in sections of type `SHT_MIPS_LIBLIST`. + +// TODO: Elf32_Lib, Elf64_Lib + +// Values for `Lib*::l_flags`. + +pub const LL_NONE: u32 = 0; +/// Require exact match +pub const LL_EXACT_MATCH: u32 = 1 << 0; +/// Ignore interface version +pub const LL_IGNORE_INT_VER: u32 = 1 << 1; +pub const LL_REQUIRE_MINOR: u32 = 1 << 2; +pub const LL_EXPORTS: u32 = 1 << 3; +pub const LL_DELAY_LOAD: u32 = 1 << 4; +pub const LL_DELTA: u32 = 1 << 5; + +// TODO: MIPS ABI flags + +// PA-RISC specific definitions. + +// PA-RISC values for `FileHeader32::e_flags`. + +/// Trap nil pointer dereference. +pub const EF_PARISC_TRAPNIL: u32 = 0x0001_0000; +/// Program uses arch. extensions. +pub const EF_PARISC_EXT: u32 = 0x0002_0000; +/// Program expects little endian. +pub const EF_PARISC_LSB: u32 = 0x0004_0000; +/// Program expects wide mode. +pub const EF_PARISC_WIDE: u32 = 0x0008_0000; +/// No kernel assisted branch prediction. +pub const EF_PARISC_NO_KABP: u32 = 0x0010_0000; +/// Allow lazy swapping. +pub const EF_PARISC_LAZYSWAP: u32 = 0x0040_0000; +/// Architecture version. +pub const EF_PARISC_ARCH: u32 = 0x0000_ffff; + +// Values for `EF_PARISC_ARCH'. + +/// PA-RISC 1.0 big-endian. +pub const EFA_PARISC_1_0: u32 = 0x020b; +/// PA-RISC 1.1 big-endian. +pub const EFA_PARISC_1_1: u32 = 0x0210; +/// PA-RISC 2.0 big-endian. +pub const EFA_PARISC_2_0: u32 = 0x0214; + +// PA-RISC values for `Sym*::st_shndx`. + +/// Section for tentatively declared symbols in ANSI C. +pub const SHN_PARISC_ANSI_COMMON: u16 = 0xff00; +/// Common blocks in huge model. +pub const SHN_PARISC_HUGE_COMMON: u16 = 0xff01; + +// PA-RISC values for `SectionHeader32::sh_type`. + +/// Contains product specific ext. +pub const SHT_PARISC_EXT: u32 = 0x7000_0000; +/// Unwind information. +pub const SHT_PARISC_UNWIND: u32 = 0x7000_0001; +/// Debug info for optimized code. +pub const SHT_PARISC_DOC: u32 = 0x7000_0002; + +// PA-RISC values for `SectionHeader32::sh_flags`. + +/// Section with short addressing. +pub const SHF_PARISC_SHORT: u32 = 0x2000_0000; +/// Section far from gp. +pub const SHF_PARISC_HUGE: u32 = 0x4000_0000; +/// Static branch prediction code. +pub const SHF_PARISC_SBP: u32 = 0x8000_0000; + +// PA-RISC values for `st_type` component of `Sym32::st_info`. + +/// Millicode function entry point. +pub const STT_PARISC_MILLICODE: u8 = 13; + +pub const STT_HP_OPAQUE: u8 = STT_LOOS + 0x1; +pub const STT_HP_STUB: u8 = STT_LOOS + 0x2; + +// PA-RISC values for `Rel*::r_type`. + +/// No reloc. +pub const R_PARISC_NONE: u32 = 0; +/// Direct 32-bit reference. +pub const R_PARISC_DIR32: u32 = 1; +/// Left 21 bits of eff. address. +pub const R_PARISC_DIR21L: u32 = 2; +/// Right 17 bits of eff. address. +pub const R_PARISC_DIR17R: u32 = 3; +/// 17 bits of eff. address. +pub const R_PARISC_DIR17F: u32 = 4; +/// Right 14 bits of eff. address. +pub const R_PARISC_DIR14R: u32 = 6; +/// 32-bit rel. address. +pub const R_PARISC_PCREL32: u32 = 9; +/// Left 21 bits of rel. address. +pub const R_PARISC_PCREL21L: u32 = 10; +/// Right 17 bits of rel. address. +pub const R_PARISC_PCREL17R: u32 = 11; +/// 17 bits of rel. address. +pub const R_PARISC_PCREL17F: u32 = 12; +/// Right 14 bits of rel. address. +pub const R_PARISC_PCREL14R: u32 = 14; +/// Left 21 bits of rel. address. +pub const R_PARISC_DPREL21L: u32 = 18; +/// Right 14 bits of rel. address. +pub const R_PARISC_DPREL14R: u32 = 22; +/// GP-relative, left 21 bits. +pub const R_PARISC_GPREL21L: u32 = 26; +/// GP-relative, right 14 bits. +pub const R_PARISC_GPREL14R: u32 = 30; +/// LT-relative, left 21 bits. +pub const R_PARISC_LTOFF21L: u32 = 34; +/// LT-relative, right 14 bits. +pub const R_PARISC_LTOFF14R: u32 = 38; +/// 32 bits section rel. address. +pub const R_PARISC_SECREL32: u32 = 41; +/// No relocation, set segment base. +pub const R_PARISC_SEGBASE: u32 = 48; +/// 32 bits segment rel. address. +pub const R_PARISC_SEGREL32: u32 = 49; +/// PLT rel. address, left 21 bits. +pub const R_PARISC_PLTOFF21L: u32 = 50; +/// PLT rel. address, right 14 bits. +pub const R_PARISC_PLTOFF14R: u32 = 54; +/// 32 bits LT-rel. function pointer. +pub const R_PARISC_LTOFF_FPTR32: u32 = 57; +/// LT-rel. fct ptr, left 21 bits. +pub const R_PARISC_LTOFF_FPTR21L: u32 = 58; +/// LT-rel. fct ptr, right 14 bits. +pub const R_PARISC_LTOFF_FPTR14R: u32 = 62; +/// 64 bits function address. +pub const R_PARISC_FPTR64: u32 = 64; +/// 32 bits function address. +pub const R_PARISC_PLABEL32: u32 = 65; +/// Left 21 bits of fdesc address. +pub const R_PARISC_PLABEL21L: u32 = 66; +/// Right 14 bits of fdesc address. +pub const R_PARISC_PLABEL14R: u32 = 70; +/// 64 bits PC-rel. address. +pub const R_PARISC_PCREL64: u32 = 72; +/// 22 bits PC-rel. address. +pub const R_PARISC_PCREL22F: u32 = 74; +/// PC-rel. address, right 14 bits. +pub const R_PARISC_PCREL14WR: u32 = 75; +/// PC rel. address, right 14 bits. +pub const R_PARISC_PCREL14DR: u32 = 76; +/// 16 bits PC-rel. address. +pub const R_PARISC_PCREL16F: u32 = 77; +/// 16 bits PC-rel. address. +pub const R_PARISC_PCREL16WF: u32 = 78; +/// 16 bits PC-rel. address. +pub const R_PARISC_PCREL16DF: u32 = 79; +/// 64 bits of eff. address. +pub const R_PARISC_DIR64: u32 = 80; +/// 14 bits of eff. address. +pub const R_PARISC_DIR14WR: u32 = 83; +/// 14 bits of eff. address. +pub const R_PARISC_DIR14DR: u32 = 84; +/// 16 bits of eff. address. +pub const R_PARISC_DIR16F: u32 = 85; +/// 16 bits of eff. address. +pub const R_PARISC_DIR16WF: u32 = 86; +/// 16 bits of eff. address. +pub const R_PARISC_DIR16DF: u32 = 87; +/// 64 bits of GP-rel. address. +pub const R_PARISC_GPREL64: u32 = 88; +/// GP-rel. address, right 14 bits. +pub const R_PARISC_GPREL14WR: u32 = 91; +/// GP-rel. address, right 14 bits. +pub const R_PARISC_GPREL14DR: u32 = 92; +/// 16 bits GP-rel. address. +pub const R_PARISC_GPREL16F: u32 = 93; +/// 16 bits GP-rel. address. +pub const R_PARISC_GPREL16WF: u32 = 94; +/// 16 bits GP-rel. address. +pub const R_PARISC_GPREL16DF: u32 = 95; +/// 64 bits LT-rel. address. +pub const R_PARISC_LTOFF64: u32 = 96; +/// LT-rel. address, right 14 bits. +pub const R_PARISC_LTOFF14WR: u32 = 99; +/// LT-rel. address, right 14 bits. +pub const R_PARISC_LTOFF14DR: u32 = 100; +/// 16 bits LT-rel. address. +pub const R_PARISC_LTOFF16F: u32 = 101; +/// 16 bits LT-rel. address. +pub const R_PARISC_LTOFF16WF: u32 = 102; +/// 16 bits LT-rel. address. +pub const R_PARISC_LTOFF16DF: u32 = 103; +/// 64 bits section rel. address. +pub const R_PARISC_SECREL64: u32 = 104; +/// 64 bits segment rel. address. +pub const R_PARISC_SEGREL64: u32 = 112; +/// PLT-rel. address, right 14 bits. +pub const R_PARISC_PLTOFF14WR: u32 = 115; +/// PLT-rel. address, right 14 bits. +pub const R_PARISC_PLTOFF14DR: u32 = 116; +/// 16 bits LT-rel. address. +pub const R_PARISC_PLTOFF16F: u32 = 117; +/// 16 bits PLT-rel. address. +pub const R_PARISC_PLTOFF16WF: u32 = 118; +/// 16 bits PLT-rel. address. +pub const R_PARISC_PLTOFF16DF: u32 = 119; +/// 64 bits LT-rel. function ptr. +pub const R_PARISC_LTOFF_FPTR64: u32 = 120; +/// LT-rel. fct. ptr., right 14 bits. +pub const R_PARISC_LTOFF_FPTR14WR: u32 = 123; +/// LT-rel. fct. ptr., right 14 bits. +pub const R_PARISC_LTOFF_FPTR14DR: u32 = 124; +/// 16 bits LT-rel. function ptr. +pub const R_PARISC_LTOFF_FPTR16F: u32 = 125; +/// 16 bits LT-rel. function ptr. +pub const R_PARISC_LTOFF_FPTR16WF: u32 = 126; +/// 16 bits LT-rel. function ptr. +pub const R_PARISC_LTOFF_FPTR16DF: u32 = 127; +pub const R_PARISC_LORESERVE: u32 = 128; +/// Copy relocation. +pub const R_PARISC_COPY: u32 = 128; +/// Dynamic reloc, imported PLT +pub const R_PARISC_IPLT: u32 = 129; +/// Dynamic reloc, exported PLT +pub const R_PARISC_EPLT: u32 = 130; +/// 32 bits TP-rel. address. +pub const R_PARISC_TPREL32: u32 = 153; +/// TP-rel. address, left 21 bits. +pub const R_PARISC_TPREL21L: u32 = 154; +/// TP-rel. address, right 14 bits. +pub const R_PARISC_TPREL14R: u32 = 158; +/// LT-TP-rel. address, left 21 bits. +pub const R_PARISC_LTOFF_TP21L: u32 = 162; +/// LT-TP-rel. address, right 14 bits. +pub const R_PARISC_LTOFF_TP14R: u32 = 166; +/// 14 bits LT-TP-rel. address. +pub const R_PARISC_LTOFF_TP14F: u32 = 167; +/// 64 bits TP-rel. address. +pub const R_PARISC_TPREL64: u32 = 216; +/// TP-rel. address, right 14 bits. +pub const R_PARISC_TPREL14WR: u32 = 219; +/// TP-rel. address, right 14 bits. +pub const R_PARISC_TPREL14DR: u32 = 220; +/// 16 bits TP-rel. address. +pub const R_PARISC_TPREL16F: u32 = 221; +/// 16 bits TP-rel. address. +pub const R_PARISC_TPREL16WF: u32 = 222; +/// 16 bits TP-rel. address. +pub const R_PARISC_TPREL16DF: u32 = 223; +/// 64 bits LT-TP-rel. address. +pub const R_PARISC_LTOFF_TP64: u32 = 224; +/// LT-TP-rel. address, right 14 bits. +pub const R_PARISC_LTOFF_TP14WR: u32 = 227; +/// LT-TP-rel. address, right 14 bits. +pub const R_PARISC_LTOFF_TP14DR: u32 = 228; +/// 16 bits LT-TP-rel. address. +pub const R_PARISC_LTOFF_TP16F: u32 = 229; +/// 16 bits LT-TP-rel. address. +pub const R_PARISC_LTOFF_TP16WF: u32 = 230; +/// 16 bits LT-TP-rel. address. +pub const R_PARISC_LTOFF_TP16DF: u32 = 231; +pub const R_PARISC_GNU_VTENTRY: u32 = 232; +pub const R_PARISC_GNU_VTINHERIT: u32 = 233; +/// GD 21-bit left. +pub const R_PARISC_TLS_GD21L: u32 = 234; +/// GD 14-bit right. +pub const R_PARISC_TLS_GD14R: u32 = 235; +/// GD call to __t_g_a. +pub const R_PARISC_TLS_GDCALL: u32 = 236; +/// LD module 21-bit left. +pub const R_PARISC_TLS_LDM21L: u32 = 237; +/// LD module 14-bit right. +pub const R_PARISC_TLS_LDM14R: u32 = 238; +/// LD module call to __t_g_a. +pub const R_PARISC_TLS_LDMCALL: u32 = 239; +/// LD offset 21-bit left. +pub const R_PARISC_TLS_LDO21L: u32 = 240; +/// LD offset 14-bit right. +pub const R_PARISC_TLS_LDO14R: u32 = 241; +/// DTP module 32-bit. +pub const R_PARISC_TLS_DTPMOD32: u32 = 242; +/// DTP module 64-bit. +pub const R_PARISC_TLS_DTPMOD64: u32 = 243; +/// DTP offset 32-bit. +pub const R_PARISC_TLS_DTPOFF32: u32 = 244; +/// DTP offset 32-bit. +pub const R_PARISC_TLS_DTPOFF64: u32 = 245; +pub const R_PARISC_TLS_LE21L: u32 = R_PARISC_TPREL21L; +pub const R_PARISC_TLS_LE14R: u32 = R_PARISC_TPREL14R; +pub const R_PARISC_TLS_IE21L: u32 = R_PARISC_LTOFF_TP21L; +pub const R_PARISC_TLS_IE14R: u32 = R_PARISC_LTOFF_TP14R; +pub const R_PARISC_TLS_TPREL32: u32 = R_PARISC_TPREL32; +pub const R_PARISC_TLS_TPREL64: u32 = R_PARISC_TPREL64; +pub const R_PARISC_HIRESERVE: u32 = 255; + +// PA-RISC values for `ProgramHeader*::p_type`. + +pub const PT_HP_TLS: u32 = PT_LOOS + 0x0; +pub const PT_HP_CORE_NONE: u32 = PT_LOOS + 0x1; +pub const PT_HP_CORE_VERSION: u32 = PT_LOOS + 0x2; +pub const PT_HP_CORE_KERNEL: u32 = PT_LOOS + 0x3; +pub const PT_HP_CORE_COMM: u32 = PT_LOOS + 0x4; +pub const PT_HP_CORE_PROC: u32 = PT_LOOS + 0x5; +pub const PT_HP_CORE_LOADABLE: u32 = PT_LOOS + 0x6; +pub const PT_HP_CORE_STACK: u32 = PT_LOOS + 0x7; +pub const PT_HP_CORE_SHM: u32 = PT_LOOS + 0x8; +pub const PT_HP_CORE_MMF: u32 = PT_LOOS + 0x9; +pub const PT_HP_PARALLEL: u32 = PT_LOOS + 0x10; +pub const PT_HP_FASTBIND: u32 = PT_LOOS + 0x11; +pub const PT_HP_OPT_ANNOT: u32 = PT_LOOS + 0x12; +pub const PT_HP_HSL_ANNOT: u32 = PT_LOOS + 0x13; +pub const PT_HP_STACK: u32 = PT_LOOS + 0x14; + +pub const PT_PARISC_ARCHEXT: u32 = 0x7000_0000; +pub const PT_PARISC_UNWIND: u32 = 0x7000_0001; + +// PA-RISC values for `ProgramHeader*::p_flags`. + +pub const PF_PARISC_SBP: u32 = 0x0800_0000; + +pub const PF_HP_PAGE_SIZE: u32 = 0x0010_0000; +pub const PF_HP_FAR_SHARED: u32 = 0x0020_0000; +pub const PF_HP_NEAR_SHARED: u32 = 0x0040_0000; +pub const PF_HP_CODE: u32 = 0x0100_0000; +pub const PF_HP_MODIFY: u32 = 0x0200_0000; +pub const PF_HP_LAZYSWAP: u32 = 0x0400_0000; +pub const PF_HP_SBP: u32 = 0x0800_0000; + +// Alpha specific definitions. + +// Alpha values for `FileHeader64::e_flags`. + +/// All addresses must be < 2GB. +pub const EF_ALPHA_32BIT: u32 = 1; +/// Relocations for relaxing exist. +pub const EF_ALPHA_CANRELAX: u32 = 2; + +// Alpha values for `SectionHeader64::sh_type`. + +// These two are primerily concerned with ECOFF debugging info. +pub const SHT_ALPHA_DEBUG: u32 = 0x7000_0001; +pub const SHT_ALPHA_REGINFO: u32 = 0x7000_0002; + +// Alpha values for `SectionHeader64::sh_flags`. + +pub const SHF_ALPHA_GPREL: u32 = 0x1000_0000; + +// Alpha values for `Sym64::st_other`. +/// No PV required. +pub const STO_ALPHA_NOPV: u8 = 0x80; +/// PV only used for initial ldgp. +pub const STO_ALPHA_STD_GPLOAD: u8 = 0x88; + +// Alpha values for `Rel64::r_type`. + +/// No reloc +pub const R_ALPHA_NONE: u32 = 0; +/// Direct 32 bit +pub const R_ALPHA_REFLONG: u32 = 1; +/// Direct 64 bit +pub const R_ALPHA_REFQUAD: u32 = 2; +/// GP relative 32 bit +pub const R_ALPHA_GPREL32: u32 = 3; +/// GP relative 16 bit w/optimization +pub const R_ALPHA_LITERAL: u32 = 4; +/// Optimization hint for LITERAL +pub const R_ALPHA_LITUSE: u32 = 5; +/// Add displacement to GP +pub const R_ALPHA_GPDISP: u32 = 6; +/// PC+4 relative 23 bit shifted +pub const R_ALPHA_BRADDR: u32 = 7; +/// PC+4 relative 16 bit shifted +pub const R_ALPHA_HINT: u32 = 8; +/// PC relative 16 bit +pub const R_ALPHA_SREL16: u32 = 9; +/// PC relative 32 bit +pub const R_ALPHA_SREL32: u32 = 10; +/// PC relative 64 bit +pub const R_ALPHA_SREL64: u32 = 11; +/// GP relative 32 bit, high 16 bits +pub const R_ALPHA_GPRELHIGH: u32 = 17; +/// GP relative 32 bit, low 16 bits +pub const R_ALPHA_GPRELLOW: u32 = 18; +/// GP relative 16 bit +pub const R_ALPHA_GPREL16: u32 = 19; +/// Copy symbol at runtime +pub const R_ALPHA_COPY: u32 = 24; +/// Create GOT entry +pub const R_ALPHA_GLOB_DAT: u32 = 25; +/// Create PLT entry +pub const R_ALPHA_JMP_SLOT: u32 = 26; +/// Adjust by program base +pub const R_ALPHA_RELATIVE: u32 = 27; +pub const R_ALPHA_TLS_GD_HI: u32 = 28; +pub const R_ALPHA_TLSGD: u32 = 29; +pub const R_ALPHA_TLS_LDM: u32 = 30; +pub const R_ALPHA_DTPMOD64: u32 = 31; +pub const R_ALPHA_GOTDTPREL: u32 = 32; +pub const R_ALPHA_DTPREL64: u32 = 33; +pub const R_ALPHA_DTPRELHI: u32 = 34; +pub const R_ALPHA_DTPRELLO: u32 = 35; +pub const R_ALPHA_DTPREL16: u32 = 36; +pub const R_ALPHA_GOTTPREL: u32 = 37; +pub const R_ALPHA_TPREL64: u32 = 38; +pub const R_ALPHA_TPRELHI: u32 = 39; +pub const R_ALPHA_TPRELLO: u32 = 40; +pub const R_ALPHA_TPREL16: u32 = 41; + +// Magic values of the `R_ALPHA_LITUSE` relocation addend. +pub const LITUSE_ALPHA_ADDR: u32 = 0; +pub const LITUSE_ALPHA_BASE: u32 = 1; +pub const LITUSE_ALPHA_BYTOFF: u32 = 2; +pub const LITUSE_ALPHA_JSR: u32 = 3; +pub const LITUSE_ALPHA_TLS_GD: u32 = 4; +pub const LITUSE_ALPHA_TLS_LDM: u32 = 5; + +// Alpha values for `Dyn64::d_tag`. +pub const DT_ALPHA_PLTRO: u32 = DT_LOPROC + 0; + +// PowerPC specific declarations. + +// PowerPC values for `FileHeader*::e_flags`. +/// PowerPC embedded flag +pub const EF_PPC_EMB: u32 = 0x8000_0000; + +// Cygnus local bits below . +/// PowerPC -mrelocatable flag +pub const EF_PPC_RELOCATABLE: u32 = 0x0001_0000; +/// PowerPC -mrelocatable-lib flag +pub const EF_PPC_RELOCATABLE_LIB: u32 = 0x0000_8000; + +// PowerPC values for `Rel*::r_type` defined by the ABIs. +pub const R_PPC_NONE: u32 = 0; +/// 32bit absolute address +pub const R_PPC_ADDR32: u32 = 1; +/// 26bit address, 2 bits ignored. +pub const R_PPC_ADDR24: u32 = 2; +/// 16bit absolute address +pub const R_PPC_ADDR16: u32 = 3; +/// lower 16bit of absolute address +pub const R_PPC_ADDR16_LO: u32 = 4; +/// high 16bit of absolute address +pub const R_PPC_ADDR16_HI: u32 = 5; +/// adjusted high 16bit +pub const R_PPC_ADDR16_HA: u32 = 6; +/// 16bit address, 2 bits ignored +pub const R_PPC_ADDR14: u32 = 7; +pub const R_PPC_ADDR14_BRTAKEN: u32 = 8; +pub const R_PPC_ADDR14_BRNTAKEN: u32 = 9; +/// PC relative 26 bit +pub const R_PPC_REL24: u32 = 10; +/// PC relative 16 bit +pub const R_PPC_REL14: u32 = 11; +pub const R_PPC_REL14_BRTAKEN: u32 = 12; +pub const R_PPC_REL14_BRNTAKEN: u32 = 13; +pub const R_PPC_GOT16: u32 = 14; +pub const R_PPC_GOT16_LO: u32 = 15; +pub const R_PPC_GOT16_HI: u32 = 16; +pub const R_PPC_GOT16_HA: u32 = 17; +pub const R_PPC_PLTREL24: u32 = 18; +pub const R_PPC_COPY: u32 = 19; +pub const R_PPC_GLOB_DAT: u32 = 20; +pub const R_PPC_JMP_SLOT: u32 = 21; +pub const R_PPC_RELATIVE: u32 = 22; +pub const R_PPC_LOCAL24PC: u32 = 23; +pub const R_PPC_UADDR32: u32 = 24; +pub const R_PPC_UADDR16: u32 = 25; +pub const R_PPC_REL32: u32 = 26; +pub const R_PPC_PLT32: u32 = 27; +pub const R_PPC_PLTREL32: u32 = 28; +pub const R_PPC_PLT16_LO: u32 = 29; +pub const R_PPC_PLT16_HI: u32 = 30; +pub const R_PPC_PLT16_HA: u32 = 31; +pub const R_PPC_SDAREL16: u32 = 32; +pub const R_PPC_SECTOFF: u32 = 33; +pub const R_PPC_SECTOFF_LO: u32 = 34; +pub const R_PPC_SECTOFF_HI: u32 = 35; +pub const R_PPC_SECTOFF_HA: u32 = 36; + +// PowerPC values for `Rel*::r_type` defined for the TLS access ABI. +/// none (sym+add)@tls +pub const R_PPC_TLS: u32 = 67; +/// word32 (sym+add)@dtpmod +pub const R_PPC_DTPMOD32: u32 = 68; +/// half16* (sym+add)@tprel +pub const R_PPC_TPREL16: u32 = 69; +/// half16 (sym+add)@tprel@l +pub const R_PPC_TPREL16_LO: u32 = 70; +/// half16 (sym+add)@tprel@h +pub const R_PPC_TPREL16_HI: u32 = 71; +/// half16 (sym+add)@tprel@ha +pub const R_PPC_TPREL16_HA: u32 = 72; +/// word32 (sym+add)@tprel +pub const R_PPC_TPREL32: u32 = 73; +/// half16*(sym+add)@dtprel +pub const R_PPC_DTPREL16: u32 = 74; +/// half16 (sym+add)@dtprel@l +pub const R_PPC_DTPREL16_LO: u32 = 75; +/// half16 (sym+add)@dtprel@h +pub const R_PPC_DTPREL16_HI: u32 = 76; +/// half16 (sym+add)@dtprel@ha +pub const R_PPC_DTPREL16_HA: u32 = 77; +/// word32 (sym+add)@dtprel +pub const R_PPC_DTPREL32: u32 = 78; +/// half16* (sym+add)@got@tlsgd +pub const R_PPC_GOT_TLSGD16: u32 = 79; +/// half16 (sym+add)@got@tlsgd@l +pub const R_PPC_GOT_TLSGD16_LO: u32 = 80; +/// half16 (sym+add)@got@tlsgd@h +pub const R_PPC_GOT_TLSGD16_HI: u32 = 81; +/// half16 (sym+add)@got@tlsgd@ha +pub const R_PPC_GOT_TLSGD16_HA: u32 = 82; +/// half16* (sym+add)@got@tlsld +pub const R_PPC_GOT_TLSLD16: u32 = 83; +/// half16 (sym+add)@got@tlsld@l +pub const R_PPC_GOT_TLSLD16_LO: u32 = 84; +/// half16 (sym+add)@got@tlsld@h +pub const R_PPC_GOT_TLSLD16_HI: u32 = 85; +/// half16 (sym+add)@got@tlsld@ha +pub const R_PPC_GOT_TLSLD16_HA: u32 = 86; +/// half16* (sym+add)@got@tprel +pub const R_PPC_GOT_TPREL16: u32 = 87; +/// half16 (sym+add)@got@tprel@l +pub const R_PPC_GOT_TPREL16_LO: u32 = 88; +/// half16 (sym+add)@got@tprel@h +pub const R_PPC_GOT_TPREL16_HI: u32 = 89; +/// half16 (sym+add)@got@tprel@ha +pub const R_PPC_GOT_TPREL16_HA: u32 = 90; +/// half16* (sym+add)@got@dtprel +pub const R_PPC_GOT_DTPREL16: u32 = 91; +/// half16* (sym+add)@got@dtprel@l +pub const R_PPC_GOT_DTPREL16_LO: u32 = 92; +/// half16* (sym+add)@got@dtprel@h +pub const R_PPC_GOT_DTPREL16_HI: u32 = 93; +/// half16* (sym+add)@got@dtprel@ha +pub const R_PPC_GOT_DTPREL16_HA: u32 = 94; +/// none (sym+add)@tlsgd +pub const R_PPC_TLSGD: u32 = 95; +/// none (sym+add)@tlsld +pub const R_PPC_TLSLD: u32 = 96; + +// PowerPC values for `Rel*::r_type` from the Embedded ELF ABI. +pub const R_PPC_EMB_NADDR32: u32 = 101; +pub const R_PPC_EMB_NADDR16: u32 = 102; +pub const R_PPC_EMB_NADDR16_LO: u32 = 103; +pub const R_PPC_EMB_NADDR16_HI: u32 = 104; +pub const R_PPC_EMB_NADDR16_HA: u32 = 105; +pub const R_PPC_EMB_SDAI16: u32 = 106; +pub const R_PPC_EMB_SDA2I16: u32 = 107; +pub const R_PPC_EMB_SDA2REL: u32 = 108; +/// 16 bit offset in SDA +pub const R_PPC_EMB_SDA21: u32 = 109; +pub const R_PPC_EMB_MRKREF: u32 = 110; +pub const R_PPC_EMB_RELSEC16: u32 = 111; +pub const R_PPC_EMB_RELST_LO: u32 = 112; +pub const R_PPC_EMB_RELST_HI: u32 = 113; +pub const R_PPC_EMB_RELST_HA: u32 = 114; +pub const R_PPC_EMB_BIT_FLD: u32 = 115; +/// 16 bit relative offset in SDA +pub const R_PPC_EMB_RELSDA: u32 = 116; + +// Diab tool values for `Rel*::r_type`. +/// like EMB_SDA21, but lower 16 bit +pub const R_PPC_DIAB_SDA21_LO: u32 = 180; +/// like EMB_SDA21, but high 16 bit +pub const R_PPC_DIAB_SDA21_HI: u32 = 181; +/// like EMB_SDA21, adjusted high 16 +pub const R_PPC_DIAB_SDA21_HA: u32 = 182; +/// like EMB_RELSDA, but lower 16 bit +pub const R_PPC_DIAB_RELSDA_LO: u32 = 183; +/// like EMB_RELSDA, but high 16 bit +pub const R_PPC_DIAB_RELSDA_HI: u32 = 184; +/// like EMB_RELSDA, adjusted high 16 +pub const R_PPC_DIAB_RELSDA_HA: u32 = 185; + +/// GNU extension to support local ifunc. +pub const R_PPC_IRELATIVE: u32 = 248; + +// GNU relocs used in PIC code sequences. +/// half16 (sym+add-.) +pub const R_PPC_REL16: u32 = 249; +/// half16 (sym+add-.)@l +pub const R_PPC_REL16_LO: u32 = 250; +/// half16 (sym+add-.)@h +pub const R_PPC_REL16_HI: u32 = 251; +/// half16 (sym+add-.)@ha +pub const R_PPC_REL16_HA: u32 = 252; + +/// This is a phony reloc to handle any old fashioned TOC16 references that may +/// still be in object files. +pub const R_PPC_TOC16: u32 = 255; + +// PowerPC specific values for `Dyn*::d_tag`. +pub const DT_PPC_GOT: u32 = DT_LOPROC + 0; +pub const DT_PPC_OPT: u32 = DT_LOPROC + 1; + +// PowerPC specific values for the `DT_PPC_OPT` entry. +pub const PPC_OPT_TLS: u32 = 1; + +// PowerPC64 values for `Rel*::r_type` defined by the ABIs. +pub const R_PPC64_NONE: u32 = R_PPC_NONE; +/// 32bit absolute address +pub const R_PPC64_ADDR32: u32 = R_PPC_ADDR32; +/// 26bit address, word aligned +pub const R_PPC64_ADDR24: u32 = R_PPC_ADDR24; +/// 16bit absolute address +pub const R_PPC64_ADDR16: u32 = R_PPC_ADDR16; +/// lower 16bits of address +pub const R_PPC64_ADDR16_LO: u32 = R_PPC_ADDR16_LO; +/// high 16bits of address. +pub const R_PPC64_ADDR16_HI: u32 = R_PPC_ADDR16_HI; +/// adjusted high 16bits. +pub const R_PPC64_ADDR16_HA: u32 = R_PPC_ADDR16_HA; +/// 16bit address, word aligned +pub const R_PPC64_ADDR14: u32 = R_PPC_ADDR14; +pub const R_PPC64_ADDR14_BRTAKEN: u32 = R_PPC_ADDR14_BRTAKEN; +pub const R_PPC64_ADDR14_BRNTAKEN: u32 = R_PPC_ADDR14_BRNTAKEN; +/// PC-rel. 26 bit, word aligned +pub const R_PPC64_REL24: u32 = R_PPC_REL24; +/// PC relative 16 bit +pub const R_PPC64_REL14: u32 = R_PPC_REL14; +pub const R_PPC64_REL14_BRTAKEN: u32 = R_PPC_REL14_BRTAKEN; +pub const R_PPC64_REL14_BRNTAKEN: u32 = R_PPC_REL14_BRNTAKEN; +pub const R_PPC64_GOT16: u32 = R_PPC_GOT16; +pub const R_PPC64_GOT16_LO: u32 = R_PPC_GOT16_LO; +pub const R_PPC64_GOT16_HI: u32 = R_PPC_GOT16_HI; +pub const R_PPC64_GOT16_HA: u32 = R_PPC_GOT16_HA; + +pub const R_PPC64_COPY: u32 = R_PPC_COPY; +pub const R_PPC64_GLOB_DAT: u32 = R_PPC_GLOB_DAT; +pub const R_PPC64_JMP_SLOT: u32 = R_PPC_JMP_SLOT; +pub const R_PPC64_RELATIVE: u32 = R_PPC_RELATIVE; + +pub const R_PPC64_UADDR32: u32 = R_PPC_UADDR32; +pub const R_PPC64_UADDR16: u32 = R_PPC_UADDR16; +pub const R_PPC64_REL32: u32 = R_PPC_REL32; +pub const R_PPC64_PLT32: u32 = R_PPC_PLT32; +pub const R_PPC64_PLTREL32: u32 = R_PPC_PLTREL32; +pub const R_PPC64_PLT16_LO: u32 = R_PPC_PLT16_LO; +pub const R_PPC64_PLT16_HI: u32 = R_PPC_PLT16_HI; +pub const R_PPC64_PLT16_HA: u32 = R_PPC_PLT16_HA; + +pub const R_PPC64_SECTOFF: u32 = R_PPC_SECTOFF; +pub const R_PPC64_SECTOFF_LO: u32 = R_PPC_SECTOFF_LO; +pub const R_PPC64_SECTOFF_HI: u32 = R_PPC_SECTOFF_HI; +pub const R_PPC64_SECTOFF_HA: u32 = R_PPC_SECTOFF_HA; +/// word30 (S + A - P) >> 2 +pub const R_PPC64_ADDR30: u32 = 37; +/// doubleword64 S + A +pub const R_PPC64_ADDR64: u32 = 38; +/// half16 #higher(S + A) +pub const R_PPC64_ADDR16_HIGHER: u32 = 39; +/// half16 #highera(S + A) +pub const R_PPC64_ADDR16_HIGHERA: u32 = 40; +/// half16 #highest(S + A) +pub const R_PPC64_ADDR16_HIGHEST: u32 = 41; +/// half16 #highesta(S + A) +pub const R_PPC64_ADDR16_HIGHESTA: u32 = 42; +/// doubleword64 S + A +pub const R_PPC64_UADDR64: u32 = 43; +/// doubleword64 S + A - P +pub const R_PPC64_REL64: u32 = 44; +/// doubleword64 L + A +pub const R_PPC64_PLT64: u32 = 45; +/// doubleword64 L + A - P +pub const R_PPC64_PLTREL64: u32 = 46; +/// half16* S + A - .TOC +pub const R_PPC64_TOC16: u32 = 47; +/// half16 #lo(S + A - .TOC.) +pub const R_PPC64_TOC16_LO: u32 = 48; +/// half16 #hi(S + A - .TOC.) +pub const R_PPC64_TOC16_HI: u32 = 49; +/// half16 #ha(S + A - .TOC.) +pub const R_PPC64_TOC16_HA: u32 = 50; +/// doubleword64 .TOC +pub const R_PPC64_TOC: u32 = 51; +/// half16* M + A +pub const R_PPC64_PLTGOT16: u32 = 52; +/// half16 #lo(M + A) +pub const R_PPC64_PLTGOT16_LO: u32 = 53; +/// half16 #hi(M + A) +pub const R_PPC64_PLTGOT16_HI: u32 = 54; +/// half16 #ha(M + A) +pub const R_PPC64_PLTGOT16_HA: u32 = 55; + +/// half16ds* (S + A) >> 2 +pub const R_PPC64_ADDR16_DS: u32 = 56; +/// half16ds #lo(S + A) >> 2 +pub const R_PPC64_ADDR16_LO_DS: u32 = 57; +/// half16ds* (G + A) >> 2 +pub const R_PPC64_GOT16_DS: u32 = 58; +/// half16ds #lo(G + A) >> 2 +pub const R_PPC64_GOT16_LO_DS: u32 = 59; +/// half16ds #lo(L + A) >> 2 +pub const R_PPC64_PLT16_LO_DS: u32 = 60; +/// half16ds* (R + A) >> 2 +pub const R_PPC64_SECTOFF_DS: u32 = 61; +/// half16ds #lo(R + A) >> 2 +pub const R_PPC64_SECTOFF_LO_DS: u32 = 62; +/// half16ds* (S + A - .TOC.) >> 2 +pub const R_PPC64_TOC16_DS: u32 = 63; +/// half16ds #lo(S + A - .TOC.) >> 2 +pub const R_PPC64_TOC16_LO_DS: u32 = 64; +/// half16ds* (M + A) >> 2 +pub const R_PPC64_PLTGOT16_DS: u32 = 65; +/// half16ds #lo(M + A) >> 2 +pub const R_PPC64_PLTGOT16_LO_DS: u32 = 66; + +// PowerPC64 values for `Rel*::r_type` defined for the TLS access ABI. +/// none (sym+add)@tls +pub const R_PPC64_TLS: u32 = 67; +/// doubleword64 (sym+add)@dtpmod +pub const R_PPC64_DTPMOD64: u32 = 68; +/// half16* (sym+add)@tprel +pub const R_PPC64_TPREL16: u32 = 69; +/// half16 (sym+add)@tprel@l +pub const R_PPC64_TPREL16_LO: u32 = 70; +/// half16 (sym+add)@tprel@h +pub const R_PPC64_TPREL16_HI: u32 = 71; +/// half16 (sym+add)@tprel@ha +pub const R_PPC64_TPREL16_HA: u32 = 72; +/// doubleword64 (sym+add)@tprel +pub const R_PPC64_TPREL64: u32 = 73; +/// half16* (sym+add)@dtprel +pub const R_PPC64_DTPREL16: u32 = 74; +/// half16 (sym+add)@dtprel@l +pub const R_PPC64_DTPREL16_LO: u32 = 75; +/// half16 (sym+add)@dtprel@h +pub const R_PPC64_DTPREL16_HI: u32 = 76; +/// half16 (sym+add)@dtprel@ha +pub const R_PPC64_DTPREL16_HA: u32 = 77; +/// doubleword64 (sym+add)@dtprel +pub const R_PPC64_DTPREL64: u32 = 78; +/// half16* (sym+add)@got@tlsgd +pub const R_PPC64_GOT_TLSGD16: u32 = 79; +/// half16 (sym+add)@got@tlsgd@l +pub const R_PPC64_GOT_TLSGD16_LO: u32 = 80; +/// half16 (sym+add)@got@tlsgd@h +pub const R_PPC64_GOT_TLSGD16_HI: u32 = 81; +/// half16 (sym+add)@got@tlsgd@ha +pub const R_PPC64_GOT_TLSGD16_HA: u32 = 82; +/// half16* (sym+add)@got@tlsld +pub const R_PPC64_GOT_TLSLD16: u32 = 83; +/// half16 (sym+add)@got@tlsld@l +pub const R_PPC64_GOT_TLSLD16_LO: u32 = 84; +/// half16 (sym+add)@got@tlsld@h +pub const R_PPC64_GOT_TLSLD16_HI: u32 = 85; +/// half16 (sym+add)@got@tlsld@ha +pub const R_PPC64_GOT_TLSLD16_HA: u32 = 86; +/// half16ds* (sym+add)@got@tprel +pub const R_PPC64_GOT_TPREL16_DS: u32 = 87; +/// half16ds (sym+add)@got@tprel@l +pub const R_PPC64_GOT_TPREL16_LO_DS: u32 = 88; +/// half16 (sym+add)@got@tprel@h +pub const R_PPC64_GOT_TPREL16_HI: u32 = 89; +/// half16 (sym+add)@got@tprel@ha +pub const R_PPC64_GOT_TPREL16_HA: u32 = 90; +/// half16ds* (sym+add)@got@dtprel +pub const R_PPC64_GOT_DTPREL16_DS: u32 = 91; +/// half16ds (sym+add)@got@dtprel@l +pub const R_PPC64_GOT_DTPREL16_LO_DS: u32 = 92; +/// half16 (sym+add)@got@dtprel@h +pub const R_PPC64_GOT_DTPREL16_HI: u32 = 93; +/// half16 (sym+add)@got@dtprel@ha +pub const R_PPC64_GOT_DTPREL16_HA: u32 = 94; +/// half16ds* (sym+add)@tprel +pub const R_PPC64_TPREL16_DS: u32 = 95; +/// half16ds (sym+add)@tprel@l +pub const R_PPC64_TPREL16_LO_DS: u32 = 96; +/// half16 (sym+add)@tprel@higher +pub const R_PPC64_TPREL16_HIGHER: u32 = 97; +/// half16 (sym+add)@tprel@highera +pub const R_PPC64_TPREL16_HIGHERA: u32 = 98; +/// half16 (sym+add)@tprel@highest +pub const R_PPC64_TPREL16_HIGHEST: u32 = 99; +/// half16 (sym+add)@tprel@highesta +pub const R_PPC64_TPREL16_HIGHESTA: u32 = 100; +/// half16ds* (sym+add)@dtprel +pub const R_PPC64_DTPREL16_DS: u32 = 101; +/// half16ds (sym+add)@dtprel@l +pub const R_PPC64_DTPREL16_LO_DS: u32 = 102; +/// half16 (sym+add)@dtprel@higher +pub const R_PPC64_DTPREL16_HIGHER: u32 = 103; +/// half16 (sym+add)@dtprel@highera +pub const R_PPC64_DTPREL16_HIGHERA: u32 = 104; +/// half16 (sym+add)@dtprel@highest +pub const R_PPC64_DTPREL16_HIGHEST: u32 = 105; +/// half16 (sym+add)@dtprel@highesta +pub const R_PPC64_DTPREL16_HIGHESTA: u32 = 106; +/// none (sym+add)@tlsgd +pub const R_PPC64_TLSGD: u32 = 107; +/// none (sym+add)@tlsld +pub const R_PPC64_TLSLD: u32 = 108; +/// none +pub const R_PPC64_TOCSAVE: u32 = 109; + +// Added when HA and HI relocs were changed to report overflows. +pub const R_PPC64_ADDR16_HIGH: u32 = 110; +pub const R_PPC64_ADDR16_HIGHA: u32 = 111; +pub const R_PPC64_TPREL16_HIGH: u32 = 112; +pub const R_PPC64_TPREL16_HIGHA: u32 = 113; +pub const R_PPC64_DTPREL16_HIGH: u32 = 114; +pub const R_PPC64_DTPREL16_HIGHA: u32 = 115; + +/// GNU extension to support local ifunc. +pub const R_PPC64_JMP_IREL: u32 = 247; +/// GNU extension to support local ifunc. +pub const R_PPC64_IRELATIVE: u32 = 248; +/// half16 (sym+add-.) +pub const R_PPC64_REL16: u32 = 249; +/// half16 (sym+add-.)@l +pub const R_PPC64_REL16_LO: u32 = 250; +/// half16 (sym+add-.)@h +pub const R_PPC64_REL16_HI: u32 = 251; +/// half16 (sym+add-.)@ha +pub const R_PPC64_REL16_HA: u32 = 252; + +// PowerPC64 values for `FileHeader64::e_flags. +/// PowerPC64 bits specifying ABI. +/// +/// 1 for original function descriptor using ABI, +/// 2 for revised ABI without function descriptors, +/// 0 for unspecified or not using any features affected by the differences. +pub const EF_PPC64_ABI: u32 = 3; + +// PowerPC64 values for `Dyn64::d_tag. +pub const DT_PPC64_GLINK: u32 = DT_LOPROC + 0; +pub const DT_PPC64_OPD: u32 = DT_LOPROC + 1; +pub const DT_PPC64_OPDSZ: u32 = DT_LOPROC + 2; +pub const DT_PPC64_OPT: u32 = DT_LOPROC + 3; + +// PowerPC64 bits for `DT_PPC64_OPT` entry. +pub const PPC64_OPT_TLS: u32 = 1; +pub const PPC64_OPT_MULTI_TOC: u32 = 2; +pub const PPC64_OPT_LOCALENTRY: u32 = 4; + +// PowerPC64 values for `Sym64::st_other. +pub const STO_PPC64_LOCAL_BIT: u8 = 5; +pub const STO_PPC64_LOCAL_MASK: u8 = 7 << STO_PPC64_LOCAL_BIT; + +// ARM specific declarations. + +// ARM values for `FileHeader*::e_flags`. +pub const EF_ARM_RELEXEC: u32 = 0x01; +pub const EF_ARM_HASENTRY: u32 = 0x02; +pub const EF_ARM_INTERWORK: u32 = 0x04; +pub const EF_ARM_APCS_26: u32 = 0x08; +pub const EF_ARM_APCS_FLOAT: u32 = 0x10; +pub const EF_ARM_PIC: u32 = 0x20; +/// 8-bit structure alignment is in use +pub const EF_ARM_ALIGN8: u32 = 0x40; +pub const EF_ARM_NEW_ABI: u32 = 0x80; +pub const EF_ARM_OLD_ABI: u32 = 0x100; +pub const EF_ARM_SOFT_FLOAT: u32 = 0x200; +pub const EF_ARM_VFP_FLOAT: u32 = 0x400; +pub const EF_ARM_MAVERICK_FLOAT: u32 = 0x800; + +/// NB conflicts with EF_ARM_SOFT_FLOAT +pub const EF_ARM_ABI_FLOAT_SOFT: u32 = 0x200; +/// NB conflicts with EF_ARM_VFP_FLOAT +pub const EF_ARM_ABI_FLOAT_HARD: u32 = 0x400; + +// Other constants defined in the ARM ELF spec. version B-01. +// NB. These conflict with values defined above. +pub const EF_ARM_SYMSARESORTED: u32 = 0x04; +pub const EF_ARM_DYNSYMSUSESEGIDX: u32 = 0x08; +pub const EF_ARM_MAPSYMSFIRST: u32 = 0x10; + +// Constants defined in AAELF. +pub const EF_ARM_BE8: u32 = 0x0080_0000; +pub const EF_ARM_LE8: u32 = 0x0040_0000; + +pub const EF_ARM_EABIMASK: u32 = 0xff00_0000; +pub const EF_ARM_EABI_UNKNOWN: u32 = 0x0000_0000; +pub const EF_ARM_EABI_VER1: u32 = 0x0100_0000; +pub const EF_ARM_EABI_VER2: u32 = 0x0200_0000; +pub const EF_ARM_EABI_VER3: u32 = 0x0300_0000; +pub const EF_ARM_EABI_VER4: u32 = 0x0400_0000; +pub const EF_ARM_EABI_VER5: u32 = 0x0500_0000; + +// ARM Thumb values for `st_type` component of `Sym*::st_info`. +/// A Thumb function. +pub const STT_ARM_TFUNC: u8 = STT_LOPROC; +/// A Thumb label. +pub const STT_ARM_16BIT: u8 = STT_HIPROC; + +// ARM values for `SectionHeader*::sh_flags`. +/// Section contains an entry point +pub const SHF_ARM_ENTRYSECT: u32 = 0x1000_0000; +/// Section may be multiply defined in the input to a link step. +pub const SHF_ARM_COMDEF: u32 = 0x8000_0000; + +// ARM values for `ProgramHeader*::p_flags`. +/// Segment contains the location addressed by the static base. +pub const PF_ARM_SB: u32 = 0x1000_0000; +/// Position-independent segment. +pub const PF_ARM_PI: u32 = 0x2000_0000; +/// Absolute segment. +pub const PF_ARM_ABS: u32 = 0x4000_0000; + +// ARM values for `ProgramHeader*::p_type`. +/// ARM unwind segment. +pub const PT_ARM_EXIDX: u32 = PT_LOPROC + 1; + +// ARM values for `SectionHeader*::sh_type`. +/// ARM unwind section. +pub const SHT_ARM_EXIDX: u32 = SHT_LOPROC + 1; +/// Preemption details. +pub const SHT_ARM_PREEMPTMAP: u32 = SHT_LOPROC + 2; +/// ARM attributes section. +pub const SHT_ARM_ATTRIBUTES: u32 = SHT_LOPROC + 3; + +// AArch64 values for `SectionHeader*::sh_type`. +/// AArch64 attributes section. +pub const SHT_AARCH64_ATTRIBUTES: u32 = SHT_LOPROC + 3; + +// AArch64 values for `Rel*::r_type`. + +/// No relocation. +pub const R_AARCH64_NONE: u32 = 0; + +// ILP32 AArch64 relocs. +/// Direct 32 bit. +pub const R_AARCH64_P32_ABS32: u32 = 1; +/// Copy symbol at runtime. +pub const R_AARCH64_P32_COPY: u32 = 180; +/// Create GOT entry. +pub const R_AARCH64_P32_GLOB_DAT: u32 = 181; +/// Create PLT entry. +pub const R_AARCH64_P32_JUMP_SLOT: u32 = 182; +/// Adjust by program base. +pub const R_AARCH64_P32_RELATIVE: u32 = 183; +/// Module number, 32 bit. +pub const R_AARCH64_P32_TLS_DTPMOD: u32 = 184; +/// Module-relative offset, 32 bit. +pub const R_AARCH64_P32_TLS_DTPREL: u32 = 185; +/// TP-relative offset, 32 bit. +pub const R_AARCH64_P32_TLS_TPREL: u32 = 186; +/// TLS Descriptor. +pub const R_AARCH64_P32_TLSDESC: u32 = 187; +/// STT_GNU_IFUNC relocation. +pub const R_AARCH64_P32_IRELATIVE: u32 = 188; + +// LP64 AArch64 relocs. +/// Direct 64 bit. +pub const R_AARCH64_ABS64: u32 = 257; +/// Direct 32 bit. +pub const R_AARCH64_ABS32: u32 = 258; +/// Direct 16-bit. +pub const R_AARCH64_ABS16: u32 = 259; +/// PC-relative 64-bit. +pub const R_AARCH64_PREL64: u32 = 260; +/// PC-relative 32-bit. +pub const R_AARCH64_PREL32: u32 = 261; +/// PC-relative 16-bit. +pub const R_AARCH64_PREL16: u32 = 262; +/// Dir. MOVZ imm. from bits 15:0. +pub const R_AARCH64_MOVW_UABS_G0: u32 = 263; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_UABS_G0_NC: u32 = 264; +/// Dir. MOVZ imm. from bits 31:16. +pub const R_AARCH64_MOVW_UABS_G1: u32 = 265; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_UABS_G1_NC: u32 = 266; +/// Dir. MOVZ imm. from bits 47:32. +pub const R_AARCH64_MOVW_UABS_G2: u32 = 267; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_UABS_G2_NC: u32 = 268; +/// Dir. MOV{K,Z} imm. from 63:48. +pub const R_AARCH64_MOVW_UABS_G3: u32 = 269; +/// Dir. MOV{N,Z} imm. from 15:0. +pub const R_AARCH64_MOVW_SABS_G0: u32 = 270; +/// Dir. MOV{N,Z} imm. from 31:16. +pub const R_AARCH64_MOVW_SABS_G1: u32 = 271; +/// Dir. MOV{N,Z} imm. from 47:32. +pub const R_AARCH64_MOVW_SABS_G2: u32 = 272; +/// PC-rel. LD imm. from bits 20:2. +pub const R_AARCH64_LD_PREL_LO19: u32 = 273; +/// PC-rel. ADR imm. from bits 20:0. +pub const R_AARCH64_ADR_PREL_LO21: u32 = 274; +/// Page-rel. ADRP imm. from 32:12. +pub const R_AARCH64_ADR_PREL_PG_HI21: u32 = 275; +/// Likewise; no overflow check. +pub const R_AARCH64_ADR_PREL_PG_HI21_NC: u32 = 276; +/// Dir. ADD imm. from bits 11:0. +pub const R_AARCH64_ADD_ABS_LO12_NC: u32 = 277; +/// Likewise for LD/ST; no check. +pub const R_AARCH64_LDST8_ABS_LO12_NC: u32 = 278; +/// PC-rel. TBZ/TBNZ imm. from 15:2. +pub const R_AARCH64_TSTBR14: u32 = 279; +/// PC-rel. cond. br. imm. from 20:2. +pub const R_AARCH64_CONDBR19: u32 = 280; +/// PC-rel. B imm. from bits 27:2. +pub const R_AARCH64_JUMP26: u32 = 282; +/// Likewise for CALL. +pub const R_AARCH64_CALL26: u32 = 283; +/// Dir. ADD imm. from bits 11:1. +pub const R_AARCH64_LDST16_ABS_LO12_NC: u32 = 284; +/// Likewise for bits 11:2. +pub const R_AARCH64_LDST32_ABS_LO12_NC: u32 = 285; +/// Likewise for bits 11:3. +pub const R_AARCH64_LDST64_ABS_LO12_NC: u32 = 286; +/// PC-rel. MOV{N,Z} imm. from 15:0. +pub const R_AARCH64_MOVW_PREL_G0: u32 = 287; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_PREL_G0_NC: u32 = 288; +/// PC-rel. MOV{N,Z} imm. from 31:16. +pub const R_AARCH64_MOVW_PREL_G1: u32 = 289; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_PREL_G1_NC: u32 = 290; +/// PC-rel. MOV{N,Z} imm. from 47:32. +pub const R_AARCH64_MOVW_PREL_G2: u32 = 291; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_PREL_G2_NC: u32 = 292; +/// PC-rel. MOV{N,Z} imm. from 63:48. +pub const R_AARCH64_MOVW_PREL_G3: u32 = 293; +/// Dir. ADD imm. from bits 11:4. +pub const R_AARCH64_LDST128_ABS_LO12_NC: u32 = 299; +/// GOT-rel. off. MOV{N,Z} imm. 15:0. +pub const R_AARCH64_MOVW_GOTOFF_G0: u32 = 300; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_GOTOFF_G0_NC: u32 = 301; +/// GOT-rel. o. MOV{N,Z} imm. 31:16. +pub const R_AARCH64_MOVW_GOTOFF_G1: u32 = 302; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_GOTOFF_G1_NC: u32 = 303; +/// GOT-rel. o. MOV{N,Z} imm. 47:32. +pub const R_AARCH64_MOVW_GOTOFF_G2: u32 = 304; +/// Likewise for MOVK; no check. +pub const R_AARCH64_MOVW_GOTOFF_G2_NC: u32 = 305; +/// GOT-rel. o. MOV{N,Z} imm. 63:48. +pub const R_AARCH64_MOVW_GOTOFF_G3: u32 = 306; +/// GOT-relative 64-bit. +pub const R_AARCH64_GOTREL64: u32 = 307; +/// GOT-relative 32-bit. +pub const R_AARCH64_GOTREL32: u32 = 308; +/// PC-rel. GOT off. load imm. 20:2. +pub const R_AARCH64_GOT_LD_PREL19: u32 = 309; +/// GOT-rel. off. LD/ST imm. 14:3. +pub const R_AARCH64_LD64_GOTOFF_LO15: u32 = 310; +/// P-page-rel. GOT off. ADRP 32:12. +pub const R_AARCH64_ADR_GOT_PAGE: u32 = 311; +/// Dir. GOT off. LD/ST imm. 11:3. +pub const R_AARCH64_LD64_GOT_LO12_NC: u32 = 312; +/// GOT-page-rel. GOT off. LD/ST 14:3 +pub const R_AARCH64_LD64_GOTPAGE_LO15: u32 = 313; +/// PC-relative ADR imm. 20:0. +pub const R_AARCH64_TLSGD_ADR_PREL21: u32 = 512; +/// page-rel. ADRP imm. 32:12. +pub const R_AARCH64_TLSGD_ADR_PAGE21: u32 = 513; +/// direct ADD imm. from 11:0. +pub const R_AARCH64_TLSGD_ADD_LO12_NC: u32 = 514; +/// GOT-rel. MOV{N,Z} 31:16. +pub const R_AARCH64_TLSGD_MOVW_G1: u32 = 515; +/// GOT-rel. MOVK imm. 15:0. +pub const R_AARCH64_TLSGD_MOVW_G0_NC: u32 = 516; +/// Like 512; local dynamic model. +pub const R_AARCH64_TLSLD_ADR_PREL21: u32 = 517; +/// Like 513; local dynamic model. +pub const R_AARCH64_TLSLD_ADR_PAGE21: u32 = 518; +/// Like 514; local dynamic model. +pub const R_AARCH64_TLSLD_ADD_LO12_NC: u32 = 519; +/// Like 515; local dynamic model. +pub const R_AARCH64_TLSLD_MOVW_G1: u32 = 520; +/// Like 516; local dynamic model. +pub const R_AARCH64_TLSLD_MOVW_G0_NC: u32 = 521; +/// TLS PC-rel. load imm. 20:2. +pub const R_AARCH64_TLSLD_LD_PREL19: u32 = 522; +/// TLS DTP-rel. MOV{N,Z} 47:32. +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G2: u32 = 523; +/// TLS DTP-rel. MOV{N,Z} 31:16. +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G1: u32 = 524; +/// Likewise; MOVK; no check. +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC: u32 = 525; +/// TLS DTP-rel. MOV{N,Z} 15:0. +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G0: u32 = 526; +/// Likewise; MOVK; no check. +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: u32 = 527; +/// DTP-rel. ADD imm. from 23:12. +pub const R_AARCH64_TLSLD_ADD_DTPREL_HI12: u32 = 528; +/// DTP-rel. ADD imm. from 11:0. +pub const R_AARCH64_TLSLD_ADD_DTPREL_LO12: u32 = 529; +/// Likewise; no ovfl. check. +pub const R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC: u32 = 530; +/// DTP-rel. LD/ST imm. 11:0. +pub const R_AARCH64_TLSLD_LDST8_DTPREL_LO12: u32 = 531; +/// Likewise; no check. +pub const R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC: u32 = 532; +/// DTP-rel. LD/ST imm. 11:1. +pub const R_AARCH64_TLSLD_LDST16_DTPREL_LO12: u32 = 533; +/// Likewise; no check. +pub const R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC: u32 = 534; +/// DTP-rel. LD/ST imm. 11:2. +pub const R_AARCH64_TLSLD_LDST32_DTPREL_LO12: u32 = 535; +/// Likewise; no check. +pub const R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC: u32 = 536; +/// DTP-rel. LD/ST imm. 11:3. +pub const R_AARCH64_TLSLD_LDST64_DTPREL_LO12: u32 = 537; +/// Likewise; no check. +pub const R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC: u32 = 538; +/// GOT-rel. MOV{N,Z} 31:16. +pub const R_AARCH64_TLSIE_MOVW_GOTTPREL_G1: u32 = 539; +/// GOT-rel. MOVK 15:0. +pub const R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC: u32 = 540; +/// Page-rel. ADRP 32:12. +pub const R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: u32 = 541; +/// Direct LD off. 11:3. +pub const R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: u32 = 542; +/// PC-rel. load imm. 20:2. +pub const R_AARCH64_TLSIE_LD_GOTTPREL_PREL19: u32 = 543; +/// TLS TP-rel. MOV{N,Z} 47:32. +pub const R_AARCH64_TLSLE_MOVW_TPREL_G2: u32 = 544; +/// TLS TP-rel. MOV{N,Z} 31:16. +pub const R_AARCH64_TLSLE_MOVW_TPREL_G1: u32 = 545; +/// Likewise; MOVK; no check. +pub const R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: u32 = 546; +/// TLS TP-rel. MOV{N,Z} 15:0. +pub const R_AARCH64_TLSLE_MOVW_TPREL_G0: u32 = 547; +/// Likewise; MOVK; no check. +pub const R_AARCH64_TLSLE_MOVW_TPREL_G0_NC: u32 = 548; +/// TP-rel. ADD imm. 23:12. +pub const R_AARCH64_TLSLE_ADD_TPREL_HI12: u32 = 549; +/// TP-rel. ADD imm. 11:0. +pub const R_AARCH64_TLSLE_ADD_TPREL_LO12: u32 = 550; +/// Likewise; no ovfl. check. +pub const R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: u32 = 551; +/// TP-rel. LD/ST off. 11:0. +pub const R_AARCH64_TLSLE_LDST8_TPREL_LO12: u32 = 552; +/// Likewise; no ovfl. check. +pub const R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: u32 = 553; +/// TP-rel. LD/ST off. 11:1. +pub const R_AARCH64_TLSLE_LDST16_TPREL_LO12: u32 = 554; +/// Likewise; no check. +pub const R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: u32 = 555; +/// TP-rel. LD/ST off. 11:2. +pub const R_AARCH64_TLSLE_LDST32_TPREL_LO12: u32 = 556; +/// Likewise; no check. +pub const R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: u32 = 557; +/// TP-rel. LD/ST off. 11:3. +pub const R_AARCH64_TLSLE_LDST64_TPREL_LO12: u32 = 558; +/// Likewise; no check. +pub const R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: u32 = 559; +/// PC-rel. load immediate 20:2. +pub const R_AARCH64_TLSDESC_LD_PREL19: u32 = 560; +/// PC-rel. ADR immediate 20:0. +pub const R_AARCH64_TLSDESC_ADR_PREL21: u32 = 561; +/// Page-rel. ADRP imm. 32:12. +pub const R_AARCH64_TLSDESC_ADR_PAGE21: u32 = 562; +/// Direct LD off. from 11:3. +pub const R_AARCH64_TLSDESC_LD64_LO12: u32 = 563; +/// Direct ADD imm. from 11:0. +pub const R_AARCH64_TLSDESC_ADD_LO12: u32 = 564; +/// GOT-rel. MOV{N,Z} imm. 31:16. +pub const R_AARCH64_TLSDESC_OFF_G1: u32 = 565; +/// GOT-rel. MOVK imm. 15:0; no ck. +pub const R_AARCH64_TLSDESC_OFF_G0_NC: u32 = 566; +/// Relax LDR. +pub const R_AARCH64_TLSDESC_LDR: u32 = 567; +/// Relax ADD. +pub const R_AARCH64_TLSDESC_ADD: u32 = 568; +/// Relax BLR. +pub const R_AARCH64_TLSDESC_CALL: u32 = 569; +/// TP-rel. LD/ST off. 11:4. +pub const R_AARCH64_TLSLE_LDST128_TPREL_LO12: u32 = 570; +/// Likewise; no check. +pub const R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: u32 = 571; +/// DTP-rel. LD/ST imm. 11:4. +pub const R_AARCH64_TLSLD_LDST128_DTPREL_LO12: u32 = 572; +/// Likewise; no check. +pub const R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC: u32 = 573; +/// Copy symbol at runtime. +pub const R_AARCH64_COPY: u32 = 1024; +/// Create GOT entry. +pub const R_AARCH64_GLOB_DAT: u32 = 1025; +/// Create PLT entry. +pub const R_AARCH64_JUMP_SLOT: u32 = 1026; +/// Adjust by program base. +pub const R_AARCH64_RELATIVE: u32 = 1027; +/// Module number, 64 bit. +pub const R_AARCH64_TLS_DTPMOD: u32 = 1028; +/// Module-relative offset, 64 bit. +pub const R_AARCH64_TLS_DTPREL: u32 = 1029; +/// TP-relative offset, 64 bit. +pub const R_AARCH64_TLS_TPREL: u32 = 1030; +/// TLS Descriptor. +pub const R_AARCH64_TLSDESC: u32 = 1031; +/// STT_GNU_IFUNC relocation. +pub const R_AARCH64_IRELATIVE: u32 = 1032; + +// AVR values for `FileHeader*::e_flags`. + +/// Bitmask for `EF_AVR_ARCH_*`. +pub const EF_AVR_ARCH: u32 = 0x7F; + +/// If set, it is assumed that the elf file uses local symbols as reference +/// for the relocations so that linker relaxation is possible. +pub const EF_AVR_LINKRELAX_PREPARED: u32 = 0x80; + +pub const EF_AVR_ARCH_AVR1: u32 = 1; +pub const EF_AVR_ARCH_AVR2: u32 = 2; +pub const EF_AVR_ARCH_AVR25: u32 = 25; +pub const EF_AVR_ARCH_AVR3: u32 = 3; +pub const EF_AVR_ARCH_AVR31: u32 = 31; +pub const EF_AVR_ARCH_AVR35: u32 = 35; +pub const EF_AVR_ARCH_AVR4: u32 = 4; +pub const EF_AVR_ARCH_AVR5: u32 = 5; +pub const EF_AVR_ARCH_AVR51: u32 = 51; +pub const EF_AVR_ARCH_AVR6: u32 = 6; +pub const EF_AVR_ARCH_AVRTINY: u32 = 100; +pub const EF_AVR_ARCH_XMEGA1: u32 = 101; +pub const EF_AVR_ARCH_XMEGA2: u32 = 102; +pub const EF_AVR_ARCH_XMEGA3: u32 = 103; +pub const EF_AVR_ARCH_XMEGA4: u32 = 104; +pub const EF_AVR_ARCH_XMEGA5: u32 = 105; +pub const EF_AVR_ARCH_XMEGA6: u32 = 106; +pub const EF_AVR_ARCH_XMEGA7: u32 = 107; + +// AVR values for `Rel*::r_type`. + +pub const R_AVR_NONE: u32 = 0; +/// Direct 32 bit +pub const R_AVR_32: u32 = 1; +pub const R_AVR_7_PCREL: u32 = 2; +pub const R_AVR_13_PCREL: u32 = 3; +/// Direct 16 bit +pub const R_AVR_16: u32 = 4; +pub const R_AVR_16_PM: u32 = 5; +pub const R_AVR_LO8_LDI: u32 = 6; +pub const R_AVR_HI8_LDI: u32 = 7; +pub const R_AVR_HH8_LDI: u32 = 8; +pub const R_AVR_LO8_LDI_NEG: u32 = 9; +pub const R_AVR_HI8_LDI_NEG: u32 = 10; +pub const R_AVR_HH8_LDI_NEG: u32 = 11; +pub const R_AVR_LO8_LDI_PM: u32 = 12; +pub const R_AVR_HI8_LDI_PM: u32 = 13; +pub const R_AVR_HH8_LDI_PM: u32 = 14; +pub const R_AVR_LO8_LDI_PM_NEG: u32 = 15; +pub const R_AVR_HI8_LDI_PM_NEG: u32 = 16; +pub const R_AVR_HH8_LDI_PM_NEG: u32 = 17; +pub const R_AVR_CALL: u32 = 18; +pub const R_AVR_LDI: u32 = 19; +pub const R_AVR_6: u32 = 20; +pub const R_AVR_6_ADIW: u32 = 21; +pub const R_AVR_MS8_LDI: u32 = 22; +pub const R_AVR_MS8_LDI_NEG: u32 = 23; +pub const R_AVR_LO8_LDI_GS: u32 = 24; +pub const R_AVR_HI8_LDI_GS: u32 = 25; +pub const R_AVR_8: u32 = 26; +pub const R_AVR_8_LO8: u32 = 27; +pub const R_AVR_8_HI8: u32 = 28; +pub const R_AVR_8_HLO8: u32 = 29; +pub const R_AVR_DIFF8: u32 = 30; +pub const R_AVR_DIFF16: u32 = 31; +pub const R_AVR_DIFF32: u32 = 32; +pub const R_AVR_LDS_STS_16: u32 = 33; +pub const R_AVR_PORT6: u32 = 34; +pub const R_AVR_PORT5: u32 = 35; +pub const R_AVR_32_PCREL: u32 = 36; + +// MSP430 values for `Rel*::r_type`. + +/// Direct 32 bit +pub const R_MSP430_32: u32 = 1; +/// Direct 16 bit +pub const R_MSP430_16_BYTE: u32 = 5; + +// Hexagon values for `Rel*::r_type`. + +/// Direct 32 bit +pub const R_HEX_32: u32 = 6; + +// ARM values for `Rel*::r_type`. + +/// No reloc +pub const R_ARM_NONE: u32 = 0; +/// Deprecated PC relative 26 bit branch. +pub const R_ARM_PC24: u32 = 1; +/// Direct 32 bit +pub const R_ARM_ABS32: u32 = 2; +/// PC relative 32 bit +pub const R_ARM_REL32: u32 = 3; +pub const R_ARM_PC13: u32 = 4; +/// Direct 16 bit +pub const R_ARM_ABS16: u32 = 5; +/// Direct 12 bit +pub const R_ARM_ABS12: u32 = 6; +/// Direct & 0x7C (`LDR`, `STR`). +pub const R_ARM_THM_ABS5: u32 = 7; +/// Direct 8 bit +pub const R_ARM_ABS8: u32 = 8; +pub const R_ARM_SBREL32: u32 = 9; +/// PC relative 24 bit (Thumb32 `BL`). +pub const R_ARM_THM_PC22: u32 = 10; +/// PC relative & 0x3FC (Thumb16 `LDR`, `ADD`, `ADR`). +pub const R_ARM_THM_PC8: u32 = 11; +pub const R_ARM_AMP_VCALL9: u32 = 12; +/// Obsolete static relocation. +pub const R_ARM_SWI24: u32 = 13; +/// Dynamic relocation. +pub const R_ARM_TLS_DESC: u32 = 13; +/// Reserved. +pub const R_ARM_THM_SWI8: u32 = 14; +/// Reserved. +pub const R_ARM_XPC25: u32 = 15; +/// Reserved. +pub const R_ARM_THM_XPC22: u32 = 16; +/// ID of module containing symbol +pub const R_ARM_TLS_DTPMOD32: u32 = 17; +/// Offset in TLS block +pub const R_ARM_TLS_DTPOFF32: u32 = 18; +/// Offset in static TLS block +pub const R_ARM_TLS_TPOFF32: u32 = 19; +/// Copy symbol at runtime +pub const R_ARM_COPY: u32 = 20; +/// Create GOT entry +pub const R_ARM_GLOB_DAT: u32 = 21; +/// Create PLT entry +pub const R_ARM_JUMP_SLOT: u32 = 22; +/// Adjust by program base +pub const R_ARM_RELATIVE: u32 = 23; +/// 32 bit offset to GOT +pub const R_ARM_GOTOFF: u32 = 24; +/// 32 bit PC relative offset to GOT +pub const R_ARM_GOTPC: u32 = 25; +/// 32 bit GOT entry +pub const R_ARM_GOT32: u32 = 26; +/// Deprecated, 32 bit PLT address. +pub const R_ARM_PLT32: u32 = 27; +/// PC relative 24 bit (`BL`, `BLX`). +pub const R_ARM_CALL: u32 = 28; +/// PC relative 24 bit (`B`, `BL<cond>`). +pub const R_ARM_JUMP24: u32 = 29; +/// PC relative 24 bit (Thumb32 `B.W`). +pub const R_ARM_THM_JUMP24: u32 = 30; +/// Adjust by program base. +pub const R_ARM_BASE_ABS: u32 = 31; +/// Obsolete. +pub const R_ARM_ALU_PCREL_7_0: u32 = 32; +/// Obsolete. +pub const R_ARM_ALU_PCREL_15_8: u32 = 33; +/// Obsolete. +pub const R_ARM_ALU_PCREL_23_15: u32 = 34; +/// Deprecated, prog. base relative. +pub const R_ARM_LDR_SBREL_11_0: u32 = 35; +/// Deprecated, prog. base relative. +pub const R_ARM_ALU_SBREL_19_12: u32 = 36; +/// Deprecated, prog. base relative. +pub const R_ARM_ALU_SBREL_27_20: u32 = 37; +pub const R_ARM_TARGET1: u32 = 38; +/// Program base relative. +pub const R_ARM_SBREL31: u32 = 39; +pub const R_ARM_V4BX: u32 = 40; +pub const R_ARM_TARGET2: u32 = 41; +/// 32 bit PC relative. +pub const R_ARM_PREL31: u32 = 42; +/// Direct 16-bit (`MOVW`). +pub const R_ARM_MOVW_ABS_NC: u32 = 43; +/// Direct high 16-bit (`MOVT`). +pub const R_ARM_MOVT_ABS: u32 = 44; +/// PC relative 16-bit (`MOVW`). +pub const R_ARM_MOVW_PREL_NC: u32 = 45; +/// PC relative (MOVT). +pub const R_ARM_MOVT_PREL: u32 = 46; +/// Direct 16 bit (Thumb32 `MOVW`). +pub const R_ARM_THM_MOVW_ABS_NC: u32 = 47; +/// Direct high 16 bit (Thumb32 `MOVT`). +pub const R_ARM_THM_MOVT_ABS: u32 = 48; +/// PC relative 16 bit (Thumb32 `MOVW`). +pub const R_ARM_THM_MOVW_PREL_NC: u32 = 49; +/// PC relative high 16 bit (Thumb32 `MOVT`). +pub const R_ARM_THM_MOVT_PREL: u32 = 50; +/// PC relative 20 bit (Thumb32 `B<cond>.W`). +pub const R_ARM_THM_JUMP19: u32 = 51; +/// PC relative X & 0x7E (Thumb16 `CBZ`, `CBNZ`). +pub const R_ARM_THM_JUMP6: u32 = 52; +/// PC relative 12 bit (Thumb32 `ADR.W`). +pub const R_ARM_THM_ALU_PREL_11_0: u32 = 53; +/// PC relative 12 bit (Thumb32 `LDR{D,SB,H,SH}`). +pub const R_ARM_THM_PC12: u32 = 54; +/// Direct 32-bit. +pub const R_ARM_ABS32_NOI: u32 = 55; +/// PC relative 32-bit. +pub const R_ARM_REL32_NOI: u32 = 56; +/// PC relative (`ADD`, `SUB`). +pub const R_ARM_ALU_PC_G0_NC: u32 = 57; +/// PC relative (`ADD`, `SUB`). +pub const R_ARM_ALU_PC_G0: u32 = 58; +/// PC relative (`ADD`, `SUB`). +pub const R_ARM_ALU_PC_G1_NC: u32 = 59; +/// PC relative (`ADD`, `SUB`). +pub const R_ARM_ALU_PC_G1: u32 = 60; +/// PC relative (`ADD`, `SUB`). +pub const R_ARM_ALU_PC_G2: u32 = 61; +/// PC relative (`LDR`,`STR`,`LDRB`,`STRB`). +pub const R_ARM_LDR_PC_G1: u32 = 62; +/// PC relative (`LDR`,`STR`,`LDRB`,`STRB`). +pub const R_ARM_LDR_PC_G2: u32 = 63; +/// PC relative (`STR{D,H}`, `LDR{D,SB,H,SH}`). +pub const R_ARM_LDRS_PC_G0: u32 = 64; +/// PC relative (`STR{D,H}`, `LDR{D,SB,H,SH}`). +pub const R_ARM_LDRS_PC_G1: u32 = 65; +/// PC relative (`STR{D,H}`, `LDR{D,SB,H,SH}`). +pub const R_ARM_LDRS_PC_G2: u32 = 66; +/// PC relative (`LDC`, `STC`). +pub const R_ARM_LDC_PC_G0: u32 = 67; +/// PC relative (`LDC`, `STC`). +pub const R_ARM_LDC_PC_G1: u32 = 68; +/// PC relative (`LDC`, `STC`). +pub const R_ARM_LDC_PC_G2: u32 = 69; +/// Program base relative (`ADD`,`SUB`). +pub const R_ARM_ALU_SB_G0_NC: u32 = 70; +/// Program base relative (`ADD`,`SUB`). +pub const R_ARM_ALU_SB_G0: u32 = 71; +/// Program base relative (`ADD`,`SUB`). +pub const R_ARM_ALU_SB_G1_NC: u32 = 72; +/// Program base relative (`ADD`,`SUB`). +pub const R_ARM_ALU_SB_G1: u32 = 73; +/// Program base relative (`ADD`,`SUB`). +pub const R_ARM_ALU_SB_G2: u32 = 74; +/// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). +pub const R_ARM_LDR_SB_G0: u32 = 75; +/// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). +pub const R_ARM_LDR_SB_G1: u32 = 76; +/// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). +pub const R_ARM_LDR_SB_G2: u32 = 77; +/// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). +pub const R_ARM_LDRS_SB_G0: u32 = 78; +/// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). +pub const R_ARM_LDRS_SB_G1: u32 = 79; +/// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). +pub const R_ARM_LDRS_SB_G2: u32 = 80; +/// Program base relative (`LDC`,`STC`). +pub const R_ARM_LDC_SB_G0: u32 = 81; +/// Program base relative (`LDC`,`STC`). +pub const R_ARM_LDC_SB_G1: u32 = 82; +/// Program base relative (`LDC`,`STC`). +pub const R_ARM_LDC_SB_G2: u32 = 83; +/// Program base relative 16 bit (`MOVW`). +pub const R_ARM_MOVW_BREL_NC: u32 = 84; +/// Program base relative high 16 bit (`MOVT`). +pub const R_ARM_MOVT_BREL: u32 = 85; +/// Program base relative 16 bit (`MOVW`). +pub const R_ARM_MOVW_BREL: u32 = 86; +/// Program base relative 16 bit (Thumb32 `MOVW`). +pub const R_ARM_THM_MOVW_BREL_NC: u32 = 87; +/// Program base relative high 16 bit (Thumb32 `MOVT`). +pub const R_ARM_THM_MOVT_BREL: u32 = 88; +/// Program base relative 16 bit (Thumb32 `MOVW`). +pub const R_ARM_THM_MOVW_BREL: u32 = 89; +pub const R_ARM_TLS_GOTDESC: u32 = 90; +pub const R_ARM_TLS_CALL: u32 = 91; +/// TLS relaxation. +pub const R_ARM_TLS_DESCSEQ: u32 = 92; +pub const R_ARM_THM_TLS_CALL: u32 = 93; +pub const R_ARM_PLT32_ABS: u32 = 94; +/// GOT entry. +pub const R_ARM_GOT_ABS: u32 = 95; +/// PC relative GOT entry. +pub const R_ARM_GOT_PREL: u32 = 96; +/// GOT entry relative to GOT origin (`LDR`). +pub const R_ARM_GOT_BREL12: u32 = 97; +/// 12 bit, GOT entry relative to GOT origin (`LDR`, `STR`). +pub const R_ARM_GOTOFF12: u32 = 98; +pub const R_ARM_GOTRELAX: u32 = 99; +pub const R_ARM_GNU_VTENTRY: u32 = 100; +pub const R_ARM_GNU_VTINHERIT: u32 = 101; +/// PC relative & 0xFFE (Thumb16 `B`). +pub const R_ARM_THM_PC11: u32 = 102; +/// PC relative & 0x1FE (Thumb16 `B`/`B<cond>`). +pub const R_ARM_THM_PC9: u32 = 103; +/// PC-rel 32 bit for global dynamic thread local data +pub const R_ARM_TLS_GD32: u32 = 104; +/// PC-rel 32 bit for local dynamic thread local data +pub const R_ARM_TLS_LDM32: u32 = 105; +/// 32 bit offset relative to TLS block +pub const R_ARM_TLS_LDO32: u32 = 106; +/// PC-rel 32 bit for GOT entry of static TLS block offset +pub const R_ARM_TLS_IE32: u32 = 107; +/// 32 bit offset relative to static TLS block +pub const R_ARM_TLS_LE32: u32 = 108; +/// 12 bit relative to TLS block (`LDR`, `STR`). +pub const R_ARM_TLS_LDO12: u32 = 109; +/// 12 bit relative to static TLS block (`LDR`, `STR`). +pub const R_ARM_TLS_LE12: u32 = 110; +/// 12 bit GOT entry relative to GOT origin (`LDR`). +pub const R_ARM_TLS_IE12GP: u32 = 111; +/// Obsolete. +pub const R_ARM_ME_TOO: u32 = 128; +pub const R_ARM_THM_TLS_DESCSEQ: u32 = 129; +pub const R_ARM_THM_TLS_DESCSEQ16: u32 = 129; +pub const R_ARM_THM_TLS_DESCSEQ32: u32 = 130; +/// GOT entry relative to GOT origin, 12 bit (Thumb32 `LDR`). +pub const R_ARM_THM_GOT_BREL12: u32 = 131; +pub const R_ARM_IRELATIVE: u32 = 160; +pub const R_ARM_RXPC25: u32 = 249; +pub const R_ARM_RSBREL32: u32 = 250; +pub const R_ARM_THM_RPC22: u32 = 251; +pub const R_ARM_RREL32: u32 = 252; +pub const R_ARM_RABS22: u32 = 253; +pub const R_ARM_RPC24: u32 = 254; +pub const R_ARM_RBASE: u32 = 255; + +// C-SKY values for `Rel*::r_type`. +/// no reloc +pub const R_CKCORE_NONE: u32 = 0; +/// direct 32 bit (S + A) +pub const R_CKCORE_ADDR32: u32 = 1; +/// disp ((S + A - P) >> 2) & 0xff +pub const R_CKCORE_PCRELIMM8BY4: u32 = 2; +/// disp ((S + A - P) >> 1) & 0x7ff +pub const R_CKCORE_PCRELIMM11BY2: u32 = 3; +/// 32-bit rel (S + A - P) +pub const R_CKCORE_PCREL32: u32 = 5; +/// disp ((S + A - P) >>1) & 0x7ff +pub const R_CKCORE_PCRELJSR_IMM11BY2: u32 = 6; +/// 32 bit adjust program base(B + A) +pub const R_CKCORE_RELATIVE: u32 = 9; +/// 32 bit adjust by program base +pub const R_CKCORE_COPY: u32 = 10; +/// off between got and sym (S) +pub const R_CKCORE_GLOB_DAT: u32 = 11; +/// PLT entry (S) +pub const R_CKCORE_JUMP_SLOT: u32 = 12; +/// offset to GOT (S + A - GOT) +pub const R_CKCORE_GOTOFF: u32 = 13; +/// PC offset to GOT (GOT + A - P) +pub const R_CKCORE_GOTPC: u32 = 14; +/// 32 bit GOT entry (G) +pub const R_CKCORE_GOT32: u32 = 15; +/// 32 bit PLT entry (G) +pub const R_CKCORE_PLT32: u32 = 16; +/// GOT entry in GLOB_DAT (GOT + G) +pub const R_CKCORE_ADDRGOT: u32 = 17; +/// PLT entry in GLOB_DAT (GOT + G) +pub const R_CKCORE_ADDRPLT: u32 = 18; +/// ((S + A - P) >> 1) & 0x3ff_ffff +pub const R_CKCORE_PCREL_IMM26BY2: u32 = 19; +/// disp ((S + A - P) >> 1) & 0xffff +pub const R_CKCORE_PCREL_IMM16BY2: u32 = 20; +/// disp ((S + A - P) >> 2) & 0xffff +pub const R_CKCORE_PCREL_IMM16BY4: u32 = 21; +/// disp ((S + A - P) >> 1) & 0x3ff +pub const R_CKCORE_PCREL_IMM10BY2: u32 = 22; +/// disp ((S + A - P) >> 2) & 0x3ff +pub const R_CKCORE_PCREL_IMM10BY4: u32 = 23; +/// high & low 16 bit ADDR, ((S + A) >> 16) & 0xffff +pub const R_CKCORE_ADDR_HI16: u32 = 24; +/// (S + A) & 0xffff +pub const R_CKCORE_ADDR_LO16: u32 = 25; +/// high & low 16 bit GOTPC, ((GOT + A - P) >> 16) & 0xffff +pub const R_CKCORE_GOTPC_HI16: u32 = 26; +/// (GOT + A - P) & 0xffff +pub const R_CKCORE_GOTPC_LO16: u32 = 27; +/// high & low 16 bit GOTOFF, ((S + A - GOT) >> 16) & 0xffff +pub const R_CKCORE_GOTOFF_HI16: u32 = 28; +/// (S + A - GOT) & 0xffff +pub const R_CKCORE_GOTOFF_LO16: u32 = 29; +/// 12 bit disp GOT entry (G) +pub const R_CKCORE_GOT12: u32 = 30; +/// high & low 16 bit GOT, (G >> 16) & 0xffff +pub const R_CKCORE_GOT_HI16: u32 = 31; +/// (G & 0xffff) +pub const R_CKCORE_GOT_LO16: u32 = 32; +/// 12 bit disp PLT entry (G) +pub const R_CKCORE_PLT12: u32 = 33; +/// high & low 16 bit PLT, (G >> 16) & 0xffff +pub const R_CKCORE_PLT_HI16: u32 = 34; +/// G & 0xffff +pub const R_CKCORE_PLT_LO16: u32 = 35; +/// high & low 16 bit ADDRGOT, (GOT + G * 4) & 0xffff +pub const R_CKCORE_ADDRGOT_HI16: u32 = 36; +/// (GOT + G * 4) & 0xffff +pub const R_CKCORE_ADDRGOT_LO16: u32 = 37; +/// high & low 16 bit ADDRPLT, ((GOT + G * 4) >> 16) & 0xFFFF +pub const R_CKCORE_ADDRPLT_HI16: u32 = 38; +/// (GOT+G*4) & 0xffff +pub const R_CKCORE_ADDRPLT_LO16: u32 = 39; +/// disp ((S+A-P) >>1) & x3ff_ffff +pub const R_CKCORE_PCREL_JSR_IMM26BY2: u32 = 40; +/// (S+A-BTEXT) & 0xffff +pub const R_CKCORE_TOFFSET_LO16: u32 = 41; +/// (S+A-BTEXT) & 0xffff +pub const R_CKCORE_DOFFSET_LO16: u32 = 42; +/// disp ((S+A-P) >>1) & 0x3ffff +pub const R_CKCORE_PCREL_IMM18BY2: u32 = 43; +/// disp (S+A-BDATA) & 0x3ffff +pub const R_CKCORE_DOFFSET_IMM18: u32 = 44; +/// disp ((S+A-BDATA)>>1) & 0x3ffff +pub const R_CKCORE_DOFFSET_IMM18BY2: u32 = 45; +/// disp ((S+A-BDATA)>>2) & 0x3ffff +pub const R_CKCORE_DOFFSET_IMM18BY4: u32 = 46; +/// disp (G >> 2) +pub const R_CKCORE_GOT_IMM18BY4: u32 = 48; +/// disp (G >> 2) +pub const R_CKCORE_PLT_IMM18BY4: u32 = 49; +/// disp ((S+A-P) >>2) & 0x7f +pub const R_CKCORE_PCREL_IMM7BY4: u32 = 50; +/// 32 bit offset to TLS block +pub const R_CKCORE_TLS_LE32: u32 = 51; +pub const R_CKCORE_TLS_IE32: u32 = 52; +pub const R_CKCORE_TLS_GD32: u32 = 53; +pub const R_CKCORE_TLS_LDM32: u32 = 54; +pub const R_CKCORE_TLS_LDO32: u32 = 55; +pub const R_CKCORE_TLS_DTPMOD32: u32 = 56; +pub const R_CKCORE_TLS_DTPOFF32: u32 = 57; +pub const R_CKCORE_TLS_TPOFF32: u32 = 58; + +// C-SKY values for `FileHeader*::e_flags`. +pub const EF_CSKY_ABIMASK: u32 = 0xF000_0000; +pub const EF_CSKY_OTHER: u32 = 0x0FFF_0000; +pub const EF_CSKY_PROCESSOR: u32 = 0x0000_FFFF; + +pub const EF_CSKY_ABIV1: u32 = 0x1000_0000; +pub const EF_CSKY_ABIV2: u32 = 0x2000_0000; + +// C-SKY values for `SectionHeader*::sh_type`. +/// C-SKY attributes section. +pub const SHT_CSKY_ATTRIBUTES: u32 = SHT_LOPROC + 1; + +// IA-64 specific declarations. + +// IA-64 values for `FileHeader64::e_flags`. +/// os-specific flags +pub const EF_IA_64_MASKOS: u32 = 0x0000_000f; +/// 64-bit ABI +pub const EF_IA_64_ABI64: u32 = 0x0000_0010; +/// arch. version mask +pub const EF_IA_64_ARCH: u32 = 0xff00_0000; + +// IA-64 values for `ProgramHeader64::p_type`. +/// arch extension bits +pub const PT_IA_64_ARCHEXT: u32 = PT_LOPROC + 0; +/// ia64 unwind bits +pub const PT_IA_64_UNWIND: u32 = PT_LOPROC + 1; +pub const PT_IA_64_HP_OPT_ANOT: u32 = PT_LOOS + 0x12; +pub const PT_IA_64_HP_HSL_ANOT: u32 = PT_LOOS + 0x13; +pub const PT_IA_64_HP_STACK: u32 = PT_LOOS + 0x14; + +// IA-64 values for `ProgramHeader64::p_flags`. +/// spec insns w/o recovery +pub const PF_IA_64_NORECOV: u32 = 0x8000_0000; + +// IA-64 values for `SectionHeader64::sh_type`. +/// extension bits +pub const SHT_IA_64_EXT: u32 = SHT_LOPROC + 0; +/// unwind bits +pub const SHT_IA_64_UNWIND: u32 = SHT_LOPROC + 1; + +// IA-64 values for `SectionHeader64::sh_flags`. +/// section near gp +pub const SHF_IA_64_SHORT: u32 = 0x1000_0000; +/// spec insns w/o recovery +pub const SHF_IA_64_NORECOV: u32 = 0x2000_0000; + +// IA-64 values for `Dyn64::d_tag`. +pub const DT_IA_64_PLT_RESERVE: u32 = DT_LOPROC + 0; + +// IA-64 values for `Rel*::r_type`. +/// none +pub const R_IA64_NONE: u32 = 0x00; +/// symbol + addend, add imm14 +pub const R_IA64_IMM14: u32 = 0x21; +/// symbol + addend, add imm22 +pub const R_IA64_IMM22: u32 = 0x22; +/// symbol + addend, mov imm64 +pub const R_IA64_IMM64: u32 = 0x23; +/// symbol + addend, data4 MSB +pub const R_IA64_DIR32MSB: u32 = 0x24; +/// symbol + addend, data4 LSB +pub const R_IA64_DIR32LSB: u32 = 0x25; +/// symbol + addend, data8 MSB +pub const R_IA64_DIR64MSB: u32 = 0x26; +/// symbol + addend, data8 LSB +pub const R_IA64_DIR64LSB: u32 = 0x27; +/// @gprel(sym + add), add imm22 +pub const R_IA64_GPREL22: u32 = 0x2a; +/// @gprel(sym + add), mov imm64 +pub const R_IA64_GPREL64I: u32 = 0x2b; +/// @gprel(sym + add), data4 MSB +pub const R_IA64_GPREL32MSB: u32 = 0x2c; +/// @gprel(sym + add), data4 LSB +pub const R_IA64_GPREL32LSB: u32 = 0x2d; +/// @gprel(sym + add), data8 MSB +pub const R_IA64_GPREL64MSB: u32 = 0x2e; +/// @gprel(sym + add), data8 LSB +pub const R_IA64_GPREL64LSB: u32 = 0x2f; +/// @ltoff(sym + add), add imm22 +pub const R_IA64_LTOFF22: u32 = 0x32; +/// @ltoff(sym + add), mov imm64 +pub const R_IA64_LTOFF64I: u32 = 0x33; +/// @pltoff(sym + add), add imm22 +pub const R_IA64_PLTOFF22: u32 = 0x3a; +/// @pltoff(sym + add), mov imm64 +pub const R_IA64_PLTOFF64I: u32 = 0x3b; +/// @pltoff(sym + add), data8 MSB +pub const R_IA64_PLTOFF64MSB: u32 = 0x3e; +/// @pltoff(sym + add), data8 LSB +pub const R_IA64_PLTOFF64LSB: u32 = 0x3f; +/// @fptr(sym + add), mov imm64 +pub const R_IA64_FPTR64I: u32 = 0x43; +/// @fptr(sym + add), data4 MSB +pub const R_IA64_FPTR32MSB: u32 = 0x44; +/// @fptr(sym + add), data4 LSB +pub const R_IA64_FPTR32LSB: u32 = 0x45; +/// @fptr(sym + add), data8 MSB +pub const R_IA64_FPTR64MSB: u32 = 0x46; +/// @fptr(sym + add), data8 LSB +pub const R_IA64_FPTR64LSB: u32 = 0x47; +/// @pcrel(sym + add), brl +pub const R_IA64_PCREL60B: u32 = 0x48; +/// @pcrel(sym + add), ptb, call +pub const R_IA64_PCREL21B: u32 = 0x49; +/// @pcrel(sym + add), chk.s +pub const R_IA64_PCREL21M: u32 = 0x4a; +/// @pcrel(sym + add), fchkf +pub const R_IA64_PCREL21F: u32 = 0x4b; +/// @pcrel(sym + add), data4 MSB +pub const R_IA64_PCREL32MSB: u32 = 0x4c; +/// @pcrel(sym + add), data4 LSB +pub const R_IA64_PCREL32LSB: u32 = 0x4d; +/// @pcrel(sym + add), data8 MSB +pub const R_IA64_PCREL64MSB: u32 = 0x4e; +/// @pcrel(sym + add), data8 LSB +pub const R_IA64_PCREL64LSB: u32 = 0x4f; +/// @ltoff(@fptr(s+a)), imm22 +pub const R_IA64_LTOFF_FPTR22: u32 = 0x52; +/// @ltoff(@fptr(s+a)), imm64 +pub const R_IA64_LTOFF_FPTR64I: u32 = 0x53; +/// @ltoff(@fptr(s+a)), data4 MSB +pub const R_IA64_LTOFF_FPTR32MSB: u32 = 0x54; +/// @ltoff(@fptr(s+a)), data4 LSB +pub const R_IA64_LTOFF_FPTR32LSB: u32 = 0x55; +/// @ltoff(@fptr(s+a)), data8 MSB +pub const R_IA64_LTOFF_FPTR64MSB: u32 = 0x56; +/// @ltoff(@fptr(s+a)), data8 LSB +pub const R_IA64_LTOFF_FPTR64LSB: u32 = 0x57; +/// @segrel(sym + add), data4 MSB +pub const R_IA64_SEGREL32MSB: u32 = 0x5c; +/// @segrel(sym + add), data4 LSB +pub const R_IA64_SEGREL32LSB: u32 = 0x5d; +/// @segrel(sym + add), data8 MSB +pub const R_IA64_SEGREL64MSB: u32 = 0x5e; +/// @segrel(sym + add), data8 LSB +pub const R_IA64_SEGREL64LSB: u32 = 0x5f; +/// @secrel(sym + add), data4 MSB +pub const R_IA64_SECREL32MSB: u32 = 0x64; +/// @secrel(sym + add), data4 LSB +pub const R_IA64_SECREL32LSB: u32 = 0x65; +/// @secrel(sym + add), data8 MSB +pub const R_IA64_SECREL64MSB: u32 = 0x66; +/// @secrel(sym + add), data8 LSB +pub const R_IA64_SECREL64LSB: u32 = 0x67; +/// data 4 + REL +pub const R_IA64_REL32MSB: u32 = 0x6c; +/// data 4 + REL +pub const R_IA64_REL32LSB: u32 = 0x6d; +/// data 8 + REL +pub const R_IA64_REL64MSB: u32 = 0x6e; +/// data 8 + REL +pub const R_IA64_REL64LSB: u32 = 0x6f; +/// symbol + addend, data4 MSB +pub const R_IA64_LTV32MSB: u32 = 0x74; +/// symbol + addend, data4 LSB +pub const R_IA64_LTV32LSB: u32 = 0x75; +/// symbol + addend, data8 MSB +pub const R_IA64_LTV64MSB: u32 = 0x76; +/// symbol + addend, data8 LSB +pub const R_IA64_LTV64LSB: u32 = 0x77; +/// @pcrel(sym + add), 21bit inst +pub const R_IA64_PCREL21BI: u32 = 0x79; +/// @pcrel(sym + add), 22bit inst +pub const R_IA64_PCREL22: u32 = 0x7a; +/// @pcrel(sym + add), 64bit inst +pub const R_IA64_PCREL64I: u32 = 0x7b; +/// dynamic reloc, imported PLT, MSB +pub const R_IA64_IPLTMSB: u32 = 0x80; +/// dynamic reloc, imported PLT, LSB +pub const R_IA64_IPLTLSB: u32 = 0x81; +/// copy relocation +pub const R_IA64_COPY: u32 = 0x84; +/// Addend and symbol difference +pub const R_IA64_SUB: u32 = 0x85; +/// LTOFF22, relaxable. +pub const R_IA64_LTOFF22X: u32 = 0x86; +/// Use of LTOFF22X. +pub const R_IA64_LDXMOV: u32 = 0x87; +/// @tprel(sym + add), imm14 +pub const R_IA64_TPREL14: u32 = 0x91; +/// @tprel(sym + add), imm22 +pub const R_IA64_TPREL22: u32 = 0x92; +/// @tprel(sym + add), imm64 +pub const R_IA64_TPREL64I: u32 = 0x93; +/// @tprel(sym + add), data8 MSB +pub const R_IA64_TPREL64MSB: u32 = 0x96; +/// @tprel(sym + add), data8 LSB +pub const R_IA64_TPREL64LSB: u32 = 0x97; +/// @ltoff(@tprel(s+a)), imm2 +pub const R_IA64_LTOFF_TPREL22: u32 = 0x9a; +/// @dtpmod(sym + add), data8 MSB +pub const R_IA64_DTPMOD64MSB: u32 = 0xa6; +/// @dtpmod(sym + add), data8 LSB +pub const R_IA64_DTPMOD64LSB: u32 = 0xa7; +/// @ltoff(@dtpmod(sym + add)), imm22 +pub const R_IA64_LTOFF_DTPMOD22: u32 = 0xaa; +/// @dtprel(sym + add), imm14 +pub const R_IA64_DTPREL14: u32 = 0xb1; +/// @dtprel(sym + add), imm22 +pub const R_IA64_DTPREL22: u32 = 0xb2; +/// @dtprel(sym + add), imm64 +pub const R_IA64_DTPREL64I: u32 = 0xb3; +/// @dtprel(sym + add), data4 MSB +pub const R_IA64_DTPREL32MSB: u32 = 0xb4; +/// @dtprel(sym + add), data4 LSB +pub const R_IA64_DTPREL32LSB: u32 = 0xb5; +/// @dtprel(sym + add), data8 MSB +pub const R_IA64_DTPREL64MSB: u32 = 0xb6; +/// @dtprel(sym + add), data8 LSB +pub const R_IA64_DTPREL64LSB: u32 = 0xb7; +/// @ltoff(@dtprel(s+a)), imm22 +pub const R_IA64_LTOFF_DTPREL22: u32 = 0xba; + +// SH specific declarations. + +// SH values `FileHeader*::e_flags`. +pub const EF_SH_MACH_MASK: u32 = 0x1f; +pub const EF_SH_UNKNOWN: u32 = 0x0; +pub const EF_SH1: u32 = 0x1; +pub const EF_SH2: u32 = 0x2; +pub const EF_SH3: u32 = 0x3; +pub const EF_SH_DSP: u32 = 0x4; +pub const EF_SH3_DSP: u32 = 0x5; +pub const EF_SH4AL_DSP: u32 = 0x6; +pub const EF_SH3E: u32 = 0x8; +pub const EF_SH4: u32 = 0x9; +pub const EF_SH2E: u32 = 0xb; +pub const EF_SH4A: u32 = 0xc; +pub const EF_SH2A: u32 = 0xd; +pub const EF_SH4_NOFPU: u32 = 0x10; +pub const EF_SH4A_NOFPU: u32 = 0x11; +pub const EF_SH4_NOMMU_NOFPU: u32 = 0x12; +pub const EF_SH2A_NOFPU: u32 = 0x13; +pub const EF_SH3_NOMMU: u32 = 0x14; +pub const EF_SH2A_SH4_NOFPU: u32 = 0x15; +pub const EF_SH2A_SH3_NOFPU: u32 = 0x16; +pub const EF_SH2A_SH4: u32 = 0x17; +pub const EF_SH2A_SH3E: u32 = 0x18; + +// SH values `Rel*::r_type`. +pub const R_SH_NONE: u32 = 0; +pub const R_SH_DIR32: u32 = 1; +pub const R_SH_REL32: u32 = 2; +pub const R_SH_DIR8WPN: u32 = 3; +pub const R_SH_IND12W: u32 = 4; +pub const R_SH_DIR8WPL: u32 = 5; +pub const R_SH_DIR8WPZ: u32 = 6; +pub const R_SH_DIR8BP: u32 = 7; +pub const R_SH_DIR8W: u32 = 8; +pub const R_SH_DIR8L: u32 = 9; +pub const R_SH_SWITCH16: u32 = 25; +pub const R_SH_SWITCH32: u32 = 26; +pub const R_SH_USES: u32 = 27; +pub const R_SH_COUNT: u32 = 28; +pub const R_SH_ALIGN: u32 = 29; +pub const R_SH_CODE: u32 = 30; +pub const R_SH_DATA: u32 = 31; +pub const R_SH_LABEL: u32 = 32; +pub const R_SH_SWITCH8: u32 = 33; +pub const R_SH_GNU_VTINHERIT: u32 = 34; +pub const R_SH_GNU_VTENTRY: u32 = 35; +pub const R_SH_TLS_GD_32: u32 = 144; +pub const R_SH_TLS_LD_32: u32 = 145; +pub const R_SH_TLS_LDO_32: u32 = 146; +pub const R_SH_TLS_IE_32: u32 = 147; +pub const R_SH_TLS_LE_32: u32 = 148; +pub const R_SH_TLS_DTPMOD32: u32 = 149; +pub const R_SH_TLS_DTPOFF32: u32 = 150; +pub const R_SH_TLS_TPOFF32: u32 = 151; +pub const R_SH_GOT32: u32 = 160; +pub const R_SH_PLT32: u32 = 161; +pub const R_SH_COPY: u32 = 162; +pub const R_SH_GLOB_DAT: u32 = 163; +pub const R_SH_JMP_SLOT: u32 = 164; +pub const R_SH_RELATIVE: u32 = 165; +pub const R_SH_GOTOFF: u32 = 166; +pub const R_SH_GOTPC: u32 = 167; + +// S/390 specific definitions. + +// S/390 values `FileHeader*::e_flags`. + +/// High GPRs kernel facility needed. +pub const EF_S390_HIGH_GPRS: u32 = 0x0000_0001; + +// S/390 values `Rel*::r_type`. + +/// No reloc. +pub const R_390_NONE: u32 = 0; +/// Direct 8 bit. +pub const R_390_8: u32 = 1; +/// Direct 12 bit. +pub const R_390_12: u32 = 2; +/// Direct 16 bit. +pub const R_390_16: u32 = 3; +/// Direct 32 bit. +pub const R_390_32: u32 = 4; +/// PC relative 32 bit. +pub const R_390_PC32: u32 = 5; +/// 12 bit GOT offset. +pub const R_390_GOT12: u32 = 6; +/// 32 bit GOT offset. +pub const R_390_GOT32: u32 = 7; +/// 32 bit PC relative PLT address. +pub const R_390_PLT32: u32 = 8; +/// Copy symbol at runtime. +pub const R_390_COPY: u32 = 9; +/// Create GOT entry. +pub const R_390_GLOB_DAT: u32 = 10; +/// Create PLT entry. +pub const R_390_JMP_SLOT: u32 = 11; +/// Adjust by program base. +pub const R_390_RELATIVE: u32 = 12; +/// 32 bit offset to GOT. +pub const R_390_GOTOFF32: u32 = 13; +/// 32 bit PC relative offset to GOT. +pub const R_390_GOTPC: u32 = 14; +/// 16 bit GOT offset. +pub const R_390_GOT16: u32 = 15; +/// PC relative 16 bit. +pub const R_390_PC16: u32 = 16; +/// PC relative 16 bit shifted by 1. +pub const R_390_PC16DBL: u32 = 17; +/// 16 bit PC rel. PLT shifted by 1. +pub const R_390_PLT16DBL: u32 = 18; +/// PC relative 32 bit shifted by 1. +pub const R_390_PC32DBL: u32 = 19; +/// 32 bit PC rel. PLT shifted by 1. +pub const R_390_PLT32DBL: u32 = 20; +/// 32 bit PC rel. GOT shifted by 1. +pub const R_390_GOTPCDBL: u32 = 21; +/// Direct 64 bit. +pub const R_390_64: u32 = 22; +/// PC relative 64 bit. +pub const R_390_PC64: u32 = 23; +/// 64 bit GOT offset. +pub const R_390_GOT64: u32 = 24; +/// 64 bit PC relative PLT address. +pub const R_390_PLT64: u32 = 25; +/// 32 bit PC rel. to GOT entry >> 1. +pub const R_390_GOTENT: u32 = 26; +/// 16 bit offset to GOT. +pub const R_390_GOTOFF16: u32 = 27; +/// 64 bit offset to GOT. +pub const R_390_GOTOFF64: u32 = 28; +/// 12 bit offset to jump slot. +pub const R_390_GOTPLT12: u32 = 29; +/// 16 bit offset to jump slot. +pub const R_390_GOTPLT16: u32 = 30; +/// 32 bit offset to jump slot. +pub const R_390_GOTPLT32: u32 = 31; +/// 64 bit offset to jump slot. +pub const R_390_GOTPLT64: u32 = 32; +/// 32 bit rel. offset to jump slot. +pub const R_390_GOTPLTENT: u32 = 33; +/// 16 bit offset from GOT to PLT. +pub const R_390_PLTOFF16: u32 = 34; +/// 32 bit offset from GOT to PLT. +pub const R_390_PLTOFF32: u32 = 35; +/// 16 bit offset from GOT to PLT. +pub const R_390_PLTOFF64: u32 = 36; +/// Tag for load insn in TLS code. +pub const R_390_TLS_LOAD: u32 = 37; +/// Tag for function call in general dynamic TLS code. +pub const R_390_TLS_GDCALL: u32 = 38; +/// Tag for function call in local dynamic TLS code. +pub const R_390_TLS_LDCALL: u32 = 39; +/// Direct 32 bit for general dynamic thread local data. +pub const R_390_TLS_GD32: u32 = 40; +/// Direct 64 bit for general dynamic thread local data. +pub const R_390_TLS_GD64: u32 = 41; +/// 12 bit GOT offset for static TLS block offset. +pub const R_390_TLS_GOTIE12: u32 = 42; +/// 32 bit GOT offset for static TLS block offset. +pub const R_390_TLS_GOTIE32: u32 = 43; +/// 64 bit GOT offset for static TLS block offset. +pub const R_390_TLS_GOTIE64: u32 = 44; +/// Direct 32 bit for local dynamic thread local data in LE code. +pub const R_390_TLS_LDM32: u32 = 45; +/// Direct 64 bit for local dynamic thread local data in LE code. +pub const R_390_TLS_LDM64: u32 = 46; +/// 32 bit address of GOT entry for negated static TLS block offset. +pub const R_390_TLS_IE32: u32 = 47; +/// 64 bit address of GOT entry for negated static TLS block offset. +pub const R_390_TLS_IE64: u32 = 48; +/// 32 bit rel. offset to GOT entry for negated static TLS block offset. +pub const R_390_TLS_IEENT: u32 = 49; +/// 32 bit negated offset relative to static TLS block. +pub const R_390_TLS_LE32: u32 = 50; +/// 64 bit negated offset relative to static TLS block. +pub const R_390_TLS_LE64: u32 = 51; +/// 32 bit offset relative to TLS block. +pub const R_390_TLS_LDO32: u32 = 52; +/// 64 bit offset relative to TLS block. +pub const R_390_TLS_LDO64: u32 = 53; +/// ID of module containing symbol. +pub const R_390_TLS_DTPMOD: u32 = 54; +/// Offset in TLS block. +pub const R_390_TLS_DTPOFF: u32 = 55; +/// Negated offset in static TLS block. +pub const R_390_TLS_TPOFF: u32 = 56; +/// Direct 20 bit. +pub const R_390_20: u32 = 57; +/// 20 bit GOT offset. +pub const R_390_GOT20: u32 = 58; +/// 20 bit offset to jump slot. +pub const R_390_GOTPLT20: u32 = 59; +/// 20 bit GOT offset for static TLS block offset. +pub const R_390_TLS_GOTIE20: u32 = 60; +/// STT_GNU_IFUNC relocation. +pub const R_390_IRELATIVE: u32 = 61; + +// CRIS values `Rel*::r_type`. +pub const R_CRIS_NONE: u32 = 0; +pub const R_CRIS_8: u32 = 1; +pub const R_CRIS_16: u32 = 2; +pub const R_CRIS_32: u32 = 3; +pub const R_CRIS_8_PCREL: u32 = 4; +pub const R_CRIS_16_PCREL: u32 = 5; +pub const R_CRIS_32_PCREL: u32 = 6; +pub const R_CRIS_GNU_VTINHERIT: u32 = 7; +pub const R_CRIS_GNU_VTENTRY: u32 = 8; +pub const R_CRIS_COPY: u32 = 9; +pub const R_CRIS_GLOB_DAT: u32 = 10; +pub const R_CRIS_JUMP_SLOT: u32 = 11; +pub const R_CRIS_RELATIVE: u32 = 12; +pub const R_CRIS_16_GOT: u32 = 13; +pub const R_CRIS_32_GOT: u32 = 14; +pub const R_CRIS_16_GOTPLT: u32 = 15; +pub const R_CRIS_32_GOTPLT: u32 = 16; +pub const R_CRIS_32_GOTREL: u32 = 17; +pub const R_CRIS_32_PLT_GOTREL: u32 = 18; +pub const R_CRIS_32_PLT_PCREL: u32 = 19; + +// AMD x86-64 values `Rel*::r_type`. +/// No reloc +pub const R_X86_64_NONE: u32 = 0; +/// Direct 64 bit +pub const R_X86_64_64: u32 = 1; +/// PC relative 32 bit signed +pub const R_X86_64_PC32: u32 = 2; +/// 32 bit GOT entry +pub const R_X86_64_GOT32: u32 = 3; +/// 32 bit PLT address +pub const R_X86_64_PLT32: u32 = 4; +/// Copy symbol at runtime +pub const R_X86_64_COPY: u32 = 5; +/// Create GOT entry +pub const R_X86_64_GLOB_DAT: u32 = 6; +/// Create PLT entry +pub const R_X86_64_JUMP_SLOT: u32 = 7; +/// Adjust by program base +pub const R_X86_64_RELATIVE: u32 = 8; +/// 32 bit signed PC relative offset to GOT +pub const R_X86_64_GOTPCREL: u32 = 9; +/// Direct 32 bit zero extended +pub const R_X86_64_32: u32 = 10; +/// Direct 32 bit sign extended +pub const R_X86_64_32S: u32 = 11; +/// Direct 16 bit zero extended +pub const R_X86_64_16: u32 = 12; +/// 16 bit sign extended pc relative +pub const R_X86_64_PC16: u32 = 13; +/// Direct 8 bit sign extended +pub const R_X86_64_8: u32 = 14; +/// 8 bit sign extended pc relative +pub const R_X86_64_PC8: u32 = 15; +/// ID of module containing symbol +pub const R_X86_64_DTPMOD64: u32 = 16; +/// Offset in module's TLS block +pub const R_X86_64_DTPOFF64: u32 = 17; +/// Offset in initial TLS block +pub const R_X86_64_TPOFF64: u32 = 18; +/// 32 bit signed PC relative offset to two GOT entries for GD symbol +pub const R_X86_64_TLSGD: u32 = 19; +/// 32 bit signed PC relative offset to two GOT entries for LD symbol +pub const R_X86_64_TLSLD: u32 = 20; +/// Offset in TLS block +pub const R_X86_64_DTPOFF32: u32 = 21; +/// 32 bit signed PC relative offset to GOT entry for IE symbol +pub const R_X86_64_GOTTPOFF: u32 = 22; +/// Offset in initial TLS block +pub const R_X86_64_TPOFF32: u32 = 23; +/// PC relative 64 bit +pub const R_X86_64_PC64: u32 = 24; +/// 64 bit offset to GOT +pub const R_X86_64_GOTOFF64: u32 = 25; +/// 32 bit signed pc relative offset to GOT +pub const R_X86_64_GOTPC32: u32 = 26; +/// 64-bit GOT entry offset +pub const R_X86_64_GOT64: u32 = 27; +/// 64-bit PC relative offset to GOT entry +pub const R_X86_64_GOTPCREL64: u32 = 28; +/// 64-bit PC relative offset to GOT +pub const R_X86_64_GOTPC64: u32 = 29; +/// like GOT64, says PLT entry needed +pub const R_X86_64_GOTPLT64: u32 = 30; +/// 64-bit GOT relative offset to PLT entry +pub const R_X86_64_PLTOFF64: u32 = 31; +/// Size of symbol plus 32-bit addend +pub const R_X86_64_SIZE32: u32 = 32; +/// Size of symbol plus 64-bit addend +pub const R_X86_64_SIZE64: u32 = 33; +/// GOT offset for TLS descriptor. +pub const R_X86_64_GOTPC32_TLSDESC: u32 = 34; +/// Marker for call through TLS descriptor. +pub const R_X86_64_TLSDESC_CALL: u32 = 35; +/// TLS descriptor. +pub const R_X86_64_TLSDESC: u32 = 36; +/// Adjust indirectly by program base +pub const R_X86_64_IRELATIVE: u32 = 37; +/// 64-bit adjust by program base +pub const R_X86_64_RELATIVE64: u32 = 38; +// 39 Reserved was R_X86_64_PC32_BND +// 40 Reserved was R_X86_64_PLT32_BND +/// Load from 32 bit signed pc relative offset to GOT entry without REX prefix, relaxable. +pub const R_X86_64_GOTPCRELX: u32 = 41; +/// Load from 32 bit signed pc relative offset to GOT entry with REX prefix, relaxable. +pub const R_X86_64_REX_GOTPCRELX: u32 = 42; + +// AMD x86-64 values `SectionHeader*::sh_type`. +/// Unwind information. +pub const SHT_X86_64_UNWIND: u32 = 0x7000_0001; + +// AM33 values `Rel*::r_type`. +/// No reloc. +pub const R_MN10300_NONE: u32 = 0; +/// Direct 32 bit. +pub const R_MN10300_32: u32 = 1; +/// Direct 16 bit. +pub const R_MN10300_16: u32 = 2; +/// Direct 8 bit. +pub const R_MN10300_8: u32 = 3; +/// PC-relative 32-bit. +pub const R_MN10300_PCREL32: u32 = 4; +/// PC-relative 16-bit signed. +pub const R_MN10300_PCREL16: u32 = 5; +/// PC-relative 8-bit signed. +pub const R_MN10300_PCREL8: u32 = 6; +/// Ancient C++ vtable garbage... +pub const R_MN10300_GNU_VTINHERIT: u32 = 7; +/// ... collection annotation. +pub const R_MN10300_GNU_VTENTRY: u32 = 8; +/// Direct 24 bit. +pub const R_MN10300_24: u32 = 9; +/// 32-bit PCrel offset to GOT. +pub const R_MN10300_GOTPC32: u32 = 10; +/// 16-bit PCrel offset to GOT. +pub const R_MN10300_GOTPC16: u32 = 11; +/// 32-bit offset from GOT. +pub const R_MN10300_GOTOFF32: u32 = 12; +/// 24-bit offset from GOT. +pub const R_MN10300_GOTOFF24: u32 = 13; +/// 16-bit offset from GOT. +pub const R_MN10300_GOTOFF16: u32 = 14; +/// 32-bit PCrel to PLT entry. +pub const R_MN10300_PLT32: u32 = 15; +/// 16-bit PCrel to PLT entry. +pub const R_MN10300_PLT16: u32 = 16; +/// 32-bit offset to GOT entry. +pub const R_MN10300_GOT32: u32 = 17; +/// 24-bit offset to GOT entry. +pub const R_MN10300_GOT24: u32 = 18; +/// 16-bit offset to GOT entry. +pub const R_MN10300_GOT16: u32 = 19; +/// Copy symbol at runtime. +pub const R_MN10300_COPY: u32 = 20; +/// Create GOT entry. +pub const R_MN10300_GLOB_DAT: u32 = 21; +/// Create PLT entry. +pub const R_MN10300_JMP_SLOT: u32 = 22; +/// Adjust by program base. +pub const R_MN10300_RELATIVE: u32 = 23; +/// 32-bit offset for global dynamic. +pub const R_MN10300_TLS_GD: u32 = 24; +/// 32-bit offset for local dynamic. +pub const R_MN10300_TLS_LD: u32 = 25; +/// Module-relative offset. +pub const R_MN10300_TLS_LDO: u32 = 26; +/// GOT offset for static TLS block offset. +pub const R_MN10300_TLS_GOTIE: u32 = 27; +/// GOT address for static TLS block offset. +pub const R_MN10300_TLS_IE: u32 = 28; +/// Offset relative to static TLS block. +pub const R_MN10300_TLS_LE: u32 = 29; +/// ID of module containing symbol. +pub const R_MN10300_TLS_DTPMOD: u32 = 30; +/// Offset in module TLS block. +pub const R_MN10300_TLS_DTPOFF: u32 = 31; +/// Offset in static TLS block. +pub const R_MN10300_TLS_TPOFF: u32 = 32; +/// Adjustment for next reloc as needed by linker relaxation. +pub const R_MN10300_SYM_DIFF: u32 = 33; +/// Alignment requirement for linker relaxation. +pub const R_MN10300_ALIGN: u32 = 34; + +// M32R values `Rel32::r_type`. +/// No reloc. +pub const R_M32R_NONE: u32 = 0; +/// Direct 16 bit. +pub const R_M32R_16: u32 = 1; +/// Direct 32 bit. +pub const R_M32R_32: u32 = 2; +/// Direct 24 bit. +pub const R_M32R_24: u32 = 3; +/// PC relative 10 bit shifted. +pub const R_M32R_10_PCREL: u32 = 4; +/// PC relative 18 bit shifted. +pub const R_M32R_18_PCREL: u32 = 5; +/// PC relative 26 bit shifted. +pub const R_M32R_26_PCREL: u32 = 6; +/// High 16 bit with unsigned low. +pub const R_M32R_HI16_ULO: u32 = 7; +/// High 16 bit with signed low. +pub const R_M32R_HI16_SLO: u32 = 8; +/// Low 16 bit. +pub const R_M32R_LO16: u32 = 9; +/// 16 bit offset in SDA. +pub const R_M32R_SDA16: u32 = 10; +pub const R_M32R_GNU_VTINHERIT: u32 = 11; +pub const R_M32R_GNU_VTENTRY: u32 = 12; +// M32R values `Rela32::r_type`. +/// Direct 16 bit. +pub const R_M32R_16_RELA: u32 = 33; +/// Direct 32 bit. +pub const R_M32R_32_RELA: u32 = 34; +/// Direct 24 bit. +pub const R_M32R_24_RELA: u32 = 35; +/// PC relative 10 bit shifted. +pub const R_M32R_10_PCREL_RELA: u32 = 36; +/// PC relative 18 bit shifted. +pub const R_M32R_18_PCREL_RELA: u32 = 37; +/// PC relative 26 bit shifted. +pub const R_M32R_26_PCREL_RELA: u32 = 38; +/// High 16 bit with unsigned low +pub const R_M32R_HI16_ULO_RELA: u32 = 39; +/// High 16 bit with signed low +pub const R_M32R_HI16_SLO_RELA: u32 = 40; +/// Low 16 bit +pub const R_M32R_LO16_RELA: u32 = 41; +/// 16 bit offset in SDA +pub const R_M32R_SDA16_RELA: u32 = 42; +pub const R_M32R_RELA_GNU_VTINHERIT: u32 = 43; +pub const R_M32R_RELA_GNU_VTENTRY: u32 = 44; +/// PC relative 32 bit. +pub const R_M32R_REL32: u32 = 45; + +/// 24 bit GOT entry +pub const R_M32R_GOT24: u32 = 48; +/// 26 bit PC relative to PLT shifted +pub const R_M32R_26_PLTREL: u32 = 49; +/// Copy symbol at runtime +pub const R_M32R_COPY: u32 = 50; +/// Create GOT entry +pub const R_M32R_GLOB_DAT: u32 = 51; +/// Create PLT entry +pub const R_M32R_JMP_SLOT: u32 = 52; +/// Adjust by program base +pub const R_M32R_RELATIVE: u32 = 53; +/// 24 bit offset to GOT +pub const R_M32R_GOTOFF: u32 = 54; +/// 24 bit PC relative offset to GOT +pub const R_M32R_GOTPC24: u32 = 55; +/// High 16 bit GOT entry with unsigned low +pub const R_M32R_GOT16_HI_ULO: u32 = 56; +/// High 16 bit GOT entry with signed low +pub const R_M32R_GOT16_HI_SLO: u32 = 57; +/// Low 16 bit GOT entry +pub const R_M32R_GOT16_LO: u32 = 58; +/// High 16 bit PC relative offset to GOT with unsigned low +pub const R_M32R_GOTPC_HI_ULO: u32 = 59; +/// High 16 bit PC relative offset to GOT with signed low +pub const R_M32R_GOTPC_HI_SLO: u32 = 60; +/// Low 16 bit PC relative offset to GOT +pub const R_M32R_GOTPC_LO: u32 = 61; +/// High 16 bit offset to GOT with unsigned low +pub const R_M32R_GOTOFF_HI_ULO: u32 = 62; +/// High 16 bit offset to GOT with signed low +pub const R_M32R_GOTOFF_HI_SLO: u32 = 63; +/// Low 16 bit offset to GOT +pub const R_M32R_GOTOFF_LO: u32 = 64; +/// Keep this the last entry. +pub const R_M32R_NUM: u32 = 256; + +// MicroBlaze values `Rel*::r_type`. +/// No reloc. +pub const R_MICROBLAZE_NONE: u32 = 0; +/// Direct 32 bit. +pub const R_MICROBLAZE_32: u32 = 1; +/// PC relative 32 bit. +pub const R_MICROBLAZE_32_PCREL: u32 = 2; +/// PC relative 64 bit. +pub const R_MICROBLAZE_64_PCREL: u32 = 3; +/// Low 16 bits of PCREL32. +pub const R_MICROBLAZE_32_PCREL_LO: u32 = 4; +/// Direct 64 bit. +pub const R_MICROBLAZE_64: u32 = 5; +/// Low 16 bit. +pub const R_MICROBLAZE_32_LO: u32 = 6; +/// Read-only small data area. +pub const R_MICROBLAZE_SRO32: u32 = 7; +/// Read-write small data area. +pub const R_MICROBLAZE_SRW32: u32 = 8; +/// No reloc. +pub const R_MICROBLAZE_64_NONE: u32 = 9; +/// Symbol Op Symbol relocation. +pub const R_MICROBLAZE_32_SYM_OP_SYM: u32 = 10; +/// GNU C++ vtable hierarchy. +pub const R_MICROBLAZE_GNU_VTINHERIT: u32 = 11; +/// GNU C++ vtable member usage. +pub const R_MICROBLAZE_GNU_VTENTRY: u32 = 12; +/// PC-relative GOT offset. +pub const R_MICROBLAZE_GOTPC_64: u32 = 13; +/// GOT entry offset. +pub const R_MICROBLAZE_GOT_64: u32 = 14; +/// PLT offset (PC-relative). +pub const R_MICROBLAZE_PLT_64: u32 = 15; +/// Adjust by program base. +pub const R_MICROBLAZE_REL: u32 = 16; +/// Create PLT entry. +pub const R_MICROBLAZE_JUMP_SLOT: u32 = 17; +/// Create GOT entry. +pub const R_MICROBLAZE_GLOB_DAT: u32 = 18; +/// 64 bit offset to GOT. +pub const R_MICROBLAZE_GOTOFF_64: u32 = 19; +/// 32 bit offset to GOT. +pub const R_MICROBLAZE_GOTOFF_32: u32 = 20; +/// Runtime copy. +pub const R_MICROBLAZE_COPY: u32 = 21; +/// TLS Reloc. +pub const R_MICROBLAZE_TLS: u32 = 22; +/// TLS General Dynamic. +pub const R_MICROBLAZE_TLSGD: u32 = 23; +/// TLS Local Dynamic. +pub const R_MICROBLAZE_TLSLD: u32 = 24; +/// TLS Module ID. +pub const R_MICROBLAZE_TLSDTPMOD32: u32 = 25; +/// TLS Offset Within TLS Block. +pub const R_MICROBLAZE_TLSDTPREL32: u32 = 26; +/// TLS Offset Within TLS Block. +pub const R_MICROBLAZE_TLSDTPREL64: u32 = 27; +/// TLS Offset From Thread Pointer. +pub const R_MICROBLAZE_TLSGOTTPREL32: u32 = 28; +/// TLS Offset From Thread Pointer. +pub const R_MICROBLAZE_TLSTPREL32: u32 = 29; + +// Nios II values `Dyn::d_tag`. +/// Address of _gp. +pub const DT_NIOS2_GP: u32 = 0x7000_0002; + +// Nios II values `Rel*::r_type`. +/// No reloc. +pub const R_NIOS2_NONE: u32 = 0; +/// Direct signed 16 bit. +pub const R_NIOS2_S16: u32 = 1; +/// Direct unsigned 16 bit. +pub const R_NIOS2_U16: u32 = 2; +/// PC relative 16 bit. +pub const R_NIOS2_PCREL16: u32 = 3; +/// Direct call. +pub const R_NIOS2_CALL26: u32 = 4; +/// 5 bit constant expression. +pub const R_NIOS2_IMM5: u32 = 5; +/// 5 bit expression, shift 22. +pub const R_NIOS2_CACHE_OPX: u32 = 6; +/// 6 bit constant expression. +pub const R_NIOS2_IMM6: u32 = 7; +/// 8 bit constant expression. +pub const R_NIOS2_IMM8: u32 = 8; +/// High 16 bit. +pub const R_NIOS2_HI16: u32 = 9; +/// Low 16 bit. +pub const R_NIOS2_LO16: u32 = 10; +/// High 16 bit, adjusted. +pub const R_NIOS2_HIADJ16: u32 = 11; +/// 32 bit symbol value + addend. +pub const R_NIOS2_BFD_RELOC_32: u32 = 12; +/// 16 bit symbol value + addend. +pub const R_NIOS2_BFD_RELOC_16: u32 = 13; +/// 8 bit symbol value + addend. +pub const R_NIOS2_BFD_RELOC_8: u32 = 14; +/// 16 bit GP pointer offset. +pub const R_NIOS2_GPREL: u32 = 15; +/// GNU C++ vtable hierarchy. +pub const R_NIOS2_GNU_VTINHERIT: u32 = 16; +/// GNU C++ vtable member usage. +pub const R_NIOS2_GNU_VTENTRY: u32 = 17; +/// Unconditional branch. +pub const R_NIOS2_UJMP: u32 = 18; +/// Conditional branch. +pub const R_NIOS2_CJMP: u32 = 19; +/// Indirect call through register. +pub const R_NIOS2_CALLR: u32 = 20; +/// Alignment requirement for linker relaxation. +pub const R_NIOS2_ALIGN: u32 = 21; +/// 16 bit GOT entry. +pub const R_NIOS2_GOT16: u32 = 22; +/// 16 bit GOT entry for function. +pub const R_NIOS2_CALL16: u32 = 23; +/// %lo of offset to GOT pointer. +pub const R_NIOS2_GOTOFF_LO: u32 = 24; +/// %hiadj of offset to GOT pointer. +pub const R_NIOS2_GOTOFF_HA: u32 = 25; +/// %lo of PC relative offset. +pub const R_NIOS2_PCREL_LO: u32 = 26; +/// %hiadj of PC relative offset. +pub const R_NIOS2_PCREL_HA: u32 = 27; +/// 16 bit GOT offset for TLS GD. +pub const R_NIOS2_TLS_GD16: u32 = 28; +/// 16 bit GOT offset for TLS LDM. +pub const R_NIOS2_TLS_LDM16: u32 = 29; +/// 16 bit module relative offset. +pub const R_NIOS2_TLS_LDO16: u32 = 30; +/// 16 bit GOT offset for TLS IE. +pub const R_NIOS2_TLS_IE16: u32 = 31; +/// 16 bit LE TP-relative offset. +pub const R_NIOS2_TLS_LE16: u32 = 32; +/// Module number. +pub const R_NIOS2_TLS_DTPMOD: u32 = 33; +/// Module-relative offset. +pub const R_NIOS2_TLS_DTPREL: u32 = 34; +/// TP-relative offset. +pub const R_NIOS2_TLS_TPREL: u32 = 35; +/// Copy symbol at runtime. +pub const R_NIOS2_COPY: u32 = 36; +/// Create GOT entry. +pub const R_NIOS2_GLOB_DAT: u32 = 37; +/// Create PLT entry. +pub const R_NIOS2_JUMP_SLOT: u32 = 38; +/// Adjust by program base. +pub const R_NIOS2_RELATIVE: u32 = 39; +/// 16 bit offset to GOT pointer. +pub const R_NIOS2_GOTOFF: u32 = 40; +/// Direct call in .noat section. +pub const R_NIOS2_CALL26_NOAT: u32 = 41; +/// %lo() of GOT entry. +pub const R_NIOS2_GOT_LO: u32 = 42; +/// %hiadj() of GOT entry. +pub const R_NIOS2_GOT_HA: u32 = 43; +/// %lo() of function GOT entry. +pub const R_NIOS2_CALL_LO: u32 = 44; +/// %hiadj() of function GOT entry. +pub const R_NIOS2_CALL_HA: u32 = 45; + +// TILEPro values `Rel*::r_type`. +/// No reloc +pub const R_TILEPRO_NONE: u32 = 0; +/// Direct 32 bit +pub const R_TILEPRO_32: u32 = 1; +/// Direct 16 bit +pub const R_TILEPRO_16: u32 = 2; +/// Direct 8 bit +pub const R_TILEPRO_8: u32 = 3; +/// PC relative 32 bit +pub const R_TILEPRO_32_PCREL: u32 = 4; +/// PC relative 16 bit +pub const R_TILEPRO_16_PCREL: u32 = 5; +/// PC relative 8 bit +pub const R_TILEPRO_8_PCREL: u32 = 6; +/// Low 16 bit +pub const R_TILEPRO_LO16: u32 = 7; +/// High 16 bit +pub const R_TILEPRO_HI16: u32 = 8; +/// High 16 bit, adjusted +pub const R_TILEPRO_HA16: u32 = 9; +/// Copy relocation +pub const R_TILEPRO_COPY: u32 = 10; +/// Create GOT entry +pub const R_TILEPRO_GLOB_DAT: u32 = 11; +/// Create PLT entry +pub const R_TILEPRO_JMP_SLOT: u32 = 12; +/// Adjust by program base +pub const R_TILEPRO_RELATIVE: u32 = 13; +/// X1 pipe branch offset +pub const R_TILEPRO_BROFF_X1: u32 = 14; +/// X1 pipe jump offset +pub const R_TILEPRO_JOFFLONG_X1: u32 = 15; +/// X1 pipe jump offset to PLT +pub const R_TILEPRO_JOFFLONG_X1_PLT: u32 = 16; +/// X0 pipe 8-bit +pub const R_TILEPRO_IMM8_X0: u32 = 17; +/// Y0 pipe 8-bit +pub const R_TILEPRO_IMM8_Y0: u32 = 18; +/// X1 pipe 8-bit +pub const R_TILEPRO_IMM8_X1: u32 = 19; +/// Y1 pipe 8-bit +pub const R_TILEPRO_IMM8_Y1: u32 = 20; +/// X1 pipe mtspr +pub const R_TILEPRO_MT_IMM15_X1: u32 = 21; +/// X1 pipe mfspr +pub const R_TILEPRO_MF_IMM15_X1: u32 = 22; +/// X0 pipe 16-bit +pub const R_TILEPRO_IMM16_X0: u32 = 23; +/// X1 pipe 16-bit +pub const R_TILEPRO_IMM16_X1: u32 = 24; +/// X0 pipe low 16-bit +pub const R_TILEPRO_IMM16_X0_LO: u32 = 25; +/// X1 pipe low 16-bit +pub const R_TILEPRO_IMM16_X1_LO: u32 = 26; +/// X0 pipe high 16-bit +pub const R_TILEPRO_IMM16_X0_HI: u32 = 27; +/// X1 pipe high 16-bit +pub const R_TILEPRO_IMM16_X1_HI: u32 = 28; +/// X0 pipe high 16-bit, adjusted +pub const R_TILEPRO_IMM16_X0_HA: u32 = 29; +/// X1 pipe high 16-bit, adjusted +pub const R_TILEPRO_IMM16_X1_HA: u32 = 30; +/// X0 pipe PC relative 16 bit +pub const R_TILEPRO_IMM16_X0_PCREL: u32 = 31; +/// X1 pipe PC relative 16 bit +pub const R_TILEPRO_IMM16_X1_PCREL: u32 = 32; +/// X0 pipe PC relative low 16 bit +pub const R_TILEPRO_IMM16_X0_LO_PCREL: u32 = 33; +/// X1 pipe PC relative low 16 bit +pub const R_TILEPRO_IMM16_X1_LO_PCREL: u32 = 34; +/// X0 pipe PC relative high 16 bit +pub const R_TILEPRO_IMM16_X0_HI_PCREL: u32 = 35; +/// X1 pipe PC relative high 16 bit +pub const R_TILEPRO_IMM16_X1_HI_PCREL: u32 = 36; +/// X0 pipe PC relative ha() 16 bit +pub const R_TILEPRO_IMM16_X0_HA_PCREL: u32 = 37; +/// X1 pipe PC relative ha() 16 bit +pub const R_TILEPRO_IMM16_X1_HA_PCREL: u32 = 38; +/// X0 pipe 16-bit GOT offset +pub const R_TILEPRO_IMM16_X0_GOT: u32 = 39; +/// X1 pipe 16-bit GOT offset +pub const R_TILEPRO_IMM16_X1_GOT: u32 = 40; +/// X0 pipe low 16-bit GOT offset +pub const R_TILEPRO_IMM16_X0_GOT_LO: u32 = 41; +/// X1 pipe low 16-bit GOT offset +pub const R_TILEPRO_IMM16_X1_GOT_LO: u32 = 42; +/// X0 pipe high 16-bit GOT offset +pub const R_TILEPRO_IMM16_X0_GOT_HI: u32 = 43; +/// X1 pipe high 16-bit GOT offset +pub const R_TILEPRO_IMM16_X1_GOT_HI: u32 = 44; +/// X0 pipe ha() 16-bit GOT offset +pub const R_TILEPRO_IMM16_X0_GOT_HA: u32 = 45; +/// X1 pipe ha() 16-bit GOT offset +pub const R_TILEPRO_IMM16_X1_GOT_HA: u32 = 46; +/// X0 pipe mm "start" +pub const R_TILEPRO_MMSTART_X0: u32 = 47; +/// X0 pipe mm "end" +pub const R_TILEPRO_MMEND_X0: u32 = 48; +/// X1 pipe mm "start" +pub const R_TILEPRO_MMSTART_X1: u32 = 49; +/// X1 pipe mm "end" +pub const R_TILEPRO_MMEND_X1: u32 = 50; +/// X0 pipe shift amount +pub const R_TILEPRO_SHAMT_X0: u32 = 51; +/// X1 pipe shift amount +pub const R_TILEPRO_SHAMT_X1: u32 = 52; +/// Y0 pipe shift amount +pub const R_TILEPRO_SHAMT_Y0: u32 = 53; +/// Y1 pipe shift amount +pub const R_TILEPRO_SHAMT_Y1: u32 = 54; +/// X1 pipe destination 8-bit +pub const R_TILEPRO_DEST_IMM8_X1: u32 = 55; +// Relocs 56-59 are currently not defined. +/// "jal" for TLS GD +pub const R_TILEPRO_TLS_GD_CALL: u32 = 60; +/// X0 pipe "addi" for TLS GD +pub const R_TILEPRO_IMM8_X0_TLS_GD_ADD: u32 = 61; +/// X1 pipe "addi" for TLS GD +pub const R_TILEPRO_IMM8_X1_TLS_GD_ADD: u32 = 62; +/// Y0 pipe "addi" for TLS GD +pub const R_TILEPRO_IMM8_Y0_TLS_GD_ADD: u32 = 63; +/// Y1 pipe "addi" for TLS GD +pub const R_TILEPRO_IMM8_Y1_TLS_GD_ADD: u32 = 64; +/// "lw_tls" for TLS IE +pub const R_TILEPRO_TLS_IE_LOAD: u32 = 65; +/// X0 pipe 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X0_TLS_GD: u32 = 66; +/// X1 pipe 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X1_TLS_GD: u32 = 67; +/// X0 pipe low 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X0_TLS_GD_LO: u32 = 68; +/// X1 pipe low 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X1_TLS_GD_LO: u32 = 69; +/// X0 pipe high 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X0_TLS_GD_HI: u32 = 70; +/// X1 pipe high 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X1_TLS_GD_HI: u32 = 71; +/// X0 pipe ha() 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X0_TLS_GD_HA: u32 = 72; +/// X1 pipe ha() 16-bit TLS GD offset +pub const R_TILEPRO_IMM16_X1_TLS_GD_HA: u32 = 73; +/// X0 pipe 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X0_TLS_IE: u32 = 74; +/// X1 pipe 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X1_TLS_IE: u32 = 75; +/// X0 pipe low 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X0_TLS_IE_LO: u32 = 76; +/// X1 pipe low 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X1_TLS_IE_LO: u32 = 77; +/// X0 pipe high 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X0_TLS_IE_HI: u32 = 78; +/// X1 pipe high 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X1_TLS_IE_HI: u32 = 79; +/// X0 pipe ha() 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X0_TLS_IE_HA: u32 = 80; +/// X1 pipe ha() 16-bit TLS IE offset +pub const R_TILEPRO_IMM16_X1_TLS_IE_HA: u32 = 81; +/// ID of module containing symbol +pub const R_TILEPRO_TLS_DTPMOD32: u32 = 82; +/// Offset in TLS block +pub const R_TILEPRO_TLS_DTPOFF32: u32 = 83; +/// Offset in static TLS block +pub const R_TILEPRO_TLS_TPOFF32: u32 = 84; +/// X0 pipe 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X0_TLS_LE: u32 = 85; +/// X1 pipe 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X1_TLS_LE: u32 = 86; +/// X0 pipe low 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X0_TLS_LE_LO: u32 = 87; +/// X1 pipe low 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X1_TLS_LE_LO: u32 = 88; +/// X0 pipe high 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X0_TLS_LE_HI: u32 = 89; +/// X1 pipe high 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X1_TLS_LE_HI: u32 = 90; +/// X0 pipe ha() 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X0_TLS_LE_HA: u32 = 91; +/// X1 pipe ha() 16-bit TLS LE offset +pub const R_TILEPRO_IMM16_X1_TLS_LE_HA: u32 = 92; + +/// GNU C++ vtable hierarchy +pub const R_TILEPRO_GNU_VTINHERIT: u32 = 128; +/// GNU C++ vtable member usage +pub const R_TILEPRO_GNU_VTENTRY: u32 = 129; + +// TILE-Gx values `Rel*::r_type`. +/// No reloc +pub const R_TILEGX_NONE: u32 = 0; +/// Direct 64 bit +pub const R_TILEGX_64: u32 = 1; +/// Direct 32 bit +pub const R_TILEGX_32: u32 = 2; +/// Direct 16 bit +pub const R_TILEGX_16: u32 = 3; +/// Direct 8 bit +pub const R_TILEGX_8: u32 = 4; +/// PC relative 64 bit +pub const R_TILEGX_64_PCREL: u32 = 5; +/// PC relative 32 bit +pub const R_TILEGX_32_PCREL: u32 = 6; +/// PC relative 16 bit +pub const R_TILEGX_16_PCREL: u32 = 7; +/// PC relative 8 bit +pub const R_TILEGX_8_PCREL: u32 = 8; +/// hword 0 16-bit +pub const R_TILEGX_HW0: u32 = 9; +/// hword 1 16-bit +pub const R_TILEGX_HW1: u32 = 10; +/// hword 2 16-bit +pub const R_TILEGX_HW2: u32 = 11; +/// hword 3 16-bit +pub const R_TILEGX_HW3: u32 = 12; +/// last hword 0 16-bit +pub const R_TILEGX_HW0_LAST: u32 = 13; +/// last hword 1 16-bit +pub const R_TILEGX_HW1_LAST: u32 = 14; +/// last hword 2 16-bit +pub const R_TILEGX_HW2_LAST: u32 = 15; +/// Copy relocation +pub const R_TILEGX_COPY: u32 = 16; +/// Create GOT entry +pub const R_TILEGX_GLOB_DAT: u32 = 17; +/// Create PLT entry +pub const R_TILEGX_JMP_SLOT: u32 = 18; +/// Adjust by program base +pub const R_TILEGX_RELATIVE: u32 = 19; +/// X1 pipe branch offset +pub const R_TILEGX_BROFF_X1: u32 = 20; +/// X1 pipe jump offset +pub const R_TILEGX_JUMPOFF_X1: u32 = 21; +/// X1 pipe jump offset to PLT +pub const R_TILEGX_JUMPOFF_X1_PLT: u32 = 22; +/// X0 pipe 8-bit +pub const R_TILEGX_IMM8_X0: u32 = 23; +/// Y0 pipe 8-bit +pub const R_TILEGX_IMM8_Y0: u32 = 24; +/// X1 pipe 8-bit +pub const R_TILEGX_IMM8_X1: u32 = 25; +/// Y1 pipe 8-bit +pub const R_TILEGX_IMM8_Y1: u32 = 26; +/// X1 pipe destination 8-bit +pub const R_TILEGX_DEST_IMM8_X1: u32 = 27; +/// X1 pipe mtspr +pub const R_TILEGX_MT_IMM14_X1: u32 = 28; +/// X1 pipe mfspr +pub const R_TILEGX_MF_IMM14_X1: u32 = 29; +/// X0 pipe mm "start" +pub const R_TILEGX_MMSTART_X0: u32 = 30; +/// X0 pipe mm "end" +pub const R_TILEGX_MMEND_X0: u32 = 31; +/// X0 pipe shift amount +pub const R_TILEGX_SHAMT_X0: u32 = 32; +/// X1 pipe shift amount +pub const R_TILEGX_SHAMT_X1: u32 = 33; +/// Y0 pipe shift amount +pub const R_TILEGX_SHAMT_Y0: u32 = 34; +/// Y1 pipe shift amount +pub const R_TILEGX_SHAMT_Y1: u32 = 35; +/// X0 pipe hword 0 +pub const R_TILEGX_IMM16_X0_HW0: u32 = 36; +/// X1 pipe hword 0 +pub const R_TILEGX_IMM16_X1_HW0: u32 = 37; +/// X0 pipe hword 1 +pub const R_TILEGX_IMM16_X0_HW1: u32 = 38; +/// X1 pipe hword 1 +pub const R_TILEGX_IMM16_X1_HW1: u32 = 39; +/// X0 pipe hword 2 +pub const R_TILEGX_IMM16_X0_HW2: u32 = 40; +/// X1 pipe hword 2 +pub const R_TILEGX_IMM16_X1_HW2: u32 = 41; +/// X0 pipe hword 3 +pub const R_TILEGX_IMM16_X0_HW3: u32 = 42; +/// X1 pipe hword 3 +pub const R_TILEGX_IMM16_X1_HW3: u32 = 43; +/// X0 pipe last hword 0 +pub const R_TILEGX_IMM16_X0_HW0_LAST: u32 = 44; +/// X1 pipe last hword 0 +pub const R_TILEGX_IMM16_X1_HW0_LAST: u32 = 45; +/// X0 pipe last hword 1 +pub const R_TILEGX_IMM16_X0_HW1_LAST: u32 = 46; +/// X1 pipe last hword 1 +pub const R_TILEGX_IMM16_X1_HW1_LAST: u32 = 47; +/// X0 pipe last hword 2 +pub const R_TILEGX_IMM16_X0_HW2_LAST: u32 = 48; +/// X1 pipe last hword 2 +pub const R_TILEGX_IMM16_X1_HW2_LAST: u32 = 49; +/// X0 pipe PC relative hword 0 +pub const R_TILEGX_IMM16_X0_HW0_PCREL: u32 = 50; +/// X1 pipe PC relative hword 0 +pub const R_TILEGX_IMM16_X1_HW0_PCREL: u32 = 51; +/// X0 pipe PC relative hword 1 +pub const R_TILEGX_IMM16_X0_HW1_PCREL: u32 = 52; +/// X1 pipe PC relative hword 1 +pub const R_TILEGX_IMM16_X1_HW1_PCREL: u32 = 53; +/// X0 pipe PC relative hword 2 +pub const R_TILEGX_IMM16_X0_HW2_PCREL: u32 = 54; +/// X1 pipe PC relative hword 2 +pub const R_TILEGX_IMM16_X1_HW2_PCREL: u32 = 55; +/// X0 pipe PC relative hword 3 +pub const R_TILEGX_IMM16_X0_HW3_PCREL: u32 = 56; +/// X1 pipe PC relative hword 3 +pub const R_TILEGX_IMM16_X1_HW3_PCREL: u32 = 57; +/// X0 pipe PC-rel last hword 0 +pub const R_TILEGX_IMM16_X0_HW0_LAST_PCREL: u32 = 58; +/// X1 pipe PC-rel last hword 0 +pub const R_TILEGX_IMM16_X1_HW0_LAST_PCREL: u32 = 59; +/// X0 pipe PC-rel last hword 1 +pub const R_TILEGX_IMM16_X0_HW1_LAST_PCREL: u32 = 60; +/// X1 pipe PC-rel last hword 1 +pub const R_TILEGX_IMM16_X1_HW1_LAST_PCREL: u32 = 61; +/// X0 pipe PC-rel last hword 2 +pub const R_TILEGX_IMM16_X0_HW2_LAST_PCREL: u32 = 62; +/// X1 pipe PC-rel last hword 2 +pub const R_TILEGX_IMM16_X1_HW2_LAST_PCREL: u32 = 63; +/// X0 pipe hword 0 GOT offset +pub const R_TILEGX_IMM16_X0_HW0_GOT: u32 = 64; +/// X1 pipe hword 0 GOT offset +pub const R_TILEGX_IMM16_X1_HW0_GOT: u32 = 65; +/// X0 pipe PC-rel PLT hword 0 +pub const R_TILEGX_IMM16_X0_HW0_PLT_PCREL: u32 = 66; +/// X1 pipe PC-rel PLT hword 0 +pub const R_TILEGX_IMM16_X1_HW0_PLT_PCREL: u32 = 67; +/// X0 pipe PC-rel PLT hword 1 +pub const R_TILEGX_IMM16_X0_HW1_PLT_PCREL: u32 = 68; +/// X1 pipe PC-rel PLT hword 1 +pub const R_TILEGX_IMM16_X1_HW1_PLT_PCREL: u32 = 69; +/// X0 pipe PC-rel PLT hword 2 +pub const R_TILEGX_IMM16_X0_HW2_PLT_PCREL: u32 = 70; +/// X1 pipe PC-rel PLT hword 2 +pub const R_TILEGX_IMM16_X1_HW2_PLT_PCREL: u32 = 71; +/// X0 pipe last hword 0 GOT offset +pub const R_TILEGX_IMM16_X0_HW0_LAST_GOT: u32 = 72; +/// X1 pipe last hword 0 GOT offset +pub const R_TILEGX_IMM16_X1_HW0_LAST_GOT: u32 = 73; +/// X0 pipe last hword 1 GOT offset +pub const R_TILEGX_IMM16_X0_HW1_LAST_GOT: u32 = 74; +/// X1 pipe last hword 1 GOT offset +pub const R_TILEGX_IMM16_X1_HW1_LAST_GOT: u32 = 75; +/// X0 pipe PC-rel PLT hword 3 +pub const R_TILEGX_IMM16_X0_HW3_PLT_PCREL: u32 = 76; +/// X1 pipe PC-rel PLT hword 3 +pub const R_TILEGX_IMM16_X1_HW3_PLT_PCREL: u32 = 77; +/// X0 pipe hword 0 TLS GD offset +pub const R_TILEGX_IMM16_X0_HW0_TLS_GD: u32 = 78; +/// X1 pipe hword 0 TLS GD offset +pub const R_TILEGX_IMM16_X1_HW0_TLS_GD: u32 = 79; +/// X0 pipe hword 0 TLS LE offset +pub const R_TILEGX_IMM16_X0_HW0_TLS_LE: u32 = 80; +/// X1 pipe hword 0 TLS LE offset +pub const R_TILEGX_IMM16_X1_HW0_TLS_LE: u32 = 81; +/// X0 pipe last hword 0 LE off +pub const R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE: u32 = 82; +/// X1 pipe last hword 0 LE off +pub const R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE: u32 = 83; +/// X0 pipe last hword 1 LE off +pub const R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE: u32 = 84; +/// X1 pipe last hword 1 LE off +pub const R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE: u32 = 85; +/// X0 pipe last hword 0 GD off +pub const R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD: u32 = 86; +/// X1 pipe last hword 0 GD off +pub const R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD: u32 = 87; +/// X0 pipe last hword 1 GD off +pub const R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD: u32 = 88; +/// X1 pipe last hword 1 GD off +pub const R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD: u32 = 89; +// Relocs 90-91 are currently not defined. +/// X0 pipe hword 0 TLS IE offset +pub const R_TILEGX_IMM16_X0_HW0_TLS_IE: u32 = 92; +/// X1 pipe hword 0 TLS IE offset +pub const R_TILEGX_IMM16_X1_HW0_TLS_IE: u32 = 93; +/// X0 pipe PC-rel PLT last hword 0 +pub const R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL: u32 = 94; +/// X1 pipe PC-rel PLT last hword 0 +pub const R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL: u32 = 95; +/// X0 pipe PC-rel PLT last hword 1 +pub const R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL: u32 = 96; +/// X1 pipe PC-rel PLT last hword 1 +pub const R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL: u32 = 97; +/// X0 pipe PC-rel PLT last hword 2 +pub const R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL: u32 = 98; +/// X1 pipe PC-rel PLT last hword 2 +pub const R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL: u32 = 99; +/// X0 pipe last hword 0 IE off +pub const R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE: u32 = 100; +/// X1 pipe last hword 0 IE off +pub const R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE: u32 = 101; +/// X0 pipe last hword 1 IE off +pub const R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE: u32 = 102; +/// X1 pipe last hword 1 IE off +pub const R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE: u32 = 103; +// Relocs 104-105 are currently not defined. +/// 64-bit ID of symbol's module +pub const R_TILEGX_TLS_DTPMOD64: u32 = 106; +/// 64-bit offset in TLS block +pub const R_TILEGX_TLS_DTPOFF64: u32 = 107; +/// 64-bit offset in static TLS block +pub const R_TILEGX_TLS_TPOFF64: u32 = 108; +/// 32-bit ID of symbol's module +pub const R_TILEGX_TLS_DTPMOD32: u32 = 109; +/// 32-bit offset in TLS block +pub const R_TILEGX_TLS_DTPOFF32: u32 = 110; +/// 32-bit offset in static TLS block +pub const R_TILEGX_TLS_TPOFF32: u32 = 111; +/// "jal" for TLS GD +pub const R_TILEGX_TLS_GD_CALL: u32 = 112; +/// X0 pipe "addi" for TLS GD +pub const R_TILEGX_IMM8_X0_TLS_GD_ADD: u32 = 113; +/// X1 pipe "addi" for TLS GD +pub const R_TILEGX_IMM8_X1_TLS_GD_ADD: u32 = 114; +/// Y0 pipe "addi" for TLS GD +pub const R_TILEGX_IMM8_Y0_TLS_GD_ADD: u32 = 115; +/// Y1 pipe "addi" for TLS GD +pub const R_TILEGX_IMM8_Y1_TLS_GD_ADD: u32 = 116; +/// "ld_tls" for TLS IE +pub const R_TILEGX_TLS_IE_LOAD: u32 = 117; +/// X0 pipe "addi" for TLS GD/IE +pub const R_TILEGX_IMM8_X0_TLS_ADD: u32 = 118; +/// X1 pipe "addi" for TLS GD/IE +pub const R_TILEGX_IMM8_X1_TLS_ADD: u32 = 119; +/// Y0 pipe "addi" for TLS GD/IE +pub const R_TILEGX_IMM8_Y0_TLS_ADD: u32 = 120; +/// Y1 pipe "addi" for TLS GD/IE +pub const R_TILEGX_IMM8_Y1_TLS_ADD: u32 = 121; + +/// GNU C++ vtable hierarchy +pub const R_TILEGX_GNU_VTINHERIT: u32 = 128; +/// GNU C++ vtable member usage +pub const R_TILEGX_GNU_VTENTRY: u32 = 129; + +// RISC-V values `FileHeader*::e_flags`. +pub const EF_RISCV_RVC: u32 = 0x0001; +pub const EF_RISCV_FLOAT_ABI: u32 = 0x0006; +pub const EF_RISCV_FLOAT_ABI_SOFT: u32 = 0x0000; +pub const EF_RISCV_FLOAT_ABI_SINGLE: u32 = 0x0002; +pub const EF_RISCV_FLOAT_ABI_DOUBLE: u32 = 0x0004; +pub const EF_RISCV_FLOAT_ABI_QUAD: u32 = 0x0006; +pub const EF_RISCV_RVE: u32 = 0x0008; +pub const EF_RISCV_TSO: u32 = 0x0010; +pub const EF_RISCV_RV64ILP32: u32 = 0x0020; + +// RISC-V values for `SectionHeader*::sh_type`. +/// RISC-V attributes section. +pub const SHT_RISCV_ATTRIBUTES: u32 = SHT_LOPROC + 3; + +// RISC-V values `Rel*::r_type`. +pub const R_RISCV_NONE: u32 = 0; +pub const R_RISCV_32: u32 = 1; +pub const R_RISCV_64: u32 = 2; +pub const R_RISCV_RELATIVE: u32 = 3; +pub const R_RISCV_COPY: u32 = 4; +pub const R_RISCV_JUMP_SLOT: u32 = 5; +pub const R_RISCV_TLS_DTPMOD32: u32 = 6; +pub const R_RISCV_TLS_DTPMOD64: u32 = 7; +pub const R_RISCV_TLS_DTPREL32: u32 = 8; +pub const R_RISCV_TLS_DTPREL64: u32 = 9; +pub const R_RISCV_TLS_TPREL32: u32 = 10; +pub const R_RISCV_TLS_TPREL64: u32 = 11; +pub const R_RISCV_TLSDESC: u32 = 12; +pub const R_RISCV_BRANCH: u32 = 16; +pub const R_RISCV_JAL: u32 = 17; +pub const R_RISCV_CALL: u32 = 18; +pub const R_RISCV_CALL_PLT: u32 = 19; +pub const R_RISCV_GOT_HI20: u32 = 20; +pub const R_RISCV_TLS_GOT_HI20: u32 = 21; +pub const R_RISCV_TLS_GD_HI20: u32 = 22; +pub const R_RISCV_PCREL_HI20: u32 = 23; +pub const R_RISCV_PCREL_LO12_I: u32 = 24; +pub const R_RISCV_PCREL_LO12_S: u32 = 25; +pub const R_RISCV_HI20: u32 = 26; +pub const R_RISCV_LO12_I: u32 = 27; +pub const R_RISCV_LO12_S: u32 = 28; +pub const R_RISCV_TPREL_HI20: u32 = 29; +pub const R_RISCV_TPREL_LO12_I: u32 = 30; +pub const R_RISCV_TPREL_LO12_S: u32 = 31; +pub const R_RISCV_TPREL_ADD: u32 = 32; +pub const R_RISCV_ADD8: u32 = 33; +pub const R_RISCV_ADD16: u32 = 34; +pub const R_RISCV_ADD32: u32 = 35; +pub const R_RISCV_ADD64: u32 = 36; +pub const R_RISCV_SUB8: u32 = 37; +pub const R_RISCV_SUB16: u32 = 38; +pub const R_RISCV_SUB32: u32 = 39; +pub const R_RISCV_SUB64: u32 = 40; +pub const R_RISCV_GOT32_PCREL: u32 = 41; +// 42 Reserved was R_RISCV_GNU_VTENTRY +pub const R_RISCV_ALIGN: u32 = 43; +pub const R_RISCV_RVC_BRANCH: u32 = 44; +pub const R_RISCV_RVC_JUMP: u32 = 45; +pub const R_RISCV_RVC_LUI: u32 = 46; +pub const R_RISCV_GPREL_I: u32 = 47; +pub const R_RISCV_GPREL_S: u32 = 48; +pub const R_RISCV_TPREL_I: u32 = 49; +pub const R_RISCV_TPREL_S: u32 = 50; +pub const R_RISCV_RELAX: u32 = 51; +pub const R_RISCV_SUB6: u32 = 52; +pub const R_RISCV_SET6: u32 = 53; +pub const R_RISCV_SET8: u32 = 54; +pub const R_RISCV_SET16: u32 = 55; +pub const R_RISCV_SET32: u32 = 56; +pub const R_RISCV_32_PCREL: u32 = 57; +pub const R_RISCV_IRELATIVE: u32 = 58; +pub const R_RISCV_PLT32: u32 = 59; +pub const R_RISCV_SET_ULEB128: u32 = 60; +pub const R_RISCV_SUB_ULEB128: u32 = 61; +pub const R_RISCV_TLSDESC_HI20: u32 = 62; +pub const R_RISCV_TLSDESC_LOAD_LO12: u32 = 63; +pub const R_RISCV_TLSDESC_ADD_LO12: u32 = 64; +pub const R_RISCV_TLSDESC_CALL: u32 = 65; + +// BPF values `Rel*::r_type`. +/// No reloc +pub const R_BPF_NONE: u32 = 0; +pub const R_BPF_64_64: u32 = 1; +pub const R_BPF_64_32: u32 = 10; + +// SBF values `Rel*::r_type`. +/// No reloc +pub const R_SBF_NONE: u32 = 0; +pub const R_SBF_64_64: u32 = 1; +pub const R_SBF_64_32: u32 = 10; + +// Imagination Meta values `Rel*::r_type`. + +pub const R_METAG_HIADDR16: u32 = 0; +pub const R_METAG_LOADDR16: u32 = 1; +/// 32bit absolute address +pub const R_METAG_ADDR32: u32 = 2; +/// No reloc +pub const R_METAG_NONE: u32 = 3; +pub const R_METAG_RELBRANCH: u32 = 4; +pub const R_METAG_GETSETOFF: u32 = 5; + +// Backward compatibility +pub const R_METAG_REG32OP1: u32 = 6; +pub const R_METAG_REG32OP2: u32 = 7; +pub const R_METAG_REG32OP3: u32 = 8; +pub const R_METAG_REG16OP1: u32 = 9; +pub const R_METAG_REG16OP2: u32 = 10; +pub const R_METAG_REG16OP3: u32 = 11; +pub const R_METAG_REG32OP4: u32 = 12; + +pub const R_METAG_HIOG: u32 = 13; +pub const R_METAG_LOOG: u32 = 14; + +pub const R_METAG_REL8: u32 = 15; +pub const R_METAG_REL16: u32 = 16; + +pub const R_METAG_GNU_VTINHERIT: u32 = 30; +pub const R_METAG_GNU_VTENTRY: u32 = 31; + +// PIC relocations +pub const R_METAG_HI16_GOTOFF: u32 = 32; +pub const R_METAG_LO16_GOTOFF: u32 = 33; +pub const R_METAG_GETSET_GOTOFF: u32 = 34; +pub const R_METAG_GETSET_GOT: u32 = 35; +pub const R_METAG_HI16_GOTPC: u32 = 36; +pub const R_METAG_LO16_GOTPC: u32 = 37; +pub const R_METAG_HI16_PLT: u32 = 38; +pub const R_METAG_LO16_PLT: u32 = 39; +pub const R_METAG_RELBRANCH_PLT: u32 = 40; +pub const R_METAG_GOTOFF: u32 = 41; +pub const R_METAG_PLT: u32 = 42; +pub const R_METAG_COPY: u32 = 43; +pub const R_METAG_JMP_SLOT: u32 = 44; +pub const R_METAG_RELATIVE: u32 = 45; +pub const R_METAG_GLOB_DAT: u32 = 46; + +// TLS relocations +pub const R_METAG_TLS_GD: u32 = 47; +pub const R_METAG_TLS_LDM: u32 = 48; +pub const R_METAG_TLS_LDO_HI16: u32 = 49; +pub const R_METAG_TLS_LDO_LO16: u32 = 50; +pub const R_METAG_TLS_LDO: u32 = 51; +pub const R_METAG_TLS_IE: u32 = 52; +pub const R_METAG_TLS_IENONPIC: u32 = 53; +pub const R_METAG_TLS_IENONPIC_HI16: u32 = 54; +pub const R_METAG_TLS_IENONPIC_LO16: u32 = 55; +pub const R_METAG_TLS_TPOFF: u32 = 56; +pub const R_METAG_TLS_DTPMOD: u32 = 57; +pub const R_METAG_TLS_DTPOFF: u32 = 58; +pub const R_METAG_TLS_LE: u32 = 59; +pub const R_METAG_TLS_LE_HI16: u32 = 60; +pub const R_METAG_TLS_LE_LO16: u32 = 61; + +// NDS32 values `Rel*::r_type`. +pub const R_NDS32_NONE: u32 = 0; +pub const R_NDS32_32_RELA: u32 = 20; +pub const R_NDS32_COPY: u32 = 39; +pub const R_NDS32_GLOB_DAT: u32 = 40; +pub const R_NDS32_JMP_SLOT: u32 = 41; +pub const R_NDS32_RELATIVE: u32 = 42; +pub const R_NDS32_TLS_TPOFF: u32 = 102; +pub const R_NDS32_TLS_DESC: u32 = 119; + +// LoongArch values `FileHeader*::e_flags`. +/// Additional properties of the base ABI type, including the FP calling +/// convention. +pub const EF_LARCH_ABI_MODIFIER_MASK: u32 = 0x7; +/// Uses GPRs and the stack for parameter passing +pub const EF_LARCH_ABI_SOFT_FLOAT: u32 = 0x1; +/// Uses GPRs, 32-bit FPRs and the stack for parameter passing +pub const EF_LARCH_ABI_SINGLE_FLOAT: u32 = 0x2; +/// Uses GPRs, 64-bit FPRs and the stack for parameter passing +pub const EF_LARCH_ABI_DOUBLE_FLOAT: u32 = 0x3; +/// Uses relocation types directly writing to immediate slots +pub const EF_LARCH_OBJABI_V1: u32 = 0x40; + +// LoongArch values `Rel*::r_type`. +/// No reloc +pub const R_LARCH_NONE: u32 = 0; +/// Runtime address resolving +pub const R_LARCH_32: u32 = 1; +/// Runtime address resolving +pub const R_LARCH_64: u32 = 2; +/// Runtime fixup for load-address +pub const R_LARCH_RELATIVE: u32 = 3; +/// Runtime memory copy in executable +pub const R_LARCH_COPY: u32 = 4; +/// Runtime PLT supporting +pub const R_LARCH_JUMP_SLOT: u32 = 5; +/// Runtime relocation for TLS-GD +pub const R_LARCH_TLS_DTPMOD32: u32 = 6; +/// Runtime relocation for TLS-GD +pub const R_LARCH_TLS_DTPMOD64: u32 = 7; +/// Runtime relocation for TLS-GD +pub const R_LARCH_TLS_DTPREL32: u32 = 8; +/// Runtime relocation for TLS-GD +pub const R_LARCH_TLS_DTPREL64: u32 = 9; +/// Runtime relocation for TLE-IE +pub const R_LARCH_TLS_TPREL32: u32 = 10; +/// Runtime relocation for TLE-IE +pub const R_LARCH_TLS_TPREL64: u32 = 11; +/// Runtime local indirect function resolving +pub const R_LARCH_IRELATIVE: u32 = 12; +/// Mark la.abs: load absolute address for static link. +pub const R_LARCH_MARK_LA: u32 = 20; +/// Mark external label branch: access PC relative address for static link. +pub const R_LARCH_MARK_PCREL: u32 = 21; +/// Push PC-relative offset +pub const R_LARCH_SOP_PUSH_PCREL: u32 = 22; +/// Push constant or absolute address +pub const R_LARCH_SOP_PUSH_ABSOLUTE: u32 = 23; +/// Duplicate stack top +pub const R_LARCH_SOP_PUSH_DUP: u32 = 24; +/// Push for access GOT entry +pub const R_LARCH_SOP_PUSH_GPREL: u32 = 25; +/// Push for TLS-LE +pub const R_LARCH_SOP_PUSH_TLS_TPREL: u32 = 26; +/// Push for TLS-IE +pub const R_LARCH_SOP_PUSH_TLS_GOT: u32 = 27; +/// Push for TLS-GD +pub const R_LARCH_SOP_PUSH_TLS_GD: u32 = 28; +/// Push for external function calling +pub const R_LARCH_SOP_PUSH_PLT_PCREL: u32 = 29; +/// Assert stack top +pub const R_LARCH_SOP_ASSERT: u32 = 30; +/// Stack top logical not (unary) +pub const R_LARCH_SOP_NOT: u32 = 31; +/// Stack top subtraction (binary) +pub const R_LARCH_SOP_SUB: u32 = 32; +/// Stack top left shift (binary) +pub const R_LARCH_SOP_SL: u32 = 33; +/// Stack top right shift (binary) +pub const R_LARCH_SOP_SR: u32 = 34; +/// Stack top addition (binary) +pub const R_LARCH_SOP_ADD: u32 = 35; +/// Stack top bitwise and (binary) +pub const R_LARCH_SOP_AND: u32 = 36; +/// Stack top selection (tertiary) +pub const R_LARCH_SOP_IF_ELSE: u32 = 37; +/// Pop stack top to fill 5-bit signed immediate operand +pub const R_LARCH_SOP_POP_32_S_10_5: u32 = 38; +/// Pop stack top to fill 12-bit unsigned immediate operand +pub const R_LARCH_SOP_POP_32_U_10_12: u32 = 39; +/// Pop stack top to fill 12-bit signed immediate operand +pub const R_LARCH_SOP_POP_32_S_10_12: u32 = 40; +/// Pop stack top to fill 16-bit signed immediate operand +pub const R_LARCH_SOP_POP_32_S_10_16: u32 = 41; +/// Pop stack top to fill 18-bit signed immediate operand with two trailing +/// zeros implied +pub const R_LARCH_SOP_POP_32_S_10_16_S2: u32 = 42; +/// Pop stack top to fill 20-bit signed immediate operand +pub const R_LARCH_SOP_POP_32_S_5_20: u32 = 43; +/// Pop stack top to fill 23-bit signed immediate operand with two trailing +/// zeros implied +pub const R_LARCH_SOP_POP_32_S_0_5_10_16_S2: u32 = 44; +/// Pop stack top to fill 28-bit signed immediate operand with two trailing +/// zeros implied +pub const R_LARCH_SOP_POP_32_S_0_10_10_16_S2: u32 = 45; +/// Pop stack top to fill an instruction +pub const R_LARCH_SOP_POP_32_U: u32 = 46; +/// 8-bit in-place addition +pub const R_LARCH_ADD8: u32 = 47; +/// 16-bit in-place addition +pub const R_LARCH_ADD16: u32 = 48; +/// 24-bit in-place addition +pub const R_LARCH_ADD24: u32 = 49; +/// 32-bit in-place addition +pub const R_LARCH_ADD32: u32 = 50; +/// 64-bit in-place addition +pub const R_LARCH_ADD64: u32 = 51; +/// 8-bit in-place subtraction +pub const R_LARCH_SUB8: u32 = 52; +/// 16-bit in-place subtraction +pub const R_LARCH_SUB16: u32 = 53; +/// 24-bit in-place subtraction +pub const R_LARCH_SUB24: u32 = 54; +/// 32-bit in-place subtraction +pub const R_LARCH_SUB32: u32 = 55; +/// 64-bit in-place subtraction +pub const R_LARCH_SUB64: u32 = 56; +/// GNU C++ vtable hierarchy +pub const R_LARCH_GNU_VTINHERIT: u32 = 57; +/// GNU C++ vtable member usage +pub const R_LARCH_GNU_VTENTRY: u32 = 58; +/// 18-bit PC-relative jump offset with two trailing zeros +pub const R_LARCH_B16: u32 = 64; +/// 23-bit PC-relative jump offset with two trailing zeros +pub const R_LARCH_B21: u32 = 65; +/// 28-bit PC-relative jump offset with two trailing zeros +pub const R_LARCH_B26: u32 = 66; +/// 12..=31 bits of 32/64-bit absolute address +pub const R_LARCH_ABS_HI20: u32 = 67; +/// 0..=11 bits of 32/64-bit absolute address +pub const R_LARCH_ABS_LO12: u32 = 68; +/// 32..=51 bits of 64-bit absolute address +pub const R_LARCH_ABS64_LO20: u32 = 69; +/// 52..=63 bits of 64-bit absolute address +pub const R_LARCH_ABS64_HI12: u32 = 70; +/// The signed 32-bit offset `offs` from `PC & 0xfffff000` to +/// `(S + A + 0x800) & 0xfffff000`, with 12 trailing zeros removed. +/// +/// We define the *PC relative anchor* for `S + A` as `PC + offs` (`offs` +/// is sign-extended to VA bits). +pub const R_LARCH_PCALA_HI20: u32 = 71; +/// Same as R_LARCH_ABS_LO12. 0..=11 bits of the 32/64-bit offset from the +/// [PC relative anchor][R_LARCH_PCALA_HI20]. +pub const R_LARCH_PCALA_LO12: u32 = 72; +/// 32..=51 bits of the 64-bit offset from the +/// [PC relative anchor][R_LARCH_PCALA_HI20]. +pub const R_LARCH_PCALA64_LO20: u32 = 73; +/// 52..=63 bits of the 64-bit offset from the +/// [PC relative anchor][R_LARCH_PCALA_HI20]. +pub const R_LARCH_PCALA64_HI12: u32 = 74; +/// The signed 32-bit offset `offs` from `PC & 0xfffff000` to +/// `(GP + G + 0x800) & 0xfffff000`, with 12 trailing zeros removed. +/// +/// We define the *PC relative anchor* for the GOT entry at `GP + G` as +/// `PC + offs` (`offs` is sign-extended to VA bits). +pub const R_LARCH_GOT_PC_HI20: u32 = 75; +/// 0..=11 bits of the 32/64-bit offset from the +/// [PC relative anchor][R_LARCH_GOT_PC_HI20] to the GOT entry. +pub const R_LARCH_GOT_PC_LO12: u32 = 76; +/// 32..=51 bits of the 64-bit offset from the +/// [PC relative anchor][R_LARCH_GOT_PC_HI20] to the GOT entry. +pub const R_LARCH_GOT64_PC_LO20: u32 = 77; +/// 52..=63 bits of the 64-bit offset from the +/// [PC relative anchor][R_LARCH_GOT_PC_HI20] to the GOT entry. +pub const R_LARCH_GOT64_PC_HI12: u32 = 78; +/// 12..=31 bits of 32/64-bit GOT entry absolute address +pub const R_LARCH_GOT_HI20: u32 = 79; +/// 0..=11 bits of 32/64-bit GOT entry absolute address +pub const R_LARCH_GOT_LO12: u32 = 80; +/// 32..=51 bits of 64-bit GOT entry absolute address +pub const R_LARCH_GOT64_LO20: u32 = 81; +/// 52..=63 bits of 64-bit GOT entry absolute address +pub const R_LARCH_GOT64_HI12: u32 = 82; +/// 12..=31 bits of TLS LE 32/64-bit offset from thread pointer +pub const R_LARCH_TLS_LE_HI20: u32 = 83; +/// 0..=11 bits of TLS LE 32/64-bit offset from thread pointer +pub const R_LARCH_TLS_LE_LO12: u32 = 84; +/// 32..=51 bits of TLS LE 64-bit offset from thread pointer +pub const R_LARCH_TLS_LE64_LO20: u32 = 85; +/// 52..=63 bits of TLS LE 64-bit offset from thread pointer +pub const R_LARCH_TLS_LE64_HI12: u32 = 86; +/// The signed 32-bit offset `offs` from `PC & 0xfffff000` to +/// `(GP + IE + 0x800) & 0xfffff000`, with 12 trailing zeros removed. +/// +/// We define the *PC relative anchor* for the TLS IE GOT entry at +/// `GP + IE` as `PC + offs` (`offs` is sign-extended to VA bits). +pub const R_LARCH_TLS_IE_PC_HI20: u32 = 87; +/// 0..=12 bits of the 32/64-bit offset from the +/// [PC-relative anchor][R_LARCH_TLS_IE_PC_HI20] to the TLS IE GOT entry. +pub const R_LARCH_TLS_IE_PC_LO12: u32 = 88; +/// 32..=51 bits of the 64-bit offset from the +/// [PC-relative anchor][R_LARCH_TLS_IE_PC_HI20] to the TLS IE GOT entry. +pub const R_LARCH_TLS_IE64_PC_LO20: u32 = 89; +/// 52..=63 bits of the 64-bit offset from the +/// [PC-relative anchor][R_LARCH_TLS_IE_PC_HI20] to the TLS IE GOT entry. +pub const R_LARCH_TLS_IE64_PC_HI12: u32 = 90; +/// 12..=31 bits of TLS IE GOT entry 32/64-bit absolute address +pub const R_LARCH_TLS_IE_HI20: u32 = 91; +/// 0..=11 bits of TLS IE GOT entry 32/64-bit absolute address +pub const R_LARCH_TLS_IE_LO12: u32 = 92; +/// 32..=51 bits of TLS IE GOT entry 64-bit absolute address +pub const R_LARCH_TLS_IE64_LO20: u32 = 93; +/// 51..=63 bits of TLS IE GOT entry 64-bit absolute address +pub const R_LARCH_TLS_IE64_HI12: u32 = 94; +/// 12..=31 bits of the offset from `PC` to `GP + GD + 0x800`, where +/// `GP + GD` is a TLS LD GOT entry +pub const R_LARCH_TLS_LD_PC_HI20: u32 = 95; +/// 12..=31 bits of TLS LD GOT entry 32/64-bit absolute address +pub const R_LARCH_TLS_LD_HI20: u32 = 96; +/// 12..=31 bits of the 32/64-bit PC-relative offset to the PC-relative +/// anchor for the TLE GD GOT entry. +pub const R_LARCH_TLS_GD_PC_HI20: u32 = 97; +/// 12..=31 bits of TLS GD GOT entry 32/64-bit absolute address +pub const R_LARCH_TLS_GD_HI20: u32 = 98; +/// 32-bit PC relative +pub const R_LARCH_32_PCREL: u32 = 99; +/// Paired with a normal relocation at the same address to indicate the +/// instruction can be relaxed +pub const R_LARCH_RELAX: u32 = 100; +/// Reserved +pub const R_LARCH_DELETE: u32 = 101; +/// Delete some bytes to ensure the instruction at PC + A aligned to +/// `A.next_power_of_two()`-byte boundary +pub const R_LARCH_ALIGN: u32 = 102; +/// 22-bit PC-relative offset with two trailing zeros +pub const R_LARCH_PCREL20_S2: u32 = 103; +/// Reserved +pub const R_LARCH_CFA: u32 = 104; +/// 6-bit in-place addition +pub const R_LARCH_ADD6: u32 = 105; +/// 6-bit in-place subtraction +pub const R_LARCH_SUB6: u32 = 106; +/// LEB128 in-place addition +pub const R_LARCH_ADD_ULEB128: u32 = 107; +/// LEB128 in-place subtraction +pub const R_LARCH_SUB_ULEB128: u32 = 108; +/// 64-bit PC relative +pub const R_LARCH_64_PCREL: u32 = 109; +/// 18..=37 bits of `S + A - PC` into the `pcaddu18i` instruction at `PC`, +/// and 2..=17 bits of `S + A - PC` into the `jirl` instruction at `PC + 4` +pub const R_LARCH_CALL36: u32 = 110; +/// 12..=31 bits of 32/64-bit PC-relative offset to TLS DESC GOT entry +pub const R_LARCH_TLS_DESC_PC_HI20: u32 = 111; +/// 0..=11 bits of 32/64-bit TLS DESC GOT entry address +pub const R_LARCH_TLS_DESC_PC_LO12: u32 = 112; +/// 32..=51 bits of 64-bit PC-relative offset to TLS DESC GOT entry +pub const R_LARCH_TLS_DESC64_PC_LO20: u32 = 113; +/// 52..=63 bits of 64-bit PC-relative offset to TLS DESC GOT entry +pub const R_LARCH_TLS_DESC64_PC_HI12: u32 = 114; +/// 12..=31 bits of 32/64-bit TLS DESC GOT entry absolute address +pub const R_LARCH_TLS_DESC_HI20: u32 = 115; +/// 0..=11 bits of 32/64-bit TLS DESC GOT entry absolute address +pub const R_LARCH_TLS_DESC_LO12: u32 = 116; +/// 32..=51 bits of 64-bit TLS DESC GOT entry absolute address +pub const R_LARCH_TLS_DESC64_LO20: u32 = 117; +/// 52..=63 bits of 64-bit TLS DESC GOT entry absolute address +pub const R_LARCH_TLS_DESC64_HI12: u32 = 118; +/// Used on ld.{w,d} for TLS DESC to get the resolve function address +/// from GOT entry +pub const R_LARCH_TLS_DESC_LD: u32 = 119; +/// Used on jirl for TLS DESC to call the resolve function +pub const R_LARCH_TLS_DESC_CALL: u32 = 120; +/// 12..=31 bits of TLS LE 32/64-bit offset from TP register, can be relaxed +pub const R_LARCH_TLS_LE_HI20_R: u32 = 121; +/// TLS LE thread pointer usage, can be relaxed +pub const R_LARCH_TLS_LE_ADD_R: u32 = 122; +/// 0..=11 bits of TLS LE 32/64-bit offset from TP register, sign-extended, +/// can be relaxed. +pub const R_LARCH_TLS_LE_LO12_R: u32 = 123; +/// 22-bit PC-relative offset to TLS LD GOT entry +pub const R_LARCH_TLS_LD_PCREL20_S2: u32 = 124; +/// 22-bit PC-relative offset to TLS GD GOT entry +pub const R_LARCH_TLS_GD_PCREL20_S2: u32 = 125; +/// 22-bit PC-relative offset to TLS DESC GOT entry +pub const R_LARCH_TLS_DESC_PCREL20_S2: u32 = 126; + +// Xtensa values Rel*::r_type`. +pub const R_XTENSA_NONE: u32 = 0; +pub const R_XTENSA_32: u32 = 1; +pub const R_XTENSA_RTLD: u32 = 2; +pub const R_XTENSA_GLOB_DAT: u32 = 3; +pub const R_XTENSA_JMP_SLOT: u32 = 4; +pub const R_XTENSA_RELATIVE: u32 = 5; +pub const R_XTENSA_PLT: u32 = 6; +pub const R_XTENSA_OP0: u32 = 8; +pub const R_XTENSA_OP1: u32 = 9; +pub const R_XTENSA_OP2: u32 = 10; +pub const R_XTENSA_ASM_EXPAND: u32 = 11; +pub const R_XTENSA_ASM_SIMPLIFY: u32 = 12; +pub const R_XTENSA_32_PCREL: u32 = 14; +pub const R_XTENSA_GNU_VTINHERIT: u32 = 15; +pub const R_XTENSA_GNU_VTENTRY: u32 = 16; +pub const R_XTENSA_DIFF8: u32 = 17; +pub const R_XTENSA_DIFF16: u32 = 18; +pub const R_XTENSA_DIFF32: u32 = 19; +pub const R_XTENSA_SLOT0_OP: u32 = 20; +pub const R_XTENSA_SLOT1_OP: u32 = 21; +pub const R_XTENSA_SLOT2_OP: u32 = 22; +pub const R_XTENSA_SLOT3_OP: u32 = 23; +pub const R_XTENSA_SLOT4_OP: u32 = 24; +pub const R_XTENSA_SLOT5_OP: u32 = 25; +pub const R_XTENSA_SLOT6_OP: u32 = 26; +pub const R_XTENSA_SLOT7_OP: u32 = 27; +pub const R_XTENSA_SLOT8_OP: u32 = 28; +pub const R_XTENSA_SLOT9_OP: u32 = 29; +pub const R_XTENSA_SLOT10_OP: u32 = 30; +pub const R_XTENSA_SLOT11_OP: u32 = 31; +pub const R_XTENSA_SLOT12_OP: u32 = 32; +pub const R_XTENSA_SLOT13_OP: u32 = 33; +pub const R_XTENSA_SLOT14_OP: u32 = 34; +pub const R_XTENSA_SLOT0_ALT: u32 = 35; +pub const R_XTENSA_SLOT1_ALT: u32 = 36; +pub const R_XTENSA_SLOT2_ALT: u32 = 37; +pub const R_XTENSA_SLOT3_ALT: u32 = 38; +pub const R_XTENSA_SLOT4_ALT: u32 = 39; +pub const R_XTENSA_SLOT5_ALT: u32 = 40; +pub const R_XTENSA_SLOT6_ALT: u32 = 41; +pub const R_XTENSA_SLOT7_ALT: u32 = 42; +pub const R_XTENSA_SLOT8_ALT: u32 = 43; +pub const R_XTENSA_SLOT9_ALT: u32 = 44; +pub const R_XTENSA_SLOT10_ALT: u32 = 45; +pub const R_XTENSA_SLOT11_ALT: u32 = 46; +pub const R_XTENSA_SLOT12_ALT: u32 = 47; +pub const R_XTENSA_SLOT13_ALT: u32 = 48; +pub const R_XTENSA_SLOT14_ALT: u32 = 49; +pub const R_XTENSA_TLSDESC_FN: u32 = 50; +pub const R_XTENSA_TLSDESC_ARG: u32 = 51; +pub const R_XTENSA_TLS_DTPOFF: u32 = 52; +pub const R_XTENSA_TLS_TPOFF: u32 = 53; +pub const R_XTENSA_TLS_FUNC: u32 = 54; +pub const R_XTENSA_TLS_ARG: u32 = 55; +pub const R_XTENSA_TLS_CALL: u32 = 56; +pub const R_XTENSA_PDIFF8: u32 = 57; +pub const R_XTENSA_PDIFF16: u32 = 58; +pub const R_XTENSA_PDIFF32: u32 = 59; +pub const R_XTENSA_NDIFF8: u32 = 60; +pub const R_XTENSA_NDIFF16: u32 = 61; +pub const R_XTENSA_NDIFF32: u32 = 62; + +// E2K values for `FileHeader*::e_flags`. +pub const EF_E2K_IPD: u32 = 3; +pub const EF_E2K_X86APP: u32 = 4; +pub const EF_E2K_4MB_PAGES: u32 = 8; +pub const EF_E2K_INCOMPAT: u32 = 16; +pub const EF_E2K_PM: u32 = 32; +pub const EF_E2K_PACK_SEGMENTS: u32 = 64; + +/// Encode `E_E2K_MACH_*` into `FileHeader*::e_flags`. +pub const fn ef_e2k_mach_to_flag(e_flags: u32, x: u32) -> u32 { + (e_flags & 0xffffff) | (x << 24) +} + +/// Decode `E_E2K_MACH_*` from `FileHeader*::e_flags`. +pub const fn ef_e2k_flag_to_mach(e_flags: u32) -> u32 { + e_flags >> 24 +} + +// Codes of supported E2K machines. + +/// -march=generic code. +/// +/// Legacy. Shouldn't be created nowadays. +pub const E_E2K_MACH_BASE: u32 = 0; +/// -march=elbrus-v1 code. +/// +/// Legacy. Shouldn't be created nowadays. +pub const E_E2K_MACH_EV1: u32 = 1; +/// -march=elbrus-v2 code. +pub const E_E2K_MACH_EV2: u32 = 2; +/// -march=elbrus-v3 code. +pub const E_E2K_MACH_EV3: u32 = 3; +/// -march=elbrus-v4 code. +pub const E_E2K_MACH_EV4: u32 = 4; +/// -march=elbrus-v5 code. +pub const E_E2K_MACH_EV5: u32 = 5; +/// -march=elbrus-v6 code. +pub const E_E2K_MACH_EV6: u32 = 6; +/// -march=elbrus-v7 code. +pub const E_E2K_MACH_EV7: u32 = 7; +/// -mtune=elbrus-8c code. +pub const E_E2K_MACH_8C: u32 = 19; +/// -mtune=elbrus-1c+ code. +pub const E_E2K_MACH_1CPLUS: u32 = 20; +/// -mtune=elbrus-12c code. +pub const E_E2K_MACH_12C: u32 = 21; +/// -mtune=elbrus-16c code. +pub const E_E2K_MACH_16C: u32 = 22; +/// -mtune=elbrus-2c3 code. +pub const E_E2K_MACH_2C3: u32 = 23; +/// -mtune=elbrus-48c code. +pub const E_E2K_MACH_48C: u32 = 24; +/// -mtune=elbrus-8v7 code. +pub const E_E2K_MACH_8V7: u32 = 25; + +// E2K values `Rel*::r_type`. + +/// Direct 32 bit. +pub const R_E2K_32_ABS: u32 = 0; +/// PC relative 32 bit. +pub const R_E2K_32_PC: u32 = 2; +/// 32-bit offset of AP GOT entry. +pub const R_E2K_AP_GOT: u32 = 3; +/// 32-bit offset of PL GOT entry. +pub const R_E2K_PL_GOT: u32 = 4; +/// Create PLT entry. +pub const R_E2K_32_JMP_SLOT: u32 = 8; +/// Copy relocation, 32-bit case. +pub const R_E2K_32_COPY: u32 = 9; +/// Adjust by program base, 32-bit case. +pub const R_E2K_32_RELATIVE: u32 = 10; +/// Adjust indirectly by program base, 32-bit case. +pub const R_E2K_32_IRELATIVE: u32 = 11; +/// Size of symbol plus 32-bit addend. +pub const R_E2K_32_SIZE: u32 = 12; +/// Symbol value if resolved by the definition in the same +/// compilation unit or NULL otherwise, 32-bit case. +pub const R_E2K_32_DYNOPT: u32 = 13; +/// Direct 64 bit. +pub const R_E2K_64_ABS: u32 = 50; +/// Direct 64 bit for literal. +pub const R_E2K_64_ABS_LIT: u32 = 51; +/// PC relative 64 bit for literal. +pub const R_E2K_64_PC_LIT: u32 = 54; +/// Create PLT entry, 64-bit case. +pub const R_E2K_64_JMP_SLOT: u32 = 63; +/// Copy relocation, 64-bit case. +pub const R_E2K_64_COPY: u32 = 64; +/// Adjust by program base, 64-bit case. +pub const R_E2K_64_RELATIVE: u32 = 65; +/// Adjust by program base for literal, 64-bit case. +pub const R_E2K_64_RELATIVE_LIT: u32 = 66; +/// Adjust indirectly by program base, 64-bit case. +pub const R_E2K_64_IRELATIVE: u32 = 67; +/// Size of symbol plus 64-bit addend. +pub const R_E2K_64_SIZE: u32 = 68; +/// 64-bit offset of the symbol from GOT. +pub const R_E2K_64_GOTOFF: u32 = 69; + +/// GOT entry for ID of module containing symbol. +pub const R_E2K_TLS_GDMOD: u32 = 70; +/// GOT entry for offset in module TLS block. +pub const R_E2K_TLS_GDREL: u32 = 71; +/// Static TLS block offset GOT entry. +pub const R_E2K_TLS_IE: u32 = 74; +/// Offset relative to static TLS block, 32-bit case. +pub const R_E2K_32_TLS_LE: u32 = 75; +/// Offset relative to static TLS block, 64-bit case. +pub const R_E2K_64_TLS_LE: u32 = 76; +/// ID of module containing symbol, 32-bit case. +pub const R_E2K_TLS_32_DTPMOD: u32 = 80; +/// Offset in module TLS block, 32-bit case. +pub const R_E2K_TLS_32_DTPREL: u32 = 81; +/// ID of module containing symbol, 64-bit case. +pub const R_E2K_TLS_64_DTPMOD: u32 = 82; +/// Offset in module TLS block, 64-bit case. +pub const R_E2K_TLS_64_DTPREL: u32 = 83; +/// Offset in static TLS block, 32-bit case. +pub const R_E2K_TLS_32_TPREL: u32 = 84; +/// Offset in static TLS block, 64-bit case. +pub const R_E2K_TLS_64_TPREL: u32 = 85; + +/// Direct AP. +pub const R_E2K_AP: u32 = 100; +/// Direct PL. +pub const R_E2K_PL: u32 = 101; + +/// 32-bit offset of the symbol's entry in GOT. +pub const R_E2K_GOT: u32 = 108; +/// 32-bit offset of the symbol from GOT. +pub const R_E2K_GOTOFF: u32 = 109; +/// PC relative 28 bit for DISP. +pub const R_E2K_DISP: u32 = 110; +/// Prefetch insn line containing the label (symbol). +pub const R_E2K_PREF: u32 = 111; +/// No reloc. +pub const R_E2K_NONE: u32 = 112; +/// 32-bit offset of the symbol's entry in .got.plt. +pub const R_E2K_GOTPLT: u32 = 114; +/// Is symbol resolved locally during the link. +/// The result is encoded in 5-bit ALS.src1. +pub const R_E2K_ISLOCAL: u32 = 115; +/// Is symbol resloved locally during the link. +/// The result is encoded in a long 32-bit LTS. +pub const R_E2K_ISLOCAL32: u32 = 118; +/// The symbol's offset from GOT encoded within a 64-bit literal. +pub const R_E2K_64_GOTOFF_LIT: u32 = 256; +/// Symbol value if resolved by the definition in the same +/// compilation unit or NULL otherwise, 64-bit case. +pub const R_E2K_64_DYNOPT: u32 = 257; +/// PC relative 64 bit in data. +pub const R_E2K_64_PC: u32 = 258; + +// E2K values for `Dyn32::d_tag`. + +pub const DT_E2K_LAZY: u32 = DT_LOPROC + 1; +pub const DT_E2K_LAZY_GOT: u32 = DT_LOPROC + 3; + +pub const DT_E2K_INIT_GOT: u32 = DT_LOPROC + 0x101c; +pub const DT_E2K_EXPORT_PL: u32 = DT_LOPROC + 0x101d; +pub const DT_E2K_EXPORT_PLSZ: u32 = DT_LOPROC + 0x101e; +pub const DT_E2K_REAL_PLTGOT: u32 = DT_LOPROC + 0x101f; +pub const DT_E2K_NO_SELFINIT: u32 = DT_LOPROC + 0x1020; + +pub const DT_E2K_NUM: u32 = 0x1021; + +#[allow(non_upper_case_globals)] +pub const Tag_File: u8 = 1; +#[allow(non_upper_case_globals)] +pub const Tag_Section: u8 = 2; +#[allow(non_upper_case_globals)] +pub const Tag_Symbol: u8 = 3; + +unsafe_impl_endian_pod!( + FileHeader32, + FileHeader64, + SectionHeader32, + SectionHeader64, + CompressionHeader32, + CompressionHeader64, + Sym32, + Sym64, + Syminfo32, + Syminfo64, + Rel32, + Rel64, + Rela32, + Rela64, + Relr32, + Relr64, + ProgramHeader32, + ProgramHeader64, + Dyn32, + Dyn64, + Versym, + Verdef, + Verdaux, + Verneed, + Vernaux, + NoteHeader32, + NoteHeader64, + HashHeader, + GnuHashHeader, +);
diff --git a/chromium_crates_io/vendor/object-v0_37/src/endian.rs b/chromium_crates_io/vendor/object-v0_37/src/endian.rs new file mode 100644 index 0000000..9081879 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/endian.rs
@@ -0,0 +1,831 @@ +//! Types for compile-time and run-time endianness. + +use crate::pod::Pod; +use core::fmt::{self, Debug}; +use core::marker::PhantomData; + +/// A trait for using an endianness specification. +/// +/// Provides methods for converting between the specified endianness and +/// the native endianness of the target machine. +/// +/// This trait does not require that the endianness is known at compile time. +pub trait Endian: Debug + Default + Clone + Copy + PartialEq + Eq + 'static { + /// Construct a specification for the endianness of some values. + /// + /// Returns `None` if the type does not support specifying the given endianness. + fn from_big_endian(big_endian: bool) -> Option<Self>; + + /// Construct a specification for the endianness of some values. + /// + /// Returns `None` if the type does not support specifying the given endianness. + fn from_little_endian(little_endian: bool) -> Option<Self> { + Self::from_big_endian(!little_endian) + } + + /// 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() + } + + /// Converts an unsigned 16 bit integer to native endian. + #[inline] + fn read_u16(self, n: u16) -> u16 { + if self.is_big_endian() { + u16::from_be(n) + } else { + u16::from_le(n) + } + } + + /// Converts an unsigned 32 bit integer to native endian. + #[inline] + fn read_u32(self, n: u32) -> u32 { + if self.is_big_endian() { + u32::from_be(n) + } else { + u32::from_le(n) + } + } + + /// Converts an unsigned 64 bit integer to native endian. + #[inline] + fn read_u64(self, n: u64) -> u64 { + if self.is_big_endian() { + u64::from_be(n) + } else { + u64::from_le(n) + } + } + + /// Converts a signed 16 bit integer to native endian. + #[inline] + fn read_i16(self, n: i16) -> i16 { + if self.is_big_endian() { + i16::from_be(n) + } else { + i16::from_le(n) + } + } + + /// Converts a signed 32 bit integer to native endian. + #[inline] + fn read_i32(self, n: i32) -> i32 { + if self.is_big_endian() { + i32::from_be(n) + } else { + i32::from_le(n) + } + } + + /// Converts a signed 64 bit integer to native endian. + #[inline] + fn read_i64(self, n: i64) -> i64 { + if self.is_big_endian() { + i64::from_be(n) + } else { + i64::from_le(n) + } + } + + /// Converts an unaligned unsigned 16 bit integer to native endian. + #[inline] + fn read_u16_bytes(self, n: [u8; 2]) -> u16 { + if self.is_big_endian() { + u16::from_be_bytes(n) + } else { + u16::from_le_bytes(n) + } + } + + /// Converts an unaligned unsigned 32 bit integer to native endian. + #[inline] + fn read_u32_bytes(self, n: [u8; 4]) -> u32 { + if self.is_big_endian() { + u32::from_be_bytes(n) + } else { + u32::from_le_bytes(n) + } + } + + /// Converts an unaligned unsigned 64 bit integer to native endian. + #[inline] + fn read_u64_bytes(self, n: [u8; 8]) -> u64 { + if self.is_big_endian() { + u64::from_be_bytes(n) + } else { + u64::from_le_bytes(n) + } + } + + /// Converts an unaligned signed 16 bit integer to native endian. + #[inline] + fn read_i16_bytes(self, n: [u8; 2]) -> i16 { + if self.is_big_endian() { + i16::from_be_bytes(n) + } else { + i16::from_le_bytes(n) + } + } + + /// Converts an unaligned signed 32 bit integer to native endian. + #[inline] + fn read_i32_bytes(self, n: [u8; 4]) -> i32 { + if self.is_big_endian() { + i32::from_be_bytes(n) + } else { + i32::from_le_bytes(n) + } + } + + /// Converts an unaligned signed 64 bit integer to native endian. + #[inline] + fn read_i64_bytes(self, n: [u8; 8]) -> i64 { + if self.is_big_endian() { + i64::from_be_bytes(n) + } else { + i64::from_le_bytes(n) + } + } + + /// Converts an unsigned 16 bit integer from native endian. + #[inline] + fn write_u16(self, n: u16) -> u16 { + if self.is_big_endian() { + u16::to_be(n) + } else { + u16::to_le(n) + } + } + + /// Converts an unsigned 32 bit integer from native endian. + #[inline] + fn write_u32(self, n: u32) -> u32 { + if self.is_big_endian() { + u32::to_be(n) + } else { + u32::to_le(n) + } + } + + /// Converts an unsigned 64 bit integer from native endian. + #[inline] + fn write_u64(self, n: u64) -> u64 { + if self.is_big_endian() { + u64::to_be(n) + } else { + u64::to_le(n) + } + } + + /// Converts a signed 16 bit integer from native endian. + #[inline] + fn write_i16(self, n: i16) -> i16 { + if self.is_big_endian() { + i16::to_be(n) + } else { + i16::to_le(n) + } + } + + /// Converts a signed 32 bit integer from native endian. + #[inline] + fn write_i32(self, n: i32) -> i32 { + if self.is_big_endian() { + i32::to_be(n) + } else { + i32::to_le(n) + } + } + + /// Converts a signed 64 bit integer from native endian. + #[inline] + fn write_i64(self, n: i64) -> i64 { + if self.is_big_endian() { + i64::to_be(n) + } else { + i64::to_le(n) + } + } + + /// Converts an unaligned unsigned 16 bit integer from native endian. + #[inline] + fn write_u16_bytes(self, n: u16) -> [u8; 2] { + if self.is_big_endian() { + u16::to_be_bytes(n) + } else { + u16::to_le_bytes(n) + } + } + + /// Converts an unaligned unsigned 32 bit integer from native endian. + #[inline] + fn write_u32_bytes(self, n: u32) -> [u8; 4] { + if self.is_big_endian() { + u32::to_be_bytes(n) + } else { + u32::to_le_bytes(n) + } + } + + /// Converts an unaligned unsigned 64 bit integer from native endian. + #[inline] + fn write_u64_bytes(self, n: u64) -> [u8; 8] { + if self.is_big_endian() { + u64::to_be_bytes(n) + } else { + u64::to_le_bytes(n) + } + } + + /// Converts an unaligned signed 16 bit integer from native endian. + #[inline] + fn write_i16_bytes(self, n: i16) -> [u8; 2] { + if self.is_big_endian() { + i16::to_be_bytes(n) + } else { + i16::to_le_bytes(n) + } + } + + /// Converts an unaligned signed 32 bit integer from native endian. + #[inline] + fn write_i32_bytes(self, n: i32) -> [u8; 4] { + if self.is_big_endian() { + i32::to_be_bytes(n) + } else { + i32::to_le_bytes(n) + } + } + + /// Converts an unaligned signed 64 bit integer from native endian. + #[inline] + fn write_i64_bytes(self, n: i64) -> [u8; 8] { + if self.is_big_endian() { + i64::to_be_bytes(n) + } else { + i64::to_le_bytes(n) + } + } +} + +/// An endianness that is selectable at run-time. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Endianness { + /// Little endian byte order. + Little, + /// Big endian byte order. + Big, +} + +impl Default for Endianness { + #[cfg(target_endian = "little")] + #[inline] + fn default() -> Endianness { + Endianness::Little + } + + #[cfg(target_endian = "big")] + #[inline] + fn default() -> Endianness { + Endianness::Big + } +} + +impl Endian for Endianness { + #[inline] + fn from_big_endian(big_endian: bool) -> Option<Self> { + Some(if big_endian { + Endianness::Big + } else { + Endianness::Little + }) + } + + #[inline] + fn is_big_endian(self) -> bool { + self != Endianness::Little + } +} + +/// Compile-time little endian byte order. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LittleEndian; + +impl Default for LittleEndian { + #[inline] + fn default() -> LittleEndian { + LittleEndian + } +} + +impl Endian for LittleEndian { + #[inline] + fn from_big_endian(big_endian: bool) -> Option<Self> { + if big_endian { + None + } else { + Some(LittleEndian) + } + } + + #[inline] + fn is_big_endian(self) -> bool { + false + } +} + +/// Compile-time big endian byte order. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BigEndian; + +impl Default for BigEndian { + #[inline] + fn default() -> BigEndian { + BigEndian + } +} + +impl Endian for BigEndian { + #[inline] + fn from_big_endian(big_endian: bool) -> Option<Self> { + if big_endian { + Some(BigEndian) + } else { + None + } + } + + #[inline] + fn is_big_endian(self) -> bool { + true + } +} + +/// The native endianness 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 endianness 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; + +macro_rules! unsafe_impl_endian_pod { + ($($struct_name:ident),+ $(,)?) => { + $( + unsafe impl<E: Endian> Pod for $struct_name<E> { } + )+ + } +} + +#[cfg(not(feature = "unaligned"))] +mod aligned { + use super::{fmt, Endian, PhantomData, Pod}; + + /// A `u16` value with an externally specified endianness of type `E`. + #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(transparent)] + pub struct U16<E: Endian>(u16, PhantomData<E>); + + impl<E: Endian> U16<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 2]) -> Self { + Self(u16::from_ne_bytes(n), PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: u16) -> Self { + Self(e.write_u16(n), PhantomData) + } + + /// Return the value as a native endian value. + pub fn get(self, e: E) -> u16 { + e.read_u16(self.0) + } + + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: u16) { + self.0 = e.write_u16(n); + } + } + + /// A `u32` value with an externally specified endianness of type `E`. + #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(transparent)] + pub struct U32<E: Endian>(u32, PhantomData<E>); + + impl<E: Endian> U32<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 4]) -> Self { + Self(u32::from_ne_bytes(n), PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: u32) -> Self { + Self(e.write_u32(n), PhantomData) + } + /// Return the value as a native endian value. + pub fn get(self, e: E) -> u32 { + e.read_u32(self.0) + } + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: u32) { + self.0 = e.write_u32(n); + } + } + + /// A `u64` value with an externally specified endianness of type `E`. + #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(transparent)] + pub struct U64<E: Endian>(u64, PhantomData<E>); + + impl<E: Endian> U64<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 8]) -> Self { + Self(u64::from_ne_bytes(n), PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: u64) -> Self { + Self(e.write_u64(n), PhantomData) + } + /// Return the value as a native endian value. + pub fn get(self, e: E) -> u64 { + e.read_u64(self.0) + } + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: u64) { + self.0 = e.write_u64(n); + } + } + + /// An `i16` value with an externally specified endianness of type `E`. + #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(transparent)] + pub struct I16<E: Endian>(i16, PhantomData<E>); + + impl<E: Endian> I16<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 2]) -> Self { + Self(i16::from_ne_bytes(n), PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: i16) -> Self { + Self(e.write_i16(n), PhantomData) + } + /// Return the value as a native endian value. + pub fn get(self, e: E) -> i16 { + e.read_i16(self.0) + } + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: i16) { + self.0 = e.write_i16(n); + } + } + + /// An `i32` value with an externally specified endianness of type `E`. + #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(transparent)] + pub struct I32<E: Endian>(i32, PhantomData<E>); + + impl<E: Endian> I32<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 4]) -> Self { + Self(i32::from_ne_bytes(n), PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: i32) -> Self { + Self(e.write_i32(n), PhantomData) + } + /// Return the value as a native endian value. + pub fn get(self, e: E) -> i32 { + e.read_i32(self.0) + } + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: i32) { + self.0 = e.write_i32(n); + } + } + + /// An `i64` value with an externally specified endianness of type `E`. + #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(transparent)] + pub struct I64<E: Endian>(i64, PhantomData<E>); + + impl<E: Endian> I64<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 8]) -> Self { + Self(i64::from_ne_bytes(n), PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: i64) -> Self { + Self(e.write_i64(n), PhantomData) + } + /// Return the value as a native endian value. + pub fn get(self, e: E) -> i64 { + e.read_i64(self.0) + } + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: i64) { + self.0 = e.write_i64(n); + } + } + + impl<E: Endian> fmt::Debug for U16<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U16({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for U32<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U32({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for U64<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U64({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for I16<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "I16({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for I32<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "I32({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for I64<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "I64({:x})", self.0) + } + } + + unsafe_impl_endian_pod!(U16, U32, U64, I16, I32, I64); +} + +#[cfg(not(feature = "unaligned"))] +pub use aligned::*; + +/// A `u16` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type U16<E> = U16Bytes<E>; + +/// A `u32` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type U32<E> = U32Bytes<E>; + +/// A `u64` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type U64<E> = U64Bytes<E>; + +/// An `i16` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type I16<E> = I16Bytes<E>; + +/// An `i32` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type I32<E> = I32Bytes<E>; + +/// An `i64` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type I64<E> = I64Bytes<E>; + +/// An unaligned `u16` value with an externally specified endianness of type `E`. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct U16Bytes<E: Endian>([u8; 2], PhantomData<E>); + +impl<E: Endian> U16Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 2]) -> Self { + Self(n, PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: u16) -> Self { + Self(e.write_u16_bytes(n), PhantomData) + } + + /// Return the value as a native endian value. + pub fn get(self, e: E) -> u16 { + e.read_u16_bytes(self.0) + } + + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: u16) { + self.0 = e.write_u16_bytes(n); + } +} + +/// An unaligned `u32` value with an externally specified endianness of type `E`. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct U32Bytes<E: Endian>([u8; 4], PhantomData<E>); + +impl<E: Endian> U32Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 4]) -> Self { + Self(n, PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: u32) -> Self { + Self(e.write_u32_bytes(n), PhantomData) + } + + /// Return the value as a native endian value. + pub fn get(self, e: E) -> u32 { + e.read_u32_bytes(self.0) + } + + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: u32) { + self.0 = e.write_u32_bytes(n); + } +} + +/// An unaligned `u64` value with an externally specified endianness of type `E`. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct U64Bytes<E: Endian>([u8; 8], PhantomData<E>); + +impl<E: Endian> U64Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 8]) -> Self { + Self(n, PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: u64) -> Self { + Self(e.write_u64_bytes(n), PhantomData) + } + + /// Return the value as a native endian value. + pub fn get(self, e: E) -> u64 { + e.read_u64_bytes(self.0) + } + + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: u64) { + self.0 = e.write_u64_bytes(n); + } +} + +/// An unaligned `i16` value with an externally specified endianness of type `E`. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct I16Bytes<E: Endian>([u8; 2], PhantomData<E>); + +impl<E: Endian> I16Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 2]) -> Self { + Self(n, PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: i16) -> Self { + Self(e.write_i16_bytes(n), PhantomData) + } + + /// Return the value as a native endian value. + pub fn get(self, e: E) -> i16 { + e.read_i16_bytes(self.0) + } + + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: i16) { + self.0 = e.write_i16_bytes(n); + } +} + +/// An unaligned `i32` value with an externally specified endianness of type `E`. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct I32Bytes<E: Endian>([u8; 4], PhantomData<E>); + +impl<E: Endian> I32Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 4]) -> Self { + Self(n, PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: i32) -> Self { + Self(e.write_i32_bytes(n), PhantomData) + } + + /// Return the value as a native endian value. + pub fn get(self, e: E) -> i32 { + e.read_i32_bytes(self.0) + } + + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: i32) { + self.0 = e.write_i32_bytes(n); + } +} + +/// An unaligned `i64` value with an externally specified endianness of type `E`. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct I64Bytes<E: Endian>([u8; 8], PhantomData<E>); + +impl<E: Endian> I64Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub const fn from_bytes(n: [u8; 8]) -> Self { + Self(n, PhantomData) + } + + /// Construct a new value given a native endian value. + pub fn new(e: E, n: i64) -> Self { + Self(e.write_i64_bytes(n), PhantomData) + } + + /// Return the value as a native endian value. + pub fn get(self, e: E) -> i64 { + e.read_i64_bytes(self.0) + } + + /// Set the value given a native endian value. + pub fn set(&mut self, e: E, n: i64) { + self.0 = e.write_i64_bytes(n); + } +} + +impl<E: Endian> fmt::Debug for U16Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U16({:x}, {:x})", self.0[0], self.0[1],) + } +} + +impl<E: Endian> fmt::Debug for U32Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "U32({:x}, {:x}, {:x}, {:x})", + self.0[0], self.0[1], self.0[2], self.0[3], + ) + } +} + +impl<E: Endian> fmt::Debug for U64Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "U64({:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x})", + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], + ) + } +} + +impl<E: Endian> fmt::Debug for I16Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "I16({:x}, {:x})", self.0[0], self.0[1],) + } +} + +impl<E: Endian> fmt::Debug for I32Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "I32({:x}, {:x}, {:x}, {:x})", + self.0[0], self.0[1], self.0[2], self.0[3], + ) + } +} + +impl<E: Endian> fmt::Debug for I64Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "I64({:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x})", + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], + ) + } +} + +unsafe_impl_endian_pod!(U16Bytes, U32Bytes, U64Bytes, I16Bytes, I32Bytes, I64Bytes);
diff --git a/chromium_crates_io/vendor/object-v0_37/src/lib.rs b/chromium_crates_io/vendor/object-v0_37/src/lib.rs new file mode 100644 index 0000000..9a2fdd4 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/lib.rs
@@ -0,0 +1,94 @@ +//! # `object` +//! +//! The `object` crate provides a unified interface to working with object files +//! across platforms. It supports reading relocatable object files and executable files, +//! and writing relocatable object files and some executable files. +//! +//! ## Raw struct definitions +//! +//! Raw structs are defined for: [ELF](elf), [Mach-O](macho), [PE/COFF](pe), +//! [XCOFF](xcoff), [archive]. +//! Types and traits for zerocopy support are defined in the [`pod`] and [`endian`] modules. +//! +//! ## Unified read API +//! +//! The [`read`] module provides a unified read API using the [`read::Object`] trait. +//! There is an implementation of this trait for [`read::File`], which allows reading any +//! file format, as well as implementations for each file format. +//! +//! ## Low level read API +//! +//! The [`read#modules`] submodules define helpers that operate on the raw structs. +//! These can be used instead of the unified API, or in conjunction with it to access +//! details that are not available via the unified API. +//! +//! ## Unified write API +//! +//! The [`mod@write`] module provides a unified write API for relocatable object files +//! using [`write::Object`]. This does not support writing executable files. +//! +//! ## Low level write API +//! +//! The [`mod@write#modules`] submodules define helpers for writing the raw structs. +//! +//! ## Build API +//! +//! The [`mod@build`] submodules define helpers for building object files, either from +//! scratch or by modifying existing files. +//! +//! ## Shared definitions +//! +//! The crate provides a number of definitions that are used by both the read and write +//! APIs. These are defined at the top level module, but none of these are the main entry +//! points of the crate. + +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![no_std] +#![warn(rust_2018_idioms)] + +#[cfg(feature = "cargo-all")] +compile_error!("'--all-features' is not supported; use '--features all' instead"); + +#[cfg(any(feature = "read_core", feature = "write_core"))] +#[allow(unused_imports)] +#[macro_use] +extern crate alloc; + +#[cfg(feature = "std")] +#[allow(unused_imports)] +#[macro_use] +extern crate std; + +mod common; +pub use common::*; + +#[macro_use] +pub mod endian; +pub use endian::*; + +#[macro_use] +pub mod pod; +pub use pod::*; + +#[cfg(feature = "read_core")] +pub mod read; +#[cfg(feature = "read_core")] +pub use read::*; + +#[cfg(feature = "write_core")] +pub mod write; + +#[cfg(feature = "build_core")] +pub mod build; + +#[cfg(feature = "archive")] +pub mod archive; +#[cfg(feature = "elf")] +pub mod elf; +#[cfg(feature = "macho")] +pub mod macho; +#[cfg(any(feature = "coff", feature = "pe"))] +pub mod pe; +#[cfg(feature = "xcoff")] +pub mod xcoff;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/macho.rs b/chromium_crates_io/vendor/object-v0_37/src/macho.rs new file mode 100644 index 0000000..0e12616 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/macho.rs
@@ -0,0 +1,3606 @@ +//! Mach-O definitions. +//! +//! These definitions are independent of read/write support, although we do implement +//! some traits useful for those. +//! +//! This module is based heavily on header files from MacOSX11.1.sdk. + +#![allow(missing_docs)] + +use crate::endian::{BigEndian, Endian, U64Bytes, U16, U32, U64}; +use crate::pod::Pod; + +// Definitions from "/usr/include/mach/machine.h". + +/* + * Capability bits used in the definition of cpu_type. + */ + +/// mask for architecture bits +pub const CPU_ARCH_MASK: u32 = 0xff00_0000; +/// 64 bit ABI +pub const CPU_ARCH_ABI64: u32 = 0x0100_0000; +/// ABI for 64-bit hardware with 32-bit types; LP32 +pub const CPU_ARCH_ABI64_32: u32 = 0x0200_0000; + +/* + * Machine types known by all. + */ + +pub const CPU_TYPE_ANY: u32 = !0; + +pub const CPU_TYPE_VAX: u32 = 1; +pub const CPU_TYPE_MC680X0: u32 = 6; +pub const CPU_TYPE_X86: u32 = 7; +pub const CPU_TYPE_X86_64: u32 = CPU_TYPE_X86 | CPU_ARCH_ABI64; +pub const CPU_TYPE_MIPS: u32 = 8; +pub const CPU_TYPE_MC98000: u32 = 10; +pub const CPU_TYPE_HPPA: u32 = 11; +pub const CPU_TYPE_ARM: u32 = 12; +pub const CPU_TYPE_ARM64: u32 = CPU_TYPE_ARM | CPU_ARCH_ABI64; +pub const CPU_TYPE_ARM64_32: u32 = CPU_TYPE_ARM | CPU_ARCH_ABI64_32; +pub const CPU_TYPE_MC88000: u32 = 13; +pub const CPU_TYPE_SPARC: u32 = 14; +pub const CPU_TYPE_I860: u32 = 15; +pub const CPU_TYPE_ALPHA: u32 = 16; +pub const CPU_TYPE_POWERPC: u32 = 18; +pub const CPU_TYPE_POWERPC64: u32 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64; + +/* + * Capability bits used in the definition of cpu_subtype. + */ +/// mask for feature flags +pub const CPU_SUBTYPE_MASK: u32 = 0xff00_0000; +/// 64 bit libraries +pub const CPU_SUBTYPE_LIB64: u32 = 0x8000_0000; +/// pointer authentication with versioned ABI +pub const CPU_SUBTYPE_PTRAUTH_ABI: u32 = 0x8000_0000; + +/// When selecting a slice, ANY will pick the slice with the best +/// grading for the selected cpu_type_t, unlike the "ALL" subtypes, +/// which are the slices that can run on any hardware for that cpu type. +pub const CPU_SUBTYPE_ANY: u32 = !0; + +/* + * Object files that are hand-crafted to run on any + * implementation of an architecture are tagged with + * CPU_SUBTYPE_MULTIPLE. This functions essentially the same as + * the "ALL" subtype of an architecture except that it allows us + * to easily find object files that may need to be modified + * whenever a new implementation of an architecture comes out. + * + * It is the responsibility of the implementor to make sure the + * software handles unsupported implementations elegantly. + */ +pub const CPU_SUBTYPE_MULTIPLE: u32 = !0; +pub const CPU_SUBTYPE_LITTLE_ENDIAN: u32 = 0; +pub const CPU_SUBTYPE_BIG_ENDIAN: u32 = 1; + +/* + * VAX subtypes (these do *not* necessary conform to the actual cpu + * ID assigned by DEC available via the SID register). + */ + +pub const CPU_SUBTYPE_VAX_ALL: u32 = 0; +pub const CPU_SUBTYPE_VAX780: u32 = 1; +pub const CPU_SUBTYPE_VAX785: u32 = 2; +pub const CPU_SUBTYPE_VAX750: u32 = 3; +pub const CPU_SUBTYPE_VAX730: u32 = 4; +pub const CPU_SUBTYPE_UVAXI: u32 = 5; +pub const CPU_SUBTYPE_UVAXII: u32 = 6; +pub const CPU_SUBTYPE_VAX8200: u32 = 7; +pub const CPU_SUBTYPE_VAX8500: u32 = 8; +pub const CPU_SUBTYPE_VAX8600: u32 = 9; +pub const CPU_SUBTYPE_VAX8650: u32 = 10; +pub const CPU_SUBTYPE_VAX8800: u32 = 11; +pub const CPU_SUBTYPE_UVAXIII: u32 = 12; + +/* + * 680x0 subtypes + * + * The subtype definitions here are unusual for historical reasons. + * NeXT used to consider 68030 code as generic 68000 code. For + * backwards compatibility: + * + * CPU_SUBTYPE_MC68030 symbol has been preserved for source code + * compatibility. + * + * CPU_SUBTYPE_MC680x0_ALL has been defined to be the same + * subtype as CPU_SUBTYPE_MC68030 for binary comatability. + * + * CPU_SUBTYPE_MC68030_ONLY has been added to allow new object + * files to be tagged as containing 68030-specific instructions. + */ + +pub const CPU_SUBTYPE_MC680X0_ALL: u32 = 1; +// compat +pub const CPU_SUBTYPE_MC68030: u32 = 1; +pub const CPU_SUBTYPE_MC68040: u32 = 2; +pub const CPU_SUBTYPE_MC68030_ONLY: u32 = 3; + +/* + * I386 subtypes + */ + +#[inline] +pub const fn cpu_subtype_intel(f: u32, m: u32) -> u32 { + f + (m << 4) +} + +pub const CPU_SUBTYPE_I386_ALL: u32 = cpu_subtype_intel(3, 0); +pub const CPU_SUBTYPE_386: u32 = cpu_subtype_intel(3, 0); +pub const CPU_SUBTYPE_486: u32 = cpu_subtype_intel(4, 0); +pub const CPU_SUBTYPE_486SX: u32 = cpu_subtype_intel(4, 8); +pub const CPU_SUBTYPE_586: u32 = cpu_subtype_intel(5, 0); +pub const CPU_SUBTYPE_PENT: u32 = cpu_subtype_intel(5, 0); +pub const CPU_SUBTYPE_PENTPRO: u32 = cpu_subtype_intel(6, 1); +pub const CPU_SUBTYPE_PENTII_M3: u32 = cpu_subtype_intel(6, 3); +pub const CPU_SUBTYPE_PENTII_M5: u32 = cpu_subtype_intel(6, 5); +pub const CPU_SUBTYPE_CELERON: u32 = cpu_subtype_intel(7, 6); +pub const CPU_SUBTYPE_CELERON_MOBILE: u32 = cpu_subtype_intel(7, 7); +pub const CPU_SUBTYPE_PENTIUM_3: u32 = cpu_subtype_intel(8, 0); +pub const CPU_SUBTYPE_PENTIUM_3_M: u32 = cpu_subtype_intel(8, 1); +pub const CPU_SUBTYPE_PENTIUM_3_XEON: u32 = cpu_subtype_intel(8, 2); +pub const CPU_SUBTYPE_PENTIUM_M: u32 = cpu_subtype_intel(9, 0); +pub const CPU_SUBTYPE_PENTIUM_4: u32 = cpu_subtype_intel(10, 0); +pub const CPU_SUBTYPE_PENTIUM_4_M: u32 = cpu_subtype_intel(10, 1); +pub const CPU_SUBTYPE_ITANIUM: u32 = cpu_subtype_intel(11, 0); +pub const CPU_SUBTYPE_ITANIUM_2: u32 = cpu_subtype_intel(11, 1); +pub const CPU_SUBTYPE_XEON: u32 = cpu_subtype_intel(12, 0); +pub const CPU_SUBTYPE_XEON_MP: u32 = cpu_subtype_intel(12, 1); + +#[inline] +pub const fn cpu_subtype_intel_family(x: u32) -> u32 { + x & 15 +} +pub const CPU_SUBTYPE_INTEL_FAMILY_MAX: u32 = 15; + +#[inline] +pub const fn cpu_subtype_intel_model(x: u32) -> u32 { + x >> 4 +} +pub const CPU_SUBTYPE_INTEL_MODEL_ALL: u32 = 0; + +/* + * X86 subtypes. + */ + +pub const CPU_SUBTYPE_X86_ALL: u32 = 3; +pub const CPU_SUBTYPE_X86_64_ALL: u32 = 3; +pub const CPU_SUBTYPE_X86_ARCH1: u32 = 4; +/// Haswell feature subset +pub const CPU_SUBTYPE_X86_64_H: u32 = 8; + +/* + * Mips subtypes. + */ + +pub const CPU_SUBTYPE_MIPS_ALL: u32 = 0; +pub const CPU_SUBTYPE_MIPS_R2300: u32 = 1; +pub const CPU_SUBTYPE_MIPS_R2600: u32 = 2; +pub const CPU_SUBTYPE_MIPS_R2800: u32 = 3; +/// pmax +pub const CPU_SUBTYPE_MIPS_R2000A: u32 = 4; +pub const CPU_SUBTYPE_MIPS_R2000: u32 = 5; +/// 3max +pub const CPU_SUBTYPE_MIPS_R3000A: u32 = 6; +pub const CPU_SUBTYPE_MIPS_R3000: u32 = 7; + +/* + * MC98000 (PowerPC) subtypes + */ +pub const CPU_SUBTYPE_MC98000_ALL: u32 = 0; +pub const CPU_SUBTYPE_MC98601: u32 = 1; + +/* + * HPPA subtypes for Hewlett-Packard HP-PA family of + * risc processors. Port by NeXT to 700 series. + */ + +pub const CPU_SUBTYPE_HPPA_ALL: u32 = 0; +pub const CPU_SUBTYPE_HPPA_7100LC: u32 = 1; + +/* + * MC88000 subtypes. + */ +pub const CPU_SUBTYPE_MC88000_ALL: u32 = 0; +pub const CPU_SUBTYPE_MC88100: u32 = 1; +pub const CPU_SUBTYPE_MC88110: u32 = 2; + +/* + * SPARC subtypes + */ +pub const CPU_SUBTYPE_SPARC_ALL: u32 = 0; + +/* + * I860 subtypes + */ +pub const CPU_SUBTYPE_I860_ALL: u32 = 0; +pub const CPU_SUBTYPE_I860_860: u32 = 1; + +/* + * PowerPC subtypes + */ +pub const CPU_SUBTYPE_POWERPC_ALL: u32 = 0; +pub const CPU_SUBTYPE_POWERPC_601: u32 = 1; +pub const CPU_SUBTYPE_POWERPC_602: u32 = 2; +pub const CPU_SUBTYPE_POWERPC_603: u32 = 3; +pub const CPU_SUBTYPE_POWERPC_603E: u32 = 4; +pub const CPU_SUBTYPE_POWERPC_603EV: u32 = 5; +pub const CPU_SUBTYPE_POWERPC_604: u32 = 6; +pub const CPU_SUBTYPE_POWERPC_604E: u32 = 7; +pub const CPU_SUBTYPE_POWERPC_620: u32 = 8; +pub const CPU_SUBTYPE_POWERPC_750: u32 = 9; +pub const CPU_SUBTYPE_POWERPC_7400: u32 = 10; +pub const CPU_SUBTYPE_POWERPC_7450: u32 = 11; +pub const CPU_SUBTYPE_POWERPC_970: u32 = 100; + +/* + * ARM subtypes + */ +pub const CPU_SUBTYPE_ARM_ALL: u32 = 0; +pub const CPU_SUBTYPE_ARM_V4T: u32 = 5; +pub const CPU_SUBTYPE_ARM_V6: u32 = 6; +pub const CPU_SUBTYPE_ARM_V5TEJ: u32 = 7; +pub const CPU_SUBTYPE_ARM_XSCALE: u32 = 8; +/// ARMv7-A and ARMv7-R +pub const CPU_SUBTYPE_ARM_V7: u32 = 9; +/// Cortex A9 +pub const CPU_SUBTYPE_ARM_V7F: u32 = 10; +/// Swift +pub const CPU_SUBTYPE_ARM_V7S: u32 = 11; +pub const CPU_SUBTYPE_ARM_V7K: u32 = 12; +pub const CPU_SUBTYPE_ARM_V8: u32 = 13; +/// Not meant to be run under xnu +pub const CPU_SUBTYPE_ARM_V6M: u32 = 14; +/// Not meant to be run under xnu +pub const CPU_SUBTYPE_ARM_V7M: u32 = 15; +/// Not meant to be run under xnu +pub const CPU_SUBTYPE_ARM_V7EM: u32 = 16; +/// Not meant to be run under xnu +pub const CPU_SUBTYPE_ARM_V8M: u32 = 17; + +/* + * ARM64 subtypes + */ +pub const CPU_SUBTYPE_ARM64_ALL: u32 = 0; +pub const CPU_SUBTYPE_ARM64_V8: u32 = 1; +pub const CPU_SUBTYPE_ARM64E: u32 = 2; + +/* + * ARM64_32 subtypes + */ +pub const CPU_SUBTYPE_ARM64_32_ALL: u32 = 0; +pub const CPU_SUBTYPE_ARM64_32_V8: u32 = 1; + +// Definitions from "/usr/include/mach/vm_prot.h". + +/// read permission +pub const VM_PROT_READ: u32 = 0x01; +/// write permission +pub const VM_PROT_WRITE: u32 = 0x02; +/// execute permission +pub const VM_PROT_EXECUTE: u32 = 0x04; + +// Definitions from ptrauth.h + +/// The key used to sign a pointer for authentication. +/// +/// The variant values correspond to the values used in the +/// `ptrauth_key` enum in `ptrauth.h`. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PtrauthKey { + /// Instruction key A. + IA = 0, + /// Instruction key B. + IB = 1, + /// Data key A. + DA = 2, + /// Data key B. + DB = 3, +} + +// Definitions from https://opensource.apple.com/source/dyld/dyld-210.2.3/launch-cache/dyld_cache_format.h.auto.html + +/// The dyld cache header. +/// Corresponds to struct dyld_cache_header from dyld_cache_format.h. +/// This header has grown over time. Only the fields up to and including dyld_base_address +/// are guaranteed to be present. For all other fields, check the header size before +/// accessing the field. The header size is stored in mapping_offset; the mappings start +/// right after the theader. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheHeader<E: Endian> { + /// e.g. "dyld_v0 i386" + pub magic: [u8; 16], + /// file offset to first dyld_cache_mapping_info + pub mapping_offset: U32<E>, + /// number of dyld_cache_mapping_info entries + pub mapping_count: U32<E>, + /// UNUSED: moved to imagesOffset to prevent older dsc_extarctors from crashing + pub images_offset_old: U32<E>, + /// UNUSED: moved to imagesCount to prevent older dsc_extarctors from crashing + pub images_count_old: U32<E>, + /// base address of dyld when cache was built + pub dyld_base_address: U64<E>, + /// file offset of code signature blob + pub code_signature_offset: U64<E>, + /// size of code signature blob (zero means to end of file) + pub code_signature_size: U64<E>, + /// unused. Used to be file offset of kernel slid info + pub slide_info_offset_unused: U64<E>, + /// unused. Used to be size of kernel slid info + pub slide_info_size_unused: U64<E>, + /// file offset of where local symbols are stored + pub local_symbols_offset: U64<E>, + /// size of local symbols information + pub local_symbols_size: U64<E>, + /// unique value for each shared cache file + pub uuid: [u8; 16], + /// 0 for development, 1 for production, 2 for multi-cache + pub cache_type: U64<E>, + /// file offset to table of uint64_t pool addresses + pub branch_pools_offset: U32<E>, + /// number of uint64_t entries + pub branch_pools_count: U32<E>, + /// (unslid) address of mach_header of dyld in cache + pub dyld_in_cache_mh: U64<E>, + /// (unslid) address of entry point (_dyld_start) of dyld in cache + pub dyld_in_cache_entry: U64<E>, + /// file offset to first dyld_cache_image_text_info + pub images_text_offset: U64<E>, + /// number of dyld_cache_image_text_info entries + pub images_text_count: U64<E>, + /// (unslid) address of dyld_cache_patch_info + pub patch_info_addr: U64<E>, + /// Size of all of the patch information pointed to via the dyld_cache_patch_info + pub patch_info_size: U64<E>, + /// unused + pub other_image_group_addr_unused: U64<E>, + /// unused + pub other_image_group_size_unused: U64<E>, + /// (unslid) address of list of program launch closures + pub prog_closures_addr: U64<E>, + /// size of list of program launch closures + pub prog_closures_size: U64<E>, + /// (unslid) address of trie of indexes into program launch closures + pub prog_closures_trie_addr: U64<E>, + /// size of trie of indexes into program launch closures + pub prog_closures_trie_size: U64<E>, + /// platform number (macOS=1, etc) + pub platform: U32<E>, + // bitfield of values + pub flags: U32<E>, + /// base load address of cache if not slid + pub shared_region_start: U64<E>, + /// overall size required to map the cache and all subCaches, if any + pub shared_region_size: U64<E>, + /// runtime slide of cache can be between zero and this value + pub max_slide: U64<E>, + /// (unslid) address of ImageArray for dylibs in this cache + pub dylibs_image_array_addr: U64<E>, + /// size of ImageArray for dylibs in this cache + pub dylibs_image_array_size: U64<E>, + /// (unslid) address of trie of indexes of all cached dylibs + pub dylibs_trie_addr: U64<E>, + /// size of trie of cached dylib paths + pub dylibs_trie_size: U64<E>, + /// (unslid) address of ImageArray for dylibs and bundles with dlopen closures + pub other_image_array_addr: U64<E>, + /// size of ImageArray for dylibs and bundles with dlopen closures + pub other_image_array_size: U64<E>, + /// (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures + pub other_trie_addr: U64<E>, + /// size of trie of dylibs and bundles with dlopen closures + pub other_trie_size: U64<E>, + /// file offset to first dyld_cache_mapping_and_slide_info + pub mapping_with_slide_offset: U32<E>, + /// number of dyld_cache_mapping_and_slide_info entries + pub mapping_with_slide_count: U32<E>, + /// unused + pub dylibs_pbl_state_array_addr_unused: U64<E>, + /// (unslid) address of PrebuiltLoaderSet of all cached dylibs + pub dylibs_pbl_set_addr: U64<E>, + /// (unslid) address of pool of PrebuiltLoaderSet for each program + pub programs_pbl_set_pool_addr: U64<E>, + /// size of pool of PrebuiltLoaderSet for each program + pub programs_pbl_set_pool_size: U64<E>, + /// (unslid) address of trie mapping program path to PrebuiltLoaderSet + pub program_trie_addr: U64<E>, + /// OS Version of dylibs in this cache for the main platform + pub os_version: U32<E>, + /// e.g. iOSMac on macOS + pub alt_platform: U32<E>, + /// e.g. 14.0 for iOSMac + pub alt_os_version: U32<E>, + reserved1: [u8; 4], + /// VM offset from cache_header* to Swift optimizations header + pub swift_opts_offset: U64<E>, + /// size of Swift optimizations header + pub swift_opts_size: U64<E>, + /// file offset to first dyld_subcache_entry + pub sub_cache_array_offset: U32<E>, + /// number of subCache entries + pub sub_cache_array_count: U32<E>, + /// unique value for the shared cache file containing unmapped local symbols + pub symbol_file_uuid: [u8; 16], + /// (unslid) address of the start of where Rosetta can add read-only/executable data + pub rosetta_read_only_addr: U64<E>, + /// maximum size of the Rosetta read-only/executable region + pub rosetta_read_only_size: U64<E>, + /// (unslid) address of the start of where Rosetta can add read-write data + pub rosetta_read_write_addr: U64<E>, + /// maximum size of the Rosetta read-write region + pub rosetta_read_write_size: U64<E>, + /// file offset to first dyld_cache_image_info + pub images_offset: U32<E>, + /// number of dyld_cache_image_info entries + pub images_count: U32<E>, + /// 0 for development, 1 for production, when cacheType is multi-cache(2) + pub cache_sub_type: U32<E>, + /// VM offset from cache_header* to ObjC optimizations header + pub objc_opts_offset: U64<E>, + /// size of ObjC optimizations header + pub objc_opts_size: U64<E>, + /// VM offset from cache_header* to embedded cache atlas for process introspection + pub cache_atlas_offset: U64<E>, + /// size of embedded cache atlas + pub cache_atlas_size: U64<E>, + /// VM offset from cache_header* to the location of dyld_cache_dynamic_data_header + pub dynamic_data_offset: U64<E>, + /// maximum size of space reserved from dynamic data + pub dynamic_data_max_size: U64<E>, +} + +/// Corresponds to struct dyld_cache_mapping_info from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheMappingInfo<E: Endian> { + pub address: U64<E>, + pub size: U64<E>, + pub file_offset: U64<E>, + pub max_prot: U32<E>, + pub init_prot: U32<E>, +} + +// Contains the flags for the dyld_cache_mapping_and_slide_info flags field +pub const DYLD_CACHE_MAPPING_AUTH_DATA: u64 = 1 << 0; +pub const DYLD_CACHE_MAPPING_DIRTY_DATA: u64 = 1 << 1; +pub const DYLD_CACHE_MAPPING_CONST_DATA: u64 = 1 << 2; +pub const DYLD_CACHE_MAPPING_TEXT_STUBS: u64 = 1 << 3; +pub const DYLD_CACHE_DYNAMIC_CONFIG_DATA: u64 = 1 << 4; + +/// Corresponds to struct dyld_cache_mapping_and_slide_info from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheMappingAndSlideInfo<E: Endian> { + pub address: U64<E>, + pub size: U64<E>, + pub file_offset: U64<E>, + pub slide_info_file_offset: U64<E>, + pub slide_info_file_size: U64<E>, + pub flags: U64<E>, + pub max_prot: U32<E>, + pub init_prot: U32<E>, +} + +/// Corresponds to struct dyld_cache_image_info from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheImageInfo<E: Endian> { + pub address: U64<E>, + pub mod_time: U64<E>, + pub inode: U64<E>, + pub path_file_offset: U32<E>, + pub pad: U32<E>, +} + +/// Corresponds to struct dyld_cache_slide_info2 from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheSlideInfo2<E: Endian> { + pub version: U32<E>, // currently 2 + pub page_size: U32<E>, // currently 4096 (may also be 16384) + pub page_starts_offset: U32<E>, + pub page_starts_count: U32<E>, + pub page_extras_offset: U32<E>, + pub page_extras_count: U32<E>, + pub delta_mask: U64<E>, // which (contiguous) set of bits contains the delta to the next rebase location + pub value_add: U64<E>, +} + +pub const DYLD_CACHE_SLIDE_PAGE_ATTRS: u16 = 0xC000; +// Index is into extras array (not starts array). +pub const DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA: u16 = 0x8000; +// Page has no rebasing. +pub const DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE: u16 = 0x4000; +// Last chain entry for page. +pub const DYLD_CACHE_SLIDE_PAGE_ATTR_END: u16 = 0x8000; + +/// Corresponds to struct dyld_cache_slide_info3 from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheSlideInfo3<E: Endian> { + pub version: U32<E>, // currently 3 + pub page_size: U32<E>, // currently 4096 (may also be 16384) + pub page_starts_count: U32<E>, + reserved1: [u8; 4], + pub auth_value_add: U64<E>, +} + +/// Page has no rebasing. +pub const DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE: u16 = 0xFFFF; + +/// Corresponds to union dyld_cache_slide_pointer3 from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +pub struct DyldCacheSlidePointer3(pub u64); + +impl DyldCacheSlidePointer3 { + /// Whether the pointer is authenticated. + pub fn is_auth(&self) -> bool { + ((self.0 >> 63) & 1) != 0 + } + + /// The target of the pointer. + /// + /// Only valid if `is_auth` is false. + pub fn target(&self) -> u64 { + self.0 & ((1 << 43) - 1) + } + + /// The high 8 bits of the pointer. + /// + /// Only valid if `is_auth` is false. + pub fn high8(&self) -> u64 { + (self.0 >> 43) & 0xff + } + + /// The target of the pointer as an offset from the start of the shared cache. + /// + /// Only valid if `is_auth` is true. + pub fn runtime_offset(&self) -> u64 { + self.0 & ((1 << 32) - 1) + } + + /// The diversity value for authentication. + /// + /// Only valid if `is_auth` is true. + pub fn diversity(&self) -> u16 { + ((self.0 >> 32) & 0xffff) as u16 + } + + /// Whether to use address diversity for authentication. + /// + /// Only valid if `is_auth` is true. + pub fn addr_div(&self) -> bool { + ((self.0 >> 48) & 1) != 0 + } + + /// The key for authentication. + /// + /// Only valid if `is_auth` is true. + pub fn key(&self) -> u8 { + ((self.0 >> 49) & 3) as u8 + } + + /// The offset to the next slide pointer in 8-byte units. + /// + /// 0 if no next slide pointer. + pub fn next(&self) -> u64 { + (self.0 >> 51) & ((1 << 11) - 1) + } +} + +/// Corresponds to struct dyld_cache_slide_info5 from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheSlideInfo5<E: Endian> { + pub version: U32<E>, // currently 5 + pub page_size: U32<E>, // currently 4096 (may also be 16384) + pub page_starts_count: U32<E>, + reserved1: [u8; 4], + pub value_add: U64<E>, +} + +/// Page has no rebasing. +pub const DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE: u16 = 0xFFFF; + +/// Corresponds to struct dyld_cache_slide_pointer5 from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +pub struct DyldCacheSlidePointer5(pub u64); + +impl DyldCacheSlidePointer5 { + /// Whether the pointer is authenticated. + pub fn is_auth(&self) -> bool { + ((self.0 >> 63) & 1) != 0 + } + + /// The target of the pointer as an offset from the start of the shared cache. + pub fn runtime_offset(&self) -> u64 { + self.0 & 0x3_ffff_ffff + } + + /// The high 8 bits of the pointer. + /// + /// Only valid if `is_auth` is false. + pub fn high8(&self) -> u64 { + (self.0 >> 34) & 0xff + } + + /// The diversity value for authentication. + /// + /// Only valid if `is_auth` is true. + pub fn diversity(&self) -> u16 { + ((self.0 >> 34) & 0xffff) as u16 + } + + /// Whether to use address diversity for authentication. + /// + /// Only valid if `is_auth` is true. + pub fn addr_div(&self) -> bool { + ((self.0 >> 50) & 1) != 0 + } + + /// Whether the key is IA or DA. + /// + /// Only valid if `is_auth` is true. + pub fn key_is_data(&self) -> bool { + ((self.0 >> 51) & 1) != 0 + } + + /// The offset to the next slide pointer in 8-byte units. + /// + /// 0 if no next slide pointer. + pub fn next(&self) -> u64 { + (self.0 >> 52) & 0x7ff + } +} + +/// Added in dyld-940, which shipped with macOS 12 / iOS 15. +/// Originally called `dyld_subcache_entry`, renamed to `dyld_subcache_entry_v1` +/// in dyld-1042.1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldSubCacheEntryV1<E: Endian> { + /// The UUID of this subcache. + pub uuid: [u8; 16], + /// The offset of this subcache from the main cache base address. + pub cache_vm_offset: U64<E>, +} + +/// Added in dyld-1042.1, which shipped with macOS 13 / iOS 16. +/// Called `dyld_subcache_entry` as of dyld-1042.1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldSubCacheEntryV2<E: Endian> { + /// The UUID of this subcache. + pub uuid: [u8; 16], + /// The offset of this subcache from the main cache base address. + pub cache_vm_offset: U64<E>, + /// The file name suffix of the subCache file, e.g. ".25.data" or ".03.development". + pub file_suffix: [u8; 32], +} + +// Definitions from "/usr/include/mach-o/loader.h". + +/* + * This header file describes the structures of the file format for "fat" + * architecture specific file (wrapper design). At the beginning of the file + * there is one `FatHeader` structure followed by a number of `FatArch*` + * structures. For each architecture in the file, specified by a pair of + * cputype and cpusubtype, the `FatHeader` describes the file offset, file + * size and alignment in the file of the architecture specific member. + * The padded bytes in the file to place each member on it's specific alignment + * are defined to be read as zeros and can be left as "holes" if the file system + * can support them as long as they read as zeros. + * + * All structures defined here are always written and read to/from disk + * in big-endian order. + */ + +pub const FAT_MAGIC: u32 = 0xcafe_babe; +/// NXSwapLong(FAT_MAGIC) +pub const FAT_CIGAM: u32 = 0xbeba_feca; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FatHeader { + /// FAT_MAGIC or FAT_MAGIC_64 + pub magic: U32<BigEndian>, + /// number of structs that follow + pub nfat_arch: U32<BigEndian>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FatArch32 { + /// cpu specifier (int) + pub cputype: U32<BigEndian>, + /// machine specifier (int) + pub cpusubtype: U32<BigEndian>, + /// file offset to this object file + pub offset: U32<BigEndian>, + /// size of this object file + pub size: U32<BigEndian>, + /// alignment as a power of 2 + pub align: U32<BigEndian>, +} + +/* + * The support for the 64-bit fat file format described here is a work in + * progress and not yet fully supported in all the Apple Developer Tools. + * + * When a slice is greater than 4mb or an offset to a slice is greater than 4mb + * then the 64-bit fat file format is used. + */ +pub const FAT_MAGIC_64: u32 = 0xcafe_babf; +/// NXSwapLong(FAT_MAGIC_64) +pub const FAT_CIGAM_64: u32 = 0xbfba_feca; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FatArch64 { + /// cpu specifier (int) + pub cputype: U32<BigEndian>, + /// machine specifier (int) + pub cpusubtype: U32<BigEndian>, + /// file offset to this object file + pub offset: U64<BigEndian>, + /// size of this object file + pub size: U64<BigEndian>, + /// alignment as a power of 2 + pub align: U32<BigEndian>, + /// reserved + pub reserved: U32<BigEndian>, +} + +// Definitions from "/usr/include/mach-o/loader.h". + +/// The 32-bit mach header. +/// +/// Appears at the very beginning of the object file for 32-bit architectures. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct MachHeader32<E: Endian> { + /// mach magic number identifier + pub magic: U32<BigEndian>, + /// cpu specifier + pub cputype: U32<E>, + /// machine specifier + pub cpusubtype: U32<E>, + /// type of file + pub filetype: U32<E>, + /// number of load commands + pub ncmds: U32<E>, + /// the size of all the load commands + pub sizeofcmds: U32<E>, + /// flags + pub flags: U32<E>, +} + +// Values for `MachHeader32::magic`. +/// the mach magic number +pub const MH_MAGIC: u32 = 0xfeed_face; +/// NXSwapInt(MH_MAGIC) +pub const MH_CIGAM: u32 = 0xcefa_edfe; + +/// The 64-bit mach header. +/// +/// Appears at the very beginning of object files for 64-bit architectures. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct MachHeader64<E: Endian> { + /// mach magic number identifier + pub magic: U32<BigEndian>, + /// cpu specifier + pub cputype: U32<E>, + /// machine specifier + pub cpusubtype: U32<E>, + /// type of file + pub filetype: U32<E>, + /// number of load commands + pub ncmds: U32<E>, + /// the size of all the load commands + pub sizeofcmds: U32<E>, + /// flags + pub flags: U32<E>, + /// reserved + pub reserved: U32<E>, +} + +// Values for `MachHeader64::magic`. +/// the 64-bit mach magic number +pub const MH_MAGIC_64: u32 = 0xfeed_facf; +/// NXSwapInt(MH_MAGIC_64) +pub const MH_CIGAM_64: u32 = 0xcffa_edfe; + +/* + * The layout of the file depends on the filetype. For all but the MH_OBJECT + * file type the segments are padded out and aligned on a segment alignment + * boundary for efficient demand pageing. The MH_EXECUTE, MH_FVMLIB, MH_DYLIB, + * MH_DYLINKER and MH_BUNDLE file types also have the headers included as part + * of their first segment. + * + * The file type MH_OBJECT is a compact format intended as output of the + * assembler and input (and possibly output) of the link editor (the .o + * format). All sections are in one unnamed segment with no segment padding. + * This format is used as an executable format when the file is so small the + * segment padding greatly increases its size. + * + * The file type MH_PRELOAD is an executable format intended for things that + * are not executed under the kernel (proms, stand alones, kernels, etc). The + * format can be executed under the kernel but may demand paged it and not + * preload it before execution. + * + * A core file is in MH_CORE format and can be any in an arbritray legal + * Mach-O file. + */ + +// Values for `MachHeader*::filetype`. +/// relocatable object file +pub const MH_OBJECT: u32 = 0x1; +/// demand paged executable file +pub const MH_EXECUTE: u32 = 0x2; +/// fixed VM shared library file +pub const MH_FVMLIB: u32 = 0x3; +/// core file +pub const MH_CORE: u32 = 0x4; +/// preloaded executable file +pub const MH_PRELOAD: u32 = 0x5; +/// dynamically bound shared library +pub const MH_DYLIB: u32 = 0x6; +/// dynamic link editor +pub const MH_DYLINKER: u32 = 0x7; +/// dynamically bound bundle file +pub const MH_BUNDLE: u32 = 0x8; +/// shared library stub for static linking only, no section contents +pub const MH_DYLIB_STUB: u32 = 0x9; +/// companion file with only debug sections +pub const MH_DSYM: u32 = 0xa; +/// x86_64 kexts +pub const MH_KEXT_BUNDLE: u32 = 0xb; +/// set of mach-o's +pub const MH_FILESET: u32 = 0xc; + +// Values for `MachHeader*::flags`. +/// the object file has no undefined references +pub const MH_NOUNDEFS: u32 = 0x1; +/// the object file is the output of an incremental link against a base file and can't be link edited again +pub const MH_INCRLINK: u32 = 0x2; +/// the object file is input for the dynamic linker and can't be statically link edited again +pub const MH_DYLDLINK: u32 = 0x4; +/// the object file's undefined references are bound by the dynamic linker when loaded. +pub const MH_BINDATLOAD: u32 = 0x8; +/// the file has its dynamic undefined references prebound. +pub const MH_PREBOUND: u32 = 0x10; +/// the file has its read-only and read-write segments split +pub const MH_SPLIT_SEGS: u32 = 0x20; +/// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete) +pub const MH_LAZY_INIT: u32 = 0x40; +/// the image is using two-level name space bindings +pub const MH_TWOLEVEL: u32 = 0x80; +/// the executable is forcing all images to use flat name space bindings +pub const MH_FORCE_FLAT: u32 = 0x100; +/// this umbrella guarantees no multiple definitions of symbols in its sub-images so the two-level namespace hints can always be used. +pub const MH_NOMULTIDEFS: u32 = 0x200; +/// do not have dyld notify the prebinding agent about this executable +pub const MH_NOFIXPREBINDING: u32 = 0x400; +/// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. +pub const MH_PREBINDABLE: u32 = 0x800; +/// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. +pub const MH_ALLMODSBOUND: u32 = 0x1000; +/// safe to divide up the sections into sub-sections via symbols for dead code stripping +pub const MH_SUBSECTIONS_VIA_SYMBOLS: u32 = 0x2000; +/// the binary has been canonicalized via the unprebind operation +pub const MH_CANONICAL: u32 = 0x4000; +/// the final linked image contains external weak symbols +pub const MH_WEAK_DEFINES: u32 = 0x8000; +/// the final linked image uses weak symbols +pub const MH_BINDS_TO_WEAK: u32 = 0x10000; +/// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. +pub const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000; +/// When this bit is set, the binary declares it is safe for use in processes with uid zero +pub const MH_ROOT_SAFE: u32 = 0x40000; +/// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true +pub const MH_SETUID_SAFE: u32 = 0x80000; +/// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported +pub const MH_NO_REEXPORTED_DYLIBS: u32 = 0x10_0000; +/// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. +pub const MH_PIE: u32 = 0x20_0000; +/// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib. +pub const MH_DEAD_STRIPPABLE_DYLIB: u32 = 0x40_0000; +/// Contains a section of type S_THREAD_LOCAL_VARIABLES +pub const MH_HAS_TLV_DESCRIPTORS: u32 = 0x80_0000; +/// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes. +pub const MH_NO_HEAP_EXECUTION: u32 = 0x100_0000; +/// The code was linked for use in an application extension. +pub const MH_APP_EXTENSION_SAFE: u32 = 0x0200_0000; +/// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info. +pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO: u32 = 0x0400_0000; +/// Allow LC_MIN_VERSION_MACOS and LC_BUILD_VERSION load commands with +/// the platforms macOS, iOSMac, iOSSimulator, tvOSSimulator and watchOSSimulator. +pub const MH_SIM_SUPPORT: u32 = 0x0800_0000; +/// Only for use on dylibs. When this bit is set, the dylib is part of the dyld +/// shared cache, rather than loose in the filesystem. +pub const MH_DYLIB_IN_CACHE: u32 = 0x8000_0000; + +/// Common fields at the start of every load command. +/// +/// The load commands directly follow the mach_header. The total size of all +/// of the commands is given by the sizeofcmds field in the mach_header. All +/// load commands must have as their first two fields `cmd` and `cmdsize`. The `cmd` +/// field is filled in with a constant for that command type. Each command type +/// has a structure specifically for it. The `cmdsize` field is the size in bytes +/// of the particular load command structure plus anything that follows it that +/// is a part of the load command (i.e. section structures, strings, etc.). To +/// advance to the next load command the `cmdsize` can be added to the offset or +/// pointer of the current load command. The `cmdsize` for 32-bit architectures +/// MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple +/// of 8 bytes (these are forever the maximum alignment of any load commands). +/// The padded bytes must be zero. All tables in the object file must also +/// follow these rules so the file can be memory mapped. Otherwise the pointers +/// to these tables will not work well or at all on some machines. With all +/// padding zeroed like objects will compare byte for byte. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct LoadCommand<E: Endian> { + /// Type of load command. + /// + /// One of the `LC_*` constants. + pub cmd: U32<E>, + /// Total size of command in bytes. + pub cmdsize: U32<E>, +} + +/* + * After MacOS X 10.1 when a new load command is added that is required to be + * understood by the dynamic linker for the image to execute properly the + * LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic + * linker sees such a load command it it does not understand will issue a + * "unknown load command required for execution" error and refuse to use the + * image. Other load commands without this bit that are not understood will + * simply be ignored. + */ +pub const LC_REQ_DYLD: u32 = 0x8000_0000; + +/* Constants for the cmd field of all load commands, the type */ +/// segment of this file to be mapped +pub const LC_SEGMENT: u32 = 0x1; +/// link-edit stab symbol table info +pub const LC_SYMTAB: u32 = 0x2; +/// link-edit gdb symbol table info (obsolete) +pub const LC_SYMSEG: u32 = 0x3; +/// thread +pub const LC_THREAD: u32 = 0x4; +/// unix thread (includes a stack) +pub const LC_UNIXTHREAD: u32 = 0x5; +/// load a specified fixed VM shared library +pub const LC_LOADFVMLIB: u32 = 0x6; +/// fixed VM shared library identification +pub const LC_IDFVMLIB: u32 = 0x7; +/// object identification info (obsolete) +pub const LC_IDENT: u32 = 0x8; +/// fixed VM file inclusion (internal use) +pub const LC_FVMFILE: u32 = 0x9; +/// prepage command (internal use) +pub const LC_PREPAGE: u32 = 0xa; +/// dynamic link-edit symbol table info +pub const LC_DYSYMTAB: u32 = 0xb; +/// load a dynamically linked shared library +pub const LC_LOAD_DYLIB: u32 = 0xc; +/// dynamically linked shared lib ident +pub const LC_ID_DYLIB: u32 = 0xd; +/// load a dynamic linker +pub const LC_LOAD_DYLINKER: u32 = 0xe; +/// dynamic linker identification +pub const LC_ID_DYLINKER: u32 = 0xf; +/// modules prebound for a dynamically linked shared library +pub const LC_PREBOUND_DYLIB: u32 = 0x10; +/// image routines +pub const LC_ROUTINES: u32 = 0x11; +/// sub framework +pub const LC_SUB_FRAMEWORK: u32 = 0x12; +/// sub umbrella +pub const LC_SUB_UMBRELLA: u32 = 0x13; +/// sub client +pub const LC_SUB_CLIENT: u32 = 0x14; +/// sub library +pub const LC_SUB_LIBRARY: u32 = 0x15; +/// two-level namespace lookup hints +pub const LC_TWOLEVEL_HINTS: u32 = 0x16; +/// prebind checksum +pub const LC_PREBIND_CKSUM: u32 = 0x17; +/// load a dynamically linked shared library that is allowed to be missing +/// (all symbols are weak imported). +pub const LC_LOAD_WEAK_DYLIB: u32 = 0x18 | LC_REQ_DYLD; +/// 64-bit segment of this file to be mapped +pub const LC_SEGMENT_64: u32 = 0x19; +/// 64-bit image routines +pub const LC_ROUTINES_64: u32 = 0x1a; +/// the uuid +pub const LC_UUID: u32 = 0x1b; +/// runpath additions +pub const LC_RPATH: u32 = 0x1c | LC_REQ_DYLD; +/// local of code signature +pub const LC_CODE_SIGNATURE: u32 = 0x1d; +/// local of info to split segments +pub const LC_SEGMENT_SPLIT_INFO: u32 = 0x1e; +/// load and re-export dylib +pub const LC_REEXPORT_DYLIB: u32 = 0x1f | LC_REQ_DYLD; +/// delay load of dylib until first use +pub const LC_LAZY_LOAD_DYLIB: u32 = 0x20; +/// encrypted segment information +pub const LC_ENCRYPTION_INFO: u32 = 0x21; +/// compressed dyld information +pub const LC_DYLD_INFO: u32 = 0x22; +/// compressed dyld information only +pub const LC_DYLD_INFO_ONLY: u32 = 0x22 | LC_REQ_DYLD; +/// load upward dylib +pub const LC_LOAD_UPWARD_DYLIB: u32 = 0x23 | LC_REQ_DYLD; +/// build for MacOSX min OS version +pub const LC_VERSION_MIN_MACOSX: u32 = 0x24; +/// build for iPhoneOS min OS version +pub const LC_VERSION_MIN_IPHONEOS: u32 = 0x25; +/// compressed table of function start addresses +pub const LC_FUNCTION_STARTS: u32 = 0x26; +/// string for dyld to treat like environment variable +pub const LC_DYLD_ENVIRONMENT: u32 = 0x27; +/// replacement for LC_UNIXTHREAD +pub const LC_MAIN: u32 = 0x28 | LC_REQ_DYLD; +/// table of non-instructions in __text +pub const LC_DATA_IN_CODE: u32 = 0x29; +/// source version used to build binary +pub const LC_SOURCE_VERSION: u32 = 0x2A; +/// Code signing DRs copied from linked dylibs +pub const LC_DYLIB_CODE_SIGN_DRS: u32 = 0x2B; +/// 64-bit encrypted segment information +pub const LC_ENCRYPTION_INFO_64: u32 = 0x2C; +/// linker options in MH_OBJECT files +pub const LC_LINKER_OPTION: u32 = 0x2D; +/// optimization hints in MH_OBJECT files +pub const LC_LINKER_OPTIMIZATION_HINT: u32 = 0x2E; +/// build for AppleTV min OS version +pub const LC_VERSION_MIN_TVOS: u32 = 0x2F; +/// build for Watch min OS version +pub const LC_VERSION_MIN_WATCHOS: u32 = 0x30; +/// arbitrary data included within a Mach-O file +pub const LC_NOTE: u32 = 0x31; +/// build for platform min OS version +pub const LC_BUILD_VERSION: u32 = 0x32; +/// used with `LinkeditDataCommand`, payload is trie +pub const LC_DYLD_EXPORTS_TRIE: u32 = 0x33 | LC_REQ_DYLD; +/// used with `LinkeditDataCommand` +pub const LC_DYLD_CHAINED_FIXUPS: u32 = 0x34 | LC_REQ_DYLD; +/// used with `FilesetEntryCommand` +pub const LC_FILESET_ENTRY: u32 = 0x35 | LC_REQ_DYLD; + +/// A variable length string in a load command. +/// +/// The strings are stored just after the load command structure and +/// the offset is from the start of the load command structure. The size +/// of the string is reflected in the `cmdsize` field of the load command. +/// Once again any padded bytes to bring the `cmdsize` field to a multiple +/// of 4 bytes must be zero. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct LcStr<E: Endian> { + /// offset to the string + pub offset: U32<E>, +} + +/// 32-bit segment load command. +/// +/// The segment load command indicates that a part of this file is to be +/// mapped into the task's address space. The size of this segment in memory, +/// vmsize, maybe equal to or larger than the amount to map from this file, +/// filesize. The file is mapped starting at fileoff to the beginning of +/// the segment in memory, vmaddr. The rest of the memory of the segment, +/// if any, is allocated zero fill on demand. The segment's maximum virtual +/// memory protection and initial virtual memory protection are specified +/// by the maxprot and initprot fields. If the segment has sections then the +/// `Section32` structures directly follow the segment command and their size is +/// reflected in `cmdsize`. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SegmentCommand32<E: Endian> { + /// LC_SEGMENT + pub cmd: U32<E>, + /// includes sizeof section structs + pub cmdsize: U32<E>, + /// segment name + pub segname: [u8; 16], + /// memory address of this segment + pub vmaddr: U32<E>, + /// memory size of this segment + pub vmsize: U32<E>, + /// file offset of this segment + pub fileoff: U32<E>, + /// amount to map from the file + pub filesize: U32<E>, + /// maximum VM protection + pub maxprot: U32<E>, + /// initial VM protection + pub initprot: U32<E>, + /// number of sections in segment + pub nsects: U32<E>, + /// flags + pub flags: U32<E>, +} + +/// 64-bit segment load command. +/// +/// The 64-bit segment load command indicates that a part of this file is to be +/// mapped into a 64-bit task's address space. If the 64-bit segment has +/// sections then `Section64` structures directly follow the 64-bit segment +/// command and their size is reflected in `cmdsize`. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SegmentCommand64<E: Endian> { + /// LC_SEGMENT_64 + pub cmd: U32<E>, + /// includes sizeof section_64 structs + pub cmdsize: U32<E>, + /// segment name + pub segname: [u8; 16], + /// memory address of this segment + pub vmaddr: U64<E>, + /// memory size of this segment + pub vmsize: U64<E>, + /// file offset of this segment + pub fileoff: U64<E>, + /// amount to map from the file + pub filesize: U64<E>, + /// maximum VM protection + pub maxprot: U32<E>, + /// initial VM protection + pub initprot: U32<E>, + /// number of sections in segment + pub nsects: U32<E>, + /// flags + pub flags: U32<E>, +} + +// Values for `SegmentCommand*::flags`. +/// the file contents for this segment is for the high part of the VM space, the low part is zero filled (for stacks in core files) +pub const SG_HIGHVM: u32 = 0x1; +/// this segment is the VM that is allocated by a fixed VM library, for overlap checking in the link editor +pub const SG_FVMLIB: u32 = 0x2; +/// this segment has nothing that was relocated in it and nothing relocated to it, that is it maybe safely replaced without relocation +pub const SG_NORELOC: u32 = 0x4; +/// This segment is protected. If the segment starts at file offset 0, the first page of the segment is not protected. All other pages of the segment are protected. +pub const SG_PROTECTED_VERSION_1: u32 = 0x8; +/// This segment is made read-only after fixups +pub const SG_READ_ONLY: u32 = 0x10; + +/* + * A segment is made up of zero or more sections. Non-MH_OBJECT files have + * all of their segments with the proper sections in each, and padded to the + * specified segment alignment when produced by the link editor. The first + * segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header + * and load commands of the object file before its first section. The zero + * fill sections are always last in their segment (in all formats). This + * allows the zeroed segment padding to be mapped into memory where zero fill + * sections might be. The gigabyte zero fill sections, those with the section + * type S_GB_ZEROFILL, can only be in a segment with sections of this type. + * These segments are then placed after all other segments. + * + * The MH_OBJECT format has all of its sections in one segment for + * compactness. There is no padding to a specified segment boundary and the + * mach_header and load commands are not part of the segment. + * + * Sections with the same section name, sectname, going into the same segment, + * segname, are combined by the link editor. The resulting section is aligned + * to the maximum alignment of the combined sections and is the new section's + * alignment. The combined sections are aligned to their original alignment in + * the combined section. Any padded bytes to get the specified alignment are + * zeroed. + * + * The format of the relocation entries referenced by the reloff and nreloc + * fields of the section structure for mach object files is described in the + * header file <reloc.h>. + */ +/// 32-bit section. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Section32<E: Endian> { + /// name of this section + pub sectname: [u8; 16], + /// segment this section goes in + pub segname: [u8; 16], + /// memory address of this section + pub addr: U32<E>, + /// size in bytes of this section + pub size: U32<E>, + /// file offset of this section + pub offset: U32<E>, + /// section alignment (power of 2) + pub align: U32<E>, + /// file offset of relocation entries + pub reloff: U32<E>, + /// number of relocation entries + pub nreloc: U32<E>, + /// flags (section type and attributes) + pub flags: U32<E>, + /// reserved (for offset or index) + pub reserved1: U32<E>, + /// reserved (for count or sizeof) + pub reserved2: U32<E>, +} + +/// 64-bit section. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Section64<E: Endian> { + /// name of this section + pub sectname: [u8; 16], + /// segment this section goes in + pub segname: [u8; 16], + /// memory address of this section + pub addr: U64<E>, + /// size in bytes of this section + pub size: U64<E>, + /// file offset of this section + pub offset: U32<E>, + /// section alignment (power of 2) + pub align: U32<E>, + /// file offset of relocation entries + pub reloff: U32<E>, + /// number of relocation entries + pub nreloc: U32<E>, + /// flags (section type and attributes) + pub flags: U32<E>, + /// reserved (for offset or index) + pub reserved1: U32<E>, + /// reserved (for count or sizeof) + pub reserved2: U32<E>, + /// reserved + pub reserved3: U32<E>, +} + +/* + * The flags field of a section structure is separated into two parts a section + * type and section attributes. The section types are mutually exclusive (it + * can only have one type) but the section attributes are not (it may have more + * than one attribute). + */ +/// 256 section types +pub const SECTION_TYPE: u32 = 0x0000_00ff; +/// 24 section attributes +pub const SECTION_ATTRIBUTES: u32 = 0xffff_ff00; + +/* Constants for the type of a section */ +/// regular section +pub const S_REGULAR: u32 = 0x0; +/// zero fill on demand section +pub const S_ZEROFILL: u32 = 0x1; +/// section with only literal C strings +pub const S_CSTRING_LITERALS: u32 = 0x2; +/// section with only 4 byte literals +pub const S_4BYTE_LITERALS: u32 = 0x3; +/// section with only 8 byte literals +pub const S_8BYTE_LITERALS: u32 = 0x4; +/// section with only pointers to literals +pub const S_LITERAL_POINTERS: u32 = 0x5; +/* + * For the two types of symbol pointers sections and the symbol stubs section + * they have indirect symbol table entries. For each of the entries in the + * section the indirect symbol table entries, in corresponding order in the + * indirect symbol table, start at the index stored in the reserved1 field + * of the section structure. Since the indirect symbol table entries + * correspond to the entries in the section the number of indirect symbol table + * entries is inferred from the size of the section divided by the size of the + * entries in the section. For symbol pointers sections the size of the entries + * in the section is 4 bytes and for symbol stubs sections the byte size of the + * stubs is stored in the reserved2 field of the section structure. + */ +/// section with only non-lazy symbol pointers +pub const S_NON_LAZY_SYMBOL_POINTERS: u32 = 0x6; +/// section with only lazy symbol pointers +pub const S_LAZY_SYMBOL_POINTERS: u32 = 0x7; +/// section with only symbol stubs, byte size of stub in the reserved2 field +pub const S_SYMBOL_STUBS: u32 = 0x8; +/// section with only function pointers for initialization +pub const S_MOD_INIT_FUNC_POINTERS: u32 = 0x9; +/// section with only function pointers for termination +pub const S_MOD_TERM_FUNC_POINTERS: u32 = 0xa; +/// section contains symbols that are to be coalesced +pub const S_COALESCED: u32 = 0xb; +/// zero fill on demand section (that can be larger than 4 gigabytes) +pub const S_GB_ZEROFILL: u32 = 0xc; +/// section with only pairs of function pointers for interposing +pub const S_INTERPOSING: u32 = 0xd; +/// section with only 16 byte literals +pub const S_16BYTE_LITERALS: u32 = 0xe; +/// section contains DTrace Object Format +pub const S_DTRACE_DOF: u32 = 0xf; +/// section with only lazy symbol pointers to lazy loaded dylibs +pub const S_LAZY_DYLIB_SYMBOL_POINTERS: u32 = 0x10; +/* + * Section types to support thread local variables + */ +/// template of initial values for TLVs +pub const S_THREAD_LOCAL_REGULAR: u32 = 0x11; +/// template of initial values for TLVs +pub const S_THREAD_LOCAL_ZEROFILL: u32 = 0x12; +/// TLV descriptors +pub const S_THREAD_LOCAL_VARIABLES: u32 = 0x13; +/// pointers to TLV descriptors +pub const S_THREAD_LOCAL_VARIABLE_POINTERS: u32 = 0x14; +/// functions to call to initialize TLV values +pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: u32 = 0x15; +/// 32-bit offsets to initializers +pub const S_INIT_FUNC_OFFSETS: u32 = 0x16; + +/* + * Constants for the section attributes part of the flags field of a section + * structure. + */ +/// User setable attributes +pub const SECTION_ATTRIBUTES_USR: u32 = 0xff00_0000; +/// section contains only true machine instructions +pub const S_ATTR_PURE_INSTRUCTIONS: u32 = 0x8000_0000; +/// section contains coalesced symbols that are not to be in a ranlib table of contents +pub const S_ATTR_NO_TOC: u32 = 0x4000_0000; +/// ok to strip static symbols in this section in files with the MH_DYLDLINK flag +pub const S_ATTR_STRIP_STATIC_SYMS: u32 = 0x2000_0000; +/// no dead stripping +pub const S_ATTR_NO_DEAD_STRIP: u32 = 0x1000_0000; +/// blocks are live if they reference live blocks +pub const S_ATTR_LIVE_SUPPORT: u32 = 0x0800_0000; +/// Used with i386 code stubs written on by dyld +pub const S_ATTR_SELF_MODIFYING_CODE: u32 = 0x0400_0000; +/* + * If a segment contains any sections marked with S_ATTR_DEBUG then all + * sections in that segment must have this attribute. No section other than + * a section marked with this attribute may reference the contents of this + * section. A section with this attribute may contain no symbols and must have + * a section type S_REGULAR. The static linker will not copy section contents + * from sections with this attribute into its output file. These sections + * generally contain DWARF debugging info. + */ +/// a debug section +pub const S_ATTR_DEBUG: u32 = 0x0200_0000; +/// system setable attributes +pub const SECTION_ATTRIBUTES_SYS: u32 = 0x00ff_ff00; +/// section contains some machine instructions +pub const S_ATTR_SOME_INSTRUCTIONS: u32 = 0x0000_0400; +/// section has external relocation entries +pub const S_ATTR_EXT_RELOC: u32 = 0x0000_0200; +/// section has local relocation entries +pub const S_ATTR_LOC_RELOC: u32 = 0x0000_0100; + +/* + * The names of segments and sections in them are mostly meaningless to the + * link-editor. But there are few things to support traditional UNIX + * executables that require the link-editor and assembler to use some names + * agreed upon by convention. + * + * The initial protection of the "__TEXT" segment has write protection turned + * off (not writeable). + * + * The link-editor will allocate common symbols at the end of the "__common" + * section in the "__DATA" segment. It will create the section and segment + * if needed. + */ + +/* The currently known segment names and the section names in those segments */ + +/// the pagezero segment which has no protections and catches NULL references for MH_EXECUTE files +pub const SEG_PAGEZERO: &str = "__PAGEZERO"; + +/// the tradition UNIX text segment +pub const SEG_TEXT: &str = "__TEXT"; +/// the real text part of the text section no headers, and no padding +pub const SECT_TEXT: &str = "__text"; +/// the fvmlib initialization section +pub const SECT_FVMLIB_INIT0: &str = "__fvmlib_init0"; +/// the section following the fvmlib initialization section +pub const SECT_FVMLIB_INIT1: &str = "__fvmlib_init1"; + +/// the tradition UNIX data segment +pub const SEG_DATA: &str = "__DATA"; +/// the real initialized data section no padding, no bss overlap +pub const SECT_DATA: &str = "__data"; +/// the real uninitialized data section no padding +pub const SECT_BSS: &str = "__bss"; +/// the section common symbols are allocated in by the link editor +pub const SECT_COMMON: &str = "__common"; + +/// objective-C runtime segment +pub const SEG_OBJC: &str = "__OBJC"; +/// symbol table +pub const SECT_OBJC_SYMBOLS: &str = "__symbol_table"; +/// module information +pub const SECT_OBJC_MODULES: &str = "__module_info"; +/// string table +pub const SECT_OBJC_STRINGS: &str = "__selector_strs"; +/// string table +pub const SECT_OBJC_REFS: &str = "__selector_refs"; + +/// the icon segment +pub const SEG_ICON: &str = "__ICON"; +/// the icon headers +pub const SECT_ICON_HEADER: &str = "__header"; +/// the icons in tiff format +pub const SECT_ICON_TIFF: &str = "__tiff"; + +/// the segment containing all structs created and maintained by the link editor. Created with -seglinkedit option to ld(1) for MH_EXECUTE and FVMLIB file types only +pub const SEG_LINKEDIT: &str = "__LINKEDIT"; + +/// the segment overlapping with linkedit containing linking information +pub const SEG_LINKINFO: &str = "__LINKINFO"; + +/// the unix stack segment +pub const SEG_UNIXSTACK: &str = "__UNIXSTACK"; + +/// the segment for the self (dyld) modifying code stubs that has read, write and execute permissions +pub const SEG_IMPORT: &str = "__IMPORT"; + +/* + * Fixed virtual memory shared libraries are identified by two things. The + * target pathname (the name of the library as found for execution), and the + * minor version number. The address of where the headers are loaded is in + * header_addr. (THIS IS OBSOLETE and no longer supported). + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Fvmlib<E: Endian> { + /// library's target pathname + pub name: LcStr<E>, + /// library's minor version number + pub minor_version: U32<E>, + /// library's header address + pub header_addr: U32<E>, +} + +/* + * A fixed virtual shared library (filetype == MH_FVMLIB in the mach header) + * contains a `FvmlibCommand` (cmd == LC_IDFVMLIB) to identify the library. + * An object that uses a fixed virtual shared library also contains a + * `FvmlibCommand` (cmd == LC_LOADFVMLIB) for each library it uses. + * (THIS IS OBSOLETE and no longer supported). + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FvmlibCommand<E: Endian> { + /// LC_IDFVMLIB or LC_LOADFVMLIB + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// the library identification + pub fvmlib: Fvmlib<E>, +} + +/* + * Dynamically linked shared libraries are identified by two things. The + * pathname (the name of the library as found for execution), and the + * compatibility version number. The pathname must match and the compatibility + * number in the user of the library must be greater than or equal to the + * library being used. The time stamp is used to record the time a library was + * built and copied into user so it can be use to determined if the library used + * at runtime is exactly the same as used to built the program. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Dylib<E: Endian> { + /// library's path name + pub name: LcStr<E>, + /// library's build time stamp + pub timestamp: U32<E>, + /// library's current version number + pub current_version: U32<E>, + /// library's compatibility vers number + pub compatibility_version: U32<E>, +} + +/* + * A dynamically linked shared library (filetype == MH_DYLIB in the mach header) + * contains a `DylibCommand` (cmd == LC_ID_DYLIB) to identify the library. + * An object that uses a dynamically linked shared library also contains a + * `DylibCommand` (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or + * LC_REEXPORT_DYLIB) for each library it uses. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibCommand<E: Endian> { + /// LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// the library identification + pub dylib: Dylib<E>, +} + +/* + * A dynamically linked shared library may be a subframework of an umbrella + * framework. If so it will be linked with "-umbrella umbrella_name" where + * Where "umbrella_name" is the name of the umbrella framework. A subframework + * can only be linked against by its umbrella framework or other subframeworks + * that are part of the same umbrella framework. Otherwise the static link + * editor produces an error and states to link against the umbrella framework. + * The name of the umbrella framework for subframeworks is recorded in the + * following structure. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SubFrameworkCommand<E: Endian> { + /// LC_SUB_FRAMEWORK + pub cmd: U32<E>, + /// includes umbrella string + pub cmdsize: U32<E>, + /// the umbrella framework name + pub umbrella: LcStr<E>, +} + +/* + * For dynamically linked shared libraries that are subframework of an umbrella + * framework they can allow clients other than the umbrella framework or other + * subframeworks in the same umbrella framework. To do this the subframework + * is built with "-allowable_client client_name" and an LC_SUB_CLIENT load + * command is created for each -allowable_client flag. The client_name is + * usually a framework name. It can also be a name used for bundles clients + * where the bundle is built with "-client_name client_name". + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SubClientCommand<E: Endian> { + /// LC_SUB_CLIENT + pub cmd: U32<E>, + /// includes client string + pub cmdsize: U32<E>, + /// the client name + pub client: LcStr<E>, +} + +/* + * A dynamically linked shared library may be a sub_umbrella of an umbrella + * framework. If so it will be linked with "-sub_umbrella umbrella_name" where + * Where "umbrella_name" is the name of the sub_umbrella framework. When + * statically linking when -twolevel_namespace is in effect a twolevel namespace + * umbrella framework will only cause its subframeworks and those frameworks + * listed as sub_umbrella frameworks to be implicited linked in. Any other + * dependent dynamic libraries will not be linked it when -twolevel_namespace + * is in effect. The primary library recorded by the static linker when + * resolving a symbol in these libraries will be the umbrella framework. + * Zero or more sub_umbrella frameworks may be use by an umbrella framework. + * The name of a sub_umbrella framework is recorded in the following structure. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SubUmbrellaCommand<E: Endian> { + /// LC_SUB_UMBRELLA + pub cmd: U32<E>, + /// includes sub_umbrella string + pub cmdsize: U32<E>, + /// the sub_umbrella framework name + pub sub_umbrella: LcStr<E>, +} + +/* + * A dynamically linked shared library may be a sub_library of another shared + * library. If so it will be linked with "-sub_library library_name" where + * Where "library_name" is the name of the sub_library shared library. When + * statically linking when -twolevel_namespace is in effect a twolevel namespace + * shared library will only cause its subframeworks and those frameworks + * listed as sub_umbrella frameworks and libraries listed as sub_libraries to + * be implicited linked in. Any other dependent dynamic libraries will not be + * linked it when -twolevel_namespace is in effect. The primary library + * recorded by the static linker when resolving a symbol in these libraries + * will be the umbrella framework (or dynamic library). Zero or more sub_library + * shared libraries may be use by an umbrella framework or (or dynamic library). + * The name of a sub_library framework is recorded in the following structure. + * For example /usr/lib/libobjc_profile.A.dylib would be recorded as "libobjc". + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SubLibraryCommand<E: Endian> { + /// LC_SUB_LIBRARY + pub cmd: U32<E>, + /// includes sub_library string + pub cmdsize: U32<E>, + /// the sub_library name + pub sub_library: LcStr<E>, +} + +/* + * A program (filetype == MH_EXECUTE) that is + * prebound to its dynamic libraries has one of these for each library that + * the static linker used in prebinding. It contains a bit vector for the + * modules in the library. The bits indicate which modules are bound (1) and + * which are not (0) from the library. The bit for module 0 is the low bit + * of the first byte. So the bit for the Nth module is: + * (linked_modules[N/8] >> N%8) & 1 + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct PreboundDylibCommand<E: Endian> { + /// LC_PREBOUND_DYLIB + pub cmd: U32<E>, + /// includes strings + pub cmdsize: U32<E>, + /// library's path name + pub name: LcStr<E>, + /// number of modules in library + pub nmodules: U32<E>, + /// bit vector of linked modules + pub linked_modules: LcStr<E>, +} + +/* + * A program that uses a dynamic linker contains a `DylinkerCommand` to identify + * the name of the dynamic linker (LC_LOAD_DYLINKER). And a dynamic linker + * contains a `DylinkerCommand` to identify the dynamic linker (LC_ID_DYLINKER). + * A file can have at most one of these. + * This struct is also used for the LC_DYLD_ENVIRONMENT load command and + * contains string for dyld to treat like environment variable. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylinkerCommand<E: Endian> { + /// LC_ID_DYLINKER, LC_LOAD_DYLINKER or LC_DYLD_ENVIRONMENT + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// dynamic linker's path name + pub name: LcStr<E>, +} + +/* + * Thread commands contain machine-specific data structures suitable for + * use in the thread state primitives. The machine specific data structures + * follow the struct `ThreadCommand` as follows. + * Each flavor of machine specific data structure is preceded by an uint32_t + * constant for the flavor of that data structure, an uint32_t that is the + * count of uint32_t's of the size of the state data structure and then + * the state data structure follows. This triple may be repeated for many + * flavors. The constants for the flavors, counts and state data structure + * definitions are expected to be in the header file <machine/thread_status.h>. + * These machine specific data structures sizes must be multiples of + * 4 bytes. The `cmdsize` reflects the total size of the `ThreadCommand` + * and all of the sizes of the constants for the flavors, counts and state + * data structures. + * + * For executable objects that are unix processes there will be one + * `ThreadCommand` (cmd == LC_UNIXTHREAD) created for it by the link-editor. + * This is the same as a LC_THREAD, except that a stack is automatically + * created (based on the shell's limit for the stack size). Command arguments + * and environment variables are copied onto that stack. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ThreadCommand<E: Endian> { + /// LC_THREAD or LC_UNIXTHREAD + pub cmd: U32<E>, + /// total size of this command + pub cmdsize: U32<E>, + /* uint32_t flavor flavor of thread state */ + /* uint32_t count count of uint32_t's in thread state */ + /* struct XXX_thread_state state thread state for this flavor */ + /* ... */ +} + +/* + * The routines command contains the address of the dynamic shared library + * initialization routine and an index into the module table for the module + * that defines the routine. Before any modules are used from the library the + * dynamic linker fully binds the module that defines the initialization routine + * and then calls it. This gets called before any module initialization + * routines (used for C++ static constructors) in the library. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct RoutinesCommand32<E: Endian> { + /* for 32-bit architectures */ + /// LC_ROUTINES + pub cmd: U32<E>, + /// total size of this command + pub cmdsize: U32<E>, + /// address of initialization routine + pub init_address: U32<E>, + /// index into the module table that the init routine is defined in + pub init_module: U32<E>, + pub reserved1: U32<E>, + pub reserved2: U32<E>, + pub reserved3: U32<E>, + pub reserved4: U32<E>, + pub reserved5: U32<E>, + pub reserved6: U32<E>, +} + +/* + * The 64-bit routines command. Same use as above. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct RoutinesCommand64<E: Endian> { + /* for 64-bit architectures */ + /// LC_ROUTINES_64 + pub cmd: U32<E>, + /// total size of this command + pub cmdsize: U32<E>, + /// address of initialization routine + pub init_address: U64<E>, + /// index into the module table that the init routine is defined in + pub init_module: U64<E>, + pub reserved1: U64<E>, + pub reserved2: U64<E>, + pub reserved3: U64<E>, + pub reserved4: U64<E>, + pub reserved5: U64<E>, + pub reserved6: U64<E>, +} + +/* + * The `SymtabCommand` contains the offsets and sizes of the link-edit 4.3BSD + * "stab" style symbol table information as described in the header files + * <nlist.h> and <stab.h>. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SymtabCommand<E: Endian> { + /// LC_SYMTAB + pub cmd: U32<E>, + /// sizeof(struct SymtabCommand) + pub cmdsize: U32<E>, + /// symbol table offset + pub symoff: U32<E>, + /// number of symbol table entries + pub nsyms: U32<E>, + /// string table offset + pub stroff: U32<E>, + /// string table size in bytes + pub strsize: U32<E>, +} + +/* + * This is the second set of the symbolic information which is used to support + * the data structures for the dynamically link editor. + * + * The original set of symbolic information in the `SymtabCommand` which contains + * the symbol and string tables must also be present when this load command is + * present. When this load command is present the symbol table is organized + * into three groups of symbols: + * local symbols (static and debugging symbols) - grouped by module + * defined external symbols - grouped by module (sorted by name if not lib) + * undefined external symbols (sorted by name if MH_BINDATLOAD is not set, + * and in order the were seen by the static + * linker if MH_BINDATLOAD is set) + * In this load command there are offsets and counts to each of the three groups + * of symbols. + * + * This load command contains a the offsets and sizes of the following new + * symbolic information tables: + * table of contents + * module table + * reference symbol table + * indirect symbol table + * The first three tables above (the table of contents, module table and + * reference symbol table) are only present if the file is a dynamically linked + * shared library. For executable and object modules, which are files + * containing only one module, the information that would be in these three + * tables is determined as follows: + * table of contents - the defined external symbols are sorted by name + * module table - the file contains only one module so everything in the + * file is part of the module. + * reference symbol table - is the defined and undefined external symbols + * + * For dynamically linked shared library files this load command also contains + * offsets and sizes to the pool of relocation entries for all sections + * separated into two groups: + * external relocation entries + * local relocation entries + * For executable and object modules the relocation entries continue to hang + * off the section structures. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DysymtabCommand<E: Endian> { + /// LC_DYSYMTAB + pub cmd: U32<E>, + /// sizeof(struct DysymtabCommand) + pub cmdsize: U32<E>, + + /* + * The symbols indicated by symoff and nsyms of the LC_SYMTAB load command + * are grouped into the following three groups: + * local symbols (further grouped by the module they are from) + * defined external symbols (further grouped by the module they are from) + * undefined symbols + * + * The local symbols are used only for debugging. The dynamic binding + * process may have to use them to indicate to the debugger the local + * symbols for a module that is being bound. + * + * The last two groups are used by the dynamic binding process to do the + * binding (indirectly through the module table and the reference symbol + * table when this is a dynamically linked shared library file). + */ + /// index to local symbols + pub ilocalsym: U32<E>, + /// number of local symbols + pub nlocalsym: U32<E>, + + /// index to externally defined symbols + pub iextdefsym: U32<E>, + /// number of externally defined symbols + pub nextdefsym: U32<E>, + + /// index to undefined symbols + pub iundefsym: U32<E>, + /// number of undefined symbols + pub nundefsym: U32<E>, + + /* + * For the for the dynamic binding process to find which module a symbol + * is defined in the table of contents is used (analogous to the ranlib + * structure in an archive) which maps defined external symbols to modules + * they are defined in. This exists only in a dynamically linked shared + * library file. For executable and object modules the defined external + * symbols are sorted by name and is use as the table of contents. + */ + /// file offset to table of contents + pub tocoff: U32<E>, + /// number of entries in table of contents + pub ntoc: U32<E>, + + /* + * To support dynamic binding of "modules" (whole object files) the symbol + * table must reflect the modules that the file was created from. This is + * done by having a module table that has indexes and counts into the merged + * tables for each module. The module structure that these two entries + * refer to is described below. This exists only in a dynamically linked + * shared library file. For executable and object modules the file only + * contains one module so everything in the file belongs to the module. + */ + /// file offset to module table + pub modtaboff: U32<E>, + /// number of module table entries + pub nmodtab: U32<E>, + + /* + * To support dynamic module binding the module structure for each module + * indicates the external references (defined and undefined) each module + * makes. For each module there is an offset and a count into the + * reference symbol table for the symbols that the module references. + * This exists only in a dynamically linked shared library file. For + * executable and object modules the defined external symbols and the + * undefined external symbols indicates the external references. + */ + /// offset to referenced symbol table + pub extrefsymoff: U32<E>, + /// number of referenced symbol table entries + pub nextrefsyms: U32<E>, + + /* + * The sections that contain "symbol pointers" and "routine stubs" have + * indexes and (implied counts based on the size of the section and fixed + * size of the entry) into the "indirect symbol" table for each pointer + * and stub. For every section of these two types the index into the + * indirect symbol table is stored in the section header in the field + * reserved1. An indirect symbol table entry is simply a 32bit index into + * the symbol table to the symbol that the pointer or stub is referring to. + * The indirect symbol table is ordered to match the entries in the section. + */ + /// file offset to the indirect symbol table + pub indirectsymoff: U32<E>, + /// number of indirect symbol table entries + pub nindirectsyms: U32<E>, + + /* + * To support relocating an individual module in a library file quickly the + * external relocation entries for each module in the library need to be + * accessed efficiently. Since the relocation entries can't be accessed + * through the section headers for a library file they are separated into + * groups of local and external entries further grouped by module. In this + * case the presents of this load command who's extreloff, nextrel, + * locreloff and nlocrel fields are non-zero indicates that the relocation + * entries of non-merged sections are not referenced through the section + * structures (and the reloff and nreloc fields in the section headers are + * set to zero). + * + * Since the relocation entries are not accessed through the section headers + * this requires the r_address field to be something other than a section + * offset to identify the item to be relocated. In this case r_address is + * set to the offset from the vmaddr of the first LC_SEGMENT command. + * For MH_SPLIT_SEGS images r_address is set to the the offset from the + * vmaddr of the first read-write LC_SEGMENT command. + * + * The relocation entries are grouped by module and the module table + * entries have indexes and counts into them for the group of external + * relocation entries for that the module. + * + * For sections that are merged across modules there must not be any + * remaining external relocation entries for them (for merged sections + * remaining relocation entries must be local). + */ + /// offset to external relocation entries + pub extreloff: U32<E>, + /// number of external relocation entries + pub nextrel: U32<E>, + + /* + * All the local relocation entries are grouped together (they are not + * grouped by their module since they are only used if the object is moved + * from it statically link edited address). + */ + /// offset to local relocation entries + pub locreloff: U32<E>, + /// number of local relocation entries + pub nlocrel: U32<E>, +} + +/* + * An indirect symbol table entry is simply a 32bit index into the symbol table + * to the symbol that the pointer or stub is referring to. Unless it is for a + * non-lazy symbol pointer section for a defined symbol which strip(1) as + * removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the + * symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that. + */ +pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x8000_0000; +pub const INDIRECT_SYMBOL_ABS: u32 = 0x4000_0000; + +/* a table of contents entry */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibTableOfContents<E: Endian> { + /// the defined external symbol (index into the symbol table) + pub symbol_index: U32<E>, + /// index into the module table this symbol is defined in + pub module_index: U32<E>, +} + +/* a module table entry */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibModule32<E: Endian> { + /// the module name (index into string table) + pub module_name: U32<E>, + + /// index into externally defined symbols + pub iextdefsym: U32<E>, + /// number of externally defined symbols + pub nextdefsym: U32<E>, + /// index into reference symbol table + pub irefsym: U32<E>, + /// number of reference symbol table entries + pub nrefsym: U32<E>, + /// index into symbols for local symbols + pub ilocalsym: U32<E>, + /// number of local symbols + pub nlocalsym: U32<E>, + + /// index into external relocation entries + pub iextrel: U32<E>, + /// number of external relocation entries + pub nextrel: U32<E>, + + /// low 16 bits are the index into the init section, high 16 bits are the index into the term section + pub iinit_iterm: U32<E>, + /// low 16 bits are the number of init section entries, high 16 bits are the number of term section entries + pub ninit_nterm: U32<E>, + + /// for this module address of the start of the (__OBJC,__module_info) section + pub objc_module_info_addr: U32<E>, + /// for this module size of the (__OBJC,__module_info) section + pub objc_module_info_size: U32<E>, +} + +/* a 64-bit module table entry */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibModule64<E: Endian> { + /// the module name (index into string table) + pub module_name: U32<E>, + + /// index into externally defined symbols + pub iextdefsym: U32<E>, + /// number of externally defined symbols + pub nextdefsym: U32<E>, + /// index into reference symbol table + pub irefsym: U32<E>, + /// number of reference symbol table entries + pub nrefsym: U32<E>, + /// index into symbols for local symbols + pub ilocalsym: U32<E>, + /// number of local symbols + pub nlocalsym: U32<E>, + + /// index into external relocation entries + pub iextrel: U32<E>, + /// number of external relocation entries + pub nextrel: U32<E>, + + /// low 16 bits are the index into the init section, high 16 bits are the index into the term section + pub iinit_iterm: U32<E>, + /// low 16 bits are the number of init section entries, high 16 bits are the number of term section entries + pub ninit_nterm: U32<E>, + + /// for this module size of the (__OBJC,__module_info) section + pub objc_module_info_size: U32<E>, + /// for this module address of the start of the (__OBJC,__module_info) section + pub objc_module_info_addr: U64<E>, +} + +/* + * The entries in the reference symbol table are used when loading the module + * (both by the static and dynamic link editors) and if the module is unloaded + * or replaced. Therefore all external symbols (defined and undefined) are + * listed in the module's reference table. The flags describe the type of + * reference that is being made. The constants for the flags are defined in + * <mach-o/nlist.h> as they are also used for symbol table entries. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibReference<E: Endian> { + /* TODO: + uint32_t isym:24, /* index into the symbol table */ + flags:8; /* flags to indicate the type of reference */ + */ + pub bitfield: U32<E>, +} + +/* + * The TwolevelHintsCommand contains the offset and number of hints in the + * two-level namespace lookup hints table. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct TwolevelHintsCommand<E: Endian> { + /// LC_TWOLEVEL_HINTS + pub cmd: U32<E>, + /// sizeof(struct TwolevelHintsCommand) + pub cmdsize: U32<E>, + /// offset to the hint table + pub offset: U32<E>, + /// number of hints in the hint table + pub nhints: U32<E>, +} + +/* + * The entries in the two-level namespace lookup hints table are TwolevelHint + * structs. These provide hints to the dynamic link editor where to start + * looking for an undefined symbol in a two-level namespace image. The + * isub_image field is an index into the sub-images (sub-frameworks and + * sub-umbrellas list) that made up the two-level image that the undefined + * symbol was found in when it was built by the static link editor. If + * isub-image is 0 the the symbol is expected to be defined in library and not + * in the sub-images. If isub-image is non-zero it is an index into the array + * of sub-images for the umbrella with the first index in the sub-images being + * 1. The array of sub-images is the ordered list of sub-images of the umbrella + * that would be searched for a symbol that has the umbrella recorded as its + * primary library. The table of contents index is an index into the + * library's table of contents. This is used as the starting point of the + * binary search or a directed linear search. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct TwolevelHint<E: Endian> { + /* TODO: + uint32_t + isub_image:8, /* index into the sub images */ + itoc:24; /* index into the table of contents */ + */ + pub bitfield: U32<E>, +} + +/* + * The PrebindCksumCommand contains the value of the original check sum for + * prebound files or zero. When a prebound file is first created or modified + * for other than updating its prebinding information the value of the check sum + * is set to zero. When the file has it prebinding re-done and if the value of + * the check sum is zero the original check sum is calculated and stored in + * cksum field of this load command in the output file. If when the prebinding + * is re-done and the cksum field is non-zero it is left unchanged from the + * input file. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct PrebindCksumCommand<E: Endian> { + /// LC_PREBIND_CKSUM + pub cmd: U32<E>, + /// sizeof(struct PrebindCksumCommand) + pub cmdsize: U32<E>, + /// the check sum or zero + pub cksum: U32<E>, +} + +/* + * The uuid load command contains a single 128-bit unique random number that + * identifies an object produced by the static link editor. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct UuidCommand<E: Endian> { + /// LC_UUID + pub cmd: U32<E>, + /// sizeof(struct UuidCommand) + pub cmdsize: U32<E>, + /// the 128-bit uuid + pub uuid: [u8; 16], +} + +/* + * The RpathCommand contains a path which at runtime should be added to + * the current run path used to find @rpath prefixed dylibs. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct RpathCommand<E: Endian> { + /// LC_RPATH + pub cmd: U32<E>, + /// includes string + pub cmdsize: U32<E>, + /// path to add to run path + pub path: LcStr<E>, +} + +/* + * The LinkeditDataCommand contains the offsets and sizes of a blob + * of data in the __LINKEDIT segment. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct LinkeditDataCommand<E: Endian> { + /// `LC_CODE_SIGNATURE`, `LC_SEGMENT_SPLIT_INFO`, `LC_FUNCTION_STARTS`, + /// `LC_DATA_IN_CODE`, `LC_DYLIB_CODE_SIGN_DRS`, `LC_LINKER_OPTIMIZATION_HINT`, + /// `LC_DYLD_EXPORTS_TRIE`, or `LC_DYLD_CHAINED_FIXUPS`. + pub cmd: U32<E>, + /// sizeof(struct LinkeditDataCommand) + pub cmdsize: U32<E>, + /// file offset of data in __LINKEDIT segment + pub dataoff: U32<E>, + /// file size of data in __LINKEDIT segment + pub datasize: U32<E>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FilesetEntryCommand<E: Endian> { + // LC_FILESET_ENTRY + pub cmd: U32<E>, + /// includes id string + pub cmdsize: U32<E>, + /// memory address of the dylib + pub vmaddr: U64<E>, + /// file offset of the dylib + pub fileoff: U64<E>, + /// contained entry id + pub entry_id: LcStr<E>, + /// entry_id is 32-bits long, so this is the reserved padding + pub reserved: U32<E>, +} + +/* + * The EncryptionInfoCommand32 contains the file offset and size of an + * of an encrypted segment. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct EncryptionInfoCommand32<E: Endian> { + /// LC_ENCRYPTION_INFO + pub cmd: U32<E>, + /// sizeof(struct EncryptionInfoCommand32) + pub cmdsize: U32<E>, + /// file offset of encrypted range + pub cryptoff: U32<E>, + /// file size of encrypted range + pub cryptsize: U32<E>, + /// which enryption system, 0 means not-encrypted yet + pub cryptid: U32<E>, +} + +/* + * The EncryptionInfoCommand64 contains the file offset and size of an + * of an encrypted segment (for use in x86_64 targets). + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct EncryptionInfoCommand64<E: Endian> { + /// LC_ENCRYPTION_INFO_64 + pub cmd: U32<E>, + /// sizeof(struct EncryptionInfoCommand64) + pub cmdsize: U32<E>, + /// file offset of encrypted range + pub cryptoff: U32<E>, + /// file size of encrypted range + pub cryptsize: U32<E>, + /// which enryption system, 0 means not-encrypted yet + pub cryptid: U32<E>, + /// padding to make this struct's size a multiple of 8 bytes + pub pad: U32<E>, +} + +/* + * The VersionMinCommand contains the min OS version on which this + * binary was built to run. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct VersionMinCommand<E: Endian> { + /// LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS or LC_VERSION_MIN_WATCHOS or LC_VERSION_MIN_TVOS + pub cmd: U32<E>, + /// sizeof(struct VersionMinCommand) + pub cmdsize: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub version: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub sdk: U32<E>, +} + +/* + * The BuildVersionCommand contains the min OS version on which this + * binary was built to run for its platform. The list of known platforms and + * tool values following it. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct BuildVersionCommand<E: Endian> { + /// LC_BUILD_VERSION + pub cmd: U32<E>, + /// sizeof(struct BuildVersionCommand) plus ntools * sizeof(struct BuildToolVersion) + pub cmdsize: U32<E>, + /// platform + pub platform: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub minos: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub sdk: U32<E>, + /// number of tool entries following this + pub ntools: U32<E>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct BuildToolVersion<E: Endian> { + /// enum for the tool + pub tool: U32<E>, + /// version number of the tool + pub version: U32<E>, +} + +/* Known values for the platform field above. */ +pub const PLATFORM_MACOS: u32 = 1; +pub const PLATFORM_IOS: u32 = 2; +pub const PLATFORM_TVOS: u32 = 3; +pub const PLATFORM_WATCHOS: u32 = 4; +pub const PLATFORM_BRIDGEOS: u32 = 5; +pub const PLATFORM_MACCATALYST: u32 = 6; +pub const PLATFORM_IOSSIMULATOR: u32 = 7; +pub const PLATFORM_TVOSSIMULATOR: u32 = 8; +pub const PLATFORM_WATCHOSSIMULATOR: u32 = 9; +pub const PLATFORM_DRIVERKIT: u32 = 10; +pub const PLATFORM_XROS: u32 = 11; +pub const PLATFORM_XROSSIMULATOR: u32 = 12; + +/* Known values for the tool field above. */ +pub const TOOL_CLANG: u32 = 1; +pub const TOOL_SWIFT: u32 = 2; +pub const TOOL_LD: u32 = 3; + +/* + * The DyldInfoCommand contains the file offsets and sizes of + * the new compressed form of the information dyld needs to + * load the image. This information is used by dyld on Mac OS X + * 10.6 and later. All information pointed to by this command + * is encoded using byte streams, so no endian swapping is needed + * to interpret it. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldInfoCommand<E: Endian> { + /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY + pub cmd: U32<E>, + /// sizeof(struct DyldInfoCommand) + pub cmdsize: U32<E>, + + /* + * Dyld rebases an image whenever dyld loads it at an address different + * from its preferred address. The rebase information is a stream + * of byte sized opcodes whose symbolic names start with REBASE_OPCODE_. + * Conceptually the rebase information is a table of tuples: + * <seg-index, seg-offset, type> + * The opcodes are a compressed way to encode the table by only + * encoding when a column changes. In addition simple patterns + * like "every n'th offset for m times" can be encoded in a few + * bytes. + */ + /// file offset to rebase info + pub rebase_off: U32<E>, + /// size of rebase info + pub rebase_size: U32<E>, + + /* + * Dyld binds an image during the loading process, if the image + * requires any pointers to be initialized to symbols in other images. + * The bind information is a stream of byte sized + * opcodes whose symbolic names start with BIND_OPCODE_. + * Conceptually the bind information is a table of tuples: + * <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend> + * The opcodes are a compressed way to encode the table by only + * encoding when a column changes. In addition simple patterns + * like for runs of pointers initialized to the same value can be + * encoded in a few bytes. + */ + /// file offset to binding info + pub bind_off: U32<E>, + /// size of binding info + pub bind_size: U32<E>, + + /* + * Some C++ programs require dyld to unique symbols so that all + * images in the process use the same copy of some code/data. + * This step is done after binding. The content of the weak_bind + * info is an opcode stream like the bind_info. But it is sorted + * alphabetically by symbol name. This enable dyld to walk + * all images with weak binding information in order and look + * for collisions. If there are no collisions, dyld does + * no updating. That means that some fixups are also encoded + * in the bind_info. For instance, all calls to "operator new" + * are first bound to libstdc++.dylib using the information + * in bind_info. Then if some image overrides operator new + * that is detected when the weak_bind information is processed + * and the call to operator new is then rebound. + */ + /// file offset to weak binding info + pub weak_bind_off: U32<E>, + /// size of weak binding info + pub weak_bind_size: U32<E>, + + /* + * Some uses of external symbols do not need to be bound immediately. + * Instead they can be lazily bound on first use. The lazy_bind + * are contains a stream of BIND opcodes to bind all lazy symbols. + * Normal use is that dyld ignores the lazy_bind section when + * loading an image. Instead the static linker arranged for the + * lazy pointer to initially point to a helper function which + * pushes the offset into the lazy_bind area for the symbol + * needing to be bound, then jumps to dyld which simply adds + * the offset to lazy_bind_off to get the information on what + * to bind. + */ + /// file offset to lazy binding info + pub lazy_bind_off: U32<E>, + /// size of lazy binding infs + pub lazy_bind_size: U32<E>, + + /* + * The symbols exported by a dylib are encoded in a trie. This + * is a compact representation that factors out common prefixes. + * It also reduces LINKEDIT pages in RAM because it encodes all + * information (name, address, flags) in one small, contiguous range. + * The export area is a stream of nodes. The first node sequentially + * is the start node for the trie. + * + * Nodes for a symbol start with a uleb128 that is the length of + * the exported symbol information for the string so far. + * If there is no exported symbol, the node starts with a zero byte. + * If there is exported info, it follows the length. + * + * First is a uleb128 containing flags. Normally, it is followed by + * a uleb128 encoded offset which is location of the content named + * by the symbol from the mach_header for the image. If the flags + * is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is + * a uleb128 encoded library ordinal, then a zero terminated + * UTF8 string. If the string is zero length, then the symbol + * is re-export from the specified dylib with the same name. + * If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following + * the flags is two uleb128s: the stub offset and the resolver offset. + * The stub is used by non-lazy pointers. The resolver is used + * by lazy pointers and must be called to get the actual address to use. + * + * After the optional exported symbol information is a byte of + * how many edges (0-255) that this node has leaving it, + * followed by each edge. + * Each edge is a zero terminated UTF8 of the addition chars + * in the symbol, followed by a uleb128 offset for the node that + * edge points to. + * + */ + /// file offset to lazy binding info + pub export_off: U32<E>, + /// size of lazy binding infs + pub export_size: U32<E>, +} + +/* + * The following are used to encode rebasing information + */ +pub const REBASE_TYPE_POINTER: u8 = 1; +pub const REBASE_TYPE_TEXT_ABSOLUTE32: u8 = 2; +pub const REBASE_TYPE_TEXT_PCREL32: u8 = 3; + +pub const REBASE_OPCODE_MASK: u8 = 0xF0; +pub const REBASE_IMMEDIATE_MASK: u8 = 0x0F; +pub const REBASE_OPCODE_DONE: u8 = 0x00; +pub const REBASE_OPCODE_SET_TYPE_IMM: u8 = 0x10; +pub const REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x20; +pub const REBASE_OPCODE_ADD_ADDR_ULEB: u8 = 0x30; +pub const REBASE_OPCODE_ADD_ADDR_IMM_SCALED: u8 = 0x40; +pub const REBASE_OPCODE_DO_REBASE_IMM_TIMES: u8 = 0x50; +pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES: u8 = 0x60; +pub const REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: u8 = 0x70; +pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: u8 = 0x80; + +/* + * The following are used to encode binding information + */ +pub const BIND_TYPE_POINTER: u8 = 1; +pub const BIND_TYPE_TEXT_ABSOLUTE32: u8 = 2; +pub const BIND_TYPE_TEXT_PCREL32: u8 = 3; + +pub const BIND_SPECIAL_DYLIB_SELF: i8 = 0; +pub const BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: i8 = -1; +pub const BIND_SPECIAL_DYLIB_FLAT_LOOKUP: i8 = -2; +pub const BIND_SPECIAL_DYLIB_WEAK_LOOKUP: i8 = -3; + +pub const BIND_SYMBOL_FLAGS_WEAK_IMPORT: u8 = 0x1; +pub const BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION: u8 = 0x8; + +pub const BIND_OPCODE_MASK: u8 = 0xF0; +pub const BIND_IMMEDIATE_MASK: u8 = 0x0F; +pub const BIND_OPCODE_DONE: u8 = 0x00; +pub const BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: u8 = 0x10; +pub const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: u8 = 0x20; +pub const BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: u8 = 0x30; +pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40; +pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50; +pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60; +pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70; +pub const BIND_OPCODE_ADD_ADDR_ULEB: u8 = 0x80; +pub const BIND_OPCODE_DO_BIND: u8 = 0x90; +pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xA0; +pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xB0; +pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = 0xC0; +pub const BIND_OPCODE_THREADED: u8 = 0xD0; +pub const BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB: u8 = 0x00; +pub const BIND_SUBOPCODE_THREADED_APPLY: u8 = 0x01; + +/* + * The following are used on the flags byte of a terminal node + * in the export information. + */ +pub const EXPORT_SYMBOL_FLAGS_KIND_MASK: u32 = 0x03; +pub const EXPORT_SYMBOL_FLAGS_KIND_REGULAR: u32 = 0x00; +pub const EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: u32 = 0x01; +pub const EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: u32 = 0x02; +pub const EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION: u32 = 0x04; +pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u32 = 0x08; +pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u32 = 0x10; + +/* + * The LinkerOptionCommand contains linker options embedded in object files. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct LinkerOptionCommand<E: Endian> { + /// LC_LINKER_OPTION only used in MH_OBJECT filetypes + pub cmd: U32<E>, + pub cmdsize: U32<E>, + /// number of strings + pub count: U32<E>, + /* concatenation of zero terminated UTF8 strings. + Zero filled at end to align */ +} + +/* + * The SymsegCommand contains the offset and size of the GNU style + * symbol table information as described in the header file <symseg.h>. + * The symbol roots of the symbol segments must also be aligned properly + * in the file. So the requirement of keeping the offsets aligned to a + * multiple of a 4 bytes translates to the length field of the symbol + * roots also being a multiple of a long. Also the padding must again be + * zeroed. (THIS IS OBSOLETE and no longer supported). + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SymsegCommand<E: Endian> { + /// LC_SYMSEG + pub cmd: U32<E>, + /// sizeof(struct SymsegCommand) + pub cmdsize: U32<E>, + /// symbol segment offset + pub offset: U32<E>, + /// symbol segment size in bytes + pub size: U32<E>, +} + +/* + * The IdentCommand contains a free format string table following the + * IdentCommand structure. The strings are null terminated and the size of + * the command is padded out with zero bytes to a multiple of 4 bytes/ + * (THIS IS OBSOLETE and no longer supported). + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct IdentCommand<E: Endian> { + /// LC_IDENT + pub cmd: U32<E>, + /// strings that follow this command + pub cmdsize: U32<E>, +} + +/* + * The FvmfileCommand contains a reference to a file to be loaded at the + * specified virtual address. (Presently, this command is reserved for + * internal use. The kernel ignores this command when loading a program into + * memory). + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FvmfileCommand<E: Endian> { + /// LC_FVMFILE + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// files pathname + pub name: LcStr<E>, + /// files virtual address + pub header_addr: U32<E>, +} + +/* + * The EntryPointCommand is a replacement for thread_command. + * It is used for main executables to specify the location (file offset) + * of main(). If -stack_size was used at link time, the stacksize + * field will contain the stack size need for the main thread. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct EntryPointCommand<E: Endian> { + /// LC_MAIN only used in MH_EXECUTE filetypes + pub cmd: U32<E>, + /// 24 + pub cmdsize: U32<E>, + /// file (__TEXT) offset of main() + pub entryoff: U64<E>, + /// if not zero, initial stack size + pub stacksize: U64<E>, +} + +/* + * The SourceVersionCommand is an optional load command containing + * the version of the sources used to build the binary. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SourceVersionCommand<E: Endian> { + /// LC_SOURCE_VERSION + pub cmd: U32<E>, + /// 16 + pub cmdsize: U32<E>, + /// A.B.C.D.E packed as a24.b10.c10.d10.e10 + pub version: U64<E>, +} + +/* + * The LC_DATA_IN_CODE load commands uses a LinkeditDataCommand + * to point to an array of DataInCodeEntry entries. Each entry + * describes a range of data in a code section. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DataInCodeEntry<E: Endian> { + /// from mach_header to start of data range + pub offset: U32<E>, + /// number of bytes in data range + pub length: U16<E>, + /// a DICE_KIND_* value + pub kind: U16<E>, +} +pub const DICE_KIND_DATA: u32 = 0x0001; +pub const DICE_KIND_JUMP_TABLE8: u32 = 0x0002; +pub const DICE_KIND_JUMP_TABLE16: u32 = 0x0003; +pub const DICE_KIND_JUMP_TABLE32: u32 = 0x0004; +pub const DICE_KIND_ABS_JUMP_TABLE32: u32 = 0x0005; + +/* + * Sections of type S_THREAD_LOCAL_VARIABLES contain an array + * of TlvDescriptor structures. + */ +/* TODO: +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct TlvDescriptor<E: Endian> +{ + void* (*thunk)(struct TlvDescriptor*); + unsigned long key; + unsigned long offset; +} +*/ + +/* + * LC_NOTE commands describe a region of arbitrary data included in a Mach-O + * file. Its initial use is to record extra data in MH_CORE files. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct NoteCommand<E: Endian> { + /// LC_NOTE + pub cmd: U32<E>, + /// sizeof(struct NoteCommand) + pub cmdsize: U32<E>, + /// owner name for this LC_NOTE + pub data_owner: [u8; 16], + /// file offset of this data + pub offset: U64<E>, + /// length of data region + pub size: U64<E>, +} + +// Definitions from "/usr/include/mach-o/nlist.h". + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Nlist32<E: Endian> { + /// index into the string table + pub n_strx: U32<E>, + /// type flag, see below + pub n_type: u8, + /// section number or NO_SECT + pub n_sect: u8, + /// see <mach-o/stab.h> + pub n_desc: U16<E>, + /// value of this symbol (or stab offset) + pub n_value: U32<E>, +} + +/* + * This is the symbol table entry structure for 64-bit architectures. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Nlist64<E: Endian> { + /// index into the string table + pub n_strx: U32<E>, + /// type flag, see below + pub n_type: u8, + /// section number or NO_SECT + pub n_sect: u8, + /// see <mach-o/stab.h> + pub n_desc: U16<E>, + /// value of this symbol (or stab offset) + // Note: 4 byte alignment has been observed in practice. + pub n_value: U64Bytes<E>, +} + +/* + * Symbols with a index into the string table of zero (n_un.n_strx == 0) are + * defined to have a null, "", name. Therefore all string indexes to non null + * names must not have a zero string index. This is bit historical information + * that has never been well documented. + */ + +/* + * The n_type field really contains four fields: + * unsigned char N_STAB:3, + * N_PEXT:1, + * N_TYPE:3, + * N_EXT:1; + * which are used via the following masks. + */ +/// if any of these bits set, a symbolic debugging entry +pub const N_STAB: u8 = 0xe0; +/// private external symbol bit +pub const N_PEXT: u8 = 0x10; +/// mask for the type bits +pub const N_TYPE: u8 = 0x0e; +/// external symbol bit, set for external symbols +pub const N_EXT: u8 = 0x01; + +/* + * Only symbolic debugging entries have some of the N_STAB bits set and if any + * of these bits are set then it is a symbolic debugging entry (a stab). In + * which case then the values of the n_type field (the entire field) are given + * in <mach-o/stab.h> + */ + +/* + * Values for N_TYPE bits of the n_type field. + */ +/// undefined, n_sect == NO_SECT +pub const N_UNDF: u8 = 0x0; +/// absolute, n_sect == NO_SECT +pub const N_ABS: u8 = 0x2; +/// defined in section number n_sect +pub const N_SECT: u8 = 0xe; +/// prebound undefined (defined in a dylib) +pub const N_PBUD: u8 = 0xc; +/// indirect +pub const N_INDR: u8 = 0xa; + +/* + * If the type is N_INDR then the symbol is defined to be the same as another + * symbol. In this case the n_value field is an index into the string table + * of the other symbol's name. When the other symbol is defined then they both + * take on the defined type and value. + */ + +/* + * If the type is N_SECT then the n_sect field contains an ordinal of the + * section the symbol is defined in. The sections are numbered from 1 and + * refer to sections in order they appear in the load commands for the file + * they are in. This means the same ordinal may very well refer to different + * sections in different files. + * + * The n_value field for all symbol table entries (including N_STAB's) gets + * updated by the link editor based on the value of it's n_sect field and where + * the section n_sect references gets relocated. If the value of the n_sect + * field is NO_SECT then it's n_value field is not changed by the link editor. + */ +/// symbol is not in any section +pub const NO_SECT: u8 = 0; +/// 1 thru 255 inclusive +pub const MAX_SECT: u8 = 255; + +/* + * Common symbols are represented by undefined (N_UNDF) external (N_EXT) types + * who's values (n_value) are non-zero. In which case the value of the n_value + * field is the size (in bytes) of the common symbol. The n_sect field is set + * to NO_SECT. The alignment of a common symbol may be set as a power of 2 + * between 2^1 and 2^15 as part of the n_desc field using the macros below. If + * the alignment is not set (a value of zero) then natural alignment based on + * the size is used. + */ +/* TODO: +#define GET_COMM_ALIGN(n_desc) (((n_desc) >> 8) & 0x0f) +#define SET_COMM_ALIGN(n_desc,align) \ + (n_desc) = (((n_desc) & 0xf0ff) | (((align) & 0x0f) << 8)) + */ + +/* + * To support the lazy binding of undefined symbols in the dynamic link-editor, + * the undefined symbols in the symbol table (the nlist structures) are marked + * with the indication if the undefined reference is a lazy reference or + * non-lazy reference. If both a non-lazy reference and a lazy reference is + * made to the same symbol the non-lazy reference takes precedence. A reference + * is lazy only when all references to that symbol are made through a symbol + * pointer in a lazy symbol pointer section. + * + * The implementation of marking nlist structures in the symbol table for + * undefined symbols will be to use some of the bits of the n_desc field as a + * reference type. The mask REFERENCE_TYPE will be applied to the n_desc field + * of an nlist structure for an undefined symbol to determine the type of + * undefined reference (lazy or non-lazy). + * + * The constants for the REFERENCE FLAGS are propagated to the reference table + * in a shared library file. In that case the constant for a defined symbol, + * REFERENCE_FLAG_DEFINED, is also used. + */ +/* Reference type bits of the n_desc field of undefined symbols */ +pub const REFERENCE_TYPE: u16 = 0x7; +/* types of references */ +pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0; +pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 1; +pub const REFERENCE_FLAG_DEFINED: u16 = 2; +pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 3; +pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 4; +pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 5; + +/* + * To simplify stripping of objects that use are used with the dynamic link + * editor, the static link editor marks the symbols defined an object that are + * referenced by a dynamically bound object (dynamic shared libraries, bundles). + * With this marking strip knows not to strip these symbols. + */ +pub const REFERENCED_DYNAMICALLY: u16 = 0x0010; + +/* + * For images created by the static link editor with the -twolevel_namespace + * option in effect the flags field of the mach header is marked with + * MH_TWOLEVEL. And the binding of the undefined references of the image are + * determined by the static link editor. Which library an undefined symbol is + * bound to is recorded by the static linker in the high 8 bits of the n_desc + * field using the SET_LIBRARY_ORDINAL macro below. The ordinal recorded + * references the libraries listed in the Mach-O's LC_LOAD_DYLIB, + * LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB, and + * LC_LAZY_LOAD_DYLIB, etc. load commands in the order they appear in the + * headers. The library ordinals start from 1. + * For a dynamic library that is built as a two-level namespace image the + * undefined references from module defined in another use the same nlist struct + * an in that case SELF_LIBRARY_ORDINAL is used as the library ordinal. For + * defined symbols in all images they also must have the library ordinal set to + * SELF_LIBRARY_ORDINAL. The EXECUTABLE_ORDINAL refers to the executable + * image for references from plugins that refer to the executable that loads + * them. + * + * The DYNAMIC_LOOKUP_ORDINAL is for undefined symbols in a two-level namespace + * image that are looked up by the dynamic linker with flat namespace semantics. + * This ordinal was added as a feature in Mac OS X 10.3 by reducing the + * value of MAX_LIBRARY_ORDINAL by one. So it is legal for existing binaries + * or binaries built with older tools to have 0xfe (254) dynamic libraries. In + * this case the ordinal value 0xfe (254) must be treated as a library ordinal + * for compatibility. + */ +/* TODO: +#define GET_LIBRARY_ORDINAL(n_desc) (((n_desc) >> 8) & 0xff) +#define SET_LIBRARY_ORDINAL(n_desc,ordinal) \ + (n_desc) = (((n_desc) & 0x00ff) | (((ordinal) & 0xff) << 8)) + */ +pub const SELF_LIBRARY_ORDINAL: u8 = 0x0; +pub const MAX_LIBRARY_ORDINAL: u8 = 0xfd; +pub const DYNAMIC_LOOKUP_ORDINAL: u8 = 0xfe; +pub const EXECUTABLE_ORDINAL: u8 = 0xff; + +/* + * The bit 0x0020 of the n_desc field is used for two non-overlapping purposes + * and has two different symbolic names, N_NO_DEAD_STRIP and N_DESC_DISCARDED. + */ + +/* + * The N_NO_DEAD_STRIP bit of the n_desc field only ever appears in a + * relocatable .o file (MH_OBJECT filetype). And is used to indicate to the + * static link editor it is never to dead strip the symbol. + */ +/// symbol is not to be dead stripped +pub const N_NO_DEAD_STRIP: u16 = 0x0020; + +/* + * The N_DESC_DISCARDED bit of the n_desc field never appears in linked image. + * But is used in very rare cases by the dynamic link editor to mark an in + * memory symbol as discared and longer used for linking. + */ +/// symbol is discarded +pub const N_DESC_DISCARDED: u16 = 0x0020; + +/* + * The N_WEAK_REF bit of the n_desc field indicates to the dynamic linker that + * the undefined symbol is allowed to be missing and is to have the address of + * zero when missing. + */ +/// symbol is weak referenced +pub const N_WEAK_REF: u16 = 0x0040; + +/* + * The N_WEAK_DEF bit of the n_desc field indicates to the static and dynamic + * linkers that the symbol definition is weak, allowing a non-weak symbol to + * also be used which causes the weak definition to be discared. Currently this + * is only supported for symbols in coalesced sections. + */ +/// coalesced symbol is a weak definition +pub const N_WEAK_DEF: u16 = 0x0080; + +/* + * The N_REF_TO_WEAK bit of the n_desc field indicates to the dynamic linker + * that the undefined symbol should be resolved using flat namespace searching. + */ +/// reference to a weak symbol +pub const N_REF_TO_WEAK: u16 = 0x0080; + +/* + * The N_ARM_THUMB_DEF bit of the n_desc field indicates that the symbol is + * a definition of a Thumb function. + */ +/// symbol is a Thumb function (ARM) +pub const N_ARM_THUMB_DEF: u16 = 0x0008; + +/* + * The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the + * that the function is actually a resolver function and should + * be called to get the address of the real function to use. + * This bit is only available in .o files (MH_OBJECT filetype) + */ +pub const N_SYMBOL_RESOLVER: u16 = 0x0100; + +/* + * The N_ALT_ENTRY bit of the n_desc field indicates that the + * symbol is pinned to the previous content. + */ +pub const N_ALT_ENTRY: u16 = 0x0200; + +// Definitions from "/usr/include/mach-o/stab.h". + +/* + * This file gives definitions supplementing <nlist.h> for permanent symbol + * table entries of Mach-O files. Modified from the BSD definitions. The + * modifications from the original definitions were changing what the values of + * what was the n_other field (an unused field) which is now the n_sect field. + * These modifications are required to support symbols in an arbitrary number of + * sections not just the three sections (text, data and bss) in a BSD file. + * The values of the defined constants have NOT been changed. + * + * These must have one of the N_STAB bits on. The n_value fields are subject + * to relocation according to the value of their n_sect field. So for types + * that refer to things in sections the n_sect field must be filled in with the + * proper section ordinal. For types that are not to have their n_value field + * relocatated the n_sect field must be NO_SECT. + */ + +/* + * Symbolic debugger symbols. The comments give the conventional use for + * + * .stabs "n_name", n_type, n_sect, n_desc, n_value + * + * where n_type is the defined constant and not listed in the comment. Other + * fields not listed are zero. n_sect is the section ordinal the entry is + * referring to. + */ +/// global symbol: name,,NO_SECT,type,0 +pub const N_GSYM: u8 = 0x20; +/// procedure name (f77 kludge): name,,NO_SECT,0,0 +pub const N_FNAME: u8 = 0x22; +/// procedure: name,,n_sect,linenumber,address +pub const N_FUN: u8 = 0x24; +/// static symbol: name,,n_sect,type,address +pub const N_STSYM: u8 = 0x26; +/// .lcomm symbol: name,,n_sect,type,address +pub const N_LCSYM: u8 = 0x28; +/// begin nsect sym: 0,,n_sect,0,address +pub const N_BNSYM: u8 = 0x2e; +/// AST file path: name,,NO_SECT,0,0 +pub const N_AST: u8 = 0x32; +/// emitted with gcc2_compiled and in gcc source +pub const N_OPT: u8 = 0x3c; +/// register sym: name,,NO_SECT,type,register +pub const N_RSYM: u8 = 0x40; +/// src line: 0,,n_sect,linenumber,address +pub const N_SLINE: u8 = 0x44; +/// end nsect sym: 0,,n_sect,0,address +pub const N_ENSYM: u8 = 0x4e; +/// structure elt: name,,NO_SECT,type,struct_offset +pub const N_SSYM: u8 = 0x60; +/// source file name: name,,n_sect,0,address +pub const N_SO: u8 = 0x64; +/// object file name: name,,0,0,st_mtime +pub const N_OSO: u8 = 0x66; +/// local sym: name,,NO_SECT,type,offset +pub const N_LSYM: u8 = 0x80; +/// include file beginning: name,,NO_SECT,0,sum +pub const N_BINCL: u8 = 0x82; +/// #included file name: name,,n_sect,0,address +pub const N_SOL: u8 = 0x84; +/// compiler parameters: name,,NO_SECT,0,0 +pub const N_PARAMS: u8 = 0x86; +/// compiler version: name,,NO_SECT,0,0 +pub const N_VERSION: u8 = 0x88; +/// compiler -O level: name,,NO_SECT,0,0 +pub const N_OLEVEL: u8 = 0x8A; +/// parameter: name,,NO_SECT,type,offset +pub const N_PSYM: u8 = 0xa0; +/// include file end: name,,NO_SECT,0,0 +pub const N_EINCL: u8 = 0xa2; +/// alternate entry: name,,n_sect,linenumber,address +pub const N_ENTRY: u8 = 0xa4; +/// left bracket: 0,,NO_SECT,nesting level,address +pub const N_LBRAC: u8 = 0xc0; +/// deleted include file: name,,NO_SECT,0,sum +pub const N_EXCL: u8 = 0xc2; +/// right bracket: 0,,NO_SECT,nesting level,address +pub const N_RBRAC: u8 = 0xe0; +/// begin common: name,,NO_SECT,0,0 +pub const N_BCOMM: u8 = 0xe2; +/// end common: name,,n_sect,0,0 +pub const N_ECOMM: u8 = 0xe4; +/// end common (local name): 0,,n_sect,0,address +pub const N_ECOML: u8 = 0xe8; +/// second stab entry with length information +pub const N_LENG: u8 = 0xfe; + +/* + * for the berkeley pascal compiler, pc(1): + */ +/// global pascal symbol: name,,NO_SECT,subtype,line +pub const N_PC: u8 = 0x30; + +// Definitions from "/usr/include/mach-o/reloc.h". + +/// A relocation entry. +/// +/// Mach-O relocations have plain and scattered variants, with the +/// meaning of the fields depending on the variant. +/// +/// This type provides functions for determining whether the relocation +/// is scattered, and for accessing the fields of each variant. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Relocation<E: Endian> { + pub r_word0: U32<E>, + pub r_word1: U32<E>, +} + +impl<E: Endian> Relocation<E> { + /// Determine whether this is a scattered relocation. + #[inline] + pub fn r_scattered(self, endian: E, cputype: u32) -> bool { + if cputype == CPU_TYPE_X86_64 { + false + } else { + self.r_word0.get(endian) & R_SCATTERED != 0 + } + } + + /// Return the fields of a plain relocation. + pub fn info(self, endian: E) -> RelocationInfo { + let r_address = self.r_word0.get(endian); + let r_word1 = self.r_word1.get(endian); + if endian.is_little_endian() { + RelocationInfo { + r_address, + r_symbolnum: r_word1 & 0x00ff_ffff, + r_pcrel: ((r_word1 >> 24) & 0x1) != 0, + r_length: ((r_word1 >> 25) & 0x3) as u8, + r_extern: ((r_word1 >> 27) & 0x1) != 0, + r_type: (r_word1 >> 28) as u8, + } + } else { + RelocationInfo { + r_address, + r_symbolnum: r_word1 >> 8, + r_pcrel: ((r_word1 >> 7) & 0x1) != 0, + r_length: ((r_word1 >> 5) & 0x3) as u8, + r_extern: ((r_word1 >> 4) & 0x1) != 0, + r_type: (r_word1 & 0xf) as u8, + } + } + } + + /// Return the fields of a scattered relocation. + pub fn scattered_info(self, endian: E) -> ScatteredRelocationInfo { + let r_word0 = self.r_word0.get(endian); + let r_value = self.r_word1.get(endian); + ScatteredRelocationInfo { + r_address: r_word0 & 0x00ff_ffff, + r_type: ((r_word0 >> 24) & 0xf) as u8, + r_length: ((r_word0 >> 28) & 0x3) as u8, + r_pcrel: ((r_word0 >> 30) & 0x1) != 0, + r_value, + } + } +} + +/* + * Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD + * format. The modifications from the original format were changing the value + * of the r_symbolnum field for "local" (r_extern == 0) relocation entries. + * This modification is required to support symbols in an arbitrary number of + * sections not just the three sections (text, data and bss) in a 4.3BSD file. + * Also the last 4 bits have had the r_type tag added to them. + */ + +#[derive(Debug, Clone, Copy)] +pub struct RelocationInfo { + /// offset in the section to what is being relocated + pub r_address: u32, + /// symbol index if r_extern == 1 or section ordinal if r_extern == 0 + pub r_symbolnum: u32, + /// was relocated pc relative already + pub r_pcrel: bool, + /// 0=byte, 1=word, 2=long, 3=quad + pub r_length: u8, + /// does not include value of sym referenced + pub r_extern: bool, + /// if not 0, machine specific relocation type + pub r_type: u8, +} + +impl RelocationInfo { + /// Combine the fields into a `Relocation`. + pub fn relocation<E: Endian>(self, endian: E) -> Relocation<E> { + let r_word0 = U32::new(endian, self.r_address); + let r_word1 = U32::new( + endian, + if endian.is_little_endian() { + self.r_symbolnum & 0x00ff_ffff + | u32::from(self.r_pcrel) << 24 + | u32::from(self.r_length & 0x3) << 25 + | u32::from(self.r_extern) << 27 + | u32::from(self.r_type) << 28 + } else { + self.r_symbolnum >> 8 + | u32::from(self.r_pcrel) << 7 + | u32::from(self.r_length & 0x3) << 5 + | u32::from(self.r_extern) << 4 + | u32::from(self.r_type) & 0xf + }, + ); + Relocation { r_word0, r_word1 } + } +} + +/// absolute relocation type for Mach-O files +pub const R_ABS: u8 = 0; + +/* + * The r_address is not really the address as it's name indicates but an offset. + * In 4.3BSD a.out objects this offset is from the start of the "segment" for + * which relocation entry is for (text or data). For Mach-O object files it is + * also an offset but from the start of the "section" for which the relocation + * entry is for. See comments in <mach-o/loader.h> about the r_address feild + * in images for used with the dynamic linker. + * + * In 4.3BSD a.out objects if r_extern is zero then r_symbolnum is an ordinal + * for the segment the symbol being relocated is in. These ordinals are the + * symbol types N_TEXT, N_DATA, N_BSS or N_ABS. In Mach-O object files these + * ordinals refer to the sections in the object file in the order their section + * structures appear in the headers of the object file they are in. The first + * section has the ordinal 1, the second 2, and so on. This means that the + * same ordinal in two different object files could refer to two different + * sections. And further could have still different ordinals when combined + * by the link-editor. The value R_ABS is used for relocation entries for + * absolute symbols which need no further relocation. + */ + +/* + * For RISC machines some of the references are split across two instructions + * and the instruction does not contain the complete value of the reference. + * In these cases a second, or paired relocation entry, follows each of these + * relocation entries, using a PAIR r_type, which contains the other part of the + * reference not contained in the instruction. This other part is stored in the + * pair's r_address field. The exact number of bits of the other part of the + * reference store in the r_address field is dependent on the particular + * relocation type for the particular architecture. + */ + +/* + * To make scattered loading by the link editor work correctly "local" + * relocation entries can't be used when the item to be relocated is the value + * of a symbol plus an offset (where the resulting expression is outside the + * block the link editor is moving, a blocks are divided at symbol addresses). + * In this case. where the item is a symbol value plus offset, the link editor + * needs to know more than just the section the symbol was defined. What is + * needed is the actual value of the symbol without the offset so it can do the + * relocation correctly based on where the value of the symbol got relocated to + * not the value of the expression (with the offset added to the symbol value). + * So for the NeXT 2.0 release no "local" relocation entries are ever used when + * there is a non-zero offset added to a symbol. The "external" and "local" + * relocation entries remain unchanged. + * + * The implementation is quite messy given the compatibility with the existing + * relocation entry format. The ASSUMPTION is that a section will never be + * bigger than 2**24 - 1 (0x00ffffff or 16,777,215) bytes. This assumption + * allows the r_address (which is really an offset) to fit in 24 bits and high + * bit of the r_address field in the relocation_info structure to indicate + * it is really a scattered_relocation_info structure. Since these are only + * used in places where "local" relocation entries are used and not where + * "external" relocation entries are used the r_extern field has been removed. + * + * For scattered loading to work on a RISC machine where some of the references + * are split across two instructions the link editor needs to be assured that + * each reference has a unique 32 bit reference (that more than one reference is + * NOT sharing the same high 16 bits for example) so it move each referenced + * item independent of each other. Some compilers guarantees this but the + * compilers don't so scattered loading can be done on those that do guarantee + * this. + */ + +/// Bit set in `Relocation::r_word0` for scattered relocations. +pub const R_SCATTERED: u32 = 0x8000_0000; + +#[derive(Debug, Clone, Copy)] +pub struct ScatteredRelocationInfo { + /// offset in the section to what is being relocated + pub r_address: u32, + /// if not 0, machine specific relocation type + pub r_type: u8, + /// 0=byte, 1=word, 2=long, 3=quad + pub r_length: u8, + /// was relocated pc relative already + pub r_pcrel: bool, + /// the value the item to be relocated is referring to (without any offset added) + pub r_value: u32, +} + +impl ScatteredRelocationInfo { + /// Combine the fields into a `Relocation`. + pub fn relocation<E: Endian>(self, endian: E) -> Relocation<E> { + let r_word0 = U32::new( + endian, + self.r_address & 0x00ff_ffff + | u32::from(self.r_type & 0xf) << 24 + | u32::from(self.r_length & 0x3) << 28 + | u32::from(self.r_pcrel) << 30 + | R_SCATTERED, + ); + let r_word1 = U32::new(endian, self.r_value); + Relocation { r_word0, r_word1 } + } +} + +/* + * Relocation types used in a generic implementation. Relocation entries for + * normal things use the generic relocation as described above and their r_type + * is GENERIC_RELOC_VANILLA (a value of zero). + * + * Another type of generic relocation, GENERIC_RELOC_SECTDIFF, is to support + * the difference of two symbols defined in different sections. That is the + * expression "symbol1 - symbol2 + constant" is a relocatable expression when + * both symbols are defined in some section. For this type of relocation the + * both relocations entries are scattered relocation entries. The value of + * symbol1 is stored in the first relocation entry's r_value field and the + * value of symbol2 is stored in the pair's r_value field. + * + * A special case for a prebound lazy pointer is needed to beable to set the + * value of the lazy pointer back to its non-prebound state. This is done + * using the GENERIC_RELOC_PB_LA_PTR r_type. This is a scattered relocation + * entry where the r_value feild is the value of the lazy pointer not prebound. + */ +/// generic relocation as described above +pub const GENERIC_RELOC_VANILLA: u8 = 0; +/// Only follows a GENERIC_RELOC_SECTDIFF +pub const GENERIC_RELOC_PAIR: u8 = 1; +pub const GENERIC_RELOC_SECTDIFF: u8 = 2; +/// prebound lazy pointer +pub const GENERIC_RELOC_PB_LA_PTR: u8 = 3; +pub const GENERIC_RELOC_LOCAL_SECTDIFF: u8 = 4; +/// thread local variables +pub const GENERIC_RELOC_TLV: u8 = 5; + +// Definitions from "/usr/include/mach-o/arm/reloc.h". + +/* + * Relocation types used in the arm implementation. Relocation entries for + * things other than instructions use the same generic relocation as described + * in <mach-o/reloc.h> and their r_type is ARM_RELOC_VANILLA, one of the + * *_SECTDIFF or the *_PB_LA_PTR types. The rest of the relocation types are + * for instructions. Since they are for instructions the r_address field + * indicates the 32 bit instruction that the relocation is to be performed on. + */ +/// generic relocation as described above +pub const ARM_RELOC_VANILLA: u8 = 0; +/// the second relocation entry of a pair +pub const ARM_RELOC_PAIR: u8 = 1; +/// a PAIR follows with subtract symbol value +pub const ARM_RELOC_SECTDIFF: u8 = 2; +/// like ARM_RELOC_SECTDIFF, but the symbol referenced was local. +pub const ARM_RELOC_LOCAL_SECTDIFF: u8 = 3; +/// prebound lazy pointer +pub const ARM_RELOC_PB_LA_PTR: u8 = 4; +/// 24 bit branch displacement (to a word address) +pub const ARM_RELOC_BR24: u8 = 5; +/// 22 bit branch displacement (to a half-word address) +pub const ARM_THUMB_RELOC_BR22: u8 = 6; +/// obsolete - a thumb 32-bit branch instruction possibly needing page-spanning branch workaround +pub const ARM_THUMB_32BIT_BRANCH: u8 = 7; + +/* + * For these two r_type relocations they always have a pair following them + * and the r_length bits are used differently. The encoding of the + * r_length is as follows: + * low bit of r_length: + * 0 - :lower16: for movw instructions + * 1 - :upper16: for movt instructions + * high bit of r_length: + * 0 - arm instructions + * 1 - thumb instructions + * the other half of the relocated expression is in the following pair + * relocation entry in the the low 16 bits of r_address field. + */ +pub const ARM_RELOC_HALF: u8 = 8; +pub const ARM_RELOC_HALF_SECTDIFF: u8 = 9; + +// Definitions from "/usr/include/mach-o/arm64/reloc.h". + +/* + * Relocation types used in the arm64 implementation. + */ +/// for pointers +pub const ARM64_RELOC_UNSIGNED: u8 = 0; +/// must be followed by a ARM64_RELOC_UNSIGNED +pub const ARM64_RELOC_SUBTRACTOR: u8 = 1; +/// a B/BL instruction with 26-bit displacement +pub const ARM64_RELOC_BRANCH26: u8 = 2; +/// pc-rel distance to page of target +pub const ARM64_RELOC_PAGE21: u8 = 3; +/// offset within page, scaled by r_length +pub const ARM64_RELOC_PAGEOFF12: u8 = 4; +/// pc-rel distance to page of GOT slot +pub const ARM64_RELOC_GOT_LOAD_PAGE21: u8 = 5; +/// offset within page of GOT slot, scaled by r_length +pub const ARM64_RELOC_GOT_LOAD_PAGEOFF12: u8 = 6; +/// for pointers to GOT slots +pub const ARM64_RELOC_POINTER_TO_GOT: u8 = 7; +/// pc-rel distance to page of TLVP slot +pub const ARM64_RELOC_TLVP_LOAD_PAGE21: u8 = 8; +/// offset within page of TLVP slot, scaled by r_length +pub const ARM64_RELOC_TLVP_LOAD_PAGEOFF12: u8 = 9; +/// must be followed by PAGE21 or PAGEOFF12 +pub const ARM64_RELOC_ADDEND: u8 = 10; + +// An arm64e authenticated pointer. +// +// Represents a pointer to a symbol (like ARM64_RELOC_UNSIGNED). +// Additionally, the resulting pointer is signed. The signature is +// specified in the target location: the addend is restricted to the lower +// 32 bits (instead of the full 64 bits for ARM64_RELOC_UNSIGNED): +// +// |63|62|61-51|50-49| 48 |47 - 32|31 - 0| +// | 1| 0| 0 | key | addr | discriminator | addend | +// +// The key is one of: +// IA: 00 IB: 01 +// DA: 10 DB: 11 +// +// The discriminator field is used as extra signature diversification. +// +// The addr field indicates whether the target address should be blended +// into the discriminator. +// +pub const ARM64_RELOC_AUTHENTICATED_POINTER: u8 = 11; + +// Definitions from "/usr/include/mach-o/ppc/reloc.h". + +/* + * Relocation types used in the ppc implementation. Relocation entries for + * things other than instructions use the same generic relocation as described + * above and their r_type is RELOC_VANILLA. The rest of the relocation types + * are for instructions. Since they are for instructions the r_address field + * indicates the 32 bit instruction that the relocation is to be performed on. + * The fields r_pcrel and r_length are ignored for non-RELOC_VANILLA r_types + * except for PPC_RELOC_BR14. + * + * For PPC_RELOC_BR14 if the r_length is the unused value 3, then the branch was + * statically predicted setting or clearing the Y-bit based on the sign of the + * displacement or the opcode. If this is the case the static linker must flip + * the value of the Y-bit if the sign of the displacement changes for non-branch + * always conditions. + */ +/// generic relocation as described above +pub const PPC_RELOC_VANILLA: u8 = 0; +/// the second relocation entry of a pair +pub const PPC_RELOC_PAIR: u8 = 1; +/// 14 bit branch displacement (to a word address) +pub const PPC_RELOC_BR14: u8 = 2; +/// 24 bit branch displacement (to a word address) +pub const PPC_RELOC_BR24: u8 = 3; +/// a PAIR follows with the low half +pub const PPC_RELOC_HI16: u8 = 4; +/// a PAIR follows with the high half +pub const PPC_RELOC_LO16: u8 = 5; +/// Same as the RELOC_HI16 except the low 16 bits and the high 16 bits are added together +/// with the low 16 bits sign extended first. This means if bit 15 of the low 16 bits is +/// set the high 16 bits stored in the instruction will be adjusted. +pub const PPC_RELOC_HA16: u8 = 6; +/// Same as the LO16 except that the low 2 bits are not stored in the instruction and are +/// always zero. This is used in double word load/store instructions. +pub const PPC_RELOC_LO14: u8 = 7; +/// a PAIR follows with subtract symbol value +pub const PPC_RELOC_SECTDIFF: u8 = 8; +/// prebound lazy pointer +pub const PPC_RELOC_PB_LA_PTR: u8 = 9; +/// section difference forms of above. a PAIR +pub const PPC_RELOC_HI16_SECTDIFF: u8 = 10; +/// follows these with subtract symbol value +pub const PPC_RELOC_LO16_SECTDIFF: u8 = 11; +pub const PPC_RELOC_HA16_SECTDIFF: u8 = 12; +pub const PPC_RELOC_JBSR: u8 = 13; +pub const PPC_RELOC_LO14_SECTDIFF: u8 = 14; +/// like PPC_RELOC_SECTDIFF, but the symbol referenced was local. +pub const PPC_RELOC_LOCAL_SECTDIFF: u8 = 15; + +// Definitions from "/usr/include/mach-o/x86_64/reloc.h". + +/* + * Relocations for x86_64 are a bit different than for other architectures in + * Mach-O: Scattered relocations are not used. Almost all relocations produced + * by the compiler are external relocations. An external relocation has the + * r_extern bit set to 1 and the r_symbolnum field contains the symbol table + * index of the target label. + * + * When the assembler is generating relocations, if the target label is a local + * label (begins with 'L'), then the previous non-local label in the same + * section is used as the target of the external relocation. An addend is used + * with the distance from that non-local label to the target label. Only when + * there is no previous non-local label in the section is an internal + * relocation used. + * + * The addend (i.e. the 4 in _foo+4) is encoded in the instruction (Mach-O does + * not have RELA relocations). For PC-relative relocations, the addend is + * stored directly in the instruction. This is different from other Mach-O + * architectures, which encode the addend minus the current section offset. + * + * The relocation types are: + * + * X86_64_RELOC_UNSIGNED // for absolute addresses + * X86_64_RELOC_SIGNED // for signed 32-bit displacement + * X86_64_RELOC_BRANCH // a CALL/JMP instruction with 32-bit displacement + * X86_64_RELOC_GOT_LOAD // a MOVQ load of a GOT entry + * X86_64_RELOC_GOT // other GOT references + * X86_64_RELOC_SUBTRACTOR // must be followed by a X86_64_RELOC_UNSIGNED + * + * The following are sample assembly instructions, followed by the relocation + * and section content they generate in an object file: + * + * call _foo + * r_type=X86_64_RELOC_BRANCH, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * E8 00 00 00 00 + * + * call _foo+4 + * r_type=X86_64_RELOC_BRANCH, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * E8 04 00 00 00 + * + * movq _foo@GOTPCREL(%rip), %rax + * r_type=X86_64_RELOC_GOT_LOAD, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * 48 8B 05 00 00 00 00 + * + * pushq _foo@GOTPCREL(%rip) + * r_type=X86_64_RELOC_GOT, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * FF 35 00 00 00 00 + * + * movl _foo(%rip), %eax + * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * 8B 05 00 00 00 00 + * + * movl _foo+4(%rip), %eax + * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * 8B 05 04 00 00 00 + * + * movb $0x12, _foo(%rip) + * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * C6 05 FF FF FF FF 12 + * + * movl $0x12345678, _foo(%rip) + * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo + * C7 05 FC FF FF FF 78 56 34 12 + * + * .quad _foo + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo + * 00 00 00 00 00 00 00 00 + * + * .quad _foo+4 + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo + * 04 00 00 00 00 00 00 00 + * + * .quad _foo - _bar + * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_bar + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo + * 00 00 00 00 00 00 00 00 + * + * .quad _foo - _bar + 4 + * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_bar + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo + * 04 00 00 00 00 00 00 00 + * + * .long _foo - _bar + * r_type=X86_64_RELOC_SUBTRACTOR, r_length=2, r_extern=1, r_pcrel=0, r_symbolnum=_bar + * r_type=X86_64_RELOC_UNSIGNED, r_length=2, r_extern=1, r_pcrel=0, r_symbolnum=_foo + * 00 00 00 00 + * + * lea L1(%rip), %rax + * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_prev + * 48 8d 05 12 00 00 00 + * // assumes _prev is the first non-local label 0x12 bytes before L1 + * + * lea L0(%rip), %rax + * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=0, r_pcrel=1, r_symbolnum=3 + * 48 8d 05 56 00 00 00 + * // assumes L0 is in third section and there is no previous non-local label. + * // The rip-relative-offset of 0x00000056 is L0-address_of_next_instruction. + * // address_of_next_instruction is the address of the relocation + 4. + * + * add $6,L0(%rip) + * r_type=X86_64_RELOC_SIGNED_1, r_length=2, r_extern=0, r_pcrel=1, r_symbolnum=3 + * 83 05 18 00 00 00 06 + * // assumes L0 is in third section and there is no previous non-local label. + * // The rip-relative-offset of 0x00000018 is L0-address_of_next_instruction. + * // address_of_next_instruction is the address of the relocation + 4 + 1. + * // The +1 comes from SIGNED_1. This is used because the relocation is not + * // at the end of the instruction. + * + * .quad L1 + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev + * 12 00 00 00 00 00 00 00 + * // assumes _prev is the first non-local label 0x12 bytes before L1 + * + * .quad L0 + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=0, r_pcrel=0, r_symbolnum=3 + * 56 00 00 00 00 00 00 00 + * // assumes L0 is in third section, has an address of 0x00000056 in .o + * // file, and there is no previous non-local label + * + * .quad _foo - . + * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo + * EE FF FF FF FF FF FF FF + * // assumes _prev is the first non-local label 0x12 bytes before this + * // .quad + * + * .quad _foo - L1 + * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo + * EE FF FF FF FF FF FF FF + * // assumes _prev is the first non-local label 0x12 bytes before L1 + * + * .quad L1 - _prev + * // No relocations. This is an assembly time constant. + * 12 00 00 00 00 00 00 00 + * // assumes _prev is the first non-local label 0x12 bytes before L1 + * + * + * + * In final linked images, there are only two valid relocation kinds: + * + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_pcrel=0, r_extern=1, r_symbolnum=sym_index + * This tells dyld to add the address of a symbol to a pointer sized (8-byte) + * piece of data (i.e on disk the 8-byte piece of data contains the addend). The + * r_symbolnum contains the index into the symbol table of the target symbol. + * + * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_pcrel=0, r_extern=0, r_symbolnum=0 + * This tells dyld to adjust the pointer sized (8-byte) piece of data by the amount + * the containing image was loaded from its base address (e.g. slide). + * + */ +/// for absolute addresses +pub const X86_64_RELOC_UNSIGNED: u8 = 0; +/// for signed 32-bit displacement +pub const X86_64_RELOC_SIGNED: u8 = 1; +/// a CALL/JMP instruction with 32-bit displacement +pub const X86_64_RELOC_BRANCH: u8 = 2; +/// a MOVQ load of a GOT entry +pub const X86_64_RELOC_GOT_LOAD: u8 = 3; +/// other GOT references +pub const X86_64_RELOC_GOT: u8 = 4; +/// must be followed by a X86_64_RELOC_UNSIGNED +pub const X86_64_RELOC_SUBTRACTOR: u8 = 5; +/// for signed 32-bit displacement with a -1 addend +pub const X86_64_RELOC_SIGNED_1: u8 = 6; +/// for signed 32-bit displacement with a -2 addend +pub const X86_64_RELOC_SIGNED_2: u8 = 7; +/// for signed 32-bit displacement with a -4 addend +pub const X86_64_RELOC_SIGNED_4: u8 = 8; +/// for thread local variables +pub const X86_64_RELOC_TLV: u8 = 9; + +unsafe_impl_pod!(FatHeader, FatArch32, FatArch64,); +unsafe_impl_endian_pod!( + DyldCacheHeader, + DyldCacheMappingInfo, + DyldCacheMappingAndSlideInfo, + DyldCacheImageInfo, + DyldCacheSlideInfo2, + DyldCacheSlideInfo3, + DyldCacheSlideInfo5, + DyldSubCacheEntryV1, + DyldSubCacheEntryV2, + MachHeader32, + MachHeader64, + LoadCommand, + LcStr, + SegmentCommand32, + SegmentCommand64, + Section32, + Section64, + Fvmlib, + FvmlibCommand, + Dylib, + DylibCommand, + SubFrameworkCommand, + SubClientCommand, + SubUmbrellaCommand, + SubLibraryCommand, + PreboundDylibCommand, + DylinkerCommand, + ThreadCommand, + RoutinesCommand32, + RoutinesCommand64, + SymtabCommand, + DysymtabCommand, + DylibTableOfContents, + DylibModule32, + DylibModule64, + DylibReference, + TwolevelHintsCommand, + TwolevelHint, + PrebindCksumCommand, + UuidCommand, + RpathCommand, + LinkeditDataCommand, + FilesetEntryCommand, + EncryptionInfoCommand32, + EncryptionInfoCommand64, + VersionMinCommand, + BuildVersionCommand, + BuildToolVersion, + DyldInfoCommand, + LinkerOptionCommand, + SymsegCommand, + IdentCommand, + FvmfileCommand, + EntryPointCommand, + SourceVersionCommand, + DataInCodeEntry, + //TlvDescriptor, + NoteCommand, + Nlist32, + Nlist64, + Relocation, +);
diff --git a/chromium_crates_io/vendor/object-v0_37/src/pe.rs b/chromium_crates_io/vendor/object-v0_37/src/pe.rs new file mode 100644 index 0000000..28c4b2c --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/pe.rs
@@ -0,0 +1,3062 @@ +//! PE/COFF definitions. +//! +//! These definitions are independent of read/write support, although we do implement +//! some traits useful for those. +//! +//! This module is based heavily on "winnt.h" (10.0.17763.0). + +#![allow(missing_docs)] + +use core::convert::TryInto; + +use crate::endian::{I32Bytes, LittleEndian as LE, U16Bytes, U32Bytes, I32, U16, U32, U64}; +use crate::pod::Pod; + +/// MZ +pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; +/// NE +pub const IMAGE_OS2_SIGNATURE: u16 = 0x454E; +/// LE +pub const IMAGE_OS2_SIGNATURE_LE: u16 = 0x454C; +/// LE +pub const IMAGE_VXD_SIGNATURE: u16 = 0x454C; +/// PE00 +pub const IMAGE_NT_SIGNATURE: u32 = 0x0000_4550; + +/// DOS .EXE header +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDosHeader { + /// Magic number + pub e_magic: U16<LE>, + /// Bytes on last page of file + pub e_cblp: U16<LE>, + /// Pages in file + pub e_cp: U16<LE>, + /// Relocations + pub e_crlc: U16<LE>, + /// Size of header in paragraphs + pub e_cparhdr: U16<LE>, + /// Minimum extra paragraphs needed + pub e_minalloc: U16<LE>, + /// Maximum extra paragraphs needed + pub e_maxalloc: U16<LE>, + /// Initial (relative) SS value + pub e_ss: U16<LE>, + /// Initial SP value + pub e_sp: U16<LE>, + /// Checksum + pub e_csum: U16<LE>, + /// Initial IP value + pub e_ip: U16<LE>, + /// Initial (relative) CS value + pub e_cs: U16<LE>, + /// File address of relocation table + pub e_lfarlc: U16<LE>, + /// Overlay number + pub e_ovno: U16<LE>, + /// Reserved words + pub e_res: [U16<LE>; 4], + /// OEM identifier (for e_oeminfo) + pub e_oemid: U16<LE>, + /// OEM information; e_oemid specific + pub e_oeminfo: U16<LE>, + /// Reserved words + pub e_res2: [U16<LE>; 10], + /// File address of new exe header + pub e_lfanew: U32<LE>, +} + +/// OS/2 .EXE header +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageOs2Header { + /// Magic number + pub ne_magic: U16<LE>, + /// Version number + pub ne_ver: i8, + /// Revision number + pub ne_rev: i8, + /// Offset of Entry Table + pub ne_enttab: U16<LE>, + /// Number of bytes in Entry Table + pub ne_cbenttab: U16<LE>, + /// Checksum of whole file + pub ne_crc: I32<LE>, + /// Flag word + pub ne_flags: U16<LE>, + /// Automatic data segment number + pub ne_autodata: U16<LE>, + /// Initial heap allocation + pub ne_heap: U16<LE>, + /// Initial stack allocation + pub ne_stack: U16<LE>, + /// Initial CS:IP setting + pub ne_csip: I32<LE>, + /// Initial SS:SP setting + pub ne_sssp: I32<LE>, + /// Count of file segments + pub ne_cseg: U16<LE>, + /// Entries in Module Reference Table + pub ne_cmod: U16<LE>, + /// Size of non-resident name table + pub ne_cbnrestab: U16<LE>, + /// Offset of Segment Table + pub ne_segtab: U16<LE>, + /// Offset of Resource Table + pub ne_rsrctab: U16<LE>, + /// Offset of resident name table + pub ne_restab: U16<LE>, + /// Offset of Module Reference Table + pub ne_modtab: U16<LE>, + /// Offset of Imported Names Table + pub ne_imptab: U16<LE>, + /// Offset of Non-resident Names Table + pub ne_nrestab: I32<LE>, + /// Count of movable entries + pub ne_cmovent: U16<LE>, + /// Segment alignment shift count + pub ne_align: U16<LE>, + /// Count of resource segments + pub ne_cres: U16<LE>, + /// Target Operating system + pub ne_exetyp: u8, + /// Other .EXE flags + pub ne_flagsothers: u8, + /// offset to return thunks + pub ne_pretthunks: U16<LE>, + /// offset to segment ref. bytes + pub ne_psegrefbytes: U16<LE>, + /// Minimum code swap area size + pub ne_swaparea: U16<LE>, + /// Expected Windows version number + pub ne_expver: U16<LE>, +} + +/// Windows VXD header +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageVxdHeader { + /// Magic number + pub e32_magic: U16<LE>, + /// The byte ordering for the VXD + pub e32_border: u8, + /// The word ordering for the VXD + pub e32_worder: u8, + /// The EXE format level for now = 0 + pub e32_level: U32<LE>, + /// The CPU type + pub e32_cpu: U16<LE>, + /// The OS type + pub e32_os: U16<LE>, + /// Module version + pub e32_ver: U32<LE>, + /// Module flags + pub e32_mflags: U32<LE>, + /// Module # pages + pub e32_mpages: U32<LE>, + /// Object # for instruction pointer + pub e32_startobj: U32<LE>, + /// Extended instruction pointer + pub e32_eip: U32<LE>, + /// Object # for stack pointer + pub e32_stackobj: U32<LE>, + /// Extended stack pointer + pub e32_esp: U32<LE>, + /// VXD page size + pub e32_pagesize: U32<LE>, + /// Last page size in VXD + pub e32_lastpagesize: U32<LE>, + /// Fixup section size + pub e32_fixupsize: U32<LE>, + /// Fixup section checksum + pub e32_fixupsum: U32<LE>, + /// Loader section size + pub e32_ldrsize: U32<LE>, + /// Loader section checksum + pub e32_ldrsum: U32<LE>, + /// Object table offset + pub e32_objtab: U32<LE>, + /// Number of objects in module + pub e32_objcnt: U32<LE>, + /// Object page map offset + pub e32_objmap: U32<LE>, + /// Object iterated data map offset + pub e32_itermap: U32<LE>, + /// Offset of Resource Table + pub e32_rsrctab: U32<LE>, + /// Number of resource entries + pub e32_rsrccnt: U32<LE>, + /// Offset of resident name table + pub e32_restab: U32<LE>, + /// Offset of Entry Table + pub e32_enttab: U32<LE>, + /// Offset of Module Directive Table + pub e32_dirtab: U32<LE>, + /// Number of module directives + pub e32_dircnt: U32<LE>, + /// Offset of Fixup Page Table + pub e32_fpagetab: U32<LE>, + /// Offset of Fixup Record Table + pub e32_frectab: U32<LE>, + /// Offset of Import Module Name Table + pub e32_impmod: U32<LE>, + /// Number of entries in Import Module Name Table + pub e32_impmodcnt: U32<LE>, + /// Offset of Import Procedure Name Table + pub e32_impproc: U32<LE>, + /// Offset of Per-Page Checksum Table + pub e32_pagesum: U32<LE>, + /// Offset of Enumerated Data Pages + pub e32_datapage: U32<LE>, + /// Number of preload pages + pub e32_preload: U32<LE>, + /// Offset of Non-resident Names Table + pub e32_nrestab: U32<LE>, + /// Size of Non-resident Name Table + pub e32_cbnrestab: U32<LE>, + /// Non-resident Name Table Checksum + pub e32_nressum: U32<LE>, + /// Object # for automatic data object + pub e32_autodata: U32<LE>, + /// Offset of the debugging information + pub e32_debuginfo: U32<LE>, + /// The length of the debugging info. in bytes + pub e32_debuglen: U32<LE>, + /// Number of instance pages in preload section of VXD file + pub e32_instpreload: U32<LE>, + /// Number of instance pages in demand load section of VXD file + pub e32_instdemand: U32<LE>, + /// Size of heap - for 16-bit apps + pub e32_heapsize: U32<LE>, + /// Reserved words + pub e32_res3: [u8; 12], + pub e32_winresoff: U32<LE>, + pub e32_winreslen: U32<LE>, + /// Device ID for VxD + pub e32_devid: U16<LE>, + /// DDK version for VxD + pub e32_ddkver: U16<LE>, +} + +/// A PE rich header entry. +/// +/// Rich headers have no official documentation, but have been heavily +/// reversed-engineered and documented in the wild, e.g.: +/// * `http://www.ntcore.com/files/richsign.htm` +/// * `https://www.researchgate.net/figure/Structure-of-the-Rich-Header_fig1_318145388` +/// +/// This data is "masked", i.e. XORed with a checksum derived from the file data. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct MaskedRichHeaderEntry { + pub masked_comp_id: U32<LE>, + pub masked_count: U32<LE>, +} + +// +// File header format. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageFileHeader { + pub machine: U16<LE>, + pub number_of_sections: U16<LE>, + pub time_date_stamp: U32<LE>, + pub pointer_to_symbol_table: U32<LE>, + pub number_of_symbols: U32<LE>, + pub size_of_optional_header: U16<LE>, + pub characteristics: U16<LE>, +} + +pub const IMAGE_SIZEOF_FILE_HEADER: usize = 20; + +/// Relocation info stripped from file. +pub const IMAGE_FILE_RELOCS_STRIPPED: u16 = 0x0001; +/// File is executable (i.e. no unresolved external references). +pub const IMAGE_FILE_EXECUTABLE_IMAGE: u16 = 0x0002; +/// Line numbers stripped from file. +pub const IMAGE_FILE_LINE_NUMS_STRIPPED: u16 = 0x0004; +/// Local symbols stripped from file. +pub const IMAGE_FILE_LOCAL_SYMS_STRIPPED: u16 = 0x0008; +/// Aggressively trim working set +pub const IMAGE_FILE_AGGRESIVE_WS_TRIM: u16 = 0x0010; +/// App can handle >2gb addresses +pub const IMAGE_FILE_LARGE_ADDRESS_AWARE: u16 = 0x0020; +/// Bytes of machine word are reversed. +pub const IMAGE_FILE_BYTES_REVERSED_LO: u16 = 0x0080; +/// 32 bit word machine. +pub const IMAGE_FILE_32BIT_MACHINE: u16 = 0x0100; +/// Debugging info stripped from file in .DBG file +pub const IMAGE_FILE_DEBUG_STRIPPED: u16 = 0x0200; +/// If Image is on removable media, copy and run from the swap file. +pub const IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP: u16 = 0x0400; +/// If Image is on Net, copy and run from the swap file. +pub const IMAGE_FILE_NET_RUN_FROM_SWAP: u16 = 0x0800; +/// System File. +pub const IMAGE_FILE_SYSTEM: u16 = 0x1000; +/// File is a DLL. +pub const IMAGE_FILE_DLL: u16 = 0x2000; +/// File should only be run on a UP machine +pub const IMAGE_FILE_UP_SYSTEM_ONLY: u16 = 0x4000; +/// Bytes of machine word are reversed. +pub const IMAGE_FILE_BYTES_REVERSED_HI: u16 = 0x8000; + +pub const IMAGE_FILE_MACHINE_UNKNOWN: u16 = 0; +/// Useful for indicating we want to interact with the host and not a WoW guest. +pub const IMAGE_FILE_MACHINE_TARGET_HOST: u16 = 0x0001; +/// Intel 386. +pub const IMAGE_FILE_MACHINE_I386: u16 = 0x014c; +/// MIPS little-endian, 0x160 big-endian +pub const IMAGE_FILE_MACHINE_R3000: u16 = 0x0162; +/// MIPS little-endian +pub const IMAGE_FILE_MACHINE_R4000: u16 = 0x0166; +/// MIPS little-endian +pub const IMAGE_FILE_MACHINE_R10000: u16 = 0x0168; +/// MIPS little-endian WCE v2 +pub const IMAGE_FILE_MACHINE_WCEMIPSV2: u16 = 0x0169; +/// Alpha_AXP +pub const IMAGE_FILE_MACHINE_ALPHA: u16 = 0x0184; +/// SH3 little-endian +pub const IMAGE_FILE_MACHINE_SH3: u16 = 0x01a2; +pub const IMAGE_FILE_MACHINE_SH3DSP: u16 = 0x01a3; +/// SH3E little-endian +pub const IMAGE_FILE_MACHINE_SH3E: u16 = 0x01a4; +/// SH4 little-endian +pub const IMAGE_FILE_MACHINE_SH4: u16 = 0x01a6; +/// SH5 +pub const IMAGE_FILE_MACHINE_SH5: u16 = 0x01a8; +/// ARM Little-Endian +pub const IMAGE_FILE_MACHINE_ARM: u16 = 0x01c0; +/// ARM Thumb/Thumb-2 Little-Endian +pub const IMAGE_FILE_MACHINE_THUMB: u16 = 0x01c2; +/// ARM Thumb-2 Little-Endian +pub const IMAGE_FILE_MACHINE_ARMNT: u16 = 0x01c4; +pub const IMAGE_FILE_MACHINE_AM33: u16 = 0x01d3; +/// IBM PowerPC Little-Endian +pub const IMAGE_FILE_MACHINE_POWERPC: u16 = 0x01F0; +pub const IMAGE_FILE_MACHINE_POWERPCFP: u16 = 0x01f1; +/// IBM PowerPC Big-Endian +pub const IMAGE_FILE_MACHINE_POWERPCBE: u16 = 0x01f2; +/// Intel 64 +pub const IMAGE_FILE_MACHINE_IA64: u16 = 0x0200; +/// MIPS +pub const IMAGE_FILE_MACHINE_MIPS16: u16 = 0x0266; +/// ALPHA64 +pub const IMAGE_FILE_MACHINE_ALPHA64: u16 = 0x0284; +/// MIPS +pub const IMAGE_FILE_MACHINE_MIPSFPU: u16 = 0x0366; +/// MIPS +pub const IMAGE_FILE_MACHINE_MIPSFPU16: u16 = 0x0466; +pub const IMAGE_FILE_MACHINE_AXP64: u16 = IMAGE_FILE_MACHINE_ALPHA64; +/// Infineon +pub const IMAGE_FILE_MACHINE_TRICORE: u16 = 0x0520; +pub const IMAGE_FILE_MACHINE_CEF: u16 = 0x0CEF; +/// EFI Byte Code +pub const IMAGE_FILE_MACHINE_EBC: u16 = 0x0EBC; +/// AMD64 (K8) +pub const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664; +/// M32R little-endian +pub const IMAGE_FILE_MACHINE_M32R: u16 = 0x9041; +/// ARM64 Little-Endian +pub const IMAGE_FILE_MACHINE_ARM64: u16 = 0xAA64; +/// ARM64EC ("Emulation Compatible") +pub const IMAGE_FILE_MACHINE_ARM64EC: u16 = 0xA641; +pub const IMAGE_FILE_MACHINE_CEE: u16 = 0xC0EE; +/// RISCV32 +pub const IMAGE_FILE_MACHINE_RISCV32: u16 = 0x5032; +/// RISCV64 +pub const IMAGE_FILE_MACHINE_RISCV64: u16 = 0x5064; +/// RISCV128 +pub const IMAGE_FILE_MACHINE_RISCV128: u16 = 0x5128; +/// ARM64X (Mixed ARM64 and ARM64EC) +pub const IMAGE_FILE_MACHINE_ARM64X: u16 = 0xA64E; +/// CHPE x86 ("Compiled Hybrid Portable Executable") +pub const IMAGE_FILE_MACHINE_CHPE_X86: u16 = 0x3A64; + +// +// Directory format. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDataDirectory { + pub virtual_address: U32<LE>, + pub size: U32<LE>, +} + +pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES: usize = 16; + +// +// Optional header format. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageOptionalHeader32 { + // Standard fields. + pub magic: U16<LE>, + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub size_of_code: U32<LE>, + pub size_of_initialized_data: U32<LE>, + pub size_of_uninitialized_data: U32<LE>, + pub address_of_entry_point: U32<LE>, + pub base_of_code: U32<LE>, + pub base_of_data: U32<LE>, + + // NT additional fields. + pub image_base: U32<LE>, + pub section_alignment: U32<LE>, + pub file_alignment: U32<LE>, + pub major_operating_system_version: U16<LE>, + pub minor_operating_system_version: U16<LE>, + pub major_image_version: U16<LE>, + pub minor_image_version: U16<LE>, + pub major_subsystem_version: U16<LE>, + pub minor_subsystem_version: U16<LE>, + pub win32_version_value: U32<LE>, + pub size_of_image: U32<LE>, + pub size_of_headers: U32<LE>, + pub check_sum: U32<LE>, + pub subsystem: U16<LE>, + pub dll_characteristics: U16<LE>, + pub size_of_stack_reserve: U32<LE>, + pub size_of_stack_commit: U32<LE>, + pub size_of_heap_reserve: U32<LE>, + pub size_of_heap_commit: U32<LE>, + pub loader_flags: U32<LE>, + pub number_of_rva_and_sizes: U32<LE>, + //pub data_directory: [ImageDataDirectory; IMAGE_NUMBEROF_DIRECTORY_ENTRIES], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageRomOptionalHeader { + pub magic: U16<LE>, + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub size_of_code: U32<LE>, + pub size_of_initialized_data: U32<LE>, + pub size_of_uninitialized_data: U32<LE>, + pub address_of_entry_point: U32<LE>, + pub base_of_code: U32<LE>, + pub base_of_data: U32<LE>, + pub base_of_bss: U32<LE>, + pub gpr_mask: U32<LE>, + pub cpr_mask: [U32<LE>; 4], + pub gp_value: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageOptionalHeader64 { + pub magic: U16<LE>, + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub size_of_code: U32<LE>, + pub size_of_initialized_data: U32<LE>, + pub size_of_uninitialized_data: U32<LE>, + pub address_of_entry_point: U32<LE>, + pub base_of_code: U32<LE>, + pub image_base: U64<LE>, + pub section_alignment: U32<LE>, + pub file_alignment: U32<LE>, + pub major_operating_system_version: U16<LE>, + pub minor_operating_system_version: U16<LE>, + pub major_image_version: U16<LE>, + pub minor_image_version: U16<LE>, + pub major_subsystem_version: U16<LE>, + pub minor_subsystem_version: U16<LE>, + pub win32_version_value: U32<LE>, + pub size_of_image: U32<LE>, + pub size_of_headers: U32<LE>, + pub check_sum: U32<LE>, + pub subsystem: U16<LE>, + pub dll_characteristics: U16<LE>, + pub size_of_stack_reserve: U64<LE>, + pub size_of_stack_commit: U64<LE>, + pub size_of_heap_reserve: U64<LE>, + pub size_of_heap_commit: U64<LE>, + pub loader_flags: U32<LE>, + pub number_of_rva_and_sizes: U32<LE>, + //pub data_directory: [ImageDataDirectory; IMAGE_NUMBEROF_DIRECTORY_ENTRIES], +} + +pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC: u16 = 0x10b; +pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC: u16 = 0x20b; +pub const IMAGE_ROM_OPTIONAL_HDR_MAGIC: u16 = 0x107; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageNtHeaders64 { + pub signature: U32<LE>, + pub file_header: ImageFileHeader, + pub optional_header: ImageOptionalHeader64, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageNtHeaders32 { + pub signature: U32<LE>, + pub file_header: ImageFileHeader, + pub optional_header: ImageOptionalHeader32, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageRomHeaders { + pub file_header: ImageFileHeader, + pub optional_header: ImageRomOptionalHeader, +} + +// Values for `ImageOptionalHeader*::subsystem`. + +/// Unknown subsystem. +pub const IMAGE_SUBSYSTEM_UNKNOWN: u16 = 0; +/// Image doesn't require a subsystem. +pub const IMAGE_SUBSYSTEM_NATIVE: u16 = 1; +/// Image runs in the Windows GUI subsystem. +pub const IMAGE_SUBSYSTEM_WINDOWS_GUI: u16 = 2; +/// Image runs in the Windows character subsystem. +pub const IMAGE_SUBSYSTEM_WINDOWS_CUI: u16 = 3; +/// image runs in the OS/2 character subsystem. +pub const IMAGE_SUBSYSTEM_OS2_CUI: u16 = 5; +/// image runs in the Posix character subsystem. +pub const IMAGE_SUBSYSTEM_POSIX_CUI: u16 = 7; +/// image is a native Win9x driver. +pub const IMAGE_SUBSYSTEM_NATIVE_WINDOWS: u16 = 8; +/// Image runs in the Windows CE subsystem. +pub const IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: u16 = 9; +pub const IMAGE_SUBSYSTEM_EFI_APPLICATION: u16 = 10; +pub const IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: u16 = 11; +pub const IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: u16 = 12; +pub const IMAGE_SUBSYSTEM_EFI_ROM: u16 = 13; +pub const IMAGE_SUBSYSTEM_XBOX: u16 = 14; +pub const IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: u16 = 16; +pub const IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG: u16 = 17; + +// Values for `ImageOptionalHeader*::dll_characteristics`. + +// IMAGE_LIBRARY_PROCESS_INIT 0x0001 // Reserved. +// IMAGE_LIBRARY_PROCESS_TERM 0x0002 // Reserved. +// IMAGE_LIBRARY_THREAD_INIT 0x0004 // Reserved. +// IMAGE_LIBRARY_THREAD_TERM 0x0008 // Reserved. +/// Image can handle a high entropy 64-bit virtual address space. +pub const IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA: u16 = 0x0020; +/// DLL can move. +pub const IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE: u16 = 0x0040; +/// Code Integrity Image +pub const IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY: u16 = 0x0080; +/// Image is NX compatible +pub const IMAGE_DLLCHARACTERISTICS_NX_COMPAT: u16 = 0x0100; +/// Image understands isolation and doesn't want it +pub const IMAGE_DLLCHARACTERISTICS_NO_ISOLATION: u16 = 0x0200; +/// Image does not use SEH. No SE handler may reside in this image +pub const IMAGE_DLLCHARACTERISTICS_NO_SEH: u16 = 0x0400; +/// Do not bind this image. +pub const IMAGE_DLLCHARACTERISTICS_NO_BIND: u16 = 0x0800; +/// Image should execute in an AppContainer +pub const IMAGE_DLLCHARACTERISTICS_APPCONTAINER: u16 = 0x1000; +/// Driver uses WDM model +pub const IMAGE_DLLCHARACTERISTICS_WDM_DRIVER: u16 = 0x2000; +/// Image supports Control Flow Guard. +pub const IMAGE_DLLCHARACTERISTICS_GUARD_CF: u16 = 0x4000; +pub const IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE: u16 = 0x8000; + +// Indices for `ImageOptionalHeader*::data_directory`. + +/// Export Directory +pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0; +/// Import Directory +pub const IMAGE_DIRECTORY_ENTRY_IMPORT: usize = 1; +/// Resource Directory +pub const IMAGE_DIRECTORY_ENTRY_RESOURCE: usize = 2; +/// Exception Directory +pub const IMAGE_DIRECTORY_ENTRY_EXCEPTION: usize = 3; +/// Security Directory +pub const IMAGE_DIRECTORY_ENTRY_SECURITY: usize = 4; +/// Base Relocation Table +pub const IMAGE_DIRECTORY_ENTRY_BASERELOC: usize = 5; +/// Debug Directory +pub const IMAGE_DIRECTORY_ENTRY_DEBUG: usize = 6; +// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) +/// Architecture Specific Data +pub const IMAGE_DIRECTORY_ENTRY_ARCHITECTURE: usize = 7; +/// RVA of GP +pub const IMAGE_DIRECTORY_ENTRY_GLOBALPTR: usize = 8; +/// TLS Directory +pub const IMAGE_DIRECTORY_ENTRY_TLS: usize = 9; +/// Load Configuration Directory +pub const IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: usize = 10; +/// Bound Import Directory in headers +pub const IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: usize = 11; +/// Import Address Table +pub const IMAGE_DIRECTORY_ENTRY_IAT: usize = 12; +/// Delay Load Import Descriptors +pub const IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: usize = 13; +/// COM Runtime descriptor +pub const IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: usize = 14; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct Guid(pub [u8; 16]); + +impl Guid { + #[inline] + pub fn data1(self) -> U32<LE> { + U32::from_bytes(self.0[0..4].try_into().unwrap()) + } + + #[inline] + pub fn data2(self) -> U16<LE> { + U16::from_bytes(self.0[4..6].try_into().unwrap()) + } + + #[inline] + pub fn data3(self) -> U16<LE> { + U16::from_bytes(self.0[6..8].try_into().unwrap()) + } + + #[inline] + pub fn data4(self) -> [u8; 8] { + self.0[8..16].try_into().unwrap() + } +} + +pub use Guid as ClsId; + +/// Non-COFF Object file header +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AnonObjectHeader { + /// Must be IMAGE_FILE_MACHINE_UNKNOWN + pub sig1: U16<LE>, + /// Must be 0xffff + pub sig2: U16<LE>, + /// >= 1 (implies the ClsId field is present) + pub version: U16<LE>, + pub machine: U16<LE>, + pub time_date_stamp: U32<LE>, + /// Used to invoke CoCreateInstance + pub class_id: ClsId, + /// Size of data that follows the header + pub size_of_data: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AnonObjectHeaderV2 { + /// Must be IMAGE_FILE_MACHINE_UNKNOWN + pub sig1: U16<LE>, + /// Must be 0xffff + pub sig2: U16<LE>, + /// >= 2 (implies the Flags field is present - otherwise V1) + pub version: U16<LE>, + pub machine: U16<LE>, + pub time_date_stamp: U32<LE>, + /// Used to invoke CoCreateInstance + pub class_id: ClsId, + /// Size of data that follows the header + pub size_of_data: U32<LE>, + /// 0x1 -> contains metadata + pub flags: U32<LE>, + /// Size of CLR metadata + pub meta_data_size: U32<LE>, + /// Offset of CLR metadata + pub meta_data_offset: U32<LE>, +} + +/// The required value of `AnonObjectHeaderBigobj::class_id`. +pub const ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID: ClsId = ClsId([ + 0xC7, 0xA1, 0xBA, 0xD1, 0xEE, 0xBA, 0xA9, 0x4B, 0xAF, 0x20, 0xFA, 0xF6, 0x6A, 0xA4, 0xDC, 0xB8, +]); + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AnonObjectHeaderBigobj { + /* same as ANON_OBJECT_HEADER_V2 */ + /// Must be IMAGE_FILE_MACHINE_UNKNOWN + pub sig1: U16<LE>, + /// Must be 0xffff + pub sig2: U16<LE>, + /// >= 2 (implies the Flags field is present) + pub version: U16<LE>, + /// Actual machine - IMAGE_FILE_MACHINE_xxx + pub machine: U16<LE>, + pub time_date_stamp: U32<LE>, + /// Must be `ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID`. + pub class_id: ClsId, + /// Size of data that follows the header + pub size_of_data: U32<LE>, + /// 0x1 -> contains metadata + pub flags: U32<LE>, + /// Size of CLR metadata + pub meta_data_size: U32<LE>, + /// Offset of CLR metadata + pub meta_data_offset: U32<LE>, + + /* bigobj specifics */ + /// extended from WORD + pub number_of_sections: U32<LE>, + pub pointer_to_symbol_table: U32<LE>, + pub number_of_symbols: U32<LE>, +} + +pub const IMAGE_SIZEOF_SHORT_NAME: usize = 8; + +// +// Section header format. +// + +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct ImageSectionHeader { + pub name: [u8; IMAGE_SIZEOF_SHORT_NAME], + pub virtual_size: U32<LE>, + pub virtual_address: U32<LE>, + pub size_of_raw_data: U32<LE>, + pub pointer_to_raw_data: U32<LE>, + pub pointer_to_relocations: U32<LE>, + pub pointer_to_linenumbers: U32<LE>, + pub number_of_relocations: U16<LE>, + pub number_of_linenumbers: U16<LE>, + pub characteristics: U32<LE>, +} + +pub const IMAGE_SIZEOF_SECTION_HEADER: usize = 40; + +// Values for `ImageSectionHeader::characteristics`. + +// IMAGE_SCN_TYPE_REG 0x00000000 // Reserved. +// IMAGE_SCN_TYPE_DSECT 0x00000001 // Reserved. +// IMAGE_SCN_TYPE_NOLOAD 0x00000002 // Reserved. +// IMAGE_SCN_TYPE_GROUP 0x00000004 // Reserved. +/// Reserved. +pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x0000_0008; +// IMAGE_SCN_TYPE_COPY 0x00000010 // Reserved. + +/// Section contains code. +pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020; +/// Section contains initialized data. +pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040; +/// Section contains uninitialized data. +pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080; + +/// Reserved. +pub const IMAGE_SCN_LNK_OTHER: u32 = 0x0000_0100; +/// Section contains comments or some other type of information. +pub const IMAGE_SCN_LNK_INFO: u32 = 0x0000_0200; +// IMAGE_SCN_TYPE_OVER 0x00000400 // Reserved. +/// Section contents will not become part of image. +pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800; +/// Section contents comdat. +pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000; +// 0x00002000 // Reserved. +// IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000 +/// Reset speculative exceptions handling bits in the TLB entries for this section. +pub const IMAGE_SCN_NO_DEFER_SPEC_EXC: u32 = 0x0000_4000; +/// Section content can be accessed relative to GP +pub const IMAGE_SCN_GPREL: u32 = 0x0000_8000; +pub const IMAGE_SCN_MEM_FARDATA: u32 = 0x0000_8000; +// IMAGE_SCN_MEM_SYSHEAP - Obsolete 0x00010000 +pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x0002_0000; +pub const IMAGE_SCN_MEM_16BIT: u32 = 0x0002_0000; +pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x0004_0000; +pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x0008_0000; + +pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000; +pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x0020_0000; +pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000; +pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000; +/// Default alignment if no others are specified. +pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000; +pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x0060_0000; +pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x0070_0000; +pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x0080_0000; +pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x0090_0000; +pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A0_0000; +pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B0_0000; +pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C0_0000; +pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D0_0000; +pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E0_0000; +// Unused 0x00F0_0000 +pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F0_0000; + +/// Section contains extended relocations. +pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x0100_0000; +/// Section can be discarded. +pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000; +/// Section is not cacheable. +pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x0400_0000; +/// Section is not pageable. +pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x0800_0000; +/// Section is shareable. +pub const IMAGE_SCN_MEM_SHARED: u32 = 0x1000_0000; +/// Section is executable. +pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000; +/// Section is readable. +pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000; +/// Section is writeable. +pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000; + +// +// TLS Characteristic Flags +// +/// Tls index is scaled +pub const IMAGE_SCN_SCALE_INDEX: u32 = 0x0000_0001; + +// +// Symbol format. +// + +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageSymbol { + /// If first 4 bytes are 0, then second 4 bytes are offset into string table. + pub name: [u8; 8], + pub value: U32Bytes<LE>, + pub section_number: U16Bytes<LE>, + pub typ: U16Bytes<LE>, + pub storage_class: u8, + pub number_of_aux_symbols: u8, +} + +pub const IMAGE_SIZEOF_SYMBOL: usize = 18; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageSymbolBytes(pub [u8; IMAGE_SIZEOF_SYMBOL]); + +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageSymbolEx { + /// If first 4 bytes are 0, then second 4 bytes are offset into string table. + pub name: [u8; 8], + pub value: U32Bytes<LE>, + pub section_number: I32Bytes<LE>, + pub typ: U16Bytes<LE>, + pub storage_class: u8, + pub number_of_aux_symbols: u8, +} + +pub const IMAGE_SIZEOF_SYMBOL_EX: usize = 20; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageSymbolExBytes(pub [u8; IMAGE_SIZEOF_SYMBOL_EX]); + +// Values for `ImageSymbol::section_number`. +// +// Symbols have a section number of the section in which they are +// defined. Otherwise, section numbers have the following meanings: + +/// Symbol is undefined or is common. +pub const IMAGE_SYM_UNDEFINED: i32 = 0; +/// Symbol is an absolute value. +pub const IMAGE_SYM_ABSOLUTE: i32 = -1; +/// Symbol is a special debug item. +pub const IMAGE_SYM_DEBUG: i32 = -2; +/// Values 0xFF00-0xFFFF are special +pub const IMAGE_SYM_SECTION_MAX: u16 = 0xFEFF; +pub const IMAGE_SYM_SECTION_MAX_EX: u32 = 0x7fff_ffff; + +// Values for `ImageSymbol::typ` (basic component). + +/// no type. +pub const IMAGE_SYM_TYPE_NULL: u16 = 0x0000; +pub const IMAGE_SYM_TYPE_VOID: u16 = 0x0001; +/// type character. +pub const IMAGE_SYM_TYPE_CHAR: u16 = 0x0002; +/// type short integer. +pub const IMAGE_SYM_TYPE_SHORT: u16 = 0x0003; +pub const IMAGE_SYM_TYPE_INT: u16 = 0x0004; +pub const IMAGE_SYM_TYPE_LONG: u16 = 0x0005; +pub const IMAGE_SYM_TYPE_FLOAT: u16 = 0x0006; +pub const IMAGE_SYM_TYPE_DOUBLE: u16 = 0x0007; +pub const IMAGE_SYM_TYPE_STRUCT: u16 = 0x0008; +pub const IMAGE_SYM_TYPE_UNION: u16 = 0x0009; +/// enumeration. +pub const IMAGE_SYM_TYPE_ENUM: u16 = 0x000A; +/// member of enumeration. +pub const IMAGE_SYM_TYPE_MOE: u16 = 0x000B; +pub const IMAGE_SYM_TYPE_BYTE: u16 = 0x000C; +pub const IMAGE_SYM_TYPE_WORD: u16 = 0x000D; +pub const IMAGE_SYM_TYPE_UINT: u16 = 0x000E; +pub const IMAGE_SYM_TYPE_DWORD: u16 = 0x000F; +pub const IMAGE_SYM_TYPE_PCODE: u16 = 0x8000; + +// Values for `ImageSymbol::typ` (derived component). + +/// no derived type. +pub const IMAGE_SYM_DTYPE_NULL: u16 = 0; +/// pointer. +pub const IMAGE_SYM_DTYPE_POINTER: u16 = 1; +/// function. +pub const IMAGE_SYM_DTYPE_FUNCTION: u16 = 2; +/// array. +pub const IMAGE_SYM_DTYPE_ARRAY: u16 = 3; + +// Values for `ImageSymbol::storage_class`. +pub const IMAGE_SYM_CLASS_END_OF_FUNCTION: u8 = 0xff; +pub const IMAGE_SYM_CLASS_NULL: u8 = 0x00; +pub const IMAGE_SYM_CLASS_AUTOMATIC: u8 = 0x01; +pub const IMAGE_SYM_CLASS_EXTERNAL: u8 = 0x02; +pub const IMAGE_SYM_CLASS_STATIC: u8 = 0x03; +pub const IMAGE_SYM_CLASS_REGISTER: u8 = 0x04; +pub const IMAGE_SYM_CLASS_EXTERNAL_DEF: u8 = 0x05; +pub const IMAGE_SYM_CLASS_LABEL: u8 = 0x06; +pub const IMAGE_SYM_CLASS_UNDEFINED_LABEL: u8 = 0x07; +pub const IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: u8 = 0x08; +pub const IMAGE_SYM_CLASS_ARGUMENT: u8 = 0x09; +pub const IMAGE_SYM_CLASS_STRUCT_TAG: u8 = 0x0A; +pub const IMAGE_SYM_CLASS_MEMBER_OF_UNION: u8 = 0x0B; +pub const IMAGE_SYM_CLASS_UNION_TAG: u8 = 0x0C; +pub const IMAGE_SYM_CLASS_TYPE_DEFINITION: u8 = 0x0D; +pub const IMAGE_SYM_CLASS_UNDEFINED_STATIC: u8 = 0x0E; +pub const IMAGE_SYM_CLASS_ENUM_TAG: u8 = 0x0F; +pub const IMAGE_SYM_CLASS_MEMBER_OF_ENUM: u8 = 0x10; +pub const IMAGE_SYM_CLASS_REGISTER_PARAM: u8 = 0x11; +pub const IMAGE_SYM_CLASS_BIT_FIELD: u8 = 0x12; + +pub const IMAGE_SYM_CLASS_FAR_EXTERNAL: u8 = 0x44; + +pub const IMAGE_SYM_CLASS_BLOCK: u8 = 0x64; +pub const IMAGE_SYM_CLASS_FUNCTION: u8 = 0x65; +pub const IMAGE_SYM_CLASS_END_OF_STRUCT: u8 = 0x66; +pub const IMAGE_SYM_CLASS_FILE: u8 = 0x67; +// new +pub const IMAGE_SYM_CLASS_SECTION: u8 = 0x68; +pub const IMAGE_SYM_CLASS_WEAK_EXTERNAL: u8 = 0x69; + +pub const IMAGE_SYM_CLASS_CLR_TOKEN: u8 = 0x6B; + +// type packing constants + +pub const N_BTMASK: u16 = 0x000F; +pub const N_TMASK: u16 = 0x0030; +pub const N_TMASK1: u16 = 0x00C0; +pub const N_TMASK2: u16 = 0x00F0; +pub const N_BTSHFT: usize = 4; +pub const N_TSHIFT: usize = 2; + +pub const IMAGE_SYM_DTYPE_SHIFT: usize = N_BTSHFT; + +// +// Auxiliary entry format. +// + +// Used for both ImageSymbol and ImageSymbolEx (with padding). +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAuxSymbolTokenDef { + /// IMAGE_AUX_SYMBOL_TYPE + pub aux_type: u8, + /// Must be 0 + pub reserved1: u8, + pub symbol_table_index: U32Bytes<LE>, + /// Must be 0 + pub reserved2: [u8; 12], +} + +pub const IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF: u16 = 1; + +/// Auxiliary symbol format 1: function definitions. +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAuxSymbolFunction { + pub tag_index: U32Bytes<LE>, + pub total_size: U32Bytes<LE>, + pub pointer_to_linenumber: U32Bytes<LE>, + pub pointer_to_next_function: U32Bytes<LE>, + pub unused: [u8; 2], +} + +/// Auxiliary symbol format 2: .bf and .ef symbols. +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAuxSymbolFunctionBeginEnd { + pub unused1: [u8; 4], + /// declaration line number + pub linenumber: U16Bytes<LE>, + pub unused2: [u8; 6], + pub pointer_to_next_function: U32Bytes<LE>, + pub unused3: [u8; 2], +} + +/// Auxiliary symbol format 3: weak externals. +/// +/// Used for both `ImageSymbol` and `ImageSymbolEx` (both with padding). +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAuxSymbolWeak { + /// the weak extern default symbol index + pub weak_default_sym_index: U32Bytes<LE>, + pub weak_search_type: U32Bytes<LE>, +} + +/// Auxiliary symbol format 5: sections. +/// +/// Used for both `ImageSymbol` and `ImageSymbolEx` (with padding). +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAuxSymbolSection { + /// section length + pub length: U32Bytes<LE>, + /// number of relocation entries + pub number_of_relocations: U16Bytes<LE>, + /// number of line numbers + pub number_of_linenumbers: U16Bytes<LE>, + /// checksum for communal + pub check_sum: U32Bytes<LE>, + /// section number to associate with + pub number: U16Bytes<LE>, + /// communal selection type + pub selection: u8, + pub reserved: u8, + /// high bits of the section number + pub high_number: U16Bytes<LE>, +} + +// Used for both ImageSymbol and ImageSymbolEx (both with padding). +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAuxSymbolCrc { + pub crc: U32Bytes<LE>, +} + +// +// Communal selection types. +// + +pub const IMAGE_COMDAT_SELECT_NODUPLICATES: u8 = 1; +pub const IMAGE_COMDAT_SELECT_ANY: u8 = 2; +pub const IMAGE_COMDAT_SELECT_SAME_SIZE: u8 = 3; +pub const IMAGE_COMDAT_SELECT_EXACT_MATCH: u8 = 4; +pub const IMAGE_COMDAT_SELECT_ASSOCIATIVE: u8 = 5; +pub const IMAGE_COMDAT_SELECT_LARGEST: u8 = 6; +pub const IMAGE_COMDAT_SELECT_NEWEST: u8 = 7; + +pub const IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY: u32 = 1; +pub const IMAGE_WEAK_EXTERN_SEARCH_LIBRARY: u32 = 2; +pub const IMAGE_WEAK_EXTERN_SEARCH_ALIAS: u32 = 3; +pub const IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY: u32 = 4; + +// +// Relocation format. +// + +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageRelocation { + /// Also `RelocCount` when IMAGE_SCN_LNK_NRELOC_OVFL is set + pub virtual_address: U32Bytes<LE>, + pub symbol_table_index: U32Bytes<LE>, + pub typ: U16Bytes<LE>, +} + +// +// I386 relocation types. +// +/// Reference is absolute, no relocation is necessary +pub const IMAGE_REL_I386_ABSOLUTE: u16 = 0x0000; +/// Direct 16-bit reference to the symbols virtual address +pub const IMAGE_REL_I386_DIR16: u16 = 0x0001; +/// PC-relative 16-bit reference to the symbols virtual address +pub const IMAGE_REL_I386_REL16: u16 = 0x0002; +/// Direct 32-bit reference to the symbols virtual address +pub const IMAGE_REL_I386_DIR32: u16 = 0x0006; +/// Direct 32-bit reference to the symbols virtual address, base not included +pub const IMAGE_REL_I386_DIR32NB: u16 = 0x0007; +/// Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address +pub const IMAGE_REL_I386_SEG12: u16 = 0x0009; +pub const IMAGE_REL_I386_SECTION: u16 = 0x000A; +pub const IMAGE_REL_I386_SECREL: u16 = 0x000B; +/// clr token +pub const IMAGE_REL_I386_TOKEN: u16 = 0x000C; +/// 7 bit offset from base of section containing target +pub const IMAGE_REL_I386_SECREL7: u16 = 0x000D; +/// PC-relative 32-bit reference to the symbols virtual address +pub const IMAGE_REL_I386_REL32: u16 = 0x0014; + +// +// MIPS relocation types. +// +/// Reference is absolute, no relocation is necessary +pub const IMAGE_REL_MIPS_ABSOLUTE: u16 = 0x0000; +pub const IMAGE_REL_MIPS_REFHALF: u16 = 0x0001; +pub const IMAGE_REL_MIPS_REFWORD: u16 = 0x0002; +pub const IMAGE_REL_MIPS_JMPADDR: u16 = 0x0003; +pub const IMAGE_REL_MIPS_REFHI: u16 = 0x0004; +pub const IMAGE_REL_MIPS_REFLO: u16 = 0x0005; +pub const IMAGE_REL_MIPS_GPREL: u16 = 0x0006; +pub const IMAGE_REL_MIPS_LITERAL: u16 = 0x0007; +pub const IMAGE_REL_MIPS_SECTION: u16 = 0x000A; +pub const IMAGE_REL_MIPS_SECREL: u16 = 0x000B; +/// Low 16-bit section relative reference (used for >32k TLS) +pub const IMAGE_REL_MIPS_SECRELLO: u16 = 0x000C; +/// High 16-bit section relative reference (used for >32k TLS) +pub const IMAGE_REL_MIPS_SECRELHI: u16 = 0x000D; +/// clr token +pub const IMAGE_REL_MIPS_TOKEN: u16 = 0x000E; +pub const IMAGE_REL_MIPS_JMPADDR16: u16 = 0x0010; +pub const IMAGE_REL_MIPS_REFWORDNB: u16 = 0x0022; +pub const IMAGE_REL_MIPS_PAIR: u16 = 0x0025; + +// +// Alpha Relocation types. +// +pub const IMAGE_REL_ALPHA_ABSOLUTE: u16 = 0x0000; +pub const IMAGE_REL_ALPHA_REFLONG: u16 = 0x0001; +pub const IMAGE_REL_ALPHA_REFQUAD: u16 = 0x0002; +pub const IMAGE_REL_ALPHA_GPREL32: u16 = 0x0003; +pub const IMAGE_REL_ALPHA_LITERAL: u16 = 0x0004; +pub const IMAGE_REL_ALPHA_LITUSE: u16 = 0x0005; +pub const IMAGE_REL_ALPHA_GPDISP: u16 = 0x0006; +pub const IMAGE_REL_ALPHA_BRADDR: u16 = 0x0007; +pub const IMAGE_REL_ALPHA_HINT: u16 = 0x0008; +pub const IMAGE_REL_ALPHA_INLINE_REFLONG: u16 = 0x0009; +pub const IMAGE_REL_ALPHA_REFHI: u16 = 0x000A; +pub const IMAGE_REL_ALPHA_REFLO: u16 = 0x000B; +pub const IMAGE_REL_ALPHA_PAIR: u16 = 0x000C; +pub const IMAGE_REL_ALPHA_MATCH: u16 = 0x000D; +pub const IMAGE_REL_ALPHA_SECTION: u16 = 0x000E; +pub const IMAGE_REL_ALPHA_SECREL: u16 = 0x000F; +pub const IMAGE_REL_ALPHA_REFLONGNB: u16 = 0x0010; +/// Low 16-bit section relative reference +pub const IMAGE_REL_ALPHA_SECRELLO: u16 = 0x0011; +/// High 16-bit section relative reference +pub const IMAGE_REL_ALPHA_SECRELHI: u16 = 0x0012; +/// High 16 bits of 48 bit reference +pub const IMAGE_REL_ALPHA_REFQ3: u16 = 0x0013; +/// Middle 16 bits of 48 bit reference +pub const IMAGE_REL_ALPHA_REFQ2: u16 = 0x0014; +/// Low 16 bits of 48 bit reference +pub const IMAGE_REL_ALPHA_REFQ1: u16 = 0x0015; +/// Low 16-bit GP relative reference +pub const IMAGE_REL_ALPHA_GPRELLO: u16 = 0x0016; +/// High 16-bit GP relative reference +pub const IMAGE_REL_ALPHA_GPRELHI: u16 = 0x0017; + +// +// IBM PowerPC relocation types. +// +/// NOP +pub const IMAGE_REL_PPC_ABSOLUTE: u16 = 0x0000; +/// 64-bit address +pub const IMAGE_REL_PPC_ADDR64: u16 = 0x0001; +/// 32-bit address +pub const IMAGE_REL_PPC_ADDR32: u16 = 0x0002; +/// 26-bit address, shifted left 2 (branch absolute) +pub const IMAGE_REL_PPC_ADDR24: u16 = 0x0003; +/// 16-bit address +pub const IMAGE_REL_PPC_ADDR16: u16 = 0x0004; +/// 16-bit address, shifted left 2 (load doubleword) +pub const IMAGE_REL_PPC_ADDR14: u16 = 0x0005; +/// 26-bit PC-relative offset, shifted left 2 (branch relative) +pub const IMAGE_REL_PPC_REL24: u16 = 0x0006; +/// 16-bit PC-relative offset, shifted left 2 (br cond relative) +pub const IMAGE_REL_PPC_REL14: u16 = 0x0007; +/// 16-bit offset from TOC base +pub const IMAGE_REL_PPC_TOCREL16: u16 = 0x0008; +/// 16-bit offset from TOC base, shifted left 2 (load doubleword) +pub const IMAGE_REL_PPC_TOCREL14: u16 = 0x0009; + +/// 32-bit addr w/o image base +pub const IMAGE_REL_PPC_ADDR32NB: u16 = 0x000A; +/// va of containing section (as in an image sectionhdr) +pub const IMAGE_REL_PPC_SECREL: u16 = 0x000B; +/// sectionheader number +pub const IMAGE_REL_PPC_SECTION: u16 = 0x000C; +/// substitute TOC restore instruction iff symbol is glue code +pub const IMAGE_REL_PPC_IFGLUE: u16 = 0x000D; +/// symbol is glue code; virtual address is TOC restore instruction +pub const IMAGE_REL_PPC_IMGLUE: u16 = 0x000E; +/// va of containing section (limited to 16 bits) +pub const IMAGE_REL_PPC_SECREL16: u16 = 0x000F; +pub const IMAGE_REL_PPC_REFHI: u16 = 0x0010; +pub const IMAGE_REL_PPC_REFLO: u16 = 0x0011; +pub const IMAGE_REL_PPC_PAIR: u16 = 0x0012; +/// Low 16-bit section relative reference (used for >32k TLS) +pub const IMAGE_REL_PPC_SECRELLO: u16 = 0x0013; +/// High 16-bit section relative reference (used for >32k TLS) +pub const IMAGE_REL_PPC_SECRELHI: u16 = 0x0014; +pub const IMAGE_REL_PPC_GPREL: u16 = 0x0015; +/// clr token +pub const IMAGE_REL_PPC_TOKEN: u16 = 0x0016; + +/// mask to isolate above values in IMAGE_RELOCATION.Type +pub const IMAGE_REL_PPC_TYPEMASK: u16 = 0x00FF; + +// Flag bits in `ImageRelocation::typ`. + +/// subtract reloc value rather than adding it +pub const IMAGE_REL_PPC_NEG: u16 = 0x0100; +/// fix branch prediction bit to predict branch taken +pub const IMAGE_REL_PPC_BRTAKEN: u16 = 0x0200; +/// fix branch prediction bit to predict branch not taken +pub const IMAGE_REL_PPC_BRNTAKEN: u16 = 0x0400; +/// toc slot defined in file (or, data in toc) +pub const IMAGE_REL_PPC_TOCDEFN: u16 = 0x0800; + +// +// Hitachi SH3 relocation types. +// +/// No relocation +pub const IMAGE_REL_SH3_ABSOLUTE: u16 = 0x0000; +/// 16 bit direct +pub const IMAGE_REL_SH3_DIRECT16: u16 = 0x0001; +/// 32 bit direct +pub const IMAGE_REL_SH3_DIRECT32: u16 = 0x0002; +/// 8 bit direct, -128..255 +pub const IMAGE_REL_SH3_DIRECT8: u16 = 0x0003; +/// 8 bit direct .W (0 ext.) +pub const IMAGE_REL_SH3_DIRECT8_WORD: u16 = 0x0004; +/// 8 bit direct .L (0 ext.) +pub const IMAGE_REL_SH3_DIRECT8_LONG: u16 = 0x0005; +/// 4 bit direct (0 ext.) +pub const IMAGE_REL_SH3_DIRECT4: u16 = 0x0006; +/// 4 bit direct .W (0 ext.) +pub const IMAGE_REL_SH3_DIRECT4_WORD: u16 = 0x0007; +/// 4 bit direct .L (0 ext.) +pub const IMAGE_REL_SH3_DIRECT4_LONG: u16 = 0x0008; +/// 8 bit PC relative .W +pub const IMAGE_REL_SH3_PCREL8_WORD: u16 = 0x0009; +/// 8 bit PC relative .L +pub const IMAGE_REL_SH3_PCREL8_LONG: u16 = 0x000A; +/// 12 LSB PC relative .W +pub const IMAGE_REL_SH3_PCREL12_WORD: u16 = 0x000B; +/// Start of EXE section +pub const IMAGE_REL_SH3_STARTOF_SECTION: u16 = 0x000C; +/// Size of EXE section +pub const IMAGE_REL_SH3_SIZEOF_SECTION: u16 = 0x000D; +/// Section table index +pub const IMAGE_REL_SH3_SECTION: u16 = 0x000E; +/// Offset within section +pub const IMAGE_REL_SH3_SECREL: u16 = 0x000F; +/// 32 bit direct not based +pub const IMAGE_REL_SH3_DIRECT32_NB: u16 = 0x0010; +/// GP-relative addressing +pub const IMAGE_REL_SH3_GPREL4_LONG: u16 = 0x0011; +/// clr token +pub const IMAGE_REL_SH3_TOKEN: u16 = 0x0012; +/// Offset from current instruction in longwords +/// if not NOMODE, insert the inverse of the low bit at bit 32 to select PTA/PTB +pub const IMAGE_REL_SHM_PCRELPT: u16 = 0x0013; +/// Low bits of 32-bit address +pub const IMAGE_REL_SHM_REFLO: u16 = 0x0014; +/// High bits of 32-bit address +pub const IMAGE_REL_SHM_REFHALF: u16 = 0x0015; +/// Low bits of relative reference +pub const IMAGE_REL_SHM_RELLO: u16 = 0x0016; +/// High bits of relative reference +pub const IMAGE_REL_SHM_RELHALF: u16 = 0x0017; +/// offset operand for relocation +pub const IMAGE_REL_SHM_PAIR: u16 = 0x0018; + +/// relocation ignores section mode +pub const IMAGE_REL_SH_NOMODE: u16 = 0x8000; + +/// No relocation required +pub const IMAGE_REL_ARM_ABSOLUTE: u16 = 0x0000; +/// 32 bit address +pub const IMAGE_REL_ARM_ADDR32: u16 = 0x0001; +/// 32 bit address w/o image base +pub const IMAGE_REL_ARM_ADDR32NB: u16 = 0x0002; +/// 24 bit offset << 2 & sign ext. +pub const IMAGE_REL_ARM_BRANCH24: u16 = 0x0003; +/// Thumb: 2 11 bit offsets +pub const IMAGE_REL_ARM_BRANCH11: u16 = 0x0004; +/// clr token +pub const IMAGE_REL_ARM_TOKEN: u16 = 0x0005; +/// GP-relative addressing (ARM) +pub const IMAGE_REL_ARM_GPREL12: u16 = 0x0006; +/// GP-relative addressing (Thumb) +pub const IMAGE_REL_ARM_GPREL7: u16 = 0x0007; +pub const IMAGE_REL_ARM_BLX24: u16 = 0x0008; +pub const IMAGE_REL_ARM_BLX11: u16 = 0x0009; +/// 32-bit relative address from byte following reloc +pub const IMAGE_REL_ARM_REL32: u16 = 0x000A; +/// Section table index +pub const IMAGE_REL_ARM_SECTION: u16 = 0x000E; +/// Offset within section +pub const IMAGE_REL_ARM_SECREL: u16 = 0x000F; +/// ARM: MOVW/MOVT +pub const IMAGE_REL_ARM_MOV32A: u16 = 0x0010; +/// ARM: MOVW/MOVT (deprecated) +pub const IMAGE_REL_ARM_MOV32: u16 = 0x0010; +/// Thumb: MOVW/MOVT +pub const IMAGE_REL_ARM_MOV32T: u16 = 0x0011; +/// Thumb: MOVW/MOVT (deprecated) +pub const IMAGE_REL_THUMB_MOV32: u16 = 0x0011; +/// Thumb: 32-bit conditional B +pub const IMAGE_REL_ARM_BRANCH20T: u16 = 0x0012; +/// Thumb: 32-bit conditional B (deprecated) +pub const IMAGE_REL_THUMB_BRANCH20: u16 = 0x0012; +/// Thumb: 32-bit B or BL +pub const IMAGE_REL_ARM_BRANCH24T: u16 = 0x0014; +/// Thumb: 32-bit B or BL (deprecated) +pub const IMAGE_REL_THUMB_BRANCH24: u16 = 0x0014; +/// Thumb: BLX immediate +pub const IMAGE_REL_ARM_BLX23T: u16 = 0x0015; +/// Thumb: BLX immediate (deprecated) +pub const IMAGE_REL_THUMB_BLX23: u16 = 0x0015; + +pub const IMAGE_REL_AM_ABSOLUTE: u16 = 0x0000; +pub const IMAGE_REL_AM_ADDR32: u16 = 0x0001; +pub const IMAGE_REL_AM_ADDR32NB: u16 = 0x0002; +pub const IMAGE_REL_AM_CALL32: u16 = 0x0003; +pub const IMAGE_REL_AM_FUNCINFO: u16 = 0x0004; +pub const IMAGE_REL_AM_REL32_1: u16 = 0x0005; +pub const IMAGE_REL_AM_REL32_2: u16 = 0x0006; +pub const IMAGE_REL_AM_SECREL: u16 = 0x0007; +pub const IMAGE_REL_AM_SECTION: u16 = 0x0008; +pub const IMAGE_REL_AM_TOKEN: u16 = 0x0009; + +// +// ARM64 relocations types. +// + +/// No relocation required +pub const IMAGE_REL_ARM64_ABSOLUTE: u16 = 0x0000; +/// 32 bit address. Review! do we need it? +pub const IMAGE_REL_ARM64_ADDR32: u16 = 0x0001; +/// 32 bit address w/o image base (RVA: for Data/PData/XData) +pub const IMAGE_REL_ARM64_ADDR32NB: u16 = 0x0002; +/// 26 bit offset << 2 & sign ext. for B & BL +pub const IMAGE_REL_ARM64_BRANCH26: u16 = 0x0003; +/// ADRP +pub const IMAGE_REL_ARM64_PAGEBASE_REL21: u16 = 0x0004; +/// ADR +pub const IMAGE_REL_ARM64_REL21: u16 = 0x0005; +/// ADD/ADDS (immediate) with zero shift, for page offset +pub const IMAGE_REL_ARM64_PAGEOFFSET_12A: u16 = 0x0006; +/// LDR (indexed, unsigned immediate), for page offset +pub const IMAGE_REL_ARM64_PAGEOFFSET_12L: u16 = 0x0007; +/// Offset within section +pub const IMAGE_REL_ARM64_SECREL: u16 = 0x0008; +/// ADD/ADDS (immediate) with zero shift, for bit 0:11 of section offset +pub const IMAGE_REL_ARM64_SECREL_LOW12A: u16 = 0x0009; +/// ADD/ADDS (immediate) with zero shift, for bit 12:23 of section offset +pub const IMAGE_REL_ARM64_SECREL_HIGH12A: u16 = 0x000A; +/// LDR (indexed, unsigned immediate), for bit 0:11 of section offset +pub const IMAGE_REL_ARM64_SECREL_LOW12L: u16 = 0x000B; +pub const IMAGE_REL_ARM64_TOKEN: u16 = 0x000C; +/// Section table index +pub const IMAGE_REL_ARM64_SECTION: u16 = 0x000D; +/// 64 bit address +pub const IMAGE_REL_ARM64_ADDR64: u16 = 0x000E; +/// 19 bit offset << 2 & sign ext. for conditional B +pub const IMAGE_REL_ARM64_BRANCH19: u16 = 0x000F; +/// TBZ/TBNZ +pub const IMAGE_REL_ARM64_BRANCH14: u16 = 0x0010; +/// 32-bit relative address from byte following reloc +pub const IMAGE_REL_ARM64_REL32: u16 = 0x0011; + +// +// x64 relocations +// +/// Reference is absolute, no relocation is necessary +pub const IMAGE_REL_AMD64_ABSOLUTE: u16 = 0x0000; +/// 64-bit address (VA). +pub const IMAGE_REL_AMD64_ADDR64: u16 = 0x0001; +/// 32-bit address (VA). +pub const IMAGE_REL_AMD64_ADDR32: u16 = 0x0002; +/// 32-bit address w/o image base (RVA). +pub const IMAGE_REL_AMD64_ADDR32NB: u16 = 0x0003; +/// 32-bit relative address from byte following reloc +pub const IMAGE_REL_AMD64_REL32: u16 = 0x0004; +/// 32-bit relative address from byte distance 1 from reloc +pub const IMAGE_REL_AMD64_REL32_1: u16 = 0x0005; +/// 32-bit relative address from byte distance 2 from reloc +pub const IMAGE_REL_AMD64_REL32_2: u16 = 0x0006; +/// 32-bit relative address from byte distance 3 from reloc +pub const IMAGE_REL_AMD64_REL32_3: u16 = 0x0007; +/// 32-bit relative address from byte distance 4 from reloc +pub const IMAGE_REL_AMD64_REL32_4: u16 = 0x0008; +/// 32-bit relative address from byte distance 5 from reloc +pub const IMAGE_REL_AMD64_REL32_5: u16 = 0x0009; +/// Section index +pub const IMAGE_REL_AMD64_SECTION: u16 = 0x000A; +/// 32 bit offset from base of section containing target +pub const IMAGE_REL_AMD64_SECREL: u16 = 0x000B; +/// 7 bit unsigned offset from base of section containing target +pub const IMAGE_REL_AMD64_SECREL7: u16 = 0x000C; +/// 32 bit metadata token +pub const IMAGE_REL_AMD64_TOKEN: u16 = 0x000D; +/// 32 bit signed span-dependent value emitted into object +pub const IMAGE_REL_AMD64_SREL32: u16 = 0x000E; +pub const IMAGE_REL_AMD64_PAIR: u16 = 0x000F; +/// 32 bit signed span-dependent value applied at link time +pub const IMAGE_REL_AMD64_SSPAN32: u16 = 0x0010; +pub const IMAGE_REL_AMD64_EHANDLER: u16 = 0x0011; +/// Indirect branch to an import +pub const IMAGE_REL_AMD64_IMPORT_BR: u16 = 0x0012; +/// Indirect call to an import +pub const IMAGE_REL_AMD64_IMPORT_CALL: u16 = 0x0013; +/// Indirect branch to a CFG check +pub const IMAGE_REL_AMD64_CFG_BR: u16 = 0x0014; +/// Indirect branch to a CFG check, with REX.W prefix +pub const IMAGE_REL_AMD64_CFG_BR_REX: u16 = 0x0015; +/// Indirect call to a CFG check +pub const IMAGE_REL_AMD64_CFG_CALL: u16 = 0x0016; +/// Indirect branch to a target in RAX (no CFG) +pub const IMAGE_REL_AMD64_INDIR_BR: u16 = 0x0017; +/// Indirect branch to a target in RAX, with REX.W prefix (no CFG) +pub const IMAGE_REL_AMD64_INDIR_BR_REX: u16 = 0x0018; +/// Indirect call to a target in RAX (no CFG) +pub const IMAGE_REL_AMD64_INDIR_CALL: u16 = 0x0019; +/// Indirect branch for a switch table using Reg 0 (RAX) +pub const IMAGE_REL_AMD64_INDIR_BR_SWITCHTABLE_FIRST: u16 = 0x0020; +/// Indirect branch for a switch table using Reg 15 (R15) +pub const IMAGE_REL_AMD64_INDIR_BR_SWITCHTABLE_LAST: u16 = 0x002F; + +// +// IA64 relocation types. +// +pub const IMAGE_REL_IA64_ABSOLUTE: u16 = 0x0000; +pub const IMAGE_REL_IA64_IMM14: u16 = 0x0001; +pub const IMAGE_REL_IA64_IMM22: u16 = 0x0002; +pub const IMAGE_REL_IA64_IMM64: u16 = 0x0003; +pub const IMAGE_REL_IA64_DIR32: u16 = 0x0004; +pub const IMAGE_REL_IA64_DIR64: u16 = 0x0005; +pub const IMAGE_REL_IA64_PCREL21B: u16 = 0x0006; +pub const IMAGE_REL_IA64_PCREL21M: u16 = 0x0007; +pub const IMAGE_REL_IA64_PCREL21F: u16 = 0x0008; +pub const IMAGE_REL_IA64_GPREL22: u16 = 0x0009; +pub const IMAGE_REL_IA64_LTOFF22: u16 = 0x000A; +pub const IMAGE_REL_IA64_SECTION: u16 = 0x000B; +pub const IMAGE_REL_IA64_SECREL22: u16 = 0x000C; +pub const IMAGE_REL_IA64_SECREL64I: u16 = 0x000D; +pub const IMAGE_REL_IA64_SECREL32: u16 = 0x000E; +// +pub const IMAGE_REL_IA64_DIR32NB: u16 = 0x0010; +pub const IMAGE_REL_IA64_SREL14: u16 = 0x0011; +pub const IMAGE_REL_IA64_SREL22: u16 = 0x0012; +pub const IMAGE_REL_IA64_SREL32: u16 = 0x0013; +pub const IMAGE_REL_IA64_UREL32: u16 = 0x0014; +/// This is always a BRL and never converted +pub const IMAGE_REL_IA64_PCREL60X: u16 = 0x0015; +/// If possible, convert to MBB bundle with NOP.B in slot 1 +pub const IMAGE_REL_IA64_PCREL60B: u16 = 0x0016; +/// If possible, convert to MFB bundle with NOP.F in slot 1 +pub const IMAGE_REL_IA64_PCREL60F: u16 = 0x0017; +/// If possible, convert to MIB bundle with NOP.I in slot 1 +pub const IMAGE_REL_IA64_PCREL60I: u16 = 0x0018; +/// If possible, convert to MMB bundle with NOP.M in slot 1 +pub const IMAGE_REL_IA64_PCREL60M: u16 = 0x0019; +pub const IMAGE_REL_IA64_IMMGPREL64: u16 = 0x001A; +/// clr token +pub const IMAGE_REL_IA64_TOKEN: u16 = 0x001B; +pub const IMAGE_REL_IA64_GPREL32: u16 = 0x001C; +pub const IMAGE_REL_IA64_ADDEND: u16 = 0x001F; + +// +// CEF relocation types. +// +/// Reference is absolute, no relocation is necessary +pub const IMAGE_REL_CEF_ABSOLUTE: u16 = 0x0000; +/// 32-bit address (VA). +pub const IMAGE_REL_CEF_ADDR32: u16 = 0x0001; +/// 64-bit address (VA). +pub const IMAGE_REL_CEF_ADDR64: u16 = 0x0002; +/// 32-bit address w/o image base (RVA). +pub const IMAGE_REL_CEF_ADDR32NB: u16 = 0x0003; +/// Section index +pub const IMAGE_REL_CEF_SECTION: u16 = 0x0004; +/// 32 bit offset from base of section containing target +pub const IMAGE_REL_CEF_SECREL: u16 = 0x0005; +/// 32 bit metadata token +pub const IMAGE_REL_CEF_TOKEN: u16 = 0x0006; + +// +// clr relocation types. +// +/// Reference is absolute, no relocation is necessary +pub const IMAGE_REL_CEE_ABSOLUTE: u16 = 0x0000; +/// 32-bit address (VA). +pub const IMAGE_REL_CEE_ADDR32: u16 = 0x0001; +/// 64-bit address (VA). +pub const IMAGE_REL_CEE_ADDR64: u16 = 0x0002; +/// 32-bit address w/o image base (RVA). +pub const IMAGE_REL_CEE_ADDR32NB: u16 = 0x0003; +/// Section index +pub const IMAGE_REL_CEE_SECTION: u16 = 0x0004; +/// 32 bit offset from base of section containing target +pub const IMAGE_REL_CEE_SECREL: u16 = 0x0005; +/// 32 bit metadata token +pub const IMAGE_REL_CEE_TOKEN: u16 = 0x0006; + +/// No relocation required +pub const IMAGE_REL_M32R_ABSOLUTE: u16 = 0x0000; +/// 32 bit address +pub const IMAGE_REL_M32R_ADDR32: u16 = 0x0001; +/// 32 bit address w/o image base +pub const IMAGE_REL_M32R_ADDR32NB: u16 = 0x0002; +/// 24 bit address +pub const IMAGE_REL_M32R_ADDR24: u16 = 0x0003; +/// GP relative addressing +pub const IMAGE_REL_M32R_GPREL16: u16 = 0x0004; +/// 24 bit offset << 2 & sign ext. +pub const IMAGE_REL_M32R_PCREL24: u16 = 0x0005; +/// 16 bit offset << 2 & sign ext. +pub const IMAGE_REL_M32R_PCREL16: u16 = 0x0006; +/// 8 bit offset << 2 & sign ext. +pub const IMAGE_REL_M32R_PCREL8: u16 = 0x0007; +/// 16 MSBs +pub const IMAGE_REL_M32R_REFHALF: u16 = 0x0008; +/// 16 MSBs; adj for LSB sign ext. +pub const IMAGE_REL_M32R_REFHI: u16 = 0x0009; +/// 16 LSBs +pub const IMAGE_REL_M32R_REFLO: u16 = 0x000A; +/// Link HI and LO +pub const IMAGE_REL_M32R_PAIR: u16 = 0x000B; +/// Section table index +pub const IMAGE_REL_M32R_SECTION: u16 = 0x000C; +/// 32 bit section relative reference +pub const IMAGE_REL_M32R_SECREL32: u16 = 0x000D; +/// clr token +pub const IMAGE_REL_M32R_TOKEN: u16 = 0x000E; + +/// No relocation required +pub const IMAGE_REL_EBC_ABSOLUTE: u16 = 0x0000; +/// 32 bit address w/o image base +pub const IMAGE_REL_EBC_ADDR32NB: u16 = 0x0001; +/// 32-bit relative address from byte following reloc +pub const IMAGE_REL_EBC_REL32: u16 = 0x0002; +/// Section table index +pub const IMAGE_REL_EBC_SECTION: u16 = 0x0003; +/// Offset within section +pub const IMAGE_REL_EBC_SECREL: u16 = 0x0004; + +/* +// TODO? +#define EXT_IMM64(Value, Address, Size, InstPos, ValPos) /* Intel-IA64-Filler */ \ + Value |= (((ULONGLONG)((*(Address) >> InstPos) & (((ULONGLONG)1 << Size) - 1))) << ValPos) // Intel-IA64-Filler + +#define INS_IMM64(Value, Address, Size, InstPos, ValPos) /* Intel-IA64-Filler */\ + *(PDWORD)Address = (*(PDWORD)Address & ~(((1 << Size) - 1) << InstPos)) | /* Intel-IA64-Filler */\ + ((DWORD)((((ULONGLONG)Value >> ValPos) & (((ULONGLONG)1 << Size) - 1))) << InstPos) // Intel-IA64-Filler +*/ + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM7B_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM7B_SIZE_X: u16 = 7; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM7B_INST_WORD_POS_X: u16 = 4; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM7B_VAL_POS_X: u16 = 0; + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM9D_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM9D_SIZE_X: u16 = 9; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM9D_INST_WORD_POS_X: u16 = 18; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM9D_VAL_POS_X: u16 = 7; + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM5C_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM5C_SIZE_X: u16 = 5; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM5C_INST_WORD_POS_X: u16 = 13; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM5C_VAL_POS_X: u16 = 16; + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IC_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IC_SIZE_X: u16 = 1; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IC_INST_WORD_POS_X: u16 = 12; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IC_VAL_POS_X: u16 = 21; + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41A_INST_WORD_X: u16 = 1; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41A_SIZE_X: u16 = 10; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41A_INST_WORD_POS_X: u16 = 14; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41A_VAL_POS_X: u16 = 22; + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41B_INST_WORD_X: u16 = 1; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41B_SIZE_X: u16 = 8; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41B_INST_WORD_POS_X: u16 = 24; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41B_VAL_POS_X: u16 = 32; + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41C_INST_WORD_X: u16 = 2; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41C_SIZE_X: u16 = 23; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41C_INST_WORD_POS_X: u16 = 0; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_IMM41C_VAL_POS_X: u16 = 40; + +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_SIGN_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_SIGN_SIZE_X: u16 = 1; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_SIGN_INST_WORD_POS_X: u16 = 27; +/// Intel-IA64-Filler +pub const EMARCH_ENC_I17_SIGN_VAL_POS_X: u16 = 63; + +/// Intel-IA64-Filler +pub const X3_OPCODE_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const X3_OPCODE_SIZE_X: u16 = 4; +/// Intel-IA64-Filler +pub const X3_OPCODE_INST_WORD_POS_X: u16 = 28; +/// Intel-IA64-Filler +pub const X3_OPCODE_SIGN_VAL_POS_X: u16 = 0; + +/// Intel-IA64-Filler +pub const X3_I_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const X3_I_SIZE_X: u16 = 1; +/// Intel-IA64-Filler +pub const X3_I_INST_WORD_POS_X: u16 = 27; +/// Intel-IA64-Filler +pub const X3_I_SIGN_VAL_POS_X: u16 = 59; + +/// Intel-IA64-Filler +pub const X3_D_WH_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const X3_D_WH_SIZE_X: u16 = 3; +/// Intel-IA64-Filler +pub const X3_D_WH_INST_WORD_POS_X: u16 = 24; +/// Intel-IA64-Filler +pub const X3_D_WH_SIGN_VAL_POS_X: u16 = 0; + +/// Intel-IA64-Filler +pub const X3_IMM20_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const X3_IMM20_SIZE_X: u16 = 20; +/// Intel-IA64-Filler +pub const X3_IMM20_INST_WORD_POS_X: u16 = 4; +/// Intel-IA64-Filler +pub const X3_IMM20_SIGN_VAL_POS_X: u16 = 0; + +/// Intel-IA64-Filler +pub const X3_IMM39_1_INST_WORD_X: u16 = 2; +/// Intel-IA64-Filler +pub const X3_IMM39_1_SIZE_X: u16 = 23; +/// Intel-IA64-Filler +pub const X3_IMM39_1_INST_WORD_POS_X: u16 = 0; +/// Intel-IA64-Filler +pub const X3_IMM39_1_SIGN_VAL_POS_X: u16 = 36; + +/// Intel-IA64-Filler +pub const X3_IMM39_2_INST_WORD_X: u16 = 1; +/// Intel-IA64-Filler +pub const X3_IMM39_2_SIZE_X: u16 = 16; +/// Intel-IA64-Filler +pub const X3_IMM39_2_INST_WORD_POS_X: u16 = 16; +/// Intel-IA64-Filler +pub const X3_IMM39_2_SIGN_VAL_POS_X: u16 = 20; + +/// Intel-IA64-Filler +pub const X3_P_INST_WORD_X: u16 = 3; +/// Intel-IA64-Filler +pub const X3_P_SIZE_X: u16 = 4; +/// Intel-IA64-Filler +pub const X3_P_INST_WORD_POS_X: u16 = 0; +/// Intel-IA64-Filler +pub const X3_P_SIGN_VAL_POS_X: u16 = 0; + +/// Intel-IA64-Filler +pub const X3_TMPLT_INST_WORD_X: u16 = 0; +/// Intel-IA64-Filler +pub const X3_TMPLT_SIZE_X: u16 = 4; +/// Intel-IA64-Filler +pub const X3_TMPLT_INST_WORD_POS_X: u16 = 0; +/// Intel-IA64-Filler +pub const X3_TMPLT_SIGN_VAL_POS_X: u16 = 0; + +/// Intel-IA64-Filler +pub const X3_BTYPE_QP_INST_WORD_X: u16 = 2; +/// Intel-IA64-Filler +pub const X3_BTYPE_QP_SIZE_X: u16 = 9; +/// Intel-IA64-Filler +pub const X3_BTYPE_QP_INST_WORD_POS_X: u16 = 23; +/// Intel-IA64-Filler +pub const X3_BTYPE_QP_INST_VAL_POS_X: u16 = 0; + +/// Intel-IA64-Filler +pub const X3_EMPTY_INST_WORD_X: u16 = 1; +/// Intel-IA64-Filler +pub const X3_EMPTY_SIZE_X: u16 = 2; +/// Intel-IA64-Filler +pub const X3_EMPTY_INST_WORD_POS_X: u16 = 14; +/// Intel-IA64-Filler +pub const X3_EMPTY_INST_VAL_POS_X: u16 = 0; + +// +// Line number format. +// + +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageLinenumber { + /// Symbol table index of function name if Linenumber is 0. + /// Otherwise virtual address of line number. + pub symbol_table_index_or_virtual_address: U32Bytes<LE>, + /// Line number. + pub linenumber: U16Bytes<LE>, +} + +// +// Based relocation format. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageBaseRelocation { + pub virtual_address: U32<LE>, + pub size_of_block: U32<LE>, + // pub type_offset[1]: U16<LE>, +} + +// +// Based relocation types. +// + +pub const IMAGE_REL_BASED_ABSOLUTE: u16 = 0; +pub const IMAGE_REL_BASED_HIGH: u16 = 1; +pub const IMAGE_REL_BASED_LOW: u16 = 2; +pub const IMAGE_REL_BASED_HIGHLOW: u16 = 3; +pub const IMAGE_REL_BASED_HIGHADJ: u16 = 4; +pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_5: u16 = 5; +pub const IMAGE_REL_BASED_RESERVED: u16 = 6; +pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_7: u16 = 7; +pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_8: u16 = 8; +pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_9: u16 = 9; +pub const IMAGE_REL_BASED_DIR64: u16 = 10; + +// +// Platform-specific based relocation types. +// + +pub const IMAGE_REL_BASED_IA64_IMM64: u16 = 9; + +pub const IMAGE_REL_BASED_MIPS_JMPADDR: u16 = 5; +pub const IMAGE_REL_BASED_MIPS_JMPADDR16: u16 = 9; + +pub const IMAGE_REL_BASED_ARM_MOV32: u16 = 5; +pub const IMAGE_REL_BASED_THUMB_MOV32: u16 = 7; + +pub const IMAGE_REL_BASED_RISCV_HIGH20: u16 = 5; +pub const IMAGE_REL_BASED_RISCV_LOW12I: u16 = 7; +pub const IMAGE_REL_BASED_RISCV_LOW12S: u16 = 8; + +// +// Archive format. +// + +pub const IMAGE_ARCHIVE_START_SIZE: usize = 8; +pub const IMAGE_ARCHIVE_START: &[u8; 8] = b"!<arch>\n"; +pub const IMAGE_ARCHIVE_END: &[u8] = b"`\n"; +pub const IMAGE_ARCHIVE_PAD: &[u8] = b"\n"; +pub const IMAGE_ARCHIVE_LINKER_MEMBER: &[u8; 16] = b"/ "; +pub const IMAGE_ARCHIVE_LONGNAMES_MEMBER: &[u8; 16] = b"// "; +pub const IMAGE_ARCHIVE_HYBRIDMAP_MEMBER: &[u8; 16] = b"/<HYBRIDMAP>/ "; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageArchiveMemberHeader { + /// File member name - `/' terminated. + pub name: [u8; 16], + /// File member date - decimal. + pub date: [u8; 12], + /// File member user id - decimal. + pub user_id: [u8; 6], + /// File member group id - decimal. + pub group_id: [u8; 6], + /// File member mode - octal. + pub mode: [u8; 8], + /// File member size - decimal. + pub size: [u8; 10], + /// String to end header. + pub end_header: [u8; 2], +} + +pub const IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR: u16 = 60; + +// +// DLL support. +// + +// +// Export Format +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageExportDirectory { + pub characteristics: U32<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub name: U32<LE>, + pub base: U32<LE>, + pub number_of_functions: U32<LE>, + pub number_of_names: U32<LE>, + /// RVA from base of image + pub address_of_functions: U32<LE>, + /// RVA from base of image + pub address_of_names: U32<LE>, + /// RVA from base of image + pub address_of_name_ordinals: U32<LE>, +} + +// +// Import Format +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageImportByName { + pub hint: U16<LE>, + //pub name: [i8; 1], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageThunkData64(pub U64<LE>); +/* + union { +/// PBYTE + pub forwarder_string: U64<LE>, +/// PDWORD + pub function: U64<LE>, + pub ordinal: U64<LE>, +/// PIMAGE_IMPORT_BY_NAME + pub address_of_data: U64<LE>, + } u1; +*/ + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageThunkData32(pub U32<LE>); +/* + union { +/// PBYTE + pub forwarder_string: U32<LE>, +/// PDWORD + pub function: U32<LE>, + pub ordinal: U32<LE>, +/// PIMAGE_IMPORT_BY_NAME + pub address_of_data: U32<LE>, + } u1; +} +*/ + +pub const IMAGE_ORDINAL_FLAG64: u64 = 0x8000000000000000; +pub const IMAGE_ORDINAL_FLAG32: u32 = 0x80000000; + +/* +#define IMAGE_ORDINAL64(Ordinal) (Ordinal & 0xffff) +#define IMAGE_ORDINAL32(Ordinal) (Ordinal & 0xffff) +#define IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0) +#define IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0) + +*/ + +// +// Thread Local Storage +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageTlsDirectory64 { + pub start_address_of_raw_data: U64<LE>, + pub end_address_of_raw_data: U64<LE>, + /// PDWORD + pub address_of_index: U64<LE>, + /// PIMAGE_TLS_CALLBACK *; + pub address_of_call_backs: U64<LE>, + pub size_of_zero_fill: U32<LE>, + pub characteristics: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageTlsDirectory32 { + pub start_address_of_raw_data: U32<LE>, + pub end_address_of_raw_data: U32<LE>, + /// PDWORD + pub address_of_index: U32<LE>, + /// PIMAGE_TLS_CALLBACK * + pub address_of_call_backs: U32<LE>, + pub size_of_zero_fill: U32<LE>, + pub characteristics: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageImportDescriptor { + /// RVA to original unbound IAT (`ImageThunkData32`/`ImageThunkData64`) + /// 0 for terminating null import descriptor + pub original_first_thunk: U32Bytes<LE>, + /// 0 if not bound, + /// -1 if bound, and real date\time stamp + /// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + /// O.W. date/time stamp of DLL bound to (Old BIND) + pub time_date_stamp: U32Bytes<LE>, + /// -1 if no forwarders + pub forwarder_chain: U32Bytes<LE>, + pub name: U32Bytes<LE>, + /// RVA to IAT (if bound this IAT has actual addresses) + pub first_thunk: U32Bytes<LE>, +} + +impl ImageImportDescriptor { + /// Tell whether this import descriptor is the null descriptor + /// (used to mark the end of the iterator array in a PE) + pub fn is_null(&self) -> bool { + self.original_first_thunk.get(LE) == 0 + && self.time_date_stamp.get(LE) == 0 + && self.forwarder_chain.get(LE) == 0 + && self.name.get(LE) == 0 + && self.first_thunk.get(LE) == 0 + } +} + +// +// New format import descriptors pointed to by DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ] +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageBoundImportDescriptor { + pub time_date_stamp: U32<LE>, + pub offset_module_name: U16<LE>, + pub number_of_module_forwarder_refs: U16<LE>, + // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageBoundForwarderRef { + pub time_date_stamp: U32<LE>, + pub offset_module_name: U16<LE>, + pub reserved: U16<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDelayloadDescriptor { + pub attributes: U32<LE>, + + /// RVA to the name of the target library (NULL-terminate ASCII string) + pub dll_name_rva: U32<LE>, + /// RVA to the HMODULE caching location (PHMODULE) + pub module_handle_rva: U32<LE>, + /// RVA to the start of the IAT (PIMAGE_THUNK_DATA) + pub import_address_table_rva: U32<LE>, + /// RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData) + pub import_name_table_rva: U32<LE>, + /// RVA to an optional bound IAT + pub bound_import_address_table_rva: U32<LE>, + /// RVA to an optional unload info table + pub unload_information_table_rva: U32<LE>, + /// 0 if not bound, otherwise, date/time of the target DLL + pub time_date_stamp: U32<LE>, +} + +impl ImageDelayloadDescriptor { + /// Tell whether this delay-load import descriptor is the null descriptor + /// (used to mark the end of the iterator array in a PE) + pub fn is_null(&self) -> bool { + self.attributes.get(LE) == 0 + && self.dll_name_rva.get(LE) == 0 + && self.module_handle_rva.get(LE) == 0 + && self.import_address_table_rva.get(LE) == 0 + && self.import_name_table_rva.get(LE) == 0 + && self.bound_import_address_table_rva.get(LE) == 0 + && self.unload_information_table_rva.get(LE) == 0 + && self.time_date_stamp.get(LE) == 0 + } +} + +/// Delay load version 2 flag for `ImageDelayloadDescriptor::attributes`. +pub const IMAGE_DELAYLOAD_RVA_BASED: u32 = 0x8000_0000; + +// +// Resource Format. +// + +// +// Resource directory consists of two counts, following by a variable length +// array of directory entries. The first count is the number of entries at +// beginning of the array that have actual names associated with each entry. +// The entries are in ascending order, case insensitive strings. The second +// count is the number of entries that immediately follow the named entries. +// This second count identifies the number of entries that have 16-bit integer +// Ids as their name. These entries are also sorted in ascending order. +// +// This structure allows fast lookup by either name or number, but for any +// given resource entry only one form of lookup is supported, not both. +// This is consistent with the syntax of the .RC file and the .RES file. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageResourceDirectory { + pub characteristics: U32<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub number_of_named_entries: U16<LE>, + pub number_of_id_entries: U16<LE>, +} + +pub const IMAGE_RESOURCE_NAME_IS_STRING: u32 = 0x8000_0000; +pub const IMAGE_RESOURCE_DATA_IS_DIRECTORY: u32 = 0x8000_0000; +// +// Each directory contains the 32-bit Name of the entry and an offset, +// relative to the beginning of the resource directory of the data associated +// with this directory entry. If the name of the entry is an actual text +// string instead of an integer Id, then the high order bit of the name field +// is set to one and the low order 31-bits are an offset, relative to the +// beginning of the resource directory of the string, which is of type +// IMAGE_RESOURCE_DIRECTORY_STRING. Otherwise the high bit is clear and the +// low-order 16-bits are the integer Id that identify this resource directory +// entry. If the directory entry is yet another resource directory (i.e. a +// subdirectory), then the high order bit of the offset field will be +// set to indicate this. Otherwise the high bit is clear and the offset +// field points to a resource data entry. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageResourceDirectoryEntry { + pub name_or_id: U32<LE>, + pub offset_to_data_or_directory: U32<LE>, +} + +// +// For resource directory entries that have actual string names, the Name +// field of the directory entry points to an object of the following type. +// All of these string objects are stored together after the last resource +// directory entry and before the first resource data object. This minimizes +// the impact of these variable length objects on the alignment of the fixed +// size directory entry objects. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageResourceDirectoryString { + pub length: U16<LE>, + //pub name_string: [i8; 1], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageResourceDirStringU { + pub length: U16<LE>, + //pub name_string: [U16<LE>; 1], +} + +// +// Each resource data entry describes a leaf node in the resource directory +// tree. It contains an offset, relative to the beginning of the resource +// directory of the data for the resource, a size field that gives the number +// of bytes of data at that offset, a CodePage that should be used when +// decoding code point values within the resource data. Typically for new +// applications the code page would be the unicode code page. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageResourceDataEntry { + /// RVA of the data. + pub offset_to_data: U32<LE>, + pub size: U32<LE>, + pub code_page: U32<LE>, + pub reserved: U32<LE>, +} + +// Resource type: https://docs.microsoft.com/en-us/windows/win32/menurc/resource-types + +/// ID for: Hardware-dependent cursor resource. +pub const RT_CURSOR: u16 = 1; +/// ID for: Bitmap resource. +pub const RT_BITMAP: u16 = 2; +/// ID for: Hardware-dependent icon resource. +pub const RT_ICON: u16 = 3; +/// ID for: Menu resource. +pub const RT_MENU: u16 = 4; +/// ID for: Dialog box. +pub const RT_DIALOG: u16 = 5; +/// ID for: String-table entry. +pub const RT_STRING: u16 = 6; +/// ID for: Font directory resource. +pub const RT_FONTDIR: u16 = 7; +/// ID for: Font resource. +pub const RT_FONT: u16 = 8; +/// ID for: Accelerator table. +pub const RT_ACCELERATOR: u16 = 9; +/// ID for: Application-defined resource (raw data). +pub const RT_RCDATA: u16 = 10; +/// ID for: Message-table entry. +pub const RT_MESSAGETABLE: u16 = 11; +/// ID for: Hardware-independent cursor resource. +pub const RT_GROUP_CURSOR: u16 = 12; +/// ID for: Hardware-independent icon resource. +pub const RT_GROUP_ICON: u16 = 14; +/// ID for: Version resource. +pub const RT_VERSION: u16 = 16; +/// ID for: Allows a resource editing tool to associate a string with an .rc file. +pub const RT_DLGINCLUDE: u16 = 17; +/// ID for: Plug and Play resource. +pub const RT_PLUGPLAY: u16 = 19; +/// ID for: VXD. +pub const RT_VXD: u16 = 20; +/// ID for: Animated cursor. +pub const RT_ANICURSOR: u16 = 21; +/// ID for: Animated icon. +pub const RT_ANIICON: u16 = 22; +/// ID for: HTML resource. +pub const RT_HTML: u16 = 23; +/// ID for: Side-by-Side Assembly Manifest. +pub const RT_MANIFEST: u16 = 24; + +// +// Code Integrity in loadconfig (CI) +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageLoadConfigCodeIntegrity { + /// Flags to indicate if CI information is available, etc. + pub flags: U16<LE>, + /// 0xFFFF means not available + pub catalog: U16<LE>, + pub catalog_offset: U32<LE>, + /// Additional bitmask to be defined later + pub reserved: U32<LE>, +} + +// +// Dynamic value relocation table in loadconfig +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocationTable { + pub version: U32<LE>, + pub size: U32<LE>, + // DynamicRelocations: [ImageDynamicRelocation; 0], +} + +// +// Dynamic value relocation entries following IMAGE_DYNAMIC_RELOCATION_TABLE +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation32 { + pub symbol: U32<LE>, + pub base_reloc_size: U32<LE>, + // BaseRelocations: [ImageBaseRelocation; 0], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation64 { + pub symbol: U64<LE>, + pub base_reloc_size: U32<LE>, + // BaseRelocations: [ImageBaseRelocation; 0], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation32V2 { + pub header_size: U32<LE>, + pub fixup_info_size: U32<LE>, + pub symbol: U32<LE>, + pub symbol_group: U32<LE>, + pub flags: U32<LE>, + // ... variable length header fields + // pub fixup_info: [u8; fixup_info_size] +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation64V2 { + pub header_size: U32<LE>, + pub fixup_info_size: U32<LE>, + pub symbol: U64<LE>, + pub symbol_group: U32<LE>, + pub flags: U32<LE>, + // ... variable length header fields + // pub fixup_info[u8; fixup_info_size] +} + +// +// Defined symbolic dynamic relocation entries. +// + +pub const IMAGE_DYNAMIC_RELOCATION_GUARD_RF_PROLOGUE: u32 = 0x0000_0001; +pub const IMAGE_DYNAMIC_RELOCATION_GUARD_RF_EPILOGUE: u32 = 0x0000_0002; +pub const IMAGE_DYNAMIC_RELOCATION_GUARD_IMPORT_CONTROL_TRANSFER: u32 = 0x0000_0003; +pub const IMAGE_DYNAMIC_RELOCATION_GUARD_INDIR_CONTROL_TRANSFER: u32 = 0x0000_0004; +pub const IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH: u32 = 0x0000_0005; + +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImagePrologueDynamicRelocationHeader { + pub prologue_byte_count: u8, + // pub prologue_bytes: [u8; prologue_byte_count], +} + +// This struct has alignment 1. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageEpilogueDynamicRelocationHeader { + pub epilogue_count: U32Bytes<LE>, + pub epilogue_byte_count: u8, + pub branch_descriptor_element_size: u8, + pub branch_descriptor_count: U16Bytes<LE>, + // pub branch_descriptors[...], + // pub branch_descriptor_bit_map[...], +} + +/* +// TODO? bitfields +// TODO: unaligned? +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageImportControlTransferDynamicRelocation { + DWORD PageRelativeOffset : 12; + DWORD IndirectCall : 1; + DWORD IATIndex : 19; +} + +// TODO: unaligned? +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageIndirControlTransferDynamicRelocation { + WORD PageRelativeOffset : 12; + WORD IndirectCall : 1; + WORD RexWPrefix : 1; + WORD CfgCheck : 1; + WORD Reserved : 1; +} + +// TODO: unaligned? +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageSwitchtableBranchDynamicRelocation { + WORD PageRelativeOffset : 12; + WORD RegisterNumber : 4; +} +*/ + +// +// Load Configuration Directory Entry +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageLoadConfigDirectory32 { + pub size: U32<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub global_flags_clear: U32<LE>, + pub global_flags_set: U32<LE>, + pub critical_section_default_timeout: U32<LE>, + pub de_commit_free_block_threshold: U32<LE>, + pub de_commit_total_free_threshold: U32<LE>, + /// VA + pub lock_prefix_table: U32<LE>, + pub maximum_allocation_size: U32<LE>, + pub virtual_memory_threshold: U32<LE>, + pub process_heap_flags: U32<LE>, + pub process_affinity_mask: U32<LE>, + pub csd_version: U16<LE>, + pub dependent_load_flags: U16<LE>, + /// VA + pub edit_list: U32<LE>, + /// VA + pub security_cookie: U32<LE>, + /// VA + pub sehandler_table: U32<LE>, + pub sehandler_count: U32<LE>, + /// VA + pub guard_cf_check_function_pointer: U32<LE>, + /// VA + pub guard_cf_dispatch_function_pointer: U32<LE>, + /// VA + pub guard_cf_function_table: U32<LE>, + pub guard_cf_function_count: U32<LE>, + pub guard_flags: U32<LE>, + pub code_integrity: ImageLoadConfigCodeIntegrity, + /// VA + pub guard_address_taken_iat_entry_table: U32<LE>, + pub guard_address_taken_iat_entry_count: U32<LE>, + /// VA + pub guard_long_jump_target_table: U32<LE>, + pub guard_long_jump_target_count: U32<LE>, + /// VA + pub dynamic_value_reloc_table: U32<LE>, + pub chpe_metadata_pointer: U32<LE>, + /// VA + pub guard_rf_failure_routine: U32<LE>, + /// VA + pub guard_rf_failure_routine_function_pointer: U32<LE>, + pub dynamic_value_reloc_table_offset: U32<LE>, + pub dynamic_value_reloc_table_section: U16<LE>, + pub reserved2: U16<LE>, + /// VA + pub guard_rf_verify_stack_pointer_function_pointer: U32<LE>, + pub hot_patch_table_offset: U32<LE>, + pub reserved3: U32<LE>, + /// VA + pub enclave_configuration_pointer: U32<LE>, + /// VA + pub volatile_metadata_pointer: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageLoadConfigDirectory64 { + pub size: U32<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub global_flags_clear: U32<LE>, + pub global_flags_set: U32<LE>, + pub critical_section_default_timeout: U32<LE>, + pub de_commit_free_block_threshold: U64<LE>, + pub de_commit_total_free_threshold: U64<LE>, + /// VA + pub lock_prefix_table: U64<LE>, + pub maximum_allocation_size: U64<LE>, + pub virtual_memory_threshold: U64<LE>, + pub process_affinity_mask: U64<LE>, + pub process_heap_flags: U32<LE>, + pub csd_version: U16<LE>, + pub dependent_load_flags: U16<LE>, + /// VA + pub edit_list: U64<LE>, + /// VA + pub security_cookie: U64<LE>, + /// VA + pub sehandler_table: U64<LE>, + pub sehandler_count: U64<LE>, + /// VA + pub guard_cf_check_function_pointer: U64<LE>, + /// VA + pub guard_cf_dispatch_function_pointer: U64<LE>, + /// VA + pub guard_cf_function_table: U64<LE>, + pub guard_cf_function_count: U64<LE>, + pub guard_flags: U32<LE>, + pub code_integrity: ImageLoadConfigCodeIntegrity, + /// VA + pub guard_address_taken_iat_entry_table: U64<LE>, + pub guard_address_taken_iat_entry_count: U64<LE>, + /// VA + pub guard_long_jump_target_table: U64<LE>, + pub guard_long_jump_target_count: U64<LE>, + /// VA + pub dynamic_value_reloc_table: U64<LE>, + /// VA + pub chpe_metadata_pointer: U64<LE>, + /// VA + pub guard_rf_failure_routine: U64<LE>, + /// VA + pub guard_rf_failure_routine_function_pointer: U64<LE>, + pub dynamic_value_reloc_table_offset: U32<LE>, + pub dynamic_value_reloc_table_section: U16<LE>, + pub reserved2: U16<LE>, + /// VA + pub guard_rf_verify_stack_pointer_function_pointer: U64<LE>, + pub hot_patch_table_offset: U32<LE>, + pub reserved3: U32<LE>, + /// VA + pub enclave_configuration_pointer: U64<LE>, + /// VA + pub volatile_metadata_pointer: U64<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageHotPatchInfo { + pub version: U32<LE>, + pub size: U32<LE>, + pub sequence_number: U32<LE>, + pub base_image_list: U32<LE>, + pub base_image_count: U32<LE>, + /// Version 2 and later + pub buffer_offset: U32<LE>, + /// Version 3 and later + pub extra_patch_size: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageHotPatchBase { + pub sequence_number: U32<LE>, + pub flags: U32<LE>, + pub original_time_date_stamp: U32<LE>, + pub original_check_sum: U32<LE>, + pub code_integrity_info: U32<LE>, + pub code_integrity_size: U32<LE>, + pub patch_table: U32<LE>, + /// Version 2 and later + pub buffer_offset: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageHotPatchHashes { + pub sha256: [u8; 32], + pub sha1: [u8; 20], +} + +pub const IMAGE_HOT_PATCH_BASE_OBLIGATORY: u32 = 0x0000_0001; +pub const IMAGE_HOT_PATCH_BASE_CAN_ROLL_BACK: u32 = 0x0000_0002; + +pub const IMAGE_HOT_PATCH_CHUNK_INVERSE: u32 = 0x8000_0000; +pub const IMAGE_HOT_PATCH_CHUNK_OBLIGATORY: u32 = 0x4000_0000; +pub const IMAGE_HOT_PATCH_CHUNK_RESERVED: u32 = 0x3FF0_3000; +pub const IMAGE_HOT_PATCH_CHUNK_TYPE: u32 = 0x000F_C000; +pub const IMAGE_HOT_PATCH_CHUNK_SOURCE_RVA: u32 = 0x0000_8000; +pub const IMAGE_HOT_PATCH_CHUNK_TARGET_RVA: u32 = 0x0000_4000; +pub const IMAGE_HOT_PATCH_CHUNK_SIZE: u32 = 0x0000_0FFF; + +pub const IMAGE_HOT_PATCH_NONE: u32 = 0x0000_0000; +pub const IMAGE_HOT_PATCH_FUNCTION: u32 = 0x0001_C000; +pub const IMAGE_HOT_PATCH_ABSOLUTE: u32 = 0x0002_C000; +pub const IMAGE_HOT_PATCH_REL32: u32 = 0x0003_C000; +pub const IMAGE_HOT_PATCH_CALL_TARGET: u32 = 0x0004_4000; +pub const IMAGE_HOT_PATCH_INDIRECT: u32 = 0x0005_C000; +pub const IMAGE_HOT_PATCH_NO_CALL_TARGET: u32 = 0x0006_4000; +pub const IMAGE_HOT_PATCH_DYNAMIC_VALUE: u32 = 0x0007_8000; + +/// Module performs control flow integrity checks using system-supplied support +pub const IMAGE_GUARD_CF_INSTRUMENTED: u32 = 0x0000_0100; +/// Module performs control flow and write integrity checks +pub const IMAGE_GUARD_CFW_INSTRUMENTED: u32 = 0x0000_0200; +/// Module contains valid control flow target metadata +pub const IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT: u32 = 0x0000_0400; +/// Module does not make use of the /GS security cookie +pub const IMAGE_GUARD_SECURITY_COOKIE_UNUSED: u32 = 0x0000_0800; +/// Module supports read only delay load IAT +pub const IMAGE_GUARD_PROTECT_DELAYLOAD_IAT: u32 = 0x0000_1000; +/// Delayload import table in its own .didat section (with nothing else in it) that can be freely reprotected +pub const IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION: u32 = 0x0000_2000; +/// Module contains suppressed export information. +/// +/// This also infers that the address taken taken IAT table is also present in the load config. +pub const IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT: u32 = 0x0000_4000; +/// Module enables suppression of exports +pub const IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION: u32 = 0x0000_8000; +/// Module contains longjmp target information +pub const IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT: u32 = 0x0001_0000; +/// Module contains return flow instrumentation and metadata +pub const IMAGE_GUARD_RF_INSTRUMENTED: u32 = 0x0002_0000; +/// Module requests that the OS enable return flow protection +pub const IMAGE_GUARD_RF_ENABLE: u32 = 0x0004_0000; +/// Module requests that the OS enable return flow protection in strict mode +pub const IMAGE_GUARD_RF_STRICT: u32 = 0x0008_0000; +/// Module was built with retpoline support +pub const IMAGE_GUARD_RETPOLINE_PRESENT: u32 = 0x0010_0000; + +/// Stride of Guard CF function table encoded in these bits (additional count of bytes per element) +pub const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK: u32 = 0xF000_0000; +/// Shift to right-justify Guard CF function table stride +pub const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT: u32 = 28; + +// +// GFIDS table entry flags. +// + +/// The containing GFID entry is suppressed +pub const IMAGE_GUARD_FLAG_FID_SUPPRESSED: u16 = 0x01; +/// The containing GFID entry is export suppressed +pub const IMAGE_GUARD_FLAG_EXPORT_SUPPRESSED: u16 = 0x02; + +// +// WIN CE Exception table format +// + +// +// Function table entry format. Function table is pointed to by the +// IMAGE_DIRECTORY_ENTRY_EXCEPTION directory entry. +// + +/* +// TODO? bitfields +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageCeRuntimeFunctionEntry { + pub func_start: U32<LE>, + DWORD PrologLen : 8; + DWORD FuncLen : 22; + DWORD ThirtyTwoBit : 1; + DWORD ExceptionFlag : 1; +} +*/ + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageArmRuntimeFunctionEntry { + pub begin_address: U32<LE>, + pub unwind_data: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageArm64RuntimeFunctionEntry { + pub begin_address: U32<LE>, + pub unwind_data: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAlpha64RuntimeFunctionEntry { + pub begin_address: U64<LE>, + pub end_address: U64<LE>, + pub exception_handler: U64<LE>, + pub handler_data: U64<LE>, + pub prolog_end_address: U64<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAlphaRuntimeFunctionEntry { + pub begin_address: U32<LE>, + pub end_address: U32<LE>, + pub exception_handler: U32<LE>, + pub handler_data: U32<LE>, + pub prolog_end_address: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageRuntimeFunctionEntry { + pub begin_address: U32<LE>, + pub end_address: U32<LE>, + pub unwind_info_address_or_data: U32<LE>, +} + +// +// Software enclave information +// + +pub const IMAGE_ENCLAVE_LONG_ID_LENGTH: usize = 32; +pub const IMAGE_ENCLAVE_SHORT_ID_LENGTH: usize = 16; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageEnclaveConfig32 { + pub size: U32<LE>, + pub minimum_required_config_size: U32<LE>, + pub policy_flags: U32<LE>, + pub number_of_imports: U32<LE>, + pub import_list: U32<LE>, + pub import_entry_size: U32<LE>, + pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_version: U32<LE>, + pub security_version: U32<LE>, + pub enclave_size: U32<LE>, + pub number_of_threads: U32<LE>, + pub enclave_flags: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageEnclaveConfig64 { + pub size: U32<LE>, + pub minimum_required_config_size: U32<LE>, + pub policy_flags: U32<LE>, + pub number_of_imports: U32<LE>, + pub import_list: U32<LE>, + pub import_entry_size: U32<LE>, + pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_version: U32<LE>, + pub security_version: U32<LE>, + pub enclave_size: U64<LE>, + pub number_of_threads: U32<LE>, + pub enclave_flags: U32<LE>, +} + +//pub const IMAGE_ENCLAVE_MINIMUM_CONFIG_SIZE: usize = FIELD_OFFSET(IMAGE_ENCLAVE_CONFIG, EnclaveFlags); + +pub const IMAGE_ENCLAVE_POLICY_DEBUGGABLE: u32 = 0x0000_0001; + +pub const IMAGE_ENCLAVE_FLAG_PRIMARY_IMAGE: u32 = 0x0000_0001; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageEnclaveImport { + pub match_type: U32<LE>, + pub minimum_security_version: U32<LE>, + pub unique_or_author_id: [u8; IMAGE_ENCLAVE_LONG_ID_LENGTH], + pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub import_name: U32<LE>, + pub reserved: U32<LE>, +} + +pub const IMAGE_ENCLAVE_IMPORT_MATCH_NONE: u32 = 0x0000_0000; +pub const IMAGE_ENCLAVE_IMPORT_MATCH_UNIQUE_ID: u32 = 0x0000_0001; +pub const IMAGE_ENCLAVE_IMPORT_MATCH_AUTHOR_ID: u32 = 0x0000_0002; +pub const IMAGE_ENCLAVE_IMPORT_MATCH_FAMILY_ID: u32 = 0x0000_0003; +pub const IMAGE_ENCLAVE_IMPORT_MATCH_IMAGE_ID: u32 = 0x0000_0004; + +// +// Debug Format +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDebugDirectory { + pub characteristics: U32<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub typ: U32<LE>, + pub size_of_data: U32<LE>, + pub address_of_raw_data: U32<LE>, + pub pointer_to_raw_data: U32<LE>, +} + +pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0; +pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1; +pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2; +pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3; +pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4; +pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5; +pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6; +pub const IMAGE_DEBUG_TYPE_OMAP_TO_SRC: u32 = 7; +pub const IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: u32 = 8; +pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9; +pub const IMAGE_DEBUG_TYPE_RESERVED10: u32 = 10; +pub const IMAGE_DEBUG_TYPE_CLSID: u32 = 11; +pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12; +pub const IMAGE_DEBUG_TYPE_POGO: u32 = 13; +pub const IMAGE_DEBUG_TYPE_ILTCG: u32 = 14; +pub const IMAGE_DEBUG_TYPE_MPX: u32 = 15; +pub const IMAGE_DEBUG_TYPE_REPRO: u32 = 16; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageCoffSymbolsHeader { + pub number_of_symbols: U32<LE>, + pub lva_to_first_symbol: U32<LE>, + pub number_of_linenumbers: U32<LE>, + pub lva_to_first_linenumber: U32<LE>, + pub rva_to_first_byte_of_code: U32<LE>, + pub rva_to_last_byte_of_code: U32<LE>, + pub rva_to_first_byte_of_data: U32<LE>, + pub rva_to_last_byte_of_data: U32<LE>, +} + +pub const FRAME_FPO: u16 = 0; +pub const FRAME_TRAP: u16 = 1; +pub const FRAME_TSS: u16 = 2; +pub const FRAME_NONFPO: u16 = 3; + +/* +// TODO? bitfields +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FpoData { +/// offset 1st byte of function code + pub ul_off_start: U32<LE>, +/// # bytes in function + pub cb_proc_size: U32<LE>, +/// # bytes in locals/4 + pub cdw_locals: U32<LE>, +/// # bytes in params/4 + pub cdw_params: U16<LE>, +/// # bytes in prolog + WORD cbProlog : 8; +/// # regs saved + WORD cbRegs : 3; +/// TRUE if SEH in func + WORD fHasSEH : 1; +/// TRUE if EBP has been allocated + WORD fUseBP : 1; +/// reserved for future use + WORD reserved : 1; +/// frame type + WORD cbFrame : 2; +} +pub const SIZEOF_RFPO_DATA: usize = 16; +*/ + +pub const IMAGE_DEBUG_MISC_EXENAME: u16 = 1; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDebugMisc { + /// type of misc data, see defines + pub data_type: U32<LE>, + /// total length of record, rounded to four byte multiple. + pub length: U32<LE>, + /// TRUE if data is unicode string + pub unicode: u8, + pub reserved: [u8; 3], + // Actual data + //pub data: [u8; 1], +} + +// +// Function table extracted from MIPS/ALPHA/IA64 images. Does not contain +// information needed only for runtime support. Just those fields for +// each entry needed by a debugger. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageFunctionEntry { + pub starting_address: U32<LE>, + pub ending_address: U32<LE>, + pub end_of_prologue: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageFunctionEntry64 { + pub starting_address: U64<LE>, + pub ending_address: U64<LE>, + pub end_of_prologue_or_unwind_info_address: U64<LE>, +} + +// +// Debugging information can be stripped from an image file and placed +// in a separate .DBG file, whose file name part is the same as the +// image file name part (e.g. symbols for CMD.EXE could be stripped +// and placed in CMD.DBG). This is indicated by the IMAGE_FILE_DEBUG_STRIPPED +// flag in the Characteristics field of the file header. The beginning of +// the .DBG file contains the following structure which captures certain +// information from the image file. This allows a debug to proceed even if +// the original image file is not accessible. This header is followed by +// zero of more IMAGE_SECTION_HEADER structures, followed by zero or more +// IMAGE_DEBUG_DIRECTORY structures. The latter structures and those in +// the image file contain file offsets relative to the beginning of the +// .DBG file. +// +// If symbols have been stripped from an image, the IMAGE_DEBUG_MISC structure +// is left in the image file, but not mapped. This allows a debugger to +// compute the name of the .DBG file, from the name of the image in the +// IMAGE_DEBUG_MISC structure. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageSeparateDebugHeader { + pub signature: U16<LE>, + pub flags: U16<LE>, + pub machine: U16<LE>, + pub characteristics: U16<LE>, + pub time_date_stamp: U32<LE>, + pub check_sum: U32<LE>, + pub image_base: U32<LE>, + pub size_of_image: U32<LE>, + pub number_of_sections: U32<LE>, + pub exported_names_size: U32<LE>, + pub debug_directory_size: U32<LE>, + pub section_alignment: U32<LE>, + pub reserved: [U32<LE>; 2], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct NonPagedDebugInfo { + pub signature: U16<LE>, + pub flags: U16<LE>, + pub size: U32<LE>, + pub machine: U16<LE>, + pub characteristics: U16<LE>, + pub time_date_stamp: U32<LE>, + pub check_sum: U32<LE>, + pub size_of_image: U32<LE>, + pub image_base: U64<LE>, + //debug_directory_size + //ImageDebugDirectory +} + +pub const IMAGE_SEPARATE_DEBUG_SIGNATURE: u16 = 0x4944; +pub const NON_PAGED_DEBUG_SIGNATURE: u16 = 0x494E; + +pub const IMAGE_SEPARATE_DEBUG_FLAGS_MASK: u16 = 0x8000; +/// when DBG was updated, the old checksum didn't match. +pub const IMAGE_SEPARATE_DEBUG_MISMATCH: u16 = 0x8000; + +// +// The .arch section is made up of headers, each describing an amask position/value +// pointing to an array of IMAGE_ARCHITECTURE_ENTRY's. Each "array" (both the header +// and entry arrays) are terminiated by a quadword of 0xffffffffL. +// +// NOTE: There may be quadwords of 0 sprinkled around and must be skipped. +// + +/* +// TODO? bitfields +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageArchitectureHeader { + /// 1 -> code section depends on mask bit + /// 0 -> new instruction depends on mask bit + unsigned int AmaskValue: 1; + /// MBZ + int :7; + /// Amask bit in question for this fixup + unsigned int AmaskShift: 8; + /// MBZ + int :16; + /// RVA into .arch section to array of ARCHITECTURE_ENTRY's + pub first_entry_rva: U32<LE>, +} +*/ + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageArchitectureEntry { + /// RVA of instruction to fixup + pub fixup_inst_rva: U32<LE>, + /// fixup instruction (see alphaops.h) + pub new_inst: U32<LE>, +} + +// The following structure defines the new import object. Note the values of the first two fields, +// which must be set as stated in order to differentiate old and new import members. +// Following this structure, the linker emits two null-terminated strings used to recreate the +// import at the time of use. The first string is the import's name, the second is the dll's name. + +pub const IMPORT_OBJECT_HDR_SIG2: u16 = 0xffff; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImportObjectHeader { + /// Must be IMAGE_FILE_MACHINE_UNKNOWN + pub sig1: U16<LE>, + /// Must be IMPORT_OBJECT_HDR_SIG2. + pub sig2: U16<LE>, + pub version: U16<LE>, + pub machine: U16<LE>, + /// Time/date stamp + pub time_date_stamp: U32<LE>, + /// particularly useful for incremental links + pub size_of_data: U32<LE>, + + /// if grf & IMPORT_OBJECT_ORDINAL + pub ordinal_or_hint: U16<LE>, + + // WORD Type : 2; + // WORD NameType : 3; + // WORD Reserved : 11; + pub name_type: U16<LE>, +} + +pub const IMPORT_OBJECT_TYPE_MASK: u16 = 0b11; +pub const IMPORT_OBJECT_TYPE_SHIFT: u16 = 0; +pub const IMPORT_OBJECT_CODE: u16 = 0; +pub const IMPORT_OBJECT_DATA: u16 = 1; +pub const IMPORT_OBJECT_CONST: u16 = 2; + +pub const IMPORT_OBJECT_NAME_MASK: u16 = 0b111; +pub const IMPORT_OBJECT_NAME_SHIFT: u16 = 2; +/// Import by ordinal +pub const IMPORT_OBJECT_ORDINAL: u16 = 0; +/// Import name == public symbol name. +pub const IMPORT_OBJECT_NAME: u16 = 1; +/// Import name == public symbol name skipping leading ?, @, or optionally _. +pub const IMPORT_OBJECT_NAME_NO_PREFIX: u16 = 2; +/// Import name == public symbol name skipping leading ?, @, or optionally _ and truncating at first @. +pub const IMPORT_OBJECT_NAME_UNDECORATE: u16 = 3; +/// Import name == a name is explicitly provided after the DLL name. +pub const IMPORT_OBJECT_NAME_EXPORTAS: u16 = 4; + +// COM+ Header entry point flags. +pub const COMIMAGE_FLAGS_ILONLY: u32 = 0x0000_0001; +pub const COMIMAGE_FLAGS_32BITREQUIRED: u32 = 0x0000_0002; +pub const COMIMAGE_FLAGS_IL_LIBRARY: u32 = 0x0000_0004; +pub const COMIMAGE_FLAGS_STRONGNAMESIGNED: u32 = 0x0000_0008; +pub const COMIMAGE_FLAGS_NATIVE_ENTRYPOINT: u32 = 0x0000_0010; +pub const COMIMAGE_FLAGS_TRACKDEBUGDATA: u32 = 0x0001_0000; +pub const COMIMAGE_FLAGS_32BITPREFERRED: u32 = 0x0002_0000; + +// Version flags for image. +pub const COR_VERSION_MAJOR_V2: u16 = 2; +pub const COR_VERSION_MAJOR: u16 = COR_VERSION_MAJOR_V2; +pub const COR_VERSION_MINOR: u16 = 5; +pub const COR_DELETED_NAME_LENGTH: usize = 8; +pub const COR_VTABLEGAP_NAME_LENGTH: usize = 8; + +// Maximum size of a NativeType descriptor. +pub const NATIVE_TYPE_MAX_CB: u16 = 1; +pub const COR_ILMETHOD_SECT_SMALL_MAX_DATASIZE: u16 = 0xFF; + +// Consts for the MIH FLAGS +pub const IMAGE_COR_MIH_METHODRVA: u16 = 0x01; +pub const IMAGE_COR_MIH_EHRVA: u16 = 0x02; +pub const IMAGE_COR_MIH_BASICBLOCK: u16 = 0x08; + +// V-table constants +/// V-table slots are 32-bits in size. +pub const COR_VTABLE_32BIT: u16 = 0x01; +/// V-table slots are 64-bits in size. +pub const COR_VTABLE_64BIT: u16 = 0x02; +/// If set, transition from unmanaged. +pub const COR_VTABLE_FROM_UNMANAGED: u16 = 0x04; +/// If set, transition from unmanaged with keeping the current appdomain. +pub const COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN: u16 = 0x08; +/// Call most derived method described by +pub const COR_VTABLE_CALL_MOST_DERIVED: u16 = 0x10; + +// EATJ constants +/// Size of a jump thunk reserved range. +pub const IMAGE_COR_EATJ_THUNK_SIZE: usize = 32; + +// Max name lengths +pub const MAX_CLASS_NAME: usize = 1024; +pub const MAX_PACKAGE_NAME: usize = 1024; + +// CLR 2.0 header structure. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageCor20Header { + // Header versioning + pub cb: U32<LE>, + pub major_runtime_version: U16<LE>, + pub minor_runtime_version: U16<LE>, + + // Symbol table and startup information + pub meta_data: ImageDataDirectory, + pub flags: U32<LE>, + + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint. + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint. + pub entry_point_token_or_rva: U32<LE>, + + // Binding information + pub resources: ImageDataDirectory, + pub strong_name_signature: ImageDataDirectory, + + // Regular fixup and binding information + pub code_manager_table: ImageDataDirectory, + pub vtable_fixups: ImageDataDirectory, + pub export_address_table_jumps: ImageDataDirectory, + + // Precompiled image info (internal use only - set to zero) + pub managed_native_header: ImageDataDirectory, +} + +unsafe_impl_pod!( + ImageDosHeader, + ImageOs2Header, + ImageVxdHeader, + ImageFileHeader, + ImageDataDirectory, + ImageOptionalHeader32, + ImageRomOptionalHeader, + ImageOptionalHeader64, + ImageNtHeaders64, + ImageNtHeaders32, + ImageRomHeaders, + Guid, + AnonObjectHeader, + AnonObjectHeaderV2, + AnonObjectHeaderBigobj, + ImageSectionHeader, + ImageSymbol, + ImageSymbolBytes, + ImageSymbolEx, + ImageSymbolExBytes, + ImageAuxSymbolTokenDef, + ImageAuxSymbolFunction, + ImageAuxSymbolFunctionBeginEnd, + ImageAuxSymbolWeak, + ImageAuxSymbolSection, + ImageAuxSymbolCrc, + ImageRelocation, + ImageLinenumber, + ImageBaseRelocation, + ImageArchiveMemberHeader, + ImageExportDirectory, + ImageImportByName, + ImageThunkData64, + ImageThunkData32, + ImageTlsDirectory64, + ImageTlsDirectory32, + ImageImportDescriptor, + ImageBoundImportDescriptor, + ImageBoundForwarderRef, + ImageDelayloadDescriptor, + ImageResourceDirectory, + ImageResourceDirectoryEntry, + ImageResourceDirectoryString, + ImageResourceDirStringU, + ImageResourceDataEntry, + ImageLoadConfigCodeIntegrity, + ImageDynamicRelocationTable, + ImageDynamicRelocation32, + ImageDynamicRelocation64, + ImageDynamicRelocation32V2, + ImageDynamicRelocation64V2, + ImagePrologueDynamicRelocationHeader, + ImageEpilogueDynamicRelocationHeader, + //ImageImportControlTransferDynamicRelocation, + //ImageIndirControlTransferDynamicRelocation, + //ImageSwitchtableBranchDynamicRelocation, + ImageLoadConfigDirectory32, + ImageLoadConfigDirectory64, + ImageHotPatchInfo, + ImageHotPatchBase, + ImageHotPatchHashes, + //ImageCeRuntimeFunctionEntry, + ImageArmRuntimeFunctionEntry, + ImageArm64RuntimeFunctionEntry, + ImageAlpha64RuntimeFunctionEntry, + ImageAlphaRuntimeFunctionEntry, + ImageRuntimeFunctionEntry, + ImageEnclaveConfig32, + ImageEnclaveConfig64, + ImageEnclaveImport, + ImageDebugDirectory, + ImageCoffSymbolsHeader, + //FpoData, + ImageDebugMisc, + ImageFunctionEntry, + ImageFunctionEntry64, + ImageSeparateDebugHeader, + NonPagedDebugInfo, + //ImageArchitectureHeader, + ImageArchitectureEntry, + ImportObjectHeader, + ImageCor20Header, + MaskedRichHeaderEntry, +);
diff --git a/chromium_crates_io/vendor/object-v0_37/src/pod.rs b/chromium_crates_io/vendor/object-v0_37/src/pod.rs new file mode 100644 index 0000000..2907e1e --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/pod.rs
@@ -0,0 +1,281 @@ +//! Tools for converting file format structures to and from bytes. +//! +//! This module should be replaced once rust provides safe transmutes. + +// This module provides functions for both read and write features. +#![cfg_attr( + not(all(feature = "read_core", feature = "write_core")), + allow(dead_code) +)] + +use core::{mem, result, slice}; + +type Result<T> = result::Result<T, ()>; + +/// A trait for types that can safely be converted from and to byte slices. +/// +/// # Safety +/// A type that is `Pod` must: +/// - be `#[repr(C)]` or `#[repr(transparent)]` +/// - have no invalid byte values +/// - have no padding +pub unsafe trait Pod: Copy + 'static {} + +/// Cast the head of a byte slice to a `Pod` type. +/// +/// Returns the type and the tail of the byte slice. +/// +/// Returns an error if the byte slice is too short or the alignment is invalid. +#[inline] +pub fn from_bytes<T: Pod>(data: &[u8]) -> Result<(&T, &[u8])> { + let size = mem::size_of::<T>(); + let tail = data.get(size..).ok_or(())?; + let ptr = data.as_ptr(); + if (ptr as usize) % mem::align_of::<T>() != 0 { + return Err(()); + } + // Safety: + // The alignment and size are checked by this function. + // The Pod trait ensures the type is valid to cast from bytes. + let val = unsafe { &*ptr.cast() }; + Ok((val, tail)) +} + +/// Cast the head of a mutable byte slice to a `Pod` type. +/// +/// Returns the type and the tail of the byte slice. +/// +/// Returns an error if the byte slice is too short or the alignment is invalid. +#[inline] +pub fn from_bytes_mut<T: Pod>(data: &mut [u8]) -> Result<(&mut T, &mut [u8])> { + let size = mem::size_of::<T>(); + if size > data.len() { + return Err(()); + } + let (data, tail) = data.split_at_mut(size); + let ptr = data.as_mut_ptr(); + if (ptr as usize) % mem::align_of::<T>() != 0 { + return Err(()); + } + // Safety: + // The alignment and size are checked by this function. + // The Pod trait ensures the type is valid to cast from bytes. + let val = unsafe { &mut *ptr.cast() }; + Ok((val, tail)) +} + +/// Cast the head of a byte slice to a slice of a `Pod` type. +/// +/// Returns the type slice and the tail of the byte slice. +/// +/// Returns an error if the byte slice is too short or the alignment is invalid. +#[inline] +pub fn slice_from_bytes<T: Pod>(data: &[u8], count: usize) -> Result<(&[T], &[u8])> { + let size = count.checked_mul(mem::size_of::<T>()).ok_or(())?; + let tail = data.get(size..).ok_or(())?; + let ptr = data.as_ptr(); + if (ptr as usize) % mem::align_of::<T>() != 0 { + return Err(()); + } + // Safety: + // The alignment and size are checked by this function. + // The Pod trait ensures the type is valid to cast from bytes. + let slice = unsafe { slice::from_raw_parts(ptr.cast(), count) }; + Ok((slice, tail)) +} + +/// Cast the head of a mutable byte slice to a slice of a `Pod` type. +/// +/// Returns the type slice and the tail of the byte slice. +/// +/// Returns an error if the byte slice is too short or the alignment is invalid. +#[inline] +pub fn slice_from_bytes_mut<T: Pod>( + data: &mut [u8], + count: usize, +) -> Result<(&mut [T], &mut [u8])> { + let size = count.checked_mul(mem::size_of::<T>()).ok_or(())?; + if size > data.len() { + return Err(()); + } + let (data, tail) = data.split_at_mut(size); + let ptr = data.as_mut_ptr(); + if (ptr as usize) % mem::align_of::<T>() != 0 { + return Err(()); + } + // Safety: + // The alignment and size are checked by this function. + // The Pod trait ensures the type is valid to cast from bytes. + let slice = unsafe { slice::from_raw_parts_mut(ptr.cast(), count) }; + Ok((slice, tail)) +} + +/// Cast all of a byte slice to a slice of a `Pod` type. +/// +/// Returns the type slice. +/// +/// Returns an error if the size of the byte slice is not an exact multiple +/// of the type size, or the alignment is invalid. +#[inline] +pub fn slice_from_all_bytes<T: Pod>(data: &[u8]) -> Result<&[T]> { + let count = data.len() / mem::size_of::<T>(); + let (slice, tail) = slice_from_bytes(data, count)?; + if !tail.is_empty() { + return Err(()); + } + Ok(slice) +} + +/// Cast all of a byte slice to a slice of a `Pod` type. +/// +/// Returns the type slice. +/// +/// Returns an error if the size of the byte slice is not an exact multiple +/// of the type size, or the alignment is invalid. +#[inline] +pub fn slice_from_all_bytes_mut<T: Pod>(data: &mut [u8]) -> Result<&mut [T]> { + let count = data.len() / mem::size_of::<T>(); + let (slice, tail) = slice_from_bytes_mut(data, count)?; + if !tail.is_empty() { + return Err(()); + } + Ok(slice) +} + +/// Cast a `Pod` type to a byte slice. +#[inline] +pub fn bytes_of<T: Pod>(val: &T) -> &[u8] { + let size = mem::size_of::<T>(); + // Safety: + // Any alignment is allowed. + // The size is determined in this function. + // The Pod trait ensures the type is valid to cast to bytes. + unsafe { slice::from_raw_parts(slice::from_ref(val).as_ptr().cast(), size) } +} + +/// Cast a `Pod` type to a mutable byte slice. +#[inline] +pub fn bytes_of_mut<T: Pod>(val: &mut T) -> &mut [u8] { + let size = mem::size_of::<T>(); + // Safety: + // Any alignment is allowed. + // The size is determined in this function. + // The Pod trait ensures the type is valid to cast to bytes. + unsafe { slice::from_raw_parts_mut(slice::from_mut(val).as_mut_ptr().cast(), size) } +} + +/// Cast a slice of a `Pod` type to a byte slice. +#[inline] +pub fn bytes_of_slice<T: Pod>(val: &[T]) -> &[u8] { + let size = val.len().wrapping_mul(mem::size_of::<T>()); + // Safety: + // Any alignment is allowed. + // The size is determined in this function. + // The Pod trait ensures the type is valid to cast to bytes. + unsafe { slice::from_raw_parts(val.as_ptr().cast(), size) } +} + +/// Cast a slice of a `Pod` type to a mutable byte slice. +#[inline] +pub fn bytes_of_slice_mut<T: Pod>(val: &mut [T]) -> &mut [u8] { + let size = val.len().wrapping_mul(mem::size_of::<T>()); + // Safety: + // Any alignment is allowed. + // The size is determined in this function. + // The Pod trait ensures the type is valid to cast to bytes. + unsafe { slice::from_raw_parts_mut(val.as_mut_ptr().cast(), size) } +} + +macro_rules! unsafe_impl_pod { + ($($struct_name:ident),+ $(,)?) => { + $( + unsafe impl Pod for $struct_name { } + )+ + } +} + +unsafe_impl_pod!(u8, u16, u32, u64); + +unsafe impl<const N: usize, T: Pod> Pod for [T; N] {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn single() { + let x = u32::to_be(0x0123_4567); + let mut x_mut = x; + let bytes = bytes_of(&x); + let bytes_mut = bytes_of_mut(&mut x_mut); + assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67]); + assert_eq!(bytes, bytes_mut); + + let x16 = [u16::to_be(0x0123), u16::to_be(0x4567)]; + + let (y, tail) = from_bytes::<u32>(bytes).unwrap(); + let (y_mut, tail_mut) = from_bytes_mut::<u32>(bytes_mut).unwrap(); + assert_eq!(*y, x); + assert_eq!(y, y_mut); + assert_eq!(tail, &[]); + assert_eq!(tail, tail_mut); + + let (y, tail) = from_bytes::<u16>(bytes).unwrap(); + let (y_mut, tail_mut) = from_bytes_mut::<u16>(bytes_mut).unwrap(); + assert_eq!(*y, x16[0]); + assert_eq!(y, y_mut); + assert_eq!(tail, &bytes[2..]); + assert_eq!(tail, tail_mut); + + let (y, tail) = from_bytes::<u16>(&bytes[2..]).unwrap(); + let (y_mut, tail_mut) = from_bytes_mut::<u16>(&mut bytes_mut[2..]).unwrap(); + assert_eq!(*y, x16[1]); + assert_eq!(y, y_mut); + assert_eq!(tail, &[]); + assert_eq!(tail, tail_mut); + + assert_eq!(from_bytes::<u16>(&bytes[1..]), Err(())); + assert_eq!(from_bytes::<u16>(&bytes[3..]), Err(())); + assert_eq!(from_bytes::<u16>(&bytes[4..]), Err(())); + assert_eq!(from_bytes_mut::<u16>(&mut bytes_mut[1..]), Err(())); + assert_eq!(from_bytes_mut::<u16>(&mut bytes_mut[3..]), Err(())); + assert_eq!(from_bytes_mut::<u16>(&mut bytes_mut[4..]), Err(())); + } + + #[test] + fn slice() { + let x = [ + u16::to_be(0x0123), + u16::to_be(0x4567), + u16::to_be(0x89ab), + u16::to_be(0xcdef), + ]; + let mut x_mut = x; + + let bytes = bytes_of_slice(&x); + let bytes_mut = bytes_of_slice_mut(&mut x_mut); + assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); + assert_eq!(bytes, bytes_mut); + + let (y, tail) = slice_from_bytes::<u16>(bytes, 4).unwrap(); + let (y_mut, tail_mut) = slice_from_bytes_mut::<u16>(bytes_mut, 4).unwrap(); + assert_eq!(y, x); + assert_eq!(y, y_mut); + assert_eq!(tail, &[]); + assert_eq!(tail, tail_mut); + + let (y, tail) = slice_from_bytes::<u16>(&bytes[2..], 2).unwrap(); + let (y_mut, tail_mut) = slice_from_bytes_mut::<u16>(&mut bytes_mut[2..], 2).unwrap(); + assert_eq!(y, &x[1..3]); + assert_eq!(y, y_mut); + assert_eq!(tail, &bytes[6..]); + assert_eq!(tail, tail_mut); + + assert_eq!(slice_from_bytes::<u16>(bytes, 5), Err(())); + assert_eq!(slice_from_bytes::<u16>(&bytes[2..], 4), Err(())); + assert_eq!(slice_from_bytes::<u16>(&bytes[1..], 2), Err(())); + assert_eq!(slice_from_bytes_mut::<u16>(bytes_mut, 5), Err(())); + assert_eq!(slice_from_bytes_mut::<u16>(&mut bytes_mut[2..], 4), Err(())); + assert_eq!(slice_from_bytes_mut::<u16>(&mut bytes_mut[1..], 2), Err(())); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/any.rs b/chromium_crates_io/vendor/object-v0_37/src/read/any.rs new file mode 100644 index 0000000..d6408ad --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/any.rs
@@ -0,0 +1,1374 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::marker::PhantomData; + +#[allow(unused_imports)] // Unused for Wasm +use crate::endian::Endianness; +#[cfg(feature = "coff")] +use crate::read::coff; +#[cfg(feature = "elf")] +use crate::read::elf; +#[cfg(feature = "macho")] +use crate::read::macho; +#[cfg(feature = "pe")] +use crate::read::pe; +#[cfg(feature = "wasm")] +use crate::read::wasm; +#[cfg(feature = "xcoff")] +use crate::read::xcoff; +use crate::read::{ + self, Architecture, BinaryFormat, CodeView, ComdatKind, CompressedData, CompressedFileRange, + Error, Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectKind, ObjectMap, + ObjectSection, ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadRef, Relocation, + RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SubArchitecture, + SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, +}; + +/// Evaluate an expression on the contents of a file format enum. +/// +/// This is a hack to avoid virtual calls. +macro_rules! with_inner { + ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { + match $inner { + #[cfg(feature = "coff")] + $enum::Coff(ref $var) => $body, + #[cfg(feature = "coff")] + $enum::CoffBig(ref $var) => $body, + #[cfg(feature = "elf")] + $enum::Elf32(ref $var) => $body, + #[cfg(feature = "elf")] + $enum::Elf64(ref $var) => $body, + #[cfg(feature = "macho")] + $enum::MachO32(ref $var) => $body, + #[cfg(feature = "macho")] + $enum::MachO64(ref $var) => $body, + #[cfg(feature = "pe")] + $enum::Pe32(ref $var) => $body, + #[cfg(feature = "pe")] + $enum::Pe64(ref $var) => $body, + #[cfg(feature = "wasm")] + $enum::Wasm(ref $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff32(ref $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff64(ref $var) => $body, + } + }; +} + +macro_rules! with_inner_mut { + ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { + match $inner { + #[cfg(feature = "coff")] + $enum::Coff(ref mut $var) => $body, + #[cfg(feature = "coff")] + $enum::CoffBig(ref mut $var) => $body, + #[cfg(feature = "elf")] + $enum::Elf32(ref mut $var) => $body, + #[cfg(feature = "elf")] + $enum::Elf64(ref mut $var) => $body, + #[cfg(feature = "macho")] + $enum::MachO32(ref mut $var) => $body, + #[cfg(feature = "macho")] + $enum::MachO64(ref mut $var) => $body, + #[cfg(feature = "pe")] + $enum::Pe32(ref mut $var) => $body, + #[cfg(feature = "pe")] + $enum::Pe64(ref mut $var) => $body, + #[cfg(feature = "wasm")] + $enum::Wasm(ref mut $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff32(ref mut $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff64(ref mut $var) => $body, + } + }; +} + +/// Like `with_inner!`, but wraps the result in another enum. +macro_rules! map_inner { + ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { + match $inner { + #[cfg(feature = "coff")] + $from::Coff(ref $var) => $to::Coff($body), + #[cfg(feature = "coff")] + $from::CoffBig(ref $var) => $to::CoffBig($body), + #[cfg(feature = "elf")] + $from::Elf32(ref $var) => $to::Elf32($body), + #[cfg(feature = "elf")] + $from::Elf64(ref $var) => $to::Elf64($body), + #[cfg(feature = "macho")] + $from::MachO32(ref $var) => $to::MachO32($body), + #[cfg(feature = "macho")] + $from::MachO64(ref $var) => $to::MachO64($body), + #[cfg(feature = "pe")] + $from::Pe32(ref $var) => $to::Pe32($body), + #[cfg(feature = "pe")] + $from::Pe64(ref $var) => $to::Pe64($body), + #[cfg(feature = "wasm")] + $from::Wasm(ref $var) => $to::Wasm($body), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref $var) => $to::Xcoff32($body), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref $var) => $to::Xcoff64($body), + } + }; +} + +/// Like `map_inner!`, but the result is a Result or Option. +macro_rules! map_inner_option { + ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { + match $inner { + #[cfg(feature = "coff")] + $from::Coff(ref $var) => $body.map($to::Coff), + #[cfg(feature = "coff")] + $from::CoffBig(ref $var) => $body.map($to::CoffBig), + #[cfg(feature = "elf")] + $from::Elf32(ref $var) => $body.map($to::Elf32), + #[cfg(feature = "elf")] + $from::Elf64(ref $var) => $body.map($to::Elf64), + #[cfg(feature = "macho")] + $from::MachO32(ref $var) => $body.map($to::MachO32), + #[cfg(feature = "macho")] + $from::MachO64(ref $var) => $body.map($to::MachO64), + #[cfg(feature = "pe")] + $from::Pe32(ref $var) => $body.map($to::Pe32), + #[cfg(feature = "pe")] + $from::Pe64(ref $var) => $body.map($to::Pe64), + #[cfg(feature = "wasm")] + $from::Wasm(ref $var) => $body.map($to::Wasm), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref $var) => $body.map($to::Xcoff32), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref $var) => $body.map($to::Xcoff64), + } + }; +} + +macro_rules! map_inner_option_mut { + ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { + match $inner { + #[cfg(feature = "coff")] + $from::Coff(ref mut $var) => $body.map($to::Coff), + #[cfg(feature = "coff")] + $from::CoffBig(ref mut $var) => $body.map($to::CoffBig), + #[cfg(feature = "elf")] + $from::Elf32(ref mut $var) => $body.map($to::Elf32), + #[cfg(feature = "elf")] + $from::Elf64(ref mut $var) => $body.map($to::Elf64), + #[cfg(feature = "macho")] + $from::MachO32(ref mut $var) => $body.map($to::MachO32), + #[cfg(feature = "macho")] + $from::MachO64(ref mut $var) => $body.map($to::MachO64), + #[cfg(feature = "pe")] + $from::Pe32(ref mut $var) => $body.map($to::Pe32), + #[cfg(feature = "pe")] + $from::Pe64(ref mut $var) => $body.map($to::Pe64), + #[cfg(feature = "wasm")] + $from::Wasm(ref mut $var) => $body.map($to::Wasm), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref mut $var) => $body.map($to::Xcoff32), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref mut $var) => $body.map($to::Xcoff64), + } + }; +} + +/// Call `next` for a file format iterator. +macro_rules! next_inner { + ($inner:expr, $from:ident, $to:ident) => { + match $inner { + #[cfg(feature = "coff")] + $from::Coff(ref mut iter) => iter.next().map($to::Coff), + #[cfg(feature = "coff")] + $from::CoffBig(ref mut iter) => iter.next().map($to::CoffBig), + #[cfg(feature = "elf")] + $from::Elf32(ref mut iter) => iter.next().map($to::Elf32), + #[cfg(feature = "elf")] + $from::Elf64(ref mut iter) => iter.next().map($to::Elf64), + #[cfg(feature = "macho")] + $from::MachO32(ref mut iter) => iter.next().map($to::MachO32), + #[cfg(feature = "macho")] + $from::MachO64(ref mut iter) => iter.next().map($to::MachO64), + #[cfg(feature = "pe")] + $from::Pe32(ref mut iter) => iter.next().map($to::Pe32), + #[cfg(feature = "pe")] + $from::Pe64(ref mut iter) => iter.next().map($to::Pe64), + #[cfg(feature = "wasm")] + $from::Wasm(ref mut iter) => iter.next().map($to::Wasm), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref mut iter) => iter.next().map($to::Xcoff32), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref mut iter) => iter.next().map($to::Xcoff64), + } + }; +} + +/// An object file that can be any supported file format. +/// +/// Most functionality is provided by the [`Object`] trait implementation. +#[derive(Debug)] +#[non_exhaustive] +#[allow(missing_docs)] +pub enum File<'data, R: ReadRef<'data> = &'data [u8]> { + #[cfg(feature = "coff")] + Coff(coff::CoffFile<'data, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigFile<'data, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfFile32<'data, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfFile64<'data, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOFile32<'data, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOFile64<'data, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeFile32<'data, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeFile64<'data, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmFile<'data, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffFile32<'data, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffFile64<'data, R>), +} + +impl<'data, R: ReadRef<'data>> File<'data, R> { + /// Parse the raw file data. + pub fn parse(data: R) -> Result<Self> { + Ok(match FileKind::parse(data)? { + #[cfg(feature = "elf")] + FileKind::Elf32 => File::Elf32(elf::ElfFile32::parse(data)?), + #[cfg(feature = "elf")] + FileKind::Elf64 => File::Elf64(elf::ElfFile64::parse(data)?), + #[cfg(feature = "macho")] + FileKind::MachO32 => File::MachO32(macho::MachOFile32::parse(data)?), + #[cfg(feature = "macho")] + FileKind::MachO64 => File::MachO64(macho::MachOFile64::parse(data)?), + #[cfg(feature = "wasm")] + FileKind::Wasm => File::Wasm(wasm::WasmFile::parse(data)?), + #[cfg(feature = "pe")] + FileKind::Pe32 => File::Pe32(pe::PeFile32::parse(data)?), + #[cfg(feature = "pe")] + FileKind::Pe64 => File::Pe64(pe::PeFile64::parse(data)?), + #[cfg(feature = "coff")] + FileKind::Coff => File::Coff(coff::CoffFile::parse(data)?), + #[cfg(feature = "coff")] + FileKind::CoffBig => File::CoffBig(coff::CoffBigFile::parse(data)?), + #[cfg(feature = "xcoff")] + FileKind::Xcoff32 => File::Xcoff32(xcoff::XcoffFile32::parse(data)?), + #[cfg(feature = "xcoff")] + FileKind::Xcoff64 => File::Xcoff64(xcoff::XcoffFile64::parse(data)?), + #[allow(unreachable_patterns)] + _ => return Err(Error("Unsupported file format")), + }) + } + + /// Parse a Mach-O image from the dyld shared cache. + #[cfg(feature = "macho")] + pub fn parse_dyld_cache_image<'cache, E: crate::Endian>( + image: &macho::DyldCacheImage<'data, 'cache, E, R>, + ) -> Result<Self> { + Ok(match image.cache.architecture().address_size() { + Some(read::AddressSize::U64) => { + File::MachO64(macho::MachOFile64::parse_dyld_cache_image(image)?) + } + Some(read::AddressSize::U32) => { + File::MachO32(macho::MachOFile32::parse_dyld_cache_image(image)?) + } + _ => return Err(Error("Unsupported file format")), + }) + } + + /// Return the file format. + pub fn format(&self) -> BinaryFormat { + match self { + #[cfg(feature = "coff")] + File::Coff(_) | File::CoffBig(_) => BinaryFormat::Coff, + #[cfg(feature = "elf")] + File::Elf32(_) | File::Elf64(_) => BinaryFormat::Elf, + #[cfg(feature = "macho")] + File::MachO32(_) | File::MachO64(_) => BinaryFormat::MachO, + #[cfg(feature = "pe")] + File::Pe32(_) | File::Pe64(_) => BinaryFormat::Pe, + #[cfg(feature = "wasm")] + File::Wasm(_) => BinaryFormat::Wasm, + #[cfg(feature = "xcoff")] + File::Xcoff32(_) | File::Xcoff64(_) => BinaryFormat::Xcoff, + } + } +} + +impl<'data, R: ReadRef<'data>> read::private::Sealed for File<'data, R> {} + +impl<'data, R> Object<'data> for File<'data, R> +where + R: ReadRef<'data>, +{ + type Segment<'file> + = Segment<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SegmentIterator<'file> + = SegmentIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type Section<'file> + = Section<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SectionIterator<'file> + = SectionIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type Comdat<'file> + = Comdat<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type ComdatIterator<'file> + = ComdatIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type Symbol<'file> + = Symbol<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SymbolIterator<'file> + = SymbolIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SymbolTable<'file> + = SymbolTable<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type DynamicRelocationIterator<'file> + = DynamicRelocationIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + + fn architecture(&self) -> Architecture { + with_inner!(self, File, |x| x.architecture()) + } + + fn sub_architecture(&self) -> Option<SubArchitecture> { + with_inner!(self, File, |x| x.sub_architecture()) + } + + fn is_little_endian(&self) -> bool { + with_inner!(self, File, |x| x.is_little_endian()) + } + + fn is_64(&self) -> bool { + with_inner!(self, File, |x| x.is_64()) + } + + fn kind(&self) -> ObjectKind { + with_inner!(self, File, |x| x.kind()) + } + + fn segments(&self) -> SegmentIterator<'data, '_, R> { + SegmentIterator { + inner: map_inner!(self, File, SegmentIteratorInternal, |x| x.segments()), + } + } + + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<Section<'data, 'file, R>> { + map_inner_option!(self, File, SectionInternal, |x| x + .section_by_name_bytes(section_name)) + .map(|inner| Section { inner }) + } + + fn section_by_index(&self, index: SectionIndex) -> Result<Section<'data, '_, R>> { + map_inner_option!(self, File, SectionInternal, |x| x.section_by_index(index)) + .map(|inner| Section { inner }) + } + + fn sections(&self) -> SectionIterator<'data, '_, R> { + SectionIterator { + inner: map_inner!(self, File, SectionIteratorInternal, |x| x.sections()), + } + } + + fn comdats(&self) -> ComdatIterator<'data, '_, R> { + ComdatIterator { + inner: map_inner!(self, File, ComdatIteratorInternal, |x| x.comdats()), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Symbol<'data, '_, R>> { + map_inner_option!(self, File, SymbolInternal, |x| x + .symbol_by_index(index) + .map(|x| (x, PhantomData))) + .map(|inner| Symbol { inner }) + } + + fn symbols(&self) -> SymbolIterator<'data, '_, R> { + SymbolIterator { + inner: map_inner!(self, File, SymbolIteratorInternal, |x| ( + x.symbols(), + PhantomData + )), + } + } + + fn symbol_table(&self) -> Option<SymbolTable<'data, '_, R>> { + map_inner_option!(self, File, SymbolTableInternal, |x| x + .symbol_table() + .map(|x| (x, PhantomData))) + .map(|inner| SymbolTable { inner }) + } + + fn dynamic_symbols(&self) -> SymbolIterator<'data, '_, R> { + SymbolIterator { + inner: map_inner!(self, File, SymbolIteratorInternal, |x| ( + x.dynamic_symbols(), + PhantomData + )), + } + } + + fn dynamic_symbol_table(&self) -> Option<SymbolTable<'data, '_, R>> { + map_inner_option!(self, File, SymbolTableInternal, |x| x + .dynamic_symbol_table() + .map(|x| (x, PhantomData))) + .map(|inner| SymbolTable { inner }) + } + + #[cfg(feature = "elf")] + fn dynamic_relocations(&self) -> Option<DynamicRelocationIterator<'data, '_, R>> { + let inner = match self { + File::Elf32(ref elf) => { + DynamicRelocationIteratorInternal::Elf32(elf.dynamic_relocations()?) + } + File::Elf64(ref elf) => { + DynamicRelocationIteratorInternal::Elf64(elf.dynamic_relocations()?) + } + #[allow(unreachable_patterns)] + _ => return None, + }; + Some(DynamicRelocationIterator { inner }) + } + + #[cfg(not(feature = "elf"))] + fn dynamic_relocations(&self) -> Option<DynamicRelocationIterator<'data, '_, R>> { + None + } + + fn symbol_map(&self) -> SymbolMap<SymbolMapName<'data>> { + with_inner!(self, File, |x| x.symbol_map()) + } + + fn object_map(&self) -> ObjectMap<'data> { + with_inner!(self, File, |x| x.object_map()) + } + + fn imports(&self) -> Result<Vec<Import<'data>>> { + with_inner!(self, File, |x| x.imports()) + } + + fn exports(&self) -> Result<Vec<Export<'data>>> { + with_inner!(self, File, |x| x.exports()) + } + + fn has_debug_symbols(&self) -> bool { + with_inner!(self, File, |x| x.has_debug_symbols()) + } + + #[inline] + fn mach_uuid(&self) -> Result<Option<[u8; 16]>> { + with_inner!(self, File, |x| x.mach_uuid()) + } + + #[inline] + fn build_id(&self) -> Result<Option<&'data [u8]>> { + with_inner!(self, File, |x| x.build_id()) + } + + #[inline] + fn gnu_debuglink(&self) -> Result<Option<(&'data [u8], u32)>> { + with_inner!(self, File, |x| x.gnu_debuglink()) + } + + #[inline] + fn gnu_debugaltlink(&self) -> Result<Option<(&'data [u8], &'data [u8])>> { + with_inner!(self, File, |x| x.gnu_debugaltlink()) + } + + #[inline] + fn pdb_info(&self) -> Result<Option<CodeView<'_>>> { + with_inner!(self, File, |x| x.pdb_info()) + } + + fn relative_address_base(&self) -> u64 { + with_inner!(self, File, |x| x.relative_address_base()) + } + + fn entry(&self) -> u64 { + with_inner!(self, File, |x| x.entry()) + } + + fn flags(&self) -> FileFlags { + with_inner!(self, File, |x| x.flags()) + } +} + +/// An iterator for the loadable segments in a [`File`]. +#[derive(Debug)] +pub struct SegmentIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: SegmentIteratorInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum SegmentIteratorInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffSegmentIterator<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigSegmentIterator<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfSegmentIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfSegmentIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOSegmentIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOSegmentIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeSegmentIterator32<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeSegmentIterator64<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSegmentIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSegmentIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSegmentIterator64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for SegmentIterator<'data, 'file, R> { + type Item = Segment<'data, 'file, R>; + + fn next(&mut self) -> Option<Self::Item> { + next_inner!(self.inner, SegmentIteratorInternal, SegmentInternal) + .map(|inner| Segment { inner }) + } +} + +/// A loadable segment in a [`File`]. +/// +/// Most functionality is provided by the [`ObjectSegment`] trait implementation. +pub struct Segment<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: SegmentInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum SegmentInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffSegment<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigSegment<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfSegment32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfSegment64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOSegment32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOSegment64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeSegment32<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeSegment64<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSegment<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSegment32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSegment64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Segment<'data, 'file, R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's painful to do much better than this + let mut s = f.debug_struct("Segment"); + match self.name() { + Ok(Some(ref name)) => { + s.field("name", name); + } + Ok(None) => {} + Err(_) => { + s.field("name", &"<invalid>"); + } + } + s.field("address", &self.address()) + .field("size", &self.size()) + .finish() + } +} + +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Segment<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectSegment<'data> for Segment<'data, 'file, R> { + fn address(&self) -> u64 { + with_inner!(self.inner, SegmentInternal, |x| x.address()) + } + + fn size(&self) -> u64 { + with_inner!(self.inner, SegmentInternal, |x| x.size()) + } + + fn align(&self) -> u64 { + with_inner!(self.inner, SegmentInternal, |x| x.align()) + } + + fn file_range(&self) -> (u64, u64) { + with_inner!(self.inner, SegmentInternal, |x| x.file_range()) + } + + fn data(&self) -> Result<&'data [u8]> { + with_inner!(self.inner, SegmentInternal, |x| x.data()) + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + with_inner!(self.inner, SegmentInternal, |x| x.data_range(address, size)) + } + + fn name_bytes(&self) -> Result<Option<&[u8]>> { + with_inner!(self.inner, SegmentInternal, |x| x.name_bytes()) + } + + fn name(&self) -> Result<Option<&str>> { + with_inner!(self.inner, SegmentInternal, |x| x.name()) + } + + fn flags(&self) -> SegmentFlags { + with_inner!(self.inner, SegmentInternal, |x| x.flags()) + } +} + +/// An iterator for the sections in a [`File`]. +#[derive(Debug)] +pub struct SectionIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: SectionIteratorInternal<'data, 'file, R>, +} + +// we wrap our enums in a struct so that they are kept private. +#[derive(Debug)] +enum SectionIteratorInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffSectionIterator<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigSectionIterator<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfSectionIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfSectionIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOSectionIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOSectionIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeSectionIterator32<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeSectionIterator64<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSectionIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSectionIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSectionIterator64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for SectionIterator<'data, 'file, R> { + type Item = Section<'data, 'file, R>; + + fn next(&mut self) -> Option<Self::Item> { + next_inner!(self.inner, SectionIteratorInternal, SectionInternal) + .map(|inner| Section { inner }) + } +} + +/// A section in a [`File`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +pub struct Section<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: SectionInternal<'data, 'file, R>, +} + +enum SectionInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffSection<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigSection<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfSection32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfSection64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOSection32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOSection64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeSection32<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeSection64<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSection<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSection32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSection64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Section<'data, 'file, R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's painful to do much better than this + let mut s = f.debug_struct("Section"); + match self.segment_name() { + Ok(Some(ref name)) => { + s.field("segment", name); + } + Ok(None) => {} + Err(_) => { + s.field("segment", &"<invalid>"); + } + } + s.field("name", &self.name().unwrap_or("<invalid>")) + .field("address", &self.address()) + .field("size", &self.size()) + .field("align", &self.align()) + .field("kind", &self.kind()) + .field("flags", &self.flags()) + .finish() + } +} + +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Section<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for Section<'data, 'file, R> { + type RelocationIterator = SectionRelocationIterator<'data, 'file, R>; + + fn index(&self) -> SectionIndex { + with_inner!(self.inner, SectionInternal, |x| x.index()) + } + + fn address(&self) -> u64 { + with_inner!(self.inner, SectionInternal, |x| x.address()) + } + + fn size(&self) -> u64 { + with_inner!(self.inner, SectionInternal, |x| x.size()) + } + + fn align(&self) -> u64 { + with_inner!(self.inner, SectionInternal, |x| x.align()) + } + + fn file_range(&self) -> Option<(u64, u64)> { + with_inner!(self.inner, SectionInternal, |x| x.file_range()) + } + + fn data(&self) -> Result<&'data [u8]> { + with_inner!(self.inner, SectionInternal, |x| x.data()) + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + with_inner!(self.inner, SectionInternal, |x| x.data_range(address, size)) + } + + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + with_inner!(self.inner, SectionInternal, |x| x.compressed_file_range()) + } + + fn compressed_data(&self) -> Result<CompressedData<'data>> { + with_inner!(self.inner, SectionInternal, |x| x.compressed_data()) + } + + fn name_bytes(&self) -> Result<&'data [u8]> { + with_inner!(self.inner, SectionInternal, |x| x.name_bytes()) + } + + fn name(&self) -> Result<&'data str> { + with_inner!(self.inner, SectionInternal, |x| x.name()) + } + + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + with_inner!(self.inner, SectionInternal, |x| x.segment_name_bytes()) + } + + fn segment_name(&self) -> Result<Option<&str>> { + with_inner!(self.inner, SectionInternal, |x| x.segment_name()) + } + + fn kind(&self) -> SectionKind { + with_inner!(self.inner, SectionInternal, |x| x.kind()) + } + + fn relocations(&self) -> SectionRelocationIterator<'data, 'file, R> { + SectionRelocationIterator { + inner: map_inner!( + self.inner, + SectionInternal, + SectionRelocationIteratorInternal, + |x| x.relocations() + ), + } + } + + fn relocation_map(&self) -> Result<RelocationMap> { + with_inner!(self.inner, SectionInternal, |x| x.relocation_map()) + } + + fn flags(&self) -> SectionFlags { + with_inner!(self.inner, SectionInternal, |x| x.flags()) + } +} + +/// An iterator for the COMDAT section groups in a [`File`]. +#[derive(Debug)] +pub struct ComdatIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: ComdatIteratorInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum ComdatIteratorInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffComdatIterator<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigComdatIterator<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfComdatIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfComdatIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOComdatIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOComdatIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeComdatIterator32<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeComdatIterator64<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmComdatIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffComdatIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffComdatIterator64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for ComdatIterator<'data, 'file, R> { + type Item = Comdat<'data, 'file, R>; + + fn next(&mut self) -> Option<Self::Item> { + next_inner!(self.inner, ComdatIteratorInternal, ComdatInternal) + .map(|inner| Comdat { inner }) + } +} + +/// A COMDAT section group in a [`File`]. +/// +/// Most functionality is provided by the [`ObjectComdat`] trait implementation. +pub struct Comdat<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: ComdatInternal<'data, 'file, R>, +} + +enum ComdatInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffComdat<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigComdat<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfComdat32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfComdat64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOComdat32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOComdat64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeComdat32<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeComdat64<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmComdat<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffComdat32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffComdat64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Comdat<'data, 'file, R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("Comdat"); + s.field("symbol", &self.symbol()) + .field("name", &self.name().unwrap_or("<invalid>")) + .field("kind", &self.kind()) + .finish() + } +} + +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Comdat<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectComdat<'data> for Comdat<'data, 'file, R> { + type SectionIterator = ComdatSectionIterator<'data, 'file, R>; + + fn kind(&self) -> ComdatKind { + with_inner!(self.inner, ComdatInternal, |x| x.kind()) + } + + fn symbol(&self) -> SymbolIndex { + with_inner!(self.inner, ComdatInternal, |x| x.symbol()) + } + + fn name_bytes(&self) -> Result<&'data [u8]> { + with_inner!(self.inner, ComdatInternal, |x| x.name_bytes()) + } + + fn name(&self) -> Result<&'data str> { + with_inner!(self.inner, ComdatInternal, |x| x.name()) + } + + fn sections(&self) -> ComdatSectionIterator<'data, 'file, R> { + ComdatSectionIterator { + inner: map_inner!( + self.inner, + ComdatInternal, + ComdatSectionIteratorInternal, + |x| x.sections() + ), + } + } +} + +/// An iterator for the sections in a [`Comdat`]. +#[derive(Debug)] +pub struct ComdatSectionIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: ComdatSectionIteratorInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum ComdatSectionIteratorInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffComdatSectionIterator<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigComdatSectionIterator<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfComdatSectionIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfComdatSectionIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachOComdatSectionIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachOComdatSectionIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeComdatSectionIterator32<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeComdatSectionIterator64<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmComdatSectionIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffComdatSectionIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffComdatSectionIterator64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for ComdatSectionIterator<'data, 'file, R> { + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + with_inner_mut!(self.inner, ComdatSectionIteratorInternal, |x| x.next()) + } +} + +/// A symbol table in a [`File`]. +/// +/// Most functionality is provided by the [`ObjectSymbolTable`] trait implementation. +#[derive(Debug)] +pub struct SymbolTable<'data, 'file, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + inner: SymbolTableInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum SymbolTableInternal<'data, 'file, R> +where + R: ReadRef<'data>, +{ + #[cfg(feature = "coff")] + Coff((coff::CoffSymbolTable<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "coff")] + CoffBig((coff::CoffBigSymbolTable<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "elf")] + Elf32( + ( + elf::ElfSymbolTable32<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "elf")] + Elf64( + ( + elf::ElfSymbolTable64<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "macho")] + MachO32( + ( + macho::MachOSymbolTable32<'data, 'file, Endianness, R>, + PhantomData<()>, + ), + ), + #[cfg(feature = "macho")] + MachO64( + ( + macho::MachOSymbolTable64<'data, 'file, Endianness, R>, + PhantomData<()>, + ), + ), + #[cfg(feature = "pe")] + Pe32((coff::CoffSymbolTable<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "pe")] + Pe64((coff::CoffSymbolTable<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "wasm")] + Wasm((wasm::WasmSymbolTable<'data, 'file>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff32((xcoff::XcoffSymbolTable32<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff64((xcoff::XcoffSymbolTable64<'data, 'file, R>, PhantomData<R>)), +} + +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for SymbolTable<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectSymbolTable<'data> for SymbolTable<'data, 'file, R> { + type Symbol = Symbol<'data, 'file, R>; + type SymbolIterator = SymbolIterator<'data, 'file, R>; + + fn symbols(&self) -> Self::SymbolIterator { + SymbolIterator { + inner: map_inner!( + self.inner, + SymbolTableInternal, + SymbolIteratorInternal, + |x| (x.0.symbols(), PhantomData) + ), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { + map_inner_option!(self.inner, SymbolTableInternal, SymbolInternal, |x| x + .0 + .symbol_by_index(index) + .map(|x| (x, PhantomData))) + .map(|inner| Symbol { inner }) + } +} + +/// An iterator for the symbols in a [`SymbolTable`]. +#[derive(Debug)] +pub struct SymbolIterator<'data, 'file, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + inner: SymbolIteratorInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum SymbolIteratorInternal<'data, 'file, R> +where + R: ReadRef<'data>, +{ + #[cfg(feature = "coff")] + Coff((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "coff")] + CoffBig((coff::CoffBigSymbolIterator<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "elf")] + Elf32( + ( + elf::ElfSymbolIterator32<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "elf")] + Elf64( + ( + elf::ElfSymbolIterator64<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "macho")] + MachO32( + ( + macho::MachOSymbolIterator32<'data, 'file, Endianness, R>, + PhantomData<()>, + ), + ), + #[cfg(feature = "macho")] + MachO64( + ( + macho::MachOSymbolIterator64<'data, 'file, Endianness, R>, + PhantomData<()>, + ), + ), + #[cfg(feature = "pe")] + Pe32((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "pe")] + Pe64((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "wasm")] + Wasm((wasm::WasmSymbolIterator<'data, 'file>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff32( + ( + xcoff::XcoffSymbolIterator32<'data, 'file, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "xcoff")] + Xcoff64( + ( + xcoff::XcoffSymbolIterator64<'data, 'file, R>, + PhantomData<R>, + ), + ), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'file, R> { + type Item = Symbol<'data, 'file, R>; + + fn next(&mut self) -> Option<Self::Item> { + map_inner_option_mut!(self.inner, SymbolIteratorInternal, SymbolInternal, |iter| { + iter.0.next().map(|x| (x, PhantomData)) + }) + .map(|inner| Symbol { inner }) + } +} + +/// An symbol in a [`SymbolTable`]. +/// +/// Most functionality is provided by the [`ObjectSymbol`] trait implementation. +pub struct Symbol<'data, 'file, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + inner: SymbolInternal<'data, 'file, R>, +} + +enum SymbolInternal<'data, 'file, R> +where + R: ReadRef<'data>, +{ + #[cfg(feature = "coff")] + Coff((coff::CoffSymbol<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "coff")] + CoffBig((coff::CoffBigSymbol<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "elf")] + Elf32( + ( + elf::ElfSymbol32<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "elf")] + Elf64( + ( + elf::ElfSymbol64<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "macho")] + MachO32( + ( + macho::MachOSymbol32<'data, 'file, Endianness, R>, + PhantomData<()>, + ), + ), + #[cfg(feature = "macho")] + MachO64( + ( + macho::MachOSymbol64<'data, 'file, Endianness, R>, + PhantomData<()>, + ), + ), + #[cfg(feature = "pe")] + Pe32((coff::CoffSymbol<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "pe")] + Pe64((coff::CoffSymbol<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "wasm")] + Wasm((wasm::WasmSymbol<'data, 'file>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff32((xcoff::XcoffSymbol32<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff64((xcoff::XcoffSymbol64<'data, 'file, R>, PhantomData<R>)), +} + +impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Symbol<'data, 'file, R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Symbol") + .field("name", &self.name().unwrap_or("<invalid>")) + .field("address", &self.address()) + .field("size", &self.size()) + .field("kind", &self.kind()) + .field("section", &self.section()) + .field("scope", &self.scope()) + .field("weak", &self.is_weak()) + .field("flags", &self.flags()) + .finish() + } +} + +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Symbol<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectSymbol<'data> for Symbol<'data, 'file, R> { + fn index(&self) -> SymbolIndex { + with_inner!(self.inner, SymbolInternal, |x| x.0.index()) + } + + fn name_bytes(&self) -> Result<&'data [u8]> { + with_inner!(self.inner, SymbolInternal, |x| x.0.name_bytes()) + } + + fn name(&self) -> Result<&'data str> { + with_inner!(self.inner, SymbolInternal, |x| x.0.name()) + } + + fn address(&self) -> u64 { + with_inner!(self.inner, SymbolInternal, |x| x.0.address()) + } + + fn size(&self) -> u64 { + with_inner!(self.inner, SymbolInternal, |x| x.0.size()) + } + + fn kind(&self) -> SymbolKind { + with_inner!(self.inner, SymbolInternal, |x| x.0.kind()) + } + + fn section(&self) -> SymbolSection { + with_inner!(self.inner, SymbolInternal, |x| x.0.section()) + } + + fn is_undefined(&self) -> bool { + with_inner!(self.inner, SymbolInternal, |x| x.0.is_undefined()) + } + + fn is_definition(&self) -> bool { + with_inner!(self.inner, SymbolInternal, |x| x.0.is_definition()) + } + + fn is_common(&self) -> bool { + with_inner!(self.inner, SymbolInternal, |x| x.0.is_common()) + } + + fn is_weak(&self) -> bool { + with_inner!(self.inner, SymbolInternal, |x| x.0.is_weak()) + } + + fn scope(&self) -> SymbolScope { + with_inner!(self.inner, SymbolInternal, |x| x.0.scope()) + } + + fn is_global(&self) -> bool { + with_inner!(self.inner, SymbolInternal, |x| x.0.is_global()) + } + + fn is_local(&self) -> bool { + with_inner!(self.inner, SymbolInternal, |x| x.0.is_local()) + } + + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { + with_inner!(self.inner, SymbolInternal, |x| x.0.flags()) + } +} + +/// An iterator for the dynamic relocation entries in a [`File`]. +#[derive(Debug)] +pub struct DynamicRelocationIterator<'data, 'file, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + inner: DynamicRelocationIteratorInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum DynamicRelocationIteratorInternal<'data, 'file, R> +where + R: ReadRef<'data>, +{ + #[cfg(feature = "elf")] + Elf32(elf::ElfDynamicRelocationIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfDynamicRelocationIterator64<'data, 'file, Endianness, R>), + // We need to always use the lifetime parameters. + #[allow(unused)] + None(PhantomData<(&'data (), &'file (), R)>), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for DynamicRelocationIterator<'data, 'file, R> { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + match self.inner { + #[cfg(feature = "elf")] + DynamicRelocationIteratorInternal::Elf32(ref mut elf) => elf.next(), + #[cfg(feature = "elf")] + DynamicRelocationIteratorInternal::Elf64(ref mut elf) => elf.next(), + DynamicRelocationIteratorInternal::None(_) => None, + } + } +} + +/// An iterator for the relocation entries in a [`Section`]. +#[derive(Debug)] +pub struct SectionRelocationIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + inner: SectionRelocationIteratorInternal<'data, 'file, R>, +} + +#[derive(Debug)] +enum SectionRelocationIteratorInternal<'data, 'file, R: ReadRef<'data>> { + #[cfg(feature = "coff")] + Coff(coff::CoffRelocationIterator<'data, 'file, R>), + #[cfg(feature = "coff")] + CoffBig(coff::CoffBigRelocationIterator<'data, 'file, R>), + #[cfg(feature = "elf")] + Elf32(elf::ElfSectionRelocationIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "elf")] + Elf64(elf::ElfSectionRelocationIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO32(macho::MachORelocationIterator32<'data, 'file, Endianness, R>), + #[cfg(feature = "macho")] + MachO64(macho::MachORelocationIterator64<'data, 'file, Endianness, R>), + #[cfg(feature = "pe")] + Pe32(pe::PeRelocationIterator<'data, 'file, R>), + #[cfg(feature = "pe")] + Pe64(pe::PeRelocationIterator<'data, 'file, R>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmRelocationIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffRelocationIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffRelocationIterator64<'data, 'file, R>), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for SectionRelocationIterator<'data, 'file, R> { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + with_inner_mut!(self.inner, SectionRelocationIteratorInternal, |x| x.next()) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/archive.rs b/chromium_crates_io/vendor/object-v0_37/src/read/archive.rs new file mode 100644 index 0000000..9035d1a --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/archive.rs
@@ -0,0 +1,1147 @@ +//! Support for archive files. +//! +//! ## Example +//! ```no_run +//! use object::{Object, ObjectSection}; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads an archive and displays the name of each member. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(feature = "std")] { +//! let data = fs::read("path/to/binary")?; +//! let file = object::read::archive::ArchiveFile::parse(&*data)?; +//! for member in file.members() { +//! let member = member?; +//! println!("{}", String::from_utf8_lossy(member.name())); +//! } +//! # } +//! Ok(()) +//! } +//! ``` + +use core::convert::TryInto; +use core::slice; + +use crate::archive; +use crate::endian::{BigEndian as BE, LittleEndian as LE, U16Bytes, U32Bytes, U64Bytes}; +use crate::read::{self, Bytes, Error, ReadError, ReadRef}; + +/// The kind of archive format. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum ArchiveKind { + /// There are no special files that indicate the archive format. + Unknown, + /// The GNU (or System V) archive format. + Gnu, + /// The GNU (or System V) archive format with 64-bit symbol table. + Gnu64, + /// The BSD archive format. + Bsd, + /// The BSD archive format with 64-bit symbol table. + /// + /// This is used for Darwin. + Bsd64, + /// The Windows COFF archive format. + Coff, + /// The AIX big archive format. + AixBig, +} + +/// The list of members in the archive. +#[derive(Debug, Clone, Copy)] +enum Members<'data> { + Common { + offset: u64, + end_offset: u64, + }, + AixBig { + index: &'data [archive::AixMemberOffset], + }, +} + +/// A partially parsed archive file. +#[derive(Debug, Clone, Copy)] +pub struct ArchiveFile<'data, R: ReadRef<'data> = &'data [u8]> { + data: R, + kind: ArchiveKind, + members: Members<'data>, + symbols: (u64, u64), + names: &'data [u8], + thin: bool, +} + +impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { + /// Parse the archive header and special members. + pub fn parse(data: R) -> read::Result<Self> { + let len = data.len().read_error("Unknown archive length")?; + let mut tail = 0; + let magic = data + .read_bytes(&mut tail, archive::MAGIC.len() as u64) + .read_error("Invalid archive size")?; + + let thin = if magic == archive::AIX_BIG_MAGIC { + return Self::parse_aixbig(data); + } else if magic == archive::THIN_MAGIC { + true + } else if magic == archive::MAGIC { + false + } else { + return Err(Error("Unsupported archive identifier")); + }; + + let mut members_offset = tail; + let members_end_offset = len; + + let mut file = ArchiveFile { + data, + kind: ArchiveKind::Unknown, + members: Members::Common { + offset: 0, + end_offset: 0, + }, + symbols: (0, 0), + names: &[], + thin, + }; + + // The first few members may be special, so parse them. + // GNU has: + // - "/" or "/SYM64/": symbol table (optional) + // - "//": names table (optional) + // COFF has: + // - "/": first linker member + // - "/": second linker member + // - "//": names table + // BSD has: + // - "__.SYMDEF" or "__.SYMDEF SORTED": symbol table (optional) + // BSD 64-bit has: + // - "__.SYMDEF_64" or "__.SYMDEF_64 SORTED": symbol table (optional) + // BSD may use the extended name for the symbol table. This is handled + // by `ArchiveMember::parse`. + if tail < len { + let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; + if member.name == b"/" { + // GNU symbol table (unless we later determine this is COFF). + file.kind = ArchiveKind::Gnu; + file.symbols = member.file_range(); + members_offset = tail; + + if tail < len { + let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; + if member.name == b"/" { + // COFF linker member. + file.kind = ArchiveKind::Coff; + file.symbols = member.file_range(); + members_offset = tail; + + if tail < len { + let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; + if member.name == b"//" { + // COFF names table. + file.names = member.data(data)?; + members_offset = tail; + } + } + if tail < len { + let member = ArchiveMember::parse(data, &mut tail, file.names, thin)?; + if member.name == b"/<ECSYMBOLS>/" { + // COFF EC Symbol Table. + members_offset = tail; + } + } + } else if member.name == b"//" { + // GNU names table. + file.names = member.data(data)?; + members_offset = tail; + } + } + } else if member.name == b"/SYM64/" { + // GNU 64-bit symbol table. + file.kind = ArchiveKind::Gnu64; + file.symbols = member.file_range(); + members_offset = tail; + + if tail < len { + let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; + if member.name == b"//" { + // GNU names table. + file.names = member.data(data)?; + members_offset = tail; + } + } + } else if member.name == b"//" { + // GNU names table. + file.kind = ArchiveKind::Gnu; + file.names = member.data(data)?; + members_offset = tail; + } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" { + // BSD symbol table. + file.kind = ArchiveKind::Bsd; + file.symbols = member.file_range(); + members_offset = tail; + } else if member.name == b"__.SYMDEF_64" || member.name == b"__.SYMDEF_64 SORTED" { + // BSD 64-bit symbol table. + file.kind = ArchiveKind::Bsd64; + file.symbols = member.file_range(); + members_offset = tail; + } else { + // TODO: This could still be a BSD file. We leave this as unknown for now. + } + } + file.members = Members::Common { + offset: members_offset, + end_offset: members_end_offset, + }; + Ok(file) + } + + fn parse_aixbig(data: R) -> read::Result<Self> { + let mut tail = 0; + + let file_header = data + .read::<archive::AixFileHeader>(&mut tail) + .read_error("Invalid AIX big archive file header")?; + // Caller already validated this. + debug_assert_eq!(file_header.magic, archive::AIX_BIG_MAGIC); + + let mut file = ArchiveFile { + data, + kind: ArchiveKind::AixBig, + members: Members::AixBig { index: &[] }, + symbols: (0, 0), + names: &[], + thin: false, + }; + + // Read the span of symbol table. + // TODO: an archive may have both 32-bit and 64-bit symbol tables. + let symtbl64 = parse_u64_digits(&file_header.gst64off, 10) + .read_error("Invalid offset to 64-bit symbol table in AIX big archive")?; + if symtbl64 > 0 { + // The symbol table is also a file with header. + let member = ArchiveMember::parse_aixbig(data, symtbl64)?; + file.symbols = member.file_range(); + } else { + let symtbl = parse_u64_digits(&file_header.gstoff, 10) + .read_error("Invalid offset to symbol table in AIX big archive")?; + if symtbl > 0 { + // The symbol table is also a file with header. + let member = ArchiveMember::parse_aixbig(data, symtbl)?; + file.symbols = member.file_range(); + } + } + + // Big archive member index table lists file entries with offsets and names. + // To avoid potential infinite loop (members are double-linked list), the + // iterator goes through the index instead of real members. + let member_table_offset = parse_u64_digits(&file_header.memoff, 10) + .read_error("Invalid offset for member table of AIX big archive")?; + if member_table_offset == 0 { + // The offset would be zero if archive contains no file. + return Ok(file); + } + + // The member index table is also a file with header. + let member = ArchiveMember::parse_aixbig(data, member_table_offset)?; + let mut member_data = Bytes(member.data(data)?); + + // Structure of member index table: + // Number of entries (20 bytes) + // Offsets of each entry (20*N bytes) + // Names string table (the rest of bytes to fill size defined in header) + let members_count_bytes = member_data + .read_slice::<u8>(20) + .read_error("Missing member count in AIX big archive")?; + let members_count = parse_u64_digits(members_count_bytes, 10) + .and_then(|size| size.try_into().ok()) + .read_error("Invalid member count in AIX big archive")?; + let index = member_data + .read_slice::<archive::AixMemberOffset>(members_count) + .read_error("Member count overflow in AIX big archive")?; + file.members = Members::AixBig { index }; + + Ok(file) + } + + /// Return the archive format. + #[inline] + pub fn kind(&self) -> ArchiveKind { + self.kind + } + + /// Return true if the archive is a thin archive. + pub fn is_thin(&self) -> bool { + self.thin + } + + /// Iterate over the members of the archive. + /// + /// This does not return special members. + #[inline] + pub fn members(&self) -> ArchiveMemberIterator<'data, R> { + ArchiveMemberIterator { + data: self.data, + members: self.members, + names: self.names, + thin: self.thin, + } + } + + /// Return the member at the given offset. + pub fn member(&self, member: ArchiveOffset) -> read::Result<ArchiveMember<'data>> { + match self.members { + Members::Common { offset, end_offset } => { + if member.0 < offset || member.0 >= end_offset { + return Err(Error("Invalid archive member offset")); + } + let mut offset = member.0; + ArchiveMember::parse(self.data, &mut offset, self.names, self.thin) + } + Members::AixBig { .. } => { + let offset = member.0; + ArchiveMember::parse_aixbig(self.data, offset) + } + } + } + + /// Iterate over the symbols in the archive. + pub fn symbols(&self) -> read::Result<Option<ArchiveSymbolIterator<'data>>> { + if self.symbols == (0, 0) { + return Ok(None); + } + let (offset, size) = self.symbols; + ArchiveSymbolIterator::new(self.kind, self.data, offset, size) + .read_error("Invalid archive symbol table") + .map(Some) + } +} + +/// An iterator over the members of an archive. +#[derive(Debug)] +pub struct ArchiveMemberIterator<'data, R: ReadRef<'data> = &'data [u8]> { + data: R, + members: Members<'data>, + names: &'data [u8], + thin: bool, +} + +impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> { + type Item = read::Result<ArchiveMember<'data>>; + + fn next(&mut self) -> Option<Self::Item> { + match &mut self.members { + Members::Common { + ref mut offset, + ref mut end_offset, + } => { + if *offset >= *end_offset { + return None; + } + let member = ArchiveMember::parse(self.data, offset, self.names, self.thin); + if member.is_err() { + *offset = *end_offset; + } + Some(member) + } + Members::AixBig { ref mut index } => match **index { + [] => None, + [ref first, ref rest @ ..] => { + *index = rest; + let member = ArchiveMember::parse_aixbig_index(self.data, first); + if member.is_err() { + *index = &[]; + } + Some(member) + } + }, + } + } +} + +/// An archive member header. +#[derive(Debug, Clone, Copy)] +enum MemberHeader<'data> { + /// Common header used by many formats. + Common(&'data archive::Header), + /// AIX big archive header + AixBig(&'data archive::AixHeader), +} + +/// A partially parsed archive member. +#[derive(Debug)] +pub struct ArchiveMember<'data> { + header: MemberHeader<'data>, + name: &'data [u8], + // May be zero for thin members. + offset: u64, + size: u64, +} + +impl<'data> ArchiveMember<'data> { + /// Parse the member header, name, and file data in an archive with the common format. + /// + /// This reads the extended name (if any) and adjusts the file size. + fn parse<R: ReadRef<'data>>( + data: R, + offset: &mut u64, + names: &'data [u8], + thin: bool, + ) -> read::Result<Self> { + let header = data + .read::<archive::Header>(offset) + .read_error("Invalid archive member header")?; + if header.terminator != archive::TERMINATOR { + return Err(Error("Invalid archive terminator")); + } + + let header_file_size = + parse_u64_digits(&header.size, 10).read_error("Invalid archive member size")?; + let mut file_offset = *offset; + let mut file_size = header_file_size; + + let name = if header.name[0] == b'/' && (header.name[1] as char).is_ascii_digit() { + // Read file name from the names table. + parse_sysv_extended_name(&header.name[1..], names) + .read_error("Invalid archive extended name offset")? + } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_ascii_digit() { + // Read file name from the start of the file data. + parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size) + .read_error("Invalid archive extended name length")? + } else if header.name[0] == b'/' { + let name_len = memchr::memchr(b' ', &header.name).unwrap_or(header.name.len()); + &header.name[..name_len] + } else { + // Name is terminated by slash or space. + // Slash allows embedding spaces in the name, so only look + // for space if there is no slash. + let name_len = memchr::memchr(b'/', &header.name) + .or_else(|| memchr::memchr(b' ', &header.name)) + .unwrap_or(header.name.len()); + &header.name[..name_len] + }; + + // Members in thin archives don't have data unless they are special members. + if thin && name != b"/" && name != b"//" && name != b"/SYM64/" { + return Ok(ArchiveMember { + header: MemberHeader::Common(header), + name, + offset: 0, + size: file_size, + }); + } + + // Skip the file data. + *offset = offset + .checked_add(header_file_size) + .read_error("Archive member size is too large")?; + // Entries are padded to an even number of bytes. + if (header_file_size & 1) != 0 { + *offset = offset.saturating_add(1); + } + + Ok(ArchiveMember { + header: MemberHeader::Common(header), + name, + offset: file_offset, + size: file_size, + }) + } + + /// Parse a member index entry in an AIX big archive, + /// and then parse the member header, name, and file data. + fn parse_aixbig_index<R: ReadRef<'data>>( + data: R, + index: &archive::AixMemberOffset, + ) -> read::Result<Self> { + let offset = parse_u64_digits(&index.0, 10) + .read_error("Invalid AIX big archive file member offset")?; + Self::parse_aixbig(data, offset) + } + + /// Parse the member header, name, and file data in an AIX big archive. + fn parse_aixbig<R: ReadRef<'data>>(data: R, mut offset: u64) -> read::Result<Self> { + // The format was described at + // https://www.ibm.com/docs/en/aix/7.3?topic=formats-ar-file-format-big + let header = data + .read::<archive::AixHeader>(&mut offset) + .read_error("Invalid AIX big archive member header")?; + let name_length = parse_u64_digits(&header.namlen, 10) + .read_error("Invalid AIX big archive member name length")?; + let name = data + .read_bytes(&mut offset, name_length) + .read_error("Invalid AIX big archive member name")?; + + // The actual data for a file member begins at the first even-byte boundary beyond the + // member header and continues for the number of bytes specified by the ar_size field. The + // ar command inserts null bytes for padding where necessary. + if offset & 1 != 0 { + offset = offset.saturating_add(1); + } + // Because of the even-byte boundary, we have to read and check terminator after header. + let terminator = data + .read_bytes(&mut offset, 2) + .read_error("Invalid AIX big archive terminator")?; + if terminator != archive::TERMINATOR { + return Err(Error("Invalid AIX big archive terminator")); + } + + let size = parse_u64_digits(&header.size, 10) + .read_error("Invalid archive member size in AIX big archive")?; + Ok(ArchiveMember { + header: MemberHeader::AixBig(header), + name, + offset, + size, + }) + } + + /// Return the raw header that is common to many archive formats. + /// + /// Returns `None` if this archive does not use the common header format. + #[inline] + pub fn header(&self) -> Option<&'data archive::Header> { + match self.header { + MemberHeader::Common(header) => Some(header), + _ => None, + } + } + + /// Return the raw header for AIX big archives. + /// + /// Returns `None` if this is not an AIX big archive. + #[inline] + pub fn aix_header(&self) -> Option<&'data archive::AixHeader> { + match self.header { + MemberHeader::AixBig(header) => Some(header), + _ => None, + } + } + + /// Return the parsed file name. + /// + /// This may be an extended file name. + #[inline] + pub fn name(&self) -> &'data [u8] { + self.name + } + + /// Parse the file modification timestamp from the header. + #[inline] + pub fn date(&self) -> Option<u64> { + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.date, 10), + MemberHeader::AixBig(header) => parse_u64_digits(&header.date, 10), + } + } + + /// Parse the user ID from the header. + #[inline] + pub fn uid(&self) -> Option<u64> { + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.uid, 10), + MemberHeader::AixBig(header) => parse_u64_digits(&header.uid, 10), + } + } + + /// Parse the group ID from the header. + #[inline] + pub fn gid(&self) -> Option<u64> { + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.gid, 10), + MemberHeader::AixBig(header) => parse_u64_digits(&header.gid, 10), + } + } + + /// Parse the file mode from the header. + #[inline] + pub fn mode(&self) -> Option<u64> { + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8), + MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8), + } + } + + /// Return the size of the file data. + pub fn size(&self) -> u64 { + self.size + } + + /// Return the offset and size of the file data. + pub fn file_range(&self) -> (u64, u64) { + (self.offset, self.size) + } + + /// Return true if the member is a thin member. + /// + /// Thin members have no file data. + pub fn is_thin(&self) -> bool { + self.offset == 0 + } + + /// Return the file data. + /// + /// This is an empty slice for thin members. + #[inline] + pub fn data<R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [u8]> { + if self.is_thin() { + return Ok(&[]); + } + data.read_bytes_at(self.offset, self.size) + .read_error("Archive member size is too large") + } +} + +/// An offset of a member in an archive. +#[derive(Debug, Clone, Copy)] +pub struct ArchiveOffset(pub u64); + +/// An iterator over the symbols in the archive symbol table. +#[derive(Debug, Clone)] +pub struct ArchiveSymbolIterator<'data>(SymbolIteratorInternal<'data>); + +#[derive(Debug, Clone)] +enum SymbolIteratorInternal<'data> { + /// There is no symbol table. + None, + /// A GNU symbol table. + /// + /// Contains: + /// - the number of symbols as a 32-bit big-endian integer + /// - the offsets of the member headers as 32-bit big-endian integers + /// - the symbol names as null-terminated strings + Gnu { + offsets: slice::Iter<'data, U32Bytes<BE>>, + names: Bytes<'data>, + }, + /// A GNU 64-bit symbol table + /// + /// Contains: + /// - the number of symbols as a 64-bit big-endian integer + /// - the offsets of the member headers as 64-bit big-endian integers + /// - the symbol names as null-terminated strings + Gnu64 { + offsets: slice::Iter<'data, U64Bytes<BE>>, + names: Bytes<'data>, + }, + /// A BSD symbol table. + /// + /// Contains: + /// - the size in bytes of the offsets array as a 32-bit little-endian integer + /// - the offsets array, for which each entry is a pair of 32-bit little-endian integers + /// for the offset of the member header and the offset of the symbol name + /// - the size in bytes of the symbol names as a 32-bit little-endian integer + /// - the symbol names as null-terminated strings + Bsd { + offsets: slice::Iter<'data, [U32Bytes<LE>; 2]>, + names: Bytes<'data>, + }, + /// A BSD 64-bit symbol table. + /// + /// Contains: + /// - the size in bytes of the offsets array as a 64-bit little-endian integer + /// - the offsets array, for which each entry is a pair of 64-bit little-endian integers + /// for the offset of the member header and the offset of the symbol name + /// - the size in bytes of the symbol names as a 64-bit little-endian integer + /// - the symbol names as null-terminated strings + Bsd64 { + offsets: slice::Iter<'data, [U64Bytes<LE>; 2]>, + names: Bytes<'data>, + }, + /// A Windows COFF symbol table. + /// + /// Contains: + /// - the number of members as a 32-bit little-endian integer + /// - the offsets of the member headers as 32-bit little-endian integers + /// - the number of symbols as a 32-bit little-endian integer + /// - the member index for each symbol as a 16-bit little-endian integer + /// - the symbol names as null-terminated strings in lexical order + Coff { + members: &'data [U32Bytes<LE>], + indices: slice::Iter<'data, U16Bytes<LE>>, + names: Bytes<'data>, + }, +} + +impl<'data> ArchiveSymbolIterator<'data> { + fn new<R: ReadRef<'data>>( + kind: ArchiveKind, + data: R, + offset: u64, + size: u64, + ) -> Result<Self, ()> { + let mut data = data.read_bytes_at(offset, size).map(Bytes)?; + match kind { + ArchiveKind::Unknown => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)), + ArchiveKind::Gnu => { + let offsets_count = data.read::<U32Bytes<BE>>()?.get(BE); + let offsets = data.read_slice::<U32Bytes<BE>>(offsets_count as usize)?; + Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu { + offsets: offsets.iter(), + names: data, + })) + } + ArchiveKind::Gnu64 => { + let offsets_count = data.read::<U64Bytes<BE>>()?.get(BE); + let offsets = data.read_slice::<U64Bytes<BE>>(offsets_count as usize)?; + Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu64 { + offsets: offsets.iter(), + names: data, + })) + } + ArchiveKind::Bsd => { + let offsets_size = data.read::<U32Bytes<LE>>()?.get(LE); + let offsets = data.read_slice::<[U32Bytes<LE>; 2]>(offsets_size as usize / 8)?; + let names_size = data.read::<U32Bytes<LE>>()?.get(LE); + let names = data.read_bytes(names_size as usize)?; + Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd { + offsets: offsets.iter(), + names, + })) + } + ArchiveKind::Bsd64 => { + let offsets_size = data.read::<U64Bytes<LE>>()?.get(LE); + let offsets = data.read_slice::<[U64Bytes<LE>; 2]>(offsets_size as usize / 16)?; + let names_size = data.read::<U64Bytes<LE>>()?.get(LE); + let names = data.read_bytes(names_size as usize)?; + Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd64 { + offsets: offsets.iter(), + names, + })) + } + ArchiveKind::Coff => { + let members_count = data.read::<U32Bytes<LE>>()?.get(LE); + let members = data.read_slice::<U32Bytes<LE>>(members_count as usize)?; + let indices_count = data.read::<U32Bytes<LE>>()?.get(LE); + let indices = data.read_slice::<U16Bytes<LE>>(indices_count as usize)?; + Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Coff { + members, + indices: indices.iter(), + names: data, + })) + } + // TODO: Implement AIX big archive symbol table. + ArchiveKind::AixBig => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)), + } + } +} + +impl<'data> Iterator for ArchiveSymbolIterator<'data> { + type Item = read::Result<ArchiveSymbol<'data>>; + + fn next(&mut self) -> Option<Self::Item> { + match &mut self.0 { + SymbolIteratorInternal::None => None, + SymbolIteratorInternal::Gnu { offsets, names } => { + let offset = offsets.next()?.get(BE); + Some( + names + .read_string() + .read_error("Missing archive symbol name") + .map(|name| ArchiveSymbol { + name, + offset: ArchiveOffset(offset.into()), + }), + ) + } + SymbolIteratorInternal::Gnu64 { offsets, names } => { + let offset = offsets.next()?.get(BE); + Some( + names + .read_string() + .read_error("Missing archive symbol name") + .map(|name| ArchiveSymbol { + name, + offset: ArchiveOffset(offset), + }), + ) + } + SymbolIteratorInternal::Bsd { offsets, names } => { + let entry = offsets.next()?; + Some( + names + .read_string_at(entry[0].get(LE) as usize) + .read_error("Invalid archive symbol name offset") + .map(|name| ArchiveSymbol { + name, + offset: ArchiveOffset(entry[1].get(LE).into()), + }), + ) + } + SymbolIteratorInternal::Bsd64 { offsets, names } => { + let entry = offsets.next()?; + Some( + names + .read_string_at(entry[0].get(LE) as usize) + .read_error("Invalid archive symbol name offset") + .map(|name| ArchiveSymbol { + name, + offset: ArchiveOffset(entry[1].get(LE)), + }), + ) + } + SymbolIteratorInternal::Coff { + members, + indices, + names, + } => { + let index = indices.next()?.get(LE).wrapping_sub(1); + let member = members + .get(index as usize) + .read_error("Invalid archive symbol member index"); + let name = names + .read_string() + .read_error("Missing archive symbol name"); + Some(member.and_then(|member| { + name.map(|name| ArchiveSymbol { + name, + offset: ArchiveOffset(member.get(LE).into()), + }) + })) + } + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + match &self.0 { + SymbolIteratorInternal::None => (0, None), + SymbolIteratorInternal::Gnu { offsets, .. } => offsets.size_hint(), + SymbolIteratorInternal::Gnu64 { offsets, .. } => offsets.size_hint(), + SymbolIteratorInternal::Bsd { offsets, .. } => offsets.size_hint(), + SymbolIteratorInternal::Bsd64 { offsets, .. } => offsets.size_hint(), + SymbolIteratorInternal::Coff { indices, .. } => { + // The `slice::Iter` is in the indices field for this variant + indices.size_hint() + } + } + } +} + +/// A symbol in the archive symbol table. +/// +/// This is used to find the member containing the symbol. +#[derive(Debug, Clone, Copy)] +pub struct ArchiveSymbol<'data> { + name: &'data [u8], + offset: ArchiveOffset, +} + +impl<'data> ArchiveSymbol<'data> { + /// Return the symbol name. + #[inline] + pub fn name(&self) -> &'data [u8] { + self.name + } + + /// Return the offset of the header for the member containing the symbol. + #[inline] + pub fn offset(&self) -> ArchiveOffset { + self.offset + } +} + +// Ignores bytes starting from the first space. +fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> { + if let [b' ', ..] = digits { + return None; + } + let mut result: u64 = 0; + for &c in digits { + if c == b' ' { + return Some(result); + } else { + let x = (c as char).to_digit(radix)?; + result = result + .checked_mul(u64::from(radix))? + .checked_add(u64::from(x))?; + } + } + Some(result) +} + +/// Digits are a decimal offset into the extended name table. +/// Name is terminated by "/\n" (for GNU) or a null byte (for COFF). +fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<&'data [u8], ()> { + let offset = parse_u64_digits(digits, 10).ok_or(())?; + let offset = offset.try_into().map_err(|_| ())?; + let name_data = names.get(offset..).ok_or(())?; + let len = memchr::memchr2(b'\n', b'\0', name_data).ok_or(())?; + if name_data[len] == b'\n' { + if len < 1 || name_data[len - 1] != b'/' { + Err(()) + } else { + Ok(&name_data[..len - 1]) + } + } else { + Ok(&name_data[..len]) + } +} + +/// Digits are a decimal length of the extended name, which is contained +/// in `data` at `offset`. +/// Modifies `offset` and `size` to start after the extended name. +fn parse_bsd_extended_name<'data, R: ReadRef<'data>>( + digits: &[u8], + data: R, + offset: &mut u64, + size: &mut u64, +) -> Result<&'data [u8], ()> { + let len = parse_u64_digits(digits, 10).ok_or(())?; + *size = size.checked_sub(len).ok_or(())?; + let name_data = data.read_bytes(offset, len)?; + let name = match memchr::memchr(b'\0', name_data) { + Some(len) => &name_data[..len], + None => name_data, + }; + Ok(name) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn kind() { + let data = b"!<arch>\n"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Unknown); + + let data = b"\ + !<arch>\n\ + / 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + + let data = b"\ + !<arch>\n\ + // 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + + let data = b"\ + !<arch>\n\ + / 4 `\n\ + 0000\ + // 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + + let data = b"\ + !<arch>\n\ + /SYM64/ 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu64); + + let data = b"\ + !<arch>\n\ + /SYM64/ 4 `\n\ + 0000\ + // 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu64); + + let data = b"\ + !<arch>\n\ + __.SYMDEF 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd); + + let data = b"\ + !<arch>\n\ + #1/9 13 `\n\ + __.SYMDEF0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd); + + let data = b"\ + !<arch>\n\ + #1/16 20 `\n\ + __.SYMDEF SORTED0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd); + + let data = b"\ + !<arch>\n\ + __.SYMDEF_64 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd64); + + let data = b"\ + !<arch>\n\ + #1/12 16 `\n\ + __.SYMDEF_640000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd64); + + let data = b"\ + !<arch>\n\ + #1/19 23 `\n\ + __.SYMDEF_64 SORTED0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd64); + + let data = b"\ + !<arch>\n\ + / 4 `\n\ + 0000\ + / 4 `\n\ + 0000\ + // 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Coff); + + let data = b"\ + <bigaf>\n\ + 0 0 \ + 0 0 \ + 0 128 \ + 6 0 \ + 0 \0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::AixBig); + + let data = b"\ + !<thin>\n\ + / 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + assert!(archive.is_thin()); + } + + #[test] + fn gnu_names() { + let data = b"\ + !<arch>\n\ + // 18 `\n\ + 0123456789abcdef/\n\ + s p a c e/ 0 0 0 644 4 `\n\ + 0000\ + 0123456789abcde/0 0 0 644 3 `\n\ + odd\n\ + /0 0 0 0 644 4 `\n\ + even"; + let data = &data[..]; + let archive = ArchiveFile::parse(data).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + let mut members = archive.members(); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"s p a c e"); + assert_eq!(member.data(data).unwrap(), &b"0000"[..]); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789abcde"); + assert_eq!(member.data(data).unwrap(), &b"odd"[..]); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789abcdef"); + assert_eq!(member.data(data).unwrap(), &b"even"[..]); + + assert!(members.next().is_none()); + } + + #[test] + fn thin_gnu_names() { + let data = b"\ + !<thin>\n\ + // 18 `\n\ + 0123456789/abcde/\n\ + s p a c e/ 0 0 0 644 4 `\n\ + 0123456789abcde/0 0 0 644 3 `\n\ + /0 0 0 0 644 4 `\n\ + "; + let data = &data[..]; + let archive = ArchiveFile::parse(data).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + let mut members = archive.members(); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"s p a c e"); + assert!(member.is_thin()); + assert_eq!(member.size(), 4); + assert_eq!(member.data(data).unwrap(), &[]); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789abcde"); + assert!(member.is_thin()); + assert_eq!(member.size(), 3); + assert_eq!(member.data(data).unwrap(), &[]); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789/abcde"); + assert!(member.is_thin()); + assert_eq!(member.size(), 4); + assert_eq!(member.data(data).unwrap(), &[]); + + assert!(members.next().is_none()); + } + + #[test] + fn bsd_names() { + let data = b"\ + !<arch>\n\ + 0123456789abcde 0 0 0 644 3 `\n\ + odd\n\ + #1/16 0 0 0 644 20 `\n\ + 0123456789abcdefeven"; + let data = &data[..]; + let archive = ArchiveFile::parse(data).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Unknown); + let mut members = archive.members(); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789abcde"); + assert_eq!(member.data(data).unwrap(), &b"odd"[..]); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789abcdef"); + assert_eq!(member.data(data).unwrap(), &b"even"[..]); + + assert!(members.next().is_none()); + } + + #[test] + fn aix_names() { + let data = b"\ + <bigaf>\n\ + 396 0 0 \ + 128 262 0 \ + 4 262 0 \ + 1662610370 223 1 644 16 \ + 0123456789abcdef`\nord\n\ + 4 396 128 \ + 1662610374 223 1 644 16 \ + fedcba9876543210`\nrev\n\ + 94 0 262 \ + 0 0 0 0 0 \ + `\n2 128 \ + 262 0123456789abcdef\0fedcba9876543210\0"; + let data = &data[..]; + let archive = ArchiveFile::parse(data).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::AixBig); + let mut members = archive.members(); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789abcdef"); + assert_eq!(member.data(data).unwrap(), &b"ord\n"[..]); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"fedcba9876543210"); + assert_eq!(member.data(data).unwrap(), &b"rev\n"[..]); + + assert!(members.next().is_none()); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/coff/comdat.rs b/chromium_crates_io/vendor/object-v0_37/src/read/coff/comdat.rs new file mode 100644 index 0000000..464d238 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/coff/comdat.rs
@@ -0,0 +1,220 @@ +use core::str; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::read::{ + self, ComdatKind, ObjectComdat, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, +}; + +use super::{CoffFile, CoffHeader, ImageSymbol}; + +/// An iterator for the COMDAT section groups in a [`CoffBigFile`](super::CoffBigFile). +pub type CoffBigComdatIterator<'data, 'file, R = &'data [u8]> = + CoffComdatIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator for the COMDAT section groups in a [`CoffFile`]. +#[derive(Debug)] +pub struct CoffComdatIterator< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + file: &'file CoffFile<'data, R, Coff>, + index: SymbolIndex, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffComdatIterator<'data, 'file, R, Coff> { + pub(crate) fn new(file: &'file CoffFile<'data, R, Coff>) -> Self { + CoffComdatIterator { + file, + index: SymbolIndex(0), + } + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for CoffComdatIterator<'data, 'file, R, Coff> +{ + type Item = CoffComdat<'data, 'file, R, Coff>; + + fn next(&mut self) -> Option<Self::Item> { + loop { + let index = self.index; + let symbol = self.file.common.symbols.symbol(index).ok()?; + self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; + if let Some(comdat) = CoffComdat::parse(self.file, symbol, index) { + return Some(comdat); + } + } + } +} + +/// A COMDAT section group in a [`CoffBigFile`](super::CoffBigFile). +/// +/// Most functionality is provided by the [`ObjectComdat`] trait implementation. +pub type CoffBigComdat<'data, 'file, R = &'data [u8]> = + CoffComdat<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A COMDAT section group in a [`CoffFile`]. +/// +/// Most functionality is provided by the [`ObjectComdat`] trait implementation. +#[derive(Debug)] +pub struct CoffComdat< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + file: &'file CoffFile<'data, R, Coff>, + symbol_index: SymbolIndex, + symbol: &'data Coff::ImageSymbol, + selection: u8, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffComdat<'data, 'file, R, Coff> { + fn parse( + file: &'file CoffFile<'data, R, Coff>, + section_symbol: &'data Coff::ImageSymbol, + index: SymbolIndex, + ) -> Option<CoffComdat<'data, 'file, R, Coff>> { + // Must be a section symbol. + if !section_symbol.has_aux_section() { + return None; + } + + // Auxiliary record must have a non-associative selection. + let aux = file.common.symbols.aux_section(index).ok()?; + let selection = aux.selection; + if selection == 0 || selection == pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE { + return None; + } + + // Find the COMDAT symbol. + let mut symbol_index = index; + let mut symbol = section_symbol; + let section_number = section_symbol.section_number(); + loop { + symbol_index.0 += 1 + symbol.number_of_aux_symbols() as usize; + symbol = file.common.symbols.symbol(symbol_index).ok()?; + if section_number == symbol.section_number() { + break; + } + } + + Some(CoffComdat { + file, + symbol_index, + symbol, + selection, + }) + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed + for CoffComdat<'data, 'file, R, Coff> +{ +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectComdat<'data> + for CoffComdat<'data, 'file, R, Coff> +{ + type SectionIterator = CoffComdatSectionIterator<'data, 'file, R, Coff>; + + #[inline] + fn kind(&self) -> ComdatKind { + match self.selection { + pe::IMAGE_COMDAT_SELECT_NODUPLICATES => ComdatKind::NoDuplicates, + pe::IMAGE_COMDAT_SELECT_ANY => ComdatKind::Any, + pe::IMAGE_COMDAT_SELECT_SAME_SIZE => ComdatKind::SameSize, + pe::IMAGE_COMDAT_SELECT_EXACT_MATCH => ComdatKind::ExactMatch, + pe::IMAGE_COMDAT_SELECT_LARGEST => ComdatKind::Largest, + pe::IMAGE_COMDAT_SELECT_NEWEST => ComdatKind::Newest, + _ => ComdatKind::Unknown, + } + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + self.symbol_index + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + // Find the name of first symbol referring to the section. + self.symbol.name(self.file.common.symbols.strings()) + } + + #[inline] + fn name(&self) -> Result<&'data str> { + let bytes = self.name_bytes()?; + str::from_utf8(bytes) + .ok() + .read_error("Non UTF-8 COFF COMDAT name") + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + CoffComdatSectionIterator { + file: self.file, + section_number: self.symbol.section_number(), + index: SymbolIndex(0), + } + } +} + +/// An iterator for the sections in a COMDAT section group in a [`CoffBigFile`](super::CoffBigFile). +pub type CoffBigComdatSectionIterator<'data, 'file, R = &'data [u8]> = + CoffComdatSectionIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator for the sections in a COMDAT section group in a [`CoffFile`]. +#[derive(Debug)] +pub struct CoffComdatSectionIterator< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + file: &'file CoffFile<'data, R, Coff>, + section_number: i32, + index: SymbolIndex, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for CoffComdatSectionIterator<'data, 'file, R, Coff> +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + // Find associated COMDAT symbols. + // TODO: it seems gcc doesn't use associated symbols for this + loop { + let index = self.index; + let symbol = self.file.common.symbols.symbol(index).ok()?; + self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; + + // Must be a section symbol. + if !symbol.has_aux_section() { + continue; + } + + let section_number = symbol.section_number(); + + let aux = self.file.common.symbols.aux_section(index).ok()?; + if aux.selection == pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE { + let number = if Coff::is_type_bigobj() { + u32::from(aux.number.get(LE)) | (u32::from(aux.high_number.get(LE)) << 16) + } else { + u32::from(aux.number.get(LE)) + }; + if number as i32 == self.section_number { + return Some(SectionIndex(section_number as usize)); + } + } else if aux.selection != 0 { + if section_number == self.section_number { + return Some(SectionIndex(section_number as usize)); + } + } + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/coff/file.rs b/chromium_crates_io/vendor/object-v0_37/src/read/coff/file.rs new file mode 100644 index 0000000..3d766f9 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/coff/file.rs
@@ -0,0 +1,427 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::pod::Pod; +use crate::read::{ + self, Architecture, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind, + ObjectSection, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex, +}; + +use super::{ + CoffComdat, CoffComdatIterator, CoffSection, CoffSectionIterator, CoffSegment, + CoffSegmentIterator, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, ImageSymbol, + SectionTable, SymbolTable, +}; + +/// The common parts of `PeFile` and `CoffFile`. +#[derive(Debug)] +pub(crate) struct CoffCommon<'data, R: ReadRef<'data>, Coff: CoffHeader = pe::ImageFileHeader> { + pub(crate) sections: SectionTable<'data>, + pub(crate) symbols: SymbolTable<'data, R, Coff>, + pub(crate) image_base: u64, +} + +/// A COFF bigobj object file with 32-bit section numbers. +/// +/// This is a file that starts with [`pe::AnonObjectHeaderBigobj`], and corresponds +/// to [`crate::FileKind::CoffBig`]. +/// +/// Most functionality is provided by the [`Object`] trait implementation. +pub type CoffBigFile<'data, R = &'data [u8]> = CoffFile<'data, R, pe::AnonObjectHeaderBigobj>; + +/// A COFF object file. +/// +/// This is a file that starts with [`pe::ImageFileHeader`], and corresponds +/// to [`crate::FileKind::Coff`]. +/// +/// Most functionality is provided by the [`Object`] trait implementation. +#[derive(Debug)] +pub struct CoffFile<'data, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader> +{ + pub(super) header: &'data Coff, + pub(super) common: CoffCommon<'data, R, Coff>, + pub(super) data: R, +} + +impl<'data, R: ReadRef<'data>, Coff: CoffHeader> CoffFile<'data, R, Coff> { + /// Parse the raw COFF file data. + pub fn parse(data: R) -> Result<Self> { + let mut offset = 0; + let header = Coff::parse(data, &mut offset)?; + let sections = header.sections(data, offset)?; + let symbols = header.symbols(data)?; + + Ok(CoffFile { + header, + common: CoffCommon { + sections, + symbols, + image_base: 0, + }, + data, + }) + } + + /// Get the raw COFF file header. + pub fn coff_header(&self) -> &'data Coff { + self.header + } + + /// Get the COFF section table. + pub fn coff_section_table(&self) -> SectionTable<'data> { + self.common.sections + } + + /// Get the COFF symbol table. + pub fn coff_symbol_table(&self) -> &SymbolTable<'data, R, Coff> { + &self.common.symbols + } +} + +impl<'data, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed + for CoffFile<'data, R, Coff> +{ +} + +impl<'data, R, Coff> Object<'data> for CoffFile<'data, R, Coff> +where + R: ReadRef<'data>, + Coff: CoffHeader, +{ + type Segment<'file> + = CoffSegment<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type SegmentIterator<'file> + = CoffSegmentIterator<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type Section<'file> + = CoffSection<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type SectionIterator<'file> + = CoffSectionIterator<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type Comdat<'file> + = CoffComdat<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type ComdatIterator<'file> + = CoffComdatIterator<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type Symbol<'file> + = CoffSymbol<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type SymbolIterator<'file> + = CoffSymbolIterator<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type SymbolTable<'file> + = CoffSymbolTable<'data, 'file, R, Coff> + where + Self: 'file, + 'data: 'file; + type DynamicRelocationIterator<'file> + = NoDynamicRelocationIterator + where + Self: 'file, + 'data: 'file; + + fn architecture(&self) -> Architecture { + match self.header.machine() { + pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, + pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64, + pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, + pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, + pe::IMAGE_FILE_MACHINE_POWERPC + | pe::IMAGE_FILE_MACHINE_POWERPCFP + | pe::IMAGE_FILE_MACHINE_POWERPCBE => Architecture::PowerPc, + _ => Architecture::Unknown, + } + } + + fn sub_architecture(&self) -> Option<SubArchitecture> { + match self.header.machine() { + pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC), + _ => None, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + match self.header.machine() { + pe::IMAGE_FILE_MACHINE_POWERPCBE => false, + _ => true, + } + } + + #[inline] + fn is_64(&self) -> bool { + // Windows COFF is always 32-bit, even for 64-bit architectures. This could be confusing. + false + } + + fn kind(&self) -> ObjectKind { + ObjectKind::Relocatable + } + + fn segments(&self) -> CoffSegmentIterator<'data, '_, R, Coff> { + CoffSegmentIterator { + file: self, + iter: self.common.sections.iter(), + } + } + + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<CoffSection<'data, 'file, R, Coff>> { + self.sections() + .find(|section| section.name_bytes() == Ok(section_name)) + } + + fn section_by_index(&self, index: SectionIndex) -> Result<CoffSection<'data, '_, R, Coff>> { + let section = self.common.sections.section(index)?; + Ok(CoffSection { + file: self, + index, + section, + }) + } + + fn sections(&self) -> CoffSectionIterator<'data, '_, R, Coff> { + CoffSectionIterator { + file: self, + iter: self.common.sections.iter().enumerate(), + } + } + + fn comdats(&self) -> CoffComdatIterator<'data, '_, R, Coff> { + CoffComdatIterator::new(self) + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<CoffSymbol<'data, '_, R, Coff>> { + let symbol = self.common.symbols.symbol(index)?; + Ok(CoffSymbol { + file: &self.common, + index, + symbol, + }) + } + + fn symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> { + CoffSymbolIterator::new(&self.common) + } + + #[inline] + fn symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> { + Some(CoffSymbolTable { file: &self.common }) + } + + fn dynamic_symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> { + CoffSymbolIterator::empty(&self.common) + } + + #[inline] + fn dynamic_symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> { + None + } + + #[inline] + fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> { + None + } + + #[inline] + fn imports(&self) -> Result<Vec<Import<'data>>> { + // TODO: this could return undefined symbols, but not needed yet. + Ok(Vec::new()) + } + + #[inline] + fn exports(&self) -> Result<Vec<Export<'data>>> { + // TODO: this could return global symbols, but not needed yet. + Ok(Vec::new()) + } + + fn has_debug_symbols(&self) -> bool { + self.section_by_name(".debug_info").is_some() + } + + fn relative_address_base(&self) -> u64 { + 0 + } + + #[inline] + fn entry(&self) -> u64 { + 0 + } + + fn flags(&self) -> FileFlags { + FileFlags::Coff { + characteristics: self.header.characteristics(), + } + } +} + +/// Read the `class_id` field from a [`pe::AnonObjectHeader`]. +/// +/// This can be used to determine the format of the header. +pub fn anon_object_class_id<'data, R: ReadRef<'data>>(data: R) -> Result<pe::ClsId> { + let header = data + .read_at::<pe::AnonObjectHeader>(0) + .read_error("Invalid anon object header size or alignment")?; + Ok(header.class_id) +} + +/// A trait for generic access to [`pe::ImageFileHeader`] and [`pe::AnonObjectHeaderBigobj`]. +#[allow(missing_docs)] +pub trait CoffHeader: Debug + Pod { + type ImageSymbol: ImageSymbol; + type ImageSymbolBytes: Debug + Pod; + + /// Return true if this type is [`pe::AnonObjectHeaderBigobj`]. + /// + /// This is a property of the type, not a value in the header data. + fn is_type_bigobj() -> bool; + + fn machine(&self) -> u16; + fn number_of_sections(&self) -> u32; + fn pointer_to_symbol_table(&self) -> u32; + fn number_of_symbols(&self) -> u32; + fn characteristics(&self) -> u16; + + /// Read the file header. + /// + /// `data` must be the entire file data. + /// `offset` must be the file header offset. It is updated to point after the optional header, + /// which is where the section headers are located. + fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self>; + + /// Read the section table. + /// + /// `data` must be the entire file data. + /// `offset` must be after the optional file header. + #[inline] + fn sections<'data, R: ReadRef<'data>>( + &self, + data: R, + offset: u64, + ) -> read::Result<SectionTable<'data>> { + SectionTable::parse(self, data, offset) + } + + /// Read the symbol table and string table. + /// + /// `data` must be the entire file data. + #[inline] + fn symbols<'data, R: ReadRef<'data>>( + &self, + data: R, + ) -> read::Result<SymbolTable<'data, R, Self>> { + SymbolTable::parse(self, data) + } +} + +impl CoffHeader for pe::ImageFileHeader { + type ImageSymbol = pe::ImageSymbol; + type ImageSymbolBytes = pe::ImageSymbolBytes; + + fn is_type_bigobj() -> bool { + false + } + + fn machine(&self) -> u16 { + self.machine.get(LE) + } + + fn number_of_sections(&self) -> u32 { + self.number_of_sections.get(LE).into() + } + + fn pointer_to_symbol_table(&self) -> u32 { + self.pointer_to_symbol_table.get(LE) + } + + fn number_of_symbols(&self) -> u32 { + self.number_of_symbols.get(LE) + } + + fn characteristics(&self) -> u16 { + self.characteristics.get(LE) + } + + fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> { + let header = data + .read::<pe::ImageFileHeader>(offset) + .read_error("Invalid COFF file header size or alignment")?; + + // Skip over the optional header. + *offset = offset + .checked_add(header.size_of_optional_header.get(LE).into()) + .read_error("Invalid COFF optional header size")?; + + // TODO: maybe validate that the machine is known? + Ok(header) + } +} + +impl CoffHeader for pe::AnonObjectHeaderBigobj { + type ImageSymbol = pe::ImageSymbolEx; + type ImageSymbolBytes = pe::ImageSymbolExBytes; + + fn is_type_bigobj() -> bool { + true + } + + fn machine(&self) -> u16 { + self.machine.get(LE) + } + + fn number_of_sections(&self) -> u32 { + self.number_of_sections.get(LE) + } + + fn pointer_to_symbol_table(&self) -> u32 { + self.pointer_to_symbol_table.get(LE) + } + + fn number_of_symbols(&self) -> u32 { + self.number_of_symbols.get(LE) + } + + fn characteristics(&self) -> u16 { + 0 + } + + fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> { + let header = data + .read::<pe::AnonObjectHeaderBigobj>(offset) + .read_error("Invalid COFF bigobj file header size or alignment")?; + + if header.sig1.get(LE) != pe::IMAGE_FILE_MACHINE_UNKNOWN + || header.sig2.get(LE) != 0xffff + || header.version.get(LE) < 2 + || header.class_id != pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID + { + return Err(read::Error("Invalid COFF bigobj header values")); + } + + // TODO: maybe validate that the machine is known? + Ok(header) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/coff/import.rs b/chromium_crates_io/vendor/object-v0_37/src/read/coff/import.rs new file mode 100644 index 0000000..91f9fd7 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/coff/import.rs
@@ -0,0 +1,223 @@ +//! Support for reading short import files. +//! +//! These are used by some Windows linkers as a more compact way to describe +//! dynamically imported symbols. + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::read::{ + Architecture, ByteString, Bytes, Error, ReadError, ReadRef, Result, SubArchitecture, +}; + +/// A Windows short form description of a symbol to import. +/// +/// Used in Windows import libraries to provide a mapping from +/// a symbol name to a DLL export. This is not an object file. +/// +/// This is a file that starts with [`pe::ImportObjectHeader`], and corresponds +/// to [`crate::FileKind::CoffImport`]. +#[derive(Debug, Clone)] +pub struct ImportFile<'data> { + header: &'data pe::ImportObjectHeader, + kind: ImportType, + dll: ByteString<'data>, + symbol: ByteString<'data>, + import: Option<ByteString<'data>>, +} + +impl<'data> ImportFile<'data> { + /// Parse it. + pub fn parse<R: ReadRef<'data>>(data: R) -> Result<Self> { + let mut offset = 0; + let header = pe::ImportObjectHeader::parse(data, &mut offset)?; + let data = header.parse_data(data, &mut offset)?; + + // Unmangles a name by removing a `?`, `@` or `_` prefix. + fn strip_prefix(s: &[u8]) -> &[u8] { + match s.split_first() { + Some((b, rest)) if [b'?', b'@', b'_'].contains(b) => rest, + _ => s, + } + } + Ok(Self { + header, + dll: data.dll, + symbol: data.symbol, + kind: match header.import_type() { + pe::IMPORT_OBJECT_CODE => ImportType::Code, + pe::IMPORT_OBJECT_DATA => ImportType::Data, + pe::IMPORT_OBJECT_CONST => ImportType::Const, + _ => return Err(Error("Invalid COFF import library import type")), + }, + import: match header.name_type() { + pe::IMPORT_OBJECT_ORDINAL => None, + pe::IMPORT_OBJECT_NAME => Some(data.symbol()), + pe::IMPORT_OBJECT_NAME_NO_PREFIX => Some(strip_prefix(data.symbol())), + pe::IMPORT_OBJECT_NAME_UNDECORATE => Some( + strip_prefix(data.symbol()) + .split(|&b| b == b'@') + .next() + .unwrap(), + ), + pe::IMPORT_OBJECT_NAME_EXPORTAS => data.export(), + _ => return Err(Error("Unknown COFF import library name type")), + } + .map(ByteString), + }) + } + + /// Get the machine type. + pub fn architecture(&self) -> Architecture { + match self.header.machine.get(LE) { + pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, + pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64, + pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, + pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, + _ => Architecture::Unknown, + } + } + + /// Get the sub machine type, if available. + pub fn sub_architecture(&self) -> Option<SubArchitecture> { + match self.header.machine.get(LE) { + pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC), + _ => None, + } + } + + /// The public symbol name. + pub fn symbol(&self) -> &'data [u8] { + self.symbol.0 + } + + /// The name of the DLL to import the symbol from. + pub fn dll(&self) -> &'data [u8] { + self.dll.0 + } + + /// The name exported from the DLL. + pub fn import(&self) -> ImportName<'data> { + match self.import { + Some(name) => ImportName::Name(name.0), + None => ImportName::Ordinal(self.header.ordinal_or_hint.get(LE)), + } + } + + /// The type of import. Usually either a function or data. + pub fn import_type(&self) -> ImportType { + self.kind + } +} + +/// The name or ordinal to import from a DLL. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ImportName<'data> { + /// Import by ordinal. Ordinarily this is a 1-based index. + Ordinal(u16), + /// Import by name. + Name(&'data [u8]), +} + +/// The kind of import symbol. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ImportType { + /// An executable code symbol. + Code, + /// A data symbol. + Data, + /// A constant value. + Const, +} + +impl pe::ImportObjectHeader { + /// Read the short import header. + /// + /// Also checks that the signature and version are valid. + /// Directly following this header will be the string data. + pub fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> Result<&'data Self> { + let header = data + .read::<pe::ImportObjectHeader>(offset) + .read_error("Invalid COFF import library header size")?; + if header.sig1.get(LE) != 0 || header.sig2.get(LE) != pe::IMPORT_OBJECT_HDR_SIG2 { + Err(Error("Invalid COFF import library header")) + } else if header.version.get(LE) != 0 { + Err(Error("Unknown COFF import library header version")) + } else { + Ok(header) + } + } + + /// Parse the data following the header. + pub fn parse_data<'data, R: ReadRef<'data>>( + &self, + data: R, + offset: &mut u64, + ) -> Result<ImportObjectData<'data>> { + let mut data = Bytes( + data.read_bytes(offset, u64::from(self.size_of_data.get(LE))) + .read_error("Invalid COFF import library data size")?, + ); + let symbol = data + .read_string() + .map(ByteString) + .read_error("Could not read COFF import library symbol name")?; + let dll = data + .read_string() + .map(ByteString) + .read_error("Could not read COFF import library DLL name")?; + let export = if self.name_type() == pe::IMPORT_OBJECT_NAME_EXPORTAS { + data.read_string() + .map(ByteString) + .map(Some) + .read_error("Could not read COFF import library export name")? + } else { + None + }; + Ok(ImportObjectData { + symbol, + dll, + export, + }) + } + + /// The type of import. + /// + /// This is one of the `IMPORT_OBJECT_*` constants. + pub fn import_type(&self) -> u16 { + self.name_type.get(LE) & pe::IMPORT_OBJECT_TYPE_MASK + } + + /// The type of import name. + /// + /// This is one of the `IMPORT_OBJECT_*` constants. + pub fn name_type(&self) -> u16 { + (self.name_type.get(LE) >> pe::IMPORT_OBJECT_NAME_SHIFT) & pe::IMPORT_OBJECT_NAME_MASK + } +} + +/// The data following [`pe::ImportObjectHeader`]. +#[derive(Debug, Clone)] +pub struct ImportObjectData<'data> { + symbol: ByteString<'data>, + dll: ByteString<'data>, + export: Option<ByteString<'data>>, +} + +impl<'data> ImportObjectData<'data> { + /// The public symbol name. + pub fn symbol(&self) -> &'data [u8] { + self.symbol.0 + } + + /// The name of the DLL to import the symbol from. + pub fn dll(&self) -> &'data [u8] { + self.dll.0 + } + + /// The name exported from the DLL. + /// + /// This is only set if the name is not derived from the symbol name. + pub fn export(&self) -> Option<&'data [u8]> { + self.export.map(|export| export.0) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/coff/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/read/coff/mod.rs new file mode 100644 index 0000000..de397da --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/coff/mod.rs
@@ -0,0 +1,66 @@ +//! Support for reading Windows COFF files. +//! +//! Traits are used to abstract over the difference between COFF object files +//! and COFF bigobj files. The primary trait for this is [`CoffHeader`]. +//! +//! ## High level API +//! +//! [`CoffFile`] implements the [`Object`](crate::read::Object) trait for +//! COFF files. [`CoffFile`] is parameterised by [`CoffHeader`]. +//! The default parameter allows reading regular COFF object files, +//! while the type alias [`CoffBigFile`] allows reading COFF bigobj files. +//! +//! [`ImportFile`] allows reading COFF short imports that are used in import +//! libraries. Currently these are not integrated with the unified read API. +//! +//! ## Low level API +//! +//! The [`CoffHeader`] trait can be directly used to parse both COFF +//! object files (which start with [`pe::ImageFileHeader`]) and COFF bigobj +//! files (which start with [`pe::AnonObjectHeaderBigobj`]). +//! +//! ### Example for low level API +//! ```no_run +//! use object::pe; +//! use object::read::coff::{CoffHeader, ImageSymbol as _}; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the name of each section and symbol. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(feature = "std")] { +//! let data = fs::read("path/to/binary")?; +//! let mut offset = 0; +//! let header = pe::ImageFileHeader::parse(&*data, &mut offset)?; +//! let sections = header.sections(&*data, offset)?; +//! let symbols = header.symbols(&*data)?; +//! for section in sections.iter() { +//! println!("{}", String::from_utf8_lossy(section.name(symbols.strings())?)); +//! } +//! for (_index, symbol) in symbols.iter() { +//! println!("{}", String::from_utf8_lossy(symbol.name(symbols.strings())?)); +//! } +//! # } +//! Ok(()) +//! } +//! ``` +#[cfg(doc)] +use crate::pe; + +mod file; +pub use file::*; + +mod section; +pub use section::*; + +mod symbol; +pub use symbol::*; + +mod relocation; +pub use relocation::*; + +mod comdat; +pub use comdat::*; + +mod import; +pub use import::*;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/coff/relocation.rs b/chromium_crates_io/vendor/object-v0_37/src/read/coff/relocation.rs new file mode 100644 index 0000000..d4f1707 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/coff/relocation.rs
@@ -0,0 +1,119 @@ +use alloc::fmt; +use core::slice; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::read::{ + ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, + SymbolIndex, +}; + +use super::{CoffFile, CoffHeader}; + +/// An iterator for the relocations in a [`CoffBigSection`](super::CoffBigSection). +pub type CoffBigRelocationIterator<'data, 'file, R = &'data [u8]> = + CoffRelocationIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator for the relocations in a [`CoffSection`](super::CoffSection). +pub struct CoffRelocationIterator< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + pub(super) file: &'file CoffFile<'data, R, Coff>, + pub(super) iter: slice::Iter<'data, pe::ImageRelocation>, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for CoffRelocationIterator<'data, 'file, R, Coff> +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + use RelocationEncoding as E; + use RelocationKind as K; + + self.iter.next().map(|relocation| { + let typ = relocation.typ.get(LE); + let flags = RelocationFlags::Coff { typ }; + let g = E::Generic; + let unknown = (K::Unknown, E::Generic, 0, 0); + let (kind, encoding, size, addend) = match self.file.header.machine() { + pe::IMAGE_FILE_MACHINE_ARMNT => match typ { + pe::IMAGE_REL_ARM_ADDR32 => (K::Absolute, g, 32, 0), + pe::IMAGE_REL_ARM_ADDR32NB => (K::ImageOffset, g, 32, 0), + pe::IMAGE_REL_ARM_REL32 => (K::Relative, g, 32, -4), + pe::IMAGE_REL_ARM_SECTION => (K::SectionIndex, g, 16, 0), + pe::IMAGE_REL_ARM_SECREL => (K::SectionOffset, g, 32, 0), + _ => unknown, + }, + pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => match typ { + pe::IMAGE_REL_ARM64_ADDR32 => (K::Absolute, g, 32, 0), + pe::IMAGE_REL_ARM64_ADDR32NB => (K::ImageOffset, g, 32, 0), + pe::IMAGE_REL_ARM64_SECREL => (K::SectionOffset, g, 32, 0), + pe::IMAGE_REL_ARM64_SECTION => (K::SectionIndex, g, 16, 0), + pe::IMAGE_REL_ARM64_ADDR64 => (K::Absolute, g, 64, 0), + pe::IMAGE_REL_ARM64_REL32 => (K::Relative, g, 32, -4), + pe::IMAGE_REL_ARM64_BRANCH26 => (K::Relative, E::AArch64Call, 26, 0), + _ => unknown, + }, + pe::IMAGE_FILE_MACHINE_I386 => match typ { + pe::IMAGE_REL_I386_DIR16 => (K::Absolute, g, 16, 0), + pe::IMAGE_REL_I386_REL16 => (K::Relative, g, 16, 0), + pe::IMAGE_REL_I386_DIR32 => (K::Absolute, g, 32, 0), + pe::IMAGE_REL_I386_DIR32NB => (K::ImageOffset, g, 32, 0), + pe::IMAGE_REL_I386_SECTION => (K::SectionIndex, g, 16, 0), + pe::IMAGE_REL_I386_SECREL => (K::SectionOffset, g, 32, 0), + pe::IMAGE_REL_I386_SECREL7 => (K::SectionOffset, g, 7, 0), + pe::IMAGE_REL_I386_REL32 => (K::Relative, g, 32, -4), + _ => unknown, + }, + pe::IMAGE_FILE_MACHINE_AMD64 => match typ { + pe::IMAGE_REL_AMD64_ADDR64 => (K::Absolute, g, 64, 0), + pe::IMAGE_REL_AMD64_ADDR32 => (K::Absolute, g, 32, 0), + pe::IMAGE_REL_AMD64_ADDR32NB => (K::ImageOffset, g, 32, 0), + pe::IMAGE_REL_AMD64_REL32 => (K::Relative, g, 32, -4), + pe::IMAGE_REL_AMD64_REL32_1 => (K::Relative, g, 32, -5), + pe::IMAGE_REL_AMD64_REL32_2 => (K::Relative, g, 32, -6), + pe::IMAGE_REL_AMD64_REL32_3 => (K::Relative, g, 32, -7), + pe::IMAGE_REL_AMD64_REL32_4 => (K::Relative, g, 32, -8), + pe::IMAGE_REL_AMD64_REL32_5 => (K::Relative, g, 32, -9), + pe::IMAGE_REL_AMD64_SECTION => (K::SectionIndex, g, 16, 0), + pe::IMAGE_REL_AMD64_SECREL => (K::SectionOffset, g, 32, 0), + pe::IMAGE_REL_AMD64_SECREL7 => (K::SectionOffset, g, 7, 0), + _ => unknown, + }, + _ => unknown, + }; + let target = RelocationTarget::Symbol(relocation.symbol()); + ( + u64::from(relocation.virtual_address.get(LE)), + Relocation { + kind, + encoding, + size, + target, + addend, + implicit_addend: true, + flags, + }, + ) + }) + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> fmt::Debug + for CoffRelocationIterator<'data, 'file, R, Coff> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CoffRelocationIterator").finish() + } +} + +impl pe::ImageRelocation { + /// Get the index of the symbol referenced by this relocation. + pub fn symbol(&self) -> SymbolIndex { + SymbolIndex(self.symbol_table_index.get(LE) as usize) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/coff/section.rs b/chromium_crates_io/vendor/object-v0_37/src/read/coff/section.rs new file mode 100644 index 0000000..280b5c4 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/coff/section.rs
@@ -0,0 +1,619 @@ +use core::convert::TryFrom; +use core::{iter, result, slice, str}; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::read::util::StringTable; +use crate::read::{ + self, CompressedData, CompressedFileRange, Error, ObjectSection, ObjectSegment, ReadError, + ReadRef, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, +}; + +use super::{CoffFile, CoffHeader, CoffRelocationIterator}; + +/// The table of section headers in a COFF or PE file. +/// +/// Returned by [`CoffHeader::sections`] and +/// [`ImageNtHeaders::sections`](crate::read::pe::ImageNtHeaders::sections). +#[derive(Debug, Default, Clone, Copy)] +pub struct SectionTable<'data> { + sections: &'data [pe::ImageSectionHeader], +} + +impl<'data> SectionTable<'data> { + /// Parse the section table. + /// + /// `data` must be the entire file data. + /// `offset` must be after the optional file header. + pub fn parse<Coff: CoffHeader, R: ReadRef<'data>>( + header: &Coff, + data: R, + offset: u64, + ) -> Result<Self> { + let sections = data + .read_slice_at(offset, header.number_of_sections() as usize) + .read_error("Invalid COFF/PE section headers")?; + Ok(SectionTable { sections }) + } + + /// Iterate over the section headers. + /// + /// Warning: section indices start at 1. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, pe::ImageSectionHeader> { + self.sections.iter() + } + + /// Iterate over the section headers and their indices. + pub fn enumerate(&self) -> impl Iterator<Item = (SectionIndex, &'data pe::ImageSectionHeader)> { + self.sections + .iter() + .enumerate() + .map(|(i, section)| (SectionIndex(i + 1), section)) + } + + /// Return true if the section table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.sections.is_empty() + } + + /// The number of section headers. + #[inline] + pub fn len(&self) -> usize { + self.sections.len() + } + + /// Return the section header at the given index. + /// + /// The index is 1-based. + pub fn section(&self, index: SectionIndex) -> read::Result<&'data pe::ImageSectionHeader> { + self.sections + .get(index.0.wrapping_sub(1)) + .read_error("Invalid COFF/PE section index") + } + + /// Return the section header with the given name. + /// + /// The returned index is 1-based. + /// + /// Ignores sections with invalid names. + pub fn section_by_name<R: ReadRef<'data>>( + &self, + strings: StringTable<'data, R>, + name: &[u8], + ) -> Option<(SectionIndex, &'data pe::ImageSectionHeader)> { + self.enumerate() + .find(|(_, section)| section.name(strings) == Ok(name)) + } + + /// Compute the maximum file offset used by sections. + /// + /// This will usually match the end of file, unless the PE file has a + /// [data overlay](https://security.stackexchange.com/questions/77336/how-is-the-file-overlay-read-by-an-exe-virus) + pub fn max_section_file_offset(&self) -> u64 { + let mut max = 0; + for section in self.iter() { + match (section.pointer_to_raw_data.get(LE) as u64) + .checked_add(section.size_of_raw_data.get(LE) as u64) + { + None => { + // This cannot happen, we're suming two u32 into a u64 + continue; + } + Some(end_of_section) => { + if end_of_section > max { + max = end_of_section; + } + } + } + } + max + } +} + +/// An iterator for the loadable sections in a [`CoffBigFile`](super::CoffBigFile). +pub type CoffBigSegmentIterator<'data, 'file, R = &'data [u8]> = + CoffSegmentIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator for the loadable sections in a [`CoffFile`]. +#[derive(Debug)] +pub struct CoffSegmentIterator< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + pub(super) file: &'file CoffFile<'data, R, Coff>, + pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for CoffSegmentIterator<'data, 'file, R, Coff> +{ + type Item = CoffSegment<'data, 'file, R, Coff>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|section| CoffSegment { + file: self.file, + section, + }) + } +} + +/// A loadable section in a [`CoffBigFile`](super::CoffBigFile). +/// +/// Most functionality is provided by the [`ObjectSegment`] trait implementation. +pub type CoffBigSegment<'data, 'file, R = &'data [u8]> = + CoffSegment<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A loadable section in a [`CoffFile`]. +/// +/// Most functionality is provided by the [`ObjectSegment`] trait implementation. +#[derive(Debug)] +pub struct CoffSegment< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + pub(super) file: &'file CoffFile<'data, R, Coff>, + pub(super) section: &'data pe::ImageSectionHeader, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffSegment<'data, 'file, R, Coff> { + /// Get the COFF file containing this segment. + pub fn coff_file(&self) -> &'file CoffFile<'data, R, Coff> { + self.file + } + + /// Get the raw COFF section header. + pub fn coff_section(&self) -> &'data pe::ImageSectionHeader { + self.section + } + + fn bytes(&self) -> Result<&'data [u8]> { + self.section + .coff_data(self.file.data) + .read_error("Invalid COFF section offset or size") + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed + for CoffSegment<'data, 'file, R, Coff> +{ +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSegment<'data> + for CoffSegment<'data, 'file, R, Coff> +{ + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address.get(LE)) + } + + #[inline] + fn size(&self) -> u64 { + u64::from(self.section.virtual_size.get(LE)) + } + + #[inline] + fn align(&self) -> u64 { + self.section.coff_alignment() + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + let (offset, size) = self.section.coff_file_range().unwrap_or((0, 0)); + (u64::from(offset), u64::from(size)) + } + + fn data(&self) -> Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + self.section + .name(self.file.common.symbols.strings()) + .map(Some) + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + let name = self.section.name(self.file.common.symbols.strings())?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 COFF section name") + .map(Some) + } + + #[inline] + fn flags(&self) -> SegmentFlags { + let characteristics = self.section.characteristics.get(LE); + SegmentFlags::Coff { characteristics } + } +} + +/// An iterator for the sections in a [`CoffBigFile`](super::CoffBigFile). +pub type CoffBigSectionIterator<'data, 'file, R = &'data [u8]> = + CoffSectionIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator for the sections in a [`CoffFile`]. +#[derive(Debug)] +pub struct CoffSectionIterator< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + pub(super) file: &'file CoffFile<'data, R, Coff>, + pub(super) iter: iter::Enumerate<slice::Iter<'data, pe::ImageSectionHeader>>, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for CoffSectionIterator<'data, 'file, R, Coff> +{ + type Item = CoffSection<'data, 'file, R, Coff>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|(index, section)| CoffSection { + file: self.file, + index: SectionIndex(index + 1), + section, + }) + } +} + +/// A section in a [`CoffBigFile`](super::CoffBigFile). +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +pub type CoffBigSection<'data, 'file, R = &'data [u8]> = + CoffSection<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A section in a [`CoffFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct CoffSection< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + pub(super) file: &'file CoffFile<'data, R, Coff>, + pub(super) index: SectionIndex, + pub(super) section: &'data pe::ImageSectionHeader, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffSection<'data, 'file, R, Coff> { + /// Get the COFF file containing this section. + pub fn coff_file(&self) -> &'file CoffFile<'data, R, Coff> { + self.file + } + + /// Get the raw COFF section header. + pub fn coff_section(&self) -> &'data pe::ImageSectionHeader { + self.section + } + + /// Get the raw COFF relocations for this section. + pub fn coff_relocations(&self) -> Result<&'data [pe::ImageRelocation]> { + self.section.coff_relocations(self.file.data) + } + + fn bytes(&self) -> Result<&'data [u8]> { + self.section + .coff_data(self.file.data) + .read_error("Invalid COFF section offset or size") + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed + for CoffSection<'data, 'file, R, Coff> +{ +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSection<'data> + for CoffSection<'data, 'file, R, Coff> +{ + type RelocationIterator = CoffRelocationIterator<'data, 'file, R, Coff>; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address.get(LE)) + } + + #[inline] + fn size(&self) -> u64 { + // TODO: This may need to be the length from the auxiliary symbol for this section. + u64::from(self.section.size_of_raw_data.get(LE)) + } + + #[inline] + fn align(&self) -> u64 { + self.section.coff_alignment() + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + let (offset, size) = self.section.coff_file_range()?; + Some((u64::from(offset), u64::from(size))) + } + + fn data(&self) -> Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + #[inline] + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + self.section.name(self.file.common.symbols.strings()) + } + + #[inline] + fn name(&self) -> Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 COFF section name") + } + + #[inline] + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + Ok(None) + } + + #[inline] + fn kind(&self) -> SectionKind { + self.section.kind() + } + + fn relocations(&self) -> CoffRelocationIterator<'data, 'file, R, Coff> { + let relocations = self.coff_relocations().unwrap_or(&[]); + CoffRelocationIterator { + file: self.file, + iter: relocations.iter(), + } + } + + fn relocation_map(&self) -> read::Result<RelocationMap> { + RelocationMap::new(self.file, self) + } + + fn flags(&self) -> SectionFlags { + SectionFlags::Coff { + characteristics: self.section.characteristics.get(LE), + } + } +} + +impl pe::ImageSectionHeader { + pub(crate) fn kind(&self) -> SectionKind { + let characteristics = self.characteristics.get(LE); + if characteristics & (pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE) != 0 { + SectionKind::Text + } else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { + if characteristics & pe::IMAGE_SCN_MEM_DISCARDABLE != 0 { + SectionKind::Other + } else if characteristics & pe::IMAGE_SCN_MEM_WRITE != 0 { + SectionKind::Data + } else { + SectionKind::ReadOnlyData + } + } else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { + SectionKind::UninitializedData + } else if characteristics & pe::IMAGE_SCN_LNK_INFO != 0 { + SectionKind::Linker + } else { + SectionKind::Unknown + } + } +} + +impl pe::ImageSectionHeader { + /// Return the string table offset of the section name. + /// + /// Returns `Ok(None)` if the name doesn't use the string table + /// and can be obtained with `raw_name` instead. + pub fn name_offset(&self) -> Result<Option<u32>> { + let bytes = &self.name; + if bytes[0] != b'/' { + return Ok(None); + } + + if bytes[1] == b'/' { + let mut offset = 0; + for byte in bytes[2..].iter() { + let digit = match byte { + b'A'..=b'Z' => byte - b'A', + b'a'..=b'z' => byte - b'a' + 26, + b'0'..=b'9' => byte - b'0' + 52, + b'+' => 62, + b'/' => 63, + _ => return Err(Error("Invalid COFF section name base-64 offset")), + }; + offset = offset * 64 + digit as u64; + } + u32::try_from(offset) + .ok() + .read_error("Invalid COFF section name base-64 offset") + .map(Some) + } else { + let mut offset = 0; + for byte in bytes[1..].iter() { + let digit = match byte { + b'0'..=b'9' => byte - b'0', + 0 => break, + _ => return Err(Error("Invalid COFF section name base-10 offset")), + }; + offset = offset * 10 + digit as u32; + } + Ok(Some(offset)) + } + } + + /// Return the section name. + /// + /// This handles decoding names that are offsets into the symbol string table. + pub fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + if let Some(offset) = self.name_offset()? { + strings + .get(offset) + .read_error("Invalid COFF section name offset") + } else { + Ok(self.raw_name()) + } + } + + /// Return the raw section name. + pub fn raw_name(&self) -> &[u8] { + let bytes = &self.name; + match memchr::memchr(b'\0', bytes) { + Some(end) => &bytes[..end], + None => &bytes[..], + } + } + + /// Return the offset and size of the section in a COFF file. + /// + /// Returns `None` for sections that have no data in the file. + pub fn coff_file_range(&self) -> Option<(u32, u32)> { + if self.characteristics.get(LE) & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { + None + } else { + let offset = self.pointer_to_raw_data.get(LE); + // Note: virtual size is not used for COFF. + let size = self.size_of_raw_data.get(LE); + Some((offset, size)) + } + } + + /// Return the section data in a COFF file. + /// + /// Returns `Ok(&[])` if the section has no data. + /// Returns `Err` for invalid values. + pub fn coff_data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> { + if let Some((offset, size)) = self.coff_file_range() { + data.read_bytes_at(offset.into(), size.into()) + } else { + Ok(&[]) + } + } + + /// Return the section alignment in bytes. + /// + /// This is only valid for sections in a COFF file. + pub fn coff_alignment(&self) -> u64 { + match self.characteristics.get(LE) & pe::IMAGE_SCN_ALIGN_MASK { + pe::IMAGE_SCN_ALIGN_1BYTES => 1, + pe::IMAGE_SCN_ALIGN_2BYTES => 2, + pe::IMAGE_SCN_ALIGN_4BYTES => 4, + pe::IMAGE_SCN_ALIGN_8BYTES => 8, + pe::IMAGE_SCN_ALIGN_16BYTES => 16, + pe::IMAGE_SCN_ALIGN_32BYTES => 32, + pe::IMAGE_SCN_ALIGN_64BYTES => 64, + pe::IMAGE_SCN_ALIGN_128BYTES => 128, + pe::IMAGE_SCN_ALIGN_256BYTES => 256, + pe::IMAGE_SCN_ALIGN_512BYTES => 512, + pe::IMAGE_SCN_ALIGN_1024BYTES => 1024, + pe::IMAGE_SCN_ALIGN_2048BYTES => 2048, + pe::IMAGE_SCN_ALIGN_4096BYTES => 4096, + pe::IMAGE_SCN_ALIGN_8192BYTES => 8192, + _ => 16, + } + } + + /// Read the relocations in a COFF file. + /// + /// `data` must be the entire file data. + pub fn coff_relocations<'data, R: ReadRef<'data>>( + &self, + data: R, + ) -> read::Result<&'data [pe::ImageRelocation]> { + let mut pointer = self.pointer_to_relocations.get(LE).into(); + let mut number: usize = self.number_of_relocations.get(LE).into(); + if number == u16::MAX.into() + && self.characteristics.get(LE) & pe::IMAGE_SCN_LNK_NRELOC_OVFL != 0 + { + // Extended relocations. Read first relocation (which contains extended count) & adjust + // relocations pointer. + let extended_relocation_info = data + .read_at::<pe::ImageRelocation>(pointer) + .read_error("Invalid COFF relocation offset or number")?; + number = extended_relocation_info.virtual_address.get(LE) as usize; + if number == 0 { + return Err(Error("Invalid COFF relocation number")); + } + pointer += core::mem::size_of::<pe::ImageRelocation>() as u64; + // Extended relocation info does not contribute to the count of sections. + number -= 1; + } + data.read_slice_at(pointer, number) + .read_error("Invalid COFF relocation offset or number") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn name_offset() { + let mut section = pe::ImageSectionHeader::default(); + section.name = *b"xxxxxxxx"; + assert_eq!(section.name_offset(), Ok(None)); + section.name = *b"/0\0\0\0\0\0\0"; + assert_eq!(section.name_offset(), Ok(Some(0))); + section.name = *b"/9999999"; + assert_eq!(section.name_offset(), Ok(Some(999_9999))); + section.name = *b"//AAAAAA"; + assert_eq!(section.name_offset(), Ok(Some(0))); + section.name = *b"//D/////"; + assert_eq!(section.name_offset(), Ok(Some(0xffff_ffff))); + section.name = *b"//EAAAAA"; + assert!(section.name_offset().is_err()); + section.name = *b"////////"; + assert!(section.name_offset().is_err()); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/coff/symbol.rs b/chromium_crates_io/vendor/object-v0_37/src/read/coff/symbol.rs new file mode 100644 index 0000000..73119e3 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/coff/symbol.rs
@@ -0,0 +1,695 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::convert::TryInto; +use core::fmt::Debug; +use core::str; + +use super::{CoffCommon, CoffHeader, SectionTable}; +use crate::endian::{LittleEndian as LE, U32Bytes}; +use crate::pe; +use crate::pod::{bytes_of, bytes_of_slice, Pod}; +use crate::read::util::StringTable; +use crate::read::{ + self, Bytes, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, + SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, +}; + +/// A table of symbol entries in a COFF or PE file. +/// +/// Also includes the string table used for the symbol names. +/// +/// Returned by [`CoffHeader::symbols`] and +/// [`ImageNtHeaders::symbols`](crate::read::pe::ImageNtHeaders::symbols). +#[derive(Debug)] +pub struct SymbolTable<'data, R = &'data [u8], Coff = pe::ImageFileHeader> +where + R: ReadRef<'data>, + Coff: CoffHeader, +{ + symbols: &'data [Coff::ImageSymbolBytes], + strings: StringTable<'data, R>, +} + +impl<'data, R: ReadRef<'data>, Coff: CoffHeader> Default for SymbolTable<'data, R, Coff> { + fn default() -> Self { + Self { + symbols: &[], + strings: StringTable::default(), + } + } +} + +impl<'data, R: ReadRef<'data>, Coff: CoffHeader> SymbolTable<'data, R, Coff> { + /// Read the symbol table. + pub fn parse(header: &Coff, data: R) -> Result<Self> { + // The symbol table may not be present. + let mut offset = header.pointer_to_symbol_table().into(); + let (symbols, strings) = if offset != 0 { + let symbols = data + .read_slice(&mut offset, header.number_of_symbols() as usize) + .read_error("Invalid COFF symbol table offset or size")?; + + // Note: don't update data when reading length; the length includes itself. + let length = data + .read_at::<U32Bytes<_>>(offset) + .read_error("Missing COFF string table")? + .get(LE); + let str_end = offset + .checked_add(length as u64) + .read_error("Invalid COFF string table length")?; + let strings = StringTable::new(data, offset, str_end); + + (symbols, strings) + } else { + (&[][..], StringTable::default()) + }; + + Ok(SymbolTable { symbols, strings }) + } + + /// Return the string table used for the symbol names. + #[inline] + pub fn strings(&self) -> StringTable<'data, R> { + self.strings + } + + /// Return true if the symbol table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// The number of symbol table entries. + /// + /// This includes auxiliary symbol table entries. + #[inline] + pub fn len(&self) -> usize { + self.symbols.len() + } + + /// Iterate over the symbols. + #[inline] + pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table, R, Coff> { + SymbolIterator { + symbols: self, + index: SymbolIndex(0), + } + } + + /// Return the symbol table entry at the given index. + #[inline] + pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Coff::ImageSymbol> { + self.get::<Coff::ImageSymbol>(index, 0) + } + + /// Return the auxiliary function symbol for the symbol table entry at the given index. + /// + /// Note that the index is of the symbol, not the first auxiliary record. + #[inline] + pub fn aux_function(&self, index: SymbolIndex) -> Result<&'data pe::ImageAuxSymbolFunction> { + self.get::<pe::ImageAuxSymbolFunction>(index, 1) + } + + /// Return the auxiliary section symbol for the symbol table entry at the given index. + /// + /// Note that the index is of the symbol, not the first auxiliary record. + #[inline] + pub fn aux_section(&self, index: SymbolIndex) -> Result<&'data pe::ImageAuxSymbolSection> { + self.get::<pe::ImageAuxSymbolSection>(index, 1) + } + + /// Return the auxiliary weak external symbol for the symbol table entry at the given index. + /// + /// Note that the index is of the symbol, not the first auxiliary record. + #[inline] + pub fn aux_weak_external(&self, index: SymbolIndex) -> Result<&'data pe::ImageAuxSymbolWeak> { + self.get::<pe::ImageAuxSymbolWeak>(index, 1) + } + + /// Return the auxiliary file name for the symbol table entry at the given index. + /// + /// Note that the index is of the symbol, not the first auxiliary record. + pub fn aux_file_name(&self, index: SymbolIndex, aux_count: u8) -> Result<&'data [u8]> { + let entries = index + .0 + .checked_add(1) + .and_then(|x| Some(x..x.checked_add(aux_count.into())?)) + .and_then(|x| self.symbols.get(x)) + .read_error("Invalid COFF symbol index")?; + let bytes = bytes_of_slice(entries); + // The name is padded with nulls. + Ok(match memchr::memchr(b'\0', bytes) { + Some(end) => &bytes[..end], + None => bytes, + }) + } + + /// Return the symbol table entry or auxiliary record at the given index and offset. + pub fn get<T: Pod>(&self, index: SymbolIndex, offset: usize) -> Result<&'data T> { + let bytes = index + .0 + .checked_add(offset) + .and_then(|x| self.symbols.get(x)) + .read_error("Invalid COFF symbol index")?; + Bytes(bytes_of(bytes)) + .read() + .read_error("Invalid COFF symbol data") + } + + /// Construct a map from addresses to a user-defined map entry. + pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Coff::ImageSymbol) -> Option<Entry>>( + &self, + f: F, + ) -> SymbolMap<Entry> { + let mut symbols = Vec::with_capacity(self.symbols.len()); + for (_, symbol) in self.iter() { + if !symbol.is_definition() { + continue; + } + if let Some(entry) = f(symbol) { + symbols.push(entry); + } + } + SymbolMap::new(symbols) + } +} + +/// An iterator for symbol entries in a COFF or PE file. +/// +/// Yields the index and symbol structure for each symbol. +#[derive(Debug)] +pub struct SymbolIterator<'data, 'table, R = &'data [u8], Coff = pe::ImageFileHeader> +where + R: ReadRef<'data>, + Coff: CoffHeader, +{ + symbols: &'table SymbolTable<'data, R, Coff>, + index: SymbolIndex, +} + +impl<'data, 'table, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for SymbolIterator<'data, 'table, R, Coff> +{ + type Item = (SymbolIndex, &'data Coff::ImageSymbol); + + fn next(&mut self) -> Option<Self::Item> { + let index = self.index; + let symbol = self.symbols.symbol(index).ok()?; + self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; + Some((index, symbol)) + } +} + +/// A symbol table in a [`CoffBigFile`](super::CoffBigFile). +pub type CoffBigSymbolTable<'data, 'file, R = &'data [u8]> = + CoffSymbolTable<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A symbol table in a [`CoffFile`](super::CoffFile) +/// or [`PeFile`](crate::read::pe::PeFile). +#[derive(Debug, Clone, Copy)] +pub struct CoffSymbolTable<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader> +where + R: ReadRef<'data>, + Coff: CoffHeader, +{ + pub(crate) file: &'file CoffCommon<'data, R, Coff>, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed + for CoffSymbolTable<'data, 'file, R, Coff> +{ +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSymbolTable<'data> + for CoffSymbolTable<'data, 'file, R, Coff> +{ + type Symbol = CoffSymbol<'data, 'file, R, Coff>; + type SymbolIterator = CoffSymbolIterator<'data, 'file, R, Coff>; + + fn symbols(&self) -> Self::SymbolIterator { + CoffSymbolIterator::new(self.file) + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { + let symbol = self.file.symbols.symbol(index)?; + Ok(CoffSymbol { + file: self.file, + index, + symbol, + }) + } +} + +/// An iterator for the symbols in a [`CoffBigFile`](super::CoffBigFile). +pub type CoffBigSymbolIterator<'data, 'file, R = &'data [u8]> = + CoffSymbolIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator for the symbols in a [`CoffFile`](super::CoffFile) +/// or [`PeFile`](crate::read::pe::PeFile). +pub struct CoffSymbolIterator<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader> +where + R: ReadRef<'data>, + Coff: CoffHeader, +{ + file: &'file CoffCommon<'data, R, Coff>, + index: SymbolIndex, +} + +impl<'data, 'file, R, Coff> CoffSymbolIterator<'data, 'file, R, Coff> +where + R: ReadRef<'data>, + Coff: CoffHeader, +{ + pub(crate) fn new(file: &'file CoffCommon<'data, R, Coff>) -> Self { + Self { + file, + index: SymbolIndex(0), + } + } + + pub(crate) fn empty(file: &'file CoffCommon<'data, R, Coff>) -> Self { + Self { + file, + index: SymbolIndex(file.symbols.len()), + } + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> fmt::Debug + for CoffSymbolIterator<'data, 'file, R, Coff> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CoffSymbolIterator").finish() + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for CoffSymbolIterator<'data, 'file, R, Coff> +{ + type Item = CoffSymbol<'data, 'file, R, Coff>; + + fn next(&mut self) -> Option<Self::Item> { + let index = self.index; + let symbol = self.file.symbols.symbol(index).ok()?; + self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; + Some(CoffSymbol { + file: self.file, + index, + symbol, + }) + } +} + +/// A symbol in a [`CoffBigFile`](super::CoffBigFile). +/// +/// Most functionality is provided by the [`ObjectSymbol`] trait implementation. +pub type CoffBigSymbol<'data, 'file, R = &'data [u8]> = + CoffSymbol<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A symbol in a [`CoffFile`](super::CoffFile) or [`PeFile`](crate::read::pe::PeFile). +/// +/// Most functionality is provided by the [`ObjectSymbol`] trait implementation. +#[derive(Debug, Clone, Copy)] +pub struct CoffSymbol<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader> +where + R: ReadRef<'data>, + Coff: CoffHeader, +{ + pub(crate) file: &'file CoffCommon<'data, R, Coff>, + pub(crate) index: SymbolIndex, + pub(crate) symbol: &'data Coff::ImageSymbol, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffSymbol<'data, 'file, R, Coff> { + #[inline] + /// Get the raw `ImageSymbol` struct. + #[deprecated(note = "Use `coff_symbol` instead")] + pub fn raw_symbol(&self) -> &'data Coff::ImageSymbol { + self.symbol + } + + /// Get the raw `ImageSymbol` struct. + pub fn coff_symbol(&self) -> &'data Coff::ImageSymbol { + self.symbol + } +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed + for CoffSymbol<'data, 'file, R, Coff> +{ +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSymbol<'data> + for CoffSymbol<'data, 'file, R, Coff> +{ + #[inline] + fn index(&self) -> SymbolIndex { + self.index + } + + fn name_bytes(&self) -> read::Result<&'data [u8]> { + if self.symbol.has_aux_file_name() { + self.file + .symbols + .aux_file_name(self.index, self.symbol.number_of_aux_symbols()) + } else { + self.symbol.name(self.file.symbols.strings()) + } + } + + fn name(&self) -> read::Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 COFF symbol name") + } + + fn address(&self) -> u64 { + self.symbol + .address(self.file.image_base, &self.file.sections) + .unwrap_or(None) + .unwrap_or(0) + } + + fn size(&self) -> u64 { + match self.symbol.storage_class() { + pe::IMAGE_SYM_CLASS_STATIC => { + // Section symbols may duplicate the size from the section table. + if self.symbol.has_aux_section() { + if let Ok(aux) = self.file.symbols.aux_section(self.index) { + u64::from(aux.length.get(LE)) + } else { + 0 + } + } else { + 0 + } + } + pe::IMAGE_SYM_CLASS_EXTERNAL => { + if self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED { + // For undefined symbols, symbol.value is 0 and the size is 0. + // For common data, symbol.value is the size. + u64::from(self.symbol.value()) + } else if self.symbol.has_aux_function() { + // Function symbols may have a size. + if let Ok(aux) = self.file.symbols.aux_function(self.index) { + u64::from(aux.total_size.get(LE)) + } else { + 0 + } + } else { + 0 + } + } + // Most symbols don't have sizes. + _ => 0, + } + } + + fn kind(&self) -> SymbolKind { + let derived_kind = if self.symbol.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION { + SymbolKind::Text + } else { + SymbolKind::Data + }; + match self.symbol.storage_class() { + pe::IMAGE_SYM_CLASS_STATIC => { + if self.symbol.has_aux_section() { + SymbolKind::Section + } else { + derived_kind + } + } + pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => derived_kind, + pe::IMAGE_SYM_CLASS_SECTION => SymbolKind::Section, + pe::IMAGE_SYM_CLASS_FILE => SymbolKind::File, + pe::IMAGE_SYM_CLASS_LABEL => SymbolKind::Label, + _ => SymbolKind::Unknown, + } + } + + fn section(&self) -> SymbolSection { + match self.symbol.section_number() { + pe::IMAGE_SYM_UNDEFINED => { + if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL { + if self.symbol.value() == 0 { + SymbolSection::Undefined + } else { + SymbolSection::Common + } + } else if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_SECTION { + SymbolSection::Undefined + } else { + SymbolSection::Unknown + } + } + pe::IMAGE_SYM_ABSOLUTE => SymbolSection::Absolute, + pe::IMAGE_SYM_DEBUG => { + if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_FILE { + SymbolSection::None + } else { + SymbolSection::Unknown + } + } + index if index > 0 => SymbolSection::Section(SectionIndex(index as usize)), + _ => SymbolSection::Unknown, + } + } + + #[inline] + fn is_undefined(&self) -> bool { + self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL + && self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED + && self.symbol.value() == 0 + } + + #[inline] + fn is_definition(&self) -> bool { + self.symbol.is_definition() + } + + #[inline] + fn is_common(&self) -> bool { + self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL + && self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED + && self.symbol.value() != 0 + } + + #[inline] + fn is_weak(&self) -> bool { + self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL + } + + #[inline] + fn scope(&self) -> SymbolScope { + match self.symbol.storage_class() { + pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => { + // TODO: determine if symbol is exported + SymbolScope::Linkage + } + _ => SymbolScope::Compilation, + } + } + + #[inline] + fn is_global(&self) -> bool { + match self.symbol.storage_class() { + pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true, + _ => false, + } + } + + #[inline] + fn is_local(&self) -> bool { + !self.is_global() + } + + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { + if self.symbol.has_aux_section() { + if let Ok(aux) = self.file.symbols.aux_section(self.index) { + let number = if Coff::is_type_bigobj() { + u32::from(aux.number.get(LE)) | (u32::from(aux.high_number.get(LE)) << 16) + } else { + u32::from(aux.number.get(LE)) + }; + return SymbolFlags::CoffSection { + selection: aux.selection, + associative_section: if number == 0 { + None + } else { + Some(SectionIndex(number as usize)) + }, + }; + } + } + SymbolFlags::None + } +} + +/// A trait for generic access to [`pe::ImageSymbol`] and [`pe::ImageSymbolEx`]. +#[allow(missing_docs)] +pub trait ImageSymbol: Debug + Pod { + fn raw_name(&self) -> &[u8; 8]; + fn value(&self) -> u32; + fn section_number(&self) -> i32; + fn typ(&self) -> u16; + fn storage_class(&self) -> u8; + fn number_of_aux_symbols(&self) -> u8; + + /// Parse a COFF symbol name. + /// + /// `strings` must be the string table used for symbol names. + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + let name = self.raw_name(); + if name[0] == 0 { + // If the name starts with 0 then the last 4 bytes are a string table offset. + let offset = u32::from_le_bytes(name[4..8].try_into().unwrap()); + strings + .get(offset) + .read_error("Invalid COFF symbol name offset") + } else { + // The name is inline and padded with nulls. + Ok(match memchr::memchr(b'\0', name) { + Some(end) => &name[..end], + None => &name[..], + }) + } + } + + /// Return the symbol address. + /// + /// This takes into account the image base and the section address, + /// and only returns an address for symbols that have an address. + fn address(&self, image_base: u64, sections: &SectionTable<'_>) -> Result<Option<u64>> { + // Only return an address for storage classes that we know use an address. + match self.storage_class() { + pe::IMAGE_SYM_CLASS_STATIC + | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL + | pe::IMAGE_SYM_CLASS_LABEL + | pe::IMAGE_SYM_CLASS_EXTERNAL => {} + _ => return Ok(None), + } + let Some(section_index) = self.section() else { + return Ok(None); + }; + let section = sections.section(section_index)?; + let virtual_address = u64::from(section.virtual_address.get(LE)); + let value = u64::from(self.value()); + Ok(Some(image_base + virtual_address + value)) + } + + /// Return the section index for the symbol. + fn section(&self) -> Option<SectionIndex> { + let section_number = self.section_number(); + if section_number > 0 { + Some(SectionIndex(section_number as usize)) + } else { + None + } + } + + /// Return true if the symbol is a definition of a function or data object. + fn is_definition(&self) -> bool { + if self.section_number() <= 0 { + return false; + } + match self.storage_class() { + pe::IMAGE_SYM_CLASS_STATIC => !self.has_aux_section(), + pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true, + _ => false, + } + } + + /// Return true if the symbol has an auxiliary file name. + fn has_aux_file_name(&self) -> bool { + self.number_of_aux_symbols() > 0 && self.storage_class() == pe::IMAGE_SYM_CLASS_FILE + } + + /// Return true if the symbol has an auxiliary function symbol. + fn has_aux_function(&self) -> bool { + self.number_of_aux_symbols() > 0 + && self.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION + && (self.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL + || self.storage_class() == pe::IMAGE_SYM_CLASS_STATIC) + } + + /// Return true if the symbol has an auxiliary section symbol. + fn has_aux_section(&self) -> bool { + self.number_of_aux_symbols() > 0 + && self.storage_class() == pe::IMAGE_SYM_CLASS_STATIC + && self.typ() == 0 + } + + /// Return true if the symbol has an auxiliary weak external symbol. + fn has_aux_weak_external(&self) -> bool { + self.number_of_aux_symbols() > 0 + && self.storage_class() == pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL + && self.section_number() == pe::IMAGE_SYM_UNDEFINED + && self.value() == 0 + } + + fn base_type(&self) -> u16 { + self.typ() & pe::N_BTMASK + } + + fn derived_type(&self) -> u16 { + (self.typ() & pe::N_TMASK) >> pe::N_BTSHFT + } +} + +impl ImageSymbol for pe::ImageSymbol { + fn raw_name(&self) -> &[u8; 8] { + &self.name + } + fn value(&self) -> u32 { + self.value.get(LE) + } + fn section_number(&self) -> i32 { + let section_number = self.section_number.get(LE); + if section_number >= pe::IMAGE_SYM_SECTION_MAX { + (section_number as i16) as i32 + } else { + section_number as i32 + } + } + fn typ(&self) -> u16 { + self.typ.get(LE) + } + fn storage_class(&self) -> u8 { + self.storage_class + } + fn number_of_aux_symbols(&self) -> u8 { + self.number_of_aux_symbols + } +} + +impl ImageSymbol for pe::ImageSymbolEx { + fn raw_name(&self) -> &[u8; 8] { + &self.name + } + fn value(&self) -> u32 { + self.value.get(LE) + } + fn section_number(&self) -> i32 { + self.section_number.get(LE) + } + fn typ(&self) -> u16 { + self.typ.get(LE) + } + fn storage_class(&self) -> u8 { + self.storage_class + } + fn number_of_aux_symbols(&self) -> u8 { + self.number_of_aux_symbols + } +} + +impl pe::ImageAuxSymbolWeak { + /// Get the symbol index of the default definition. + pub fn default_symbol(&self) -> SymbolIndex { + SymbolIndex(self.weak_default_sym_index.get(LE) as usize) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/attributes.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/attributes.rs new file mode 100644 index 0000000..39bc221 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/attributes.rs
@@ -0,0 +1,340 @@ +use core::convert::TryInto; + +use crate::elf; +use crate::endian; +use crate::read::{Bytes, Error, ReadError, Result}; + +use super::FileHeader; + +/// An ELF attributes section. +/// +/// This may be a GNU attributes section, or an architecture specific attributes section. +/// +/// An attributes section contains a series of [`AttributesSubsection`]. +/// +/// Returned by [`SectionHeader::attributes`](super::SectionHeader::attributes) +/// and [`SectionHeader::gnu_attributes`](super::SectionHeader::gnu_attributes). +#[derive(Debug, Clone)] +pub struct AttributesSection<'data, Elf: FileHeader> { + endian: Elf::Endian, + version: u8, + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> AttributesSection<'data, Elf> { + /// Parse an ELF attributes section given the section data. + pub fn new(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> { + let mut data = Bytes(data); + + // Skip the version field that is one byte long. + // If the section is empty then the version doesn't matter. + let version = data.read::<u8>().cloned().unwrap_or(b'A'); + + Ok(AttributesSection { + endian, + version, + data, + }) + } + + /// Return the version of the attributes section. + pub fn version(&self) -> u8 { + self.version + } + + /// Return an iterator over the subsections. + pub fn subsections(&self) -> Result<AttributesSubsectionIterator<'data, Elf>> { + // There is currently only one format version. + if self.version != b'A' { + return Err(Error("Unsupported ELF attributes section version")); + } + + Ok(AttributesSubsectionIterator { + endian: self.endian, + data: self.data, + }) + } +} + +/// An iterator for the subsections in an [`AttributesSection`]. +#[derive(Debug, Clone)] +pub struct AttributesSubsectionIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> AttributesSubsectionIterator<'data, Elf> { + /// Return the next subsection. + pub fn next(&mut self) -> Result<Option<AttributesSubsection<'data, Elf>>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> Result<AttributesSubsection<'data, Elf>> { + // First read the subsection length. + let mut data = self.data; + let length = data + .read::<endian::U32Bytes<Elf::Endian>>() + .read_error("ELF attributes section is too short")? + .get(self.endian); + + // Now read the entire subsection, updating self.data. + let mut data = self + .data + .read_bytes(length as usize) + .read_error("Invalid ELF attributes subsection length")?; + // Skip the subsection length field. + data.skip(4) + .read_error("Invalid ELF attributes subsection length")?; + + // TODO: errors here should not prevent reading the next subsection. + let vendor = data + .read_string() + .read_error("Invalid ELF attributes vendor")?; + + Ok(AttributesSubsection { + endian: self.endian, + length, + vendor, + data, + }) + } +} + +impl<'data, Elf: FileHeader> Iterator for AttributesSubsectionIterator<'data, Elf> { + type Item = Result<AttributesSubsection<'data, Elf>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// A subsection in an [`AttributesSection`]. +/// +/// A subsection is identified by a vendor name. It contains a series of +/// [`AttributesSubsubsection`]. +#[derive(Debug, Clone)] +pub struct AttributesSubsection<'data, Elf: FileHeader> { + endian: Elf::Endian, + length: u32, + vendor: &'data [u8], + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> AttributesSubsection<'data, Elf> { + /// Return the length of the attributes subsection. + pub fn length(&self) -> u32 { + self.length + } + + /// Return the vendor name of the attributes subsection. + pub fn vendor(&self) -> &'data [u8] { + self.vendor + } + + /// Return an iterator over the sub-subsections. + pub fn subsubsections(&self) -> AttributesSubsubsectionIterator<'data, Elf> { + AttributesSubsubsectionIterator { + endian: self.endian, + data: self.data, + } + } +} + +/// An iterator for the sub-subsections in an [`AttributesSubsection`]. +#[derive(Debug, Clone)] +pub struct AttributesSubsubsectionIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> AttributesSubsubsectionIterator<'data, Elf> { + /// Return the next sub-subsection. + pub fn next(&mut self) -> Result<Option<AttributesSubsubsection<'data>>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> Result<AttributesSubsubsection<'data>> { + // The format of a sub-section looks like this: + // + // <file-tag> <size> <attribute>* + // | <section-tag> <size> <section-number>* 0 <attribute>* + // | <symbol-tag> <size> <symbol-number>* 0 <attribute>* + let mut data = self.data; + let tag = *data + .read::<u8>() + .read_error("ELF attributes subsection is too short")?; + let length = data + .read::<endian::U32Bytes<Elf::Endian>>() + .read_error("ELF attributes subsection is too short")? + .get(self.endian); + + // Now read the entire sub-subsection, updating self.data. + let mut data = self + .data + .read_bytes(length as usize) + .read_error("Invalid ELF attributes sub-subsection length")?; + // Skip the tag and sub-subsection size field. + data.skip(1 + 4) + .read_error("Invalid ELF attributes sub-subsection length")?; + + // TODO: errors here should not prevent reading the next sub-subsection. + let indices = if tag == elf::Tag_Section || tag == elf::Tag_Symbol { + data.read_string() + .map(Bytes) + .read_error("Missing ELF attributes sub-subsection indices")? + } else if tag == elf::Tag_File { + Bytes(&[]) + } else { + return Err(Error("Unimplemented ELF attributes sub-subsection tag")); + }; + + Ok(AttributesSubsubsection { + tag, + length, + indices, + data, + }) + } +} + +impl<'data, Elf: FileHeader> Iterator for AttributesSubsubsectionIterator<'data, Elf> { + type Item = Result<AttributesSubsubsection<'data>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// A sub-subsection in an [`AttributesSubsection`]. +/// +/// A sub-subsection is identified by a tag. It contains an optional series of indices, +/// followed by a series of attributes. +#[derive(Debug, Clone)] +pub struct AttributesSubsubsection<'data> { + tag: u8, + length: u32, + indices: Bytes<'data>, + data: Bytes<'data>, +} + +impl<'data> AttributesSubsubsection<'data> { + /// Return the tag of the attributes sub-subsection. + pub fn tag(&self) -> u8 { + self.tag + } + + /// Return the length of the attributes sub-subsection. + pub fn length(&self) -> u32 { + self.length + } + + /// Return the data containing the indices. + pub fn indices_data(&self) -> &'data [u8] { + self.indices.0 + } + + /// Return the indices. + /// + /// This will be section indices if the tag is `Tag_Section`, + /// or symbol indices if the tag is `Tag_Symbol`, + /// and otherwise it will be empty. + pub fn indices(&self) -> AttributeIndexIterator<'data> { + AttributeIndexIterator { data: self.indices } + } + + /// Return the data containing the attributes. + pub fn attributes_data(&self) -> &'data [u8] { + self.data.0 + } + + /// Return a parser for the data containing the attributes. + pub fn attributes(&self) -> AttributeReader<'data> { + AttributeReader { data: self.data } + } +} + +/// An iterator over the indices in an [`AttributesSubsubsection`]. +#[derive(Debug, Clone)] +pub struct AttributeIndexIterator<'data> { + data: Bytes<'data>, +} + +impl<'data> AttributeIndexIterator<'data> { + /// Parse the next index. + pub fn next(&mut self) -> Result<Option<u32>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> Result<u32> { + let err = "Invalid ELF attribute index"; + self.data + .read_uleb128() + .read_error(err)? + .try_into() + .map_err(|_| ()) + .read_error(err) + } +} + +impl<'data> Iterator for AttributeIndexIterator<'data> { + type Item = Result<u32>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// A parser for the attributes in an [`AttributesSubsubsection`]. +/// +/// The parser relies on the caller to know the format of the data for each attribute tag. +#[derive(Debug, Clone)] +pub struct AttributeReader<'data> { + data: Bytes<'data>, +} + +impl<'data> AttributeReader<'data> { + /// Parse a tag. + pub fn read_tag(&mut self) -> Result<Option<u64>> { + if self.data.is_empty() { + return Ok(None); + } + let err = "Invalid ELF attribute tag"; + self.data.read_uleb128().read_error(err).map(Some) + } + + /// Parse an integer value. + pub fn read_integer(&mut self) -> Result<u64> { + let err = "Invalid ELF attribute integer value"; + self.data.read_uleb128().read_error(err) + } + + /// Parse a string value. + pub fn read_string(&mut self) -> Result<&'data [u8]> { + let err = "Invalid ELF attribute string value"; + self.data.read_string().read_error(err) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/comdat.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/comdat.rs new file mode 100644 index 0000000..cfeb04f --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/comdat.rs
@@ -0,0 +1,186 @@ +use core::fmt::Debug; +use core::{iter, slice, str}; + +use crate::elf; +use crate::endian::{Endianness, U32Bytes}; +use crate::read::{self, ComdatKind, ObjectComdat, ReadError, ReadRef, SectionIndex, SymbolIndex}; + +use super::{ElfFile, FileHeader, SectionHeader, Sym}; + +/// An iterator for the COMDAT section groups in an [`ElfFile32`](super::ElfFile32). +pub type ElfComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator for the COMDAT section groups in an [`ElfFile64`](super::ElfFile64). +pub type ElfComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator for the COMDAT section groups in an [`ElfFile`]. +#[derive(Debug)] +pub struct ElfComdatIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + file: &'file ElfFile<'data, Elf, R>, + iter: iter::Enumerate<slice::Iter<'data, Elf::SectionHeader>>, +} + +impl<'data, 'file, Elf, R> ElfComdatIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) fn new( + file: &'file ElfFile<'data, Elf, R>, + ) -> ElfComdatIterator<'data, 'file, Elf, R> { + let mut iter = file.sections.iter().enumerate(); + iter.next(); // Skip null section. + ElfComdatIterator { file, iter } + } +} + +impl<'data, 'file, Elf, R> Iterator for ElfComdatIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = ElfComdat<'data, 'file, Elf, R>; + + fn next(&mut self) -> Option<Self::Item> { + for (_index, section) in self.iter.by_ref() { + if let Some(comdat) = ElfComdat::parse(self.file, section) { + return Some(comdat); + } + } + None + } +} + +/// A COMDAT section group in an [`ElfFile32`](super::ElfFile32). +pub type ElfComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdat<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A COMDAT section group in an [`ElfFile64`](super::ElfFile64). +pub type ElfComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdat<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A COMDAT section group in an [`ElfFile`]. +/// +/// Most functionality is provided by the [`ObjectComdat`] trait implementation. +#[derive(Debug)] +pub struct ElfComdat<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + file: &'file ElfFile<'data, Elf, R>, + section: &'data Elf::SectionHeader, + sections: &'data [U32Bytes<Elf::Endian>], +} + +impl<'data, 'file, Elf, R> ElfComdat<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + fn parse( + file: &'file ElfFile<'data, Elf, R>, + section: &'data Elf::SectionHeader, + ) -> Option<ElfComdat<'data, 'file, Elf, R>> { + let (flag, sections) = section.group(file.endian, file.data).ok()??; + if flag != elf::GRP_COMDAT { + return None; + } + Some(ElfComdat { + file, + section, + sections, + }) + } + + /// Get the ELF file containing this COMDAT section group. + pub fn elf_file(&self) -> &'file ElfFile<'data, Elf, R> { + self.file + } + + /// Get the raw ELF section header for the COMDAT section group. + pub fn elf_section_header(&self) -> &'data Elf::SectionHeader { + self.section + } +} + +impl<'data, 'file, Elf, R> read::private::Sealed for ElfComdat<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Elf, R> ObjectComdat<'data> for ElfComdat<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type SectionIterator = ElfComdatSectionIterator<'data, 'file, Elf, R>; + + #[inline] + fn kind(&self) -> ComdatKind { + ComdatKind::Any + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + SymbolIndex(self.section.sh_info(self.file.endian) as usize) + } + + fn name_bytes(&self) -> read::Result<&'data [u8]> { + // FIXME: check sh_link + let index = self.symbol(); + let symbol = self.file.symbols.symbol(index)?; + symbol.name(self.file.endian, self.file.symbols.strings()) + } + + fn name(&self) -> read::Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 ELF COMDAT name") + } + + fn sections(&self) -> Self::SectionIterator { + ElfComdatSectionIterator { + file: self.file, + sections: self.sections.iter(), + } + } +} + +/// An iterator for the sections in a COMDAT section group in an [`ElfFile32`](super::ElfFile32). +pub type ElfComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatSectionIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator for the sections in a COMDAT section group in an [`ElfFile64`](super::ElfFile64). +pub type ElfComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatSectionIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator for the sections in a COMDAT section group in an [`ElfFile`]. +#[derive(Debug)] +pub struct ElfComdatSectionIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + file: &'file ElfFile<'data, Elf, R>, + sections: slice::Iter<'data, U32Bytes<Elf::Endian>>, +} + +impl<'data, 'file, Elf, R> Iterator for ElfComdatSectionIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + let index = self.sections.next()?; + Some(SectionIndex(index.get(self.file.endian) as usize)) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/compression.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/compression.rs new file mode 100644 index 0000000..de2533f --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/compression.rs
@@ -0,0 +1,56 @@ +use core::fmt::Debug; + +use crate::elf; +use crate::endian; +use crate::pod::Pod; + +/// A trait for generic access to [`elf::CompressionHeader32`] and [`elf::CompressionHeader64`]. +#[allow(missing_docs)] +pub trait CompressionHeader: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn ch_type(&self, endian: Self::Endian) -> u32; + fn ch_size(&self, endian: Self::Endian) -> Self::Word; + fn ch_addralign(&self, endian: Self::Endian) -> Self::Word; +} + +impl<Endian: endian::Endian> CompressionHeader for elf::CompressionHeader32<Endian> { + type Word = u32; + type Endian = Endian; + + #[inline] + fn ch_type(&self, endian: Self::Endian) -> u32 { + self.ch_type.get(endian) + } + + #[inline] + fn ch_size(&self, endian: Self::Endian) -> Self::Word { + self.ch_size.get(endian) + } + + #[inline] + fn ch_addralign(&self, endian: Self::Endian) -> Self::Word { + self.ch_addralign.get(endian) + } +} + +impl<Endian: endian::Endian> CompressionHeader for elf::CompressionHeader64<Endian> { + type Word = u64; + type Endian = Endian; + + #[inline] + fn ch_type(&self, endian: Self::Endian) -> u32 { + self.ch_type.get(endian) + } + + #[inline] + fn ch_size(&self, endian: Self::Endian) -> Self::Word { + self.ch_size.get(endian) + } + + #[inline] + fn ch_addralign(&self, endian: Self::Endian) -> Self::Word { + self.ch_addralign.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/dynamic.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/dynamic.rs new file mode 100644 index 0000000..1661434 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/dynamic.rs
@@ -0,0 +1,117 @@ +use core::convert::TryInto; +use core::fmt::Debug; + +use crate::elf; +use crate::endian; +use crate::pod::Pod; +use crate::read::{ReadError, Result, StringTable}; + +/// A trait for generic access to [`elf::Dyn32`] and [`elf::Dyn64`]. +#[allow(missing_docs)] +pub trait Dyn: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn d_tag(&self, endian: Self::Endian) -> Self::Word; + fn d_val(&self, endian: Self::Endian) -> Self::Word; + + /// Try to convert the tag to a `u32`. + fn tag32(&self, endian: Self::Endian) -> Option<u32> { + self.d_tag(endian).into().try_into().ok() + } + + /// Try to convert the value to a `u32`. + fn val32(&self, endian: Self::Endian) -> Option<u32> { + self.d_val(endian).into().try_into().ok() + } + + /// Return true if the value is an offset in the dynamic string table. + fn is_string(&self, endian: Self::Endian) -> bool { + if let Some(tag) = self.tag32(endian) { + match tag { + elf::DT_NEEDED + | elf::DT_SONAME + | elf::DT_RPATH + | elf::DT_RUNPATH + | elf::DT_AUXILIARY + | elf::DT_FILTER => true, + _ => false, + } + } else { + false + } + } + + /// Use the value to get a string in a string table. + /// + /// Does not check for an appropriate tag. + fn string<'data>( + &self, + endian: Self::Endian, + strings: StringTable<'data>, + ) -> Result<&'data [u8]> { + self.val32(endian) + .and_then(|val| strings.get(val).ok()) + .read_error("Invalid ELF dyn string") + } + + /// Return true if the value is an address. + fn is_address(&self, endian: Self::Endian) -> bool { + if let Some(tag) = self.tag32(endian) { + match tag { + elf::DT_PLTGOT + | elf::DT_HASH + | elf::DT_STRTAB + | elf::DT_SYMTAB + | elf::DT_RELA + | elf::DT_INIT + | elf::DT_FINI + | elf::DT_SYMBOLIC + | elf::DT_REL + | elf::DT_DEBUG + | elf::DT_JMPREL + | elf::DT_FINI_ARRAY + | elf::DT_INIT_ARRAY + | elf::DT_PREINIT_ARRAY + | elf::DT_SYMTAB_SHNDX + | elf::DT_VERDEF + | elf::DT_VERNEED + | elf::DT_VERSYM + | elf::DT_ADDRRNGLO..=elf::DT_ADDRRNGHI => true, + _ => false, + } + } else { + false + } + } +} + +impl<Endian: endian::Endian> Dyn for elf::Dyn32<Endian> { + type Word = u32; + type Endian = Endian; + + #[inline] + fn d_tag(&self, endian: Self::Endian) -> Self::Word { + self.d_tag.get(endian) + } + + #[inline] + fn d_val(&self, endian: Self::Endian) -> Self::Word { + self.d_val.get(endian) + } +} + +impl<Endian: endian::Endian> Dyn for elf::Dyn64<Endian> { + type Word = u64; + type Endian = Endian; + + #[inline] + fn d_tag(&self, endian: Self::Endian) -> Self::Word { + self.d_tag.get(endian) + } + + #[inline] + fn d_val(&self, endian: Self::Endian) -> Self::Word { + self.d_val.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/file.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/file.rs new file mode 100644 index 0000000..b464b10 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/file.rs
@@ -0,0 +1,1015 @@ +use alloc::vec::Vec; +use core::convert::TryInto; +use core::fmt::Debug; +use core::mem; + +use crate::elf; +use crate::endian::{self, Endian, Endianness, U32}; +use crate::pod::Pod; +use crate::read::{ + self, util, Architecture, ByteString, Bytes, Error, Export, FileFlags, Import, Object, + ObjectKind, ReadError, ReadRef, SectionIndex, StringTable, SymbolIndex, +}; + +use super::{ + CompressionHeader, Dyn, ElfComdat, ElfComdatIterator, ElfDynamicRelocationIterator, ElfSection, + ElfSectionIterator, ElfSegment, ElfSegmentIterator, ElfSymbol, ElfSymbolIterator, + ElfSymbolTable, NoteHeader, ProgramHeader, Rel, Rela, RelocationSections, Relr, SectionHeader, + SectionTable, Sym, SymbolTable, +}; + +/// A 32-bit ELF object file. +/// +/// This is a file that starts with [`elf::FileHeader32`], and corresponds +/// to [`crate::FileKind::Elf32`]. +pub type ElfFile32<'data, Endian = Endianness, R = &'data [u8]> = + ElfFile<'data, elf::FileHeader32<Endian>, R>; +/// A 64-bit ELF object file. +/// +/// This is a file that starts with [`elf::FileHeader64`], and corresponds +/// to [`crate::FileKind::Elf64`]. +pub type ElfFile64<'data, Endian = Endianness, R = &'data [u8]> = + ElfFile<'data, elf::FileHeader64<Endian>, R>; + +/// A partially parsed ELF file. +/// +/// Most functionality is provided by the [`Object`] trait implementation. +#[derive(Debug)] +pub struct ElfFile<'data, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Elf::Endian, + pub(super) data: R, + pub(super) header: &'data Elf, + pub(super) segments: &'data [Elf::ProgramHeader], + pub(super) sections: SectionTable<'data, Elf, R>, + pub(super) relocations: RelocationSections, + pub(super) symbols: SymbolTable<'data, Elf, R>, + pub(super) dynamic_symbols: SymbolTable<'data, Elf, R>, +} + +impl<'data, Elf, R> ElfFile<'data, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + /// Parse the raw ELF file data. + pub fn parse(data: R) -> read::Result<Self> { + let header = Elf::parse(data)?; + let endian = header.endian()?; + let segments = header.program_headers(endian, data)?; + let sections = header.sections(endian, data)?; + let symbols = sections.symbols(endian, data, elf::SHT_SYMTAB)?; + // TODO: get dynamic symbols from DT_SYMTAB if there are no sections + let dynamic_symbols = sections.symbols(endian, data, elf::SHT_DYNSYM)?; + // The API we provide requires a mapping from section to relocations, so build it now. + let relocations = sections.relocation_sections(endian, symbols.section())?; + + Ok(ElfFile { + endian, + data, + header, + segments, + sections, + relocations, + symbols, + dynamic_symbols, + }) + } + + /// Returns the endianness. + pub fn endian(&self) -> Elf::Endian { + self.endian + } + + /// Returns the raw data. + pub fn data(&self) -> R { + self.data + } + + /// Returns the raw ELF file header. + #[deprecated(note = "Use `elf_header` instead")] + pub fn raw_header(&self) -> &'data Elf { + self.header + } + + /// Returns the raw ELF segments. + #[deprecated(note = "Use `elf_program_headers` instead")] + pub fn raw_segments(&self) -> &'data [Elf::ProgramHeader] { + self.segments + } + + /// Get the raw ELF file header. + pub fn elf_header(&self) -> &'data Elf { + self.header + } + + /// Get the raw ELF program headers. + /// + /// Returns an empty slice if the file has no program headers. + pub fn elf_program_headers(&self) -> &'data [Elf::ProgramHeader] { + self.segments + } + + /// Get the ELF section table. + /// + /// Returns an empty section table if the file has no section headers. + pub fn elf_section_table(&self) -> &SectionTable<'data, Elf, R> { + &self.sections + } + + /// Get the ELF symbol table. + /// + /// Returns an empty symbol table if the file has no symbol table. + pub fn elf_symbol_table(&self) -> &SymbolTable<'data, Elf, R> { + &self.symbols + } + + /// Get the ELF dynamic symbol table. + /// + /// Returns an empty symbol table if the file has no dynamic symbol table. + pub fn elf_dynamic_symbol_table(&self) -> &SymbolTable<'data, Elf, R> { + &self.dynamic_symbols + } + + /// Get a mapping for linked relocation sections. + pub fn elf_relocation_sections(&self) -> &RelocationSections { + &self.relocations + } + + fn raw_section_by_name<'file>( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + self.sections + .section_by_name(self.endian, section_name) + .map(|(index, section)| ElfSection { + file: self, + index, + section, + }) + } + + #[cfg(feature = "compression")] + fn zdebug_section_by_name<'file>( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + if !section_name.starts_with(b".debug_") { + return None; + } + let mut name = Vec::with_capacity(section_name.len() + 1); + name.extend_from_slice(b".zdebug_"); + name.extend_from_slice(§ion_name[7..]); + self.raw_section_by_name(&name) + } + + #[cfg(not(feature = "compression"))] + fn zdebug_section_by_name<'file>( + &'file self, + _section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + None + } +} + +impl<'data, Elf, R> read::private::Sealed for ElfFile<'data, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, Elf, R> Object<'data> for ElfFile<'data, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Segment<'file> + = ElfSegment<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type SegmentIterator<'file> + = ElfSegmentIterator<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type Section<'file> + = ElfSection<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type SectionIterator<'file> + = ElfSectionIterator<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type Comdat<'file> + = ElfComdat<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type ComdatIterator<'file> + = ElfComdatIterator<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type Symbol<'file> + = ElfSymbol<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type SymbolIterator<'file> + = ElfSymbolIterator<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type SymbolTable<'file> + = ElfSymbolTable<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + type DynamicRelocationIterator<'file> + = ElfDynamicRelocationIterator<'data, 'file, Elf, R> + where + Self: 'file, + 'data: 'file; + + fn architecture(&self) -> Architecture { + match ( + self.header.e_machine(self.endian), + self.header.is_class_64(), + ) { + (elf::EM_AARCH64, true) => Architecture::Aarch64, + (elf::EM_AARCH64, false) => Architecture::Aarch64_Ilp32, + (elf::EM_ALPHA, true) => Architecture::Alpha, + (elf::EM_ARM, _) => Architecture::Arm, + (elf::EM_AVR, _) => Architecture::Avr, + (elf::EM_BPF, _) => Architecture::Bpf, + (elf::EM_CSKY, _) => Architecture::Csky, + (elf::EM_MCST_ELBRUS, false) => Architecture::E2K32, + (elf::EM_MCST_ELBRUS, true) => Architecture::E2K64, + (elf::EM_386, _) => Architecture::I386, + (elf::EM_X86_64, false) => Architecture::X86_64_X32, + (elf::EM_X86_64, true) => Architecture::X86_64, + (elf::EM_HEXAGON, _) => Architecture::Hexagon, + (elf::EM_LOONGARCH, false) => Architecture::LoongArch32, + (elf::EM_LOONGARCH, true) => Architecture::LoongArch64, + (elf::EM_68K, false) => Architecture::M68k, + (elf::EM_MIPS, false) => { + if (self.header.e_flags(self.endian) & elf::EF_MIPS_ABI2) != 0 { + Architecture::Mips64_N32 + } else { + Architecture::Mips + } + } + (elf::EM_MIPS, true) => Architecture::Mips64, + (elf::EM_MSP430, _) => Architecture::Msp430, + (elf::EM_PARISC, _) => Architecture::Hppa, + (elf::EM_PPC, _) => Architecture::PowerPc, + (elf::EM_PPC64, _) => Architecture::PowerPc64, + (elf::EM_RISCV, false) => Architecture::Riscv32, + (elf::EM_RISCV, true) => Architecture::Riscv64, + // This is either s390 or s390x, depending on the ELF class. + // We only support the 64-bit variant s390x here. + (elf::EM_S390, true) => Architecture::S390x, + (elf::EM_SBF, _) => Architecture::Sbf, + (elf::EM_SHARC, false) => Architecture::Sharc, + (elf::EM_SPARC, false) => Architecture::Sparc, + (elf::EM_SPARC32PLUS, false) => Architecture::Sparc32Plus, + (elf::EM_SPARCV9, true) => Architecture::Sparc64, + (elf::EM_XTENSA, false) => Architecture::Xtensa, + (elf::EM_SH, false) => Architecture::SuperH, + _ => Architecture::Unknown, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + self.header.is_little_endian() + } + + #[inline] + fn is_64(&self) -> bool { + self.header.is_class_64() + } + + fn kind(&self) -> ObjectKind { + match self.header.e_type(self.endian) { + elf::ET_REL => ObjectKind::Relocatable, + elf::ET_EXEC => ObjectKind::Executable, + // TODO: check for `DF_1_PIE`? + elf::ET_DYN => ObjectKind::Dynamic, + elf::ET_CORE => ObjectKind::Core, + _ => ObjectKind::Unknown, + } + } + + fn segments(&self) -> ElfSegmentIterator<'data, '_, Elf, R> { + ElfSegmentIterator { + file: self, + iter: self.segments.iter(), + } + } + + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + self.raw_section_by_name(section_name) + .or_else(|| self.zdebug_section_by_name(section_name)) + } + + fn section_by_index(&self, index: SectionIndex) -> read::Result<ElfSection<'data, '_, Elf, R>> { + let section = self.sections.section(index)?; + Ok(ElfSection { + file: self, + index, + section, + }) + } + + fn sections(&self) -> ElfSectionIterator<'data, '_, Elf, R> { + ElfSectionIterator::new(self) + } + + fn comdats(&self) -> ElfComdatIterator<'data, '_, Elf, R> { + ElfComdatIterator::new(self) + } + + fn symbol_by_index(&self, index: SymbolIndex) -> read::Result<ElfSymbol<'data, '_, Elf, R>> { + let symbol = self.symbols.symbol(index)?; + Ok(ElfSymbol { + endian: self.endian, + symbols: &self.symbols, + index, + symbol, + }) + } + + fn symbols(&self) -> ElfSymbolIterator<'data, '_, Elf, R> { + ElfSymbolIterator::new(self.endian, &self.symbols) + } + + fn symbol_table(&self) -> Option<ElfSymbolTable<'data, '_, Elf, R>> { + if self.symbols.is_empty() { + return None; + } + Some(ElfSymbolTable { + endian: self.endian, + symbols: &self.symbols, + }) + } + + fn dynamic_symbols(&self) -> ElfSymbolIterator<'data, '_, Elf, R> { + ElfSymbolIterator::new(self.endian, &self.dynamic_symbols) + } + + fn dynamic_symbol_table(&self) -> Option<ElfSymbolTable<'data, '_, Elf, R>> { + if self.dynamic_symbols.is_empty() { + return None; + } + Some(ElfSymbolTable { + endian: self.endian, + symbols: &self.dynamic_symbols, + }) + } + + fn dynamic_relocations<'file>( + &'file self, + ) -> Option<ElfDynamicRelocationIterator<'data, 'file, Elf, R>> { + Some(ElfDynamicRelocationIterator { + section_index: SectionIndex(1), + file: self, + relocations: None, + }) + } + + fn imports(&self) -> read::Result<Vec<Import<'data>>> { + let versions = self.sections.versions(self.endian, self.data)?; + + let mut imports = Vec::new(); + for (index, symbol) in self.dynamic_symbols.enumerate() { + if symbol.is_undefined(self.endian) { + let name = symbol.name(self.endian, self.dynamic_symbols.strings())?; + if !name.is_empty() { + let library = if let Some(svt) = versions.as_ref() { + let vi = svt.version_index(self.endian, index); + svt.version(vi)?.and_then(|v| v.file()) + } else { + None + } + .unwrap_or(&[]); + imports.push(Import { + name: ByteString(name), + library: ByteString(library), + }); + } + } + } + Ok(imports) + } + + fn exports(&self) -> read::Result<Vec<Export<'data>>> { + let mut exports = Vec::new(); + for symbol in self.dynamic_symbols.iter() { + if symbol.is_definition(self.endian) { + let name = symbol.name(self.endian, self.dynamic_symbols.strings())?; + let address = symbol.st_value(self.endian).into(); + exports.push(Export { + name: ByteString(name), + address, + }); + } + } + Ok(exports) + } + + fn has_debug_symbols(&self) -> bool { + for section in self.sections.iter() { + if let Ok(name) = self.sections.section_name(self.endian, section) { + if name == b".debug_info" || name == b".zdebug_info" { + return true; + } + } + } + false + } + + fn build_id(&self) -> read::Result<Option<&'data [u8]>> { + let endian = self.endian; + // Use section headers if present, otherwise use program headers. + if !self.sections.is_empty() { + for section in self.sections.iter() { + if let Some(mut notes) = section.notes(endian, self.data)? { + while let Some(note) = notes.next()? { + if note.name() == elf::ELF_NOTE_GNU + && note.n_type(endian) == elf::NT_GNU_BUILD_ID + { + return Ok(Some(note.desc())); + } + } + } + } + } else { + for segment in self.segments { + if let Some(mut notes) = segment.notes(endian, self.data)? { + while let Some(note) = notes.next()? { + if note.name() == elf::ELF_NOTE_GNU + && note.n_type(endian) == elf::NT_GNU_BUILD_ID + { + return Ok(Some(note.desc())); + } + } + } + } + } + Ok(None) + } + + fn gnu_debuglink(&self) -> read::Result<Option<(&'data [u8], u32)>> { + let section = match self.raw_section_by_name(b".gnu_debuglink") { + Some(section) => section, + None => return Ok(None), + }; + let data = section + .section + .data(self.endian, self.data) + .read_error("Invalid ELF .gnu_debuglink section offset or size") + .map(Bytes)?; + let filename = data + .read_string_at(0) + .read_error("Missing ELF .gnu_debuglink filename")?; + let crc_offset = util::align(filename.len() + 1, 4); + let crc = data + .read_at::<U32<_>>(crc_offset) + .read_error("Missing ELF .gnu_debuglink crc")? + .get(self.endian); + Ok(Some((filename, crc))) + } + + fn gnu_debugaltlink(&self) -> read::Result<Option<(&'data [u8], &'data [u8])>> { + let section = match self.raw_section_by_name(b".gnu_debugaltlink") { + Some(section) => section, + None => return Ok(None), + }; + let mut data = section + .section + .data(self.endian, self.data) + .read_error("Invalid ELF .gnu_debugaltlink section offset or size") + .map(Bytes)?; + let filename = data + .read_string() + .read_error("Missing ELF .gnu_debugaltlink filename")?; + let build_id = data.0; + Ok(Some((filename, build_id))) + } + + fn relative_address_base(&self) -> u64 { + 0 + } + + fn entry(&self) -> u64 { + self.header.e_entry(self.endian).into() + } + + fn flags(&self) -> FileFlags { + FileFlags::Elf { + os_abi: self.header.e_ident().os_abi, + abi_version: self.header.e_ident().abi_version, + e_flags: self.header.e_flags(self.endian), + } + } +} + +/// A trait for generic access to [`elf::FileHeader32`] and [`elf::FileHeader64`]. +#[allow(missing_docs)] +pub trait FileHeader: Debug + Pod { + // Ideally this would be a `u64: From<Word>`, but can't express that. + type Word: Into<u64> + Default + Copy; + type Sword: Into<i64>; + type Endian: endian::Endian; + type ProgramHeader: ProgramHeader<Elf = Self, Endian = Self::Endian, Word = Self::Word>; + type SectionHeader: SectionHeader<Elf = Self, Endian = Self::Endian, Word = Self::Word>; + type CompressionHeader: CompressionHeader<Endian = Self::Endian, Word = Self::Word>; + type NoteHeader: NoteHeader<Endian = Self::Endian>; + type Dyn: Dyn<Endian = Self::Endian, Word = Self::Word>; + type Sym: Sym<Endian = Self::Endian, Word = Self::Word>; + type Rel: Rel<Endian = Self::Endian, Word = Self::Word>; + type Rela: Rela<Endian = Self::Endian, Word = Self::Word> + From<Self::Rel>; + type Relr: Relr<Endian = Self::Endian, Word = Self::Word>; + + /// Return true if this type is a 64-bit header. + /// + /// This is a property of the type, not a value in the header data. + fn is_type_64(&self) -> bool; + + /// Return true if this type is a 64-bit header. + /// + /// This is a property of the type, not a value in the header data. + /// + /// This is the same as [`Self::is_type_64`], but is non-dispatchable. + fn is_type_64_sized() -> bool + where + Self: Sized; + + fn e_ident(&self) -> &elf::Ident; + fn e_type(&self, endian: Self::Endian) -> u16; + fn e_machine(&self, endian: Self::Endian) -> u16; + fn e_version(&self, endian: Self::Endian) -> u32; + fn e_entry(&self, endian: Self::Endian) -> Self::Word; + fn e_phoff(&self, endian: Self::Endian) -> Self::Word; + fn e_shoff(&self, endian: Self::Endian) -> Self::Word; + fn e_flags(&self, endian: Self::Endian) -> u32; + fn e_ehsize(&self, endian: Self::Endian) -> u16; + fn e_phentsize(&self, endian: Self::Endian) -> u16; + fn e_phnum(&self, endian: Self::Endian) -> u16; + fn e_shentsize(&self, endian: Self::Endian) -> u16; + fn e_shnum(&self, endian: Self::Endian) -> u16; + fn e_shstrndx(&self, endian: Self::Endian) -> u16; + + // Provided methods. + + /// Read the file header. + /// + /// Also checks that the ident field in the file header is a supported format. + fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> { + let header = data + .read_at::<Self>(0) + .read_error("Invalid ELF header size or alignment")?; + if !header.is_supported() { + return Err(Error("Unsupported ELF header")); + } + // TODO: Check self.e_ehsize? + Ok(header) + } + + /// Check that the ident field in the file header is a supported format. + /// + /// This checks the magic number, version, class, and endianness. + fn is_supported(&self) -> bool { + let ident = self.e_ident(); + // TODO: Check self.e_version too? Requires endian though. + ident.magic == elf::ELFMAG + && (self.is_type_64() || self.is_class_32()) + && (!self.is_type_64() || self.is_class_64()) + && (self.is_little_endian() || self.is_big_endian()) + && ident.version == elf::EV_CURRENT + } + + fn is_class_32(&self) -> bool { + self.e_ident().class == elf::ELFCLASS32 + } + + fn is_class_64(&self) -> bool { + self.e_ident().class == elf::ELFCLASS64 + } + + fn is_little_endian(&self) -> bool { + self.e_ident().data == elf::ELFDATA2LSB + } + + fn is_big_endian(&self) -> bool { + self.e_ident().data == elf::ELFDATA2MSB + } + + fn endian(&self) -> read::Result<Self::Endian> { + Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported ELF endian") + } + + /// Return the first section header, if present. + /// + /// Section 0 is a special case because getting the section headers normally + /// requires `shnum`, but `shnum` may be in the first section header. + fn section_0<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<&'data Self::SectionHeader>> { + let shoff: u64 = self.e_shoff(endian).into(); + if shoff == 0 { + // No section headers is ok. + return Ok(None); + } + let shentsize = usize::from(self.e_shentsize(endian)); + if shentsize != mem::size_of::<Self::SectionHeader>() { + // Section header size must match. + return Err(Error("Invalid ELF section header entry size")); + } + data.read_at(shoff) + .map(Some) + .read_error("Invalid ELF section header offset or size") + } + + /// Return the `e_phnum` field of the header. Handles extended values. + /// + /// Returns `Err` for invalid values. + fn phnum<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<usize> { + let e_phnum = self.e_phnum(endian); + if e_phnum < elf::PN_XNUM { + Ok(e_phnum as usize) + } else if let Some(section_0) = self.section_0(endian, data)? { + Ok(section_0.sh_info(endian) as usize) + } else { + // Section 0 must exist if e_phnum overflows. + Err(Error("Missing ELF section headers for e_phnum overflow")) + } + } + + /// Return the `e_shnum` field of the header. Handles extended values. + /// + /// Returns `Err` for invalid values. + fn shnum<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<usize> { + let e_shnum = self.e_shnum(endian); + if e_shnum > 0 { + Ok(e_shnum as usize) + } else if let Some(section_0) = self.section_0(endian, data)? { + section_0 + .sh_size(endian) + .into() + .try_into() + .ok() + .read_error("Invalid ELF extended e_shnum") + } else { + // No section headers is ok. + Ok(0) + } + } + + /// Return the `e_shstrndx` field of the header. Handles extended values. + /// + /// Returns `Err` for invalid values (including if the index is 0). + fn shstrndx<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<u32> { + let e_shstrndx = self.e_shstrndx(endian); + let index = if e_shstrndx != elf::SHN_XINDEX { + e_shstrndx.into() + } else if let Some(section_0) = self.section_0(endian, data)? { + section_0.sh_link(endian) + } else { + // Section 0 must exist if we're trying to read e_shstrndx. + return Err(Error("Missing ELF section headers for e_shstrndx overflow")); + }; + if index == 0 { + return Err(Error("Missing ELF e_shstrndx")); + } + Ok(index) + } + + /// Return the slice of program headers. + /// + /// Returns `Ok(&[])` if there are no program headers. + /// Returns `Err` for invalid values. + fn program_headers<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<&'data [Self::ProgramHeader]> { + let phoff: u64 = self.e_phoff(endian).into(); + if phoff == 0 { + // No program headers is ok. + return Ok(&[]); + } + let phnum = self.phnum(endian, data)?; + if phnum == 0 { + // No program headers is ok. + return Ok(&[]); + } + let phentsize = self.e_phentsize(endian) as usize; + if phentsize != mem::size_of::<Self::ProgramHeader>() { + // Program header size must match. + return Err(Error("Invalid ELF program header entry size")); + } + data.read_slice_at(phoff, phnum) + .read_error("Invalid ELF program header size or alignment") + } + + /// Return the slice of section headers. + /// + /// Returns `Ok(&[])` if there are no section headers. + /// Returns `Err` for invalid values. + fn section_headers<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<&'data [Self::SectionHeader]> { + let shoff: u64 = self.e_shoff(endian).into(); + if shoff == 0 { + // No section headers is ok. + return Ok(&[]); + } + let shnum = self.shnum(endian, data)?; + if shnum == 0 { + // No section headers is ok. + return Ok(&[]); + } + let shentsize = usize::from(self.e_shentsize(endian)); + if shentsize != mem::size_of::<Self::SectionHeader>() { + // Section header size must match. + return Err(Error("Invalid ELF section header entry size")); + } + data.read_slice_at(shoff, shnum) + .read_error("Invalid ELF section header offset/size/alignment") + } + + /// Get the section index of the section header string table. + /// + /// Returns `Err` for invalid values (including if the index is 0). + fn section_strings_index<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<SectionIndex> { + self.shstrndx(endian, data) + .map(|index| SectionIndex(index as usize)) + } + + /// Return the string table for the section headers. + fn section_strings<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + sections: &[Self::SectionHeader], + ) -> read::Result<StringTable<'data, R>> { + if sections.is_empty() { + return Ok(StringTable::default()); + } + let index = self.section_strings_index(endian, data)?; + let shstrtab = sections.get(index.0).read_error("Invalid ELF e_shstrndx")?; + let strings = if let Some((shstrtab_offset, shstrtab_size)) = shstrtab.file_range(endian) { + let shstrtab_end = shstrtab_offset + .checked_add(shstrtab_size) + .read_error("Invalid ELF shstrtab size")?; + StringTable::new(data, shstrtab_offset, shstrtab_end) + } else { + StringTable::default() + }; + Ok(strings) + } + + /// Return the section table. + fn sections<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<SectionTable<'data, Self, R>> { + let sections = self.section_headers(endian, data)?; + let strings = self.section_strings(endian, data, sections)?; + Ok(SectionTable::new(sections, strings)) + } + + /// Returns whether this is a mips64el elf file. + fn is_mips64el(&self, endian: Self::Endian) -> bool { + self.is_class_64() && self.is_little_endian() && self.e_machine(endian) == elf::EM_MIPS + } +} + +impl<Endian: endian::Endian> FileHeader for elf::FileHeader32<Endian> { + type Word = u32; + type Sword = i32; + type Endian = Endian; + type ProgramHeader = elf::ProgramHeader32<Endian>; + type SectionHeader = elf::SectionHeader32<Endian>; + type CompressionHeader = elf::CompressionHeader32<Endian>; + type NoteHeader = elf::NoteHeader32<Endian>; + type Dyn = elf::Dyn32<Endian>; + type Sym = elf::Sym32<Endian>; + type Rel = elf::Rel32<Endian>; + type Rela = elf::Rela32<Endian>; + type Relr = elf::Relr32<Endian>; + + #[inline] + fn is_type_64(&self) -> bool { + false + } + + #[inline] + fn is_type_64_sized() -> bool + where + Self: Sized, + { + false + } + + #[inline] + fn e_ident(&self) -> &elf::Ident { + &self.e_ident + } + + #[inline] + fn e_type(&self, endian: Self::Endian) -> u16 { + self.e_type.get(endian) + } + + #[inline] + fn e_machine(&self, endian: Self::Endian) -> u16 { + self.e_machine.get(endian) + } + + #[inline] + fn e_version(&self, endian: Self::Endian) -> u32 { + self.e_version.get(endian) + } + + #[inline] + fn e_entry(&self, endian: Self::Endian) -> Self::Word { + self.e_entry.get(endian) + } + + #[inline] + fn e_phoff(&self, endian: Self::Endian) -> Self::Word { + self.e_phoff.get(endian) + } + + #[inline] + fn e_shoff(&self, endian: Self::Endian) -> Self::Word { + self.e_shoff.get(endian) + } + + #[inline] + fn e_flags(&self, endian: Self::Endian) -> u32 { + self.e_flags.get(endian) + } + + #[inline] + fn e_ehsize(&self, endian: Self::Endian) -> u16 { + self.e_ehsize.get(endian) + } + + #[inline] + fn e_phentsize(&self, endian: Self::Endian) -> u16 { + self.e_phentsize.get(endian) + } + + #[inline] + fn e_phnum(&self, endian: Self::Endian) -> u16 { + self.e_phnum.get(endian) + } + + #[inline] + fn e_shentsize(&self, endian: Self::Endian) -> u16 { + self.e_shentsize.get(endian) + } + + #[inline] + fn e_shnum(&self, endian: Self::Endian) -> u16 { + self.e_shnum.get(endian) + } + + #[inline] + fn e_shstrndx(&self, endian: Self::Endian) -> u16 { + self.e_shstrndx.get(endian) + } +} + +impl<Endian: endian::Endian> FileHeader for elf::FileHeader64<Endian> { + type Word = u64; + type Sword = i64; + type Endian = Endian; + type ProgramHeader = elf::ProgramHeader64<Endian>; + type SectionHeader = elf::SectionHeader64<Endian>; + type CompressionHeader = elf::CompressionHeader64<Endian>; + type NoteHeader = elf::NoteHeader32<Endian>; + type Dyn = elf::Dyn64<Endian>; + type Sym = elf::Sym64<Endian>; + type Rel = elf::Rel64<Endian>; + type Rela = elf::Rela64<Endian>; + type Relr = elf::Relr64<Endian>; + + #[inline] + fn is_type_64(&self) -> bool { + true + } + + #[inline] + fn is_type_64_sized() -> bool + where + Self: Sized, + { + true + } + + #[inline] + fn e_ident(&self) -> &elf::Ident { + &self.e_ident + } + + #[inline] + fn e_type(&self, endian: Self::Endian) -> u16 { + self.e_type.get(endian) + } + + #[inline] + fn e_machine(&self, endian: Self::Endian) -> u16 { + self.e_machine.get(endian) + } + + #[inline] + fn e_version(&self, endian: Self::Endian) -> u32 { + self.e_version.get(endian) + } + + #[inline] + fn e_entry(&self, endian: Self::Endian) -> Self::Word { + self.e_entry.get(endian) + } + + #[inline] + fn e_phoff(&self, endian: Self::Endian) -> Self::Word { + self.e_phoff.get(endian) + } + + #[inline] + fn e_shoff(&self, endian: Self::Endian) -> Self::Word { + self.e_shoff.get(endian) + } + + #[inline] + fn e_flags(&self, endian: Self::Endian) -> u32 { + self.e_flags.get(endian) + } + + #[inline] + fn e_ehsize(&self, endian: Self::Endian) -> u16 { + self.e_ehsize.get(endian) + } + + #[inline] + fn e_phentsize(&self, endian: Self::Endian) -> u16 { + self.e_phentsize.get(endian) + } + + #[inline] + fn e_phnum(&self, endian: Self::Endian) -> u16 { + self.e_phnum.get(endian) + } + + #[inline] + fn e_shentsize(&self, endian: Self::Endian) -> u16 { + self.e_shentsize.get(endian) + } + + #[inline] + fn e_shnum(&self, endian: Self::Endian) -> u16 { + self.e_shnum.get(endian) + } + + #[inline] + fn e_shstrndx(&self, endian: Self::Endian) -> u16 { + self.e_shstrndx.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/hash.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/hash.rs new file mode 100644 index 0000000..bbc7ae9 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/hash.rs
@@ -0,0 +1,236 @@ +use core::mem; + +use crate::elf; +use crate::endian::{U32, U64}; +use crate::read::{ReadError, ReadRef, Result, SymbolIndex}; + +use super::{FileHeader, Sym, SymbolTable, Version, VersionTable}; + +/// A SysV symbol hash table in an ELF file. +/// +/// Returned by [`SectionHeader::hash`](super::SectionHeader::hash). +#[derive(Debug)] +pub struct HashTable<'data, Elf: FileHeader> { + buckets: &'data [U32<Elf::Endian>], + chains: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader> HashTable<'data, Elf> { + /// Parse a SysV hash table. + /// + /// `data` should be from an [`elf::SHT_HASH`] section, or from a + /// segment pointed to via the [`elf::DT_HASH`] entry. + /// + /// The header is read at offset 0 in the given `data`. + pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> { + let mut offset = 0; + let header = data + .read::<elf::HashHeader<Elf::Endian>>(&mut offset) + .read_error("Invalid hash header")?; + let buckets = data + .read_slice(&mut offset, header.bucket_count.get(endian) as usize) + .read_error("Invalid hash buckets")?; + let chains = data + .read_slice(&mut offset, header.chain_count.get(endian) as usize) + .read_error("Invalid hash chains")?; + Ok(HashTable { buckets, chains }) + } + + /// Return the symbol table length. + pub fn symbol_table_length(&self) -> u32 { + self.chains.len() as u32 + } + + fn bucket(&self, endian: Elf::Endian, hash: u32) -> SymbolIndex { + SymbolIndex(self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize) + } + + fn chain(&self, endian: Elf::Endian, index: SymbolIndex) -> SymbolIndex { + SymbolIndex(self.chains[index.0].get(endian) as usize) + } + + /// Use the hash table to find the symbol table entry with the given name, hash and version. + pub fn find<R: ReadRef<'data>>( + &self, + endian: Elf::Endian, + name: &[u8], + hash: u32, + version: Option<&Version<'_>>, + symbols: &SymbolTable<'data, Elf, R>, + versions: &VersionTable<'data, Elf>, + ) -> Option<(SymbolIndex, &'data Elf::Sym)> { + // Get the chain start from the bucket for this hash. + let mut index = self.bucket(endian, hash); + // Avoid infinite loop. + let mut i = 0; + let strings = symbols.strings(); + while index != SymbolIndex(0) && i < self.chains.len() { + if let Ok(symbol) = symbols.symbol(index) { + if symbol.name(endian, strings) == Ok(name) + && versions.matches(endian, index, version) + { + return Some((index, symbol)); + } + } + index = self.chain(endian, index); + i += 1; + } + None + } +} + +/// A GNU symbol hash table in an ELF file. +/// +/// Returned by [`SectionHeader::gnu_hash`](super::SectionHeader::gnu_hash). +#[derive(Debug)] +pub struct GnuHashTable<'data, Elf: FileHeader> { + symbol_base: u32, + bloom_shift: u32, + bloom_filters: &'data [u8], + buckets: &'data [U32<Elf::Endian>], + values: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> { + /// Parse a GNU hash table. + /// + /// `data` should be from an [`elf::SHT_GNU_HASH`] section, or from a + /// segment pointed to via the [`elf::DT_GNU_HASH`] entry. + /// + /// The header is read at offset 0 in the given `data`. + /// + /// The header does not contain a length field, and so all of `data` + /// will be used as the hash table values. It does not matter if this + /// is longer than needed, and this will often the case when accessing + /// the hash table via the [`elf::DT_GNU_HASH`] entry. + pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> { + let mut offset = 0; + let header = data + .read::<elf::GnuHashHeader<Elf::Endian>>(&mut offset) + .read_error("Invalid GNU hash header")?; + let bloom_len = + u64::from(header.bloom_count.get(endian)) * mem::size_of::<Elf::Word>() as u64; + let bloom_filters = data + .read_bytes(&mut offset, bloom_len) + .read_error("Invalid GNU hash bloom filters")?; + let buckets = data + .read_slice(&mut offset, header.bucket_count.get(endian) as usize) + .read_error("Invalid GNU hash buckets")?; + let chain_count = (data.len() - offset as usize) / 4; + let values = data + .read_slice(&mut offset, chain_count) + .read_error("Invalid GNU hash values")?; + Ok(GnuHashTable { + symbol_base: header.symbol_base.get(endian), + bloom_shift: header.bloom_shift.get(endian), + bloom_filters, + buckets, + values, + }) + } + + /// Return the symbol table index of the first symbol in the hash table. + pub fn symbol_base(&self) -> u32 { + self.symbol_base + } + + /// Determine the symbol table length by finding the last entry in the hash table. + /// + /// Returns `None` if the hash table is empty or invalid. + pub fn symbol_table_length(&self, endian: Elf::Endian) -> Option<u32> { + // Ensure we find a non-empty bucket. + if self.symbol_base == 0 { + return None; + } + + // Find the highest chain index in a bucket. + let mut max_symbol = 0; + for bucket in self.buckets { + let bucket = bucket.get(endian); + if max_symbol < bucket { + max_symbol = bucket; + } + } + + // Find the end of the chain. + for value in self + .values + .get(max_symbol.checked_sub(self.symbol_base)? as usize..)? + { + max_symbol += 1; + if value.get(endian) & 1 != 0 { + return Some(max_symbol); + } + } + + None + } + + fn bucket(&self, endian: Elf::Endian, hash: u32) -> SymbolIndex { + SymbolIndex(self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize) + } + + /// Use the hash table to find the symbol table entry with the given name, hash, and version. + pub fn find<R: ReadRef<'data>>( + &self, + endian: Elf::Endian, + name: &[u8], + hash: u32, + version: Option<&Version<'_>>, + symbols: &SymbolTable<'data, Elf, R>, + versions: &VersionTable<'data, Elf>, + ) -> Option<(SymbolIndex, &'data Elf::Sym)> { + let word_bits = mem::size_of::<Elf::Word>() as u32 * 8; + + // Test against bloom filter. + let bloom_count = self.bloom_filters.len() / mem::size_of::<Elf::Word>(); + let offset = + ((hash / word_bits) & (bloom_count as u32 - 1)) * mem::size_of::<Elf::Word>() as u32; + let filter = if word_bits == 64 { + self.bloom_filters + .read_at::<U64<Elf::Endian>>(offset.into()) + .ok()? + .get(endian) + } else { + self.bloom_filters + .read_at::<U32<Elf::Endian>>(offset.into()) + .ok()? + .get(endian) + .into() + }; + if filter & (1 << (hash % word_bits)) == 0 { + return None; + } + if filter & (1 << ((hash >> self.bloom_shift) % word_bits)) == 0 { + return None; + } + + // Get the chain start from the bucket for this hash. + let mut index = self.bucket(endian, hash); + if index == SymbolIndex(0) { + return None; + } + + // Test symbols in the chain. + let strings = symbols.strings(); + let symbols = symbols.symbols().get(index.0..)?; + let values = self + .values + .get(index.0.checked_sub(self.symbol_base as usize)?..)?; + for (symbol, value) in symbols.iter().zip(values.iter()) { + let value = value.get(endian); + if value | 1 == hash | 1 { + if symbol.name(endian, strings) == Ok(name) + && versions.matches(endian, index, version) + { + return Some((index, symbol)); + } + } + if value & 1 != 0 { + break; + } + index.0 += 1; + } + None + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/mod.rs new file mode 100644 index 0000000..66931bd --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/mod.rs
@@ -0,0 +1,78 @@ +//! Support for reading ELF files. +//! +//! Traits are used to abstract over the difference between 32-bit and 64-bit ELF. +//! The primary trait for this is [`FileHeader`]. +//! +//! ## High level API +//! +//! [`ElfFile`] implements the [`Object`](crate::read::Object) trait for ELF files. +//! [`ElfFile`] is parameterised by [`FileHeader`] to allow reading both 32-bit and +//! 64-bit ELF. There are type aliases for these parameters ([`ElfFile32`] and +//! [`ElfFile64`]). +//! +//! ## Low level API +//! +//! The [`FileHeader`] trait can be directly used to parse both [`elf::FileHeader32`] +//! and [`elf::FileHeader64`]. +//! +//! ### Example for low level API +//! ```no_run +//! use object::elf; +//! use object::read::elf::{FileHeader, Sym}; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the name of each symbol. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(feature = "std")] { +//! let data = fs::read("path/to/binary")?; +//! let elf = elf::FileHeader64::<object::Endianness>::parse(&*data)?; +//! let endian = elf.endian()?; +//! let sections = elf.sections(endian, &*data)?; +//! let symbols = sections.symbols(endian, &*data, elf::SHT_SYMTAB)?; +//! for symbol in symbols.iter() { +//! let name = symbol.name(endian, symbols.strings())?; +//! println!("{}", String::from_utf8_lossy(name)); +//! } +//! # } +//! Ok(()) +//! } +//! ``` +#[cfg(doc)] +use crate::elf; + +mod file; +pub use file::*; + +mod segment; +pub use segment::*; + +mod section; +pub use section::*; + +mod symbol; +pub use symbol::*; + +mod relocation; +pub use relocation::*; + +mod comdat; +pub use comdat::*; + +mod dynamic; +pub use dynamic::*; + +mod compression; +pub use compression::*; + +mod note; +pub use note::*; + +mod hash; +pub use hash::*; + +mod version; +pub use version::*; + +mod attributes; +pub use attributes::*;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/note.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/note.rs new file mode 100644 index 0000000..bf4a3ae --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/note.rs
@@ -0,0 +1,302 @@ +use core::fmt::Debug; +use core::mem; + +use crate::elf; +use crate::endian::{self, U32}; +use crate::pod::Pod; +use crate::read::util; +use crate::read::{self, Bytes, Error, ReadError}; + +use super::FileHeader; + +/// An iterator over the notes in an ELF section or segment. +/// +/// Returned [`ProgramHeader::notes`](super::ProgramHeader::notes) +/// and [`SectionHeader::notes`](super::SectionHeader::notes). +#[derive(Debug)] +pub struct NoteIterator<'data, Elf> +where + Elf: FileHeader, +{ + endian: Elf::Endian, + align: usize, + data: Bytes<'data>, +} + +impl<'data, Elf> NoteIterator<'data, Elf> +where + Elf: FileHeader, +{ + /// An iterator over the notes in an ELF section or segment. + /// + /// `align` should be from the `p_align` field of the segment, + /// or the `sh_addralign` field of the section. Supported values are + /// either 4 or 8, but values less than 4 are treated as 4. + /// This matches the behaviour of binutils. + /// + /// Returns `Err` if `align` is invalid. + pub fn new(endian: Elf::Endian, align: Elf::Word, data: &'data [u8]) -> read::Result<Self> { + let align = match align.into() { + 0u64..=4 => 4, + 8 => 8, + _ => return Err(Error("Invalid ELF note alignment")), + }; + // TODO: check data alignment? + Ok(NoteIterator { + endian, + align, + data: Bytes(data), + }) + } + + /// Returns the next note. + pub fn next(&mut self) -> read::Result<Option<Note<'data, Elf>>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> read::Result<Note<'data, Elf>> { + let header = self + .data + .read_at::<Elf::NoteHeader>(0) + .read_error("ELF note is too short")?; + + // The name has no alignment requirement. + let offset = mem::size_of::<Elf::NoteHeader>(); + let namesz = header.n_namesz(self.endian) as usize; + let name = self + .data + .read_bytes_at(offset, namesz) + .read_error("Invalid ELF note namesz")? + .0; + + // The descriptor must be aligned. + let offset = util::align(offset + namesz, self.align); + let descsz = header.n_descsz(self.endian) as usize; + let desc = self + .data + .read_bytes_at(offset, descsz) + .read_error("Invalid ELF note descsz")? + .0; + + // The next note (if any) must be aligned. + let offset = util::align(offset + descsz, self.align); + if self.data.skip(offset).is_err() { + self.data = Bytes(&[]); + } + + Ok(Note { header, name, desc }) + } +} + +impl<'data, Elf: FileHeader> Iterator for NoteIterator<'data, Elf> { + type Item = read::Result<Note<'data, Elf>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// A parsed [`NoteHeader`]. +#[derive(Debug)] +pub struct Note<'data, Elf> +where + Elf: FileHeader, +{ + header: &'data Elf::NoteHeader, + name: &'data [u8], + desc: &'data [u8], +} + +impl<'data, Elf: FileHeader> Note<'data, Elf> { + /// Return the `n_type` field of the `NoteHeader`. + /// + /// The meaning of this field is determined by `name`. + pub fn n_type(&self, endian: Elf::Endian) -> u32 { + self.header.n_type(endian) + } + + /// Return the `n_namesz` field of the `NoteHeader`. + pub fn n_namesz(&self, endian: Elf::Endian) -> u32 { + self.header.n_namesz(endian) + } + + /// Return the `n_descsz` field of the `NoteHeader`. + pub fn n_descsz(&self, endian: Elf::Endian) -> u32 { + self.header.n_descsz(endian) + } + + /// Return the bytes for the name field following the `NoteHeader`. + /// + /// This field is usually a string including one or more trailing null bytes + /// (but it is not required to be). + /// + /// The length of this field is given by `n_namesz`. + pub fn name_bytes(&self) -> &'data [u8] { + self.name + } + + /// Return the bytes for the name field following the `NoteHeader`, + /// excluding all trailing null bytes. + pub fn name(&self) -> &'data [u8] { + let mut name = self.name; + while let [rest @ .., 0] = name { + name = rest; + } + name + } + + /// Return the bytes for the desc field following the `NoteHeader`. + /// + /// The length of this field is given by `n_descsz`. The meaning + /// of this field is determined by `name` and `n_type`. + pub fn desc(&self) -> &'data [u8] { + self.desc + } + + /// Return an iterator for properties if this note's type is [`elf::NT_GNU_PROPERTY_TYPE_0`]. + pub fn gnu_properties( + &self, + endian: Elf::Endian, + ) -> Option<GnuPropertyIterator<'data, Elf::Endian>> { + if self.name() != elf::ELF_NOTE_GNU || self.n_type(endian) != elf::NT_GNU_PROPERTY_TYPE_0 { + return None; + } + // Use the ELF class instead of the section alignment. + // This matches what other parsers do. + let align = if Elf::is_type_64_sized() { 8 } else { 4 }; + Some(GnuPropertyIterator { + endian, + align, + data: Bytes(self.desc), + }) + } +} + +/// A trait for generic access to [`elf::NoteHeader32`] and [`elf::NoteHeader64`]. +#[allow(missing_docs)] +pub trait NoteHeader: Debug + Pod { + type Endian: endian::Endian; + + fn n_namesz(&self, endian: Self::Endian) -> u32; + fn n_descsz(&self, endian: Self::Endian) -> u32; + fn n_type(&self, endian: Self::Endian) -> u32; +} + +impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader32<Endian> { + type Endian = Endian; + + #[inline] + fn n_namesz(&self, endian: Self::Endian) -> u32 { + self.n_namesz.get(endian) + } + + #[inline] + fn n_descsz(&self, endian: Self::Endian) -> u32 { + self.n_descsz.get(endian) + } + + #[inline] + fn n_type(&self, endian: Self::Endian) -> u32 { + self.n_type.get(endian) + } +} + +impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader64<Endian> { + type Endian = Endian; + + #[inline] + fn n_namesz(&self, endian: Self::Endian) -> u32 { + self.n_namesz.get(endian) + } + + #[inline] + fn n_descsz(&self, endian: Self::Endian) -> u32 { + self.n_descsz.get(endian) + } + + #[inline] + fn n_type(&self, endian: Self::Endian) -> u32 { + self.n_type.get(endian) + } +} + +/// An iterator for the properties in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note. +/// +/// Returned by [`Note::gnu_properties`]. +#[derive(Debug)] +pub struct GnuPropertyIterator<'data, Endian: endian::Endian> { + endian: Endian, + align: usize, + data: Bytes<'data>, +} + +impl<'data, Endian: endian::Endian> GnuPropertyIterator<'data, Endian> { + /// Returns the next property. + pub fn next(&mut self) -> read::Result<Option<GnuProperty<'data>>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> read::Result<GnuProperty<'data>> { + (|| -> Result<_, ()> { + let pr_type = self.data.read_at::<U32<Endian>>(0)?.get(self.endian); + let pr_datasz = self.data.read_at::<U32<Endian>>(4)?.get(self.endian) as usize; + let pr_data = self.data.read_bytes_at(8, pr_datasz)?.0; + self.data.skip(util::align(8 + pr_datasz, self.align))?; + Ok(GnuProperty { pr_type, pr_data }) + })() + .read_error("Invalid ELF GNU property") + } +} + +impl<'data, Endian: endian::Endian> Iterator for GnuPropertyIterator<'data, Endian> { + type Item = read::Result<GnuProperty<'data>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// A property in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note. +#[derive(Debug)] +pub struct GnuProperty<'data> { + pr_type: u32, + pr_data: &'data [u8], +} + +impl<'data> GnuProperty<'data> { + /// Return the property type. + /// + /// This is one of the `GNU_PROPERTY_*` constants. + pub fn pr_type(&self) -> u32 { + self.pr_type + } + + /// Return the property data. + pub fn pr_data(&self) -> &'data [u8] { + self.pr_data + } + + /// Parse the property data as an unsigned 32-bit integer. + pub fn data_u32<E: endian::Endian>(&self, endian: E) -> read::Result<u32> { + Bytes(self.pr_data) + .read_at::<U32<E>>(0) + .read_error("Invalid ELF GNU property data") + .map(|val| val.get(endian)) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/relocation.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/relocation.rs new file mode 100644 index 0000000..f0c8c4a --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/relocation.rs
@@ -0,0 +1,991 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::fmt::Debug; +use core::slice; + +use crate::elf; +use crate::endian::{self, Endianness}; +use crate::pod::Pod; +use crate::read::{ + self, Bytes, Error, ReadError, ReadRef, Relocation, RelocationEncoding, RelocationFlags, + RelocationKind, RelocationTarget, SectionIndex, SymbolIndex, +}; + +use super::{ElfFile, FileHeader, SectionHeader, SectionTable}; + +/// A mapping from section index to associated relocation sections. +#[derive(Debug, Default)] +pub struct RelocationSections { + relocations: Vec<usize>, +} + +impl RelocationSections { + /// Create a new mapping using the section table. + /// + /// Skips relocation sections that do not use the given symbol table section. + pub fn parse<'data, Elf: FileHeader, R: ReadRef<'data>>( + endian: Elf::Endian, + sections: &SectionTable<'data, Elf, R>, + symbol_section: SectionIndex, + ) -> read::Result<Self> { + let mut relocations = vec![0; sections.len()]; + for (index, section) in sections.iter().enumerate().rev() { + let sh_type = section.sh_type(endian); + if sh_type == elf::SHT_REL || sh_type == elf::SHT_RELA || sh_type == elf::SHT_CREL { + // The symbol indices used in relocations must be for the symbol table + // we are expecting to use. + let sh_link = section.link(endian); + if sh_link != symbol_section { + continue; + } + + let sh_info = section.info_link(endian); + if sh_info == SectionIndex(0) { + // Skip dynamic relocations. + continue; + } + if sh_info.0 >= relocations.len() { + return Err(Error("Invalid ELF sh_info for relocation section")); + } + + // We don't support relocations that apply to other relocation sections + // because it interferes with the chaining of relocation sections below. + let sh_info_type = sections.section(sh_info)?.sh_type(endian); + if sh_info_type == elf::SHT_REL + || sh_info_type == elf::SHT_RELA + || sh_info_type == elf::SHT_CREL + { + return Err(Error("Unsupported ELF sh_info for relocation section")); + } + + // Handle multiple relocation sections by chaining them. + let next = relocations[sh_info.0]; + relocations[sh_info.0] = index; + relocations[index] = next; + } + } + Ok(Self { relocations }) + } + + /// Given a section index, return the section index of the associated relocation section. + /// + /// This may also be called with a relocation section index, and it will return the + /// next associated relocation section. + pub fn get(&self, index: SectionIndex) -> Option<SectionIndex> { + self.relocations + .get(index.0) + .cloned() + .filter(|x| *x != 0) + .map(SectionIndex) + } +} + +pub(super) enum ElfRelocationIterator<'data, Elf: FileHeader> { + Rel(slice::Iter<'data, Elf::Rel>, Elf::Endian), + Rela(slice::Iter<'data, Elf::Rela>, Elf::Endian, bool), + Crel(CrelIterator<'data>), +} + +impl<'data, Elf: FileHeader> ElfRelocationIterator<'data, Elf> { + fn is_rel(&self) -> bool { + match self { + ElfRelocationIterator::Rel(..) => true, + ElfRelocationIterator::Rela(..) => false, + ElfRelocationIterator::Crel(i) => !i.is_rela(), + } + } +} + +impl<'data, Elf: FileHeader> Iterator for ElfRelocationIterator<'data, Elf> { + type Item = Crel; + + fn next(&mut self) -> Option<Self::Item> { + match self { + ElfRelocationIterator::Rel(ref mut i, endian) => { + i.next().map(|r| Crel::from_rel(r, *endian)) + } + ElfRelocationIterator::Rela(ref mut i, endian, is_mips64el) => { + i.next().map(|r| Crel::from_rela(r, *endian, *is_mips64el)) + } + ElfRelocationIterator::Crel(ref mut i) => i.next().and_then(Result::ok), + } + } +} + +/// An iterator for the dynamic relocations in an [`ElfFile32`](super::ElfFile32). +pub type ElfDynamicRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator for the dynamic relocations in an [`ElfFile64`](super::ElfFile64). +pub type ElfDynamicRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator for the dynamic relocations in an [`ElfFile`]. +pub struct ElfDynamicRelocationIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + /// The current relocation section index. + pub(super) section_index: SectionIndex, + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) relocations: Option<ElfRelocationIterator<'data, Elf>>, +} + +impl<'data, 'file, Elf, R> Iterator for ElfDynamicRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + let endian = self.file.endian; + loop { + if let Some(ref mut relocations) = self.relocations { + if let Some(reloc) = relocations.next() { + let relocation = + parse_relocation(self.file.header, endian, reloc, relocations.is_rel()); + return Some((reloc.r_offset, relocation)); + } + self.relocations = None; + } + + let section = self.file.sections.section(self.section_index).ok()?; + self.section_index.0 += 1; + + if section.link(endian) != self.file.dynamic_symbols.section() { + continue; + } + + match section.sh_type(endian) { + elf::SHT_REL => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = + Some(ElfRelocationIterator::Rel(relocations.iter(), endian)); + } + } + elf::SHT_RELA => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = Some(ElfRelocationIterator::Rela( + relocations.iter(), + endian, + self.file.header.is_mips64el(endian), + )); + } + } + elf::SHT_CREL => { + if let Ok(data) = section.data(endian, self.file.data) { + if let Ok(relocations) = CrelIterator::new(data) { + self.relocations = Some(ElfRelocationIterator::Crel(relocations)); + } + } + } + _ => {} + } + } + } +} + +impl<'data, 'file, Elf, R> fmt::Debug for ElfDynamicRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfDynamicRelocationIterator").finish() + } +} + +/// An iterator for the relocations for an [`ElfSection32`](super::ElfSection32). +pub type ElfSectionRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator for the relocations for an [`ElfSection64`](super::ElfSection64). +pub type ElfSectionRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator for the relocations for an [`ElfSection`](super::ElfSection). +pub struct ElfSectionRelocationIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + /// The current pointer in the chain of relocation sections. + pub(super) section_index: SectionIndex, + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) relocations: Option<ElfRelocationIterator<'data, Elf>>, +} + +impl<'data, 'file, Elf, R> Iterator for ElfSectionRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + let endian = self.file.endian; + loop { + if let Some(ref mut relocations) = self.relocations { + if let Some(reloc) = relocations.next() { + let relocation = + parse_relocation(self.file.header, endian, reloc, relocations.is_rel()); + return Some((reloc.r_offset, relocation)); + } + self.relocations = None; + } + self.section_index = self.file.relocations.get(self.section_index)?; + // The construction of RelocationSections ensures section_index is valid. + let section = self.file.sections.section(self.section_index).unwrap(); + match section.sh_type(endian) { + elf::SHT_REL => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = + Some(ElfRelocationIterator::Rel(relocations.iter(), endian)); + } + } + elf::SHT_RELA => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = Some(ElfRelocationIterator::Rela( + relocations.iter(), + endian, + self.file.header.is_mips64el(endian), + )); + } + } + elf::SHT_CREL => { + if let Ok(data) = section.data(endian, self.file.data) { + if let Ok(relocations) = CrelIterator::new(data) { + self.relocations = Some(ElfRelocationIterator::Crel(relocations)); + } + } + } + _ => {} + } + } + } +} + +impl<'data, 'file, Elf, R> fmt::Debug for ElfSectionRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfSectionRelocationIterator").finish() + } +} + +fn parse_relocation<Elf: FileHeader>( + header: &Elf, + endian: Elf::Endian, + reloc: Crel, + implicit_addend: bool, +) -> Relocation { + use RelocationEncoding as E; + use RelocationKind as K; + + let r_type = reloc.r_type; + let flags = RelocationFlags::Elf { r_type }; + let g = E::Generic; + let unknown = (K::Unknown, E::Generic, 0); + let (kind, encoding, size) = match header.e_machine(endian) { + elf::EM_AARCH64 => { + if header.is_type_64() { + match r_type { + elf::R_AARCH64_ABS64 => (K::Absolute, g, 64), + elf::R_AARCH64_ABS32 => (K::Absolute, g, 32), + elf::R_AARCH64_ABS16 => (K::Absolute, g, 16), + elf::R_AARCH64_PREL64 => (K::Relative, g, 64), + elf::R_AARCH64_PREL32 => (K::Relative, g, 32), + elf::R_AARCH64_PREL16 => (K::Relative, g, 16), + elf::R_AARCH64_CALL26 => (K::PltRelative, E::AArch64Call, 26), + _ => unknown, + } + } else { + match r_type { + elf::R_AARCH64_P32_ABS32 => (K::Absolute, g, 32), + _ => unknown, + } + } + } + elf::EM_ALPHA => match r_type { + // Absolute + elf::R_ALPHA_REFLONG => (K::Absolute, g, 32), + elf::R_ALPHA_REFQUAD => (K::Absolute, g, 64), + // Relative to the PC + elf::R_ALPHA_SREL16 => (K::Relative, g, 16), + elf::R_ALPHA_SREL32 => (K::Relative, g, 32), + elf::R_ALPHA_SREL64 => (K::Relative, g, 64), + _ => unknown, + }, + elf::EM_ARM => match r_type { + elf::R_ARM_ABS32 => (K::Absolute, g, 32), + _ => unknown, + }, + elf::EM_AVR => match r_type { + elf::R_AVR_32 => (K::Absolute, g, 32), + elf::R_AVR_16 => (K::Absolute, g, 16), + _ => unknown, + }, + elf::EM_BPF => match r_type { + elf::R_BPF_64_64 => (K::Absolute, g, 64), + elf::R_BPF_64_32 => (K::Absolute, g, 32), + _ => unknown, + }, + elf::EM_CSKY => match r_type { + elf::R_CKCORE_ADDR32 => (K::Absolute, g, 32), + elf::R_CKCORE_PCREL32 => (K::Relative, g, 32), + _ => unknown, + }, + elf::EM_MCST_ELBRUS => match r_type { + elf::R_E2K_32_ABS => (K::Absolute, g, 32), + elf::R_E2K_64_ABS => (K::Absolute, g, 64), + elf::R_E2K_64_ABS_LIT => (K::Absolute, E::E2KLit, 64), + elf::R_E2K_DISP => (K::Relative, E::E2KDisp, 28), + elf::R_E2K_GOT => (K::Got, g, 32), + _ => unknown, + }, + elf::EM_386 => match r_type { + elf::R_386_32 => (K::Absolute, g, 32), + elf::R_386_PC32 => (K::Relative, g, 32), + elf::R_386_GOT32 => (K::Got, g, 32), + elf::R_386_PLT32 => (K::PltRelative, g, 32), + elf::R_386_GOTOFF => (K::GotBaseOffset, g, 32), + elf::R_386_GOTPC => (K::GotBaseRelative, g, 32), + elf::R_386_16 => (K::Absolute, g, 16), + elf::R_386_PC16 => (K::Relative, g, 16), + elf::R_386_8 => (K::Absolute, g, 8), + elf::R_386_PC8 => (K::Relative, g, 8), + _ => unknown, + }, + elf::EM_X86_64 => match r_type { + elf::R_X86_64_64 => (K::Absolute, g, 64), + elf::R_X86_64_PC32 => (K::Relative, g, 32), + elf::R_X86_64_GOT32 => (K::Got, g, 32), + elf::R_X86_64_PLT32 => (K::PltRelative, g, 32), + elf::R_X86_64_GOTPCREL => (K::GotRelative, g, 32), + elf::R_X86_64_32 => (K::Absolute, g, 32), + elf::R_X86_64_32S => (K::Absolute, E::X86Signed, 32), + elf::R_X86_64_16 => (K::Absolute, g, 16), + elf::R_X86_64_PC16 => (K::Relative, g, 16), + elf::R_X86_64_8 => (K::Absolute, g, 8), + elf::R_X86_64_PC8 => (K::Relative, g, 8), + _ => unknown, + }, + elf::EM_HEXAGON => match r_type { + elf::R_HEX_32 => (K::Absolute, g, 32), + _ => unknown, + }, + elf::EM_LOONGARCH => match r_type { + elf::R_LARCH_32 => (K::Absolute, g, 32), + elf::R_LARCH_64 => (K::Absolute, g, 64), + elf::R_LARCH_32_PCREL => (K::Relative, g, 32), + elf::R_LARCH_64_PCREL => (K::Relative, g, 64), + elf::R_LARCH_B16 => (K::Relative, E::LoongArchBranch, 16), + elf::R_LARCH_B21 => (K::Relative, E::LoongArchBranch, 21), + elf::R_LARCH_B26 => (K::Relative, E::LoongArchBranch, 26), + _ => unknown, + }, + elf::EM_68K => match r_type { + elf::R_68K_32 => (K::Absolute, g, 32), + elf::R_68K_16 => (K::Absolute, g, 16), + elf::R_68K_8 => (K::Absolute, g, 8), + elf::R_68K_PC32 => (K::Relative, g, 32), + elf::R_68K_PC16 => (K::Relative, g, 16), + elf::R_68K_PC8 => (K::Relative, g, 8), + elf::R_68K_GOT32O => (K::Got, g, 32), + elf::R_68K_GOT16O => (K::Got, g, 16), + elf::R_68K_GOT8O => (K::Got, g, 8), + elf::R_68K_GOT32 => (K::GotRelative, g, 32), + elf::R_68K_GOT16 => (K::GotRelative, g, 16), + elf::R_68K_GOT8 => (K::GotRelative, g, 8), + elf::R_68K_PLT32 => (K::PltRelative, g, 32), + elf::R_68K_PLT16 => (K::PltRelative, g, 16), + elf::R_68K_PLT8 => (K::PltRelative, g, 8), + _ => unknown, + }, + elf::EM_MIPS => match r_type { + elf::R_MIPS_16 => (K::Absolute, g, 16), + elf::R_MIPS_32 => (K::Absolute, g, 32), + elf::R_MIPS_64 => (K::Absolute, g, 64), + _ => unknown, + }, + elf::EM_MSP430 => match r_type { + elf::R_MSP430_32 => (K::Absolute, g, 32), + elf::R_MSP430_16_BYTE => (K::Absolute, g, 16), + _ => unknown, + }, + elf::EM_PARISC => match r_type { + elf::R_PARISC_DIR32 => (K::Absolute, g, 32), + elf::R_PARISC_PCREL32 => (K::Relative, g, 32), + _ => unknown, + }, + elf::EM_PPC => match r_type { + elf::R_PPC_ADDR32 => (K::Absolute, g, 32), + _ => unknown, + }, + elf::EM_PPC64 => match r_type { + elf::R_PPC64_ADDR32 => (K::Absolute, g, 32), + elf::R_PPC64_ADDR64 => (K::Absolute, g, 64), + _ => unknown, + }, + elf::EM_RISCV => match r_type { + elf::R_RISCV_32 => (K::Absolute, g, 32), + elf::R_RISCV_64 => (K::Absolute, g, 64), + _ => unknown, + }, + elf::EM_S390 => match r_type { + elf::R_390_8 => (K::Absolute, g, 8), + elf::R_390_16 => (K::Absolute, g, 16), + elf::R_390_32 => (K::Absolute, g, 32), + elf::R_390_64 => (K::Absolute, g, 64), + elf::R_390_PC16 => (K::Relative, g, 16), + elf::R_390_PC32 => (K::Relative, g, 32), + elf::R_390_PC64 => (K::Relative, g, 64), + elf::R_390_PC16DBL => (K::Relative, E::S390xDbl, 16), + elf::R_390_PC32DBL => (K::Relative, E::S390xDbl, 32), + elf::R_390_PLT16DBL => (K::PltRelative, E::S390xDbl, 16), + elf::R_390_PLT32DBL => (K::PltRelative, E::S390xDbl, 32), + elf::R_390_GOT16 => (K::Got, g, 16), + elf::R_390_GOT32 => (K::Got, g, 32), + elf::R_390_GOT64 => (K::Got, g, 64), + elf::R_390_GOTENT => (K::GotRelative, E::S390xDbl, 32), + elf::R_390_GOTOFF16 => (K::GotBaseOffset, g, 16), + elf::R_390_GOTOFF32 => (K::GotBaseOffset, g, 32), + elf::R_390_GOTOFF64 => (K::GotBaseOffset, g, 64), + elf::R_390_GOTPC => (K::GotBaseRelative, g, 64), + elf::R_390_GOTPCDBL => (K::GotBaseRelative, E::S390xDbl, 32), + _ => unknown, + }, + elf::EM_SBF => match r_type { + elf::R_SBF_64_64 => (K::Absolute, g, 64), + elf::R_SBF_64_32 => (K::Absolute, g, 32), + _ => unknown, + }, + elf::EM_SHARC => match r_type { + elf::R_SHARC_ADDR24_V3 => (K::Absolute, E::SharcTypeA, 24), + elf::R_SHARC_ADDR32_V3 => (K::Absolute, E::SharcTypeA, 32), + elf::R_SHARC_ADDR_VAR_V3 => (K::Absolute, E::Generic, 32), + elf::R_SHARC_PCRSHORT_V3 => (K::Relative, E::SharcTypeA, 6), + elf::R_SHARC_PCRLONG_V3 => (K::Relative, E::SharcTypeA, 24), + elf::R_SHARC_DATA6_V3 => (K::Absolute, E::SharcTypeA, 6), + elf::R_SHARC_DATA16_V3 => (K::Absolute, E::SharcTypeA, 16), + elf::R_SHARC_DATA6_VISA_V3 => (K::Absolute, E::SharcTypeB, 6), + elf::R_SHARC_DATA7_VISA_V3 => (K::Absolute, E::SharcTypeB, 7), + elf::R_SHARC_DATA16_VISA_V3 => (K::Absolute, E::SharcTypeB, 16), + elf::R_SHARC_PCR6_VISA_V3 => (K::Relative, E::SharcTypeB, 16), + elf::R_SHARC_ADDR_VAR16_V3 => (K::Absolute, E::Generic, 16), + _ => unknown, + }, + elf::EM_SPARC | elf::EM_SPARC32PLUS | elf::EM_SPARCV9 => match r_type { + elf::R_SPARC_32 | elf::R_SPARC_UA32 => (K::Absolute, g, 32), + elf::R_SPARC_64 | elf::R_SPARC_UA64 => (K::Absolute, g, 64), + _ => unknown, + }, + elf::EM_SH => match r_type { + elf::R_SH_DIR32 => (K::Absolute, g, 32), + elf::R_SH_REL32 => (K::Relative, g, 32), + _ => unknown, + }, + elf::EM_XTENSA => match r_type { + elf::R_XTENSA_32 => (K::Absolute, g, 32), + elf::R_XTENSA_32_PCREL => (K::Relative, g, 32), + _ => unknown, + }, + _ => unknown, + }; + let target = match reloc.symbol() { + None => RelocationTarget::Absolute, + Some(symbol) => RelocationTarget::Symbol(symbol), + }; + Relocation { + kind, + encoding, + size, + target, + addend: reloc.r_addend, + implicit_addend, + flags, + } +} + +/// A trait for generic access to [`elf::Rel32`] and [`elf::Rel64`]. +#[allow(missing_docs)] +pub trait Rel: Debug + Pod + Clone { + type Word: Into<u64>; + type Sword: Into<i64>; + type Endian: endian::Endian; + + fn r_offset(&self, endian: Self::Endian) -> Self::Word; + fn r_info(&self, endian: Self::Endian) -> Self::Word; + fn r_sym(&self, endian: Self::Endian) -> u32; + fn r_type(&self, endian: Self::Endian) -> u32; + + /// Get the symbol index referenced by the relocation. + /// + /// Returns `None` for the null symbol index. + fn symbol(&self, endian: Self::Endian) -> Option<SymbolIndex> { + let sym = self.r_sym(endian); + if sym == 0 { + None + } else { + Some(SymbolIndex(sym as usize)) + } + } +} + +impl<Endian: endian::Endian> Rel for elf::Rel32<Endian> { + type Word = u32; + type Sword = i32; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian) -> Self::Word { + self.r_info.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian) -> u32 { + self.r_sym(endian) + } + + #[inline] + fn r_type(&self, endian: Self::Endian) -> u32 { + self.r_type(endian) + } +} + +impl<Endian: endian::Endian> Rel for elf::Rel64<Endian> { + type Word = u64; + type Sword = i64; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian) -> Self::Word { + self.r_info.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian) -> u32 { + self.r_sym(endian) + } + + #[inline] + fn r_type(&self, endian: Self::Endian) -> u32 { + self.r_type(endian) + } +} + +/// A trait for generic access to [`elf::Rela32`] and [`elf::Rela64`]. +#[allow(missing_docs)] +pub trait Rela: Debug + Pod + Clone { + type Word: Into<u64>; + type Sword: Into<i64>; + type Endian: endian::Endian; + + fn r_offset(&self, endian: Self::Endian) -> Self::Word; + fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word; + fn r_addend(&self, endian: Self::Endian) -> Self::Sword; + fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32; + fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32; + + /// Get the symbol index referenced by the relocation. + /// + /// Returns `None` for the null symbol index. + fn symbol(&self, endian: Self::Endian, is_mips64el: bool) -> Option<SymbolIndex> { + let sym = self.r_sym(endian, is_mips64el); + if sym == 0 { + None + } else { + Some(SymbolIndex(sym as usize)) + } + } +} + +impl<Endian: endian::Endian> Rela for elf::Rela32<Endian> { + type Word = u32; + type Sword = i32; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian, _is_mips64el: bool) -> Self::Word { + self.r_info.get(endian) + } + + #[inline] + fn r_addend(&self, endian: Self::Endian) -> Self::Sword { + self.r_addend.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 { + self.r_sym(endian) + } + + #[inline] + fn r_type(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 { + self.r_type(endian) + } +} + +impl<Endian: endian::Endian> Rela for elf::Rela64<Endian> { + type Word = u64; + type Sword = i64; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word { + self.get_r_info(endian, is_mips64el) + } + + #[inline] + fn r_addend(&self, endian: Self::Endian) -> Self::Sword { + self.r_addend.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32 { + self.r_sym(endian, is_mips64el) + } + + #[inline] + fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32 { + self.r_type(endian, is_mips64el) + } +} + +/// An iterator over the relative relocations in an ELF `SHT_RELR` section. +/// +/// Returned by [`SectionHeader::relr`](super::SectionHeader::relr). +#[derive(Debug)] +pub struct RelrIterator<'data, Elf: FileHeader> { + offset: Elf::Word, + bits: Elf::Word, + count: u8, + iter: slice::Iter<'data, Elf::Relr>, + endian: Elf::Endian, +} + +impl<'data, Elf: FileHeader> RelrIterator<'data, Elf> { + /// Create a new iterator given the `SHT_RELR` section data. + pub fn new(endian: Elf::Endian, data: &'data [Elf::Relr]) -> Self { + RelrIterator { + offset: Elf::Word::default(), + bits: Elf::Word::default(), + count: 0, + iter: data.iter(), + endian, + } + } +} + +impl<'data, Elf: FileHeader> Iterator for RelrIterator<'data, Elf> { + type Item = Elf::Word; + + fn next(&mut self) -> Option<Self::Item> { + loop { + while self.count > 0 { + self.count -= 1; + let offset = Elf::Relr::next(&mut self.offset, &mut self.bits); + if offset.is_some() { + return offset; + } + } + let next = self.iter.next()?.get(self.endian); + if next.into() & 1 == 0 { + self.offset = next; + return Some(next); + } + self.bits = next; + self.count = Elf::Relr::COUNT; + } + } +} + +/// A trait for generic access to [`elf::Relr32`] and [`elf::Relr64`]. +#[allow(missing_docs)] +pub trait Relr: Debug + Pod + Clone { + type Word: Into<u64>; + type Endian: endian::Endian; + + /// The number of bits in the bit mask, excluding the lowest bit. + const COUNT: u8; + + /// Get the relocation entry. + /// + /// This value is an offset if the lowest bit is clear, or a bit mask if the lowest bit is set. + fn get(&self, endian: Self::Endian) -> Self::Word; + + /// Return the offset corresponding to the next bit in the bit mask. + /// + /// Updates the offset and bit mask. This method should be called 31 times + /// for Relr32 and 63 times for Relr64 to iterate over all the bits. + /// + /// Returns `None` if the bit is not set. + fn next(offset: &mut Self::Word, bits: &mut Self::Word) -> Option<Self::Word>; +} + +impl<Endian: endian::Endian> Relr for elf::Relr32<Endian> { + type Word = u32; + type Endian = Endian; + const COUNT: u8 = 31; + + fn get(&self, endian: Self::Endian) -> Self::Word { + self.0.get(endian) + } + + fn next(offset: &mut Self::Word, bits: &mut Self::Word) -> Option<Self::Word> { + *offset += 4; + *bits >>= 1; + if *bits & 1 != 0 { + Some(*offset) + } else { + None + } + } +} + +impl<Endian: endian::Endian> Relr for elf::Relr64<Endian> { + type Word = u64; + type Endian = Endian; + const COUNT: u8 = 63; + + fn get(&self, endian: Self::Endian) -> Self::Word { + self.0.get(endian) + } + + fn next(offset: &mut Self::Word, bits: &mut Self::Word) -> Option<Self::Word> { + *offset += 8; + *bits >>= 1; + if *bits & 1 != 0 { + Some(*offset) + } else { + None + } + } +} + +/// Compact relocation +/// +/// The specification has been submited here: <https://groups.google.com/g/generic-abi/c/ppkaxtLb0P0/m/awgqZ_1CBAAJ>. +#[derive(Debug, Clone, Copy)] +pub struct Crel { + /// Relocation offset. + pub r_offset: u64, + /// Relocation symbol index. + pub r_sym: u32, + /// Relocation type. + pub r_type: u32, + /// Relocation addend. + /// + /// Only set if `CrelIterator::is_rela()` returns `true`. + pub r_addend: i64, +} + +impl Crel { + /// Get the symbol index referenced by the relocation. + /// + /// Returns `None` for the null symbol index. + pub fn symbol(&self) -> Option<SymbolIndex> { + if self.r_sym == 0 { + None + } else { + Some(SymbolIndex(self.r_sym as usize)) + } + } + + /// Build Crel type from Rel. + pub fn from_rel<R: Rel>(r: &R, endian: R::Endian) -> Crel { + Crel { + r_offset: r.r_offset(endian).into(), + r_sym: r.r_sym(endian), + r_type: r.r_type(endian), + r_addend: 0, + } + } + + /// Build Crel type from Rela. + pub fn from_rela<R: Rela>(r: &R, endian: R::Endian, is_mips64el: bool) -> Crel { + Crel { + r_offset: r.r_offset(endian).into(), + r_sym: r.r_sym(endian, is_mips64el), + r_type: r.r_type(endian, is_mips64el), + r_addend: r.r_addend(endian).into(), + } + } +} + +#[derive(Debug, Clone)] +struct CrelIteratorHeader { + /// The number of encoded relocations. + count: usize, + /// The number of flag bits each relocation uses. + flag_bits: u64, + /// Shift of the relocation value. + shift: u64, + /// True if the relocation format encodes addend. + is_rela: bool, +} + +#[derive(Default, Debug, Clone)] +struct CrelIteratorState { + /// Index of the current relocation. + index: usize, + /// Offset of the latest relocation. + offset: u64, + /// Addend of the latest relocation. + addend: i64, + /// Symbol index of the latest relocation. + symidx: u32, + /// Type of the latest relocation. + typ: u32, +} + +/// Compact relocation iterator. +#[derive(Debug, Clone)] +pub struct CrelIterator<'data> { + /// Input stream reader. + data: Bytes<'data>, + /// Parsed header information. + header: CrelIteratorHeader, + /// State of the iterator. + state: CrelIteratorState, +} + +impl<'data> CrelIterator<'data> { + /// Create a new CREL relocation iterator. + pub fn new(data: &'data [u8]) -> Result<Self, Error> { + const HEADER_ADDEND_BIT_MASK: u64 = 1 << 2; + const HEADER_SHIFT_MASK: u64 = 0x3; + + let mut data = Bytes(data); + let header = data.read_uleb128().read_error("Invalid ELF CREL header")?; + let count = header >> 3; + let flag_bits = if header & HEADER_ADDEND_BIT_MASK != 0 { + 3 + } else { + 2 + }; + let shift = header & HEADER_SHIFT_MASK; + let is_rela = header & HEADER_ADDEND_BIT_MASK != 0; + + Ok(CrelIterator { + data, + header: CrelIteratorHeader { + count: count as usize, + flag_bits, + shift, + is_rela, + }, + state: Default::default(), + }) + } + + /// True if the encoded relocations have addend. + pub fn is_rela(&self) -> bool { + self.header.is_rela + } + + /// Return the number of encoded relocations. + pub fn len(&self) -> usize { + self.header.count - self.state.index + } + + /// Return true if there are no more relocations to parse. + pub fn is_empty(&self) -> bool { + self.header.count == self.state.index + } + + fn parse(&mut self) -> read::Result<Crel> { + const DELTA_SYMBOL_INDEX_MASK: u8 = 1 << 0; + const DELTA_TYPE_MASK: u8 = 1 << 1; + const DELTA_ADDEND_MASK: u8 = 1 << 2; + + // The delta offset and flags combined may be larger than u64, + // so we handle the first byte separately. + let byte = *self + .data + .read::<u8>() + .read_error("Cannot read offset and flags of CREL relocation")?; + let flags = byte & ((1 << self.header.flag_bits) - 1); + + let mut delta_offset = u64::from(byte & 0x7f) >> self.header.flag_bits; + if byte & 0x80 != 0 { + delta_offset |= self + .data + .read_uleb128() + .read_error("Cannot read offset and flags of CREL relocation")? + << (7 - self.header.flag_bits); + } + self.state.offset = self.state.offset.wrapping_add(delta_offset); + + if flags & DELTA_SYMBOL_INDEX_MASK != 0 { + let delta_symidx = self + .data + .read_sleb128() + .read_error("Cannot read symidx of CREL relocation")?; + self.state.symidx = self.state.symidx.wrapping_add(delta_symidx as u32); + } + if flags & DELTA_TYPE_MASK != 0 { + let delta_typ = self + .data + .read_sleb128() + .read_error("Cannot read type of CREL relocation")?; + self.state.typ = self.state.typ.wrapping_add(delta_typ as u32); + } + if self.header.is_rela && flags & DELTA_ADDEND_MASK != 0 { + let delta_addend = self + .data + .read_sleb128() + .read_error("Cannot read addend of CREL relocation")?; + self.state.addend = self.state.addend.wrapping_add(delta_addend); + } + self.state.index += 1; + Ok(Crel { + r_offset: self.state.offset << self.header.shift, + r_sym: self.state.symidx, + r_type: self.state.typ, + r_addend: self.state.addend, + }) + } +} + +impl<'data> Iterator for CrelIterator<'data> { + type Item = read::Result<Crel>; + + fn next(&mut self) -> Option<Self::Item> { + if self.state.index >= self.header.count { + return None; + } + + let result = self.parse(); + if result.is_err() { + self.state.index = self.header.count; + } + Some(result) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.len(), Some(self.len())) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/section.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/section.rs new file mode 100644 index 0000000..997c561 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/section.rs
@@ -0,0 +1,1282 @@ +use core::fmt::Debug; +use core::{iter, slice, str}; + +use crate::elf; +use crate::endian::{self, Endianness, U32Bytes}; +use crate::pod::{self, Pod}; +use crate::read::{ + self, gnu_compression, CompressedData, CompressedFileRange, CompressionFormat, Error, + ObjectSection, ReadError, ReadRef, RelocationMap, SectionFlags, SectionIndex, SectionKind, + StringTable, +}; + +use super::{ + AttributesSection, CompressionHeader, CrelIterator, ElfFile, ElfSectionRelocationIterator, + FileHeader, GnuHashTable, HashTable, NoteIterator, RelocationSections, RelrIterator, + SymbolTable, VerdefIterator, VerneedIterator, VersionTable, +}; + +/// The table of section headers in an ELF file. +/// +/// Also includes the string table used for the section names. +/// +/// Returned by [`FileHeader::sections`]. +#[derive(Debug, Clone, Copy)] +pub struct SectionTable<'data, Elf: FileHeader, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + sections: &'data [Elf::SectionHeader], + strings: StringTable<'data, R>, +} + +impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SectionTable<'data, Elf, R> { + fn default() -> Self { + SectionTable { + sections: &[], + strings: StringTable::default(), + } + } +} + +impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { + /// Create a new section table. + #[inline] + pub fn new(sections: &'data [Elf::SectionHeader], strings: StringTable<'data, R>) -> Self { + SectionTable { sections, strings } + } + + /// Iterate over the section headers. + /// + /// This includes the null section at index 0, which you will usually need to skip. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Elf::SectionHeader> { + self.sections.iter() + } + + /// Iterate over the section headers and their indices. + /// + /// This includes the null section at index 0, which you will usually need to skip. + #[inline] + pub fn enumerate(&self) -> impl Iterator<Item = (SectionIndex, &'data Elf::SectionHeader)> { + self.sections + .iter() + .enumerate() + .map(|(i, section)| (SectionIndex(i), section)) + } + + /// Return true if the section table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.sections.is_empty() + } + + /// The number of section headers. + #[inline] + pub fn len(&self) -> usize { + self.sections.len() + } + + /// Get the section header at the given index. + /// + /// Returns an error for the null section at index 0. + pub fn section(&self, index: SectionIndex) -> read::Result<&'data Elf::SectionHeader> { + if index == SectionIndex(0) { + return Err(read::Error("Invalid ELF section index")); + } + self.sections + .get(index.0) + .read_error("Invalid ELF section index") + } + + /// Return the section header with the given name. + /// + /// Ignores sections with invalid names. + pub fn section_by_name( + &self, + endian: Elf::Endian, + name: &[u8], + ) -> Option<(SectionIndex, &'data Elf::SectionHeader)> { + self.enumerate() + .find(|(_, section)| self.section_name(endian, section) == Ok(name)) + } + + /// Return the section name for the given section header. + pub fn section_name( + &self, + endian: Elf::Endian, + section: &Elf::SectionHeader, + ) -> read::Result<&'data [u8]> { + section.name(endian, self.strings) + } + + /// Return the string table at the given section index. + /// + /// Returns an empty string table if the index is 0. + /// Returns an error if the section is not a string table. + #[inline] + pub fn strings( + &self, + endian: Elf::Endian, + data: R, + index: SectionIndex, + ) -> read::Result<StringTable<'data, R>> { + if index == SectionIndex(0) { + return Ok(StringTable::default()); + } + self.section(index)? + .strings(endian, data)? + .read_error("Invalid ELF string section type") + } + + /// Return the symbol table of the given section type. + /// + /// Returns an empty symbol table if the symbol table does not exist. + #[inline] + pub fn symbols( + &self, + endian: Elf::Endian, + data: R, + sh_type: u32, + ) -> read::Result<SymbolTable<'data, Elf, R>> { + debug_assert!(sh_type == elf::SHT_DYNSYM || sh_type == elf::SHT_SYMTAB); + + let (index, section) = match self.enumerate().find(|s| s.1.sh_type(endian) == sh_type) { + Some(s) => s, + None => return Ok(SymbolTable::default()), + }; + + SymbolTable::parse(endian, data, self, index, section) + } + + /// Return the symbol table at the given section index. + /// + /// Returns an error if the section is not a symbol table. + #[inline] + pub fn symbol_table_by_index( + &self, + endian: Elf::Endian, + data: R, + index: SectionIndex, + ) -> read::Result<SymbolTable<'data, Elf, R>> { + let section = self.section(index)?; + match section.sh_type(endian) { + elf::SHT_DYNSYM | elf::SHT_SYMTAB => {} + _ => return Err(Error("Invalid ELF symbol table section type")), + } + SymbolTable::parse(endian, data, self, index, section) + } + + /// Create a mapping from section index to associated relocation sections. + #[inline] + pub fn relocation_sections( + &self, + endian: Elf::Endian, + symbol_section: SectionIndex, + ) -> read::Result<RelocationSections> { + RelocationSections::parse(endian, self, symbol_section) + } + + /// Return the contents of a dynamic section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if there is no `SHT_DYNAMIC` section. + /// Returns `Err` for invalid values. + pub fn dynamic( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<(&'data [Elf::Dyn], SectionIndex)>> { + for section in self.sections { + if let Some(dynamic) = section.dynamic(endian, data)? { + return Ok(Some(dynamic)); + } + } + Ok(None) + } + + /// Return the header of a SysV hash section. + /// + /// Returns `Ok(None)` if there is no SysV GNU hash section. + /// Returns `Err` for invalid values. + pub fn hash_header( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<&'data elf::HashHeader<Elf::Endian>>> { + for section in self.sections { + if let Some(hash) = section.hash_header(endian, data)? { + return Ok(Some(hash)); + } + } + Ok(None) + } + + /// Return the contents of a SysV hash section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if there is no SysV hash section. + /// Returns `Err` for invalid values. + pub fn hash( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<(HashTable<'data, Elf>, SectionIndex)>> { + for section in self.sections { + if let Some(hash) = section.hash(endian, data)? { + return Ok(Some(hash)); + } + } + Ok(None) + } + + /// Return the header of a GNU hash section. + /// + /// Returns `Ok(None)` if there is no GNU hash section. + /// Returns `Err` for invalid values. + pub fn gnu_hash_header( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<&'data elf::GnuHashHeader<Elf::Endian>>> { + for section in self.sections { + if let Some(hash) = section.gnu_hash_header(endian, data)? { + return Ok(Some(hash)); + } + } + Ok(None) + } + + /// Return the contents of a GNU hash section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if there is no GNU hash section. + /// Returns `Err` for invalid values. + pub fn gnu_hash( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<(GnuHashTable<'data, Elf>, SectionIndex)>> { + for section in self.sections { + if let Some(hash) = section.gnu_hash(endian, data)? { + return Ok(Some(hash)); + } + } + Ok(None) + } + + /// Return the contents of a `SHT_GNU_VERSYM` section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section. + /// Returns `Err` for invalid values. + pub fn gnu_versym( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<(&'data [elf::Versym<Elf::Endian>], SectionIndex)>> { + for section in self.sections { + if let Some(syms) = section.gnu_versym(endian, data)? { + return Ok(Some(syms)); + } + } + Ok(None) + } + + /// Return the contents of a `SHT_GNU_VERDEF` section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if there is no `SHT_GNU_VERDEF` section. + /// Returns `Err` for invalid values. + pub fn gnu_verdef( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<(VerdefIterator<'data, Elf>, SectionIndex)>> { + for section in self.sections { + if let Some(defs) = section.gnu_verdef(endian, data)? { + return Ok(Some(defs)); + } + } + Ok(None) + } + + /// Return the contents of a `SHT_GNU_VERNEED` section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if there is no `SHT_GNU_VERNEED` section. + /// Returns `Err` for invalid values. + pub fn gnu_verneed( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<(VerneedIterator<'data, Elf>, SectionIndex)>> { + for section in self.sections { + if let Some(needs) = section.gnu_verneed(endian, data)? { + return Ok(Some(needs)); + } + } + Ok(None) + } + + /// Returns the symbol version table. + /// + /// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section. + /// Returns `Err` for invalid values. + pub fn versions( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result<Option<VersionTable<'data, Elf>>> { + let (versyms, link) = match self.gnu_versym(endian, data)? { + Some(val) => val, + None => return Ok(None), + }; + let strings = self.symbol_table_by_index(endian, data, link)?.strings(); + // TODO: check links? + let verdefs = self.gnu_verdef(endian, data)?.map(|x| x.0); + let verneeds = self.gnu_verneed(endian, data)?.map(|x| x.0); + VersionTable::parse(endian, versyms, verdefs, verneeds, strings).map(Some) + } +} + +/// An iterator for the sections in an [`ElfFile32`](super::ElfFile32). +pub type ElfSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator for the sections in an [`ElfFile64`](super::ElfFile64). +pub type ElfSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator for the sections in an [`ElfFile`]. +#[derive(Debug)] +pub struct ElfSectionIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + file: &'file ElfFile<'data, Elf, R>, + iter: iter::Enumerate<slice::Iter<'data, Elf::SectionHeader>>, +} + +impl<'data, 'file, Elf, R> ElfSectionIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) fn new(file: &'file ElfFile<'data, Elf, R>) -> Self { + let mut iter = file.sections.iter().enumerate(); + iter.next(); // Skip null section. + ElfSectionIterator { file, iter } + } +} + +impl<'data, 'file, Elf, R> Iterator for ElfSectionIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = ElfSection<'data, 'file, Elf, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|(index, section)| ElfSection { + index: SectionIndex(index), + file: self.file, + section, + }) + } +} + +/// A section in an [`ElfFile32`](super::ElfFile32). +pub type ElfSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSection<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A section in an [`ElfFile64`](super::ElfFile64). +pub type ElfSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSection<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A section in an [`ElfFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct ElfSection<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) index: SectionIndex, + pub(super) section: &'data Elf::SectionHeader, +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSection<'data, 'file, Elf, R> { + /// Get the ELF file containing this section. + pub fn elf_file(&self) -> &'file ElfFile<'data, Elf, R> { + self.file + } + + /// Get the raw ELF section header. + pub fn elf_section_header(&self) -> &'data Elf::SectionHeader { + self.section + } + + /// Get the index of the relocation section that references this section. + /// + /// Returns `None` if there are no relocations. + /// Returns an error if there are multiple relocation sections that reference this section. + pub fn elf_relocation_section_index(&self) -> read::Result<Option<SectionIndex>> { + let Some(relocation_index) = self.file.relocations.get(self.index) else { + return Ok(None); + }; + if self.file.relocations.get(relocation_index).is_some() { + return Err(Error( + "Unsupported ELF section with multiple relocation sections", + )); + } + Ok(Some(relocation_index)) + } + + /// Get the relocation section that references this section. + /// + /// Returns `None` if there are no relocations. + /// Returns an error if there are multiple relocation sections that reference this section. + pub fn elf_relocation_section(&self) -> read::Result<Option<&'data Elf::SectionHeader>> { + let Some(relocation_index) = self.elf_relocation_section_index()? else { + return Ok(None); + }; + self.file.sections.section(relocation_index).map(Some) + } + + /// Get the `Elf::Rel` entries that apply to this section. + /// + /// Returns an empty slice if there are no relocations. + /// Returns an error if there are multiple relocation sections that reference this section. + pub fn elf_linked_rel(&self) -> read::Result<&'data [Elf::Rel]> { + let Some(relocation_section) = self.elf_relocation_section()? else { + return Ok(&[]); + }; + // The linked symbol table was already checked when self.file.relocations was created. + let Some((rel, _)) = relocation_section.rel(self.file.endian, self.file.data)? else { + return Ok(&[]); + }; + Ok(rel) + } + + /// Get the `Elf::Rela` entries that apply to this section. + /// + /// Returns an empty slice if there are no relocations. + /// Returns an error if there are multiple relocation sections that reference this section. + pub fn elf_linked_rela(&self) -> read::Result<&'data [Elf::Rela]> { + let Some(relocation_section) = self.elf_relocation_section()? else { + return Ok(&[]); + }; + // The linked symbol table was already checked when self.file.relocations was created. + let Some((rela, _)) = relocation_section.rela(self.file.endian, self.file.data)? else { + return Ok(&[]); + }; + Ok(rela) + } + + fn bytes(&self) -> read::Result<&'data [u8]> { + self.section + .data(self.file.endian, self.file.data) + .read_error("Invalid ELF section size or offset") + } + + fn maybe_compressed(&self) -> read::Result<Option<CompressedFileRange>> { + let endian = self.file.endian; + if let Some((header, offset, compressed_size)) = + self.section.compression(endian, self.file.data)? + { + let format = match header.ch_type(endian) { + elf::ELFCOMPRESS_ZLIB => CompressionFormat::Zlib, + elf::ELFCOMPRESS_ZSTD => CompressionFormat::Zstandard, + _ => return Err(Error("Unsupported ELF compression type")), + }; + let uncompressed_size = header.ch_size(endian).into(); + Ok(Some(CompressedFileRange { + format, + offset, + compressed_size, + uncompressed_size, + })) + } else { + Ok(None) + } + } + + // Try GNU-style "ZLIB" header decompression. + fn maybe_compressed_gnu(&self) -> read::Result<Option<CompressedFileRange>> { + if !self + .name() + .map_or(false, |name| name.starts_with(".zdebug_")) + { + return Ok(None); + } + let (section_offset, section_size) = self + .file_range() + .read_error("Invalid ELF GNU compressed section type")?; + gnu_compression::compressed_file_range(self.file.data, section_offset, section_size) + .map(Some) + } +} + +impl<'data, 'file, Elf, R> read::private::Sealed for ElfSection<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Elf, R> ObjectSection<'data> for ElfSection<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type RelocationIterator = ElfSectionRelocationIterator<'data, 'file, Elf, R>; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + self.section.sh_addr(self.file.endian).into() + } + + #[inline] + fn size(&self) -> u64 { + self.section.sh_size(self.file.endian).into() + } + + #[inline] + fn align(&self) -> u64 { + self.section.sh_addralign(self.file.endian).into() + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + self.section.file_range(self.file.endian) + } + + #[inline] + fn data(&self) -> read::Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> read::Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + fn compressed_file_range(&self) -> read::Result<CompressedFileRange> { + Ok(if let Some(data) = self.maybe_compressed()? { + data + } else if let Some(data) = self.maybe_compressed_gnu()? { + data + } else { + CompressedFileRange::none(self.file_range()) + }) + } + + fn compressed_data(&self) -> read::Result<CompressedData<'data>> { + self.compressed_file_range()?.data(self.file.data) + } + + fn name_bytes(&self) -> read::Result<&'data [u8]> { + self.file + .sections + .section_name(self.file.endian, self.section) + } + + fn name(&self) -> read::Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 ELF section name") + } + + #[inline] + fn segment_name_bytes(&self) -> read::Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> read::Result<Option<&str>> { + Ok(None) + } + + fn kind(&self) -> SectionKind { + let flags = self.section.sh_flags(self.file.endian).into(); + let sh_type = self.section.sh_type(self.file.endian); + match sh_type { + elf::SHT_PROGBITS => { + if flags & u64::from(elf::SHF_ALLOC) != 0 { + if flags & u64::from(elf::SHF_EXECINSTR) != 0 { + SectionKind::Text + } else if flags & u64::from(elf::SHF_TLS) != 0 { + SectionKind::Tls + } else if flags & u64::from(elf::SHF_WRITE) != 0 { + SectionKind::Data + } else if flags & u64::from(elf::SHF_STRINGS) != 0 { + SectionKind::ReadOnlyString + } else { + SectionKind::ReadOnlyData + } + } else if flags & u64::from(elf::SHF_STRINGS) != 0 { + SectionKind::OtherString + } else { + SectionKind::Other + } + } + elf::SHT_NOBITS => { + if flags & u64::from(elf::SHF_TLS) != 0 { + SectionKind::UninitializedTls + } else { + SectionKind::UninitializedData + } + } + elf::SHT_NOTE => SectionKind::Note, + elf::SHT_NULL + | elf::SHT_SYMTAB + | elf::SHT_STRTAB + | elf::SHT_RELA + | elf::SHT_HASH + | elf::SHT_DYNAMIC + | elf::SHT_REL + | elf::SHT_DYNSYM + | elf::SHT_GROUP + | elf::SHT_SYMTAB_SHNDX + | elf::SHT_RELR + | elf::SHT_CREL => SectionKind::Metadata, + _ => SectionKind::Elf(sh_type), + } + } + + fn relocations(&self) -> ElfSectionRelocationIterator<'data, 'file, Elf, R> { + ElfSectionRelocationIterator { + section_index: self.index, + file: self.file, + relocations: None, + } + } + + fn relocation_map(&self) -> read::Result<RelocationMap> { + RelocationMap::new(self.file, self) + } + + fn flags(&self) -> SectionFlags { + SectionFlags::Elf { + sh_flags: self.section.sh_flags(self.file.endian).into(), + } + } +} + +/// A trait for generic access to [`elf::SectionHeader32`] and [`elf::SectionHeader64`]. +#[allow(missing_docs)] +pub trait SectionHeader: Debug + Pod { + type Elf: FileHeader<SectionHeader = Self, Endian = Self::Endian, Word = Self::Word>; + type Word: Into<u64>; + type Endian: endian::Endian; + + fn sh_name(&self, endian: Self::Endian) -> u32; + fn sh_type(&self, endian: Self::Endian) -> u32; + fn sh_flags(&self, endian: Self::Endian) -> Self::Word; + fn sh_addr(&self, endian: Self::Endian) -> Self::Word; + fn sh_offset(&self, endian: Self::Endian) -> Self::Word; + fn sh_size(&self, endian: Self::Endian) -> Self::Word; + fn sh_link(&self, endian: Self::Endian) -> u32; + fn sh_info(&self, endian: Self::Endian) -> u32; + fn sh_addralign(&self, endian: Self::Endian) -> Self::Word; + fn sh_entsize(&self, endian: Self::Endian) -> Self::Word; + + /// Parse the section name from the string table. + fn name<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + strings: StringTable<'data, R>, + ) -> read::Result<&'data [u8]> { + strings + .get(self.sh_name(endian)) + .read_error("Invalid ELF section name offset") + } + + /// Get the `sh_link` field as a section index. + /// + /// This may return a null section index, and does not check for validity. + fn link(&self, endian: Self::Endian) -> SectionIndex { + SectionIndex(self.sh_link(endian) as usize) + } + + /// Return true if the `SHF_INFO_LINK` flag is set. + fn has_info_link(&self, endian: Self::Endian) -> bool { + self.sh_flags(endian).into() & u64::from(elf::SHF_INFO_LINK) != 0 + } + + /// Get the `sh_info` field as a section index. + /// + /// This does not check the `SHF_INFO_LINK` flag. + /// This may return a null section index, and does not check for validity. + fn info_link(&self, endian: Self::Endian) -> SectionIndex { + SectionIndex(self.sh_info(endian) as usize) + } + + /// Return the offset and size of the section in the file. + /// + /// Returns `None` for sections that have no data in the file. + fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> { + if self.sh_type(endian) == elf::SHT_NOBITS { + None + } else { + Some((self.sh_offset(endian).into(), self.sh_size(endian).into())) + } + } + + /// Return the section data. + /// + /// Returns `Ok(&[])` if the section has no data. + /// Returns `Err` for invalid values. + fn data<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<&'data [u8]> { + if let Some((offset, size)) = self.file_range(endian) { + data.read_bytes_at(offset, size) + .read_error("Invalid ELF section size or offset") + } else { + Ok(&[]) + } + } + + /// Return the section data as a slice of the given type. + /// + /// Allows padding at the end of the data. + /// Returns `Ok(&[])` if the section has no data. + /// Returns `Err` for invalid values, including bad alignment. + fn data_as_array<'data, T: Pod, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<&'data [T]> { + pod::slice_from_all_bytes(self.data(endian, data)?) + .read_error("Invalid ELF section size or offset") + } + + /// Return the strings in the section. + /// + /// Returns `Ok(None)` if the section does not contain strings. + /// Returns `Err` for invalid values. + fn strings<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<StringTable<'data, R>>> { + if self.sh_type(endian) != elf::SHT_STRTAB { + return Ok(None); + } + let str_offset = self.sh_offset(endian).into(); + let str_size = self.sh_size(endian).into(); + let str_end = str_offset + .checked_add(str_size) + .read_error("Invalid ELF string section offset or size")?; + Ok(Some(StringTable::new(data, str_offset, str_end))) + } + + /// Return the symbols in the section. + /// + /// Also finds the linked string table in `sections`. + /// + /// `section_index` must be the 0-based index of this section, and is used + /// to find the corresponding extended section index table in `sections`. + /// + /// Returns `Ok(None)` if the section does not contain symbols. + /// Returns `Err` for invalid values. + fn symbols<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + sections: &SectionTable<'data, Self::Elf, R>, + section_index: SectionIndex, + ) -> read::Result<Option<SymbolTable<'data, Self::Elf, R>>> { + let sh_type = self.sh_type(endian); + if sh_type != elf::SHT_SYMTAB && sh_type != elf::SHT_DYNSYM { + return Ok(None); + } + SymbolTable::parse(endian, data, sections, section_index, self).map(Some) + } + + /// Return the `Elf::Rel` entries in the section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if the section does not contain relocations. + /// Returns `Err` for invalid values. + fn rel<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(&'data [<Self::Elf as FileHeader>::Rel], SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_REL { + return Ok(None); + } + let rel = self + .data_as_array(endian, data) + .read_error("Invalid ELF relocation section offset or size")?; + Ok(Some((rel, self.link(endian)))) + } + + /// Return the `Elf::Rela` entries in the section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if the section does not contain relocations. + /// Returns `Err` for invalid values. + fn rela<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(&'data [<Self::Elf as FileHeader>::Rela], SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_RELA { + return Ok(None); + } + let rela = self + .data_as_array(endian, data) + .read_error("Invalid ELF relocation section offset or size")?; + Ok(Some((rela, self.link(endian)))) + } + + /// Return the `Elf::Relr` entries in the section. + /// + /// Returns `Ok(None)` if the section does not contain relative relocations. + /// Returns `Err` for invalid values. + fn relr<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<RelrIterator<'data, Self::Elf>>> { + if self.sh_type(endian) != elf::SHT_RELR { + return Ok(None); + } + let data = self + .data_as_array(endian, data) + .read_error("Invalid ELF relocation section offset or size")?; + let relrs = RelrIterator::new(endian, data); + Ok(Some(relrs)) + } + + /// Return the `Crel` entries in the section. + /// + /// Returns `Ok(None)` if the section does not contain compact relocations. + /// Returns `Err` for invalid values. + fn crel<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(CrelIterator<'data>, SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_CREL { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF relocation section offset or size")?; + let relrs = CrelIterator::new(data); + Ok(Some((relrs?, self.link(endian)))) + } + + /// Return entries in a dynamic section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if the section type is not `SHT_DYNAMIC`. + /// Returns `Err` for invalid values. + fn dynamic<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(&'data [<Self::Elf as FileHeader>::Dyn], SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_DYNAMIC { + return Ok(None); + } + let dynamic = self + .data_as_array(endian, data) + .read_error("Invalid ELF dynamic section offset or size")?; + Ok(Some((dynamic, self.link(endian)))) + } + + /// Return a note iterator for the section data. + /// + /// Returns `Ok(None)` if the section does not contain notes. + /// Returns `Err` for invalid values. + fn notes<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<NoteIterator<'data, Self::Elf>>> { + if self.sh_type(endian) != elf::SHT_NOTE { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF note section offset or size")?; + let notes = NoteIterator::new(endian, self.sh_addralign(endian), data)?; + Ok(Some(notes)) + } + + /// Return the contents of a group section. + /// + /// The first value is a `GRP_*` value, and the remaining values + /// are section indices. + /// + /// Returns `Ok(None)` if the section does not define a group. + /// Returns `Err` for invalid values. + fn group<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(u32, &'data [U32Bytes<Self::Endian>])>> { + if self.sh_type(endian) != elf::SHT_GROUP { + return Ok(None); + } + let msg = "Invalid ELF group section offset or size"; + let data = self.data(endian, data).read_error(msg)?; + let (flag, data) = pod::from_bytes::<U32Bytes<_>>(data).read_error(msg)?; + let sections = pod::slice_from_all_bytes(data).read_error(msg)?; + Ok(Some((flag.get(endian), sections))) + } + + /// Return the header of a SysV hash section. + /// + /// Returns `Ok(None)` if the section does not contain a SysV hash. + /// Returns `Err` for invalid values. + fn hash_header<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<&'data elf::HashHeader<Self::Endian>>> { + if self.sh_type(endian) != elf::SHT_HASH { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF hash section offset or size")?; + let header = data + .read_at::<elf::HashHeader<Self::Endian>>(0) + .read_error("Invalid hash header")?; + Ok(Some(header)) + } + + /// Return the contents of a SysV hash section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if the section does not contain a SysV hash. + /// Returns `Err` for invalid values. + fn hash<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(HashTable<'data, Self::Elf>, SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_HASH { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF hash section offset or size")?; + let hash = HashTable::parse(endian, data)?; + Ok(Some((hash, self.link(endian)))) + } + + /// Return the header of a GNU hash section. + /// + /// Returns `Ok(None)` if the section does not contain a GNU hash. + /// Returns `Err` for invalid values. + fn gnu_hash_header<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<&'data elf::GnuHashHeader<Self::Endian>>> { + if self.sh_type(endian) != elf::SHT_GNU_HASH { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF GNU hash section offset or size")?; + let header = data + .read_at::<elf::GnuHashHeader<Self::Endian>>(0) + .read_error("Invalid GNU hash header")?; + Ok(Some(header)) + } + + /// Return the contents of a GNU hash section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if the section does not contain a GNU hash. + /// Returns `Err` for invalid values. + fn gnu_hash<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(GnuHashTable<'data, Self::Elf>, SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_GNU_HASH { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF GNU hash section offset or size")?; + let hash = GnuHashTable::parse(endian, data)?; + Ok(Some((hash, self.link(endian)))) + } + + /// Return the contents of a `SHT_GNU_VERSYM` section. + /// + /// Also returns the linked symbol table index. + /// + /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERSYM`. + /// Returns `Err` for invalid values. + fn gnu_versym<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(&'data [elf::Versym<Self::Endian>], SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_GNU_VERSYM { + return Ok(None); + } + let versym = self + .data_as_array(endian, data) + .read_error("Invalid ELF GNU versym section offset or size")?; + Ok(Some((versym, self.link(endian)))) + } + + /// Return an iterator for the entries of a `SHT_GNU_VERDEF` section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERDEF`. + /// Returns `Err` for invalid values. + fn gnu_verdef<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(VerdefIterator<'data, Self::Elf>, SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_GNU_VERDEF { + return Ok(None); + } + let verdef = self + .data(endian, data) + .read_error("Invalid ELF GNU verdef section offset or size")?; + Ok(Some(( + VerdefIterator::new(endian, verdef), + self.link(endian), + ))) + } + + /// Return an iterator for the entries of a `SHT_GNU_VERNEED` section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERNEED`. + /// Returns `Err` for invalid values. + fn gnu_verneed<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<(VerneedIterator<'data, Self::Elf>, SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_GNU_VERNEED { + return Ok(None); + } + let verneed = self + .data(endian, data) + .read_error("Invalid ELF GNU verneed section offset or size")?; + Ok(Some(( + VerneedIterator::new(endian, verneed), + self.link(endian), + ))) + } + + /// Return the contents of a `SHT_GNU_ATTRIBUTES` section. + /// + /// Returns `Ok(None)` if the section type is not `SHT_GNU_ATTRIBUTES`. + /// Returns `Err` for invalid values. + fn gnu_attributes<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<AttributesSection<'data, Self::Elf>>> { + if self.sh_type(endian) != elf::SHT_GNU_ATTRIBUTES { + return Ok(None); + } + self.attributes(endian, data).map(Some) + } + + /// Parse the contents of the section as attributes. + /// + /// This function does not check whether section type corresponds + /// to a section that contains attributes. + /// + /// Returns `Err` for invalid values. + fn attributes<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<AttributesSection<'data, Self::Elf>> { + let data = self.data(endian, data)?; + AttributesSection::new(endian, data) + } + + /// Parse the compression header if present. + /// + /// Returns the header, and the offset and size of the compressed section data + /// in the file. + /// + /// Returns `Ok(None)` if the section flags do not have `SHF_COMPRESSED`. + /// Returns `Err` for invalid values. + fn compression<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result< + Option<( + &'data <Self::Elf as FileHeader>::CompressionHeader, + u64, + u64, + )>, + > { + if (self.sh_flags(endian).into() & u64::from(elf::SHF_COMPRESSED)) == 0 { + return Ok(None); + } + let (section_offset, section_size) = self + .file_range(endian) + .read_error("Invalid ELF compressed section type")?; + let mut offset = section_offset; + let header = data + .read::<<Self::Elf as FileHeader>::CompressionHeader>(&mut offset) + .read_error("Invalid ELF compressed section offset")?; + let compressed_size = section_size + .checked_sub(offset - section_offset) + .read_error("Invalid ELF compressed section size")?; + Ok(Some((header, offset, compressed_size))) + } +} + +impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader32<Endian> { + type Elf = elf::FileHeader32<Endian>; + type Word = u32; + type Endian = Endian; + + #[inline] + fn sh_name(&self, endian: Self::Endian) -> u32 { + self.sh_name.get(endian) + } + + #[inline] + fn sh_type(&self, endian: Self::Endian) -> u32 { + self.sh_type.get(endian) + } + + #[inline] + fn sh_flags(&self, endian: Self::Endian) -> Self::Word { + self.sh_flags.get(endian) + } + + #[inline] + fn sh_addr(&self, endian: Self::Endian) -> Self::Word { + self.sh_addr.get(endian) + } + + #[inline] + fn sh_offset(&self, endian: Self::Endian) -> Self::Word { + self.sh_offset.get(endian) + } + + #[inline] + fn sh_size(&self, endian: Self::Endian) -> Self::Word { + self.sh_size.get(endian) + } + + #[inline] + fn sh_link(&self, endian: Self::Endian) -> u32 { + self.sh_link.get(endian) + } + + #[inline] + fn sh_info(&self, endian: Self::Endian) -> u32 { + self.sh_info.get(endian) + } + + #[inline] + fn sh_addralign(&self, endian: Self::Endian) -> Self::Word { + self.sh_addralign.get(endian) + } + + #[inline] + fn sh_entsize(&self, endian: Self::Endian) -> Self::Word { + self.sh_entsize.get(endian) + } +} + +impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader64<Endian> { + type Word = u64; + type Endian = Endian; + type Elf = elf::FileHeader64<Endian>; + + #[inline] + fn sh_name(&self, endian: Self::Endian) -> u32 { + self.sh_name.get(endian) + } + + #[inline] + fn sh_type(&self, endian: Self::Endian) -> u32 { + self.sh_type.get(endian) + } + + #[inline] + fn sh_flags(&self, endian: Self::Endian) -> Self::Word { + self.sh_flags.get(endian) + } + + #[inline] + fn sh_addr(&self, endian: Self::Endian) -> Self::Word { + self.sh_addr.get(endian) + } + + #[inline] + fn sh_offset(&self, endian: Self::Endian) -> Self::Word { + self.sh_offset.get(endian) + } + + #[inline] + fn sh_size(&self, endian: Self::Endian) -> Self::Word { + self.sh_size.get(endian) + } + + #[inline] + fn sh_link(&self, endian: Self::Endian) -> u32 { + self.sh_link.get(endian) + } + + #[inline] + fn sh_info(&self, endian: Self::Endian) -> u32 { + self.sh_info.get(endian) + } + + #[inline] + fn sh_addralign(&self, endian: Self::Endian) -> Self::Word { + self.sh_addralign.get(endian) + } + + #[inline] + fn sh_entsize(&self, endian: Self::Endian) -> Self::Word { + self.sh_entsize.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/segment.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/segment.rs new file mode 100644 index 0000000..7ef0910 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/segment.rs
@@ -0,0 +1,365 @@ +use core::fmt::Debug; +use core::{slice, str}; + +use crate::elf; +use crate::endian::{self, Endianness}; +use crate::pod::{self, Pod}; +use crate::read::{self, ObjectSegment, ReadError, ReadRef, SegmentFlags}; + +use super::{ElfFile, FileHeader, NoteIterator}; + +/// An iterator for the segments in an [`ElfFile32`](super::ElfFile32). +pub type ElfSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegmentIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator for the segments in an [`ElfFile64`](super::ElfFile64). +pub type ElfSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegmentIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator for the segments in an [`ElfFile`]. +#[derive(Debug)] +pub struct ElfSegmentIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) iter: slice::Iter<'data, Elf::ProgramHeader>, +} + +impl<'data, 'file, Elf, R> Iterator for ElfSegmentIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = ElfSegment<'data, 'file, Elf, R>; + + fn next(&mut self) -> Option<Self::Item> { + for segment in self.iter.by_ref() { + if segment.p_type(self.file.endian) == elf::PT_LOAD { + return Some(ElfSegment { + file: self.file, + segment, + }); + } + } + None + } +} + +/// A segment in an [`ElfFile32`](super::ElfFile32). +pub type ElfSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegment<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A segment in an [`ElfFile64`](super::ElfFile64). +pub type ElfSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegment<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A segment in an [`ElfFile`]. +/// +/// Most functionality is provided by the [`ObjectSegment`] trait implementation. +#[derive(Debug)] +pub struct ElfSegment<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) segment: &'data Elf::ProgramHeader, +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSegment<'data, 'file, Elf, R> { + /// Get the ELF file containing this segment. + pub fn elf_file(&self) -> &'file ElfFile<'data, Elf, R> { + self.file + } + + /// Get the raw ELF program header for the segment. + pub fn elf_program_header(&self) -> &'data Elf::ProgramHeader { + self.segment + } + + fn bytes(&self) -> read::Result<&'data [u8]> { + self.segment + .data(self.file.endian, self.file.data) + .read_error("Invalid ELF segment size or offset") + } +} + +impl<'data, 'file, Elf, R> read::private::Sealed for ElfSegment<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Elf, R> ObjectSegment<'data> for ElfSegment<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + #[inline] + fn address(&self) -> u64 { + self.segment.p_vaddr(self.file.endian).into() + } + + #[inline] + fn size(&self) -> u64 { + self.segment.p_memsz(self.file.endian).into() + } + + #[inline] + fn align(&self) -> u64 { + self.segment.p_align(self.file.endian).into() + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + self.segment.file_range(self.file.endian) + } + + #[inline] + fn data(&self) -> read::Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> read::Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> read::Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn name(&self) -> read::Result<Option<&str>> { + Ok(None) + } + + #[inline] + fn flags(&self) -> SegmentFlags { + let p_flags = self.segment.p_flags(self.file.endian); + SegmentFlags::Elf { p_flags } + } +} + +/// A trait for generic access to [`elf::ProgramHeader32`] and [`elf::ProgramHeader64`]. +#[allow(missing_docs)] +pub trait ProgramHeader: Debug + Pod { + type Elf: FileHeader<ProgramHeader = Self, Endian = Self::Endian, Word = Self::Word>; + type Word: Into<u64>; + type Endian: endian::Endian; + + fn p_type(&self, endian: Self::Endian) -> u32; + fn p_flags(&self, endian: Self::Endian) -> u32; + fn p_offset(&self, endian: Self::Endian) -> Self::Word; + fn p_vaddr(&self, endian: Self::Endian) -> Self::Word; + fn p_paddr(&self, endian: Self::Endian) -> Self::Word; + fn p_filesz(&self, endian: Self::Endian) -> Self::Word; + fn p_memsz(&self, endian: Self::Endian) -> Self::Word; + fn p_align(&self, endian: Self::Endian) -> Self::Word; + + /// Return the offset and size of the segment in the file. + fn file_range(&self, endian: Self::Endian) -> (u64, u64) { + (self.p_offset(endian).into(), self.p_filesz(endian).into()) + } + + /// Return the segment data. + /// + /// Returns `Err` for invalid values. + fn data<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> Result<&'data [u8], ()> { + let (offset, size) = self.file_range(endian); + data.read_bytes_at(offset, size) + } + + /// Return the segment data as a slice of the given type. + /// + /// Allows padding at the end of the data. + /// Returns `Ok(&[])` if the segment has no data. + /// Returns `Err` for invalid values, including bad alignment. + fn data_as_array<'data, T: Pod, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> Result<&'data [T], ()> { + pod::slice_from_all_bytes(self.data(endian, data)?) + } + + /// Return the segment data in the given virtual address range + /// + /// Returns `Ok(None)` if the segment does not contain the address. + /// Returns `Err` for invalid values. + fn data_range<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + address: u64, + size: u64, + ) -> Result<Option<&'data [u8]>, ()> { + Ok(read::util::data_range( + self.data(endian, data)?, + self.p_vaddr(endian).into(), + address, + size, + )) + } + + /// Return entries in a dynamic segment. + /// + /// Returns `Ok(None)` if the segment is not `PT_DYNAMIC`. + /// Returns `Err` for invalid values. + fn dynamic<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<&'data [<Self::Elf as FileHeader>::Dyn]>> { + if self.p_type(endian) != elf::PT_DYNAMIC { + return Ok(None); + } + let dynamic = self + .data_as_array(endian, data) + .read_error("Invalid ELF dynamic segment offset or size")?; + Ok(Some(dynamic)) + } + + /// Return the data in an interpreter segment. + /// + /// Returns `Ok(None)` if the segment is not `PT_INTERP`. + /// Returns `Err` for invalid values. + fn interpreter<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<&'data [u8]>> { + if self.p_type(endian) != elf::PT_INTERP { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF interpreter segment offset or size")?; + let len = data + .iter() + .position(|&b| b == 0) + .read_error("Invalid ELF interpreter segment data")?; + Ok(Some(&data[..len])) + } + + /// Return a note iterator for the segment data. + /// + /// Returns `Ok(None)` if the segment does not contain notes. + /// Returns `Err` for invalid values. + fn notes<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<NoteIterator<'data, Self::Elf>>> { + if self.p_type(endian) != elf::PT_NOTE { + return Ok(None); + } + let data = self + .data(endian, data) + .read_error("Invalid ELF note segment offset or size")?; + let notes = NoteIterator::new(endian, self.p_align(endian), data)?; + Ok(Some(notes)) + } +} + +impl<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader32<Endian> { + type Word = u32; + type Endian = Endian; + type Elf = elf::FileHeader32<Endian>; + + #[inline] + fn p_type(&self, endian: Self::Endian) -> u32 { + self.p_type.get(endian) + } + + #[inline] + fn p_flags(&self, endian: Self::Endian) -> u32 { + self.p_flags.get(endian) + } + + #[inline] + fn p_offset(&self, endian: Self::Endian) -> Self::Word { + self.p_offset.get(endian) + } + + #[inline] + fn p_vaddr(&self, endian: Self::Endian) -> Self::Word { + self.p_vaddr.get(endian) + } + + #[inline] + fn p_paddr(&self, endian: Self::Endian) -> Self::Word { + self.p_paddr.get(endian) + } + + #[inline] + fn p_filesz(&self, endian: Self::Endian) -> Self::Word { + self.p_filesz.get(endian) + } + + #[inline] + fn p_memsz(&self, endian: Self::Endian) -> Self::Word { + self.p_memsz.get(endian) + } + + #[inline] + fn p_align(&self, endian: Self::Endian) -> Self::Word { + self.p_align.get(endian) + } +} + +impl<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader64<Endian> { + type Word = u64; + type Endian = Endian; + type Elf = elf::FileHeader64<Endian>; + + #[inline] + fn p_type(&self, endian: Self::Endian) -> u32 { + self.p_type.get(endian) + } + + #[inline] + fn p_flags(&self, endian: Self::Endian) -> u32 { + self.p_flags.get(endian) + } + + #[inline] + fn p_offset(&self, endian: Self::Endian) -> Self::Word { + self.p_offset.get(endian) + } + + #[inline] + fn p_vaddr(&self, endian: Self::Endian) -> Self::Word { + self.p_vaddr.get(endian) + } + + #[inline] + fn p_paddr(&self, endian: Self::Endian) -> Self::Word { + self.p_paddr.get(endian) + } + + #[inline] + fn p_filesz(&self, endian: Self::Endian) -> Self::Word { + self.p_filesz.get(endian) + } + + #[inline] + fn p_memsz(&self, endian: Self::Endian) -> Self::Word { + self.p_memsz.get(endian) + } + + #[inline] + fn p_align(&self, endian: Self::Endian) -> Self::Word { + self.p_align.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/symbol.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/symbol.rs new file mode 100644 index 0000000..3c0bee3 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/symbol.rs
@@ -0,0 +1,654 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::fmt::Debug; +use core::slice; +use core::str; + +use crate::elf; +use crate::endian::{self, Endianness, U32}; +use crate::pod::Pod; +use crate::read::util::StringTable; +use crate::read::{ + self, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, SectionIndex, SymbolFlags, + SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, +}; + +use super::{FileHeader, SectionHeader, SectionTable}; + +/// A table of symbol entries in an ELF file. +/// +/// Also includes the string table used for the symbol names. +/// +/// Returned by [`SectionTable::symbols`]. +#[derive(Debug, Clone, Copy)] +pub struct SymbolTable<'data, Elf: FileHeader, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + section: SectionIndex, + string_section: SectionIndex, + shndx_section: SectionIndex, + symbols: &'data [Elf::Sym], + strings: StringTable<'data, R>, + shndx: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Elf, R> { + fn default() -> Self { + SymbolTable { + section: SectionIndex(0), + string_section: SectionIndex(0), + shndx_section: SectionIndex(0), + symbols: &[], + strings: Default::default(), + shndx: &[], + } + } +} + +impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { + /// Parse the given symbol table section. + pub fn parse( + endian: Elf::Endian, + data: R, + sections: &SectionTable<'data, Elf, R>, + section_index: SectionIndex, + section: &Elf::SectionHeader, + ) -> read::Result<SymbolTable<'data, Elf, R>> { + debug_assert!( + section.sh_type(endian) == elf::SHT_DYNSYM + || section.sh_type(endian) == elf::SHT_SYMTAB + ); + + let symbols = section + .data_as_array(endian, data) + .read_error("Invalid ELF symbol table data")?; + + let link = SectionIndex(section.sh_link(endian) as usize); + let strings = sections.strings(endian, data, link)?; + + let mut shndx_section = SectionIndex(0); + let mut shndx = &[][..]; + for (i, s) in sections.enumerate() { + if s.sh_type(endian) == elf::SHT_SYMTAB_SHNDX && s.link(endian) == section_index { + shndx_section = i; + shndx = s + .data_as_array(endian, data) + .read_error("Invalid ELF symtab_shndx data")?; + } + } + + Ok(SymbolTable { + section: section_index, + string_section: link, + symbols, + strings, + shndx, + shndx_section, + }) + } + + /// Return the section index of this symbol table. + #[inline] + pub fn section(&self) -> SectionIndex { + self.section + } + + /// Return the section index of the shndx table. + #[inline] + pub fn shndx_section(&self) -> SectionIndex { + self.shndx_section + } + + /// Return the section index of the linked string table. + #[inline] + pub fn string_section(&self) -> SectionIndex { + self.string_section + } + + /// Return the string table used for the symbol names. + #[inline] + pub fn strings(&self) -> StringTable<'data, R> { + self.strings + } + + /// Return the symbol table. + #[inline] + pub fn symbols(&self) -> &'data [Elf::Sym] { + self.symbols + } + + /// Iterate over the symbols. + /// + /// This includes the null symbol at index 0, which you will usually need to skip. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Elf::Sym> { + self.symbols.iter() + } + + /// Iterate over the symbols and their indices. + /// + /// This includes the null symbol at index 0, which you will usually need to skip. + #[inline] + pub fn enumerate(&self) -> impl Iterator<Item = (SymbolIndex, &'data Elf::Sym)> { + self.symbols + .iter() + .enumerate() + .map(|(i, sym)| (SymbolIndex(i), sym)) + } + + /// Return true if the symbol table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// The number of symbols. + #[inline] + pub fn len(&self) -> usize { + self.symbols.len() + } + + /// Get the symbol at the given index. + /// + /// Returns an error for null entry at index 0. + pub fn symbol(&self, index: SymbolIndex) -> read::Result<&'data Elf::Sym> { + if index == SymbolIndex(0) { + return Err(read::Error("Invalid ELF symbol index")); + } + self.symbols + .get(index.0) + .read_error("Invalid ELF symbol index") + } + + /// Return the extended section index for the given symbol if present. + #[inline] + pub fn shndx(&self, endian: Elf::Endian, index: SymbolIndex) -> Option<u32> { + self.shndx.get(index.0).map(|x| x.get(endian)) + } + + /// Return the section index for the given symbol. + /// + /// This uses the extended section index if present. + pub fn symbol_section( + &self, + endian: Elf::Endian, + symbol: &Elf::Sym, + index: SymbolIndex, + ) -> read::Result<Option<SectionIndex>> { + match symbol.st_shndx(endian) { + elf::SHN_UNDEF => Ok(None), + elf::SHN_XINDEX => { + let shndx = self + .shndx(endian, index) + .read_error("Missing ELF symbol extended index")?; + if shndx == 0 { + Ok(None) + } else { + Ok(Some(SectionIndex(shndx as usize))) + } + } + shndx if shndx < elf::SHN_LORESERVE => Ok(Some(SectionIndex(shndx.into()))), + _ => Ok(None), + } + } + + /// Return the symbol name for the given symbol. + pub fn symbol_name(&self, endian: Elf::Endian, symbol: &Elf::Sym) -> read::Result<&'data [u8]> { + symbol.name(endian, self.strings) + } + + /// Construct a map from addresses to a user-defined map entry. + pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Elf::Sym) -> Option<Entry>>( + &self, + endian: Elf::Endian, + f: F, + ) -> SymbolMap<Entry> { + let mut symbols = Vec::with_capacity(self.symbols.len()); + for symbol in self.symbols { + if !symbol.is_definition(endian) { + continue; + } + if let Some(entry) = f(symbol) { + symbols.push(entry); + } + } + SymbolMap::new(symbols) + } +} + +/// A symbol table in an [`ElfFile32`](super::ElfFile32). +pub type ElfSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A symbol table in an [`ElfFile32`](super::ElfFile32). +pub type ElfSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A symbol table in an [`ElfFile`](super::ElfFile). +#[derive(Debug, Clone, Copy)] +pub struct ElfSymbolTable<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Elf::Endian, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed + for ElfSymbolTable<'data, 'file, Elf, R> +{ +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data> + for ElfSymbolTable<'data, 'file, Elf, R> +{ + type Symbol = ElfSymbol<'data, 'file, Elf, R>; + type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; + + fn symbols(&self) -> Self::SymbolIterator { + ElfSymbolIterator::new(self.endian, self.symbols) + } + + fn symbol_by_index(&self, index: SymbolIndex) -> read::Result<Self::Symbol> { + let symbol = self.symbols.symbol(index)?; + Ok(ElfSymbol { + endian: self.endian, + symbols: self.symbols, + index, + symbol, + }) + } +} + +/// An iterator for the symbols in an [`ElfFile32`](super::ElfFile32). +pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator for the symbols in an [`ElfFile64`](super::ElfFile64). +pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator for the symbols in an [`ElfFile`](super::ElfFile). +pub struct ElfSymbolIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + endian: Elf::Endian, + symbols: &'file SymbolTable<'data, Elf, R>, + index: SymbolIndex, +} + +impl<'data, 'file, Elf, R> ElfSymbolIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) fn new(endian: Elf::Endian, symbols: &'file SymbolTable<'data, Elf, R>) -> Self { + ElfSymbolIterator { + endian, + symbols, + index: SymbolIndex(1), + } + } +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> fmt::Debug + for ElfSymbolIterator<'data, 'file, Elf, R> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfSymbolIterator").finish() + } +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> Iterator + for ElfSymbolIterator<'data, 'file, Elf, R> +{ + type Item = ElfSymbol<'data, 'file, Elf, R>; + + fn next(&mut self) -> Option<Self::Item> { + let index = self.index; + let symbol = self.symbols.symbols.get(index.0)?; + self.index.0 += 1; + Some(ElfSymbol { + endian: self.endian, + symbols: self.symbols, + index, + symbol, + }) + } +} + +/// A symbol in an [`ElfFile32`](super::ElfFile32). +pub type ElfSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A symbol in an [`ElfFile64`](super::ElfFile64). +pub type ElfSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A symbol in an [`ElfFile`](super::ElfFile). +/// +/// Most functionality is provided by the [`ObjectSymbol`] trait implementation. +#[derive(Debug, Clone, Copy)] +pub struct ElfSymbol<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Elf::Endian, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, + pub(super) index: SymbolIndex, + pub(super) symbol: &'data Elf::Sym, +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSymbol<'data, 'file, Elf, R> { + /// Get the endianness of the ELF file. + pub fn endian(&self) -> Elf::Endian { + self.endian + } + + /// Return a reference to the raw symbol structure. + #[inline] + #[deprecated(note = "Use `elf_symbol` instead")] + pub fn raw_symbol(&self) -> &'data Elf::Sym { + self.symbol + } + + /// Get the raw ELF symbol structure. + pub fn elf_symbol(&self) -> &'data Elf::Sym { + self.symbol + } +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed + for ElfSymbol<'data, 'file, Elf, R> +{ +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> + for ElfSymbol<'data, 'file, Elf, R> +{ + #[inline] + fn index(&self) -> SymbolIndex { + self.index + } + + fn name_bytes(&self) -> read::Result<&'data [u8]> { + self.symbol.name(self.endian, self.symbols.strings()) + } + + fn name(&self) -> read::Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 ELF symbol name") + } + + #[inline] + fn address(&self) -> u64 { + self.symbol.st_value(self.endian).into() + } + + #[inline] + fn size(&self) -> u64 { + self.symbol.st_size(self.endian).into() + } + + fn kind(&self) -> SymbolKind { + match self.symbol.st_type() { + elf::STT_NOTYPE => SymbolKind::Unknown, + elf::STT_OBJECT | elf::STT_COMMON => SymbolKind::Data, + elf::STT_FUNC | elf::STT_GNU_IFUNC => SymbolKind::Text, + elf::STT_SECTION => SymbolKind::Section, + elf::STT_FILE => SymbolKind::File, + elf::STT_TLS => SymbolKind::Tls, + _ => SymbolKind::Unknown, + } + } + + fn section(&self) -> SymbolSection { + match self.symbol.st_shndx(self.endian) { + elf::SHN_UNDEF => SymbolSection::Undefined, + elf::SHN_ABS => { + if self.symbol.st_type() == elf::STT_FILE { + SymbolSection::None + } else { + SymbolSection::Absolute + } + } + elf::SHN_COMMON => SymbolSection::Common, + elf::SHN_XINDEX => match self.symbols.shndx(self.endian, self.index) { + Some(0) => SymbolSection::None, + Some(index) => SymbolSection::Section(SectionIndex(index as usize)), + None => SymbolSection::Unknown, + }, + index if index < elf::SHN_LORESERVE => { + SymbolSection::Section(SectionIndex(index as usize)) + } + _ => SymbolSection::Unknown, + } + } + + #[inline] + fn is_undefined(&self) -> bool { + self.symbol.is_undefined(self.endian) + } + + #[inline] + fn is_definition(&self) -> bool { + self.symbol.is_definition(self.endian) + } + + #[inline] + fn is_common(&self) -> bool { + self.symbol.is_common(self.endian) + } + + #[inline] + fn is_weak(&self) -> bool { + self.symbol.is_weak() + } + + fn scope(&self) -> SymbolScope { + if self.symbol.st_shndx(self.endian) == elf::SHN_UNDEF { + SymbolScope::Unknown + } else { + match self.symbol.st_bind() { + elf::STB_LOCAL => SymbolScope::Compilation, + elf::STB_GLOBAL | elf::STB_WEAK => { + if self.symbol.st_visibility() == elf::STV_HIDDEN { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + } + } + _ => SymbolScope::Unknown, + } + } + } + + #[inline] + fn is_global(&self) -> bool { + !self.symbol.is_local() + } + + #[inline] + fn is_local(&self) -> bool { + self.symbol.is_local() + } + + #[inline] + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { + SymbolFlags::Elf { + st_info: self.symbol.st_info(), + st_other: self.symbol.st_other(), + } + } +} + +/// A trait for generic access to [`elf::Sym32`] and [`elf::Sym64`]. +#[allow(missing_docs)] +pub trait Sym: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn st_name(&self, endian: Self::Endian) -> u32; + fn st_info(&self) -> u8; + fn st_bind(&self) -> u8; + fn st_type(&self) -> u8; + fn st_other(&self) -> u8; + fn st_visibility(&self) -> u8; + fn st_shndx(&self, endian: Self::Endian) -> u16; + fn st_value(&self, endian: Self::Endian) -> Self::Word; + fn st_size(&self, endian: Self::Endian) -> Self::Word; + + /// Parse the symbol name from the string table. + fn name<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + strings: StringTable<'data, R>, + ) -> read::Result<&'data [u8]> { + strings + .get(self.st_name(endian)) + .read_error("Invalid ELF symbol name offset") + } + + /// Return true if the symbol section is `SHN_UNDEF`. + #[inline] + fn is_undefined(&self, endian: Self::Endian) -> bool { + self.st_shndx(endian) == elf::SHN_UNDEF + } + + /// Return true if the symbol is a definition of a function or data object. + fn is_definition(&self, endian: Self::Endian) -> bool { + let shndx = self.st_shndx(endian); + if shndx == elf::SHN_UNDEF || (shndx >= elf::SHN_LORESERVE && shndx != elf::SHN_XINDEX) { + return false; + } + match self.st_type() { + elf::STT_NOTYPE => self.st_size(endian).into() != 0, + elf::STT_FUNC | elf::STT_OBJECT => true, + _ => false, + } + } + + /// Return true if the symbol section is `SHN_COMMON`. + fn is_common(&self, endian: Self::Endian) -> bool { + self.st_shndx(endian) == elf::SHN_COMMON + } + + /// Return true if the symbol section is `SHN_ABS`. + fn is_absolute(&self, endian: Self::Endian) -> bool { + self.st_shndx(endian) == elf::SHN_ABS + } + + /// Return true if the symbol binding is `STB_LOCAL`. + fn is_local(&self) -> bool { + self.st_bind() == elf::STB_LOCAL + } + + /// Return true if the symbol binding is `STB_WEAK`. + fn is_weak(&self) -> bool { + self.st_bind() == elf::STB_WEAK + } +} + +impl<Endian: endian::Endian> Sym for elf::Sym32<Endian> { + type Word = u32; + type Endian = Endian; + + #[inline] + fn st_name(&self, endian: Self::Endian) -> u32 { + self.st_name.get(endian) + } + + #[inline] + fn st_info(&self) -> u8 { + self.st_info + } + + #[inline] + fn st_bind(&self) -> u8 { + self.st_bind() + } + + #[inline] + fn st_type(&self) -> u8 { + self.st_type() + } + + #[inline] + fn st_other(&self) -> u8 { + self.st_other + } + + #[inline] + fn st_visibility(&self) -> u8 { + self.st_visibility() + } + + #[inline] + fn st_shndx(&self, endian: Self::Endian) -> u16 { + self.st_shndx.get(endian) + } + + #[inline] + fn st_value(&self, endian: Self::Endian) -> Self::Word { + self.st_value.get(endian) + } + + #[inline] + fn st_size(&self, endian: Self::Endian) -> Self::Word { + self.st_size.get(endian) + } +} + +impl<Endian: endian::Endian> Sym for elf::Sym64<Endian> { + type Word = u64; + type Endian = Endian; + + #[inline] + fn st_name(&self, endian: Self::Endian) -> u32 { + self.st_name.get(endian) + } + + #[inline] + fn st_info(&self) -> u8 { + self.st_info + } + + #[inline] + fn st_bind(&self) -> u8 { + self.st_bind() + } + + #[inline] + fn st_type(&self) -> u8 { + self.st_type() + } + + #[inline] + fn st_other(&self) -> u8 { + self.st_other + } + + #[inline] + fn st_visibility(&self) -> u8 { + self.st_visibility() + } + + #[inline] + fn st_shndx(&self, endian: Self::Endian) -> u16 { + self.st_shndx.get(endian) + } + + #[inline] + fn st_value(&self, endian: Self::Endian) -> Self::Word { + self.st_value.get(endian) + } + + #[inline] + fn st_size(&self, endian: Self::Endian) -> Self::Word { + self.st_size.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/elf/version.rs b/chromium_crates_io/vendor/object-v0_37/src/read/elf/version.rs new file mode 100644 index 0000000..2e350a8 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/elf/version.rs
@@ -0,0 +1,513 @@ +use alloc::vec::Vec; + +use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable, SymbolIndex}; +use crate::{elf, endian}; + +use super::FileHeader; + +/// A version index. +#[derive(Debug, Default, Clone, Copy)] +pub struct VersionIndex(pub u16); + +impl VersionIndex { + /// Return the version index. + pub fn index(&self) -> u16 { + self.0 & elf::VERSYM_VERSION + } + + /// Return true if it is the local index. + pub fn is_local(&self) -> bool { + self.index() == elf::VER_NDX_LOCAL + } + + /// Return true if it is the global index. + pub fn is_global(&self) -> bool { + self.index() == elf::VER_NDX_GLOBAL + } + + /// Return the hidden flag. + pub fn is_hidden(&self) -> bool { + self.0 & elf::VERSYM_HIDDEN != 0 + } +} + +/// A version definition or requirement. +/// +/// This is derived from entries in the [`elf::SHT_GNU_VERDEF`] and [`elf::SHT_GNU_VERNEED`] sections. +#[derive(Debug, Default, Clone, Copy)] +pub struct Version<'data> { + name: &'data [u8], + hash: u32, + // Used to keep track of valid indices in `VersionTable`. + valid: bool, + file: Option<&'data [u8]>, +} + +impl<'data> Version<'data> { + /// Return the version name. + pub fn name(&self) -> &'data [u8] { + self.name + } + + /// Return hash of the version name. + pub fn hash(&self) -> u32 { + self.hash + } + + /// Return the filename of the library containing this version. + /// + /// This is the `vn_file` field of the associated entry in [`elf::SHT_GNU_VERNEED`]. + /// or `None` if the version info was parsed from a [`elf::SHT_GNU_VERDEF`] section. + pub fn file(&self) -> Option<&'data [u8]> { + self.file + } +} + +/// A table of version definitions and requirements. +/// +/// It allows looking up the version information for a given symbol index. +/// +/// This is derived from entries in the [`elf::SHT_GNU_VERSYM`], [`elf::SHT_GNU_VERDEF`] +/// and [`elf::SHT_GNU_VERNEED`] sections. +/// +/// Returned by [`SectionTable::versions`](super::SectionTable::versions). +#[derive(Debug, Clone)] +pub struct VersionTable<'data, Elf: FileHeader> { + symbols: &'data [elf::Versym<Elf::Endian>], + versions: Vec<Version<'data>>, +} + +impl<'data, Elf: FileHeader> Default for VersionTable<'data, Elf> { + fn default() -> Self { + VersionTable { + symbols: &[], + versions: Vec::new(), + } + } +} + +impl<'data, Elf: FileHeader> VersionTable<'data, Elf> { + /// Parse the version sections. + pub fn parse<R: ReadRef<'data>>( + endian: Elf::Endian, + versyms: &'data [elf::Versym<Elf::Endian>], + verdefs: Option<VerdefIterator<'data, Elf>>, + verneeds: Option<VerneedIterator<'data, Elf>>, + strings: StringTable<'data, R>, + ) -> Result<Self> { + let mut max_index = 0; + if let Some(mut verdefs) = verdefs.clone() { + while let Some((verdef, _)) = verdefs.next()? { + if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { + continue; + } + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + if let Some(mut verneeds) = verneeds.clone() { + while let Some((_, mut vernauxs)) = verneeds.next()? { + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + } + + // Indices should be sequential, but this could be up to + // 32k * size_of::<Version>() if max_index is bad. + let mut versions = vec![Version::default(); max_index as usize + 1]; + + if let Some(mut verdefs) = verdefs { + while let Some((verdef, mut verdauxs)) = verdefs.next()? { + if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { + continue; + } + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if index <= elf::VER_NDX_GLOBAL { + // TODO: return error? + continue; + } + if let Some(verdaux) = verdauxs.next()? { + versions[usize::from(index)] = Version { + name: verdaux.name(endian, strings)?, + hash: verdef.vd_hash.get(endian), + valid: true, + file: None, + }; + } + } + } + if let Some(mut verneeds) = verneeds { + while let Some((verneed, mut vernauxs)) = verneeds.next()? { + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if index <= elf::VER_NDX_GLOBAL { + // TODO: return error? + continue; + } + versions[usize::from(index)] = Version { + name: vernaux.name(endian, strings)?, + hash: vernaux.vna_hash.get(endian), + valid: true, + file: Some(verneed.file(endian, strings)?), + }; + } + } + } + + Ok(VersionTable { + symbols: versyms, + versions, + }) + } + + /// Return true if the version table is empty. + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// Return version index for a given symbol index. + pub fn version_index(&self, endian: Elf::Endian, index: SymbolIndex) -> VersionIndex { + let version_index = match self.symbols.get(index.0) { + Some(x) => x.0.get(endian), + // Ideally this would be VER_NDX_LOCAL for undefined symbols, + // but currently there are no checks that need this distinction. + None => elf::VER_NDX_GLOBAL, + }; + VersionIndex(version_index) + } + + /// Return version information for a given symbol version index. + /// + /// Returns `Ok(None)` for local and global versions. + /// Returns `Err(_)` if index is invalid. + pub fn version(&self, index: VersionIndex) -> Result<Option<&Version<'data>>> { + if index.index() <= elf::VER_NDX_GLOBAL { + return Ok(None); + } + self.versions + .get(usize::from(index.index())) + .filter(|version| version.valid) + .read_error("Invalid ELF symbol version index") + .map(Some) + } + + /// Return true if the given symbol index satisfies the requirements of `need`. + /// + /// Returns false for any error. + /// + /// Note: this function hasn't been fully tested and is likely to be incomplete. + pub fn matches( + &self, + endian: Elf::Endian, + index: SymbolIndex, + need: Option<&Version<'_>>, + ) -> bool { + let version_index = self.version_index(endian, index); + let def = match self.version(version_index) { + Ok(def) => def, + Err(_) => return false, + }; + match (def, need) { + (Some(def), Some(need)) => need.hash == def.hash && need.name == def.name, + (None, Some(_need)) => { + // Version must be present if needed. + false + } + (Some(_def), None) => { + // For a dlsym call, use the newest version. + // TODO: if not a dlsym call, then use the oldest version. + !version_index.is_hidden() + } + (None, None) => true, + } + } +} + +/// An iterator for the entries in an ELF [`elf::SHT_GNU_VERDEF`] section. +#[derive(Debug, Clone)] +pub struct VerdefIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { + VerdefIterator { + endian, + data: Bytes(data), + } + } + + /// Return the next `Verdef` entry. + pub fn next( + &mut self, + ) -> Result<Option<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> Result<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)> { + let verdef = self + .data + .read_at::<elf::Verdef<_>>(0) + .read_error("ELF verdef is too short")?; + + let mut verdaux_data = self.data; + verdaux_data + .skip(verdef.vd_aux.get(self.endian) as usize) + .read_error("Invalid ELF vd_aux")?; + let verdaux = + VerdauxIterator::new(self.endian, verdaux_data.0, verdef.vd_cnt.get(self.endian)); + + let next = verdef.vd_next.get(self.endian); + if next != 0 { + self.data + .skip(next as usize) + .read_error("Invalid ELF vd_next")?; + } else { + self.data = Bytes(&[]); + } + Ok((verdef, verdaux)) + } +} + +impl<'data, Elf: FileHeader> Iterator for VerdefIterator<'data, Elf> { + type Item = Result<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERDEF`] section. +#[derive(Debug, Clone)] +pub struct VerdauxIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, + count: u16, +} + +impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { + VerdauxIterator { + endian, + data: Bytes(data), + count, + } + } + + /// Return the next `Verdaux` entry. + pub fn next(&mut self) -> Result<Option<&'data elf::Verdaux<Elf::Endian>>> { + if self.count == 0 { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.count = 0; + } else { + self.count -= 1; + } + result + } + + fn parse(&mut self) -> Result<&'data elf::Verdaux<Elf::Endian>> { + let verdaux = self + .data + .read_at::<elf::Verdaux<_>>(0) + .read_error("ELF verdaux is too short")?; + + self.data + .skip(verdaux.vda_next.get(self.endian) as usize) + .read_error("Invalid ELF vda_next")?; + Ok(verdaux) + } +} + +impl<'data, Elf: FileHeader> Iterator for VerdauxIterator<'data, Elf> { + type Item = Result<&'data elf::Verdaux<Elf::Endian>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// An iterator for the entries in an ELF [`elf::SHT_GNU_VERNEED`] section. +#[derive(Debug, Clone)] +pub struct VerneedIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { + VerneedIterator { + endian, + data: Bytes(data), + } + } + + /// Return the next `Verneed` entry. + pub fn next( + &mut self, + ) -> Result< + Option<( + &'data elf::Verneed<Elf::Endian>, + VernauxIterator<'data, Elf>, + )>, + > { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse( + &mut self, + ) -> Result<( + &'data elf::Verneed<Elf::Endian>, + VernauxIterator<'data, Elf>, + )> { + let verneed = self + .data + .read_at::<elf::Verneed<_>>(0) + .read_error("ELF verneed is too short")?; + + let mut vernaux_data = self.data; + vernaux_data + .skip(verneed.vn_aux.get(self.endian) as usize) + .read_error("Invalid ELF vn_aux")?; + let vernaux = + VernauxIterator::new(self.endian, vernaux_data.0, verneed.vn_cnt.get(self.endian)); + + let next = verneed.vn_next.get(self.endian); + if next != 0 { + self.data + .skip(next as usize) + .read_error("Invalid ELF vn_next")?; + } else { + self.data = Bytes(&[]); + } + Ok((verneed, vernaux)) + } +} + +impl<'data, Elf: FileHeader> Iterator for VerneedIterator<'data, Elf> { + type Item = Result<( + &'data elf::Verneed<Elf::Endian>, + VernauxIterator<'data, Elf>, + )>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERNEED`] section. +#[derive(Debug, Clone)] +pub struct VernauxIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, + count: u16, +} + +impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { + VernauxIterator { + endian, + data: Bytes(data), + count, + } + } + + /// Return the next `Vernaux` entry. + pub fn next(&mut self) -> Result<Option<&'data elf::Vernaux<Elf::Endian>>> { + if self.count == 0 { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.count = 0; + } else { + self.count -= 1; + } + result + } + + fn parse(&mut self) -> Result<&'data elf::Vernaux<Elf::Endian>> { + let vernaux = self + .data + .read_at::<elf::Vernaux<_>>(0) + .read_error("ELF vernaux is too short")?; + self.data + .skip(vernaux.vna_next.get(self.endian) as usize) + .read_error("Invalid ELF vna_next")?; + Ok(vernaux) + } +} + +impl<'data, Elf: FileHeader> Iterator for VernauxIterator<'data, Elf> { + type Item = Result<&'data elf::Vernaux<Elf::Endian>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +impl<Endian: endian::Endian> elf::Verdaux<Endian> { + /// Parse the version name from the string table. + pub fn name<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vda_name.get(endian)) + .read_error("Invalid ELF vda_name") + } +} + +impl<Endian: endian::Endian> elf::Verneed<Endian> { + /// Parse the file from the string table. + pub fn file<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vn_file.get(endian)) + .read_error("Invalid ELF vn_file") + } +} + +impl<Endian: endian::Endian> elf::Vernaux<Endian> { + /// Parse the version name from the string table. + pub fn name<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vna_name.get(endian)) + .read_error("Invalid ELF vna_name") + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/gnu_compression.rs b/chromium_crates_io/vendor/object-v0_37/src/read/gnu_compression.rs new file mode 100644 index 0000000..7ef7d91 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/gnu_compression.rs
@@ -0,0 +1,36 @@ +use crate::read::{self, Error, ReadError as _}; +use crate::{endian, CompressedFileRange, CompressionFormat, ReadRef, U32Bytes}; + +// Attempt to parse the the CompressedFileRange for a section using the GNU-style +// inline compression header format. This is used by the Go compiler in Mach-O files +// as well as by the GNU linker in some ELF files. +pub(super) fn compressed_file_range<'data, R: ReadRef<'data>>( + file_data: R, + section_offset: u64, + section_size: u64, +) -> read::Result<CompressedFileRange> { + let mut offset = section_offset; + // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally + // huge allocations. This also reduces the chance of accidentally matching on a + // .debug_str that happens to start with "ZLIB". + let header = file_data + .read_bytes(&mut offset, 8) + .read_error("GNU compressed section is too short")?; + if header != b"ZLIB\0\0\0\0" { + return Err(Error("Invalid GNU compressed section header")); + } + let uncompressed_size = file_data + .read::<U32Bytes<_>>(&mut offset) + .read_error("GNU compressed section is too short")? + .get(endian::BigEndian) + .into(); + let compressed_size = section_size + .checked_sub(offset - section_offset) + .read_error("GNU compressed section is too short")?; + Ok(CompressedFileRange { + format: CompressionFormat::Zlib, + offset, + compressed_size, + uncompressed_size, + }) +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/dyld_cache.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/dyld_cache.rs new file mode 100644 index 0000000..b095876 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/dyld_cache.rs
@@ -0,0 +1,1148 @@ +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::fmt::{self, Debug}; +use core::{mem, slice}; + +use crate::endian::{Endian, Endianness, U16, U32, U64}; +use crate::macho; +use crate::read::{Architecture, Error, File, ReadError, ReadRef, Result}; + +/// A parsed representation of the dyld shared cache. +#[derive(Debug)] +pub struct DyldCache<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + endian: E, + data: R, + /// The first entry is the main cache file, and the rest are subcaches. + files: Vec<DyldFile<'data, E, R>>, + images: &'data [macho::DyldCacheImageInfo<E>], + arch: Architecture, +} + +/// A slice of structs describing each subcache. +/// +/// The struct gained an additional field (the file suffix) in dyld-1042.1 (macOS 13 / iOS 16), +/// so this is an enum of the two possible slice types. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum DyldSubCacheSlice<'data, E: Endian> { + /// V1, used between dyld-940 and dyld-1042.1. + V1(&'data [macho::DyldSubCacheEntryV1<E>]), + /// V2, used since dyld-1042.1. + V2(&'data [macho::DyldSubCacheEntryV2<E>]), +} + +// This is the offset of the end of the images_count field. +const MIN_HEADER_SIZE_SUBCACHES_V1: u32 = 0x1c8; + +// This is the offset of the end of the cache_sub_type field. +const MIN_HEADER_SIZE_SUBCACHES_V2: u32 = 0x1d0; + +impl<'data, E, R> DyldCache<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + /// Return the suffixes of the subcache files given the data of the main cache file. + /// + /// Each of these should be appended to the path of the main cache file. + pub fn subcache_suffixes(data: R) -> Result<Vec<String>> { + let header = macho::DyldCacheHeader::<E>::parse(data)?; + let (_arch, endian) = header.parse_magic()?; + let Some(subcaches_info) = header.subcaches(endian, data)? else { + return Ok(Vec::new()); + }; + let mut subcache_suffixes: Vec<String> = match subcaches_info { + DyldSubCacheSlice::V1(subcaches) => { + // macOS 12: Subcaches have the file suffixes .1, .2, .3 etc. + (1..subcaches.len() + 1).map(|i| format!(".{i}")).collect() + } + DyldSubCacheSlice::V2(subcaches) => { + // macOS 13+: The subcache file suffix is written down in the header of the main cache. + subcaches + .iter() + .map(|s| { + // The suffix is a nul-terminated string in a fixed-size byte array. + let suffix = s.file_suffix; + let len = suffix.iter().position(|&c| c == 0).unwrap_or(suffix.len()); + String::from_utf8_lossy(&suffix[..len]).to_string() + }) + .collect() + } + }; + if header.symbols_subcache_uuid(endian).is_some() { + subcache_suffixes.push(".symbols".to_string()); + } + Ok(subcache_suffixes) + } + + /// Parse the raw dyld shared cache data. + /// + /// For shared caches from macOS 12 / iOS 15 and above, the subcache files need to be + /// supplied as well, in the correct order. Use [`Self::subcache_suffixes`] to obtain + /// the suffixes for the path of the files. + pub fn parse(data: R, subcache_data: &[R]) -> Result<Self> { + let header = macho::DyldCacheHeader::parse(data)?; + let (arch, endian) = header.parse_magic()?; + + let mut files = Vec::new(); + let mappings = header.mappings(endian, data)?; + files.push(DyldFile { data, mappings }); + + let symbols_subcache_uuid = header.symbols_subcache_uuid(endian); + let subcaches_info = header.subcaches(endian, data)?; + let subcaches_count = match subcaches_info { + Some(DyldSubCacheSlice::V1(subcaches)) => subcaches.len(), + Some(DyldSubCacheSlice::V2(subcaches)) => subcaches.len(), + None => 0, + }; + if subcache_data.len() != subcaches_count + symbols_subcache_uuid.is_some() as usize { + return Err(Error("Incorrect number of SubCaches")); + } + + // Split out the .symbols subcache data from the other subcaches. + let (symbols_subcache_data_and_uuid, subcache_data) = + if let Some(symbols_uuid) = symbols_subcache_uuid { + let (sym_data, rest_data) = subcache_data.split_last().unwrap(); + (Some((*sym_data, symbols_uuid)), rest_data) + } else { + (None, subcache_data) + }; + + // Read the regular SubCaches, if present. + if let Some(subcaches_info) = subcaches_info { + let (v1, v2) = match subcaches_info { + DyldSubCacheSlice::V1(s) => (s, &[][..]), + DyldSubCacheSlice::V2(s) => (&[][..], s), + }; + let uuids = v1.iter().map(|e| &e.uuid).chain(v2.iter().map(|e| &e.uuid)); + for (&data, uuid) in subcache_data.iter().zip(uuids) { + let header = macho::DyldCacheHeader::<E>::parse(data)?; + if &header.uuid != uuid { + return Err(Error("Unexpected SubCache UUID")); + } + let mappings = header.mappings(endian, data)?; + files.push(DyldFile { data, mappings }); + } + } + + // Read the .symbols SubCache, if present. + // Other than the UUID verification, the symbols SubCache is currently unused. + let _symbols_subcache = match symbols_subcache_data_and_uuid { + Some((data, uuid)) => { + let header = macho::DyldCacheHeader::<E>::parse(data)?; + if header.uuid != uuid { + return Err(Error("Unexpected .symbols SubCache UUID")); + } + let mappings = header.mappings(endian, data)?; + Some(DyldFile { data, mappings }) + } + None => None, + }; + + let images = header.images(endian, data)?; + Ok(DyldCache { + endian, + data, + files, + images, + arch, + }) + } + + /// Get the architecture type of the file. + pub fn architecture(&self) -> Architecture { + self.arch + } + + /// Get the endianness of the file. + #[inline] + pub fn endianness(&self) -> Endianness { + if self.is_little_endian() { + Endianness::Little + } else { + Endianness::Big + } + } + + /// Get the data of the main cache file. + #[inline] + pub fn data(&self) -> R { + self.data + } + + /// Return true if the file is little endian, false if it is big endian. + pub fn is_little_endian(&self) -> bool { + self.endian.is_little_endian() + } + + /// Iterate over the images in this cache. + pub fn images<'cache>(&'cache self) -> DyldCacheImageIterator<'data, 'cache, E, R> { + DyldCacheImageIterator { + cache: self, + iter: self.images.iter(), + } + } + + /// Return all the mappings in this cache. + pub fn mappings<'cache>( + &'cache self, + ) -> impl Iterator<Item = DyldCacheMapping<'data, E, R>> + 'cache { + let endian = self.endian; + self.files + .iter() + .flat_map(move |file| file.mappings(endian)) + } + + /// Find the address in a mapping and return the cache or subcache data it was found in, + /// together with the translated file offset. + pub fn data_and_offset_for_address(&self, address: u64) -> Option<(R, u64)> { + for file in &self.files { + if let Some(file_offset) = file.address_to_file_offset(self.endian, address) { + return Some((file.data, file_offset)); + } + } + None + } +} + +/// The data for one file in the cache. +#[derive(Debug)] +struct DyldFile<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + data: R, + mappings: DyldCacheMappingSlice<'data, E>, +} + +impl<'data, E, R> DyldFile<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + /// Return an iterator for the mappings. + fn mappings(&self, endian: E) -> DyldCacheMappingIterator<'data, E, R> { + let iter = match self.mappings { + DyldCacheMappingSlice::V1(info) => DyldCacheMappingVersionIterator::V1(info.iter()), + DyldCacheMappingSlice::V2(info) => DyldCacheMappingVersionIterator::V2(info.iter()), + }; + DyldCacheMappingIterator { + endian, + data: self.data, + iter, + } + } + + /// Find the file offset an address in the mappings. + fn address_to_file_offset(&self, endian: E, address: u64) -> Option<u64> { + for mapping in self.mappings(endian) { + let mapping_address = mapping.address(); + if address >= mapping_address && address < mapping_address.wrapping_add(mapping.size()) + { + return Some(address - mapping_address + mapping.file_offset()); + } + } + None + } +} + +/// An iterator over all the images (dylibs) in the dyld shared cache. +#[derive(Debug)] +pub struct DyldCacheImageIterator<'data, 'cache, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + cache: &'cache DyldCache<'data, E, R>, + iter: slice::Iter<'data, macho::DyldCacheImageInfo<E>>, +} + +impl<'data, 'cache, E, R> Iterator for DyldCacheImageIterator<'data, 'cache, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + type Item = DyldCacheImage<'data, 'cache, E, R>; + + fn next(&mut self) -> Option<DyldCacheImage<'data, 'cache, E, R>> { + let image_info = self.iter.next()?; + Some(DyldCacheImage { + cache: self.cache, + image_info, + }) + } +} + +/// One image (dylib) from inside the dyld shared cache. +#[derive(Debug)] +pub struct DyldCacheImage<'data, 'cache, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + pub(crate) cache: &'cache DyldCache<'data, E, R>, + image_info: &'data macho::DyldCacheImageInfo<E>, +} + +impl<'data, 'cache, E, R> DyldCacheImage<'data, 'cache, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + /// Return the raw data structure for this image. + pub fn info(&self) -> &'data macho::DyldCacheImageInfo<E> { + self.image_info + } + + /// The file system path of this image. + pub fn path(&self) -> Result<&'data str> { + let path = self.image_info.path(self.cache.endian, self.cache.data)?; + // The path should always be ascii, so from_utf8 should always succeed. + let path = core::str::from_utf8(path).map_err(|_| Error("Path string not valid utf-8"))?; + Ok(path) + } + + /// The subcache data which contains the Mach-O header for this image, + /// together with the file offset at which this image starts. + pub fn image_data_and_offset(&self) -> Result<(R, u64)> { + let address = self.image_info.address.get(self.cache.endian); + self.cache + .data_and_offset_for_address(address) + .ok_or(Error("Address not found in any mapping")) + } + + /// Parse this image into an Object. + pub fn parse_object(&self) -> Result<File<'data, R>> { + File::parse_dyld_cache_image(self) + } +} + +/// The array of mappings for a single dyld cache file. +/// +/// The mappings gained slide info in dyld-832.7 (macOS 11) +/// so this is an enum of the two possible slice types. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum DyldCacheMappingSlice<'data, E: Endian = Endianness> { + /// V1, used before dyld-832.7. + V1(&'data [macho::DyldCacheMappingInfo<E>]), + /// V2, used since dyld-832.7. + V2(&'data [macho::DyldCacheMappingAndSlideInfo<E>]), +} + +// This is the offset of the end of the mapping_with_slide_count field. +const MIN_HEADER_SIZE_MAPPINGS_V2: u32 = 0x140; + +/// An iterator over all the mappings for one subcache in a dyld shared cache. +#[derive(Debug)] +pub struct DyldCacheMappingIterator<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + endian: E, + data: R, + iter: DyldCacheMappingVersionIterator<'data, E>, +} + +#[derive(Debug)] +enum DyldCacheMappingVersionIterator<'data, E = Endianness> +where + E: Endian, +{ + V1(slice::Iter<'data, macho::DyldCacheMappingInfo<E>>), + V2(slice::Iter<'data, macho::DyldCacheMappingAndSlideInfo<E>>), +} + +impl<'data, E, R> Iterator for DyldCacheMappingIterator<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + type Item = DyldCacheMapping<'data, E, R>; + + fn next(&mut self) -> Option<Self::Item> { + let info = match &mut self.iter { + DyldCacheMappingVersionIterator::V1(iter) => DyldCacheMappingVersion::V1(iter.next()?), + DyldCacheMappingVersionIterator::V2(iter) => DyldCacheMappingVersion::V2(iter.next()?), + }; + Some(DyldCacheMapping { + endian: self.endian, + data: self.data, + info, + }) + } +} + +/// Information about a mapping. +#[derive(Clone, Copy)] +pub struct DyldCacheMapping<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + endian: E, + data: R, + info: DyldCacheMappingVersion<'data, E>, +} + +#[derive(Clone, Copy)] +enum DyldCacheMappingVersion<'data, E = Endianness> +where + E: Endian, +{ + V1(&'data macho::DyldCacheMappingInfo<E>), + V2(&'data macho::DyldCacheMappingAndSlideInfo<E>), +} + +impl<'data, E, R> Debug for DyldCacheMapping<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DyldCacheMapping") + .field("address", &format_args!("{:#x}", self.address())) + .field("size", &format_args!("{:#x}", self.size())) + .field("file_offset", &format_args!("{:#x}", self.file_offset())) + .field("max_prot", &format_args!("{:#x}", self.max_prot())) + .field("init_prot", &format_args!("{:#x}", self.init_prot())) + .finish() + } +} + +impl<'data, E, R> DyldCacheMapping<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + /// The mapping address + pub fn address(&self) -> u64 { + match self.info { + DyldCacheMappingVersion::V1(info) => info.address.get(self.endian), + DyldCacheMappingVersion::V2(info) => info.address.get(self.endian), + } + } + + /// The mapping size + pub fn size(&self) -> u64 { + match self.info { + DyldCacheMappingVersion::V1(info) => info.size.get(self.endian), + DyldCacheMappingVersion::V2(info) => info.size.get(self.endian), + } + } + + /// The mapping file offset + pub fn file_offset(&self) -> u64 { + match self.info { + DyldCacheMappingVersion::V1(info) => info.file_offset.get(self.endian), + DyldCacheMappingVersion::V2(info) => info.file_offset.get(self.endian), + } + } + + /// The mapping maximum protection + pub fn max_prot(&self) -> u32 { + match self.info { + DyldCacheMappingVersion::V1(info) => info.max_prot.get(self.endian), + DyldCacheMappingVersion::V2(info) => info.max_prot.get(self.endian), + } + } + + /// The mapping initial protection + pub fn init_prot(&self) -> u32 { + match self.info { + DyldCacheMappingVersion::V1(info) => info.init_prot.get(self.endian), + DyldCacheMappingVersion::V2(info) => info.init_prot.get(self.endian), + } + } + + /// The mapping data + pub fn data(&self) -> Result<&'data [u8]> { + self.data + .read_bytes_at(self.file_offset(), self.size()) + .read_error("Failed to read bytes for mapping") + } + + /// Relocations for the mapping + pub fn relocations(&self) -> Result<DyldCacheRelocationIterator<'data, E, R>> { + let data = self.data; + let endian = self.endian; + let version = match self.info { + DyldCacheMappingVersion::V1(_) => DyldCacheRelocationIteratorVersion::None, + DyldCacheMappingVersion::V2(mapping) => match mapping.slide(self.endian, self.data)? { + DyldCacheSlideInfo::None => DyldCacheRelocationIteratorVersion::None, + DyldCacheSlideInfo::V2 { + slide, + page_starts, + page_extras, + } => { + let delta_mask = slide.delta_mask.get(endian); + let delta_shift = delta_mask.trailing_zeros(); + DyldCacheRelocationIteratorVersion::V2(DyldCacheRelocationIteratorV2 { + data, + endian, + mapping_file_offset: mapping.file_offset.get(endian), + page_size: slide.page_size.get(endian).into(), + delta_mask, + delta_shift, + value_add: slide.value_add.get(endian), + page_starts, + page_extras, + state: RelocationStateV2::Start, + start_index: 0, + extra_index: 0, + page_offset: 0, + offset: 0, + }) + } + DyldCacheSlideInfo::V3 { slide, page_starts } => { + DyldCacheRelocationIteratorVersion::V3(DyldCacheRelocationIteratorV3 { + data, + endian, + mapping_file_offset: mapping.file_offset.get(endian), + page_size: slide.page_size.get(endian).into(), + auth_value_add: slide.auth_value_add.get(endian), + page_starts, + state: RelocationStateV3::Start, + start_index: 0, + offset: 0, + }) + } + DyldCacheSlideInfo::V5 { slide, page_starts } => { + DyldCacheRelocationIteratorVersion::V5(DyldCacheRelocationIteratorV5 { + data, + endian, + mapping_file_offset: mapping.file_offset.get(endian), + page_size: slide.page_size.get(endian).into(), + value_add: slide.value_add.get(endian), + page_starts, + state: RelocationStateV5::Start, + start_index: 0, + offset: 0, + }) + } + }, + }; + Ok(DyldCacheRelocationIterator { version }) + } +} + +/// The slide info for a dyld cache mapping, including variable length arrays. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +#[allow(missing_docs)] +pub enum DyldCacheSlideInfo<'data, E: Endian> { + None, + V2 { + slide: &'data macho::DyldCacheSlideInfo2<E>, + page_starts: &'data [U16<E>], + page_extras: &'data [U16<E>], + }, + V3 { + slide: &'data macho::DyldCacheSlideInfo3<E>, + page_starts: &'data [U16<E>], + }, + V5 { + slide: &'data macho::DyldCacheSlideInfo5<E>, + page_starts: &'data [U16<E>], + }, +} + +/// An iterator over relocations in a mapping +#[derive(Debug)] +pub struct DyldCacheRelocationIterator<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + version: DyldCacheRelocationIteratorVersion<'data, E, R>, +} + +impl<'data, E, R> Iterator for DyldCacheRelocationIterator<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + type Item = Result<DyldRelocation>; + + fn next(&mut self) -> Option<Self::Item> { + match &mut self.version { + DyldCacheRelocationIteratorVersion::None => Ok(None), + DyldCacheRelocationIteratorVersion::V2(iter) => iter.next(), + DyldCacheRelocationIteratorVersion::V3(iter) => iter.next(), + DyldCacheRelocationIteratorVersion::V5(iter) => iter.next(), + } + .transpose() + } +} + +#[derive(Debug)] +enum DyldCacheRelocationIteratorVersion<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + None, + V2(DyldCacheRelocationIteratorV2<'data, E, R>), + V3(DyldCacheRelocationIteratorV3<'data, E, R>), + V5(DyldCacheRelocationIteratorV5<'data, E, R>), +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum RelocationStateV2 { + Start, + Extra, + Page, + PageExtra, +} + +#[derive(Debug)] +struct DyldCacheRelocationIteratorV2<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + data: R, + endian: E, + mapping_file_offset: u64, + page_size: u64, + delta_mask: u64, + delta_shift: u32, + value_add: u64, + page_starts: &'data [U16<E>], + page_extras: &'data [U16<E>], + + state: RelocationStateV2, + /// The next index within page_starts. + start_index: usize, + /// The next index within page_extras. + extra_index: usize, + /// The current page offset within the mapping. + page_offset: u64, + /// The offset of the next linked list entry within the page. + offset: u64, +} + +impl<'data, E, R> DyldCacheRelocationIteratorV2<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + fn next(&mut self) -> Result<Option<DyldRelocation>> { + loop { + match self.state { + RelocationStateV2::Start => { + let Some(page_start) = self.page_starts.get(self.start_index) else { + return Ok(None); + }; + self.page_offset = self.start_index as u64 * self.page_size; + self.start_index += 1; + + let page_start = page_start.get(self.endian); + if page_start & macho::DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE != 0 { + self.state = RelocationStateV2::Start; + } else if page_start & macho::DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA != 0 { + self.state = RelocationStateV2::Extra; + self.extra_index = + usize::from(page_start & !macho::DYLD_CACHE_SLIDE_PAGE_ATTRS); + } else { + self.state = RelocationStateV2::Page; + self.offset = + u64::from(page_start & !macho::DYLD_CACHE_SLIDE_PAGE_ATTRS) * 4; + } + } + RelocationStateV2::Extra => { + let Some(page_extra) = self.page_extras.get(self.extra_index) else { + return Ok(None); + }; + self.extra_index += 1; + + let page_extra = page_extra.get(self.endian); + self.offset = u64::from(page_extra & !macho::DYLD_CACHE_SLIDE_PAGE_ATTRS) * 4; + if page_extra & macho::DYLD_CACHE_SLIDE_PAGE_ATTR_END != 0 { + self.state = RelocationStateV2::Page; + } else { + self.state = RelocationStateV2::PageExtra; + } + } + RelocationStateV2::Page | RelocationStateV2::PageExtra => { + let offset = self.offset; + let pointer = self + .data + .read_at::<U64<E>>(self.mapping_file_offset + self.page_offset + offset) + .read_error("Invalid dyld cache slide pointer offset")? + .get(self.endian); + + let next = (pointer & self.delta_mask) >> self.delta_shift; + if next == 0 { + if self.state == RelocationStateV2::PageExtra { + self.state = RelocationStateV2::Extra + } else { + self.state = RelocationStateV2::Start + }; + } else { + self.offset = offset + next * 4; + }; + + let value = pointer & !self.delta_mask; + if value != 0 { + return Ok(Some(DyldRelocation { + offset, + value: value + self.value_add, + auth: None, + })); + } + } + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum RelocationStateV3 { + Start, + Page, +} + +#[derive(Debug)] +struct DyldCacheRelocationIteratorV3<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + data: R, + endian: E, + mapping_file_offset: u64, + auth_value_add: u64, + page_size: u64, + page_starts: &'data [U16<E>], + + state: RelocationStateV3, + /// Index of the page within the mapping. + start_index: usize, + /// The current offset within the mapping. + offset: u64, +} + +impl<'data, E, R> DyldCacheRelocationIteratorV3<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + fn next(&mut self) -> Result<Option<DyldRelocation>> { + loop { + match self.state { + RelocationStateV3::Start => { + let Some(page_start) = self.page_starts.get(self.start_index) else { + return Ok(None); + }; + let page_offset = self.start_index as u64 * self.page_size; + self.start_index += 1; + + let page_start = page_start.get(self.endian); + if page_start == macho::DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE { + self.state = RelocationStateV3::Start; + } else { + self.state = RelocationStateV3::Page; + self.offset = page_offset + u64::from(page_start); + } + } + RelocationStateV3::Page => { + let offset = self.offset; + let pointer = self + .data + .read_at::<U64<E>>(self.mapping_file_offset + offset) + .read_error("Invalid dyld cache slide pointer offset")? + .get(self.endian); + let pointer = macho::DyldCacheSlidePointer3(pointer); + + let next = pointer.next(); + if next == 0 { + self.state = RelocationStateV3::Start; + } else { + self.offset = offset + next * 8; + } + + if pointer.is_auth() { + let value = pointer.runtime_offset() + self.auth_value_add; + let key = match pointer.key() { + 1 => macho::PtrauthKey::IB, + 2 => macho::PtrauthKey::DA, + 3 => macho::PtrauthKey::DB, + _ => macho::PtrauthKey::IA, + }; + let auth = Some(DyldRelocationAuth { + key, + diversity: pointer.diversity(), + addr_div: pointer.addr_div(), + }); + return Ok(Some(DyldRelocation { + offset, + value, + auth, + })); + } else { + let value = pointer.target() | pointer.high8() << 56; + return Ok(Some(DyldRelocation { + offset, + value, + auth: None, + })); + }; + } + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum RelocationStateV5 { + Start, + Page, +} + +#[derive(Debug)] +struct DyldCacheRelocationIteratorV5<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + data: R, + endian: E, + mapping_file_offset: u64, + page_size: u64, + value_add: u64, + page_starts: &'data [U16<E>], + + state: RelocationStateV5, + /// The next index within page_starts. + start_index: usize, + /// The current offset within the mapping. + offset: u64, +} + +impl<'data, E, R> DyldCacheRelocationIteratorV5<'data, E, R> +where + E: Endian, + R: ReadRef<'data>, +{ + fn next(&mut self) -> Result<Option<DyldRelocation>> { + loop { + match self.state { + RelocationStateV5::Start => { + let Some(page_start) = self.page_starts.get(self.start_index) else { + return Ok(None); + }; + let page_offset = self.start_index as u64 * self.page_size; + self.start_index += 1; + + let page_start = page_start.get(self.endian); + if page_start == macho::DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE { + self.state = RelocationStateV5::Start; + } else { + self.state = RelocationStateV5::Page; + self.offset = page_offset + u64::from(page_start); + } + } + RelocationStateV5::Page => { + let offset = self.offset; + let pointer = self + .data + .read_at::<U64<E>>(self.mapping_file_offset + offset) + .read_error("Invalid dyld cache slide pointer offset")? + .get(self.endian); + let pointer = macho::DyldCacheSlidePointer5(pointer); + + let next = pointer.next(); + if next == 0 { + self.state = RelocationStateV5::Start; + } else { + self.offset = offset + next * 8; + } + + let mut value = pointer.runtime_offset() + self.value_add; + let auth = if pointer.is_auth() { + let key = if pointer.key_is_data() { + macho::PtrauthKey::DA + } else { + macho::PtrauthKey::IA + }; + Some(DyldRelocationAuth { + key, + diversity: pointer.diversity(), + addr_div: pointer.addr_div(), + }) + } else { + value |= pointer.high8() << 56; + None + }; + return Ok(Some(DyldRelocation { + offset, + value, + auth, + })); + } + } + } + } +} + +/// A cache mapping relocation. +pub struct DyldRelocation { + /// The offset of the relocation within the mapping. + /// + /// This can be added to either the mapping file offset or the + /// mapping address. + pub offset: u64, + /// The value to be relocated. + pub value: u64, + /// The pointer authentication data, if present. + pub auth: Option<DyldRelocationAuth>, +} + +impl Debug for DyldRelocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DyldRelocation") + .field("offset", &format_args!("{:#x}", self.offset)) + .field("value", &format_args!("{:#x}", self.value)) + .field("auth", &self.auth) + .finish() + } +} + +/// Pointer authentication data. +/// +/// This is used for signing pointers for the arm64e ABI. +pub struct DyldRelocationAuth { + /// The key used to generate the signed value. + pub key: macho::PtrauthKey, + /// The integer diversity value. + pub diversity: u16, + /// Whether the address should be blended with the diversity value. + pub addr_div: bool, +} + +impl Debug for DyldRelocationAuth { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Ptrauth") + .field("key", &self.key) + .field("diversity", &format_args!("{:#x}", self.diversity)) + .field("addr_div", &self.addr_div) + .finish() + } +} + +impl<E: Endian> macho::DyldCacheHeader<E> { + /// Read the dyld cache header. + pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<&'data Self> { + data.read_at::<macho::DyldCacheHeader<E>>(0) + .read_error("Invalid dyld cache header size or alignment") + } + + /// Returns (arch, endian) based on the magic string. + pub fn parse_magic(&self) -> Result<(Architecture, E)> { + let (arch, is_big_endian) = match &self.magic { + b"dyld_v1 i386\0" => (Architecture::I386, false), + b"dyld_v1 x86_64\0" => (Architecture::X86_64, false), + b"dyld_v1 x86_64h\0" => (Architecture::X86_64, false), + b"dyld_v1 ppc\0" => (Architecture::PowerPc, true), + b"dyld_v1 armv6\0" => (Architecture::Arm, false), + b"dyld_v1 armv7\0" => (Architecture::Arm, false), + b"dyld_v1 armv7f\0" => (Architecture::Arm, false), + b"dyld_v1 armv7s\0" => (Architecture::Arm, false), + b"dyld_v1 armv7k\0" => (Architecture::Arm, false), + b"dyld_v1 arm64\0" => (Architecture::Aarch64, false), + b"dyld_v1 arm64e\0" => (Architecture::Aarch64, false), + _ => return Err(Error("Unrecognized dyld cache magic")), + }; + let endian = + E::from_big_endian(is_big_endian).read_error("Unsupported dyld cache endian")?; + Ok((arch, endian)) + } + + /// Return the mapping information table. + pub fn mappings<'data, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result<DyldCacheMappingSlice<'data, E>> { + let header_size = self.mapping_offset.get(endian); + if header_size >= MIN_HEADER_SIZE_MAPPINGS_V2 { + let info = data + .read_slice_at::<macho::DyldCacheMappingAndSlideInfo<E>>( + self.mapping_with_slide_offset.get(endian).into(), + self.mapping_with_slide_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache mapping size or alignment")?; + Ok(DyldCacheMappingSlice::V2(info)) + } else { + let info = data + .read_slice_at::<macho::DyldCacheMappingInfo<E>>( + self.mapping_offset.get(endian).into(), + self.mapping_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache mapping size or alignment")?; + Ok(DyldCacheMappingSlice::V1(info)) + } + } + + /// Return the information about subcaches, if present. + /// + /// Returns `None` for dyld caches produced before dyld-940 (macOS 12). + pub fn subcaches<'data, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result<Option<DyldSubCacheSlice<'data, E>>> { + let header_size = self.mapping_offset.get(endian); + if header_size >= MIN_HEADER_SIZE_SUBCACHES_V2 { + let subcaches = data + .read_slice_at::<macho::DyldSubCacheEntryV2<E>>( + self.sub_cache_array_offset.get(endian).into(), + self.sub_cache_array_count.get(endian) as usize, + ) + .read_error("Invalid dyld subcaches size or alignment")?; + Ok(Some(DyldSubCacheSlice::V2(subcaches))) + } else if header_size >= MIN_HEADER_SIZE_SUBCACHES_V1 { + let subcaches = data + .read_slice_at::<macho::DyldSubCacheEntryV1<E>>( + self.sub_cache_array_offset.get(endian).into(), + self.sub_cache_array_count.get(endian) as usize, + ) + .read_error("Invalid dyld subcaches size or alignment")?; + Ok(Some(DyldSubCacheSlice::V1(subcaches))) + } else { + Ok(None) + } + } + + /// Return the UUID for the .symbols subcache, if present. + pub fn symbols_subcache_uuid(&self, endian: E) -> Option<[u8; 16]> { + if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES_V1 { + let uuid = self.symbol_file_uuid; + if uuid != [0; 16] { + return Some(uuid); + } + } + None + } + + /// Return the image information table. + pub fn images<'data, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result<&'data [macho::DyldCacheImageInfo<E>]> { + if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES_V1 { + data.read_slice_at::<macho::DyldCacheImageInfo<E>>( + self.images_offset.get(endian).into(), + self.images_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache image size or alignment") + } else { + data.read_slice_at::<macho::DyldCacheImageInfo<E>>( + self.images_offset_old.get(endian).into(), + self.images_count_old.get(endian) as usize, + ) + .read_error("Invalid dyld cache image size or alignment") + } + } +} + +impl<E: Endian> macho::DyldCacheImageInfo<E> { + /// The file system path of this image. + /// + /// `data` should be the main cache file, not the subcache containing the image. + pub fn path<'data, R: ReadRef<'data>>(&self, endian: E, data: R) -> Result<&'data [u8]> { + let r_start = self.path_file_offset.get(endian).into(); + let r_end = data.len().read_error("Couldn't get data len()")?; + data.read_bytes_at_until(r_start..r_end, 0) + .read_error("Couldn't read dyld cache image path") + } +} + +impl<E: Endian> macho::DyldCacheMappingAndSlideInfo<E> { + /// Return the (optional) array of slide information structs + pub fn slide<'data, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result<DyldCacheSlideInfo<'data, E>> { + // TODO: limit further reads to this size? + if self.slide_info_file_size.get(endian) == 0 { + return Ok(DyldCacheSlideInfo::None); + } + + let slide_info_file_offset = self.slide_info_file_offset.get(endian); + let version = data + .read_at::<U32<E>>(slide_info_file_offset) + .read_error("Invalid slide info file offset size or alignment")? + .get(endian); + match version { + 2 => { + let slide = data + .read_at::<macho::DyldCacheSlideInfo2<E>>(slide_info_file_offset) + .read_error("Invalid dyld cache slide info offset or alignment")?; + let page_starts_offset = slide_info_file_offset + .checked_add(slide.page_starts_offset.get(endian) as u64) + .read_error("Invalid dyld cache page starts offset")?; + let page_starts = data + .read_slice_at::<U16<E>>( + page_starts_offset, + slide.page_starts_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache page starts size or alignment")?; + let page_extras_offset = slide_info_file_offset + .checked_add(slide.page_extras_offset.get(endian) as u64) + .read_error("Invalid dyld cache page extras offset")?; + let page_extras = data + .read_slice_at::<U16<E>>( + page_extras_offset, + slide.page_extras_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache page extras size or alignment")?; + Ok(DyldCacheSlideInfo::V2 { + slide, + page_starts, + page_extras, + }) + } + 3 => { + let slide = data + .read_at::<macho::DyldCacheSlideInfo3<E>>(slide_info_file_offset) + .read_error("Invalid dyld cache slide info offset or alignment")?; + let page_starts_offset = slide_info_file_offset + .checked_add(mem::size_of::<macho::DyldCacheSlideInfo3<E>>() as u64) + .read_error("Invalid dyld cache page starts offset")?; + let page_starts = data + .read_slice_at::<U16<E>>( + page_starts_offset, + slide.page_starts_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache page starts size or alignment")?; + Ok(DyldCacheSlideInfo::V3 { slide, page_starts }) + } + 5 => { + let slide = data + .read_at::<macho::DyldCacheSlideInfo5<E>>(slide_info_file_offset) + .read_error("Invalid dyld cache slide info offset or alignment")?; + let page_starts_offset = slide_info_file_offset + .checked_add(mem::size_of::<macho::DyldCacheSlideInfo5<E>>() as u64) + .read_error("Invalid dyld cache page starts offset")?; + let page_starts = data + .read_slice_at::<U16<E>>( + page_starts_offset, + slide.page_starts_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache page starts size or alignment")?; + Ok(DyldCacheSlideInfo::V5 { slide, page_starts }) + } + _ => Err(Error("Unsupported dyld cache slide info version")), + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/fat.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/fat.rs new file mode 100644 index 0000000..9a049d9 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/fat.rs
@@ -0,0 +1,140 @@ +use crate::endian::BigEndian; +use crate::macho; +use crate::pod::Pod; +use crate::read::{Architecture, Error, ReadError, ReadRef, Result}; + +pub use macho::{FatArch32, FatArch64, FatHeader}; + +/// A 32-bit Mach-O universal binary. +/// +/// This is a file that starts with [`macho::FatHeader`], and corresponds +/// to [`crate::FileKind::MachOFat32`]. +pub type MachOFatFile32<'data> = MachOFatFile<'data, macho::FatArch32>; + +/// A 64-bit Mach-O universal binary. +/// +/// This is a file that starts with [`macho::FatHeader`], and corresponds +/// to [`crate::FileKind::MachOFat64`]. +pub type MachOFatFile64<'data> = MachOFatFile<'data, macho::FatArch64>; + +/// A Mach-O universal binary. +/// +/// This is a file that starts with [`macho::FatHeader`], and corresponds +/// to [`crate::FileKind::MachOFat32`] or [`crate::FileKind::MachOFat64`]. +#[derive(Debug, Clone)] +pub struct MachOFatFile<'data, Fat: FatArch> { + header: &'data macho::FatHeader, + arches: &'data [Fat], +} + +impl<'data, Fat: FatArch> MachOFatFile<'data, Fat> { + /// Attempt to parse the fat header and fat arches. + pub fn parse<R: ReadRef<'data>>(data: R) -> Result<Self> { + let mut offset = 0; + let header = data + .read::<FatHeader>(&mut offset) + .read_error("Invalid fat header size or alignment")?; + if header.magic.get(BigEndian) != Fat::MAGIC { + return Err(Error("Invalid fat magic")); + } + let arches = data + .read_slice::<Fat>(&mut offset, header.nfat_arch.get(BigEndian) as usize) + .read_error("Invalid nfat_arch")?; + Ok(MachOFatFile { header, arches }) + } + + /// Return the fat header + pub fn header(&self) -> &'data macho::FatHeader { + self.header + } + + /// Return the array of fat arches. + pub fn arches(&self) -> &'data [Fat] { + self.arches + } +} + +/// A trait for generic access to [`macho::FatArch32`] and [`macho::FatArch64`]. +#[allow(missing_docs)] +pub trait FatArch: Pod { + type Word: Into<u64>; + const MAGIC: u32; + + fn cputype(&self) -> u32; + fn cpusubtype(&self) -> u32; + fn offset(&self) -> Self::Word; + fn size(&self) -> Self::Word; + fn align(&self) -> u32; + + fn architecture(&self) -> Architecture { + match self.cputype() { + macho::CPU_TYPE_ARM => Architecture::Arm, + macho::CPU_TYPE_ARM64 => Architecture::Aarch64, + macho::CPU_TYPE_X86 => Architecture::I386, + macho::CPU_TYPE_X86_64 => Architecture::X86_64, + macho::CPU_TYPE_MIPS => Architecture::Mips, + macho::CPU_TYPE_POWERPC => Architecture::PowerPc, + macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64, + _ => Architecture::Unknown, + } + } + + fn file_range(&self) -> (u64, u64) { + (self.offset().into(), self.size().into()) + } + + fn data<'data, R: ReadRef<'data>>(&self, file: R) -> Result<&'data [u8]> { + file.read_bytes_at(self.offset().into(), self.size().into()) + .read_error("Invalid fat arch offset or size") + } +} + +impl FatArch for FatArch32 { + type Word = u32; + const MAGIC: u32 = macho::FAT_MAGIC; + + fn cputype(&self) -> u32 { + self.cputype.get(BigEndian) + } + + fn cpusubtype(&self) -> u32 { + self.cpusubtype.get(BigEndian) + } + + fn offset(&self) -> Self::Word { + self.offset.get(BigEndian) + } + + fn size(&self) -> Self::Word { + self.size.get(BigEndian) + } + + fn align(&self) -> u32 { + self.align.get(BigEndian) + } +} + +impl FatArch for FatArch64 { + type Word = u64; + const MAGIC: u32 = macho::FAT_MAGIC_64; + + fn cputype(&self) -> u32 { + self.cputype.get(BigEndian) + } + + fn cpusubtype(&self) -> u32 { + self.cpusubtype.get(BigEndian) + } + + fn offset(&self) -> Self::Word { + self.offset.get(BigEndian) + } + + fn size(&self) -> Self::Word { + self.size.get(BigEndian) + } + + fn align(&self) -> u32 { + self.align.get(BigEndian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/file.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/file.rs new file mode 100644 index 0000000..074acaa --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/file.rs
@@ -0,0 +1,819 @@ +use alloc::vec::Vec; +use core::fmt::Debug; +use core::{mem, str}; + +use crate::endian::{self, BigEndian, Endian, Endianness}; +use crate::macho; +use crate::pod::Pod; +use crate::read::{ + self, Architecture, ByteString, ComdatKind, Error, Export, FileFlags, Import, + NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectMap, ObjectSection, + ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex, +}; + +use super::{ + DyldCacheImage, LoadCommandIterator, MachOSection, MachOSectionInternal, MachOSectionIterator, + MachOSegment, MachOSegmentInternal, MachOSegmentIterator, MachOSymbol, MachOSymbolIterator, + MachOSymbolTable, Nlist, Section, Segment, SymbolTable, +}; + +/// A 32-bit Mach-O object file. +/// +/// This is a file that starts with [`macho::MachHeader32`], and corresponds +/// to [`crate::FileKind::MachO32`]. +pub type MachOFile32<'data, Endian = Endianness, R = &'data [u8]> = + MachOFile<'data, macho::MachHeader32<Endian>, R>; +/// A 64-bit Mach-O object file. +/// +/// This is a file that starts with [`macho::MachHeader64`], and corresponds +/// to [`crate::FileKind::MachO64`]. +pub type MachOFile64<'data, Endian = Endianness, R = &'data [u8]> = + MachOFile<'data, macho::MachHeader64<Endian>, R>; + +/// A partially parsed Mach-O file. +/// +/// Most of the functionality of this type is provided by the [`Object`] trait implementation. +#[derive(Debug)] +pub struct MachOFile<'data, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Mach::Endian, + pub(super) data: R, + pub(super) header_offset: u64, + pub(super) header: &'data Mach, + pub(super) segments: Vec<MachOSegmentInternal<'data, Mach, R>>, + pub(super) sections: Vec<MachOSectionInternal<'data, Mach, R>>, + pub(super) symbols: SymbolTable<'data, Mach, R>, +} + +impl<'data, Mach, R> MachOFile<'data, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + /// Parse the raw Mach-O file data. + pub fn parse(data: R) -> Result<Self> { + let header = Mach::parse(data, 0)?; + let endian = header.endian()?; + + // Build a list of segments and sections to make some operations more efficient. + let mut segments = Vec::new(); + let mut sections = Vec::new(); + let mut symbols = SymbolTable::default(); + if let Ok(mut commands) = header.load_commands(endian, data, 0) { + while let Ok(Some(command)) = commands.next() { + if let Some((segment, section_data)) = Mach::Segment::from_command(command)? { + segments.push(MachOSegmentInternal { segment, data }); + for section in segment.sections(endian, section_data)? { + let index = SectionIndex(sections.len() + 1); + sections.push(MachOSectionInternal::parse(index, section, data)); + } + } else if let Some(symtab) = command.symtab()? { + symbols = symtab.symbols(endian, data)?; + } + } + } + + Ok(MachOFile { + endian, + data, + header_offset: 0, + header, + segments, + sections, + symbols, + }) + } + + /// Parse the Mach-O file for the given image from the dyld shared cache. + /// This will read different sections from different subcaches, if necessary. + pub fn parse_dyld_cache_image<'cache, E: Endian>( + image: &DyldCacheImage<'data, 'cache, E, R>, + ) -> Result<Self> { + let (data, header_offset) = image.image_data_and_offset()?; + let header = Mach::parse(data, header_offset)?; + let endian = header.endian()?; + + // Build a list of sections to make some operations more efficient. + // Also build a list of segments, because we need to remember which ReadRef + // to read each section's data from. Only the DyldCache knows this information, + // and we won't have access to it once we've exited this function. + let mut segments = Vec::new(); + let mut sections = Vec::new(); + let mut linkedit_data: Option<R> = None; + let mut symtab = None; + if let Ok(mut commands) = header.load_commands(endian, data, header_offset) { + while let Ok(Some(command)) = commands.next() { + if let Some((segment, section_data)) = Mach::Segment::from_command(command)? { + // Each segment can be stored in a different subcache. Get the segment's + // address and look it up in the cache mappings, to find the correct cache data. + // This was observed for the arm64e __LINKEDIT segment in macOS 12.0.1. + let addr = segment.vmaddr(endian).into(); + let (data, _offset) = image + .cache + .data_and_offset_for_address(addr) + .read_error("Could not find segment data in dyld shared cache")?; + if segment.name() == macho::SEG_LINKEDIT.as_bytes() { + linkedit_data = Some(data); + } + segments.push(MachOSegmentInternal { segment, data }); + + for section in segment.sections(endian, section_data)? { + let index = SectionIndex(sections.len() + 1); + sections.push(MachOSectionInternal::parse(index, section, data)); + } + } else if let Some(st) = command.symtab()? { + symtab = Some(st); + } + } + } + + // The symbols are found in the __LINKEDIT segment, so make sure to read them from the + // correct subcache. + let symbols = match (symtab, linkedit_data) { + (Some(symtab), Some(linkedit_data)) => symtab.symbols(endian, linkedit_data)?, + _ => SymbolTable::default(), + }; + + Ok(MachOFile { + endian, + data, + header_offset, + header, + segments, + sections, + symbols, + }) + } + + /// Return the section at the given index. + #[inline] + pub(super) fn section_internal( + &self, + index: SectionIndex, + ) -> Result<&MachOSectionInternal<'data, Mach, R>> { + index + .0 + .checked_sub(1) + .and_then(|index| self.sections.get(index)) + .read_error("Invalid Mach-O section index") + } + + /// Returns the endianness. + pub fn endian(&self) -> Mach::Endian { + self.endian + } + + /// Returns the raw data. + pub fn data(&self) -> R { + self.data + } + + /// Returns the raw Mach-O file header. + #[deprecated(note = "Use `macho_header` instead")] + pub fn raw_header(&self) -> &'data Mach { + self.header + } + + /// Get the raw Mach-O file header. + pub fn macho_header(&self) -> &'data Mach { + self.header + } + + /// Get the Mach-O load commands. + pub fn macho_load_commands(&self) -> Result<LoadCommandIterator<'data, Mach::Endian>> { + self.header + .load_commands(self.endian, self.data, self.header_offset) + } + + /// Get the Mach-O symbol table. + /// + /// Returns an empty symbol table if the file has no symbol table. + pub fn macho_symbol_table(&self) -> &SymbolTable<'data, Mach, R> { + &self.symbols + } + + /// Return the `LC_BUILD_VERSION` load command if present. + pub fn build_version(&self) -> Result<Option<&'data macho::BuildVersionCommand<Mach::Endian>>> { + let mut commands = self + .header + .load_commands(self.endian, self.data, self.header_offset)?; + while let Some(command) = commands.next()? { + if let Some(build_version) = command.build_version()? { + return Ok(Some(build_version)); + } + } + Ok(None) + } +} + +impl<'data, Mach, R> read::private::Sealed for MachOFile<'data, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, Mach, R> Object<'data> for MachOFile<'data, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Segment<'file> + = MachOSegment<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type SegmentIterator<'file> + = MachOSegmentIterator<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type Section<'file> + = MachOSection<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type SectionIterator<'file> + = MachOSectionIterator<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type Comdat<'file> + = MachOComdat<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type ComdatIterator<'file> + = MachOComdatIterator<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type Symbol<'file> + = MachOSymbol<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type SymbolIterator<'file> + = MachOSymbolIterator<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type SymbolTable<'file> + = MachOSymbolTable<'data, 'file, Mach, R> + where + Self: 'file, + 'data: 'file; + type DynamicRelocationIterator<'file> + = NoDynamicRelocationIterator + where + Self: 'file, + 'data: 'file; + + fn architecture(&self) -> Architecture { + match self.header.cputype(self.endian) { + macho::CPU_TYPE_ARM => Architecture::Arm, + macho::CPU_TYPE_ARM64 => Architecture::Aarch64, + macho::CPU_TYPE_ARM64_32 => Architecture::Aarch64_Ilp32, + macho::CPU_TYPE_X86 => Architecture::I386, + macho::CPU_TYPE_X86_64 => Architecture::X86_64, + macho::CPU_TYPE_MIPS => Architecture::Mips, + macho::CPU_TYPE_POWERPC => Architecture::PowerPc, + macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64, + _ => Architecture::Unknown, + } + } + + fn sub_architecture(&self) -> Option<SubArchitecture> { + match ( + self.header.cputype(self.endian), + self.header.cpusubtype(self.endian), + ) { + (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E) => Some(SubArchitecture::Arm64E), + _ => None, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + self.header.is_little_endian() + } + + #[inline] + fn is_64(&self) -> bool { + self.header.is_type_64() + } + + fn kind(&self) -> ObjectKind { + match self.header.filetype(self.endian) { + macho::MH_OBJECT => ObjectKind::Relocatable, + macho::MH_EXECUTE => ObjectKind::Executable, + macho::MH_CORE => ObjectKind::Core, + macho::MH_DYLIB => ObjectKind::Dynamic, + _ => ObjectKind::Unknown, + } + } + + fn segments(&self) -> MachOSegmentIterator<'data, '_, Mach, R> { + MachOSegmentIterator { + file: self, + iter: self.segments.iter(), + } + } + + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<MachOSection<'data, 'file, Mach, R>> { + // Translate the section_name by stripping the query_prefix to construct + // a function that matches names starting with name_prefix, taking into + // consideration the maximum section name length. + let make_prefix_matcher = |query_prefix: &'static [u8], name_prefix: &'static [u8]| { + const MAX_SECTION_NAME_LEN: usize = 16; + let suffix = section_name.strip_prefix(query_prefix).map(|suffix| { + let max_len = MAX_SECTION_NAME_LEN - name_prefix.len(); + &suffix[..suffix.len().min(max_len)] + }); + move |name: &[u8]| suffix.is_some() && name.strip_prefix(name_prefix) == suffix + }; + // Matches "__text" when searching for ".text" and "__debug_str_offs" + // when searching for ".debug_str_offsets", as is common in + // macOS/Mach-O. + let matches_underscores_prefix = make_prefix_matcher(b".", b"__"); + // Matches "__zdebug_info" when searching for ".debug_info" and + // "__zdebug_str_off" when searching for ".debug_str_offsets", as is + // used by Go when using GNU-style compression. + let matches_zdebug_prefix = make_prefix_matcher(b".debug_", b"__zdebug_"); + self.sections().find(|section| { + section.name_bytes().map_or(false, |name| { + name == section_name + || matches_underscores_prefix(name) + || matches_zdebug_prefix(name) + }) + }) + } + + fn section_by_index(&self, index: SectionIndex) -> Result<MachOSection<'data, '_, Mach, R>> { + let internal = *self.section_internal(index)?; + Ok(MachOSection { + file: self, + internal, + }) + } + + fn sections(&self) -> MachOSectionIterator<'data, '_, Mach, R> { + MachOSectionIterator { + file: self, + iter: self.sections.iter(), + } + } + + fn comdats(&self) -> MachOComdatIterator<'data, '_, Mach, R> { + MachOComdatIterator { file: self } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<MachOSymbol<'data, '_, Mach, R>> { + let nlist = self.symbols.symbol(index)?; + MachOSymbol::new(self, index, nlist).read_error("Unsupported Mach-O symbol index") + } + + fn symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> { + MachOSymbolIterator::new(self) + } + + #[inline] + fn symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> { + Some(MachOSymbolTable { file: self }) + } + + fn dynamic_symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> { + MachOSymbolIterator::empty(self) + } + + #[inline] + fn dynamic_symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> { + None + } + + fn object_map(&self) -> ObjectMap<'data> { + self.symbols.object_map(self.endian) + } + + fn imports(&self) -> Result<Vec<Import<'data>>> { + let mut dysymtab = None; + let mut libraries = Vec::new(); + let twolevel = self.header.flags(self.endian) & macho::MH_TWOLEVEL != 0; + if twolevel { + libraries.push(&[][..]); + } + let mut commands = self + .header + .load_commands(self.endian, self.data, self.header_offset)?; + while let Some(command) = commands.next()? { + if let Some(command) = command.dysymtab()? { + dysymtab = Some(command); + } + if twolevel { + if let Some(dylib) = command.dylib()? { + libraries.push(command.string(self.endian, dylib.dylib.name)?); + } + } + } + + let mut imports = Vec::new(); + if let Some(dysymtab) = dysymtab { + let index = dysymtab.iundefsym.get(self.endian) as usize; + let number = dysymtab.nundefsym.get(self.endian) as usize; + for i in index..(index.wrapping_add(number)) { + let symbol = self.symbols.symbol(SymbolIndex(i))?; + let name = symbol.name(self.endian, self.symbols.strings())?; + let library = if twolevel { + libraries + .get(symbol.library_ordinal(self.endian) as usize) + .copied() + .read_error("Invalid Mach-O symbol library ordinal")? + } else { + &[] + }; + imports.push(Import { + name: ByteString(name), + library: ByteString(library), + }); + } + } + Ok(imports) + } + + fn exports(&self) -> Result<Vec<Export<'data>>> { + let mut dysymtab = None; + let mut commands = self + .header + .load_commands(self.endian, self.data, self.header_offset)?; + while let Some(command) = commands.next()? { + if let Some(command) = command.dysymtab()? { + dysymtab = Some(command); + break; + } + } + + let mut exports = Vec::new(); + if let Some(dysymtab) = dysymtab { + let index = dysymtab.iextdefsym.get(self.endian) as usize; + let number = dysymtab.nextdefsym.get(self.endian) as usize; + for i in index..(index.wrapping_add(number)) { + let symbol = self.symbols.symbol(SymbolIndex(i))?; + let name = symbol.name(self.endian, self.symbols.strings())?; + let address = symbol.n_value(self.endian).into(); + exports.push(Export { + name: ByteString(name), + address, + }); + } + } + Ok(exports) + } + + #[inline] + fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> { + None + } + + fn has_debug_symbols(&self) -> bool { + self.section_by_name(".debug_info").is_some() + } + + fn mach_uuid(&self) -> Result<Option<[u8; 16]>> { + self.header.uuid(self.endian, self.data, self.header_offset) + } + + fn relative_address_base(&self) -> u64 { + 0 + } + + fn entry(&self) -> u64 { + if let Ok(mut commands) = + self.header + .load_commands(self.endian, self.data, self.header_offset) + { + while let Ok(Some(command)) = commands.next() { + if let Ok(Some(command)) = command.entry_point() { + return command.entryoff.get(self.endian); + } + } + } + 0 + } + + fn flags(&self) -> FileFlags { + FileFlags::MachO { + flags: self.header.flags(self.endian), + } + } +} + +/// An iterator for the COMDAT section groups in a [`MachOFile64`]. +pub type MachOComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator for the COMDAT section groups in a [`MachOFile64`]. +pub type MachOComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator for the COMDAT section groups in a [`MachOFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct MachOComdatIterator<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file MachOFile<'data, Mach, R>, +} + +impl<'data, 'file, Mach, R> Iterator for MachOComdatIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Item = MachOComdat<'data, 'file, Mach, R>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A COMDAT section group in a [`MachOFile32`]. +pub type MachOComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdat<'data, 'file, macho::MachHeader32<Endian>, R>; + +/// A COMDAT section group in a [`MachOFile64`]. +pub type MachOComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdat<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A COMDAT section group in a [`MachOFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct MachOComdat<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file MachOFile<'data, Mach, R>, +} + +impl<'data, 'file, Mach, R> read::private::Sealed for MachOComdat<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Mach, R> ObjectComdat<'data> for MachOComdat<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type SectionIterator = MachOComdatSectionIterator<'data, 'file, Mach, R>; + + #[inline] + fn kind(&self) -> ComdatKind { + unreachable!(); + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + unreachable!(); + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&'data str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator for the sections in a COMDAT section group in a [`MachOFile32`]. +pub type MachOComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator for the sections in a COMDAT section group in a [`MachOFile64`]. +pub type MachOComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator for the sections in a COMDAT section group in a [`MachOFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct MachOComdatSectionIterator<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file MachOFile<'data, Mach, R>, +} + +impl<'data, 'file, Mach, R> Iterator for MachOComdatSectionIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A trait for generic access to [`macho::MachHeader32`] and [`macho::MachHeader64`]. +#[allow(missing_docs)] +pub trait MachHeader: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + type Segment: Segment<Endian = Self::Endian, Section = Self::Section>; + type Section: Section<Endian = Self::Endian>; + type Nlist: Nlist<Endian = Self::Endian>; + + /// Return true if this type is a 64-bit header. + /// + /// This is a property of the type, not a value in the header data. + fn is_type_64(&self) -> bool; + + /// Return true if the `magic` field signifies big-endian. + fn is_big_endian(&self) -> bool; + + /// Return true if the `magic` field signifies little-endian. + fn is_little_endian(&self) -> bool; + + fn magic(&self) -> u32; + fn cputype(&self, endian: Self::Endian) -> u32; + fn cpusubtype(&self, endian: Self::Endian) -> u32; + fn filetype(&self, endian: Self::Endian) -> u32; + fn ncmds(&self, endian: Self::Endian) -> u32; + fn sizeofcmds(&self, endian: Self::Endian) -> u32; + fn flags(&self, endian: Self::Endian) -> u32; + + // Provided methods. + + /// Read the file header. + /// + /// Also checks that the magic field in the file header is a supported format. + fn parse<'data, R: ReadRef<'data>>(data: R, offset: u64) -> read::Result<&'data Self> { + let header = data + .read_at::<Self>(offset) + .read_error("Invalid Mach-O header size or alignment")?; + if !header.is_supported() { + return Err(Error("Unsupported Mach-O header")); + } + Ok(header) + } + + fn is_supported(&self) -> bool { + self.is_little_endian() || self.is_big_endian() + } + + fn endian(&self) -> Result<Self::Endian> { + Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported Mach-O endian") + } + + fn load_commands<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + header_offset: u64, + ) -> Result<LoadCommandIterator<'data, Self::Endian>> { + let data = data + .read_bytes_at( + header_offset + mem::size_of::<Self>() as u64, + self.sizeofcmds(endian).into(), + ) + .read_error("Invalid Mach-O load command table size")?; + Ok(LoadCommandIterator::new(endian, data, self.ncmds(endian))) + } + + /// Return the UUID from the `LC_UUID` load command, if one is present. + fn uuid<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + header_offset: u64, + ) -> Result<Option<[u8; 16]>> { + let mut commands = self.load_commands(endian, data, header_offset)?; + while let Some(command) = commands.next()? { + if let Ok(Some(uuid)) = command.uuid() { + return Ok(Some(uuid.uuid)); + } + } + Ok(None) + } +} + +impl<Endian: endian::Endian> MachHeader for macho::MachHeader32<Endian> { + type Word = u32; + type Endian = Endian; + type Segment = macho::SegmentCommand32<Endian>; + type Section = macho::Section32<Endian>; + type Nlist = macho::Nlist32<Endian>; + + fn is_type_64(&self) -> bool { + false + } + + fn is_big_endian(&self) -> bool { + self.magic() == macho::MH_MAGIC + } + + fn is_little_endian(&self) -> bool { + self.magic() == macho::MH_CIGAM + } + + fn magic(&self) -> u32 { + self.magic.get(BigEndian) + } + + fn cputype(&self, endian: Self::Endian) -> u32 { + self.cputype.get(endian) + } + + fn cpusubtype(&self, endian: Self::Endian) -> u32 { + self.cpusubtype.get(endian) + } + + fn filetype(&self, endian: Self::Endian) -> u32 { + self.filetype.get(endian) + } + + fn ncmds(&self, endian: Self::Endian) -> u32 { + self.ncmds.get(endian) + } + + fn sizeofcmds(&self, endian: Self::Endian) -> u32 { + self.sizeofcmds.get(endian) + } + + fn flags(&self, endian: Self::Endian) -> u32 { + self.flags.get(endian) + } +} + +impl<Endian: endian::Endian> MachHeader for macho::MachHeader64<Endian> { + type Word = u64; + type Endian = Endian; + type Segment = macho::SegmentCommand64<Endian>; + type Section = macho::Section64<Endian>; + type Nlist = macho::Nlist64<Endian>; + + fn is_type_64(&self) -> bool { + true + } + + fn is_big_endian(&self) -> bool { + self.magic() == macho::MH_MAGIC_64 + } + + fn is_little_endian(&self) -> bool { + self.magic() == macho::MH_CIGAM_64 + } + + fn magic(&self) -> u32 { + self.magic.get(BigEndian) + } + + fn cputype(&self, endian: Self::Endian) -> u32 { + self.cputype.get(endian) + } + + fn cpusubtype(&self, endian: Self::Endian) -> u32 { + self.cpusubtype.get(endian) + } + + fn filetype(&self, endian: Self::Endian) -> u32 { + self.filetype.get(endian) + } + + fn ncmds(&self, endian: Self::Endian) -> u32 { + self.ncmds.get(endian) + } + + fn sizeofcmds(&self, endian: Self::Endian) -> u32 { + self.sizeofcmds.get(endian) + } + + fn flags(&self, endian: Self::Endian) -> u32 { + self.flags.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/load_command.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/load_command.rs new file mode 100644 index 0000000..be690db --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/load_command.rs
@@ -0,0 +1,402 @@ +use core::marker::PhantomData; +use core::mem; + +use crate::endian::Endian; +use crate::macho; +use crate::pod::Pod; +use crate::read::macho::{MachHeader, SymbolTable}; +use crate::read::{Bytes, Error, ReadError, ReadRef, Result, StringTable}; + +/// An iterator for the load commands from a [`MachHeader`]. +#[derive(Debug, Default, Clone, Copy)] +pub struct LoadCommandIterator<'data, E: Endian> { + endian: E, + data: Bytes<'data>, + ncmds: u32, +} + +impl<'data, E: Endian> LoadCommandIterator<'data, E> { + pub(super) fn new(endian: E, data: &'data [u8], ncmds: u32) -> Self { + LoadCommandIterator { + endian, + data: Bytes(data), + ncmds, + } + } + + /// Return the next load command. + pub fn next(&mut self) -> Result<Option<LoadCommandData<'data, E>>> { + if self.ncmds == 0 { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.ncmds = 0; + } else { + self.ncmds -= 1; + } + result + } + + fn parse(&mut self) -> Result<LoadCommandData<'data, E>> { + let header = self + .data + .read_at::<macho::LoadCommand<E>>(0) + .read_error("Invalid Mach-O load command header")?; + let cmd = header.cmd.get(self.endian); + let cmdsize = header.cmdsize.get(self.endian) as usize; + if cmdsize < mem::size_of::<macho::LoadCommand<E>>() { + return Err(Error("Invalid Mach-O load command size")); + } + let data = self + .data + .read_bytes(cmdsize) + .read_error("Invalid Mach-O load command size")?; + Ok(LoadCommandData { + cmd, + data, + marker: Default::default(), + }) + } +} + +impl<'data, E: Endian> Iterator for LoadCommandIterator<'data, E> { + type Item = Result<LoadCommandData<'data, E>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// The data for a [`macho::LoadCommand`]. +#[derive(Debug, Clone, Copy)] +pub struct LoadCommandData<'data, E: Endian> { + cmd: u32, + // Includes the header. + data: Bytes<'data>, + marker: PhantomData<E>, +} + +impl<'data, E: Endian> LoadCommandData<'data, E> { + /// Return the `cmd` field of the [`macho::LoadCommand`]. + /// + /// This is one of the `LC_` constants. + pub fn cmd(&self) -> u32 { + self.cmd + } + + /// Return the `cmdsize` field of the [`macho::LoadCommand`]. + pub fn cmdsize(&self) -> u32 { + self.data.len() as u32 + } + + /// Parse the data as the given type. + #[inline] + pub fn data<T: Pod>(&self) -> Result<&'data T> { + self.data + .read_at(0) + .read_error("Invalid Mach-O command size") + } + + /// Raw bytes of this [`macho::LoadCommand`] structure. + pub fn raw_data(&self) -> &'data [u8] { + self.data.0 + } + + /// Parse a load command string value. + /// + /// Strings used by load commands are specified by offsets that are + /// relative to the load command header. + pub fn string(&self, endian: E, s: macho::LcStr<E>) -> Result<&'data [u8]> { + self.data + .read_string_at(s.offset.get(endian) as usize) + .read_error("Invalid load command string offset") + } + + /// Parse the command data according to the `cmd` field. + pub fn variant(&self) -> Result<LoadCommandVariant<'data, E>> { + Ok(match self.cmd { + macho::LC_SEGMENT => { + let mut data = self.data; + let segment = data.read().read_error("Invalid Mach-O command size")?; + LoadCommandVariant::Segment32(segment, data.0) + } + macho::LC_SYMTAB => LoadCommandVariant::Symtab(self.data()?), + macho::LC_THREAD | macho::LC_UNIXTHREAD => { + let mut data = self.data; + let thread = data.read().read_error("Invalid Mach-O command size")?; + LoadCommandVariant::Thread(thread, data.0) + } + macho::LC_DYSYMTAB => LoadCommandVariant::Dysymtab(self.data()?), + macho::LC_LOAD_DYLIB + | macho::LC_LOAD_WEAK_DYLIB + | macho::LC_REEXPORT_DYLIB + | macho::LC_LAZY_LOAD_DYLIB + | macho::LC_LOAD_UPWARD_DYLIB => LoadCommandVariant::Dylib(self.data()?), + macho::LC_ID_DYLIB => LoadCommandVariant::IdDylib(self.data()?), + macho::LC_LOAD_DYLINKER => LoadCommandVariant::LoadDylinker(self.data()?), + macho::LC_ID_DYLINKER => LoadCommandVariant::IdDylinker(self.data()?), + macho::LC_PREBOUND_DYLIB => LoadCommandVariant::PreboundDylib(self.data()?), + macho::LC_ROUTINES => LoadCommandVariant::Routines32(self.data()?), + macho::LC_SUB_FRAMEWORK => LoadCommandVariant::SubFramework(self.data()?), + macho::LC_SUB_UMBRELLA => LoadCommandVariant::SubUmbrella(self.data()?), + macho::LC_SUB_CLIENT => LoadCommandVariant::SubClient(self.data()?), + macho::LC_SUB_LIBRARY => LoadCommandVariant::SubLibrary(self.data()?), + macho::LC_TWOLEVEL_HINTS => LoadCommandVariant::TwolevelHints(self.data()?), + macho::LC_PREBIND_CKSUM => LoadCommandVariant::PrebindCksum(self.data()?), + macho::LC_SEGMENT_64 => { + let mut data = self.data; + let segment = data.read().read_error("Invalid Mach-O command size")?; + LoadCommandVariant::Segment64(segment, data.0) + } + macho::LC_ROUTINES_64 => LoadCommandVariant::Routines64(self.data()?), + macho::LC_UUID => LoadCommandVariant::Uuid(self.data()?), + macho::LC_RPATH => LoadCommandVariant::Rpath(self.data()?), + macho::LC_CODE_SIGNATURE + | macho::LC_SEGMENT_SPLIT_INFO + | macho::LC_FUNCTION_STARTS + | macho::LC_DATA_IN_CODE + | macho::LC_DYLIB_CODE_SIGN_DRS + | macho::LC_LINKER_OPTIMIZATION_HINT + | macho::LC_DYLD_EXPORTS_TRIE + | macho::LC_DYLD_CHAINED_FIXUPS => LoadCommandVariant::LinkeditData(self.data()?), + macho::LC_ENCRYPTION_INFO => LoadCommandVariant::EncryptionInfo32(self.data()?), + macho::LC_DYLD_INFO | macho::LC_DYLD_INFO_ONLY => { + LoadCommandVariant::DyldInfo(self.data()?) + } + macho::LC_VERSION_MIN_MACOSX + | macho::LC_VERSION_MIN_IPHONEOS + | macho::LC_VERSION_MIN_TVOS + | macho::LC_VERSION_MIN_WATCHOS => LoadCommandVariant::VersionMin(self.data()?), + macho::LC_DYLD_ENVIRONMENT => LoadCommandVariant::DyldEnvironment(self.data()?), + macho::LC_MAIN => LoadCommandVariant::EntryPoint(self.data()?), + macho::LC_SOURCE_VERSION => LoadCommandVariant::SourceVersion(self.data()?), + macho::LC_ENCRYPTION_INFO_64 => LoadCommandVariant::EncryptionInfo64(self.data()?), + macho::LC_LINKER_OPTION => LoadCommandVariant::LinkerOption(self.data()?), + macho::LC_NOTE => LoadCommandVariant::Note(self.data()?), + macho::LC_BUILD_VERSION => LoadCommandVariant::BuildVersion(self.data()?), + macho::LC_FILESET_ENTRY => LoadCommandVariant::FilesetEntry(self.data()?), + _ => LoadCommandVariant::Other, + }) + } + + /// Try to parse this command as a [`macho::SegmentCommand32`]. + /// + /// Returns the segment command and the data containing the sections. + pub fn segment_32(self) -> Result<Option<(&'data macho::SegmentCommand32<E>, &'data [u8])>> { + if self.cmd == macho::LC_SEGMENT { + let mut data = self.data; + let segment = data.read().read_error("Invalid Mach-O command size")?; + Ok(Some((segment, data.0))) + } else { + Ok(None) + } + } + + /// Try to parse this command as a [`macho::SymtabCommand`]. + pub fn symtab(self) -> Result<Option<&'data macho::SymtabCommand<E>>> { + if self.cmd == macho::LC_SYMTAB { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a [`macho::DysymtabCommand`]. + pub fn dysymtab(self) -> Result<Option<&'data macho::DysymtabCommand<E>>> { + if self.cmd == macho::LC_DYSYMTAB { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a [`macho::DylibCommand`]. + pub fn dylib(self) -> Result<Option<&'data macho::DylibCommand<E>>> { + if self.cmd == macho::LC_LOAD_DYLIB + || self.cmd == macho::LC_LOAD_WEAK_DYLIB + || self.cmd == macho::LC_REEXPORT_DYLIB + || self.cmd == macho::LC_LAZY_LOAD_DYLIB + || self.cmd == macho::LC_LOAD_UPWARD_DYLIB + { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a [`macho::UuidCommand`]. + pub fn uuid(self) -> Result<Option<&'data macho::UuidCommand<E>>> { + if self.cmd == macho::LC_UUID { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a [`macho::SegmentCommand64`]. + pub fn segment_64(self) -> Result<Option<(&'data macho::SegmentCommand64<E>, &'data [u8])>> { + if self.cmd == macho::LC_SEGMENT_64 { + let mut data = self.data; + let command = data.read().read_error("Invalid Mach-O command size")?; + Ok(Some((command, data.0))) + } else { + Ok(None) + } + } + + /// Try to parse this command as a [`macho::DyldInfoCommand`]. + pub fn dyld_info(self) -> Result<Option<&'data macho::DyldInfoCommand<E>>> { + if self.cmd == macho::LC_DYLD_INFO || self.cmd == macho::LC_DYLD_INFO_ONLY { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as an [`macho::EntryPointCommand`]. + pub fn entry_point(self) -> Result<Option<&'data macho::EntryPointCommand<E>>> { + if self.cmd == macho::LC_MAIN { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a [`macho::BuildVersionCommand`]. + pub fn build_version(self) -> Result<Option<&'data macho::BuildVersionCommand<E>>> { + if self.cmd == macho::LC_BUILD_VERSION { + Some(self.data()).transpose() + } else { + Ok(None) + } + } +} + +/// A [`macho::LoadCommand`] that has been interpreted according to its `cmd` field. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum LoadCommandVariant<'data, E: Endian> { + /// `LC_SEGMENT` + Segment32(&'data macho::SegmentCommand32<E>, &'data [u8]), + /// `LC_SYMTAB` + Symtab(&'data macho::SymtabCommand<E>), + // obsolete: `LC_SYMSEG` + //Symseg(&'data macho::SymsegCommand<E>), + /// `LC_THREAD` or `LC_UNIXTHREAD` + Thread(&'data macho::ThreadCommand<E>, &'data [u8]), + // obsolete: `LC_IDFVMLIB` or `LC_LOADFVMLIB` + //Fvmlib(&'data macho::FvmlibCommand<E>), + // obsolete: `LC_IDENT` + //Ident(&'data macho::IdentCommand<E>), + // internal: `LC_FVMFILE` + //Fvmfile(&'data macho::FvmfileCommand<E>), + // internal: `LC_PREPAGE` + /// `LC_DYSYMTAB` + Dysymtab(&'data macho::DysymtabCommand<E>), + /// `LC_LOAD_DYLIB`, `LC_LOAD_WEAK_DYLIB`, `LC_REEXPORT_DYLIB`, + /// `LC_LAZY_LOAD_DYLIB`, or `LC_LOAD_UPWARD_DYLIB` + Dylib(&'data macho::DylibCommand<E>), + /// `LC_ID_DYLIB` + IdDylib(&'data macho::DylibCommand<E>), + /// `LC_LOAD_DYLINKER` + LoadDylinker(&'data macho::DylinkerCommand<E>), + /// `LC_ID_DYLINKER` + IdDylinker(&'data macho::DylinkerCommand<E>), + /// `LC_PREBOUND_DYLIB` + PreboundDylib(&'data macho::PreboundDylibCommand<E>), + /// `LC_ROUTINES` + Routines32(&'data macho::RoutinesCommand32<E>), + /// `LC_SUB_FRAMEWORK` + SubFramework(&'data macho::SubFrameworkCommand<E>), + /// `LC_SUB_UMBRELLA` + SubUmbrella(&'data macho::SubUmbrellaCommand<E>), + /// `LC_SUB_CLIENT` + SubClient(&'data macho::SubClientCommand<E>), + /// `LC_SUB_LIBRARY` + SubLibrary(&'data macho::SubLibraryCommand<E>), + /// `LC_TWOLEVEL_HINTS` + TwolevelHints(&'data macho::TwolevelHintsCommand<E>), + /// `LC_PREBIND_CKSUM` + PrebindCksum(&'data macho::PrebindCksumCommand<E>), + /// `LC_SEGMENT_64` + Segment64(&'data macho::SegmentCommand64<E>, &'data [u8]), + /// `LC_ROUTINES_64` + Routines64(&'data macho::RoutinesCommand64<E>), + /// `LC_UUID` + Uuid(&'data macho::UuidCommand<E>), + /// `LC_RPATH` + Rpath(&'data macho::RpathCommand<E>), + /// `LC_CODE_SIGNATURE`, `LC_SEGMENT_SPLIT_INFO`, `LC_FUNCTION_STARTS`, + /// `LC_DATA_IN_CODE`, `LC_DYLIB_CODE_SIGN_DRS`, `LC_LINKER_OPTIMIZATION_HINT`, + /// `LC_DYLD_EXPORTS_TRIE`, or `LC_DYLD_CHAINED_FIXUPS`. + LinkeditData(&'data macho::LinkeditDataCommand<E>), + /// `LC_ENCRYPTION_INFO` + EncryptionInfo32(&'data macho::EncryptionInfoCommand32<E>), + /// `LC_DYLD_INFO` or `LC_DYLD_INFO_ONLY` + DyldInfo(&'data macho::DyldInfoCommand<E>), + /// `LC_VERSION_MIN_MACOSX`, `LC_VERSION_MIN_IPHONEOS`, `LC_VERSION_MIN_WATCHOS`, + /// or `LC_VERSION_MIN_TVOS` + VersionMin(&'data macho::VersionMinCommand<E>), + /// `LC_DYLD_ENVIRONMENT` + DyldEnvironment(&'data macho::DylinkerCommand<E>), + /// `LC_MAIN` + EntryPoint(&'data macho::EntryPointCommand<E>), + /// `LC_SOURCE_VERSION` + SourceVersion(&'data macho::SourceVersionCommand<E>), + /// `LC_ENCRYPTION_INFO_64` + EncryptionInfo64(&'data macho::EncryptionInfoCommand64<E>), + /// `LC_LINKER_OPTION` + LinkerOption(&'data macho::LinkerOptionCommand<E>), + /// `LC_NOTE` + Note(&'data macho::NoteCommand<E>), + /// `LC_BUILD_VERSION` + BuildVersion(&'data macho::BuildVersionCommand<E>), + /// `LC_FILESET_ENTRY` + FilesetEntry(&'data macho::FilesetEntryCommand<E>), + /// An unrecognized or obsolete load command. + Other, +} + +impl<E: Endian> macho::SymtabCommand<E> { + /// Return the symbol table that this command references. + pub fn symbols<'data, Mach: MachHeader<Endian = E>, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result<SymbolTable<'data, Mach, R>> { + let symbols = data + .read_slice_at( + self.symoff.get(endian).into(), + self.nsyms.get(endian) as usize, + ) + .read_error("Invalid Mach-O symbol table offset or size")?; + let str_start: u64 = self.stroff.get(endian).into(); + let str_end = str_start + .checked_add(self.strsize.get(endian).into()) + .read_error("Invalid Mach-O string table length")?; + let strings = StringTable::new(data, str_start, str_end); + Ok(SymbolTable::new(symbols, strings)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::LittleEndian; + + #[test] + fn cmd_size_invalid() { + #[repr(align(16))] + struct Align<const N: usize>([u8; N]); + let mut commands = LoadCommandIterator::new(LittleEndian, &Align([0; 8]).0, 10); + assert!(commands.next().is_err()); + let mut commands = + LoadCommandIterator::new(LittleEndian, &Align([0, 0, 0, 0, 7, 0, 0, 0, 0]).0, 10); + assert!(commands.next().is_err()); + let mut commands = + LoadCommandIterator::new(LittleEndian, &Align([0, 0, 0, 0, 8, 0, 0, 0, 0]).0, 10); + assert!(commands.next().is_ok()); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/mod.rs new file mode 100644 index 0000000..ab51ff3 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/mod.rs
@@ -0,0 +1,72 @@ +//! Support for reading Mach-O files. +//! +//! Traits are used to abstract over the difference between 32-bit and 64-bit Mach-O +//! files. The primary trait for this is [`MachHeader`]. +//! +//! ## High level API +//! +//! [`MachOFile`] implements the [`Object`](crate::read::Object) trait for Mach-O files. +//! [`MachOFile`] is parameterised by [`MachHeader`] to allow reading both 32-bit and +//! 64-bit Mach-O files. There are type aliases for these parameters ([`MachOFile32`] and +//! [`MachOFile64`]). +//! +//! ## Low level API +//! +//! The [`MachHeader`] trait can be directly used to parse both [`macho::MachHeader32`] +//! and [`macho::MachHeader64`]. Additionally, [`FatHeader`] and the [`FatArch`] trait +//! can be used to iterate images in multi-architecture binaries, and [`DyldCache`] can +//! be used to locate images in a dyld shared cache. +//! +//! ### Example for low level API +//! ```no_run +//! use object::macho; +//! use object::read::macho::{MachHeader, Nlist}; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the name of each symbol. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(feature = "std")] { +//! let data = fs::read("path/to/binary")?; +//! let header = macho::MachHeader64::<object::Endianness>::parse(&*data, 0)?; +//! let endian = header.endian()?; +//! let mut commands = header.load_commands(endian, &*data, 0)?; +//! while let Some(command) = commands.next()? { +//! if let Some(symtab_command) = command.symtab()? { +//! let symbols = symtab_command.symbols::<macho::MachHeader64<_>, _>(endian, &*data)?; +//! for symbol in symbols.iter() { +//! let name = symbol.name(endian, symbols.strings())?; +//! println!("{}", String::from_utf8_lossy(name)); +//! } +//! } +//! } +//! # } +//! Ok(()) +//! } +//! ``` +#[cfg(doc)] +use crate::macho; + +mod dyld_cache; +pub use dyld_cache::*; + +mod fat; +pub use fat::*; + +mod file; +pub use file::*; + +mod load_command; +pub use load_command::*; + +mod segment; +pub use segment::*; + +mod section; +pub use section::*; + +mod symbol; +pub use symbol::*; + +mod relocation; +pub use relocation::*;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/relocation.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/relocation.rs new file mode 100644 index 0000000..a2d1ea7 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/relocation.rs
@@ -0,0 +1,144 @@ +use core::{fmt, slice}; + +use crate::endian::Endianness; +use crate::macho; +use crate::read::{ + ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, + SectionIndex, SymbolIndex, +}; + +use super::{MachHeader, MachOFile}; + +/// An iterator for the relocations in a [`MachOSection32`](super::MachOSection32). +pub type MachORelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachORelocationIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator for the relocations in a [`MachOSection64`](super::MachOSection64). +pub type MachORelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachORelocationIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator for the relocations in a [`MachOSection`](super::MachOSection). +pub struct MachORelocationIterator<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file MachOFile<'data, Mach, R>, + pub(super) relocations: slice::Iter<'data, macho::Relocation<Mach::Endian>>, +} + +impl<'data, 'file, Mach, R> Iterator for MachORelocationIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + use RelocationEncoding as E; + use RelocationKind as K; + + let mut paired_addend = 0; + loop { + let reloc = self.relocations.next()?; + let endian = self.file.endian; + let cputype = self.file.header.cputype(endian); + if reloc.r_scattered(endian, cputype) { + // FIXME: handle scattered relocations + // We need to add `RelocationTarget::Address` for this. + continue; + } + let reloc = reloc.info(self.file.endian); + let flags = RelocationFlags::MachO { + r_type: reloc.r_type, + r_pcrel: reloc.r_pcrel, + r_length: reloc.r_length, + }; + let g = E::Generic; + let unknown = (K::Unknown, E::Generic); + let (kind, encoding) = match cputype { + macho::CPU_TYPE_ARM => match (reloc.r_type, reloc.r_pcrel) { + (macho::ARM_RELOC_VANILLA, false) => (K::Absolute, g), + _ => unknown, + }, + macho::CPU_TYPE_ARM64 | macho::CPU_TYPE_ARM64_32 => { + match (reloc.r_type, reloc.r_pcrel) { + (macho::ARM64_RELOC_UNSIGNED, false) => (K::Absolute, g), + (macho::ARM64_RELOC_ADDEND, _) => { + paired_addend = i64::from(reloc.r_symbolnum) + .wrapping_shl(64 - 24) + .wrapping_shr(64 - 24); + continue; + } + _ => unknown, + } + } + macho::CPU_TYPE_X86 => match (reloc.r_type, reloc.r_pcrel) { + (macho::GENERIC_RELOC_VANILLA, false) => (K::Absolute, g), + _ => unknown, + }, + macho::CPU_TYPE_X86_64 => match (reloc.r_type, reloc.r_pcrel) { + (macho::X86_64_RELOC_UNSIGNED, false) => (K::Absolute, g), + (macho::X86_64_RELOC_SIGNED, true) => (K::Relative, E::X86RipRelative), + (macho::X86_64_RELOC_BRANCH, true) => (K::Relative, E::X86Branch), + (macho::X86_64_RELOC_GOT, true) => (K::GotRelative, g), + (macho::X86_64_RELOC_GOT_LOAD, true) => (K::GotRelative, E::X86RipRelativeMovq), + _ => unknown, + }, + _ => unknown, + }; + let size = 8 << reloc.r_length; + let target = if reloc.r_extern { + RelocationTarget::Symbol(SymbolIndex(reloc.r_symbolnum as usize)) + } else { + RelocationTarget::Section(SectionIndex(reloc.r_symbolnum as usize)) + }; + let implicit_addend = paired_addend == 0; + let mut addend = paired_addend; + if reloc.r_pcrel { + // For PC relative relocations on some architectures, the + // addend does not include the offset required due to the + // PC being different from the place of the relocation. + // This differs from other file formats, so adjust the + // addend here to account for this. + match cputype { + macho::CPU_TYPE_X86 => { + addend -= 1 << reloc.r_length; + } + macho::CPU_TYPE_X86_64 => { + addend -= 1 << reloc.r_length; + match reloc.r_type { + macho::X86_64_RELOC_SIGNED_1 => addend -= 1, + macho::X86_64_RELOC_SIGNED_2 => addend -= 2, + macho::X86_64_RELOC_SIGNED_4 => addend -= 4, + _ => {} + } + } + // TODO: maybe missing support for some architectures and relocations + _ => {} + } + } + return Some(( + reloc.r_address as u64, + Relocation { + kind, + encoding, + size, + target, + addend, + implicit_addend, + flags, + }, + )); + } + } +} + +impl<'data, 'file, Mach, R> fmt::Debug for MachORelocationIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MachORelocationIterator").finish() + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/section.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/section.rs new file mode 100644 index 0000000..d27a9a9 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/section.rs
@@ -0,0 +1,420 @@ +use core::fmt::Debug; +use core::{fmt, result, slice, str}; + +use crate::endian::{self, Endianness}; +use crate::macho; +use crate::pod::Pod; +use crate::read::{ + self, gnu_compression, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, + RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, +}; + +use super::{MachHeader, MachOFile, MachORelocationIterator}; + +/// An iterator for the sections in a [`MachOFile32`](super::MachOFile32). +pub type MachOSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator for the sections in a [`MachOFile64`](super::MachOFile64). +pub type MachOSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator for the sections in a [`MachOFile`]. +pub struct MachOSectionIterator<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file MachOFile<'data, Mach, R>, + pub(super) iter: slice::Iter<'file, MachOSectionInternal<'data, Mach, R>>, +} + +impl<'data, 'file, Mach, R> fmt::Debug for MachOSectionIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's painful to do much better than this + f.debug_struct("MachOSectionIterator").finish() + } +} + +impl<'data, 'file, Mach, R> Iterator for MachOSectionIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Item = MachOSection<'data, 'file, Mach, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|&internal| MachOSection { + file: self.file, + internal, + }) + } +} + +/// A section in a [`MachOFile32`](super::MachOFile32). +pub type MachOSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSection<'data, 'file, macho::MachHeader32<Endian>, R>; +/// A section in a [`MachOFile64`](super::MachOFile64). +pub type MachOSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSection<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A section in a [`MachOFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct MachOSection<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file MachOFile<'data, Mach, R>, + pub(super) internal: MachOSectionInternal<'data, Mach, R>, +} + +impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + /// Get the Mach-O file containing this section. + pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> { + self.file + } + + /// Get the raw Mach-O section structure. + pub fn macho_section(&self) -> &'data Mach::Section { + self.internal.section + } + + /// Get the raw Mach-O relocation entries. + pub fn macho_relocations(&self) -> Result<&'data [macho::Relocation<Mach::Endian>]> { + self.internal + .section + .relocations(self.file.endian, self.internal.data) + } + + fn bytes(&self) -> Result<&'data [u8]> { + self.internal + .section + .data(self.file.endian, self.internal.data) + .read_error("Invalid Mach-O section size or offset") + } + + // Try GNU-style "ZLIB" header decompression. + fn maybe_compressed_gnu(&self) -> Result<Option<CompressedFileRange>> { + if !self + .name() + .map_or(false, |name| name.starts_with("__zdebug_")) + { + return Ok(None); + } + let (section_offset, section_size) = self + .file_range() + .read_error("Invalid ELF GNU compressed section type")?; + gnu_compression::compressed_file_range(self.internal.data, section_offset, section_size) + .map(Some) + } +} + +impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type RelocationIterator = MachORelocationIterator<'data, 'file, Mach, R>; + + #[inline] + fn index(&self) -> SectionIndex { + self.internal.index + } + + #[inline] + fn address(&self) -> u64 { + self.internal.section.addr(self.file.endian).into() + } + + #[inline] + fn size(&self) -> u64 { + self.internal.section.size(self.file.endian).into() + } + + #[inline] + fn align(&self) -> u64 { + let align = self.internal.section.align(self.file.endian); + if align < 64 { + 1 << align + } else { + 0 + } + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + self.internal.section.file_range(self.file.endian) + } + + #[inline] + fn data(&self) -> Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(if let Some(data) = self.maybe_compressed_gnu()? { + data + } else { + CompressedFileRange::none(self.file_range()) + }) + } + + fn compressed_data(&self) -> read::Result<CompressedData<'data>> { + self.compressed_file_range()?.data(self.file.data) + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + Ok(self.internal.section.name()) + } + + #[inline] + fn name(&self) -> Result<&'data str> { + str::from_utf8(self.internal.section.name()) + .ok() + .read_error("Non UTF-8 Mach-O section name") + } + + #[inline] + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(Some(self.internal.section.segment_name())) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + Ok(Some( + str::from_utf8(self.internal.section.segment_name()) + .ok() + .read_error("Non UTF-8 Mach-O segment name")?, + )) + } + + fn kind(&self) -> SectionKind { + self.internal.kind + } + + fn relocations(&self) -> MachORelocationIterator<'data, 'file, Mach, R> { + MachORelocationIterator { + file: self.file, + relocations: self.macho_relocations().unwrap_or(&[]).iter(), + } + } + + fn relocation_map(&self) -> read::Result<RelocationMap> { + RelocationMap::new(self.file, self) + } + + fn flags(&self) -> SectionFlags { + SectionFlags::MachO { + flags: self.internal.section.flags(self.file.endian), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub(super) struct MachOSectionInternal<'data, Mach: MachHeader, R: ReadRef<'data>> { + pub index: SectionIndex, + pub kind: SectionKind, + pub section: &'data Mach::Section, + /// The data for the file that contains the section data. + /// + /// This is required for dyld caches, where this may be a different subcache + /// from the file containing the Mach-O load commands. + pub data: R, +} + +impl<'data, Mach: MachHeader, R: ReadRef<'data>> MachOSectionInternal<'data, Mach, R> { + pub(super) fn parse(index: SectionIndex, section: &'data Mach::Section, data: R) -> Self { + // TODO: we don't validate flags, should we? + let kind = match (section.segment_name(), section.name()) { + (b"__TEXT", b"__text") => SectionKind::Text, + (b"__TEXT", b"__const") => SectionKind::ReadOnlyData, + (b"__TEXT", b"__cstring") => SectionKind::ReadOnlyString, + (b"__TEXT", b"__literal4") => SectionKind::ReadOnlyData, + (b"__TEXT", b"__literal8") => SectionKind::ReadOnlyData, + (b"__TEXT", b"__literal16") => SectionKind::ReadOnlyData, + (b"__TEXT", b"__eh_frame") => SectionKind::ReadOnlyData, + (b"__TEXT", b"__gcc_except_tab") => SectionKind::ReadOnlyData, + (b"__DATA", b"__data") => SectionKind::Data, + (b"__DATA", b"__const") => SectionKind::ReadOnlyData, + (b"__DATA", b"__bss") => SectionKind::UninitializedData, + (b"__DATA", b"__common") => SectionKind::Common, + (b"__DATA", b"__thread_data") => SectionKind::Tls, + (b"__DATA", b"__thread_bss") => SectionKind::UninitializedTls, + (b"__DATA", b"__thread_vars") => SectionKind::TlsVariables, + (b"__DWARF", _) => SectionKind::Debug, + _ => SectionKind::Unknown, + }; + MachOSectionInternal { + index, + kind, + section, + data, + } + } +} + +/// A trait for generic access to [`macho::Section32`] and [`macho::Section64`]. +#[allow(missing_docs)] +pub trait Section: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn sectname(&self) -> &[u8; 16]; + fn segname(&self) -> &[u8; 16]; + fn addr(&self, endian: Self::Endian) -> Self::Word; + fn size(&self, endian: Self::Endian) -> Self::Word; + fn offset(&self, endian: Self::Endian) -> u32; + fn align(&self, endian: Self::Endian) -> u32; + fn reloff(&self, endian: Self::Endian) -> u32; + fn nreloc(&self, endian: Self::Endian) -> u32; + fn flags(&self, endian: Self::Endian) -> u32; + + /// Return the `sectname` bytes up until the null terminator. + fn name(&self) -> &[u8] { + let sectname = &self.sectname()[..]; + match memchr::memchr(b'\0', sectname) { + Some(end) => §name[..end], + None => sectname, + } + } + + /// Return the `segname` bytes up until the null terminator. + fn segment_name(&self) -> &[u8] { + let segname = &self.segname()[..]; + match memchr::memchr(b'\0', segname) { + Some(end) => &segname[..end], + None => segname, + } + } + + /// Return the offset and size of the section in the file. + /// + /// Returns `None` for sections that have no data in the file. + fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> { + match self.flags(endian) & macho::SECTION_TYPE { + macho::S_ZEROFILL | macho::S_GB_ZEROFILL | macho::S_THREAD_LOCAL_ZEROFILL => None, + _ => Some((self.offset(endian).into(), self.size(endian).into())), + } + } + + /// Return the section data. + /// + /// Returns `Ok(&[])` if the section has no data. + /// Returns `Err` for invalid values. + fn data<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> result::Result<&'data [u8], ()> { + if let Some((offset, size)) = self.file_range(endian) { + data.read_bytes_at(offset, size) + } else { + Ok(&[]) + } + } + + /// Return the relocation array. + /// + /// Returns `Err` for invalid values. + fn relocations<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> Result<&'data [macho::Relocation<Self::Endian>]> { + data.read_slice_at(self.reloff(endian).into(), self.nreloc(endian) as usize) + .read_error("Invalid Mach-O relocations offset or number") + } +} + +impl<Endian: endian::Endian> Section for macho::Section32<Endian> { + type Word = u32; + type Endian = Endian; + + fn sectname(&self) -> &[u8; 16] { + &self.sectname + } + fn segname(&self) -> &[u8; 16] { + &self.segname + } + fn addr(&self, endian: Self::Endian) -> Self::Word { + self.addr.get(endian) + } + fn size(&self, endian: Self::Endian) -> Self::Word { + self.size.get(endian) + } + fn offset(&self, endian: Self::Endian) -> u32 { + self.offset.get(endian) + } + fn align(&self, endian: Self::Endian) -> u32 { + self.align.get(endian) + } + fn reloff(&self, endian: Self::Endian) -> u32 { + self.reloff.get(endian) + } + fn nreloc(&self, endian: Self::Endian) -> u32 { + self.nreloc.get(endian) + } + fn flags(&self, endian: Self::Endian) -> u32 { + self.flags.get(endian) + } +} + +impl<Endian: endian::Endian> Section for macho::Section64<Endian> { + type Word = u64; + type Endian = Endian; + + fn sectname(&self) -> &[u8; 16] { + &self.sectname + } + fn segname(&self) -> &[u8; 16] { + &self.segname + } + fn addr(&self, endian: Self::Endian) -> Self::Word { + self.addr.get(endian) + } + fn size(&self, endian: Self::Endian) -> Self::Word { + self.size.get(endian) + } + fn offset(&self, endian: Self::Endian) -> u32 { + self.offset.get(endian) + } + fn align(&self, endian: Self::Endian) -> u32 { + self.align.get(endian) + } + fn reloff(&self, endian: Self::Endian) -> u32 { + self.reloff.get(endian) + } + fn nreloc(&self, endian: Self::Endian) -> u32 { + self.nreloc.get(endian) + } + fn flags(&self, endian: Self::Endian) -> u32 { + self.flags.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/segment.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/segment.rs new file mode 100644 index 0000000..f704c5c --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/segment.rs
@@ -0,0 +1,317 @@ +use core::fmt::Debug; +use core::{result, slice, str}; + +use crate::endian::{self, Endianness}; +use crate::macho; +use crate::pod::Pod; +use crate::read::{self, ObjectSegment, ReadError, ReadRef, Result, SegmentFlags}; + +use super::{LoadCommandData, MachHeader, MachOFile, Section}; + +/// An iterator for the segments in a [`MachOFile32`](super::MachOFile32). +pub type MachOSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegmentIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator for the segments in a [`MachOFile64`](super::MachOFile64). +pub type MachOSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegmentIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator for the segments in a [`MachOFile`]. +#[derive(Debug)] +pub struct MachOSegmentIterator<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file MachOFile<'data, Mach, R>, + pub(super) iter: slice::Iter<'file, MachOSegmentInternal<'data, Mach, R>>, +} + +impl<'data, 'file, Mach, R> Iterator for MachOSegmentIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Item = MachOSegment<'data, 'file, Mach, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|internal| MachOSegment { + file: self.file, + internal, + }) + } +} + +/// A segment in a [`MachOFile32`](super::MachOFile32). +pub type MachOSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegment<'data, 'file, macho::MachHeader32<Endian>, R>; +/// A segment in a [`MachOFile64`](super::MachOFile64). +pub type MachOSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegment<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A segment in a [`MachOFile`]. +/// +/// Most functionality is provided by the [`ObjectSegment`] trait implementation. +#[derive(Debug)] +pub struct MachOSegment<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + file: &'file MachOFile<'data, Mach, R>, + internal: &'file MachOSegmentInternal<'data, Mach, R>, +} + +impl<'data, 'file, Mach, R> MachOSegment<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + /// Get the Mach-O file containing this segment. + pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> { + self.file + } + + /// Get the raw Mach-O segment structure. + pub fn macho_segment(&self) -> &'data Mach::Segment { + self.internal.segment + } + + fn bytes(&self) -> Result<&'data [u8]> { + self.internal + .segment + .data(self.file.endian, self.internal.data) + .read_error("Invalid Mach-O segment size or offset") + } +} + +impl<'data, 'file, Mach, R> read::private::Sealed for MachOSegment<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Mach, R> ObjectSegment<'data> for MachOSegment<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + #[inline] + fn address(&self) -> u64 { + self.internal.segment.vmaddr(self.file.endian).into() + } + + #[inline] + fn size(&self) -> u64 { + self.internal.segment.vmsize(self.file.endian).into() + } + + #[inline] + fn align(&self) -> u64 { + // Page size. + 0x1000 + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + self.internal.segment.file_range(self.file.endian) + } + + fn data(&self) -> Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(Some(self.internal.segment.name())) + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + Ok(Some( + str::from_utf8(self.internal.segment.name()) + .ok() + .read_error("Non UTF-8 Mach-O segment name")?, + )) + } + + #[inline] + fn flags(&self) -> SegmentFlags { + let flags = self.internal.segment.flags(self.file.endian); + let maxprot = self.internal.segment.maxprot(self.file.endian); + let initprot = self.internal.segment.initprot(self.file.endian); + SegmentFlags::MachO { + flags, + maxprot, + initprot, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub(super) struct MachOSegmentInternal<'data, Mach: MachHeader, R: ReadRef<'data>> { + pub segment: &'data Mach::Segment, + /// The data for the file that contains the segment data. + /// + /// This is required for dyld caches, where this may be a different subcache + /// from the file containing the Mach-O load commands. + pub data: R, +} + +/// A trait for generic access to [`macho::SegmentCommand32`] and [`macho::SegmentCommand64`]. +#[allow(missing_docs)] +pub trait Segment: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + type Section: Section<Endian = Self::Endian>; + + fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>>; + + fn cmd(&self, endian: Self::Endian) -> u32; + fn cmdsize(&self, endian: Self::Endian) -> u32; + fn segname(&self) -> &[u8; 16]; + fn vmaddr(&self, endian: Self::Endian) -> Self::Word; + fn vmsize(&self, endian: Self::Endian) -> Self::Word; + fn fileoff(&self, endian: Self::Endian) -> Self::Word; + fn filesize(&self, endian: Self::Endian) -> Self::Word; + fn maxprot(&self, endian: Self::Endian) -> u32; + fn initprot(&self, endian: Self::Endian) -> u32; + fn nsects(&self, endian: Self::Endian) -> u32; + fn flags(&self, endian: Self::Endian) -> u32; + + /// Return the `segname` bytes up until the null terminator. + fn name(&self) -> &[u8] { + let segname = &self.segname()[..]; + match memchr::memchr(b'\0', segname) { + Some(end) => &segname[..end], + None => segname, + } + } + + /// Return the offset and size of the segment in the file. + fn file_range(&self, endian: Self::Endian) -> (u64, u64) { + (self.fileoff(endian).into(), self.filesize(endian).into()) + } + + /// Get the segment data from the file data. + /// + /// Returns `Err` for invalid values. + fn data<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> result::Result<&'data [u8], ()> { + let (offset, size) = self.file_range(endian); + data.read_bytes_at(offset, size) + } + + /// Get the array of sections from the data following the segment command. + /// + /// Returns `Err` for invalid values. + fn sections<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + section_data: R, + ) -> Result<&'data [Self::Section]> { + section_data + .read_slice_at(0, self.nsects(endian) as usize) + .read_error("Invalid Mach-O number of sections") + } +} + +impl<Endian: endian::Endian> Segment for macho::SegmentCommand32<Endian> { + type Word = u32; + type Endian = Endian; + type Section = macho::Section32<Self::Endian>; + + fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> { + command.segment_32() + } + + fn cmd(&self, endian: Self::Endian) -> u32 { + self.cmd.get(endian) + } + fn cmdsize(&self, endian: Self::Endian) -> u32 { + self.cmdsize.get(endian) + } + fn segname(&self) -> &[u8; 16] { + &self.segname + } + fn vmaddr(&self, endian: Self::Endian) -> Self::Word { + self.vmaddr.get(endian) + } + fn vmsize(&self, endian: Self::Endian) -> Self::Word { + self.vmsize.get(endian) + } + fn fileoff(&self, endian: Self::Endian) -> Self::Word { + self.fileoff.get(endian) + } + fn filesize(&self, endian: Self::Endian) -> Self::Word { + self.filesize.get(endian) + } + fn maxprot(&self, endian: Self::Endian) -> u32 { + self.maxprot.get(endian) + } + fn initprot(&self, endian: Self::Endian) -> u32 { + self.initprot.get(endian) + } + fn nsects(&self, endian: Self::Endian) -> u32 { + self.nsects.get(endian) + } + fn flags(&self, endian: Self::Endian) -> u32 { + self.flags.get(endian) + } +} + +impl<Endian: endian::Endian> Segment for macho::SegmentCommand64<Endian> { + type Word = u64; + type Endian = Endian; + type Section = macho::Section64<Self::Endian>; + + fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> { + command.segment_64() + } + + fn cmd(&self, endian: Self::Endian) -> u32 { + self.cmd.get(endian) + } + fn cmdsize(&self, endian: Self::Endian) -> u32 { + self.cmdsize.get(endian) + } + fn segname(&self) -> &[u8; 16] { + &self.segname + } + fn vmaddr(&self, endian: Self::Endian) -> Self::Word { + self.vmaddr.get(endian) + } + fn vmsize(&self, endian: Self::Endian) -> Self::Word { + self.vmsize.get(endian) + } + fn fileoff(&self, endian: Self::Endian) -> Self::Word { + self.fileoff.get(endian) + } + fn filesize(&self, endian: Self::Endian) -> Self::Word { + self.filesize.get(endian) + } + fn maxprot(&self, endian: Self::Endian) -> u32 { + self.maxprot.get(endian) + } + fn initprot(&self, endian: Self::Endian) -> u32 { + self.initprot.get(endian) + } + fn nsects(&self, endian: Self::Endian) -> u32 { + self.nsects.get(endian) + } + fn flags(&self, endian: Self::Endian) -> u32 { + self.flags.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/macho/symbol.rs b/chromium_crates_io/vendor/object-v0_37/src/read/macho/symbol.rs new file mode 100644 index 0000000..cff93d3 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/macho/symbol.rs
@@ -0,0 +1,546 @@ +use alloc::vec::Vec; +use core::fmt::Debug; +use core::{fmt, slice, str}; + +use crate::endian::{self, Endianness}; +use crate::macho; +use crate::pod::Pod; +use crate::read::util::StringTable; +use crate::read::{ + self, ObjectMap, ObjectMapEntry, ObjectMapFile, ObjectSymbol, ObjectSymbolTable, ReadError, + ReadRef, Result, SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, + SymbolMapEntry, SymbolScope, SymbolSection, +}; + +use super::{MachHeader, MachOFile}; + +/// A table of symbol entries in a Mach-O file. +/// +/// Also includes the string table used for the symbol names. +/// +/// Returned by [`macho::SymtabCommand::symbols`]. +#[derive(Debug, Clone, Copy)] +pub struct SymbolTable<'data, Mach: MachHeader, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + symbols: &'data [Mach::Nlist], + strings: StringTable<'data, R>, +} + +impl<'data, Mach: MachHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Mach, R> { + fn default() -> Self { + SymbolTable { + symbols: &[], + strings: Default::default(), + } + } +} + +impl<'data, Mach: MachHeader, R: ReadRef<'data>> SymbolTable<'data, Mach, R> { + #[inline] + pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>) -> Self { + SymbolTable { symbols, strings } + } + + /// Return the string table used for the symbol names. + #[inline] + pub fn strings(&self) -> StringTable<'data, R> { + self.strings + } + + /// Iterate over the symbols. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Mach::Nlist> { + self.symbols.iter() + } + + /// Return true if the symbol table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// The number of symbols. + #[inline] + pub fn len(&self) -> usize { + self.symbols.len() + } + + /// Return the symbol at the given index. + pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Mach::Nlist> { + self.symbols + .get(index.0) + .read_error("Invalid Mach-O symbol index") + } + + /// Construct a map from addresses to a user-defined map entry. + pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>( + &self, + f: F, + ) -> SymbolMap<Entry> { + let mut symbols = Vec::new(); + for nlist in self.symbols { + if !nlist.is_definition() { + continue; + } + if let Some(entry) = f(nlist) { + symbols.push(entry); + } + } + SymbolMap::new(symbols) + } + + /// Construct a map from addresses to symbol names and object file names. + pub fn object_map(&self, endian: Mach::Endian) -> ObjectMap<'data> { + let mut symbols = Vec::new(); + let mut objects = Vec::new(); + let mut object = None; + let mut current_function = None; + // Each module starts with one or two N_SO symbols (path, or directory + filename) + // and one N_OSO symbol. The module is terminated by an empty N_SO symbol. + for nlist in self.symbols { + let n_type = nlist.n_type(); + if n_type & macho::N_STAB == 0 { + continue; + } + // TODO: includes global symbols too (N_GSYM). These may need to get their + // address from regular symbols though. + match n_type { + macho::N_SO => { + object = None; + } + macho::N_OSO => { + object = None; + if let Ok(name) = nlist.name(endian, self.strings) { + if !name.is_empty() { + object = Some(objects.len()); + // `N_OSO` symbol names can be either `/path/to/object.o` + // or `/path/to/archive.a(object.o)`. + let (path, member) = name + .split_last() + .and_then(|(last, head)| { + if *last != b')' { + return None; + } + let index = head.iter().position(|&x| x == b'(')?; + let (archive, rest) = head.split_at(index); + Some((archive, Some(&rest[1..]))) + }) + .unwrap_or((name, None)); + objects.push(ObjectMapFile::new(path, member)); + } + } + } + macho::N_FUN => { + if let Ok(name) = nlist.name(endian, self.strings) { + if !name.is_empty() { + current_function = Some((name, nlist.n_value(endian).into())) + } else if let Some((name, address)) = current_function.take() { + if let Some(object) = object { + symbols.push(ObjectMapEntry { + address, + size: nlist.n_value(endian).into(), + name, + object, + }); + } + } + } + } + macho::N_STSYM => { + // Static symbols have a single entry with the address of the symbol + // but no size + if let Ok(name) = nlist.name(endian, self.strings) { + if let Some(object) = object { + symbols.push(ObjectMapEntry { + address: nlist.n_value(endian).into(), + size: 0, + name, + object, + }) + } + } + } + _ => {} + } + } + ObjectMap { + symbols: SymbolMap::new(symbols), + objects, + } + } +} + +/// A symbol table in a [`MachOFile32`](super::MachOFile32). +pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>, R>; +/// A symbol table in a [`MachOFile64`](super::MachOFile64). +pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A symbol table in a [`MachOFile`]. +#[derive(Debug, Clone, Copy)] +pub struct MachOSymbolTable<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file MachOFile<'data, Mach, R>, +} + +impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbolTable<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Mach, R> ObjectSymbolTable<'data> for MachOSymbolTable<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Symbol = MachOSymbol<'data, 'file, Mach, R>; + type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>; + + fn symbols(&self) -> Self::SymbolIterator { + MachOSymbolIterator::new(self.file) + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { + let nlist = self.file.symbols.symbol(index)?; + MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index") + } +} + +/// An iterator for the symbols in a [`MachOFile32`](super::MachOFile32). +pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator for the symbols in a [`MachOFile64`](super::MachOFile64). +pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator for the symbols in a [`MachOFile`]. +pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + file: &'file MachOFile<'data, Mach, R>, + index: SymbolIndex, +} + +impl<'data, 'file, Mach, R> MachOSymbolIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) fn new(file: &'file MachOFile<'data, Mach, R>) -> Self { + MachOSymbolIterator { + file, + index: SymbolIndex(0), + } + } + + pub(super) fn empty(file: &'file MachOFile<'data, Mach, R>) -> Self { + MachOSymbolIterator { + file, + index: SymbolIndex(file.symbols.len()), + } + } +} + +impl<'data, 'file, Mach, R> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MachOSymbolIterator").finish() + } +} + +impl<'data, 'file, Mach, R> Iterator for MachOSymbolIterator<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + type Item = MachOSymbol<'data, 'file, Mach, R>; + + fn next(&mut self) -> Option<Self::Item> { + loop { + let index = self.index; + let nlist = self.file.symbols.symbols.get(index.0)?; + self.index.0 += 1; + if let Some(symbol) = MachOSymbol::new(self.file, index, nlist) { + return Some(symbol); + } + } + } +} + +/// A symbol in a [`MachOFile32`](super::MachOFile32). +pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbol<'data, 'file, macho::MachHeader32<Endian>, R>; +/// A symbol in a [`MachOFile64`](super::MachOFile64). +pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbol<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A symbol in a [`MachOFile`]. +/// +/// Most functionality is provided by the [`ObjectSymbol`] trait implementation. +#[derive(Debug, Clone, Copy)] +pub struct MachOSymbol<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + file: &'file MachOFile<'data, Mach, R>, + index: SymbolIndex, + nlist: &'data Mach::Nlist, +} + +impl<'data, 'file, Mach, R> MachOSymbol<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) fn new( + file: &'file MachOFile<'data, Mach, R>, + index: SymbolIndex, + nlist: &'data Mach::Nlist, + ) -> Option<Self> { + if nlist.n_type() & macho::N_STAB != 0 { + return None; + } + Some(MachOSymbol { file, index, nlist }) + } + + /// Get the Mach-O file containing this symbol. + pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> { + self.file + } + + /// Get the raw Mach-O symbol structure. + pub fn macho_symbol(&self) -> &'data Mach::Nlist { + self.nlist + } +} + +impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbol<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Mach, R> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + #[inline] + fn index(&self) -> SymbolIndex { + self.index + } + + fn name_bytes(&self) -> Result<&'data [u8]> { + self.nlist.name(self.file.endian, self.file.symbols.strings) + } + + fn name(&self) -> Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 Mach-O symbol name") + } + + #[inline] + fn address(&self) -> u64 { + self.nlist.n_value(self.file.endian).into() + } + + #[inline] + fn size(&self) -> u64 { + 0 + } + + fn kind(&self) -> SymbolKind { + self.section() + .index() + .and_then(|index| self.file.section_internal(index).ok()) + .map(|section| match section.kind { + SectionKind::Text => SymbolKind::Text, + SectionKind::Data + | SectionKind::ReadOnlyData + | SectionKind::ReadOnlyString + | SectionKind::UninitializedData + | SectionKind::Common => SymbolKind::Data, + SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => { + SymbolKind::Tls + } + _ => SymbolKind::Unknown, + }) + .unwrap_or(SymbolKind::Unknown) + } + + fn section(&self) -> SymbolSection { + match self.nlist.n_type() & macho::N_TYPE { + macho::N_UNDF => SymbolSection::Undefined, + macho::N_ABS => SymbolSection::Absolute, + macho::N_SECT => { + let n_sect = self.nlist.n_sect(); + if n_sect != 0 { + SymbolSection::Section(SectionIndex(n_sect as usize)) + } else { + SymbolSection::Unknown + } + } + _ => SymbolSection::Unknown, + } + } + + #[inline] + fn is_undefined(&self) -> bool { + self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF + } + + #[inline] + fn is_definition(&self) -> bool { + self.nlist.is_definition() + } + + #[inline] + fn is_common(&self) -> bool { + // Mach-O common symbols are based on section, not symbol + false + } + + #[inline] + fn is_weak(&self) -> bool { + self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0 + } + + fn scope(&self) -> SymbolScope { + let n_type = self.nlist.n_type(); + if n_type & macho::N_TYPE == macho::N_UNDF { + SymbolScope::Unknown + } else if n_type & macho::N_EXT == 0 { + SymbolScope::Compilation + } else if n_type & macho::N_PEXT != 0 { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + } + } + + #[inline] + fn is_global(&self) -> bool { + self.scope() != SymbolScope::Compilation + } + + #[inline] + fn is_local(&self) -> bool { + self.scope() == SymbolScope::Compilation + } + + #[inline] + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { + let n_desc = self.nlist.n_desc(self.file.endian); + SymbolFlags::MachO { n_desc } + } +} + +/// A trait for generic access to [`macho::Nlist32`] and [`macho::Nlist64`]. +#[allow(missing_docs)] +pub trait Nlist: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn n_strx(&self, endian: Self::Endian) -> u32; + fn n_type(&self) -> u8; + fn n_sect(&self) -> u8; + fn n_desc(&self, endian: Self::Endian) -> u16; + fn n_value(&self, endian: Self::Endian) -> Self::Word; + + fn name<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.n_strx(endian)) + .read_error("Invalid Mach-O symbol name offset") + } + + /// Return true if this is a STAB symbol. + /// + /// This determines the meaning of the `n_type` field. + fn is_stab(&self) -> bool { + self.n_type() & macho::N_STAB != 0 + } + + /// Return true if this is an undefined symbol. + fn is_undefined(&self) -> bool { + let n_type = self.n_type(); + n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_UNDF + } + + /// Return true if the symbol is a definition of a function or data object. + fn is_definition(&self) -> bool { + let n_type = self.n_type(); + n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_SECT + } + + /// Return the library ordinal. + /// + /// This is either a 1-based index into the dylib load commands, + /// or a special ordinal. + #[inline] + fn library_ordinal(&self, endian: Self::Endian) -> u8 { + (self.n_desc(endian) >> 8) as u8 + } +} + +impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> { + type Word = u32; + type Endian = Endian; + + fn n_strx(&self, endian: Self::Endian) -> u32 { + self.n_strx.get(endian) + } + fn n_type(&self) -> u8 { + self.n_type + } + fn n_sect(&self) -> u8 { + self.n_sect + } + fn n_desc(&self, endian: Self::Endian) -> u16 { + self.n_desc.get(endian) + } + fn n_value(&self, endian: Self::Endian) -> Self::Word { + self.n_value.get(endian) + } +} + +impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> { + type Word = u64; + type Endian = Endian; + + fn n_strx(&self, endian: Self::Endian) -> u32 { + self.n_strx.get(endian) + } + fn n_type(&self) -> u8 { + self.n_type + } + fn n_sect(&self) -> u8 { + self.n_sect + } + fn n_desc(&self, endian: Self::Endian) -> u16 { + self.n_desc.get(endian) + } + fn n_value(&self, endian: Self::Endian) -> Self::Word { + self.n_value.get(endian) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/read/mod.rs new file mode 100644 index 0000000..11d2e73 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/mod.rs
@@ -0,0 +1,1041 @@ +//! Interface for reading object files. +//! +//! ## Unified read API +//! +//! The [`Object`] trait provides a unified read API for accessing common features of +//! object files, such as sections and symbols. There is an implementation of this +//! trait for [`File`], which allows reading any file format, as well as implementations +//! for each file format: +//! [`ElfFile`](elf::ElfFile), [`MachOFile`](macho::MachOFile), [`CoffFile`](coff::CoffFile), +//! [`PeFile`](pe::PeFile), [`WasmFile`](wasm::WasmFile), [`XcoffFile`](xcoff::XcoffFile). +//! +//! ## Low level read API +//! +//! The submodules for each file format define helpers that operate on the raw structs. +//! These can be used instead of the unified API, or in conjunction with it to access +//! details that are not available via the unified API. +//! +//! See the [submodules](#modules) for examples of the low level read API. +//! +//! ## Naming Convention +//! +//! Types that form part of the unified API for a file format are prefixed with the +//! name of the file format. +//! +//! ## Example for unified read API +//! ```no_run +//! use object::{Object, ObjectSection}; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the name of each section. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(all(feature = "read", feature = "std"))] { +//! let data = fs::read("path/to/binary")?; +//! let file = object::File::parse(&*data)?; +//! for section in file.sections() { +//! println!("{}", section.name()?); +//! } +//! # } +//! Ok(()) +//! } +//! ``` + +use alloc::borrow::Cow; +use alloc::vec::Vec; +use core::{fmt, result}; + +#[cfg(not(feature = "std"))] +use alloc::collections::btree_map::BTreeMap as Map; +#[cfg(feature = "std")] +use std::collections::hash_map::HashMap as Map; + +pub use crate::common::*; + +mod read_ref; +pub use read_ref::*; + +mod read_cache; +pub use read_cache::*; + +mod util; +pub use util::*; + +#[cfg(any(feature = "elf", feature = "macho"))] +mod gnu_compression; + +#[cfg(any( + feature = "coff", + feature = "elf", + feature = "macho", + feature = "pe", + feature = "wasm", + feature = "xcoff" +))] +mod any; +#[cfg(any( + feature = "coff", + feature = "elf", + feature = "macho", + feature = "pe", + feature = "wasm", + feature = "xcoff" +))] +pub use any::*; + +#[cfg(feature = "archive")] +pub mod archive; + +#[cfg(feature = "coff")] +pub mod coff; + +#[cfg(feature = "elf")] +pub mod elf; + +#[cfg(feature = "macho")] +pub mod macho; + +#[cfg(feature = "pe")] +pub mod pe; + +#[cfg(feature = "wasm")] +pub mod wasm; + +#[cfg(feature = "xcoff")] +pub mod xcoff; + +mod traits; +pub use traits::*; + +mod private { + pub trait Sealed {} +} + +/// The error type used within the read module. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Error(pub(crate) &'static str); + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.0) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} +#[cfg(all(not(feature = "std"), core_error))] +impl core::error::Error for Error {} + +/// The result type used within the read module. +pub type Result<T> = result::Result<T, Error>; + +trait ReadError<T> { + fn read_error(self, error: &'static str) -> Result<T>; +} + +impl<T> ReadError<T> for result::Result<T, ()> { + fn read_error(self, error: &'static str) -> Result<T> { + self.map_err(|()| Error(error)) + } +} + +impl<T> ReadError<T> for result::Result<T, Error> { + fn read_error(self, error: &'static str) -> Result<T> { + self.map_err(|_| Error(error)) + } +} + +impl<T> ReadError<T> for Option<T> { + fn read_error(self, error: &'static str) -> Result<T> { + self.ok_or(Error(error)) + } +} + +/// The native executable file for the target platform. +#[cfg(all( + unix, + not(target_os = "macos"), + target_pointer_width = "32", + feature = "elf" +))] +pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile32<'data, crate::endian::Endianness, R>; + +/// The native executable file for the target platform. +#[cfg(all( + unix, + not(target_os = "macos"), + target_pointer_width = "64", + feature = "elf" +))] +pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile64<'data, crate::endian::Endianness, R>; + +/// The native executable file for the target platform. +#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))] +pub type NativeFile<'data, R = &'data [u8]> = + macho::MachOFile32<'data, crate::endian::Endianness, R>; + +/// The native executable file for the target platform. +#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))] +pub type NativeFile<'data, R = &'data [u8]> = + macho::MachOFile64<'data, crate::endian::Endianness, R>; + +/// The native executable file for the target platform. +#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))] +pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>; + +/// The native executable file for the target platform. +#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))] +pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>; + +/// The native executable file for the target platform. +#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))] +pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>; + +/// A file format kind. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum FileKind { + /// A Unix archive. + /// + /// See [`archive::ArchiveFile`]. + #[cfg(feature = "archive")] + Archive, + /// A COFF object file. + /// + /// See [`coff::CoffFile`]. + #[cfg(feature = "coff")] + Coff, + /// A COFF bigobj object file. + /// + /// This supports a larger number of sections. + /// + /// See [`coff::CoffBigFile`]. + #[cfg(feature = "coff")] + CoffBig, + /// A Windows short import file. + /// + /// See [`coff::ImportFile`]. + #[cfg(feature = "coff")] + CoffImport, + /// A dyld cache file containing Mach-O images. + /// + /// See [`macho::DyldCache`] + #[cfg(feature = "macho")] + DyldCache, + /// A 32-bit ELF file. + /// + /// See [`elf::ElfFile32`]. + #[cfg(feature = "elf")] + Elf32, + /// A 64-bit ELF file. + /// + /// See [`elf::ElfFile64`]. + #[cfg(feature = "elf")] + Elf64, + /// A 32-bit Mach-O file. + /// + /// See [`macho::MachOFile32`]. + #[cfg(feature = "macho")] + MachO32, + /// A 64-bit Mach-O file. + /// + /// See [`macho::MachOFile64`]. + #[cfg(feature = "macho")] + MachO64, + /// A 32-bit Mach-O fat binary. + /// + /// See [`macho::MachOFatFile32`]. + #[cfg(feature = "macho")] + MachOFat32, + /// A 64-bit Mach-O fat binary. + /// + /// See [`macho::MachOFatFile64`]. + #[cfg(feature = "macho")] + MachOFat64, + /// A 32-bit PE file. + /// + /// See [`pe::PeFile32`]. + #[cfg(feature = "pe")] + Pe32, + /// A 64-bit PE file. + /// + /// See [`pe::PeFile64`]. + #[cfg(feature = "pe")] + Pe64, + /// A Wasm file. + /// + /// See [`wasm::WasmFile`]. + #[cfg(feature = "wasm")] + Wasm, + /// A 32-bit XCOFF file. + /// + /// See [`xcoff::XcoffFile32`]. + #[cfg(feature = "xcoff")] + Xcoff32, + /// A 64-bit XCOFF file. + /// + /// See [`xcoff::XcoffFile64`]. + #[cfg(feature = "xcoff")] + Xcoff64, +} + +impl FileKind { + /// Determine a file kind by parsing the start of the file. + pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> { + Self::parse_at(data, 0) + } + + /// Determine a file kind by parsing at the given offset. + pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> { + let magic = data + .read_bytes_at(offset, 16) + .read_error("Could not read file magic")?; + if magic.len() < 16 { + return Err(Error("File too short")); + } + + let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] { + #[cfg(feature = "archive")] + [b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n'] + | [b'!', b'<', b't', b'h', b'i', b'n', b'>', b'\n'] => FileKind::Archive, + #[cfg(feature = "macho")] + [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache, + #[cfg(feature = "elf")] + [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32, + #[cfg(feature = "elf")] + [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64, + #[cfg(feature = "macho")] + [0xfe, 0xed, 0xfa, 0xce, ..] + | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32, + #[cfg(feature = "macho")] + | [0xfe, 0xed, 0xfa, 0xcf, ..] + | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64, + #[cfg(feature = "macho")] + [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32, + #[cfg(feature = "macho")] + [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64, + #[cfg(feature = "wasm")] + [0x00, b'a', b's', b'm', _, _, 0x00, 0x00] => FileKind::Wasm, + #[cfg(feature = "pe")] + [b'M', b'Z', ..] if offset == 0 => { + // offset == 0 restriction is because optional_header_magic only looks at offset 0 + match pe::optional_header_magic(data) { + Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => { + FileKind::Pe32 + } + Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => { + FileKind::Pe64 + } + _ => return Err(Error("Unknown MS-DOS file")), + } + } + // TODO: more COFF machines + #[cfg(feature = "coff")] + // COFF arm + [0xc4, 0x01, ..] + // COFF arm64 + | [0x64, 0xaa, ..] + // COFF arm64ec + | [0x41, 0xa6, ..] + // COFF ppc + | [0xf0, 0x01, ..] + | [0xf1, 0x01, ..] + | [0xf2, 0x01, ..] + // COFF x86 + | [0x4c, 0x01, ..] + // COFF x86-64 + | [0x64, 0x86, ..] => FileKind::Coff, + #[cfg(feature = "coff")] + [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport, + #[cfg(feature = "coff")] + [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => { + // offset == 0 restriction is because anon_object_class_id only looks at offset 0 + match coff::anon_object_class_id(data) { + Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig, + _ => return Err(Error("Unknown anon object file")), + } + } + #[cfg(feature = "xcoff")] + [0x01, 0xdf, ..] => FileKind::Xcoff32, + #[cfg(feature = "xcoff")] + [0x01, 0xf7, ..] => FileKind::Xcoff64, + _ => return Err(Error("Unknown file magic")), + }; + Ok(kind) + } +} + +/// An object kind. +/// +/// Returned by [`Object::kind`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum ObjectKind { + /// The object kind is unknown. + Unknown, + /// Relocatable object. + Relocatable, + /// Executable. + Executable, + /// Dynamic shared object. + Dynamic, + /// Core. + Core, +} + +/// The index used to identify a section in a file. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SectionIndex(pub usize); + +impl fmt::Display for SectionIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// The index used to identify a symbol in a symbol table. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SymbolIndex(pub usize); + +impl fmt::Display for SymbolIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// The section where an [`ObjectSymbol`] is defined. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SymbolSection { + /// The section is unknown. + Unknown, + /// The section is not applicable for this symbol (such as file symbols). + None, + /// The symbol is undefined. + Undefined, + /// The symbol has an absolute value. + Absolute, + /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions. + Common, + /// The symbol is defined in the given section. + Section(SectionIndex), +} + +impl SymbolSection { + /// Returns the section index for the section where the symbol is defined. + /// + /// May return `None` if the symbol is not defined in a section. + #[inline] + pub fn index(self) -> Option<SectionIndex> { + if let SymbolSection::Section(index) = self { + Some(index) + } else { + None + } + } +} + +/// An entry in a [`SymbolMap`]. +pub trait SymbolMapEntry { + /// The symbol address. + fn address(&self) -> u64; +} + +/// A map from addresses to symbol information. +/// +/// The symbol information depends on the chosen entry type, such as [`SymbolMapName`]. +/// +/// Returned by [`Object::symbol_map`]. +#[derive(Debug, Default, Clone)] +pub struct SymbolMap<T: SymbolMapEntry> { + symbols: Vec<T>, +} + +impl<T: SymbolMapEntry> SymbolMap<T> { + /// Construct a new symbol map. + /// + /// This function will sort the symbols by address. + pub fn new(mut symbols: Vec<T>) -> Self { + symbols.sort_by_key(|s| s.address()); + SymbolMap { symbols } + } + + /// Get the symbol before the given address. + pub fn get(&self, address: u64) -> Option<&T> { + let index = match self + .symbols + .binary_search_by_key(&address, |symbol| symbol.address()) + { + Ok(index) => index, + Err(index) => index.checked_sub(1)?, + }; + self.symbols.get(index) + } + + /// Get all symbols in the map. + #[inline] + pub fn symbols(&self) -> &[T] { + &self.symbols + } +} + +/// The type used for entries in a [`SymbolMap`] that maps from addresses to names. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SymbolMapName<'data> { + address: u64, + name: &'data str, +} + +impl<'data> SymbolMapName<'data> { + /// Construct a `SymbolMapName`. + pub fn new(address: u64, name: &'data str) -> Self { + SymbolMapName { address, name } + } + + /// The symbol address. + #[inline] + pub fn address(&self) -> u64 { + self.address + } + + /// The symbol name. + #[inline] + pub fn name(&self) -> &'data str { + self.name + } +} + +impl<'data> SymbolMapEntry for SymbolMapName<'data> { + #[inline] + fn address(&self) -> u64 { + self.address + } +} + +/// A map from addresses to symbol names and object files. +/// +/// This is derived from STAB entries in Mach-O files. +/// +/// Returned by [`Object::object_map`]. +#[derive(Debug, Default, Clone)] +pub struct ObjectMap<'data> { + symbols: SymbolMap<ObjectMapEntry<'data>>, + objects: Vec<ObjectMapFile<'data>>, +} + +impl<'data> ObjectMap<'data> { + /// Get the entry containing the given address. + pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> { + self.symbols + .get(address) + .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size) + } + + /// Get all symbols in the map. + #[inline] + pub fn symbols(&self) -> &[ObjectMapEntry<'data>] { + self.symbols.symbols() + } + + /// Get all objects in the map. + #[inline] + pub fn objects(&self) -> &[ObjectMapFile<'data>] { + &self.objects + } +} + +/// A symbol in an [`ObjectMap`]. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ObjectMapEntry<'data> { + address: u64, + size: u64, + name: &'data [u8], + object: usize, +} + +impl<'data> ObjectMapEntry<'data> { + /// Get the symbol address. + #[inline] + pub fn address(&self) -> u64 { + self.address + } + + /// Get the symbol size. + /// + /// This may be 0 if the size is unknown. + #[inline] + pub fn size(&self) -> u64 { + self.size + } + + /// Get the symbol name. + #[inline] + pub fn name(&self) -> &'data [u8] { + self.name + } + + /// Get the index of the object file name. + #[inline] + pub fn object_index(&self) -> usize { + self.object + } + + /// Get the object file name. + #[inline] + pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> { + &map.objects[self.object] + } +} + +impl<'data> SymbolMapEntry for ObjectMapEntry<'data> { + #[inline] + fn address(&self) -> u64 { + self.address + } +} + +/// An object file name in an [`ObjectMap`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ObjectMapFile<'data> { + path: &'data [u8], + member: Option<&'data [u8]>, +} + +impl<'data> ObjectMapFile<'data> { + #[cfg(feature = "macho")] + fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self { + ObjectMapFile { path, member } + } + + /// Get the path to the file containing the object. + #[inline] + pub fn path(&self) -> &'data [u8] { + self.path + } + + /// If the file is an archive, get the name of the member containing the object. + #[inline] + pub fn member(&self) -> Option<&'data [u8]> { + self.member + } +} + +/// An imported symbol. +/// +/// Returned by [`Object::imports`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Import<'data> { + library: ByteString<'data>, + // TODO: or ordinal + name: ByteString<'data>, +} + +impl<'data> Import<'data> { + /// The symbol name. + #[inline] + pub fn name(&self) -> &'data [u8] { + self.name.0 + } + + /// The name of the library to import the symbol from. + #[inline] + pub fn library(&self) -> &'data [u8] { + self.library.0 + } +} + +/// An exported symbol. +/// +/// Returned by [`Object::exports`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Export<'data> { + // TODO: and ordinal? + name: ByteString<'data>, + address: u64, +} + +impl<'data> Export<'data> { + /// The symbol name. + #[inline] + pub fn name(&self) -> &'data [u8] { + self.name.0 + } + + /// The virtual address of the symbol. + #[inline] + pub fn address(&self) -> u64 { + self.address + } +} + +/// PDB information from the debug directory in a PE file. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CodeView<'data> { + guid: [u8; 16], + path: ByteString<'data>, + age: u32, +} + +impl<'data> CodeView<'data> { + /// The path to the PDB as stored in CodeView. + #[inline] + pub fn path(&self) -> &'data [u8] { + self.path.0 + } + + /// The age of the PDB. + #[inline] + pub fn age(&self) -> u32 { + self.age + } + + /// The GUID of the PDB. + #[inline] + pub fn guid(&self) -> [u8; 16] { + self.guid + } +} + +/// The target referenced by a [`Relocation`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum RelocationTarget { + /// The target is a symbol. + Symbol(SymbolIndex), + /// The target is a section. + Section(SectionIndex), + /// The offset is an absolute address. + Absolute, +} + +/// A relocation entry. +/// +/// Returned by [`Object::dynamic_relocations`] or [`ObjectSection::relocations`]. +#[derive(Debug)] +pub struct Relocation { + kind: RelocationKind, + encoding: RelocationEncoding, + size: u8, + target: RelocationTarget, + addend: i64, + implicit_addend: bool, + flags: RelocationFlags, +} + +impl Relocation { + /// The operation used to calculate the result of the relocation. + #[inline] + pub fn kind(&self) -> RelocationKind { + self.kind + } + + /// Information about how the result of the relocation operation is encoded in the place. + #[inline] + pub fn encoding(&self) -> RelocationEncoding { + self.encoding + } + + /// The size in bits of the place of the relocation. + /// + /// If 0, then the size is determined by the relocation kind. + #[inline] + pub fn size(&self) -> u8 { + self.size + } + + /// The target of the relocation. + #[inline] + pub fn target(&self) -> RelocationTarget { + self.target + } + + /// The addend to use in the relocation calculation. + #[inline] + pub fn addend(&self) -> i64 { + self.addend + } + + /// Set the addend to use in the relocation calculation. + #[inline] + pub fn set_addend(&mut self, addend: i64) { + self.addend = addend; + } + + /// Returns true if there is an implicit addend stored in the data at the offset + /// to be relocated. + #[inline] + pub fn has_implicit_addend(&self) -> bool { + self.implicit_addend + } + + /// Relocation flags that are specific to each file format. + /// + /// The values returned by `kind`, `encoding` and `size` are derived + /// from these flags. + #[inline] + pub fn flags(&self) -> RelocationFlags { + self.flags + } +} + +/// A map from section offsets to relocation information. +/// +/// This can be used to apply relocations to a value at a given section offset. +/// This is intended for use with DWARF in relocatable object files, and only +/// supports relocations that are used in DWARF. +/// +/// Returned by [`ObjectSection::relocation_map`]. +#[derive(Debug, Default)] +pub struct RelocationMap(Map<u64, RelocationMapEntry>); + +impl RelocationMap { + /// Construct a new relocation map for a section. + /// + /// Fails if any relocation cannot be added to the map. + /// You can manually use `add` if you need different error handling, + /// such as to list all errors or to ignore them. + pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result<Self> + where + T: Object<'data>, + { + let mut map = RelocationMap(Map::new()); + for (offset, relocation) in section.relocations() { + map.add(file, offset, relocation)?; + } + Ok(map) + } + + /// Add a single relocation to the map. + pub fn add<'data: 'file, 'file, T>( + &mut self, + file: &'file T, + offset: u64, + relocation: Relocation, + ) -> Result<()> + where + T: Object<'data>, + { + let mut entry = RelocationMapEntry { + implicit_addend: relocation.has_implicit_addend(), + addend: relocation.addend() as u64, + }; + match relocation.kind() { + RelocationKind::Absolute => match relocation.target() { + RelocationTarget::Symbol(symbol_idx) => { + let symbol = file + .symbol_by_index(symbol_idx) + .read_error("Relocation with invalid symbol")?; + entry.addend = symbol.address().wrapping_add(entry.addend); + } + RelocationTarget::Section(section_idx) => { + let section = file + .section_by_index(section_idx) + .read_error("Relocation with invalid section")?; + // DWARF parsers expect references to DWARF sections to be section offsets, + // not addresses. Addresses are useful for everything else. + if section.kind() != SectionKind::Debug { + entry.addend = section.address().wrapping_add(entry.addend); + } + } + _ => { + return Err(Error("Unsupported relocation target")); + } + }, + _ => { + return Err(Error("Unsupported relocation type")); + } + } + if self.0.insert(offset, entry).is_some() { + return Err(Error("Multiple relocations for offset")); + } + Ok(()) + } + + /// Relocate a value that was read from the section at the given offset. + pub fn relocate(&self, offset: u64, value: u64) -> u64 { + if let Some(relocation) = self.0.get(&offset) { + if relocation.implicit_addend { + // Use the explicit addend too, because it may have the symbol value. + value.wrapping_add(relocation.addend) + } else { + relocation.addend + } + } else { + value + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct RelocationMapEntry { + implicit_addend: bool, + addend: u64, +} + +/// A data compression format. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum CompressionFormat { + /// The data is uncompressed. + None, + /// The data is compressed, but the compression format is unknown. + Unknown, + /// ZLIB/DEFLATE. + /// + /// Used for ELF compression and GNU compressed debug information. + Zlib, + /// Zstandard. + /// + /// Used for ELF compression. + Zstandard, +} + +/// A range in a file that may be compressed. +/// +/// Returned by [`ObjectSection::compressed_file_range`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CompressedFileRange { + /// The data compression format. + pub format: CompressionFormat, + /// The file offset of the compressed data. + pub offset: u64, + /// The compressed data size. + pub compressed_size: u64, + /// The uncompressed data size. + pub uncompressed_size: u64, +} + +impl CompressedFileRange { + /// Data that is uncompressed. + #[inline] + pub fn none(range: Option<(u64, u64)>) -> Self { + if let Some((offset, size)) = range { + CompressedFileRange { + format: CompressionFormat::None, + offset, + compressed_size: size, + uncompressed_size: size, + } + } else { + CompressedFileRange { + format: CompressionFormat::None, + offset: 0, + compressed_size: 0, + uncompressed_size: 0, + } + } + } + + /// Convert to [`CompressedData`] by reading from the file. + pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> { + let data = file + .read_bytes_at(self.offset, self.compressed_size) + .read_error("Invalid compressed data size or offset")?; + Ok(CompressedData { + format: self.format, + data, + uncompressed_size: self.uncompressed_size, + }) + } +} + +/// Data that may be compressed. +/// +/// Returned by [`ObjectSection::compressed_data`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CompressedData<'data> { + /// The data compression format. + pub format: CompressionFormat, + /// The compressed data. + pub data: &'data [u8], + /// The uncompressed data size. + pub uncompressed_size: u64, +} + +impl<'data> CompressedData<'data> { + /// Data that is uncompressed. + #[inline] + pub fn none(data: &'data [u8]) -> Self { + CompressedData { + format: CompressionFormat::None, + data, + uncompressed_size: data.len() as u64, + } + } + + /// Return the uncompressed data. + /// + /// Returns an error for invalid data or unsupported compression. + /// This includes if the data is compressed but the `compression` feature + /// for this crate is disabled. + pub fn decompress(self) -> Result<Cow<'data, [u8]>> { + match self.format { + CompressionFormat::None => Ok(Cow::Borrowed(self.data)), + #[cfg(feature = "compression")] + CompressionFormat::Zlib | CompressionFormat::Zstandard => { + use core::convert::TryInto; + use std::io::Read; + let size = self + .uncompressed_size + .try_into() + .ok() + .read_error("Uncompressed data size is too large.")?; + let mut decompressed = Vec::new(); + decompressed + .try_reserve_exact(size) + .ok() + .read_error("Uncompressed data allocation failed")?; + + match self.format { + CompressionFormat::Zlib => { + let mut decompress = flate2::Decompress::new(true); + decompress + .decompress_vec( + self.data, + &mut decompressed, + flate2::FlushDecompress::Finish, + ) + .ok() + .read_error("Invalid zlib compressed data")?; + } + CompressionFormat::Zstandard => { + let mut input = self.data; + while !input.is_empty() { + let mut decoder = match ruzstd::decoding::StreamingDecoder::new(&mut input) { + Ok(decoder) => decoder, + Err( + ruzstd::decoding::errors::FrameDecoderError::ReadFrameHeaderError( + ruzstd::decoding::errors::ReadFrameHeaderError::SkipFrame { + length, + .. + }, + ), + ) => { + input = input + .get(length as usize..) + .read_error("Invalid zstd compressed data")?; + continue; + } + x => x.ok().read_error("Invalid zstd compressed data")?, + }; + decoder + .read_to_end(&mut decompressed) + .ok() + .read_error("Invalid zstd compressed data")?; + } + } + _ => unreachable!(), + } + if size != decompressed.len() { + return Err(Error( + "Uncompressed data size does not match compression header", + )); + } + + Ok(Cow::Owned(decompressed)) + } + _ => Err(Error("Unsupported compressed data.")), + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/data_directory.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/data_directory.rs new file mode 100644 index 0000000..be7a00f --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/data_directory.rs
@@ -0,0 +1,214 @@ +use core::slice; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::read::{Error, ReadError, ReadRef, Result}; + +use super::{ + DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, + SectionTable, +}; + +/// The table of data directories in a PE file. +/// +/// Returned by [`ImageNtHeaders::parse`](super::ImageNtHeaders::parse). +#[derive(Debug, Clone, Copy)] +pub struct DataDirectories<'data> { + entries: &'data [pe::ImageDataDirectory], +} + +impl<'data> DataDirectories<'data> { + /// Parse the data directory table. + /// + /// `data` must be the remaining optional data following the + /// [optional header](pe::ImageOptionalHeader64). `number` must be from the + /// [`number_of_rva_and_sizes`](pe::ImageOptionalHeader64::number_of_rva_and_sizes) + /// field of the optional header. + pub fn parse(data: &'data [u8], number: u32) -> Result<Self> { + let entries = data + .read_slice_at(0, number as usize) + .read_error("Invalid PE number of RVA and sizes")?; + Ok(DataDirectories { entries }) + } + + /// The number of data directories. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.entries.len() + } + + /// Iterator over the data directories. + pub fn iter(&self) -> slice::Iter<'data, pe::ImageDataDirectory> { + self.entries.iter() + } + + /// Iterator which gives the directories as well as their index (one of the IMAGE_DIRECTORY_ENTRY_* constants). + pub fn enumerate(&self) -> core::iter::Enumerate<slice::Iter<'data, pe::ImageDataDirectory>> { + self.entries.iter().enumerate() + } + + /// Returns the data directory at the given index. + /// + /// Index should be one of the `IMAGE_DIRECTORY_ENTRY_*` constants. + /// + /// Returns `None` if the index is larger than the table size, + /// or if the entry at the index has a zero virtual address. + pub fn get(&self, index: usize) -> Option<&'data pe::ImageDataDirectory> { + self.entries + .get(index) + .filter(|d| d.virtual_address.get(LE) != 0) + } + + /// Returns the unparsed export directory. + /// + /// `data` must be the entire file data. + pub fn export_directory<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<&'data pe::ImageExportDirectory>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let export_data = data_dir.data(data, sections)?; + ExportTable::parse_directory(export_data).map(Some) + } + + /// Returns the partially parsed export directory. + /// + /// `data` must be the entire file data. + pub fn export_table<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<ExportTable<'data>>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let export_va = data_dir.virtual_address.get(LE); + let export_data = data_dir.data(data, sections)?; + ExportTable::parse(export_data, export_va).map(Some) + } + + /// Returns the partially parsed import directory. + /// + /// `data` must be the entire file data. + pub fn import_table<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<ImportTable<'data>>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_IMPORT) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let import_va = data_dir.virtual_address.get(LE); + let (section_data, section_va) = sections + .pe_data_containing(data, import_va) + .read_error("Invalid import data dir virtual address")?; + Ok(Some(ImportTable::new(section_data, section_va, import_va))) + } + + /// Returns the partially parsed delay-load import directory. + /// + /// `data` must be the entire file data. + pub fn delay_load_import_table<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<DelayLoadImportTable<'data>>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let import_va = data_dir.virtual_address.get(LE); + let (section_data, section_va) = sections + .pe_data_containing(data, import_va) + .read_error("Invalid import data dir virtual address")?; + Ok(Some(DelayLoadImportTable::new( + section_data, + section_va, + import_va, + ))) + } + + /// Returns the blocks in the base relocation directory. + /// + /// `data` must be the entire file data. + pub fn relocation_blocks<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<RelocationBlockIterator<'data>>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let reloc_data = data_dir.data(data, sections)?; + Ok(Some(RelocationBlockIterator::new(reloc_data))) + } + + /// Returns the resource directory. + /// + /// `data` must be the entire file data. + pub fn resource_directory<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<ResourceDirectory<'data>>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let rsrc_data = data_dir.data(data, sections)?; + Ok(Some(ResourceDirectory::new(rsrc_data))) + } +} + +impl pe::ImageDataDirectory { + /// Return the virtual address range of this directory entry. + pub fn address_range(&self) -> (u32, u32) { + (self.virtual_address.get(LE), self.size.get(LE)) + } + + /// Return the file offset and size of this directory entry. + /// + /// This function has some limitations: + /// - It requires that the data is contained in a single section. + /// - It uses the size field of the directory entry, which is + /// not desirable for all data directories. + /// - It uses the `virtual_address` of the directory entry as an address, + /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`. + pub fn file_range(&self, sections: &SectionTable<'_>) -> Result<(u32, u32)> { + let (offset, section_size) = sections + .pe_file_range_at(self.virtual_address.get(LE)) + .read_error("Invalid data dir virtual address")?; + let size = self.size.get(LE); + if size > section_size { + return Err(Error("Invalid data dir size")); + } + Ok((offset, size)) + } + + /// Get the data referenced by this directory entry. + /// + /// This function has some limitations: + /// - It requires that the data is contained in a single section. + /// - It uses the size field of the directory entry, which is + /// not desirable for all data directories. + /// - It uses the `virtual_address` of the directory entry as an address, + /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`. + pub fn data<'data, R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<&'data [u8]> { + sections + .pe_data_at(data, self.virtual_address.get(LE)) + .read_error("Invalid data dir virtual address")? + .get(..self.size.get(LE) as usize) + .read_error("Invalid data dir size") + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/export.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/export.rs new file mode 100644 index 0000000..d66d406 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/export.rs
@@ -0,0 +1,334 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes}; +use crate::pe; +use crate::read::{ByteString, Bytes, Error, ReadError, ReadRef, Result}; + +/// Where an export is pointing to. +#[derive(Clone, Copy)] +pub enum ExportTarget<'data> { + /// The address of the export, relative to the image base. + Address(u32), + /// Forwarded to an export ordinal in another DLL. + /// + /// This gives the name of the DLL, and the ordinal. + ForwardByOrdinal(&'data [u8], u32), + /// Forwarded to an export name in another DLL. + /// + /// This gives the name of the DLL, and the export name. + ForwardByName(&'data [u8], &'data [u8]), +} + +impl<'data> ExportTarget<'data> { + /// Returns true if the target is an address. + pub fn is_address(&self) -> bool { + match self { + ExportTarget::Address(_) => true, + _ => false, + } + } + + /// Returns true if the export is forwarded to another DLL. + pub fn is_forward(&self) -> bool { + !self.is_address() + } +} + +/// An export from a PE file. +/// +/// There are multiple kinds of PE exports (with or without a name, and local or forwarded). +#[derive(Clone, Copy)] +pub struct Export<'data> { + /// The ordinal of the export. + /// + /// These are sequential, starting at a base specified in the DLL. + pub ordinal: u32, + /// The name of the export, if known. + pub name: Option<&'data [u8]>, + /// The target of this export. + pub target: ExportTarget<'data>, +} + +impl<'a> Debug for Export<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { + f.debug_struct("Export") + .field("ordinal", &self.ordinal) + .field("name", &self.name.map(ByteString)) + .field("target", &self.target) + .finish() + } +} + +impl<'a> Debug for ExportTarget<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { + match self { + ExportTarget::Address(address) => write!(f, "Address({:#x})", address), + ExportTarget::ForwardByOrdinal(library, ordinal) => write!( + f, + "ForwardByOrdinal({:?}.#{})", + ByteString(library), + ordinal + ), + ExportTarget::ForwardByName(library, name) => write!( + f, + "ForwardByName({:?}.{:?})", + ByteString(library), + ByteString(name) + ), + } + } +} + +/// A partially parsed PE export table. +/// +/// Returned by [`DataDirectories::export_table`](super::DataDirectories::export_table). +#[derive(Debug, Clone)] +pub struct ExportTable<'data> { + data: Bytes<'data>, + virtual_address: u32, + directory: &'data pe::ImageExportDirectory, + addresses: &'data [U32Bytes<LE>], + names: &'data [U32Bytes<LE>], + name_ordinals: &'data [U16Bytes<LE>], +} + +impl<'data> ExportTable<'data> { + /// Parse the export table given its section data and address. + pub fn parse(data: &'data [u8], virtual_address: u32) -> Result<Self> { + let directory = Self::parse_directory(data)?; + let data = Bytes(data); + + let mut addresses = &[][..]; + let address_of_functions = directory.address_of_functions.get(LE); + if address_of_functions != 0 { + addresses = data + .read_slice_at::<U32Bytes<_>>( + address_of_functions.wrapping_sub(virtual_address) as usize, + directory.number_of_functions.get(LE) as usize, + ) + .read_error("Invalid PE export address table")?; + } + + let mut names = &[][..]; + let mut name_ordinals = &[][..]; + let address_of_names = directory.address_of_names.get(LE); + let address_of_name_ordinals = directory.address_of_name_ordinals.get(LE); + if address_of_names != 0 { + if address_of_name_ordinals == 0 { + return Err(Error("Missing PE export ordinal table")); + } + + let number = directory.number_of_names.get(LE) as usize; + names = data + .read_slice_at::<U32Bytes<_>>( + address_of_names.wrapping_sub(virtual_address) as usize, + number, + ) + .read_error("Invalid PE export name pointer table")?; + name_ordinals = data + .read_slice_at::<U16Bytes<_>>( + address_of_name_ordinals.wrapping_sub(virtual_address) as usize, + number, + ) + .read_error("Invalid PE export ordinal table")?; + } + + Ok(ExportTable { + data, + virtual_address, + directory, + addresses, + names, + name_ordinals, + }) + } + + /// Parse the export directory given its section data. + pub fn parse_directory(data: &'data [u8]) -> Result<&'data pe::ImageExportDirectory> { + data.read_at::<pe::ImageExportDirectory>(0) + .read_error("Invalid PE export dir size") + } + + /// Returns the header of the export table. + pub fn directory(&self) -> &'data pe::ImageExportDirectory { + self.directory + } + + /// Returns the base value of ordinals. + /// + /// Adding this to an address index will give an ordinal. + pub fn ordinal_base(&self) -> u32 { + self.directory.base.get(LE) + } + + /// Returns the unparsed address table. + /// + /// An address table entry may be a local address, or the address of a forwarded export entry. + /// See [`Self::is_forward`] and [`Self::target_from_address`]. + pub fn addresses(&self) -> &'data [U32Bytes<LE>] { + self.addresses + } + + /// Returns the unparsed name pointer table. + /// + /// A name pointer table entry can be used with [`Self::name_from_pointer`]. + pub fn name_pointers(&self) -> &'data [U32Bytes<LE>] { + self.names + } + + /// Returns the unparsed ordinal table. + /// + /// An ordinal table entry is a 0-based index into the address table. + /// See [`Self::address_by_index`] and [`Self::target_by_index`]. + pub fn name_ordinals(&self) -> &'data [U16Bytes<LE>] { + self.name_ordinals + } + + /// Returns an iterator for the entries in the name pointer table and ordinal table. + /// + /// A name pointer table entry can be used with [`Self::name_from_pointer`]. + /// + /// An ordinal table entry is a 0-based index into the address table. + /// See [`Self::address_by_index`] and [`Self::target_by_index`]. + pub fn name_iter(&self) -> impl Iterator<Item = (u32, u16)> + 'data { + self.names + .iter() + .map(|x| x.get(LE)) + .zip(self.name_ordinals.iter().map(|x| x.get(LE))) + } + + /// Returns the export address table entry at the given address index. + /// + /// This may be a local address, or the address of a forwarded export entry. + /// See [`Self::is_forward`] and [`Self::target_from_address`]. + /// + /// `index` is a 0-based index into the export address table. + pub fn address_by_index(&self, index: u32) -> Result<u32> { + Ok(self + .addresses + .get(index as usize) + .read_error("Invalid PE export address index")? + .get(LE)) + } + + /// Returns the export address table entry at the given ordinal. + /// + /// This may be a local address, or the address of a forwarded export entry. + /// See [`Self::is_forward`] and [`Self::target_from_address`]. + pub fn address_by_ordinal(&self, ordinal: u32) -> Result<u32> { + self.address_by_index(ordinal.wrapping_sub(self.ordinal_base())) + } + + /// Returns the target of the export at the given address index. + /// + /// `index` is a 0-based index into the export address table. + pub fn target_by_index(&self, index: u32) -> Result<ExportTarget<'data>> { + self.target_from_address(self.address_by_index(index)?) + } + + /// Returns the target of the export at the given ordinal. + pub fn target_by_ordinal(&self, ordinal: u32) -> Result<ExportTarget<'data>> { + self.target_from_address(self.address_by_ordinal(ordinal)?) + } + + /// Convert an export address table entry into a target. + pub fn target_from_address(&self, address: u32) -> Result<ExportTarget<'data>> { + Ok(if let Some(forward) = self.forward_string(address)? { + let i = forward + .iter() + .position(|x| *x == b'.') + .read_error("Missing PE forwarded export separator")?; + let library = &forward[..i]; + match &forward[i + 1..] { + [b'#', digits @ ..] => { + let ordinal = + parse_ordinal(digits).read_error("Invalid PE forwarded export ordinal")?; + ExportTarget::ForwardByOrdinal(library, ordinal) + } + [] => { + return Err(Error("Missing PE forwarded export name")); + } + name => ExportTarget::ForwardByName(library, name), + } + } else { + ExportTarget::Address(address) + }) + } + + fn forward_offset(&self, address: u32) -> Option<usize> { + let offset = address.wrapping_sub(self.virtual_address) as usize; + if offset < self.data.len() { + Some(offset) + } else { + None + } + } + + /// Return true if the export address table entry is a forward. + pub fn is_forward(&self, address: u32) -> bool { + self.forward_offset(address).is_some() + } + + /// Return the forward string if the export address table entry is a forward. + pub fn forward_string(&self, address: u32) -> Result<Option<&'data [u8]>> { + if let Some(offset) = self.forward_offset(address) { + self.data + .read_string_at(offset) + .read_error("Invalid PE forwarded export address") + .map(Some) + } else { + Ok(None) + } + } + + /// Convert an export name pointer table entry into a name. + pub fn name_from_pointer(&self, name_pointer: u32) -> Result<&'data [u8]> { + let offset = name_pointer.wrapping_sub(self.virtual_address); + self.data + .read_string_at(offset as usize) + .read_error("Invalid PE export name pointer") + } + + /// Returns the parsed exports in this table. + pub fn exports(&self) -> Result<Vec<Export<'data>>> { + // First, let's list all exports. + let mut exports = Vec::new(); + let ordinal_base = self.ordinal_base(); + for (i, address) in self.addresses.iter().enumerate() { + // Convert from an array index to an ordinal. + let ordinal = ordinal_base.wrapping_add(i as u32); + let target = self.target_from_address(address.get(LE))?; + exports.push(Export { + ordinal, + target, + // Might be populated later. + name: None, + }); + } + + // Now, check whether some (or all) of them have an associated name. + // `ordinal_index` is a 0-based index into `addresses`. + for (name_pointer, ordinal_index) in self.name_iter() { + let name = self.name_from_pointer(name_pointer)?; + exports + .get_mut(ordinal_index as usize) + .read_error("Invalid PE export ordinal")? + .name = Some(name); + } + + Ok(exports) + } +} + +fn parse_ordinal(digits: &[u8]) -> Option<u32> { + if digits.is_empty() { + return None; + } + let mut result: u32 = 0; + for &c in digits { + let x = (c as char).to_digit(10)?; + result = result.checked_mul(10)?.checked_add(x)?; + } + Some(result) +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/file.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/file.rs new file mode 100644 index 0000000..d0ab409 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/file.rs
@@ -0,0 +1,1073 @@ +use alloc::vec::Vec; +use core::fmt::Debug; +use core::{mem, str}; + +use core::convert::TryInto; + +use crate::endian::{LittleEndian as LE, U32}; +use crate::pe; +use crate::pod::{self, Pod}; +use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable}; +use crate::read::{ + self, Architecture, ByteString, Bytes, CodeView, ComdatKind, Error, Export, FileFlags, Import, + NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ReadError, ReadRef, Result, + SectionIndex, SubArchitecture, SymbolIndex, +}; + +use super::{ + DataDirectories, ExportTable, ImageThunkData, ImportTable, PeSection, PeSectionIterator, + PeSegment, PeSegmentIterator, RichHeaderInfo, SectionTable, +}; + +/// A PE32 (32-bit) image file. +/// +/// This is a file that starts with [`pe::ImageNtHeaders32`], and corresponds +/// to [`crate::FileKind::Pe32`]. +pub type PeFile32<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders32, R>; +/// A PE32+ (64-bit) image file. +/// +/// This is a file that starts with [`pe::ImageNtHeaders64`], and corresponds +/// to [`crate::FileKind::Pe64`]. +pub type PeFile64<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders64, R>; + +/// A PE image file. +/// +/// Most functionality is provided by the [`Object`] trait implementation. +#[derive(Debug)] +pub struct PeFile<'data, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + pub(super) dos_header: &'data pe::ImageDosHeader, + pub(super) nt_headers: &'data Pe, + pub(super) data_directories: DataDirectories<'data>, + pub(super) common: CoffCommon<'data, R>, + pub(super) data: R, +} + +impl<'data, Pe, R> PeFile<'data, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + /// Parse the raw PE file data. + pub fn parse(data: R) -> Result<Self> { + let dos_header = pe::ImageDosHeader::parse(data)?; + let mut offset = dos_header.nt_headers_offset().into(); + let (nt_headers, data_directories) = Pe::parse(data, &mut offset)?; + let sections = nt_headers.sections(data, offset)?; + let coff_symbols = nt_headers.symbols(data); + let image_base = nt_headers.optional_header().image_base(); + + Ok(PeFile { + dos_header, + nt_headers, + data_directories, + common: CoffCommon { + sections, + // The PE file format deprecates the COFF symbol table (https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image) + // We do not want to prevent parsing the rest of the PE file for a corrupt COFF header, but rather return an empty symbol table + symbols: coff_symbols.unwrap_or_default(), + image_base, + }, + data, + }) + } + + /// Returns this binary data. + pub fn data(&self) -> R { + self.data + } + + /// Return the DOS header of this file. + pub fn dos_header(&self) -> &'data pe::ImageDosHeader { + self.dos_header + } + + /// Return the NT Headers of this file. + pub fn nt_headers(&self) -> &'data Pe { + self.nt_headers + } + + /// Returns information about the rich header of this file (if any). + pub fn rich_header_info(&self) -> Option<RichHeaderInfo<'_>> { + RichHeaderInfo::parse(self.data, self.dos_header.nt_headers_offset().into()) + } + + /// Returns the section table of this binary. + pub fn section_table(&self) -> SectionTable<'data> { + self.common.sections + } + + /// Returns the data directories of this file. + pub fn data_directories(&self) -> DataDirectories<'data> { + self.data_directories + } + + /// Returns the data directory at the given index. + pub fn data_directory(&self, id: usize) -> Option<&'data pe::ImageDataDirectory> { + self.data_directories.get(id) + } + + /// Returns the export table of this file. + /// + /// The export table is located using the data directory. + pub fn export_table(&self) -> Result<Option<ExportTable<'data>>> { + self.data_directories + .export_table(self.data, &self.common.sections) + } + + /// Returns the import table of this file. + /// + /// The import table is located using the data directory. + pub fn import_table(&self) -> Result<Option<ImportTable<'data>>> { + self.data_directories + .import_table(self.data, &self.common.sections) + } + + pub(super) fn section_alignment(&self) -> u64 { + u64::from(self.nt_headers.optional_header().section_alignment()) + } +} + +impl<'data, Pe, R> read::private::Sealed for PeFile<'data, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ +} + +impl<'data, Pe, R> Object<'data> for PeFile<'data, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + type Segment<'file> + = PeSegment<'data, 'file, Pe, R> + where + Self: 'file, + 'data: 'file; + type SegmentIterator<'file> + = PeSegmentIterator<'data, 'file, Pe, R> + where + Self: 'file, + 'data: 'file; + type Section<'file> + = PeSection<'data, 'file, Pe, R> + where + Self: 'file, + 'data: 'file; + type SectionIterator<'file> + = PeSectionIterator<'data, 'file, Pe, R> + where + Self: 'file, + 'data: 'file; + type Comdat<'file> + = PeComdat<'data, 'file, Pe, R> + where + Self: 'file, + 'data: 'file; + type ComdatIterator<'file> + = PeComdatIterator<'data, 'file, Pe, R> + where + Self: 'file, + 'data: 'file; + type Symbol<'file> + = CoffSymbol<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SymbolIterator<'file> + = CoffSymbolIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SymbolTable<'file> + = CoffSymbolTable<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type DynamicRelocationIterator<'file> + = NoDynamicRelocationIterator + where + Self: 'file, + 'data: 'file; + + fn architecture(&self) -> Architecture { + match self.nt_headers.file_header().machine.get(LE) { + pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, + pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64, + pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, + pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, + _ => Architecture::Unknown, + } + } + + fn sub_architecture(&self) -> Option<SubArchitecture> { + match self.nt_headers.file_header().machine.get(LE) { + pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC), + _ => None, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + // Only little endian is supported. + true + } + + #[inline] + fn is_64(&self) -> bool { + self.nt_headers.is_type_64() + } + + fn kind(&self) -> ObjectKind { + let characteristics = self.nt_headers.file_header().characteristics.get(LE); + if characteristics & pe::IMAGE_FILE_DLL != 0 { + ObjectKind::Dynamic + } else if characteristics & pe::IMAGE_FILE_SYSTEM != 0 { + ObjectKind::Unknown + } else { + ObjectKind::Executable + } + } + + fn segments(&self) -> PeSegmentIterator<'data, '_, Pe, R> { + PeSegmentIterator { + file: self, + iter: self.common.sections.iter(), + } + } + + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<PeSection<'data, 'file, Pe, R>> { + self.common + .sections + .section_by_name(self.common.symbols.strings(), section_name) + .map(|(index, section)| PeSection { + file: self, + index, + section, + }) + } + + fn section_by_index(&self, index: SectionIndex) -> Result<PeSection<'data, '_, Pe, R>> { + let section = self.common.sections.section(index)?; + Ok(PeSection { + file: self, + index, + section, + }) + } + + fn sections(&self) -> PeSectionIterator<'data, '_, Pe, R> { + PeSectionIterator { + file: self, + iter: self.common.sections.iter().enumerate(), + } + } + + fn comdats(&self) -> PeComdatIterator<'data, '_, Pe, R> { + PeComdatIterator { file: self } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<CoffSymbol<'data, '_, R>> { + let symbol = self.common.symbols.symbol(index)?; + Ok(CoffSymbol { + file: &self.common, + index, + symbol, + }) + } + + fn symbols(&self) -> CoffSymbolIterator<'data, '_, R> { + CoffSymbolIterator::new(&self.common) + } + + fn symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R>> { + Some(CoffSymbolTable { file: &self.common }) + } + + fn dynamic_symbols(&self) -> CoffSymbolIterator<'data, '_, R> { + CoffSymbolIterator::empty(&self.common) + } + + fn dynamic_symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R>> { + None + } + + fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> { + None + } + + fn imports(&self) -> Result<Vec<Import<'data>>> { + let mut imports = Vec::new(); + if let Some(import_table) = self.import_table()? { + let mut import_descs = import_table.descriptors()?; + while let Some(import_desc) = import_descs.next()? { + let library = import_table.name(import_desc.name.get(LE))?; + let mut first_thunk = import_desc.original_first_thunk.get(LE); + if first_thunk == 0 { + first_thunk = import_desc.first_thunk.get(LE); + } + let mut thunks = import_table.thunks(first_thunk)?; + while let Some(thunk) = thunks.next::<Pe>()? { + if !thunk.is_ordinal() { + let (_hint, name) = import_table.hint_name(thunk.address())?; + imports.push(Import { + library: ByteString(library), + name: ByteString(name), + }); + } + } + } + } + Ok(imports) + } + + fn exports(&self) -> Result<Vec<Export<'data>>> { + let mut exports = Vec::new(); + if let Some(export_table) = self.export_table()? { + for (name_pointer, address_index) in export_table.name_iter() { + let name = export_table.name_from_pointer(name_pointer)?; + let address = export_table.address_by_index(address_index.into())?; + if !export_table.is_forward(address) { + exports.push(Export { + name: ByteString(name), + address: self.common.image_base.wrapping_add(address.into()), + }) + } + } + } + Ok(exports) + } + + fn pdb_info(&self) -> Result<Option<CodeView<'_>>> { + let data_dir = match self.data_directory(pe::IMAGE_DIRECTORY_ENTRY_DEBUG) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let debug_data = data_dir.data(self.data, &self.common.sections)?; + let debug_dirs = pod::slice_from_all_bytes::<pe::ImageDebugDirectory>(debug_data) + .read_error("Invalid PE debug dir size")?; + + for debug_dir in debug_dirs { + if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW { + continue; + } + + let info = self + .data + .read_slice_at::<u8>( + debug_dir.pointer_to_raw_data.get(LE) as u64, + debug_dir.size_of_data.get(LE) as usize, + ) + .read_error("Invalid CodeView Info address")?; + + let mut info = Bytes(info); + + let sig = info + .read_bytes(4) + .read_error("Invalid CodeView signature")?; + if sig.0 != b"RSDS" { + continue; + } + + let guid: [u8; 16] = info + .read_bytes(16) + .read_error("Invalid CodeView GUID")? + .0 + .try_into() + .unwrap(); + + let age = info.read::<U32<LE>>().read_error("Invalid CodeView Age")?; + + let path = info + .read_string() + .read_error("Invalid CodeView file path")?; + + return Ok(Some(CodeView { + path: ByteString(path), + guid, + age: age.get(LE), + })); + } + Ok(None) + } + + fn has_debug_symbols(&self) -> bool { + self.section_by_name(".debug_info").is_some() + } + + fn relative_address_base(&self) -> u64 { + self.common.image_base + } + + fn entry(&self) -> u64 { + u64::from(self.nt_headers.optional_header().address_of_entry_point()) + .wrapping_add(self.common.image_base) + } + + fn flags(&self) -> FileFlags { + FileFlags::Coff { + characteristics: self.nt_headers.file_header().characteristics.get(LE), + } + } +} + +/// An iterator for the COMDAT section groups in a [`PeFile32`]. +pub type PeComdatIterator32<'data, 'file, R = &'data [u8]> = + PeComdatIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the COMDAT section groups in a [`PeFile64`]. +pub type PeComdatIterator64<'data, 'file, R = &'data [u8]> = + PeComdatIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the COMDAT section groups in a [`PeFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeComdatIterator<'data, 'file, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file PeFile<'data, Pe, R>, +} + +impl<'data, 'file, Pe, R> Iterator for PeComdatIterator<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + type Item = PeComdat<'data, 'file, Pe, R>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A COMDAT section group in a [`PeFile32`]. +pub type PeComdat32<'data, 'file, R = &'data [u8]> = + PeComdat<'data, 'file, pe::ImageNtHeaders32, R>; +/// A COMDAT section group in a [`PeFile64`]. +pub type PeComdat64<'data, 'file, R = &'data [u8]> = + PeComdat<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A COMDAT section group in a [`PeFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeComdat<'data, 'file, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file PeFile<'data, Pe, R>, +} + +impl<'data, 'file, Pe, R> read::private::Sealed for PeComdat<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Pe, R> ObjectComdat<'data> for PeComdat<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + type SectionIterator = PeComdatSectionIterator<'data, 'file, Pe, R>; + + #[inline] + fn kind(&self) -> ComdatKind { + unreachable!(); + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + unreachable!(); + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&'data str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator for the sections in a COMDAT section group in a [`PeFile32`]. +pub type PeComdatSectionIterator32<'data, 'file, R = &'data [u8]> = + PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the sections in a COMDAT section group in a [`PeFile64`]. +pub type PeComdatSectionIterator64<'data, 'file, R = &'data [u8]> = + PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the sections in a COMDAT section group in a [`PeFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeComdatSectionIterator<'data, 'file, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file PeFile<'data, Pe, R>, +} + +impl<'data, 'file, Pe, R> Iterator for PeComdatSectionIterator<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +impl pe::ImageDosHeader { + /// Read the DOS header. + /// + /// Also checks that the `e_magic` field in the header is valid. + pub fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> { + // DOS header comes first. + let dos_header = data + .read_at::<pe::ImageDosHeader>(0) + .read_error("Invalid DOS header size or alignment")?; + if dos_header.e_magic.get(LE) != pe::IMAGE_DOS_SIGNATURE { + return Err(Error("Invalid DOS magic")); + } + Ok(dos_header) + } + + /// Return the file offset of the nt_headers. + #[inline] + pub fn nt_headers_offset(&self) -> u32 { + self.e_lfanew.get(LE) + } +} + +/// Find the optional header and read its `magic` field. +/// +/// It can be useful to know this magic value before trying to +/// fully parse the NT headers. +pub fn optional_header_magic<'data, R: ReadRef<'data>>(data: R) -> Result<u16> { + let dos_header = pe::ImageDosHeader::parse(data)?; + // NT headers are at an offset specified in the DOS header. + let offset = dos_header.nt_headers_offset().into(); + // It doesn't matter which NT header type is used for the purpose + // of reading the optional header magic. + let nt_headers = data + .read_at::<pe::ImageNtHeaders32>(offset) + .read_error("Invalid NT headers offset, size, or alignment")?; + if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE { + return Err(Error("Invalid PE magic")); + } + Ok(nt_headers.optional_header().magic()) +} + +/// A trait for generic access to [`pe::ImageNtHeaders32`] and [`pe::ImageNtHeaders64`]. +#[allow(missing_docs)] +pub trait ImageNtHeaders: Debug + Pod { + type ImageOptionalHeader: ImageOptionalHeader; + type ImageThunkData: ImageThunkData; + + /// Return true if this type is a 64-bit header. + /// + /// This is a property of the type, not a value in the header data. + fn is_type_64(&self) -> bool; + + /// Return true if the magic field in the optional header is valid. + fn is_valid_optional_magic(&self) -> bool; + + /// Return the signature + fn signature(&self) -> u32; + + /// Return the file header. + fn file_header(&self) -> &pe::ImageFileHeader; + + /// Return the optional header. + fn optional_header(&self) -> &Self::ImageOptionalHeader; + + // Provided methods. + + /// Read the NT headers, including the data directories. + /// + /// `data` must be for the entire file. + /// + /// `offset` must be headers offset, which can be obtained from [`pe::ImageDosHeader::nt_headers_offset`]. + /// It is updated to point after the optional header, which is where the section headers are located. + /// + /// Also checks that the `signature` and `magic` fields in the headers are valid. + fn parse<'data, R: ReadRef<'data>>( + data: R, + offset: &mut u64, + ) -> read::Result<(&'data Self, DataDirectories<'data>)> { + // Note that this does not include the data directories in the optional header. + let nt_headers = data + .read::<Self>(offset) + .read_error("Invalid PE headers offset or size")?; + if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE { + return Err(Error("Invalid PE magic")); + } + if !nt_headers.is_valid_optional_magic() { + return Err(Error("Invalid PE optional header magic")); + } + + // Read the rest of the optional header, and then read the data directories from that. + let optional_data_size = + u64::from(nt_headers.file_header().size_of_optional_header.get(LE)) + .checked_sub(mem::size_of::<Self::ImageOptionalHeader>() as u64) + .read_error("PE optional header size is too small")?; + let optional_data = data + .read_bytes(offset, optional_data_size) + .read_error("Invalid PE optional header size")?; + let data_directories = DataDirectories::parse( + optional_data, + nt_headers.optional_header().number_of_rva_and_sizes(), + )?; + + Ok((nt_headers, data_directories)) + } + + /// Read the section table. + /// + /// `data` must be for the entire file. + /// `offset` must be after the optional file header. + #[inline] + fn sections<'data, R: ReadRef<'data>>( + &self, + data: R, + offset: u64, + ) -> read::Result<SectionTable<'data>> { + SectionTable::parse(self.file_header(), data, offset) + } + + /// Read the COFF symbol table and string table. + /// + /// `data` must be the entire file data. + #[inline] + fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<SymbolTable<'data, R>> { + SymbolTable::parse(self.file_header(), data) + } +} + +/// A trait for generic access to [`pe::ImageOptionalHeader32`] and [`pe::ImageOptionalHeader64`]. +#[allow(missing_docs)] +pub trait ImageOptionalHeader: Debug + Pod { + // Standard fields. + fn magic(&self) -> u16; + fn major_linker_version(&self) -> u8; + fn minor_linker_version(&self) -> u8; + fn size_of_code(&self) -> u32; + fn size_of_initialized_data(&self) -> u32; + fn size_of_uninitialized_data(&self) -> u32; + fn address_of_entry_point(&self) -> u32; + fn base_of_code(&self) -> u32; + fn base_of_data(&self) -> Option<u32>; + + // NT additional fields. + fn image_base(&self) -> u64; + fn section_alignment(&self) -> u32; + fn file_alignment(&self) -> u32; + fn major_operating_system_version(&self) -> u16; + fn minor_operating_system_version(&self) -> u16; + fn major_image_version(&self) -> u16; + fn minor_image_version(&self) -> u16; + fn major_subsystem_version(&self) -> u16; + fn minor_subsystem_version(&self) -> u16; + fn win32_version_value(&self) -> u32; + fn size_of_image(&self) -> u32; + fn size_of_headers(&self) -> u32; + fn check_sum(&self) -> u32; + fn subsystem(&self) -> u16; + fn dll_characteristics(&self) -> u16; + fn size_of_stack_reserve(&self) -> u64; + fn size_of_stack_commit(&self) -> u64; + fn size_of_heap_reserve(&self) -> u64; + fn size_of_heap_commit(&self) -> u64; + fn loader_flags(&self) -> u32; + fn number_of_rva_and_sizes(&self) -> u32; +} + +impl ImageNtHeaders for pe::ImageNtHeaders32 { + type ImageOptionalHeader = pe::ImageOptionalHeader32; + type ImageThunkData = pe::ImageThunkData32; + + #[inline] + fn is_type_64(&self) -> bool { + false + } + + #[inline] + fn is_valid_optional_magic(&self) -> bool { + self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC + } + + #[inline] + fn signature(&self) -> u32 { + self.signature.get(LE) + } + + #[inline] + fn file_header(&self) -> &pe::ImageFileHeader { + &self.file_header + } + + #[inline] + fn optional_header(&self) -> &Self::ImageOptionalHeader { + &self.optional_header + } +} + +impl ImageOptionalHeader for pe::ImageOptionalHeader32 { + #[inline] + fn magic(&self) -> u16 { + self.magic.get(LE) + } + + #[inline] + fn major_linker_version(&self) -> u8 { + self.major_linker_version + } + + #[inline] + fn minor_linker_version(&self) -> u8 { + self.minor_linker_version + } + + #[inline] + fn size_of_code(&self) -> u32 { + self.size_of_code.get(LE) + } + + #[inline] + fn size_of_initialized_data(&self) -> u32 { + self.size_of_initialized_data.get(LE) + } + + #[inline] + fn size_of_uninitialized_data(&self) -> u32 { + self.size_of_uninitialized_data.get(LE) + } + + #[inline] + fn address_of_entry_point(&self) -> u32 { + self.address_of_entry_point.get(LE) + } + + #[inline] + fn base_of_code(&self) -> u32 { + self.base_of_code.get(LE) + } + + #[inline] + fn base_of_data(&self) -> Option<u32> { + Some(self.base_of_data.get(LE)) + } + + #[inline] + fn image_base(&self) -> u64 { + self.image_base.get(LE).into() + } + + #[inline] + fn section_alignment(&self) -> u32 { + self.section_alignment.get(LE) + } + + #[inline] + fn file_alignment(&self) -> u32 { + self.file_alignment.get(LE) + } + + #[inline] + fn major_operating_system_version(&self) -> u16 { + self.major_operating_system_version.get(LE) + } + + #[inline] + fn minor_operating_system_version(&self) -> u16 { + self.minor_operating_system_version.get(LE) + } + + #[inline] + fn major_image_version(&self) -> u16 { + self.major_image_version.get(LE) + } + + #[inline] + fn minor_image_version(&self) -> u16 { + self.minor_image_version.get(LE) + } + + #[inline] + fn major_subsystem_version(&self) -> u16 { + self.major_subsystem_version.get(LE) + } + + #[inline] + fn minor_subsystem_version(&self) -> u16 { + self.minor_subsystem_version.get(LE) + } + + #[inline] + fn win32_version_value(&self) -> u32 { + self.win32_version_value.get(LE) + } + + #[inline] + fn size_of_image(&self) -> u32 { + self.size_of_image.get(LE) + } + + #[inline] + fn size_of_headers(&self) -> u32 { + self.size_of_headers.get(LE) + } + + #[inline] + fn check_sum(&self) -> u32 { + self.check_sum.get(LE) + } + + #[inline] + fn subsystem(&self) -> u16 { + self.subsystem.get(LE) + } + + #[inline] + fn dll_characteristics(&self) -> u16 { + self.dll_characteristics.get(LE) + } + + #[inline] + fn size_of_stack_reserve(&self) -> u64 { + self.size_of_stack_reserve.get(LE).into() + } + + #[inline] + fn size_of_stack_commit(&self) -> u64 { + self.size_of_stack_commit.get(LE).into() + } + + #[inline] + fn size_of_heap_reserve(&self) -> u64 { + self.size_of_heap_reserve.get(LE).into() + } + + #[inline] + fn size_of_heap_commit(&self) -> u64 { + self.size_of_heap_commit.get(LE).into() + } + + #[inline] + fn loader_flags(&self) -> u32 { + self.loader_flags.get(LE) + } + + #[inline] + fn number_of_rva_and_sizes(&self) -> u32 { + self.number_of_rva_and_sizes.get(LE) + } +} + +impl ImageNtHeaders for pe::ImageNtHeaders64 { + type ImageOptionalHeader = pe::ImageOptionalHeader64; + type ImageThunkData = pe::ImageThunkData64; + + #[inline] + fn is_type_64(&self) -> bool { + true + } + + #[inline] + fn is_valid_optional_magic(&self) -> bool { + self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC + } + + #[inline] + fn signature(&self) -> u32 { + self.signature.get(LE) + } + + #[inline] + fn file_header(&self) -> &pe::ImageFileHeader { + &self.file_header + } + + #[inline] + fn optional_header(&self) -> &Self::ImageOptionalHeader { + &self.optional_header + } +} + +impl ImageOptionalHeader for pe::ImageOptionalHeader64 { + #[inline] + fn magic(&self) -> u16 { + self.magic.get(LE) + } + + #[inline] + fn major_linker_version(&self) -> u8 { + self.major_linker_version + } + + #[inline] + fn minor_linker_version(&self) -> u8 { + self.minor_linker_version + } + + #[inline] + fn size_of_code(&self) -> u32 { + self.size_of_code.get(LE) + } + + #[inline] + fn size_of_initialized_data(&self) -> u32 { + self.size_of_initialized_data.get(LE) + } + + #[inline] + fn size_of_uninitialized_data(&self) -> u32 { + self.size_of_uninitialized_data.get(LE) + } + + #[inline] + fn address_of_entry_point(&self) -> u32 { + self.address_of_entry_point.get(LE) + } + + #[inline] + fn base_of_code(&self) -> u32 { + self.base_of_code.get(LE) + } + + #[inline] + fn base_of_data(&self) -> Option<u32> { + None + } + + #[inline] + fn image_base(&self) -> u64 { + self.image_base.get(LE) + } + + #[inline] + fn section_alignment(&self) -> u32 { + self.section_alignment.get(LE) + } + + #[inline] + fn file_alignment(&self) -> u32 { + self.file_alignment.get(LE) + } + + #[inline] + fn major_operating_system_version(&self) -> u16 { + self.major_operating_system_version.get(LE) + } + + #[inline] + fn minor_operating_system_version(&self) -> u16 { + self.minor_operating_system_version.get(LE) + } + + #[inline] + fn major_image_version(&self) -> u16 { + self.major_image_version.get(LE) + } + + #[inline] + fn minor_image_version(&self) -> u16 { + self.minor_image_version.get(LE) + } + + #[inline] + fn major_subsystem_version(&self) -> u16 { + self.major_subsystem_version.get(LE) + } + + #[inline] + fn minor_subsystem_version(&self) -> u16 { + self.minor_subsystem_version.get(LE) + } + + #[inline] + fn win32_version_value(&self) -> u32 { + self.win32_version_value.get(LE) + } + + #[inline] + fn size_of_image(&self) -> u32 { + self.size_of_image.get(LE) + } + + #[inline] + fn size_of_headers(&self) -> u32 { + self.size_of_headers.get(LE) + } + + #[inline] + fn check_sum(&self) -> u32 { + self.check_sum.get(LE) + } + + #[inline] + fn subsystem(&self) -> u16 { + self.subsystem.get(LE) + } + + #[inline] + fn dll_characteristics(&self) -> u16 { + self.dll_characteristics.get(LE) + } + + #[inline] + fn size_of_stack_reserve(&self) -> u64 { + self.size_of_stack_reserve.get(LE) + } + + #[inline] + fn size_of_stack_commit(&self) -> u64 { + self.size_of_stack_commit.get(LE) + } + + #[inline] + fn size_of_heap_reserve(&self) -> u64 { + self.size_of_heap_reserve.get(LE) + } + + #[inline] + fn size_of_heap_commit(&self) -> u64 { + self.size_of_heap_commit.get(LE) + } + + #[inline] + fn loader_flags(&self) -> u32 { + self.loader_flags.get(LE) + } + + #[inline] + fn number_of_rva_and_sizes(&self) -> u32 { + self.number_of_rva_and_sizes.get(LE) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/import.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/import.rs new file mode 100644 index 0000000..c35dc98 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/import.rs
@@ -0,0 +1,381 @@ +use core::fmt::Debug; +use core::mem; + +use crate::endian::{LittleEndian as LE, U16Bytes}; +use crate::pe; +use crate::pod::Pod; +use crate::read::{Bytes, ReadError, Result}; + +use super::ImageNtHeaders; + +/// Information for parsing a PE import table. +/// +/// Returned by [`DataDirectories::import_table`](super::DataDirectories::import_table). +#[derive(Debug, Clone)] +pub struct ImportTable<'data> { + section_data: Bytes<'data>, + section_address: u32, + import_address: u32, +} + +impl<'data> ImportTable<'data> { + /// Create a new import table parser. + /// + /// The import descriptors start at `import_address`. + /// The size declared in the `IMAGE_DIRECTORY_ENTRY_IMPORT` data directory is + /// ignored by the Windows loader, and so descriptors will be parsed until a null entry. + /// + /// `section_data` should be from the section containing `import_address`, and + /// `section_address` should be the address of that section. Pointers within the + /// descriptors and thunks may point to anywhere within the section data. + pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self { + ImportTable { + section_data: Bytes(section_data), + section_address, + import_address, + } + } + + /// Return an iterator for the import descriptors. + pub fn descriptors(&self) -> Result<ImportDescriptorIterator<'data>> { + let offset = self.import_address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE import descriptor address")?; + Ok(ImportDescriptorIterator { data, null: false }) + } + + /// Return a library name given its address. + /// + /// This address may be from [`pe::ImageImportDescriptor::name`]. + pub fn name(&self, address: u32) -> Result<&'data [u8]> { + self.section_data + .read_string_at(address.wrapping_sub(self.section_address) as usize) + .read_error("Invalid PE import descriptor name") + } + + /// Return a list of thunks given its address. + /// + /// This address may be from [`pe::ImageImportDescriptor::original_first_thunk`] + /// or [`pe::ImageImportDescriptor::first_thunk`]. + pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> { + let offset = address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE import thunk table address")?; + Ok(ImportThunkList { data }) + } + + /// Parse a thunk. + pub fn import<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> { + if thunk.is_ordinal() { + Ok(Import::Ordinal(thunk.ordinal())) + } else { + let (hint, name) = self.hint_name(thunk.address())?; + Ok(Import::Name(hint, name)) + } + } + + /// Return the hint and name at the given address. + /// + /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`]. + /// + /// The hint is an index into the export name pointer table in the target library. + pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> { + let offset = address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE import thunk address")?; + let hint = data + .read::<U16Bytes<LE>>() + .read_error("Missing PE import thunk hint")? + .get(LE); + let name = data + .read_string() + .read_error("Missing PE import thunk name")?; + Ok((hint, name)) + } +} + +/// A fallible iterator for the descriptors in the import data directory. +#[derive(Debug, Clone)] +pub struct ImportDescriptorIterator<'data> { + data: Bytes<'data>, + null: bool, +} + +impl<'data> ImportDescriptorIterator<'data> { + /// Return the next descriptor. + /// + /// Returns `Ok(None)` when a null descriptor is found. + pub fn next(&mut self) -> Result<Option<&'data pe::ImageImportDescriptor>> { + if self.null { + return Ok(None); + } + let result = self + .data + .read::<pe::ImageImportDescriptor>() + .read_error("Missing PE null import descriptor"); + match result { + Ok(import_desc) => { + if import_desc.is_null() { + self.null = true; + Ok(None) + } else { + Ok(Some(import_desc)) + } + } + Err(e) => { + self.null = true; + Err(e) + } + } + } +} + +impl<'data> Iterator for ImportDescriptorIterator<'data> { + type Item = Result<&'data pe::ImageImportDescriptor>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// A list of import thunks. +/// +/// These may be in the import lookup table, or the import address table. +#[derive(Debug, Clone)] +pub struct ImportThunkList<'data> { + data: Bytes<'data>, +} + +impl<'data> ImportThunkList<'data> { + /// Get the thunk at the given index. + pub fn get<Pe: ImageNtHeaders>(&self, index: usize) -> Result<Pe::ImageThunkData> { + let thunk = self + .data + .read_at(index * mem::size_of::<Pe::ImageThunkData>()) + .read_error("Invalid PE import thunk index")?; + Ok(*thunk) + } + + /// Return the first thunk in the list, and update `self` to point after it. + /// + /// Returns `Ok(None)` when a null thunk is found. + pub fn next<Pe: ImageNtHeaders>(&mut self) -> Result<Option<Pe::ImageThunkData>> { + let thunk = self + .data + .read::<Pe::ImageThunkData>() + .read_error("Missing PE null import thunk")?; + if thunk.address() == 0 { + Ok(None) + } else { + Ok(Some(*thunk)) + } + } +} + +/// A parsed import thunk. +#[derive(Debug, Clone, Copy)] +pub enum Import<'data> { + /// Import by ordinal. + Ordinal(u16), + /// Import by name. + /// + /// Includes a hint for the index into the export name pointer table in the target library. + Name(u16, &'data [u8]), +} + +/// A trait for generic access to [`pe::ImageThunkData32`] and [`pe::ImageThunkData64`]. +#[allow(missing_docs)] +pub trait ImageThunkData: Debug + Pod { + /// Return the raw thunk value. + fn raw(self) -> u64; + + /// Returns true if the ordinal flag is set. + fn is_ordinal(self) -> bool; + + /// Return the ordinal portion of the thunk. + /// + /// Does not check the ordinal flag. + fn ordinal(self) -> u16; + + /// Return the RVA portion of the thunk. + /// + /// Does not check the ordinal flag. + fn address(self) -> u32; +} + +impl ImageThunkData for pe::ImageThunkData64 { + fn raw(self) -> u64 { + self.0.get(LE) + } + + fn is_ordinal(self) -> bool { + self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG64 != 0 + } + + fn ordinal(self) -> u16 { + self.0.get(LE) as u16 + } + + fn address(self) -> u32 { + self.0.get(LE) as u32 & 0x7fff_ffff + } +} + +impl ImageThunkData for pe::ImageThunkData32 { + fn raw(self) -> u64 { + self.0.get(LE).into() + } + + fn is_ordinal(self) -> bool { + self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG32 != 0 + } + + fn ordinal(self) -> u16 { + self.0.get(LE) as u16 + } + + fn address(self) -> u32 { + self.0.get(LE) & 0x7fff_ffff + } +} + +/// Information for parsing a PE delay-load import table. +/// +/// Returned by +/// [`DataDirectories::delay_load_import_table`](super::DataDirectories::delay_load_import_table). +#[derive(Debug, Clone)] +pub struct DelayLoadImportTable<'data> { + section_data: Bytes<'data>, + section_address: u32, + import_address: u32, +} + +impl<'data> DelayLoadImportTable<'data> { + /// Create a new delay load import table parser. + /// + /// The import descriptors start at `import_address`. + /// This table works in the same way the import table does: descriptors will be + /// parsed until a null entry. + /// + /// `section_data` should be from the section containing `import_address`, and + /// `section_address` should be the address of that section. Pointers within the + /// descriptors and thunks may point to anywhere within the section data. + pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self { + DelayLoadImportTable { + section_data: Bytes(section_data), + section_address, + import_address, + } + } + + /// Return an iterator for the import descriptors. + pub fn descriptors(&self) -> Result<DelayLoadDescriptorIterator<'data>> { + let offset = self.import_address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE delay-load import descriptor address")?; + Ok(DelayLoadDescriptorIterator { data, null: false }) + } + + /// Return a library name given its address. + /// + /// This address may be from [`pe::ImageDelayloadDescriptor::dll_name_rva`]. + pub fn name(&self, address: u32) -> Result<&'data [u8]> { + self.section_data + .read_string_at(address.wrapping_sub(self.section_address) as usize) + .read_error("Invalid PE import descriptor name") + } + + /// Return a list of thunks given its address. + /// + /// This address may be from the INT, i.e. from + /// [`pe::ImageDelayloadDescriptor::import_name_table_rva`]. + /// + /// Please note that others RVA values from [`pe::ImageDelayloadDescriptor`] are used + /// by the delay loader at runtime to store values, and thus do not point inside the same + /// section as the INT. Calling this function on those addresses will fail. + pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> { + let offset = address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE delay load import thunk table address")?; + Ok(ImportThunkList { data }) + } + + /// Parse a thunk. + pub fn import<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> { + if thunk.is_ordinal() { + Ok(Import::Ordinal(thunk.ordinal())) + } else { + let (hint, name) = self.hint_name(thunk.address())?; + Ok(Import::Name(hint, name)) + } + } + + /// Return the hint and name at the given address. + /// + /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`]. + /// + /// The hint is an index into the export name pointer table in the target library. + pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> { + let offset = address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE delay load import thunk address")?; + let hint = data + .read::<U16Bytes<LE>>() + .read_error("Missing PE delay load import thunk hint")? + .get(LE); + let name = data + .read_string() + .read_error("Missing PE delay load import thunk name")?; + Ok((hint, name)) + } +} + +/// A fallible iterator for the descriptors in the delay-load data directory. +#[derive(Debug, Clone)] +pub struct DelayLoadDescriptorIterator<'data> { + data: Bytes<'data>, + null: bool, +} + +impl<'data> DelayLoadDescriptorIterator<'data> { + /// Return the next descriptor. + /// + /// Returns `Ok(None)` when a null descriptor is found. + pub fn next(&mut self) -> Result<Option<&'data pe::ImageDelayloadDescriptor>> { + if self.null { + return Ok(None); + } + let result = self + .data + .read::<pe::ImageDelayloadDescriptor>() + .read_error("Missing PE null delay-load import descriptor"); + match result { + Ok(import_desc) => { + if import_desc.is_null() { + self.null = true; + Ok(None) + } else { + Ok(Some(import_desc)) + } + } + Err(e) => { + self.null = true; + Err(e) + } + } + } +} + +impl<'data> Iterator for DelayLoadDescriptorIterator<'data> { + type Item = Result<&'data pe::ImageDelayloadDescriptor>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/mod.rs new file mode 100644 index 0000000..ab6011c --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/mod.rs
@@ -0,0 +1,68 @@ +//! Support for reading PE files. +//! +//! Traits are used to abstract over the difference between PE32 and PE32+. +//! The primary trait for this is [`ImageNtHeaders`]. +//! +//! ## High level API +//! +//! [`PeFile`] implements the [`Object`](crate::read::Object) trait for +//! PE files. [`PeFile`] is parameterised by [`ImageNtHeaders`] to allow +//! reading both PE32 and PE32+. There are type aliases for these parameters +//! ([`PeFile32`] and [`PeFile64`]). +//! +//! ## Low level API +//! +//! The [`ImageNtHeaders`] trait can be directly used to parse both +//! [`pe::ImageNtHeaders32`] and [`pe::ImageNtHeaders64`]. +//! +//! ### Example for low level API +//! ```no_run +//! use object::pe; +//! use object::read::pe::ImageNtHeaders; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the name of each section. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(feature = "std")] { +//! let data = fs::read("path/to/binary")?; +//! let dos_header = pe::ImageDosHeader::parse(&*data)?; +//! let mut offset = dos_header.nt_headers_offset().into(); +//! let (nt_headers, data_directories) = pe::ImageNtHeaders64::parse(&*data, &mut offset)?; +//! let sections = nt_headers.sections(&*data, offset)?; +//! let symbols = nt_headers.symbols(&*data)?; +//! for section in sections.iter() { +//! println!("{}", String::from_utf8_lossy(section.name(symbols.strings())?)); +//! } +//! # } +//! Ok(()) +//! } +//! ``` +#[cfg(doc)] +use crate::pe; + +mod file; +pub use file::*; + +mod section; +pub use section::*; + +mod data_directory; +pub use data_directory::*; + +mod export; +pub use export::*; + +mod import; +pub use import::*; + +mod relocation; +pub use relocation::*; + +mod resource; +pub use resource::*; + +mod rich; +pub use rich::*; + +pub use super::coff::{SectionTable, SymbolTable};
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/relocation.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/relocation.rs new file mode 100644 index 0000000..92b3f08 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/relocation.rs
@@ -0,0 +1,109 @@ +use core::slice; + +use crate::endian::{LittleEndian as LE, U16}; +use crate::pe; +use crate::read::{Bytes, Error, ReadError, Result}; + +/// An iterator over the relocation blocks in the `.reloc` section of a PE file. +/// +/// Returned by [`DataDirectories::relocation_blocks`](super::DataDirectories::relocation_blocks). +#[derive(Debug, Default, Clone, Copy)] +pub struct RelocationBlockIterator<'data> { + data: Bytes<'data>, +} + +impl<'data> RelocationBlockIterator<'data> { + /// Construct a new iterator from the data of the `.reloc` section. + pub fn new(data: &'data [u8]) -> Self { + RelocationBlockIterator { data: Bytes(data) } + } + + /// Read the next relocation page. + pub fn next(&mut self) -> Result<Option<RelocationIterator<'data>>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse().map(Some); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> Result<RelocationIterator<'data>> { + let header = self + .data + .read::<pe::ImageBaseRelocation>() + .read_error("Invalid PE reloc section size")?; + let virtual_address = header.virtual_address.get(LE); + let size = header.size_of_block.get(LE); + if size <= 8 || size & 3 != 0 { + return Err(Error("Invalid PE reloc block size")); + } + let count = (size - 8) / 2; + let relocs = self + .data + .read_slice::<U16<LE>>(count as usize) + .read_error("Invalid PE reloc block size")? + .iter(); + Ok(RelocationIterator { + virtual_address, + size, + relocs, + }) + } +} + +impl<'data> Iterator for RelocationBlockIterator<'data> { + type Item = Result<RelocationIterator<'data>>; + + fn next(&mut self) -> Option<Self::Item> { + self.next().transpose() + } +} + +/// An iterator of the relocations in a block in the `.reloc` section of a PE file. +#[derive(Debug, Clone)] +pub struct RelocationIterator<'data> { + virtual_address: u32, + size: u32, + relocs: slice::Iter<'data, U16<LE>>, +} + +impl<'data> RelocationIterator<'data> { + /// Return the virtual address of the page that this block of relocations applies to. + pub fn virtual_address(&self) -> u32 { + self.virtual_address + } + + /// Return the size in bytes of this block of relocations. + pub fn size(&self) -> u32 { + self.size + } +} + +impl<'data> Iterator for RelocationIterator<'data> { + type Item = Relocation; + + fn next(&mut self) -> Option<Relocation> { + loop { + let reloc = self.relocs.next()?.get(LE); + if reloc != 0 { + return Some(Relocation { + virtual_address: self.virtual_address.wrapping_add((reloc & 0xfff) as u32), + typ: reloc >> 12, + }); + } + } + } +} + +/// A relocation in the `.reloc` section of a PE file. +#[derive(Debug, Default, Clone, Copy)] +pub struct Relocation { + /// The virtual address of the relocation. + pub virtual_address: u32, + /// One of the `pe::IMAGE_REL_BASED_*` constants. + pub typ: u16, +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/resource.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/resource.rs new file mode 100644 index 0000000..bf1fa70 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/resource.rs
@@ -0,0 +1,210 @@ +use alloc::string::String; +use core::char; + +use crate::endian::{LittleEndian as LE, U16Bytes}; +use crate::pe; +use crate::read::{ReadError, ReadRef, Result}; + +/// The `.rsrc` section of a PE file. +/// +/// Returned by [`DataDirectories::resource_directory`](super::DataDirectories::resource_directory). +#[derive(Debug, Clone, Copy)] +pub struct ResourceDirectory<'data> { + data: &'data [u8], +} + +impl<'data> ResourceDirectory<'data> { + /// Construct from the data of the `.rsrc` section. + pub fn new(data: &'data [u8]) -> Self { + ResourceDirectory { data } + } + + /// Parses the root resource directory. + pub fn root(&self) -> Result<ResourceDirectoryTable<'data>> { + ResourceDirectoryTable::parse(self.data, 0) + } +} + +/// A table of resource entries. +#[derive(Debug, Clone)] +pub struct ResourceDirectoryTable<'data> { + /// The table header. + pub header: &'data pe::ImageResourceDirectory, + /// The table entries. + pub entries: &'data [pe::ImageResourceDirectoryEntry], +} + +impl<'data> ResourceDirectoryTable<'data> { + fn parse(data: &'data [u8], offset: u32) -> Result<Self> { + let mut offset = u64::from(offset); + let header = data + .read::<pe::ImageResourceDirectory>(&mut offset) + .read_error("Invalid resource table header")?; + let entries_count = header.number_of_id_entries.get(LE) as usize + + header.number_of_named_entries.get(LE) as usize; + let entries = data + .read_slice::<pe::ImageResourceDirectoryEntry>(&mut offset, entries_count) + .read_error("Invalid resource table entries")?; + Ok(Self { header, entries }) + } +} + +impl pe::ImageResourceDirectoryEntry { + /// Returns true if the entry has a name, rather than an ID. + pub fn has_name(&self) -> bool { + self.name_or_id.get(LE) & pe::IMAGE_RESOURCE_NAME_IS_STRING != 0 + } + + /// Returns the section offset of the name. + /// + /// Valid if `has_name()` returns true. + fn name(&self) -> ResourceName { + let offset = self.name_or_id.get(LE) & !pe::IMAGE_RESOURCE_NAME_IS_STRING; + ResourceName { offset } + } + + /// Returns the ID. + /// + /// Valid if `has_string_name()` returns false. + fn id(&self) -> u16 { + (self.name_or_id.get(LE) & 0x0000_FFFF) as u16 + } + + /// Returns the entry name + pub fn name_or_id(&self) -> ResourceNameOrId { + if self.has_name() { + ResourceNameOrId::Name(self.name()) + } else { + ResourceNameOrId::Id(self.id()) + } + } + + /// Returns true if the entry is a subtable. + pub fn is_table(&self) -> bool { + self.offset_to_data_or_directory.get(LE) & pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY != 0 + } + + /// Returns the section offset of the associated table or data. + pub fn data_offset(&self) -> u32 { + self.offset_to_data_or_directory.get(LE) & !pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY + } + + /// Returns the data associated to this directory entry. + pub fn data<'data>( + &self, + section: ResourceDirectory<'data>, + ) -> Result<ResourceDirectoryEntryData<'data>> { + if self.is_table() { + ResourceDirectoryTable::parse(section.data, self.data_offset()) + .map(ResourceDirectoryEntryData::Table) + } else { + section + .data + .read_at::<pe::ImageResourceDataEntry>(self.data_offset().into()) + .read_error("Invalid resource entry") + .map(ResourceDirectoryEntryData::Data) + } + } +} + +/// Data associated with a resource directory entry. +#[derive(Debug, Clone)] +pub enum ResourceDirectoryEntryData<'data> { + /// A subtable entry. + Table(ResourceDirectoryTable<'data>), + /// A resource data entry. + Data(&'data pe::ImageResourceDataEntry), +} + +impl<'data> ResourceDirectoryEntryData<'data> { + /// Converts to an option of table. + /// + /// Helper for iterator filtering. + pub fn table(self) -> Option<ResourceDirectoryTable<'data>> { + match self { + Self::Table(dir) => Some(dir), + _ => None, + } + } + + /// Converts to an option of data entry. + /// + /// Helper for iterator filtering. + pub fn data(self) -> Option<&'data pe::ImageResourceDataEntry> { + match self { + Self::Data(rsc) => Some(rsc), + _ => None, + } + } +} + +/// A resource name. +#[derive(Debug, Clone, Copy)] +pub struct ResourceName { + offset: u32, +} + +impl ResourceName { + /// Converts to a `String`. + pub fn to_string_lossy(&self, directory: ResourceDirectory<'_>) -> Result<String> { + let d = self.data(directory)?.iter().map(|c| c.get(LE)); + + Ok(char::decode_utf16(d) + .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) + .collect::<String>()) + } + + /// Returns the string unicode buffer. + pub fn data<'data>( + &self, + directory: ResourceDirectory<'data>, + ) -> Result<&'data [U16Bytes<LE>]> { + let mut offset = u64::from(self.offset); + let len = directory + .data + .read::<U16Bytes<LE>>(&mut offset) + .read_error("Invalid resource name offset")?; + directory + .data + .read_slice::<U16Bytes<LE>>(&mut offset, len.get(LE).into()) + .read_error("Invalid resource name length") + } + + /// Returns the string buffer as raw bytes. + pub fn raw_data<'data>(&self, directory: ResourceDirectory<'data>) -> Result<&'data [u8]> { + self.data(directory).map(crate::pod::bytes_of_slice) + } +} + +/// A resource name or ID. +/// +/// Can be either a string or a numeric ID. +#[derive(Debug)] +pub enum ResourceNameOrId { + /// A resource name. + Name(ResourceName), + /// A resource ID. + Id(u16), +} + +impl ResourceNameOrId { + /// Converts to an option of name. + /// + /// Helper for iterator filtering. + pub fn name(self) -> Option<ResourceName> { + match self { + Self::Name(name) => Some(name), + _ => None, + } + } + + /// Converts to an option of ID. + /// + /// Helper for iterator filtering. + pub fn id(self) -> Option<u16> { + match self { + Self::Id(id) => Some(id), + _ => None, + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/rich.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/rich.rs new file mode 100644 index 0000000..584be64 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/rich.rs
@@ -0,0 +1,92 @@ +//! PE rich header handling + +use core::mem; + +use crate::endian::{LittleEndian as LE, U32}; +use crate::pe; +use crate::pod::bytes_of_slice; +use crate::read::{Bytes, ReadRef}; + +/// Parsed information about a Rich Header. +#[derive(Debug, Clone, Copy)] +pub struct RichHeaderInfo<'data> { + /// The offset at which the rich header starts. + pub offset: usize, + /// The length (in bytes) of the rich header. + /// + /// This includes the payload, but also the 16-byte start sequence and the + /// 8-byte final "Rich" and XOR key. + pub length: usize, + /// The XOR key used to mask the rich header. + /// + /// Unless the file has been tampered with, it should be equal to a checksum + /// of the file header. + pub xor_key: u32, + masked_entries: &'data [pe::MaskedRichHeaderEntry], +} + +/// A PE rich header entry after it has been unmasked. +/// +/// See [`pe::MaskedRichHeaderEntry`]. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct RichHeaderEntry { + /// ID of the component. + pub comp_id: u32, + /// Number of times this component has been used when building this PE. + pub count: u32, +} + +impl<'data> RichHeaderInfo<'data> { + /// Try to locate a rich header and its entries in the current PE file. + pub fn parse<R: ReadRef<'data>>(data: R, nt_header_offset: u64) -> Option<Self> { + // Locate the rich header, if any. + // It ends with the "Rich" string and an XOR key, before the NT header. + let data = data.read_bytes_at(0, nt_header_offset).map(Bytes).ok()?; + let end_marker_offset = memmem(data.0, b"Rich", 4)?; + let xor_key = *data.read_at::<U32<LE>>(end_marker_offset + 4).ok()?; + + // It starts at the masked "DanS" string and 3 masked zeroes. + let masked_start_marker = U32::new(LE, 0x536e_6144 ^ xor_key.get(LE)); + let start_header = [masked_start_marker, xor_key, xor_key, xor_key]; + let start_sequence = bytes_of_slice(&start_header); + let start_marker_offset = memmem(&data.0[..end_marker_offset], start_sequence, 4)?; + + // Extract the items between the markers. + let items_offset = start_marker_offset + start_sequence.len(); + let items_len = end_marker_offset - items_offset; + let item_count = items_len / mem::size_of::<pe::MaskedRichHeaderEntry>(); + let items = data.read_slice_at(items_offset, item_count).ok()?; + Some(RichHeaderInfo { + offset: start_marker_offset, + // Includes "Rich" marker and the XOR key. + length: end_marker_offset - start_marker_offset + 8, + xor_key: xor_key.get(LE), + masked_entries: items, + }) + } + + /// Returns an iterator over the unmasked entries. + pub fn unmasked_entries(&self) -> impl Iterator<Item = RichHeaderEntry> + 'data { + let xor_key = self.xor_key; + self.masked_entries + .iter() + .map(move |entry| RichHeaderEntry { + comp_id: entry.masked_comp_id.get(LE) ^ xor_key, + count: entry.masked_count.get(LE) ^ xor_key, + }) + } +} + +/// Find the offset of the first occurrence of needle in the data. +/// +/// The offset must have the given alignment. +fn memmem(data: &[u8], needle: &[u8], align: usize) -> Option<usize> { + let mut offset = 0; + loop { + if data.get(offset..)?.get(..needle.len())? == needle { + return Some(offset); + } + offset += align; + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/pe/section.rs b/chromium_crates_io/vendor/object-v0_37/src/read/pe/section.rs new file mode 100644 index 0000000..4c53d1e --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/pe/section.rs
@@ -0,0 +1,476 @@ +use core::marker::PhantomData; +use core::{cmp, iter, slice, str}; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::pe::ImageSectionHeader; +use crate::read::{ + self, CompressedData, CompressedFileRange, ObjectSection, ObjectSegment, ReadError, ReadRef, + Relocation, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, +}; + +use super::{ImageNtHeaders, PeFile, SectionTable}; + +/// An iterator for the loadable sections in a [`PeFile32`](super::PeFile32). +pub type PeSegmentIterator32<'data, 'file, R = &'data [u8]> = + PeSegmentIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the loadable sections in a [`PeFile64`](super::PeFile64). +pub type PeSegmentIterator64<'data, 'file, R = &'data [u8]> = + PeSegmentIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the loadable sections in a [`PeFile`]. +#[derive(Debug)] +pub struct PeSegmentIterator<'data, 'file, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + pub(super) file: &'file PeFile<'data, Pe, R>, + pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>, +} + +impl<'data, 'file, Pe, R> Iterator for PeSegmentIterator<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + type Item = PeSegment<'data, 'file, Pe, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|section| PeSegment { + file: self.file, + section, + }) + } +} + +/// A loadable section in a [`PeFile32`](super::PeFile32). +pub type PeSegment32<'data, 'file, R = &'data [u8]> = + PeSegment<'data, 'file, pe::ImageNtHeaders32, R>; +/// A loadable section in a [`PeFile64`](super::PeFile64). +pub type PeSegment64<'data, 'file, R = &'data [u8]> = + PeSegment<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A loadable section in a [`PeFile`]. +/// +/// Most functionality is provided by the [`ObjectSegment`] trait implementation. +#[derive(Debug)] +pub struct PeSegment<'data, 'file, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + file: &'file PeFile<'data, Pe, R>, + section: &'data pe::ImageSectionHeader, +} + +impl<'data, 'file, Pe, R> PeSegment<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + /// Get the PE file containing this segment. + pub fn pe_file(&self) -> &'file PeFile<'data, Pe, R> { + self.file + } + + /// Get the raw PE section header. + pub fn pe_section(&self) -> &'data pe::ImageSectionHeader { + self.section + } +} + +impl<'data, 'file, Pe, R> read::private::Sealed for PeSegment<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Pe, R> ObjectSegment<'data> for PeSegment<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base) + } + + #[inline] + fn size(&self) -> u64 { + u64::from(self.section.virtual_size.get(LE)) + } + + #[inline] + fn align(&self) -> u64 { + self.file.section_alignment() + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + let (offset, size) = self.section.pe_file_range(); + (u64::from(offset), u64::from(size)) + } + + fn data(&self) -> Result<&'data [u8]> { + self.section.pe_data(self.file.data) + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.data()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + self.section + .name(self.file.common.symbols.strings()) + .map(Some) + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + let name = self.section.name(self.file.common.symbols.strings())?; + Ok(Some( + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 PE section name")?, + )) + } + + #[inline] + fn flags(&self) -> SegmentFlags { + let characteristics = self.section.characteristics.get(LE); + SegmentFlags::Coff { characteristics } + } +} + +/// An iterator for the sections in a [`PeFile32`](super::PeFile32). +pub type PeSectionIterator32<'data, 'file, R = &'data [u8]> = + PeSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the sections in a [`PeFile64`](super::PeFile64). +pub type PeSectionIterator64<'data, 'file, R = &'data [u8]> = + PeSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the sections in a [`PeFile`]. +#[derive(Debug)] +pub struct PeSectionIterator<'data, 'file, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + pub(super) file: &'file PeFile<'data, Pe, R>, + pub(super) iter: iter::Enumerate<slice::Iter<'data, pe::ImageSectionHeader>>, +} + +impl<'data, 'file, Pe, R> Iterator for PeSectionIterator<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + type Item = PeSection<'data, 'file, Pe, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|(index, section)| PeSection { + file: self.file, + index: SectionIndex(index + 1), + section, + }) + } +} + +/// A section in a [`PeFile32`](super::PeFile32). +pub type PeSection32<'data, 'file, R = &'data [u8]> = + PeSection<'data, 'file, pe::ImageNtHeaders32, R>; +/// A section in a [`PeFile64`](super::PeFile64). +pub type PeSection64<'data, 'file, R = &'data [u8]> = + PeSection<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A section in a [`PeFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct PeSection<'data, 'file, Pe, R = &'data [u8]> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + pub(super) file: &'file PeFile<'data, Pe, R>, + pub(super) index: SectionIndex, + pub(super) section: &'data pe::ImageSectionHeader, +} + +impl<'data, 'file, Pe, R> PeSection<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + /// Get the PE file containing this segment. + pub fn pe_file(&self) -> &'file PeFile<'data, Pe, R> { + self.file + } + + /// Get the raw PE section header. + pub fn pe_section(&self) -> &'data pe::ImageSectionHeader { + self.section + } +} + +impl<'data, 'file, Pe, R> read::private::Sealed for PeSection<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Pe, R> ObjectSection<'data> for PeSection<'data, 'file, Pe, R> +where + Pe: ImageNtHeaders, + R: ReadRef<'data>, +{ + type RelocationIterator = PeRelocationIterator<'data, 'file, R>; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base) + } + + #[inline] + fn size(&self) -> u64 { + u64::from(self.section.virtual_size.get(LE)) + } + + #[inline] + fn align(&self) -> u64 { + self.file.section_alignment() + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + let (offset, size) = self.section.pe_file_range(); + if size == 0 { + None + } else { + Some((u64::from(offset), u64::from(size))) + } + } + + fn data(&self) -> Result<&'data [u8]> { + self.section.pe_data(self.file.data) + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.data()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + #[inline] + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + self.section.name(self.file.common.symbols.strings()) + } + + #[inline] + fn name(&self) -> Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 PE section name") + } + + #[inline] + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + Ok(None) + } + + #[inline] + fn kind(&self) -> SectionKind { + self.section.kind() + } + + fn relocations(&self) -> PeRelocationIterator<'data, 'file, R> { + PeRelocationIterator(PhantomData) + } + + fn relocation_map(&self) -> read::Result<RelocationMap> { + RelocationMap::new(self.file, self) + } + + fn flags(&self) -> SectionFlags { + SectionFlags::Coff { + characteristics: self.section.characteristics.get(LE), + } + } +} + +impl<'data> SectionTable<'data> { + /// Return the file offset of the given virtual address, and the size up + /// to the end of the section containing it. + /// + /// Returns `None` if no section contains the address. + pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> { + self.iter().find_map(|section| section.pe_file_range_at(va)) + } + + /// Return the data starting at the given virtual address, up to the end of the + /// section containing it. + /// + /// Ignores sections with invalid data. + /// + /// Returns `None` if no section contains the address. + pub fn pe_data_at<R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> { + self.iter().find_map(|section| section.pe_data_at(data, va)) + } + + /// Return the data of the section that contains the given virtual address in a PE file. + /// + /// Also returns the virtual address of that section. + /// + /// Ignores sections with invalid data. + pub fn pe_data_containing<R: ReadRef<'data>>( + &self, + data: R, + va: u32, + ) -> Option<(&'data [u8], u32)> { + self.iter() + .find_map(|section| section.pe_data_containing(data, va)) + } + + /// Return the section that contains a given virtual address. + pub fn section_containing(&self, va: u32) -> Option<&'data ImageSectionHeader> { + self.iter().find(|section| section.contains_rva(va)) + } +} + +impl pe::ImageSectionHeader { + /// Return the offset and size of the section in a PE file. + /// + /// The size of the range will be the minimum of the file size and virtual size. + pub fn pe_file_range(&self) -> (u32, u32) { + // Pointer and size will be zero for uninitialized data; we don't need to validate this. + let offset = self.pointer_to_raw_data.get(LE); + let size = cmp::min(self.virtual_size.get(LE), self.size_of_raw_data.get(LE)); + (offset, size) + } + + /// Return the file offset of the given virtual address, and the remaining size up + /// to the end of the section. + /// + /// Returns `None` if the section does not contain the address. + pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> { + let section_va = self.virtual_address.get(LE); + let offset = va.checked_sub(section_va)?; + let (section_offset, section_size) = self.pe_file_range(); + // Address must be within section (and not at its end). + if offset < section_size { + Some((section_offset.checked_add(offset)?, section_size - offset)) + } else { + None + } + } + + /// Return the virtual address and size of the section. + pub fn pe_address_range(&self) -> (u32, u32) { + (self.virtual_address.get(LE), self.virtual_size.get(LE)) + } + + /// Return the section data in a PE file. + /// + /// The length of the data will be the minimum of the file size and virtual size. + pub fn pe_data<'data, R: ReadRef<'data>>(&self, data: R) -> Result<&'data [u8]> { + let (offset, size) = self.pe_file_range(); + data.read_bytes_at(offset.into(), size.into()) + .read_error("Invalid PE section offset or size") + } + + /// Return the data starting at the given virtual address, up to the end of the + /// section. + /// + /// Ignores sections with invalid data. + /// + /// Returns `None` if the section does not contain the address. + pub fn pe_data_at<'data, R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> { + let (offset, size) = self.pe_file_range_at(va)?; + data.read_bytes_at(offset.into(), size.into()).ok() + } + + /// Tests whether a given RVA is part of this section + pub fn contains_rva(&self, va: u32) -> bool { + let section_va = self.virtual_address.get(LE); + match va.checked_sub(section_va) { + None => false, + Some(offset) => { + // Address must be within section (and not at its end). + offset < self.virtual_size.get(LE) + } + } + } + + /// Return the section data if it contains the given virtual address. + /// + /// Also returns the virtual address of that section. + /// + /// Ignores sections with invalid data. + pub fn pe_data_containing<'data, R: ReadRef<'data>>( + &self, + data: R, + va: u32, + ) -> Option<(&'data [u8], u32)> { + let section_va = self.virtual_address.get(LE); + let offset = va.checked_sub(section_va)?; + let (section_offset, section_size) = self.pe_file_range(); + // Address must be within section (and not at its end). + if offset < section_size { + let section_data = data + .read_bytes_at(section_offset.into(), section_size.into()) + .ok()?; + Some((section_data, section_va)) + } else { + None + } + } +} + +/// An iterator for the relocations in an [`PeSection`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeRelocationIterator<'data, 'file, R = &'data [u8]>( + PhantomData<(&'data (), &'file (), R)>, +); + +impl<'data, 'file, R> Iterator for PeRelocationIterator<'data, 'file, R> { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + None + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/read_cache.rs b/chromium_crates_io/vendor/object-v0_37/src/read/read_cache.rs new file mode 100644 index 0000000..bfb9bf7 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/read_cache.rs
@@ -0,0 +1,261 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cell::RefCell; +use core::convert::TryInto; +use core::mem; +use core::ops::Range; +#[cfg(feature = "std")] +use std::io::{Read, Seek, SeekFrom}; + +#[cfg(not(feature = "std"))] +use alloc::collections::btree_map::{BTreeMap as Map, Entry}; +#[cfg(feature = "std")] +use std::collections::hash_map::{Entry, HashMap as Map}; + +use crate::read::ReadRef; + +/// An implementation of [`ReadRef`] for data in a stream that implements +/// `Read + Seek`. +/// +/// Contains a cache of read-only blocks of data, allowing references to +/// them to be returned. Entries in the cache are never removed. +/// Entries are keyed on the offset and size of the read. +/// Currently overlapping reads are considered separate reads. +/// +/// This is primarily intended for environments where memory mapped files +/// are not available or not suitable, such as WebAssembly. +/// +/// Note that malformed files can cause the cache to grow much larger than +/// the file size. +#[derive(Debug)] +pub struct ReadCache<R: ReadCacheOps> { + cache: RefCell<ReadCacheInternal<R>>, +} + +#[derive(Debug)] +struct ReadCacheInternal<R: ReadCacheOps> { + read: R, + bufs: Map<(u64, u64), Box<[u8]>>, + strings: Map<(u64, u8), Box<[u8]>>, + len: Option<u64>, +} + +impl<R: ReadCacheOps> ReadCacheInternal<R> { + /// Ensures this range is contained in the len of the file + fn range_in_bounds(&mut self, range: &Range<u64>) -> Result<(), ()> { + if range.start <= range.end && range.end <= self.len()? { + Ok(()) + } else { + Err(()) + } + } + + /// The length of the underlying read, memoized + fn len(&mut self) -> Result<u64, ()> { + match self.len { + Some(len) => Ok(len), + None => { + let len = self.read.len()?; + self.len = Some(len); + Ok(len) + } + } + } +} + +impl<R: ReadCacheOps> ReadCache<R> { + /// Create an empty `ReadCache` for the given stream. + pub fn new(read: R) -> Self { + ReadCache { + cache: RefCell::new(ReadCacheInternal { + read, + bufs: Map::new(), + strings: Map::new(), + len: None, + }), + } + } + + /// Return an implementation of `ReadRef` that restricts reads + /// to the given range of the stream. + pub fn range(&self, offset: u64, size: u64) -> ReadCacheRange<'_, R> { + ReadCacheRange { + r: self, + offset, + size, + } + } + + /// Free buffers used by the cache. + pub fn clear(&mut self) { + self.cache.borrow_mut().bufs.clear(); + } + + /// Unwrap this `ReadCache<R>`, returning the underlying reader. + pub fn into_inner(self) -> R { + self.cache.into_inner().read + } +} + +impl<'a, R: ReadCacheOps> ReadRef<'a> for &'a ReadCache<R> { + fn len(self) -> Result<u64, ()> { + self.cache.borrow_mut().len() + } + + fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8], ()> { + if size == 0 { + return Ok(&[]); + } + let cache = &mut *self.cache.borrow_mut(); + cache.range_in_bounds(&(offset..(offset.saturating_add(size))))?; + let buf = match cache.bufs.entry((offset, size)) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let size = size.try_into().map_err(|_| ())?; + cache.read.seek(offset)?; + let mut bytes = Vec::new(); + bytes.try_reserve_exact(size).map_err(|_| ())?; + bytes.resize(size, 0); + let mut bytes = bytes.into_boxed_slice(); + cache.read.read_exact(&mut bytes)?; + entry.insert(bytes) + } + }; + // Extend the lifetime to that of self. + // This is OK because we never mutate or remove entries. + Ok(unsafe { mem::transmute::<&[u8], &[u8]>(buf) }) + } + + fn read_bytes_at_until(self, range: Range<u64>, delimiter: u8) -> Result<&'a [u8], ()> { + let cache = &mut *self.cache.borrow_mut(); + cache.range_in_bounds(&range)?; + let buf = match cache.strings.entry((range.start, delimiter)) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + cache.read.seek(range.start)?; + + let max_check: usize = (range.end - range.start).try_into().map_err(|_| ())?; + // Strings should be relatively small. + // TODO: make this configurable? + let max_check = max_check.min(4096); + + let mut bytes = Vec::new(); + let mut checked = 0; + loop { + bytes.resize((checked + 256).min(max_check), 0); + let read = cache.read.read(&mut bytes[checked..])?; + if read == 0 { + return Err(()); + } + if let Some(len) = memchr::memchr(delimiter, &bytes[checked..][..read]) { + bytes.truncate(checked + len); + break entry.insert(bytes.into_boxed_slice()); + } + checked += read; + if checked >= max_check { + return Err(()); + } + } + } + }; + // Extend the lifetime to that of self. + // This is OK because we never mutate or remove entries. + Ok(unsafe { mem::transmute::<&[u8], &[u8]>(buf) }) + } +} + +/// An implementation of [`ReadRef`] for a range of data in a stream that +/// implements `Read + Seek`. +/// +/// Shares an underlying [`ReadCache`] with a lifetime of `'a`. +#[derive(Debug)] +pub struct ReadCacheRange<'a, R: ReadCacheOps> { + r: &'a ReadCache<R>, + offset: u64, + size: u64, +} + +impl<'a, R: ReadCacheOps> Clone for ReadCacheRange<'a, R> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, R: ReadCacheOps> Copy for ReadCacheRange<'a, R> {} + +impl<'a, R: ReadCacheOps> ReadRef<'a> for ReadCacheRange<'a, R> { + fn len(self) -> Result<u64, ()> { + Ok(self.size) + } + + fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8], ()> { + if size == 0 { + return Ok(&[]); + } + let end = offset.checked_add(size).ok_or(())?; + if end > self.size { + return Err(()); + } + let r_offset = self.offset.checked_add(offset).ok_or(())?; + self.r.read_bytes_at(r_offset, size) + } + + fn read_bytes_at_until(self, range: Range<u64>, delimiter: u8) -> Result<&'a [u8], ()> { + let r_start = self.offset.checked_add(range.start).ok_or(())?; + let r_end = self.offset.checked_add(range.end).ok_or(())?; + let bytes = self.r.read_bytes_at_until(r_start..r_end, delimiter)?; + let size = bytes.len().try_into().map_err(|_| ())?; + let end = range.start.checked_add(size).ok_or(())?; + if end > self.size { + return Err(()); + } + Ok(bytes) + } +} + +/// Operations required to implement [`ReadCache`]. +/// +/// This is a subset of the `Read` and `Seek` traits. +/// A blanket implementation is provided for all types that implement +/// `Read + Seek`. +#[allow(clippy::len_without_is_empty)] +pub trait ReadCacheOps { + /// Return the length of the stream. + /// + /// Equivalent to `std::io::Seek::seek(SeekFrom::End(0))`. + fn len(&mut self) -> Result<u64, ()>; + + /// Seek to the given position in the stream. + /// + /// Equivalent to `std::io::Seek::seek` with `SeekFrom::Start(pos)`. + fn seek(&mut self, pos: u64) -> Result<u64, ()>; + + /// Read up to `buf.len()` bytes into `buf`. + /// + /// Equivalent to `std::io::Read::read`. + fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()>; + + /// Read exactly `buf.len()` bytes into `buf`. + /// + /// Equivalent to `std::io::Read::read_exact`. + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ()>; +} + +#[cfg(feature = "std")] +impl<T: Read + Seek> ReadCacheOps for T { + fn len(&mut self) -> Result<u64, ()> { + self.seek(SeekFrom::End(0)).map_err(|_| ()) + } + + fn seek(&mut self, pos: u64) -> Result<u64, ()> { + self.seek(SeekFrom::Start(pos)).map_err(|_| ()) + } + + fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> { + Read::read(self, buf).map_err(|_| ()) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ()> { + Read::read_exact(self, buf).map_err(|_| ()) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/read_ref.rs b/chromium_crates_io/vendor/object-v0_37/src/read/read_ref.rs new file mode 100644 index 0000000..c348fae --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/read_ref.rs
@@ -0,0 +1,153 @@ +#![allow(clippy::len_without_is_empty)] + +use core::convert::TryInto; +use core::ops::Range; +use core::{mem, result}; + +use crate::pod::{from_bytes, slice_from_bytes, Pod}; + +type Result<T> = result::Result<T, ()>; + +/// A trait for reading references to [`Pod`] types from a block of data. +/// +/// This allows parsers to handle both of these cases: +/// - the block of data exists in memory, and it is desirable +/// to use references to this block instead of copying it, +/// - the block of data exists in storage, and it is desirable +/// to read on demand to minimize I/O and memory usage. +/// +/// A block of data typically exists in memory as a result of using a memory +/// mapped file, and the crate was written with this use case in mind. +/// Reading the entire file into a `Vec` is also possible, but it often uses +/// more I/O and memory. +/// Both of these are handled by the `ReadRef` implementation for `&[u8]`. +/// +/// For the second use case, the `ReadRef` trait is implemented for +/// [`&ReadCache`](super::ReadCache). This is useful for environments where +/// memory mapped files are not available or not suitable, such as WebAssembly. +/// This differs from reading into a `Vec` in that it only reads the portions +/// of the file that are needed for parsing. +/// +/// The methods accept `self` by value because `Self` is expected to behave +/// similar to a reference: it may be a reference with a lifetime of `'a`, +/// or it may be a wrapper of a reference. +/// +/// The `Clone` and `Copy` bounds are for convenience, and since `Self` is +/// expected to be similar to a reference, these are easily satisfied. +/// +/// Object file parsers typically use offsets to locate the structures +/// in the block, and will most commonly use the `*_at` methods to +/// read a structure at a known offset. +/// +/// Occasionally file parsers will need to treat the block as a stream, +/// and so convenience methods are provided that update an offset with +/// the size that was read. +// +// An alternative would be for methods to accept `&mut self` and use a +// `seek` method instead of the `offset` parameters, but this is less +// convenient for implementers. +pub trait ReadRef<'a>: Clone + Copy { + /// The total size of the block of data. + fn len(self) -> Result<u64>; + + /// Get a reference to a `u8` slice at the given offset. + /// + /// Returns an error if offset or size are out of bounds. + fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]>; + + /// Get a reference to a delimited `u8` slice which starts at range.start. + /// + /// Does not include the delimiter. + /// + /// Returns an error if the range is out of bounds or the delimiter is + /// not found in the range. + fn read_bytes_at_until(self, range: Range<u64>, delimiter: u8) -> Result<&'a [u8]>; + + /// Get a reference to a `u8` slice at the given offset, and update the offset. + /// + /// Returns an error if offset or size are out of bounds. + fn read_bytes(self, offset: &mut u64, size: u64) -> Result<&'a [u8]> { + let bytes = self.read_bytes_at(*offset, size)?; + *offset = offset.wrapping_add(size); + Ok(bytes) + } + + /// Get a reference to a `Pod` type at the given offset, and update the offset. + /// + /// Returns an error if offset or size are out of bounds. + /// + /// The default implementation uses `read_bytes`, and returns an error if + /// `read_bytes` does not return bytes with the correct alignment for `T`. + /// Implementors may want to provide their own implementation that ensures + /// the alignment can be satisfied. Alternatively, only use this method with + /// types that do not need alignment (see the `unaligned` feature of this crate). + fn read<T: Pod>(self, offset: &mut u64) -> Result<&'a T> { + let size = mem::size_of::<T>().try_into().map_err(|_| ())?; + let bytes = self.read_bytes(offset, size)?; + let (t, _) = from_bytes(bytes)?; + Ok(t) + } + + /// Get a reference to a `Pod` type at the given offset. + /// + /// Returns an error if offset or size are out of bounds. + /// + /// Also see the `read` method for information regarding alignment of `T`. + fn read_at<T: Pod>(self, mut offset: u64) -> Result<&'a T> { + self.read(&mut offset) + } + + /// Get a reference to a slice of a `Pod` type at the given offset, and update the offset. + /// + /// Returns an error if offset or size are out of bounds. + /// + /// Also see the `read` method for information regarding alignment of `T`. + fn read_slice<T: Pod>(self, offset: &mut u64, count: usize) -> Result<&'a [T]> { + let size = count + .checked_mul(mem::size_of::<T>()) + .ok_or(())? + .try_into() + .map_err(|_| ())?; + let bytes = self.read_bytes(offset, size)?; + let (t, _) = slice_from_bytes(bytes, count)?; + Ok(t) + } + + /// Get a reference to a slice of a `Pod` type at the given offset. + /// + /// Returns an error if offset or size are out of bounds. + /// + /// Also see the `read` method for information regarding alignment of `T`. + fn read_slice_at<T: Pod>(self, mut offset: u64, count: usize) -> Result<&'a [T]> { + self.read_slice(&mut offset, count) + } +} + +impl<'a> ReadRef<'a> for &'a [u8] { + fn len(self) -> Result<u64> { + self.len().try_into().map_err(|_| ()) + } + + fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]> { + if size == 0 { + return Ok(&[]); + } + + let offset: usize = offset.try_into().map_err(|_| ())?; + let size: usize = size.try_into().map_err(|_| ())?; + self.get(offset..).ok_or(())?.get(..size).ok_or(()) + } + + fn read_bytes_at_until(self, range: Range<u64>, delimiter: u8) -> Result<&'a [u8]> { + let start: usize = range.start.try_into().map_err(|_| ())?; + let end: usize = range.end.try_into().map_err(|_| ())?; + let bytes = self.get(start..end).ok_or(())?; + match memchr::memchr(delimiter, bytes) { + Some(len) => { + // This will never fail. + bytes.get(..len).ok_or(()) + } + None => Err(()), + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/traits.rs b/chromium_crates_io/vendor/object-v0_37/src/read/traits.rs new file mode 100644 index 0000000..cbc93fc --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/traits.rs
@@ -0,0 +1,589 @@ +use alloc::borrow::Cow; +use alloc::vec::Vec; + +use crate::endian::Endianness; +use crate::read::{ + self, Architecture, CodeView, ComdatKind, CompressedData, CompressedFileRange, Export, + FileFlags, Import, ObjectKind, ObjectMap, Relocation, RelocationMap, Result, SectionFlags, + SectionIndex, SectionKind, SegmentFlags, SubArchitecture, SymbolFlags, SymbolIndex, SymbolKind, + SymbolMap, SymbolMapName, SymbolScope, SymbolSection, +}; + +/// An object file. +/// +/// This is the primary trait for the unified read API. +pub trait Object<'data>: read::private::Sealed { + /// A loadable segment in the object file. + type Segment<'file>: ObjectSegment<'data> + where + Self: 'file, + 'data: 'file; + + /// An iterator for the loadable segments in the object file. + type SegmentIterator<'file>: Iterator<Item = Self::Segment<'file>> + where + Self: 'file, + 'data: 'file; + + /// A section in the object file. + type Section<'file>: ObjectSection<'data> + where + Self: 'file, + 'data: 'file; + + /// An iterator for the sections in the object file. + type SectionIterator<'file>: Iterator<Item = Self::Section<'file>> + where + Self: 'file, + 'data: 'file; + + /// A COMDAT section group in the object file. + type Comdat<'file>: ObjectComdat<'data> + where + Self: 'file, + 'data: 'file; + + /// An iterator for the COMDAT section groups in the object file. + type ComdatIterator<'file>: Iterator<Item = Self::Comdat<'file>> + where + Self: 'file, + 'data: 'file; + + /// A symbol in the object file. + type Symbol<'file>: ObjectSymbol<'data> + where + Self: 'file, + 'data: 'file; + + /// An iterator for symbols in the object file. + type SymbolIterator<'file>: Iterator<Item = Self::Symbol<'file>> + where + Self: 'file, + 'data: 'file; + + /// A symbol table in the object file. + type SymbolTable<'file>: ObjectSymbolTable< + 'data, + Symbol = Self::Symbol<'file>, + SymbolIterator = Self::SymbolIterator<'file>, + > + where + Self: 'file, + 'data: 'file; + + /// An iterator for the dynamic relocations in the file. + /// + /// The first field in the item tuple is the address + /// that the relocation applies to. + type DynamicRelocationIterator<'file>: Iterator<Item = (u64, Relocation)> + where + Self: 'file, + 'data: 'file; + + /// Get the architecture type of the file. + fn architecture(&self) -> Architecture; + + /// Get the sub-architecture type of the file if known. + /// + /// A value of `None` has a range of meanings: the file supports all + /// sub-architectures, the file does not explicitly specify a + /// sub-architecture, or the sub-architecture is currently unrecognized. + fn sub_architecture(&self) -> Option<SubArchitecture> { + None + } + + /// Get the endianness of the file. + #[inline] + fn endianness(&self) -> Endianness { + if self.is_little_endian() { + Endianness::Little + } else { + Endianness::Big + } + } + + /// Return true if the file is little endian, false if it is big endian. + fn is_little_endian(&self) -> bool; + + /// Return true if the file can contain 64-bit addresses. + fn is_64(&self) -> bool; + + /// Return the kind of this object. + fn kind(&self) -> ObjectKind; + + /// Get an iterator for the loadable segments in the file. + /// + /// For ELF, this is program headers with type [`PT_LOAD`](crate::elf::PT_LOAD). + /// For Mach-O, this is load commands with type [`LC_SEGMENT`](crate::macho::LC_SEGMENT) + /// or [`LC_SEGMENT_64`](crate::macho::LC_SEGMENT_64). + /// For PE, this is all sections. + fn segments(&self) -> Self::SegmentIterator<'_>; + + /// Get the section named `section_name`, if such a section exists. + /// + /// If `section_name` starts with a '.' then it is treated as a system + /// section name, and is compared using the conventions specific to the + /// object file format. This includes: + /// - if ".debug_str_offsets" is requested for a Mach-O object file, then + /// the actual section name that is searched for is "__debug_str_offs". + /// - if ".debug_info" is requested for an ELF object file, then + /// ".zdebug_info" may be returned (and similarly for other debug + /// sections). Similarly, if ".debug_info" is requested for a Mach-O + /// object file, then "__zdebug_info" may be returned. + /// + /// For some object files, multiple segments may contain sections with the + /// same name. In this case, the first matching section will be used. + /// + /// This method skips over sections with invalid names. + fn section_by_name(&self, section_name: &str) -> Option<Self::Section<'_>> { + self.section_by_name_bytes(section_name.as_bytes()) + } + + /// Like [`Self::section_by_name`], but allows names that are not UTF-8. + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<Self::Section<'file>>; + + /// Get the section at the given index. + /// + /// The meaning of the index depends on the object file. + /// + /// For some object files, this requires iterating through all sections. + /// + /// Returns an error if the index is invalid. + fn section_by_index(&self, index: SectionIndex) -> Result<Self::Section<'_>>; + + /// Get an iterator for the sections in the file. + fn sections(&self) -> Self::SectionIterator<'_>; + + /// Get an iterator for the COMDAT section groups in the file. + fn comdats(&self) -> Self::ComdatIterator<'_>; + + /// Get the debugging symbol table, if any. + fn symbol_table(&self) -> Option<Self::SymbolTable<'_>>; + + /// Get the debugging symbol at the given index. + /// + /// The meaning of the index depends on the object file. + /// + /// Returns an error if the index is invalid. + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol<'_>>; + + /// Get an iterator for the debugging symbols in the file. + /// + /// This may skip over symbols that are malformed or unsupported. + /// + /// For Mach-O files, this does not include STAB entries. + fn symbols(&self) -> Self::SymbolIterator<'_>; + + /// Get the symbol named `symbol_name`, if the symbol exists. + fn symbol_by_name<'file>(&'file self, symbol_name: &str) -> Option<Self::Symbol<'file>> { + self.symbol_by_name_bytes(symbol_name.as_bytes()) + } + + /// Like [`Self::symbol_by_name`], but allows names that are not UTF-8. + fn symbol_by_name_bytes<'file>(&'file self, symbol_name: &[u8]) -> Option<Self::Symbol<'file>> { + self.symbols() + .find(|sym| sym.name_bytes() == Ok(symbol_name)) + } + + /// Get the dynamic linking symbol table, if any. + /// + /// Only ELF has a separate dynamic linking symbol table. + /// Consider using [`Self::exports`] or [`Self::imports`] instead. + fn dynamic_symbol_table(&self) -> Option<Self::SymbolTable<'_>>; + + /// Get an iterator for the dynamic linking symbols in the file. + /// + /// This may skip over symbols that are malformed or unsupported. + /// + /// Only ELF has dynamic linking symbols. + /// Other file formats will return an empty iterator. + /// Consider using [`Self::exports`] or [`Self::imports`] instead. + fn dynamic_symbols(&self) -> Self::SymbolIterator<'_>; + + /// Get the dynamic relocations for this file. + /// + /// Symbol indices in these relocations refer to the dynamic symbol table. + /// + /// Only ELF has dynamic relocations. + fn dynamic_relocations(&self) -> Option<Self::DynamicRelocationIterator<'_>>; + + /// Construct a map from addresses to symbol names. + /// + /// The map will only contain defined text and data symbols. + /// The dynamic symbol table will only be used if there are no debugging symbols. + fn symbol_map(&self) -> SymbolMap<SymbolMapName<'data>> { + let mut symbols = Vec::new(); + if let Some(table) = self.symbol_table().or_else(|| self.dynamic_symbol_table()) { + // Sometimes symbols share addresses. Collect them all then choose the "best". + let mut all_symbols = Vec::new(); + for symbol in table.symbols() { + // Must have an address. + if !symbol.is_definition() { + continue; + } + // Must have a name. + let name = match symbol.name() { + Ok(name) => name, + _ => continue, + }; + if name.is_empty() { + continue; + } + + // Lower is better. + let mut priority = 0u32; + + // Prefer known kind. + match symbol.kind() { + SymbolKind::Text | SymbolKind::Data => {} + SymbolKind::Unknown => priority += 1, + _ => continue, + } + priority *= 2; + + // Prefer global visibility. + priority += match symbol.scope() { + SymbolScope::Unknown => 3, + SymbolScope::Compilation => 2, + SymbolScope::Linkage => 1, + SymbolScope::Dynamic => 0, + }; + priority *= 4; + + // Prefer later entries (earlier symbol is likely to be less specific). + let index = !0 - symbol.index().0; + + // Tuple is ordered for sort. + all_symbols.push((symbol.address(), priority, index, name)); + } + // Unstable sort is okay because tuple includes index. + all_symbols.sort_unstable(); + + let mut previous_address = !0; + for (address, _priority, _index, name) in all_symbols { + if address != previous_address { + symbols.push(SymbolMapName::new(address, name)); + previous_address = address; + } + } + } + SymbolMap::new(symbols) + } + + /// Construct a map from addresses to symbol names and object file names. + /// + /// This is derived from Mach-O STAB entries. + fn object_map(&self) -> ObjectMap<'data> { + ObjectMap::default() + } + + /// Get the imported symbols. + fn imports(&self) -> Result<Vec<Import<'data>>>; + + /// Get the exported symbols that expose both a name and an address. + /// + /// Some file formats may provide other kinds of symbols that can be retrieved using + /// the low level API. + fn exports(&self) -> Result<Vec<Export<'data>>>; + + /// Return true if the file contains DWARF debug information sections, false if not. + fn has_debug_symbols(&self) -> bool; + + /// The UUID from a Mach-O [`LC_UUID`](crate::macho::LC_UUID) load command. + #[inline] + fn mach_uuid(&self) -> Result<Option<[u8; 16]>> { + Ok(None) + } + + /// The build ID from an ELF [`NT_GNU_BUILD_ID`](crate::elf::NT_GNU_BUILD_ID) note. + #[inline] + fn build_id(&self) -> Result<Option<&'data [u8]>> { + Ok(None) + } + + /// The filename and CRC from a `.gnu_debuglink` section. + #[inline] + fn gnu_debuglink(&self) -> Result<Option<(&'data [u8], u32)>> { + Ok(None) + } + + /// The filename and build ID from a `.gnu_debugaltlink` section. + #[inline] + fn gnu_debugaltlink(&self) -> Result<Option<(&'data [u8], &'data [u8])>> { + Ok(None) + } + + /// The filename and GUID from the PE CodeView section. + #[inline] + fn pdb_info(&self) -> Result<Option<CodeView<'_>>> { + Ok(None) + } + + /// Get the base address used for relative virtual addresses. + /// + /// Currently this is only non-zero for PE. + fn relative_address_base(&self) -> u64; + + /// Get the virtual address of the entry point of the binary. + fn entry(&self) -> u64; + + /// File flags that are specific to each file format. + fn flags(&self) -> FileFlags; +} + +/// A loadable segment in an [`Object`]. +/// +/// This trait is part of the unified read API. +pub trait ObjectSegment<'data>: read::private::Sealed { + /// Returns the virtual address of the segment. + fn address(&self) -> u64; + + /// Returns the size of the segment in memory. + fn size(&self) -> u64; + + /// Returns the alignment of the segment in memory. + fn align(&self) -> u64; + + /// Returns the offset and size of the segment in the file. + fn file_range(&self) -> (u64, u64); + + /// Returns a reference to the file contents of the segment. + /// + /// The length of this data may be different from the size of the + /// segment in memory. + fn data(&self) -> Result<&'data [u8]>; + + /// Return the segment data in the given range. + /// + /// Returns `Ok(None)` if the segment does not contain the given range. + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>>; + + /// Returns the name of the segment. + fn name_bytes(&self) -> Result<Option<&[u8]>>; + + /// Returns the name of the segment. + /// + /// Returns an error if the name is not UTF-8. + fn name(&self) -> Result<Option<&str>>; + + /// Return the flags of segment. + fn flags(&self) -> SegmentFlags; +} + +/// A section in an [`Object`]. +/// +/// This trait is part of the unified read API. +pub trait ObjectSection<'data>: read::private::Sealed { + /// An iterator for the relocations for a section. + /// + /// The first field in the item tuple is the section offset + /// that the relocation applies to. + type RelocationIterator: Iterator<Item = (u64, Relocation)>; + + /// Returns the section index. + fn index(&self) -> SectionIndex; + + /// Returns the address of the section. + fn address(&self) -> u64; + + /// Returns the size of the section in memory. + fn size(&self) -> u64; + + /// Returns the alignment of the section in memory. + fn align(&self) -> u64; + + /// Returns offset and size of on-disk segment (if any). + fn file_range(&self) -> Option<(u64, u64)>; + + /// Returns the raw contents of the section. + /// + /// The length of this data may be different from the size of the + /// section in memory. + /// + /// This does not do any decompression. + fn data(&self) -> Result<&'data [u8]>; + + /// Return the raw contents of the section data in the given range. + /// + /// This does not do any decompression. + /// + /// Returns `Ok(None)` if the section does not contain the given range. + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>>; + + /// Returns the potentially compressed file range of the section, + /// along with information about the compression. + fn compressed_file_range(&self) -> Result<CompressedFileRange>; + + /// Returns the potentially compressed contents of the section, + /// along with information about the compression. + fn compressed_data(&self) -> Result<CompressedData<'data>>; + + /// Returns the uncompressed contents of the section. + /// + /// The length of this data may be different from the size of the + /// section in memory. + /// + /// If no compression is detected, then returns the data unchanged. + /// Returns `Err` if decompression fails. + fn uncompressed_data(&self) -> Result<Cow<'data, [u8]>> { + self.compressed_data()?.decompress() + } + + /// Returns the name of the section. + fn name_bytes(&self) -> Result<&'data [u8]>; + + /// Returns the name of the section. + /// + /// Returns an error if the name is not UTF-8. + fn name(&self) -> Result<&'data str>; + + /// Returns the name of the segment for this section. + fn segment_name_bytes(&self) -> Result<Option<&[u8]>>; + + /// Returns the name of the segment for this section. + /// + /// Returns an error if the name is not UTF-8. + fn segment_name(&self) -> Result<Option<&str>>; + + /// Return the kind of this section. + fn kind(&self) -> SectionKind; + + /// Get the relocations for this section. + fn relocations(&self) -> Self::RelocationIterator; + + /// Construct a relocation map for this section. + fn relocation_map(&self) -> Result<RelocationMap>; + + /// Section flags that are specific to each file format. + fn flags(&self) -> SectionFlags; +} + +/// A COMDAT section group in an [`Object`]. +/// +/// This trait is part of the unified read API. +pub trait ObjectComdat<'data>: read::private::Sealed { + /// An iterator for the sections in the section group. + type SectionIterator: Iterator<Item = SectionIndex>; + + /// Returns the COMDAT selection kind. + fn kind(&self) -> ComdatKind; + + /// Returns the index of the symbol used for the name of COMDAT section group. + fn symbol(&self) -> SymbolIndex; + + /// Returns the name of the COMDAT section group. + fn name_bytes(&self) -> Result<&'data [u8]>; + + /// Returns the name of the COMDAT section group. + /// + /// Returns an error if the name is not UTF-8. + fn name(&self) -> Result<&'data str>; + + /// Get the sections in this section group. + fn sections(&self) -> Self::SectionIterator; +} + +/// A symbol table in an [`Object`]. +/// +/// This trait is part of the unified read API. +pub trait ObjectSymbolTable<'data>: read::private::Sealed { + /// A symbol table entry. + type Symbol: ObjectSymbol<'data>; + + /// An iterator for the symbols in a symbol table. + type SymbolIterator: Iterator<Item = Self::Symbol>; + + /// Get an iterator for the symbols in the table. + /// + /// This may skip over symbols that are malformed or unsupported. + fn symbols(&self) -> Self::SymbolIterator; + + /// Get the symbol at the given index. + /// + /// The meaning of the index depends on the object file. + /// + /// Returns an error if the index is invalid. + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol>; +} + +/// A symbol table entry in an [`Object`]. +/// +/// This trait is part of the unified read API. +pub trait ObjectSymbol<'data>: read::private::Sealed { + /// The index of the symbol. + fn index(&self) -> SymbolIndex; + + /// The name of the symbol. + fn name_bytes(&self) -> Result<&'data [u8]>; + + /// The name of the symbol. + /// + /// Returns an error if the name is not UTF-8. + fn name(&self) -> Result<&'data str>; + + /// The address of the symbol. May be zero if the address is unknown. + fn address(&self) -> u64; + + /// The size of the symbol. May be zero if the size is unknown. + fn size(&self) -> u64; + + /// Return the kind of this symbol. + fn kind(&self) -> SymbolKind; + + /// Returns the section where the symbol is defined. + fn section(&self) -> SymbolSection; + + /// Returns the section index for the section containing this symbol. + /// + /// May return `None` if the symbol is not defined in a section. + fn section_index(&self) -> Option<SectionIndex> { + self.section().index() + } + + /// Return true if the symbol is undefined. + fn is_undefined(&self) -> bool; + + /// Return true if the symbol is a definition of a function or data object + /// that has a known address. + /// + /// This is primarily used to implement [`Object::symbol_map`]. + fn is_definition(&self) -> bool; + + /// Return true if the symbol is common data. + /// + /// Note: does not check for [`SymbolSection::Section`] with [`SectionKind::Common`]. + fn is_common(&self) -> bool; + + /// Return true if the symbol is weak. + fn is_weak(&self) -> bool; + + /// Returns the symbol scope. + fn scope(&self) -> SymbolScope; + + /// Return true if the symbol visible outside of the compilation unit. + /// + /// This treats [`SymbolScope::Unknown`] as global. + fn is_global(&self) -> bool; + + /// Return true if the symbol is only visible within the compilation unit. + fn is_local(&self) -> bool; + + /// Symbol flags that are specific to each file format. + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex>; +} + +/// An iterator for files that don't have dynamic relocations. +#[derive(Debug)] +pub struct NoDynamicRelocationIterator; + +impl Iterator for NoDynamicRelocationIterator { + type Item = (u64, Relocation); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + None + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/util.rs b/chromium_crates_io/vendor/object-v0_37/src/read/util.rs new file mode 100644 index 0000000..93dbc17 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/util.rs
@@ -0,0 +1,425 @@ +use alloc::string::String; +use core::convert::TryInto; +use core::fmt; +use core::marker::PhantomData; + +use crate::pod::{from_bytes, slice_from_bytes, Pod}; +use crate::read::ReadRef; + +/// A newtype for byte slices. +/// +/// It has these important features: +/// - no methods that can panic, such as `Index` +/// - convenience methods for `Pod` types +/// - a useful `Debug` implementation +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub struct Bytes<'data>(pub &'data [u8]); + +impl<'data> fmt::Debug for Bytes<'data> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_list_bytes(self.0, fmt) + } +} + +impl<'data> Bytes<'data> { + /// Return the length of the byte slice. + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Return true if the byte slice is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Skip over the given number of bytes at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes. + #[inline] + pub fn skip(&mut self, offset: usize) -> Result<(), ()> { + match self.0.get(offset..) { + Some(tail) => { + self.0 = tail; + Ok(()) + } + None => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to the given number of bytes at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes. + #[inline] + pub fn read_bytes(&mut self, count: usize) -> Result<Bytes<'data>, ()> { + match (self.0.get(..count), self.0.get(count..)) { + (Some(head), Some(tail)) => { + self.0 = tail; + Ok(Bytes(head)) + } + _ => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to the given number of bytes at the given offset of the byte slice. + /// + /// Returns an error if the offset is invalid or there are too few bytes. + #[inline] + pub fn read_bytes_at(mut self, offset: usize, count: usize) -> Result<Bytes<'data>, ()> { + self.skip(offset)?; + self.read_bytes(count) + } + + /// Return a reference to a `Pod` struct at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes or the slice is incorrectly aligned. + #[inline] + pub fn read<T: Pod>(&mut self) -> Result<&'data T, ()> { + match from_bytes(self.0) { + Ok((value, tail)) => { + self.0 = tail; + Ok(value) + } + Err(()) => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to a `Pod` struct at the given offset of the byte slice. + /// + /// Returns an error if there are too few bytes or the offset is incorrectly aligned. + #[inline] + pub fn read_at<T: Pod>(mut self, offset: usize) -> Result<&'data T, ()> { + self.skip(offset)?; + self.read() + } + + /// Return a reference to a slice of `Pod` structs at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes or the offset is incorrectly aligned. + #[inline] + pub fn read_slice<T: Pod>(&mut self, count: usize) -> Result<&'data [T], ()> { + match slice_from_bytes(self.0, count) { + Ok((value, tail)) => { + self.0 = tail; + Ok(value) + } + Err(()) => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to a slice of `Pod` structs at the given offset of the byte slice. + /// + /// Returns an error if there are too few bytes or the offset is incorrectly aligned. + #[inline] + pub fn read_slice_at<T: Pod>(mut self, offset: usize, count: usize) -> Result<&'data [T], ()> { + self.skip(offset)?; + self.read_slice(count) + } + + /// Read a null terminated string. + /// + /// Does not assume any encoding. + /// Reads past the null byte, but doesn't return it. + #[inline] + pub fn read_string(&mut self) -> Result<&'data [u8], ()> { + match memchr::memchr(b'\0', self.0) { + Some(null) => { + // These will never fail. + let bytes = self.read_bytes(null)?; + self.skip(1)?; + Ok(bytes.0) + } + None => { + self.0 = &[]; + Err(()) + } + } + } + + /// Read a null terminated string at an offset. + /// + /// Does not assume any encoding. Does not return the null byte. + #[inline] + pub fn read_string_at(mut self, offset: usize) -> Result<&'data [u8], ()> { + self.skip(offset)?; + self.read_string() + } + + /// Read an unsigned LEB128 number. + pub fn read_uleb128(&mut self) -> Result<u64, ()> { + let mut result = 0; + let mut shift = 0; + + loop { + let byte = *self.read::<u8>()?; + if shift == 63 && byte != 0x00 && byte != 0x01 { + return Err(()); + } + result |= u64::from(byte & 0x7f) << shift; + shift += 7; + + if byte & 0x80 == 0 { + return Ok(result); + } + } + } + + /// Read a signed LEB128 number. + pub fn read_sleb128(&mut self) -> Result<i64, ()> { + let mut result = 0; + let mut shift = 0; + + loop { + let byte = *self.read::<u8>()?; + if shift == 63 && byte != 0x00 && byte != 0x7f { + return Err(()); + } + result |= i64::from(byte & 0x7f) << shift; + shift += 7; + + if byte & 0x80 == 0 { + if shift < 64 && (byte & 0x40) != 0 { + // Sign extend the result. + result |= !0 << shift; + } + return Ok(result); + } + } + } +} + +// Only for Debug impl of `Bytes`. +fn debug_list_bytes(bytes: &[u8], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = fmt.debug_list(); + list.entries(bytes.iter().take(8).copied().map(DebugByte)); + if bytes.len() > 8 { + list.entry(&DebugLen(bytes.len())); + } + list.finish() +} + +struct DebugByte(u8); + +impl fmt::Debug for DebugByte { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "0x{:02x}", self.0) + } +} + +struct DebugLen(usize); + +impl fmt::Debug for DebugLen { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "...; {}", self.0) + } +} + +/// A newtype for byte strings. +/// +/// For byte slices that are strings of an unknown encoding. +/// +/// Provides a `Debug` implementation that interprets the bytes as UTF-8. +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub(crate) struct ByteString<'data>(pub &'data [u8]); + +impl<'data> fmt::Debug for ByteString<'data> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"{}\"", String::from_utf8_lossy(self.0)) + } +} + +#[allow(dead_code)] +#[inline] +pub(crate) fn align(offset: usize, size: usize) -> usize { + (offset + (size - 1)) & !(size - 1) +} + +#[allow(dead_code)] +pub(crate) fn data_range( + data: &[u8], + data_address: u64, + range_address: u64, + size: u64, +) -> Option<&[u8]> { + let offset = range_address.checked_sub(data_address)?; + data.get(offset.try_into().ok()?..)? + .get(..size.try_into().ok()?) +} + +/// A table of zero-terminated strings. +/// +/// This is used by most file formats for strings such as section names and symbol names. +#[derive(Debug, Clone, Copy)] +pub struct StringTable<'data, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + data: Option<R>, + start: u64, + end: u64, + marker: PhantomData<&'data ()>, +} + +impl<'data, R: ReadRef<'data>> StringTable<'data, R> { + /// Interpret the given data as a string table. + pub fn new(data: R, start: u64, end: u64) -> Self { + StringTable { + data: Some(data), + start, + end, + marker: PhantomData, + } + } + + /// Return the string at the given offset. + pub fn get(&self, offset: u32) -> Result<&'data [u8], ()> { + match self.data { + Some(data) => { + let r_start = self.start.checked_add(offset.into()).ok_or(())?; + data.read_bytes_at_until(r_start..self.end, 0) + } + None => Err(()), + } + } +} + +impl<'data, R: ReadRef<'data>> Default for StringTable<'data, R> { + fn default() -> Self { + StringTable { + data: None, + start: 0, + end: 0, + marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pod::bytes_of; + + #[test] + fn bytes() { + let x = u32::to_be(0x0123_4567); + let data = Bytes(bytes_of(&x)); + + let mut bytes = data; + assert_eq!(bytes.skip(0), Ok(())); + assert_eq!(bytes, data); + + let mut bytes = data; + assert_eq!(bytes.skip(4), Ok(())); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.skip(5), Err(())); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_bytes(0), Ok(Bytes(&[]))); + assert_eq!(bytes, data); + + let mut bytes = data; + assert_eq!(bytes.read_bytes(4), Ok(data)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_bytes(5), Err(())); + assert_eq!(bytes, Bytes(&[])); + + assert_eq!(data.read_bytes_at(0, 0), Ok(Bytes(&[]))); + assert_eq!(data.read_bytes_at(4, 0), Ok(Bytes(&[]))); + assert_eq!(data.read_bytes_at(0, 4), Ok(data)); + assert_eq!(data.read_bytes_at(1, 4), Err(())); + + let mut bytes = data; + assert_eq!(bytes.read::<u16>(), Ok(&u16::to_be(0x0123))); + assert_eq!(bytes, Bytes(&[0x45, 0x67])); + assert_eq!(data.read_at::<u16>(2), Ok(&u16::to_be(0x4567))); + assert_eq!(data.read_at::<u16>(3), Err(())); + assert_eq!(data.read_at::<u16>(4), Err(())); + + let mut bytes = data; + assert_eq!(bytes.read::<u32>(), Ok(&x)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read::<u64>(), Err(())); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_slice::<u8>(0), Ok(&[][..])); + assert_eq!(bytes, data); + + let mut bytes = data; + assert_eq!(bytes.read_slice::<u8>(4), Ok(data.0)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_slice::<u8>(5), Err(())); + assert_eq!(bytes, Bytes(&[])); + + assert_eq!(data.read_slice_at::<u8>(0, 0), Ok(&[][..])); + assert_eq!(data.read_slice_at::<u8>(4, 0), Ok(&[][..])); + assert_eq!(data.read_slice_at::<u8>(0, 4), Ok(data.0)); + assert_eq!(data.read_slice_at::<u8>(1, 4), Err(())); + + let data = Bytes(&[0x01, 0x02, 0x00, 0x04]); + + let mut bytes = data; + assert_eq!(bytes.read_string(), Ok(&data.0[..2])); + assert_eq!(bytes.0, &data.0[3..]); + + let mut bytes = data; + bytes.skip(3).unwrap(); + assert_eq!(bytes.read_string(), Err(())); + assert_eq!(bytes.0, &[]); + + assert_eq!(data.read_string_at(0), Ok(&data.0[..2])); + assert_eq!(data.read_string_at(1), Ok(&data.0[1..2])); + assert_eq!(data.read_string_at(2), Ok(&[][..])); + assert_eq!(data.read_string_at(3), Err(())); + } + + #[test] + fn bytes_debug() { + assert_eq!(format!("{:?}", Bytes(&[])), "[]"); + assert_eq!(format!("{:?}", Bytes(&[0x01])), "[0x01]"); + assert_eq!( + format!( + "{:?}", + Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) + ), + "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]" + ); + assert_eq!( + format!( + "{:?}", + Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]) + ), + "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ...; 9]" + ); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/wasm.rs b/chromium_crates_io/vendor/object-v0_37/src/read/wasm.rs new file mode 100644 index 0000000..7062574 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/wasm.rs
@@ -0,0 +1,1147 @@ +//! Support for reading Wasm files. +//! +//! [`WasmFile`] implements the [`Object`] trait for Wasm files. +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::marker::PhantomData; +use core::ops::Range; +use core::{slice, str}; +use wasmparser as wp; + +use crate::read::{ + self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags, + Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectSection, + ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Relocation, RelocationMap, + Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, + SymbolKind, SymbolScope, SymbolSection, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(usize)] +enum SectionId { + Custom = 0, + Type = 1, + Import = 2, + Function = 3, + Table = 4, + Memory = 5, + Global = 6, + Export = 7, + Start = 8, + Element = 9, + Code = 10, + Data = 11, + DataCount = 12, + Tag = 13, +} +// Update this constant when adding new section id: +const MAX_SECTION_ID: usize = SectionId::Tag as usize; + +/// A WebAssembly object file. +#[derive(Debug)] +pub struct WasmFile<'data, R = &'data [u8]> { + data: &'data [u8], + has_memory64: bool, + // All sections, including custom sections. + sections: Vec<SectionHeader<'data>>, + // Indices into `sections` of sections with a non-zero id. + id_sections: Box<[Option<usize>; MAX_SECTION_ID + 1]>, + // Whether the file has DWARF information. + has_debug_symbols: bool, + // Symbols collected from imports, exports, code and name sections. + symbols: Vec<WasmSymbolInternal<'data>>, + // Address of the function body for the entry point. + entry: u64, + marker: PhantomData<R>, +} + +#[derive(Debug)] +struct SectionHeader<'data> { + id: SectionId, + range: Range<usize>, + name: &'data str, +} + +#[derive(Clone)] +enum LocalFunctionKind { + Unknown, + Exported, +} + +impl<T> ReadError<T> for wasmparser::Result<T> { + fn read_error(self, error: &'static str) -> Result<T> { + self.map_err(|_| Error(error)) + } +} + +impl<'data, R: ReadRef<'data>> WasmFile<'data, R> { + /// Parse the raw wasm data. + pub fn parse(data: R) -> Result<Self> { + let len = data.len().read_error("Unknown Wasm file size")?; + let data = data.read_bytes_at(0, len).read_error("Wasm read failed")?; + let parser = wp::Parser::new(0).parse_all(data); + + let mut file = WasmFile { + data, + has_memory64: false, + sections: Vec::new(), + id_sections: Default::default(), + has_debug_symbols: false, + symbols: Vec::new(), + entry: 0, + marker: PhantomData, + }; + + let mut main_file_symbol = Some(WasmSymbolInternal { + name: "", + address: 0, + size: 0, + kind: SymbolKind::File, + section: SymbolSection::None, + scope: SymbolScope::Compilation, + weak: false, + }); + + let mut local_func_kinds = Vec::new(); + let mut entry_func_id = None; + let mut code_range_start = 0; + let mut code_ranges = Vec::new(); + let mut imports = None; + let mut exports = None; + let mut names = None; + let mut symbols = None; + // One-to-one mapping of globals to their value (if the global is a constant integer). + let mut global_values = Vec::new(); + + for payload in parser { + let payload = payload.read_error("Invalid Wasm section header")?; + + match payload { + wp::Payload::Version { encoding, .. } => { + if encoding != wp::Encoding::Module { + return Err(Error("Unsupported Wasm encoding")); + } + } + wp::Payload::TypeSection(section) => { + file.add_section(SectionId::Type, section.range(), ""); + } + wp::Payload::ImportSection(section) => { + file.add_section(SectionId::Import, section.range(), ""); + imports = Some(section); + } + wp::Payload::FunctionSection(section) => { + file.add_section(SectionId::Function, section.range(), ""); + local_func_kinds = + vec![LocalFunctionKind::Unknown; section.into_iter().count()]; + } + wp::Payload::TableSection(section) => { + file.add_section(SectionId::Table, section.range(), ""); + } + wp::Payload::MemorySection(section) => { + file.add_section(SectionId::Memory, section.range(), ""); + for memory in section { + let memory = memory.read_error("Couldn't read a memory item")?; + file.has_memory64 |= memory.memory64; + } + } + wp::Payload::GlobalSection(section) => { + file.add_section(SectionId::Global, section.range(), ""); + for global in section { + let global = global.read_error("Couldn't read a global item")?; + let mut address = None; + if !global.ty.mutable { + // There should be exactly one instruction. + let init = global.init_expr.get_operators_reader().read(); + address = match init.read_error("Couldn't read a global init expr")? { + wp::Operator::I32Const { value } => Some(value as u64), + wp::Operator::I64Const { value } => Some(value as u64), + _ => None, + }; + } + global_values.push(address); + } + } + wp::Payload::ExportSection(section) => { + file.add_section(SectionId::Export, section.range(), ""); + exports = Some(section); + } + wp::Payload::StartSection { func, range, .. } => { + file.add_section(SectionId::Start, range, ""); + entry_func_id = Some(func); + } + wp::Payload::ElementSection(section) => { + file.add_section(SectionId::Element, section.range(), ""); + } + wp::Payload::CodeSectionStart { range, .. } => { + code_range_start = range.start; + file.add_section(SectionId::Code, range, ""); + } + wp::Payload::CodeSectionEntry(body) => { + let range = body.range(); + let address = range.start as u64 - code_range_start as u64; + let size = (range.end - range.start) as u64; + code_ranges.push((address, size)); + } + wp::Payload::DataSection(section) => { + file.add_section(SectionId::Data, section.range(), ""); + } + wp::Payload::DataCountSection { range, .. } => { + file.add_section(SectionId::DataCount, range, ""); + } + wp::Payload::TagSection(section) => { + file.add_section(SectionId::Tag, section.range(), ""); + } + wp::Payload::CustomSection(section) => { + let name = section.name(); + let size = section.data().len(); + let mut range = section.range(); + range.start = range.end - size; + file.add_section(SectionId::Custom, range, name); + if name == "name" { + let reader = wp::BinaryReader::new(section.data(), section.data_offset()); + names = Some(wp::NameSectionReader::new(reader)); + } else if name == "linking" { + // https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md + let reader = wp::BinaryReader::new(section.data(), section.data_offset()); + let linking = wp::LinkingSectionReader::new(reader) + .read_error("Invalid Wasm linking section")?; + for subsection in linking { + let subsection = + subsection.read_error("Invalid Wasm linking subsection")?; + if let wp::Linking::SymbolTable(s) = subsection { + symbols = Some(s); + } + } + } else if name.starts_with(".debug_") { + file.has_debug_symbols = true; + } + } + _ => {} + } + } + + if let Some(entry_func_id) = entry_func_id { + if let Some(range) = code_ranges.get(entry_func_id as usize) { + file.entry = range.0; + } + } + + let mut import_func_names = Vec::new(); + let mut import_global_names = Vec::new(); + if let Some(imports) = imports { + let mut last_module_name = None; + + for import in imports { + let import = import.read_error("Couldn't read an import item")?; + let kind = match import.ty { + wp::TypeRef::Func(_) => { + import_func_names.push(import.name); + SymbolKind::Text + } + wp::TypeRef::Memory(memory) => { + file.has_memory64 |= memory.memory64; + SymbolKind::Data + } + wp::TypeRef::Global(_) => { + import_global_names.push(import.name); + SymbolKind::Data + } + wp::TypeRef::Table(_) => SymbolKind::Data, + wp::TypeRef::Tag(_) => SymbolKind::Unknown, + }; + + if symbols.is_some() { + // We have a symbol table, so we don't need to add symbols for imports. + // TODO: never add symbols for imports. Return them via Object::imports instead. + continue; + } + + let module_name = import.module; + if last_module_name != Some(module_name) { + file.symbols.push(WasmSymbolInternal { + name: module_name, + address: 0, + size: 0, + kind: SymbolKind::File, + section: SymbolSection::None, + scope: SymbolScope::Dynamic, + weak: false, + }); + last_module_name = Some(module_name); + } + + file.symbols.push(WasmSymbolInternal { + name: import.name, + address: 0, + size: 0, + kind, + section: SymbolSection::Undefined, + scope: SymbolScope::Dynamic, + weak: false, + }); + } + } + + if let Some(symbols) = symbols { + // We have a symbol table, so we don't need to add symbols for locals or exports. + // These sections shouldn't be present at the same time as a symbol table anyway. + // TODO: never add symbols for exports. Return them via Object::exports instead. + exports = None; + names = None; + + for symbol in symbols { + let symbol = symbol.read_error("Invalid Wasm linking symbol")?; + let flags = match symbol { + wp::SymbolInfo::Func { flags, .. } => flags, + wp::SymbolInfo::Data { flags, .. } => flags, + wp::SymbolInfo::Global { flags, .. } => flags, + wp::SymbolInfo::Section { flags, .. } => flags, + wp::SymbolInfo::Event { flags, .. } => flags, + wp::SymbolInfo::Table { flags, .. } => flags, + }; + let kind = if flags.contains(wp::SymbolFlags::TLS) { + SymbolKind::Tls + } else { + match symbol { + wp::SymbolInfo::Func { .. } => SymbolKind::Text, + wp::SymbolInfo::Data { .. } => SymbolKind::Data, + wp::SymbolInfo::Global { .. } => SymbolKind::Data, + wp::SymbolInfo::Section { .. } => SymbolKind::Section, + wp::SymbolInfo::Event { .. } => SymbolKind::Unknown, + wp::SymbolInfo::Table { .. } => SymbolKind::Data, + } + }; + let section = if flags.contains(wp::SymbolFlags::UNDEFINED) { + SymbolSection::Undefined + } else if flags.contains(wp::SymbolFlags::ABSOLUTE) { + SymbolSection::Absolute + } else { + match symbol { + wp::SymbolInfo::Func { .. } => { + SymbolSection::Section(SectionIndex(SectionId::Code as usize)) + } + _ => { + // TODO: anything that is defined should have a known section. + // Additionally, address and size should be within this section. + SymbolSection::Unknown + } + } + }; + let scope = if flags.contains(wp::SymbolFlags::BINDING_LOCAL) { + SymbolScope::Compilation + } else if flags.contains(wp::SymbolFlags::VISIBILITY_HIDDEN) { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + }; + let weak = flags.contains(wp::SymbolFlags::BINDING_WEAK); + + let mut address = 0; + let mut size = 0; + let name = match symbol { + wp::SymbolInfo::Func { + index, mut name, .. + } => { + if let Some(local_index) = index.checked_sub(import_func_names.len() as u32) + { + if let Some(range) = code_ranges.get(local_index as usize).copied() { + address = range.0; + size = range.1; + } + } else { + if !flags.contains(wp::SymbolFlags::EXPLICIT_NAME) { + name = Some(import_func_names[index as usize]); + } + } + name + } + wp::SymbolInfo::Data { name, symbol, .. } => { + if let Some(symbol) = symbol { + // TODO: this is an offset within a data segment. + // This may need to be changed to be an offset within the data section. + address = symbol.offset.into(); + size = symbol.size.into(); + } + Some(name) + } + wp::SymbolInfo::Section { .. } => { + // TODO: find the section name + None + } + wp::SymbolInfo::Global { name, index, .. } => { + if !flags.contains(wp::SymbolFlags::EXPLICIT_NAME) { + import_global_names.get(index as usize).copied() + } else { + name + } + } + wp::SymbolInfo::Event { name, .. } | wp::SymbolInfo::Table { name, .. } => name, + }; + + file.symbols.push(WasmSymbolInternal { + name: name.unwrap_or(""), + address, + size, + kind, + section, + scope, + weak, + }); + } + } + + if let Some(exports) = exports { + if let Some(main_file_symbol) = main_file_symbol.take() { + file.symbols.push(main_file_symbol); + } + + for export in exports { + let export = export.read_error("Couldn't read an export item")?; + + let (kind, section_idx) = match export.kind { + wp::ExternalKind::Func => { + if let Some(local_func_id) = + export.index.checked_sub(import_func_names.len() as u32) + { + let local_func_kind = local_func_kinds + .get_mut(local_func_id as usize) + .read_error("Invalid Wasm export index")?; + *local_func_kind = LocalFunctionKind::Exported; + } + (SymbolKind::Text, SectionId::Code) + } + wp::ExternalKind::Table + | wp::ExternalKind::Memory + | wp::ExternalKind::Global => (SymbolKind::Data, SectionId::Data), + // TODO + wp::ExternalKind::Tag => continue, + }; + + // Try to guess the symbol address. Rust and C export a global containing + // the address in linear memory of the symbol. + let mut address = 0; + let mut size = 0; + if export.kind == wp::ExternalKind::Global { + if let Some(&Some(x)) = global_values.get(export.index as usize) { + address = x; + } + } + if export.kind == wp::ExternalKind::Func { + if let Some(local_func_id) = + export.index.checked_sub(import_func_names.len() as u32) + { + if let Some(range) = code_ranges.get(local_func_id as usize) { + address = range.0; + size = range.1 + } + } + } + + file.symbols.push(WasmSymbolInternal { + name: export.name, + address, + size, + kind, + section: SymbolSection::Section(SectionIndex(section_idx as usize)), + scope: SymbolScope::Dynamic, + weak: false, + }); + } + } + if let Some(names) = names { + if let Some(main_file_symbol) = main_file_symbol.take() { + file.symbols.push(main_file_symbol); + } + for name in names { + let name = name.read_error("Invalid wasm name section")?; + let wp::Name::Function(name_map) = name else { + continue; + }; + for naming in name_map { + let naming = naming.read_error("Couldn't read a function name")?; + let Some(local_index) = + naming.index.checked_sub(import_func_names.len() as u32) + else { + continue; + }; + let Some(LocalFunctionKind::Unknown) = + local_func_kinds.get(local_index as usize) + else { + continue; + }; + let Some((address, size)) = code_ranges.get(local_index as usize).copied() + else { + continue; + }; + file.symbols.push(WasmSymbolInternal { + name: naming.name, + address, + size, + kind: SymbolKind::Text, + section: SymbolSection::Section(SectionIndex(SectionId::Code as usize)), + scope: SymbolScope::Compilation, + weak: false, + }); + } + } + } + + Ok(file) + } + + fn add_section(&mut self, id: SectionId, range: Range<usize>, name: &'data str) { + let section = SectionHeader { id, range, name }; + self.id_sections[id as usize] = Some(self.sections.len()); + self.sections.push(section); + } +} + +impl<'data, R> read::private::Sealed for WasmFile<'data, R> {} + +impl<'data, R: ReadRef<'data>> Object<'data> for WasmFile<'data, R> { + type Segment<'file> + = WasmSegment<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SegmentIterator<'file> + = WasmSegmentIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type Section<'file> + = WasmSection<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type SectionIterator<'file> + = WasmSectionIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type Comdat<'file> + = WasmComdat<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type ComdatIterator<'file> + = WasmComdatIterator<'data, 'file, R> + where + Self: 'file, + 'data: 'file; + type Symbol<'file> + = WasmSymbol<'data, 'file> + where + Self: 'file, + 'data: 'file; + type SymbolIterator<'file> + = WasmSymbolIterator<'data, 'file> + where + Self: 'file, + 'data: 'file; + type SymbolTable<'file> + = WasmSymbolTable<'data, 'file> + where + Self: 'file, + 'data: 'file; + type DynamicRelocationIterator<'file> + = NoDynamicRelocationIterator + where + Self: 'file, + 'data: 'file; + + #[inline] + fn architecture(&self) -> Architecture { + if self.has_memory64 { + Architecture::Wasm64 + } else { + Architecture::Wasm32 + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + true + } + + #[inline] + fn is_64(&self) -> bool { + self.has_memory64 + } + + fn kind(&self) -> ObjectKind { + // TODO: check for `linking` custom section + ObjectKind::Unknown + } + + fn segments(&self) -> Self::SegmentIterator<'_> { + WasmSegmentIterator { file: self } + } + + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<WasmSection<'data, 'file, R>> { + self.sections() + .find(|section| section.name_bytes() == Ok(section_name)) + } + + fn section_by_index(&self, index: SectionIndex) -> Result<WasmSection<'data, '_, R>> { + // TODO: Missing sections should return an empty section. + let id_section = self + .id_sections + .get(index.0) + .and_then(|x| *x) + .read_error("Invalid Wasm section index")?; + let section = self.sections.get(id_section).unwrap(); + Ok(WasmSection { + file: self, + section, + }) + } + + fn sections(&self) -> Self::SectionIterator<'_> { + WasmSectionIterator { + file: self, + sections: self.sections.iter(), + } + } + + fn comdats(&self) -> Self::ComdatIterator<'_> { + WasmComdatIterator { file: self } + } + + #[inline] + fn symbol_by_index(&self, index: SymbolIndex) -> Result<WasmSymbol<'data, '_>> { + let symbol = self + .symbols + .get(index.0) + .read_error("Invalid Wasm symbol index")?; + Ok(WasmSymbol { index, symbol }) + } + + fn symbols(&self) -> Self::SymbolIterator<'_> { + WasmSymbolIterator { + symbols: self.symbols.iter().enumerate(), + } + } + + fn symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> { + Some(WasmSymbolTable { + symbols: &self.symbols, + }) + } + + fn dynamic_symbols(&self) -> Self::SymbolIterator<'_> { + WasmSymbolIterator { + symbols: [].iter().enumerate(), + } + } + + #[inline] + fn dynamic_symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> { + None + } + + #[inline] + fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> { + None + } + + fn imports(&self) -> Result<Vec<Import<'data>>> { + // TODO: return entries in the import section + Ok(Vec::new()) + } + + fn exports(&self) -> Result<Vec<Export<'data>>> { + // TODO: return entries in the export section + Ok(Vec::new()) + } + + fn has_debug_symbols(&self) -> bool { + self.has_debug_symbols + } + + fn relative_address_base(&self) -> u64 { + 0 + } + + #[inline] + fn entry(&self) -> u64 { + self.entry + } + + #[inline] + fn flags(&self) -> FileFlags { + FileFlags::None + } +} + +/// An iterator for the segments in a [`WasmFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct WasmSegmentIterator<'data, 'file, R = &'data [u8]> { + #[allow(unused)] + file: &'file WasmFile<'data, R>, +} + +impl<'data, 'file, R> Iterator for WasmSegmentIterator<'data, 'file, R> { + type Item = WasmSegment<'data, 'file, R>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A segment in a [`WasmFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct WasmSegment<'data, 'file, R = &'data [u8]> { + #[allow(unused)] + file: &'file WasmFile<'data, R>, +} + +impl<'data, 'file, R> read::private::Sealed for WasmSegment<'data, 'file, R> {} + +impl<'data, 'file, R> ObjectSegment<'data> for WasmSegment<'data, 'file, R> { + #[inline] + fn address(&self) -> u64 { + unreachable!() + } + + #[inline] + fn size(&self) -> u64 { + unreachable!() + } + + #[inline] + fn align(&self) -> u64 { + unreachable!() + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + unreachable!() + } + + fn data(&self) -> Result<&'data [u8]> { + unreachable!() + } + + fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> { + unreachable!() + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + unreachable!() + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + unreachable!() + } + + #[inline] + fn flags(&self) -> SegmentFlags { + unreachable!() + } +} + +/// An iterator for the sections in a [`WasmFile`]. +#[derive(Debug)] +pub struct WasmSectionIterator<'data, 'file, R = &'data [u8]> { + file: &'file WasmFile<'data, R>, + sections: slice::Iter<'file, SectionHeader<'data>>, +} + +impl<'data, 'file, R> Iterator for WasmSectionIterator<'data, 'file, R> { + type Item = WasmSection<'data, 'file, R>; + + fn next(&mut self) -> Option<Self::Item> { + let section = self.sections.next()?; + Some(WasmSection { + file: self.file, + section, + }) + } +} + +/// A section in a [`WasmFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct WasmSection<'data, 'file, R = &'data [u8]> { + file: &'file WasmFile<'data, R>, + section: &'file SectionHeader<'data>, +} + +impl<'data, 'file, R> read::private::Sealed for WasmSection<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for WasmSection<'data, 'file, R> { + type RelocationIterator = WasmRelocationIterator<'data, 'file, R>; + + #[inline] + fn index(&self) -> SectionIndex { + // Note that we treat all custom sections as index 0. + // This is ok because they are never looked up by index. + SectionIndex(self.section.id as usize) + } + + #[inline] + fn address(&self) -> u64 { + 0 + } + + #[inline] + fn size(&self) -> u64 { + let range = &self.section.range; + (range.end - range.start) as u64 + } + + #[inline] + fn align(&self) -> u64 { + 1 + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + let range = &self.section.range; + Some((range.start as _, range.end as _)) + } + + #[inline] + fn data(&self) -> Result<&'data [u8]> { + let range = &self.section.range; + self.file + .data + .read_bytes_at(range.start as u64, range.end as u64 - range.start as u64) + .read_error("Invalid Wasm section size or offset") + } + + fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> { + unimplemented!() + } + + #[inline] + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + #[inline] + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + self.name().map(str::as_bytes) + } + + #[inline] + fn name(&self) -> Result<&'data str> { + Ok(match self.section.id { + SectionId::Custom => self.section.name, + SectionId::Type => "<type>", + SectionId::Import => "<import>", + SectionId::Function => "<function>", + SectionId::Table => "<table>", + SectionId::Memory => "<memory>", + SectionId::Global => "<global>", + SectionId::Export => "<export>", + SectionId::Start => "<start>", + SectionId::Element => "<element>", + SectionId::Code => "<code>", + SectionId::Data => "<data>", + SectionId::DataCount => "<data_count>", + SectionId::Tag => "<tag>", + }) + } + + #[inline] + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + Ok(None) + } + + #[inline] + fn kind(&self) -> SectionKind { + match self.section.id { + SectionId::Custom => match self.section.name { + "reloc." | "linking" => SectionKind::Linker, + _ => SectionKind::Other, + }, + SectionId::Type => SectionKind::Metadata, + SectionId::Import => SectionKind::Linker, + SectionId::Function => SectionKind::Metadata, + SectionId::Table => SectionKind::UninitializedData, + SectionId::Memory => SectionKind::UninitializedData, + SectionId::Global => SectionKind::Data, + SectionId::Export => SectionKind::Linker, + SectionId::Start => SectionKind::Linker, + SectionId::Element => SectionKind::Data, + SectionId::Code => SectionKind::Text, + SectionId::Data => SectionKind::Data, + SectionId::DataCount => SectionKind::UninitializedData, + SectionId::Tag => SectionKind::Data, + } + } + + #[inline] + fn relocations(&self) -> WasmRelocationIterator<'data, 'file, R> { + WasmRelocationIterator(PhantomData) + } + + fn relocation_map(&self) -> read::Result<RelocationMap> { + RelocationMap::new(self.file, self) + } + + #[inline] + fn flags(&self) -> SectionFlags { + SectionFlags::None + } +} + +/// An iterator for the COMDAT section groups in a [`WasmFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct WasmComdatIterator<'data, 'file, R = &'data [u8]> { + #[allow(unused)] + file: &'file WasmFile<'data, R>, +} + +impl<'data, 'file, R> Iterator for WasmComdatIterator<'data, 'file, R> { + type Item = WasmComdat<'data, 'file, R>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A COMDAT section group in a [`WasmFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct WasmComdat<'data, 'file, R = &'data [u8]> { + #[allow(unused)] + file: &'file WasmFile<'data, R>, +} + +impl<'data, 'file, R> read::private::Sealed for WasmComdat<'data, 'file, R> {} + +impl<'data, 'file, R> ObjectComdat<'data> for WasmComdat<'data, 'file, R> { + type SectionIterator = WasmComdatSectionIterator<'data, 'file, R>; + + #[inline] + fn kind(&self) -> ComdatKind { + unreachable!(); + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + unreachable!(); + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&'data str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator for the sections in a COMDAT section group in a [`WasmFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct WasmComdatSectionIterator<'data, 'file, R = &'data [u8]> { + #[allow(unused)] + file: &'file WasmFile<'data, R>, +} + +impl<'data, 'file, R> Iterator for WasmComdatSectionIterator<'data, 'file, R> { + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A symbol table in a [`WasmFile`]. +#[derive(Debug)] +pub struct WasmSymbolTable<'data, 'file> { + symbols: &'file [WasmSymbolInternal<'data>], +} + +impl<'data, 'file> read::private::Sealed for WasmSymbolTable<'data, 'file> {} + +impl<'data, 'file> ObjectSymbolTable<'data> for WasmSymbolTable<'data, 'file> { + type Symbol = WasmSymbol<'data, 'file>; + type SymbolIterator = WasmSymbolIterator<'data, 'file>; + + fn symbols(&self) -> Self::SymbolIterator { + WasmSymbolIterator { + symbols: self.symbols.iter().enumerate(), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { + let symbol = self + .symbols + .get(index.0) + .read_error("Invalid Wasm symbol index")?; + Ok(WasmSymbol { index, symbol }) + } +} + +/// An iterator for the symbols in a [`WasmFile`]. +#[derive(Debug)] +pub struct WasmSymbolIterator<'data, 'file> { + symbols: core::iter::Enumerate<slice::Iter<'file, WasmSymbolInternal<'data>>>, +} + +impl<'data, 'file> Iterator for WasmSymbolIterator<'data, 'file> { + type Item = WasmSymbol<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + let (index, symbol) = self.symbols.next()?; + Some(WasmSymbol { + index: SymbolIndex(index), + symbol, + }) + } +} + +/// A symbol in a [`WasmFile`]. +/// +/// Most functionality is provided by the [`ObjectSymbol`] trait implementation. +#[derive(Clone, Copy, Debug)] +pub struct WasmSymbol<'data, 'file> { + index: SymbolIndex, + symbol: &'file WasmSymbolInternal<'data>, +} + +#[derive(Clone, Debug)] +struct WasmSymbolInternal<'data> { + name: &'data str, + address: u64, + size: u64, + kind: SymbolKind, + section: SymbolSection, + scope: SymbolScope, + weak: bool, +} + +impl<'data, 'file> read::private::Sealed for WasmSymbol<'data, 'file> {} + +impl<'data, 'file> ObjectSymbol<'data> for WasmSymbol<'data, 'file> { + #[inline] + fn index(&self) -> SymbolIndex { + self.index + } + + #[inline] + fn name_bytes(&self) -> read::Result<&'data [u8]> { + Ok(self.symbol.name.as_bytes()) + } + + #[inline] + fn name(&self) -> read::Result<&'data str> { + Ok(self.symbol.name) + } + + #[inline] + fn address(&self) -> u64 { + self.symbol.address + } + + #[inline] + fn size(&self) -> u64 { + self.symbol.size + } + + #[inline] + fn kind(&self) -> SymbolKind { + self.symbol.kind + } + + #[inline] + fn section(&self) -> SymbolSection { + self.symbol.section + } + + #[inline] + fn is_undefined(&self) -> bool { + self.symbol.section == SymbolSection::Undefined + } + + #[inline] + fn is_definition(&self) -> bool { + (self.symbol.kind == SymbolKind::Text || self.symbol.kind == SymbolKind::Data) + && self.symbol.section != SymbolSection::Undefined + } + + #[inline] + fn is_common(&self) -> bool { + self.symbol.section == SymbolSection::Common + } + + #[inline] + fn is_weak(&self) -> bool { + self.symbol.weak + } + + #[inline] + fn scope(&self) -> SymbolScope { + self.symbol.scope + } + + #[inline] + fn is_global(&self) -> bool { + self.symbol.scope != SymbolScope::Compilation + } + + #[inline] + fn is_local(&self) -> bool { + self.symbol.scope == SymbolScope::Compilation + } + + #[inline] + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { + SymbolFlags::None + } +} + +/// An iterator for the relocations for a [`WasmSection`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct WasmRelocationIterator<'data, 'file, R = &'data [u8]>( + PhantomData<(&'data (), &'file (), R)>, +); + +impl<'data, 'file, R> Iterator for WasmRelocationIterator<'data, 'file, R> { + type Item = (u64, Relocation); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + None + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/comdat.rs b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/comdat.rs new file mode 100644 index 0000000..142dd52 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/comdat.rs
@@ -0,0 +1,134 @@ +//! XCOFF doesn't support the COMDAT section. + +use core::fmt::Debug; + +use crate::read::{self, ComdatKind, ObjectComdat, ReadRef, Result, SectionIndex, SymbolIndex}; +use crate::xcoff; + +use super::{FileHeader, XcoffFile}; + +/// An iterator for the COMDAT section groups in a [`XcoffFile32`](super::XcoffFile32). +pub type XcoffComdatIterator32<'data, 'file, R = &'data [u8]> = + XcoffComdatIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator for the COMDAT section groups in a [`XcoffFile64`](super::XcoffFile64). +pub type XcoffComdatIterator64<'data, 'file, R = &'data [u8]> = + XcoffComdatIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator for the COMDAT section groups in a [`XcoffFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct XcoffComdatIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(crate) file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffComdatIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = XcoffComdat<'data, 'file, Xcoff, R>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A COMDAT section group in a [`XcoffFile32`](super::XcoffFile32). +pub type XcoffComdat32<'data, 'file, R = &'data [u8]> = + XcoffComdat<'data, 'file, xcoff::FileHeader32, R>; + +/// A COMDAT section group in a [`XcoffFile64`](super::XcoffFile64). +pub type XcoffComdat64<'data, 'file, R = &'data [u8]> = + XcoffComdat<'data, 'file, xcoff::FileHeader64, R>; + +/// A COMDAT section group in a [`XcoffFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct XcoffComdat<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffComdat<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> ObjectComdat<'data> for XcoffComdat<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type SectionIterator = XcoffComdatSectionIterator<'data, 'file, Xcoff, R>; + + #[inline] + fn kind(&self) -> ComdatKind { + unreachable!(); + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + unreachable!(); + } + + #[inline] + fn name_bytes(&self) -> Result<&'data [u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&'data str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator for the sections in a COMDAT section group in a [`XcoffFile32`](super::XcoffFile32). +pub type XcoffComdatSectionIterator32<'data, 'file, R = &'data [u8]> = + XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator for the sections in a COMDAT section group in a [`XcoffFile64`](super::XcoffFile64). +pub type XcoffComdatSectionIterator64<'data, 'file, R = &'data [u8]> = + XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator for the sections in a COMDAT section group in a [`XcoffFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct XcoffComdatSectionIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffComdatSectionIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + None + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/file.rs b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/file.rs new file mode 100644 index 0000000..a30aee6 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/file.rs
@@ -0,0 +1,756 @@ +use core::fmt::Debug; +use core::mem; + +use alloc::vec::Vec; + +use crate::endian::BigEndian as BE; +use crate::pod::Pod; +use crate::read::{ + self, Architecture, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, + ObjectKind, ObjectSection, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, +}; +use crate::xcoff; + +use super::{ + CsectAux, FileAux, Rel, SectionHeader, SectionTable, Symbol, SymbolTable, XcoffComdat, + XcoffComdatIterator, XcoffSection, XcoffSectionIterator, XcoffSegment, XcoffSegmentIterator, + XcoffSymbol, XcoffSymbolIterator, XcoffSymbolTable, +}; + +/// A 32-bit XCOFF object file. +/// +/// This is a file that starts with [`xcoff::FileHeader32`], and corresponds +/// to [`crate::FileKind::Xcoff32`]. +pub type XcoffFile32<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader32, R>; +/// A 64-bit XCOFF object file. +/// +/// This is a file that starts with [`xcoff::FileHeader64`], and corresponds +/// to [`crate::FileKind::Xcoff64`]. +pub type XcoffFile64<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader64, R>; + +/// A partially parsed XCOFF file. +/// +/// Most functionality is provided by the [`Object`] trait implementation. +#[derive(Debug)] +pub struct XcoffFile<'data, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) data: R, + pub(super) header: &'data Xcoff, + pub(super) aux_header: Option<&'data Xcoff::AuxHeader>, + pub(super) sections: SectionTable<'data, Xcoff>, + pub(super) symbols: SymbolTable<'data, Xcoff, R>, +} + +impl<'data, Xcoff, R> XcoffFile<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + /// Parse the raw XCOFF file data. + pub fn parse(data: R) -> Result<Self> { + let mut offset = 0; + let header = Xcoff::parse(data, &mut offset)?; + let aux_header = header.aux_header(data, &mut offset)?; + let sections = header.sections(data, &mut offset)?; + let symbols = header.symbols(data)?; + + Ok(XcoffFile { + data, + header, + aux_header, + sections, + symbols, + }) + } + + /// Returns the raw data. + pub fn data(&self) -> R { + self.data + } + + /// Returns the raw XCOFF file header. + #[deprecated(note = "Use `xcoff_header` instead")] + pub fn raw_header(&self) -> &'data Xcoff { + self.header + } + + /// Get the raw XCOFF file header. + pub fn xcoff_header(&self) -> &'data Xcoff { + self.header + } + + /// Get the raw XCOFF auxiliary header. + pub fn xcoff_aux_header(&self) -> Option<&'data Xcoff::AuxHeader> { + self.aux_header + } + + /// Get the XCOFF section table. + pub fn xcoff_section_table(&self) -> &SectionTable<'data, Xcoff> { + &self.sections + } + + /// Get the XCOFF symbol table. + pub fn xcoff_symbol_table(&self) -> &SymbolTable<'data, Xcoff, R> { + &self.symbols + } +} + +impl<'data, Xcoff, R> read::private::Sealed for XcoffFile<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, Xcoff, R> Object<'data> for XcoffFile<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Segment<'file> + = XcoffSegment<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type SegmentIterator<'file> + = XcoffSegmentIterator<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type Section<'file> + = XcoffSection<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type SectionIterator<'file> + = XcoffSectionIterator<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type Comdat<'file> + = XcoffComdat<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type ComdatIterator<'file> + = XcoffComdatIterator<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type Symbol<'file> + = XcoffSymbol<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type SymbolIterator<'file> + = XcoffSymbolIterator<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type SymbolTable<'file> + = XcoffSymbolTable<'data, 'file, Xcoff, R> + where + Self: 'file, + 'data: 'file; + type DynamicRelocationIterator<'file> + = NoDynamicRelocationIterator + where + Self: 'file, + 'data: 'file; + + fn architecture(&self) -> Architecture { + if self.is_64() { + Architecture::PowerPc64 + } else { + Architecture::PowerPc + } + } + + fn is_little_endian(&self) -> bool { + false + } + + fn is_64(&self) -> bool { + self.header.is_type_64() + } + + fn kind(&self) -> ObjectKind { + let flags = self.header.f_flags(); + if flags & xcoff::F_EXEC != 0 { + ObjectKind::Executable + } else if flags & xcoff::F_SHROBJ != 0 { + ObjectKind::Dynamic + } else if flags & xcoff::F_RELFLG == 0 { + ObjectKind::Relocatable + } else { + ObjectKind::Unknown + } + } + + fn segments(&self) -> XcoffSegmentIterator<'data, '_, Xcoff, R> { + XcoffSegmentIterator { file: self } + } + + fn section_by_name_bytes<'file>( + &'file self, + section_name: &[u8], + ) -> Option<XcoffSection<'data, 'file, Xcoff, R>> { + self.sections() + .find(|section| section.name_bytes() == Ok(section_name)) + } + + fn section_by_index(&self, index: SectionIndex) -> Result<XcoffSection<'data, '_, Xcoff, R>> { + let section = self.sections.section(index)?; + Ok(XcoffSection { + file: self, + section, + index, + }) + } + + fn sections(&self) -> XcoffSectionIterator<'data, '_, Xcoff, R> { + XcoffSectionIterator { + file: self, + iter: self.sections.iter().enumerate(), + } + } + + fn comdats(&self) -> XcoffComdatIterator<'data, '_, Xcoff, R> { + XcoffComdatIterator { file: self } + } + + fn symbol_table(&self) -> Option<XcoffSymbolTable<'data, '_, Xcoff, R>> { + if self.symbols.is_empty() { + return None; + } + Some(XcoffSymbolTable { + symbols: &self.symbols, + file: self, + }) + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<XcoffSymbol<'data, '_, Xcoff, R>> { + let symbol = self.symbols.symbol(index)?; + Ok(XcoffSymbol { + symbols: &self.symbols, + index, + symbol, + file: self, + }) + } + + fn symbols(&self) -> XcoffSymbolIterator<'data, '_, Xcoff, R> { + XcoffSymbolIterator { + file: self, + symbols: self.symbols.iter(), + } + } + + fn dynamic_symbol_table<'file>( + &'file self, + ) -> Option<XcoffSymbolTable<'data, 'file, Xcoff, R>> { + None + } + + fn dynamic_symbols(&self) -> XcoffSymbolIterator<'data, '_, Xcoff, R> { + // TODO: return the symbols in the STYP_LOADER section. + XcoffSymbolIterator { + file: self, + symbols: self.symbols.iter_none(), + } + } + + fn dynamic_relocations(&self) -> Option<Self::DynamicRelocationIterator<'_>> { + // TODO: return the relocations in the STYP_LOADER section. + None + } + + fn imports(&self) -> Result<alloc::vec::Vec<Import<'data>>> { + // TODO: return the imports in the STYP_LOADER section. + Ok(Vec::new()) + } + + fn exports(&self) -> Result<alloc::vec::Vec<Export<'data>>> { + // TODO: return the exports in the STYP_LOADER section. + Ok(Vec::new()) + } + + fn has_debug_symbols(&self) -> bool { + self.section_by_name(".debug").is_some() || self.section_by_name(".dwinfo").is_some() + } + + fn relative_address_base(&self) -> u64 { + 0 + } + + fn entry(&self) -> u64 { + if let Some(aux_header) = self.aux_header { + aux_header.o_entry().into() + } else { + 0 + } + } + + fn flags(&self) -> FileFlags { + FileFlags::Xcoff { + f_flags: self.header.f_flags(), + } + } +} + +/// A trait for generic access to [`xcoff::FileHeader32`] and [`xcoff::FileHeader64`]. +#[allow(missing_docs)] +pub trait FileHeader: Debug + Pod { + type Word: Into<u64>; + type AuxHeader: AuxHeader<Word = Self::Word>; + type SectionHeader: SectionHeader<Word = Self::Word, Rel = Self::Rel>; + type Symbol: Symbol<Word = Self::Word>; + type FileAux: FileAux; + type CsectAux: CsectAux; + type Rel: Rel<Word = Self::Word>; + + /// Return true if this type is a 64-bit header. + fn is_type_64(&self) -> bool; + + fn f_magic(&self) -> u16; + fn f_nscns(&self) -> u16; + fn f_timdat(&self) -> u32; + fn f_symptr(&self) -> Self::Word; + fn f_nsyms(&self) -> u32; + fn f_opthdr(&self) -> u16; + fn f_flags(&self) -> u16; + + // Provided methods. + + /// Read the file header. + /// + /// Also checks that the magic field in the file header is a supported format. + fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> Result<&'data Self> { + let header = data + .read::<Self>(offset) + .read_error("Invalid XCOFF header size or alignment")?; + if !header.is_supported() { + return Err(Error("Unsupported XCOFF header")); + } + Ok(header) + } + + fn is_supported(&self) -> bool { + (self.is_type_64() && self.f_magic() == xcoff::MAGIC_64) + || (!self.is_type_64() && self.f_magic() == xcoff::MAGIC_32) + } + + /// Read the auxiliary file header. + fn aux_header<'data, R: ReadRef<'data>>( + &self, + data: R, + offset: &mut u64, + ) -> Result<Option<&'data Self::AuxHeader>> { + let aux_header_size = self.f_opthdr(); + if self.f_flags() & xcoff::F_EXEC == 0 { + // No auxiliary header is required for an object file that is not an executable. + // TODO: Some AIX programs generate auxiliary headers for 32-bit object files + // that end after the data_start field. + *offset += u64::from(aux_header_size); + return Ok(None); + } + // Executables, however, must have auxiliary headers that include the + // full structure definitions. + if aux_header_size != mem::size_of::<Self::AuxHeader>() as u16 { + *offset += u64::from(aux_header_size); + return Ok(None); + } + let aux_header = data + .read::<Self::AuxHeader>(offset) + .read_error("Invalid XCOFF auxiliary header size")?; + Ok(Some(aux_header)) + } + + /// Read the section table. + #[inline] + fn sections<'data, R: ReadRef<'data>>( + &self, + data: R, + offset: &mut u64, + ) -> Result<SectionTable<'data, Self>> { + SectionTable::parse(self, data, offset) + } + + /// Return the symbol table. + #[inline] + fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> Result<SymbolTable<'data, Self, R>> { + SymbolTable::parse(*self, data) + } +} + +impl FileHeader for xcoff::FileHeader32 { + type Word = u32; + type AuxHeader = xcoff::AuxHeader32; + type SectionHeader = xcoff::SectionHeader32; + type Symbol = xcoff::Symbol32; + type FileAux = xcoff::FileAux32; + type CsectAux = xcoff::CsectAux32; + type Rel = xcoff::Rel32; + + fn is_type_64(&self) -> bool { + false + } + + fn f_magic(&self) -> u16 { + self.f_magic.get(BE) + } + + fn f_nscns(&self) -> u16 { + self.f_nscns.get(BE) + } + + fn f_timdat(&self) -> u32 { + self.f_timdat.get(BE) + } + + fn f_symptr(&self) -> Self::Word { + self.f_symptr.get(BE) + } + + fn f_nsyms(&self) -> u32 { + self.f_nsyms.get(BE) + } + + fn f_opthdr(&self) -> u16 { + self.f_opthdr.get(BE) + } + + fn f_flags(&self) -> u16 { + self.f_flags.get(BE) + } +} + +impl FileHeader for xcoff::FileHeader64 { + type Word = u64; + type AuxHeader = xcoff::AuxHeader64; + type SectionHeader = xcoff::SectionHeader64; + type Symbol = xcoff::Symbol64; + type FileAux = xcoff::FileAux64; + type CsectAux = xcoff::CsectAux64; + type Rel = xcoff::Rel64; + + fn is_type_64(&self) -> bool { + true + } + + fn f_magic(&self) -> u16 { + self.f_magic.get(BE) + } + + fn f_nscns(&self) -> u16 { + self.f_nscns.get(BE) + } + + fn f_timdat(&self) -> u32 { + self.f_timdat.get(BE) + } + + fn f_symptr(&self) -> Self::Word { + self.f_symptr.get(BE) + } + + fn f_nsyms(&self) -> u32 { + self.f_nsyms.get(BE) + } + + fn f_opthdr(&self) -> u16 { + self.f_opthdr.get(BE) + } + + fn f_flags(&self) -> u16 { + self.f_flags.get(BE) + } +} + +/// A trait for generic access to [`xcoff::AuxHeader32`] and [`xcoff::AuxHeader64`]. +#[allow(missing_docs)] +pub trait AuxHeader: Debug + Pod { + type Word: Into<u64>; + + fn o_mflag(&self) -> u16; + fn o_vstamp(&self) -> u16; + fn o_tsize(&self) -> Self::Word; + fn o_dsize(&self) -> Self::Word; + fn o_bsize(&self) -> Self::Word; + fn o_entry(&self) -> Self::Word; + fn o_text_start(&self) -> Self::Word; + fn o_data_start(&self) -> Self::Word; + fn o_toc(&self) -> Self::Word; + fn o_snentry(&self) -> u16; + fn o_sntext(&self) -> u16; + fn o_sndata(&self) -> u16; + fn o_sntoc(&self) -> u16; + fn o_snloader(&self) -> u16; + fn o_snbss(&self) -> u16; + fn o_algntext(&self) -> u16; + fn o_algndata(&self) -> u16; + fn o_modtype(&self) -> u16; + fn o_cpuflag(&self) -> u8; + fn o_cputype(&self) -> u8; + fn o_maxstack(&self) -> Self::Word; + fn o_maxdata(&self) -> Self::Word; + fn o_debugger(&self) -> u32; + fn o_textpsize(&self) -> u8; + fn o_datapsize(&self) -> u8; + fn o_stackpsize(&self) -> u8; + fn o_flags(&self) -> u8; + fn o_sntdata(&self) -> u16; + fn o_sntbss(&self) -> u16; + fn o_x64flags(&self) -> Option<u16>; +} + +impl AuxHeader for xcoff::AuxHeader32 { + type Word = u32; + + fn o_mflag(&self) -> u16 { + self.o_mflag.get(BE) + } + + fn o_vstamp(&self) -> u16 { + self.o_vstamp.get(BE) + } + + fn o_tsize(&self) -> Self::Word { + self.o_tsize.get(BE) + } + + fn o_dsize(&self) -> Self::Word { + self.o_dsize.get(BE) + } + + fn o_bsize(&self) -> Self::Word { + self.o_bsize.get(BE) + } + + fn o_entry(&self) -> Self::Word { + self.o_entry.get(BE) + } + + fn o_text_start(&self) -> Self::Word { + self.o_text_start.get(BE) + } + + fn o_data_start(&self) -> Self::Word { + self.o_data_start.get(BE) + } + + fn o_toc(&self) -> Self::Word { + self.o_toc.get(BE) + } + + fn o_snentry(&self) -> u16 { + self.o_snentry.get(BE) + } + + fn o_sntext(&self) -> u16 { + self.o_sntext.get(BE) + } + + fn o_sndata(&self) -> u16 { + self.o_sndata.get(BE) + } + + fn o_sntoc(&self) -> u16 { + self.o_sntoc.get(BE) + } + + fn o_snloader(&self) -> u16 { + self.o_snloader.get(BE) + } + + fn o_snbss(&self) -> u16 { + self.o_snbss.get(BE) + } + + fn o_algntext(&self) -> u16 { + self.o_algntext.get(BE) + } + + fn o_algndata(&self) -> u16 { + self.o_algndata.get(BE) + } + + fn o_modtype(&self) -> u16 { + self.o_modtype.get(BE) + } + + fn o_cpuflag(&self) -> u8 { + self.o_cpuflag + } + + fn o_cputype(&self) -> u8 { + self.o_cputype + } + + fn o_maxstack(&self) -> Self::Word { + self.o_maxstack.get(BE) + } + + fn o_maxdata(&self) -> Self::Word { + self.o_maxdata.get(BE) + } + + fn o_debugger(&self) -> u32 { + self.o_debugger.get(BE) + } + + fn o_textpsize(&self) -> u8 { + self.o_textpsize + } + + fn o_datapsize(&self) -> u8 { + self.o_datapsize + } + + fn o_stackpsize(&self) -> u8 { + self.o_stackpsize + } + + fn o_flags(&self) -> u8 { + self.o_flags + } + + fn o_sntdata(&self) -> u16 { + self.o_sntdata.get(BE) + } + + fn o_sntbss(&self) -> u16 { + self.o_sntbss.get(BE) + } + + fn o_x64flags(&self) -> Option<u16> { + None + } +} + +impl AuxHeader for xcoff::AuxHeader64 { + type Word = u64; + + fn o_mflag(&self) -> u16 { + self.o_mflag.get(BE) + } + + fn o_vstamp(&self) -> u16 { + self.o_vstamp.get(BE) + } + + fn o_tsize(&self) -> Self::Word { + self.o_tsize.get(BE) + } + + fn o_dsize(&self) -> Self::Word { + self.o_dsize.get(BE) + } + + fn o_bsize(&self) -> Self::Word { + self.o_bsize.get(BE) + } + + fn o_entry(&self) -> Self::Word { + self.o_entry.get(BE) + } + + fn o_text_start(&self) -> Self::Word { + self.o_text_start.get(BE) + } + + fn o_data_start(&self) -> Self::Word { + self.o_data_start.get(BE) + } + + fn o_toc(&self) -> Self::Word { + self.o_toc.get(BE) + } + + fn o_snentry(&self) -> u16 { + self.o_snentry.get(BE) + } + + fn o_sntext(&self) -> u16 { + self.o_sntext.get(BE) + } + + fn o_sndata(&self) -> u16 { + self.o_sndata.get(BE) + } + + fn o_sntoc(&self) -> u16 { + self.o_sntoc.get(BE) + } + + fn o_snloader(&self) -> u16 { + self.o_snloader.get(BE) + } + + fn o_snbss(&self) -> u16 { + self.o_snbss.get(BE) + } + + fn o_algntext(&self) -> u16 { + self.o_algntext.get(BE) + } + + fn o_algndata(&self) -> u16 { + self.o_algndata.get(BE) + } + + fn o_modtype(&self) -> u16 { + self.o_modtype.get(BE) + } + + fn o_cpuflag(&self) -> u8 { + self.o_cpuflag + } + + fn o_cputype(&self) -> u8 { + self.o_cputype + } + + fn o_maxstack(&self) -> Self::Word { + self.o_maxstack.get(BE) + } + + fn o_maxdata(&self) -> Self::Word { + self.o_maxdata.get(BE) + } + + fn o_debugger(&self) -> u32 { + self.o_debugger.get(BE) + } + + fn o_textpsize(&self) -> u8 { + self.o_textpsize + } + + fn o_datapsize(&self) -> u8 { + self.o_datapsize + } + + fn o_stackpsize(&self) -> u8 { + self.o_stackpsize + } + + fn o_flags(&self) -> u8 { + self.o_flags + } + + fn o_sntdata(&self) -> u16 { + self.o_sntdata.get(BE) + } + + fn o_sntbss(&self) -> u16 { + self.o_sntbss.get(BE) + } + + fn o_x64flags(&self) -> Option<u16> { + Some(self.o_x64flags.get(BE)) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/mod.rs new file mode 100644 index 0000000..b75c4da --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/mod.rs
@@ -0,0 +1,63 @@ +//! Support for reading AIX XCOFF files. +//! +//! Traits are used to abstract over the difference between 32-bit and 64-bit XCOFF. +//! The primary trait for this is [`FileHeader`]. +//! +//! ## High level API +//! +//! [`XcoffFile`] implements the [`Object`](crate::read::Object) trait for XCOFF files. +//! [`XcoffFile`] is parameterised by [`FileHeader`] to allow reading both 32-bit and +//! 64-bit XCOFF. There are type aliases for these parameters ([`XcoffFile32`] and +//! [`XcoffFile64`]). +//! +//! ## Low level API +//! +//! The [`FileHeader`] trait can be directly used to parse both [`xcoff::FileHeader32`] +//! and [`xcoff::FileHeader64`]. +//! +//! ### Example for low level API +//! ```no_run +//! use object::xcoff; +//! use object::read::xcoff::{FileHeader, SectionHeader, Symbol}; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the name of each section and symbol. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(feature = "std")] { +//! let data = fs::read("path/to/binary")?; +//! let mut offset = 0; +//! let header = xcoff::FileHeader64::parse(&*data, &mut offset)?; +//! let aux_header = header.aux_header(&*data, &mut offset)?; +//! let sections = header.sections(&*data, &mut offset)?; +//! let symbols = header.symbols(&*data)?; +//! for section in sections.iter() { +//! println!("{}", String::from_utf8_lossy(section.name())); +//! } +//! for (_index, symbol) in symbols.iter() { +//! println!("{}", String::from_utf8_lossy(symbol.name(symbols.strings())?)); +//! } +//! # } +//! Ok(()) +//! } +//! ``` +#[cfg(doc)] +use crate::xcoff; + +mod file; +pub use file::*; + +mod section; +pub use section::*; + +mod symbol; +pub use symbol::*; + +mod relocation; +pub use relocation::*; + +mod comdat; +pub use comdat::*; + +mod segment; +pub use segment::*;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/relocation.rs b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/relocation.rs new file mode 100644 index 0000000..bfabf87 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/relocation.rs
@@ -0,0 +1,138 @@ +use alloc::fmt; +use core::fmt::Debug; +use core::slice; + +use crate::endian::BigEndian as BE; +use crate::pod::Pod; +use crate::read::{ + ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, + SymbolIndex, +}; +use crate::xcoff; + +use super::{FileHeader, SectionHeader, XcoffFile}; + +/// An iterator for the relocations in an [`XcoffSection32`](super::XcoffSection32). +pub type XcoffRelocationIterator32<'data, 'file, R = &'data [u8]> = + XcoffRelocationIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator for the relocations in an [`XcoffSection64`](super::XcoffSection64). +pub type XcoffRelocationIterator64<'data, 'file, R = &'data [u8]> = + XcoffRelocationIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator for the relocations in an [`XcoffSection`](super::XcoffSection). +pub struct XcoffRelocationIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) relocations: + slice::Iter<'data, <<Xcoff as FileHeader>::SectionHeader as SectionHeader>::Rel>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffRelocationIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + self.relocations.next().map(|relocation| { + let r_rtype = relocation.r_rtype(); + let r_rsize = relocation.r_rsize(); + let flags = RelocationFlags::Xcoff { r_rtype, r_rsize }; + let encoding = RelocationEncoding::Generic; + let (kind, addend) = match r_rtype { + xcoff::R_POS + | xcoff::R_RL + | xcoff::R_RLA + | xcoff::R_BA + | xcoff::R_RBA + | xcoff::R_TLS => (RelocationKind::Absolute, 0), + xcoff::R_REL | xcoff::R_BR | xcoff::R_RBR => (RelocationKind::Relative, -4), + xcoff::R_TOC | xcoff::R_TOCL | xcoff::R_TOCU => (RelocationKind::Got, 0), + _ => (RelocationKind::Unknown, 0), + }; + let size = (r_rsize & 0x3F) + 1; + let target = RelocationTarget::Symbol(relocation.symbol()); + ( + relocation.r_vaddr().into(), + Relocation { + kind, + encoding, + size, + target, + addend, + implicit_addend: true, + flags, + }, + ) + }) + } +} + +impl<'data, 'file, Xcoff, R> fmt::Debug for XcoffRelocationIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("XcoffRelocationIterator").finish() + } +} + +/// A trait for generic access to [`xcoff::Rel32`] and [`xcoff::Rel64`]. +#[allow(missing_docs)] +pub trait Rel: Debug + Pod { + type Word: Into<u64>; + fn r_vaddr(&self) -> Self::Word; + fn r_symndx(&self) -> u32; + fn r_rsize(&self) -> u8; + fn r_rtype(&self) -> u8; + + fn symbol(&self) -> SymbolIndex { + SymbolIndex(self.r_symndx() as usize) + } +} + +impl Rel for xcoff::Rel32 { + type Word = u32; + + fn r_vaddr(&self) -> Self::Word { + self.r_vaddr.get(BE) + } + + fn r_symndx(&self) -> u32 { + self.r_symndx.get(BE) + } + + fn r_rsize(&self) -> u8 { + self.r_rsize + } + + fn r_rtype(&self) -> u8 { + self.r_rtype + } +} + +impl Rel for xcoff::Rel64 { + type Word = u64; + + fn r_vaddr(&self) -> Self::Word { + self.r_vaddr.get(BE) + } + + fn r_symndx(&self) -> u32 { + self.r_symndx.get(BE) + } + + fn r_rsize(&self) -> u8 { + self.r_rsize + } + + fn r_rtype(&self) -> u8 { + self.r_rtype + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/section.rs b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/section.rs new file mode 100644 index 0000000..dac3c2a --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/section.rs
@@ -0,0 +1,452 @@ +use core::fmt::Debug; +use core::{iter, result, slice, str}; + +use crate::endian::BigEndian as BE; +use crate::pod::Pod; +use crate::read::{ + self, CompressedData, CompressedFileRange, Error, ObjectSection, ReadError, ReadRef, + RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, +}; +use crate::xcoff; + +use super::{AuxHeader, FileHeader, Rel, XcoffFile, XcoffRelocationIterator}; + +/// An iterator for the sections in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSectionIterator32<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator for the sections in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSectionIterator64<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator for the sections in an [`XcoffFile`]. +#[derive(Debug)] +pub struct XcoffSectionIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) iter: iter::Enumerate<slice::Iter<'data, Xcoff::SectionHeader>>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffSectionIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = XcoffSection<'data, 'file, Xcoff, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|(index, section)| XcoffSection { + index: SectionIndex(index + 1), + file: self.file, + section, + }) + } +} + +/// A section in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSection32<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader32, R>; +/// A section in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSection64<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader64, R>; + +/// A section in an [`XcoffFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct XcoffSection<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) section: &'data Xcoff::SectionHeader, + pub(super) index: SectionIndex, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> XcoffSection<'data, 'file, Xcoff, R> { + /// Get the XCOFF file containing this section. + pub fn xcoff_file(&self) -> &'file XcoffFile<'data, Xcoff, R> { + self.file + } + + /// Get the raw XCOFF section header. + pub fn xcoff_section(&self) -> &'data Xcoff::SectionHeader { + self.section + } + + /// Get the raw XCOFF relocation entries for this section. + pub fn xcoff_relocations(&self) -> Result<&'data [Xcoff::Rel]> { + self.section.relocations(self.file.data) + } + + fn bytes(&self) -> Result<&'data [u8]> { + self.section + .data(self.file.data) + .read_error("Invalid XCOFF section offset or size") + } +} + +impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSection<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> ObjectSection<'data> for XcoffSection<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type RelocationIterator = XcoffRelocationIterator<'data, 'file, Xcoff, R>; + + fn index(&self) -> SectionIndex { + self.index + } + + fn address(&self) -> u64 { + self.section.s_paddr().into() + } + + fn size(&self) -> u64 { + self.section.s_size().into() + } + + fn align(&self) -> u64 { + // The default section alignment is 4. + if let Some(aux_header) = self.file.aux_header { + match self.kind() { + SectionKind::Text => aux_header.o_algntext().into(), + SectionKind::Data => aux_header.o_algndata().into(), + _ => 4, + } + } else { + 4 + } + } + + fn file_range(&self) -> Option<(u64, u64)> { + self.section.file_range() + } + + fn data(&self) -> Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + fn name_bytes(&self) -> read::Result<&'data [u8]> { + Ok(self.section.name()) + } + + fn name(&self) -> read::Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 XCOFF section name") + } + + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(None) + } + + fn segment_name(&self) -> Result<Option<&str>> { + Ok(None) + } + + fn kind(&self) -> SectionKind { + let section_type = self.section.s_flags() as u16; + if section_type & xcoff::STYP_TEXT != 0 { + SectionKind::Text + } else if section_type & xcoff::STYP_DATA != 0 { + SectionKind::Data + } else if section_type & xcoff::STYP_TDATA != 0 { + SectionKind::Tls + } else if section_type & xcoff::STYP_BSS != 0 { + SectionKind::UninitializedData + } else if section_type & xcoff::STYP_TBSS != 0 { + SectionKind::UninitializedTls + } else if section_type & (xcoff::STYP_DEBUG | xcoff::STYP_DWARF) != 0 { + SectionKind::Debug + } else if section_type & (xcoff::STYP_LOADER | xcoff::STYP_OVRFLO) != 0 { + SectionKind::Metadata + } else if section_type + & (xcoff::STYP_INFO | xcoff::STYP_EXCEPT | xcoff::STYP_PAD | xcoff::STYP_TYPCHK) + != 0 + { + SectionKind::Other + } else { + SectionKind::Unknown + } + } + + fn relocations(&self) -> Self::RelocationIterator { + let rel = self.xcoff_relocations().unwrap_or(&[]); + XcoffRelocationIterator { + file: self.file, + relocations: rel.iter(), + } + } + + fn relocation_map(&self) -> read::Result<RelocationMap> { + RelocationMap::new(self.file, self) + } + + fn flags(&self) -> SectionFlags { + SectionFlags::Xcoff { + s_flags: self.section.s_flags(), + } + } + + fn uncompressed_data(&self) -> Result<alloc::borrow::Cow<'data, [u8]>> { + self.compressed_data()?.decompress() + } +} + +/// The table of section headers in an XCOFF file. +/// +/// Returned by [`FileHeader::sections`]. +#[derive(Debug, Clone, Copy)] +pub struct SectionTable<'data, Xcoff: FileHeader> { + sections: &'data [Xcoff::SectionHeader], +} + +impl<'data, Xcoff> Default for SectionTable<'data, Xcoff> +where + Xcoff: FileHeader, +{ + fn default() -> Self { + Self { sections: &[] } + } +} + +impl<'data, Xcoff> SectionTable<'data, Xcoff> +where + Xcoff: FileHeader, +{ + /// Parse the section table. + /// + /// `data` must be the entire file data. + /// `offset` must be after the optional file header. + pub fn parse<R: ReadRef<'data>>(header: &Xcoff, data: R, offset: &mut u64) -> Result<Self> { + let section_num = header.f_nscns(); + if section_num == 0 { + return Ok(SectionTable::default()); + } + let sections = data + .read_slice(offset, section_num as usize) + .read_error("Invalid XCOFF section headers")?; + Ok(SectionTable { sections }) + } + + /// Iterate over the section headers. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Xcoff::SectionHeader> { + self.sections.iter() + } + + /// Return true if the section table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.sections.is_empty() + } + + /// The number of section headers. + #[inline] + pub fn len(&self) -> usize { + self.sections.len() + } + + /// Return the section header at the given index. + /// + /// The index is 1-based. + pub fn section(&self, index: SectionIndex) -> read::Result<&'data Xcoff::SectionHeader> { + self.sections + .get(index.0.wrapping_sub(1)) + .read_error("Invalid XCOFF section index") + } +} + +/// A trait for generic access to [`xcoff::SectionHeader32`] and [`xcoff::SectionHeader64`]. +#[allow(missing_docs)] +pub trait SectionHeader: Debug + Pod { + type Word: Into<u64>; + type HalfWord: Into<u32>; + type Xcoff: FileHeader<SectionHeader = Self, Word = Self::Word>; + type Rel: Rel<Word = Self::Word>; + + fn s_name(&self) -> &[u8; 8]; + fn s_paddr(&self) -> Self::Word; + fn s_vaddr(&self) -> Self::Word; + fn s_size(&self) -> Self::Word; + fn s_scnptr(&self) -> Self::Word; + fn s_relptr(&self) -> Self::Word; + fn s_lnnoptr(&self) -> Self::Word; + fn s_nreloc(&self) -> Self::HalfWord; + fn s_nlnno(&self) -> Self::HalfWord; + fn s_flags(&self) -> u32; + + /// Return the section name. + fn name(&self) -> &[u8] { + let sectname = &self.s_name()[..]; + match memchr::memchr(b'\0', sectname) { + Some(end) => §name[..end], + None => sectname, + } + } + + /// Return the offset and size of the section in the file. + fn file_range(&self) -> Option<(u64, u64)> { + Some((self.s_scnptr().into(), self.s_size().into())) + } + + /// Return the section data. + /// + /// Returns `Ok(&[])` if the section has no data. + /// Returns `Err` for invalid values. + fn data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> { + if let Some((offset, size)) = self.file_range() { + data.read_bytes_at(offset, size) + } else { + Ok(&[]) + } + } + + /// Read the relocations. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]>; +} + +impl SectionHeader for xcoff::SectionHeader32 { + type Word = u32; + type HalfWord = u16; + type Xcoff = xcoff::FileHeader32; + type Rel = xcoff::Rel32; + + fn s_name(&self) -> &[u8; 8] { + &self.s_name + } + + fn s_paddr(&self) -> Self::Word { + self.s_paddr.get(BE) + } + + fn s_vaddr(&self) -> Self::Word { + self.s_vaddr.get(BE) + } + + fn s_size(&self) -> Self::Word { + self.s_size.get(BE) + } + + fn s_scnptr(&self) -> Self::Word { + self.s_scnptr.get(BE) + } + + fn s_relptr(&self) -> Self::Word { + self.s_relptr.get(BE) + } + + fn s_lnnoptr(&self) -> Self::Word { + self.s_lnnoptr.get(BE) + } + + fn s_nreloc(&self) -> Self::HalfWord { + self.s_nreloc.get(BE) + } + + fn s_nlnno(&self) -> Self::HalfWord { + self.s_nlnno.get(BE) + } + + fn s_flags(&self) -> u32 { + self.s_flags.get(BE) + } + + /// Read the relocations in a XCOFF32 file. + /// + /// `data` must be the entire file data. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { + let reloc_num = self.s_nreloc() as usize; + // TODO: If more than 65,534 relocation entries are required, the field value will be 65535, + // and an STYP_OVRFLO section header will contain the actual count of relocation entries in + // the s_paddr field. + if reloc_num == 65535 { + return Err(Error("Overflow section is not supported yet.")); + } + data.read_slice_at(self.s_relptr().into(), reloc_num) + .read_error("Invalid XCOFF relocation offset or number") + } +} + +impl SectionHeader for xcoff::SectionHeader64 { + type Word = u64; + type HalfWord = u32; + type Xcoff = xcoff::FileHeader64; + type Rel = xcoff::Rel64; + + fn s_name(&self) -> &[u8; 8] { + &self.s_name + } + + fn s_paddr(&self) -> Self::Word { + self.s_paddr.get(BE) + } + + fn s_vaddr(&self) -> Self::Word { + self.s_vaddr.get(BE) + } + + fn s_size(&self) -> Self::Word { + self.s_size.get(BE) + } + + fn s_scnptr(&self) -> Self::Word { + self.s_scnptr.get(BE) + } + + fn s_relptr(&self) -> Self::Word { + self.s_relptr.get(BE) + } + + fn s_lnnoptr(&self) -> Self::Word { + self.s_lnnoptr.get(BE) + } + + fn s_nreloc(&self) -> Self::HalfWord { + self.s_nreloc.get(BE) + } + + fn s_nlnno(&self) -> Self::HalfWord { + self.s_nlnno.get(BE) + } + + fn s_flags(&self) -> u32 { + self.s_flags.get(BE) + } + + /// Read the relocations in a XCOFF64 file. + /// + /// `data` must be the entire file data. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { + data.read_slice_at(self.s_relptr(), self.s_nreloc() as usize) + .read_error("Invalid XCOFF relocation offset or number") + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/segment.rs b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/segment.rs new file mode 100644 index 0000000..a45c522 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/segment.rs
@@ -0,0 +1,117 @@ +//! TODO: Support the segment for XCOFF when auxiliary file header and loader section is ready. + +use core::fmt::Debug; +use core::str; + +use crate::read::{self, ObjectSegment, ReadRef, Result, SegmentFlags}; +use crate::xcoff; + +use super::{FileHeader, XcoffFile}; + +/// An iterator for the segments in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSegmentIterator32<'data, 'file, R = &'data [u8]> = + XcoffSegmentIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator for the segments in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSegmentIterator64<'data, 'file, R = &'data [u8]> = + XcoffSegmentIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator for the segments in an [`XcoffFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct XcoffSegmentIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffSegmentIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = XcoffSegment<'data, 'file, Xcoff, R>; + + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A segment in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSegment32<'data, 'file, R = &'data [u8]> = + XcoffSegment<'data, 'file, xcoff::FileHeader32, R>; +/// A segment in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSegment64<'data, 'file, R = &'data [u8]> = + XcoffSegment<'data, 'file, xcoff::FileHeader64, R>; + +/// A loadable section in an [`XcoffFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct XcoffSegment<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> XcoffSegment<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSegment<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> ObjectSegment<'data> for XcoffSegment<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + fn address(&self) -> u64 { + unreachable!(); + } + + fn size(&self) -> u64 { + unreachable!(); + } + + fn align(&self) -> u64 { + unreachable!(); + } + + fn file_range(&self) -> (u64, u64) { + unreachable!(); + } + + fn data(&self) -> Result<&'data [u8]> { + unreachable!(); + } + + fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> { + unreachable!(); + } + + fn name_bytes(&self) -> Result<Option<&[u8]>> { + unreachable!(); + } + + fn name(&self) -> Result<Option<&str>> { + unreachable!(); + } + + fn flags(&self) -> SegmentFlags { + unreachable!(); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/symbol.rs b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/symbol.rs new file mode 100644 index 0000000..248976a --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/symbol.rs
@@ -0,0 +1,836 @@ +use alloc::fmt; +use core::convert::TryInto; +use core::fmt::Debug; +use core::marker::PhantomData; +use core::str; + +use crate::endian::{BigEndian as BE, U32Bytes}; +use crate::pod::{bytes_of, Pod}; +use crate::read::{ + self, Bytes, Error, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, + StringTable, SymbolFlags, SymbolIndex, SymbolKind, SymbolScope, SymbolSection, +}; +use crate::xcoff; + +use super::{FileHeader, XcoffFile}; + +/// A table of symbol entries in an XCOFF file. +/// +/// Also includes the string table used for the symbol names. +/// +/// Returned by [`FileHeader::symbols`]. +#[derive(Debug)] +pub struct SymbolTable<'data, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + symbols: &'data [xcoff::SymbolBytes], + strings: StringTable<'data, R>, + header: PhantomData<Xcoff>, +} + +impl<'data, Xcoff, R> Default for SymbolTable<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + fn default() -> Self { + Self { + symbols: &[], + strings: StringTable::default(), + header: PhantomData, + } + } +} + +impl<'data, Xcoff, R> SymbolTable<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + /// Parse the symbol table. + pub fn parse(header: Xcoff, data: R) -> Result<Self> { + let mut offset = header.f_symptr().into(); + let (symbols, strings) = if offset != 0 { + let symbols = data + .read_slice(&mut offset, header.f_nsyms() as usize) + .read_error("Invalid XCOFF symbol table offset or size")?; + + // Parse the string table. + // Note: don't update data when reading length; the length includes itself. + let length = data + .read_at::<U32Bytes<_>>(offset) + .read_error("Missing XCOFF string table")? + .get(BE); + let str_end = offset + .checked_add(length as u64) + .read_error("Invalid XCOFF string table length")?; + let strings = StringTable::new(data, offset, str_end); + + (symbols, strings) + } else { + (&[][..], StringTable::default()) + }; + + Ok(SymbolTable { + symbols, + strings, + header: PhantomData, + }) + } + + /// Return the string table used for the symbol names. + #[inline] + pub fn strings(&self) -> StringTable<'data, R> { + self.strings + } + + /// Iterate over the symbols. + /// + /// This does not return null symbols. + #[inline] + pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table, Xcoff, R> { + SymbolIterator { + symbols: self, + index: 0, + } + } + + /// Empty symbol iterator. + #[inline] + pub(super) fn iter_none<'table>(&'table self) -> SymbolIterator<'data, 'table, Xcoff, R> { + SymbolIterator { + symbols: self, + index: self.symbols.len(), + } + } + + /// Return the symbol entry at the given index and offset. + pub fn get<T: Pod>(&self, index: SymbolIndex, offset: usize) -> Result<&'data T> { + let entry = index + .0 + .checked_add(offset) + .and_then(|x| self.symbols.get(x)) + .read_error("Invalid XCOFF symbol index")?; + let bytes = bytes_of(entry); + Bytes(bytes).read().read_error("Invalid XCOFF symbol data") + } + + /// Get the symbol at the given index. + /// + /// This does not check if the symbol is null, but does check if the index is in bounds. + fn symbol_unchecked(&self, index: SymbolIndex) -> Result<&'data Xcoff::Symbol> { + self.get::<Xcoff::Symbol>(index, 0) + } + + /// Get the symbol at the given index. + /// + /// Returns an error for null symbols and out of bounds indices. + /// Note that this is unable to check whether the index is an auxiliary symbol. + pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Xcoff::Symbol> { + let symbol = self.symbol_unchecked(index)?; + if symbol.is_null() { + return Err(Error("Invalid XCOFF symbol index")); + } + Ok(symbol) + } + + /// Return a file auxiliary symbol. + pub fn aux_file(&self, index: SymbolIndex, offset: usize) -> Result<&'data Xcoff::FileAux> { + debug_assert!(self.symbol(index)?.has_aux_file()); + let aux_file = self.get::<Xcoff::FileAux>(index, offset)?; + if let Some(aux_type) = aux_file.x_auxtype() { + if aux_type != xcoff::AUX_FILE { + return Err(Error("Invalid index for file auxiliary symbol.")); + } + } + Ok(aux_file) + } + + /// Return the csect auxiliary symbol. + pub fn aux_csect(&self, index: SymbolIndex, offset: usize) -> Result<&'data Xcoff::CsectAux> { + debug_assert!(self.symbol(index)?.has_aux_csect()); + let aux_csect = self.get::<Xcoff::CsectAux>(index, offset)?; + if let Some(aux_type) = aux_csect.x_auxtype() { + if aux_type != xcoff::AUX_CSECT { + return Err(Error("Invalid index/offset for csect auxiliary symbol.")); + } + } + Ok(aux_csect) + } + + /// Return true if the symbol table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// The number of symbol table entries. + /// + /// This includes auxiliary symbol table entries. + #[inline] + pub fn len(&self) -> usize { + self.symbols.len() + } +} + +/// An iterator for symbol entries in an XCOFF file. +/// +/// Yields the index and symbol structure for each symbol. +#[derive(Debug)] +pub struct SymbolIterator<'data, 'table, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + symbols: &'table SymbolTable<'data, Xcoff, R>, + index: usize, +} + +impl<'data, 'table, Xcoff: FileHeader, R: ReadRef<'data>> Iterator + for SymbolIterator<'data, 'table, Xcoff, R> +{ + type Item = (SymbolIndex, &'data Xcoff::Symbol); + + fn next(&mut self) -> Option<Self::Item> { + loop { + let index = SymbolIndex(self.index); + let symbol = self.symbols.symbol_unchecked(index).ok()?; + self.index += 1 + symbol.n_numaux() as usize; + if !symbol.is_null() { + return Some((index, symbol)); + } + } + } +} + +/// A symbol table in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSymbolTable32<'data, 'file, R = &'data [u8]> = + XcoffSymbolTable<'data, 'file, xcoff::FileHeader32, R>; +/// A symbol table in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSymbolTable64<'data, 'file, R = &'data [u8]> = + XcoffSymbolTable<'data, 'file, xcoff::FileHeader64, R>; + +/// A symbol table in an [`XcoffFile`]. +#[derive(Debug, Clone, Copy)] +pub struct XcoffSymbolTable<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> read::private::Sealed + for XcoffSymbolTable<'data, 'file, Xcoff, R> +{ +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data> + for XcoffSymbolTable<'data, 'file, Xcoff, R> +{ + type Symbol = XcoffSymbol<'data, 'file, Xcoff, R>; + type SymbolIterator = XcoffSymbolIterator<'data, 'file, Xcoff, R>; + + fn symbols(&self) -> Self::SymbolIterator { + XcoffSymbolIterator { + file: self.file, + symbols: self.symbols.iter(), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> read::Result<Self::Symbol> { + let symbol = self.symbols.symbol(index)?; + Ok(XcoffSymbol { + file: self.file, + symbols: self.symbols, + index, + symbol, + }) + } +} + +/// An iterator for the symbols in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSymbolIterator32<'data, 'file, R = &'data [u8]> = + XcoffSymbolIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator for the symbols in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSymbolIterator64<'data, 'file, R = &'data [u8]> = + XcoffSymbolIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator for the symbols in an [`XcoffFile`]. +pub struct XcoffSymbolIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) symbols: SymbolIterator<'data, 'file, Xcoff, R>, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> fmt::Debug + for XcoffSymbolIterator<'data, 'file, Xcoff, R> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("XcoffSymbolIterator").finish() + } +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> Iterator + for XcoffSymbolIterator<'data, 'file, Xcoff, R> +{ + type Item = XcoffSymbol<'data, 'file, Xcoff, R>; + + fn next(&mut self) -> Option<Self::Item> { + let (index, symbol) = self.symbols.next()?; + Some(XcoffSymbol { + file: self.file, + symbols: self.symbols.symbols, + index, + symbol, + }) + } +} + +/// A symbol in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSymbol32<'data, 'file, R = &'data [u8]> = + XcoffSymbol<'data, 'file, xcoff::FileHeader32, R>; +/// A symbol in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSymbol64<'data, 'file, R = &'data [u8]> = + XcoffSymbol<'data, 'file, xcoff::FileHeader64, R>; + +/// A symbol in an [`XcoffFile`]. +/// +/// Most functionality is provided by the [`ObjectSymbol`] trait implementation. +#[derive(Debug, Clone, Copy)] +pub struct XcoffSymbol<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, + pub(super) index: SymbolIndex, + pub(super) symbol: &'data Xcoff::Symbol, +} + +impl<'data, 'file, Xcoff, R> XcoffSymbol<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + /// Get the XCOFF file containing this symbol. + pub fn xcoff_file(&self) -> &'file XcoffFile<'data, Xcoff, R> { + self.file + } + + /// Get the raw XCOFF symbol structure. + pub fn xcoff_symbol(&self) -> &'data Xcoff::Symbol { + self.symbol + } +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> read::private::Sealed + for XcoffSymbol<'data, 'file, Xcoff, R> +{ +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> + for XcoffSymbol<'data, 'file, Xcoff, R> +{ + #[inline] + fn index(&self) -> SymbolIndex { + self.index + } + + fn name_bytes(&self) -> Result<&'data [u8]> { + if self.symbol.has_aux_file() { + // By convention the file name is in the first auxiliary entry. + self.symbols + .aux_file(self.index, 1)? + .fname(self.symbols.strings) + } else { + self.symbol.name(self.symbols.strings) + } + } + + fn name(&self) -> Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 XCOFF symbol name") + } + + #[inline] + fn address(&self) -> u64 { + match self.symbol.n_sclass() { + // Relocatable address. + xcoff::C_EXT + | xcoff::C_WEAKEXT + | xcoff::C_HIDEXT + | xcoff::C_FCN + | xcoff::C_BLOCK + | xcoff::C_STAT + | xcoff::C_INFO => self.symbol.n_value().into(), + _ => 0, + } + } + + #[inline] + fn size(&self) -> u64 { + if self.symbol.has_aux_csect() { + // XCOFF32 must have the csect auxiliary entry as the last auxiliary entry. + // XCOFF64 doesn't require this, but conventionally does. + if let Ok(aux_csect) = self + .file + .symbols + .aux_csect(self.index, self.symbol.n_numaux() as usize) + { + let sym_type = aux_csect.sym_type(); + if sym_type == xcoff::XTY_SD || sym_type == xcoff::XTY_CM { + return aux_csect.x_scnlen(); + } + } + } + 0 + } + + fn kind(&self) -> SymbolKind { + if self.symbol.has_aux_csect() { + if let Ok(aux_csect) = self + .file + .symbols + .aux_csect(self.index, self.symbol.n_numaux() as usize) + { + let sym_type = aux_csect.sym_type(); + if sym_type == xcoff::XTY_SD || sym_type == xcoff::XTY_CM { + return match aux_csect.x_smclas() { + xcoff::XMC_PR | xcoff::XMC_GL => SymbolKind::Text, + xcoff::XMC_RO | xcoff::XMC_RW | xcoff::XMC_TD | xcoff::XMC_BS => { + SymbolKind::Data + } + xcoff::XMC_TL | xcoff::XMC_UL => SymbolKind::Tls, + xcoff::XMC_DS | xcoff::XMC_TC0 | xcoff::XMC_TC => { + // `Metadata` might be a better kind for these if we had it. + SymbolKind::Data + } + _ => SymbolKind::Unknown, + }; + } else if sym_type == xcoff::XTY_LD { + // A function entry point. Neither `Text` nor `Label` are a good fit for this. + return SymbolKind::Text; + } else if sym_type == xcoff::XTY_ER { + return SymbolKind::Unknown; + } + } + } + match self.symbol.n_sclass() { + xcoff::C_FILE => SymbolKind::File, + _ => SymbolKind::Unknown, + } + } + + fn section(&self) -> SymbolSection { + match self.symbol.n_scnum() { + xcoff::N_ABS => SymbolSection::Absolute, + xcoff::N_UNDEF => SymbolSection::Undefined, + xcoff::N_DEBUG => SymbolSection::None, + index if index > 0 => SymbolSection::Section(SectionIndex(index as usize)), + _ => SymbolSection::Unknown, + } + } + + #[inline] + fn is_undefined(&self) -> bool { + self.symbol.is_undefined() + } + + /// Return true if the symbol is a definition of a function or data object. + #[inline] + fn is_definition(&self) -> bool { + if self.symbol.n_scnum() <= 0 { + return false; + } + if self.symbol.has_aux_csect() { + if let Ok(aux_csect) = self + .symbols + .aux_csect(self.index, self.symbol.n_numaux() as usize) + { + let sym_type = aux_csect.sym_type(); + sym_type == xcoff::XTY_SD || sym_type == xcoff::XTY_LD || sym_type == xcoff::XTY_CM + } else { + false + } + } else { + false + } + } + + #[inline] + fn is_common(&self) -> bool { + self.symbol.n_sclass() == xcoff::C_EXT && self.symbol.n_scnum() == xcoff::N_UNDEF + } + + #[inline] + fn is_weak(&self) -> bool { + self.symbol.n_sclass() == xcoff::C_WEAKEXT + } + + fn scope(&self) -> SymbolScope { + if self.symbol.n_scnum() == xcoff::N_UNDEF { + SymbolScope::Unknown + } else { + match self.symbol.n_sclass() { + xcoff::C_EXT | xcoff::C_WEAKEXT => { + let visibility = self.symbol.n_type() & xcoff::SYM_V_MASK; + if visibility == xcoff::SYM_V_HIDDEN { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + } + } + _ => SymbolScope::Compilation, + } + } + } + + #[inline] + fn is_global(&self) -> bool { + match self.symbol.n_sclass() { + xcoff::C_EXT | xcoff::C_WEAKEXT => true, + _ => false, + } + } + + #[inline] + fn is_local(&self) -> bool { + !self.is_global() + } + + #[inline] + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { + let mut x_smtyp = 0; + let mut x_smclas = 0; + let mut containing_csect = None; + if self.symbol.has_aux_csect() { + if let Ok(aux_csect) = self + .file + .symbols + .aux_csect(self.index, self.symbol.n_numaux() as usize) + { + x_smtyp = aux_csect.x_smtyp(); + x_smclas = aux_csect.x_smclas(); + if aux_csect.sym_type() == xcoff::XTY_LD { + containing_csect = Some(SymbolIndex(aux_csect.x_scnlen() as usize)) + } + } + } + SymbolFlags::Xcoff { + n_sclass: self.symbol.n_sclass(), + x_smtyp, + x_smclas, + containing_csect, + } + } +} + +/// A trait for generic access to [`xcoff::Symbol32`] and [`xcoff::Symbol64`]. +#[allow(missing_docs)] +pub trait Symbol: Debug + Pod { + type Word: Into<u64>; + + fn n_value(&self) -> Self::Word; + fn n_scnum(&self) -> i16; + fn n_type(&self) -> u16; + fn n_sclass(&self) -> u8; + fn n_numaux(&self) -> u8; + + fn name_offset(&self) -> Option<u32>; + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]>; + + /// Return the section index for the symbol. + fn section(&self) -> Option<SectionIndex> { + let index = self.n_scnum(); + if index > 0 { + Some(SectionIndex(index as usize)) + } else { + None + } + } + + /// Return true if the symbol is a null placeholder. + #[inline] + fn is_null(&self) -> bool { + self.n_sclass() == xcoff::C_NULL + } + + /// Return true if the symbol is undefined. + #[inline] + fn is_undefined(&self) -> bool { + let n_sclass = self.n_sclass(); + (n_sclass == xcoff::C_EXT || n_sclass == xcoff::C_WEAKEXT) + && self.n_scnum() == xcoff::N_UNDEF + } + + /// Return true if the symbol has file auxiliary entry. + fn has_aux_file(&self) -> bool { + self.n_numaux() > 0 && self.n_sclass() == xcoff::C_FILE + } + + /// Return true if the symbol has csect auxiliary entry. + /// + /// A csect auxiliary entry is required for each symbol table entry that has + /// a storage class value of C_EXT, C_WEAKEXT, or C_HIDEXT. + fn has_aux_csect(&self) -> bool { + let sclass = self.n_sclass(); + self.n_numaux() > 0 + && (sclass == xcoff::C_EXT || sclass == xcoff::C_WEAKEXT || sclass == xcoff::C_HIDEXT) + } +} + +impl Symbol for xcoff::Symbol64 { + type Word = u64; + + fn n_value(&self) -> Self::Word { + self.n_value.get(BE) + } + + fn n_scnum(&self) -> i16 { + self.n_scnum.get(BE) + } + + fn n_type(&self) -> u16 { + self.n_type.get(BE) + } + + fn n_sclass(&self) -> u8 { + self.n_sclass + } + + fn n_numaux(&self) -> u8 { + self.n_numaux + } + + fn name_offset(&self) -> Option<u32> { + Some(self.n_offset.get(BE)) + } + + /// Parse the symbol name for XCOFF64. + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.n_offset.get(BE)) + .read_error("Invalid XCOFF symbol name offset") + } +} + +impl Symbol for xcoff::Symbol32 { + type Word = u32; + + fn n_value(&self) -> Self::Word { + self.n_value.get(BE) + } + + fn n_scnum(&self) -> i16 { + self.n_scnum.get(BE) + } + + fn n_type(&self) -> u16 { + self.n_type.get(BE) + } + + fn n_sclass(&self) -> u8 { + self.n_sclass + } + + fn n_numaux(&self) -> u8 { + self.n_numaux + } + + fn name_offset(&self) -> Option<u32> { + if self.n_name[0] == 0 { + let offset = u32::from_be_bytes(self.n_name[4..8].try_into().unwrap()); + Some(offset) + } else { + None + } + } + + /// Parse the symbol name for XCOFF32. + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + if let Some(offset) = self.name_offset() { + // If the name starts with 0 then the last 4 bytes are a string table offset. + strings + .get(offset) + .read_error("Invalid XCOFF symbol name offset") + } else { + // The name is inline and padded with nulls. + Ok(match memchr::memchr(b'\0', &self.n_name) { + Some(end) => &self.n_name[..end], + None => &self.n_name, + }) + } + } +} + +/// A trait for generic access to [`xcoff::FileAux32`] and [`xcoff::FileAux64`]. +#[allow(missing_docs)] +pub trait FileAux: Debug + Pod { + fn x_fname(&self) -> &[u8; 8]; + fn x_ftype(&self) -> u8; + fn x_auxtype(&self) -> Option<u8>; + + fn name_offset(&self) -> Option<u32> { + let x_fname = self.x_fname(); + if x_fname[0] == 0 { + Some(u32::from_be_bytes(x_fname[4..8].try_into().unwrap())) + } else { + None + } + } + + /// Parse the x_fname field, which may be an inline string or a string table offset. + fn fname<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + if let Some(offset) = self.name_offset() { + // If the name starts with 0 then the last 4 bytes are a string table offset. + strings + .get(offset) + .read_error("Invalid XCOFF symbol name offset") + } else { + // The name is inline and padded with nulls. + let x_fname = self.x_fname(); + Ok(match memchr::memchr(b'\0', x_fname) { + Some(end) => &x_fname[..end], + None => x_fname, + }) + } + } +} + +impl FileAux for xcoff::FileAux64 { + fn x_fname(&self) -> &[u8; 8] { + &self.x_fname + } + + fn x_ftype(&self) -> u8 { + self.x_ftype + } + + fn x_auxtype(&self) -> Option<u8> { + Some(self.x_auxtype) + } +} + +impl FileAux for xcoff::FileAux32 { + fn x_fname(&self) -> &[u8; 8] { + &self.x_fname + } + + fn x_ftype(&self) -> u8 { + self.x_ftype + } + + fn x_auxtype(&self) -> Option<u8> { + None + } +} + +/// A trait for generic access to [`xcoff::CsectAux32`] and [`xcoff::CsectAux64`]. +#[allow(missing_docs)] +pub trait CsectAux: Debug + Pod { + fn x_scnlen(&self) -> u64; + fn x_parmhash(&self) -> u32; + fn x_snhash(&self) -> u16; + fn x_smtyp(&self) -> u8; + fn x_smclas(&self) -> u8; + fn x_stab(&self) -> Option<u32>; + fn x_snstab(&self) -> Option<u16>; + fn x_auxtype(&self) -> Option<u8>; + + fn alignment(&self) -> u8 { + self.x_smtyp() >> 3 + } + fn sym_type(&self) -> u8 { + self.x_smtyp() & 0x07 + } +} + +impl CsectAux for xcoff::CsectAux64 { + fn x_scnlen(&self) -> u64 { + self.x_scnlen_lo.get(BE) as u64 | ((self.x_scnlen_hi.get(BE) as u64) << 32) + } + + fn x_parmhash(&self) -> u32 { + self.x_parmhash.get(BE) + } + + fn x_snhash(&self) -> u16 { + self.x_snhash.get(BE) + } + + fn x_smtyp(&self) -> u8 { + self.x_smtyp + } + + fn x_smclas(&self) -> u8 { + self.x_smclas + } + + fn x_stab(&self) -> Option<u32> { + None + } + + fn x_snstab(&self) -> Option<u16> { + None + } + + fn x_auxtype(&self) -> Option<u8> { + Some(self.x_auxtype) + } +} + +impl CsectAux for xcoff::CsectAux32 { + fn x_scnlen(&self) -> u64 { + self.x_scnlen.get(BE) as u64 + } + + fn x_parmhash(&self) -> u32 { + self.x_parmhash.get(BE) + } + + fn x_snhash(&self) -> u16 { + self.x_snhash.get(BE) + } + + fn x_smtyp(&self) -> u8 { + self.x_smtyp + } + + fn x_smclas(&self) -> u8 { + self.x_smclas + } + + fn x_stab(&self) -> Option<u32> { + Some(self.x_stab.get(BE)) + } + + fn x_snstab(&self) -> Option<u16> { + Some(self.x_snstab.get(BE)) + } + + fn x_auxtype(&self) -> Option<u8> { + None + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/coff/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/write/coff/mod.rs new file mode 100644 index 0000000..6e0f5ed --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/coff/mod.rs
@@ -0,0 +1,10 @@ +//! Support for writing COFF files. +//! +//! Provides [`Writer`] for low level writing of COFF files. +//! This is also used to provide COFF support for [`write::Object`](crate::write::Object). + +mod object; +pub use self::object::*; + +mod writer; +pub use writer::*;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/coff/object.rs b/chromium_crates_io/vendor/object-v0_37/src/write/coff/object.rs new file mode 100644 index 0000000..dc63116 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/coff/object.rs
@@ -0,0 +1,831 @@ +use alloc::vec::Vec; + +use crate::endian::*; +use crate::pe as coff; +use crate::write::coff::writer; +use crate::write::util::*; +use crate::write::*; + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + name: writer::Name, + offset: u32, + reloc_offset: u32, + selection: u8, + associative_section: u32, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + name: writer::Name, + index: u32, + aux_count: u8, +} + +/// Internal format to use for the `.drectve` section containing linker +/// directives for symbol exports. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CoffExportStyle { + /// MSVC format supported by link.exe and LLD. + Msvc, + /// Gnu format supported by GNU LD and LLD. + Gnu, +} + +impl<'a> Object<'a> { + pub(crate) fn coff_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { + match section { + StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None), + StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None), + StandardSection::ReadOnlyData + | StandardSection::ReadOnlyDataWithRel + | StandardSection::ReadOnlyString => ( + &[], + &b".rdata"[..], + SectionKind::ReadOnlyData, + SectionFlags::None, + ), + StandardSection::UninitializedData => ( + &[], + &b".bss"[..], + SectionKind::UninitializedData, + SectionFlags::None, + ), + // TLS sections are data sections with a special name. + StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Data, SectionFlags::None), + StandardSection::UninitializedTls => { + // Unsupported section. + (&[], &[], SectionKind::UninitializedTls, SectionFlags::None) + } + StandardSection::TlsVariables => { + // Unsupported section. + (&[], &[], SectionKind::TlsVariables, SectionFlags::None) + } + StandardSection::Common => { + // Unsupported section. + (&[], &[], SectionKind::Common, SectionFlags::None) + } + StandardSection::GnuProperty => { + // Unsupported section. + (&[], &[], SectionKind::Note, SectionFlags::None) + } + } + } + + pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + let mut name = section.to_vec(); + if !value.is_empty() { + name.push(b'$'); + name.extend_from_slice(value); + } + name + } + + pub(crate) fn coff_section_flags(&self, section: &Section<'_>) -> SectionFlags { + let characteristics = match section.kind { + SectionKind::Text => { + coff::IMAGE_SCN_CNT_CODE | coff::IMAGE_SCN_MEM_EXECUTE | coff::IMAGE_SCN_MEM_READ + } + SectionKind::Data => { + coff::IMAGE_SCN_CNT_INITIALIZED_DATA + | coff::IMAGE_SCN_MEM_READ + | coff::IMAGE_SCN_MEM_WRITE + } + SectionKind::UninitializedData => { + coff::IMAGE_SCN_CNT_UNINITIALIZED_DATA + | coff::IMAGE_SCN_MEM_READ + | coff::IMAGE_SCN_MEM_WRITE + } + SectionKind::ReadOnlyData + | SectionKind::ReadOnlyDataWithRel + | SectionKind::ReadOnlyString => { + coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ + } + SectionKind::Debug + | SectionKind::DebugString + | SectionKind::Other + | SectionKind::OtherString => { + coff::IMAGE_SCN_CNT_INITIALIZED_DATA + | coff::IMAGE_SCN_MEM_READ + | coff::IMAGE_SCN_MEM_DISCARDABLE + } + SectionKind::Linker => coff::IMAGE_SCN_LNK_INFO | coff::IMAGE_SCN_LNK_REMOVE, + SectionKind::Common + | SectionKind::Tls + | SectionKind::UninitializedTls + | SectionKind::TlsVariables + | SectionKind::Note + | SectionKind::Unknown + | SectionKind::Metadata + | SectionKind::Elf(_) => { + return SectionFlags::None; + } + }; + SectionFlags::Coff { characteristics } + } + + pub(crate) fn coff_symbol_flags(&self, _symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> { + // TODO: Need SymbolFlags::Coff for COFF-specific flags (type and storage class). + SymbolFlags::None + } + + pub(crate) fn coff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { + use RelocationEncoding as E; + use RelocationKind as K; + + let (mut kind, encoding, size) = if let RelocationFlags::Generic { + kind, + encoding, + size, + } = reloc.flags + { + (kind, encoding, size) + } else { + return Ok(()); + }; + if kind == K::GotRelative { + // Use a stub symbol for the relocation instead. + // This isn't really a GOT, but it's a similar purpose. + // TODO: need to handle DLL imports differently? + kind = K::Relative; + reloc.symbol = self.coff_add_stub_symbol(reloc.symbol)?; + } else if kind == K::PltRelative { + // Windows doesn't need a separate relocation type for + // references to functions in import libraries. + // For convenience, treat this the same as Relative. + kind = K::Relative; + } + + let unsupported_reloc = || Err(Error(format!("unimplemented relocation {:?}", reloc))); + let typ = match self.architecture { + Architecture::I386 => match (kind, size) { + (K::Absolute, 16) => coff::IMAGE_REL_I386_DIR16, + (K::Relative, 16) => coff::IMAGE_REL_I386_REL16, + (K::Absolute, 32) => coff::IMAGE_REL_I386_DIR32, + (K::ImageOffset, 32) => coff::IMAGE_REL_I386_DIR32NB, + (K::SectionIndex, 16) => coff::IMAGE_REL_I386_SECTION, + (K::SectionOffset, 32) => coff::IMAGE_REL_I386_SECREL, + (K::SectionOffset, 7) => coff::IMAGE_REL_I386_SECREL7, + (K::Relative, 32) => coff::IMAGE_REL_I386_REL32, + _ => return unsupported_reloc(), + }, + Architecture::X86_64 => match (kind, size) { + (K::Absolute, 64) => coff::IMAGE_REL_AMD64_ADDR64, + (K::Absolute, 32) => coff::IMAGE_REL_AMD64_ADDR32, + (K::ImageOffset, 32) => coff::IMAGE_REL_AMD64_ADDR32NB, + (K::Relative, 32) => match reloc.addend { + -5 => coff::IMAGE_REL_AMD64_REL32_1, + -6 => coff::IMAGE_REL_AMD64_REL32_2, + -7 => coff::IMAGE_REL_AMD64_REL32_3, + -8 => coff::IMAGE_REL_AMD64_REL32_4, + -9 => coff::IMAGE_REL_AMD64_REL32_5, + _ => coff::IMAGE_REL_AMD64_REL32, + }, + (K::SectionIndex, 16) => coff::IMAGE_REL_AMD64_SECTION, + (K::SectionOffset, 32) => coff::IMAGE_REL_AMD64_SECREL, + (K::SectionOffset, 7) => coff::IMAGE_REL_AMD64_SECREL7, + _ => return unsupported_reloc(), + }, + Architecture::Arm => match (kind, size) { + (K::Absolute, 32) => coff::IMAGE_REL_ARM_ADDR32, + (K::ImageOffset, 32) => coff::IMAGE_REL_ARM_ADDR32NB, + (K::Relative, 32) => coff::IMAGE_REL_ARM_REL32, + (K::SectionIndex, 16) => coff::IMAGE_REL_ARM_SECTION, + (K::SectionOffset, 32) => coff::IMAGE_REL_ARM_SECREL, + _ => return unsupported_reloc(), + }, + Architecture::Aarch64 => match (kind, encoding, size) { + (K::Absolute, _, 32) => coff::IMAGE_REL_ARM64_ADDR32, + (K::ImageOffset, _, 32) => coff::IMAGE_REL_ARM64_ADDR32NB, + (K::SectionIndex, _, 16) => coff::IMAGE_REL_ARM64_SECTION, + (K::SectionOffset, _, 32) => coff::IMAGE_REL_ARM64_SECREL, + (K::Absolute, _, 64) => coff::IMAGE_REL_ARM64_ADDR64, + (K::Relative, _, 32) => coff::IMAGE_REL_ARM64_REL32, + (K::Relative, E::AArch64Call, 26) => coff::IMAGE_REL_ARM64_BRANCH26, + _ => return unsupported_reloc(), + }, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }; + reloc.flags = RelocationFlags::Coff { typ }; + Ok(()) + } + + pub(crate) fn coff_adjust_addend(&self, relocation: &mut Relocation) -> Result<bool> { + let typ = if let RelocationFlags::Coff { typ } = relocation.flags { + typ + } else { + return Err(Error(format!("invalid relocation flags {:?}", relocation))); + }; + let offset = match self.architecture { + Architecture::Arm => { + if typ == coff::IMAGE_REL_ARM_REL32 { + 4 + } else { + 0 + } + } + Architecture::Aarch64 => { + if typ == coff::IMAGE_REL_ARM64_REL32 { + 4 + } else { + 0 + } + } + Architecture::I386 => { + if typ == coff::IMAGE_REL_I386_REL32 { + 4 + } else { + 0 + } + } + Architecture::X86_64 => match typ { + coff::IMAGE_REL_AMD64_REL32 => 4, + coff::IMAGE_REL_AMD64_REL32_1 => 5, + coff::IMAGE_REL_AMD64_REL32_2 => 6, + coff::IMAGE_REL_AMD64_REL32_3 => 7, + coff::IMAGE_REL_AMD64_REL32_4 => 8, + coff::IMAGE_REL_AMD64_REL32_5 => 9, + _ => 0, + }, + Architecture::PowerPc | Architecture::PowerPc64 => 0, + _ => return Err(Error(format!("unimplemented relocation {:?}", relocation))), + }; + relocation.addend += offset; + Ok(true) + } + + pub(crate) fn coff_relocation_size(&self, reloc: &Relocation) -> Result<u8> { + let typ = if let RelocationFlags::Coff { typ } = reloc.flags { + typ + } else { + return Err(Error(format!("unexpected relocation for size {:?}", reloc))); + }; + let size = match self.architecture { + Architecture::I386 => match typ { + coff::IMAGE_REL_I386_DIR16 + | coff::IMAGE_REL_I386_REL16 + | coff::IMAGE_REL_I386_SECTION => Some(16), + coff::IMAGE_REL_I386_DIR32 + | coff::IMAGE_REL_I386_DIR32NB + | coff::IMAGE_REL_I386_SECREL + | coff::IMAGE_REL_I386_TOKEN + | coff::IMAGE_REL_I386_REL32 => Some(32), + _ => None, + }, + Architecture::X86_64 => match typ { + coff::IMAGE_REL_AMD64_SECTION => Some(16), + coff::IMAGE_REL_AMD64_ADDR32 + | coff::IMAGE_REL_AMD64_ADDR32NB + | coff::IMAGE_REL_AMD64_REL32 + | coff::IMAGE_REL_AMD64_REL32_1 + | coff::IMAGE_REL_AMD64_REL32_2 + | coff::IMAGE_REL_AMD64_REL32_3 + | coff::IMAGE_REL_AMD64_REL32_4 + | coff::IMAGE_REL_AMD64_REL32_5 + | coff::IMAGE_REL_AMD64_SECREL + | coff::IMAGE_REL_AMD64_TOKEN => Some(32), + coff::IMAGE_REL_AMD64_ADDR64 => Some(64), + _ => None, + }, + Architecture::Arm => match typ { + coff::IMAGE_REL_ARM_SECTION => Some(16), + coff::IMAGE_REL_ARM_ADDR32 + | coff::IMAGE_REL_ARM_ADDR32NB + | coff::IMAGE_REL_ARM_TOKEN + | coff::IMAGE_REL_ARM_REL32 + | coff::IMAGE_REL_ARM_SECREL => Some(32), + _ => None, + }, + Architecture::Aarch64 => match typ { + coff::IMAGE_REL_ARM64_SECTION => Some(16), + coff::IMAGE_REL_ARM64_ADDR32 + | coff::IMAGE_REL_ARM64_ADDR32NB + | coff::IMAGE_REL_ARM64_SECREL + | coff::IMAGE_REL_ARM64_TOKEN + | coff::IMAGE_REL_ARM64_REL32 => Some(32), + coff::IMAGE_REL_ARM64_ADDR64 => Some(64), + _ => None, + }, + _ => None, + }; + size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc))) + } + + fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> Result<SymbolId> { + if let Some(stub_id) = self.stub_symbols.get(&symbol_id) { + return Ok(*stub_id); + } + let stub_size = self.architecture.address_size().unwrap().bytes(); + + let name = b".rdata$.refptr".to_vec(); + let section_id = self.add_section(Vec::new(), name, SectionKind::ReadOnlyData); + let section = self.section_mut(section_id); + section.set_data(vec![0; stub_size as usize], u64::from(stub_size)); + self.add_relocation( + section_id, + Relocation { + offset: 0, + symbol: symbol_id, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: stub_size * 8, + }, + }, + )?; + + let mut name = b".refptr.".to_vec(); + name.extend_from_slice(&self.symbol(symbol_id).name); + let stub_id = self.add_raw_symbol(Symbol { + name, + value: 0, + size: u64::from(stub_size), + kind: SymbolKind::Data, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }); + self.stub_symbols.insert(symbol_id, stub_id); + + Ok(stub_id) + } + + /// Appends linker directives to the `.drectve` section to tell the linker + /// to export all symbols with `SymbolScope::Dynamic`. + /// + /// This must be called after all symbols have been defined. + pub fn add_coff_exports(&mut self, style: CoffExportStyle) { + assert_eq!(self.format, BinaryFormat::Coff); + + let mut directives = vec![]; + for symbol in &self.symbols { + if symbol.scope == SymbolScope::Dynamic { + match style { + CoffExportStyle::Msvc => directives.extend(b" /EXPORT:\""), + CoffExportStyle::Gnu => directives.extend(b" -export:\""), + } + directives.extend(&symbol.name); + directives.extend(b"\""); + if symbol.kind != SymbolKind::Text { + match style { + CoffExportStyle::Msvc => directives.extend(b",DATA"), + CoffExportStyle::Gnu => directives.extend(b",data"), + } + } + } + } + let drectve = self.add_section(vec![], b".drectve".to_vec(), SectionKind::Linker); + self.append_section_data(drectve, &directives, 1); + } + + pub(crate) fn coff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { + let mut writer = writer::Writer::new(buffer); + + // Add section strings to strtab. + let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; + for (index, section) in self.sections.iter().enumerate() { + section_offsets[index].name = writer.add_name(§ion.name); + } + + // Set COMDAT flags. + for comdat in &self.comdats { + let symbol = &self.symbols[comdat.symbol.0]; + let comdat_section = match symbol.section { + SymbolSection::Section(id) => id.0, + _ => { + return Err(Error(format!( + "unsupported COMDAT symbol `{}` section {:?}", + symbol.name().unwrap_or(""), + symbol.section + ))); + } + }; + section_offsets[comdat_section].selection = match comdat.kind { + ComdatKind::NoDuplicates => coff::IMAGE_COMDAT_SELECT_NODUPLICATES, + ComdatKind::Any => coff::IMAGE_COMDAT_SELECT_ANY, + ComdatKind::SameSize => coff::IMAGE_COMDAT_SELECT_SAME_SIZE, + ComdatKind::ExactMatch => coff::IMAGE_COMDAT_SELECT_EXACT_MATCH, + ComdatKind::Largest => coff::IMAGE_COMDAT_SELECT_LARGEST, + ComdatKind::Newest => coff::IMAGE_COMDAT_SELECT_NEWEST, + ComdatKind::Unknown => { + return Err(Error(format!( + "unsupported COMDAT symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + comdat.kind + ))); + } + }; + for id in &comdat.sections { + let section = &self.sections[id.0]; + if section.symbol.is_none() { + return Err(Error(format!( + "missing symbol for COMDAT section `{}`", + section.name().unwrap_or(""), + ))); + } + if id.0 != comdat_section { + section_offsets[id.0].selection = coff::IMAGE_COMDAT_SELECT_ASSOCIATIVE; + section_offsets[id.0].associative_section = comdat_section as u32 + 1; + } + } + } + + // Prepare creation of weak default symbols + let weak_symbol_count = self.symbols.iter().filter(|symbol| symbol.weak).count(); + let mut weak_default_names = HashMap::new(); + let mut weak_default_offsets = HashMap::new(); + + if weak_symbol_count > 0 { + weak_default_names.reserve(weak_symbol_count); + weak_default_offsets.reserve(weak_symbol_count); + + let defined_external_symbol = |symbol: &&Symbol| -> bool { + !symbol.weak + && (symbol.scope == SymbolScope::Linkage + || symbol.scope == SymbolScope::Dynamic) + && (matches!(symbol.section, SymbolSection::Section(_)) + || matches!(symbol.section, SymbolSection::Absolute)) + }; + + let mut weak_default_unique_name = Default::default(); + + // search for an external symbol defined in a non-COMDAT section to + // use for the weak default names + for symbol in self.symbols.iter().filter(defined_external_symbol) { + let SymbolSection::Section(section_id) = symbol.section else { + weak_default_unique_name = &*symbol.name; + break; + }; + + if !self + .comdats + .iter() + .flat_map(|comdat| comdat.sections.iter()) + .any(|comdat_section| *comdat_section == section_id) + { + weak_default_unique_name = &*symbol.name; + break; + } + } + + // fallback to also include COMDAT defined symbols + if weak_default_unique_name.is_empty() { + for symbol in self.symbols.iter().filter(defined_external_symbol) { + if matches!(symbol.section, SymbolSection::Section(_)) { + weak_default_unique_name = &*symbol.name; + break; + } + } + } + + // create and store the names for the weak default symbols + for (index, symbol) in self + .symbols + .iter() + .enumerate() + .filter(|(_, symbol)| symbol.weak) + { + let mut weak_default_name = [b".weak.", symbol.name.as_slice()].concat(); + if !weak_default_unique_name.is_empty() { + weak_default_name.push(b'.'); + weak_default_name.extend(weak_default_unique_name); + } + + weak_default_names.insert(index, weak_default_name); + } + } + + // Reserve symbol indices and add symbol strings to strtab. + let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; + for (index, symbol) in self.symbols.iter().enumerate() { + if symbol.weak { + // Reserve the weak default symbol + let weak_default_name = weak_default_names.get(&index).unwrap_or_else(|| { + unreachable!("weak default symbol name should have been created") + }); + + weak_default_offsets.insert( + index, + SymbolOffsets { + name: writer.add_name(weak_default_name.as_slice()), + index: writer.reserve_symbol_index(), + aux_count: 0, + }, + ); + } + + symbol_offsets[index].index = writer.reserve_symbol_index(); + let mut name = &*symbol.name; + match symbol.kind { + _ if symbol.weak => { + symbol_offsets[index].aux_count = writer.reserve_aux_weak_external(); + } + SymbolKind::File => { + // Name goes in auxiliary symbol records. + symbol_offsets[index].aux_count = writer.reserve_aux_file_name(&symbol.name); + name = b".file"; + } + SymbolKind::Section if symbol.section.id().is_some() => { + symbol_offsets[index].aux_count = writer.reserve_aux_section(); + } + _ => {} + }; + symbol_offsets[index].name = writer.add_name(name); + } + + // Reserve file ranges. + writer.reserve_file_header(); + writer.reserve_section_headers(self.sections.len() as u16); + for (index, section) in self.sections.iter().enumerate() { + section_offsets[index].offset = writer.reserve_section(section.data.len()); + section_offsets[index].reloc_offset = + writer.reserve_relocations(section.relocations.len()); + } + writer.reserve_symtab_strtab(); + + // Start writing. + writer.write_file_header(writer::FileHeader { + machine: match (self.architecture, self.sub_architecture, self.endian) { + (Architecture::Arm, None, _) => coff::IMAGE_FILE_MACHINE_ARMNT, + (Architecture::Aarch64, None, _) => coff::IMAGE_FILE_MACHINE_ARM64, + (Architecture::Aarch64, Some(SubArchitecture::Arm64EC), _) => { + coff::IMAGE_FILE_MACHINE_ARM64EC + } + (Architecture::I386, None, _) => coff::IMAGE_FILE_MACHINE_I386, + (Architecture::X86_64, None, _) => coff::IMAGE_FILE_MACHINE_AMD64, + (Architecture::PowerPc | Architecture::PowerPc64, None, Endianness::Little) => { + coff::IMAGE_FILE_MACHINE_POWERPC + } + (Architecture::PowerPc | Architecture::PowerPc64, None, Endianness::Big) => { + coff::IMAGE_FILE_MACHINE_POWERPCBE + } + _ => { + return Err(Error(format!( + "unimplemented architecture {:?} with sub-architecture {:?}", + self.architecture, self.sub_architecture + ))); + } + }, + time_date_stamp: 0, + characteristics: match self.flags { + FileFlags::Coff { characteristics } => characteristics, + _ => 0, + }, + })?; + + // Write section headers. + for (index, section) in self.sections.iter().enumerate() { + let SectionFlags::Coff { + mut characteristics, + .. + } = self.section_flags(section) + else { + return Err(Error(format!( + "unimplemented section `{}` kind {:?}", + section.name().unwrap_or(""), + section.kind + ))); + }; + if section_offsets[index].selection != 0 { + characteristics |= coff::IMAGE_SCN_LNK_COMDAT; + }; + if section.relocations.len() > 0xffff { + characteristics |= coff::IMAGE_SCN_LNK_NRELOC_OVFL; + } + characteristics |= match section.align { + 1 => coff::IMAGE_SCN_ALIGN_1BYTES, + 2 => coff::IMAGE_SCN_ALIGN_2BYTES, + 4 => coff::IMAGE_SCN_ALIGN_4BYTES, + 8 => coff::IMAGE_SCN_ALIGN_8BYTES, + 16 => coff::IMAGE_SCN_ALIGN_16BYTES, + 32 => coff::IMAGE_SCN_ALIGN_32BYTES, + 64 => coff::IMAGE_SCN_ALIGN_64BYTES, + 128 => coff::IMAGE_SCN_ALIGN_128BYTES, + 256 => coff::IMAGE_SCN_ALIGN_256BYTES, + 512 => coff::IMAGE_SCN_ALIGN_512BYTES, + 1024 => coff::IMAGE_SCN_ALIGN_1024BYTES, + 2048 => coff::IMAGE_SCN_ALIGN_2048BYTES, + 4096 => coff::IMAGE_SCN_ALIGN_4096BYTES, + 8192 => coff::IMAGE_SCN_ALIGN_8192BYTES, + _ => { + return Err(Error(format!( + "unimplemented section `{}` align {}", + section.name().unwrap_or(""), + section.align + ))); + } + }; + writer.write_section_header(writer::SectionHeader { + name: section_offsets[index].name, + size_of_raw_data: section.size as u32, + pointer_to_raw_data: section_offsets[index].offset, + pointer_to_relocations: section_offsets[index].reloc_offset, + pointer_to_linenumbers: 0, + number_of_relocations: section.relocations.len() as u32, + number_of_linenumbers: 0, + characteristics, + }); + } + + // Write section data and relocations. + for section in &self.sections { + writer.write_section(§ion.data); + + if !section.relocations.is_empty() { + //debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + writer.write_relocations_count(section.relocations.len()); + for reloc in §ion.relocations { + let typ = if let RelocationFlags::Coff { typ } = reloc.flags { + typ + } else { + return Err(Error("invalid relocation flags".into())); + }; + writer.write_relocation(writer::Relocation { + virtual_address: reloc.offset as u32, + symbol: symbol_offsets[reloc.symbol.0].index, + typ, + }); + } + } + } + + // Write symbols. + for (index, symbol) in self.symbols.iter().enumerate() { + let SymbolFlags::None = symbol.flags else { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))); + }; + let section_number = match symbol.section { + // weak symbols are always undefined + _ if symbol.weak => coff::IMAGE_SYM_UNDEFINED as u16, + SymbolSection::None => { + debug_assert_eq!(symbol.kind, SymbolKind::File); + coff::IMAGE_SYM_DEBUG as u16 + } + SymbolSection::Undefined => coff::IMAGE_SYM_UNDEFINED as u16, + SymbolSection::Absolute => coff::IMAGE_SYM_ABSOLUTE as u16, + SymbolSection::Common => coff::IMAGE_SYM_UNDEFINED as u16, + SymbolSection::Section(id) => id.0 as u16 + 1, + }; + let typ = if symbol.kind == SymbolKind::Text { + coff::IMAGE_SYM_DTYPE_FUNCTION << coff::IMAGE_SYM_DTYPE_SHIFT + } else { + coff::IMAGE_SYM_TYPE_NULL + }; + let storage_class = match symbol.kind { + _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL, + SymbolKind::File => coff::IMAGE_SYM_CLASS_FILE, + SymbolKind::Section => { + if symbol.section.id().is_some() { + coff::IMAGE_SYM_CLASS_STATIC + } else { + coff::IMAGE_SYM_CLASS_SECTION + } + } + SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL, + SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => match symbol.section { + SymbolSection::None => { + return Err(Error(format!( + "missing section for symbol `{}`", + symbol.name().unwrap_or("") + ))); + } + SymbolSection::Undefined | SymbolSection::Common => { + coff::IMAGE_SYM_CLASS_EXTERNAL + } + SymbolSection::Absolute | SymbolSection::Section(_) => match symbol.scope { + SymbolScope::Unknown => { + return Err(Error(format!( + "unimplemented symbol `{}` scope {:?}", + symbol.name().unwrap_or(""), + symbol.scope + ))); + } + SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC, + SymbolScope::Linkage | SymbolScope::Dynamic => { + coff::IMAGE_SYM_CLASS_EXTERNAL + } + }, + }, + SymbolKind::Unknown => match symbol.section { + SymbolSection::Undefined => coff::IMAGE_SYM_CLASS_EXTERNAL, + _ => { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))) + } + }, + }; + let number_of_aux_symbols = symbol_offsets[index].aux_count; + let value = if symbol.weak { + // weak symbols should have a value of 0 + 0 + } else if symbol.section == SymbolSection::Common { + symbol.size as u32 + } else { + symbol.value as u32 + }; + + // write the weak default symbol before the weak symbol + if symbol.weak { + let weak_default_symbol = weak_default_offsets.get(&index).unwrap_or_else(|| { + unreachable!("weak symbol should have a weak default offset") + }); + + writer.write_symbol(writer::Symbol { + name: weak_default_symbol.name, + value: symbol.value as u32, + section_number: match symbol.section { + SymbolSection::Section(id) => id.0 as u16 + 1, + SymbolSection::Undefined => coff::IMAGE_SYM_ABSOLUTE as u16, + o => { + return Err(Error(format!( + "invalid symbol section for weak external `{}` section {o:?}", + symbol.name().unwrap_or("") + ))); + } + }, + number_of_aux_symbols: 0, + typ: 0, + storage_class: coff::IMAGE_SYM_CLASS_EXTERNAL, + }); + } + + writer.write_symbol(writer::Symbol { + name: symbol_offsets[index].name, + value, + section_number, + typ, + storage_class, + number_of_aux_symbols, + }); + + // Write auxiliary symbols. + match symbol.kind { + _ if symbol.weak => { + let weak_default_offset = + weak_default_offsets.get(&index).unwrap_or_else(|| { + unreachable!("weak symbol should have a weak default offset") + }); + + let weak_default_sym_index = weak_default_offset.index; + writer.write_aux_weak_external(writer::AuxSymbolWeak { + weak_default_sym_index, + weak_search_type: coff::IMAGE_WEAK_EXTERN_SEARCH_ALIAS, + }); + } + SymbolKind::File => { + writer.write_aux_file_name(&symbol.name, number_of_aux_symbols); + } + SymbolKind::Section if symbol.section.id().is_some() => { + debug_assert_eq!(number_of_aux_symbols, 1); + let section_index = symbol.section.id().unwrap().0; + let section = &self.sections[section_index]; + writer.write_aux_section(writer::AuxSymbolSection { + length: section.size as u32, + number_of_relocations: section.relocations.len() as u32, + number_of_linenumbers: 0, + check_sum: if section.is_bss() { + 0 + } else { + checksum(section.data()) + }, + number: section_offsets[section_index].associative_section, + selection: section_offsets[section_index].selection, + }); + } + _ => { + debug_assert_eq!(number_of_aux_symbols, 0); + } + } + } + + writer.write_strtab(); + + debug_assert_eq!(writer.reserved_len(), writer.len()); + + Ok(()) + } +} + +// JamCRC +fn checksum(data: &[u8]) -> u32 { + let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff); + hasher.update(data); + !hasher.finalize() +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/coff/writer.rs b/chromium_crates_io/vendor/object-v0_37/src/write/coff/writer.rs new file mode 100644 index 0000000..658321e --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/coff/writer.rs
@@ -0,0 +1,551 @@ +//! Helper for writing COFF files. +use alloc::string::String; +use alloc::vec::Vec; +use core::mem; + +use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32}; +use crate::pe; +use crate::write::string::{StringId, StringTable}; +use crate::write::util; +use crate::write::{Error, Result, WritableBuffer}; + +/// A helper for writing COFF files. +/// +/// Writing uses a two phase approach. The first phase builds up all of the information +/// that may need to be known ahead of time: +/// - build string table +/// - reserve section indices +/// - reserve symbol indices +/// - reserve file ranges for headers and sections +/// +/// Some of the information has ordering requirements. For example, strings must be added +/// to the string table before reserving the file range for the string table. There are debug +/// asserts to check some of these requirements. +/// +/// The second phase writes everything out in order. Thus the caller must ensure writing +/// is in the same order that file ranges were reserved. There are debug asserts to assist +/// with checking this. +#[allow(missing_debug_implementations)] +pub struct Writer<'a> { + buffer: &'a mut dyn WritableBuffer, + len: usize, + + section_num: u16, + + symtab_offset: u32, + symtab_num: u32, + + strtab: StringTable<'a>, + strtab_len: usize, + strtab_offset: u32, + strtab_data: Vec<u8>, +} + +impl<'a> Writer<'a> { + /// Create a new `Writer`. + pub fn new(buffer: &'a mut dyn WritableBuffer) -> Self { + Writer { + buffer, + len: 0, + + section_num: 0, + + symtab_offset: 0, + symtab_num: 0, + + strtab: StringTable::default(), + strtab_len: 0, + strtab_offset: 0, + strtab_data: Vec::new(), + } + } + + /// Return the current file length that has been reserved. + pub fn reserved_len(&self) -> usize { + self.len + } + + /// Return the current file length that has been written. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.buffer.len() + } + + /// Reserve a file range with the given size and starting alignment. + /// + /// Returns the aligned offset of the start of the range. + /// + /// `align_start` must be a power of two. + pub fn reserve(&mut self, len: usize, align_start: usize) -> u32 { + if align_start > 1 { + self.len = util::align(self.len, align_start); + } + let offset = self.len; + self.len += len; + offset as u32 + } + + /// Write alignment padding bytes. + pub fn write_align(&mut self, align_start: usize) { + if align_start > 1 { + util::write_align(self.buffer, align_start); + } + } + + /// Write data. + pub fn write(&mut self, data: &[u8]) { + self.buffer.write_bytes(data); + } + + /// Reserve the file range up to the given file offset. + pub fn reserve_until(&mut self, offset: usize) { + debug_assert!(self.len <= offset); + self.len = offset; + } + + /// Write padding up to the given file offset. + pub fn pad_until(&mut self, offset: usize) { + debug_assert!(self.buffer.len() <= offset); + self.buffer.resize(offset); + } + + /// Reserve the range for the file header. + /// + /// This must be at the start of the file. + pub fn reserve_file_header(&mut self) { + debug_assert_eq!(self.len, 0); + self.reserve(mem::size_of::<pe::ImageFileHeader>(), 1); + } + + /// Write the file header. + /// + /// This must be at the start of the file. + /// + /// Fields that can be derived from known information are automatically set by this function. + pub fn write_file_header(&mut self, header: FileHeader) -> Result<()> { + debug_assert_eq!(self.buffer.len(), 0); + + // Start writing. + self.buffer + .reserve(self.len) + .map_err(|_| Error(String::from("Cannot allocate buffer")))?; + + // Write file header. + let header = pe::ImageFileHeader { + machine: U16::new(LE, header.machine), + number_of_sections: U16::new(LE, self.section_num), + time_date_stamp: U32::new(LE, header.time_date_stamp), + pointer_to_symbol_table: U32::new(LE, self.symtab_offset), + number_of_symbols: U32::new(LE, self.symtab_num), + size_of_optional_header: U16::default(), + characteristics: U16::new(LE, header.characteristics), + }; + self.buffer.write(&header); + + Ok(()) + } + + /// Reserve the range for the section headers. + pub fn reserve_section_headers(&mut self, section_num: u16) { + debug_assert_eq!(self.section_num, 0); + self.section_num = section_num; + self.reserve( + section_num as usize * mem::size_of::<pe::ImageSectionHeader>(), + 1, + ); + } + + /// Write a section header. + pub fn write_section_header(&mut self, section: SectionHeader) { + let mut coff_section = pe::ImageSectionHeader { + name: [0; 8], + virtual_size: U32::default(), + virtual_address: U32::default(), + size_of_raw_data: U32::new(LE, section.size_of_raw_data), + pointer_to_raw_data: U32::new(LE, section.pointer_to_raw_data), + pointer_to_relocations: U32::new(LE, section.pointer_to_relocations), + pointer_to_linenumbers: U32::new(LE, section.pointer_to_linenumbers), + number_of_relocations: if section.number_of_relocations > 0xffff { + U16::new(LE, 0xffff) + } else { + U16::new(LE, section.number_of_relocations as u16) + }, + number_of_linenumbers: U16::default(), + characteristics: U32::new(LE, section.characteristics), + }; + match section.name { + Name::Short(name) => coff_section.name = name, + Name::Long(str_id) => { + let mut str_offset = self.strtab.get_offset(str_id); + if str_offset <= 9_999_999 { + let mut name = [0; 7]; + let mut len = 0; + if str_offset == 0 { + name[6] = b'0'; + len = 1; + } else { + while str_offset != 0 { + let rem = (str_offset % 10) as u8; + str_offset /= 10; + name[6 - len] = b'0' + rem; + len += 1; + } + } + coff_section.name = [0; 8]; + coff_section.name[0] = b'/'; + coff_section.name[1..][..len].copy_from_slice(&name[7 - len..]); + } else { + debug_assert!(str_offset as u64 <= 0xf_ffff_ffff); + coff_section.name[0] = b'/'; + coff_section.name[1] = b'/'; + for i in 0..6 { + let rem = (str_offset % 64) as u8; + str_offset /= 64; + let c = match rem { + 0..=25 => b'A' + rem, + 26..=51 => b'a' + rem - 26, + 52..=61 => b'0' + rem - 52, + 62 => b'+', + 63 => b'/', + _ => unreachable!(), + }; + coff_section.name[7 - i] = c; + } + } + } + } + self.buffer.write(&coff_section); + } + + /// Reserve the range for the section data. + /// + /// Returns the aligned offset of the start of the range. + /// Does nothing and returns 0 if the length is zero. + pub fn reserve_section(&mut self, len: usize) -> u32 { + if len == 0 { + return 0; + } + // TODO: not sure what alignment is required here, but this seems to match LLVM + self.reserve(len, 4) + } + + /// Write the alignment bytes prior to section data. + /// + /// This is unneeded if you are using `write_section` or `write_section_zeroes` + /// for the data. + pub fn write_section_align(&mut self) { + util::write_align(self.buffer, 4); + } + + /// Write the section data. + /// + /// Writes alignment bytes prior to the data. + /// Does nothing if the data is empty. + pub fn write_section(&mut self, data: &[u8]) { + if data.is_empty() { + return; + } + self.write_section_align(); + self.buffer.write_bytes(data); + } + + /// Write the section data using zero bytes. + /// + /// Writes alignment bytes prior to the data. + /// Does nothing if the length is zero. + pub fn write_section_zeroes(&mut self, len: usize) { + if len == 0 { + return; + } + self.write_section_align(); + self.buffer.resize(self.buffer.len() + len); + } + + /// Reserve a file range for the given number of relocations. + /// + /// This will automatically reserve an extra relocation if there are more than 0xffff. + /// + /// Returns the offset of the range. + /// Does nothing and returns 0 if the count is zero. + pub fn reserve_relocations(&mut self, mut count: usize) -> u32 { + if count == 0 { + return 0; + } + if count > 0xffff { + count += 1; + } + self.reserve(count * mem::size_of::<pe::ImageRelocation>(), 1) + } + + /// Write a relocation containing the count if required. + /// + /// This should be called before writing the first relocation for a section. + pub fn write_relocations_count(&mut self, count: usize) { + if count > 0xffff { + let coff_relocation = pe::ImageRelocation { + virtual_address: U32Bytes::new(LE, count as u32 + 1), + symbol_table_index: U32Bytes::new(LE, 0), + typ: U16Bytes::new(LE, 0), + }; + self.buffer.write(&coff_relocation); + } + } + + /// Write a relocation. + pub fn write_relocation(&mut self, reloc: Relocation) { + let coff_relocation = pe::ImageRelocation { + virtual_address: U32Bytes::new(LE, reloc.virtual_address), + symbol_table_index: U32Bytes::new(LE, reloc.symbol), + typ: U16Bytes::new(LE, reloc.typ), + }; + self.buffer.write(&coff_relocation); + } + + /// Reserve a symbol table entry. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn reserve_symbol_index(&mut self) -> u32 { + debug_assert_eq!(self.symtab_offset, 0); + let index = self.symtab_num; + self.symtab_num += 1; + index + } + + /// Reserve a number of symbol table entries. + pub fn reserve_symbol_indices(&mut self, count: u32) { + debug_assert_eq!(self.symtab_offset, 0); + self.symtab_num += count; + } + + /// Write a symbol table entry. + pub fn write_symbol(&mut self, symbol: Symbol) { + let mut coff_symbol = pe::ImageSymbol { + name: [0; 8], + value: U32Bytes::new(LE, symbol.value), + section_number: U16Bytes::new(LE, symbol.section_number), + typ: U16Bytes::new(LE, symbol.typ), + storage_class: symbol.storage_class, + number_of_aux_symbols: symbol.number_of_aux_symbols, + }; + match symbol.name { + Name::Short(name) => coff_symbol.name = name, + Name::Long(str_id) => { + let str_offset = self.strtab.get_offset(str_id); + coff_symbol.name[4..8].copy_from_slice(&u32::to_le_bytes(str_offset as u32)); + } + } + self.buffer.write(&coff_symbol); + } + + /// Reserve auxiliary symbols for a file name. + /// + /// Returns the number of auxiliary symbols required. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn reserve_aux_file_name(&mut self, name: &[u8]) -> u8 { + debug_assert_eq!(self.symtab_offset, 0); + let aux_count = (name.len() + pe::IMAGE_SIZEOF_SYMBOL - 1) / pe::IMAGE_SIZEOF_SYMBOL; + self.symtab_num += aux_count as u32; + aux_count as u8 + } + + /// Write auxiliary symbols for a file name. + pub fn write_aux_file_name(&mut self, name: &[u8], aux_count: u8) { + let aux_len = aux_count as usize * pe::IMAGE_SIZEOF_SYMBOL; + debug_assert!(aux_len >= name.len()); + let old_len = self.buffer.len(); + self.buffer.write_bytes(name); + self.buffer.resize(old_len + aux_len); + } + + /// Reserve an auxiliary symbol for a section. + /// + /// Returns the number of auxiliary symbols required. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn reserve_aux_section(&mut self) -> u8 { + debug_assert_eq!(self.symtab_offset, 0); + self.symtab_num += 1; + 1 + } + + /// Write an auxiliary symbol for a section. + pub fn write_aux_section(&mut self, section: AuxSymbolSection) { + let aux = pe::ImageAuxSymbolSection { + length: U32Bytes::new(LE, section.length), + number_of_relocations: if section.number_of_relocations > 0xffff { + U16Bytes::new(LE, 0xffff) + } else { + U16Bytes::new(LE, section.number_of_relocations as u16) + }, + number_of_linenumbers: U16Bytes::new(LE, section.number_of_linenumbers), + check_sum: U32Bytes::new(LE, section.check_sum), + number: U16Bytes::new(LE, section.number as u16), + selection: section.selection, + reserved: 0, + high_number: U16Bytes::new(LE, (section.number >> 16) as u16), + }; + self.buffer.write(&aux); + } + + /// Reserve an auxiliary symbol for a weak external. + /// + /// Returns the number of auxiliary symbols required. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn reserve_aux_weak_external(&mut self) -> u8 { + debug_assert_eq!(self.symtab_offset, 0); + self.symtab_num += 1; + 1 + } + + /// Write an auxiliary symbol for a weak external. + pub fn write_aux_weak_external(&mut self, weak: AuxSymbolWeak) { + let aux = pe::ImageAuxSymbolWeak { + weak_default_sym_index: U32Bytes::new(LE, weak.weak_default_sym_index), + weak_search_type: U32Bytes::new(LE, weak.weak_search_type), + }; + self.buffer.write(&aux); + // write padding for the unused field + const PAD_LEN: usize = pe::IMAGE_SIZEOF_SYMBOL - mem::size_of::<pe::ImageAuxSymbolWeak>(); + self.buffer.write_bytes(&[0u8; PAD_LEN]); + } + + /// Return the number of reserved symbol table entries. + pub fn symbol_count(&self) -> u32 { + self.symtab_num + } + + /// Add a string to the string table. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn add_string(&mut self, name: &'a [u8]) -> StringId { + debug_assert_eq!(self.strtab_offset, 0); + self.strtab.add(name) + } + + /// Add a section or symbol name to the string table if required. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn add_name(&mut self, name: &'a [u8]) -> Name { + if name.len() > 8 { + Name::Long(self.add_string(name)) + } else { + let mut short_name = [0; 8]; + short_name[..name.len()].copy_from_slice(name); + Name::Short(short_name) + } + } + + /// Reserve the range for the symbol table and string table. + /// + /// This must be called after functions that reserve symbol + /// indices or add strings. + pub fn reserve_symtab_strtab(&mut self) { + debug_assert_eq!(self.symtab_offset, 0); + self.symtab_offset = self.reserve(self.symtab_num as usize * pe::IMAGE_SIZEOF_SYMBOL, 1); + + debug_assert_eq!(self.strtab_offset, 0); + // First 4 bytes of strtab are the length. + self.strtab.write(4, &mut self.strtab_data); + self.strtab_len = self.strtab_data.len() + 4; + self.strtab_offset = self.reserve(self.strtab_len, 1); + } + + /// Write the string table. + pub fn write_strtab(&mut self) { + debug_assert_eq!(self.strtab_offset, self.buffer.len() as u32); + self.buffer + .write_bytes(&u32::to_le_bytes(self.strtab_len as u32)); + self.buffer.write_bytes(&self.strtab_data); + } +} + +/// Shortened and native endian version of [`pe::ImageFileHeader`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct FileHeader { + pub machine: u16, + pub time_date_stamp: u32, + pub characteristics: u16, +} + +/// A section or symbol name. +#[derive(Debug, Clone, Copy)] +pub enum Name { + /// An inline name. + Short([u8; 8]), + /// An id of a string table entry. + Long(StringId), +} + +impl Default for Name { + fn default() -> Name { + Name::Short([0; 8]) + } +} + +// From isn't useful. +#[allow(clippy::from_over_into)] +impl<'a> Into<Name> for &'a [u8; 8] { + fn into(self) -> Name { + Name::Short(*self) + } +} + +/// Native endian version of [`pe::ImageSectionHeader`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct SectionHeader { + pub name: Name, + pub size_of_raw_data: u32, + pub pointer_to_raw_data: u32, + pub pointer_to_relocations: u32, + pub pointer_to_linenumbers: u32, + /// This will automatically be clamped if there are more than 0xffff. + pub number_of_relocations: u32, + pub number_of_linenumbers: u16, + pub characteristics: u32, +} + +/// Native endian version of [`pe::ImageSymbol`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct Symbol { + pub name: Name, + pub value: u32, + pub section_number: u16, + pub typ: u16, + pub storage_class: u8, + pub number_of_aux_symbols: u8, +} + +/// Native endian version of [`pe::ImageAuxSymbolSection`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct AuxSymbolSection { + pub length: u32, + /// This will automatically be clamped if there are more than 0xffff. + pub number_of_relocations: u32, + pub number_of_linenumbers: u16, + pub check_sum: u32, + pub number: u32, + pub selection: u8, +} + +/// Native endian version of [`pe::ImageAuxSymbolWeak`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct AuxSymbolWeak { + pub weak_default_sym_index: u32, + pub weak_search_type: u32, +} + +/// Native endian version of [`pe::ImageRelocation`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct Relocation { + pub virtual_address: u32, + pub symbol: u32, + pub typ: u16, +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/elf/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/write/elf/mod.rs new file mode 100644 index 0000000..3a4f371 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/elf/mod.rs
@@ -0,0 +1,9 @@ +//! Support for writing ELF files. +//! +//! Provides [`Writer`] for low level writing of ELF files. +//! This is also used to provide ELF support for [`write::Object`](crate::write::Object). + +mod object; + +mod writer; +pub use writer::*;
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/elf/object.rs b/chromium_crates_io/vendor/object-v0_37/src/write/elf/object.rs new file mode 100644 index 0000000..fbe9986 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/elf/object.rs
@@ -0,0 +1,869 @@ +use alloc::vec::Vec; + +use crate::write::elf::writer::*; +use crate::write::string::StringId; +use crate::write::*; +use crate::{elf, pod}; + +#[derive(Clone, Copy)] +struct ComdatOffsets { + offset: usize, + str_id: StringId, +} + +#[derive(Clone, Copy)] +struct SectionOffsets { + index: SectionIndex, + offset: usize, + str_id: StringId, + reloc_offset: usize, + reloc_str_id: Option<StringId>, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: SymbolIndex, + str_id: Option<StringId>, +} + +// Public methods. +impl<'a> Object<'a> { + /// Add a property with a u32 value to the ELF ".note.gnu.property" section. + /// + /// Requires `feature = "elf"`. + pub fn add_elf_gnu_property_u32(&mut self, property: u32, value: u32) { + if self.format != BinaryFormat::Elf { + return; + } + + let align = if self.elf_is_64() { 8 } else { 4 }; + let mut data = Vec::with_capacity(32); + let n_name = b"GNU\0"; + data.extend_from_slice(pod::bytes_of(&elf::NoteHeader32 { + n_namesz: U32::new(self.endian, n_name.len() as u32), + n_descsz: U32::new(self.endian, util::align(3 * 4, align) as u32), + n_type: U32::new(self.endian, elf::NT_GNU_PROPERTY_TYPE_0), + })); + data.extend_from_slice(n_name); + // This happens to already be aligned correctly. + debug_assert_eq!(util::align(data.len(), align), data.len()); + data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, property))); + // Value size + data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, 4))); + data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, value))); + util::write_align(&mut data, align); + + let section = self.section_id(StandardSection::GnuProperty); + self.append_section_data(section, &data, align as u64); + } +} + +// Private methods. +impl<'a> Object<'a> { + pub(crate) fn elf_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { + match section { + StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None), + StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None), + StandardSection::ReadOnlyData | StandardSection::ReadOnlyString => ( + &[], + &b".rodata"[..], + SectionKind::ReadOnlyData, + SectionFlags::None, + ), + StandardSection::ReadOnlyDataWithRel => ( + &[], + b".data.rel.ro", + SectionKind::ReadOnlyDataWithRel, + SectionFlags::None, + ), + StandardSection::UninitializedData => ( + &[], + &b".bss"[..], + SectionKind::UninitializedData, + SectionFlags::None, + ), + StandardSection::Tls => (&[], &b".tdata"[..], SectionKind::Tls, SectionFlags::None), + StandardSection::UninitializedTls => ( + &[], + &b".tbss"[..], + SectionKind::UninitializedTls, + SectionFlags::None, + ), + StandardSection::TlsVariables => { + // Unsupported section. + (&[], &[], SectionKind::TlsVariables, SectionFlags::None) + } + StandardSection::Common => { + // Unsupported section. + (&[], &[], SectionKind::Common, SectionFlags::None) + } + StandardSection::GnuProperty => ( + &[], + &b".note.gnu.property"[..], + SectionKind::Note, + SectionFlags::Elf { + sh_flags: u64::from(elf::SHF_ALLOC), + }, + ), + } + } + + pub(crate) fn elf_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + let mut name = section.to_vec(); + if !value.is_empty() { + name.push(b'.'); + name.extend_from_slice(value); + } + name + } + + pub(crate) fn elf_section_flags(&self, section: &Section<'_>) -> SectionFlags { + let sh_flags = match section.kind { + SectionKind::Text => elf::SHF_ALLOC | elf::SHF_EXECINSTR, + SectionKind::Data | SectionKind::ReadOnlyDataWithRel => elf::SHF_ALLOC | elf::SHF_WRITE, + SectionKind::Tls => elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_TLS, + SectionKind::UninitializedData => elf::SHF_ALLOC | elf::SHF_WRITE, + SectionKind::UninitializedTls => elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_TLS, + SectionKind::ReadOnlyData => elf::SHF_ALLOC, + SectionKind::ReadOnlyString => elf::SHF_ALLOC | elf::SHF_STRINGS | elf::SHF_MERGE, + SectionKind::OtherString | SectionKind::DebugString => { + elf::SHF_STRINGS | elf::SHF_MERGE + } + SectionKind::Other + | SectionKind::Debug + | SectionKind::Metadata + | SectionKind::Linker + | SectionKind::Note + | SectionKind::Elf(_) => 0, + SectionKind::Unknown | SectionKind::Common | SectionKind::TlsVariables => { + return SectionFlags::None; + } + } + .into(); + SectionFlags::Elf { sh_flags } + } + + pub(crate) fn elf_symbol_flags(&self, symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> { + let st_type = match symbol.kind { + SymbolKind::Text => { + if symbol.is_undefined() { + elf::STT_NOTYPE + } else { + elf::STT_FUNC + } + } + SymbolKind::Data => { + if symbol.is_undefined() { + elf::STT_NOTYPE + } else if symbol.is_common() { + elf::STT_COMMON + } else { + elf::STT_OBJECT + } + } + SymbolKind::Section => elf::STT_SECTION, + SymbolKind::File => elf::STT_FILE, + SymbolKind::Tls => elf::STT_TLS, + SymbolKind::Label => elf::STT_NOTYPE, + SymbolKind::Unknown => { + if symbol.is_undefined() { + elf::STT_NOTYPE + } else { + return SymbolFlags::None; + } + } + }; + let st_bind = if symbol.weak { + elf::STB_WEAK + } else if symbol.is_undefined() { + elf::STB_GLOBAL + } else if symbol.is_local() { + elf::STB_LOCAL + } else { + elf::STB_GLOBAL + }; + let st_info = (st_bind << 4) + st_type; + let st_other = if symbol.scope == SymbolScope::Linkage { + elf::STV_HIDDEN + } else { + elf::STV_DEFAULT + }; + SymbolFlags::Elf { st_info, st_other } + } + + fn elf_has_relocation_addend(&self) -> Result<bool> { + Ok(match self.architecture { + Architecture::Aarch64 => true, + Architecture::Aarch64_Ilp32 => true, + Architecture::Alpha => true, + Architecture::Arm => false, + Architecture::Avr => true, + Architecture::Bpf => false, + Architecture::Csky => true, + Architecture::E2K32 => true, + Architecture::E2K64 => true, + Architecture::I386 => false, + Architecture::X86_64 => true, + Architecture::X86_64_X32 => true, + Architecture::Hppa => false, + Architecture::Hexagon => true, + Architecture::LoongArch32 => true, + Architecture::LoongArch64 => true, + Architecture::M68k => true, + Architecture::Mips => false, + Architecture::Mips64 => true, + Architecture::Mips64_N32 => true, + Architecture::Msp430 => true, + Architecture::PowerPc => true, + Architecture::PowerPc64 => true, + Architecture::Riscv64 => true, + Architecture::Riscv32 => true, + Architecture::S390x => true, + Architecture::Sbf => false, + Architecture::Sharc => true, + Architecture::Sparc => true, + Architecture::Sparc32Plus => true, + Architecture::Sparc64 => true, + Architecture::SuperH => false, + Architecture::Xtensa => true, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }) + } + + pub(crate) fn elf_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { + use RelocationEncoding as E; + use RelocationKind as K; + + let (kind, encoding, size) = if let RelocationFlags::Generic { + kind, + encoding, + size, + } = reloc.flags + { + (kind, encoding, size) + } else { + return Ok(()); + }; + + let unsupported_reloc = || Err(Error(format!("unimplemented ELF relocation {:?}", reloc))); + let r_type = match self.architecture { + Architecture::Aarch64 => match (kind, encoding, size) { + (K::Absolute, E::Generic, 64) => elf::R_AARCH64_ABS64, + (K::Absolute, E::Generic, 32) => elf::R_AARCH64_ABS32, + (K::Absolute, E::Generic, 16) => elf::R_AARCH64_ABS16, + (K::Relative, E::Generic, 64) => elf::R_AARCH64_PREL64, + (K::Relative, E::Generic, 32) => elf::R_AARCH64_PREL32, + (K::Relative, E::Generic, 16) => elf::R_AARCH64_PREL16, + (K::Relative, E::AArch64Call, 26) => elf::R_AARCH64_CALL26, + (K::PltRelative, E::AArch64Call, 26) => elf::R_AARCH64_CALL26, + _ => return unsupported_reloc(), + }, + Architecture::Aarch64_Ilp32 => match (kind, encoding, size) { + (K::Absolute, E::Generic, 32) => elf::R_AARCH64_P32_ABS32, + _ => return unsupported_reloc(), + }, + Architecture::Alpha => match (kind, encoding, size) { + // Absolute + (K::Absolute, _, 32) => elf::R_ALPHA_REFLONG, + (K::Absolute, _, 64) => elf::R_ALPHA_REFQUAD, + // Relative to the PC + (K::Relative, _, 16) => elf::R_ALPHA_SREL16, + (K::Relative, _, 32) => elf::R_ALPHA_SREL32, + (K::Relative, _, 64) => elf::R_ALPHA_SREL64, + _ => return unsupported_reloc(), + }, + Architecture::Arm => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_ARM_ABS32, + _ => return unsupported_reloc(), + }, + Architecture::Avr => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_AVR_32, + (K::Absolute, _, 16) => elf::R_AVR_16, + _ => return unsupported_reloc(), + }, + Architecture::Bpf => match (kind, encoding, size) { + (K::Absolute, _, 64) => elf::R_BPF_64_64, + (K::Absolute, _, 32) => elf::R_BPF_64_32, + _ => return unsupported_reloc(), + }, + Architecture::Csky => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_CKCORE_ADDR32, + (K::Relative, E::Generic, 32) => elf::R_CKCORE_PCREL32, + _ => return unsupported_reloc(), + }, + Architecture::I386 => match (kind, size) { + (K::Absolute, 32) => elf::R_386_32, + (K::Relative, 32) => elf::R_386_PC32, + (K::Got, 32) => elf::R_386_GOT32, + (K::PltRelative, 32) => elf::R_386_PLT32, + (K::GotBaseOffset, 32) => elf::R_386_GOTOFF, + (K::GotBaseRelative, 32) => elf::R_386_GOTPC, + (K::Absolute, 16) => elf::R_386_16, + (K::Relative, 16) => elf::R_386_PC16, + (K::Absolute, 8) => elf::R_386_8, + (K::Relative, 8) => elf::R_386_PC8, + _ => return unsupported_reloc(), + }, + Architecture::E2K32 | Architecture::E2K64 => match (kind, encoding, size) { + (K::Absolute, E::Generic, 32) => elf::R_E2K_32_ABS, + (K::Absolute, E::E2KLit, 64) => elf::R_E2K_64_ABS_LIT, + (K::Absolute, E::Generic, 64) => elf::R_E2K_64_ABS, + (K::Relative, E::E2KDisp, 28) => elf::R_E2K_DISP, + (K::Got, _, 32) => elf::R_E2K_GOT, + _ => return unsupported_reloc(), + }, + Architecture::X86_64 | Architecture::X86_64_X32 => match (kind, encoding, size) { + (K::Absolute, E::Generic, 64) => elf::R_X86_64_64, + (K::Relative, E::X86Branch, 32) => elf::R_X86_64_PLT32, + (K::Relative, _, 32) => elf::R_X86_64_PC32, + (K::Got, _, 32) => elf::R_X86_64_GOT32, + (K::PltRelative, _, 32) => elf::R_X86_64_PLT32, + (K::GotRelative, _, 32) => elf::R_X86_64_GOTPCREL, + (K::Absolute, E::Generic, 32) => elf::R_X86_64_32, + (K::Absolute, E::X86Signed, 32) => elf::R_X86_64_32S, + (K::Absolute, _, 16) => elf::R_X86_64_16, + (K::Relative, _, 16) => elf::R_X86_64_PC16, + (K::Absolute, _, 8) => elf::R_X86_64_8, + (K::Relative, _, 8) => elf::R_X86_64_PC8, + _ => return unsupported_reloc(), + }, + Architecture::Hppa => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_PARISC_DIR32, + (K::Relative, _, 32) => elf::R_PARISC_PCREL32, + _ => return unsupported_reloc(), + }, + Architecture::Hexagon => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_HEX_32, + _ => return unsupported_reloc(), + }, + Architecture::LoongArch32 | Architecture::LoongArch64 => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_LARCH_32, + (K::Absolute, _, 64) => elf::R_LARCH_64, + (K::Relative, _, 32) => elf::R_LARCH_32_PCREL, + (K::Relative, _, 64) => elf::R_LARCH_64_PCREL, + (K::Relative, E::LoongArchBranch, 16) => elf::R_LARCH_B16, + (K::PltRelative, E::LoongArchBranch, 16) => elf::R_LARCH_B16, + (K::Relative, E::LoongArchBranch, 21) => elf::R_LARCH_B21, + (K::PltRelative, E::LoongArchBranch, 21) => elf::R_LARCH_B21, + (K::Relative, E::LoongArchBranch, 26) => elf::R_LARCH_B26, + (K::PltRelative, E::LoongArchBranch, 26) => elf::R_LARCH_B26, + _ => return unsupported_reloc(), + }, + Architecture::M68k => match (kind, encoding, size) { + (K::Absolute, _, 8) => elf::R_68K_8, + (K::Absolute, _, 16) => elf::R_68K_16, + (K::Absolute, _, 32) => elf::R_68K_32, + (K::Relative, _, 8) => elf::R_68K_PC8, + (K::Relative, _, 16) => elf::R_68K_PC16, + (K::Relative, _, 32) => elf::R_68K_PC32, + (K::GotRelative, _, 8) => elf::R_68K_GOT8, + (K::GotRelative, _, 16) => elf::R_68K_GOT16, + (K::GotRelative, _, 32) => elf::R_68K_GOT32, + (K::Got, _, 8) => elf::R_68K_GOT8O, + (K::Got, _, 16) => elf::R_68K_GOT16O, + (K::Got, _, 32) => elf::R_68K_GOT32O, + (K::PltRelative, _, 8) => elf::R_68K_PLT8, + (K::PltRelative, _, 16) => elf::R_68K_PLT16, + (K::PltRelative, _, 32) => elf::R_68K_PLT32, + _ => return unsupported_reloc(), + }, + Architecture::Mips | Architecture::Mips64 | Architecture::Mips64_N32 => { + match (kind, encoding, size) { + (K::Absolute, _, 16) => elf::R_MIPS_16, + (K::Absolute, _, 32) => elf::R_MIPS_32, + (K::Absolute, _, 64) => elf::R_MIPS_64, + _ => return unsupported_reloc(), + } + } + Architecture::Msp430 => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_MSP430_32, + (K::Absolute, _, 16) => elf::R_MSP430_16_BYTE, + _ => return unsupported_reloc(), + }, + Architecture::PowerPc => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_PPC_ADDR32, + _ => return unsupported_reloc(), + }, + Architecture::PowerPc64 => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_PPC64_ADDR32, + (K::Absolute, _, 64) => elf::R_PPC64_ADDR64, + _ => return unsupported_reloc(), + }, + Architecture::Riscv32 | Architecture::Riscv64 => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_RISCV_32, + (K::Absolute, _, 64) => elf::R_RISCV_64, + (K::Relative, E::Generic, 32) => elf::R_RISCV_32_PCREL, + _ => return unsupported_reloc(), + }, + Architecture::S390x => match (kind, encoding, size) { + (K::Absolute, E::Generic, 8) => elf::R_390_8, + (K::Absolute, E::Generic, 16) => elf::R_390_16, + (K::Absolute, E::Generic, 32) => elf::R_390_32, + (K::Absolute, E::Generic, 64) => elf::R_390_64, + (K::Relative, E::Generic, 16) => elf::R_390_PC16, + (K::Relative, E::Generic, 32) => elf::R_390_PC32, + (K::Relative, E::Generic, 64) => elf::R_390_PC64, + (K::Relative, E::S390xDbl, 16) => elf::R_390_PC16DBL, + (K::Relative, E::S390xDbl, 32) => elf::R_390_PC32DBL, + (K::PltRelative, E::S390xDbl, 16) => elf::R_390_PLT16DBL, + (K::PltRelative, E::S390xDbl, 32) => elf::R_390_PLT32DBL, + (K::Got, E::Generic, 16) => elf::R_390_GOT16, + (K::Got, E::Generic, 32) => elf::R_390_GOT32, + (K::Got, E::Generic, 64) => elf::R_390_GOT64, + (K::GotRelative, E::S390xDbl, 32) => elf::R_390_GOTENT, + (K::GotBaseOffset, E::Generic, 16) => elf::R_390_GOTOFF16, + (K::GotBaseOffset, E::Generic, 32) => elf::R_390_GOTOFF32, + (K::GotBaseOffset, E::Generic, 64) => elf::R_390_GOTOFF64, + (K::GotBaseRelative, E::Generic, 64) => elf::R_390_GOTPC, + (K::GotBaseRelative, E::S390xDbl, 32) => elf::R_390_GOTPCDBL, + _ => return unsupported_reloc(), + }, + Architecture::Sbf => match (kind, encoding, size) { + (K::Absolute, _, 64) => elf::R_SBF_64_64, + (K::Absolute, _, 32) => elf::R_SBF_64_32, + _ => return unsupported_reloc(), + }, + Architecture::Sharc => match (kind, encoding, size) { + (K::Absolute, E::SharcTypeA, 32) => elf::R_SHARC_ADDR32_V3, + (K::Absolute, E::Generic, 32) => elf::R_SHARC_ADDR_VAR_V3, + (K::Relative, E::SharcTypeA, 24) => elf::R_SHARC_PCRLONG_V3, + (K::Relative, E::SharcTypeA, 6) => elf::R_SHARC_PCRSHORT_V3, + (K::Relative, E::SharcTypeB, 6) => elf::R_SHARC_PCRSHORT_V3, + (K::Absolute, E::Generic, 16) => elf::R_SHARC_ADDR_VAR16_V3, + (K::Absolute, E::SharcTypeA, 16) => elf::R_SHARC_DATA16_V3, + (K::Absolute, E::SharcTypeB, 16) => elf::R_SHARC_DATA16_VISA_V3, + (K::Absolute, E::SharcTypeA, 24) => elf::R_SHARC_ADDR24_V3, + (K::Absolute, E::SharcTypeA, 6) => elf::R_SHARC_DATA6_V3, + (K::Absolute, E::SharcTypeB, 6) => elf::R_SHARC_DATA6_VISA_V3, + (K::Absolute, E::SharcTypeB, 7) => elf::R_SHARC_DATA7_VISA_V3, + _ => return unsupported_reloc(), + }, + Architecture::Sparc | Architecture::Sparc32Plus => match (kind, encoding, size) { + // TODO: use R_SPARC_32 if aligned. + (K::Absolute, _, 32) => elf::R_SPARC_UA32, + _ => return unsupported_reloc(), + }, + Architecture::Sparc64 => match (kind, encoding, size) { + // TODO: use R_SPARC_32/R_SPARC_64 if aligned. + (K::Absolute, _, 32) => elf::R_SPARC_UA32, + (K::Absolute, _, 64) => elf::R_SPARC_UA64, + _ => return unsupported_reloc(), + }, + Architecture::SuperH => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_SH_DIR32, + (K::Relative, _, 32) => elf::R_SH_REL32, + _ => return unsupported_reloc(), + }, + Architecture::Xtensa => match (kind, encoding, size) { + (K::Absolute, _, 32) => elf::R_XTENSA_32, + (K::Relative, E::Generic, 32) => elf::R_XTENSA_32_PCREL, + _ => return unsupported_reloc(), + }, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }; + reloc.flags = RelocationFlags::Elf { r_type }; + Ok(()) + } + + pub(crate) fn elf_adjust_addend(&mut self, _relocation: &mut Relocation) -> Result<bool> { + // Determine whether the addend is stored in the relocation or the data. + let implicit = !self.elf_has_relocation_addend()?; + Ok(implicit) + } + + pub(crate) fn elf_relocation_size(&self, reloc: &Relocation) -> Result<u8> { + let r_type = if let RelocationFlags::Elf { r_type } = reloc.flags { + r_type + } else { + return Err(Error("invalid relocation flags".into())); + }; + // This only needs to support architectures that use implicit addends. + let size = match self.architecture { + Architecture::Arm => match r_type { + elf::R_ARM_ABS16 => Some(16), + elf::R_ARM_ABS32 | elf::R_ARM_REL32 => Some(32), + _ => None, + }, + Architecture::Bpf => match r_type { + elf::R_BPF_64_32 => Some(32), + elf::R_BPF_64_64 => Some(64), + _ => None, + }, + Architecture::I386 => match r_type { + elf::R_386_8 | elf::R_386_PC8 => Some(8), + elf::R_386_16 | elf::R_386_PC16 => Some(16), + elf::R_386_32 + | elf::R_386_PC32 + | elf::R_386_GOT32 + | elf::R_386_PLT32 + | elf::R_386_GOTOFF + | elf::R_386_GOTPC => Some(32), + _ => None, + }, + Architecture::Mips => match r_type { + elf::R_MIPS_16 => Some(16), + elf::R_MIPS_32 => Some(32), + elf::R_MIPS_64 => Some(64), + _ => None, + }, + Architecture::Sbf => match r_type { + elf::R_SBF_64_32 => Some(32), + elf::R_SBF_64_64 => Some(64), + _ => None, + }, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }; + size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc))) + } + + pub(crate) fn elf_is_64(&self) -> bool { + match self.architecture.address_size().unwrap() { + AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false, + AddressSize::U64 => true, + } + } + + pub(crate) fn elf_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { + // Create reloc section header names so we can reference them. + let is_rela = self.elf_has_relocation_addend()?; + let reloc_names: Vec<_> = self + .sections + .iter() + .map(|section| { + let mut reloc_name = Vec::with_capacity( + if is_rela { ".rela".len() } else { ".rel".len() } + section.name.len(), + ); + if !section.relocations.is_empty() { + reloc_name.extend_from_slice(if is_rela { + &b".rela"[..] + } else { + &b".rel"[..] + }); + reloc_name.extend_from_slice(§ion.name); + } + reloc_name + }) + .collect(); + + // Start calculating offsets of everything. + let mut writer = Writer::new(self.endian, self.elf_is_64(), buffer); + writer.reserve_file_header(); + + // Calculate size of section data. + let mut comdat_offsets = Vec::with_capacity(self.comdats.len()); + for comdat in &self.comdats { + if comdat.kind != ComdatKind::Any { + return Err(Error(format!( + "unsupported COMDAT symbol `{}` kind {:?}", + self.symbols[comdat.symbol.0].name().unwrap_or(""), + comdat.kind + ))); + } + + writer.reserve_section_index(); + let offset = writer.reserve_comdat(comdat.sections.len()); + let str_id = writer.add_section_name(b".group"); + comdat_offsets.push(ComdatOffsets { offset, str_id }); + } + let mut section_offsets = Vec::with_capacity(self.sections.len()); + for (section, reloc_name) in self.sections.iter().zip(reloc_names.iter()) { + let index = writer.reserve_section_index(); + let offset = writer.reserve(section.data.len(), section.align as usize); + let str_id = writer.add_section_name(§ion.name); + let mut reloc_str_id = None; + if !section.relocations.is_empty() { + writer.reserve_section_index(); + reloc_str_id = Some(writer.add_section_name(reloc_name)); + } + section_offsets.push(SectionOffsets { + index, + offset, + str_id, + // Relocation data is reserved later. + reloc_offset: 0, + reloc_str_id, + }); + } + + // Calculate index of symbols and add symbol strings to strtab. + let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; + writer.reserve_null_symbol_index(); + // Local symbols must come before global. + for (index, symbol) in self.symbols.iter().enumerate() { + if symbol.is_local() { + let section_index = symbol.section.id().map(|s| section_offsets[s.0].index); + symbol_offsets[index].index = writer.reserve_symbol_index(section_index); + } + } + let symtab_num_local = writer.symbol_count(); + for (index, symbol) in self.symbols.iter().enumerate() { + if !symbol.is_local() { + let section_index = symbol.section.id().map(|s| section_offsets[s.0].index); + symbol_offsets[index].index = writer.reserve_symbol_index(section_index); + } + } + for (index, symbol) in self.symbols.iter().enumerate() { + if symbol.kind != SymbolKind::Section && !symbol.name.is_empty() { + symbol_offsets[index].str_id = Some(writer.add_string(&symbol.name)); + } + } + + // Calculate size of symbols. + writer.reserve_symtab_section_index(); + writer.reserve_symtab(); + if writer.symtab_shndx_needed() { + writer.reserve_symtab_shndx_section_index(); + } + writer.reserve_symtab_shndx(); + writer.reserve_strtab_section_index(); + writer.reserve_strtab(); + + // Calculate size of relocations. + for (index, section) in self.sections.iter().enumerate() { + let count = section.relocations.len(); + if count != 0 { + section_offsets[index].reloc_offset = writer.reserve_relocations(count, is_rela); + } + } + + // Calculate size of section headers. + writer.reserve_shstrtab_section_index(); + writer.reserve_shstrtab(); + writer.reserve_section_headers(); + + // Start writing. + let e_type = elf::ET_REL; + let e_machine = match (self.architecture, self.sub_architecture) { + (Architecture::Aarch64, None) => elf::EM_AARCH64, + (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64, + (Architecture::Alpha, None) => elf::EM_ALPHA, + (Architecture::Arm, None) => elf::EM_ARM, + (Architecture::Avr, None) => elf::EM_AVR, + (Architecture::Bpf, None) => elf::EM_BPF, + (Architecture::Csky, None) => elf::EM_CSKY, + (Architecture::E2K32, None) => elf::EM_MCST_ELBRUS, + (Architecture::E2K64, None) => elf::EM_MCST_ELBRUS, + (Architecture::I386, None) => elf::EM_386, + (Architecture::X86_64, None) => elf::EM_X86_64, + (Architecture::X86_64_X32, None) => elf::EM_X86_64, + (Architecture::Hppa, None) => elf::EM_PARISC, + (Architecture::Hexagon, None) => elf::EM_HEXAGON, + (Architecture::LoongArch32, None) => elf::EM_LOONGARCH, + (Architecture::LoongArch64, None) => elf::EM_LOONGARCH, + (Architecture::M68k, None) => elf::EM_68K, + (Architecture::Mips, None) => elf::EM_MIPS, + (Architecture::Mips64, None) => elf::EM_MIPS, + (Architecture::Mips64_N32, None) => elf::EM_MIPS, + (Architecture::Msp430, None) => elf::EM_MSP430, + (Architecture::PowerPc, None) => elf::EM_PPC, + (Architecture::PowerPc64, None) => elf::EM_PPC64, + (Architecture::Riscv32, None) => elf::EM_RISCV, + (Architecture::Riscv64, None) => elf::EM_RISCV, + (Architecture::S390x, None) => elf::EM_S390, + (Architecture::Sbf, None) => elf::EM_SBF, + (Architecture::Sharc, None) => elf::EM_SHARC, + (Architecture::Sparc, None) => elf::EM_SPARC, + (Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS, + (Architecture::Sparc64, None) => elf::EM_SPARCV9, + (Architecture::SuperH, None) => elf::EM_SH, + (Architecture::Xtensa, None) => elf::EM_XTENSA, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?} with sub-architecture {:?}", + self.architecture, self.sub_architecture + ))); + } + }; + let (os_abi, abi_version, mut e_flags) = if let FileFlags::Elf { + os_abi, + abi_version, + e_flags, + } = self.flags + { + (os_abi, abi_version, e_flags) + } else { + (elf::ELFOSABI_NONE, 0, 0) + }; + + if self.architecture == Architecture::Mips64_N32 { + e_flags |= elf::EF_MIPS_ABI2; + } + + writer.write_file_header(&FileHeader { + os_abi, + abi_version, + e_type, + e_machine, + e_entry: 0, + e_flags, + })?; + + // Write section data. + for comdat in &self.comdats { + writer.write_comdat_header(); + for section in &comdat.sections { + writer.write_comdat_entry(section_offsets[section.0].index); + } + } + for (index, section) in self.sections.iter().enumerate() { + writer.write_align(section.align as usize); + debug_assert_eq!(section_offsets[index].offset, writer.len()); + writer.write(§ion.data); + } + + // Write symbols. + writer.write_null_symbol(); + let mut write_symbol = |index: usize, symbol: &Symbol| -> Result<()> { + let SymbolFlags::Elf { st_info, st_other } = self.symbol_flags(symbol) else { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))); + }; + let (st_shndx, section) = match symbol.section { + SymbolSection::None => { + debug_assert_eq!(symbol.kind, SymbolKind::File); + (elf::SHN_ABS, None) + } + SymbolSection::Undefined => (elf::SHN_UNDEF, None), + SymbolSection::Absolute => (elf::SHN_ABS, None), + SymbolSection::Common => (elf::SHN_COMMON, None), + SymbolSection::Section(id) => (0, Some(section_offsets[id.0].index)), + }; + writer.write_symbol(&Sym { + name: symbol_offsets[index].str_id, + section, + st_info, + st_other, + st_shndx, + st_value: symbol.value, + st_size: symbol.size, + }); + Ok(()) + }; + for (index, symbol) in self.symbols.iter().enumerate() { + if symbol.is_local() { + write_symbol(index, symbol)?; + } + } + for (index, symbol) in self.symbols.iter().enumerate() { + if !symbol.is_local() { + write_symbol(index, symbol)?; + } + } + writer.write_symtab_shndx(); + writer.write_strtab(); + + // Write relocations. + for (index, section) in self.sections.iter().enumerate() { + if !section.relocations.is_empty() { + writer.write_align_relocation(); + debug_assert_eq!(section_offsets[index].reloc_offset, writer.len()); + for reloc in §ion.relocations { + let r_type = if let RelocationFlags::Elf { r_type } = reloc.flags { + r_type + } else { + return Err(Error("invalid relocation flags".into())); + }; + let r_sym = symbol_offsets[reloc.symbol.0].index.0; + writer.write_relocation( + is_rela, + &Rel { + r_offset: reloc.offset, + r_sym, + r_type, + r_addend: reloc.addend, + }, + ); + } + } + } + + writer.write_shstrtab(); + + // Write section headers. + writer.write_null_section_header(); + + let symtab_index = writer.symtab_index(); + for (comdat, comdat_offset) in self.comdats.iter().zip(comdat_offsets.iter()) { + writer.write_comdat_section_header( + comdat_offset.str_id, + symtab_index, + symbol_offsets[comdat.symbol.0].index, + comdat_offset.offset, + comdat.sections.len(), + ); + } + for (index, section) in self.sections.iter().enumerate() { + let sh_type = match section.kind { + SectionKind::UninitializedData | SectionKind::UninitializedTls => elf::SHT_NOBITS, + SectionKind::Note => elf::SHT_NOTE, + SectionKind::Elf(sh_type) => sh_type, + _ => elf::SHT_PROGBITS, + }; + let SectionFlags::Elf { sh_flags } = self.section_flags(section) else { + return Err(Error(format!( + "unimplemented section `{}` kind {:?}", + section.name().unwrap_or(""), + section.kind + ))); + }; + // TODO: not sure if this is correct, maybe user should determine this + let sh_entsize = match section.kind { + SectionKind::ReadOnlyString | SectionKind::OtherString => 1, + _ => 0, + }; + writer.write_section_header(&SectionHeader { + name: Some(section_offsets[index].str_id), + sh_type, + sh_flags, + sh_addr: 0, + sh_offset: section_offsets[index].offset as u64, + sh_size: section.size, + sh_link: 0, + sh_info: 0, + sh_addralign: section.align, + sh_entsize, + }); + + if !section.relocations.is_empty() { + writer.write_relocation_section_header( + section_offsets[index].reloc_str_id.unwrap(), + section_offsets[index].index, + symtab_index, + section_offsets[index].reloc_offset, + section.relocations.len(), + is_rela, + ); + } + } + + writer.write_symtab_section_header(symtab_num_local); + writer.write_symtab_shndx_section_header(); + writer.write_strtab_section_header(); + writer.write_shstrtab_section_header(); + + debug_assert_eq!(writer.reserved_len(), writer.len()); + + Ok(()) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/elf/writer.rs b/chromium_crates_io/vendor/object-v0_37/src/write/elf/writer.rs new file mode 100644 index 0000000..ca3ab59 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/elf/writer.rs
@@ -0,0 +1,2394 @@ +//! Helper for writing ELF files. +use alloc::string::String; +use alloc::vec::Vec; +use core::mem; + +use crate::elf; +use crate::endian::*; +use crate::pod; +use crate::write::string::{StringId, StringTable}; +use crate::write::util; +use crate::write::{Error, Result, WritableBuffer}; + +const ALIGN_SYMTAB_SHNDX: usize = 4; +const ALIGN_HASH: usize = 4; +const ALIGN_GNU_VERSYM: usize = 2; +const ALIGN_GNU_VERDEF: usize = 4; +const ALIGN_GNU_VERNEED: usize = 4; + +/// The index of an ELF section. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SectionIndex(pub u32); + +/// The index of an ELF symbol. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SymbolIndex(pub u32); + +/// A helper for writing ELF files. +/// +/// Writing uses a two phase approach. The first phase builds up all of the information +/// that may need to be known ahead of time: +/// - build string tables +/// - reserve section indices +/// - reserve symbol indices +/// - reserve file ranges for headers and sections +/// +/// Some of the information has ordering requirements. For example, strings must be added +/// to string tables before reserving the file range for the string table. Symbol indices +/// must be reserved after reserving the section indices they reference. There are debug +/// asserts to check some of these requirements. +/// +/// The second phase writes everything out in order. Thus the caller must ensure writing +/// is in the same order that file ranges were reserved. There are debug asserts to assist +/// with checking this. +#[allow(missing_debug_implementations)] +pub struct Writer<'a> { + endian: Endianness, + is_64: bool, + is_mips64el: bool, + elf_align: usize, + + buffer: &'a mut dyn WritableBuffer, + len: usize, + + segment_offset: usize, + segment_num: u32, + + section_offset: usize, + section_num: u32, + + shstrtab: StringTable<'a>, + shstrtab_str_id: Option<StringId>, + shstrtab_index: SectionIndex, + shstrtab_offset: usize, + shstrtab_data: Vec<u8>, + + need_strtab: bool, + strtab: StringTable<'a>, + strtab_str_id: Option<StringId>, + strtab_index: SectionIndex, + strtab_offset: usize, + strtab_data: Vec<u8>, + + symtab_str_id: Option<StringId>, + symtab_index: SectionIndex, + symtab_offset: usize, + symtab_num: u32, + + need_symtab_shndx: bool, + symtab_shndx_str_id: Option<StringId>, + symtab_shndx_offset: usize, + symtab_shndx_data: Vec<u8>, + + need_dynstr: bool, + dynstr: StringTable<'a>, + dynstr_str_id: Option<StringId>, + dynstr_index: SectionIndex, + dynstr_offset: usize, + dynstr_data: Vec<u8>, + + dynsym_str_id: Option<StringId>, + dynsym_index: SectionIndex, + dynsym_offset: usize, + dynsym_num: u32, + + dynamic_str_id: Option<StringId>, + dynamic_offset: usize, + dynamic_num: usize, + + hash_str_id: Option<StringId>, + hash_offset: usize, + hash_size: usize, + + gnu_hash_str_id: Option<StringId>, + gnu_hash_offset: usize, + gnu_hash_size: usize, + + gnu_versym_str_id: Option<StringId>, + gnu_versym_offset: usize, + + gnu_verdef_str_id: Option<StringId>, + gnu_verdef_offset: usize, + gnu_verdef_size: usize, + gnu_verdef_count: u16, + gnu_verdef_remaining: u16, + gnu_verdaux_remaining: u16, + + gnu_verneed_str_id: Option<StringId>, + gnu_verneed_offset: usize, + gnu_verneed_size: usize, + gnu_verneed_count: u16, + gnu_verneed_remaining: u16, + gnu_vernaux_remaining: u16, + + gnu_attributes_str_id: Option<StringId>, + gnu_attributes_offset: usize, + gnu_attributes_size: usize, +} + +impl<'a> Writer<'a> { + /// Create a new `Writer` for the given endianness and ELF class. + pub fn new(endian: Endianness, is_64: bool, buffer: &'a mut dyn WritableBuffer) -> Self { + let elf_align = if is_64 { 8 } else { 4 }; + Writer { + endian, + is_64, + // Determined later. + is_mips64el: false, + elf_align, + + buffer, + len: 0, + + segment_offset: 0, + segment_num: 0, + + section_offset: 0, + section_num: 0, + + shstrtab: StringTable::default(), + shstrtab_str_id: None, + shstrtab_index: SectionIndex(0), + shstrtab_offset: 0, + shstrtab_data: Vec::new(), + + need_strtab: false, + strtab: StringTable::default(), + strtab_str_id: None, + strtab_index: SectionIndex(0), + strtab_offset: 0, + strtab_data: Vec::new(), + + symtab_str_id: None, + symtab_index: SectionIndex(0), + symtab_offset: 0, + symtab_num: 0, + + need_symtab_shndx: false, + symtab_shndx_str_id: None, + symtab_shndx_offset: 0, + symtab_shndx_data: Vec::new(), + + need_dynstr: false, + dynstr: StringTable::default(), + dynstr_str_id: None, + dynstr_index: SectionIndex(0), + dynstr_offset: 0, + dynstr_data: Vec::new(), + + dynsym_str_id: None, + dynsym_index: SectionIndex(0), + dynsym_offset: 0, + dynsym_num: 0, + + dynamic_str_id: None, + dynamic_offset: 0, + dynamic_num: 0, + + hash_str_id: None, + hash_offset: 0, + hash_size: 0, + + gnu_hash_str_id: None, + gnu_hash_offset: 0, + gnu_hash_size: 0, + + gnu_versym_str_id: None, + gnu_versym_offset: 0, + + gnu_verdef_str_id: None, + gnu_verdef_offset: 0, + gnu_verdef_size: 0, + gnu_verdef_count: 0, + gnu_verdef_remaining: 0, + gnu_verdaux_remaining: 0, + + gnu_verneed_str_id: None, + gnu_verneed_offset: 0, + gnu_verneed_size: 0, + gnu_verneed_count: 0, + gnu_verneed_remaining: 0, + gnu_vernaux_remaining: 0, + + gnu_attributes_str_id: None, + gnu_attributes_offset: 0, + gnu_attributes_size: 0, + } + } + + /// Get the file class that will be written. + fn class(&self) -> Class { + Class { is_64: self.is_64 } + } + + /// Return the current file length that has been reserved. + pub fn reserved_len(&self) -> usize { + self.len + } + + /// Return the current file length that has been written. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.buffer.len() + } + + /// Reserve a file range with the given size and starting alignment. + /// + /// Returns the aligned offset of the start of the range. + /// + /// `align_start` must be a power of two. + pub fn reserve(&mut self, len: usize, align_start: usize) -> usize { + if align_start > 1 { + self.len = util::align(self.len, align_start); + } + let offset = self.len; + self.len += len; + offset + } + + /// Write alignment padding bytes. + pub fn write_align(&mut self, align_start: usize) { + if align_start > 1 { + util::write_align(self.buffer, align_start); + } + } + + /// Write data. + /// + /// This is typically used to write section data. + pub fn write(&mut self, data: &[u8]) { + self.buffer.write_bytes(data); + } + + /// Reserve the file range up to the given file offset. + pub fn reserve_until(&mut self, offset: usize) { + debug_assert!(self.len <= offset); + self.len = offset; + } + + /// Write padding up to the given file offset. + pub fn pad_until(&mut self, offset: usize) { + debug_assert!(self.buffer.len() <= offset); + self.buffer.resize(offset); + } + + /// Reserve the range for the file header. + /// + /// This must be at the start of the file. + pub fn reserve_file_header(&mut self) { + debug_assert_eq!(self.len, 0); + self.reserve(self.class().file_header_size(), 1); + } + + /// Write the file header. + /// + /// This must be at the start of the file. + /// + /// Fields that can be derived from known information are automatically set by this function. + pub fn write_file_header(&mut self, header: &FileHeader) -> Result<()> { + debug_assert_eq!(self.buffer.len(), 0); + + self.is_mips64el = + self.is_64 && self.endian.is_little_endian() && header.e_machine == elf::EM_MIPS; + + // Start writing. + self.buffer + .reserve(self.len) + .map_err(|_| Error(String::from("Cannot allocate buffer")))?; + + // Write file header. + let e_ident = elf::Ident { + magic: elf::ELFMAG, + class: if self.is_64 { + elf::ELFCLASS64 + } else { + elf::ELFCLASS32 + }, + data: if self.endian.is_little_endian() { + elf::ELFDATA2LSB + } else { + elf::ELFDATA2MSB + }, + version: elf::EV_CURRENT, + os_abi: header.os_abi, + abi_version: header.abi_version, + padding: [0; 7], + }; + + let e_ehsize = self.class().file_header_size() as u16; + + let e_phoff = self.segment_offset as u64; + let e_phentsize = if self.segment_num == 0 { + 0 + } else { + self.class().program_header_size() as u16 + }; + // TODO: overflow + let e_phnum = self.segment_num as u16; + + let e_shoff = self.section_offset as u64; + let e_shentsize = if self.section_num == 0 { + 0 + } else { + self.class().section_header_size() as u16 + }; + let e_shnum = if self.section_num >= elf::SHN_LORESERVE.into() { + 0 + } else { + self.section_num as u16 + }; + let e_shstrndx = if self.shstrtab_index.0 >= elf::SHN_LORESERVE.into() { + elf::SHN_XINDEX + } else { + self.shstrtab_index.0 as u16 + }; + + let endian = self.endian; + if self.is_64 { + let file = elf::FileHeader64 { + e_ident, + e_type: U16::new(endian, header.e_type), + e_machine: U16::new(endian, header.e_machine), + e_version: U32::new(endian, elf::EV_CURRENT.into()), + e_entry: U64::new(endian, header.e_entry), + e_phoff: U64::new(endian, e_phoff), + e_shoff: U64::new(endian, e_shoff), + e_flags: U32::new(endian, header.e_flags), + e_ehsize: U16::new(endian, e_ehsize), + e_phentsize: U16::new(endian, e_phentsize), + e_phnum: U16::new(endian, e_phnum), + e_shentsize: U16::new(endian, e_shentsize), + e_shnum: U16::new(endian, e_shnum), + e_shstrndx: U16::new(endian, e_shstrndx), + }; + self.buffer.write(&file) + } else { + let file = elf::FileHeader32 { + e_ident, + e_type: U16::new(endian, header.e_type), + e_machine: U16::new(endian, header.e_machine), + e_version: U32::new(endian, elf::EV_CURRENT.into()), + e_entry: U32::new(endian, header.e_entry as u32), + e_phoff: U32::new(endian, e_phoff as u32), + e_shoff: U32::new(endian, e_shoff as u32), + e_flags: U32::new(endian, header.e_flags), + e_ehsize: U16::new(endian, e_ehsize), + e_phentsize: U16::new(endian, e_phentsize), + e_phnum: U16::new(endian, e_phnum), + e_shentsize: U16::new(endian, e_shentsize), + e_shnum: U16::new(endian, e_shnum), + e_shstrndx: U16::new(endian, e_shstrndx), + }; + self.buffer.write(&file); + } + + Ok(()) + } + + /// Reserve the range for the program headers. + pub fn reserve_program_headers(&mut self, num: u32) { + debug_assert_eq!(self.segment_offset, 0); + if num == 0 { + return; + } + self.segment_num = num; + self.segment_offset = self.reserve( + num as usize * self.class().program_header_size(), + self.elf_align, + ); + } + + /// Write alignment padding bytes prior to the program headers. + pub fn write_align_program_headers(&mut self) { + if self.segment_offset == 0 { + return; + } + util::write_align(self.buffer, self.elf_align); + debug_assert_eq!(self.segment_offset, self.buffer.len()); + } + + /// Write a program header. + pub fn write_program_header(&mut self, header: &ProgramHeader) { + let endian = self.endian; + if self.is_64 { + let header = elf::ProgramHeader64 { + p_type: U32::new(endian, header.p_type), + p_flags: U32::new(endian, header.p_flags), + p_offset: U64::new(endian, header.p_offset), + p_vaddr: U64::new(endian, header.p_vaddr), + p_paddr: U64::new(endian, header.p_paddr), + p_filesz: U64::new(endian, header.p_filesz), + p_memsz: U64::new(endian, header.p_memsz), + p_align: U64::new(endian, header.p_align), + }; + self.buffer.write(&header); + } else { + let header = elf::ProgramHeader32 { + p_type: U32::new(endian, header.p_type), + p_offset: U32::new(endian, header.p_offset as u32), + p_vaddr: U32::new(endian, header.p_vaddr as u32), + p_paddr: U32::new(endian, header.p_paddr as u32), + p_filesz: U32::new(endian, header.p_filesz as u32), + p_memsz: U32::new(endian, header.p_memsz as u32), + p_flags: U32::new(endian, header.p_flags), + p_align: U32::new(endian, header.p_align as u32), + }; + self.buffer.write(&header); + } + } + + /// Reserve the section index for the null section header. + /// + /// The null section header is usually automatically reserved, + /// but this can be used to force an empty section table. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_null_section_index(&mut self) -> SectionIndex { + debug_assert_eq!(self.section_num, 0); + if self.section_num == 0 { + self.section_num = 1; + } + SectionIndex(0) + } + + /// Reserve a section table index. + /// + /// Automatically also reserves the null section header if required. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_section_index(&mut self) -> SectionIndex { + debug_assert_eq!(self.section_offset, 0); + if self.section_num == 0 { + self.section_num = 1; + } + let index = self.section_num; + self.section_num += 1; + SectionIndex(index) + } + + /// Reserve the range for the section headers. + /// + /// This function does nothing if no sections were reserved. + /// This must be called after [`Self::reserve_section_index`] + /// and other functions that reserve section indices. + pub fn reserve_section_headers(&mut self) { + debug_assert_eq!(self.section_offset, 0); + if self.section_num == 0 { + return; + } + self.section_offset = self.reserve( + self.section_num as usize * self.class().section_header_size(), + self.elf_align, + ); + } + + /// Write the null section header. + /// + /// This must be the first section header that is written. + /// This function does nothing if no sections were reserved. + pub fn write_null_section_header(&mut self) { + if self.section_num == 0 { + return; + } + util::write_align(self.buffer, self.elf_align); + debug_assert_eq!(self.section_offset, self.buffer.len()); + self.write_section_header(&SectionHeader { + name: None, + sh_type: 0, + sh_flags: 0, + sh_addr: 0, + sh_offset: 0, + sh_size: if self.section_num >= elf::SHN_LORESERVE.into() { + self.section_num.into() + } else { + 0 + }, + sh_link: if self.shstrtab_index.0 >= elf::SHN_LORESERVE.into() { + self.shstrtab_index.0 + } else { + 0 + }, + // TODO: e_phnum overflow + sh_info: 0, + sh_addralign: 0, + sh_entsize: 0, + }); + } + + /// Write a section header. + pub fn write_section_header(&mut self, section: &SectionHeader) { + let sh_name = if let Some(name) = section.name { + self.shstrtab.get_offset(name) as u32 + } else { + 0 + }; + let endian = self.endian; + if self.is_64 { + let section = elf::SectionHeader64 { + sh_name: U32::new(endian, sh_name), + sh_type: U32::new(endian, section.sh_type), + sh_flags: U64::new(endian, section.sh_flags), + sh_addr: U64::new(endian, section.sh_addr), + sh_offset: U64::new(endian, section.sh_offset), + sh_size: U64::new(endian, section.sh_size), + sh_link: U32::new(endian, section.sh_link), + sh_info: U32::new(endian, section.sh_info), + sh_addralign: U64::new(endian, section.sh_addralign), + sh_entsize: U64::new(endian, section.sh_entsize), + }; + self.buffer.write(§ion); + } else { + let section = elf::SectionHeader32 { + sh_name: U32::new(endian, sh_name), + sh_type: U32::new(endian, section.sh_type), + sh_flags: U32::new(endian, section.sh_flags as u32), + sh_addr: U32::new(endian, section.sh_addr as u32), + sh_offset: U32::new(endian, section.sh_offset as u32), + sh_size: U32::new(endian, section.sh_size as u32), + sh_link: U32::new(endian, section.sh_link), + sh_info: U32::new(endian, section.sh_info), + sh_addralign: U32::new(endian, section.sh_addralign as u32), + sh_entsize: U32::new(endian, section.sh_entsize as u32), + }; + self.buffer.write(§ion); + } + } + + /// Add a section name to the section header string table. + /// + /// This will be stored in the `.shstrtab` section. + /// + /// This must be called before [`Self::reserve_shstrtab`]. + pub fn add_section_name(&mut self, name: &'a [u8]) -> StringId { + debug_assert_eq!(self.shstrtab_offset, 0); + self.shstrtab.add(name) + } + + /// Reserve the range for the section header string table. + /// + /// This range is used for a section named `.shstrtab`. + /// + /// This function does nothing if no sections were reserved. + /// This must be called after [`Self::add_section_name`]. + /// and other functions that reserve section names and indices. + pub fn reserve_shstrtab(&mut self) { + debug_assert_eq!(self.shstrtab_offset, 0); + if self.section_num == 0 { + return; + } + // Start with null section name. + self.shstrtab_data = vec![0]; + self.shstrtab.write(1, &mut self.shstrtab_data); + self.shstrtab_offset = self.reserve(self.shstrtab_data.len(), 1); + } + + /// Write the section header string table. + /// + /// This function does nothing if the section was not reserved. + pub fn write_shstrtab(&mut self) { + if self.shstrtab_offset == 0 { + return; + } + debug_assert_eq!(self.shstrtab_offset, self.buffer.len()); + self.buffer.write_bytes(&self.shstrtab_data); + } + + /// Reserve the section index for the section header string table. + /// + /// This must be called before [`Self::reserve_shstrtab`] + /// and [`Self::reserve_section_headers`]. + pub fn reserve_shstrtab_section_index(&mut self) -> SectionIndex { + self.reserve_shstrtab_section_index_with_name(&b".shstrtab"[..]) + } + + /// Reserve the section index for the section header string table. + /// + /// This must be called before [`Self::reserve_shstrtab`] + /// and [`Self::reserve_section_headers`]. + pub fn reserve_shstrtab_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert_eq!(self.shstrtab_index, SectionIndex(0)); + self.shstrtab_str_id = Some(self.add_section_name(name)); + self.shstrtab_index = self.reserve_section_index(); + self.shstrtab_index + } + + /// Write the section header for the section header string table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_shstrtab_section_header(&mut self) { + if self.shstrtab_index == SectionIndex(0) { + return; + } + self.write_section_header(&SectionHeader { + name: self.shstrtab_str_id, + sh_type: elf::SHT_STRTAB, + sh_flags: 0, + sh_addr: 0, + sh_offset: self.shstrtab_offset as u64, + sh_size: self.shstrtab_data.len() as u64, + sh_link: 0, + sh_info: 0, + sh_addralign: 1, + sh_entsize: 0, + }); + } + + /// Add a string to the string table. + /// + /// This will be stored in the `.strtab` section. + /// + /// This must be called before [`Self::reserve_strtab`]. + pub fn add_string(&mut self, name: &'a [u8]) -> StringId { + debug_assert_eq!(self.strtab_offset, 0); + self.need_strtab = true; + self.strtab.add(name) + } + + /// Return true if `.strtab` is needed. + pub fn strtab_needed(&self) -> bool { + self.need_strtab + } + + /// Require the string table even if no strings were added. + pub fn require_strtab(&mut self) { + self.need_strtab = true; + } + + /// Reserve the range for the string table. + /// + /// This range is used for a section named `.strtab`. + /// + /// This function does nothing if a string table is not required. + /// This must be called after [`Self::add_string`]. + pub fn reserve_strtab(&mut self) { + debug_assert_eq!(self.strtab_offset, 0); + if !self.need_strtab { + return; + } + // Start with null string. + self.strtab_data = vec![0]; + self.strtab.write(1, &mut self.strtab_data); + self.strtab_offset = self.reserve(self.strtab_data.len(), 1); + } + + /// Write the string table. + /// + /// This function does nothing if the section was not reserved. + pub fn write_strtab(&mut self) { + if self.strtab_offset == 0 { + return; + } + debug_assert_eq!(self.strtab_offset, self.buffer.len()); + self.buffer.write_bytes(&self.strtab_data); + } + + /// Reserve the section index for the string table. + /// + /// You should check [`Self::strtab_needed`] before calling this + /// unless you have other means of knowing if this section is needed. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_strtab_section_index(&mut self) -> SectionIndex { + self.reserve_strtab_section_index_with_name(&b".strtab"[..]) + } + + /// Reserve the section index for the string table. + /// + /// You should check [`Self::strtab_needed`] before calling this + /// unless you have other means of knowing if this section is needed. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_strtab_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert_eq!(self.strtab_index, SectionIndex(0)); + self.strtab_str_id = Some(self.add_section_name(name)); + self.strtab_index = self.reserve_section_index(); + self.strtab_index + } + + /// Write the section header for the string table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_strtab_section_header(&mut self) { + if self.strtab_index == SectionIndex(0) { + return; + } + self.write_section_header(&SectionHeader { + name: self.strtab_str_id, + sh_type: elf::SHT_STRTAB, + sh_flags: 0, + sh_addr: 0, + sh_offset: self.strtab_offset as u64, + sh_size: self.strtab_data.len() as u64, + sh_link: 0, + sh_info: 0, + sh_addralign: 1, + sh_entsize: 0, + }); + } + + /// Reserve the null symbol table entry. + /// + /// This will be stored in the `.symtab` section. + /// + /// The null symbol table entry is usually automatically reserved, + /// but this can be used to force an empty symbol table. + /// + /// This must be called before [`Self::reserve_symtab`]. + pub fn reserve_null_symbol_index(&mut self) -> SymbolIndex { + debug_assert_eq!(self.symtab_offset, 0); + debug_assert_eq!(self.symtab_num, 0); + self.symtab_num = 1; + // The symtab must link to a strtab. + self.need_strtab = true; + SymbolIndex(0) + } + + /// Reserve a symbol table entry. + /// + /// This will be stored in the `.symtab` section. + /// + /// `section_index` is used to determine whether `.symtab_shndx` is required. + /// + /// Automatically also reserves the null symbol if required. + /// Callers may assume that the returned indices will be sequential + /// starting at 1. + /// + /// This must be called before [`Self::reserve_symtab`] and + /// [`Self::reserve_symtab_shndx`]. + pub fn reserve_symbol_index(&mut self, section_index: Option<SectionIndex>) -> SymbolIndex { + debug_assert_eq!(self.symtab_offset, 0); + debug_assert_eq!(self.symtab_shndx_offset, 0); + if self.symtab_num == 0 { + self.symtab_num = 1; + // The symtab must link to a strtab. + self.need_strtab = true; + } + let index = self.symtab_num; + self.symtab_num += 1; + if let Some(section_index) = section_index { + if section_index.0 >= elf::SHN_LORESERVE.into() { + self.need_symtab_shndx = true; + } + } + SymbolIndex(index) + } + + /// Return the number of reserved symbol table entries. + /// + /// Includes the null symbol. + pub fn symbol_count(&self) -> u32 { + self.symtab_num + } + + /// Reserve the range for the symbol table. + /// + /// This range is used for a section named `.symtab`. + /// This function does nothing if no symbols were reserved. + /// This must be called after [`Self::reserve_symbol_index`]. + pub fn reserve_symtab(&mut self) { + debug_assert_eq!(self.symtab_offset, 0); + if self.symtab_num == 0 { + return; + } + self.symtab_offset = self.reserve( + self.symtab_num as usize * self.class().sym_size(), + self.elf_align, + ); + } + + /// Write the null symbol. + /// + /// This must be the first symbol that is written. + /// This function does nothing if no symbols were reserved. + pub fn write_null_symbol(&mut self) { + if self.symtab_num == 0 { + return; + } + util::write_align(self.buffer, self.elf_align); + debug_assert_eq!(self.symtab_offset, self.buffer.len()); + if self.is_64 { + self.buffer.write(&elf::Sym64::<Endianness>::default()); + } else { + self.buffer.write(&elf::Sym32::<Endianness>::default()); + } + + if self.need_symtab_shndx { + self.symtab_shndx_data.write_pod(&U32::new(self.endian, 0)); + } + } + + /// Write a symbol. + pub fn write_symbol(&mut self, sym: &Sym) { + let st_name = if let Some(name) = sym.name { + self.strtab.get_offset(name) as u32 + } else { + 0 + }; + let st_shndx = if let Some(section) = sym.section { + if section.0 >= elf::SHN_LORESERVE as u32 { + elf::SHN_XINDEX + } else { + section.0 as u16 + } + } else { + sym.st_shndx + }; + + let endian = self.endian; + if self.is_64 { + let sym = elf::Sym64 { + st_name: U32::new(endian, st_name), + st_info: sym.st_info, + st_other: sym.st_other, + st_shndx: U16::new(endian, st_shndx), + st_value: U64::new(endian, sym.st_value), + st_size: U64::new(endian, sym.st_size), + }; + self.buffer.write(&sym); + } else { + let sym = elf::Sym32 { + st_name: U32::new(endian, st_name), + st_info: sym.st_info, + st_other: sym.st_other, + st_shndx: U16::new(endian, st_shndx), + st_value: U32::new(endian, sym.st_value as u32), + st_size: U32::new(endian, sym.st_size as u32), + }; + self.buffer.write(&sym); + } + + if self.need_symtab_shndx { + let section_index = sym.section.unwrap_or(SectionIndex(0)); + self.symtab_shndx_data + .write_pod(&U32::new(self.endian, section_index.0)); + } + } + + /// Reserve the section index for the symbol table. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_symtab_section_index(&mut self) -> SectionIndex { + self.reserve_symtab_section_index_with_name(&b".symtab"[..]) + } + + /// Reserve the section index for the symbol table. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_symtab_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert_eq!(self.symtab_index, SectionIndex(0)); + self.symtab_str_id = Some(self.add_section_name(name)); + self.symtab_index = self.reserve_section_index(); + self.symtab_index + } + + /// Return the section index of the symbol table. + pub fn symtab_index(&mut self) -> SectionIndex { + self.symtab_index + } + + /// Write the section header for the symbol table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_symtab_section_header(&mut self, num_local: u32) { + if self.symtab_index == SectionIndex(0) { + return; + } + self.write_section_header(&SectionHeader { + name: self.symtab_str_id, + sh_type: elf::SHT_SYMTAB, + sh_flags: 0, + sh_addr: 0, + sh_offset: self.symtab_offset as u64, + sh_size: self.symtab_num as u64 * self.class().sym_size() as u64, + sh_link: self.strtab_index.0, + sh_info: num_local, + sh_addralign: self.elf_align as u64, + sh_entsize: self.class().sym_size() as u64, + }); + } + + /// Return true if `.symtab_shndx` is needed. + pub fn symtab_shndx_needed(&self) -> bool { + self.need_symtab_shndx + } + + /// Require the extended section indices for the symbol table even + /// if no section indices are too large. + pub fn require_symtab_shndx(&mut self) { + self.need_symtab_shndx = true; + } + + /// Reserve the range for the extended section indices for the symbol table. + /// + /// This range is used for a section named `.symtab_shndx`. + /// This also reserves a section index. + /// + /// This function does nothing if extended section indices are not needed. + /// This must be called after [`Self::reserve_symbol_index`]. + pub fn reserve_symtab_shndx(&mut self) { + debug_assert_eq!(self.symtab_shndx_offset, 0); + if !self.need_symtab_shndx { + return; + } + self.symtab_shndx_offset = self.reserve(self.symtab_num as usize * 4, ALIGN_SYMTAB_SHNDX); + self.symtab_shndx_data.reserve(self.symtab_num as usize * 4); + } + + /// Write the extended section indices for the symbol table. + /// + /// This function does nothing if the section was not reserved. + pub fn write_symtab_shndx(&mut self) { + if self.symtab_shndx_offset == 0 { + return; + } + util::write_align(self.buffer, ALIGN_SYMTAB_SHNDX); + debug_assert_eq!(self.symtab_shndx_offset, self.buffer.len()); + debug_assert_eq!(self.symtab_num as usize * 4, self.symtab_shndx_data.len()); + self.buffer.write_bytes(&self.symtab_shndx_data); + } + + /// Reserve the section index for the extended section indices symbol table. + /// + /// You should check [`Self::symtab_shndx_needed`] before calling this + /// unless you have other means of knowing if this section is needed. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_symtab_shndx_section_index(&mut self) -> SectionIndex { + self.reserve_symtab_shndx_section_index_with_name(&b".symtab_shndx"[..]) + } + + /// Reserve the section index for the extended section indices symbol table. + /// + /// You should check [`Self::symtab_shndx_needed`] before calling this + /// unless you have other means of knowing if this section is needed. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_symtab_shndx_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert!(self.symtab_shndx_str_id.is_none()); + self.symtab_shndx_str_id = Some(self.add_section_name(name)); + self.reserve_section_index() + } + + /// Write the section header for the extended section indices for the symbol table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_symtab_shndx_section_header(&mut self) { + if self.symtab_shndx_str_id.is_none() { + return; + } + let sh_size = if self.symtab_shndx_offset == 0 { + 0 + } else { + (self.symtab_num * 4) as u64 + }; + self.write_section_header(&SectionHeader { + name: self.symtab_shndx_str_id, + sh_type: elf::SHT_SYMTAB_SHNDX, + sh_flags: 0, + sh_addr: 0, + sh_offset: self.symtab_shndx_offset as u64, + sh_size, + sh_link: self.symtab_index.0, + sh_info: 0, + sh_addralign: ALIGN_SYMTAB_SHNDX as u64, + sh_entsize: 4, + }); + } + + /// Add a string to the dynamic string table. + /// + /// This will be stored in the `.dynstr` section. + /// + /// This must be called before [`Self::reserve_dynstr`]. + pub fn add_dynamic_string(&mut self, name: &'a [u8]) -> StringId { + debug_assert_eq!(self.dynstr_offset, 0); + self.need_dynstr = true; + self.dynstr.add(name) + } + + /// Get a string that was previously added to the dynamic string table. + /// + /// Panics if the string was not added. + pub fn get_dynamic_string(&self, name: &'a [u8]) -> StringId { + self.dynstr.get_id(name) + } + + /// Return true if `.dynstr` is needed. + pub fn dynstr_needed(&self) -> bool { + self.need_dynstr + } + + /// Require the dynamic string table even if no strings were added. + pub fn require_dynstr(&mut self) { + self.need_dynstr = true; + } + + /// Reserve the range for the dynamic string table. + /// + /// This range is used for a section named `.dynstr`. + /// + /// This function does nothing if no dynamic strings were defined. + /// This must be called after [`Self::add_dynamic_string`]. + pub fn reserve_dynstr(&mut self) -> usize { + debug_assert_eq!(self.dynstr_offset, 0); + if !self.need_dynstr { + return 0; + } + // Start with null string. + self.dynstr_data = vec![0]; + self.dynstr.write(1, &mut self.dynstr_data); + self.dynstr_offset = self.reserve(self.dynstr_data.len(), 1); + self.dynstr_offset + } + + /// Return the size of the dynamic string table. + /// + /// This must be called after [`Self::reserve_dynstr`]. + pub fn dynstr_len(&mut self) -> usize { + debug_assert_ne!(self.dynstr_offset, 0); + self.dynstr_data.len() + } + + /// Write the dynamic string table. + /// + /// This function does nothing if the section was not reserved. + pub fn write_dynstr(&mut self) { + if self.dynstr_offset == 0 { + return; + } + debug_assert_eq!(self.dynstr_offset, self.buffer.len()); + self.buffer.write_bytes(&self.dynstr_data); + } + + /// Reserve the section index for the dynamic string table. + /// + /// You should check [`Self::dynstr_needed`] before calling this + /// unless you have other means of knowing if this section is needed. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_dynstr_section_index(&mut self) -> SectionIndex { + self.reserve_dynstr_section_index_with_name(&b".dynstr"[..]) + } + + /// Reserve the section index for the dynamic string table. + /// + /// You should check [`Self::dynstr_needed`] before calling this + /// unless you have other means of knowing if this section is needed. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_dynstr_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert_eq!(self.dynstr_index, SectionIndex(0)); + self.dynstr_str_id = Some(self.add_section_name(name)); + self.dynstr_index = self.reserve_section_index(); + self.dynstr_index + } + + /// Return the section index of the dynamic string table. + pub fn dynstr_index(&mut self) -> SectionIndex { + self.dynstr_index + } + + /// Write the section header for the dynamic string table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_dynstr_section_header(&mut self, sh_addr: u64) { + if self.dynstr_index == SectionIndex(0) { + return; + } + self.write_section_header(&SectionHeader { + name: self.dynstr_str_id, + sh_type: elf::SHT_STRTAB, + sh_flags: elf::SHF_ALLOC.into(), + sh_addr, + sh_offset: self.dynstr_offset as u64, + sh_size: self.dynstr_data.len() as u64, + sh_link: 0, + sh_info: 0, + sh_addralign: 1, + sh_entsize: 0, + }); + } + + /// Reserve the null dynamic symbol table entry. + /// + /// This will be stored in the `.dynsym` section. + /// + /// The null dynamic symbol table entry is usually automatically reserved, + /// but this can be used to force an empty dynamic symbol table. + /// + /// This must be called before [`Self::reserve_dynsym`]. + pub fn reserve_null_dynamic_symbol_index(&mut self) -> SymbolIndex { + debug_assert_eq!(self.dynsym_offset, 0); + debug_assert_eq!(self.dynsym_num, 0); + self.dynsym_num = 1; + SymbolIndex(0) + } + + /// Reserve a dynamic symbol table entry. + /// + /// This will be stored in the `.dynsym` section. + /// + /// Automatically also reserves the null symbol if required. + /// Callers may assume that the returned indices will be sequential + /// starting at 1. + /// + /// This must be called before [`Self::reserve_dynsym`]. + pub fn reserve_dynamic_symbol_index(&mut self) -> SymbolIndex { + debug_assert_eq!(self.dynsym_offset, 0); + if self.dynsym_num == 0 { + self.dynsym_num = 1; + } + let index = self.dynsym_num; + self.dynsym_num += 1; + SymbolIndex(index) + } + + /// Return the number of reserved dynamic symbols. + /// + /// Includes the null symbol. + pub fn dynamic_symbol_count(&mut self) -> u32 { + self.dynsym_num + } + + /// Reserve the range for the dynamic symbol table. + /// + /// This range is used for a section named `.dynsym`. + /// + /// This function does nothing if no dynamic symbols were reserved. + /// This must be called after [`Self::reserve_dynamic_symbol_index`]. + pub fn reserve_dynsym(&mut self) -> usize { + debug_assert_eq!(self.dynsym_offset, 0); + if self.dynsym_num == 0 { + return 0; + } + self.dynsym_offset = self.reserve( + self.dynsym_num as usize * self.class().sym_size(), + self.elf_align, + ); + self.dynsym_offset + } + + /// Write the null dynamic symbol. + /// + /// This must be the first dynamic symbol that is written. + /// This function does nothing if no dynamic symbols were reserved. + pub fn write_null_dynamic_symbol(&mut self) { + if self.dynsym_num == 0 { + return; + } + util::write_align(self.buffer, self.elf_align); + debug_assert_eq!(self.dynsym_offset, self.buffer.len()); + if self.is_64 { + self.buffer.write(&elf::Sym64::<Endianness>::default()); + } else { + self.buffer.write(&elf::Sym32::<Endianness>::default()); + } + } + + /// Write a dynamic symbol. + pub fn write_dynamic_symbol(&mut self, sym: &Sym) { + let st_name = if let Some(name) = sym.name { + self.dynstr.get_offset(name) as u32 + } else { + 0 + }; + + let st_shndx = if let Some(section) = sym.section { + if section.0 >= elf::SHN_LORESERVE as u32 { + // TODO: we don't actually write out .dynsym_shndx yet. + // This is unlikely to be needed though. + elf::SHN_XINDEX + } else { + section.0 as u16 + } + } else { + sym.st_shndx + }; + + let endian = self.endian; + if self.is_64 { + let sym = elf::Sym64 { + st_name: U32::new(endian, st_name), + st_info: sym.st_info, + st_other: sym.st_other, + st_shndx: U16::new(endian, st_shndx), + st_value: U64::new(endian, sym.st_value), + st_size: U64::new(endian, sym.st_size), + }; + self.buffer.write(&sym); + } else { + let sym = elf::Sym32 { + st_name: U32::new(endian, st_name), + st_info: sym.st_info, + st_other: sym.st_other, + st_shndx: U16::new(endian, st_shndx), + st_value: U32::new(endian, sym.st_value as u32), + st_size: U32::new(endian, sym.st_size as u32), + }; + self.buffer.write(&sym); + } + } + + /// Reserve the section index for the dynamic symbol table. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_dynsym_section_index(&mut self) -> SectionIndex { + self.reserve_dynsym_section_index_with_name(&b".dynsym"[..]) + } + + /// Reserve the section index for the dynamic symbol table. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_dynsym_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert_eq!(self.dynsym_index, SectionIndex(0)); + self.dynsym_str_id = Some(self.add_section_name(name)); + self.dynsym_index = self.reserve_section_index(); + self.dynsym_index + } + + /// Return the section index of the dynamic symbol table. + pub fn dynsym_index(&mut self) -> SectionIndex { + self.dynsym_index + } + + /// Write the section header for the dynamic symbol table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_dynsym_section_header(&mut self, sh_addr: u64, num_local: u32) { + if self.dynsym_index == SectionIndex(0) { + return; + } + self.write_section_header(&SectionHeader { + name: self.dynsym_str_id, + sh_type: elf::SHT_DYNSYM, + sh_flags: elf::SHF_ALLOC.into(), + sh_addr, + sh_offset: self.dynsym_offset as u64, + sh_size: self.dynsym_num as u64 * self.class().sym_size() as u64, + sh_link: self.dynstr_index.0, + sh_info: num_local, + sh_addralign: self.elf_align as u64, + sh_entsize: self.class().sym_size() as u64, + }); + } + + /// Reserve the range for the `.dynamic` section. + /// + /// This function does nothing if `dynamic_num` is zero. + pub fn reserve_dynamic(&mut self, dynamic_num: usize) -> usize { + debug_assert_eq!(self.dynamic_offset, 0); + if dynamic_num == 0 { + return 0; + } + self.dynamic_num = dynamic_num; + self.dynamic_offset = self.reserve_dynamics(dynamic_num); + self.dynamic_offset + } + + /// Write alignment padding bytes prior to the `.dynamic` section. + /// + /// This function does nothing if the section was not reserved. + pub fn write_align_dynamic(&mut self) { + if self.dynamic_offset == 0 { + return; + } + util::write_align(self.buffer, self.elf_align); + debug_assert_eq!(self.dynamic_offset, self.buffer.len()); + } + + /// Reserve a file range for the given number of dynamic entries. + /// + /// Returns the offset of the range. + pub fn reserve_dynamics(&mut self, dynamic_num: usize) -> usize { + self.reserve(dynamic_num * self.class().dyn_size(), self.elf_align) + } + + /// Write a dynamic string entry. + pub fn write_dynamic_string(&mut self, tag: u32, id: StringId) { + self.write_dynamic(tag, self.dynstr.get_offset(id) as u64); + } + + /// Write a dynamic value entry. + pub fn write_dynamic(&mut self, d_tag: u32, d_val: u64) { + let endian = self.endian; + if self.is_64 { + let d = elf::Dyn64 { + d_tag: U64::new(endian, d_tag.into()), + d_val: U64::new(endian, d_val), + }; + self.buffer.write(&d); + } else { + let d = elf::Dyn32 { + d_tag: U32::new(endian, d_tag), + d_val: U32::new(endian, d_val as u32), + }; + self.buffer.write(&d); + } + } + + /// Reserve the section index for the dynamic table. + pub fn reserve_dynamic_section_index(&mut self) -> SectionIndex { + debug_assert!(self.dynamic_str_id.is_none()); + self.dynamic_str_id = Some(self.add_section_name(&b".dynamic"[..])); + self.reserve_section_index() + } + + /// Write the section header for the dynamic table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_dynamic_section_header(&mut self, sh_addr: u64) { + if self.dynamic_str_id.is_none() { + return; + } + self.write_section_header(&SectionHeader { + name: self.dynamic_str_id, + sh_type: elf::SHT_DYNAMIC, + sh_flags: (elf::SHF_WRITE | elf::SHF_ALLOC).into(), + sh_addr, + sh_offset: self.dynamic_offset as u64, + sh_size: (self.dynamic_num * self.class().dyn_size()) as u64, + sh_link: self.dynstr_index.0, + sh_info: 0, + sh_addralign: self.elf_align as u64, + sh_entsize: self.class().dyn_size() as u64, + }); + } + + /// Reserve a file range for a SysV hash section. + /// + /// `symbol_count` is the number of symbols in the hash, + /// not the total number of symbols. + pub fn reserve_hash(&mut self, bucket_count: u32, chain_count: u32) -> usize { + self.hash_size = self.class().hash_size(bucket_count, chain_count); + self.hash_offset = self.reserve(self.hash_size, ALIGN_HASH); + self.hash_offset + } + + /// Write a SysV hash section. + /// + /// `chain_count` is the number of symbols in the hash. + /// The argument to `hash` will be in the range `0..chain_count`. + pub fn write_hash<F>(&mut self, bucket_count: u32, chain_count: u32, hash: F) + where + F: Fn(u32) -> Option<u32>, + { + let mut buckets = vec![U32::new(self.endian, 0); bucket_count as usize]; + let mut chains = vec![U32::new(self.endian, 0); chain_count as usize]; + for i in 0..chain_count { + if let Some(hash) = hash(i) { + let bucket = hash % bucket_count; + chains[i as usize] = buckets[bucket as usize]; + buckets[bucket as usize] = U32::new(self.endian, i); + } + } + + util::write_align(self.buffer, ALIGN_HASH); + debug_assert_eq!(self.hash_offset, self.buffer.len()); + self.buffer.write(&elf::HashHeader { + bucket_count: U32::new(self.endian, bucket_count), + chain_count: U32::new(self.endian, chain_count), + }); + self.buffer.write_slice(&buckets); + self.buffer.write_slice(&chains); + } + + /// Reserve the section index for the SysV hash table. + pub fn reserve_hash_section_index(&mut self) -> SectionIndex { + self.reserve_hash_section_index_with_name(&b".hash"[..]) + } + + /// Reserve the section index for the SysV hash table. + pub fn reserve_hash_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert!(self.hash_str_id.is_none()); + self.hash_str_id = Some(self.add_section_name(name)); + self.reserve_section_index() + } + + /// Write the section header for the SysV hash table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_hash_section_header(&mut self, sh_addr: u64) { + if self.hash_str_id.is_none() { + return; + } + self.write_section_header(&SectionHeader { + name: self.hash_str_id, + sh_type: elf::SHT_HASH, + sh_flags: elf::SHF_ALLOC.into(), + sh_addr, + sh_offset: self.hash_offset as u64, + sh_size: self.hash_size as u64, + sh_link: self.dynsym_index.0, + sh_info: 0, + sh_addralign: ALIGN_HASH as u64, + sh_entsize: 4, + }); + } + + /// Reserve a file range for a GNU hash section. + /// + /// `symbol_count` is the number of symbols in the hash, + /// not the total number of symbols. + pub fn reserve_gnu_hash( + &mut self, + bloom_count: u32, + bucket_count: u32, + symbol_count: u32, + ) -> usize { + self.gnu_hash_size = self + .class() + .gnu_hash_size(bloom_count, bucket_count, symbol_count); + self.gnu_hash_offset = self.reserve(self.gnu_hash_size, self.elf_align); + self.gnu_hash_offset + } + + /// Write a GNU hash section. + /// + /// `symbol_count` is the number of symbols in the hash. + /// The argument to `hash` will be in the range `0..symbol_count`. + /// + /// This requires that symbols are already sorted by bucket. + pub fn write_gnu_hash<F>( + &mut self, + symbol_base: u32, + bloom_shift: u32, + bloom_count: u32, + bucket_count: u32, + symbol_count: u32, + hash: F, + ) where + F: Fn(u32) -> u32, + { + util::write_align(self.buffer, self.elf_align); + debug_assert_eq!(self.gnu_hash_offset, self.buffer.len()); + self.buffer.write(&elf::GnuHashHeader { + bucket_count: U32::new(self.endian, bucket_count), + symbol_base: U32::new(self.endian, symbol_base), + bloom_count: U32::new(self.endian, bloom_count), + bloom_shift: U32::new(self.endian, bloom_shift), + }); + + // Calculate and write bloom filter. + if self.is_64 { + let mut bloom_filters = vec![0; bloom_count as usize]; + for i in 0..symbol_count { + let h = hash(i); + bloom_filters[((h / 64) & (bloom_count - 1)) as usize] |= + 1 << (h % 64) | 1 << ((h >> bloom_shift) % 64); + } + for bloom_filter in bloom_filters { + self.buffer.write(&U64::new(self.endian, bloom_filter)); + } + } else { + let mut bloom_filters = vec![0; bloom_count as usize]; + for i in 0..symbol_count { + let h = hash(i); + bloom_filters[((h / 32) & (bloom_count - 1)) as usize] |= + 1 << (h % 32) | 1 << ((h >> bloom_shift) % 32); + } + for bloom_filter in bloom_filters { + self.buffer.write(&U32::new(self.endian, bloom_filter)); + } + } + + // Write buckets. + // + // This requires that symbols are already sorted by bucket. + let mut bucket = 0; + for i in 0..symbol_count { + let symbol_bucket = hash(i) % bucket_count; + while bucket < symbol_bucket { + self.buffer.write(&U32::new(self.endian, 0)); + bucket += 1; + } + if bucket == symbol_bucket { + self.buffer.write(&U32::new(self.endian, symbol_base + i)); + bucket += 1; + } + } + while bucket < bucket_count { + self.buffer.write(&U32::new(self.endian, 0)); + bucket += 1; + } + + // Write hash values. + for i in 0..symbol_count { + let mut h = hash(i); + if i == symbol_count - 1 || h % bucket_count != hash(i + 1) % bucket_count { + h |= 1; + } else { + h &= !1; + } + self.buffer.write(&U32::new(self.endian, h)); + } + } + + /// Reserve the section index for the GNU hash table. + pub fn reserve_gnu_hash_section_index(&mut self) -> SectionIndex { + self.reserve_gnu_hash_section_index_with_name(&b".gnu.hash"[..]) + } + + /// Reserve the section index for the GNU hash table. + pub fn reserve_gnu_hash_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert!(self.gnu_hash_str_id.is_none()); + self.gnu_hash_str_id = Some(self.add_section_name(name)); + self.reserve_section_index() + } + + /// Write the section header for the GNU hash table. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_gnu_hash_section_header(&mut self, sh_addr: u64) { + if self.gnu_hash_str_id.is_none() { + return; + } + self.write_section_header(&SectionHeader { + name: self.gnu_hash_str_id, + sh_type: elf::SHT_GNU_HASH, + sh_flags: elf::SHF_ALLOC.into(), + sh_addr, + sh_offset: self.gnu_hash_offset as u64, + sh_size: self.gnu_hash_size as u64, + sh_link: self.dynsym_index.0, + sh_info: 0, + sh_addralign: self.elf_align as u64, + sh_entsize: if self.is_64 { 0 } else { 4 }, + }); + } + + /// Reserve the range for the `.gnu.version` section. + /// + /// This function does nothing if no dynamic symbols were reserved. + pub fn reserve_gnu_versym(&mut self) -> usize { + debug_assert_eq!(self.gnu_versym_offset, 0); + if self.dynsym_num == 0 { + return 0; + } + self.gnu_versym_offset = self.reserve(self.dynsym_num as usize * 2, ALIGN_GNU_VERSYM); + self.gnu_versym_offset + } + + /// Write the null symbol version entry. + /// + /// This must be the first symbol version that is written. + /// This function does nothing if no dynamic symbols were reserved. + pub fn write_null_gnu_versym(&mut self) { + if self.dynsym_num == 0 { + return; + } + util::write_align(self.buffer, ALIGN_GNU_VERSYM); + debug_assert_eq!(self.gnu_versym_offset, self.buffer.len()); + self.write_gnu_versym(0); + } + + /// Write a symbol version entry. + pub fn write_gnu_versym(&mut self, versym: u16) { + self.buffer.write(&U16::new(self.endian, versym)); + } + + /// Reserve the section index for the `.gnu.version` section. + pub fn reserve_gnu_versym_section_index(&mut self) -> SectionIndex { + self.reserve_gnu_versym_section_index_with_name(&b".gnu.version"[..]) + } + + /// Reserve the section index for the `.gnu.version` section. + pub fn reserve_gnu_versym_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert!(self.gnu_versym_str_id.is_none()); + self.gnu_versym_str_id = Some(self.add_section_name(name)); + self.reserve_section_index() + } + + /// Write the section header for the `.gnu.version` section. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_gnu_versym_section_header(&mut self, sh_addr: u64) { + if self.gnu_versym_str_id.is_none() { + return; + } + self.write_section_header(&SectionHeader { + name: self.gnu_versym_str_id, + sh_type: elf::SHT_GNU_VERSYM, + sh_flags: elf::SHF_ALLOC.into(), + sh_addr, + sh_offset: self.gnu_versym_offset as u64, + sh_size: self.class().gnu_versym_size(self.dynsym_num as usize) as u64, + sh_link: self.dynsym_index.0, + sh_info: 0, + sh_addralign: ALIGN_GNU_VERSYM as u64, + sh_entsize: 2, + }); + } + + /// Reserve the range for the `.gnu.version_d` section. + pub fn reserve_gnu_verdef(&mut self, verdef_count: usize, verdaux_count: usize) -> usize { + debug_assert_eq!(self.gnu_verdef_offset, 0); + if verdef_count == 0 { + return 0; + } + self.gnu_verdef_size = self.class().gnu_verdef_size(verdef_count, verdaux_count); + self.gnu_verdef_offset = self.reserve(self.gnu_verdef_size, ALIGN_GNU_VERDEF); + self.gnu_verdef_count = verdef_count as u16; + self.gnu_verdef_remaining = self.gnu_verdef_count; + self.gnu_verdef_offset + } + + /// Write alignment padding bytes prior to a `.gnu.version_d` section. + pub fn write_align_gnu_verdef(&mut self) { + if self.gnu_verdef_offset == 0 { + return; + } + util::write_align(self.buffer, ALIGN_GNU_VERDEF); + debug_assert_eq!(self.gnu_verdef_offset, self.buffer.len()); + } + + /// Write a version definition entry. + pub fn write_gnu_verdef(&mut self, verdef: &Verdef) { + debug_assert_ne!(self.gnu_verdef_remaining, 0); + self.gnu_verdef_remaining -= 1; + let vd_next = if self.gnu_verdef_remaining == 0 { + 0 + } else { + mem::size_of::<elf::Verdef<Endianness>>() as u32 + + verdef.aux_count as u32 * mem::size_of::<elf::Verdaux<Endianness>>() as u32 + }; + + debug_assert_ne!(verdef.aux_count, 0); + self.gnu_verdaux_remaining = verdef.aux_count; + let vd_aux = mem::size_of::<elf::Verdef<Endianness>>() as u32; + + self.buffer.write(&elf::Verdef { + vd_version: U16::new(self.endian, verdef.version), + vd_flags: U16::new(self.endian, verdef.flags), + vd_ndx: U16::new(self.endian, verdef.index), + vd_cnt: U16::new(self.endian, verdef.aux_count), + vd_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(verdef.name))), + vd_aux: U32::new(self.endian, vd_aux), + vd_next: U32::new(self.endian, vd_next), + }); + self.write_gnu_verdaux(verdef.name); + } + + /// Write a version definition entry that shares the names of the next definition. + /// + /// This is typically useful when there are only two versions (including the base) + /// and they have the same name. + pub fn write_gnu_verdef_shared(&mut self, verdef: &Verdef) { + debug_assert_ne!(self.gnu_verdef_remaining, 0); + self.gnu_verdef_remaining -= 1; + debug_assert_ne!(self.gnu_verdef_remaining, 0); + let vd_next = mem::size_of::<elf::Verdef<Endianness>>() as u32; + + debug_assert_ne!(verdef.aux_count, 0); + self.gnu_verdaux_remaining = 0; + let vd_aux = 2 * mem::size_of::<elf::Verdef<Endianness>>() as u32; + + self.buffer.write(&elf::Verdef { + vd_version: U16::new(self.endian, verdef.version), + vd_flags: U16::new(self.endian, verdef.flags), + vd_ndx: U16::new(self.endian, verdef.index), + vd_cnt: U16::new(self.endian, verdef.aux_count), + vd_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(verdef.name))), + vd_aux: U32::new(self.endian, vd_aux), + vd_next: U32::new(self.endian, vd_next), + }); + } + + /// Write a version definition auxiliary entry. + pub fn write_gnu_verdaux(&mut self, name: StringId) { + debug_assert_ne!(self.gnu_verdaux_remaining, 0); + self.gnu_verdaux_remaining -= 1; + let vda_next = if self.gnu_verdaux_remaining == 0 { + 0 + } else { + mem::size_of::<elf::Verdaux<Endianness>>() as u32 + }; + self.buffer.write(&elf::Verdaux { + vda_name: U32::new(self.endian, self.dynstr.get_offset(name) as u32), + vda_next: U32::new(self.endian, vda_next), + }); + } + + /// Reserve the section index for the `.gnu.version_d` section. + pub fn reserve_gnu_verdef_section_index(&mut self) -> SectionIndex { + self.reserve_gnu_verdef_section_index_with_name(&b".gnu.version_d"[..]) + } + + /// Reserve the section index for the `.gnu.version_d` section. + pub fn reserve_gnu_verdef_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert!(self.gnu_verdef_str_id.is_none()); + self.gnu_verdef_str_id = Some(self.add_section_name(name)); + self.reserve_section_index() + } + + /// Write the section header for the `.gnu.version_d` section. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_gnu_verdef_section_header(&mut self, sh_addr: u64) { + if self.gnu_verdef_str_id.is_none() { + return; + } + self.write_section_header(&SectionHeader { + name: self.gnu_verdef_str_id, + sh_type: elf::SHT_GNU_VERDEF, + sh_flags: elf::SHF_ALLOC.into(), + sh_addr, + sh_offset: self.gnu_verdef_offset as u64, + sh_size: self.gnu_verdef_size as u64, + sh_link: self.dynstr_index.0, + sh_info: self.gnu_verdef_count.into(), + sh_addralign: ALIGN_GNU_VERDEF as u64, + sh_entsize: 0, + }); + } + + /// Reserve the range for the `.gnu.version_r` section. + pub fn reserve_gnu_verneed(&mut self, verneed_count: usize, vernaux_count: usize) -> usize { + debug_assert_eq!(self.gnu_verneed_offset, 0); + if verneed_count == 0 { + return 0; + } + self.gnu_verneed_size = self.class().gnu_verneed_size(verneed_count, vernaux_count); + self.gnu_verneed_offset = self.reserve(self.gnu_verneed_size, ALIGN_GNU_VERNEED); + self.gnu_verneed_count = verneed_count as u16; + self.gnu_verneed_remaining = self.gnu_verneed_count; + self.gnu_verneed_offset + } + + /// Write alignment padding bytes prior to a `.gnu.version_r` section. + pub fn write_align_gnu_verneed(&mut self) { + if self.gnu_verneed_offset == 0 { + return; + } + util::write_align(self.buffer, ALIGN_GNU_VERNEED); + debug_assert_eq!(self.gnu_verneed_offset, self.buffer.len()); + } + + /// Write a version need entry. + pub fn write_gnu_verneed(&mut self, verneed: &Verneed) { + debug_assert_ne!(self.gnu_verneed_remaining, 0); + self.gnu_verneed_remaining -= 1; + let vn_next = if self.gnu_verneed_remaining == 0 { + 0 + } else { + mem::size_of::<elf::Verneed<Endianness>>() as u32 + + verneed.aux_count as u32 * mem::size_of::<elf::Vernaux<Endianness>>() as u32 + }; + + self.gnu_vernaux_remaining = verneed.aux_count; + let vn_aux = if verneed.aux_count == 0 { + 0 + } else { + mem::size_of::<elf::Verneed<Endianness>>() as u32 + }; + + self.buffer.write(&elf::Verneed { + vn_version: U16::new(self.endian, verneed.version), + vn_cnt: U16::new(self.endian, verneed.aux_count), + vn_file: U32::new(self.endian, self.dynstr.get_offset(verneed.file) as u32), + vn_aux: U32::new(self.endian, vn_aux), + vn_next: U32::new(self.endian, vn_next), + }); + } + + /// Write a version need auxiliary entry. + pub fn write_gnu_vernaux(&mut self, vernaux: &Vernaux) { + debug_assert_ne!(self.gnu_vernaux_remaining, 0); + self.gnu_vernaux_remaining -= 1; + let vna_next = if self.gnu_vernaux_remaining == 0 { + 0 + } else { + mem::size_of::<elf::Vernaux<Endianness>>() as u32 + }; + self.buffer.write(&elf::Vernaux { + vna_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(vernaux.name))), + vna_flags: U16::new(self.endian, vernaux.flags), + vna_other: U16::new(self.endian, vernaux.index), + vna_name: U32::new(self.endian, self.dynstr.get_offset(vernaux.name) as u32), + vna_next: U32::new(self.endian, vna_next), + }); + } + + /// Reserve the section index for the `.gnu.version_r` section. + pub fn reserve_gnu_verneed_section_index(&mut self) -> SectionIndex { + self.reserve_gnu_verneed_section_index_with_name(&b".gnu.version_r"[..]) + } + + /// Reserve the section index for the `.gnu.version_r` section. + pub fn reserve_gnu_verneed_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { + debug_assert!(self.gnu_verneed_str_id.is_none()); + self.gnu_verneed_str_id = Some(self.add_section_name(name)); + self.reserve_section_index() + } + + /// Write the section header for the `.gnu.version_r` section. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_gnu_verneed_section_header(&mut self, sh_addr: u64) { + if self.gnu_verneed_str_id.is_none() { + return; + } + self.write_section_header(&SectionHeader { + name: self.gnu_verneed_str_id, + sh_type: elf::SHT_GNU_VERNEED, + sh_flags: elf::SHF_ALLOC.into(), + sh_addr, + sh_offset: self.gnu_verneed_offset as u64, + sh_size: self.gnu_verneed_size as u64, + sh_link: self.dynstr_index.0, + sh_info: self.gnu_verneed_count.into(), + sh_addralign: ALIGN_GNU_VERNEED as u64, + sh_entsize: 0, + }); + } + + /// Reserve the section index for the `.gnu.attributes` section. + pub fn reserve_gnu_attributes_section_index(&mut self) -> SectionIndex { + self.reserve_gnu_attributes_section_index_with_name(&b".gnu.attributes"[..]) + } + + /// Reserve the section index for the `.gnu.attributes` section. + pub fn reserve_gnu_attributes_section_index_with_name( + &mut self, + name: &'a [u8], + ) -> SectionIndex { + debug_assert!(self.gnu_attributes_str_id.is_none()); + self.gnu_attributes_str_id = Some(self.add_section_name(name)); + self.reserve_section_index() + } + + /// Reserve the range for the `.gnu.attributes` section. + pub fn reserve_gnu_attributes(&mut self, gnu_attributes_size: usize) -> usize { + debug_assert_eq!(self.gnu_attributes_offset, 0); + if gnu_attributes_size == 0 { + return 0; + } + self.gnu_attributes_size = gnu_attributes_size; + self.gnu_attributes_offset = self.reserve(self.gnu_attributes_size, self.elf_align); + self.gnu_attributes_offset + } + + /// Write the section header for the `.gnu.attributes` section. + /// + /// This function does nothing if the section index was not reserved. + pub fn write_gnu_attributes_section_header(&mut self) { + if self.gnu_attributes_str_id.is_none() { + return; + } + self.write_section_header(&SectionHeader { + name: self.gnu_attributes_str_id, + sh_type: elf::SHT_GNU_ATTRIBUTES, + sh_flags: 0, + sh_addr: 0, + sh_offset: self.gnu_attributes_offset as u64, + sh_size: self.gnu_attributes_size as u64, + sh_link: self.dynstr_index.0, + sh_info: 0, // TODO + sh_addralign: self.elf_align as u64, + sh_entsize: 0, + }); + } + + /// Write the data for the `.gnu.attributes` section. + pub fn write_gnu_attributes(&mut self, data: &[u8]) { + if self.gnu_attributes_offset == 0 { + return; + } + util::write_align(self.buffer, self.elf_align); + debug_assert_eq!(self.gnu_attributes_offset, self.buffer.len()); + self.buffer.write_bytes(data); + } + + /// Reserve a file range for the given number of relocations. + /// + /// Returns the offset of the range. + pub fn reserve_relocations(&mut self, count: usize, is_rela: bool) -> usize { + self.reserve(count * self.class().rel_size(is_rela), self.elf_align) + } + + /// Write alignment padding bytes prior to a relocation section. + pub fn write_align_relocation(&mut self) { + util::write_align(self.buffer, self.elf_align); + } + + /// Write a relocation. + pub fn write_relocation(&mut self, is_rela: bool, rel: &Rel) { + let endian = self.endian; + if self.is_64 { + if is_rela { + let rel = elf::Rela64 { + r_offset: U64::new(endian, rel.r_offset), + r_info: elf::Rela64::r_info(endian, self.is_mips64el, rel.r_sym, rel.r_type), + r_addend: I64::new(endian, rel.r_addend), + }; + self.buffer.write(&rel); + } else { + let rel = elf::Rel64 { + r_offset: U64::new(endian, rel.r_offset), + r_info: elf::Rel64::r_info(endian, rel.r_sym, rel.r_type), + }; + self.buffer.write(&rel); + } + } else { + if is_rela { + let rel = elf::Rela32 { + r_offset: U32::new(endian, rel.r_offset as u32), + r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), + r_addend: I32::new(endian, rel.r_addend as i32), + }; + self.buffer.write(&rel); + } else { + let rel = elf::Rel32 { + r_offset: U32::new(endian, rel.r_offset as u32), + r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), + }; + self.buffer.write(&rel); + } + } + } + + /// Write the section header for a relocation section. + /// + /// `section` is the index of the section the relocations apply to, + /// or 0 if none. + /// + /// `symtab` is the index of the symbol table the relocations refer to, + /// or 0 if none. + /// + /// `offset` is the file offset of the relocations. + pub fn write_relocation_section_header( + &mut self, + name: StringId, + section: SectionIndex, + symtab: SectionIndex, + offset: usize, + count: usize, + is_rela: bool, + ) { + self.write_section_header(&SectionHeader { + name: Some(name), + sh_type: if is_rela { elf::SHT_RELA } else { elf::SHT_REL }, + sh_flags: elf::SHF_INFO_LINK.into(), + sh_addr: 0, + sh_offset: offset as u64, + sh_size: (count * self.class().rel_size(is_rela)) as u64, + sh_link: symtab.0, + sh_info: section.0, + sh_addralign: self.elf_align as u64, + sh_entsize: self.class().rel_size(is_rela) as u64, + }); + } + + /// Write the section header for a relative relocation section. + /// + /// `offset` is the file offset of the relocations. + /// `size` is the size of the section in bytes. + pub fn write_relative_relocation_section_header( + &mut self, + name: StringId, + offset: usize, + size: usize, + ) { + self.write_section_header(&SectionHeader { + name: Some(name), + sh_type: elf::SHT_RELA, + sh_flags: 0, + sh_addr: 0, + sh_offset: offset as u64, + sh_size: size as u64, + sh_link: 0, + sh_info: 0, + sh_addralign: self.elf_align as u64, + sh_entsize: self.class().relr_size() as u64, + }); + } + + /// Reserve a file range for a COMDAT section. + /// + /// `count` is the number of sections in the COMDAT group. + /// + /// Returns the offset of the range. + pub fn reserve_comdat(&mut self, count: usize) -> usize { + self.reserve((count + 1) * 4, 4) + } + + /// Write `GRP_COMDAT` at the start of the COMDAT section. + pub fn write_comdat_header(&mut self) { + util::write_align(self.buffer, 4); + self.buffer.write(&U32::new(self.endian, elf::GRP_COMDAT)); + } + + /// Write an entry in a COMDAT section. + pub fn write_comdat_entry(&mut self, entry: SectionIndex) { + self.buffer.write(&U32::new(self.endian, entry.0)); + } + + /// Write the section header for a COMDAT section. + pub fn write_comdat_section_header( + &mut self, + name: StringId, + symtab: SectionIndex, + symbol: SymbolIndex, + offset: usize, + count: usize, + ) { + self.write_section_header(&SectionHeader { + name: Some(name), + sh_type: elf::SHT_GROUP, + sh_flags: 0, + sh_addr: 0, + sh_offset: offset as u64, + sh_size: ((count + 1) * 4) as u64, + sh_link: symtab.0, + sh_info: symbol.0, + sh_addralign: 4, + sh_entsize: 4, + }); + } + + /// Return a helper for writing an attributes section. + pub fn attributes_writer(&self) -> AttributesWriter { + AttributesWriter::new(self.endian) + } +} + +/// A helper for writing an attributes section. +/// +/// Attributes have a variable length encoding, so it is awkward to write them in a +/// single pass. Instead, we build the entire attributes section data in memory, using +/// placeholders for unknown lengths that are filled in later. +#[allow(missing_debug_implementations)] +pub struct AttributesWriter { + endian: Endianness, + data: Vec<u8>, + subsection_offset: usize, + subsubsection_offset: usize, +} + +impl AttributesWriter { + /// Create a new `AttributesWriter` for the given endianness. + pub fn new(endian: Endianness) -> Self { + AttributesWriter { + endian, + data: vec![0x41], + subsection_offset: 0, + subsubsection_offset: 0, + } + } + + /// Start a new subsection with the given vendor name. + pub fn start_subsection(&mut self, vendor: &[u8]) { + debug_assert_eq!(self.subsection_offset, 0); + debug_assert_eq!(self.subsubsection_offset, 0); + self.subsection_offset = self.data.len(); + self.data.extend_from_slice(&[0; 4]); + self.data.extend_from_slice(vendor); + self.data.push(0); + } + + /// End the subsection. + /// + /// The subsection length is automatically calculated and written. + pub fn end_subsection(&mut self) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_eq!(self.subsubsection_offset, 0); + let length = self.data.len() - self.subsection_offset; + self.data[self.subsection_offset..][..4] + .copy_from_slice(pod::bytes_of(&U32::new(self.endian, length as u32))); + self.subsection_offset = 0; + } + + /// Start a new sub-subsection with the given tag. + pub fn start_subsubsection(&mut self, tag: u8) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_eq!(self.subsubsection_offset, 0); + self.subsubsection_offset = self.data.len(); + self.data.push(tag); + self.data.extend_from_slice(&[0; 4]); + } + + /// Write a section or symbol index to the sub-subsection. + /// + /// The user must also call this function to write the terminating 0 index. + pub fn write_subsubsection_index(&mut self, index: u32) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_ne!(self.subsubsection_offset, 0); + util::write_uleb128(&mut self.data, u64::from(index)); + } + + /// Write raw index data to the sub-subsection. + /// + /// The terminating 0 index is automatically written. + pub fn write_subsubsection_indices(&mut self, indices: &[u8]) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_ne!(self.subsubsection_offset, 0); + self.data.extend_from_slice(indices); + self.data.push(0); + } + + /// Write an attribute tag to the sub-subsection. + pub fn write_attribute_tag(&mut self, tag: u64) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_ne!(self.subsubsection_offset, 0); + util::write_uleb128(&mut self.data, tag); + } + + /// Write an attribute integer value to the sub-subsection. + pub fn write_attribute_integer(&mut self, value: u64) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_ne!(self.subsubsection_offset, 0); + util::write_uleb128(&mut self.data, value); + } + + /// Write an attribute string value to the sub-subsection. + /// + /// The value must not include the null terminator. + pub fn write_attribute_string(&mut self, value: &[u8]) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_ne!(self.subsubsection_offset, 0); + self.data.extend_from_slice(value); + self.data.push(0); + } + + /// Write raw attribute data to the sub-subsection. + pub fn write_subsubsection_attributes(&mut self, attributes: &[u8]) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_ne!(self.subsubsection_offset, 0); + self.data.extend_from_slice(attributes); + } + + /// End the sub-subsection. + /// + /// The sub-subsection length is automatically calculated and written. + pub fn end_subsubsection(&mut self) { + debug_assert_ne!(self.subsection_offset, 0); + debug_assert_ne!(self.subsubsection_offset, 0); + let length = self.data.len() - self.subsubsection_offset; + self.data[self.subsubsection_offset + 1..][..4] + .copy_from_slice(pod::bytes_of(&U32::new(self.endian, length as u32))); + self.subsubsection_offset = 0; + } + + /// Return the completed section data. + pub fn data(self) -> Vec<u8> { + debug_assert_eq!(self.subsection_offset, 0); + debug_assert_eq!(self.subsubsection_offset, 0); + self.data + } +} + +/// An ELF file class. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct Class { + /// Whether the file is 64-bit. + pub is_64: bool, +} + +impl Class { + /// Return the alignment size. + pub fn align(self) -> usize { + if self.is_64 { + 8 + } else { + 4 + } + } + + /// Return the size of the file header. + pub fn file_header_size(self) -> usize { + if self.is_64 { + mem::size_of::<elf::FileHeader64<Endianness>>() + } else { + mem::size_of::<elf::FileHeader32<Endianness>>() + } + } + + /// Return the size of a program header. + pub fn program_header_size(self) -> usize { + if self.is_64 { + mem::size_of::<elf::ProgramHeader64<Endianness>>() + } else { + mem::size_of::<elf::ProgramHeader32<Endianness>>() + } + } + + /// Return the size of a section header. + pub fn section_header_size(self) -> usize { + if self.is_64 { + mem::size_of::<elf::SectionHeader64<Endianness>>() + } else { + mem::size_of::<elf::SectionHeader32<Endianness>>() + } + } + + /// Return the size of a symbol. + pub fn sym_size(self) -> usize { + if self.is_64 { + mem::size_of::<elf::Sym64<Endianness>>() + } else { + mem::size_of::<elf::Sym32<Endianness>>() + } + } + + /// Return the size of a relocation entry. + pub fn rel_size(self, is_rela: bool) -> usize { + if self.is_64 { + if is_rela { + mem::size_of::<elf::Rela64<Endianness>>() + } else { + mem::size_of::<elf::Rel64<Endianness>>() + } + } else { + if is_rela { + mem::size_of::<elf::Rela32<Endianness>>() + } else { + mem::size_of::<elf::Rel32<Endianness>>() + } + } + } + + /// Return the size of a relative relocation entry. + pub fn relr_size(self) -> usize { + if self.is_64 { + mem::size_of::<elf::Relr64<Endianness>>() + } else { + mem::size_of::<elf::Relr32<Endianness>>() + } + } + + /// Return the size of a dynamic entry. + pub fn dyn_size(self) -> usize { + if self.is_64 { + mem::size_of::<elf::Dyn64<Endianness>>() + } else { + mem::size_of::<elf::Dyn32<Endianness>>() + } + } + + /// Return the size of a hash table. + pub fn hash_size(self, bucket_count: u32, chain_count: u32) -> usize { + mem::size_of::<elf::HashHeader<Endianness>>() + + bucket_count as usize * 4 + + chain_count as usize * 4 + } + + /// Return the size of a GNU hash table. + pub fn gnu_hash_size(self, bloom_count: u32, bucket_count: u32, symbol_count: u32) -> usize { + let bloom_size = if self.is_64 { 8 } else { 4 }; + mem::size_of::<elf::GnuHashHeader<Endianness>>() + + bloom_count as usize * bloom_size + + bucket_count as usize * 4 + + symbol_count as usize * 4 + } + + /// Return the size of a GNU symbol version section. + pub fn gnu_versym_size(self, symbol_count: usize) -> usize { + symbol_count * 2 + } + + /// Return the size of a GNU version definition section. + pub fn gnu_verdef_size(self, verdef_count: usize, verdaux_count: usize) -> usize { + verdef_count * mem::size_of::<elf::Verdef<Endianness>>() + + verdaux_count * mem::size_of::<elf::Verdaux<Endianness>>() + } + + /// Return the size of a GNU version dependency section. + pub fn gnu_verneed_size(self, verneed_count: usize, vernaux_count: usize) -> usize { + verneed_count * mem::size_of::<elf::Verneed<Endianness>>() + + vernaux_count * mem::size_of::<elf::Vernaux<Endianness>>() + } +} + +/// Native endian version of [`elf::FileHeader64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct FileHeader { + pub os_abi: u8, + pub abi_version: u8, + pub e_type: u16, + pub e_machine: u16, + pub e_entry: u64, + pub e_flags: u32, +} + +/// Native endian version of [`elf::ProgramHeader64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct ProgramHeader { + pub p_type: u32, + pub p_flags: u32, + pub p_offset: u64, + pub p_vaddr: u64, + pub p_paddr: u64, + pub p_filesz: u64, + pub p_memsz: u64, + pub p_align: u64, +} + +/// Native endian version of [`elf::SectionHeader64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct SectionHeader { + pub name: Option<StringId>, + pub sh_type: u32, + pub sh_flags: u64, + pub sh_addr: u64, + pub sh_offset: u64, + pub sh_size: u64, + pub sh_link: u32, + pub sh_info: u32, + pub sh_addralign: u64, + pub sh_entsize: u64, +} + +/// Native endian version of [`elf::Sym64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Sym { + pub name: Option<StringId>, + pub section: Option<SectionIndex>, + pub st_info: u8, + pub st_other: u8, + pub st_shndx: u16, + pub st_value: u64, + pub st_size: u64, +} + +/// Unified native endian version of [`elf::Rel64`] and [`elf::Rela64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Rel { + pub r_offset: u64, + pub r_sym: u32, + pub r_type: u32, + pub r_addend: i64, +} + +/// Information required for writing [`elf::Verdef`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Verdef { + pub version: u16, + pub flags: u16, + pub index: u16, + pub aux_count: u16, + /// The name for the first [`elf::Verdaux`] entry. + pub name: StringId, +} + +/// Information required for writing [`elf::Verneed`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Verneed { + pub version: u16, + pub aux_count: u16, + pub file: StringId, +} + +/// Information required for writing [`elf::Vernaux`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Vernaux { + pub flags: u16, + pub index: u16, + pub name: StringId, +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/macho.rs b/chromium_crates_io/vendor/object-v0_37/src/write/macho.rs new file mode 100644 index 0000000..fabc9f4 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/macho.rs
@@ -0,0 +1,1119 @@ +use core::mem; + +use crate::endian::*; +use crate::macho; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + index: usize, + offset: usize, + address: u64, + reloc_offset: usize, + reloc_count: usize, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: usize, + str_id: Option<StringId>, +} + +/// The customizable portion of a [`macho::BuildVersionCommand`]. +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] // May want to add the tool list? +pub struct MachOBuildVersion { + /// One of the `PLATFORM_` constants (for example, + /// [`object::macho::PLATFORM_MACOS`](macho::PLATFORM_MACOS)). + pub platform: u32, + /// The minimum OS version, where `X.Y.Z` is encoded in nibbles as + /// `xxxx.yy.zz`. + pub minos: u32, + /// The SDK version as `X.Y.Z`, where `X.Y.Z` is encoded in nibbles as + /// `xxxx.yy.zz`. + pub sdk: u32, +} + +impl MachOBuildVersion { + fn cmdsize(&self) -> u32 { + // Same size for both endianness, and we don't have `ntools`. + let sz = mem::size_of::<macho::BuildVersionCommand<Endianness>>(); + debug_assert!(sz <= u32::MAX as usize); + sz as u32 + } +} + +// Public methods. +impl<'a> Object<'a> { + /// Specify the Mach-O CPU subtype. + /// + /// Requires `feature = "macho"`. + #[inline] + pub fn set_macho_cpu_subtype(&mut self, cpu_subtype: u32) { + self.macho_cpu_subtype = Some(cpu_subtype); + } + + /// Specify information for a Mach-O `LC_BUILD_VERSION` command. + /// + /// Requires `feature = "macho"`. + #[inline] + pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) { + self.macho_build_version = Some(info); + } +} + +// Private methods. +impl<'a> Object<'a> { + pub(crate) fn macho_segment_name(&self, segment: StandardSegment) -> &'static [u8] { + match segment { + StandardSegment::Text => &b"__TEXT"[..], + StandardSegment::Data => &b"__DATA"[..], + StandardSegment::Debug => &b"__DWARF"[..], + } + } + + pub(crate) fn macho_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { + match section { + StandardSection::Text => ( + &b"__TEXT"[..], + &b"__text"[..], + SectionKind::Text, + SectionFlags::None, + ), + StandardSection::Data => ( + &b"__DATA"[..], + &b"__data"[..], + SectionKind::Data, + SectionFlags::None, + ), + StandardSection::ReadOnlyData => ( + &b"__TEXT"[..], + &b"__const"[..], + SectionKind::ReadOnlyData, + SectionFlags::None, + ), + StandardSection::ReadOnlyDataWithRel => ( + &b"__DATA"[..], + &b"__const"[..], + SectionKind::ReadOnlyDataWithRel, + SectionFlags::None, + ), + StandardSection::ReadOnlyString => ( + &b"__TEXT"[..], + &b"__cstring"[..], + SectionKind::ReadOnlyString, + SectionFlags::None, + ), + StandardSection::UninitializedData => ( + &b"__DATA"[..], + &b"__bss"[..], + SectionKind::UninitializedData, + SectionFlags::None, + ), + StandardSection::Tls => ( + &b"__DATA"[..], + &b"__thread_data"[..], + SectionKind::Tls, + SectionFlags::None, + ), + StandardSection::UninitializedTls => ( + &b"__DATA"[..], + &b"__thread_bss"[..], + SectionKind::UninitializedTls, + SectionFlags::None, + ), + StandardSection::TlsVariables => ( + &b"__DATA"[..], + &b"__thread_vars"[..], + SectionKind::TlsVariables, + SectionFlags::None, + ), + StandardSection::Common => ( + &b"__DATA"[..], + &b"__common"[..], + SectionKind::Common, + SectionFlags::None, + ), + StandardSection::GnuProperty => { + // Unsupported section. + (&[], &[], SectionKind::Note, SectionFlags::None) + } + } + } + + pub(crate) fn macho_section_flags(&self, section: &Section<'_>) -> SectionFlags { + let flags = match section.kind { + SectionKind::Text => macho::S_ATTR_PURE_INSTRUCTIONS | macho::S_ATTR_SOME_INSTRUCTIONS, + SectionKind::Data => 0, + SectionKind::ReadOnlyData | SectionKind::ReadOnlyDataWithRel => 0, + SectionKind::ReadOnlyString => macho::S_CSTRING_LITERALS, + SectionKind::UninitializedData | SectionKind::Common => macho::S_ZEROFILL, + SectionKind::Tls => macho::S_THREAD_LOCAL_REGULAR, + SectionKind::UninitializedTls => macho::S_THREAD_LOCAL_ZEROFILL, + SectionKind::TlsVariables => macho::S_THREAD_LOCAL_VARIABLES, + SectionKind::Debug | SectionKind::DebugString => macho::S_ATTR_DEBUG, + SectionKind::OtherString => macho::S_CSTRING_LITERALS, + SectionKind::Other | SectionKind::Linker | SectionKind::Metadata => 0, + SectionKind::Note | SectionKind::Unknown | SectionKind::Elf(_) => { + return SectionFlags::None; + } + }; + SectionFlags::MachO { flags } + } + + pub(crate) fn macho_symbol_flags(&self, symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> { + let mut n_desc = 0; + if symbol.weak { + if symbol.is_undefined() { + n_desc |= macho::N_WEAK_REF; + } else { + n_desc |= macho::N_WEAK_DEF; + } + } + // TODO: include n_type + SymbolFlags::MachO { n_desc } + } + + fn macho_tlv_bootstrap(&mut self) -> SymbolId { + match self.tlv_bootstrap { + Some(id) => id, + None => { + let id = self.add_symbol(Symbol { + name: b"_tlv_bootstrap".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + self.tlv_bootstrap = Some(id); + id + } + } + } + + /// Create the `__thread_vars` entry for a TLS variable. + /// + /// The symbol given by `symbol_id` will be updated to point to this entry. + /// + /// A new `SymbolId` will be returned. The caller must update this symbol + /// to point to the initializer. + /// + /// If `symbol_id` is not for a TLS variable, then it is returned unchanged. + pub(crate) fn macho_add_thread_var(&mut self, symbol_id: SymbolId) -> SymbolId { + let symbol = self.symbol_mut(symbol_id); + if symbol.kind != SymbolKind::Tls { + return symbol_id; + } + + // Create the initializer symbol. + let mut name = symbol.name.clone(); + name.extend_from_slice(b"$tlv$init"); + let init_symbol_id = self.add_raw_symbol(Symbol { + name, + value: 0, + size: 0, + kind: SymbolKind::Tls, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + + // Add the tlv entry. + // Three pointers in size: + // - __tlv_bootstrap - used to make sure support exists + // - spare pointer - used when mapped by the runtime + // - pointer to symbol initializer + let section = self.section_id(StandardSection::TlsVariables); + let address_size = self.architecture.address_size().unwrap().bytes(); + let size = u64::from(address_size) * 3; + let data = vec![0; size as usize]; + let offset = self.append_section_data(section, &data, u64::from(address_size)); + + let tlv_bootstrap = self.macho_tlv_bootstrap(); + self.add_relocation( + section, + Relocation { + offset, + symbol: tlv_bootstrap, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: address_size * 8, + }, + }, + ) + .unwrap(); + self.add_relocation( + section, + Relocation { + offset: offset + u64::from(address_size) * 2, + symbol: init_symbol_id, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: address_size * 8, + }, + }, + ) + .unwrap(); + + // Update the symbol to point to the tlv. + let symbol = self.symbol_mut(symbol_id); + symbol.value = offset; + symbol.size = size; + symbol.section = SymbolSection::Section(section); + + init_symbol_id + } + + pub(crate) fn macho_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { + use RelocationEncoding as E; + use RelocationKind as K; + + let (kind, encoding, mut size) = if let RelocationFlags::Generic { + kind, + encoding, + size, + } = reloc.flags + { + (kind, encoding, size) + } else { + return Ok(()); + }; + // Aarch64 relocs of these sizes act as if they are double-word length + if self.architecture == Architecture::Aarch64 && matches!(size, 12 | 21 | 26) { + size = 32; + } + let r_length = match size { + 8 => 0, + 16 => 1, + 32 => 2, + 64 => 3, + _ => return Err(Error(format!("unimplemented reloc size {:?}", reloc))), + }; + let unsupported_reloc = || Err(Error(format!("unimplemented relocation {:?}", reloc))); + let (r_pcrel, r_type) = match self.architecture { + Architecture::I386 => match kind { + K::Absolute => (false, macho::GENERIC_RELOC_VANILLA), + _ => return unsupported_reloc(), + }, + Architecture::Arm => match kind { + K::Absolute => (false, macho::ARM_RELOC_VANILLA), + _ => return unsupported_reloc(), + }, + Architecture::X86_64 => match (kind, encoding) { + (K::Absolute, E::Generic) => (false, macho::X86_64_RELOC_UNSIGNED), + (K::Relative, E::Generic) => (true, macho::X86_64_RELOC_SIGNED), + (K::Relative, E::X86RipRelative) => (true, macho::X86_64_RELOC_SIGNED), + (K::Relative, E::X86Branch) => (true, macho::X86_64_RELOC_BRANCH), + (K::PltRelative, E::X86Branch) => (true, macho::X86_64_RELOC_BRANCH), + (K::GotRelative, E::Generic) => (true, macho::X86_64_RELOC_GOT), + (K::GotRelative, E::X86RipRelativeMovq) => (true, macho::X86_64_RELOC_GOT_LOAD), + _ => return unsupported_reloc(), + }, + Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => match (kind, encoding) { + (K::Absolute, E::Generic) => (false, macho::ARM64_RELOC_UNSIGNED), + (K::Relative, E::AArch64Call) => (true, macho::ARM64_RELOC_BRANCH26), + _ => return unsupported_reloc(), + }, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }; + reloc.flags = RelocationFlags::MachO { + r_type, + r_pcrel, + r_length, + }; + Ok(()) + } + + pub(crate) fn macho_adjust_addend(&mut self, relocation: &mut Relocation) -> Result<bool> { + let (r_type, r_pcrel) = if let RelocationFlags::MachO { + r_type, r_pcrel, .. + } = relocation.flags + { + (r_type, r_pcrel) + } else { + return Err(Error(format!("invalid relocation flags {:?}", relocation))); + }; + if r_pcrel { + // For PC relative relocations on some architectures, the + // addend does not include the offset required due to the + // PC being different from the place of the relocation. + // This differs from other file formats, so adjust the + // addend here to account for this. + let pcrel_offset = match self.architecture { + Architecture::I386 => 4, + Architecture::X86_64 => match r_type { + macho::X86_64_RELOC_SIGNED_1 => 5, + macho::X86_64_RELOC_SIGNED_2 => 6, + macho::X86_64_RELOC_SIGNED_4 => 8, + _ => 4, + }, + // TODO: maybe missing support for some architectures and relocations + _ => 0, + }; + relocation.addend += pcrel_offset; + } + // Determine if addend is implicit. + let implicit = if self.architecture == Architecture::Aarch64 { + match r_type { + macho::ARM64_RELOC_BRANCH26 + | macho::ARM64_RELOC_PAGE21 + | macho::ARM64_RELOC_PAGEOFF12 => false, + _ => true, + } + } else { + true + }; + Ok(implicit) + } + + pub(crate) fn macho_relocation_size(&self, reloc: &Relocation) -> Result<u8> { + if let RelocationFlags::MachO { r_length, .. } = reloc.flags { + Ok(8 << r_length) + } else { + Err(Error("invalid relocation flags".into())) + } + } + + pub(crate) fn macho_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { + let address_size = self.architecture.address_size().unwrap(); + let endian = self.endian; + let macho32 = MachO32 { endian }; + let macho64 = MachO64 { endian }; + let macho: &dyn MachO = match address_size { + AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => &macho32, + AddressSize::U64 => &macho64, + }; + let pointer_align = address_size.bytes() as usize; + + // Calculate offsets of everything, and build strtab. + let mut offset = 0; + + // Calculate size of Mach-O header. + offset += macho.mach_header_size(); + + // Calculate size of commands. + let mut ncmds = 0; + let command_offset = offset; + + // Calculate size of segment command and section headers. + let segment_command_offset = offset; + let segment_command_len = + macho.segment_command_size() + self.sections.len() * macho.section_header_size(); + offset += segment_command_len; + ncmds += 1; + + // Calculate size of build version. + let build_version_offset = offset; + if let Some(version) = &self.macho_build_version { + offset += version.cmdsize() as usize; + ncmds += 1; + } + + // Calculate size of symtab command. + let symtab_command_offset = offset; + let symtab_command_len = mem::size_of::<macho::SymtabCommand<Endianness>>(); + offset += symtab_command_len; + ncmds += 1; + + // Calculate size of dysymtab command. + let dysymtab_command_offset = offset; + let dysymtab_command_len = mem::size_of::<macho::DysymtabCommand<Endianness>>(); + offset += dysymtab_command_len; + ncmds += 1; + + let sizeofcmds = offset - command_offset; + + // Calculate size of section data. + // Section data can immediately follow the load commands without any alignment padding. + let segment_file_offset = offset; + let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; + let mut address = 0; + for (index, section) in self.sections.iter().enumerate() { + section_offsets[index].index = 1 + index; + if !section.is_bss() { + address = align_u64(address, section.align); + section_offsets[index].address = address; + section_offsets[index].offset = segment_file_offset + address as usize; + address += section.size; + } + } + let segment_file_size = address as usize; + offset += address as usize; + for (index, section) in self.sections.iter().enumerate() { + if section.is_bss() { + debug_assert!(section.data.is_empty()); + address = align_u64(address, section.align); + section_offsets[index].address = address; + address += section.size; + } + } + + // Partition symbols and add symbol strings to strtab. + let mut strtab = StringTable::default(); + let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; + let mut local_symbols = vec![]; + let mut external_symbols = vec![]; + let mut undefined_symbols = vec![]; + for (index, symbol) in self.symbols.iter().enumerate() { + // The unified API allows creating symbols that we don't emit, so filter + // them out here. + // + // Since we don't actually emit the symbol kind, we validate it here too. + match symbol.kind { + SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls | SymbolKind::Unknown => {} + SymbolKind::File | SymbolKind::Section => continue, + SymbolKind::Label => { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))); + } + } + if !symbol.name.is_empty() { + symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); + } + if symbol.is_undefined() { + undefined_symbols.push(index); + } else if symbol.is_local() { + local_symbols.push(index); + } else { + external_symbols.push(index); + } + } + + external_symbols.sort_by_key(|index| &*self.symbols[*index].name); + undefined_symbols.sort_by_key(|index| &*self.symbols[*index].name); + + // Count symbols. + let mut nsyms = 0; + for index in local_symbols + .iter() + .copied() + .chain(external_symbols.iter().copied()) + .chain(undefined_symbols.iter().copied()) + { + symbol_offsets[index].index = nsyms; + nsyms += 1; + } + + // Calculate size of relocations. + for (index, section) in self.sections.iter().enumerate() { + let count: usize = section + .relocations + .iter() + .map(|reloc| 1 + usize::from(reloc.addend != 0)) + .sum(); + if count != 0 { + offset = align(offset, pointer_align); + section_offsets[index].reloc_offset = offset; + section_offsets[index].reloc_count = count; + let len = count * mem::size_of::<macho::Relocation<Endianness>>(); + offset += len; + } + } + + // Calculate size of symtab. + offset = align(offset, pointer_align); + let symtab_offset = offset; + let symtab_len = nsyms * macho.nlist_size(); + offset += symtab_len; + + // Calculate size of strtab. + let strtab_offset = offset; + // Start with null name. + let mut strtab_data = vec![0]; + strtab.write(1, &mut strtab_data); + write_align(&mut strtab_data, pointer_align); + offset += strtab_data.len(); + + // Start writing. + buffer + .reserve(offset) + .map_err(|_| Error(String::from("Cannot allocate buffer")))?; + + // Write file header. + let (cputype, mut cpusubtype) = match (self.architecture, self.sub_architecture) { + (Architecture::Arm, None) => (macho::CPU_TYPE_ARM, macho::CPU_SUBTYPE_ARM_ALL), + (Architecture::Aarch64, None) => (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64_ALL), + (Architecture::Aarch64, Some(SubArchitecture::Arm64E)) => { + (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E) + } + (Architecture::Aarch64_Ilp32, None) => { + (macho::CPU_TYPE_ARM64_32, macho::CPU_SUBTYPE_ARM64_32_V8) + } + (Architecture::I386, None) => (macho::CPU_TYPE_X86, macho::CPU_SUBTYPE_I386_ALL), + (Architecture::X86_64, None) => (macho::CPU_TYPE_X86_64, macho::CPU_SUBTYPE_X86_64_ALL), + (Architecture::PowerPc, None) => { + (macho::CPU_TYPE_POWERPC, macho::CPU_SUBTYPE_POWERPC_ALL) + } + (Architecture::PowerPc64, None) => { + (macho::CPU_TYPE_POWERPC64, macho::CPU_SUBTYPE_POWERPC_ALL) + } + _ => { + return Err(Error(format!( + "unimplemented architecture {:?} with sub-architecture {:?}", + self.architecture, self.sub_architecture + ))); + } + }; + + if let Some(cpu_subtype) = self.macho_cpu_subtype { + cpusubtype = cpu_subtype; + } + + let mut flags = match self.flags { + FileFlags::MachO { flags } => flags, + _ => 0, + }; + if self.macho_subsections_via_symbols { + flags |= macho::MH_SUBSECTIONS_VIA_SYMBOLS; + } + macho.write_mach_header( + buffer, + MachHeader { + cputype, + cpusubtype, + filetype: macho::MH_OBJECT, + ncmds, + sizeofcmds: sizeofcmds as u32, + flags, + }, + ); + + // Write segment command. + debug_assert_eq!(segment_command_offset, buffer.len()); + macho.write_segment_command( + buffer, + SegmentCommand { + cmdsize: segment_command_len as u32, + segname: [0; 16], + vmaddr: 0, + vmsize: address, + fileoff: segment_file_offset as u64, + filesize: segment_file_size as u64, + maxprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE, + initprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE, + nsects: self.sections.len() as u32, + flags: 0, + }, + ); + + // Write section headers. + for (index, section) in self.sections.iter().enumerate() { + let mut sectname = [0; 16]; + sectname + .get_mut(..section.name.len()) + .ok_or_else(|| { + Error(format!( + "section name `{}` is too long", + section.name().unwrap_or(""), + )) + })? + .copy_from_slice(§ion.name); + let mut segname = [0; 16]; + segname + .get_mut(..section.segment.len()) + .ok_or_else(|| { + Error(format!( + "segment name `{}` is too long", + section.segment().unwrap_or(""), + )) + })? + .copy_from_slice(§ion.segment); + let SectionFlags::MachO { flags } = self.section_flags(section) else { + return Err(Error(format!( + "unimplemented section `{}` kind {:?}", + section.name().unwrap_or(""), + section.kind + ))); + }; + macho.write_section( + buffer, + SectionHeader { + sectname, + segname, + addr: section_offsets[index].address, + size: section.size, + offset: section_offsets[index].offset as u32, + align: section.align.trailing_zeros(), + reloff: section_offsets[index].reloc_offset as u32, + nreloc: section_offsets[index].reloc_count as u32, + flags, + }, + ); + } + + // Write build version. + if let Some(version) = &self.macho_build_version { + debug_assert_eq!(build_version_offset, buffer.len()); + buffer.write(&macho::BuildVersionCommand { + cmd: U32::new(endian, macho::LC_BUILD_VERSION), + cmdsize: U32::new(endian, version.cmdsize()), + platform: U32::new(endian, version.platform), + minos: U32::new(endian, version.minos), + sdk: U32::new(endian, version.sdk), + ntools: U32::new(endian, 0), + }); + } + + // Write symtab command. + debug_assert_eq!(symtab_command_offset, buffer.len()); + let symtab_command = macho::SymtabCommand { + cmd: U32::new(endian, macho::LC_SYMTAB), + cmdsize: U32::new(endian, symtab_command_len as u32), + symoff: U32::new(endian, symtab_offset as u32), + nsyms: U32::new(endian, nsyms as u32), + stroff: U32::new(endian, strtab_offset as u32), + strsize: U32::new(endian, strtab_data.len() as u32), + }; + buffer.write(&symtab_command); + + // Write dysymtab command. + debug_assert_eq!(dysymtab_command_offset, buffer.len()); + let dysymtab_command = macho::DysymtabCommand { + cmd: U32::new(endian, macho::LC_DYSYMTAB), + cmdsize: U32::new(endian, dysymtab_command_len as u32), + ilocalsym: U32::new(endian, 0), + nlocalsym: U32::new(endian, local_symbols.len() as u32), + iextdefsym: U32::new(endian, local_symbols.len() as u32), + nextdefsym: U32::new(endian, external_symbols.len() as u32), + iundefsym: U32::new( + endian, + local_symbols.len() as u32 + external_symbols.len() as u32, + ), + nundefsym: U32::new(endian, undefined_symbols.len() as u32), + tocoff: U32::default(), + ntoc: U32::default(), + modtaboff: U32::default(), + nmodtab: U32::default(), + extrefsymoff: U32::default(), + nextrefsyms: U32::default(), + indirectsymoff: U32::default(), + nindirectsyms: U32::default(), + extreloff: U32::default(), + nextrel: U32::default(), + locreloff: U32::default(), + nlocrel: U32::default(), + }; + buffer.write(&dysymtab_command); + + // Write section data. + for (index, section) in self.sections.iter().enumerate() { + if !section.is_bss() { + buffer.resize(section_offsets[index].offset); + buffer.write_bytes(§ion.data); + } + } + debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len()); + + // Write relocations. + for (index, section) in self.sections.iter().enumerate() { + if !section.relocations.is_empty() { + write_align(buffer, pointer_align); + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + + let mut write_reloc = |reloc: &Relocation| { + let (r_type, r_pcrel, r_length) = if let RelocationFlags::MachO { + r_type, + r_pcrel, + r_length, + } = reloc.flags + { + (r_type, r_pcrel, r_length) + } else { + return Err(Error("invalid relocation flags".into())); + }; + + // Write explicit addend. + if reloc.addend != 0 { + let r_type = match self.architecture { + Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => { + macho::ARM64_RELOC_ADDEND + } + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))) + } + }; + + let reloc_info = macho::RelocationInfo { + r_address: reloc.offset as u32, + r_symbolnum: reloc.addend as u32, + r_pcrel: false, + r_length, + r_extern: false, + r_type, + }; + buffer.write(&reloc_info.relocation(endian)); + } + + let r_extern; + let r_symbolnum; + let symbol = &self.symbols[reloc.symbol.0]; + if symbol.kind == SymbolKind::Section { + r_symbolnum = section_offsets[symbol.section.id().unwrap().0].index as u32; + r_extern = false; + } else { + r_symbolnum = symbol_offsets[reloc.symbol.0].index as u32; + r_extern = true; + } + + let reloc_info = macho::RelocationInfo { + r_address: reloc.offset as u32, + r_symbolnum, + r_pcrel, + r_length, + r_extern, + r_type, + }; + buffer.write(&reloc_info.relocation(endian)); + Ok(()) + }; + + // Relocations are emitted in descending order as otherwise Apple's + // new linker crashes. This matches LLVM's behavior too: + // https://github.com/llvm/llvm-project/blob/e9b8cd0c8/llvm/lib/MC/MachObjectWriter.cpp#L1001-L1002 + let need_reverse = |relocs: &[Relocation]| { + let Some(first) = relocs.first() else { + return false; + }; + let Some(last) = relocs.last() else { + return false; + }; + first.offset < last.offset + }; + if need_reverse(§ion.relocations) { + for reloc in section.relocations.iter().rev() { + write_reloc(reloc)?; + } + } else { + for reloc in §ion.relocations { + write_reloc(reloc)?; + } + } + } + } + + // Write symtab. + write_align(buffer, pointer_align); + debug_assert_eq!(symtab_offset, buffer.len()); + for index in local_symbols + .iter() + .copied() + .chain(external_symbols.iter().copied()) + .chain(undefined_symbols.iter().copied()) + { + let symbol = &self.symbols[index]; + // TODO: N_STAB + let (mut n_type, n_sect) = match symbol.section { + SymbolSection::Undefined => (macho::N_UNDF | macho::N_EXT, 0), + SymbolSection::Absolute => (macho::N_ABS, 0), + SymbolSection::Section(id) => (macho::N_SECT, id.0 + 1), + SymbolSection::None | SymbolSection::Common => { + return Err(Error(format!( + "unimplemented symbol `{}` section {:?}", + symbol.name().unwrap_or(""), + symbol.section + ))); + } + }; + match symbol.scope { + SymbolScope::Unknown | SymbolScope::Compilation => {} + SymbolScope::Linkage => { + n_type |= macho::N_EXT | macho::N_PEXT; + } + SymbolScope::Dynamic => { + n_type |= macho::N_EXT; + } + } + + let SymbolFlags::MachO { n_desc } = self.symbol_flags(symbol) else { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))); + }; + + let n_value = match symbol.section.id() { + Some(section) => section_offsets[section.0].address + symbol.value, + None => symbol.value, + }; + + let n_strx = symbol_offsets[index] + .str_id + .map(|id| strtab.get_offset(id)) + .unwrap_or(0); + + macho.write_nlist( + buffer, + Nlist { + n_strx: n_strx as u32, + n_type, + n_sect: n_sect as u8, + n_desc, + n_value, + }, + ); + } + + // Write strtab. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.write_bytes(&strtab_data); + + debug_assert_eq!(offset, buffer.len()); + + Ok(()) + } +} + +struct MachHeader { + cputype: u32, + cpusubtype: u32, + filetype: u32, + ncmds: u32, + sizeofcmds: u32, + flags: u32, +} + +struct SegmentCommand { + cmdsize: u32, + segname: [u8; 16], + vmaddr: u64, + vmsize: u64, + fileoff: u64, + filesize: u64, + maxprot: u32, + initprot: u32, + nsects: u32, + flags: u32, +} + +pub struct SectionHeader { + sectname: [u8; 16], + segname: [u8; 16], + addr: u64, + size: u64, + offset: u32, + align: u32, + reloff: u32, + nreloc: u32, + flags: u32, +} + +struct Nlist { + n_strx: u32, + n_type: u8, + n_sect: u8, + n_desc: u16, + n_value: u64, +} + +trait MachO { + fn mach_header_size(&self) -> usize; + fn segment_command_size(&self) -> usize; + fn section_header_size(&self) -> usize; + fn nlist_size(&self) -> usize; + fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, section: MachHeader); + fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand); + fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader); + fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist); +} + +struct MachO32<E> { + endian: E, +} + +impl<E: Endian> MachO for MachO32<E> { + fn mach_header_size(&self) -> usize { + mem::size_of::<macho::MachHeader32<E>>() + } + + fn segment_command_size(&self) -> usize { + mem::size_of::<macho::SegmentCommand32<E>>() + } + + fn section_header_size(&self) -> usize { + mem::size_of::<macho::Section32<E>>() + } + + fn nlist_size(&self) -> usize { + mem::size_of::<macho::Nlist32<E>>() + } + + fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) { + let endian = self.endian; + let magic = if endian.is_big_endian() { + macho::MH_MAGIC + } else { + macho::MH_CIGAM + }; + let header = macho::MachHeader32 { + magic: U32::new(BigEndian, magic), + cputype: U32::new(endian, header.cputype), + cpusubtype: U32::new(endian, header.cpusubtype), + filetype: U32::new(endian, header.filetype), + ncmds: U32::new(endian, header.ncmds), + sizeofcmds: U32::new(endian, header.sizeofcmds), + flags: U32::new(endian, header.flags), + }; + buffer.write(&header); + } + + fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) { + let endian = self.endian; + let segment = macho::SegmentCommand32 { + cmd: U32::new(endian, macho::LC_SEGMENT), + cmdsize: U32::new(endian, segment.cmdsize), + segname: segment.segname, + vmaddr: U32::new(endian, segment.vmaddr as u32), + vmsize: U32::new(endian, segment.vmsize as u32), + fileoff: U32::new(endian, segment.fileoff as u32), + filesize: U32::new(endian, segment.filesize as u32), + maxprot: U32::new(endian, segment.maxprot), + initprot: U32::new(endian, segment.initprot), + nsects: U32::new(endian, segment.nsects), + flags: U32::new(endian, segment.flags), + }; + buffer.write(&segment); + } + + fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) { + let endian = self.endian; + let section = macho::Section32 { + sectname: section.sectname, + segname: section.segname, + addr: U32::new(endian, section.addr as u32), + size: U32::new(endian, section.size as u32), + offset: U32::new(endian, section.offset), + align: U32::new(endian, section.align), + reloff: U32::new(endian, section.reloff), + nreloc: U32::new(endian, section.nreloc), + flags: U32::new(endian, section.flags), + reserved1: U32::default(), + reserved2: U32::default(), + }; + buffer.write(§ion); + } + + fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) { + let endian = self.endian; + let nlist = macho::Nlist32 { + n_strx: U32::new(endian, nlist.n_strx), + n_type: nlist.n_type, + n_sect: nlist.n_sect, + n_desc: U16::new(endian, nlist.n_desc), + n_value: U32::new(endian, nlist.n_value as u32), + }; + buffer.write(&nlist); + } +} + +struct MachO64<E> { + endian: E, +} + +impl<E: Endian> MachO for MachO64<E> { + fn mach_header_size(&self) -> usize { + mem::size_of::<macho::MachHeader64<E>>() + } + + fn segment_command_size(&self) -> usize { + mem::size_of::<macho::SegmentCommand64<E>>() + } + + fn section_header_size(&self) -> usize { + mem::size_of::<macho::Section64<E>>() + } + + fn nlist_size(&self) -> usize { + mem::size_of::<macho::Nlist64<E>>() + } + + fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) { + let endian = self.endian; + let magic = if endian.is_big_endian() { + macho::MH_MAGIC_64 + } else { + macho::MH_CIGAM_64 + }; + let header = macho::MachHeader64 { + magic: U32::new(BigEndian, magic), + cputype: U32::new(endian, header.cputype), + cpusubtype: U32::new(endian, header.cpusubtype), + filetype: U32::new(endian, header.filetype), + ncmds: U32::new(endian, header.ncmds), + sizeofcmds: U32::new(endian, header.sizeofcmds), + flags: U32::new(endian, header.flags), + reserved: U32::default(), + }; + buffer.write(&header); + } + + fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) { + let endian = self.endian; + let segment = macho::SegmentCommand64 { + cmd: U32::new(endian, macho::LC_SEGMENT_64), + cmdsize: U32::new(endian, segment.cmdsize), + segname: segment.segname, + vmaddr: U64::new(endian, segment.vmaddr), + vmsize: U64::new(endian, segment.vmsize), + fileoff: U64::new(endian, segment.fileoff), + filesize: U64::new(endian, segment.filesize), + maxprot: U32::new(endian, segment.maxprot), + initprot: U32::new(endian, segment.initprot), + nsects: U32::new(endian, segment.nsects), + flags: U32::new(endian, segment.flags), + }; + buffer.write(&segment); + } + + fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) { + let endian = self.endian; + let section = macho::Section64 { + sectname: section.sectname, + segname: section.segname, + addr: U64::new(endian, section.addr), + size: U64::new(endian, section.size), + offset: U32::new(endian, section.offset), + align: U32::new(endian, section.align), + reloff: U32::new(endian, section.reloff), + nreloc: U32::new(endian, section.nreloc), + flags: U32::new(endian, section.flags), + reserved1: U32::default(), + reserved2: U32::default(), + reserved3: U32::default(), + }; + buffer.write(§ion); + } + + fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) { + let endian = self.endian; + let nlist = macho::Nlist64 { + n_strx: U32::new(endian, nlist.n_strx), + n_type: nlist.n_type, + n_sect: nlist.n_sect, + n_desc: U16::new(endian, nlist.n_desc), + n_value: U64Bytes::new(endian, nlist.n_value), + }; + buffer.write(&nlist); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/mod.rs b/chromium_crates_io/vendor/object-v0_37/src/write/mod.rs new file mode 100644 index 0000000..fa2f7b0 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/mod.rs
@@ -0,0 +1,1140 @@ +//! Interface for writing object files. +//! +//! This module provides a unified write API for relocatable object files +//! using [`Object`]. This does not support writing executable files. +//! This supports the following file formats: COFF, ELF, Mach-O, and XCOFF. +//! +//! The submodules define helpers for writing the raw structs. These support +//! writing both relocatable and executable files. There are writers for +//! the following file formats: [COFF](coff::Writer), [ELF](elf::Writer), +//! and [PE](pe::Writer). + +use alloc::borrow::Cow; +use alloc::string::String; +use alloc::vec::Vec; +use core::{fmt, result, str}; +#[cfg(not(feature = "std"))] +use hashbrown::HashMap; +#[cfg(feature = "std")] +use std::{boxed::Box, collections::HashMap, error, io}; + +use crate::endian::{Endianness, U32, U64}; + +pub use crate::common::*; + +#[cfg(feature = "coff")] +pub mod coff; +#[cfg(feature = "coff")] +pub use coff::CoffExportStyle; + +#[cfg(feature = "elf")] +pub mod elf; + +#[cfg(feature = "macho")] +mod macho; +#[cfg(feature = "macho")] +pub use macho::MachOBuildVersion; + +#[cfg(feature = "pe")] +pub mod pe; + +#[cfg(feature = "xcoff")] +mod xcoff; + +pub(crate) mod string; +pub use string::StringId; + +mod util; +pub use util::*; + +/// The error type used within the write module. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Error(pub(crate) String); + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +#[cfg(feature = "std")] +impl error::Error for Error {} +#[cfg(all(not(feature = "std"), core_error))] +impl core::error::Error for Error {} + +/// The result type used within the write module. +pub type Result<T> = result::Result<T, Error>; + +/// A writable relocatable object file. +#[derive(Debug)] +pub struct Object<'a> { + format: BinaryFormat, + architecture: Architecture, + sub_architecture: Option<SubArchitecture>, + endian: Endianness, + sections: Vec<Section<'a>>, + standard_sections: HashMap<StandardSection, SectionId>, + symbols: Vec<Symbol>, + symbol_map: HashMap<Vec<u8>, SymbolId>, + comdats: Vec<Comdat>, + /// File flags that are specific to each file format. + pub flags: FileFlags, + /// The symbol name mangling scheme. + pub mangling: Mangling, + #[cfg(feature = "coff")] + stub_symbols: HashMap<SymbolId, SymbolId>, + /// Mach-O "_tlv_bootstrap" symbol. + #[cfg(feature = "macho")] + tlv_bootstrap: Option<SymbolId>, + /// Mach-O CPU subtype. + #[cfg(feature = "macho")] + macho_cpu_subtype: Option<u32>, + #[cfg(feature = "macho")] + macho_build_version: Option<MachOBuildVersion>, + /// Mach-O MH_SUBSECTIONS_VIA_SYMBOLS flag. Only ever set if format is Mach-O. + #[cfg(feature = "macho")] + macho_subsections_via_symbols: bool, +} + +impl<'a> Object<'a> { + /// Create an empty object file. + pub fn new(format: BinaryFormat, architecture: Architecture, endian: Endianness) -> Object<'a> { + Object { + format, + architecture, + sub_architecture: None, + endian, + sections: Vec::new(), + standard_sections: HashMap::new(), + symbols: Vec::new(), + symbol_map: HashMap::new(), + comdats: Vec::new(), + flags: FileFlags::None, + mangling: Mangling::default(format, architecture), + #[cfg(feature = "coff")] + stub_symbols: HashMap::new(), + #[cfg(feature = "macho")] + tlv_bootstrap: None, + #[cfg(feature = "macho")] + macho_cpu_subtype: None, + #[cfg(feature = "macho")] + macho_build_version: None, + #[cfg(feature = "macho")] + macho_subsections_via_symbols: false, + } + } + + /// Return the file format. + #[inline] + pub fn format(&self) -> BinaryFormat { + self.format + } + + /// Return the architecture. + #[inline] + pub fn architecture(&self) -> Architecture { + self.architecture + } + + /// Return the sub-architecture. + #[inline] + pub fn sub_architecture(&self) -> Option<SubArchitecture> { + self.sub_architecture + } + + /// Specify the sub-architecture. + pub fn set_sub_architecture(&mut self, sub_architecture: Option<SubArchitecture>) { + self.sub_architecture = sub_architecture; + } + + /// Return the current mangling setting. + #[inline] + pub fn mangling(&self) -> Mangling { + self.mangling + } + + /// Specify the mangling setting. + #[inline] + pub fn set_mangling(&mut self, mangling: Mangling) { + self.mangling = mangling; + } + + /// Return the name for a standard segment. + /// + /// This will vary based on the file format. + #[allow(unused_variables)] + pub fn segment_name(&self, segment: StandardSegment) -> &'static [u8] { + match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => &[], + #[cfg(feature = "elf")] + BinaryFormat::Elf => &[], + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_segment_name(segment), + _ => unimplemented!(), + } + } + + /// Get the section with the given `SectionId`. + #[inline] + pub fn section(&self, section: SectionId) -> &Section<'a> { + &self.sections[section.0] + } + + /// Mutably get the section with the given `SectionId`. + #[inline] + pub fn section_mut(&mut self, section: SectionId) -> &mut Section<'a> { + &mut self.sections[section.0] + } + + /// Set the data for an existing section. + /// + /// Must not be called for sections that already have data, or that contain uninitialized data. + /// `align` must be a power of two. + pub fn set_section_data<T>(&mut self, section: SectionId, data: T, align: u64) + where + T: Into<Cow<'a, [u8]>>, + { + self.sections[section.0].set_data(data, align) + } + + /// Append data to an existing section. Returns the section offset of the data. + /// + /// Must not be called for sections that contain uninitialized data. + /// `align` must be a power of two. + pub fn append_section_data(&mut self, section: SectionId, data: &[u8], align: u64) -> u64 { + self.sections[section.0].append_data(data, align) + } + + /// Append zero-initialized data to an existing section. Returns the section offset of the data. + /// + /// Must not be called for sections that contain initialized data. + /// `align` must be a power of two. + pub fn append_section_bss(&mut self, section: SectionId, size: u64, align: u64) -> u64 { + self.sections[section.0].append_bss(size, align) + } + + /// Return the `SectionId` of a standard section. + /// + /// If the section doesn't already exist then it is created. + pub fn section_id(&mut self, section: StandardSection) -> SectionId { + self.standard_sections + .get(§ion) + .cloned() + .unwrap_or_else(|| { + let (segment, name, kind, flags) = self.section_info(section); + let id = self.add_section(segment.to_vec(), name.to_vec(), kind); + self.section_mut(id).flags = flags; + id + }) + } + + /// Add a new section and return its `SectionId`. + /// + /// This also creates a section symbol. + pub fn add_section(&mut self, segment: Vec<u8>, name: Vec<u8>, kind: SectionKind) -> SectionId { + let id = SectionId(self.sections.len()); + self.sections.push(Section { + segment, + name, + kind, + size: 0, + align: 1, + data: Cow::Borrowed(&[]), + relocations: Vec::new(), + symbol: None, + flags: SectionFlags::None, + }); + + // Add to self.standard_sections if required. This may match multiple standard sections. + let section = &self.sections[id.0]; + for standard_section in StandardSection::all() { + if !self.standard_sections.contains_key(standard_section) { + let (segment, name, kind, _flags) = self.section_info(*standard_section); + if segment == &*section.segment && name == &*section.name && kind == section.kind { + self.standard_sections.insert(*standard_section, id); + } + } + } + + id + } + + fn section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { + match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_section_info(section), + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_section_info(section), + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_section_info(section), + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_section_info(section), + _ => unimplemented!(), + } + } + + /// Add a subsection. Returns the `SectionId` and section offset of the data. + /// + /// For Mach-O, this does not create a subsection, and instead uses the + /// section from [`Self::section_id`]. Use [`Self::set_subsections_via_symbols`] + /// to enable subsections via symbols. + pub fn add_subsection(&mut self, section: StandardSection, name: &[u8]) -> SectionId { + if self.has_subsections_via_symbols() { + self.section_id(section) + } else { + let (segment, name, kind, flags) = self.subsection_info(section, name); + let id = self.add_section(segment.to_vec(), name, kind); + self.section_mut(id).flags = flags; + id + } + } + + fn has_subsections_via_symbols(&self) -> bool { + self.format == BinaryFormat::MachO + } + + /// Enable subsections via symbols if supported. + /// + /// This should be called before adding any subsections or symbols. + /// + /// For Mach-O, this sets the `MH_SUBSECTIONS_VIA_SYMBOLS` flag. + /// For other formats, this does nothing. + pub fn set_subsections_via_symbols(&mut self) { + #[cfg(feature = "macho")] + if self.format == BinaryFormat::MachO { + self.macho_subsections_via_symbols = true; + } + } + + fn subsection_info( + &self, + section: StandardSection, + value: &[u8], + ) -> (&'static [u8], Vec<u8>, SectionKind, SectionFlags) { + let (segment, section, kind, flags) = self.section_info(section); + let name = self.subsection_name(section, value); + (segment, name, kind, flags) + } + + #[allow(unused_variables)] + fn subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + debug_assert!(!self.has_subsections_via_symbols()); + match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_subsection_name(section, value), + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_subsection_name(section, value), + _ => unimplemented!(), + } + } + + /// Return the default flags for a section. + /// + /// The default flags are the section flags that will be written if + /// the section flags are set to `SectionFlags::None`. + /// These flags are determined by the file format and fields in the section + /// such as the section kind. + /// + /// This may return `SectionFlags::None` if the file format does not support + /// the section kind. + pub fn default_section_flags(&self, section: &Section<'_>) -> SectionFlags { + match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_section_flags(section), + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_section_flags(section), + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_section_flags(section), + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_section_flags(section), + _ => SectionFlags::None, + } + } + + /// Return the flags for a section. + /// + /// If `section.flags` is `SectionFlags::None`, then returns + /// [`Self::default_section_flags`]. + /// Otherwise, `section.flags` is returned as is. + pub fn section_flags(&self, section: &Section<'_>) -> SectionFlags { + if section.flags != SectionFlags::None { + section.flags + } else { + self.default_section_flags(section) + } + } + + /// Mutably get the flags for a section. + /// + /// If `section.flags` is `SectionFlags::None`, then replace it with + /// [`Self::default_section_flags`] first. + /// Otherwise, `&mut section.flags` is returned as is. + pub fn section_flags_mut(&mut self, section_id: SectionId) -> &mut SectionFlags { + if self.section(section_id).flags != SectionFlags::None { + &mut self.section_mut(section_id).flags + } else { + let flags = self.default_section_flags(self.section(section_id)); + let section = self.section_mut(section_id); + section.flags = flags; + &mut section.flags + } + } + + /// Get the COMDAT section group with the given `ComdatId`. + #[inline] + pub fn comdat(&self, comdat: ComdatId) -> &Comdat { + &self.comdats[comdat.0] + } + + /// Mutably get the COMDAT section group with the given `ComdatId`. + #[inline] + pub fn comdat_mut(&mut self, comdat: ComdatId) -> &mut Comdat { + &mut self.comdats[comdat.0] + } + + /// Add a new COMDAT section group and return its `ComdatId`. + pub fn add_comdat(&mut self, comdat: Comdat) -> ComdatId { + let comdat_id = ComdatId(self.comdats.len()); + self.comdats.push(comdat); + comdat_id + } + + /// Get the `SymbolId` of the symbol with the given name. + pub fn symbol_id(&self, name: &[u8]) -> Option<SymbolId> { + self.symbol_map.get(name).cloned() + } + + /// Get the symbol with the given `SymbolId`. + #[inline] + pub fn symbol(&self, symbol: SymbolId) -> &Symbol { + &self.symbols[symbol.0] + } + + /// Mutably get the symbol with the given `SymbolId`. + #[inline] + pub fn symbol_mut(&mut self, symbol: SymbolId) -> &mut Symbol { + &mut self.symbols[symbol.0] + } + + /// Add a new symbol and return its `SymbolId`. + /// + /// If the symbol is a section symbol that is already defined, + /// it will update the flags of the existing section symbol + /// instead of creating adding a new symbol. + /// + /// The symbol name will be modified to include the global prefix + /// if the mangling scheme has one. + pub fn add_symbol(&mut self, mut symbol: Symbol) -> SymbolId { + // Defined symbols must have a scope. + debug_assert!(symbol.is_undefined() || symbol.scope != SymbolScope::Unknown); + if symbol.kind == SymbolKind::Section { + // There can only be one section symbol, but update its flags, since + // the automatically generated section symbol will have none. + let symbol_id = self.section_symbol(symbol.section.id().unwrap()); + if symbol.flags != SymbolFlags::None { + self.symbol_mut(symbol_id).flags = symbol.flags; + } + return symbol_id; + } + if !symbol.name.is_empty() + && (symbol.kind == SymbolKind::Text + || symbol.kind == SymbolKind::Data + || symbol.kind == SymbolKind::Tls) + { + let unmangled_name = symbol.name.clone(); + if let Some(prefix) = self.mangling.global_prefix() { + symbol.name.insert(0, prefix); + } + let symbol_id = self.add_raw_symbol(symbol); + self.symbol_map.insert(unmangled_name, symbol_id); + symbol_id + } else { + self.add_raw_symbol(symbol) + } + } + + fn add_raw_symbol(&mut self, symbol: Symbol) -> SymbolId { + let symbol_id = SymbolId(self.symbols.len()); + self.symbols.push(symbol); + symbol_id + } + + /// Return the default flags for a symbol. + /// + /// The default flags are the symbol flags that will be written if the + /// symbol flags are set to `SymbolFlags::None`. These flags are determined + /// by the file format and fields in the symbol such as the symbol kind and + /// scope. Therefore you should call this function after the symbol + /// has been fully defined. + /// + /// This may return `SymbolFlags::None` if the file format does not + /// support symbol flags, or does not support the symbol kind or scope. + pub fn default_symbol_flags(&self, symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> { + match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_symbol_flags(symbol), + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_symbol_flags(symbol), + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_symbol_flags(symbol), + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_symbol_flags(symbol), + _ => SymbolFlags::None, + } + } + + /// Return the flags for a symbol. + /// + /// If `symbol.flags` is `SymbolFlags::None`, then returns + /// [`Self::default_symbol_flags`]. + /// Otherwise, `symbol.flags` is returned as is. + pub fn symbol_flags(&self, symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> { + if symbol.flags != SymbolFlags::None { + symbol.flags + } else { + self.default_symbol_flags(symbol) + } + } + + /// Mutably get the flags for a symbol. + /// + /// If `symbol.flags` is `SymbolFlags::None`, then replace it with + /// [`Self::default_symbol_flags`]. + /// Otherwise, `&mut symbol.flags` is returned as is. + pub fn symbol_flags_mut( + &mut self, + symbol_id: SymbolId, + ) -> &mut SymbolFlags<SectionId, SymbolId> { + if self.symbol(symbol_id).flags != SymbolFlags::None { + &mut self.symbol_mut(symbol_id).flags + } else { + let flags = self.default_symbol_flags(self.symbol(symbol_id)); + let symbol = self.symbol_mut(symbol_id); + symbol.flags = flags; + &mut symbol.flags + } + } + + /// Return true if the file format supports `StandardSection::UninitializedTls`. + #[inline] + pub fn has_uninitialized_tls(&self) -> bool { + self.format != BinaryFormat::Coff + } + + /// Return true if the file format supports `StandardSection::Common`. + #[inline] + pub fn has_common(&self) -> bool { + self.format == BinaryFormat::MachO + } + + /// Add a new common symbol and return its `SymbolId`. + /// + /// For Mach-O, this appends the symbol to the `__common` section. + /// + /// `align` must be a power of two. + pub fn add_common_symbol(&mut self, mut symbol: Symbol, size: u64, align: u64) -> SymbolId { + if self.has_common() { + let symbol_id = self.add_symbol(symbol); + let section = self.section_id(StandardSection::Common); + self.add_symbol_bss(symbol_id, section, size, align); + symbol_id + } else { + symbol.section = SymbolSection::Common; + symbol.size = size; + self.add_symbol(symbol) + } + } + + /// Add a new file symbol and return its `SymbolId`. + pub fn add_file_symbol(&mut self, name: Vec<u8>) -> SymbolId { + self.add_raw_symbol(Symbol { + name, + value: 0, + size: 0, + kind: SymbolKind::File, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::None, + flags: SymbolFlags::None, + }) + } + + /// Get the symbol for a section. + pub fn section_symbol(&mut self, section_id: SectionId) -> SymbolId { + let section = &mut self.sections[section_id.0]; + if let Some(symbol) = section.symbol { + return symbol; + } + let name = if self.format == BinaryFormat::Coff { + section.name.clone() + } else { + Vec::new() + }; + let symbol_id = SymbolId(self.symbols.len()); + self.symbols.push(Symbol { + name, + value: 0, + size: 0, + kind: SymbolKind::Section, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }); + section.symbol = Some(symbol_id); + symbol_id + } + + /// Append data to an existing section, and update a symbol to refer to it. + /// + /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the + /// symbol will indirectly point to the added data via the `__thread_vars` entry. + /// + /// For Mach-O, if [`Self::set_subsections_via_symbols`] is enabled, this will + /// automatically ensure the data size is at least 1. + /// + /// Returns the section offset of the data. + /// + /// Must not be called for sections that contain uninitialized data. + /// `align` must be a power of two. + pub fn add_symbol_data( + &mut self, + symbol_id: SymbolId, + section: SectionId, + #[cfg_attr(not(feature = "macho"), allow(unused_mut))] mut data: &[u8], + align: u64, + ) -> u64 { + #[cfg(feature = "macho")] + if data.is_empty() && self.macho_subsections_via_symbols { + data = &[0]; + } + let offset = self.append_section_data(section, data, align); + self.set_symbol_data(symbol_id, section, offset, data.len() as u64); + offset + } + + /// Append zero-initialized data to an existing section, and update a symbol to refer to it. + /// + /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the + /// symbol will indirectly point to the added data via the `__thread_vars` entry. + /// + /// For Mach-O, if [`Self::set_subsections_via_symbols`] is enabled, this will + /// automatically ensure the data size is at least 1. + /// + /// Returns the section offset of the data. + /// + /// Must not be called for sections that contain initialized data. + /// `align` must be a power of two. + pub fn add_symbol_bss( + &mut self, + symbol_id: SymbolId, + section: SectionId, + #[cfg_attr(not(feature = "macho"), allow(unused_mut))] mut size: u64, + align: u64, + ) -> u64 { + #[cfg(feature = "macho")] + if size == 0 && self.macho_subsections_via_symbols { + size = 1; + } + let offset = self.append_section_bss(section, size, align); + self.set_symbol_data(symbol_id, section, offset, size); + offset + } + + /// Update a symbol to refer to the given data within a section. + /// + /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the + /// symbol will indirectly point to the data via the `__thread_vars` entry. + #[allow(unused_mut)] + pub fn set_symbol_data( + &mut self, + mut symbol_id: SymbolId, + section: SectionId, + offset: u64, + size: u64, + ) { + // Defined symbols must have a scope. + debug_assert!(self.symbol(symbol_id).scope != SymbolScope::Unknown); + match self.format { + #[cfg(feature = "macho")] + BinaryFormat::MachO => symbol_id = self.macho_add_thread_var(symbol_id), + _ => {} + } + let symbol = self.symbol_mut(symbol_id); + symbol.value = offset; + symbol.size = size; + symbol.section = SymbolSection::Section(section); + } + + /// Convert a symbol to a section symbol and offset. + /// + /// Returns `None` if the symbol does not have a section. + pub fn symbol_section_and_offset(&mut self, symbol_id: SymbolId) -> Option<(SymbolId, u64)> { + let symbol = self.symbol(symbol_id); + if symbol.kind == SymbolKind::Section { + return Some((symbol_id, 0)); + } + let symbol_offset = symbol.value; + let section = symbol.section.id()?; + let section_symbol = self.section_symbol(section); + Some((section_symbol, symbol_offset)) + } + + /// Add a relocation to a section. + /// + /// Relocations must only be added after the referenced symbols have been added + /// and defined (if applicable). + pub fn add_relocation(&mut self, section: SectionId, mut relocation: Relocation) -> Result<()> { + match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_translate_relocation(&mut relocation)?, + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_translate_relocation(&mut relocation)?, + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_translate_relocation(&mut relocation)?, + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_translate_relocation(&mut relocation)?, + _ => unimplemented!(), + } + let implicit = match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_adjust_addend(&mut relocation)?, + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_adjust_addend(&mut relocation)?, + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_adjust_addend(&mut relocation)?, + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_adjust_addend(&mut relocation)?, + _ => unimplemented!(), + }; + if implicit && relocation.addend != 0 { + self.write_relocation_addend(section, &relocation)?; + relocation.addend = 0; + } + self.sections[section.0].relocations.push(relocation); + Ok(()) + } + + fn write_relocation_addend( + &mut self, + section: SectionId, + relocation: &Relocation, + ) -> Result<()> { + let size = match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_relocation_size(relocation)?, + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_relocation_size(relocation)?, + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_relocation_size(relocation)?, + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_relocation_size(relocation)?, + _ => unimplemented!(), + }; + let data = self.sections[section.0].data_mut(); + let offset = relocation.offset as usize; + match size { + 32 => data.write_at(offset, &U32::new(self.endian, relocation.addend as u32)), + 64 => data.write_at(offset, &U64::new(self.endian, relocation.addend as u64)), + _ => { + return Err(Error(format!( + "unimplemented relocation addend {:?}", + relocation + ))); + } + } + .map_err(|_| { + Error(format!( + "invalid relocation offset {}+{} (max {})", + relocation.offset, + size, + data.len() + )) + }) + } + + /// Write the object to a `Vec`. + pub fn write(&self) -> Result<Vec<u8>> { + let mut buffer = Vec::new(); + self.emit(&mut buffer)?; + Ok(buffer) + } + + /// Write the object to a `Write` implementation. + /// + /// Also flushes the writer. + /// + /// It is advisable to use a buffered writer like [`BufWriter`](std::io::BufWriter) + /// instead of an unbuffered writer like [`File`](std::fs::File). + #[cfg(feature = "std")] + pub fn write_stream<W: io::Write>(&self, w: W) -> result::Result<(), Box<dyn error::Error>> { + let mut stream = StreamingBuffer::new(w); + self.emit(&mut stream)?; + stream.result()?; + stream.into_inner().flush()?; + Ok(()) + } + + /// Write the object to a `WritableBuffer`. + pub fn emit(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { + match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_write(buffer), + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_write(buffer), + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_write(buffer), + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_write(buffer), + _ => unimplemented!(), + } + } +} + +/// A standard segment kind. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum StandardSegment { + Text, + Data, + Debug, +} + +/// A standard section kind. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum StandardSection { + Text, + Data, + ReadOnlyData, + ReadOnlyDataWithRel, + ReadOnlyString, + UninitializedData, + Tls, + /// Zero-fill TLS initializers. Unsupported for COFF. + UninitializedTls, + /// TLS variable structures. Only supported for Mach-O. + TlsVariables, + /// Common data. Only supported for Mach-O. + Common, + /// Notes for GNU properties. Only supported for ELF. + GnuProperty, +} + +impl StandardSection { + /// Return the section kind of a standard section. + pub fn kind(self) -> SectionKind { + match self { + StandardSection::Text => SectionKind::Text, + StandardSection::Data => SectionKind::Data, + StandardSection::ReadOnlyData => SectionKind::ReadOnlyData, + StandardSection::ReadOnlyDataWithRel => SectionKind::ReadOnlyDataWithRel, + StandardSection::ReadOnlyString => SectionKind::ReadOnlyString, + StandardSection::UninitializedData => SectionKind::UninitializedData, + StandardSection::Tls => SectionKind::Tls, + StandardSection::UninitializedTls => SectionKind::UninitializedTls, + StandardSection::TlsVariables => SectionKind::TlsVariables, + StandardSection::Common => SectionKind::Common, + StandardSection::GnuProperty => SectionKind::Note, + } + } + + // TODO: remembering to update this is error-prone, can we do better? + fn all() -> &'static [StandardSection] { + &[ + StandardSection::Text, + StandardSection::Data, + StandardSection::ReadOnlyData, + StandardSection::ReadOnlyDataWithRel, + StandardSection::ReadOnlyString, + StandardSection::UninitializedData, + StandardSection::Tls, + StandardSection::UninitializedTls, + StandardSection::TlsVariables, + StandardSection::Common, + StandardSection::GnuProperty, + ] + } +} + +/// An identifier used to reference a section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SectionId(usize); + +/// A section in an object file. +#[derive(Debug)] +pub struct Section<'a> { + segment: Vec<u8>, + name: Vec<u8>, + kind: SectionKind, + size: u64, + align: u64, + data: Cow<'a, [u8]>, + relocations: Vec<Relocation>, + symbol: Option<SymbolId>, + /// Section flags that are specific to each file format. + pub flags: SectionFlags, +} + +impl<'a> Section<'a> { + /// Try to convert the name to a utf8 string. + #[inline] + pub fn name(&self) -> Option<&str> { + str::from_utf8(&self.name).ok() + } + + /// Try to convert the segment to a utf8 string. + #[inline] + pub fn segment(&self) -> Option<&str> { + str::from_utf8(&self.segment).ok() + } + + /// Return true if this section contains zerofill data. + #[inline] + pub fn is_bss(&self) -> bool { + self.kind.is_bss() + } + + /// Set the data for a section. + /// + /// Must not be called for sections that already have data, or that contain uninitialized data. + /// `align` must be a power of two. + pub fn set_data<T>(&mut self, data: T, align: u64) + where + T: Into<Cow<'a, [u8]>>, + { + debug_assert!(!self.is_bss()); + debug_assert_eq!(align & (align - 1), 0); + debug_assert!(self.data.is_empty()); + self.data = data.into(); + self.size = self.data.len() as u64; + self.align = align; + } + + /// Append data to a section. + /// + /// Must not be called for sections that contain uninitialized data. + /// `align` must be a power of two. + pub fn append_data(&mut self, append_data: &[u8], align: u64) -> u64 { + debug_assert!(!self.is_bss()); + debug_assert_eq!(align & (align - 1), 0); + if self.align < align { + self.align = align; + } + let align = align as usize; + let data = self.data.to_mut(); + let mut offset = data.len(); + if offset & (align - 1) != 0 { + offset += align - (offset & (align - 1)); + data.resize(offset, 0); + } + data.extend_from_slice(append_data); + self.size = data.len() as u64; + offset as u64 + } + + /// Append uninitialized data to a section. + /// + /// Must not be called for sections that contain initialized data. + /// `align` must be a power of two. + pub fn append_bss(&mut self, size: u64, align: u64) -> u64 { + debug_assert!(self.is_bss()); + debug_assert_eq!(align & (align - 1), 0); + if self.align < align { + self.align = align; + } + let mut offset = self.size; + if offset & (align - 1) != 0 { + offset += align - (offset & (align - 1)); + self.size = offset; + } + self.size += size; + offset + } + + /// Returns the section as-built so far. + /// + /// This requires that the section is not a bss section. + pub fn data(&self) -> &[u8] { + debug_assert!(!self.is_bss()); + &self.data + } + + /// Returns the section as-built so far. + /// + /// This requires that the section is not a bss section. + pub fn data_mut(&mut self) -> &mut [u8] { + debug_assert!(!self.is_bss()); + self.data.to_mut() + } +} + +/// The section where a symbol is defined. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SymbolSection { + /// The section is not applicable for this symbol (such as file symbols). + None, + /// The symbol is undefined. + Undefined, + /// The symbol has an absolute value. + Absolute, + /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions. + Common, + /// The symbol is defined in the given section. + Section(SectionId), +} + +impl SymbolSection { + /// Returns the section id for the section where the symbol is defined. + /// + /// May return `None` if the symbol is not defined in a section. + #[inline] + pub fn id(self) -> Option<SectionId> { + if let SymbolSection::Section(id) = self { + Some(id) + } else { + None + } + } +} + +/// An identifier used to reference a symbol. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SymbolId(usize); + +/// A symbol in an object file. +#[derive(Debug)] +pub struct Symbol { + /// The name of the symbol. + pub name: Vec<u8>, + /// The value of the symbol. + /// + /// If the symbol defined in a section, then this is the section offset of the symbol. + pub value: u64, + /// The size of the symbol. + pub size: u64, + /// The kind of the symbol. + pub kind: SymbolKind, + /// The scope of the symbol. + pub scope: SymbolScope, + /// Whether the symbol has weak binding. + pub weak: bool, + /// The section containing the symbol. + pub section: SymbolSection, + /// Symbol flags that are specific to each file format. + pub flags: SymbolFlags<SectionId, SymbolId>, +} + +impl Symbol { + /// Try to convert the name to a utf8 string. + #[inline] + pub fn name(&self) -> Option<&str> { + str::from_utf8(&self.name).ok() + } + + /// Return true if the symbol is undefined. + #[inline] + pub fn is_undefined(&self) -> bool { + self.section == SymbolSection::Undefined + } + + /// Return true if the symbol is common data. + /// + /// Note: does not check for `SymbolSection::Section` with `SectionKind::Common`. + #[inline] + pub fn is_common(&self) -> bool { + self.section == SymbolSection::Common + } + + /// Return true if the symbol scope is local. + #[inline] + pub fn is_local(&self) -> bool { + self.scope == SymbolScope::Compilation + } +} + +/// A relocation in an object file. +#[derive(Debug)] +pub struct Relocation { + /// The section offset of the place of the relocation. + pub offset: u64, + /// The symbol referred to by the relocation. + /// + /// This may be a section symbol. + pub symbol: SymbolId, + /// The addend to use in the relocation calculation. + /// + /// This may be in addition to an implicit addend stored at the place of the relocation. + pub addend: i64, + /// The fields that define the relocation type. + pub flags: RelocationFlags, +} + +/// An identifier used to reference a COMDAT section group. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ComdatId(usize); + +/// A COMDAT section group. +#[derive(Debug)] +pub struct Comdat { + /// The COMDAT selection kind. + /// + /// This determines the way in which the linker resolves multiple definitions of the COMDAT + /// sections. + pub kind: ComdatKind, + /// The COMDAT symbol. + /// + /// If this symbol is referenced, then all sections in the group will be included by the + /// linker. + pub symbol: SymbolId, + /// The sections in the group. + pub sections: Vec<SectionId>, +} + +/// The symbol name mangling scheme. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum Mangling { + /// No symbol mangling. + None, + /// Windows COFF symbol mangling. + Coff, + /// Windows COFF i386 symbol mangling. + CoffI386, + /// ELF symbol mangling. + Elf, + /// Mach-O symbol mangling. + MachO, + /// Xcoff symbol mangling. + Xcoff, +} + +impl Mangling { + /// Return the default symboling mangling for the given format and architecture. + pub fn default(format: BinaryFormat, architecture: Architecture) -> Self { + match (format, architecture) { + (BinaryFormat::Coff, Architecture::I386) => Mangling::CoffI386, + (BinaryFormat::Coff, _) => Mangling::Coff, + (BinaryFormat::Elf, _) => Mangling::Elf, + (BinaryFormat::MachO, _) => Mangling::MachO, + (BinaryFormat::Xcoff, _) => Mangling::Xcoff, + _ => Mangling::None, + } + } + + /// Return the prefix to use for global symbols. + pub fn global_prefix(self) -> Option<u8> { + match self { + Mangling::None | Mangling::Elf | Mangling::Coff | Mangling::Xcoff => None, + Mangling::CoffI386 | Mangling::MachO => Some(b'_'), + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/pe.rs b/chromium_crates_io/vendor/object-v0_37/src/write/pe.rs new file mode 100644 index 0000000..03f79fa --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/pe.rs
@@ -0,0 +1,849 @@ +//! Helper for writing PE files. +use alloc::string::String; +use alloc::vec::Vec; +use core::mem; + +use crate::endian::{LittleEndian as LE, *}; +use crate::pe; +use crate::write::util; +use crate::write::{Error, Result, WritableBuffer}; + +/// A helper for writing PE files. +/// +/// Writing uses a two phase approach. The first phase reserves file ranges and virtual +/// address ranges for everything in the order that they will be written. +/// +/// The second phase writes everything out in order. Thus the caller must ensure writing +/// is in the same order that file ranges were reserved. +#[allow(missing_debug_implementations)] +pub struct Writer<'a> { + is_64: bool, + section_alignment: u32, + file_alignment: u32, + + buffer: &'a mut dyn WritableBuffer, + len: u32, + virtual_len: u32, + headers_len: u32, + + code_address: u32, + data_address: u32, + code_len: u32, + data_len: u32, + bss_len: u32, + + nt_headers_offset: u32, + data_directories: Vec<DataDirectory>, + section_header_num: u16, + sections: Vec<Section>, + + symbol_offset: u32, + symbol_num: u32, + + reloc_blocks: Vec<RelocBlock>, + relocs: Vec<U16<LE>>, + reloc_offset: u32, +} + +impl<'a> Writer<'a> { + /// Create a new `Writer`. + /// + /// The alignment values must be powers of two. + pub fn new( + is_64: bool, + section_alignment: u32, + file_alignment: u32, + buffer: &'a mut dyn WritableBuffer, + ) -> Self { + Writer { + is_64, + section_alignment, + file_alignment, + + buffer, + len: 0, + virtual_len: 0, + headers_len: 0, + + code_address: 0, + data_address: 0, + code_len: 0, + data_len: 0, + bss_len: 0, + + nt_headers_offset: 0, + data_directories: Vec::new(), + section_header_num: 0, + sections: Vec::new(), + + symbol_offset: 0, + symbol_num: 0, + + reloc_blocks: Vec::new(), + relocs: Vec::new(), + reloc_offset: 0, + } + } + + /// Return the current virtual address size that has been reserved. + /// + /// This is only valid after section headers have been reserved. + pub fn virtual_len(&self) -> u32 { + self.virtual_len + } + + /// Reserve a virtual address range with the given size. + /// + /// The reserved length will be increased to match the section alignment. + /// + /// Returns the aligned offset of the start of the range. + pub fn reserve_virtual(&mut self, len: u32) -> u32 { + let offset = self.virtual_len; + self.virtual_len += len; + self.virtual_len = util::align_u32(self.virtual_len, self.section_alignment); + offset + } + + /// Reserve up to the given virtual address. + /// + /// The reserved length will be increased to match the section alignment. + pub fn reserve_virtual_until(&mut self, address: u32) { + debug_assert!(self.virtual_len <= address); + self.virtual_len = util::align_u32(address, self.section_alignment); + } + + /// Return the current file length that has been reserved. + pub fn reserved_len(&self) -> u32 { + self.len + } + + /// Return the current file length that has been written. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.buffer.len() + } + + /// Reserve a file range with the given size and starting alignment. + /// + /// Returns the aligned offset of the start of the range. + pub fn reserve(&mut self, len: u32, align_start: u32) -> u32 { + if len == 0 { + return self.len; + } + self.reserve_align(align_start); + let offset = self.len; + self.len += len; + offset + } + + /// Reserve a file range with the given size and using the file alignment. + /// + /// Returns the aligned offset of the start of the range. + pub fn reserve_file(&mut self, len: u32) -> u32 { + self.reserve(len, self.file_alignment) + } + + /// Write data. + pub fn write(&mut self, data: &[u8]) { + self.buffer.write_bytes(data); + } + + /// Reserve alignment padding bytes. + pub fn reserve_align(&mut self, align_start: u32) { + self.len = util::align_u32(self.len, align_start); + } + + /// Write alignment padding bytes. + pub fn write_align(&mut self, align_start: u32) { + util::write_align(self.buffer, align_start as usize); + } + + /// Write padding up to the next multiple of file alignment. + pub fn write_file_align(&mut self) { + self.write_align(self.file_alignment); + } + + /// Reserve the file range up to the given file offset. + pub fn reserve_until(&mut self, offset: u32) { + debug_assert!(self.len <= offset); + self.len = offset; + } + + /// Write padding up to the given file offset. + pub fn pad_until(&mut self, offset: u32) { + debug_assert!(self.buffer.len() <= offset as usize); + self.buffer.resize(offset as usize); + } + + /// Reserve the range for the DOS header. + /// + /// This must be at the start of the file. + /// + /// When writing, you may use `write_custom_dos_header` or `write_empty_dos_header`. + pub fn reserve_dos_header(&mut self) { + debug_assert_eq!(self.len, 0); + self.reserve(mem::size_of::<pe::ImageDosHeader>() as u32, 1); + } + + /// Write a custom DOS header. + /// + /// This must be at the start of the file. + pub fn write_custom_dos_header(&mut self, dos_header: &pe::ImageDosHeader) -> Result<()> { + debug_assert_eq!(self.buffer.len(), 0); + + // Start writing. + self.buffer + .reserve(self.len as usize) + .map_err(|_| Error(String::from("Cannot allocate buffer")))?; + + self.buffer.write(dos_header); + Ok(()) + } + + /// Write the DOS header for a file without a stub. + /// + /// This must be at the start of the file. + /// + /// Uses default values for all fields. + pub fn write_empty_dos_header(&mut self) -> Result<()> { + self.write_custom_dos_header(&pe::ImageDosHeader { + e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE), + e_cblp: U16::new(LE, 0), + e_cp: U16::new(LE, 0), + e_crlc: U16::new(LE, 0), + e_cparhdr: U16::new(LE, 0), + e_minalloc: U16::new(LE, 0), + e_maxalloc: U16::new(LE, 0), + e_ss: U16::new(LE, 0), + e_sp: U16::new(LE, 0), + e_csum: U16::new(LE, 0), + e_ip: U16::new(LE, 0), + e_cs: U16::new(LE, 0), + e_lfarlc: U16::new(LE, 0), + e_ovno: U16::new(LE, 0), + e_res: [U16::new(LE, 0); 4], + e_oemid: U16::new(LE, 0), + e_oeminfo: U16::new(LE, 0), + e_res2: [U16::new(LE, 0); 10], + e_lfanew: U32::new(LE, self.nt_headers_offset), + }) + } + + /// Reserve a fixed DOS header and stub. + /// + /// Use `reserve_dos_header` and `reserve` if you need a custom stub. + pub fn reserve_dos_header_and_stub(&mut self) { + self.reserve_dos_header(); + self.reserve(64, 1); + } + + /// Write a fixed DOS header and stub. + /// + /// Use `write_custom_dos_header` and `write` if you need a custom stub. + pub fn write_dos_header_and_stub(&mut self) -> Result<()> { + self.write_custom_dos_header(&pe::ImageDosHeader { + e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE), + e_cblp: U16::new(LE, 0x90), + e_cp: U16::new(LE, 3), + e_crlc: U16::new(LE, 0), + e_cparhdr: U16::new(LE, 4), + e_minalloc: U16::new(LE, 0), + e_maxalloc: U16::new(LE, 0xffff), + e_ss: U16::new(LE, 0), + e_sp: U16::new(LE, 0xb8), + e_csum: U16::new(LE, 0), + e_ip: U16::new(LE, 0), + e_cs: U16::new(LE, 0), + e_lfarlc: U16::new(LE, 0x40), + e_ovno: U16::new(LE, 0), + e_res: [U16::new(LE, 0); 4], + e_oemid: U16::new(LE, 0), + e_oeminfo: U16::new(LE, 0), + e_res2: [U16::new(LE, 0); 10], + e_lfanew: U32::new(LE, self.nt_headers_offset), + })?; + + #[rustfmt::skip] + self.buffer.write_bytes(&[ + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, + 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, + 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]); + + Ok(()) + } + + fn nt_headers_size(&self) -> u32 { + if self.is_64 { + mem::size_of::<pe::ImageNtHeaders64>() as u32 + } else { + mem::size_of::<pe::ImageNtHeaders32>() as u32 + } + } + + fn optional_header_size(&self) -> u32 { + let size = if self.is_64 { + mem::size_of::<pe::ImageOptionalHeader64>() as u32 + } else { + mem::size_of::<pe::ImageOptionalHeader32>() as u32 + }; + size + self.data_directories.len() as u32 * mem::size_of::<pe::ImageDataDirectory>() as u32 + } + + /// Return the offset of the NT headers, if reserved. + pub fn nt_headers_offset(&self) -> u32 { + self.nt_headers_offset + } + + /// Reserve the range for the NT headers. + pub fn reserve_nt_headers(&mut self, data_directory_num: usize) { + debug_assert_eq!(self.nt_headers_offset, 0); + self.nt_headers_offset = self.reserve(self.nt_headers_size(), 8); + self.data_directories = vec![DataDirectory::default(); data_directory_num]; + self.reserve( + data_directory_num as u32 * mem::size_of::<pe::ImageDataDirectory>() as u32, + 1, + ); + } + + /// Set the virtual address and size of a data directory. + pub fn set_data_directory(&mut self, index: usize, virtual_address: u32, size: u32) { + self.data_directories[index] = DataDirectory { + virtual_address, + size, + } + } + + /// Write the NT headers. + pub fn write_nt_headers(&mut self, nt_headers: NtHeaders) { + self.pad_until(self.nt_headers_offset); + self.buffer.write(&U32::new(LE, pe::IMAGE_NT_SIGNATURE)); + let file_header = pe::ImageFileHeader { + machine: U16::new(LE, nt_headers.machine), + number_of_sections: U16::new(LE, self.section_header_num), + time_date_stamp: U32::new(LE, nt_headers.time_date_stamp), + pointer_to_symbol_table: U32::new(LE, self.symbol_offset), + number_of_symbols: U32::new(LE, self.symbol_num), + size_of_optional_header: U16::new(LE, self.optional_header_size() as u16), + characteristics: U16::new(LE, nt_headers.characteristics), + }; + self.buffer.write(&file_header); + if self.is_64 { + let optional_header = pe::ImageOptionalHeader64 { + magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC), + major_linker_version: nt_headers.major_linker_version, + minor_linker_version: nt_headers.minor_linker_version, + size_of_code: U32::new(LE, self.code_len), + size_of_initialized_data: U32::new(LE, self.data_len), + size_of_uninitialized_data: U32::new(LE, self.bss_len), + address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point), + base_of_code: U32::new(LE, self.code_address), + image_base: U64::new(LE, nt_headers.image_base), + section_alignment: U32::new(LE, self.section_alignment), + file_alignment: U32::new(LE, self.file_alignment), + major_operating_system_version: U16::new( + LE, + nt_headers.major_operating_system_version, + ), + minor_operating_system_version: U16::new( + LE, + nt_headers.minor_operating_system_version, + ), + major_image_version: U16::new(LE, nt_headers.major_image_version), + minor_image_version: U16::new(LE, nt_headers.minor_image_version), + major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version), + minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version), + win32_version_value: U32::new(LE, 0), + size_of_image: U32::new(LE, self.virtual_len), + size_of_headers: U32::new(LE, self.headers_len), + check_sum: U32::new(LE, 0), + subsystem: U16::new(LE, nt_headers.subsystem), + dll_characteristics: U16::new(LE, nt_headers.dll_characteristics), + size_of_stack_reserve: U64::new(LE, nt_headers.size_of_stack_reserve), + size_of_stack_commit: U64::new(LE, nt_headers.size_of_stack_commit), + size_of_heap_reserve: U64::new(LE, nt_headers.size_of_heap_reserve), + size_of_heap_commit: U64::new(LE, nt_headers.size_of_heap_commit), + loader_flags: U32::new(LE, 0), + number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32), + }; + self.buffer.write(&optional_header); + } else { + let optional_header = pe::ImageOptionalHeader32 { + magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC), + major_linker_version: nt_headers.major_linker_version, + minor_linker_version: nt_headers.minor_linker_version, + size_of_code: U32::new(LE, self.code_len), + size_of_initialized_data: U32::new(LE, self.data_len), + size_of_uninitialized_data: U32::new(LE, self.bss_len), + address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point), + base_of_code: U32::new(LE, self.code_address), + base_of_data: U32::new(LE, self.data_address), + image_base: U32::new(LE, nt_headers.image_base as u32), + section_alignment: U32::new(LE, self.section_alignment), + file_alignment: U32::new(LE, self.file_alignment), + major_operating_system_version: U16::new( + LE, + nt_headers.major_operating_system_version, + ), + minor_operating_system_version: U16::new( + LE, + nt_headers.minor_operating_system_version, + ), + major_image_version: U16::new(LE, nt_headers.major_image_version), + minor_image_version: U16::new(LE, nt_headers.minor_image_version), + major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version), + minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version), + win32_version_value: U32::new(LE, 0), + size_of_image: U32::new(LE, self.virtual_len), + size_of_headers: U32::new(LE, self.headers_len), + check_sum: U32::new(LE, 0), + subsystem: U16::new(LE, nt_headers.subsystem), + dll_characteristics: U16::new(LE, nt_headers.dll_characteristics), + size_of_stack_reserve: U32::new(LE, nt_headers.size_of_stack_reserve as u32), + size_of_stack_commit: U32::new(LE, nt_headers.size_of_stack_commit as u32), + size_of_heap_reserve: U32::new(LE, nt_headers.size_of_heap_reserve as u32), + size_of_heap_commit: U32::new(LE, nt_headers.size_of_heap_commit as u32), + loader_flags: U32::new(LE, 0), + number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32), + }; + self.buffer.write(&optional_header); + } + + for dir in &self.data_directories { + self.buffer.write(&pe::ImageDataDirectory { + virtual_address: U32::new(LE, dir.virtual_address), + size: U32::new(LE, dir.size), + }) + } + } + + /// Reserve the section headers. + /// + /// The number of reserved section headers must be the same as the number of sections that + /// are later reserved. + // TODO: change this to a maximum number of sections? + pub fn reserve_section_headers(&mut self, section_header_num: u16) { + debug_assert_eq!(self.section_header_num, 0); + self.section_header_num = section_header_num; + self.reserve( + u32::from(section_header_num) * mem::size_of::<pe::ImageSectionHeader>() as u32, + 1, + ); + // Padding before sections must be included in headers_len. + self.reserve_align(self.file_alignment); + self.headers_len = self.len; + self.reserve_virtual(self.len); + } + + /// Write the section headers. + /// + /// This uses information that was recorded when the sections were reserved. + pub fn write_section_headers(&mut self) { + debug_assert_eq!(self.section_header_num as usize, self.sections.len()); + for section in &self.sections { + let section_header = pe::ImageSectionHeader { + name: section.name, + virtual_size: U32::new(LE, section.range.virtual_size), + virtual_address: U32::new(LE, section.range.virtual_address), + size_of_raw_data: U32::new(LE, section.range.file_size), + pointer_to_raw_data: U32::new(LE, section.range.file_offset), + pointer_to_relocations: U32::new(LE, 0), + pointer_to_linenumbers: U32::new(LE, 0), + number_of_relocations: U16::new(LE, 0), + number_of_linenumbers: U16::new(LE, 0), + characteristics: U32::new(LE, section.characteristics), + }; + self.buffer.write(§ion_header); + } + } + + /// Reserve a section. + /// + /// Returns the file range and virtual address range that are reserved + /// for the section. + pub fn reserve_section( + &mut self, + name: [u8; 8], + characteristics: u32, + virtual_size: u32, + data_size: u32, + ) -> SectionRange { + let virtual_address = self.reserve_virtual(virtual_size); + + // Padding after section must be included in section file size. + let file_size = util::align_u32(data_size, self.file_alignment); + let file_offset = if file_size != 0 { + self.reserve(file_size, self.file_alignment) + } else { + 0 + }; + + // Sizes in optional header use the virtual size with the file alignment. + let aligned_virtual_size = util::align_u32(virtual_size, self.file_alignment); + if characteristics & pe::IMAGE_SCN_CNT_CODE != 0 { + if self.code_address == 0 { + self.code_address = virtual_address; + } + self.code_len += aligned_virtual_size; + } else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { + if self.data_address == 0 { + self.data_address = virtual_address; + } + self.data_len += aligned_virtual_size; + } else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { + if self.data_address == 0 { + self.data_address = virtual_address; + } + self.bss_len += aligned_virtual_size; + } + + let range = SectionRange { + virtual_address, + virtual_size, + file_offset, + file_size, + }; + self.sections.push(Section { + name, + characteristics, + range, + }); + range + } + + /// Write the data for a section. + pub fn write_section(&mut self, offset: u32, data: &[u8]) { + if data.is_empty() { + return; + } + self.pad_until(offset); + self.write(data); + self.write_align(self.file_alignment); + } + + /// Reserve a `.text` section. + /// + /// Contains executable code. + pub fn reserve_text_section(&mut self, size: u32) -> SectionRange { + self.reserve_section( + *b".text\0\0\0", + pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE | pe::IMAGE_SCN_MEM_READ, + size, + size, + ) + } + + /// Reserve a `.data` section. + /// + /// Contains initialized data. + /// + /// May also contain uninitialized data if `virtual_size` is greater than `data_size`. + pub fn reserve_data_section(&mut self, virtual_size: u32, data_size: u32) -> SectionRange { + self.reserve_section( + *b".data\0\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, + virtual_size, + data_size, + ) + } + + /// Reserve a `.rdata` section. + /// + /// Contains read-only initialized data. + pub fn reserve_rdata_section(&mut self, size: u32) -> SectionRange { + self.reserve_section( + *b".rdata\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, + size, + size, + ) + } + + /// Reserve a `.bss` section. + /// + /// Contains uninitialized data. + pub fn reserve_bss_section(&mut self, size: u32) -> SectionRange { + self.reserve_section( + *b".bss\0\0\0\0", + pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, + size, + 0, + ) + } + + /// Reserve an `.idata` section. + /// + /// Contains import tables. Note that it is permissible to store import tables in a different + /// section. + /// + /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_IMPORT` data directory. + pub fn reserve_idata_section(&mut self, size: u32) -> SectionRange { + let range = self.reserve_section( + *b".idata\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, + size, + size, + ); + let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_IMPORT]; + debug_assert_eq!(dir.virtual_address, 0); + *dir = DataDirectory { + virtual_address: range.virtual_address, + size, + }; + range + } + + /// Reserve an `.edata` section. + /// + /// Contains export tables. + /// + /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXPORT` data directory. + pub fn reserve_edata_section(&mut self, size: u32) -> SectionRange { + let range = self.reserve_section( + *b".edata\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, + size, + size, + ); + let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXPORT]; + debug_assert_eq!(dir.virtual_address, 0); + *dir = DataDirectory { + virtual_address: range.virtual_address, + size, + }; + range + } + + /// Reserve a `.pdata` section. + /// + /// Contains exception information. + /// + /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION` data directory. + pub fn reserve_pdata_section(&mut self, size: u32) -> SectionRange { + let range = self.reserve_section( + *b".pdata\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, + size, + size, + ); + let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION]; + debug_assert_eq!(dir.virtual_address, 0); + *dir = DataDirectory { + virtual_address: range.virtual_address, + size, + }; + range + } + + /// Reserve a `.xdata` section. + /// + /// Contains exception information. + pub fn reserve_xdata_section(&mut self, size: u32) -> SectionRange { + self.reserve_section( + *b".xdata\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, + size, + size, + ) + } + + /// Reserve a `.rsrc` section. + /// + /// Contains the resource directory. + /// + /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_RESOURCE` data directory. + pub fn reserve_rsrc_section(&mut self, size: u32) -> SectionRange { + let range = self.reserve_section( + *b".rsrc\0\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, + size, + size, + ); + let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_RESOURCE]; + debug_assert_eq!(dir.virtual_address, 0); + *dir = DataDirectory { + virtual_address: range.virtual_address, + size, + }; + range + } + + /// Add a base relocation. + /// + /// `typ` must be one of the `IMAGE_REL_BASED_*` constants. + pub fn add_reloc(&mut self, mut virtual_address: u32, typ: u16) { + let reloc = U16::new(LE, typ << 12 | (virtual_address & 0xfff) as u16); + virtual_address &= !0xfff; + if let Some(block) = self.reloc_blocks.last_mut() { + if block.virtual_address == virtual_address { + self.relocs.push(reloc); + block.count += 1; + return; + } + // Blocks must have an even number of relocations. + if block.count & 1 != 0 { + self.relocs.push(U16::new(LE, 0)); + block.count += 1; + } + debug_assert!(block.virtual_address < virtual_address); + } + self.relocs.push(reloc); + self.reloc_blocks.push(RelocBlock { + virtual_address, + count: 1, + }); + } + + /// Return true if a base relocation has been added. + pub fn has_relocs(&mut self) -> bool { + !self.relocs.is_empty() + } + + /// Reserve a `.reloc` section. + /// + /// This contains the base relocations that were added with `add_reloc`. + /// + /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_BASERELOC` data directory. + pub fn reserve_reloc_section(&mut self) -> SectionRange { + if let Some(block) = self.reloc_blocks.last_mut() { + // Blocks must have an even number of relocations. + if block.count & 1 != 0 { + self.relocs.push(U16::new(LE, 0)); + block.count += 1; + } + } + let size = self.reloc_blocks.iter().map(RelocBlock::size).sum(); + let range = self.reserve_section( + *b".reloc\0\0", + pe::IMAGE_SCN_CNT_INITIALIZED_DATA + | pe::IMAGE_SCN_MEM_READ + | pe::IMAGE_SCN_MEM_DISCARDABLE, + size, + size, + ); + let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_BASERELOC]; + debug_assert_eq!(dir.virtual_address, 0); + *dir = DataDirectory { + virtual_address: range.virtual_address, + size, + }; + self.reloc_offset = range.file_offset; + range + } + + /// Write a `.reloc` section. + /// + /// This contains the base relocations that were added with `add_reloc`. + pub fn write_reloc_section(&mut self) { + if self.reloc_offset == 0 { + return; + } + self.pad_until(self.reloc_offset); + + let mut total = 0; + for block in &self.reloc_blocks { + self.buffer.write(&pe::ImageBaseRelocation { + virtual_address: U32::new(LE, block.virtual_address), + size_of_block: U32::new(LE, block.size()), + }); + self.buffer + .write_slice(&self.relocs[total..][..block.count as usize]); + total += block.count as usize; + } + debug_assert_eq!(total, self.relocs.len()); + + self.write_align(self.file_alignment); + } + + /// Reserve the certificate table. + /// + /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_SECURITY` data directory. + // TODO: reserve individual certificates + pub fn reserve_certificate_table(&mut self, size: u32) { + let size = util::align_u32(size, 8); + let offset = self.reserve(size, 8); + let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY]; + debug_assert_eq!(dir.virtual_address, 0); + *dir = DataDirectory { + virtual_address: offset, + size, + }; + } + + /// Write the certificate table. + // TODO: write individual certificates + pub fn write_certificate_table(&mut self, data: &[u8]) { + let dir = self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY]; + self.pad_until(dir.virtual_address); + self.write(data); + self.pad_until(dir.virtual_address + dir.size); + } +} + +/// Information required for writing [`pe::ImageNtHeaders32`] or [`pe::ImageNtHeaders64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct NtHeaders { + // ImageFileHeader + pub machine: u16, + pub time_date_stamp: u32, + pub characteristics: u16, + // ImageOptionalHeader + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub address_of_entry_point: u32, + pub image_base: u64, + pub major_operating_system_version: u16, + pub minor_operating_system_version: u16, + pub major_image_version: u16, + pub minor_image_version: u16, + pub major_subsystem_version: u16, + pub minor_subsystem_version: u16, + pub subsystem: u16, + pub dll_characteristics: u16, + pub size_of_stack_reserve: u64, + pub size_of_stack_commit: u64, + pub size_of_heap_reserve: u64, + pub size_of_heap_commit: u64, +} + +#[derive(Default, Clone, Copy)] +struct DataDirectory { + virtual_address: u32, + size: u32, +} + +/// Information required for writing [`pe::ImageSectionHeader`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Section { + pub name: [u8; pe::IMAGE_SIZEOF_SHORT_NAME], + pub characteristics: u32, + pub range: SectionRange, +} + +/// The file range and virtual address range for a section. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone, Copy)] +pub struct SectionRange { + pub virtual_address: u32, + pub virtual_size: u32, + pub file_offset: u32, + pub file_size: u32, +} + +struct RelocBlock { + virtual_address: u32, + count: u32, +} + +impl RelocBlock { + fn size(&self) -> u32 { + mem::size_of::<pe::ImageBaseRelocation>() as u32 + self.count * mem::size_of::<u16>() as u32 + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/string.rs b/chromium_crates_io/vendor/object-v0_37/src/write/string.rs new file mode 100644 index 0000000..0bee1a7 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/string.rs
@@ -0,0 +1,186 @@ +use alloc::vec::Vec; + +#[cfg(feature = "write_std")] +type IndexSet<K> = indexmap::IndexSet<K>; +#[cfg(not(feature = "write_std"))] +type IndexSet<K> = indexmap::IndexSet<K, hashbrown::DefaultHashBuilder>; + +/// An identifier for an entry in a string table. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct StringId(usize); + +#[derive(Debug, Default)] +pub(crate) struct StringTable<'a> { + strings: IndexSet<&'a [u8]>, + offsets: Vec<usize>, +} + +impl<'a> StringTable<'a> { + /// Add a string to the string table. + /// + /// Panics if the string table has already been written, or + /// if the string contains a null byte. + pub fn add(&mut self, string: &'a [u8]) -> StringId { + assert!(self.offsets.is_empty()); + assert!(!string.contains(&0)); + let id = self.strings.insert_full(string).0; + StringId(id) + } + + /// Return the id of the given string. + /// + /// Panics if the string is not in the string table. + #[allow(dead_code)] + pub fn get_id(&self, string: &[u8]) -> StringId { + let id = self.strings.get_index_of(string).unwrap(); + StringId(id) + } + + /// Return the string for the given id. + /// + /// Panics if the string is not in the string table. + #[allow(dead_code)] + pub fn get_string(&self, id: StringId) -> &'a [u8] { + self.strings.get_index(id.0).unwrap() + } + + /// Return the offset of the given string. + /// + /// Panics if the string table has not been written, or + /// if the string is not in the string table. + pub fn get_offset(&self, id: StringId) -> usize { + self.offsets[id.0] + } + + /// Append the string table to the given `Vec`, and + /// calculate the list of string offsets. + /// + /// `base` is the initial string table offset. For example, + /// this should be 1 for ELF, to account for the initial + /// null byte (which must have been written by the caller). + /// + /// Panics if the string table has already been written. + pub fn write(&mut self, base: usize, w: &mut Vec<u8>) { + assert!(self.offsets.is_empty()); + + let mut ids: Vec<_> = (0..self.strings.len()).collect(); + sort(&mut ids, 1, &self.strings); + + self.offsets = vec![0; ids.len()]; + let mut offset = base; + let mut previous = &[][..]; + for id in ids { + let string = self.strings.get_index(id).unwrap(); + if previous.ends_with(string) { + self.offsets[id] = offset - string.len() - 1; + } else { + self.offsets[id] = offset; + w.extend_from_slice(string); + w.push(0); + offset += string.len() + 1; + previous = string; + } + } + } + + /// Calculate the size in bytes of the string table. + /// + /// `base` is the initial string table offset. For example, + /// this should be 1 for ELF, to account for the initial + /// null byte. + #[allow(dead_code)] + pub fn size(&self, base: usize) -> usize { + // TODO: cache this result? + let mut ids: Vec<_> = (0..self.strings.len()).collect(); + sort(&mut ids, 1, &self.strings); + + let mut size = base; + let mut previous = &[][..]; + for id in ids { + let string = self.strings.get_index(id).unwrap(); + if !previous.ends_with(string) { + size += string.len() + 1; + previous = string; + } + } + size + } +} + +// Multi-key quicksort. +// +// Ordering is such that if a string is a suffix of at least one other string, +// then it is placed immediately after one of those strings. That is: +// - comparison starts at the end of the string +// - shorter strings come later +// +// Based on the implementation in LLVM. +fn sort(mut ids: &mut [usize], mut pos: usize, strings: &IndexSet<&[u8]>) { + loop { + if ids.len() <= 1 { + return; + } + + let pivot = byte(ids[0], pos, strings); + let mut lower = 0; + let mut upper = ids.len(); + let mut i = 1; + while i < upper { + let b = byte(ids[i], pos, strings); + if b > pivot { + ids.swap(lower, i); + lower += 1; + i += 1; + } else if b < pivot { + upper -= 1; + ids.swap(upper, i); + } else { + i += 1; + } + } + + sort(&mut ids[..lower], pos, strings); + sort(&mut ids[upper..], pos, strings); + + if pivot == 0 { + return; + } + ids = &mut ids[lower..upper]; + pos += 1; + } +} + +fn byte(id: usize, pos: usize, strings: &IndexSet<&[u8]>) -> u8 { + let string = strings.get_index(id).unwrap(); + let len = string.len(); + if len >= pos { + string[len - pos] + } else { + // We know the strings don't contain null bytes. + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn string_table() { + let mut table = StringTable::default(); + let id0 = table.add(b""); + let id1 = table.add(b"foo"); + let id2 = table.add(b"bar"); + let id3 = table.add(b"foobar"); + + let mut data = Vec::new(); + data.push(0); + table.write(1, &mut data); + assert_eq!(data, b"\0foobar\0foo\0"); + + assert_eq!(table.get_offset(id0), 11); + assert_eq!(table.get_offset(id1), 8); + assert_eq!(table.get_offset(id2), 4); + assert_eq!(table.get_offset(id3), 1); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/util.rs b/chromium_crates_io/vendor/object-v0_37/src/write/util.rs new file mode 100644 index 0000000..56ed994 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/util.rs
@@ -0,0 +1,261 @@ +use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::{io, mem}; + +use crate::pod::{bytes_of, bytes_of_slice, Pod}; + +/// Trait for writable buffer. +#[allow(clippy::len_without_is_empty)] +pub trait WritableBuffer { + /// Returns position/offset for data to be written at. + /// + /// Should only be used in debug assertions + fn len(&self) -> usize; + + /// Reserves specified number of bytes in the buffer. + /// + /// This will be called exactly once before writing anything to the buffer, + /// and the given size is the exact total number of bytes that will be written. + fn reserve(&mut self, size: usize) -> Result<(), ()>; + + /// Writes zero bytes at the end of the buffer until the buffer + /// has the specified length. + fn resize(&mut self, new_len: usize); + + /// Writes the specified slice of bytes at the end of the buffer. + fn write_bytes(&mut self, val: &[u8]); + + /// Writes the specified `Pod` type at the end of the buffer. + fn write_pod<T: Pod>(&mut self, val: &T) + where + Self: Sized, + { + self.write_bytes(bytes_of(val)) + } + + /// Writes the specified `Pod` slice at the end of the buffer. + fn write_pod_slice<T: Pod>(&mut self, val: &[T]) + where + Self: Sized, + { + self.write_bytes(bytes_of_slice(val)) + } +} + +impl<'a> dyn WritableBuffer + 'a { + /// Writes the specified `Pod` type at the end of the buffer. + pub fn write<T: Pod>(&mut self, val: &T) { + self.write_bytes(bytes_of(val)) + } + + /// Writes the specified `Pod` slice at the end of the buffer. + pub fn write_slice<T: Pod>(&mut self, val: &[T]) { + self.write_bytes(bytes_of_slice(val)) + } +} + +impl WritableBuffer for Vec<u8> { + #[inline] + fn len(&self) -> usize { + self.len() + } + + #[inline] + fn reserve(&mut self, size: usize) -> Result<(), ()> { + debug_assert!(self.is_empty()); + self.reserve(size); + Ok(()) + } + + #[inline] + fn resize(&mut self, new_len: usize) { + debug_assert!(new_len >= self.len()); + self.resize(new_len, 0); + } + + #[inline] + fn write_bytes(&mut self, val: &[u8]) { + debug_assert!(self.len() + val.len() <= self.capacity()); + self.extend_from_slice(val) + } +} + +/// A [`WritableBuffer`] that streams data to a [`Write`](std::io::Write) implementation. +/// +/// [`Self::result`] must be called to determine if an I/O error occurred during writing. +/// +/// It is advisable to use a buffered writer like [`BufWriter`](std::io::BufWriter) +/// instead of an unbuffered writer like [`File`](std::fs::File). +#[cfg(feature = "std")] +#[derive(Debug)] +pub struct StreamingBuffer<W> { + writer: W, + len: usize, + result: Result<(), io::Error>, +} + +#[cfg(feature = "std")] +impl<W> StreamingBuffer<W> { + /// Create a new `StreamingBuffer` backed by the given writer. + pub fn new(writer: W) -> Self { + StreamingBuffer { + writer, + len: 0, + result: Ok(()), + } + } + + /// Unwraps this [`StreamingBuffer`] giving back the original writer. + pub fn into_inner(self) -> W { + self.writer + } + + /// Returns any error that occurred during writing. + pub fn result(&mut self) -> Result<(), io::Error> { + mem::replace(&mut self.result, Ok(())) + } +} + +#[cfg(feature = "std")] +impl<W: io::Write> WritableBuffer for StreamingBuffer<W> { + #[inline] + fn len(&self) -> usize { + self.len + } + + #[inline] + fn reserve(&mut self, _size: usize) -> Result<(), ()> { + Ok(()) + } + + #[inline] + fn resize(&mut self, new_len: usize) { + debug_assert!(self.len <= new_len); + while self.len < new_len { + let write_amt = (new_len - self.len - 1) % 1024 + 1; + self.write_bytes(&[0; 1024][..write_amt]); + } + } + + #[inline] + fn write_bytes(&mut self, val: &[u8]) { + if self.result.is_ok() { + self.result = self.writer.write_all(val); + } + self.len += val.len(); + } +} + +/// A trait for mutable byte slices. +/// +/// It provides convenience methods for `Pod` types. +pub(crate) trait BytesMut { + fn write_at<T: Pod>(self, offset: usize, val: &T) -> Result<(), ()>; +} + +impl<'a> BytesMut for &'a mut [u8] { + #[inline] + fn write_at<T: Pod>(self, offset: usize, val: &T) -> Result<(), ()> { + let src = bytes_of(val); + let dest = self.get_mut(offset..).ok_or(())?; + let dest = dest.get_mut(..src.len()).ok_or(())?; + dest.copy_from_slice(src); + Ok(()) + } +} + +/// Write an unsigned number using the LEB128 encoding to a buffer. +/// +/// Returns the number of bytes written. +#[allow(dead_code)] +pub(crate) fn write_uleb128(buf: &mut Vec<u8>, mut val: u64) -> usize { + let mut len = 0; + loop { + let mut byte = (val & 0x7f) as u8; + val >>= 7; + let done = val == 0; + if !done { + byte |= 0x80; + } + + buf.push(byte); + len += 1; + + if done { + return len; + } + } +} + +/// Write a signed number using the LEB128 encoding to a buffer. +/// +/// Returns the number of bytes written. +#[allow(dead_code)] +pub(crate) fn write_sleb128(buf: &mut Vec<u8>, mut val: i64) -> usize { + let mut len = 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 &= !0x80; + } else { + // Remove the sign bit + val >>= 1; + byte |= 0x80; + } + + buf.push(byte); + len += 1; + + if done { + return len; + } + } +} + +pub(crate) fn align(offset: usize, size: usize) -> usize { + (offset + (size - 1)) & !(size - 1) +} + +#[allow(dead_code)] +pub(crate) fn align_u32(offset: u32, size: u32) -> u32 { + (offset + (size - 1)) & !(size - 1) +} + +#[allow(dead_code)] +pub(crate) fn align_u64(offset: u64, size: u64) -> u64 { + (offset + (size - 1)) & !(size - 1) +} + +pub(crate) fn write_align(buffer: &mut dyn WritableBuffer, size: usize) { + let new_len = align(buffer.len(), size); + buffer.resize(new_len); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bytes_mut() { + let data = vec![0x01, 0x23, 0x45, 0x67]; + + let mut bytes = data.clone(); + bytes.extend_from_slice(bytes_of(&u16::to_be(0x89ab))); + assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab]); + + let mut bytes = data.clone(); + assert_eq!(bytes.write_at(0, &u16::to_be(0x89ab)), Ok(())); + assert_eq!(bytes, [0x89, 0xab, 0x45, 0x67]); + + let mut bytes = data.clone(); + assert_eq!(bytes.write_at(2, &u16::to_be(0x89ab)), Ok(())); + assert_eq!(bytes, [0x01, 0x23, 0x89, 0xab]); + + assert_eq!(bytes.write_at(3, &u16::to_be(0x89ab)), Err(())); + assert_eq!(bytes.write_at(4, &u16::to_be(0x89ab)), Err(())); + assert_eq!([].write_at(0, &u32::to_be(0x89ab)), Err(())); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/write/xcoff.rs b/chromium_crates_io/vendor/object-v0_37/src/write/xcoff.rs new file mode 100644 index 0000000..4dbdfcd --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/write/xcoff.rs
@@ -0,0 +1,608 @@ +use core::mem; + +use crate::endian::{BigEndian as BE, I16, U16, U32}; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; + +use crate::xcoff; + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + address: u64, + data_offset: usize, + reloc_offset: usize, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: usize, + str_id: Option<StringId>, + aux_count: u8, + storage_class: u8, + x_smtyp: u8, + x_smclas: u8, + containing_csect: Option<SymbolId>, +} + +impl<'a> Object<'a> { + pub(crate) fn xcoff_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { + match section { + StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None), + StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None), + StandardSection::ReadOnlyData + | StandardSection::ReadOnlyDataWithRel + | StandardSection::ReadOnlyString => ( + &[], + &b".rdata"[..], + SectionKind::ReadOnlyData, + SectionFlags::None, + ), + StandardSection::UninitializedData => ( + &[], + &b".bss"[..], + SectionKind::UninitializedData, + SectionFlags::None, + ), + StandardSection::Tls => (&[], &b".tdata"[..], SectionKind::Tls, SectionFlags::None), + StandardSection::UninitializedTls => ( + &[], + &b".tbss"[..], + SectionKind::UninitializedTls, + SectionFlags::None, + ), + StandardSection::TlsVariables => { + // Unsupported section. + (&[], &[], SectionKind::TlsVariables, SectionFlags::None) + } + StandardSection::Common => { + // Unsupported section. + (&[], &[], SectionKind::Common, SectionFlags::None) + } + StandardSection::GnuProperty => { + // Unsupported section. + (&[], &[], SectionKind::Note, SectionFlags::None) + } + } + } + + pub(crate) fn xcoff_section_flags(&self, section: &Section<'_>) -> SectionFlags { + let s_flags = match section.kind { + SectionKind::Text + | SectionKind::ReadOnlyData + | SectionKind::ReadOnlyString + | SectionKind::ReadOnlyDataWithRel => xcoff::STYP_TEXT, + SectionKind::Data => xcoff::STYP_DATA, + SectionKind::UninitializedData => xcoff::STYP_BSS, + SectionKind::Tls => xcoff::STYP_TDATA, + SectionKind::UninitializedTls => xcoff::STYP_TBSS, + SectionKind::OtherString => xcoff::STYP_INFO, + SectionKind::Debug | SectionKind::DebugString => xcoff::STYP_DEBUG, + SectionKind::Other | SectionKind::Metadata => 0, + SectionKind::Note + | SectionKind::Linker + | SectionKind::Common + | SectionKind::Unknown + | SectionKind::TlsVariables + | SectionKind::Elf(_) => { + return SectionFlags::None; + } + } + .into(); + SectionFlags::Xcoff { s_flags } + } + + pub(crate) fn xcoff_symbol_flags(&self, symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> { + let n_sclass = match symbol.kind { + SymbolKind::File => xcoff::C_FILE, + SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => { + if symbol.is_local() { + xcoff::C_STAT + } else if symbol.weak { + xcoff::C_WEAKEXT + } else { + xcoff::C_EXT + } + } + SymbolKind::Section | SymbolKind::Label | SymbolKind::Unknown => { + return SymbolFlags::None; + } + }; + let (x_smtyp, x_smclas) = if n_sclass == xcoff::C_EXT + || n_sclass == xcoff::C_WEAKEXT + || n_sclass == xcoff::C_HIDEXT + { + let section_kind = if let SymbolSection::Section(id) = symbol.section { + self.sections[id.0].kind + } else { + SectionKind::Unknown + }; + match symbol.kind { + SymbolKind::Text => (xcoff::XTY_SD, xcoff::XMC_PR), + SymbolKind::Data => { + if section_kind == SectionKind::UninitializedData { + (xcoff::XTY_CM, xcoff::XMC_BS) + } else if section_kind == SectionKind::ReadOnlyData { + (xcoff::XTY_SD, xcoff::XMC_RO) + } else { + (xcoff::XTY_SD, xcoff::XMC_RW) + } + } + SymbolKind::Tls => { + if section_kind == SectionKind::UninitializedTls { + (xcoff::XTY_CM, xcoff::XMC_UL) + } else { + (xcoff::XTY_SD, xcoff::XMC_TL) + } + } + _ => { + return SymbolFlags::None; + } + } + } else { + (0, 0) + }; + SymbolFlags::Xcoff { + n_sclass, + x_smtyp, + x_smclas, + containing_csect: None, + } + } + + pub(crate) fn xcoff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { + let (kind, _encoding, size) = if let RelocationFlags::Generic { + kind, + encoding, + size, + } = reloc.flags + { + (kind, encoding, size) + } else { + return Ok(()); + }; + + let r_rtype = match kind { + RelocationKind::Absolute => xcoff::R_POS, + RelocationKind::Relative => xcoff::R_REL, + RelocationKind::Got => xcoff::R_TOC, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }; + let r_rsize = size - 1; + reloc.flags = RelocationFlags::Xcoff { r_rtype, r_rsize }; + Ok(()) + } + + pub(crate) fn xcoff_adjust_addend(&mut self, relocation: &mut Relocation) -> Result<bool> { + let r_rtype = if let RelocationFlags::Xcoff { r_rtype, .. } = relocation.flags { + r_rtype + } else { + return Err(Error(format!("invalid relocation flags {:?}", relocation))); + }; + if r_rtype == xcoff::R_REL { + relocation.addend += 4; + } + Ok(true) + } + + pub(crate) fn xcoff_relocation_size(&self, reloc: &Relocation) -> Result<u8> { + let r_rsize = if let RelocationFlags::Xcoff { r_rsize, .. } = reloc.flags { + r_rsize + } else { + return Err(Error(format!("unexpected relocation {:?}", reloc))); + }; + Ok(r_rsize + 1) + } + + pub(crate) fn xcoff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { + let is_64 = match self.architecture.address_size().unwrap() { + AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false, + AddressSize::U64 => true, + }; + + let (hdr_size, sechdr_size, rel_size, sym_size) = if is_64 { + ( + mem::size_of::<xcoff::FileHeader64>(), + mem::size_of::<xcoff::SectionHeader64>(), + mem::size_of::<xcoff::Rel64>(), + mem::size_of::<xcoff::Symbol64>(), + ) + } else { + ( + mem::size_of::<xcoff::FileHeader32>(), + mem::size_of::<xcoff::SectionHeader32>(), + mem::size_of::<xcoff::Rel32>(), + mem::size_of::<xcoff::Symbol32>(), + ) + }; + + // Calculate offsets and build strtab. + let mut offset = 0; + let mut strtab = StringTable::default(); + // We place the shared address 0 immediately after the section header table. + let mut address = 0; + + // XCOFF file header. + offset += hdr_size; + // Section headers. + offset += self.sections.len() * sechdr_size; + + // Calculate size of section data. + let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; + for (index, section) in self.sections.iter().enumerate() { + let len = section.data.len(); + let sectype = section.kind; + // Section address should be 0 for all sections except the .text, .data, and .bss sections. + if sectype == SectionKind::Data + || sectype == SectionKind::Text + || sectype == SectionKind::UninitializedData + { + section_offsets[index].address = address as u64; + address += len; + address = align(address, 4); + } else { + section_offsets[index].address = 0; + } + if len != 0 { + // Set the default section alignment as 4. + offset = align(offset, 4); + section_offsets[index].data_offset = offset; + offset += len; + } else { + section_offsets[index].data_offset = 0; + } + } + + // Calculate size of relocations. + for (index, section) in self.sections.iter().enumerate() { + let count = section.relocations.len(); + if count != 0 { + section_offsets[index].reloc_offset = offset; + offset += count * rel_size; + } else { + section_offsets[index].reloc_offset = 0; + } + } + + // Calculate size of symbols. + let mut file_str_id = None; + let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; + let mut symtab_count = 0; + for (index, symbol) in self.symbols.iter().enumerate() { + symbol_offsets[index].index = symtab_count; + symtab_count += 1; + + let SymbolFlags::Xcoff { + n_sclass, + x_smtyp, + x_smclas, + containing_csect, + } = self.symbol_flags(symbol) + else { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))); + }; + symbol_offsets[index].storage_class = n_sclass; + symbol_offsets[index].x_smtyp = x_smtyp; + symbol_offsets[index].x_smclas = x_smclas; + symbol_offsets[index].containing_csect = containing_csect; + + if n_sclass == xcoff::C_FILE { + if is_64 && file_str_id.is_none() { + file_str_id = Some(strtab.add(b".file")); + } + if symbol.name.len() > 8 { + symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); + } + } else if is_64 || symbol.name.len() > 8 { + symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); + } + + symbol_offsets[index].aux_count = 0; + match n_sclass { + xcoff::C_FILE => { + symbol_offsets[index].aux_count = 1; + symtab_count += 1; + } + xcoff::C_EXT | xcoff::C_WEAKEXT | xcoff::C_HIDEXT => { + symbol_offsets[index].aux_count = 1; + symtab_count += 1; + } + // TODO: support auxiliary entry for other types of symbol. + _ => {} + } + } + let symtab_offset = offset; + let symtab_len = symtab_count * sym_size; + offset += symtab_len; + + // Calculate size of strtab. + let strtab_offset = offset; + let mut strtab_data = Vec::new(); + // First 4 bytes of strtab are the length. + strtab.write(4, &mut strtab_data); + let strtab_len = strtab_data.len() + 4; + offset += strtab_len; + + // Start writing. + buffer + .reserve(offset) + .map_err(|_| Error(String::from("Cannot allocate buffer")))?; + + // Write file header. + if is_64 { + let header = xcoff::FileHeader64 { + f_magic: U16::new(BE, xcoff::MAGIC_64), + f_nscns: U16::new(BE, self.sections.len() as u16), + f_timdat: U32::new(BE, 0), + f_symptr: U64::new(BE, symtab_offset as u64), + f_nsyms: U32::new(BE, symtab_count as u32), + f_opthdr: U16::new(BE, 0), + f_flags: match self.flags { + FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags), + _ => U16::default(), + }, + }; + buffer.write(&header); + } else { + let header = xcoff::FileHeader32 { + f_magic: U16::new(BE, xcoff::MAGIC_32), + f_nscns: U16::new(BE, self.sections.len() as u16), + f_timdat: U32::new(BE, 0), + f_symptr: U32::new(BE, symtab_offset as u32), + f_nsyms: U32::new(BE, symtab_count as u32), + f_opthdr: U16::new(BE, 0), + f_flags: match self.flags { + FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags), + _ => U16::default(), + }, + }; + buffer.write(&header); + } + + // Write section headers. + for (index, section) in self.sections.iter().enumerate() { + let mut sectname = [0; 8]; + sectname + .get_mut(..section.name.len()) + .ok_or_else(|| { + Error(format!( + "section name `{}` is too long", + section.name().unwrap_or(""), + )) + })? + .copy_from_slice(§ion.name); + let SectionFlags::Xcoff { s_flags } = self.section_flags(section) else { + return Err(Error(format!( + "unimplemented section `{}` kind {:?}", + section.name().unwrap_or(""), + section.kind + ))); + }; + if is_64 { + let section_header = xcoff::SectionHeader64 { + s_name: sectname, + s_paddr: U64::new(BE, section_offsets[index].address), + // This field has the same value as the s_paddr field. + s_vaddr: U64::new(BE, section_offsets[index].address), + s_size: U64::new(BE, section.data.len() as u64), + s_scnptr: U64::new(BE, section_offsets[index].data_offset as u64), + s_relptr: U64::new(BE, section_offsets[index].reloc_offset as u64), + s_lnnoptr: U64::new(BE, 0), + s_nreloc: U32::new(BE, section.relocations.len() as u32), + s_nlnno: U32::new(BE, 0), + s_flags: U32::new(BE, s_flags), + s_reserve: U32::new(BE, 0), + }; + buffer.write(§ion_header); + } else { + let section_header = xcoff::SectionHeader32 { + s_name: sectname, + s_paddr: U32::new(BE, section_offsets[index].address as u32), + // This field has the same value as the s_paddr field. + s_vaddr: U32::new(BE, section_offsets[index].address as u32), + s_size: U32::new(BE, section.data.len() as u32), + s_scnptr: U32::new(BE, section_offsets[index].data_offset as u32), + s_relptr: U32::new(BE, section_offsets[index].reloc_offset as u32), + s_lnnoptr: U32::new(BE, 0), + // TODO: If more than 65,534 relocation entries are required, the field + // value will be 65535, and an STYP_OVRFLO section header will contain + // the actual count of relocation entries in the s_paddr field. + s_nreloc: U16::new(BE, section.relocations.len() as u16), + s_nlnno: U16::new(BE, 0), + s_flags: U32::new(BE, s_flags), + }; + buffer.write(§ion_header); + } + } + + // Write section data. + for (index, section) in self.sections.iter().enumerate() { + let len = section.data.len(); + if len != 0 { + write_align(buffer, 4); + debug_assert_eq!(section_offsets[index].data_offset, buffer.len()); + buffer.write_bytes(§ion.data); + } + } + + // Write relocations. + for (index, section) in self.sections.iter().enumerate() { + if !section.relocations.is_empty() { + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + for reloc in §ion.relocations { + let (r_rtype, r_rsize) = + if let RelocationFlags::Xcoff { r_rtype, r_rsize } = reloc.flags { + (r_rtype, r_rsize) + } else { + return Err(Error("invalid relocation flags".into())); + }; + if is_64 { + let xcoff_rel = xcoff::Rel64 { + r_vaddr: U64::new(BE, reloc.offset), + r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32), + r_rsize, + r_rtype, + }; + buffer.write(&xcoff_rel); + } else { + let xcoff_rel = xcoff::Rel32 { + r_vaddr: U32::new(BE, reloc.offset as u32), + r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32), + r_rsize, + r_rtype, + }; + buffer.write(&xcoff_rel); + } + } + } + } + + // Write symbols. + debug_assert_eq!(symtab_offset, buffer.len()); + for (index, symbol) in self.symbols.iter().enumerate() { + let n_value = if let SymbolSection::Section(id) = symbol.section { + section_offsets[id.0].address + symbol.value + } else { + symbol.value + }; + let n_scnum = match symbol.section { + SymbolSection::None => { + debug_assert_eq!(symbol.kind, SymbolKind::File); + xcoff::N_DEBUG + } + SymbolSection::Undefined | SymbolSection::Common => xcoff::N_UNDEF, + SymbolSection::Absolute => xcoff::N_ABS, + SymbolSection::Section(id) => id.0 as i16 + 1, + }; + let n_sclass = symbol_offsets[index].storage_class; + let n_type = if (symbol.scope == SymbolScope::Linkage) + && (n_sclass == xcoff::C_EXT + || n_sclass == xcoff::C_WEAKEXT + || n_sclass == xcoff::C_HIDEXT) + { + xcoff::SYM_V_HIDDEN + } else { + 0 + }; + let n_numaux = symbol_offsets[index].aux_count; + if is_64 { + let str_id = if n_sclass == xcoff::C_FILE { + file_str_id.unwrap() + } else { + symbol_offsets[index].str_id.unwrap() + }; + let xcoff_sym = xcoff::Symbol64 { + n_value: U64::new(BE, n_value), + n_offset: U32::new(BE, strtab.get_offset(str_id) as u32), + n_scnum: I16::new(BE, n_scnum), + n_type: U16::new(BE, n_type), + n_sclass, + n_numaux, + }; + buffer.write(&xcoff_sym); + } else { + let mut sym_name = [0; 8]; + if n_sclass == xcoff::C_FILE { + sym_name[..5].copy_from_slice(b".file"); + } else if symbol.name.len() <= 8 { + sym_name[..symbol.name.len()].copy_from_slice(&symbol.name[..]); + } else { + let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); + sym_name[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32)); + } + let xcoff_sym = xcoff::Symbol32 { + n_name: sym_name, + n_value: U32::new(BE, n_value as u32), + n_scnum: I16::new(BE, n_scnum), + n_type: U16::new(BE, n_type), + n_sclass, + n_numaux, + }; + buffer.write(&xcoff_sym); + } + // Generate auxiliary entries. + if n_sclass == xcoff::C_FILE { + debug_assert_eq!(n_numaux, 1); + let mut x_fname = [0; 8]; + if symbol.name.len() <= 8 { + x_fname[..symbol.name.len()].copy_from_slice(&symbol.name[..]); + } else { + let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); + x_fname[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32)); + } + if is_64 { + let file_aux = xcoff::FileAux64 { + x_fname, + x_fpad: Default::default(), + x_ftype: xcoff::XFT_FN, + x_freserve: Default::default(), + x_auxtype: xcoff::AUX_FILE, + }; + buffer.write(&file_aux); + } else { + let file_aux = xcoff::FileAux32 { + x_fname, + x_fpad: Default::default(), + x_ftype: xcoff::XFT_FN, + x_freserve: Default::default(), + }; + buffer.write(&file_aux); + } + } else if n_sclass == xcoff::C_EXT + || n_sclass == xcoff::C_WEAKEXT + || n_sclass == xcoff::C_HIDEXT + { + debug_assert_eq!(n_numaux, 1); + let x_smtyp = symbol_offsets[index].x_smtyp; + let x_smclas = symbol_offsets[index].x_smclas; + let scnlen = if let Some(containing_csect) = symbol_offsets[index].containing_csect + { + symbol_offsets[containing_csect.0].index as u64 + } else { + symbol.size + }; + if is_64 { + let csect_aux = xcoff::CsectAux64 { + x_scnlen_lo: U32::new(BE, (scnlen & 0xFFFFFFFF) as u32), + x_scnlen_hi: U32::new(BE, ((scnlen >> 32) & 0xFFFFFFFF) as u32), + x_parmhash: U32::new(BE, 0), + x_snhash: U16::new(BE, 0), + x_smtyp, + x_smclas, + pad: 0, + x_auxtype: xcoff::AUX_CSECT, + }; + buffer.write(&csect_aux); + } else { + let csect_aux = xcoff::CsectAux32 { + x_scnlen: U32::new(BE, scnlen as u32), + x_parmhash: U32::new(BE, 0), + x_snhash: U16::new(BE, 0), + x_smtyp, + x_smclas, + x_stab: U32::new(BE, 0), + x_snstab: U16::new(BE, 0), + }; + buffer.write(&csect_aux); + } + } + } + + // Write string table. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.write_bytes(&u32::to_be_bytes(strtab_len as u32)); + buffer.write_bytes(&strtab_data); + + debug_assert_eq!(offset, buffer.len()); + Ok(()) + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/src/xcoff.rs b/chromium_crates_io/vendor/object-v0_37/src/xcoff.rs new file mode 100644 index 0000000..ef54c93 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/src/xcoff.rs
@@ -0,0 +1,905 @@ +//! XCOFF definitions +//! +//! These definitions are independent of read/write support, although we do implement +//! some traits useful for those. +//! +//! This module is the equivalent of /usr/include/xcoff.h, and is based heavily on it. + +#![allow(missing_docs)] + +use crate::endian::{BigEndian as BE, I16, U16, U32, U64}; +use crate::pod::Pod; + +/// The header at the start of every 32-bit XCOFF file. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FileHeader32 { + /// Magic number. Must be 0x01DF. + pub f_magic: U16<BE>, + /// Number of sections. + pub f_nscns: U16<BE>, + /// Time and date of file creation. + pub f_timdat: U32<BE>, + /// Byte offset to symbol table start. + pub f_symptr: U32<BE>, + /// Number of entries in symbol table. + pub f_nsyms: U32<BE>, + /// Number of bytes in optional header + pub f_opthdr: U16<BE>, + /// Extra flags. + pub f_flags: U16<BE>, +} + +/// The header at the start of every 64-bit XCOFF file. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FileHeader64 { + /// Magic number. Must be 0x01F7. + pub f_magic: U16<BE>, + /// Number of sections. + pub f_nscns: U16<BE>, + /// Time and date of file creation + pub f_timdat: U32<BE>, + /// Byte offset to symbol table start. + pub f_symptr: U64<BE>, + /// Number of bytes in optional header + pub f_opthdr: U16<BE>, + /// Extra flags. + pub f_flags: U16<BE>, + /// Number of entries in symbol table. + pub f_nsyms: U32<BE>, +} + +// Values for `f_magic`. +// +/// the 64-bit mach magic number +pub const MAGIC_64: u16 = 0x01F7; +/// the 32-bit mach magic number +pub const MAGIC_32: u16 = 0x01DF; + +// Values for `f_flags`. +// +/// Indicates that the relocation information for binding has been removed from +/// the file. +pub const F_RELFLG: u16 = 0x0001; +/// Indicates that the file is executable. No unresolved external references exist. +pub const F_EXEC: u16 = 0x0002; +/// Indicates that line numbers have been stripped from the file by a utility program. +pub const F_LNNO: u16 = 0x0004; +/// Indicates that the file was profiled with the fdpr command. +pub const F_FDPR_PROF: u16 = 0x0010; +/// Indicates that the file was reordered with the fdpr command. +pub const F_FDPR_OPTI: u16 = 0x0020; +/// Indicates that the file uses Very Large Program Support. +pub const F_DSA: u16 = 0x0040; +/// Indicates that one of the members of the auxiliary header specifying the +/// medium page sizes is non-zero. +pub const F_VARPG: u16 = 0x0100; +/// Indicates the file is dynamically loadable and executable. External references +/// are resolved by way of imports, and the file might contain exports and loader +/// relocation. +pub const F_DYNLOAD: u16 = 0x1000; +/// Indicates the file is a shared object (shared library). The file is separately +/// loadable. That is, it is not normally bound with other objects, and its loader +/// exports symbols are used as automatic import symbols for other object files. +pub const F_SHROBJ: u16 = 0x2000; +/// If the object file is a member of an archive, it can be loaded by the system +/// loader, but the member is ignored by the binder. If the object file is not in +/// an archive, this flag has no effect. +pub const F_LOADONLY: u16 = 0x4000; + +/// The auxiliary header immediately following file header. If the value of the +/// f_opthdr field in the file header is 0, the auxiliary header does not exist. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AuxHeader32 { + /// Flags. + pub o_mflag: U16<BE>, + /// Version. + pub o_vstamp: U16<BE>, + /// Text size in bytes. + pub o_tsize: U32<BE>, + /// Initialized data size in bytes. + pub o_dsize: U32<BE>, + /// Uninitialized data size in bytes. + pub o_bsize: U32<BE>, + /// Entry point descriptor (virtual address). + pub o_entry: U32<BE>, + /// Base address of text (virtual address). + pub o_text_start: U32<BE>, + /// Base address of data (virtual address). + pub o_data_start: U32<BE>, + /// Address of TOC anchor. + pub o_toc: U32<BE>, + /// Section number for entry point. + pub o_snentry: U16<BE>, + /// Section number for .text. + pub o_sntext: U16<BE>, + /// Section number for .data. + pub o_sndata: U16<BE>, + /// Section number for TOC. + pub o_sntoc: U16<BE>, + /// Section number for loader data. + pub o_snloader: U16<BE>, + /// Section number for .bss. + pub o_snbss: U16<BE>, + /// Maximum alignment for .text. + pub o_algntext: U16<BE>, + /// Maximum alignment for .data. + pub o_algndata: U16<BE>, + /// Module type field. + pub o_modtype: U16<BE>, + /// Bit flags - cpu types of objects. + pub o_cpuflag: u8, + /// Reserved for CPU type. + pub o_cputype: u8, + /// Maximum stack size allowed (bytes). + pub o_maxstack: U32<BE>, + /// Maximum data size allowed (bytes). + pub o_maxdata: U32<BE>, + /// Reserved for debuggers. + pub o_debugger: U32<BE>, + /// Requested text page size. + pub o_textpsize: u8, + /// Requested data page size. + pub o_datapsize: u8, + /// Requested stack page size. + pub o_stackpsize: u8, + /// Flags and thread-local storage alignment. + pub o_flags: u8, + /// Section number for .tdata. + pub o_sntdata: U16<BE>, + /// Section number for .tbss. + pub o_sntbss: U16<BE>, +} + +/// The auxiliary header immediately following file header. If the value of the +/// f_opthdr field in the file header is 0, the auxiliary header does not exist. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AuxHeader64 { + /// Flags. + pub o_mflag: U16<BE>, + /// Version. + pub o_vstamp: U16<BE>, + /// Reserved for debuggers. + pub o_debugger: U32<BE>, + /// Base address of text (virtual address). + pub o_text_start: U64<BE>, + /// Base address of data (virtual address). + pub o_data_start: U64<BE>, + /// Address of TOC anchor. + pub o_toc: U64<BE>, + /// Section number for entry point. + pub o_snentry: U16<BE>, + /// Section number for .text. + pub o_sntext: U16<BE>, + /// Section number for .data. + pub o_sndata: U16<BE>, + /// Section number for TOC. + pub o_sntoc: U16<BE>, + /// Section number for loader data. + pub o_snloader: U16<BE>, + /// Section number for .bss. + pub o_snbss: U16<BE>, + /// Maximum alignment for .text. + pub o_algntext: U16<BE>, + /// Maximum alignment for .data. + pub o_algndata: U16<BE>, + /// Module type field. + pub o_modtype: U16<BE>, + /// Bit flags - cpu types of objects. + pub o_cpuflag: u8, + /// Reserved for CPU type. + pub o_cputype: u8, + /// Requested text page size. + pub o_textpsize: u8, + /// Requested data page size. + pub o_datapsize: u8, + /// Requested stack page size. + pub o_stackpsize: u8, + /// Flags and thread-local storage alignment. + pub o_flags: u8, + /// Text size in bytes. + pub o_tsize: U64<BE>, + /// Initialized data size in bytes. + pub o_dsize: U64<BE>, + /// Uninitialized data size in bytes. + pub o_bsize: U64<BE>, + /// Entry point descriptor (virtual address). + pub o_entry: U64<BE>, + /// Maximum stack size allowed (bytes). + pub o_maxstack: U64<BE>, + /// Maximum data size allowed (bytes). + pub o_maxdata: U64<BE>, + /// Section number for .tdata. + pub o_sntdata: U16<BE>, + /// Section number for .tbss. + pub o_sntbss: U16<BE>, + /// XCOFF64 flags. + pub o_x64flags: U16<BE>, + /// Reserved. + pub o_resv3a: U16<BE>, + /// Reserved. + pub o_resv3: [U32<BE>; 2], +} + +/// Some AIX programs generate auxiliary headers for 32-bit object files that +/// end after the data_start field. +pub const AOUTHSZ_SHORT: u16 = 28; + +/// Section header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SectionHeader32 { + /// Section name. + pub s_name: [u8; 8], + /// Physical address. + pub s_paddr: U32<BE>, + /// Virtual address (same as physical address). + pub s_vaddr: U32<BE>, + /// Section size. + pub s_size: U32<BE>, + /// Offset in file to raw data for section. + pub s_scnptr: U32<BE>, + /// Offset in file to relocation entries for section. + pub s_relptr: U32<BE>, + /// Offset in file to line number entries for section. + pub s_lnnoptr: U32<BE>, + /// Number of relocation entries. + pub s_nreloc: U16<BE>, + /// Number of line number entries. + pub s_nlnno: U16<BE>, + /// Flags to define the section type. + pub s_flags: U32<BE>, +} + +/// Section header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SectionHeader64 { + /// Section name. + pub s_name: [u8; 8], + /// Physical address. + pub s_paddr: U64<BE>, + /// Virtual address (same as physical address). + pub s_vaddr: U64<BE>, + /// Section size. + pub s_size: U64<BE>, + /// Offset in file to raw data for section. + pub s_scnptr: U64<BE>, + /// Offset in file to relocation entries for section. + pub s_relptr: U64<BE>, + /// Offset in file to line number entries for section. + pub s_lnnoptr: U64<BE>, + /// Number of relocation entries. + pub s_nreloc: U32<BE>, + /// Number of line number entries. + pub s_nlnno: U32<BE>, + /// Flags to define the section type. + pub s_flags: U32<BE>, + /// Reserved. + pub s_reserve: U32<BE>, +} + +// Values for `s_flags`. +// +/// "regular" section +pub const STYP_REG: u16 = 0x00; +/// Specifies a pad section. A section of this type is used to provide alignment +/// padding between sections within an XCOFF executable object file. This section +/// header type is obsolete since padding is allowed in an XCOFF file without a +/// corresponding pad section header. +pub const STYP_PAD: u16 = 0x08; +/// Specifies a DWARF debugging section, which provide source file and symbol +/// information for the symbolic debugger. +pub const STYP_DWARF: u16 = 0x10; +/// Specifies an executable text (code) section. A section of this type contains +/// the executable instructions of a program. +pub const STYP_TEXT: u16 = 0x20; +/// Specifies an initialized data section. A section of this type contains the +/// initialized data and the TOC of a program. +pub const STYP_DATA: u16 = 0x40; +/// Specifies an uninitialized data section. A section header of this type +/// defines the uninitialized data of a program. +pub const STYP_BSS: u16 = 0x80; +/// Specifies an exception section. A section of this type provides information +/// to identify the reason that a trap or exception occurred within an executable +/// object program. +pub const STYP_EXCEPT: u16 = 0x0100; +/// Specifies a comment section. A section of this type provides comments or data +/// to special processing utility programs. +pub const STYP_INFO: u16 = 0x0200; +/// Specifies an initialized thread-local data section. +pub const STYP_TDATA: u16 = 0x0400; +/// Specifies an uninitialized thread-local data section. +pub const STYP_TBSS: u16 = 0x0800; +/// Specifies a loader section. A section of this type contains object file +/// information for the system loader to load an XCOFF executable. The information +/// includes imported symbols, exported symbols, relocation data, type-check +/// information, and shared object names. +pub const STYP_LOADER: u16 = 0x1000; +/// Specifies a debug section. A section of this type contains stabstring +/// information used by the symbolic debugger. +pub const STYP_DEBUG: u16 = 0x2000; +/// Specifies a type-check section. A section of this type contains +/// parameter/argument type-check strings used by the binder. +pub const STYP_TYPCHK: u16 = 0x4000; +/// Specifies a relocation or line-number field overflow section. A section +/// header of this type contains the count of relocation entries and line +/// number entries for some other section. This section header is required +/// when either of the counts exceeds 65,534. +pub const STYP_OVRFLO: u16 = 0x8000; + +pub const SSUBTYP_DWINFO: u32 = 0x10000; +pub const SSUBTYP_DWLINE: u32 = 0x20000; +pub const SSUBTYP_DWPBNMS: u32 = 0x30000; +pub const SSUBTYP_DWPBTYP: u32 = 0x40000; +pub const SSUBTYP_DWARNGE: u32 = 0x50000; +pub const SSUBTYP_DWABREV: u32 = 0x60000; +pub const SSUBTYP_DWSTR: u32 = 0x70000; +pub const SSUBTYP_DWRNGES: u32 = 0x80000; +pub const SSUBTYP_DWLOC: u32 = 0x90000; +pub const SSUBTYP_DWFRAME: u32 = 0xA0000; +pub const SSUBTYP_DWMAC: u32 = 0xB0000; + +pub const SIZEOF_SYMBOL: usize = 18; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SymbolBytes(pub [u8; SIZEOF_SYMBOL]); + +/// Symbol table entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Symbol32 { + /// Symbol name. + /// + /// If first 4 bytes are 0, then second 4 bytes are offset into string table. + pub n_name: [u8; 8], + /// Symbol value; storage class-dependent. + pub n_value: U32<BE>, + /// Section number of symbol. + pub n_scnum: I16<BE>, + /// Basic and derived type specification. + pub n_type: U16<BE>, + /// Storage class of symbol. + pub n_sclass: u8, + /// Number of auxiliary entries. + pub n_numaux: u8, +} + +/// Symbol table entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Symbol64 { + /// Symbol value; storage class-dependent. + pub n_value: U64<BE>, + /// Offset of the name in string table or .debug section. + pub n_offset: U32<BE>, + /// Section number of symbol. + pub n_scnum: I16<BE>, + /// Basic and derived type specification. + pub n_type: U16<BE>, + /// Storage class of symbol. + pub n_sclass: u8, + /// Number of auxiliary entries. + pub n_numaux: u8, +} + +// Values for `n_scnum`. +// +/// A special symbolic debugging symbol. +pub const N_DEBUG: i16 = -2; +/// An absolute symbol. The symbol has a value but is not relocatable. +pub const N_ABS: i16 = -1; +/// An undefined external symbol. +pub const N_UNDEF: i16 = 0; + +// Values for `n_type`. +// +/// Values for visibility as they would appear when encoded in the high 4 bits +/// of the 16-bit unsigned n_type field of symbol table entries. Valid for +/// 32-bit XCOFF only when the o_vstamp in the auxiliary header is greater than 1. +pub const SYM_V_MASK: u16 = 0xF000; +pub const SYM_V_INTERNAL: u16 = 0x1000; +pub const SYM_V_HIDDEN: u16 = 0x2000; +pub const SYM_V_PROTECTED: u16 = 0x3000; +pub const SYM_V_EXPORTED: u16 = 0x4000; + +// Values for `n_sclass`. +// +// Storage classes used for symbolic debugging symbols. +// +/// Source file name and compiler information. +pub const C_FILE: u8 = 103; +/// Beginning of include file. +pub const C_BINCL: u8 = 108; +/// Ending of include file. +pub const C_EINCL: u8 = 109; +/// Global variable. +pub const C_GSYM: u8 = 128; +/// Statically allocated symbol. +pub const C_STSYM: u8 = 133; +/// Beginning of common block. +pub const C_BCOMM: u8 = 135; +/// End of common block. +pub const C_ECOMM: u8 = 137; +/// Alternate entry. +pub const C_ENTRY: u8 = 141; +/// Beginning of static block. +pub const C_BSTAT: u8 = 143; +/// End of static block. +pub const C_ESTAT: u8 = 144; +/// Global thread-local variable. +pub const C_GTLS: u8 = 145; +/// Static thread-local variable. +pub const C_STTLS: u8 = 146; +/// DWARF section symbol. +pub const C_DWARF: u8 = 112; +// +// Storage classes used for absolute symbols. +// +/// Automatic variable allocated on stack. +pub const C_LSYM: u8 = 129; +/// Argument to subroutine allocated on stack. +pub const C_PSYM: u8 = 130; +/// Register variable. +pub const C_RSYM: u8 = 131; +/// Argument to function or procedure stored in register. +pub const C_RPSYM: u8 = 132; +/// Local member of common block. +pub const C_ECOML: u8 = 136; +/// Function or procedure. +pub const C_FUN: u8 = 142; +// +// Storage classes used for undefined external symbols or symbols of general sections. +// +/// External symbol. +pub const C_EXT: u8 = 2; +/// Weak external symbol. +pub const C_WEAKEXT: u8 = 111; +// +// Storage classes used for symbols of general sections. +// +/// Symbol table entry marked for deletion. +pub const C_NULL: u8 = 0; +/// Static. +pub const C_STAT: u8 = 3; +/// Beginning or end of inner block. +pub const C_BLOCK: u8 = 100; +/// Beginning or end of function. +pub const C_FCN: u8 = 101; +/// Un-named external symbol. +pub const C_HIDEXT: u8 = 107; +/// Comment string in .info section. +pub const C_INFO: u8 = 110; +/// Declaration of object (type). +pub const C_DECL: u8 = 140; +// +// Storage classes - Obsolete/Undocumented. +// +/// Automatic variable. +pub const C_AUTO: u8 = 1; +/// Register variable. +pub const C_REG: u8 = 4; +/// External definition. +pub const C_EXTDEF: u8 = 5; +/// Label. +pub const C_LABEL: u8 = 6; +/// Undefined label. +pub const C_ULABEL: u8 = 7; +/// Member of structure. +pub const C_MOS: u8 = 8; +/// Function argument. +pub const C_ARG: u8 = 9; +/// Structure tag. +pub const C_STRTAG: u8 = 10; +/// Member of union. +pub const C_MOU: u8 = 11; +/// Union tag. +pub const C_UNTAG: u8 = 12; +/// Type definition. +pub const C_TPDEF: u8 = 13; +/// Undefined static. +pub const C_USTATIC: u8 = 14; +/// Enumeration tag. +pub const C_ENTAG: u8 = 15; +/// Member of enumeration. +pub const C_MOE: u8 = 16; +/// Register parameter. +pub const C_REGPARM: u8 = 17; +/// Bit field. +pub const C_FIELD: u8 = 18; +/// End of structure. +pub const C_EOS: u8 = 102; +/// Duplicate tag. +pub const C_ALIAS: u8 = 105; +/// Special storage class for external. +pub const C_HIDDEN: u8 = 106; +/// Physical end of function. +pub const C_EFCN: u8 = 255; +/// Reserved. +pub const C_TCSYM: u8 = 134; + +/// File Auxiliary Entry for C_FILE Symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FileAux32 { + /// The source file name or compiler-related string. + /// + /// If first 4 bytes are 0, then second 4 bytes are offset into string table. + pub x_fname: [u8; 8], + /// Pad size for file name. + pub x_fpad: [u8; 6], + /// The source-file string type. + pub x_ftype: u8, + /// Reserved. + pub x_freserve: [u8; 3], +} + +/// File Auxiliary Entry for C_FILE Symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FileAux64 { + /// The source file name or compiler-related string. + /// + /// If first 4 bytes are 0, then second 4 bytes are offset into string table. + pub x_fname: [u8; 8], + /// Pad size for file name. + pub x_fpad: [u8; 6], + /// The source-file string type. + pub x_ftype: u8, + /// Reserved. + pub x_freserve: [u8; 2], + /// Specifies the type of auxiliary entry. Contains _AUX_FILE for this auxiliary entry. + pub x_auxtype: u8, +} + +// Values for `x_ftype`. +// +/// Specifies the source-file name. +pub const XFT_FN: u8 = 0; +/// Specifies the compiler time stamp. +pub const XFT_CT: u8 = 1; +/// Specifies the compiler version number. +pub const XFT_CV: u8 = 2; +/// Specifies compiler-defined information. +pub const XFT_CD: u8 = 128; + +/// Csect auxiliary entry for C_EXT, C_WEAKEXT, and C_HIDEXT symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct CsectAux32 { + /// Section length. + pub x_scnlen: U32<BE>, + /// Offset of parameter type-check hash in .typchk section. + pub x_parmhash: U32<BE>, + /// .typchk section number. + pub x_snhash: U16<BE>, + /// Symbol alignment and type. + pub x_smtyp: u8, + /// Storage mapping class. + pub x_smclas: u8, + /// Reserved. + pub x_stab: U32<BE>, + /// x_snstab. + pub x_snstab: U16<BE>, +} + +/// Csect auxiliary entry for C_EXT, C_WEAKEXT, and C_HIDEXT symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct CsectAux64 { + /// Low 4 bytes of section length. + pub x_scnlen_lo: U32<BE>, + /// Offset of parameter type-check hash in .typchk section. + pub x_parmhash: U32<BE>, + /// .typchk section number. + pub x_snhash: U16<BE>, + /// Symbol alignment and type. + pub x_smtyp: u8, + /// Storage mapping class. + pub x_smclas: u8, + /// High 4 bytes of section length. + pub x_scnlen_hi: U32<BE>, + /// Reserved. + pub pad: u8, + /// Contains _AUX_CSECT; indicates type of auxiliary entry. + pub x_auxtype: u8, +} + +// Values for `x_smtyp`. +// +/// External reference. +pub const XTY_ER: u8 = 0; +/// Csect definition for initialized storage. +pub const XTY_SD: u8 = 1; +/// Defines an entry point to an initialized csect. +pub const XTY_LD: u8 = 2; +/// Common csect definition. For uninitialized storage. +pub const XTY_CM: u8 = 3; + +// Values for `x_smclas`. +// +// READ ONLY CLASSES +// +/// Program Code +pub const XMC_PR: u8 = 0; +/// Read Only Constant +pub const XMC_RO: u8 = 1; +/// Debug Dictionary Table +pub const XMC_DB: u8 = 2; +/// Global Linkage (Interfile Interface Code) +pub const XMC_GL: u8 = 6; +/// Extended Operation (Pseudo Machine Instruction) +pub const XMC_XO: u8 = 7; +/// Supervisor Call (32-bit process only) +pub const XMC_SV: u8 = 8; +/// Supervisor Call for 64-bit process +pub const XMC_SV64: u8 = 17; +/// Supervisor Call for both 32- and 64-bit processes +pub const XMC_SV3264: u8 = 18; +/// Traceback Index csect +pub const XMC_TI: u8 = 12; +/// Traceback Table csect +pub const XMC_TB: u8 = 13; +// +// READ WRITE CLASSES +// +/// Read Write Data +pub const XMC_RW: u8 = 5; +/// TOC Anchor for TOC Addressability +pub const XMC_TC0: u8 = 15; +/// General TOC item +pub const XMC_TC: u8 = 3; +/// Scalar data item in the TOC +pub const XMC_TD: u8 = 16; +/// Descriptor csect +pub const XMC_DS: u8 = 10; +/// Unclassified - Treated as Read Write +pub const XMC_UA: u8 = 4; +/// BSS class (uninitialized static internal) +pub const XMC_BS: u8 = 9; +/// Un-named Fortran Common +pub const XMC_UC: u8 = 11; +/// Initialized thread-local variable +pub const XMC_TL: u8 = 20; +/// Uninitialized thread-local variable +pub const XMC_UL: u8 = 21; +/// Symbol mapped at the end of TOC +pub const XMC_TE: u8 = 22; + +/// Function auxiliary entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FunAux32 { + /// File offset to exception table entry. + pub x_exptr: U32<BE>, + /// Size of function in bytes. + pub x_fsize: U32<BE>, + /// File pointer to line number + pub x_lnnoptr: U32<BE>, + /// Symbol table index of next entry beyond this function. + pub x_endndx: U32<BE>, + /// Pad + pub pad: U16<BE>, +} + +/// Function auxiliary entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FunAux64 { + /// File pointer to line number + pub x_lnnoptr: U64<BE>, + /// Size of function in bytes. + pub x_fsize: U32<BE>, + /// Symbol table index of next entry beyond this function. + pub x_endndx: U32<BE>, + /// Pad + pub pad: u8, + /// Contains _AUX_FCN; Type of auxiliary entry. + pub x_auxtype: u8, +} + +/// Exception auxiliary entry. (XCOFF64 only) +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ExpAux { + /// File offset to exception table entry. + pub x_exptr: U64<BE>, + /// Size of function in bytes. + pub x_fsize: U32<BE>, + /// Symbol table index of next entry beyond this function. + pub x_endndx: U32<BE>, + /// Pad + pub pad: u8, + /// Contains _AUX_EXCEPT; Type of auxiliary entry + pub x_auxtype: u8, +} + +/// Block auxiliary entry for the C_BLOCK and C_FCN Symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct BlockAux32 { + /// Reserved. + pub pad: [u8; 2], + /// High-order 2 bytes of the source line number. + pub x_lnnohi: U16<BE>, + /// Low-order 2 bytes of the source line number. + pub x_lnnolo: U16<BE>, + /// Reserved. + pub pad2: [u8; 12], +} + +/// Block auxiliary entry for the C_BLOCK and C_FCN Symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct BlockAux64 { + /// Source line number. + pub x_lnno: U32<BE>, + /// Reserved. + pub pad: [u8; 13], + /// Contains _AUX_SYM; Type of auxiliary entry. + pub x_auxtype: u8, +} + +/// Section auxiliary entry for the C_STAT Symbol. (XCOFF32 Only) +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct StatAux { + /// Section length. + pub x_scnlen: U32<BE>, + /// Number of relocation entries. + pub x_nreloc: U16<BE>, + /// Number of line numbers. + pub x_nlinno: U16<BE>, + /// Reserved. + pub pad: [u8; 10], +} + +/// Section auxiliary entry Format for C_DWARF symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DwarfAux32 { + /// Length of portion of section represented by symbol. + pub x_scnlen: U32<BE>, + /// Reserved. + pub pad: [u8; 4], + /// Number of relocation entries in section. + pub x_nreloc: U32<BE>, + /// Reserved. + pub pad2: [u8; 6], +} + +/// Section auxiliary entry Format for C_DWARF symbols. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DwarfAux64 { + /// Length of portion of section represented by symbol. + pub x_scnlen: U64<BE>, + /// Number of relocation entries in section. + pub x_nreloc: U64<BE>, + /// Reserved. + pub pad: u8, + /// Contains _AUX_SECT; Type of Auxiliary entry. + pub x_auxtype: u8, +} + +// Values for `x_auxtype` +// +/// Identifies an exception auxiliary entry. +pub const AUX_EXCEPT: u8 = 255; +/// Identifies a function auxiliary entry. +pub const AUX_FCN: u8 = 254; +/// Identifies a symbol auxiliary entry. +pub const AUX_SYM: u8 = 253; +/// Identifies a file auxiliary entry. +pub const AUX_FILE: u8 = 252; +/// Identifies a csect auxiliary entry. +pub const AUX_CSECT: u8 = 251; +/// Identifies a SECT auxiliary entry. +pub const AUX_SECT: u8 = 250; + +/// Relocation table entry +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Rel32 { + /// Virtual address (position) in section to be relocated. + pub r_vaddr: U32<BE>, + /// Symbol table index of item that is referenced. + pub r_symndx: U32<BE>, + /// Relocation size and information. + pub r_rsize: u8, + /// Relocation type. + pub r_rtype: u8, +} + +/// Relocation table entry +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Rel64 { + /// Virtual address (position) in section to be relocated. + pub r_vaddr: U64<BE>, + /// Symbol table index of item that is referenced. + pub r_symndx: U32<BE>, + /// Relocation size and information. + pub r_rsize: u8, + /// Relocation type. + pub r_rtype: u8, +} + +// Values for `r_rtype`. +// +/// Positive relocation. +pub const R_POS: u8 = 0x00; +/// Positive indirect load relocation. +pub const R_RL: u8 = 0x0c; +/// Positive load address relocation. Modifiable instruction. +pub const R_RLA: u8 = 0x0d; +/// Negative relocation. +pub const R_NEG: u8 = 0x01; +/// Relative to self relocation. +pub const R_REL: u8 = 0x02; +/// Relative to the TOC relocation. +pub const R_TOC: u8 = 0x03; +/// TOC relative indirect load relocation. +pub const R_TRL: u8 = 0x12; +/// Relative to the TOC or to the thread-local storage base relocation. +pub const R_TRLA: u8 = 0x13; +/// Global linkage-external TOC address relocation. +pub const R_GL: u8 = 0x05; +/// Local object TOC address relocation. +pub const R_TCL: u8 = 0x06; +/// A non-relocating relocation. +pub const R_REF: u8 = 0x0f; +/// Branch absolute relocation. References a non-modifiable instruction. +pub const R_BA: u8 = 0x08; +/// Branch relative to self relocation. References a non-modifiable instruction. +pub const R_BR: u8 = 0x0a; +/// Branch absolute relocation. References a modifiable instruction. +pub const R_RBA: u8 = 0x18; +/// Branch relative to self relocation. References a modifiable instruction. +pub const R_RBR: u8 = 0x1a; +/// General-dynamic reference to TLS symbol. +pub const R_TLS: u8 = 0x20; +/// Initial-exec reference to TLS symbol. +pub const R_TLS_IE: u8 = 0x21; +/// Local-dynamic reference to TLS symbol. +pub const R_TLS_LD: u8 = 0x22; +/// Local-exec reference to TLS symbol. +pub const R_TLS_LE: u8 = 0x23; +/// Module reference to TLS. +pub const R_TLSM: u8 = 0x24; +/// Module reference to the local TLS storage. +pub const R_TLSML: u8 = 0x25; +/// Relative to TOC upper. +pub const R_TOCU: u8 = 0x30; +/// Relative to TOC lower. +pub const R_TOCL: u8 = 0x31; + +unsafe_impl_pod!( + FileHeader32, + FileHeader64, + AuxHeader32, + AuxHeader64, + SectionHeader32, + SectionHeader64, + SymbolBytes, + Symbol32, + Symbol64, + FileAux32, + FileAux64, + CsectAux32, + CsectAux64, + FunAux32, + FunAux64, + ExpAux, + BlockAux32, + BlockAux64, + StatAux, + DwarfAux32, + DwarfAux64, + Rel32, + Rel64, +);
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/build/elf.rs b/chromium_crates_io/vendor/object-v0_37/tests/build/elf.rs new file mode 100644 index 0000000..53979ee --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/build/elf.rs
@@ -0,0 +1,254 @@ +use object::{build, elf}; + +// Test that offset 0 is supported for SHT_NOBITS sections. +#[test] +fn test_nobits_offset() { + let mut builder = build::elf::Builder::new(object::Endianness::Little, true); + builder.header.e_type = elf::ET_EXEC; + builder.header.e_phoff = 0x40; + + let section = builder.sections.add(); + section.name = b".shstrtab"[..].into(); + section.sh_type = elf::SHT_STRTAB; + section.data = build::elf::SectionData::SectionString; + + let section = builder.sections.add(); + section.name = b".bss"[..].into(); + section.sh_type = elf::SHT_NOBITS; + section.sh_flags = (elf::SHF_ALLOC | elf::SHF_WRITE) as u64; + section.sh_addr = 0x1000; + section.sh_offset = 0; + section.sh_size = 0x1000; + section.sh_addralign = 16; + section.data = build::elf::SectionData::UninitializedData(0x1000); + let section_id = section.id(); + + let segment = builder.segments.add(); + segment.p_type = elf::PT_LOAD; + segment.p_flags = elf::PF_R | elf::PF_W; + segment.p_offset = 0x1000; + segment.p_vaddr = 0x1000; + segment.p_paddr = 0x1000; + segment.p_filesz = 0; + segment.p_memsz = 0x1000; + segment.p_align = 16; + segment.sections.push(section_id); + + let mut buf = Vec::new(); + builder.write(&mut buf).unwrap(); +} + +// Test that we can read and write a file with no dynamic string table. +#[test] +fn test_no_dynstr() { + let mut builder = build::elf::Builder::new(object::Endianness::Little, true); + builder.header.e_type = elf::ET_EXEC; + builder.header.e_machine = elf::EM_X86_64; + builder.header.e_phoff = 0x40; + + let section = builder.sections.add(); + section.name = b".shstrtab"[..].into(); + section.sh_type = elf::SHT_STRTAB; + section.data = build::elf::SectionData::SectionString; + + let section = builder.sections.add(); + section.name = b".dynsym"[..].into(); + section.sh_type = elf::SHT_DYNSYM; + section.sh_flags = elf::SHF_ALLOC as u64; + section.sh_addralign = 8; + section.data = build::elf::SectionData::DynamicSymbol; + let dynsym_id = section.id(); + + let section = builder.sections.add(); + section.name = b".rela.dyn"[..].into(); + section.sh_type = elf::SHT_RELA; + section.sh_flags = elf::SHF_ALLOC as u64; + section.sh_addralign = 8; + section.data = + build::elf::SectionData::DynamicRelocation(vec![build::elf::DynamicRelocation { + r_offset: 0x1000, + symbol: None, + r_type: elf::R_X86_64_64, + r_addend: 0x300, + }]); + let rela_id = section.id(); + + builder.set_section_sizes(); + + let segment = builder.segments.add(); + segment.p_type = elf::PT_LOAD; + segment.p_flags = elf::PF_R; + segment.p_filesz = 0x1000; + segment.p_memsz = 0x1000; + segment.p_align = 8; + segment.append_section(builder.sections.get_mut(dynsym_id)); + segment.append_section(builder.sections.get_mut(rela_id)); + + let mut buf = Vec::new(); + builder.write(&mut buf).unwrap(); + + let builder = build::elf::Builder::read(&*buf).unwrap(); + assert_eq!(builder.sections.count(), 3); + assert_eq!(builder.segments.count(), 1); + for section in &builder.sections { + match §ion.data { + build::elf::SectionData::DynamicSymbol => { + assert_eq!(section.sh_offset, 0x1000); + } + build::elf::SectionData::DynamicRelocation(rela) => { + assert_eq!(section.sh_offset, 0x1018); + assert_eq!(rela.len(), 1); + } + _ => {} + } + } +} + +#[test] +fn test_attribute() { + let mut builder = build::elf::Builder::new(object::Endianness::Little, true); + builder.header.e_type = elf::ET_EXEC; + builder.header.e_machine = elf::EM_X86_64; + builder.header.e_phoff = 0x40; + + let section = builder.sections.add(); + section.name = b".shstrtab"[..].into(); + section.sh_type = elf::SHT_STRTAB; + section.data = build::elf::SectionData::SectionString; + + let attributes = build::elf::AttributesSection { + subsections: vec![build::elf::AttributesSubsection { + vendor: b"GNU"[..].into(), + subsubsections: vec![ + (build::elf::AttributesSubsubsection { + tag: build::elf::AttributeTag::File, + data: b"123"[..].into(), + }), + ], + }], + }; + let section = builder.sections.add(); + section.name = b".gnu.attributes"[..].into(); + section.sh_type = elf::SHT_GNU_ATTRIBUTES; + section.sh_addralign = 8; + section.data = build::elf::SectionData::Attributes(attributes); + + let mut buf = Vec::new(); + builder.write(&mut buf).unwrap(); + + let builder = build::elf::Builder::read(&*buf).unwrap(); + assert_eq!(builder.sections.count(), 2); + for section in &builder.sections { + if let build::elf::SectionData::Attributes(attributes) = §ion.data { + assert_eq!(attributes.subsections.len(), 1); + assert_eq!(attributes.subsections[0].vendor.as_slice(), b"GNU"); + assert_eq!(attributes.subsections[0].subsubsections.len(), 1); + assert_eq!( + attributes.subsections[0].subsubsections[0].tag, + build::elf::AttributeTag::File + ); + assert_eq!( + attributes.subsections[0].subsubsections[0].data.as_slice(), + b"123" + ); + } + } +} + +#[test] +fn test_dynsym() { + let mut builder = build::elf::Builder::new(object::Endianness::Little, true); + builder.header.e_type = elf::ET_EXEC; + builder.header.e_machine = elf::EM_X86_64; + builder.header.e_phoff = 0x40; + + let section = builder.sections.add(); + section.name = b".shstrtab"[..].into(); + section.sh_type = elf::SHT_STRTAB; + section.data = build::elf::SectionData::SectionString; + + let section = builder.sections.add(); + section.name = b".text"[..].into(); + section.sh_type = elf::SHT_PROGBITS; + section.sh_flags = (elf::SHF_ALLOC | elf::SHF_EXECINSTR) as u64; + section.sh_addralign = 16; + section.data = build::elf::SectionData::Data(vec![0xcc; 100].into()); + let text_id = section.id(); + + let section = builder.sections.add(); + section.name = b".dynsym"[..].into(); + section.sh_type = elf::SHT_DYNSYM; + section.sh_flags = elf::SHF_ALLOC as u64; + section.sh_addralign = 8; + section.data = build::elf::SectionData::DynamicSymbol; + let dynsym_id = section.id(); + + let section = builder.sections.add(); + section.name = b".dynstr"[..].into(); + section.sh_type = elf::SHT_STRTAB; + section.sh_flags = elf::SHF_ALLOC as u64; + section.sh_addralign = 1; + section.data = build::elf::SectionData::DynamicString; + let dynstr_id = section.id(); + + let section = builder.sections.add(); + section.name = b".gnu.hash"[..].into(); + section.sh_type = elf::SHT_GNU_HASH; + section.sh_flags = elf::SHF_ALLOC as u64; + section.sh_addralign = 8; + section.data = build::elf::SectionData::GnuHash; + let gnu_hash_id = section.id(); + builder.gnu_hash_bloom_shift = 1; + builder.gnu_hash_bloom_count = 1; + builder.gnu_hash_bucket_count = 1; + + let symbol = builder.dynamic_symbols.add(); + symbol.name = b"global"[..].into(); + symbol.set_st_info(elf::STB_GLOBAL, elf::STT_FUNC); + symbol.section = Some(text_id); + + let symbol = builder.dynamic_symbols.add(); + symbol.name = b"undefined"[..].into(); + symbol.set_st_info(elf::STB_GLOBAL, elf::STT_NOTYPE); + + let symbol = builder.dynamic_symbols.add(); + symbol.name = b"local"[..].into(); + symbol.set_st_info(elf::STB_LOCAL, elf::STT_FUNC); + symbol.section = Some(text_id); + + builder.set_section_sizes(); + + let segment = builder.segments.add(); + segment.p_type = elf::PT_LOAD; + segment.p_flags = elf::PF_R; + segment.p_filesz = 0x1000; + segment.p_memsz = 0x1000; + segment.p_align = 8; + segment.append_section(builder.sections.get_mut(text_id)); + segment.append_section(builder.sections.get_mut(dynsym_id)); + segment.append_section(builder.sections.get_mut(dynstr_id)); + segment.append_section(builder.sections.get_mut(gnu_hash_id)); + + let mut buf = Vec::new(); + builder.write(&mut buf).unwrap(); + + let builder = build::elf::Builder::read(&*buf).unwrap(); + assert_eq!(builder.sections.count(), 5); + assert_eq!(builder.dynamic_symbols.count(), 3); + // Check that the dynamic symbol table sorting handles + // local and undefined symbols correctly. + assert_eq!( + builder + .dynamic_symbols + .iter() + .map(|s| s.name.as_slice()) + .collect::<Vec<_>>(), + vec![&b"local"[..], &b"undefined"[..], &b"global"[..]] + ); + for section in &builder.sections { + if let build::elf::SectionData::DynamicSymbol = §ion.data { + // Check that sh_info includes the number of local symbols. + assert_eq!(section.sh_info, 2); + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/build/mod.rs b/chromium_crates_io/vendor/object-v0_37/tests/build/mod.rs new file mode 100644 index 0000000..6a77736 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/build/mod.rs
@@ -0,0 +1,3 @@ +#![cfg(feature = "build")] + +mod elf;
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/integration.rs b/chromium_crates_io/vendor/object-v0_37/tests/integration.rs new file mode 100644 index 0000000..560ba62 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/integration.rs
@@ -0,0 +1,3 @@ +mod build; +mod read; +mod round_trip;
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/parse_self.rs b/chromium_crates_io/vendor/object-v0_37/tests/parse_self.rs new file mode 100644 index 0000000..1e7df67 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/parse_self.rs
@@ -0,0 +1,25 @@ +#![cfg(feature = "read")] +use object::{File, Object}; +use std::{env, fs}; + +#[test] +fn parse_self() { + let exe = env::current_exe().unwrap(); + let data = fs::read(exe).unwrap(); + let object = File::parse(&*data).unwrap(); + assert!(object.entry() != 0); + assert!(object.sections().count() != 0); +} + +#[cfg(feature = "std")] +#[test] +fn parse_self_cache() { + use object::read::{ReadCache, ReadRef}; + let exe = env::current_exe().unwrap(); + let file = fs::File::open(exe).unwrap(); + let cache = ReadCache::new(file); + let data = cache.range(0, cache.len().unwrap()); + let object = File::parse(data).unwrap(); + assert!(object.entry() != 0); + assert!(object.sections().count() != 0); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/read/coff.rs b/chromium_crates_io/vendor/object-v0_37/tests/read/coff.rs new file mode 100644 index 0000000..959e317 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/read/coff.rs
@@ -0,0 +1,23 @@ +use object::{pe, read, Object, ObjectSection}; +use std::fs; +use std::path::PathBuf; + +#[cfg(feature = "coff")] +#[test] +fn coff_extended_relocations() { + let path_to_obj: PathBuf = ["testfiles", "coff", "relocs_overflow.o"].iter().collect(); + let contents = fs::read(path_to_obj).expect("Could not read relocs_overflow.o"); + let file = + read::coff::CoffFile::<_>::parse(&contents[..]).expect("Could not parse relocs_overflow.o"); + let code_section = file + .section_by_name(".text") + .expect("Could not find .text section in relocs_overflow.o"); + match code_section.flags() { + object::SectionFlags::Coff { characteristics } => { + assert!(characteristics & pe::IMAGE_SCN_LNK_NRELOC_OVFL != 0) + } + _ => panic!("Invalid section flags flavour."), + }; + let relocations = code_section.relocations().collect::<Vec<_>>(); + assert_eq!(relocations.len(), 65536); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/read/elf.rs b/chromium_crates_io/vendor/object-v0_37/tests/read/elf.rs new file mode 100644 index 0000000..44261f7 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/read/elf.rs
@@ -0,0 +1,62 @@ +#[cfg(feature = "std")] +use std::path::{Path, PathBuf}; + +#[cfg(feature = "std")] +fn get_buildid(path: &Path) -> Result<Option<Vec<u8>>, object::read::Error> { + use object::Object; + let file = std::fs::File::open(path).unwrap(); + let reader = object::read::ReadCache::new(file); + let object = object::read::File::parse(&reader)?; + object + .build_id() + .map(|option| option.map(ToOwned::to_owned)) +} + +#[cfg(feature = "std")] +#[test] +/// Regression test: used to attempt to allocate 5644418395173552131 bytes +fn get_buildid_bad_elf() { + let path: PathBuf = [ + "testfiles", + "elf", + "yara-fuzzing", + "crash-7dc27920ae1cb85333e7f2735a45014488134673", + ] + .iter() + .collect(); + let _ = get_buildid(&path); +} + +#[cfg(feature = "std")] +#[test] +fn get_buildid_less_bad_elf() { + let path: PathBuf = [ + "testfiles", + "elf", + "yara-fuzzing", + "crash-f1fd008da535b110853885221ebfaac3f262a1c1e280f10929f7b353c44996c8", + ] + .iter() + .collect(); + let buildid = get_buildid(&path).unwrap().unwrap(); + // ground truth obtained from GNU binutils's readelf + assert_eq!( + buildid, + b"\xf9\xc0\xc6\x05\xd3\x76\xbb\xa5\x7e\x02\xf5\x74\x50\x9d\x16\xcc\xe9\x9c\x1b\xf1" + ); +} + +#[cfg(feature = "std")] +#[test] +fn zero_sized_section_works() { + use object::{Object as _, ObjectSection as _}; + let path: PathBuf = ["testfiles", "elf", "base.debug"].iter().collect(); + let data = std::fs::read(&path).unwrap(); + let object = object::read::File::parse(&data[..]).unwrap(); + + // The unwrap here should not fail, even though the section has an invalid offset, its size is + // zero so this should succeed. + let section = object.section_by_name(".bss").unwrap(); + let data = section.data().unwrap(); + assert_eq!(data.len(), 0); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/read/macho.rs b/chromium_crates_io/vendor/object-v0_37/tests/read/macho.rs new file mode 100644 index 0000000..59f314b --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/read/macho.rs
@@ -0,0 +1,49 @@ +#[cfg(feature = "std")] +use object::{Object, ObjectSection as _}; + +// Test that we can read compressed sections in Mach-O files as produced +// by the Go compiler. +#[cfg(feature = "std")] +#[test] +fn test_go_macho() { + let macho_testfiles = std::path::Path::new("testfiles/macho"); + + // Section names we expect to find, whether they should be + // compressed, and the actual name of the section in the file. + const EXPECTED: &[(&str, bool, &str)] = &[ + (".debug_abbrev", true, "__zdebug_abbrev"), + (".debug_gdb_scripts", false, "__debug_gdb_scri"), + (".debug_ranges", true, "__zdebug_ranges"), + ("__data", false, "__data"), + ]; + + for file in &["go-aarch64", "go-x86_64"] { + let path = macho_testfiles.join(file); + let file = std::fs::File::open(path).unwrap(); + let reader = object::read::ReadCache::new(file); + let object = object::read::File::parse(&reader).unwrap(); + for &(name, compressed, actual_name) in EXPECTED { + let section = object.section_by_name(name).unwrap(); + assert_eq!(section.name(), Ok(actual_name)); + let compressed_file_range = section.compressed_file_range().unwrap(); + let size = section.size(); + if compressed { + assert_eq!( + compressed_file_range.format, + object::CompressionFormat::Zlib + ); + assert_eq!(compressed_file_range.compressed_size, size - 12); + assert!( + compressed_file_range.uncompressed_size > compressed_file_range.compressed_size, + "decompressed size is greater than compressed size" + ); + } else { + assert_eq!( + compressed_file_range.format, + object::CompressionFormat::None + ); + assert_eq!(compressed_file_range.compressed_size, size); + } + } + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/read/mod.rs b/chromium_crates_io/vendor/object-v0_37/tests/read/mod.rs new file mode 100644 index 0000000..48e005e --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/read/mod.rs
@@ -0,0 +1,5 @@ +#![cfg(feature = "read")] + +mod coff; +mod elf; +mod macho;
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/bss.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/bss.rs new file mode 100644 index 0000000..e13003d --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/bss.rs
@@ -0,0 +1,256 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection, ObjectSymbol}; +use object::{read, write}; +use object::{ + Architecture, BinaryFormat, Endianness, SectionKind, SymbolFlags, SymbolKind, SymbolScope, +}; + +#[test] +fn coff_x86_64_bss() { + let mut object = + write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); + + let section = object.section_id(write::StandardSection::UninitializedData); + + let _bss_section_symbol = object.section_symbol(section); + + let symbol = object.add_symbol(write::Symbol { + name: b"v1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 18, 4); + + let symbol = object.add_symbol(write::Symbol { + name: b"v2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 34, 8); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"bss.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Coff); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let bss = sections.next().unwrap(); + println!("{:?}", bss); + let bss_index = bss.index(); + assert_eq!(bss.name(), Ok(".bss")); + assert_eq!(bss.kind(), SectionKind::UninitializedData); + assert_eq!(bss.size(), 58); + assert_eq!(bss.data(), Ok(&[][..])); + + let section = sections.next(); + assert!(section.is_none(), "unexpected section {:?}", section); + + let mut symbols = object.symbols(); + + let section_symbol = symbols.next().unwrap(); + println!("{:?}", section_symbol); + assert_eq!(section_symbol.name(), Ok(".bss")); + assert_eq!(section_symbol.kind(), SymbolKind::Section); + assert_eq!(section_symbol.section_index(), Some(bss_index)); + assert_eq!(section_symbol.scope(), SymbolScope::Compilation); + assert!(!section_symbol.is_weak()); + assert!(!section_symbol.is_undefined()); + assert_eq!(section_symbol.address(), 0); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(bss_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v2")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(bss_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 24); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +} + +#[test] +fn elf_x86_64_bss() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + let section = object.section_id(write::StandardSection::UninitializedData); + + let symbol = object.add_symbol(write::Symbol { + name: b"v1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 18, 4); + + let symbol = object.add_symbol(write::Symbol { + name: b"v2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 34, 8); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"bss.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let bss = sections.next().unwrap(); + println!("{:?}", bss); + let bss_index = bss.index(); + assert_eq!(bss.name(), Ok(".bss")); + assert_eq!(bss.kind(), SectionKind::UninitializedData); + assert_eq!(bss.size(), 58); + assert_eq!(bss.data(), Ok(&[][..])); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(bss_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.size(), 18); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v2")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(bss_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 24); + assert_eq!(symbol.size(), 34); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +} + +#[test] +fn macho_x86_64_bss() { + let mut object = write::Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ); + + let section = object.section_id(write::StandardSection::UninitializedData); + + let symbol = object.add_symbol(write::Symbol { + name: b"v1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 18, 4); + + let symbol = object.add_symbol(write::Symbol { + name: b"v2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 34, 8); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"bss.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::MachO); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let bss = sections.next().unwrap(); + println!("{:?}", bss); + let bss_index = bss.index(); + assert_eq!(bss.name(), Ok("__bss")); + assert_eq!(bss.segment_name(), Ok(Some("__DATA"))); + assert_eq!(bss.kind(), SectionKind::UninitializedData); + assert_eq!(bss.size(), 58); + assert_eq!(bss.data(), Ok(&[][..])); + + let section = sections.next(); + assert!(section.is_none(), "unexpected section {:?}", section); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("_v1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(bss_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("_v2")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(bss_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 24); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/coff.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/coff.rs new file mode 100644 index 0000000..2b55788 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/coff.rs
@@ -0,0 +1,58 @@ +use object::read::{Object, ObjectSection}; +use object::{read, write}; +use object::{ + Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationFlags, RelocationKind, + SymbolFlags, SymbolKind, SymbolScope, +}; + +#[test] +fn reloc_overflow() { + let mut object = + write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); + let text = object.section_id(write::StandardSection::Text); + object.append_section_data(text, &[0; 4], 4); + let symbol = object.add_symbol(write::Symbol { + name: b"f".to_vec(), + value: 0, + size: 4, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(text), + flags: SymbolFlags::None, + }); + for i in 0..0x10000 { + object + .add_relocation( + text, + write::Relocation { + offset: i, + symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 64, + }, + }, + ) + .unwrap(); + } + let bytes = object.write().unwrap(); + + //std::fs::write(&"reloc_overflow.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Coff); + assert_eq!(object.architecture(), Architecture::X86_64); + + let section = object.sections().next().unwrap(); + assert_eq!(section.name(), Ok(".text")); + + let mut i = 0; + for (offset, _relocation) in section.relocations() { + assert_eq!(offset, i); + i += 1; + } + assert_eq!(i, 0x10000); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/comdat.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/comdat.rs new file mode 100644 index 0000000..88a2e79 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/comdat.rs
@@ -0,0 +1,217 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::pe; +use object::read::{Object, ObjectComdat, ObjectSection, ObjectSymbol}; +use object::{read, write}; +use object::{ + Architecture, BinaryFormat, ComdatKind, Endianness, SectionKind, SymbolFlags, SymbolKind, + SymbolScope, +}; + +#[test] +fn coff_x86_64_comdat() { + let mut object = + write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); + + let section1 = object.add_subsection(write::StandardSection::Text, b"s1"); + let offset = object.append_section_data(section1, &[0, 1, 2, 3], 4); + object.section_symbol(section1); + let section2 = object.add_subsection(write::StandardSection::Data, b"s1"); + object.append_section_data(section2, &[0, 1, 2, 3], 4); + object.section_symbol(section2); + + let symbol = object.add_symbol(write::Symbol { + name: b"s1".to_vec(), + value: offset, + size: 4, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(section1), + flags: SymbolFlags::None, + }); + + object.add_comdat(write::Comdat { + kind: ComdatKind::NoDuplicates, + symbol, + sections: vec![section1, section2], + }); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"comdat.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Coff); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let section1 = sections.next().unwrap(); + println!("{:?}", section1); + let section1_index = section1.index(); + assert_eq!(section1.name(), Ok(".text$s1")); + assert_eq!(section1.kind(), SectionKind::Text); + assert_eq!(section1.address(), 0); + assert_eq!(section1.size(), 4); + + let section2 = sections.next().unwrap(); + println!("{:?}", section2); + let section2_index = section2.index(); + assert_eq!(section2.name(), Ok(".data$s1")); + assert_eq!(section2.kind(), SectionKind::Data); + assert_eq!(section2.address(), 0); + assert_eq!(section2.size(), 4); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok(".text$s1")); + assert_eq!(symbol.kind(), SymbolKind::Section); + assert_eq!( + symbol.section(), + read::SymbolSection::Section(section1.index()) + ); + assert_eq!( + symbol.flags(), + SymbolFlags::CoffSection { + selection: pe::IMAGE_COMDAT_SELECT_NODUPLICATES, + associative_section: None + } + ); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok(".data$s1")); + assert_eq!(symbol.kind(), SymbolKind::Section); + assert_eq!( + symbol.section(), + read::SymbolSection::Section(section2.index()) + ); + assert_eq!( + symbol.flags(), + SymbolFlags::CoffSection { + selection: pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE, + associative_section: Some(section1_index) + } + ); + + let symbol = symbols.next().unwrap(); + let symbol_index = symbol.index(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("s1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!( + symbol.section(), + read::SymbolSection::Section(section1.index()) + ); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); + + let mut comdats = object.comdats(); + + let comdat = comdats.next().unwrap(); + println!("{:?}", comdat); + assert_eq!(comdat.kind(), ComdatKind::NoDuplicates); + assert_eq!(comdat.symbol(), symbol_index); + + let mut comdat_sections = comdat.sections(); + assert_eq!(comdat_sections.next(), Some(section1_index)); + assert_eq!(comdat_sections.next(), Some(section2_index)); + assert_eq!(comdat_sections.next(), None); +} + +#[test] +fn elf_x86_64_comdat() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + let section1 = object.add_subsection(write::StandardSection::Text, b"s1"); + let offset = object.append_section_data(section1, &[0, 1, 2, 3], 4); + let section2 = object.add_subsection(write::StandardSection::Data, b"s1"); + object.append_section_data(section2, &[0, 1, 2, 3], 4); + + let symbol = object.add_symbol(write::Symbol { + name: b"s1".to_vec(), + value: offset, + size: 4, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(section1), + flags: SymbolFlags::None, + }); + + object.add_comdat(write::Comdat { + kind: ComdatKind::Any, + symbol, + sections: vec![section1, section2], + }); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"comdat.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let section = sections.next().unwrap(); + println!("{:?}", section); + assert_eq!(section.name(), Ok(".group")); + + let section1 = sections.next().unwrap(); + println!("{:?}", section1); + let section1_index = section1.index(); + assert_eq!(section1.name(), Ok(".text.s1")); + assert_eq!(section1.kind(), SectionKind::Text); + assert_eq!(section1.address(), 0); + assert_eq!(section1.size(), 4); + + let section2 = sections.next().unwrap(); + println!("{:?}", section2); + let section2_index = section2.index(); + assert_eq!(section2.name(), Ok(".data.s1")); + assert_eq!(section2.kind(), SectionKind::Data); + assert_eq!(section2.address(), 0); + assert_eq!(section2.size(), 4); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + let symbol_index = symbol.index(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("s1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!( + symbol.section(), + read::SymbolSection::Section(section1.index()) + ); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); + + let mut comdats = object.comdats(); + + let comdat = comdats.next().unwrap(); + println!("{:?}", comdat); + assert_eq!(comdat.kind(), ComdatKind::Any); + assert_eq!(comdat.symbol(), symbol_index); + + let mut comdat_sections = comdat.sections(); + assert_eq!(comdat_sections.next(), Some(section1_index)); + assert_eq!(comdat_sections.next(), Some(section2_index)); + assert_eq!(comdat_sections.next(), None); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/common.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/common.rs new file mode 100644 index 0000000..051688b --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/common.rs
@@ -0,0 +1,241 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection, ObjectSymbol}; +use object::{read, write}; +use object::{ + Architecture, BinaryFormat, Endianness, SectionKind, SymbolFlags, SymbolKind, SymbolScope, +}; + +#[test] +fn coff_x86_64_common() { + let mut object = + write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); + + let symbol = write::Symbol { + name: b"v1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }; + object.add_common_symbol(symbol, 4, 4); + + let symbol = write::Symbol { + name: b"v2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }; + object.add_common_symbol(symbol, 8, 8); + + // Also check undefined symbols, which are very similar. + let symbol = write::Symbol { + name: b"v3".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }; + object.add_symbol(symbol); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"common.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Coff); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section(), read::SymbolSection::Common); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.size(), 4); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v2")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section(), read::SymbolSection::Common); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.size(), 8); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v3")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section(), read::SymbolSection::Undefined); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.size(), 0); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +} + +#[test] +fn elf_x86_64_common() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + let symbol = write::Symbol { + name: b"v1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }; + object.add_common_symbol(symbol, 4, 4); + + let symbol = write::Symbol { + name: b"v2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }; + object.add_common_symbol(symbol, 8, 8); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"common.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section(), read::SymbolSection::Common); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.size(), 4); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("v2")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section(), read::SymbolSection::Common); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.size(), 8); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +} + +#[test] +fn macho_x86_64_common() { + let mut object = write::Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ); + + let symbol = write::Symbol { + name: b"v1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }; + object.add_common_symbol(symbol, 4, 4); + + let symbol = write::Symbol { + name: b"v2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }; + object.add_common_symbol(symbol, 8, 8); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"common.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::MachO); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let common = sections.next().unwrap(); + println!("{:?}", common); + let common_index = common.index(); + assert_eq!(common.name(), Ok("__common")); + assert_eq!(common.segment_name(), Ok(Some("__DATA"))); + assert_eq!(common.kind(), SectionKind::Common); + assert_eq!(common.size(), 16); + assert_eq!(common.data(), Ok(&[][..])); + + let section = sections.next(); + assert!(section.is_none(), "unexpected section {:?}", section); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("_v1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(common_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 0); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("_v2")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(common_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.address(), 8); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/elf.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/elf.rs new file mode 100644 index 0000000..2f3351f --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/elf.rs
@@ -0,0 +1,302 @@ +use object::read::elf::{FileHeader, SectionHeader}; +use object::read::{Object, ObjectSection, ObjectSymbol}; +use object::{ + elf, read, write, Architecture, BinaryFormat, Endianness, LittleEndian, SectionIndex, + SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection, U32, +}; +use std::io::Write; + +#[test] +fn symtab_shndx() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + for i in 0..0x10000 { + let name = format!("func{}", i).into_bytes(); + let section = object.add_subsection(write::StandardSection::Text, &name); + let offset = object.append_section_data(section, &[0xcc], 1); + object.add_symbol(write::Symbol { + name, + value: offset, + size: 1, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(section), + flags: SymbolFlags::None, + }); + } + let bytes = object.write().unwrap(); + + //std::fs::write(&"symtab_shndx.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + for symbol in object.symbols() { + assert_eq!( + symbol.section(), + SymbolSection::Section(SectionIndex(symbol.index().0)) + ); + } +} + +#[test] +fn empty_symtab() { + let object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + let bytes = object.write().unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + let symtab = object.section_by_name(".symtab").unwrap(); + assert_eq!(symtab.size(), 24); + let strtab = object.section_by_name(".strtab").unwrap(); + assert_eq!(strtab.size(), 1); +} + +#[test] +fn aligned_sections() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + let text_section_id = object.add_section(vec![], b".text".to_vec(), SectionKind::Text); + let text_section = object.section_mut(text_section_id); + text_section.set_data(&[][..], 4096); + + let data_section_id = object.add_section(vec![], b".data".to_vec(), SectionKind::Data); + let data_section = object.section_mut(data_section_id); + data_section.set_data(&b"1234"[..], 16); + + let bytes = object.write().unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let section = sections.next().unwrap(); + assert_eq!(section.name(), Ok(".text")); + assert_eq!(section.file_range(), Some((4096, 0))); + + let section = sections.next().unwrap(); + assert_eq!(section.name(), Ok(".data")); + assert_eq!(section.file_range(), Some((4096, 4))); +} + +#[cfg(feature = "compression")] +#[test] +fn compression_zlib() { + use object::read::ObjectSection; + use object::LittleEndian as LE; + + let data = b"test data data data"; + let len = data.len() as u64; + + let mut ch = object::elf::CompressionHeader64::<LE>::default(); + ch.ch_type.set(LE, object::elf::ELFCOMPRESS_ZLIB); + ch.ch_size.set(LE, len); + ch.ch_addralign.set(LE, 1); + + let mut buf = Vec::new(); + buf.write_all(object::bytes_of(&ch)).unwrap(); + let mut encoder = flate2::write::ZlibEncoder::new(buf, flate2::Compression::default()); + encoder.write_all(data).unwrap(); + let compressed = encoder.finish().unwrap(); + + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + let section = object.add_section( + Vec::new(), + b".debug_info".to_vec(), + object::SectionKind::Other, + ); + object.section_mut(section).set_data(compressed, 1); + object.section_mut(section).flags = object::SectionFlags::Elf { + sh_flags: object::elf::SHF_COMPRESSED.into(), + }; + let bytes = object.write().unwrap(); + + //std::fs::write(&"compression.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let section = object.section_by_name(".debug_info").unwrap(); + let uncompressed = section.uncompressed_data().unwrap(); + assert_eq!(data, &*uncompressed); +} + +#[cfg(feature = "compression")] +#[test] +fn compression_gnu() { + use object::read::ObjectSection; + use std::io::Write; + + let data = b"test data data data"; + let len = data.len() as u32; + + let mut buf = Vec::new(); + buf.write_all(b"ZLIB\0\0\0\0").unwrap(); + buf.write_all(&len.to_be_bytes()).unwrap(); + let mut encoder = flate2::write::ZlibEncoder::new(buf, flate2::Compression::default()); + encoder.write_all(data).unwrap(); + let compressed = encoder.finish().unwrap(); + + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + let section = object.add_section( + Vec::new(), + b".zdebug_info".to_vec(), + object::SectionKind::Other, + ); + object.section_mut(section).set_data(compressed, 1); + let bytes = object.write().unwrap(); + + //std::fs::write(&"compression.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let section = object.section_by_name(".zdebug_info").unwrap(); + let uncompressed = section.uncompressed_data().unwrap(); + assert_eq!(data, &*uncompressed); +} + +#[test] +fn note() { + let endian = Endianness::Little; + let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, endian); + + // Add note section with align = 4. + let mut buffer = Vec::new(); + + buffer + .write_all(object::bytes_of(&elf::NoteHeader32 { + n_namesz: U32::new(endian, 6), + n_descsz: U32::new(endian, 11), + n_type: U32::new(endian, 1), + })) + .unwrap(); + buffer.write_all(b"name1\0\0\0").unwrap(); + buffer.write_all(b"descriptor\0\0").unwrap(); + + buffer + .write_all(object::bytes_of(&elf::NoteHeader32 { + n_namesz: U32::new(endian, 6), + n_descsz: U32::new(endian, 11), + n_type: U32::new(endian, 2), + })) + .unwrap(); + buffer.write_all(b"name2\0\0\0").unwrap(); + buffer.write_all(b"descriptor\0\0").unwrap(); + + let section = object.add_section(Vec::new(), b".note4".to_vec(), SectionKind::Note); + object.section_mut(section).set_data(buffer, 4); + + // Add note section with align = 8. + let mut buffer = Vec::new(); + + buffer + .write_all(object::bytes_of(&elf::NoteHeader32 { + n_namesz: U32::new(endian, 6), + n_descsz: U32::new(endian, 11), + n_type: U32::new(endian, 1), + })) + .unwrap(); + buffer.write_all(b"name1\0\0\0\0\0\0\0").unwrap(); + buffer.write_all(b"descriptor\0\0\0\0\0\0").unwrap(); + + buffer + .write_all(object::bytes_of(&elf::NoteHeader32 { + n_namesz: U32::new(endian, 4), + n_descsz: U32::new(endian, 11), + n_type: U32::new(endian, 2), + })) + .unwrap(); + buffer.write_all(b"abc\0").unwrap(); + buffer.write_all(b"descriptor\0\0\0\0\0\0").unwrap(); + + let section = object.add_section(Vec::new(), b".note8".to_vec(), SectionKind::Note); + object.section_mut(section).set_data(buffer, 8); + + let bytes = &*object.write().unwrap(); + + //std::fs::write(&"note.o", &bytes).unwrap(); + + let header = elf::FileHeader64::parse(bytes).unwrap(); + let endian: LittleEndian = header.endian().unwrap(); + let sections = header.sections(endian, bytes).unwrap(); + + let section = sections.section(SectionIndex(1)).unwrap(); + assert_eq!(sections.section_name(endian, section).unwrap(), b".note4"); + assert_eq!(section.sh_addralign(endian), 4); + let mut notes = section.notes(endian, bytes).unwrap().unwrap(); + let note = notes.next().unwrap().unwrap(); + assert_eq!(note.name(), b"name1"); + assert_eq!(note.desc(), b"descriptor\0"); + assert_eq!(note.n_type(endian), 1); + let note = notes.next().unwrap().unwrap(); + assert_eq!(note.name(), b"name2"); + assert_eq!(note.desc(), b"descriptor\0"); + assert_eq!(note.n_type(endian), 2); + assert!(notes.next().unwrap().is_none()); + + let section = sections.section(SectionIndex(2)).unwrap(); + assert_eq!(sections.section_name(endian, section).unwrap(), b".note8"); + assert_eq!(section.sh_addralign(endian), 8); + let mut notes = section.notes(endian, bytes).unwrap().unwrap(); + let note = notes.next().unwrap().unwrap(); + assert_eq!(note.name(), b"name1"); + assert_eq!(note.desc(), b"descriptor\0"); + assert_eq!(note.n_type(endian), 1); + let note = notes.next().unwrap().unwrap(); + assert_eq!(note.name(), b"abc"); + assert_eq!(note.desc(), b"descriptor\0"); + assert_eq!(note.n_type(endian), 2); + assert!(notes.next().unwrap().is_none()); +} + +#[test] +fn gnu_property() { + gnu_property_inner::<elf::FileHeader32<Endianness>>(Architecture::I386); + gnu_property_inner::<elf::FileHeader64<Endianness>>(Architecture::X86_64); +} + +fn gnu_property_inner<Elf: FileHeader<Endian = Endianness>>(architecture: Architecture) { + let endian = Endianness::Little; + let mut object = write::Object::new(BinaryFormat::Elf, architecture, endian); + object.add_elf_gnu_property_u32( + elf::GNU_PROPERTY_X86_FEATURE_1_AND, + elf::GNU_PROPERTY_X86_FEATURE_1_IBT | elf::GNU_PROPERTY_X86_FEATURE_1_SHSTK, + ); + + let bytes = &*object.write().unwrap(); + + //std::fs::write(&"note.o", &bytes).unwrap(); + + let header = Elf::parse(bytes).unwrap(); + assert_eq!(header.endian().unwrap(), endian); + let sections = header.sections(endian, bytes).unwrap(); + let section = sections.section(SectionIndex(1)).unwrap(); + assert_eq!( + sections.section_name(endian, section).unwrap(), + b".note.gnu.property" + ); + assert_eq!(section.sh_flags(endian).into(), u64::from(elf::SHF_ALLOC)); + let mut notes = section.notes(endian, bytes).unwrap().unwrap(); + let note = notes.next().unwrap().unwrap(); + let mut props = note.gnu_properties(endian).unwrap(); + let prop = props.next().unwrap().unwrap(); + assert_eq!(prop.pr_type(), elf::GNU_PROPERTY_X86_FEATURE_1_AND); + assert_eq!( + prop.data_u32(endian).unwrap(), + elf::GNU_PROPERTY_X86_FEATURE_1_IBT | elf::GNU_PROPERTY_X86_FEATURE_1_SHSTK + ); + assert!(props.next().unwrap().is_none()); + assert!(notes.next().unwrap().is_none()); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/macho.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/macho.rs new file mode 100644 index 0000000..d8ed0f8 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/macho.rs
@@ -0,0 +1,64 @@ +use object::read::macho::MachHeader; +use object::read::{Object, ObjectSection}; +use object::{macho, read, write, Architecture, BinaryFormat, Endianness}; + +// Test that segment size is valid when the first section needs alignment. +#[test] +fn issue_286_segment_file_size() { + let mut object = write::Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ); + + let text = object.section_id(write::StandardSection::Text); + object.append_section_data(text, &[1; 30], 0x1000); + + let bytes = &*object.write().unwrap(); + let header = macho::MachHeader64::parse(bytes, 0).unwrap(); + let endian: Endianness = header.endian().unwrap(); + let mut commands = header.load_commands(endian, bytes, 0).unwrap(); + let command = commands.next().unwrap().unwrap(); + let (segment, _) = command.segment_64().unwrap().unwrap(); + assert_eq!(segment.vmsize.get(endian), 30); + assert_eq!(segment.filesize.get(endian), 30); +} + +// We were emitting section file alignment padding that didn't match the address alignment padding. +#[test] +fn issue_552_section_file_alignment() { + let mut object = write::Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ); + + // The starting file offset is not a multiple of 32 (checked later). + // Length of 32 ensures that the file offset of the end of this section is still not a + // multiple of 32. + let section = object.add_section(vec![], vec![], object::SectionKind::ReadOnlyDataWithRel); + object.append_section_data(section, &[0u8; 32], 1); + + // Address is already aligned correctly, so there must not any padding, + // even though file offset is not aligned. + let section = object.add_section(vec![], vec![], object::SectionKind::ReadOnlyData); + object.append_section_data(section, &[0u8; 1], 32); + + let bytes = &*object.write().unwrap(); + //std::fs::write(&"align.o", &bytes).unwrap(); + let object = read::File::parse(bytes).unwrap(); + let mut sections = object.sections(); + + let section = sections.next().unwrap(); + let offset = section.file_range().unwrap().0; + // Check file offset is not aligned to 32. + assert_ne!(offset % 32, 0); + assert_eq!(section.address(), 0); + assert_eq!(section.size(), 32); + + let section = sections.next().unwrap(); + // Check there is no padding. + assert_eq!(section.file_range(), Some((offset + 32, 1))); + assert_eq!(section.address(), 32); + assert_eq!(section.size(), 1); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/mod.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/mod.rs new file mode 100644 index 0000000..1e45b82 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/mod.rs
@@ -0,0 +1,690 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection, ObjectSymbol}; +use object::{read, write, SectionIndex, SubArchitecture}; +use object::{ + Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationFlags, RelocationKind, + SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection, +}; + +mod bss; +mod coff; +mod comdat; +mod common; +mod elf; +mod macho; +mod section_flags; +mod tls; + +#[test] +fn coff_any() { + for (arch, sub_arch) in [ + (Architecture::Aarch64, None), + (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)), + (Architecture::Arm, None), + (Architecture::I386, None), + (Architecture::X86_64, None), + ] + .iter() + .copied() + { + let mut object = write::Object::new(BinaryFormat::Coff, arch, Endianness::Little); + object.set_sub_architecture(sub_arch); + + object.add_file_symbol(b"file.c".to_vec()); + + let text = object.section_id(write::StandardSection::Text); + object.append_section_data(text, &[1; 30], 4); + + let func1_offset = object.append_section_data(text, &[1; 30], 4); + assert_eq!(func1_offset, 32); + let func1_symbol = object.add_symbol(write::Symbol { + name: b"func1".to_vec(), + value: func1_offset, + size: 32, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(text), + flags: SymbolFlags::None, + }); + let func2_offset = object.append_section_data(text, &[1; 30], 4); + assert_eq!(func2_offset, 64); + object.add_symbol(write::Symbol { + name: b"func2_long".to_vec(), + value: func2_offset, + size: 32, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(text), + flags: SymbolFlags::None, + }); + object + .add_relocation( + text, + write::Relocation { + offset: 8, + symbol: func1_symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: arch.address_size().unwrap().bytes() * 8, + }, + }, + ) + .unwrap(); + + let bytes = object.write().unwrap(); + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Coff); + assert_eq!(object.architecture(), arch); + assert_eq!(object.sub_architecture(), sub_arch); + assert_eq!(object.endianness(), Endianness::Little); + + let mut sections = object.sections(); + + let text = sections.next().unwrap(); + println!("{:?}", text); + let text_index = text.index(); + assert_eq!(text.name(), Ok(".text")); + assert_eq!(text.kind(), SectionKind::Text); + assert_eq!(text.address(), 0); + assert_eq!(text.size(), 94); + assert_eq!(&text.data().unwrap()[..30], &[1; 30]); + assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("file.c")); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.kind(), SymbolKind::File); + assert_eq!(symbol.section(), SymbolSection::None); + assert_eq!(symbol.scope(), SymbolScope::Compilation); + assert!(!symbol.is_weak()); + + let decorated_name = |name: &str| { + if arch == Architecture::I386 { + format!("_{name}") + } else { + name.to_owned() + } + }; + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + let func1_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok(decorated_name("func1").as_str())); + assert_eq!(symbol.address(), func1_offset); + assert_eq!(symbol.kind(), SymbolKind::Text); + assert_eq!(symbol.section_index(), Some(text_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok(decorated_name("func2_long").as_str())); + assert_eq!(symbol.address(), func2_offset); + assert_eq!(symbol.kind(), SymbolKind::Text); + assert_eq!(symbol.section_index(), Some(text_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let mut relocations = text.relocations(); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 8); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), arch.address_size().unwrap().bytes() * 8); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(func1_symbol) + ); + assert_eq!(relocation.addend(), 0); + + let map = object.symbol_map(); + let symbol = map.get(func1_offset + 1).unwrap(); + assert_eq!(symbol.address(), func1_offset); + assert_eq!(symbol.name(), decorated_name("func1")); + assert_eq!(map.get(func1_offset - 1), None); + } +} + +#[test] +fn elf_x86_64() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + object.add_file_symbol(b"file.c".to_vec()); + + let text = object.section_id(write::StandardSection::Text); + object.append_section_data(text, &[1; 30], 4); + + let func1_offset = object.append_section_data(text, &[1; 30], 4); + assert_eq!(func1_offset, 32); + let func1_symbol = object.add_symbol(write::Symbol { + name: b"func1".to_vec(), + value: func1_offset, + size: 32, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(text), + flags: SymbolFlags::None, + }); + object + .add_relocation( + text, + write::Relocation { + offset: 8, + symbol: func1_symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 64, + }, + }, + ) + .unwrap(); + + let bytes = object.write().unwrap(); + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + assert_eq!(object.endianness(), Endianness::Little); + + let mut sections = object.sections(); + + let text = sections.next().unwrap(); + println!("{:?}", text); + let text_index = text.index(); + assert_eq!(text.name(), Ok(".text")); + assert_eq!(text.kind(), SectionKind::Text); + assert_eq!(text.address(), 0); + assert_eq!(text.size(), 62); + assert_eq!(&text.data().unwrap()[..30], &[1; 30]); + assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("file.c")); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.kind(), SymbolKind::File); + assert_eq!(symbol.section(), SymbolSection::None); + assert_eq!(symbol.scope(), SymbolScope::Compilation); + assert!(!symbol.is_weak()); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + let func1_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("func1")); + assert_eq!(symbol.address(), func1_offset); + assert_eq!(symbol.kind(), SymbolKind::Text); + assert_eq!(symbol.section_index(), Some(text_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let mut relocations = text.relocations(); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 8); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(func1_symbol) + ); + assert_eq!(relocation.addend(), 0); + + let map = object.symbol_map(); + let symbol = map.get(func1_offset + 1).unwrap(); + assert_eq!(symbol.address(), func1_offset); + assert_eq!(symbol.name(), "func1"); + assert_eq!(map.get(func1_offset - 1), None); +} + +#[test] +fn elf_any() { + for (arch, endian) in [ + (Architecture::Aarch64, Endianness::Little), + (Architecture::Aarch64_Ilp32, Endianness::Little), + (Architecture::Alpha, Endianness::Little), + (Architecture::Arm, Endianness::Little), + (Architecture::Avr, Endianness::Little), + (Architecture::Bpf, Endianness::Little), + (Architecture::Csky, Endianness::Little), + (Architecture::E2K32, Endianness::Little), + (Architecture::E2K64, Endianness::Little), + (Architecture::I386, Endianness::Little), + (Architecture::X86_64, Endianness::Little), + (Architecture::X86_64_X32, Endianness::Little), + (Architecture::Hppa, Endianness::Big), + (Architecture::Hexagon, Endianness::Little), + (Architecture::LoongArch32, Endianness::Little), + (Architecture::LoongArch64, Endianness::Little), + (Architecture::M68k, Endianness::Big), + (Architecture::Mips, Endianness::Little), + (Architecture::Mips64, Endianness::Little), + (Architecture::Mips64_N32, Endianness::Little), + (Architecture::Msp430, Endianness::Little), + (Architecture::PowerPc, Endianness::Big), + (Architecture::PowerPc64, Endianness::Big), + (Architecture::Riscv32, Endianness::Little), + (Architecture::Riscv64, Endianness::Little), + (Architecture::S390x, Endianness::Big), + (Architecture::Sbf, Endianness::Little), + (Architecture::Sparc, Endianness::Big), + (Architecture::Sparc32Plus, Endianness::Big), + (Architecture::Sparc64, Endianness::Big), + (Architecture::SuperH, Endianness::Big), + (Architecture::Xtensa, Endianness::Little), + ] + .iter() + .copied() + { + let mut object = write::Object::new(BinaryFormat::Elf, arch, endian); + + let section = object.section_id(write::StandardSection::Data); + object.append_section_data(section, &[1; 30], 4); + let symbol = object.section_symbol(section); + + object + .add_relocation( + section, + write::Relocation { + offset: 8, + symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 32, + }, + }, + ) + .unwrap(); + if arch.address_size().unwrap().bytes() >= 8 { + object + .add_relocation( + section, + write::Relocation { + offset: 16, + symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 64, + }, + }, + ) + .unwrap(); + } + + let bytes = object.write().unwrap(); + let object = read::File::parse(&*bytes).unwrap(); + println!("{:?}", object.architecture()); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), arch); + assert_eq!(object.endianness(), endian); + + let mut sections = object.sections(); + + let data = sections.next().unwrap(); + println!("{:?}", data); + assert_eq!(data.name(), Ok(".data")); + assert_eq!(data.kind(), SectionKind::Data); + + let mut relocations = data.relocations(); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 8); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 32); + assert_eq!(relocation.addend(), 0); + + if arch.address_size().unwrap().bytes() >= 8 { + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 16); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!(relocation.addend(), 0); + } + } +} + +#[test] +fn macho_x86_64() { + let mut object = write::Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ); + + object.add_file_symbol(b"file.c".to_vec()); + + let text = object.section_id(write::StandardSection::Text); + object.append_section_data(text, &[1; 30], 4); + + let func1_offset = object.append_section_data(text, &[1; 30], 4); + assert_eq!(func1_offset, 32); + let func1_symbol = object.add_symbol(write::Symbol { + name: b"func1".to_vec(), + value: func1_offset, + size: 32, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(text), + flags: SymbolFlags::None, + }); + object + .add_relocation( + text, + write::Relocation { + offset: 8, + symbol: func1_symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 64, + }, + }, + ) + .unwrap(); + object + .add_relocation( + text, + write::Relocation { + offset: 16, + symbol: func1_symbol, + addend: -4, + flags: RelocationFlags::Generic { + kind: RelocationKind::Relative, + encoding: RelocationEncoding::Generic, + size: 32, + }, + }, + ) + .unwrap(); + + let bytes = object.write().unwrap(); + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::MachO); + assert_eq!(object.architecture(), Architecture::X86_64); + assert_eq!(object.endianness(), Endianness::Little); + + let mut sections = object.sections(); + + let text = sections.next().unwrap(); + println!("{:?}", text); + let text_index = text.index(); + assert_eq!(text.name(), Ok("__text")); + assert_eq!(text.segment_name(), Ok(Some("__TEXT"))); + assert_eq!(text.kind(), SectionKind::Text); + assert_eq!(text.address(), 0); + assert_eq!(text.size(), 62); + assert_eq!(&text.data().unwrap()[..30], &[1; 30]); + assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + let func1_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("_func1")); + assert_eq!(symbol.address(), func1_offset); + assert_eq!(symbol.kind(), SymbolKind::Text); + assert_eq!(symbol.section_index(), Some(text_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let mut relocations = text.relocations(); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 16); + assert_eq!(relocation.kind(), RelocationKind::Relative); + assert_eq!(relocation.encoding(), RelocationEncoding::X86RipRelative); + assert_eq!(relocation.size(), 32); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(func1_symbol) + ); + assert_eq!(relocation.addend(), -4); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 8); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(func1_symbol) + ); + assert_eq!(relocation.addend(), 0); + + let map = object.symbol_map(); + let symbol = map.get(func1_offset + 1).unwrap(); + assert_eq!(symbol.address(), func1_offset); + assert_eq!(symbol.name(), "_func1"); + assert_eq!(map.get(func1_offset - 1), None); +} + +#[test] +fn macho_any() { + for (arch, subarch, endian) in [ + (Architecture::Aarch64, None, Endianness::Little), + ( + Architecture::Aarch64, + Some(SubArchitecture::Arm64E), + Endianness::Little, + ), + (Architecture::Aarch64_Ilp32, None, Endianness::Little), + /* TODO: + (Architecture::Arm, None, Endianness::Little), + */ + (Architecture::I386, None, Endianness::Little), + (Architecture::X86_64, None, Endianness::Little), + /* TODO: + (Architecture::PowerPc, None, Endianness::Big), + (Architecture::PowerPc64, None, Endianness::Big), + */ + ] + .iter() + .copied() + { + let mut object = write::Object::new(BinaryFormat::MachO, arch, endian); + object.set_sub_architecture(subarch); + + let section = object.section_id(write::StandardSection::Data); + object.append_section_data(section, &[1; 30], 4); + let symbol = object.section_symbol(section); + + object + .add_relocation( + section, + write::Relocation { + offset: 8, + symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 32, + }, + }, + ) + .unwrap(); + if arch.address_size().unwrap().bytes() >= 8 { + object + .add_relocation( + section, + write::Relocation { + offset: 16, + symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 64, + }, + }, + ) + .unwrap(); + } + + let bytes = object.write().unwrap(); + let object = read::File::parse(&*bytes).unwrap(); + println!("{:?}", object.architecture()); + assert_eq!(object.format(), BinaryFormat::MachO); + assert_eq!(object.architecture(), arch); + assert_eq!(object.sub_architecture(), subarch); + assert_eq!(object.endianness(), endian); + + let mut sections = object.sections(); + + let data = sections.next().unwrap(); + println!("{:?}", data); + assert_eq!(data.segment_name(), Ok(Some("__DATA"))); + assert_eq!(data.name(), Ok("__data")); + assert_eq!(data.kind(), SectionKind::Data); + + let mut relocations = data.relocations(); + + if arch.address_size().unwrap().bytes() >= 8 { + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 16); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!(relocation.addend(), 0); + } + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 8); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 32); + assert_eq!(relocation.addend(), 0); + } +} + +#[cfg(feature = "xcoff")] +#[test] +fn xcoff_powerpc() { + for arch in [Architecture::PowerPc, Architecture::PowerPc64] { + let mut object = write::Object::new(BinaryFormat::Xcoff, arch, Endianness::Big); + + object.add_file_symbol(b"file.c".to_vec()); + + let text = object.section_id(write::StandardSection::Text); + object.append_section_data(text, &[1; 30], 4); + + let func1_offset = object.append_section_data(text, &[1; 30], 4); + assert_eq!(func1_offset, 32); + let func1_symbol = object.add_symbol(write::Symbol { + name: b"func1".to_vec(), + value: func1_offset, + size: 32, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(text), + flags: SymbolFlags::None, + }); + + object + .add_relocation( + text, + write::Relocation { + offset: 8, + symbol: func1_symbol, + addend: 0, + flags: RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 64, + }, + }, + ) + .unwrap(); + + let bytes = object.write().unwrap(); + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Xcoff); + assert_eq!(object.architecture(), arch); + assert_eq!(object.endianness(), Endianness::Big); + + let mut sections = object.sections(); + + let text = sections.next().unwrap(); + println!("{:?}", text); + let text_index = text.index().0; + assert_eq!(text.name(), Ok(".text")); + assert_eq!(text.kind(), SectionKind::Text); + assert_eq!(text.address(), 0); + assert_eq!(text.size(), 62); + assert_eq!(&text.data().unwrap()[..30], &[1; 30]); + assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); + + let mut symbols = object.symbols(); + + let mut symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("file.c")); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.kind(), SymbolKind::File); + assert_eq!(symbol.section_index(), None); + assert_eq!(symbol.scope(), SymbolScope::Compilation); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + let func1_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("func1")); + assert_eq!(symbol.address(), func1_offset); + assert_eq!(symbol.kind(), SymbolKind::Text); + assert_eq!(symbol.section_index(), Some(SectionIndex(text_index))); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let mut relocations = text.relocations(); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 8); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(func1_symbol) + ); + assert_eq!(relocation.addend(), 0); + } +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/section_flags.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/section_flags.rs new file mode 100644 index 0000000..791ba8f --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/section_flags.rs
@@ -0,0 +1,89 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection}; +use object::{read, write}; +use object::{Architecture, BinaryFormat, Endianness, SectionFlags, SectionKind}; + +#[test] +fn coff_x86_64_section_flags() { + let mut object = + write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); + + let section = object.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); + object.section_mut(section).flags = SectionFlags::Coff { + characteristics: object::pe::IMAGE_SCN_MEM_WRITE, + }; + + let bytes = object.write().unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Coff); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + let section = sections.next().unwrap(); + assert_eq!(section.name(), Ok(".text")); + assert_eq!( + section.flags(), + SectionFlags::Coff { + characteristics: object::pe::IMAGE_SCN_MEM_WRITE | object::pe::IMAGE_SCN_ALIGN_1BYTES, + } + ); +} + +#[test] +fn elf_x86_64_section_flags() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + let section = object.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); + object.section_mut(section).flags = SectionFlags::Elf { + sh_flags: object::elf::SHF_WRITE.into(), + }; + + let bytes = object.write().unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + let section = sections.next().unwrap(); + assert_eq!(section.name(), Ok(".text")); + assert_eq!( + section.flags(), + SectionFlags::Elf { + sh_flags: object::elf::SHF_WRITE.into(), + } + ); +} + +#[test] +fn macho_x86_64_section_flags() { + let mut object = write::Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ); + + let section = object.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); + object.section_mut(section).flags = SectionFlags::MachO { + flags: object::macho::S_ATTR_SELF_MODIFYING_CODE, + }; + + let bytes = object.write().unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::MachO); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + let section = sections.next().unwrap(); + assert_eq!(section.name(), Ok(".text")); + assert_eq!( + section.flags(), + SectionFlags::MachO { + flags: object::macho::S_ATTR_SELF_MODIFYING_CODE, + } + ); +}
diff --git a/chromium_crates_io/vendor/object-v0_37/tests/round_trip/tls.rs b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/tls.rs new file mode 100644 index 0000000..fa41096 --- /dev/null +++ b/chromium_crates_io/vendor/object-v0_37/tests/round_trip/tls.rs
@@ -0,0 +1,308 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection, ObjectSymbol}; +use object::{read, write}; +use object::{ + Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind, + SymbolFlags, SymbolKind, SymbolScope, +}; + +#[test] +fn coff_x86_64_tls() { + let mut object = + write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); + + let section = object.section_id(write::StandardSection::Tls); + let symbol = object.add_symbol(write::Symbol { + name: b"tls1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Tls, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_data(symbol, section, &[1; 30], 4); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"tls.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Coff); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let section = sections.next().unwrap(); + println!("{:?}", section); + let tls_index = section.index(); + assert_eq!(section.name(), Ok(".tls$")); + assert_eq!(section.kind(), SectionKind::Data); + assert_eq!(section.size(), 30); + assert_eq!(section.data().unwrap(), &[1; 30]); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("tls1")); + assert_eq!(symbol.kind(), SymbolKind::Data); + assert_eq!(symbol.section_index(), Some(tls_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); +} + +#[test] +fn elf_x86_64_tls() { + let mut object = + write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); + + let section = object.section_id(write::StandardSection::Tls); + let symbol = object.add_symbol(write::Symbol { + name: b"tls1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Tls, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_data(symbol, section, &[1; 30], 4); + + let section = object.section_id(write::StandardSection::UninitializedTls); + let symbol = object.add_symbol(write::Symbol { + name: b"tls2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Tls, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 31, 4); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"tls.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::Elf); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let section = sections.next().unwrap(); + println!("{:?}", section); + let tdata_index = section.index(); + assert_eq!(section.name(), Ok(".tdata")); + assert_eq!(section.kind(), SectionKind::Tls); + assert_eq!(section.size(), 30); + assert_eq!(section.data().unwrap(), &[1; 30]); + + let section = sections.next().unwrap(); + println!("{:?}", section); + let tbss_index = section.index(); + assert_eq!(section.name(), Ok(".tbss")); + assert_eq!(section.kind(), SectionKind::UninitializedTls); + assert_eq!(section.size(), 31); + assert_eq!(section.data().unwrap(), &[]); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("tls1")); + assert_eq!(symbol.kind(), SymbolKind::Tls); + assert_eq!(symbol.section_index(), Some(tdata_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.size(), 30); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("tls2")); + assert_eq!(symbol.kind(), SymbolKind::Tls); + assert_eq!(symbol.section_index(), Some(tbss_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + assert_eq!(symbol.size(), 31); +} + +#[test] +fn macho_x86_64_tls() { + let mut object = write::Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ); + + let section = object.section_id(write::StandardSection::Tls); + let symbol = object.add_symbol(write::Symbol { + name: b"tls1".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Tls, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_data(symbol, section, &[1; 30], 4); + + let section = object.section_id(write::StandardSection::UninitializedTls); + let symbol = object.add_symbol(write::Symbol { + name: b"tls2".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Tls, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + object.add_symbol_bss(symbol, section, 31, 4); + + let bytes = object.write().unwrap(); + + //std::fs::write(&"tls.o", &bytes).unwrap(); + + let object = read::File::parse(&*bytes).unwrap(); + assert_eq!(object.format(), BinaryFormat::MachO); + assert_eq!(object.architecture(), Architecture::X86_64); + + let mut sections = object.sections(); + + let thread_data = sections.next().unwrap(); + println!("{:?}", thread_data); + let thread_data_index = thread_data.index(); + assert_eq!(thread_data.name(), Ok("__thread_data")); + assert_eq!(thread_data.segment_name(), Ok(Some("__DATA"))); + assert_eq!(thread_data.kind(), SectionKind::Tls); + assert_eq!(thread_data.size(), 30); + assert_eq!(thread_data.data().unwrap(), &[1; 30]); + + let thread_vars = sections.next().unwrap(); + println!("{:?}", thread_vars); + let thread_vars_index = thread_vars.index(); + assert_eq!(thread_vars.name(), Ok("__thread_vars")); + assert_eq!(thread_vars.segment_name(), Ok(Some("__DATA"))); + assert_eq!(thread_vars.kind(), SectionKind::TlsVariables); + assert_eq!(thread_vars.size(), 2 * 3 * 8); + assert_eq!(thread_vars.data().unwrap(), &[0; 48]); + + let thread_bss = sections.next().unwrap(); + println!("{:?}", thread_bss); + let thread_bss_index = thread_bss.index(); + assert_eq!(thread_bss.name(), Ok("__thread_bss")); + assert_eq!(thread_bss.segment_name(), Ok(Some("__DATA"))); + assert_eq!(thread_bss.kind(), SectionKind::UninitializedTls); + assert_eq!(thread_bss.size(), 31); + assert_eq!(thread_bss.data(), Ok(&[][..])); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + let tls1_init_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("_tls1$tlv$init")); + assert_eq!(symbol.kind(), SymbolKind::Tls); + assert_eq!(symbol.section_index(), Some(thread_data_index)); + assert_eq!(symbol.scope(), SymbolScope::Compilation); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + let tls2_init_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("_tls2$tlv$init")); + assert_eq!(symbol.kind(), SymbolKind::Tls); + assert_eq!(symbol.section_index(), Some(thread_bss_index)); + assert_eq!(symbol.scope(), SymbolScope::Compilation); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("_tls1")); + assert_eq!(symbol.kind(), SymbolKind::Tls); + assert_eq!(symbol.section_index(), Some(thread_vars_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("_tls2")); + assert_eq!(symbol.kind(), SymbolKind::Tls); + assert_eq!(symbol.section_index(), Some(thread_vars_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert!(!symbol.is_weak()); + assert!(!symbol.is_undefined()); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + let tlv_bootstrap_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("__tlv_bootstrap")); + assert_eq!(symbol.kind(), SymbolKind::Unknown); + assert_eq!(symbol.section_index(), None); + assert_eq!(symbol.scope(), SymbolScope::Unknown); + assert!(!symbol.is_weak()); + assert!(symbol.is_undefined()); + + let mut relocations = thread_vars.relocations(); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 40); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(tls2_init_symbol) + ); + assert_eq!(relocation.addend(), 0); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 24); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(tlv_bootstrap_symbol) + ); + assert_eq!(relocation.addend(), 0); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 16); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(tls1_init_symbol) + ); + assert_eq!(relocation.addend(), 0); + + let (offset, relocation) = relocations.next().unwrap(); + println!("{:?}", relocation); + assert_eq!(offset, 0); + assert_eq!(relocation.kind(), RelocationKind::Absolute); + assert_eq!(relocation.encoding(), RelocationEncoding::Generic); + assert_eq!(relocation.size(), 64); + assert_eq!( + relocation.target(), + read::RelocationTarget::Symbol(tlv_bootstrap_symbol) + ); + assert_eq!(relocation.addend(), 0); +}
diff --git a/gimli/v0_32/BUILD.gn b/gimli/v0_32/BUILD.gn new file mode 100644 index 0000000..bf75689 --- /dev/null +++ b/gimli/v0_32/BUILD.gn
@@ -0,0 +1,117 @@ +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# @generated from third_party/rust/chromium_crates_io/BUILD.gn.hbs by +# tools/crates/gnrt. +# Do not edit! + +import("//build/rust/cargo_crate.gni") + +cargo_crate("lib") { + crate_name = "gimli" + epoch = "0.32" + enabled = !is_win + crate_type = "rlib" + crate_root = + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/lib.rs" + sources = [ + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/arch.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/common.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/constants.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/endianity.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/leb128.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/lib.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/abbrev.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/addr.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/aranges.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/cfi.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/dwarf.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_reader.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/endian_slice.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/index.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/line.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/lists.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/loclists.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/lookup.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/macros.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/op.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/pubnames.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/pubtypes.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/reader.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/relocate.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/rnglists.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/str.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/unit.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/util.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/read/value.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/test_util.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/abbrev.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/cfi.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/dwarf.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/endian_vec.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/line.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/loc.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/op.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/range.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/relocate.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/section.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/str.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/unit.rs", + "//third_party/rust/chromium_crates_io/vendor/gimli-v0_32/src/write/writer.rs", + ] + inputs = [] + + build_native_rust_unit_tests = false + edition = "2018" + cargo_pkg_name = "gimli" + cargo_pkg_description = + "A library for reading and writing the DWARF debugging format." + cargo_pkg_repository = "https://github.com/gimli-rs/gimli" + cargo_pkg_version = "0.32.3" + + allow_unsafe = true + + features = [ + "read", + "read-core", + ] + + # Only for usage from third-party crates. Add the crate to + # //third_party/rust/chromium_crates_io/Cargo.toml to use + # it from first-party code. + visibility = [ "//third_party/rust/*" ] + testonly = true + + ##################################################################### + # Tweaking which GN `config`s apply to this target. + + # Config changes that apply to all `//third_party/rust` crates. + _configs_to_remove = [ + # We don't need code coverage data for any `chromium_crates_io` crates. + "//build/config/coverage:default_coverage", + + # This is third-party code, so remove `chromium_code` config. We are not + # at the same time adding `//build/config/compiler:no_chromium_code`, + # because 1) we don't want to pull how warnings are handled by that config + # and 2) that config doesn't have any non-warnings-related stuff. + "//build/config/compiler:chromium_code", + ] + _configs_to_add = [] + + # Changing (if needed) which configs apply to this specific crate (based on + # `extra_kv.configs_to_remove` and `extra_kv.configs_to_add` from + # `gnrt_config.toml`). + _configs_to_remove += [] + _configs_to_add += [] + + # Applying config changes. + library_configs -= _configs_to_remove + library_configs += _configs_to_add + executable_configs -= _configs_to_remove + executable_configs += _configs_to_add + proc_macro_configs -= _configs_to_remove + proc_macro_configs += _configs_to_add +}
diff --git a/gimli/v0_32/README.chromium b/gimli/v0_32/README.chromium new file mode 100644 index 0000000..bfa7144 --- /dev/null +++ b/gimli/v0_32/README.chromium
@@ -0,0 +1,11 @@ +Name: gimli +URL: https://crates.io/crates/gimli +Version: 0.32.3 +Revision: 8bc8e622fcb9be20fc9f03c96bc6335d936b869d +Update Mechanism: Manual (https://crbug.com/449898466) +License: Apache-2.0 +License File: //third_party/rust/chromium_crates_io/vendor/gimli-v0_32/LICENSE-APACHE +Shipped: no +Security Critical: no + +Description: A library for reading and writing the DWARF debugging format.
diff --git a/object/v0_37/BUILD.gn b/object/v0_37/BUILD.gn new file mode 100644 index 0000000..f9f0890 --- /dev/null +++ b/object/v0_37/BUILD.gn
@@ -0,0 +1,166 @@ +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# @generated from third_party/rust/chromium_crates_io/BUILD.gn.hbs by +# tools/crates/gnrt. +# Do not edit! + +import("//build/rust/cargo_crate.gni") + +cargo_crate("lib") { + crate_name = "object" + epoch = "0.37" + enabled = !is_win + crate_type = "rlib" + crate_root = + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/lib.rs" + sources = [ + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/archive.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/build/bytes.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/build/elf.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/build/error.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/build/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/build/table.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/common.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/elf.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/endian.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/lib.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/macho.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/pe.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/pod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/any.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/archive.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/coff/comdat.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/coff/file.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/coff/import.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/coff/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/coff/relocation.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/coff/section.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/coff/symbol.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/attributes.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/comdat.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/compression.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/dynamic.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/file.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/hash.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/note.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/relocation.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/section.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/segment.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/symbol.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/elf/version.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/gnu_compression.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/dyld_cache.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/fat.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/file.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/load_command.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/relocation.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/section.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/segment.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/macho/symbol.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/data_directory.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/export.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/file.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/import.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/relocation.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/resource.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/rich.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/pe/section.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/read_cache.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/read_ref.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/traits.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/util.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/wasm.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/comdat.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/file.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/relocation.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/section.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/segment.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/read/xcoff/symbol.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/coff/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/coff/object.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/coff/writer.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/elf/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/elf/object.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/elf/writer.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/macho.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/pe.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/string.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/util.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/write/xcoff.rs", + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/src/xcoff.rs", + ] + inputs = [] + + build_native_rust_unit_tests = false + edition = "2018" + cargo_pkg_name = "object" + cargo_pkg_description = + "A unified interface for reading and writing object file formats." + cargo_pkg_repository = "https://github.com/gimli-rs/object" + cargo_pkg_version = "0.37.3" + + allow_unsafe = true + + deps = [] + if (!is_win) { + deps += [ "//third_party/rust/memchr/v2:lib" ] + } + features = [ + "archive", + "coff", + "elf", + "macho", + "pe", + "read_core", + "unaligned", + "xcoff", + ] + build_root = + "//third_party/rust/chromium_crates_io/vendor/object-v0_37/build.rs" + build_sources = + [ "//third_party/rust/chromium_crates_io/vendor/object-v0_37/build.rs" ] + + # Only for usage from third-party crates. Add the crate to + # //third_party/rust/chromium_crates_io/Cargo.toml to use + # it from first-party code. + visibility = [ "//third_party/rust/*" ] + testonly = true + + ##################################################################### + # Tweaking which GN `config`s apply to this target. + + # Config changes that apply to all `//third_party/rust` crates. + _configs_to_remove = [ + # We don't need code coverage data for any `chromium_crates_io` crates. + "//build/config/coverage:default_coverage", + + # This is third-party code, so remove `chromium_code` config. We are not + # at the same time adding `//build/config/compiler:no_chromium_code`, + # because 1) we don't want to pull how warnings are handled by that config + # and 2) that config doesn't have any non-warnings-related stuff. + "//build/config/compiler:chromium_code", + ] + _configs_to_add = [] + + # Changing (if needed) which configs apply to this specific crate (based on + # `extra_kv.configs_to_remove` and `extra_kv.configs_to_add` from + # `gnrt_config.toml`). + _configs_to_remove += [] + _configs_to_add += [] + + # Applying config changes. + library_configs -= _configs_to_remove + library_configs += _configs_to_add + executable_configs -= _configs_to_remove + executable_configs += _configs_to_add + proc_macro_configs -= _configs_to_remove + proc_macro_configs += _configs_to_add +}
diff --git a/object/v0_37/README.chromium b/object/v0_37/README.chromium new file mode 100644 index 0000000..882f420 --- /dev/null +++ b/object/v0_37/README.chromium
@@ -0,0 +1,11 @@ +Name: object +URL: https://crates.io/crates/object +Version: 0.37.3 +Revision: 916c47b90e5c0bea139ca4bdfc53811f7d2c3383 +Update Mechanism: Manual (https://crbug.com/449898466) +License: Apache-2.0 +License File: //third_party/rust/chromium_crates_io/vendor/object-v0_37/LICENSE-APACHE +Shipped: no +Security Critical: no + +Description: A unified interface for reading and writing object file formats.