├── sample-objects ├── symver.ver ├── basic.x86_64 ├── phnum.m68k.so ├── shnum.x86_64 ├── symver.armhf.so ├── symver.m68k.so ├── symver.x86_64.so ├── stripped.x86_64.so ├── symver.aarch64.so ├── symver.riscv64.so ├── symver.powerpc64.so ├── symver.powerpc64le.so ├── symver.sh └── symver.c ├── fuzz ├── .gitignore ├── fuzz_targets │ ├── notes.rs │ ├── symbol_table.rs │ ├── stream.rs │ ├── symver.rs │ └── common.rs └── Cargo.toml ├── .gitignore ├── COPYRIGHT ├── Cargo.toml ├── .github └── workflows │ ├── rust.yml │ └── fuzz.yml ├── LICENSE-MIT ├── src ├── string_table.rs ├── dynamic.rs ├── compression.rs ├── segment.rs ├── lib.rs ├── section.rs ├── symbol.rs ├── relocation.rs ├── endian.rs ├── file.rs ├── hash.rs ├── parse.rs ├── note.rs └── to_str.rs ├── README.md ├── CHANGELOG.md └── LICENSE-APACHE /sample-objects/symver.ver: -------------------------------------------------------------------------------- 1 | HELLO_1.0 {}; 2 | HELLO_1.42 {}; 3 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | artifacts 2 | corpus 3 | target 4 | coverage 5 | -------------------------------------------------------------------------------- /sample-objects/basic.x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/basic.x86_64 -------------------------------------------------------------------------------- /sample-objects/phnum.m68k.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/phnum.m68k.so -------------------------------------------------------------------------------- /sample-objects/shnum.x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/shnum.x86_64 -------------------------------------------------------------------------------- /sample-objects/symver.armhf.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/symver.armhf.so -------------------------------------------------------------------------------- /sample-objects/symver.m68k.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/symver.m68k.so -------------------------------------------------------------------------------- /sample-objects/symver.x86_64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/symver.x86_64.so -------------------------------------------------------------------------------- /sample-objects/stripped.x86_64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/stripped.x86_64.so -------------------------------------------------------------------------------- /sample-objects/symver.aarch64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/symver.aarch64.so -------------------------------------------------------------------------------- /sample-objects/symver.riscv64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/symver.riscv64.so -------------------------------------------------------------------------------- /sample-objects/symver.powerpc64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/symver.powerpc64.so -------------------------------------------------------------------------------- /sample-objects/symver.powerpc64le.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cole14/rust-elf/HEAD/sample-objects/symver.powerpc64le.so -------------------------------------------------------------------------------- /sample-objects/symver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | gcc -o symver.x86_64.so symver.c -Wl,--as-needed -shared -fPIC -Xlinker --hash-style=both -Wl,--version-script=symver.ver 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | *.swp 7 | 8 | # Executables 9 | *.exe 10 | 11 | # Generated by Cargo 12 | /target/ 13 | 14 | Cargo.lock 15 | -------------------------------------------------------------------------------- /sample-objects/symver.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int use_memset(unsigned char *buf, size_t n) { 5 | memset(buf, 0, n); 6 | } 7 | 8 | int use_memset_v2(unsigned char *buf) { 9 | memset(buf, 0, 42); 10 | } 11 | 12 | __asm__(".symver use_memset, use_memset@HELLO_1.0"); 13 | __asm__(".symver use_memset_v2, use_memset_v2@HELLO_1.42"); 14 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/notes.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use elf::endian::NativeEndian; 4 | use elf::file::Class; 5 | use elf::note::{Note, NoteIterator}; 6 | use libfuzzer_sys::fuzz_target; 7 | 8 | fuzz_target!(|data: &[u8]| { 9 | if data.is_empty() { 10 | return; 11 | } 12 | 13 | let (head, tail) = data.split_at(1); 14 | 15 | let iter = NoteIterator::new(NativeEndian, Class::ELF64, head[0] as usize, tail); 16 | let _: Vec = iter.collect(); 17 | }); 18 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | rust-elf is licensed under either of 2 | 3 | * Apache License, Version 2.0 (LICENSE-APACHE or 4 | http://www.apache.org/licenses/LICENSE-2.0) 5 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 6 | http://opensource.org/licenses/MIT) 7 | 8 | at your option. 9 | 10 | Unless you explicitly state otherwise, any contribution intentionally submitted 11 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 12 | be dual licensed as above, without any additional terms or conditions. 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elf" 3 | version = "0.8.0" 4 | authors = ["Christopher Cole "] 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/cole14/rust-elf/" 7 | documentation = "https://docs.rs/elf/latest/elf/" 8 | description = "A pure-rust library for parsing ELF files" 9 | keywords = ["binary", "elf", "object", "parser"] 10 | categories = ["no-std", "os", "embedded"] 11 | exclude = [".gitignore", "/.github", "/sample-objects"] 12 | readme = "README.md" 13 | edition = "2021" 14 | rust-version = "1.81.0" 15 | 16 | [lib] 17 | name = "elf" 18 | 19 | [dependencies] 20 | 21 | [features] 22 | default = ["alloc" , "std", "to_str"] 23 | alloc = [] 24 | std = ["alloc"] 25 | to_str = [] 26 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Build 18 | run: cargo build --verbose 19 | - name: Run tests 20 | run: cargo test --verbose 21 | - name: Build no_std 22 | run: cargo build --no-default-features 23 | 24 | msrv-all: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Install MSRV 29 | run: rustup update 1.81.0 && rustup default 1.81.0 30 | - name: Test 31 | run: cargo test --verbose 32 | - name: Build no_std 33 | run: cargo build --no-default-features 34 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/symbol_table.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use elf::endian::AnyEndian; 4 | use elf::symbol::Symbol; 5 | use elf::ElfBytes; 6 | use libfuzzer_sys::fuzz_target; 7 | 8 | fuzz_target!(|data: &[u8]| { 9 | if let Ok(file) = ElfBytes::::minimal_parse(data) { 10 | if let Ok(Some((symtab, strtab))) = file.symbol_table() { 11 | let _: Vec<(&str, Symbol)> = symtab 12 | .iter() 13 | .map(|sym| (strtab.get(sym.st_name as usize).unwrap_or("unknown"), sym)) 14 | .collect(); 15 | } 16 | 17 | if let Ok(Some((dynsym, dynstr))) = file.dynamic_symbol_table() { 18 | let _: Vec<(&str, Symbol)> = dynsym 19 | .iter() 20 | .map(|sym| (dynstr.get(sym.st_name as usize).unwrap_or("unknown"), sym)) 21 | .collect(); 22 | } 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/stream.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use elf::endian::AnyEndian; 4 | use elf::ElfStream; 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | let mut cur = std::io::Cursor::new(data); 9 | if let Ok(mut file) = ElfStream::::open_stream(&mut cur) { 10 | // parse the symbol table 11 | if let Ok(Some((symtab, _))) = file.symbol_table() { 12 | let _: Vec<_> = symtab.iter().collect(); 13 | } 14 | 15 | // parse any notes 16 | if let Some(shdr) = file 17 | .section_headers() 18 | .iter() 19 | .find(|shdr| shdr.sh_type == elf::abi::SHT_NOTE) 20 | { 21 | let shdr = *shdr; 22 | if let Ok(notes) = file.section_data_as_notes(&shdr) { 23 | let _: Vec<_> = notes.collect(); 24 | } 25 | } 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/symver.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use elf::endian::AnyEndian; 4 | use elf::symbol::Symbol; 5 | use elf::ElfBytes; 6 | use libfuzzer_sys::fuzz_target; 7 | 8 | fuzz_target!(|data: &[u8]| { 9 | if let Ok(file) = ElfBytes::::minimal_parse(data) { 10 | if let Ok(Some((dynsym, _))) = file.dynamic_symbol_table() { 11 | let symbols: Vec = dynsym.iter().collect(); 12 | 13 | if let Ok(Some(table)) = file.symbol_version_table() { 14 | for (idx, _) in symbols.iter().enumerate() { 15 | if let Ok(Some(def)) = table.get_definition(idx) { 16 | let _: Vec<&str> = 17 | def.names.map(|name| name.unwrap_or("unknown")).collect(); 18 | } 19 | 20 | if let Ok(Some(_)) = table.get_requirement(idx) {} 21 | } 22 | } 23 | } 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elf-fuzz" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | 13 | [dependencies.elf] 14 | path = ".." 15 | 16 | # Prevent this from interfering with workspaces 17 | [workspace] 18 | members = ["."] 19 | 20 | [profile.release] 21 | debug = 1 22 | 23 | [[bin]] 24 | name = "common" 25 | path = "fuzz_targets/common.rs" 26 | test = false 27 | doc = false 28 | 29 | [[bin]] 30 | name = "notes" 31 | path = "fuzz_targets/notes.rs" 32 | test = false 33 | doc = false 34 | 35 | [[bin]] 36 | name = "symbol_table" 37 | path = "fuzz_targets/symbol_table.rs" 38 | test = false 39 | doc = false 40 | 41 | [[bin]] 42 | name = "symver" 43 | path = "fuzz_targets/symver.rs" 44 | test = false 45 | doc = false 46 | 47 | [[bin]] 48 | name = "stream" 49 | path = "fuzz_targets/stream.rs" 50 | test = false 51 | doc = false 52 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Christopher Cole 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Fuzz 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Install nightly Rust 20 | shell: bash 21 | run: | 22 | curl -sSL https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain=nightly 23 | export PATH="$HOME/.cargo/bin:$PATH" 24 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 25 | - name: Install cargo fuzz 26 | run: cargo install cargo-fuzz 27 | - name: Build 28 | run: cargo build --verbose 29 | - name: Cache fuzzy corpus 30 | uses: actions/cache@v4 31 | with: 32 | path: fuzz/corpus 33 | key: fuzz-corpus-${{ github.run_id }} 34 | restore-keys: | 35 | fuzz-corpus- 36 | - name: fuzzy common 37 | run: cargo fuzz run common -- -max_total_time=60 38 | - name: fuzzy notes 39 | run: cargo fuzz run notes -- -max_total_time=60 40 | - name: fuzzy symver 41 | run: cargo fuzz run symver -- -max_total_time=60 42 | - name: fuzzy symbol_table 43 | run: cargo fuzz run symbol_table -- -max_total_time=60 44 | - name: fuzzy stream 45 | run: cargo fuzz run stream -- -max_total_time=60 46 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/common.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use elf::endian::AnyEndian; 4 | use elf::ElfBytes; 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | if let Ok(file) = ElfBytes::::minimal_parse(data) { 9 | if let Some(shdrs) = file.section_headers() { 10 | let _: Vec<_> = shdrs.iter().collect(); 11 | } 12 | 13 | if let Ok(common) = file.find_common_data() { 14 | // parse the symbol table 15 | if let Some(symtab) = common.symtab { 16 | let _: Vec<_> = symtab.iter().collect(); 17 | } 18 | 19 | // parse the dynamic symbol table 20 | if let Some(dynsyms) = common.dynsyms { 21 | if let Some(dynstrs) = common.dynsyms_strs { 22 | let sym_names: Vec<&[u8]> = dynsyms 23 | .iter() 24 | .map(|sym| dynstrs.get_raw(sym.st_name as usize).unwrap_or(b"unk")) 25 | .collect(); 26 | 27 | // use the hash table 28 | if let Some(hash) = common.sysv_hash { 29 | for name in sym_names.iter() { 30 | let _ = hash.find(name, &dynsyms, &dynstrs); 31 | } 32 | } 33 | 34 | // use the gnu hash table 35 | if let Some(hash) = common.gnu_hash { 36 | for name in sym_names.iter() { 37 | let _ = hash.find(name, &dynsyms, &dynstrs); 38 | } 39 | } 40 | } 41 | } 42 | 43 | // parse the .dynamic table 44 | if let Some(dyns) = common.dynamic { 45 | let _: Vec<_> = dyns.iter().collect(); 46 | } 47 | } 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /src/string_table.rs: -------------------------------------------------------------------------------- 1 | //! Interpreting string table sections: `.strtab`, [SHT_STRTAB][crate::abi::SHT_STRTAB] 2 | use crate::parse::ParseError; 3 | use core::str::from_utf8; 4 | 5 | #[derive(Debug, Default, Clone, Copy)] 6 | pub struct StringTable<'data> { 7 | data: &'data [u8], 8 | } 9 | 10 | impl<'data> StringTable<'data> { 11 | pub fn new(data: &'data [u8]) -> Self { 12 | StringTable { data } 13 | } 14 | 15 | pub fn get_raw(&self, offset: usize) -> Result<&'data [u8], ParseError> { 16 | if self.data.is_empty() { 17 | return Err(ParseError::BadOffset(offset as u64)); 18 | }; 19 | 20 | let start = self 21 | .data 22 | .get(offset..) 23 | .ok_or(ParseError::BadOffset(offset as u64))?; 24 | let end = start 25 | .iter() 26 | .position(|&b| b == 0u8) 27 | .ok_or(ParseError::StringTableMissingNul(offset as u64))?; 28 | 29 | Ok(start.split_at(end).0) 30 | } 31 | 32 | pub fn get(&self, offset: usize) -> Result<&'data str, ParseError> { 33 | let raw_data = self.get_raw(offset)?; 34 | Ok(from_utf8(raw_data)?) 35 | } 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | 42 | #[test] 43 | fn test_empty_table_errors() { 44 | let st = StringTable::default(); 45 | assert!(matches!(st.get(0), Err(ParseError::BadOffset(0)))); 46 | assert!(matches!(st.get(1), Err(ParseError::BadOffset(1)))); 47 | } 48 | 49 | /// Note: ELF string tables are defined to always start with a NUL and use 50 | /// index 0 to give an empty string, so getting a string starting at a NUL 51 | /// should properly give an empty string. 52 | #[test] 53 | fn test_get_index_0_gives_empty_string() { 54 | let data = [0u8, 42u8, 0u8]; 55 | let st = StringTable::new(&data); 56 | assert_eq!(st.get(0).unwrap(), ""); 57 | } 58 | 59 | #[test] 60 | fn test_get_raw_works() { 61 | let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; 62 | let st = StringTable::new(&data); 63 | assert_eq!(st.get_raw(1).unwrap(), [0x45, 0x4c, 0x46]); 64 | } 65 | 66 | #[test] 67 | fn test_get_string_works() { 68 | let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; 69 | let st = StringTable::new(&data); 70 | assert_eq!(st.get(1).unwrap(), "ELF"); 71 | } 72 | 73 | #[test] 74 | fn test_get_raw_index_out_of_bounds_errors() { 75 | let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; 76 | let st = StringTable::new(&data); 77 | let result = st.get_raw(7); 78 | assert!( 79 | matches!(result, Err(ParseError::BadOffset(7))), 80 | "Unexpected Error type found: {result:?}" 81 | ); 82 | } 83 | 84 | #[test] 85 | fn test_get_index_out_of_bounds_errors() { 86 | let data = [0u8, 0x45, 0x4C, 0x46, 0u8]; 87 | let st = StringTable::new(&data); 88 | let result = st.get(7); 89 | assert!( 90 | matches!(result, Err(ParseError::BadOffset(7))), 91 | "Unexpected Error type found: {result:?}" 92 | ); 93 | } 94 | 95 | #[test] 96 | fn test_get_raw_with_malformed_table_no_trailing_nul() { 97 | let data = [0u8, 0x45, 0x4C, 0x46]; 98 | let st = StringTable::new(&data); 99 | let result = st.get_raw(1); 100 | assert!( 101 | matches!(result, Err(ParseError::StringTableMissingNul(1))), 102 | "Unexpected Error type found: {result:?}" 103 | ); 104 | } 105 | 106 | #[test] 107 | fn test_get_with_malformed_table_no_trailing_nul() { 108 | let data = [0u8, 0x45, 0x4C, 0x46]; 109 | let st = StringTable::new(&data); 110 | let result = st.get(1); 111 | assert!( 112 | matches!(result, Err(ParseError::StringTableMissingNul(1))), 113 | "Unexpected Error type found: {result:?}" 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/dynamic.rs: -------------------------------------------------------------------------------- 1 | //! Parsing `.dynamic` section or [PT_DYNAMIC](crate::abi::PT_DYNAMIC) segment contents 2 | use crate::endian::EndianParse; 3 | use crate::file::Class; 4 | use crate::parse::{ParseAt, ParseError, ParsingTable}; 5 | 6 | pub type DynamicTable<'data, E> = ParsingTable<'data, E, Dyn>; 7 | 8 | /// C-style 32-bit ELF Dynamic section entry definition 9 | /// 10 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 11 | #[derive(Debug)] 12 | #[repr(C)] 13 | pub struct Elf32_Dyn { 14 | pub d_tag: i32, 15 | // union of both {d_val, d_ptr} 16 | pub d_un: u32, 17 | } 18 | 19 | /// C-style 64-bit ELF Dynamic section entry definition 20 | /// 21 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 22 | #[derive(Debug)] 23 | #[repr(C)] 24 | pub struct Elf64_Dyn { 25 | pub d_tag: i64, 26 | // union of both {d_val, d_ptr} 27 | pub d_un: u64, 28 | } 29 | 30 | #[derive(Debug, Clone, PartialEq, Eq)] 31 | pub struct Dyn { 32 | pub d_tag: i64, 33 | pub(super) d_un: u64, 34 | } 35 | 36 | impl Dyn { 37 | pub fn d_val(&self) -> u64 { 38 | self.d_un 39 | } 40 | 41 | pub fn d_ptr(&self) -> u64 { 42 | self.d_un 43 | } 44 | } 45 | 46 | impl ParseAt for Dyn { 47 | fn parse_at( 48 | endian: E, 49 | class: Class, 50 | offset: &mut usize, 51 | data: &[u8], 52 | ) -> Result { 53 | match class { 54 | Class::ELF32 => Ok(Dyn { 55 | d_tag: endian.parse_i32_at(offset, data)? as i64, 56 | d_un: endian.parse_u32_at(offset, data)? as u64, 57 | }), 58 | Class::ELF64 => Ok(Dyn { 59 | d_tag: endian.parse_i64_at(offset, data)?, 60 | d_un: endian.parse_u64_at(offset, data)?, 61 | }), 62 | } 63 | } 64 | 65 | #[inline] 66 | fn size_for(class: Class) -> usize { 67 | match class { 68 | Class::ELF32 => 8, 69 | Class::ELF64 => 16, 70 | } 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod parse_tests { 76 | use super::*; 77 | use crate::endian::{BigEndian, LittleEndian}; 78 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 79 | 80 | #[test] 81 | fn test_d_val_and_d_ptr() { 82 | let val = Dyn { 83 | d_tag: 0x01, 84 | d_un: 0x0102030405060708, 85 | }; 86 | assert_eq!(val.d_ptr(), 0x0102030405060708); 87 | assert_eq!(val.d_val(), 0x0102030405060708); 88 | } 89 | 90 | #[test] 91 | fn parse_dyn32_lsb() { 92 | test_parse_for( 93 | LittleEndian, 94 | Class::ELF32, 95 | Dyn { 96 | d_tag: 0x03020100, 97 | d_un: 0x07060504, 98 | }, 99 | ); 100 | } 101 | 102 | #[test] 103 | fn parse_dyn32_msb() { 104 | test_parse_for( 105 | BigEndian, 106 | Class::ELF32, 107 | Dyn { 108 | d_tag: 0x00010203, 109 | d_un: 0x04050607, 110 | }, 111 | ); 112 | } 113 | 114 | #[test] 115 | fn parse_dyn64_lsb() { 116 | test_parse_for( 117 | LittleEndian, 118 | Class::ELF64, 119 | Dyn { 120 | d_tag: 0x0706050403020100, 121 | d_un: 0x0F0E0D0C0B0A0908, 122 | }, 123 | ); 124 | } 125 | 126 | #[test] 127 | fn parse_dyn64_msb() { 128 | test_parse_for( 129 | BigEndian, 130 | Class::ELF64, 131 | Dyn { 132 | d_tag: 0x0001020304050607, 133 | d_un: 0x08090A0B0C0D0E0F, 134 | }, 135 | ); 136 | } 137 | 138 | #[test] 139 | fn parse_dyn32_lsb_fuzz_too_short() { 140 | test_parse_fuzz_too_short::<_, Dyn>(LittleEndian, Class::ELF32); 141 | } 142 | 143 | #[test] 144 | fn parse_dyn32_msb_fuzz_too_short() { 145 | test_parse_fuzz_too_short::<_, Dyn>(BigEndian, Class::ELF32); 146 | } 147 | 148 | #[test] 149 | fn parse_dyn64_lsb_fuzz_too_short() { 150 | test_parse_fuzz_too_short::<_, Dyn>(LittleEndian, Class::ELF64); 151 | } 152 | 153 | #[test] 154 | fn parse_dyn64_msb_fuzz_too_short() { 155 | test_parse_fuzz_too_short::<_, Dyn>(BigEndian, Class::ELF64); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/compression.rs: -------------------------------------------------------------------------------- 1 | //! Parsing [CompressionHeader] from compressed ELF sections 2 | //! 3 | //! Note: This library does not provide any decompression functionality, but 4 | //! does expose parsed ELF compression headers alongside the raw compressed data. 5 | //! 6 | //! It is up to users of the library to choose the decompression library of 7 | //! their choice when dealing with compressed section contents. 8 | use crate::endian::EndianParse; 9 | use crate::file::Class; 10 | use crate::parse::{ParseAt, ParseError}; 11 | 12 | /// C-style 32-bit ELF Compression Header definition 13 | /// 14 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 15 | #[derive(Debug)] 16 | #[repr(C)] 17 | pub struct Elf32_Chdr { 18 | pub ch_type: u32, 19 | pub ch_size: u32, 20 | pub ch_addralign: u32, 21 | } 22 | 23 | /// C-style 64-bit ELF Compression Header definition 24 | /// 25 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 26 | #[derive(Debug)] 27 | #[repr(C)] 28 | pub struct Elf64_Chdr { 29 | pub ch_type: u32, 30 | pub ch_reserved: u32, 31 | pub ch_size: u64, 32 | pub ch_addralign: u64, 33 | } 34 | 35 | #[derive(Debug, Clone, PartialEq, Eq)] 36 | pub struct CompressionHeader { 37 | pub ch_type: u32, 38 | pub ch_size: u64, 39 | pub ch_addralign: u64, 40 | } 41 | 42 | impl ParseAt for CompressionHeader { 43 | fn parse_at( 44 | endian: E, 45 | class: Class, 46 | offset: &mut usize, 47 | data: &[u8], 48 | ) -> Result { 49 | match class { 50 | Class::ELF32 => Ok(CompressionHeader { 51 | ch_type: endian.parse_u32_at(offset, data)?, 52 | ch_size: endian.parse_u32_at(offset, data)? as u64, 53 | ch_addralign: endian.parse_u32_at(offset, data)? as u64, 54 | }), 55 | Class::ELF64 => { 56 | let ch_type = endian.parse_u32_at(offset, data)?; 57 | let _ch_reserved = endian.parse_u32_at(offset, data)?; 58 | Ok(CompressionHeader { 59 | ch_type, 60 | ch_size: endian.parse_u64_at(offset, data)?, 61 | ch_addralign: endian.parse_u64_at(offset, data)?, 62 | }) 63 | } 64 | } 65 | } 66 | 67 | #[inline] 68 | fn size_for(class: Class) -> usize { 69 | match class { 70 | Class::ELF32 => 12, 71 | Class::ELF64 => 24, 72 | } 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod parse_tests { 78 | use super::*; 79 | use crate::endian::{BigEndian, LittleEndian}; 80 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 81 | 82 | #[test] 83 | fn parse_chdr32_lsb() { 84 | test_parse_for( 85 | LittleEndian, 86 | Class::ELF32, 87 | CompressionHeader { 88 | ch_type: 0x03020100, 89 | ch_size: 0x07060504, 90 | ch_addralign: 0x0B0A0908, 91 | }, 92 | ); 93 | } 94 | 95 | #[test] 96 | fn parse_chdr32_msb() { 97 | test_parse_for( 98 | BigEndian, 99 | Class::ELF32, 100 | CompressionHeader { 101 | ch_type: 0x00010203, 102 | ch_size: 0x04050607, 103 | ch_addralign: 0x08090A0B, 104 | }, 105 | ); 106 | } 107 | 108 | #[test] 109 | fn parse_chdr64_lsb() { 110 | test_parse_for( 111 | LittleEndian, 112 | Class::ELF64, 113 | CompressionHeader { 114 | ch_type: 0x03020100, 115 | ch_size: 0x0F0E0D0C0B0A0908, 116 | ch_addralign: 0x1716151413121110, 117 | }, 118 | ); 119 | } 120 | 121 | #[test] 122 | fn parse_chdr64_msb() { 123 | test_parse_for( 124 | BigEndian, 125 | Class::ELF64, 126 | CompressionHeader { 127 | ch_type: 0x00010203, 128 | ch_size: 0x08090A0B0C0D0E0F, 129 | ch_addralign: 0x1011121314151617, 130 | }, 131 | ); 132 | } 133 | 134 | #[test] 135 | fn parse_chdr32_lsb_fuzz_too_short() { 136 | test_parse_fuzz_too_short::<_, CompressionHeader>(LittleEndian, Class::ELF32); 137 | } 138 | 139 | #[test] 140 | fn parse_chdr32_msb_fuzz_too_short() { 141 | test_parse_fuzz_too_short::<_, CompressionHeader>(BigEndian, Class::ELF32); 142 | } 143 | 144 | #[test] 145 | fn parse_chdr64_lsb_fuzz_too_short() { 146 | test_parse_fuzz_too_short::<_, CompressionHeader>(LittleEndian, Class::ELF64); 147 | } 148 | 149 | #[test] 150 | fn parse_chdr64_msb_fuzz_too_short() { 151 | test_parse_fuzz_too_short::<_, CompressionHeader>(BigEndian, Class::ELF64); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/crates/v/elf.svg)](https://crates.io/crates/elf) 2 | [![](https://img.shields.io/crates/d/elf.svg)](https://crates.io/crates/elf) 3 | [![Build Status](https://github.com/cole14/rust-elf/actions/workflows/rust.yml/badge.svg)](https://github.com/cole14/rust-elf/actions) 4 | [![](https://docs.rs/elf/badge.svg)](https://docs.rs/elf/) 5 | 6 | # rust-elf 7 | 8 | The `elf` crate provides a pure-safe-rust interface for reading ELF object files. 9 | 10 | [Documentation](https://docs.rs/elf/) 11 | 12 | # Capabilities 13 | 14 | ### ✨ Works in `no_std` environments ✨ 15 | This crate provides an elf parsing interface which does not allocate or use any std 16 | features, so it can be used in `no_std` environments such as kernels and bootloaders. 17 | The no_std variant merely disables the additional stream-oriented `std:: Read + Seek` interface. 18 | All core parsing functionality is the same! 19 | 20 | ### ✨ Endian-aware ✨ 21 | This crate handles translating between file and host endianness when 22 | parsing the ELF contents and provides four endian parsing implementations 23 | optimized to support the different common use-cases for an ELF parsing library. 24 | Parsing is generic across the specifications and each trait impl represents a 25 | specification that encapsulates an interface for parsing integers from some 26 | set of allowed byte orderings. 27 | 28 | * `AnyEndian`: Dynamically parsing either byte order at runtime based on the type of ELF object being parsed. 29 | * `BigEndian`/`LittleEndian`: For tools that know they only want to parse a single given byte order known at compile time. 30 | * `NativeEndian`: For tools that know they want to parse the same byte order as the compilation target's byte order. 31 | 32 | When the limited specifications are used, errors are properly returned when asked to parse an ELF file 33 | with an unexpected byte ordering. 34 | 35 | ### ✨ Zero-alloc parser ✨ 36 | This crate implements parsing in a way that avoids heap allocations. ELF structures 37 | are parsed and stored on the stack and provided by patterns such as lazily parsed iterators 38 | that yield stack allocated rust types, or lazily parsing tables that only parse out a particular 39 | entry on table.get(index). The structures are copy-converted as needed from the underlying file 40 | data into Rust's native struct representation. 41 | 42 | ### ✨ Fuzz Tested ✨ 43 | Various parts of the library are fuzz tested for panics and crashes (see `fuzz/`). 44 | 45 | Memory safety is a core goal, as is providing a safe interface that errors on bad data 46 | over crashing or panicking. Checked integer math is used where appropriate, and ParseErrors are 47 | returned when bad or corrupted ELF structures are encountered. 48 | 49 | ### ✨ Uses only safe interfaces ✨ 50 | With memory safety a core goal, this crate contains zero unsafe code blocks of 51 | its own and only uses safe interface methods from core and std, so you can 52 | trust in rust's memory safety guarantees without also having to trust this 53 | library developer as having truly been "right" in why some unsafe block was 54 | safe. 💃 55 | 56 | Note: I'd love to see this crate be enhanced further once rust provides safe transmutes. 57 | 58 | See: 59 | 60 | ### ✨ Some zero-copy interfaces ✨ 61 | The StringTable, for instance, yields `&[u8]` and `&str` backed by the raw string table bytes. 62 | 63 | The `ElfBytes` parser type also does not make raw copies of the underlying file data to back 64 | the parser lazy parser interfaces `ParsingIterator` and `ParsingTable`. They merely wrap byte slices 65 | internally, and yield rust repr values on demand, which does entail copying of the bytes into the 66 | parsed rust-native format. 67 | 68 | Depending on the use-case, it can be more efficient to restructure the raw ELF into different layouts 69 | for more efficient interpretation, say, by re-indexing a flat table into a HashMap. `ParsingIterator`s 70 | make that easy and rustily-intuitive. 71 | 72 | The `ParsingIterator`s are also nice in that you can easily zip/enumerate/filter/collect them 73 | how you wish. Do you know that you want to do multiple passes over pairs from different tables? Just 74 | zip/collect them into another type so you only parse/endian-flip each entry once! 75 | 76 | ### ✨ Stream-based lazy i/o interface ✨ 77 | The `ElfStream` parser type takes a `std:: Read + Seek` (such as `std::fs::File`) where ranges of 78 | file contents are read lazily on-demand based on what the user wants to parse. 79 | 80 | This, alongside the bytes-oriented interface, allow you to decide which tradeoffs 81 | you want to make. If you're going to be working with the whole file contents, 82 | then the byte slice approach is probably worthwhile to minimize i/o overhead by 83 | streaming the whole file into memory at once. If you're only going to be 84 | inspecting part of the file, then the `ElfStream` approach would help avoid the 85 | overhead of reading a bunch of unused file data just to parse out a few things, (like 86 | grabbing the `.gnu.note.build-id`) 87 | 88 | ### ✨ Tiny library with no dependencies and fast compilation times ✨ 89 | Release-target compilation times on this developer's 2021 m1 macbook are sub-second. 90 | 91 | ## Example using `ElfBytes`: 92 | 93 | ```rust 94 | use elf::ElfBytes; 95 | use elf::endian::AnyEndian; 96 | use elf::note::Note; 97 | use elf::note::NoteGnuBuildId; 98 | use elf::section::SectionHeader; 99 | 100 | let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); 101 | let file_data = std::fs::read(path).expect("Could not read file."); 102 | let slice = file_data.as_slice(); 103 | let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); 104 | 105 | // Get the ELF file's build-id 106 | let abi_shdr: SectionHeader = file 107 | .section_header_by_name(".note.gnu.build-id") 108 | .expect("section table should be parseable") 109 | .expect("file should have a .note.ABI-tag section"); 110 | 111 | let notes: Vec = file 112 | .section_data_as_notes(&abi_shdr) 113 | .expect("Should be able to get note section data") 114 | .collect(); 115 | assert_eq!( 116 | notes[0], 117 | Note::GnuBuildId(NoteGnuBuildId( 118 | &[140, 51, 19, 23, 221, 90, 215, 131, 169, 13, 119 | 210, 183, 215, 77, 216, 175, 167, 110, 3, 209])) 120 | ); 121 | 122 | // Find lazy-parsing types for the common ELF sections (we want .dynsym, .dynstr, .hash) 123 | let common = file.find_common_data().expect("shdrs should parse"); 124 | let (dynsyms, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap()); 125 | let hash_table = common.sysv_hash.unwrap(); 126 | 127 | // Use the hash table to find a given symbol in it. 128 | let name = b"memset"; 129 | let (sym_idx, sym) = hash_table.find(name, &dynsyms, &strtab) 130 | .expect("hash table and symbols should parse").unwrap(); 131 | 132 | // Verify that we got the same symbol from the hash table we expected 133 | assert_eq!(sym_idx, 2); 134 | assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset"); 135 | assert_eq!(sym, dynsyms.get(sym_idx).unwrap()); 136 | ``` 137 | -------------------------------------------------------------------------------- /src/segment.rs: -------------------------------------------------------------------------------- 1 | //! Parsing the Program Header table aka Segment table aka `Elf_Phdr` 2 | use crate::endian::EndianParse; 3 | use crate::file::Class; 4 | use crate::parse::{ParseAt, ParseError, ParsingTable}; 5 | 6 | pub type SegmentTable<'data, E> = ParsingTable<'data, E, ProgramHeader>; 7 | 8 | /// C-style 32-bit ELF Program Segment Header definition 9 | /// 10 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 11 | #[derive(Debug)] 12 | #[repr(C)] 13 | pub struct Elf32_Phdr { 14 | pub p_type: u32, 15 | pub p_offset: u32, 16 | pub p_vaddr: u32, 17 | pub p_paddr: u32, 18 | pub p_filesz: u32, 19 | pub p_memsz: u32, 20 | pub p_flags: u32, 21 | pub p_align: u32, 22 | } 23 | 24 | /// C-style 64-bit ELF Program Segment Header definition 25 | /// 26 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 27 | #[derive(Debug)] 28 | #[repr(C)] 29 | pub struct Elf64_Phdr { 30 | pub p_type: u32, 31 | pub p_flags: u32, 32 | pub p_offset: u64, 33 | pub p_vaddr: u64, 34 | pub p_paddr: u64, 35 | pub p_filesz: u64, 36 | pub p_memsz: u64, 37 | pub p_align: u64, 38 | } 39 | 40 | /// Encapsulates the contents of an ELF Program Header 41 | /// 42 | /// The program header table is an array of program header structures describing 43 | /// the various segments for program execution. 44 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 45 | pub struct ProgramHeader { 46 | /// Program segment type 47 | pub p_type: u32, 48 | /// Offset into the ELF file where this segment begins 49 | pub p_offset: u64, 50 | /// Virtual adress where this segment should be loaded 51 | pub p_vaddr: u64, 52 | /// Physical address where this segment should be loaded 53 | pub p_paddr: u64, 54 | /// Size of this segment in the file 55 | pub p_filesz: u64, 56 | /// Size of this segment in memory 57 | pub p_memsz: u64, 58 | /// Flags for this segment 59 | pub p_flags: u32, 60 | /// file and memory alignment 61 | pub p_align: u64, 62 | } 63 | 64 | impl ParseAt for ProgramHeader { 65 | fn parse_at( 66 | endian: E, 67 | class: Class, 68 | offset: &mut usize, 69 | data: &[u8], 70 | ) -> Result { 71 | if class == Class::ELF32 { 72 | return Ok(ProgramHeader { 73 | p_type: endian.parse_u32_at(offset, data)?, 74 | p_offset: endian.parse_u32_at(offset, data)? as u64, 75 | p_vaddr: endian.parse_u32_at(offset, data)? as u64, 76 | p_paddr: endian.parse_u32_at(offset, data)? as u64, 77 | p_filesz: endian.parse_u32_at(offset, data)? as u64, 78 | p_memsz: endian.parse_u32_at(offset, data)? as u64, 79 | p_flags: endian.parse_u32_at(offset, data)?, 80 | p_align: endian.parse_u32_at(offset, data)? as u64, 81 | }); 82 | } 83 | 84 | // Note: 64-bit fields are in a different order 85 | let p_type = endian.parse_u32_at(offset, data)?; 86 | let p_flags = endian.parse_u32_at(offset, data)?; 87 | let p_offset = endian.parse_u64_at(offset, data)?; 88 | let p_vaddr = endian.parse_u64_at(offset, data)?; 89 | let p_paddr = endian.parse_u64_at(offset, data)?; 90 | let p_filesz = endian.parse_u64_at(offset, data)?; 91 | let p_memsz = endian.parse_u64_at(offset, data)?; 92 | let p_align = endian.parse_u64_at(offset, data)?; 93 | Ok(ProgramHeader { 94 | p_type, 95 | p_offset, 96 | p_vaddr, 97 | p_paddr, 98 | p_filesz, 99 | p_memsz, 100 | p_flags, 101 | p_align, 102 | }) 103 | } 104 | 105 | #[inline] 106 | fn size_for(class: Class) -> usize { 107 | match class { 108 | Class::ELF32 => 32, 109 | Class::ELF64 => 56, 110 | } 111 | } 112 | } 113 | 114 | impl ProgramHeader { 115 | /// Helper method which uses checked integer math to get a tuple of (start, end) for 116 | /// the location in bytes for this ProgramHeader's data in the file. 117 | /// i.e. (p_offset, p_offset + p_filesz) 118 | pub(crate) fn get_file_data_range(&self) -> Result<(usize, usize), ParseError> { 119 | let start: usize = self.p_offset.try_into()?; 120 | let size: usize = self.p_filesz.try_into()?; 121 | let end = start.checked_add(size).ok_or(ParseError::IntegerOverflow)?; 122 | Ok((start, end)) 123 | } 124 | } 125 | 126 | #[cfg(test)] 127 | mod parse_tests { 128 | use super::*; 129 | use crate::endian::{BigEndian, LittleEndian}; 130 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 131 | 132 | #[test] 133 | fn parse_phdr32_lsb() { 134 | test_parse_for( 135 | LittleEndian, 136 | Class::ELF32, 137 | ProgramHeader { 138 | p_type: 0x03020100, 139 | p_offset: 0x07060504, 140 | p_vaddr: 0xB0A0908, 141 | p_paddr: 0x0F0E0D0C, 142 | p_filesz: 0x13121110, 143 | p_memsz: 0x17161514, 144 | p_flags: 0x1B1A1918, 145 | p_align: 0x1F1E1D1C, 146 | }, 147 | ); 148 | } 149 | 150 | #[test] 151 | fn parse_phdr32_msb() { 152 | test_parse_for( 153 | BigEndian, 154 | Class::ELF32, 155 | ProgramHeader { 156 | p_type: 0x00010203, 157 | p_offset: 0x04050607, 158 | p_vaddr: 0x08090A0B, 159 | p_paddr: 0x0C0D0E0F, 160 | p_filesz: 0x10111213, 161 | p_memsz: 0x14151617, 162 | p_flags: 0x18191A1B, 163 | p_align: 0x1C1D1E1F, 164 | }, 165 | ); 166 | } 167 | 168 | #[test] 169 | fn parse_phdr64_lsb() { 170 | test_parse_for( 171 | LittleEndian, 172 | Class::ELF64, 173 | ProgramHeader { 174 | p_type: 0x03020100, 175 | p_offset: 0x0F0E0D0C0B0A0908, 176 | p_vaddr: 0x1716151413121110, 177 | p_paddr: 0x1F1E1D1C1B1A1918, 178 | p_filesz: 0x2726252423222120, 179 | p_memsz: 0x2F2E2D2C2B2A2928, 180 | p_flags: 0x07060504, 181 | p_align: 0x3736353433323130, 182 | }, 183 | ); 184 | } 185 | 186 | #[test] 187 | fn parse_phdr64_msb() { 188 | test_parse_for( 189 | BigEndian, 190 | Class::ELF64, 191 | ProgramHeader { 192 | p_type: 0x00010203, 193 | p_offset: 0x08090A0B0C0D0E0F, 194 | p_vaddr: 0x1011121314151617, 195 | p_paddr: 0x18191A1B1C1D1E1F, 196 | p_filesz: 0x2021222324252627, 197 | p_memsz: 0x28292A2B2C2D2E2F, 198 | p_flags: 0x04050607, 199 | p_align: 0x3031323334353637, 200 | }, 201 | ); 202 | } 203 | 204 | #[test] 205 | fn parse_phdr32_lsb_fuzz_too_short() { 206 | test_parse_fuzz_too_short::<_, ProgramHeader>(LittleEndian, Class::ELF32); 207 | } 208 | 209 | #[test] 210 | fn parse_phdr32_msb_fuzz_too_short() { 211 | test_parse_fuzz_too_short::<_, ProgramHeader>(BigEndian, Class::ELF32); 212 | } 213 | 214 | #[test] 215 | fn parse_phdr64_lsb_fuzz_too_short() { 216 | test_parse_fuzz_too_short::<_, ProgramHeader>(LittleEndian, Class::ELF64); 217 | } 218 | 219 | #[test] 220 | fn parse_phdr64_msb_fuzz_too_short() { 221 | test_parse_fuzz_too_short::<_, ProgramHeader>(BigEndian, Class::ELF64); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The `elf` crate provides a pure-safe-rust interface for reading ELF object files. 2 | //! 3 | //! # Capabilities 4 | //! 5 | //! ### ✨ Works in `no_std` environments ✨ 6 | //! This crate provides an elf parsing interface which does not allocate or use any std 7 | //! features, so it can be used in `no_std` environments such as kernels and bootloaders. 8 | //! The no_std variant merely disables the additional stream-oriented `std:: Read + Seek` interface. 9 | //! All core parsing functionality is the same! 10 | //! 11 | //! ### ✨ Endian-aware ✨ 12 | //! This crate handles translating between file and host endianness when 13 | //! parsing the ELF contents and provides four endian parsing implementations 14 | //! optimized to support the different common use-cases for an ELF parsing library. 15 | //! Parsing is generic across the specifications and each trait impl represents a 16 | //! specification that encapsulates an interface for parsing integers from some 17 | //! set of allowed byte orderings. 18 | //! 19 | //! * [AnyEndian](endian::AnyEndian): Dynamically parsing either byte order at runtime based on the type of ELF object being parsed. 20 | //! * [BigEndian](endian::BigEndian)/[LittleEndian](endian::LittleEndian): For tools that know they only want to parse a single given byte order known at compile time. 21 | //! * [NativeEndian](type@endian::NativeEndian): For tools that know they want to parse the same byte order as the compilation target's byte order. 22 | //! 23 | //! When the limited specifications are used, errors are properly returned when asked to parse an ELF file 24 | //! with an unexpected byte ordering. 25 | //! 26 | //! ### ✨ Zero-alloc parser ✨ 27 | //! This crate implements parsing in a way that avoids heap allocations. ELF structures 28 | //! are parsed and stored on the stack and provided by patterns such as lazily parsed iterators 29 | //! that yield stack allocated rust types, or lazily parsing tables that only parse out a particular 30 | //! entry on table.get(index). The structures are copy-converted as needed from the underlying file 31 | //! data into Rust's native struct representation. 32 | //! 33 | //! ### ✨ Fuzz Tested ✨ 34 | //! Various parts of the library are fuzz tested for panics and crashes (see `fuzz/`). 35 | //! 36 | //! Memory safety is a core goal, as is providing a safe interface that errors on bad data 37 | //! over crashing or panicking. Checked integer math is used where appropriate, and ParseErrors are 38 | //! returned when bad or corrupted ELF structures are encountered. 39 | //! 40 | //! ### ✨ Uses only safe interfaces ✨ 41 | //! With memory safety a core goal, this crate contains zero unsafe code blocks 42 | //! of its own and only uses safe interface methods from core and std, so you can 43 | //! trust in rust's memory safety guarantees without also having to trust this 44 | //! library developer as having truly been "right" in why some unsafe block was 45 | //! safe. 💃 46 | //! 47 | //! Note: I'd love to see this crate be enhanced further once rust provides safe transmutes. 48 | //! 49 | //! See: 50 | //! 51 | //! ### ✨ Some zero-copy interfaces ✨ 52 | //! The StringTable, for instance, yields `&[u8]` and `&str` backed by the raw string table bytes. 53 | //! 54 | //! The [ElfBytes] parser type also does not make raw copies of the underlying file data to back 55 | //! the parser lazy parser interfaces `ParsingIterator` and `ParsingTable`. They merely wrap byte slices 56 | //! internally, and yield rust repr values on demand, which does entail copying of the bytes into the 57 | //! parsed rust-native format. 58 | //! 59 | //! Depending on the use-case, it can be more efficient to restructure the raw ELF into different layouts 60 | //! for more efficient interpretation, say, by re-indexing a flat table into a HashMap. `ParsingIterator`s 61 | //! make that easy and rustily-intuitive. 62 | //! 63 | //! The `ParsingIterator`s are also nice in that you can easily zip/enumerate/filter/collect them 64 | //! how you wish. Do you know that you want to do multiple passes over pairs from different tables? Just 65 | //! zip/collect them into another type so you only parse/endian-flip each entry once! 66 | //! 67 | //! ### ✨ Stream-based lazy i/o interface ✨ 68 | //! The [ElfStream] parser type takes a `std:: Read + Seek` (such as `std::fs::File`) where ranges of 69 | //! file contents are read lazily on-demand based on what the user wants to parse. 70 | //! 71 | //! This, alongside the bytes-oriented interface, allow you to decide which tradeoffs 72 | //! you want to make. If you're going to be working with the whole file contents, 73 | //! then the byte slice approach is probably worthwhile to minimize i/o overhead by 74 | //! streaming the whole file into memory at once. If you're only going to be 75 | //! inspecting part of the file, then the [ElfStream] approach would help avoid the 76 | //! overhead of reading a bunch of unused file data just to parse out a few things, (like 77 | //! grabbing the `.gnu.note.build-id`) 78 | //! 79 | //! ### ✨ Tiny library with no dependencies and fast compilation times ✨ 80 | //! Release-target compilation times on this developer's 2021 m1 macbook are sub-second. 81 | //! 82 | //! Example using [ElfBytes]: 83 | //! ``` 84 | //! use elf::ElfBytes; 85 | //! use elf::endian::AnyEndian; 86 | //! use elf::note::Note; 87 | //! use elf::note::NoteGnuBuildId; 88 | //! use elf::section::SectionHeader; 89 | //! 90 | //! let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so"); 91 | //! let file_data = std::fs::read(path).expect("Could not read file."); 92 | //! let slice = file_data.as_slice(); 93 | //! let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); 94 | //! 95 | //! // Get the ELF file's build-id 96 | //! let abi_shdr: SectionHeader = file 97 | //! .section_header_by_name(".note.gnu.build-id") 98 | //! .expect("section table should be parseable") 99 | //! .expect("file should have a .note.ABI-tag section"); 100 | //! 101 | //! let notes: Vec = file 102 | //! .section_data_as_notes(&abi_shdr) 103 | //! .expect("Should be able to get note section data") 104 | //! .collect(); 105 | //! assert_eq!( 106 | //! notes[0], 107 | //! Note::GnuBuildId(NoteGnuBuildId( 108 | //! &[140, 51, 19, 23, 221, 90, 215, 131, 169, 13, 109 | //! 210, 183, 215, 77, 216, 175, 167, 110, 3, 209])) 110 | //! ); 111 | //! 112 | //! // Find lazy-parsing types for the common ELF sections (we want .dynsym, .dynstr, .hash) 113 | //! let common = file.find_common_data().expect("shdrs should parse"); 114 | //! let (dynsyms, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap()); 115 | //! let hash_table = common.sysv_hash.unwrap(); 116 | //! 117 | //! // Use the hash table to find a given symbol in it. 118 | //! let name = b"memset"; 119 | //! let (sym_idx, sym) = hash_table.find(name, &dynsyms, &strtab) 120 | //! .expect("hash table and symbols should parse").unwrap(); 121 | //! 122 | //! // Verify that we got the same symbol from the hash table we expected 123 | //! assert_eq!(sym_idx, 2); 124 | //! assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset"); 125 | //! assert_eq!(sym, dynsyms.get(sym_idx).unwrap()); 126 | //! ``` 127 | 128 | #![cfg_attr(not(feature = "std"), no_std)] 129 | #![warn(rust_2018_idioms)] 130 | #![deny(missing_debug_implementations)] 131 | #![forbid(unsafe_code)] 132 | 133 | #[cfg(all(feature = "alloc", not(feature = "std")))] 134 | extern crate alloc; 135 | 136 | pub mod abi; 137 | 138 | pub mod compression; 139 | pub mod dynamic; 140 | pub mod file; 141 | pub mod gnu_symver; 142 | pub mod hash; 143 | pub mod note; 144 | pub mod relocation; 145 | pub mod section; 146 | pub mod segment; 147 | pub mod string_table; 148 | pub mod symbol; 149 | 150 | #[cfg(feature = "to_str")] 151 | pub mod to_str; 152 | 153 | pub mod endian; 154 | pub mod parse; 155 | 156 | mod elf_bytes; 157 | pub use elf_bytes::CommonElfData; 158 | pub use elf_bytes::ElfBytes; 159 | 160 | #[cfg(feature = "std")] 161 | mod elf_stream; 162 | #[cfg(feature = "std")] 163 | pub use elf_stream::ElfStream; 164 | 165 | pub use parse::ParseError; 166 | -------------------------------------------------------------------------------- /src/section.rs: -------------------------------------------------------------------------------- 1 | //! Parsing the Section Header table 2 | use crate::endian::EndianParse; 3 | use crate::file::Class; 4 | use crate::parse::{ParseAt, ParseError, ParsingTable}; 5 | 6 | pub type SectionHeaderTable<'data, E> = ParsingTable<'data, E, SectionHeader>; 7 | 8 | /// C-style 32-bit ELF Section Header definition 9 | /// 10 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 11 | #[derive(Debug)] 12 | #[repr(C)] 13 | pub struct Elf32_Shdr { 14 | pub sh_name: u32, 15 | pub sh_type: u32, 16 | pub sh_flags: u32, 17 | pub sh_addr: u32, 18 | pub sh_offset: u32, 19 | pub sh_size: u32, 20 | pub sh_link: u32, 21 | pub sh_info: u32, 22 | pub sh_addralign: u32, 23 | pub sh_entsize: u32, 24 | } 25 | 26 | /// C-style 64-bit ELF Section Header definition 27 | /// 28 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 29 | #[derive(Debug)] 30 | #[repr(C)] 31 | pub struct Elf64_Shdr { 32 | pub sh_name: u32, 33 | pub sh_type: u32, 34 | pub sh_flags: u64, 35 | pub sh_addr: u64, 36 | pub sh_offset: u64, 37 | pub sh_size: u64, 38 | pub sh_link: u32, 39 | pub sh_info: u32, 40 | pub sh_addralign: u64, 41 | pub sh_entsize: u64, 42 | } 43 | 44 | /// Encapsulates the contents of an ELF Section Header 45 | /// 46 | /// This is a Rust-native type that represents a Section Header that is bit-width-agnostic. 47 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 48 | pub struct SectionHeader { 49 | /// Section Name 50 | pub sh_name: u32, 51 | /// Section Type 52 | pub sh_type: u32, 53 | /// Section Flags 54 | pub sh_flags: u64, 55 | /// in-memory address where this section is loaded 56 | pub sh_addr: u64, 57 | /// Byte-offset into the file where this section starts 58 | pub sh_offset: u64, 59 | /// Section size in bytes 60 | pub sh_size: u64, 61 | /// Defined by section type 62 | pub sh_link: u32, 63 | /// Defined by section type 64 | pub sh_info: u32, 65 | /// address alignment 66 | pub sh_addralign: u64, 67 | /// size of an entry if section data is an array of entries 68 | pub sh_entsize: u64, 69 | } 70 | 71 | impl ParseAt for SectionHeader { 72 | fn parse_at( 73 | endian: E, 74 | class: Class, 75 | offset: &mut usize, 76 | data: &[u8], 77 | ) -> Result { 78 | match class { 79 | Class::ELF32 => Ok(SectionHeader { 80 | sh_name: endian.parse_u32_at(offset, data)?, 81 | sh_type: endian.parse_u32_at(offset, data)?, 82 | sh_flags: endian.parse_u32_at(offset, data)? as u64, 83 | sh_addr: endian.parse_u32_at(offset, data)? as u64, 84 | sh_offset: endian.parse_u32_at(offset, data)? as u64, 85 | sh_size: endian.parse_u32_at(offset, data)? as u64, 86 | sh_link: endian.parse_u32_at(offset, data)?, 87 | sh_info: endian.parse_u32_at(offset, data)?, 88 | sh_addralign: endian.parse_u32_at(offset, data)? as u64, 89 | sh_entsize: endian.parse_u32_at(offset, data)? as u64, 90 | }), 91 | Class::ELF64 => Ok(SectionHeader { 92 | sh_name: endian.parse_u32_at(offset, data)?, 93 | sh_type: endian.parse_u32_at(offset, data)?, 94 | sh_flags: endian.parse_u64_at(offset, data)?, 95 | sh_addr: endian.parse_u64_at(offset, data)?, 96 | sh_offset: endian.parse_u64_at(offset, data)?, 97 | sh_size: endian.parse_u64_at(offset, data)?, 98 | sh_link: endian.parse_u32_at(offset, data)?, 99 | sh_info: endian.parse_u32_at(offset, data)?, 100 | sh_addralign: endian.parse_u64_at(offset, data)?, 101 | sh_entsize: endian.parse_u64_at(offset, data)?, 102 | }), 103 | } 104 | } 105 | 106 | #[inline] 107 | fn size_for(class: Class) -> usize { 108 | match class { 109 | Class::ELF32 => 40, 110 | Class::ELF64 => 64, 111 | } 112 | } 113 | } 114 | 115 | impl SectionHeader { 116 | /// Helper method which uses checked integer math to get a tuple of (start,end) for 117 | /// this SectionHeader's (sh_offset, sh_offset + sh_size) 118 | pub(crate) fn get_data_range(&self) -> Result<(usize, usize), ParseError> { 119 | let start: usize = self.sh_offset.try_into()?; 120 | let size: usize = self.sh_size.try_into()?; 121 | let end = start.checked_add(size).ok_or(ParseError::IntegerOverflow)?; 122 | Ok((start, end)) 123 | } 124 | } 125 | 126 | #[cfg(test)] 127 | mod parse_tests { 128 | use super::*; 129 | use crate::endian::{BigEndian, LittleEndian}; 130 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 131 | 132 | #[test] 133 | fn parse_shdr32_lsb() { 134 | test_parse_for( 135 | LittleEndian, 136 | Class::ELF32, 137 | SectionHeader { 138 | sh_name: 0x03020100, 139 | sh_type: 0x07060504, 140 | sh_flags: 0xB0A0908, 141 | sh_addr: 0x0F0E0D0C, 142 | sh_offset: 0x13121110, 143 | sh_size: 0x17161514, 144 | sh_link: 0x1B1A1918, 145 | sh_info: 0x1F1E1D1C, 146 | sh_addralign: 0x23222120, 147 | sh_entsize: 0x27262524, 148 | }, 149 | ); 150 | } 151 | 152 | #[test] 153 | fn parse_shdr32_msb() { 154 | test_parse_for( 155 | BigEndian, 156 | Class::ELF32, 157 | SectionHeader { 158 | sh_name: 0x00010203, 159 | sh_type: 0x04050607, 160 | sh_flags: 0x08090A0B, 161 | sh_addr: 0x0C0D0E0F, 162 | sh_offset: 0x10111213, 163 | sh_size: 0x14151617, 164 | sh_link: 0x18191A1B, 165 | sh_info: 0x1C1D1E1F, 166 | sh_addralign: 0x20212223, 167 | sh_entsize: 0x24252627, 168 | }, 169 | ); 170 | } 171 | 172 | #[test] 173 | fn parse_shdr64_lsb() { 174 | test_parse_for( 175 | LittleEndian, 176 | Class::ELF64, 177 | SectionHeader { 178 | sh_name: 0x03020100, 179 | sh_type: 0x07060504, 180 | sh_flags: 0x0F0E0D0C0B0A0908, 181 | sh_addr: 0x1716151413121110, 182 | sh_offset: 0x1F1E1D1C1B1A1918, 183 | sh_size: 0x2726252423222120, 184 | sh_link: 0x2B2A2928, 185 | sh_info: 0x2F2E2D2C, 186 | sh_addralign: 0x3736353433323130, 187 | sh_entsize: 0x3F3E3D3C3B3A3938, 188 | }, 189 | ); 190 | } 191 | 192 | #[test] 193 | fn parse_shdr64_msb() { 194 | test_parse_for( 195 | BigEndian, 196 | Class::ELF64, 197 | SectionHeader { 198 | sh_name: 0x00010203, 199 | sh_type: 0x04050607, 200 | sh_flags: 0x08090A0B0C0D0E0F, 201 | sh_addr: 0x1011121314151617, 202 | sh_offset: 0x18191A1B1C1D1E1F, 203 | sh_size: 0x2021222324252627, 204 | sh_link: 0x28292A2B, 205 | sh_info: 0x2C2D2E2F, 206 | sh_addralign: 0x3031323334353637, 207 | sh_entsize: 0x38393A3B3C3D3E3F, 208 | }, 209 | ); 210 | } 211 | 212 | #[test] 213 | fn parse_shdr32_lsb_fuzz_too_short() { 214 | test_parse_fuzz_too_short::<_, SectionHeader>(LittleEndian, Class::ELF32); 215 | } 216 | 217 | #[test] 218 | fn parse_shdr32_msb_fuzz_too_short() { 219 | test_parse_fuzz_too_short::<_, SectionHeader>(BigEndian, Class::ELF32); 220 | } 221 | 222 | #[test] 223 | fn parse_shdr64_lsb_fuzz_too_short() { 224 | test_parse_fuzz_too_short::<_, SectionHeader>(LittleEndian, Class::ELF64); 225 | } 226 | 227 | #[test] 228 | fn parse_shdr64_msb_fuzz_too_short() { 229 | test_parse_fuzz_too_short::<_, SectionHeader>(BigEndian, Class::ELF64); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/symbol.rs: -------------------------------------------------------------------------------- 1 | //! Parsing symbol table sections: `.symtab`, `.dynsym` 2 | use crate::abi; 3 | use crate::endian::EndianParse; 4 | use crate::file::Class; 5 | use crate::parse::{ParseAt, ParseError, ParsingTable}; 6 | 7 | pub type SymbolTable<'data, E> = ParsingTable<'data, E, Symbol>; 8 | 9 | /// C-style 32-bit ELF Symbol definition 10 | /// 11 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 12 | #[derive(Debug)] 13 | #[repr(C)] 14 | pub struct Elf32_Sym { 15 | pub st_name: u32, 16 | pub st_value: u32, 17 | pub st_size: u32, 18 | pub st_info: u8, 19 | pub st_other: u8, 20 | pub st_shndx: u16, 21 | } 22 | 23 | /// C-style 64-bit ELF Symbol definition 24 | /// 25 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 26 | #[derive(Debug)] 27 | #[repr(C)] 28 | pub struct Elf64_Sym { 29 | pub st_name: u32, 30 | pub st_info: u8, 31 | pub st_other: u8, 32 | pub st_shndx: u16, 33 | pub st_value: u64, 34 | pub st_size: u64, 35 | } 36 | 37 | #[derive(Debug, Clone, PartialEq, Eq)] 38 | pub struct Symbol { 39 | /// This member holds an index into the symbol table's string table, 40 | /// which holds the character representations of the symbol names. If the 41 | /// value is non-zero, it represents a string table index that gives the 42 | /// symbol name. Otherwise, the symbol table entry has no name. 43 | pub st_name: u32, 44 | 45 | /// Every symbol table entry is defined in relation to some section. This 46 | /// member holds the relevant section header table index. As the sh_link and 47 | /// sh_info interpretation table and the related text describe, some section 48 | /// indexes indicate special meanings. 49 | /// 50 | /// If this member contains SHN_XINDEX, then the actual section header index 51 | /// is too large to fit in this field. The actual value is contained in the 52 | /// associated section of type SHT_SYMTAB_SHNDX. 53 | pub st_shndx: u16, 54 | 55 | /// This member specifies the symbol's type and binding attributes. 56 | pub st_info: u8, 57 | 58 | /// This member currently specifies a symbol's visibility. 59 | pub st_other: u8, 60 | 61 | /// This member gives the value of the associated symbol. Depending on the 62 | /// context, this may be an absolute value, an address, and so on. 63 | /// 64 | /// * In relocatable files, st_value holds alignment constraints for a 65 | /// symbol whose section index is SHN_COMMON. 66 | /// * In relocatable files, st_value holds a section offset for a defined 67 | /// symbol. st_value is an offset from the beginning of the section that 68 | /// st_shndx identifies. 69 | /// * In executable and shared object files, st_value holds a virtual 70 | /// address. To make these files' symbols more useful for the dynamic 71 | /// linker, the section offset (file interpretation) gives way to a 72 | /// virtual address (memory interpretation) for which the section number 73 | /// is irrelevant. 74 | pub st_value: u64, 75 | 76 | /// This member gives the symbol's size. 77 | /// For example, a data object's size is the number of bytes contained in 78 | /// the object. This member holds 0 if the symbol has no size or an unknown 79 | /// size. 80 | pub st_size: u64, 81 | } 82 | 83 | impl Symbol { 84 | /// Returns true if a symbol is undefined in this ELF object. 85 | /// 86 | /// When linking and loading, undefined symbols in this object get linked to 87 | /// a defined symbol in another object. 88 | pub fn is_undefined(&self) -> bool { 89 | self.st_shndx == abi::SHN_UNDEF 90 | } 91 | 92 | pub fn st_symtype(&self) -> u8 { 93 | self.st_info & 0xf 94 | } 95 | 96 | pub fn st_bind(&self) -> u8 { 97 | self.st_info >> 4 98 | } 99 | 100 | pub fn st_vis(&self) -> u8 { 101 | self.st_other & 0x3 102 | } 103 | } 104 | 105 | impl ParseAt for Symbol { 106 | fn parse_at( 107 | endian: E, 108 | class: Class, 109 | offset: &mut usize, 110 | data: &[u8], 111 | ) -> Result { 112 | let st_name: u32; 113 | let st_value: u64; 114 | let st_size: u64; 115 | let st_shndx: u16; 116 | let st_info: u8; 117 | let st_other: u8; 118 | 119 | if class == Class::ELF32 { 120 | st_name = endian.parse_u32_at(offset, data)?; 121 | st_value = endian.parse_u32_at(offset, data)? as u64; 122 | st_size = endian.parse_u32_at(offset, data)? as u64; 123 | st_info = endian.parse_u8_at(offset, data)?; 124 | st_other = endian.parse_u8_at(offset, data)?; 125 | st_shndx = endian.parse_u16_at(offset, data)?; 126 | } else { 127 | st_name = endian.parse_u32_at(offset, data)?; 128 | st_info = endian.parse_u8_at(offset, data)?; 129 | st_other = endian.parse_u8_at(offset, data)?; 130 | st_shndx = endian.parse_u16_at(offset, data)?; 131 | st_value = endian.parse_u64_at(offset, data)?; 132 | st_size = endian.parse_u64_at(offset, data)?; 133 | } 134 | 135 | Ok(Symbol { 136 | st_name, 137 | st_value, 138 | st_size, 139 | st_shndx, 140 | st_info, 141 | st_other, 142 | }) 143 | } 144 | 145 | #[inline] 146 | fn size_for(class: Class) -> usize { 147 | match class { 148 | Class::ELF32 => 16, 149 | Class::ELF64 => 24, 150 | } 151 | } 152 | } 153 | 154 | #[cfg(test)] 155 | mod symbol_tests { 156 | use super::*; 157 | 158 | #[test] 159 | fn symbol_undefined() { 160 | let undef_sym = Symbol { 161 | st_name: 0, 162 | st_value: 0, 163 | st_size: 0, 164 | st_shndx: 0, 165 | st_info: 0, 166 | st_other: 0, 167 | }; 168 | assert!(undef_sym.is_undefined()); 169 | 170 | let def_sym = Symbol { 171 | st_name: 0, 172 | st_value: 0, 173 | st_size: 0, 174 | st_shndx: 42, 175 | st_info: 0, 176 | st_other: 0, 177 | }; 178 | assert!(!def_sym.is_undefined()); 179 | } 180 | } 181 | 182 | #[cfg(test)] 183 | mod parse_tests { 184 | use super::*; 185 | use crate::endian::{BigEndian, LittleEndian}; 186 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 187 | 188 | #[test] 189 | fn parse_sym32_lsb() { 190 | test_parse_for( 191 | LittleEndian, 192 | Class::ELF32, 193 | Symbol { 194 | st_name: 0x03020100, 195 | st_value: 0x07060504, 196 | st_size: 0x0B0A0908, 197 | st_shndx: 0x0F0E, 198 | st_info: 0x0C, 199 | st_other: 0x0D, 200 | }, 201 | ); 202 | } 203 | 204 | #[test] 205 | fn parse_sym32_msb() { 206 | test_parse_for( 207 | BigEndian, 208 | Class::ELF32, 209 | Symbol { 210 | st_name: 0x00010203, 211 | st_value: 0x04050607, 212 | st_size: 0x08090A0B, 213 | st_shndx: 0x0E0F, 214 | st_info: 0x0C, 215 | st_other: 0x0D, 216 | }, 217 | ); 218 | } 219 | 220 | #[test] 221 | fn parse_sym64_lsb() { 222 | test_parse_for( 223 | LittleEndian, 224 | Class::ELF64, 225 | Symbol { 226 | st_name: 0x03020100, 227 | st_value: 0x0F0E0D0C0B0A0908, 228 | st_size: 0x1716151413121110, 229 | st_shndx: 0x0706, 230 | st_info: 0x04, 231 | st_other: 0x05, 232 | }, 233 | ); 234 | } 235 | 236 | #[test] 237 | fn parse_sym64_msb() { 238 | test_parse_for( 239 | BigEndian, 240 | Class::ELF64, 241 | Symbol { 242 | st_name: 0x00010203, 243 | st_value: 0x08090A0B0C0D0E0F, 244 | st_size: 0x1011121314151617, 245 | st_shndx: 0x0607, 246 | st_info: 0x04, 247 | st_other: 0x05, 248 | }, 249 | ); 250 | } 251 | 252 | #[test] 253 | fn parse_sym32_lsb_fuzz_too_short() { 254 | test_parse_fuzz_too_short::<_, Symbol>(LittleEndian, Class::ELF32); 255 | } 256 | 257 | #[test] 258 | fn parse_sym32_msb_fuzz_too_short() { 259 | test_parse_fuzz_too_short::<_, Symbol>(BigEndian, Class::ELF32); 260 | } 261 | 262 | #[test] 263 | fn parse_sym64_lsb_fuzz_too_short() { 264 | test_parse_fuzz_too_short::<_, Symbol>(LittleEndian, Class::ELF64); 265 | } 266 | 267 | #[test] 268 | fn parse_sym64_msb_fuzz_too_short() { 269 | test_parse_fuzz_too_short::<_, Symbol>(BigEndian, Class::ELF64); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/relocation.rs: -------------------------------------------------------------------------------- 1 | //! Parsing relocation sections: `.rel.*`, `.rela.*`, [SHT_REL](crate::abi::SHT_REL), [SHT_RELA](crate::abi::SHT_RELA) 2 | use crate::endian::EndianParse; 3 | use crate::file::Class; 4 | use crate::parse::{ParseAt, ParseError, ParsingIterator}; 5 | 6 | pub type RelIterator<'data, E> = ParsingIterator<'data, E, Rel>; 7 | pub type RelaIterator<'data, E> = ParsingIterator<'data, E, Rela>; 8 | 9 | /// C-style 32-bit ELF Relocation definition 10 | /// 11 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 12 | #[derive(Debug)] 13 | #[repr(C)] 14 | pub struct Elf32_Rel { 15 | pub r_offset: u32, 16 | pub r_info: u32, 17 | } 18 | 19 | /// C-style 64-bit ELF Relocation definition 20 | /// 21 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 22 | #[derive(Debug)] 23 | #[repr(C)] 24 | pub struct Elf64_Rel { 25 | pub r_offset: u64, 26 | pub r_info: u64, 27 | } 28 | 29 | #[derive(Debug, Clone, PartialEq, Eq)] 30 | pub struct Rel { 31 | pub r_offset: u64, 32 | pub r_sym: u32, 33 | pub r_type: u32, 34 | } 35 | 36 | impl ParseAt for Rel { 37 | fn parse_at( 38 | endian: E, 39 | class: Class, 40 | offset: &mut usize, 41 | data: &[u8], 42 | ) -> Result { 43 | match class { 44 | Class::ELF32 => { 45 | let r_offset = endian.parse_u32_at(offset, data)? as u64; 46 | let r_info = endian.parse_u32_at(offset, data)?; 47 | Ok(Rel { 48 | r_offset, 49 | r_sym: r_info >> 8, 50 | r_type: r_info & 0xFF, 51 | }) 52 | } 53 | Class::ELF64 => { 54 | let r_offset = endian.parse_u64_at(offset, data)?; 55 | let r_info = endian.parse_u64_at(offset, data)?; 56 | Ok(Rel { 57 | r_offset, 58 | r_sym: (r_info >> 32) as u32, 59 | r_type: (r_info & 0xFFFFFFFF) as u32, 60 | }) 61 | } 62 | } 63 | } 64 | 65 | #[inline] 66 | fn size_for(class: Class) -> usize { 67 | match class { 68 | Class::ELF32 => 8, 69 | Class::ELF64 => 16, 70 | } 71 | } 72 | } 73 | 74 | /// C-style 32-bit ELF Relocation (with addend) definition 75 | /// 76 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 77 | #[derive(Debug)] 78 | #[repr(C)] 79 | pub struct Elf32_Rela { 80 | pub r_offset: u32, 81 | pub r_info: u32, 82 | pub r_addend: i32, 83 | } 84 | 85 | /// C-style 64-bit ELF Relocation (with addend) definition 86 | /// 87 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 88 | #[derive(Debug)] 89 | #[repr(C)] 90 | pub struct Elf64_Rela { 91 | pub r_offset: u64, 92 | pub r_info: u64, 93 | pub r_addend: i64, 94 | } 95 | 96 | #[derive(Debug, Clone, PartialEq, Eq)] 97 | pub struct Rela { 98 | pub r_offset: u64, 99 | pub r_sym: u32, 100 | pub r_type: u32, 101 | pub r_addend: i64, 102 | } 103 | 104 | impl ParseAt for Rela { 105 | fn parse_at( 106 | endian: E, 107 | class: Class, 108 | offset: &mut usize, 109 | data: &[u8], 110 | ) -> Result { 111 | match class { 112 | Class::ELF32 => { 113 | let r_offset = endian.parse_u32_at(offset, data)? as u64; 114 | let r_info = endian.parse_u32_at(offset, data)?; 115 | let r_addend = endian.parse_i32_at(offset, data)? as i64; 116 | Ok(Rela { 117 | r_offset, 118 | r_sym: r_info >> 8, 119 | r_type: r_info & 0xFF, 120 | r_addend, 121 | }) 122 | } 123 | Class::ELF64 => { 124 | let r_offset = endian.parse_u64_at(offset, data)?; 125 | let r_info = endian.parse_u64_at(offset, data)?; 126 | let r_addend = endian.parse_i64_at(offset, data)?; 127 | Ok(Rela { 128 | r_offset, 129 | r_sym: (r_info >> 32) as u32, 130 | r_type: (r_info & 0xFFFFFFFF) as u32, 131 | r_addend, 132 | }) 133 | } 134 | } 135 | } 136 | 137 | #[inline] 138 | fn size_for(class: Class) -> usize { 139 | match class { 140 | Class::ELF32 => 12, 141 | Class::ELF64 => 24, 142 | } 143 | } 144 | } 145 | 146 | #[cfg(test)] 147 | mod parse_tests { 148 | use super::*; 149 | use crate::endian::{BigEndian, LittleEndian}; 150 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 151 | 152 | #[test] 153 | fn parse_rel32_lsb() { 154 | test_parse_for( 155 | LittleEndian, 156 | Class::ELF32, 157 | Rel { 158 | r_offset: 0x03020100, 159 | r_sym: 0x00070605, 160 | r_type: 0x00000004, 161 | }, 162 | ); 163 | } 164 | 165 | #[test] 166 | fn parse_rel32_msb() { 167 | test_parse_for( 168 | BigEndian, 169 | Class::ELF32, 170 | Rel { 171 | r_offset: 0x00010203, 172 | r_sym: 0x00040506, 173 | r_type: 0x00000007, 174 | }, 175 | ); 176 | } 177 | 178 | #[test] 179 | fn parse_rel64_lsb() { 180 | test_parse_for( 181 | LittleEndian, 182 | Class::ELF64, 183 | Rel { 184 | r_offset: 0x0706050403020100, 185 | r_sym: 0x0F0E0D0C, 186 | r_type: 0x0B0A0908, 187 | }, 188 | ); 189 | } 190 | 191 | #[test] 192 | fn parse_rel64_msb() { 193 | test_parse_for( 194 | BigEndian, 195 | Class::ELF64, 196 | Rel { 197 | r_offset: 0x0001020304050607, 198 | r_sym: 0x08090A0B, 199 | r_type: 0x0C0D0E0F, 200 | }, 201 | ); 202 | } 203 | 204 | #[test] 205 | fn parse_rel32_lsb_fuzz_too_short() { 206 | test_parse_fuzz_too_short::<_, Rel>(LittleEndian, Class::ELF32); 207 | } 208 | 209 | #[test] 210 | fn parse_rel32_msb_fuzz_too_short() { 211 | test_parse_fuzz_too_short::<_, Rel>(BigEndian, Class::ELF32); 212 | } 213 | 214 | #[test] 215 | fn parse_rel64_lsb_fuzz_too_short() { 216 | test_parse_fuzz_too_short::<_, Rel>(LittleEndian, Class::ELF64); 217 | } 218 | 219 | #[test] 220 | fn parse_rel64_msb_fuzz_too_short() { 221 | test_parse_fuzz_too_short::<_, Rel>(BigEndian, Class::ELF64); 222 | } 223 | 224 | #[test] 225 | fn parse_rela32_lsb() { 226 | test_parse_for( 227 | LittleEndian, 228 | Class::ELF32, 229 | Rela { 230 | r_offset: 0x03020100, 231 | r_sym: 0x00070605, 232 | r_type: 0x00000004, 233 | r_addend: 0x0B0A0908, 234 | }, 235 | ); 236 | } 237 | 238 | #[test] 239 | fn parse_rela32_msb() { 240 | test_parse_for( 241 | BigEndian, 242 | Class::ELF32, 243 | Rela { 244 | r_offset: 0x00010203, 245 | r_sym: 0x00040506, 246 | r_type: 0x00000007, 247 | r_addend: 0x08090A0B, 248 | }, 249 | ); 250 | } 251 | 252 | #[test] 253 | fn parse_rela64_lsb() { 254 | test_parse_for( 255 | LittleEndian, 256 | Class::ELF64, 257 | Rela { 258 | r_offset: 0x0706050403020100, 259 | r_sym: 0x0F0E0D0C, 260 | r_type: 0x0B0A0908, 261 | r_addend: 0x1716151413121110, 262 | }, 263 | ); 264 | } 265 | 266 | #[test] 267 | fn parse_rela64_msb() { 268 | test_parse_for( 269 | BigEndian, 270 | Class::ELF64, 271 | Rela { 272 | r_offset: 0x0001020304050607, 273 | r_sym: 0x08090A0B, 274 | r_type: 0x0C0D0E0F, 275 | r_addend: 0x1011121314151617, 276 | }, 277 | ); 278 | } 279 | 280 | #[test] 281 | fn parse_rela32_lsb_fuzz_too_short() { 282 | test_parse_fuzz_too_short::<_, Rela>(LittleEndian, Class::ELF32); 283 | } 284 | 285 | #[test] 286 | fn parse_rela32_msb_fuzz_too_short() { 287 | test_parse_fuzz_too_short::<_, Rela>(BigEndian, Class::ELF32); 288 | } 289 | 290 | #[test] 291 | fn parse_rela64_lsb_fuzz_too_short() { 292 | test_parse_fuzz_too_short::<_, Rela>(LittleEndian, Class::ELF64); 293 | } 294 | 295 | #[test] 296 | fn parse_rela64_msb_fuzz_too_short() { 297 | test_parse_fuzz_too_short::<_, Rela>(BigEndian, Class::ELF64); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | 6 | ## [0.8.0] - 2025-05-13 7 | 8 | ### Breaking Improvements 9 | 10 | - Drop "nightly" feature in favor of stabilized core::error::Error for ParseError when building for no_std. This bumps the MSRV to 1.81.0 (Sept. 5, 2024). 11 | - Change dyn.d_val() and dyn.d_ptr() to take self by ref 12 | - Relax NoteAny parsing to allow notes with names that contain invalid utf8 sequences 13 | 14 | ### New Features 15 | 16 | - Add ElfStream::segment_data() to get raw segment data 17 | - Add ABI constants for MIPS 18 | - Drop "nightly" feature in favor of stabilized core::error::Error for ParseError when building for no_std 19 | - Add EM_CSKY, EM_LOONGARCH EM_FRV, EM_LANAI, and EM_VE 20 | - Make to_str no_std compatible 21 | - Make Symbol fields st_info and st_other fully public 22 | - Add NoteAny::name_str() helper to parse the note name as valid UTF8 23 | 24 | ### Bug Fixes 25 | 26 | - Update e_machine_to_str to return proper value for EM_RISCV 27 | - Various docstring fixes 28 | 29 | ## [0.7.4] - 2023-11-22 30 | 31 | ### Bug Fixes 32 | 33 | - Fix note parsing for notes with n_namesz == (align * x + 1) 34 | 35 | ## [0.7.3] - 2023-10-09 36 | 37 | ### New Features 38 | 39 | - Derive Debug on LittleEndian and BigEndian 40 | 41 | ### Misc Improvements 42 | 43 | - Enable #![forbid(unsafe_code)] 44 | - Enable #![deny(missing_debug_implementations)] 45 | - Enable #![warn(rust_2018_idioms)] 46 | - Fix doc comment on file::Class 47 | - Fix README example so it compiles 48 | 49 | ## [0.7.2] - 2023-02-15 50 | 51 | ### New Features 52 | 53 | - Implement core::error::Error for ParsingError accessible via a new non-default "nightly" cargo feature 54 | - Add abi constants for note descriptor types (n_type) 55 | - 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. 56 | 57 | ### Bug Fixes 58 | 59 | - Fix an 'attempt to shift right with overflow' panic in the GnuHashTable if nshift is wider than the bloom filter word size 60 | 61 | ### Misc Improvements 62 | 63 | - Add doc comments for EM_* abi constants 64 | - Tweak formatting and update language for various doc comments 65 | 66 | ## [0.7.1] - 2023-01-08 67 | 68 | ### Bug Fixes 69 | 70 | - Fix a divide by zero panic in GnuHashTable.find() for tables with nbloom = 0 71 | 72 | ## [0.7.0] - 2022-11-14 73 | 74 | ### New Features 75 | 76 | - Add new ElfBytes type with better ergonomics for parsing from a &[u8] 77 | - Add GnuHashTable which interprets the contents of a SHT_GNU_HASH section 78 | - Add convenience method section_header_by_name to ElfBytes and ElfStream 79 | - Add GnuBuildIdNote and parsing for NT_GNU_BUILD_ID note contents 80 | - Add GnuAbiTagNote and parsing for NT_GNU_ABI_TAG note contents 81 | - Add ElfBytes::symbol_version_table() to get the GNU extension symbol version table. 82 | - Add ElfBytes::find_common_data() to efficiently discover common ELF structures 83 | - Add a new endian-aware integer parsing trait impl 84 | - Add ParsingTable.is_empty() 85 | - Add abi constants for powerpc and powerpc64 86 | - Add abi constants for RISC-V 87 | - Add abi constants for x86_64 88 | - Add abi constants for ARM32 and ARM64 (AARCH64) 89 | - Add abi constant for GNU-extension ELF note name ELF_NOTE_GNU 90 | - Add abi constant for PT_GNU_PROPERTY 91 | - Add abi constants for SHN_ABS and SHN_COMMON 92 | - Add elf::to_str::d_tag_to_str() 93 | - Add elf::to_str::note_abi_tag_os_to_str() 94 | 95 | ### Changed Interfaces 96 | 97 | - Rename elf::File -> elf::ElfStream and make it specific to the Read + Seek interface 98 | - Rename gabi -> abi since it also includes extension constants 99 | - Make ELF structures generic across the new endian-aware integer parsing trait EndianParse 100 | - Refactor parsed Note type to be a typed enum 101 | - Rename ElfStream::dynamic_section() -> dynamic() to match ElfBytes 102 | - Change ElfStream::dynamic() to yield a DynamicTable just like in ElfBytes 103 | - Standardize ElfBytes' interfaces for the .dynamic contents to return a DynamicTable 104 | - Export the parsing utilities ParsingTable, ParsingIterator in the public interface 105 | - Refactor section_headers_with_strtab to work with files that have shdrs but no shstrtab 106 | - Remove redundant hash arg from SysVHashTable.find() 107 | - Expose Class in the public interface alongside FileHeader 108 | - Remove opinionated Display impl for file::Class 109 | - Remove section_data_as_symbol_table() from public ElfBytes interface 110 | - Change SymbolVersionTable::new() to take Options instead of Default-empty iterators 111 | - Change ElfStream to parse out the ProgramHeaders into an allocated vec as part of ElfStream::open_stream() 112 | - Change ElfStream to parse out the SectionHeaders into an allocated Vec as part of ElfStream::open_stream() 113 | 114 | ### Bug Fixes 115 | 116 | - Properly parse program header table when ehdr.e_phnum > 0xffff 117 | - Fix OOM in ElfStream parsing when parsing corrupted files 118 | - Fix a divide by zero panic in SysVHashTable.find() for empty tables 119 | 120 | ### Misc Improvements 121 | 122 | - Add more fuzz testing 123 | - Add some simple parsing smoke tests for the various sample architecture objects 124 | - Add sample object and testing with > 0xff00 section headers 125 | - Add a lot more doc comments to each of the modules 126 | 127 | ## [0.6.1] - 2022-11-05 128 | 129 | ### New Features 130 | - 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. 131 | 132 | ## [0.6.0] - 2022-11-01 133 | 134 | ### New Features 135 | 136 | - Add fuzz targets for parts of our ELF parsing interface via cargo-fuzz 137 | - Add SysVHashTable which interprets the contents of a SHT_HASH section 138 | - Add StringTable::get_raw() to get an uninterpreted &[u8] 139 | - Add ParsingTable.len() method to get the number of elements in the table 140 | - Add some note n_type constants for GNU extension notes. 141 | - Add default "to_str" feature to get &str for gabi constant names 142 | 143 | ### Changed Interfaces 144 | 145 | - Change File::segments() to return a ParsingTable instead of just a ParsingIterator 146 | - Change File's SectionHeader interfaces to provide a ParsingTable instead of just a ParsingIterator 147 | - Remove deprecated File::section_data_for_header() in favor of File::section_data() 148 | - Remove FileHeader wrapper types OSABI, Architecture, and ObjectFileType 149 | - Remove ProgramHeader wrapper types ProgType and ProgFlag 150 | - Remove Symbol wrapper types SymbolType SymbolBind SymbolVis 151 | - Remove wrapper type SectionType 152 | - Remove unhelpful SectionFlag wrapper type 153 | - Remove Display impl for FileHeader, SectionHeader, ProgramHeader, Symbol 154 | - Remove ParseError::UnsupportedElfVersion in favor of more general ParseError::UnsupportedVersion 155 | 156 | ### Bug Fixes 157 | 158 | - Fix divide by zero panic when parsing a note with alignment of 0 (Error instead of panic) 159 | - Use checked integer math all over the parsing code (Error instead of panic or overflow) 160 | - Fix note parsing for 8-byte aligned .note.gnu.property sections (Successfully parse instead of Erroring) 161 | - Add size validation when parsing tables with entsizes (Error instead of panic) 162 | 163 | ## [0.5.0] - 2022-10-30 164 | 165 | ### New Features 166 | 167 | - Add File::symbol_version_table() interface to get the GNU extension symbol versioning table 168 | - Add Symbol.is_undefined() helper to check if a symbol is defined in this object 169 | - Add File::section_data() which opportunistically parses the CompressionHeader if present 170 | 171 | ### Bug Fixes 172 | 173 | - Fix StringTable to return a ParseError on index out of bounds instead of panicking 174 | - Fix File::section_data_as_rels to properly parse Rels (not Relas) 175 | 176 | ## [0.4.0] - 2022-10-24 177 | 178 | ### New Features 179 | 180 | - Add .note section and segment parsing 181 | - Add .dynamic section and segment parsing 182 | - Add .rel and .rela section parsing 183 | - Add File::section_headers_with_strtab to get both a header iter and strtab concurrently. 184 | 185 | ### Changed Interfaces 186 | 187 | - 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. 188 | 189 | ## [0.3.1] - 2022-10-21 190 | 191 | ### New Features 192 | - Add File::section_data_for_header() to get raw section data for a given section 193 | 194 | ### Bug fixes 195 | - Fix section header table parsing when ehdr.e_shnum > 0xff00 196 | 197 | ## [0.3.0] - 2022-10-20 198 | 199 | ### New Features 200 | - Add a `no_std` option by fully moving the parser over to lazy zero-alloc parsing patterns. 201 | 202 | 203 | [0.8.0]: https://github.com/cole14/rust-elf/compare/v0.7.4...v0.8.0 204 | [0.7.4]: https://github.com/cole14/rust-elf/compare/v0.7.3...v0.7.4 205 | [0.7.3]: https://github.com/cole14/rust-elf/compare/v0.7.2...v0.7.3 206 | [0.7.2]: https://github.com/cole14/rust-elf/compare/v0.7.1...v0.7.2 207 | [0.7.1]: https://github.com/cole14/rust-elf/compare/v0.7.0...v0.7.1 208 | [0.7.0]: https://github.com/cole14/rust-elf/compare/v0.6.1...v0.7.0 209 | [0.6.1]: https://github.com/cole14/rust-elf/compare/v0.6.0...v0.6.1 210 | [0.6.0]: https://github.com/cole14/rust-elf/compare/v0.5.0...v0.6.0 211 | [0.5.0]: https://github.com/cole14/rust-elf/compare/v0.4.0...v0.5.0 212 | [0.4.0]: https://github.com/cole14/rust-elf/compare/v0.3.1...v0.4.0 213 | [0.3.1]: https://github.com/cole14/rust-elf/compare/v0.3.0...v0.3.1 214 | [0.3.0]: https://github.com/cole14/rust-elf/compare/v0.2.0...v0.3.0 215 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Christopher Cole 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/endian.rs: -------------------------------------------------------------------------------- 1 | //! An all-safe-code endian-aware integer parsing implementation via the 2 | //! [EndianParse] trait. 3 | //! 4 | //! This module provides four endian parsing implementations optimized to support the different 5 | //! common use-cases for an ELF parsing library. Each trait impl represents a 6 | //! specification that encapsulates an interface for parsing integers from some 7 | //! set of allowed byte orderings. 8 | //! 9 | //! * [AnyEndian]: Dynamically parsing either byte order at runtime based on the type of ELF object being parsed. 10 | //! * [BigEndian]/[LittleEndian]: For tools that know they only want to parse a single given byte order known at compile time. 11 | //! * [type@NativeEndian]: For tools that know they want to parse the same byte order as the target's byte order. 12 | // 13 | // Note: 14 | // I'd love to see this get replaced with safe transmutes, if that RFC ever gets formalized. 15 | // Until then, this crate serves as an example implementation for what's possible with purely safe rust. 16 | use crate::abi; 17 | use crate::parse::ParseError; 18 | 19 | /// This macro writes out safe code to get a subslice from the the byte slice $data 20 | /// at the given $off as a [u8; size_of<$typ>], then calls the corresponding safe 21 | /// endian-aware conversion on it. 22 | /// 23 | /// This uses safe integer math and returns a ParseError on overflow or if $data did 24 | /// not contain enough bytes at $off to perform the conversion. 25 | macro_rules! safe_from { 26 | ( $self:ident, $typ:ty, $off:ident, $data:ident) => {{ 27 | const SIZE: usize = core::mem::size_of::<$typ>(); 28 | 29 | let end = (*$off) 30 | .checked_add(SIZE) 31 | .ok_or(ParseError::IntegerOverflow)?; 32 | 33 | let buf: [u8; SIZE] = $data 34 | .get(*$off..end) 35 | .ok_or(ParseError::SliceReadError((*$off, end)))? 36 | .try_into()?; 37 | 38 | *$off = end; 39 | 40 | // Note: This check evaluates to a constant true/false for the "fixed" types 41 | // so the compiler should optimize out the check (LittleEndian, BigEndian, NativeEndian) 42 | if $self.is_little() { 43 | Ok(<$typ>::from_le_bytes(buf)) 44 | } else { 45 | Ok(<$typ>::from_be_bytes(buf)) 46 | } 47 | }}; 48 | } 49 | 50 | /// An all-safe-code endian-aware integer parsing trait. 51 | /// 52 | /// These methods use safe code to get a subslice from the the byte slice $data 53 | /// at the given $off as a [u8; size_of<$typ>], then calls the corresponding safe 54 | /// endian-aware conversion on it. 55 | /// 56 | /// These use checked integer math and returns a ParseError on overflow or if $data did 57 | /// not contain enough bytes at $off to perform the conversion. 58 | pub trait EndianParse: Clone + Copy + Default + PartialEq + Eq { 59 | fn parse_u8_at(self, offset: &mut usize, data: &[u8]) -> Result { 60 | safe_from!(self, u8, offset, data) 61 | } 62 | 63 | fn parse_u16_at(self, offset: &mut usize, data: &[u8]) -> Result { 64 | safe_from!(self, u16, offset, data) 65 | } 66 | 67 | fn parse_u32_at(self, offset: &mut usize, data: &[u8]) -> Result { 68 | safe_from!(self, u32, offset, data) 69 | } 70 | 71 | fn parse_u64_at(self, offset: &mut usize, data: &[u8]) -> Result { 72 | safe_from!(self, u64, offset, data) 73 | } 74 | 75 | fn parse_i32_at(self, offset: &mut usize, data: &[u8]) -> Result { 76 | safe_from!(self, i32, offset, data) 77 | } 78 | 79 | fn parse_i64_at(self, offset: &mut usize, data: &[u8]) -> Result { 80 | safe_from!(self, i64, offset, data) 81 | } 82 | 83 | /// Get an endian-aware integer parsing spec for an ELF [FileHeader](crate::file::FileHeader)'s 84 | /// `ident[EI_DATA]` byte. 85 | /// 86 | /// Returns an [UnsupportedElfEndianness](ParseError::UnsupportedElfEndianness) if this spec 87 | /// doesn't support parsing the byte-order represented by ei_data. If you're 88 | /// seeing this error, are you trying to read files of any endianness? i.e. 89 | /// did you want to use AnyEndian? 90 | fn from_ei_data(ei_data: u8) -> Result; 91 | 92 | fn is_little(self) -> bool; 93 | 94 | #[inline(always)] 95 | fn is_big(self) -> bool { 96 | !self.is_little() 97 | } 98 | } 99 | 100 | /// An endian parsing type that can choose at runtime which byte order to parse integers as. 101 | /// This is useful for scenarios where a single compiled binary wants to dynamically 102 | /// interpret ELF files of any byte order. 103 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] 104 | pub enum AnyEndian { 105 | /// Used for a little-endian ELF structures that have been parsed with AnyEndian 106 | #[default] 107 | Little, 108 | /// Used for a big-endian ELF structures that have been parsed with AnyEndian 109 | Big, 110 | } 111 | 112 | /// A zero-sized type that always parses integers as if they're in little-endian order. 113 | /// This is useful for scenarios where a combiled binary knows it only wants to interpret 114 | /// little-endian ELF files and doesn't want the performance penalty of evaluating a match 115 | /// each time it parses an integer. 116 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] 117 | pub struct LittleEndian; 118 | 119 | /// A zero-sized type that always parses integers as if they're in big-endian order. 120 | /// This is useful for scenarios where a combiled binary knows it only wants to interpret 121 | /// big-endian ELF files and doesn't want the performance penalty of evaluating a match 122 | /// each time it parses an integer. 123 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] 124 | pub struct BigEndian; 125 | 126 | /// A zero-sized type that always parses integers as if they're in the compilation target's native-endian order. 127 | /// This is useful for toolchain scenarios where a combiled binary knows it only wants to interpret 128 | /// ELF files compiled for the same target and doesn't want the performance penalty of evaluating a match 129 | /// each time it parses an integer. 130 | #[cfg(target_endian = "little")] 131 | pub type NativeEndian = LittleEndian; 132 | 133 | #[cfg(target_endian = "little")] 134 | #[allow(non_upper_case_globals)] 135 | #[doc(hidden)] 136 | pub const NativeEndian: LittleEndian = LittleEndian; 137 | 138 | /// A zero-sized type that always parses integers as if they're in the compilation target's native-endian order. 139 | /// This is useful for toolchain scenarios where a combiled binary knows it only wants to interpret 140 | /// ELF files compiled for the same target and doesn't want the performance penalty of evaluating a match 141 | /// each time it parses an integer. 142 | #[cfg(target_endian = "big")] 143 | pub type NativeEndian = BigEndian; 144 | 145 | #[cfg(target_endian = "big")] 146 | #[allow(non_upper_case_globals)] 147 | #[doc(hidden)] 148 | pub const NativeEndian: BigEndian = BigEndian; 149 | 150 | impl EndianParse for LittleEndian { 151 | fn from_ei_data(ei_data: u8) -> Result { 152 | match ei_data { 153 | abi::ELFDATA2LSB => Ok(LittleEndian), 154 | _ => Err(ParseError::UnsupportedElfEndianness(ei_data)), 155 | } 156 | } 157 | 158 | #[inline(always)] 159 | fn is_little(self) -> bool { 160 | true 161 | } 162 | } 163 | 164 | impl EndianParse for BigEndian { 165 | fn from_ei_data(ei_data: u8) -> Result { 166 | match ei_data { 167 | abi::ELFDATA2MSB => Ok(BigEndian), 168 | _ => Err(ParseError::UnsupportedElfEndianness(ei_data)), 169 | } 170 | } 171 | 172 | #[inline(always)] 173 | fn is_little(self) -> bool { 174 | false 175 | } 176 | } 177 | 178 | impl EndianParse for AnyEndian { 179 | fn from_ei_data(ei_data: u8) -> Result { 180 | match ei_data { 181 | abi::ELFDATA2LSB => Ok(AnyEndian::Little), 182 | abi::ELFDATA2MSB => Ok(AnyEndian::Big), 183 | _ => Err(ParseError::UnsupportedElfEndianness(ei_data)), 184 | } 185 | } 186 | 187 | #[inline(always)] 188 | fn is_little(self) -> bool { 189 | match self { 190 | AnyEndian::Little => true, 191 | AnyEndian::Big => false, 192 | } 193 | } 194 | } 195 | 196 | #[cfg(test)] 197 | mod tests { 198 | use super::*; 199 | 200 | macro_rules! parse_test { 201 | ( $endian:expr, $res_typ:ty, $method:ident, $expect:expr) => {{ 202 | let bytes = [ 203 | 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, 0x08u8, 204 | ]; 205 | let mut offset = 0; 206 | let result = $endian.$method(&mut offset, &bytes).unwrap(); 207 | assert_eq!(result, $expect); 208 | assert_eq!(offset, core::mem::size_of::<$res_typ>()); 209 | }}; 210 | } 211 | 212 | macro_rules! fuzz_too_short_test { 213 | ( $endian:expr, $res_typ:ty, $method:ident) => {{ 214 | let bytes = [ 215 | 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, 0x08u8, 216 | ]; 217 | let size = core::mem::size_of::<$res_typ>(); 218 | for n in 0..size { 219 | let buf = bytes.split_at(n).0.as_ref(); 220 | let mut offset: usize = 0; 221 | let error = $endian 222 | .$method(&mut offset, buf) 223 | .expect_err("Expected an error, but parsed: "); 224 | assert!( 225 | matches!(error, ParseError::SliceReadError(_)), 226 | "Unexpected Error type found: {error}" 227 | ); 228 | } 229 | }}; 230 | } 231 | 232 | #[test] 233 | fn parse_u8_at() { 234 | parse_test!(LittleEndian, u8, parse_u8_at, 0x01u8); 235 | parse_test!(BigEndian, u8, parse_u8_at, 0x01u8); 236 | parse_test!(AnyEndian::Little, u8, parse_u8_at, 0x01u8); 237 | parse_test!(AnyEndian::Big, u8, parse_u8_at, 0x01u8); 238 | } 239 | 240 | #[test] 241 | fn parse_u16_at() { 242 | parse_test!(LittleEndian, u16, parse_u16_at, 0x0201u16); 243 | parse_test!(BigEndian, u16, parse_u16_at, 0x0102u16); 244 | parse_test!(AnyEndian::Little, u16, parse_u16_at, 0x0201u16); 245 | parse_test!(AnyEndian::Big, u16, parse_u16_at, 0x0102u16); 246 | } 247 | 248 | #[test] 249 | fn parse_u32_at() { 250 | parse_test!(LittleEndian, u32, parse_u32_at, 0x04030201u32); 251 | parse_test!(BigEndian, u32, parse_u32_at, 0x01020304u32); 252 | parse_test!(AnyEndian::Little, u32, parse_u32_at, 0x04030201u32); 253 | parse_test!(AnyEndian::Big, u32, parse_u32_at, 0x01020304u32); 254 | } 255 | 256 | #[test] 257 | fn parse_u64_at() { 258 | parse_test!(LittleEndian, u64, parse_u64_at, 0x0807060504030201u64); 259 | parse_test!(BigEndian, u64, parse_u64_at, 0x0102030405060708u64); 260 | parse_test!(AnyEndian::Little, u64, parse_u64_at, 0x0807060504030201u64); 261 | parse_test!(AnyEndian::Big, u64, parse_u64_at, 0x0102030405060708u64); 262 | } 263 | 264 | #[test] 265 | fn parse_i32_at() { 266 | parse_test!(LittleEndian, i32, parse_i32_at, 0x04030201i32); 267 | parse_test!(BigEndian, i32, parse_i32_at, 0x01020304i32); 268 | parse_test!(AnyEndian::Little, i32, parse_i32_at, 0x04030201i32); 269 | parse_test!(AnyEndian::Big, i32, parse_i32_at, 0x01020304i32); 270 | } 271 | 272 | #[test] 273 | fn parse_i64_at() { 274 | parse_test!(LittleEndian, i64, parse_i64_at, 0x0807060504030201i64); 275 | parse_test!(BigEndian, i64, parse_i64_at, 0x0102030405060708i64); 276 | parse_test!(AnyEndian::Little, i64, parse_i64_at, 0x0807060504030201i64); 277 | parse_test!(AnyEndian::Big, i64, parse_i64_at, 0x0102030405060708i64); 278 | } 279 | 280 | #[test] 281 | fn fuzz_u8_too_short() { 282 | fuzz_too_short_test!(LittleEndian, u8, parse_u8_at); 283 | fuzz_too_short_test!(BigEndian, u8, parse_u8_at); 284 | fuzz_too_short_test!(AnyEndian::Little, u8, parse_u8_at); 285 | fuzz_too_short_test!(AnyEndian::Big, u8, parse_u8_at); 286 | } 287 | 288 | #[test] 289 | fn fuzz_u16_too_short() { 290 | fuzz_too_short_test!(LittleEndian, u16, parse_u16_at); 291 | fuzz_too_short_test!(BigEndian, u16, parse_u16_at); 292 | fuzz_too_short_test!(AnyEndian::Little, u16, parse_u16_at); 293 | fuzz_too_short_test!(AnyEndian::Big, u16, parse_u16_at); 294 | } 295 | 296 | #[test] 297 | fn fuzz_u32_too_short() { 298 | fuzz_too_short_test!(LittleEndian, u32, parse_u32_at); 299 | fuzz_too_short_test!(BigEndian, u32, parse_u32_at); 300 | fuzz_too_short_test!(AnyEndian::Little, u32, parse_u32_at); 301 | fuzz_too_short_test!(AnyEndian::Big, u32, parse_u32_at); 302 | } 303 | 304 | #[test] 305 | fn fuzz_i32_too_short() { 306 | fuzz_too_short_test!(LittleEndian, i32, parse_i32_at); 307 | fuzz_too_short_test!(BigEndian, i32, parse_i32_at); 308 | fuzz_too_short_test!(AnyEndian::Little, i32, parse_i32_at); 309 | fuzz_too_short_test!(AnyEndian::Big, i32, parse_i32_at); 310 | } 311 | 312 | #[test] 313 | fn fuzz_u64_too_short() { 314 | fuzz_too_short_test!(LittleEndian, u64, parse_u64_at); 315 | fuzz_too_short_test!(BigEndian, u64, parse_u64_at); 316 | fuzz_too_short_test!(AnyEndian::Little, u64, parse_u64_at); 317 | fuzz_too_short_test!(AnyEndian::Big, u64, parse_u64_at); 318 | } 319 | 320 | #[test] 321 | fn fuzz_i64_too_short() { 322 | fuzz_too_short_test!(LittleEndian, i64, parse_i64_at); 323 | fuzz_too_short_test!(BigEndian, i64, parse_i64_at); 324 | fuzz_too_short_test!(AnyEndian::Little, i64, parse_i64_at); 325 | fuzz_too_short_test!(AnyEndian::Big, i64, parse_i64_at); 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/file.rs: -------------------------------------------------------------------------------- 1 | //! Parsing the ELF File Header 2 | use crate::abi; 3 | use crate::endian::EndianParse; 4 | use crate::parse::ParseError; 5 | 6 | /// Represents the ELF file word size (32-bit vs 64-bit) 7 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 8 | pub enum Class { 9 | ELF32, 10 | ELF64, 11 | } 12 | 13 | /// C-style 32-bit ELF File Header definition 14 | /// 15 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 16 | #[derive(Debug)] 17 | #[repr(C)] 18 | pub struct Elf32_Ehdr { 19 | pub e_ident: [u8; abi::EI_NIDENT], 20 | pub e_type: u16, 21 | pub e_machine: u16, 22 | pub e_version: u32, 23 | pub e_entry: u32, 24 | pub e_phoff: u32, 25 | pub e_shoff: u32, 26 | pub e_flags: u32, 27 | pub e_ehsize: u16, 28 | pub e_phentsize: u16, 29 | pub e_phnum: u16, 30 | pub e_shentsize: u16, 31 | pub e_shnum: u16, 32 | pub e_shstrndx: u16, 33 | } 34 | 35 | /// C-style 64-bit ELF File Header definition 36 | /// 37 | /// These C-style definitions are for users who want to implement their own ELF manipulation logic. 38 | #[derive(Debug)] 39 | #[repr(C)] 40 | pub struct Elf64_Ehdr { 41 | pub e_ident: [u8; abi::EI_NIDENT], 42 | pub e_type: u16, 43 | pub e_machine: u16, 44 | pub e_version: u32, 45 | pub e_entry: u64, 46 | pub e_phoff: u64, 47 | pub e_shoff: u64, 48 | pub e_flags: u32, 49 | pub e_ehsize: u16, 50 | pub e_phentsize: u16, 51 | pub e_phnum: u16, 52 | pub e_shentsize: u16, 53 | pub e_shnum: u16, 54 | pub e_shstrndx: u16, 55 | } 56 | 57 | /// Encapsulates the contents of the ELF File Header 58 | /// 59 | /// The ELF File Header starts off every ELF file and both identifies the 60 | /// file contents and informs how to interpret said contents. This includes 61 | /// the width of certain fields (32-bit vs 64-bit), the data endianness, the 62 | /// file type, and more. 63 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 64 | pub struct FileHeader { 65 | /// 32-bit vs 64-bit 66 | pub class: Class, 67 | // file byte order 68 | pub endianness: E, 69 | /// elf version 70 | pub version: u32, 71 | /// OS ABI 72 | pub osabi: u8, 73 | /// Version of the OS ABI 74 | pub abiversion: u8, 75 | /// ELF file type 76 | pub e_type: u16, 77 | /// Target machine architecture 78 | pub e_machine: u16, 79 | /// Virtual address of program entry point 80 | /// This member gives the virtual address to which the system first transfers control, 81 | /// thus starting the process. If the file has no associated entry point, this member holds zero. 82 | /// 83 | /// Note: Type is Elf32_Addr or Elf64_Addr which are either 4 or 8 bytes. We aren't trying to zero-copy 84 | /// parse the FileHeader since there's only one per file and its only ~45 bytes anyway, so we use 85 | /// u64 for the three Elf*_Addr and Elf*_Off fields here. 86 | pub e_entry: u64, 87 | /// This member holds the program header table's file offset in bytes. If the file has no program header 88 | /// table, this member holds zero. 89 | pub e_phoff: u64, 90 | /// This member holds the section header table's file offset in bytes. If the file has no section header 91 | /// table, this member holds zero. 92 | pub e_shoff: u64, 93 | /// This member holds processor-specific flags associated with the file. Flag names take the form EF_machine_flag. 94 | pub e_flags: u32, 95 | /// This member holds the ELF header's size in bytes. 96 | pub e_ehsize: u16, 97 | /// This member holds the size in bytes of one entry in the file's program header table; all entries are the same size. 98 | pub e_phentsize: u16, 99 | /// This member holds the number of entries in the program header table. Thus the product of e_phentsize and e_phnum 100 | /// gives the table's size in bytes. If a file has no program header table, e_phnum holds the value zero. 101 | pub e_phnum: u16, 102 | /// This member holds a section header's size in bytes. A section header is one entry in the section header table; 103 | /// all entries are the same size. 104 | pub e_shentsize: u16, 105 | /// This member holds the number of entries in the section header table. Thus the product of e_shentsize and e_shnum 106 | /// gives the section header table's size in bytes. If a file has no section header table, e_shnum holds the value zero. 107 | /// 108 | /// If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), this member has the value zero and 109 | /// the actual number of section header table entries is contained in the sh_size field of the section header at index 0. 110 | /// (Otherwise, the sh_size member of the initial entry contains 0.) 111 | pub e_shnum: u16, 112 | /// This member holds the section header table index of the entry associated with the section name string table. If the 113 | /// file has no section name string table, this member holds the value SHN_UNDEF. 114 | /// 115 | /// If the section name string table section index is greater than or equal to SHN_LORESERVE (0xff00), this member has 116 | /// the value SHN_XINDEX (0xffff) and the actual index of the section name string table section is contained in the 117 | /// sh_link field of the section header at index 0. (Otherwise, the sh_link member of the initial entry contains 0.) 118 | pub e_shstrndx: u16, 119 | } 120 | 121 | pub const ELF32_EHDR_TAILSIZE: usize = 36; 122 | pub const ELF64_EHDR_TAILSIZE: usize = 48; 123 | 124 | fn verify_ident(buf: &[u8]) -> Result<(), ParseError> { 125 | // Verify the magic number 126 | let magic = buf.split_at(abi::EI_CLASS).0; 127 | if magic != abi::ELFMAGIC { 128 | return Err(ParseError::BadMagic([ 129 | magic[0], magic[1], magic[2], magic[3], 130 | ])); 131 | } 132 | 133 | // Verify ELF Version 134 | let version = buf[abi::EI_VERSION]; 135 | if version != abi::EV_CURRENT { 136 | return Err(ParseError::UnsupportedVersion(( 137 | version as u64, 138 | abi::EV_CURRENT as u64, 139 | ))); 140 | } 141 | 142 | Ok(()) 143 | } 144 | 145 | pub fn parse_ident(data: &[u8]) -> Result<(E, Class, u8, u8), ParseError> { 146 | verify_ident(data)?; 147 | 148 | let e_class = data[abi::EI_CLASS]; 149 | let class = match e_class { 150 | abi::ELFCLASS32 => Class::ELF32, 151 | abi::ELFCLASS64 => Class::ELF64, 152 | _ => { 153 | return Err(ParseError::UnsupportedElfClass(e_class)); 154 | } 155 | }; 156 | 157 | // Verify endianness is something we know how to parse 158 | let file_endian = E::from_ei_data(data[abi::EI_DATA])?; 159 | 160 | Ok(( 161 | file_endian, 162 | class, 163 | data[abi::EI_OSABI], 164 | data[abi::EI_ABIVERSION], 165 | )) 166 | } 167 | 168 | impl FileHeader { 169 | pub fn parse_tail(ident: (E, Class, u8, u8), data: &[u8]) -> Result, ParseError> { 170 | let (file_endian, class, osabi, abiversion) = ident; 171 | 172 | let mut offset = 0; 173 | let e_type = file_endian.parse_u16_at(&mut offset, data)?; 174 | let e_machine = file_endian.parse_u16_at(&mut offset, data)?; 175 | let version = file_endian.parse_u32_at(&mut offset, data)?; 176 | 177 | let e_entry: u64; 178 | let e_phoff: u64; 179 | let e_shoff: u64; 180 | 181 | if class == Class::ELF32 { 182 | e_entry = file_endian.parse_u32_at(&mut offset, data)? as u64; 183 | e_phoff = file_endian.parse_u32_at(&mut offset, data)? as u64; 184 | e_shoff = file_endian.parse_u32_at(&mut offset, data)? as u64; 185 | } else { 186 | e_entry = file_endian.parse_u64_at(&mut offset, data)?; 187 | e_phoff = file_endian.parse_u64_at(&mut offset, data)?; 188 | e_shoff = file_endian.parse_u64_at(&mut offset, data)?; 189 | } 190 | 191 | let e_flags = file_endian.parse_u32_at(&mut offset, data)?; 192 | let e_ehsize = file_endian.parse_u16_at(&mut offset, data)?; 193 | let e_phentsize = file_endian.parse_u16_at(&mut offset, data)?; 194 | let e_phnum = file_endian.parse_u16_at(&mut offset, data)?; 195 | let e_shentsize = file_endian.parse_u16_at(&mut offset, data)?; 196 | let e_shnum = file_endian.parse_u16_at(&mut offset, data)?; 197 | let e_shstrndx = file_endian.parse_u16_at(&mut offset, data)?; 198 | 199 | Ok(FileHeader { 200 | class, 201 | endianness: file_endian, 202 | version, 203 | e_type, 204 | e_machine, 205 | osabi, 206 | abiversion, 207 | e_entry, 208 | e_phoff, 209 | e_shoff, 210 | e_flags, 211 | e_ehsize, 212 | e_phentsize, 213 | e_phnum, 214 | e_shentsize, 215 | e_shnum, 216 | e_shstrndx, 217 | }) 218 | } 219 | } 220 | 221 | #[cfg(test)] 222 | mod parse_tests { 223 | use super::*; 224 | use crate::endian::AnyEndian; 225 | 226 | #[test] 227 | fn test_verify_ident_valid() { 228 | let data: [u8; abi::EI_NIDENT] = [ 229 | abi::ELFMAG0, 230 | abi::ELFMAG1, 231 | abi::ELFMAG2, 232 | abi::ELFMAG3, 233 | abi::ELFCLASS32, 234 | abi::ELFDATA2LSB, 235 | abi::EV_CURRENT, 236 | abi::ELFOSABI_LINUX, 237 | 0, 238 | 0, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 0, 245 | ]; 246 | verify_ident(data.as_ref()).expect("Expected Ok result"); 247 | } 248 | 249 | #[test] 250 | fn test_verify_ident_invalid_mag0() { 251 | let data: [u8; abi::EI_NIDENT] = [ 252 | 0xFF, 253 | abi::ELFMAG1, 254 | abi::ELFMAG2, 255 | abi::ELFMAG3, 256 | abi::ELFCLASS32, 257 | abi::ELFDATA2LSB, 258 | abi::EV_CURRENT, 259 | abi::ELFOSABI_LINUX, 260 | 0, 261 | 0, 262 | 0, 263 | 0, 264 | 0, 265 | 0, 266 | 0, 267 | 0, 268 | ]; 269 | let result = verify_ident(data.as_ref()).expect_err("Expected an error"); 270 | assert!( 271 | matches!(result, ParseError::BadMagic(_)), 272 | "Unexpected Error type found: {result}" 273 | ); 274 | } 275 | 276 | #[test] 277 | fn test_verify_ident_invalid_mag1() { 278 | let data: [u8; abi::EI_NIDENT] = [ 279 | abi::ELFMAG0, 280 | 0xFF, 281 | abi::ELFMAG2, 282 | abi::ELFMAG3, 283 | abi::ELFCLASS32, 284 | abi::ELFDATA2LSB, 285 | abi::EV_CURRENT, 286 | abi::ELFOSABI_LINUX, 287 | 0, 288 | 0, 289 | 0, 290 | 0, 291 | 0, 292 | 0, 293 | 0, 294 | 0, 295 | ]; 296 | let result = verify_ident(data.as_ref()).expect_err("Expected an error"); 297 | assert!( 298 | matches!(result, ParseError::BadMagic(_)), 299 | "Unexpected Error type found: {result}" 300 | ); 301 | } 302 | 303 | #[test] 304 | fn test_verify_ident_invalid_mag2() { 305 | let data: [u8; abi::EI_NIDENT] = [ 306 | abi::ELFMAG0, 307 | abi::ELFMAG1, 308 | 0xFF, 309 | abi::ELFMAG3, 310 | abi::ELFCLASS32, 311 | abi::ELFDATA2LSB, 312 | abi::EV_CURRENT, 313 | abi::ELFOSABI_LINUX, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 0, 321 | 0, 322 | ]; 323 | let result = verify_ident(data.as_ref()).expect_err("Expected an error"); 324 | assert!( 325 | matches!(result, ParseError::BadMagic(_)), 326 | "Unexpected Error type found: {result}" 327 | ); 328 | } 329 | 330 | #[test] 331 | fn test_verify_ident_invalid_mag3() { 332 | let data: [u8; abi::EI_NIDENT] = [ 333 | abi::ELFMAG0, 334 | abi::ELFMAG1, 335 | abi::ELFMAG2, 336 | 0xFF, 337 | abi::ELFCLASS32, 338 | abi::ELFDATA2LSB, 339 | abi::EV_CURRENT, 340 | abi::ELFOSABI_LINUX, 341 | 0, 342 | 0, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 0, 349 | ]; 350 | let result = verify_ident(data.as_ref()).expect_err("Expected an error"); 351 | assert!( 352 | matches!(result, ParseError::BadMagic(_)), 353 | "Unexpected Error type found: {result}" 354 | ); 355 | } 356 | 357 | #[allow(deprecated)] 358 | #[test] 359 | fn test_verify_ident_invalid_version() { 360 | let data: [u8; abi::EI_NIDENT] = [ 361 | abi::ELFMAG0, 362 | abi::ELFMAG1, 363 | abi::ELFMAG2, 364 | abi::ELFMAG3, 365 | abi::ELFCLASS32, 366 | abi::ELFDATA2LSB, 367 | 42, 368 | abi::ELFOSABI_LINUX, 369 | 0, 370 | 0, 371 | 0, 372 | 0, 373 | 0, 374 | 0, 375 | 0, 376 | 0, 377 | ]; 378 | let result = verify_ident(data.as_ref()).expect_err("Expected an error"); 379 | assert!( 380 | matches!(result, ParseError::UnsupportedVersion((42, 1))), 381 | "Unexpected Error type found: {result}" 382 | ); 383 | } 384 | 385 | #[test] 386 | fn test_parse_ehdr32_works() { 387 | let ident = (AnyEndian::Little, Class::ELF32, abi::ELFOSABI_LINUX, 7u8); 388 | let mut tail = [0u8; ELF64_EHDR_TAILSIZE]; 389 | for (n, elem) in tail.iter_mut().enumerate().take(ELF64_EHDR_TAILSIZE) { 390 | *elem = n as u8; 391 | } 392 | 393 | assert_eq!( 394 | FileHeader::parse_tail(ident, &tail).unwrap(), 395 | FileHeader { 396 | class: Class::ELF32, 397 | endianness: AnyEndian::Little, 398 | version: 0x7060504, 399 | osabi: abi::ELFOSABI_LINUX, 400 | abiversion: 7, 401 | e_type: 0x100, 402 | e_machine: 0x302, 403 | e_entry: 0x0B0A0908, 404 | e_phoff: 0x0F0E0D0C, 405 | e_shoff: 0x13121110, 406 | e_flags: 0x17161514, 407 | e_ehsize: 0x1918, 408 | e_phentsize: 0x1B1A, 409 | e_phnum: 0x1D1C, 410 | e_shentsize: 0x1F1E, 411 | e_shnum: 0x2120, 412 | e_shstrndx: 0x2322, 413 | } 414 | ); 415 | } 416 | 417 | #[test] 418 | fn test_parse_ehdr32_fuzz_too_short() { 419 | let ident = (AnyEndian::Little, Class::ELF32, abi::ELFOSABI_LINUX, 7u8); 420 | let tail = [0u8; ELF32_EHDR_TAILSIZE]; 421 | 422 | for n in 0..ELF32_EHDR_TAILSIZE { 423 | let buf = tail.split_at(n).0; 424 | let result = FileHeader::parse_tail(ident, buf).expect_err("Expected an error"); 425 | assert!( 426 | matches!(result, ParseError::SliceReadError(_)), 427 | "Unexpected Error type found: {result:?}" 428 | ); 429 | } 430 | } 431 | 432 | #[test] 433 | fn test_parse_ehdr64_works() { 434 | let ident = (AnyEndian::Big, Class::ELF64, abi::ELFOSABI_LINUX, 7u8); 435 | let mut tail = [0u8; ELF64_EHDR_TAILSIZE]; 436 | for (n, elem) in tail.iter_mut().enumerate().take(ELF64_EHDR_TAILSIZE) { 437 | *elem = n as u8; 438 | } 439 | 440 | assert_eq!( 441 | FileHeader::parse_tail(ident, &tail).unwrap(), 442 | FileHeader { 443 | class: Class::ELF64, 444 | endianness: AnyEndian::Big, 445 | version: 0x04050607, 446 | osabi: abi::ELFOSABI_LINUX, 447 | abiversion: 7, 448 | e_type: 0x0001, 449 | e_machine: 0x0203, 450 | e_entry: 0x08090A0B0C0D0E0F, 451 | e_phoff: 0x1011121314151617, 452 | e_shoff: 0x18191A1B1C1D1E1F, 453 | e_flags: 0x20212223, 454 | e_ehsize: 0x2425, 455 | e_phentsize: 0x2627, 456 | e_phnum: 0x2829, 457 | e_shentsize: 0x2A2B, 458 | e_shnum: 0x2C2D, 459 | e_shstrndx: 0x2E2F, 460 | } 461 | ); 462 | } 463 | 464 | #[test] 465 | fn test_parse_ehdr64_fuzz_too_short() { 466 | let ident = (AnyEndian::Little, Class::ELF64, abi::ELFOSABI_LINUX, 7u8); 467 | let tail = [0u8; ELF64_EHDR_TAILSIZE]; 468 | 469 | for n in 0..ELF64_EHDR_TAILSIZE { 470 | let buf = tail.split_at(n).0; 471 | let result = FileHeader::parse_tail(ident, buf).expect_err("Expected an error"); 472 | assert!( 473 | matches!(result, ParseError::SliceReadError(_)), 474 | "Unexpected Error type found: {result:?}" 475 | ); 476 | } 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /src/hash.rs: -------------------------------------------------------------------------------- 1 | //! Parsing hash table sections for symbol tables: `.hash`, and `.gnu.hash` 2 | use core::mem::size_of; 3 | 4 | use crate::endian::EndianParse; 5 | use crate::file::Class; 6 | use crate::parse::{ParseAt, ParseError, ParsingTable, ReadBytesExt}; 7 | use crate::string_table::StringTable; 8 | use crate::symbol::{Symbol, SymbolTable}; 9 | 10 | impl ParseAt for u32 { 11 | fn parse_at( 12 | endian: E, 13 | _class: Class, 14 | offset: &mut usize, 15 | data: &[u8], 16 | ) -> Result { 17 | endian.parse_u32_at(offset, data) 18 | } 19 | 20 | #[inline] 21 | fn size_for(_class: Class) -> usize { 22 | core::mem::size_of::() 23 | } 24 | } 25 | 26 | type U32Table<'data, E> = ParsingTable<'data, E, u32>; 27 | 28 | /// Header at the start of SysV Hash Table sections of type [SHT_HASH](crate::abi::SHT_HASH). 29 | #[derive(Debug, Clone, PartialEq, Eq)] 30 | pub struct SysVHashHeader { 31 | pub nbucket: u32, 32 | pub nchain: u32, 33 | } 34 | 35 | impl ParseAt for SysVHashHeader { 36 | fn parse_at( 37 | endian: E, 38 | _class: Class, 39 | offset: &mut usize, 40 | data: &[u8], 41 | ) -> Result { 42 | Ok(SysVHashHeader { 43 | nbucket: endian.parse_u32_at(offset, data)?, 44 | nchain: endian.parse_u32_at(offset, data)?, 45 | }) 46 | } 47 | 48 | #[inline] 49 | fn size_for(_class: Class) -> usize { 50 | size_of::() + size_of::() 51 | } 52 | } 53 | 54 | /// Calculate the SysV hash value for a given symbol name. 55 | pub fn sysv_hash(name: &[u8]) -> u32 { 56 | let mut hash = 0u32; 57 | for byte in name { 58 | hash = hash.wrapping_mul(16).wrapping_add(*byte as u32); 59 | hash ^= (hash >> 24) & 0xf0; 60 | } 61 | hash & 0xfffffff 62 | } 63 | 64 | #[derive(Debug)] 65 | pub struct SysVHashTable<'data, E: EndianParse> { 66 | buckets: U32Table<'data, E>, 67 | chains: U32Table<'data, E>, 68 | } 69 | 70 | /// This constructs a lazy-parsing type that keeps a reference to the provided data 71 | /// bytes from which it lazily parses and interprets its contents. 72 | impl<'data, E: EndianParse> SysVHashTable<'data, E> { 73 | /// Construct a SysVHashTable from given bytes. Keeps a reference to the data for lazy parsing. 74 | pub fn new(endian: E, class: Class, data: &'data [u8]) -> Result { 75 | let mut offset = 0; 76 | let hdr = SysVHashHeader::parse_at(endian, class, &mut offset, data)?; 77 | 78 | let buckets_size = size_of::() 79 | .checked_mul(hdr.nbucket.try_into()?) 80 | .ok_or(ParseError::IntegerOverflow)?; 81 | let buckets_end = offset 82 | .checked_add(buckets_size) 83 | .ok_or(ParseError::IntegerOverflow)?; 84 | let buckets_buf = data.get_bytes(offset..buckets_end)?; 85 | let buckets = U32Table::new(endian, class, buckets_buf); 86 | offset = buckets_end; 87 | 88 | let chains_size = size_of::() 89 | .checked_mul(hdr.nchain.try_into()?) 90 | .ok_or(ParseError::IntegerOverflow)?; 91 | let chains_end = offset 92 | .checked_add(chains_size) 93 | .ok_or(ParseError::IntegerOverflow)?; 94 | let chains_buf = data.get_bytes(offset..chains_end)?; 95 | let chains = U32Table::new(endian, class, chains_buf); 96 | 97 | Ok(SysVHashTable { buckets, chains }) 98 | } 99 | 100 | /// Use the hash table to find the symbol table entry with the given name and hash. 101 | pub fn find( 102 | &self, 103 | name: &[u8], 104 | symtab: &SymbolTable<'data, E>, 105 | strtab: &StringTable<'data>, 106 | ) -> Result, ParseError> { 107 | // empty hash tables don't have any entries. This avoids a divde by zero in the modulus calculation 108 | if self.buckets.is_empty() { 109 | return Ok(None); 110 | } 111 | 112 | let hash = sysv_hash(name); 113 | 114 | let start = (hash as usize) % self.buckets.len(); 115 | let mut index = self.buckets.get(start)? as usize; 116 | 117 | // Bound the number of chain lookups by the chain size so we don't loop forever 118 | let mut i = 0; 119 | while index != 0 && i < self.chains.len() { 120 | let symbol = symtab.get(index)?; 121 | if strtab.get_raw(symbol.st_name as usize)? == name { 122 | return Ok(Some((index, symbol))); 123 | } 124 | 125 | index = self.chains.get(index)? as usize; 126 | i += 1; 127 | } 128 | Ok(None) 129 | } 130 | } 131 | 132 | /// Calculate the GNU hash for a given symbol name. 133 | pub fn gnu_hash(name: &[u8]) -> u32 { 134 | let mut hash = 5381u32; 135 | for byte in name { 136 | hash = hash.wrapping_mul(33).wrapping_add(u32::from(*byte)); 137 | } 138 | hash 139 | } 140 | 141 | /// Header at the start of a GNU extension Hash Table section of type [SHT_GNU_HASH](crate::abi::SHT_GNU_HASH). 142 | #[derive(Debug, Clone, PartialEq, Eq)] 143 | pub struct GnuHashHeader { 144 | pub nbucket: u32, 145 | /// The symbol table index of the first symbol in the hash table. 146 | /// (GNU hash sections omit symbols at the start of the table that wont be looked up) 147 | pub table_start_idx: u32, 148 | /// The number of words in the bloom filter. (must be a non-zero power of 2) 149 | pub nbloom: u32, 150 | /// The bit shift count for the bloom filter. 151 | pub nshift: u32, 152 | } 153 | 154 | impl ParseAt for GnuHashHeader { 155 | fn parse_at( 156 | endian: E, 157 | _class: Class, 158 | offset: &mut usize, 159 | data: &[u8], 160 | ) -> Result { 161 | Ok(GnuHashHeader { 162 | nbucket: endian.parse_u32_at(offset, data)?, 163 | table_start_idx: endian.parse_u32_at(offset, data)?, 164 | nbloom: endian.parse_u32_at(offset, data)?, 165 | nshift: endian.parse_u32_at(offset, data)?, 166 | }) 167 | } 168 | 169 | #[inline] 170 | fn size_for(_class: Class) -> usize { 171 | size_of::() + size_of::() + size_of::() + size_of::() 172 | } 173 | } 174 | 175 | type U64Table<'data, E> = ParsingTable<'data, E, u64>; 176 | 177 | impl ParseAt for u64 { 178 | fn parse_at( 179 | endian: E, 180 | _class: Class, 181 | offset: &mut usize, 182 | data: &[u8], 183 | ) -> Result { 184 | endian.parse_u64_at(offset, data) 185 | } 186 | 187 | #[inline] 188 | fn size_for(_class: Class) -> usize { 189 | core::mem::size_of::() 190 | } 191 | } 192 | 193 | #[derive(Debug)] 194 | pub struct GnuHashTable<'data, E: EndianParse> { 195 | pub hdr: GnuHashHeader, 196 | 197 | endian: E, 198 | class: Class, 199 | bloom: &'data [u8], 200 | buckets: U32Table<'data, E>, 201 | chains: U32Table<'data, E>, 202 | } 203 | 204 | impl<'data, E: EndianParse> GnuHashTable<'data, E> { 205 | /// Construct a GnuHashTable from given bytes. Keeps a reference to the data for lazy parsing. 206 | pub fn new(endian: E, class: Class, data: &'data [u8]) -> Result { 207 | let mut offset = 0; 208 | let hdr = GnuHashHeader::parse_at(endian, class, &mut offset, data)?; 209 | 210 | // length of the bloom filter in bytes. ELF32 is [u32; nbloom], ELF64 is [u64; nbloom]. 211 | let nbloom: usize = hdr.nbloom as usize; 212 | let bloom_size = match class { 213 | Class::ELF32 => nbloom 214 | .checked_mul(size_of::()) 215 | .ok_or(ParseError::IntegerOverflow)?, 216 | Class::ELF64 => nbloom 217 | .checked_mul(size_of::()) 218 | .ok_or(ParseError::IntegerOverflow)?, 219 | }; 220 | let bloom_end = offset 221 | .checked_add(bloom_size) 222 | .ok_or(ParseError::IntegerOverflow)?; 223 | let bloom_buf = data.get_bytes(offset..bloom_end)?; 224 | offset = bloom_end; 225 | 226 | let buckets_size = size_of::() 227 | .checked_mul(hdr.nbucket.try_into()?) 228 | .ok_or(ParseError::IntegerOverflow)?; 229 | let buckets_end = offset 230 | .checked_add(buckets_size) 231 | .ok_or(ParseError::IntegerOverflow)?; 232 | let buckets_buf = data.get_bytes(offset..buckets_end)?; 233 | let buckets = U32Table::new(endian, class, buckets_buf); 234 | offset = buckets_end; 235 | 236 | // the rest of the section is the chains 237 | let chains_buf = data 238 | .get(offset..) 239 | .ok_or(ParseError::SliceReadError((offset, data.len())))?; 240 | let chains = U32Table::new(endian, class, chains_buf); 241 | 242 | Ok(GnuHashTable { 243 | hdr, 244 | endian, 245 | class, 246 | bloom: bloom_buf, 247 | buckets, 248 | chains, 249 | }) 250 | } 251 | 252 | /// Use the hash table to find the symbol table entry with the given name. 253 | pub fn find( 254 | &self, 255 | name: &[u8], 256 | symtab: &SymbolTable<'data, E>, 257 | strtab: &StringTable<'data>, 258 | ) -> Result, ParseError> { 259 | // empty hash tables don't have any entries. This avoids a divde by zero in the modulus calculation, 260 | // and also avoids a potential division by zero panic in the bloom filter index calculation. 261 | if self.buckets.is_empty() || self.hdr.nbloom == 0 { 262 | return Ok(None); 263 | } 264 | 265 | let hash = gnu_hash(name); 266 | 267 | // Test against bloom filter. 268 | let (bloom_width, filter) = match self.class { 269 | Class::ELF32 => { 270 | let bloom_width: u32 = 8 * size_of::() as u32; // 32 271 | let bloom_idx = (hash / (bloom_width)) % self.hdr.nbloom; 272 | let bloom_table = U32Table::new(self.endian, self.class, self.bloom); 273 | (bloom_width, bloom_table.get(bloom_idx as usize)? as u64) 274 | } 275 | Class::ELF64 => { 276 | let bloom_width: u32 = 8 * size_of::() as u32; // 64 277 | let bloom_idx = (hash / (bloom_width)) % self.hdr.nbloom; 278 | let bloom_table = U64Table::new(self.endian, self.class, self.bloom); 279 | (bloom_width, bloom_table.get(bloom_idx as usize)?) 280 | } 281 | }; 282 | 283 | // Check bloom filter for both hashes - symbol is present in the hash table IFF both bits are set. 284 | if filter & (1 << (hash % bloom_width)) == 0 { 285 | return Ok(None); 286 | } 287 | let hash2 = hash 288 | .checked_shr(self.hdr.nshift) 289 | .ok_or(ParseError::IntegerOverflow)?; 290 | if filter & (1 << (hash2 % bloom_width)) == 0 { 291 | return Ok(None); 292 | } 293 | 294 | let table_start_idx = self.hdr.table_start_idx as usize; 295 | let chain_start_idx = self.buckets.get((hash as usize) % self.buckets.len())? as usize; 296 | if chain_start_idx < table_start_idx { 297 | // All symbols before table_start_idx don't exist in the hash table 298 | return Ok(None); 299 | } 300 | 301 | let chain_len = self.chains.len(); 302 | for chain_idx in (chain_start_idx - table_start_idx)..chain_len { 303 | let chain_hash = self.chains.get(chain_idx)?; 304 | 305 | // compare the hashes by or'ing the 1's bit back on 306 | if hash | 1 == chain_hash | 1 { 307 | // we have a hash match! 308 | // let's see if this symtab[sym_idx].name is what we're looking for 309 | let sym_idx = chain_idx 310 | .checked_add(table_start_idx) 311 | .ok_or(ParseError::IntegerOverflow)?; 312 | let symbol = symtab.get(sym_idx)?; 313 | let r_sym_name = strtab.get_raw(symbol.st_name as usize)?; 314 | 315 | if r_sym_name == name { 316 | return Ok(Some((sym_idx, symbol))); 317 | } 318 | } 319 | 320 | // the chain uses the 1's bit to signal chain comparison stoppage 321 | if chain_hash & 1 != 0 { 322 | break; 323 | } 324 | } 325 | 326 | Ok(None) 327 | } 328 | } 329 | 330 | #[cfg(test)] 331 | mod sysv_parse_tests { 332 | use super::*; 333 | use crate::endian::{BigEndian, LittleEndian}; 334 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 335 | 336 | #[test] 337 | fn parse_sysvhdr32_lsb() { 338 | test_parse_for( 339 | LittleEndian, 340 | Class::ELF32, 341 | SysVHashHeader { 342 | nbucket: 0x03020100, 343 | nchain: 0x07060504, 344 | }, 345 | ); 346 | } 347 | 348 | #[test] 349 | fn parse_sysvhdr32_msb() { 350 | test_parse_for( 351 | BigEndian, 352 | Class::ELF32, 353 | SysVHashHeader { 354 | nbucket: 0x00010203, 355 | nchain: 0x04050607, 356 | }, 357 | ); 358 | } 359 | 360 | #[test] 361 | fn parse_sysvhdr64_lsb() { 362 | test_parse_for( 363 | LittleEndian, 364 | Class::ELF64, 365 | SysVHashHeader { 366 | nbucket: 0x03020100, 367 | nchain: 0x07060504, 368 | }, 369 | ); 370 | } 371 | 372 | #[test] 373 | fn parse_sysvhdr64_msb() { 374 | test_parse_for( 375 | BigEndian, 376 | Class::ELF64, 377 | SysVHashHeader { 378 | nbucket: 0x00010203, 379 | nchain: 0x04050607, 380 | }, 381 | ); 382 | } 383 | 384 | #[test] 385 | fn parse_sysvhdr32_lsb_fuzz_too_short() { 386 | test_parse_fuzz_too_short::<_, SysVHashHeader>(LittleEndian, Class::ELF32); 387 | } 388 | 389 | #[test] 390 | fn parse_sysvhdr32_msb_fuzz_too_short() { 391 | test_parse_fuzz_too_short::<_, SysVHashHeader>(BigEndian, Class::ELF32); 392 | } 393 | 394 | #[test] 395 | fn parse_sysvhdr64_lsb_fuzz_too_short() { 396 | test_parse_fuzz_too_short::<_, SysVHashHeader>(LittleEndian, Class::ELF64); 397 | } 398 | 399 | #[test] 400 | fn parse_sysvhdr64_msb_fuzz_too_short() { 401 | test_parse_fuzz_too_short::<_, SysVHashHeader>(BigEndian, Class::ELF64); 402 | } 403 | } 404 | 405 | #[cfg(test)] 406 | mod gnu_parse_tests { 407 | use super::*; 408 | use crate::endian::{BigEndian, LittleEndian}; 409 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 410 | 411 | #[test] 412 | fn gnu_hash_tests() { 413 | // some known example hash values 414 | assert_eq!(gnu_hash(b""), 0x00001505); 415 | assert_eq!(gnu_hash(b"printf"), 0x156b2bb8); 416 | assert_eq!(gnu_hash(b"exit"), 0x7c967e3f); 417 | assert_eq!(gnu_hash(b"syscall"), 0xbac212a0); 418 | } 419 | 420 | #[test] 421 | fn parse_gnuhdr32_lsb() { 422 | test_parse_for( 423 | LittleEndian, 424 | Class::ELF32, 425 | GnuHashHeader { 426 | nbucket: 0x03020100, 427 | table_start_idx: 0x07060504, 428 | nbloom: 0x0B0A0908, 429 | nshift: 0x0F0E0D0C, 430 | }, 431 | ); 432 | } 433 | 434 | #[test] 435 | fn parse_gnuhdr32_msb() { 436 | test_parse_for( 437 | BigEndian, 438 | Class::ELF32, 439 | GnuHashHeader { 440 | nbucket: 0x00010203, 441 | table_start_idx: 0x04050607, 442 | nbloom: 0x008090A0B, 443 | nshift: 0x0C0D0E0F, 444 | }, 445 | ); 446 | } 447 | 448 | #[test] 449 | fn parse_gnuhdr64_lsb() { 450 | test_parse_for( 451 | LittleEndian, 452 | Class::ELF64, 453 | GnuHashHeader { 454 | nbucket: 0x03020100, 455 | table_start_idx: 0x07060504, 456 | nbloom: 0x0B0A0908, 457 | nshift: 0x0F0E0D0C, 458 | }, 459 | ); 460 | } 461 | 462 | #[test] 463 | fn parse_gnuhdr64_msb() { 464 | test_parse_for( 465 | BigEndian, 466 | Class::ELF64, 467 | GnuHashHeader { 468 | nbucket: 0x00010203, 469 | table_start_idx: 0x04050607, 470 | nbloom: 0x008090A0B, 471 | nshift: 0x0C0D0E0F, 472 | }, 473 | ); 474 | } 475 | 476 | #[test] 477 | fn parse_gnuhdr32_lsb_fuzz_too_short() { 478 | test_parse_fuzz_too_short::<_, GnuHashHeader>(LittleEndian, Class::ELF32); 479 | } 480 | 481 | #[test] 482 | fn parse_gnuhdr32_msb_fuzz_too_short() { 483 | test_parse_fuzz_too_short::<_, GnuHashHeader>(BigEndian, Class::ELF32); 484 | } 485 | 486 | #[test] 487 | fn parse_gnuhdr64_lsb_fuzz_too_short() { 488 | test_parse_fuzz_too_short::<_, GnuHashHeader>(LittleEndian, Class::ELF64); 489 | } 490 | 491 | #[test] 492 | fn parse_gnuhdr64_msb_fuzz_too_short() { 493 | test_parse_fuzz_too_short::<_, GnuHashHeader>(BigEndian, Class::ELF64); 494 | } 495 | } 496 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | //! Utilities to drive safe and lazy parsing of ELF structures. 2 | use core::{marker::PhantomData, ops::Range}; 3 | 4 | use crate::endian::EndianParse; 5 | use crate::file::Class; 6 | 7 | #[derive(Debug)] 8 | pub enum ParseError { 9 | /// Returned when the ELF File Header's magic bytes weren't ELF's defined 10 | /// magic bytes 11 | BadMagic([u8; 4]), 12 | /// Returned when the ELF File Header's `e_ident[EI_CLASS]` wasn't one of the 13 | /// defined `ELFCLASS*` constants 14 | UnsupportedElfClass(u8), 15 | /// Returned when the ELF File Header's `e_ident[EI_DATA]` wasn't one of the 16 | /// defined `ELFDATA*` constants 17 | UnsupportedElfEndianness(u8), 18 | /// Returned when parsing an ELF struct with a version field whose value wasn't 19 | /// something we support and know how to parse. 20 | UnsupportedVersion((u64, u64)), 21 | /// Returned when parsing an ELF structure resulted in an offset which fell 22 | /// out of bounds of the requested structure 23 | BadOffset(u64), 24 | /// Returned when parsing a string out of a StringTable failed to find the 25 | /// terminating NUL byte 26 | StringTableMissingNul(u64), 27 | /// Returned when parsing a table of ELF structures and the file specified 28 | /// an entry size for that table that was different than what we had 29 | /// expected 30 | BadEntsize((u64, u64)), 31 | /// Returned when trying to interpret a section's data as the wrong type. 32 | /// For example, trying to treat an SHT_PROGBIGS section as a SHT_STRTAB. 33 | UnexpectedSectionType((u32, u32)), 34 | /// Returned when trying to interpret a segment's data as the wrong type. 35 | /// For example, trying to treat an PT_LOAD section as a PT_NOTE. 36 | UnexpectedSegmentType((u32, u32)), 37 | /// Returned when a section has a sh_addralign value that was different 38 | /// than we expected. 39 | UnexpectedAlignment(usize), 40 | /// Returned when parsing an ELF structure out of an in-memory `&[u8]` 41 | /// resulted in a request for a section of file bytes outside the range of 42 | /// the slice. Commonly caused by truncated file contents. 43 | SliceReadError((usize, usize)), 44 | /// Returned when doing math with parsed elf fields that resulted in integer overflow. 45 | IntegerOverflow, 46 | /// Returned when parsing a string out of a StringTable that contained 47 | /// invalid Utf8 48 | Utf8Error(core::str::Utf8Error), 49 | /// Returned when parsing an ELF structure and the underlying structure data 50 | /// was truncated and thus the full structure contents could not be parsed. 51 | TryFromSliceError(core::array::TryFromSliceError), 52 | /// Returned when parsing an ELF structure whose on-disk fields were too big 53 | /// to represent in the native machine's usize type for in-memory processing. 54 | /// This could be the case when processessing large 64-bit files on a 32-bit machine. 55 | TryFromIntError(core::num::TryFromIntError), 56 | #[cfg(feature = "std")] 57 | /// Returned when parsing an ELF structure out of an io stream encountered 58 | /// an io error. 59 | IOError(std::io::Error), 60 | } 61 | 62 | #[cfg(feature = "std")] 63 | impl std::error::Error for ParseError { 64 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 65 | match *self { 66 | ParseError::BadMagic(_) => None, 67 | ParseError::UnsupportedElfClass(_) => None, 68 | ParseError::UnsupportedElfEndianness(_) => None, 69 | ParseError::UnsupportedVersion(_) => None, 70 | ParseError::BadOffset(_) => None, 71 | ParseError::StringTableMissingNul(_) => None, 72 | ParseError::BadEntsize(_) => None, 73 | ParseError::UnexpectedSectionType(_) => None, 74 | ParseError::UnexpectedSegmentType(_) => None, 75 | ParseError::UnexpectedAlignment(_) => None, 76 | ParseError::SliceReadError(_) => None, 77 | ParseError::IntegerOverflow => None, 78 | ParseError::Utf8Error(ref err) => Some(err), 79 | ParseError::TryFromSliceError(ref err) => Some(err), 80 | ParseError::TryFromIntError(ref err) => Some(err), 81 | ParseError::IOError(ref err) => Some(err), 82 | } 83 | } 84 | } 85 | 86 | #[cfg(not(feature = "std"))] 87 | impl core::error::Error for ParseError { 88 | fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { 89 | match *self { 90 | ParseError::BadMagic(_) => None, 91 | ParseError::UnsupportedElfClass(_) => None, 92 | ParseError::UnsupportedElfEndianness(_) => None, 93 | ParseError::UnsupportedVersion(_) => None, 94 | ParseError::BadOffset(_) => None, 95 | ParseError::StringTableMissingNul(_) => None, 96 | ParseError::BadEntsize(_) => None, 97 | ParseError::UnexpectedSectionType(_) => None, 98 | ParseError::UnexpectedSegmentType(_) => None, 99 | ParseError::UnexpectedAlignment(_) => None, 100 | ParseError::SliceReadError(_) => None, 101 | ParseError::IntegerOverflow => None, 102 | ParseError::Utf8Error(ref err) => Some(err), 103 | ParseError::TryFromSliceError(ref err) => Some(err), 104 | ParseError::TryFromIntError(ref err) => Some(err), 105 | } 106 | } 107 | } 108 | 109 | impl core::fmt::Display for ParseError { 110 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 111 | match *self { 112 | ParseError::BadMagic(ref magic) => { 113 | write!(f, "Invalid Magic Bytes: {magic:X?}") 114 | } 115 | ParseError::UnsupportedElfClass(class) => { 116 | write!(f, "Unsupported ELF Class: {class}") 117 | } 118 | ParseError::UnsupportedElfEndianness(endianness) => { 119 | write!(f, "Unsupported ELF Endianness: {endianness}") 120 | } 121 | ParseError::UnsupportedVersion((found, expected)) => { 122 | write!( 123 | f, 124 | "Unsupported ELF Version field found: {found} expected: {expected}" 125 | ) 126 | } 127 | ParseError::BadOffset(offset) => { 128 | write!(f, "Bad offset: {offset:#X}") 129 | } 130 | ParseError::StringTableMissingNul(offset) => { 131 | write!( 132 | f, 133 | "Could not find terminating NUL byte starting at offset: {offset:#X}" 134 | ) 135 | } 136 | ParseError::BadEntsize((found, expected)) => { 137 | write!( 138 | f, 139 | "Invalid entsize. Expected: {expected:#X}, Found: {found:#X}" 140 | ) 141 | } 142 | ParseError::UnexpectedSectionType((found, expected)) => { 143 | write!( 144 | f, 145 | "Could not interpret section of type {found} as type {expected}" 146 | ) 147 | } 148 | ParseError::UnexpectedSegmentType((found, expected)) => { 149 | write!( 150 | f, 151 | "Could not interpret section of type {found} as type {expected}" 152 | ) 153 | } 154 | ParseError::UnexpectedAlignment(align) => { 155 | write!( 156 | f, 157 | "Could not interpret section with unexpected alignment of {align}" 158 | ) 159 | } 160 | ParseError::SliceReadError((start, end)) => { 161 | write!(f, "Could not read bytes in range [{start:#X}, {end:#X})") 162 | } 163 | ParseError::IntegerOverflow => { 164 | write!(f, "Integer overflow detected") 165 | } 166 | ParseError::Utf8Error(ref err) => err.fmt(f), 167 | ParseError::TryFromSliceError(ref err) => err.fmt(f), 168 | ParseError::TryFromIntError(ref err) => err.fmt(f), 169 | #[cfg(feature = "std")] 170 | ParseError::IOError(ref err) => err.fmt(f), 171 | } 172 | } 173 | } 174 | 175 | impl From for ParseError { 176 | fn from(err: core::str::Utf8Error) -> Self { 177 | ParseError::Utf8Error(err) 178 | } 179 | } 180 | 181 | impl From for ParseError { 182 | fn from(err: core::array::TryFromSliceError) -> Self { 183 | ParseError::TryFromSliceError(err) 184 | } 185 | } 186 | 187 | impl From for ParseError { 188 | fn from(err: core::num::TryFromIntError) -> Self { 189 | ParseError::TryFromIntError(err) 190 | } 191 | } 192 | 193 | #[cfg(feature = "std")] 194 | impl From for ParseError { 195 | fn from(err: std::io::Error) -> ParseError { 196 | ParseError::IOError(err) 197 | } 198 | } 199 | 200 | /// Trait for safely parsing an ELF structure of a given class (32/64 bit) with 201 | /// an given endian-awareness at the given offset into the data buffer. 202 | /// 203 | /// This is the trait that drives our elf parser, where the various ELF 204 | /// structures implement ParseAt in order to parse their Rust-native representation 205 | /// from a buffer, all using safe code. 206 | pub trait ParseAt: Sized { 207 | /// Parse this type by using the given endian-awareness and ELF class layout. 208 | /// This is generic on EndianParse in order to allow users to optimize for 209 | /// their expectations of data layout. See EndianParse for more details. 210 | fn parse_at( 211 | endian: E, 212 | class: Class, 213 | offset: &mut usize, 214 | data: &[u8], 215 | ) -> Result; 216 | 217 | /// Returns the expected size of the type being parsed for the given ELF class 218 | fn size_for(class: Class) -> usize; 219 | 220 | /// Checks whether the given entsize matches what we need to parse this type 221 | /// 222 | /// Returns a ParseError for bad/unexpected entsizes that don't match what this type parses. 223 | fn validate_entsize(class: Class, entsize: usize) -> Result { 224 | let expected = Self::size_for(class); 225 | match entsize == expected { 226 | true => Ok(entsize), 227 | false => Err(ParseError::BadEntsize((entsize as u64, expected as u64))), 228 | } 229 | } 230 | } 231 | 232 | /// Lazy-parsing iterator which wraps bytes and parses out a `P: ParseAt` on each `next()` 233 | #[derive(Debug)] 234 | pub struct ParsingIterator<'data, E: EndianParse, P: ParseAt> { 235 | endian: E, 236 | class: Class, 237 | data: &'data [u8], 238 | offset: usize, 239 | // This struct doesn't technically own a P, but it yields them 240 | // as it iterates 241 | pd: PhantomData<&'data P>, 242 | } 243 | 244 | impl<'data, E: EndianParse, P: ParseAt> ParsingIterator<'data, E, P> { 245 | pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self { 246 | ParsingIterator { 247 | endian, 248 | class, 249 | data, 250 | offset: 0, 251 | pd: PhantomData, 252 | } 253 | } 254 | } 255 | 256 | impl Iterator for ParsingIterator<'_, E, P> { 257 | type Item = P; 258 | fn next(&mut self) -> Option { 259 | if self.data.is_empty() { 260 | return None; 261 | } 262 | 263 | Self::Item::parse_at(self.endian, self.class, &mut self.offset, self.data).ok() 264 | } 265 | } 266 | 267 | /// Lazy-parsing table which wraps bytes and parses out a `P: ParseAt` at a given index into 268 | /// the table on each `get()`. 269 | #[derive(Debug, Clone, Copy)] 270 | pub struct ParsingTable<'data, E: EndianParse, P: ParseAt> { 271 | endian: E, 272 | class: Class, 273 | data: &'data [u8], 274 | // This struct doesn't technically own a P, but it yields them 275 | pd: PhantomData<&'data P>, 276 | } 277 | 278 | impl<'data, E: EndianParse, P: ParseAt> ParsingTable<'data, E, P> { 279 | pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self { 280 | ParsingTable { 281 | endian, 282 | class, 283 | data, 284 | pd: PhantomData, 285 | } 286 | } 287 | 288 | /// Get a lazy-parsing iterator for the table's bytes 289 | pub fn iter(&self) -> ParsingIterator<'data, E, P> { 290 | ParsingIterator::new(self.endian, self.class, self.data) 291 | } 292 | 293 | /// Returns the number of elements of type P in the table. 294 | pub fn len(&self) -> usize { 295 | self.data.len() / P::size_for(self.class) 296 | } 297 | 298 | /// Returns whether the table is empty (contains zero elements). 299 | pub fn is_empty(&self) -> bool { 300 | self.len() == 0 301 | } 302 | 303 | /// Parse the element at `index` in the table. 304 | pub fn get(&self, index: usize) -> Result { 305 | if self.data.is_empty() { 306 | return Err(ParseError::BadOffset(index as u64)); 307 | } 308 | 309 | let entsize = P::size_for(self.class); 310 | let mut start = index 311 | .checked_mul(entsize) 312 | .ok_or(ParseError::IntegerOverflow)?; 313 | if start > self.data.len() { 314 | return Err(ParseError::BadOffset(index as u64)); 315 | } 316 | 317 | P::parse_at(self.endian, self.class, &mut start, self.data) 318 | } 319 | } 320 | 321 | impl<'data, E: EndianParse, P: ParseAt> IntoIterator for ParsingTable<'data, E, P> { 322 | type IntoIter = ParsingIterator<'data, E, P>; 323 | type Item = P; 324 | 325 | fn into_iter(self) -> Self::IntoIter { 326 | ParsingIterator::new(self.endian, self.class, self.data) 327 | } 328 | } 329 | 330 | // Simple convenience extension trait to wrap get() with .ok_or(SliceReadError) 331 | pub(crate) trait ReadBytesExt<'data> { 332 | fn get_bytes(self, range: Range) -> Result<&'data [u8], ParseError>; 333 | } 334 | 335 | impl<'data> ReadBytesExt<'data> for &'data [u8] { 336 | fn get_bytes(self, range: Range) -> Result<&'data [u8], ParseError> { 337 | let start = range.start; 338 | let end = range.end; 339 | self.get(range) 340 | .ok_or(ParseError::SliceReadError((start, end))) 341 | } 342 | } 343 | 344 | #[cfg(test)] 345 | pub(crate) fn test_parse_for( 346 | endian: E, 347 | class: Class, 348 | expected: P, 349 | ) { 350 | let size = P::size_for(class); 351 | let mut data = vec![0u8; size]; 352 | for (n, elem) in data.iter_mut().enumerate().take(size) { 353 | *elem = n as u8; 354 | } 355 | 356 | let mut offset = 0; 357 | let entry = P::parse_at(endian, class, &mut offset, data.as_ref()).expect("Failed to parse"); 358 | 359 | assert_eq!(entry, expected); 360 | assert_eq!(offset, size); 361 | } 362 | 363 | #[cfg(test)] 364 | pub(crate) fn test_parse_fuzz_too_short( 365 | endian: E, 366 | class: Class, 367 | ) { 368 | let size = P::size_for(class); 369 | let data = vec![0u8; size]; 370 | for n in 0..size { 371 | let buf = data.split_at(n).0; 372 | let mut offset: usize = 0; 373 | let error = P::parse_at(endian, class, &mut offset, buf).expect_err("Expected an error"); 374 | assert!( 375 | matches!(error, ParseError::SliceReadError(_)), 376 | "Unexpected Error type found: {error}" 377 | ); 378 | } 379 | } 380 | 381 | #[cfg(test)] 382 | mod read_bytes_tests { 383 | use super::ParseError; 384 | use super::ReadBytesExt; 385 | 386 | #[test] 387 | fn get_bytes_works() { 388 | let data = &[0u8, 1, 2, 3]; 389 | let subslice = data.get_bytes(1..3).expect("should be within range"); 390 | assert_eq!(subslice, [1, 2]); 391 | } 392 | 393 | #[test] 394 | fn get_bytes_out_of_range_errors() { 395 | let data = &[0u8, 1, 2, 3]; 396 | let err = data.get_bytes(3..9).expect_err("should be out of range"); 397 | assert!( 398 | matches!(err, ParseError::SliceReadError((3, 9))), 399 | "Unexpected Error type found: {err}" 400 | ); 401 | } 402 | } 403 | 404 | #[cfg(test)] 405 | mod parsing_table_tests { 406 | use crate::endian::{AnyEndian, BigEndian, LittleEndian}; 407 | 408 | use super::*; 409 | 410 | type U32Table<'data, E> = ParsingTable<'data, E, u32>; 411 | 412 | #[test] 413 | fn test_u32_validate_entsize() { 414 | assert!(matches!(u32::validate_entsize(Class::ELF32, 4), Ok(4))); 415 | assert!(matches!( 416 | u32::validate_entsize(Class::ELF32, 8), 417 | Err(ParseError::BadEntsize((8, 4))) 418 | )); 419 | } 420 | 421 | #[test] 422 | fn test_u32_parse_at() { 423 | let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; 424 | let mut offset = 2; 425 | let result = u32::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref()) 426 | .expect("Expected to parse but:"); 427 | assert_eq!(result, 0x05040302); 428 | } 429 | 430 | #[test] 431 | fn test_u32_table_len() { 432 | let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; 433 | let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); 434 | assert_eq!(table.len(), 2); 435 | } 436 | 437 | #[test] 438 | fn test_u32_table_is_empty() { 439 | let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; 440 | let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); 441 | assert!(!table.is_empty()); 442 | 443 | let table = U32Table::new(LittleEndian, Class::ELF32, &[]); 444 | assert!(table.is_empty()); 445 | 446 | let table = U32Table::new(LittleEndian, Class::ELF32, data.get(0..1).unwrap()); 447 | assert!(table.is_empty()); 448 | } 449 | 450 | #[test] 451 | fn test_u32_table_get_parse_failure() { 452 | let data = vec![0u8, 1]; 453 | let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); 454 | assert!(matches!( 455 | table.get(0), 456 | Err(ParseError::SliceReadError((0, 4))) 457 | )); 458 | } 459 | 460 | #[test] 461 | fn test_lsb_u32_table_get() { 462 | let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; 463 | let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref()); 464 | assert!(matches!(table.get(0), Ok(0x03020100))); 465 | assert!(matches!(table.get(1), Ok(0x07060504))); 466 | assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); 467 | } 468 | 469 | #[test] 470 | fn test_any_lsb_u32_table_get() { 471 | let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; 472 | let table = U32Table::new(AnyEndian::Little, Class::ELF32, data.as_ref()); 473 | assert!(matches!(table.get(0), Ok(0x03020100))); 474 | assert!(matches!(table.get(1), Ok(0x07060504))); 475 | assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); 476 | } 477 | 478 | #[test] 479 | fn test_msb_u32_table_get() { 480 | let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; 481 | let table = U32Table::new(BigEndian, Class::ELF32, data.as_ref()); 482 | assert!(matches!(table.get(0), Ok(0x00010203))); 483 | assert!(matches!(table.get(1), Ok(0x04050607))); 484 | assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); 485 | } 486 | 487 | #[test] 488 | fn test_any_msb_u32_table_get() { 489 | let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; 490 | let table = U32Table::new(AnyEndian::Big, Class::ELF32, data.as_ref()); 491 | assert!(matches!(table.get(0), Ok(0x00010203))); 492 | assert!(matches!(table.get(1), Ok(0x04050607))); 493 | assert!(matches!(table.get(7), Err(ParseError::BadOffset(7)))); 494 | } 495 | 496 | #[test] 497 | fn test_u32_table_get_unaligned() { 498 | let data = [0u8, 1, 2, 3, 4, 5, 6, 7]; 499 | let table = U32Table::new(LittleEndian, Class::ELF32, data.get(1..).unwrap()); 500 | assert!(matches!(table.get(0), Ok(0x04030201))); 501 | } 502 | } 503 | -------------------------------------------------------------------------------- /src/note.rs: -------------------------------------------------------------------------------- 1 | //! Parsing ELF notes: `.note.*`, [SHT_NOTE](crate::abi::SHT_NOTE), [PT_NOTE](crate::abi::PT_NOTE) 2 | //! 3 | //! Example for getting the GNU ABI-tag note: 4 | //! ``` 5 | //! use elf::ElfBytes; 6 | //! use elf::endian::AnyEndian; 7 | //! use elf::note::Note; 8 | //! use elf::note::NoteGnuAbiTag; 9 | //! 10 | //! let path = std::path::PathBuf::from("sample-objects/basic.x86_64"); 11 | //! let file_data = std::fs::read(path).expect("Could not read file."); 12 | //! let slice = file_data.as_slice(); 13 | //! let file = ElfBytes::::minimal_parse(slice).expect("Open test1"); 14 | //! 15 | //! let shdr = file 16 | //! .section_header_by_name(".note.ABI-tag") 17 | //! .expect("section table should be parseable") 18 | //! .expect("file should have a .note.ABI-tag section"); 19 | //! 20 | //! let notes: Vec<_> = file 21 | //! .section_data_as_notes(&shdr) 22 | //! .expect("Should be able to get note section data") 23 | //! .collect(); 24 | //! assert_eq!( 25 | //! notes[0], 26 | //! Note::GnuAbiTag(NoteGnuAbiTag { 27 | //! os: 0, 28 | //! major: 2, 29 | //! minor: 6, 30 | //! subminor: 32 31 | //! }) 32 | //! ); 33 | //! ``` 34 | use crate::abi; 35 | use crate::endian::EndianParse; 36 | use crate::file::Class; 37 | use crate::parse::{ParseAt, ParseError, ReadBytesExt}; 38 | use core::mem::size_of; 39 | use core::str::from_utf8; 40 | 41 | /// This enum contains parsed Note variants which can be matched on 42 | #[derive(Debug, PartialEq, Eq)] 43 | pub enum Note<'data> { 44 | /// (name: [abi::ELF_NOTE_GNU], n_type: [abi::NT_GNU_ABI_TAG]) 45 | GnuAbiTag(NoteGnuAbiTag), 46 | /// (name: [abi::ELF_NOTE_GNU], n_type: [abi::NT_GNU_BUILD_ID]) 47 | GnuBuildId(NoteGnuBuildId<'data>), 48 | /// All other notes that we don't know how to parse 49 | Unknown(NoteAny<'data>), 50 | } 51 | 52 | impl<'data> Note<'data> { 53 | fn parse_at( 54 | endian: E, 55 | _class: Class, 56 | mut align: usize, 57 | offset: &mut usize, 58 | data: &'data [u8], 59 | ) -> Result { 60 | // A segment with 0 alignment means no alignment required, i.e. 1, but 61 | // both name and description are padded to a 4 byte boundary per the 62 | // elf(5) man page and in practice this seems to apply when p_align is 63 | // 0 as well. 64 | if align == 0 { 65 | align = 4; 66 | } 67 | 68 | // It looks like clang and gcc emit 32-bit notes for 64-bit files, so we 69 | // currently always parse all note headers as 32-bit. 70 | let nhdr = NoteHeader::parse_at(endian, Class::ELF32, offset, data)?; 71 | 72 | let name_start = *offset; 73 | let name_size: usize = nhdr.n_namesz.try_into()?; 74 | let name_end = name_start 75 | .checked_add(name_size) 76 | .ok_or(ParseError::IntegerOverflow)?; 77 | let name = data.get_bytes(name_start..name_end)?; 78 | *offset = name_end; 79 | 80 | // skip over padding if needed to get back to 4-byte alignment 81 | if *offset % align > 0 { 82 | *offset = (*offset) 83 | .checked_add(align - *offset % align) 84 | .ok_or(ParseError::IntegerOverflow)?; 85 | } 86 | 87 | let desc_start = *offset; 88 | let desc_size: usize = nhdr.n_descsz.try_into()?; 89 | let desc_end = desc_start 90 | .checked_add(desc_size) 91 | .ok_or(ParseError::IntegerOverflow)?; 92 | let raw_desc = data.get_bytes(desc_start..desc_end)?; 93 | *offset = desc_end; 94 | 95 | // skip over padding if needed to get back to 4-byte alignment 96 | if *offset % align > 0 { 97 | *offset = (*offset) 98 | .checked_add(align - *offset % align) 99 | .ok_or(ParseError::IntegerOverflow)?; 100 | } 101 | 102 | // Interpret the note contents to try to return a known note variant 103 | match name { 104 | abi::ELF_NOTE_GNU => match nhdr.n_type { 105 | abi::NT_GNU_ABI_TAG => { 106 | let mut offset = 0; 107 | Ok(Note::GnuAbiTag(NoteGnuAbiTag::parse_at( 108 | endian, 109 | _class, 110 | &mut offset, 111 | raw_desc, 112 | )?)) 113 | } 114 | abi::NT_GNU_BUILD_ID => Ok(Note::GnuBuildId(NoteGnuBuildId(raw_desc))), 115 | _ => Ok(Note::Unknown(NoteAny { 116 | n_type: nhdr.n_type, 117 | name, 118 | desc: raw_desc, 119 | })), 120 | }, 121 | _ => Ok(Note::Unknown(NoteAny { 122 | n_type: nhdr.n_type, 123 | name, 124 | desc: raw_desc, 125 | })), 126 | } 127 | } 128 | } 129 | 130 | /// Contains four 4-byte integers. 131 | /// The first 4-byte integer specifies the os. The second, third, and fourth 132 | /// 4-byte integers contain the earliest compatible kernel version. 133 | /// For example, if the 3 integers are 6, 0, and 7, this signifies a 6.0.7 kernel. 134 | /// 135 | /// (see: ) 136 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 137 | pub struct NoteGnuAbiTag { 138 | pub os: u32, 139 | pub major: u32, 140 | pub minor: u32, 141 | pub subminor: u32, 142 | } 143 | 144 | impl ParseAt for NoteGnuAbiTag { 145 | fn parse_at( 146 | endian: E, 147 | _class: Class, 148 | offset: &mut usize, 149 | data: &[u8], 150 | ) -> Result { 151 | Ok(NoteGnuAbiTag { 152 | os: endian.parse_u32_at(offset, data)?, 153 | major: endian.parse_u32_at(offset, data)?, 154 | minor: endian.parse_u32_at(offset, data)?, 155 | subminor: endian.parse_u32_at(offset, data)?, 156 | }) 157 | } 158 | 159 | fn size_for(_class: Class) -> usize { 160 | size_of::() * 4 161 | } 162 | } 163 | 164 | /// Contains a build ID note which is unique among the set of meaningful contents 165 | /// for ELF files and identical when the output file would otherwise have been identical. 166 | /// This is a zero-copy type which merely contains a slice of the note data from which it was parsed. 167 | /// 168 | /// (see: ) 169 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 170 | pub struct NoteGnuBuildId<'data>(pub &'data [u8]); 171 | 172 | /// Contains the raw fields found in any ELF note. Used for notes that we don't know 173 | /// how to parse into more specific types. 174 | #[derive(Debug, PartialEq, Eq)] 175 | pub struct NoteAny<'data> { 176 | pub n_type: u64, 177 | pub name: &'data [u8], 178 | pub desc: &'data [u8], 179 | } 180 | 181 | impl NoteAny<'_> { 182 | /// Parses the note's name bytes as a utf8 sequence, with any trailing NUL bytes removed 183 | pub fn name_str(&self) -> Result<&str, ParseError> { 184 | let name = from_utf8(self.name)?; 185 | Ok(name.trim_end_matches('\0')) 186 | } 187 | } 188 | 189 | #[derive(Debug)] 190 | pub struct NoteIterator<'data, E: EndianParse> { 191 | endian: E, 192 | class: Class, 193 | align: usize, 194 | data: &'data [u8], 195 | offset: usize, 196 | } 197 | 198 | impl<'data, E: EndianParse> NoteIterator<'data, E> { 199 | pub fn new(endian: E, class: Class, align: usize, data: &'data [u8]) -> Self { 200 | NoteIterator { 201 | endian, 202 | class, 203 | align, 204 | data, 205 | offset: 0, 206 | } 207 | } 208 | } 209 | 210 | impl<'data, E: EndianParse> Iterator for NoteIterator<'data, E> { 211 | type Item = Note<'data>; 212 | fn next(&mut self) -> Option { 213 | if self.data.is_empty() { 214 | return None; 215 | } 216 | 217 | Note::parse_at( 218 | self.endian, 219 | self.class, 220 | self.align, 221 | &mut self.offset, 222 | self.data, 223 | ) 224 | .ok() 225 | } 226 | } 227 | 228 | #[derive(Debug, Clone, PartialEq, Eq)] 229 | struct NoteHeader { 230 | pub n_namesz: u64, 231 | pub n_descsz: u64, 232 | pub n_type: u64, 233 | } 234 | 235 | impl ParseAt for NoteHeader { 236 | fn parse_at( 237 | endian: E, 238 | class: Class, 239 | offset: &mut usize, 240 | data: &[u8], 241 | ) -> Result { 242 | match class { 243 | Class::ELF32 => Ok(NoteHeader { 244 | n_namesz: endian.parse_u32_at(offset, data)? as u64, 245 | n_descsz: endian.parse_u32_at(offset, data)? as u64, 246 | n_type: endian.parse_u32_at(offset, data)? as u64, 247 | }), 248 | Class::ELF64 => Ok(NoteHeader { 249 | n_namesz: endian.parse_u64_at(offset, data)?, 250 | n_descsz: endian.parse_u64_at(offset, data)?, 251 | n_type: endian.parse_u64_at(offset, data)?, 252 | }), 253 | } 254 | } 255 | 256 | #[inline] 257 | fn size_for(class: Class) -> usize { 258 | match class { 259 | Class::ELF32 => 12, 260 | Class::ELF64 => 24, 261 | } 262 | } 263 | } 264 | 265 | #[cfg(test)] 266 | mod parse_tests { 267 | use super::*; 268 | use crate::endian::{BigEndian, LittleEndian}; 269 | 270 | #[test] 271 | fn parse_nt_gnu_abi_tag() { 272 | #[rustfmt::skip] 273 | let data = [ 274 | 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 275 | 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 276 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 277 | 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 278 | ]; 279 | 280 | let mut offset = 0; 281 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 282 | .expect("Failed to parse"); 283 | 284 | assert_eq!( 285 | note, 286 | Note::GnuAbiTag(NoteGnuAbiTag { 287 | os: abi::ELF_NOTE_GNU_ABI_TAG_OS_LINUX, 288 | major: 2, 289 | minor: 6, 290 | subminor: 32 291 | }) 292 | ); 293 | } 294 | 295 | #[test] 296 | fn parse_desc_gnu_build_id() { 297 | let data = [ 298 | 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 299 | 0x55, 0x00, 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 300 | 0xb0, 0xee, 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, 301 | ]; 302 | 303 | let mut offset = 0; 304 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 305 | .expect("Failed to parse"); 306 | 307 | assert_eq!( 308 | note, 309 | Note::GnuBuildId(NoteGnuBuildId(&[ 310 | 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 0xb0, 0xee, 311 | 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, 312 | ])) 313 | ); 314 | } 315 | 316 | #[test] 317 | fn parse_note_with_8_byte_alignment() { 318 | // This is a .note.gnu.property section, which has been seen generated with 8-byte alignment 319 | #[rustfmt::skip] 320 | let data = [ 321 | 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 322 | 0x05, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 323 | 0x02, 0x00, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00, 324 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 325 | ]; 326 | 327 | // Even though the file class is ELF64, we parse it as a 32-bit struct. gcc/clang seem to output 32-bit notes 328 | // even though the gABI states that ELF64 files should contain 64-bit notes. Sometimes those notes are generated 329 | // in sections with 4-byte alignment, and other times with 8-byte alignment, as specified by shdr.sh_addralign. 330 | // 331 | // See https://raw.githubusercontent.com/wiki/hjl-tools/linux-abi/linux-abi-draft.pdf 332 | // Excerpt: 333 | // All entries in a PT_NOTE segment have the same alignment which equals to the 334 | // p_align field in program header. 335 | // According to gABI, each note entry should be aligned to 4 bytes in 32-bit 336 | // objects or 8 bytes in 64-bit objects. But .note.ABI-tag section (see Sec- 337 | // tion 2.1.6) and .note.gnu.build-id section (see Section 2.1.4) are aligned 338 | // to 4 bytes in both 32-bit and 64-bit objects. Note parser should use p_align for 339 | // note alignment, instead of assuming alignment based on ELF file class. 340 | let mut offset = 0; 341 | let note = Note::parse_at(LittleEndian, Class::ELF64, 8, &mut offset, &data) 342 | .expect("Failed to parse"); 343 | assert_eq!( 344 | note, 345 | Note::Unknown(NoteAny { 346 | n_type: 5, 347 | name: abi::ELF_NOTE_GNU, 348 | desc: &[ 349 | 0x2, 0x0, 0x0, 0xc0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 350 | ] 351 | }) 352 | ); 353 | } 354 | 355 | #[test] 356 | fn parse_note_with_8_byte_alignment_unaligned_namesz() { 357 | let data = [ 358 | 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // namesz 5, descsz 2 359 | 0x42, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x55, // type 42 (unknown), name GNUU 360 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // NUL + 7 pad for 8 alignment 361 | 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // desc 0102 + 6 pad for alignment 362 | ]; 363 | 364 | let mut offset = 0; 365 | let note = Note::parse_at(LittleEndian, Class::ELF32, 8, &mut offset, &data) 366 | .expect("Failed to parse"); 367 | assert_eq!( 368 | note, 369 | Note::Unknown(NoteAny { 370 | n_type: 0x42, 371 | name: b"GNUU\0", 372 | desc: &[0x01, 0x02], 373 | }) 374 | ); 375 | assert_eq!(offset, 32); 376 | } 377 | 378 | #[test] 379 | fn parse_note_for_elf64_expects_nhdr32() { 380 | let data = [ 381 | 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 382 | 0x55, 0x00, 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 383 | 0xb0, 0xee, 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, 384 | ]; 385 | 386 | let mut offset = 0; 387 | // Even though the file class is ELF64, we parse it as a 32-bit struct. gcc/clang seem to output 32-bit notes 388 | // even though the gABI states that ELF64 files should contain 64-bit notes. 389 | let note = Note::parse_at(LittleEndian, Class::ELF64, 4, &mut offset, &data) 390 | .expect("Failed to parse"); 391 | assert_eq!( 392 | note, 393 | Note::GnuBuildId(NoteGnuBuildId(&[ 394 | 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 0xb0, 0xee, 395 | 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3, 396 | ])) 397 | ); 398 | } 399 | 400 | #[test] 401 | fn parse_note_32_lsb() { 402 | let data = [ 403 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 404 | 0x00, 0x00, 405 | ]; 406 | 407 | let mut offset = 0; 408 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 409 | .expect("Failed to parse"); 410 | assert_eq!( 411 | note, 412 | Note::Unknown(NoteAny { 413 | n_type: 6, 414 | name: &[], 415 | desc: &[0x20, 0x0], 416 | }) 417 | ); 418 | assert_eq!(offset, 16); 419 | } 420 | 421 | #[test] 422 | fn parse_note_32_lsb_with_name_padding() { 423 | let data = [ 424 | 0x03, 0x00, 0x00, 0x00, // namesz 3 425 | 0x04, 0x00, 0x00, 0x00, // descsz 4 426 | 0x01, 0x00, 0x00, 0x00, // type 1 427 | 0x47, 0x4e, 0x00, 0x00, // name GN\0 + 1 pad byte 428 | 0x01, 0x02, 0x03, 0x04, 429 | ]; // desc 01020304 430 | 431 | let mut offset = 0; 432 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 433 | .expect("Failed to parse"); 434 | assert_eq!( 435 | note, 436 | Note::Unknown(NoteAny { 437 | n_type: 1, 438 | name: b"GN\0", 439 | desc: &[0x01, 0x02, 0x03, 0x04], 440 | }) 441 | ); 442 | assert_eq!(offset, 20); 443 | } 444 | 445 | #[test] 446 | fn parse_note_32_lsb_with_desc_padding() { 447 | let data = [ 448 | 0x04, 0x00, 0x00, 0x00, // namesz 4 449 | 0x02, 0x00, 0x00, 0x00, // descsz 2 450 | 0x42, 0x00, 0x00, 0x00, // type 42 (unknown) 451 | 0x47, 0x4e, 0x55, 0x00, // name GNU\0 452 | 0x01, 0x02, 0x00, 0x00, // desc 0102 + 2 pad bytes 453 | ]; 454 | 455 | let mut offset = 0; 456 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 457 | .expect("Failed to parse"); 458 | assert_eq!( 459 | note, 460 | Note::Unknown(NoteAny { 461 | n_type: 0x42, 462 | name: abi::ELF_NOTE_GNU, 463 | desc: &[0x01, 0x02], 464 | }) 465 | ); 466 | assert_eq!(offset, 20); 467 | } 468 | 469 | #[test] 470 | fn parse_note_32_lsb_with_no_name() { 471 | let data = [ 472 | 0x00, 0x00, 0x00, 0x00, // namesz 0 473 | 0x02, 0x00, 0x00, 0x00, // descsz 2 474 | 0x42, 0x00, 0x00, 0x00, // type 42 (unknown) 475 | 0x20, 0x00, 0x00, 0x00, // desc 20, 00 + 2 pad bytes 476 | ]; 477 | 478 | let mut offset = 0; 479 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 480 | .expect("Failed to parse"); 481 | assert_eq!( 482 | note, 483 | Note::Unknown(NoteAny { 484 | n_type: 0x42, 485 | name: &[], 486 | desc: &[0x20, 0x0], 487 | }) 488 | ); 489 | assert_eq!(offset, 16); 490 | } 491 | 492 | #[test] 493 | fn parse_note_32_lsb_with_no_desc() { 494 | let data = [ 495 | 0x04, 0x00, 0x00, 0x00, // namesz 4 496 | 0x00, 0x00, 0x00, 0x00, // descsz 0 497 | 0x42, 0x00, 0x00, 0x00, // type 42 (unknown) 498 | 0x47, 0x4e, 0x55, 0x00, // name GNU\0 499 | ]; 500 | 501 | let mut offset = 0; 502 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 503 | .expect("Failed to parse"); 504 | assert_eq!( 505 | note, 506 | Note::Unknown(NoteAny { 507 | n_type: 0x42, 508 | name: abi::ELF_NOTE_GNU, 509 | desc: &[], 510 | }) 511 | ); 512 | assert_eq!(offset, 16); 513 | } 514 | 515 | #[test] 516 | fn parse_note_any_with_invalid_utf8_name() { 517 | let data = [ 518 | 0x04, 0x00, 0x00, 0x00, // namesz 4 519 | 0x00, 0x00, 0x00, 0x00, // descsz 0 520 | 0x42, 0x00, 0x00, 0x00, // type 42 (unknown) 521 | 0x47, 0xc3, 0x28, 0x00, // name G..\0 (dots are an invalid utf8 sequence) 522 | ]; 523 | 524 | let mut offset = 0; 525 | let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data) 526 | .expect("Failed to parse"); 527 | assert_eq!( 528 | note, 529 | Note::Unknown(NoteAny { 530 | n_type: 0x42, 531 | name: &[0x47, 0xc3, 0x28, 0x0], 532 | desc: &[], 533 | }) 534 | ); 535 | assert_eq!(offset, 16); 536 | } 537 | 538 | #[test] 539 | fn name_str_works_for_note_any_with_valid_utf8_name() { 540 | let note = NoteAny { 541 | n_type: 1, 542 | name: &[0x47, 0x4e, 0x55, 0x00], 543 | desc: &[], 544 | }; 545 | let name = note.name_str().expect("Failed to parse utf8"); 546 | assert_eq!(name, "GNU"); 547 | } 548 | 549 | #[test] 550 | fn name_str_errors_for_note_any_with_invalid_utf8_name() { 551 | let note = NoteAny { 552 | n_type: 1, 553 | name: &[0x47, 0xc3, 0x28, 0x00], 554 | desc: &[], 555 | }; 556 | assert!(matches!(note.name_str(), Err(ParseError::Utf8Error(_)))); 557 | } 558 | 559 | use crate::parse::{test_parse_for, test_parse_fuzz_too_short}; 560 | 561 | #[test] 562 | fn parse_nhdr32_lsb() { 563 | test_parse_for( 564 | LittleEndian, 565 | Class::ELF32, 566 | NoteHeader { 567 | n_namesz: 0x03020100, 568 | n_descsz: 0x07060504, 569 | n_type: 0x0B0A0908, 570 | }, 571 | ); 572 | } 573 | 574 | #[test] 575 | fn parse_nhdr32_msb() { 576 | test_parse_for( 577 | BigEndian, 578 | Class::ELF32, 579 | NoteHeader { 580 | n_namesz: 0x00010203, 581 | n_descsz: 0x04050607, 582 | n_type: 0x08090A0B, 583 | }, 584 | ); 585 | } 586 | 587 | #[test] 588 | fn parse_nhdr64_lsb() { 589 | test_parse_for( 590 | LittleEndian, 591 | Class::ELF64, 592 | NoteHeader { 593 | n_namesz: 0x0706050403020100, 594 | n_descsz: 0x0F0E0D0C0B0A0908, 595 | n_type: 0x1716151413121110, 596 | }, 597 | ); 598 | } 599 | 600 | #[test] 601 | fn parse_nhdr64_msb() { 602 | test_parse_for( 603 | BigEndian, 604 | Class::ELF64, 605 | NoteHeader { 606 | n_namesz: 0x0001020304050607, 607 | n_descsz: 0x08090A0B0C0D0E0F, 608 | n_type: 0x1011121314151617, 609 | }, 610 | ); 611 | } 612 | 613 | #[test] 614 | fn parse_nhdr32_lsb_fuzz_too_short() { 615 | test_parse_fuzz_too_short::<_, NoteHeader>(LittleEndian, Class::ELF32); 616 | } 617 | 618 | #[test] 619 | fn parse_nhdr32_msb_fuzz_too_short() { 620 | test_parse_fuzz_too_short::<_, NoteHeader>(BigEndian, Class::ELF32); 621 | } 622 | 623 | #[test] 624 | fn parse_nhdr64_lsb_fuzz_too_short() { 625 | test_parse_fuzz_too_short::<_, NoteHeader>(LittleEndian, Class::ELF64); 626 | } 627 | 628 | #[test] 629 | fn parse_nhdr64_msb_fuzz_too_short() { 630 | test_parse_fuzz_too_short::<_, NoteHeader>(BigEndian, Class::ELF64); 631 | } 632 | } 633 | -------------------------------------------------------------------------------- /src/to_str.rs: -------------------------------------------------------------------------------- 1 | //! Optional module for getting string representations of ELF constants 2 | use crate::abi; 3 | 4 | #[cfg(all(feature = "alloc", not(feature = "std")))] 5 | use alloc::{ 6 | format, 7 | string::{String, ToString}, 8 | }; 9 | 10 | pub fn e_osabi_to_str(e_osabi: u8) -> Option<&'static str> { 11 | match e_osabi { 12 | abi::ELFOSABI_SYSV => Some("ELFOSABI_SYSV"), 13 | abi::ELFOSABI_HPUX => Some("ELFOSABI_HPUX"), 14 | abi::ELFOSABI_NETBSD => Some("ELFOSABI_NETBSD"), 15 | abi::ELFOSABI_LINUX => Some("ELFOSABI_LINUX"), 16 | abi::ELFOSABI_SOLARIS => Some("ELFOSABI_SOLARIS"), 17 | abi::ELFOSABI_AIX => Some("ELFOSABI_AIX"), 18 | abi::ELFOSABI_IRIX => Some("ELFOSABI_IRIX"), 19 | abi::ELFOSABI_FREEBSD => Some("ELFOSABI_FREEBSD"), 20 | abi::ELFOSABI_TRU64 => Some("ELFOSABI_TRU64"), 21 | abi::ELFOSABI_MODESTO => Some("ELFOSABI_MODESTO"), 22 | abi::ELFOSABI_OPENBSD => Some("ELFOSABI_OPENBSD"), 23 | abi::ELFOSABI_OPENVMS => Some("ELFOSABI_OPENVMS"), 24 | abi::ELFOSABI_NSK => Some("ELFOSABI_NSK"), 25 | abi::ELFOSABI_AROS => Some("ELFOSABI_AROS"), 26 | abi::ELFOSABI_FENIXOS => Some("ELFOSABI_FENIXOS"), 27 | abi::ELFOSABI_CLOUDABI => Some("ELFOSABI_CLOUDABI"), 28 | abi::ELFOSABI_OPENVOS => Some("ELFOSABI_OPENVOS"), 29 | _ => None, 30 | } 31 | } 32 | 33 | #[cfg(feature = "alloc")] 34 | pub fn e_osabi_to_string(e_osabi: u8) -> String { 35 | match e_osabi_to_str(e_osabi) { 36 | Some(s) => s.to_string(), 37 | None => format!("e_osabi({e_osabi:#x})"), 38 | } 39 | } 40 | 41 | pub fn e_type_to_human_str(e_type: u16) -> Option<&'static str> { 42 | match e_type { 43 | abi::ET_NONE => Some("No file type"), 44 | abi::ET_REL => Some("Relocatable file"), 45 | abi::ET_EXEC => Some("Executable file"), 46 | abi::ET_DYN => Some("Shared object file"), 47 | abi::ET_CORE => Some("Core file"), 48 | _ => None, 49 | } 50 | } 51 | 52 | pub fn e_type_to_str(e_type: u16) -> Option<&'static str> { 53 | match e_type { 54 | abi::ET_NONE => Some("ET_NONE"), 55 | abi::ET_REL => Some("ET_REL"), 56 | abi::ET_EXEC => Some("ET_EXEC"), 57 | abi::ET_DYN => Some("ET_DYN"), 58 | abi::ET_CORE => Some("ET_CORE"), 59 | _ => None, 60 | } 61 | } 62 | 63 | #[cfg(feature = "alloc")] 64 | pub fn e_type_to_string(e_type: u16) -> String { 65 | match e_type_to_str(e_type) { 66 | Some(s) => s.to_string(), 67 | None => format!("e_type({e_type:#x})"), 68 | } 69 | } 70 | 71 | pub fn e_machine_to_human_str(e_machine: u16) -> Option<&'static str> { 72 | match e_machine { 73 | abi::EM_NONE => Some("No machine"), 74 | abi::EM_M32 => Some("AT&T WE 32100"), 75 | abi::EM_SPARC => Some("SPARC"), 76 | abi::EM_386 => Some("Intel 80386"), 77 | abi::EM_68K => Some("Motorola 68000"), 78 | abi::EM_88K => Some("Motorola 88000"), 79 | abi::EM_IAMCU => Some("Intel MCU"), 80 | abi::EM_860 => Some("Intel 80860"), 81 | abi::EM_MIPS => Some("MIPS I Architecture"), 82 | abi::EM_S370 => Some("IBM System/370 Processor"), 83 | abi::EM_MIPS_RS3_LE => Some("MIPS RS3000 Little-endian"), 84 | abi::EM_PARISC => Some("Hewlett-Packard PA-RISC"), 85 | abi::EM_VPP500 => Some("Fujitsu VPP500"), 86 | abi::EM_SPARC32PLUS => Some("Enhanced instruction set SPARC"), 87 | abi::EM_960 => Some("Intel 80960"), 88 | abi::EM_PPC => Some("PowerPC"), 89 | abi::EM_PPC64 => Some("64-bit PowerPC"), 90 | abi::EM_S390 => Some("IBM System/390 Processor"), 91 | abi::EM_SPU => Some("IBM SPU/SPC"), 92 | abi::EM_V800 => Some("NEC V800"), 93 | abi::EM_FR20 => Some("Fujitsu FR20"), 94 | abi::EM_RH32 => Some("TRW RH-32"), 95 | abi::EM_RCE => Some("Motorola RCE"), 96 | abi::EM_ARM => Some("ARM 32-bit architecture (AARCH32)"), 97 | abi::EM_ALPHA => Some("Digital Alpha"), 98 | abi::EM_SH => Some("Hitachi SH"), 99 | abi::EM_SPARCV9 => Some("SPARC Version 9"), 100 | abi::EM_TRICORE => Some("Siemens TriCore embedded processor"), 101 | abi::EM_ARC => Some("Argonaut RISC Core, Argonaut Technologies Inc."), 102 | abi::EM_H8_300 => Some("Hitachi H8/300"), 103 | abi::EM_H8_300H => Some("Hitachi H8/300H"), 104 | abi::EM_H8S => Some("Hitachi H8S"), 105 | abi::EM_H8_500 => Some("Hitachi H8/500"), 106 | abi::EM_IA_64 => Some("Intel IA-64 processor architecture"), 107 | abi::EM_MIPS_X => Some("Stanford MIPS-X"), 108 | abi::EM_COLDFIRE => Some("Motorola ColdFire"), 109 | abi::EM_68HC12 => Some("Motorola M68HC12"), 110 | abi::EM_MMA => Some("Fujitsu MMA Multimedia Accelerator"), 111 | abi::EM_PCP => Some("Siemens PCP"), 112 | abi::EM_NCPU => Some("Sony nCPU embedded RISC processor"), 113 | abi::EM_NDR1 => Some("Denso NDR1 microprocessor"), 114 | abi::EM_STARCORE => Some("Motorola Star*Core processor"), 115 | abi::EM_ME16 => Some("Toyota ME16 processor"), 116 | abi::EM_ST100 => Some("STMicroelectronics ST100 processor"), 117 | abi::EM_TINYJ => Some("Advanced Logic Corp. TinyJ embedded processor family"), 118 | abi::EM_X86_64 => Some("AMD x86-64 architecture"), 119 | abi::EM_PDSP => Some("Sony DSP Processor"), 120 | abi::EM_PDP10 => Some("Digital Equipment Corp. PDP-10"), 121 | abi::EM_PDP11 => Some("Digital Equipment Corp. PDP-11"), 122 | abi::EM_FX66 => Some("Siemens FX66 microcontroller"), 123 | abi::EM_ST9PLUS => Some("STMicroelectronics ST9+ 8/16 bit microcontroller"), 124 | abi::EM_ST7 => Some("STMicroelectronics ST7 8-bit microcontroller"), 125 | abi::EM_68HC16 => Some("Motorola MC68HC16 Microcontroller"), 126 | abi::EM_68HC11 => Some("Motorola MC68HC11 Microcontroller"), 127 | abi::EM_68HC08 => Some("Motorola MC68HC08 Microcontroller"), 128 | abi::EM_68HC05 => Some("Motorola MC68HC05 Microcontroller"), 129 | abi::EM_SVX => Some("Silicon Graphics SVx"), 130 | abi::EM_ST19 => Some("STMicroelectronics ST19 8-bit microcontroller"), 131 | abi::EM_VAX => Some("Digital VAX"), 132 | abi::EM_CRIS => Some("Axis Communications 32-bit embedded processor"), 133 | abi::EM_JAVELIN => Some("Infineon Technologies 32-bit embedded processor"), 134 | abi::EM_FIREPATH => Some("Element 14 64-bit DSP Processor"), 135 | abi::EM_ZSP => Some("LSI Logic 16-bit DSP Processor"), 136 | abi::EM_MMIX => Some("Donald Knuth's educational 64-bit processor"), 137 | abi::EM_HUANY => Some("Harvard University machine-independent object files"), 138 | abi::EM_PRISM => Some("SiTera Prism"), 139 | abi::EM_AVR => Some("Atmel AVR 8-bit microcontroller"), 140 | abi::EM_FR30 => Some("Fujitsu FR30"), 141 | abi::EM_D10V => Some("Mitsubishi D10V"), 142 | abi::EM_D30V => Some("Mitsubishi D30V"), 143 | abi::EM_V850 => Some("NEC v850"), 144 | abi::EM_M32R => Some("Mitsubishi M32R"), 145 | abi::EM_MN10300 => Some("Matsushita MN10300"), 146 | abi::EM_MN10200 => Some("Matsushita MN10200"), 147 | abi::EM_PJ => Some("picoJava"), 148 | abi::EM_OPENRISC => Some("OpenRISC 32-bit embedded processor"), 149 | abi::EM_ARC_COMPACT => Some("ARC International ARCompact processor"), 150 | abi::EM_XTENSA => Some("Tensilica Xtensa Architecture"), 151 | abi::EM_VIDEOCORE => Some("Alphamosaic VideoCore processor"), 152 | abi::EM_TMM_GPP => Some("Thompson Multimedia General Purpose Processor"), 153 | abi::EM_NS32K => Some("National Semiconductor 32000 series"), 154 | abi::EM_TPC => Some("Tenor Network TPC processor"), 155 | abi::EM_SNP1K => Some("Trebia SNP 1000 processor"), 156 | abi::EM_ST200 => Some("STMicroelectronics (www.st.com) ST200 microcontroller"), 157 | abi::EM_IP2K => Some("Ubicom IP2xxx microcontroller family"), 158 | abi::EM_MAX => Some("MAX Processor"), 159 | abi::EM_CR => Some("National Semiconductor CompactRISC microprocessor"), 160 | abi::EM_F2MC16 => Some("Fujitsu F2MC16"), 161 | abi::EM_MSP430 => Some("Texas Instruments embedded microcontroller msp430"), 162 | abi::EM_BLACKFIN => Some("Analog Devices Blackfin (DSP) processor"), 163 | abi::EM_SE_C33 => Some("S1C33 Family of Seiko Epson processors"), 164 | abi::EM_SEP => Some("Sharp embedded microprocessor"), 165 | abi::EM_ARCA => Some("Arca RISC Microprocessor"), 166 | abi::EM_UNICORE => { 167 | Some("Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University") 168 | } 169 | abi::EM_EXCESS => Some("eXcess: 16/32/64-bit configurable embedded CPU"), 170 | abi::EM_DXP => Some("Icera Semiconductor Inc. Deep Execution Processor"), 171 | abi::EM_ALTERA_NIOS2 => Some("Altera Nios II soft-core processor"), 172 | abi::EM_CRX => Some("National Semiconductor CompactRISC CRX microprocessor"), 173 | abi::EM_XGATE => Some("Motorola XGATE embedded processor"), 174 | abi::EM_C166 => Some("Infineon C16x/XC16x processor"), 175 | abi::EM_M16C => Some("Renesas M16C series microprocessors"), 176 | abi::EM_DSPIC30F => Some("Microchip Technology dsPIC30F Digital Signal Controller"), 177 | abi::EM_CE => Some("Freescale Communication Engine RISC core"), 178 | abi::EM_M32C => Some("Renesas M32C series microprocessors"), 179 | abi::EM_TSK3000 => Some("Altium TSK3000 core"), 180 | abi::EM_RS08 => Some("Freescale RS08 embedded processor"), 181 | abi::EM_SHARC => Some("Analog Devices SHARC family of 32-bit DSP processors"), 182 | abi::EM_ECOG2 => Some("Cyan Technology eCOG2 microprocessor"), 183 | abi::EM_SCORE7 => Some("Sunplus S+core7 RISC processor"), 184 | abi::EM_DSP24 => Some("New Japan Radio (NJR) 24-bit DSP Processor"), 185 | abi::EM_VIDEOCORE3 => Some("Broadcom VideoCore III processor"), 186 | abi::EM_LATTICEMICO32 => Some("RISC processor for Lattice FPGA architecture"), 187 | abi::EM_SE_C17 => Some("Seiko Epson C17 family"), 188 | abi::EM_TI_C6000 => Some("The Texas Instruments TMS320C6000 DSP family"), 189 | abi::EM_TI_C2000 => Some("The Texas Instruments TMS320C2000 DSP family"), 190 | abi::EM_TI_C5500 => Some("The Texas Instruments TMS320C55x DSP family"), 191 | abi::EM_TI_ARP32 => { 192 | Some("Texas Instruments Application Specific RISC Processor, 32bit fetch") 193 | } 194 | abi::EM_TI_PRU => Some("Texas Instruments Programmable Realtime Unit"), 195 | abi::EM_MMDSP_PLUS => Some("STMicroelectronics 64bit VLIW Data Signal Processor"), 196 | abi::EM_CYPRESS_M8C => Some("Cypress M8C microprocessor"), 197 | abi::EM_R32C => Some("Renesas R32C series microprocessors"), 198 | abi::EM_TRIMEDIA => Some("NXP Semiconductors TriMedia architecture family"), 199 | abi::EM_QDSP6 => Some("QUALCOMM DSP6 Processor"), 200 | abi::EM_8051 => Some("Intel 8051 and variants"), 201 | abi::EM_STXP7X => { 202 | Some("STMicroelectronics STxP7x family of configurable and extensible RISC processors") 203 | } 204 | abi::EM_NDS32 => Some("Andes Technology compact code size embedded RISC processor family"), 205 | abi::EM_ECOG1X => Some("Cyan Technology eCOG1X family"), 206 | abi::EM_MAXQ30 => Some("Dallas Semiconductor MAXQ30 Core Micro-controllers"), 207 | abi::EM_XIMO16 => Some("New Japan Radio (NJR) 16-bit DSP Processor"), 208 | abi::EM_MANIK => Some("M2000 Reconfigurable RISC Microprocessor"), 209 | abi::EM_CRAYNV2 => Some("Cray Inc. NV2 vector architecture"), 210 | abi::EM_RX => Some("Renesas RX family"), 211 | abi::EM_METAG => Some("Imagination Technologies META processor architecture"), 212 | abi::EM_MCST_ELBRUS => Some("MCST Elbrus general purpose hardware architecture"), 213 | abi::EM_ECOG16 => Some("Cyan Technology eCOG16 family"), 214 | abi::EM_CR16 => Some("National Semiconductor CompactRISC CR16 16-bit microprocessor"), 215 | abi::EM_ETPU => Some("Freescale Extended Time Processing Unit"), 216 | abi::EM_SLE9X => Some("Infineon Technologies SLE9X core"), 217 | abi::EM_L10M => Some("Intel L10M"), 218 | abi::EM_K10M => Some("Intel K10M"), 219 | abi::EM_AARCH64 => Some("ARM 64-bit architecture (AARCH64)"), 220 | abi::EM_AVR32 => Some("Atmel Corporation 32-bit microprocessor family"), 221 | abi::EM_STM8 => Some("STMicroeletronics STM8 8-bit microcontroller"), 222 | abi::EM_TILE64 => Some("Tilera TILE64 multicore architecture family"), 223 | abi::EM_TILEPRO => Some("Tilera TILEPro multicore architecture family"), 224 | abi::EM_MICROBLAZE => Some("Xilinx MicroBlaze 32-bit RISC soft processor core"), 225 | abi::EM_CUDA => Some("NVIDIA CUDA architecture"), 226 | abi::EM_TILEGX => Some("Tilera TILE-Gx multicore architecture family"), 227 | abi::EM_CLOUDSHIELD => Some("CloudShield architecture family"), 228 | abi::EM_COREA_1ST => Some("KIPO-KAIST Core-A 1st generation processor family"), 229 | abi::EM_COREA_2ND => Some("KIPO-KAIST Core-A 2nd generation processor family"), 230 | abi::EM_ARC_COMPACT2 => Some("Synopsys ARCompact V2"), 231 | abi::EM_OPEN8 => Some("Open8 8-bit RISC soft processor core"), 232 | abi::EM_RL78 => Some("Renesas RL78 family"), 233 | abi::EM_VIDEOCORE5 => Some("Broadcom VideoCore V processor"), 234 | abi::EM_78KOR => Some("Renesas 78KOR family"), 235 | abi::EM_56800EX => Some("Freescale 56800EX Digital Signal Controller (DSC)"), 236 | abi::EM_BA1 => Some("Beyond BA1 CPU architecture"), 237 | abi::EM_BA2 => Some("Beyond BA2 CPU architecture"), 238 | abi::EM_XCORE => Some("XMOS xCORE processor family"), 239 | abi::EM_MCHP_PIC => Some("Microchip 8-bit PIC(r) family"), 240 | abi::EM_INTEL205 => Some("Reserved by Intel"), 241 | abi::EM_INTEL206 => Some("Reserved by Intel"), 242 | abi::EM_INTEL207 => Some("Reserved by Intel"), 243 | abi::EM_INTEL208 => Some("Reserved by Intel"), 244 | abi::EM_INTEL209 => Some("Reserved by Intel"), 245 | abi::EM_KM32 => Some("KM211 KM32 32-bit processor"), 246 | abi::EM_KMX32 => Some("KM211 KMX32 32-bit processor"), 247 | abi::EM_KMX16 => Some("KM211 KMX16 16-bit processor"), 248 | abi::EM_KMX8 => Some("KM211 KMX8 8-bit processor"), 249 | abi::EM_KVARC => Some("KM211 KVARC processor"), 250 | abi::EM_CDP => Some("Paneve CDP architecture family"), 251 | abi::EM_COGE => Some("Cognitive Smart Memory Processor"), 252 | abi::EM_COOL => Some("Bluechip Systems CoolEngine"), 253 | abi::EM_NORC => Some("Nanoradio Optimized RISC"), 254 | abi::EM_CSR_KALIMBA => Some("CSR Kalimba architecture family"), 255 | abi::EM_Z80 => Some("Zilog Z80"), 256 | abi::EM_VISIUM => Some("Controls and Data Services VISIUMcore processor"), 257 | abi::EM_FT32 => Some("FTDI Chip FT32 high performance 32-bit RISC architecture"), 258 | abi::EM_MOXIE => Some("Moxie processor family"), 259 | abi::EM_AMDGPU => Some("AMD GPU architecture"), 260 | abi::EM_RISCV => Some("RISC-V"), 261 | abi::EM_BPF => Some("Linux BPF"), 262 | _ => None, 263 | } 264 | } 265 | 266 | pub fn e_machine_to_str(e_machine: u16) -> Option<&'static str> { 267 | match e_machine { 268 | abi::EM_NONE => Some("EM_NONE"), 269 | abi::EM_M32 => Some("EM_M32"), 270 | abi::EM_SPARC => Some("EM_SPARC"), 271 | abi::EM_386 => Some("EM_386"), 272 | abi::EM_68K => Some("EM_68K"), 273 | abi::EM_88K => Some("EM_88K"), 274 | abi::EM_IAMCU => Some("EM_IAMCU"), 275 | abi::EM_860 => Some("EM_860"), 276 | abi::EM_MIPS => Some("EM_MIPS"), 277 | abi::EM_S370 => Some("EM_S370"), 278 | abi::EM_MIPS_RS3_LE => Some("EM_MIPS_RS3_LE"), 279 | abi::EM_PARISC => Some("EM_PARISC"), 280 | abi::EM_VPP500 => Some("EM_VPP500"), 281 | abi::EM_SPARC32PLUS => Some("EM_SPARC32PLUS"), 282 | abi::EM_960 => Some("EM_960"), 283 | abi::EM_PPC => Some("EM_PPC"), 284 | abi::EM_PPC64 => Some("EM_PPC64"), 285 | abi::EM_S390 => Some("EM_S390"), 286 | abi::EM_SPU => Some("EM_SPU"), 287 | abi::EM_V800 => Some("EM_V800"), 288 | abi::EM_FR20 => Some("EM_FR20"), 289 | abi::EM_RH32 => Some("EM_RH32"), 290 | abi::EM_RCE => Some("EM_RCE"), 291 | abi::EM_ARM => Some("EM_ARM"), 292 | abi::EM_ALPHA => Some("EM_ALPHA"), 293 | abi::EM_SH => Some("EM_SH"), 294 | abi::EM_SPARCV9 => Some("EM_SPARCV9"), 295 | abi::EM_TRICORE => Some("EM_TRICORE"), 296 | abi::EM_ARC => Some("EM_ARC"), 297 | abi::EM_H8_300 => Some("EM_H8_300"), 298 | abi::EM_H8_300H => Some("EM_H8_300H"), 299 | abi::EM_H8S => Some("EM_H8S"), 300 | abi::EM_H8_500 => Some("EM_H8_500"), 301 | abi::EM_IA_64 => Some("EM_IA_64"), 302 | abi::EM_MIPS_X => Some("EM_MIPS_X"), 303 | abi::EM_COLDFIRE => Some("EM_COLDFIRE"), 304 | abi::EM_68HC12 => Some("EM_68HC12"), 305 | abi::EM_MMA => Some("EM_MMA"), 306 | abi::EM_PCP => Some("EM_PCP"), 307 | abi::EM_NCPU => Some("EM_NCPU"), 308 | abi::EM_NDR1 => Some("EM_NDR1"), 309 | abi::EM_STARCORE => Some("EM_STARCORE"), 310 | abi::EM_ME16 => Some("EM_ME16"), 311 | abi::EM_ST100 => Some("EM_ST100"), 312 | abi::EM_TINYJ => Some("EM_TINYJ"), 313 | abi::EM_X86_64 => Some("EM_X86_64"), 314 | abi::EM_PDSP => Some("EM_PDSP"), 315 | abi::EM_PDP10 => Some("EM_PDP10"), 316 | abi::EM_PDP11 => Some("EM_PDP11"), 317 | abi::EM_FX66 => Some("EM_FX66"), 318 | abi::EM_ST9PLUS => Some("EM_ST9PLUS"), 319 | abi::EM_ST7 => Some("EM_ST7"), 320 | abi::EM_68HC16 => Some("EM_68HC16"), 321 | abi::EM_68HC11 => Some("EM_68HC11"), 322 | abi::EM_68HC08 => Some("EM_68HC08"), 323 | abi::EM_68HC05 => Some("EM_68HC05"), 324 | abi::EM_SVX => Some("EM_SVX"), 325 | abi::EM_ST19 => Some("EM_ST19"), 326 | abi::EM_VAX => Some("EM_VAX"), 327 | abi::EM_CRIS => Some("EM_CRIS"), 328 | abi::EM_JAVELIN => Some("EM_JAVELIN"), 329 | abi::EM_FIREPATH => Some("EM_FIREPATH"), 330 | abi::EM_ZSP => Some("EM_ZSP"), 331 | abi::EM_MMIX => Some("EM_MMIX"), 332 | abi::EM_HUANY => Some("EM_HUANY"), 333 | abi::EM_PRISM => Some("EM_PRISM"), 334 | abi::EM_AVR => Some("EM_AVR"), 335 | abi::EM_FR30 => Some("EM_FR30"), 336 | abi::EM_D10V => Some("EM_D10V"), 337 | abi::EM_D30V => Some("EM_D30V"), 338 | abi::EM_V850 => Some("EM_V850"), 339 | abi::EM_M32R => Some("EM_M32R"), 340 | abi::EM_MN10300 => Some("EM_MN10300"), 341 | abi::EM_MN10200 => Some("EM_MN10200"), 342 | abi::EM_PJ => Some("EM_PJ"), 343 | abi::EM_OPENRISC => Some("EM_OPENRISC"), 344 | abi::EM_ARC_COMPACT => Some("EM_ARC_COMPACT"), 345 | abi::EM_XTENSA => Some("EM_XTENSA"), 346 | abi::EM_VIDEOCORE => Some("EM_VIDEOCORE"), 347 | abi::EM_TMM_GPP => Some("EM_TMM_GPP"), 348 | abi::EM_NS32K => Some("EM_NS32K"), 349 | abi::EM_TPC => Some("EM_TPC"), 350 | abi::EM_SNP1K => Some("EM_SNP1K"), 351 | abi::EM_ST200 => Some("EM_ST200"), 352 | abi::EM_IP2K => Some("EM_IP2K"), 353 | abi::EM_MAX => Some("EM_MAX"), 354 | abi::EM_CR => Some("EM_CR"), 355 | abi::EM_F2MC16 => Some("EM_F2MC16"), 356 | abi::EM_MSP430 => Some("EM_MSP430"), 357 | abi::EM_BLACKFIN => Some("EM_BLACKFIN"), 358 | abi::EM_SE_C33 => Some("EM_SE_C33"), 359 | abi::EM_SEP => Some("EM_SEP"), 360 | abi::EM_ARCA => Some("EM_ARCA"), 361 | abi::EM_UNICORE => Some("EM_UNICORE"), 362 | abi::EM_EXCESS => Some("EM_EXCESS"), 363 | abi::EM_DXP => Some("EM_DXP"), 364 | abi::EM_ALTERA_NIOS2 => Some("EM_ALTERA_NIOS2"), 365 | abi::EM_CRX => Some("EM_CRX"), 366 | abi::EM_XGATE => Some("EM_XGATE"), 367 | abi::EM_C166 => Some("EM_C166"), 368 | abi::EM_M16C => Some("EM_M16C"), 369 | abi::EM_DSPIC30F => Some("EM_DSPIC30F"), 370 | abi::EM_CE => Some("EM_CE"), 371 | abi::EM_M32C => Some("EM_M32C"), 372 | abi::EM_TSK3000 => Some("EM_TSK3000"), 373 | abi::EM_RS08 => Some("EM_RS08"), 374 | abi::EM_SHARC => Some("EM_SHARC"), 375 | abi::EM_ECOG2 => Some("EM_ECOG2"), 376 | abi::EM_SCORE7 => Some("EM_SCORE7"), 377 | abi::EM_DSP24 => Some("EM_DSP24"), 378 | abi::EM_VIDEOCORE3 => Some("EM_VIDEOCORE3"), 379 | abi::EM_LATTICEMICO32 => Some("EM_LATTICEMICO32"), 380 | abi::EM_SE_C17 => Some("EM_SE_C17"), 381 | abi::EM_TI_C6000 => Some("EM_TI_C6000"), 382 | abi::EM_TI_C2000 => Some("EM_TI_C2000"), 383 | abi::EM_TI_C5500 => Some("EM_TI_C5500"), 384 | abi::EM_TI_ARP32 => Some("EM_TI_ARP32"), 385 | abi::EM_TI_PRU => Some("EM_TI_PRU"), 386 | abi::EM_MMDSP_PLUS => Some("EM_MMDSP_PLUS"), 387 | abi::EM_CYPRESS_M8C => Some("EM_CYPRESS_M8C"), 388 | abi::EM_R32C => Some("EM_R32C"), 389 | abi::EM_TRIMEDIA => Some("EM_TRIMEDIA"), 390 | abi::EM_QDSP6 => Some("EM_QDSP6"), 391 | abi::EM_8051 => Some("EM_8051"), 392 | abi::EM_STXP7X => Some("EM_STXP7X"), 393 | abi::EM_NDS32 => Some("EM_NDS32"), 394 | abi::EM_ECOG1X => Some("EM_ECOG1X"), 395 | abi::EM_MAXQ30 => Some("EM_MAXQ30"), 396 | abi::EM_XIMO16 => Some("EM_XIMO16"), 397 | abi::EM_MANIK => Some("EM_MANIK"), 398 | abi::EM_CRAYNV2 => Some("EM_CRAYNV2"), 399 | abi::EM_RX => Some("EM_RX"), 400 | abi::EM_METAG => Some("EM_METAG"), 401 | abi::EM_MCST_ELBRUS => Some("EM_MCST_ELBRUS"), 402 | abi::EM_ECOG16 => Some("EM_ECOG16"), 403 | abi::EM_CR16 => Some("EM_CR16"), 404 | abi::EM_ETPU => Some("EM_ETPU"), 405 | abi::EM_SLE9X => Some("EM_SLE9X"), 406 | abi::EM_L10M => Some("EM_L10M"), 407 | abi::EM_K10M => Some("EM_K10M"), 408 | abi::EM_AARCH64 => Some("EM_AARCH64"), 409 | abi::EM_AVR32 => Some("EM_AVR32"), 410 | abi::EM_STM8 => Some("EM_STM8"), 411 | abi::EM_TILE64 => Some("EM_TILE64"), 412 | abi::EM_TILEPRO => Some("EM_TILEPRO"), 413 | abi::EM_MICROBLAZE => Some("EM_MICROBLAZE"), 414 | abi::EM_CUDA => Some("EM_CUDA"), 415 | abi::EM_TILEGX => Some("EM_TILEGX"), 416 | abi::EM_CLOUDSHIELD => Some("EM_CLOUDSHIELD"), 417 | abi::EM_COREA_1ST => Some("EM_COREA_1ST"), 418 | abi::EM_COREA_2ND => Some("EM_COREA_2ND"), 419 | abi::EM_ARC_COMPACT2 => Some("EM_ARC_COMPACT2"), 420 | abi::EM_OPEN8 => Some("EM_OPEN8"), 421 | abi::EM_RL78 => Some("EM_RL78"), 422 | abi::EM_VIDEOCORE5 => Some("EM_VIDEOCORE5"), 423 | abi::EM_78KOR => Some("EM_78KOR"), 424 | abi::EM_56800EX => Some("EM_56800EX"), 425 | abi::EM_BA1 => Some("EM_BA1"), 426 | abi::EM_BA2 => Some("EM_BA2"), 427 | abi::EM_XCORE => Some("EM_XCORE"), 428 | abi::EM_MCHP_PIC => Some("EM_MCHP_PIC"), 429 | abi::EM_INTEL205 => Some("EM_INTEL205"), 430 | abi::EM_INTEL206 => Some("EM_INTEL206"), 431 | abi::EM_INTEL207 => Some("EM_INTEL207"), 432 | abi::EM_INTEL208 => Some("EM_INTEL208"), 433 | abi::EM_INTEL209 => Some("EM_INTEL209"), 434 | abi::EM_KM32 => Some("EM_KM32"), 435 | abi::EM_KMX32 => Some("EM_KMX32"), 436 | abi::EM_KMX16 => Some("EM_KMX16"), 437 | abi::EM_KMX8 => Some("EM_KMX8"), 438 | abi::EM_KVARC => Some("EM_KVARC"), 439 | abi::EM_CDP => Some("EM_CDP"), 440 | abi::EM_COGE => Some("EM_COGE"), 441 | abi::EM_COOL => Some("EM_COOL"), 442 | abi::EM_NORC => Some("EM_NORC"), 443 | abi::EM_CSR_KALIMBA => Some("EM_CSR_KALIMBA"), 444 | abi::EM_Z80 => Some("EM_Z80"), 445 | abi::EM_VISIUM => Some("EM_VISIUM"), 446 | abi::EM_FT32 => Some("EM_FT32"), 447 | abi::EM_MOXIE => Some("EM_MOXIE"), 448 | abi::EM_AMDGPU => Some("EM_AMDGPU"), 449 | abi::EM_RISCV => Some("EM_RISCV"), 450 | abi::EM_BPF => Some("EM_BPF"), 451 | _ => None, 452 | } 453 | } 454 | 455 | #[cfg(feature = "alloc")] 456 | pub fn e_machine_to_string(e_machine: u16) -> String { 457 | match e_machine_to_str(e_machine) { 458 | Some(s) => s.to_string(), 459 | None => format!("e_machine({e_machine:#x})"), 460 | } 461 | } 462 | 463 | pub fn sh_type_to_str(sh_type: u32) -> Option<&'static str> { 464 | match sh_type { 465 | abi::SHT_NULL => Some("SHT_NULL"), 466 | abi::SHT_PROGBITS => Some("SHT_PROGBITS"), 467 | abi::SHT_SYMTAB => Some("SHT_SYMTAB"), 468 | abi::SHT_STRTAB => Some("SHT_STRTAB"), 469 | abi::SHT_RELA => Some("SHT_RELA"), 470 | abi::SHT_HASH => Some("SHT_HASH"), 471 | abi::SHT_DYNAMIC => Some("SHT_DYNAMIC"), 472 | abi::SHT_NOTE => Some("SHT_NOTE"), 473 | abi::SHT_NOBITS => Some("SHT_NOBITS"), 474 | abi::SHT_REL => Some("SHT_REL"), 475 | abi::SHT_SHLIB => Some("SHT_SHLIB"), 476 | abi::SHT_DYNSYM => Some("SHT_DYNSYM"), 477 | abi::SHT_INIT_ARRAY => Some("SHT_INIT_ARRAY"), 478 | abi::SHT_FINI_ARRAY => Some("SHT_FINI_ARRAY"), 479 | abi::SHT_PREINIT_ARRAY => Some("SHT_PREINIT_ARRAY"), 480 | abi::SHT_GROUP => Some("SHT_GROUP"), 481 | abi::SHT_SYMTAB_SHNDX => Some("SHT_SYMTAB_SHNDX"), 482 | abi::SHT_GNU_ATTRIBUTES => Some("SHT_GNU_ATTRIBUTES"), 483 | abi::SHT_GNU_HASH => Some("SHT_GNU_HASH"), 484 | abi::SHT_GNU_LIBLIST => Some("SHT_GNU_LIBLIST"), 485 | abi::SHT_GNU_VERDEF => Some("SHT_GNU_VERDEF"), 486 | abi::SHT_GNU_VERNEED => Some("SHT_GNU_VERNEED"), 487 | abi::SHT_GNU_VERSYM => Some("SHT_GNU_VERSYM"), 488 | _ => None, 489 | } 490 | } 491 | 492 | #[cfg(feature = "alloc")] 493 | pub fn sh_type_to_string(sh_type: u32) -> String { 494 | match sh_type_to_str(sh_type) { 495 | Some(s) => s.to_string(), 496 | None => format!("sh_type({sh_type:#x})"), 497 | } 498 | } 499 | 500 | #[cfg(feature = "alloc")] 501 | pub fn p_flags_to_string(p_flags: u32) -> String { 502 | match p_flags < 8 { 503 | true => { 504 | let r = if p_flags & abi::PF_R != 0 { "R" } else { " " }; 505 | let w = if p_flags & abi::PF_W != 0 { "W" } else { " " }; 506 | let x = if p_flags & abi::PF_X != 0 { "E" } else { " " }; 507 | format!("{r}{w}{x}") 508 | } 509 | false => format!("p_flags({p_flags:#x})"), 510 | } 511 | } 512 | 513 | pub fn p_type_to_str(p_type: u32) -> Option<&'static str> { 514 | match p_type { 515 | abi::PT_NULL => Some("PT_NULL"), 516 | abi::PT_LOAD => Some("PT_LOAD"), 517 | abi::PT_DYNAMIC => Some("PT_DYNAMIC"), 518 | abi::PT_INTERP => Some("PT_INTERP"), 519 | abi::PT_NOTE => Some("PT_NOTE"), 520 | abi::PT_SHLIB => Some("PT_SHLIB"), 521 | abi::PT_PHDR => Some("PT_PHDR"), 522 | abi::PT_TLS => Some("PT_TLS"), 523 | abi::PT_GNU_EH_FRAME => Some("PT_GNU_EH_FRAME"), 524 | abi::PT_GNU_STACK => Some("PT_GNU_STACK"), 525 | abi::PT_GNU_RELRO => Some("PT_GNU_RELRO"), 526 | abi::PT_GNU_PROPERTY => Some("PT_GNU_PROPERTY"), 527 | _ => None, 528 | } 529 | } 530 | 531 | #[cfg(feature = "alloc")] 532 | pub fn p_type_to_string(p_type: u32) -> String { 533 | match p_type_to_str(p_type) { 534 | Some(s) => s.to_string(), 535 | None => format!("p_type({p_type:#x})"), 536 | } 537 | } 538 | 539 | pub fn st_symtype_to_str(st_symtype: u8) -> Option<&'static str> { 540 | match st_symtype { 541 | abi::STT_NOTYPE => Some("STT_NOTYPE"), 542 | abi::STT_OBJECT => Some("STT_OBJECT"), 543 | abi::STT_FUNC => Some("STT_FUNC"), 544 | abi::STT_SECTION => Some("STT_SECTION"), 545 | abi::STT_FILE => Some("STT_FILE"), 546 | abi::STT_COMMON => Some("STT_COMMON"), 547 | abi::STT_TLS => Some("STT_TLS"), 548 | abi::STT_GNU_IFUNC => Some("STT_GNU_IFUNC"), 549 | _ => None, 550 | } 551 | } 552 | 553 | #[cfg(feature = "alloc")] 554 | pub fn st_symtype_to_string(st_symtype: u8) -> String { 555 | match st_symtype_to_str(st_symtype) { 556 | Some(s) => s.to_string(), 557 | None => format!("st_symtype({st_symtype:#x})"), 558 | } 559 | } 560 | 561 | pub fn st_bind_to_str(st_bind: u8) -> Option<&'static str> { 562 | match st_bind { 563 | abi::STB_LOCAL => Some("STB_LOCAL"), 564 | abi::STB_GLOBAL => Some("STB_GLOBAL"), 565 | abi::STB_WEAK => Some("STB_WEAK"), 566 | abi::STB_GNU_UNIQUE => Some("STB_GNU_UNIQUE"), 567 | _ => None, 568 | } 569 | } 570 | 571 | #[cfg(feature = "alloc")] 572 | pub fn st_bind_to_string(st_bind: u8) -> String { 573 | match st_bind_to_str(st_bind) { 574 | Some(s) => s.to_string(), 575 | None => format!("st_bind({st_bind:#x})"), 576 | } 577 | } 578 | 579 | pub fn st_vis_to_str(st_vis: u8) -> Option<&'static str> { 580 | match st_vis { 581 | abi::STV_DEFAULT => Some("STV_DEFAULT"), 582 | abi::STV_INTERNAL => Some("STV_INTERNAL"), 583 | abi::STV_HIDDEN => Some("STV_HIDDEN"), 584 | abi::STV_PROTECTED => Some("STV_PROTECTED"), 585 | _ => None, 586 | } 587 | } 588 | 589 | #[cfg(feature = "alloc")] 590 | pub fn st_vis_to_string(st_vis: u8) -> String { 591 | match st_vis_to_str(st_vis) { 592 | Some(s) => s.to_string(), 593 | None => format!("st_vis({st_vis:#x})"), 594 | } 595 | } 596 | 597 | pub fn ch_type_to_str(ch_type: u32) -> Option<&'static str> { 598 | match ch_type { 599 | abi::ELFCOMPRESS_ZLIB => Some("ELFCOMPRESS_ZLIB"), 600 | abi::ELFCOMPRESS_ZSTD => Some("ELFCOMPRESS_ZSTD "), 601 | _ => None, 602 | } 603 | } 604 | 605 | pub fn note_abi_tag_os_to_str(os: u32) -> Option<&'static str> { 606 | match os { 607 | abi::ELF_NOTE_GNU_ABI_TAG_OS_LINUX => Some("Linux"), 608 | abi::ELF_NOTE_GNU_ABI_TAG_OS_GNU => Some("GNU"), 609 | abi::ELF_NOTE_GNU_ABI_TAG_OS_SOLARIS2 => Some("Solaris"), 610 | abi::ELF_NOTE_GNU_ABI_TAG_OS_FREEBSD => Some("FreeBSD"), 611 | _ => None, 612 | } 613 | } 614 | 615 | pub fn d_tag_to_str(d_tag: i64) -> Option<&'static str> { 616 | match d_tag { 617 | abi::DT_NULL => Some("DT_NULL"), 618 | abi::DT_NEEDED => Some("DT_NEEDED"), 619 | abi::DT_PLTRELSZ => Some("DT_PLTRELSZ"), 620 | abi::DT_PLTGOT => Some("DT_PLTGOT"), 621 | abi::DT_HASH => Some("DT_HASH"), 622 | abi::DT_STRTAB => Some("DT_STRTAB"), 623 | abi::DT_SYMTAB => Some("DT_SYMTAB"), 624 | abi::DT_RELA => Some("DT_RELA"), 625 | abi::DT_RELASZ => Some("DT_RELASZ"), 626 | abi::DT_RELAENT => Some("DT_RELAENT"), 627 | abi::DT_STRSZ => Some("DT_STRSZ"), 628 | abi::DT_SYMENT => Some("DT_SYMENT"), 629 | abi::DT_INIT => Some("DT_INIT"), 630 | abi::DT_FINI => Some("DT_FINI"), 631 | abi::DT_SONAME => Some("DT_SONAME"), 632 | abi::DT_RPATH => Some("DT_RPATH"), 633 | abi::DT_SYMBOLIC => Some("DT_SYMBOLIC"), 634 | abi::DT_REL => Some("DT_REL"), 635 | abi::DT_RELSZ => Some("DT_RELSZ"), 636 | abi::DT_RELENT => Some("DT_RELENT"), 637 | abi::DT_PLTREL => Some("DT_PLTREL"), 638 | abi::DT_DEBUG => Some("DT_DEBUG"), 639 | abi::DT_TEXTREL => Some("DT_TEXTREL"), 640 | abi::DT_JMPREL => Some("DT_JMPREL"), 641 | abi::DT_BIND_NOW => Some("DT_BIND_NOW"), 642 | abi::DT_INIT_ARRAY => Some("DT_INIT_ARRAY"), 643 | abi::DT_FINI_ARRAY => Some("DT_FINI_ARRAY"), 644 | abi::DT_INIT_ARRAYSZ => Some("DT_INIT_ARRAYSZ"), 645 | abi::DT_FINI_ARRAYSZ => Some("DT_FINI_ARRAYSZ"), 646 | abi::DT_RUNPATH => Some("DT_RUNPATH"), 647 | abi::DT_FLAGS => Some("DT_FLAGS"), 648 | abi::DT_PREINIT_ARRAY => Some("DT_PREINIT_ARRAY"), 649 | abi::DT_PREINIT_ARRAYSZ => Some("DT_PREINIT_ARRAYSZ"), 650 | abi::DT_SYMTAB_SHNDX => Some("DT_SYMTAB_SHNDX"), 651 | abi::DT_GUILE_GC_ROOT => Some("DT_GUILE_GC_ROOT"), 652 | abi::DT_GUILE_GC_ROOT_SZ => Some("DT_GUILE_GC_ROOT_SZ"), 653 | abi::DT_GUILE_ENTRY => Some("DT_GUILE_ENTRY"), 654 | abi::DT_GUILE_VM_VERSION => Some("DT_GUILE_VM_VERSION"), 655 | abi::DT_GUILE_FRAME_MAPS => Some("DT_GUILE_FRAME_MAPS"), 656 | abi::DT_LOOS => Some("DT_LOOS"), 657 | abi::DT_GNU_PRELINKED => Some("DT_GNU_PRELINKED"), 658 | abi::DT_GNU_CONFLICTSZ => Some("DT_GNU_CONFLICTSZ"), 659 | abi::DT_GNU_LIBLISTSZ => Some("DT_GNU_LIBLISTSZ"), 660 | abi::DT_CHECKSUM => Some("DT_CHECKSUM"), 661 | abi::DT_PLTPADSZ => Some("DT_PLTPADSZ"), 662 | abi::DT_MOVEENT => Some("DT_MOVEENT"), 663 | abi::DT_MOVESZ => Some("DT_MOVESZ"), 664 | abi::DT_FEATURE_1 => Some("DT_FEATURE_1"), 665 | abi::DT_POSFLAG_1 => Some("DT_POSFLAG_1"), 666 | abi::DT_SYMINSZ => Some("DT_SYMINSZ"), 667 | abi::DT_SYMINENT => Some("DT_SYMINENT"), 668 | abi::DT_GNU_HASH => Some("DT_GNU_HASH"), 669 | abi::DT_TLSDESC_PLT => Some("DT_TLSDESC_PLT"), 670 | abi::DT_TLSDESC_GOT => Some("DT_TLSDESC_GOT"), 671 | abi::DT_GNU_CONFLICT => Some("DT_GNU_CONFLICT"), 672 | abi::DT_GNU_LIBLIST => Some("DT_GNU_LIBLIST"), 673 | abi::DT_CONFIG => Some("DT_CONFIG"), 674 | abi::DT_DEPAUDIT => Some("DT_DEPAUDIT"), 675 | abi::DT_AUDIT => Some("DT_AUDIT"), 676 | abi::DT_PLTPAD => Some("DT_PLTPAD"), 677 | abi::DT_MOVETAB => Some("DT_MOVETAB"), 678 | abi::DT_SYMINFO => Some("DT_SYMINFO"), 679 | abi::DT_VERSYM => Some("DT_VERSYM"), 680 | abi::DT_RELACOUNT => Some("DT_RELACOUNT"), 681 | abi::DT_RELCOUNT => Some("DT_RELCOUNT"), 682 | abi::DT_FLAGS_1 => Some("DT_FLAGS_1"), 683 | abi::DT_VERDEF => Some("DT_VERDEF"), 684 | abi::DT_VERDEFNUM => Some("DT_VERDEFNUM"), 685 | abi::DT_VERNEED => Some("DT_VERNEED"), 686 | abi::DT_VERNEEDNUM => Some("DT_VERNEEDNUM"), 687 | abi::DT_HIOS => Some("DT_HIOS"), 688 | abi::DT_LOPROC => Some("DT_LOPROC"), 689 | abi::DT_HIPROC => Some("DT_HIPROC"), 690 | _ => None, 691 | } 692 | } 693 | --------------------------------------------------------------------------------