Add mikros ELF loader to std

This commit is contained in:
pjht 2024-06-06 22:08:20 -05:00
parent a428b626e5
commit a9084ef21d
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
33 changed files with 12589 additions and 3 deletions

View File

@ -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",

View File

@ -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]

View File

@ -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")]

View File

@ -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::<AnyEndian>::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(&section).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(&section).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))
}
}

View File

@ -4,4 +4,5 @@
pub mod ipc;
pub mod address_space;
pub mod loader;
pub mod syscalls;

View File

@ -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<u64, ()> {
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<u64> {
syscalls::try_get_registered(typ)
}
#[stable(feature = "mikros", since = "1.80.0")]
#[must_use]
pub fn get_pid() -> u64 {
syscalls::get_pid()
}

View File

@ -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(())
}
}

View File

@ -0,0 +1,6 @@
{
"git": {
"sha1": "94d6780552465ebdc3d732f4a32d6bd00f1b8d70"
},
"path_in_vcs": ""
}

View File

@ -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.
<!-- next-url -->
[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

View File

@ -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.

View File

@ -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 <chris.cole.09@gmail.com>"]
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",
]

View File

@ -0,0 +1,25 @@
[package]
name = "elf"
version = "0.7.4"
authors = ["Christopher Cole <chris.cole.09@gmail.com>"]
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 = []

View File

@ -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.

View File

@ -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.

View File

@ -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: <https://github.com/rust-lang/project-safe-transmute>
### ✨ 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::<AnyEndian>::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<Note> = 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());
```

File diff suppressed because it is too large Load Diff

View File

@ -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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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);
}
}

View File

@ -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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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<u8, ParseError> {
safe_from!(self, u8, offset, data)
}
fn parse_u16_at(self, offset: &mut usize, data: &[u8]) -> Result<u16, ParseError> {
safe_from!(self, u16, offset, data)
}
fn parse_u32_at(self, offset: &mut usize, data: &[u8]) -> Result<u32, ParseError> {
safe_from!(self, u32, offset, data)
}
fn parse_u64_at(self, offset: &mut usize, data: &[u8]) -> Result<u64, ParseError> {
safe_from!(self, u64, offset, data)
}
fn parse_i32_at(self, offset: &mut usize, data: &[u8]) -> Result<i32, ParseError> {
safe_from!(self, i32, offset, data)
}
fn parse_i64_at(self, offset: &mut usize, data: &[u8]) -> Result<i64, ParseError> {
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<Self, ParseError>;
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<Self, ParseError> {
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<Self, ParseError> {
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<Self, ParseError> {
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);
}
}

View File

@ -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<E: EndianParse> {
/// 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<E: EndianParse>(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<E: EndianParse> FileHeader<E> {
pub fn parse_tail(ident: (E, Class, u8, u8), data: &[u8]) -> Result<FileHeader<E>, 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:?}"
);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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<E: EndianParse>(
endian: E,
_class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
endian.parse_u32_at(offset, data)
}
#[inline]
fn size_for(_class: Class) -> usize {
core::mem::size_of::<u32>()
}
}
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<E: EndianParse>(
endian: E,
_class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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::<u32>() + size_of::<u32>()
}
}
/// 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<Self, ParseError> {
let mut offset = 0;
let hdr = SysVHashHeader::parse_at(endian, class, &mut offset, data)?;
let buckets_size = size_of::<u32>()
.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::<u32>()
.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<Option<(usize, Symbol)>, 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<E: EndianParse>(
endian: E,
_class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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::<u32>() + size_of::<u32>() + size_of::<u32>() + size_of::<u32>()
}
}
type U64Table<'data, E> = ParsingTable<'data, E, u64>;
impl ParseAt for u64 {
fn parse_at<E: EndianParse>(
endian: E,
_class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
endian.parse_u64_at(offset, data)
}
#[inline]
fn size_for(_class: Class) -> usize {
core::mem::size_of::<u64>()
}
}
#[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<Self, ParseError> {
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::<u32>())
.ok_or(ParseError::IntegerOverflow)?,
Class::ELF64 => nbloom
.checked_mul(size_of::<u64>())
.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::<u32>()
.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<Option<(usize, Symbol)>, 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::<u32>() 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::<u64>() 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);
}
}

View File

@ -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: <https://github.com/rust-lang/project-safe-transmute>
//!
//! ### ✨ 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::<AnyEndian>::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<Note> = 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;

View File

@ -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::<AnyEndian>::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<E: EndianParse>(
endian: E,
_class: Class,
align: usize,
offset: &mut usize,
data: &'data [u8],
) -> Result<Self, ParseError> {
// 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: <https://raw.githubusercontent.com/wiki/hjl-tools/linux-abi/linux-abi-draft.pdf>)
#[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<E: EndianParse>(
endian: E,
_class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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::<u32>() * 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: <https://raw.githubusercontent.com/wiki/hjl-tools/linux-abi/linux-abi-draft.pdf>)
#[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<Self::Item> {
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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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);
}
}

View File

@ -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<core::str::Utf8Error> for ParseError {
fn from(err: core::str::Utf8Error) -> Self {
ParseError::Utf8Error(err)
}
}
impl From<core::array::TryFromSliceError> for ParseError {
fn from(err: core::array::TryFromSliceError) -> Self {
ParseError::TryFromSliceError(err)
}
}
impl From<core::num::TryFromIntError> for ParseError {
fn from(err: core::num::TryFromIntError) -> Self {
ParseError::TryFromIntError(err)
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> 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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError>;
/// 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<usize, ParseError> {
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<Self::Item> {
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<P, ParseError> {
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<usize>) -> Result<&'data [u8], ParseError>;
}
impl<'data> ReadBytesExt<'data> for &'data [u8] {
fn get_bytes(self, range: Range<usize>) -> 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<E: EndianParse, P: ParseAt + core::fmt::Debug + PartialEq>(
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<E: EndianParse, P: ParseAt + core::fmt::Debug>(
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)));
}
}

View File

@ -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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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);
}
}

View File

@ -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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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);
}
}

View File

@ -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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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);
}
}

View File

@ -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:?}"
);
}
}

View File

@ -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<E: EndianParse>(
endian: E,
class: Class,
offset: &mut usize,
data: &[u8],
) -> Result<Self, ParseError> {
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);
}
}

View File

@ -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,
}
}