diff --git a/Cargo.lock b/Cargo.lock index b41c7dde492..dee40a28978 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1255,6 +1255,14 @@ dependencies = [ "serde_json", ] +[[package]] +name = "elf" +version = "0.7.4" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + [[package]] name = "elsa" version = "1.7.1" @@ -5377,6 +5385,7 @@ dependencies = [ "compiler_builtins", "core", "dlmalloc", + "elf", "fortanix-sgx-abi", "hashbrown", "hermit-abi 0.4.0", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index c60cd79b66c..f566c4da5f3 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -71,7 +71,7 @@ x86_64 = { path = "../../mikros_std_deps/x86_64-0.15.1", default-features = fals # derive-try-from-primitive = "1.0.0" # serde = { version = "1.0.151", default-features = false, features = ["alloc", "derive"] } # postcard = { version = "1.0.2", default-features = false, features = ["alloc"] } -# elf = { version = "0.7.4", default-features = false } +elf = { path = "../../mikros_std_deps/elf-0.7.4/", default-features = false, features = ["rustc-dep-of-std"] } [features] diff --git a/library/std/src/os/mikros/address_space.rs b/library/std/src/os/mikros/address_space.rs index 04ae8c99e61..8cd8555dd90 100644 --- a/library/std/src/os/mikros/address_space.rs +++ b/library/std/src/os/mikros/address_space.rs @@ -3,7 +3,7 @@ use crate::sys::{ buffers::KernelBufferAllocator, - syscalls::{copy_to, drop_space, map_assert_unused, map_only_unused, map_free, new_space}, + syscalls::{copy_to, drop_space, map_assert_unused, map_only_unused, map_free, new_space, adsp_zero}, }; use alloc::vec::Vec; use crate::mem; @@ -81,6 +81,11 @@ pub fn copy_to(&self, dst: *mut u8, src: &[u8]) -> Result<(), PagingError> { buf.extend_from_slice(src); copy_to(self.0, buf, dst, src.len()).map_err(|_| PagingError) } + + #[stable(feature = "mikros", since = "1.80.0")] + pub fn zero(&self, dst: *mut u8, len: usize) -> Result<(), PagingError> { + adsp_zero(self.0, dst, len).map_err(|_| PagingError) + } } #[stable(feature = "mikros", since = "1.80.0")] diff --git a/library/std/src/os/mikros/loader.rs b/library/std/src/os/mikros/loader.rs new file mode 100644 index 00000000000..0625d0c0893 --- /dev/null +++ b/library/std/src/os/mikros/loader.rs @@ -0,0 +1,75 @@ +#![stable(feature = "mikros", since = "1.80.0")] + +use elf::{abi::{PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_GNU_STACK, PT_LOAD, PT_NULL, PT_PHDR, R_X86_64_RELATIVE, SHT_REL, SHT_RELA}, endian::AnyEndian, ElfBytes}; +use x86_64::{structures::paging::Page, VirtAddr}; + +use super::address_space::AddressSpace; +use crate::ptr; + +#[stable(feature = "mikros", since = "1.80.0")] +pub struct Loader; + +impl Loader { + #[must_use] + #[stable(feature = "mikros", since = "1.80.0")] + pub fn load(binary_raw: &[u8]) -> (AddressSpace, *const ()) { + let binary = ElfBytes::::minimal_parse(&binary_raw).unwrap(); + let mut addr_space = AddressSpace::new(); + for mut pheader in binary.segments().unwrap().iter() { + match pheader.p_type { + PT_NULL => (), + PT_LOAD => { + if pheader.p_vaddr < 0x1000 { + if pheader.p_memsz < 0x1000 { + continue; + } + pheader.p_offset += 0x1000-pheader.p_vaddr; + pheader.p_memsz -= 0x1000-pheader.p_vaddr; + pheader.p_filesz -= 0x1000-pheader.p_vaddr; + pheader.p_vaddr = 0x1000; + } + let start_page = Page::containing_address(VirtAddr::new(pheader.p_vaddr)); + let num_pages = (pheader.p_memsz.div_ceil(4096) + + (pheader.p_vaddr & 0xFFF).div_ceil(4096)) + as usize; + assert!( + (start_page.start_address().as_u64() + num_pages as u64 * 4096) + >= (pheader.p_vaddr + pheader.p_memsz) + ); + #[allow(clippy::cast_possible_truncation)] + addr_space + .map_only_unused(start_page, num_pages) + .expect("Unable to map region"); + addr_space.copy_to(ptr::with_exposed_provenance_mut(pheader.p_vaddr as usize), &binary_raw[(pheader.p_offset as usize)..((pheader.p_offset+pheader.p_filesz) as usize)]).unwrap(); + addr_space.zero(ptr::with_exposed_provenance_mut((pheader.p_vaddr + pheader.p_filesz) as usize), (pheader.p_memsz - pheader.p_filesz) as usize).unwrap(); + } + PT_GNU_RELRO => (), + PT_GNU_EH_FRAME => (), + PT_GNU_STACK => (), + PT_DYNAMIC => (), + PT_PHDR => (), + _ => println!("Warning: Unimplemented ELF program header type {:#x}", pheader.p_type), + } + } + for section in binary.section_headers().unwrap().iter() { + if section.sh_type == SHT_REL { + for rel in binary.section_data_as_rels(§ion).unwrap() { + match rel.r_type { + _ => unimplemented!("ELF relocation type {}", rel.r_type), + } + } + } + if section.sh_type == SHT_RELA { + for rela in binary.section_data_as_relas(§ion).unwrap() { + match rela.r_type { + R_X86_64_RELATIVE => { + addr_space.copy_to(ptr::with_exposed_provenance_mut(rela.r_offset as usize), (rela.r_addend as u64).to_ne_bytes().as_ref()).unwrap(); + } + _ => unimplemented!("ELF relocation type {}", rela.r_type), + } + } + } + } + (addr_space, ptr::with_exposed_provenance(binary.ehdr.e_entry as usize)) + } +} diff --git a/library/std/src/os/mikros/mod.rs b/library/std/src/os/mikros/mod.rs index a68165b1f41..167eb3435ba 100644 --- a/library/std/src/os/mikros/mod.rs +++ b/library/std/src/os/mikros/mod.rs @@ -4,4 +4,5 @@ pub mod ipc; pub mod address_space; - +pub mod loader; +pub mod syscalls; diff --git a/library/std/src/os/mikros/syscalls.rs b/library/std/src/os/mikros/syscalls.rs new file mode 100644 index 00000000000..b10f631165e --- /dev/null +++ b/library/std/src/os/mikros/syscalls.rs @@ -0,0 +1,33 @@ +#![stable(feature = "mikros", since = "1.80.0")] +#![allow(clippy::result_unit_err)] + +use crate::sys::syscalls; +use crate::os::mikros::address_space::AddressSpace; + +#[must_use] +#[stable(feature = "mikros", since = "1.80.0")] +pub fn get_initrd() -> &'static [u8] { + syscalls::get_initrd() +} + +#[stable(feature = "mikros", since = "1.80.0")] +pub fn new_process(entry_point: u64, space: AddressSpace) -> Result { + syscalls::new_process(entry_point, space) +} + +#[stable(feature = "mikros", since = "1.80.0")] +pub fn register(typ: u64) { + syscalls::register(typ) +} + +#[stable(feature = "mikros", since = "1.80.0")] +#[must_use] +pub fn try_get_registered(typ: u64) -> Option { + syscalls::try_get_registered(typ) +} + +#[stable(feature = "mikros", since = "1.80.0")] +#[must_use] +pub fn get_pid() -> u64 { + syscalls::get_pid() +} diff --git a/library/std/src/sys/pal/mikros/syscalls.rs b/library/std/src/sys/pal/mikros/syscalls.rs index 46957afda2a..f0963cf722a 100644 --- a/library/std/src/sys/pal/mikros/syscalls.rs +++ b/library/std/src/sys/pal/mikros/syscalls.rs @@ -160,3 +160,15 @@ pub(crate) fn map_only_unused( Ok(()) } } + +pub(crate) fn adsp_zero( + space: u64, + dst: *mut u8, + len: usize, +) -> Result<(), ()> { + if syscall3(17, space, dst as u64, len as u64) == 1 { + Err(()) + } else { + Ok(()) + } +} diff --git a/mikros_std_deps/elf-0.7.4/.cargo_vcs_info.json b/mikros_std_deps/elf-0.7.4/.cargo_vcs_info.json new file mode 100644 index 00000000000..632e8d00e0e --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "94d6780552465ebdc3d732f4a32d6bd00f1b8d70" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/mikros_std_deps/elf-0.7.4/CHANGELOG.md b/mikros_std_deps/elf-0.7.4/CHANGELOG.md new file mode 100644 index 00000000000..76b4256eb60 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/CHANGELOG.md @@ -0,0 +1,190 @@ +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) + +## [0.7.4] - 2023-11-22 + +### Bug Fixes + +- Fix note parsing for notes with n_namesz == (align * x + 1) + +## [0.7.3] - 2023-10-09 + +### New Features + +- Derive Debug on LittleEndian and BigEndian + +### Misc Improvements + +- Enable #![forbid(unsafe_code)] +- Enable #![deny(missing_debug_implementations)] +- Enable #![warn(rust_2018_idioms)] +- Fix doc comment on file::Class +- Fix README example so it compiles + +## [0.7.2] - 2023-02-15 + +### New Features + +- Implement core::error::Error for ParsingError accessible via a new non-default "nightly" cargo feature +- Add abi constants for note descriptor types (n_type) +- Add C-style struct definitions for various abi structs (Elf[32|64]_Ehdr etc). These aren't used by the parser, but are useful definitions for folks wanting to manually muck with elf bytes. + +### Bug Fixes + +- Fix an 'attempt to shift right with overflow' panic in the GnuHashTable if nshift is wider than the bloom filter word size + +### Misc Improvements + +- Add doc comments for EM_* abi constants +- Tweak formatting and update language for various doc comments + +## [0.7.1] - 2023-01-08 + +### Bug Fixes + +- Fix a divide by zero panic in GnuHashTable.find() for tables with nbloom = 0 + +## [0.7.0] - 2022-11-14 + +### New Features + +- Add new ElfBytes type with better ergonomics for parsing from a &[u8] +- Add GnuHashTable which interprets the contents of a SHT_GNU_HASH section +- Add convenience method section_header_by_name to ElfBytes and ElfStream +- Add GnuBuildIdNote and parsing for NT_GNU_BUILD_ID note contents +- Add GnuAbiTagNote and parsing for NT_GNU_ABI_TAG note contents +- Add ElfBytes::symbol_version_table() to get the GNU extension symbol version table. +- Add ElfBytes::find_common_data() to efficiently discover common ELF structures +- Add a new endian-aware integer parsing trait impl +- Add ParsingTable.is_empty() +- Add abi constants for powerpc and powerpc64 +- Add abi constants for RISC-V +- Add abi constants for x86_64 +- Add abi constants for ARM32 and ARM64 (AARCH64) +- Add abi constant for GNU-extension ELF note name ELF_NOTE_GNU +- Add abi constant for PT_GNU_PROPERTY +- Add abi constants for SHN_ABS and SHN_COMMON +- Add elf::to_str::d_tag_to_str() +- Add elf::to_str::note_abi_tag_os_to_str() + +### Changed Interfaces + +- Rename elf::File -> elf::ElfStream and make it specific to the Read + Seek interface +- Rename gabi -> abi since it also includes extension constants +- Make ELF structures generic across the new endian-aware integer parsing trait EndianParse +- Refactor parsed Note type to be a typed enum +- Rename ElfStream::dynamic_section() -> dynamic() to match ElfBytes +- Change ElfStream::dynamic() to yield a DynamicTable just like in ElfBytes +- Standardize ElfBytes' interfaces for the .dynamic contents to return a DynamicTable +- Export the parsing utilities ParsingTable, ParsingIterator in the public interface +- Refactor section_headers_with_strtab to work with files that have shdrs but no shstrtab +- Remove redundant hash arg from SysVHashTable.find() +- Expose Class in the public interface alongside FileHeader +- Remove opinionated Display impl for file::Class +- Remove section_data_as_symbol_table() from public ElfBytes interface +- Change SymbolVersionTable::new() to take Options instead of Default-empty iterators +- Change ElfStream to parse out the ProgramHeaders into an allocated vec as part of ElfStream::open_stream() +- Change ElfStream to parse out the SectionHeaders into an allocated Vec as part of ElfStream::open_stream() + +### Bug Fixes + +- Properly parse program header table when ehdr.e_phnum > 0xffff +- Fix OOM in ElfStream parsing when parsing corrupted files +- Fix a divide by zero panic in SysVHashTable.find() for empty tables + +### Misc Improvements + +- Add more fuzz testing +- Add some simple parsing smoke tests for the various sample architecture objects +- Add sample object and testing with > 0xff00 section headers +- Add a lot more doc comments to each of the modules + +## [0.6.1] - 2022-11-05 + +### New Features +- Expose Class and Endian in the public interface. These types are exposed in the FileHeader type and so they should also be accessible for users to inspect. + +## [0.6.0] - 2022-11-01 + +### New Features + +- Add fuzz targets for parts of our ELF parsing interface via cargo-fuzz +- Add SysVHashTable which interprets the contents of a SHT_HASH section +- Add StringTable::get_raw() to get an uninterpreted &[u8] +- Add ParsingTable.len() method to get the number of elements in the table +- Add some note n_type constants for GNU extension notes. +- Add default "to_str" feature to get &str for gabi constant names + +### Changed Interfaces + +- Change File::segments() to return a ParsingTable instead of just a ParsingIterator +- Change File's SectionHeader interfaces to provide a ParsingTable instead of just a ParsingIterator +- Remove deprecated File::section_data_for_header() in favor of File::section_data() +- Remove FileHeader wrapper types OSABI, Architecture, and ObjectFileType +- Remove ProgramHeader wrapper types ProgType and ProgFlag +- Remove Symbol wrapper types SymbolType SymbolBind SymbolVis +- Remove wrapper type SectionType +- Remove unhelpful SectionFlag wrapper type +- Remove Display impl for FileHeader, SectionHeader, ProgramHeader, Symbol +- Remove ParseError::UnsupportedElfVersion in favor of more general ParseError::UnsupportedVersion + +### Bug Fixes + +- Fix divide by zero panic when parsing a note with alignment of 0 (Error instead of panic) +- Use checked integer math all over the parsing code (Error instead of panic or overflow) +- Fix note parsing for 8-byte aligned .note.gnu.property sections (Successfully parse instead of Erroring) +- Add size validation when parsing tables with entsizes (Error instead of panic) + +## [0.5.0] - 2022-10-30 + +### New Features + +- Add File::symbol_version_table() interface to get the GNU extension symbol versioning table +- Add Symbol.is_undefined() helper to check if a symbol is defined in this object +- Add File::section_data() which opportunistically parses the CompressionHeader if present + +### Bug Fixes + +- Fix StringTable to return a ParseError on index out of bounds instead of panicking +- Fix File::section_data_as_rels to properly parse Rels (not Relas) + +## [0.4.0] - 2022-10-24 + +### New Features + +- Add .note section and segment parsing +- Add .dynamic section and segment parsing +- Add .rel and .rela section parsing +- Add File::section_headers_with_strtab to get both a header iter and strtab concurrently. + +### Changed Interfaces + +- The ReadBytesAt trait was changed to be implemented for an owned CachedReadBytes. This means that File::open_stream now expects to move-own the CachedReadBytes as opposed to taking a mutable reference. + +## [0.3.1] - 2022-10-21 + +### New Features +- Add File::section_data_for_header() to get raw section data for a given section + +### Bug fixes +- Fix section header table parsing when ehdr.e_shnum > 0xff00 + +## [0.3.0] - 2022-10-20 + +### New Features +- Add a `no_std` option by fully moving the parser over to lazy zero-alloc parsing patterns. + + +[0.7.4]: https://github.com/cole14/rust-elf/compare/v0.7.3...v0.7.4 +[0.7.3]: https://github.com/cole14/rust-elf/compare/v0.7.2...v0.7.3 +[0.7.2]: https://github.com/cole14/rust-elf/compare/v0.7.1...v0.7.2 +[0.7.1]: https://github.com/cole14/rust-elf/compare/v0.7.0...v0.7.1 +[0.7.0]: https://github.com/cole14/rust-elf/compare/v0.6.1...v0.7.0 +[0.6.1]: https://github.com/cole14/rust-elf/compare/v0.6.0...v0.6.1 +[0.6.0]: https://github.com/cole14/rust-elf/compare/v0.5.0...v0.6.0 +[0.5.0]: https://github.com/cole14/rust-elf/compare/v0.4.0...v0.5.0 +[0.4.0]: https://github.com/cole14/rust-elf/compare/v0.3.1...v0.4.0 +[0.3.1]: https://github.com/cole14/rust-elf/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/cole14/rust-elf/compare/v0.2.0...v0.3.0 diff --git a/mikros_std_deps/elf-0.7.4/COPYRIGHT b/mikros_std_deps/elf-0.7.4/COPYRIGHT new file mode 100644 index 00000000000..767cb81e479 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/COPYRIGHT @@ -0,0 +1,12 @@ +rust-elf is licensed under either of + + * Apache License, Version 2.0 (LICENSE-APACHE or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://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/mikros_std_deps/elf-0.7.4/Cargo.toml b/mikros_std_deps/elf-0.7.4/Cargo.toml new file mode 100644 index 00000000000..189a0ac3981 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/Cargo.toml @@ -0,0 +1,62 @@ +# 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" +name = "elf" +version = "0.7.4" +authors = ["Christopher Cole "] +exclude = [ + ".gitignore", + "/.github", + "/sample-objects", +] +description = "A pure-rust library for parsing ELF files" +documentation = "https://docs.rs/elf/latest/elf/" +readme = "README.md" +keywords = [ + "binary", + "elf", + "object", + "parser", +] +categories = [ + "no-std", + "os", + "embedded", +] +license = "MIT/Apache-2.0" +repository = "https://github.com/cole14/rust-elf/" + +[lib] +name = "elf" + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" + +[dependencies.compiler_builtins] +version = "0.1" +optional = true + +[features] +default = [ + "std", + "to_str", +] +nightly = [] +std = [] +to_str = [] +rustc-dep-of-std = [ + "core", + "compiler_builtins", +] diff --git a/mikros_std_deps/elf-0.7.4/Cargo.toml.orig b/mikros_std_deps/elf-0.7.4/Cargo.toml.orig new file mode 100644 index 00000000000..b360816c5c2 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/Cargo.toml.orig @@ -0,0 +1,25 @@ +[package] +name = "elf" +version = "0.7.4" +authors = ["Christopher Cole "] +license = "MIT/Apache-2.0" +repository = "https://github.com/cole14/rust-elf/" +documentation = "https://docs.rs/elf/latest/elf/" +description = "A pure-rust library for parsing ELF files" +keywords = ["binary", "elf", "object", "parser"] +categories = ["no-std", "os", "embedded"] +exclude = [".gitignore", "/.github", "/sample-objects"] +readme = "README.md" +edition = "2021" + +[lib] +name = "elf" + +[dependencies] + +[features] +default = ["std", "to_str"] +std = [] +to_str = [] +# Enable for nightly feature(error_in_core) to impl core::error::Error on ParseError +nightly = [] diff --git a/mikros_std_deps/elf-0.7.4/LICENSE-APACHE b/mikros_std_deps/elf-0.7.4/LICENSE-APACHE new file mode 100644 index 00000000000..ebe4b418f80 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/LICENSE-APACHE @@ -0,0 +1,202 @@ +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 2016 Christopher Cole + + 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/mikros_std_deps/elf-0.7.4/LICENSE-MIT b/mikros_std_deps/elf-0.7.4/LICENSE-MIT new file mode 100644 index 00000000000..cdd6fa3feb1 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Christopher Cole + +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/mikros_std_deps/elf-0.7.4/README.md b/mikros_std_deps/elf-0.7.4/README.md new file mode 100644 index 00000000000..eae313f65b6 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/README.md @@ -0,0 +1,136 @@ +[![](https://img.shields.io/crates/v/elf.svg)](https://crates.io/crates/elf) +[![](https://img.shields.io/crates/d/elf.svg)](https://crates.io/crates/elf) +[![Build Status](https://github.com/cole14/rust-elf/actions/workflows/rust.yml/badge.svg)](https://github.com/cole14/rust-elf/actions) +[![](https://docs.rs/elf/badge.svg)](https://docs.rs/elf/) + +# rust-elf + +The `elf` crate provides a pure-safe-rust interface for reading ELF object files. + +[Documentation](https://docs.rs/elf/) + +# Capabilities + +### ✨ Works in `no_std` environments ✨ +This crate provides an elf parsing interface which does not allocate or use any std +features, so it can be used in `no_std` environments such as kernels and bootloaders. +The no_std variant merely disables the additional stream-oriented `std:: Read + Seek` interface. +All core parsing functionality is the same! + +### ✨ Endian-aware ✨ +This crate handles translating between file and host endianness when +parsing the ELF contents and provides four endian parsing implementations +optimized to support the different common use-cases for an ELF parsing library. +Parsing is generic across the specifications and each trait impl represents a +specification that encapsulates an interface for parsing integers from some +set of allowed byte orderings. + +* `AnyEndian`: Dynamically parsing either byte order at runtime based on the type of ELF object being parsed. +* `BigEndian`/`LittleEndian`: For tools that know they only want to parse a single given byte order known at compile time. +* `NativeEndian`: For tools that know they want to parse the same byte order as the compilation target's byte order. + +When the limited specifications are used, errors are properly returned when asked to parse an ELF file +with an unexpected byte ordering. + +### ✨ Zero-alloc parser ✨ +This crate implements parsing in a way that avoids heap allocations. ELF structures +are parsed and stored on the stack and provided by patterns such as lazily parsed iterators +that yield stack allocated rust types, or lazily parsing tables that only parse out a particular +entry on table.get(index). The structures are copy-converted as needed from the underlying file +data into Rust's native struct representation. + +### ✨ Fuzz Tested ✨ +Various parts of the library are fuzz tested for panics and crashes (see `fuzz/`). + +Memory safety is a core goal, as is providing a safe interface that errors on bad data +over crashing or panicking. Checked integer math is used where appropriate, and ParseErrors are +returned when bad or corrupted ELF structures are encountered. + +### ✨ Uses only safe interfaces ✨ +With memory safety a core goal, this crate contains zero unsafe code blocks of +its own and only uses safe interface methods from core and std, so you can +trust in rust's memory safety guarantees without also having to trust this +library developer as having truly been "right" in why some unsafe block was +safe. 💃 + +Note: I'd love to see this crate be enhanced further once rust provides safe transmutes. + +See: + +### ✨ Some zero-copy interfaces ✨ +The StringTable, for instance, yields `&[u8]` and `&str` backed by the raw string table bytes. + +The `ElfBytes` parser type also does not make raw copies of the underlying file data to back +the parser lazy parser interfaces `ParsingIterator` and `ParsingTable`. They merely wrap byte slices +internally, and yield rust repr values on demand, which does entail copying of the bytes into the +parsed rust-native format. + +Depending on the use-case, it can be more efficient to restructure the raw ELF into different layouts +for more efficient interpretation, say, by re-indexing a flat table into a HashMap. `ParsingIterator`s +make that easy and rustily-intuitive. + +The `ParsingIterator`s are also nice in that you can easily zip/enumerate/filter/collect them +how you wish. Do you know that you want to do multiple passes over pairs from different tables? Just +zip/collect them into another type so you only parse/endian-flip each entry once! + +### ✨ Stream-based lazy i/o interface ✨ +The `ElfStream` parser type takes a `std:: Read + Seek` (such as `std::fs::File`) where ranges of +file contents are read lazily on-demand based on what the user wants to parse. + +This, alongside the bytes-oriented interface, allow you to decide which tradeoffs +you want to make. If you're going to be working with the whole file contents, +then the byte slice approach is probably worthwhile to minimize i/o overhead by +streaming the whole file into memory at once. If you're only going to be +inspecting part of the file, then the `ElfStream` approach would help avoid the +overhead of reading a bunch of unused file data just to parse out a few things, (like +grabbing the `.gnu.note.build-id`) + +### ✨ Tiny library with no dependencies and fast compilation times ✨ +Release-target compilation times on this developer's 2021 m1 macbook are sub-second. + +## Example using `ElfBytes`: + +```rust +use elf::ElfBytes; +use elf::endian::AnyEndian; +use elf::note::Note; +use elf::note::NoteGnuBuildId; +use elf::section::SectionHeader; + +let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); +let file_data = std::fs::read(path).expect("Could not read file."); +let slice = file_data.as_slice(); +let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + +// Get the ELF file's build-id +let abi_shdr: SectionHeader = file + .section_header_by_name(".note.gnu.build-id") + .expect("section table should be parseable") + .expect("file should have a .note.ABI-tag section"); + +let notes: Vec = file + .section_data_as_notes(&abi_shdr) + .expect("Should be able to get note section data") + .collect(); +assert_eq!( + notes[0], + Note::GnuBuildId(NoteGnuBuildId( + &[140, 51, 19, 23, 221, 90, 215, 131, 169, 13, + 210, 183, 215, 77, 216, 175, 167, 110, 3, 209])) +); + +// Find lazy-parsing types for the common ELF sections (we want .dynsym, .dynstr, .hash) +let common = file.find_common_data().expect("shdrs should parse"); +let (dynsyms, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap()); +let hash_table = common.sysv_hash.unwrap(); + +// Use the hash table to find a given symbol in it. +let name = b"memset"; +let (sym_idx, sym) = hash_table.find(name, &dynsyms, &strtab) + .expect("hash table and symbols should parse").unwrap(); + +// Verify that we got the same symbol from the hash table we expected +assert_eq!(sym_idx, 2); +assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset"); +assert_eq!(sym, dynsyms.get(sym_idx).unwrap()); +``` diff --git a/mikros_std_deps/elf-0.7.4/src/abi.rs b/mikros_std_deps/elf-0.7.4/src/abi.rs new file mode 100644 index 00000000000..61ba47f6e29 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/abi.rs @@ -0,0 +1,2660 @@ +//! Contains ELF constants defined in the ELF gABI and various extensions +// See +// Note: At least in 2022, it seems like the above site is not being updated. Official communication +// occurs on the Generic System V Application Binary Interface mailing list: +// + +// EI_* define indexes into the ELF File Header's e_ident[] byte array. +// We define them as usize in order to use them to easily index into [u8]. + +/// Location of first ELF magic number byte +pub const EI_MAG0: usize = 0; +/// Location of second ELF magic number byte +pub const EI_MAG1: usize = 1; +/// Location of third ELF magic number byte +pub const EI_MAG2: usize = 2; +/// Location of fourth ELF magic number byte +pub const EI_MAG3: usize = 3; +/// Location of ELF class field in ELF file header ident array +pub const EI_CLASS: usize = 4; +/// Location of data format field in ELF file header ident array +pub const EI_DATA: usize = 5; +/// Location of ELF version field in ELF file header ident array +pub const EI_VERSION: usize = 6; +/// Location of OS ABI field in ELF file header ident array +pub const EI_OSABI: usize = 7; +/// Location of ABI version field in ELF file header ident array +pub const EI_ABIVERSION: usize = 8; +/// Start of padding bytes +pub const EI_PAD: usize = 9; +/// Length of ELF file header platform-independent identification fields (e_ident[]) +pub const EI_NIDENT: usize = 16; + +/// ELF magic number byte 1 +pub const ELFMAG0: u8 = 0x7f; +/// ELF magic number byte 2 +pub const ELFMAG1: u8 = 0x45; +/// ELF magic number byte 3 +pub const ELFMAG2: u8 = 0x4c; +/// ELF magic number byte 4 +pub const ELFMAG3: u8 = 0x46; +pub const ELFMAGIC: [u8; 4] = [ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3]; + +// ELFCLASS* define constants for e_ident[EI_CLASS] + +/// Invalid ELF file class +pub const ELFCLASSNONE: u8 = 0; +/// 32-bit ELF file +pub const ELFCLASS32: u8 = 1; +/// 64-bit ELF file +pub const ELFCLASS64: u8 = 2; + +// ELFDATA* define constants for e_ident[EI_DATA] + +/// Invalid ELF data format +pub const ELFDATANONE: u8 = 0; +/// 2's complement values, with the least significant byte occupying the lowest address. +pub const ELFDATA2LSB: u8 = 1; +/// 2's complement values, with the most significant byte occupying the lowest address. +pub const ELFDATA2MSB: u8 = 2; + +// ELFOSABI* define constants for e_ident[EI_OSABI] + +/// No extensions or unspecified +pub const ELFOSABI_NONE: u8 = 0; +/// Alias of unspecified for UNIX System V ABI +pub const ELFOSABI_SYSV: u8 = 0; +/// Hewlett-Packard HP-UX +pub const ELFOSABI_HPUX: u8 = 1; +/// NetBSD +pub const ELFOSABI_NETBSD: u8 = 2; +/// GNU +pub const ELFOSABI_GNU: u8 = 3; +/// Linux historical - alias for ELFOSABI_GNU +pub const ELFOSABI_LINUX: u8 = 3; +/// Sun Solaris +pub const ELFOSABI_SOLARIS: u8 = 6; +/// AIX +pub const ELFOSABI_AIX: u8 = 7; +/// 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; +/// Open BSD +pub const ELFOSABI_OPENBSD: u8 = 12; +/// Open VMS +pub const ELFOSABI_OPENVMS: u8 = 13; +/// Hewlett-Packard Non-Stop Kernel +pub const ELFOSABI_NSK: u8 = 14; +/// Amiga Research OS +pub const ELFOSABI_AROS: u8 = 15; +/// The FenixOS highly scalable multi-core OS +pub const ELFOSABI_FENIXOS: u8 = 16; +/// Nuxi CloudABI +pub const ELFOSABI_CLOUDABI: u8 = 17; +/// Stratus Technologies OpenVOS +pub const ELFOSABI_OPENVOS: u8 = 18; +/// 64-255 Architecture-specific value range + +// ET_* define constants for the ELF File Header's e_type field. +// Represented as Elf32_Half in Elf32_Ehdr and Elf64_Half in Elf64_Ehdr which +// are both are 2-byte unsigned integers with 2-byte alignment + +/// 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; +/// Operating system-specific +pub const ET_LOOS: u16 = 0xfe00; +/// Operating system-specific +pub const ET_HIOS: u16 = 0xfeff; +/// Processor-specific +pub const ET_LOPROC: u16 = 0xff00; +/// Processor-specific +pub const ET_HIPROC: u16 = 0xffff; + +// EM_* define constants for the ELF File Header's e_machine field. +// Represented as Elf32_Half in Elf32_Ehdr and Elf64_Half in Elf64_Ehdr which +// are both 2-byte unsigned integers with 2-byte alignment + +/// No machine +pub const EM_NONE: u16 = 0; +/// AT&T WE 32100 +pub const EM_M32: u16 = 1; +/// SPARC +pub const EM_SPARC: u16 = 2; +/// Intel 80386 +pub const EM_386: u16 = 3; +/// Motorola 68000 +pub const EM_68K: u16 = 4; +/// Motorola 88000 +pub const EM_88K: u16 = 5; +/// Intel MCU +pub const EM_IAMCU: u16 = 6; +/// Intel 80860 +pub const EM_860: u16 = 7; +/// MIPS I Architecture +pub const EM_MIPS: u16 = 8; +/// IBM System/370 Processor +pub const EM_S370: u16 = 9; +/// MIPS RS3000 Little-endian +pub const EM_MIPS_RS3_LE: u16 = 10; +// 11-14 Reserved for future use +/// Hewlett-Packard PA-RISC +pub const EM_PARISC: u16 = 15; +// 16 Reserved for future use +/// Fujitsu VPP500 +pub const EM_VPP500: u16 = 17; +/// Enhanced instruction set SPARC +pub const EM_SPARC32PLUS: u16 = 18; +/// Intel 80960 +pub const EM_960: u16 = 19; +/// PowerPC +pub const EM_PPC: u16 = 20; +/// 64-bit PowerPC +pub const EM_PPC64: u16 = 21; +/// IBM System/390 Processor +pub const EM_S390: u16 = 22; +/// IBM SPU/SPC +pub const EM_SPU: u16 = 23; +// 24-35 Reserved for future use +/// NEC V800 +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 32-bit architecture (AARCH32) +pub const EM_ARM: u16 = 40; +/// Digital Alpha +pub const EM_ALPHA: u16 = 41; +/// Hitachi SH +pub const EM_SH: u16 = 42; +/// SPARC Version 9 +pub const EM_SPARCV9: u16 = 43; +/// Siemens TriCore embedded processor +pub const EM_TRICORE: u16 = 44; +/// Argonaut RISC Core, Argonaut Technologies Inc. +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 IA-64 processor architecture +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 embedded RISC processor +pub const EM_NCPU: u16 = 56; +/// Denso NDR1 microprocessor +pub const EM_NDR1: u16 = 57; +/// Motorola Star*Core processor +pub const EM_STARCORE: u16 = 58; +/// Toyota ME16 processor +pub const EM_ME16: u16 = 59; +/// STMicroelectronics ST100 processor +pub const EM_ST100: u16 = 60; +/// Advanced Logic Corp. TinyJ embedded processor family +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 Equipment Corp. PDP-10 +pub const EM_PDP10: u16 = 64; +/// Digital Equipment Corp. PDP-11 +pub const EM_PDP11: u16 = 65; +/// Siemens FX66 microcontroller +pub const EM_FX66: u16 = 66; +/// STMicroelectronics ST9+ 8/16 bit microcontroller +pub const EM_ST9PLUS: u16 = 67; +/// STMicroelectronics ST7 8-bit microcontroller +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 microcontroller +pub const EM_ST19: u16 = 74; +/// Digital VAX +pub const EM_VAX: u16 = 75; +/// Axis Communications 32-bit embedded processor +pub const EM_CRIS: u16 = 76; +/// Infineon Technologies 32-bit embedded processor +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 processor +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 processor (old spelling/synonym: EM_ARC_A5) +pub const EM_ARC_COMPACT: u16 = 93; +/// Tensilica Xtensa Architecture +pub const EM_XTENSA: u16 = 94; +/// Alphamosaic VideoCore processor +pub const EM_VIDEOCORE: u16 = 95; +/// Thompson Multimedia General Purpose Processor +pub const EM_TMM_GPP: u16 = 96; +/// National Semiconductor 32000 series +pub const EM_NS32K: u16 = 97; +/// Tenor Network TPC processor +pub const EM_TPC: u16 = 98; +/// Trebia SNP 1000 processor +pub const EM_SNP1K: u16 = 99; +/// STMicroelectronics (www.st.com) ST200 microcontroller +pub const EM_ST200: u16 = 100; +/// Ubicom IP2xxx microcontroller family +pub const EM_IP2K: u16 = 101; +/// MAX Processor +pub const EM_MAX: u16 = 102; +/// National Semiconductor CompactRISC microprocessor +pub const EM_CR: u16 = 103; +/// Fujitsu F2MC16 +pub const EM_F2MC16: u16 = 104; +/// Texas Instruments embedded microcontroller msp430 +pub const EM_MSP430: u16 = 105; +/// Analog Devices Blackfin (DSP) processor +pub const EM_BLACKFIN: u16 = 106; +/// S1C33 Family of Seiko Epson processors +pub const EM_SE_C33: u16 = 107; +/// Sharp embedded microprocessor +pub const EM_SEP: u16 = 108; +/// Arca RISC Microprocessor +pub const EM_ARCA: u16 = 109; +/// Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University +pub const EM_UNICORE: u16 = 110; +/// eXcess: 16/32/64-bit configurable embedded CPU +pub const EM_EXCESS: u16 = 111; +/// Icera Semiconductor Inc. Deep Execution Processor +pub const EM_DXP: u16 = 112; +/// Altera Nios II soft-core processor +pub const EM_ALTERA_NIOS2: u16 = 113; +/// National Semiconductor CompactRISC CRX microprocessor +pub const EM_CRX: u16 = 114; +/// Motorola XGATE embedded processor +pub const EM_XGATE: u16 = 115; +/// Infineon C16x/XC16x processor +pub const EM_C166: u16 = 116; +/// Renesas M16C series microprocessors +pub const EM_M16C: u16 = 117; +/// Microchip Technology dsPIC30F Digital Signal Controller +pub const EM_DSPIC30F: u16 = 118; +/// Freescale Communication Engine RISC core +pub const EM_CE: u16 = 119; +/// Renesas M32C series microprocessors +pub const EM_M32C: u16 = 120; +// 121-130 Reserved for future use +/// Altium TSK3000 core +pub const EM_TSK3000: u16 = 131; +/// Freescale RS08 embedded processor +pub const EM_RS08: u16 = 132; +/// Analog Devices SHARC family of 32-bit DSP processors +pub const EM_SHARC: u16 = 133; +/// Cyan Technology eCOG2 microprocessor +pub const EM_ECOG2: u16 = 134; +/// Sunplus S+core7 RISC processor +pub const EM_SCORE7: u16 = 135; +/// New Japan Radio (NJR) 24-bit DSP Processor +pub const EM_DSP24: u16 = 136; +/// Broadcom VideoCore III processor +pub const EM_VIDEOCORE3: u16 = 137; +/// RISC processor for Lattice FPGA architecture +pub const EM_LATTICEMICO32: u16 = 138; +/// Seiko Epson C17 family +pub const EM_SE_C17: u16 = 139; +/// The Texas Instruments TMS320C6000 DSP family +pub const EM_TI_C6000: u16 = 140; +/// The Texas Instruments TMS320C2000 DSP family +pub const EM_TI_C2000: u16 = 141; +/// The Texas Instruments TMS320C55x DSP family +pub const EM_TI_C5500: u16 = 142; +/// Texas Instruments Application Specific RISC Processor, 32bit fetch +pub const EM_TI_ARP32: u16 = 143; +/// Texas Instruments Programmable Realtime Unit +pub const EM_TI_PRU: u16 = 144; +// 145-159 Reserved for future use +/// STMicroelectronics 64bit VLIW Data Signal Processor +pub const EM_MMDSP_PLUS: u16 = 160; +/// Cypress M8C microprocessor +pub const EM_CYPRESS_M8C: u16 = 161; +/// Renesas R32C series microprocessors +pub const EM_R32C: u16 = 162; +/// NXP Semiconductors TriMedia architecture family +pub const EM_TRIMEDIA: u16 = 163; +/// QUALCOMM DSP6 Processor +pub const EM_QDSP6: u16 = 164; +/// Intel 8051 and variants +pub const EM_8051: u16 = 165; +/// STMicroelectronics STxP7x family of configurable and extensible RISC processors +pub const EM_STXP7X: u16 = 166; +/// Andes Technology compact code size embedded RISC processor family +pub const EM_NDS32: u16 = 167; +/// Cyan Technology eCOG1X family +pub const EM_ECOG1: u16 = 168; +/// Cyan Technology eCOG1X family +pub const EM_ECOG1X: u16 = 168; +/// Dallas Semiconductor MAXQ30 Core Micro-controllers +pub const EM_MAXQ30: u16 = 169; +/// New Japan Radio (NJR) 16-bit DSP Processor +pub const EM_XIMO16: u16 = 170; +/// M2000 Reconfigurable RISC Microprocessor +pub const EM_MANIK: u16 = 171; +/// Cray Inc. NV2 vector architecture +pub const EM_CRAYNV2: u16 = 172; +/// Renesas RX family +pub const EM_RX: u16 = 173; +/// Imagination Technologies META processor architecture +pub const EM_METAG: u16 = 174; +/// MCST Elbrus general purpose hardware architecture +pub const EM_MCST_ELBRUS: u16 = 175; +/// Cyan Technology eCOG16 family +pub const EM_ECOG16: u16 = 176; +/// National Semiconductor CompactRISC CR16 16-bit microprocessor +pub const EM_CR16: u16 = 177; +/// Freescale Extended Time Processing Unit +pub const EM_ETPU: u16 = 178; +/// Infineon Technologies SLE9X core +pub const EM_SLE9X: u16 = 179; +/// Intel L10M +pub const EM_L10M: u16 = 180; +/// Intel K10M +pub const EM_K10M: u16 = 181; +// 182 Reserved for future Intel use +/// ARM 64-bit architecture (AARCH64) +pub const EM_AARCH64: u16 = 183; +// 184 Reserved for future ARM use +/// Atmel Corporation 32-bit microprocessor family +pub const EM_AVR32: u16 = 185; +/// STMicroeletronics STM8 8-bit microcontroller +pub const EM_STM8: u16 = 186; +/// Tilera TILE64 multicore architecture family +pub const EM_TILE64: u16 = 187; +/// Tilera TILEPro multicore architecture family +pub const EM_TILEPRO: u16 = 188; +/// Xilinx MicroBlaze 32-bit RISC soft processor core +pub const EM_MICROBLAZE: u16 = 189; +/// NVIDIA CUDA architecture +pub const EM_CUDA: u16 = 190; +/// Tilera TILE-Gx multicore architecture family +pub const EM_TILEGX: u16 = 191; +/// CloudShield architecture family +pub const EM_CLOUDSHIELD: u16 = 192; +/// KIPO-KAIST Core-A 1st generation processor family +pub const EM_COREA_1ST: u16 = 193; +/// KIPO-KAIST Core-A 2nd generation processor family +pub const EM_COREA_2ND: u16 = 194; +/// Synopsys ARCompact V2 +pub const EM_ARC_COMPACT2: u16 = 195; +/// Open8 8-bit RISC soft processor core +pub const EM_OPEN8: u16 = 196; +/// Renesas RL78 family +pub const EM_RL78: u16 = 197; +/// Broadcom VideoCore V processor +pub const EM_VIDEOCORE5: u16 = 198; +/// Renesas 78KOR family +pub const EM_78KOR: u16 = 199; +/// Freescale 56800EX Digital Signal Controller (DSC) +pub const EM_56800EX: u16 = 200; +/// Beyond BA1 CPU architecture +pub const EM_BA1: u16 = 201; +/// Beyond BA2 CPU architecture +pub const EM_BA2: u16 = 202; +/// XMOS xCORE processor family +pub const EM_XCORE: u16 = 203; +/// Microchip 8-bit PIC(r) family +pub const EM_MCHP_PIC: u16 = 204; +/// Reserved by Intel +pub const EM_INTEL205: u16 = 205; +/// Reserved by Intel +pub const EM_INTEL206: u16 = 206; +/// Reserved by Intel +pub const EM_INTEL207: u16 = 207; +/// Reserved by Intel +pub const EM_INTEL208: u16 = 208; +/// Reserved by Intel +pub const EM_INTEL209: u16 = 209; +/// KM211 KM32 32-bit processor +pub const EM_KM32: u16 = 210; +/// KM211 KMX32 32-bit processor +pub const EM_KMX32: u16 = 211; +/// KM211 KMX16 16-bit processor +pub const EM_KMX16: u16 = 212; +/// KM211 KMX8 8-bit processor +pub const EM_KMX8: u16 = 213; +/// KM211 KVARC processor +pub const EM_KVARC: u16 = 214; +/// Paneve CDP architecture family +pub const EM_CDP: u16 = 215; +/// Cognitive Smart Memory Processor +pub const EM_COGE: u16 = 216; +/// Bluechip Systems CoolEngine +pub const EM_COOL: u16 = 217; +/// Nanoradio Optimized RISC +pub const EM_NORC: u16 = 218; +/// CSR Kalimba architecture family +pub const EM_CSR_KALIMBA: u16 = 219; +/// Zilog Z80 +pub const EM_Z80: u16 = 220; +/// Controls and Data Services VISIUMcore processor +pub const EM_VISIUM: u16 = 221; +/// FTDI Chip FT32 high performance 32-bit RISC architecture +pub const EM_FT32: u16 = 222; +/// Moxie processor family +pub const EM_MOXIE: u16 = 223; +/// AMD GPU architecture +pub const EM_AMDGPU: u16 = 224; +/// RISC-V +pub const EM_RISCV: u16 = 243; +/// Linux BPF +pub const EM_BPF: u16 = 247; + +// EV_* define constants for the ELF File Header's e_version field. +// Represented as Elf32_Word in Elf32_Ehdr and Elf64_Word in Elf64_Ehdr which +// are both 4-byte unsigned integers with 4-byte alignment + +/// Invalid version +pub const EV_NONE: u8 = 0; +/// Current version +pub const EV_CURRENT: u8 = 1; + +/// If the number of program headers is greater than or equal to PN_XNUM (0xffff), +/// this member has the value PN_XNUM (0xffff). The actual number of +/// program header table entries is contained in the sh_info field of the +/// section header at index 0. Otherwise, the sh_info member of the initial +/// section header entry contains the value zero. +pub const PN_XNUM: u16 = 0xffff; + +// PF_* define constants for the ELF Program Header's p_flags field. +// Represented as Elf32_Word in Elf32_Ehdr and Elf64_Word in Elf64_Ehdr which +// are both 4-byte unsigned integers with 4-byte alignment + +pub const PF_NONE: u32 = 0; +/// Executable program segment +pub const PF_X: u32 = 1; +/// Writable program segment +pub const PF_W: u32 = 2; +/// Readable program segment +pub const PF_R: u32 = 4; +// All bits included in the PF_MASKOS mask are reserved for operating system-specific semantics. +pub const PF_MASKOS: u32 = 0x0ff00000; +// All bits included in the PF_MASKPROC mask are reserved for processor-specific semantics. +pub const PF_MASKPROC: u32 = 0xf0000000; + +// PT_* define constants for the ELF Program Header's p_type field. +// Represented as Elf32_Word in Elf32_Ehdr and Elf64_Word in Elf64_Ehdr which +// are both 4-byte unsigned integers with 4-byte alignment + +/// Program header table entry 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; +/// Unused +pub const PT_SHLIB: u32 = 5; +/// The program header table +pub const PT_PHDR: u32 = 6; +/// Thread-local storage segment +pub const PT_TLS: u32 = 7; +/// GCC .eh_frame_hdr segment +pub const PT_GNU_EH_FRAME: u32 = 0x6474e550; +/// Indicates stack executability +pub const PT_GNU_STACK: u32 = 0x6474e551; +/// Read-only after relocation +pub const PT_GNU_RELRO: u32 = 0x6474e552; +/// The segment contains .note.gnu.property section +pub const PT_GNU_PROPERTY: u32 = 0x6474e553; +/// Values between [PT_LOOS, PT_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const PT_LOOS: u32 = 0x60000000; +/// Values between [PT_LOOS, PT_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const PT_HIOS: u32 = 0x6fffffff; +/// Values between [PT_LOPROC, PT_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const PT_LOPROC: u32 = 0x70000000; +/// Values between [PT_LOPROC, PT_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const PT_HIPROC: u32 = 0x7fffffff; + +// SHT_* define constants for the ELF Section Header's p_type field. +// Represented as Elf32_Word in Elf32_Ehdr and Elf64_Word in Elf64_Ehdr which +// are both 4-byte unsigned integers with 4-byte alignment + +/// Inactive section with undefined values +pub const SHT_NULL: u32 = 0; +/// Information defined by the program, includes executable code and data +pub const SHT_PROGBITS: u32 = 1; +/// Section data contains a symbol table +pub const SHT_SYMTAB: u32 = 2; +/// Section data contains a string table +pub const SHT_STRTAB: u32 = 3; +/// Section data contains relocation entries with explicit addends +pub const SHT_RELA: u32 = 4; +/// Section data contains a symbol hash table. Must be present for dynamic linking +pub const SHT_HASH: u32 = 5; +/// Section data contains information for dynamic linking +pub const SHT_DYNAMIC: u32 = 6; +/// Section data contains information that marks the file in some way +pub const SHT_NOTE: u32 = 7; +/// Section data occupies no space in the file but otherwise resembles SHT_PROGBITS +pub const SHT_NOBITS: u32 = 8; +/// Section data contains relocation entries without explicit addends +pub const SHT_REL: u32 = 9; +/// Section is reserved but has unspecified semantics +pub const SHT_SHLIB: u32 = 10; +/// Section data contains a minimal set of dynamic linking symbols +pub const SHT_DYNSYM: u32 = 11; +/// Section data contains an array of constructors +pub const SHT_INIT_ARRAY: u32 = 14; +/// Section data contains an array of destructors +pub const SHT_FINI_ARRAY: u32 = 15; +/// Section data contains an array of pre-constructors +pub const SHT_PREINIT_ARRAY: u32 = 16; +/// Section group +pub const SHT_GROUP: u32 = 17; +/// Extended symbol table section index +pub const SHT_SYMTAB_SHNDX: u32 = 18; +/// Values in [SHT_LOOS, SHT_HIOS] are reserved for operating system-specific semantics. +pub const SHT_LOOS: u32 = 0x60000000; +/// Object attributes +pub const SHT_GNU_ATTRIBUTES: u32 = 0x6ffffff5; +/// GNU-style hash section +pub const SHT_GNU_HASH: u32 = 0x6ffffff6; +/// Pre-link library list +pub const SHT_GNU_LIBLIST: u32 = 0x6ffffff7; +/// Version definition section +pub const SHT_GNU_VERDEF: u32 = 0x6ffffffd; +/// Version needs section +pub const SHT_GNU_VERNEED: u32 = 0x6ffffffe; +/// Version symbol table +pub const SHT_GNU_VERSYM: u32 = 0x6fffffff; +/// Values in [SHT_LOOS, SHT_HIOS] are reserved for operating system-specific semantics. +pub const SHT_HIOS: u32 = 0x6fffffff; +/// Values in [SHT_LOPROC, SHT_HIPROC] are reserved for processor-specific semantics. +pub const SHT_LOPROC: u32 = 0x70000000; +/// IA_64 extension bits +pub const SHT_IA_64_EXT: u32 = 0x70000000; // SHT_LOPROC + 0; +/// IA_64 unwind section +pub const SHT_IA_64_UNWIND: u32 = 0x70000001; // SHT_LOPROC + 1; +/// Values in [SHT_LOPROC, SHT_HIPROC] are reserved for processor-specific semantics. +pub const SHT_HIPROC: u32 = 0x7fffffff; +/// Values in [SHT_LOUSER, SHT_HIUSER] are reserved for application-specific semantics. +pub const SHT_LOUSER: u32 = 0x80000000; +/// Values in [SHT_LOUSER, SHT_HIUSER] are reserved for application-specific semantics. +pub const SHT_HIUSER: u32 = 0x8fffffff; + +/// This value marks an undefined, missing, irrelevant, or otherwise meaningless +/// section reference. +pub const SHN_UNDEF: u16 = 0; +/// Symbols with st_shndx=SHN_ABS are absolute and are not affected by relocation. +pub const SHN_ABS: u16 = 0xfff1; +/// Symbols with st_shndx=SHN_COMMON are sometimes used for unallocated C external variables. +pub const SHN_COMMON: u16 = 0xfff2; +pub const SHN_XINDEX: u16 = 0xffff; + +// SHF_* define constants for the ELF Section Header's sh_flags field. +// Represented as Elf32_Word in Elf32_Ehdr and Elf64_Xword in Elf64_Ehdr which +// are both 4-byte and 8-byte unsigned integers, respectively. +// All of the constants are < 32-bits, so we use a u32 to represent these in order +// to make working with them easier. + +/// Empty flags +pub const SHF_NONE: u32 = 0; +/// The section contains data that should be writable during process execution. +pub const SHF_WRITE: u32 = 1; +/// The section occupies memory during process execution. Some control sections +/// do not reside in the memory image of an object file; this attribute is off for +/// those sections. +pub const SHF_ALLOC: u32 = 1 << 1; +/// The section contains executable machine instructions. +pub const SHF_EXECINSTR: u32 = 1 << 2; +/// The data in the section may be merged to eliminate duplication. Unless the +/// SHF_STRINGS flag is also set, the data elements in the section are of a uniform size. +/// The size of each element is specified in the section header's sh_entsize field. If +/// the SHF_STRINGS flag is also set, the data elements consist of null-terminated +/// character strings. The size of each character is specified in the section header's +/// sh_entsize field. +/// +/// Each element in the section is compared against other elements in sections with the +/// same name, type and flags. Elements that would have identical values at program +/// run-time may be merged. Relocations referencing elements of such sections must be +/// resolved to the merged locations of the referenced values. Note that any relocatable +/// values, including values that would result in run-time relocations, must be analyzed +/// to determine whether the run-time values would actually be identical. An +/// ABI-conforming object file may not depend on specific elements being merged, and an +/// ABI-conforming link editor may choose not to merge specific elements. +pub const SHF_MERGE: u32 = 1 << 4; +/// The data elements in the section consist of null-terminated character strings. +/// The size of each character is specified in the section header's sh_entsize field. +pub const SHF_STRINGS: u32 = 1 << 5; +/// The sh_info field of this section header holds a section header table index. +pub const SHF_INFO_LINK: u32 = 1 << 6; +/// This flag adds special ordering requirements for link editors. The requirements +/// apply if the sh_link field of this section's header references another section (the +/// linked-to section). If this section is combined with other sections in the output +/// file, it must appear in the same relative order with respect to those sections, +/// as the linked-to section appears with respect to sections the linked-to section is +/// combined with. +pub const SHF_LINK_ORDER: u32 = 1 << 7; +/// This section requires special OS-specific processing (beyond the standard linking +/// rules) to avoid incorrect behavior. If this section has either an sh_type value or +/// contains sh_flags bits in the OS-specific ranges for those fields, and a link +/// editor processing this section does not recognize those values, then the link editor +/// should reject the object file containing this section with an error. +pub const SHF_OS_NONCONFORMING: u32 = 1 << 8; +/// This section is a member (perhaps the only one) of a section group. The section must +/// be referenced by a section of type SHT_GROUP. The SHF_GROUP flag may be set only for +/// sections contained in relocatable objects (objects with the ELF header e_type member +/// set to ET_REL). +pub const SHF_GROUP: u32 = 1 << 9; +/// This section holds Thread-Local Storage, meaning that each separate execution flow +/// has its own distinct instance of this data. Implementations need not support this flag. +pub const SHF_TLS: u32 = 1 << 10; +/// This flag identifies a section containing compressed data. SHF_COMPRESSED applies only +/// to non-allocable sections, and cannot be used in conjunction with SHF_ALLOC. In +/// addition, SHF_COMPRESSED cannot be applied to sections of type SHT_NOBITS. +/// +/// All relocations to a compressed section specifiy offsets to the uncompressed section +/// data. It is therefore necessary to decompress the section data before relocations can +/// be applied. Each compressed section specifies the algorithm independently. It is +/// permissible for different sections in a given ELF object to employ different +/// compression algorithms. +/// +/// Compressed sections begin with a compression header structure that identifies the +/// compression algorithm. +pub const SHF_COMPRESSED: u32 = 1 << 11; +/// Masked bits are reserved for operating system-specific semantics. +pub const SHF_MASKOS: u32 = 0x0ff00000; +/// Masked bits are reserved for processor-specific semantics. +pub const SHF_MASKPROC: u32 = 0xf0000000; + +// STT_* define constants for the ELF Symbol's st_type (encoded in the st_info field). + +/// Unspecified symbol type +pub const STT_NOTYPE: u8 = 0; +/// Data object symbol +pub const STT_OBJECT: u8 = 1; +/// Code object symbol +pub const STT_FUNC: u8 = 2; +/// Section symbol +pub const STT_SECTION: u8 = 3; +/// File name symbol +pub const STT_FILE: u8 = 4; +/// Common data object symbol +pub const STT_COMMON: u8 = 5; +/// Thread-local data object symbol +pub const STT_TLS: u8 = 6; +/// Indirect code object symbol +pub const STT_GNU_IFUNC: u8 = 10; +/// Values between [STT_LOOS, STT_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const STT_LOOS: u8 = 10; +/// Values between [STT_LOOS, STT_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const STT_HIOS: u8 = 12; +/// Values between [STT_LOPROC, STT_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const STT_LOPROC: u8 = 13; +/// Values between [STT_LOPROC, STT_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const STT_HIPROC: u8 = 15; + +// STB_* define constants for the ELF Symbol's st_bind (encoded in the st_info field). + +/// Local symbols are not visible outside the object file containing their +/// definition. Local symbols of the same name may exist in multiple files +/// without interfering with each other. +pub const STB_LOCAL: u8 = 0; +/// Global symbols are visible to all object files being combined. One file's +/// definition of a global symbol will satisfy another file's undefined +/// reference to the same global symbol. +pub const STB_GLOBAL: u8 = 1; +/// Weak symbols resemble global symbols, but their definitions have lower +/// precedence. +pub const STB_WEAK: u8 = 2; +/// Unique symbol +pub const STB_GNU_UNIQUE: u8 = 10; +/// Values between [STB_LOOS, STB_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const STB_LOOS: u8 = 10; +/// Values between [STB_LOOS, STB_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const STB_HIOS: u8 = 12; +/// Values between [STB_LOPROC, STB_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const STB_LOPROC: u8 = 13; +/// Values between [STB_LOPROC, STB_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const STB_HIPROC: u8 = 15; + +/// STV_* define constants for the ELF Symbol's st_visibility (encoded in the st_other field). + +/// The visibility of symbols with the STV_DEFAULT attribute is as specified by +/// the symbol's binding type. That is, global and weak symbols are visible +/// outside of their defining component (executable file or shared object). +/// Local symbols are hidden, as described below. Global and weak symbols are +/// also preemptable, that is, they may by preempted by definitions of the same +/// name in another component. +pub const STV_DEFAULT: u8 = 0; +/// The meaning of this visibility attribute may be defined by processor +/// supplements to further constrain hidden symbols. A processor supplement's +/// definition should be such that generic tools can safely treat internal +/// symbols as hidden. +pub const STV_INTERNAL: u8 = 1; +/// A symbol defined in the current component is hidden if its name is not +/// visible to other components. Such a symbol is necessarily protected. This +/// attribute may be used to control the external interface of a component. Note +/// that an object named by such a symbol may still be referenced from another +/// component if its address is passed outside. +pub const STV_HIDDEN: u8 = 2; +/// A symbol defined in the current component is protected if it is visible in +/// other components but not preemptable, meaning that any reference to such a +/// symbol from within the defining component must be resolved to the definition +/// in that component, even if there is a definition in another component that +/// would preempt by the default rules. +pub const STV_PROTECTED: u8 = 3; + +/// An entry with a DT_NULL tag marks the end of the _DYNAMIC array. +pub const DT_NULL: i64 = 0; +/// This element holds the string table offset of a null-terminated string, +/// giving the name of a needed library. The offset is an index into the table +/// recorded in the DT_STRTAB code. The dynamic array may contain multiple +/// entries with this type. These entries' relative order is significant, though +/// their relation to entries of other types is not. +pub const DT_NEEDED: i64 = 1; +/// This element holds the total size, in bytes, of the relocation entries +/// associated with the procedure linkage table. If an entry of type DT_JMPREL +/// is present, a DT_PLTRELSZ must accompany it. +pub const DT_PLTRELSZ: i64 = 2; +/// This element holds an address associated with the procedure linkage table +/// and/or the global offset table. +pub const DT_PLTGOT: i64 = 3; +/// This element holds the address of the symbol hash table. This hash table +/// refers to the symbol table referenced by the DT_SYMTAB element. +pub const DT_HASH: i64 = 4; +/// This element holds the address of the string table. Symbol names, library +/// names, and other strings reside in this table. +pub const DT_STRTAB: i64 = 5; +/// This element holds the address of the symbol table. +pub const DT_SYMTAB: i64 = 6; +/// This element holds the address of a relocation table. Entries in the table +/// have explicit addends, (Rela). An object file may have multiple relocation +/// sections. When building the relocation table for an executable or shared +/// object file, the link editor catenates those sections to form a single +/// table. Although the sections remain independent in the object file, the +/// dynamic linker sees a single table. When the dynamic linker creates the +/// process image for an executable file or adds a shared object to the process +/// image, it reads the relocation table and performs the associated actions. +/// If this element is present, the dynamic structure must also have DT_RELASZ +/// and DT_RELAENT elements. When relocation is mandatory for a file, either +/// DT_RELA or DT_REL may occur (both are permitted but not required). +pub const DT_RELA: i64 = 7; +/// This element holds the total size, in bytes, of the DT_RELA relocation table. +pub const DT_RELASZ: i64 = 8; +/// This element holds the size, in bytes, of the DT_RELA relocation entry. +pub const DT_RELAENT: i64 = 9; +/// This element holds the size, in bytes, of the string table. +pub const DT_STRSZ: i64 = 10; +/// This element holds the size, in bytes, of a symbol table entry. +pub const DT_SYMENT: i64 = 11; +/// This element holds the address of the initialization function. +pub const DT_INIT: i64 = 12; +/// This element holds the address of the termination function. +pub const DT_FINI: i64 = 13; +/// This element holds the string table offset of a null-terminated string, +/// giving the name of the shared object. The offset is an index into the table +/// recorded in the DT_STRTAB entry. +pub const DT_SONAME: i64 = 14; +/// This element holds the string table offset of a null-terminated search +/// library search path string. The offset is an index into the table recorded +/// in the DT_STRTAB entry. Its use has been superseded by DT_RUNPATH. +pub const DT_RPATH: i64 = 15; +/// This element's presence in a shared object library alters the dynamic +/// linker's symbol resolution algorithm for references within the library. +/// Instead of starting a symbol search with the executable file, the dynamic +/// linker starts from the shared object itself. If the shared object fails to +/// supply the referenced symbol, the dynamic linker then searches the +/// executable file and other shared objects as usual. Its use has been +/// superseded by the DF_SYMBOLIC flag. +pub const DT_SYMBOLIC: i64 = 16; +/// This element is similar to DT_RELA, except its table has implicit addends (Rel). +/// If this element is present, the dynamic structure must also have DT_RELSZ +/// and DT_RELENT elements. +pub const DT_REL: i64 = 17; +/// This element holds the total size, in bytes, of the DT_REL relocation table. +pub const DT_RELSZ: i64 = 18; +/// This element holds the size, in bytes, of the DT_REL relocation entry. +pub const DT_RELENT: i64 = 19; +/// This member specifies the type of relocation entry to which the procedure +/// linkage table refers. The d_val member holds DT_REL or DT_RELA, as +/// appropriate. All relocations in a procedure linkage table must use the same +/// relocation. +pub const DT_PLTREL: i64 = 20; +/// This member is used for debugging. Its contents are not specified for the +/// ABI; programs that access this entry are not ABI-conforming. +pub const DT_DEBUG: i64 = 21; +/// This member's absence signifies that no relocation entry should cause a +/// modification to a non-writable segment, as specified by the segment +/// permissions in the program header table. If this member is present, one or +/// more relocation entries might request modifications to a non-writable +/// segment, and the dynamic linker can prepare accordingly. Its use has been +/// superseded by the DF_TEXTREL flag. +pub const DT_TEXTREL: i64 = 22; +/// If present, this entry's d_ptr member holds the address of relocation +/// entries associated solely with the procedure linkage table. Separating these +/// relocation entries lets the dynamic linker ignore them during process +/// initialization, if lazy binding is enabled. If this entry is present, the +/// related entries of types DT_PLTRELSZ and DT_PLTREL must also be present. +pub const DT_JMPREL: i64 = 23; +/// If present in a shared object or executable, this entry instructs the +/// dynamic linker to process all relocations for the object containing this +/// entry before transferring control to the program. The presence of this entry +/// takes precedence over a directive to use lazy binding for this object when +/// specified through the environment or via dlopen(BA_LIB). Its use has been +/// superseded by the DF_BIND_NOW flag. +pub const DT_BIND_NOW: i64 = 24; +/// This element holds the address of the array of pointers to initialization functions. +pub const DT_INIT_ARRAY: i64 = 25; +/// This element holds the address of the array of pointers to termination functions. +pub const DT_FINI_ARRAY: i64 = 26; +/// This element holds the size in bytes of the array of initialization +/// functions pointed to by the DT_INIT_ARRAY entry. If an object has a +/// DT_INIT_ARRAY entry, it must also have a DT_INIT_ARRAYSZ entry. +pub const DT_INIT_ARRAYSZ: i64 = 27; +/// This element holds the size in bytes of the array of termination functions +/// pointed to by the DT_FINI_ARRAY entry. If an object has a DT_FINI_ARRAY +/// entry, it must also have a DT_FINI_ARRAYSZ entry. +pub const DT_FINI_ARRAYSZ: i64 = 28; +/// This element holds the string table offset of a null-terminated library +/// search path string. The offset is an index into the table recorded in the +/// DT_STRTAB entry. +pub const DT_RUNPATH: i64 = 29; +/// This element holds flag values specific to the object being loaded. Each +/// flag value will have the name DF_flag_name. Defined values and their +/// meanings are described below. All other values are reserved. +pub const DT_FLAGS: i64 = 30; +/// This element holds the address of the array of pointers to +/// pre-initialization functions. The DT_PREINIT_ARRAY table is processed only +/// in an executable file; it is ignored if contained in a shared object. +pub const DT_PREINIT_ARRAY: i64 = 32; +/// This element holds the size in bytes of the array of pre-initialization +/// functions pointed to by the DT_PREINIT_ARRAY entry. If an object has a +/// DT_PREINIT_ARRAY entry, it must also have a DT_PREINIT_ARRAYSZ entry. As +/// with DT_PREINIT_ARRAY, this entry is ignored if it appears in a shared +/// object. +pub const DT_PREINIT_ARRAYSZ: i64 = 33; +/// This element holds the address of the SHT_SYMTAB_SHNDX section associated +/// with the dynamic symbol table referenced by the DT_SYMTAB element. +pub const DT_SYMTAB_SHNDX: i64 = 34; +/// Guile offset of GC roots +pub const DT_GUILE_GC_ROOT: i64 = 0x37146000; +/// Guile size in machine words of GC roots +pub const DT_GUILE_GC_ROOT_SZ: i64 = 0x37146001; +/// Guile address of entry thunk +pub const DT_GUILE_ENTRY: i64 = 0x37146002; +/// Guile bytecode version +pub const DT_GUILE_VM_VERSION: i64 = 0x37146003; +/// Guile frame maps +pub const DT_GUILE_FRAME_MAPS: i64 = 0x37146004; +/// Values in [DT_LOOS, DT_HIOS] are reserved for operating system-specific semantics. +pub const DT_LOOS: i64 = 0x6000000D; +/// Prelinking timestamp +pub const DT_GNU_PRELINKED: i64 = 0x6ffffdf5; +/// Size of conflict section +pub const DT_GNU_CONFLICTSZ: i64 = 0x6ffffdf6; +/// Size of library list +pub const DT_GNU_LIBLISTSZ: i64 = 0x6ffffdf7; +pub const DT_CHECKSUM: i64 = 0x6ffffdf8; +pub const DT_PLTPADSZ: i64 = 0x6ffffdf9; +pub const DT_MOVEENT: i64 = 0x6ffffdfa; +pub const DT_MOVESZ: i64 = 0x6ffffdfb; +/// Feature selection (DTF_*) +pub const DT_FEATURE_1: i64 = 0x6ffffdfc; +/// Flags for DT_* entries, effecting the following DT_* entry +pub const DT_POSFLAG_1: i64 = 0x6ffffdfd; +/// Size of syminfo table (in bytes) +pub const DT_SYMINSZ: i64 = 0x6ffffdfe; +/// Entry size of syminfo table +pub const DT_SYMINENT: i64 = 0x6ffffdff; +/// GNU-style hash table +pub const DT_GNU_HASH: i64 = 0x6ffffef5; +pub const DT_TLSDESC_PLT: i64 = 0x6ffffef6; +pub const DT_TLSDESC_GOT: i64 = 0x6ffffef7; +/// Start of conflict section +pub const DT_GNU_CONFLICT: i64 = 0x6ffffef8; +/// Library list +pub const DT_GNU_LIBLIST: i64 = 0x6ffffef9; +/// Configuration information +pub const DT_CONFIG: i64 = 0x6ffffefa; +/// Dependency auditing +pub const DT_DEPAUDIT: i64 = 0x6ffffefb; +/// Object auditing +pub const DT_AUDIT: i64 = 0x6ffffefc; +/// PLT padding +pub const DT_PLTPAD: i64 = 0x6ffffefd; +/// Move table +pub const DT_MOVETAB: i64 = 0x6ffffefe; +/// Syminfo table +pub const DT_SYMINFO: i64 = 0x6ffffeff; +pub const DT_VERSYM: i64 = 0x6ffffff0; +pub const DT_RELACOUNT: i64 = 0x6ffffff9; +pub const DT_RELCOUNT: i64 = 0x6ffffffa; +/// State flags, see DF_1_* below. +pub const DT_FLAGS_1: i64 = 0x6ffffffb; +/// Address of version definition table +pub const DT_VERDEF: i64 = 0x6ffffffc; +/// Number of version definitions +pub const DT_VERDEFNUM: i64 = 0x6ffffffd; +/// Address of table with needed versions +pub const DT_VERNEED: i64 = 0x6ffffffe; +/// Number of needed versions +pub const DT_VERNEEDNUM: i64 = 0x6fffffff; +/// Values in [DT_LOOS, DT_HIOS] are reserved for operating system-specific semantics. +pub const DT_HIOS: i64 = 0x6ffff000; +/// Values in [DT_LOPROC, DT_HIPROC] are reserved for processor-specific semantics. +pub const DT_LOPROC: i64 = 0x70000000; +/// Values in [DT_LOPROC, DT_HIPROC] are reserved for processor-specific semantics. +pub const DT_HIPROC: i64 = 0x7fffffff; + +/// This flag signifies that the object being loaded may make reference to the +/// $ORIGIN substitution string. The dynamic linker must determine the pathname +/// of the object containing this entry when the object is loaded. +pub const DF_ORIGIN: i64 = 0x1; +/// If this flag is set in a shared object library, the dynamic linker's symbol +/// resolution algorithm for references within the library is changed. Instead +/// of starting a symbol search with the executable file, the dynamic linker +/// starts from the shared object itself. If the shared object fails to supply +/// the referenced symbol, the dynamic linker then searches the executable file +/// and other shared objects as usual. +pub const DF_SYMBOLIC: i64 = 0x2; +/// If this flag is not set, no relocation entry should cause a modification to +/// a non-writable segment, as specified by the segment permissions in the +/// program header table. If this flag is set, one or more relocation entries +/// might request modifications to a non-writable segment, and the dynamic +/// linker can prepare accordingly. +pub const DF_TEXTREL: i64 = 0x4; +/// If set in a shared object or executable, this flag instructs the dynamic +/// linker to process all relocations for the object containing this entry +/// before transferring control to the program. The presence of this entry takes +/// precedence over a directive to use lazy binding for this object when +/// specified through the environment or via dlopen(BA_LIB). +pub const DF_BIND_NOW: i64 = 0x8; +/// If set in a shared object or executable, this flag instructs the dynamic +/// linker to reject attempts to load this file dynamically. It indicates that +/// the shared object or executable contains code using a static thread-local +/// storage scheme. Implementations need not support any form of thread-local +/// storage. +pub const DF_STATIC_TLS: i64 = 0x10; + +// State flags selectable in Dyn.d_val() of the DT_FLAGS_1 entries in the dynamic section + +/// Set RTLD_NOW for this object +pub const DF_1_NOW: i64 = 0x00000001; +/// Set RTLD_GLOBAL for this object +pub const DF_1_GLOBAL: i64 = 0x00000002; +/// Set RTLD_GROUP for this object +pub const DF_1_GROUP: i64 = 0x00000004; +/// Set RTLD_NODELETE for this object +pub const DF_1_NODELETE: i64 = 0x00000008; +/// Trigger filtee loading at runtime +pub const DF_1_LOADFLTR: i64 = 0x00000010; +/// Set RTLD_INITFIRST for this object +pub const DF_1_INITFIRST: i64 = 0x00000020; +/// Set RTLD_NOOPEN for this object +pub const DF_1_NOOPEN: i64 = 0x00000040; +/// $ORIGIN must be handled +pub const DF_1_ORIGIN: i64 = 0x00000080; +/// Direct binding enabled +pub const DF_1_DIRECT: i64 = 0x00000100; +pub const DF_1_TRANS: i64 = 0x00000200; +/// Object is used to interpose +pub const DF_1_INTERPOSE: i64 = 0x00000400; +/// Ignore default lib search path +pub const DF_1_NODEFLIB: i64 = 0x00000800; +/// Object can't be dldump'ed +pub const DF_1_NODUMP: i64 = 0x00001000; +/// Configuration alternative created +pub const DF_1_CONFALT: i64 = 0x00002000; +/// Filtee terminates filters search +pub const DF_1_ENDFILTEE: i64 = 0x00004000; +/// Disp reloc applied at build time +pub const DF_1_DISPRELDNE: i64 = 0x00008000; +/// Disp reloc applied at run-time +pub const DF_1_DISPRELPND: i64 = 0x00010000; +/// Object has no-direct binding +pub const DF_1_NODIRECT: i64 = 0x00020000; +pub const DF_1_IGNMULDEF: i64 = 0x00040000; +pub const DF_1_NOKSYMS: i64 = 0x00080000; +pub const DF_1_NOHDR: i64 = 0x00100000; +/// Object is modified after built +pub const DF_1_EDITED: i64 = 0x00200000; +pub const DF_1_NORELOC: i64 = 0x00400000; +/// Object has individual interposers +pub const DF_1_SYMINTPOSE: i64 = 0x00800000; +/// Global auditing required +pub const DF_1_GLOBAUDIT: i64 = 0x01000000; +/// Singleton symbols are used +pub const DF_1_SINGLETON: i64 = 0x02000000; +pub const DF_1_STUB: i64 = 0x04000000; +pub const DF_1_PIE: i64 = 0x08000000; +pub const DF_1_KMOD: i64 = 0x10000000; +pub const DF_1_WEAKFILTER: i64 = 0x20000000; +pub const DF_1_NOCOMMON: i64 = 0x40000000; + +// Flags for the feature selection in DT_FEATURE_1 +pub const DTF_1_PARINIT: i64 = 0x00000001; +pub const DTF_1_CONFEXP: i64 = 0x00000002; + +// Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry +/// Lazyload following object +pub const DF_P1_LAZYLOAD: i64 = 0x00000001; +/// Symbols from next object are not generally available +pub const DF_P1_GROUPPERM: i64 = 0x00000002; + +// .gnu.version index reserved values +/// Symbol is local +pub const VER_NDX_LOCAL: u16 = 0; +/// Symbol is global +pub const VER_NDX_GLOBAL: u16 = 1; +/// .gnu.version index mask +pub const VER_NDX_VERSION: u16 = 0x7fff; +/// Symbol is hidden +pub const VER_NDX_HIDDEN: u16 = 0x8000; + +// .gnu.version_d VerDef.vd_version reserved values +/// Only defined valid vd_version value +pub const VER_DEF_CURRENT: u16 = 1; + +// .gnu.version_r VerNeed.vn_version reserved values +/// Only defined valid vn_version value +pub const VER_NEED_CURRENT: u16 = 1; + +// Bit flags which appear in vd_flags of VerDef and vna_flags of VerNeedAux. +pub const VER_FLG_BASE: u16 = 0x1; +pub const VER_FLG_WEAK: u16 = 0x2; +pub const VER_FLG_INFO: u16 = 0x4; + +/// ZLIB/DEFLATE +pub const ELFCOMPRESS_ZLIB: u32 = 1; +/// zstd algorithm +pub const ELFCOMPRESS_ZSTD: u32 = 2; +/// Values in [ELFCOMPRESS_LOOS, ELFCOMPRESS_HIOS] are reserved for operating system-specific semantics. +pub const ELFCOMPRESS_LOOS: u32 = 0x60000000; +/// Values in [ELFCOMPRESS_LOOS, ELFCOMPRESS_HIOS] are reserved for operating system-specific semantics. +pub const ELFCOMPRESS_HIOS: u32 = 0x6fffffff; +/// Values in [ELFCOMPRESS_LOPROC, ELFCOMPRESS_HIPROC] are reserved for processor-specific semantics. +pub const ELFCOMPRESS_LOPROC: u32 = 0x70000000; +/// Values in [ELFCOMPRESS_LOPROC, ELFCOMPRESS_HIPROC] are reserved for processor-specific semantics. +pub const ELFCOMPRESS_HIPROC: u32 = 0x7fffffff; + +/// GNU-extension notes have this name +pub const ELF_NOTE_GNU: &str = "GNU"; + +// Note header descriptor types constants (n_type) + +/// Contains copy of prstatus struct +pub const NT_PRSTATUS: u64 = 1; +/// Contains copy of fpregset struct +pub const NT_PRFPREG: u64 = 2; +/// Contains copy of fpregset struct +pub const NT_FPREGSET: u64 = 2; +/// Contains copy of prpsinfo struct +pub const NT_PRPSINFO: u64 = 3; +/// Contains copy of prxregset struct +pub const NT_PRXREG: u64 = 4; +/// Contains copy of task structure +pub const NT_TASKSTRUCT: u64 = 4; +/// String from sysinfo(SI_PLATFORM) +pub const NT_PLATFORM: u64 = 5; +/// Contains copy of auxv array +pub const NT_AUXV: u64 = 6; +/// Contains copy of gwindows struct +pub const NT_GWINDOWS: u64 = 7; +/// Contains copy of asrset struct +pub const NT_ASRS: u64 = 8; +/// Contains copy of pstatus struct +pub const NT_PSTATUS: u64 = 10; +/// Contains copy of psinfo struct +pub const NT_PSINFO: u64 = 13; +/// Contains copy of prcred struct +pub const NT_PRCRED: u64 = 14; +/// Contains copy of utsname struct +pub const NT_UTSNAME: u64 = 15; +/// Contains copy of lwpstatus struct +pub const NT_LWPSTATUS: u64 = 16; +/// Contains copy of lwpinfo struct +pub const NT_LWPSINFO: u64 = 17; +/// Contains copy of fprxregset struct +pub const NT_PRFPXREG: u64 = 20; +/// Contains copy of siginfo_t, size might increase +pub const NT_SIGINFO: u64 = 0x53494749; +/// Contains information about mapped files +pub const NT_FILE: u64 = 0x46494c45; +/// Contains copy of user_fxsr_struct +pub const NT_PRXFPREG: u64 = 0x46e62b7f; +/// /// PowerPC Altivec/VMX registers +pub const NT_PPC_VMX: u64 = 0x100; +/// PowerPC SPE/EVR registers +pub const NT_PPC_SPE: u64 = 0x101; +/// PowerPC VSX registers +pub const NT_PPC_VSX: u64 = 0x102; +/// Target Address Register +pub const NT_PPC_TAR: u64 = 0x103; +/// Program Priority Register +pub const NT_PPC_PPR: u64 = 0x104; +/// Data Stream Control Register +pub const NT_PPC_DSCR: u64 = 0x105; +/// Event Based Branch Registers +pub const NT_PPC_EBB: u64 = 0x106; +/// Performance Monitor Registers +pub const NT_PPC_PMU: u64 = 0x107; +/// TM checkpointed GPR Registers +pub const NT_PPC_TM_CGPR: u64 = 0x108; +/// TM checkpointed FPR Registers +pub const NT_PPC_TM_CFPR: u64 = 0x109; +/// TM checkpointed VMX Registers +pub const NT_PPC_TM_CVMX: u64 = 0x10a; +/// TM checkpointed VSX Registers +pub const NT_PPC_TM_CVSX: u64 = 0x10b; +/// TM Special Purpose Registers +pub const NT_PPC_TM_SPR: u64 = 0x10c; +/// TM checkpointed Target Address Register +pub const NT_PPC_TM_CTAR: u64 = 0x10d; +/// TM checkpointed Program Priority Register +pub const NT_PPC_TM_CPPR: u64 = 0x10e; +/// TM checkpointed Data Stream Control Register +pub const NT_PPC_TM_CDSCR: u64 = 0x10f; +/// Memory Protection Keys registers +pub const NT_PPC_PKEY: u64 = 0x110; +/// i386 TLS slots (struct user_desc) +pub const NT_386_TLS: u64 = 0x200; +/// x86 io permission bitmap (1=deny) +pub const NT_386_IOPERM: u64 = 0x201; +/// x86 extended state using xsave +pub const NT_X86_XSTATE: u64 = 0x202; +/// ARM VFP/NEON registers +pub const NT_ARM_VFP: u64 = 0x400; +/// ARM TLS register +pub const NT_ARM_TLS: u64 = 0x401; +/// ARM hardware breakpoint registers +pub const NT_ARM_HW_BREAK: u64 = 0x402; +/// ARM hardware watchpoint registers +pub const NT_ARM_HW_WATCH: u64 = 0x403; +/// ARM system call number +pub const NT_ARM_SYSTEM_CALL: u64 = 0x404; +/// ARM Scalable Vector Extension registers +pub const NT_ARM_SVE: u64 = 0x405; +/// ARM pointer authentication code masks +pub const NT_ARM_PAC_MASK: u64 = 0x406; +/// ARM pointer authentication address keys +pub const NT_ARM_PACA_KEYS: u64 = 0x407; +/// ARM pointer authentication generic key +pub const NT_ARM_PACG_KEYS: u64 = 0x408; +/// AArch64 tagged address control. +pub const NT_ARM_TAGGED_ADDR_CTRL: u64 = 0x409; +/// AArch64 pointer authentication enabled keys +pub const NT_ARM_PAC_ENABLED_KEYS: u64 = 0x40a; +/// Vmcore Device Dump Note +pub const NT_VMCOREDD: u64 = 0x700; + +/// 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: u64 = 1; +/// Synthetic hwcap information +pub const NT_GNU_HWCAP: u64 = 2; +/// Build ID bits as generated by ld --build-id. +pub const NT_GNU_BUILD_ID: u64 = 3; +/// Version note generated by GNU gold containing a version string +pub const NT_GNU_GOLD_VERSION: u64 = 4; +/// Program property note which describes special handling requirements for linker and run-time loader. +pub const NT_GNU_PROPERTY_TYPE_0: u64 = 5; + +// These values can appear in word 0 of an NT_GNU_ABI_TAG note section entry. +pub const ELF_NOTE_GNU_ABI_TAG_OS_LINUX: u32 = 0; +pub const ELF_NOTE_GNU_ABI_TAG_OS_GNU: u32 = 1; +pub const ELF_NOTE_GNU_ABI_TAG_OS_SOLARIS2: u32 = 2; +pub const ELF_NOTE_GNU_ABI_TAG_OS_FREEBSD: u32 = 3; + +// _ ____ __ __ +// / \ | _ \| \/ | +// / _ \ | |_) | |\/| | +// / ___ \| _ <| | | | +// /_/ \_\_| \_\_| |_| +// +// ARM-specific declarations (ABI v5) +// See: https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst +// See: https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst + +/// Set in executable file headers (e_type = ET_EXEC or ET_DYN) to note explicitly that the +/// executable file was built to conform to the software floating-point procedure-call standard +/// (the base standard). If both EF_ARM_ABI_FLOAT_XXXX bits are clear, conformance to the base +/// procedure-call standard is implied. +pub const EF_ARM_ABI_FLOAT_SOFT: u32 = 0x200; +/// Compatible with legacy (pre version 5) gcc use as EF_ARM_SOFT_FLOAT +pub const EF_ARM_SOFT_FLOAT: u32 = EF_ARM_ABI_FLOAT_SOFT; +/// Set in executable file headers (e_type = ET_EXEC or ET_DYN) to note that the executable file +/// was built to conform to the hardware floating-point procedure-call standard. +pub const EF_ARM_ABI_FLOAT_HARD: u32 = 0x400; +/// Compatible with legacy (pre version 5) gcc use as EF_ARM_VFP_FLOAT. +pub const EF_ARM_VFP_FLOAT: u32 = EF_ARM_ABI_FLOAT_HARD; + +/// The ELF file contains BE-8 code, suitable for execution on an Arm Architecture v6 processor. +/// This flag must only be set on an executable file. +pub const EF_ARM_BE8: u32 = 0x00800000; + +/// Legacy code (ABI version 4 and earlier) generated by gcc-arm-xxx might use these bits. +pub const EF_ARM_GCCMASK: u32 = 0x00400FFF; + +/// This masks an 8-bit version number, the version of the ABI to which this ELF +/// file conforms. This ABI is version 5. A value of 0 denotes unknown conformance. +pub const EF_ARM_EABIMASK: u32 = 0xFF000000; +pub const EF_ARM_EABI_UNKNOWN: u32 = 0x00000000; +pub const EF_ARM_EABI_VER1: u32 = 0x01000000; +pub const EF_ARM_EABI_VER2: u32 = 0x02000000; +pub const EF_ARM_EABI_VER3: u32 = 0x03000000; +pub const EF_ARM_EABI_VER4: u32 = 0x04000000; +pub const EF_ARM_EABI_VER5: u32 = 0x05000000; + +/// Section contains index information for exception unwinding +pub const SHT_ARM_EXIDX: u32 = 0x70000001; +/// BPABI DLL dynamic linking pre-emption map +pub const SHT_ARM_PREEMPTMAP: u32 = 0x70000002; +/// Object file compatibility attributes +pub const SHT_ARM_ATTRIBUTES: u32 = 0x70000003; +/// See +pub const SHT_ARM_DEBUGOVERLAY: u32 = 0x70000004; +/// See +pub const SHT_ARM_OVERLAYSECTION: u32 = 0x70000005; + +/// The contents of this section contains only program instructions and no program data. +/// +/// If any section contained by a segment does not have the SHF_ARM_PURECODE +/// section flag set, the PF_R segment flag must be set in the program header +/// for the segment. If all sections contained by a segment have the +/// SHF_ARM_PURECODE section flag, a linker may optionally clear the PF_R +/// segment flag in the program header of the segment, to signal to the runtime +/// that the program does not rely on being able to read that segment. +pub const SHF_ARM_PURECODE: u64 = 0x20000000; + +/// Architecture compatibility information. +/// +/// A segment of type PT_ARM_ARCHEXT contains information describing the +/// platform capabilities required by the executable file. The segment is +/// optional, but if present it must appear before segment of type PT_LOAD. +pub const PT_ARM_ARCHEXT: u32 = 0x70000000; +/// alias for unwind +pub const PT_ARM_EXIDX: u32 = 0x70000001; +/// Exception unwind tables +pub const PT_ARM_UNWIND: u32 = 0x70000001; + +// Contents of a PT_ARM_ARCHEXT segment shall contain at least one 32-bit word with meanings: +/// Masks bits describing the format of data in subsequent words. +pub const PT_ARM_ARCHEXT_FMTMSK: u32 = 0xff000000; +/// There are no additional words of data. However, if EF_OSABI is non-zero, the +/// relevant platform ABI may define additional data that follows the initial word. +pub const PT_ARM_ARCHEXT_FMT_OS: u32 = 0x00000000; +/// See +/// and +pub const PT_ARM_ARCHEXT_FMT_ABI: u32 = 0x01000000; + +/// Masks bits describing the architecture profile required by the executable. +pub const PT_ARM_ARCHEXT_PROFMSK: u32 = 0x00ff0000; +/// The architecture has no profile variants, or the image has no profile-specific constraints +pub const PT_ARM_ARCHEXT_PROF_NONE: u32 = 0x00000000; +/// (‘A’<<16) The executable file requires the Application profile +pub const PT_ARM_ARCHEXT_PROF_ARM: u32 = 0x00410000; +/// (‘R’<<16) The executable file requires the Real-Time profile +pub const PT_ARM_ARCHEXT_PROF_RT: u32 = 0x00520000; +/// (‘M’<<16) The executable file requires the Microcontroller profile +pub const PT_ARM_ARCHEXT_PROF_MC: u32 = 0x004D0000; +/// (‘S’<<16) The executable file requires the ‘classic’ (‘A’ or ‘R’ profile) exception model. +pub const PT_ARM_ARCHEXT_PROF_CLASSIC: u32 = 0x00530000; + +/// Masks bits describing the base architecture required by the executable. +pub const PT_ARM_ARCHEXT_ARCHMSK: u32 = 0x000000ff; +/// The needed architecture is unknown or specified in some other way +pub const PT_ARM_ARCHEXT_ARCH_UNKN: u32 = 0x00; +/// Architecture v4 +pub const PT_ARM_ARCHEXT_ARCHV4: u32 = 0x01; +/// Architecture v4T +pub const PT_ARM_ARCHEXT_ARCHV4T: u32 = 0x02; +/// Architecture v5T +pub const PT_ARM_ARCHEXT_ARCHV5T: u32 = 0x03; +/// Architecture v5TE +pub const PT_ARM_ARCHEXT_ARCHV5TE: u32 = 0x04; +/// Architecture v5TEJ +pub const PT_ARM_ARCHEXT_ARCHV5TEJ: u32 = 0x05; +/// Architecture v6 +pub const PT_ARM_ARCHEXT_ARCHV6: u32 = 0x06; +/// Architecture v6KZ +pub const PT_ARM_ARCHEXT_ARCHV6KZ: u32 = 0x07; +/// Architecture v6T2 +pub const PT_ARM_ARCHEXT_ARCHV6T2: u32 = 0x08; +/// Architecture v6K +pub const PT_ARM_ARCHEXT_ARCHV6K: u32 = 0x09; +/// Architecture v7 (in this case the architecture profile may also be +/// required to fully specify the needed execution environment). +pub const PT_ARM_ARCHEXT_ARCHV7: u32 = 0x0A; +/// Architecture v6M (e.g. Cortex-M0) +pub const PT_ARM_ARCHEXT_ARCHV6M: u32 = 0x0B; +/// Architecture v6S-M (e.g. Cortex-M0) +pub const PT_ARM_ARCHEXT_ARCHV6SM: u32 = 0x0C; +/// Architecture v7E-M +pub const PT_ARM_ARCHEXT_ARCHV7EM: u32 = 0x0D; + +/// gives the number of entries in the dynamic symbol table, including the initial dummy symbol +pub const DT_ARM_SYMTABSZ: i64 = 0x70000001; +/// holds the address of the pre-emption map for platforms that use the DLL static binding mode +pub const DT_ARM_PREEMPTMAP: i64 = 0x70000002; + +// ARM relocs +// +// * S (when used on its own) is the address of the symbol. +// * A is the addend for the relocation. +// * P is the address of the place being relocated (derived from r_offset). +// * Pa is the adjusted address of the place being relocated, defined as (P & 0xFFFFFFFC). +// * T is 1 if the target symbol S has type STT_FUNC and the symbol addresses a Thumb instruction; 0 otherwise. +// * B(S) is the addressing origin of the output segment defining the symbol S. +// The origin is not required to be the base address of the segment. This value must always be word-aligned. +// * GOT_ORG is the addressing origin of the Global Offset Table (the indirection table for imported data addresses). +// This value must always be word-aligned. +// * GOT(S) is the address of the GOT entry for the symbol S + +/// no reloc +pub const R_ARM_NONE: u32 = 0; +/// Deprecated PC relative 26 bit branch. `((S + A) | T) – P` +pub const R_ARM_PC24: u32 = 1; +/// Direct 32 bit. `(S + A) | T` +pub const R_ARM_ABS32: u32 = 2; +/// PC relative 32 bit. `((S + A) | T) | – P` +pub const R_ARM_REL32: u32 = 3; +/// `S + A – P` +pub const R_ARM_LDR_PC_G0: u32 = 4; +/// Direct 16 bit. `S + A` +pub const R_ARM_ABS16: u32 = 5; +/// Direct 12 bit. `S + A` +pub const R_ARM_ABS12: u32 = 6; +/// Direct & 0x7C `(LDR, STR). S + A` +pub const R_ARM_THM_ABS5: u32 = 7; +/// Direct 8 bit. `S + A` +pub const R_ARM_ABS8: u32 = 8; +/// `((S + A) | T) – B(S)` +pub const R_ARM_SBREL32: u32 = 9; +/// PC relative 24 bit (Thumb32 BL). `((S + A) | T) – P` +pub const R_ARM_THM_CALL: u32 = 10; +/// PC relative & 0x3FC (Thumb16 LDR, ADD, ADR). `S + A – Pa` +pub const R_ARM_THM_PC8: u32 = 11; +pub const R_ARM_BREL_ADJ: u32 = 12; +/// dynamic reloc +pub const R_ARM_TLS_DESC: u32 = 13; +/// obsolete/reserved +pub const R_ARM_THM_SWI8: u32 = 14; +/// obsolete/reserved +pub const R_ARM_XPC25: u32 = 15; +/// obsolete/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. `S + A – TLS` +pub const R_ARM_TLS_DTPOFF32: u32 = 18; +/// Offset in static TLS block. `S + A – Tp` +pub const R_ARM_TLS_TPOFF32: u32 = 19; +/// dynamic reloc Copy symbol at runtime. +pub const R_ARM_COPY: u32 = 20; +/// Create GOT entry. `(S + A) | T` +pub const R_ARM_GLOB_DAT: u32 = 21; +/// Create PLT entry. `(S + A) | T` +pub const R_ARM_JUMP_SLOT: u32 = 22; +/// Adjust by program base. `B(S) + A` +pub const R_ARM_RELATIVE: u32 = 23; +/// 32 bit offset to GOT. `((S + A) | T) – GOT_ORG` +pub const R_ARM_GOTOFF32: u32 = 24; +/// 32 bit PC relative offset to GOT. `B(S) + A – P` +pub const R_ARM_BASE_PREL: u32 = 25; +/// 32 bit GOT entry. `GOT(S) + A – GOT_ORG` +pub const R_ARM_BASE_BREL: u32 = 26; +/// Deprecated, 32 bit PLT address. +pub const R_ARM_PLT32: u32 = 27; +/// PC relative 24 bit (BL, BLX). `((S + A) | T) – P` +pub const R_ARM_CALL: u32 = 28; +/// PC relative 24 bit (B, BL{cond}). `((S + A) | T) – P` +pub const R_ARM_JUMP24: u32 = 29; +/// PC relative 24 bit (Thumb32 B.W). `((S + A) | T) – P` +pub const R_ARM_THM_JUMP24: u32 = 30; +/// Adjust by program base. `B(S) + A` +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. `((S + A) | T) – B(S)` +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. `((S + A) | T) – P` +pub const R_ARM_PREL31: u32 = 42; +/// Direct 16-bit (MOVW). `(S + A) | T` +pub const R_ARM_MOVW_ABS_NC: u32 = 43; +/// Direct high 16-bit (MOVT). `S + A` +pub const R_ARM_MOVT_ABS: u32 = 44; +/// PC relative 16-bit (MOVW). `((S + A) | T) – P` +pub const R_ARM_MOVW_PREL_NC: u32 = 45; +/// PC relative (MOVT). `S + A - P` +pub const R_ARM_MOVT_PREL: u32 = 46; +/// Direct 16 bit (Thumb32 MOVW). `(S + A) | T` +pub const R_ARM_THM_MOVW_ABS_NC: u32 = 47; +/// Direct high 16 bit (Thumb32 MOVT). `S + A` +pub const R_ARM_THM_MOVT_ABS: u32 = 48; +/// PC relative 16 bit (Thumb32 MOVW). `((S + A) | T) – P` +pub const R_ARM_THM_MOVW_PREL_NC: u32 = 49; +/// PC relative high 16 bit (Thumb32 MOVT). `S + A – P` +pub const R_ARM_THM_MOVT_PREL: u32 = 50; +/// PC relative 20 bit (Thumb32 B{cond}.W). `((S + A) | T) – P` +pub const R_ARM_THM_JUMP19: u32 = 51; +/// PC relative X & 0x7E (Thumb16 CBZ, CBNZ). `S + A – P` +pub const R_ARM_THM_JUMP6: u32 = 52; +/// PC relative 12 bit (Thumb32 ADR.W). `((S + A) | T) – Pa` +pub const R_ARM_THM_ALU_PREL_11_0: u32 = 53; +/// PC relative 12 bit (Thumb32 LDR{D,SB,H,SH}). `S + A – Pa` +pub const R_ARM_THM_PC12: u32 = 54; +/// Direct 32-bit. `S + A` +pub const R_ARM_ABS32_NOI: u32 = 55; +/// PC relative 32-bit. `S + A - P` +pub const R_ARM_REL32_NOI: u32 = 56; +/// PC relative (ADD, SUB). `((S + A) | T) – P` +pub const R_ARM_ALU_PC_G0_NC: u32 = 57; +/// PC relative (ADD, SUB). `((S + A) | T) – P` +pub const R_ARM_ALU_PC_G0: u32 = 58; +/// PC relative (ADD, SUB). `((S + A) | T) – P` +pub const R_ARM_ALU_PC_G1_NC: u32 = 59; +/// PC relative (ADD, SUB). `((S + A) | T) – P` +pub const R_ARM_ALU_PC_G1: u32 = 60; +/// PC relative (ADD, SUB). `((S + A) | T) – P` +pub const R_ARM_ALU_PC_G2: u32 = 61; +/// PC relative (LDR,STR,LDRB,STRB). `S + A – P` +pub const R_ARM_LDR_PC_G1: u32 = 62; +/// PC relative (LDR,STR,LDRB,STRB). `S + A – P` +pub const R_ARM_LDR_PC_G2: u32 = 63; +/// PC relative (STR{D,H}, LDR{D,SB,H,SH}). `S + A – P` +pub const R_ARM_LDRS_PC_G0: u32 = 64; +/// PC relative (STR{D,H}, LDR{D,SB,H,SH}). `S + A – P` +pub const R_ARM_LDRS_PC_G1: u32 = 65; +/// PC relative (STR{D,H}, LDR{D,SB,H,SH}). `S + A – P` +pub const R_ARM_LDRS_PC_G2: u32 = 66; +/// PC relative (LDC, STC). `S + A – P` +pub const R_ARM_LDC_PC_G0: u32 = 67; +/// PC relative (LDC, STC). `S + A – P` +pub const R_ARM_LDC_PC_G1: u32 = 68; +/// PC relative (LDC, STC). `S + A – P` +pub const R_ARM_LDC_PC_G2: u32 = 69; +/// Program base relative (ADD,SUB). `((S + A) | T) – B(S)` +pub const R_ARM_ALU_SB_G0_NC: u32 = 70; +/// Program base relative (ADD,SUB). `((S + A) | T) – B(S)` +pub const R_ARM_ALU_SB_G0: u32 = 71; +/// Program base relative (ADD,SUB). `((S + A) | T) – B(S)` +pub const R_ARM_ALU_SB_G1_NC: u32 = 72; +/// Program base relative (ADD,SUB). `((S + A) | T) – B(S)` +pub const R_ARM_ALU_SB_G1: u32 = 73; +/// Program base relative (ADD,SUB). `((S + A) | T) – B(S)` +pub const R_ARM_ALU_SB_G2: u32 = 74; +/// Program base relative (LDR, STR, LDRB, STRB). `S + A – B(S)` +pub const R_ARM_LDR_SB_G0: u32 = 75; +/// Program base relative (LDR, STR, LDRB, STRB). `S + A – B(S)` +pub const R_ARM_LDR_SB_G1: u32 = 76; +/// Program base relative (LDR, STR, LDRB, STRB). `S + A – B(S)` +pub const R_ARM_LDR_SB_G2: u32 = 77; +/// Program base relative (LDR, STR, LDRB, STRB). `S + A – B(S)` +pub const R_ARM_LDRS_SB_G0: u32 = 78; +/// Program base relative (LDR, STR, LDRB, STRB). `S + A – B(S)` +pub const R_ARM_LDRS_SB_G1: u32 = 79; +/// Program base relative (LDR, STR, LDRB, STRB). `S + A – B(S)` +pub const R_ARM_LDRS_SB_G2: u32 = 80; +/// Program base relative (LDC,STC). `S + A – B(S)` +pub const R_ARM_LDC_SB_G0: u32 = 81; +/// Program base relative (LDC,STC). `S + A – B(S)` +pub const R_ARM_LDC_SB_G1: u32 = 82; +/// Program base relative (LDC,STC). `S + A – B(S)` +pub const R_ARM_LDC_SB_G2: u32 = 83; +/// Program base relative 16 bit (MOVW). `((S + A) | T) – B(S)` +pub const R_ARM_MOVW_BREL_NC: u32 = 84; +/// Program base relative high 16 bit (MOVT). `S + A – B(S)` +pub const R_ARM_MOVT_BREL: u32 = 85; +/// Program base relative 16 bit (MOVW). `((S + A) | T) – B(S)` +pub const R_ARM_MOVW_BREL: u32 = 86; +/// Program base relative 16 bit (Thumb32 MOVW). `((S + A) | T) – B(S)` +pub const R_ARM_THM_MOVW_BREL_NC: u32 = 87; +/// Program base relative high 16 bit (Thumb32 MOVT). `S + A – B(S)` +pub const R_ARM_THM_MOVT_BREL: u32 = 88; +/// Program base relative 16 bit (Thumb32 MOVW). `((S + A) | T) – B(S)` +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; +/// `PLT(S) + A` +pub const R_ARM_PLT32_ABS: u32 = 94; +/// GOT entry. `GOT(S) + A` +pub const R_ARM_GOT_ABS: u32 = 95; +/// PC relative GOT entry. `GOT(S) + A – P` +pub const R_ARM_GOT_PREL: u32 = 96; +/// GOT entry relative to GOT origin (LDR). `GOT(S) + A – GOT_ORG` +pub const R_ARM_GOT_BREL12: u32 = 97; +/// 12 bit, GOT entry relative to GOT origin (LDR, STR). `S + A – GOT_ORG` +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). `S + A – P` +pub const R_ARM_THM_JUMP11: u32 = 102; +/// PC relative & 0x1FE (Thumb16 B/B{cond}). `S + A – P` +pub const R_ARM_THM_JUMP8: u32 = 103; +/// PC-rel 32 bit for global dynamic thread local data. `GOT(S) + A – P` +pub const R_ARM_TLS_GD32: u32 = 104; +/// PC-rel 32 bit for local dynamic thread local data. `GOT(S) + A – P` +pub const R_ARM_TLS_LDM32: u32 = 105; +/// 32 bit offset relative to TLS block. `S + A – TLS` +pub const R_ARM_TLS_LDO32: u32 = 106; +/// PC-rel 32 bit for GOT entry of static TLS block offset. `GOT(S) + A – P` +pub const R_ARM_TLS_IE32: u32 = 107; +/// 32 bit offset relative to static TLS block. `S + A – tp` +pub const R_ARM_TLS_LE32: u32 = 108; +/// 12 bit relative to TLS block (LDR, STR). `S + A – TLS` +pub const R_ARM_TLS_LDO12: u32 = 109; +/// 12 bit relative to static TLS block (LDR, STR). `S + A – tp` +pub const R_ARM_TLS_LE12: u32 = 110; +/// 12 bit GOT entry relative to GOT origin (LDR). `GOT(S) + A – GOT_ORG` +pub const R_ARM_TLS_IE12GP: u32 = 111; +/// Obsolete. +pub const R_ARM_ME_TOO: u32 = 128; +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). `GOT(S) + A – GOT_ORG` +pub const R_ARM_THM_GOT_BREL12: u32 = 131; +/// Static Thumb16 `(S + A) | T` +pub const R_ARM_THM_ALU_ABS_G0_NC: u32 = 132; +/// Static Thumb16 `S + A` +pub const R_ARM_THM_ALU_ABS_G1_NC: u32 = 133; +/// Static Thumb16 `S + A` +pub const R_ARM_THM_ALU_ABS_G2_NC: u32 = 134; +/// Static Thumb16 `S + A` +pub const R_ARM_THM_ALU_ABS_G3: u32 = 135; +/// Static Arm `((S + A) | T) – P` +pub const R_ARM_THM_BF16: u32 = 136; +/// Static Arm `((S + A) | T) – P` +pub const R_ARM_THM_BF12: u32 = 137; +/// Static Arm `((S + A) | T) – P` +pub const R_ARM_THM_BF18: u32 = 138; +pub const R_ARM_IRELATIVE: u32 = 160; + +/// Object file compatibility attributes +pub const SHT_AARCH64_ATTRIBUTES: u32 = 0x70000003; +pub const SHT_AARCH64_ATTRIBUTES_SECTION_NAME: &str = ".ARM.attributes"; + +/// Architecture compatibility information. +/// +/// A segment of type PT_AARCH64_ARCHEXT (if present) contains information +/// describing the architecture capabilities required by the executable file. +/// Not all platform ABIs require this segment; the Linux ABI does not. If the +/// segment is present it must appear before segment of type PT_LOAD. +pub const PT_AARCH64_ARCHEXT: u32 = 0x70000000; +/// (if present) describes the location of a program’s exception unwind tables. +pub const PT_AARCH64_UNWIND: u32 = 0x70000001; +/// Reserved for MTE memory tag data dumps in core files +/// (if present) hold MTE memory tags for a particular memory range. +/// At present they are defined for core dump files of type ET_CORE +/// See +pub const PT_AARCH64_MEMTAG_MTE: u32 = 0x70000002; + +/// The function associated with the symbol may follow a variant procedure call +/// standard with different register usage convention. +/// Found in Symbol's st_other field +pub const STO_AARCH64_VARIANT_PCS: u8 = 0x80; + +pub const GNU_PROPERTY_AARCH64_FEATURE_1_AND: u32 = 0xc0000000; +pub const GNU_PROPERTY_AARCH64_FEATURE_1_BTI: u32 = 0x1; +pub const GNU_PROPERTY_AARCH64_FEATURE_1_PAC: u32 = 0x2; + +// AArch64 specific values for the Dyn d_tag field. +/// indicates PLTs enabled with Branch Target Identification mechanism +pub const DT_AARCH64_BTI_PLT: i64 = 0x70000001; +/// indicates PLTs enabled with Pointer Authentication. +pub const DT_AARCH64_PAC_PLT: i64 = 0x70000003; +/// must be present if there are R_{CLS}_JUMP_SLOT relocations that reference +/// symbols marked with the STO_AARCH64_VARIANT_PCS flag set in their st_other field +pub const DT_AARCH64_VARIANT_PCS: i64 = 0x70000005; + +/// No relocation. +pub const R_AARCH64_NONE: u32 = 0; +/// 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; +/// 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: u32 = 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: u32 = 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: u32 = 15:0. +pub const R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC: u32 = 540; +/// Page-rel. ADRP: u32 = 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; + +// ____ ____ ____ +// | _ \ _____ _____ _ __| _ \ / ___| +// | |_) / _ \ \ /\ / / _ \ '__| |_) | | +// | __/ (_) \ V V / __/ | | __/| |___ +// |_| \___/ \_/\_/ \___|_| |_| \____| +// +// See: https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#ELF-HEAD + +/// PowerPC embedded flag +pub const EF_PPC_EMB: u32 = 0x80000000; +/// PowerPC -mrelocatable flag +pub const EF_PPC_RELOCATABLE: u32 = 0x00010000; +/// PowerPC -mrelocatable-lib flag +pub const EF_PPC_RELOCATABLE_LIB: u32 = 0x00008000; + +// PowerPC relocations types +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; + +/// (sym+add)@tls +pub const R_PPC_TLS: u32 = 67; +/// (sym+add)@dtpmod +pub const R_PPC_DTPMOD32: u32 = 68; +/// (sym+add)@tprel +pub const R_PPC_TPREL16: u32 = 69; +/// (sym+add)@tprel@l +pub const R_PPC_TPREL16_LO: u32 = 70; +/// (sym+add)@tprel@h +pub const R_PPC_TPREL16_HI: u32 = 71; +/// (sym+add)@tprel@ha +pub const R_PPC_TPREL16_HA: u32 = 72; +/// (sym+add)@tprel +pub const R_PPC_TPREL32: u32 = 73; +/// (sym+add)@dtprel +pub const R_PPC_DTPREL16: u32 = 74; +/// (sym+add)@dtprel@l +pub const R_PPC_DTPREL16_LO: u32 = 75; +/// (sym+add)@dtprel@h +pub const R_PPC_DTPREL16_HI: u32 = 76; +/// (sym+add)@dtprel@ha +pub const R_PPC_DTPREL16_HA: u32 = 77; +/// (sym+add)@dtprel +pub const R_PPC_DTPREL32: u32 = 78; +/// (sym+add)@got@tlsgd +pub const R_PPC_GOT_TLSGD16: u32 = 79; +/// (sym+add)@got@tlsgd@l +pub const R_PPC_GOT_TLSGD16_LO: u32 = 80; +/// (sym+add)@got@tlsgd@h +pub const R_PPC_GOT_TLSGD16_HI: u32 = 81; +/// (sym+add)@got@tlsgd@ha +pub const R_PPC_GOT_TLSGD16_HA: u32 = 82; +/// (sym+add)@got@tlsld +pub const R_PPC_GOT_TLSLD16: u32 = 83; +/// (sym+add)@got@tlsld@l +pub const R_PPC_GOT_TLSLD16_LO: u32 = 84; +/// (sym+add)@got@tlsld@h +pub const R_PPC_GOT_TLSLD16_HI: u32 = 85; +/// (sym+add)@got@tlsld@ha +pub const R_PPC_GOT_TLSLD16_HA: u32 = 86; +/// (sym+add)@got@tprel +pub const R_PPC_GOT_TPREL16: u32 = 87; +/// (sym+add)@got@tprel@l +pub const R_PPC_GOT_TPREL16_LO: u32 = 88; +/// (sym+add)@got@tprel@h +pub const R_PPC_GOT_TPREL16_HI: u32 = 89; +/// (sym+add)@got@tprel@ha +pub const R_PPC_GOT_TPREL16_HA: u32 = 90; +/// (sym+add)@got@dtprel +pub const R_PPC_GOT_DTPREL16: u32 = 91; +/// (sym+add)@got@dtprel@l +pub const R_PPC_GOT_DTPREL16_LO: u32 = 92; +/// (sym+add)@got@dtprel@h +pub const R_PPC_GOT_DTPREL16_HI: u32 = 93; +/// (sym+add)@got@dtprel@ha +pub const R_PPC_GOT_DTPREL16_HA: u32 = 94; +/// (sym+add)@tlsgd +pub const R_PPC_TLSGD: u32 = 95; +/// (sym+add)@tlsld +pub const R_PPC_TLSLD: u32 = 96; + +// The remaining relocs are from the Embedded ELF ABI, and are not in the SVR4 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; +pub const R_PPC_EMB_RELSDA: u32 = 116; + +/// 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. +/// (sym+add-.) +pub const R_PPC_REL16: u32 = 249; +/// (sym+add-.)@l +pub const R_PPC_REL16_LO: u32 = 250; +/// (sym+add-.)@h +pub const R_PPC_REL16_HI: u32 = 251; +/// (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 the Dyn d_tag field. +pub const DT_PPC_GOT: i64 = 0x70000000; +pub const DT_PPC_OPT: i64 = 0x70000001; + +/// PowerPC specific values for the DT_PPC_OPT Dyn entry. +pub const PPC_OPT_TLS: u64 = 1; + +// e_flags 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 specific values for the Dyn d_tag field. +pub const DT_PPC64_GLINK: i64 = 0x70000000; +pub const DT_PPC64_OPD: i64 = 0x70000001; +pub const DT_PPC64_OPDSZ: i64 = 0x70000002; +pub const DT_PPC64_OPT: i64 = 0x70000003; + +// PowerPC64 specific bits in the DT_PPC64_OPT Dyn entry. +pub const PPC64_OPT_TLS: u64 = 1; +pub const PPC64_OPT_MULTI_TOC: u64 = 2; +pub const PPC64_OPT_LOCALENTRY: u64 = 4; + +// PowerPC64 specific values for the Elf64_Sym st_other field. +pub const STO_PPC64_LOCAL_BIT: u8 = 5; +pub const STO_PPC64_LOCAL_MASK: u8 = 7 << STO_PPC64_LOCAL_BIT; + +// Relocation types +// +// A Represents the addend used to compute the value of the relocatable field. +// B Represents the base address at which a shared object has been loaded into memory during execution. +// G Represents the offset into the global offset table, relative to +// the TOC base, at which the address of the relocation entry's symbol +// plus addend will reside during execution. +// L Represents the section offset or address of the procedure linkage +// table entry for the symbol plus addend. +// M Similar to G, except that the address which is stored may be the +// address of the procedure linkage table entry for the symbol. +// P Represents the place (section offset or address) of the storage +// unit being relocated (computed using r_offset). +// R Represents the offset of the symbol within the section in which +// the symbol is defined (its section-relative address). +// S Represents the value of the symbol whose index resides in the relocation entry. + +/// none +pub const R_PPC64_NONE: u32 = 0; +/// `S + A` +pub const R_PPC64_ADDR32: u32 = 1; +/// `(S + A) >> 2` +pub const R_PPC64_ADDR24: u32 = 2; +/// `S + A` +pub const R_PPC64_ADDR16: u32 = 3; +/// `#lo(S + A)` +pub const R_PPC64_ADDR16_LO: u32 = 4; +/// `#hi(S + A)` +pub const R_PPC64_ADDR16_HI: u32 = 5; +/// `#ha(S + A)` +pub const R_PPC64_ADDR16_HA: u32 = 6; +/// `(S + A) >> 2` +pub const R_PPC64_ADDR14: u32 = 7; +/// `(S + A) >> 2` +pub const R_PPC64_ADDR14_BRTAKEN: u32 = 8; +/// `(S + A) >> 2` +pub const R_PPC64_ADDR14_BRNTAKEN: u32 = 9; +/// `(S + A - P) >> 2` +pub const R_PPC64_REL24: u32 = 10; +/// `(S + A - P) >> 2` +pub const R_PPC64_REL14: u32 = 11; +/// `(S + A - P) >> 2` +pub const R_PPC64_REL14_BRTAKEN: u32 = 12; +/// `(S + A - P) >> 2` +pub const R_PPC64_REL14_BRNTAKEN: u32 = 13; +/// `G` +pub const R_PPC64_GOT16: u32 = 14; +/// `#lo(G)` +pub const R_PPC64_GOT16_LO: u32 = 15; +/// `#hi(G)` +pub const R_PPC64_GOT16_HI: u32 = 16; +/// `#ha(G)` +pub const R_PPC64_GOT16_HA: u32 = 17; +/// none +pub const R_PPC64_COPY: u32 = 19; +/// `S + A` +pub const R_PPC64_GLOB_DAT: u32 = 20; +/// see below +pub const R_PPC64_JMP_SLOT: u32 = 21; +/// `B + A` +pub const R_PPC64_RELATIVE: u32 = 22; +/// `S + A` +pub const R_PPC64_UADDR32: u32 = 24; +/// `S + A` +pub const R_PPC64_UADDR16: u32 = 25; +/// `S + A - P` +pub const R_PPC64_REL32: u32 = 26; +/// `L` +pub const R_PPC64_PLT32: u32 = 27; +/// `L - P` +pub const R_PPC64_PLTREL32: u32 = 28; +/// `#lo(L)` +pub const R_PPC64_PLT16_LO: u32 = 29; +/// `#hi(L)` +pub const R_PPC64_PLT16_HI: u32 = 30; +/// `#ha(L)` +pub const R_PPC64_PLT16_HA: u32 = 31; +/// `R + A` +pub const R_PPC64_SECTOFF: u32 = 33; +/// `#lo(R + A)` +pub const R_PPC64_SECTOFF_LO: u32 = 34; +/// `#hi(R + A)` +pub const R_PPC64_SECTOFF_HI: u32 = 35; +/// `#ha(R + A)` +pub const R_PPC64_SECTOFF_HA: u32 = 36; +/// `(S + A - P) >> 2` +pub const R_PPC64_ADDR30: u32 = 37; +/// `S + A` +pub const R_PPC64_ADDR64: u32 = 38; +/// `#higher(S + A)` +pub const R_PPC64_ADDR16_HIGHER: u32 = 39; +/// `#highera(S + A)` +pub const R_PPC64_ADDR16_HIGHERA: u32 = 40; +/// `#highest(S + A)` +pub const R_PPC64_ADDR16_HIGHEST: u32 = 41; +/// `#highesta(S + A)` +pub const R_PPC64_ADDR16_HIGHESTA: u32 = 42; +/// `S + A` +pub const R_PPC64_UADDR64: u32 = 43; +/// `S + A - P` +pub const R_PPC64_REL64: u32 = 44; +/// `L` +pub const R_PPC64_PLT64: u32 = 45; +/// `L - P` +pub const R_PPC64_PLTREL64: u32 = 46; +/// `S + A - .TOC.` +pub const R_PPC64_TOC16: u32 = 47; +/// `#lo(S + A - .TOC.)` +pub const R_PPC64_TOC16_LO: u32 = 48; +/// `#hi(S + A - .TOC.)` +pub const R_PPC64_TOC16_HI: u32 = 49; +/// `#ha(S + A - .TOC.)` +pub const R_PPC64_TOC16_HA: u32 = 50; +/// `.TOC.` +pub const R_PPC64_TOC: u32 = 51; +/// `M` +pub const R_PPC64_PLTGOT16: u32 = 52; +/// `#lo(M)` +pub const R_PPC64_PLTGOT16_LO: u32 = 53; +/// `#hi(M)` +pub const R_PPC64_PLTGOT16_HI: u32 = 54; +/// `#ha(M)` +pub const R_PPC64_PLTGOT16_HA: u32 = 55; +/// `(S + A) >> 2` +pub const R_PPC64_ADDR16_DS: u32 = 56; +/// `#lo(S + A) >> 2` +pub const R_PPC64_ADDR16_LO_DS: u32 = 57; +/// `G >> 2` +pub const R_PPC64_GOT16_DS: u32 = 58; +/// `#lo(G) >> 2` +pub const R_PPC64_GOT16_LO_DS: u32 = 59; +/// `#lo(L) >> 2` +pub const R_PPC64_PLT16_LO_DS: u32 = 60; +/// `(R + A) >> 2` +pub const R_PPC64_SECTOFF_DS: u32 = 61; +/// `#lo(R + A) >> 2` +pub const R_PPC64_SECTOFF_LO_DS: u32 = 62; +/// `(S + A - .TOC.) >> 2` +pub const R_PPC64_TOC16_DS: u32 = 63; +/// `#lo(S + A - .TOC.) >> 2` +pub const R_PPC64_TOC16_LO_DS: u32 = 64; +/// `M >> 2` +pub const R_PPC64_PLTGOT16_DS: u32 = 65; +/// `#lo(M) >> 2` +pub const R_PPC64_PLTGOT16_LO_DS: u32 = 66; +/// none +pub const R_PPC64_TLS: u32 = 67; +/// `@dtpmod` +pub const R_PPC64_DTPMOD64: u32 = 68; +/// `@tprel` +pub const R_PPC64_TPREL16: u32 = 69; +/// `#lo(@tprel)` +pub const R_PPC64_TPREL16_LO: u32 = 60; +/// `#hi(@tprel)` +pub const R_PPC64_TPREL16_HI: u32 = 71; +/// `#ha(@tprel)` +pub const R_PPC64_TPREL16_HA: u32 = 72; +/// `@tprel` +pub const R_PPC64_TPREL64: u32 = 73; +/// `@dtprel` +pub const R_PPC64_DTPREL16: u32 = 74; +/// `#lo(@dtprel)` +pub const R_PPC64_DTPREL16_LO: u32 = 75; +/// `#hi(@dtprel)` +pub const R_PPC64_DTPREL16_HI: u32 = 76; +/// `#ha(@dtprel)` +pub const R_PPC64_DTPREL16_HA: u32 = 77; +/// `@dtprel` +pub const R_PPC64_DTPREL64: u32 = 78; +/// `@got@tlsgd` +pub const R_PPC64_GOT_TLSGD16: u32 = 79; +/// `#lo(@got@tlsgd)` +pub const R_PPC64_GOT_TLSGD16_LO: u32 = 80; +/// `#hi(@got@tlsgd)` +pub const R_PPC64_GOT_TLSGD16_HI: u32 = 81; +/// `#ha(@got@tlsgd)` +pub const R_PPC64_GOT_TLSGD16_HA: u32 = 82; +/// `@got@tlsld` +pub const R_PPC64_GOT_TLSLD16: u32 = 83; +/// `#lo(@got@tlsld)` +pub const R_PPC64_GOT_TLSLD16_LO: u32 = 84; +/// `#hi(@got@tlsld)` +pub const R_PPC64_GOT_TLSLD16_HI: u32 = 85; +/// `#ha(@got@tlsld)` +pub const R_PPC64_GOT_TLSLD16_HA: u32 = 86; +/// `@got@tprel` +pub const R_PPC64_GOT_TPREL16_DS: u32 = 87; +/// `#lo(@got@tprel)` +pub const R_PPC64_GOT_TPREL16_LO_DS: u32 = 88; +/// `#hi(@got@tprel)` +pub const R_PPC64_GOT_TPREL16_HI: u32 = 89; +/// `#ha(@got@tprel)` +pub const R_PPC64_GOT_TPREL16_HA: u32 = 90; +/// `@got@dtprel` +pub const R_PPC64_GOT_DTPREL16_DS: u32 = 91; +/// `#lo(@got@dtprel)` +pub const R_PPC64_GOT_DTPREL16_LO_DS: u32 = 92; +/// `#hi(@got@dtprel)` +pub const R_PPC64_GOT_DTPREL16_HI: u32 = 93; +/// `#ha(@got@dtprel)` +pub const R_PPC64_GOT_DTPREL16_HA: u32 = 94; +/// `@tprel` +pub const R_PPC64_TPREL16_DS: u32 = 95; +/// `#lo(@tprel)` +pub const R_PPC64_TPREL16_LO_DS: u32 = 96; +/// `#higher(@tprel)` +pub const R_PPC64_TPREL16_HIGHER: u32 = 97; +/// `#highera(@tprel)` +pub const R_PPC64_TPREL16_HIGHERA: u32 = 98; +/// `#highest(@tprel)` +pub const R_PPC64_TPREL16_HIGHEST: u32 = 99; +/// `#highesta(@tprel)` +pub const R_PPC64_TPREL16_HIGHESTA: u32 = 100; +/// `@dtprel` +pub const R_PPC64_DTPREL16_DS: u32 = 101; +/// `#lo(@dtprel)` +pub const R_PPC64_DTPREL16_LO_DS: u32 = 102; +/// `#higher(@dtprel)` +pub const R_PPC64_DTPREL16_HIGHER: u32 = 103; +/// `#highera(@dtprel)` +pub const R_PPC64_DTPREL16_HIGHERA: u32 = 104; +/// `#highest(@dtprel)` +pub const R_PPC64_DTPREL16_HIGHEST: u32 = 105; +/// `#highesta(@dtprel)` +pub const R_PPC64_DTPREL16_HIGHESTA: u32 = 106; +/// `(sym+add)@tlsgd` +pub const R_PPC64_TLSGD: u32 = 107; +/// `(sym+add)@tlsld` +pub const R_PPC64_TLSLD: u32 = 108; +pub const R_PPC64_TOCSAVE: u32 = 109; +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; +pub const R_PPC64_IRELATIVE: u32 = 248; +/// `(sym+add-.)` +pub const R_PPC64_REL16: u32 = 249; +/// `(sym+add-.)@l` +pub const R_PPC64_REL16_LO: u32 = 250; +/// `(sym+add-.)@h` +pub const R_PPC64_REL16_HI: u32 = 251; +/// `(sym+add-.)@ha` +pub const R_PPC64_REL16_HA: u32 = 252; + +// ____ ___ ____ ____ __ __ +// | _ \|_ _/ ___| / ___| \ \ / / +// | |_) || |\___ \| | ____\ \ / / +// | _ < | | ___) | |__|_____\ V / +// |_| \_\___|____/ \____| \_/ +// +// See: https://github.com/riscv-non-isa/riscv-elf-psabi-doc + +/// This bit is set when the binary targets the C ABI. +pub const EF_RISCV_RVC: u32 = 0x0001; +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; +/// This is used as a mask to test for one of the above floating-point ABIs, +/// e.g., (e_flags & EF_RISCV_FLOAT_ABI) == EF_RISCV_FLOAT_ABI_DOUBLE. +pub const EF_RISCV_FLOAT_ABI_MASK: u32 = 0x0006; +/// This bit is set when the binary targets the E ABI. +pub const EF_RISCV_RVE: u32 = 0x0008; +/// This bit is set when the binary requires the RVTSO memory consistency model. +pub const EF_RISCV_TSO: u32 = 0x0010; + +pub const SHT_RISCV_ATTRIBUTES: u32 = 0x70000003; // SHT_LOPROC + 3; +pub const SHT_RISCV_ATTRIBUTES_SECTION_NAME: &str = ".riscv.attributes"; + +pub const PT_RISCV_ATTRIBUTES: u32 = 0x70000003; + +/// Any functions that use registers in a way that is incompatible with the +/// calling convention of the ABI in use must be annotated with STO_RISCV_VARIANT_CC +pub const STO_RISCV_VARIANT_CC: u8 = 0x80; + +/// An object must have the dynamic tag DT_RISCV_VARIANT_CC if it has one or more R_RISCV_JUMP_SLOT +/// relocations against symbols with the STO_RISCV_VARIANT_CC attribute. +pub const DT_RISCV_VARIANT_CC: i64 = 0x70000001; + +// RISC-V relocation types +// +// A Addend field in the relocation entry associated with the symbol +// B Base address of a shared object loaded into memory +// G Offset of the symbol into the GOT (Global Offset Table) +// GOT Address of the GOT (Global Offset Table) +// P Position of the relocation +// S Value of the symbol in the symbol table +// V Value at the position of the relocation +// GP Value of __global_pointer$ symbol +// TLSMODULE TLS module index for the object containing the symbol +// TLSOFFSET TLS static block offset (relative to tp) for the object containing the symbol + +pub const R_RISCV_NONE: u32 = 0; +/// 32-bit relocation: `S + A` +pub const R_RISCV_32: u32 = 1; +/// 64-bit relocation: `S + A` +pub const R_RISCV_64: u32 = 2; +/// Adjust a link address (A) to its load address: `(B + A).` +pub const R_RISCV_RELATIVE: u32 = 3; +/// Must be in executable; not allowed in shared library +pub const R_RISCV_COPY: u32 = 4; +/// Indicates the symbol associated with a PLT entry: `S` +pub const R_RISCV_JUMP_SLOT: u32 = 5; +/// `TLSMODULE` +pub const R_RISCV_TLS_DTPMOD32: u32 = 6; +/// `TLSMODULE` +pub const R_RISCV_TLS_DTPMOD64: u32 = 7; +/// `S + A - TLS_DTV_OFFSET` +pub const R_RISCV_TLS_DTPREL32: u32 = 8; +/// `S + A - TLS_DTV_OFFSET` +pub const R_RISCV_TLS_DTPREL64: u32 = 9; +/// `S + A + TLSOFFSET` +pub const R_RISCV_TLS_TPREL32: u32 = 10; +/// `S + A + TLSOFFSET` +pub const R_RISCV_TLS_TPREL64: u32 = 11; +/// 12-bit PC-relative branch offset `S + A - P` +pub const R_RISCV_BRANCH: u32 = 16; +/// 20-bit PC-relative jump offset `S + A - P` +pub const R_RISCV_JAL: u32 = 17; +/// Deprecated, please use CALL_PLT instead 32-bit PC-relative function call, macros call, tail: `S + A - P` +pub const R_RISCV_CALL: u32 = 18; +/// 32-bit PC-relative function call, macros call, tail (PIC): `S + A - P` +pub const R_RISCV_CALL_PLT: u32 = 19; +/// High 20 bits of 32-bit PC-relative GOT access, %got_pcrel_hi(symbol): `G + GOT + A - P` +pub const R_RISCV_GOT_HI20: u32 = 20; +/// High 20 bits of 32-bit PC-relative TLS IE GOT access, macro la.tls.ie +pub const R_RISCV_TLS_GOT_HI20: u32 = 21; +/// High 20 bits of 32-bit PC-relative TLS GD GOT reference, macro la.tls.gd +pub const R_RISCV_TLS_GD_HI20: u32 = 22; +/// High 20 bits of 32-bit PC-relative reference, %pcrel_hi(symbol): `S + A - P` +pub const R_RISCV_PCREL_HI20: u32 = 23; +/// Low 12 bits of a 32-bit PC-relative, %pcrel_lo(address of %pcrel_hi), the addend must be 0: `S - P` +pub const R_RISCV_PCREL_LO12_I: u32 = 24; +/// Low 12 bits of a 32-bit PC-relative, %pcrel_lo(address of %pcrel_hi), the addend must be 0: `S - P` +pub const R_RISCV_PCREL_LO12_S: u32 = 25; +/// High 20 bits of 32-bit absolute address, %hi(symbol): `S + A` +pub const R_RISCV_HI20: u32 = 26; +/// Low 12 bits of 32-bit absolute address, %lo(symbol): `S + A` +pub const R_RISCV_LO12_I: u32 = 27; +/// Low 12 bits of 32-bit absolute address, %lo(symbol): `S + A` +pub const R_RISCV_LO12_S: u32 = 28; +/// High 20 bits of TLS LE thread pointer offset, `%tprel_hi(symbol)` +pub const R_RISCV_TPREL_HI20: u32 = 29; +/// Low 12 bits of TLS LE thread pointer offset, `%tprel_lo(symbol)` +pub const R_RISCV_TPREL_LO12_I: u32 = 30; +/// Low 12 bits of TLS LE thread pointer offset, `%tprel_lo(symbol)` +pub const R_RISCV_TPREL_LO12_S: u32 = 31; +/// TLS LE thread pointer usage, `%tprel_add(symbol)` +pub const R_RISCV_TPREL_ADD: u32 = 32; +/// 8-bit label addition: `V + S + A` +pub const R_RISCV_ADD8: u32 = 33; +/// 16-bit label addition: `V + S + A` +pub const R_RISCV_ADD16: u32 = 34; +/// 32-bit label addition: `V + S + A` +pub const R_RISCV_ADD32: u32 = 35; +/// 64-bit label addition: `V + S + A` +pub const R_RISCV_ADD64: u32 = 36; +/// 8-bit label subtraction: `V - S - A` +pub const R_RISCV_SUB8: u32 = 37; +/// 16-bit label subtraction: `V - S - A` +pub const R_RISCV_SUB16: u32 = 38; +/// 32-bit label subtraction: `V - S - A` +pub const R_RISCV_SUB32: u32 = 39; +/// 64-bit label subtraction: `V - S - A` +pub const R_RISCV_SUB64: u32 = 40; +/// Alignment statement. The addend indicates the number of bytes occupied by +/// nop instructions at the relocation offset. The alignment boundary is +/// specified by the addend rounded up to the next power of two. +pub const R_RISCV_ALIGN: u32 = 43; +/// 8-bit PC-relative branch offset: `S + A - P` +pub const R_RISCV_RVC_BRANCH: u32 = 44; +/// 11-bit PC-relative jump offset: `S + A - P` +pub const R_RISCV_RVC_JUMP: u32 = 45; +/// High 6 bits of 18-bit absolute address: `S + A` +pub const R_RISCV_RVC_LUI: u32 = 46; +/// Instruction can be relaxed, paired with a normal relocation at the same address +pub const R_RISCV_RELAX: u32 = 51; +/// Local label subtraction: `V - S - A` +pub const R_RISCV_SUB6: u32 = 52; +/// Local label assignment: `S + A` +pub const R_RISCV_SET6: u32 = 53; +/// Local label assignment: `S + A` +pub const R_RISCV_SET8: u32 = 54; +/// Local label assignment: `S + A` +pub const R_RISCV_SET16: u32 = 55; +/// Local label assignment: `S + A` +pub const R_RISCV_SET32: u32 = 56; +/// 32-bit PC relative: `S + A - P` +pub const R_RISCV_32_PCREL: u32 = 57; +/// Relocation against a non-preemptible ifunc symbolifunc_resolver: `(B + A)` +pub const R_RISCV_IRELATIVE: u32 = 58; + +// ___ __ __ _ _ +// __ _( _ ) / /_ / /_ | || | +// \ \/ / _ \| '_ \ | '_ \| || |_ +// > < (_) | (_) | | (_) |__ _| +// /_/\_\___/ \___/___\___/ |_| +// |_____| +// +// See: https://gitlab.com/x86-psABIs/x86-64-ABI + +/// If an object file section does not have this flag set, then it may not hold +/// more than 2GB and can be freely referred to in objects using smaller code models. +pub const SHF_X86_64_LARGE: u64 = 0x10000000; + +/// This section contains unwind function table entries for stack unwinding. +pub const SHT_X86_64_UNWIND: u32 = 0x70000001; // SHT_LOPROC + 1; + +// x86_64 reloc types +// +// A Represents the addend used to compute the value of the relocatable field. +// B Represents the base address at which a shared object has been loaded into memory +// during execution. Generally, a shared object is built with a 0 base virtual address, +// but the execution address will be different. +// G Represents the offset into the global offset table at which the relocation entry’s symbol +// will reside during execution. +// GOT Represents the address of the global offset table. +// L Represents the place (section offset or address) of the Procedure Linkage Table entry for a symbol. +// P Represents the place (section offset or address) of the storage unit being relocated (computed using r_offset). +// S Represents the value of the symbol whose index resides in the relocation entry. +// Z Represents the size of the symbol whose index resides in the relocation entry. + +pub const R_X86_64_NONE: u32 = 0; +/// `S + A` +pub const R_X86_64_64: u32 = 1; +/// `S + A - P` +pub const R_X86_64_PC32: u32 = 2; +/// `G + A` +pub const R_X86_64_GOT32: u32 = 3; +/// `L + A - P` +pub const R_X86_64_PLT32: u32 = 4; +pub const R_X86_64_COPY: u32 = 5; +/// `S` +pub const R_X86_64_GLOB_DAT: u32 = 6; +/// `S` +pub const R_X86_64_JUMP_SLOT: u32 = 7; +/// `B + A` +pub const R_X86_64_RELATIVE: u32 = 8; +/// `G + GOT + A - P` +pub const R_X86_64_GOTPCREL: u32 = 9; +/// `S + A` +pub const R_X86_64_32: u32 = 10; +/// `S + A` +pub const R_X86_64_32S: u32 = 11; +/// `S + A` +pub const R_X86_64_16: u32 = 12; +/// `S + A - P` +pub const R_X86_64_PC16: u32 = 13; +/// `S + A` +pub const R_X86_64_8: u32 = 14; +/// `S + A - P` +pub const R_X86_64_PC8: u32 = 15; +pub const R_X86_64_DTPMOD64: u32 = 16; +pub const R_X86_64_DTPOFF64: u32 = 17; +pub const R_X86_64_TPOFF64: u32 = 18; +pub const R_X86_64_TLSGD: u32 = 19; +pub const R_X86_64_TLSLD: u32 = 20; +pub const R_X86_64_DTPOFF32: u32 = 21; +pub const R_X86_64_GOTTPOFF: u32 = 22; +pub const R_X86_64_TPOFF32: u32 = 23; +/// `S + A - P` +pub const R_X86_64_PC64: u32 = 24; +/// `S + A - GOT` +pub const R_X86_64_GOTOFF64: u32 = 25; +/// `GOT + A - P` +pub const R_X86_64_GOTPC32: u32 = 26; +/// `G + A` +pub const R_X86_64_GOT64: u32 = 27; +/// `G + GOT - P + A` +pub const R_X86_64_GOTPCREL64: u32 = 28; +/// `GOT - P + A` +pub const R_X86_64_GOTPC64: u32 = 29; +/// `L - GOT + A` +pub const R_X86_64_PLTOFF64: u32 = 31; +/// `Z + A` +pub const R_X86_64_SIZE32: u32 = 32; +/// `Z + A` +pub const R_X86_64_SIZE64: u32 = 33; +pub const R_X86_64_GOTPC32_TLSDESC: u32 = 34; +pub const R_X86_64_TLSDESC_CALL: u32 = 35; +pub const R_X86_64_TLSDESC: u32 = 36; +/// `indirect (B + A)` +pub const R_X86_64_IRELATIVE: u32 = 37; +/// `B + A` +pub const R_X86_64_RELATIVE64: u32 = 38; +/// `G + GOT + A - P` +pub const R_X86_64_GOTPCRELX: u32 = 41; +/// `G + GOT + A - P` +pub const R_X86_64_REX_GOTPCRELX: u32 = 42; diff --git a/mikros_std_deps/elf-0.7.4/src/compression.rs b/mikros_std_deps/elf-0.7.4/src/compression.rs new file mode 100644 index 00000000000..1e11fdb6f62 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/compression.rs @@ -0,0 +1,153 @@ +//! Parsing [CompressionHeader] from compressed ELF sections +//! +//! Note: This library does not provide any decompression functionality, but +//! does expose parsed ELF compression headers alongside the raw compressed data. +//! +//! It is up to users of the library to choose the decompression library of +//! their choice when dealing with compressed section contents. +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError}; + +/// C-style 32-bit ELF Compression Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Chdr { + pub ch_type: u32, + pub ch_size: u32, + pub ch_addralign: u32, +} + +/// C-style 64-bit ELF Compression Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Chdr { + pub ch_type: u32, + pub ch_reserved: u32, + pub ch_size: u64, + pub ch_addralign: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CompressionHeader { + pub ch_type: u32, + pub ch_size: u64, + pub ch_addralign: u64, +} + +impl ParseAt for CompressionHeader { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + match class { + Class::ELF32 => Ok(CompressionHeader { + ch_type: endian.parse_u32_at(offset, data)?, + ch_size: endian.parse_u32_at(offset, data)? as u64, + ch_addralign: endian.parse_u32_at(offset, data)? as u64, + }), + Class::ELF64 => { + let ch_type = endian.parse_u32_at(offset, data)?; + let _ch_reserved = endian.parse_u32_at(offset, data)?; + Ok(CompressionHeader { + ch_type, + ch_size: endian.parse_u64_at(offset, data)?, + ch_addralign: endian.parse_u64_at(offset, data)?, + }) + } + } + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 12, + Class::ELF64 => 24, + } + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_chdr32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + CompressionHeader { + ch_type: 0x03020100, + ch_size: 0x07060504, + ch_addralign: 0x0B0A0908, + }, + ); + } + + #[test] + fn parse_chdr32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + CompressionHeader { + ch_type: 0x00010203, + ch_size: 0x04050607, + ch_addralign: 0x08090A0B, + }, + ); + } + + #[test] + fn parse_chdr64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + CompressionHeader { + ch_type: 0x03020100, + ch_size: 0x0F0E0D0C0B0A0908, + ch_addralign: 0x1716151413121110, + }, + ); + } + + #[test] + fn parse_chdr64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + CompressionHeader { + ch_type: 0x00010203, + ch_size: 0x08090A0B0C0D0E0F, + ch_addralign: 0x1011121314151617, + }, + ); + } + + #[test] + fn parse_chdr32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, CompressionHeader>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_chdr32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, CompressionHeader>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_chdr64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, CompressionHeader>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_chdr64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, CompressionHeader>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/dynamic.rs b/mikros_std_deps/elf-0.7.4/src/dynamic.rs new file mode 100644 index 00000000000..af88f46dd08 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/dynamic.rs @@ -0,0 +1,147 @@ +//! Parsing `.dynamic` section or [PT_DYNAMIC](crate::abi::PT_DYNAMIC) segment contents +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ParsingTable}; + +pub type DynamicTable<'data, E> = ParsingTable<'data, E, Dyn>; + +/// C-style 32-bit ELF Dynamic section entry definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Dyn { + pub d_tag: i32, + // union of both {d_val, d_ptr} + pub d_un: u32, +} + +/// C-style 64-bit ELF Dynamic section entry definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Dyn { + pub d_tag: i64, + // union of both {d_val, d_ptr} + pub d_un: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Dyn { + pub d_tag: i64, + pub(super) d_un: u64, +} + +impl Dyn { + pub fn d_val(self) -> u64 { + self.d_un + } + + pub fn d_ptr(self) -> u64 { + self.d_un + } +} + +impl ParseAt for Dyn { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + match class { + Class::ELF32 => Ok(Dyn { + d_tag: endian.parse_i32_at(offset, data)? as i64, + d_un: endian.parse_u32_at(offset, data)? as u64, + }), + Class::ELF64 => Ok(Dyn { + d_tag: endian.parse_i64_at(offset, data)?, + d_un: endian.parse_u64_at(offset, data)?, + }), + } + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 8, + Class::ELF64 => 16, + } + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_dyn32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + Dyn { + d_tag: 0x03020100, + d_un: 0x07060504, + }, + ); + } + + #[test] + fn parse_dyn32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + Dyn { + d_tag: 0x00010203, + d_un: 0x04050607, + }, + ); + } + + #[test] + fn parse_dyn64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + Dyn { + d_tag: 0x0706050403020100, + d_un: 0x0F0E0D0C0B0A0908, + }, + ); + } + + #[test] + fn parse_dyn64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + Dyn { + d_tag: 0x0001020304050607, + d_un: 0x08090A0B0C0D0E0F, + }, + ); + } + + #[test] + fn parse_dyn32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Dyn>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_dyn32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Dyn>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_dyn64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Dyn>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_dyn64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Dyn>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/elf_bytes.rs b/mikros_std_deps/elf-0.7.4/src/elf_bytes.rs new file mode 100644 index 00000000000..f37c2212015 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/elf_bytes.rs @@ -0,0 +1,1580 @@ +use crate::abi; +use crate::compression::CompressionHeader; +use crate::dynamic::{Dyn, DynamicTable}; +use crate::endian::EndianParse; +use crate::file::{parse_ident, Class, FileHeader}; +use crate::gnu_symver::{ + SymbolVersionTable, VerDefIterator, VerNeedIterator, VersionIndex, VersionIndexTable, +}; +use crate::hash::{GnuHashTable, SysVHashTable}; +use crate::note::NoteIterator; +use crate::parse::{ParseAt, ParseError, ReadBytesExt}; +use crate::relocation::{RelIterator, RelaIterator}; +use crate::section::{SectionHeader, SectionHeaderTable}; +use crate::segment::{ProgramHeader, SegmentTable}; +use crate::string_table::StringTable; +use crate::symbol::{Symbol, SymbolTable}; + +// _____ _ _____ ____ _ +// | ____| | | ___| __ ) _ _| |_ ___ ___ +// | _| | | | |_ | _ \| | | | __/ _ \/ __| +// | |___| |___| _| | |_) | |_| | || __/\__ \ +// |_____|_____|_| |____/ \__, |\__\___||___/ +// |___/ +// + +/// This type encapsulates the bytes-oriented interface for parsing ELF objects from `&[u8]`. +/// +/// This parser is no_std and zero-alloc, returning lazy-parsing interfaces wrapped around +/// subslices of the provided ELF bytes `&[u8]`. The various ELF structures are +/// parsed on-demand into a native Rust representation. +/// +/// Example usage: +/// ``` +/// use elf::abi::PT_LOAD; +/// use elf::endian::AnyEndian; +/// use elf::ElfBytes; +/// use elf::segment::ProgramHeader; +/// +/// let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); +/// let file_data = std::fs::read(path).unwrap(); +/// +/// let slice = file_data.as_slice(); +/// let file = ElfBytes::::minimal_parse(slice).unwrap(); +/// +/// // Get all the common ELF sections (if any). We have a lot of ELF work to do! +/// let common_sections = file.find_common_data().unwrap(); +/// // ... do some stuff with the symtab, dynsyms etc +/// +/// // It can also yield iterators on which we can do normal iterator things, like filtering +/// // for all the segments of a specific type. Parsing is done on each iter.next() call, so +/// // if you end iteration early, it won't parse the rest of the table. +/// let first_load_phdr: Option = file.segments().unwrap() +/// .iter() +/// .find(|phdr|{phdr.p_type == PT_LOAD}); +/// println!("First load segment is at: {}", first_load_phdr.unwrap().p_vaddr); +/// +/// // Or if you do things like this to get a vec of only the PT_LOAD segments. +/// let all_load_phdrs: Vec = file.segments().unwrap() +/// .iter() +/// .filter(|phdr|{phdr.p_type == PT_LOAD}) +/// .collect(); +/// println!("There are {} PT_LOAD segments", all_load_phdrs.len()); +/// ``` +#[derive(Debug)] +pub struct ElfBytes<'data, E: EndianParse> { + pub ehdr: FileHeader, + data: &'data [u8], + shdrs: Option>, + phdrs: Option>, +} + +/// Find the location (if any) of the section headers in the given data buffer and take a +/// subslice of their data and wrap it in a lazy-parsing SectionHeaderTable. +/// If shnum > SHN_LORESERVE (0xff00), then this will additionally parse out shdr[0] to calculate +/// the full table size, but all other parsing of SectionHeaders is deferred. +fn find_shdrs<'data, E: EndianParse>( + ehdr: &FileHeader, + data: &'data [u8], +) -> Result>, ParseError> { + // It's Ok to have no section headers + if ehdr.e_shoff == 0 { + return Ok(None); + } + + // If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), + // e_shnum is zero and the actual number of section header table entries + // is contained in the sh_size field of the section header at index 0. + let shoff: usize = ehdr.e_shoff.try_into()?; + let mut shnum = ehdr.e_shnum as usize; + if shnum == 0 { + let mut offset = shoff; + let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?; + shnum = shdr0.sh_size.try_into()?; + } + + // Validate shentsize before trying to read the table so that we can error early for corrupted files + let entsize = SectionHeader::validate_entsize(ehdr.class, ehdr.e_shentsize as usize)?; + + let size = entsize + .checked_mul(shnum) + .ok_or(ParseError::IntegerOverflow)?; + let end = shoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?; + let buf = data.get_bytes(shoff..end)?; + Ok(Some(SectionHeaderTable::new( + ehdr.endianness, + ehdr.class, + buf, + ))) +} + +/// Find the location (if any) of the program headers in the given data buffer and take a +/// subslice of their data and wrap it in a lazy-parsing SegmentTable. +fn find_phdrs<'data, E: EndianParse>( + ehdr: &FileHeader, + data: &'data [u8], +) -> Result>, ParseError> { + // It's Ok to have no program headers + if ehdr.e_phoff == 0 { + return Ok(None); + } + + // If the number of segments is greater than or equal to PN_XNUM (0xffff), + // e_phnum is set to PN_XNUM, and the actual number of program header table + // entries is contained in the sh_info field of the section header at index 0. + let mut phnum = ehdr.e_phnum as usize; + if phnum == abi::PN_XNUM as usize { + let shoff: usize = ehdr.e_shoff.try_into()?; + let mut offset = shoff; + let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?; + phnum = shdr0.sh_info.try_into()?; + } + + // Validate phentsize before trying to read the table so that we can error early for corrupted files + let entsize = ProgramHeader::validate_entsize(ehdr.class, ehdr.e_phentsize as usize)?; + + let phoff: usize = ehdr.e_phoff.try_into()?; + let size = entsize + .checked_mul(phnum) + .ok_or(ParseError::IntegerOverflow)?; + let end = phoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?; + let buf = data.get_bytes(phoff..end)?; + Ok(Some(SegmentTable::new(ehdr.endianness, ehdr.class, buf))) +} + +/// This struct collects the common sections found in ELF objects +#[derive(Debug, Default)] +pub struct CommonElfData<'data, E: EndianParse> { + /// .symtab section + pub symtab: Option>, + /// strtab for .symtab + pub symtab_strs: Option>, + + /// .dynsym section + pub dynsyms: Option>, + /// strtab for .dynsym + pub dynsyms_strs: Option>, + + /// .dynamic section or PT_DYNAMIC segment (both point to the same table) + pub dynamic: Option>, + + /// .hash section + pub sysv_hash: Option>, + + /// .gnu.hash section + pub gnu_hash: Option>, +} + +impl<'data, E: EndianParse> ElfBytes<'data, E> { + /// Do the minimal parsing work to get an [ElfBytes] handle from a byte slice containing an ELF object. + /// + /// This parses the ELF [FileHeader], and locates (but does not parse) the + /// Section Header Table and Segment Table. + /// + // N.B. I thought about calling this "sparse_parse", but it felt too silly for a serious lib like this + pub fn minimal_parse(data: &'data [u8]) -> Result { + let ident_buf = data.get_bytes(0..abi::EI_NIDENT)?; + let ident = parse_ident(ident_buf)?; + + let tail_start = abi::EI_NIDENT; + let tail_end = match ident.1 { + Class::ELF32 => tail_start + crate::file::ELF32_EHDR_TAILSIZE, + Class::ELF64 => tail_start + crate::file::ELF64_EHDR_TAILSIZE, + }; + let tail_buf = data.get_bytes(tail_start..tail_end)?; + + let ehdr = FileHeader::parse_tail(ident, tail_buf)?; + + let shdrs = find_shdrs(&ehdr, data)?; + let phdrs = find_phdrs(&ehdr, data)?; + Ok(ElfBytes { + ehdr, + data, + shdrs, + phdrs, + }) + } + + /// Get this Elf object's zero-alloc lazy-parsing [SegmentTable] (if any). + /// + /// This table parses [ProgramHeader]s on demand and does not make any internal heap allocations + /// when parsing. + pub fn segments(&self) -> Option> { + self.phdrs + } + + /// Get this Elf object's zero-alloc lazy-parsing [SectionHeaderTable] (if any). + /// + /// This table parses [SectionHeader]s on demand and does not make any internal heap allocations + /// when parsing. + pub fn section_headers(&self) -> Option> { + self.shdrs + } + + /// Get this ELF object's [SectionHeaderTable] alongside its corresponding [StringTable]. + /// + /// This is useful if you want to know the string name of sections. + /// + /// Example usage: + /// ``` + /// use std::collections::HashMap; + /// use elf::endian::AnyEndian; + /// use elf::ElfBytes; + /// use elf::note::Note; + /// use elf::note::NoteGnuBuildId; + /// use elf::section::SectionHeader; + /// + /// let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); + /// let file_data = std::fs::read(path).unwrap(); + /// + /// let slice = file_data.as_slice(); + /// let file = ElfBytes::::minimal_parse(slice).unwrap(); + /// + /// // Get the section header table alongside its string table + /// let (shdrs_opt, strtab_opt) = file + /// .section_headers_with_strtab() + /// .expect("shdrs offsets should be valid"); + /// let (shdrs, strtab) = ( + /// shdrs_opt.expect("Should have shdrs"), + /// strtab_opt.expect("Should have strtab") + /// ); + /// + /// // Parse the shdrs and collect them into a map keyed on their zero-copied name + /// let with_names: HashMap<&str, SectionHeader> = shdrs + /// .iter() + /// .map(|shdr| { + /// ( + /// strtab.get(shdr.sh_name as usize).expect("Failed to get section name"), + /// shdr, + /// ) + /// }) + /// .collect(); + /// + /// // Get the zero-copy parsed type for the the build id note + /// let build_id_note_shdr: &SectionHeader = with_names + /// .get(".note.gnu.build-id") + /// .expect("Should have build id note section"); + /// let notes: Vec<_> = file + /// .section_data_as_notes(build_id_note_shdr) + /// .expect("Should be able to get note section data") + /// .collect(); + /// println!("{:?}", notes[0]); + /// ``` + pub fn section_headers_with_strtab( + &self, + ) -> Result< + ( + Option>, + Option>, + ), + ParseError, + > { + // It's Ok to have no section headers + let shdrs = match self.section_headers() { + Some(shdrs) => shdrs, + None => { + return Ok((None, None)); + } + }; + + // It's Ok to not have a string table + if self.ehdr.e_shstrndx == abi::SHN_UNDEF { + return Ok((Some(shdrs), None)); + } + + // If the section name string table section index is greater than or + // equal to SHN_LORESERVE (0xff00), e_shstrndx has the value SHN_XINDEX + // (0xffff) and the actual index of the section name string table section + // is contained in the sh_link field of the section header at index 0. + let mut shstrndx = self.ehdr.e_shstrndx as usize; + if self.ehdr.e_shstrndx == abi::SHN_XINDEX { + let shdr_0 = shdrs.get(0)?; + shstrndx = shdr_0.sh_link as usize; + } + + let strtab = shdrs.get(shstrndx)?; + let (strtab_start, strtab_end) = strtab.get_data_range()?; + let strtab_buf = self.data.get_bytes(strtab_start..strtab_end)?; + Ok((Some(shdrs), Some(StringTable::new(strtab_buf)))) + } + + /// Parse section headers until one is found with the given name + /// + /// Example to get the ELF file's ABI-tag note + /// ``` + /// use elf::ElfBytes; + /// use elf::endian::AnyEndian; + /// use elf::section::SectionHeader; + /// use elf::note::Note; + /// use elf::note::NoteGnuAbiTag; + /// + /// let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + /// let file_data = std::fs::read(path).unwrap(); + /// let slice = file_data.as_slice(); + /// let file = ElfBytes::::minimal_parse(slice).unwrap(); + /// + /// let shdr: SectionHeader = file + /// .section_header_by_name(".note.ABI-tag") + /// .expect("section table should be parseable") + /// .expect("file should have a .note.ABI-tag section"); + /// + /// let notes: Vec<_> = file + /// .section_data_as_notes(&shdr) + /// .expect("Should be able to get note section data") + /// .collect(); + /// assert_eq!( + /// notes[0], + /// Note::GnuAbiTag(NoteGnuAbiTag { + /// os: 0, + /// major: 2, + /// minor: 6, + /// subminor: 32 + /// })); + /// ``` + pub fn section_header_by_name(&self, name: &str) -> Result, ParseError> { + let (shdrs, strtab) = match self.section_headers_with_strtab()? { + (Some(shdrs), Some(strtab)) => (shdrs, strtab), + _ => { + // If we don't have shdrs, or don't have a strtab, we can't find a section by its name + return Ok(None); + } + }; + + Ok(shdrs.iter().find(|shdr| { + let sh_name = match strtab.get(shdr.sh_name as usize) { + Ok(name) => name, + _ => { + return false; + } + }; + name == sh_name + })) + } + + /// Efficiently locate the set of common sections found in ELF files by doing a single iteration + /// over the SectionHeaders table. + /// + /// This is useful for those who know they're going to be accessing multiple common sections, like + /// symbol tables, string tables. Many of these can also be accessed by the more targeted + /// helpers like [ElfBytes::symbol_table] or [ElfBytes::dynamic], though those each do their own + /// internal searches through the shdrs to find the section. + pub fn find_common_data(&self) -> Result, ParseError> { + let mut result: CommonElfData<'data, E> = CommonElfData::default(); + + // Iterate once over the shdrs to collect up any known sections + if let Some(shdrs) = self.shdrs { + for shdr in shdrs.iter() { + match shdr.sh_type { + abi::SHT_SYMTAB => { + let strtab_shdr = shdrs.get(shdr.sh_link as usize)?; + let (symtab, strtab) = + self.section_data_as_symbol_table(&shdr, &strtab_shdr)?; + + result.symtab = Some(symtab); + result.symtab_strs = Some(strtab); + } + abi::SHT_DYNSYM => { + let strtab_shdr = shdrs.get(shdr.sh_link as usize)?; + let (symtab, strtab) = + self.section_data_as_symbol_table(&shdr, &strtab_shdr)?; + + result.dynsyms = Some(symtab); + result.dynsyms_strs = Some(strtab); + } + abi::SHT_DYNAMIC => { + result.dynamic = Some(self.section_data_as_dynamic(&shdr)?); + } + abi::SHT_HASH => { + let (start, end) = shdr.get_data_range()?; + let buf = self.data.get_bytes(start..end)?; + result.sysv_hash = Some(SysVHashTable::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + )?); + } + abi::SHT_GNU_HASH => { + let (start, end) = shdr.get_data_range()?; + let buf = self.data.get_bytes(start..end)?; + result.gnu_hash = Some(GnuHashTable::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + )?); + } + _ => { + continue; + } + } + } + } + + // If we didn't find SHT_DYNAMIC from the section headers, try the program headers + if result.dynamic.is_none() { + if let Some(phdrs) = self.phdrs { + if let Some(dyn_phdr) = phdrs.iter().find(|phdr| phdr.p_type == abi::PT_DYNAMIC) { + let (start, end) = dyn_phdr.get_file_data_range()?; + let buf = self.data.get_bytes(start..end)?; + result.dynamic = Some(DynamicTable::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + )); + } + } + } + + Ok(result) + } + + /// Get the section data for a given [SectionHeader], alongside an optional compression context. + /// + /// This library does not do any decompression for the user, but merely returns the raw compressed + /// section data if the section is compressed alongside its ELF compression structure describing the + /// compression algorithm used. + /// + /// Users who wish to work with compressed sections must pick their compression library of choice + /// and do the decompression themselves. The only two options supported by the ELF spec for section + /// compression are: [abi::ELFCOMPRESS_ZLIB] and [abi::ELFCOMPRESS_ZSTD]. + pub fn section_data( + &self, + shdr: &SectionHeader, + ) -> Result<(&'data [u8], Option), ParseError> { + if shdr.sh_type == abi::SHT_NOBITS { + return Ok((&[], None)); + } + + let (start, end) = shdr.get_data_range()?; + let buf = self.data.get_bytes(start..end)?; + + if shdr.sh_flags & abi::SHF_COMPRESSED as u64 == 0 { + Ok((buf, None)) + } else { + let mut offset = 0; + let chdr = CompressionHeader::parse_at( + self.ehdr.endianness, + self.ehdr.class, + &mut offset, + buf, + )?; + let compressed_buf = buf.get(offset..).ok_or(ParseError::SliceReadError(( + offset, + shdr.sh_size.try_into()?, + )))?; + Ok((compressed_buf, Some(chdr))) + } + } + + /// Get the section data for a given [SectionHeader], and interpret it as a [StringTable] + /// + /// Returns a ParseError if the section is not of type [abi::SHT_STRTAB] + pub fn section_data_as_strtab( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_STRTAB { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_STRTAB, + ))); + } + + let (buf, _) = self.section_data(shdr)?; + Ok(StringTable::new(buf)) + } + + /// Get the section data for a given [SectionHeader], and interpret it as an + /// iterator over no-addend relocations [Rel](crate::relocation::Rel) + /// + /// Returns a ParseError if the section is not of type [abi::SHT_REL] + pub fn section_data_as_rels( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_REL { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_REL, + ))); + } + + let (buf, _) = self.section_data(shdr)?; + Ok(RelIterator::new(self.ehdr.endianness, self.ehdr.class, buf)) + } + + /// Get the section data for a given [SectionHeader], and interpret it as an + /// iterator over relocations with addends [Rela](crate::relocation::Rela) + /// + /// Returns a ParseError if the section is not of type [abi::SHT_RELA] + pub fn section_data_as_relas( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_RELA { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_RELA, + ))); + } + + let (buf, _) = self.section_data(shdr)?; + Ok(RelaIterator::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + )) + } + + /// Get the section data for a given [SectionHeader], and interpret it as an + /// iterator over [Note](crate::note::Note)s + /// + /// Returns a ParseError if the section is not of type [abi::SHT_NOTE] + pub fn section_data_as_notes( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_NOTE { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_NOTE, + ))); + } + + let (buf, _) = self.section_data(shdr)?; + Ok(NoteIterator::new( + self.ehdr.endianness, + self.ehdr.class, + shdr.sh_addralign as usize, + buf, + )) + } + + /// Internal helper to get the section data for an SHT_DYNAMIC section as a .dynamic section table. + /// See [ElfBytes::dynamic] or [ElfBytes::find_common_data] for the public interface + fn section_data_as_dynamic( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_DYNAMIC { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_DYNAMIC, + ))); + } + + // Validate entsize before trying to read the table so that we can error early for corrupted files + Dyn::validate_entsize(self.ehdr.class, shdr.sh_entsize.try_into()?)?; + let (buf, _) = self.section_data(shdr)?; + Ok(DynamicTable::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + )) + } + + /// Get the segment's file data for a given segment/[ProgramHeader]. + /// + /// This is the segment's data as found in the file. + pub fn segment_data(&self, phdr: &ProgramHeader) -> Result<&'data [u8], ParseError> { + let (start, end) = phdr.get_file_data_range()?; + self.data.get_bytes(start..end) + } + + /// Get the segment's file data for a given [ProgramHeader], and interpret it as an + /// iterator over [Note](crate::note::Note)s + /// + /// Returns a ParseError if the section is not of type [abi::PT_NOTE] + pub fn segment_data_as_notes( + &self, + phdr: &ProgramHeader, + ) -> Result, ParseError> { + if phdr.p_type != abi::PT_NOTE { + return Err(ParseError::UnexpectedSegmentType(( + phdr.p_type, + abi::PT_NOTE, + ))); + } + + let buf = self.segment_data(phdr)?; + Ok(NoteIterator::new( + self.ehdr.endianness, + self.ehdr.class, + phdr.p_align as usize, + buf, + )) + } + + /// Get the .dynamic section or [abi::PT_DYNAMIC] segment contents. + pub fn dynamic(&self) -> Result>, ParseError> { + // If we have section headers, look for the SHT_DYNAMIC section + if let Some(shdrs) = self.section_headers() { + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == abi::SHT_DYNAMIC) { + return Ok(Some(self.section_data_as_dynamic(&shdr)?)); + } + // Otherwise, look up the PT_DYNAMIC segment (if any) + } else if let Some(phdrs) = self.segments() { + if let Some(phdr) = phdrs.iter().find(|phdr| phdr.p_type == abi::PT_DYNAMIC) { + let (start, end) = phdr.get_file_data_range()?; + let buf = self.data.get_bytes(start..end)?; + return Ok(Some(DynamicTable::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + ))); + } + } + + Ok(None) + } + + /// Helper method to get the section data for a given pair of [SectionHeader] for the symbol + /// table and its linked strtab, and interpret them as [SymbolTable] and [StringTable]. + fn section_data_as_symbol_table( + &self, + shdr: &SectionHeader, + strtab_shdr: &SectionHeader, + ) -> Result<(SymbolTable<'data, E>, StringTable<'data>), ParseError> { + // Validate entsize before trying to read the table so that we can error early for corrupted files + Symbol::validate_entsize(self.ehdr.class, shdr.sh_entsize.try_into()?)?; + + // Load the section bytes for the symtab + // (we want immutable references to both the symtab and its strtab concurrently) + let (symtab_start, symtab_end) = shdr.get_data_range()?; + let symtab_buf = self.data.get_bytes(symtab_start..symtab_end)?; + + // Load the section bytes for the strtab + // (we want immutable references to both the symtab and its strtab concurrently) + let (strtab_start, strtab_end) = strtab_shdr.get_data_range()?; + let strtab_buf = self.data.get_bytes(strtab_start..strtab_end)?; + + let symtab = SymbolTable::new(self.ehdr.endianness, self.ehdr.class, symtab_buf); + let strtab = StringTable::new(strtab_buf); + Ok((symtab, strtab)) + } + + /// Get the ELF file's `.symtab` and associated strtab (if any) + pub fn symbol_table( + &self, + ) -> Result, StringTable<'data>)>, ParseError> { + let shdrs = match self.section_headers() { + Some(shdrs) => shdrs, + None => { + return Ok(None); + } + }; + + // Get the symtab header for the symtab. The GABI states there can be zero or one per ELF file. + let symtab_shdr = match shdrs.iter().find(|shdr| shdr.sh_type == abi::SHT_SYMTAB) { + Some(shdr) => shdr, + None => { + return Ok(None); + } + }; + + let strtab_shdr = shdrs.get(symtab_shdr.sh_link as usize)?; + Ok(Some(self.section_data_as_symbol_table( + &symtab_shdr, + &strtab_shdr, + )?)) + } + + /// Get the ELF file's `.dynsym` and associated strtab (if any) + pub fn dynamic_symbol_table( + &self, + ) -> Result, StringTable<'data>)>, ParseError> { + let shdrs = match self.section_headers() { + Some(shdrs) => shdrs, + None => { + return Ok(None); + } + }; + + // Get the symtab header for the symtab. The GABI states there can be zero or one per ELF file. + let symtab_shdr = match shdrs.iter().find(|shdr| shdr.sh_type == abi::SHT_DYNSYM) { + Some(shdr) => shdr, + None => { + return Ok(None); + } + }; + + let strtab_shdr = shdrs.get(symtab_shdr.sh_link as usize)?; + Ok(Some(self.section_data_as_symbol_table( + &symtab_shdr, + &strtab_shdr, + )?)) + } + + /// Locate the section data for the various GNU Symbol Versioning sections (if any) + /// and return them in a [SymbolVersionTable] that which can interpret them in-place to + /// yield [SymbolRequirement](crate::gnu_symver::SymbolRequirement)s + /// and [SymbolDefinition](crate::gnu_symver::SymbolDefinition)s + /// + /// This is a GNU extension and not all objects use symbol versioning. + /// Returns an empty Option if the object does not use symbol versioning. + pub fn symbol_version_table(&self) -> Result>, ParseError> { + // No sections means no GNU symbol versioning sections, which is ok + let shdrs = match self.section_headers() { + Some(shdrs) => shdrs, + None => { + return Ok(None); + } + }; + + let mut versym_opt: Option = None; + let mut needs_opt: Option = None; + let mut defs_opt: Option = None; + // Find the GNU Symbol versioning sections (if any) + for shdr in shdrs.iter() { + if shdr.sh_type == abi::SHT_GNU_VERSYM { + versym_opt = Some(shdr); + } else if shdr.sh_type == abi::SHT_GNU_VERNEED { + needs_opt = Some(shdr); + } else if shdr.sh_type == abi::SHT_GNU_VERDEF { + defs_opt = Some(shdr); + } + + // If we've found all three sections, then we're done + if versym_opt.is_some() && needs_opt.is_some() && defs_opt.is_some() { + break; + } + } + + let versym_shdr = match versym_opt { + Some(shdr) => shdr, + // No VERSYM section means the object doesn't use symbol versioning, which is ok. + None => { + return Ok(None); + } + }; + + // Load the versym table + // Validate VERSYM entsize before trying to read the table so that we can error early for corrupted files + VersionIndex::validate_entsize(self.ehdr.class, versym_shdr.sh_entsize.try_into()?)?; + let (versym_start, versym_end) = versym_shdr.get_data_range()?; + let version_ids = VersionIndexTable::new( + self.ehdr.endianness, + self.ehdr.class, + self.data.get_bytes(versym_start..versym_end)?, + ); + + // Wrap the VERNEED section and strings data in an iterator and string table (if any) + let verneeds = match needs_opt { + Some(shdr) => { + let (start, end) = shdr.get_data_range()?; + let needs_buf = self.data.get_bytes(start..end)?; + + let strs_shdr = shdrs.get(shdr.sh_link as usize)?; + let (strs_start, strs_end) = strs_shdr.get_data_range()?; + let strs_buf = self.data.get_bytes(strs_start..strs_end)?; + + Some(( + VerNeedIterator::new( + self.ehdr.endianness, + self.ehdr.class, + shdr.sh_info as u64, + 0, + needs_buf, + ), + StringTable::new(strs_buf), + )) + } + // It's possible to have symbol versioning with no NEEDs if we're an object that only + // exports defined symbols. + None => None, + }; + + // Wrap the VERDEF section and strings data in an iterator and string table (if any) + let verdefs = match defs_opt { + Some(shdr) => { + let (start, end) = shdr.get_data_range()?; + let defs_buf = self.data.get_bytes(start..end)?; + + let strs_shdr = shdrs.get(shdr.sh_link as usize)?; + let (strs_start, strs_end) = strs_shdr.get_data_range()?; + let strs_buf = self.data.get_bytes(strs_start..strs_end)?; + + Some(( + VerDefIterator::new( + self.ehdr.endianness, + self.ehdr.class, + shdr.sh_info as u64, + 0, + defs_buf, + ), + StringTable::new(strs_buf), + )) + } + // It's possible to have symbol versioning with no NEEDs if we're an object that only + // exports defined symbols. + None => None, + }; + + // whew, we're done here! + Ok(Some(SymbolVersionTable::new( + version_ids, + verneeds, + verdefs, + ))) + } +} + +// _ _ +// | |_ ___ ___| |_ ___ +// | __/ _ \/ __| __/ __| +// | || __/\__ \ |_\__ \ +// \__\___||___/\__|___/ +// + +#[cfg(test)] +mod interface_tests { + use super::*; + use crate::abi::{SHT_GNU_HASH, SHT_NOBITS, SHT_NOTE, SHT_NULL, SHT_REL, SHT_RELA, SHT_STRTAB}; + use crate::dynamic::Dyn; + use crate::endian::AnyEndian; + use crate::hash::sysv_hash; + use crate::note::{Note, NoteGnuAbiTag, NoteGnuBuildId}; + use crate::relocation::Rela; + use crate::segment::ProgramHeader; + + #[test] + fn simultaenous_segments_parsing() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + // With the bytes interface, we should be able to get multiple lazy-parsing types concurrently, + // since the trait is implemented for shared references. + // + // Get the segment table + let iter = file.segments().expect("File should have a segment table"); + + // Concurrently get the segment table again as an iterator and collect the headers into a vec + let segments: Vec = file + .segments() + .expect("File should have a segment table") + .iter() + .collect(); + + let expected_phdr = ProgramHeader { + p_type: abi::PT_PHDR, + p_offset: 64, + p_vaddr: 4194368, + p_paddr: 4194368, + p_filesz: 448, + p_memsz: 448, + p_flags: 5, + p_align: 8, + }; + + // Assert we parsed the first header correctly + assert_eq!(segments[0], expected_phdr); + + // Now use the original lazy-parsing table to parse out the first entry + assert_eq!( + iter.get(0).expect("should be able to parse phdr"), + expected_phdr + ) + } + + #[test] + fn segments() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let segments: Vec = file + .segments() + .expect("File should have a segment table") + .iter() + .collect(); + assert_eq!( + segments[0], + ProgramHeader { + p_type: abi::PT_PHDR, + p_offset: 64, + p_vaddr: 4194368, + p_paddr: 4194368, + p_filesz: 448, + p_memsz: 448, + p_flags: 5, + p_align: 8, + } + ); + } + + #[test] + fn segments_phnum_in_shdr0() { + let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let segments: Vec = file + .segments() + .expect("File should have a segment table") + .iter() + .collect(); + assert_eq!( + segments[0], + ProgramHeader { + p_type: abi::PT_PHDR, + p_offset: 92, + p_vaddr: 0, + p_paddr: 0, + p_filesz: 32, + p_memsz: 32, + p_flags: 0x20003, + p_align: 0x40000, + } + ); + } + + #[test] + fn section_headers() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let shdrs = file + .section_headers() + .expect("File should have a section table"); + + let shdrs_vec: Vec = shdrs.iter().collect(); + + assert_eq!(shdrs_vec[4].sh_type, SHT_GNU_HASH); + } + + #[test] + fn section_headers_with_strtab() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let (shdrs, strtab) = file + .section_headers_with_strtab() + .expect("shdrs should be parsable"); + let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap()); + + let with_names: Vec<(&str, SectionHeader)> = shdrs + .iter() + .map(|shdr| { + ( + strtab + .get(shdr.sh_name as usize) + .expect("Failed to get section name"), + shdr, + ) + }) + .collect(); + + let (name, shdr) = with_names[4]; + assert_eq!(name, ".gnu.hash"); + assert_eq!(shdr.sh_type, abi::SHT_GNU_HASH); + } + + #[test] + fn shnum_and_shstrndx_in_shdr0() { + let path = std::path::PathBuf::from("sample-objects/shnum.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).unwrap(); + + let (shdrs, strtab) = file + .section_headers_with_strtab() + .expect("shdrs should be parsable"); + let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap()); + + let shdrs_len = shdrs.len(); + assert_eq!(shdrs_len, 0xFF15); + + let shdr = shdrs.get(shdrs_len - 1).unwrap(); + let name = strtab + .get(shdr.sh_name as usize) + .expect("Failed to get section name"); + + assert_eq!(name, ".shstrtab"); + assert_eq!(shdr.sh_type, abi::SHT_STRTAB); + } + + #[test] + fn section_header_by_name() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let shdr = file + .section_header_by_name(".gnu.hash") + .expect("section table should be parseable") + .expect("file should have .gnu.hash section"); + + assert_eq!(shdr.sh_type, SHT_GNU_HASH); + + let shdr = file + .section_header_by_name(".not.found") + .expect("section table should be parseable"); + + assert_eq!(shdr, None); + } + + #[test] + fn find_common_data() { + let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let elf_scns = file.find_common_data().expect("file should parse"); + + // hello.so should find everything + assert!(elf_scns.symtab.is_some()); + assert!(elf_scns.symtab_strs.is_some()); + assert!(elf_scns.dynsyms.is_some()); + assert!(elf_scns.dynsyms_strs.is_some()); + assert!(elf_scns.dynamic.is_some()); + assert!(elf_scns.sysv_hash.is_some()); + assert!(elf_scns.gnu_hash.is_some()); + } + + #[test] + fn section_data() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let shdr = file + .section_headers() + .expect("File should have section table") + .get(26) + .expect("shdr should be parsable"); + + assert_eq!(shdr.sh_type, SHT_NOBITS); + + let (data, chdr) = file + .section_data(&shdr) + .expect("Failed to get section data"); + + assert_eq!(chdr, None); + assert_eq!(data, &[]); + } + + // Test all the different section_data_as* with a section of the wrong type + #[test] + fn section_data_as_wrong_type() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + // Section 0 is SHT_NULL, so all of the section_data_as* should error on it + let shdr = file + .section_headers() + .expect("File should have section table") + .get(0) + .expect("shdr should be parsable"); + + let err = file + .section_data_as_strtab(&shdr) + .expect_err("shdr0 should be the wrong type"); + assert!( + matches!( + err, + ParseError::UnexpectedSectionType((SHT_NULL, SHT_STRTAB)) + ), + "Unexpected Error type found: {err}" + ); + + let err = file + .section_data_as_rels(&shdr) + .expect_err("shdr0 should be the wrong type"); + assert!( + matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_REL))), + "Unexpected Error type found: {err}" + ); + + let err = file + .section_data_as_relas(&shdr) + .expect_err("shdr0 should be the wrong type"); + assert!( + matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_RELA))), + "Unexpected Error type found: {err}" + ); + + let err = file + .section_data_as_notes(&shdr) + .expect_err("shdr0 should be the wrong type"); + assert!( + matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_NOTE))), + "Unexpected Error type found: {err}" + ); + } + + #[test] + fn section_data_as_strtab() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let shdr = file + .section_headers() + .expect("File should have section table") + .get(file.ehdr.e_shstrndx as usize) + .expect("shdr should be parsable"); + + let strtab = file + .section_data_as_strtab(&shdr) + .expect("Failed to read strtab"); + + assert_eq!( + strtab.get(1).expect("Failed to get strtab entry"), + ".symtab" + ); + } + + #[test] + fn section_data_as_relas() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let shdr = file + .section_headers() + .expect("File should have section table") + .get(10) + .expect("Failed to get rela shdr"); + + let mut relas = file + .section_data_as_relas(&shdr) + .expect("Failed to read relas section"); + assert_eq!( + relas.next().expect("Failed to get rela entry"), + Rela { + r_offset: 6293704, + r_sym: 1, + r_type: 7, + r_addend: 0, + } + ); + assert_eq!( + relas.next().expect("Failed to get rela entry"), + Rela { + r_offset: 6293712, + r_sym: 2, + r_type: 7, + r_addend: 0, + } + ); + assert!(relas.next().is_none()); + } + + #[test] + fn section_data_as_notes() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let shdr = file + .section_headers() + .expect("File should have section table") + .get(2) + .expect("Failed to get note shdr"); + + let mut notes = file + .section_data_as_notes(&shdr) + .expect("Failed to read note section"); + assert_eq!( + notes.next().expect("Failed to get first note"), + Note::GnuAbiTag(NoteGnuAbiTag { + os: 0, + major: 2, + minor: 6, + subminor: 32 + }) + ); + assert!(notes.next().is_none()); + } + + #[test] + fn segment_data_as_notes() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let phdr = file + .segments() + .expect("File should have segmetn table") + .get(5) + .expect("Failed to get notes phdr"); + + let mut notes = file + .segment_data_as_notes(&phdr) + .expect("Failed to read notes segment"); + assert_eq!( + notes.next().expect("Failed to get first note"), + Note::GnuAbiTag(NoteGnuAbiTag { + os: 0, + major: 2, + minor: 6, + subminor: 32 + }) + ); + assert_eq!( + notes.next().expect("Failed to get second note"), + Note::GnuBuildId(NoteGnuBuildId(&[ + 119, 65, 159, 13, 165, 16, 131, 12, 87, 167, 200, 204, 176, 238, 133, 95, 238, 211, + 118, 163 + ])) + ); + assert!(notes.next().is_none()); + } + + #[test] + fn dynamic() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let mut dynamic = file + .dynamic() + .expect("Failed to parse .dynamic") + .expect("Failed to find .dynamic") + .iter(); + assert_eq!( + dynamic.next().expect("Failed to get dyn entry"), + Dyn { + d_tag: abi::DT_NEEDED, + d_un: 1 + } + ); + assert_eq!( + dynamic.next().expect("Failed to get dyn entry"), + Dyn { + d_tag: abi::DT_INIT, + d_un: 4195216 + } + ); + } + + #[test] + fn symbol_table() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let (symtab, strtab) = file + .symbol_table() + .expect("Failed to read symbol table") + .expect("Failed to find symbol table"); + let symbol = symtab.get(30).expect("Failed to get symbol"); + assert_eq!( + symbol, + Symbol { + st_name: 19, + st_value: 6293200, + st_size: 0, + st_shndx: 21, + st_info: 1, + st_other: 0, + } + ); + assert_eq!( + strtab + .get(symbol.st_name as usize) + .expect("Failed to get name from strtab"), + "__JCR_LIST__" + ); + } + + #[test] + fn dynamic_symbol_table() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let (symtab, strtab) = file + .dynamic_symbol_table() + .expect("Failed to read symbol table") + .expect("Failed to find symbol table"); + let symbol = symtab.get(1).expect("Failed to get symbol"); + assert_eq!( + symbol, + Symbol { + st_name: 11, + st_value: 0, + st_size: 0, + st_shndx: 0, + st_info: 18, + st_other: 0, + } + ); + assert_eq!( + strtab + .get(symbol.st_name as usize) + .expect("Failed to get name from strtab"), + "memset" + ); + } + + #[test] + fn symbol_version_table() { + let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + let vst = file + .symbol_version_table() + .expect("Failed to parse GNU symbol versions") + .expect("Failed to find GNU symbol versions"); + + let req = vst + .get_requirement(2) + .expect("Failed to parse NEED") + .expect("Failed to find NEED"); + assert_eq!(req.file, "libc.so.6"); + assert_eq!(req.name, "GLIBC_2.2.5"); + assert_eq!(req.hash, 0x9691A75); + + let req = vst.get_requirement(3).expect("Failed to parse NEED"); + assert!(req.is_none()); + + let req = vst.get_requirement(4).expect("Failed to parse NEED"); + assert!(req.is_none()); + + let req = vst + .get_requirement(5) + .expect("Failed to parse NEED") + .expect("Failed to find NEED"); + assert_eq!(req.file, "libc.so.6"); + assert_eq!(req.name, "GLIBC_2.2.5"); + assert_eq!(req.hash, 0x9691A75); + + let def = vst + .get_definition(3) + .expect("Failed to parse DEF") + .expect("Failed to find DEF"); + assert_eq!(def.hash, 0xC33237F); + assert_eq!(def.flags, 1); + assert!(!def.hidden); + let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect(); + assert_eq!(def_names, &["hello.so"]); + + let def = vst + .get_definition(7) + .expect("Failed to parse DEF") + .expect("Failed to find DEF"); + assert_eq!(def.hash, 0x1570B62); + assert_eq!(def.flags, 0); + assert!(def.hidden); + let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect(); + assert_eq!(def_names, &["HELLO_1.42"]); + } + + #[test] + fn sysv_hash_table() { + let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); + + // Look up the SysV hash section header + let common = file.find_common_data().expect("should parse"); + let hash_table = common.sysv_hash.expect("should have .hash section"); + + // Get the dynamic symbol table. + let (symtab, strtab) = file + .dynamic_symbol_table() + .expect("Failed to read symbol table") + .expect("Failed to find symbol table"); + + // Verify that these three symbols all collide in the hash table's buckets + assert_eq!(sysv_hash(b"use_memset_v2"), 0x8080542); + assert_eq!(sysv_hash(b"__gmon_start__"), 0xF4D007F); + assert_eq!(sysv_hash(b"memset"), 0x73C49C4); + assert_eq!(sysv_hash(b"use_memset_v2") % 3, 0); + assert_eq!(sysv_hash(b"__gmon_start__") % 3, 0); + assert_eq!(sysv_hash(b"memset") % 3, 0); + + // Use the hash table to find a given symbol in it. + let (sym_idx, sym) = hash_table + .find(b"memset", &symtab, &strtab) + .expect("Failed to parse hash") + .expect("Failed to find hash"); + + // Verify that we got the same symbol from the hash table we expected + assert_eq!(sym_idx, 2); + assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset"); + assert_eq!( + sym, + symtab.get(sym_idx).expect("Failed to get expected sym") + ); + } + + #[test] + fn gnu_hash_table() { + let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); + let file_data = std::fs::read(path).expect("Could not read file."); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).unwrap(); + + // Look up the SysV hash section header + let common = file.find_common_data().unwrap(); + let hash_table = common.gnu_hash.expect("should have .gnu.hash section"); + + // Get the dynamic symbol table. + let (symtab, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap()); + + // manually look one up by explicit name to make sure the above loop is doing something + let (sym_idx, sym) = hash_table + .find(b"use_memset", &symtab, &strtab) + .expect("Failed to parse hash") + .expect("Failed to find hash"); + + // Verify that we got the same symbol from the hash table we expected + assert_eq!(sym_idx, 9); + assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "use_memset"); + assert_eq!( + sym, + symtab.get(sym_idx).expect("Failed to get expected sym") + ); + } +} + +#[cfg(test)] +mod arch_tests { + use super::*; + use crate::endian::AnyEndian; + + // Basic smoke test which parses out symbols and headers for a given sample object of a given architecture + macro_rules! arch_test { + ( $arch:expr, $e_machine:expr, $endian:expr) => {{ + let path_str = format!("sample-objects/symver.{}.so", $arch); + let path = std::path::PathBuf::from(path_str); + let file_data = std::fs::read(path).expect("file should exist"); + let slice = file_data.as_slice(); + let file = ElfBytes::::minimal_parse(slice).expect("should parse"); + + assert_eq!(file.ehdr.e_machine, $e_machine); + assert_eq!(file.ehdr.endianness, $endian); + + let (shdrs, strtab) = file.section_headers_with_strtab().expect("should parse"); + let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap()); + let _: Vec<_> = shdrs + .iter() + .map(|shdr| { + ( + strtab.get(shdr.sh_name as usize).expect("should parse"), + shdr, + ) + }) + .collect(); + + let common = file.find_common_data().expect("should parse"); + + // parse out all the normal symbol table symbols with their names + { + let symtab = common.symtab.unwrap(); + let strtab = common.symtab_strs.unwrap(); + let _: Vec<_> = symtab + .iter() + .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym)) + .collect(); + } + + // parse out all the dynamic symbols and look them up in the gnu hash table + { + let symtab = common.dynsyms.unwrap(); + let strtab = common.dynsyms_strs.unwrap(); + let symbols_with_names: Vec<_> = symtab + .iter() + .map(|sym| (strtab.get_raw(sym.st_name as usize).expect("should parse"), sym)) + .collect(); + + let hash_table = common.gnu_hash.unwrap(); + + // look up each entry that should be in the hash table and make sure its there + let start_idx = hash_table.hdr.table_start_idx as usize; + for sym_idx in 0..symtab.len() { + let (symbol_name, symbol) = symbols_with_names.get(sym_idx).unwrap(); + + let result = hash_table + .find(symbol_name, &symtab, &strtab) + .expect("Failed to parse hash"); + + if sym_idx < start_idx { + assert_eq!(result, None); + } else { + let (hash_sym_idx, hash_symbol) = result.unwrap(); + + // Verify that we got the same symbol from the hash table we expected + assert_eq!(sym_idx, hash_sym_idx); + assert_eq!( + strtab.get_raw(hash_symbol.st_name as usize).unwrap(), + *symbol_name + ); + assert_eq!(*symbol, hash_symbol); + } + } + } + + let phdrs = file.segments().unwrap(); + let note_phdrs: Vec<_> = phdrs + .iter() + .filter(|phdr| phdr.p_type == abi::PT_NOTE) + .collect(); + for phdr in note_phdrs { + let _: Vec<_> = file + .segment_data_as_notes(&phdr) + .expect("should parse") + .collect(); + } + }}; + } + + #[test] + fn x86_64() { + arch_test!("x86_64", abi::EM_X86_64, AnyEndian::Little); + } + + #[test] + fn m68k() { + arch_test!("m68k", abi::EM_68K, AnyEndian::Big); + } + + #[test] + fn aarch64() { + arch_test!("aarch64", abi::EM_AARCH64, AnyEndian::Little); + } + + #[test] + fn armhf() { + arch_test!("armhf", abi::EM_ARM, AnyEndian::Little); + } + + #[test] + fn powerpc64() { + arch_test!("powerpc64", abi::EM_PPC64, AnyEndian::Big); + } + + #[test] + fn powerpc64le() { + arch_test!("powerpc64le", abi::EM_PPC64, AnyEndian::Little); + } + + #[test] + fn riscv64() { + arch_test!("riscv64", abi::EM_RISCV, AnyEndian::Little); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/elf_stream.rs b/mikros_std_deps/elf-0.7.4/src/elf_stream.rs new file mode 100644 index 00000000000..9b9b661abeb --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/elf_stream.rs @@ -0,0 +1,1280 @@ +use core::ops::Range; +use std::collections::HashMap; +use std::io::{Read, Seek, SeekFrom}; + +use crate::abi; +use crate::compression::CompressionHeader; +use crate::dynamic::DynamicTable; +use crate::endian::EndianParse; +use crate::file::{parse_ident, Class}; +use crate::gnu_symver::{ + SymbolVersionTable, VerDefIterator, VerNeedIterator, VersionIndex, VersionIndexTable, +}; +use crate::note::NoteIterator; +use crate::parse::{ParseAt, ParseError}; +use crate::relocation::{RelIterator, RelaIterator}; +use crate::section::{SectionHeader, SectionHeaderTable}; +use crate::segment::ProgramHeader; +use crate::segment::SegmentTable; +use crate::string_table::StringTable; +use crate::symbol::{Symbol, SymbolTable}; + +use crate::file::FileHeader; + +/// This type encapsulates the stream-oriented interface for parsing ELF objects from +/// a `Read + Seek`. +#[derive(Debug)] +pub struct ElfStream { + pub ehdr: FileHeader, + shdrs: Vec, + phdrs: Vec, + reader: CachingReader, +} + +/// Read the stream bytes backing the section headers table and parse them all into their Rust native type. +/// +/// Returns a [ParseError] if the data bytes for the section table cannot be read. +/// i.e. if the ELF [FileHeader]'s e_shnum, e_shoff, e_shentsize are invalid and point +/// to a range in the file data that does not actually exist, or if any of the headers failed to parse. +fn parse_section_headers( + ehdr: &FileHeader, + reader: &mut CachingReader, +) -> Result, ParseError> { + // It's Ok to have no section headers + if ehdr.e_shoff == 0 { + return Ok(Vec::default()); + } + + // Validate shentsize before trying to read the table so that we can error early for corrupted files + let entsize = SectionHeader::validate_entsize(ehdr.class, ehdr.e_shentsize as usize)?; + + // If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), + // e_shnum is zero and the actual number of section header table entries + // is contained in the sh_size field of the section header at index 0. + let shoff: usize = ehdr.e_shoff.try_into()?; + let mut shnum = ehdr.e_shnum as usize; + if shnum == 0 { + let end = shoff + .checked_add(entsize) + .ok_or(ParseError::IntegerOverflow)?; + let mut offset = 0; + let data = reader.read_bytes(shoff, end)?; + let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?; + shnum = shdr0.sh_size.try_into()?; + } + + let size = entsize + .checked_mul(shnum) + .ok_or(ParseError::IntegerOverflow)?; + let end = shoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?; + let buf = reader.read_bytes(shoff, end)?; + let shdr_vec = SectionHeaderTable::new(ehdr.endianness, ehdr.class, buf) + .iter() + .collect(); + Ok(shdr_vec) +} + +fn parse_program_headers( + ehdr: &FileHeader, + reader: &mut CachingReader, +) -> Result, ParseError> { + // It's Ok to have no program headers + if ehdr.e_phoff == 0 { + return Ok(Vec::default()); + } + + // If the number of segments is greater than or equal to PN_XNUM (0xffff), + // e_phnum is set to PN_XNUM, and the actual number of program header table + // entries is contained in the sh_info field of the section header at index 0. + let mut phnum = ehdr.e_phnum as usize; + if phnum == abi::PN_XNUM as usize { + let shoff: usize = ehdr.e_shoff.try_into()?; + let end = shoff + .checked_add(SectionHeader::size_for(ehdr.class)) + .ok_or(ParseError::IntegerOverflow)?; + let data = reader.read_bytes(shoff, end)?; + let mut offset = 0; + let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?; + phnum = shdr0.sh_info.try_into()?; + } + + // Validate phentsize before trying to read the table so that we can error early for corrupted files + let entsize = ProgramHeader::validate_entsize(ehdr.class, ehdr.e_phentsize as usize)?; + + let phoff: usize = ehdr.e_phoff.try_into()?; + let size = entsize + .checked_mul(phnum) + .ok_or(ParseError::IntegerOverflow)?; + let end = phoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?; + let buf = reader.read_bytes(phoff, end)?; + let phdrs_vec = SegmentTable::new(ehdr.endianness, ehdr.class, buf) + .iter() + .collect(); + Ok(phdrs_vec) +} + +impl ElfStream { + /// Do a minimal amount of parsing work to open an [ElfStream] handle from a Read+Seek containing an ELF object. + /// + /// This parses the ELF [FileHeader], [SectionHeader] table, and [ProgramHeader] (segments) table. + /// All other file data (section data, segment data) is left unread and unparsed. + pub fn open_stream(reader: S) -> Result, ParseError> { + let mut cr = CachingReader::new(reader)?; + let ident_buf = cr.read_bytes(0, abi::EI_NIDENT)?; + let ident = parse_ident(ident_buf)?; + + let tail_start = abi::EI_NIDENT; + let tail_end = match ident.1 { + Class::ELF32 => tail_start + crate::file::ELF32_EHDR_TAILSIZE, + Class::ELF64 => tail_start + crate::file::ELF64_EHDR_TAILSIZE, + }; + let tail_buf = cr.read_bytes(tail_start, tail_end)?; + + let ehdr = FileHeader::parse_tail(ident, tail_buf)?; + + let shdrs = parse_section_headers(&ehdr, &mut cr)?; + let phdrs = parse_program_headers(&ehdr, &mut cr)?; + + // We parsed out the ehdr and shdrs into their own allocated containers, so there's no need to keep + // around their backing data anymore. + cr.clear_cache(); + + Ok(ElfStream { + ehdr, + shdrs, + phdrs, + reader: cr, + }) + } + + /// Get the parsed section headers table + pub fn segments(&self) -> &Vec { + &self.phdrs + } + + /// Get the parsed section headers table + pub fn section_headers(&self) -> &Vec { + &self.shdrs + } + + /// Get an lazy-parsing table for the Section Headers in the file and its associated StringTable. + /// + /// The underlying ELF bytes backing the section headers table and string + /// table are read all at once when the table is requested, but parsing is + /// deferred to be lazily parsed on demand on each table.get(), strtab.get(), or + /// table.iter().next() call. + /// + /// Returns a [ParseError] if the data bytes for these tables cannot be + /// read i.e. if the ELF [FileHeader]'s + /// [e_shnum](FileHeader#structfield.e_shnum), + /// [e_shoff](FileHeader#structfield.e_shoff), + /// [e_shentsize](FileHeader#structfield.e_shentsize), + /// [e_shstrndx](FileHeader#structfield.e_shstrndx) are invalid and point + /// to a ranges in the file data that does not actually exist. + pub fn section_headers_with_strtab( + &mut self, + ) -> Result<(&Vec, Option>), ParseError> { + // It's Ok to have no section headers + if self.shdrs.is_empty() { + return Ok((&self.shdrs, None)); + } + + // It's Ok to not have a string table + if self.ehdr.e_shstrndx == abi::SHN_UNDEF { + return Ok((&self.shdrs, None)); + } + + // If the section name string table section index is greater than or + // equal to SHN_LORESERVE (0xff00), e_shstrndx has the value SHN_XINDEX + // (0xffff) and the actual index of the section name string table section + // is contained in the sh_link field of the section header at index 0. + let mut shstrndx = self.ehdr.e_shstrndx as usize; + if self.ehdr.e_shstrndx == abi::SHN_XINDEX { + shstrndx = self.shdrs[0].sh_link as usize; + } + + // We have a strtab, so wrap it in a zero-copy StringTable + let strtab = self + .shdrs + .get(shstrndx) + .ok_or(ParseError::BadOffset(shstrndx as u64))?; + let (strtab_start, strtab_end) = strtab.get_data_range()?; + let strtab_buf = self.reader.read_bytes(strtab_start, strtab_end)?; + let strtab = StringTable::new(strtab_buf); + Ok((&self.shdrs, Some(strtab))) + } + + /// Find the parsed section header with the given name (if any). + /// + /// Returns a ParseError if the section headers string table can't be read + /// + /// Example to get the ELF file's ABI-tag note + /// ``` + /// use elf::ElfStream; + /// use elf::endian::AnyEndian; + /// use elf::section::SectionHeader; + /// use elf::note::Note; + /// use elf::note::NoteGnuAbiTag; + /// + /// let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + /// let io = std::fs::File::open(path).expect("Could not open file."); + /// let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + /// let shdr: SectionHeader = *file + /// .section_header_by_name(".note.ABI-tag") + /// .expect("section table should be parseable") + /// .expect("file should have a .note.ABI-tag section"); + /// + /// let notes: Vec<_> = file + /// .section_data_as_notes(&shdr) + /// .expect("Should be able to get note section data") + /// .collect(); + /// assert_eq!( + /// notes[0], + /// Note::GnuAbiTag(NoteGnuAbiTag { + /// os: 0, + /// major: 2, + /// minor: 6, + /// subminor: 32 + /// })); + /// ``` + pub fn section_header_by_name( + &mut self, + name: &str, + ) -> Result, ParseError> { + let (shdrs, strtab) = match self.section_headers_with_strtab()? { + (shdr, Some(strtab)) => (shdr, strtab), + // We can't look up shdrs by name if there's no strtab. + // (hint: try looking it up by its sh_type). + _ => { + return Ok(None); + } + }; + + Ok(shdrs.iter().find(|shdr| { + let sh_name = match strtab.get(shdr.sh_name as usize) { + Ok(name) => name, + _ => { + return false; + } + }; + name == sh_name + })) + } + + /// Read the section data for the given [SectionHeader](SectionHeader). + /// Returns both the secion data and an optional CompressionHeader. + /// + /// No compression header signals that the section contents are uncompressed and can be used as-is. + /// + /// Some(chdr) signals that the section contents are compressed and need to be uncompressed via the + /// compression algorithm described in [ch_type](CompressionHeader#structfield.ch_type). + /// The returned buffer represents the compressed section bytes as found in the file, without the + /// CompressionHeader. + /// + /// It is up to the user to perform the decompression themselves with the compression library of + /// their choosing. + /// + /// SHT_NOBITS sections yield an empty slice. + pub fn section_data( + &mut self, + shdr: &SectionHeader, + ) -> Result<(&[u8], Option), ParseError> { + if shdr.sh_type == abi::SHT_NOBITS { + return Ok((&[], None)); + } + + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + + if shdr.sh_flags & abi::SHF_COMPRESSED as u64 == 0 { + Ok((buf, None)) + } else { + let mut offset = 0; + let chdr = CompressionHeader::parse_at( + self.ehdr.endianness, + self.ehdr.class, + &mut offset, + buf, + )?; + let compressed_buf = buf.get(offset..).ok_or(ParseError::SliceReadError(( + offset, + shdr.sh_size.try_into()?, + )))?; + Ok((compressed_buf, Some(chdr))) + } + } + + /// Read the section data for the given + /// [SectionHeader](SectionHeader) and interpret it in-place as a + /// [StringTable](StringTable). + /// + /// Returns a [ParseError] if the + /// [sh_type](SectionHeader#structfield.sh_type) is not + /// [SHT_STRTAB](abi::SHT_STRTAB). + pub fn section_data_as_strtab( + &mut self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_STRTAB { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_STRTAB, + ))); + } + + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + Ok(StringTable::new(buf)) + } + + fn get_symbol_table_of_type( + &mut self, + symtab_type: u32, + ) -> Result, StringTable<'_>)>, ParseError> { + if self.shdrs.is_empty() { + return Ok(None); + } + + // Get the symtab header for the symtab. The gABI states there can be zero or one per ELF file. + match self.shdrs.iter().find(|shdr| shdr.sh_type == symtab_type) { + Some(shdr) => { + // Load the section bytes for the symtab + // (we want immutable references to both the symtab and its strtab concurrently) + let (symtab_start, symtab_end) = shdr.get_data_range()?; + self.reader.load_bytes(symtab_start..symtab_end)?; + + // Load the section bytes for the strtab + // (we want immutable references to both the symtab and its strtab concurrently) + let strtab = self + .shdrs + .get(shdr.sh_link as usize) + .ok_or(ParseError::BadOffset(shdr.sh_link as u64))?; + let (strtab_start, strtab_end) = strtab.get_data_range()?; + self.reader.load_bytes(strtab_start..strtab_end)?; + + // Validate entsize before trying to read the table so that we can error early for corrupted files + Symbol::validate_entsize(self.ehdr.class, shdr.sh_entsize.try_into()?)?; + let symtab = SymbolTable::new( + self.ehdr.endianness, + self.ehdr.class, + self.reader.get_bytes(symtab_start..symtab_end), + ); + let strtab = StringTable::new(self.reader.get_bytes(strtab_start..strtab_end)); + Ok(Some((symtab, strtab))) + } + None => Ok(None), + } + } + + /// Get the symbol table (section of type SHT_SYMTAB) and its associated string table. + /// + /// The gABI specifies that ELF object files may have zero or one sections of type SHT_SYMTAB. + pub fn symbol_table( + &mut self, + ) -> Result, StringTable<'_>)>, ParseError> { + self.get_symbol_table_of_type(abi::SHT_SYMTAB) + } + + /// Get the dynamic symbol table (section of type SHT_DYNSYM) and its associated string table. + /// + /// The gABI specifies that ELF object files may have zero or one sections of type SHT_DYNSYM. + pub fn dynamic_symbol_table( + &mut self, + ) -> Result, StringTable<'_>)>, ParseError> { + self.get_symbol_table_of_type(abi::SHT_DYNSYM) + } + + /// Get the .dynamic section/segment contents. + pub fn dynamic(&mut self) -> Result>, ParseError> { + // If we have section headers, then look it up there + if !self.shdrs.is_empty() { + if let Some(shdr) = self + .shdrs + .iter() + .find(|shdr| shdr.sh_type == abi::SHT_DYNAMIC) + { + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + return Ok(Some(DynamicTable::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + ))); + } + // Otherwise, look up the PT_DYNAMIC segment (if any) + } else if !self.phdrs.is_empty() { + if let Some(phdr) = self + .phdrs + .iter() + .find(|phdr| phdr.p_type == abi::PT_DYNAMIC) + { + let (start, end) = phdr.get_file_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + return Ok(Some(DynamicTable::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + ))); + } + } + Ok(None) + } + + /// Read the section data for the various GNU Symbol Versioning sections (if any) + /// and return them in a [SymbolVersionTable] that which can interpret them in-place to + /// yield [SymbolRequirement](crate::gnu_symver::SymbolRequirement)s + /// and [SymbolDefinition](crate::gnu_symver::SymbolDefinition)s + /// + /// This is a GNU extension and not all objects use symbol versioning. + /// Returns an empty Option if the object does not use symbol versioning. + pub fn symbol_version_table( + &mut self, + ) -> Result>, ParseError> { + // No sections means no GNU symbol versioning sections, which is ok + if self.shdrs.is_empty() { + return Ok(None); + } + + let mut versym_opt: Option = None; + let mut needs_opt: Option = None; + let mut defs_opt: Option = None; + // Find the GNU Symbol versioning sections (if any) + for shdr in self.shdrs.iter() { + if shdr.sh_type == abi::SHT_GNU_VERSYM { + versym_opt = Some(*shdr); + } else if shdr.sh_type == abi::SHT_GNU_VERNEED { + needs_opt = Some(*shdr); + } else if shdr.sh_type == abi::SHT_GNU_VERDEF { + defs_opt = Some(*shdr); + } + + // If we've found all three sections, then we're done + if versym_opt.is_some() && needs_opt.is_some() && defs_opt.is_some() { + break; + } + } + + // No VERSYM section means the object doesn't use symbol versioning, which is ok. + if versym_opt.is_none() { + return Ok(None); + } + + // Load the versym table + let versym_shdr = versym_opt.unwrap(); + // Validate VERSYM entsize before trying to read the table so that we can error early for corrupted files + VersionIndex::validate_entsize(self.ehdr.class, versym_shdr.sh_entsize.try_into()?)?; + let (versym_start, versym_end) = versym_shdr.get_data_range()?; + self.reader.load_bytes(versym_start..versym_end)?; + + // Get the VERNEED string shdr and load the VERNEED section data (if any) + let needs_shdrs = match needs_opt { + Some(shdr) => { + let (start, end) = shdr.get_data_range()?; + self.reader.load_bytes(start..end)?; + + let strs_shdr = self + .shdrs + .get(shdr.sh_link as usize) + .ok_or(ParseError::BadOffset(shdr.sh_link as u64))?; + let (strs_start, strs_end) = strs_shdr.get_data_range()?; + self.reader.load_bytes(strs_start..strs_end)?; + + Some((shdr, strs_shdr)) + } + // It's possible to have symbol versioning with no NEEDs if we're an object that only + // exports defined symbols. + None => None, + }; + + // Get the VERDEF string shdr and load the VERDEF section data (if any) + let defs_shdrs = match defs_opt { + Some(shdr) => { + let (start, end) = shdr.get_data_range()?; + self.reader.load_bytes(start..end)?; + + let strs_shdr = self + .shdrs + .get(shdr.sh_link as usize) + .ok_or(ParseError::BadOffset(shdr.sh_link as u64))?; + let (strs_start, strs_end) = strs_shdr.get_data_range()?; + self.reader.load_bytes(strs_start..strs_end)?; + + Some((shdr, strs_shdr)) + } + // It's possible to have symbol versioning with no DEFs if we're an object that doesn't + // export any symbols but does use dynamic symbols from other objects. + None => None, + }; + + // Wrap the VERNEED section and strings data in an iterator and string table + let verneeds = match needs_shdrs { + Some((shdr, strs_shdr)) => { + let (strs_start, strs_end) = strs_shdr.get_data_range()?; + let strs_buf = self.reader.get_bytes(strs_start..strs_end); + + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.get_bytes(start..end); + Some(( + VerNeedIterator::new( + self.ehdr.endianness, + self.ehdr.class, + shdr.sh_info as u64, + 0, + buf, + ), + StringTable::new(strs_buf), + )) + } + // If there's no NEEDs, then construct empty wrappers for them + None => None, + }; + + // Wrap the VERDEF section and strings data in an iterator and string table + let verdefs = match defs_shdrs { + Some((shdr, strs_shdr)) => { + let (strs_start, strs_end) = strs_shdr.get_data_range()?; + let strs_buf = self.reader.get_bytes(strs_start..strs_end); + + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.get_bytes(start..end); + Some(( + VerDefIterator::new( + self.ehdr.endianness, + self.ehdr.class, + shdr.sh_info as u64, + 0, + buf, + ), + StringTable::new(strs_buf), + )) + } + // If there's no DEFs, then construct empty wrappers for them + None => None, + }; + + // Wrap the versym section data in a parsing table + let version_ids = VersionIndexTable::new( + self.ehdr.endianness, + self.ehdr.class, + self.reader.get_bytes(versym_start..versym_end), + ); + + // whew, we're done here! + Ok(Some(SymbolVersionTable::new( + version_ids, + verneeds, + verdefs, + ))) + } + + /// Read the section data for the given + /// [SectionHeader](SectionHeader) and interpret it in-place as a + /// [RelIterator](RelIterator). + /// + /// Returns a [ParseError] if the + /// [sh_type](SectionHeader#structfield.sh_type) is not + /// [SHT_REL](abi::SHT_REL). + pub fn section_data_as_rels( + &mut self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_REL { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_REL, + ))); + } + + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + Ok(RelIterator::new(self.ehdr.endianness, self.ehdr.class, buf)) + } + + /// Read the section data for the given + /// [SectionHeader](SectionHeader) and interpret it in-place as a + /// [RelaIterator](RelaIterator). + /// + /// Returns a [ParseError] if the + /// [sh_type](SectionHeader#structfield.sh_type) is not + /// [SHT_RELA](abi::SHT_RELA). + pub fn section_data_as_relas( + &mut self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_RELA { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_RELA, + ))); + } + + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + Ok(RelaIterator::new( + self.ehdr.endianness, + self.ehdr.class, + buf, + )) + } + + /// Read the section data for the given + /// [SectionHeader](SectionHeader) and interpret it in-place as a + /// [NoteIterator](NoteIterator). + /// + /// Returns a [ParseError] if the + /// [sh_type](SectionHeader#structfield.sh_type) is not + /// [SHT_RELA](abi::SHT_NOTE). + pub fn section_data_as_notes( + &mut self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_NOTE { + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_NOTE, + ))); + } + + let (start, end) = shdr.get_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + Ok(NoteIterator::new( + self.ehdr.endianness, + self.ehdr.class, + shdr.sh_addralign as usize, + buf, + )) + } + + /// Read the segment data for the given + /// [Segment](ProgramHeader) and interpret it in-place as a + /// [NoteIterator](NoteIterator). + /// + /// Returns a [ParseError] if the + /// [p_type](ProgramHeader#structfield.p_type) is not + /// [PT_RELA](abi::PT_NOTE). + pub fn segment_data_as_notes( + &mut self, + phdr: &ProgramHeader, + ) -> Result, ParseError> { + if phdr.p_type != abi::PT_NOTE { + return Err(ParseError::UnexpectedSegmentType(( + phdr.p_type, + abi::PT_NOTE, + ))); + } + + let (start, end) = phdr.get_file_data_range()?; + let buf = self.reader.read_bytes(start, end)?; + Ok(NoteIterator::new( + self.ehdr.endianness, + self.ehdr.class, + phdr.p_align as usize, + buf, + )) + } +} + +#[derive(Debug)] +struct CachingReader { + reader: R, + stream_len: u64, + bufs: HashMap<(usize, usize), Box<[u8]>>, +} + +impl CachingReader { + fn new(mut reader: R) -> Result { + // Cache the size of the stream so that we can err (rather than OOM) on invalid + // huge read requests. + let stream_len = reader.seek(SeekFrom::End(0))?; + Ok(CachingReader { + reader, + stream_len, + bufs: HashMap::<(usize, usize), Box<[u8]>>::default(), + }) + } + + fn read_bytes(&mut self, start: usize, end: usize) -> Result<&[u8], ParseError> { + self.load_bytes(start..end)?; + Ok(self.get_bytes(start..end)) + } + + fn get_bytes(&self, range: Range) -> &[u8] { + // It's a programmer error to call get_bytes without first calling load_bytes, so + // we want to panic here. + self.bufs + .get(&(range.start, range.end)) + .expect("load_bytes must be called before get_bytes for every range") + } + + fn load_bytes(&mut self, range: Range) -> Result<(), ParseError> { + if self.bufs.contains_key(&(range.start, range.end)) { + return Ok(()); + } + + // Verify that the read range doesn't go past the end of the stream (corrupted files) + let end = range.end as u64; + if end > self.stream_len { + return Err(ParseError::BadOffset(end)); + } + + self.reader.seek(SeekFrom::Start(range.start as u64))?; + let mut bytes = vec![0; range.len()].into_boxed_slice(); + self.reader.read_exact(&mut bytes)?; + self.bufs.insert((range.start, range.end), bytes); + Ok(()) + } + + fn clear_cache(&mut self) { + self.bufs.clear() + } +} + +#[cfg(test)] +mod interface_tests { + use super::*; + use crate::dynamic::Dyn; + use crate::endian::AnyEndian; + use crate::hash::SysVHashTable; + use crate::note::{Note, NoteGnuAbiTag, NoteGnuBuildId}; + use crate::relocation::Rela; + use crate::symbol::Symbol; + + #[test] + fn test_open_stream() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let file = ElfStream::::open_stream(io).expect("Open test1"); + assert_eq!(file.ehdr.e_type, abi::ET_EXEC); + } + + #[test] + fn section_headers_with_strtab() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let (shdrs, strtab) = file + .section_headers_with_strtab() + .expect("Failed to get shdrs"); + let (shdrs, strtab) = (shdrs, strtab.unwrap()); + + let shdr_4 = &shdrs[4]; + let name = strtab + .get(shdr_4.sh_name as usize) + .expect("Failed to get section name"); + + assert_eq!(name, ".gnu.hash"); + assert_eq!(shdr_4.sh_type, abi::SHT_GNU_HASH); + } + + #[test] + fn shnum_and_shstrndx_in_shdr0() { + let path = std::path::PathBuf::from("sample-objects/shnum.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let (shdrs, strtab) = file + .section_headers_with_strtab() + .expect("shdrs should be parsable"); + let (shdrs, strtab) = (shdrs, strtab.unwrap()); + + let shdrs_len = shdrs.len(); + assert_eq!(shdrs_len, 0xFF15); + + let shdr = shdrs.get(shdrs_len - 1).unwrap(); + let name = strtab + .get(shdr.sh_name as usize) + .expect("Failed to get section name"); + + assert_eq!(name, ".shstrtab"); + assert_eq!(shdr.sh_type, abi::SHT_STRTAB); + } + + #[test] + fn section_header_by_name() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let shdr: SectionHeader = *file + .section_header_by_name(".gnu.hash") + .expect("section table should be parseable") + .expect("file should have .gnu.hash section"); + + assert_eq!(shdr.sh_type, abi::SHT_GNU_HASH); + + let shdr = file + .section_header_by_name(".not.found") + .expect("section table should be parseable"); + + assert_eq!(shdr, None); + } + + #[test] + fn section_data_for_nobits() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let shdr = file.section_headers()[26]; + assert_eq!(shdr.sh_type, abi::SHT_NOBITS); + let (data, chdr) = file + .section_data(&shdr) + .expect("Failed to get section data"); + assert_eq!(chdr, None); + assert_eq!(data, &[]); + } + + #[test] + fn section_data() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let shdr = file.section_headers()[7]; + assert_eq!(shdr.sh_type, abi::SHT_GNU_VERSYM); + let (data, chdr) = file + .section_data(&shdr) + .expect("Failed to get section data"); + assert_eq!(chdr, None); + assert_eq!(data, [0, 0, 2, 0, 2, 0, 0, 0]); + } + + #[test] + fn section_data_as_strtab() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let shdr = file.section_headers()[file.ehdr.e_shstrndx as usize]; + let strtab = file + .section_data_as_strtab(&shdr) + .expect("Failed to read strtab"); + assert_eq!( + strtab.get(1).expect("Failed to get strtab entry"), + ".symtab" + ); + } + + #[test] + fn segments() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let file = ElfStream::::open_stream(io).expect("Open test1"); + + let segments = file.segments(); + assert_eq!( + segments[0], + ProgramHeader { + p_type: abi::PT_PHDR, + p_offset: 64, + p_vaddr: 4194368, + p_paddr: 4194368, + p_filesz: 448, + p_memsz: 448, + p_flags: 5, + p_align: 8, + } + ) + } + + #[test] + fn segments_phnum_in_shdr0() { + let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so"); + let io = std::fs::File::open(path).expect("Could not open file."); + let file = ElfStream::::open_stream(io).expect("Open test1"); + + assert_eq!( + file.segments()[0], + ProgramHeader { + p_type: abi::PT_PHDR, + p_offset: 92, + p_vaddr: 0, + p_paddr: 0, + p_filesz: 32, + p_memsz: 32, + p_flags: 0x20003, + p_align: 0x40000, + } + ); + } + + #[test] + fn symbol_table() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let (symtab, strtab) = file + .symbol_table() + .expect("Failed to read symbol table") + .expect("Failed to find symbol table"); + let symbol = symtab.get(30).expect("Failed to get symbol"); + assert_eq!( + symbol, + Symbol { + st_name: 19, + st_value: 6293200, + st_size: 0, + st_shndx: 21, + st_info: 1, + st_other: 0, + } + ); + assert_eq!( + strtab + .get(symbol.st_name as usize) + .expect("Failed to get name from strtab"), + "__JCR_LIST__" + ); + } + + #[test] + fn dynamic_symbol_table() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let (symtab, strtab) = file + .dynamic_symbol_table() + .expect("Failed to read symbol table") + .expect("Failed to find symbol table"); + let symbol = symtab.get(1).expect("Failed to get symbol"); + assert_eq!( + symbol, + Symbol { + st_name: 11, + st_value: 0, + st_size: 0, + st_shndx: 0, + st_info: 18, + st_other: 0, + } + ); + assert_eq!( + strtab + .get(symbol.st_name as usize) + .expect("Failed to get name from strtab"), + "memset" + ); + } + + #[test] + fn dynamic() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let mut dynamic = file + .dynamic() + .expect("Failed to parse .dynamic") + .expect("Failed to find .dynamic") + .iter(); + assert_eq!( + dynamic.next().expect("Failed to get dyn entry"), + Dyn { + d_tag: abi::DT_NEEDED, + d_un: 1 + } + ); + assert_eq!( + dynamic.next().expect("Failed to get dyn entry"), + Dyn { + d_tag: abi::DT_INIT, + d_un: 4195216 + } + ); + } + + #[test] + fn section_data_as_rels() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let shdr = file.section_headers()[10]; + file.section_data_as_rels(&shdr) + .expect_err("Expected error parsing non-REL scn as RELs"); + } + + #[test] + fn section_data_as_relas() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let shdr = file.section_headers()[10]; + let mut relas = file + .section_data_as_relas(&shdr) + .expect("Failed to read relas section"); + assert_eq!( + relas.next().expect("Failed to get rela entry"), + Rela { + r_offset: 6293704, + r_sym: 1, + r_type: 7, + r_addend: 0, + } + ); + assert_eq!( + relas.next().expect("Failed to get rela entry"), + Rela { + r_offset: 6293712, + r_sym: 2, + r_type: 7, + r_addend: 0, + } + ); + assert!(relas.next().is_none()); + } + + #[test] + fn section_data_as_notes() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let shdr = file.section_headers()[2]; + let mut notes = file + .section_data_as_notes(&shdr) + .expect("Failed to read relas section"); + assert_eq!( + notes.next().expect("Failed to get first note"), + Note::GnuAbiTag(NoteGnuAbiTag { + os: 0, + major: 2, + minor: 6, + subminor: 32 + }) + ); + assert!(notes.next().is_none()); + } + + #[test] + fn segment_data_as_notes() { + let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let phdrs = file.segments(); + let note_phdr = phdrs[5]; + let mut notes = file + .segment_data_as_notes(¬e_phdr) + .expect("Failed to read relas section"); + assert_eq!( + notes.next().expect("Failed to get first note"), + Note::GnuAbiTag(NoteGnuAbiTag { + os: 0, + major: 2, + minor: 6, + subminor: 32 + }) + ); + assert_eq!( + notes.next().expect("Failed to get second note"), + Note::GnuBuildId(NoteGnuBuildId(&[ + 119, 65, 159, 13, 165, 16, 131, 12, 87, 167, 200, 204, 176, 238, 133, 95, 238, 211, + 118, 163 + ])) + ); + assert!(notes.next().is_none()); + } + + #[test] + fn symbol_version_table() { + let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + let vst = file + .symbol_version_table() + .expect("Failed to parse GNU symbol versions") + .expect("Failed to find GNU symbol versions"); + + let req = vst + .get_requirement(2) + .expect("Failed to parse NEED") + .expect("Failed to find NEED"); + assert_eq!(req.file, "libc.so.6"); + assert_eq!(req.name, "GLIBC_2.2.5"); + assert_eq!(req.hash, 0x9691A75); + + let req = vst.get_requirement(3).expect("Failed to parse NEED"); + assert!(req.is_none()); + + let req = vst.get_requirement(4).expect("Failed to parse NEED"); + assert!(req.is_none()); + + let req = vst + .get_requirement(5) + .expect("Failed to parse NEED") + .expect("Failed to find NEED"); + assert_eq!(req.file, "libc.so.6"); + assert_eq!(req.name, "GLIBC_2.2.5"); + assert_eq!(req.hash, 0x9691A75); + + let def = vst + .get_definition(3) + .expect("Failed to parse DEF") + .expect("Failed to find DEF"); + assert_eq!(def.hash, 0xC33237F); + assert_eq!(def.flags, 1); + assert!(!def.hidden); + let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect(); + assert_eq!(def_names, &["hello.so"]); + + let def = vst + .get_definition(7) + .expect("Failed to parse DEF") + .expect("Failed to find DEF"); + assert_eq!(def.hash, 0x1570B62); + assert_eq!(def.flags, 0); + assert!(def.hidden); + let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect(); + assert_eq!(def_names, &["HELLO_1.42"]); + } + + #[test] + fn sysv_hash_table() { + let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); + let io = std::fs::File::open(path).expect("Could not open file."); + let mut file = ElfStream::::open_stream(io).expect("Open test1"); + + // Look up the SysV hash section header + let hash_shdr = *file + .section_header_by_name(".hash") + .expect("Failed to find sysv hash section") + .expect("Failed to find sysv hash section"); + + // We don't have a file interface for getting the SysV hash section yet, so clone the section bytes + // So we can use them to back a SysVHashTable + let (data, _) = file + .section_data(&hash_shdr) + .expect("Failed to get hash section data"); + let data_copy: Vec = data.into(); + let hash_table = + SysVHashTable::new(file.ehdr.endianness, file.ehdr.class, data_copy.as_ref()) + .expect("Failed to parse hash table"); + + // Get the dynamic symbol table. + let (symtab, strtab) = file + .dynamic_symbol_table() + .expect("Failed to read symbol table") + .expect("Failed to find symbol table"); + + // Verify that these three symbols all collide in the hash table's buckets + assert_eq!(crate::hash::sysv_hash(b"use_memset_v2"), 0x8080542); + assert_eq!(crate::hash::sysv_hash(b"__gmon_start__"), 0xF4D007F); + assert_eq!(crate::hash::sysv_hash(b"memset"), 0x73C49C4); + assert_eq!(crate::hash::sysv_hash(b"use_memset_v2") % 3, 0); + assert_eq!(crate::hash::sysv_hash(b"__gmon_start__") % 3, 0); + assert_eq!(crate::hash::sysv_hash(b"memset") % 3, 0); + + // Use the hash table to find a given symbol in it. + let (sym_idx, sym) = hash_table + .find(b"memset", &symtab, &strtab) + .expect("Failed to parse hash") + .expect("Failed to find hash"); + + // Verify that we got the same symbol from the hash table we expected + assert_eq!(sym_idx, 2); + assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset"); + assert_eq!( + sym, + symtab.get(sym_idx).expect("Failed to get expected sym") + ); + } +} + +#[cfg(test)] +mod arch_tests { + use super::*; + use crate::endian::AnyEndian; + + // Basic smoke test which parses out symbols and headers for a given sample object of a given architecture + macro_rules! arch_test { + ( $arch:expr, $e_machine:expr, $endian:expr) => {{ + let path_str = format!("sample-objects/symver.{}.so", $arch); + let path = std::path::PathBuf::from(path_str); + let io = std::fs::File::open(path).expect("file should exist"); + let mut file = ElfStream::::open_stream(io).expect("should parse"); + + assert_eq!(file.ehdr.e_machine, $e_machine); + assert_eq!(file.ehdr.endianness, $endian); + + let (shdrs, strtab) = file.section_headers_with_strtab().expect("should parse"); + let (shdrs, strtab) = (shdrs, strtab.unwrap()); + let _: Vec<_> = shdrs + .iter() + .map(|shdr| { + ( + strtab.get(shdr.sh_name as usize).expect("should parse"), + shdr, + ) + }) + .collect(); + + if let Some((symtab, strtab)) = file.symbol_table().expect("should parse") { + let _: Vec<_> = symtab + .iter() + .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym)) + .collect(); + } + + if let Some((symtab, strtab)) = file.dynamic_symbol_table().expect("should parse") { + let _: Vec<_> = symtab + .iter() + .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym)) + .collect(); + } + + let note_phdrs: Vec<_> = file.segments() + .iter() + .filter(|phdr| phdr.p_type == abi::PT_NOTE) + .map(|phdr| *phdr) + .collect(); + for phdr in note_phdrs { + let _: Vec<_> = file + .segment_data_as_notes(&phdr) + .expect("should parse") + .collect(); + } + }}; + } + + #[test] + fn x86_64() { + arch_test!("x86_64", abi::EM_X86_64, AnyEndian::Little); + } + + #[test] + fn m68k() { + arch_test!("m68k", abi::EM_68K, AnyEndian::Big); + } + + #[test] + fn aarch64() { + arch_test!("aarch64", abi::EM_AARCH64, AnyEndian::Little); + } + + #[test] + fn armhf() { + arch_test!("armhf", abi::EM_ARM, AnyEndian::Little); + } + + #[test] + fn powerpc64() { + arch_test!("powerpc64", abi::EM_PPC64, AnyEndian::Big); + } + + #[test] + fn powerpc64le() { + arch_test!("powerpc64le", abi::EM_PPC64, AnyEndian::Little); + } + + #[test] + fn riscv64() { + arch_test!("riscv64", abi::EM_RISCV, AnyEndian::Little); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/endian.rs b/mikros_std_deps/elf-0.7.4/src/endian.rs new file mode 100644 index 00000000000..d07812d74b9 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/endian.rs @@ -0,0 +1,327 @@ +//! An all-safe-code endian-aware integer parsing implementation via the +//! [EndianParse] trait. +//! +//! This module provides four endian parsing implementations optimized to support the different +//! common use-cases for an ELF parsing library. Each trait impl represents a +//! specification that encapsulates an interface for parsing integers from some +//! set of allowed byte orderings. +//! +//! * [AnyEndian]: Dynamically parsing either byte order at runtime based on the type of ELF object being parsed. +//! * [BigEndian]/[LittleEndian]: For tools that know they only want to parse a single given byte order known at compile time. +//! * [type@NativeEndian]: For tools that know they want to parse the same byte order as the target's byte order. +// +// Note: +// I'd love to see this get replaced with safe transmutes, if that RFC ever gets formalized. +// Until then, this crate serves as an example implementation for what's possible with purely safe rust. +use crate::abi; +use crate::parse::ParseError; + +/// This macro writes out safe code to get a subslice from the the byte slice $data +/// at the given $off as a [u8; size_of<$typ>], then calls the corresponding safe +/// endian-aware conversion on it. +/// +/// This uses safe integer math and returns a ParseError on overflow or if $data did +/// not contain enough bytes at $off to perform the conversion. +macro_rules! safe_from { + ( $self:ident, $typ:ty, $off:ident, $data:ident) => {{ + const SIZE: usize = core::mem::size_of::<$typ>(); + + let end = (*$off) + .checked_add(SIZE) + .ok_or(ParseError::IntegerOverflow)?; + + let buf: [u8; SIZE] = $data + .get(*$off..end) + .ok_or(ParseError::SliceReadError((*$off, end)))? + .try_into()?; + + *$off = end; + + // Note: This check evaluates to a constant true/false for the "fixed" types + // so the compiler should optimize out the check (LittleEndian, BigEndian, NativeEndian) + if $self.is_little() { + Ok(<$typ>::from_le_bytes(buf)) + } else { + Ok(<$typ>::from_be_bytes(buf)) + } + }}; +} + +/// An all-safe-code endian-aware integer parsing trait. +/// +/// These methods use safe code to get a subslice from the the byte slice $data +/// at the given $off as a [u8; size_of<$typ>], then calls the corresponding safe +/// endian-aware conversion on it. +/// +/// These use checked integer math and returns a ParseError on overflow or if $data did +/// not contain enough bytes at $off to perform the conversion. +pub trait EndianParse: Clone + Copy + Default + PartialEq + Eq { + fn parse_u8_at(self, offset: &mut usize, data: &[u8]) -> Result { + safe_from!(self, u8, offset, data) + } + + fn parse_u16_at(self, offset: &mut usize, data: &[u8]) -> Result { + safe_from!(self, u16, offset, data) + } + + fn parse_u32_at(self, offset: &mut usize, data: &[u8]) -> Result { + safe_from!(self, u32, offset, data) + } + + fn parse_u64_at(self, offset: &mut usize, data: &[u8]) -> Result { + safe_from!(self, u64, offset, data) + } + + fn parse_i32_at(self, offset: &mut usize, data: &[u8]) -> Result { + safe_from!(self, i32, offset, data) + } + + fn parse_i64_at(self, offset: &mut usize, data: &[u8]) -> Result { + safe_from!(self, i64, offset, data) + } + + /// Get an endian-aware integer parsing spec for an ELF [FileHeader](crate::file::FileHeader)'s + /// `ident[EI_DATA]` byte. + /// + /// Returns an [UnsupportedElfEndianness](ParseError::UnsupportedElfEndianness) if this spec + /// doesn't support parsing the byte-order represented by ei_data. If you're + /// seeing this error, are you trying to read files of any endianness? i.e. + /// did you want to use AnyEndian? + fn from_ei_data(ei_data: u8) -> Result; + + fn is_little(self) -> bool; + + #[inline(always)] + fn is_big(self) -> bool { + !self.is_little() + } +} + +/// An endian parsing type that can choose at runtime which byte order to parse integers as. +/// This is useful for scenarios where a single compiled binary wants to dynamically +/// interpret ELF files of any byte order. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum AnyEndian { + /// Used for a little-endian ELF structures that have been parsed with AnyEndian + #[default] + Little, + /// Used for a big-endian ELF structures that have been parsed with AnyEndian + Big, +} + +/// A zero-sized type that always parses integers as if they're in little-endian order. +/// This is useful for scenarios where a combiled binary knows it only wants to interpret +/// little-endian ELF files and doesn't want the performance penalty of evaluating a match +/// each time it parses an integer. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct LittleEndian; + +/// A zero-sized type that always parses integers as if they're in big-endian order. +/// This is useful for scenarios where a combiled binary knows it only wants to interpret +/// big-endian ELF files and doesn't want the performance penalty of evaluating a match +/// each time it parses an integer. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct BigEndian; + +/// A zero-sized type that always parses integers as if they're in the compilation target's native-endian order. +/// This is useful for toolchain scenarios where a combiled binary knows it only wants to interpret +/// ELF files compiled for the same target and doesn't want the performance penalty of evaluating a match +/// each time it parses an integer. +#[cfg(target_endian = "little")] +pub type NativeEndian = LittleEndian; + +#[cfg(target_endian = "little")] +#[allow(non_upper_case_globals)] +#[doc(hidden)] +pub const NativeEndian: LittleEndian = LittleEndian; + +/// A zero-sized type that always parses integers as if they're in the compilation target's native-endian order. +/// This is useful for toolchain scenarios where a combiled binary knows it only wants to interpret +/// ELF files compiled for the same target and doesn't want the performance penalty of evaluating a match +/// each time it parses an integer. +#[cfg(target_endian = "big")] +pub type NativeEndian = BigEndian; + +#[cfg(target_endian = "big")] +#[allow(non_upper_case_globals)] +#[doc(hidden)] +pub const NativeEndian: BigEndian = BigEndian; + +impl EndianParse for LittleEndian { + fn from_ei_data(ei_data: u8) -> Result { + match ei_data { + abi::ELFDATA2LSB => Ok(LittleEndian), + _ => Err(ParseError::UnsupportedElfEndianness(ei_data)), + } + } + + #[inline(always)] + fn is_little(self) -> bool { + true + } +} + +impl EndianParse for BigEndian { + fn from_ei_data(ei_data: u8) -> Result { + match ei_data { + abi::ELFDATA2MSB => Ok(BigEndian), + _ => Err(ParseError::UnsupportedElfEndianness(ei_data)), + } + } + + #[inline(always)] + fn is_little(self) -> bool { + false + } +} + +impl EndianParse for AnyEndian { + fn from_ei_data(ei_data: u8) -> Result { + match ei_data { + abi::ELFDATA2LSB => Ok(AnyEndian::Little), + abi::ELFDATA2MSB => Ok(AnyEndian::Big), + _ => Err(ParseError::UnsupportedElfEndianness(ei_data)), + } + } + + #[inline(always)] + fn is_little(self) -> bool { + match self { + AnyEndian::Little => true, + AnyEndian::Big => false, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! parse_test { + ( $endian:expr, $res_typ:ty, $method:ident, $expect:expr) => {{ + let bytes = [ + 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, 0x08u8, + ]; + let mut offset = 0; + let result = $endian.$method(&mut offset, &bytes).unwrap(); + assert_eq!(result, $expect); + assert_eq!(offset, core::mem::size_of::<$res_typ>()); + }}; + } + + macro_rules! fuzz_too_short_test { + ( $endian:expr, $res_typ:ty, $method:ident) => {{ + let bytes = [ + 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, 0x08u8, + ]; + let size = core::mem::size_of::<$res_typ>(); + for n in 0..size { + let buf = bytes.split_at(n).0.as_ref(); + let mut offset: usize = 0; + let error = $endian + .$method(&mut offset, buf) + .expect_err("Expected an error, but parsed: "); + assert!( + matches!(error, ParseError::SliceReadError(_)), + "Unexpected Error type found: {error}" + ); + } + }}; + } + + #[test] + fn parse_u8_at() { + parse_test!(LittleEndian, u8, parse_u8_at, 0x01u8); + parse_test!(BigEndian, u8, parse_u8_at, 0x01u8); + parse_test!(AnyEndian::Little, u8, parse_u8_at, 0x01u8); + parse_test!(AnyEndian::Big, u8, parse_u8_at, 0x01u8); + } + + #[test] + fn parse_u16_at() { + parse_test!(LittleEndian, u16, parse_u16_at, 0x0201u16); + parse_test!(BigEndian, u16, parse_u16_at, 0x0102u16); + parse_test!(AnyEndian::Little, u16, parse_u16_at, 0x0201u16); + parse_test!(AnyEndian::Big, u16, parse_u16_at, 0x0102u16); + } + + #[test] + fn parse_u32_at() { + parse_test!(LittleEndian, u32, parse_u32_at, 0x04030201u32); + parse_test!(BigEndian, u32, parse_u32_at, 0x01020304u32); + parse_test!(AnyEndian::Little, u32, parse_u32_at, 0x04030201u32); + parse_test!(AnyEndian::Big, u32, parse_u32_at, 0x01020304u32); + } + + #[test] + fn parse_u64_at() { + parse_test!(LittleEndian, u64, parse_u64_at, 0x0807060504030201u64); + parse_test!(BigEndian, u64, parse_u64_at, 0x0102030405060708u64); + parse_test!(AnyEndian::Little, u64, parse_u64_at, 0x0807060504030201u64); + parse_test!(AnyEndian::Big, u64, parse_u64_at, 0x0102030405060708u64); + } + + #[test] + fn parse_i32_at() { + parse_test!(LittleEndian, i32, parse_i32_at, 0x04030201i32); + parse_test!(BigEndian, i32, parse_i32_at, 0x01020304i32); + parse_test!(AnyEndian::Little, i32, parse_i32_at, 0x04030201i32); + parse_test!(AnyEndian::Big, i32, parse_i32_at, 0x01020304i32); + } + + #[test] + fn parse_i64_at() { + parse_test!(LittleEndian, i64, parse_i64_at, 0x0807060504030201i64); + parse_test!(BigEndian, i64, parse_i64_at, 0x0102030405060708i64); + parse_test!(AnyEndian::Little, i64, parse_i64_at, 0x0807060504030201i64); + parse_test!(AnyEndian::Big, i64, parse_i64_at, 0x0102030405060708i64); + } + + #[test] + fn fuzz_u8_too_short() { + fuzz_too_short_test!(LittleEndian, u8, parse_u8_at); + fuzz_too_short_test!(BigEndian, u8, parse_u8_at); + fuzz_too_short_test!(AnyEndian::Little, u8, parse_u8_at); + fuzz_too_short_test!(AnyEndian::Big, u8, parse_u8_at); + } + + #[test] + fn fuzz_u16_too_short() { + fuzz_too_short_test!(LittleEndian, u16, parse_u16_at); + fuzz_too_short_test!(BigEndian, u16, parse_u16_at); + fuzz_too_short_test!(AnyEndian::Little, u16, parse_u16_at); + fuzz_too_short_test!(AnyEndian::Big, u16, parse_u16_at); + } + + #[test] + fn fuzz_u32_too_short() { + fuzz_too_short_test!(LittleEndian, u32, parse_u32_at); + fuzz_too_short_test!(BigEndian, u32, parse_u32_at); + fuzz_too_short_test!(AnyEndian::Little, u32, parse_u32_at); + fuzz_too_short_test!(AnyEndian::Big, u32, parse_u32_at); + } + + #[test] + fn fuzz_i32_too_short() { + fuzz_too_short_test!(LittleEndian, i32, parse_i32_at); + fuzz_too_short_test!(BigEndian, i32, parse_i32_at); + fuzz_too_short_test!(AnyEndian::Little, i32, parse_i32_at); + fuzz_too_short_test!(AnyEndian::Big, i32, parse_i32_at); + } + + #[test] + fn fuzz_u64_too_short() { + fuzz_too_short_test!(LittleEndian, u64, parse_u64_at); + fuzz_too_short_test!(BigEndian, u64, parse_u64_at); + fuzz_too_short_test!(AnyEndian::Little, u64, parse_u64_at); + fuzz_too_short_test!(AnyEndian::Big, u64, parse_u64_at); + } + + #[test] + fn fuzz_i64_too_short() { + fuzz_too_short_test!(LittleEndian, i64, parse_i64_at); + fuzz_too_short_test!(BigEndian, i64, parse_i64_at); + fuzz_too_short_test!(AnyEndian::Little, i64, parse_i64_at); + fuzz_too_short_test!(AnyEndian::Big, i64, parse_i64_at); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/file.rs b/mikros_std_deps/elf-0.7.4/src/file.rs new file mode 100644 index 00000000000..bf4137d1159 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/file.rs @@ -0,0 +1,478 @@ +//! Parsing the ELF File Header +use crate::abi; +use crate::endian::EndianParse; +use crate::parse::ParseError; + +/// Represents the ELF file word size (32-bit vs 64-bit) +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Class { + ELF32, + ELF64, +} + +/// C-style 32-bit ELF File Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Ehdr { + pub e_ident: [u8; abi::EI_NIDENT], + pub e_type: u16, + pub e_machine: u16, + pub e_version: u32, + pub e_entry: u32, + pub e_phoff: u32, + pub e_shoff: u32, + pub e_flags: u32, + pub e_ehsize: u16, + pub e_phentsize: u16, + pub e_phnum: u16, + pub e_shentsize: u16, + pub e_shnum: u16, + pub e_shstrndx: u16, +} + +/// C-style 64-bit ELF File Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Ehdr { + pub e_ident: [u8; abi::EI_NIDENT], + pub e_type: u16, + pub e_machine: u16, + pub e_version: u32, + pub e_entry: u64, + pub e_phoff: u64, + pub e_shoff: u64, + pub e_flags: u32, + pub e_ehsize: u16, + pub e_phentsize: u16, + pub e_phnum: u16, + pub e_shentsize: u16, + pub e_shnum: u16, + pub e_shstrndx: u16, +} + +/// Encapsulates the contents of the ELF File Header +/// +/// The ELF File Header starts off every ELF file and both identifies the +/// file contents and informs how to interpret said contents. This includes +/// the width of certain fields (32-bit vs 64-bit), the data endianness, the +/// file type, and more. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct FileHeader { + /// 32-bit vs 64-bit + pub class: Class, + // file byte order + pub endianness: E, + /// elf version + pub version: u32, + /// OS ABI + pub osabi: u8, + /// Version of the OS ABI + pub abiversion: u8, + /// ELF file type + pub e_type: u16, + /// Target machine architecture + pub e_machine: u16, + /// Virtual address of program entry point + /// This member gives the virtual address to which the system first transfers control, + /// thus starting the process. If the file has no associated entry point, this member holds zero. + /// + /// Note: Type is Elf32_Addr or Elf64_Addr which are either 4 or 8 bytes. We aren't trying to zero-copy + /// parse the FileHeader since there's only one per file and its only ~45 bytes anyway, so we use + /// u64 for the three Elf*_Addr and Elf*_Off fields here. + pub e_entry: u64, + /// This member holds the program header table's file offset in bytes. If the file has no program header + /// table, this member holds zero. + pub e_phoff: u64, + /// This member holds the section header table's file offset in bytes. If the file has no section header + /// table, this member holds zero. + pub e_shoff: u64, + /// This member holds processor-specific flags associated with the file. Flag names take the form EF_machine_flag. + pub e_flags: u32, + /// This member holds the ELF header's size in bytes. + pub e_ehsize: u16, + /// This member holds the size in bytes of one entry in the file's program header table; all entries are the same size. + pub e_phentsize: u16, + /// This member holds the number of entries in the program header table. Thus the product of e_phentsize and e_phnum + /// gives the table's size in bytes. If a file has no program header table, e_phnum holds the value zero. + pub e_phnum: u16, + /// This member holds a section header's size in bytes. A section header is one entry in the section header table; + /// all entries are the same size. + pub e_shentsize: u16, + /// This member holds the number of entries in the section header table. Thus the product of e_shentsize and e_shnum + /// gives the section header table's size in bytes. If a file has no section header table, e_shnum holds the value zero. + /// + /// If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), this member has the value zero and + /// the actual number of section header table entries is contained in the sh_size field of the section header at index 0. + /// (Otherwise, the sh_size member of the initial entry contains 0.) + pub e_shnum: u16, + /// This member holds the section header table index of the entry associated with the section name string table. If the + /// file has no section name string table, this member holds the value SHN_UNDEF. + /// + /// If the section name string table section index is greater than or equal to SHN_LORESERVE (0xff00), this member has + /// the value SHN_XINDEX (0xffff) and the actual index of the section name string table section is contained in the + /// sh_link field of the section header at index 0. (Otherwise, the sh_link member of the initial entry contains 0.) + pub e_shstrndx: u16, +} + +pub const ELF32_EHDR_TAILSIZE: usize = 36; +pub const ELF64_EHDR_TAILSIZE: usize = 48; + +fn verify_ident(buf: &[u8]) -> Result<(), ParseError> { + // Verify the magic number + let magic = buf.split_at(abi::EI_CLASS).0; + if magic != abi::ELFMAGIC { + return Err(ParseError::BadMagic([ + magic[0], magic[1], magic[2], magic[3], + ])); + } + + // Verify ELF Version + let version = buf[abi::EI_VERSION]; + if version != abi::EV_CURRENT { + return Err(ParseError::UnsupportedVersion(( + version as u64, + abi::EV_CURRENT as u64, + ))); + } + + Ok(()) +} + +pub fn parse_ident(data: &[u8]) -> Result<(E, Class, u8, u8), ParseError> { + verify_ident(data)?; + + let e_class = data[abi::EI_CLASS]; + let class = match e_class { + abi::ELFCLASS32 => Class::ELF32, + abi::ELFCLASS64 => Class::ELF64, + _ => { + return Err(ParseError::UnsupportedElfClass(e_class)); + } + }; + + // Verify endianness is something we know how to parse + let file_endian = E::from_ei_data(data[abi::EI_DATA])?; + + Ok(( + file_endian, + class, + data[abi::EI_OSABI], + data[abi::EI_ABIVERSION], + )) +} + +impl FileHeader { + pub fn parse_tail(ident: (E, Class, u8, u8), data: &[u8]) -> Result, ParseError> { + let (file_endian, class, osabi, abiversion) = ident; + + let mut offset = 0; + let e_type = file_endian.parse_u16_at(&mut offset, data)?; + let e_machine = file_endian.parse_u16_at(&mut offset, data)?; + let version = file_endian.parse_u32_at(&mut offset, data)?; + + let e_entry: u64; + let e_phoff: u64; + let e_shoff: u64; + + if class == Class::ELF32 { + e_entry = file_endian.parse_u32_at(&mut offset, data)? as u64; + e_phoff = file_endian.parse_u32_at(&mut offset, data)? as u64; + e_shoff = file_endian.parse_u32_at(&mut offset, data)? as u64; + } else { + e_entry = file_endian.parse_u64_at(&mut offset, data)?; + e_phoff = file_endian.parse_u64_at(&mut offset, data)?; + e_shoff = file_endian.parse_u64_at(&mut offset, data)?; + } + + let e_flags = file_endian.parse_u32_at(&mut offset, data)?; + let e_ehsize = file_endian.parse_u16_at(&mut offset, data)?; + let e_phentsize = file_endian.parse_u16_at(&mut offset, data)?; + let e_phnum = file_endian.parse_u16_at(&mut offset, data)?; + let e_shentsize = file_endian.parse_u16_at(&mut offset, data)?; + let e_shnum = file_endian.parse_u16_at(&mut offset, data)?; + let e_shstrndx = file_endian.parse_u16_at(&mut offset, data)?; + + Ok(FileHeader { + class, + endianness: file_endian, + version, + e_type, + e_machine, + osabi, + abiversion, + e_entry, + e_phoff, + e_shoff, + e_flags, + e_ehsize, + e_phentsize, + e_phnum, + e_shentsize, + e_shnum, + e_shstrndx, + }) + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::AnyEndian; + + #[test] + fn test_verify_ident_valid() { + let data: [u8; abi::EI_NIDENT] = [ + abi::ELFMAG0, + abi::ELFMAG1, + abi::ELFMAG2, + abi::ELFMAG3, + abi::ELFCLASS32, + abi::ELFDATA2LSB, + abi::EV_CURRENT, + abi::ELFOSABI_LINUX, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + verify_ident(data.as_ref()).expect("Expected Ok result"); + } + + #[test] + fn test_verify_ident_invalid_mag0() { + let data: [u8; abi::EI_NIDENT] = [ + 0xFF, + abi::ELFMAG1, + abi::ELFMAG2, + abi::ELFMAG3, + abi::ELFCLASS32, + abi::ELFDATA2LSB, + abi::EV_CURRENT, + abi::ELFOSABI_LINUX, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + let result = verify_ident(data.as_ref()).expect_err("Expected an error"); + assert!( + matches!(result, ParseError::BadMagic(_)), + "Unexpected Error type found: {result}" + ); + } + + #[test] + fn test_verify_ident_invalid_mag1() { + let data: [u8; abi::EI_NIDENT] = [ + abi::ELFMAG0, + 0xFF, + abi::ELFMAG2, + abi::ELFMAG3, + abi::ELFCLASS32, + abi::ELFDATA2LSB, + abi::EV_CURRENT, + abi::ELFOSABI_LINUX, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + let result = verify_ident(data.as_ref()).expect_err("Expected an error"); + assert!( + matches!(result, ParseError::BadMagic(_)), + "Unexpected Error type found: {result}" + ); + } + + #[test] + fn test_verify_ident_invalid_mag2() { + let data: [u8; abi::EI_NIDENT] = [ + abi::ELFMAG0, + abi::ELFMAG1, + 0xFF, + abi::ELFMAG3, + abi::ELFCLASS32, + abi::ELFDATA2LSB, + abi::EV_CURRENT, + abi::ELFOSABI_LINUX, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + let result = verify_ident(data.as_ref()).expect_err("Expected an error"); + assert!( + matches!(result, ParseError::BadMagic(_)), + "Unexpected Error type found: {result}" + ); + } + + #[test] + fn test_verify_ident_invalid_mag3() { + let data: [u8; abi::EI_NIDENT] = [ + abi::ELFMAG0, + abi::ELFMAG1, + abi::ELFMAG2, + 0xFF, + abi::ELFCLASS32, + abi::ELFDATA2LSB, + abi::EV_CURRENT, + abi::ELFOSABI_LINUX, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + let result = verify_ident(data.as_ref()).expect_err("Expected an error"); + assert!( + matches!(result, ParseError::BadMagic(_)), + "Unexpected Error type found: {result}" + ); + } + + #[allow(deprecated)] + #[test] + fn test_verify_ident_invalid_version() { + let data: [u8; abi::EI_NIDENT] = [ + abi::ELFMAG0, + abi::ELFMAG1, + abi::ELFMAG2, + abi::ELFMAG3, + abi::ELFCLASS32, + abi::ELFDATA2LSB, + 42, + abi::ELFOSABI_LINUX, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + let result = verify_ident(data.as_ref()).expect_err("Expected an error"); + assert!( + matches!(result, ParseError::UnsupportedVersion((42, 1))), + "Unexpected Error type found: {result}" + ); + } + + #[test] + fn test_parse_ehdr32_works() { + let ident = (AnyEndian::Little, Class::ELF32, abi::ELFOSABI_LINUX, 7u8); + let mut tail = [0u8; ELF64_EHDR_TAILSIZE]; + for (n, elem) in tail.iter_mut().enumerate().take(ELF64_EHDR_TAILSIZE) { + *elem = n as u8; + } + + assert_eq!( + FileHeader::parse_tail(ident, &tail).unwrap(), + FileHeader { + class: Class::ELF32, + endianness: AnyEndian::Little, + version: 0x7060504, + osabi: abi::ELFOSABI_LINUX, + abiversion: 7, + e_type: 0x100, + e_machine: 0x302, + e_entry: 0x0B0A0908, + e_phoff: 0x0F0E0D0C, + e_shoff: 0x13121110, + e_flags: 0x17161514, + e_ehsize: 0x1918, + e_phentsize: 0x1B1A, + e_phnum: 0x1D1C, + e_shentsize: 0x1F1E, + e_shnum: 0x2120, + e_shstrndx: 0x2322, + } + ); + } + + #[test] + fn test_parse_ehdr32_fuzz_too_short() { + let ident = (AnyEndian::Little, Class::ELF32, abi::ELFOSABI_LINUX, 7u8); + let tail = [0u8; ELF32_EHDR_TAILSIZE]; + + for n in 0..ELF32_EHDR_TAILSIZE { + let buf = tail.split_at(n).0; + let result = FileHeader::parse_tail(ident, buf).expect_err("Expected an error"); + assert!( + matches!(result, ParseError::SliceReadError(_)), + "Unexpected Error type found: {result:?}" + ); + } + } + + #[test] + fn test_parse_ehdr64_works() { + let ident = (AnyEndian::Big, Class::ELF64, abi::ELFOSABI_LINUX, 7u8); + let mut tail = [0u8; ELF64_EHDR_TAILSIZE]; + for (n, elem) in tail.iter_mut().enumerate().take(ELF64_EHDR_TAILSIZE) { + *elem = n as u8; + } + + assert_eq!( + FileHeader::parse_tail(ident, &tail).unwrap(), + FileHeader { + class: Class::ELF64, + endianness: AnyEndian::Big, + version: 0x04050607, + osabi: abi::ELFOSABI_LINUX, + abiversion: 7, + e_type: 0x0001, + e_machine: 0x0203, + e_entry: 0x08090A0B0C0D0E0F, + e_phoff: 0x1011121314151617, + e_shoff: 0x18191A1B1C1D1E1F, + e_flags: 0x20212223, + e_ehsize: 0x2425, + e_phentsize: 0x2627, + e_phnum: 0x2829, + e_shentsize: 0x2A2B, + e_shnum: 0x2C2D, + e_shstrndx: 0x2E2F, + } + ); + } + + #[test] + fn test_parse_ehdr64_fuzz_too_short() { + let ident = (AnyEndian::Little, Class::ELF64, abi::ELFOSABI_LINUX, 7u8); + let tail = [0u8; ELF64_EHDR_TAILSIZE]; + + for n in 0..ELF64_EHDR_TAILSIZE { + let buf = tail.split_at(n).0; + let result = FileHeader::parse_tail(ident, buf).expect_err("Expected an error"); + assert!( + matches!(result, ParseError::SliceReadError(_)), + "Unexpected Error type found: {result:?}" + ); + } + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/gnu_symver.rs b/mikros_std_deps/elf-0.7.4/src/gnu_symver.rs new file mode 100644 index 00000000000..bd279c7d8b6 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/gnu_symver.rs @@ -0,0 +1,1592 @@ +//! Parsing GNU extension sections for dynamic symbol versioning `.gnu.version.*` +use crate::abi; +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ParsingTable}; +use crate::string_table::StringTable; + +#[derive(Debug, PartialEq, Eq)] +pub struct SymbolRequirement<'data> { + pub file: &'data str, + pub name: &'data str, + pub hash: u32, + pub flags: u16, + pub hidden: bool, +} + +#[derive(Debug)] +pub struct SymbolDefinition<'data, E: EndianParse> { + pub hash: u32, + pub flags: u16, + pub names: SymbolNamesIterator<'data, E>, + pub hidden: bool, +} + +#[derive(Debug)] +pub struct SymbolNamesIterator<'data, E: EndianParse> { + vda_iter: VerDefAuxIterator<'data, E>, + strtab: &'data StringTable<'data>, +} + +impl<'data, E: EndianParse> SymbolNamesIterator<'data, E> { + pub fn new(vda_iter: VerDefAuxIterator<'data, E>, strtab: &'data StringTable<'data>) -> Self { + SymbolNamesIterator { vda_iter, strtab } + } +} + +impl<'data, E: EndianParse> Iterator for SymbolNamesIterator<'data, E> { + type Item = Result<&'data str, ParseError>; + fn next(&mut self) -> Option { + let vda = self.vda_iter.next(); + match vda { + Some(vda) => Some(self.strtab.get(vda.vda_name as usize)), + None => None, + } + } +} + +#[derive(Debug)] +pub struct SymbolVersionTable<'data, E: EndianParse> { + version_ids: VersionIndexTable<'data, E>, + + verneeds: Option<(VerNeedIterator<'data, E>, StringTable<'data>)>, + verdefs: Option<(VerDefIterator<'data, E>, StringTable<'data>)>, +} + +impl<'data, E: EndianParse> SymbolVersionTable<'data, E> { + pub fn new( + version_ids: VersionIndexTable<'data, E>, + verneeds: Option<(VerNeedIterator<'data, E>, StringTable<'data>)>, + verdefs: Option<(VerDefIterator<'data, E>, StringTable<'data>)>, + ) -> Self { + SymbolVersionTable { + version_ids, + verneeds, + verdefs, + } + } + + pub fn get_requirement( + &self, + sym_idx: usize, + ) -> Result>, ParseError> { + let (verneeds, verneed_strs) = match self.verneeds { + Some(verneeds) => verneeds, + None => { + return Ok(None); + } + }; + + let ver_ndx = self.version_ids.get(sym_idx)?; + let iter = verneeds; + for (vn, vna_iter) in iter { + for vna in vna_iter { + if vna.vna_other != ver_ndx.index() { + continue; + } + + let file = verneed_strs.get(vn.vn_file as usize)?; + let name = verneed_strs.get(vna.vna_name as usize)?; + let hash = vna.vna_hash; + let hidden = ver_ndx.is_hidden(); + return Ok(Some(SymbolRequirement { + file, + name, + hash, + flags: vna.vna_flags, + hidden, + })); + } + } + + // Maybe we should treat this as a ParseError instead of returning an + // empty Option? This can only happen if .gnu.versions[N] contains an + // index that doesn't exist, which is likely a file corruption or + // programmer error (i.e asking for a requirement for a defined symbol) + Ok(None) + } + + pub fn get_definition( + &self, + sym_idx: usize, + ) -> Result>, ParseError> { + let (ref verdefs, ref verdef_strs) = match self.verdefs { + Some(ref verdefs) => verdefs, + None => { + return Ok(None); + } + }; + + let ver_ndx = self.version_ids.get(sym_idx)?; + let iter = *verdefs; + for (vd, vda_iter) in iter { + if vd.vd_ndx != ver_ndx.index() { + continue; + } + + let flags = vd.vd_flags; + let hash = vd.vd_hash; + let hidden = ver_ndx.is_hidden(); + return Ok(Some(SymbolDefinition { + hash, + flags, + names: SymbolNamesIterator { + vda_iter, + strtab: verdef_strs, + }, + hidden, + })); + } + + // Maybe we should treat this as a ParseError instead of returning an + // empty Option? This can only happen if .gnu.versions[N] contains an + // index that doesn't exist, which is likely a file corruption or + // programmer error (i.e asking for a definition for an undefined symbol) + Ok(None) + } +} + +//////////////////////////////////////////////////////////////////// +// _ // +// __ _ _ __ _ _ __ _____ _ __ ___(_) ___ _ __ // +// / _` | '_ \| | | | \ \ / / _ \ '__/ __| |/ _ \| '_ \ // +// _ | (_| | | | | |_| | _ \ V / __/ | \__ \ | (_) | | | | // +// (_) \__, |_| |_|\__,_| (_) \_/ \___|_| |___/_|\___/|_| |_| // +// |___/ // +//////////////////////////////////////////////////////////////////// + +pub type VersionIndexTable<'data, E> = ParsingTable<'data, E, VersionIndex>; + +/// The special GNU extension section .gnu.version has a section type of SHT_GNU_VERSYM. +/// This section shall have the same number of entries as the Dynamic Symbol Table in +/// the .dynsym section. The .gnu.version section shall contain an array of +/// elements of type Elfxx_Half (both of which are 16-bit unsigned integers). +/// +/// The .gnu.version section and VersionIndex values act as a lookup table for specifying +/// the version defined for or required by the corresponding symbol in the Dynamic Symbol Table. +/// +/// For example, the symbol at index N in the .dynsym Symbol Table will have a VersionIndex +/// value located in the versym table at .gnu.version\[N\] which identifies +/// structures in the .gnu.version_d and .gnu.version_r sections. These values +/// are located in identifiers provided by the the vna_other member of the VerNeedAux +/// structure or the vd_ndx member of the VerDef structure. +#[derive(Debug, PartialEq, Eq)] +pub struct VersionIndex(pub u16); + +impl VersionIndex { + pub fn index(&self) -> u16 { + self.0 & abi::VER_NDX_VERSION + } + + pub fn is_local(&self) -> bool { + self.index() == abi::VER_NDX_LOCAL + } + + pub fn is_global(&self) -> bool { + self.index() == abi::VER_NDX_GLOBAL + } + + pub fn is_hidden(&self) -> bool { + (self.0 & abi::VER_NDX_HIDDEN) != 0 + } +} + +impl ParseAt for VersionIndex { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + Ok(VersionIndex(endian.parse_u16_at(offset, data)?)) + } + + #[inline] + fn size_for(_class: Class) -> usize { + core::mem::size_of::() + } +} + +/////////////////////////////////////////////////////////////////////////////// +// _ _ // +// __ _ _ __ _ _ __ _____ _ __ ___(_) ___ _ __ __| | // +// / _` | '_ \| | | | \ \ / / _ \ '__/ __| |/ _ \| '_ \ / _` | // +// _ | (_| | | | | |_| | _ \ V / __/ | \__ \ | (_) | | | | | (_| | // +// (_) \__, |_| |_|\__,_| (_) \_/ \___|_| |___/_|\___/|_| |_|_____\__,_| // +// |___/ |_____| // +/////////////////////////////////////////////////////////////////////////////// + +/// The special GNU extension section .gnu.version_d has a section type of SHT_GNU_VERDEF +/// This section shall contain symbol version definitions. The number of entries +/// in this section shall be contained in the DT_VERDEFNUM entry of the Dynamic +/// Section .dynamic, and also the sh_info member of the section header. +/// The sh_link member of the section header shall point to the section that +/// contains the strings referenced by this section. +/// +/// The .gnu.version_d section shall contain an array of VerDef structures +/// optionally followed by an array of VerDefAux structures. +#[derive(Debug, PartialEq, Eq)] +pub struct VerDef { + /// Version information flag bitmask. + pub vd_flags: u16, + /// VersionIndex value referencing the SHT_GNU_VERSYM section. + pub vd_ndx: u16, + /// Number of associated verdaux array entries. + pub vd_cnt: u16, + /// Version name hash value (ELF hash function). + pub vd_hash: u32, + /// Offset in bytes to a corresponding entry in an array of VerDefAux structures. + vd_aux: u32, + /// Offset to the next VerDef entry, in bytes. + vd_next: u32, +} + +impl ParseAt for VerDef { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + let vd_version = endian.parse_u16_at(offset, data)?; + if vd_version != abi::VER_DEF_CURRENT { + return Err(ParseError::UnsupportedVersion(( + vd_version as u64, + abi::VER_DEF_CURRENT as u64, + ))); + } + + Ok(VerDef { + vd_flags: endian.parse_u16_at(offset, data)?, + vd_ndx: endian.parse_u16_at(offset, data)?, + vd_cnt: endian.parse_u16_at(offset, data)?, + vd_hash: endian.parse_u32_at(offset, data)?, + vd_aux: endian.parse_u32_at(offset, data)?, + vd_next: endian.parse_u32_at(offset, data)?, + }) + } + + #[inline] + fn size_for(_class: Class) -> usize { + ELFVERDEFSIZE + } +} + +const ELFVERDEFSIZE: usize = 20; + +#[derive(Debug, Clone, Copy)] +pub struct VerDefIterator<'data, E: EndianParse> { + endian: E, + class: Class, + /// The number of entries in this iterator is given by the .dynamic DT_VERDEFNUM entry + /// and also in the .gnu.version_d section header's sh_info field. + count: u64, + data: &'data [u8], + offset: usize, +} + +impl<'data, E: EndianParse> VerDefIterator<'data, E> { + pub fn new( + endian: E, + class: Class, + count: u64, + starting_offset: usize, + data: &'data [u8], + ) -> Self { + VerDefIterator { + endian, + class, + count, + data, + offset: starting_offset, + } + } +} + +impl<'data, E: EndianParse> Iterator for VerDefIterator<'data, E> { + type Item = (VerDef, VerDefAuxIterator<'data, E>); + fn next(&mut self) -> Option { + if self.data.is_empty() || self.count == 0 { + return None; + } + + let mut start = self.offset; + let vd = VerDef::parse_at(self.endian, self.class, &mut start, self.data).ok()?; + let vda_iter = VerDefAuxIterator::new( + self.endian, + self.class, + vd.vd_cnt, + self.offset + vd.vd_aux as usize, + self.data, + ); + + // If offset overflows, silently end iteration + match self.offset.checked_add(vd.vd_next as usize) { + Some(new_off) => self.offset = new_off, + None => self.count = 0, + } + self.count -= 1; + + // Silently end iteration early if the next link stops pointing somewhere new + // TODO: Make this an error condition by allowing the iterator to yield a ParseError + if self.count > 0 && vd.vd_next == 0 { + self.count = 0 + } + Some((vd, vda_iter)) + } +} + +/// Version Definition Auxiliary Entries from the .gnu.version_d section +#[derive(Debug, PartialEq, Eq)] +pub struct VerDefAux { + /// Offset to the version or dependency name string in the linked string table, in bytes. + pub vda_name: u32, + /// Offset to the next VerDefAux entry, in bytes. + vda_next: u32, +} + +impl ParseAt for VerDefAux { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + Ok(VerDefAux { + vda_name: endian.parse_u32_at(offset, data)?, + vda_next: endian.parse_u32_at(offset, data)?, + }) + } + + #[inline] + fn size_for(_class: Class) -> usize { + 8 + } +} + +#[derive(Debug)] +pub struct VerDefAuxIterator<'data, E: EndianParse> { + endian: E, + class: Class, + count: u16, + data: &'data [u8], + offset: usize, +} + +impl<'data, E: EndianParse> VerDefAuxIterator<'data, E> { + pub fn new( + endian: E, + class: Class, + count: u16, + starting_offset: usize, + data: &'data [u8], + ) -> Self { + VerDefAuxIterator { + endian, + class, + count, + data, + offset: starting_offset, + } + } +} + +impl<'data, E: EndianParse> Iterator for VerDefAuxIterator<'data, E> { + type Item = VerDefAux; + fn next(&mut self) -> Option { + if self.data.is_empty() || self.count == 0 { + return None; + } + + // N.B. This offset handling is maybe unnecessary, but faithful to the + // spec. As far as I've observed, VerDefAux entries for a VerDef are all + // encoded sequentially after the VerDef, so we could likely just + // use the normal pattern here and pass in &mut self.offset here. + // + // The spec claims that "The section shall contain an array of + // Elfxx_Verdef structures, optionally followed by an array of + // Elfxx_Verdaux structures." This reads a bit ambiguously + // (is there one big array of Verdefs followed by one big array of + // Verdauxs?). If so, the vd_next and vda_next links seem unnecessary + // given the vd_cnt field. In practice, it appears that all the VerDefAux + // fields for a given VerDef are sequentially following the VerDef, meaning + // they're contiguous, but intersersed. The _next fields could theoretically + // give non-contiguous linked-list-like configurations, though (but only linking + // forward, not backward, since the link is a u32). + // + // The vd_next and vda_next fields are also not "pointers" i.e. offsets from + // the start of the section, but rather "increments" in telling how far to + // advance from where you just read the containing struct for where you should + // read the next. Given the sequentially-following nature described, these vd_next + // and vda_next fields end up being 0x14 and 0x8 (the size of the VerDef and + // VerDefAux structs). + // + // So observationally, we could likely get away with using self.offset and count here + // and ignoring the vda_next field, but that'd break things if they weren't contiguous. + let mut start = self.offset; + let vda = VerDefAux::parse_at(self.endian, self.class, &mut start, self.data).ok()?; + + // If offset overflows, silently end iteration + match self.offset.checked_add(vda.vda_next as usize) { + Some(new_off) => self.offset = new_off, + None => self.count = 0, + } + self.count -= 1; + + // Silently end iteration early if the next link stops pointing somewhere new + // TODO: Make this an error condition by allowing the iterator to yield a ParseError + if self.count > 0 && vda.vda_next == 0 { + self.count = 0 + } + Some(vda) + } +} + +/////////////////////////////////////////////////////////////////////////////// +// _ // +// __ _ _ __ _ _ __ _____ _ __ ___(_) ___ _ __ _ __ // +// / _` | '_ \| | | | \ \ / / _ \ '__/ __| |/ _ \| '_ \ | '__| // +// _ | (_| | | | | |_| | _ \ V / __/ | \__ \ | (_) | | | | | | // +// (_) \__, |_| |_|\__,_| (_) \_/ \___|_| |___/_|\___/|_| |_|_____|_| // +// |___/ |_____| // +/////////////////////////////////////////////////////////////////////////////// + +/// The GNU extension section .gnu.version_r has a section type of SHT_GNU_VERNEED. +/// This section contains required symbol version definitions. The number of +/// entries in this section shall be contained in the DT_VERNEEDNUM entry of the +/// Dynamic Section .dynamic and also the sh_info member of the section header. +/// The sh_link member of the section header shall point to the referenced +/// string table section. +/// +/// The section shall contain an array of VerNeed structures optionally +/// followed by an array of VerNeedAux structures. +#[derive(Debug, PartialEq, Eq)] +pub struct VerNeed { + /// Number of associated verneed array entries. + pub vn_cnt: u16, + /// Offset to the file name string in the linked string table, in bytes. + pub vn_file: u32, + /// Offset to a corresponding entry in the VerNeedAux array, in bytes. + vn_aux: u32, + /// Offset to the next VerNeed entry, in bytes. + vn_next: u32, +} + +impl ParseAt for VerNeed { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + let vd_version = endian.parse_u16_at(offset, data)?; + if vd_version != abi::VER_NEED_CURRENT { + return Err(ParseError::UnsupportedVersion(( + vd_version as u64, + abi::VER_DEF_CURRENT as u64, + ))); + } + Ok(VerNeed { + vn_cnt: endian.parse_u16_at(offset, data)?, + vn_file: endian.parse_u32_at(offset, data)?, + vn_aux: endian.parse_u32_at(offset, data)?, + vn_next: endian.parse_u32_at(offset, data)?, + }) + } + + #[inline] + fn size_for(_class: Class) -> usize { + ELFVERNEEDSIZE + } +} + +const ELFVERNEEDSIZE: usize = 16; + +#[derive(Debug, Copy, Clone)] +pub struct VerNeedIterator<'data, E: EndianParse> { + endian: E, + class: Class, + /// The number of entries in this iterator is given by the .dynamic DT_VERNEEDNUM entry + /// and also in the .gnu.version_r section header's sh_info field. + count: u64, + data: &'data [u8], + offset: usize, +} + +impl<'data, E: EndianParse> VerNeedIterator<'data, E> { + pub fn new( + endian: E, + class: Class, + count: u64, + starting_offset: usize, + data: &'data [u8], + ) -> Self { + VerNeedIterator { + endian, + class, + count, + data, + offset: starting_offset, + } + } +} + +impl<'data, E: EndianParse> Iterator for VerNeedIterator<'data, E> { + type Item = (VerNeed, VerNeedAuxIterator<'data, E>); + fn next(&mut self) -> Option { + if self.data.is_empty() || self.count == 0 { + return None; + } + + let mut start = self.offset; + let vn = VerNeed::parse_at(self.endian, self.class, &mut start, self.data).ok()?; + let vna_iter = VerNeedAuxIterator::new( + self.endian, + self.class, + vn.vn_cnt, + self.offset + vn.vn_aux as usize, + self.data, + ); + + // If offset overflows, silently end iteration + match self.offset.checked_add(vn.vn_next as usize) { + Some(new_off) => self.offset = new_off, + None => self.count = 0, + } + self.count -= 1; + + // Silently end iteration early if the next link stops pointing somewhere new + // TODO: Make this an error condition by allowing the iterator to yield a ParseError + if self.count > 0 && vn.vn_next == 0 { + self.count = 0 + } + Some((vn, vna_iter)) + } +} + +/// Version Need Auxiliary Entries from the .gnu.version_r section +#[derive(Debug, PartialEq, Eq)] +pub struct VerNeedAux { + /// Dependency name hash value (ELF hash function). + pub vna_hash: u32, + /// Dependency information flag bitmask. + pub vna_flags: u16, + /// VersionIndex value used in the .gnu.version symbol version array. + pub vna_other: u16, + /// Offset to the dependency name string in the linked string table, in bytes. + pub vna_name: u32, + /// Offset to the next vernaux entry, in bytes. + vna_next: u32, +} + +impl ParseAt for VerNeedAux { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + Ok(VerNeedAux { + vna_hash: endian.parse_u32_at(offset, data)?, + vna_flags: endian.parse_u16_at(offset, data)?, + vna_other: endian.parse_u16_at(offset, data)?, + vna_name: endian.parse_u32_at(offset, data)?, + vna_next: endian.parse_u32_at(offset, data)?, + }) + } + + #[inline] + fn size_for(_class: Class) -> usize { + 16 + } +} + +#[derive(Debug)] +pub struct VerNeedAuxIterator<'data, E: EndianParse> { + endian: E, + class: Class, + count: u16, + data: &'data [u8], + offset: usize, +} + +impl<'data, E: EndianParse> VerNeedAuxIterator<'data, E> { + pub fn new( + endian: E, + class: Class, + count: u16, + starting_offset: usize, + data: &'data [u8], + ) -> Self { + VerNeedAuxIterator { + endian, + class, + count, + data, + offset: starting_offset, + } + } +} + +impl<'data, E: EndianParse> Iterator for VerNeedAuxIterator<'data, E> { + type Item = VerNeedAux; + fn next(&mut self) -> Option { + if self.data.is_empty() || self.count == 0 { + return None; + } + + let mut start = self.offset; + let vna = VerNeedAux::parse_at(self.endian, self.class, &mut start, self.data).ok()?; + + // If offset overflows, silently end iteration + match self.offset.checked_add(vna.vna_next as usize) { + Some(new_off) => self.offset = new_off, + None => self.count = 0, + } + self.count -= 1; + + // Silently end iteration early if the next link stops pointing somewhere new + // TODO: Make this an error condition by allowing the iterator to yield a ParseError + if self.count > 0 && vna.vna_next == 0 { + self.count = 0 + } + Some(vna) + } +} + +////////////////////////////// +// _____ _ // +// |_ _|__ ___| |_ ___ // +// | |/ _ \/ __| __/ __| // +// | | __/\__ \ |_\__ \ // +// |_|\___||___/\__|___/ // +// // +////////////////////////////// + +#[cfg(test)] +mod iter_tests { + use super::*; + use crate::endian::LittleEndian; + + #[rustfmt::skip] + const GNU_VERNEED_STRINGS: [u8; 65] = [ + // ZLIB_1.2.0 (0x1) + 0x00, 0x5a, 0x4c, 0x49, 0x42, 0x5f, 0x31, 0x2e, 0x32, 0x2e, 0x30, 0x00, + // GLIBC_2.33 (0xC) + 0x47, 0x4c, 0x49, 0x42, 0x43, 0x5f, 0x32, 0x2e, 0x33, 0x33, 0x00, + // GLIBC_2.2.5 (0x17) + 0x47, 0x4c, 0x49, 0x42, 0x43, 0x5f, 0x32, 0x2e, 0x32, 0x2e, 0x35, 0x00, + // libz.so.1 (0x23) + 0x6c, 0x69, 0x62, 0x7a, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, + // libc.so.6 (0x2D) + 0x6c, 0x69, 0x62, 0x63, 0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00, + // GLIBC_2.3 (0x37) + 0x47, 0x4c, 0x49, 0x42, 0x43, 0x5f, 0x32, 0x2e, 0x33, 0x00, + ]; + + #[rustfmt::skip] + const GNU_VERNEED_DATA: [u8; 96] = [ + // {vn_version, vn_cnt, vn_file, vn_aux, vn_next } + 0x01, 0x00, 0x01, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0xc0, 0xe5, 0x27, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // {vn_version, vn_cnt, vn_file, vn_aux, vn_next } + 0x01, 0x00, 0x03, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0x13, 0x69, 0x69, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0xb3, 0x91, 0x96, 0x06, 0x00, 0x00, 0x0b, 0x00, 0x17, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0x94, 0x91, 0x96, 0x06, 0x00, 0x00, 0x09, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + #[test] + fn verneed_iter() { + let iter = VerNeedIterator::new(LittleEndian, Class::ELF64, 2, 0, &GNU_VERNEED_DATA); + let entries: Vec<(VerNeed, Vec)> = + iter.map(|(vn, iter)| (vn, iter.collect())).collect(); + + assert_eq!(entries.len(), 2); + } + + #[test] + fn verneed_iter_early_termination_on_broken_next_link() { + // set count = 3 even though there's only 2 entries + let iter = VerNeedIterator::new(LittleEndian, Class::ELF64, 3, 0, &GNU_VERNEED_DATA); + let entries: Vec<(VerNeed, Vec)> = + iter.map(|(vn, iter)| (vn, iter.collect())).collect(); + + // TODO: make this a ParseError condition instead of silently returning only the good data. + assert_eq!(entries.len(), 2); + } + + #[test] + fn verneedaux_iter_one_entry() { + let mut iter = + VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 1, 0x10, &GNU_VERNEED_DATA); + let aux1 = iter.next().expect("Failed to parse"); + assert_eq!( + aux1, + VerNeedAux { + vna_hash: 0x0827e5c0, + vna_flags: 0, + vna_other: 0x0a, + vna_name: 0x01, + vna_next: 0 + } + ); + assert!(iter.next().is_none()); + } + + #[test] + fn verneedaux_iter_multiple_entries() { + let mut iter = + VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 3, 0x30, &GNU_VERNEED_DATA); + let aux1 = iter.next().expect("Failed to parse"); + assert_eq!( + aux1, + VerNeedAux { + vna_hash: 0x0d696913, + vna_flags: 0, + vna_other: 0x0c, + vna_name: 0x0c, + vna_next: 0x10 + } + ); + let aux2 = iter.next().expect("Failed to parse"); + assert_eq!( + aux2, + VerNeedAux { + vna_hash: 0x069691b3, + vna_flags: 0, + vna_other: 0x0b, + vna_name: 0x17, + vna_next: 0x10 + } + ); + let aux3 = iter.next().expect("Failed to parse"); + assert_eq!( + aux3, + VerNeedAux { + vna_hash: 0x06969194, + vna_flags: 0, + vna_other: 0x09, + vna_name: 0x37, + vna_next: 0 + } + ); + assert!(iter.next().is_none()); + } + + // Hypothetical case where VerDefAux entries are non-contiguous + #[test] + fn verneedaux_iter_two_lists_interspersed() { + #[rustfmt::skip] + let data: [u8; 64] = [ + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0xc0, 0xe5, 0x27, 0x08, 0x00, 0x00, 0x0a, 0x00, 0xcc, 0x0c, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0x13, 0x69, 0x69, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0xd7, 0x0c, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0xb3, 0x91, 0x96, 0x06, 0x00, 0x00, 0x0b, 0x00, 0xe1, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // {vn_hash, vn_flags, vn_other, vn_name, vn_next } + 0x94, 0x91, 0x96, 0x06, 0x00, 0x00, 0x09, 0x00, 0xec, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let mut iter1 = VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 2, 0, &data); + let mut iter2 = VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 2, 0x10, &data); + + let aux1_1 = iter1.next().expect("Failed to parse"); + assert_eq!( + aux1_1, + VerNeedAux { + vna_hash: 0x0827e5c0, + vna_flags: 0, + vna_other: 0x0a, + vna_name: 0x0ccc, + vna_next: 0x20, + } + ); + let aux2_1 = iter2.next().expect("Failed to parse"); + assert_eq!( + aux2_1, + VerNeedAux { + vna_hash: 0x0d696913, + vna_flags: 0, + vna_other: 0x0c, + vna_name: 0x0cd7, + vna_next: 0x20 + } + ); + let aux1_2 = iter1.next().expect("Failed to parse"); + assert_eq!( + aux1_2, + VerNeedAux { + vna_hash: 0x069691b3, + vna_flags: 0, + vna_other: 0x0b, + vna_name: 0x0ce1, + vna_next: 0 + } + ); + let aux2_2 = iter2.next().expect("Failed to parse"); + assert_eq!( + aux2_2, + VerNeedAux { + vna_hash: 0x06969194, + vna_flags: 0, + vna_other: 0x09, + vna_name: 0x0cec, + vna_next: 0 + } + ); + assert!(iter1.next().is_none()); + assert!(iter2.next().is_none()); + } + + #[test] + fn verneedaux_iter_early_termination_on_broken_next_link() { + // set count = 7 even though there's only 1 entry + let iter = VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 7, 0x10, &GNU_VERNEED_DATA); + let entries: Vec = iter.collect(); + + // TODO: make this a ParseError condition instead of silently returning only the good data. + assert_eq!(entries.len(), 1); + } + + #[rustfmt::skip] + const GNU_VERDEF_STRINGS: [u8; 34] = [ + // LIBCTF_1.0 (0x1) + 0x00, 0x4c, 0x49, 0x42, 0x43, 0x54, 0x46, 0x5f, 0x31, 0x2e, 0x30, 0x00, + // LIBCTF_1.1 (0xC) + 0x4c, 0x49, 0x42, 0x43, 0x54, 0x46, 0x5f, 0x31, 0x2e, 0x31, 0x00, + // LIBCTF_1.2 (0x17) + 0x4c, 0x49, 0x42, 0x43, 0x54, 0x46, 0x5f, 0x31, 0x2e, 0x32, 0x00, + ]; + + // Sample .gnu.version_d section contents + #[rustfmt::skip] + const GNU_VERDEF_DATA: [u8; 128] = [ + // {vd_version, vd_flags, vd_ndx, vd_cnt + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + // vd_hash, vd_aux, + 0xb0, 0x7a, 0x07, 0x0b, 0x14, 0x00, 0x00, 0x00, + // vd_next}, {vda_name, + 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // vda_next}, {vd_version, vd_flags, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // vd_ndx, vd_cnt, vd_hash, + 0x02, 0x00, 0x01, 0x00, 0x70, 0x2f, 0x8f, 0x08, + // vd_aux, vd_next}, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + // {vda_name, vda_next}, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // {vd_version, vd_flags, vd_ndx, vd_cnt + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, + // vd_hash, vd_aux, + 0x71, 0x2f, 0x8f, 0x08, 0x14, 0x00, 0x00, 0x00, + // vd_next}, {vda_name, + 0x24, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + // vda_next}, {vda_name, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + // vda_next}, {vd_version, vd_flags, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // vd_ndx, vd_cnt, vd_hash, + 0x04, 0x00, 0x02, 0x00, 0x72, 0x2f, 0x8f, 0x08, + // vd_aux, vd_next}, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // {vda_name, vda_next}, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + // {vda_name, vda_next}, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + #[test] + fn verdef_iter() { + let iter = VerDefIterator::new(LittleEndian, Class::ELF64, 4, 0, &GNU_VERDEF_DATA); + let entries: Vec<(VerDef, Vec)> = + iter.map(|(vd, iter)| (vd, iter.collect())).collect(); + + assert_eq!(entries.len(), 4); + + assert_eq!( + entries, + vec![ + ( + VerDef { + vd_flags: 1, + vd_ndx: 1, + vd_cnt: 1, + vd_hash: 0x0B077AB0, + vd_aux: 20, + vd_next: 28, + }, + vec![VerDefAux { + vda_name: 0x1, + vda_next: 0 + }] + ), + ( + VerDef { + vd_flags: 0, + vd_ndx: 2, + vd_cnt: 1, + vd_hash: 0x088f2f70, + vd_aux: 20, + vd_next: 28, + }, + vec![VerDefAux { + vda_name: 0xC, + vda_next: 0 + }] + ), + ( + VerDef { + vd_flags: 0, + vd_ndx: 3, + vd_cnt: 2, + vd_hash: 0x088f2f71, + vd_aux: 20, + vd_next: 36, + }, + vec![ + VerDefAux { + vda_name: 0x17, + vda_next: 8 + }, + VerDefAux { + vda_name: 0xC, + vda_next: 0 + } + ] + ), + ( + VerDef { + vd_flags: 0, + vd_ndx: 4, + vd_cnt: 2, + vd_hash: 0x088f2f72, + vd_aux: 20, + vd_next: 0, + }, + vec![ + VerDefAux { + vda_name: 0xC, + vda_next: 8 + }, + VerDefAux { + vda_name: 0x17, + vda_next: 0 + } + ] + ), + ] + ); + } + + #[test] + fn verdef_iter_early_termination_on_broken_next_link() { + // set count = 7 even though there's only 4 entries + let iter = VerDefIterator::new(LittleEndian, Class::ELF64, 7, 0, &GNU_VERDEF_DATA); + let entries: Vec<(VerDef, Vec)> = + iter.map(|(vn, iter)| (vn, iter.collect())).collect(); + + // TODO: make this a ParseError condition instead of silently returning only the good data. + assert_eq!(entries.len(), 4); + } + + #[test] + fn verdefaux_iter_one_entry() { + let mut iter = + VerDefAuxIterator::new(LittleEndian, Class::ELF64, 1, 0x14, &GNU_VERDEF_DATA); + let aux1 = iter.next().expect("Failed to parse"); + assert_eq!( + aux1, + VerDefAux { + vda_name: 0x01, + vda_next: 0 + } + ); + assert!(iter.next().is_none()); + } + + #[test] + fn verdefaux_iter_multiple_entries() { + let mut iter = + VerDefAuxIterator::new(LittleEndian, Class::ELF64, 2, 0x4C, &GNU_VERDEF_DATA); + let aux1 = iter.next().expect("Failed to parse"); + assert_eq!( + aux1, + VerDefAux { + vda_name: 0x17, + vda_next: 8 + } + ); + let aux1 = iter.next().expect("Failed to parse"); + assert_eq!( + aux1, + VerDefAux { + vda_name: 0xC, + vda_next: 0 + } + ); + assert!(iter.next().is_none()); + } + + // Hypothetical case where VerDefAux entries are non-contiguous + #[test] + fn verdefaux_iter_two_lists_interspersed() { + #[rustfmt::skip] + let data: [u8; 32] = [ + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // list 1 entry 1 + 0xA1, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // list 2 entry 1 + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // list 1 entry 2 + 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // list 2 entry 2 + ]; + + let mut iter1 = VerDefAuxIterator::new(LittleEndian, Class::ELF64, 2, 0, &data); + let mut iter2 = VerDefAuxIterator::new(LittleEndian, Class::ELF64, 2, 8, &data); + + let aux1_1 = iter1.next().expect("Failed to parse"); + assert_eq!( + aux1_1, + VerDefAux { + vda_name: 0x0001, + vda_next: 0x10, + } + ); + let aux2_1 = iter2.next().expect("Failed to parse"); + assert_eq!( + aux2_1, + VerDefAux { + vda_name: 0x00A1, + vda_next: 0x10, + } + ); + let aux1_2 = iter1.next().expect("Failed to parse"); + assert_eq!( + aux1_2, + VerDefAux { + vda_name: 0x0002, + vda_next: 0, + } + ); + let aux2_2 = iter2.next().expect("Failed to parse"); + assert_eq!( + aux2_2, + VerDefAux { + vda_name: 0x00A2, + vda_next: 0, + } + ); + assert!(iter1.next().is_none()); + assert!(iter2.next().is_none()); + } + + #[test] + fn verdefaux_iter_early_termination_on_broken_next_link() { + // set count = 7 even though there's only 1 entry + let iter = VerDefAuxIterator::new(LittleEndian, Class::ELF64, 7, 0x14, &GNU_VERDEF_DATA); + let entries: Vec = iter.collect(); + + // TODO: make this a ParseError condition instead of silently returning only the good data. + assert_eq!(entries.len(), 1); + } + + #[test] + fn version_table() { + let ver_idx_buf: [u8; 10] = [0x02, 0x00, 0x03, 0x00, 0x09, 0x00, 0x0A, 0x00, 0xff, 0xff]; + let version_ids = VersionIndexTable::new(LittleEndian, Class::ELF64, &ver_idx_buf); + let verdefs = VerDefIterator::new(LittleEndian, Class::ELF64, 4, 0, &GNU_VERDEF_DATA); + let verneed_strs = StringTable::new(&GNU_VERNEED_STRINGS); + let verneeds = VerNeedIterator::new(LittleEndian, Class::ELF64, 2, 0, &GNU_VERNEED_DATA); + let verdef_strs = StringTable::new(&GNU_VERDEF_STRINGS); + + let table = SymbolVersionTable::new( + version_ids, + Some((verneeds, verneed_strs)), + Some((verdefs, verdef_strs)), + ); + + let def1 = table + .get_definition(0) + .expect("Failed to parse definition") + .expect("Failed to find def"); + assert_eq!(def1.hash, 0x088f2f70); + assert_eq!(def1.flags, 0); + let def1_names: Vec<&str> = def1 + .names + .map(|res| res.expect("Failed to parse")) + .collect(); + assert_eq!(def1_names, ["LIBCTF_1.1"]); + assert!(!def1.hidden); + + let def2 = table + .get_definition(1) + .expect("Failed to parse definition") + .expect("Failed to find def"); + assert_eq!(def2.hash, 0x088f2f71); + assert_eq!(def2.flags, 0); + let def2_names: Vec<&str> = def2 + .names + .map(|res| res.expect("Failed to parse")) + .collect(); + assert_eq!(def2_names, ["LIBCTF_1.2", "LIBCTF_1.1"]); + assert!(!def2.hidden); + + let req1 = table + .get_requirement(2) + .expect("Failed to parse definition") + .expect("Failed to find req"); + assert_eq!( + req1, + SymbolRequirement { + file: "libc.so.6", + name: "GLIBC_2.3", + hash: 0x6969194, + flags: 0, + hidden: false + } + ); + + let req2 = table + .get_requirement(3) + .expect("Failed to parse definition") + .expect("Failed to find req"); + assert_eq!( + req2, + SymbolRequirement { + file: "libz.so.1", + name: "ZLIB_1.2.0", + hash: 0x827E5C0, + flags: 0, + hidden: false + } + ); + + // The last version_index points to non-existent definitions. Maybe we should treat + // this as a ParseError instead of returning an empty Option? This can only happen + // if .gnu.versions[N] contains an index that doesn't exist, which is likely a file corruption + // or programmer error (i.e asking for a definition for an undefined symbol) + assert!(table.get_definition(4).expect("Failed to parse").is_none()); + assert!(table.get_requirement(4).expect("Failed to parse").is_none()); + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_verndx32_lsb() { + test_parse_for(LittleEndian, Class::ELF32, VersionIndex(0x0100)); + } + + #[test] + fn parse_verndx32_msb() { + test_parse_for(BigEndian, Class::ELF32, VersionIndex(0x0001)); + } + + #[test] + fn parse_verndx64_lsb() { + test_parse_for(LittleEndian, Class::ELF64, VersionIndex(0x0100)); + } + + #[test] + fn parse_verndx64_msb() { + test_parse_for(BigEndian, Class::ELF64, VersionIndex(0x0001)); + } + + #[test] + fn parse_verndx32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VersionIndex>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_verndx32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VersionIndex>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_verndx64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VersionIndex>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_verndx64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VersionIndex>(BigEndian, Class::ELF64); + } + + // + // VerDef + // + #[test] + fn parse_verdef32_lsb() { + let mut data = [0u8; ELFVERDEFSIZE]; + for (n, elem) in data.iter_mut().enumerate().take(ELFVERDEFSIZE) { + *elem = n as u8; + } + data[0] = 1; + data[1] = 0; + + let mut offset = 0; + let entry = VerDef::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref()) + .expect("Failed to parse VerDef"); + + assert_eq!( + entry, + VerDef { + vd_flags: 0x0302, + vd_ndx: 0x0504, + vd_cnt: 0x0706, + vd_hash: 0x0B0A0908, + vd_aux: 0x0F0E0D0C, + vd_next: 0x13121110, + } + ); + assert_eq!(offset, ELFVERDEFSIZE); + } + + #[test] + fn parse_verdef32_fuzz_too_short() { + let mut data = [0u8; ELFVERDEFSIZE]; + data[1] = 1; + for n in 0..ELFVERDEFSIZE { + let buf = data.split_at(n).0; + let mut offset: usize = 0; + let error = VerDef::parse_at(BigEndian, Class::ELF32, &mut offset, buf) + .expect_err("Expected an error"); + assert!( + matches!(error, ParseError::SliceReadError(_)), + "Unexpected Error type found: {error}" + ); + } + } + + #[test] + fn parse_verdef64_msb() { + let mut data = [0u8; ELFVERDEFSIZE]; + for (n, elem) in data.iter_mut().enumerate().take(ELFVERDEFSIZE).skip(2) { + *elem = n as u8; + } + data[1] = 1; + + let mut offset = 0; + let entry = VerDef::parse_at(BigEndian, Class::ELF64, &mut offset, data.as_ref()) + .expect("Failed to parse VerDef"); + + assert_eq!( + entry, + VerDef { + vd_flags: 0x0203, + vd_ndx: 0x0405, + vd_cnt: 0x0607, + vd_hash: 0x08090A0B, + vd_aux: 0x0C0D0E0F, + vd_next: 0x10111213, + } + ); + assert_eq!(offset, ELFVERDEFSIZE); + } + + #[test] + fn parse_verdef64_fuzz_too_short() { + let mut data = [0u8; ELFVERDEFSIZE]; + data[1] = 1; + for n in 0..ELFVERDEFSIZE { + let buf = data.split_at(n).0; + let mut offset: usize = 0; + let error = VerDef::parse_at(BigEndian, Class::ELF64, &mut offset, buf) + .expect_err("Expected an error"); + assert!( + matches!(error, ParseError::SliceReadError(_)), + "Unexpected Error type found: {error}" + ); + } + } + + #[test] + fn parse_verdef_bad_version_errors() { + let data = [0u8; ELFVERDEFSIZE]; + // version is 0, which is not 1, which is bad :) + + let mut offset = 0; + let err = VerDef::parse_at(BigEndian, Class::ELF64, &mut offset, data.as_ref()) + .expect_err("Expected an error"); + assert!( + matches!(err, ParseError::UnsupportedVersion((0, 1))), + "Unexpected Error type found: {err}" + ); + } + + // + // VerDefAux + // + #[test] + fn parse_verdefaux32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + VerDefAux { + vda_name: 0x03020100, + vda_next: 0x07060504, + }, + ); + } + + #[test] + fn parse_verdefaux32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + VerDefAux { + vda_name: 0x00010203, + vda_next: 0x04050607, + }, + ); + } + + #[test] + fn parse_verdefaux64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + VerDefAux { + vda_name: 0x03020100, + vda_next: 0x07060504, + }, + ); + } + + #[test] + fn parse_verdefaux64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + VerDefAux { + vda_name: 0x00010203, + vda_next: 0x04050607, + }, + ); + } + + #[test] + fn parse_verdefaux32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerDefAux>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_verdefaux32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerDefAux>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_verdefaux64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerDefAux>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_verdefaux64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerDefAux>(BigEndian, Class::ELF64); + } + + // + // VerNeed + // + #[test] + fn parse_verneed32_lsb() { + let mut data = [0u8; ELFVERNEEDSIZE]; + for (n, elem) in data.iter_mut().enumerate().take(ELFVERNEEDSIZE) { + *elem = n as u8; + } + data[0] = 1; + data[1] = 0; + + let mut offset = 0; + let entry = VerNeed::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref()) + .expect("Failed to parse VerNeed"); + + assert_eq!( + entry, + VerNeed { + vn_cnt: 0x0302, + vn_file: 0x07060504, + vn_aux: 0x0B0A0908, + vn_next: 0x0F0E0D0C, + } + ); + assert_eq!(offset, ELFVERNEEDSIZE); + } + + #[test] + fn parse_verneed32_fuzz_too_short() { + let mut data = [0u8; ELFVERNEEDSIZE]; + data[1] = 1; + for n in 0..ELFVERNEEDSIZE { + let buf = data.split_at(n).0; + let mut offset: usize = 0; + let error = VerNeed::parse_at(BigEndian, Class::ELF32, &mut offset, buf) + .expect_err("Expected an error"); + assert!( + matches!(error, ParseError::SliceReadError(_)), + "Unexpected Error type found: {error}" + ); + } + } + + #[test] + fn parse_verneed64_msb() { + let mut data = [0u8; ELFVERNEEDSIZE]; + for (n, elem) in data.iter_mut().enumerate().take(ELFVERNEEDSIZE) { + *elem = n as u8; + } + data[1] = 1; + + let mut offset = 0; + let entry = VerNeed::parse_at(BigEndian, Class::ELF64, &mut offset, data.as_ref()) + .expect("Failed to parse VerNeed"); + + assert_eq!( + entry, + VerNeed { + vn_cnt: 0x0203, + vn_file: 0x04050607, + vn_aux: 0x08090A0B, + vn_next: 0x0C0D0E0F, + } + ); + assert_eq!(offset, ELFVERNEEDSIZE); + } + + #[test] + fn parse_verneed64_fuzz_too_short() { + let mut data = [0u8; ELFVERNEEDSIZE]; + data[1] = 1; + for n in 0..ELFVERNEEDSIZE { + let buf = data.split_at(n).0; + let mut offset: usize = 0; + let error = VerNeed::parse_at(BigEndian, Class::ELF64, &mut offset, buf) + .expect_err("Expected an error"); + assert!( + matches!(error, ParseError::SliceReadError(_)), + "Unexpected Error type found: {error}" + ); + } + } + + // + // VerNeedAux + // + #[test] + fn parse_verneedaux32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + VerNeedAux { + vna_hash: 0x03020100, + vna_flags: 0x0504, + vna_other: 0x0706, + vna_name: 0x0B0A0908, + vna_next: 0x0F0E0D0C, + }, + ); + } + + #[test] + fn parse_verneedaux32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + VerNeedAux { + vna_hash: 0x00010203, + vna_flags: 0x0405, + vna_other: 0x0607, + vna_name: 0x08090A0B, + vna_next: 0x0C0D0E0F, + }, + ); + } + + #[test] + fn parse_verneedaux64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + VerNeedAux { + vna_hash: 0x03020100, + vna_flags: 0x0504, + vna_other: 0x0706, + vna_name: 0x0B0A0908, + vna_next: 0x0F0E0D0C, + }, + ); + } + + #[test] + fn parse_verneedaux64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + VerNeedAux { + vna_hash: 0x00010203, + vna_flags: 0x0405, + vna_other: 0x0607, + vna_name: 0x08090A0B, + vna_next: 0x0C0D0E0F, + }, + ); + } + + #[test] + fn parse_verneedaux32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerNeedAux>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_verneedaux32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerNeedAux>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_verneedaux64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerNeedAux>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_verneedaux64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, VerNeedAux>(BigEndian, Class::ELF64); + } +} + +#[cfg(test)] +mod version_index_tests { + use super::*; + + #[test] + fn is_local() { + let idx = VersionIndex(0); + assert!(idx.is_local()); + } + + #[test] + fn is_global() { + let idx = VersionIndex(1); + assert!(idx.is_global()); + } + + #[test] + fn index_visible() { + let idx = VersionIndex(42); + assert_eq!(idx.index(), 42); + assert!(!idx.is_hidden()); + } + + #[test] + fn index_hidden() { + let idx = VersionIndex(42 | abi::VER_NDX_HIDDEN); + assert_eq!(idx.index(), 42); + assert!(idx.is_hidden()); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/hash.rs b/mikros_std_deps/elf-0.7.4/src/hash.rs new file mode 100644 index 00000000000..eb461f4127c --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/hash.rs @@ -0,0 +1,495 @@ +//! Parsing hash table sections for symbol tables: `.hash`, and `.gnu.hash` +use core::mem::size_of; + +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ParsingTable, ReadBytesExt}; +use crate::string_table::StringTable; +use crate::symbol::{Symbol, SymbolTable}; + +impl ParseAt for u32 { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + endian.parse_u32_at(offset, data) + } + + #[inline] + fn size_for(_class: Class) -> usize { + core::mem::size_of::() + } +} + +type U32Table<'data, E> = ParsingTable<'data, E, u32>; + +/// Header at the start of SysV Hash Table sections of type [SHT_HASH](crate::abi::SHT_HASH). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SysVHashHeader { + pub nbucket: u32, + pub nchain: u32, +} + +impl ParseAt for SysVHashHeader { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + Ok(SysVHashHeader { + nbucket: endian.parse_u32_at(offset, data)?, + nchain: endian.parse_u32_at(offset, data)?, + }) + } + + #[inline] + fn size_for(_class: Class) -> usize { + size_of::() + size_of::() + } +} + +/// Calculate the SysV hash value for a given symbol name. +pub fn sysv_hash(name: &[u8]) -> u32 { + let mut hash = 0u32; + for byte in name { + hash = hash.wrapping_mul(16).wrapping_add(*byte as u32); + hash ^= (hash >> 24) & 0xf0; + } + hash & 0xfffffff +} + +#[derive(Debug)] +pub struct SysVHashTable<'data, E: EndianParse> { + buckets: U32Table<'data, E>, + chains: U32Table<'data, E>, +} + +/// This constructs a lazy-parsing type that keeps a reference to the provided data +/// bytes from which it lazily parses and interprets its contents. +impl<'data, E: EndianParse> SysVHashTable<'data, E> { + /// Construct a SysVHashTable from given bytes. Keeps a reference to the data for lazy parsing. + pub fn new(endian: E, class: Class, data: &'data [u8]) -> Result { + let mut offset = 0; + let hdr = SysVHashHeader::parse_at(endian, class, &mut offset, data)?; + + let buckets_size = size_of::() + .checked_mul(hdr.nbucket.try_into()?) + .ok_or(ParseError::IntegerOverflow)?; + let buckets_end = offset + .checked_add(buckets_size) + .ok_or(ParseError::IntegerOverflow)?; + let buckets_buf = data.get_bytes(offset..buckets_end)?; + let buckets = U32Table::new(endian, class, buckets_buf); + offset = buckets_end; + + let chains_size = size_of::() + .checked_mul(hdr.nchain.try_into()?) + .ok_or(ParseError::IntegerOverflow)?; + let chains_end = offset + .checked_add(chains_size) + .ok_or(ParseError::IntegerOverflow)?; + let chains_buf = data.get_bytes(offset..chains_end)?; + let chains = U32Table::new(endian, class, chains_buf); + + Ok(SysVHashTable { buckets, chains }) + } + + /// Use the hash table to find the symbol table entry with the given name and hash. + pub fn find( + &self, + name: &[u8], + symtab: &SymbolTable<'data, E>, + strtab: &StringTable<'data>, + ) -> Result, ParseError> { + // empty hash tables don't have any entries. This avoids a divde by zero in the modulus calculation + if self.buckets.is_empty() { + return Ok(None); + } + + let hash = sysv_hash(name); + + let start = (hash as usize) % self.buckets.len(); + let mut index = self.buckets.get(start)? as usize; + + // Bound the number of chain lookups by the chain size so we don't loop forever + let mut i = 0; + while index != 0 && i < self.chains.len() { + let symbol = symtab.get(index)?; + if strtab.get_raw(symbol.st_name as usize)? == name { + return Ok(Some((index, symbol))); + } + + index = self.chains.get(index)? as usize; + i += 1; + } + Ok(None) + } +} + +/// Calculate the GNU hash for a given symbol name. +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 +} + +/// Header at the start of a GNU extension Hash Table section of type [SHT_GNU_HASH](crate::abi::SHT_GNU_HASH). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GnuHashHeader { + pub nbucket: u32, + /// The symbol table index of the first symbol in the hash table. + /// (GNU hash sections omit symbols at the start of the table that wont be looked up) + pub table_start_idx: u32, + /// The number of words in the bloom filter. (must be a non-zero power of 2) + pub nbloom: u32, + /// The bit shift count for the bloom filter. + pub nshift: u32, +} + +impl ParseAt for GnuHashHeader { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + Ok(GnuHashHeader { + nbucket: endian.parse_u32_at(offset, data)?, + table_start_idx: endian.parse_u32_at(offset, data)?, + nbloom: endian.parse_u32_at(offset, data)?, + nshift: endian.parse_u32_at(offset, data)?, + }) + } + + #[inline] + fn size_for(_class: Class) -> usize { + size_of::() + size_of::() + size_of::() + size_of::() + } +} + +type U64Table<'data, E> = ParsingTable<'data, E, u64>; + +impl ParseAt for u64 { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + endian.parse_u64_at(offset, data) + } + + #[inline] + fn size_for(_class: Class) -> usize { + core::mem::size_of::() + } +} + +#[derive(Debug)] +pub struct GnuHashTable<'data, E: EndianParse> { + pub hdr: GnuHashHeader, + + endian: E, + class: Class, + bloom: &'data [u8], + buckets: U32Table<'data, E>, + chains: U32Table<'data, E>, +} + +impl<'data, E: EndianParse> GnuHashTable<'data, E> { + /// Construct a GnuHashTable from given bytes. Keeps a reference to the data for lazy parsing. + pub fn new(endian: E, class: Class, data: &'data [u8]) -> Result { + let mut offset = 0; + let hdr = GnuHashHeader::parse_at(endian, class, &mut offset, data)?; + + // length of the bloom filter in bytes. ELF32 is [u32; nbloom], ELF64 is [u64; nbloom]. + let nbloom: usize = hdr.nbloom as usize; + let bloom_size = match class { + Class::ELF32 => nbloom + .checked_mul(size_of::()) + .ok_or(ParseError::IntegerOverflow)?, + Class::ELF64 => nbloom + .checked_mul(size_of::()) + .ok_or(ParseError::IntegerOverflow)?, + }; + let bloom_end = offset + .checked_add(bloom_size) + .ok_or(ParseError::IntegerOverflow)?; + let bloom_buf = data.get_bytes(offset..bloom_end)?; + offset = bloom_end; + + let buckets_size = size_of::() + .checked_mul(hdr.nbucket.try_into()?) + .ok_or(ParseError::IntegerOverflow)?; + let buckets_end = offset + .checked_add(buckets_size) + .ok_or(ParseError::IntegerOverflow)?; + let buckets_buf = data.get_bytes(offset..buckets_end)?; + let buckets = U32Table::new(endian, class, buckets_buf); + offset = buckets_end; + + // the rest of the section is the chains + let chains_buf = data + .get(offset..) + .ok_or(ParseError::SliceReadError((offset, data.len())))?; + let chains = U32Table::new(endian, class, chains_buf); + + Ok(GnuHashTable { + hdr, + endian, + class, + bloom: bloom_buf, + buckets, + chains, + }) + } + + /// Use the hash table to find the symbol table entry with the given name. + pub fn find( + &self, + name: &[u8], + symtab: &SymbolTable<'data, E>, + strtab: &StringTable<'data>, + ) -> Result, ParseError> { + // empty hash tables don't have any entries. This avoids a divde by zero in the modulus calculation, + // and also avoids a potential division by zero panic in the bloom filter index calculation. + if self.buckets.is_empty() || self.hdr.nbloom == 0 { + return Ok(None); + } + + let hash = gnu_hash(name); + + // Test against bloom filter. + let (bloom_width, filter) = match self.class { + Class::ELF32 => { + let bloom_width: u32 = 8 * size_of::() as u32; // 32 + let bloom_idx = (hash / (bloom_width)) % self.hdr.nbloom; + let bloom_table = U32Table::new(self.endian, self.class, self.bloom); + (bloom_width, bloom_table.get(bloom_idx as usize)? as u64) + } + Class::ELF64 => { + let bloom_width: u32 = 8 * size_of::() as u32; // 64 + let bloom_idx = (hash / (bloom_width)) % self.hdr.nbloom; + let bloom_table = U64Table::new(self.endian, self.class, self.bloom); + (bloom_width, bloom_table.get(bloom_idx as usize)?) + } + }; + + // Check bloom filter for both hashes - symbol is present in the hash table IFF both bits are set. + if filter & (1 << (hash % bloom_width)) == 0 { + return Ok(None); + } + let hash2 = hash + .checked_shr(self.hdr.nshift) + .ok_or(ParseError::IntegerOverflow)?; + if filter & (1 << (hash2 % bloom_width)) == 0 { + return Ok(None); + } + + let table_start_idx = self.hdr.table_start_idx as usize; + let chain_start_idx = self.buckets.get((hash as usize) % self.buckets.len())? as usize; + if chain_start_idx < table_start_idx { + // All symbols before table_start_idx don't exist in the hash table + return Ok(None); + } + + let chain_len = self.chains.len(); + for chain_idx in (chain_start_idx - table_start_idx)..chain_len { + let chain_hash = self.chains.get(chain_idx)?; + + // compare the hashes by or'ing the 1's bit back on + if hash | 1 == chain_hash | 1 { + // we have a hash match! + // let's see if this symtab[sym_idx].name is what we're looking for + let sym_idx = chain_idx + .checked_add(table_start_idx) + .ok_or(ParseError::IntegerOverflow)?; + let symbol = symtab.get(sym_idx)?; + let r_sym_name = strtab.get_raw(symbol.st_name as usize)?; + + if r_sym_name == name { + return Ok(Some((sym_idx, symbol))); + } + } + + // the chain uses the 1's bit to signal chain comparison stoppage + if chain_hash & 1 != 0 { + break; + } + } + + Ok(None) + } +} + +#[cfg(test)] +mod sysv_parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_sysvhdr32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + SysVHashHeader { + nbucket: 0x03020100, + nchain: 0x07060504, + }, + ); + } + + #[test] + fn parse_sysvhdr32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + SysVHashHeader { + nbucket: 0x00010203, + nchain: 0x04050607, + }, + ); + } + + #[test] + fn parse_sysvhdr64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + SysVHashHeader { + nbucket: 0x03020100, + nchain: 0x07060504, + }, + ); + } + + #[test] + fn parse_sysvhdr64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + SysVHashHeader { + nbucket: 0x00010203, + nchain: 0x04050607, + }, + ); + } + + #[test] + fn parse_sysvhdr32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SysVHashHeader>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_sysvhdr32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SysVHashHeader>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_sysvhdr64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SysVHashHeader>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_sysvhdr64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SysVHashHeader>(BigEndian, Class::ELF64); + } +} + +#[cfg(test)] +mod gnu_parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn gnu_hash_tests() { + // some known example hash values + assert_eq!(gnu_hash(b""), 0x00001505); + assert_eq!(gnu_hash(b"printf"), 0x156b2bb8); + assert_eq!(gnu_hash(b"exit"), 0x7c967e3f); + assert_eq!(gnu_hash(b"syscall"), 0xbac212a0); + } + + #[test] + fn parse_gnuhdr32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + GnuHashHeader { + nbucket: 0x03020100, + table_start_idx: 0x07060504, + nbloom: 0x0B0A0908, + nshift: 0x0F0E0D0C, + }, + ); + } + + #[test] + fn parse_gnuhdr32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + GnuHashHeader { + nbucket: 0x00010203, + table_start_idx: 0x04050607, + nbloom: 0x008090A0B, + nshift: 0x0C0D0E0F, + }, + ); + } + + #[test] + fn parse_gnuhdr64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + GnuHashHeader { + nbucket: 0x03020100, + table_start_idx: 0x07060504, + nbloom: 0x0B0A0908, + nshift: 0x0F0E0D0C, + }, + ); + } + + #[test] + fn parse_gnuhdr64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + GnuHashHeader { + nbucket: 0x00010203, + table_start_idx: 0x04050607, + nbloom: 0x008090A0B, + nshift: 0x0C0D0E0F, + }, + ); + } + + #[test] + fn parse_gnuhdr32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, GnuHashHeader>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_gnuhdr32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, GnuHashHeader>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_gnuhdr64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, GnuHashHeader>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_gnuhdr64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, GnuHashHeader>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/lib.rs b/mikros_std_deps/elf-0.7.4/src/lib.rs new file mode 100644 index 00000000000..5ac480ba757 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/lib.rs @@ -0,0 +1,163 @@ +//! The `elf` crate provides a pure-safe-rust interface for reading ELF object files. +//! +//! # Capabilities +//! +//! ### ✨ Works in `no_std` environments ✨ +//! This crate provides an elf parsing interface which does not allocate or use any std +//! features, so it can be used in `no_std` environments such as kernels and bootloaders. +//! The no_std variant merely disables the additional stream-oriented `std:: Read + Seek` interface. +//! All core parsing functionality is the same! +//! +//! ### ✨ Endian-aware ✨ +//! This crate handles translating between file and host endianness when +//! parsing the ELF contents and provides four endian parsing implementations +//! optimized to support the different common use-cases for an ELF parsing library. +//! Parsing is generic across the specifications and each trait impl represents a +//! specification that encapsulates an interface for parsing integers from some +//! set of allowed byte orderings. +//! +//! * [AnyEndian](endian::AnyEndian): Dynamically parsing either byte order at runtime based on the type of ELF object being parsed. +//! * [BigEndian](endian::BigEndian)/[LittleEndian](endian::LittleEndian): For tools that know they only want to parse a single given byte order known at compile time. +//! * [NativeEndian](type@endian::NativeEndian): For tools that know they want to parse the same byte order as the compilation target's byte order. +//! +//! When the limited specifications are used, errors are properly returned when asked to parse an ELF file +//! with an unexpected byte ordering. +//! +//! ### ✨ Zero-alloc parser ✨ +//! This crate implements parsing in a way that avoids heap allocations. ELF structures +//! are parsed and stored on the stack and provided by patterns such as lazily parsed iterators +//! that yield stack allocated rust types, or lazily parsing tables that only parse out a particular +//! entry on table.get(index). The structures are copy-converted as needed from the underlying file +//! data into Rust's native struct representation. +//! +//! ### ✨ Fuzz Tested ✨ +//! Various parts of the library are fuzz tested for panics and crashes (see `fuzz/`). +//! +//! Memory safety is a core goal, as is providing a safe interface that errors on bad data +//! over crashing or panicking. Checked integer math is used where appropriate, and ParseErrors are +//! returned when bad or corrupted ELF structures are encountered. +//! +//! ### ✨ Uses only safe interfaces ✨ +//! With memory safety a core goal, this crate contains zero unsafe code blocks +//! of its own and only uses safe interface methods from core and std, so you can +//! trust in rust's memory safety guarantees without also having to trust this +//! library developer as having truly been "right" in why some unsafe block was +//! safe. 💃 +//! +//! Note: I'd love to see this crate be enhanced further once rust provides safe transmutes. +//! +//! See: +//! +//! ### ✨ Some zero-copy interfaces ✨ +//! The StringTable, for instance, yields `&[u8]` and `&str` backed by the raw string table bytes. +//! +//! The [ElfBytes] parser type also does not make raw copies of the underlying file data to back +//! the parser lazy parser interfaces `ParsingIterator` and `ParsingTable`. They merely wrap byte slices +//! internally, and yield rust repr values on demand, which does entail copying of the bytes into the +//! parsed rust-native format. +//! +//! Depending on the use-case, it can be more efficient to restructure the raw ELF into different layouts +//! for more efficient interpretation, say, by re-indexing a flat table into a HashMap. `ParsingIterator`s +//! make that easy and rustily-intuitive. +//! +//! The `ParsingIterator`s are also nice in that you can easily zip/enumerate/filter/collect them +//! how you wish. Do you know that you want to do multiple passes over pairs from different tables? Just +//! zip/collect them into another type so you only parse/endian-flip each entry once! +//! +//! ### ✨ Stream-based lazy i/o interface ✨ +//! The [ElfStream] parser type takes a `std:: Read + Seek` (such as `std::fs::File`) where ranges of +//! file contents are read lazily on-demand based on what the user wants to parse. +//! +//! This, alongside the bytes-oriented interface, allow you to decide which tradeoffs +//! you want to make. If you're going to be working with the whole file contents, +//! then the byte slice approach is probably worthwhile to minimize i/o overhead by +//! streaming the whole file into memory at once. If you're only going to be +//! inspecting part of the file, then the [ElfStream] approach would help avoid the +//! overhead of reading a bunch of unused file data just to parse out a few things, (like +//! grabbing the `.gnu.note.build-id`) +//! +//! ### ✨ Tiny library with no dependencies and fast compilation times ✨ +//! Release-target compilation times on this developer's 2021 m1 macbook are sub-second. +//! +//! Example using [ElfBytes]: +//! ``` +//! use elf::ElfBytes; +//! use elf::endian::AnyEndian; +//! use elf::note::Note; +//! use elf::note::NoteGnuBuildId; +//! use elf::section::SectionHeader; +//! +//! let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); +//! let file_data = std::fs::read(path).expect("Could not read file."); +//! let slice = file_data.as_slice(); +//! let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); +//! +//! // Get the ELF file's build-id +//! let abi_shdr: SectionHeader = file +//! .section_header_by_name(".note.gnu.build-id") +//! .expect("section table should be parseable") +//! .expect("file should have a .note.ABI-tag section"); +//! +//! let notes: Vec = file +//! .section_data_as_notes(&abi_shdr) +//! .expect("Should be able to get note section data") +//! .collect(); +//! assert_eq!( +//! notes[0], +//! Note::GnuBuildId(NoteGnuBuildId( +//! &[140, 51, 19, 23, 221, 90, 215, 131, 169, 13, +//! 210, 183, 215, 77, 216, 175, 167, 110, 3, 209])) +//! ); +//! +//! // Find lazy-parsing types for the common ELF sections (we want .dynsym, .dynstr, .hash) +//! let common = file.find_common_data().expect("shdrs should parse"); +//! let (dynsyms, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap()); +//! let hash_table = common.sysv_hash.unwrap(); +//! +//! // Use the hash table to find a given symbol in it. +//! let name = b"memset"; +//! let (sym_idx, sym) = hash_table.find(name, &dynsyms, &strtab) +//! .expect("hash table and symbols should parse").unwrap(); +//! +//! // Verify that we got the same symbol from the hash table we expected +//! assert_eq!(sym_idx, 2); +//! assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset"); +//! assert_eq!(sym, dynsyms.get(sym_idx).unwrap()); +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(feature = "nightly", not(feature = "std")), feature(error_in_core))] +#![warn(rust_2018_idioms)] +#![deny(missing_debug_implementations)] +#![forbid(unsafe_code)] + +pub mod abi; + +pub mod compression; +pub mod dynamic; +pub mod file; +pub mod gnu_symver; +pub mod hash; +pub mod note; +pub mod relocation; +pub mod section; +pub mod segment; +pub mod string_table; +pub mod symbol; + +#[cfg(feature = "to_str")] +pub mod to_str; + +pub mod endian; +pub mod parse; + +mod elf_bytes; +pub use elf_bytes::CommonElfData; +pub use elf_bytes::ElfBytes; + +#[cfg(feature = "std")] +mod elf_stream; +#[cfg(feature = "std")] +pub use elf_stream::ElfStream; + +pub use parse::ParseError; diff --git a/mikros_std_deps/elf-0.7.4/src/note.rs b/mikros_std_deps/elf-0.7.4/src/note.rs new file mode 100644 index 00000000000..c196083c29e --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/note.rs @@ -0,0 +1,599 @@ +//! Parsing ELF notes: `.note.*`, [SHT_NOTE](crate::abi::SHT_NOTE), [PT_NOTE](crate::abi::PT_NOTE) +//! +//! Example for getting the GNU ABI-tag note: +//! ``` +//! use elf::ElfBytes; +//! use elf::endian::AnyEndian; +//! use elf::note::Note; +//! use elf::note::NoteGnuAbiTag; +//! +//! let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); +//! let file_data = std::fs::read(path).expect("Could not read file."); +//! let slice = file_data.as_slice(); +//! let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); +//! +//! let shdr = file +//! .section_header_by_name(".note.ABI-tag") +//! .expect("section table should be parseable") +//! .expect("file should have a .note.ABI-tag section"); +//! +//! let notes: Vec<_> = file +//! .section_data_as_notes(&shdr) +//! .expect("Should be able to get note section data") +//! .collect(); +//! assert_eq!( +//! notes[0], +//! Note::GnuAbiTag(NoteGnuAbiTag { +//! os: 0, +//! major: 2, +//! minor: 6, +//! subminor: 32 +//! }) +//! ); +//! ``` +use crate::abi; +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ReadBytesExt}; +use core::mem::size_of; +use core::str::from_utf8; + +/// This enum contains parsed Note variants which can be matched on +#[derive(Debug, PartialEq, Eq)] +pub enum Note<'data> { + /// (name: [abi::ELF_NOTE_GNU], n_type: [abi::NT_GNU_ABI_TAG]) + GnuAbiTag(NoteGnuAbiTag), + /// (name: [abi::ELF_NOTE_GNU], n_type: [abi::NT_GNU_BUILD_ID]) + GnuBuildId(NoteGnuBuildId<'data>), + /// All other notes that we don't know how to parse + Unknown(NoteAny<'data>), +} + +impl<'data> Note<'data> { + fn parse_at( + endian: E, + _class: Class, + align: usize, + offset: &mut usize, + data: &'data [u8], + ) -> Result { + // We don't know what to do if the section or segment header specified a zero alignment, so error + // (this is likely a file corruption) + if align == 0 { + return Err(ParseError::UnexpectedAlignment(align)); + } + + // It looks like clang and gcc emit 32-bit notes for 64-bit files, so we + // currently always parse all note headers as 32-bit. + let nhdr = NoteHeader::parse_at(endian, Class::ELF32, offset, data)?; + + let name_start = *offset; + let name_buf_size: usize = nhdr.n_namesz.saturating_sub(1).try_into()?; + let name_buf_end = name_start + .checked_add(name_buf_size) + .ok_or(ParseError::IntegerOverflow)?; + let name_buf = data.get_bytes(name_start..name_buf_end)?; + let name = from_utf8(name_buf)?; + + // move forward for entire namesz, including the NUL byte we left out of our str + *offset = (*offset) + .checked_add(nhdr.n_namesz.try_into()?) + .ok_or(ParseError::IntegerOverflow)?; + + // skip over padding if needed to get back to 4-byte alignment + if *offset % align > 0 { + *offset = (*offset) + .checked_add(align - *offset % align) + .ok_or(ParseError::IntegerOverflow)?; + } + + let desc_start = *offset; + let desc_size: usize = nhdr.n_descsz.try_into()?; + let desc_end = desc_start + .checked_add(desc_size) + .ok_or(ParseError::IntegerOverflow)?; + let raw_desc = data.get_bytes(desc_start..desc_end)?; + *offset = desc_end; + + // skip over padding if needed to get back to 4-byte alignment + if *offset % align > 0 { + *offset = (*offset) + .checked_add(align - *offset % align) + .ok_or(ParseError::IntegerOverflow)?; + } + + // Interpret the note contents to try to return a known note variant + match name { + abi::ELF_NOTE_GNU => match nhdr.n_type { + abi::NT_GNU_ABI_TAG => { + let mut offset = 0; + Ok(Note::GnuAbiTag(NoteGnuAbiTag::parse_at( + endian, + _class, + &mut offset, + raw_desc, + )?)) + } + abi::NT_GNU_BUILD_ID => Ok(Note::GnuBuildId(NoteGnuBuildId(raw_desc))), + _ => Ok(Note::Unknown(NoteAny { + n_type: nhdr.n_type, + name, + desc: raw_desc, + })), + }, + _ => Ok(Note::Unknown(NoteAny { + n_type: nhdr.n_type, + name, + desc: raw_desc, + })), + } + } +} + +/// Contains four 4-byte integers. +/// The first 4-byte integer specifies the os. The second, third, and fourth +/// 4-byte integers contain the earliest compatible kernel version. +/// For example, if the 3 integers are 6, 0, and 7, this signifies a 6.0.7 kernel. +/// +/// (see: ) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NoteGnuAbiTag { + pub os: u32, + pub major: u32, + pub minor: u32, + pub subminor: u32, +} + +impl ParseAt for NoteGnuAbiTag { + fn parse_at( + endian: E, + _class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + Ok(NoteGnuAbiTag { + os: endian.parse_u32_at(offset, data)?, + major: endian.parse_u32_at(offset, data)?, + minor: endian.parse_u32_at(offset, data)?, + subminor: endian.parse_u32_at(offset, data)?, + }) + } + + fn size_for(_class: Class) -> usize { + size_of::() * 4 + } +} + +/// Contains a build ID note which is unique among the set of meaningful contents +/// for ELF files and identical when the output file would otherwise have been identical. +/// This is a zero-copy type which merely contains a slice of the note data from which it was parsed. +/// +/// (see: ) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NoteGnuBuildId<'data>(pub &'data [u8]); + +/// Contains the raw fields found in any ELF note. Used for notes that we don't know +/// how to parse into more specific types. +#[derive(Debug, PartialEq, Eq)] +pub struct NoteAny<'data> { + pub n_type: u64, + pub name: &'data str, + pub desc: &'data [u8], +} + +#[derive(Debug)] +pub struct NoteIterator<'data, E: EndianParse> { + endian: E, + class: Class, + align: usize, + data: &'data [u8], + offset: usize, +} + +impl<'data, E: EndianParse> NoteIterator<'data, E> { + pub fn new(endian: E, class: Class, align: usize, data: &'data [u8]) -> Self { + NoteIterator { + endian, + class, + align, + data, + offset: 0, + } + } +} + +impl<'data, E: EndianParse> Iterator for NoteIterator<'data, E> { + type Item = Note<'data>; + fn next(&mut self) -> Option { + if self.data.is_empty() { + return None; + } + + Note::parse_at( + self.endian, + self.class, + self.align, + &mut self.offset, + self.data, + ) + .ok() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct NoteHeader { + pub n_namesz: u64, + pub n_descsz: u64, + pub n_type: u64, +} + +impl ParseAt for NoteHeader { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + match class { + Class::ELF32 => Ok(NoteHeader { + n_namesz: endian.parse_u32_at(offset, data)? as u64, + n_descsz: endian.parse_u32_at(offset, data)? as u64, + n_type: endian.parse_u32_at(offset, data)? as u64, + }), + Class::ELF64 => Ok(NoteHeader { + n_namesz: endian.parse_u64_at(offset, data)?, + n_descsz: endian.parse_u64_at(offset, data)?, + n_type: endian.parse_u64_at(offset, data)?, + }), + } + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 12, + Class::ELF64 => 24, + } + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::abi; + use crate::endian::{BigEndian, LittleEndian}; + + #[test] + fn parse_nt_gnu_abi_tag() { + #[rustfmt::skip] + let data = [ + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + ]; + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) + .expect("Failed to parse"); + + assert_eq!( + note, + Note::GnuAbiTag(NoteGnuAbiTag { + os: abi::ELF_NOTE_GNU_ABI_TAG_OS_LINUX, + major: 2, + minor: 6, + subminor: 32 + }) + ); + } + + #[test] + fn parse_desc_gnu_build_id() { + let data = [ + 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, + 0x55, 0x00, 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, + 0xb0, 0xee, 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, + ]; + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) + .expect("Failed to parse"); + + assert_eq!( + note, + Note::GnuBuildId(NoteGnuBuildId(&[ + 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 0xb0, 0xee, + 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, + ])) + ); + } + + #[test] + fn parse_note_errors_with_zero_alignment() { + // This is a .note.gnu.property section + #[rustfmt::skip] + let data = [ + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, + 0x02, 0x00, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let mut offset = 0; + Note::parse_at(LittleEndian, Class::ELF64, 0, &mut offset, &data) + .expect_err("Should have gotten an alignment error"); + } + #[test] + fn parse_note_with_8_byte_alignment() { + // This is a .note.gnu.property section, which has been seen generated with 8-byte alignment + #[rustfmt::skip] + let data = [ + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, + 0x02, 0x00, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + // Even though the file class is ELF64, we parse it as a 32-bit struct. gcc/clang seem to output 32-bit notes + // even though the gABI states that ELF64 files should contain 64-bit notes. Sometimes those notes are generated + // in sections with 4-byte alignment, and other times with 8-byte alignment, as specified by shdr.sh_addralign. + // + // See https://raw.githubusercontent.com/wiki/hjl-tools/linux-abi/linux-abi-draft.pdf + // Excerpt: + // All entries in a PT_NOTE segment have the same alignment which equals to the + // p_align field in program header. + // According to gABI, each note entry should be aligned to 4 bytes in 32-bit + // objects or 8 bytes in 64-bit objects. But .note.ABI-tag section (see Sec- + // tion 2.1.6) and .note.gnu.build-id section (see Section 2.1.4) are aligned + // to 4 bytes in both 32-bit and 64-bit objects. Note parser should use p_align for + // note alignment, instead of assuming alignment based on ELF file class. + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF64, 8, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::Unknown(NoteAny { + n_type: 5, + name: abi::ELF_NOTE_GNU, + desc: &[ + 0x2, 0x0, 0x0, 0xc0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + ] + }) + ); + } + + #[test] + fn parse_note_with_8_byte_alignment_unaligned_namesz() { + let data = [ + 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // namesz 5, descsz 2 + 0x42, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x55, // type 42 (unknown), name GNUU + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // NUL + 7 pad for 8 alignment + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // desc 0102 + 6 pad for alignment + ]; + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 8, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::Unknown(NoteAny { + n_type: 0x42, + name: &"GNUU", + desc: &[0x01, 0x02], + }) + ); + assert_eq!(offset, 32); + } + + #[test] + fn parse_note_for_elf64_expects_nhdr32() { + let data = [ + 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, + 0x55, 0x00, 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, + 0xb0, 0xee, 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, + ]; + + let mut offset = 0; + // Even though the file class is ELF64, we parse it as a 32-bit struct. gcc/clang seem to output 32-bit notes + // even though the gABI states that ELF64 files should contain 64-bit notes. + let note = Note::parse_at(LittleEndian, Class::ELF64, 4, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::GnuBuildId(NoteGnuBuildId(&[ + 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 0xb0, 0xee, + 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, + ])) + ); + } + + #[test] + fn parse_note_32_lsb() { + let data = [ + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, + ]; + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::Unknown(NoteAny { + n_type: 6, + name: "", + desc: &[0x20, 0x0], + }) + ); + assert_eq!(offset, 16); + } + + #[test] + fn parse_note_32_lsb_with_name_padding() { + let data = [ + 0x03, 0x00, 0x00, 0x00, // namesz 3 + 0x04, 0x00, 0x00, 0x00, // descsz 4 + 0x01, 0x00, 0x00, 0x00, // type 1 + 0x47, 0x4e, 0x00, 0x00, // name GN\0 + 1 pad byte + 0x01, 0x02, 0x03, 0x04, + ]; // desc 01020304 + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::Unknown(NoteAny { + n_type: 1, + name: "GN", + desc: &[0x01, 0x02, 0x03, 0x04], + }) + ); + assert_eq!(offset, 20); + } + + #[test] + fn parse_note_32_lsb_with_desc_padding() { + let data = [ + 0x04, 0x00, 0x00, 0x00, // namesz 4 + 0x02, 0x00, 0x00, 0x00, // descsz 2 + 0x42, 0x00, 0x00, 0x00, // type 42 (unknown) + 0x47, 0x4e, 0x55, 0x00, // name GNU\0 + 0x01, 0x02, 0x00, 0x00, // desc 0102 + 2 pad bytes + ]; + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::Unknown(NoteAny { + n_type: 0x42, + name: abi::ELF_NOTE_GNU, + desc: &[0x01, 0x02], + }) + ); + assert_eq!(offset, 20); + } + + #[test] + fn parse_note_32_lsb_with_no_name() { + let data = [ + 0x00, 0x00, 0x00, 0x00, // namesz 0 + 0x02, 0x00, 0x00, 0x00, // descsz 2 + 0x42, 0x00, 0x00, 0x00, // type 42 (unknown) + 0x20, 0x00, 0x00, 0x00, // desc 20, 00 + 2 pad bytes + ]; + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::Unknown(NoteAny { + n_type: 0x42, + name: "", + desc: &[0x20, 0x0], + }) + ); + assert_eq!(offset, 16); + } + + #[test] + fn parse_note_32_lsb_with_no_desc() { + let data = [ + 0x04, 0x00, 0x00, 0x00, // namesz 4 + 0x00, 0x00, 0x00, 0x00, // descsz 0 + 0x42, 0x00, 0x00, 0x00, // type 42 (unknown) + 0x47, 0x4e, 0x55, 0x00, // name GNU\0 + ]; + + let mut offset = 0; + let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) + .expect("Failed to parse"); + assert_eq!( + note, + Note::Unknown(NoteAny { + n_type: 0x42, + name: abi::ELF_NOTE_GNU, + desc: &[], + }) + ); + assert_eq!(offset, 16); + } + + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_nhdr32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + NoteHeader { + n_namesz: 0x03020100, + n_descsz: 0x07060504, + n_type: 0x0B0A0908, + }, + ); + } + + #[test] + fn parse_nhdr32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + NoteHeader { + n_namesz: 0x00010203, + n_descsz: 0x04050607, + n_type: 0x08090A0B, + }, + ); + } + + #[test] + fn parse_nhdr64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + NoteHeader { + n_namesz: 0x0706050403020100, + n_descsz: 0x0F0E0D0C0B0A0908, + n_type: 0x1716151413121110, + }, + ); + } + + #[test] + fn parse_nhdr64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + NoteHeader { + n_namesz: 0x0001020304050607, + n_descsz: 0x08090A0B0C0D0E0F, + n_type: 0x1011121314151617, + }, + ); + } + + #[test] + fn parse_nhdr32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, NoteHeader>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_nhdr32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, NoteHeader>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_nhdr64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, NoteHeader>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_nhdr64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, NoteHeader>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/parse.rs b/mikros_std_deps/elf-0.7.4/src/parse.rs new file mode 100644 index 00000000000..a47e90559a0 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/parse.rs @@ -0,0 +1,502 @@ +//! Utilities to drive safe and lazy parsing of ELF structures. +use core::{marker::PhantomData, ops::Range}; + +use crate::endian::EndianParse; +use crate::file::Class; + +#[derive(Debug)] +pub enum ParseError { + /// Returned when the ELF File Header's magic bytes weren't ELF's defined + /// magic bytes + BadMagic([u8; 4]), + /// Returned when the ELF File Header's `e_ident[EI_CLASS]` wasn't one of the + /// defined `ELFCLASS*` constants + UnsupportedElfClass(u8), + /// Returned when the ELF File Header's `e_ident[EI_DATA]` wasn't one of the + /// defined `ELFDATA*` constants + UnsupportedElfEndianness(u8), + /// Returned when parsing an ELF struct with a version field whose value wasn't + /// something we support and know how to parse. + UnsupportedVersion((u64, u64)), + /// Returned when parsing an ELF structure resulted in an offset which fell + /// out of bounds of the requested structure + BadOffset(u64), + /// Returned when parsing a string out of a StringTable failed to find the + /// terminating NUL byte + StringTableMissingNul(u64), + /// Returned when parsing a table of ELF structures and the file specified + /// an entry size for that table that was different than what we had + /// expected + BadEntsize((u64, u64)), + /// Returned when trying to interpret a section's data as the wrong type. + /// For example, trying to treat an SHT_PROGBIGS section as a SHT_STRTAB. + UnexpectedSectionType((u32, u32)), + /// Returned when trying to interpret a segment's data as the wrong type. + /// For example, trying to treat an PT_LOAD section as a PT_NOTE. + UnexpectedSegmentType((u32, u32)), + /// Returned when a section has a sh_addralign value that was different + /// than we expected. + UnexpectedAlignment(usize), + /// Returned when parsing an ELF structure out of an in-memory `&[u8]` + /// resulted in a request for a section of file bytes outside the range of + /// the slice. Commonly caused by truncated file contents. + SliceReadError((usize, usize)), + /// Returned when doing math with parsed elf fields that resulted in integer overflow. + IntegerOverflow, + /// Returned when parsing a string out of a StringTable that contained + /// invalid Utf8 + Utf8Error(core::str::Utf8Error), + /// Returned when parsing an ELF structure and the underlying structure data + /// was truncated and thus the full structure contents could not be parsed. + TryFromSliceError(core::array::TryFromSliceError), + /// Returned when parsing an ELF structure whose on-disk fields were too big + /// to represent in the native machine's usize type for in-memory processing. + /// This could be the case when processessing large 64-bit files on a 32-bit machine. + TryFromIntError(core::num::TryFromIntError), + #[cfg(feature = "std")] + /// Returned when parsing an ELF structure out of an io stream encountered + /// an io error. + IOError(std::io::Error), +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + ParseError::BadMagic(_) => None, + ParseError::UnsupportedElfClass(_) => None, + ParseError::UnsupportedElfEndianness(_) => None, + ParseError::UnsupportedVersion(_) => None, + ParseError::BadOffset(_) => None, + ParseError::StringTableMissingNul(_) => None, + ParseError::BadEntsize(_) => None, + ParseError::UnexpectedSectionType(_) => None, + ParseError::UnexpectedSegmentType(_) => None, + ParseError::UnexpectedAlignment(_) => None, + ParseError::SliceReadError(_) => None, + ParseError::IntegerOverflow => None, + ParseError::Utf8Error(ref err) => Some(err), + ParseError::TryFromSliceError(ref err) => Some(err), + ParseError::TryFromIntError(ref err) => Some(err), + ParseError::IOError(ref err) => Some(err), + } + } +} + +#[cfg(all(feature = "nightly", not(feature = "std")))] +impl core::error::Error for ParseError { + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + match *self { + ParseError::BadMagic(_) => None, + ParseError::UnsupportedElfClass(_) => None, + ParseError::UnsupportedElfEndianness(_) => None, + ParseError::UnsupportedVersion(_) => None, + ParseError::BadOffset(_) => None, + ParseError::StringTableMissingNul(_) => None, + ParseError::BadEntsize(_) => None, + ParseError::UnexpectedSectionType(_) => None, + ParseError::UnexpectedSegmentType(_) => None, + ParseError::UnexpectedAlignment(_) => None, + ParseError::SliceReadError(_) => None, + ParseError::IntegerOverflow => None, + ParseError::Utf8Error(ref err) => Some(err), + ParseError::TryFromSliceError(ref err) => Some(err), + ParseError::TryFromIntError(ref err) => Some(err), + } + } +} + +impl core::fmt::Display for ParseError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match *self { + ParseError::BadMagic(ref magic) => { + write!(f, "Invalid Magic Bytes: {magic:X?}") + } + ParseError::UnsupportedElfClass(class) => { + write!(f, "Unsupported ELF Class: {class}") + } + ParseError::UnsupportedElfEndianness(endianness) => { + write!(f, "Unsupported ELF Endianness: {endianness}") + } + ParseError::UnsupportedVersion((found, expected)) => { + write!( + f, + "Unsupported ELF Version field found: {found} expected: {expected}" + ) + } + ParseError::BadOffset(offset) => { + write!(f, "Bad offset: {offset:#X}") + } + ParseError::StringTableMissingNul(offset) => { + write!( + f, + "Could not find terminating NUL byte starting at offset: {offset:#X}" + ) + } + ParseError::BadEntsize((found, expected)) => { + write!( + f, + "Invalid entsize. Expected: {expected:#X}, Found: {found:#X}" + ) + } + ParseError::UnexpectedSectionType((found, expected)) => { + write!( + f, + "Could not interpret section of type {found} as type {expected}" + ) + } + ParseError::UnexpectedSegmentType((found, expected)) => { + write!( + f, + "Could not interpret section of type {found} as type {expected}" + ) + } + ParseError::UnexpectedAlignment(align) => { + write!( + f, + "Could not interpret section with unexpected alignment of {align}" + ) + } + ParseError::SliceReadError((start, end)) => { + write!(f, "Could not read bytes in range [{start:#X}, {end:#X})") + } + ParseError::IntegerOverflow => { + write!(f, "Integer overflow detected") + } + ParseError::Utf8Error(ref err) => err.fmt(f), + ParseError::TryFromSliceError(ref err) => err.fmt(f), + ParseError::TryFromIntError(ref err) => err.fmt(f), + #[cfg(feature = "std")] + ParseError::IOError(ref err) => err.fmt(f), + } + } +} + +impl From for ParseError { + fn from(err: core::str::Utf8Error) -> Self { + ParseError::Utf8Error(err) + } +} + +impl From for ParseError { + fn from(err: core::array::TryFromSliceError) -> Self { + ParseError::TryFromSliceError(err) + } +} + +impl From for ParseError { + fn from(err: core::num::TryFromIntError) -> Self { + ParseError::TryFromIntError(err) + } +} + +#[cfg(feature = "std")] +impl From for ParseError { + fn from(err: std::io::Error) -> ParseError { + ParseError::IOError(err) + } +} + +/// Trait for safely parsing an ELF structure of a given class (32/64 bit) with +/// an given endian-awareness at the given offset into the data buffer. +/// +/// This is the trait that drives our elf parser, where the various ELF +/// structures implement ParseAt in order to parse their Rust-native representation +/// from a buffer, all using safe code. +pub trait ParseAt: Sized { + /// Parse this type by using the given endian-awareness and ELF class layout. + /// This is generic on EndianParse in order to allow users to optimize for + /// their expectations of data layout. See EndianParse for more details. + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result; + + /// Returns the expected size of the type being parsed for the given ELF class + fn size_for(class: Class) -> usize; + + /// Checks whether the given entsize matches what we need to parse this type + /// + /// Returns a ParseError for bad/unexpected entsizes that don't match what this type parses. + fn validate_entsize(class: Class, entsize: usize) -> Result { + let expected = Self::size_for(class); + match entsize == expected { + true => Ok(entsize), + false => Err(ParseError::BadEntsize((entsize as u64, expected as u64))), + } + } +} + +/// Lazy-parsing iterator which wraps bytes and parses out a `P: ParseAt` on each `next()` +#[derive(Debug)] +pub struct ParsingIterator<'data, E: EndianParse, P: ParseAt> { + endian: E, + class: Class, + data: &'data [u8], + offset: usize, + // This struct doesn't technically own a P, but it yields them + // as it iterates + pd: PhantomData<&'data P>, +} + +impl<'data, E: EndianParse, P: ParseAt> ParsingIterator<'data, E, P> { + pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self { + ParsingIterator { + endian, + class, + data, + offset: 0, + pd: PhantomData, + } + } +} + +impl<'data, E: EndianParse, P: ParseAt> Iterator for ParsingIterator<'data, E, P> { + type Item = P; + fn next(&mut self) -> Option { + if self.data.is_empty() { + return None; + } + + Self::Item::parse_at(self.endian, self.class, &mut self.offset, self.data).ok() + } +} + +/// Lazy-parsing table which wraps bytes and parses out a `P: ParseAt` at a given index into +/// the table on each `get()`. +#[derive(Debug, Clone, Copy)] +pub struct ParsingTable<'data, E: EndianParse, P: ParseAt> { + endian: E, + class: Class, + data: &'data [u8], + // This struct doesn't technically own a P, but it yields them + pd: PhantomData<&'data P>, +} + +impl<'data, E: EndianParse, P: ParseAt> ParsingTable<'data, E, P> { + pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self { + ParsingTable { + endian, + class, + data, + pd: PhantomData, + } + } + + /// Get a lazy-parsing iterator for the table's bytes + pub fn iter(&self) -> ParsingIterator<'data, E, P> { + ParsingIterator::new(self.endian, self.class, self.data) + } + + /// Returns the number of elements of type P in the table. + pub fn len(&self) -> usize { + self.data.len() / P::size_for(self.class) + } + + /// Returns whether the table is empty (contains zero elements). + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Parse the element at `index` in the table. + pub fn get(&self, index: usize) -> Result { + if self.data.is_empty() { + return Err(ParseError::BadOffset(index as u64)); + } + + let entsize = P::size_for(self.class); + let mut start = index + .checked_mul(entsize) + .ok_or(ParseError::IntegerOverflow)?; + if start > self.data.len() { + return Err(ParseError::BadOffset(index as u64)); + } + + P::parse_at(self.endian, self.class, &mut start, self.data) + } +} + +impl<'data, E: EndianParse, P: ParseAt> IntoIterator for ParsingTable<'data, E, P> { + type IntoIter = ParsingIterator<'data, E, P>; + type Item = P; + + fn into_iter(self) -> Self::IntoIter { + ParsingIterator::new(self.endian, self.class, self.data) + } +} + +// Simple convenience extension trait to wrap get() with .ok_or(SliceReadError) +pub(crate) trait ReadBytesExt<'data> { + fn get_bytes(self, range: Range) -> Result<&'data [u8], ParseError>; +} + +impl<'data> ReadBytesExt<'data> for &'data [u8] { + fn get_bytes(self, range: Range) -> Result<&'data [u8], ParseError> { + let start = range.start; + let end = range.end; + self.get(range) + .ok_or(ParseError::SliceReadError((start, end))) + } +} + +#[cfg(test)] +pub(crate) fn test_parse_for( + endian: E, + class: Class, + expected: P, +) { + let size = P::size_for(class); + let mut data = vec![0u8; size]; + for (n, elem) in data.iter_mut().enumerate().take(size) { + *elem = n as u8; + } + + let mut offset = 0; + let entry = P::parse_at(endian, class, &mut offset, data.as_ref()).expect("Failed to parse"); + + assert_eq!(entry, expected); + assert_eq!(offset, size); +} + +#[cfg(test)] +pub(crate) fn test_parse_fuzz_too_short( + endian: E, + class: Class, +) { + let size = P::size_for(class); + let data = vec![0u8; size]; + for n in 0..size { + let buf = data.split_at(n).0; + let mut offset: usize = 0; + let error = P::parse_at(endian, class, &mut offset, buf).expect_err("Expected an error"); + assert!( + matches!(error, ParseError::SliceReadError(_)), + "Unexpected Error type found: {error}" + ); + } +} + +#[cfg(test)] +mod read_bytes_tests { + use super::ParseError; + use super::ReadBytesExt; + + #[test] + fn get_bytes_works() { + let data = &[0u8, 1, 2, 3]; + let subslice = data.get_bytes(1..3).expect("should be within range"); + assert_eq!(subslice, [1, 2]); + } + + #[test] + fn get_bytes_out_of_range_errors() { + let data = &[0u8, 1, 2, 3]; + let err = data.get_bytes(3..9).expect_err("should be out of range"); + assert!( + matches!(err, ParseError::SliceReadError((3, 9))), + "Unexpected Error type found: {err}" + ); + } +} + +#[cfg(test)] +mod parsing_table_tests { + use crate::endian::{AnyEndian, BigEndian, LittleEndian}; + + use super::*; + + type U32Table<'data, E> = ParsingTable<'data, E, u32>; + + #[test] + fn test_u32_validate_entsize() { + assert!(matches!(u32::validate_entsize(Class::ELF32, 4), Ok(4))); + assert!(matches!( + u32::validate_entsize(Class::ELF32, 8), + Err(ParseError::BadEntsize((8, 4))) + )); + } + + #[test] + fn test_u32_parse_at() { + let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let mut offset = 2; + let result = u32::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref()) + .expect("Expected to parse but:"); + assert_eq!(result, 0x05040302); + } + + #[test] + fn test_u32_table_len() { + let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); + assert_eq!(table.len(), 2); + } + + #[test] + fn test_u32_table_is_empty() { + let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); + assert!(!table.is_empty()); + + let table = U32Table::new(LittleEndian, Class::ELF32, &[]); + assert!(table.is_empty()); + + let table = U32Table::new(LittleEndian, Class::ELF32, data.get(0..1).unwrap()); + assert!(table.is_empty()); + } + + #[test] + fn test_u32_table_get_parse_failure() { + let data = vec![0u8, 1]; + let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); + assert!(matches!( + table.get(0), + Err(ParseError::SliceReadError((0, 4))) + )); + } + + #[test] + fn test_lsb_u32_table_get() { + let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); + assert!(matches!(table.get(0), Ok(0x03020100))); + assert!(matches!(table.get(1), Ok(0x07060504))); + assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); + } + + #[test] + fn test_any_lsb_u32_table_get() { + let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let table = U32Table::new(AnyEndian::Little, Class::ELF32, data.as_ref()); + assert!(matches!(table.get(0), Ok(0x03020100))); + assert!(matches!(table.get(1), Ok(0x07060504))); + assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); + } + + #[test] + fn test_msb_u32_table_get() { + let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let table = U32Table::new(BigEndian, Class::ELF32, data.as_ref()); + assert!(matches!(table.get(0), Ok(0x00010203))); + assert!(matches!(table.get(1), Ok(0x04050607))); + assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); + } + + #[test] + fn test_any_msb_u32_table_get() { + let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let table = U32Table::new(AnyEndian::Big, Class::ELF32, data.as_ref()); + assert!(matches!(table.get(0), Ok(0x00010203))); + assert!(matches!(table.get(1), Ok(0x04050607))); + assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); + } + + #[test] + fn test_u32_table_get_unaligned() { + let data = [0u8, 1, 2, 3, 4, 5, 6, 7]; + let table = U32Table::new(LittleEndian, Class::ELF32, data.get(1..).unwrap()); + assert!(matches!(table.get(0), Ok(0x04030201))); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/relocation.rs b/mikros_std_deps/elf-0.7.4/src/relocation.rs new file mode 100644 index 00000000000..bbec4a0f3c1 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/relocation.rs @@ -0,0 +1,299 @@ +//! Parsing relocation sections: `.rel.*`, `.rela.*`, [SHT_REL](crate::abi::SHT_REL), [SHT_RELA](crate::abi::SHT_RELA) +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ParsingIterator}; + +pub type RelIterator<'data, E> = ParsingIterator<'data, E, Rel>; +pub type RelaIterator<'data, E> = ParsingIterator<'data, E, Rela>; + +/// C-style 32-bit ELF Relocation definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Rel { + pub r_offset: u32, + pub r_info: u32, +} + +/// C-style 64-bit ELF Relocation definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Rel { + pub r_offset: u64, + pub r_info: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Rel { + pub r_offset: u64, + pub r_sym: u32, + pub r_type: u32, +} + +impl ParseAt for Rel { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + match class { + Class::ELF32 => { + let r_offset = endian.parse_u32_at(offset, data)? as u64; + let r_info = endian.parse_u32_at(offset, data)?; + Ok(Rel { + r_offset, + r_sym: r_info >> 8, + r_type: r_info & 0xFF, + }) + } + Class::ELF64 => { + let r_offset = endian.parse_u64_at(offset, data)?; + let r_info = endian.parse_u64_at(offset, data)?; + Ok(Rel { + r_offset, + r_sym: (r_info >> 32) as u32, + r_type: (r_info & 0xFFFFFFFF) as u32, + }) + } + } + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 8, + Class::ELF64 => 16, + } + } +} + +/// C-style 32-bit ELF Relocation (with addend) definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Rela { + pub r_offset: u32, + pub r_info: u32, + pub r_addend: i32, +} + +/// C-style 64-bit ELF Relocation (with addend) definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Rela { + pub r_offset: u64, + pub r_info: u64, + pub r_addend: i64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Rela { + pub r_offset: u64, + pub r_sym: u32, + pub r_type: u32, + pub r_addend: i64, +} + +impl ParseAt for Rela { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + match class { + Class::ELF32 => { + let r_offset = endian.parse_u32_at(offset, data)? as u64; + let r_info = endian.parse_u32_at(offset, data)?; + let r_addend = endian.parse_i32_at(offset, data)? as i64; + Ok(Rela { + r_offset, + r_sym: r_info >> 8, + r_type: r_info & 0xFF, + r_addend, + }) + } + Class::ELF64 => { + let r_offset = endian.parse_u64_at(offset, data)?; + let r_info = endian.parse_u64_at(offset, data)?; + let r_addend = endian.parse_i64_at(offset, data)?; + Ok(Rela { + r_offset, + r_sym: (r_info >> 32) as u32, + r_type: (r_info & 0xFFFFFFFF) as u32, + r_addend, + }) + } + } + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 12, + Class::ELF64 => 24, + } + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_rel32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + Rel { + r_offset: 0x03020100, + r_sym: 0x00070605, + r_type: 0x00000004, + }, + ); + } + + #[test] + fn parse_rel32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + Rel { + r_offset: 0x00010203, + r_sym: 0x00040506, + r_type: 0x00000007, + }, + ); + } + + #[test] + fn parse_rel64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + Rel { + r_offset: 0x0706050403020100, + r_sym: 0x0F0E0D0C, + r_type: 0x0B0A0908, + }, + ); + } + + #[test] + fn parse_rel64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + Rel { + r_offset: 0x0001020304050607, + r_sym: 0x08090A0B, + r_type: 0x0C0D0E0F, + }, + ); + } + + #[test] + fn parse_rel32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rel>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_rel32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rel>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_rel64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rel>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_rel64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rel>(BigEndian, Class::ELF64); + } + + #[test] + fn parse_rela32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + Rela { + r_offset: 0x03020100, + r_sym: 0x00070605, + r_type: 0x00000004, + r_addend: 0x0B0A0908, + }, + ); + } + + #[test] + fn parse_rela32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + Rela { + r_offset: 0x00010203, + r_sym: 0x00040506, + r_type: 0x00000007, + r_addend: 0x08090A0B, + }, + ); + } + + #[test] + fn parse_rela64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + Rela { + r_offset: 0x0706050403020100, + r_sym: 0x0F0E0D0C, + r_type: 0x0B0A0908, + r_addend: 0x1716151413121110, + }, + ); + } + + #[test] + fn parse_rela64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + Rela { + r_offset: 0x0001020304050607, + r_sym: 0x08090A0B, + r_type: 0x0C0D0E0F, + r_addend: 0x1011121314151617, + }, + ); + } + + #[test] + fn parse_rela32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rela>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_rela32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rela>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_rela64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rela>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_rela64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Rela>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/section.rs b/mikros_std_deps/elf-0.7.4/src/section.rs new file mode 100644 index 00000000000..a9730516d4a --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/section.rs @@ -0,0 +1,231 @@ +//! Parsing the Section Header table +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ParsingTable}; + +pub type SectionHeaderTable<'data, E> = ParsingTable<'data, E, SectionHeader>; + +/// C-style 32-bit ELF Section Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Shdr { + pub sh_name: u32, + pub sh_type: u32, + pub sh_flags: u32, + pub sh_addr: u32, + pub sh_offset: u32, + pub sh_size: u32, + pub sh_link: u32, + pub sh_info: u32, + pub sh_addralign: u32, + pub sh_entsize: u32, +} + +/// C-style 64-bit ELF Section Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Shdr { + pub sh_name: u32, + 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, +} + +/// Encapsulates the contents of an ELF Section Header +/// +/// This is a Rust-native type that represents a Section Header that is bit-width-agnostic. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SectionHeader { + /// Section Name + pub sh_name: u32, + /// Section Type + pub sh_type: u32, + /// Section Flags + pub sh_flags: u64, + /// in-memory address where this section is loaded + pub sh_addr: u64, + /// Byte-offset into the file where this section starts + pub sh_offset: u64, + /// Section size in bytes + pub sh_size: u64, + /// Defined by section type + pub sh_link: u32, + /// Defined by section type + pub sh_info: u32, + /// address alignment + pub sh_addralign: u64, + /// size of an entry if section data is an array of entries + pub sh_entsize: u64, +} + +impl ParseAt for SectionHeader { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + match class { + Class::ELF32 => Ok(SectionHeader { + sh_name: endian.parse_u32_at(offset, data)?, + sh_type: endian.parse_u32_at(offset, data)?, + sh_flags: endian.parse_u32_at(offset, data)? as u64, + sh_addr: endian.parse_u32_at(offset, data)? as u64, + sh_offset: endian.parse_u32_at(offset, data)? as u64, + sh_size: endian.parse_u32_at(offset, data)? as u64, + sh_link: endian.parse_u32_at(offset, data)?, + sh_info: endian.parse_u32_at(offset, data)?, + sh_addralign: endian.parse_u32_at(offset, data)? as u64, + sh_entsize: endian.parse_u32_at(offset, data)? as u64, + }), + Class::ELF64 => Ok(SectionHeader { + sh_name: endian.parse_u32_at(offset, data)?, + sh_type: endian.parse_u32_at(offset, data)?, + sh_flags: endian.parse_u64_at(offset, data)?, + sh_addr: endian.parse_u64_at(offset, data)?, + sh_offset: endian.parse_u64_at(offset, data)?, + sh_size: endian.parse_u64_at(offset, data)?, + sh_link: endian.parse_u32_at(offset, data)?, + sh_info: endian.parse_u32_at(offset, data)?, + sh_addralign: endian.parse_u64_at(offset, data)?, + sh_entsize: endian.parse_u64_at(offset, data)?, + }), + } + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 40, + Class::ELF64 => 64, + } + } +} + +impl SectionHeader { + /// Helper method which uses checked integer math to get a tuple of (start,end) for + /// this SectionHeader's (sh_offset, sh_offset + sh_size) + pub(crate) fn get_data_range(&self) -> Result<(usize, usize), ParseError> { + let start: usize = self.sh_offset.try_into()?; + let size: usize = self.sh_size.try_into()?; + let end = start.checked_add(size).ok_or(ParseError::IntegerOverflow)?; + Ok((start, end)) + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_shdr32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + SectionHeader { + sh_name: 0x03020100, + sh_type: 0x07060504, + sh_flags: 0xB0A0908, + sh_addr: 0x0F0E0D0C, + sh_offset: 0x13121110, + sh_size: 0x17161514, + sh_link: 0x1B1A1918, + sh_info: 0x1F1E1D1C, + sh_addralign: 0x23222120, + sh_entsize: 0x27262524, + }, + ); + } + + #[test] + fn parse_shdr32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + SectionHeader { + sh_name: 0x00010203, + sh_type: 0x04050607, + sh_flags: 0x08090A0B, + sh_addr: 0x0C0D0E0F, + sh_offset: 0x10111213, + sh_size: 0x14151617, + sh_link: 0x18191A1B, + sh_info: 0x1C1D1E1F, + sh_addralign: 0x20212223, + sh_entsize: 0x24252627, + }, + ); + } + + #[test] + fn parse_shdr64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + SectionHeader { + sh_name: 0x03020100, + sh_type: 0x07060504, + sh_flags: 0x0F0E0D0C0B0A0908, + sh_addr: 0x1716151413121110, + sh_offset: 0x1F1E1D1C1B1A1918, + sh_size: 0x2726252423222120, + sh_link: 0x2B2A2928, + sh_info: 0x2F2E2D2C, + sh_addralign: 0x3736353433323130, + sh_entsize: 0x3F3E3D3C3B3A3938, + }, + ); + } + + #[test] + fn parse_shdr64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + SectionHeader { + sh_name: 0x00010203, + sh_type: 0x04050607, + sh_flags: 0x08090A0B0C0D0E0F, + sh_addr: 0x1011121314151617, + sh_offset: 0x18191A1B1C1D1E1F, + sh_size: 0x2021222324252627, + sh_link: 0x28292A2B, + sh_info: 0x2C2D2E2F, + sh_addralign: 0x3031323334353637, + sh_entsize: 0x38393A3B3C3D3E3F, + }, + ); + } + + #[test] + fn parse_shdr32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SectionHeader>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_shdr32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SectionHeader>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_shdr64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SectionHeader>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_shdr64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, SectionHeader>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/segment.rs b/mikros_std_deps/elf-0.7.4/src/segment.rs new file mode 100644 index 00000000000..5f148f0f285 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/segment.rs @@ -0,0 +1,223 @@ +//! Parsing the Program Header table aka Segment table aka `Elf_Phdr` +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ParsingTable}; + +pub type SegmentTable<'data, E> = ParsingTable<'data, E, ProgramHeader>; + +/// C-style 32-bit ELF Program Segment Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Phdr { + pub p_type: u32, + pub p_offset: u32, + pub p_vaddr: u32, + pub p_paddr: u32, + pub p_filesz: u32, + pub p_memsz: u32, + pub p_flags: u32, + pub p_align: u32, +} + +/// C-style 64-bit ELF Program Segment Header definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Phdr { + 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, +} + +/// Encapsulates the contents of an ELF Program Header +/// +/// The program header table is an array of program header structures describing +/// the various segments for program execution. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ProgramHeader { + /// Program segment type + pub p_type: u32, + /// Offset into the ELF file where this segment begins + pub p_offset: u64, + /// Virtual adress where this segment should be loaded + pub p_vaddr: u64, + /// Physical address where this segment should be loaded + pub p_paddr: u64, + /// Size of this segment in the file + pub p_filesz: u64, + /// Size of this segment in memory + pub p_memsz: u64, + /// Flags for this segment + pub p_flags: u32, + /// file and memory alignment + pub p_align: u64, +} + +impl ParseAt for ProgramHeader { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + if class == Class::ELF32 { + return Ok(ProgramHeader { + p_type: endian.parse_u32_at(offset, data)?, + p_offset: endian.parse_u32_at(offset, data)? as u64, + p_vaddr: endian.parse_u32_at(offset, data)? as u64, + p_paddr: endian.parse_u32_at(offset, data)? as u64, + p_filesz: endian.parse_u32_at(offset, data)? as u64, + p_memsz: endian.parse_u32_at(offset, data)? as u64, + p_flags: endian.parse_u32_at(offset, data)?, + p_align: endian.parse_u32_at(offset, data)? as u64, + }); + } + + // Note: 64-bit fields are in a different order + let p_type = endian.parse_u32_at(offset, data)?; + let p_flags = endian.parse_u32_at(offset, data)?; + let p_offset = endian.parse_u64_at(offset, data)?; + let p_vaddr = endian.parse_u64_at(offset, data)?; + let p_paddr = endian.parse_u64_at(offset, data)?; + let p_filesz = endian.parse_u64_at(offset, data)?; + let p_memsz = endian.parse_u64_at(offset, data)?; + let p_align = endian.parse_u64_at(offset, data)?; + Ok(ProgramHeader { + p_type, + p_offset, + p_vaddr, + p_paddr, + p_filesz, + p_memsz, + p_flags, + p_align, + }) + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 32, + Class::ELF64 => 56, + } + } +} + +impl ProgramHeader { + /// Helper method which uses checked integer math to get a tuple of (start, end) for + /// the location in bytes for this ProgramHeader's data in the file. + /// i.e. (p_offset, p_offset + p_filesz) + pub(crate) fn get_file_data_range(&self) -> Result<(usize, usize), ParseError> { + let start: usize = self.p_offset.try_into()?; + let size: usize = self.p_filesz.try_into()?; + let end = start.checked_add(size).ok_or(ParseError::IntegerOverflow)?; + Ok((start, end)) + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_phdr32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + ProgramHeader { + p_type: 0x03020100, + p_offset: 0x07060504, + p_vaddr: 0xB0A0908, + p_paddr: 0x0F0E0D0C, + p_filesz: 0x13121110, + p_memsz: 0x17161514, + p_flags: 0x1B1A1918, + p_align: 0x1F1E1D1C, + }, + ); + } + + #[test] + fn parse_phdr32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + ProgramHeader { + p_type: 0x00010203, + p_offset: 0x04050607, + p_vaddr: 0x08090A0B, + p_paddr: 0x0C0D0E0F, + p_filesz: 0x10111213, + p_memsz: 0x14151617, + p_flags: 0x18191A1B, + p_align: 0x1C1D1E1F, + }, + ); + } + + #[test] + fn parse_phdr64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + ProgramHeader { + p_type: 0x03020100, + p_offset: 0x0F0E0D0C0B0A0908, + p_vaddr: 0x1716151413121110, + p_paddr: 0x1F1E1D1C1B1A1918, + p_filesz: 0x2726252423222120, + p_memsz: 0x2F2E2D2C2B2A2928, + p_flags: 0x07060504, + p_align: 0x3736353433323130, + }, + ); + } + + #[test] + fn parse_phdr64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + ProgramHeader { + p_type: 0x00010203, + p_offset: 0x08090A0B0C0D0E0F, + p_vaddr: 0x1011121314151617, + p_paddr: 0x18191A1B1C1D1E1F, + p_filesz: 0x2021222324252627, + p_memsz: 0x28292A2B2C2D2E2F, + p_flags: 0x04050607, + p_align: 0x3031323334353637, + }, + ); + } + + #[test] + fn parse_phdr32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, ProgramHeader>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_phdr32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, ProgramHeader>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_phdr64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, ProgramHeader>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_phdr64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, ProgramHeader>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/string_table.rs b/mikros_std_deps/elf-0.7.4/src/string_table.rs new file mode 100644 index 00000000000..0311cb0a249 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/string_table.rs @@ -0,0 +1,116 @@ +//! Interpreting string table sections: `.strtab`, [SHT_STRTAB][crate::abi::SHT_STRTAB] +use crate::parse::ParseError; +use core::str::from_utf8; + +#[derive(Debug, Default, Clone, Copy)] +pub struct StringTable<'data> { + data: &'data [u8], +} + +impl<'data> StringTable<'data> { + pub fn new(data: &'data [u8]) -> Self { + StringTable { data } + } + + pub fn get_raw(&self, offset: usize) -> Result<&'data [u8], ParseError> { + if self.data.is_empty() { + return Err(ParseError::BadOffset(offset as u64)); + }; + + let start = self + .data + .get(offset..) + .ok_or(ParseError::BadOffset(offset as u64))?; + let end = start + .iter() + .position(|&b| b == 0u8) + .ok_or(ParseError::StringTableMissingNul(offset as u64))?; + + Ok(start.split_at(end).0) + } + + pub fn get(&self, offset: usize) -> Result<&'data str, ParseError> { + let raw_data = self.get_raw(offset)?; + Ok(from_utf8(raw_data)?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_empty_table_errors() { + let st = StringTable::default(); + assert!(matches!(st.get(0), Err(ParseError::BadOffset(0)))); + assert!(matches!(st.get(1), Err(ParseError::BadOffset(1)))); + } + + /// Note: ELF string tables are defined to always start with a NUL and use + /// index 0 to give an empty string, so getting a string starting at a NUL + /// should properly give an empty string. + #[test] + fn test_get_index_0_gives_empty_string() { + let data = [0u8, 42u8, 0u8]; + let st = StringTable::new(&data); + assert_eq!(st.get(0).unwrap(), ""); + } + + #[test] + fn test_get_raw_works() { + let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; + let st = StringTable::new(&data); + assert_eq!(st.get_raw(1).unwrap(), [0x45, 0x4c, 0x46]); + } + + #[test] + fn test_get_string_works() { + let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; + let st = StringTable::new(&data); + assert_eq!(st.get(1).unwrap(), "ELF"); + } + + #[test] + fn test_get_raw_index_out_of_bounds_errors() { + let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; + let st = StringTable::new(&data); + let result = st.get_raw(7); + assert!( + matches!(result, Err(ParseError::BadOffset(7))), + "Unexpected Error type found: {result:?}" + ); + } + + #[test] + fn test_get_index_out_of_bounds_errors() { + let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; + let st = StringTable::new(&data); + let result = st.get(7); + assert!( + matches!(result, Err(ParseError::BadOffset(7))), + "Unexpected Error type found: {result:?}" + ); + } + + #[test] + fn test_get_raw_with_malformed_table_no_trailing_nul() { + let data = [0u8, 0x45, 0x4C, 0x46]; + let st = StringTable::new(&data); + let result = st.get_raw(1); + assert!( + matches!(result, Err(ParseError::StringTableMissingNul(1))), + "Unexpected Error type found: {result:?}" + ); + } + + #[test] + fn test_get_with_malformed_table_no_trailing_nul() { + let data = [0u8, 0x45, 0x4C, 0x46]; + let st = StringTable::new(&data); + let result = st.get(1); + assert!( + matches!(result, Err(ParseError::StringTableMissingNul(1))), + "Unexpected Error type found: {result:?}" + ); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/symbol.rs b/mikros_std_deps/elf-0.7.4/src/symbol.rs new file mode 100644 index 00000000000..90a12b4a3b9 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/symbol.rs @@ -0,0 +1,271 @@ +//! Parsing symbol table sections: `.symtab`, `.dynsym` +use crate::abi; +use crate::endian::EndianParse; +use crate::file::Class; +use crate::parse::{ParseAt, ParseError, ParsingTable}; + +pub type SymbolTable<'data, E> = ParsingTable<'data, E, Symbol>; + +/// C-style 32-bit ELF Symbol definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf32_Sym { + pub st_name: u32, + pub st_value: u32, + pub st_size: u32, + pub st_info: u8, + pub st_other: u8, + pub st_shndx: u32, +} + +/// C-style 64-bit ELF Symbol definition +/// +/// These C-style definitions are for users who want to implement their own ELF manipulation logic. +#[derive(Debug)] +#[repr(C)] +pub struct Elf64_Sym { + pub st_name: u32, + pub st_info: u8, + pub st_other: u8, + pub st_shndx: u16, + pub st_value: u64, + pub st_size: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Symbol { + /// This member holds an index into the symbol table's string table, + /// which holds the character representations of the symbol names. If the + /// value is non-zero, it represents a string table index that gives the + /// symbol name. Otherwise, the symbol table entry has no name. + pub st_name: u32, + + /// Every symbol table entry is defined in relation to some section. This + /// member holds the relevant section header table index. As the sh_link and + /// sh_info interpretation table and the related text describe, some section + /// indexes indicate special meanings. + /// + /// If this member contains SHN_XINDEX, then the actual section header index + /// is too large to fit in this field. The actual value is contained in the + /// associated section of type SHT_SYMTAB_SHNDX. + pub st_shndx: u16, + + /// This member specifies the symbol's type and binding attributes. + pub(super) st_info: u8, + + /// This member currently specifies a symbol's visibility. + pub(super) st_other: u8, + + /// This member gives the value of the associated symbol. Depending on the + /// context, this may be an absolute value, an address, and so on. + /// + /// * In relocatable files, st_value holds alignment constraints for a + /// symbol whose section index is SHN_COMMON. + /// * In relocatable files, st_value holds a section offset for a defined + /// symbol. st_value is an offset from the beginning of the section that + /// st_shndx identifies. + /// * In executable and shared object files, st_value holds a virtual + /// address. To make these files' symbols more useful for the dynamic + /// linker, the section offset (file interpretation) gives way to a + /// virtual address (memory interpretation) for which the section number + /// is irrelevant. + pub st_value: u64, + + /// This member gives the symbol's size. + /// For example, a data object's size is the number of bytes contained in + /// the object. This member holds 0 if the symbol has no size or an unknown + /// size. + pub st_size: u64, +} + +impl Symbol { + /// Returns true if a symbol is undefined in this ELF object. + /// + /// When linking and loading, undefined symbols in this object get linked to + /// a defined symbol in another object. + pub fn is_undefined(&self) -> bool { + self.st_shndx == abi::SHN_UNDEF + } + + pub fn st_symtype(&self) -> u8 { + self.st_info & 0xf + } + + pub fn st_bind(&self) -> u8 { + self.st_info >> 4 + } + + pub fn st_vis(&self) -> u8 { + self.st_other & 0x3 + } +} + +impl ParseAt for Symbol { + fn parse_at( + endian: E, + class: Class, + offset: &mut usize, + data: &[u8], + ) -> Result { + let st_name: u32; + let st_value: u64; + let st_size: u64; + let st_shndx: u16; + let st_info: u8; + let st_other: u8; + + if class == Class::ELF32 { + st_name = endian.parse_u32_at(offset, data)?; + st_value = endian.parse_u32_at(offset, data)? as u64; + st_size = endian.parse_u32_at(offset, data)? as u64; + st_info = endian.parse_u8_at(offset, data)?; + st_other = endian.parse_u8_at(offset, data)?; + st_shndx = endian.parse_u16_at(offset, data)?; + } else { + st_name = endian.parse_u32_at(offset, data)?; + st_info = endian.parse_u8_at(offset, data)?; + st_other = endian.parse_u8_at(offset, data)?; + st_shndx = endian.parse_u16_at(offset, data)?; + st_value = endian.parse_u64_at(offset, data)?; + st_size = endian.parse_u64_at(offset, data)?; + } + + Ok(Symbol { + st_name, + st_value, + st_size, + st_shndx, + st_info, + st_other, + }) + } + + #[inline] + fn size_for(class: Class) -> usize { + match class { + Class::ELF32 => 16, + Class::ELF64 => 24, + } + } +} + +#[cfg(test)] +mod symbol_tests { + use super::*; + + #[test] + fn symbol_undefined() { + let undef_sym = Symbol { + st_name: 0, + st_value: 0, + st_size: 0, + st_shndx: 0, + st_info: 0, + st_other: 0, + }; + assert!(undef_sym.is_undefined()); + + let def_sym = Symbol { + st_name: 0, + st_value: 0, + st_size: 0, + st_shndx: 42, + st_info: 0, + st_other: 0, + }; + assert!(!def_sym.is_undefined()); + } +} + +#[cfg(test)] +mod parse_tests { + use super::*; + use crate::endian::{BigEndian, LittleEndian}; + use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; + + #[test] + fn parse_sym32_lsb() { + test_parse_for( + LittleEndian, + Class::ELF32, + Symbol { + st_name: 0x03020100, + st_value: 0x07060504, + st_size: 0x0B0A0908, + st_shndx: 0x0F0E, + st_info: 0x0C, + st_other: 0x0D, + }, + ); + } + + #[test] + fn parse_sym32_msb() { + test_parse_for( + BigEndian, + Class::ELF32, + Symbol { + st_name: 0x00010203, + st_value: 0x04050607, + st_size: 0x08090A0B, + st_shndx: 0x0E0F, + st_info: 0x0C, + st_other: 0x0D, + }, + ); + } + + #[test] + fn parse_sym64_lsb() { + test_parse_for( + LittleEndian, + Class::ELF64, + Symbol { + st_name: 0x03020100, + st_value: 0x0F0E0D0C0B0A0908, + st_size: 0x1716151413121110, + st_shndx: 0x0706, + st_info: 0x04, + st_other: 0x05, + }, + ); + } + + #[test] + fn parse_sym64_msb() { + test_parse_for( + BigEndian, + Class::ELF64, + Symbol { + st_name: 0x00010203, + st_value: 0x08090A0B0C0D0E0F, + st_size: 0x1011121314151617, + st_shndx: 0x0607, + st_info: 0x04, + st_other: 0x05, + }, + ); + } + + #[test] + fn parse_sym32_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Symbol>(LittleEndian, Class::ELF32); + } + + #[test] + fn parse_sym32_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Symbol>(BigEndian, Class::ELF32); + } + + #[test] + fn parse_sym64_lsb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Symbol>(LittleEndian, Class::ELF64); + } + + #[test] + fn parse_sym64_msb_fuzz_too_short() { + test_parse_fuzz_too_short::<_, Symbol>(BigEndian, Class::ELF64); + } +} diff --git a/mikros_std_deps/elf-0.7.4/src/to_str.rs b/mikros_std_deps/elf-0.7.4/src/to_str.rs new file mode 100644 index 00000000000..0f1e2e24a77 --- /dev/null +++ b/mikros_std_deps/elf-0.7.4/src/to_str.rs @@ -0,0 +1,677 @@ +//! Optional module for getting string representations of ELF constants +use crate::abi; + +pub fn e_osabi_to_str(e_osabi: u8) -> Option<&'static str> { + match e_osabi { + abi::ELFOSABI_SYSV => Some("ELFOSABI_SYSV"), + abi::ELFOSABI_HPUX => Some("ELFOSABI_HPUX"), + abi::ELFOSABI_NETBSD => Some("ELFOSABI_NETBSD"), + abi::ELFOSABI_LINUX => Some("ELFOSABI_LINUX"), + abi::ELFOSABI_SOLARIS => Some("ELFOSABI_SOLARIS"), + abi::ELFOSABI_AIX => Some("ELFOSABI_AIX"), + abi::ELFOSABI_IRIX => Some("ELFOSABI_IRIX"), + abi::ELFOSABI_FREEBSD => Some("ELFOSABI_FREEBSD"), + abi::ELFOSABI_TRU64 => Some("ELFOSABI_TRU64"), + abi::ELFOSABI_MODESTO => Some("ELFOSABI_MODESTO"), + abi::ELFOSABI_OPENBSD => Some("ELFOSABI_OPENBSD"), + abi::ELFOSABI_OPENVMS => Some("ELFOSABI_OPENVMS"), + abi::ELFOSABI_NSK => Some("ELFOSABI_NSK"), + abi::ELFOSABI_AROS => Some("ELFOSABI_AROS"), + abi::ELFOSABI_FENIXOS => Some("ELFOSABI_FENIXOS"), + abi::ELFOSABI_CLOUDABI => Some("ELFOSABI_CLOUDABI"), + abi::ELFOSABI_OPENVOS => Some("ELFOSABI_OPENVOS"), + _ => None, + } +} + +pub fn e_osabi_to_string(e_osabi: u8) -> String { + match e_osabi_to_str(e_osabi) { + Some(s) => s.to_string(), + None => format!("e_osabi({e_osabi:#x})"), + } +} + +pub fn e_type_to_human_str(e_type: u16) -> Option<&'static str> { + match e_type { + abi::ET_NONE => Some("No file type"), + abi::ET_REL => Some("Relocatable file"), + abi::ET_EXEC => Some("Executable file"), + abi::ET_DYN => Some("Shared object file"), + abi::ET_CORE => Some("Core file"), + _ => None, + } +} + +pub fn e_type_to_str(e_type: u16) -> Option<&'static str> { + match e_type { + abi::ET_NONE => Some("ET_NONE"), + abi::ET_REL => Some("ET_REL"), + abi::ET_EXEC => Some("ET_EXEC"), + abi::ET_DYN => Some("ET_DYN"), + abi::ET_CORE => Some("ET_CORE"), + _ => None, + } +} + +pub fn e_type_to_string(e_type: u16) -> String { + match e_type_to_str(e_type) { + Some(s) => s.to_string(), + None => format!("e_type({e_type:#x})"), + } +} + +pub fn e_machine_to_human_str(e_machine: u16) -> Option<&'static str> { + match e_machine { + abi::EM_NONE => Some("No machine"), + abi::EM_M32 => Some("AT&T WE 32100"), + abi::EM_SPARC => Some("SPARC"), + abi::EM_386 => Some("Intel 80386"), + abi::EM_68K => Some("Motorola 68000"), + abi::EM_88K => Some("Motorola 88000"), + abi::EM_IAMCU => Some("Intel MCU"), + abi::EM_860 => Some("Intel 80860"), + abi::EM_MIPS => Some("MIPS I Architecture"), + abi::EM_S370 => Some("IBM System/370 Processor"), + abi::EM_MIPS_RS3_LE => Some("MIPS RS3000 Little-endian"), + abi::EM_PARISC => Some("Hewlett-Packard PA-RISC"), + abi::EM_VPP500 => Some("Fujitsu VPP500"), + abi::EM_SPARC32PLUS => Some("Enhanced instruction set SPARC"), + abi::EM_960 => Some("Intel 80960"), + abi::EM_PPC => Some("PowerPC"), + abi::EM_PPC64 => Some("64-bit PowerPC"), + abi::EM_S390 => Some("IBM System/390 Processor"), + abi::EM_SPU => Some("IBM SPU/SPC"), + abi::EM_V800 => Some("NEC V800"), + abi::EM_FR20 => Some("Fujitsu FR20"), + abi::EM_RH32 => Some("TRW RH-32"), + abi::EM_RCE => Some("Motorola RCE"), + abi::EM_ARM => Some("ARM 32-bit architecture (AARCH32)"), + abi::EM_ALPHA => Some("Digital Alpha"), + abi::EM_SH => Some("Hitachi SH"), + abi::EM_SPARCV9 => Some("SPARC Version 9"), + abi::EM_TRICORE => Some("Siemens TriCore embedded processor"), + abi::EM_ARC => Some("Argonaut RISC Core, Argonaut Technologies Inc."), + abi::EM_H8_300 => Some("Hitachi H8/300"), + abi::EM_H8_300H => Some("Hitachi H8/300H"), + abi::EM_H8S => Some("Hitachi H8S"), + abi::EM_H8_500 => Some("Hitachi H8/500"), + abi::EM_IA_64 => Some("Intel IA-64 processor architecture"), + abi::EM_MIPS_X => Some("Stanford MIPS-X"), + abi::EM_COLDFIRE => Some("Motorola ColdFire"), + abi::EM_68HC12 => Some("Motorola M68HC12"), + abi::EM_MMA => Some("Fujitsu MMA Multimedia Accelerator"), + abi::EM_PCP => Some("Siemens PCP"), + abi::EM_NCPU => Some("Sony nCPU embedded RISC processor"), + abi::EM_NDR1 => Some("Denso NDR1 microprocessor"), + abi::EM_STARCORE => Some("Motorola Star*Core processor"), + abi::EM_ME16 => Some("Toyota ME16 processor"), + abi::EM_ST100 => Some("STMicroelectronics ST100 processor"), + abi::EM_TINYJ => Some("Advanced Logic Corp. TinyJ embedded processor family"), + abi::EM_X86_64 => Some("AMD x86-64 architecture"), + abi::EM_PDSP => Some("Sony DSP Processor"), + abi::EM_PDP10 => Some("Digital Equipment Corp. PDP-10"), + abi::EM_PDP11 => Some("Digital Equipment Corp. PDP-11"), + abi::EM_FX66 => Some("Siemens FX66 microcontroller"), + abi::EM_ST9PLUS => Some("STMicroelectronics ST9+ 8/16 bit microcontroller"), + abi::EM_ST7 => Some("STMicroelectronics ST7 8-bit microcontroller"), + abi::EM_68HC16 => Some("Motorola MC68HC16 Microcontroller"), + abi::EM_68HC11 => Some("Motorola MC68HC11 Microcontroller"), + abi::EM_68HC08 => Some("Motorola MC68HC08 Microcontroller"), + abi::EM_68HC05 => Some("Motorola MC68HC05 Microcontroller"), + abi::EM_SVX => Some("Silicon Graphics SVx"), + abi::EM_ST19 => Some("STMicroelectronics ST19 8-bit microcontroller"), + abi::EM_VAX => Some("Digital VAX"), + abi::EM_CRIS => Some("Axis Communications 32-bit embedded processor"), + abi::EM_JAVELIN => Some("Infineon Technologies 32-bit embedded processor"), + abi::EM_FIREPATH => Some("Element 14 64-bit DSP Processor"), + abi::EM_ZSP => Some("LSI Logic 16-bit DSP Processor"), + abi::EM_MMIX => Some("Donald Knuth's educational 64-bit processor"), + abi::EM_HUANY => Some("Harvard University machine-independent object files"), + abi::EM_PRISM => Some("SiTera Prism"), + abi::EM_AVR => Some("Atmel AVR 8-bit microcontroller"), + abi::EM_FR30 => Some("Fujitsu FR30"), + abi::EM_D10V => Some("Mitsubishi D10V"), + abi::EM_D30V => Some("Mitsubishi D30V"), + abi::EM_V850 => Some("NEC v850"), + abi::EM_M32R => Some("Mitsubishi M32R"), + abi::EM_MN10300 => Some("Matsushita MN10300"), + abi::EM_MN10200 => Some("Matsushita MN10200"), + abi::EM_PJ => Some("picoJava"), + abi::EM_OPENRISC => Some("OpenRISC 32-bit embedded processor"), + abi::EM_ARC_COMPACT => Some("ARC International ARCompact processor"), + abi::EM_XTENSA => Some("Tensilica Xtensa Architecture"), + abi::EM_VIDEOCORE => Some("Alphamosaic VideoCore processor"), + abi::EM_TMM_GPP => Some("Thompson Multimedia General Purpose Processor"), + abi::EM_NS32K => Some("National Semiconductor 32000 series"), + abi::EM_TPC => Some("Tenor Network TPC processor"), + abi::EM_SNP1K => Some("Trebia SNP 1000 processor"), + abi::EM_ST200 => Some("STMicroelectronics (www.st.com) ST200 microcontroller"), + abi::EM_IP2K => Some("Ubicom IP2xxx microcontroller family"), + abi::EM_MAX => Some("MAX Processor"), + abi::EM_CR => Some("National Semiconductor CompactRISC microprocessor"), + abi::EM_F2MC16 => Some("Fujitsu F2MC16"), + abi::EM_MSP430 => Some("Texas Instruments embedded microcontroller msp430"), + abi::EM_BLACKFIN => Some("Analog Devices Blackfin (DSP) processor"), + abi::EM_SE_C33 => Some("S1C33 Family of Seiko Epson processors"), + abi::EM_SEP => Some("Sharp embedded microprocessor"), + abi::EM_ARCA => Some("Arca RISC Microprocessor"), + abi::EM_UNICORE => { + Some("Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University") + } + abi::EM_EXCESS => Some("eXcess: 16/32/64-bit configurable embedded CPU"), + abi::EM_DXP => Some("Icera Semiconductor Inc. Deep Execution Processor"), + abi::EM_ALTERA_NIOS2 => Some("Altera Nios II soft-core processor"), + abi::EM_CRX => Some("National Semiconductor CompactRISC CRX microprocessor"), + abi::EM_XGATE => Some("Motorola XGATE embedded processor"), + abi::EM_C166 => Some("Infineon C16x/XC16x processor"), + abi::EM_M16C => Some("Renesas M16C series microprocessors"), + abi::EM_DSPIC30F => Some("Microchip Technology dsPIC30F Digital Signal Controller"), + abi::EM_CE => Some("Freescale Communication Engine RISC core"), + abi::EM_M32C => Some("Renesas M32C series microprocessors"), + abi::EM_TSK3000 => Some("Altium TSK3000 core"), + abi::EM_RS08 => Some("Freescale RS08 embedded processor"), + abi::EM_SHARC => Some("Analog Devices SHARC family of 32-bit DSP processors"), + abi::EM_ECOG2 => Some("Cyan Technology eCOG2 microprocessor"), + abi::EM_SCORE7 => Some("Sunplus S+core7 RISC processor"), + abi::EM_DSP24 => Some("New Japan Radio (NJR) 24-bit DSP Processor"), + abi::EM_VIDEOCORE3 => Some("Broadcom VideoCore III processor"), + abi::EM_LATTICEMICO32 => Some("RISC processor for Lattice FPGA architecture"), + abi::EM_SE_C17 => Some("Seiko Epson C17 family"), + abi::EM_TI_C6000 => Some("The Texas Instruments TMS320C6000 DSP family"), + abi::EM_TI_C2000 => Some("The Texas Instruments TMS320C2000 DSP family"), + abi::EM_TI_C5500 => Some("The Texas Instruments TMS320C55x DSP family"), + abi::EM_TI_ARP32 => { + Some("Texas Instruments Application Specific RISC Processor, 32bit fetch") + } + abi::EM_TI_PRU => Some("Texas Instruments Programmable Realtime Unit"), + abi::EM_MMDSP_PLUS => Some("STMicroelectronics 64bit VLIW Data Signal Processor"), + abi::EM_CYPRESS_M8C => Some("Cypress M8C microprocessor"), + abi::EM_R32C => Some("Renesas R32C series microprocessors"), + abi::EM_TRIMEDIA => Some("NXP Semiconductors TriMedia architecture family"), + abi::EM_QDSP6 => Some("QUALCOMM DSP6 Processor"), + abi::EM_8051 => Some("Intel 8051 and variants"), + abi::EM_STXP7X => { + Some("STMicroelectronics STxP7x family of configurable and extensible RISC processors") + } + abi::EM_NDS32 => Some("Andes Technology compact code size embedded RISC processor family"), + abi::EM_ECOG1X => Some("Cyan Technology eCOG1X family"), + abi::EM_MAXQ30 => Some("Dallas Semiconductor MAXQ30 Core Micro-controllers"), + abi::EM_XIMO16 => Some("New Japan Radio (NJR) 16-bit DSP Processor"), + abi::EM_MANIK => Some("M2000 Reconfigurable RISC Microprocessor"), + abi::EM_CRAYNV2 => Some("Cray Inc. NV2 vector architecture"), + abi::EM_RX => Some("Renesas RX family"), + abi::EM_METAG => Some("Imagination Technologies META processor architecture"), + abi::EM_MCST_ELBRUS => Some("MCST Elbrus general purpose hardware architecture"), + abi::EM_ECOG16 => Some("Cyan Technology eCOG16 family"), + abi::EM_CR16 => Some("National Semiconductor CompactRISC CR16 16-bit microprocessor"), + abi::EM_ETPU => Some("Freescale Extended Time Processing Unit"), + abi::EM_SLE9X => Some("Infineon Technologies SLE9X core"), + abi::EM_L10M => Some("Intel L10M"), + abi::EM_K10M => Some("Intel K10M"), + abi::EM_AARCH64 => Some("ARM 64-bit architecture (AARCH64)"), + abi::EM_AVR32 => Some("Atmel Corporation 32-bit microprocessor family"), + abi::EM_STM8 => Some("STMicroeletronics STM8 8-bit microcontroller"), + abi::EM_TILE64 => Some("Tilera TILE64 multicore architecture family"), + abi::EM_TILEPRO => Some("Tilera TILEPro multicore architecture family"), + abi::EM_MICROBLAZE => Some("Xilinx MicroBlaze 32-bit RISC soft processor core"), + abi::EM_CUDA => Some("NVIDIA CUDA architecture"), + abi::EM_TILEGX => Some("Tilera TILE-Gx multicore architecture family"), + abi::EM_CLOUDSHIELD => Some("CloudShield architecture family"), + abi::EM_COREA_1ST => Some("KIPO-KAIST Core-A 1st generation processor family"), + abi::EM_COREA_2ND => Some("KIPO-KAIST Core-A 2nd generation processor family"), + abi::EM_ARC_COMPACT2 => Some("Synopsys ARCompact V2"), + abi::EM_OPEN8 => Some("Open8 8-bit RISC soft processor core"), + abi::EM_RL78 => Some("Renesas RL78 family"), + abi::EM_VIDEOCORE5 => Some("Broadcom VideoCore V processor"), + abi::EM_78KOR => Some("Renesas 78KOR family"), + abi::EM_56800EX => Some("Freescale 56800EX Digital Signal Controller (DSC)"), + abi::EM_BA1 => Some("Beyond BA1 CPU architecture"), + abi::EM_BA2 => Some("Beyond BA2 CPU architecture"), + abi::EM_XCORE => Some("XMOS xCORE processor family"), + abi::EM_MCHP_PIC => Some("Microchip 8-bit PIC(r) family"), + abi::EM_INTEL205 => Some("Reserved by Intel"), + abi::EM_INTEL206 => Some("Reserved by Intel"), + abi::EM_INTEL207 => Some("Reserved by Intel"), + abi::EM_INTEL208 => Some("Reserved by Intel"), + abi::EM_INTEL209 => Some("Reserved by Intel"), + abi::EM_KM32 => Some("KM211 KM32 32-bit processor"), + abi::EM_KMX32 => Some("KM211 KMX32 32-bit processor"), + abi::EM_KMX16 => Some("KM211 KMX16 16-bit processor"), + abi::EM_KMX8 => Some("KM211 KMX8 8-bit processor"), + abi::EM_KVARC => Some("KM211 KVARC processor"), + abi::EM_CDP => Some("Paneve CDP architecture family"), + abi::EM_COGE => Some("Cognitive Smart Memory Processor"), + abi::EM_COOL => Some("Bluechip Systems CoolEngine"), + abi::EM_NORC => Some("Nanoradio Optimized RISC"), + abi::EM_CSR_KALIMBA => Some("CSR Kalimba architecture family"), + abi::EM_Z80 => Some("Zilog Z80"), + abi::EM_VISIUM => Some("Controls and Data Services VISIUMcore processor"), + abi::EM_FT32 => Some("FTDI Chip FT32 high performance 32-bit RISC architecture"), + abi::EM_MOXIE => Some("Moxie processor family"), + abi::EM_AMDGPU => Some("AMD GPU architecture"), + abi::EM_RISCV => Some("RISC-V"), + abi::EM_BPF => Some("Linux BPF"), + _ => None, + } +} + +pub fn e_machine_to_str(e_machine: u16) -> Option<&'static str> { + match e_machine { + abi::EM_NONE => Some("EM_NONE"), + abi::EM_M32 => Some("EM_M32"), + abi::EM_SPARC => Some("EM_SPARC"), + abi::EM_386 => Some("EM_386"), + abi::EM_68K => Some("EM_68K"), + abi::EM_88K => Some("EM_88K"), + abi::EM_IAMCU => Some("EM_IAMCU"), + abi::EM_860 => Some("EM_860"), + abi::EM_MIPS => Some("EM_MIPS"), + abi::EM_S370 => Some("EM_S370"), + abi::EM_MIPS_RS3_LE => Some("EM_MIPS_RS3_LE"), + abi::EM_PARISC => Some("EM_PARISC"), + abi::EM_VPP500 => Some("EM_VPP500"), + abi::EM_SPARC32PLUS => Some("EM_SPARC32PLUS"), + abi::EM_960 => Some("EM_960"), + abi::EM_PPC => Some("EM_PPC"), + abi::EM_PPC64 => Some("EM_PPC64"), + abi::EM_S390 => Some("EM_S390"), + abi::EM_SPU => Some("EM_SPU"), + abi::EM_V800 => Some("EM_V800"), + abi::EM_FR20 => Some("EM_FR20"), + abi::EM_RH32 => Some("EM_RH32"), + abi::EM_RCE => Some("EM_RCE"), + abi::EM_ARM => Some("EM_ARM"), + abi::EM_ALPHA => Some("EM_ALPHA"), + abi::EM_SH => Some("EM_SH"), + abi::EM_SPARCV9 => Some("EM_SPARCV9"), + abi::EM_TRICORE => Some("EM_TRICORE"), + abi::EM_ARC => Some("EM_ARC"), + abi::EM_H8_300 => Some("EM_H8_300"), + abi::EM_H8_300H => Some("EM_H8_300H"), + abi::EM_H8S => Some("EM_H8S"), + abi::EM_H8_500 => Some("EM_H8_500"), + abi::EM_IA_64 => Some("EM_IA_64"), + abi::EM_MIPS_X => Some("EM_MIPS_X"), + abi::EM_COLDFIRE => Some("EM_COLDFIRE"), + abi::EM_68HC12 => Some("EM_68HC12"), + abi::EM_MMA => Some("EM_MMA"), + abi::EM_PCP => Some("EM_PCP"), + abi::EM_NCPU => Some("EM_NCPU"), + abi::EM_NDR1 => Some("EM_NDR1"), + abi::EM_STARCORE => Some("EM_STARCORE"), + abi::EM_ME16 => Some("EM_ME16"), + abi::EM_ST100 => Some("EM_ST100"), + abi::EM_TINYJ => Some("EM_TINYJ"), + abi::EM_X86_64 => Some("EM_X86_64"), + abi::EM_PDSP => Some("EM_PDSP"), + abi::EM_PDP10 => Some("EM_PDP10"), + abi::EM_PDP11 => Some("EM_PDP11"), + abi::EM_FX66 => Some("EM_FX66"), + abi::EM_ST9PLUS => Some("EM_ST9PLUS"), + abi::EM_ST7 => Some("EM_ST7"), + abi::EM_68HC16 => Some("EM_68HC16"), + abi::EM_68HC11 => Some("EM_68HC11"), + abi::EM_68HC08 => Some("EM_68HC08"), + abi::EM_68HC05 => Some("EM_68HC05"), + abi::EM_SVX => Some("EM_SVX"), + abi::EM_ST19 => Some("EM_ST19"), + abi::EM_VAX => Some("EM_VAX"), + abi::EM_CRIS => Some("EM_CRIS"), + abi::EM_JAVELIN => Some("EM_JAVELIN"), + abi::EM_FIREPATH => Some("EM_FIREPATH"), + abi::EM_ZSP => Some("EM_ZSP"), + abi::EM_MMIX => Some("EM_MMIX"), + abi::EM_HUANY => Some("EM_HUANY"), + abi::EM_PRISM => Some("EM_PRISM"), + abi::EM_AVR => Some("EM_AVR"), + abi::EM_FR30 => Some("EM_FR30"), + abi::EM_D10V => Some("EM_D10V"), + abi::EM_D30V => Some("EM_D30V"), + abi::EM_V850 => Some("EM_V850"), + abi::EM_M32R => Some("EM_M32R"), + abi::EM_MN10300 => Some("EM_MN10300"), + abi::EM_MN10200 => Some("EM_MN10200"), + abi::EM_PJ => Some("EM_PJ"), + abi::EM_OPENRISC => Some("EM_OPENRISC"), + abi::EM_ARC_COMPACT => Some("EM_ARC_COMPACT"), + abi::EM_XTENSA => Some("EM_XTENSA"), + abi::EM_VIDEOCORE => Some("EM_VIDEOCORE"), + abi::EM_TMM_GPP => Some("EM_TMM_GPP"), + abi::EM_NS32K => Some("EM_NS32K"), + abi::EM_TPC => Some("EM_TPC"), + abi::EM_SNP1K => Some("EM_SNP1K"), + abi::EM_ST200 => Some("EM_ST200"), + abi::EM_IP2K => Some("EM_IP2K"), + abi::EM_MAX => Some("EM_MAX"), + abi::EM_CR => Some("EM_CR"), + abi::EM_F2MC16 => Some("EM_F2MC16"), + abi::EM_MSP430 => Some("EM_MSP430"), + abi::EM_BLACKFIN => Some("EM_BLACKFIN"), + abi::EM_SE_C33 => Some("EM_SE_C33"), + abi::EM_SEP => Some("EM_SEP"), + abi::EM_ARCA => Some("EM_ARCA"), + abi::EM_UNICORE => Some("EM_UNICORE"), + abi::EM_EXCESS => Some("EM_EXCESS"), + abi::EM_DXP => Some("EM_DXP"), + abi::EM_ALTERA_NIOS2 => Some("EM_ALTERA_NIOS2"), + abi::EM_CRX => Some("EM_CRX"), + abi::EM_XGATE => Some("EM_XGATE"), + abi::EM_C166 => Some("EM_C166"), + abi::EM_M16C => Some("EM_M16C"), + abi::EM_DSPIC30F => Some("EM_DSPIC30F"), + abi::EM_CE => Some("EM_CE"), + abi::EM_M32C => Some("EM_M32C"), + abi::EM_TSK3000 => Some("EM_TSK3000"), + abi::EM_RS08 => Some("EM_RS08"), + abi::EM_SHARC => Some("EM_SHARC"), + abi::EM_ECOG2 => Some("EM_ECOG2"), + abi::EM_SCORE7 => Some("EM_SCORE7"), + abi::EM_DSP24 => Some("EM_DSP24"), + abi::EM_VIDEOCORE3 => Some("EM_VIDEOCORE3"), + abi::EM_LATTICEMICO32 => Some("EM_LATTICEMICO32"), + abi::EM_SE_C17 => Some("EM_SE_C17"), + abi::EM_TI_C6000 => Some("EM_TI_C6000"), + abi::EM_TI_C2000 => Some("EM_TI_C2000"), + abi::EM_TI_C5500 => Some("EM_TI_C5500"), + abi::EM_TI_ARP32 => Some("EM_TI_ARP32"), + abi::EM_TI_PRU => Some("EM_TI_PRU"), + abi::EM_MMDSP_PLUS => Some("EM_MMDSP_PLUS"), + abi::EM_CYPRESS_M8C => Some("EM_CYPRESS_M8C"), + abi::EM_R32C => Some("EM_R32C"), + abi::EM_TRIMEDIA => Some("EM_TRIMEDIA"), + abi::EM_QDSP6 => Some("EM_QDSP6"), + abi::EM_8051 => Some("EM_8051"), + abi::EM_STXP7X => Some("EM_STXP7X"), + abi::EM_NDS32 => Some("EM_NDS32"), + abi::EM_ECOG1X => Some("EM_ECOG1X"), + abi::EM_MAXQ30 => Some("EM_MAXQ30"), + abi::EM_XIMO16 => Some("EM_XIMO16"), + abi::EM_MANIK => Some("EM_MANIK"), + abi::EM_CRAYNV2 => Some("EM_CRAYNV2"), + abi::EM_RX => Some("EM_RX"), + abi::EM_METAG => Some("EM_METAG"), + abi::EM_MCST_ELBRUS => Some("EM_MCST_ELBRUS"), + abi::EM_ECOG16 => Some("EM_ECOG16"), + abi::EM_CR16 => Some("EM_CR16"), + abi::EM_ETPU => Some("EM_ETPU"), + abi::EM_SLE9X => Some("EM_SLE9X"), + abi::EM_L10M => Some("EM_L10M"), + abi::EM_K10M => Some("EM_K10M"), + abi::EM_AARCH64 => Some("EM_AARCH64"), + abi::EM_AVR32 => Some("EM_AVR32"), + abi::EM_STM8 => Some("EM_STM8"), + abi::EM_TILE64 => Some("EM_TILE64"), + abi::EM_TILEPRO => Some("EM_TILEPRO"), + abi::EM_MICROBLAZE => Some("EM_MICROBLAZE"), + abi::EM_CUDA => Some("EM_CUDA"), + abi::EM_TILEGX => Some("EM_TILEGX"), + abi::EM_CLOUDSHIELD => Some("EM_CLOUDSHIELD"), + abi::EM_COREA_1ST => Some("EM_COREA_1ST"), + abi::EM_COREA_2ND => Some("EM_COREA_2ND"), + abi::EM_ARC_COMPACT2 => Some("EM_ARC_COMPACT2"), + abi::EM_OPEN8 => Some("EM_OPEN8"), + abi::EM_RL78 => Some("EM_RL78"), + abi::EM_VIDEOCORE5 => Some("EM_VIDEOCORE5"), + abi::EM_78KOR => Some("EM_78KOR"), + abi::EM_56800EX => Some("EM_56800EX"), + abi::EM_BA1 => Some("EM_BA1"), + abi::EM_BA2 => Some("EM_BA2"), + abi::EM_XCORE => Some("EM_XCORE"), + abi::EM_MCHP_PIC => Some("EM_MCHP_PIC"), + abi::EM_INTEL205 => Some("EM_INTEL205"), + abi::EM_INTEL206 => Some("EM_INTEL206"), + abi::EM_INTEL207 => Some("EM_INTEL207"), + abi::EM_INTEL208 => Some("EM_INTEL208"), + abi::EM_INTEL209 => Some("EM_INTEL209"), + abi::EM_KM32 => Some("EM_KM32"), + abi::EM_KMX32 => Some("EM_KMX32"), + abi::EM_KMX16 => Some("EM_KMX16"), + abi::EM_KMX8 => Some("EM_KMX8"), + abi::EM_KVARC => Some("EM_KVARC"), + abi::EM_CDP => Some("EM_CDP"), + abi::EM_COGE => Some("EM_COGE"), + abi::EM_COOL => Some("EM_COOL"), + abi::EM_NORC => Some("EM_NORC"), + abi::EM_CSR_KALIMBA => Some("EM_CSR_KALIMBA"), + abi::EM_Z80 => Some("EM_Z80"), + abi::EM_VISIUM => Some("EM_VISIUM"), + abi::EM_FT32 => Some("EM_FT32"), + abi::EM_MOXIE => Some("EM_MOXIE"), + abi::EM_AMDGPU => Some("EM_AMDGPU"), + abi::EM_RISCV => Some("RISC-V"), + abi::EM_BPF => Some("EM_BPF"), + _ => None, + } +} + +pub fn e_machine_to_string(e_machine: u16) -> String { + match e_machine_to_str(e_machine) { + Some(s) => s.to_string(), + None => format!("e_machine({e_machine:#x})"), + } +} + +pub fn sh_type_to_str(sh_type: u32) -> Option<&'static str> { + match sh_type { + abi::SHT_NULL => Some("SHT_NULL"), + abi::SHT_PROGBITS => Some("SHT_PROGBITS"), + abi::SHT_SYMTAB => Some("SHT_SYMTAB"), + abi::SHT_STRTAB => Some("SHT_STRTAB"), + abi::SHT_RELA => Some("SHT_RELA"), + abi::SHT_HASH => Some("SHT_HASH"), + abi::SHT_DYNAMIC => Some("SHT_DYNAMIC"), + abi::SHT_NOTE => Some("SHT_NOTE"), + abi::SHT_NOBITS => Some("SHT_NOBITS"), + abi::SHT_REL => Some("SHT_REL"), + abi::SHT_SHLIB => Some("SHT_SHLIB"), + abi::SHT_DYNSYM => Some("SHT_DYNSYM"), + abi::SHT_INIT_ARRAY => Some("SHT_INIT_ARRAY"), + abi::SHT_FINI_ARRAY => Some("SHT_FINI_ARRAY"), + abi::SHT_PREINIT_ARRAY => Some("SHT_PREINIT_ARRAY"), + abi::SHT_GROUP => Some("SHT_GROUP"), + abi::SHT_SYMTAB_SHNDX => Some("SHT_SYMTAB_SHNDX"), + abi::SHT_GNU_ATTRIBUTES => Some("SHT_GNU_ATTRIBUTES"), + abi::SHT_GNU_HASH => Some("SHT_GNU_HASH"), + abi::SHT_GNU_LIBLIST => Some("SHT_GNU_LIBLIST"), + abi::SHT_GNU_VERDEF => Some("SHT_GNU_VERDEF"), + abi::SHT_GNU_VERNEED => Some("SHT_GNU_VERNEED"), + abi::SHT_GNU_VERSYM => Some("SHT_GNU_VERSYM"), + _ => None, + } +} + +pub fn sh_type_to_string(sh_type: u32) -> String { + match sh_type_to_str(sh_type) { + Some(s) => s.to_string(), + None => format!("sh_type({sh_type:#x})"), + } +} + +pub fn p_flags_to_string(p_flags: u32) -> String { + match p_flags < 8 { + true => { + let r = if p_flags & abi::PF_R != 0 { "R" } else { " " }; + let w = if p_flags & abi::PF_W != 0 { "W" } else { " " }; + let x = if p_flags & abi::PF_X != 0 { "E" } else { " " }; + format!("{r}{w}{x}") + } + false => format!("p_flags({p_flags:#x})"), + } +} + +pub fn p_type_to_str(p_type: u32) -> Option<&'static str> { + match p_type { + abi::PT_NULL => Some("PT_NULL"), + abi::PT_LOAD => Some("PT_LOAD"), + abi::PT_DYNAMIC => Some("PT_DYNAMIC"), + abi::PT_INTERP => Some("PT_INTERP"), + abi::PT_NOTE => Some("PT_NOTE"), + abi::PT_SHLIB => Some("PT_SHLIB"), + abi::PT_PHDR => Some("PT_PHDR"), + abi::PT_TLS => Some("PT_TLS"), + abi::PT_GNU_EH_FRAME => Some("PT_GNU_EH_FRAME"), + abi::PT_GNU_STACK => Some("PT_GNU_STACK"), + abi::PT_GNU_RELRO => Some("PT_GNU_RELRO"), + abi::PT_GNU_PROPERTY => Some("PT_GNU_PROPERTY"), + _ => None, + } +} + +pub fn p_type_to_string(p_type: u32) -> String { + match p_type_to_str(p_type) { + Some(s) => s.to_string(), + None => format!("p_type({p_type:#x})"), + } +} + +pub fn st_symtype_to_str(st_symtype: u8) -> Option<&'static str> { + match st_symtype { + abi::STT_NOTYPE => Some("STT_NOTYPE"), + abi::STT_OBJECT => Some("STT_OBJECT"), + abi::STT_FUNC => Some("STT_FUNC"), + abi::STT_SECTION => Some("STT_SECTION"), + abi::STT_FILE => Some("STT_FILE"), + abi::STT_COMMON => Some("STT_COMMON"), + abi::STT_TLS => Some("STT_TLS"), + abi::STT_GNU_IFUNC => Some("STT_GNU_IFUNC"), + _ => None, + } +} + +pub fn st_symtype_to_string(st_symtype: u8) -> String { + match st_symtype_to_str(st_symtype) { + Some(s) => s.to_string(), + None => format!("st_symtype({st_symtype:#x})"), + } +} + +pub fn st_bind_to_str(st_bind: u8) -> Option<&'static str> { + match st_bind { + abi::STB_LOCAL => Some("STB_LOCAL"), + abi::STB_GLOBAL => Some("STB_GLOBAL"), + abi::STB_WEAK => Some("STB_WEAK"), + abi::STB_GNU_UNIQUE => Some("STB_GNU_UNIQUE"), + _ => None, + } +} + +pub fn st_bind_to_string(st_bind: u8) -> String { + match st_bind_to_str(st_bind) { + Some(s) => s.to_string(), + None => format!("st_bind({st_bind:#x})"), + } +} + +pub fn st_vis_to_str(st_vis: u8) -> Option<&'static str> { + match st_vis { + abi::STV_DEFAULT => Some("STV_DEFAULT"), + abi::STV_INTERNAL => Some("STV_INTERNAL"), + abi::STV_HIDDEN => Some("STV_HIDDEN"), + abi::STV_PROTECTED => Some("STV_PROTECTED"), + _ => None, + } +} + +pub fn st_vis_to_string(st_vis: u8) -> String { + match st_vis_to_str(st_vis) { + Some(s) => s.to_string(), + None => format!("st_vis({st_vis:#x})"), + } +} + +pub fn ch_type_to_str(ch_type: u32) -> Option<&'static str> { + match ch_type { + abi::ELFCOMPRESS_ZLIB => Some("ELFCOMPRESS_ZLIB"), + abi::ELFCOMPRESS_ZSTD => Some("ELFCOMPRESS_ZSTD "), + _ => None, + } +} + +pub fn note_abi_tag_os_to_str(os: u32) -> Option<&'static str> { + match os { + abi::ELF_NOTE_GNU_ABI_TAG_OS_LINUX => Some("Linux"), + abi::ELF_NOTE_GNU_ABI_TAG_OS_GNU => Some("GNU"), + abi::ELF_NOTE_GNU_ABI_TAG_OS_SOLARIS2 => Some("Solaris"), + abi::ELF_NOTE_GNU_ABI_TAG_OS_FREEBSD => Some("FreeBSD"), + _ => None, + } +} + +pub fn d_tag_to_str(d_tag: i64) -> Option<&'static str> { + match d_tag { + abi::DT_NULL => Some("DT_NULL"), + abi::DT_NEEDED => Some("DT_NEEDED"), + abi::DT_PLTRELSZ => Some("DT_PLTRELSZ"), + abi::DT_PLTGOT => Some("DT_PLTGOT"), + abi::DT_HASH => Some("DT_HASH"), + abi::DT_STRTAB => Some("DT_STRTAB"), + abi::DT_SYMTAB => Some("DT_SYMTAB"), + abi::DT_RELA => Some("DT_RELA"), + abi::DT_RELASZ => Some("DT_RELASZ"), + abi::DT_RELAENT => Some("DT_RELAENT"), + abi::DT_STRSZ => Some("DT_STRSZ"), + abi::DT_SYMENT => Some("DT_SYMENT"), + abi::DT_INIT => Some("DT_INIT"), + abi::DT_FINI => Some("DT_FINI"), + abi::DT_SONAME => Some("DT_SONAME"), + abi::DT_RPATH => Some("DT_RPATH"), + abi::DT_SYMBOLIC => Some("DT_SYMBOLIC"), + abi::DT_REL => Some("DT_REL"), + abi::DT_RELSZ => Some("DT_RELSZ"), + abi::DT_RELENT => Some("DT_RELENT"), + abi::DT_PLTREL => Some("DT_PLTREL"), + abi::DT_DEBUG => Some("DT_DEBUG"), + abi::DT_TEXTREL => Some("DT_TEXTREL"), + abi::DT_JMPREL => Some("DT_JMPREL"), + abi::DT_BIND_NOW => Some("DT_BIND_NOW"), + abi::DT_INIT_ARRAY => Some("DT_INIT_ARRAY"), + abi::DT_FINI_ARRAY => Some("DT_FINI_ARRAY"), + abi::DT_INIT_ARRAYSZ => Some("DT_INIT_ARRAYSZ"), + abi::DT_FINI_ARRAYSZ => Some("DT_FINI_ARRAYSZ"), + abi::DT_RUNPATH => Some("DT_RUNPATH"), + abi::DT_FLAGS => Some("DT_FLAGS"), + abi::DT_PREINIT_ARRAY => Some("DT_PREINIT_ARRAY"), + abi::DT_PREINIT_ARRAYSZ => Some("DT_PREINIT_ARRAYSZ"), + abi::DT_SYMTAB_SHNDX => Some("DT_SYMTAB_SHNDX"), + abi::DT_GUILE_GC_ROOT => Some("DT_GUILE_GC_ROOT"), + abi::DT_GUILE_GC_ROOT_SZ => Some("DT_GUILE_GC_ROOT_SZ"), + abi::DT_GUILE_ENTRY => Some("DT_GUILE_ENTRY"), + abi::DT_GUILE_VM_VERSION => Some("DT_GUILE_VM_VERSION"), + abi::DT_GUILE_FRAME_MAPS => Some("DT_GUILE_FRAME_MAPS"), + abi::DT_LOOS => Some("DT_LOOS"), + abi::DT_GNU_PRELINKED => Some("DT_GNU_PRELINKED"), + abi::DT_GNU_CONFLICTSZ => Some("DT_GNU_CONFLICTSZ"), + abi::DT_GNU_LIBLISTSZ => Some("DT_GNU_LIBLISTSZ"), + abi::DT_CHECKSUM => Some("DT_CHECKSUM"), + abi::DT_PLTPADSZ => Some("DT_PLTPADSZ"), + abi::DT_MOVEENT => Some("DT_MOVEENT"), + abi::DT_MOVESZ => Some("DT_MOVESZ"), + abi::DT_FEATURE_1 => Some("DT_FEATURE_1"), + abi::DT_POSFLAG_1 => Some("DT_POSFLAG_1"), + abi::DT_SYMINSZ => Some("DT_SYMINSZ"), + abi::DT_SYMINENT => Some("DT_SYMINENT"), + abi::DT_GNU_HASH => Some("DT_GNU_HASH"), + abi::DT_TLSDESC_PLT => Some("DT_TLSDESC_PLT"), + abi::DT_TLSDESC_GOT => Some("DT_TLSDESC_GOT"), + abi::DT_GNU_CONFLICT => Some("DT_GNU_CONFLICT"), + abi::DT_GNU_LIBLIST => Some("DT_GNU_LIBLIST"), + abi::DT_CONFIG => Some("DT_CONFIG"), + abi::DT_DEPAUDIT => Some("DT_DEPAUDIT"), + abi::DT_AUDIT => Some("DT_AUDIT"), + abi::DT_PLTPAD => Some("DT_PLTPAD"), + abi::DT_MOVETAB => Some("DT_MOVETAB"), + abi::DT_SYMINFO => Some("DT_SYMINFO"), + abi::DT_VERSYM => Some("DT_VERSYM"), + abi::DT_RELACOUNT => Some("DT_RELACOUNT"), + abi::DT_RELCOUNT => Some("DT_RELCOUNT"), + abi::DT_FLAGS_1 => Some("DT_FLAGS_1"), + abi::DT_VERDEF => Some("DT_VERDEF"), + abi::DT_VERDEFNUM => Some("DT_VERDEFNUM"), + abi::DT_VERNEED => Some("DT_VERNEED"), + abi::DT_VERNEEDNUM => Some("DT_VERNEEDNUM"), + abi::DT_HIOS => Some("DT_HIOS"), + abi::DT_LOPROC => Some("DT_LOPROC"), + abi::DT_HIPROC => Some("DT_HIPROC"), + _ => None, + } +}